366 lines
14 KiB
C#
366 lines
14 KiB
C#
using UnityEngine;
|
|
using Unity.Netcode;
|
|
using System.Net.Sockets;
|
|
using System.Net;
|
|
using System;
|
|
using System.Threading;
|
|
|
|
namespace Northbound
|
|
{
|
|
/// <summary>
|
|
/// Continuous port monitor - keeps checking if port is actually open
|
|
/// Helps diagnose why Unity Netcode doesn't keep port open
|
|
/// </summary>
|
|
public class ContinuousPortMonitor : MonoBehaviour
|
|
{
|
|
[Header("Monitoring Settings")]
|
|
[SerializeField] private ushort targetPort = 40445;
|
|
[SerializeField] private bool startMonitoringOnStart = false;
|
|
[SerializeField] private float checkInterval = 1f;
|
|
[SerializeField] private int maxFailedChecksBeforeWarning = 5;
|
|
|
|
[Header("Actions")]
|
|
[SerializeField] private bool autoRestartBinding = true;
|
|
[SerializeField] private bool keepTestListenerAlive = false;
|
|
|
|
[Header("Debug")]
|
|
[SerializeField] private bool verboseLogging = true;
|
|
|
|
private NetworkManager _networkManager;
|
|
private TcpListener _testListener;
|
|
private Thread _listenerThread;
|
|
private volatile bool _listenerRunning = false;
|
|
private float _lastCheckTime;
|
|
private int _failedCheckCount = 0;
|
|
private bool _wasListeningPreviously = false;
|
|
|
|
private void Start()
|
|
{
|
|
StartCoroutine(InitializeDelayed());
|
|
}
|
|
|
|
private System.Collections.IEnumerator InitializeDelayed()
|
|
{
|
|
Debug.Log("[ContinuousPortMonitor] Waiting for NetworkManager...");
|
|
|
|
int attempts = 0;
|
|
while (_networkManager == null && attempts < 50)
|
|
{
|
|
_networkManager = NetworkManager.Singleton;
|
|
if (_networkManager == null)
|
|
{
|
|
yield return new WaitForSeconds(0.1f);
|
|
attempts++;
|
|
}
|
|
}
|
|
|
|
if (_networkManager == null)
|
|
{
|
|
Debug.LogError("[ContinuousPortMonitor] NetworkManager not found!");
|
|
yield break;
|
|
}
|
|
|
|
Debug.Log("[ContinuousPortMonitor] NetworkManager found!");
|
|
|
|
if (startMonitoringOnStart)
|
|
{
|
|
StartMonitoring();
|
|
}
|
|
}
|
|
|
|
[ContextMenu("Start Port Monitoring")]
|
|
public void StartMonitoring()
|
|
{
|
|
Debug.Log("<color=cyan>[ContinuousPortMonitor] Starting port monitoring for " + targetPort + "</color>");
|
|
|
|
if (keepTestListenerAlive)
|
|
{
|
|
StartTestListener();
|
|
}
|
|
|
|
Debug.Log("[ContinuousPortMonitor] Monitoring started - check interval: " + checkInterval + "s");
|
|
Debug.Log("[ContinuousPortMonitor] Will check if port remains accessible");
|
|
}
|
|
|
|
[ContextMenu("Stop Port Monitoring")]
|
|
public void StopMonitoring()
|
|
{
|
|
Debug.Log("[ContinuousPortMonitor] Stopping monitoring...");
|
|
|
|
StopTestListener();
|
|
enabled = false;
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (!enabled) return;
|
|
|
|
if (Time.time - _lastCheckTime >= checkInterval)
|
|
{
|
|
_lastCheckTime = Time.time;
|
|
CheckPortAccessibility();
|
|
}
|
|
}
|
|
|
|
private void CheckPortAccessibility()
|
|
{
|
|
bool isUnityServerRunning = _networkManager != null && _networkManager.IsServer;
|
|
bool isCurrentlyListening = _listenerRunning || isUnityServerRunning;
|
|
|
|
if (!verboseLogging)
|
|
{
|
|
// Only log when status changes
|
|
if (isCurrentlyListening != _wasListeningPreviously)
|
|
{
|
|
_wasListeningPreviously = isCurrentlyListening;
|
|
|
|
if (isCurrentlyListening)
|
|
{
|
|
Debug.Log("<color=green>[ContinuousPortMonitor] ✓ Port " + targetPort + " is NOW ACCESSIBLE</color>");
|
|
}
|
|
else
|
|
{
|
|
Debug.LogWarning("<color=yellow>[ContinuousPortMonitor] ⚠ Port " + targetPort + " became INACCESSIBLE</color>");
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Verbose logging
|
|
if (isUnityServerRunning)
|
|
{
|
|
Debug.Log("[ContinuousPortMonitor] Unity Server: RUNNING");
|
|
}
|
|
|
|
if (_listenerRunning)
|
|
{
|
|
Debug.Log("[ContinuousPortMonitor] Test Listener: RUNNING");
|
|
}
|
|
|
|
bool portAccessible = TestPortConnection();
|
|
|
|
if (portAccessible)
|
|
{
|
|
_failedCheckCount = 0;
|
|
if (Time.frameCount % 60 == 0) // Every second at 60fps
|
|
{
|
|
Debug.Log("<color=green>[ContinuousPortMonitor] ✓ Port " + targetPort + " is accessible</color>");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_failedCheckCount++;
|
|
|
|
if (_failedCheckCount == 1)
|
|
{
|
|
Debug.LogWarning("<color=yellow>[ContinuousPortMonitor] ⚠ Port " + targetPort + " became inaccessible</color>");
|
|
}
|
|
else if (_failedCheckCount % 10 == 0)
|
|
{
|
|
Debug.LogWarning("<color=yellow>[ContinuousPortMonitor] ⚠ Port still inaccessible (check #" + _failedCheckCount + ")</color>");
|
|
}
|
|
|
|
if (_failedCheckCount >= maxFailedChecksBeforeWarning)
|
|
{
|
|
Debug.LogError("<color=red>[ContinuousPortMonitor] ✗ Port has been inaccessible for " + _failedCheckCount + " checks!</color>");
|
|
Debug.LogError("[ContinuousPortMonitor] This indicates a problem with port binding");
|
|
Debug.LogError("[ContinuousPortMonitor] Possible causes:");
|
|
Debug.LogError("1. Unity Netcode is not actually listening");
|
|
Debug.LogError("2. Another process is interfering");
|
|
Debug.LogError("3. NetworkManager has crashed/stopped");
|
|
|
|
if (autoRestartBinding)
|
|
{
|
|
Debug.Log("<color=cyan>[ContinuousPortMonitor] Attempting to restore binding...</color>");
|
|
TryRestoreBinding();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool TestPortConnection()
|
|
{
|
|
try
|
|
{
|
|
using (TcpClient client = new TcpClient("127.0.0.1", targetPort))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private void StartTestListener()
|
|
{
|
|
if (_listenerRunning)
|
|
{
|
|
Debug.LogWarning("[ContinuousPortMonitor] Test listener already running");
|
|
return;
|
|
}
|
|
|
|
_listenerRunning = true;
|
|
_listenerThread = new Thread(new ThreadStart(ListenerThread));
|
|
_listenerThread.IsBackground = true;
|
|
_listenerThread.Start();
|
|
|
|
Debug.Log("[ContinuousPortMonitor] Test listener started on port " + targetPort);
|
|
}
|
|
|
|
private void StopTestListener()
|
|
{
|
|
_listenerRunning = false;
|
|
|
|
if (_testListener != null)
|
|
{
|
|
_testListener.Stop();
|
|
_testListener = null;
|
|
}
|
|
|
|
if (_listenerThread != null && _listenerThread.IsAlive)
|
|
{
|
|
_listenerThread.Join(1000);
|
|
}
|
|
|
|
Debug.Log("[ContinuousPortMonitor] Test listener stopped");
|
|
}
|
|
|
|
private void ListenerThread()
|
|
{
|
|
try
|
|
{
|
|
_testListener = new TcpListener(IPAddress.Any, targetPort);
|
|
_testListener.Start();
|
|
|
|
Debug.Log("[ContinuousPortMonitor] Test listener successfully bound to 0.0.0.0:" + targetPort);
|
|
Debug.Log("[ContinuousPortMonitor] Port should now be accessible on yougetsignal");
|
|
|
|
while (_listenerRunning)
|
|
{
|
|
if (_testListener.Pending())
|
|
{
|
|
try
|
|
{
|
|
TcpClient client = _testListener.AcceptTcpClient();
|
|
IPEndPoint endPoint = (IPEndPoint)client.Client.RemoteEndPoint;
|
|
|
|
Debug.Log("<color=green>[ContinuousPortMonitor] ✓ Connection received from " + endPoint.Address + ":" + endPoint.Port + "</color>");
|
|
Debug.Log("[ContinuousPortMonitor] This proves port forwarding is working!");
|
|
|
|
Thread.Sleep(100);
|
|
client.Close();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.LogWarning("[ContinuousPortMonitor] Error accepting connection: " + e.Message);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Thread.Sleep(100);
|
|
}
|
|
}
|
|
|
|
Debug.Log("[ContinuousPortMonitor] Listener thread exiting");
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
_listenerRunning = false;
|
|
Debug.LogError("<color=red>[ContinuousPortMonitor] Listener error: " + e.Message + "</color>");
|
|
}
|
|
}
|
|
|
|
private void TryRestoreBinding()
|
|
{
|
|
Debug.Log("<color=cyan>[ContinuousPortMonitor] Attempting to restore port binding...</color>");
|
|
|
|
// This is a placeholder - in reality, we'd need to restart Unity Netcode
|
|
// But we can't do that safely from this script
|
|
Debug.LogWarning("[ContinuousPortMonitor] Can't automatically restart Unity Netcode");
|
|
Debug.LogWarning("[ContinuousPortMonitor] Manual intervention required:");
|
|
Debug.LogWarning("1. Stop Host/Server");
|
|
Debug.LogWarning("2. Use ForceTransportBinding → 'Force Then Start Host'");
|
|
Debug.LogWarning("3. Check if port becomes accessible");
|
|
}
|
|
|
|
[ContextMenu("Check Current Status")]
|
|
public void CheckCurrentStatus()
|
|
{
|
|
Debug.Log("=== PORT MONITOR STATUS ===");
|
|
|
|
bool isUnityServerRunning = _networkManager != null && _networkManager.IsServer;
|
|
bool isTestListenerRunning = _listenerRunning;
|
|
bool portAccessible = TestPortConnection();
|
|
|
|
Debug.Log("Unity Server Running: " + isUnityServerRunning);
|
|
Debug.Log("Test Listener Running: " + isTestListenerRunning);
|
|
Debug.Log("Port Accessible: " + portAccessible);
|
|
|
|
if (portAccessible)
|
|
{
|
|
Debug.Log("<color=green>✓ Port " + targetPort + " is accessible</color>");
|
|
}
|
|
else
|
|
{
|
|
Debug.LogError("<color=red>✗ Port " + targetPort + " is NOT accessible</color>");
|
|
Debug.LogError("Check:");
|
|
Debug.LogError("1. Is Unity Netcode actually running?");
|
|
Debug.LogError("2. Is there a firewall blocking after some time?");
|
|
Debug.LogError("3. Is router restarting port forwarding?");
|
|
}
|
|
|
|
Debug.Log("============================");
|
|
}
|
|
|
|
[ContextMenu("Generate Diagnosis Report")]
|
|
public void GenerateDiagnosisReport()
|
|
{
|
|
Debug.Log("=== PORT MONITOR DIAGNOSIS ===");
|
|
Debug.Log("");
|
|
Debug.Log("PROBLEM:");
|
|
Debug.Log("- PortListenerTest: WORKS (port accessible)");
|
|
Debug.Log("- After test ends: PORT INACCESSIBLE");
|
|
Debug.Log("- Unity Netcode: PORT INACCESSIBLE");
|
|
Debug.Log("");
|
|
Debug.Log("ANALYSIS:");
|
|
Debug.Log("1. Port forwarding: WORKING (proven by test)");
|
|
Debug.Log("2. Firewall: ALLOWING (proven by test)");
|
|
Debug.Log("3. Unity Netcode: NOT BINDING OR CRASHING");
|
|
Debug.Log("");
|
|
Debug.Log("POSSIBLE CAUSES:");
|
|
Debug.Log("1. Unity NetworkManager is not actually starting server");
|
|
Debug.Log("2. Player Prefab not assigned (server doesn't start)");
|
|
Debug.Log("3. Connection Approval rejecting all connections");
|
|
Debug.Log("4. NetworkConnectionHandler has error and stops server");
|
|
Debug.Log("5. Unity Netcode version bug");
|
|
Debug.Log("");
|
|
Debug.Log("CHECKLIST:");
|
|
Debug.Log("☐ NetworkManager → Player Prefab: ASSIGNED?");
|
|
Debug.Log("☐ NetworkManager → Connection Approval: CHECKED?");
|
|
Debug.Log("☐ NetworkConnectionHandler: IN SCENE?");
|
|
Debug.Log("☐ AutoHost: DISABLED or configured correctly?");
|
|
Debug.Log("☐ ForceTransportBinding: APPLIED?");
|
|
Debug.Log("");
|
|
Debug.Log("NEXT STEPS:");
|
|
Debug.Log("1. Add ForceTransportBinding to scene");
|
|
Debug.Log("2. Right-click → 'Force Then Start Host'");
|
|
Debug.Log("3. Watch Console for '✓ Server STARTED'");
|
|
Debug.Log("4. Immediately test on yougetsignal");
|
|
Debug.Log("5. If still closed: Check NetworkManager config");
|
|
Debug.Log("================================");
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
StopTestListener();
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
StopMonitoring();
|
|
}
|
|
}
|
|
}
|