feat(headless): complete deterministic bot command parity
This commit is contained in:
parent
7e8acb74dd
commit
38e83640d9
37 changed files with 2805 additions and 295 deletions
|
|
@ -3,12 +3,76 @@ using AcDream.Headless.Credentials;
|
|||
using AcDream.Headless.Diagnostics;
|
||||
using AcDream.Headless.Policies;
|
||||
using AcDream.Runtime;
|
||||
using AcDream.Runtime.Gameplay;
|
||||
using AcDream.Runtime.Session;
|
||||
|
||||
namespace AcDream.Headless.Hosting;
|
||||
|
||||
internal sealed class HeadlessSessionHost : IDisposable
|
||||
{
|
||||
private sealed class SessionCommandRoute(
|
||||
ILiveSessionCommandRouting gameplay,
|
||||
ILiveSessionCommandRouting commands)
|
||||
: ILiveSessionCommandRouting
|
||||
{
|
||||
private bool _gameplayActive;
|
||||
private bool _commandsActive;
|
||||
|
||||
public void Activate()
|
||||
{
|
||||
if (_gameplayActive || _commandsActive)
|
||||
return;
|
||||
gameplay.Activate();
|
||||
_gameplayActive = true;
|
||||
try
|
||||
{
|
||||
commands.Activate();
|
||||
_commandsActive = true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
gameplay.Dispose();
|
||||
_gameplayActive = false;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
List<Exception>? failures = null;
|
||||
if (_commandsActive)
|
||||
{
|
||||
try
|
||||
{
|
||||
commands.Dispose();
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
(failures ??= []).Add(error);
|
||||
}
|
||||
_commandsActive = false;
|
||||
}
|
||||
if (_gameplayActive)
|
||||
{
|
||||
try
|
||||
{
|
||||
gameplay.Dispose();
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
(failures ??= []).Add(error);
|
||||
}
|
||||
_gameplayActive = false;
|
||||
}
|
||||
if (failures is not null)
|
||||
{
|
||||
throw new AggregateException(
|
||||
"Headless command routes did not detach cleanly.",
|
||||
failures);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class SessionCommandBridge : IRuntimeSessionCommands
|
||||
{
|
||||
private HeadlessSessionHost? _owner;
|
||||
|
|
@ -52,6 +116,7 @@ internal sealed class HeadlessSessionHost : IDisposable
|
|||
private readonly IHeadlessBotPolicy _policy;
|
||||
private readonly IDisposable _policySubscription;
|
||||
private readonly LiveSessionHost _liveSession;
|
||||
private readonly RuntimeLocalPlayerFrameController _localPlayerFrame;
|
||||
private int _disposeStage;
|
||||
private long _reconnectDeadline;
|
||||
private bool _reconnectPending;
|
||||
|
|
@ -114,7 +179,9 @@ internal sealed class HeadlessSessionHost : IDisposable
|
|||
new LiveSessionHostBindings(
|
||||
new LiveSessionRoutingFactories(
|
||||
CreateEventRoute,
|
||||
commands.CreateRoute),
|
||||
session => new SessionCommandRoute(
|
||||
gameplay.CreateRoute(session),
|
||||
commands.CreateRoute(session))),
|
||||
generation =>
|
||||
runtime.ResetGeneration(generation, _resetHost),
|
||||
new LiveSessionSelectionBindings(
|
||||
|
|
@ -143,6 +210,13 @@ internal sealed class HeadlessSessionHost : IDisposable
|
|||
Runtime = runtime;
|
||||
Commands = commands;
|
||||
_liveSession = liveSession;
|
||||
_localPlayerFrame =
|
||||
runtime.CreateLocalPlayerFrameController(
|
||||
new HeadlessLocalPlayerFrameHost(
|
||||
runtime,
|
||||
liveSession),
|
||||
new HeadlessMovementInputSource(
|
||||
runtime.MovementOwner));
|
||||
bridge.Bind(this);
|
||||
|
||||
hostLease = runtime.AcquireHostLease(
|
||||
|
|
@ -194,7 +268,10 @@ internal sealed class HeadlessSessionHost : IDisposable
|
|||
if (_reconnectPending)
|
||||
return;
|
||||
_ = Runtime.Clock.Advance(deltaSeconds);
|
||||
_localPlayerFrame.AdvanceBeforeNetwork(
|
||||
checked((float)deltaSeconds));
|
||||
Runtime.Session.Tick();
|
||||
_localPlayerFrame.RunPostNetworkCommandPhase();
|
||||
Runtime.ActionOwner.CombatAttack.Tick();
|
||||
_policy.Tick(Runtime, Commands);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue