mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-11-15 00:07:13 +00:00
refactor(entity): components load their config and save after all exist, as the client
LWOGameObject::MakeReadyForSerialization runs each component's LoadConfigData and then a character's LoadFromSaveData, in the order the components were made, once DoObjectComponentLoad has made them all; then their Startup. Entity::Initialize now does the same with a component list in creation order (the client's componentList); the walk's own save calls (mission, physics, destroyable, level, character, skill, ghost) and the destroyable's call into the buff go. A model's behaviors load in its LoadConfigData; controllable physics places a non-character at construction, as the client's physics takes the spawn position. A component added later still loads at once. Same bytes for every LOT. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -274,7 +274,7 @@ Entity::ObjectLoadState Entity::DoObjectPreLoad() {
|
||||
|
||||
// The player's own components (HandleLocalPlayer makes them for the local player, not from the registry)
|
||||
if (m_Character && m_Character->GetParentUser()) {
|
||||
AddComponent<MissionComponent>(-1)->LoadFromSaveData(m_Character->GetXMLDoc());
|
||||
AddComponent<MissionComponent>(-1);
|
||||
}
|
||||
|
||||
// markedAsPhantom doesn't count when the registry has phantom physics
|
||||
@@ -353,8 +353,7 @@ void Entity::DoObjectComponentLoad(ObjectLoadState& state) {
|
||||
if (controllablePhysicsComponentID > 0) state.modelPhysicsComponentID = controllablePhysicsComponentID;
|
||||
} else if (controllablePhysicsComponentID > 0) {
|
||||
// The component places it: where the level put an object, where a character's save and the zone put it
|
||||
auto* controllablePhysics = AddComponent<ControllablePhysicsComponent>(controllablePhysicsComponentID);
|
||||
if (m_Character) controllablePhysics->LoadFromSaveData(m_Character->GetXMLDoc());
|
||||
AddComponent<ControllablePhysicsComponent>(controllablePhysicsComponentID);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -413,8 +412,7 @@ void Entity::DoObjectComponentLoad(ObjectLoadState& state) {
|
||||
// A collectible's (right after it) and a smashable object's (after the walk) come later, as the client makes them
|
||||
if (destroyableComponentID > 0) {
|
||||
// The row, the level config and a character's save, in the client's order
|
||||
DestroyableComponent* comp = AddComponent<DestroyableComponent>(destroyableComponentID);
|
||||
if (m_Character) comp->LoadFromSaveData(m_Character->GetXMLDoc());
|
||||
AddComponent<DestroyableComponent>(destroyableComponentID);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -431,11 +429,11 @@ void Entity::DoObjectComponentLoad(ObjectLoadState& state) {
|
||||
AddComponent<PossessorComponent>(-1);
|
||||
AddComponent<MountControlComponent>(-1);
|
||||
AddComponent<PetCreatorComponent>(-1);
|
||||
AddComponent<LevelProgressionComponent>(-1)->LoadFromSaveData(m_Character->GetXMLDoc());
|
||||
AddComponent<LevelProgressionComponent>(-1);
|
||||
AddComponent<PlayerForcedMovementComponent>(-1);
|
||||
|
||||
auto& systemAddress = m_Character->GetParentUser() ? m_Character->GetParentUser()->GetSystemAddress() : UNASSIGNED_SYSTEM_ADDRESS;
|
||||
AddComponent<CharacterComponent>(-1, m_Character, systemAddress)->LoadFromSaveData(m_Character->GetXMLDoc());
|
||||
AddComponent<CharacterComponent>(-1, m_Character, systemAddress);
|
||||
|
||||
}
|
||||
break;
|
||||
@@ -510,8 +508,7 @@ void Entity::DoObjectComponentLoad(ObjectLoadState& state) {
|
||||
case eReplicaComponentType::SKILL: {
|
||||
const auto skillID = state.GetComponentID(eReplicaComponentType::SKILL, -1);
|
||||
if (skillID > -1 || m_Character) {
|
||||
auto* const skillComponent = AddComponent<SkillComponent>(skillID);
|
||||
if (m_Character) skillComponent->LoadFromSaveData(m_Character->GetXMLDoc());
|
||||
AddComponent<SkillComponent>(skillID);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -677,7 +674,7 @@ void Entity::DoObjectComponentLoad(ObjectLoadState& state) {
|
||||
else AddComponent<SimplePhysicsComponent>(state.modelPhysicsComponentID == -1 ? 4247 : state.modelPhysicsComponentID);
|
||||
}
|
||||
if (modelType == 0 && !HasComponent(eReplicaComponentType::PATHFINDING)) AddComponent<PathfindingComponent>(-1);
|
||||
AddComponent<ModelComponent>(modelID)->LoadBehaviors();
|
||||
AddComponent<ModelComponent>(modelID);
|
||||
if (!HasComponent(eReplicaComponentType::DESTROYABLE)) {
|
||||
AddComponent<DestroyableComponent>(-1, true);
|
||||
}
|
||||
@@ -995,7 +992,7 @@ void Entity::DoObjectComponentLoad(ObjectLoadState& state) {
|
||||
AddComponent<DestroyableComponent>(-1);
|
||||
}
|
||||
if (m_Character && HasComponent(eReplicaComponentType::CONTROLLABLE_PHYSICS)) {
|
||||
AddComponent<GhostComponent>(-1)->LoadFromSaveData(m_Character->GetXMLDoc());
|
||||
AddComponent<GhostComponent>(-1);
|
||||
}
|
||||
// A mission offer's object gets a proximity monitor after the walk when it has none (the registry's mission offer;
|
||||
// a character's is DLU's own)
|
||||
@@ -1034,11 +1031,20 @@ void Entity::Initialize() {
|
||||
m_ParentEntity->AddChild(this);
|
||||
}
|
||||
|
||||
m_LoadingComponents = true;
|
||||
auto loadState = DoObjectPreLoad();
|
||||
DoObjectComponentLoad(loadState);
|
||||
m_LoadingComponents = false;
|
||||
|
||||
// LWOGameObject::MakeReadyForSerialization (0x0101c230): every component exists; each loads the object's config,
|
||||
// then a character's save, in the order they were made
|
||||
for (auto* const component : m_ComponentList) {
|
||||
component->LoadConfigData();
|
||||
if (m_Character) component->LoadFromSaveData(m_Character->GetXMLDoc());
|
||||
}
|
||||
|
||||
// Every component exists: the client's Startup step
|
||||
for (auto* const component : m_Components | std::views::values) if (component) component->Startup();
|
||||
for (auto* const component : m_ComponentList) component->Startup();
|
||||
m_ComponentsStarted = true;
|
||||
|
||||
// Hacky way to trigger these when the object has had a chance to get constructed
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <typeinfo>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "NiPoint3.h"
|
||||
@@ -460,6 +461,10 @@ private:
|
||||
|
||||
// Every component has had its Startup (the end of Initialize)
|
||||
bool m_ComponentsStarted = false;
|
||||
// Initialize is making the components (DoObjectComponentLoad); their config and save load after
|
||||
bool m_LoadingComponents = false;
|
||||
// The components in the order they were made (the client's componentList)
|
||||
std::vector<Component*> m_ComponentList;
|
||||
std::vector<EntityTimer> m_Timers;
|
||||
std::vector<EntityTimer> m_PendingTimers;
|
||||
std::vector<EntityCallbackTimer> m_CallbackTimers;
|
||||
@@ -651,8 +656,14 @@ inline ComponentType* Entity::AddComponent(VaArgs... args) {
|
||||
// PrepareConfigData); one added after the object started up starts up at once
|
||||
componentToReturn->LoadDataFromTemplate();
|
||||
componentToReturn->PrepareConfigData();
|
||||
componentToReturn->LoadConfigData();
|
||||
if (m_ComponentsStarted) componentToReturn->Startup();
|
||||
if (m_LoadingComponents) {
|
||||
// While the object loads, its config, save and startup come after every component exists
|
||||
// (LWOGameObject::MakeReadyForSerialization), in the order the components were made
|
||||
if (std::ranges::find(m_ComponentList, componentToReturn) == m_ComponentList.end()) m_ComponentList.push_back(componentToReturn);
|
||||
} else {
|
||||
componentToReturn->LoadConfigData();
|
||||
if (m_ComponentsStarted) componentToReturn->Startup();
|
||||
}
|
||||
|
||||
// Finally return the created or already existing component.
|
||||
// Because of the assert above, this should always be a ComponentType* but I need a way to guarantee the map cannot be modifed outside this function
|
||||
|
||||
@@ -274,11 +274,6 @@ void DestroyableComponent::LoadFromSaveData(const tinyxml2::XMLDocument& doc) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* buffComponent = m_Parent->GetComponent<BuffComponent>();
|
||||
|
||||
if (buffComponent != nullptr) {
|
||||
buffComponent->LoadFromSaveData(doc);
|
||||
}
|
||||
|
||||
dest->QueryAttribute("hc", &m_iHealth);
|
||||
dest->QueryAttribute("hm", &m_fMaxHealth);
|
||||
|
||||
@@ -33,6 +33,7 @@ ModelComponent::ModelComponent(Entity* parent, const int32_t componentID) : Comp
|
||||
// The level config: the user model this is
|
||||
void ModelComponent::LoadConfigData() {
|
||||
m_userModelID = m_Parent->GetVarAs<LWOOBJID>(u"userModelID");
|
||||
LoadBehaviors();
|
||||
}
|
||||
|
||||
bool ModelComponent::OnResetModelToDefaults(GameMessages::ResetModelToDefaults& reset) {
|
||||
|
||||
@@ -51,18 +51,19 @@ ControllablePhysicsComponent::ControllablePhysicsComponent(Entity* entity, const
|
||||
m_ImmuneToStunMoveCount = 0;
|
||||
m_ImmuneToStunTurnCount = 0;
|
||||
m_ImmuneToStunUseItemCount = 0;
|
||||
|
||||
// An object that isn't a character starts where the level put it (from the object's spawn info, as the client's
|
||||
// physics does; a character's place comes with its save)
|
||||
if (!m_Parent->GetCharacter()) {
|
||||
SetPosition(m_Parent->GetDefaultPosition());
|
||||
SetRotation(m_Parent->GetDefaultRotation());
|
||||
}
|
||||
}
|
||||
|
||||
// After the physics config: a character's body, in the physics component's group
|
||||
void ControllablePhysicsComponent::LoadConfigData() {
|
||||
PhysicsComponent::LoadConfigData();
|
||||
|
||||
// An object that isn't a character starts where the level put it (a character's place comes with its save)
|
||||
if (!m_Parent->GetCharacter()) {
|
||||
SetPosition(m_Parent->GetDefaultPosition());
|
||||
SetRotation(m_Parent->GetDefaultRotation());
|
||||
}
|
||||
|
||||
if (m_Parent->GetLOT() != 1) // Other physics entities we care about will be added by BaseCombatAI
|
||||
return;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user