refactor(runtime): own projectile simulation

Move projectile component identity, prediction invalidation, spatial worksets, authoritative corrections, and the retail physics step into AcDream.Runtime. Keep App as the DAT-shape and presentation adapter so ACE outcomes and visible behavior remain unchanged.
This commit is contained in:
Erik 2026-07-26 14:17:42 +02:00
parent fce8e7b18e
commit 2aee33569f
19 changed files with 1255 additions and 335 deletions

View file

@ -3,6 +3,7 @@ using System.Reflection;
using AcDream.App.Physics;
using AcDream.App.Rendering;
using AcDream.App.World;
using AcDream.Core.Physics;
namespace AcDream.App.Tests.World;
@ -161,6 +162,18 @@ public sealed class GameWindowLiveEntityCompositionTests
Assert.Equal(
typeof(LiveWorldOriginState),
Assert.Single(projectileFields, field => field.Name == "_origin").FieldType);
Assert.Equal(
typeof(RuntimeProjectilePhysicsUpdater),
Assert.Single(
projectileFields,
field => field.Name == "_runtimeUpdater").FieldType);
Assert.DoesNotContain(
projectileFields,
field => field.FieldType == typeof(PhysicsEngine)
|| field.FieldType == typeof(ProjectilePhysicsStepper)
|| field.FieldType.IsGenericType
&& field.FieldType.GetGenericTypeDefinition()
== typeof(Dictionary<,>));
Assert.DoesNotContain(
projectileFields,
field => typeof(Delegate).IsAssignableFrom(field.FieldType)

View file

@ -422,7 +422,7 @@ public sealed class LiveEntityLifecycleStressTests
randomUnit: () => 0.5,
canAdvanceOwner: ownerId => _effects?.CanAdvanceOwner(ownerId) ?? true);
EntityObjects = new RuntimeEntityObjectLifetime();
EntityObjects = new RuntimeEntityObjectLifetime(Engine);
Runtime = new LiveEntityRuntime(
Spatial,
new DelegateLiveEntityResourceLifecycle(
@ -463,7 +463,7 @@ public sealed class LiveEntityLifecycleStressTests
Poses);
_effects = Effects;
Router.Register(Effects);
Projectiles = new ProjectileController(Runtime, Engine);
Projectiles = new ProjectileController(Runtime);
LiveLights = new LiveEntityLightController(
Runtime,
Poses,

View file

@ -199,10 +199,10 @@ public sealed class LiveEntityProjectionWithdrawalControllerTests
Live = LiveEntityRuntimeFixture.Create(
Spatial,
new DelegateLiveEntityResourceLifecycle(_ => { }, _ => { }),
lifecycle);
Projectiles = new ProjectileController(
Live,
lifecycle,
Physics);
Projectiles = new ProjectileController(
Live);
Controller = new LiveEntityProjectionWithdrawalController(
Live,
Projectiles,

View file

@ -53,11 +53,6 @@ public sealed class LiveEntityRuntimeTests
public PhysicsBody Body { get; } = new();
}
private sealed class ProjectileRuntime(PhysicsBody body) : ILiveEntityProjectileRuntime
{
public PhysicsBody Body { get; } = body;
}
private sealed class FailingRegisterResources : ILiveEntityResourceLifecycle
{
public int RegisterCount { get; private set; }
@ -427,10 +422,12 @@ public sealed class LiveEntityRuntimeTests
Assert.True(runtime.TryGetRecord(guid, out LiveEntityRecord record));
var animation = new AnimationRuntime(entity);
var remote = new RemoteMotionRuntime();
var projectile = new ProjectileRuntime(remote.Body);
runtime.SetAnimationRuntime(guid, animation);
runtime.SetRemoteMotionRuntime(guid, remote);
runtime.SetProjectileRuntime(guid, projectile);
IRuntimeProjectile projectile = runtime.BindProjectileRuntime(
guid,
remote.Body,
new ProjectileCollisionSphere(Vector3.Zero, 0.25f));
Assert.True(runtime.IsCurrentSpatialAnimation(record, animation));
Assert.True(runtime.IsCurrentSpatialRemoteMotion(record, remote));