mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-11-14 07:47:13 +00:00
fix: write what the client reads for a chest, a choice build and a roller
The three components were made from the registry and wrote nothing, so the client read the next component's bits as theirs. - LWOChestComponent::Deserialize (0x00c22010), construction only: the player (u64) and the loot offer count (u32). - LWOChoiceBuildComponent::Deserialize (0x00bf26d0): a flag, then the state (u32, 0 waiting, 1 building), the built object and the user (u64 each). Always in a construction. - LWORollerComponent::Deserialize (0x00c15920), construction: the level's activity_camera_pos and activity_camera_lookat_pos (3 floats each) and the projectile skill (u32, the client starts at 1348); then the flag of the block about its user, which is never set. LWOGameObject::Deserialize (0x00f99690) reads the components in the order of the object's component list, which ObjectLoader2::DoObjectComponentLoad (0x0105ebb0) fills in the order of FillComponentOrder (0x0101f8e0): the roller after the shooting gallery and before the moving platform, the choice build after the moving platform and before the LUP exhibit and the model, the chest after the zone control and before render. They are placed against the components their objects have. The gate rush control (103) is left out: its two registry entries have no object, and there is no component for it. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
This commit is contained in:
@@ -760,6 +760,14 @@ namespace {
|
||||
* The order the client reads components in (it builds an object's component list in this order in
|
||||
* ObjectLoader2::DoObjectComponentLoad). CHARACTER is preceded by POSSESSOR, LEVEL_PROGRESSION and
|
||||
* PLAYER_FORCED_MOVEMENT, which WriteComponents writes with it.
|
||||
*
|
||||
* LWOGameObject::Deserialize (0x00f99690) calls each component's Deserialize in the order of the object's component
|
||||
* list, which DoObjectComponentLoad (0x0105ebb0) fills in the order of ObjectLoader2::FillComponentOrder
|
||||
* (0x0101f8e0). From that list: ROLLER (105) comes after SHOOTING_GALLERY (19) and before SCRIPTED_ACTIVITY (39) and
|
||||
* MOVING_PLATFORM (25); CHOICE_BUILD (52) after MOVING_PLATFORM and before LUP_EXHIBIT (75) and MODEL (42); CHEST (22)
|
||||
* after MINI_GAME_CONTROL (50) and before RENDER (2). They are placed here against the components their objects
|
||||
* have (roller: physics, script, skill, inventory, moving platform, render; choice build: physics, destroyable,
|
||||
* script, render; chest: phantom physics, render).
|
||||
*/
|
||||
constexpr auto SERIALIZATION_ORDER = [] {
|
||||
using enum eReplicaComponentType;
|
||||
@@ -767,8 +775,8 @@ namespace {
|
||||
POSSESSABLE, MODULE_ASSEMBLY, CONTROLLABLE_PHYSICS, SIMPLE_PHYSICS, RIGID_BODY_PHANTOM_PHYSICS, HAVOK_VEHICLE_PHYSICS,
|
||||
PHANTOM_PHYSICS, SOUND_TRIGGER, RACING_SOUND_TRIGGER, BUFF, DESTROYABLE, COLLECTIBLE, PET, PLAYER_FORCED_MOVEMENT, CHARACTER,
|
||||
INVENTORY,
|
||||
SCRIPT, SKILL, BASE_COMBAT_AI, ITEM, QUICK_BUILD, MOVING_PLATFORM, SWITCH, VENDOR, DONATION_VENDOR, ACHIEVEMENT_VENDOR,
|
||||
BOUNCER, SCRIPTED_ACTIVITY, SHOOTING_GALLERY, RACING_CONTROL, LUP_EXHIBIT, MODEL, RENDER, MINI_GAME_CONTROL, BBB,
|
||||
SCRIPT, SKILL, BASE_COMBAT_AI, ITEM, QUICK_BUILD, ROLLER, MOVING_PLATFORM, SWITCH, VENDOR, DONATION_VENDOR, ACHIEVEMENT_VENDOR,
|
||||
BOUNCER, SCRIPTED_ACTIVITY, SHOOTING_GALLERY, RACING_CONTROL, CHOICE_BUILD, LUP_EXHIBIT, MODEL, CHEST, RENDER, MINI_GAME_CONTROL, BBB,
|
||||
};
|
||||
}();
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ set(DGAME_DCOMPONENTS_SOURCES
|
||||
"ProximityMonitorComponent.cpp"
|
||||
"Activity/RacingControlComponent.cpp"
|
||||
"RailActivatorComponent.cpp"
|
||||
"RollerComponent.cpp"
|
||||
"Activity/QuickBuildComponent.cpp"
|
||||
"RenderComponent.cpp"
|
||||
"Physics/RigidbodyPhantomPhysicsComponent.cpp"
|
||||
|
||||
@@ -1,17 +1,40 @@
|
||||
#ifndef CHESTCOMPONENT_H
|
||||
#define CHESTCOMPONENT_H
|
||||
|
||||
#include "BitStream.h"
|
||||
#include "Component.h"
|
||||
#include "dCommonVars.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
/**
|
||||
* Chest, as the client's LWOChestComponent makes it from the registry. Nothing of it is sent or done on the server yet.
|
||||
* Chest, as the client's LWOChestComponent makes it from the registry (LOT 2196). Its construction carries the player
|
||||
* the chest is for and the number of loot offers in it; nothing else of it is done on the server yet.
|
||||
*/
|
||||
class ChestComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::CHEST;
|
||||
|
||||
ChestComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {}
|
||||
|
||||
// LWOChestComponent::Deserialize (0x00c22010) reads on construction only: the player (u64) and the loot offer count
|
||||
// (u32), which it keeps when the chest is the local player's. Nothing in an update.
|
||||
void Serialize(RakNet::BitStream& outBitStream, const bool isConstruction) override {
|
||||
if (!isConstruction) return;
|
||||
outBitStream.Write(m_PlayerID);
|
||||
outBitStream.Write(m_LootOfferCount);
|
||||
}
|
||||
|
||||
LWOOBJID GetPlayerID() const { return m_PlayerID; }
|
||||
void SetPlayerID(const LWOOBJID value) { m_PlayerID = value; }
|
||||
uint32_t GetLootOfferCount() const { return m_LootOfferCount; }
|
||||
void SetLootOfferCount(const uint32_t value) { m_LootOfferCount = value; }
|
||||
|
||||
private:
|
||||
// The player the chest belongs to; none (the client's own start value) until something gives it one
|
||||
LWOOBJID m_PlayerID = LWOOBJID_EMPTY;
|
||||
|
||||
// The loot offers in the chest
|
||||
uint32_t m_LootOfferCount = 0;
|
||||
};
|
||||
|
||||
#endif // CHESTCOMPONENT_H
|
||||
|
||||
@@ -1,17 +1,61 @@
|
||||
#ifndef CHOICEBUILDCOMPONENT_H
|
||||
#define CHOICEBUILDCOMPONENT_H
|
||||
|
||||
#include "BitStream.h"
|
||||
#include "Component.h"
|
||||
#include "dCommonVars.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
/**
|
||||
* Choice build, as the client's LWOChoiceBuildComponent makes it from the registry. Nothing of it is sent or done on the server yet.
|
||||
* Choice build, as the client's LWOChoiceBuildComponent makes it from the registry (LOTs 5668, 6414, 6462, 7002, 16205).
|
||||
* Its state, the object built and the player using it are sent; nothing else of it is done on the server yet.
|
||||
*/
|
||||
class ChoiceBuildComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::CHOICE_BUILD;
|
||||
|
||||
// The client's states (its constructor starts at Waiting)
|
||||
enum class eState : uint32_t {
|
||||
Waiting = 0,
|
||||
Building = 1,
|
||||
};
|
||||
|
||||
ChoiceBuildComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {}
|
||||
|
||||
// LWOChoiceBuildComponent::Deserialize (0x00bf26d0) reads, in constructions and updates alike: a flag, then the
|
||||
// state (u32), the built object (u64) and the user (u64). A construction always carries them.
|
||||
void Serialize(RakNet::BitStream& outBitStream, const bool isConstruction) override {
|
||||
outBitStream.Write(isConstruction || m_Dirty);
|
||||
if (isConstruction || m_Dirty) {
|
||||
outBitStream.Write(m_State);
|
||||
outBitStream.Write(m_ChildObject);
|
||||
outBitStream.Write(m_UserID);
|
||||
}
|
||||
if (!isConstruction) m_Dirty = false;
|
||||
}
|
||||
|
||||
eState GetState() const { return m_State; }
|
||||
LWOOBJID GetChildObject() const { return m_ChildObject; }
|
||||
LWOOBJID GetUserID() const { return m_UserID; }
|
||||
|
||||
// Changes what the clients are told with the next update (the caller serializes the object)
|
||||
void SetState(const eState state, const LWOOBJID childObject, const LWOOBJID userID) {
|
||||
m_State = state;
|
||||
m_ChildObject = childObject;
|
||||
m_UserID = userID;
|
||||
m_Dirty = true;
|
||||
}
|
||||
|
||||
private:
|
||||
eState m_State = eState::Waiting;
|
||||
|
||||
// The object the choice build made, none while waiting
|
||||
LWOOBJID m_ChildObject = LWOOBJID_EMPTY;
|
||||
|
||||
// The player using the choice build, none while waiting
|
||||
LWOOBJID m_UserID = LWOOBJID_EMPTY;
|
||||
|
||||
bool m_Dirty = false;
|
||||
};
|
||||
|
||||
#endif // CHOICEBUILDCOMPONENT_H
|
||||
|
||||
17
dGame/dComponents/RollerComponent.cpp
Normal file
17
dGame/dComponents/RollerComponent.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#include "RollerComponent.h"
|
||||
|
||||
#include "Entity.h"
|
||||
#include "GeneralUtils.h"
|
||||
|
||||
namespace {
|
||||
// A level point: the three numbers with character 31 between them
|
||||
NiPoint3 LevelPoint(const Entity& entity, const std::u16string& key) {
|
||||
const auto parts = GeneralUtils::SplitString(entity.GetVarAsString(key), '\x1f');
|
||||
return GeneralUtils::TryParse<NiPoint3>(parts).value_or(NiPoint3Constant::ZERO);
|
||||
}
|
||||
}
|
||||
|
||||
void RollerComponent::LoadConfigData() {
|
||||
m_CameraPosition = LevelPoint(*m_Parent, u"activity_camera_pos");
|
||||
m_CameraLookAt = LevelPoint(*m_Parent, u"activity_camera_lookat_pos");
|
||||
}
|
||||
@@ -1,17 +1,54 @@
|
||||
#ifndef ROLLERCOMPONENT_H
|
||||
#define ROLLERCOMPONENT_H
|
||||
|
||||
#include "BitStream.h"
|
||||
#include "Component.h"
|
||||
#include "NiPoint3.h"
|
||||
#include "dCommonVars.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
/**
|
||||
* Roller, as the client's LWORollerComponent makes it from the registry. Nothing of it is sent or done on the server yet.
|
||||
* Roller (a launcher a player fires from: LOTs 2714, 13951), as the client's LWORollerComponent makes it from the
|
||||
* registry. Its construction carries the activity camera and the projectile skill; nobody uses one on the server
|
||||
* yet, so the block about its user is never sent.
|
||||
*/
|
||||
class RollerComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::ROLLER;
|
||||
|
||||
// The projectile skill the client's constructor starts with (LWORollerComponent::Initialize 0x00cc7e30)
|
||||
static constexpr uint32_t DEFAULT_PROJECTILE_SKILL = 1348;
|
||||
|
||||
RollerComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {}
|
||||
|
||||
// The level's activity_camera_pos and activity_camera_lookat_pos (LWORollerComponent::LoadConfigData 0x00c31820);
|
||||
// both stay at the origin without the key, as in the client
|
||||
void LoadConfigData() override;
|
||||
|
||||
// LWORollerComponent::Deserialize (0x00c15920) reads on construction: the camera position and what it looks at
|
||||
// (3 floats each) and the projectile skill (u32). Then, in constructions and updates alike, a flag in front of the
|
||||
// block about the player using it (3 x u64, 3 floats, a float, 3 floats, the player u64, a float): never set here.
|
||||
void Serialize(RakNet::BitStream& outBitStream, const bool isConstruction) override {
|
||||
if (isConstruction) {
|
||||
outBitStream.Write(m_CameraPosition.x);
|
||||
outBitStream.Write(m_CameraPosition.y);
|
||||
outBitStream.Write(m_CameraPosition.z);
|
||||
outBitStream.Write(m_CameraLookAt.x);
|
||||
outBitStream.Write(m_CameraLookAt.y);
|
||||
outBitStream.Write(m_CameraLookAt.z);
|
||||
outBitStream.Write(m_ProjectileSkill);
|
||||
}
|
||||
outBitStream.Write0();
|
||||
}
|
||||
|
||||
const NiPoint3& GetCameraPosition() const { return m_CameraPosition; }
|
||||
const NiPoint3& GetCameraLookAt() const { return m_CameraLookAt; }
|
||||
uint32_t GetProjectileSkill() const { return m_ProjectileSkill; }
|
||||
|
||||
private:
|
||||
NiPoint3 m_CameraPosition = NiPoint3Constant::ZERO;
|
||||
NiPoint3 m_CameraLookAt = NiPoint3Constant::ZERO;
|
||||
uint32_t m_ProjectileSkill = DEFAULT_PROJECTILE_SKILL;
|
||||
};
|
||||
|
||||
#endif // ROLLERCOMPONENT_H
|
||||
|
||||
@@ -6,6 +6,7 @@ set(DCOMPONENTS_TESTS
|
||||
"DestroyableLevelConfigTests.cpp"
|
||||
"ProximityMonitorComponentTests.cpp"
|
||||
"RailActivatorComponentTests.cpp"
|
||||
"ConstructionOnlyComponentTests.cpp"
|
||||
"PropertyVendorComponentTests.cpp"
|
||||
"VendorBuybackTests.cpp"
|
||||
"RacingWrongWayTests.cpp"
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
// What the client reads for a chest, a choice build and a roller: LWOChestComponent::Deserialize (0x00c22010),
|
||||
// LWOChoiceBuildComponent::Deserialize (0x00bf26d0) and LWORollerComponent::Deserialize (0x00c15920). Where they are
|
||||
// read among an object's components is in ReplicaComponentOrderTests.
|
||||
#include "GameDependencies.h"
|
||||
|
||||
#include "BitStream.h"
|
||||
#include "ChestComponent.h"
|
||||
#include "ChoiceBuildComponent.h"
|
||||
#include "Entity.h"
|
||||
#include "RollerComponent.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <vector>
|
||||
|
||||
class ConstructionOnlyComponentTests : public GameDependenciesTest {
|
||||
protected:
|
||||
void SetUp() override { SetUpDependencies(); }
|
||||
void TearDown() override { TearDownDependencies(); }
|
||||
|
||||
template <typename Component>
|
||||
static std::vector<uint8_t> Bytes(Component& component, const bool construction, uint32_t* const bits = nullptr) {
|
||||
RakNet::BitStream stream;
|
||||
component.Serialize(stream, construction);
|
||||
if (bits) *bits = stream.GetNumberOfBitsUsed();
|
||||
return { stream.GetData(), stream.GetData() + stream.GetNumberOfBytesUsed() };
|
||||
}
|
||||
};
|
||||
|
||||
// Construction: the player (u64) and the loot offer count (u32); nothing in an update
|
||||
TEST_F(ConstructionOnlyComponentTests, Chest) {
|
||||
Entity entity(15, info);
|
||||
auto* const chest = entity.AddComponent<ChestComponent>(0);
|
||||
uint32_t bits{};
|
||||
EXPECT_EQ(Bytes(*chest, true, &bits), (std::vector<uint8_t>{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }));
|
||||
EXPECT_EQ(bits, 96u);
|
||||
|
||||
chest->SetPlayerID(0x1000000000000001LL);
|
||||
chest->SetLootOfferCount(3);
|
||||
EXPECT_EQ(Bytes(*chest, true, &bits), (std::vector<uint8_t>{ 0x01, 0, 0, 0, 0, 0, 0, 0x10, 0x03, 0, 0, 0 }));
|
||||
EXPECT_EQ(bits, 96u);
|
||||
|
||||
EXPECT_TRUE(Bytes(*chest, false, &bits).empty());
|
||||
EXPECT_EQ(bits, 0u);
|
||||
}
|
||||
|
||||
// A flag, then the state (u32), the built object (u64) and the user (u64): always in a construction, in an update
|
||||
// after a change only
|
||||
TEST_F(ConstructionOnlyComponentTests, ChoiceBuild) {
|
||||
Entity entity(15, info);
|
||||
auto* const choiceBuild = entity.AddComponent<ChoiceBuildComponent>(1);
|
||||
uint32_t bits{};
|
||||
// 1 flag bit, then 160 zero bits: waiting, nothing built, nobody using it (the client's own start values)
|
||||
std::vector<uint8_t> waiting(21, 0);
|
||||
waiting[0] = 0x80;
|
||||
EXPECT_EQ(Bytes(*choiceBuild, true, &bits), waiting);
|
||||
EXPECT_EQ(bits, 161u);
|
||||
|
||||
EXPECT_EQ(Bytes(*choiceBuild, false, &bits), (std::vector<uint8_t>{ 0x00 }));
|
||||
EXPECT_EQ(bits, 1u);
|
||||
|
||||
choiceBuild->SetState(ChoiceBuildComponent::eState::Building, 0x0000400000000002LL, 0x1000000000000001LL);
|
||||
// The same fields shifted by the flag bit: state 1, object 0x0000400000000002, user 0x1000000000000001
|
||||
RakNet::BitStream expected;
|
||||
expected.Write1();
|
||||
expected.Write<uint32_t>(1);
|
||||
expected.Write<LWOOBJID>(0x0000400000000002LL);
|
||||
expected.Write<LWOOBJID>(0x1000000000000001LL);
|
||||
const std::vector<uint8_t> building{ expected.GetData(), expected.GetData() + expected.GetNumberOfBytesUsed() };
|
||||
EXPECT_EQ(building[0], 0x80); // the flag, then the low byte of the state (1) one bit on
|
||||
EXPECT_EQ(building[1], 0x80);
|
||||
EXPECT_EQ(Bytes(*choiceBuild, false, &bits), building);
|
||||
EXPECT_EQ(bits, 161u);
|
||||
// Sent once
|
||||
EXPECT_EQ(Bytes(*choiceBuild, false, &bits), (std::vector<uint8_t>{ 0x00 }));
|
||||
// A later construction carries the state as it is
|
||||
EXPECT_EQ(Bytes(*choiceBuild, true, &bits), building);
|
||||
}
|
||||
|
||||
// Construction: camera position and look-at (3 floats each), the projectile skill (u32, the client starts at 1348),
|
||||
// then the flag of the user block, which is never set. An update is that flag alone.
|
||||
TEST_F(ConstructionOnlyComponentTests, Roller) {
|
||||
Entity entity(15, info);
|
||||
auto* const roller = entity.AddComponent<RollerComponent>(13951);
|
||||
uint32_t bits{};
|
||||
std::vector<uint8_t> expected(29, 0);
|
||||
expected[24] = 0x44; // 1348 = 0x544
|
||||
expected[25] = 0x05;
|
||||
EXPECT_EQ(Bytes(*roller, true, &bits), expected);
|
||||
EXPECT_EQ(bits, 225u);
|
||||
|
||||
EXPECT_EQ(Bytes(*roller, false, &bits), (std::vector<uint8_t>{ 0x00 }));
|
||||
EXPECT_EQ(bits, 1u);
|
||||
}
|
||||
|
||||
// The level's activity_camera_pos / activity_camera_lookat_pos: three numbers with character 31 between them
|
||||
TEST_F(ConstructionOnlyComponentTests, RollerCameraComesFromTheLevel) {
|
||||
Entity entity(15, info);
|
||||
entity.SetVar<std::u16string>(u"activity_camera_pos", u"-327.861\x1f" u"256.91\x1f" u".64822");
|
||||
entity.SetVar<std::u16string>(u"activity_camera_lookat_pos", u"1\x1f" u"2\x1f" u"-0.5");
|
||||
auto* const roller = entity.AddComponent<RollerComponent>(13951); // loads the level's keys
|
||||
EXPECT_EQ(roller->GetCameraPosition(), NiPoint3(-327.861f, 256.91f, 0.64822f));
|
||||
EXPECT_EQ(roller->GetCameraLookAt(), NiPoint3(1.0f, 2.0f, -0.5f));
|
||||
|
||||
RakNet::BitStream expected;
|
||||
for (const float value : { -327.861f, 256.91f, 0.64822f, 1.0f, 2.0f, -0.5f }) expected.Write(value);
|
||||
expected.Write<uint32_t>(1348);
|
||||
expected.Write0();
|
||||
uint32_t bits{};
|
||||
EXPECT_EQ(Bytes(*roller, true, &bits), (std::vector<uint8_t>{ expected.GetData(), expected.GetData() + expected.GetNumberOfBytesUsed() }));
|
||||
EXPECT_EQ(bits, 225u);
|
||||
// 1.0f little endian, the first look-at number
|
||||
EXPECT_EQ(Bytes(*roller, true)[12 + 3], 0x3f);
|
||||
}
|
||||
@@ -18,6 +18,9 @@
|
||||
#include "BouncerComponent.h"
|
||||
#include "BuffComponent.h"
|
||||
#include "CharacterComponent.h"
|
||||
#include "ChestComponent.h"
|
||||
#include "ChoiceBuildComponent.h"
|
||||
#include "RollerComponent.h"
|
||||
#include "CollectibleComponent.h"
|
||||
#include "ControllablePhysicsComponent.h"
|
||||
#include "DestroyableComponent.h"
|
||||
@@ -561,3 +564,102 @@ TEST_F(ReplicaComponentOrderTest, EveryListedComponent) {
|
||||
entity.AddComponent<MiniGameControlComponent>(-1);
|
||||
});
|
||||
}
|
||||
|
||||
namespace {
|
||||
// The bits of `expected` are the first bits of `actual`
|
||||
void ExpectStartsWith(RakNet::BitStream& actual, RakNet::BitStream& expected) {
|
||||
ASSERT_GE(actual.GetNumberOfBitsUsed(), expected.GetNumberOfBitsUsed());
|
||||
actual.ResetReadPointer();
|
||||
expected.ResetReadPointer();
|
||||
for (uint32_t i = 0; i < expected.GetNumberOfBitsUsed(); i++) {
|
||||
ASSERT_EQ(actual.ReadBit(), expected.ReadBit()) << "bit " << i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LWOGameObject::Deserialize (0x00f99690) reads the components in the order ObjectLoader2::FillComponentOrder
|
||||
// (0x0101f8e0) lists them: a chest (22) after its phantom physics (40) and before its render component (2).
|
||||
// LOT 2196: phantom physics, chest, render.
|
||||
TEST_F(ReplicaComponentOrderTest, ChestIsWrittenBeforeRender) {
|
||||
Entity entity(15, info);
|
||||
auto* const physics = entity.AddComponent<PhantomPhysicsComponent>(-1);
|
||||
auto* const chest = entity.AddComponent<ChestComponent>(0);
|
||||
chest->SetPlayerID(0x1000000000000001LL);
|
||||
chest->SetLootOfferCount(3);
|
||||
auto* const render = entity.AddComponent<RenderComponent>(-1);
|
||||
|
||||
RakNet::BitStream actual;
|
||||
entity.WriteComponents(actual, eReplicaPacketType::CONSTRUCTION);
|
||||
|
||||
Entity copy(15, info);
|
||||
RakNet::BitStream expected;
|
||||
copy.AddComponent<PhantomPhysicsComponent>(-1)->Serialize(expected, true);
|
||||
expected.Write<LWOOBJID>(0x1000000000000001LL);
|
||||
expected.Write<uint32_t>(3);
|
||||
copy.AddComponent<RenderComponent>(-1)->Serialize(expected, true);
|
||||
ExpectStartsWith(actual, expected);
|
||||
static_cast<void>(physics);
|
||||
static_cast<void>(render);
|
||||
}
|
||||
|
||||
// A choice build (52) is read after physics, destroyable and script and before render. LOT 6414: simple physics,
|
||||
// destroyable, choice build, render; LOT 5668 has a script in place of the destroyable.
|
||||
TEST_F(ReplicaComponentOrderTest, ChoiceBuildIsWrittenAfterScriptAndBeforeRender) {
|
||||
const auto build = [](Entity& entity) {
|
||||
entity.AddComponent<SimplePhysicsComponent>(-1);
|
||||
entity.AddComponent<BuffComponent>(-1);
|
||||
entity.AddComponent<DestroyableComponent>(-1)->SetMaxHealth(1.0f);
|
||||
entity.AddComponent<ScriptComponent>(-1, "", true);
|
||||
entity.AddComponent<ChoiceBuildComponent>(1);
|
||||
entity.AddComponent<RenderComponent>(-1);
|
||||
};
|
||||
Entity entity(15, info);
|
||||
build(entity);
|
||||
RakNet::BitStream actual;
|
||||
entity.WriteComponents(actual, eReplicaPacketType::CONSTRUCTION);
|
||||
|
||||
Entity copy(15, info);
|
||||
build(copy);
|
||||
RakNet::BitStream expected;
|
||||
copy.GetComponent<SimplePhysicsComponent>()->Serialize(expected, true);
|
||||
copy.GetComponent<BuffComponent>()->Serialize(expected, true);
|
||||
copy.GetComponent<DestroyableComponent>()->Serialize(expected, true);
|
||||
copy.GetComponent<ScriptComponent>()->Serialize(expected, true);
|
||||
expected.Write1();
|
||||
expected.Write<uint32_t>(0); // waiting
|
||||
expected.Write<LWOOBJID>(0); // nothing built
|
||||
expected.Write<LWOOBJID>(0); // nobody using it
|
||||
copy.GetComponent<RenderComponent>()->Serialize(expected, true);
|
||||
ExpectStartsWith(actual, expected);
|
||||
}
|
||||
|
||||
// A roller (105) is read after script, skill and inventory and before the moving platform (25) and render.
|
||||
// LOT 2714: simple physics, roller, moving platform, render; LOT 13951 adds script, skill and inventory.
|
||||
TEST_F(ReplicaComponentOrderTest, RollerIsWrittenBeforeTheMovingPlatform) {
|
||||
const auto build = [](Entity& entity) {
|
||||
entity.AddComponent<SimplePhysicsComponent>(-1);
|
||||
entity.AddComponent<ScriptComponent>(-1, "", true);
|
||||
entity.AddComponent<SkillComponent>(-1);
|
||||
entity.AddComponent<RollerComponent>(13951);
|
||||
entity.AddComponent<MovingPlatformComponent>(-1, "");
|
||||
entity.AddComponent<RenderComponent>(-1);
|
||||
};
|
||||
for (const auto packetType : { eReplicaPacketType::CONSTRUCTION, eReplicaPacketType::SERIALIZATION }) {
|
||||
const bool construction = packetType == eReplicaPacketType::CONSTRUCTION;
|
||||
Entity entity(15, info);
|
||||
build(entity);
|
||||
RakNet::BitStream actual;
|
||||
entity.WriteComponents(actual, packetType);
|
||||
|
||||
Entity copy(15, info);
|
||||
build(copy);
|
||||
RakNet::BitStream expected;
|
||||
copy.GetComponent<SimplePhysicsComponent>()->Serialize(expected, construction);
|
||||
copy.GetComponent<ScriptComponent>()->Serialize(expected, construction);
|
||||
copy.GetComponent<SkillComponent>()->Serialize(expected, construction);
|
||||
copy.GetComponent<RollerComponent>()->Serialize(expected, construction);
|
||||
copy.GetComponent<MovingPlatformComponent>()->Serialize(expected, construction);
|
||||
copy.GetComponent<RenderComponent>()->Serialize(expected, construction);
|
||||
ExpectStartsWith(actual, expected);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user