mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 02:43:44 +00:00
fix(rocket): RocketEquipped goes to every client, with the clone and LUP world
Live sent the launchpad's FireEventClientSide("RocketEquipped") to every
client in the zone: 1,196 of 1,265 captured were other players'
launches. On each client it starts that player's launch
(LWORocketLaunchComponentCommon::msgFireEventClientSide sends
BeginLaunch to the sender), so others see the rocket take off; DLU sent
it only to the launcher. param1 is the property clone for property
launches (7 of 7 equal the clone in the following TransferToZone) and
param2 the picked world of a LUP entrance (0-3, one pad only).
The capture notes read the RocketEquipped and ChangeObjectWorldState
seen before LEVEL_LOAD_COMPLETE as the arriving player's own rocket:
all 21 are other players launching (their sender is never the loading
player), so arrival itself is left as it is.
Check: with two players at a launchpad, launch one: the other sees the
rocket launch animation; launch to a property and to a LUP world and
land in the right place.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -34,5 +34,5 @@ void MultiZoneEntranceComponent::OnSelectWorld(Entity* originator, uint32_t inde
|
||||
auto* rocketLaunchpadControlComponent = m_Parent->GetComponent<RocketLaunchpadControlComponent>();
|
||||
if (!rocketLaunchpadControlComponent || index >= m_LUPWorlds.size()) return;
|
||||
|
||||
rocketLaunchpadControlComponent->Launch(originator, m_LUPWorlds[index], 0);
|
||||
rocketLaunchpadControlComponent->Launch(originator, m_LUPWorlds[index], 0, static_cast<int32_t>(index));
|
||||
}
|
||||
|
||||
@@ -45,7 +45,14 @@ RocketLaunchpadControlComponent::~RocketLaunchpadControlComponent() {
|
||||
delete m_AltPrecondition;
|
||||
}
|
||||
|
||||
void RocketLaunchpadControlComponent::Launch(Entity* originator, LWOMAPID mapId, LWOCLONEID cloneId) {
|
||||
GameMessages::FireEventClientSide RocketLaunchpadControlComponent::MakeRocketEquipped(const LWOOBJID launchpad, const LWOOBJID rocket, const LWOOBJID player, const LWOCLONEID cloneId, const int32_t worldIndex) {
|
||||
GameMessages::FireEventClientSide rocketEquipped(launchpad, u"RocketEquipped", rocket, player);
|
||||
if (cloneId != LWOCLONEID_INVALID) rocketEquipped.param1 = cloneId;
|
||||
rocketEquipped.param2 = worldIndex;
|
||||
return rocketEquipped;
|
||||
}
|
||||
|
||||
void RocketLaunchpadControlComponent::Launch(Entity* originator, LWOMAPID mapId, LWOCLONEID cloneId, int32_t worldIndex) {
|
||||
auto zone = mapId == LWOMAPID_INVALID ? m_TargetZone : mapId;
|
||||
|
||||
if (zone == 0) {
|
||||
@@ -79,7 +86,7 @@ void RocketLaunchpadControlComponent::Launch(Entity* originator, LWOMAPID mapId,
|
||||
SetSelectedMapId(originator->GetObjectID(), zone);
|
||||
|
||||
// Equipping the rocket (RocketEquip) sent ChangeObjectWorldState(ATTACHED); live sent it before this event
|
||||
GameMessages::FireEventClientSide(m_Parent->GetObjectID(), u"RocketEquipped", rocket->GetId(), originator->GetObjectID()).SendToClient(originator->GetSystemAddress());
|
||||
MakeRocketEquipped(m_Parent->GetObjectID(), rocket->GetId(), originator->GetObjectID(), cloneId, worldIndex).Send(UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
Game::entityManager->SerializeEntity(originator);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
#include "Component.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
namespace GameMessages {
|
||||
struct FireEventClientSide;
|
||||
}
|
||||
|
||||
class PreconditionExpression;
|
||||
|
||||
/**
|
||||
@@ -28,8 +32,17 @@ public:
|
||||
* @param originator the entity to launch
|
||||
* @param mapId the world to go to
|
||||
* @param cloneId the clone ID (for properties)
|
||||
* @param worldIndex which of a LUP entrance's worlds was picked, -1 otherwise
|
||||
*/
|
||||
void Launch(Entity* originator, LWOMAPID mapId = LWOMAPID_INVALID, LWOCLONEID cloneId = LWOCLONEID_INVALID);
|
||||
void Launch(Entity* originator, LWOMAPID mapId = LWOMAPID_INVALID, LWOCLONEID cloneId = LWOCLONEID_INVALID, int32_t worldIndex = -1);
|
||||
|
||||
/**
|
||||
* The "RocketEquipped" event live sent from the launchpad to every client in the zone when a player launched
|
||||
* (1,196 of the 1,265 captured were other players' launches): it plays the launch for that player on every
|
||||
* client (LWORocketLaunchComponentCommon::msgFireEventClientSide). param1 is the property clone (0 is left out),
|
||||
* param2 the picked LUP world index (-1 is left out).
|
||||
*/
|
||||
static GameMessages::FireEventClientSide MakeRocketEquipped(LWOOBJID launchpad, LWOOBJID rocket, LWOOBJID player, LWOCLONEID cloneId, int32_t worldIndex);
|
||||
|
||||
/**
|
||||
* Handles an OnUse event from some entity, preparing it for launch to some other world
|
||||
|
||||
@@ -315,7 +315,7 @@ the 1.10.64 client.
|
||||
| `SetStatusImmunity` | Fixed (wire fix): used to write the flags in DLU's order (BasicAttack, DOT, Knockback, Interrupt, Speed, ImaginationGain, ImaginationLoss, QuickbuildInterrupt, PullToPoint). | The client uses alphabetical order: BasicAttack, DOT, ImaginationGain, ImaginationLoss, Interrupt, Knockback, PullToPoint, QuickbuildInterrupt, Speed (`0x00d8f140`; field offsets named by `0x00d8f410`). |
|
||||
| `RequestDie` | Read with the `Die` layout. | Starts with one `bDieAccepted` bit and has a mandatory `lootOwnerID` (`0x00e02d90`). |
|
||||
| `SetCurrency` | `sourceTradeID` is an optional `int32_t`. | lu_packets has an object ID (8 bytes). DLU only ever sends 0 (flag bit 0), so no bytes differ today. |
|
||||
| `FireEventClientSide` | Never writes `param1`/`param2` (both flag bits 0), whatever the caller passed: `RocketEquipped` loses the clone ID. | Optional `i64 param1` (default 0) and `i32 param2` (default -1). |
|
||||
| `FireEventClientSide` | Fixed: writes `param1`/`param2`. `RocketEquipped` carries the property clone in `param1` and a LUP entrance's world index in `param2`, and goes to every client (live: 1,196 of 1,265 captured were other players' launches). | Optional `i64 param1` (default 0) and `i32 param2` (default -1). |
|
||||
| `PickupCurrency` | Reads only the amount. | lu_packets has a position after it (ignored, harmless). |
|
||||
| `MatchUpdate`, `MatchRequest` | Name-value text is widened/narrowed one byte per UTF-16 unit, so non-ASCII names are garbled. | UTF-16 text. |
|
||||
| `ScriptNetworkVarUpdate` | The text goes through `ASCIIToUTF16`. | UTF-16 text (non-ASCII values are garbled). |
|
||||
|
||||
@@ -14,6 +14,7 @@ set(DCOMPONENTS_TESTS
|
||||
"CollectibleComponentTests.cpp"
|
||||
"ReplicaComponentOrderTests.cpp"
|
||||
"QuickBuildCompleteTests.cpp"
|
||||
"RocketLaunchTests.cpp"
|
||||
)
|
||||
|
||||
# Get the folder name and prepend it to the files above
|
||||
|
||||
51
tests/dGameTests/dComponentsTests/RocketLaunchTests.cpp
Normal file
51
tests/dGameTests/dComponentsTests/RocketLaunchTests.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
#include "GameDependencies.h"
|
||||
#include "dGameMessagesTests/GameMessageTestUtils.h"
|
||||
|
||||
#include "ObjectMessages.h"
|
||||
#include "RocketLaunchpadControlComponent.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace GameMessageTestUtils;
|
||||
|
||||
// The launchpad's RocketEquipped event, compared with live. Each expected packet is a live one with the launchpad,
|
||||
// rocket and player IDs replaced.
|
||||
class RocketLaunchTests : public GameDependenciesTest {
|
||||
protected:
|
||||
void SetUp() override { SetUpDependencies(); }
|
||||
void TearDown() override { TearDownDependencies(); }
|
||||
|
||||
static constexpr LWOOBJID PAD = 0x0000400000112233LL;
|
||||
static constexpr LWOOBJID ROCKET = 0x1000000000000002LL;
|
||||
static constexpr LWOOBJID PLAYER = 0x1000000000000001LL;
|
||||
};
|
||||
|
||||
TEST_F(RocketLaunchTests, ZoneLaunchHasNoParameters) {
|
||||
const auto msg = RocketLaunchpadControlComponent::MakeRocketEquipped(PAD, ROCKET, PLAYER, LWOCLONEID_INVALID, -1);
|
||||
EXPECT_PACKET_EQ(FromHex(
|
||||
"53 05 00 0c 00 00 00 00 33 22 11 00 00 40 00 00 bd 04 0e 00 00 00 52 00 6f 00 63 00 6b 00 65 00 74 00 45 00 71 00 "
|
||||
"75 00 69 00 70 00 70 00 65 00 64 00 02 00 00 00 00 00 00 10 00 40 00 00 00 00 00 04 00", 530), StructPacket(msg));
|
||||
}
|
||||
|
||||
TEST_F(RocketLaunchTests, PropertyLaunchCarriesTheClone) {
|
||||
const auto msg = RocketLaunchpadControlComponent::MakeRocketEquipped(PAD, ROCKET, PLAYER, 338862, -1);
|
||||
EXPECT_PACKET_EQ(FromHex(
|
||||
"53 05 00 0c 00 00 00 00 33 22 11 00 00 40 00 00 bd 04 0e 00 00 00 52 00 6f 00 63 00 6b 00 65 00 74 00 45 00 71 00 "
|
||||
"75 00 69 00 70 00 70 00 65 00 64 00 02 00 00 00 00 00 00 10 d7 15 82 80 00 00 00 00 00 40 00 00 00 00 00 04 00", 594), StructPacket(msg));
|
||||
}
|
||||
|
||||
TEST_F(RocketLaunchTests, LupLaunchCarriesTheWorldIndex) {
|
||||
const auto msg = RocketLaunchpadControlComponent::MakeRocketEquipped(PAD, ROCKET, PLAYER, 0, 0);
|
||||
EXPECT_PACKET_EQ(FromHex(
|
||||
"53 05 00 0c 00 00 00 00 33 22 11 00 00 40 00 00 bd 04 0e 00 00 00 52 00 6f 00 63 00 6b 00 65 00 74 00 45 00 71 00 "
|
||||
"75 00 69 00 70 00 70 00 65 00 64 00 02 00 00 00 00 00 00 10 40 00 00 00 00 40 00 00 00 00 00 04 00", 562), StructPacket(msg));
|
||||
}
|
||||
|
||||
// Sent to every client (Send with UNASSIGNED_SYSTEM_ADDRESS broadcasts), as Launch does.
|
||||
TEST_F(RocketLaunchTests, RocketEquippedIsBroadcast) {
|
||||
const auto sent = Capture([&] {
|
||||
RocketLaunchpadControlComponent::MakeRocketEquipped(PAD, ROCKET, PLAYER, LWOCLONEID_INVALID, -1).Send(UNASSIGNED_SYSTEM_ADDRESS);
|
||||
});
|
||||
ASSERT_EQ(sent.size(), 1u);
|
||||
EXPECT_TRUE(sent[0].broadcast);
|
||||
}
|
||||
Reference in New Issue
Block a user