using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Godot; public partial class GameManager : Node3D { [Export] public Control GameMenu { get; private set; } [Export] public QueueManager QueueManager { get; private set; } [Export] public double closeTickInterval = 1; [Export] public double farTickInterval = 10; [Export] public int maxFarThreads = 16; public Player MainPlayer { get; private set; } public static GameManager Singleton { get; private set; } public IGenerator Generator { get; private set; } public Universe GameUniverse { get; private set; } private double closeTickTimer = 0; private double farTickTimer = 0; public bool simulating = false; private readonly Dictionary spawnedObjects = []; private readonly ConcurrentQueue spawnQueue = []; public override void _Ready() { Singleton = this; Generator = new TestGenerator(); GameUniverse = Generator.GenerateUniverse(); Character character = new(GameUniverse.Sectors[50, 50, 50], new(0, 0, 0)); MainPlayer = character.InstantiatePlayer(); MainPlayer.GameMenu = GameMenu; spawnedObjects.Add(character, MainPlayer); CallDeferred("add_child", MainPlayer); SpawnClose(); } public override void _Process(double delta) { closeTickTimer += delta; farTickTimer += delta; if (closeTickTimer >= closeTickInterval) { SimulateClose(closeTickTimer); closeTickTimer = 0; } if (simulating) { farTickTimer = 0; } if (farTickTimer >= farTickInterval) { double taskFarTickTimer = farTickTimer; Task.Run(() => { SimulateFar(taskFarTickTimer); }); farTickTimer = 0; } } public Sector GetCurrentSector() { return MainPlayer.PlayerData.CurrentSector; } private void SimulateClose(double delta) { List neighbours = GetCurrentSector().GetNeighbouringSectors(); neighbours.ForEach(sector => { Task.Run(() => { sector.Simulate(delta); }); }); } private void SimulateFar(double delta) { simulating = true; List tasks = []; Sector[,,] sectors = GameUniverse.Sectors; int sizeX = sectors.GetLength(0); int countX = Mathf.Clamp(sizeX / maxFarThreads, 1, int.MaxValue); for (int x = 0; x < sizeX; x += countX) { double taskDelta = delta; int taskStartX = x; int taskCountX = countX; Task clusteredTask = Task.Run(() => { SimulateFarClustered(taskDelta, taskStartX, taskCountX); }); tasks.Add(clusteredTask); } Task.WaitAll([.. tasks]); simulating = false; } private void SimulateFarClustered(double delta, int startX, int countX) { Vector3I currentCoordinates = GetCurrentSector().Coordinates; Sector[,,] sectors = GameUniverse.Sectors; int sizeX = sectors.GetLength(0); int sizeY = sectors.GetLength(1); int sizeZ = sectors.GetLength(2); int startSkipX = Mathf.Clamp(currentCoordinates.X - 1, 0, sizeX); int startSkipY = Mathf.Clamp(currentCoordinates.Y - 1, 0, sizeY); int startSkipZ = Mathf.Clamp(currentCoordinates.Z - 1, 0, sizeZ); int endSkipX = Mathf.Clamp(currentCoordinates.X + 1, 0, sizeX); int endSkipY = Mathf.Clamp(currentCoordinates.Y + 1, 0, sizeY); int endSkipZ = Mathf.Clamp(currentCoordinates.Z + 1, 0, sizeZ); int endX = Mathf.Clamp(startX + countX, 0, sizeX); for (int x = startX; x < endX; x++) { QueueManager.LogQueue.Enqueue("Simulating: " + x); for (int y = 0; y < sizeY; y++) { Thread.Sleep(5); for (int z = 0; z < sizeZ; z++) { if (Helpers.IsBetweenInclusive(x, startSkipX, endSkipX)) { if (Helpers.IsBetweenInclusive(y, startSkipY, endSkipY)) { if (Helpers.IsBetweenInclusive(z, startSkipZ, endSkipZ)) { continue; } } } try { sectors[x, y, z].Simulate(delta); } catch (Exception e) { QueueManager.LogQueue.Enqueue("EXCEPTION: " + e.Message); } } } } } private void SpawnClose() { List neighbours = GetCurrentSector().GetNeighbouringSectors(); neighbours.ForEach(sector => sector.SpawnObjects()); } public void Spawn(GameObject gameObject) { spawnQueue.Enqueue(gameObject); CallDeferred(nameof(ProcessSpawnQueue)); } private void ProcessSpawnQueue() { spawnQueue.TryDequeue(out GameObject gameObject); if (gameObject == null) { return; } if (spawnedObjects.ContainsKey(gameObject)) { return; } Node3D instance = gameObject.Instantiate(GetCurrentSector()); spawnedObjects.Add(gameObject, instance); CallDeferred("add_child", instance); } public void Despawn(GameObject gameObject) { if (!spawnedObjects.ContainsKey(gameObject)) { return; } Node3D nodeToDespawn = spawnedObjects.GetValueOrDefault(gameObject); spawnedObjects.Remove(gameObject); CallDeferred("remove_child", nodeToDespawn); nodeToDespawn.CallDeferred("queue_free"); } public void ApplyOrigin() { Sector current = GetCurrentSector(); List nearby = current.GetNeighbouringSectors(); foreach (Sector sector in nearby) { foreach (GameObject gameObject in sector.GameObjects) { gameObject.UpdateSectorOffset(current); } } foreach (GameObject spawned in spawnedObjects.Keys) { if (!nearby.Contains(spawned.CurrentSector)) { Despawn(spawned); } } nearby.ForEach(sector => sector.SpawnObjects()); } }