Make PacketUtils only for packetAdd BitstreamUtils and make better read/writesupdate

This commit is contained in:
Aaron Kimbre
2023-08-04 21:28:07 -05:00
parent c2b4aa4026
commit e145d237e4
33 changed files with 986 additions and 573 deletions

View File

@@ -3,7 +3,6 @@
#include "CDClientManager.h"
#include "Game.h"
#include "dLogger.h"
#include <PacketUtils.h>
#include <functional>
#include "CDDestructibleComponentTable.h"
#include "CDClientDatabase.h"

View File

@@ -11,7 +11,7 @@
#include <WorldPackets.h>
#include "Character.h"
#include <BitStream.h>
#include "PacketUtils.h"
#include "BitstreamUtils.h"
#include "../dWorldServer/ObjectIDManager.h"
#include "dLogger.h"
#include "GeneralUtils.h"
@@ -248,30 +248,44 @@ void UserManager::RequestCharacterList(const SystemAddress& sysAddr) {
void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet) {
User* u = GetUser(sysAddr);
if (!u) return;
CINSTREAM;
LUWString name(33);
inStream.Read(name);
std::string name = PacketUtils::ReadString(8, packet, true);
uint32_t firstNameIndex = PacketUtils::ReadPacketU32(74, packet);
uint32_t middleNameIndex = PacketUtils::ReadPacketU32(78, packet);
uint32_t lastNameIndex = PacketUtils::ReadPacketU32(82, packet);
uint32_t firstNameIndex;
inStream.Read(firstNameIndex);
uint32_t middleNameIndex;
inStream.Read(middleNameIndex);
uint32_t lastNameIndex;
inStream.Read(lastNameIndex);
std::string predefinedName = GetPredefinedName(firstNameIndex, middleNameIndex, lastNameIndex);
uint32_t shirtColor = PacketUtils::ReadPacketU32(95, packet);
uint32_t shirtStyle = PacketUtils::ReadPacketU32(99, packet);
uint32_t pantsColor = PacketUtils::ReadPacketU32(103, packet);
uint32_t hairStyle = PacketUtils::ReadPacketU32(107, packet);
uint32_t hairColor = PacketUtils::ReadPacketU32(111, packet);
uint32_t lh = PacketUtils::ReadPacketU32(115, packet);
uint32_t rh = PacketUtils::ReadPacketU32(119, packet);
uint32_t eyebrows = PacketUtils::ReadPacketU32(123, packet);
uint32_t eyes = PacketUtils::ReadPacketU32(127, packet);
uint32_t mouth = PacketUtils::ReadPacketU32(131, packet);
uint32_t shirtColor;
inStream.Read(shirtColor);
uint32_t shirtStyle;
inStream.Read(shirtStyle);
uint32_t pantsColor;
inStream.Read(pantsColor);
uint32_t hairStyle;
inStream.Read(hairStyle);
uint32_t hairColor;
inStream.Read(hairColor);
uint32_t lh;
inStream.Read(lh);
uint32_t rh;
inStream.Read(rh);
uint32_t eyebrows;
inStream.Read(eyebrows);
uint32_t eyes;
inStream.Read(eyes);
uint32_t mouth;
inStream.Read(mouth);
LOT shirtLOT = FindCharShirtID(shirtColor, shirtStyle);
LOT pantsLOT = FindCharPantsID(pantsColor);
if (name != "" && !UserManager::IsNameAvailable(name)) {
Game::logger->Log("UserManager", "AccountID: %i chose unavailable name: %s", u->GetAccountID(), name.c_str());
if (name.GetAsString() != "" && !UserManager::IsNameAvailable(name.GetAsString())) {
Game::logger->Log("UserManager", "AccountID: %i chose unavailable name: %s", u->GetAccountID(), name.GetAsString().c_str());
WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::CUSTOM_NAME_IN_USE);
return;
}
@@ -282,10 +296,10 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet)
return;
}
if (name == "") {
if (name.GetAsString() == "") {
Game::logger->Log("UserManager", "AccountID: %i is creating a character with predefined name: %s", u->GetAccountID(), predefinedName.c_str());
} else {
Game::logger->Log("UserManager", "AccountID: %i is creating a character with name: %s (temporary: %s)", u->GetAccountID(), name.c_str(), predefinedName.c_str());
Game::logger->Log("UserManager", "AccountID: %i is creating a character with name: %s (temporary: %s)", u->GetAccountID(), name.GetAsString().c_str(), predefinedName.c_str());
}
//Now that the name is ok, we can get an objectID from Master:
@@ -334,20 +348,20 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet)
xml3 << "</in></items></inv><lvl l=\"1\" cv=\"1\" sb=\"500\"/><flag></flag></obj>";
//Check to see if our name was pre-approved:
bool nameOk = IsNamePreapproved(name);
bool nameOk = IsNamePreapproved(name.GetAsString());
if (!nameOk && u->GetMaxGMLevel() > eGameMasterLevel::FORUM_MODERATOR) nameOk = true;
if (name != "") {
if (name.GetAsString() != "") {
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("INSERT INTO `charinfo`(`id`, `account_id`, `name`, `pending_name`, `needs_rename`, `last_login`) VALUES (?,?,?,?,?,?)");
stmt->setUInt(1, objectID);
stmt->setUInt(2, u->GetAccountID());
stmt->setString(3, predefinedName.c_str());
stmt->setString(4, name.c_str());
stmt->setString(4, name.GetAsString().c_str());
stmt->setBoolean(5, false);
stmt->setUInt64(6, time(NULL));
if (nameOk) {
stmt->setString(3, name.c_str());
stmt->setString(3, name.GetAsString().c_str());
stmt->setString(4, "");
}
@@ -386,8 +400,9 @@ void UserManager::DeleteCharacter(const SystemAddress& sysAddr, Packet* packet)
Game::logger->Log("UserManager", "Couldn't get user to delete character");
return;
}
LWOOBJID objectID = PacketUtils::ReadPacketS64(8, packet);
CINSTREAM_SKIP_HEADER;
LWOOBJID objectID;
inStream.Read(objectID);
uint32_t charID = static_cast<uint32_t>(objectID);
Game::logger->Log("UserManager", "Received char delete req for ID: %llu (%u)", objectID, charID);
@@ -423,7 +438,7 @@ void UserManager::DeleteCharacter(const SystemAddress& sysAddr, Packet* packet)
stmt->execute();
delete stmt;
CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::PLAYER_REMOVED_NOTIFICATION);
BitstreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::PLAYER_REMOVED_NOTIFICATION);
bitStream.Write(objectID);
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
}
@@ -482,15 +497,17 @@ void UserManager::RenameCharacter(const SystemAddress& sysAddr, Packet* packet)
Game::logger->Log("UserManager", "Couldn't get user to delete character");
return;
}
LWOOBJID objectID = PacketUtils::ReadPacketS64(8, packet);
CINSTREAM_SKIP_HEADER;
LWOOBJID objectID;
inStream.Read(objectID);
GeneralUtils::ClearBit(objectID, eObjectBits::CHARACTER);
GeneralUtils::ClearBit(objectID, eObjectBits::PERSISTENT);
uint32_t charID = static_cast<uint32_t>(objectID);
Game::logger->Log("UserManager", "Received char rename request for ID: %llu (%u)", objectID, charID);
std::string newName = PacketUtils::ReadString(16, packet, true);
LUWString newName(33);
inStream.Read(newName);
Character* character = nullptr;
@@ -505,32 +522,32 @@ void UserManager::RenameCharacter(const SystemAddress& sysAddr, Packet* packet)
Game::logger->Log("UserManager", "User %i tried to rename a character that it does not own!", u->GetAccountID());
WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::UNKNOWN_ERROR);
} else if (hasCharacter && character) {
if (newName == character->GetName()) {
if (newName.GetAsString() == character->GetName()) {
WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::NAME_UNAVAILABLE);
return;
}
if (IsNameAvailable(newName)) {
if (IsNamePreapproved(newName)) {
if (IsNameAvailable(newName.GetAsString())) {
if (IsNamePreapproved(newName.GetAsString())) {
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("UPDATE charinfo SET name=?, pending_name='', needs_rename=0, last_login=? WHERE id=? LIMIT 1");
stmt->setString(1, newName);
stmt->setString(1, newName.GetAsString());
stmt->setUInt64(2, time(NULL));
stmt->setUInt(3, character->GetID());
stmt->execute();
delete stmt;
Game::logger->Log("UserManager", "Character %s now known as %s", character->GetName().c_str(), newName.c_str());
Game::logger->Log("UserManager", "Character %s now known as %s", character->GetName().c_str(), newName.GetAsString().c_str());
WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::SUCCESS);
UserManager::RequestCharacterList(sysAddr);
} else {
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("UPDATE charinfo SET pending_name=?, needs_rename=0, last_login=? WHERE id=? LIMIT 1");
stmt->setString(1, newName);
stmt->setString(1, newName.GetAsString());
stmt->setUInt64(2, time(NULL));
stmt->setUInt(3, character->GetID());
stmt->execute();
delete stmt;
Game::logger->Log("UserManager", "Character %s has been renamed to %s and is pending approval by a moderator.", character->GetName().c_str(), newName.c_str());
Game::logger->Log("UserManager", "Character %s has been renamed to %s and is pending approval by a moderator.", character->GetName().c_str(), newName.GetAsString().c_str());
WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::SUCCESS);
UserManager::RequestCharacterList(sysAddr);
}

View File

@@ -6,7 +6,7 @@
#include "Game.h"
#include "dLogger.h"
#include "dServer.h"
#include "PacketUtils.h"
#include "BitstreamUtils.h"
#include <sstream>
@@ -253,7 +253,7 @@ bool BehaviorContext::CalculateUpdate(const float deltaTime) {
// Write message
RakNet::BitStream message;
PacketUtils::WriteHeader(message, eConnectionType::CLIENT, eClientMessageType::GAME_MSG);
BitstreamUtils::WriteHeader(message, eConnectionType::CLIENT, eClientMessageType::GAME_MSG);
message.Write(this->originator);
echo.Serialize(&message);

View File

@@ -14,7 +14,6 @@
#include "eGameActivity.h"
#include "dServer.h"
#include "PacketUtils.h"
#include "Spawner.h"
#include "MovingPlatformComponent.h"
#include "Preconditions.h"

View File

@@ -5,7 +5,6 @@
#include <iomanip>
#include "Entity.h"
#include "PacketUtils.h"
#include "CDClientManager.h"
#include "GameMessages.h"

View File

@@ -15,7 +15,7 @@
#include "PropertyEntranceComponent.h"
#include "RocketLaunchLupComponent.h"
#include "dServer.h"
#include "PacketUtils.h"
#include "BitstreamUtils.h"
#include "eObjectWorldState.h"
#include "eConnectionType.h"
#include "eMasterMessageType.h"
@@ -137,7 +137,7 @@ LWOCLONEID RocketLaunchpadControlComponent::GetSelectedCloneId(LWOOBJID player)
void RocketLaunchpadControlComponent::TellMasterToPrepZone(int zoneID) {
CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::PREP_ZONE);
BitstreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::PREP_ZONE);
bitStream.Write(zoneID);
Game::server->SendToMaster(&bitStream);
}

View File

@@ -11,7 +11,7 @@
#include "EntityManager.h"
#include "ChatPackets.h"
#include "Player.h"
#include "PacketUtils.h"
#include "BitstreamUtils.h"
#include "dServer.h"
#include "GeneralUtils.h"
#include "dZoneManager.h"
@@ -516,7 +516,7 @@ void ActivityInstance::StartZone() {
// only make a team if we have more than one participant
if (participants.size() > 1) {
CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::CREATE_TEAM);
BitstreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::CREATE_TEAM);
bitStream.Write(leader->GetObjectID());
bitStream.Write(m_Participants.size());

View File

@@ -15,7 +15,7 @@
#include "dServer.h"
#include "EntityManager.h"
#include "Game.h"
#include "PacketUtils.h"
#include "BitstreamUtils.h"
#include "BaseCombatAIComponent.h"
#include "ScriptComponent.h"
#include "BuffComponent.h"
@@ -304,7 +304,7 @@ SkillExecutionResult SkillComponent::CalculateBehavior(const uint32_t skillId, c
// Write message
RakNet::BitStream message;
PacketUtils::WriteHeader(message, eConnectionType::CLIENT, eClientMessageType::GAME_MSG);
BitstreamUtils::WriteHeader(message, eConnectionType::CLIENT, eClientMessageType::GAME_MSG);
message.Write(this->m_Parent->GetObjectID());
start.Serialize(&message);
@@ -437,7 +437,7 @@ void SkillComponent::SyncProjectileCalculation(const ProjectileSyncEntry& entry)
RakNet::BitStream message;
PacketUtils::WriteHeader(message, eConnectionType::CLIENT, eClientMessageType::GAME_MSG);
BitstreamUtils::WriteHeader(message, eConnectionType::CLIENT, eClientMessageType::GAME_MSG);
message.Write(this->m_Parent->GetObjectID());
projectileImpact.Serialize(&message);

View File

@@ -5,7 +5,7 @@
#include "GameMessageHandler.h"
#include "MissionComponent.h"
#include "PacketUtils.h"
#include "BitstreamUtils.h"
#include "dServer.h"
#include "../thirdparty/raknet/Source/RakNetworkFactory.h"
#include <future>
@@ -314,7 +314,7 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System
if (success) {
//Broadcast our startSkill:
RakNet::BitStream bitStreamLocal;
PacketUtils::WriteHeader(bitStreamLocal, eConnectionType::CLIENT, eClientMessageType::GAME_MSG);
BitstreamUtils::WriteHeader(bitStreamLocal, eConnectionType::CLIENT, eClientMessageType::GAME_MSG);
bitStreamLocal.Write(entity->GetObjectID());
EchoStartSkill echoStartSkill;
@@ -336,7 +336,7 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System
case eGameMessageType::SYNC_SKILL: {
RakNet::BitStream bitStreamLocal;
PacketUtils::WriteHeader(bitStreamLocal, eConnectionType::CLIENT, eClientMessageType::GAME_MSG);
BitstreamUtils::WriteHeader(bitStreamLocal, eConnectionType::CLIENT, eClientMessageType::GAME_MSG);
bitStreamLocal.Write(entity->GetObjectID());
//bitStreamLocal.Write((unsigned short)eGameMessageType::ECHO_SYNC_SKILL);
//bitStreamLocal.Write(inStream);

View File

@@ -2,6 +2,7 @@
#include "User.h"
#include "Entity.h"
#include "PacketUtils.h"
#include "BitstreamUtils.h"
#include "BitStream.h"
#include "Game.h"
#include "SlashCommandHandler.h"
@@ -172,7 +173,7 @@ void GameMessages::SendPlayAnimation(Entity* entity, const std::u16string& anima
bitStream.Write(eGameMessageType::PLAY_ANIMATION);
bitStream.Write(animationIDLength);
PacketUtils::WriteWString(bitStream, animationName, animationIDLength);
bitStream.Write(animationName);
bitStream.Write(bExpectAnimToExist);
@@ -325,7 +326,7 @@ void GameMessages::SendPlayNDAudioEmitter(Entity* entity, const SystemAddress& s
bitStream.Write(static_cast<char>(audioGUID[k]));
}
//PacketUtils::WriteString(bitStream, audioGUID, audioGUID.size());
//BitstreamUtils::WriteString(bitStream, audioGUID, audioGUID.size());
//bitStream.Write(uint32_t(audioGUID.size()));
//for (char character : audioGUID) {
@@ -2179,7 +2180,7 @@ void GameMessages::HandleUnUseModel(RakNet::BitStream* inStream, Entity* entity,
if (unknown) {
CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::BLUEPRINT_SAVE_RESPONSE);
BitstreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::BLUEPRINT_SAVE_RESPONSE);
bitStream.Write<LWOOBJID>(LWOOBJID_EMPTY); //always zero so that a check on the client passes
bitStream.Write(eBlueprintSaveResponseType::PlacementFailed); // Sending a non-zero error code here prevents the client from deleting its in progress build for some reason?
bitStream.Write<uint32_t>(0);
@@ -2431,7 +2432,7 @@ void GameMessages::HandleBBBLoadItemRequest(RakNet::BitStream* inStream, Entity*
void GameMessages::SendBlueprintLoadItemResponse(const SystemAddress& sysAddr, bool success, LWOOBJID oldItemId, LWOOBJID newItemId) {
CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::BLUEPRINT_LOAD_RESPONSE_ITEMID);
BitstreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::BLUEPRINT_LOAD_RESPONSE_ITEMID);
bitStream.Write(static_cast<uint8_t>(success));
bitStream.Write<LWOOBJID>(oldItemId);
bitStream.Write<LWOOBJID>(newItemId);
@@ -2679,7 +2680,7 @@ void GameMessages::HandleBBBSaveRequest(RakNet::BitStream* inStream, Entity* ent
//Tell the client their model is saved: (this causes us to actually pop out of our current state):
CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::BLUEPRINT_SAVE_RESPONSE);
BitstreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::BLUEPRINT_SAVE_RESPONSE);
bitStream.Write(localId);
bitStream.Write(eBlueprintSaveResponseType::EverythingWorked);
bitStream.Write<uint32_t>(1);

View File

@@ -283,7 +283,7 @@ void Mail::HandleDataRequest(RakNet::BitStream* packet, const SystemAddress& sys
sql::ResultSet* res = stmt->executeQuery();
RakNet::BitStream bitStream;
PacketUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::MAIL);
BitstreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::MAIL);
bitStream.Write(int(MailMessageID::MailData));
bitStream.Write(int(0));
@@ -406,7 +406,7 @@ void Mail::HandleNotificationRequest(const SystemAddress& sysAddr, uint32_t obje
void Mail::SendSendResponse(const SystemAddress& sysAddr, MailSendResponse response) {
RakNet::BitStream bitStream;
PacketUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::MAIL);
BitstreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::MAIL);
bitStream.Write(int(MailMessageID::SendResponse));
bitStream.Write(int(response));
Game::server->Send(&bitStream, sysAddr, false);
@@ -414,7 +414,7 @@ void Mail::SendSendResponse(const SystemAddress& sysAddr, MailSendResponse respo
void Mail::SendNotification(const SystemAddress& sysAddr, int mailCount) {
RakNet::BitStream bitStream;
PacketUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::MAIL);
BitstreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::MAIL);
uint64_t messageType = 2;
uint64_t s1 = 0;
uint64_t s2 = 0;
@@ -433,7 +433,7 @@ void Mail::SendNotification(const SystemAddress& sysAddr, int mailCount) {
void Mail::SendAttachmentRemoveConfirm(const SystemAddress& sysAddr, uint64_t mailID) {
RakNet::BitStream bitStream;
PacketUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::MAIL);
BitstreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::MAIL);
bitStream.Write(int(MailMessageID::AttachmentCollectConfirm));
bitStream.Write(int(0)); //unknown
bitStream.Write(mailID);
@@ -442,7 +442,7 @@ void Mail::SendAttachmentRemoveConfirm(const SystemAddress& sysAddr, uint64_t ma
void Mail::SendDeleteConfirm(const SystemAddress& sysAddr, uint64_t mailID, LWOOBJID playerID) {
RakNet::BitStream bitStream;
PacketUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::MAIL);
BitstreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::MAIL);
bitStream.Write(int(MailMessageID::MailDeleteConfirm));
bitStream.Write(int(0)); //unknown
bitStream.Write(mailID);
@@ -456,7 +456,7 @@ void Mail::SendDeleteConfirm(const SystemAddress& sysAddr, uint64_t mailID, LWOO
void Mail::SendReadConfirm(const SystemAddress& sysAddr, uint64_t mailID) {
RakNet::BitStream bitStream;
PacketUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::MAIL);
BitstreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::MAIL);
bitStream.Write(int(MailMessageID::MailReadConfirm));
bitStream.Write(int(0)); //unknown
bitStream.Write(mailID);

View File

@@ -49,7 +49,7 @@
#include "dpWorld.h"
#include "Item.h"
#include "PropertyManagementComponent.h"
#include "PacketUtils.h"
#include "BitstreamUtils.h"
#include "Loot.h"
#include "EntityInfo.h"
#include "LUTriggers.h"
@@ -761,7 +761,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
if (chatCommand == "shutdownuniverse" && entity->GetGMLevel() == eGameMasterLevel::OPERATOR) {
//Tell the master server that we're going to be shutting down whole "universe":
CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::SHUTDOWN_UNIVERSE);
BitstreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::SHUTDOWN_UNIVERSE);
Game::server->SendToMaster(&bitStream);
ChatPackets::SendSystemMessage(sysAddr, u"Sent universe shutdown notification to master.");
@@ -1092,7 +1092,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
//Notify chat about it
CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::MUTE_UPDATE);
BitstreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::MUTE_UPDATE);
bitStream.Write(characterId);
bitStream.Write(expire);
@@ -2013,7 +2013,7 @@ void SlashCommandHandler::SendAnnouncement(const std::string& title, const std::
//Notify chat about it
CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ANNOUNCEMENT);
BitstreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ANNOUNCEMENT);
bitStream.Write<uint32_t>(title.size());
for (auto character : title) {