vPOD: pod power controls, end-mission restart cycle, egg viewer polish
Adds pod-lifecycle controls to the vPOD window: - Power Off / Power On: Power Off closes the TCP listener so the console can no longer connect, mimicking a pod with no game client running; Power On reopens it. The listener close/open is the faithful signal a console sees. - End-mission graceful-exit + watchdog restart: on the end-mission command (StopMission) the game exits gracefully (listener closes, connection drops) and a watchdog relaunches it about 1.5 s later, coming back up in WaitingForEgg - the real pod's per-game cycle. A "Restart game after mission ends (watchdog)" checkbox (default on) toggles it; unchecked, the pod just returns to WaitingForEgg without exiting. - Egg viewer: the last egg is kept across missions/restarts (no auto-clear) so it can be copied for dev use; a Clear button empties it on demand. - Log pane defaults to half the window (egg lines are rarely wide); the split is set at load once the control has its real width. Verified over real TCP: driving egg -> run -> end-mission makes the listener drop then reopen with the pod back in WaitingForEgg (confirmed in vPOD's own log: "Game exited gracefully" then "Watchdog restarted the game"). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -33,6 +33,7 @@ internal sealed class PodSimulator
|
||||
private ApplicationID mApplicationId;
|
||||
private int mHostId;
|
||||
private int mTransitionDelayMs = 400;
|
||||
private bool mRestartOnEndMission = true;
|
||||
|
||||
// Egg reassembly
|
||||
private byte[] mEggBuffer;
|
||||
@@ -49,6 +50,7 @@ internal sealed class PodSimulator
|
||||
public event Action<string> EggReceived; // full egg text (NUL-normalized)
|
||||
public event Action EggProgress; // a chunk arrived (mid-transfer)
|
||||
public event Action<string> Log;
|
||||
public event Action EndMissionExit; // game exited on end mission; watchdog should restart it
|
||||
|
||||
public PodSimulator(MungaPodServer server, ApplicationID applicationId, int hostId)
|
||||
{
|
||||
@@ -77,6 +79,17 @@ internal sealed class PodSimulator
|
||||
set { lock (mLock) { mTransitionDelayMs = Math.Max(0, value); } }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When true (the faithful default), the end-mission command makes the game
|
||||
/// exit and its watchdog restart it (the form drops and reopens the listener).
|
||||
/// When false, the pod simply returns to WaitingForEgg without exiting.
|
||||
/// </summary>
|
||||
public bool RestartOnEndMission
|
||||
{
|
||||
get { lock (mLock) { return mRestartOnEndMission; } }
|
||||
set { lock (mLock) { mRestartOnEndMission = value; } }
|
||||
}
|
||||
|
||||
public string EggText { get { lock (mLock) { return mEggText; } } }
|
||||
|
||||
public int EggPercent
|
||||
@@ -139,11 +152,24 @@ internal sealed class PodSimulator
|
||||
break;
|
||||
|
||||
case StopMissionMessage _:
|
||||
Log?.Invoke("<- StopMissionMessage");
|
||||
Log?.Invoke("<- StopMissionMessage (end mission)");
|
||||
if (State == ApplicationState.RunningMission || State == ApplicationState.SuspendingMission)
|
||||
{
|
||||
SetState(ApplicationState.EndingMission);
|
||||
ScheduleTransition(ApplicationState.WaitingForEgg, ResetEggOnArrive: true);
|
||||
bool restart;
|
||||
lock (mLock) { restart = mRestartOnEndMission; }
|
||||
if (restart)
|
||||
{
|
||||
// Faithful pod behaviour: the game exe exits gracefully on the
|
||||
// end-mission command, then its watchdog restarts it. The form
|
||||
// simulates the exit + restart by dropping and reopening the
|
||||
// listener; the pod comes back up in WaitingForEgg.
|
||||
EndMissionExit?.Invoke();
|
||||
}
|
||||
else
|
||||
{
|
||||
ScheduleTransition(ApplicationState.WaitingForEgg, ResetEggOnArrive: true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user