fix(replica): send each object's real age as time_since_created_on_server

Live sent the milliseconds since the object was created on the server in
every construction header (nonzero in 142,258 of 145,677 captured
constructions; the same object constructed again later carries a larger
value by the time elapsed; the local player's own construction on load is
0). DLU always wrote 0. The client stores it in the object's config
(LWOGameObject::time_since_created_on_server).

Entities now remember when they were created (steady clock) and write
their age.

Test: ReplicaConstructionTest.TimeSinceCreatedOnServerIsTheObjectsAge.

Check in game: zone in and walk around several worlds; objects, NPCs,
enemies and other players appear and animate as before (no objects
popping, stuck or misplaced animations on platforms/spinners).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-09-29 04:41:44 -05:00
parent d5563ad5da
commit b035f60e72
4 changed files with 99 additions and 1 deletions

View File

@@ -997,7 +997,9 @@ void Entity::WriteBaseReplicaData(RakNet::BitStream& outBitStream, eReplicaPacke
outBitStream.Write<uint8_t>(name.size());
outBitStream.Write(name);
outBitStream.Write<uint32_t>(0); //Time since created on server
// Live sent the object's age on this server in milliseconds: 0 for an object constructed as it is created
// (a player's own construction on load), the time since load for zone objects.
outBitStream.Write<uint32_t>(GetTimeSinceCreatedMs());
const auto& syncLDF = GetVar<std::vector<std::u16string>>(u"syncLDF");

View File

@@ -1,5 +1,6 @@
#pragma once
#include <chrono>
#include <map>
#include <functional>
#include <tuple>
@@ -218,6 +219,11 @@ public:
std::unordered_map<eReplicaComponentType, Component*>& GetComponents() { return m_Components; } // TODO: Remove
void WriteBaseReplicaData(RakNet::BitStream& outBitStream, eReplicaPacketType packetType);
// Milliseconds since this object was created on this server (the construction's time_since_created_on_server).
uint32_t GetTimeSinceCreatedMs() const {
return static_cast<uint32_t>(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - m_CreationTime).count());
}
void WriteComponents(RakNet::BitStream& outBitStream, eReplicaPacketType packetType) const;
void UpdateXMLDoc(tinyxml2::XMLDocument& doc);
void Update(float deltaTime);
@@ -433,6 +439,9 @@ private:
bool m_IsParentChildDirty = true;
// When this object was created on this server. Construction sends its age from this, as live did.
std::chrono::steady_clock::time_point m_CreationTime = std::chrono::steady_clock::now();
bool m_IsSleeping = false;
/*