190 lines
8.2 KiB
C#
190 lines
8.2 KiB
C#
// Assets/_Project/Scripts/Net/NetworkBootstrap.cs
|
|
using System;
|
|
using Unity.Netcode;
|
|
using Unity.Netcode.Transports.UTP;
|
|
using UnityEngine;
|
|
|
|
namespace TD.Net
|
|
{
|
|
/// <summary>
|
|
/// Static facade for starting a host, joining a host, and disconnecting.
|
|
/// Wraps NGO's <see cref="NetworkManager"/> and <see cref="UnityTransport"/>
|
|
/// so the rest of the codebase (lobby UI, main menu UI) doesn't talk to
|
|
/// NGO directly — and so the transport implementation can be swapped
|
|
/// without touching lobby code.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para><b>Why a static facade.</b> Today this is a thin wrapper over
|
|
/// <see cref="UnityTransport"/>. When Steam integration lands
|
|
/// (see <c>Project_Roadmap.md</c> §1.7-Future Steam Lobby Migration), this
|
|
/// class becomes the seam: it grows into an <c>IConnectionProvider</c>
|
|
/// abstraction with two implementations (<c>DirectIpConnectionProvider</c>
|
|
/// for LAN / DRM-free distribution, <c>SteamConnectionProvider</c> for
|
|
/// Steam friend-invite + lobby-browser flows). The lobby UI never changes;
|
|
/// only this class does.</para>
|
|
///
|
|
/// <para><b>Default port.</b> NGO's NetworkConfig holds the canonical
|
|
/// transport settings (port, listen address). <see cref="DefaultPort"/>
|
|
/// is just the value we use when the user doesn't override it in the UI.</para>
|
|
///
|
|
/// <para><b>Scene management.</b> NGO's <c>SceneManager.LoadScene</c> is
|
|
/// called by the host AFTER <see cref="StartHost"/> succeeds — see
|
|
/// <see cref="LoadSceneAsHost"/>. The lobby and match scene names are
|
|
/// canonicalized in <see cref="SceneNames"/>.</para>
|
|
/// </remarks>
|
|
public static class NetworkBootstrap
|
|
{
|
|
// ----- Configuration ---------------------------------------------
|
|
|
|
/// <summary>Default port used by Host / Join when the UI doesn't override.</summary>
|
|
public const ushort DefaultPort = 7777;
|
|
|
|
/// <summary>Default listen address for the host. 0.0.0.0 listens on all interfaces.</summary>
|
|
public const string DefaultListenAddress = "0.0.0.0";
|
|
|
|
/// <summary>Default address clients use when no IP is entered (loopback for solo testing).</summary>
|
|
public const string DefaultConnectAddress = "127.0.0.1";
|
|
|
|
// ----- Public API ------------------------------------------------
|
|
|
|
/// <summary>True if a host or client connection is currently active.</summary>
|
|
public static bool IsConnected =>
|
|
NetworkManager.Singleton != null
|
|
&& (NetworkManager.Singleton.IsHost
|
|
|| NetworkManager.Singleton.IsServer
|
|
|| NetworkManager.Singleton.IsClient);
|
|
|
|
/// <summary>True if the local peer is the host (server + client in one process).</summary>
|
|
public static bool IsHost =>
|
|
NetworkManager.Singleton != null && NetworkManager.Singleton.IsHost;
|
|
|
|
/// <summary>
|
|
/// Starts a host on <paramref name="listenAddress"/>:<paramref name="port"/>.
|
|
/// Returns true on success. Logs and returns false if NetworkManager
|
|
/// is missing or already running.
|
|
/// </summary>
|
|
public static bool StartHost(ushort port = DefaultPort, string listenAddress = DefaultListenAddress)
|
|
{
|
|
var nm = NetworkManager.Singleton;
|
|
if (nm == null)
|
|
{
|
|
Debug.LogError("[NetworkBootstrap] NetworkManager.Singleton is null. " +
|
|
"Make sure a NetworkManager exists in the active scene.");
|
|
return false;
|
|
}
|
|
if (IsConnected)
|
|
{
|
|
Debug.LogWarning("[NetworkBootstrap] StartHost called while already connected. Ignored.");
|
|
return false;
|
|
}
|
|
|
|
ConfigureTransport(listenAddress, port);
|
|
|
|
bool ok = nm.StartHost();
|
|
if (!ok) Debug.LogError("[NetworkBootstrap] NetworkManager.StartHost() returned false.");
|
|
return ok;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Starts a client and connects to <paramref name="address"/>:<paramref name="port"/>.
|
|
/// Returns true on success. Logs and returns false if NetworkManager
|
|
/// is missing or already running.
|
|
/// </summary>
|
|
public static bool StartClient(string address, ushort port = DefaultPort)
|
|
{
|
|
var nm = NetworkManager.Singleton;
|
|
if (nm == null)
|
|
{
|
|
Debug.LogError("[NetworkBootstrap] NetworkManager.Singleton is null. " +
|
|
"Make sure a NetworkManager exists in the active scene.");
|
|
return false;
|
|
}
|
|
if (IsConnected)
|
|
{
|
|
Debug.LogWarning("[NetworkBootstrap] StartClient called while already connected. Ignored.");
|
|
return false;
|
|
}
|
|
if (string.IsNullOrWhiteSpace(address))
|
|
address = DefaultConnectAddress;
|
|
|
|
ConfigureTransport(address, port);
|
|
|
|
bool ok = nm.StartClient();
|
|
if (!ok) Debug.LogError("[NetworkBootstrap] NetworkManager.StartClient() returned false.");
|
|
return ok;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Shuts down the current host or client connection. Safe to call when
|
|
/// already disconnected.
|
|
/// </summary>
|
|
public static void Disconnect()
|
|
{
|
|
var nm = NetworkManager.Singleton;
|
|
if (nm == null) return;
|
|
if (!IsConnected) return;
|
|
|
|
nm.Shutdown();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Server-only helper that transitions every peer to <paramref name="sceneName"/>
|
|
/// via NGO's networked scene manager. Logs and no-ops if not the server,
|
|
/// if NGO scene management is disabled, or if the scene name is invalid.
|
|
/// </summary>
|
|
public static void LoadSceneAsHost(string sceneName)
|
|
{
|
|
var nm = NetworkManager.Singleton;
|
|
if (nm == null) { Debug.LogError("[NetworkBootstrap] NetworkManager null."); return; }
|
|
if (!nm.IsServer)
|
|
{
|
|
Debug.LogWarning($"[NetworkBootstrap] LoadSceneAsHost('{sceneName}') called on a client. Ignored.");
|
|
return;
|
|
}
|
|
if (nm.SceneManager == null)
|
|
{
|
|
Debug.LogError("[NetworkBootstrap] NetworkManager.SceneManager is null — " +
|
|
"Enable Scene Management on the NetworkManager.");
|
|
return;
|
|
}
|
|
nm.SceneManager.LoadScene(sceneName, UnityEngine.SceneManagement.LoadSceneMode.Single);
|
|
}
|
|
|
|
// ----- Internals --------------------------------------------------
|
|
|
|
// Writes connection data into the UnityTransport component attached to
|
|
// the NetworkManager. Doesn't allocate; reuses the transport instance
|
|
// that's already in the scene.
|
|
//
|
|
// FUTURE STEAM MIGRATION: when SteamConnectionProvider lands, this
|
|
// method's body is replaced (or split) to configure the appropriate
|
|
// transport. The public API above stays as-is so call sites in the
|
|
// lobby UI don't change.
|
|
private static void ConfigureTransport(string address, ushort port)
|
|
{
|
|
var nm = NetworkManager.Singleton;
|
|
var transport = nm.NetworkConfig?.NetworkTransport as UnityTransport;
|
|
if (transport == null)
|
|
{
|
|
Debug.LogError("[NetworkBootstrap] NetworkManager's transport is not UnityTransport. " +
|
|
"If you switched transports (e.g. Steam), update NetworkBootstrap " +
|
|
"to configure the new one.");
|
|
return;
|
|
}
|
|
transport.SetConnectionData(address, port, DefaultListenAddress);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Canonical scene names used by scene transitions.
|
|
/// Centralized so renames or additions touch one place.
|
|
/// </summary>
|
|
public static class SceneNames
|
|
{
|
|
public const string MainMenu = "MainMenu";
|
|
public const string Lobby = "Lobby";
|
|
|
|
/// <summary>The gameplay scene (currently "Main" — the original prototype scene).</summary>
|
|
public const string Match = "Main";
|
|
}
|
|
}
|