diff --git a/dGame/dComponents/RocketLaunchpadControlComponent.cpp b/dGame/dComponents/RocketLaunchpadControlComponent.cpp index b920cb0db..06982ea4d 100644 --- a/dGame/dComponents/RocketLaunchpadControlComponent.cpp +++ b/dGame/dComponents/RocketLaunchpadControlComponent.cpp @@ -71,8 +71,9 @@ void RocketLaunchpadControlComponent::Launch(Entity* originator, LWOMAPID mapId, return; } - // we have the ability to launch, so now we prep the zone - TellMasterToPrepZone(zone); + // we have the ability to launch, so now we prep the zone: the clone the player is going to (a property), not an extra + // clone 0 instance nobody asked for + TellMasterToPrepZone(zone, cloneId == LWOCLONEID_INVALID ? 0 : cloneId); // Achievement unlocked: "All zones unlocked" if (!m_AltLandingScene.empty() && m_AltPrecondition->Check(originator)) { @@ -142,9 +143,10 @@ LWOCLONEID RocketLaunchpadControlComponent::GetSelectedCloneId(LWOOBJID player) return index->second; } -void RocketLaunchpadControlComponent::TellMasterToPrepZone(int zoneID) { +void RocketLaunchpadControlComponent::TellMasterToPrepZone(int zoneID, LWOCLONEID cloneID) { MasterPackets::PrepZone request; request.zoneID = zoneID; + request.cloneID = cloneID; MasterPackets::SendToMaster(request); } diff --git a/dGame/dComponents/RocketLaunchpadControlComponent.h b/dGame/dComponents/RocketLaunchpadControlComponent.h index ee593a894..136c267ce 100644 --- a/dGame/dComponents/RocketLaunchpadControlComponent.h +++ b/dGame/dComponents/RocketLaunchpadControlComponent.h @@ -140,7 +140,7 @@ private: * Notifies the master server to prepare some world for a player to be able to travel to it * @param zoneID the ID of the zone to prepare */ - void TellMasterToPrepZone(int zoneID); + void TellMasterToPrepZone(int zoneID, LWOCLONEID cloneID = 0); }; #endif // ROCKETLAUNCHPADCONTROLCOMPONENT_H diff --git a/dMasterServer/MasterServer.cpp b/dMasterServer/MasterServer.cpp index 96645fb6a..59ddf193c 100644 --- a/dMasterServer/MasterServer.cpp +++ b/dMasterServer/MasterServer.cpp @@ -899,8 +899,8 @@ namespace { if (shutdownSequenceStarted) { LOG("Shutdown sequence has been started. Not prepping a new zone."); } else { - LOG("Prepping zone %i", zoneID); - Game::im->GetInstance(zoneID, false, 0); + LOG("Prepping zone %i clone %u", zoneID, request.cloneID); + Game::im->GetInstance(zoneID, false, request.cloneID); } } diff --git a/dNet/MasterPackets.cpp b/dNet/MasterPackets.cpp index 22c03603a..a012671a1 100644 --- a/dNet/MasterPackets.cpp +++ b/dNet/MasterPackets.cpp @@ -201,10 +201,13 @@ namespace MasterPackets { void PrepZone::Serialize(RakNet::BitStream& bitStream) const { bitStream.Write(zoneID); + if (cloneID != 0) bitStream.Write(cloneID); } bool PrepZone::Deserialize(RakNet::BitStream& bitStream) { VALIDATE_READ(bitStream.Read(zoneID)); + cloneID = 0; + if (bitStream.GetNumberOfUnreadBits() >= 32) VALIDATE_READ(bitStream.Read(cloneID)); return true; } diff --git a/dNet/MasterPackets.h b/dNet/MasterPackets.h index b81a5cf16..cbeb4b7e6 100644 --- a/dNet/MasterPackets.h +++ b/dNet/MasterPackets.h @@ -190,9 +190,11 @@ namespace MasterPackets { bool Deserialize(RakNet::BitStream& bitStream) override; }; - // World -> master: start an instance of zoneID now, players are about to ask for it + // World -> master: start an instance of zoneID now, players are about to ask for it. cloneID: the clone they will ask + // for (a property), written only when not 0 so a plain prep stays as it always was struct PrepZone : public LUBitStream { int32_t zoneID{}; + uint32_t cloneID{}; PrepZone() : LUBitStream(ServiceType::MASTER, MessageType::Master::PREP_ZONE) {} void Serialize(RakNet::BitStream& bitStream) const override; diff --git a/tests/dGameTests/dNetTests/MasterPacketsTests.cpp b/tests/dGameTests/dNetTests/MasterPacketsTests.cpp index 8eab5cde3..5b1407e50 100644 --- a/tests/dGameTests/dNetTests/MasterPacketsTests.cpp +++ b/tests/dGameTests/dNetTests/MasterPacketsTests.cpp @@ -290,6 +290,22 @@ TEST(MasterPacketsTests, WorldStateMatchesLegacy) { RoundTrip(prep); ExpectTruncatedFails(prep); } + { + // A property's clone rides after the zone; a prep without one reads as clone 0 + MasterPackets::PrepZone prep; + prep.zoneID = 1251; + prep.cloneID = 2290; + RakNet::BitStream stream; + prep.Serialize(stream); + MasterPackets::PrepZone read; + ASSERT_TRUE(read.Deserialize(stream)); + EXPECT_EQ(read.zoneID, 1251); + EXPECT_EQ(read.cloneID, 2290u); + RakNet::BitStream plain; + plain.Write(1100); + ASSERT_TRUE(read.Deserialize(plain)); + EXPECT_EQ(read.cloneID, 0u); + } for (const auto requestID : g_RequestIds) { MasterPackets::AffirmTransferRequest request;