using AcDream.Core.Net;
using AcDream.Core.Net.Messages;
using AcDream.Core.Physics;
using AcDream.Core.Items;
using AcDream.Runtime.Entities;
namespace AcDream.Runtime.Tests.Entities;
public sealed class ParentAttachmentStateTests
{
[Fact]
public void ParentEventBeforeChildCreateResolvesToCanonicalAttachment()
{
const uint parentGuid = 0x70000100u;
const uint childGuid = 0x70000101u;
var inbound = new InboundPhysicsStateController();
var relations = new ParentAttachmentState();
inbound.AcceptCreate(Spawn(parentGuid, instance: 9, positionSequence: 1));
relations.Enqueue(new ParentEvent.Parsed(parentGuid, childGuid, 1, 2, 9, 5));
Resolve(relations, inbound, childGuid);
Assert.False(relations.TryGetProjection(childGuid, out _));
inbound.AcceptCreate(Spawn(childGuid, instance: 3, positionSequence: 4));
Resolve(relations, inbound, childGuid);
Assert.True(relations.TryGetProjection(childGuid, out ParentAttachmentRelation projection));
Assert.Equal(parentGuid, projection.ParentGuid);
Assert.True(inbound.TryGetSnapshot(childGuid, out WorldSession.EntitySpawn child));
Assert.NotNull(child.Position);
Assert.Null(child.ParentGuid);
Assert.Equal((ushort)5, child.PositionSequence);
Assert.True(Commit(relations, inbound, projection, out child));
Assert.Null(child.Position);
Assert.Equal(parentGuid, child.ParentGuid);
}
///
/// #319 A1 (architecture review, 2026-08-05). Dual-parent-class matrix:
/// is a PURE,
/// side-effect-free precondition - it must refuse a mismatch without
/// mutating ANY state, so a caller can check it before either half of a
/// commit runs (never a throw; the original shape threw from inside
/// , reached only
/// after the canonical commit had already landed, tearing the
/// transaction).
///
[Theory]
[InlineData(0x50000210u, (ushort)5)]
[InlineData(0x70000211u, (ushort)0)]
public void CanCommitIncarnation_MismatchedIncarnation_RefusesWithoutMutatingState(
uint parentGuid,
ushort liveParentIncarnation)
{
const uint childGuid = 0x70000212u;
var relations = new ParentAttachmentState();
var wrongRelation = new ParentAttachmentRelation(
parentGuid,
childGuid,
ParentLocation: 0,
PlacementId: 0,
ParentInstanceSequence: (ushort)(liveParentIncarnation + 1),
ChildPositionSequence: 1);
relations.AcceptCreateObjectRelation(wrongRelation);
Assert.False(relations.CanCommitIncarnation(
wrongRelation,
guid => guid == parentGuid ? liveParentIncarnation : (ushort?)null));
// Purely a read - the relation is untouched, still staged, no
// committed parent exists.
Assert.True(relations.TryGetStagedProjection(childGuid, out ParentAttachmentRelation staged));
Assert.Equal(wrongRelation, staged);
Assert.False(relations.TryGetCommittedParent(childGuid, out _, out _));
// A NULL resolver (parent not currently addressable) must not
// refuse - the tripwire only fires "whenever the parent is
// active" (contract F1).
Assert.True(relations.CanCommitIncarnation(wrongRelation, _ => null));
}
///
/// #319 A1. itself
/// must be non-tearing for ANY caller (not just the two production call
/// sites that also pre-check): a mismatch returns false and mutates
/// nothing, never throws.
///
[Theory]
[InlineData(0x50000213u, (ushort)5)]
[InlineData(0x70000214u, (ushort)0)]
public void CommitProjection_MismatchedIncarnation_ReturnsFalseWithoutThrowingOrMutating(
uint parentGuid,
ushort liveParentIncarnation)
{
const uint childGuid = 0x70000215u;
var relations = new ParentAttachmentState();
var wrongRelation = new ParentAttachmentRelation(
parentGuid,
childGuid,
ParentLocation: 0,
PlacementId: 0,
ParentInstanceSequence: (ushort)(liveParentIncarnation + 1),
ChildPositionSequence: 1);
relations.AcceptCreateObjectRelation(wrongRelation);
Assert.False(relations.CommitProjection(
wrongRelation,
guid => guid == parentGuid ? liveParentIncarnation : (ushort?)null));
Assert.False(relations.TryGetCommittedParent(childGuid, out _, out _));
Assert.True(relations.TryGetStagedProjection(childGuid, out ParentAttachmentRelation staged));
Assert.Equal(wrongRelation, staged);
// A matching resolver commits normally.
Assert.True(relations.CommitProjection(
wrongRelation,
guid => guid == parentGuid
? (ushort)(liveParentIncarnation + 1)
: (ushort?)null));
Assert.True(relations.TryGetCommittedParent(
childGuid,
out uint committedParent,
out ushort committedInstance));
Assert.Equal(parentGuid, committedParent);
Assert.Equal((ushort)(liveParentIncarnation + 1), committedInstance);
}
///
/// #319 contract §6 test 9 (ledger convergence) - BLOCKS per the
/// architecture review, since a stranded/accumulating relation is
/// precisely a ledger-convergence defect. Dual-parent-class matrix.
/// Removing the CHILD must zero every one of
/// 's four tables
/// (staged/recovery/committed/unresolved), not just the committed
/// entry.
///
[Theory]
[InlineData(0x50000900u, (ushort)3)]
[InlineData(0x70000901u, (ushort)0)]
public void LedgerConvergence_ChildRemoval_ZeroesEveryTable(
uint parentGuid,
ushort parentIncarnation)
{
const uint childGuid = 0x70000902u;
var relations = new ParentAttachmentState();
var relation = new ParentAttachmentRelation(
parentGuid,
childGuid,
ParentLocation: 0,
PlacementId: 0,
parentIncarnation,
ChildPositionSequence: 1);
relations.AcceptCreateObjectRelation(relation);
Assert.True(relations.CommitProjection(relation));
// MarkProjected(Recovery) simulates a completed realize/render
// pass, which clears the retry-candidate bookkeeping - the
// permanent committed record in _lastAcceptedByChild is untouched
// by it, which is the table this test cares about.
relations.MarkProjected(relation, ParentProjectionCandidateKind.Recovery);
Assert.Equal(1, relations.CommittedRelationCount);
Assert.Equal(0, relations.RecoveryRelationCount);
Assert.NotEmpty(relations.ChildrenAttachedToParent(parentGuid, parentIncarnation));
relations.RemoveChild(childGuid);
Assert.Equal(0, relations.CommittedRelationCount);
Assert.Equal(0, relations.RecoveryRelationCount);
Assert.Equal(0, relations.StagedRelationCount);
Assert.Equal(0, relations.UnresolvedRelationCount);
Assert.Empty(relations.ChildrenAttachedToParent(parentGuid, parentIncarnation));
Assert.False(relations.TryGetCommittedParent(childGuid, out _, out _));
Assert.False(relations.HasCommittedParent(childGuid));
}
///
/// #319 contract §6 test 9, the PARENT-removal companion. Dual-parent-
/// class matrix. Removing the PARENT must converge the committed
/// child's entry via 's
/// parent-reference sweep.
///
[Theory]
[InlineData(0x50000920u, (ushort)2)]
[InlineData(0x70000921u, (ushort)0)]
public void LedgerConvergence_ParentRemoval_ZeroesEveryTable(
uint parentGuid,
ushort parentIncarnation)
{
const uint childGuid = 0x70000922u;
var relations = new ParentAttachmentState();
var relation = new ParentAttachmentRelation(
parentGuid,
childGuid,
ParentLocation: 0,
PlacementId: 0,
parentIncarnation,
ChildPositionSequence: 1);
relations.AcceptCreateObjectRelation(relation);
Assert.True(relations.CommitProjection(relation));
relations.MarkProjected(relation, ParentProjectionCandidateKind.Recovery);
relations.RemoveObject(parentGuid);
Assert.Equal(0, relations.CommittedRelationCount);
Assert.Equal(0, relations.RecoveryRelationCount);
Assert.Equal(0, relations.StagedRelationCount);
Assert.Empty(relations.ChildrenAttachedToParent(parentGuid, parentIncarnation));
Assert.False(relations.HasCommittedParent(childGuid));
}
///
/// #319 contract §6 test 9, full-teardown companion. Dual-parent-class
/// matrix. must converge every
/// table to zero with a player-parented committed child AND a still-
/// unresolved ParentEvent present simultaneously - the mid-session
/// accumulation shape the architecture review's A5 finding was
/// concerned with (now moot for the deleted deferred-CreateObject
/// queue, but the pre-existing ParentEvent `_unresolvedByChild` queue
/// this fix left untouched still needs its own convergence proof).
///
[Theory]
[InlineData(0x50000930u, (ushort)9)]
[InlineData(0x70000931u, (ushort)0)]
public void LedgerConvergence_Teardown_ZeroesEveryTableWithMixedPendingState(
uint parentGuid,
ushort parentIncarnation)
{
const uint committedChildGuid = 0x70000932u;
const uint unresolvedChildGuid = 0x70000933u;
var relations = new ParentAttachmentState();
var relation = new ParentAttachmentRelation(
parentGuid,
committedChildGuid,
ParentLocation: 0,
PlacementId: 0,
parentIncarnation,
ChildPositionSequence: 1);
relations.AcceptCreateObjectRelation(relation);
Assert.True(relations.CommitProjection(relation));
relations.MarkProjected(relation, ParentProjectionCandidateKind.Recovery);
relations.Enqueue(new ParentEvent.Parsed(
parentGuid, unresolvedChildGuid, 0, 0, parentIncarnation, 1));
Assert.True(relations.CommittedRelationCount > 0);
Assert.True(relations.UnresolvedRelationCount > 0);
relations.Clear();
Assert.Equal(0, relations.CommittedRelationCount);
Assert.Equal(0, relations.RecoveryRelationCount);
Assert.Equal(0, relations.StagedRelationCount);
Assert.Equal(0, relations.UnresolvedRelationCount);
Assert.False(relations.HasCommittedParent(committedChildGuid));
Assert.Empty(relations.ChildrenAttachedToParent(parentGuid, parentIncarnation));
}
[Fact]
public void MultipleQueuedRelationsRemainOrderedAndNewestAcceptedWins()
{
const uint parentGuid = 0x70000110u;
const uint childGuid = 0x70000111u;
var gate = new PhysicsTimestampGate();
gate.SeedForCreateObject(4, 0, 0, 0, 0, 0, 0, 0, 3);
var relations = new ParentAttachmentState();
relations.Enqueue(new ParentEvent.Parsed(parentGuid, childGuid, 1, 2, 9, 5));
relations.Enqueue(new ParentEvent.Parsed(parentGuid, childGuid, 1, 3, 9, 6));
relations.Resolve(
childGuid,
_ => true,
guid => guid == parentGuid ? (ushort)9 : null,
update => gate.TryAcceptPositionChannelEvent(3, update.ChildPositionSequence));
Assert.True(relations.TryGetProjection(childGuid, out ParentAttachmentRelation projection));
Assert.Equal((ushort)5, projection.ChildPositionSequence);
Assert.Equal((uint)2, projection.PlacementId);
Assert.True(relations.CommitProjection(projection));
relations.MarkProjected(
projection,
ParentProjectionCandidateKind.Recovery);
relations.Resolve(
childGuid,
_ => true,
guid => guid == parentGuid ? (ushort)9 : null,
update => gate.TryAcceptPositionChannelEvent(3, update.ChildPositionSequence));
Assert.True(relations.TryGetStagedProjection(childGuid, out projection));
Assert.Equal((ushort)6, projection.ChildPositionSequence);
Assert.Equal((uint)3, projection.PlacementId);
Assert.True(relations.CommitProjection(projection));
relations.MarkProjected(
projection,
ParentProjectionCandidateKind.Recovery);
Assert.True(relations.RestoreLastAccepted(childGuid));
Assert.True(relations.TryGetProjection(childGuid, out projection));
Assert.Equal((ushort)6, projection.ChildPositionSequence);
}
[Fact]
public void RejectedStaleRelationCannotReplaceAcceptedCreateRelation()
{
const uint childGuid = 0x70000121u;
var gate = new PhysicsTimestampGate();
gate.SeedForCreateObject(10, 0, 0, 0, 0, 0, 0, 0, 3);
var relations = new ParentAttachmentState();
relations.AcceptCreateObjectRelation(new ParentAttachmentRelation(
0x70000120u, childGuid, 1, 2, 0, 10));
relations.Enqueue(new ParentEvent.Parsed(
0x70000122u, childGuid, 1, 4, 8, 9));
relations.Resolve(
childGuid,
_ => true,
guid => guid == 0x70000122u ? (ushort)8 : null,
update => gate.TryAcceptPositionChannelEvent(3, update.ChildPositionSequence));
Assert.True(relations.TryGetProjection(childGuid, out ParentAttachmentRelation projection));
Assert.Equal(0x70000120u, projection.ParentGuid);
Assert.Equal((ushort)10, projection.ChildPositionSequence);
}
[Fact]
public void FutureParentGenerationDoesNotBlockCurrentRelationBehindIt()
{
const uint childGuid = 0x70000141u;
const uint parentGuid = 0x70000140u;
var gate = new PhysicsTimestampGate();
gate.SeedForCreateObject(4, 0, 0, 0, 0, 0, 0, 0, 3);
var relations = new ParentAttachmentState();
relations.Enqueue(new ParentEvent.Parsed(
parentGuid, childGuid, 1, 2, 10, 6));
relations.Enqueue(new ParentEvent.Parsed(
parentGuid, childGuid, 1, 3, 9, 5));
relations.Resolve(
childGuid,
_ => true,
guid => guid == parentGuid ? (ushort)9 : null,
update => gate.TryAcceptPositionChannelEvent(3, update.ChildPositionSequence));
Assert.True(relations.TryGetProjection(childGuid, out ParentAttachmentRelation projection));
Assert.Equal((ushort)5, projection.ChildPositionSequence);
Assert.Equal((uint)3, projection.PlacementId);
Assert.Contains(childGuid, relations.ChildrenWaitingForParent(parentGuid));
}
[Fact]
public void FutureRelationSurvivesAtomicParentGenerationReplacement()
{
const uint parentGuid = 0x70000150u;
const uint childGuid = 0x70000151u;
var inbound = new InboundPhysicsStateController();
var relations = new ParentAttachmentState();
var objects = new ClientObjectTable();
inbound.AcceptCreate(Spawn(parentGuid, instance: 9, positionSequence: 1));
inbound.AcceptCreate(Spawn(childGuid, instance: 3, positionSequence: 4));
relations.AcceptCreateObjectRelation(new ParentAttachmentRelation(
parentGuid, childGuid, 1, 2, 0, 4));
relations.Enqueue(new ParentEvent.Parsed(
parentGuid, childGuid, 1, 3, 10, 5));
Resolve(relations, inbound, childGuid);
objects.Ingest(MinimalWeenie(parentGuid, "generation 9"));
objects.ObjectRemovalClassified += removal =>
{
if (removal.Reason == ClientObjectRemovalReason.GenerationReplacement)
relations.EndGeneration(removal.Object.ObjectId, removal.Generation);
};
inbound.AcceptCreate(Spawn(parentGuid, instance: 10, positionSequence: 1));
objects.ReplaceGeneration(MinimalWeenie(parentGuid, "generation 10"), 10);
Resolve(relations, inbound, childGuid);
Assert.True(relations.TryGetProjection(childGuid, out ParentAttachmentRelation projection));
Assert.Equal(parentGuid, projection.ParentGuid);
Assert.Equal((ushort)10, projection.ParentInstanceSequence);
Assert.Equal((ushort)5, projection.ChildPositionSequence);
Assert.True(Commit(relations, inbound, projection, out _));
Assert.True(inbound.TryGetSnapshot(childGuid, out WorldSession.EntitySpawn child));
Assert.Equal(parentGuid, child.ParentGuid);
}
[Fact]
public void FutureParentRelationSurvivesChildGenerationReplacement()
{
const uint parentGuid = 0x70000180u;
const uint childGuid = 0x70000181u;
var relations = new ParentAttachmentState();
relations.Enqueue(new ParentEvent.Parsed(
parentGuid, childGuid, 1, 3, 10, 5));
// Parent generation 9 exists, so retail queues this blob against the
// future parent rather than against the already-live child.
relations.Resolve(
childGuid,
_ => true,
guid => guid == parentGuid ? (ushort)9 : null,
_ => throw new InvalidOperationException("future parent must not apply"));
relations.EndGeneration(childGuid, replacementGeneration: 4);
var replacementGate = new PhysicsTimestampGate();
replacementGate.SeedForCreateObject(4, 0, 0, 0, 0, 0, 0, 0, 4);
relations.Resolve(
childGuid,
_ => true,
guid => guid == parentGuid ? (ushort)10 : null,
update => replacementGate.TryAcceptPositionChannelEvent(
4, update.ChildPositionSequence));
Assert.True(relations.TryGetProjection(childGuid, out ParentAttachmentRelation projection));
Assert.Equal((ushort)10, projection.ParentInstanceSequence);
Assert.Equal((ushort)5, projection.ChildPositionSequence);
}
[Fact]
public void RemoveAndClearDiscardPendingAndRollbackState()
{
const uint parentGuid = 0x70000130u;
const uint childGuid = 0x70000131u;
var relations = new ParentAttachmentState();
relations.AcceptCreateObjectRelation(new ParentAttachmentRelation(
parentGuid, childGuid, 1, 2, 0, 4));
relations.Enqueue(new ParentEvent.Parsed(parentGuid, childGuid, 1, 3, 9, 5));
relations.RemoveObject(parentGuid);
Assert.False(relations.TryGetProjection(childGuid, out _));
Assert.False(relations.RestoreLastAccepted(childGuid));
Assert.Empty(relations.ChildrenWaitingForParent(parentGuid));
relations.AcceptCreateObjectRelation(new ParentAttachmentRelation(
parentGuid, childGuid, 1, 2, 0, 4));
relations.Clear();
Assert.False(relations.TryGetProjection(childGuid, out _));
Assert.False(relations.RestoreLastAccepted(childGuid));
}
[Fact]
public void UnparentProjectionRetainsFresherPendingParentEvent()
{
const uint parentGuid = 0x70000160u;
const uint childGuid = 0x70000161u;
var gate = new PhysicsTimestampGate();
gate.SeedForCreateObject(4, 0, 0, 0, 0, 0, 0, 0, 3);
var relations = new ParentAttachmentState();
relations.AcceptCreateObjectRelation(new ParentAttachmentRelation(
0x70000162u, childGuid, 1, 2, 0, 4));
relations.Enqueue(new ParentEvent.Parsed(
parentGuid, childGuid, 1, 3, 9, 5));
relations.EndChildProjection(childGuid);
Assert.False(relations.TryGetProjection(childGuid, out _));
relations.Resolve(
childGuid,
_ => true,
guid => guid == parentGuid ? (ushort)9 : null,
update => gate.TryAcceptPositionChannelEvent(3, update.ChildPositionSequence));
Assert.True(relations.TryGetProjection(childGuid, out ParentAttachmentRelation projection));
Assert.Equal(parentGuid, projection.ParentGuid);
Assert.Equal((ushort)5, projection.ChildPositionSequence);
}
[Fact]
public void ExactParentDeleteRetainsOnlyStrictlyFutureGenerationRelation()
{
const uint parentGuid = 0x70000170u;
const uint childGuid = 0x70000171u;
var gate = new PhysicsTimestampGate();
gate.SeedForCreateObject(4, 0, 0, 0, 0, 0, 0, 0, 3);
var relations = new ParentAttachmentState();
relations.Enqueue(new ParentEvent.Parsed(
parentGuid, childGuid, 1, 2, 9, 5));
relations.Enqueue(new ParentEvent.Parsed(
parentGuid, childGuid, 1, 3, 10, 6));
relations.DeleteGeneration(parentGuid, deletedGeneration: 9);
relations.Resolve(
childGuid,
_ => true,
guid => guid == parentGuid ? (ushort)10 : null,
update => gate.TryAcceptPositionChannelEvent(3, update.ChildPositionSequence));
Assert.True(relations.TryGetProjection(childGuid, out ParentAttachmentRelation projection));
Assert.Equal((ushort)10, projection.ParentInstanceSequence);
Assert.Equal((ushort)6, projection.ChildPositionSequence);
}
[Fact]
public void CommittedChildrenAppendInCommitOrder()
{
const uint parentGuid = 0x70000200u;
var relations = new ParentAttachmentState();
Commit(relations, Relation(parentGuid, 0x70000201u, parentInstance: 9));
Commit(relations, Relation(parentGuid, 0x70000202u, parentInstance: 9));
IReadOnlyList committed = relations.ChildrenAttachedToParent(
parentGuid,
9);
Commit(relations, Relation(parentGuid, 0x70000203u, parentInstance: 9));
Assert.Same(
committed,
relations.ChildrenAttachedToParent(parentGuid, 9));
Assert.Equal(
[0x70000201u, 0x70000202u, 0x70000203u],
committed);
}
[Fact]
public void EndingChildProjectionSwapRemovesCommittedChild()
{
const uint parentGuid = 0x70000210u;
var relations = new ParentAttachmentState();
Commit(relations, Relation(parentGuid, 0x70000211u, parentInstance: 9));
Commit(relations, Relation(parentGuid, 0x70000212u, parentInstance: 9));
Commit(relations, Relation(parentGuid, 0x70000213u, parentInstance: 9));
relations.EndChildProjection(0x70000211u);
Assert.Equal(
[0x70000213u, 0x70000212u],
relations.ChildrenAttachedToParent(parentGuid, 9));
}
[Fact]
public void ReparentSwapRemovesOldEntryAndAppendsNewEntry()
{
const uint oldParentGuid = 0x70000220u;
const uint newParentGuid = 0x70000221u;
const uint childGuid = 0x70000222u;
var relations = new ParentAttachmentState();
Commit(relations, Relation(oldParentGuid, childGuid, parentInstance: 9));
Commit(relations, Relation(oldParentGuid, 0x70000223u, parentInstance: 9));
Commit(relations, Relation(oldParentGuid, 0x70000224u, parentInstance: 9));
Commit(relations, Relation(newParentGuid, 0x70000225u, parentInstance: 4));
Commit(relations, Relation(newParentGuid, childGuid, parentInstance: 4));
Assert.Equal(
[0x70000224u, 0x70000223u],
relations.ChildrenAttachedToParent(oldParentGuid, 9));
Assert.Equal(
[0x70000225u, childGuid],
relations.ChildrenAttachedToParent(newParentGuid, 4));
}
[Fact]
public void CommittedChildrenAreIsolatedByParentGuidAndIncarnation()
{
const uint parentGuid = 0x70000230u;
const uint otherParentGuid = 0x70000231u;
var relations = new ParentAttachmentState();
Commit(relations, Relation(parentGuid, 0x70000232u, parentInstance: 9));
Commit(relations, Relation(parentGuid, 0x70000233u, parentInstance: 10));
Commit(relations, Relation(otherParentGuid, 0x70000234u, parentInstance: 9));
Assert.Equal(
[0x70000232u],
relations.ChildrenAttachedToParent(parentGuid, 9));
Assert.Equal(
[0x70000233u],
relations.ChildrenAttachedToParent(parentGuid, 10));
Assert.Equal(
[0x70000234u],
relations.ChildrenAttachedToParent(otherParentGuid, 9));
Assert.Empty(relations.ChildrenAttachedToParent(otherParentGuid, 10));
}
private static void Resolve(
ParentAttachmentState relations,
InboundPhysicsStateController inbound,
uint childGuid) =>
relations.Resolve(
childGuid,
guid => inbound.TryGetSnapshot(guid, out _),
guid => inbound.TryGetSnapshot(guid, out WorldSession.EntitySpawn spawn)
? spawn.InstanceSequence
: null,
update => inbound.TryApplyParent(update, out _));
private static bool Commit(
ParentAttachmentState relations,
InboundPhysicsStateController inbound,
ParentAttachmentRelation relation,
out WorldSession.EntitySpawn accepted) =>
inbound.TryCommitParent(
relation.ChildGuid,
relation.ParentGuid,
relation.ParentLocation,
relation.PlacementId,
relation.ChildPositionSequence,
out accepted)
&& relations.CommitProjection(relation);
private static void Commit(
ParentAttachmentState relations,
ParentAttachmentRelation relation)
{
relations.AcceptCreateObjectRelation(relation);
Assert.True(relations.CommitProjection(relation));
}
private static ParentAttachmentRelation Relation(
uint parentGuid,
uint childGuid,
ushort parentInstance) =>
new(parentGuid, childGuid, 1, 2, parentInstance, 1);
private static WorldSession.EntitySpawn Spawn(
uint guid,
ushort instance,
ushort positionSequence)
{
var position = new CreateObject.ServerPosition(
0x0101FFFFu, 10f, 10f, 5f, 1f, 0f, 0f, 0f);
var timestamps = new PhysicsTimestamps(
positionSequence, 1, 1, 1, 0, 1, 0, 1, instance);
var physics = new PhysicsSpawnData(
RawState: 0x408u,
Position: position,
Movement: null,
AnimationFrame: null,
SetupTableId: 0x02000001u,
MotionTableId: 0x09000001u,
SoundTableId: null,
PhysicsScriptTableId: null,
Parent: null,
Children: null,
Scale: null,
Friction: null,
Elasticity: null,
Translucency: null,
Velocity: null,
Acceleration: null,
AngularVelocity: null,
DefaultScriptType: null,
DefaultScriptIntensity: null,
Timestamps: timestamps);
return new WorldSession.EntitySpawn(
guid,
position,
0x02000001u,
Array.Empty(),
Array.Empty(),
Array.Empty(),
null,
null,
"fixture",
null,
null,
0x09000001u,
PhysicsState: 0x408u,
InstanceSequence: instance,
MovementSequence: 1,
ServerControlSequence: 1,
PositionSequence: positionSequence,
Physics: physics);
}
private static WeenieData MinimalWeenie(uint guid, string name) => new(
Guid: guid,
Name: name,
Type: null,
WeenieClassId: 0,
IconId: 0,
IconOverlayId: 0,
IconUnderlayId: 0,
Effects: 0,
Value: null,
StackSize: null,
StackSizeMax: null,
Burden: null,
ContainerId: null,
WielderId: null,
ValidLocations: null,
CurrentWieldedLocation: null,
Priority: null,
ItemsCapacity: null,
ContainersCapacity: null,
Structure: null,
MaxStructure: null,
Workmanship: null);
}