Polishes
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
using Unity.Entities;
|
||||
using Unity.NetCode;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ProjectM.Client
|
||||
{
|
||||
/// <summary>
|
||||
/// Why the last session ended, surfaced once on the main menu (client-local). Set by
|
||||
/// <see cref="ConnectionWatchdogSystem"/> (host vanished / join never connected) or by the connect path
|
||||
/// (unparseable address); <see cref="MainMenuController"/> shows + clears it. Statics reset on play-enter
|
||||
/// (fast-enter-playmode safe, the VFXConfig precedent).
|
||||
/// </summary>
|
||||
public static class SessionEnd
|
||||
{
|
||||
public static string Reason;
|
||||
|
||||
/// <summary>Force an immediate teardown-to-menu (e.g. a bad join address) without waiting for the
|
||||
/// watchdog's connect timeout. Set <see cref="Reason"/> first.</summary>
|
||||
public static bool Abort;
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
||||
static void Reset() { Reason = null; Abort = false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hands-off co-op fail-safe: watches this client world's connection and returns the player to the main
|
||||
/// menu (reason on <see cref="SessionEnd"/>) instead of leaving them in a dead world with a live HUD.
|
||||
/// Exits: (1) the accepted connection VANISHED on a pure join client — host quit or the network died (the
|
||||
/// host's own in-proc client is excluded: its server outliving user-driven teardown is
|
||||
/// <see cref="WorldLauncher.Busy"/>-guarded); (2) a join was requested but never accepted within
|
||||
/// <see cref="JoinTimeoutSeconds"/> (wrong IP / host not hosting — covers the silent
|
||||
/// NetworkStreamRequestConnect failure modes); (3) <see cref="SessionEnd.Abort"/> was flagged. Managed
|
||||
/// observe-only client system — never writes sim state; world disposal runs on the WorldLauncher /
|
||||
/// SessionRunner frame-boundary path, never here.
|
||||
/// </summary>
|
||||
[WorldSystemFilter(WorldSystemFilterFlags.ClientSimulation)]
|
||||
public partial class ConnectionWatchdogSystem : SystemBase
|
||||
{
|
||||
const float JoinTimeoutSeconds = 12f;
|
||||
|
||||
bool _sawConnection;
|
||||
float _unconnectedElapsed;
|
||||
|
||||
protected override void OnUpdate()
|
||||
{
|
||||
if (WorldLauncher.Busy) return; // a start/teardown routine owns world lifecycle right now
|
||||
|
||||
if (SessionEnd.Abort)
|
||||
{
|
||||
SessionEnd.Abort = false;
|
||||
WorldLauncher.TeardownToMenu();
|
||||
return;
|
||||
}
|
||||
|
||||
// An ACCEPTED connection carries NetworkId (at most one per client world).
|
||||
bool accepted = SystemAPI.HasSingleton<NetworkId>();
|
||||
if (accepted)
|
||||
{
|
||||
_sawConnection = true;
|
||||
_unconnectedElapsed = 0f;
|
||||
return;
|
||||
}
|
||||
|
||||
var serverWorld = ClientServerBootstrap.ServerWorld;
|
||||
bool pureJoiner = serverWorld == null || !serverWorld.IsCreated;
|
||||
|
||||
if (_sawConnection)
|
||||
{
|
||||
_sawConnection = false;
|
||||
if (pureJoiner)
|
||||
{
|
||||
SessionEnd.Reason = "CONNECTION LOST — the host ended the session.";
|
||||
Debug.Log("[ConnectionWatchdog] " + SessionEnd.Reason); // build-log trace for headless smokes
|
||||
WorldLauncher.TeardownToMenu();
|
||||
}
|
||||
// Host/single: losing the loopback connection outside teardown is transient noise; re-arm.
|
||||
return;
|
||||
}
|
||||
|
||||
// Never accepted yet — only a real remote join gets the timeout escape hatch.
|
||||
if (!pureJoiner) return;
|
||||
_unconnectedElapsed += SystemAPI.Time.DeltaTime;
|
||||
if (_unconnectedElapsed >= JoinTimeoutSeconds)
|
||||
{
|
||||
SessionEnd.Reason = "COULD NOT REACH HOST — check the IP and that the host is hosting.";
|
||||
Debug.Log("[ConnectionWatchdog] " + SessionEnd.Reason); // build-log trace for headless smokes
|
||||
WorldLauncher.TeardownToMenu();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user