From acdfd2212db869970eee153a1d0d8b31afde8e90 Mon Sep 17 00:00:00 2001 From: Aaron Kimbrell Date: Sun, 27 Sep 2026 00:49:30 -0500 Subject: [PATCH] refactor: remaining game messages as structs, switch removed Every game message still written or read by hand is now a NetGameMsg with Serialize and Deserialize, in per-domain files: - MovementMessages: teleport, platforms (resync and its request), orient to angle, node rotation lock, gravity scale, jetpack mode, control scheme, respawn checkpoint, rails (set/start, ready, cancel, arrived), mount inventory ID, dismount complete, possession ack, ghost reference override and position, camera cycling (eCameraTargetCyclingMode moves here). - ZoneMessages: player loaded (the old PLAYER_LOADED case), ready for updates, player ready, restore to post load stats, server done loading, invalid zone transfer list, zone summary display/dismissed, level processing complete, object world state, and the localized announcement WorldMigration wrote by hand. - PlayerMessages: chat mode, GM level, LEGO score, currency, reputation, GM invis, pickup currency, zone and player statistics, chat commands, bug reports, verify ack. - ObjectMessages: fire event client/server side, notify client (zone) object, notify object, script network vars, failed preconditions, terminate interaction, set name, request use, request server object info. - QuickBuildMessages: notify state, enable, cancel. - ActivityMessages gains match response/update/request, leaderboard request and data, shooting gallery score/rotation/fire, activity state change. - MissionMessages gains MissionDialogueCancelled (a no-op, as before). The wire structs left in GameMessages.h move to their domains (tooltip and emote to Effects, loot and item use to Inventory, model build to Building, behavior sound to Property, skill sets to Skill) and gain the missing direction. The dismount logic moves to PossessorComponent::OnDismountComplete. Every inbound message is registered in the GameMessageHandler map; the switch is gone, and GameMessages.cpp only holds the GameMsg/NetGameMsg base code. Call sites build the structs (NotifyClientObject, TerminateInteraction, Teleport, PlatformResync, FireEventClientSide, NotifyObject and NotifyClientZoneObject get convenience constructors like PlayFXEffect). Dead senders are dropped: SendSetShootingGalleryParams (no callers, field order was a guess), SendTeamPickupItem (the struct already existed), SendRequestActivitySummaryLeaderboardData (the struct covers it). Verified with RemainingMessagesTests: every old Send* function is frozen verbatim in Legacy/RemainingMessagesLegacy.h and compared byte for byte (same bits, destination and broadcast flag) over grids of inputs; every old Handle* read sequence is frozen as a Read* oracle and compared with the struct's Deserialize; round trips, truncation and a golden packet. PlayerLoaded (0x00dc36f0), SetGMLevel (0x00dd6230), MissionDialogueCancelled (0x00d9cc10) and LocalizedAnnouncementServerToSingleClient (0x00f23c50) were checked against the client. Behaviour notes: an inbound message that fails to deserialize is dropped, so ParseChatMessage over MAX_MESSAGE_LENGTH is dropped instead of truncated, and PLAYER_LOADED / READY_FOR_UPDATES / MISSION_DIALOGUE_CANCELLED now read their (unused) client fields. Co-Authored-By: Claude Opus 5.5 --- dGame/Character.cpp | 12 +- dGame/Entity.cpp | 19 +- dGame/LeaderboardManager.cpp | 6 +- dGame/dBehaviors/JetPackBehavior.cpp | 18 +- dGame/dComponents/ActivityComponent.cpp | 51 +- dGame/dComponents/DestroyableComponent.cpp | 14 +- dGame/dComponents/GhostComponent.cpp | 1 + dGame/dComponents/GhostComponent.h | 1 + dGame/dComponents/InventoryComponent.cpp | 6 +- dGame/dComponents/ModelComponent.cpp | 1 + dGame/dComponents/ModelComponent.h | 1 + dGame/dComponents/PetComponent.cpp | 7 +- dGame/dComponents/PhantomPhysicsComponent.cpp | 12 +- dGame/dComponents/PossessorComponent.cpp | 44 +- dGame/dComponents/PossessorComponent.h | 7 + dGame/dComponents/QuickBuildComponent.cpp | 76 +- dGame/dComponents/RacingControlComponent.cpp | 20 +- dGame/dComponents/RailActivatorComponent.cpp | 35 +- .../RocketLaunchpadControlComponent.cpp | 9 +- dGame/dComponents/TriggerComponent.cpp | 11 +- dGame/dGameMessages/ActivityMessages.cpp | 187 ++ dGame/dGameMessages/ActivityMessages.h | 108 + dGame/dGameMessages/BuildingMessages.cpp | 33 + dGame/dGameMessages/BuildingMessages.h | 16 + dGame/dGameMessages/CMakeLists.txt | 7 +- dGame/dGameMessages/EffectsMessages.cpp | 37 + dGame/dGameMessages/EffectsMessages.h | 26 + dGame/dGameMessages/GameMessageDecoder.cpp | 3 + dGame/dGameMessages/GameMessageHandler.cpp | 259 +-- dGame/dGameMessages/GameMessages.cpp | 1774 +---------------- dGame/dGameMessages/GameMessages.h | 315 +-- dGame/dGameMessages/InventoryMessages.cpp | 91 + dGame/dGameMessages/InventoryMessages.h | 48 + dGame/dGameMessages/MissionMessages.cpp | 15 + dGame/dGameMessages/MissionMessages.h | 12 + dGame/dGameMessages/MovementMessages.cpp | 414 ++++ dGame/dGameMessages/MovementMessages.h | 282 +++ dGame/dGameMessages/ObjectMessages.cpp | 292 +++ dGame/dGameMessages/ObjectMessages.h | 188 ++ dGame/dGameMessages/PlayerMessages.cpp | 209 ++ dGame/dGameMessages/PlayerMessages.h | 149 ++ dGame/dGameMessages/PropertyMessages.cpp | 9 + dGame/dGameMessages/PropertyMessages.h | 9 + dGame/dGameMessages/QuickBuildMessages.cpp | 58 + dGame/dGameMessages/QuickBuildMessages.h | 48 + dGame/dGameMessages/SkillMessages.cpp | 15 + dGame/dGameMessages/SkillMessages.h | 10 + dGame/dGameMessages/ZoneMessages.cpp | 259 +++ dGame/dGameMessages/ZoneMessages.h | 112 ++ dGame/dMission/Mission.cpp | 12 +- dGame/dPropertyBehaviors/PropertyBehavior.cpp | 1 + dGame/dPropertyBehaviors/State.cpp | 1 + dGame/dPropertyBehaviors/Strip.cpp | 3 + dGame/dUtilities/DashboardActions.cpp | 9 +- dGame/dUtilities/Loot.cpp | 1 + dGame/dUtilities/Preconditions.cpp | 7 +- .../SlashCommands/DEVGMCommands.cpp | 35 +- .../GMGreaterThanZeroCommands.cpp | 43 +- dGame/dUtilities/VanityUtilities.cpp | 4 +- dGame/dUtilities/WorldMigration.cpp | 24 +- dNet/BitStreamUtils.h | 21 + dNet/PacketUtils.cpp | 18 - dNet/PacketUtils.h | 14 - .../Enemy/AG/BossSpiderQueenEnemyServer.cpp | 7 +- .../02_server/Enemy/AM/AmDarklingDragon.cpp | 3 +- .../02_server/Enemy/FV/FvMaelstromDragon.cpp | 3 +- .../02_server/Map/AG/AgCagedBricksServer.cpp | 3 +- .../02_server/Map/AG/NpcAgCourseStarter.cpp | 27 +- dScripts/02_server/Map/AG/NpcCowboyServer.cpp | 4 +- .../02_server/Map/AG/NpcNjAssistantServer.cpp | 3 +- dScripts/02_server/Map/AG/NpcWispServer.cpp | 4 +- .../Map/AG_Spider_Queen/ZoneAgSpiderQueen.cpp | 7 +- dScripts/02_server/Map/AM/AmDrawBridge.cpp | 3 +- dScripts/02_server/Map/AM/AmTeapotServer.cpp | 3 +- .../02_server/Map/FV/ImgBrickConsoleQB.cpp | 3 +- .../02_server/Map/GF/GfCaptainsCannon.cpp | 6 +- dScripts/02_server/Map/GF/GfTikiTorch.cpp | 4 +- dScripts/02_server/Map/GF/MastTeleport.cpp | 13 +- .../Map/General/BankInteractServer.cpp | 3 +- dScripts/02_server/Map/General/Binoculars.cpp | 3 +- .../General/ImaginationBackpackHealServer.cpp | 4 +- .../Map/General/PropertyPlatform.cpp | 7 +- .../Map/General/StoryBoxInteractServer.cpp | 3 +- .../Map/General/TokenConsoleServer.cpp | 3 +- .../Map/General/VisToggleNotifierServer.cpp | 3 +- .../Map/General/WishingWellServer.cpp | 7 +- .../02_server/Map/NS/NsTokenConsoleServer.cpp | 3 +- .../Map/NT/NTPipeVisibilityServer.cpp | 3 +- .../02_server/Map/NT/NtAssemblyTubeServer.cpp | 3 +- .../Map/NT/NtCombatChallengeServer.cpp | 7 +- .../Map/NT/NtImagimeterVisibility.cpp | 3 +- .../02_server/Map/NT/NtParadoxPanelServer.cpp | 9 +- .../02_server/Map/NT/NtParadoxTeleServer.cpp | 3 +- .../Map/NT/NtVentureCannonServer.cpp | 8 +- dScripts/02_server/Map/PR/HydrantBroken.cpp | 5 +- .../02_server/Map/PR/SpawnGryphonServer.cpp | 3 +- .../Map/Property/AG_Small/ZoneAgProperty.cpp | 20 +- .../Map/Property/PropertyBankInteract.cpp | 7 +- .../02_server/Map/VE/VeBricksampleServer.cpp | 3 +- dScripts/02_server/Map/VE/VeEpsilonServer.cpp | 3 +- .../02_server/Map/VE/VeMissionConsole.cpp | 5 +- .../Map/njhub/CatapultBaseServer.cpp | 3 +- .../Map/njhub/CatapultBouncerServer.cpp | 3 +- .../02_server/Map/njhub/CavePrisonCage.cpp | 3 +- dScripts/02_server/Map/njhub/NjWuNPC.cpp | 7 +- .../boss_instance/NjMonastryBossInstance.cpp | 68 +- .../Objects/AgSurvivalBuffStation.cpp | 1 + .../02_server/Pets/PetFromObjectServer.cpp | 4 +- dScripts/ActivityManager.cpp | 3 +- dScripts/BaseConsoleTeleportServer.cpp | 13 +- dScripts/BasePropertyServer.cpp | 25 +- dScripts/BaseSurvivalServer.cpp | 14 +- dScripts/BaseWavesServer.cpp | 17 +- dScripts/ChooseYourDestinationNsToNt.cpp | 3 +- dScripts/CppScripts.h | 4 + dScripts/Darkitect.cpp | 3 +- dScripts/NtFactionSpyServer.cpp | 5 +- dScripts/ScriptedPowerupSpawner.cpp | 1 + dScripts/SpawnPetBaseServer.cpp | 3 +- dScripts/ai/AG/AgBusDoor.cpp | 5 +- dScripts/ai/AG/AgImagSmashable.cpp | 1 + dScripts/ai/AG/AgJetEffectServer.cpp | 9 +- dScripts/ai/AG/AgPicnicBlanket.cpp | 4 +- dScripts/ai/AG/AgQbElevator.cpp | 10 +- dScripts/ai/AG/AgShipPlayerShockServer.cpp | 3 +- dScripts/ai/AG/AgTurret.cpp | 6 +- dScripts/ai/FV/FvConsoleLeftQuickbuild.cpp | 3 +- dScripts/ai/FV/FvConsoleRightQuickbuild.cpp | 3 +- dScripts/ai/FV/FvPandaSpawnerServer.cpp | 7 +- ...nceExitTransferPlayerToLastNonInstance.cpp | 3 +- .../ai/MINIGAME/SG_GF/SERVER/SGCannon.cpp | 17 +- .../ai/NS/NS_PP_01/PropertyDeathPlane.cpp | 3 +- dScripts/ai/NS/NsConcertInstrument.cpp | 16 +- dScripts/ai/NS/NsQbImaginationStatue.cpp | 1 + dScripts/ai/NS/WH/RockHydrantBroken.cpp | 5 +- dScripts/ai/PROPERTY/AgPropguards.cpp | 4 +- dWorldServer/WorldServer.cpp | 20 +- tests/dCommonTests/HeaderSkipTest.cpp | 37 - tests/dGameTests/LegacyPacketMacros.h | 41 + .../dGameMessagesTests/CMakeLists.txt | 1 + .../dGameMessagesTests/GameMsgSplitTests.cpp | 1 + .../Legacy/RemainingMessagesLegacy.h | 1230 ++++++++++++ .../RemainingMessagesTests.cpp | 724 +++++++ 143 files changed, 5376 insertions(+), 2698 deletions(-) create mode 100644 dGame/dGameMessages/MovementMessages.cpp create mode 100644 dGame/dGameMessages/MovementMessages.h create mode 100644 dGame/dGameMessages/ObjectMessages.cpp create mode 100644 dGame/dGameMessages/ObjectMessages.h create mode 100644 dGame/dGameMessages/PlayerMessages.cpp create mode 100644 dGame/dGameMessages/PlayerMessages.h create mode 100644 dGame/dGameMessages/QuickBuildMessages.cpp create mode 100644 dGame/dGameMessages/QuickBuildMessages.h create mode 100644 dGame/dGameMessages/ZoneMessages.cpp create mode 100644 dGame/dGameMessages/ZoneMessages.h delete mode 100644 dNet/PacketUtils.cpp delete mode 100644 dNet/PacketUtils.h delete mode 100644 tests/dCommonTests/HeaderSkipTest.cpp create mode 100644 tests/dGameTests/LegacyPacketMacros.h create mode 100644 tests/dGameTests/dGameMessagesTests/Legacy/RemainingMessagesLegacy.h create mode 100644 tests/dGameTests/dGameMessagesTests/RemainingMessagesTests.cpp diff --git a/dGame/Character.cpp b/dGame/Character.cpp index 7d321ecba..8e29d381b 100644 --- a/dGame/Character.cpp +++ b/dGame/Character.cpp @@ -12,6 +12,7 @@ #include "GameMessages.h" #include "EffectsMessages.h" #include "MissionMessages.h" +#include "PlayerMessages.h" #include "MissionComponent.h" #include "dZoneManager.h" #include "dServer.h" @@ -597,7 +598,16 @@ void Character::SetCoins(int64_t newCoins, eLootSourceType lootSource) { EconomyLedger::RecordCoins(m_ID, newCoins - m_Coins, lootSource); m_Coins = newCoins; - GameMessages::SendSetCurrency(Game::entityManager->GetEntity(m_ObjectID), m_Coins, 0, 0, 0, 0, true, lootSource); + auto* entity = Game::entityManager->GetEntity(m_ObjectID); + GameMessages::SetCurrency setCurrency; + setCurrency.target = entity->GetObjectID(); + setCurrency.currency = m_Coins; + setCurrency.lootType = 0; + setCurrency.sourceLOT = 0; + setCurrency.sourceID = 0; + setCurrency.sourceTradeID = 0; + setCurrency.sourceType = lootSource; + setCurrency.SendToClient(entity->GetSystemAddress()); } bool Character::HasBeenToWorld(LWOMAPID mapID) const { diff --git a/dGame/Entity.cpp b/dGame/Entity.cpp index 652e308d0..d4ca9a0df 100644 --- a/dGame/Entity.cpp +++ b/dGame/Entity.cpp @@ -14,6 +14,9 @@ #include "dServer.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "MovementMessages.h" +#include "ObjectMessages.h" +#include "PlayerMessages.h" #include "EntityManager.h" #include "dZoneManager.h" #include "Zone.h" @@ -107,6 +110,8 @@ #include "StringifiedEnum.h" #include +#include "InventoryMessages.h" +#include "ActivityMessages.h" Observable Entity::OnPlayerPositionUpdate; @@ -623,7 +628,7 @@ void Entity::Initialize() { // FV tree handler for when built so it sets the state to moving at the correct time if (GetLOT() == 9483) quickBuildComponent->AddQuickBuildCompleteCallback([objectID](Entity* user) { auto* const entity = Game::entityManager->GetEntity(objectID); - if (entity) GameMessages::SendPlatformResync(entity, UNASSIGNED_SYSTEM_ADDRESS, false, 0, 1, 1, eMovementPlatformState::Moving, true); + if (entity) GameMessages::PlatformResync(*entity, false, 0, 1, 1, eMovementPlatformState::Moving, true).Send(UNASSIGNED_SYSTEM_ADDRESS); }); const auto activityID = GetVar(u"activityID"); @@ -942,7 +947,10 @@ void Entity::SetGMLevel(eGameMasterLevel value) { characterComponent->SetGMLevel(value); - GameMessages::SendGMLevelBroadcast(m_ObjectID, value); + GameMessages::SetGMLevel setGMLevel; + setGMLevel.target = m_ObjectID; + setGMLevel.level = value; + setGMLevel.Send(UNASSIGNED_SYSTEM_ADDRESS); // Update the chat server of our GM Level { @@ -1300,7 +1308,7 @@ void Entity::OnCinematicUpdate(Entity* self, Entity* sender, eCinematicEvent eve } void Entity::NotifyObject(Entity* sender, const std::string& name, int32_t param1, int32_t param2) { - GameMessages::SendNotifyObject(GetObjectID(), sender->GetObjectID(), GeneralUtils::ASCIIToUTF16(name), UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyObject(GetObjectID(), sender->GetObjectID(), GeneralUtils::ASCIIToUTF16(name)).Send(UNASSIGNED_SYSTEM_ADDRESS); GetScript()->OnNotifyObject(this, sender, name, param1, param2); } @@ -1899,7 +1907,10 @@ std::vector Entity::GetTargetsInPhantom() { } void Entity::SendNetworkVar(const std::string& data, const SystemAddress& sysAddr) { - GameMessages::SendSetNetworkScriptVar(this, sysAddr, data); + GameMessages::ScriptNetworkVarUpdate scriptVar; + scriptVar.target = this->GetObjectID(); + scriptVar.tableOfVars = GeneralUtils::ASCIIToUTF16(data); + scriptVar.Send(sysAddr); } const LDFBaseData* const Entity::GetVarData(const std::u16string& name) const { diff --git a/dGame/LeaderboardManager.cpp b/dGame/LeaderboardManager.cpp index 12f258da0..202306e26 100644 --- a/dGame/LeaderboardManager.cpp +++ b/dGame/LeaderboardManager.cpp @@ -9,6 +9,7 @@ #include "Character.h" #include "Game.h" #include "GameMessages.h" +#include "ActivityMessages.h" #include "Logger.h" #include "dConfig.h" #include "CDClientManager.h" @@ -243,7 +244,10 @@ void Leaderboard::SetupLeaderboard(bool weekly) { void Leaderboard::Send(const LWOOBJID targetID) const { auto* player = Game::entityManager->GetEntity(relatedPlayer); if (player != nullptr) { - GameMessages::SendActivitySummaryLeaderboardData(targetID, this, player->GetSystemAddress()); + GameMessages::SendActivitySummaryLeaderboardData leaderboardData; + leaderboardData.target = targetID; + leaderboardData.leaderboard = this; + leaderboardData.SendToClient(player->GetSystemAddress()); } } diff --git a/dGame/dBehaviors/JetPackBehavior.cpp b/dGame/dBehaviors/JetPackBehavior.cpp index 78f25fcd4..61afc2385 100644 --- a/dGame/dBehaviors/JetPackBehavior.cpp +++ b/dGame/dBehaviors/JetPackBehavior.cpp @@ -2,6 +2,7 @@ #include "BehaviorBranchContext.h" #include "GameMessages.h" +#include "MovementMessages.h" #include "Character.h" @@ -9,7 +10,17 @@ void JetPackBehavior::Handle(BehaviorContext* context, RakNet::BitStream& bit_st auto* entity = Game::entityManager->GetEntity(branch.target); if (!entity) return; - GameMessages::SendSetJetPackMode(entity, true, this->m_BypassChecks, this->m_EnableHover, this->m_effectId, this->m_Airspeed, this->m_MaxAirspeed, this->m_VerticalVelocity, this->m_WarningEffectID); + GameMessages::SetJetPackMode jetPackMode; + jetPackMode.target = entity->GetObjectID(); + jetPackMode.bUse = true; + jetPackMode.bBypassChecks = this->m_BypassChecks; + jetPackMode.bDoHover = this->m_EnableHover; + jetPackMode.effectID = this->m_effectId; + jetPackMode.fAirspeed = this->m_Airspeed; + jetPackMode.fMaxAirspeed = this->m_MaxAirspeed; + jetPackMode.fVertVel = this->m_VerticalVelocity; + jetPackMode.iWarningEffectID = this->m_WarningEffectID; + jetPackMode.Send(UNASSIGNED_SYSTEM_ADDRESS); if (entity->IsPlayer()) { auto* character = entity->GetCharacter(); @@ -24,7 +35,10 @@ void JetPackBehavior::UnCast(BehaviorContext* context, BehaviorBranchContext bra auto* entity = Game::entityManager->GetEntity(branch.target); if (!entity) return; - GameMessages::SendSetJetPackMode(entity, false); + GameMessages::SetJetPackMode jetPackMode; + jetPackMode.target = entity->GetObjectID(); + jetPackMode.bUse = false; + jetPackMode.Send(UNASSIGNED_SYSTEM_ADDRESS); if (entity->IsPlayer()) { auto* character = entity->GetCharacter(); diff --git a/dGame/dComponents/ActivityComponent.cpp b/dGame/dComponents/ActivityComponent.cpp index 9a3b45153..a45ee4b8b 100644 --- a/dGame/dComponents/ActivityComponent.cpp +++ b/dGame/dComponents/ActivityComponent.cpp @@ -2,6 +2,7 @@ #include "ChatServerLink.h" #include "ChatPackets.h" #include "GameMessages.h" +#include "ActivityMessages.h" #include "CDClientManager.h" #include "MissionComponent.h" #include "Character.h" @@ -124,8 +125,12 @@ void ActivityComponent::PlayerJoin(Entity* player) { } void ActivityComponent::PlayerJoinLobby(Entity* player) { - if (!m_Parent->HasComponent(eReplicaComponentType::QUICK_BUILD)) - GameMessages::SendMatchResponse(player, player->GetSystemAddress(), 0); // tell the client they joined a lobby + if (!m_Parent->HasComponent(eReplicaComponentType::QUICK_BUILD)) { + GameMessages::MatchResponse matchResponse; + matchResponse.target = player->GetObjectID(); + matchResponse.response = 0; + matchResponse.SendToClient(player->GetSystemAddress()); // tell the client they joined a lobby + } LobbyPlayer newLobbyPlayer{}; newLobbyPlayer.entityID = player->GetObjectID(); LWOOBJID playerLobbyID = LWOOBJID_EMPTY; @@ -154,9 +159,17 @@ void ActivityComponent::PlayerJoinLobby(Entity* player) { LDFData entityLDF("player", entity->GetObjectID()); LDFData entityName("playerName", entity->GetCharacter()->GetName()); std::string matchUpdate = entityLDF.GetString() + "\n" + entityName.GetString(); - GameMessages::SendMatchUpdate(player, player->GetSystemAddress(), matchUpdate, eMatchUpdate::PLAYER_ADDED); + GameMessages::MatchUpdate existingPlayerUpdate; + existingPlayerUpdate.target = player->GetObjectID(); + existingPlayerUpdate.data = matchUpdate; + existingPlayerUpdate.type = eMatchUpdate::PLAYER_ADDED; + existingPlayerUpdate.SendToClient(player->GetSystemAddress()); PlayerReady(entity, joinedPlayer.ready); - GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchUpdateJoined, eMatchUpdate::PLAYER_ADDED); + GameMessages::MatchUpdate joinedPlayerUpdate; + joinedPlayerUpdate.target = entity->GetObjectID(); + joinedPlayerUpdate.data = matchUpdateJoined; + joinedPlayerUpdate.type = eMatchUpdate::PLAYER_ADDED; + joinedPlayerUpdate.SendToClient(entity->GetSystemAddress()); } break; } @@ -174,7 +187,11 @@ void ActivityComponent::PlayerJoinLobby(Entity* player) { if (m_ActivityInfo.maxTeamSize != 1 && lobby.players.size() >= m_ActivityInfo.minTeamSize || m_ActivityInfo.maxTeamSize == 1 && lobby.players.size() >= m_ActivityInfo.minTeams) { // Update the joining player on the match timer LDFData matchTimer("time", lobby.timer); - GameMessages::SendMatchUpdate(player, player->GetSystemAddress(), matchTimer.GetString(), eMatchUpdate::PHASE_WAIT_READY); + GameMessages::MatchUpdate matchUpdate; + matchUpdate.target = player->GetObjectID(); + matchUpdate.data = matchTimer.GetString(); + matchUpdate.type = eMatchUpdate::PHASE_WAIT_READY; + matchUpdate.SendToClient(player->GetSystemAddress()); } } @@ -190,7 +207,11 @@ void ActivityComponent::PlayerLeave(LWOOBJID playerID) { if (entity == nullptr) continue; - GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchUpdateLeft.GetString(), eMatchUpdate::PLAYER_REMOVED); + GameMessages::MatchUpdate matchUpdate; + matchUpdate.target = entity->GetObjectID(); + matchUpdate.data = matchUpdateLeft.GetString(); + matchUpdate.type = eMatchUpdate::PLAYER_REMOVED; + matchUpdate.SendToClient(entity->GetSystemAddress()); } lobby.players.erase(lobby.players.begin() + i); @@ -229,7 +250,11 @@ void ActivityComponent::Update(float deltaTime) { continue; LDFData matchTimerUpdate("time", lobby.timer); - GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchTimerUpdate.GetString(), eMatchUpdate::PHASE_WAIT_READY); + GameMessages::MatchUpdate matchUpdate; + matchUpdate.target = entity->GetObjectID(); + matchUpdate.data = matchTimerUpdate.GetString(); + matchUpdate.type = eMatchUpdate::PHASE_WAIT_READY; + matchUpdate.SendToClient(entity->GetSystemAddress()); } } @@ -254,7 +279,11 @@ void ActivityComponent::Update(float deltaTime) { if (entity == nullptr) continue; - GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchTimerUpdate.GetString(), eMatchUpdate::PHASE_WAIT_START); + GameMessages::MatchUpdate matchUpdate; + matchUpdate.target = entity->GetObjectID(); + matchUpdate.data = matchTimerUpdate.GetString(); + matchUpdate.type = eMatchUpdate::PHASE_WAIT_START; + matchUpdate.SendToClient(entity->GetSystemAddress()); } } @@ -349,7 +378,11 @@ void ActivityComponent::PlayerReady(Entity* player, bool bReady) { if (entity == nullptr) continue; - GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchReadyUpdate.GetString(), readyStatus); + GameMessages::MatchUpdate matchUpdate; + matchUpdate.target = entity->GetObjectID(); + matchUpdate.data = matchReadyUpdate.GetString(); + matchUpdate.type = readyStatus; + matchUpdate.SendToClient(entity->GetSystemAddress()); } } } diff --git a/dGame/dComponents/DestroyableComponent.cpp b/dGame/dComponents/DestroyableComponent.cpp index 927889e47..22c750434 100644 --- a/dGame/dComponents/DestroyableComponent.cpp +++ b/dGame/dComponents/DestroyableComponent.cpp @@ -14,6 +14,7 @@ #include "AmfSerialize.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "PlayerMessages.h" #include "User.h" #include "CDClientManager.h" #include "CDDestructibleComponentTable.h" @@ -47,6 +48,7 @@ #include #include "CDComponentsRegistryTable.h" +#include "InventoryMessages.h" Implementation DestroyableComponent::IsEnemyImplentation; Implementation DestroyableComponent::IsFriendImplentation; @@ -967,7 +969,11 @@ void DestroyableComponent::DoHardcoreModeDrops(const LWOOBJID source) { LOG("Player %llu has lost %llu uscore!", m_Parent->GetObjectID(), uscoreToLose); character->SetUScore(uscore - uscoreToLose, EconomyLedger::HARDCORE_DEATH_SOURCE); - GameMessages::SendModifyLEGOScore(m_Parent, m_Parent->GetSystemAddress(), -uscoreToLose, eLootSourceType::MISSION); + GameMessages::ModifyLEGOScore modifyScore; + modifyScore.target = m_Parent->GetObjectID(); + modifyScore.score = -uscoreToLose; + modifyScore.sourceType = eLootSourceType::MISSION; + modifyScore.SendToClient(m_Parent->GetSystemAddress()); if (Game::entityManager->GetHardcoreDropinventoryOnDeath()) { //drop all items from inventory: @@ -1051,7 +1057,11 @@ void DestroyableComponent::DoHardcoreModeDrops(const LWOOBJID source) { int uscore = maxHealth * Game::entityManager->GetHardcoreUscoreEnemiesMultiplier() * uscoreReduction; LOG("Rewarding player %llu with %i uscore for killing enemy %i", player->GetObjectID(), uscore, lot); playerStats->SetUScore(playerStats->GetUScore() + uscore, EconomyLedger::HARDCORE_KILL_SOURCE); - GameMessages::SendModifyLEGOScore(player, player->GetSystemAddress(), uscore, eLootSourceType::MISSION); + GameMessages::ModifyLEGOScore modifyScore; + modifyScore.target = player->GetObjectID(); + modifyScore.score = uscore; + modifyScore.sourceType = eLootSourceType::MISSION; + modifyScore.SendToClient(player->GetSystemAddress()); Game::entityManager->SerializeEntity(m_Parent); } diff --git a/dGame/dComponents/GhostComponent.cpp b/dGame/dComponents/GhostComponent.cpp index 6c0800b82..c202ff87f 100644 --- a/dGame/dComponents/GhostComponent.cpp +++ b/dGame/dComponents/GhostComponent.cpp @@ -7,6 +7,7 @@ #include "Amf3.h" #include "GameMessages.h" +#include "PlayerMessages.h" GhostComponent::GhostComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) { m_GhostReferencePoint = NiPoint3Constant::ZERO; diff --git a/dGame/dComponents/GhostComponent.h b/dGame/dComponents/GhostComponent.h index 2011943d6..c66916b59 100644 --- a/dGame/dComponents/GhostComponent.h +++ b/dGame/dComponents/GhostComponent.h @@ -4,6 +4,7 @@ #include "Component.h" #include "eReplicaComponentType.h" #include +#include "PlayerMessages.h" class NiPoint3; diff --git a/dGame/dComponents/InventoryComponent.cpp b/dGame/dComponents/InventoryComponent.cpp index 56f54a437..12b3c89e0 100644 --- a/dGame/dComponents/InventoryComponent.cpp +++ b/dGame/dComponents/InventoryComponent.cpp @@ -16,6 +16,7 @@ #include "InventoryMessages.h" #include "Database.h" #include "SkillMessages.h" +#include "MovementMessages.h" #include "SkillComponent.h" #include "Character.h" #include "EntityManager.h" @@ -1105,7 +1106,10 @@ void InventoryComponent::HandlePossession(Item* item) { // Set the mount item ID so that we know what we're handling possessorComponent->SetMountItemID(item->GetId()); - GameMessages::SendSetMountInventoryID(m_Parent, item->GetId(), UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::SetMountInventoryID mountInventoryID; + mountInventoryID.target = m_Parent->GetObjectID(); + mountInventoryID.inventoryMountID = item->GetId(); + mountInventoryID.Send(UNASSIGNED_SYSTEM_ADDRESS); // Create the mount entity EntityInfo info{}; diff --git a/dGame/dComponents/ModelComponent.cpp b/dGame/dComponents/ModelComponent.cpp index ffc325339..da24fa3fa 100644 --- a/dGame/dComponents/ModelComponent.cpp +++ b/dGame/dComponents/ModelComponent.cpp @@ -17,6 +17,7 @@ #include "Database.h" #include "DluAssert.h" +#include "ObjectMessages.h" ModelComponent::ModelComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) { m_OriginalPosition = m_Parent->GetDefaultPosition(); diff --git a/dGame/dComponents/ModelComponent.h b/dGame/dComponents/ModelComponent.h index fae2ada69..474fcbfd6 100644 --- a/dGame/dComponents/ModelComponent.h +++ b/dGame/dComponents/ModelComponent.h @@ -14,6 +14,7 @@ #include "PropertyBehavior.h" #include "PropertyBehaviorActions.h" #include "StripUiPosition.h" +#include "ObjectMessages.h" class AddMessage; class AMFArrayValue; diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index a504423ce..fe0c17448 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -4,6 +4,7 @@ #include "PetMessages.h" #include "InventoryMessages.h" #include "EffectsMessages.h" +#include "ObjectMessages.h" #include "BrickDatabase.h" #include "CDClientDatabase.h" #include "CDTamingBuildPuzzleTable.h" @@ -740,7 +741,7 @@ void PetComponent::RequestSetPetName(std::u16string name) { characterComponent->SetCurrentActivity(eGameActivity::NONE); Game::entityManager->SerializeEntity(tamer); } - GameMessages::SendTerminateInteraction(m_Tamer, eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID()); + GameMessages::TerminateInteraction(m_Tamer, eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID()).Send(UNASSIGNED_SYSTEM_ADDRESS); auto* modelEntity = Game::entityManager->GetEntity(m_ModelId); @@ -791,7 +792,7 @@ void PetComponent::ClientExitTamingMinigame(bool voluntaryExit) { msg.Send(tamer->GetSystemAddress()); } - GameMessages::SendTerminateInteraction(m_Tamer, eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID()); + GameMessages::TerminateInteraction(m_Tamer, eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID()).Send(UNASSIGNED_SYSTEM_ADDRESS); currentActivities.erase(m_Tamer); @@ -847,7 +848,7 @@ void PetComponent::ClientFailTamingMinigame() { msg.Send(tamer->GetSystemAddress()); } - GameMessages::SendTerminateInteraction(m_Tamer, eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID()); + GameMessages::TerminateInteraction(m_Tamer, eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID()).Send(UNASSIGNED_SYSTEM_ADDRESS); currentActivities.erase(m_Tamer); diff --git a/dGame/dComponents/PhantomPhysicsComponent.cpp b/dGame/dComponents/PhantomPhysicsComponent.cpp index 0ac8129d4..e367255b0 100644 --- a/dGame/dComponents/PhantomPhysicsComponent.cpp +++ b/dGame/dComponents/PhantomPhysicsComponent.cpp @@ -14,6 +14,7 @@ #include "EntityManager.h" #include "ControllablePhysicsComponent.h" #include "GameMessages.h" +#include "MovementMessages.h" #include "ePhysicsEffectType.h" #include "CDClientManager.h" @@ -157,7 +158,10 @@ void ApplyCollisionEffect(const LWOOBJID& target, const ePhysicsEffectType effec // dont want to apply an effect to nothing. if (!controllablePhysicsComponent) return; controllablePhysicsComponent->SetGravityScale(effectScale); - GameMessages::SendSetGravityScale(target, effectScale, targetEntity->GetSystemAddress()); + GameMessages::SetGravityScale gravityScale; + gravityScale.target = target; + gravityScale.scale = effectScale; + gravityScale.SendToClient(targetEntity->GetSystemAddress()); } break; } @@ -185,7 +189,11 @@ void PhantomPhysicsComponent::Update(float deltaTime) { auto* const entity = Game::entityManager->GetEntity(id); if (entity) { - GameMessages::SendPlayerReachedRespawnCheckpoint(entity, m_RespawnPos, m_RespawnRot); + GameMessages::PlayerReachedRespawnCheckpoint respawnCheckpoint; + respawnCheckpoint.target = entity->GetObjectID(); + respawnCheckpoint.pos = m_RespawnPos; + respawnCheckpoint.rot = m_RespawnRot; + respawnCheckpoint.SendToClient(entity->GetSystemAddress()); entity->SetRespawnPos(m_RespawnPos); entity->SetRespawnRot(m_RespawnRot); } diff --git a/dGame/dComponents/PossessorComponent.cpp b/dGame/dComponents/PossessorComponent.cpp index f4d5fa428..399c41f92 100644 --- a/dGame/dComponents/PossessorComponent.cpp +++ b/dGame/dComponents/PossessorComponent.cpp @@ -7,6 +7,9 @@ #include "RacingMessages.h" #include "eUnequippableActiveType.h" #include "InventoryMessages.h" +#include "MovementMessages.h" +#include "ControllablePhysicsComponent.h" +#include "SkillMessages.h" PossessorComponent::PossessorComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) { m_Possessable = LWOOBJID_EMPTY; @@ -63,7 +66,10 @@ void PossessorComponent::Mount(Entity* mount) { } // GM's to send - GameMessages::SendSetJetPackMode(m_Parent, false); + GameMessages::SetJetPackMode jetPackMode; + jetPackMode.target = m_Parent->GetObjectID(); + jetPackMode.bUse = false; + jetPackMode.Send(UNASSIGNED_SYSTEM_ADDRESS); if (mount->GetComponent()) { auto characterComponent = m_Parent->GetComponent(); if (characterComponent) characterComponent->SetIsRacing(true); @@ -105,3 +111,39 @@ void PossessorComponent::Dismount(Entity* mount, bool forceDismount) { } } } + +void PossessorComponent::OnDismountComplete(const LWOOBJID mountId) { + auto* mount = Game::entityManager->GetEntity(mountId); + // make sure we have the things we need and they aren't null + if (!mount) return; + if (!GetIsDismounting()) return; + SetIsDismounting(false); + SetPossessable(LWOOBJID_EMPTY); + SetPossessableType(ePossessionType::NO_POSSESSION); + + // character related things + auto* character = m_Parent->GetComponent(); + if (character) { + // If we had an active item turn it off + if (GetMountItemID() != LWOOBJID_EMPTY) { + GameMessages::MarkInventoryItemAsActive markActive; + markActive.target = m_Parent->GetObjectID(); + markActive.bActive = false; + markActive.iType = eUnequippableActiveType::MOUNT; + markActive.itemID = GetMountItemID(); + markActive.Send(m_Parent->GetSystemAddress()); + } + SetMountItemID(LWOOBJID_EMPTY); + } + + // Set that the controllabel phsyics comp is teleporting + auto* controllablePhysicsComponent = m_Parent->GetComponent(); + if (controllablePhysicsComponent) controllablePhysicsComponent->SetIsTeleporting(true); + + // Call dismoint on the possessable comp to let it handle killing the possessable + auto* possessableComponent = mount->GetComponent(); + if (possessableComponent) possessableComponent->Dismount(); + + // Update the entity that was possessing + Game::entityManager->SerializeEntity(m_Parent); +} diff --git a/dGame/dComponents/PossessorComponent.h b/dGame/dComponents/PossessorComponent.h index 8fb6ee3f2..05ac59595 100644 --- a/dGame/dComponents/PossessorComponent.h +++ b/dGame/dComponents/PossessorComponent.h @@ -40,6 +40,13 @@ public: */ void Dismount(Entity* mount, bool forceDismount = false); + /** + * @brief Handles the client's DismountComplete: finishes a dismount started by Dismount + * + * @param mountId The object ID of the mount the client finished dismounting + */ + void OnDismountComplete(const LWOOBJID mountId); + /** * Sets the ID that this entity is possessing * @param value The ID that this entity is possessing diff --git a/dGame/dComponents/QuickBuildComponent.cpp b/dGame/dComponents/QuickBuildComponent.cpp index f41cc72c5..698750596 100644 --- a/dGame/dComponents/QuickBuildComponent.cpp +++ b/dGame/dComponents/QuickBuildComponent.cpp @@ -4,6 +4,8 @@ #include "GameMessages.h" #include "EffectsMessages.h" #include "CombatMessages.h" +#include "ObjectMessages.h" +#include "QuickBuildMessages.h" #include "EntityManager.h" #include "Game.h" #include "Logger.h" @@ -406,8 +408,21 @@ void QuickBuildComponent::StartQuickBuild(Entity* const user) { Game::entityManager->SerializeEntity(user); - GameMessages::SendQuickBuildNotifyState(m_Parent, m_State, eQuickBuildState::BUILDING, user->GetObjectID()); - GameMessages::SendEnableQuickBuild(m_Parent, true, false, false, eQuickBuildFailReason::NOT_GIVEN, 0.0f, user->GetObjectID()); + GameMessages::RebuildNotifyState notifyState; + notifyState.target = m_Parent->GetObjectID(); + notifyState.prevState = m_State; + notifyState.state = eQuickBuildState::BUILDING; + notifyState.player = user->GetObjectID(); + notifyState.Send(UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::EnableRebuild enableRebuild; + enableRebuild.target = m_Parent->GetObjectID(); + enableRebuild.bEnable = true; + enableRebuild.bFail = false; + enableRebuild.bSuccess = false; + enableRebuild.eFailReason = eQuickBuildFailReason::NOT_GIVEN; + enableRebuild.fDuration = 0.0f; + enableRebuild.user = user->GetObjectID(); + enableRebuild.Send(UNASSIGNED_SYSTEM_ADDRESS); SetState(eQuickBuildState::BUILDING); Game::entityManager->SerializeEntity(m_Parent); @@ -442,12 +457,25 @@ void QuickBuildComponent::CompleteQuickBuild(Entity* const user) { Game::entityManager->SerializeEntity(user); - GameMessages::SendQuickBuildNotifyState(m_Parent, m_State, eQuickBuildState::COMPLETED, user->GetObjectID()); + GameMessages::RebuildNotifyState notifyState; + notifyState.target = m_Parent->GetObjectID(); + notifyState.prevState = m_State; + notifyState.state = eQuickBuildState::COMPLETED; + notifyState.player = user->GetObjectID(); + notifyState.Send(UNASSIGNED_SYSTEM_ADDRESS); GameMessages::PlayFXEffect fx(m_Parent->GetObjectID(), 507, u"create", "BrickFadeUpVisCompleteEffect"); fx.priority = 0.4f; fx.Send(UNASSIGNED_SYSTEM_ADDRESS); - GameMessages::SendEnableQuickBuild(m_Parent, false, false, true, eQuickBuildFailReason::NOT_GIVEN, m_ResetTime, user->GetObjectID()); - GameMessages::SendTerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID()); + GameMessages::EnableRebuild enableRebuild; + enableRebuild.target = m_Parent->GetObjectID(); + enableRebuild.bEnable = false; + enableRebuild.bFail = false; + enableRebuild.bSuccess = true; + enableRebuild.eFailReason = eQuickBuildFailReason::NOT_GIVEN; + enableRebuild.fDuration = m_ResetTime; + enableRebuild.user = user->GetObjectID(); + enableRebuild.Send(UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::TerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID()).Send(UNASSIGNED_SYSTEM_ADDRESS); SetState(eQuickBuildState::COMPLETED); @@ -522,14 +550,27 @@ void QuickBuildComponent::ResetQuickBuild(const bool failed) { Entity* builder = GetBuilder(); if (m_State == eQuickBuildState::BUILDING && builder) { - GameMessages::SendEnableQuickBuild(m_Parent, false, false, failed, eQuickBuildFailReason::NOT_GIVEN, m_ResetTime, builder->GetObjectID()); + GameMessages::EnableRebuild enableRebuild; + enableRebuild.target = m_Parent->GetObjectID(); + enableRebuild.bEnable = false; + enableRebuild.bFail = false; + enableRebuild.bSuccess = failed; + enableRebuild.eFailReason = eQuickBuildFailReason::NOT_GIVEN; + enableRebuild.fDuration = m_ResetTime; + enableRebuild.user = builder->GetObjectID(); + enableRebuild.Send(UNASSIGNED_SYSTEM_ADDRESS); if (failed) { RenderComponent::PlayAnimation(builder, u"rebuild-fail"); } } - GameMessages::SendQuickBuildNotifyState(m_Parent, m_State, eQuickBuildState::RESETTING, LWOOBJID_EMPTY); + GameMessages::RebuildNotifyState notifyState; + notifyState.target = m_Parent->GetObjectID(); + notifyState.prevState = m_State; + notifyState.state = eQuickBuildState::RESETTING; + notifyState.player = LWOOBJID_EMPTY; + notifyState.Send(UNASSIGNED_SYSTEM_ADDRESS); SetState(eQuickBuildState::RESETTING); SetTimer(0.0f); @@ -559,12 +600,25 @@ void QuickBuildComponent::CancelQuickBuild(Entity* const entity, const eQuickBui const auto entityID = entity != nullptr ? entity->GetObjectID() : LWOOBJID_EMPTY; // Notify the client that a state has changed - GameMessages::SendQuickBuildNotifyState(m_Parent, m_State, eQuickBuildState::INCOMPLETE, entityID); - GameMessages::SendEnableQuickBuild(m_Parent, false, true, false, failReason, m_Timer, entityID); + GameMessages::RebuildNotifyState notifyState; + notifyState.target = m_Parent->GetObjectID(); + notifyState.prevState = m_State; + notifyState.state = eQuickBuildState::INCOMPLETE; + notifyState.player = entityID; + notifyState.Send(UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::EnableRebuild enableRebuild; + enableRebuild.target = m_Parent->GetObjectID(); + enableRebuild.bEnable = false; + enableRebuild.bFail = true; + enableRebuild.bSuccess = false; + enableRebuild.eFailReason = failReason; + enableRebuild.fDuration = m_Timer; + enableRebuild.user = entityID; + enableRebuild.Send(UNASSIGNED_SYSTEM_ADDRESS); // Now terminate any interaction with the rebuild - GameMessages::SendTerminateInteraction(entityID, eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID()); - GameMessages::SendTerminateInteraction(m_Parent->GetObjectID(), eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID()); + GameMessages::TerminateInteraction(entityID, eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID()).Send(UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::TerminateInteraction(m_Parent->GetObjectID(), eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID()).Send(UNASSIGNED_SYSTEM_ADDRESS); // Now update the component itself SetState(eQuickBuildState::INCOMPLETE); diff --git a/dGame/dComponents/RacingControlComponent.cpp b/dGame/dComponents/RacingControlComponent.cpp index e86364e93..314ec2f0f 100644 --- a/dGame/dComponents/RacingControlComponent.cpp +++ b/dGame/dComponents/RacingControlComponent.cpp @@ -11,6 +11,7 @@ #include "CombatMessages.h" #include "ActivityMessages.h" #include "RacingMessages.h" +#include "MovementMessages.h" #include "InventoryComponent.h" #include "Item.h" #include "MissionComponent.h" @@ -152,8 +153,7 @@ void RacingControlComponent::LoadPlayerVehicle(Entity* player, // Make sure the player is at the correct position. - GameMessages::SendTeleport(player->GetObjectID(), startPosition, - startRotation, player->GetSystemAddress(), true); + GameMessages::Teleport(player->GetObjectID(), startPosition, startRotation, true).SendToClient(player->GetSystemAddress()); // Spawn the vehicle entity. @@ -265,7 +265,10 @@ void RacingControlComponent::LoadPlayerVehicle(Entity* player, resetPlayer.Send(UNASSIGNED_SYSTEM_ADDRESS); }); - GameMessages::SendSetJetPackMode(player, false); + GameMessages::SetJetPackMode jetPackMode; + jetPackMode.target = player->GetObjectID(); + jetPackMode.bUse = false; + jetPackMode.Send(UNASSIGNED_SYSTEM_ADDRESS); // Set the vehicle's state. GameMessages::NotifyVehicleOfRacingObject notifyVehicle; @@ -280,10 +283,8 @@ void RacingControlComponent::LoadPlayerVehicle(Entity* player, wheelLockState.Send(UNASSIGNED_SYSTEM_ADDRESS); // Make sure everything has the correct position. - GameMessages::SendTeleport(player->GetObjectID(), startPosition, - startRotation, player->GetSystemAddress(), true); - GameMessages::SendTeleport(carEntity->GetObjectID(), startPosition, - startRotation, player->GetSystemAddress(), true); + GameMessages::Teleport(player->GetObjectID(), startPosition, startRotation, true).SendToClient(player->GetSystemAddress()); + GameMessages::Teleport(carEntity->GetObjectID(), startPosition, startRotation, true).SendToClient(player->GetSystemAddress()); } void RacingControlComponent::OnRacingClientReady(Entity* player) { @@ -691,10 +692,7 @@ void RacingControlComponent::Update(float deltaTime) { Game::entityManager->GetEntity(player.playerID); if (vehicle != nullptr && playerEntity != nullptr) { - GameMessages::SendTeleport( - player.playerID, player.respawnPosition, - player.respawnRotation, - playerEntity->GetSystemAddress(), true); + GameMessages::Teleport(player.playerID, player.respawnPosition, player.respawnRotation, true).SendToClient(playerEntity->GetSystemAddress()); vehicle->SetPosition(player.respawnPosition); vehicle->SetRotation(player.respawnRotation); diff --git a/dGame/dComponents/RailActivatorComponent.cpp b/dGame/dComponents/RailActivatorComponent.cpp index 1df1b9917..6108e7f1e 100644 --- a/dGame/dComponents/RailActivatorComponent.cpp +++ b/dGame/dComponents/RailActivatorComponent.cpp @@ -6,6 +6,7 @@ #include "GameMessages.h" #include "EffectsMessages.h" #include "CombatMessages.h" +#include "MovementMessages.h" #include "QuickBuildComponent.h" #include "Game.h" #include "Logger.h" @@ -74,11 +75,24 @@ void RailActivatorComponent::OnUse(Entity* originator) { return; } - GameMessages::SendStartRailMovement(originator->GetObjectID(), m_Path, m_StartSound, - m_loopSound, m_StopSound, originator->GetSystemAddress(), - m_PathStart, m_PathDirection, m_DamageImmune, m_NoAggro, m_NotifyArrived, - m_ShowNameBillboard, m_CameraLocked, m_CollisionEnabled, m_UseDB, m_ComponentID, - m_Parent->GetObjectID()); + GameMessages::StartRailMovement startRail; + startRail.target = originator->GetObjectID(); + startRail.pathName = m_Path; + startRail.startSound = m_StartSound; + startRail.loopSound = m_loopSound; + startRail.stopSound = m_StopSound; + startRail.pathStart = m_PathStart; + startRail.goForward = m_PathDirection; + startRail.bDamageImmune = m_DamageImmune; + startRail.bNoAggro = m_NoAggro; + startRail.bNotifyActor = m_NotifyArrived; + startRail.bShowNameBillboard = m_ShowNameBillboard; + startRail.bCameraLocked = m_CameraLocked; + startRail.bCollisionEnabled = m_CollisionEnabled; + startRail.bUseDB = m_UseDB; + startRail.railActivatorComponentID = m_ComponentID; + startRail.railActivatorObjectID = m_Parent->GetObjectID(); + startRail.Send(originator->GetSystemAddress()); }); } @@ -111,9 +125,14 @@ void RailActivatorComponent::OnRailMovementReady(Entity* originator) const { RenderComponent::PlayAnimation(originator, m_LoopAnimation); } - GameMessages::SendSetRailMovement(originator->GetObjectID(), m_PathDirection, m_Path, m_PathStart, - originator->GetSystemAddress(), m_ComponentID, - m_Parent->GetObjectID()); + GameMessages::SetRailMovement setRail; + setRail.target = originator->GetObjectID(); + setRail.pathGoForward = m_PathDirection; + setRail.pathName = m_Path; + setRail.pathStart = m_PathStart; + setRail.railActivatorComponentID = m_ComponentID; + setRail.railActivatorObjectID = m_Parent->GetObjectID(); + setRail.Send(originator->GetSystemAddress()); } } diff --git a/dGame/dComponents/RocketLaunchpadControlComponent.cpp b/dGame/dComponents/RocketLaunchpadControlComponent.cpp index a64cce767..73243c80b 100644 --- a/dGame/dComponents/RocketLaunchpadControlComponent.cpp +++ b/dGame/dComponents/RocketLaunchpadControlComponent.cpp @@ -4,6 +4,8 @@ #include #include "GameMessages.h" +#include "ObjectMessages.h" +#include "ZoneMessages.h" #include "CharacterComponent.h" #include "dZoneManager.h" #include "EntityManager.h" @@ -78,9 +80,12 @@ void RocketLaunchpadControlComponent::Launch(Entity* originator, LWOMAPID mapId, SetSelectedMapId(originator->GetObjectID(), zone); - GameMessages::SendFireEventClientSide(m_Parent->GetObjectID(), originator->GetSystemAddress(), u"RocketEquipped", rocket->GetId(), cloneId, -1, originator->GetObjectID()); + GameMessages::FireEventClientSide(m_Parent->GetObjectID(), u"RocketEquipped", rocket->GetId(), originator->GetObjectID()).SendToClient(originator->GetSystemAddress()); - GameMessages::SendChangeObjectWorldState(rocket->GetId(), eObjectWorldState::ATTACHED, UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::ChangeObjectWorldState worldState; + worldState.target = rocket->GetId(); + worldState.newState = eObjectWorldState::ATTACHED; + worldState.Send(UNASSIGNED_SYSTEM_ADDRESS); Game::entityManager->SerializeEntity(originator); } diff --git a/dGame/dComponents/TriggerComponent.cpp b/dGame/dComponents/TriggerComponent.cpp index a159ff654..7aa97f65c 100644 --- a/dGame/dComponents/TriggerComponent.cpp +++ b/dGame/dComponents/TriggerComponent.cpp @@ -19,6 +19,7 @@ #include #include "EffectsMessages.h" +#include "ZoneMessages.h" TriggerComponent::TriggerComponent(Entity* parent, const int32_t componentID, const std::string triggerInfo) : Component(parent, componentID) { m_Parent = parent; @@ -130,9 +131,15 @@ void TriggerComponent::HandleTriggerCommand(LUTriggers::Command* command, Entity case eTriggerCommandType::CAST_SKILL: HandleCastSkill(targetEntity, command->args); break; - case eTriggerCommandType::DISPLAY_ZONE_SUMMARY: - GameMessages::SendDisplayZoneSummary(targetEntity->GetObjectID(), targetEntity->GetSystemAddress(), false, command->args == "1", m_Parent->GetObjectID()); + case eTriggerCommandType::DISPLAY_ZONE_SUMMARY: { + GameMessages::DisplayZoneSummary zoneSummary; + zoneSummary.target = targetEntity->GetObjectID(); + zoneSummary.isPropertyMap = false; + zoneSummary.isZoneStart = command->args == "1"; + zoneSummary.sender = m_Parent->GetObjectID(); + zoneSummary.Send(targetEntity->GetSystemAddress()); break; + } case eTriggerCommandType::SET_PHYSICS_VOLUME_EFFECT: HandleSetPhysicsVolumeEffect(targetEntity, argArray); break; diff --git a/dGame/dGameMessages/ActivityMessages.cpp b/dGame/dGameMessages/ActivityMessages.cpp index 4f5709e41..dc54e2f10 100644 --- a/dGame/dGameMessages/ActivityMessages.cpp +++ b/dGame/dGameMessages/ActivityMessages.cpp @@ -4,6 +4,10 @@ #include "Entity.h" #include "EntityManager.h" #include "Game.h" +#include "LeaderboardManager.h" +#include "Logger.h" +#include "ScriptedActivityComponent.h" +#include "eReplicaComponentType.h" namespace GameMessages { void ActivityStop::Serialize(RakNet::BitStream& bitStream) const { @@ -79,4 +83,187 @@ namespace GameMessages { VALIDATE_READ(bitStream.Read(stateToPlaySoundOn)); return true; } + + namespace { + void WritePoint(RakNet::BitStream& bitStream, const NiPoint3& point) { + bitStream.Write(point.x); + bitStream.Write(point.y); + bitStream.Write(point.z); + } + + bool ReadPoint(RakNet::BitStream& bitStream, NiPoint3& point) { + return bitStream.Read(point.x) && bitStream.Read(point.y) && bitStream.Read(point.z); + } + } + + void MatchResponse::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(response); + } + + bool MatchResponse::Deserialize(RakNet::BitStream& bitStream) { + return bitStream.Read(response); + } + + void MatchUpdate::Serialize(RakNet::BitStream& bitStream) const { + BitStreamUtils::WriteNameValueText(bitStream, std::u16string(data.begin(), data.end())); + bitStream.Write(type); + } + + bool MatchUpdate::Deserialize(RakNet::BitStream& bitStream) { + std::u16string wide; + VALIDATE_READ(BitStreamUtils::ReadNameValueText(bitStream, wide)); + data.assign(wide.begin(), wide.end()); + VALIDATE_READ(bitStream.Read(type)); + return true; + } + + void MatchRequest::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(activator); + BitStreamUtils::WriteNameValueText(bitStream, std::u16string(playerChoices.begin(), playerChoices.end())); + bitStream.Write(type); + bitStream.Write(value); + } + + bool MatchRequest::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(activator)); + std::u16string wide; + VALIDATE_READ(BitStreamUtils::ReadNameValueText(bitStream, wide)); + playerChoices.clear(); + for (const auto character : wide) playerChoices.push_back(character); + VALIDATE_READ(bitStream.Read(type)); + VALIDATE_READ(bitStream.Read(value)); + return true; + } + + void MatchRequest::Handle(Entity& entity, const SystemAddress& sysAddr) { + std::vector scriptedActs = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SCRIPTED_ACTIVITY); + if (type == 0) { // join + if (value != 0) { + for (Entity* scriptedAct : scriptedActs) { + auto* comp = scriptedAct->GetComponent(); + if (!comp) continue; + if (comp->GetActivityID() == value) { + comp->PlayerJoin(&entity); + } + } + } + } else if (type == 1) { // ready/unready + for (Entity* scriptedAct : scriptedActs) { + auto* comp = scriptedAct->GetComponent(); + if (!comp) continue; + if (comp->PlayerIsInQueue(&entity)) { + comp->PlayerReady(&entity, value); + } + } + } + } + + void RequestActivitySummaryLeaderboardData::Serialize(RakNet::BitStream& bitStream) const { + BitStreamUtils::WriteOptional(bitStream, gameID, 0); + BitStreamUtils::WriteOptional(bitStream, queryType, 1); + BitStreamUtils::WriteOptional(bitStream, resultsEnd, 10); + BitStreamUtils::WriteOptional(bitStream, resultsStart, 0); + bitStream.Write(targetID); + bitStream.Write(weekly); + } + + bool RequestActivitySummaryLeaderboardData::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, gameID, 0)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, queryType, 1)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, resultsEnd, 10)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, resultsStart, 0)); + VALIDATE_READ(bitStream.Read(targetID)); + VALIDATE_READ(bitStream.Read(weekly)); + return true; + } + + void RequestActivitySummaryLeaderboardData::Handle(Entity& entity, const SystemAddress& sysAddr) { + // The client won't accept more than 10 results even if we wanted it to + LeaderboardManager::SendLeaderboard(gameID, static_cast(queryType), weekly, entity.GetObjectID(), entity.GetObjectID(), 10); + } + + void SendActivitySummaryLeaderboardData::Serialize(RakNet::BitStream& bitStream) const { + if (leaderboard) leaderboard->Serialize(bitStream); + } + + void SendActivitySummaryLeaderboardData::Handle(Entity& entity, const SystemAddress& sysAddr) { + LOG("We got mail!"); + } + + void NotifyClientShootingGalleryScore::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(addTime); + bitStream.Write(score); + bitStream.Write(targetID); + WritePoint(bitStream, targetPos); + } + + bool NotifyClientShootingGalleryScore::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(addTime)); + VALIDATE_READ(bitStream.Read(score)); + VALIDATE_READ(bitStream.Read(targetID)); + VALIDATE_READ(ReadPoint(bitStream, targetPos)); + return true; + } + + void UpdateShootingGalleryRotation::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(angle); + WritePoint(bitStream, facing); + WritePoint(bitStream, muzzlePos); + } + + bool UpdateShootingGalleryRotation::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(angle)); + VALIDATE_READ(ReadPoint(bitStream, facing)); + VALIDATE_READ(ReadPoint(bitStream, muzzlePos)); + return true; + } + + void ShootingGalleryFire::Serialize(RakNet::BitStream& bitStream) const { + WritePoint(bitStream, target); + bitStream.Write(rotation.w); + bitStream.Write(rotation.x); + bitStream.Write(rotation.y); + bitStream.Write(rotation.z); + } + + bool ShootingGalleryFire::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(ReadPoint(bitStream, target)); + VALIDATE_READ(bitStream.Read(rotation.w)); + VALIDATE_READ(bitStream.Read(rotation.x)); + VALIDATE_READ(bitStream.Read(rotation.y)); + VALIDATE_READ(bitStream.Read(rotation.z)); + return true; + } + + void ShootingGalleryFire::Handle(Entity& entity, const SystemAddress& sysAddr) { + entity.OnShootingGalleryFire(*this); + } + + void ActivityStateChangeRequest::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(objectID); + bitStream.Write(value1); + bitStream.Write(value2); + BitStreamUtils::WriteLengthPrefixed(bitStream, stringValue); + } + + bool ActivityStateChangeRequest::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(objectID)); + VALIDATE_READ(bitStream.Read(value1)); + VALIDATE_READ(bitStream.Read(value2)); + VALIDATE_READ(BitStreamUtils::ReadLengthPrefixed(bitStream, stringValue)); + return true; + } + + void ActivityStateChangeRequest::Handle(Entity& entity, const SystemAddress& sysAddr) { + auto* assosiate = Game::entityManager->GetEntity(objectID); + + LOG("%s [%i, %i] from %i to %i", GeneralUtils::UTF16ToWTF8(stringValue).c_str(), value1, value2, entity.GetLOT(), assosiate != nullptr ? assosiate->GetLOT() : 0); + + std::vector scriptedActs = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SHOOTING_GALLERY); + for (Entity* scriptEntity : scriptedActs) { + scriptEntity->OnActivityStateChangeRequest(objectID, value1, value2, stringValue); + } + + entity.OnActivityStateChangeRequest(objectID, value1, value2, stringValue); + } } diff --git a/dGame/dGameMessages/ActivityMessages.h b/dGame/dGameMessages/ActivityMessages.h index fff7f8023..ac51e2fbe 100644 --- a/dGame/dGameMessages/ActivityMessages.h +++ b/dGame/dGameMessages/ActivityMessages.h @@ -2,6 +2,8 @@ #define ACTIVITYMESSAGES_H #include "GameMessages.h" +#include "NiQuaternion.h" +#include "eMatchUpdate.h" #include @@ -83,6 +85,112 @@ namespace GameMessages { std::u16string sndName{}; int32_t stateToPlaySoundOn{}; }; + + // Server -> client, to one client. + struct MatchResponse : public NetGameMsg { + MatchResponse() : NetGameMsg(MessageType::Game::MATCH_RESPONSE) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + int32_t response{}; + }; + + // Server -> client, to one client. + struct MatchUpdate : public NetGameMsg { + MatchUpdate() : NetGameMsg(MessageType::Game::MATCH_UPDATE) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + // Name-value (LDF) text. Each byte is widened to one UTF-16 unit on the wire. + std::string data{}; + eMatchUpdate type{}; + }; + + // Client -> server. Join (type 0, value = activity ID) or ready/unready (type 1, value = ready) a match. + struct MatchRequest : public NetGameMsg { + MatchRequest() : NetGameMsg(MessageType::Game::MATCH_REQUEST) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + void Handle(Entity& entity, const SystemAddress& sysAddr) override; + + LWOOBJID activator{}; + // Name-value (LDF) text, narrowed to one byte per character as DLU always did. + std::string playerChoices{}; + int32_t type{}; + int32_t value{}; + }; + + // Client -> server (and server -> client in the client's definition). Answered with the leaderboard. + struct RequestActivitySummaryLeaderboardData : public NetGameMsg { + RequestActivitySummaryLeaderboardData() : NetGameMsg(MessageType::Game::REQUEST_ACTIVITY_SUMMARY_LEADERBOARD_DATA) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + void Handle(Entity& entity, const SystemAddress& sysAddr) override; + + int32_t gameID{ 0 }; // optional + int32_t queryType{ 1 }; // optional, a Leaderboard::InfoType + int32_t resultsEnd{ 10 }; // optional + int32_t resultsStart{ 0 }; // optional + LWOOBJID targetID{}; // "target" in the client; renamed so it doesn't hide NetGameMsg::target + bool weekly{}; + }; + + // Server -> client, to one client (UNASSIGNED broadcasts). The payload is written by Leaderboard::Serialize. + // When a client sends it, nothing is read (DLU only logs it). + struct SendActivitySummaryLeaderboardData : public NetGameMsg { + SendActivitySummaryLeaderboardData() : NetGameMsg(MessageType::Game::SEND_ACTIVITY_SUMMARY_LEADERBOARD_DATA) {} + void Serialize(RakNet::BitStream& bitStream) const override; + void Handle(Entity& entity, const SystemAddress& sysAddr) override; + + const Leaderboard* leaderboard{}; + }; + + // Server -> client, to one client (UNASSIGNED broadcasts). + struct NotifyClientShootingGalleryScore : public NetGameMsg { + NotifyClientShootingGalleryScore() : NetGameMsg(MessageType::Game::NOTIFY_CLIENT_SHOOTING_GALLERY_SCORE) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + float addTime{}; + int32_t score{}; + LWOOBJID targetID{}; + NiPoint3 targetPos{}; + }; + + // Client -> server. Nothing is done with it. + struct UpdateShootingGalleryRotation : public NetGameMsg { + UpdateShootingGalleryRotation() : NetGameMsg(MessageType::Game::UPDATE_SHOOTING_GALLERY_ROTATION) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + float angle{}; + NiPoint3 facing{}; + NiPoint3 muzzlePos{}; + }; + + // Client -> server. + struct ShootingGalleryFire : public NetGameMsg { + ShootingGalleryFire() : NetGameMsg(MessageType::Game::SHOOTING_GALLERY_FIRE) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + void Handle(Entity& entity, const SystemAddress& sysAddr) override; + + NiPoint3 target{}; + NiQuaternion rotation = QuatUtils::IDENTITY; // written w, x, y, z + }; + + // Client -> server. + struct ActivityStateChangeRequest : public NetGameMsg { + ActivityStateChangeRequest() : NetGameMsg(MessageType::Game::ACTIVITY_STATE_CHANGE_REQUEST) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + void Handle(Entity& entity, const SystemAddress& sysAddr) override; + + LWOOBJID objectID{}; + int32_t value1{}; + int32_t value2{}; + std::u16string stringValue{}; + }; }; #endif // ACTIVITYMESSAGES_H diff --git a/dGame/dGameMessages/BuildingMessages.cpp b/dGame/dGameMessages/BuildingMessages.cpp index e8a2e249f..96a7f0528 100644 --- a/dGame/dGameMessages/BuildingMessages.cpp +++ b/dGame/dGameMessages/BuildingMessages.cpp @@ -728,4 +728,37 @@ namespace GameMessages { } } } + + void SetModelToBuild::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(modelLot != -1); + if (modelLot != -1) bitStream.Write(modelLot); + } + + void SpawnModelBricks::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(amount != 0.0f); + if (amount != 0.0f) bitStream.Write(amount); + bitStream.Write(position != NiPoint3Constant::ZERO); + if (position != NiPoint3Constant::ZERO) { + bitStream.Write(position.x); + bitStream.Write(position.y); + bitStream.Write(position.z); + } + } + + bool SetModelToBuild::Deserialize(RakNet::BitStream& bitStream) { + return BitStreamUtils::ReadOptional(bitStream, modelLot, LOT{ -1 }); + } + + bool SpawnModelBricks::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, amount, 0.0f)); + bool hasPosition{}; + VALIDATE_READ(bitStream.Read(hasPosition)); + position = NiPoint3Constant::ZERO; + if (hasPosition) { + VALIDATE_READ(bitStream.Read(position.x)); + VALIDATE_READ(bitStream.Read(position.y)); + VALIDATE_READ(bitStream.Read(position.z)); + } + return true; + } } diff --git a/dGame/dGameMessages/BuildingMessages.h b/dGame/dGameMessages/BuildingMessages.h index 49e259414..4bf0d36c3 100644 --- a/dGame/dGameMessages/BuildingMessages.h +++ b/dGame/dGameMessages/BuildingMessages.h @@ -205,6 +205,22 @@ namespace GameMessages { std::string lxfmlDataCompressed{}; // u32 byte count, then the bytes uint32_t timeTakenInMs{}; }; + + struct SetModelToBuild : public NetGameMsg { + SetModelToBuild() : NetGameMsg(MessageType::Game::SET_MODEL_TO_BUILD) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + LOT modelLot{ -1 }; + }; + + struct SpawnModelBricks : public NetGameMsg { + SpawnModelBricks() : NetGameMsg(MessageType::Game::SPAWN_MODEL_BRICKS) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + float amount{ 0.0f }; + NiPoint3 position{ NiPoint3Constant::ZERO }; + }; }; #endif // BUILDINGMESSAGES_H diff --git a/dGame/dGameMessages/CMakeLists.txt b/dGame/dGameMessages/CMakeLists.txt index 1f95cdd73..c8968f83f 100644 --- a/dGame/dGameMessages/CMakeLists.txt +++ b/dGame/dGameMessages/CMakeLists.txt @@ -8,12 +8,17 @@ set(DGAME_DGAMEMESSAGES_SOURCES "GameMessages.cpp" "InventoryMessages.cpp" "MissionMessages.cpp" + "MovementMessages.cpp" + "ObjectMessages.cpp" "PetMessages.cpp" + "PlayerMessages.cpp" "PropertyMessages.cpp" + "QuickBuildMessages.cpp" "RacingMessages.cpp" "SkillMessages.cpp" "TradeMessages.cpp" - "VendorMessages.cpp") + "VendorMessages.cpp" + "ZoneMessages.cpp") add_library(dGameMessages OBJECT ${DGAME_DGAMEMESSAGES_SOURCES}) target_link_libraries(dGameMessages diff --git a/dGame/dGameMessages/EffectsMessages.cpp b/dGame/dGameMessages/EffectsMessages.cpp index 5fada082c..17fbbbd68 100644 --- a/dGame/dGameMessages/EffectsMessages.cpp +++ b/dGame/dGameMessages/EffectsMessages.cpp @@ -486,4 +486,41 @@ namespace GameMessages { VALIDATE_READ(bitStream.Read(emoteID)); return true; } + + void DisplayTooltip::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(doOrDie); + bitStream.Write(noRepeat); + bitStream.Write(noRevive); + bitStream.Write(isPropertyTooltip); + bitStream.Write(show); + bitStream.Write(translate); + bitStream.Write(time); + bitStream.Write(id.size()); + bitStream.Write(id); + + std::string toWrite; + for (const auto& item : localizeParams | std::views::values) { + toWrite += item->GetString() + "\n"; + } + if (!toWrite.empty()) toWrite.pop_back(); + bitStream.Write(toWrite.size()); + bitStream.Write(GeneralUtils::ASCIIToUTF16(toWrite)); + if (!toWrite.empty()) bitStream.Write(0x00); // Null Terminator + + bitStream.Write(imageName.size()); + bitStream.Write(imageName); + bitStream.Write(text.size()); + bitStream.Write(text); + } + + void EmotePlayed::Serialize(RakNet::BitStream& stream) const { + stream.Write(emoteID); + stream.Write(targetID); + } + + bool EmotePlayed::Deserialize(RakNet::BitStream& stream) { + VALIDATE_READ(stream.Read(emoteID)); + VALIDATE_READ(stream.Read(targetID)); + return true; + } } diff --git a/dGame/dGameMessages/EffectsMessages.h b/dGame/dGameMessages/EffectsMessages.h index 518603524..b6cace93d 100644 --- a/dGame/dGameMessages/EffectsMessages.h +++ b/dGame/dGameMessages/EffectsMessages.h @@ -322,6 +322,32 @@ namespace GameMessages { bool bLock{}; int32_t emoteID{}; }; + + struct DisplayTooltip : public NetGameMsg { + DisplayTooltip() : NetGameMsg(MessageType::Game::DISPLAY_TOOLTIP) {} + bool doOrDie{}; + bool noRepeat{}; + bool noRevive{}; + bool isPropertyTooltip{}; + bool show{}; + bool translate{}; + int32_t time{}; + std::u16string id{}; + LwoNameValue localizeParams{}; + std::u16string imageName{}; + std::u16string text{}; + void Serialize(RakNet::BitStream& bitStream) const override; + }; + + struct EmotePlayed : public NetGameMsg { + EmotePlayed() : NetGameMsg(MessageType::Game::EMOTE_PLAYED), emoteID(0), targetID(0) {} + + void Serialize(RakNet::BitStream& stream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + int32_t emoteID; + LWOOBJID targetID; + }; }; #endif // EFFECTSMESSAGES_H diff --git a/dGame/dGameMessages/GameMessageDecoder.cpp b/dGame/dGameMessages/GameMessageDecoder.cpp index 6681c0d53..1b2ee3b96 100644 --- a/dGame/dGameMessages/GameMessageDecoder.cpp +++ b/dGame/dGameMessages/GameMessageDecoder.cpp @@ -6,6 +6,9 @@ #include #include "GameMessages.h" +#include "ActivityMessages.h" +#include "InventoryMessages.h" +#include "ObjectMessages.h" #include "SkillMessages.h" #include "master/MessageCapture.h" diff --git a/dGame/dGameMessages/GameMessageHandler.cpp b/dGame/dGameMessages/GameMessageHandler.cpp index 89fb16d01..de4e4559e 100644 --- a/dGame/dGameMessages/GameMessageHandler.cpp +++ b/dGame/dGameMessages/GameMessageHandler.cpp @@ -38,6 +38,11 @@ #include "InventoryMessages.h" #include "PetMessages.h" #include "PropertyMessages.h" +#include "MovementMessages.h" +#include "ObjectMessages.h" +#include "PlayerMessages.h" +#include "QuickBuildMessages.h" +#include "ZoneMessages.h" #include "eMissionTaskType.h" #include "eReplicaComponentType.h" #include "ServiceType.h" @@ -168,6 +173,43 @@ namespace { { RESURRECT, []() { return std::make_unique(); } }, { ACTIVATE_BUBBLE_BUFF, []() { return std::make_unique(); } }, { DECTIVATE_BUBBLE_BUFF, []() { return std::make_unique(); } }, + // Zone lifecycle + { PLAYER_LOADED, []() { return std::make_unique(); } }, + { READY_FOR_UPDATES, []() { return std::make_unique(); } }, + { NOTIFY_SERVER_LEVEL_PROCESSING_COMPLETE, []() { return std::make_unique(); } }, + { ZONE_SUMMARY_DISMISSED, []() { return std::make_unique(); } }, + { MISSION_DIALOGUE_CANCELLED, []() { return std::make_unique(); } }, + + // Objects and scripts + { FIRE_EVENT_SERVER_SIDE, []() { return std::make_unique(); } }, + + // Player state, chat commands, reports + { PARSE_CHAT_MESSAGE, []() { return std::make_unique(); } }, + { PICKUP_CURRENCY, []() { return std::make_unique(); } }, + { MODIFY_PLAYER_ZONE_STATISTIC, []() { return std::make_unique(); } }, + { UPDATE_PLAYER_STATISTIC, []() { return std::make_unique(); } }, + { REPORT_BUG, []() { return std::make_unique(); } }, + { VERIFY_ACK, []() { return std::make_unique(); } }, + + // Quickbuilds + { REBUILD_CANCEL, []() { return std::make_unique(); } }, + + // Activities, matches, leaderboards, shooting galleries + { MATCH_REQUEST, []() { return std::make_unique(); } }, + { REQUEST_ACTIVITY_SUMMARY_LEADERBOARD_DATA, []() { return std::make_unique(); } }, + { SEND_ACTIVITY_SUMMARY_LEADERBOARD_DATA, []() { return std::make_unique(); } }, + { ACTIVITY_STATE_CHANGE_REQUEST, []() { return std::make_unique(); } }, + { UPDATE_SHOOTING_GALLERY_ROTATION, []() { return std::make_unique(); } }, + + // Movement: platforms, rails, mounts, ghosting + { REQUEST_PLATFORM_RESYNC, []() { return std::make_unique(); } }, + { CLIENT_RAIL_MOVEMENT_READY, []() { return std::make_unique(); } }, + { CANCEL_RAIL_MOVEMENT, []() { return std::make_unique(); } }, + { PLAYER_RAIL_ARRIVED_NOTIFICATION, []() { return std::make_unique(); } }, + { DISMOUNT_COMPLETE, []() { return std::make_unique(); } }, + { ACKNOWLEDGE_POSSESSION, []() { return std::make_unique(); } }, + { TOGGLE_GHOST_REFERENCE_OVERRIDE, []() { return std::make_unique(); } }, + { SET_GHOST_REFERENCE_POSITION, []() { return std::make_unique(); } }, }; }; @@ -175,8 +217,6 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const System // The dashboard's message inspector (sees every message a client sends; nothing to do unless a capture runs) if (MessageInspector::IsCapturing()) MessageInspector::RecordReceived(sysAddr, objectID, messageID, inStream); - CBITSTREAM; - // Get the entity Entity* entity = Game::entityManager->GetEntity(objectID); @@ -216,218 +256,5 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const System return; } - switch (messageID) { - - case MessageType::Game::PLAYER_LOADED: { - GameMessages::SendPlayerReady(entity, sysAddr); - entity->SetPlayerReadyForUpdates(); - - auto* ghostComponent = entity->GetComponent(); - if (ghostComponent != nullptr) { - ghostComponent->ConstructLimboEntities(); - } - - InventoryComponent* inv = entity->GetComponent(); - if (inv) { - // Clear server-side skill state so AddItemSkills sends fresh AddSkill - // packets to the now-ready client. Skills sent during entity construction - // (Serialize) arrive before LWOSkillComponent is initialized and are dropped. - inv->ClearSkills(); - auto items = inv->GetEquippedItems(); - for (auto pair : items) { - const auto item = pair.second; - - inv->AddItemSkills(item.lot); - } - - // Fixes a bug where testmapping too fast causes large item inventories to become invisible. - // Only affects item inventory - GameMessages::SetInventorySize setSize; - setSize.target = entity->GetObjectID(); - setSize.inventoryType = eInventoryType::ITEMS; - setSize.size = inv->GetInventory(eInventoryType::ITEMS)->GetSize(); - setSize.SendToClient(entity->GetSystemAddress()); - } - - GameMessages::SendRestoreToPostLoadStats(entity, sysAddr); - - auto* const destroyable = entity->GetComponent(); - if (destroyable) destroyable->SetImagination(destroyable->GetImagination()); - Game::entityManager->SerializeEntity(entity); - - std::vector racingControllers = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::RACING_CONTROL); - for (Entity* racingController : racingControllers) { - auto* racingComponent = racingController->GetComponent(); - if (racingComponent != nullptr) { - racingComponent->OnPlayerLoaded(entity); - } - } - - Entity* zoneControl = Game::entityManager->GetZoneControlEntity(); - if (zoneControl) { - zoneControl->GetScript()->OnPlayerLoaded(zoneControl, entity); - } - - std::vector scriptedActs = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SCRIPT); - for (Entity* scriptEntity : scriptedActs) { - if (!zoneControl || scriptEntity->GetObjectID() != zoneControl->GetObjectID()) { // Don't want to trigger twice on instance worlds - scriptEntity->GetScript()->OnPlayerLoaded(scriptEntity, entity); - } - } - - //Kill player if health == 0 - if (entity->GetIsDead()) { - entity->Smash(entity->GetObjectID()); - } - - //if the player has moved significantly, move them back: - if ((entity->GetPosition().y - entity->GetCharacter()->GetOriginalPos().y) > 2.0f) { - // Disabled until fixed - //GameMessages::SendTeleport(entity->GetObjectID(), entity->GetCharacter()->GetOriginalPos(), entity->GetCharacter()->GetOriginalRot(), entity->GetSystemAddress(), true, true); - } - - /** - * Invoke the OnZoneLoad event on the player character - */ - auto* character = entity->GetCharacter(); - - if (character != nullptr) { - character->OnZoneLoad(); - } - - // Moved here from another instance: put back what their save doesn't keep (the pet that was out) - WorldMigration::OnPlayerLoaded(entity); - - LOG("Player %s (%llu) loaded.", entity->GetCharacter()->GetName().c_str(), entity->GetObjectID()); - - // After we've done our thing, tell the client they're ready - GameMessages::SendPlayerReady(Game::zoneManager->GetZoneControlObject(), sysAddr); - - if (Game::config->GetValue("allow_players_to_skip_cinematics") != "1" - || !entity->GetCharacter() - || !entity->GetCharacter()->GetPlayerFlag(ePlayerFlag::DLU_SKIP_CINEMATICS)) return; - entity->AddCallbackTimer(0.5f, [entity, sysAddr]() { - if (!entity) return; - GameMessages::EndCinematic endCinematic; - endCinematic.target = entity->GetObjectID(); - endCinematic.Send(sysAddr); - }); - break; - } - - case MessageType::Game::MISSION_DIALOGUE_CANCELLED: { - // This message is pointless for our implementation, as the client just carries on after - // rejecting a mission offer. We dont need to do anything. This is just here to remove a warning in our logs :) - break; - } - - case MessageType::Game::REQUEST_PLATFORM_RESYNC: { - GameMessages::HandleRequestPlatformResync(inStream, entity, sysAddr); - break; - } - - case MessageType::Game::FIRE_EVENT_SERVER_SIDE: { - GameMessages::HandleFireEventServerSide(inStream, entity, sysAddr); - break; - } - - case MessageType::Game::SEND_ACTIVITY_SUMMARY_LEADERBOARD_DATA: { - GameMessages::HandleActivitySummaryLeaderboardData(inStream, entity, sysAddr); - break; - } - - case MessageType::Game::REQUEST_ACTIVITY_SUMMARY_LEADERBOARD_DATA: { - GameMessages::HandleRequestActivitySummaryLeaderboardData(inStream, entity, sysAddr); - break; - } - - case MessageType::Game::ACTIVITY_STATE_CHANGE_REQUEST: { - GameMessages::HandleActivityStateChangeRequest(inStream, entity); - break; - } - - case MessageType::Game::PARSE_CHAT_MESSAGE: { - GameMessages::HandleParseChatMessage(inStream, entity, sysAddr); - break; - } - - case MessageType::Game::NOTIFY_SERVER_LEVEL_PROCESSING_COMPLETE: { - GameMessages::HandleNotifyServerLevelProcessingComplete(inStream, entity); - break; - } - - case MessageType::Game::PICKUP_CURRENCY: { - GameMessages::HandlePickupCurrency(inStream, entity); - break; - } - - case MessageType::Game::REBUILD_CANCEL: - GameMessages::HandleQuickBuildCancel(inStream, entity); - break; - - case MessageType::Game::MATCH_REQUEST: - GameMessages::HandleMatchRequest(inStream, entity); - break; - - case MessageType::Game::VERIFY_ACK: - GameMessages::HandleVerifyAck(inStream, entity, sysAddr); - break; - - // Trading - case MessageType::Game::ACKNOWLEDGE_POSSESSION: - GameMessages::HandleAcknowledgePossession(inStream, entity, sysAddr); - break; - - case MessageType::Game::UPDATE_SHOOTING_GALLERY_ROTATION: - GameMessages::HandleUpdateShootingGalleryRotation(inStream, entity, sysAddr); - break; - - // NT - case MessageType::Game::TOGGLE_GHOST_REFERENCE_OVERRIDE: - GameMessages::HandleToggleGhostReferenceOverride(inStream, entity, sysAddr); - break; - - case MessageType::Game::SET_GHOST_REFERENCE_POSITION: - GameMessages::HandleSetGhostReferencePosition(inStream, entity, sysAddr); - break; - - case MessageType::Game::READY_FOR_UPDATES: - //We don't really care about this message, as it's simply here to inform us that the client is done loading an object. - //In the event we _do_ send an update to an object that hasn't finished loading, the client will handle it anyway. - break; - - case MessageType::Game::REPORT_BUG: - GameMessages::HandleReportBug(inStream, entity); - break; - - case MessageType::Game::CLIENT_RAIL_MOVEMENT_READY: - GameMessages::HandleClientRailMovementReady(inStream, entity, sysAddr); - break; - - case MessageType::Game::CANCEL_RAIL_MOVEMENT: - GameMessages::HandleCancelRailMovement(inStream, entity, sysAddr); - break; - - case MessageType::Game::PLAYER_RAIL_ARRIVED_NOTIFICATION: - GameMessages::HandlePlayerRailArrivedNotification(inStream, entity, sysAddr); - break; - - case MessageType::Game::MODIFY_PLAYER_ZONE_STATISTIC: - GameMessages::HandleModifyPlayerZoneStatistic(inStream, entity); - break; - - case MessageType::Game::UPDATE_PLAYER_STATISTIC: - GameMessages::HandleUpdatePlayerStatistic(inStream, entity); - break; - - case MessageType::Game::DISMOUNT_COMPLETE: - GameMessages::HandleDismountComplete(inStream, entity, sysAddr); - break; - case MessageType::Game::ZONE_SUMMARY_DISMISSED: - GameMessages::HandleZoneSummaryDismissed(inStream, entity); - break; - default: - LOG_DEBUG("Received Unknown GM with ID: %4i, %s", messageID, StringifiedEnum::ToString(messageID).data()); - break; - } + LOG_DEBUG("Received Unknown GM with ID: %4i, %s", messageID, StringifiedEnum::ToString(messageID).data()); } diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index 82bca7225..48c1e72b1 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -1,1570 +1,12 @@ #include "GameMessages.h" -#include "EffectsMessages.h" -#include "InventoryMessages.h" -#include "DashboardNotify.h" -#include "PlayerReports.h" -#include "EconomyLedger.h" -#include "User.h" -#include "Entity.h" -#include "BitStreamUtils.h" + #include "BitStream.h" -#include "Game.h" -#include "SlashCommandHandler.h" -#include "NiPoint3.h" -#include "NiQuaternion.h" -#include "Logger.h" -#include "GeneralUtils.h" -#include "Character.h" +#include "BitStreamUtils.h" #include "EntityManager.h" -#include "Database.h" -#include "dServer.h" -#include "ObjectIDManager.h" -#include "CppScripts.h" -#include "UserManager.h" -#include "ZoneInstanceManager.h" -#include "ClientPackets.h" -#include "Item.h" -#include "ZCompression.h" -#include "dConfig.h" -#include "TeamManager.h" -#include "ChatPackets.h" -#include "MultiZoneEntranceComponent.h" -#include "eUnequippableActiveType.h" -#include "eMovementPlatformState.h" -#include "LeaderboardManager.h" -#include "Amf3.h" -#include "Loot.h" -#include "eRacingTaskParam.h" -#include "eMissionTaskType.h" -#include "eMissionState.h" -#include "eObjectBits.h" -#include "eTriggerEventType.h" -#include "eMatchUpdate.h" -#include "eCyclingMode.h" -#include "eCinematicEvent.h" -#include "eQuickBuildFailReason.h" -#include "eControlScheme.h" -#include "eStateChangeType.h" -#include "ServiceType.h" -#include "ePlayerFlag.h" - -#include -#include -#include -#include -#include "RakString.h" - -//CDB includes: -#include "CDClientManager.h" -#include "CDEmoteTable.h" - -//Component includes: -#include "ControllablePhysicsComponent.h" -#include "CharacterComponent.h" -#include "MissionOfferComponent.h" -#include "MissionComponent.h" -#include "DestroyableComponent.h" -#include "ScriptComponent.h" -#include "QuickBuildComponent.h" -#include "VendorComponent.h" -#include "InventoryComponent.h" -#include "RocketLaunchpadControlComponent.h" -#include "PropertyEntranceComponent.h" -#include "MovingPlatformComponent.h" -#include "PetComponent.h" -#include "ModuleAssemblyComponent.h" -#include "HavokVehiclePhysicsComponent.h" -#include "RenderComponent.h" -#include "PossessableComponent.h" -#include "PossessorComponent.h" -#include "RacingControlComponent.h" -#include "RailActivatorComponent.h" -#include "LevelProgressionComponent.h" -#include "DonationVendorComponent.h" -#include "GhostComponent.h" -#include "AchievementVendorComponent.h" - -// Message includes: -#include "dZoneManager.h" -#include "PropertyManagementComponent.h" -#include "PropertyVendorComponent.h" -#include "TradingManager.h" -#include "ControlBehaviors.h" -#include "AMFDeserialize.h" -#include "eBlueprintSaveResponseType.h" -#include "eAnimationFlags.h" -#include "AmfSerialize.h" -#include "eReplicaComponentType.h" +#include "Game.h" #include "MessageType/Client.h" -#include "MessageType/Game.h" -#include "ePetAbilityType.h" -#include "ActivityManager.h" -#include "PlayerManager.h" -#include "eVendorTransactionResult.h" -#include "eReponseMoveItemBetweenInventoryTypeCode.h" - -#include "CDComponentsRegistryTable.h" -#include "CDObjectsTable.h" -#include "eItemType.h" -#include "Lxfml.h" -#include "Sd0.h" - -void GameMessages::SendFireEventClientSide(const LWOOBJID& objectID, const SystemAddress& sysAddr, std::u16string args, const LWOOBJID& object, int64_t param1, int param2, const LWOOBJID& sender) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(objectID); - bitStream.Write(MessageType::Game::FIRE_EVENT_CLIENT_SIDE); - - //bitStream.Write(args); - uint32_t argSize = args.size(); - bitStream.Write(argSize); - for (uint32_t k = 0; k < argSize; k++) { - bitStream.Write(args[k]); - } - bitStream.Write(object); - bitStream.Write0(); - //bitStream.Write(param1); - bitStream.Write0(); - //bitStream.Write(param2); - bitStream.Write(sender); - - SEND_PACKET; -} - -void GameMessages::SendTeleport(const LWOOBJID& objectID, const NiPoint3& pos, const NiQuaternion& rot, const SystemAddress& sysAddr, bool bSetRotation) { - CBITSTREAM; - CMSGHEADER; - bitStream.Write(objectID); - bitStream.Write(MessageType::Game::TELEPORT); - - bool bIgnoreY = (pos.y == 0.0f); - bool bUseNavmesh = false; - bool bSkipAllChecks = false; - //float w = 1.0f; - //float x = 0.0f; - //float y = 0.0f; - //float z = 0.0f; - - bitStream.Write(bIgnoreY); - bitStream.Write(bSetRotation); - bitStream.Write(bSkipAllChecks); - bitStream.Write(pos.x); - bitStream.Write(pos.y); - bitStream.Write(pos.z); - bitStream.Write(bUseNavmesh); - - bitStream.Write(rot.w != 1.0f); - if (rot.w != 1.0f) bitStream.Write(rot.w); - - bitStream.Write(rot.x); - bitStream.Write(rot.y); - bitStream.Write(rot.z); - - SEND_PACKET; -} - -void GameMessages::SendPlayerReady(Entity* entity, const SystemAddress& sysAddr) { - CBITSTREAM; - CMSGHEADER; - bitStream.Write(entity->GetObjectID()); - bitStream.Write(MessageType::Game::PLAYER_READY); - SEND_PACKET; -} - -void GameMessages::SendInvalidZoneTransferList(Entity* entity, const SystemAddress& sysAddr, const std::u16string& feedbackURL, const std::u16string& invalidMapTransferList, bool feedbackOnExit, bool feedbackOnInvalidTransfer) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(entity->GetObjectID()); - bitStream.Write(MessageType::Game::INVALID_ZONE_TRANSFER_LIST); - - uint32_t CustomerFeedbackURLLength = feedbackURL.size(); - bitStream.Write(CustomerFeedbackURLLength); - for (uint32_t k = 0; k < CustomerFeedbackURLLength; k++) { - bitStream.Write(feedbackURL[k]); - } - - uint32_t InvalidMapTransferListLength = invalidMapTransferList.size(); - bitStream.Write(InvalidMapTransferListLength); - for (uint32_t k = 0; k < InvalidMapTransferListLength; k++) { - bitStream.Write(invalidMapTransferList[k]); - } - - bitStream.Write(feedbackOnExit); - bitStream.Write(feedbackOnInvalidTransfer); - - SEND_PACKET; -} - -void GameMessages::SendPlayerSetCameraCyclingMode(const LWOOBJID& objectID, const SystemAddress& sysAddr, - bool bAllowCyclingWhileDeadOnly, eCyclingMode cyclingMode) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(objectID); - bitStream.Write(MessageType::Game::PLAYER_SET_CAMERA_CYCLING_MODE); - - bitStream.Write(bAllowCyclingWhileDeadOnly); - - bitStream.Write(cyclingMode != eCyclingMode::ALLOW_CYCLE_TEAMMATES); - if (cyclingMode != eCyclingMode::ALLOW_CYCLE_TEAMMATES) { - bitStream.Write(cyclingMode); - } - - SEND_PACKET; -} - -void GameMessages::SendStartPathing(Entity* entity) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(entity->GetObjectID()); - bitStream.Write(MessageType::Game::START_PATHING); - - SEND_PACKET_BROADCAST; -} - -void GameMessages::SendPlatformResync(Entity* entity, const SystemAddress& sysAddr, bool bStopAtDesiredWaypoint, - int iIndex, int iDesiredWaypointIndex, int nextIndex, - eMovementPlatformState movementState, bool special) { - CBITSTREAM; - CMSGHEADER; - - const auto objID = entity->GetObjectID(); - const auto lot = entity->GetLOT(); - - if (lot == 12341 || lot == 5027 || lot == 5028 || lot == 14335 || lot == 14447 || lot == 14449 || lot == 11306 || lot == 11308 || lot == 9483) { - iDesiredWaypointIndex = (lot == 11306 || lot == 11308) ? 1 : 0; - iIndex = lot == 9483 ? 1 : 0; - nextIndex = lot == 9483 && !special ? 1 : 0; - bStopAtDesiredWaypoint = true; - movementState = lot == 9483 && !special ? eMovementPlatformState::Stopped : eMovementPlatformState::Stationary; - } - - bitStream.Write(entity->GetObjectID()); - bitStream.Write(MessageType::Game::PLATFORM_RESYNC); - - bool bReverse = false; - int eCommand = 0; - int eUnexpectedCommand = 0; - float fIdleTimeElapsed = 0.0f; - float fMoveTimeElapsed = 0.0f; - float fPercentBetweenPoints = 0.0f; - NiPoint3 ptUnexpectedLocation = NiPoint3Constant::ZERO; - NiQuaternion qUnexpectedRotation = QuatUtils::IDENTITY; - - bitStream.Write(bReverse); - bitStream.Write(bStopAtDesiredWaypoint); - bitStream.Write(eCommand); - bitStream.Write(static_cast(movementState)); - bitStream.Write(eUnexpectedCommand); - bitStream.Write(fIdleTimeElapsed); - bitStream.Write(fMoveTimeElapsed); - bitStream.Write(fPercentBetweenPoints); - bitStream.Write(iDesiredWaypointIndex); - bitStream.Write(iIndex); - bitStream.Write(nextIndex); - bitStream.Write(ptUnexpectedLocation.x); - bitStream.Write(ptUnexpectedLocation.y); - bitStream.Write(ptUnexpectedLocation.z); - - bitStream.Write(qUnexpectedRotation != QuatUtils::IDENTITY); - if (qUnexpectedRotation != QuatUtils::IDENTITY) { - bitStream.Write(qUnexpectedRotation.x); - bitStream.Write(qUnexpectedRotation.y); - bitStream.Write(qUnexpectedRotation.z); - bitStream.Write(qUnexpectedRotation.w); - } - - if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; - SEND_PACKET; -} - -void GameMessages::SendRestoreToPostLoadStats(Entity* entity, const SystemAddress& sysAddr) { - CBITSTREAM; - CMSGHEADER; - bitStream.Write(entity->GetObjectID()); - bitStream.Write(MessageType::Game::RESTORE_TO_POST_LOAD_STATS); - SEND_PACKET; -} - -void GameMessages::SendServerDoneLoadingAllObjects(Entity* entity, const SystemAddress& sysAddr) { - CBITSTREAM; - CMSGHEADER; - bitStream.Write(entity->GetObjectID()); - bitStream.Write(MessageType::Game::SERVER_DONE_LOADING_ALL_OBJECTS); - SEND_PACKET; -} - -void GameMessages::SendChatModeUpdate(const LWOOBJID& objectID, eGameMasterLevel level) { - CBITSTREAM; - CMSGHEADER; - bitStream.Write(objectID); - bitStream.Write(MessageType::Game::UPDATE_CHAT_MODE); - bitStream.Write(level); - SEND_PACKET_BROADCAST; -} - -void GameMessages::SendGMLevelBroadcast(const LWOOBJID& objectID, eGameMasterLevel level) { - CBITSTREAM; - CMSGHEADER; - bitStream.Write(objectID); - bitStream.Write(MessageType::Game::SET_GM_LEVEL); - bitStream.Write1(); - bitStream.Write(level); - SEND_PACKET_BROADCAST; -} - -void GameMessages::SendChangeObjectWorldState(const LWOOBJID& objectID, eObjectWorldState state, const SystemAddress& sysAddr) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(objectID); - bitStream.Write(MessageType::Game::CHANGE_OBJECT_WORLD_STATE); - bitStream.Write(state); - - if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST - SEND_PACKET; -} - -void GameMessages::SendModifyLEGOScore(Entity* entity, const SystemAddress& sysAddr, int64_t score, eLootSourceType sourceType) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(entity->GetObjectID()); - bitStream.Write(MessageType::Game::MODIFY_LEGO_SCORE); - bitStream.Write(score); - - bitStream.Write(sourceType != eLootSourceType::NONE); - if (sourceType != eLootSourceType::NONE) bitStream.Write(sourceType); - - SEND_PACKET; -} - -void GameMessages::SendSetCurrency(Entity* entity, int64_t currency, int lootType, const LWOOBJID& sourceID, const LOT& sourceLOT, int sourceTradeID, bool overrideCurrent, eLootSourceType sourceType) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(entity->GetObjectID()); - bitStream.Write(MessageType::Game::SET_CURRENCY); - - bitStream.Write(currency); - - bitStream.Write(lootType != LOOTTYPE_NONE); - if (lootType != LOOTTYPE_NONE) bitStream.Write(lootType); - - bitStream.Write(NiPoint3Constant::ZERO); - - bitStream.Write(sourceLOT != LOT_NULL); - if (sourceLOT != LOT_NULL) bitStream.Write(sourceLOT); - - bitStream.Write(sourceID != LWOOBJID_EMPTY); - if (sourceID != LWOOBJID_EMPTY) bitStream.Write(sourceID); - - bitStream.Write(sourceTradeID != LWOOBJID_EMPTY); - if (sourceTradeID != LWOOBJID_EMPTY) bitStream.Write(sourceTradeID); - - bitStream.Write(sourceType != eLootSourceType::NONE); - if (sourceType != eLootSourceType::NONE) bitStream.Write(sourceType); - - SystemAddress sysAddr = entity->GetSystemAddress(); - SEND_PACKET; -} - -void GameMessages::SendQuickBuildNotifyState(Entity* entity, eQuickBuildState prevState, eQuickBuildState state, const LWOOBJID& playerID) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(entity->GetObjectID()); - bitStream.Write(MessageType::Game::REBUILD_NOTIFY_STATE); - - bitStream.Write(prevState); - bitStream.Write(state); - bitStream.Write(playerID); - - SEND_PACKET_BROADCAST; -} - -void GameMessages::SendEnableQuickBuild(Entity* entity, bool enable, bool fail, bool success, eQuickBuildFailReason failReason, float duration, const LWOOBJID& playerID) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(entity->GetObjectID()); - bitStream.Write(MessageType::Game::ENABLE_REBUILD); - - bitStream.Write(enable); - bitStream.Write(fail); - bitStream.Write(success); - - bitStream.Write(failReason != eQuickBuildFailReason::NOT_GIVEN); - if (failReason != eQuickBuildFailReason::NOT_GIVEN) bitStream.Write(failReason); - - bitStream.Write(duration); - bitStream.Write(playerID); - - SEND_PACKET_BROADCAST; -} - -void GameMessages::SendTerminateInteraction(const LWOOBJID& objectID, eTerminateType type, const LWOOBJID& terminator) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(objectID); - bitStream.Write(MessageType::Game::TERMINATE_INTERACTION); - - bitStream.Write(terminator); - bitStream.Write(type); - - SEND_PACKET_BROADCAST; -} - -void GameMessages::SendSetJetPackMode(Entity* entity, bool use, bool bypassChecks, bool doHover, int effectID, float airspeed, float maxAirspeed, float verticalVelocity, int warningEffectID) { - /* historical jamesster jetpack values - if (bIsJamessterPhysics) { - fAirspeed = 75; - fMaxAirspeed = 75; - fVertVel = 15; - } - */ - - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(entity->GetObjectID()); - bitStream.Write(MessageType::Game::SET_JET_PACK_MODE); - - bitStream.Write(bypassChecks); - bitStream.Write(doHover); - bitStream.Write(use); - - bitStream.Write(effectID != -1); - if (effectID != -1) bitStream.Write(effectID); - - bitStream.Write(airspeed != 10); - if (airspeed != 10) bitStream.Write(airspeed); - - bitStream.Write(maxAirspeed != 15); - if (maxAirspeed != 15) bitStream.Write(maxAirspeed); - - bitStream.Write(verticalVelocity != 1); - if (verticalVelocity != 1) bitStream.Write(verticalVelocity); - - bitStream.Write(warningEffectID != -1); - if (warningEffectID != -1) bitStream.Write(warningEffectID); - - SEND_PACKET_BROADCAST; -} - -void GameMessages::SendSetNetworkScriptVar(Entity* entity, const SystemAddress& sysAddr, std::string data) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(entity->GetObjectID()); - bitStream.Write(MessageType::Game::SCRIPT_NETWORK_VAR_UPDATE); - - // FIXME: this is a bad place to need to do a conversion because we have no clue whether data is utf8 or plain ascii - // an this has performance implications - const auto u16Data = GeneralUtils::ASCIIToUTF16(data); - uint32_t dataSize = static_cast(u16Data.size()); - - bitStream.Write(dataSize); - for (auto value : u16Data) { - bitStream.Write(value); - } - if (dataSize > 0) bitStream.Write(0); - - if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; - SEND_PACKET; -} - -void GameMessages::SendSetPlayerControlScheme(Entity* entity, eControlScheme controlScheme) { - CBITSTREAM; - CMSGHEADER; - - bool bDelayCamSwitchIfInCinematic = true; - bool bSwitchCam = true; - - bitStream.Write(entity->GetObjectID()); - bitStream.Write(MessageType::Game::SET_PLAYER_CONTROL_SCHEME); - - bitStream.Write(bDelayCamSwitchIfInCinematic); - bitStream.Write(bSwitchCam); - - bitStream.Write(controlScheme != eControlScheme::SCHEME_A); - if (controlScheme != eControlScheme::SCHEME_A) bitStream.Write(controlScheme); - - SystemAddress sysAddr = entity->GetSystemAddress(); - SEND_PACKET; -} - -void GameMessages::SendPlayerReachedRespawnCheckpoint(Entity* entity, const NiPoint3& position, const NiQuaternion& rotation) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(entity->GetObjectID()); - bitStream.Write(MessageType::Game::PLAYER_REACHED_RESPAWN_CHECKPOINT); - - bitStream.Write(position.x); - bitStream.Write(position.y); - bitStream.Write(position.z); - - const bool bIsNotIdentity = rotation != QuatUtils::IDENTITY; - bitStream.Write(bIsNotIdentity); - - if (bIsNotIdentity) { - bitStream.Write(rotation.w); - bitStream.Write(rotation.x); - bitStream.Write(rotation.y); - bitStream.Write(rotation.z); - } - - SystemAddress sysAddr = entity->GetSystemAddress(); - SEND_PACKET; -} - -void GameMessages::SendMatchResponse(Entity* entity, const SystemAddress& sysAddr, int response) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(entity->GetObjectID()); - bitStream.Write(MessageType::Game::MATCH_RESPONSE); - bitStream.Write(response); - - SEND_PACKET; -} - -void GameMessages::SendMatchUpdate(Entity* entity, const SystemAddress& sysAddr, std::string data, eMatchUpdate type) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(entity->GetObjectID()); - bitStream.Write(MessageType::Game::MATCH_UPDATE); - bitStream.Write(data.size()); - for (char character : data) { - bitStream.Write(character); - } - if (data.size() > 0) bitStream.Write(0); - bitStream.Write(type); - - SEND_PACKET; -} - -void GameMessages::SendRequestActivitySummaryLeaderboardData(const LWOOBJID& objectID, const LWOOBJID& targetID, - const SystemAddress& sysAddr, const int32_t& gameID, - const int32_t& queryType, const int32_t& resultsEnd, - const int32_t& resultsStart, bool weekly) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(objectID); - bitStream.Write(MessageType::Game::REQUEST_ACTIVITY_SUMMARY_LEADERBOARD_DATA); - - bitStream.Write(gameID != 0); - if (gameID != 0) { - bitStream.Write(gameID); - } - - bitStream.Write(queryType != 1); - if (queryType != 1) { - bitStream.Write(queryType); - } - - bitStream.Write(resultsEnd != 10); - if (resultsEnd != 10) { - bitStream.Write(resultsEnd); - } - - bitStream.Write(resultsStart != 0); - if (resultsStart != 0) { - bitStream.Write(resultsStart); - } - - bitStream.Write(targetID); - bitStream.Write(weekly); - - SEND_PACKET; -} - -void GameMessages::SendSetShootingGalleryParams(LWOOBJID objectId, const SystemAddress& sysAddr, - float cameraFOV, - float cooldown, - float minDistance, - NiPoint3 muzzlePosOffset, - NiPoint3 playerPosOffset, - float projectileVelocity, - float timeLimit, - bool bUseLeaderboards) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(objectId); - bitStream.Write(MessageType::Game::SET_SHOOTING_GALLERY_PARAMS); - /* - bitStream.Write(cameraFOV); - bitStream.Write(cooldown); - bitStream.Write(minDistance); - bitStream.Write(muzzlePosOffset); - bitStream.Write(playerPosOffset); - bitStream.Write(projectileVelocity); - bitStream.Write(timeLimit); - bitStream.Write(bUseLeaderboards); - */ - // No clue about the order here - bitStream.Write(playerPosOffset); - bitStream.Write(projectileVelocity); - bitStream.Write(cooldown); - bitStream.Write(muzzlePosOffset); - bitStream.Write(minDistance); - bitStream.Write(cameraFOV); - bitStream.Write(bUseLeaderboards); - bitStream.Write(timeLimit); - - if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; - SEND_PACKET; -} - - -void GameMessages::SendNotifyClientShootingGalleryScore(LWOOBJID objectId, const SystemAddress& sysAddr, - float addTime, - int32_t score, - LWOOBJID target, - NiPoint3 targetPos) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(objectId); - bitStream.Write(MessageType::Game::NOTIFY_CLIENT_SHOOTING_GALLERY_SCORE); - bitStream.Write(addTime); - bitStream.Write(score); - bitStream.Write(target); - bitStream.Write(targetPos); - - if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; - SEND_PACKET; -} - - -void GameMessages::HandleUpdateShootingGalleryRotation(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) { - float angle = 0.0f; - NiPoint3 facing = NiPoint3Constant::ZERO; - NiPoint3 muzzlePos = NiPoint3Constant::ZERO; - inStream.Read(angle); - inStream.Read(facing); - inStream.Read(muzzlePos); -} - - -void GameMessages::HandleActivitySummaryLeaderboardData(RakNet::BitStream& inStream, Entity* entity, - const SystemAddress& sysAddr) { - LOG("We got mail!"); -} - -void GameMessages::SendActivitySummaryLeaderboardData(const LWOOBJID& objectID, const Leaderboard* leaderboard, const SystemAddress& sysAddr) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(objectID); - bitStream.Write(MessageType::Game::SEND_ACTIVITY_SUMMARY_LEADERBOARD_DATA); - - leaderboard->Serialize(bitStream); - SEND_PACKET; -} - -void GameMessages::HandleRequestActivitySummaryLeaderboardData(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) { - int32_t gameID = 0; - if (inStream.ReadBit()) inStream.Read(gameID); - - Leaderboard::InfoType queryType = Leaderboard::InfoType::MyStanding; - if (inStream.ReadBit()) inStream.Read(queryType); - - int32_t resultsEnd = 10; - if (inStream.ReadBit()) inStream.Read(resultsEnd); - - int32_t resultsStart = 0; - if (inStream.ReadBit()) inStream.Read(resultsStart); - - LWOOBJID target{}; - inStream.Read(target); - - bool weekly = inStream.ReadBit(); - - // The client won't accept more than 10 results even if we wanted it to - LeaderboardManager::SendLeaderboard(gameID, queryType, weekly, entity->GetObjectID(), entity->GetObjectID(), 10); -} - -void GameMessages::HandleActivityStateChangeRequest(RakNet::BitStream& inStream, Entity* entity) { - LWOOBJID objectID; - inStream.Read(objectID); - - int32_t value1; - inStream.Read(value1); - - int32_t value2; - inStream.Read(value2); - - uint32_t stringValueLength; - inStream.Read(stringValueLength); - - std::u16string stringValue; - for (uint32_t i = 0; i < stringValueLength; ++i) { - uint16_t character; - inStream.Read(character); - stringValue.push_back(character); - } - - auto* assosiate = Game::entityManager->GetEntity(objectID); - - LOG("%s [%i, %i] from %i to %i", GeneralUtils::UTF16ToWTF8(stringValue).c_str(), value1, value2, entity->GetLOT(), assosiate != nullptr ? assosiate->GetLOT() : 0); - - std::vector scriptedActs = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SHOOTING_GALLERY); - for (Entity* scriptEntity : scriptedActs) { - scriptEntity->OnActivityStateChangeRequest(objectID, value1, value2, stringValue); - } - - entity->OnActivityStateChangeRequest(objectID, value1, value2, stringValue); -} - -void GameMessages::SendSetRailMovement(const LWOOBJID& objectID, bool pathGoForward, std::u16string pathName, - uint32_t pathStart, const SystemAddress& sysAddr, int32_t railActivatorComponentID, - LWOOBJID railActivatorObjectID) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(objectID); - bitStream.Write(MessageType::Game::SET_RAIL_MOVEMENT); - - bitStream.Write(pathGoForward); - - bitStream.Write(pathName.size()); - for (auto character : pathName) { - bitStream.Write(character); - } - - bitStream.Write(pathStart); - - const auto componentIDIsDefault = railActivatorComponentID == -1; - bitStream.Write(!componentIDIsDefault); - if (!componentIDIsDefault) - bitStream.Write(railActivatorComponentID); - - const auto activatorObjectIDIsDefault = railActivatorObjectID == LWOOBJID_EMPTY; - bitStream.Write(!activatorObjectIDIsDefault); - if (!activatorObjectIDIsDefault) - bitStream.Write(railActivatorObjectID); - - if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; - SEND_PACKET; -} - -void GameMessages::SendStartRailMovement(const LWOOBJID& objectID, std::u16string pathName, std::u16string startSound, - std::u16string loopSound, std::u16string stopSound, const SystemAddress& sysAddr, - uint32_t pathStart, bool goForward, bool damageImmune, bool noAggro, bool notifyActor, - bool showNameBillboard, bool cameraLocked, bool collisionEnabled, bool useDB, - int32_t railComponentID, LWOOBJID railActivatorObjectID) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(objectID); - bitStream.Write(MessageType::Game::START_RAIL_MOVEMENT); - - bitStream.Write(damageImmune); - bitStream.Write(noAggro); - bitStream.Write(notifyActor); - bitStream.Write(showNameBillboard); - bitStream.Write(cameraLocked); - bitStream.Write(collisionEnabled); - - bitStream.Write(loopSound.size()); - for (auto character : loopSound) { - bitStream.Write(character); - } - - bitStream.Write(goForward); - - bitStream.Write(pathName.size()); - for (auto character : pathName) { - bitStream.Write(character); - } - - const auto pathStartIsDefault = pathStart == 0; - bitStream.Write(!pathStartIsDefault); - if (!pathStartIsDefault) { - bitStream.Write(pathStart); - } - - const auto railComponentIDIsDefault = railComponentID == -1; - bitStream.Write(!railComponentIDIsDefault); - if (!railComponentIDIsDefault) { - bitStream.Write(railComponentID); - } - - const auto railObjectIDIsDefault = railActivatorObjectID == LWOOBJID_EMPTY; - bitStream.Write(!railObjectIDIsDefault); - if (!railObjectIDIsDefault) { - bitStream.Write(railActivatorObjectID); - } - - bitStream.Write(startSound.size()); - for (auto character : startSound) { - bitStream.Write(character); - } - - bitStream.Write(stopSound.size()); - for (auto character : stopSound) { - bitStream.Write(character); - } - - bitStream.Write(useDB); - - if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; - SEND_PACKET; -} - -void GameMessages::SendNotifyClientObject(const LWOOBJID& objectID, std::u16string name, int param1, int param2, const LWOOBJID& paramObj, std::string paramStr, const SystemAddress& sysAddr) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(objectID); - bitStream.Write(MessageType::Game::NOTIFY_CLIENT_OBJECT); - - bitStream.Write(name.size()); - for (auto character : name) { - bitStream.Write(character); - } - - bitStream.Write(param1); - - bitStream.Write(param2); - - bitStream.Write(paramObj); - - bitStream.Write(paramStr.size()); - for (auto character : paramStr) { - bitStream.Write(character); - } - - if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; - SEND_PACKET; -} - -void GameMessages::SendNotifyClientZoneObject(const LWOOBJID& objectID, const std::u16string& name, int param1, - int param2, const LWOOBJID& paramObj, const std::string& paramStr, - const SystemAddress& sysAddr) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(objectID); - bitStream.Write(MessageType::Game::NOTIFY_CLIENT_ZONE_OBJECT); - - bitStream.Write(name.size()); - for (const auto& character : name) { - bitStream.Write(character); - } - - bitStream.Write(param1); - bitStream.Write(param2); - bitStream.Write(paramObj); - - bitStream.Write(paramStr.size()); - for (const auto& character : paramStr) { - bitStream.Write(character); - } - - if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; - SEND_PACKET; -} - -void GameMessages::SendNotifyClientFailedPrecondition(LWOOBJID objectId, const SystemAddress& sysAddr, - const std::u16string& failedReason, int preconditionID) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(objectId); - bitStream.Write(MessageType::Game::NOTIFY_CLIENT_FAILED_PRECONDITION); - - bitStream.Write(failedReason.size()); - for (uint16_t character : failedReason) { - bitStream.Write(character); - } - - bitStream.Write(preconditionID); - - if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; - SEND_PACKET; -} - -void GameMessages::SendSetName(LWOOBJID objectID, std::u16string name, const SystemAddress& sysAddr) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(objectID); - bitStream.Write(MessageType::Game::SET_NAME); - - bitStream.Write(name.size()); - - for (size_t i = 0; i < name.size(); ++i) - bitStream.Write(name[i]); - - if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; - SEND_PACKET; -} - -// Property - -void GameMessages::SendLockNodeRotation(Entity* entity, std::string nodeName) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(entity->GetObjectID()); - bitStream.Write(MessageType::Game::LOCK_NODE_ROTATION); - - bitStream.Write(nodeName.size()); - for (char character : nodeName) { - bitStream.Write(character); - } - - SEND_PACKET_BROADCAST; -} - -void GameMessages::SendOrientToAngle(LWOOBJID objectId, bool bRelativeToCurrent, float fAngle, const SystemAddress& sysAddr) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(objectId); - bitStream.Write(MessageType::Game::ORIENT_TO_ANGLE); - - bitStream.Write(bRelativeToCurrent); - bitStream.Write(fAngle); - - if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; - SEND_PACKET; -} - - -void GameMessages::SendNotifyObject(LWOOBJID objectId, LWOOBJID objIDSender, std::u16string name, const SystemAddress& sysAddr, int param1, int param2) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(objectId); - bitStream.Write(MessageType::Game::NOTIFY_OBJECT); - - bitStream.Write(objIDSender); - bitStream.Write(name.size()); - for (const auto character : name) { - bitStream.Write(character); - } - - bitStream.Write(param1); - bitStream.Write(param2); - - if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; - SEND_PACKET; -} - -void GameMessages::HandleVerifyAck(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) { - bool bDifferent; - std::string sBitStream; - uint32_t uiHandle = 0; - - bDifferent = inStream.ReadBit(); - - uint32_t sBitStreamLength = 0; - inStream.Read(sBitStreamLength); - if (sBitStreamLength > MAX_MESSAGE_LENGTH) return; - for (uint64_t k = 0; k < sBitStreamLength; k++) { - uint8_t character; - inStream.Read(character); - sBitStream.push_back(character); - } - - if (inStream.ReadBit()) { - inStream.Read(uiHandle); - } -} - -void GameMessages::SendTeamPickupItem(LWOOBJID objectId, LWOOBJID lootID, LWOOBJID lootOwnerID, const SystemAddress& sysAddr) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(objectId); - bitStream.Write(MessageType::Game::TEAM_PICKUP_ITEM); - - bitStream.Write(lootID); - bitStream.Write(lootOwnerID); - - if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; - SEND_PACKET; -} - -//Pets: - -void GameMessages::SendDisplayZoneSummary(LWOOBJID objectId, const SystemAddress& sysAddr, bool isPropertyMap, bool isZoneStart, LWOOBJID sender) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(objectId); - bitStream.Write(MessageType::Game::DISPLAY_ZONE_SUMMARY); - - bitStream.Write(isPropertyMap); - bitStream.Write(isZoneStart); - bitStream.Write(sender != LWOOBJID_EMPTY); - if (sender != LWOOBJID_EMPTY) bitStream.Write(sender); - - if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; - SEND_PACKET; -} - -//UI - -// Mounts - -void GameMessages::SendSetMountInventoryID(Entity* entity, const LWOOBJID& objectID, const SystemAddress& sysAddr) { - CBITSTREAM; - CMSGHEADER; - bitStream.Write(entity->GetObjectID()); - bitStream.Write(MessageType::Game::SET_MOUNT_INVENTORY_ID); - bitStream.Write(objectID != LWOOBJID_EMPTY); - if (objectID != LWOOBJID_EMPTY) bitStream.Write(objectID); - - SEND_PACKET_BROADCAST; -} - -void GameMessages::UseSkillSet::Serialize(RakNet::BitStream& bitStream) const { - bitStream.Write(bRemove); - bitStream.Write(possessedId != LWOOBJID_EMPTY); - if (possessedId != LWOOBJID_EMPTY) bitStream.Write(possessedId); - bitStream.Write(setId != -1); - if (setId != -1) bitStream.Write(setId); -} - -void GameMessages::HandleDismountComplete(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) { - // Get the objectID from the bitstream - LWOOBJID objectId{}; - inStream.Read(objectId); - - // If we aren't possessing somethings, the don't do anything - if (objectId != LWOOBJID_EMPTY) { - auto* possessorComponent = entity->GetComponent(); - auto* mount = Game::entityManager->GetEntity(objectId); - // make sure we have the things we need and they aren't null - if (possessorComponent && mount) { - if (!possessorComponent->GetIsDismounting()) return; - possessorComponent->SetIsDismounting(false); - possessorComponent->SetPossessable(LWOOBJID_EMPTY); - possessorComponent->SetPossessableType(ePossessionType::NO_POSSESSION); - - // character related things - auto* character = entity->GetComponent(); - if (character) { - // If we had an active item turn it off - if (possessorComponent->GetMountItemID() != LWOOBJID_EMPTY) { - GameMessages::MarkInventoryItemAsActive markActive; - markActive.target = entity->GetObjectID(); - markActive.bActive = false; - markActive.iType = eUnequippableActiveType::MOUNT; - markActive.itemID = possessorComponent->GetMountItemID(); - markActive.Send(entity->GetSystemAddress()); - } - possessorComponent->SetMountItemID(LWOOBJID_EMPTY); - } - - // Set that the controllabel phsyics comp is teleporting - auto* controllablePhysicsComponent = entity->GetComponent(); - if (controllablePhysicsComponent) controllablePhysicsComponent->SetIsTeleporting(true); - - // Call dismoint on the possessable comp to let it handle killing the possessable - auto* possessableComponent = mount->GetComponent(); - if (possessableComponent) possessableComponent->Dismount(); - - // Update the entity that was possessing - Game::entityManager->SerializeEntity(entity); - } - } -} - - -void GameMessages::HandleAcknowledgePossession(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) { - Game::entityManager->SerializeEntity(entity); - bool hasObjectId{}; - inStream.Read(hasObjectId); - if (hasObjectId) { - LWOOBJID objectId{}; - inStream.Read(objectId); - auto* mount = Game::entityManager->GetEntity(objectId); - if (mount) Game::entityManager->SerializeEntity(mount); - } -} - -//Racing - - - - - -void GameMessages::SendUpdateReputation(const LWOOBJID objectId, const int64_t reputation, const SystemAddress& sysAddr) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(objectId); - bitStream.Write(MessageType::Game::UPDATE_REPUTATION); - - bitStream.Write(reputation); - - SEND_PACKET; -} - -// NT - -//----------------------------------------------------------------------------------------------------------------------------------------------- -//------------------------------------------------------------------- Handlers ------------------------------------------------------------------ -//----------------------------------------------------------------------------------------------------------------------------------------------- - -void GameMessages::HandleToggleGhostReferenceOverride(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) { - bool bOverride = false; - - inStream.Read(bOverride); - - auto* player = PlayerManager::GetPlayer(sysAddr); - - if (player != nullptr) { - auto* ghostComponent = entity->GetComponent(); - if (ghostComponent) ghostComponent->SetGhostOverride(bOverride); - - Game::entityManager->UpdateGhosting(player); - } -} - - -void GameMessages::HandleSetGhostReferencePosition(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) { - NiPoint3 position; - - inStream.Read(position); - - auto* player = PlayerManager::GetPlayer(sysAddr); - - if (player != nullptr) { - auto* ghostComponent = entity->GetComponent(); - if (ghostComponent) ghostComponent->SetGhostOverridePoint(position); - - Game::entityManager->UpdateGhosting(player); - } -} - - -void GameMessages::HandleParseChatMessage(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) { - std::u16string wsString; - int iClientState; - inStream.Read(iClientState); - - uint32_t wsStringLength; - inStream.Read(wsStringLength); - - if (wsStringLength > MAX_MESSAGE_LENGTH) { - LOG("Max message length reached, capping message."); - wsStringLength = MAX_MESSAGE_LENGTH; - } - - for (uint32_t i = 0; i < wsStringLength; ++i) { - uint16_t character; - inStream.Read(character); - wsString.push_back(character); - } - - if (!wsString.empty() && wsString[0] == L'/') { - SlashCommandHandler::HandleChatCommand(wsString, entity, sysAddr); - } -} - -void GameMessages::HandleFireEventServerSide(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) { - uint32_t argsLength{}; - std::u16string args{}; - bool param1IsDefault{}; - int param1 = -1; - bool param2IsDefault{}; - int param2 = -1; - bool param3IsDefault{}; - int param3 = -1; - LWOOBJID senderID{}; - - inStream.Read(argsLength); - if (argsLength > MAX_MESSAGE_LENGTH) return; - for (uint32_t i = 0; i < argsLength; ++i) { - uint16_t character; - inStream.Read(character); - args.push_back(character); - } - inStream.Read(param1IsDefault); - if (param1IsDefault) inStream.Read(param1); - inStream.Read(param2IsDefault); - if (param2IsDefault) inStream.Read(param2); - inStream.Read(param3IsDefault); - if (param3IsDefault) inStream.Read(param3); - inStream.Read(senderID); - - auto* sender = Game::entityManager->GetEntity(senderID); - auto* player = PlayerManager::GetPlayer(sysAddr); - - if (!player) { - return; - } - - // This should probably get it's own "ServerEvents" system or something at some point - if (args == u"ZonePlayer") { - // Should probably check to make sure they're using a launcher at some point before someone makes a hack that lets you testmap - - LWOCLONEID cloneId = 0; - LWOMAPID mapId = 0; - - auto* rocketPad = entity->GetComponent(); - - if (rocketPad == nullptr) return; - - cloneId = rocketPad->GetSelectedCloneId(player->GetObjectID()); - - if (param2) { - mapId = rocketPad->GetDefaultZone(); - } else { - mapId = param3; - } - - if (mapId == 0) { - mapId = rocketPad->GetSelectedMapId(player->GetObjectID()); - } - - if (mapId == 0) { - mapId = Game::zoneManager->GetZoneID().GetMapID(); // Fallback to sending the player back to the same zone. - } - - LOG("Player %llu has requested zone transfer to (%i, %i).", sender->GetObjectID(), static_cast(mapId), static_cast(cloneId)); - - auto* character = player->GetCharacter(); - - if (mapId <= 0) { - return; - } - - ZoneInstanceManager::Instance()->RequestZoneTransfer(Game::server, mapId, cloneId, false, [=](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string serverIP, uint16_t serverPort) { - LOG("Transferring %s to Zone %i (Instance %i | Clone %i | Mythran Shift: %s) with IP %s and Port %i", character->GetName().c_str(), zoneID, zoneInstance, zoneClone, mythranShift == true ? "true" : "false", serverIP.c_str(), serverPort); - - if (character) { - auto* characterComponent = player->GetComponent(); - if (characterComponent) { - characterComponent->AddVisitedLevel(LWOZONEID(zoneID, LWOINSTANCEID_INVALID, zoneClone)); - } - - character->SetZoneID(zoneID); - character->SetZoneInstance(zoneInstance); - character->SetZoneClone(zoneClone); - } - - ClientPackets::TransferToWorld transfer; - transfer.serverIP = LUString(serverIP); - transfer.serverPort = serverPort; - transfer.mythranShift = mythranShift; - transfer.Send(sysAddr); - return; - }); - } - - entity->OnFireEventServerSide(sender, GeneralUtils::UTF16ToWTF8(args), param1, param2, param3); -} - -void GameMessages::HandleRequestPlatformResync(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) { - if (entity->GetLOT() == 6267 || entity->GetLOT() == 16141) return; - GameMessages::SendPlatformResync(entity, sysAddr); -} - -void GameMessages::HandleQuickBuildCancel(RakNet::BitStream& inStream, Entity* entity) { - bool bEarlyRelease; - LWOOBJID userID; - - inStream.Read(bEarlyRelease); - inStream.Read(userID); - - auto* quickBuildComponent = static_cast(entity->GetComponent(eReplicaComponentType::QUICK_BUILD));; - if (!quickBuildComponent) return; - - quickBuildComponent->CancelQuickBuild(Game::entityManager->GetEntity(userID), eQuickBuildFailReason::CANCELED_EARLY); -} - -void GameMessages::HandleNotifyServerLevelProcessingComplete(RakNet::BitStream& inStream, Entity* entity) { - auto* levelComp = entity->GetComponent(); - if (!levelComp) return; - auto* character = entity->GetComponent(); - if (!character) return; - - //Update our character's level in memory: - levelComp->SetLevel(levelComp->GetLevel() + 1); - - levelComp->HandleLevelUp(); - - auto* inventoryComponent = entity->GetComponent(); - - if (inventoryComponent != nullptr) { - auto* inventory = inventoryComponent->GetInventory(ITEMS); - - if (inventory != nullptr && Game::config->GetValue("disable_extra_backpack") != "1") { - inventory->SetSize(inventory->GetSize() + 2); - } - } - - //Play the level up effect: - GameMessages::PlayFXEffect(entity->GetObjectID(), 7074, u"create", "7074").Send(UNASSIGNED_SYSTEM_ADDRESS); - - //Send a notification in chat: - std::stringstream wss; - wss << "level=1:"; - wss << levelComp->GetLevel(); - wss << "\n"; - wss << "name=0:"; - wss << character->GetName(); - - // FIXME: only really need utf8 conversion for the name, so move that up? - std::u16string attrs = GeneralUtils::UTF8ToUTF16(wss.str()); - std::u16string wsText = u"UI_LEVEL_PROGRESSION_LEVELUP_MESSAGE"; - - GameMessages::BroadcastTextToChatbox chatboxText; - chatboxText.target = entity->GetObjectID(); - chatboxText.attrs = attrs; - chatboxText.wsText = wsText; - chatboxText.Send(UNASSIGNED_SYSTEM_ADDRESS); -} - -void GameMessages::HandlePickupCurrency(RakNet::BitStream& inStream, Entity* entity) { - unsigned int currency; - inStream.Read(currency); - - if (currency == 0) return; - - auto* ch = entity->GetCharacter(); - if (ch && entity->PickupCoins(currency)) { - ch->SetCoins(ch->GetCoins() + currency, eLootSourceType::PICKUP); - } -} - -void GameMessages::SendSetGravityScale(const LWOOBJID& target, const float effectScale, const SystemAddress& sysAddr) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(target); - bitStream.Write(MessageType::Game::SET_GRAVITY_SCALE); - - bitStream.Write(effectScale); - - SEND_PACKET; -} - -void GameMessages::HandleMatchRequest(RakNet::BitStream& inStream, Entity* entity) { - LWOOBJID activator; - uint32_t playerChoicesLen; - std::string playerChoices; - int type; - int value; - - inStream.Read(activator); - inStream.Read(playerChoicesLen); - if (playerChoicesLen > MAX_MESSAGE_LENGTH) return; - for (uint32_t i = 0; i < playerChoicesLen; ++i) { - uint16_t character; - inStream.Read(character); - playerChoices.push_back(character); - } - if (playerChoicesLen > 0) { - uint16_t nullTerm; - inStream.Read(nullTerm); - } - inStream.Read(type); - inStream.Read(value); - - std::vector scriptedActs = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SCRIPTED_ACTIVITY); - if (type == 0) { // join - if (value != 0) { - for (Entity* scriptedAct : scriptedActs) { - ScriptedActivityComponent* comp = static_cast(scriptedAct->GetComponent(eReplicaComponentType::SCRIPTED_ACTIVITY)); - if (!comp) continue; - if (comp->GetActivityID() == value) { - comp->PlayerJoin(entity); - } - } - } else { - - } - } else if (type == 1) { // ready/unready - for (Entity* scriptedAct : scriptedActs) { - ScriptedActivityComponent* comp = static_cast(scriptedAct->GetComponent(eReplicaComponentType::SCRIPTED_ACTIVITY)); - if (!comp) continue; - if (comp->PlayerIsInQueue(entity)) { - comp->PlayerReady(entity, value); - } - } - } -} - -void GameMessages::HandleReportBug(RakNet::BitStream& inStream, Entity* entity) { - //Definitely not stolen from autogenerated code, no sir: - IBugReports::Info reportInfo; - - //Reading: - uint32_t messageLength; - inStream.Read(messageLength); - - if (messageLength > MAX_MESSAGE_LENGTH) return; - - for (uint32_t i = 0; i < (messageLength); ++i) { - uint16_t character; - inStream.Read(character); - reportInfo.body.push_back(static_cast(character)); - } - - auto character = entity->GetCharacter(); - if (character) reportInfo.characterId = character->GetID(); - - uint32_t clientVersionLength; - inStream.Read(clientVersionLength); - if (clientVersionLength > MAX_MESSAGE_LENGTH) return; - for (unsigned int k = 0; k < clientVersionLength; k++) { - unsigned char character; - inStream.Read(character); - reportInfo.clientVersion.push_back(character); - } - - uint32_t nOtherPlayerIDLength; - inStream.Read(nOtherPlayerIDLength); - if (nOtherPlayerIDLength > MAX_MESSAGE_LENGTH) return; - for (unsigned int k = 0; k < nOtherPlayerIDLength; k++) { - unsigned char character; - inStream.Read(character); - reportInfo.otherPlayer.push_back(character); - } - - uint32_t selectionLength; - inStream.Read(selectionLength); - if (selectionLength > MAX_MESSAGE_LENGTH) return; - for (unsigned int k = 0; k < selectionLength; k++) { - unsigned char character; - inStream.Read(character); - reportInfo.selection.push_back(character); - } - - // Report Abuse about another player sends their ID here (and "0" otherwise); that's a player report, not a bug - if (const auto reportedId = GeneralUtils::TryParse(reportInfo.otherPlayer).value_or(LWOOBJID_EMPTY); reportedId != LWOOBJID_EMPTY) { - PlayerReports::ReportPlayer(entity, reportedId, reportInfo.body); - return; - } - - Database::Get()->InsertNewBugReport(reportInfo); - DashboardNotify::Changed("bug_reports"); -} - -void -GameMessages::HandleClientRailMovementReady(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) { - const auto possibleRails = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::RAIL_ACTIVATOR); - for (const auto* possibleRail : possibleRails) { - const auto* rail = possibleRail->GetComponent(); - if (rail != nullptr) { - rail->OnRailMovementReady(entity); - } - } -} - -void GameMessages::HandleCancelRailMovement(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) { - const auto immediate = inStream.ReadBit(); - - const auto possibleRails = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::RAIL_ACTIVATOR); - for (const auto* possibleRail : possibleRails) { - auto* rail = possibleRail->GetComponent(); - if (rail != nullptr) { - rail->OnCancelRailMovement(entity); - } - } -} - -void GameMessages::HandlePlayerRailArrivedNotification(RakNet::BitStream& inStream, Entity* entity, - const SystemAddress& sysAddr) { - uint32_t pathNameLength; - inStream.Read(pathNameLength); - if (pathNameLength > MAX_MESSAGE_LENGTH) return; - std::u16string pathName; - for (auto k = 0; k < pathNameLength; k++) { - uint16_t c; - inStream.Read(c); - pathName.push_back(c); - } - - int32_t waypointNumber; - inStream.Read(waypointNumber); - - const auto possibleRails = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::RAIL_ACTIVATOR); - for (auto* possibleRail : possibleRails) { - if (possibleRail) possibleRail->GetScript()->OnPlayerRailArrived(possibleRail, entity, pathName, waypointNumber); - } -} - -void GameMessages::HandleModifyPlayerZoneStatistic(RakNet::BitStream& inStream, Entity* entity) { - const auto set = inStream.ReadBit(); - const auto statisticsName = GeneralUtils::ReadWString(inStream); - - int32_t value; - if (inStream.ReadBit()) { - inStream.Read(value); - } else { - value = 0; - } - - LWOMAPID zone; - if (inStream.ReadBit()) { - inStream.Read(zone); - } else { - zone = LWOMAPID_INVALID; - } - - // Notify the character component that something's changed - auto* characterComponent = entity->GetComponent(); - if (characterComponent != nullptr) { - characterComponent->HandleZoneStatisticsUpdate(zone, statisticsName, value); - } -} - -void GameMessages::HandleUpdatePlayerStatistic(RakNet::BitStream& inStream, Entity* entity) { - int32_t updateID; - inStream.Read(updateID); - - int64_t updateValue; - if (inStream.ReadBit()) { - inStream.Read(updateValue); - } else { - updateValue = 1; - } - - auto* characterComponent = entity->GetComponent(); - if (characterComponent != nullptr) { - characterComponent->UpdatePlayerStatistic(static_cast(updateID), static_cast(std::max(updateValue, static_cast(0))), true); - } -} - -void GameMessages::HandleZoneSummaryDismissed(RakNet::BitStream& inStream, Entity* entity) { - LWOOBJID player_id; - inStream.Read(player_id); - auto target = Game::entityManager->GetEntity(player_id); - entity->TriggerEvent(eTriggerEventType::ZONE_SUMMARY_DISMISSED, target); -}; - -void GameMessages::SendForceCameraTargetCycle(Entity* entity, bool bForceCycling, eCameraTargetCyclingMode cyclingMode, LWOOBJID optionalTargetID) { - CBITSTREAM; - CMSGHEADER; - - bitStream.Write(entity->GetObjectID()); - bitStream.Write(MessageType::Game::FORCE_CAMERA_TARGET_CYCLE); - bitStream.Write(bForceCycling); - bitStream.Write(cyclingMode != eCameraTargetCyclingMode::ALLOW_CYCLE_TEAMMATES); - if (cyclingMode != eCameraTargetCyclingMode::ALLOW_CYCLE_TEAMMATES) bitStream.Write(cyclingMode); - bitStream.Write(optionalTargetID); - - auto sysAddr = entity->GetSystemAddress(); - SEND_PACKET; -} - +#include "ServiceType.h" +#include "dServer.h" namespace GameMessages { bool GameMsg::Send() { @@ -1577,7 +19,7 @@ namespace GameMessages { } void NetGameMsg::WritePacket(RakNet::BitStream& bitStream) const { - CMSGHEADER; + LUBitStream(ServiceType::CLIENT, MessageType::Client::GAME_MSG).WriteHeader(bitStream); bitStream.Write(target); // Who this message will be sent to on the (a) client bitStream.Write(msgId); // the ID of this message @@ -1586,216 +28,22 @@ namespace GameMessages { } void NetGameMsg::SendToClient(const SystemAddress& sysAddr) const { - CBITSTREAM; + RakNet::BitStream bitStream; WritePacket(bitStream); - SEND_PACKET; + Game::server->Send(bitStream, sysAddr, false); } void NetGameMsg::BroadcastExcept(const SystemAddress& excluded) const { - CBITSTREAM; + RakNet::BitStream bitStream; WritePacket(bitStream); Game::server->Send(bitStream, excluded, true); } void NetGameMsg::Send(const SystemAddress& sysAddr) const { - CBITSTREAM; + RakNet::BitStream bitStream; WritePacket(bitStream); // Send to everyone if someone sent unassigned system address, or to one specific client. - if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) { - SEND_PACKET_BROADCAST; - } else { - SEND_PACKET; - } - } - - void DisplayTooltip::Serialize(RakNet::BitStream& bitStream) const { - bitStream.Write(doOrDie); - bitStream.Write(noRepeat); - bitStream.Write(noRevive); - bitStream.Write(isPropertyTooltip); - bitStream.Write(show); - bitStream.Write(translate); - bitStream.Write(time); - bitStream.Write(id.size()); - bitStream.Write(id); - - std::string toWrite; - for (const auto& item : localizeParams | std::views::values) { - toWrite += item->GetString() + "\n"; - } - if (!toWrite.empty()) toWrite.pop_back(); - bitStream.Write(toWrite.size()); - bitStream.Write(GeneralUtils::ASCIIToUTF16(toWrite)); - if (!toWrite.empty()) bitStream.Write(0x00); // Null Terminator - - bitStream.Write(imageName.size()); - bitStream.Write(imageName); - bitStream.Write(text.size()); - bitStream.Write(text); - } - - void UseItemOnClient::Serialize(RakNet::BitStream& bitStream) const { - bitStream.Write(itemLOT); - bitStream.Write(itemToUse); - bitStream.Write(itemType); - bitStream.Write(playerId); - bitStream.Write(targetPosition.x); - bitStream.Write(targetPosition.y); - bitStream.Write(targetPosition.z); - } - - void SetModelToBuild::Serialize(RakNet::BitStream& bitStream) const { - bitStream.Write(modelLot != -1); - if (modelLot != -1) bitStream.Write(modelLot); - } - - void SpawnModelBricks::Serialize(RakNet::BitStream& bitStream) const { - bitStream.Write(amount != 0.0f); - if (amount != 0.0f) bitStream.Write(amount); - bitStream.Write(position != NiPoint3Constant::ZERO); - if (position != NiPoint3Constant::ZERO) { - bitStream.Write(position.x); - bitStream.Write(position.y); - bitStream.Write(position.z); - } - } - - bool ShootingGalleryFire::Deserialize(RakNet::BitStream& bitStream) { - if (!bitStream.Read(target.x)) return false; - if (!bitStream.Read(target.y)) return false; - if (!bitStream.Read(target.z)) return false; - if (!bitStream.Read(rotation.w)) return false; - if (!bitStream.Read(rotation.x)) return false; - if (!bitStream.Read(rotation.y)) return false; - if (!bitStream.Read(rotation.z)) return false; - return true; - } - - void ShootingGalleryFire::Handle(Entity& entity, const SystemAddress& sysAddr) { - entity.OnShootingGalleryFire(*this); - } - - bool RequestServerObjectInfo::Deserialize(RakNet::BitStream& bitStream) { - if (!bitStream.Read(bVerbose)) return false; - if (!bitStream.Read(clientId)) return false; - if (!bitStream.Read(targetForReport)) return false; - return true; - } - - void RequestServerObjectInfo::Handle(Entity& entity, const SystemAddress& sysAddr) { - auto* handlingEntity = Game::entityManager->GetEntity(targetForReport); - if (handlingEntity) { - RequestServerObjectInfoEvent event(*this); - handlingEntity->HandleMsg(event); - } else LOG("Failed to find target %llu", targetForReport); - } - - bool RequestUse::Deserialize(RakNet::BitStream& stream) { - if (!stream.Read(bIsMultiInteractUse)) return false; - if (!stream.Read(multiInteractID)) return false; - if (!stream.Read(multiInteractType)) return false; - if (!stream.Read(object)) return false; - if (!stream.Read(secondary)) return false; - return true; - } - - void RequestUse::Handle(Entity& entity, const SystemAddress& sysAddr) { - Entity* interactedObject = Game::entityManager->GetEntity(object); - - if (interactedObject == nullptr) { - LOG("Object %llu tried to interact, but doesn't exist!", object); - - return; - } - - if (interactedObject->GetLOT() == 9524) { - entity.GetCharacter()->SetBuildMode(true); - } - - if (bIsMultiInteractUse) { - if (multiInteractType == 0) { - auto* missionOfferComponent = static_cast(interactedObject->GetComponent(eReplicaComponentType::MISSION_OFFER)); - - if (missionOfferComponent != nullptr) { - missionOfferComponent->OfferMissions(&entity, multiInteractID); - } - } else { - interactedObject->OnUse(&entity); - } - } else { - interactedObject->OnUse(&entity); - } - - RequestUseEvent event(*this); - interactedObject->HandleMsg(event); - - //Perform use task if possible: - auto missionComponent = entity.GetComponent(); - - if (!missionComponent) return; - - missionComponent->Progress(eMissionTaskType::TALK_TO_NPC, interactedObject->GetLOT(), interactedObject->GetObjectID()); - missionComponent->Progress(eMissionTaskType::INTERACT, interactedObject->GetLOT(), interactedObject->GetObjectID()); - } - - void PlayBehaviorSound::Serialize(RakNet::BitStream& stream) const { - stream.Write(soundID != -1); - if (soundID != -1) stream.Write(soundID); - } - - void EmotePlayed::Serialize(RakNet::BitStream& stream) const { - stream.Write(emoteID); - stream.Write(targetID); - } - - void DropClientLoot::Serialize(RakNet::BitStream& stream) const { - stream.Write(bUsePosition); - - stream.Write(finalPosition != NiPoint3Constant::ZERO); - if (finalPosition != NiPoint3Constant::ZERO) stream.Write(finalPosition); - - stream.Write(currency); - stream.Write(item); - stream.Write(lootID); - stream.Write(ownerID); - stream.Write(sourceID); - - stream.Write(spawnPos != NiPoint3Constant::ZERO); - if (spawnPos != NiPoint3Constant::ZERO) stream.Write(spawnPos); - } - - bool PickupItem::Deserialize(RakNet::BitStream& stream) { - if (!stream.Read(lootID)) return false; - if (!stream.Read(lootOwnerID)) return false; - return true; - } - - void PickupItem::Handle(Entity& entity, const SystemAddress& sysAddr) { - auto* team = TeamManager::Instance()->GetTeam(entity.GetObjectID()); - LOG("Has team %i picking up %llu:%llu", team != nullptr, lootID, lootOwnerID); - if (team) { - for (const auto memberId : team->members) { - PickupItemEvent event(*this); - event.Send(memberId); - TeamPickupItem teamPickupMsg{}; - teamPickupMsg.target = lootID; - teamPickupMsg.lootID = lootID; - teamPickupMsg.lootOwnerID = lootOwnerID; - const auto* const memberEntity = Game::entityManager->GetEntity(memberId); - if (memberEntity) teamPickupMsg.Send(memberEntity->GetSystemAddress()); - } - } else { - entity.PickupItem(lootID); - } - } - - void TeamPickupItem::Serialize(RakNet::BitStream& stream) const { - stream.Write(lootID); - stream.Write(lootOwnerID); - } - - void ToggleGMInvis::Serialize(RakNet::BitStream& stream) const { - stream.Write(bStateOut); + Game::server->Send(bitStream, sysAddr, sysAddr == UNASSIGNED_SYSTEM_ADDRESS); } } diff --git a/dGame/dGameMessages/GameMessages.h b/dGame/dGameMessages/GameMessages.h index e0d2c2b8e..4e565aba5 100644 --- a/dGame/dGameMessages/GameMessages.h +++ b/dGame/dGameMessages/GameMessages.h @@ -47,11 +47,6 @@ enum class eReponseMoveItemBetweenInventoryTypeCode : int32_t; enum class eMissionState : int; enum class AiState : uint32_t; -enum class eCameraTargetCyclingMode : int32_t { - ALLOW_CYCLE_TEAMMATES, - DISALLOW_CYCLING -}; - namespace GameMessages { /** * A server-internal event, delivered only to the handlers entities/components/scripts registered with @@ -129,203 +124,7 @@ namespace GameMessages { return event.Send(); } - void SendFireEventClientSide(const LWOOBJID& objectID, const SystemAddress& sysAddr, std::u16string args, const LWOOBJID& object, int64_t param1, int param2, const LWOOBJID& sender); - void SendTeleport(const LWOOBJID& objectID, const NiPoint3& pos, const NiQuaternion& rot, const SystemAddress& sysAddr, bool bSetRotation = false); - void SendPlayerReady(Entity* entity, const SystemAddress& sysAddr); - void SendInvalidZoneTransferList(Entity* entity, const SystemAddress& sysAddr, const std::u16string& feedbackURL, const std::u16string& invalidMapTransferList, bool feedbackOnExit, bool feedbackOnInvalidTransfer); - - void SendPlayerSetCameraCyclingMode(const LWOOBJID& objectID, const SystemAddress& sysAddr, bool bAllowCyclingWhileDeadOnly = true, eCyclingMode cyclingMode = eCyclingMode::ALLOW_CYCLE_TEAMMATES); - - void SendStartPathing(Entity* entity); - - // special is for the FV tree platform, feature is complete if we just do that so meh - void SendPlatformResync(Entity* entity, const SystemAddress& sysAddr, bool bStopAtDesiredWaypoint = false, - int iIndex = 0, int iDesiredWaypointIndex = 1, int nextIndex = 1, - eMovementPlatformState movementState = eMovementPlatformState::Moving, bool special = false); - - void SendRestoreToPostLoadStats(Entity* entity, const SystemAddress& sysAddr); - void SendServerDoneLoadingAllObjects(Entity* entity, const SystemAddress& sysAddr); - void SendGMLevelBroadcast(const LWOOBJID& objectID, eGameMasterLevel level); - void SendChatModeUpdate(const LWOOBJID& objectID, eGameMasterLevel level); - - void SendChangeObjectWorldState(const LWOOBJID& objectID, eObjectWorldState state, const SystemAddress& sysAddr); - - void SendModifyLEGOScore(Entity* entity, const SystemAddress& sysAddr, int64_t score, eLootSourceType sourceType); - - void SendSetCurrency(Entity* entity, int64_t currency, int lootType, const LWOOBJID& sourceID, const LOT& sourceLOT, int sourceTradeID, bool overrideCurrent, eLootSourceType sourceType); - - void SendQuickBuildNotifyState(Entity* entity, eQuickBuildState prevState, eQuickBuildState state, const LWOOBJID& playerID); - void SendEnableQuickBuild(Entity* entity, bool enable, bool fail, bool success, eQuickBuildFailReason failReason, float duration, const LWOOBJID& playerID); - void AddActivityOwner(Entity* entity, LWOOBJID& ownerID); - void SendTerminateInteraction(const LWOOBJID& objectID, eTerminateType type, const LWOOBJID& terminator); - - void SendSetGravityScale(const LWOOBJID& target, const float effectScale, const SystemAddress& sysAddr); - - void SendSetJetPackMode(Entity* entity, bool use, bool bypassChecks = false, bool doHover = false, int effectID = -1, float airspeed = 10, float maxAirspeed = 15, float verticalVelocity = 1, int warningEffectID = -1); - void SendSetNetworkScriptVar(Entity* entity, const SystemAddress& sysAddr, std::string data); - - void SendSetPlayerControlScheme(Entity* entity, eControlScheme controlScheme); - void SendPlayerReachedRespawnCheckpoint(Entity* entity, const NiPoint3& position, const NiQuaternion& rotation); - - void SendMatchResponse(Entity* entity, const SystemAddress& sysAddr, int response); - void SendMatchUpdate(Entity* entity, const SystemAddress& sysAddr, std::string data, eMatchUpdate type); - - // Rails stuff - void SendSetRailMovement(const LWOOBJID& objectID, bool pathGoForward, std::u16string pathName, uint32_t pathStart, - const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS, - int32_t railActivatorComponentID = -1, LWOOBJID railActivatorObjectID = LWOOBJID_EMPTY); - - void SendStartRailMovement(const LWOOBJID& objectID, std::u16string pathName, std::u16string startSound, - std::u16string loopSound, std::u16string stopSound, const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS, - uint32_t pathStart = 0, bool goForward = true, bool damageImmune = true, bool noAggro = true, - bool notifyActor = false, bool showNameBillboard = true, bool cameraLocked = true, - bool collisionEnabled = true, bool useDB = true, int32_t railComponentID = -1, - LWOOBJID railActivatorObjectID = LWOOBJID_EMPTY); - - void HandleClientRailMovementReady(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr); - void HandleCancelRailMovement(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr); - void HandlePlayerRailArrivedNotification(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr); - - void SendNotifyClientObject(const LWOOBJID& objectID, std::u16string name, int param1 = 0, int param2 = 0, const LWOOBJID& paramObj = LWOOBJID_EMPTY, std::string paramStr = "", const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS); - void SendNotifyClientZoneObject(const LWOOBJID& objectID, const std::u16string& name, int param1, int param2, const LWOOBJID& paramObj, const std::string& paramStr, const SystemAddress& sysAddr); - - void SendNotifyClientFailedPrecondition(LWOOBJID objectId, const SystemAddress& sysAddr, const std::u16string& failedReason, int preconditionID); - - void SendSetName(LWOOBJID objectID, std::u16string name, const SystemAddress& sysAddr); - - void SendLockNodeRotation(Entity* entity, std::string nodeName); - - void SendOrientToAngle(LWOOBJID objectId, bool bRelativeToCurrent, float fAngle, const SystemAddress& sysAddr); - - void SendNotifyObject(LWOOBJID objectId, LWOOBJID objIDSender, std::u16string name, const SystemAddress& sysAddr, int param1 = 0, int param2 = 0); - - void HandleVerifyAck(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr); - - void SendTeamPickupItem(LWOOBJID objectId, LWOOBJID lootID, LWOOBJID lootOwnerID, const SystemAddress& sysAddr); - - //Pets: - - void SendDisplayZoneSummary(LWOOBJID objectId, const SystemAddress& sysAddr, bool isPropertyMap = false, bool isZoneStart = false, LWOOBJID sender = LWOOBJID_EMPTY); - - // Mounts - /** - * @brief Set the Inventory LWOOBJID of the mount - * - * @param entity The entity that is mounting - * @param sysAddr the system address to send game message responses to - * @param objectID LWOOBJID of the item in inventory that is being used - */ - void SendSetMountInventoryID(Entity* entity, const LWOOBJID& objectID, const SystemAddress& sysAddr); - - /** - * @brief Handle client dismounting mount - * - * @param inStream Raknet BitStream of incoming data - * @param entity The Entity that is dismounting - * @param sysAddr the system address to send game message responses to - */ - void HandleDismountComplete(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr); - - /** - * @brief Handle acknowledging that the client possessed something - * - * @param inStream Raknet BitStream of incoming data - * @param entity The Entity that is possessing - * @param sysAddr the system address to send game message responses to - */ - void HandleAcknowledgePossession(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr); - - //Racing: - - // SG: - - void SendSetShootingGalleryParams(LWOOBJID objectId, const SystemAddress& sysAddr, - float cameraFOV, - float cooldown, - float minDistance, - NiPoint3 muzzlePosOffset, - NiPoint3 playerPosOffset, - float projectileVelocity, - float timeLimit, - bool bUseLeaderboards - ); - - void SendNotifyClientShootingGalleryScore(LWOOBJID objectId, const SystemAddress& sysAddr, - float addTime, - int32_t score, - LWOOBJID target, - NiPoint3 targetPos - ); - - void HandleUpdateShootingGalleryRotation(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr); - - void SendUpdateReputation(const LWOOBJID objectId, const int64_t reputation, const SystemAddress& sysAddr); - - // Leaderboards - void SendActivitySummaryLeaderboardData(const LWOOBJID& objectID, const Leaderboard* leaderboard, - const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS); - void HandleActivitySummaryLeaderboardData(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr); - void SendRequestActivitySummaryLeaderboardData(const LWOOBJID& objectID, const LWOOBJID& targetID, - const SystemAddress& sysAddr, const int32_t& gameID = 0, - const int32_t& queryType = 1, const int32_t& resultsEnd = 10, - const int32_t& resultsStart = 0, bool weekly = false); - void HandleRequestActivitySummaryLeaderboardData(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr); - void HandleActivityStateChangeRequest(RakNet::BitStream& inStream, Entity* entity); - - //NT: - - //Handlers: - - void HandleToggleGhostReferenceOverride(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr); - void HandleSetGhostReferencePosition(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr); - - void HandleParseChatMessage(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr); - void HandleToggleGhostReffrenceOverride(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr); - void HandleSetGhostReffrenceOverride(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr); - void HandleFireEventServerSide(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr); - void HandleRequestPlatformResync(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr); - void HandleQuickBuildCancel(RakNet::BitStream& inStream, Entity* entity); - void HandleNotifyServerLevelProcessingComplete(RakNet::BitStream& inStream, Entity* entity); - void HandlePickupCurrency(RakNet::BitStream& inStream, Entity* entity); - void HandlePickupItem(RakNet::BitStream& inStream, Entity* entity); - void HandleModifyPlayerZoneStatistic(RakNet::BitStream& inStream, Entity* entity); - void HandleUpdatePlayerStatistic(RakNet::BitStream& inStream, Entity* entity); - - void HandleMatchRequest(RakNet::BitStream& inStream, Entity* entity); - - void HandleReportBug(RakNet::BitStream& inStream, Entity* entity); - - // bubble - - void HandleZoneSummaryDismissed(RakNet::BitStream& inStream, Entity* entity); - - void SendForceCameraTargetCycle(Entity* entity, bool bForceCycling, eCameraTargetCyclingMode cyclingMode, LWOOBJID optionalTargetID); - - struct DisplayTooltip : public NetGameMsg { - DisplayTooltip() : NetGameMsg(MessageType::Game::DISPLAY_TOOLTIP) {} - bool doOrDie{}; - bool noRepeat{}; - bool noRevive{}; - bool isPropertyTooltip{}; - bool show{}; - bool translate{}; - int32_t time{}; - std::u16string id{}; - LwoNameValue localizeParams{}; - std::u16string imageName{}; - std::u16string text{}; - void Serialize(RakNet::BitStream& bitStream) const override; - }; - - struct UseItemOnClient : public NetGameMsg { - UseItemOnClient() : NetGameMsg(MessageType::Game::USE_ITEM_ON_CLIENT) {} - LWOOBJID playerId{}; - LWOOBJID itemToUse{}; - uint32_t itemType{}; - LOT itemLOT{}; - NiPoint3 targetPosition{}; - void Serialize(RakNet::BitStream& bitStream) const override; - }; + // Server-internal events (GameMsg). Wire messages live in the per-domain Messages.h files. struct ZoneLoadedInfo : public GameMsg { ZoneLoadedInfo() : GameMsg(MessageType::Game::ZONE_LOADED_INFO) {} @@ -337,19 +136,7 @@ namespace GameMessages { LwoNameValue racingSettings{}; }; - struct SetModelToBuild : public NetGameMsg { - SetModelToBuild() : NetGameMsg(MessageType::Game::SET_MODEL_TO_BUILD) {} - void Serialize(RakNet::BitStream& bitStream) const override; - LOT modelLot{ -1 }; - }; - struct SpawnModelBricks : public NetGameMsg { - SpawnModelBricks() : NetGameMsg(MessageType::Game::SPAWN_MODEL_BRICKS) {} - void Serialize(RakNet::BitStream& bitStream) const override; - - float amount{ 0.0f }; - NiPoint3 position{ NiPoint3Constant::ZERO }; - }; struct ActivityNotify : public GameMsg { ActivityNotify() : GameMsg(MessageType::Game::ACTIVITY_NOTIFY) {} @@ -357,14 +144,6 @@ namespace GameMessages { LwoNameValue notification{}; }; - struct ShootingGalleryFire : public NetGameMsg { - ShootingGalleryFire() : NetGameMsg(MessageType::Game::SHOOTING_GALLERY_FIRE) {} - bool Deserialize(RakNet::BitStream& bitStream) override; - void Handle(Entity& entity, const SystemAddress& sysAddr) override; - - NiPoint3 target{}; - NiQuaternion rotation = QuatUtils::IDENTITY; - }; struct ChildLoaded : public GameMsg { ChildLoaded() : GameMsg(MessageType::Game::CHILD_LOADED) {} @@ -377,16 +156,6 @@ namespace GameMessages { PlayerResurrectionFinished() : GameMsg(MessageType::Game::PLAYER_RESURRECTION_FINISHED) {} }; - struct RequestServerObjectInfo : public NetGameMsg { - bool bVerbose{}; - LWOOBJID clientId{}; - LWOOBJID targetForReport{}; - - RequestServerObjectInfo() : NetGameMsg(MessageType::Game::REQUEST_SERVER_OBJECT_INFO, eGameMasterLevel::DEVELOPER) {} - bool Deserialize(RakNet::BitStream& bitStream) override; - void Handle(Entity& entity, const SystemAddress& sysAddr) override; - }; - using RequestServerObjectInfoEvent = NetGameMsgEvent; struct GetObjectReportInfo : public GameMsg { AMFArrayValue* info{}; @@ -397,34 +166,7 @@ namespace GameMessages { GetObjectReportInfo() : GameMsg(MessageType::Game::GET_OBJECT_REPORT_INFO) {} }; - struct RequestUse : public NetGameMsg { - RequestUse() : NetGameMsg(MessageType::Game::REQUEST_USE) {} - bool Deserialize(RakNet::BitStream& stream) override; - void Handle(Entity& entity, const SystemAddress& sysAddr) override; - - LWOOBJID object{}; - - bool secondary{ false }; - - // Set to true if this coming from a multi-interaction UI on the client. - bool bIsMultiInteractUse{}; - - // Used only for multi-interaction - unsigned int multiInteractID{}; - - // Used only for multi-interaction, is of the enum type InteractionType - int multiInteractType{}; - }; - using RequestUseEvent = NetGameMsgEvent; - - struct PlayBehaviorSound : public NetGameMsg { - PlayBehaviorSound() : NetGameMsg(MessageType::Game::PLAY_BEHAVIOR_SOUND) {} - - void Serialize(RakNet::BitStream& stream) const override; - - int32_t soundID{ -1 }; - }; struct ResetModelToDefaults : public GameMsg { ResetModelToDefaults() : GameMsg(MessageType::Game::RESET_MODEL_TO_DEFAULTS) {} @@ -435,14 +177,6 @@ namespace GameMessages { bool bResetBehaviors{ true }; }; - struct EmotePlayed : public NetGameMsg { - EmotePlayed() : NetGameMsg(MessageType::Game::EMOTE_PLAYED), emoteID(0), targetID(0) {} - - void Serialize(RakNet::BitStream& stream) const override; - - int32_t emoteID; - LWOOBJID targetID; - }; struct GetPosition : public GameMsg { GetPosition() : GameMsg(MessageType::Game::GET_POSITION) {} @@ -458,21 +192,6 @@ namespace GameMessages { bool bIgnoreChecks{ false }; }; - struct DropClientLoot : public NetGameMsg { - DropClientLoot() : NetGameMsg(MessageType::Game::DROP_CLIENT_LOOT) {} - - void Serialize(RakNet::BitStream& stream) const override; - LWOOBJID sourceID{ LWOOBJID_EMPTY }; - LOT item{ LOT_NULL }; - int32_t currency{}; - NiPoint3 spawnPos{}; - NiPoint3 finalPosition{}; - int32_t count{}; - bool bUsePosition{}; - LWOOBJID lootID{ LWOOBJID_EMPTY }; - LWOOBJID ownerID{ LWOOBJID_EMPTY }; - }; - using DropClientLootEvent = NetGameMsgEvent; struct GetMissionState : public GameMsg { GetMissionState() : GameMsg(MessageType::Game::GET_MISSION_STATE) {} @@ -502,23 +221,7 @@ namespace GameMessages { LOT item{}; }; - struct PickupItem : public NetGameMsg { - PickupItem() : NetGameMsg(MessageType::Game::PICKUP_ITEM) {} - void Handle(Entity& entity, const SystemAddress& sysAddr) override; - bool Deserialize(RakNet::BitStream& stream) override; - LWOOBJID lootID{}; - LWOOBJID lootOwnerID{}; - }; - using PickupItemEvent = NetGameMsgEvent; - - struct TeamPickupItem : public NetGameMsg { - TeamPickupItem() : NetGameMsg(MessageType::Game::TEAM_PICKUP_ITEM) {} - - void Serialize(RakNet::BitStream& stream) const override; - LWOOBJID lootID{}; - LWOOBJID lootOwnerID{}; - }; struct IsDead : public GameMsg { IsDead() : GameMsg(MessageType::Game::IS_DEAD) {} @@ -526,14 +229,6 @@ namespace GameMessages { bool bDead{}; }; - struct ToggleGMInvis : public NetGameMsg { - ToggleGMInvis() : NetGameMsg(MessageType::Game::TOGGLE_GM_INVIS) {} - - void Serialize(RakNet::BitStream& stream) const override; - bool bStateOut{ false }; - - }; - using ToggleGMInvisEvent = NetGameMsgEvent; struct GetGMInvis : public GameMsg { GetGMInvis() : GameMsg(MessageType::Game::GET_GM_INVIS) {} @@ -547,14 +242,6 @@ namespace GameMessages { LWOOBJID childID{}; }; - struct UseSkillSet : public NetGameMsg { - UseSkillSet() : NetGameMsg(MessageType::Game::USE_SKILL_SET) {} - void Serialize(RakNet::BitStream& bitStream) const override; - - bool bRemove{}; - LWOOBJID possessedId{ LWOOBJID_EMPTY }; - int32_t setId{ -1 }; - }; struct ObjectLoaded : public GameMsg { ObjectLoaded() : GameMsg(MessageType::Game::OBJECT_LOADED) {} diff --git a/dGame/dGameMessages/InventoryMessages.cpp b/dGame/dGameMessages/InventoryMessages.cpp index 91e3636fc..97289bd8f 100644 --- a/dGame/dGameMessages/InventoryMessages.cpp +++ b/dGame/dGameMessages/InventoryMessages.cpp @@ -1,6 +1,7 @@ #include "InventoryMessages.h" #include "BitStreamUtils.h" +#include "TeamManager.h" #include "EntityManager.h" #include "Game.h" #include "Entity.h" @@ -455,4 +456,94 @@ namespace GameMessages { auto* inventoryComponent = entity.GetComponent(); if (inventoryComponent) inventoryComponent->OnUpdateInventoryGroupContents(*this); } + + void UseItemOnClient::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(itemLOT); + bitStream.Write(itemToUse); + bitStream.Write(itemType); + bitStream.Write(playerId); + bitStream.Write(targetPosition.x); + bitStream.Write(targetPosition.y); + bitStream.Write(targetPosition.z); + } + + void DropClientLoot::Serialize(RakNet::BitStream& stream) const { + stream.Write(bUsePosition); + + stream.Write(finalPosition != NiPoint3Constant::ZERO); + if (finalPosition != NiPoint3Constant::ZERO) stream.Write(finalPosition); + + stream.Write(currency); + stream.Write(item); + stream.Write(lootID); + stream.Write(ownerID); + stream.Write(sourceID); + + stream.Write(spawnPos != NiPoint3Constant::ZERO); + if (spawnPos != NiPoint3Constant::ZERO) stream.Write(spawnPos); + } + + bool PickupItem::Deserialize(RakNet::BitStream& stream) { + if (!stream.Read(lootID)) return false; + if (!stream.Read(lootOwnerID)) return false; + return true; + } + + void PickupItem::Handle(Entity& entity, const SystemAddress& sysAddr) { + auto* team = TeamManager::Instance()->GetTeam(entity.GetObjectID()); + LOG("Has team %i picking up %llu:%llu", team != nullptr, lootID, lootOwnerID); + if (team) { + for (const auto memberId : team->members) { + PickupItemEvent event(*this); + event.Send(memberId); + TeamPickupItem teamPickupMsg{}; + teamPickupMsg.target = lootID; + teamPickupMsg.lootID = lootID; + teamPickupMsg.lootOwnerID = lootOwnerID; + const auto* const memberEntity = Game::entityManager->GetEntity(memberId); + if (memberEntity) teamPickupMsg.Send(memberEntity->GetSystemAddress()); + } + } else { + entity.PickupItem(lootID); + } + } + + void TeamPickupItem::Serialize(RakNet::BitStream& stream) const { + stream.Write(lootID); + stream.Write(lootOwnerID); + } + + bool UseItemOnClient::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(itemLOT)); + VALIDATE_READ(bitStream.Read(itemToUse)); + VALIDATE_READ(bitStream.Read(itemType)); + VALIDATE_READ(bitStream.Read(playerId)); + VALIDATE_READ(bitStream.Read(targetPosition.x)); + VALIDATE_READ(bitStream.Read(targetPosition.y)); + VALIDATE_READ(bitStream.Read(targetPosition.z)); + return true; + } + + bool DropClientLoot::Deserialize(RakNet::BitStream& stream) { + VALIDATE_READ(stream.Read(bUsePosition)); + VALIDATE_READ(BitStreamUtils::ReadOptional(stream, finalPosition, NiPoint3Constant::ZERO)); + VALIDATE_READ(stream.Read(currency)); + VALIDATE_READ(stream.Read(item)); + VALIDATE_READ(stream.Read(lootID)); + VALIDATE_READ(stream.Read(ownerID)); + VALIDATE_READ(stream.Read(sourceID)); + VALIDATE_READ(BitStreamUtils::ReadOptional(stream, spawnPos, NiPoint3Constant::ZERO)); + return true; + } + + void PickupItem::Serialize(RakNet::BitStream& stream) const { + stream.Write(lootID); + stream.Write(lootOwnerID); + } + + bool TeamPickupItem::Deserialize(RakNet::BitStream& stream) { + VALIDATE_READ(stream.Read(lootID)); + VALIDATE_READ(stream.Read(lootOwnerID)); + return true; + } } diff --git a/dGame/dGameMessages/InventoryMessages.h b/dGame/dGameMessages/InventoryMessages.h index 366948a12..41c56f68e 100644 --- a/dGame/dGameMessages/InventoryMessages.h +++ b/dGame/dGameMessages/InventoryMessages.h @@ -309,6 +309,54 @@ namespace GameMessages { eInventoryType inventoryType{}; LOT lot{}; }; + + struct UseItemOnClient : public NetGameMsg { + UseItemOnClient() : NetGameMsg(MessageType::Game::USE_ITEM_ON_CLIENT) {} + LWOOBJID playerId{}; + LWOOBJID itemToUse{}; + uint32_t itemType{}; + LOT itemLOT{}; + NiPoint3 targetPosition{}; + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + }; + + struct DropClientLoot : public NetGameMsg { + DropClientLoot() : NetGameMsg(MessageType::Game::DROP_CLIENT_LOOT) {} + + void Serialize(RakNet::BitStream& stream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + LWOOBJID sourceID{ LWOOBJID_EMPTY }; + LOT item{ LOT_NULL }; + int32_t currency{}; + NiPoint3 spawnPos{}; + NiPoint3 finalPosition{}; + int32_t count{}; + bool bUsePosition{}; + LWOOBJID lootID{ LWOOBJID_EMPTY }; + LWOOBJID ownerID{ LWOOBJID_EMPTY }; + }; + using DropClientLootEvent = NetGameMsgEvent; + + struct PickupItem : public NetGameMsg { + PickupItem() : NetGameMsg(MessageType::Game::PICKUP_ITEM) {} + + void Serialize(RakNet::BitStream& bitStream) const override; + void Handle(Entity& entity, const SystemAddress& sysAddr) override; + bool Deserialize(RakNet::BitStream& stream) override; + LWOOBJID lootID{}; + LWOOBJID lootOwnerID{}; + }; + using PickupItemEvent = NetGameMsgEvent; + + struct TeamPickupItem : public NetGameMsg { + TeamPickupItem() : NetGameMsg(MessageType::Game::TEAM_PICKUP_ITEM) {} + + void Serialize(RakNet::BitStream& stream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + LWOOBJID lootID{}; + LWOOBJID lootOwnerID{}; + }; }; #endif // INVENTORYMESSAGES_H diff --git a/dGame/dGameMessages/MissionMessages.cpp b/dGame/dGameMessages/MissionMessages.cpp index f4cf041b0..76e5d04aa 100644 --- a/dGame/dGameMessages/MissionMessages.cpp +++ b/dGame/dGameMessages/MissionMessages.cpp @@ -123,6 +123,21 @@ namespace GameMessages { return true; } + void MissionDialogueCancelled::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(bIsComplete); + bitStream.Write(iMissionState); + bitStream.Write(missionID); + bitStream.Write(responder); + } + + bool MissionDialogueCancelled::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(bIsComplete)); + VALIDATE_READ(bitStream.Read(iMissionState)); + VALIDATE_READ(bitStream.Read(missionID)); + VALIDATE_READ(bitStream.Read(responder)); + return true; + } + void MissionDialogueOK::Handle(Entity& entityRef, const SystemAddress& sysAddr) { Entity* entity = &entityRef; Entity* player = Game::entityManager->GetEntity(responder); diff --git a/dGame/dGameMessages/MissionMessages.h b/dGame/dGameMessages/MissionMessages.h index 1613bccfa..85b35c94d 100644 --- a/dGame/dGameMessages/MissionMessages.h +++ b/dGame/dGameMessages/MissionMessages.h @@ -79,6 +79,18 @@ namespace GameMessages { LWOOBJID responder{}; }; + // Client -> server. The player closed a mission offer without accepting. Nothing to do: the client carries on. + struct MissionDialogueCancelled : public NetGameMsg { + MissionDialogueCancelled() : NetGameMsg(MessageType::Game::MISSION_DIALOGUE_CANCELLED) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + bool bIsComplete{}; + eMissionState iMissionState{}; + int32_t missionID{}; + LWOOBJID responder{}; + }; + // Client -> server. struct RequestLinkedMission : public NetGameMsg { RequestLinkedMission() : NetGameMsg(MessageType::Game::REQUEST_LINKED_MISSION) {} diff --git a/dGame/dGameMessages/MovementMessages.cpp b/dGame/dGameMessages/MovementMessages.cpp new file mode 100644 index 000000000..d50153321 --- /dev/null +++ b/dGame/dGameMessages/MovementMessages.cpp @@ -0,0 +1,414 @@ +#include "MovementMessages.h" + +#include "BitStreamUtils.h" +#include "CppScripts.h" +#include "Entity.h" +#include "EntityManager.h" +#include "Game.h" +#include "GhostComponent.h" +#include "PlayerManager.h" +#include "PossessorComponent.h" +#include "RailActivatorComponent.h" +#include "eReplicaComponentType.h" + +namespace GameMessages { + namespace { + void WritePoint(RakNet::BitStream& bitStream, const NiPoint3& point) { + bitStream.Write(point.x); + bitStream.Write(point.y); + bitStream.Write(point.z); + } + + bool ReadPoint(RakNet::BitStream& bitStream, NiPoint3& point) { + return bitStream.Read(point.x) && bitStream.Read(point.y) && bitStream.Read(point.z); + } + } + + void Teleport::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(bIgnoreY); + bitStream.Write(bSetRotation); + bitStream.Write(bSkipAllChecks); + WritePoint(bitStream, pos); + bitStream.Write(bUseNavmesh); + BitStreamUtils::WriteOptional(bitStream, rot.w, 1.0f); + bitStream.Write(rot.x); + bitStream.Write(rot.y); + bitStream.Write(rot.z); + } + + bool Teleport::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(bIgnoreY)); + VALIDATE_READ(bitStream.Read(bSetRotation)); + VALIDATE_READ(bitStream.Read(bSkipAllChecks)); + VALIDATE_READ(ReadPoint(bitStream, pos)); + VALIDATE_READ(bitStream.Read(bUseNavmesh)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, rot.w, 1.0f)); + VALIDATE_READ(bitStream.Read(rot.x)); + VALIDATE_READ(bitStream.Read(rot.y)); + VALIDATE_READ(bitStream.Read(rot.z)); + return true; + } + + PlatformResync::PlatformResync(const Entity& entity, bool bStopAtDesiredWaypoint, int32_t iIndex, int32_t iDesiredWaypointIndex, + int32_t nextIndex, eMovementPlatformState movementState, bool special) : PlatformResync() { + const auto lot = entity.GetLOT(); + + if (lot == 12341 || lot == 5027 || lot == 5028 || lot == 14335 || lot == 14447 || lot == 14449 || lot == 11306 || lot == 11308 || lot == 9483) { + iDesiredWaypointIndex = (lot == 11306 || lot == 11308) ? 1 : 0; + iIndex = lot == 9483 ? 1 : 0; + nextIndex = lot == 9483 && !special ? 1 : 0; + bStopAtDesiredWaypoint = true; + movementState = lot == 9483 && !special ? eMovementPlatformState::Stopped : eMovementPlatformState::Stationary; + } + + target = entity.GetObjectID(); + this->bStopAtDesiredWaypoint = bStopAtDesiredWaypoint; + this->iIndex = iIndex; + this->iDesiredWaypointIndex = iDesiredWaypointIndex; + this->iNextIndex = nextIndex; + this->eState = movementState; + } + + void PlatformResync::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(bReverse); + bitStream.Write(bStopAtDesiredWaypoint); + bitStream.Write(eCommand); + bitStream.Write(static_cast(eState)); + bitStream.Write(eUnexpectedCommand); + bitStream.Write(fIdleTimeElapsed); + bitStream.Write(fMoveTimeElapsed); + bitStream.Write(fPercentBetweenPoints); + bitStream.Write(iDesiredWaypointIndex); + bitStream.Write(iIndex); + bitStream.Write(iNextIndex); + WritePoint(bitStream, ptUnexpectedLocation); + const bool hasRotation = qUnexpectedRotation != QuatUtils::IDENTITY; + bitStream.Write(hasRotation); + if (hasRotation) { + bitStream.Write(qUnexpectedRotation.x); + bitStream.Write(qUnexpectedRotation.y); + bitStream.Write(qUnexpectedRotation.z); + bitStream.Write(qUnexpectedRotation.w); + } + } + + bool PlatformResync::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(bReverse)); + VALIDATE_READ(bitStream.Read(bStopAtDesiredWaypoint)); + VALIDATE_READ(bitStream.Read(eCommand)); + int32_t state{}; + VALIDATE_READ(bitStream.Read(state)); + eState = static_cast(state); + VALIDATE_READ(bitStream.Read(eUnexpectedCommand)); + VALIDATE_READ(bitStream.Read(fIdleTimeElapsed)); + VALIDATE_READ(bitStream.Read(fMoveTimeElapsed)); + VALIDATE_READ(bitStream.Read(fPercentBetweenPoints)); + VALIDATE_READ(bitStream.Read(iDesiredWaypointIndex)); + VALIDATE_READ(bitStream.Read(iIndex)); + VALIDATE_READ(bitStream.Read(iNextIndex)); + VALIDATE_READ(ReadPoint(bitStream, ptUnexpectedLocation)); + bool hasRotation{}; + VALIDATE_READ(bitStream.Read(hasRotation)); + qUnexpectedRotation = QuatUtils::IDENTITY; + if (hasRotation) { + VALIDATE_READ(bitStream.Read(qUnexpectedRotation.x)); + VALIDATE_READ(bitStream.Read(qUnexpectedRotation.y)); + VALIDATE_READ(bitStream.Read(qUnexpectedRotation.z)); + VALIDATE_READ(bitStream.Read(qUnexpectedRotation.w)); + } + return true; + } + + void RequestPlatformResync::Handle(Entity& entity, const SystemAddress& sysAddr) { + if (entity.GetLOT() == 6267 || entity.GetLOT() == 16141) return; + PlatformResync(entity).Send(sysAddr); + } + + void OrientToAngle::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(bRelativeToCurrent); + bitStream.Write(fAngle); + } + + bool OrientToAngle::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(bRelativeToCurrent)); + VALIDATE_READ(bitStream.Read(fAngle)); + return true; + } + + void LockNodeRotation::Serialize(RakNet::BitStream& bitStream) const { + BitStreamUtils::WriteLengthPrefixed(bitStream, nodeName); + } + + bool LockNodeRotation::Deserialize(RakNet::BitStream& bitStream) { + return BitStreamUtils::ReadLengthPrefixed(bitStream, nodeName); + } + + void SetGravityScale::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(scale); + } + + bool SetGravityScale::Deserialize(RakNet::BitStream& bitStream) { + return bitStream.Read(scale); + } + + void SetJetPackMode::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(bBypassChecks); + bitStream.Write(bDoHover); + bitStream.Write(bUse); + BitStreamUtils::WriteOptional(bitStream, effectID, -1); + BitStreamUtils::WriteOptional(bitStream, fAirspeed, 10.0f); + BitStreamUtils::WriteOptional(bitStream, fMaxAirspeed, 15.0f); + BitStreamUtils::WriteOptional(bitStream, fVertVel, 1.0f); + BitStreamUtils::WriteOptional(bitStream, iWarningEffectID, -1); + } + + bool SetJetPackMode::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(bBypassChecks)); + VALIDATE_READ(bitStream.Read(bDoHover)); + VALIDATE_READ(bitStream.Read(bUse)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, effectID, -1)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, fAirspeed, 10.0f)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, fMaxAirspeed, 15.0f)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, fVertVel, 1.0f)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, iWarningEffectID, -1)); + return true; + } + + void SetPlayerControlScheme::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(bDelayCamSwitchIfInCinematic); + bitStream.Write(bSwitchCam); + BitStreamUtils::WriteOptional(bitStream, iScheme, eControlScheme::SCHEME_A); + } + + bool SetPlayerControlScheme::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(bDelayCamSwitchIfInCinematic)); + VALIDATE_READ(bitStream.Read(bSwitchCam)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, iScheme, eControlScheme::SCHEME_A)); + return true; + } + + void PlayerReachedRespawnCheckpoint::Serialize(RakNet::BitStream& bitStream) const { + WritePoint(bitStream, pos); + const bool hasRotation = rot != QuatUtils::IDENTITY; + bitStream.Write(hasRotation); + if (hasRotation) { + bitStream.Write(rot.w); + bitStream.Write(rot.x); + bitStream.Write(rot.y); + bitStream.Write(rot.z); + } + } + + bool PlayerReachedRespawnCheckpoint::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(ReadPoint(bitStream, pos)); + bool hasRotation{}; + VALIDATE_READ(bitStream.Read(hasRotation)); + rot = QuatUtils::IDENTITY; + if (hasRotation) { + VALIDATE_READ(bitStream.Read(rot.w)); + VALIDATE_READ(bitStream.Read(rot.x)); + VALIDATE_READ(bitStream.Read(rot.y)); + VALIDATE_READ(bitStream.Read(rot.z)); + } + return true; + } + + void SetRailMovement::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(pathGoForward); + BitStreamUtils::WriteLengthPrefixed(bitStream, pathName); + bitStream.Write(pathStart); + BitStreamUtils::WriteOptional(bitStream, railActivatorComponentID, -1); + BitStreamUtils::WriteOptional(bitStream, railActivatorObjectID, LWOOBJID_EMPTY); + } + + bool SetRailMovement::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(pathGoForward)); + VALIDATE_READ(BitStreamUtils::ReadLengthPrefixed(bitStream, pathName)); + VALIDATE_READ(bitStream.Read(pathStart)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, railActivatorComponentID, -1)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, railActivatorObjectID, LWOOBJID_EMPTY)); + return true; + } + + void StartRailMovement::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(bDamageImmune); + bitStream.Write(bNoAggro); + bitStream.Write(bNotifyActor); + bitStream.Write(bShowNameBillboard); + bitStream.Write(bCameraLocked); + bitStream.Write(bCollisionEnabled); + BitStreamUtils::WriteLengthPrefixed(bitStream, loopSound); + bitStream.Write(goForward); + BitStreamUtils::WriteLengthPrefixed(bitStream, pathName); + BitStreamUtils::WriteOptional(bitStream, pathStart, 0); + BitStreamUtils::WriteOptional(bitStream, railActivatorComponentID, -1); + BitStreamUtils::WriteOptional(bitStream, railActivatorObjectID, LWOOBJID_EMPTY); + BitStreamUtils::WriteLengthPrefixed(bitStream, startSound); + BitStreamUtils::WriteLengthPrefixed(bitStream, stopSound); + bitStream.Write(bUseDB); + } + + bool StartRailMovement::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(bDamageImmune)); + VALIDATE_READ(bitStream.Read(bNoAggro)); + VALIDATE_READ(bitStream.Read(bNotifyActor)); + VALIDATE_READ(bitStream.Read(bShowNameBillboard)); + VALIDATE_READ(bitStream.Read(bCameraLocked)); + VALIDATE_READ(bitStream.Read(bCollisionEnabled)); + VALIDATE_READ(BitStreamUtils::ReadLengthPrefixed(bitStream, loopSound)); + VALIDATE_READ(bitStream.Read(goForward)); + VALIDATE_READ(BitStreamUtils::ReadLengthPrefixed(bitStream, pathName)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, pathStart, 0)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, railActivatorComponentID, -1)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, railActivatorObjectID, LWOOBJID_EMPTY)); + VALIDATE_READ(BitStreamUtils::ReadLengthPrefixed(bitStream, startSound)); + VALIDATE_READ(BitStreamUtils::ReadLengthPrefixed(bitStream, stopSound)); + VALIDATE_READ(bitStream.Read(bUseDB)); + return true; + } + + void ClientRailMovementReady::Handle(Entity& entity, const SystemAddress& sysAddr) { + const auto possibleRails = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::RAIL_ACTIVATOR); + for (const auto* possibleRail : possibleRails) { + const auto* rail = possibleRail->GetComponent(); + if (rail != nullptr) { + rail->OnRailMovementReady(&entity); + } + } + } + + void CancelRailMovement::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(bImmediate); + } + + bool CancelRailMovement::Deserialize(RakNet::BitStream& bitStream) { + return bitStream.Read(bImmediate); + } + + void CancelRailMovement::Handle(Entity& entity, const SystemAddress& sysAddr) { + const auto possibleRails = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::RAIL_ACTIVATOR); + for (const auto* possibleRail : possibleRails) { + auto* rail = possibleRail->GetComponent(); + if (rail != nullptr) { + rail->OnCancelRailMovement(&entity); + } + } + } + + void PlayerRailArrivedNotification::Serialize(RakNet::BitStream& bitStream) const { + BitStreamUtils::WriteLengthPrefixed(bitStream, pathName); + bitStream.Write(waypointNumber); + } + + bool PlayerRailArrivedNotification::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(BitStreamUtils::ReadLengthPrefixed(bitStream, pathName)); + VALIDATE_READ(bitStream.Read(waypointNumber)); + return true; + } + + void PlayerRailArrivedNotification::Handle(Entity& entity, const SystemAddress& sysAddr) { + const auto possibleRails = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::RAIL_ACTIVATOR); + for (auto* possibleRail : possibleRails) { + if (possibleRail) possibleRail->GetScript()->OnPlayerRailArrived(possibleRail, &entity, pathName, waypointNumber); + } + } + + void SetMountInventoryID::Serialize(RakNet::BitStream& bitStream) const { + BitStreamUtils::WriteOptional(bitStream, inventoryMountID, LWOOBJID_EMPTY); + } + + bool SetMountInventoryID::Deserialize(RakNet::BitStream& bitStream) { + return BitStreamUtils::ReadOptional(bitStream, inventoryMountID, LWOOBJID_EMPTY); + } + + void DismountComplete::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(mountID); + } + + bool DismountComplete::Deserialize(RakNet::BitStream& bitStream) { + return bitStream.Read(mountID); + } + + void DismountComplete::Handle(Entity& entity, const SystemAddress& sysAddr) { + // If we aren't possessing something, don't do anything + if (mountID == LWOOBJID_EMPTY) return; + auto* possessorComponent = entity.GetComponent(); + if (possessorComponent) possessorComponent->OnDismountComplete(mountID); + } + + void AcknowledgePossession::Serialize(RakNet::BitStream& bitStream) const { + BitStreamUtils::WriteOptional(bitStream, possessedObjID, LWOOBJID_EMPTY); + } + + bool AcknowledgePossession::Deserialize(RakNet::BitStream& bitStream) { + return BitStreamUtils::ReadOptional(bitStream, possessedObjID, LWOOBJID_EMPTY); + } + + void AcknowledgePossession::Handle(Entity& entity, const SystemAddress& sysAddr) { + Game::entityManager->SerializeEntity(&entity); + if (possessedObjID != LWOOBJID_EMPTY) { + auto* mount = Game::entityManager->GetEntity(possessedObjID); + if (mount) Game::entityManager->SerializeEntity(mount); + } + } + + void ToggleGhostReferenceOverride::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(bOverride); + } + + bool ToggleGhostReferenceOverride::Deserialize(RakNet::BitStream& bitStream) { + return bitStream.Read(bOverride); + } + + void ToggleGhostReferenceOverride::Handle(Entity& entity, const SystemAddress& sysAddr) { + auto* player = PlayerManager::GetPlayer(sysAddr); + if (player == nullptr) return; + + auto* ghostComponent = entity.GetComponent(); + if (ghostComponent) ghostComponent->SetGhostOverride(bOverride); + + Game::entityManager->UpdateGhosting(player); + } + + void SetGhostReferencePosition::Serialize(RakNet::BitStream& bitStream) const { + WritePoint(bitStream, pos); + } + + bool SetGhostReferencePosition::Deserialize(RakNet::BitStream& bitStream) { + return ReadPoint(bitStream, pos); + } + + void SetGhostReferencePosition::Handle(Entity& entity, const SystemAddress& sysAddr) { + auto* player = PlayerManager::GetPlayer(sysAddr); + if (player == nullptr) return; + + auto* ghostComponent = entity.GetComponent(); + if (ghostComponent) ghostComponent->SetGhostOverridePoint(pos); + + Game::entityManager->UpdateGhosting(player); + } + + void PlayerSetCameraCyclingMode::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(bAllowCyclingWhileDeadOnly); + BitStreamUtils::WriteOptional(bitStream, cyclingMode, eCyclingMode::ALLOW_CYCLE_TEAMMATES); + } + + bool PlayerSetCameraCyclingMode::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(bAllowCyclingWhileDeadOnly)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, cyclingMode, eCyclingMode::ALLOW_CYCLE_TEAMMATES)); + return true; + } + + void ForceCameraTargetCycle::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(bForceCycling); + BitStreamUtils::WriteOptional(bitStream, cyclingMode, eCameraTargetCyclingMode::ALLOW_CYCLE_TEAMMATES); + bitStream.Write(optionalTargetID); + } + + bool ForceCameraTargetCycle::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(bForceCycling)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, cyclingMode, eCameraTargetCyclingMode::ALLOW_CYCLE_TEAMMATES)); + VALIDATE_READ(bitStream.Read(optionalTargetID)); + return true; + } +} diff --git a/dGame/dGameMessages/MovementMessages.h b/dGame/dGameMessages/MovementMessages.h new file mode 100644 index 000000000..efcc51b39 --- /dev/null +++ b/dGame/dGameMessages/MovementMessages.h @@ -0,0 +1,282 @@ +#ifndef MOVEMENTMESSAGES_H +#define MOVEMENTMESSAGES_H + +#include "GameMessages.h" +#include "NiQuaternion.h" +#include "eControlScheme.h" +#include "eCyclingMode.h" +#include "eMovementPlatformState.h" + +#include + +enum class eCameraTargetCyclingMode : int32_t { + ALLOW_CYCLE_TEAMMATES, + DISALLOW_CYCLING +}; + +// Game messages for moving things around: teleports, moving platforms, rails, mounts and possession, jetpacks, +// ghosting reference points, the camera and the control scheme. +// Fields are listed in wire order; names follow the client (legouniverse.exe 1.10.64) where known. +namespace GameMessages { + // Server -> client, to one client (UNASSIGNED broadcasts). + struct Teleport : public NetGameMsg { + Teleport() : NetGameMsg(MessageType::Game::TELEPORT) {} + // bIgnoreY is set when pos.y is 0, as DLU always did. + Teleport(const LWOOBJID _target, const NiPoint3& _pos, const NiQuaternion& _rot, const bool _bSetRotation = false) : Teleport() { + target = _target; + bIgnoreY = _pos.y == 0.0f; + bSetRotation = _bSetRotation; + pos = _pos; + rot = _rot; + } + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + bool bIgnoreY{ true }; + bool bSetRotation{ false }; + bool bSkipAllChecks{ false }; + NiPoint3 pos{}; + bool bUseNavmesh{ false }; + // w is optional (default 1.0f), then x, y, z always. + NiQuaternion rot{ QuatUtils::IDENTITY }; + }; + + // Server -> client, broadcast. No payload. + struct StartPathing : public NetGameMsg { + StartPathing() : NetGameMsg(MessageType::Game::START_PATHING) {} + }; + + // Server -> client, to one client (UNASSIGNED broadcasts). + struct PlatformResync : public NetGameMsg { + PlatformResync() : NetGameMsg(MessageType::Game::PLATFORM_RESYNC) {} + // What DLU sends for a platform entity: the arguments, overridden for a few LOTs whose paths only have a + // start and an end (special is for the FV tree platform). + PlatformResync(const Entity& entity, bool bStopAtDesiredWaypoint = false, + int32_t iIndex = 0, int32_t iDesiredWaypointIndex = 1, int32_t nextIndex = 1, + eMovementPlatformState movementState = eMovementPlatformState::Moving, bool special = false); + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + bool bReverse{ false }; + bool bStopAtDesiredWaypoint{ false }; + int32_t eCommand{ 0 }; + eMovementPlatformState eState{ eMovementPlatformState::Moving }; // written as int32_t + int32_t eUnexpectedCommand{ 0 }; + float fIdleTimeElapsed{ 0.0f }; + float fMoveTimeElapsed{ 0.0f }; + float fPercentBetweenPoints{ 0.0f }; + int32_t iDesiredWaypointIndex{ 1 }; + int32_t iIndex{ 0 }; + int32_t iNextIndex{ 1 }; + NiPoint3 ptUnexpectedLocation{ NiPoint3Constant::ZERO }; + // optional (default identity), written x, y, z, w + NiQuaternion qUnexpectedRotation{ QuatUtils::IDENTITY }; + }; + + // Client -> server. No payload. Answered with a PlatformResync. + struct RequestPlatformResync : public NetGameMsg { + RequestPlatformResync() : NetGameMsg(MessageType::Game::REQUEST_PLATFORM_RESYNC) {} + void Handle(Entity& entity, const SystemAddress& sysAddr) override; + }; + + // Server -> client, to one client (UNASSIGNED broadcasts). + struct OrientToAngle : public NetGameMsg { + OrientToAngle() : NetGameMsg(MessageType::Game::ORIENT_TO_ANGLE) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + bool bRelativeToCurrent{}; + float fAngle{}; + }; + + // Server -> client, broadcast. + struct LockNodeRotation : public NetGameMsg { + LockNodeRotation() : NetGameMsg(MessageType::Game::LOCK_NODE_ROTATION) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + std::string nodeName{}; + }; + + // Server -> client, to one client. + struct SetGravityScale : public NetGameMsg { + SetGravityScale() : NetGameMsg(MessageType::Game::SET_GRAVITY_SCALE) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + float scale{}; + }; + + // Server -> client, broadcast. + struct SetJetPackMode : public NetGameMsg { + SetJetPackMode() : NetGameMsg(MessageType::Game::SET_JET_PACK_MODE) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + bool bBypassChecks{ false }; + bool bDoHover{ false }; + bool bUse{}; + int32_t effectID{ -1 }; // optional + float fAirspeed{ 10.0f }; // optional + float fMaxAirspeed{ 15.0f }; // optional + float fVertVel{ 1.0f }; // optional + int32_t iWarningEffectID{ -1 }; // optional + }; + + // Server -> client, to one client. + struct SetPlayerControlScheme : public NetGameMsg { + SetPlayerControlScheme() : NetGameMsg(MessageType::Game::SET_PLAYER_CONTROL_SCHEME) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + bool bDelayCamSwitchIfInCinematic{ true }; + bool bSwitchCam{ true }; + eControlScheme iScheme{ eControlScheme::SCHEME_A }; // optional + }; + + // Server -> client, to one client. + struct PlayerReachedRespawnCheckpoint : public NetGameMsg { + PlayerReachedRespawnCheckpoint() : NetGameMsg(MessageType::Game::PLAYER_REACHED_RESPAWN_CHECKPOINT) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + NiPoint3 pos{}; + // optional (default identity), written w, x, y, z + NiQuaternion rot{ QuatUtils::IDENTITY }; + }; + + // Server -> client, to one client (UNASSIGNED broadcasts). + struct SetRailMovement : public NetGameMsg { + SetRailMovement() : NetGameMsg(MessageType::Game::SET_RAIL_MOVEMENT) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + bool pathGoForward{}; + std::u16string pathName{}; + uint32_t pathStart{}; + int32_t railActivatorComponentID{ -1 }; // optional + LWOOBJID railActivatorObjectID{ LWOOBJID_EMPTY }; // optional + }; + + // Server -> client, to one client (UNASSIGNED broadcasts). + struct StartRailMovement : public NetGameMsg { + StartRailMovement() : NetGameMsg(MessageType::Game::START_RAIL_MOVEMENT) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + bool bDamageImmune{ true }; + bool bNoAggro{ true }; + bool bNotifyActor{ false }; + bool bShowNameBillboard{ true }; + bool bCameraLocked{ true }; + bool bCollisionEnabled{ true }; + std::u16string loopSound{}; + bool goForward{ true }; + std::u16string pathName{}; + uint32_t pathStart{ 0 }; // optional + int32_t railActivatorComponentID{ -1 }; // optional + LWOOBJID railActivatorObjectID{ LWOOBJID_EMPTY }; // optional + std::u16string startSound{}; + std::u16string stopSound{}; + bool bUseDB{ true }; + }; + + // Client -> server. No payload. + struct ClientRailMovementReady : public NetGameMsg { + ClientRailMovementReady() : NetGameMsg(MessageType::Game::CLIENT_RAIL_MOVEMENT_READY) {} + void Handle(Entity& entity, const SystemAddress& sysAddr) override; + }; + + // Client -> server. + struct CancelRailMovement : public NetGameMsg { + CancelRailMovement() : NetGameMsg(MessageType::Game::CANCEL_RAIL_MOVEMENT) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + void Handle(Entity& entity, const SystemAddress& sysAddr) override; + + bool bImmediate{}; + }; + + // Client -> server. + struct PlayerRailArrivedNotification : public NetGameMsg { + PlayerRailArrivedNotification() : NetGameMsg(MessageType::Game::PLAYER_RAIL_ARRIVED_NOTIFICATION) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + void Handle(Entity& entity, const SystemAddress& sysAddr) override; + + std::u16string pathName{}; + int32_t waypointNumber{}; + }; + + // Server -> client, broadcast. + struct SetMountInventoryID : public NetGameMsg { + SetMountInventoryID() : NetGameMsg(MessageType::Game::SET_MOUNT_INVENTORY_ID) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + LWOOBJID inventoryMountID{ LWOOBJID_EMPTY }; // optional + }; + + // Client -> server. + struct DismountComplete : public NetGameMsg { + DismountComplete() : NetGameMsg(MessageType::Game::DISMOUNT_COMPLETE) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + void Handle(Entity& entity, const SystemAddress& sysAddr) override; + + LWOOBJID mountID{}; + }; + + // Client -> server. + struct AcknowledgePossession : public NetGameMsg { + AcknowledgePossession() : NetGameMsg(MessageType::Game::ACKNOWLEDGE_POSSESSION) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + void Handle(Entity& entity, const SystemAddress& sysAddr) override; + + LWOOBJID possessedObjID{ LWOOBJID_EMPTY }; // optional + }; + + // Client -> server. + struct ToggleGhostReferenceOverride : public NetGameMsg { + ToggleGhostReferenceOverride() : NetGameMsg(MessageType::Game::TOGGLE_GHOST_REFERENCE_OVERRIDE) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + void Handle(Entity& entity, const SystemAddress& sysAddr) override; + + bool bOverride{ false }; + }; + + // Client -> server. + struct SetGhostReferencePosition : public NetGameMsg { + SetGhostReferencePosition() : NetGameMsg(MessageType::Game::SET_GHOST_REFERENCE_POSITION) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + void Handle(Entity& entity, const SystemAddress& sysAddr) override; + + NiPoint3 pos{}; + }; + + // Server -> client, to one client. + struct PlayerSetCameraCyclingMode : public NetGameMsg { + PlayerSetCameraCyclingMode() : NetGameMsg(MessageType::Game::PLAYER_SET_CAMERA_CYCLING_MODE) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + bool bAllowCyclingWhileDeadOnly{ true }; + eCyclingMode cyclingMode{ eCyclingMode::ALLOW_CYCLE_TEAMMATES }; // optional + }; + + // Server -> client, to one client. + struct ForceCameraTargetCycle : public NetGameMsg { + ForceCameraTargetCycle() : NetGameMsg(MessageType::Game::FORCE_CAMERA_TARGET_CYCLE) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + bool bForceCycling{}; + eCameraTargetCyclingMode cyclingMode{ eCameraTargetCyclingMode::ALLOW_CYCLE_TEAMMATES }; // optional + LWOOBJID optionalTargetID{}; + }; +} + +#endif // MOVEMENTMESSAGES_H diff --git a/dGame/dGameMessages/ObjectMessages.cpp b/dGame/dGameMessages/ObjectMessages.cpp new file mode 100644 index 000000000..93c976f5a --- /dev/null +++ b/dGame/dGameMessages/ObjectMessages.cpp @@ -0,0 +1,292 @@ +#include "ObjectMessages.h" + +#include "BitStreamUtils.h" +#include "Character.h" +#include "CharacterComponent.h" +#include "ClientPackets.h" +#include "Entity.h" +#include "EntityManager.h" +#include "Game.h" +#include "GeneralUtils.h" +#include "Logger.h" +#include "MissionComponent.h" +#include "MissionOfferComponent.h" +#include "PlayerManager.h" +#include "RocketLaunchpadControlComponent.h" +#include "ZoneInstanceManager.h" +#include "dServer.h" +#include "dZoneManager.h" +#include "eMissionTaskType.h" +#include "eReplicaComponentType.h" + +namespace GameMessages { + void FireEventClientSide::Serialize(RakNet::BitStream& bitStream) const { + BitStreamUtils::WriteLengthPrefixed(bitStream, args); + bitStream.Write(object); + BitStreamUtils::WriteOptional(bitStream, param1, 0); + BitStreamUtils::WriteOptional(bitStream, param2, -1); + bitStream.Write(senderID); + } + + bool FireEventClientSide::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(BitStreamUtils::ReadLengthPrefixed(bitStream, args)); + VALIDATE_READ(bitStream.Read(object)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, param1, 0)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, param2, -1)); + VALIDATE_READ(bitStream.Read(senderID)); + return true; + } + + void FireEventServerSide::Serialize(RakNet::BitStream& bitStream) const { + BitStreamUtils::WriteLengthPrefixed(bitStream, args); + BitStreamUtils::WriteOptional(bitStream, param1, -1); + BitStreamUtils::WriteOptional(bitStream, param2, -1); + BitStreamUtils::WriteOptional(bitStream, param3, -1); + bitStream.Write(senderID); + } + + bool FireEventServerSide::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(BitStreamUtils::ReadLengthPrefixed(bitStream, args)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, param1, -1)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, param2, -1)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, param3, -1)); + VALIDATE_READ(bitStream.Read(senderID)); + return true; + } + + void FireEventServerSide::Handle(Entity& entity, const SystemAddress& sysAddr) { + auto* sender = Game::entityManager->GetEntity(senderID); + auto* player = PlayerManager::GetPlayer(sysAddr); + + if (!player) { + return; + } + + // This should probably get it's own "ServerEvents" system or something at some point + if (args == u"ZonePlayer") { + // Should probably check to make sure they're using a launcher at some point before someone makes a hack that lets you testmap + + LWOCLONEID cloneId = 0; + LWOMAPID mapId = 0; + + auto* rocketPad = entity.GetComponent(); + + if (rocketPad == nullptr) return; + + cloneId = rocketPad->GetSelectedCloneId(player->GetObjectID()); + + if (param2) { + mapId = rocketPad->GetDefaultZone(); + } else { + mapId = param3; + } + + if (mapId == 0) { + mapId = rocketPad->GetSelectedMapId(player->GetObjectID()); + } + + if (mapId == 0) { + mapId = Game::zoneManager->GetZoneID().GetMapID(); // Fallback to sending the player back to the same zone. + } + + LOG("Player %llu has requested zone transfer to (%i, %i).", sender->GetObjectID(), static_cast(mapId), static_cast(cloneId)); + + auto* character = player->GetCharacter(); + + if (mapId <= 0) { + return; + } + + ZoneInstanceManager::Instance()->RequestZoneTransfer(Game::server, mapId, cloneId, false, [=](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string serverIP, uint16_t serverPort) { + LOG("Transferring %s to Zone %i (Instance %i | Clone %i | Mythran Shift: %s) with IP %s and Port %i", character->GetName().c_str(), zoneID, zoneInstance, zoneClone, mythranShift == true ? "true" : "false", serverIP.c_str(), serverPort); + + if (character) { + auto* characterComponent = player->GetComponent(); + if (characterComponent) { + characterComponent->AddVisitedLevel(LWOZONEID(zoneID, LWOINSTANCEID_INVALID, zoneClone)); + } + + character->SetZoneID(zoneID); + character->SetZoneInstance(zoneInstance); + character->SetZoneClone(zoneClone); + } + + ClientPackets::TransferToWorld transfer; + transfer.serverIP = LUString(serverIP); + transfer.serverPort = serverPort; + transfer.mythranShift = mythranShift; + transfer.Send(sysAddr); + return; + }); + } + + entity.OnFireEventServerSide(sender, GeneralUtils::UTF16ToWTF8(args), param1, param2, param3); + } + + namespace { + template + void WriteNotify(RakNet::BitStream& bitStream, const Msg& msg) { + BitStreamUtils::WriteLengthPrefixed(bitStream, msg.name); + bitStream.Write(msg.param1); + bitStream.Write(msg.param2); + bitStream.Write(msg.paramObj); + BitStreamUtils::WriteLengthPrefixed(bitStream, msg.paramStr); + } + + template + bool ReadNotify(RakNet::BitStream& bitStream, Msg& msg) { + VALIDATE_READ(BitStreamUtils::ReadLengthPrefixed(bitStream, msg.name)); + VALIDATE_READ(bitStream.Read(msg.param1)); + VALIDATE_READ(bitStream.Read(msg.param2)); + VALIDATE_READ(bitStream.Read(msg.paramObj)); + VALIDATE_READ(BitStreamUtils::ReadLengthPrefixed(bitStream, msg.paramStr)); + return true; + } + } + + void NotifyClientObject::Serialize(RakNet::BitStream& bitStream) const { + WriteNotify(bitStream, *this); + } + + bool NotifyClientObject::Deserialize(RakNet::BitStream& bitStream) { + return ReadNotify(bitStream, *this); + } + + void NotifyClientZoneObject::Serialize(RakNet::BitStream& bitStream) const { + WriteNotify(bitStream, *this); + } + + bool NotifyClientZoneObject::Deserialize(RakNet::BitStream& bitStream) { + return ReadNotify(bitStream, *this); + } + + void NotifyObject::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(objIDSender); + BitStreamUtils::WriteLengthPrefixed(bitStream, name); + bitStream.Write(param1); + bitStream.Write(param2); + } + + bool NotifyObject::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(objIDSender)); + VALIDATE_READ(BitStreamUtils::ReadLengthPrefixed(bitStream, name)); + VALIDATE_READ(bitStream.Read(param1)); + VALIDATE_READ(bitStream.Read(param2)); + return true; + } + + void ScriptNetworkVarUpdate::Serialize(RakNet::BitStream& bitStream) const { + BitStreamUtils::WriteNameValueText(bitStream, tableOfVars); + } + + bool ScriptNetworkVarUpdate::Deserialize(RakNet::BitStream& bitStream) { + return BitStreamUtils::ReadNameValueText(bitStream, tableOfVars); + } + + void NotifyClientFailedPrecondition::Serialize(RakNet::BitStream& bitStream) const { + BitStreamUtils::WriteLengthPrefixed(bitStream, failedReason); + bitStream.Write(preconditionID); + } + + bool NotifyClientFailedPrecondition::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(BitStreamUtils::ReadLengthPrefixed(bitStream, failedReason)); + VALIDATE_READ(bitStream.Read(preconditionID)); + return true; + } + + void TerminateInteraction::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(terminator); + bitStream.Write(type); + } + + bool TerminateInteraction::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(terminator)); + VALIDATE_READ(bitStream.Read(type)); + return true; + } + + void SetName::Serialize(RakNet::BitStream& bitStream) const { + BitStreamUtils::WriteLengthPrefixed(bitStream, name); + } + + bool SetName::Deserialize(RakNet::BitStream& bitStream) { + return BitStreamUtils::ReadLengthPrefixed(bitStream, name); + } + + void RequestUse::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(bIsMultiInteractUse); + bitStream.Write(multiInteractID); + bitStream.Write(multiInteractType); + bitStream.Write(object); + bitStream.Write(secondary); + } + + bool RequestUse::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(bIsMultiInteractUse)); + VALIDATE_READ(bitStream.Read(multiInteractID)); + VALIDATE_READ(bitStream.Read(multiInteractType)); + VALIDATE_READ(bitStream.Read(object)); + VALIDATE_READ(bitStream.Read(secondary)); + return true; + } + + void RequestUse::Handle(Entity& entity, const SystemAddress& sysAddr) { + Entity* interactedObject = Game::entityManager->GetEntity(object); + + if (interactedObject == nullptr) { + LOG("Object %llu tried to interact, but doesn't exist!", object); + + return; + } + + if (interactedObject->GetLOT() == 9524) { + entity.GetCharacter()->SetBuildMode(true); + } + + if (bIsMultiInteractUse) { + if (multiInteractType == 0) { + auto* missionOfferComponent = static_cast(interactedObject->GetComponent(eReplicaComponentType::MISSION_OFFER)); + + if (missionOfferComponent != nullptr) { + missionOfferComponent->OfferMissions(&entity, multiInteractID); + } + } else { + interactedObject->OnUse(&entity); + } + } else { + interactedObject->OnUse(&entity); + } + + RequestUseEvent event(*this); + interactedObject->HandleMsg(event); + + //Perform use task if possible: + auto missionComponent = entity.GetComponent(); + + if (!missionComponent) return; + + missionComponent->Progress(eMissionTaskType::TALK_TO_NPC, interactedObject->GetLOT(), interactedObject->GetObjectID()); + missionComponent->Progress(eMissionTaskType::INTERACT, interactedObject->GetLOT(), interactedObject->GetObjectID()); + } + + void RequestServerObjectInfo::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(bVerbose); + bitStream.Write(clientId); + bitStream.Write(targetForReport); + } + + bool RequestServerObjectInfo::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(bVerbose)); + VALIDATE_READ(bitStream.Read(clientId)); + VALIDATE_READ(bitStream.Read(targetForReport)); + return true; + } + + void RequestServerObjectInfo::Handle(Entity& entity, const SystemAddress& sysAddr) { + auto* handlingEntity = Game::entityManager->GetEntity(targetForReport); + if (handlingEntity) { + RequestServerObjectInfoEvent event(*this); + handlingEntity->HandleMsg(event); + } else LOG("Failed to find target %llu", targetForReport); + } +} diff --git a/dGame/dGameMessages/ObjectMessages.h b/dGame/dGameMessages/ObjectMessages.h new file mode 100644 index 000000000..7e0a0475b --- /dev/null +++ b/dGame/dGameMessages/ObjectMessages.h @@ -0,0 +1,188 @@ +#ifndef OBJECTMESSAGES_H +#define OBJECTMESSAGES_H + +#include "GameMessages.h" +#include "eTerminateType.h" + +#include + +// Game messages between an object's server script and its client script (fire event, notify object), and object +// interaction (use, terminate, names, debug info). +// Fields are listed in wire order; names follow the client (legouniverse.exe 1.10.64) where known. +namespace GameMessages { + // Server -> client, to one client. + struct FireEventClientSide : public NetGameMsg { + FireEventClientSide() : NetGameMsg(MessageType::Game::FIRE_EVENT_CLIENT_SIDE) {} + FireEventClientSide(const LWOOBJID _target, const std::u16string& _args, const LWOOBJID _object, const LWOOBJID _senderID) : FireEventClientSide() { + target = _target; + args = _args; + object = _object; + senderID = _senderID; + } + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + std::u16string args{}; + LWOOBJID object{}; + int64_t param1{ 0 }; // optional + int32_t param2{ -1 }; // optional + LWOOBJID senderID{}; + }; + + // Client -> server. + struct FireEventServerSide : public NetGameMsg { + FireEventServerSide() : NetGameMsg(MessageType::Game::FIRE_EVENT_SERVER_SIDE) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + void Handle(Entity& entity, const SystemAddress& sysAddr) override; + + std::u16string args{}; + int32_t param1{ -1 }; // optional + int32_t param2{ -1 }; // optional + int32_t param3{ -1 }; // optional + LWOOBJID senderID{}; + }; + + // Server -> client, to one client (UNASSIGNED broadcasts). + struct NotifyClientObject : public NetGameMsg { + NotifyClientObject() : NetGameMsg(MessageType::Game::NOTIFY_CLIENT_OBJECT) {} + NotifyClientObject(const LWOOBJID _target, const std::u16string& _name, const int32_t _param1 = 0, const int32_t _param2 = 0, + const LWOOBJID _paramObj = LWOOBJID_EMPTY, const std::string& _paramStr = "") : NotifyClientObject() { + target = _target; + name = _name; + param1 = _param1; + param2 = _param2; + paramObj = _paramObj; + paramStr = _paramStr; + } + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + std::u16string name{}; + int32_t param1{}; + int32_t param2{}; + LWOOBJID paramObj{}; + std::string paramStr{}; + }; + + // Server -> client, to one client (UNASSIGNED broadcasts). Same layout as NotifyClientObject. + struct NotifyClientZoneObject : public NetGameMsg { + NotifyClientZoneObject() : NetGameMsg(MessageType::Game::NOTIFY_CLIENT_ZONE_OBJECT) {} + NotifyClientZoneObject(const LWOOBJID _target, const std::u16string& _name, const int32_t _param1 = 0, const int32_t _param2 = 0, + const LWOOBJID _paramObj = LWOOBJID_EMPTY, const std::string& _paramStr = "") : NotifyClientZoneObject() { + target = _target; + name = _name; + param1 = _param1; + param2 = _param2; + paramObj = _paramObj; + paramStr = _paramStr; + } + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + std::u16string name{}; + int32_t param1{}; + int32_t param2{}; + LWOOBJID paramObj{}; + std::string paramStr{}; + }; + + // Server -> client, to one client (UNASSIGNED broadcasts). + struct NotifyObject : public NetGameMsg { + NotifyObject() : NetGameMsg(MessageType::Game::NOTIFY_OBJECT) {} + NotifyObject(const LWOOBJID _target, const LWOOBJID _objIDSender, const std::u16string& _name, const int32_t _param1 = 0, const int32_t _param2 = 0) : NotifyObject() { + target = _target; + objIDSender = _objIDSender; + name = _name; + param1 = _param1; + param2 = _param2; + } + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + LWOOBJID objIDSender{}; + std::u16string name{}; + int32_t param1{ 0 }; + int32_t param2{ 0 }; + }; + + // Server -> client, to one client (UNASSIGNED broadcasts). + struct ScriptNetworkVarUpdate : public NetGameMsg { + ScriptNetworkVarUpdate() : NetGameMsg(MessageType::Game::SCRIPT_NETWORK_VAR_UPDATE) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + std::u16string tableOfVars{}; // name-value (LDF) text + }; + + // Server -> client, to one client (UNASSIGNED broadcasts). + struct NotifyClientFailedPrecondition : public NetGameMsg { + NotifyClientFailedPrecondition() : NetGameMsg(MessageType::Game::NOTIFY_CLIENT_FAILED_PRECONDITION) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + std::u16string failedReason{}; + int32_t preconditionID{}; + }; + + // Server -> client, broadcast. + struct TerminateInteraction : public NetGameMsg { + TerminateInteraction() : NetGameMsg(MessageType::Game::TERMINATE_INTERACTION) {} + TerminateInteraction(const LWOOBJID _target, const eTerminateType _type, const LWOOBJID _terminator) : TerminateInteraction() { + target = _target; + type = _type; + terminator = _terminator; + } + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + LWOOBJID terminator{}; + eTerminateType type{}; + }; + + // Server -> client, to one client (UNASSIGNED broadcasts). + struct SetName : public NetGameMsg { + SetName() : NetGameMsg(MessageType::Game::SET_NAME) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + std::u16string name{}; + }; + + // Client -> server. + struct RequestUse : public NetGameMsg { + RequestUse() : NetGameMsg(MessageType::Game::REQUEST_USE) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + void Handle(Entity& entity, const SystemAddress& sysAddr) override; + + // Set to true if this coming from a multi-interaction UI on the client. + bool bIsMultiInteractUse{}; + + // Used only for multi-interaction + uint32_t multiInteractID{}; + + // Used only for multi-interaction, is of the enum type InteractionType + int32_t multiInteractType{}; + + LWOOBJID object{}; + + bool secondary{ false }; + }; + using RequestUseEvent = NetGameMsgEvent; + + // Client -> server. Developer only. + struct RequestServerObjectInfo : public NetGameMsg { + RequestServerObjectInfo() : NetGameMsg(MessageType::Game::REQUEST_SERVER_OBJECT_INFO, eGameMasterLevel::DEVELOPER) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + void Handle(Entity& entity, const SystemAddress& sysAddr) override; + + bool bVerbose{}; + LWOOBJID clientId{}; + LWOOBJID targetForReport{}; + }; + using RequestServerObjectInfoEvent = NetGameMsgEvent; +} + +#endif // OBJECTMESSAGES_H diff --git a/dGame/dGameMessages/PlayerMessages.cpp b/dGame/dGameMessages/PlayerMessages.cpp new file mode 100644 index 000000000..fd9a2a5c2 --- /dev/null +++ b/dGame/dGameMessages/PlayerMessages.cpp @@ -0,0 +1,209 @@ +#include "PlayerMessages.h" + +#include "BitStreamUtils.h" +#include "Character.h" +#include "CharacterComponent.h" +#include "DashboardNotify.h" +#include "Database.h" +#include "Entity.h" +#include "GeneralUtils.h" +#include "IBugReports.h" +#include "PlayerReports.h" +#include "SlashCommandHandler.h" + +namespace GameMessages { + void UpdateChatMode::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(level); + } + + bool UpdateChatMode::Deserialize(RakNet::BitStream& bitStream) { + return bitStream.Read(level); + } + + void SetGMLevel::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(bOverride); + bitStream.Write(level); + } + + bool SetGMLevel::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(bOverride)); + VALIDATE_READ(bitStream.Read(level)); + return true; + } + + void ModifyLEGOScore::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(score); + BitStreamUtils::WriteOptional(bitStream, sourceType, eLootSourceType::NONE); + } + + bool ModifyLEGOScore::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(score)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, sourceType, eLootSourceType::NONE)); + return true; + } + + void SetCurrency::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(currency); + BitStreamUtils::WriteOptional(bitStream, lootType, LOOTTYPE_NONE); + bitStream.Write(position.x); + bitStream.Write(position.y); + bitStream.Write(position.z); + BitStreamUtils::WriteOptional(bitStream, sourceLOT, LOT_NULL); + BitStreamUtils::WriteOptional(bitStream, sourceID, LWOOBJID_EMPTY); + BitStreamUtils::WriteOptional(bitStream, sourceTradeID, 0); + BitStreamUtils::WriteOptional(bitStream, sourceType, eLootSourceType::NONE); + } + + bool SetCurrency::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(currency)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, lootType, LOOTTYPE_NONE)); + VALIDATE_READ(bitStream.Read(position.x)); + VALIDATE_READ(bitStream.Read(position.y)); + VALIDATE_READ(bitStream.Read(position.z)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, sourceLOT, LOT_NULL)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, sourceID, LWOOBJID_EMPTY)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, sourceTradeID, 0)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, sourceType, eLootSourceType::NONE)); + return true; + } + + void UpdateReputation::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(reputation); + } + + bool UpdateReputation::Deserialize(RakNet::BitStream& bitStream) { + return bitStream.Read(reputation); + } + + void ToggleGMInvis::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(bStateOut); + } + + bool ToggleGMInvis::Deserialize(RakNet::BitStream& bitStream) { + return bitStream.Read(bStateOut); + } + + void PickupCurrency::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(currency); + } + + bool PickupCurrency::Deserialize(RakNet::BitStream& bitStream) { + return bitStream.Read(currency); + } + + void PickupCurrency::Handle(Entity& entity, const SystemAddress& sysAddr) { + if (currency == 0) return; + + auto* ch = entity.GetCharacter(); + if (ch && entity.PickupCoins(currency)) { + ch->SetCoins(ch->GetCoins() + currency, eLootSourceType::PICKUP); + } + } + + void ModifyPlayerZoneStatistic::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(bSet); + BitStreamUtils::WriteLengthPrefixed(bitStream, statName); + BitStreamUtils::WriteOptional(bitStream, statValue, 0); + BitStreamUtils::WriteOptional(bitStream, zoneID, LWOMAPID_INVALID); + } + + bool ModifyPlayerZoneStatistic::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(bSet)); + VALIDATE_READ(BitStreamUtils::ReadLengthPrefixed(bitStream, statName)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, statValue, 0)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, zoneID, LWOMAPID_INVALID)); + return true; + } + + void ModifyPlayerZoneStatistic::Handle(Entity& entity, const SystemAddress& sysAddr) { + // Notify the character component that something's changed + auto* characterComponent = entity.GetComponent(); + if (characterComponent != nullptr) { + characterComponent->HandleZoneStatisticsUpdate(zoneID, statName, statValue); + } + } + + void UpdatePlayerStatistic::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(updateID); + BitStreamUtils::WriteOptional(bitStream, updateValue, 1); + } + + bool UpdatePlayerStatistic::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(updateID)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, updateValue, 1)); + return true; + } + + void UpdatePlayerStatistic::Handle(Entity& entity, const SystemAddress& sysAddr) { + auto* characterComponent = entity.GetComponent(); + if (characterComponent != nullptr) { + characterComponent->UpdatePlayerStatistic(static_cast(updateID), static_cast(std::max(updateValue, static_cast(0))), true); + } + } + + void ParseChatMessage::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(iClientState); + BitStreamUtils::WriteLengthPrefixed(bitStream, wsString); + } + + bool ParseChatMessage::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(iClientState)); + VALIDATE_READ(BitStreamUtils::ReadLengthPrefixed(bitStream, wsString)); + return true; + } + + void ParseChatMessage::Handle(Entity& entity, const SystemAddress& sysAddr) { + if (!wsString.empty() && wsString[0] == L'/') { + SlashCommandHandler::HandleChatCommand(wsString, &entity, sysAddr); + } + } + + void ReportBug::Serialize(RakNet::BitStream& bitStream) const { + BitStreamUtils::WriteLengthPrefixed(bitStream, body); + BitStreamUtils::WriteLengthPrefixed(bitStream, clientVersion); + BitStreamUtils::WriteLengthPrefixed(bitStream, otherPlayerID); + BitStreamUtils::WriteLengthPrefixed(bitStream, selection); + } + + bool ReportBug::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(BitStreamUtils::ReadLengthPrefixed(bitStream, body)); + VALIDATE_READ(BitStreamUtils::ReadLengthPrefixed(bitStream, clientVersion)); + VALIDATE_READ(BitStreamUtils::ReadLengthPrefixed(bitStream, otherPlayerID)); + VALIDATE_READ(BitStreamUtils::ReadLengthPrefixed(bitStream, selection)); + return true; + } + + void ReportBug::Handle(Entity& entity, const SystemAddress& sysAddr) { + IBugReports::Info reportInfo; + // The body is stored narrowed to one byte per character, as DLU always did. + for (const auto character : body) reportInfo.body.push_back(static_cast(character)); + reportInfo.clientVersion = clientVersion; + reportInfo.otherPlayer = otherPlayerID; + reportInfo.selection = selection; + + auto character = entity.GetCharacter(); + if (character) reportInfo.characterId = character->GetID(); + + // Report Abuse about another player sends their ID here (and "0" otherwise); that's a player report, not a bug + if (const auto reportedId = GeneralUtils::TryParse(reportInfo.otherPlayer).value_or(LWOOBJID_EMPTY); reportedId != LWOOBJID_EMPTY) { + PlayerReports::ReportPlayer(&entity, reportedId, reportInfo.body); + return; + } + + Database::Get()->InsertNewBugReport(reportInfo); + DashboardNotify::Changed("bug_reports"); + } + + void VerifyAck::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(bDifferent); + BitStreamUtils::WriteLengthPrefixed(bitStream, sBitStream); + BitStreamUtils::WriteOptional(bitStream, uiHandle, 0); + } + + bool VerifyAck::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(bDifferent)); + VALIDATE_READ(BitStreamUtils::ReadLengthPrefixed(bitStream, sBitStream)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, uiHandle, 0)); + return true; + } +} diff --git a/dGame/dGameMessages/PlayerMessages.h b/dGame/dGameMessages/PlayerMessages.h new file mode 100644 index 000000000..168ed7039 --- /dev/null +++ b/dGame/dGameMessages/PlayerMessages.h @@ -0,0 +1,149 @@ +#ifndef PLAYERMESSAGES_H +#define PLAYERMESSAGES_H + +#include "GameMessages.h" +#include "eGameMasterLevel.h" +#include "eLootSourceType.h" + +#include + +// Game messages about a player's own state: GM level and chat mode, score, coins, reputation, statistics, chat +// commands and bug reports. +// Fields are listed in wire order; names follow the client (legouniverse.exe 1.10.64) where known. +namespace GameMessages { + // Server -> client, broadcast. + struct UpdateChatMode : public NetGameMsg { + UpdateChatMode() : NetGameMsg(MessageType::Game::UPDATE_CHAT_MODE) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + eGameMasterLevel level{}; + }; + + // Server -> client, broadcast. + struct SetGMLevel : public NetGameMsg { + SetGMLevel() : NetGameMsg(MessageType::Game::SET_GM_LEVEL) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + // DLU always writes this bit set. + bool bOverride{ true }; + eGameMasterLevel level{}; + }; + + // Server -> client, to one client. + struct ModifyLEGOScore : public NetGameMsg { + ModifyLEGOScore() : NetGameMsg(MessageType::Game::MODIFY_LEGO_SCORE) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + int64_t score{}; + eLootSourceType sourceType{ eLootSourceType::NONE }; // optional + }; + + // Server -> client, to one client. + struct SetCurrency : public NetGameMsg { + SetCurrency() : NetGameMsg(MessageType::Game::SET_CURRENCY) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + int64_t currency{}; + int32_t lootType{ LOOTTYPE_NONE }; // optional + NiPoint3 position{ NiPoint3Constant::ZERO }; + LOT sourceLOT{ LOT_NULL }; // optional + LWOOBJID sourceID{ LWOOBJID_EMPTY }; // optional + // optional (default 0). DLU writes it as an int32_t; lu_packets has an object ID (8 bytes). Only ever 0. + int32_t sourceTradeID{ 0 }; + eLootSourceType sourceType{ eLootSourceType::NONE }; // optional + }; + + // Server -> client, to one client. + struct UpdateReputation : public NetGameMsg { + UpdateReputation() : NetGameMsg(MessageType::Game::UPDATE_REPUTATION) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + int64_t reputation{}; + }; + + // Server -> client, broadcast. + struct ToggleGMInvis : public NetGameMsg { + ToggleGMInvis() : NetGameMsg(MessageType::Game::TOGGLE_GM_INVIS) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + bool bStateOut{ false }; + }; + using ToggleGMInvisEvent = NetGameMsgEvent; + + // Client -> server. DLU reads only the amount; lu_packets has a position after it. + struct PickupCurrency : public NetGameMsg { + PickupCurrency() : NetGameMsg(MessageType::Game::PICKUP_CURRENCY) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + void Handle(Entity& entity, const SystemAddress& sysAddr) override; + + uint32_t currency{}; + }; + + // Client -> server. + struct ModifyPlayerZoneStatistic : public NetGameMsg { + ModifyPlayerZoneStatistic() : NetGameMsg(MessageType::Game::MODIFY_PLAYER_ZONE_STATISTIC) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + void Handle(Entity& entity, const SystemAddress& sysAddr) override; + + bool bSet{ false }; + std::u16string statName{}; + int32_t statValue{ 0 }; // optional + LWOMAPID zoneID{ LWOMAPID_INVALID }; // optional + }; + + // Client -> server. + struct UpdatePlayerStatistic : public NetGameMsg { + UpdatePlayerStatistic() : NetGameMsg(MessageType::Game::UPDATE_PLAYER_STATISTIC) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + void Handle(Entity& entity, const SystemAddress& sysAddr) override; + + int32_t updateID{}; + int64_t updateValue{ 1 }; // optional + }; + + // Client -> server. Chat text the client parses as a command (starts with '/'). + struct ParseChatMessage : public NetGameMsg { + ParseChatMessage() : NetGameMsg(MessageType::Game::PARSE_CHAT_MESSAGE) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + void Handle(Entity& entity, const SystemAddress& sysAddr) override; + + int32_t iClientState{}; + std::u16string wsString{}; + }; + + // Client -> server. A bug report, or a player report when otherPlayerID holds a player's ID. + struct ReportBug : public NetGameMsg { + ReportBug() : NetGameMsg(MessageType::Game::REPORT_BUG) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + void Handle(Entity& entity, const SystemAddress& sysAddr) override; + + std::u16string body{}; + std::string clientVersion{}; + std::string otherPlayerID{}; + std::string selection{}; + }; + + // Client -> server. Nothing is done with it. + struct VerifyAck : public NetGameMsg { + VerifyAck() : NetGameMsg(MessageType::Game::VERIFY_ACK) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + bool bDifferent{ false }; + std::string sBitStream{}; + uint32_t uiHandle{ 0 }; // optional + }; +} + +#endif // PLAYERMESSAGES_H diff --git a/dGame/dGameMessages/PropertyMessages.cpp b/dGame/dGameMessages/PropertyMessages.cpp index 4ee41e004..9e7e09db2 100644 --- a/dGame/dGameMessages/PropertyMessages.cpp +++ b/dGame/dGameMessages/PropertyMessages.cpp @@ -738,4 +738,13 @@ namespace GameMessages { } return true; } + + void PlayBehaviorSound::Serialize(RakNet::BitStream& stream) const { + stream.Write(soundID != -1); + if (soundID != -1) stream.Write(soundID); + } + + bool PlayBehaviorSound::Deserialize(RakNet::BitStream& stream) { + return BitStreamUtils::ReadOptional(stream, soundID, -1); + } } diff --git a/dGame/dGameMessages/PropertyMessages.h b/dGame/dGameMessages/PropertyMessages.h index 24f847d86..103fba34c 100644 --- a/dGame/dGameMessages/PropertyMessages.h +++ b/dGame/dGameMessages/PropertyMessages.h @@ -393,6 +393,15 @@ namespace GameMessages { std::vector properties; }; + + struct PlayBehaviorSound : public NetGameMsg { + PlayBehaviorSound() : NetGameMsg(MessageType::Game::PLAY_BEHAVIOR_SOUND) {} + + void Serialize(RakNet::BitStream& stream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + int32_t soundID{ -1 }; + }; }; #endif // PROPERTYMESSAGES_H diff --git a/dGame/dGameMessages/QuickBuildMessages.cpp b/dGame/dGameMessages/QuickBuildMessages.cpp new file mode 100644 index 000000000..9191b5255 --- /dev/null +++ b/dGame/dGameMessages/QuickBuildMessages.cpp @@ -0,0 +1,58 @@ +#include "QuickBuildMessages.h" + +#include "BitStreamUtils.h" +#include "Entity.h" +#include "EntityManager.h" +#include "QuickBuildComponent.h" + +namespace GameMessages { + void RebuildNotifyState::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(prevState); + bitStream.Write(state); + bitStream.Write(player); + } + + bool RebuildNotifyState::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(prevState)); + VALIDATE_READ(bitStream.Read(state)); + VALIDATE_READ(bitStream.Read(player)); + return true; + } + + void EnableRebuild::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(bEnable); + bitStream.Write(bFail); + bitStream.Write(bSuccess); + BitStreamUtils::WriteOptional(bitStream, eFailReason, eQuickBuildFailReason::NOT_GIVEN); + bitStream.Write(fDuration); + bitStream.Write(user); + } + + bool EnableRebuild::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(bEnable)); + VALIDATE_READ(bitStream.Read(bFail)); + VALIDATE_READ(bitStream.Read(bSuccess)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, eFailReason, eQuickBuildFailReason::NOT_GIVEN)); + VALIDATE_READ(bitStream.Read(fDuration)); + VALIDATE_READ(bitStream.Read(user)); + return true; + } + + void RebuildCancel::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(bEarlyRelease); + bitStream.Write(userID); + } + + bool RebuildCancel::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(bEarlyRelease)); + VALIDATE_READ(bitStream.Read(userID)); + return true; + } + + void RebuildCancel::Handle(Entity& entity, const SystemAddress& sysAddr) { + auto* quickBuildComponent = entity.GetComponent(); + if (!quickBuildComponent) return; + + quickBuildComponent->CancelQuickBuild(Game::entityManager->GetEntity(userID), eQuickBuildFailReason::CANCELED_EARLY); + } +} diff --git a/dGame/dGameMessages/QuickBuildMessages.h b/dGame/dGameMessages/QuickBuildMessages.h new file mode 100644 index 000000000..e9df7ff16 --- /dev/null +++ b/dGame/dGameMessages/QuickBuildMessages.h @@ -0,0 +1,48 @@ +#ifndef QUICKBUILDMESSAGES_H +#define QUICKBUILDMESSAGES_H + +#include "GameMessages.h" +#include "eQuickBuildFailReason.h" +#include "eQuickBuildState.h" + +// Game messages for quickbuilds (rebuilds in the client). +// Fields are listed in wire order; names follow the client (legouniverse.exe 1.10.64) where known. +namespace GameMessages { + // Server -> client, broadcast. + struct RebuildNotifyState : public NetGameMsg { + RebuildNotifyState() : NetGameMsg(MessageType::Game::REBUILD_NOTIFY_STATE) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + eQuickBuildState prevState{}; + eQuickBuildState state{}; + LWOOBJID player{}; + }; + + // Server -> client, broadcast. + struct EnableRebuild : public NetGameMsg { + EnableRebuild() : NetGameMsg(MessageType::Game::ENABLE_REBUILD) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + bool bEnable{}; + bool bFail{}; + bool bSuccess{}; + eQuickBuildFailReason eFailReason{ eQuickBuildFailReason::NOT_GIVEN }; // optional + float fDuration{}; + LWOOBJID user{}; + }; + + // Client -> server. + struct RebuildCancel : public NetGameMsg { + RebuildCancel() : NetGameMsg(MessageType::Game::REBUILD_CANCEL) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + void Handle(Entity& entity, const SystemAddress& sysAddr) override; + + bool bEarlyRelease{}; + LWOOBJID userID{}; + }; +} + +#endif // QUICKBUILDMESSAGES_H diff --git a/dGame/dGameMessages/SkillMessages.cpp b/dGame/dGameMessages/SkillMessages.cpp index b1929c358..eba5413d5 100644 --- a/dGame/dGameMessages/SkillMessages.cpp +++ b/dGame/dGameMessages/SkillMessages.cpp @@ -262,4 +262,19 @@ namespace GameMessages { VALIDATE_READ(BitStreamUtils::ReadLengthPrefixed(bitStream, sBitStream)); return true; } + + void UseSkillSet::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(bRemove); + bitStream.Write(possessedId != LWOOBJID_EMPTY); + if (possessedId != LWOOBJID_EMPTY) bitStream.Write(possessedId); + bitStream.Write(setId != -1); + if (setId != -1) bitStream.Write(setId); + } + + bool UseSkillSet::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(bRemove)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, possessedId, LWOOBJID_EMPTY)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, setId, -1)); + return true; + } } diff --git a/dGame/dGameMessages/SkillMessages.h b/dGame/dGameMessages/SkillMessages.h index 26d1195b7..c6cd5f14b 100644 --- a/dGame/dGameMessages/SkillMessages.h +++ b/dGame/dGameMessages/SkillMessages.h @@ -137,6 +137,16 @@ namespace GameMessages { LWOOBJID i64TargetID{ LWOOBJID_EMPTY }; // optional std::string sBitStream{}; }; + + struct UseSkillSet : public NetGameMsg { + UseSkillSet() : NetGameMsg(MessageType::Game::USE_SKILL_SET) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + bool bRemove{}; + LWOOBJID possessedId{ LWOOBJID_EMPTY }; + int32_t setId{ -1 }; + }; }; #endif // SKILLMESSAGES_H diff --git a/dGame/dGameMessages/ZoneMessages.cpp b/dGame/dGameMessages/ZoneMessages.cpp new file mode 100644 index 000000000..f1681b86e --- /dev/null +++ b/dGame/dGameMessages/ZoneMessages.cpp @@ -0,0 +1,259 @@ +#include "ZoneMessages.h" + +#include "BitStreamUtils.h" +#include "CDClientManager.h" +#include "Character.h" +#include "CharacterComponent.h" +#include "CppScripts.h" +#include "DestroyableComponent.h" +#include "EffectsMessages.h" +#include "Entity.h" +#include "EntityManager.h" +#include "Game.h" +#include "GhostComponent.h" +#include "InventoryComponent.h" +#include "InventoryMessages.h" +#include "LevelProgressionComponent.h" +#include "RacingControlComponent.h" +#include "WorldMigration.h" +#include "dConfig.h" +#include "dZoneManager.h" +#include "eReplicaComponentType.h" +#include "eTriggerEventType.h" +#include "ePlayerFlag.h" + +#include + +namespace GameMessages { + void PlayerLoaded::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(playerID); + } + + bool PlayerLoaded::Deserialize(RakNet::BitStream& bitStream) { + return bitStream.Read(playerID); + } + + void PlayerLoaded::Handle(Entity& entityRef, const SystemAddress& sysAddr) { + auto* entity = &entityRef; + PlayerReady playerReady; + playerReady.target = entity->GetObjectID(); + playerReady.SendToClient(sysAddr); + entity->SetPlayerReadyForUpdates(); + + auto* ghostComponent = entity->GetComponent(); + if (ghostComponent != nullptr) { + ghostComponent->ConstructLimboEntities(); + } + + InventoryComponent* inv = entity->GetComponent(); + if (inv) { + // Clear server-side skill state so AddItemSkills sends fresh AddSkill + // packets to the now-ready client. Skills sent during entity construction + // (Serialize) arrive before LWOSkillComponent is initialized and are dropped. + inv->ClearSkills(); + auto items = inv->GetEquippedItems(); + for (auto pair : items) { + const auto item = pair.second; + + inv->AddItemSkills(item.lot); + } + + // Fixes a bug where testmapping too fast causes large item inventories to become invisible. + // Only affects item inventory + GameMessages::SetInventorySize setSize; + setSize.target = entity->GetObjectID(); + setSize.inventoryType = eInventoryType::ITEMS; + setSize.size = inv->GetInventory(eInventoryType::ITEMS)->GetSize(); + setSize.SendToClient(entity->GetSystemAddress()); + } + + RestoreToPostLoadStats restoreStats; + restoreStats.target = entity->GetObjectID(); + restoreStats.SendToClient(sysAddr); + + auto* const destroyable = entity->GetComponent(); + if (destroyable) destroyable->SetImagination(destroyable->GetImagination()); + Game::entityManager->SerializeEntity(entity); + + std::vector racingControllers = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::RACING_CONTROL); + for (Entity* racingController : racingControllers) { + auto* racingComponent = racingController->GetComponent(); + if (racingComponent != nullptr) { + racingComponent->OnPlayerLoaded(entity); + } + } + + Entity* zoneControl = Game::entityManager->GetZoneControlEntity(); + if (zoneControl) { + zoneControl->GetScript()->OnPlayerLoaded(zoneControl, entity); + } + + std::vector scriptedActs = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SCRIPT); + for (Entity* scriptEntity : scriptedActs) { + if (!zoneControl || scriptEntity->GetObjectID() != zoneControl->GetObjectID()) { // Don't want to trigger twice on instance worlds + scriptEntity->GetScript()->OnPlayerLoaded(scriptEntity, entity); + } + } + + //Kill player if health == 0 + if (entity->GetIsDead()) { + entity->Smash(entity->GetObjectID()); + } + + //if the player has moved significantly, move them back: + if ((entity->GetPosition().y - entity->GetCharacter()->GetOriginalPos().y) > 2.0f) { + // Disabled until fixed + //GameMessages::Teleport(entity->GetObjectID(), entity->GetCharacter()->GetOriginalPos(), entity->GetCharacter()->GetOriginalRot(), true).Send(entity->GetSystemAddress()); + } + + /** + * Invoke the OnZoneLoad event on the player character + */ + auto* character = entity->GetCharacter(); + + if (character != nullptr) { + character->OnZoneLoad(); + } + + // Moved here from another instance: put back what their save doesn't keep (the pet that was out) + WorldMigration::OnPlayerLoaded(entity); + + LOG("Player %s (%llu) loaded.", entity->GetCharacter()->GetName().c_str(), entity->GetObjectID()); + + // After we've done our thing, tell the client they're ready + PlayerReady zoneReady; + zoneReady.target = Game::zoneManager->GetZoneControlObject()->GetObjectID(); + zoneReady.SendToClient(sysAddr); + + if (Game::config->GetValue("allow_players_to_skip_cinematics") != "1" + || !entity->GetCharacter() + || !entity->GetCharacter()->GetPlayerFlag(ePlayerFlag::DLU_SKIP_CINEMATICS)) return; + entity->AddCallbackTimer(0.5f, [entity, sysAddr]() { + if (!entity) return; + GameMessages::EndCinematic endCinematic; + endCinematic.target = entity->GetObjectID(); + endCinematic.Send(sysAddr); + }); + } + + void ReadyForUpdates::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(objectID); + } + + bool ReadyForUpdates::Deserialize(RakNet::BitStream& bitStream) { + return bitStream.Read(objectID); + } + + void InvalidZoneTransferList::Serialize(RakNet::BitStream& bitStream) const { + BitStreamUtils::WriteLengthPrefixed(bitStream, customerFeedbackURL); + BitStreamUtils::WriteLengthPrefixed(bitStream, invalidMapTransferList); + bitStream.Write(bCustomerFeedbackOnExit); + bitStream.Write(bCustomerFeedbackOnInvalidMapTransfer); + } + + bool InvalidZoneTransferList::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(BitStreamUtils::ReadLengthPrefixed(bitStream, customerFeedbackURL)); + VALIDATE_READ(BitStreamUtils::ReadLengthPrefixed(bitStream, invalidMapTransferList)); + VALIDATE_READ(bitStream.Read(bCustomerFeedbackOnExit)); + VALIDATE_READ(bitStream.Read(bCustomerFeedbackOnInvalidMapTransfer)); + return true; + } + + void DisplayZoneSummary::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(isPropertyMap); + bitStream.Write(isZoneStart); + BitStreamUtils::WriteOptional(bitStream, sender, LWOOBJID_EMPTY); + } + + bool DisplayZoneSummary::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(isPropertyMap)); + VALIDATE_READ(bitStream.Read(isZoneStart)); + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, sender, LWOOBJID_EMPTY)); + return true; + } + + void ZoneSummaryDismissed::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(playerID); + } + + bool ZoneSummaryDismissed::Deserialize(RakNet::BitStream& bitStream) { + return bitStream.Read(playerID); + } + + void ZoneSummaryDismissed::Handle(Entity& entity, const SystemAddress& sysAddr) { + auto* player = Game::entityManager->GetEntity(playerID); + entity.TriggerEvent(eTriggerEventType::ZONE_SUMMARY_DISMISSED, player); + } + + void NotifyServerLevelProcessingComplete::Handle(Entity& entity, const SystemAddress& sysAddr) { + auto* levelComp = entity.GetComponent(); + if (!levelComp) return; + auto* character = entity.GetComponent(); + if (!character) return; + + //Update our character's level in memory: + levelComp->SetLevel(levelComp->GetLevel() + 1); + + levelComp->HandleLevelUp(); + + auto* inventoryComponent = entity.GetComponent(); + + if (inventoryComponent != nullptr) { + auto* inventory = inventoryComponent->GetInventory(ITEMS); + + if (inventory != nullptr && Game::config->GetValue("disable_extra_backpack") != "1") { + inventory->SetSize(inventory->GetSize() + 2); + } + } + + //Play the level up effect: + GameMessages::PlayFXEffect(entity.GetObjectID(), 7074, u"create", "7074").Send(UNASSIGNED_SYSTEM_ADDRESS); + + //Send a notification in chat: + std::stringstream wss; + wss << "level=1:"; + wss << levelComp->GetLevel(); + wss << "\n"; + wss << "name=0:"; + wss << character->GetName(); + + // FIXME: only really need utf8 conversion for the name, so move that up? + std::u16string attrs = GeneralUtils::UTF8ToUTF16(wss.str()); + std::u16string wsText = u"UI_LEVEL_PROGRESSION_LEVELUP_MESSAGE"; + + GameMessages::BroadcastTextToChatbox chatboxText; + chatboxText.target = entity.GetObjectID(); + chatboxText.attrs = attrs; + chatboxText.wsText = wsText; + chatboxText.Send(UNASSIGNED_SYSTEM_ADDRESS); + } + + void ChangeObjectWorldState::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(newState); + } + + bool ChangeObjectWorldState::Deserialize(RakNet::BitStream& bitStream) { + return bitStream.Read(newState); + } + + void LocalizedAnnouncementServerToSingleClient::Serialize(RakNet::BitStream& bitStream) const { + BitStreamUtils::WriteNameValueText(bitStream, bodyParams); + bitStream.Write(bForceOpenChatBox); + bitStream.Write(bShowAnnouncement); + bitStream.Write(bShowInChat); + BitStreamUtils::WriteLengthPrefixed(bitStream, body); + BitStreamUtils::WriteLengthPrefixed(bitStream, title); + BitStreamUtils::WriteNameValueText(bitStream, titleParams); + } + + bool LocalizedAnnouncementServerToSingleClient::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(BitStreamUtils::ReadNameValueText(bitStream, bodyParams)); + VALIDATE_READ(bitStream.Read(bForceOpenChatBox)); + VALIDATE_READ(bitStream.Read(bShowAnnouncement)); + VALIDATE_READ(bitStream.Read(bShowInChat)); + VALIDATE_READ(BitStreamUtils::ReadLengthPrefixed(bitStream, body)); + VALIDATE_READ(BitStreamUtils::ReadLengthPrefixed(bitStream, title)); + VALIDATE_READ(BitStreamUtils::ReadNameValueText(bitStream, titleParams)); + return true; + } +} diff --git a/dGame/dGameMessages/ZoneMessages.h b/dGame/dGameMessages/ZoneMessages.h new file mode 100644 index 000000000..0597e1ff7 --- /dev/null +++ b/dGame/dGameMessages/ZoneMessages.h @@ -0,0 +1,112 @@ +#ifndef ZONEMESSAGES_H +#define ZONEMESSAGES_H + +#include "GameMessages.h" +#include "eObjectWorldState.h" + +#include + +// Game messages for a player's zone lifecycle: loading in, zone summaries, level ups, announcements. +// Fields are listed in wire order; names follow the client (legouniverse.exe 1.10.64) where known. +namespace GameMessages { + // Client -> server. The client finished loading the zone and its player. + struct PlayerLoaded : public NetGameMsg { + PlayerLoaded() : NetGameMsg(MessageType::Game::PLAYER_LOADED) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + void Handle(Entity& entity, const SystemAddress& sysAddr) override; + + LWOOBJID playerID{}; + }; + + // Client -> server. The client finished loading an object. Nothing to do. + struct ReadyForUpdates : public NetGameMsg { + ReadyForUpdates() : NetGameMsg(MessageType::Game::READY_FOR_UPDATES) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + LWOOBJID objectID{}; + }; + + // Server -> client, to one client. No payload. + struct PlayerReady : public NetGameMsg { + PlayerReady() : NetGameMsg(MessageType::Game::PLAYER_READY) {} + }; + + // Server -> client, to one client. No payload. + struct RestoreToPostLoadStats : public NetGameMsg { + RestoreToPostLoadStats() : NetGameMsg(MessageType::Game::RESTORE_TO_POST_LOAD_STATS) {} + }; + + // Server -> client, to one client. No payload. + struct ServerDoneLoadingAllObjects : public NetGameMsg { + ServerDoneLoadingAllObjects() : NetGameMsg(MessageType::Game::SERVER_DONE_LOADING_ALL_OBJECTS) {} + }; + + // Server -> client, to one client. + struct InvalidZoneTransferList : public NetGameMsg { + InvalidZoneTransferList() : NetGameMsg(MessageType::Game::INVALID_ZONE_TRANSFER_LIST) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + std::u16string customerFeedbackURL{}; + std::u16string invalidMapTransferList{}; + bool bCustomerFeedbackOnExit{}; + bool bCustomerFeedbackOnInvalidMapTransfer{}; + }; + + // Server -> client, to one client (UNASSIGNED broadcasts). + struct DisplayZoneSummary : public NetGameMsg { + DisplayZoneSummary() : NetGameMsg(MessageType::Game::DISPLAY_ZONE_SUMMARY) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + bool isPropertyMap{ false }; + bool isZoneStart{ false }; + LWOOBJID sender{ LWOOBJID_EMPTY }; // optional + }; + + // Client -> server. + struct ZoneSummaryDismissed : public NetGameMsg { + ZoneSummaryDismissed() : NetGameMsg(MessageType::Game::ZONE_SUMMARY_DISMISSED) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + void Handle(Entity& entity, const SystemAddress& sysAddr) override; + + LWOOBJID playerID{}; + }; + + // Client -> server. No payload. The client finished showing a level up. + struct NotifyServerLevelProcessingComplete : public NetGameMsg { + NotifyServerLevelProcessingComplete() : NetGameMsg(MessageType::Game::NOTIFY_SERVER_LEVEL_PROCESSING_COMPLETE) {} + void Handle(Entity& entity, const SystemAddress& sysAddr) override; + }; + + // Server -> client, to one client (UNASSIGNED broadcasts). + struct ChangeObjectWorldState : public NetGameMsg { + ChangeObjectWorldState() : NetGameMsg(MessageType::Game::CHANGE_OBJECT_WORLD_STATE) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + eObjectWorldState newState{}; + }; + + // Server -> client, to one client. The client looks both strings up in its locale (falling back to the text + // itself) and shows the announcement popup. Client Deserialize: 0x00f23c50. + struct LocalizedAnnouncementServerToSingleClient : public NetGameMsg { + LocalizedAnnouncementServerToSingleClient() : NetGameMsg(MessageType::Game::LOCALIZED_ANNOUNCEMENT_SERVER_TO_SINGLE_CLIENT) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + // Name-value (LDF) text: u32 character count, then that many characters plus a null terminator when not empty. + std::u16string bodyParams{}; + bool bForceOpenChatBox{ false }; + bool bShowAnnouncement{ true }; + bool bShowInChat{ true }; + std::u16string body{}; + std::u16string title{}; + std::u16string titleParams{}; // name-value (LDF) text, as bodyParams + }; +} + +#endif // ZONEMESSAGES_H diff --git a/dGame/dMission/Mission.cpp b/dGame/dMission/Mission.cpp index 69c6593f6..8ac8a4adf 100644 --- a/dGame/dMission/Mission.cpp +++ b/dGame/dMission/Mission.cpp @@ -12,6 +12,7 @@ #include "Game.h" #include "GameMessages.h" #include "MissionMessages.h" +#include "PlayerMessages.h" #include "Mail.h" #include "MissionComponent.h" #include "eRacingTaskParam.h" @@ -483,7 +484,11 @@ void Mission::YieldRewards() { } else { const auto uscore = LiveEvents::ScaleUScore(info.LegoScore); // a live event's U-score bonus, if one is running here characterComponent->SetUScore(characterComponent->GetUScore() + uscore, static_cast(lootSource)); - GameMessages::SendModifyLEGOScore(entity, entity->GetSystemAddress(), uscore, lootSource); + GameMessages::ModifyLEGOScore modifyScore; + modifyScore.target = entity->GetObjectID(); + modifyScore.score = uscore; + modifyScore.sourceType = lootSource; + modifyScore.SendToClient(entity->GetSystemAddress()); } } @@ -493,7 +498,10 @@ void Mission::YieldRewards() { auto* const character = entity->GetComponent(); if (character) { character->SetReputation(character->GetReputation() + info.reward_reputation); - GameMessages::SendUpdateReputation(entity->GetObjectID(), character->GetReputation(), entity->GetSystemAddress()); + GameMessages::UpdateReputation updateReputation; + updateReputation.target = entity->GetObjectID(); + updateReputation.reputation = character->GetReputation(); + updateReputation.SendToClient(entity->GetSystemAddress()); } } diff --git a/dGame/dPropertyBehaviors/PropertyBehavior.cpp b/dGame/dPropertyBehaviors/PropertyBehavior.cpp index c19c4d2fe..8153c2f06 100644 --- a/dGame/dPropertyBehaviors/PropertyBehavior.cpp +++ b/dGame/dPropertyBehaviors/PropertyBehavior.cpp @@ -8,6 +8,7 @@ #include "StringifiedEnum.h" #include +#include "ObjectMessages.h" PropertyBehavior::PropertyBehavior(bool _isTemplated) { m_LastEditedState = BehaviorState::HOME_STATE; diff --git a/dGame/dPropertyBehaviors/State.cpp b/dGame/dPropertyBehaviors/State.cpp index 82d7ae0ba..acb6fa3d5 100644 --- a/dGame/dPropertyBehaviors/State.cpp +++ b/dGame/dPropertyBehaviors/State.cpp @@ -3,6 +3,7 @@ #include "Amf3.h" #include "ControlBehaviorMsgs.h" #include "tinyxml2.h" +#include "ObjectMessages.h" template <> void State::HandleMsg(AddStripMessage& msg) { diff --git a/dGame/dPropertyBehaviors/Strip.cpp b/dGame/dPropertyBehaviors/Strip.cpp index da1958b02..8d94c6c5e 100644 --- a/dGame/dPropertyBehaviors/Strip.cpp +++ b/dGame/dPropertyBehaviors/Strip.cpp @@ -16,6 +16,9 @@ #include "DluAssert.h" #include "Loot.h" #include "PropertyBehaviorActions.h" +#include "ObjectMessages.h" +#include "InventoryMessages.h" +#include "PropertyMessages.h" template <> void Strip::HandleMsg(AddStripMessage& msg) { diff --git a/dGame/dUtilities/DashboardActions.cpp b/dGame/dUtilities/DashboardActions.cpp index 27f9e29b7..408c407e3 100644 --- a/dGame/dUtilities/DashboardActions.cpp +++ b/dGame/dUtilities/DashboardActions.cpp @@ -12,6 +12,8 @@ #include "ChatPackets.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "MovementMessages.h" +#include "PlayerMessages.h" #include "PlayerManager.h" #include "User.h" #include "UserManager.h" @@ -66,7 +68,10 @@ namespace { response.previousLevel = entity->GetGMLevel(); response.newLevel = info->maxGmLevel; response.Send(entity->GetSystemAddress()); - GameMessages::SendChatModeUpdate(entity->GetObjectID(), info->maxGmLevel); + GameMessages::UpdateChatMode chatMode; + chatMode.target = entity->GetObjectID(); + chatMode.level = info->maxGmLevel; + chatMode.Send(UNASSIGNED_SYSTEM_ADDRESS); entity->SetGMLevel(info->maxGmLevel); if (info->maxGmLevel == eGameMasterLevel::CIVILIAN) { GameMessages::ToggleGMInvisEvent msg; @@ -115,7 +120,7 @@ namespace { auto* target = PlayerManager::GetPlayer(targetId); if (!entity || !target || entity == target) return 0; const auto position = target->GetPosition(); - GameMessages::SendTeleport(entity->GetObjectID(), position, entity->GetRotation(), entity->GetSystemAddress()); + GameMessages::Teleport(entity->GetObjectID(), position, entity->GetRotation()).SendToClient(entity->GetSystemAddress()); ChatPackets::SendSystemMessage(entity->GetSystemAddress(), u"A moderator moved you to " + GeneralUtils::ASCIIToUTF16(target->GetCharacter() ? target->GetCharacter()->GetName() : "another player") + u"."); LOG("Dashboard teleport of %llu to %llu", characterId, targetId); return 1; diff --git a/dGame/dUtilities/Loot.cpp b/dGame/dUtilities/Loot.cpp index 08c8436ff..b9a421b84 100644 --- a/dGame/dUtilities/Loot.cpp +++ b/dGame/dUtilities/Loot.cpp @@ -25,6 +25,7 @@ #include "ObjectIDManager.h" #include "CDActivitiesTable.h" #include "ScriptedActivityComponent.h" +#include "InventoryMessages.h" namespace { std::unordered_set CachedMatrices; diff --git a/dGame/dUtilities/Preconditions.cpp b/dGame/dUtilities/Preconditions.cpp index 26f1f7bd8..d050f8aac 100644 --- a/dGame/dUtilities/Preconditions.cpp +++ b/dGame/dUtilities/Preconditions.cpp @@ -12,6 +12,7 @@ #include "LevelProgressionComponent.h" #include "DestroyableComponent.h" #include "GameMessages.h" +#include "ObjectMessages.h" #include "eMissionState.h" #include "PetComponent.h" @@ -292,7 +293,11 @@ bool PreconditionExpression::Check(Entity* player, bool evaluateCosts) const { const auto a = Preconditions::Check(player, condition, evaluateCosts); if (!a) { - GameMessages::SendNotifyClientFailedPrecondition(player->GetObjectID(), player->GetSystemAddress(), u"", condition); + GameMessages::NotifyClientFailedPrecondition failedPrecondition; + failedPrecondition.target = player->GetObjectID(); + failedPrecondition.failedReason = u""; + failedPrecondition.preconditionID = condition; + failedPrecondition.Send(player->GetSystemAddress()); } const auto b = next == nullptr ? true : next->Check(player, evaluateCosts); diff --git a/dGame/dUtilities/SlashCommands/DEVGMCommands.cpp b/dGame/dUtilities/SlashCommands/DEVGMCommands.cpp index 5a1b67049..1416a8b8e 100644 --- a/dGame/dUtilities/SlashCommands/DEVGMCommands.cpp +++ b/dGame/dUtilities/SlashCommands/DEVGMCommands.cpp @@ -58,6 +58,9 @@ #include "StringifiedEnum.h" #include "BinaryPathFinder.h" #include "EffectsMessages.h" +#include "MovementMessages.h" +#include "PlayerMessages.h" +#include "ObjectMessages.h" namespace DEVGMCommands { void SetGMLevel(Entity* entity, const SystemAddress& sysAddr, const std::string args) { @@ -88,7 +91,10 @@ namespace DEVGMCommands { response.previousLevel = entity->GetGMLevel(); response.newLevel = level; response.Send(entity->GetSystemAddress()); - GameMessages::SendChatModeUpdate(entity->GetObjectID(), level); + GameMessages::UpdateChatMode chatMode; + chatMode.target = entity->GetObjectID(); + chatMode.level = level; + chatMode.Send(UNASSIGNED_SYSTEM_ADDRESS); 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()); } @@ -101,7 +107,10 @@ namespace DEVGMCommands { response.previousLevel = entity->GetGMLevel(); response.newLevel = eGameMasterLevel::CIVILIAN; response.Send(entity->GetSystemAddress()); - GameMessages::SendChatModeUpdate(entity->GetObjectID(), eGameMasterLevel::CIVILIAN); + GameMessages::UpdateChatMode chatMode; + chatMode.target = entity->GetObjectID(); + chatMode.level = eGameMasterLevel::CIVILIAN; + chatMode.Send(UNASSIGNED_SYSTEM_ADDRESS); entity->SetGMLevel(eGameMasterLevel::CIVILIAN); GameMessages::ToggleGMInvisEvent msg; @@ -299,7 +308,10 @@ namespace DEVGMCommands { const auto state = !entity->GetVar(u"freecam"); entity->SetVar(u"freecam", state); - GameMessages::SendSetPlayerControlScheme(entity, static_cast(state ? 9 : 1)); + GameMessages::SetPlayerControlScheme controlScheme; + controlScheme.target = entity->GetObjectID(); + controlScheme.iScheme = static_cast(state ? 9 : 1); + controlScheme.SendToClient(entity->GetSystemAddress()); ChatPackets::SendSystemMessage(sysAddr, u"Toggled freecam."); } @@ -315,7 +327,10 @@ namespace DEVGMCommands { return; } - GameMessages::SendSetPlayerControlScheme(entity, static_cast(scheme.value())); + GameMessages::SetPlayerControlScheme controlScheme; + controlScheme.target = entity->GetObjectID(); + controlScheme.iScheme = static_cast(scheme.value()); + controlScheme.SendToClient(entity->GetSystemAddress()); ChatPackets::SendSystemMessage(sysAddr, u"Switched control scheme."); } @@ -674,7 +689,7 @@ namespace DEVGMCommands { } else { ChatPackets::SendSystemMessage(sysAddr, u"Correct usage: /teleport () - if no Y given, will teleport to the height of the terrain (or any physics object)."); } - GameMessages::SendTeleport(sourceEntity->GetObjectID(), pos, sourceEntity->GetRotation(), sourceEntity->GetSystemAddress()); + GameMessages::Teleport(sourceEntity->GetObjectID(), pos, sourceEntity->GetRotation()).SendToClient(sourceEntity->GetSystemAddress()); auto* possessorComponent = sourceEntity->GetComponent(); if (possessorComponent) { @@ -685,7 +700,7 @@ namespace DEVGMCommands { if (havokVehiclePhysicsComponent) { havokVehiclePhysicsComponent->SetPosition(pos); Game::entityManager->SerializeEntity(possassableEntity); - } else GameMessages::SendTeleport(possassableEntity->GetObjectID(), pos, QuatUtils::IDENTITY, sysAddr); + } else GameMessages::Teleport(possassableEntity->GetObjectID(), pos, QuatUtils::IDENTITY).SendToClient(sysAddr); } } } @@ -705,7 +720,7 @@ namespace DEVGMCommands { skipped++; continue; } - GameMessages::SendTeleport(character->GetObjectID(), pos, QuatUtils::IDENTITY, character->GetSystemAddress()); + GameMessages::Teleport(character->GetObjectID(), pos, QuatUtils::IDENTITY).SendToClient(character->GetSystemAddress()); } if (skipped > 0) { ChatPackets::SendSystemMessage(sysAddr, u"/tpall: left " + GeneralUtils::to_u16string(skipped) + @@ -934,7 +949,11 @@ namespace DEVGMCommands { lootType = type.value_or(lootType); } - GameMessages::SendModifyLEGOScore(entity, entity->GetSystemAddress(), uscore, lootType); + GameMessages::ModifyLEGOScore modifyScore; + modifyScore.target = entity->GetObjectID(); + modifyScore.score = uscore; + modifyScore.sourceType = lootType; + modifyScore.SendToClient(entity->GetSystemAddress()); } void SetLevel(Entity* entity, const SystemAddress& sysAddr, const std::string args) { diff --git a/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.cpp b/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.cpp index e2d47184c..bb35b3563 100644 --- a/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.cpp +++ b/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.cpp @@ -22,6 +22,9 @@ #include "eServerDisconnectIdentifiers.h" #include "eObjectBits.h" #include "EffectsMessages.h" +#include "MovementMessages.h" +#include "ObjectMessages.h" +#include "PlayerMessages.h" namespace GMGreaterThanZeroCommands { @@ -199,7 +202,10 @@ namespace GMGreaterThanZeroCommands { bool isFlying = character->GetIsFlying(); if (isFlying) { - GameMessages::SendSetJetPackMode(entity, false); + GameMessages::SetJetPackMode jetPackMode; + jetPackMode.target = entity->GetObjectID(); + jetPackMode.bUse = false; + jetPackMode.Send(UNASSIGNED_SYSTEM_ADDRESS); character->SetIsFlying(false); } else { @@ -219,7 +225,16 @@ namespace GMGreaterThanZeroCommands { float maxAirSpeed = 30 * speedScale; float verticalVelocity = 1.5 * speedScale; - GameMessages::SendSetJetPackMode(entity, true, true, false, 167, airSpeed, maxAirSpeed, verticalVelocity); + GameMessages::SetJetPackMode jetPackMode; + jetPackMode.target = entity->GetObjectID(); + jetPackMode.bUse = true; + jetPackMode.bBypassChecks = true; + jetPackMode.bDoHover = false; + jetPackMode.effectID = 167; + jetPackMode.fAirspeed = airSpeed; + jetPackMode.fMaxAirspeed = maxAirSpeed; + jetPackMode.fVertVel = verticalVelocity; + jetPackMode.Send(UNASSIGNED_SYSTEM_ADDRESS); character->SetIsFlying(true); } @@ -264,12 +279,18 @@ namespace GMGreaterThanZeroCommands { } void SetName(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - GameMessages::SendSetName(entity->GetObjectID(), GeneralUtils::UTF8ToUTF16(args), UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::SetName setName; + setName.target = entity->GetObjectID(); + setName.name = GeneralUtils::UTF8ToUTF16(args); + setName.Send(UNASSIGNED_SYSTEM_ADDRESS); } void Title(Entity* entity, const SystemAddress& sysAddr, const std::string args) { std::string name = entity->GetCharacter()->GetName() + " - " + args; - GameMessages::SendSetName(entity->GetObjectID(), GeneralUtils::UTF8ToUTF16(name), UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::SetName setName; + setName.target = entity->GetObjectID(); + setName.name = GeneralUtils::UTF8ToUTF16(name); + setName.Send(UNASSIGNED_SYSTEM_ADDRESS); } void ShowAll(Entity* entity, const SystemAddress& sysAddr, const std::string args) { @@ -301,7 +322,12 @@ namespace GMGreaterThanZeroCommands { void Spectate(Entity* entity, const SystemAddress& sysAddr, const std::string args) { if (args.empty()) { - GameMessages::SendForceCameraTargetCycle(entity, false, eCameraTargetCyclingMode::DISALLOW_CYCLING, entity->GetObjectID()); + GameMessages::ForceCameraTargetCycle cameraCycle; + cameraCycle.target = entity->GetObjectID(); + cameraCycle.bForceCycling = false; + cameraCycle.cyclingMode = eCameraTargetCyclingMode::DISALLOW_CYCLING; + cameraCycle.optionalTargetID = entity->GetObjectID(); + cameraCycle.SendToClient(entity->GetSystemAddress()); return; } @@ -311,6 +337,11 @@ namespace GMGreaterThanZeroCommands { return; } GameMessages::SlashCommandTextFeedback(entity->GetObjectID(), u"Spectating Player").SendToClient(entity->GetSystemAddress()); - GameMessages::SendForceCameraTargetCycle(entity, false, eCameraTargetCyclingMode::DISALLOW_CYCLING, player->GetObjectID()); + GameMessages::ForceCameraTargetCycle cameraCycle; + cameraCycle.target = entity->GetObjectID(); + cameraCycle.bForceCycling = false; + cameraCycle.cyclingMode = eCameraTargetCyclingMode::DISALLOW_CYCLING; + cameraCycle.optionalTargetID = player->GetObjectID(); + cameraCycle.SendToClient(entity->GetSystemAddress()); } } diff --git a/dGame/dUtilities/VanityUtilities.cpp b/dGame/dUtilities/VanityUtilities.cpp index 521e68da4..6cc32940f 100644 --- a/dGame/dUtilities/VanityUtilities.cpp +++ b/dGame/dUtilities/VanityUtilities.cpp @@ -3,6 +3,7 @@ #include "DestroyableComponent.h" #include "EntityManager.h" #include "GameMessages.h" +#include "ObjectMessages.h" #include "InventoryComponent.h" #include "PhantomPhysicsComponent.h" #include "ProximityMonitorComponent.h" @@ -349,8 +350,7 @@ void NPCTalk(Entity* npc) { const auto& selected = chats[GeneralUtils::GenerateRandomNumber(0, static_cast(chats.size() - 1))]; - GameMessages::SendNotifyClientZoneObject( - npc->GetObjectID(), u"sendToclient_bubble", 0, 0, npc->GetObjectID(), selected, UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientZoneObject(npc->GetObjectID(), u"sendToclient_bubble", 0, 0, npc->GetObjectID(), selected).Send(UNASSIGNED_SYSTEM_ADDRESS); Game::entityManager->SerializeEntity(npc); diff --git a/dGame/dUtilities/WorldMigration.cpp b/dGame/dUtilities/WorldMigration.cpp index 0bd19b828..b9c80af08 100644 --- a/dGame/dUtilities/WorldMigration.cpp +++ b/dGame/dUtilities/WorldMigration.cpp @@ -1,4 +1,5 @@ #include "WorldMigration.h" +#include "ZoneMessages.h" #include "MasterPackets.h" #include @@ -82,27 +83,14 @@ namespace { if (g_Active) SendStatus(g_Active->order.migrationId, g_Active->order.targetInstance, state, g_Active->moved, g_Active->failed, remaining, message); } - void WriteWString(RakNet::BitStream& bitStream, const std::u16string& text) { - bitStream.Write(text.size()); - for (const auto character : text) bitStream.Write(character); - } - // LocalizedAnnouncementServerToSingleClient: the client looks both strings up in its locale (falling back to // the text itself) and shows the announcement popup, like its own instance-lock warning did in live void SendLocalizedAnnouncement(Entity* player, const std::u16string& body, const std::u16string& title) { - const auto& sysAddr = player->GetSystemAddress(); - CBITSTREAM; - CMSGHEADER; - bitStream.Write(player->GetObjectID()); - bitStream.Write(MessageType::Game::LOCALIZED_ANNOUNCEMENT_SERVER_TO_SINGLE_CLIENT); - bitStream.Write(0); // body parameters (LDF), none - bitStream.Write(false); // force open chat box - bitStream.Write(true); // show the announcement - bitStream.Write(true); // and put the text in chat - WriteWString(bitStream, body); - WriteWString(bitStream, title); - bitStream.Write(0); // title parameters (LDF), none - SEND_PACKET; + GameMessages::LocalizedAnnouncementServerToSingleClient announcement; + announcement.target = player->GetObjectID(); + announcement.body = body; + announcement.title = title; + announcement.SendToClient(player->GetSystemAddress()); } uint16_t Remaining() { diff --git a/dNet/BitStreamUtils.h b/dNet/BitStreamUtils.h index a1e62b782..fa4afa219 100644 --- a/dNet/BitStreamUtils.h +++ b/dNet/BitStreamUtils.h @@ -75,6 +75,7 @@ struct LUBitStream { }; +#define BITSTREAMUTILS_HAS_WRITEHEADER namespace BitStreamUtils { template void WriteHeader(RakNet::BitStream& bitStream, ServiceType connectionType, T internalPacketID) { @@ -124,6 +125,20 @@ namespace BitStreamUtils { * Reads a string written by WriteLengthPrefixed. Fails (returns false) if the stream runs out of data or * if the length is negative or larger than maxLength characters. */ + /** + * Writes name-value (LDF) text the way the client reads it: a u32 character count, the characters, and a null + * terminator (not counted) when the text isn't empty. + */ + inline void WriteNameValueText(RakNet::BitStream& bitStream, const std::u16string& text) { + WriteLengthPrefixed(bitStream, text); + if (!text.empty()) bitStream.Write(0); + } + + /** + * Reads text written by WriteNameValueText (the null terminator is consumed and not kept). + */ + inline bool ReadNameValueText(RakNet::BitStream& bitStream, std::u16string& text, const uint32_t maxLength = 0x500000 /* MAX_MESSAGE_LENGTH */); + template bool ReadLengthPrefixed(RakNet::BitStream& bitStream, StringT& value, const uint32_t maxLength = 0x500000 /* MAX_MESSAGE_LENGTH */) { LenT length{}; @@ -136,6 +151,12 @@ namespace BitStreamUtils { if (length == 0) return true; return bitStream.ReadBits(reinterpret_cast(value.data()), BYTES_TO_BITS(value.size() * sizeof(typename StringT::value_type)), true); } + + inline bool ReadNameValueText(RakNet::BitStream& bitStream, std::u16string& text, const uint32_t maxLength) { + if (!ReadLengthPrefixed(bitStream, text, maxLength)) return false; + uint16_t terminator{}; + return text.empty() || bitStream.Read(terminator); + } } namespace RakNet { diff --git a/dNet/PacketUtils.cpp b/dNet/PacketUtils.cpp deleted file mode 100644 index 5394cff08..000000000 --- a/dNet/PacketUtils.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "PacketUtils.h" -#include -#include "Logger.h" -#include "Game.h" - -//! Saves a packet to the filesystem -void PacketUtils::SavePacket(const std::string& filename, const char* data, size_t length) { - //If we don't log to the console, don't save the bin files either. This takes up a lot of time. - if (!Game::logger->GetLogToConsole()) return; - - std::string path = "packets/" + filename; - - std::ofstream file(path, std::ios::binary); - if (!file.is_open()) return; - - file.write(data, length); - file.close(); -} diff --git a/dNet/PacketUtils.h b/dNet/PacketUtils.h deleted file mode 100644 index cf96bfc04..000000000 --- a/dNet/PacketUtils.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef PACKETUTILS_H -#define PACKETUTILS_H - -#include "MessageIdentifiers.h" -#include "BitStream.h" -#include - -enum class ServiceType : uint16_t; - -namespace PacketUtils { - void SavePacket(const std::string& filename, const char* data, size_t length); -}; - -#endif // PACKETUTILS_H diff --git a/dScripts/02_server/Enemy/AG/BossSpiderQueenEnemyServer.cpp b/dScripts/02_server/Enemy/AG/BossSpiderQueenEnemyServer.cpp index 188098e80..879ae3c48 100644 --- a/dScripts/02_server/Enemy/AG/BossSpiderQueenEnemyServer.cpp +++ b/dScripts/02_server/Enemy/AG/BossSpiderQueenEnemyServer.cpp @@ -13,6 +13,7 @@ #include "GameMessages.h" #include "EffectsMessages.h" +#include "ObjectMessages.h" #include "SkillComponent.h" #include "eReplicaComponentType.h" #include "RenderComponent.h" @@ -88,7 +89,7 @@ void BossSpiderQueenEnemyServer::OnDie(Entity* self, Entity* killer) { // There is suppose to be a 0.1 second delay here but that may be admitted? auto* controller = Game::entityManager->GetZoneControlEntity(); - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"SetColGroup", 10, 0, 0, "", UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(self->GetObjectID(), u"SetColGroup", 10, 0, 0, "").Send(UNASSIGNED_SYSTEM_ADDRESS); self->SetPosition({ 10000, 0, 10000 }); @@ -108,7 +109,7 @@ void BossSpiderQueenEnemyServer::WithdrawSpider(Entity* self, const bool withdra if (withdraw) { //Move spider away from battle zone // Disabled because we cant option the reset collition group right now - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"SetColGroup", 10, 0, 0, "", UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(self->GetObjectID(), u"SetColGroup", 10, 0, 0, "").Send(UNASSIGNED_SYSTEM_ADDRESS); //First rotate for anim NiQuaternion rot = QuatUtils::IDENTITY; @@ -536,7 +537,7 @@ void BossSpiderQueenEnemyServer::OnTimerDone(Entity* self, const std::string tim embeddedEffect.Send(UNASSIGNED_SYSTEM_ADDRESS); } else if (timerName == "AdvanceComplete") { - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"SetColGroup", 11, 0, 0, "", UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(self->GetObjectID(), u"SetColGroup", 11, 0, 0, "").Send(UNASSIGNED_SYSTEM_ADDRESS); //Wind up, telegraphing next round float animTime = PlayAnimAndReturnTime(self, spiderJeerAnim); diff --git a/dScripts/02_server/Enemy/AM/AmDarklingDragon.cpp b/dScripts/02_server/Enemy/AM/AmDarklingDragon.cpp index eb384ec27..58fd7832b 100644 --- a/dScripts/02_server/Enemy/AM/AmDarklingDragon.cpp +++ b/dScripts/02_server/Enemy/AM/AmDarklingDragon.cpp @@ -4,6 +4,7 @@ #include "EntityManager.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "ObjectMessages.h" #include "SkillComponent.h" #include "BaseCombatAIComponent.h" #include "EntityInfo.h" @@ -145,7 +146,7 @@ void AmDarklingDragon::OnTimerDone(Entity* self, std::string timerName) { idleFlags.flagsOff = eAnimationFlags::IDLE_NONE; idleFlags.Send(UNASSIGNED_SYSTEM_ADDRESS); self->SetVar(u"weakspot", -1); - GameMessages::SendNotifyObject(self->GetObjectID(), self->GetObjectID(), u"DragonRevive", UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyObject(self->GetObjectID(), self->GetObjectID(), u"DragonRevive").Send(UNASSIGNED_SYSTEM_ADDRESS); } } diff --git a/dScripts/02_server/Enemy/FV/FvMaelstromDragon.cpp b/dScripts/02_server/Enemy/FV/FvMaelstromDragon.cpp index 88eb57a9c..c09f21336 100644 --- a/dScripts/02_server/Enemy/FV/FvMaelstromDragon.cpp +++ b/dScripts/02_server/Enemy/FV/FvMaelstromDragon.cpp @@ -7,6 +7,7 @@ #include "EntityInfo.h" #include "RenderComponent.h" #include "EffectsMessages.h" +#include "ObjectMessages.h" void FvMaelstromDragon::OnStartup(Entity* self) { self->SetVar(u"weakspot", 0); @@ -164,7 +165,7 @@ void FvMaelstromDragon::OnTimerDone(Entity* self, std::string timerName) { idleFlags.Send(UNASSIGNED_SYSTEM_ADDRESS); self->SetVar(u"weakspot", -1); - GameMessages::SendNotifyObject(self->GetObjectID(), self->GetObjectID(), u"DragonRevive", UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyObject(self->GetObjectID(), self->GetObjectID(), u"DragonRevive").Send(UNASSIGNED_SYSTEM_ADDRESS); } } diff --git a/dScripts/02_server/Map/AG/AgCagedBricksServer.cpp b/dScripts/02_server/Map/AG/AgCagedBricksServer.cpp index dd51f5b46..07703cb7d 100644 --- a/dScripts/02_server/Map/AG/AgCagedBricksServer.cpp +++ b/dScripts/02_server/Map/AG/AgCagedBricksServer.cpp @@ -1,6 +1,7 @@ #include "AgCagedBricksServer.h" #include "InventoryComponent.h" #include "GameMessages.h" +#include "ObjectMessages.h" #include "Character.h" #include "EntityManager.h" #include "eReplicaComponentType.h" @@ -10,7 +11,7 @@ void AgCagedBricksServer::OnUse(Entity* self, Entity* user) { //Tell the client to spawn the baby spiderling: auto spooders = Game::entityManager->GetEntitiesInGroup("cagedSpider"); for (auto spodder : spooders) { - GameMessages::SendFireEventClientSide(spodder->GetObjectID(), user->GetSystemAddress(), u"toggle", LWOOBJID_EMPTY, 0, 0, user->GetObjectID()); + GameMessages::FireEventClientSide(spodder->GetObjectID(), u"toggle", LWOOBJID_EMPTY, user->GetObjectID()).SendToClient(user->GetSystemAddress()); } //Set the flag & mission status: diff --git a/dScripts/02_server/Map/AG/NpcAgCourseStarter.cpp b/dScripts/02_server/Map/AG/NpcAgCourseStarter.cpp index cd30b58fb..dd9b27a99 100644 --- a/dScripts/02_server/Map/AG/NpcAgCourseStarter.cpp +++ b/dScripts/02_server/Map/AG/NpcAgCourseStarter.cpp @@ -3,6 +3,7 @@ #include "ScriptedActivityComponent.h" #include "GameMessages.h" #include "ActivityMessages.h" +#include "ObjectMessages.h" #include "LeaderboardManager.h" #include "dServer.h" #include "eMissionTaskType.h" @@ -21,9 +22,9 @@ void NpcAgCourseStarter::OnUse(Entity* self, Entity* user) { const auto& userSysAddr = user->GetSystemAddress(); if (scriptedActivityComponent->PlayerHasActivityData(userId)) { - GameMessages::SendNotifyClientObject(selfId, u"exit", 0, 0, LWOOBJID_EMPTY, "", userSysAddr); + GameMessages::NotifyClientObject(selfId, u"exit", 0, 0, LWOOBJID_EMPTY, "").Send(userSysAddr); } else { - GameMessages::SendNotifyClientObject(selfId, u"start", 0, 0, LWOOBJID_EMPTY, "", userSysAddr); + GameMessages::NotifyClientObject(selfId, u"start", 0, 0, LWOOBJID_EMPTY, "").Send(userSysAddr); } } @@ -36,14 +37,14 @@ void NpcAgCourseStarter::OnMessageBoxResponse(Entity* self, Entity* sender, int3 const auto& senderSysAddr = sender->GetSystemAddress(); if (identifier == u"player_dialog_cancel_course" && button == 1) { - GameMessages::SendNotifyClientObject(selfId, u"stop_timer", 0, 0, LWOOBJID_EMPTY, "", senderSysAddr); - GameMessages::SendNotifyClientObject(selfId, u"cancel_timer", 0, 0, LWOOBJID_EMPTY, "", senderSysAddr); + GameMessages::NotifyClientObject(selfId, u"stop_timer", 0, 0, LWOOBJID_EMPTY, "").Send(senderSysAddr); + GameMessages::NotifyClientObject(selfId, u"cancel_timer", 0, 0, LWOOBJID_EMPTY, "").Send(senderSysAddr); scriptedActivityComponent->RemoveActivityPlayerData(senderId); Game::entityManager->SerializeEntity(self); } else if (identifier == u"player_dialog_start_course" && button == 1) { - GameMessages::SendNotifyClientObject(selfId, u"start_timer", 0, 0, LWOOBJID_EMPTY, "", senderSysAddr); + GameMessages::NotifyClientObject(selfId, u"start_timer", 0, 0, LWOOBJID_EMPTY, "").Send(senderSysAddr); GameMessages::ActivityStart activityStart; activityStart.target = selfId; activityStart.Send(senderSysAddr); @@ -57,12 +58,12 @@ void NpcAgCourseStarter::OnMessageBoxResponse(Entity* self, Entity* sender, int3 Game::entityManager->SerializeEntity(self); } else if (identifier == u"FootRaceCancel") { - GameMessages::SendNotifyClientObject(selfId, u"stop_timer", 0, 0, LWOOBJID_EMPTY, "", senderSysAddr); + GameMessages::NotifyClientObject(selfId, u"stop_timer", 0, 0, LWOOBJID_EMPTY, "").Send(senderSysAddr); if (scriptedActivityComponent->PlayerHasActivityData(senderId)) { - GameMessages::SendNotifyClientObject(selfId, u"exit", 0, 0, LWOOBJID_EMPTY, "", senderSysAddr); + GameMessages::NotifyClientObject(selfId, u"exit", 0, 0, LWOOBJID_EMPTY, "").Send(senderSysAddr); } else { - GameMessages::SendNotifyClientObject(selfId, u"start", 0, 0, LWOOBJID_EMPTY, "", senderSysAddr); + GameMessages::NotifyClientObject(selfId, u"start", 0, 0, LWOOBJID_EMPTY, "").Send(senderSysAddr); } scriptedActivityComponent->RemoveActivityPlayerData(senderId); @@ -80,8 +81,7 @@ void NpcAgCourseStarter::OnFireEventServerSide(Entity* self, Entity* sender, std if (!scriptedActivityComponent->PlayerHasActivityData(senderId)) return; if (args == "course_cancel") { - GameMessages::SendNotifyClientObject(selfId, u"cancel_timer", 0, 0, - LWOOBJID_EMPTY, "", senderSysAddr); + GameMessages::NotifyClientObject(selfId, u"cancel_timer", 0, 0, LWOOBJID_EMPTY, "").Send(senderSysAddr); scriptedActivityComponent->RemoveActivityPlayerData(senderId); } else if (args == "course_finish") { const auto raceEndTime = Game::server->GetUptime(); @@ -102,11 +102,8 @@ void NpcAgCourseStarter::OnFireEventServerSide(Entity* self, Entity* sender, std Game::entityManager->SerializeEntity(self); LeaderboardManager::SaveScore(senderId, scriptedActivityComponent->GetActivityID(), raceTimeElapsed); - GameMessages::SendNotifyClientObject(selfId, u"ToggleLeaderBoard", - scriptedActivityComponent->GetActivityID(), 0, senderId, - "", senderSysAddr); - GameMessages::SendNotifyClientObject(selfId, u"stop_timer", 1, raceTimeElapsed, LWOOBJID_EMPTY, "", - senderSysAddr); + GameMessages::NotifyClientObject(selfId, u"ToggleLeaderBoard", scriptedActivityComponent->GetActivityID(), 0, senderId, "").Send(senderSysAddr); + GameMessages::NotifyClientObject(selfId, u"stop_timer", 1, raceTimeElapsed, LWOOBJID_EMPTY, "").Send(senderSysAddr); scriptedActivityComponent->RemoveActivityPlayerData(senderId); } diff --git a/dScripts/02_server/Map/AG/NpcCowboyServer.cpp b/dScripts/02_server/Map/AG/NpcCowboyServer.cpp index 219110b76..32d0104c2 100644 --- a/dScripts/02_server/Map/AG/NpcCowboyServer.cpp +++ b/dScripts/02_server/Map/AG/NpcCowboyServer.cpp @@ -2,6 +2,7 @@ #include "eMissionState.h" #include "InventoryComponent.h" #include "dZoneManager.h" +#include "ObjectMessages.h" void NpcCowboyServer::OnMissionDialogueOK(Entity* self, Entity* target, int missionID, eMissionState missionState) { if (missionID != 1880) { @@ -34,7 +35,6 @@ void NpcCowboyServer::OnMissionDialogueOK(Entity* self, Entity* target, int miss auto spawners = Game::zoneManager->GetSpawnersByName("PlungerGunTargets"); for (auto* spawner : spawners) { for (const auto entity : spawner->GetSpawnedObjectIDs()) - GameMessages::SendNotifyClientObject(entity, u"SetVisibility", visible, 0, - target->GetObjectID(), "", target->GetSystemAddress()); + GameMessages::NotifyClientObject(entity, u"SetVisibility", visible, 0, target->GetObjectID(), "").Send(target->GetSystemAddress()); } } diff --git a/dScripts/02_server/Map/AG/NpcNjAssistantServer.cpp b/dScripts/02_server/Map/AG/NpcNjAssistantServer.cpp index 8ee2e9889..54a3952ba 100644 --- a/dScripts/02_server/Map/AG/NpcNjAssistantServer.cpp +++ b/dScripts/02_server/Map/AG/NpcNjAssistantServer.cpp @@ -1,5 +1,6 @@ #include "NpcNjAssistantServer.h" #include "GameMessages.h" +#include "ObjectMessages.h" #include "InventoryComponent.h" #include "MissionComponent.h" #include "Item.h" @@ -10,7 +11,7 @@ void NpcNjAssistantServer::OnMissionDialogueOK(Entity* self, Entity* target, int if (missionID != mailMission) return; if (missionState == eMissionState::COMPLETE || missionState == eMissionState::READY_TO_COMPLETE) { - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"switch", 0, 0, LWOOBJID_EMPTY, "", target->GetSystemAddress()); + GameMessages::NotifyClientObject(self->GetObjectID(), u"switch", 0, 0, LWOOBJID_EMPTY, "").Send(target->GetSystemAddress()); auto* inv = static_cast(target->GetComponent(eReplicaComponentType::INVENTORY)); diff --git a/dScripts/02_server/Map/AG/NpcWispServer.cpp b/dScripts/02_server/Map/AG/NpcWispServer.cpp index 0a6c9fda9..0e62a8ea9 100644 --- a/dScripts/02_server/Map/AG/NpcWispServer.cpp +++ b/dScripts/02_server/Map/AG/NpcWispServer.cpp @@ -3,6 +3,7 @@ #include "dZoneManager.h" #include "Entity.h" #include "GameMessages.h" +#include "ObjectMessages.h" #include "eMissionState.h" #include "Spawner.h" @@ -39,8 +40,7 @@ void NpcWispServer::OnMissionDialogueOK(Entity* self, Entity* target, int missio auto spawners = Game::zoneManager->GetSpawnersByName(group); for (const auto* spawner : spawners) { for (const auto objId : spawner->GetSpawnedObjectIDs()) - GameMessages::SendNotifyClientObject(objId, u"SetVisibility", visible, 0, - target->GetObjectID(), "", target->GetSystemAddress()); + GameMessages::NotifyClientObject(objId, u"SetVisibility", visible, 0, target->GetObjectID(), "").Send(target->GetSystemAddress()); } } } diff --git a/dScripts/02_server/Map/AG_Spider_Queen/ZoneAgSpiderQueen.cpp b/dScripts/02_server/Map/AG_Spider_Queen/ZoneAgSpiderQueen.cpp index 2b367ed2b..a0f0a0e4e 100644 --- a/dScripts/02_server/Map/AG_Spider_Queen/ZoneAgSpiderQueen.cpp +++ b/dScripts/02_server/Map/AG_Spider_Queen/ZoneAgSpiderQueen.cpp @@ -1,5 +1,6 @@ #include "ZoneAgSpiderQueen.h" #include "GameMessages.h" +#include "ObjectMessages.h" #include "EntityManager.h" #include "ZoneAgProperty.h" #include "DestroyableComponent.h" @@ -36,16 +37,14 @@ void ZoneAgSpiderQueen::BasePlayerLoaded(Entity* self, Entity* player) { } self->SetNetworkVar(u"unclaimed", true); - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"maelstromSkyOn", 0, 0, LWOOBJID_EMPTY, - "", player->GetSystemAddress()); + GameMessages::NotifyClientObject(self->GetObjectID(), u"maelstromSkyOn", 0, 0, LWOOBJID_EMPTY, "").Send(player->GetSystemAddress()); } void ZoneAgSpiderQueen::OnFireEventServerSide(Entity* self, Entity* sender, std::string args, int32_t param1, int32_t param2, int32_t param3) { if (args == "ClearProperty") { - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"PlayCinematic", 0, 0, - LWOOBJID_EMPTY, destroyedCinematic, UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(self->GetObjectID(), u"PlayCinematic", 0, 0, LWOOBJID_EMPTY, destroyedCinematic).Send(UNASSIGNED_SYSTEM_ADDRESS); self->AddTimer("tornadoOff", 0.5f); } else { ZoneAgProperty::BaseOnFireEventServerSide(self, sender, args); diff --git a/dScripts/02_server/Map/AM/AmDrawBridge.cpp b/dScripts/02_server/Map/AM/AmDrawBridge.cpp index c356145b1..0d1039c38 100644 --- a/dScripts/02_server/Map/AM/AmDrawBridge.cpp +++ b/dScripts/02_server/Map/AM/AmDrawBridge.cpp @@ -1,6 +1,7 @@ #include "AmDrawBridge.h" #include "EntityManager.h" #include "GameMessages.h" +#include "ObjectMessages.h" #include "SimplePhysicsComponent.h" #include "eTerminateType.h" @@ -26,7 +27,7 @@ void AmDrawBridge::OnUse(Entity* self, Entity* user) { auto* player = user; - GameMessages::SendTerminateInteraction(player->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID()); + GameMessages::TerminateInteraction(player->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID()).Send(UNASSIGNED_SYSTEM_ADDRESS); } void AmDrawBridge::OnTimerDone(Entity* self, std::string timerName) { diff --git a/dScripts/02_server/Map/AM/AmTeapotServer.cpp b/dScripts/02_server/Map/AM/AmTeapotServer.cpp index 66c935ce8..7b68220c1 100644 --- a/dScripts/02_server/Map/AM/AmTeapotServer.cpp +++ b/dScripts/02_server/Map/AM/AmTeapotServer.cpp @@ -1,6 +1,7 @@ #include "AmTeapotServer.h" #include "InventoryComponent.h" #include "GameMessages.h" +#include "ObjectMessages.h" #include "Item.h" #include "eTerminateType.h" @@ -14,5 +15,5 @@ void AmTeapotServer::OnUse(Entity* self, Entity* user) { inventoryComponent->AddItem(WU_S_IMAGINATION_TEA, 1); } - GameMessages::SendTerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID()); + GameMessages::TerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID()).Send(UNASSIGNED_SYSTEM_ADDRESS); } diff --git a/dScripts/02_server/Map/FV/ImgBrickConsoleQB.cpp b/dScripts/02_server/Map/FV/ImgBrickConsoleQB.cpp index 8c35de312..01029c784 100644 --- a/dScripts/02_server/Map/FV/ImgBrickConsoleQB.cpp +++ b/dScripts/02_server/Map/FV/ImgBrickConsoleQB.cpp @@ -4,6 +4,7 @@ #include "EntityManager.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "ObjectMessages.h" #include "MissionComponent.h" #include "eMissionState.h" #include "InventoryComponent.h" @@ -89,7 +90,7 @@ void ImgBrickConsoleQB::OnUse(Entity* self, Entity* user) { self->SetNetworkVar(u"used", true); - GameMessages::SendTerminateInteraction(player->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID()); + GameMessages::TerminateInteraction(player->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID()).Send(UNASSIGNED_SYSTEM_ADDRESS); } } diff --git a/dScripts/02_server/Map/GF/GfCaptainsCannon.cpp b/dScripts/02_server/Map/GF/GfCaptainsCannon.cpp index 73f5ba3d4..a8ca6b646 100644 --- a/dScripts/02_server/Map/GF/GfCaptainsCannon.cpp +++ b/dScripts/02_server/Map/GF/GfCaptainsCannon.cpp @@ -2,6 +2,8 @@ #include "GameMessages.h" #include "EffectsMessages.h" #include "CombatMessages.h" +#include "MovementMessages.h" +#include "ObjectMessages.h" #include "EntityManager.h" #include "MissionComponent.h" #include "RenderComponent.h" @@ -39,7 +41,7 @@ void GfCaptainsCannon::OnUse(Entity* self, Entity* user) { auto rotation = self->GetRotation(); - GameMessages::SendTeleport(user->GetObjectID(), position, rotation, user->GetSystemAddress()); + GameMessages::Teleport(user->GetObjectID(), position, rotation).SendToClient(user->GetSystemAddress()); RenderComponent::PlayAnimation(user, u"cannon-strike-no-equip"); @@ -107,6 +109,6 @@ void GfCaptainsCannon::OnTimerDone(Entity* self, std::string timerName) { missionComponent->ForceProgress(601, 910, 1); } - GameMessages::SendTerminateInteraction(playerId, eTerminateType::FROM_INTERACTION, self->GetObjectID()); + GameMessages::TerminateInteraction(playerId, eTerminateType::FROM_INTERACTION, self->GetObjectID()).Send(UNASSIGNED_SYSTEM_ADDRESS); } } diff --git a/dScripts/02_server/Map/GF/GfTikiTorch.cpp b/dScripts/02_server/Map/GF/GfTikiTorch.cpp index da55a77fd..a33f451bf 100644 --- a/dScripts/02_server/Map/GF/GfTikiTorch.cpp +++ b/dScripts/02_server/Map/GF/GfTikiTorch.cpp @@ -1,5 +1,6 @@ #include "GfTikiTorch.h" #include "GameMessages.h" +#include "ObjectMessages.h" #include "EntityManager.h" #include "MissionComponent.h" #include "RenderComponent.h" @@ -8,6 +9,7 @@ #include "RenderComponent.h" #include "eTerminateType.h" #include "Loot.h" +#include "InventoryMessages.h" void GfTikiTorch::OnStartup(Entity* self) { LightTorch(self); @@ -43,7 +45,7 @@ void GfTikiTorch::OnTimerDone(Entity* self, std::string timerName) { Entity* player = Game::entityManager->GetEntity(self->GetI64(u"userID")); if (player != nullptr && player->GetCharacter()) { - GameMessages::SendTerminateInteraction(player->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID()); + GameMessages::TerminateInteraction(player->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID()).Send(UNASSIGNED_SYSTEM_ADDRESS); } self->SetBoolean(u"isInUse", false); diff --git a/dScripts/02_server/Map/GF/MastTeleport.cpp b/dScripts/02_server/Map/GF/MastTeleport.cpp index 34f97e1fa..25b7824c3 100644 --- a/dScripts/02_server/Map/GF/MastTeleport.cpp +++ b/dScripts/02_server/Map/GF/MastTeleport.cpp @@ -3,6 +3,7 @@ #include "GameMessages.h" #include "EffectsMessages.h" #include "CombatMessages.h" +#include "MovementMessages.h" #include "Preconditions.h" #include "eEndBehavior.h" #include "DestroyableComponent.h" @@ -51,9 +52,9 @@ void MastTeleport::OnTimerDone(Entity* self, std::string timerName) { auto position = self->GetPosition(); auto rotation = self->GetRotation(); - GameMessages::SendTeleport(playerId, position, rotation, player->GetSystemAddress(), true); + GameMessages::Teleport(playerId, position, rotation, true).SendToClient(player->GetSystemAddress()); - GameMessages::SendTeleport(playerId, position, rotation, player->GetSystemAddress(), true); + GameMessages::Teleport(playerId, position, rotation, true).SendToClient(player->GetSystemAddress()); const auto cinematic = GeneralUtils::UTF16ToWTF8(self->GetVar(u"Cinematic")); const auto leanIn = self->GetVar(u"LeanIn"); @@ -97,9 +98,13 @@ void MastTeleport::OnTimerDone(Entity* self, std::string timerName) { position.y += 12; position.z += (forward.z * 20.5f); - GameMessages::SendOrientToAngle(playerId, true, rads, player->GetSystemAddress()); + GameMessages::OrientToAngle orientToAngle; + orientToAngle.target = playerId; + orientToAngle.bRelativeToCurrent = true; + orientToAngle.fAngle = rads; + orientToAngle.Send(player->GetSystemAddress()); - GameMessages::SendTeleport(playerId, position, QuatUtils::IDENTITY, player->GetSystemAddress()); + GameMessages::Teleport(playerId, position, QuatUtils::IDENTITY).SendToClient(player->GetSystemAddress()); GameMessages::SetStunned stun; stun.target = playerId; diff --git a/dScripts/02_server/Map/General/BankInteractServer.cpp b/dScripts/02_server/Map/General/BankInteractServer.cpp index e87185752..8567c7c2f 100644 --- a/dScripts/02_server/Map/General/BankInteractServer.cpp +++ b/dScripts/02_server/Map/General/BankInteractServer.cpp @@ -1,6 +1,7 @@ #include "BankInteractServer.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "ObjectMessages.h" #include "Entity.h" #include "Amf3.h" @@ -29,6 +30,6 @@ void BankInteractServer::OnFireEventServerSide(Entity* self, Entity* sender, std uiMessage.args = std::move(args); uiMessage.SendToClient(sender->GetSystemAddress()); - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"CloseBank", 0, 0, LWOOBJID_EMPTY, "", sender->GetSystemAddress()); + GameMessages::NotifyClientObject(self->GetObjectID(), u"CloseBank", 0, 0, LWOOBJID_EMPTY, "").Send(sender->GetSystemAddress()); } } diff --git a/dScripts/02_server/Map/General/Binoculars.cpp b/dScripts/02_server/Map/General/Binoculars.cpp index 786c0faf6..b96a255d2 100644 --- a/dScripts/02_server/Map/General/Binoculars.cpp +++ b/dScripts/02_server/Map/General/Binoculars.cpp @@ -1,6 +1,7 @@ #include "Binoculars.h" #include "Character.h" #include "GameMessages.h" +#include "ObjectMessages.h" #include "Game.h" #include "dServer.h" @@ -15,6 +16,6 @@ void Binoculars::OnUse(Entity* self, Entity* user) { if (!flagMsg.flag) { auto* const character = user->GetCharacter(); if (character) character->SetPlayerFlag(flag, true); - GameMessages::SendFireEventClientSide(self->GetObjectID(), user->GetSystemAddress(), u"achieve", LWOOBJID_EMPTY, 0, -1, LWOOBJID_EMPTY); + GameMessages::FireEventClientSide(self->GetObjectID(), u"achieve", LWOOBJID_EMPTY, LWOOBJID_EMPTY).SendToClient(user->GetSystemAddress()); } } diff --git a/dScripts/02_server/Map/General/ImaginationBackpackHealServer.cpp b/dScripts/02_server/Map/General/ImaginationBackpackHealServer.cpp index 8b3da9fa7..4fa238983 100644 --- a/dScripts/02_server/Map/General/ImaginationBackpackHealServer.cpp +++ b/dScripts/02_server/Map/General/ImaginationBackpackHealServer.cpp @@ -1,5 +1,6 @@ #include "ImaginationBackpackHealServer.h" #include "GameMessages.h" +#include "ObjectMessages.h" #include "MissionComponent.h" #include "eMissionTaskType.h" #include "eMissionState.h" @@ -15,8 +16,7 @@ void ImaginationBackpackHealServer::OnSkillEventFired(Entity* self, Entity* cast auto* missionComponent = caster->GetComponent(); if (missionComponent != nullptr && missionComponent->GetMissionState(healMission) == eMissionState::ACTIVE) { missionComponent->Progress(eMissionTaskType::SCRIPT, self->GetLOT()); - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"ClearMaelstrom", 0, 0, - caster->GetObjectID(), "", caster->GetSystemAddress()); + GameMessages::NotifyClientObject(self->GetObjectID(), u"ClearMaelstrom", 0, 0, caster->GetObjectID(), "").Send(caster->GetSystemAddress()); } } } diff --git a/dScripts/02_server/Map/General/PropertyPlatform.cpp b/dScripts/02_server/Map/General/PropertyPlatform.cpp index 4ea78314a..ac27bb46c 100644 --- a/dScripts/02_server/Map/General/PropertyPlatform.cpp +++ b/dScripts/02_server/Map/General/PropertyPlatform.cpp @@ -1,6 +1,7 @@ #include "PropertyPlatform.h" #include "QuickBuildComponent.h" #include "GameMessages.h" +#include "MovementMessages.h" #include "MovingPlatformComponent.h" void PropertyPlatform::OnQuickBuildComplete(Entity* self, Entity* target) { @@ -9,8 +10,7 @@ void PropertyPlatform::OnQuickBuildComplete(Entity* self, Entity* target) { // movingPlatform->StopPathing(); // movingPlatform->SetNoAutoStart(true); // } - GameMessages::SendPlatformResync(self, UNASSIGNED_SYSTEM_ADDRESS, true, 0, - 0, 0, eMovementPlatformState::Stationary); + GameMessages::PlatformResync(*self, true, 0, 0, 0, eMovementPlatformState::Stationary).Send(UNASSIGNED_SYSTEM_ADDRESS); } void PropertyPlatform::OnUse(Entity* self, Entity* user) { @@ -20,8 +20,7 @@ void PropertyPlatform::OnUse(Entity* self, Entity* user) { // if (movingPlatform != nullptr) { // movingPlatform->GotoWaypoint(1); // } - GameMessages::SendPlatformResync(self, UNASSIGNED_SYSTEM_ADDRESS, true, 0, - 1, 1, eMovementPlatformState::Moving); + GameMessages::PlatformResync(*self, true, 0, 1, 1, eMovementPlatformState::Moving).Send(UNASSIGNED_SYSTEM_ADDRESS); self->AddCallbackTimer(movementDelay + effectDelay, [self, this]() { self->SetNetworkVar(u"startEffect", dieDelay); diff --git a/dScripts/02_server/Map/General/StoryBoxInteractServer.cpp b/dScripts/02_server/Map/General/StoryBoxInteractServer.cpp index 05aadf8f8..6a449fa19 100644 --- a/dScripts/02_server/Map/General/StoryBoxInteractServer.cpp +++ b/dScripts/02_server/Map/General/StoryBoxInteractServer.cpp @@ -2,6 +2,7 @@ #include "Character.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "ObjectMessages.h" #include "dServer.h" #include "Amf3.h" #include "Entity.h" @@ -54,7 +55,7 @@ void StoryBoxInteractServer::OnUse(Entity* self, Entity* user) { if (!flagMsg.flag) { auto* const character = user->GetCharacter(); if (character) character->SetPlayerFlag(boxFlag, true); - GameMessages::SendFireEventClientSide(self->GetObjectID(), user->GetSystemAddress(), u"achieve", LWOOBJID_EMPTY, 0, -1, LWOOBJID_EMPTY); + GameMessages::FireEventClientSide(self->GetObjectID(), u"achieve", LWOOBJID_EMPTY, LWOOBJID_EMPTY).SendToClient(user->GetSystemAddress()); } } } diff --git a/dScripts/02_server/Map/General/TokenConsoleServer.cpp b/dScripts/02_server/Map/General/TokenConsoleServer.cpp index d8d5ba177..463cc0799 100644 --- a/dScripts/02_server/Map/General/TokenConsoleServer.cpp +++ b/dScripts/02_server/Map/General/TokenConsoleServer.cpp @@ -2,6 +2,7 @@ #include "InventoryComponent.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "ObjectMessages.h" #include "Character.h" #include "eReplicaComponentType.h" #include "eTerminateType.h" @@ -38,5 +39,5 @@ void TokenConsoleServer::OnUse(Entity* self, Entity* user) { inv->AddItem(tokenLOT, tokensToGive, eLootSourceType::NONE); } - GameMessages::SendTerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID()); + GameMessages::TerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID()).Send(UNASSIGNED_SYSTEM_ADDRESS); } diff --git a/dScripts/02_server/Map/General/VisToggleNotifierServer.cpp b/dScripts/02_server/Map/General/VisToggleNotifierServer.cpp index 16d805eb7..3077412c9 100644 --- a/dScripts/02_server/Map/General/VisToggleNotifierServer.cpp +++ b/dScripts/02_server/Map/General/VisToggleNotifierServer.cpp @@ -2,6 +2,7 @@ #include "eMissionState.h" #include "Game.h" #include "dZoneManager.h" +#include "ObjectMessages.h" void VisToggleNotifierServer::OnMissionDialogueOK(Entity* self, Entity* target, int missionId, eMissionState missionState) { auto itr = m_GameVariables.find(missionId); @@ -16,7 +17,7 @@ void VisToggleNotifierServer::OnMissionDialogueOK(Entity* self, Entity* target, for (const auto spawner : spawners) { const auto& spawnedObjIds = spawner->GetSpawnedObjectIDs(); for (const auto& objId : spawnedObjIds) { - GameMessages::SendNotifyClientObject(objId, u"SetVisibility", visible); + GameMessages::NotifyClientObject(objId, u"SetVisibility", visible).Send(UNASSIGNED_SYSTEM_ADDRESS); } } } diff --git a/dScripts/02_server/Map/General/WishingWellServer.cpp b/dScripts/02_server/Map/General/WishingWellServer.cpp index 2efa6ee21..38ba17be6 100644 --- a/dScripts/02_server/Map/General/WishingWellServer.cpp +++ b/dScripts/02_server/Map/General/WishingWellServer.cpp @@ -2,6 +2,7 @@ #include "ScriptedActivityComponent.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "ObjectMessages.h" #include "Loot.h" #include "EntityManager.h" #include "eTerminateType.h" @@ -29,7 +30,7 @@ void WishingWellServer::OnUse(Entity* self, Entity* user) { GeneralUtils::GenerateRandomNumber(1, 1000) ); - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"StartCooldown", 0, 0, LWOOBJID_EMPTY, "", user->GetSystemAddress()); + GameMessages::NotifyClientObject(self->GetObjectID(), u"StartCooldown", 0, 0, LWOOBJID_EMPTY, "").Send(user->GetSystemAddress()); const auto userID = user->GetObjectID(); @@ -38,10 +39,10 @@ void WishingWellServer::OnUse(Entity* self, Entity* user) { if (user == nullptr) return; - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"StopCooldown", 0, 0, LWOOBJID_EMPTY, "", user->GetSystemAddress()); + GameMessages::NotifyClientObject(self->GetObjectID(), u"StopCooldown", 0, 0, LWOOBJID_EMPTY, "").Send(user->GetSystemAddress()); }); - GameMessages::SendTerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID()); + GameMessages::TerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID()).Send(UNASSIGNED_SYSTEM_ADDRESS); } void WishingWellServer::OnTimerDone(Entity* self, std::string timerName) { diff --git a/dScripts/02_server/Map/NS/NsTokenConsoleServer.cpp b/dScripts/02_server/Map/NS/NsTokenConsoleServer.cpp index df0fe7b68..eb49368ae 100644 --- a/dScripts/02_server/Map/NS/NsTokenConsoleServer.cpp +++ b/dScripts/02_server/Map/NS/NsTokenConsoleServer.cpp @@ -2,6 +2,7 @@ #include "InventoryComponent.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "ObjectMessages.h" #include "Character.h" #include "MissionComponent.h" #include "QuickBuildComponent.h" @@ -58,5 +59,5 @@ void NsTokenConsoleServer::OnUse(Entity* self, Entity* user) { missionComponent->ForceProgressTaskType(863, 1, 1, false); - GameMessages::SendTerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID()); + GameMessages::TerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID()).Send(UNASSIGNED_SYSTEM_ADDRESS); } diff --git a/dScripts/02_server/Map/NT/NTPipeVisibilityServer.cpp b/dScripts/02_server/Map/NT/NTPipeVisibilityServer.cpp index 7d39e9d90..2138b88c0 100644 --- a/dScripts/02_server/Map/NT/NTPipeVisibilityServer.cpp +++ b/dScripts/02_server/Map/NT/NTPipeVisibilityServer.cpp @@ -1,6 +1,7 @@ #include "NTPipeVisibilityServer.h" #include "Entity.h" #include "Character.h" +#include "ObjectMessages.h" void NTPipeVisibilityServer::OnQuickBuildComplete(Entity* self, Entity* target) { const auto flag = self->GetVar(u"flag"); @@ -11,5 +12,5 @@ void NTPipeVisibilityServer::OnQuickBuildComplete(Entity* self, Entity* target) character->SetPlayerFlag(flag, true); - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"PipeBuilt"); + GameMessages::NotifyClientObject(self->GetObjectID(), u"PipeBuilt").Send(UNASSIGNED_SYSTEM_ADDRESS); } diff --git a/dScripts/02_server/Map/NT/NtAssemblyTubeServer.cpp b/dScripts/02_server/Map/NT/NtAssemblyTubeServer.cpp index debcaf054..081399bc9 100644 --- a/dScripts/02_server/Map/NT/NtAssemblyTubeServer.cpp +++ b/dScripts/02_server/Map/NT/NtAssemblyTubeServer.cpp @@ -2,6 +2,7 @@ #include "GameMessages.h" #include "EffectsMessages.h" #include "CombatMessages.h" +#include "MovementMessages.h" #include "EntityManager.h" #include "MissionComponent.h" #include "eMissionTaskType.h" @@ -103,7 +104,7 @@ void NtAssemblyTubeServer::TeleportPlayer(Entity* self, Entity* player) { const auto destPosition = destination->GetPosition(); const auto destRotation = destination->GetRotation(); - GameMessages::SendTeleport(player->GetObjectID(), destPosition, destRotation, player->GetSystemAddress(), true); + GameMessages::Teleport(player->GetObjectID(), destPosition, destRotation, true).SendToClient(player->GetSystemAddress()); RenderComponent::PlayAnimation(player, u"tube-resurrect", 4.0f); diff --git a/dScripts/02_server/Map/NT/NtCombatChallengeServer.cpp b/dScripts/02_server/Map/NT/NtCombatChallengeServer.cpp index 51d7ebd8d..a49d5e6ff 100644 --- a/dScripts/02_server/Map/NT/NtCombatChallengeServer.cpp +++ b/dScripts/02_server/Map/NT/NtCombatChallengeServer.cpp @@ -2,13 +2,14 @@ #include "GameMessages.h" #include "EffectsMessages.h" #include "ActivityMessages.h" +#include "ObjectMessages.h" #include "EntityManager.h" #include "EntityInfo.h" #include "InventoryComponent.h" #include "MissionComponent.h" void NtCombatChallengeServer::OnUse(Entity* self, Entity* user) { - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"UI_Open", 0, 0, user->GetObjectID(), "", user->GetSystemAddress()); + GameMessages::NotifyClientObject(self->GetObjectID(), u"UI_Open", 0, 0, user->GetObjectID(), "").Send(user->GetSystemAddress()); } void NtCombatChallengeServer::OnDie(Entity* self, Entity* killer) { @@ -40,7 +41,7 @@ void NtCombatChallengeServer::OnHitOrHealResult(Entity* self, Entity* attacker, void NtCombatChallengeServer::OnFireEventServerSide(Entity* self, Entity* sender, std::string args, int32_t param1, int32_t param2, int32_t param3) { - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"UI_Close", 0, 0, sender->GetObjectID(), "", sender->GetSystemAddress()); + GameMessages::NotifyClientObject(self->GetObjectID(), u"UI_Close", 0, 0, sender->GetObjectID(), "").Send(sender->GetSystemAddress()); } @@ -66,7 +67,7 @@ void NtCombatChallengeServer::OnMessageBoxResponse(Entity* self, Entity* sender, self->SetNetworkVar(u"toggle", true); } else if (identifier == u"CloseButton") { - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"UI_Close", 1, 0, sender->GetObjectID(), "", sender->GetSystemAddress()); + GameMessages::NotifyClientObject(self->GetObjectID(), u"UI_Close", 1, 0, sender->GetObjectID(), "").Send(sender->GetSystemAddress()); } } diff --git a/dScripts/02_server/Map/NT/NtImagimeterVisibility.cpp b/dScripts/02_server/Map/NT/NtImagimeterVisibility.cpp index 8003f4df2..31278fd90 100644 --- a/dScripts/02_server/Map/NT/NtImagimeterVisibility.cpp +++ b/dScripts/02_server/Map/NT/NtImagimeterVisibility.cpp @@ -1,5 +1,6 @@ #include "NtImagimeterVisibility.h" #include "GameMessages.h" +#include "ObjectMessages.h" #include "Entity.h" #include "Character.h" #include "ePlayerFlag.h" @@ -8,5 +9,5 @@ void NTImagimeterVisibility::OnQuickBuildComplete(Entity* self, Entity* target) auto* character = target->GetCharacter(); if (character) character->SetPlayerFlag(ePlayerFlag::NT_PLINTH_REBUILD, true); - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"PlinthBuilt", 0, 0, LWOOBJID_EMPTY, "", target->GetSystemAddress()); + GameMessages::NotifyClientObject(self->GetObjectID(), u"PlinthBuilt", 0, 0, LWOOBJID_EMPTY, "").Send(target->GetSystemAddress()); } diff --git a/dScripts/02_server/Map/NT/NtParadoxPanelServer.cpp b/dScripts/02_server/Map/NT/NtParadoxPanelServer.cpp index 9b65168dd..b2592235e 100644 --- a/dScripts/02_server/Map/NT/NtParadoxPanelServer.cpp +++ b/dScripts/02_server/Map/NT/NtParadoxPanelServer.cpp @@ -2,6 +2,7 @@ #include "GameMessages.h" #include "EffectsMessages.h" #include "CombatMessages.h" +#include "ObjectMessages.h" #include "MissionComponent.h" #include "EntityManager.h" #include "Character.h" @@ -11,9 +12,9 @@ #include "eStateChangeType.h" void NtParadoxPanelServer::OnUse(Entity* self, Entity* user) { - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"bActive", 1, 0, user->GetObjectID(), "", user->GetSystemAddress()); + GameMessages::NotifyClientObject(self->GetObjectID(), u"bActive", 1, 0, user->GetObjectID(), "").Send(user->GetSystemAddress()); - GameMessages::SendTerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID()); + GameMessages::TerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID()).Send(UNASSIGNED_SYSTEM_ADDRESS); self->SetVar(u"bActive", true); @@ -40,7 +41,7 @@ void NtParadoxPanelServer::OnUse(Entity* self, Entity* user) { RenderComponent::PlayAnimation(player, u"rebuild-celebrate"); - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"SparkStop", 0, 0, player->GetObjectID(), "", player->GetSystemAddress()); + GameMessages::NotifyClientObject(self->GetObjectID(), u"SparkStop", 0, 0, player->GetObjectID(), "").Send(player->GetSystemAddress()); GameMessages::SetStunned stun; stun.target = player->GetObjectID(); stun.StateChangeType = eStateChangeType::POP; @@ -81,7 +82,7 @@ void NtParadoxPanelServer::OnUse(Entity* self, Entity* user) { return; } - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"bActive", 0, 0, player->GetObjectID(), "", player->GetSystemAddress()); + GameMessages::NotifyClientObject(self->GetObjectID(), u"bActive", 0, 0, player->GetObjectID(), "").Send(player->GetSystemAddress()); GameMessages::StopFXEffect(self->GetObjectID(), true, "console_sparks").Send(UNASSIGNED_SYSTEM_ADDRESS); diff --git a/dScripts/02_server/Map/NT/NtParadoxTeleServer.cpp b/dScripts/02_server/Map/NT/NtParadoxTeleServer.cpp index 7e63bb5e9..ec2cb3f80 100644 --- a/dScripts/02_server/Map/NT/NtParadoxTeleServer.cpp +++ b/dScripts/02_server/Map/NT/NtParadoxTeleServer.cpp @@ -2,6 +2,7 @@ #include "GameMessages.h" #include "EffectsMessages.h" #include "CombatMessages.h" +#include "MovementMessages.h" #include "EntityManager.h" #include "MissionComponent.h" #include "eMissionTaskType.h" @@ -85,7 +86,7 @@ void NtParadoxTeleServer::TeleportPlayer(Entity* self, Entity* player) { cinematic.Send(player->GetSystemAddress()); } - GameMessages::SendTeleport(player->GetObjectID(), destPosition, destRotation, player->GetSystemAddress(), true); + GameMessages::Teleport(player->GetObjectID(), destPosition, destRotation, true).SendToClient(player->GetSystemAddress()); RenderComponent::PlayAnimation(player, u"paradox-teleport-in", 4.0f); diff --git a/dScripts/02_server/Map/NT/NtVentureCannonServer.cpp b/dScripts/02_server/Map/NT/NtVentureCannonServer.cpp index ebf7beaea..d20f34ade 100644 --- a/dScripts/02_server/Map/NT/NtVentureCannonServer.cpp +++ b/dScripts/02_server/Map/NT/NtVentureCannonServer.cpp @@ -2,6 +2,8 @@ #include "GameMessages.h" #include "EffectsMessages.h" #include "CombatMessages.h" +#include "MovementMessages.h" +#include "ObjectMessages.h" #include "EntityManager.h" #include "Entity.h" #include "GeneralUtils.h" @@ -40,7 +42,7 @@ void NtVentureCannonServer::OnUse(Entity* self, Entity* user) { auto destRotation = self->GetRotation(); - GameMessages::SendTeleport(playerID, destPosition, destRotation, player->GetSystemAddress(), true); + GameMessages::Teleport(playerID, destPosition, destRotation, true).SendToClient(player->GetSystemAddress()); RenderComponent::PlayAnimation(player, u"scale-down", 4.0f); @@ -140,7 +142,7 @@ void NtVentureCannonServer::UnlockCannonPlayer(Entity* self, Entity* player) { self->SetNetworkVar(u"bIsInUse", false); - GameMessages::SendTerminateInteraction(player->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID()); + GameMessages::TerminateInteraction(player->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID()).Send(UNASSIGNED_SYSTEM_ADDRESS); } void NtVentureCannonServer::FirePlayer(Entity* self, Entity* player) { @@ -158,7 +160,7 @@ void NtVentureCannonServer::FirePlayer(Entity* self, Entity* player) { const auto destPosition = destination->GetPosition(); const auto destRotation = destination->GetRotation(); - GameMessages::SendTeleport(player->GetObjectID(), destPosition, destRotation, player->GetSystemAddress(), true); + GameMessages::Teleport(player->GetObjectID(), destPosition, destRotation, true).SendToClient(player->GetSystemAddress()); RenderComponent::PlayAnimation(player, u"venture-cannon-out", 4.0f); } diff --git a/dScripts/02_server/Map/PR/HydrantBroken.cpp b/dScripts/02_server/Map/PR/HydrantBroken.cpp index daaee04b7..ef5ecc4c2 100644 --- a/dScripts/02_server/Map/PR/HydrantBroken.cpp +++ b/dScripts/02_server/Map/PR/HydrantBroken.cpp @@ -3,6 +3,7 @@ #include "EntityManager.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "ObjectMessages.h" void HydrantBroken::OnStartup(Entity* self) { self->AddTimer("playEffect", 1); @@ -19,7 +20,7 @@ void HydrantBroken::OnStartup(Entity* self) { msg.bActive = true; msg.Send(UNASSIGNED_SYSTEM_ADDRESS); - GameMessages::SendNotifyObject(bouncer->GetObjectID(), self->GetObjectID(), u"enableCollision", UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyObject(bouncer->GetObjectID(), self->GetObjectID(), u"enableCollision").Send(UNASSIGNED_SYSTEM_ADDRESS); } self->AddTimer("KillBroken", 25); @@ -35,7 +36,7 @@ void HydrantBroken::OnTimerDone(Entity* self, std::string timerName) { msg.bActive = false; msg.Send(UNASSIGNED_SYSTEM_ADDRESS); - GameMessages::SendNotifyObject(bouncer->GetObjectID(), self->GetObjectID(), u"disableCollision", UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyObject(bouncer->GetObjectID(), self->GetObjectID(), u"disableCollision").Send(UNASSIGNED_SYSTEM_ADDRESS); } self->Kill(); diff --git a/dScripts/02_server/Map/PR/SpawnGryphonServer.cpp b/dScripts/02_server/Map/PR/SpawnGryphonServer.cpp index 9e22efbfb..efc988b2e 100644 --- a/dScripts/02_server/Map/PR/SpawnGryphonServer.cpp +++ b/dScripts/02_server/Map/PR/SpawnGryphonServer.cpp @@ -1,6 +1,7 @@ #include "SpawnGryphonServer.h" #include "InventoryComponent.h" #include "GameMessages.h" +#include "ObjectMessages.h" #include "MissionComponent.h" #include "eMissionState.h" #include "eTerminateType.h" @@ -21,7 +22,7 @@ void SpawnGryphonServer::OnUse(Entity* self, Entity* user) { if (missionComponent != nullptr && inventoryComponent != nullptr && missionComponent->GetMissionState(1391) == eMissionState::ACTIVE) { inventoryComponent->RemoveItem(12483, inventoryComponent->GetLotCount(12483), eInventoryType::ALL); - GameMessages::SendTerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID()); + GameMessages::TerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID()).Send(UNASSIGNED_SYSTEM_ADDRESS); return; } diff --git a/dScripts/02_server/Map/Property/AG_Small/ZoneAgProperty.cpp b/dScripts/02_server/Map/Property/AG_Small/ZoneAgProperty.cpp index dc260c7c8..fb89b36c4 100644 --- a/dScripts/02_server/Map/Property/AG_Small/ZoneAgProperty.cpp +++ b/dScripts/02_server/Map/Property/AG_Small/ZoneAgProperty.cpp @@ -4,6 +4,7 @@ #include "Entity.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "ObjectMessages.h" #include "dZoneManager.h" #include "RenderComponent.h" #include "MissionComponent.h" @@ -67,8 +68,7 @@ void ZoneAgProperty::OnPlayerLoaded(Entity* self, Entity* player) { ambientSound.target = player->GetObjectID(); ambientSound.audioGUID = GUIDMaelstrom; ambientSound.SendToClient(player->GetSystemAddress()); - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"maelstromSkyOn", 0, 0, - LWOOBJID_EMPTY, "", player->GetSystemAddress()); + GameMessages::NotifyClientObject(self->GetObjectID(), u"maelstromSkyOn", 0, 0, LWOOBJID_EMPTY, "").Send(player->GetSystemAddress()); self->SetNetworkVar(u"unclaimed", true); @@ -176,8 +176,7 @@ void ZoneAgProperty::StartMaelstrom(Entity* self, Entity* player) { StartTornadoFx(self); if (player != nullptr) { - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"maelstromSkyOn", 0, 0, LWOOBJID_EMPTY, - "", player->GetSystemAddress()); + GameMessages::NotifyClientObject(self->GetObjectID(), u"maelstromSkyOn", 0, 0, LWOOBJID_EMPTY, "").Send(player->GetSystemAddress()); } } @@ -205,7 +204,7 @@ void ZoneAgProperty::BaseTimerDone(Entity* self, const std::string& timerName) { auto* entity = entities[0]; - GameMessages::SendNotifyClientObject(Game::entityManager->GetZoneControlEntity()->GetObjectID(), u"GuardChat", 0, 0, entity->GetObjectID(), "", UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(Game::entityManager->GetZoneControlEntity()->GetObjectID(), u"GuardChat", 0, 0, entity->GetObjectID(), "").Send(UNASSIGNED_SYSTEM_ADDRESS); LoadProperty(self); self->AddTimer("KillGuard", 5); @@ -235,8 +234,7 @@ void ZoneAgProperty::BaseTimerDone(Entity* self, const std::string& timerName) { self->AddTimer("turnSkyOff", 1.5f); self->AddTimer("killFXObject", 8); } else if (timerName == "turnSkyOff") { - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"SkyOff", 0, 0, LWOOBJID_EMPTY, - "", UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(self->GetObjectID(), u"SkyOff", 0, 0, LWOOBJID_EMPTY, "").Send(UNASSIGNED_SYSTEM_ADDRESS); } else if (timerName == "killSpider") { for (auto* entity : Game::entityManager->GetEntitiesInGroup(self->GetVar(EnemiesGroup))) { entity->Kill(); @@ -271,13 +269,12 @@ void ZoneAgProperty::BaseTimerDone(Entity* self, const std::string& timerName) { ambientSound.SendToClient(player->GetSystemAddress()); } } else if (timerName == "ShowVendor") { - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"vendorOn", 0, 0, LWOOBJID_EMPTY, "", UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(self->GetObjectID(), u"vendorOn", 0, 0, LWOOBJID_EMPTY, "").Send(UNASSIGNED_SYSTEM_ADDRESS); for (const auto& ambient : self->GetVar>(AmbientFXSpawner)) { ActivateSpawner(ambient); } } else if (timerName == "BoundsVisOn") { - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"boundsAnim", 0, 0, - LWOOBJID_EMPTY, "", UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(self->GetObjectID(), u"boundsAnim", 0, 0, LWOOBJID_EMPTY, "").Send(UNASSIGNED_SYSTEM_ADDRESS); } else if (timerName == "runPlayerLoadedAgain") { CheckForOwner(self); } else if (timerName == "pollTornadoFX") { @@ -424,8 +421,7 @@ void ZoneAgProperty::BaseOnFireEventServerSide(Entity* self, Entity* sender, std auto* const character = player->GetCharacter(); if (character) character->SetPlayerFlag(self->GetVar(defeatedProperyFlag), true); - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"PlayCinematic", 0, 0, - LWOOBJID_EMPTY, destroyedCinematic, UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(self->GetObjectID(), u"PlayCinematic", 0, 0, LWOOBJID_EMPTY, destroyedCinematic).Send(UNASSIGNED_SYSTEM_ADDRESS); self->AddTimer("tornadoOff", 0.5f); } diff --git a/dScripts/02_server/Map/Property/PropertyBankInteract.cpp b/dScripts/02_server/Map/Property/PropertyBankInteract.cpp index 16ee02fd1..be59d966b 100644 --- a/dScripts/02_server/Map/Property/PropertyBankInteract.cpp +++ b/dScripts/02_server/Map/Property/PropertyBankInteract.cpp @@ -2,6 +2,7 @@ #include "EntityManager.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "ObjectMessages.h" #include "Amf3.h" #include "Entity.h" @@ -31,8 +32,7 @@ void PropertyBankInteract::OnUse(Entity* self, Entity* user) { uiMessage.args = std::move(args); uiMessage.SendToClient(user->GetSystemAddress()); - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"OpenBank", 0, 0, LWOOBJID_EMPTY, - "", user->GetSystemAddress()); + GameMessages::NotifyClientObject(self->GetObjectID(), u"OpenBank", 0, 0, LWOOBJID_EMPTY, "").Send(user->GetSystemAddress()); } void PropertyBankInteract::OnFireEventServerSide(Entity* self, Entity* sender, std::string args, int32_t param1, @@ -48,7 +48,6 @@ void PropertyBankInteract::OnFireEventServerSide(Entity* self, Entity* sender, s uiMessage.args = std::move(amfArgs); uiMessage.SendToClient(sender->GetSystemAddress()); - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"CloseBank", 0, 0, LWOOBJID_EMPTY, - "", sender->GetSystemAddress()); + GameMessages::NotifyClientObject(self->GetObjectID(), u"CloseBank", 0, 0, LWOOBJID_EMPTY, "").Send(sender->GetSystemAddress()); } } diff --git a/dScripts/02_server/Map/VE/VeBricksampleServer.cpp b/dScripts/02_server/Map/VE/VeBricksampleServer.cpp index 382ce751b..9e48a2ba4 100644 --- a/dScripts/02_server/Map/VE/VeBricksampleServer.cpp +++ b/dScripts/02_server/Map/VE/VeBricksampleServer.cpp @@ -3,6 +3,7 @@ #include "EntityManager.h" #include "MissionComponent.h" #include "GameMessages.h" +#include "ObjectMessages.h" #include "eMissionState.h" void VeBricksampleServer::OnUse(Entity* self, Entity* user) { @@ -15,7 +16,7 @@ void VeBricksampleServer::OnUse(Entity* self, Entity* user) { inventoryComponent->AddItem(loot, 1, eLootSourceType::NONE); for (auto* brickEntity : Game::entityManager->GetEntitiesInGroup("Bricks")) { - GameMessages::SendNotifyClientObject(brickEntity->GetObjectID(), u"Pickedup"); + GameMessages::NotifyClientObject(brickEntity->GetObjectID(), u"Pickedup").Send(UNASSIGNED_SYSTEM_ADDRESS); } } } diff --git a/dScripts/02_server/Map/VE/VeEpsilonServer.cpp b/dScripts/02_server/Map/VE/VeEpsilonServer.cpp index 4e8cb4e9a..79023bf18 100644 --- a/dScripts/02_server/Map/VE/VeEpsilonServer.cpp +++ b/dScripts/02_server/Map/VE/VeEpsilonServer.cpp @@ -2,6 +2,7 @@ #include "Character.h" #include "EntityManager.h" #include "GameMessages.h" +#include "ObjectMessages.h" #include "eMissionState.h" #include "Entity.h" @@ -22,7 +23,7 @@ void VeEpsilonServer::OnMissionDialogueOK(Entity* self, Entity* target, int miss // Notify the client that all objects have updated self->AddCallbackTimer(3.0f, [this]() { for (const auto* console : Game::entityManager->GetEntitiesInGroup(m_ConsoleGroup)) { - GameMessages::SendNotifyClientObject(console->GetObjectID(), u""); + GameMessages::NotifyClientObject(console->GetObjectID(), u"").Send(UNASSIGNED_SYSTEM_ADDRESS); } }); } diff --git a/dScripts/02_server/Map/VE/VeMissionConsole.cpp b/dScripts/02_server/Map/VE/VeMissionConsole.cpp index aa7035eb2..443c9a570 100644 --- a/dScripts/02_server/Map/VE/VeMissionConsole.cpp +++ b/dScripts/02_server/Map/VE/VeMissionConsole.cpp @@ -2,6 +2,7 @@ #include "InventoryComponent.h" #include "Character.h" #include "GameMessages.h" +#include "ObjectMessages.h" #include "Loot.h" #include "eTerminateType.h" @@ -22,6 +23,6 @@ void VeMissionConsole::OnUse(Entity* self, Entity* user) { character->SetPlayerFlag(flag, true); } - GameMessages::SendNotifyClientObject(self->GetObjectID(), u""); - GameMessages::SendTerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID()); + GameMessages::NotifyClientObject(self->GetObjectID(), u"").Send(UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::TerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID()).Send(UNASSIGNED_SYSTEM_ADDRESS); } diff --git a/dScripts/02_server/Map/njhub/CatapultBaseServer.cpp b/dScripts/02_server/Map/njhub/CatapultBaseServer.cpp index 033da0e86..a0dc3580e 100644 --- a/dScripts/02_server/Map/njhub/CatapultBaseServer.cpp +++ b/dScripts/02_server/Map/njhub/CatapultBaseServer.cpp @@ -2,6 +2,7 @@ #include "PetMessages.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "ObjectMessages.h" #include "EntityManager.h" #include "Entity.h" #include "RenderComponent.h" @@ -65,7 +66,7 @@ void CatapultBaseServer::OnTimerDone(Entity* self, std::string timerName) { if (bouncer == nullptr) return; // kill the bouncer - GameMessages::SendNotifyClientObject(bouncer->GetObjectID(), u"TimeToDie"); + GameMessages::NotifyClientObject(bouncer->GetObjectID(), u"TimeToDie").Send(UNASSIGNED_SYSTEM_ADDRESS); bouncer->Smash(self->GetObjectID(), eKillType::VIOLENT); } } diff --git a/dScripts/02_server/Map/njhub/CatapultBouncerServer.cpp b/dScripts/02_server/Map/njhub/CatapultBouncerServer.cpp index 28ed9f804..6b6246a0a 100644 --- a/dScripts/02_server/Map/njhub/CatapultBouncerServer.cpp +++ b/dScripts/02_server/Map/njhub/CatapultBouncerServer.cpp @@ -1,9 +1,10 @@ #include "CatapultBouncerServer.h" #include "GameMessages.h" +#include "ObjectMessages.h" #include "EntityManager.h" void CatapultBouncerServer::OnQuickBuildComplete(Entity* self, Entity* target) { - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"Built", 0, 0, LWOOBJID_EMPTY, "", UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(self->GetObjectID(), u"Built", 0, 0, LWOOBJID_EMPTY, "").Send(UNASSIGNED_SYSTEM_ADDRESS); self->SetNetworkVar(u"Built", true); diff --git a/dScripts/02_server/Map/njhub/CavePrisonCage.cpp b/dScripts/02_server/Map/njhub/CavePrisonCage.cpp index 591c29d56..884271165 100644 --- a/dScripts/02_server/Map/njhub/CavePrisonCage.cpp +++ b/dScripts/02_server/Map/njhub/CavePrisonCage.cpp @@ -2,6 +2,7 @@ #include "EntityManager.h" #include "QuickBuildComponent.h" #include "GameMessages.h" +#include "ObjectMessages.h" #include "Character.h" #include "dZoneManager.h" #include "RenderComponent.h" @@ -158,7 +159,7 @@ void CavePrisonCage::OnTimerDone(Entity* self, std::string timerName) { return; } - GameMessages::SendNotifyClientObject(villager->GetObjectID(), u"TimeToChat", 0, 0, LWOOBJID_EMPTY, "", UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(villager->GetObjectID(), u"TimeToChat", 0, 0, LWOOBJID_EMPTY, "").Send(UNASSIGNED_SYSTEM_ADDRESS); // Get the builder and make sure it still exists auto* builder = Game::entityManager->GetEntity(self->GetVar(u"Builder")); diff --git a/dScripts/02_server/Map/njhub/NjWuNPC.cpp b/dScripts/02_server/Map/njhub/NjWuNPC.cpp index 53658aa4a..6540cd357 100644 --- a/dScripts/02_server/Map/njhub/NjWuNPC.cpp +++ b/dScripts/02_server/Map/njhub/NjWuNPC.cpp @@ -4,6 +4,7 @@ #include "EntityManager.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "ObjectMessages.h" #include "eMissionState.h" #include "ePlayerFlag.h" @@ -30,8 +31,7 @@ void NjWuNPC::OnMissionDialogueOK(Entity* self, Entity* target, int missionID, e // Hide the chest for (auto* chest : Game::entityManager->GetEntitiesInGroup(m_DragonChestGroup)) { - GameMessages::SendNotifyClientObject(chest->GetObjectID(), m_ShowChestNotification, 0, -1, - target->GetObjectID(), "", target->GetSystemAddress()); + GameMessages::NotifyClientObject(chest->GetObjectID(), m_ShowChestNotification, 0, -1, target->GetObjectID(), "").Send(target->GetSystemAddress()); } return; @@ -43,8 +43,7 @@ void NjWuNPC::OnMissionDialogueOK(Entity* self, Entity* target, int missionID, e // Show the chest for (auto* chest : Game::entityManager->GetEntitiesInGroup(m_DragonChestGroup)) { - GameMessages::SendNotifyClientObject(chest->GetObjectID(), m_ShowChestNotification, 1, -1, - target->GetObjectID(), "", target->GetSystemAddress()); + GameMessages::NotifyClientObject(chest->GetObjectID(), m_ShowChestNotification, 1, -1, target->GetObjectID(), "").Send(target->GetSystemAddress()); } auto playerID = target->GetObjectID(); diff --git a/dScripts/02_server/Map/njhub/boss_instance/NjMonastryBossInstance.cpp b/dScripts/02_server/Map/njhub/boss_instance/NjMonastryBossInstance.cpp index 9e20078ff..965662b75 100644 --- a/dScripts/02_server/Map/njhub/boss_instance/NjMonastryBossInstance.cpp +++ b/dScripts/02_server/Map/njhub/boss_instance/NjMonastryBossInstance.cpp @@ -5,6 +5,8 @@ #include "dZoneManager.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "MovementMessages.h" +#include "ObjectMessages.h" #include "BaseCombatAIComponent.h" #include "BuffComponent.h" #include "SkillComponent.h" @@ -84,8 +86,7 @@ void NjMonastryBossInstance::OnPlayerLoaded(Entity* self, Entity* player) { self->AddCallbackTimer(0.0f, [self, player]() { if (player != nullptr) { // If we don't have enough players yet, wait for the others to load and notify the client to play a cool cinematic - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"PlayerLoaded", 0, 0, - player->GetObjectID(), "", player->GetSystemAddress()); + GameMessages::NotifyClientObject(self->GetObjectID(), u"PlayerLoaded", 0, 0, player->GetObjectID(), "").Send(player->GetSystemAddress()); } }); @@ -114,7 +115,7 @@ void NjMonastryBossInstance::OnPlayerExit(Entity* self, Entity* player) { // resize the instance to account for such. if (totalPlayersLoaded.size() <= 2) self->SetVar(LargeTeamVariable, false); - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"PlayerLeft", 0, 0, player->GetObjectID(), "", UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(self->GetObjectID(), u"PlayerLeft", 0, 0, player->GetObjectID(), "").Send(UNASSIGNED_SYSTEM_ADDRESS); } void NjMonastryBossInstance::OnActivityTimerDone(Entity* self, const std::string& name) { @@ -173,8 +174,7 @@ void NjMonastryBossInstance::OnActivityTimerDone(Entity* self, const std::string } ActivityTimerStart(self, SpawnLowerFrakjawTimer, 1.0f, 1.0f); - GameMessages::SendNotifyClientObject(self->GetObjectID(), PlayCinematicNotification, 0, 0, - LWOOBJID_EMPTY, BottomFrakSpawn, UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(self->GetObjectID(), PlayCinematicNotification, 0, 0, LWOOBJID_EMPTY, BottomFrakSpawn).Send(UNASSIGNED_SYSTEM_ADDRESS); } else if (timerName == SpawnLowerFrakjawTimer) { auto spawners = Game::zoneManager->GetSpawnersByName(LowerFrakjawSpawner); if (!spawners.empty()) { @@ -182,8 +182,7 @@ void NjMonastryBossInstance::OnActivityTimerDone(Entity* self, const std::string spawner->Activate(); } } else if (timerName == SpawnRailTimer) { - GameMessages::SendNotifyClientObject(self->GetObjectID(), PlayCinematicNotification, 0, 0, - LWOOBJID_EMPTY, FireRailSpawn, UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(self->GetObjectID(), PlayCinematicNotification, 0, 0, LWOOBJID_EMPTY, FireRailSpawn).Send(UNASSIGNED_SYSTEM_ADDRESS); auto spawners = Game::zoneManager->GetSpawnersByName(FireRailSpawner); if (!spawners.empty()) { @@ -230,15 +229,10 @@ void NjMonastryBossInstance::HandleCounterWeightSpawned(Entity* self, Entity* co switch (state) { case eQuickBuildState::BUILDING: - GameMessages::SendNotifyClientObject(self->GetObjectID(), PlayCinematicNotification, - 0, 0, counterWeight->GetObjectID(), - BaseCounterweightQB + std::to_string(self->GetVar(WaveNumberVariable)), - UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(self->GetObjectID(), PlayCinematicNotification, 0, 0, counterWeight->GetObjectID(), BaseCounterweightQB + std::to_string(self->GetVar(WaveNumberVariable))).Send(UNASSIGNED_SYSTEM_ADDRESS); return; case eQuickBuildState::INCOMPLETE: - GameMessages::SendNotifyClientObject(self->GetObjectID(), EndCinematicNotification, - 0, 0, LWOOBJID_EMPTY, "", - UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(self->GetObjectID(), EndCinematicNotification, 0, 0, LWOOBJID_EMPTY, "").Send(UNASSIGNED_SYSTEM_ADDRESS); return; case eQuickBuildState::RESETTING: ActivityTimerStart(self, SpawnCounterWeightTimer, 0.0f, 0.0f); @@ -255,8 +249,7 @@ void NjMonastryBossInstance::HandleCounterWeightSpawned(Entity* self, Entity* co auto* frakjaw = Game::entityManager->GetEntity(self->GetVar(LedgeFrakjawVariable)); if (frakjaw == nullptr) { - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"LedgeFrakjawDead", 0, - 0, LWOOBJID_EMPTY, "", UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(self->GetObjectID(), u"LedgeFrakjawDead", 0, 0, LWOOBJID_EMPTY, "").Send(UNASSIGNED_SYSTEM_ADDRESS); return; } @@ -304,8 +297,7 @@ void NjMonastryBossInstance::HandleLowerFrakjawSpawned(Entity* self, Entity* low NjMonastryBossInstance::HandleLowerFrakjawDied(self, lowerFrakjaw); }); - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"LedgeFrakjawDead", 0, 0, - LWOOBJID_EMPTY, "", UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(self->GetObjectID(), u"LedgeFrakjawDead", 0, 0, LWOOBJID_EMPTY, "").Send(UNASSIGNED_SYSTEM_ADDRESS); if (self->GetVar(LargeTeamVariable)) { // Double frakjaws health for large teams @@ -397,37 +389,30 @@ void NjMonastryBossInstance::HandleWaveEnemyDied(Entity* self, Entity* waveEnemy void NjMonastryBossInstance::TeleportPlayer(Entity* player, uint32_t position) { for (const auto* spawnPoint : Game::entityManager->GetEntitiesInGroup("SpawnPoint" + std::to_string(position))) { - GameMessages::SendTeleport(player->GetObjectID(), spawnPoint->GetPosition(), spawnPoint->GetRotation(), - player->GetSystemAddress(), true); + GameMessages::Teleport(player->GetObjectID(), spawnPoint->GetPosition(), spawnPoint->GetRotation(), true).SendToClient(player->GetSystemAddress()); } } void NjMonastryBossInstance::SummonWave(Entity* self, Entity* frakjaw) { - GameMessages::SendNotifyClientObject(self->GetObjectID(), PlayCinematicNotification, 0, 0, LWOOBJID_EMPTY, - LedgeFrakSummon, UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(self->GetObjectID(), PlayCinematicNotification, 0, 0, LWOOBJID_EMPTY, LedgeFrakSummon).Send(UNASSIGNED_SYSTEM_ADDRESS); RenderComponent::PlayAnimation(frakjaw, SummonAnimation); // Stop the music for the first, fourth and fifth wave const auto wave = self->GetVar(WaveNumberVariable); if (wave >= 1 && wave < (m_Waves.size() - 1)) { - GameMessages::SendNotifyClientObject(self->GetObjectID(), StopMusicNotification, 0, 0, - LWOOBJID_EMPTY, AudioWaveAudio + std::to_string(wave - 1), - UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(self->GetObjectID(), StopMusicNotification, 0, 0, LWOOBJID_EMPTY, AudioWaveAudio + std::to_string(wave - 1)).Send(UNASSIGNED_SYSTEM_ADDRESS); } // After frakjaw moves down the music stays the same if (wave < (m_Waves.size() - 1)) { - GameMessages::SendNotifyClientObject(self->GetObjectID(), StartMusicNotification, 0, 0, - LWOOBJID_EMPTY, AudioWaveAudio + std::to_string(wave), - UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(self->GetObjectID(), StartMusicNotification, 0, 0, LWOOBJID_EMPTY, AudioWaveAudio + std::to_string(wave)).Send(UNASSIGNED_SYSTEM_ADDRESS); } ActivityTimerStart(self, SpawnWaveTimer, 4.0f, 4.0f); } void NjMonastryBossInstance::LowerFrakjawSummon(Entity* self, Entity* frakjaw) { - GameMessages::SendNotifyClientObject(self->GetObjectID(), PlayCinematicNotification, 0, 0, - LWOOBJID_EMPTY, BottomFrakSummon, UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(self->GetObjectID(), PlayCinematicNotification, 0, 0, LWOOBJID_EMPTY, BottomFrakSummon).Send(UNASSIGNED_SYSTEM_ADDRESS); ActivityTimerStart(self, SpawnWaveTimer, 2.0f, 2.0f); RenderComponent::PlayAnimation(frakjaw, SummonAnimation); } @@ -451,10 +436,8 @@ void NjMonastryBossInstance::LowerFrakjaw(Entity* self, Entity* frakjaw) { RenderComponent::PlayAnimation(frakjaw, TeleportOutAnimation); ActivityTimerStart(self, LowerFrakjawCamTimer, 2.0f, 2.0f); - GameMessages::SendNotifyClientObject(frakjaw->GetObjectID(), StopMusicNotification, 0, 0, - LWOOBJID_EMPTY, AudioWaveAudio + std::to_string(m_Waves.size() - 3), UNASSIGNED_SYSTEM_ADDRESS); - GameMessages::SendNotifyClientObject(frakjaw->GetObjectID(), StartMusicNotification, 0, 0, - LWOOBJID_EMPTY, AudioWaveAudio + std::to_string(m_Waves.size() - 2), UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(frakjaw->GetObjectID(), StopMusicNotification, 0, 0, LWOOBJID_EMPTY, AudioWaveAudio + std::to_string(m_Waves.size() - 3)).Send(UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(frakjaw->GetObjectID(), StartMusicNotification, 0, 0, LWOOBJID_EMPTY, AudioWaveAudio + std::to_string(m_Waves.size() - 2)).Send(UNASSIGNED_SYSTEM_ADDRESS); } void NjMonastryBossInstance::SpawnOnNetwork(Entity* self, const LOT& toSpawn, const uint32_t& numberToSpawn, const std::string& spawnerName) { @@ -475,16 +458,13 @@ void NjMonastryBossInstance::WaveOver(Entity* self) { if (wave >= m_Waves.size() - 1) return; - GameMessages::SendNotifyClientObject(self->GetObjectID(), PlayCinematicNotification, 0, 0, - LWOOBJID_EMPTY, BaseCounterweightSpawn + std::to_string(wave), - UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(self->GetObjectID(), PlayCinematicNotification, 0, 0, LWOOBJID_EMPTY, BaseCounterweightSpawn + std::to_string(wave)).Send(UNASSIGNED_SYSTEM_ADDRESS); ActivityTimerStart(self, SpawnCounterWeightTimer, 1.5f, 1.5f); RemovePoison(self); } void NjMonastryBossInstance::FightOver(Entity* self) { - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"GroundFrakjawDead", 0, 0, - LWOOBJID_EMPTY, "", UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(self->GetObjectID(), u"GroundFrakjawDead", 0, 0, LWOOBJID_EMPTY, "").Send(UNASSIGNED_SYSTEM_ADDRESS); // Remove all the enemies from the battlefield for (auto i = 1; i < 5; i++) { @@ -500,13 +480,9 @@ void NjMonastryBossInstance::FightOver(Entity* self) { ActivityTimerStart(self, SpawnRailTimer, 1.5f, 1.5f); // Set the music to play the victory music - GameMessages::SendNotifyClientObject(self->GetObjectID(), StopMusicNotification, 0, 0, - LWOOBJID_EMPTY, AudioWaveAudio + std::to_string(m_Waves.size() - 2), - UNASSIGNED_SYSTEM_ADDRESS); - GameMessages::SendNotifyClientObject(self->GetObjectID(), FlashMusicNotification, 0, 0, - LWOOBJID_EMPTY, "Monastery_Frakjaw_Battle_Win", UNASSIGNED_SYSTEM_ADDRESS); - GameMessages::SendNotifyClientObject(self->GetObjectID(), PlayCinematicNotification, 0, 0, - LWOOBJID_EMPTY, TreasureChestSpawning, UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(self->GetObjectID(), StopMusicNotification, 0, 0, LWOOBJID_EMPTY, AudioWaveAudio + std::to_string(m_Waves.size() - 2)).Send(UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(self->GetObjectID(), FlashMusicNotification, 0, 0, LWOOBJID_EMPTY, "Monastery_Frakjaw_Battle_Win").Send(UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(self->GetObjectID(), PlayCinematicNotification, 0, 0, LWOOBJID_EMPTY, TreasureChestSpawning).Send(UNASSIGNED_SYSTEM_ADDRESS); auto treasureChests = Game::entityManager->GetEntitiesInGroup(ChestSpawnpointGroup); for (auto* treasureChest : treasureChests) { diff --git a/dScripts/02_server/Objects/AgSurvivalBuffStation.cpp b/dScripts/02_server/Objects/AgSurvivalBuffStation.cpp index 16395a4e1..3f36abb5e 100644 --- a/dScripts/02_server/Objects/AgSurvivalBuffStation.cpp +++ b/dScripts/02_server/Objects/AgSurvivalBuffStation.cpp @@ -5,6 +5,7 @@ #include "SkillComponent.h" #include "TeamManager.h" #include "Loot.h" +#include "InventoryMessages.h" void AgSurvivalBuffStation::OnQuickBuildComplete(Entity* self, Entity* target) { auto destroyableComponent = self->GetComponent(); diff --git a/dScripts/02_server/Pets/PetFromObjectServer.cpp b/dScripts/02_server/Pets/PetFromObjectServer.cpp index 0a78d7ecd..2d3ae3418 100644 --- a/dScripts/02_server/Pets/PetFromObjectServer.cpp +++ b/dScripts/02_server/Pets/PetFromObjectServer.cpp @@ -1,6 +1,7 @@ #include "PetFromObjectServer.h" #include "PetComponent.h" #include "ePetTamingNotifyType.h" +#include "ObjectMessages.h" void PetFromObjectServer::OnStartup(Entity* self) { self->SetNetworkVar(u"pettamer", std::to_string(self->GetVar(u"tamer"))); @@ -27,8 +28,7 @@ void PetFromObjectServer::OnNotifyPetTamingMinigame(Entity* self, Entity* tamer, break; case ePetTamingNotifyType::SUCCESS: // TODO: Remove from groups? - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"UpdateSuccessPicking", 0, - 0, tamer->GetObjectID(), "", UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(self->GetObjectID(), u"UpdateSuccessPicking", 0, 0, tamer->GetObjectID(), "").Send(UNASSIGNED_SYSTEM_ADDRESS); default: break; } diff --git a/dScripts/ActivityManager.cpp b/dScripts/ActivityManager.cpp index 35459538c..49d47e40d 100644 --- a/dScripts/ActivityManager.cpp +++ b/dScripts/ActivityManager.cpp @@ -3,6 +3,7 @@ #include "ScriptedActivityComponent.h" #include "LeaderboardManager.h" #include "GameMessages.h" +#include "ObjectMessages.h" #include #include "Logger.h" #include "Loot.h" @@ -91,7 +92,7 @@ void ActivityManager::SaveScore(Entity* self, const LWOOBJID playerID, const flo LeaderboardManager::SaveScore(playerID, gameID, primaryScore, secondaryScore, tertiaryScore); // Makes the leaderboard show up for the player - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"ToggleLeaderBoard", gameID, 0, playerID, "", player->GetSystemAddress()); + GameMessages::NotifyClientObject(self->GetObjectID(), u"ToggleLeaderBoard", gameID, 0, playerID, "").Send(player->GetSystemAddress()); } bool ActivityManager::TakeActivityCost(const Entity* self, const LWOOBJID playerID) { diff --git a/dScripts/BaseConsoleTeleportServer.cpp b/dScripts/BaseConsoleTeleportServer.cpp index c64b8a569..3b2978441 100644 --- a/dScripts/BaseConsoleTeleportServer.cpp +++ b/dScripts/BaseConsoleTeleportServer.cpp @@ -2,6 +2,8 @@ #include "GameMessages.h" #include "EffectsMessages.h" #include "CombatMessages.h" +#include "ObjectMessages.h" +#include "ZoneMessages.h" #include "CharacterComponent.h" #include "RenderComponent.h" #include "EntityManager.h" @@ -69,10 +71,15 @@ void BaseConsoleTeleportServer::BaseOnMessageBoxResponse(Entity* self, Entity* s return; } - GameMessages::SendDisplayZoneSummary(playerID, player->GetSystemAddress(), false, false, self->GetObjectID()); + GameMessages::DisplayZoneSummary zoneSummary; + zoneSummary.target = playerID; + zoneSummary.isPropertyMap = false; + zoneSummary.isZoneStart = false; + zoneSummary.sender = self->GetObjectID(); + zoneSummary.Send(player->GetSystemAddress()); }); } else if (button == -1 || button == 0) { - GameMessages::SendTerminateInteraction(player->GetObjectID(), eTerminateType::FROM_INTERACTION, player->GetObjectID()); + GameMessages::TerminateInteraction(player->GetObjectID(), eTerminateType::FROM_INTERACTION, player->GetObjectID()).Send(UNASSIGNED_SYSTEM_ADDRESS); } } @@ -116,7 +123,7 @@ void BaseConsoleTeleportServer::TransferPlayer(Entity* self, Entity* player, int stun.bCantUseItem = true; stun.Send(player->GetSystemAddress()); - GameMessages::SendTerminateInteraction(player->GetObjectID(), eTerminateType::FROM_INTERACTION, player->GetObjectID()); + GameMessages::TerminateInteraction(player->GetObjectID(), eTerminateType::FROM_INTERACTION, player->GetObjectID()).Send(UNASSIGNED_SYSTEM_ADDRESS); const auto& teleportZone = self->GetVar(u"transferZoneID"); diff --git a/dScripts/BasePropertyServer.cpp b/dScripts/BasePropertyServer.cpp index ddd5bdda2..2a3cd88fa 100644 --- a/dScripts/BasePropertyServer.cpp +++ b/dScripts/BasePropertyServer.cpp @@ -1,6 +1,7 @@ #include "BasePropertyServer.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "ObjectMessages.h" #include "EntityManager.h" #include "dZoneManager.h" #include "Character.h" @@ -179,8 +180,7 @@ void BasePropertyServer::PropGuardCheck(Entity* self, Entity* player) { } void BasePropertyServer::BaseZonePropertyRented(Entity* self, Entity* player) const { - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"PlayCinematic", 0, 0, LWOOBJID_EMPTY, "ShowProperty", - UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(self->GetObjectID(), u"PlayCinematic", 0, 0, LWOOBJID_EMPTY, "ShowProperty").Send(UNASSIGNED_SYSTEM_ADDRESS); self->AddTimer(BoundsVisOnTimer, 2); self->SetVar(PropertyOwnerVariable, player->GetObjectID()); @@ -247,8 +247,7 @@ void BasePropertyServer::StartMaelstrom(Entity* self, Entity* player) { StartTornadoFx(self); - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"maelstromSkyOn", 0, 0, LWOOBJID_EMPTY, - "", player->GetSystemAddress()); + GameMessages::NotifyClientObject(self->GetObjectID(), u"maelstromSkyOn", 0, 0, LWOOBJID_EMPTY, "").Send(player->GetSystemAddress()); self->AddTimer(StartGeneratorTimer, 0.0f); self->AddTimer(StartOrbTimer, 0.0f); @@ -354,9 +353,7 @@ void BasePropertyServer::BaseTimerDone(Entity* self, const std::string& timerNam return; auto* guard = entities[0]; - GameMessages::SendNotifyClientObject(Game::entityManager->GetZoneControlEntity()->GetObjectID(), - u"GuardChat", 0, 0, guard->GetObjectID(), - "", UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(Game::entityManager->GetZoneControlEntity()->GetObjectID(), u"GuardChat", 0, 0, guard->GetObjectID(), "").Send(UNASSIGNED_SYSTEM_ADDRESS); self->AddTimer(KillGuardTimer, 5.0f); } else if (timerName == KillGuardTimer) { @@ -388,8 +385,7 @@ void BasePropertyServer::BaseTimerDone(Entity* self, const std::string& timerNam self->AddTimer(KillFXObjectTimer, 8.0f); } else if (timerName == TurnSkyOffTimer) { auto* controller = Game::zoneManager->GetZoneControlObject(); - GameMessages::SendNotifyClientObject(controller->GetObjectID(), u"SkyOff", 0, 0, - LWOOBJID_EMPTY, "", UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(controller->GetObjectID(), u"SkyOff", 0, 0, LWOOBJID_EMPTY, "").Send(UNASSIGNED_SYSTEM_ADDRESS); } else if (timerName == KillStrombiesTimer) { const auto enemies = Game::entityManager->GetEntitiesInGroup(self->GetVar(EnemiesGroup)); for (auto* enemy : enemies) { @@ -429,16 +425,13 @@ void BasePropertyServer::BaseTimerDone(Entity* self, const std::string& timerNam self->AddTimer(ShowVendorTimer, 1.0f); } else if (timerName == ShowVendorTimer) { - GameMessages::SendNotifyClientObject(Game::entityManager->GetZoneControlEntity()->GetObjectID(), - u"vendorOn", 0, 0, LWOOBJID_EMPTY, "", - UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(Game::entityManager->GetZoneControlEntity()->GetObjectID(), u"vendorOn", 0, 0, LWOOBJID_EMPTY, "").Send(UNASSIGNED_SYSTEM_ADDRESS); for (const auto& ambientFXSpawner : self->GetVar>(AmbientFXSpawner)) { ActivateSpawner(ambientFXSpawner); } } else if (timerName == BoundsVisOnTimer) { - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"boundsAnim", 0, 0, - LWOOBJID_EMPTY, "", UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(self->GetObjectID(), u"boundsAnim", 0, 0, LWOOBJID_EMPTY, "").Send(UNASSIGNED_SYSTEM_ADDRESS); } else if (timerName == RunPlayerLoadedAgainTimer) { CheckForOwner(self); } else if (timerName == PollTornadoFXTimer) { @@ -483,9 +476,7 @@ void BasePropertyServer::HandleOrbsTimer(Entity* self) { } DestroySpawner(self->GetVar(GeneratorFXSpawner)); - GameMessages::SendNotifyClientObject(Game::entityManager->GetZoneControlEntity()->GetObjectID(), - u"PlayCinematic", 0, 0, LWOOBJID_EMPTY, - "DestroyMaelstrom", UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(Game::entityManager->GetZoneControlEntity()->GetObjectID(), u"PlayCinematic", 0, 0, LWOOBJID_EMPTY, "DestroyMaelstrom").Send(UNASSIGNED_SYSTEM_ADDRESS); // Notifies the client that the property has been claimed with a flag, completes missions too auto* player = Game::entityManager->GetEntity(self->GetVar(PlayerIDVariable)); diff --git a/dScripts/BaseSurvivalServer.cpp b/dScripts/BaseSurvivalServer.cpp index 2a41b6e98..b7f545213 100644 --- a/dScripts/BaseSurvivalServer.cpp +++ b/dScripts/BaseSurvivalServer.cpp @@ -2,6 +2,8 @@ #include "GameMessages.h" #include "EffectsMessages.h" #include "ActivityMessages.h" +#include "MovementMessages.h" +#include "ObjectMessages.h" #include "DestroyableComponent.h" #include "EntityManager.h" #include "dZoneManager.h" @@ -140,8 +142,7 @@ void BaseSurvivalServer::BasePlayerDied(Entity* self, Entity* player) { SetActivityValue(self, player->GetObjectID(), 1, finalTime); auto paramString = CheckAllPlayersDead() ? "true" : "false"; - GameMessages::SendNotifyClientZoneObject(self->GetObjectID(), u"Player_Died", finalTime, 0, - player->GetObjectID(), paramString, player->GetSystemAddress()); + GameMessages::NotifyClientZoneObject(self->GetObjectID(), u"Player_Died", finalTime, 0, player->GetObjectID(), paramString).Send(player->GetSystemAddress()); GameOver(self); } else { player->Resurrect(); @@ -303,7 +304,9 @@ void BaseSurvivalServer::StartWaves(Entity* self) { TakeActivityCost(self, playerID); } - GameMessages::SendPlayerSetCameraCyclingMode(playerID, player->GetSystemAddress()); + GameMessages::PlayerSetCameraCyclingMode cameraCyclingMode; + cameraCyclingMode.target = playerID; + cameraCyclingMode.SendToClient(player->GetSystemAddress()); } } @@ -336,7 +339,7 @@ void BaseSurvivalServer::SetPlayerSpawnPoints() { auto possibleSpawners = Game::entityManager->GetEntitiesInGroup("P" + std::to_string(spawnerIndex) + "_Spawn"); if (!possibleSpawners.empty()) { auto* spawner = possibleSpawners.at(0); - GameMessages::SendTeleport(playerID, spawner->GetPosition(), spawner->GetRotation(), player->GetSystemAddress(), true); + GameMessages::Teleport(playerID, spawner->GetPosition(), spawner->GetRotation(), true).SendToClient(player->GetSystemAddress()); } } @@ -364,8 +367,7 @@ void BaseSurvivalServer::GameOver(Entity* self) { const auto time = GetActivityValue(self, playerID, 1); SaveScore(self, playerID, score, time); - GameMessages::SendNotifyClientZoneObject(self->GetObjectID(), u"Update_ScoreBoard", time, 0, - playerID, std::to_string(score), UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientZoneObject(self->GetObjectID(), u"Update_ScoreBoard", time, 0, playerID, std::to_string(score)).Send(UNASSIGNED_SYSTEM_ADDRESS); player->Resurrect(); // Update all mission progression diff --git a/dScripts/BaseWavesServer.cpp b/dScripts/BaseWavesServer.cpp index 1ff875800..e92f976be 100644 --- a/dScripts/BaseWavesServer.cpp +++ b/dScripts/BaseWavesServer.cpp @@ -3,6 +3,8 @@ #include "EffectsMessages.h" #include "CombatMessages.h" #include "ActivityMessages.h" +#include "MovementMessages.h" +#include "ObjectMessages.h" #include "DestroyableComponent.h" #include "EntityManager.h" #include "dZoneManager.h" @@ -22,7 +24,9 @@ void BaseWavesServer::SetGameVariables(Entity* self) { // Done void BaseWavesServer::BasePlayerLoaded(Entity* self, Entity* player) { - GameMessages::SendPlayerSetCameraCyclingMode(player->GetObjectID(), player->GetSystemAddress()); + GameMessages::PlayerSetCameraCyclingMode cameraCyclingMode; + cameraCyclingMode.target = player->GetObjectID(); + cameraCyclingMode.SendToClient(player->GetSystemAddress()); GameMessages::SetPlayerAllowedRespawn allowedRespawn; allowedRespawn.target = player->GetObjectID(); allowedRespawn.dontPromptForRespawn = true; @@ -132,8 +136,7 @@ void BaseWavesServer::BasePlayerDied(Entity* self, Entity* player) { auto paramString = CheckAllPlayersDead() ? "true" : "false"; - GameMessages::SendNotifyClientZoneObject(self->GetObjectID(), u"Player_Died", finalTime, finalWave, - player->GetObjectID(), paramString, player->GetSystemAddress()); + GameMessages::NotifyClientZoneObject(self->GetObjectID(), u"Player_Died", finalTime, finalWave, player->GetObjectID(), paramString).Send(player->GetSystemAddress()); if (!self->GetNetworkVar(WavesStartedVariable)) { player->Resurrect(); @@ -145,8 +148,7 @@ void BaseWavesServer::BasePlayerDied(Entity* self, Entity* player) { // Done void BaseWavesServer::BasePlayerResurrected(Entity* self, Entity* player) { - GameMessages::SendNotifyClientZoneObject(self->GetObjectID(), u"Player_Res", 0, 0, - player->GetObjectID(), "", player->GetSystemAddress()); + GameMessages::NotifyClientZoneObject(self->GetObjectID(), u"Player_Res", 0, 0, player->GetObjectID(), "").Send(player->GetSystemAddress()); if (self->GetNetworkVar(WavesStartedVariable)) return; @@ -335,7 +337,7 @@ void BaseWavesServer::SetPlayerSpawnPoints(const LWOOBJID& specificPlayerID) { auto possibleSpawners = Game::entityManager->GetEntitiesInGroup("P" + std::to_string(spawnerIndex) + "_Spawn"); if (!possibleSpawners.empty()) { auto* spawner = possibleSpawners.at(0); - GameMessages::SendTeleport(playerID, spawner->GetPosition(), spawner->GetRotation(), player->GetSystemAddress(), true); + GameMessages::Teleport(playerID, spawner->GetPosition(), spawner->GetRotation(), true).SendToClient(player->GetSystemAddress()); } } @@ -369,8 +371,7 @@ void BaseWavesServer::GameOver(Entity* self, bool won) { const auto time = GetActivityValue(self, playerID, 1); const auto wave = GetActivityValue(self, playerID, 2); - GameMessages::SendNotifyClientZoneObject(self->GetObjectID(), u"Update_ScoreBoard", time, 0, - playerID, std::to_string(wave), UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientZoneObject(self->GetObjectID(), u"Update_ScoreBoard", time, 0, playerID, std::to_string(wave)).Send(UNASSIGNED_SYSTEM_ADDRESS); if (won) { SetPlayerSpawnPoints(); diff --git a/dScripts/ChooseYourDestinationNsToNt.cpp b/dScripts/ChooseYourDestinationNsToNt.cpp index 47a70578f..e8edde0ec 100644 --- a/dScripts/ChooseYourDestinationNsToNt.cpp +++ b/dScripts/ChooseYourDestinationNsToNt.cpp @@ -2,6 +2,7 @@ #include "Character.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "ObjectMessages.h" #include "eTerminateType.h" bool ChooseYourDestinationNsToNt::CheckChoice(Entity* self, Entity* player) { @@ -67,6 +68,6 @@ void ChooseYourDestinationNsToNt::BaseChoiceBoxRespond(Entity* self, Entity* sen messageBox.userData = u""; messageBox.Send(sender->GetSystemAddress()); } else { - GameMessages::SendTerminateInteraction(sender->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID()); + GameMessages::TerminateInteraction(sender->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID()).Send(UNASSIGNED_SYSTEM_ADDRESS); } } diff --git a/dScripts/CppScripts.h b/dScripts/CppScripts.h index 5ba4cee2f..b45e5d730 100644 --- a/dScripts/CppScripts.h +++ b/dScripts/CppScripts.h @@ -11,6 +11,10 @@ enum class eMissionState : int32_t; enum class ePetTamingNotifyType : uint32_t; enum class eQuickBuildState : uint32_t; +namespace GameMessages { + struct ShootingGalleryFire; +}; + namespace CppScripts { /** * Base class for all scripts. Includes virtual methods to be overridden to handle LUA equivelent events. diff --git a/dScripts/Darkitect.cpp b/dScripts/Darkitect.cpp index 2881bde89..08fa15676 100644 --- a/dScripts/Darkitect.cpp +++ b/dScripts/Darkitect.cpp @@ -3,13 +3,14 @@ #include "DestroyableComponent.h" #include "EntityManager.h" #include "GameMessages.h" +#include "ObjectMessages.h" #include "Character.h" #include "eMissionState.h" void Darkitect::Reveal(Entity* self, Entity* player) { const auto playerID = player->GetObjectID(); - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"reveal", 0, 0, playerID, "", player->GetSystemAddress()); + GameMessages::NotifyClientObject(self->GetObjectID(), u"reveal", 0, 0, playerID, "").Send(player->GetSystemAddress()); self->AddCallbackTimer(20, [this, self, playerID]() { auto* player = Game::entityManager->GetEntity(playerID); diff --git a/dScripts/NtFactionSpyServer.cpp b/dScripts/NtFactionSpyServer.cpp index ab2f85a41..5772e2be6 100644 --- a/dScripts/NtFactionSpyServer.cpp +++ b/dScripts/NtFactionSpyServer.cpp @@ -4,6 +4,7 @@ #include "InventoryComponent.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "ObjectMessages.h" #include "MissionComponent.h" #include "eMissionState.h" #include "eReplicaComponentType.h" @@ -87,9 +88,7 @@ void NtFactionSpyServer::OnCinematicUpdate(Entity* self, Entity* sender, eCinema if (event == eCinematicEvent::STARTED && pathIndex >= 0 && pathIndex < dialogueTable.size()) { // If the cinematic started, show part of the conversation - GameMessages::SendNotifyClientObject(self->GetObjectID(), m_SpyDialogueNotification, 0, - 0, ParamObjectForConversationID(self, dialogueTable.at(pathIndex).conversationID), - dialogueTable.at(pathIndex).token, sender->GetSystemAddress()); + GameMessages::NotifyClientObject(self->GetObjectID(), m_SpyDialogueNotification, 0, 0, ParamObjectForConversationID(self, dialogueTable.at(pathIndex).conversationID), dialogueTable.at(pathIndex).token).Send(sender->GetSystemAddress()); } else if (event == eCinematicEvent::ENDED && pathIndex >= dialogueTable.size() - 1) { auto spyData = self->GetVar(m_SpyDataVariable); diff --git a/dScripts/ScriptedPowerupSpawner.cpp b/dScripts/ScriptedPowerupSpawner.cpp index 8539b3632..9af6c4262 100644 --- a/dScripts/ScriptedPowerupSpawner.cpp +++ b/dScripts/ScriptedPowerupSpawner.cpp @@ -2,6 +2,7 @@ #include "RenderComponent.h" #include "EntityManager.h" #include "Loot.h" +#include "InventoryMessages.h" void ScriptedPowerupSpawner::OnTemplateStartup(Entity* self) { self->SetVar(u"currentCycle", 1); diff --git a/dScripts/SpawnPetBaseServer.cpp b/dScripts/SpawnPetBaseServer.cpp index 345727e43..a86226583 100644 --- a/dScripts/SpawnPetBaseServer.cpp +++ b/dScripts/SpawnPetBaseServer.cpp @@ -1,6 +1,7 @@ #include "SpawnPetBaseServer.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "ObjectMessages.h" #include "EntityManager.h" #include "PetComponent.h" #include "EntityInfo.h" @@ -46,7 +47,7 @@ void SpawnPetBaseServer::OnUse(Entity* self, Entity* user) { cinematic.Send(UNASSIGNED_SYSTEM_ADDRESS); } - GameMessages::SendTerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID()); + GameMessages::TerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID()).Send(UNASSIGNED_SYSTEM_ADDRESS); } bool SpawnPetBaseServer::CheckNumberOfPets(Entity* self, Entity* user) { diff --git a/dScripts/ai/AG/AgBusDoor.cpp b/dScripts/ai/AG/AgBusDoor.cpp index e74e8af88..69c9c3e8c 100644 --- a/dScripts/ai/AG/AgBusDoor.cpp +++ b/dScripts/ai/AG/AgBusDoor.cpp @@ -2,6 +2,7 @@ #include "Entity.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "MovementMessages.h" #include "ProximityMonitorComponent.h" void AgBusDoor::OnStartup(Entity* self) { @@ -49,9 +50,9 @@ void AgBusDoor::OnProximityUpdate(Entity* self, Entity* entering, std::string na void AgBusDoor::MoveDoor(Entity* self, bool bOpen) { if (bOpen) { - GameMessages::SendPlatformResync(self, UNASSIGNED_SYSTEM_ADDRESS, true, 1, 0); + GameMessages::PlatformResync(*self, true, 1, 0).Send(UNASSIGNED_SYSTEM_ADDRESS); } else { - GameMessages::SendPlatformResync(self, UNASSIGNED_SYSTEM_ADDRESS, true, 0, 1); + GameMessages::PlatformResync(*self, true, 0, 1).Send(UNASSIGNED_SYSTEM_ADDRESS); self->AddTimer("dustTimer", 2.0f); } diff --git a/dScripts/ai/AG/AgImagSmashable.cpp b/dScripts/ai/AG/AgImagSmashable.cpp index 7d67cc299..52918b9b5 100644 --- a/dScripts/ai/AG/AgImagSmashable.cpp +++ b/dScripts/ai/AG/AgImagSmashable.cpp @@ -6,6 +6,7 @@ #include "DestroyableComponent.h" #include "eReplicaComponentType.h" #include "Loot.h" +#include "InventoryMessages.h" void AgImagSmashable::OnDie(Entity* self, Entity* killer) { bool maxImagGreaterThanZero = false; diff --git a/dScripts/ai/AG/AgJetEffectServer.cpp b/dScripts/ai/AG/AgJetEffectServer.cpp index 0344a2f49..52d0d7e18 100644 --- a/dScripts/ai/AG/AgJetEffectServer.cpp +++ b/dScripts/ai/AG/AgJetEffectServer.cpp @@ -1,6 +1,7 @@ #include "AgJetEffectServer.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "ObjectMessages.h" #include "EntityManager.h" #include "SkillComponent.h" #include "eReplicaComponentType.h" @@ -8,9 +9,7 @@ void AgJetEffectServer::OnUse(Entity* self, Entity* user) { if (inUse || self->GetLOT() != 6859) return; - GameMessages::SendNotifyClientObject( - self->GetObjectID(), u"toggleInUse", 1, 0, LWOOBJID_EMPTY, "", UNASSIGNED_SYSTEM_ADDRESS - ); + GameMessages::NotifyClientObject(self->GetObjectID(), u"toggleInUse", 1, 0, LWOOBJID_EMPTY, "").Send(UNASSIGNED_SYSTEM_ADDRESS); inUse = true; auto entities = Game::entityManager->GetEntitiesInGroup("Jet_FX"); @@ -53,9 +52,7 @@ void AgJetEffectServer::OnTimerDone(Entity* self, std::string timerName) { auto* skillComponent = mortar->GetComponent(); if (skillComponent) skillComponent->CastSkill(318); } else if (timerName == "CineDone") { - GameMessages::SendNotifyClientObject( - self->GetObjectID(), u"toggleInUse", -1, 0, LWOOBJID_EMPTY, "", UNASSIGNED_SYSTEM_ADDRESS - ); + GameMessages::NotifyClientObject(self->GetObjectID(), u"toggleInUse", -1, 0, LWOOBJID_EMPTY, "").Send(UNASSIGNED_SYSTEM_ADDRESS); inUse = false; } } diff --git a/dScripts/ai/AG/AgPicnicBlanket.cpp b/dScripts/ai/AG/AgPicnicBlanket.cpp index fd3ced79b..3361b4257 100644 --- a/dScripts/ai/AG/AgPicnicBlanket.cpp +++ b/dScripts/ai/AG/AgPicnicBlanket.cpp @@ -1,11 +1,13 @@ #include "AgPicnicBlanket.h" #include "Loot.h" #include "GameMessages.h" +#include "ObjectMessages.h" #include "Entity.h" #include "eTerminateType.h" +#include "InventoryMessages.h" void AgPicnicBlanket::OnUse(Entity* self, Entity* user) { - GameMessages::SendTerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID()); + GameMessages::TerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID()).Send(UNASSIGNED_SYSTEM_ADDRESS); if (self->GetVar(u"active")) return; self->SetVar(u"active", true); diff --git a/dScripts/ai/AG/AgQbElevator.cpp b/dScripts/ai/AG/AgQbElevator.cpp index 07cd7e614..d7d127773 100644 --- a/dScripts/ai/AG/AgQbElevator.cpp +++ b/dScripts/ai/AG/AgQbElevator.cpp @@ -1,6 +1,7 @@ #include "AgQbElevator.h" #include "EntityManager.h" #include "GameMessages.h" +#include "MovementMessages.h" void AgQbElevator::OnStartup(Entity* self) { @@ -14,8 +15,7 @@ void AgQbElevator::OnQuickBuildComplete(Entity* self, Entity* target) { float delayTime = killTime - endTime; if (delayTime < 1) delayTime = 1; - GameMessages::SendPlatformResync(self, UNASSIGNED_SYSTEM_ADDRESS, true, 0, - 0, 0, eMovementPlatformState::Stationary); + GameMessages::PlatformResync(*self, true, 0, 0, 0, eMovementPlatformState::Stationary).Send(UNASSIGNED_SYSTEM_ADDRESS); //add a timer that will kill the QB if no players get on in the killTime self->AddTimer("startKillTimer", killTime); @@ -32,8 +32,7 @@ void AgQbElevator::OnProximityUpdate(Entity* self, Entity* entering, std::string self->SetBoolean(u"qbPlayerRdy", true); self->CancelTimer("StartElevator"); - GameMessages::SendPlatformResync(self, UNASSIGNED_SYSTEM_ADDRESS, true, 0, - 1, 1, eMovementPlatformState::Moving); + GameMessages::PlatformResync(*self, true, 0, 1, 1, eMovementPlatformState::Moving).Send(UNASSIGNED_SYSTEM_ADDRESS); } else if (!self->GetBoolean(u"StartTimer")) { self->SetBoolean(u"StartTimer", true); self->AddTimer("StartElevator", startTime); @@ -44,8 +43,7 @@ void AgQbElevator::OnProximityUpdate(Entity* self, Entity* entering, std::string void AgQbElevator::OnTimerDone(Entity* self, std::string timerName) { if (timerName == "StartElevator") { - GameMessages::SendPlatformResync(self, UNASSIGNED_SYSTEM_ADDRESS, true, 0, - 1, 1, eMovementPlatformState::Moving); + GameMessages::PlatformResync(*self, true, 0, 1, 1, eMovementPlatformState::Moving).Send(UNASSIGNED_SYSTEM_ADDRESS); } else if (timerName == "startKillTimer") { killTimerStartup(self); } else if (timerName == "KillTimer") { diff --git a/dScripts/ai/AG/AgShipPlayerShockServer.cpp b/dScripts/ai/AG/AgShipPlayerShockServer.cpp index a439f0bde..44f2220fc 100644 --- a/dScripts/ai/AG/AgShipPlayerShockServer.cpp +++ b/dScripts/ai/AG/AgShipPlayerShockServer.cpp @@ -2,12 +2,13 @@ #include "GameMessages.h" #include "EffectsMessages.h" #include "CombatMessages.h" +#include "ObjectMessages.h" #include "RenderComponent.h" #include "Entity.h" #include "eTerminateType.h" void AgShipPlayerShockServer::OnUse(Entity* self, Entity* user) { - GameMessages::SendTerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID()); + GameMessages::TerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID()).Send(UNASSIGNED_SYSTEM_ADDRESS); if (active) { return; } diff --git a/dScripts/ai/AG/AgTurret.cpp b/dScripts/ai/AG/AgTurret.cpp index 823ab96b2..c5dba4a80 100644 --- a/dScripts/ai/AG/AgTurret.cpp +++ b/dScripts/ai/AG/AgTurret.cpp @@ -1,5 +1,6 @@ #include "AgTurret.h" #include "GameMessages.h" +#include "MovementMessages.h" void AgTurret::OnStartup(Entity* self) { // TODO: do this legit way @@ -13,5 +14,8 @@ void AgTurret::OnTimerDone(Entity* self, std::string timerName) { } void AgTurret::OnQuickBuildStart(Entity* self, Entity* user) { - GameMessages::SendLockNodeRotation(self, "base"); + GameMessages::LockNodeRotation lockNodeRotation; + lockNodeRotation.target = self->GetObjectID(); + lockNodeRotation.nodeName = "base"; + lockNodeRotation.Send(UNASSIGNED_SYSTEM_ADDRESS); } diff --git a/dScripts/ai/FV/FvConsoleLeftQuickbuild.cpp b/dScripts/ai/FV/FvConsoleLeftQuickbuild.cpp index 64bf5c968..9c7470f12 100644 --- a/dScripts/ai/FV/FvConsoleLeftQuickbuild.cpp +++ b/dScripts/ai/FV/FvConsoleLeftQuickbuild.cpp @@ -1,6 +1,7 @@ #include "FvConsoleLeftQuickbuild.h" #include "EntityManager.h" #include "GameMessages.h" +#include "ObjectMessages.h" #include "eTerminateType.h" #include "eQuickBuildState.h" @@ -45,5 +46,5 @@ void FvConsoleLeftQuickbuild::OnUse(Entity* self, Entity* user) { } } - GameMessages::SendTerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID()); + GameMessages::TerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID()).Send(UNASSIGNED_SYSTEM_ADDRESS); } diff --git a/dScripts/ai/FV/FvConsoleRightQuickbuild.cpp b/dScripts/ai/FV/FvConsoleRightQuickbuild.cpp index 9703c3c42..f369ca995 100644 --- a/dScripts/ai/FV/FvConsoleRightQuickbuild.cpp +++ b/dScripts/ai/FV/FvConsoleRightQuickbuild.cpp @@ -1,6 +1,7 @@ #include "FvConsoleRightQuickbuild.h" #include "EntityManager.h" #include "GameMessages.h" +#include "ObjectMessages.h" #include "eTerminateType.h" #include "eQuickBuildState.h" @@ -45,5 +46,5 @@ void FvConsoleRightQuickbuild::OnUse(Entity* self, Entity* user) { } } - GameMessages::SendTerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID()); + GameMessages::TerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID()).Send(UNASSIGNED_SYSTEM_ADDRESS); } diff --git a/dScripts/ai/FV/FvPandaSpawnerServer.cpp b/dScripts/ai/FV/FvPandaSpawnerServer.cpp index e02bcb553..f61890df6 100644 --- a/dScripts/ai/FV/FvPandaSpawnerServer.cpp +++ b/dScripts/ai/FV/FvPandaSpawnerServer.cpp @@ -2,6 +2,7 @@ #include "Character.h" #include "EntityManager.h" #include "GameMessages.h" +#include "ObjectMessages.h" #include "EntityInfo.h" #include "ScriptedActivityComponent.h" @@ -21,16 +22,14 @@ void FvPandaSpawnerServer::OnCollisionPhantom(Entity* self, Entity* target) { // If the player already spawned a panda auto playerPandas = Game::entityManager->GetEntitiesInGroup("panda" + std::to_string(target->GetObjectID())); if (!playerPandas.empty()) { - GameMessages::SendFireEventClientSide(self->GetObjectID(), target->GetSystemAddress(), u"playerPanda", - target->GetObjectID(), 0, 0, target->GetObjectID()); + GameMessages::FireEventClientSide(self->GetObjectID(), u"playerPanda", target->GetObjectID(), target->GetObjectID()).SendToClient(target->GetSystemAddress()); return; } // If there's already too many spawned pandas auto pandas = Game::entityManager->GetEntitiesInGroup("pandas"); if (pandas.size() > 4) { - GameMessages::SendFireEventClientSide(self->GetObjectID(), target->GetSystemAddress(), u"tooManyPandas", - target->GetObjectID(), 0, 0, target->GetObjectID()); + GameMessages::FireEventClientSide(self->GetObjectID(), u"tooManyPandas", target->GetObjectID(), target->GetObjectID()).SendToClient(target->GetSystemAddress()); return; } diff --git a/dScripts/ai/GENERAL/InstanceExitTransferPlayerToLastNonInstance.cpp b/dScripts/ai/GENERAL/InstanceExitTransferPlayerToLastNonInstance.cpp index 00792dcfd..6d7c4bb2d 100644 --- a/dScripts/ai/GENERAL/InstanceExitTransferPlayerToLastNonInstance.cpp +++ b/dScripts/ai/GENERAL/InstanceExitTransferPlayerToLastNonInstance.cpp @@ -1,6 +1,7 @@ #include "InstanceExitTransferPlayerToLastNonInstance.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "ObjectMessages.h" #include "CharacterComponent.h" #include "Character.h" #include "dServer.h" @@ -50,7 +51,7 @@ void InstanceExitTransferPlayerToLastNonInstance::OnMessageBoxResponse(Entity* s } } - GameMessages::SendTerminateInteraction(sender->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID()); + GameMessages::TerminateInteraction(sender->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID()).Send(UNASSIGNED_SYSTEM_ADDRESS); } diff --git a/dScripts/ai/MINIGAME/SG_GF/SERVER/SGCannon.cpp b/dScripts/ai/MINIGAME/SG_GF/SERVER/SGCannon.cpp index 93a61eb93..2a889eb86 100644 --- a/dScripts/ai/MINIGAME/SG_GF/SERVER/SGCannon.cpp +++ b/dScripts/ai/MINIGAME/SG_GF/SERVER/SGCannon.cpp @@ -3,6 +3,7 @@ #include "GameMessages.h" #include "EffectsMessages.h" #include "ActivityMessages.h" +#include "MovementMessages.h" #include "dZoneManager.h" #include "Character.h" #include "ShootingGalleryComponent.h" @@ -19,6 +20,7 @@ #include "eGameActivity.h" #include "Item.h" #include +#include "BuildingMessages.h" void SGCannon::OnStartup(Entity* self) { LOG("OnStartup"); @@ -314,7 +316,7 @@ void SGCannon::DoSpawnTimerFunc(Entity* self, const std::string& name) { // Save the enemy and tell it to start pathing if (enemy != nullptr) { const_cast&>(self->GetVar>(SpawnedObjects)).push_back(enemy->GetObjectID()); - GameMessages::SendPlatformResync(enemy, UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::PlatformResync(*enemy).Send(UNASSIGNED_SYSTEM_ADDRESS); } } } @@ -713,12 +715,13 @@ void SGCannon::RegisterHit(Entity* self, Entity* target, const std::string& time UpdateStreak(self); - GameMessages::SendNotifyClientShootingGalleryScore(self->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS, - 0.0f, - score, - target->GetObjectID(), - target->GetPosition() - ); + GameMessages::NotifyClientShootingGalleryScore galleryScore; + galleryScore.target = self->GetObjectID(); + galleryScore.addTime = 0.0f; + galleryScore.score = score; + galleryScore.targetID = target->GetObjectID(); + galleryScore.targetPos = target->GetPosition(); + galleryScore.Send(UNASSIGNED_SYSTEM_ADDRESS); auto newScore = self->GetVar(TotalScoreVariable) + score; diff --git a/dScripts/ai/NS/NS_PP_01/PropertyDeathPlane.cpp b/dScripts/ai/NS/NS_PP_01/PropertyDeathPlane.cpp index 8f8906d38..39f7b7cea 100644 --- a/dScripts/ai/NS/NS_PP_01/PropertyDeathPlane.cpp +++ b/dScripts/ai/NS/NS_PP_01/PropertyDeathPlane.cpp @@ -1,6 +1,7 @@ #include "PropertyDeathPlane.h" #include "Entity.h" #include "GameMessages.h" +#include "MovementMessages.h" #include "EntityManager.h" void PropertyDeathPlane::OnCollisionPhantom(Entity* self, Entity* target) { @@ -12,5 +13,5 @@ void PropertyDeathPlane::OnCollisionPhantom(Entity* self, Entity* target) { auto* teleport = teleportGroup[0]; - GameMessages::SendTeleport(target->GetObjectID(), teleport->GetPosition(), teleport->GetRotation(), target->GetSystemAddress()); + GameMessages::Teleport(target->GetObjectID(), teleport->GetPosition(), teleport->GetRotation()).SendToClient(target->GetSystemAddress()); } diff --git a/dScripts/ai/NS/NsConcertInstrument.cpp b/dScripts/ai/NS/NsConcertInstrument.cpp index 3f910ef37..2189fbd78 100644 --- a/dScripts/ai/NS/NsConcertInstrument.cpp +++ b/dScripts/ai/NS/NsConcertInstrument.cpp @@ -1,6 +1,8 @@ #include "NsConcertInstrument.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "MovementMessages.h" +#include "ObjectMessages.h" #include "Item.h" #include "DestroyableComponent.h" #include "EntityManager.h" @@ -71,8 +73,7 @@ void NsConcertInstrument::OnTimerDone(Entity* self, std::string name) { } if (activePlayer != nullptr && name == "checkPlayer" && self->GetVar(u"beingPlayed")) { - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"checkMovement", 0, 0, - activePlayer->GetObjectID(), "", UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(self->GetObjectID(), u"checkMovement", 0, 0, activePlayer->GetObjectID(), "").Send(UNASSIGNED_SYSTEM_ADDRESS); auto* stats = activePlayer->GetComponent(); if (stats) { if (stats->GetImagination() > 0) { @@ -90,8 +91,7 @@ void NsConcertInstrument::OnTimerDone(Entity* self, std::string name) { } else if (name == "cleanupAfterStop") { if (activePlayer != nullptr) { UnEquipInstruments(self, activePlayer); - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"stopPlaying", 0, 0, - activePlayer->GetObjectID(), "", UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(self->GetObjectID(), u"stopPlaying", 0, 0, activePlayer->GetObjectID(), "").Send(UNASSIGNED_SYSTEM_ADDRESS); } auto* quickBuildComponent = self->GetComponent(); @@ -120,8 +120,7 @@ void NsConcertInstrument::StartPlayingInstrument(Entity* self, Entity* player) { // Stuff to notify the player EquipInstruments(self, player); - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"startPlaying", 0, 0, - player->GetObjectID(), "", UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(self->GetObjectID(), u"startPlaying", 0, 0, player->GetObjectID(), "").Send(UNASSIGNED_SYSTEM_ADDRESS); GameMessages::PlayCinematic cinematic; cinematic.target = player->GetObjectID(); cinematic.pathName = cinematics.at(instrumentLot); @@ -163,8 +162,7 @@ void NsConcertInstrument::StopPlayingInstrument(Entity* self, Entity* player) { endCinematic.leadOut = 1.0f; endCinematic.Send(UNASSIGNED_SYSTEM_ADDRESS); RenderComponent::PlayAnimation(player, smashAnimations.at(instrumentLot), 2.0f); - GameMessages::SendNotifyClientObject(self->GetObjectID(), u"stopCheckingMovement", 0, 0, - player->GetObjectID(), "", UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(self->GetObjectID(), u"stopCheckingMovement", 0, 0, player->GetObjectID(), "").Send(UNASSIGNED_SYSTEM_ADDRESS); } self->SetVar(u"beingPlayed", false); @@ -288,7 +286,7 @@ void NsConcertInstrument::RepositionPlayer(Entity* self, Entity* player) { break; } - GameMessages::SendTeleport(player->GetObjectID(), position, rotation, player->GetSystemAddress()); + GameMessages::Teleport(player->GetObjectID(), position, rotation).SendToClient(player->GetSystemAddress()); } InstrumentLot NsConcertInstrument::GetInstrumentLot(Entity* self) { diff --git a/dScripts/ai/NS/NsQbImaginationStatue.cpp b/dScripts/ai/NS/NsQbImaginationStatue.cpp index 2a0b21077..35b79000e 100644 --- a/dScripts/ai/NS/NsQbImaginationStatue.cpp +++ b/dScripts/ai/NS/NsQbImaginationStatue.cpp @@ -2,6 +2,7 @@ #include "EntityManager.h" #include "GameMessages.h" #include "Loot.h" +#include "InventoryMessages.h" void NsQbImaginationStatue::OnStartup(Entity* self) { diff --git a/dScripts/ai/NS/WH/RockHydrantBroken.cpp b/dScripts/ai/NS/WH/RockHydrantBroken.cpp index 887254219..4eb435940 100644 --- a/dScripts/ai/NS/WH/RockHydrantBroken.cpp +++ b/dScripts/ai/NS/WH/RockHydrantBroken.cpp @@ -3,6 +3,7 @@ #include "EntityManager.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "ObjectMessages.h" void RockHydrantBroken::OnStartup(Entity* self) { self->AddTimer("playEffect", 1); @@ -20,7 +21,7 @@ void RockHydrantBroken::OnStartup(Entity* self) { msg.bActive = true; msg.Send(UNASSIGNED_SYSTEM_ADDRESS); - GameMessages::SendNotifyObject(bouncer->GetObjectID(), self->GetObjectID(), u"enableCollision", UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyObject(bouncer->GetObjectID(), self->GetObjectID(), u"enableCollision").Send(UNASSIGNED_SYSTEM_ADDRESS); } self->AddTimer("KillBroken", 10); @@ -36,7 +37,7 @@ void RockHydrantBroken::OnTimerDone(Entity* self, std::string timerName) { msg.bActive = false; msg.Send(UNASSIGNED_SYSTEM_ADDRESS); - GameMessages::SendNotifyObject(bouncer->GetObjectID(), self->GetObjectID(), u"disableCollision", UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyObject(bouncer->GetObjectID(), self->GetObjectID(), u"disableCollision").Send(UNASSIGNED_SYSTEM_ADDRESS); } self->Kill(); diff --git a/dScripts/ai/PROPERTY/AgPropguards.cpp b/dScripts/ai/PROPERTY/AgPropguards.cpp index 7d88c3790..bd69ead18 100644 --- a/dScripts/ai/PROPERTY/AgPropguards.cpp +++ b/dScripts/ai/PROPERTY/AgPropguards.cpp @@ -2,6 +2,7 @@ #include "Character.h" #include "GameMessages.h" #include "EffectsMessages.h" +#include "ObjectMessages.h" #include "EntityManager.h" #include "dZoneManager.h" #include "eMissionState.h" @@ -25,8 +26,7 @@ void AgPropguards::OnMissionDialogueOK(Entity* self, Entity* target, int mission } else if (missionState == eMissionState::READY_TO_COMPLETE) { // Makes the guard disappear once the mission has been completed const auto zoneControlID = Game::entityManager->GetZoneControlEntity()->GetObjectID(); - GameMessages::SendNotifyClientObject(zoneControlID, u"GuardChat", 0, 0, self->GetObjectID(), - "", UNASSIGNED_SYSTEM_ADDRESS); + GameMessages::NotifyClientObject(zoneControlID, u"GuardChat", 0, 0, self->GetObjectID(), "").Send(UNASSIGNED_SYSTEM_ADDRESS); self->AddCallbackTimer(5.0f, [self]() { auto spawnerName = self->GetVar(u"spawner_name"); diff --git a/dWorldServer/WorldServer.cpp b/dWorldServer/WorldServer.cpp index 5be9e2885..86b6df697 100644 --- a/dWorldServer/WorldServer.cpp +++ b/dWorldServer/WorldServer.cpp @@ -103,6 +103,8 @@ #include "eFunnessTypes.h" #include "WorldMigration.h" #include "EffectsMessages.h" +#include "MovementMessages.h" +#include "ZoneMessages.h" namespace Game { Logger* logger = nullptr; @@ -990,7 +992,11 @@ void LoadPlayer(const SystemAddress& sysAddr) { Game::entityManager->ConstructEntity(player, UNASSIGNED_SYSTEM_ADDRESS); if (respawnPoint != NiPoint3Constant::ZERO) { - GameMessages::SendPlayerReachedRespawnCheckpoint(player, respawnPoint, QuatUtils::IDENTITY); + GameMessages::PlayerReachedRespawnCheckpoint respawnCheckpoint; + respawnCheckpoint.target = player->GetObjectID(); + respawnCheckpoint.pos = respawnPoint; + respawnCheckpoint.rot = QuatUtils::IDENTITY; + respawnCheckpoint.SendToClient(player->GetSystemAddress()); } Game::entityManager->ConstructAllEntities(sysAddr); @@ -1046,8 +1052,16 @@ void LoadPlayer(const SystemAddress& sysAddr) { noBBB: // Tell the client it's done loading: - GameMessages::SendInvalidZoneTransferList(player, sysAddr, GeneralUtils::ASCIIToUTF16(Game::config->GetValue("source")), u"", false, false); - GameMessages::SendServerDoneLoadingAllObjects(player, sysAddr); + GameMessages::InvalidZoneTransferList invalidTransferList; + invalidTransferList.target = player->GetObjectID(); + invalidTransferList.customerFeedbackURL = GeneralUtils::ASCIIToUTF16(Game::config->GetValue("source")); + invalidTransferList.invalidMapTransferList = u""; + invalidTransferList.bCustomerFeedbackOnExit = false; + invalidTransferList.bCustomerFeedbackOnInvalidMapTransfer = false; + invalidTransferList.SendToClient(sysAddr); + GameMessages::ServerDoneLoadingAllObjects doneLoading; + doneLoading.target = player->GetObjectID(); + doneLoading.SendToClient(sysAddr); //Send the player it's mail count: //update: this might not be needed so im going to try disabling this here. diff --git a/tests/dCommonTests/HeaderSkipTest.cpp b/tests/dCommonTests/HeaderSkipTest.cpp deleted file mode 100644 index a8f78078c..000000000 --- a/tests/dCommonTests/HeaderSkipTest.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#include - -#include "dCommonDependencies.h" -#include "dCommonVars.h" -#include "BitStream.h" - -#define PacketUniquePtr std::unique_ptr - -TEST(dCommonTests, HeaderSkipExcessTest) { - PacketUniquePtr packet = std::make_unique(); - unsigned char headerAndData[] = { 0x53, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00 }; // positive - packet->data = headerAndData; - packet->length = sizeof(headerAndData); - CINSTREAM_SKIP_HEADER; - ASSERT_EQ(inStream.GetNumberOfUnreadBits(), 64); - ASSERT_EQ(inStream.GetNumberOfBitsAllocated(), 128); -} - -TEST(dCommonTests, HeaderSkipExactDataTest) { - PacketUniquePtr packet = std::make_unique(); - unsigned char header[] = { 0x53, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00 }; // positive - packet->data = header; - packet->length = sizeof(header); - CINSTREAM_SKIP_HEADER; - ASSERT_EQ(inStream.GetNumberOfUnreadBits(), 0); - ASSERT_EQ(inStream.GetNumberOfBitsAllocated(), 64); -} - -TEST(dCommonTests, HeaderSkipNotEnoughDataTest) { - PacketUniquePtr packet = std::make_unique(); - unsigned char notEnoughData[] = { 0x53, 0x02, 0x00, 0x07, 0x00, 0x00 }; // negative - packet->data = notEnoughData; - packet->length = sizeof(notEnoughData); - CINSTREAM_SKIP_HEADER; - ASSERT_EQ(inStream.GetNumberOfUnreadBits(), 0); - ASSERT_EQ(inStream.GetNumberOfBitsAllocated(), 48); -} diff --git a/tests/dGameTests/LegacyPacketMacros.h b/tests/dGameTests/LegacyPacketMacros.h new file mode 100644 index 000000000..6c1d89670 --- /dev/null +++ b/tests/dGameTests/LegacyPacketMacros.h @@ -0,0 +1,41 @@ +#ifndef LEGACYPACKETMACROS_H +#define LEGACYPACKETMACROS_H + +// FROZEN - DO NOT EDIT. +// The packet macros and the free BitStreamUtils::WriteHeader the hand written packet code used (dCommonVars.h and +// dNet/BitStreamUtils.h before the struct conversion). They were removed from the server once every packet became a +// struct; the frozen oracles in the Legacy/ folders still use them, verbatim. + +#include "BitStream.h" +#include "BitStreamUtils.h" +#include "Game.h" +#include "MessageIdentifiers.h" +#include "MessageType/Client.h" +#include "ServiceType.h" +#include "dServer.h" + +#ifndef HEADER_SIZE +#define HEADER_SIZE 8 +#endif +#ifndef CBITSTREAM +#define CBITSTREAM RakNet::BitStream bitStream; +#define CINSTREAM RakNet::BitStream inStream(packet->data, packet->length, false); +#define CINSTREAM_SKIP_HEADER CINSTREAM if (inStream.GetNumberOfUnreadBits() >= BYTES_TO_BITS(HEADER_SIZE)) inStream.IgnoreBytes(HEADER_SIZE); else inStream.IgnoreBits(inStream.GetNumberOfUnreadBits()); +#define CMSGHEADER BitStreamUtils::WriteHeader(bitStream, ServiceType::CLIENT, MessageType::Client::GAME_MSG); +#define SEND_PACKET Game::server->Send(bitStream, sysAddr, false); +#define SEND_PACKET_BROADCAST Game::server->Send(bitStream, UNASSIGNED_SYSTEM_ADDRESS, true); +#endif + +#ifndef BITSTREAMUTILS_HAS_WRITEHEADER +namespace BitStreamUtils { + template + void WriteHeader(RakNet::BitStream& bitStream, ServiceType connectionType, T internalPacketID) { + bitStream.Write(ID_USER_PACKET_ENUM); + bitStream.Write(connectionType); + bitStream.Write(static_cast(internalPacketID)); + bitStream.Write(0); + } +} +#endif + +#endif // LEGACYPACKETMACROS_H diff --git a/tests/dGameTests/dGameMessagesTests/CMakeLists.txt b/tests/dGameTests/dGameMessagesTests/CMakeLists.txt index 551dc0637..561531b2a 100644 --- a/tests/dGameTests/dGameMessagesTests/CMakeLists.txt +++ b/tests/dGameTests/dGameMessagesTests/CMakeLists.txt @@ -10,6 +10,7 @@ SET(DGAMEMESSAGES_TESTS "PetMessagesTests.cpp" "PropertyMessagesTests.cpp" "RacingMessagesTests.cpp" + "RemainingMessagesTests.cpp" "SkillMessagesTests.cpp" "UnSmashTests.cpp" "VendorMessagesTests.cpp" diff --git a/tests/dGameTests/dGameMessagesTests/GameMsgSplitTests.cpp b/tests/dGameTests/dGameMessagesTests/GameMsgSplitTests.cpp index e4fc00a39..51d0a6bb6 100644 --- a/tests/dGameTests/dGameMessagesTests/GameMsgSplitTests.cpp +++ b/tests/dGameTests/dGameMessagesTests/GameMsgSplitTests.cpp @@ -1,4 +1,5 @@ #include "GameMessages.h" +#include "InventoryMessages.h" #include "GameDependencies.h" #include "PacketTestUtils.h" #include "Entity.h" diff --git a/tests/dGameTests/dGameMessagesTests/Legacy/RemainingMessagesLegacy.h b/tests/dGameTests/dGameMessagesTests/Legacy/RemainingMessagesLegacy.h new file mode 100644 index 000000000..e80c009ce --- /dev/null +++ b/tests/dGameTests/dGameMessagesTests/Legacy/RemainingMessagesLegacy.h @@ -0,0 +1,1230 @@ +#ifndef REMAININGMESSAGESLEGACY_H +#define REMAININGMESSAGESLEGACY_H + +// FROZEN ORACLE - DO NOT EDIT. +// Verbatim copies of the last hand written GameMessages::Send* functions (dGame/dGameMessages/GameMessages.cpp) +// and WorldMigration's SendLocalizedAnnouncement, replaced by the Movement, Zone, Player, Script and QuickBuild +// message files and the rest of ActivityMessages. Only the namespace changed. The Read* functions are the read +// sequences of the replaced GameMessages::Handle* functions, verbatim up to the point where the handler starts +// using what it read. + +#include "LegacyPacketMacros.h" +#include "dCommonVars.h" +#include "dServer.h" +#include "Entity.h" +#include "Game.h" +#include "GameMessages.h" +#include "GeneralUtils.h" +#include "LeaderboardManager.h" +#include "MovementMessages.h" +#include "MessageType/Client.h" +#include "MessageType/Game.h" +#include "NiPoint3.h" +#include "NiQuaternion.h" +#include "ServiceType.h" +#include "eControlScheme.h" +#include "eCyclingMode.h" +#include "eLootSourceType.h" +#include "eMatchUpdate.h" +#include "eMovementPlatformState.h" +#include "eObjectWorldState.h" +#include "eQuickBuildFailReason.h" +#include "eQuickBuildState.h" +#include "eTerminateType.h" +#include "eGameMasterLevel.h" + +#include + +namespace LegacyGameMessages { + inline void SendFireEventClientSide(const LWOOBJID& objectID, const SystemAddress& sysAddr, std::u16string args, const LWOOBJID& object, int64_t param1, int param2, const LWOOBJID& sender) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(objectID); + bitStream.Write(MessageType::Game::FIRE_EVENT_CLIENT_SIDE); + + //bitStream.Write(args); + uint32_t argSize = args.size(); + bitStream.Write(argSize); + for (uint32_t k = 0; k < argSize; k++) { + bitStream.Write(args[k]); + } + bitStream.Write(object); + bitStream.Write0(); + //bitStream.Write(param1); + bitStream.Write0(); + //bitStream.Write(param2); + bitStream.Write(sender); + + SEND_PACKET; + } + + inline void SendTeleport(const LWOOBJID& objectID, const NiPoint3& pos, const NiQuaternion& rot, const SystemAddress& sysAddr, bool bSetRotation) { + CBITSTREAM; + CMSGHEADER; + bitStream.Write(objectID); + bitStream.Write(MessageType::Game::TELEPORT); + + bool bIgnoreY = (pos.y == 0.0f); + bool bUseNavmesh = false; + bool bSkipAllChecks = false; + //float w = 1.0f; + //float x = 0.0f; + //float y = 0.0f; + //float z = 0.0f; + + bitStream.Write(bIgnoreY); + bitStream.Write(bSetRotation); + bitStream.Write(bSkipAllChecks); + bitStream.Write(pos.x); + bitStream.Write(pos.y); + bitStream.Write(pos.z); + bitStream.Write(bUseNavmesh); + + bitStream.Write(rot.w != 1.0f); + if (rot.w != 1.0f) bitStream.Write(rot.w); + + bitStream.Write(rot.x); + bitStream.Write(rot.y); + bitStream.Write(rot.z); + + SEND_PACKET; + } + + inline void SendPlayerReady(Entity* entity, const SystemAddress& sysAddr) { + CBITSTREAM; + CMSGHEADER; + bitStream.Write(entity->GetObjectID()); + bitStream.Write(MessageType::Game::PLAYER_READY); + SEND_PACKET; + } + + inline void SendInvalidZoneTransferList(Entity* entity, const SystemAddress& sysAddr, const std::u16string& feedbackURL, const std::u16string& invalidMapTransferList, bool feedbackOnExit, bool feedbackOnInvalidTransfer) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(entity->GetObjectID()); + bitStream.Write(MessageType::Game::INVALID_ZONE_TRANSFER_LIST); + + uint32_t CustomerFeedbackURLLength = feedbackURL.size(); + bitStream.Write(CustomerFeedbackURLLength); + for (uint32_t k = 0; k < CustomerFeedbackURLLength; k++) { + bitStream.Write(feedbackURL[k]); + } + + uint32_t InvalidMapTransferListLength = invalidMapTransferList.size(); + bitStream.Write(InvalidMapTransferListLength); + for (uint32_t k = 0; k < InvalidMapTransferListLength; k++) { + bitStream.Write(invalidMapTransferList[k]); + } + + bitStream.Write(feedbackOnExit); + bitStream.Write(feedbackOnInvalidTransfer); + + SEND_PACKET; + } + + inline void SendPlayerSetCameraCyclingMode(const LWOOBJID& objectID, const SystemAddress& sysAddr, + bool bAllowCyclingWhileDeadOnly, eCyclingMode cyclingMode) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(objectID); + bitStream.Write(MessageType::Game::PLAYER_SET_CAMERA_CYCLING_MODE); + + bitStream.Write(bAllowCyclingWhileDeadOnly); + + bitStream.Write(cyclingMode != eCyclingMode::ALLOW_CYCLE_TEAMMATES); + if (cyclingMode != eCyclingMode::ALLOW_CYCLE_TEAMMATES) { + bitStream.Write(cyclingMode); + } + + SEND_PACKET; + } + + inline void SendStartPathing(Entity* entity) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(entity->GetObjectID()); + bitStream.Write(MessageType::Game::START_PATHING); + + SEND_PACKET_BROADCAST; + } + + inline void SendPlatformResync(Entity* entity, const SystemAddress& sysAddr, bool bStopAtDesiredWaypoint, + int iIndex, int iDesiredWaypointIndex, int nextIndex, + eMovementPlatformState movementState, bool special) { + CBITSTREAM; + CMSGHEADER; + + const auto objID = entity->GetObjectID(); + const auto lot = entity->GetLOT(); + + if (lot == 12341 || lot == 5027 || lot == 5028 || lot == 14335 || lot == 14447 || lot == 14449 || lot == 11306 || lot == 11308 || lot == 9483) { + iDesiredWaypointIndex = (lot == 11306 || lot == 11308) ? 1 : 0; + iIndex = lot == 9483 ? 1 : 0; + nextIndex = lot == 9483 && !special ? 1 : 0; + bStopAtDesiredWaypoint = true; + movementState = lot == 9483 && !special ? eMovementPlatformState::Stopped : eMovementPlatformState::Stationary; + } + + bitStream.Write(entity->GetObjectID()); + bitStream.Write(MessageType::Game::PLATFORM_RESYNC); + + bool bReverse = false; + int eCommand = 0; + int eUnexpectedCommand = 0; + float fIdleTimeElapsed = 0.0f; + float fMoveTimeElapsed = 0.0f; + float fPercentBetweenPoints = 0.0f; + NiPoint3 ptUnexpectedLocation = NiPoint3Constant::ZERO; + NiQuaternion qUnexpectedRotation = QuatUtils::IDENTITY; + + bitStream.Write(bReverse); + bitStream.Write(bStopAtDesiredWaypoint); + bitStream.Write(eCommand); + bitStream.Write(static_cast(movementState)); + bitStream.Write(eUnexpectedCommand); + bitStream.Write(fIdleTimeElapsed); + bitStream.Write(fMoveTimeElapsed); + bitStream.Write(fPercentBetweenPoints); + bitStream.Write(iDesiredWaypointIndex); + bitStream.Write(iIndex); + bitStream.Write(nextIndex); + bitStream.Write(ptUnexpectedLocation.x); + bitStream.Write(ptUnexpectedLocation.y); + bitStream.Write(ptUnexpectedLocation.z); + + bitStream.Write(qUnexpectedRotation != QuatUtils::IDENTITY); + if (qUnexpectedRotation != QuatUtils::IDENTITY) { + bitStream.Write(qUnexpectedRotation.x); + bitStream.Write(qUnexpectedRotation.y); + bitStream.Write(qUnexpectedRotation.z); + bitStream.Write(qUnexpectedRotation.w); + } + + if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; + SEND_PACKET; + } + + inline void SendRestoreToPostLoadStats(Entity* entity, const SystemAddress& sysAddr) { + CBITSTREAM; + CMSGHEADER; + bitStream.Write(entity->GetObjectID()); + bitStream.Write(MessageType::Game::RESTORE_TO_POST_LOAD_STATS); + SEND_PACKET; + } + + inline void SendServerDoneLoadingAllObjects(Entity* entity, const SystemAddress& sysAddr) { + CBITSTREAM; + CMSGHEADER; + bitStream.Write(entity->GetObjectID()); + bitStream.Write(MessageType::Game::SERVER_DONE_LOADING_ALL_OBJECTS); + SEND_PACKET; + } + + inline void SendChatModeUpdate(const LWOOBJID& objectID, eGameMasterLevel level) { + CBITSTREAM; + CMSGHEADER; + bitStream.Write(objectID); + bitStream.Write(MessageType::Game::UPDATE_CHAT_MODE); + bitStream.Write(level); + SEND_PACKET_BROADCAST; + } + + inline void SendGMLevelBroadcast(const LWOOBJID& objectID, eGameMasterLevel level) { + CBITSTREAM; + CMSGHEADER; + bitStream.Write(objectID); + bitStream.Write(MessageType::Game::SET_GM_LEVEL); + bitStream.Write1(); + bitStream.Write(level); + SEND_PACKET_BROADCAST; + } + + inline void SendChangeObjectWorldState(const LWOOBJID& objectID, eObjectWorldState state, const SystemAddress& sysAddr) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(objectID); + bitStream.Write(MessageType::Game::CHANGE_OBJECT_WORLD_STATE); + bitStream.Write(state); + + if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST + SEND_PACKET; + } + + inline void SendModifyLEGOScore(Entity* entity, const SystemAddress& sysAddr, int64_t score, eLootSourceType sourceType) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(entity->GetObjectID()); + bitStream.Write(MessageType::Game::MODIFY_LEGO_SCORE); + bitStream.Write(score); + + bitStream.Write(sourceType != eLootSourceType::NONE); + if (sourceType != eLootSourceType::NONE) bitStream.Write(sourceType); + + SEND_PACKET; + } + + inline void SendSetCurrency(Entity* entity, int64_t currency, int lootType, const LWOOBJID& sourceID, const LOT& sourceLOT, int sourceTradeID, bool overrideCurrent, eLootSourceType sourceType) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(entity->GetObjectID()); + bitStream.Write(MessageType::Game::SET_CURRENCY); + + bitStream.Write(currency); + + bitStream.Write(lootType != LOOTTYPE_NONE); + if (lootType != LOOTTYPE_NONE) bitStream.Write(lootType); + + bitStream.Write(NiPoint3Constant::ZERO); + + bitStream.Write(sourceLOT != LOT_NULL); + if (sourceLOT != LOT_NULL) bitStream.Write(sourceLOT); + + bitStream.Write(sourceID != LWOOBJID_EMPTY); + if (sourceID != LWOOBJID_EMPTY) bitStream.Write(sourceID); + + bitStream.Write(sourceTradeID != LWOOBJID_EMPTY); + if (sourceTradeID != LWOOBJID_EMPTY) bitStream.Write(sourceTradeID); + + bitStream.Write(sourceType != eLootSourceType::NONE); + if (sourceType != eLootSourceType::NONE) bitStream.Write(sourceType); + + SystemAddress sysAddr = entity->GetSystemAddress(); + SEND_PACKET; + } + + inline void SendQuickBuildNotifyState(Entity* entity, eQuickBuildState prevState, eQuickBuildState state, const LWOOBJID& playerID) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(entity->GetObjectID()); + bitStream.Write(MessageType::Game::REBUILD_NOTIFY_STATE); + + bitStream.Write(prevState); + bitStream.Write(state); + bitStream.Write(playerID); + + SEND_PACKET_BROADCAST; + } + + inline void SendEnableQuickBuild(Entity* entity, bool enable, bool fail, bool success, eQuickBuildFailReason failReason, float duration, const LWOOBJID& playerID) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(entity->GetObjectID()); + bitStream.Write(MessageType::Game::ENABLE_REBUILD); + + bitStream.Write(enable); + bitStream.Write(fail); + bitStream.Write(success); + + bitStream.Write(failReason != eQuickBuildFailReason::NOT_GIVEN); + if (failReason != eQuickBuildFailReason::NOT_GIVEN) bitStream.Write(failReason); + + bitStream.Write(duration); + bitStream.Write(playerID); + + SEND_PACKET_BROADCAST; + } + + inline void SendTerminateInteraction(const LWOOBJID& objectID, eTerminateType type, const LWOOBJID& terminator) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(objectID); + bitStream.Write(MessageType::Game::TERMINATE_INTERACTION); + + bitStream.Write(terminator); + bitStream.Write(type); + + SEND_PACKET_BROADCAST; + } + + inline void SendSetJetPackMode(Entity* entity, bool use, bool bypassChecks, bool doHover, int effectID, float airspeed, float maxAirspeed, float verticalVelocity, int warningEffectID) { + /* historical jamesster jetpack values + if (bIsJamessterPhysics) { + fAirspeed = 75; + fMaxAirspeed = 75; + fVertVel = 15; + } + */ + + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(entity->GetObjectID()); + bitStream.Write(MessageType::Game::SET_JET_PACK_MODE); + + bitStream.Write(bypassChecks); + bitStream.Write(doHover); + bitStream.Write(use); + + bitStream.Write(effectID != -1); + if (effectID != -1) bitStream.Write(effectID); + + bitStream.Write(airspeed != 10); + if (airspeed != 10) bitStream.Write(airspeed); + + bitStream.Write(maxAirspeed != 15); + if (maxAirspeed != 15) bitStream.Write(maxAirspeed); + + bitStream.Write(verticalVelocity != 1); + if (verticalVelocity != 1) bitStream.Write(verticalVelocity); + + bitStream.Write(warningEffectID != -1); + if (warningEffectID != -1) bitStream.Write(warningEffectID); + + SEND_PACKET_BROADCAST; + } + + inline void SendSetNetworkScriptVar(Entity* entity, const SystemAddress& sysAddr, std::string data) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(entity->GetObjectID()); + bitStream.Write(MessageType::Game::SCRIPT_NETWORK_VAR_UPDATE); + + // FIXME: this is a bad place to need to do a conversion because we have no clue whether data is utf8 or plain ascii + // an this has performance implications + const auto u16Data = GeneralUtils::ASCIIToUTF16(data); + uint32_t dataSize = static_cast(u16Data.size()); + + bitStream.Write(dataSize); + for (auto value : u16Data) { + bitStream.Write(value); + } + if (dataSize > 0) bitStream.Write(0); + + if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; + SEND_PACKET; + } + + inline void SendSetPlayerControlScheme(Entity* entity, eControlScheme controlScheme) { + CBITSTREAM; + CMSGHEADER; + + bool bDelayCamSwitchIfInCinematic = true; + bool bSwitchCam = true; + + bitStream.Write(entity->GetObjectID()); + bitStream.Write(MessageType::Game::SET_PLAYER_CONTROL_SCHEME); + + bitStream.Write(bDelayCamSwitchIfInCinematic); + bitStream.Write(bSwitchCam); + + bitStream.Write(controlScheme != eControlScheme::SCHEME_A); + if (controlScheme != eControlScheme::SCHEME_A) bitStream.Write(controlScheme); + + SystemAddress sysAddr = entity->GetSystemAddress(); + SEND_PACKET; + } + + inline void SendPlayerReachedRespawnCheckpoint(Entity* entity, const NiPoint3& position, const NiQuaternion& rotation) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(entity->GetObjectID()); + bitStream.Write(MessageType::Game::PLAYER_REACHED_RESPAWN_CHECKPOINT); + + bitStream.Write(position.x); + bitStream.Write(position.y); + bitStream.Write(position.z); + + const bool bIsNotIdentity = rotation != QuatUtils::IDENTITY; + bitStream.Write(bIsNotIdentity); + + if (bIsNotIdentity) { + bitStream.Write(rotation.w); + bitStream.Write(rotation.x); + bitStream.Write(rotation.y); + bitStream.Write(rotation.z); + } + + SystemAddress sysAddr = entity->GetSystemAddress(); + SEND_PACKET; + } + + inline void SendMatchResponse(Entity* entity, const SystemAddress& sysAddr, int response) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(entity->GetObjectID()); + bitStream.Write(MessageType::Game::MATCH_RESPONSE); + bitStream.Write(response); + + SEND_PACKET; + } + + inline void SendMatchUpdate(Entity* entity, const SystemAddress& sysAddr, std::string data, eMatchUpdate type) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(entity->GetObjectID()); + bitStream.Write(MessageType::Game::MATCH_UPDATE); + bitStream.Write(data.size()); + for (char character : data) { + bitStream.Write(character); + } + if (data.size() > 0) bitStream.Write(0); + bitStream.Write(type); + + SEND_PACKET; + } + + inline void SendRequestActivitySummaryLeaderboardData(const LWOOBJID& objectID, const LWOOBJID& targetID, + const SystemAddress& sysAddr, const int32_t& gameID, + const int32_t& queryType, const int32_t& resultsEnd, + const int32_t& resultsStart, bool weekly) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(objectID); + bitStream.Write(MessageType::Game::REQUEST_ACTIVITY_SUMMARY_LEADERBOARD_DATA); + + bitStream.Write(gameID != 0); + if (gameID != 0) { + bitStream.Write(gameID); + } + + bitStream.Write(queryType != 1); + if (queryType != 1) { + bitStream.Write(queryType); + } + + bitStream.Write(resultsEnd != 10); + if (resultsEnd != 10) { + bitStream.Write(resultsEnd); + } + + bitStream.Write(resultsStart != 0); + if (resultsStart != 0) { + bitStream.Write(resultsStart); + } + + bitStream.Write(targetID); + bitStream.Write(weekly); + + SEND_PACKET; + } + + inline void SendSetShootingGalleryParams(LWOOBJID objectId, const SystemAddress& sysAddr, + float cameraFOV, + float cooldown, + float minDistance, + NiPoint3 muzzlePosOffset, + NiPoint3 playerPosOffset, + float projectileVelocity, + float timeLimit, + bool bUseLeaderboards) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(objectId); + bitStream.Write(MessageType::Game::SET_SHOOTING_GALLERY_PARAMS); + /* + bitStream.Write(cameraFOV); + bitStream.Write(cooldown); + bitStream.Write(minDistance); + bitStream.Write(muzzlePosOffset); + bitStream.Write(playerPosOffset); + bitStream.Write(projectileVelocity); + bitStream.Write(timeLimit); + bitStream.Write(bUseLeaderboards); + */ + // No clue about the order here + bitStream.Write(playerPosOffset); + bitStream.Write(projectileVelocity); + bitStream.Write(cooldown); + bitStream.Write(muzzlePosOffset); + bitStream.Write(minDistance); + bitStream.Write(cameraFOV); + bitStream.Write(bUseLeaderboards); + bitStream.Write(timeLimit); + + if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; + SEND_PACKET; + } + + inline void SendNotifyClientShootingGalleryScore(LWOOBJID objectId, const SystemAddress& sysAddr, + float addTime, + int32_t score, + LWOOBJID target, + NiPoint3 targetPos) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(objectId); + bitStream.Write(MessageType::Game::NOTIFY_CLIENT_SHOOTING_GALLERY_SCORE); + bitStream.Write(addTime); + bitStream.Write(score); + bitStream.Write(target); + bitStream.Write(targetPos); + + if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; + SEND_PACKET; + } + + inline void SendActivitySummaryLeaderboardData(const LWOOBJID& objectID, const Leaderboard* leaderboard, const SystemAddress& sysAddr) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(objectID); + bitStream.Write(MessageType::Game::SEND_ACTIVITY_SUMMARY_LEADERBOARD_DATA); + + leaderboard->Serialize(bitStream); + SEND_PACKET; + } + + inline void SendSetRailMovement(const LWOOBJID& objectID, bool pathGoForward, std::u16string pathName, + uint32_t pathStart, const SystemAddress& sysAddr, int32_t railActivatorComponentID, + LWOOBJID railActivatorObjectID) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(objectID); + bitStream.Write(MessageType::Game::SET_RAIL_MOVEMENT); + + bitStream.Write(pathGoForward); + + bitStream.Write(pathName.size()); + for (auto character : pathName) { + bitStream.Write(character); + } + + bitStream.Write(pathStart); + + const auto componentIDIsDefault = railActivatorComponentID == -1; + bitStream.Write(!componentIDIsDefault); + if (!componentIDIsDefault) + bitStream.Write(railActivatorComponentID); + + const auto activatorObjectIDIsDefault = railActivatorObjectID == LWOOBJID_EMPTY; + bitStream.Write(!activatorObjectIDIsDefault); + if (!activatorObjectIDIsDefault) + bitStream.Write(railActivatorObjectID); + + if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; + SEND_PACKET; + } + + inline void SendStartRailMovement(const LWOOBJID& objectID, std::u16string pathName, std::u16string startSound, + std::u16string loopSound, std::u16string stopSound, const SystemAddress& sysAddr, + uint32_t pathStart, bool goForward, bool damageImmune, bool noAggro, bool notifyActor, + bool showNameBillboard, bool cameraLocked, bool collisionEnabled, bool useDB, + int32_t railComponentID, LWOOBJID railActivatorObjectID) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(objectID); + bitStream.Write(MessageType::Game::START_RAIL_MOVEMENT); + + bitStream.Write(damageImmune); + bitStream.Write(noAggro); + bitStream.Write(notifyActor); + bitStream.Write(showNameBillboard); + bitStream.Write(cameraLocked); + bitStream.Write(collisionEnabled); + + bitStream.Write(loopSound.size()); + for (auto character : loopSound) { + bitStream.Write(character); + } + + bitStream.Write(goForward); + + bitStream.Write(pathName.size()); + for (auto character : pathName) { + bitStream.Write(character); + } + + const auto pathStartIsDefault = pathStart == 0; + bitStream.Write(!pathStartIsDefault); + if (!pathStartIsDefault) { + bitStream.Write(pathStart); + } + + const auto railComponentIDIsDefault = railComponentID == -1; + bitStream.Write(!railComponentIDIsDefault); + if (!railComponentIDIsDefault) { + bitStream.Write(railComponentID); + } + + const auto railObjectIDIsDefault = railActivatorObjectID == LWOOBJID_EMPTY; + bitStream.Write(!railObjectIDIsDefault); + if (!railObjectIDIsDefault) { + bitStream.Write(railActivatorObjectID); + } + + bitStream.Write(startSound.size()); + for (auto character : startSound) { + bitStream.Write(character); + } + + bitStream.Write(stopSound.size()); + for (auto character : stopSound) { + bitStream.Write(character); + } + + bitStream.Write(useDB); + + if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; + SEND_PACKET; + } + + inline void SendNotifyClientObject(const LWOOBJID& objectID, std::u16string name, int param1, int param2, const LWOOBJID& paramObj, std::string paramStr, const SystemAddress& sysAddr) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(objectID); + bitStream.Write(MessageType::Game::NOTIFY_CLIENT_OBJECT); + + bitStream.Write(name.size()); + for (auto character : name) { + bitStream.Write(character); + } + + bitStream.Write(param1); + + bitStream.Write(param2); + + bitStream.Write(paramObj); + + bitStream.Write(paramStr.size()); + for (auto character : paramStr) { + bitStream.Write(character); + } + + if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; + SEND_PACKET; + } + + inline void SendNotifyClientZoneObject(const LWOOBJID& objectID, const std::u16string& name, int param1, + int param2, const LWOOBJID& paramObj, const std::string& paramStr, + const SystemAddress& sysAddr) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(objectID); + bitStream.Write(MessageType::Game::NOTIFY_CLIENT_ZONE_OBJECT); + + bitStream.Write(name.size()); + for (const auto& character : name) { + bitStream.Write(character); + } + + bitStream.Write(param1); + bitStream.Write(param2); + bitStream.Write(paramObj); + + bitStream.Write(paramStr.size()); + for (const auto& character : paramStr) { + bitStream.Write(character); + } + + if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; + SEND_PACKET; + } + + inline void SendNotifyClientFailedPrecondition(LWOOBJID objectId, const SystemAddress& sysAddr, + const std::u16string& failedReason, int preconditionID) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(objectId); + bitStream.Write(MessageType::Game::NOTIFY_CLIENT_FAILED_PRECONDITION); + + bitStream.Write(failedReason.size()); + for (uint16_t character : failedReason) { + bitStream.Write(character); + } + + bitStream.Write(preconditionID); + + if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; + SEND_PACKET; + } + + inline void SendSetName(LWOOBJID objectID, std::u16string name, const SystemAddress& sysAddr) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(objectID); + bitStream.Write(MessageType::Game::SET_NAME); + + bitStream.Write(name.size()); + + for (size_t i = 0; i < name.size(); ++i) + bitStream.Write(name[i]); + + if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; + SEND_PACKET; + } + + inline void SendLockNodeRotation(Entity* entity, std::string nodeName) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(entity->GetObjectID()); + bitStream.Write(MessageType::Game::LOCK_NODE_ROTATION); + + bitStream.Write(nodeName.size()); + for (char character : nodeName) { + bitStream.Write(character); + } + + SEND_PACKET_BROADCAST; + } + + inline void SendOrientToAngle(LWOOBJID objectId, bool bRelativeToCurrent, float fAngle, const SystemAddress& sysAddr) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(objectId); + bitStream.Write(MessageType::Game::ORIENT_TO_ANGLE); + + bitStream.Write(bRelativeToCurrent); + bitStream.Write(fAngle); + + if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; + SEND_PACKET; + } + + inline void SendNotifyObject(LWOOBJID objectId, LWOOBJID objIDSender, std::u16string name, const SystemAddress& sysAddr, int param1, int param2) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(objectId); + bitStream.Write(MessageType::Game::NOTIFY_OBJECT); + + bitStream.Write(objIDSender); + bitStream.Write(name.size()); + for (const auto character : name) { + bitStream.Write(character); + } + + bitStream.Write(param1); + bitStream.Write(param2); + + if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; + SEND_PACKET; + } + + inline void SendTeamPickupItem(LWOOBJID objectId, LWOOBJID lootID, LWOOBJID lootOwnerID, const SystemAddress& sysAddr) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(objectId); + bitStream.Write(MessageType::Game::TEAM_PICKUP_ITEM); + + bitStream.Write(lootID); + bitStream.Write(lootOwnerID); + + if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; + SEND_PACKET; + } + + inline void SendDisplayZoneSummary(LWOOBJID objectId, const SystemAddress& sysAddr, bool isPropertyMap, bool isZoneStart, LWOOBJID sender) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(objectId); + bitStream.Write(MessageType::Game::DISPLAY_ZONE_SUMMARY); + + bitStream.Write(isPropertyMap); + bitStream.Write(isZoneStart); + bitStream.Write(sender != LWOOBJID_EMPTY); + if (sender != LWOOBJID_EMPTY) bitStream.Write(sender); + + if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; + SEND_PACKET; + } + + inline void SendSetMountInventoryID(Entity* entity, const LWOOBJID& objectID, const SystemAddress& sysAddr) { + CBITSTREAM; + CMSGHEADER; + bitStream.Write(entity->GetObjectID()); + bitStream.Write(MessageType::Game::SET_MOUNT_INVENTORY_ID); + bitStream.Write(objectID != LWOOBJID_EMPTY); + if (objectID != LWOOBJID_EMPTY) bitStream.Write(objectID); + + SEND_PACKET_BROADCAST; + } + + inline void SendUpdateReputation(const LWOOBJID objectId, const int64_t reputation, const SystemAddress& sysAddr) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(objectId); + bitStream.Write(MessageType::Game::UPDATE_REPUTATION); + + bitStream.Write(reputation); + + SEND_PACKET; + } + + inline void SendSetGravityScale(const LWOOBJID& target, const float effectScale, const SystemAddress& sysAddr) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(target); + bitStream.Write(MessageType::Game::SET_GRAVITY_SCALE); + + bitStream.Write(effectScale); + + SEND_PACKET; + } + + inline void SendForceCameraTargetCycle(Entity* entity, bool bForceCycling, eCameraTargetCyclingMode cyclingMode, LWOOBJID optionalTargetID) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(entity->GetObjectID()); + bitStream.Write(MessageType::Game::FORCE_CAMERA_TARGET_CYCLE); + bitStream.Write(bForceCycling); + bitStream.Write(cyclingMode != eCameraTargetCyclingMode::ALLOW_CYCLE_TEAMMATES); + if (cyclingMode != eCameraTargetCyclingMode::ALLOW_CYCLE_TEAMMATES) bitStream.Write(cyclingMode); + bitStream.Write(optionalTargetID); + + auto sysAddr = entity->GetSystemAddress(); + SEND_PACKET; + } + + inline void WriteWString(RakNet::BitStream& bitStream, const std::u16string& text) { + bitStream.Write(text.size()); + for (const auto character : text) bitStream.Write(character); + } + + // LocalizedAnnouncementServerToSingleClient: the client looks both strings up in its locale (falling back to + // the text itself) and shows the announcement popup, like its own instance-lock warning did in live + inline void SendLocalizedAnnouncement(Entity* player, const std::u16string& body, const std::u16string& title) { + const auto& sysAddr = player->GetSystemAddress(); + CBITSTREAM; + CMSGHEADER; + bitStream.Write(player->GetObjectID()); + bitStream.Write(MessageType::Game::LOCALIZED_ANNOUNCEMENT_SERVER_TO_SINGLE_CLIENT); + bitStream.Write(0); // body parameters (LDF), none + bitStream.Write(false); // force open chat box + bitStream.Write(true); // show the announcement + bitStream.Write(true); // and put the text in chat + WriteWString(bitStream, body); + WriteWString(bitStream, title); + bitStream.Write(0); // title parameters (LDF), none + SEND_PACKET; + } + // Read sequences of the replaced GameMessages::Handle* functions. + struct LegacyLeaderboardRequest { int32_t gameID; Leaderboard::InfoType queryType; int32_t resultsEnd; int32_t resultsStart; LWOOBJID target; bool weekly; }; + inline LegacyLeaderboardRequest ReadRequestActivitySummaryLeaderboardData(RakNet::BitStream& inStream) { + int32_t gameID = 0; + if (inStream.ReadBit()) inStream.Read(gameID); + + Leaderboard::InfoType queryType = Leaderboard::InfoType::MyStanding; + if (inStream.ReadBit()) inStream.Read(queryType); + + int32_t resultsEnd = 10; + if (inStream.ReadBit()) inStream.Read(resultsEnd); + + int32_t resultsStart = 0; + if (inStream.ReadBit()) inStream.Read(resultsStart); + + LWOOBJID target{}; + inStream.Read(target); + + bool weekly = inStream.ReadBit(); + return { gameID, queryType, resultsEnd, resultsStart, target, weekly }; + } + + struct LegacyActivityStateChangeRequest { LWOOBJID objectID; int32_t value1; int32_t value2; std::u16string stringValue; }; + inline LegacyActivityStateChangeRequest ReadActivityStateChangeRequest(RakNet::BitStream& inStream) { + LWOOBJID objectID; + inStream.Read(objectID); + + int32_t value1; + inStream.Read(value1); + + int32_t value2; + inStream.Read(value2); + + uint32_t stringValueLength; + inStream.Read(stringValueLength); + + std::u16string stringValue; + for (uint32_t i = 0; i < stringValueLength; ++i) { + uint16_t character; + inStream.Read(character); + stringValue.push_back(character); + } + return { objectID, value1, value2, stringValue }; + } + + struct LegacyShootingGalleryRotation { float angle; NiPoint3 facing; NiPoint3 muzzlePos; }; + inline LegacyShootingGalleryRotation ReadUpdateShootingGalleryRotation(RakNet::BitStream& inStream) { + float angle = 0.0f; + NiPoint3 facing = NiPoint3Constant::ZERO; + NiPoint3 muzzlePos = NiPoint3Constant::ZERO; + inStream.Read(angle); + inStream.Read(facing); + inStream.Read(muzzlePos); + return { angle, facing, muzzlePos }; + } + + struct LegacyVerifyAck { bool bDifferent; std::string sBitStream; uint32_t uiHandle; }; + inline LegacyVerifyAck ReadVerifyAck(RakNet::BitStream& inStream) { + bool bDifferent; + std::string sBitStream; + uint32_t uiHandle = 0; + + bDifferent = inStream.ReadBit(); + + uint32_t sBitStreamLength = 0; + inStream.Read(sBitStreamLength); + if (sBitStreamLength > MAX_MESSAGE_LENGTH) return {}; + for (uint64_t k = 0; k < sBitStreamLength; k++) { + uint8_t character; + inStream.Read(character); + sBitStream.push_back(character); + } + + if (inStream.ReadBit()) { + inStream.Read(uiHandle); + } + return { bDifferent, sBitStream, uiHandle }; + } + + inline LWOOBJID ReadDismountComplete(RakNet::BitStream& inStream) { + // Get the objectID from the bitstream + LWOOBJID objectId{}; + inStream.Read(objectId); + return objectId; + } + + inline LWOOBJID ReadAcknowledgePossession(RakNet::BitStream& inStream) { + bool hasObjectId{}; + inStream.Read(hasObjectId); + LWOOBJID objectId{}; + if (hasObjectId) { + inStream.Read(objectId); + } + return objectId; + } + + inline bool ReadToggleGhostReferenceOverride(RakNet::BitStream& inStream) { + bool bOverride = false; + + inStream.Read(bOverride); + return bOverride; + } + + inline NiPoint3 ReadSetGhostReferencePosition(RakNet::BitStream& inStream) { + NiPoint3 position; + + inStream.Read(position); + return position; + } + + struct LegacyParseChatMessage { int iClientState; std::u16string wsString; }; + inline LegacyParseChatMessage ReadParseChatMessage(RakNet::BitStream& inStream) { + std::u16string wsString; + int iClientState; + inStream.Read(iClientState); + + uint32_t wsStringLength; + inStream.Read(wsStringLength); + + if (wsStringLength > MAX_MESSAGE_LENGTH) { + wsStringLength = MAX_MESSAGE_LENGTH; + } + + for (uint32_t i = 0; i < wsStringLength; ++i) { + uint16_t character; + inStream.Read(character); + wsString.push_back(character); + } + return { iClientState, wsString }; + } + + struct LegacyFireEventServerSide { std::u16string args; int param1; int param2; int param3; LWOOBJID senderID; }; + inline LegacyFireEventServerSide ReadFireEventServerSide(RakNet::BitStream& inStream) { + uint32_t argsLength{}; + std::u16string args{}; + bool param1IsDefault{}; + int param1 = -1; + bool param2IsDefault{}; + int param2 = -1; + bool param3IsDefault{}; + int param3 = -1; + LWOOBJID senderID{}; + + inStream.Read(argsLength); + if (argsLength > MAX_MESSAGE_LENGTH) return {}; + for (uint32_t i = 0; i < argsLength; ++i) { + uint16_t character; + inStream.Read(character); + args.push_back(character); + } + inStream.Read(param1IsDefault); + if (param1IsDefault) inStream.Read(param1); + inStream.Read(param2IsDefault); + if (param2IsDefault) inStream.Read(param2); + inStream.Read(param3IsDefault); + if (param3IsDefault) inStream.Read(param3); + inStream.Read(senderID); + return { args, param1, param2, param3, senderID }; + } + + struct LegacyQuickBuildCancel { bool bEarlyRelease; LWOOBJID userID; }; + inline LegacyQuickBuildCancel ReadQuickBuildCancel(RakNet::BitStream& inStream) { + bool bEarlyRelease; + LWOOBJID userID; + + inStream.Read(bEarlyRelease); + inStream.Read(userID); + return { bEarlyRelease, userID }; + } + + inline unsigned int ReadPickupCurrency(RakNet::BitStream& inStream) { + unsigned int currency; + inStream.Read(currency); + return currency; + } + + struct LegacyMatchRequest { LWOOBJID activator; std::string playerChoices; int type; int value; }; + inline LegacyMatchRequest ReadMatchRequest(RakNet::BitStream& inStream) { + LWOOBJID activator; + uint32_t playerChoicesLen; + std::string playerChoices; + int type; + int value; + + inStream.Read(activator); + inStream.Read(playerChoicesLen); + if (playerChoicesLen > MAX_MESSAGE_LENGTH) return {}; + for (uint32_t i = 0; i < playerChoicesLen; ++i) { + uint16_t character; + inStream.Read(character); + playerChoices.push_back(character); + } + if (playerChoicesLen > 0) { + uint16_t nullTerm; + inStream.Read(nullTerm); + } + inStream.Read(type); + inStream.Read(value); + return { activator, playerChoices, type, value }; + } + + struct LegacyBugReport { std::string body; std::string clientVersion; std::string otherPlayer; std::string selection; }; + inline LegacyBugReport ReadReportBug(RakNet::BitStream& inStream) { + LegacyBugReport reportInfo; + + //Reading: + uint32_t messageLength; + inStream.Read(messageLength); + + if (messageLength > MAX_MESSAGE_LENGTH) return {}; + + for (uint32_t i = 0; i < (messageLength); ++i) { + uint16_t character; + inStream.Read(character); + reportInfo.body.push_back(static_cast(character)); + } + + uint32_t clientVersionLength; + inStream.Read(clientVersionLength); + if (clientVersionLength > MAX_MESSAGE_LENGTH) return {}; + for (unsigned int k = 0; k < clientVersionLength; k++) { + unsigned char character; + inStream.Read(character); + reportInfo.clientVersion.push_back(character); + } + + uint32_t nOtherPlayerIDLength; + inStream.Read(nOtherPlayerIDLength); + if (nOtherPlayerIDLength > MAX_MESSAGE_LENGTH) return {}; + for (unsigned int k = 0; k < nOtherPlayerIDLength; k++) { + unsigned char character; + inStream.Read(character); + reportInfo.otherPlayer.push_back(character); + } + + uint32_t selectionLength; + inStream.Read(selectionLength); + if (selectionLength > MAX_MESSAGE_LENGTH) return {}; + for (unsigned int k = 0; k < selectionLength; k++) { + unsigned char character; + inStream.Read(character); + reportInfo.selection.push_back(character); + } + return reportInfo; + } + + inline bool ReadCancelRailMovement(RakNet::BitStream& inStream) { + const auto immediate = inStream.ReadBit(); + return immediate; + } + + struct LegacyRailArrived { std::u16string pathName; int32_t waypointNumber; }; + inline LegacyRailArrived ReadPlayerRailArrivedNotification(RakNet::BitStream& inStream) { + uint32_t pathNameLength; + inStream.Read(pathNameLength); + if (pathNameLength > MAX_MESSAGE_LENGTH) return {}; + std::u16string pathName; + for (auto k = 0; k < pathNameLength; k++) { + uint16_t c; + inStream.Read(c); + pathName.push_back(c); + } + + int32_t waypointNumber; + inStream.Read(waypointNumber); + return { pathName, waypointNumber }; + } + + struct LegacyZoneStatistic { bool set; std::u16string statisticsName; int32_t value; LWOMAPID zone; }; + inline LegacyZoneStatistic ReadModifyPlayerZoneStatistic(RakNet::BitStream& inStream) { + const auto set = inStream.ReadBit(); + const auto statisticsName = GeneralUtils::ReadWString(inStream); + + int32_t value; + if (inStream.ReadBit()) { + inStream.Read(value); + } else { + value = 0; + } + + LWOMAPID zone; + if (inStream.ReadBit()) { + inStream.Read(zone); + } else { + zone = LWOMAPID_INVALID; + } + return { set, statisticsName, value, zone }; + } + + struct LegacyPlayerStatistic { int32_t updateID; int64_t updateValue; }; + inline LegacyPlayerStatistic ReadUpdatePlayerStatistic(RakNet::BitStream& inStream) { + int32_t updateID; + inStream.Read(updateID); + + int64_t updateValue; + if (inStream.ReadBit()) { + inStream.Read(updateValue); + } else { + updateValue = 1; + } + return { updateID, updateValue }; + } + + inline LWOOBJID ReadZoneSummaryDismissed(RakNet::BitStream& inStream) { + LWOOBJID player_id; + inStream.Read(player_id); + return player_id; + } +} + +#endif // REMAININGMESSAGESLEGACY_H diff --git a/tests/dGameTests/dGameMessagesTests/RemainingMessagesTests.cpp b/tests/dGameTests/dGameMessagesTests/RemainingMessagesTests.cpp new file mode 100644 index 000000000..425073e74 --- /dev/null +++ b/tests/dGameTests/dGameMessagesTests/RemainingMessagesTests.cpp @@ -0,0 +1,724 @@ +#include "ActivityMessages.h" +#include "GameDependencies.h" +#include "GameMessageTestUtils.h" +#include "Legacy/RemainingMessagesLegacy.h" +#include "MovementMessages.h" +#include "ObjectMessages.h" +#include "PlayerMessages.h" +#include "QuickBuildMessages.h" +#include "ZoneMessages.h" + +#include +#include +#include +#include +#include + +#include + +// Byte equality of the movement, zone, player, object, quickbuild and remaining activity game messages with the +// frozen hand written functions they replaced (Legacy/RemainingMessagesLegacy.h). +using namespace GameMessageTestUtils; + +namespace { + const std::vector g_WStrings = { u"", u"ZonePlayer", u"é中", std::u16string(300, u'x') }; + const std::vector g_Strings = { "", "base", "\xff\x80z", std::string(300, 'y') }; + const std::vector g_Ids = { LWOOBJID_EMPTY, 0x1000000000000001LL, 0x0102030405060708LL }; + const std::vector g_Ints = { 0, -1, 1, 10, std::numeric_limits::max(), std::numeric_limits::min() }; + const std::vector g_Floats = { 0.0f, 1.0f, 10.0f, 15.0f, -2.5f }; + const std::vector g_Points = { NiPoint3Constant::ZERO, NiPoint3(1.0f, 0.0f, -3.5f), NiPoint3(-100.0f, 25.0f, 1e6f) }; + const std::vector g_Rotations = { QuatUtils::IDENTITY, NiQuaternion(0.5f, 0.5f, -0.5f, 0.5f), NiQuaternion(1.0f, 0.0f, 1.0f, 0.0f) }; + + bool Bit(uint32_t mask, int i) { return (mask >> i) & 1; } + + // Compares every packet (bytes, destination, broadcast flag) the legacy call and the struct send. + void ExpectSameSends(const std::function& legacySend, const std::function& structSend) { + const auto legacyPackets = Capture(legacySend); + const auto newPackets = Capture(structSend); + ASSERT_FALSE(legacyPackets.empty()); + ASSERT_EQ(newPackets.size(), 1); + for (const auto& legacyPacket : legacyPackets) EXPECT_PACKET_EQ(FromCapture(legacyPacket), FromCapture(newPackets[0])); + EXPECT_EQ(legacyPackets[0].broadcast, newPackets[0].broadcast); + EXPECT_EQ(legacyPackets[0].sysAddr, newPackets[0].sysAddr); + } + + // The broadcast-only legacy functions compared with Send(UNASSIGNED). + void ExpectSameAsLegacyBroadcast(const std::function& legacySend, const GameMessages::NetGameMsg& msg) { + ExpectSameSends(legacySend, [&] { msg.Send(UNASSIGNED_SYSTEM_ADDRESS); }); + } + + // Reads msg's payload with a legacy read sequence and checks it consumed every bit. + template + Result ReadWithLegacy(const GameMessages::NetGameMsg& msg, const std::function& read) { + RakNet::BitStream wire; + msg.Serialize(wire); + RakNet::BitStream legacyStream(wire.GetData(), wire.GetNumberOfBytesUsed(), false); + auto result = read(legacyStream); + EXPECT_EQ(legacyStream.GetReadOffset(), wire.GetNumberOfBitsUsed()); + return result; + } +} + +class RemainingMessagesTests : public GameDependenciesTest { +protected: + void SetUp() override { SetUpDependencies(); } + void TearDown() override { TearDownDependencies(); } +}; + +// ---------------------------------------------------------------- Movement + +TEST_F(RemainingMessagesTests, TeleportMatchesLegacy) { + for (const auto target : g_Targets) { + for (const auto& pos : g_Points) { + for (const auto& rot : g_Rotations) { + for (const bool setRotation : { false, true }) { + GameMessages::Teleport msg(target, pos, rot, setRotation); + for (const auto& address : g_Addresses) { + ExpectSameSends([&] { LegacyGameMessages::SendTeleport(target, pos, rot, address, setRotation); }, [&] { msg.SendToClient(address); }); + } + const auto copy = RoundTrip(msg); + EXPECT_EQ(copy.pos, pos); + EXPECT_EQ(copy.bIgnoreY, pos.y == 0.0f); + } + } + } + } +} + +TEST_F(RemainingMessagesTests, StartPathingAndPlatformResyncMatchLegacy) { + for (const LOT lot : { 999, 12341, 9483, 11306, 6267 }) { + info.lot = lot; + Entity entity(0x1000000000000001LL, info); + ExpectSameAsLegacyBroadcast([&] { LegacyGameMessages::SendStartPathing(&entity); }, [&] { GameMessages::StartPathing msg; msg.target = entity.GetObjectID(); return msg; }()); + for (uint32_t mask = 0; mask < 4; mask++) { + for (const auto state : { eMovementPlatformState::Moving, eMovementPlatformState::Stationary, eMovementPlatformState::Stopped }) { + for (const int32_t index : { 0, 1, 5 }) { + GameMessages::PlatformResync msg(entity, Bit(mask, 0), index, index + 1, index + 2, state, Bit(mask, 1)); + ExpectSameAsLegacy([&](const SystemAddress& address) { + LegacyGameMessages::SendPlatformResync(&entity, address, Bit(mask, 0), index, index + 1, index + 2, state, Bit(mask, 1)); + }, msg); + RoundTrip(msg); + } + } + } + } + GameMessages::PlatformResync rotated; + rotated.qUnexpectedRotation = NiQuaternion(0.5f, 0.5f, -0.5f, 0.5f); + const auto copy = RoundTrip(rotated); + EXPECT_EQ(copy.qUnexpectedRotation, rotated.qUnexpectedRotation); +} + +TEST_F(RemainingMessagesTests, SmallMovementMessagesMatchLegacy) { + for (const auto target : g_Targets) { + Entity entity(target, info); + for (const float value : g_Floats) { + for (const bool relative : { false, true }) { + GameMessages::OrientToAngle orient; + orient.target = target; + orient.bRelativeToCurrent = relative; + orient.fAngle = value; + ExpectSameAsLegacy([&](const SystemAddress& address) { LegacyGameMessages::SendOrientToAngle(target, relative, value, address); }, orient); + RoundTrip(orient); + } + GameMessages::SetGravityScale gravity; + gravity.target = target; + gravity.scale = value; + ExpectSameAsLegacy([&](const SystemAddress& address) { LegacyGameMessages::SendSetGravityScale(target, value, address); }, gravity, SendMode::SendToClient); + RoundTrip(gravity); + } + for (const auto& name : g_Strings) { + GameMessages::LockNodeRotation lock; + lock.target = target; + lock.nodeName = name; + ExpectSameAsLegacyBroadcast([&] { LegacyGameMessages::SendLockNodeRotation(&entity, name); }, lock); + EXPECT_EQ(RoundTrip(lock).nodeName, name); + } + for (const auto id : g_Ids) { + GameMessages::SetMountInventoryID mount; + mount.target = target; + mount.inventoryMountID = id; + ExpectSameAsLegacyBroadcast([&] { LegacyGameMessages::SendSetMountInventoryID(&entity, id, UNASSIGNED_SYSTEM_ADDRESS); }, mount); + RoundTrip(mount); + } + for (const auto scheme : { eControlScheme::SCHEME_A, eControlScheme::SCHEME_D, static_cast(9) }) { + GameMessages::SetPlayerControlScheme msg; + msg.target = target; + msg.iScheme = scheme; + ExpectSameSends([&] { LegacyGameMessages::SendSetPlayerControlScheme(&entity, scheme); }, [&] { msg.SendToClient(entity.GetSystemAddress()); }); + RoundTrip(msg); + } + for (const auto& pos : g_Points) { + for (const auto& rot : g_Rotations) { + GameMessages::PlayerReachedRespawnCheckpoint msg; + msg.target = target; + msg.pos = pos; + msg.rot = rot; + ExpectSameSends([&] { LegacyGameMessages::SendPlayerReachedRespawnCheckpoint(&entity, pos, rot); }, [&] { msg.SendToClient(entity.GetSystemAddress()); }); + EXPECT_EQ(RoundTrip(msg).rot, rot); + } + } + for (const auto mode : { eCameraTargetCyclingMode::ALLOW_CYCLE_TEAMMATES, eCameraTargetCyclingMode::DISALLOW_CYCLING }) { + for (const auto id : g_Ids) { + for (const bool force : { false, true }) { + GameMessages::ForceCameraTargetCycle msg; + msg.target = target; + msg.bForceCycling = force; + msg.cyclingMode = mode; + msg.optionalTargetID = id; + ExpectSameSends([&] { LegacyGameMessages::SendForceCameraTargetCycle(&entity, force, mode, id); }, [&] { msg.SendToClient(entity.GetSystemAddress()); }); + RoundTrip(msg); + } + } + } + for (const auto mode : { eCyclingMode::ALLOW_CYCLE_TEAMMATES, eCyclingMode::DISALLOW_CYCLING }) { + for (const bool deadOnly : { false, true }) { + GameMessages::PlayerSetCameraCyclingMode msg; + msg.target = target; + msg.bAllowCyclingWhileDeadOnly = deadOnly; + msg.cyclingMode = mode; + ExpectSameAsLegacy([&](const SystemAddress& address) { LegacyGameMessages::SendPlayerSetCameraCyclingMode(target, address, deadOnly, mode); }, msg, SendMode::SendToClient); + RoundTrip(msg); + } + } + } +} + +TEST_F(RemainingMessagesTests, SetJetPackModeMatchesLegacy) { + Entity entity(0x0102030405060708LL, info); + for (uint32_t mask = 0; mask < 8; mask++) { + for (const int32_t effect : { -1, 167 }) { + for (const float speed : g_Floats) { + for (const int32_t warning : { -1, 5 }) { + GameMessages::SetJetPackMode msg; + msg.target = entity.GetObjectID(); + msg.bUse = Bit(mask, 0); + msg.bBypassChecks = Bit(mask, 1); + msg.bDoHover = Bit(mask, 2); + msg.effectID = effect; + msg.fAirspeed = speed; + msg.fMaxAirspeed = speed; + msg.fVertVel = speed; + msg.iWarningEffectID = warning; + ExpectSameAsLegacyBroadcast([&] { LegacyGameMessages::SendSetJetPackMode(&entity, Bit(mask, 0), Bit(mask, 1), Bit(mask, 2), effect, speed, speed, speed, warning); }, msg); + RoundTrip(msg); + } + } + } + } +} + +TEST_F(RemainingMessagesTests, RailMovementMatchesLegacy) { + for (const auto target : g_Targets) { + for (const auto& path : g_WStrings) { + for (const uint32_t start : { 0u, 3u }) { + for (const int32_t component : { -1, 0, 42 }) { + for (const auto activator : g_Ids) { + for (uint32_t mask = 0; mask < 2; mask++) { + GameMessages::SetRailMovement set; + set.target = target; + set.pathGoForward = Bit(mask, 0); + set.pathName = path; + set.pathStart = start; + set.railActivatorComponentID = component; + set.railActivatorObjectID = activator; + ExpectSameAsLegacy([&](const SystemAddress& address) { LegacyGameMessages::SendSetRailMovement(target, Bit(mask, 0), path, start, address, component, activator); }, set); + EXPECT_EQ(RoundTrip(set).pathName, path); + } + for (uint32_t mask = 0; mask < 256; mask += 37) { + GameMessages::StartRailMovement msg; + msg.target = target; + msg.pathName = path; + msg.startSound = u"start"; + msg.loopSound = path; + msg.stopSound = u""; + msg.pathStart = start; + msg.goForward = Bit(mask, 0); + msg.bDamageImmune = Bit(mask, 1); + msg.bNoAggro = Bit(mask, 2); + msg.bNotifyActor = Bit(mask, 3); + msg.bShowNameBillboard = Bit(mask, 4); + msg.bCameraLocked = Bit(mask, 5); + msg.bCollisionEnabled = Bit(mask, 6); + msg.bUseDB = Bit(mask, 7); + msg.railActivatorComponentID = component; + msg.railActivatorObjectID = activator; + ExpectSameAsLegacy([&](const SystemAddress& address) { + LegacyGameMessages::SendStartRailMovement(target, path, u"start", path, u"", address, start, Bit(mask, 0), Bit(mask, 1), Bit(mask, 2), + Bit(mask, 3), Bit(mask, 4), Bit(mask, 5), Bit(mask, 6), Bit(mask, 7), component, activator); + }, msg); + RoundTrip(msg); + } + } + } + } + } + } +} + +TEST_F(RemainingMessagesTests, InboundMovementMatchesLegacy) { + for (const auto id : g_Ids) { + GameMessages::DismountComplete dismount; + dismount.mountID = id; + EXPECT_EQ(ReadWithLegacy(dismount, LegacyGameMessages::ReadDismountComplete), id); + EXPECT_EQ(RoundTrip(dismount).mountID, id); + ExpectTruncatedFails(dismount); + + GameMessages::AcknowledgePossession ack; + ack.possessedObjID = id; + EXPECT_EQ(ReadWithLegacy(ack, LegacyGameMessages::ReadAcknowledgePossession), id); + EXPECT_EQ(RoundTrip(ack).possessedObjID, id); + ExpectTruncatedFails(ack); + } + for (const bool value : { false, true }) { + GameMessages::ToggleGhostReferenceOverride toggle; + toggle.bOverride = value; + EXPECT_EQ(ReadWithLegacy(toggle, LegacyGameMessages::ReadToggleGhostReferenceOverride), value); + ExpectTruncatedFails(toggle); + + GameMessages::CancelRailMovement cancel; + cancel.bImmediate = value; + EXPECT_EQ(ReadWithLegacy(cancel, LegacyGameMessages::ReadCancelRailMovement), value); + ExpectTruncatedFails(cancel); + } + for (const auto& pos : g_Points) { + GameMessages::SetGhostReferencePosition ghost; + ghost.pos = pos; + EXPECT_EQ(ReadWithLegacy(ghost, LegacyGameMessages::ReadSetGhostReferencePosition), pos); + ExpectTruncatedFails(ghost); + } + for (const auto& path : g_WStrings) { + for (const int32_t waypoint : g_Ints) { + GameMessages::PlayerRailArrivedNotification arrived; + arrived.pathName = path; + arrived.waypointNumber = waypoint; + const auto legacy = ReadWithLegacy(arrived, LegacyGameMessages::ReadPlayerRailArrivedNotification); + EXPECT_EQ(legacy.pathName, path); + EXPECT_EQ(legacy.waypointNumber, waypoint); + RoundTrip(arrived); + } + } +} + +// ---------------------------------------------------------------- Zone + +TEST_F(RemainingMessagesTests, ZoneMessagesMatchLegacy) { + for (const auto target : g_Targets) { + Entity entity(target, info); + GameMessages::PlayerReady ready; + ready.target = target; + ExpectSameAsLegacy([&](const SystemAddress& address) { LegacyGameMessages::SendPlayerReady(&entity, address); }, ready, SendMode::SendToClient); + GameMessages::RestoreToPostLoadStats restore; + restore.target = target; + ExpectSameAsLegacy([&](const SystemAddress& address) { LegacyGameMessages::SendRestoreToPostLoadStats(&entity, address); }, restore, SendMode::SendToClient); + GameMessages::ServerDoneLoadingAllObjects done; + done.target = target; + ExpectSameAsLegacy([&](const SystemAddress& address) { LegacyGameMessages::SendServerDoneLoadingAllObjects(&entity, address); }, done, SendMode::SendToClient); + + for (const auto& url : g_WStrings) { + for (uint32_t mask = 0; mask < 4; mask++) { + GameMessages::InvalidZoneTransferList list; + list.target = target; + list.customerFeedbackURL = url; + list.invalidMapTransferList = u"1000,1001"; + list.bCustomerFeedbackOnExit = Bit(mask, 0); + list.bCustomerFeedbackOnInvalidMapTransfer = Bit(mask, 1); + ExpectSameAsLegacy([&](const SystemAddress& address) { LegacyGameMessages::SendInvalidZoneTransferList(&entity, address, url, u"1000,1001", Bit(mask, 0), Bit(mask, 1)); }, list, SendMode::SendToClient); + RoundTrip(list); + } + GameMessages::LocalizedAnnouncementServerToSingleClient announcement; + announcement.target = target; + announcement.body = url; + announcement.title = u"UI_TITLE"; + ExpectSameSends([&] { LegacyGameMessages::SendLocalizedAnnouncement(&entity, url, u"UI_TITLE"); }, [&] { announcement.SendToClient(entity.GetSystemAddress()); }); + EXPECT_EQ(RoundTrip(announcement).body, url); + } + for (const auto sender : g_Ids) { + for (uint32_t mask = 0; mask < 4; mask++) { + GameMessages::DisplayZoneSummary summary; + summary.target = target; + summary.isPropertyMap = Bit(mask, 0); + summary.isZoneStart = Bit(mask, 1); + summary.sender = sender; + ExpectSameAsLegacy([&](const SystemAddress& address) { LegacyGameMessages::SendDisplayZoneSummary(target, address, Bit(mask, 0), Bit(mask, 1), sender); }, summary); + RoundTrip(summary); + } + GameMessages::ZoneSummaryDismissed dismissed; + dismissed.playerID = sender; + EXPECT_EQ(ReadWithLegacy(dismissed, LegacyGameMessages::ReadZoneSummaryDismissed), sender); + ExpectTruncatedFails(dismissed); + } + for (const auto state : { eObjectWorldState::INWORLD, eObjectWorldState::ATTACHED, eObjectWorldState::INVENTORY }) { + GameMessages::ChangeObjectWorldState msg; + msg.target = target; + msg.newState = state; + ExpectSameAsLegacy([&](const SystemAddress& address) { LegacyGameMessages::SendChangeObjectWorldState(target, state, address); }, msg); + RoundTrip(msg); + } + } + // Name-value text: a null terminator follows non-empty text and is not counted. + GameMessages::LocalizedAnnouncementServerToSingleClient withParams; + withParams.bodyParams = u"name=0:Bob"; + withParams.titleParams = u"x=1:2"; + const auto copy = RoundTrip(withParams); + EXPECT_EQ(copy.bodyParams, withParams.bodyParams); + EXPECT_EQ(copy.titleParams, withParams.titleParams); + ExpectTruncatedFails(withParams); +} + +TEST_F(RemainingMessagesTests, ZoneGoldenBytes) { + GameMessages::DisplayZoneSummary summary; + summary.target = 0x0102030405060708LL; + summary.isZoneStart = true; + summary.sender = 0x1122334455667788LL; + // header 53 05 00 0c 00 00 00 00, target, msgId 1043 (u16), bits 0 1 1 then the sender + EXPECT_PACKET_EQ(FromHex("5305000c00000000" "0807060504030201" "1304" "710eeccaa886644220", 211), StructPacket(summary)); +} + +// ---------------------------------------------------------------- Player + +TEST_F(RemainingMessagesTests, PlayerMessagesMatchLegacy) { + for (const auto target : g_Targets) { + Entity entity(target, info); + for (const auto level : { eGameMasterLevel::CIVILIAN, eGameMasterLevel::DEVELOPER, eGameMasterLevel::OPERATOR }) { + GameMessages::UpdateChatMode chat; + chat.target = target; + chat.level = level; + ExpectSameAsLegacyBroadcast([&] { LegacyGameMessages::SendChatModeUpdate(target, level); }, chat); + RoundTrip(chat); + GameMessages::SetGMLevel gm; + gm.target = target; + gm.level = level; + ExpectSameAsLegacyBroadcast([&] { LegacyGameMessages::SendGMLevelBroadcast(target, level); }, gm); + RoundTrip(gm); + } + for (const int64_t value : { 0LL, -5LL, 123456789012LL }) { + for (const auto source : { eLootSourceType::NONE, eLootSourceType::MISSION, eLootSourceType::PICKUP }) { + GameMessages::ModifyLEGOScore score; + score.target = target; + score.score = value; + score.sourceType = source; + ExpectSameAsLegacy([&](const SystemAddress& address) { LegacyGameMessages::SendModifyLEGOScore(&entity, address, value, source); }, score, SendMode::SendToClient); + RoundTrip(score); + for (const int32_t smallValue : { 0, 7 }) { + GameMessages::SetCurrency currency; + currency.target = target; + currency.currency = value; + currency.lootType = smallValue; + currency.sourceID = smallValue; + currency.sourceLOT = smallValue; + currency.sourceTradeID = smallValue; + currency.sourceType = source; + ExpectSameSends([&] { LegacyGameMessages::SendSetCurrency(&entity, value, smallValue, smallValue, smallValue, smallValue, true, source); }, [&] { currency.SendToClient(entity.GetSystemAddress()); }); + RoundTrip(currency); + } + } + GameMessages::UpdateReputation reputation; + reputation.target = target; + reputation.reputation = value; + ExpectSameAsLegacy([&](const SystemAddress& address) { LegacyGameMessages::SendUpdateReputation(target, value, address); }, reputation, SendMode::SendToClient); + RoundTrip(reputation); + } + } +} + +TEST_F(RemainingMessagesTests, InboundPlayerMessagesMatchLegacy) { + for (const uint32_t amount : { 0u, 1u, 0xffffffffu }) { + GameMessages::PickupCurrency pickup; + pickup.currency = amount; + EXPECT_EQ(ReadWithLegacy(pickup, LegacyGameMessages::ReadPickupCurrency), amount); + ExpectTruncatedFails(pickup); + } + for (const auto& name : g_WStrings) { + for (const int32_t value : g_Ints) { + for (const LWOMAPID zone : { LWOMAPID_INVALID, LWOMAPID{ 1100 } }) { + GameMessages::ModifyPlayerZoneStatistic stat; + stat.bSet = value > 0; + stat.statName = name; + stat.statValue = value; + stat.zoneID = zone; + const auto legacy = ReadWithLegacy(stat, LegacyGameMessages::ReadModifyPlayerZoneStatistic); + EXPECT_EQ(legacy.set, stat.bSet); + EXPECT_EQ(legacy.statisticsName, name); + EXPECT_EQ(legacy.value, value); + EXPECT_EQ(legacy.zone, zone); + RoundTrip(stat); + } + } + GameMessages::ParseChatMessage chat; + chat.iClientState = 3; + chat.wsString = name; + const auto legacyChat = ReadWithLegacy(chat, LegacyGameMessages::ReadParseChatMessage); + EXPECT_EQ(legacyChat.iClientState, 3); + EXPECT_EQ(legacyChat.wsString, name); + RoundTrip(chat); + + GameMessages::ReportBug report; + report.body = name; + report.clientVersion = "1.10.64"; + report.otherPlayerID = "0"; + report.selection = "selection"; + const auto legacyReport = ReadWithLegacy(report, LegacyGameMessages::ReadReportBug); + std::string narrowed; + for (const auto character : name) narrowed.push_back(static_cast(character)); + EXPECT_EQ(legacyReport.body, narrowed); + EXPECT_EQ(legacyReport.clientVersion, "1.10.64"); + EXPECT_EQ(legacyReport.otherPlayer, "0"); + EXPECT_EQ(legacyReport.selection, "selection"); + RoundTrip(report); + } + for (const int32_t id : g_Ints) { + for (const int64_t value : { int64_t{ 1 }, int64_t{ 0 }, int64_t{ -7 }, int64_t{ 1 } << 40 }) { + GameMessages::UpdatePlayerStatistic stat; + stat.updateID = id; + stat.updateValue = value; + const auto legacy = ReadWithLegacy(stat, LegacyGameMessages::ReadUpdatePlayerStatistic); + EXPECT_EQ(legacy.updateID, id); + EXPECT_EQ(legacy.updateValue, value); + ExpectTruncatedFails(stat); + } + } + for (const auto& bytes : g_Strings) { + for (const uint32_t handle : { 0u, 9u }) { + GameMessages::VerifyAck ack; + ack.bDifferent = handle != 0; + ack.sBitStream = bytes; + ack.uiHandle = handle; + const auto legacy = ReadWithLegacy(ack, LegacyGameMessages::ReadVerifyAck); + EXPECT_EQ(legacy.bDifferent, ack.bDifferent); + EXPECT_EQ(legacy.sBitStream, bytes); + EXPECT_EQ(legacy.uiHandle, handle); + RoundTrip(ack); + } + } +} + +// ---------------------------------------------------------------- Objects and scripts + +TEST_F(RemainingMessagesTests, NotifyMessagesMatchLegacy) { + for (const auto target : g_Targets) { + for (const auto& name : g_WStrings) { + for (const int32_t param : g_Ints) { + for (const auto& paramStr : g_Strings) { + GameMessages::NotifyClientObject notify(target, name, param, -param, target, paramStr); + ExpectSameAsLegacy([&](const SystemAddress& address) { LegacyGameMessages::SendNotifyClientObject(target, name, param, -param, target, paramStr, address); }, notify); + EXPECT_EQ(RoundTrip(notify).paramStr, paramStr); + GameMessages::NotifyClientZoneObject zoneNotify(target, name, param, -param, target, paramStr); + ExpectSameAsLegacy([&](const SystemAddress& address) { LegacyGameMessages::SendNotifyClientZoneObject(target, name, param, -param, target, paramStr, address); }, zoneNotify); + RoundTrip(zoneNotify); + } + GameMessages::NotifyObject notifyObject(target, 0x1000000000000001LL, name, param, param / 2); + ExpectSameAsLegacy([&](const SystemAddress& address) { LegacyGameMessages::SendNotifyObject(target, 0x1000000000000001LL, name, address, param, param / 2); }, notifyObject); + RoundTrip(notifyObject); + GameMessages::NotifyClientFailedPrecondition precondition; + precondition.target = target; + precondition.failedReason = name; + precondition.preconditionID = param; + ExpectSameAsLegacy([&](const SystemAddress& address) { LegacyGameMessages::SendNotifyClientFailedPrecondition(target, address, name, param); }, precondition); + RoundTrip(precondition); + } + for (const auto object : g_Ids) { + // The old function never wrote param1 or param2 (both flags always 0), whatever it was given. + GameMessages::FireEventClientSide fire(target, name, object, 0x0102030405060708LL); + ExpectSameAsLegacy([&](const SystemAddress& address) { LegacyGameMessages::SendFireEventClientSide(target, address, name, object, 5, 6, 0x0102030405060708LL); }, fire, SendMode::SendToClient); + RoundTrip(fire); + } + GameMessages::SetName setName; + setName.target = target; + setName.name = name; + ExpectSameAsLegacy([&](const SystemAddress& address) { LegacyGameMessages::SendSetName(target, name, address); }, setName); + RoundTrip(setName); + } + Entity entity(target, info); + for (const auto& data : g_Strings) { + GameMessages::ScriptNetworkVarUpdate scriptVar; + scriptVar.target = target; + scriptVar.tableOfVars = GeneralUtils::ASCIIToUTF16(data); + ExpectSameAsLegacy([&](const SystemAddress& address) { LegacyGameMessages::SendSetNetworkScriptVar(&entity, address, data); }, scriptVar); + RoundTrip(scriptVar); + } + for (const auto type : { eTerminateType::RANGE, eTerminateType::USER, eTerminateType::FROM_INTERACTION }) { + for (const auto terminator : g_Ids) { + GameMessages::TerminateInteraction terminate(target, type, terminator); + ExpectSameAsLegacyBroadcast([&] { LegacyGameMessages::SendTerminateInteraction(target, type, terminator); }, terminate); + RoundTrip(terminate); + } + } + } +} + +TEST_F(RemainingMessagesTests, FireEventServerSideMatchesLegacy) { + for (const auto& args : g_WStrings) { + for (const int32_t param : g_Ints) { + for (const auto sender : g_Ids) { + GameMessages::FireEventServerSide fire; + fire.args = args; + fire.param1 = param; + fire.param2 = -1; + fire.param3 = param / 3; + fire.senderID = sender; + const auto legacy = ReadWithLegacy(fire, LegacyGameMessages::ReadFireEventServerSide); + EXPECT_EQ(legacy.args, args); + EXPECT_EQ(legacy.param1, param); + EXPECT_EQ(legacy.param2, -1); + EXPECT_EQ(legacy.param3, param / 3); + EXPECT_EQ(legacy.senderID, sender); + RoundTrip(fire); + } + } + } + GameMessages::FireEventServerSide fire; + fire.args = u"ZonePlayer"; + fire.param3 = 1100; + ExpectTruncatedFails(fire); +} + +// ---------------------------------------------------------------- Quickbuild + +TEST_F(RemainingMessagesTests, QuickBuildMessagesMatchLegacy) { + for (const auto target : g_Targets) { + Entity entity(target, info); + for (const auto player : g_Ids) { + for (const auto state : { eQuickBuildState::OPEN, eQuickBuildState::COMPLETED, eQuickBuildState::BUILDING }) { + GameMessages::RebuildNotifyState notify; + notify.target = target; + notify.prevState = eQuickBuildState::RESETTING; + notify.state = state; + notify.player = player; + ExpectSameAsLegacyBroadcast([&] { LegacyGameMessages::SendQuickBuildNotifyState(&entity, eQuickBuildState::RESETTING, state, player); }, notify); + RoundTrip(notify); + } + for (uint32_t mask = 0; mask < 8; mask++) { + for (const auto reason : { eQuickBuildFailReason::NOT_GIVEN, eQuickBuildFailReason::CANCELED_EARLY, eQuickBuildFailReason::OUT_OF_IMAGINATION }) { + for (const float duration : g_Floats) { + GameMessages::EnableRebuild enable; + enable.target = target; + enable.bEnable = Bit(mask, 0); + enable.bFail = Bit(mask, 1); + enable.bSuccess = Bit(mask, 2); + enable.eFailReason = reason; + enable.fDuration = duration; + enable.user = player; + ExpectSameAsLegacyBroadcast([&] { LegacyGameMessages::SendEnableQuickBuild(&entity, Bit(mask, 0), Bit(mask, 1), Bit(mask, 2), reason, duration, player); }, enable); + RoundTrip(enable); + } + } + } + for (const bool early : { false, true }) { + GameMessages::RebuildCancel cancel; + cancel.bEarlyRelease = early; + cancel.userID = player; + const auto legacy = ReadWithLegacy(cancel, LegacyGameMessages::ReadQuickBuildCancel); + EXPECT_EQ(legacy.bEarlyRelease, early); + EXPECT_EQ(legacy.userID, player); + ExpectTruncatedFails(cancel); + } + } + } +} + +// ---------------------------------------------------------------- Activities + +TEST_F(RemainingMessagesTests, ActivityMessagesMatchLegacy) { + for (const auto target : g_Targets) { + Entity entity(target, info); + for (const int32_t value : g_Ints) { + GameMessages::MatchResponse response; + response.target = target; + response.response = value; + ExpectSameAsLegacy([&](const SystemAddress& address) { LegacyGameMessages::SendMatchResponse(&entity, address, value); }, response, SendMode::SendToClient); + RoundTrip(response); + } + for (const auto& data : g_Strings) { + for (const auto type : { eMatchUpdate::PLAYER_ADDED, eMatchUpdate::PHASE_WAIT_READY }) { + GameMessages::MatchUpdate update; + update.target = target; + update.data = data; + update.type = type; + ExpectSameAsLegacy([&](const SystemAddress& address) { LegacyGameMessages::SendMatchUpdate(&entity, address, data, type); }, update, SendMode::SendToClient); + EXPECT_EQ(RoundTrip(update).data, data); + + GameMessages::MatchRequest request; + request.activator = target; + request.playerChoices = data; + request.type = 1; + request.value = static_cast(type); + const auto legacy = ReadWithLegacy(request, LegacyGameMessages::ReadMatchRequest); + EXPECT_EQ(legacy.activator, target); + EXPECT_EQ(legacy.playerChoices, data); + EXPECT_EQ(legacy.type, 1); + EXPECT_EQ(legacy.value, request.value); + RoundTrip(request); + } + } + for (const auto& pos : g_Points) { + for (const int32_t score : { 0, 1500, -1 }) { + GameMessages::NotifyClientShootingGalleryScore galleryScore; + galleryScore.target = target; + galleryScore.addTime = 2.5f; + galleryScore.score = score; + galleryScore.targetID = 0x1000000000000001LL; + galleryScore.targetPos = pos; + ExpectSameAsLegacy([&](const SystemAddress& address) { LegacyGameMessages::SendNotifyClientShootingGalleryScore(target, address, 2.5f, score, 0x1000000000000001LL, pos); }, galleryScore); + RoundTrip(galleryScore); + } + GameMessages::UpdateShootingGalleryRotation rotation; + rotation.angle = 1.25f; + rotation.facing = pos; + rotation.muzzlePos = NiPoint3(1.0f, 2.0f, 3.0f); + const auto legacy = ReadWithLegacy(rotation, LegacyGameMessages::ReadUpdateShootingGalleryRotation); + EXPECT_EQ(legacy.angle, 1.25f); + EXPECT_EQ(legacy.facing, pos); + EXPECT_EQ(legacy.muzzlePos, rotation.muzzlePos); + ExpectTruncatedFails(rotation); + } + for (const auto& text : g_WStrings) { + GameMessages::ActivityStateChangeRequest change; + change.objectID = target; + change.value1 = 4; + change.value2 = -4; + change.stringValue = text; + const auto legacy = ReadWithLegacy(change, LegacyGameMessages::ReadActivityStateChangeRequest); + EXPECT_EQ(legacy.objectID, target); + EXPECT_EQ(legacy.value1, 4); + EXPECT_EQ(legacy.value2, -4); + EXPECT_EQ(legacy.stringValue, text); + RoundTrip(change); + } + } +} + +TEST_F(RemainingMessagesTests, LeaderboardMessagesMatchLegacy) { + for (const int32_t gameID : { 0, 1864 }) { + for (const int32_t queryType : { 0, 1, 2 }) { + for (const int32_t end : { 10, 0 }) { + for (const int32_t start : { 0, 5 }) { + for (const bool weekly : { false, true }) { + GameMessages::RequestActivitySummaryLeaderboardData msg; + msg.target = 0x0102030405060708LL; + msg.gameID = gameID; + msg.queryType = queryType; + msg.resultsEnd = end; + msg.resultsStart = start; + msg.weekly = weekly; + msg.targetID = 0x0102030405060708LL; + ExpectSameAsLegacy([&](const SystemAddress& address) { + LegacyGameMessages::SendRequestActivitySummaryLeaderboardData(0x0102030405060708LL, 0x0102030405060708LL, address, gameID, queryType, end, start, weekly); + }, msg, SendMode::SendToClient); + const auto legacy = ReadWithLegacy(msg, LegacyGameMessages::ReadRequestActivitySummaryLeaderboardData); + EXPECT_EQ(legacy.gameID, gameID); + EXPECT_EQ(static_cast(legacy.queryType), queryType); + EXPECT_EQ(legacy.resultsEnd, end); + EXPECT_EQ(legacy.resultsStart, start); + EXPECT_EQ(legacy.weekly, weekly); + RoundTrip(msg); + } + } + } + } + } + Leaderboard leaderboard(1864, Leaderboard::InfoType::Top, false, 0x1000000000000001LL, 10, Leaderboard::Type::ShootingGallery); + GameMessages::SendActivitySummaryLeaderboardData data; + data.target = 0x1000000000000001LL; + data.leaderboard = &leaderboard; + ExpectSameAsLegacy([&](const SystemAddress& address) { LegacyGameMessages::SendActivitySummaryLeaderboardData(0x1000000000000001LL, &leaderboard, address); }, data, SendMode::SendToClient); +}