GameManager.cs
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using Clover.Player;
using Clover.Ui;

namespace Clover;

public class GameManager : Component, Component.INetworkListener, ISceneStartup
{
	public static GameManager Instance;

	[Property] public GameObject PlayerPrefab { get; set; }

	protected override void OnAwake()
	{
		base.OnAwake();
		Instance = this;
	}

	protected override void OnDestroy()
	{
		base.OnDestroy();
		Instance = null;
	}

	protected override void OnStart()
	{
		_ = Bootstrap();
	}

	private async Task Bootstrap()
	{
		if ( IsProxy ) return;

		Log.Info( "GameManager is booting up" );

		await WorldManager.Instance.LoadWorld( WorldManager.Instance.DefaultWorldData );

		/*if ( !_spawnQueue.Contains( Connection.Local ) )
		{
			OnConnected( Connection.Local );
		}*/

		Log.Info( "GameManager has booted up" );
	}

	public static JsonSerializerOptions JsonOptions = new()
	{
		WriteIndented = true,
		IncludeFields = true,
		Converters = { new JsonStringEnumConverter() },
		DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
	};

	private TimeSince _lastSave;

	public void SaveTimer()
	{
		if ( _lastSave < 60f )
		{
			return;
		}

		_lastSave = 0f;

		if ( !AutoSave ) return;

		if ( Networking.IsHost )
		{
			foreach ( var world in WorldManager.Instance.Worlds )
			{
				world.Value.Save();
			}
		}

		PlayerCharacter.Local?.Save();
	}

	[ConVar( "clover_autosave" )] public static bool AutoSave { get; set; } = true;

	protected override void OnFixedUpdate()
	{
		base.OnFixedUpdate();
		SaveTimer();
		SpawnPlayers();
	}

	private void SpawnPlayers()
	{
		if ( IsProxy ) return;

		foreach ( var channel in _spawnQueue.ToList() )
		{
			SpawnPlayer( channel );
		}
	}

	private readonly List<Connection> _spawnQueue = new();

	public void OnConnected( Connection channel )
	{
		Log.Info( $"Player '{channel.DisplayName}' has joined the game" );
		// _spawnQueue.Add( channel );
	}

	public void OnDisconnected( Connection channel )
	{
		Log.Info( $"Player '{channel.DisplayName}' has left the game" );
	}

	public void OnBecameHost( Connection channel )
	{
		Log.Info( $"Player '{channel.DisplayName}' has become the host" );
	}

	public static void LoadRealm()
	{
		Game.ActiveScene.LoadFromFile( "scenes/clover.scene" );
	}

	public void SpawnPlayer( Connection channel )
	{
		if ( Scene.GetAllComponents<PlayerCharacter>().Any( x => x.Network.Owner == channel ) )
		{
			Log.Warning( $"Player '{channel.DisplayName}' already spawned" );
			_spawnQueue.Remove( channel );
			return;
		}

		if ( !PlayerPrefab.IsValid() )
			return;

		// Spawn this object and make the client the owner
		var player = PlayerPrefab.Clone( new Transform(), name: $"Player - {channel.DisplayName}" );
		player.NetworkSpawn( channel );

		// Notify any listeners that a player has spawned
		Scene.RunEvent<IPlayerSpawned>( x => x.OnPlayerSpawned( player.GetComponent<PlayerCharacter>() ) );

		var island = WorldManager.Island;

		if ( island.IsValid() )
		{
			var spawnPoint = island.GetEntrance( "spawn" );
			if ( spawnPoint.IsValid() )
			{
				player.GetComponent<PlayerCharacter>().SetLayer( island.Layer );
				player.GetComponent<PlayerCharacter>().TeleportTo( spawnPoint.EntranceId );
			}
			else
			{
				Log.Error( "No spawn point found in the world" );
			}
		}
		else
		{
			Log.Error( "No active world found" );
		}

		_spawnQueue.Remove( channel );
	}

	[Rpc.Owner]
	public void RequestSpawn( string playerId )
	{
		var caller = Rpc.Caller;
		Log.Info( $"Player '{caller.DisplayName}' has requested to spawn player '{playerId}'" );
		_spawnQueue.Add( caller );
	}

	public void OnHostPreInitialize( SceneFile scene )
	{
		Log.Info( "BOOT" );
		PlayerCharacter.SpawnPlayerId = null;
	}

	public void OnHostInitialize()
	{
		Log.Info( "BOOT" );
		PlayerCharacter.SpawnPlayerId = null;
	}

	public void OnClientInitialize()
	{
		Log.Info( "BOOT" );
		PlayerCharacter.SpawnPlayerId = null;
	}
}