using System; using System.Collections.Generic; using System.Threading.Tasks; namespace Sandbox; /// /// Creates a networked game lobby and assigns player prefabs to connected clients. /// [Title( "Network Manager" )] [Category( "Networking" )] [Icon( "electrical_services" )] public sealed class NetworkManager : Component, Component.INetworkListener { /// /// Create a server (if we're not joining one) /// [Property] public bool StartServer { get; set; } = true; /// /// The prefab to spawn for the player to control. /// [Property] public GameObject PlayerPrefab { get; set; } /// /// A list of points to choose from randomly to spawn the player in. If not set, we'll spawn at the /// location of the NetworkHelper object. /// [Property] public List SpawnPoints { get; set; } protected override async Task OnLoad() { if ( Scene.IsEditor ) return; if ( StartServer && !Networking.IsActive ) { LoadingScreen.Title = "Creating Lobby"; await Task.DelayRealtimeSeconds( 0.1f ); Networking.CreateLobby( new() ); } } /// /// A client is fully connected to the server. This is called on the host. /// public void OnActive( Connection channel ) { Log.Info( $"Player '{channel.DisplayName}' has joined the game" ); if ( !PlayerPrefab.IsValid() ) return; // // Find a spawn location for this player // var startLocation = FindSpawnLocation().WithScale( 1 ); // Spawn this object and make the client the owner var player = PlayerPrefab.Clone( startLocation, name: $"Player - {channel.DisplayName}" ); player.NetworkSpawn( channel ); var dedugan = player.Components.Get( true ); dedugan.SetupConnection( channel ); } /// /// Called when a client disconnects from the server. /// public void OnDisconnected( Connection channel ) { Dedugan.InternalPlayers.Remove( Dedugan.GetByID( channel.Id ) ); } /// /// Called when the connection is being torn down. /// public void OnBecomeInactive( Connection channel ) { // Optional: Handle any cleanup before player becomes fully inactive } /// /// Find the most appropriate place to respawn /// Transform FindSpawnLocation() { // // If they have spawn point set then use those // if ( SpawnPoints is not null && SpawnPoints.Count > 0 ) { return Random.Shared.FromList( SpawnPoints, default ).WorldTransform; } // // If we have any SpawnPoint components in the scene, then use those // var spawnPoints = Scene.GetAllComponents().ToArray(); if ( spawnPoints.Length > 0 ) { return Random.Shared.FromArray( spawnPoints ).WorldTransform; } // // Failing that, spawn where we are // return WorldTransform; } }