refactor(world): separate live lifetime from spatial buckets
Introduce LiveEntityRuntime as the canonical owner of each accepted server-object incarnation, stable local identity, timestamped state, parent relations, runtime components, and exactly-once teardown. Split logical registration from rebucketing so pending landblocks, equipment attachment, pickup re-entry, and GUID replacement reuse the same entity and effect owners. Keep canonical materialized and visible target/radar views distinct, preserve retail leave_world versus exit_world semantics, gate root simulation while cell-less, and track transitional pre-Create F754 owners through delete and session reset. Remove stale-spawn rehydration and make GpuWorldState spatial-only for live objects. Add lifecycle, generation, pending, unload, attachment, event-publication, local-ID, rollback, and effect-cleanup coverage; update architecture, milestones, memory, and the divergence register. Co-Authored-By: Codex <noreply@openai.com>
This commit is contained in:
parent
8a5d77f7f4
commit
8dd996053d
24 changed files with 2449 additions and 631 deletions
|
|
@ -27,20 +27,17 @@ public sealed record ScriptActivationInfo(
|
|||
/// Stops the scripts and live emitters when the entity despawns.
|
||||
///
|
||||
/// <para>
|
||||
/// Handles both server-spawned entities (<c>ServerGuid != 0</c>, keyed by
|
||||
/// ServerGuid) and dat-hydrated entities (<c>ServerGuid == 0</c>, keyed by
|
||||
/// <c>entity.Id</c>). The C.1.5a guard that early-returned for
|
||||
/// Handles both server-spawned and dat-hydrated entities, always keyed by
|
||||
/// canonical local <c>entity.Id</c>. The C.1.5a guard that early-returned for
|
||||
/// <c>ServerGuid == 0</c> was relaxed in C.1.5b so EnvCell static objects
|
||||
/// (which have no server guid because they come from the dat file, not
|
||||
/// the network) also fire their DefaultScript.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Wires alongside <c>EntitySpawnAdapter</c> in <c>GpuWorldState</c>: the
|
||||
/// adapter handles meshes + animation state, the activator handles scripts
|
||||
/// + particles. Both are render-thread-only. The activator is invoked from
|
||||
/// four GpuWorldState fire-sites (AppendLiveEntity, AddLandblock,
|
||||
/// AddEntitiesToExistingLandblock, plus the matching remove paths).
|
||||
/// For live objects this is invoked by <c>LiveEntityRuntime</c> logical
|
||||
/// registration/teardown. <c>GpuWorldState</c> invokes it only for dat-static
|
||||
/// landblock load, promotion, demotion, and unload paths.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
|
|
@ -55,6 +52,7 @@ public sealed class EntityScriptActivator
|
|||
private readonly PhysicsScriptRunner _scriptRunner;
|
||||
private readonly ParticleHookSink _particleSink;
|
||||
private readonly Func<WorldEntity, ScriptActivationInfo?> _resolver;
|
||||
private readonly HashSet<uint> _legacyPendingOwners = new();
|
||||
|
||||
/// <param name="scriptRunner">Already-shipped runner from C.1. Owns the
|
||||
/// (scriptId, entityId) instance table and schedules hooks at their
|
||||
|
|
@ -84,17 +82,14 @@ public sealed class EntityScriptActivator
|
|||
|
||||
/// <summary>
|
||||
/// Resolve the entity's <c>Setup.DefaultScript</c> and fire it through
|
||||
/// the script runner. Keys by <c>entity.ServerGuid</c> when non-zero,
|
||||
/// otherwise by <c>entity.Id</c> (the latter handles dat-hydrated
|
||||
/// EnvCell statics + exterior stabs whose <c>entity.Id</c> lives in
|
||||
/// the <c>0x40xxxxxx</c> range — collision-free with server guids).
|
||||
/// the script runner, keyed by canonical local <c>entity.Id</c>.
|
||||
/// No-op if the entity has no DefaultScript (resolver returns null
|
||||
/// or zero-script).
|
||||
/// </summary>
|
||||
public void OnCreate(WorldEntity entity)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(entity);
|
||||
uint key = entity.ServerGuid != 0 ? entity.ServerGuid : entity.Id;
|
||||
uint key = entity.Id;
|
||||
if (key == 0) return; // malformed entity
|
||||
|
||||
var info = _resolver(entity);
|
||||
|
|
@ -119,10 +114,8 @@ public sealed class EntityScriptActivator
|
|||
|
||||
/// <summary>
|
||||
/// Stop every script instance the runner is tracking for this key, and
|
||||
/// kill every live emitter the sink has attributed to it. Caller picks
|
||||
/// the key (the matching ServerGuid for live entities, or
|
||||
/// <c>entity.Id</c> for dat-hydrated entities — mirror whatever was
|
||||
/// used at <see cref="OnCreate"/>). Idempotent for unknown keys.
|
||||
/// kill every live emitter the sink has attributed to the canonical
|
||||
/// local entity id. Idempotent for unknown keys.
|
||||
/// </summary>
|
||||
public void OnRemove(uint key)
|
||||
{
|
||||
|
|
@ -130,4 +123,49 @@ public sealed class EntityScriptActivator
|
|||
_scriptRunner.StopAllForEntity(key);
|
||||
_particleSink.StopAllForEntity(key, fadeOut: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the temporary server-GUID owner used when F754 precedes the
|
||||
/// target's CreateObject/materialization. Step 4 replaces this with the
|
||||
/// retail mixed pending-packet FIFO. Tracking is required because no
|
||||
/// LiveEntityRecord may exist yet for delete/session teardown.
|
||||
/// </summary>
|
||||
public bool PlayLegacyPending(
|
||||
uint serverGuid,
|
||||
uint scriptDid,
|
||||
Vector3 anchorWorldPosition)
|
||||
{
|
||||
if (serverGuid == 0)
|
||||
return false;
|
||||
bool played = _scriptRunner.Play(scriptDid, serverGuid, anchorWorldPosition);
|
||||
if (played)
|
||||
_legacyPendingOwners.Add(serverGuid);
|
||||
return played;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cleans the temporary server-GUID owner used when F754 arrives before a
|
||||
/// live projection has a canonical local ID. Step 4 replaces this alias
|
||||
/// with the retail mixed pending-packet FIFO; until then teardown must stop
|
||||
/// the alias as well as the normal local owner so an early effect cannot
|
||||
/// outlive its object incarnation.
|
||||
/// </summary>
|
||||
public void OnRemoveLegacyOwner(uint serverGuid, uint canonicalLocalId)
|
||||
{
|
||||
if (serverGuid == 0 || serverGuid == canonicalLocalId)
|
||||
return;
|
||||
if (!_legacyPendingOwners.Remove(serverGuid))
|
||||
return;
|
||||
OnRemove(serverGuid);
|
||||
}
|
||||
|
||||
/// <summary>Stops every pre-Create F754 alias at session teardown.</summary>
|
||||
public void ClearLegacyPendingOwners()
|
||||
{
|
||||
foreach (uint serverGuid in _legacyPendingOwners)
|
||||
OnRemove(serverGuid);
|
||||
_legacyPendingOwners.Clear();
|
||||
}
|
||||
|
||||
public int LegacyPendingOwnerCount => _legacyPendingOwners.Count;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue