fix(physics): port retail slope landing stop
This commit is contained in:
parent
1d8371dbe5
commit
5a0f9868a6
13 changed files with 870 additions and 103 deletions
191
src/AcDream.Runtime/Gameplay/PlayerPhysicsQuantumCapture.cs
Normal file
191
src/AcDream.Runtime/Gameplay/PlayerPhysicsQuantumCapture.cs
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
using System.Numerics;
|
||||
using System.Text.Json;
|
||||
using AcDream.Core.Physics;
|
||||
|
||||
namespace AcDream.Runtime.Gameplay;
|
||||
|
||||
/// <summary>
|
||||
/// Opt-in, player-only trace of the complete retail object quantum.
|
||||
/// <para>
|
||||
/// Set <c>ACDREAM_CAPTURE_PLAYER_QUANTA=<path></c> before process
|
||||
/// startup to emit one JSON-Lines record per admitted physics quantum. The
|
||||
/// capture is deliberately outside the physics implementation: it observes
|
||||
/// the same stage boundaries as retail
|
||||
/// <c>CPhysicsObj::UpdateObjectInternal</c> without changing their order or
|
||||
/// introducing a second simulation path. When disabled, the hot path pays
|
||||
/// one static null/empty check and performs no allocations.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
internal static class PlayerPhysicsQuantumCapture
|
||||
{
|
||||
internal static string? CapturePath { get; set; } =
|
||||
Environment.GetEnvironmentVariable("ACDREAM_CAPTURE_PLAYER_QUANTA");
|
||||
|
||||
internal static bool IsEnabled => !string.IsNullOrWhiteSpace(CapturePath);
|
||||
|
||||
private static readonly JsonSerializerOptions s_jsonOptions = new()
|
||||
{
|
||||
IncludeFields = true,
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
WriteIndented = false,
|
||||
};
|
||||
|
||||
private static readonly object s_writerLock = new();
|
||||
private static StreamWriter? s_writer;
|
||||
private static long s_sequence;
|
||||
private static bool s_processExitHooked;
|
||||
|
||||
internal static PlayerPhysicsBodyTraceSnapshot Snapshot(PhysicsBody body) =>
|
||||
new(
|
||||
Position: body.Position,
|
||||
Orientation: body.Orientation,
|
||||
Velocity: body.Velocity,
|
||||
Acceleration: body.Acceleration,
|
||||
GroundNormal: body.GroundNormal,
|
||||
ContactPlaneValid: body.ContactPlaneValid,
|
||||
ContactPlane: body.ContactPlane,
|
||||
Friction: body.Friction,
|
||||
Elasticity: body.Elasticity,
|
||||
State: (uint)body.State,
|
||||
TransientState: (uint)body.TransientState,
|
||||
FramesStationaryFall: body.FramesStationaryFall);
|
||||
|
||||
internal static void Log(
|
||||
float dt,
|
||||
uint cellBefore,
|
||||
uint cellAfter,
|
||||
MovementInput input,
|
||||
Vector3 rootAndManagerDelta,
|
||||
bool candidateMoved,
|
||||
PlayerPhysicsBodyTraceSnapshot quantumStart,
|
||||
PlayerPhysicsBodyTraceSnapshot preIntegration,
|
||||
PlayerPhysicsBodyTraceSnapshot postIntegration,
|
||||
PlayerPhysicsResolveTraceSnapshot resolve,
|
||||
PlayerPhysicsBodyTraceSnapshot postCommit)
|
||||
{
|
||||
string? path = CapturePath;
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
return;
|
||||
|
||||
var record = new PlayerPhysicsQuantumTraceRecord(
|
||||
Sequence: Interlocked.Increment(ref s_sequence) - 1,
|
||||
TimestampTicks: System.Diagnostics.Stopwatch.GetTimestamp(),
|
||||
Dt: dt,
|
||||
CellBefore: cellBefore,
|
||||
CellAfter: cellAfter,
|
||||
Input: new PlayerMovementInputTraceSnapshot(
|
||||
input.Forward,
|
||||
input.Backward,
|
||||
input.StrafeLeft,
|
||||
input.StrafeRight,
|
||||
input.TurnLeft,
|
||||
input.TurnRight,
|
||||
input.Run,
|
||||
input.Jump),
|
||||
RootAndManagerDelta: rootAndManagerDelta,
|
||||
CandidateMoved: candidateMoved,
|
||||
QuantumStart: quantumStart,
|
||||
PreIntegration: preIntegration,
|
||||
PostIntegration: postIntegration,
|
||||
Resolve: resolve,
|
||||
PostCommit: postCommit);
|
||||
|
||||
string json = JsonSerializer.Serialize(record, s_jsonOptions);
|
||||
lock (s_writerLock)
|
||||
{
|
||||
EnsureWriter_NoLock(path);
|
||||
s_writer!.WriteLine(json);
|
||||
s_writer.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Close()
|
||||
{
|
||||
lock (s_writerLock)
|
||||
{
|
||||
s_writer?.Dispose();
|
||||
s_writer = null;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void ResetForTest()
|
||||
{
|
||||
Close();
|
||||
CapturePath = null;
|
||||
Interlocked.Exchange(ref s_sequence, 0);
|
||||
}
|
||||
|
||||
private static void EnsureWriter_NoLock(string path)
|
||||
{
|
||||
if (s_writer is not null)
|
||||
return;
|
||||
|
||||
string? directory = Path.GetDirectoryName(path);
|
||||
if (!string.IsNullOrEmpty(directory))
|
||||
Directory.CreateDirectory(directory);
|
||||
|
||||
s_writer = new StreamWriter(new FileStream(
|
||||
path,
|
||||
FileMode.Append,
|
||||
FileAccess.Write,
|
||||
FileShare.Read))
|
||||
{
|
||||
AutoFlush = false,
|
||||
};
|
||||
|
||||
if (!s_processExitHooked)
|
||||
{
|
||||
AppDomain.CurrentDomain.ProcessExit += static (_, _) => Close();
|
||||
s_processExitHooked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed record PlayerPhysicsQuantumTraceRecord(
|
||||
long Sequence,
|
||||
long TimestampTicks,
|
||||
float Dt,
|
||||
uint CellBefore,
|
||||
uint CellAfter,
|
||||
PlayerMovementInputTraceSnapshot Input,
|
||||
Vector3 RootAndManagerDelta,
|
||||
bool CandidateMoved,
|
||||
PlayerPhysicsBodyTraceSnapshot QuantumStart,
|
||||
PlayerPhysicsBodyTraceSnapshot PreIntegration,
|
||||
PlayerPhysicsBodyTraceSnapshot PostIntegration,
|
||||
PlayerPhysicsResolveTraceSnapshot Resolve,
|
||||
PlayerPhysicsBodyTraceSnapshot PostCommit);
|
||||
|
||||
internal readonly record struct PlayerMovementInputTraceSnapshot(
|
||||
bool Forward,
|
||||
bool Backward,
|
||||
bool StrafeLeft,
|
||||
bool StrafeRight,
|
||||
bool TurnLeft,
|
||||
bool TurnRight,
|
||||
bool Run,
|
||||
bool Jump);
|
||||
|
||||
internal readonly record struct PlayerPhysicsBodyTraceSnapshot(
|
||||
Vector3 Position,
|
||||
Quaternion Orientation,
|
||||
Vector3 Velocity,
|
||||
Vector3 Acceleration,
|
||||
Vector3 GroundNormal,
|
||||
bool ContactPlaneValid,
|
||||
Plane ContactPlane,
|
||||
float Friction,
|
||||
float Elasticity,
|
||||
uint State,
|
||||
uint TransientState,
|
||||
int FramesStationaryFall);
|
||||
|
||||
internal readonly record struct PlayerPhysicsResolveTraceSnapshot(
|
||||
Vector3 Position,
|
||||
uint CellId,
|
||||
bool Ok,
|
||||
bool IsOnGround,
|
||||
bool InContact,
|
||||
bool OnWalkable,
|
||||
bool CollisionNormalValid,
|
||||
Vector3 CollisionNormal);
|
||||
Loading…
Add table
Add a link
Reference in a new issue