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:
Cyd
2026-07-08 10:43:44 -05:00
co-authored by Claude Fable 5
parent aff8bbf6e5
commit fd0c8c5196
3 changed files with 191 additions and 15 deletions
+28 -2
View File
@@ -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;