Fix stuck conversion render-gate on mid-settle disable/disconnect

The freshly converted hunter's body was hidden for the settle window via a
WaitForSeconds coroutine. If the object was disabled or the converting owner
disconnected mid-settle, the coroutine was stranded and _renderGate stayed
true forever, leaving the avatar invisible on every remote.

Replace it with a frame-driven Time.time deadline (_renderGateUntil) lifted
in UpdateVisibility, which runs every frame the component is active and can
never get stuck. Remove the coroutine and the now-unused System.Collections
using. Visible behaviour in the normal case is unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 4.8 committed 2026-07-02 20:09:11 +02:00
1 parent 0c0a250015
commit 17bb37cb92
1 file changed
+7 -10
+7 -10
View File
@@ -1,4 +1,3 @@
using System.Collections;
using Unity.Netcode;
using UnityEngine;
@@ -105,6 +104,7 @@ public class NetworkPlayerSetup : NetworkBehaviour
private bool _visibilityApplied;
private bool _lastVisible = true;
private bool _renderGate; // conversion: body force-hidden until the fresh hunter has settled into place
private float _renderGateUntil; // Time.time deadline that lifts _renderGate; frame-driven (see UpdateVisibility) so a mid-settle disable/despawn can't strand the body hidden forever
private MeshPainter _painter; // owns the per-avatar material instance carrying _XRayColor
private ConcealmentMeter _concealment; // owner-only embedded-percent source (disabled on remotes)
@@ -170,8 +170,8 @@ public class NetworkPlayerSetup : NetworkBehaviour
if (!float.IsNaN(_spawnOverride.Value.x))
{
_renderGate = true;
_renderGateUntil = Time.time + conversionRevealDelay;
SetBodyRenderers(false); // hide immediately, before the first UpdateVisibility runs
StartCoroutine(RevealAfterSettle());
}
}
@@ -262,6 +262,11 @@ public class NetworkPlayerSetup : NetworkBehaviour
// never fights the FirstPersonController's own-body self-hide.
private void UpdateVisibility()
{
// Frame-driven gate expiry (replaces a WaitForSeconds coroutine): a coroutine is stranded if the
// object is disabled or the converting owner disconnects mid-settle, leaving the body hidden forever.
// Time.time lifts it here every frame the component runs, so the gate can never get stuck.
if (_renderGate && Time.time >= _renderGateUntil) _renderGate = false;
bool found = _isFound.Value;
if (IsOwner && !found) return;
@@ -281,14 +286,6 @@ public class NetworkPlayerSetup : NetworkBehaviour
_visibilityApplied = true;
}
// Conversion: drop the render gate after the settle delay so the next visibility pass reveals the hunter
// at its final, replicated position. Per-client and cosmetic (each client reveals on its own clock).
private IEnumerator RevealAfterSettle()
{
yield return new WaitForSeconds(conversionRevealDelay);
_renderGate = false;
}
// Decides whether THIS avatar's body is rendered for the local viewer. Hunters and non-match avatars are
// always visible; a Classic-found hider is always hidden; Resolution reveals everyone. The rest is the
// hider-visibility matrix per mode: