diff --git a/dGame/dComponents/CharacterComponent.h b/dGame/dComponents/CharacterComponent.h index bc4be664b..05e5b7f4b 100644 --- a/dGame/dComponents/CharacterComponent.h +++ b/dGame/dComponents/CharacterComponent.h @@ -59,6 +59,13 @@ public: */ void SetLastRocketConfig(std::u16string config); + /** + * Keeps the rocket build the client reports (SetLastCustomBuild) as char@lcbp, without changing whether the + * player lands on the next load + */ + void SetLastCustomBuild(const std::u16string& tokenizedLOTList) { m_LastRocketConfig = tokenizedLOTList; } + bool GetIsLanding() const { return m_IsLanding; } + /** * Find a player's rocket * @param player the entity that triggered the event diff --git a/dGame/dGameMessages/GameMessageHandler.cpp b/dGame/dGameMessages/GameMessageHandler.cpp index 79d18310c..b9be5e22e 100644 --- a/dGame/dGameMessages/GameMessageHandler.cpp +++ b/dGame/dGameMessages/GameMessageHandler.cpp @@ -194,6 +194,7 @@ namespace { { MODIFY_PLAYER_ZONE_STATISTIC, []() { return std::make_unique(); } }, { UPDATE_PLAYER_STATISTIC, []() { return std::make_unique(); } }, { SET_TOOLTIP_FLAG, []() { return std::make_unique(); } }, + { SET_LAST_CUSTOM_BUILD, []() { return std::make_unique(); } }, { REPORT_BUG, []() { return std::make_unique(); } }, { VERIFY_ACK, []() { return std::make_unique(); } }, diff --git a/dGame/dGameMessages/PlayerMessages.cpp b/dGame/dGameMessages/PlayerMessages.cpp index 02a43ecff..608071ce0 100644 --- a/dGame/dGameMessages/PlayerMessages.cpp +++ b/dGame/dGameMessages/PlayerMessages.cpp @@ -91,6 +91,20 @@ namespace GameMessages { if (characterComponent) characterComponent->SetTooltipFlag(iToolTip, bFlag); } + void SetLastCustomBuild::Serialize(RakNet::BitStream& bitStream) const { + BitStreamUtils::WriteLengthPrefixed(bitStream, tokenizedLOTList); + } + + bool SetLastCustomBuild::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(BitStreamUtils::ReadLengthPrefixed(bitStream, tokenizedLOTList)); + return true; + } + + void SetLastCustomBuild::Handle(Entity& entity, const SystemAddress& sysAddr) { + auto* const characterComponent = entity.GetComponent(); + if (characterComponent) characterComponent->SetLastCustomBuild(tokenizedLOTList); + } + void ToggleGMInvis::Serialize(RakNet::BitStream& bitStream) const { bitStream.Write(bStateOut); } diff --git a/dGame/dGameMessages/PlayerMessages.h b/dGame/dGameMessages/PlayerMessages.h index 038564e98..b80bb5cb9 100644 --- a/dGame/dGameMessages/PlayerMessages.h +++ b/dGame/dGameMessages/PlayerMessages.h @@ -79,6 +79,18 @@ namespace GameMessages { int32_t iToolTip{}; }; + // Client -> server. The client's rocket build ("1:LOT;1:LOT;1:LOT;"), sent when it assembles the rocket the + // player carries (at the launchpad and when landing). The client keeps it as lastUsedRocketInfo and reads it + // back from char@lcbp on load. + struct SetLastCustomBuild : public NetGameMsg { + SetLastCustomBuild() : NetGameMsg(MessageType::Game::SET_LAST_CUSTOM_BUILD) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + void Handle(Entity& entity, const SystemAddress& sysAddr) override; + + std::u16string tokenizedLOTList{}; + }; + // Server -> client, broadcast. struct ToggleGMInvis : public NetGameMsg { ToggleGMInvis() : NetGameMsg(MessageType::Game::TOGGLE_GM_INVIS) {} diff --git a/tests/dGameTests/dGameMessagesTests/ClientMessagesTests.cpp b/tests/dGameTests/dGameMessagesTests/ClientMessagesTests.cpp index 1bb16e273..dd4017b86 100644 --- a/tests/dGameTests/dGameMessagesTests/ClientMessagesTests.cpp +++ b/tests/dGameTests/dGameMessagesTests/ClientMessagesTests.cpp @@ -67,3 +67,18 @@ TEST_F(ClientMessagesTests, SetTooltipFlagFollowsTheClient) { characterComponent->SetTooltipFlag(100, false); EXPECT_EQ(characterComponent->GetTooltipFlags(), 0u); } + +// SetLastCustomBuild (890): a u32-length wide string. 202 live packets, sent when the carried rocket is assembled. +TEST_F(ClientMessagesTests, SetLastCustomBuildMatchesLiveCapture) { + auto msg = FromLiveClientCapture(std::string(PLAYER_HEADER) + "7a03" + + "1600000031003a00310034003400350034003b0031003a0034003700310034003b0031003a0034003700310035003b00"); + EXPECT_EQ(msg.tokenizedLOTList, u"1:14454;1:4714;1:4715;"); + EXPECT_EQ(RoundTrip(msg).tokenizedLOTList, msg.tokenizedLOTList); + ExpectTruncatedFails(msg); + + // Kept as the rocket config (saved as char@lcbp) without turning on the landing + auto* const characterComponent = player->GetComponent(); + msg.Handle(*player, UNASSIGNED_SYSTEM_ADDRESS); + EXPECT_EQ(characterComponent->GetLastRocketConfig(), u"1:14454;1:4714;1:4715;"); + EXPECT_FALSE(characterComponent->GetIsLanding()); +}