test(physics): pin issue 270 animation fixes
This commit is contained in:
parent
bb1640f777
commit
0cb60d98a0
7 changed files with 337 additions and 43 deletions
|
|
@ -0,0 +1,52 @@
|
|||
using AcDream.App.Net;
|
||||
|
||||
namespace AcDream.App.Tests.Net;
|
||||
|
||||
public sealed class StaminaExhaustionEdgeTrackerTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(100)]
|
||||
[InlineData(0)]
|
||||
public void FirstSample_EstablishesBaselineWithoutNotification(int stamina)
|
||||
{
|
||||
var tracker = new StaminaExhaustionEdgeTracker();
|
||||
|
||||
Assert.False(tracker.Observe(stamina));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RepeatedStatsTicks_DoNotRedispatchMovement()
|
||||
{
|
||||
var tracker = new StaminaExhaustionEdgeTracker();
|
||||
|
||||
Assert.False(tracker.Observe(100));
|
||||
Assert.False(tracker.Observe(99));
|
||||
Assert.False(tracker.Observe(50));
|
||||
Assert.False(tracker.Observe(1));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExhaustedStateTransitions_ReportExactlyOncePerEdge()
|
||||
{
|
||||
var tracker = new StaminaExhaustionEdgeTracker();
|
||||
|
||||
Assert.False(tracker.Observe(50));
|
||||
Assert.True(tracker.Observe(0));
|
||||
Assert.False(tracker.Observe(0));
|
||||
Assert.True(tracker.Observe(1));
|
||||
Assert.False(tracker.Observe(80));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Reset_PreventsCrossGenerationSyntheticEdge()
|
||||
{
|
||||
var tracker = new StaminaExhaustionEdgeTracker();
|
||||
Assert.False(tracker.Observe(50));
|
||||
Assert.True(tracker.Observe(0));
|
||||
|
||||
tracker.Reset();
|
||||
|
||||
Assert.False(tracker.Observe(75));
|
||||
Assert.True(tracker.Observe(0));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace AcDream.App.Tests.Physics;
|
||||
|
||||
public sealed class Issue270ProductionWiringTests
|
||||
{
|
||||
[Fact]
|
||||
public void MovementStats_UseOneEdgeTrackerAndResetItWithTheSession()
|
||||
{
|
||||
string source = ReadSource("Net", "LiveSessionRuntimeFactory.cs");
|
||||
|
||||
Assert.Contains(
|
||||
"_staminaExhaustion.Observe(snapshot.CurrentStamina)",
|
||||
source,
|
||||
StringComparison.Ordinal);
|
||||
Assert.Single(
|
||||
Regex.Matches(
|
||||
source,
|
||||
@"controller!\.Motion\.ReportExhaustion\(\);")
|
||||
.Cast<Match>());
|
||||
Assert.Contains(
|
||||
"_staminaExhaustion.Reset();",
|
||||
source,
|
||||
StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RemoteSpawnSettle_IsRetriedAndCoversBothCreationRoutes()
|
||||
{
|
||||
string source = ReadSource(
|
||||
"Physics",
|
||||
"LiveEntityNetworkUpdateController.cs");
|
||||
|
||||
Assert.Contains(
|
||||
"if (!remote.Body.InContact)",
|
||||
source,
|
||||
StringComparison.Ordinal);
|
||||
Assert.Equal(
|
||||
3,
|
||||
Regex.Matches(source, @"SeedRemoteSpawnPlacement\(").Count);
|
||||
Assert.Contains(
|
||||
"RemoteSpawnPlacementSettler.TrySettle(",
|
||||
source,
|
||||
StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
private static string ReadSource(params string[] relativePath)
|
||||
{
|
||||
DirectoryInfo? directory = new(AppContext.BaseDirectory);
|
||||
while (directory is not null)
|
||||
{
|
||||
if (File.Exists(Path.Combine(directory.FullName, "AcDream.slnx")))
|
||||
{
|
||||
return File.ReadAllText(Path.Combine(
|
||||
directory.FullName,
|
||||
"src",
|
||||
"AcDream.App",
|
||||
Path.Combine(relativePath)));
|
||||
}
|
||||
|
||||
directory = directory.Parent;
|
||||
}
|
||||
|
||||
throw new DirectoryNotFoundException("Could not find AcDream.slnx.");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
using System.Numerics;
|
||||
using AcDream.App.Physics;
|
||||
using AcDream.Core.Physics;
|
||||
|
||||
namespace AcDream.App.Tests.Physics;
|
||||
|
||||
public sealed class RemoteSpawnPlacementSettlerTests
|
||||
{
|
||||
private const uint Landblock = 0xA9B40000u;
|
||||
private const uint Cell = Landblock | 0x0001u;
|
||||
private const float Radius = 0.48f;
|
||||
private const float Height = 1.835f;
|
||||
|
||||
[Fact]
|
||||
public void FloorWithinFirstGravityFrame_CommitsGroundContactOnce()
|
||||
{
|
||||
PhysicsEngine engine = BuildFlatEngine();
|
||||
PhysicsBody body = AirborneBody(new Vector3(12f, 12f, 0.25f));
|
||||
int hitGround = 0;
|
||||
int leaveGround = 0;
|
||||
|
||||
bool settled = RemoteSpawnPlacementSettler.TrySettle(
|
||||
engine,
|
||||
body,
|
||||
body.Position,
|
||||
Cell,
|
||||
Radius,
|
||||
Height,
|
||||
ObjectInfoState.EdgeSlide,
|
||||
movingEntityId: 0x70000001u,
|
||||
() => hitGround++,
|
||||
() => leaveGround++);
|
||||
|
||||
Assert.True(settled);
|
||||
Assert.True(body.InContact);
|
||||
Assert.True(body.OnWalkable);
|
||||
Assert.Equal(1, hitGround);
|
||||
Assert.Equal(0, leaveGround);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MissingCell_CanRetryAfterHydrationWithoutReconstructingBody()
|
||||
{
|
||||
var engine = new PhysicsEngine { DataCache = new PhysicsDataCache() };
|
||||
PhysicsBody body = AirborneBody(new Vector3(12f, 12f, 0.25f));
|
||||
Vector3 originalPosition = body.Position;
|
||||
|
||||
Assert.False(TrySettle(engine, body));
|
||||
Assert.False(body.InContact);
|
||||
Assert.Equal(originalPosition, body.Position);
|
||||
|
||||
AddFlatLandblock(engine);
|
||||
|
||||
Assert.True(TrySettle(engine, body));
|
||||
Assert.True(body.InContact);
|
||||
Assert.True(body.OnWalkable);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FloorOutsideSettleDistance_LeavesGenuinelyAirborneBodyUnchanged()
|
||||
{
|
||||
PhysicsEngine engine = BuildFlatEngine();
|
||||
PhysicsBody body = AirborneBody(new Vector3(12f, 12f, 1.25f));
|
||||
Vector3 originalPosition = body.Position;
|
||||
|
||||
Assert.False(TrySettle(engine, body));
|
||||
|
||||
Assert.False(body.InContact);
|
||||
Assert.False(body.OnWalkable);
|
||||
Assert.Equal(originalPosition, body.Position);
|
||||
}
|
||||
|
||||
private static bool TrySettle(PhysicsEngine engine, PhysicsBody body) =>
|
||||
RemoteSpawnPlacementSettler.TrySettle(
|
||||
engine,
|
||||
body,
|
||||
body.Position,
|
||||
Cell,
|
||||
Radius,
|
||||
Height,
|
||||
ObjectInfoState.EdgeSlide,
|
||||
movingEntityId: 0x70000001u,
|
||||
static () => { },
|
||||
static () => { });
|
||||
|
||||
private static PhysicsBody AirborneBody(Vector3 position) => new()
|
||||
{
|
||||
Position = position,
|
||||
Orientation = Quaternion.Identity,
|
||||
State = PhysicsStateFlags.Gravity | PhysicsStateFlags.ReportCollisions,
|
||||
TransientState = TransientStateFlags.None,
|
||||
};
|
||||
|
||||
private static PhysicsEngine BuildFlatEngine()
|
||||
{
|
||||
var engine = new PhysicsEngine { DataCache = new PhysicsDataCache() };
|
||||
AddFlatLandblock(engine);
|
||||
return engine;
|
||||
}
|
||||
|
||||
private static void AddFlatLandblock(PhysicsEngine engine) =>
|
||||
engine.AddLandblock(
|
||||
Landblock,
|
||||
new TerrainSurface(new byte[81], new float[256]),
|
||||
Array.Empty<CellSurface>(),
|
||||
Array.Empty<PortalPlane>(),
|
||||
worldOffsetX: 0f,
|
||||
worldOffsetY: 0f);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue