feat(chat): the chat server handles guilds

Connects GuildManager to the chat server (ChatGuilds): the client's GUILD_INVITE, GUILD_INVITE_RESPONSE, GUILD_LEAVE
and GUILD_GET_ALL (routed through its world), the worlds' GUILD_CREATE, GUILD_KICK, GUILD_SET_RANK and GUILD_DISBAND,
guild chat (GENERAL_CHAT_MESSAGE in channel 10, sent to the online members as channel 10 private chat, which the
client's guild tab shows, and logged as "guild"), guildmates told when a member logs in, out or changes worlds, and a
member's pending invite dropped when they log off. Client packets go through the member's world as WorldRoutePacket;
GUILD_GET_STATUS goes to the world itself.

Guild names: the chat filter's deny list refuses a name (dChatFilter::HasDenyList, since without a deny list the deny
check refuses everything); a name the allow list doesn't cover waits for moderation, as pet names do.

The dashboard's new player action GUILD_CHANGED goes from master to the chat server only (answered 1 when chat is
connected), which catches online members up. chatconfig.ini: guild_max_members (100) and guild_invite_timeout (600
seconds).

Check in game: nothing yet on its own (the world side makes guilds reachable). Server log: the chat server starts and
logs no unhandled guild packets.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-09-29 05:56:57 -05:00
parent e49c22374b
commit d1eec7dacb
12 changed files with 155 additions and 0 deletions

View File

@@ -24,6 +24,8 @@
#include "ChatIgnoreList.h"
#include "StringifiedEnum.h"
#include "TeamContainer.h"
#include "ChatGuilds.h"
#include "GuildManager.h"
#include "PacketDispatcher.h"
#include "ChatPackets.h"
#include "master/LiveUpdate.h"
@@ -145,6 +147,7 @@ int main(int argc, char** argv) {
Game::randomEngine = std::mt19937(time(0));
Game::playerContainer.Initialize();
ChatGuilds::Initialize();
//Run it until server gets a kill message from Master:
auto t = std::chrono::high_resolution_clock::now();
@@ -269,6 +272,15 @@ namespace {
announcement.WritePacket(bitStream);
Game::server->Send(bitStream, sysAddr, true); // send to everyone except origin
});
// Guilds (docs/Guilds.md): the first four come from the client through its world, the rest from worlds
handlers.On<GuildInvite>(Chat::GUILD_INVITE, [](const GuildInvite& invite, const SystemAddress&) { ChatGuilds::Get().Invite(invite.playerID, invite.invitedPlayer.GetAsString()); });
handlers.On<GuildInviteResponse>(Chat::GUILD_INVITE_RESPONSE, [](const GuildInviteResponse& response, const SystemAddress&) { ChatGuilds::Get().AnswerInvite(response.playerID, response.declined != 0); });
handlers.On<GuildLeave>(Chat::GUILD_LEAVE, [](const GuildLeave& leave, const SystemAddress&) { ChatGuilds::Get().Leave(leave.playerID); });
handlers.On<GuildGetAll>(Chat::GUILD_GET_ALL, [](const GuildGetAll& request, const SystemAddress&) { ChatGuilds::Get().GetAll(request.playerID); });
handlers.On<GuildCreate>(Chat::GUILD_CREATE, [](const GuildCreate& create, const SystemAddress&) { ChatGuilds::Get().Create(create.playerID, create.guildName.string); });
handlers.On<GuildKick>(Chat::GUILD_KICK, [](const GuildKick& kick, const SystemAddress&) { ChatGuilds::Get().Kick(kick.playerID, kick.kickedPlayer.GetAsString()); });
handlers.On<GuildSetRank>(Chat::GUILD_SET_RANK, [](const GuildSetRank& rank, const SystemAddress&) { ChatGuilds::Get().SetRank(rank.playerID, rank.targetPlayer.GetAsString(), static_cast<eGuildRank>(rank.rank)); });
handlers.On<GuildDisband>(Chat::GUILD_DISBAND, [](const GuildDisband& disband, const SystemAddress&) { ChatGuilds::Get().Disband(disband.playerID); });
handlers.On<UnexpectedDisconnect>(Chat::UNEXPECTED_DISCONNECT, [](const UnexpectedDisconnect& notify, const SystemAddress& sysAddr) { Game::playerContainer.ScheduleRemovePlayer(notify, sysAddr); });
handlers.On<FindPlayerRequest>(Chat::WHO, ChatPacketHandler::HandleWho);
handlers.On<ShowAllRequest>(Chat::SHOW_ALL, ChatPacketHandler::HandleShowAll);
@@ -308,6 +320,8 @@ namespace {
Game::chatFilter->ReloadCustomWords();
LOG("Reloaded the chat filter's words (changed on the dashboard)");
}
// A guild changed on the dashboard: tell its online members
if (request.action == ePlayerAction::GUILD_CHANGED) ChatGuilds::Get().GuildChanged(request.targetId);
});
return handlers;
}();