feat(runtime): own deferred set-position residence
This commit is contained in:
parent
e84a388e6f
commit
4c02ac4259
18 changed files with 5557 additions and 34 deletions
|
|
@ -33,6 +33,8 @@ public sealed class ShadowObjectRegistry
|
|||
_collisionWorld.Current.ShadowEntityCells; // for deregistration
|
||||
private HashSet<uint> _suspendedEntities =>
|
||||
_collisionWorld.Current.SuspendedShadowEntities;
|
||||
private Dictionary<uint, List<uint>> _suspendedEntityCells =>
|
||||
_collisionWorld.Current.SuspendedShadowEntityCells;
|
||||
// Rows withdrawn because a touched landblock streamed out. The owner may
|
||||
// be seeded in an adjacent still-resident landblock, so its remaining rows
|
||||
// cannot by themselves tell RefloodLandblock that this prefix needs repair.
|
||||
|
|
@ -691,6 +693,218 @@ public sealed class ShadowObjectRegistry
|
|||
reg.State, reg.Flags, seedCellId, reg.IsStatic);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Installs the shadow-list suffix of one canonical retail
|
||||
/// <c>CPhysicsObj::SetPositionInternal</c> commit. The transition has
|
||||
/// already selected whether retail recalculates, replaces, or preserves
|
||||
/// the owner's cross-cell list; this method consumes that decision
|
||||
/// without running a second placement/flood oracle.
|
||||
/// </summary>
|
||||
internal void CommitSetPosition(
|
||||
uint entityId,
|
||||
Vector3 worldPosition,
|
||||
Quaternion worldRotation,
|
||||
uint seedCellId,
|
||||
float worldOffsetX,
|
||||
float worldOffsetY,
|
||||
PhysicsShadowCommitAction action,
|
||||
System.Collections.Immutable.ImmutableArray<uint> crossCellIds)
|
||||
{
|
||||
if (!_entityReg.TryGetValue(
|
||||
entityId,
|
||||
out RegistrationRecord? registration))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (action)
|
||||
{
|
||||
case PhysicsShadowCommitAction.None:
|
||||
RefreshPositionRows(
|
||||
entityId,
|
||||
registration,
|
||||
worldPosition,
|
||||
worldRotation,
|
||||
seedCellId);
|
||||
return;
|
||||
case PhysicsShadowCommitAction.Recalculate:
|
||||
UpdatePosition(
|
||||
entityId,
|
||||
worldPosition,
|
||||
worldRotation,
|
||||
worldOffsetX,
|
||||
worldOffsetY,
|
||||
landblockId: seedCellId & 0xFFFF0000u,
|
||||
seedCellId);
|
||||
return;
|
||||
case PhysicsShadowCommitAction.Replace:
|
||||
if (crossCellIds.IsDefaultOrEmpty)
|
||||
{
|
||||
RefreshPositionRows(
|
||||
entityId,
|
||||
registration,
|
||||
worldPosition,
|
||||
worldRotation,
|
||||
seedCellId);
|
||||
return;
|
||||
}
|
||||
ReplacePositionRows(
|
||||
entityId,
|
||||
registration,
|
||||
worldPosition,
|
||||
worldRotation,
|
||||
seedCellId,
|
||||
crossCellIds);
|
||||
return;
|
||||
case PhysicsShadowCommitAction.Preserve:
|
||||
RefreshPositionRows(
|
||||
entityId,
|
||||
registration,
|
||||
worldPosition,
|
||||
worldRotation,
|
||||
seedCellId);
|
||||
return;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(action));
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshPositionRows(
|
||||
uint entityId,
|
||||
RegistrationRecord registration,
|
||||
Vector3 worldPosition,
|
||||
Quaternion worldRotation,
|
||||
uint seedCellId)
|
||||
{
|
||||
if ((_entityToCells.TryGetValue(
|
||||
entityId,
|
||||
out List<uint>? retainedCells)
|
||||
|| _suspendedEntityCells.TryGetValue(
|
||||
entityId,
|
||||
out retainedCells))
|
||||
&& retainedCells.Count != 0)
|
||||
{
|
||||
ReplacePositionRows(
|
||||
entityId,
|
||||
registration,
|
||||
worldPosition,
|
||||
worldRotation,
|
||||
seedCellId,
|
||||
retainedCells);
|
||||
return;
|
||||
}
|
||||
|
||||
_entityReg[entityId] = registration with
|
||||
{
|
||||
SeedCellId = seedCellId,
|
||||
EntityWorldPos = worldPosition,
|
||||
EntityWorldRot = worldRotation,
|
||||
};
|
||||
BumpOwnerVersion(entityId);
|
||||
}
|
||||
|
||||
private void ReplacePositionRows(
|
||||
uint entityId,
|
||||
RegistrationRecord registration,
|
||||
Vector3 worldPosition,
|
||||
Quaternion worldRotation,
|
||||
uint seedCellId,
|
||||
IReadOnlyList<uint> cellIds)
|
||||
{
|
||||
if (_entityToCells.TryGetValue(
|
||||
entityId,
|
||||
out List<uint>? previousCells))
|
||||
{
|
||||
for (int index = 0; index < previousCells.Count; index++)
|
||||
{
|
||||
if (_cells.TryGetValue(
|
||||
previousCells[index],
|
||||
out List<ShadowEntry>? entries))
|
||||
{
|
||||
RemoveOwnerRows(entries, entityId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_suspendedEntities.Remove(entityId);
|
||||
_suspendedEntityCells.Remove(entityId);
|
||||
_entityReg[entityId] = registration with
|
||||
{
|
||||
SeedCellId = seedCellId,
|
||||
EntityWorldPos = worldPosition,
|
||||
EntityWorldRot = worldRotation,
|
||||
};
|
||||
|
||||
var exactCells = new List<uint>(cellIds.Count);
|
||||
for (int index = 0; index < cellIds.Count; index++)
|
||||
{
|
||||
uint cellId = cellIds[index];
|
||||
if (cellId == 0u || exactCells.Contains(cellId))
|
||||
continue;
|
||||
exactCells.Add(cellId);
|
||||
}
|
||||
|
||||
if (registration.IsMultiPart
|
||||
&& _entityShapes.TryGetValue(
|
||||
entityId,
|
||||
out IReadOnlyList<ShadowShape>? shapes))
|
||||
{
|
||||
foreach (ShadowShape shape in shapes)
|
||||
{
|
||||
Vector3 partWorldPosition = worldPosition
|
||||
+ Vector3.Transform(shape.LocalPosition, worldRotation);
|
||||
Quaternion partWorldRotation = worldRotation
|
||||
* shape.LocalRotation;
|
||||
var entry = new ShadowEntry(
|
||||
entityId,
|
||||
shape.GfxObjId,
|
||||
partWorldPosition,
|
||||
partWorldRotation,
|
||||
shape.Radius,
|
||||
shape.CollisionType,
|
||||
shape.CylHeight,
|
||||
shape.Scale,
|
||||
registration.State,
|
||||
registration.Flags,
|
||||
shape.LocalPosition,
|
||||
shape.LocalRotation);
|
||||
for (int index = 0; index < exactCells.Count; index++)
|
||||
AddEntryToCell(entry, exactCells[index]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var entry = new ShadowEntry(
|
||||
entityId,
|
||||
registration.GfxObjId,
|
||||
worldPosition,
|
||||
worldRotation,
|
||||
registration.Radius,
|
||||
registration.CollisionType,
|
||||
registration.CylHeight,
|
||||
registration.Scale,
|
||||
registration.State,
|
||||
registration.Flags);
|
||||
for (int index = 0; index < exactCells.Count; index++)
|
||||
AddEntryToCell(entry, exactCells[index]);
|
||||
}
|
||||
|
||||
if (exactCells.Count == 0)
|
||||
_entityToCells.Remove(entityId);
|
||||
else
|
||||
_entityToCells[entityId] = exactCells;
|
||||
if (_withdrawnPrefixesByOwner.TryGetValue(
|
||||
entityId,
|
||||
out HashSet<uint>? withdrawn))
|
||||
{
|
||||
for (int index = 0; index < exactCells.Count; index++)
|
||||
withdrawn.Remove(exactCells[index] & 0xFFFF0000u);
|
||||
if (withdrawn.Count == 0)
|
||||
_withdrawnPrefixesByOwner.Remove(entityId);
|
||||
}
|
||||
BumpOwnerVersion(entityId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes an entity from every cell collision list while retaining the
|
||||
/// exact registration and shape payload needed to restore it later.
|
||||
|
|
@ -706,6 +920,7 @@ public sealed class ShadowObjectRegistry
|
|||
|
||||
if (_entityToCells.TryGetValue(entityId, out var cellIds))
|
||||
{
|
||||
_suspendedEntityCells[entityId] = new List<uint>(cellIds);
|
||||
foreach (uint cellId in cellIds)
|
||||
{
|
||||
if (_cells.TryGetValue(cellId, out var list))
|
||||
|
|
@ -924,7 +1139,8 @@ public sealed class ShadowObjectRegistry
|
|||
bool existed = _entityReg.ContainsKey(entityId)
|
||||
|| _entityToCells.ContainsKey(entityId)
|
||||
|| _entityShapes.ContainsKey(entityId)
|
||||
|| _suspendedEntities.Contains(entityId);
|
||||
|| _suspendedEntities.Contains(entityId)
|
||||
|| _suspendedEntityCells.ContainsKey(entityId);
|
||||
if (_entityToCells.TryGetValue(entityId, out var cellIds))
|
||||
{
|
||||
foreach (var cellId in cellIds)
|
||||
|
|
@ -937,6 +1153,7 @@ public sealed class ShadowObjectRegistry
|
|||
_entityShapes.Remove(entityId);
|
||||
_entityReg.Remove(entityId);
|
||||
_suspendedEntities.Remove(entityId);
|
||||
_suspendedEntityCells.Remove(entityId);
|
||||
_withdrawnPrefixesByOwner.Remove(entityId);
|
||||
if (existed && publishMutation)
|
||||
{
|
||||
|
|
@ -1065,6 +1282,7 @@ public sealed class ShadowObjectRegistry
|
|||
_entityShapes.Remove(eid);
|
||||
_entityReg.Remove(eid);
|
||||
_suspendedEntities.Remove(eid);
|
||||
_suspendedEntityCells.Remove(eid);
|
||||
_withdrawnPrefixesByOwner.Remove(eid);
|
||||
}
|
||||
}
|
||||
|
|
@ -1367,6 +1585,9 @@ public sealed class ShadowObjectRegistry
|
|||
return false;
|
||||
}
|
||||
_entityToCells.TryGetValue(entityId, out List<uint>? cells);
|
||||
_suspendedEntityCells.TryGetValue(
|
||||
entityId,
|
||||
out List<uint>? suspendedCells);
|
||||
_entityShapes.TryGetValue(
|
||||
entityId,
|
||||
out IReadOnlyList<ShadowShape>? shapes);
|
||||
|
|
@ -1394,6 +1615,7 @@ public sealed class ShadowObjectRegistry
|
|||
cells is null ? null : new List<uint>(cells),
|
||||
rows,
|
||||
_suspendedEntities.Contains(entityId),
|
||||
suspendedCells is null ? null : new List<uint>(suspendedCells),
|
||||
withdrawn is null ? null : new HashSet<uint>(withdrawn));
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1405,6 +1627,8 @@ public sealed class ShadowObjectRegistry
|
|||
_entityShapes[state.EntityId] = state.Shapes;
|
||||
if (state.Suspended)
|
||||
_suspendedEntities.Add(state.EntityId);
|
||||
if (state.SuspendedCellIds is not null)
|
||||
_suspendedEntityCells[state.EntityId] = state.SuspendedCellIds;
|
||||
if (state.WithdrawnPrefixes is not null)
|
||||
{
|
||||
_withdrawnPrefixesByOwner[state.EntityId] = state.WithdrawnPrefixes;
|
||||
|
|
@ -1614,6 +1838,7 @@ public sealed class ShadowObjectRegistry
|
|||
List<uint>? CellIds,
|
||||
IReadOnlyList<PreparedShadowCellRows> Rows,
|
||||
bool Suspended,
|
||||
List<uint>? SuspendedCellIds,
|
||||
HashSet<uint>? WithdrawnPrefixes);
|
||||
|
||||
internal sealed record PreparedShadowCellRows(
|
||||
|
|
@ -1629,6 +1854,7 @@ public sealed class ShadowObjectRegistry
|
|||
_cells.Clear();
|
||||
_entityToCells.Clear();
|
||||
_suspendedEntities.Clear();
|
||||
_suspendedEntityCells.Clear();
|
||||
_withdrawnPrefixesByOwner.Clear();
|
||||
_entityShapes.Clear();
|
||||
_entityReg.Clear();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue