using AcDream.Core.Net;
using AcDream.Core.Net.Messages;
using AcDream.Core.Physics;
namespace AcDream.App.World;
internal enum DormantCreateDisposition
{
NoDormantRecord,
ExistingGeneration,
NewGeneration,
StaleGeneration,
}
///
/// Cold, data-only ownership for ACE objects that left the client's active
/// visibility set. Active render, physics, animation, effect, and collision
/// owners are torn down before a snapshot enters this store.
///
/// ACE retains KnownObjects across ordinary teleports and does not reliably
/// repeat CreateObject when a destination is revisited. Keeping the accepted
/// CreateObject snapshot therefore preserves the server object's identity
/// without retaining any frame-time or GPU work.
///
internal sealed class DormantLiveEntityStore
{
private readonly Dictionary _spawns = new();
public int Count => _spawns.Count;
public DormantCreateDisposition ClassifyCreate(
WorldSession.EntitySpawn incoming)
{
if (!_spawns.TryGetValue(incoming.Guid, out WorldSession.EntitySpawn retained))
return DormantCreateDisposition.NoDormantRecord;
if (PhysicsTimestampGate.IsNewer(
retained.InstanceSequence,
incoming.InstanceSequence))
{
return DormantCreateDisposition.NewGeneration;
}
if (PhysicsTimestampGate.IsNewer(
incoming.InstanceSequence,
retained.InstanceSequence))
{
return DormantCreateDisposition.StaleGeneration;
}
return DormantCreateDisposition.ExistingGeneration;
}
public void Retain(WorldSession.EntitySpawn snapshot)
{
if (_spawns.TryGetValue(snapshot.Guid, out WorldSession.EntitySpawn current)
&& PhysicsTimestampGate.IsNewer(
snapshot.InstanceSequence,
current.InstanceSequence))
{
return;
}
_spawns[snapshot.Guid] = snapshot;
}
public bool RemoveThroughAcceptedCreate(WorldSession.EntitySpawn accepted)
{
DormantCreateDisposition disposition = ClassifyCreate(accepted);
if (disposition is DormantCreateDisposition.StaleGeneration
or DormantCreateDisposition.NoDormantRecord)
{
return false;
}
return _spawns.Remove(accepted.Guid);
}
public bool RemoveExact(DeleteObject.Parsed delete)
{
if (!_spawns.TryGetValue(delete.Guid, out WorldSession.EntitySpawn retained)
|| retained.InstanceSequence != delete.InstanceSequence)
{
return false;
}
return _spawns.Remove(delete.Guid);
}
public WorldSession.EntitySpawn[] SnapshotLandblock(uint landblockId)
{
uint canonical = CanonicalLandblock(landblockId);
return _spawns.Values
.Where(spawn => spawn.Position is { } position
&& CanonicalLandblock(position.LandblockId) == canonical
&& spawn.SetupTableId is not null)
.ToArray();
}
public bool Contains(uint guid, ushort generation) =>
_spawns.TryGetValue(guid, out WorldSession.EntitySpawn spawn)
&& spawn.InstanceSequence == generation;
public bool TryGetExact(
uint guid,
ushort generation,
out WorldSession.EntitySpawn snapshot)
{
if (_spawns.TryGetValue(guid, out snapshot)
&& snapshot.InstanceSequence == generation)
{
return true;
}
snapshot = default;
return false;
}
public void Clear() => _spawns.Clear();
private static uint CanonicalLandblock(uint cellId) =>
(cellId & 0xFFFF0000u) | 0xFFFFu;
}