refactor(world): establish live entity composition seams

This commit is contained in:
Erik 2026-07-21 13:09:58 +02:00
parent db5a11707d
commit d68c83d1a5
5 changed files with 357 additions and 21 deletions

View file

@ -1007,8 +1007,9 @@ public sealed class GameWindow : IDisposable, ILiveAnimationPresentationContext
private AcDream.App.Net.LiveSessionController? _liveSessionController;
private AcDream.Core.Net.WorldSession? LiveSession =>
_liveSessionController?.CurrentSession;
private int _liveCenterX;
private int _liveCenterY;
private readonly AcDream.App.World.LiveWorldOriginState _liveWorldOrigin = new();
private int _liveCenterX => _liveWorldOrigin.CenterX;
private int _liveCenterY => _liveWorldOrigin.CenterY;
// #192 (2026-07-09): true once the player's first canonical Position has
// been accepted — i.e. _liveCenterX/Y reflects a real, server-confirmed
// position, not the Holtburg startup placeholder. Renderability is not a
@ -1019,7 +1020,7 @@ public sealed class GameWindow : IDisposable, ILiveAnimationPresentationContext
// AcDream.Core.World.StreamingReadinessGate for the full mechanism this
// closes (stale-centered geometry built during the InWorld-but-not-yet-
// located window, applied late once its build finished).
private bool _liveCenterKnown;
private bool _liveCenterKnown => _liveWorldOrigin.IsKnown;
// K-fix1 (2026-04-26): cached at startup so per-frame branches are
// single-flag reads instead of env-var lookups. True iff
@ -2535,6 +2536,8 @@ public sealed class GameWindow : IDisposable, ILiveAnimationPresentationContext
_worldState,
AdvanceLandblockPresentationRetirement);
var liveEntityComponentLifecycle =
new AcDream.App.World.DeferredLiveEntityRuntimeComponentLifecycle();
_liveEntities = new AcDream.App.World.LiveEntityRuntime(
_worldState,
new AcDream.App.World.CompositeLiveEntityResourceLifecycle(
@ -2544,7 +2547,10 @@ public sealed class GameWindow : IDisposable, ILiveAnimationPresentationContext
new(
entityScriptActivator.OnCreate,
entityScriptActivator.OnRemove)),
TearDownLiveEntityRuntimeComponents);
liveEntityComponentLifecycle);
liveEntityComponentLifecycle.Bind(
new AcDream.App.World.DelegateLiveEntityRuntimeComponentLifecycle(
TearDownLiveEntityRuntimeComponents));
_liveEntities.ProjectionVisibilityChanged += (record, visible) =>
{
if (record.WorldEntity is { } entity)
@ -2844,8 +2850,7 @@ public sealed class GameWindow : IDisposable, ILiveAnimationPresentationContext
// enter the world as the first character on the account, and stream
// CreateObject messages into _worldGameState as they arrive. Entirely
// gated behind ACDREAM_LIVE=1 so the default run path is unchanged.
_liveCenterX = centerX;
_liveCenterY = centerY;
_liveWorldOrigin.SetPlaceholder(centerX, centerY);
_liveSessionController = new AcDream.App.Net.LiveSessionController();
AcDream.App.Net.LiveSessionStartResult liveStart =
_liveSessionController.Start(
@ -3010,7 +3015,7 @@ public sealed class GameWindow : IDisposable, ILiveAnimationPresentationContext
// X/Y are ignored until the next logical player CreateObject claims a
// new origin. Keeping the last values avoids inventing a synthetic
// landblock while still forcing first-spawn initialization.
_liveCenterKnown = false;
_liveWorldOrigin.Reset();
}
private void ResetSessionLiveRuntime()
@ -3409,21 +3414,22 @@ public sealed class GameWindow : IDisposable, ILiveAnimationPresentationContext
private void TryInitializeLiveCenter(
AcDream.Core.Net.WorldSession.EntitySpawn spawn)
{
if (_liveCenterKnown
|| spawn.Guid != _playerServerGuid
if (spawn.Guid != _playerServerGuid
|| spawn.Position is not { } position)
return;
int lbX = (int)((position.LandblockId >> 24) & 0xFFu);
int lbY = (int)((position.LandblockId >> 16) & 0xFFu);
_liveCenterKnown = true;
if (lbX != _liveCenterX || lbY != _liveCenterY)
int oldCenterX = _liveWorldOrigin.CenterX;
int oldCenterY = _liveWorldOrigin.CenterY;
if (!_liveWorldOrigin.TryInitialize(lbX, lbY))
return;
if (lbX != oldCenterX || lbY != oldCenterY)
{
Console.WriteLine(
$"live: first player position — recentering streaming from ({_liveCenterX},{_liveCenterY}) " +
$"live: first player position — recentering streaming from ({oldCenterX},{oldCenterY}) " +
$"to ({lbX},{lbY}) @0x{position.LandblockId:X8}");
_liveCenterX = lbX;
_liveCenterY = lbY;
}
if (_streamingController is not null && _dats is not null)
@ -7337,8 +7343,7 @@ public sealed class GameWindow : IDisposable, ILiveAnimationPresentationContext
// can be the abandoned first destination, not the unplaced player's source.
_physicsEngine.RemoveLandblock(streamingOriginLandblockId);
_liveCenterX = lbX;
_liveCenterY = lbY;
_liveWorldOrigin.Recenter(lbX, lbY);
newWorldPos = new System.Numerics.Vector3(
p.PositionX, p.PositionY, p.PositionZ);