diff --git a/dChatServer/ChatPacketHandler.cpp b/dChatServer/ChatPacketHandler.cpp index 5722dfa0..ff22c2ef 100644 --- a/dChatServer/ChatPacketHandler.cpp +++ b/dChatServer/ChatPacketHandler.cpp @@ -20,6 +20,7 @@ #include "eGameMasterLevel.h" #include "ChatPackets.h" #include "TeamContainer.h" +#include "eChatChannel.h" void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) { //Get from the packet which player we want to do something with: diff --git a/dChatServer/ChatPacketHandler.h b/dChatServer/ChatPacketHandler.h index 29a20587..eee80f1b 100644 --- a/dChatServer/ChatPacketHandler.h +++ b/dChatServer/ChatPacketHandler.h @@ -6,33 +6,7 @@ struct PlayerData; 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 eChatChannel : uint8_t; enum class eChatMessageResponseCode : uint8_t { SENT = 0, diff --git a/dCommon/dEnums/eChatChannel.h b/dCommon/dEnums/eChatChannel.h new file mode 100644 index 00000000..c6c43e9a --- /dev/null +++ b/dCommon/dEnums/eChatChannel.h @@ -0,0 +1,36 @@ +#pragma once + +#ifndef __ECHATCHANNEL__H__ +#define __ECHATCHANNEL__H__ + +#include + + + +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 +}; + +#endif //!__ECHATCHANNEL__H__ \ No newline at end of file diff --git a/dGame/UserManager.cpp b/dGame/UserManager.cpp index 85757ec1..bf9bbd62 100644 --- a/dGame/UserManager.cpp +++ b/dGame/UserManager.cpp @@ -8,7 +8,7 @@ #include "Game.h" #include "Logger.h" #include "User.h" -#include "WorldPackets.h" +#include "ClientPackets.h" #include "Character.h" #include "BitStream.h" #include "ObjectIDManager.h" @@ -308,13 +308,17 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet) if (!name.empty() && Database::Get()->IsNameInUse(name)) { LOG("AccountID: %i chose unavailable name: %s", u->GetAccountID(), name.c_str()); - WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::CUSTOM_NAME_IN_USE); + ClientPackets::CharacterCreationResponse response; + response.response = eCharacterCreationResponse::CUSTOM_NAME_IN_USE; + response.Send(sysAddr); return; } if (Database::Get()->IsNameInUse(predefinedName)) { LOG("AccountID: %i chose unavailable predefined name: %s", u->GetAccountID(), predefinedName.c_str()); - WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::PREDEFINED_NAME_IN_USE); + ClientPackets::CharacterCreationResponse response; + response.response = eCharacterCreationResponse::PREDEFINED_NAME_IN_USE; + response.Send(sysAddr); return; } @@ -328,7 +332,8 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet) ObjectIDManager::RequestPersistentID([=, this](uint32_t objectID) { if (Database::Get()->GetCharacterInfo(objectID)) { LOG("Character object id unavailable, check object_id_tracker!"); - WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::OBJECT_ID_UNAVAILABLE); + ClientPackets::CharacterCreationResponse response; + response.response = eCharacterCreationResponse::OBJECT_ID_UNAVAILABLE; return; } @@ -393,8 +398,10 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet) //Now finally insert our character xml: Database::Get()->InsertCharacterXml(objectID, xml.str()); - - WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::SUCCESS); + + ClientPackets::CharacterCreationResponse response; + response.response = eCharacterCreationResponse::SUCCESS; + response.Send(sysAddr); UserManager::RequestCharacterList(sysAddr); }); } @@ -419,10 +426,12 @@ void UserManager::DeleteCharacter(const SystemAddress& sysAddr, Packet* packet) CheckType::User, "User %i tried to delete a character that it does not own!", u->GetAccountID()); - + + ClientPackets::CharacterDeleteResponse response; if (!hasCharacter) { - WorldPackets::SendCharacterDeleteResponse(sysAddr, false); + response.success = false; } else { + response.success = true; LOG("Deleting character %i", charID); Database::Get()->DeleteCharacter(charID); @@ -430,9 +439,9 @@ void UserManager::DeleteCharacter(const SystemAddress& sysAddr, Packet* packet) BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::UNEXPECTED_DISCONNECT); bitStream.Write(objectID); Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false); - - WorldPackets::SendCharacterDeleteResponse(sysAddr, true); } + response.Send(sysAddr); + } void UserManager::RenameCharacter(const SystemAddress& sysAddr, Packet* packet) { @@ -473,32 +482,34 @@ void UserManager::RenameCharacter(const SystemAddress& sysAddr, Packet* packet) return false; }); + ClientPackets::CharacterRenameResponse response; if (!ownsCharacter || !character) { - WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::UNKNOWN_ERROR); + response.response = eRenameResponse::UNKNOWN_ERROR; + } else if (ownsCharacter && character) { if (newName == character->GetName()) { - WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::NAME_UNAVAILABLE); - return; - } - - if (!Database::Get()->GetCharacterInfo(newName)) { + response.response = eRenameResponse::NAME_UNAVAILABLE; + + } else if (!Database::Get()->GetCharacterInfo(newName)) { if (IsNamePreapproved(newName)) { Database::Get()->SetCharacterName(charID, newName); LOG("Character %s now known as %s", character->GetName().c_str(), newName.c_str()); - WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::SUCCESS); - UserManager::RequestCharacterList(sysAddr); + response.response = eRenameResponse::SUCCESS; } else { Database::Get()->SetPendingCharacterName(charID, newName); LOG("Character %s has been renamed to %s and is pending approval by a moderator.", character->GetName().c_str(), newName.c_str()); - WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::SUCCESS); - UserManager::RequestCharacterList(sysAddr); + response.response = eRenameResponse::SUCCESS; } } else { - WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::NAME_IN_USE); + response.response = eRenameResponse::NAME_IN_USE; } } else { LOG("Unknown error occurred when renaming character, either hasCharacter or character variable != true."); - WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::UNKNOWN_ERROR); + response.response = eRenameResponse::UNKNOWN_ERROR; + } + response.Send(sysAddr); + if (response.response == eRenameResponse::SUCCESS) { + UserManager::RequestCharacterList(sysAddr); } } @@ -537,7 +548,11 @@ void UserManager::LoginCharacter(const SystemAddress& sysAddr, uint32_t playerID character->SetZoneInstance(zoneInstance); character->SetZoneClone(zoneClone); } - WorldPackets::SendTransferToWorld(sysAddr, serverIP, serverPort, mythranShift); + ClientPackets::TransferToWorld response; + response.serverIP = serverIP; + response.serverPort = serverPort; + response.mythranShift = mythranShift; + response.Send(sysAddr); return; }); } else { diff --git a/dGame/dBehaviors/PropertyTeleportBehavior.cpp b/dGame/dBehaviors/PropertyTeleportBehavior.cpp index bfbcd163..07470f8f 100644 --- a/dGame/dBehaviors/PropertyTeleportBehavior.cpp +++ b/dGame/dBehaviors/PropertyTeleportBehavior.cpp @@ -5,7 +5,7 @@ #include "Character.h" #include "CharacterComponent.h" #include "ChatPackets.h" -#include "WorldPackets.h" +#include "ClientPackets.h" #include "EntityManager.h" #include "Game.h" #include "ZoneInstanceManager.h" @@ -55,7 +55,11 @@ void PropertyTeleportBehavior::Handle(BehaviorContext* context, RakNet::BitStrea entity->GetCharacter()->SaveXMLToDatabase(); - WorldPackets::SendTransferToWorld(sysAddr, serverIP, serverPort, mythranShift); + ClientPackets::TransferToWorld response; + response.serverIP = serverIP; + response.serverPort = serverPort; + response.mythranShift = mythranShift; + response.Send(sysAddr); return; }); } diff --git a/dGame/dComponents/ActivityComponent.cpp b/dGame/dComponents/ActivityComponent.cpp index 0e6778b0..06e22c50 100644 --- a/dGame/dComponents/ActivityComponent.cpp +++ b/dGame/dComponents/ActivityComponent.cpp @@ -7,7 +7,7 @@ #include "ZoneInstanceManager.h" #include "Game.h" #include "Logger.h" -#include "WorldPackets.h" +#include "ClientPackets.h" #include "EntityManager.h" #include "ChatPackets.h" #include "BitStreamUtils.h" @@ -537,7 +537,11 @@ void ActivityInstance::StartZone() { player->GetCharacter()->SetZoneClone(zoneClone); } - WorldPackets::SendTransferToWorld(player->GetSystemAddress(), serverIP, serverPort, mythranShift); + ClientPackets::TransferToWorld response; + response.serverIP = serverIP; + response.serverPort = serverPort; + response.mythranShift = mythranShift; + response.Send(player->GetSystemAddress()); return; }); } diff --git a/dGame/dComponents/CharacterComponent.cpp b/dGame/dComponents/CharacterComponent.cpp index 0659e743..90cd971b 100644 --- a/dGame/dComponents/CharacterComponent.cpp +++ b/dGame/dComponents/CharacterComponent.cpp @@ -21,7 +21,7 @@ #include "CDRewardCodesTable.h" #include "Mail.h" #include "ZoneInstanceManager.h" -#include "WorldPackets.h" +#include "ClientPackets.h" #include "MessageType/Game.h" #include @@ -868,8 +868,11 @@ void CharacterComponent::SendToZone(LWOMAPID zoneId, LWOCLONEID cloneId) const { character->SaveXMLToDatabase(); } - WorldPackets::SendTransferToWorld(sysAddr, serverIP, serverPort, mythranShift); - + ClientPackets::TransferToWorld response; + response.serverIP = serverIP; + response.serverPort = serverPort; + response.mythranShift = mythranShift; + response.Send(sysAddr); Game::entityManager->DestructEntity(entity); }); } diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index 94bacfcf..cf5b5098 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -17,7 +17,7 @@ #include "CppScripts.h" #include "UserManager.h" #include "ZoneInstanceManager.h" -#include "WorldPackets.h" +#include "ClientPackets.h" #include "Item.h" #include "ZCompression.h" #include "dConfig.h" @@ -4936,7 +4936,11 @@ void GameMessages::HandleFireEventServerSide(RakNet::BitStream& inStream, Entity character->SetZoneClone(zoneClone); } - WorldPackets::SendTransferToWorld(sysAddr, serverIP, serverPort, mythranShift); + ClientPackets::TransferToWorld response; + response.serverIP = serverIP; + response.serverPort = serverPort; + response.mythranShift = mythranShift; + response.Send(sysAddr); return; }); } diff --git a/dGame/dUtilities/SlashCommands/DEVGMCommands.cpp b/dGame/dUtilities/SlashCommands/DEVGMCommands.cpp index 1ea1f8ed..ed05bf7b 100644 --- a/dGame/dUtilities/SlashCommands/DEVGMCommands.cpp +++ b/dGame/dUtilities/SlashCommands/DEVGMCommands.cpp @@ -17,7 +17,7 @@ #include "UserManager.h" #include "User.h" #include "VanityUtilities.h" -#include "WorldPackets.h" +#include "ClientPackets.h" #include "ZoneInstanceManager.h" // Database @@ -73,7 +73,13 @@ namespace DEVGMCommands { bool success = user->GetMaxGMLevel() >= level; if (success) { - WorldPackets::SendGMLevelChange(entity->GetSystemAddress(), success, user->GetMaxGMLevel(), entity->GetGMLevel(), level); + ClientPackets::GMLevelChange response; + response.success = success; + response.prevLevel = entity->GetGMLevel(); + response.newLevel = level; + response.highestLevel = user->GetMaxGMLevel(); + response.Send(entity->GetSystemAddress()); + GameMessages::SendChatModeUpdate(entity->GetObjectID(), level); entity->SetGMLevel(level); LOG("User %s (%i) has changed their GM level to %i for charID %llu", user->GetUsername().c_str(), user->GetAccountID(), level, entity->GetObjectID()); @@ -81,7 +87,13 @@ namespace DEVGMCommands { #ifndef DEVELOPER_SERVER if ((entity->GetGMLevel() > user->GetMaxGMLevel()) || (entity->GetGMLevel() > eGameMasterLevel::CIVILIAN && user->GetMaxGMLevel() == eGameMasterLevel::JUNIOR_DEVELOPER)) { - WorldPackets::SendGMLevelChange(entity->GetSystemAddress(), true, user->GetMaxGMLevel(), entity->GetGMLevel(), eGameMasterLevel::CIVILIAN); + ClientPackets::GMLevelChange response; + response.success = false; + response.prevLevel = entity->GetGMLevel(); + response.newLevel = eGameMasterLevel::CIVILIAN; + response.highestLevel = user->GetMaxGMLevel(); + response.Send(entity->GetSystemAddress()); + GameMessages::SendChatModeUpdate(entity->GetObjectID(), eGameMasterLevel::CIVILIAN); entity->SetGMLevel(eGameMasterLevel::CIVILIAN); @@ -1061,7 +1073,11 @@ namespace DEVGMCommands { entity->GetCharacter()->SaveXMLToDatabase(); - WorldPackets::SendTransferToWorld(sysAddr, serverIP, serverPort, mythranShift); + ClientPackets::TransferToWorld response; + response.serverIP = serverIP; + response.serverPort = serverPort; + response.mythranShift = mythranShift; + response.Send(sysAddr); return; }); } else { diff --git a/dGame/dUtilities/SlashCommands/GMZeroCommands.cpp b/dGame/dUtilities/SlashCommands/GMZeroCommands.cpp index 9e0bb475..eba13795 100644 --- a/dGame/dUtilities/SlashCommands/GMZeroCommands.cpp +++ b/dGame/dUtilities/SlashCommands/GMZeroCommands.cpp @@ -10,7 +10,7 @@ #include "PlayerManager.h" #include "SlashCommandHandler.h" #include "VanityUtilities.h" -#include "WorldPackets.h" +#include "ClientPackets.h" #include "ZoneInstanceManager.h" #include "Database.h" @@ -179,7 +179,11 @@ namespace GMZeroCommands { entity->GetCharacter()->SaveXMLToDatabase(); - WorldPackets::SendTransferToWorld(sysAddr, serverIP, serverPort, mythranShift); + ClientPackets::TransferToWorld response; + response.serverIP = serverIP; + response.serverPort = serverPort; + response.mythranShift = mythranShift; + response.Send(sysAddr); }); } @@ -205,7 +209,11 @@ namespace GMZeroCommands { entity->GetCharacter()->SaveXMLToDatabase(); - WorldPackets::SendTransferToWorld(sysAddr, serverIP, serverPort, mythranShift); + ClientPackets::TransferToWorld response; + response.serverIP = serverIP; + response.serverPort = serverPort; + response.mythranShift = mythranShift; + response.Send(sysAddr); }); } diff --git a/dNet/ClientPackets.cpp b/dNet/ClientPackets.cpp index a6b9f8c6..016884f6 100644 --- a/dNet/ClientPackets.cpp +++ b/dNet/ClientPackets.cpp @@ -6,6 +6,139 @@ #include "ClientPackets.h" #include "dCommonVars.h" #include "PositionUpdate.h" +#include "LDFFormat.h" +#include "ZCompression.h" + +namespace ClientPackets { + void LoadStaticZone::Serialize(RakNet::BitStream &bitStream) const { + bitStream.Write(zoneID.GetMapID()); + bitStream.Write(zoneID.GetInstanceID()); + bitStream.Write(checksum); + bitStream.Write(editorEnabled); + bitStream.Write(editorLevel); + bitStream.Write(position.x); + bitStream.Write(position.y); + bitStream.Write(position.z); + bitStream.Write(instanceType); + } + void CharacterCreationResponse::Serialize(RakNet::BitStream &bitStream) const { + bitStream.Write(response); + } + + void CharacterRenameResponse::Serialize(RakNet::BitStream &bitStream) const { + bitStream.Write(response); + } + + void CharacterDeleteResponse::Serialize(RakNet::BitStream &bitStream) const { + bitStream.Write(success); + } + + void TransferToWorld::Serialize(RakNet::BitStream &bitStream) const { + bitStream.Write(LUString(serverIP)); + bitStream.Write(serverPort); + bitStream.Write(mythranShift); + } + + void ServerState::Serialize(RakNet::BitStream &bitStream) const { + bitStream.Write(serverReady); + } + void CreateCharacter::Serialize(RakNet::BitStream &bitStream) const { + + RakNet::BitStream data; + + data.Write(7); //LDF key count + std::unique_ptr> objidLDF(new LDFData(u"objid", objid)); + objidLDF->WriteToPacket(data); + + std::unique_ptr> templateIDLDF(new LDFData(u"template", templateID)); + templateIDLDF->WriteToPacket(data); + + std::unique_ptr> nameLDF(new LDFData(u"name", name)); + nameLDF->WriteToPacket(data); + + std::unique_ptr> gmlevelLDF(new LDFData(u"gmlevel", static_cast(gmLevel))); + gmlevelLDF->WriteToPacket(data); + + std::unique_ptr> chatModeLDF(new LDFData(u"chatmode", static_cast(chatMode))); + chatModeLDF->WriteToPacket(data); + + std::unique_ptr> xmlConfigData(new LDFData(u"xmlData", xmlData)); + xmlConfigData->WriteToPacket(data); + + std::unique_ptr> reputationLdf(new LDFData(u"reputation", reputation)); + reputationLdf->WriteToPacket(data); + + //Compress the data before sending: + const uint32_t reservedSize = ZCompression::GetMaxCompressedLength(data.GetNumberOfBytesUsed()); + uint8_t* compressedData = new uint8_t[reservedSize]; + + if (!compressedData) { + throw std::runtime_error("Failed to allocate memory for compressed data"); + } + + size_t size = ZCompression::Compress(data.GetData(), data.GetNumberOfBytesUsed(), compressedData, reservedSize); + + assert(size <= reservedSize); + + bitStream.Write(size + 9); //size of data + header bytes (8) + bitStream.Write(1); //compressed boolean, true + bitStream.Write(data.GetNumberOfBytesUsed()); + bitStream.Write(size); + + /** + * In practice, this warning serves no purpose for us. We allocate the max memory needed on the heap + * and then compress the data. In the off chance that the compression actually increases the size, + * an assertion is done to prevent bad data from being saved or sent. + */ + #pragma warning(disable:6385) // C6385 Reading invalid data from 'compressedData'. + bitStream.WriteAlignedBytes(compressedData, size); + #pragma warning(default:6385) + delete[] compressedData; + }; + + void ChatModerationString::Serialize(RakNet::BitStream &bitStream) const { + bitStream.Write(rejectedWords.empty()); // Accepted + bitStream.Write(0); // padding + + bitStream.Write(chatChannel); + bitStream.Write(chatMode); + + bitStream.Write(LUWString(receiver, 42)); + + for (auto it : rejectedWords) { + bitStream.Write(it.first); // start index + bitStream.Write(it.second); // length + } + + // Pad out the rest of the packet + // The client expects 64 items, so we need to write 64 - rejectedWords.size() empty items + for (int i = rejectedWords.size(); 64 > i; i++) { + bitStream.Write(0); + } + + } + + void GMLevelChange::Serialize(RakNet::BitStream &bitStream) const { + bitStream.Write(success); + bitStream.Write(static_cast(highestLevel)); + bitStream.Write(static_cast(prevLevel)); + bitStream.Write(static_cast(newLevel)); + } + + void DebugOutput::Serialize(RakNet::BitStream &bitStream) const { + bitStream.Write(data.size()); + bitStream.Write(data); + } + + void HTTPMonitorInfoResponse::Serialize(RakNet::BitStream &bitStream) const { + bitStream.Write(port); + bitStream.Write(openWeb); + bitStream.Write(supportsSum); + bitStream.Write(supportsDetail); + bitStream.Write(supportsWho); + bitStream.Write(supportsObjects); + } +} ChatMessage ClientPackets::HandleChatMessage(Packet* packet) { CINSTREAM_SKIP_HEADER; @@ -114,10 +247,3 @@ ChatModerationRequest ClientPackets::HandleChatModerationRequest(Packet* packet) return request; } - -int32_t ClientPackets::SendTop5HelpIssues(Packet* packet) { - CINSTREAM_SKIP_HEADER; - int32_t language = 0; - inStream.Read(language); - return language; -} diff --git a/dNet/ClientPackets.h b/dNet/ClientPackets.h index a7d2941b..0cd35245 100644 --- a/dNet/ClientPackets.h +++ b/dNet/ClientPackets.h @@ -8,6 +8,10 @@ #include #include +#include "BitStreamUtils.h" +#include "eGameMasterLevel.h" +#include "eChatChannel.h" +#include "dCommonVars.h" class PositionUpdate; @@ -26,11 +30,118 @@ struct ChatModerationRequest { std::string message; }; + +class User; +struct SystemAddress; +enum class eCharacterCreationResponse : uint8_t; +enum class eRenameResponse : uint8_t; + namespace ClientPackets { ChatMessage HandleChatMessage(Packet* packet); PositionUpdate HandleClientPositionUpdate(Packet* packet); ChatModerationRequest HandleChatModerationRequest(Packet* packet); - int32_t SendTop5HelpIssues(Packet* packet); + + struct LoadStaticZone : public LUBitStream { + LWOZONEID zoneID; + uint32_t checksum = 0; + bool editorEnabled = false; + uint8_t editorLevel = 0; + NiPoint3 position = NiPoint3Constant::ZERO; + uint32_t instanceType = 0; + + LoadStaticZone() : LUBitStream(eConnectionType::CLIENT, MessageType::Client::LOAD_STATIC_ZONE) {}; + void Serialize(RakNet::BitStream& bitStream) const override; + + }; + + struct CharacterCreationResponse : public LUBitStream { + eCharacterCreationResponse response; + + CharacterCreationResponse() : LUBitStream(eConnectionType::CLIENT, MessageType::Client::CHARACTER_CREATE_RESPONSE) {}; + void Serialize(RakNet::BitStream& bitStream) const override; + }; + + struct CharacterRenameResponse : public LUBitStream { + eRenameResponse response; + + CharacterRenameResponse() : LUBitStream(eConnectionType::CLIENT, MessageType::Client::CHARACTER_RENAME_RESPONSE) {}; + void Serialize(RakNet::BitStream& bitStream) const override; + }; + + struct CharacterDeleteResponse : public LUBitStream { + bool success; + + CharacterDeleteResponse() : LUBitStream(eConnectionType::CLIENT, MessageType::Client::DELETE_CHARACTER_RESPONSE) {}; + void Serialize(RakNet::BitStream& bitStream) const override; + }; + + struct TransferToWorld : public LUBitStream { + std::string serverIP; + uint32_t serverPort = 0; + bool mythranShift = false; + + TransferToWorld() : LUBitStream(eConnectionType::CLIENT, MessageType::Client::TRANSFER_TO_WORLD) {}; + void Serialize(RakNet::BitStream& bitStream) const override; + }; + + struct ServerState : public LUBitStream { + bool serverReady = false; + + ServerState() : LUBitStream(eConnectionType::CLIENT, MessageType::Client::SERVER_STATES) {}; + void Serialize(RakNet::BitStream& bitStream) const override; + }; + + struct CreateCharacter : public LUBitStream { + LWOOBJID objid = 0; + LOT templateID = 1; + std::u16string name; + eGameMasterLevel gmLevel = eGameMasterLevel::CIVILIAN; + int32_t chatMode = 0; + std::string xmlData; + int64_t reputation = 0; + + CreateCharacter() : LUBitStream(eConnectionType::CLIENT, MessageType::Client::CREATE_CHARACTER) {}; + void Serialize(RakNet::BitStream& bitStream) const override; + }; + + struct ChatModerationString : public LUBitStream { + eChatChannel chatChannel = eChatChannel::SYSTEMNOTIFY; + uint8_t chatMode = 0; + std::string receiver; + std::set> rejectedWords; + + ChatModerationString() : LUBitStream(eConnectionType::CLIENT, MessageType::Client::CHAT_MODERATION_STRING) {}; + void Serialize(RakNet::BitStream& bitStream) const override; + }; + + struct GMLevelChange : public LUBitStream { + bool success = false; + eGameMasterLevel highestLevel = eGameMasterLevel::CIVILIAN; + eGameMasterLevel prevLevel = eGameMasterLevel::CIVILIAN; + eGameMasterLevel newLevel = eGameMasterLevel::CIVILIAN; + + GMLevelChange() : LUBitStream(eConnectionType::CLIENT, MessageType::Client::MAKE_GM_RESPONSE) {}; + void Serialize(RakNet::BitStream& bitStream) const override; + }; + + struct DebugOutput : public LUBitStream { + std::string data; + + DebugOutput() : LUBitStream(eConnectionType::CLIENT, MessageType::Client::DEBUG_OUTPUT) {}; + void Serialize(RakNet::BitStream& bitStream) const override; + }; + + struct HTTPMonitorInfoResponse : public LUBitStream { + uint16_t port = 80; + bool openWeb = false; + bool supportsSum = false; + bool supportsDetail = false; + bool supportsWho = false; + bool supportsObjects = false; + + HTTPMonitorInfoResponse() : LUBitStream(eConnectionType::CLIENT, MessageType::Client::HTTP_MONITOR_INFO_RESPONSE) {}; + void Serialize(RakNet::BitStream& bitStream) const override; + }; }; #endif // CLIENTPACKETS_H diff --git a/dNet/WorldPackets.cpp b/dNet/WorldPackets.cpp index 512f2265..ebc760c5 100644 --- a/dNet/WorldPackets.cpp +++ b/dNet/WorldPackets.cpp @@ -1,186 +1,42 @@ #include "WorldPackets.h" -#include "dCommonVars.h" -#include "BitStream.h" -#include "GeneralUtils.h" -#include "Logger.h" -#include "Game.h" -#include "LDFFormat.h" -#include "dServer.h" -#include "ZCompression.h" -#include "eConnectionType.h" -#include "BitStreamUtils.h" +#include "Amf3.h" +#include "dConfig.h" +#include "GameMessages.h" +#include "Entity.h" -#include - -void HTTPMonitorInfo::Serialize(RakNet::BitStream &bitStream) const { - bitStream.Write(port); - bitStream.Write(openWeb); - bitStream.Write(supportsSum); - bitStream.Write(supportsDetail); - bitStream.Write(supportsWho); - bitStream.Write(supportsObjects); -} - -void WorldPackets::SendLoadStaticZone(const SystemAddress& sysAddr, float x, float y, float z, uint32_t checksum, LWOZONEID zone) { - RakNet::BitStream bitStream; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::LOAD_STATIC_ZONE); - - bitStream.Write(zone.GetMapID()); - bitStream.Write(zone.GetInstanceID()); - //bitStream.Write(zone.GetCloneID()); - bitStream.Write(0); - - bitStream.Write(checksum); - bitStream.Write(0); // ?? - - bitStream.Write(x); - bitStream.Write(y); - bitStream.Write(z); - - bitStream.Write(0); // Change this to eventually use 4 on activity worlds - - SEND_PACKET; -} - -void WorldPackets::SendCharacterCreationResponse(const SystemAddress& sysAddr, eCharacterCreationResponse response) { - RakNet::BitStream bitStream; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::CHARACTER_CREATE_RESPONSE); - bitStream.Write(response); - SEND_PACKET; -} - -void WorldPackets::SendCharacterRenameResponse(const SystemAddress& sysAddr, eRenameResponse response) { - RakNet::BitStream bitStream; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::CHARACTER_RENAME_RESPONSE); - bitStream.Write(response); - SEND_PACKET; -} - -void WorldPackets::SendCharacterDeleteResponse(const SystemAddress& sysAddr, bool response) { - RakNet::BitStream bitStream; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::DELETE_CHARACTER_RESPONSE); - bitStream.Write(response); - SEND_PACKET; -} - -void WorldPackets::SendTransferToWorld(const SystemAddress& sysAddr, const std::string& serverIP, uint32_t serverPort, bool mythranShift) { - RakNet::BitStream bitStream; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::TRANSFER_TO_WORLD); - - bitStream.Write(LUString(serverIP)); - bitStream.Write(serverPort); - bitStream.Write(mythranShift); - - SEND_PACKET; -} - -void WorldPackets::SendServerState(const SystemAddress& sysAddr) { - RakNet::BitStream bitStream; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::SERVER_STATES); - bitStream.Write(1); //If the server is receiving this request, it probably is ready anyway. - SEND_PACKET; -} - -void WorldPackets::SendCreateCharacter(const SystemAddress& sysAddr, int64_t reputation, LWOOBJID player, const std::string& xmlData, const std::u16string& username, eGameMasterLevel gm) { - RakNet::BitStream bitStream; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::CREATE_CHARACTER); - - RakNet::BitStream data; - data.Write(7); //LDF key count - - std::unique_ptr> objid(new LDFData(u"objid", player)); - std::unique_ptr> lot(new LDFData(u"template", 1)); - std::unique_ptr> xmlConfigData(new LDFData(u"xmlData", xmlData)); - std::unique_ptr> name(new LDFData(u"name", username)); - std::unique_ptr> gmlevel(new LDFData(u"gmlevel", static_cast(gm))); - std::unique_ptr> chatmode(new LDFData(u"chatmode", static_cast(gm))); - std::unique_ptr> reputationLdf(new LDFData(u"reputation", reputation)); - - objid->WriteToPacket(data); - lot->WriteToPacket(data); - name->WriteToPacket(data); - gmlevel->WriteToPacket(data); - chatmode->WriteToPacket(data); - xmlConfigData->WriteToPacket(data); - reputationLdf->WriteToPacket(data); - - //Compress the data before sending: - const uint32_t reservedSize = ZCompression::GetMaxCompressedLength(data.GetNumberOfBytesUsed()); - uint8_t* compressedData = new uint8_t[reservedSize]; - - // TODO There should be better handling here for not enough memory... - if (!compressedData) return; - - size_t size = ZCompression::Compress(data.GetData(), data.GetNumberOfBytesUsed(), compressedData, reservedSize); - - assert(size <= reservedSize); - - bitStream.Write(size + 9); //size of data + header bytes (8) - bitStream.Write(1); //compressed boolean, true - bitStream.Write(data.GetNumberOfBytesUsed()); - bitStream.Write(size); - - /** - * In practice, this warning serves no purpose for us. We allocate the max memory needed on the heap - * and then compress the data. In the off chance that the compression actually increases the size, - * an assertion is done to prevent bad data from being saved or sent. - */ -#pragma warning(disable:6385) // C6385 Reading invalid data from 'compressedData'. - bitStream.WriteAlignedBytes(compressedData, size); -#pragma warning(default:6385) - - SEND_PACKET; - delete[] compressedData; - LOG("Sent CreateCharacter for ID: %llu", player); -} - -void WorldPackets::SendChatModerationResponse(const SystemAddress& sysAddr, bool requestAccepted, uint32_t requestID, const std::string& receiver, std::set> unacceptedItems) { - CBITSTREAM; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::CHAT_MODERATION_STRING); - - bitStream.Write(unacceptedItems.empty()); // Is sentence ok? - bitStream.Write(0x16); // Source ID, unknown - - bitStream.Write(requestID); // request ID - bitStream.Write(0); // chat mode - - bitStream.Write(LUWString(receiver, 42)); // receiver name - - for (auto it : unacceptedItems) { - bitStream.Write(it.first); // start index - bitStream.Write(it.second); // length +namespace WorldPackets { + + bool UIHelpTop5::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(languageCode)); + return true; } - for (int i = unacceptedItems.size(); 64 > i; i++) { - bitStream.Write(0); + void UIHelpTop5::Handle() { + AMFArrayValue data; + switch (languageCode) { + case eLanguageCodeID::EN_US: + // Summaries + data.Insert("Summary0", Game::config->GetValue("help_0_summary")); + data.Insert("Summary1", Game::config->GetValue("help_1_summary")); + data.Insert("Summary2", Game::config->GetValue("help_2_summary")); + data.Insert("Summary3", Game::config->GetValue("help_3_summary")); + data.Insert("Summary4", Game::config->GetValue("help_4_summary")); + + // Descriptions + data.Insert("Description0", Game::config->GetValue("help_0_description")); + data.Insert("Description1", Game::config->GetValue("help_1_description")); + data.Insert("Description2", Game::config->GetValue("help_2_description")); + data.Insert("Description3", Game::config->GetValue("help_3_description")); + data.Insert("Description4", Game::config->GetValue("help_4_description")); + break; + case eLanguageCodeID::PL_US: + case eLanguageCodeID::DE_DE: + case eLanguageCodeID::EN_GB: + default: + break; + } + GameMessages::SendUIMessageServerToSingleClient(player, sysAddr, "UIHelpTop5", data); } + - SEND_PACKET; -} - -void WorldPackets::SendGMLevelChange(const SystemAddress& sysAddr, bool success, eGameMasterLevel highestLevel, eGameMasterLevel prevLevel, eGameMasterLevel newLevel) { - CBITSTREAM; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::MAKE_GM_RESPONSE); - - bitStream.Write(success); - bitStream.Write(static_cast(highestLevel)); - bitStream.Write(static_cast(prevLevel)); - bitStream.Write(static_cast(newLevel)); - - SEND_PACKET; -} - -void WorldPackets::SendHTTPMonitorInfo(const SystemAddress& sysAddr, const HTTPMonitorInfo& info) { - CBITSTREAM; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::HTTP_MONITOR_INFO_RESPONSE); - info.Serialize(bitStream); - SEND_PACKET; -} - -void WorldPackets::SendDebugOuput(const SystemAddress& sysAddr, const std::string& data){ - CBITSTREAM; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::DEBUG_OUTPUT); - bitStream.Write(data.size()); - bitStream.Write(data); - SEND_PACKET; } diff --git a/dNet/WorldPackets.h b/dNet/WorldPackets.h index 0da98604..31fe3549 100644 --- a/dNet/WorldPackets.h +++ b/dNet/WorldPackets.h @@ -2,40 +2,30 @@ #define WORLDPACKETS_H #include "dCommonVars.h" -#include -#include +#include "BitStreamUtils.h" +#include "MessageType/World.h" -class User; -struct SystemAddress; -enum class eGameMasterLevel : uint8_t; -enum class eCharacterCreationResponse : uint8_t; -enum class eRenameResponse : uint8_t; -namespace RakNet { - class BitStream; -}; - -struct HTTPMonitorInfo { - uint16_t port = 80; - bool openWeb = false; - bool supportsSum = false; - bool supportsDetail = false; - bool supportsWho = false; - bool supportsObjects = false; - void Serialize(RakNet::BitStream &bitstream) const; +class Entity; +enum class eLanguageCodeID : int32_t { + EN_US = 0, + PL_US = 1, + DE_DE = 2, + EN_GB = 3 }; namespace WorldPackets { - void SendLoadStaticZone(const SystemAddress& sysAddr, float x, float y, float z, uint32_t checksum, LWOZONEID zone); - void SendCharacterCreationResponse(const SystemAddress& sysAddr, eCharacterCreationResponse response); - void SendCharacterRenameResponse(const SystemAddress& sysAddr, eRenameResponse response); - void SendCharacterDeleteResponse(const SystemAddress& sysAddr, bool response); - void SendTransferToWorld(const SystemAddress& sysAddr, const std::string& serverIP, uint32_t serverPort, bool mythranShift); - void SendServerState(const SystemAddress& sysAddr); - void SendCreateCharacter(const SystemAddress& sysAddr, int64_t reputation, LWOOBJID player, const std::string& xmlData, const std::u16string& username, eGameMasterLevel gm); - void SendChatModerationResponse(const SystemAddress& sysAddr, bool requestAccepted, uint32_t requestID, const std::string& receiver, std::set> unacceptedItems); - void SendGMLevelChange(const SystemAddress& sysAddr, bool success, eGameMasterLevel highestLevel, eGameMasterLevel prevLevel, eGameMasterLevel newLevel); - void SendHTTPMonitorInfo(const SystemAddress& sysAddr, const HTTPMonitorInfo& info); - void SendDebugOuput(const SystemAddress& sysAddr, const std::string& data); + + struct UIHelpTop5: public LUBitStream { + eLanguageCodeID languageCode = eLanguageCodeID::EN_US; + + // should these be shoved up to the base class? + SystemAddress sysAddr = UNASSIGNED_SYSTEM_ADDRESS; + Entity* player = nullptr; + + UIHelpTop5() : LUBitStream(eConnectionType::WORLD, MessageType::World::UI_HELP_TOP_5) {}; + bool Deserialize(RakNet::BitStream& bitStream) override; + void Handle() override; + }; } #endif // WORLDPACKETS_H diff --git a/dWorldServer/WorldServer.cpp b/dWorldServer/WorldServer.cpp index 0f433493..30b71a9a 100644 --- a/dWorldServer/WorldServer.cpp +++ b/dWorldServer/WorldServer.cpp @@ -31,6 +31,7 @@ #include "AuthPackets.h" #include "BitStreamUtils.h" #include "WorldPackets.h" +#include "ClientPackets.h" #include "UserManager.h" #include "CDClientManager.h" #include "CDClientDatabase.h" @@ -715,18 +716,15 @@ void HandleMasterPacket(Packet* packet) { auto zone = Game::zoneManager->GetZone(); if (zone) { - float x = 0.0f; - float y = 0.0f; - float z = 0.0f; - + NiPoint3 pos = NiPoint3Constant::ZERO; if (zone->GetZoneID().GetMapID() == 1100) { - auto pos = zone->GetSpawnPos(); - x = pos.x; - y = pos.y; - z = pos.z; + pos = zone->GetSpawnPos(); } - WorldPackets::SendLoadStaticZone(it->second.sysAddr, x, y, z, zone->GetChecksum(), Game::zoneManager->GetZoneID()); + ClientPackets::LoadStaticZone load; + load.zoneID = Game::zoneManager->GetZoneID(); + load.checksum = zone->GetChecksum(); + load.position = pos; } if (Game::server->GetZoneID() == 0) { @@ -1040,8 +1038,17 @@ void HandlePacket(Packet* packet) { auto* characterComponent = player->GetComponent(); if (!characterComponent) return; - WorldPackets::SendCreateCharacter(packet->systemAddress, player->GetComponent()->GetReputation(), player->GetObjectID(), c->GetXMLData(), username, c->GetGMLevel()); - WorldPackets::SendServerState(packet->systemAddress); + ClientPackets::CreateCharacter createCharacter; + createCharacter.objid = player->GetObjectID(); + createCharacter.name = username; + createCharacter.gmLevel = c->GetGMLevel(); + createCharacter.xmlData = c->GetXMLData(); + createCharacter.reputation = player->GetComponent()->GetReputation(); + createCharacter.Send(packet->systemAddress); + + ClientPackets::ServerState serverState; + serverState.serverReady = true; + serverState.Send(packet->systemAddress); const auto respawnPoint = player->GetCharacter()->GetRespawnPoint(Game::zoneManager->GetZone()->GetWorldID()); @@ -1336,7 +1343,10 @@ void HandlePacket(Packet* packet) { } user->SetLastChatMessageApproved(bAllClean); - WorldPackets::SendChatModerationResponse(packet->systemAddress, bAllClean, request.requestID, request.receiver, segments); + ClientPackets::ChatModerationString response; + response.receiver = request.receiver; + response.rejectedWords = segments; + response.Send(packet->systemAddress); break; } @@ -1392,13 +1402,8 @@ void HandlePacket(Packet* packet) { case MessageType::World::UI_HELP_TOP_5: { - auto language = ClientPackets::SendTop5HelpIssues(packet); - // TODO: Handle different languages in a nice way - // 0: en_US - // 1: pl_US - // 2: de_DE - // 3: en_GB - + WorldPackets::UIHelpTop5 help; + help.Deserialize(inStream); // TODO: Find a good home for the logic in this case. auto* user = UserManager::Instance()->GetUser(packet->systemAddress); if (!user) return; @@ -1406,23 +1411,10 @@ void HandlePacket(Packet* packet) { if (!character) return; auto* entity = character->GetEntity(); if (!entity) return; - - AMFArrayValue data; - // Summaries - data.Insert("Summary0", Game::config->GetValue("help_0_summary")); - data.Insert("Summary1", Game::config->GetValue("help_1_summary")); - data.Insert("Summary2", Game::config->GetValue("help_2_summary")); - data.Insert("Summary3", Game::config->GetValue("help_3_summary")); - data.Insert("Summary4", Game::config->GetValue("help_4_summary")); - - // Descriptions - data.Insert("Description0", Game::config->GetValue("help_0_description")); - data.Insert("Description1", Game::config->GetValue("help_1_description")); - data.Insert("Description2", Game::config->GetValue("help_2_description")); - data.Insert("Description3", Game::config->GetValue("help_3_description")); - data.Insert("Description4", Game::config->GetValue("help_4_description")); - - GameMessages::SendUIMessageServerToSingleClient(entity, packet->systemAddress, "UIHelpTop5", data); + help.sysAddr = packet->systemAddress; + help.player = entity; + help.Handle(); + break; }