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 Unity.Netcode;
using UnityEngine; using UnityEngine;
@@ -105,6 +104,7 @@ public class NetworkPlayerSetup : NetworkBehaviour
private bool _visibilityApplied; private bool _visibilityApplied;
private bool _lastVisible = true; private bool _lastVisible = true;
private bool _renderGate; // conversion: body force-hidden until the fresh hunter has settled into place 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 MeshPainter _painter; // owns the per-avatar material instance carrying _XRayColor
private ConcealmentMeter _concealment; // owner-only embedded-percent source (disabled on remotes) 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)) if (!float.IsNaN(_spawnOverride.Value.x))
{ {
_renderGate = true; _renderGate = true;
_renderGateUntil = Time.time + conversionRevealDelay;
SetBodyRenderers(false); // hide immediately, before the first UpdateVisibility runs 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. // never fights the FirstPersonController's own-body self-hide.
private void UpdateVisibility() 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; bool found = _isFound.Value;
if (IsOwner && !found) return; if (IsOwner && !found) return;
@@ -281,14 +286,6 @@ public class NetworkPlayerSetup : NetworkBehaviour
_visibilityApplied = true; _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 // 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 // always visible; a Classic-found hider is always hidden; Resolution reveals everyone. The rest is the
// hider-visibility matrix per mode: // hider-visibility matrix per mode: