refactor: chat packets as structs

Every packet of the chat service is now an LUBitStream struct in dNet/ChatPackets.h
(docs/PacketArchitecture.md, PR 13), and nothing in the chat server or the chat side of
the world server reads or writes a packet by hand any more.

- World <-> chat: LoginSessionNotify, UnexpectedDisconnect, GMLevelUpdate, GMMute,
  Announcement (GM announce), CreateTeam, TeamUpdate (TEAM_GET_STATUS to worlds),
  AchievementNotify, ShowAllRequest, FindPlayerRequest.
- Client -> world -> chat (friends, ignore list, teams, general and private chat):
  GetFriendsList, AddFriendRequest, AddFriendResponse, RemoveFriend, GetIgnoreList,
  AddIgnore, RemoveIgnore, GeneralChatMessage, PrivateChatMessage, TeamInvite,
  TeamInviteResponse, TeamLeave, TeamKick, TeamSetLeader, TeamSetLoot, TeamGetStatus.
  The 77 bytes the handlers skipped are named fields now (the sender block the client
  fills in); the 4 unused bytes after the player ID are kept as `unknown`.
- What the client receives. Chat service (ChatPackets::Client): GeneralChatMessage
  (replaces SendChatMessage; SendSystemMessage stays as a helper built on it),
  PrivateChatMessage. Client service (ClientPackets, as structs go in the file of the
  ServiceType in their header): SendCannedText (replaces SendMessageFail), GetFriendsListResponse, AddFriendRequest,
  AddFriendResponse, RemoveFriendResponse, UpdateFriendNotify, WhoResponse,
  ShowAllResponse, Get/Add/RemoveIgnoreResponse, TeamInvite, TeamInviteInitialResponse,
  and the team game messages chat writes (TeamInviteConfirm, TeamGetStatusResponse,
  TeamSetLeader, TeamAddPlayer, TeamRemovePlayer, TeamSetOffWorldFlag) as TeamGameMsg
  structs, since chat doesn't link dGame's NetGameMsg.
- WORLD_ROUTE_PACKET is ChatPackets::WorldRoutePacket (its own file, dNet/WorldRoutePacket.h,
  since it carries packets of other services): the target and the inner packet.
  ChatPacketHandler::SendRouted replaces the per-function route headers and
  SendRoutedMsg.

Dispatch: dNet/PacketDispatcher.h is a dispatch map from packet ID to (struct, handler
function); packets that fail to Deserialize are logged and dropped. The chat server's
switch and the world's HandlePacketChat switch are now maps. The handlers take the
structs; their logic is unchanged. World code sends to chat with ChatServerLink::Send
(dGame) instead of writing to Game::chatServer by hand. eChatChannel,
eChatMessageResponseCode and eAddIgnoreResponse moved to dCommon/dEnums so the structs
can use them.

Verified: tests/dGameTests/dNetTests/Legacy/ChatPacketsLegacy.h is a verbatim copy of
the old senders and readers; ChatPacketsTests (25 tests) sends a grid of inputs through
both and requires identical bits, bytes and destination, checks the old readers read
what the structs write, round trips every struct, checks truncated packets are refused,
and has hand written golden packets for general chat, canned text, GM mute and a routed
team game message. Breaking one field width (UpdateFriendNotify) and the TeamAddPlayer
zone flag made the tests fail. No wire bytes changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-09-26 19:08:13 -05:00
parent db5af641cd
commit 313f821203
38 changed files with 3944 additions and 1141 deletions

View File

@@ -8,23 +8,14 @@
#include "eObjectBits.h"
#include "Database.h"
#include "ChatPacketHandler.h"
// A note to future readers, The client handles all the actual ignoring logic:
// not allowing teams, rejecting DMs, friends requets etc.
// The only thing not auto-handled is instance activities force joining the team on the server.
void WriteOutgoingReplyHeader(RakNet::BitStream& bitStream, const LWOOBJID& receivingPlayer, const MessageType::Client type) {
BitStreamUtils::WriteHeader(bitStream, ServiceType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(receivingPlayer);
//portion that will get routed:
BitStreamUtils::WriteHeader(bitStream, ServiceType::CLIENT, type);
}
void ChatIgnoreList::GetIgnoreList(Packet* packet) {
CINSTREAM_SKIP_HEADER;
LWOOBJID playerId;
inStream.Read(playerId);
void ChatIgnoreList::GetIgnoreList(const ChatPackets::GetIgnoreList& request, const SystemAddress& sysAddr) {
const LWOOBJID playerId = request.playerID;
auto& receiver = Game::playerContainer.GetPlayerDataMutable(playerId);
if (!receiver) {
@@ -47,25 +38,17 @@ void ChatIgnoreList::GetIgnoreList(Packet* packet) {
}
}
CBITSTREAM;
WriteOutgoingReplyHeader(bitStream, receiver.playerID, MessageType::Client::GET_IGNORE_LIST_RESPONSE);
bitStream.Write<uint8_t>(false); // Is Free Trial, but we don't care about that
bitStream.Write<uint16_t>(0); // literally spacing due to struct alignment
bitStream.Write<uint16_t>(receiver.ignoredPlayers.size());
ClientPackets::GetIgnoreListResponse response;
response.isFreeTrial = false; // Is Free Trial, but we don't care about that
for (const auto& ignoredPlayer : receiver.ignoredPlayers) {
bitStream.Write(ignoredPlayer.playerId);
bitStream.Write(LUWString(ignoredPlayer.playerName, 36));
response.ignored.push_back({ ignoredPlayer.playerId, LUWString(ignoredPlayer.playerName, 36) });
}
Game::server->Send(bitStream, packet->systemAddress, false);
ChatPacketHandler::SendRouted(receiver.playerID, sysAddr, response);
}
void ChatIgnoreList::AddIgnore(Packet* packet) {
CINSTREAM_SKIP_HEADER;
LWOOBJID playerId;
inStream.Read(playerId);
void ChatIgnoreList::AddIgnore(const ChatPackets::AddIgnore& request, const SystemAddress& sysAddr) {
const LWOOBJID playerId = request.playerID;
auto& receiver = Game::playerContainer.GetPlayerDataMutable(playerId);
if (!receiver) {
@@ -79,25 +62,20 @@ void ChatIgnoreList::AddIgnore(Packet* packet) {
return;
}
inStream.IgnoreBytes(4); // ignore some garbage zeros idk
std::string toIgnoreStr = request.playerName.GetAsString();
LUWString toIgnoreName;
inStream.Read(toIgnoreName);
std::string toIgnoreStr = toIgnoreName.GetAsString();
CBITSTREAM;
WriteOutgoingReplyHeader(bitStream, receiver.playerID, MessageType::Client::ADD_IGNORE_RESPONSE);
ClientPackets::AddIgnoreResponse response;
// Check if the player exists
LWOOBJID ignoredPlayerId = LWOOBJID_EMPTY;
if (toIgnoreStr == receiver.playerName || toIgnoreStr.find("[GM]") == 0) {
LOG_DEBUG("Player %llu tried to ignore themselves", playerId);
bitStream.Write(ChatIgnoreList::AddResponse::GENERAL_ERROR);
response.responseCode = eAddIgnoreResponse::GENERAL_ERROR;
} else if (std::count(receiver.ignoredPlayers.begin(), receiver.ignoredPlayers.end(), toIgnoreStr) > 0) {
LOG_DEBUG("Player %llu is already ignoring %s", playerId, toIgnoreStr.c_str());
bitStream.Write(ChatIgnoreList::AddResponse::ALREADY_IGNORED);
response.responseCode = eAddIgnoreResponse::ALREADY_IGNORED;
} else {
// Get the playerId falling back to query if not online
const auto& playerData = Game::playerContainer.GetPlayerData(toIgnoreStr);
@@ -120,23 +98,20 @@ void ChatIgnoreList::AddIgnore(Packet* packet) {
receiver.ignoredPlayers.emplace_back(toIgnoreStr, ignoredPlayerId);
LOG_DEBUG("Player %llu is ignoring %s", playerId, toIgnoreStr.c_str());
bitStream.Write(ChatIgnoreList::AddResponse::SUCCESS);
response.responseCode = eAddIgnoreResponse::SUCCESS;
} else {
bitStream.Write(ChatIgnoreList::AddResponse::PLAYER_NOT_FOUND);
response.responseCode = eAddIgnoreResponse::PLAYER_NOT_FOUND;
}
}
LUWString playerNameSend(toIgnoreStr, 33);
bitStream.Write(playerNameSend);
bitStream.Write(ignoredPlayerId);
response.playerName = LUWString(toIgnoreStr, 33);
response.playerID = ignoredPlayerId;
Game::server->Send(bitStream, packet->systemAddress, false);
ChatPacketHandler::SendRouted(receiver.playerID, sysAddr, response);
}
void ChatIgnoreList::RemoveIgnore(Packet* packet) {
CINSTREAM_SKIP_HEADER;
LWOOBJID playerId;
inStream.Read(playerId);
void ChatIgnoreList::RemoveIgnore(const ChatPackets::RemoveIgnore& request, const SystemAddress& sysAddr) {
const LWOOBJID playerId = request.playerID;
auto& receiver = Game::playerContainer.GetPlayerDataMutable(playerId);
if (!receiver) {
@@ -144,11 +119,7 @@ void ChatIgnoreList::RemoveIgnore(Packet* packet) {
return;
}
inStream.IgnoreBytes(4); // ignore some garbage zeros idk
LUWString removedIgnoreName;
inStream.Read(removedIgnoreName);
std::string removedIgnoreStr = removedIgnoreName.GetAsString();
std::string removedIgnoreStr = request.playerName.GetAsString();
auto toRemove = std::remove(receiver.ignoredPlayers.begin(), receiver.ignoredPlayers.end(), removedIgnoreStr);
if (toRemove == receiver.ignoredPlayers.end()) {
@@ -159,12 +130,9 @@ void ChatIgnoreList::RemoveIgnore(Packet* packet) {
Database::Get()->RemoveIgnore(playerId, toRemove->playerId);
receiver.ignoredPlayers.erase(toRemove, receiver.ignoredPlayers.end());
CBITSTREAM;
WriteOutgoingReplyHeader(bitStream, receiver.playerID, MessageType::Client::REMOVE_IGNORE_RESPONSE);
ClientPackets::RemoveIgnoreResponse response;
response.responseCode = 0;
response.playerName = LUWString(removedIgnoreStr, 33);
bitStream.Write<int8_t>(0);
LUWString playerNameSend(removedIgnoreStr, 33);
bitStream.Write(playerNameSend);
Game::server->Send(bitStream, packet->systemAddress, false);
ChatPacketHandler::SendRouted(receiver.playerID, sysAddr, response);
}

View File

@@ -1,26 +1,21 @@
#ifndef __CHATIGNORELIST__H__
#define __CHATIGNORELIST__H__
struct Packet;
struct SystemAddress;
#include <cstdint>
#include "ChatPackets.h"
/**
* @brief The ignore list allows players to ignore someone silently. Requests will generally be blocked by the client, but they should also be checked
* on the server as well so the sender can get a generic error code in response.
*
*/
namespace ChatIgnoreList {
void GetIgnoreList(Packet* packet);
void AddIgnore(Packet* packet);
void RemoveIgnore(Packet* packet);
enum class AddResponse : uint8_t {
SUCCESS,
ALREADY_IGNORED,
PLAYER_NOT_FOUND,
GENERAL_ERROR,
};
void GetIgnoreList(const ChatPackets::GetIgnoreList& request, const SystemAddress& sysAddr);
void AddIgnore(const ChatPackets::AddIgnore& request, const SystemAddress& sysAddr);
void RemoveIgnore(const ChatPackets::RemoveIgnore& request, const SystemAddress& sysAddr);
};
#endif //!__CHATIGNORELIST__H__

View File

@@ -22,11 +22,19 @@
#include "ChatPackets.h"
#include "TeamContainer.h"
void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
void ChatPacketHandler::SendRouted(const LWOOBJID target, const SystemAddress& world, const LUBitStream& msg, const bool broadcast) {
ChatPackets::WorldRoutePacket route;
route.targetID = target;
route.routed = &msg;
RakNet::BitStream bitStream;
route.WritePacket(bitStream);
Game::server->Send(bitStream, world, broadcast);
}
void ChatPacketHandler::HandleFriendlistRequest(const ChatPackets::GetFriendsList& request, const SystemAddress& sysAddr) {
//Get from the packet which player we want to do something with:
CINSTREAM_SKIP_HEADER;
LWOOBJID playerID = 0;
inStream.Read(playerID);
const LWOOBJID playerID = request.playerID;
auto& player = Game::playerContainer.GetPlayerDataMutable(playerID);
if (!player) return;
@@ -60,37 +68,16 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
}
//Now, we need to send the friendlist to the server they came from:
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, ServiceType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(playerID);
//portion that will get routed:
BitStreamUtils::WriteHeader(bitStream, ServiceType::CLIENT, MessageType::Client::GET_FRIENDS_LIST_RESPONSE);
bitStream.Write<uint8_t>(0);
bitStream.Write<uint16_t>(1); //Length of packet -- just writing one as it doesn't matter, client skips it.
bitStream.Write<uint16_t>(player.friends.size());
for (const auto& data : player.friends) {
data.Serialize(bitStream);
}
SystemAddress sysAddr = player.worldServerSysAddr;
SEND_PACKET;
ClientPackets::GetFriendsListResponse response;
response.friends = player.friends;
SendRouted(playerID, player.worldServerSysAddr, response);
}
void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
CINSTREAM_SKIP_HEADER;
void ChatPacketHandler::HandleFriendRequest(const ChatPackets::AddFriendRequest& request, const SystemAddress& sysAddr) {
const LWOOBJID requestorPlayerID = request.playerID;
const char isBestFriendRequest = request.isBestFriendRequest;
LWOOBJID requestorPlayerID;
LUWString LUplayerName;
char isBestFriendRequest{};
inStream.Read(requestorPlayerID);
inStream.IgnoreBytes(4);
inStream.Read(LUplayerName);
inStream.Read(isBestFriendRequest);
auto playerName = LUplayerName.GetAsString();
auto playerName = request.friendName.GetAsString();
auto& requestor = Game::playerContainer.GetPlayerDataMutable(requestorPlayerID);
if (!requestor) {
@@ -225,17 +212,10 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
}
}
void ChatPacketHandler::HandleFriendResponse(Packet* packet) {
CINSTREAM_SKIP_HEADER;
LWOOBJID playerID;
eAddFriendResponseCode clientResponseCode;
LUWString friendName;
inStream.Read(playerID);
inStream.IgnoreBytes(4);
inStream.Read(clientResponseCode);
inStream.Read(friendName);
void ChatPacketHandler::HandleFriendResponse(const ChatPackets::AddFriendResponse& response, const SystemAddress& sysAddr) {
const LWOOBJID playerID = response.playerID;
const eAddFriendResponseCode clientResponseCode = response.responseCode;
const LUWString& friendName = response.friendName;
//Now to try and find both of these:
auto& requestor = Game::playerContainer.GetPlayerDataMutable(playerID);
@@ -298,14 +278,9 @@ void ChatPacketHandler::HandleFriendResponse(Packet* packet) {
if (serverResponseCode != eAddFriendResponseType::ALREADYFRIEND) SendFriendResponse(requestee, requestor, serverResponseCode, isAlreadyBestFriends);
}
void ChatPacketHandler::HandleRemoveFriend(Packet* packet) {
CINSTREAM_SKIP_HEADER;
LWOOBJID playerID;
LUWString LUFriendName;
inStream.Read(playerID);
inStream.IgnoreBytes(4);
inStream.Read(LUFriendName);
auto friendName = LUFriendName.GetAsString();
void ChatPacketHandler::HandleRemoveFriend(const ChatPackets::RemoveFriend& request, const SystemAddress& sysAddr) {
const LWOOBJID playerID = request.playerID;
auto friendName = request.friendName.GetAsString();
//we'll have to query the db here to find the user, since you can delete them while they're offline.
//First, we need to find their ID:
@@ -350,20 +325,14 @@ void ChatPacketHandler::HandleRemoveFriend(Packet* packet) {
SendRemoveFriend(goonB, goonAName, true);
}
void ChatPacketHandler::HandleGMLevelUpdate(Packet* packet) {
CINSTREAM_SKIP_HEADER;
LWOOBJID playerID;
inStream.Read(playerID);
auto& player = Game::playerContainer.GetPlayerData(playerID);
void ChatPacketHandler::HandleGMLevelUpdate(const ChatPackets::GMLevelUpdate& update, const SystemAddress& sysAddr) {
auto& player = Game::playerContainer.GetPlayerDataMutable(update.playerID);
if (!player) return;
inStream.Read(player.gmLevel);
player.gmLevel = update.gmLevel;
}
void ChatPacketHandler::HandleWho(Packet* packet) {
CINSTREAM_SKIP_HEADER;
FindPlayerRequest request;
request.Deserialize(inStream);
void ChatPacketHandler::HandleWho(const ChatPackets::FindPlayerRequest& request, const SystemAddress& sysAddr) {
const auto& sender = Game::playerContainer.GetPlayerData(request.requestor);
if (!sender) return;
@@ -371,53 +340,29 @@ void ChatPacketHandler::HandleWho(Packet* packet) {
const auto& player = Game::playerContainer.GetPlayerData(request.playerName.GetAsString());
bool online = player;
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, ServiceType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(request.requestor);
BitStreamUtils::WriteHeader(bitStream, ServiceType::CLIENT, MessageType::Client::WHO_RESPONSE);
bitStream.Write<uint8_t>(online);
bitStream.Write(player.zoneID.GetMapID());
bitStream.Write(player.zoneID.GetInstanceID());
bitStream.Write(player.zoneID.GetCloneID());
bitStream.Write(request.playerName);
SystemAddress sysAddr = sender.worldServerSysAddr;
SEND_PACKET;
ClientPackets::WhoResponse response;
response.isOnline = online;
response.zoneID = player.zoneID;
response.playerName = request.playerName;
SendRouted(request.requestor, sender.worldServerSysAddr, response);
}
void ChatPacketHandler::HandleShowAll(Packet* packet) {
CINSTREAM_SKIP_HEADER;
ShowAllRequest request;
request.Deserialize(inStream);
void ChatPacketHandler::HandleShowAll(const ChatPackets::ShowAllRequest& request, const SystemAddress& sysAddr) {
const auto& sender = Game::playerContainer.GetPlayerData(request.requestor);
if (!sender) return;
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, ServiceType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(request.requestor);
BitStreamUtils::WriteHeader(bitStream, ServiceType::CLIENT, MessageType::Client::SHOW_ALL_RESPONSE);
bitStream.Write<uint8_t>(!request.displayZoneData && !request.displayIndividualPlayers);
bitStream.Write(Game::playerContainer.GetPlayerCount());
bitStream.Write(Game::playerContainer.GetSimCount());
bitStream.Write<uint8_t>(request.displayIndividualPlayers);
bitStream.Write<uint8_t>(request.displayZoneData);
ClientPackets::ShowAllResponse response;
response.playerCount = Game::playerContainer.GetPlayerCount();
response.simCount = Game::playerContainer.GetSimCount();
response.displayIndividualPlayers = request.displayIndividualPlayers;
response.displayZoneData = request.displayZoneData;
if (request.displayZoneData || request.displayIndividualPlayers) {
for (auto& [playerID, playerData] : Game::playerContainer.GetAllPlayers()) {
if (!playerData) continue;
bitStream.Write<uint8_t>(0); // structure packing
if (request.displayIndividualPlayers) bitStream.Write(LUWString(playerData.playerName));
if (request.displayZoneData) {
bitStream.Write(playerData.zoneID.GetMapID());
bitStream.Write(playerData.zoneID.GetInstanceID());
bitStream.Write(playerData.zoneID.GetCloneID());
}
response.players.push_back({ playerData.playerName, playerData.zoneID });
}
}
SystemAddress sysAddr = sender.worldServerSysAddr;
SEND_PACKET;
SendRouted(request.requestor, sender.worldServerSysAddr, response);
}
// the structure the client uses to send this packet is shared in many chat messages
@@ -446,29 +391,15 @@ namespace {
}
}
void ChatPacketHandler::HandleChatMessage(Packet* packet) {
CINSTREAM_SKIP_HEADER;
LWOOBJID playerID;
inStream.Read(playerID);
void ChatPacketHandler::HandleChatMessage(const ChatPackets::GeneralChatMessage& chatMessage, const SystemAddress& sysAddr) {
const LWOOBJID playerID = chatMessage.playerID;
const auto& sender = Game::playerContainer.GetPlayerData(playerID);
if (!sender || sender.GetIsMuted()) return;
eChatChannel channel;
uint32_t size;
inStream.IgnoreBytes(4);
inStream.Read(channel);
inStream.Read(size);
if (size > MAX_MESSAGE_LENGTH) {
LOG("Received a probably spoofed chat message, ignoring msg");
return;
}
inStream.IgnoreBytes(77);
LUWString message(size);
inStream.Read(message);
// Messages longer than MAX_MESSAGE_LENGTH were dropped when read
const eChatChannel channel = chatMessage.chatChannel;
const LUWString& message = chatMessage.message;
LOG("Got a message from (%s) via [%s]: %s", sender.playerName.c_str(), StringifiedEnum::ToString(channel).data(), message.GetAsString().c_str());
@@ -493,36 +424,18 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet) {
// the structure the client uses to send this packet is shared in many chat messages
// that are sent to the server. Because of this, there are large gaps of unused data in chat messages
void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) {
CINSTREAM_SKIP_HEADER;
LWOOBJID playerID;
inStream.Read(playerID);
void ChatPacketHandler::HandlePrivateChatMessage(const ChatPackets::PrivateChatMessage& chatMessage, const SystemAddress& sysAddr) {
const LWOOBJID playerID = chatMessage.playerID;
const auto& sender = Game::playerContainer.GetPlayerData(playerID);
if (!sender || sender.GetIsMuted()) return;
eChatChannel channel;
uint32_t size;
LUWString LUReceiverName;
inStream.IgnoreBytes(4);
inStream.Read(channel);
const eChatChannel channel = chatMessage.chatChannel;
if (channel != eChatChannel::PRIVATE_CHAT) LOG("WARNING: Received Private chat with the wrong channel!");
inStream.Read(size);
if (size > MAX_MESSAGE_LENGTH) {
LOG("Received a probably spoofed chat message, ignoring msg");
return;
}
inStream.IgnoreBytes(77);
inStream.Read(LUReceiverName);
auto receiverName = LUReceiverName.GetAsString();
inStream.IgnoreBytes(2);
LUWString message(size);
inStream.Read(message);
// Messages longer than MAX_MESSAGE_LENGTH were dropped when read
auto receiverName = chatMessage.receiverName.GetAsString();
const LUWString& message = chatMessage.message;
LOG("Got a message from (%s) via [%s]: %s to %s", sender.playerName.c_str(), StringifiedEnum::ToString(channel).data(), message.GetAsString().c_str(), receiverName.c_str());
@@ -553,9 +466,7 @@ void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) {
SendPrivateChatMessage(sender, receiver, sender, message, eChatChannel::GENERAL, eChatMessageResponseCode::NOTFRIENDS);
}
void ChatPacketHandler::OnAchievementNotify(RakNet::BitStream& bitstream, const SystemAddress& sysAddr) {
ChatPackets::AchievementNotify notify{};
notify.Deserialize(bitstream);
void ChatPacketHandler::OnAchievementNotify(ChatPackets::AchievementNotify& notify, const SystemAddress& sysAddr) {
const auto& playerData = Game::playerContainer.GetPlayerData(notify.earnerName.GetAsString());
if (!playerData) return;
@@ -565,36 +476,23 @@ void ChatPacketHandler::OnAchievementNotify(RakNet::BitStream& bitstream, const
notify.targetPlayerName.string = GeneralUtils::ASCIIToUTF16(friendData.playerName);
LOG_DEBUG("Sending achievement notify to %s", notify.targetPlayerName.GetAsString().c_str());
RakNet::BitStream worldStream;
BitStreamUtils::WriteHeader(worldStream, ServiceType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
worldStream.Write(friendData.playerID);
notify.WriteHeader(worldStream);
notify.Serialize(worldStream);
Game::server->Send(worldStream, friendData.worldServerSysAddr, false);
SendRouted(friendData.playerID, friendData.worldServerSysAddr, notify);
}
}
}
void ChatPacketHandler::SendPrivateChatMessage(const PlayerData& sender, const PlayerData& receiver, const PlayerData& routeTo, const LUWString& message, const eChatChannel channel, const eChatMessageResponseCode responseCode) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, ServiceType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(routeTo.playerID);
BitStreamUtils::WriteHeader(bitStream, ServiceType::CHAT, MessageType::Chat::PRIVATE_CHAT_MESSAGE);
bitStream.Write(sender.playerID);
bitStream.Write(channel);
bitStream.Write<uint32_t>(0); // not used
bitStream.Write(LUWString(sender.playerName));
bitStream.Write(sender.playerID);
bitStream.Write<uint16_t>(0); // sourceID
bitStream.Write(sender.gmLevel);
bitStream.Write(LUWString(receiver.playerName));
bitStream.Write(receiver.gmLevel);
bitStream.Write(responseCode);
bitStream.Write(message);
SystemAddress sysAddr = routeTo.worldServerSysAddr;
SEND_PACKET;
ChatPackets::Client::PrivateChatMessage chatMessage;
chatMessage.playerID = sender.playerID;
chatMessage.chatChannel = channel;
chatMessage.senderName = LUWString(sender.playerName);
chatMessage.senderID = sender.playerID;
chatMessage.senderGMLevel = sender.gmLevel;
chatMessage.receiverName = LUWString(receiver.playerName);
chatMessage.receiverGMLevel = receiver.gmLevel;
chatMessage.responseCode = responseCode;
chatMessage.message = message;
SendRouted(routeTo.playerID, routeTo.worldServerSysAddr, chatMessage);
}
void ChatPacketHandler::SendFriendUpdate(const PlayerData& friendData, const PlayerData& playerData, uint8_t notifyType, uint8_t isBestFriend) {
@@ -611,32 +509,21 @@ void ChatPacketHandler::SendFriendUpdate(const PlayerData& friendData, const Pla
[bool] - is best friend
[bool] - is FTP*/
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, ServiceType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(friendData.playerID);
//portion that will get routed:
BitStreamUtils::WriteHeader(bitStream, ServiceType::CLIENT, MessageType::Client::UPDATE_FRIEND_NOTIFY);
bitStream.Write<uint8_t>(notifyType);
ClientPackets::UpdateFriendNotify notify;
notify.notifyType = notifyType;
std::string playerName = playerData.playerName.c_str();
bitStream.Write(LUWString(playerName));
notify.friendName = LUWString(playerName);
bitStream.Write(playerData.zoneID.GetMapID());
bitStream.Write(playerData.zoneID.GetInstanceID());
const auto& zoneID = playerData.zoneID;
const LWOCLONEID cloneID = zoneID.GetCloneID() == friendData.zoneID.GetCloneID() ? 0 : zoneID.GetCloneID();
notify.zoneID = LWOZONEID(zoneID.GetMapID(), zoneID.GetInstanceID(), cloneID);
if (playerData.zoneID.GetCloneID() == friendData.zoneID.GetCloneID()) {
bitStream.Write(0);
} else {
bitStream.Write(playerData.zoneID.GetCloneID());
}
notify.isBestFriend = isBestFriend; //isBFF
notify.isFreeTrial = 0; //isFTP
bitStream.Write<uint8_t>(isBestFriend); //isBFF
bitStream.Write<uint8_t>(0); //isFTP
SystemAddress sysAddr = friendData.worldServerSysAddr;
SEND_PACKET;
SendRouted(friendData.playerID, friendData.worldServerSysAddr, notify);
}
void ChatPacketHandler::SendFriendRequest(const PlayerData& receiver, const PlayerData& sender) {
@@ -648,52 +535,30 @@ void ChatPacketHandler::SendFriendRequest(const PlayerData& receiver, const Play
}
}
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, ServiceType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID);
//portion that will get routed:
BitStreamUtils::WriteHeader(bitStream, ServiceType::CLIENT, MessageType::Client::ADD_FRIEND_REQUEST);
bitStream.Write(LUWString(sender.playerName));
bitStream.Write<uint8_t>(0); // This is a BFF flag however this is unused in live and does not have an implementation client side.
SystemAddress sysAddr = receiver.worldServerSysAddr;
SEND_PACKET;
ClientPackets::AddFriendRequest request;
request.requestorName = LUWString(sender.playerName);
request.isBestFriendRequest = 0; // This is a BFF flag however this is unused in live and does not have an implementation client side.
SendRouted(receiver.playerID, receiver.worldServerSysAddr, request);
}
void ChatPacketHandler::SendFriendResponse(const PlayerData& receiver, const PlayerData& sender, eAddFriendResponseType responseCode, uint8_t isBestFriendsAlready, uint8_t isBestFriendRequest) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, ServiceType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID);
// Portion that will get routed:
BitStreamUtils::WriteHeader(bitStream, ServiceType::CLIENT, MessageType::Client::ADD_FRIEND_RESPONSE);
bitStream.Write(responseCode);
ClientPackets::AddFriendResponse response;
response.responseCode = responseCode;
// For all requests besides accepted, write a flag that says whether or not we are already best friends with the receiver.
bitStream.Write<uint8_t>(responseCode != eAddFriendResponseType::ACCEPTED ? isBestFriendsAlready : sender.worldServerSysAddr != UNASSIGNED_SYSTEM_ADDRESS);
response.isOnlineOrBestFriend = responseCode != eAddFriendResponseType::ACCEPTED ? isBestFriendsAlready : sender.worldServerSysAddr != UNASSIGNED_SYSTEM_ADDRESS;
// Then write the player name
bitStream.Write(LUWString(sender.playerName));
response.friendName = LUWString(sender.playerName);
// Then if this is an acceptance code, write the following extra info.
if (responseCode == eAddFriendResponseType::ACCEPTED) {
bitStream.Write(sender.playerID);
bitStream.Write(sender.zoneID);
bitStream.Write(isBestFriendRequest); //isBFF
bitStream.Write<uint8_t>(0); //isFTP
}
SystemAddress sysAddr = receiver.worldServerSysAddr;
SEND_PACKET;
response.friendID = sender.playerID;
response.zoneID = sender.zoneID;
response.isBestFriend = isBestFriendRequest; //isBFF
response.isFreeTrial = 0; //isFTP
SendRouted(receiver.playerID, receiver.worldServerSysAddr, response);
}
void ChatPacketHandler::SendRemoveFriend(const PlayerData& receiver, std::string& personToRemove, bool isSuccessful) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, ServiceType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID);
//portion that will get routed:
BitStreamUtils::WriteHeader(bitStream, ServiceType::CLIENT, MessageType::Client::REMOVE_FRIEND_RESPONSE);
bitStream.Write<uint8_t>(isSuccessful); //isOnline
bitStream.Write(LUWString(personToRemove));
SystemAddress sysAddr = receiver.worldServerSysAddr;
SEND_PACKET;
ClientPackets::RemoveFriendResponse response;
response.isSuccessful = isSuccessful; //isOnline
response.friendName = LUWString(personToRemove);
SendRouted(receiver.playerID, receiver.worldServerSysAddr, response);
}

View File

@@ -2,60 +2,31 @@
#include "dCommonVars.h"
#include "dNetCommon.h"
#include "BitStream.h"
#include "ChatPackets.h"
#include "ClientPackets.h"
#include "WorldRoutePacket.h"
#include "eChatChannel.h"
#include "eChatMessageResponseCode.h"
struct PlayerData;
struct LUBitStream;
enum class eAddFriendResponseType : uint8_t;
enum class eChatChannel : uint8_t {
SYSTEMNOTIFY = 0,
SYSTEMWARNING,
SYSTEMERROR,
BROADCAST,
LOCAL,
LOCALNOANIM,
EMOTE,
PRIVATE_CHAT,
TEAM,
TEAMLOCAL,
GUILD,
GUILDNOTIFY,
PROPERTY,
ADMIN,
COMBATDAMAGE,
COMBATHEALING,
COMBATLOOT,
COMBATEXP,
COMBATDEATH,
GENERAL,
TRADE,
LFG,
USER
};
enum class eChatMessageResponseCode : uint8_t {
SENT = 0,
NOTONLINE,
GENERALERROR,
RECEIVEDNEWWHISPER,
NOTFRIENDS,
SENDERFREETRIAL,
RECEIVERFREETRIAL,
};
namespace ChatPacketHandler {
void HandleFriendlistRequest(Packet* packet);
void HandleFriendRequest(Packet* packet);
void HandleFriendResponse(Packet* packet);
void HandleRemoveFriend(Packet* packet);
void HandleGMLevelUpdate(Packet* packet);
void HandleWho(Packet* packet);
void HandleShowAll(Packet* packet);
void HandleChatMessage(Packet* packet);
void HandlePrivateChatMessage(Packet* packet);
// Sends msg to the world server `world`, which passes it on to the client of `target`
void SendRouted(const LWOOBJID target, const SystemAddress& world, const LUBitStream& msg, const bool broadcast = false);
void OnAchievementNotify(RakNet::BitStream& bitstream, const SystemAddress& sysAddr);
void HandleFriendlistRequest(const ChatPackets::GetFriendsList& request, const SystemAddress& sysAddr);
void HandleFriendRequest(const ChatPackets::AddFriendRequest& request, const SystemAddress& sysAddr);
void HandleFriendResponse(const ChatPackets::AddFriendResponse& response, const SystemAddress& sysAddr);
void HandleRemoveFriend(const ChatPackets::RemoveFriend& request, const SystemAddress& sysAddr);
void HandleGMLevelUpdate(const ChatPackets::GMLevelUpdate& update, const SystemAddress& sysAddr);
void HandleWho(const ChatPackets::FindPlayerRequest& request, const SystemAddress& sysAddr);
void HandleShowAll(const ChatPackets::ShowAllRequest& request, const SystemAddress& sysAddr);
void HandleChatMessage(const ChatPackets::GeneralChatMessage& chatMessage, const SystemAddress& sysAddr);
void HandlePrivateChatMessage(const ChatPackets::PrivateChatMessage& chatMessage, const SystemAddress& sysAddr);
void OnAchievementNotify(ChatPackets::AchievementNotify& notify, const SystemAddress& sysAddr);
//FriendData is the player we're SENDING this stuff to. Player is the friend that changed state.
void SendFriendUpdate(const PlayerData& friendData, const PlayerData& playerData, uint8_t notifyType, uint8_t isBestFriend);

View File

@@ -24,6 +24,8 @@
#include "ChatIgnoreList.h"
#include "StringifiedEnum.h"
#include "TeamContainer.h"
#include "PacketDispatcher.h"
#include "ChatPackets.h"
#include "Game.h"
#include "Server.h"
@@ -224,6 +226,55 @@ int main(int argc, char** argv) {
return EXIT_SUCCESS;
}
namespace {
// Packets from world servers
const PacketDispatcher<MessageType::Chat>& ChatHandlers() {
static const auto handlers = [] {
PacketDispatcher<MessageType::Chat> handlers;
using namespace ChatPackets;
using MessageType::Chat;
handlers.On<GMMute>(Chat::GM_MUTE, [](const GMMute& mute, const SystemAddress& sysAddr) { Game::playerContainer.MuteUpdate(mute, sysAddr); });
handlers.On<CreateTeam>(Chat::CREATE_TEAM, TeamContainer::CreateTeamServer);
handlers.On<GetFriendsList>(Chat::GET_FRIENDS_LIST, ChatPacketHandler::HandleFriendlistRequest);
handlers.On<GetIgnoreList>(Chat::GET_IGNORE_LIST, ChatIgnoreList::GetIgnoreList);
handlers.On<AddIgnore>(Chat::ADD_IGNORE, ChatIgnoreList::AddIgnore);
handlers.On<RemoveIgnore>(Chat::REMOVE_IGNORE, ChatIgnoreList::RemoveIgnore);
handlers.On<TeamGetStatus>(Chat::TEAM_GET_STATUS, TeamContainer::HandleTeamStatusRequest);
//this involves someone sending the initial request, the response is below, response as in from the other player.
//We basically just check to see if this player is online or not and route the packet.
handlers.On<AddFriendRequest>(Chat::ADD_FRIEND_REQUEST, ChatPacketHandler::HandleFriendRequest);
//This isn't the response a server sent, rather it is a player's response to a received request.
//Here, we'll actually have to add them to eachother's friend lists depending on the response code.
handlers.On<AddFriendResponse>(Chat::ADD_FRIEND_RESPONSE, ChatPacketHandler::HandleFriendResponse);
handlers.On<RemoveFriend>(Chat::REMOVE_FRIEND, ChatPacketHandler::HandleRemoveFriend);
handlers.On<GeneralChatMessage>(Chat::GENERAL_CHAT_MESSAGE, ChatPacketHandler::HandleChatMessage);
//This message is supposed to be echo'd to both the sender and the receiver
//BUT: they have to have different responseCodes, so we'll do some of the ol hacky wacky to fix that right up.
handlers.On<PrivateChatMessage>(Chat::PRIVATE_CHAT_MESSAGE, ChatPacketHandler::HandlePrivateChatMessage);
handlers.On<TeamInvite>(Chat::TEAM_INVITE, TeamContainer::HandleTeamInvite);
handlers.On<TeamInviteResponse>(Chat::TEAM_INVITE_RESPONSE, TeamContainer::HandleTeamInviteResponse);
handlers.On<TeamLeave>(Chat::TEAM_LEAVE, TeamContainer::HandleTeamLeave);
handlers.On<TeamSetLeader>(Chat::TEAM_SET_LEADER, TeamContainer::HandleTeamPromote);
handlers.On<TeamKick>(Chat::TEAM_KICK, TeamContainer::HandleTeamKick);
handlers.On<TeamSetLoot>(Chat::TEAM_SET_LOOT, TeamContainer::HandleTeamLootOption);
handlers.On<GMLevelUpdate>(Chat::GMLEVEL_UPDATE, ChatPacketHandler::HandleGMLevelUpdate);
handlers.On<LoginSessionNotify>(Chat::LOGIN_SESSION_NOTIFY, [](const LoginSessionNotify& notify, const SystemAddress& sysAddr) { Game::playerContainer.InsertPlayer(notify, sysAddr); });
// we just forward this packet to every connected server
handlers.On<Announcement>(Chat::GM_ANNOUNCE, [](const Announcement& announcement, const SystemAddress& sysAddr) {
RakNet::BitStream bitStream;
announcement.WritePacket(bitStream);
Game::server->Send(bitStream, sysAddr, true); // send to everyone except origin
});
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);
handlers.On<AchievementNotify>(Chat::ACHIEVEMENT_NOTIFY, ChatPacketHandler::OnAchievementNotify);
return handlers;
}();
return handlers;
}
}
// Messages from master that dServer doesn't handle itself
void HandleMasterPacket(Packet* packet) {
if (packet->length < 4 || static_cast<ServiceType>(packet->data[1]) != ServiceType::MASTER) return;
@@ -246,166 +297,15 @@ void HandlePacket(Packet* packet) {
LOG("A server is connecting, awaiting user list.");
} else if (packet->length < 4 || packet->data[0] != ID_USER_PACKET_ENUM) return; // Nothing left to process or not the right packet type
CINSTREAM;
inStream.SetReadOffset(BYTES_TO_BITS(1));
ServiceType connection;
inStream.Read(connection);
if (connection != ServiceType::CHAT) return;
MessageType::Chat chatMessageID;
inStream.Read(chatMessageID);
RakNet::BitStream inStream(packet->data, packet->length, false);
LUBitStream header;
if (!header.ReadHeader(inStream) || header.connectionType != ServiceType::CHAT) return;
// Our packing byte wasnt there? Probably a false packet
if (inStream.GetNumberOfUnreadBits() < 8) return;
inStream.IgnoreBytes(1);
if (packet->length < 8) return;
switch (chatMessageID) {
case MessageType::Chat::GM_MUTE:
Game::playerContainer.MuteUpdate(packet);
break;
case MessageType::Chat::CREATE_TEAM:
TeamContainer::CreateTeamServer(packet);
break;
case MessageType::Chat::GET_FRIENDS_LIST:
ChatPacketHandler::HandleFriendlistRequest(packet);
break;
case MessageType::Chat::GET_IGNORE_LIST:
ChatIgnoreList::GetIgnoreList(packet);
break;
case MessageType::Chat::ADD_IGNORE:
ChatIgnoreList::AddIgnore(packet);
break;
case MessageType::Chat::REMOVE_IGNORE:
ChatIgnoreList::RemoveIgnore(packet);
break;
case MessageType::Chat::TEAM_GET_STATUS:
TeamContainer::HandleTeamStatusRequest(packet);
break;
case MessageType::Chat::ADD_FRIEND_REQUEST:
//this involves someone sending the initial request, the response is below, response as in from the other player.
//We basically just check to see if this player is online or not and route the packet.
ChatPacketHandler::HandleFriendRequest(packet);
break;
case MessageType::Chat::ADD_FRIEND_RESPONSE:
//This isn't the response a server sent, rather it is a player's response to a received request.
//Here, we'll actually have to add them to eachother's friend lists depending on the response code.
ChatPacketHandler::HandleFriendResponse(packet);
break;
case MessageType::Chat::REMOVE_FRIEND:
ChatPacketHandler::HandleRemoveFriend(packet);
break;
case MessageType::Chat::GENERAL_CHAT_MESSAGE:
ChatPacketHandler::HandleChatMessage(packet);
break;
case MessageType::Chat::PRIVATE_CHAT_MESSAGE:
//This message is supposed to be echo'd to both the sender and the receiver
//BUT: they have to have different responseCodes, so we'll do some of the ol hacky wacky to fix that right up.
ChatPacketHandler::HandlePrivateChatMessage(packet);
break;
case MessageType::Chat::TEAM_INVITE:
TeamContainer::HandleTeamInvite(packet);
break;
case MessageType::Chat::TEAM_INVITE_RESPONSE:
TeamContainer::HandleTeamInviteResponse(packet);
break;
case MessageType::Chat::TEAM_LEAVE:
TeamContainer::HandleTeamLeave(packet);
break;
case MessageType::Chat::TEAM_SET_LEADER:
TeamContainer::HandleTeamPromote(packet);
break;
case MessageType::Chat::TEAM_KICK:
TeamContainer::HandleTeamKick(packet);
break;
case MessageType::Chat::TEAM_SET_LOOT:
TeamContainer::HandleTeamLootOption(packet);
break;
case MessageType::Chat::GMLEVEL_UPDATE:
ChatPacketHandler::HandleGMLevelUpdate(packet);
break;
case MessageType::Chat::LOGIN_SESSION_NOTIFY:
Game::playerContainer.InsertPlayer(packet);
break;
case MessageType::Chat::GM_ANNOUNCE:
// we just forward this packet to every connected server
inStream.ResetReadPointer();
Game::server->Send(inStream, packet->systemAddress, true); // send to everyone except origin
break;
case MessageType::Chat::UNEXPECTED_DISCONNECT:
Game::playerContainer.ScheduleRemovePlayer(packet);
break;
case MessageType::Chat::WHO:
ChatPacketHandler::HandleWho(packet);
break;
case MessageType::Chat::SHOW_ALL:
ChatPacketHandler::HandleShowAll(packet);
break;
case MessageType::Chat::ACHIEVEMENT_NOTIFY:
ChatPacketHandler::OnAchievementNotify(inStream, packet->systemAddress);
break;
case MessageType::Chat::USER_CHANNEL_CHAT_MESSAGE:
case MessageType::Chat::WORLD_DISCONNECT_REQUEST:
case MessageType::Chat::WORLD_PROXIMITY_RESPONSE:
case MessageType::Chat::WORLD_PARCEL_RESPONSE:
case MessageType::Chat::TEAM_MISSED_INVITE_CHECK:
case MessageType::Chat::GUILD_CREATE:
case MessageType::Chat::GUILD_INVITE:
case MessageType::Chat::GUILD_INVITE_RESPONSE:
case MessageType::Chat::GUILD_LEAVE:
case MessageType::Chat::GUILD_KICK:
case MessageType::Chat::GUILD_GET_STATUS:
case MessageType::Chat::GUILD_GET_ALL:
case MessageType::Chat::BLUEPRINT_MODERATED:
case MessageType::Chat::BLUEPRINT_MODEL_READY:
case MessageType::Chat::PROPERTY_READY_FOR_APPROVAL:
case MessageType::Chat::PROPERTY_MODERATION_CHANGED:
case MessageType::Chat::PROPERTY_BUILDMODE_CHANGED:
case MessageType::Chat::PROPERTY_BUILDMODE_CHANGED_REPORT:
case MessageType::Chat::MAIL:
case MessageType::Chat::WORLD_INSTANCE_LOCATION_REQUEST:
case MessageType::Chat::REPUTATION_UPDATE:
case MessageType::Chat::SEND_CANNED_TEXT:
case MessageType::Chat::CHARACTER_NAME_CHANGE_REQUEST:
case MessageType::Chat::CSR_REQUEST:
case MessageType::Chat::CSR_REPLY:
case MessageType::Chat::GM_KICK:
case MessageType::Chat::WORLD_ROUTE_PACKET:
case MessageType::Chat::GET_ZONE_POPULATIONS:
case MessageType::Chat::REQUEST_MINIMUM_CHAT_MODE:
case MessageType::Chat::MATCH_REQUEST:
case MessageType::Chat::UGCMANIFEST_REPORT_MISSING_FILE:
case MessageType::Chat::UGCMANIFEST_REPORT_DONE_FILE:
case MessageType::Chat::UGCMANIFEST_REPORT_DONE_BLUEPRINT:
case MessageType::Chat::UGCC_REQUEST:
case MessageType::Chat::WORLD_PLAYERS_PET_MODERATED_ACKNOWLEDGE:
case MessageType::Chat::GM_CLOSE_PRIVATE_CHAT_WINDOW:
case MessageType::Chat::PLAYER_READY:
case MessageType::Chat::GET_DONATION_TOTAL:
case MessageType::Chat::UPDATE_DONATION:
case MessageType::Chat::PRG_CSR_COMMAND:
case MessageType::Chat::HEARTBEAT_REQUEST_FROM_WORLD:
case MessageType::Chat::UPDATE_FREE_TRIAL_STATUS:
const auto chatMessageID = static_cast<MessageType::Chat>(header.internalPacketID);
if (!ChatHandlers().Dispatch(chatMessageID, inStream, packet->systemAddress)) {
LOG("Unhandled CHAT Message id: %s (%i)", StringifiedEnum::ToString(chatMessageID).data(), chatMessageID);
break;
default:
LOG("Unknown CHAT Message id: %i", chatMessageID);
}
}

View File

@@ -26,13 +26,9 @@ TeamData::TeamData() {
lootFlag = Game::config->GetValue("default_team_loot") == "0" ? 0 : 1;
}
void PlayerContainer::InsertPlayer(Packet* packet) {
CINSTREAM_SKIP_HEADER;
LWOOBJID playerId;
if (!inStream.Read(playerId)) {
LOG("Failed to read player ID");
return;
}
void PlayerContainer::InsertPlayer(const ChatPackets::LoginSessionNotify& notify, const SystemAddress& sysAddr) {
// Names longer than ChatPackets::LoginSessionNotify::MAX_NAME_LENGTH were dropped when read
const LWOOBJID playerId = notify.playerID;
auto isLogin = !m_Players.contains(playerId);
auto& data = m_Players[playerId];
@@ -40,21 +36,11 @@ void PlayerContainer::InsertPlayer(Packet* packet) {
data.isLogin = isLogin;
data.playerID = playerId;
uint32_t len;
if (!inStream.Read<uint32_t>(len)) return;
if (len > 33) {
LOG("Received a really long player name, probably a fake packet %i.", len);
return;
}
data.playerName.resize(len);
inStream.ReadAlignedBytes(reinterpret_cast<unsigned char*>(data.playerName.data()), len);
if (!inStream.Read(data.zoneID)) return;
if (!inStream.Read(data.muteExpire)) return;
if (!inStream.Read(data.gmLevel)) return;
data.worldServerSysAddr = packet->systemAddress;
data.playerName = notify.playerName;
data.zoneID = notify.zoneID;
data.muteExpire = static_cast<time_t>(notify.muteExpire);
data.gmLevel = notify.gmLevel;
data.worldServerSysAddr = sysAddr;
m_Names[data.playerID] = GeneralUtils::UTF8ToUTF16(data.playerName);
m_PlayerCount++;
@@ -66,10 +52,8 @@ void PlayerContainer::InsertPlayer(Packet* packet) {
m_PlayersToRemove.erase(playerId);
}
void PlayerContainer::ScheduleRemovePlayer(Packet* packet) {
CINSTREAM_SKIP_HEADER;
LWOOBJID playerID{ LWOOBJID_EMPTY };
inStream.Read(playerID);
void PlayerContainer::ScheduleRemovePlayer(const ChatPackets::UnexpectedDisconnect& notify, const SystemAddress& sysAddr) {
const LWOOBJID playerID = notify.playerID;
constexpr float updatePlayerOnLogoutTime = 20.0f;
if (playerID != LWOOBJID_EMPTY) m_PlayersToRemove.insert_or_assign(playerID, updatePlayerOnLogoutTime);
}
@@ -117,12 +101,9 @@ void PlayerContainer::RemovePlayer(const LWOOBJID playerID) {
Database::Get()->UpdateActivityLog(playerID, eActivityType::PlayerLoggedOut, player.zoneID.GetMapID());
}
void PlayerContainer::MuteUpdate(Packet* packet) {
CINSTREAM_SKIP_HEADER;
LWOOBJID playerID;
inStream.Read(playerID);
time_t expire = 0;
inStream.Read(expire);
void PlayerContainer::MuteUpdate(const ChatPackets::GMMute& mute, const SystemAddress& sysAddr) {
const LWOOBJID playerID = mute.playerID;
const time_t expire = static_cast<time_t>(mute.expire);
auto& player = this->GetPlayerDataMutable(playerID);
@@ -138,13 +119,10 @@ void PlayerContainer::MuteUpdate(Packet* packet) {
}
void PlayerContainer::BroadcastMuteUpdate(LWOOBJID player, time_t time) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, ServiceType::CHAT, MessageType::Chat::GM_MUTE);
bitStream.Write(player);
bitStream.Write(time);
Game::server->Send(bitStream, UNASSIGNED_SYSTEM_ADDRESS, true);
ChatPackets::GMMute mute;
mute.playerID = player;
mute.expire = time;
mute.Broadcast();
}
std::u16string PlayerContainer::GetName(LWOOBJID playerID) {

View File

@@ -6,6 +6,7 @@
#include "Game.h"
#include "dServer.h"
#include <unordered_map>
#include "ChatPackets.h"
enum class eGameMasterLevel : uint8_t;
@@ -65,10 +66,10 @@ struct TeamData {
class PlayerContainer {
public:
void Initialize();
void InsertPlayer(Packet* packet);
void ScheduleRemovePlayer(Packet* packet);
void InsertPlayer(const ChatPackets::LoginSessionNotify& notify, const SystemAddress& sysAddr);
void ScheduleRemovePlayer(const ChatPackets::UnexpectedDisconnect& notify, const SystemAddress& sysAddr);
void RemovePlayer(const LWOOBJID playerID);
void MuteUpdate(Packet* packet);
void MuteUpdate(const ChatPackets::GMMute& mute, const SystemAddress& sysAddr);
void BroadcastMuteUpdate(LWOOBJID player, time_t time);
void Shutdown();

View File

@@ -29,15 +29,9 @@ void TeamContainer::Shutdown() {
for (auto* team : g_TeamContainer.mTeams) if (team) delete team;
}
void TeamContainer::HandleTeamInvite(Packet* packet) {
CINSTREAM_SKIP_HEADER;
LWOOBJID playerID;
LUWString invitedPlayer;
inStream.Read(playerID);
inStream.IgnoreBytes(4);
inStream.Read(invitedPlayer);
void TeamContainer::HandleTeamInvite(const ChatPackets::TeamInvite& invite, const SystemAddress& sysAddr) {
const LWOOBJID playerID = invite.playerID;
const LUWString& invitedPlayer = invite.invitedPlayer;
const auto& player = Game::playerContainer.GetPlayerData(playerID);
@@ -76,22 +70,16 @@ void TeamContainer::HandleTeamInvite(Packet* packet) {
}
}
ChatPackets::TeamInviteInitialResponse response{};
ClientPackets::TeamInviteInitialResponse response{};
response.inviteFailedToSend = failed;
response.playerName = invitedPlayer.string;
ChatPackets::SendRoutedMsg(response, playerID, player.worldServerSysAddr);
ChatPacketHandler::SendRouted(playerID, player.worldServerSysAddr, response, player.worldServerSysAddr == UNASSIGNED_SYSTEM_ADDRESS);
}
void TeamContainer::HandleTeamInviteResponse(Packet* packet) {
CINSTREAM_SKIP_HEADER;
LWOOBJID playerID = LWOOBJID_EMPTY;
inStream.Read(playerID);
uint32_t size = 0;
inStream.Read(size);
char declined = 0;
inStream.Read(declined);
LWOOBJID leaderID = LWOOBJID_EMPTY;
inStream.Read(leaderID);
void TeamContainer::HandleTeamInviteResponse(const ChatPackets::TeamInviteResponse& response, const SystemAddress& sysAddr) {
const LWOOBJID playerID = response.playerID;
const char declined = response.declined;
const LWOOBJID leaderID = response.leaderID;
LOG("Invite reponse received: %llu -> %llu (%d)", playerID, leaderID, declined);
@@ -115,12 +103,8 @@ void TeamContainer::HandleTeamInviteResponse(Packet* packet) {
AddMember(team, playerID);
}
void TeamContainer::HandleTeamLeave(Packet* packet) {
CINSTREAM_SKIP_HEADER;
LWOOBJID playerID = LWOOBJID_EMPTY;
inStream.Read(playerID);
uint32_t size = 0;
inStream.Read(size);
void TeamContainer::HandleTeamLeave(const ChatPackets::TeamLeave& request, const SystemAddress& sysAddr) {
const LWOOBJID playerID = request.playerID;
auto* team = GetTeam(playerID);
@@ -131,15 +115,9 @@ void TeamContainer::HandleTeamLeave(Packet* packet) {
}
}
void TeamContainer::HandleTeamKick(Packet* packet) {
CINSTREAM_SKIP_HEADER;
LWOOBJID playerID = LWOOBJID_EMPTY;
LUWString kickedPlayer;
inStream.Read(playerID);
inStream.IgnoreBytes(4);
inStream.Read(kickedPlayer);
void TeamContainer::HandleTeamKick(const ChatPackets::TeamKick& request, const SystemAddress& sysAddr) {
const LWOOBJID playerID = request.playerID;
const LUWString& kickedPlayer = request.kickedPlayer;
LOG("(%llu) kicking (%s) from team", playerID, kickedPlayer.GetAsString().c_str());
@@ -165,15 +143,9 @@ void TeamContainer::HandleTeamKick(Packet* packet) {
}
}
void TeamContainer::HandleTeamPromote(Packet* packet) {
CINSTREAM_SKIP_HEADER;
LWOOBJID playerID = LWOOBJID_EMPTY;
LUWString promotedPlayer;
inStream.Read(playerID);
inStream.IgnoreBytes(4);
inStream.Read(promotedPlayer);
void TeamContainer::HandleTeamPromote(const ChatPackets::TeamSetLeader& request, const SystemAddress& sysAddr) {
const LWOOBJID playerID = request.playerID;
const LUWString& promotedPlayer = request.promotedPlayer;
LOG("(%llu) promoting (%s) to team leader", playerID, promotedPlayer.GetAsString().c_str());
@@ -190,15 +162,9 @@ void TeamContainer::HandleTeamPromote(Packet* packet) {
}
}
void TeamContainer::HandleTeamLootOption(Packet* packet) {
CINSTREAM_SKIP_HEADER;
LWOOBJID playerID = LWOOBJID_EMPTY;
inStream.Read(playerID);
uint32_t size = 0;
inStream.Read(size);
char option;
inStream.Read(option);
void TeamContainer::HandleTeamLootOption(const ChatPackets::TeamSetLoot& request, const SystemAddress& sysAddr) {
const LWOOBJID playerID = request.playerID;
const char option = request.lootFlag;
auto* team = GetTeam(playerID);
@@ -213,10 +179,8 @@ void TeamContainer::HandleTeamLootOption(Packet* packet) {
}
}
void TeamContainer::HandleTeamStatusRequest(Packet* packet) {
CINSTREAM_SKIP_HEADER;
LWOOBJID playerID = LWOOBJID_EMPTY;
inStream.Read(playerID);
void TeamContainer::HandleTeamStatusRequest(const ChatPackets::TeamGetStatus& request, const SystemAddress& sysAddr) {
const LWOOBJID playerID = request.playerID;
auto* team = GetTeam(playerID);
const auto& data = Game::playerContainer.GetPlayerData(playerID);
@@ -263,190 +227,86 @@ void TeamContainer::HandleTeamStatusRequest(Packet* packet) {
}
void TeamContainer::SendTeamInvite(const PlayerData& receiver, const PlayerData& sender) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, ServiceType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID);
//portion that will get routed:
BitStreamUtils::WriteHeader(bitStream, ServiceType::CLIENT, MessageType::Client::TEAM_INVITE);
bitStream.Write(LUWString(sender.playerName.c_str()));
bitStream.Write(sender.playerID);
SystemAddress sysAddr = receiver.worldServerSysAddr;
SEND_PACKET;
ClientPackets::TeamInvite invite;
invite.senderName = LUWString(sender.playerName.c_str());
invite.senderID = sender.playerID;
ChatPacketHandler::SendRouted(receiver.playerID, receiver.worldServerSysAddr, invite);
}
void TeamContainer::SendTeamInviteConfirm(const PlayerData& receiver, bool bLeaderIsFreeTrial, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, uint8_t ucResponseCode, std::u16string wsLeaderName) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, ServiceType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID);
//portion that will get routed:
CMSGHEADER;
bitStream.Write(receiver.playerID);
bitStream.Write(MessageType::Game::TEAM_INVITE_CONFIRM);
bitStream.Write(bLeaderIsFreeTrial);
bitStream.Write(i64LeaderID);
bitStream.Write(i64LeaderZoneID);
bitStream.Write<uint32_t>(0); // BinaryBuffe, no clue what's in here
bitStream.Write(ucLootFlag);
bitStream.Write(ucNumOfOtherPlayers);
bitStream.Write(ucResponseCode);
bitStream.Write<uint32_t>(wsLeaderName.size());
for (const auto character : wsLeaderName) {
bitStream.Write(character);
}
SystemAddress sysAddr = receiver.worldServerSysAddr;
SEND_PACKET;
ClientPackets::TeamInviteConfirm msg;
msg.target = receiver.playerID;
msg.bLeaderIsFreeTrial = bLeaderIsFreeTrial;
msg.i64LeaderID = i64LeaderID;
msg.i64LeaderZoneID = i64LeaderZoneID;
msg.ucLootFlag = ucLootFlag;
msg.ucNumOfOtherPlayers = ucNumOfOtherPlayers;
msg.ucResponseCode = ucResponseCode;
msg.wsLeaderName = wsLeaderName;
ChatPacketHandler::SendRouted(receiver.playerID, receiver.worldServerSysAddr, msg);
}
void TeamContainer::SendTeamStatus(const PlayerData& receiver, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, std::u16string wsLeaderName) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, ServiceType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID);
//portion that will get routed:
CMSGHEADER;
bitStream.Write(receiver.playerID);
bitStream.Write(MessageType::Game::TEAM_GET_STATUS_RESPONSE);
bitStream.Write(i64LeaderID);
bitStream.Write(i64LeaderZoneID);
bitStream.Write<uint32_t>(0); // BinaryBuffe, no clue what's in here
bitStream.Write(ucLootFlag);
bitStream.Write(ucNumOfOtherPlayers);
bitStream.Write<uint32_t>(wsLeaderName.size());
for (const auto character : wsLeaderName) {
bitStream.Write(character);
}
SystemAddress sysAddr = receiver.worldServerSysAddr;
SEND_PACKET;
ClientPackets::TeamGetStatusResponse msg;
msg.target = receiver.playerID;
msg.i64LeaderID = i64LeaderID;
msg.i64LeaderZoneID = i64LeaderZoneID;
msg.ucLootFlag = ucLootFlag;
msg.ucNumOfOtherPlayers = ucNumOfOtherPlayers;
msg.wsLeaderName = wsLeaderName;
ChatPacketHandler::SendRouted(receiver.playerID, receiver.worldServerSysAddr, msg);
}
void TeamContainer::SendTeamSetLeader(const PlayerData& receiver, LWOOBJID i64PlayerID) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, ServiceType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID);
//portion that will get routed:
CMSGHEADER;
bitStream.Write(receiver.playerID);
bitStream.Write(MessageType::Game::TEAM_SET_LEADER);
bitStream.Write(i64PlayerID);
SystemAddress sysAddr = receiver.worldServerSysAddr;
SEND_PACKET;
ClientPackets::TeamSetLeader msg;
msg.target = receiver.playerID;
msg.i64PlayerID = i64PlayerID;
ChatPacketHandler::SendRouted(receiver.playerID, receiver.worldServerSysAddr, msg);
}
void TeamContainer::SendTeamAddPlayer(const PlayerData& receiver, bool bIsFreeTrial, bool bLocal, bool bNoLootOnDeath, LWOOBJID i64PlayerID, std::u16string wsPlayerName, LWOZONEID zoneID) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, ServiceType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID);
//portion that will get routed:
CMSGHEADER;
bitStream.Write(receiver.playerID);
bitStream.Write(MessageType::Game::TEAM_ADD_PLAYER);
bitStream.Write(bIsFreeTrial);
bitStream.Write(bLocal);
bitStream.Write(bNoLootOnDeath);
bitStream.Write(i64PlayerID);
bitStream.Write<uint32_t>(wsPlayerName.size());
for (const auto character : wsPlayerName) {
bitStream.Write(character);
}
bitStream.Write1();
ClientPackets::TeamAddPlayer msg;
msg.target = receiver.playerID;
msg.bIsFreeTrial = bIsFreeTrial;
msg.bLocal = bLocal;
msg.bNoLootOnDeath = bNoLootOnDeath;
msg.i64PlayerID = i64PlayerID;
msg.wsPlayerName = wsPlayerName;
if (receiver.zoneID.GetCloneID() == zoneID.GetCloneID()) {
zoneID = LWOZONEID(zoneID.GetMapID(), zoneID.GetInstanceID(), 0);
}
bitStream.Write(zoneID);
SystemAddress sysAddr = receiver.worldServerSysAddr;
SEND_PACKET;
msg.zoneID = zoneID;
ChatPacketHandler::SendRouted(receiver.playerID, receiver.worldServerSysAddr, msg);
}
void TeamContainer::SendTeamRemovePlayer(const PlayerData& receiver, bool bDisband, bool bIsKicked, bool bIsLeaving, bool bLocal, LWOOBJID i64LeaderID, LWOOBJID i64PlayerID, std::u16string wsPlayerName) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, ServiceType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID);
//portion that will get routed:
CMSGHEADER;
bitStream.Write(receiver.playerID);
bitStream.Write(MessageType::Game::TEAM_REMOVE_PLAYER);
bitStream.Write(bDisband);
bitStream.Write(bIsKicked);
bitStream.Write(bIsLeaving);
bitStream.Write(bLocal);
bitStream.Write(i64LeaderID);
bitStream.Write(i64PlayerID);
bitStream.Write<uint32_t>(wsPlayerName.size());
for (const auto character : wsPlayerName) {
bitStream.Write(character);
}
SystemAddress sysAddr = receiver.worldServerSysAddr;
SEND_PACKET;
ClientPackets::TeamRemovePlayer msg;
msg.target = receiver.playerID;
msg.bDisband = bDisband;
msg.bIsKicked = bIsKicked;
msg.bIsLeaving = bIsLeaving;
msg.bLocal = bLocal;
msg.i64LeaderID = i64LeaderID;
msg.i64PlayerID = i64PlayerID;
msg.wsPlayerName = wsPlayerName;
ChatPacketHandler::SendRouted(receiver.playerID, receiver.worldServerSysAddr, msg);
}
void TeamContainer::SendTeamSetOffWorldFlag(const PlayerData& receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, ServiceType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID);
//portion that will get routed:
CMSGHEADER;
bitStream.Write(receiver.playerID);
bitStream.Write(MessageType::Game::TEAM_SET_OFF_WORLD_FLAG);
bitStream.Write(i64PlayerID);
ClientPackets::TeamSetOffWorldFlag msg;
msg.target = receiver.playerID;
msg.i64PlayerID = i64PlayerID;
if (receiver.zoneID.GetCloneID() == zoneID.GetCloneID()) {
zoneID = LWOZONEID(zoneID.GetMapID(), zoneID.GetInstanceID(), 0);
}
bitStream.Write(zoneID);
SystemAddress sysAddr = receiver.worldServerSysAddr;
SEND_PACKET;
msg.zoneID = zoneID;
ChatPacketHandler::SendRouted(receiver.playerID, receiver.worldServerSysAddr, msg);
}
void TeamContainer::CreateTeamServer(Packet* packet) {
CINSTREAM_SKIP_HEADER;
LWOOBJID playerID;
inStream.Read(playerID);
size_t membersSize = 0;
inStream.Read(membersSize);
if (membersSize >= 4) {
LOG("Tried to create a team with more than 4 players");
return;
}
std::vector<LWOOBJID> members;
members.reserve(membersSize);
for (size_t i = 0; i < membersSize; i++) {
LWOOBJID member;
inStream.Read(member);
members.push_back(member);
}
LWOZONEID zoneId;
inStream.Read(zoneId);
void TeamContainer::CreateTeamServer(const ChatPackets::CreateTeam& request, const SystemAddress& sysAddr) {
// Requests for more than 4 players (ChatPackets::CreateTeam::MAX_MEMBERS) were dropped when read
const std::vector<LWOOBJID>& members = request.members;
const LWOZONEID zoneId = request.zoneID;
auto* team = CreateLocalTeam(members);
@@ -651,19 +511,14 @@ void TeamContainer::TeamStatusUpdate(TeamData* team) {
}
void TeamContainer::UpdateTeamsOnWorld(TeamData* team, bool deleteTeam) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, ServiceType::CHAT, MessageType::Chat::TEAM_GET_STATUS);
bitStream.Write(team->teamID);
bitStream.Write(deleteTeam);
ChatPackets::TeamUpdate update;
update.teamID = team->teamID;
update.deleteTeam = deleteTeam;
if (!deleteTeam) {
bitStream.Write(team->lootFlag);
bitStream.Write<char>(team->memberIDs.size());
for (const auto memberID : team->memberIDs) {
bitStream.Write(memberID);
}
update.lootFlag = team->lootFlag;
update.members = team->memberIDs;
}
Game::server->Send(bitStream, UNASSIGNED_SYSTEM_ADDRESS, true);
update.Broadcast();
}

View File

@@ -9,8 +9,9 @@
#include <vector>
#include "dCommonVars.h"
#include "ChatPackets.h"
struct Packet;
struct SystemAddress;
struct PlayerData;
struct TeamData;
@@ -21,13 +22,13 @@ namespace TeamContainer {
void Shutdown();
void HandleTeamInvite(Packet* packet);
void HandleTeamInviteResponse(Packet* packet);
void HandleTeamLeave(Packet* packet);
void HandleTeamKick(Packet* packet);
void HandleTeamPromote(Packet* packet);
void HandleTeamLootOption(Packet* packet);
void HandleTeamStatusRequest(Packet* packet);
void HandleTeamInvite(const ChatPackets::TeamInvite& invite, const SystemAddress& sysAddr);
void HandleTeamInviteResponse(const ChatPackets::TeamInviteResponse& response, const SystemAddress& sysAddr);
void HandleTeamLeave(const ChatPackets::TeamLeave& request, const SystemAddress& sysAddr);
void HandleTeamKick(const ChatPackets::TeamKick& request, const SystemAddress& sysAddr);
void HandleTeamPromote(const ChatPackets::TeamSetLeader& request, const SystemAddress& sysAddr);
void HandleTeamLootOption(const ChatPackets::TeamSetLoot& request, const SystemAddress& sysAddr);
void HandleTeamStatusRequest(const ChatPackets::TeamGetStatus& request, const SystemAddress& sysAddr);
void SendTeamInvite(const PlayerData& receiver, const PlayerData& sender);
void SendTeamInviteConfirm(const PlayerData& receiver, bool bLeaderIsFreeTrial, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, uint8_t ucResponseCode, std::u16string wsLeaderName);
@@ -39,7 +40,7 @@ namespace TeamContainer {
void SendTeamRemovePlayer(const PlayerData& receiver, bool bDisband, bool bIsKicked, bool bIsLeaving, bool bLocal, LWOOBJID i64LeaderID, LWOOBJID i64PlayerID, std::u16string wsPlayerName);
void SendTeamSetOffWorldFlag(const PlayerData& receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID);
void CreateTeamServer(Packet* packet);
void CreateTeamServer(const ChatPackets::CreateTeam& request, const SystemAddress& sysAddr);
TeamData* CreateLocalTeam(std::vector<LWOOBJID> members);
TeamData* CreateTeam(LWOOBJID leader, bool local = false);