mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-19 11:12:49 +00:00
fix: give the destroyable and buff components their real component types
eReplicaComponentType had the destroyable component's registry type (7) named BUFF, the real buff component (98) as BUFF_REAL, and a made-up DESTROYABLE = 1000 that DestroyableComponent was stored under. Now DESTROYABLE = 7 and BUFF = 98, as in ComponentsRegistry and the client. Undone with it: - Destructible stats came from whichever of the "buff" (7), quick build and collectible registry ids was set, so the few objects without a type 7 entry read a DestructibleComponent row with an unrelated id (the NJ dragon relics 16482-16485 via their collectible id, 125 quick build LOTs when placed with is_smashable). The type 7 entry is used now; objects without one keep the defaults (is_smashable objects: 1 health, smashable, factions -1 and 6; collectibles: an empty destroyable). The client does the same (LWODestroyableComponent::AllocateComponents / DoObjectComponentLoad). - DestroyableComponent::Reinitialize, an unused copy of that pick order. - WriteComponents' destroyableSerialized flags: the components are written from a list in the client's order, and where the destroyable goes (its own place after the buff, right before a quick build that has no registry entry for it, or after the render component) is one function. - The dashboard's registry 7 -> DESTROYABLE mapping; the destroyable type also has a name there now (1000 was outside magic_enum's range). Component types are not stored or sent as enum numbers anywhere besides the CDClient's own values, which now match. migrations/cdserver/4 is unrelated (it restores LOT 12916's registry rows that migration 0 overwrote) and stays. Verified: dGameTests ReplicaComponentOrderTests serialize players, enemies, smashables, quick builds and collectibles with and without a registry entry, NPCs, pets, vehicles, models and an entity with every listed component with the new code and a frozen copy of the old WriteComponents, and expect the same bits for construction and serialization (a deliberately wrong destroyable place fails them). Full ctest passes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -11,7 +11,7 @@ enum class eReplicaComponentType : uint32_t {
|
|||||||
CHARACTER,
|
CHARACTER,
|
||||||
SCRIPT,
|
SCRIPT,
|
||||||
BOUNCER,
|
BOUNCER,
|
||||||
BUFF, // buff is really 98, this is DESTROYABLE
|
DESTROYABLE,
|
||||||
GHOST,
|
GHOST,
|
||||||
SKILL,
|
SKILL,
|
||||||
SPAWNER,
|
SPAWNER,
|
||||||
@@ -102,7 +102,7 @@ enum class eReplicaComponentType : uint32_t {
|
|||||||
USER_CONTROL,
|
USER_CONTROL,
|
||||||
IGNORE_LIST,
|
IGNORE_LIST,
|
||||||
MULTI_ZONE_ENTRANCE,
|
MULTI_ZONE_ENTRANCE,
|
||||||
BUFF_REAL, // the real buff component, should just be name BUFF
|
BUFF,
|
||||||
INTERACTION_MANAGER,
|
INTERACTION_MANAGER,
|
||||||
DONATION_VENDOR,
|
DONATION_VENDOR,
|
||||||
COMBAT_MEDIATOR,
|
COMBAT_MEDIATOR,
|
||||||
@@ -121,7 +121,6 @@ enum class eReplicaComponentType : uint32_t {
|
|||||||
BUILD_BORDER,
|
BUILD_BORDER,
|
||||||
UNKNOWN_115,
|
UNKNOWN_115,
|
||||||
CULLING_PLANE,
|
CULLING_PLANE,
|
||||||
DESTROYABLE = 1000 // Actually 7
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //!__EREPLICACOMPONENTTYPE__H__
|
#endif //!__EREPLICACOMPONENTTYPE__H__
|
||||||
|
|||||||
@@ -199,7 +199,7 @@ namespace CDClientSchema {
|
|||||||
inline const Table* ComponentTable(const Schema& schema, eReplicaComponentType type) {
|
inline const Table* ComponentTable(const Schema& schema, eReplicaComponentType type) {
|
||||||
using enum eReplicaComponentType;
|
using enum eReplicaComponentType;
|
||||||
static const std::map<eReplicaComponentType, std::string_view> DIFFERENT{
|
static const std::map<eReplicaComponentType, std::string_view> DIFFERENT{
|
||||||
{ BUFF, "DestructibleComponent" }, // the enum's BUFF (7) is the destroyable component
|
{ DESTROYABLE, "DestructibleComponent" },
|
||||||
{ CONTROLLABLE_PHYSICS, "PhysicsComponent" }, { SIMPLE_PHYSICS, "PhysicsComponent" }, { PHANTOM_PHYSICS, "PhysicsComponent" },
|
{ CONTROLLABLE_PHYSICS, "PhysicsComponent" }, { SIMPLE_PHYSICS, "PhysicsComponent" }, { PHANTOM_PHYSICS, "PhysicsComponent" },
|
||||||
{ RIGID_BODY_PHANTOM_PHYSICS, "PhysicsComponent" }, { HAVOK_VEHICLE_PHYSICS, "VehiclePhysics" },
|
{ RIGID_BODY_PHANTOM_PHYSICS, "PhysicsComponent" }, { HAVOK_VEHICLE_PHYSICS, "VehiclePhysics" },
|
||||||
{ QUICK_BUILD, "RebuildComponent" }, { MISSION_OFFER, "MissionNPCComponent" }, { ROCKET_LAUNCH, "RocketLaunchpadControlComponent" },
|
{ QUICK_BUILD, "RebuildComponent" }, { MISSION_OFFER, "MissionNPCComponent" }, { ROCKET_LAUNCH, "RocketLaunchpadControlComponent" },
|
||||||
|
|||||||
@@ -131,16 +131,11 @@ namespace WorldScene {
|
|||||||
eReplicaComponentType::SCRIPTED_ACTIVITY, eReplicaComponentType::DESTROYABLE
|
eReplicaComponentType::SCRIPTED_ACTIVITY, eReplicaComponentType::DESTROYABLE
|
||||||
};
|
};
|
||||||
|
|
||||||
// ComponentsRegistry stores the destroyable component as 7 (the enum's BUFF; see eReplicaComponentType)
|
|
||||||
inline eReplicaComponentType FromRegistry(uint32_t componentType) {
|
|
||||||
return componentType == 7 ? eReplicaComponentType::DESTROYABLE : static_cast<eReplicaComponentType>(componentType);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Index into KINDS of an object with these ComponentsRegistry component types, or KINDS.size() for "other"
|
// Index into KINDS of an object with these ComponentsRegistry component types, or KINDS.size() for "other"
|
||||||
inline size_t Classify(const std::vector<uint32_t>& registryComponents) {
|
inline size_t Classify(const std::vector<uint32_t>& registryComponents) {
|
||||||
size_t best = KINDS.size();
|
size_t best = KINDS.size();
|
||||||
for (const auto component : registryComponents) {
|
for (const auto component : registryComponents) {
|
||||||
const auto type = FromRegistry(component);
|
const auto type = static_cast<eReplicaComponentType>(component);
|
||||||
for (size_t i = 0; i < best; i++) {
|
for (size_t i = 0; i < best; i++) {
|
||||||
if (KINDS[i] == type) { best = i; break; }
|
if (KINDS[i] == type) { best = i; break; }
|
||||||
}
|
}
|
||||||
|
|||||||
276
dGame/Entity.cpp
276
dGame/Entity.cpp
@@ -10,6 +10,7 @@
|
|||||||
#include "CDDestructibleComponentTable.h"
|
#include "CDDestructibleComponentTable.h"
|
||||||
#include "CDClientDatabase.h"
|
#include "CDClientDatabase.h"
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <array>
|
||||||
#include "dServer.h"
|
#include "dServer.h"
|
||||||
#include "GameMessages.h"
|
#include "GameMessages.h"
|
||||||
#include "EffectsMessages.h"
|
#include "EffectsMessages.h"
|
||||||
@@ -383,11 +384,6 @@ void Entity::Initialize() {
|
|||||||
AddComponent<RacingSoundTriggerComponent>(racingSoundTriggerID);
|
AddComponent<RacingSoundTriggerComponent>(racingSoundTriggerID);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto buffComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::BUFF);
|
|
||||||
if (buffComponentID > 0) {
|
|
||||||
AddComponent<BuffComponent>(buffComponentID);
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto collectibleComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::COLLECTIBLE);
|
const auto collectibleComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::COLLECTIBLE);
|
||||||
|
|
||||||
if (collectibleComponentID > 0) {
|
if (collectibleComponentID > 0) {
|
||||||
@@ -395,18 +391,18 @@ void Entity::Initialize() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Multiple components require the destructible component.
|
* The destroyable component. The client makes one (with a buff component in front of it) for a DESTROYABLE
|
||||||
|
* registry entry, whose id is the DestructibleComponent row. Collectibles and smashable (is_smashable) objects
|
||||||
|
* without that entry get one too, with no row.
|
||||||
*/
|
*/
|
||||||
const auto quickBuildComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::QUICK_BUILD);
|
const auto destroyableComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::DESTROYABLE);
|
||||||
|
if (destroyableComponentID > 0) {
|
||||||
int32_t componentID = -1;
|
AddComponent<BuffComponent>(destroyableComponentID);
|
||||||
if (collectibleComponentID > 0) componentID = collectibleComponentID;
|
}
|
||||||
if (quickBuildComponentID > 0) componentID = quickBuildComponentID;
|
|
||||||
if (buffComponentID > 0) componentID = buffComponentID;
|
|
||||||
|
|
||||||
|
|
||||||
bool isSmashable = GetVarAs<int32_t>(u"is_smashable") != 0;
|
bool isSmashable = GetVarAs<int32_t>(u"is_smashable") != 0;
|
||||||
if (buffComponentID > 0 || collectibleComponentID > 0 || isSmashable) {
|
if (destroyableComponentID > 0 || collectibleComponentID > 0 || isSmashable) {
|
||||||
|
const int32_t componentID = destroyableComponentID > 0 ? destroyableComponentID : -1;
|
||||||
DestroyableComponent* comp = AddComponent<DestroyableComponent>(componentID);
|
DestroyableComponent* comp = AddComponent<DestroyableComponent>(componentID);
|
||||||
auto* const destCompTable = CDClientManager::GetTable<CDDestructibleComponentTable>();
|
auto* const destCompTable = CDClientManager::GetTable<CDDestructibleComponentTable>();
|
||||||
std::vector<CDDestructibleComponent> destCompData = destCompTable->Query([componentID](const CDDestructibleComponent& entry) { return (entry.id == componentID); });
|
std::vector<CDDestructibleComponent> destCompData = destCompTable->Query([componentID](const CDDestructibleComponent& entry) { return (entry.id == componentID); });
|
||||||
@@ -451,7 +447,8 @@ void Entity::Initialize() {
|
|||||||
comp->SetMaxCoins(currencyValues[0].maxvalue);
|
comp->SetMaxCoins(currencyValues[0].maxvalue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else if (isSmashable) {
|
||||||
|
// A smashable object without a DestructibleComponent row
|
||||||
comp->SetHealth(1);
|
comp->SetHealth(1);
|
||||||
comp->SetArmor(0);
|
comp->SetArmor(0);
|
||||||
|
|
||||||
@@ -599,6 +596,7 @@ void Entity::Initialize() {
|
|||||||
AddComponent<BaseCombatAIComponent>(combatAiID);
|
AddComponent<BaseCombatAIComponent>(combatAiID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto quickBuildComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::QUICK_BUILD);
|
||||||
if (quickBuildComponentID > 0) {
|
if (quickBuildComponentID > 0) {
|
||||||
auto* const quickBuildComponent = AddComponent<QuickBuildComponent>(quickBuildComponentID);
|
auto* const quickBuildComponent = AddComponent<QuickBuildComponent>(quickBuildComponentID);
|
||||||
|
|
||||||
@@ -1064,222 +1062,56 @@ void Entity::WriteBaseReplicaData(RakNet::BitStream& outBitStream, eReplicaPacke
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Entity::WriteComponents(RakNet::BitStream& outBitStream, eReplicaPacketType packetType) const {
|
namespace {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This has to be done in a specific order.
|
* 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.
|
||||||
*/
|
*/
|
||||||
|
constexpr auto SERIALIZATION_ORDER = [] {
|
||||||
|
using enum eReplicaComponentType;
|
||||||
|
return std::array{
|
||||||
|
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, CHARACTER, ITEM, INVENTORY,
|
||||||
|
SCRIPT, SKILL, BASE_COMBAT_AI, QUICK_BUILD, MOVING_PLATFORM, SWITCH, VENDOR, DONATION_VENDOR, ACHIEVEMENT_VENDOR,
|
||||||
|
BOUNCER, SCRIPTED_ACTIVITY, SHOOTING_GALLERY, RACING_CONTROL, LUP_EXHIBIT, MODEL, RENDER, MINI_GAME_CONTROL,
|
||||||
|
};
|
||||||
|
}();
|
||||||
|
|
||||||
bool destroyableSerialized = false;
|
// The component in SERIALIZATION_ORDER the destroyable is written in front of (DESTROYABLE: at its own place)
|
||||||
bool bIsInitialUpdate = packetType == eReplicaPacketType::CONSTRUCTION;
|
eReplicaComponentType DestroyableSerializationSlot(const Entity& entity) {
|
||||||
|
using enum eReplicaComponentType;
|
||||||
PossessableComponent* possessableComponent;
|
// A DESTROYABLE registry entry puts the destroyable (with the buff in front of it) at its own place. Collectibles
|
||||||
if (TryGetComponent(eReplicaComponentType::POSSESSABLE, possessableComponent)) {
|
// and quick builds without one get theirs right before themselves, which for a collectible is the same place.
|
||||||
possessableComponent->Serialize(outBitStream, bIsInitialUpdate);
|
if (entity.HasComponent(BUFF) || entity.HasComponent(COLLECTIBLE)) return DESTROYABLE;
|
||||||
|
if (entity.HasComponent(QUICK_BUILD)) return QUICK_BUILD;
|
||||||
|
// Anything else that has one (is_smashable objects, models) writes it after the others
|
||||||
|
return MINI_GAME_CONTROL;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ModuleAssemblyComponent* moduleAssemblyComponent;
|
void Entity::WriteComponents(RakNet::BitStream& outBitStream, eReplicaPacketType packetType) const {
|
||||||
if (TryGetComponent(eReplicaComponentType::MODULE_ASSEMBLY, moduleAssemblyComponent)) {
|
const bool bIsInitialUpdate = packetType == eReplicaPacketType::CONSTRUCTION;
|
||||||
moduleAssemblyComponent->Serialize(outBitStream, bIsInitialUpdate);
|
auto* const destroyableComponent = GetComponent<DestroyableComponent>();
|
||||||
}
|
const auto destroyableSlot = DestroyableSerializationSlot(*this);
|
||||||
|
|
||||||
ControllablePhysicsComponent* controllablePhysicsComponent;
|
using enum eReplicaComponentType;
|
||||||
if (TryGetComponent(eReplicaComponentType::CONTROLLABLE_PHYSICS, controllablePhysicsComponent)) {
|
for (const auto type : SERIALIZATION_ORDER) {
|
||||||
controllablePhysicsComponent->Serialize(outBitStream, bIsInitialUpdate);
|
if (type == destroyableSlot && destroyableComponent) destroyableComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
}
|
if (type == DESTROYABLE) continue;
|
||||||
|
|
||||||
SimplePhysicsComponent* simplePhysicsComponent;
|
auto* const component = GetComponent(type);
|
||||||
if (TryGetComponent(eReplicaComponentType::SIMPLE_PHYSICS, simplePhysicsComponent)) {
|
if (!component) continue;
|
||||||
simplePhysicsComponent->Serialize(outBitStream, bIsInitialUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
RigidbodyPhantomPhysicsComponent* rigidbodyPhantomPhysics;
|
if (type == CHARACTER) {
|
||||||
if (TryGetComponent(eReplicaComponentType::RIGID_BODY_PHANTOM_PHYSICS, rigidbodyPhantomPhysics)) {
|
// Written with the character component; every character has them, a 0 bit stands in should one be missing
|
||||||
rigidbodyPhantomPhysics->Serialize(outBitStream, bIsInitialUpdate);
|
for (const auto characterPart : { POSSESSOR, LEVEL_PROGRESSION, PLAYER_FORCED_MOVEMENT }) {
|
||||||
}
|
auto* const part = GetComponent(characterPart);
|
||||||
|
if (part) part->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
HavokVehiclePhysicsComponent* havokVehiclePhysicsComponent;
|
else outBitStream.Write0();
|
||||||
if (TryGetComponent(eReplicaComponentType::HAVOK_VEHICLE_PHYSICS, havokVehiclePhysicsComponent)) {
|
}
|
||||||
havokVehiclePhysicsComponent->Serialize(outBitStream, bIsInitialUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
PhantomPhysicsComponent* phantomPhysicsComponent;
|
|
||||||
if (TryGetComponent(eReplicaComponentType::PHANTOM_PHYSICS, phantomPhysicsComponent)) {
|
|
||||||
phantomPhysicsComponent->Serialize(outBitStream, bIsInitialUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
SoundTriggerComponent* soundTriggerComponent;
|
|
||||||
if (TryGetComponent(eReplicaComponentType::SOUND_TRIGGER, soundTriggerComponent)) {
|
|
||||||
soundTriggerComponent->Serialize(outBitStream, bIsInitialUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
RacingSoundTriggerComponent* racingSoundTriggerComponent;
|
|
||||||
if (TryGetComponent(eReplicaComponentType::RACING_SOUND_TRIGGER, racingSoundTriggerComponent)) {
|
|
||||||
racingSoundTriggerComponent->Serialize(outBitStream, bIsInitialUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
BuffComponent* buffComponent;
|
|
||||||
if (TryGetComponent(eReplicaComponentType::BUFF, buffComponent)) {
|
|
||||||
buffComponent->Serialize(outBitStream, bIsInitialUpdate);
|
|
||||||
|
|
||||||
DestroyableComponent* destroyableComponent;
|
|
||||||
if (TryGetComponent(eReplicaComponentType::DESTROYABLE, destroyableComponent)) {
|
|
||||||
destroyableComponent->Serialize(outBitStream, bIsInitialUpdate);
|
|
||||||
}
|
}
|
||||||
destroyableSerialized = true;
|
component->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
}
|
|
||||||
|
|
||||||
CollectibleComponent* collectibleComponent;
|
|
||||||
if (TryGetComponent(eReplicaComponentType::COLLECTIBLE, collectibleComponent)) {
|
|
||||||
DestroyableComponent* destroyableComponent;
|
|
||||||
if (TryGetComponent(eReplicaComponentType::DESTROYABLE, destroyableComponent) && !destroyableSerialized) {
|
|
||||||
destroyableComponent->Serialize(outBitStream, bIsInitialUpdate);
|
|
||||||
}
|
|
||||||
destroyableSerialized = true;
|
|
||||||
collectibleComponent->Serialize(outBitStream, bIsInitialUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
PetComponent* petComponent;
|
|
||||||
if (TryGetComponent(eReplicaComponentType::PET, petComponent)) {
|
|
||||||
petComponent->Serialize(outBitStream, bIsInitialUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
CharacterComponent* characterComponent;
|
|
||||||
if (TryGetComponent(eReplicaComponentType::CHARACTER, characterComponent)) {
|
|
||||||
|
|
||||||
PossessorComponent* possessorComponent;
|
|
||||||
if (TryGetComponent(eReplicaComponentType::POSSESSOR, possessorComponent)) {
|
|
||||||
possessorComponent->Serialize(outBitStream, bIsInitialUpdate);
|
|
||||||
} else {
|
|
||||||
// Should never happen, but just to be safe
|
|
||||||
outBitStream.Write0();
|
|
||||||
}
|
|
||||||
|
|
||||||
LevelProgressionComponent* levelProgressionComponent;
|
|
||||||
if (TryGetComponent(eReplicaComponentType::LEVEL_PROGRESSION, levelProgressionComponent)) {
|
|
||||||
levelProgressionComponent->Serialize(outBitStream, bIsInitialUpdate);
|
|
||||||
} else {
|
|
||||||
// Should never happen, but just to be safe
|
|
||||||
outBitStream.Write0();
|
|
||||||
}
|
|
||||||
|
|
||||||
PlayerForcedMovementComponent* playerForcedMovementComponent;
|
|
||||||
if (TryGetComponent(eReplicaComponentType::PLAYER_FORCED_MOVEMENT, playerForcedMovementComponent)) {
|
|
||||||
playerForcedMovementComponent->Serialize(outBitStream, bIsInitialUpdate);
|
|
||||||
} else {
|
|
||||||
// Should never happen, but just to be safe
|
|
||||||
outBitStream.Write0();
|
|
||||||
}
|
|
||||||
|
|
||||||
characterComponent->Serialize(outBitStream, bIsInitialUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
ItemComponent* itemComponent;
|
|
||||||
if (TryGetComponent(eReplicaComponentType::ITEM, itemComponent)) {
|
|
||||||
itemComponent->Serialize(outBitStream, bIsInitialUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
InventoryComponent* inventoryComponent;
|
|
||||||
if (TryGetComponent(eReplicaComponentType::INVENTORY, inventoryComponent)) {
|
|
||||||
inventoryComponent->Serialize(outBitStream, bIsInitialUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
ScriptComponent* scriptComponent;
|
|
||||||
if (TryGetComponent(eReplicaComponentType::SCRIPT, scriptComponent)) {
|
|
||||||
scriptComponent->Serialize(outBitStream, bIsInitialUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
SkillComponent* skillComponent;
|
|
||||||
if (TryGetComponent(eReplicaComponentType::SKILL, skillComponent)) {
|
|
||||||
skillComponent->Serialize(outBitStream, bIsInitialUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
BaseCombatAIComponent* baseCombatAiComponent;
|
|
||||||
if (TryGetComponent(eReplicaComponentType::BASE_COMBAT_AI, baseCombatAiComponent)) {
|
|
||||||
baseCombatAiComponent->Serialize(outBitStream, bIsInitialUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
QuickBuildComponent* quickBuildComponent;
|
|
||||||
if (TryGetComponent(eReplicaComponentType::QUICK_BUILD, quickBuildComponent)) {
|
|
||||||
DestroyableComponent* destroyableComponent;
|
|
||||||
if (TryGetComponent(eReplicaComponentType::DESTROYABLE, destroyableComponent) && !destroyableSerialized) {
|
|
||||||
destroyableComponent->Serialize(outBitStream, bIsInitialUpdate);
|
|
||||||
}
|
|
||||||
destroyableSerialized = true;
|
|
||||||
quickBuildComponent->Serialize(outBitStream, bIsInitialUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
MovingPlatformComponent* movingPlatformComponent;
|
|
||||||
if (TryGetComponent(eReplicaComponentType::MOVING_PLATFORM, movingPlatformComponent)) {
|
|
||||||
movingPlatformComponent->Serialize(outBitStream, bIsInitialUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
SwitchComponent* switchComponent;
|
|
||||||
if (TryGetComponent(eReplicaComponentType::SWITCH, switchComponent)) {
|
|
||||||
switchComponent->Serialize(outBitStream, bIsInitialUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
VendorComponent* vendorComponent;
|
|
||||||
if (TryGetComponent(eReplicaComponentType::VENDOR, vendorComponent)) {
|
|
||||||
vendorComponent->Serialize(outBitStream, bIsInitialUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
DonationVendorComponent* donationVendorComponent;
|
|
||||||
if (TryGetComponent(eReplicaComponentType::DONATION_VENDOR, donationVendorComponent)) {
|
|
||||||
donationVendorComponent->Serialize(outBitStream, bIsInitialUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
AchievementVendorComponent* achievementVendorComponent;
|
|
||||||
if (TryGetComponent(eReplicaComponentType::ACHIEVEMENT_VENDOR, achievementVendorComponent)) {
|
|
||||||
achievementVendorComponent->Serialize(outBitStream, bIsInitialUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
BouncerComponent* bouncerComponent;
|
|
||||||
if (TryGetComponent(eReplicaComponentType::BOUNCER, bouncerComponent)) {
|
|
||||||
bouncerComponent->Serialize(outBitStream, bIsInitialUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
ScriptedActivityComponent* scriptedActivityComponent;
|
|
||||||
if (TryGetComponent(eReplicaComponentType::SCRIPTED_ACTIVITY, scriptedActivityComponent)) {
|
|
||||||
scriptedActivityComponent->Serialize(outBitStream, bIsInitialUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
ShootingGalleryComponent* shootingGalleryComponent;
|
|
||||||
if (TryGetComponent(eReplicaComponentType::SHOOTING_GALLERY, shootingGalleryComponent)) {
|
|
||||||
shootingGalleryComponent->Serialize(outBitStream, bIsInitialUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
RacingControlComponent* racingControlComponent;
|
|
||||||
if (TryGetComponent(eReplicaComponentType::RACING_CONTROL, racingControlComponent)) {
|
|
||||||
racingControlComponent->Serialize(outBitStream, bIsInitialUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
LUPExhibitComponent* lupExhibitComponent;
|
|
||||||
if (TryGetComponent(eReplicaComponentType::LUP_EXHIBIT, lupExhibitComponent)) {
|
|
||||||
lupExhibitComponent->Serialize(outBitStream, bIsInitialUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
ModelComponent* modelComponent;
|
|
||||||
if (TryGetComponent(eReplicaComponentType::MODEL, modelComponent)) {
|
|
||||||
modelComponent->Serialize(outBitStream, bIsInitialUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
RenderComponent* renderComponent;
|
|
||||||
if (TryGetComponent(eReplicaComponentType::RENDER, renderComponent)) {
|
|
||||||
renderComponent->Serialize(outBitStream, bIsInitialUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (modelComponent || !destroyableSerialized) {
|
|
||||||
DestroyableComponent* destroyableComponent;
|
|
||||||
if (TryGetComponent(eReplicaComponentType::DESTROYABLE, destroyableComponent) && !destroyableSerialized) {
|
|
||||||
destroyableComponent->Serialize(outBitStream, bIsInitialUpdate);
|
|
||||||
destroyableSerialized = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MiniGameControlComponent* miniGameControlComponent;
|
|
||||||
if (TryGetComponent(eReplicaComponentType::MINI_GAME_CONTROL, miniGameControlComponent)) {
|
|
||||||
miniGameControlComponent->Serialize(outBitStream, bIsInitialUpdate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// BBB Component, unused currently
|
// BBB Component, unused currently
|
||||||
|
|||||||
@@ -97,48 +97,6 @@ DestroyableComponent::DestroyableComponent(Entity* parent, const int32_t compone
|
|||||||
DestroyableComponent::~DestroyableComponent() {
|
DestroyableComponent::~DestroyableComponent() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DestroyableComponent::Reinitialize(LOT templateID) {
|
|
||||||
CDComponentsRegistryTable* compRegistryTable = CDClientManager::GetTable<CDComponentsRegistryTable>();
|
|
||||||
|
|
||||||
int32_t buffComponentID = compRegistryTable->GetByIDAndType(templateID, eReplicaComponentType::BUFF);
|
|
||||||
int32_t collectibleComponentID = compRegistryTable->GetByIDAndType(templateID, eReplicaComponentType::COLLECTIBLE);
|
|
||||||
int32_t quickBuildComponentID = compRegistryTable->GetByIDAndType(templateID, eReplicaComponentType::QUICK_BUILD);
|
|
||||||
|
|
||||||
int32_t componentID = 0;
|
|
||||||
if (collectibleComponentID > 0) componentID = collectibleComponentID;
|
|
||||||
if (quickBuildComponentID > 0) componentID = quickBuildComponentID;
|
|
||||||
if (buffComponentID > 0) componentID = buffComponentID;
|
|
||||||
|
|
||||||
CDDestructibleComponentTable* destCompTable = CDClientManager::GetTable<CDDestructibleComponentTable>();
|
|
||||||
std::vector<CDDestructibleComponent> destCompData = destCompTable->Query([=](CDDestructibleComponent entry) { return (entry.id == componentID); });
|
|
||||||
|
|
||||||
if (componentID > 0) {
|
|
||||||
std::vector<CDDestructibleComponent> destCompData = destCompTable->Query([=](CDDestructibleComponent entry) { return (entry.id == componentID); });
|
|
||||||
|
|
||||||
if (destCompData.size() > 0) {
|
|
||||||
SetHealth(destCompData[0].life);
|
|
||||||
SetImagination(destCompData[0].imagination);
|
|
||||||
SetArmor(destCompData[0].armor);
|
|
||||||
|
|
||||||
SetMaxHealth(destCompData[0].life);
|
|
||||||
SetMaxImagination(destCompData[0].imagination);
|
|
||||||
SetMaxArmor(destCompData[0].armor);
|
|
||||||
|
|
||||||
SetIsSmashable(destCompData[0].isSmashable);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
SetHealth(1);
|
|
||||||
SetImagination(0);
|
|
||||||
SetArmor(0);
|
|
||||||
|
|
||||||
SetMaxHealth(1);
|
|
||||||
SetMaxImagination(0);
|
|
||||||
SetMaxArmor(0);
|
|
||||||
|
|
||||||
SetIsSmashable(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DestroyableComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) {
|
void DestroyableComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) {
|
||||||
if (bIsInitialUpdate) {
|
if (bIsInitialUpdate) {
|
||||||
outBitStream.Write1(); // always write these on construction
|
outBitStream.Write1(); // always write these on construction
|
||||||
|
|||||||
@@ -36,12 +36,6 @@ public:
|
|||||||
void LoadFromXml(const tinyxml2::XMLDocument& doc) override;
|
void LoadFromXml(const tinyxml2::XMLDocument& doc) override;
|
||||||
void UpdateXml(tinyxml2::XMLDocument& doc) override;
|
void UpdateXml(tinyxml2::XMLDocument& doc) override;
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the component using a different LOT
|
|
||||||
* @param templateID the ID to use for initialization
|
|
||||||
*/
|
|
||||||
void Reinitialize(LOT templateID);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the health of this entity. Makes sure this is serialized on the next tick and if this is a character its
|
* Sets the health of this entity. Makes sure this is serialized on the next tick and if this is a character its
|
||||||
* stats will also update.
|
* stats will also update.
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ file(COPY ${COMPONENT_TEST_DATA} DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
|||||||
|
|
||||||
# Add the executable. Remember to add all tests above this!
|
# Add the executable. Remember to add all tests above this!
|
||||||
add_executable(dGameTests ${DGAMETEST_SOURCES})
|
add_executable(dGameTests ${DGAMETEST_SOURCES})
|
||||||
target_include_directories(dGameTests PRIVATE "." "${PROJECT_SOURCE_DIR}/dChatServer")
|
target_include_directories(dGameTests PRIVATE "." "${PROJECT_SOURCE_DIR}/dChatServer" "${PROJECT_SOURCE_DIR}/dScripts")
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
add_custom_target(dGameTestsLink
|
add_custom_target(dGameTestsLink
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ set(DCOMPONENTS_TESTS
|
|||||||
"PetComponentTests.cpp"
|
"PetComponentTests.cpp"
|
||||||
"SimplePhysicsComponentTests.cpp"
|
"SimplePhysicsComponentTests.cpp"
|
||||||
"SavingTests.cpp"
|
"SavingTests.cpp"
|
||||||
|
"ReplicaComponentOrderTests.cpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Get the folder name and prepend it to the files above
|
# Get the folder name and prepend it to the files above
|
||||||
|
|||||||
561
tests/dGameTests/dComponentsTests/ReplicaComponentOrderTests.cpp
Normal file
561
tests/dGameTests/dComponentsTests/ReplicaComponentOrderTests.cpp
Normal file
@@ -0,0 +1,561 @@
|
|||||||
|
#include "GameDependencies.h"
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "BitStream.h"
|
||||||
|
#include "CDClientDatabase.h"
|
||||||
|
#include "Character.h"
|
||||||
|
#include "Entity.h"
|
||||||
|
#include "eReplicaComponentType.h"
|
||||||
|
#include "eReplicaPacketType.h"
|
||||||
|
#include "tinyxml2.h"
|
||||||
|
|
||||||
|
#include "AchievementVendorComponent.h"
|
||||||
|
#include "BaseCombatAIComponent.h"
|
||||||
|
#include "BouncerComponent.h"
|
||||||
|
#include "BuffComponent.h"
|
||||||
|
#include "CharacterComponent.h"
|
||||||
|
#include "CollectibleComponent.h"
|
||||||
|
#include "ControllablePhysicsComponent.h"
|
||||||
|
#include "DestroyableComponent.h"
|
||||||
|
#include "DonationVendorComponent.h"
|
||||||
|
#include "HavokVehiclePhysicsComponent.h"
|
||||||
|
#include "InventoryComponent.h"
|
||||||
|
#include "ItemComponent.h"
|
||||||
|
#include "LevelProgressionComponent.h"
|
||||||
|
#include "LUPExhibitComponent.h"
|
||||||
|
#include "MiniGameControlComponent.h"
|
||||||
|
#include "ModelComponent.h"
|
||||||
|
#include "ModuleAssemblyComponent.h"
|
||||||
|
#include "MovingPlatformComponent.h"
|
||||||
|
#include "PetComponent.h"
|
||||||
|
#include "PhantomPhysicsComponent.h"
|
||||||
|
#include "PlayerForcedMovementComponent.h"
|
||||||
|
#include "PossessableComponent.h"
|
||||||
|
#include "PossessorComponent.h"
|
||||||
|
#include "QuickBuildComponent.h"
|
||||||
|
#include "RacingControlComponent.h"
|
||||||
|
#include "RacingSoundTriggerComponent.h"
|
||||||
|
#include "RenderComponent.h"
|
||||||
|
#include "RigidbodyPhantomPhysicsComponent.h"
|
||||||
|
#include "ScriptComponent.h"
|
||||||
|
#include "ScriptedActivityComponent.h"
|
||||||
|
#include "ShootingGalleryComponent.h"
|
||||||
|
#include "SimplePhysicsComponent.h"
|
||||||
|
#include "SkillComponent.h"
|
||||||
|
#include "SoundTriggerComponent.h"
|
||||||
|
#include "SwitchComponent.h"
|
||||||
|
#include "VendorComponent.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entity::WriteComponents writes the components in the client's order from a list of component types. Before, the
|
||||||
|
* order was spelled out component by component, with flags for where the destroyable went. That code is kept below,
|
||||||
|
* unchanged apart from looking components up by class, as the oracle: the bytes must not change.
|
||||||
|
*/
|
||||||
|
namespace {
|
||||||
|
template<typename T>
|
||||||
|
bool TryGet(const Entity& entity, T*& component) {
|
||||||
|
component = entity.GetComponent<T>();
|
||||||
|
return component != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OldWriteComponents(const Entity& entity, RakNet::BitStream& outBitStream, eReplicaPacketType packetType) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This has to be done in a specific order.
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool destroyableSerialized = false;
|
||||||
|
bool bIsInitialUpdate = packetType == eReplicaPacketType::CONSTRUCTION;
|
||||||
|
|
||||||
|
PossessableComponent* possessableComponent;
|
||||||
|
if (TryGet(entity, possessableComponent)) {
|
||||||
|
possessableComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
ModuleAssemblyComponent* moduleAssemblyComponent;
|
||||||
|
if (TryGet(entity, moduleAssemblyComponent)) {
|
||||||
|
moduleAssemblyComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
ControllablePhysicsComponent* controllablePhysicsComponent;
|
||||||
|
if (TryGet(entity, controllablePhysicsComponent)) {
|
||||||
|
controllablePhysicsComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
SimplePhysicsComponent* simplePhysicsComponent;
|
||||||
|
if (TryGet(entity, simplePhysicsComponent)) {
|
||||||
|
simplePhysicsComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
RigidbodyPhantomPhysicsComponent* rigidbodyPhantomPhysics;
|
||||||
|
if (TryGet(entity, rigidbodyPhantomPhysics)) {
|
||||||
|
rigidbodyPhantomPhysics->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
HavokVehiclePhysicsComponent* havokVehiclePhysicsComponent;
|
||||||
|
if (TryGet(entity, havokVehiclePhysicsComponent)) {
|
||||||
|
havokVehiclePhysicsComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
PhantomPhysicsComponent* phantomPhysicsComponent;
|
||||||
|
if (TryGet(entity, phantomPhysicsComponent)) {
|
||||||
|
phantomPhysicsComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
SoundTriggerComponent* soundTriggerComponent;
|
||||||
|
if (TryGet(entity, soundTriggerComponent)) {
|
||||||
|
soundTriggerComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
RacingSoundTriggerComponent* racingSoundTriggerComponent;
|
||||||
|
if (TryGet(entity, racingSoundTriggerComponent)) {
|
||||||
|
racingSoundTriggerComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
BuffComponent* buffComponent;
|
||||||
|
if (TryGet(entity, buffComponent)) {
|
||||||
|
buffComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
|
|
||||||
|
DestroyableComponent* destroyableComponent;
|
||||||
|
if (TryGet(entity, destroyableComponent)) {
|
||||||
|
destroyableComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
|
}
|
||||||
|
destroyableSerialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CollectibleComponent* collectibleComponent;
|
||||||
|
if (TryGet(entity, collectibleComponent)) {
|
||||||
|
DestroyableComponent* destroyableComponent;
|
||||||
|
if (TryGet(entity, destroyableComponent) && !destroyableSerialized) {
|
||||||
|
destroyableComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
|
}
|
||||||
|
destroyableSerialized = true;
|
||||||
|
collectibleComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
PetComponent* petComponent;
|
||||||
|
if (TryGet(entity, petComponent)) {
|
||||||
|
petComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
CharacterComponent* characterComponent;
|
||||||
|
if (TryGet(entity, characterComponent)) {
|
||||||
|
|
||||||
|
PossessorComponent* possessorComponent;
|
||||||
|
if (TryGet(entity, possessorComponent)) {
|
||||||
|
possessorComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
|
} else {
|
||||||
|
// Should never happen, but just to be safe
|
||||||
|
outBitStream.Write0();
|
||||||
|
}
|
||||||
|
|
||||||
|
LevelProgressionComponent* levelProgressionComponent;
|
||||||
|
if (TryGet(entity, levelProgressionComponent)) {
|
||||||
|
levelProgressionComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
|
} else {
|
||||||
|
// Should never happen, but just to be safe
|
||||||
|
outBitStream.Write0();
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerForcedMovementComponent* playerForcedMovementComponent;
|
||||||
|
if (TryGet(entity, playerForcedMovementComponent)) {
|
||||||
|
playerForcedMovementComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
|
} else {
|
||||||
|
// Should never happen, but just to be safe
|
||||||
|
outBitStream.Write0();
|
||||||
|
}
|
||||||
|
|
||||||
|
characterComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemComponent* itemComponent;
|
||||||
|
if (TryGet(entity, itemComponent)) {
|
||||||
|
itemComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
InventoryComponent* inventoryComponent;
|
||||||
|
if (TryGet(entity, inventoryComponent)) {
|
||||||
|
inventoryComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
ScriptComponent* scriptComponent;
|
||||||
|
if (TryGet(entity, scriptComponent)) {
|
||||||
|
scriptComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
SkillComponent* skillComponent;
|
||||||
|
if (TryGet(entity, skillComponent)) {
|
||||||
|
skillComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseCombatAIComponent* baseCombatAiComponent;
|
||||||
|
if (TryGet(entity, baseCombatAiComponent)) {
|
||||||
|
baseCombatAiComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
QuickBuildComponent* quickBuildComponent;
|
||||||
|
if (TryGet(entity, quickBuildComponent)) {
|
||||||
|
DestroyableComponent* destroyableComponent;
|
||||||
|
if (TryGet(entity, destroyableComponent) && !destroyableSerialized) {
|
||||||
|
destroyableComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
|
}
|
||||||
|
destroyableSerialized = true;
|
||||||
|
quickBuildComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
MovingPlatformComponent* movingPlatformComponent;
|
||||||
|
if (TryGet(entity, movingPlatformComponent)) {
|
||||||
|
movingPlatformComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
SwitchComponent* switchComponent;
|
||||||
|
if (TryGet(entity, switchComponent)) {
|
||||||
|
switchComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
VendorComponent* vendorComponent;
|
||||||
|
if (TryGet(entity, vendorComponent)) {
|
||||||
|
vendorComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
DonationVendorComponent* donationVendorComponent;
|
||||||
|
if (TryGet(entity, donationVendorComponent)) {
|
||||||
|
donationVendorComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
AchievementVendorComponent* achievementVendorComponent;
|
||||||
|
if (TryGet(entity, achievementVendorComponent)) {
|
||||||
|
achievementVendorComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
BouncerComponent* bouncerComponent;
|
||||||
|
if (TryGet(entity, bouncerComponent)) {
|
||||||
|
bouncerComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
ScriptedActivityComponent* scriptedActivityComponent;
|
||||||
|
if (TryGet(entity, scriptedActivityComponent)) {
|
||||||
|
scriptedActivityComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
ShootingGalleryComponent* shootingGalleryComponent;
|
||||||
|
if (TryGet(entity, shootingGalleryComponent)) {
|
||||||
|
shootingGalleryComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
RacingControlComponent* racingControlComponent;
|
||||||
|
if (TryGet(entity, racingControlComponent)) {
|
||||||
|
racingControlComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
LUPExhibitComponent* lupExhibitComponent;
|
||||||
|
if (TryGet(entity, lupExhibitComponent)) {
|
||||||
|
lupExhibitComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
ModelComponent* modelComponent;
|
||||||
|
if (TryGet(entity, modelComponent)) {
|
||||||
|
modelComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderComponent* renderComponent;
|
||||||
|
if (TryGet(entity, renderComponent)) {
|
||||||
|
renderComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modelComponent || !destroyableSerialized) {
|
||||||
|
DestroyableComponent* destroyableComponent;
|
||||||
|
if (TryGet(entity, destroyableComponent) && !destroyableSerialized) {
|
||||||
|
destroyableComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
|
destroyableSerialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MiniGameControlComponent* miniGameControlComponent;
|
||||||
|
if (TryGet(entity, miniGameControlComponent)) {
|
||||||
|
miniGameControlComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BBB Component, unused currently
|
||||||
|
// Need to to write0 so that is serialized correctly
|
||||||
|
// TODO: Implement BBB Component
|
||||||
|
outBitStream.Write0();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Some components read the CDClient in their constructors; give them an empty one
|
||||||
|
void ConnectEmptyCDClient() {
|
||||||
|
if (CDClientDatabase::isConnected) return;
|
||||||
|
CDClientDatabase::Connect(":memory:");
|
||||||
|
for (const auto* table : {
|
||||||
|
"ComponentsRegistry (id INTEGER, component_type INTEGER, component_id INTEGER)",
|
||||||
|
"BaseCombatAIComponent (id INTEGER, aggroRadius REAL, tetherSpeed REAL, pursuitSpeed REAL, softTetherRadius REAL, hardTetherRadius REAL, minRoundLength REAL, maxRoundLength REAL, combatRoundLength REAL)",
|
||||||
|
"ItemSets (setID INTEGER, itemIDs TEXT)",
|
||||||
|
"ObjectSkills (objectTemplate INTEGER, skillID INTEGER, castOnType INTEGER, AICombatWeight INTEGER)",
|
||||||
|
"SkillBehavior (skillID INTEGER, behaviorID INTEGER)",
|
||||||
|
"RenderComponent (id INTEGER)",
|
||||||
|
"BuffParameters (BuffID INTEGER)",
|
||||||
|
"PossessableComponent (id INTEGER, possessionType INTEGER, depossessOnHit INTEGER, skillSet INTEGER)",
|
||||||
|
}) {
|
||||||
|
CDClientDatabase::ExecuteDML(std::string("CREATE TABLE ") + table + ";");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> Bytes(RakNet::BitStream& stream) {
|
||||||
|
return { stream.GetData(), stream.GetData() + stream.GetNumberOfBytesUsed() };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ReplicaComponentOrderTest : public GameDependenciesTest {
|
||||||
|
protected:
|
||||||
|
std::vector<std::unique_ptr<Character>> characters;
|
||||||
|
|
||||||
|
void SetUp() override {
|
||||||
|
SetUpDependencies();
|
||||||
|
ConnectEmptyCDClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TearDown() override {
|
||||||
|
TearDownDependencies();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Entity> MakePlayer() {
|
||||||
|
auto entity = std::make_unique<Entity>(1, info);
|
||||||
|
auto& character = characters.emplace_back(std::make_unique<Character>(1, nullptr));
|
||||||
|
tinyxml2::XMLDocument doc;
|
||||||
|
doc.LoadFile("./test_xml_data.xml");
|
||||||
|
tinyxml2::XMLPrinter printer{ 0, true, 0 };
|
||||||
|
doc.Print(&printer);
|
||||||
|
character->_setXmlData(printer.CStr());
|
||||||
|
character->_doQuickXMLDataParse();
|
||||||
|
entity->SetCharacter(character.get());
|
||||||
|
character->SetEntity(entity.get());
|
||||||
|
entity->AddComponent<ControllablePhysicsComponent>(-1);
|
||||||
|
entity->AddComponent<BuffComponent>(-1);
|
||||||
|
entity->AddComponent<DestroyableComponent>(-1)->SetMaxHealth(4.0f);
|
||||||
|
entity->AddComponent<PossessorComponent>(-1);
|
||||||
|
entity->AddComponent<LevelProgressionComponent>(-1);
|
||||||
|
entity->AddComponent<PlayerForcedMovementComponent>(-1);
|
||||||
|
entity->AddComponent<CharacterComponent>(-1, character.get(), UNASSIGNED_SYSTEM_ADDRESS);
|
||||||
|
entity->AddComponent<InventoryComponent>(-1);
|
||||||
|
entity->AddComponent<SkillComponent>(-1);
|
||||||
|
entity->AddComponent<RenderComponent>(-1);
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serializes an entity built by `build` with the old and the new code (each on its own copy, as serializing
|
||||||
|
// clears dirty flags) and expects the same bits, for the construction and a serialization.
|
||||||
|
void ExpectSameBytes(const std::function<void(Entity&)>& build) {
|
||||||
|
for (const auto packetType : { eReplicaPacketType::CONSTRUCTION, eReplicaPacketType::SERIALIZATION }) {
|
||||||
|
Entity oldEntity(15, info);
|
||||||
|
Entity newEntity(15, info);
|
||||||
|
// Same random numbers for both (a vehicle picks a random end behavior)
|
||||||
|
Game::randomEngine.seed(1);
|
||||||
|
build(oldEntity);
|
||||||
|
Game::randomEngine.seed(1);
|
||||||
|
build(newEntity);
|
||||||
|
RakNet::BitStream oldStream;
|
||||||
|
RakNet::BitStream newStream;
|
||||||
|
OldWriteComponents(oldEntity, oldStream, packetType);
|
||||||
|
newEntity.WriteComponents(newStream, packetType);
|
||||||
|
EXPECT_GT(newStream.GetNumberOfBitsUsed(), 1u);
|
||||||
|
EXPECT_EQ(oldStream.GetNumberOfBitsUsed(), newStream.GetNumberOfBitsUsed()) << "packet type " << static_cast<int>(packetType);
|
||||||
|
const auto oldBytes = Bytes(oldStream);
|
||||||
|
const auto newBytes = Bytes(newStream);
|
||||||
|
const auto mismatch = std::ranges::mismatch(oldBytes, newBytes);
|
||||||
|
EXPECT_EQ(mismatch.in1, oldBytes.end()) << "packet type " << static_cast<int>(packetType) << ", first different byte " << (mismatch.in1 - oldBytes.begin());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(ReplicaComponentTypeTest, MatchesComponentsRegistry) {
|
||||||
|
// ComponentsRegistry.component_type values
|
||||||
|
EXPECT_EQ(static_cast<uint32_t>(eReplicaComponentType::DESTROYABLE), 7u);
|
||||||
|
EXPECT_EQ(static_cast<uint32_t>(eReplicaComponentType::BUFF), 98u);
|
||||||
|
EXPECT_EQ(DestroyableComponent::ComponentType, eReplicaComponentType::DESTROYABLE);
|
||||||
|
EXPECT_EQ(BuffComponent::ComponentType, eReplicaComponentType::BUFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ReplicaComponentOrderTest, Player) {
|
||||||
|
for (const auto packetType : { eReplicaPacketType::CONSTRUCTION, eReplicaPacketType::SERIALIZATION }) {
|
||||||
|
auto oldEntity = MakePlayer();
|
||||||
|
auto newEntity = MakePlayer();
|
||||||
|
RakNet::BitStream oldStream;
|
||||||
|
RakNet::BitStream newStream;
|
||||||
|
OldWriteComponents(*oldEntity, oldStream, packetType);
|
||||||
|
newEntity->WriteComponents(newStream, packetType);
|
||||||
|
EXPECT_EQ(oldStream.GetNumberOfBitsUsed(), newStream.GetNumberOfBitsUsed());
|
||||||
|
EXPECT_EQ(Bytes(oldStream), Bytes(newStream));
|
||||||
|
oldEntity->SetCharacter(nullptr);
|
||||||
|
newEntity->SetCharacter(nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ReplicaComponentOrderTest, Enemy) {
|
||||||
|
ExpectSameBytes([](Entity& entity) {
|
||||||
|
entity.AddComponent<ControllablePhysicsComponent>(-1);
|
||||||
|
entity.AddComponent<BuffComponent>(-1);
|
||||||
|
auto* destroyable = entity.AddComponent<DestroyableComponent>(-1);
|
||||||
|
destroyable->SetMaxHealth(8.0f);
|
||||||
|
destroyable->SetHealth(8);
|
||||||
|
destroyable->AddFactionNoLookup(4);
|
||||||
|
entity.AddComponent<ScriptComponent>(-1, "", true);
|
||||||
|
entity.AddComponent<SkillComponent>(-1);
|
||||||
|
entity.AddComponent<BaseCombatAIComponent>(-1);
|
||||||
|
entity.AddComponent<RenderComponent>(-1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ReplicaComponentOrderTest, SmashableWithDestructibleRow) {
|
||||||
|
ExpectSameBytes([](Entity& entity) {
|
||||||
|
entity.AddComponent<SimplePhysicsComponent>(-1);
|
||||||
|
entity.AddComponent<BuffComponent>(-1);
|
||||||
|
entity.AddComponent<DestroyableComponent>(-1)->SetIsSmashable(true);
|
||||||
|
entity.AddComponent<RenderComponent>(-1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ReplicaComponentOrderTest, IsSmashableWithoutRegistryEntry) {
|
||||||
|
// Written after the render component
|
||||||
|
ExpectSameBytes([](Entity& entity) {
|
||||||
|
entity.AddComponent<SimplePhysicsComponent>(-1);
|
||||||
|
entity.AddComponent<ScriptComponent>(-1, "", true);
|
||||||
|
auto* destroyable = entity.AddComponent<DestroyableComponent>(-1);
|
||||||
|
destroyable->SetIsSmashable(true);
|
||||||
|
destroyable->SetMaxHealth(1.0f);
|
||||||
|
destroyable->SetHealth(1);
|
||||||
|
entity.AddComponent<RenderComponent>(-1);
|
||||||
|
entity.AddComponent<MiniGameControlComponent>(-1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ReplicaComponentOrderTest, QuickBuildWithoutDestroyable) {
|
||||||
|
ExpectSameBytes([](Entity& entity) {
|
||||||
|
entity.AddComponent<SimplePhysicsComponent>(-1);
|
||||||
|
entity.AddComponent<ScriptComponent>(-1, "", true);
|
||||||
|
entity.AddComponent<QuickBuildComponent>(-1);
|
||||||
|
entity.AddComponent<RenderComponent>(-1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ReplicaComponentOrderTest, QuickBuildWithoutRegistryEntry) {
|
||||||
|
// Written right before the quick build, after the script
|
||||||
|
ExpectSameBytes([](Entity& entity) {
|
||||||
|
entity.AddComponent<SimplePhysicsComponent>(-1);
|
||||||
|
entity.AddComponent<DestroyableComponent>(-1)->SetIsSmashable(true);
|
||||||
|
entity.AddComponent<ScriptComponent>(-1, "", true);
|
||||||
|
entity.AddComponent<SkillComponent>(-1);
|
||||||
|
entity.AddComponent<QuickBuildComponent>(-1);
|
||||||
|
entity.AddComponent<MovingPlatformComponent>(-1, "");
|
||||||
|
entity.AddComponent<RenderComponent>(-1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ReplicaComponentOrderTest, QuickBuildWithRegistryEntry) {
|
||||||
|
ExpectSameBytes([](Entity& entity) {
|
||||||
|
entity.AddComponent<SimplePhysicsComponent>(-1);
|
||||||
|
entity.AddComponent<BuffComponent>(-1);
|
||||||
|
entity.AddComponent<DestroyableComponent>(-1)->SetMaxHealth(3.0f);
|
||||||
|
entity.AddComponent<ScriptComponent>(-1, "", true);
|
||||||
|
entity.AddComponent<QuickBuildComponent>(-1);
|
||||||
|
entity.AddComponent<RenderComponent>(-1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ReplicaComponentOrderTest, CollectibleWithoutRegistryEntry) {
|
||||||
|
ExpectSameBytes([](Entity& entity) {
|
||||||
|
entity.AddComponent<SimplePhysicsComponent>(-1);
|
||||||
|
entity.AddComponent<DestroyableComponent>(-1);
|
||||||
|
entity.AddComponent<CollectibleComponent>(-1, 12);
|
||||||
|
entity.AddComponent<ScriptComponent>(-1, "", true);
|
||||||
|
entity.AddComponent<RenderComponent>(-1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ReplicaComponentOrderTest, CollectibleWithRegistryEntry) {
|
||||||
|
ExpectSameBytes([](Entity& entity) {
|
||||||
|
entity.AddComponent<SimplePhysicsComponent>(-1);
|
||||||
|
entity.AddComponent<BuffComponent>(-1);
|
||||||
|
entity.AddComponent<DestroyableComponent>(-1)->SetIsSmashable(true);
|
||||||
|
entity.AddComponent<CollectibleComponent>(-1, 3);
|
||||||
|
entity.AddComponent<RenderComponent>(-1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ReplicaComponentOrderTest, Npc) {
|
||||||
|
ExpectSameBytes([](Entity& entity) {
|
||||||
|
entity.AddComponent<ControllablePhysicsComponent>(-1);
|
||||||
|
entity.AddComponent<BuffComponent>(-1);
|
||||||
|
entity.AddComponent<DestroyableComponent>(-1);
|
||||||
|
entity.AddComponent<InventoryComponent>(-1);
|
||||||
|
entity.AddComponent<ScriptComponent>(-1, "", true);
|
||||||
|
entity.AddComponent<SkillComponent>(-1);
|
||||||
|
entity.AddComponent<SwitchComponent>(-1);
|
||||||
|
entity.AddComponent<BouncerComponent>(-1);
|
||||||
|
entity.AddComponent<RenderComponent>(-1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ReplicaComponentOrderTest, Pet) {
|
||||||
|
ExpectSameBytes([](Entity& entity) {
|
||||||
|
entity.AddComponent<ControllablePhysicsComponent>(-1);
|
||||||
|
entity.AddComponent<BuffComponent>(-1);
|
||||||
|
entity.AddComponent<DestroyableComponent>(-1);
|
||||||
|
entity.AddComponent<PetComponent>(1);
|
||||||
|
entity.AddComponent<SkillComponent>(-1);
|
||||||
|
entity.AddComponent<RenderComponent>(-1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ReplicaComponentOrderTest, Vehicle) {
|
||||||
|
ExpectSameBytes([](Entity& entity) {
|
||||||
|
entity.AddComponent<PossessableComponent>(-1);
|
||||||
|
entity.AddComponent<ModuleAssemblyComponent>(-1);
|
||||||
|
entity.AddComponent<HavokVehiclePhysicsComponent>(-1);
|
||||||
|
entity.AddComponent<BuffComponent>(-1);
|
||||||
|
entity.AddComponent<DestroyableComponent>(-1);
|
||||||
|
entity.AddComponent<RenderComponent>(-1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ReplicaComponentOrderTest, Model) {
|
||||||
|
// A model's destroyable is written after the render component
|
||||||
|
ExpectSameBytes([](Entity& entity) {
|
||||||
|
entity.AddComponent<SimplePhysicsComponent>(-1);
|
||||||
|
entity.AddComponent<ModelComponent>(-1);
|
||||||
|
entity.AddComponent<RenderComponent>(-1);
|
||||||
|
entity.AddComponent<DestroyableComponent>(-1)->SetIsSmashable(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ReplicaComponentOrderTest, EveryListedComponent) {
|
||||||
|
ExpectSameBytes([](Entity& entity) {
|
||||||
|
entity.AddComponent<PossessableComponent>(-1);
|
||||||
|
entity.AddComponent<ModuleAssemblyComponent>(-1);
|
||||||
|
entity.AddComponent<ControllablePhysicsComponent>(-1);
|
||||||
|
entity.AddComponent<SimplePhysicsComponent>(-1);
|
||||||
|
entity.AddComponent<RigidbodyPhantomPhysicsComponent>(-1);
|
||||||
|
entity.AddComponent<HavokVehiclePhysicsComponent>(-1);
|
||||||
|
entity.AddComponent<PhantomPhysicsComponent>(-1);
|
||||||
|
entity.AddComponent<SoundTriggerComponent>(-1);
|
||||||
|
entity.AddComponent<BuffComponent>(-1);
|
||||||
|
entity.AddComponent<DestroyableComponent>(-1);
|
||||||
|
entity.AddComponent<CollectibleComponent>(-1, 1);
|
||||||
|
entity.AddComponent<ItemComponent>(-1);
|
||||||
|
entity.AddComponent<InventoryComponent>(-1);
|
||||||
|
entity.AddComponent<ScriptComponent>(-1, "", true);
|
||||||
|
entity.AddComponent<SkillComponent>(-1);
|
||||||
|
entity.AddComponent<QuickBuildComponent>(-1);
|
||||||
|
entity.AddComponent<SwitchComponent>(-1);
|
||||||
|
entity.AddComponent<BouncerComponent>(-1);
|
||||||
|
entity.AddComponent<ScriptedActivityComponent>(-1);
|
||||||
|
entity.AddComponent<LUPExhibitComponent>(-1);
|
||||||
|
entity.AddComponent<ModelComponent>(-1);
|
||||||
|
entity.AddComponent<RenderComponent>(-1);
|
||||||
|
entity.AddComponent<MiniGameControlComponent>(-1);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -74,7 +74,7 @@ TEST(CDClientSchemaTest, ComponentTables) {
|
|||||||
};
|
};
|
||||||
EXPECT_EQ(name(eReplicaComponentType::VENDOR), "VendorComponent");
|
EXPECT_EQ(name(eReplicaComponentType::VENDOR), "VendorComponent");
|
||||||
EXPECT_EQ(name(eReplicaComponentType::RENDER), "RenderComponent");
|
EXPECT_EQ(name(eReplicaComponentType::RENDER), "RenderComponent");
|
||||||
EXPECT_EQ(name(eReplicaComponentType::BUFF), "DestructibleComponent");
|
EXPECT_EQ(name(eReplicaComponentType::DESTROYABLE), "DestructibleComponent");
|
||||||
EXPECT_EQ(name(eReplicaComponentType::SIMPLE_PHYSICS), "PhysicsComponent");
|
EXPECT_EQ(name(eReplicaComponentType::SIMPLE_PHYSICS), "PhysicsComponent");
|
||||||
EXPECT_EQ(name(eReplicaComponentType::PET), "-");
|
EXPECT_EQ(name(eReplicaComponentType::PET), "-");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user