Refactor; Optimize simulation
This commit is contained in:
parent
8581cf6fb8
commit
0ef5652cea
16 changed files with 508 additions and 651 deletions
|
|
@ -11,6 +11,7 @@ public partial class GameManager : Node3D
|
|||
[Export] public QueueManager QueueManager { get; private set; }
|
||||
[Export] public Node3D SpaceRoot { get; private set; }
|
||||
|
||||
[Export] public double closeDistance = 50;
|
||||
[Export] public double closeTickInterval = 1;
|
||||
[Export] public double farTickInterval = 10;
|
||||
[Export] public int maxFarThreads = 16;
|
||||
|
|
@ -21,6 +22,7 @@ public partial class GameManager : Node3D
|
|||
public static Universe GameUniverse { get; private set; }
|
||||
public Player MainPlayer { get; private set; }
|
||||
public Dictionary<long, Player> Players { get; private set; } = [];
|
||||
public Dictionary<long, HashSet<GameObject>> PlayerSpawnedObjects { get; private set; } = [];
|
||||
|
||||
private double closeTickTimer = 0;
|
||||
private double farTickTimer = 0;
|
||||
|
|
@ -29,7 +31,7 @@ public partial class GameManager : Node3D
|
|||
public bool simulatingFar = false;
|
||||
public bool playerReady = false;
|
||||
|
||||
private readonly Dictionary<GameObject, Node3D> spawnedObjects = [];
|
||||
private readonly Dictionary<GameObject, Node3D> localSpawnedObjects = [];
|
||||
private readonly ConcurrentQueue<GameObject> spawnQueue = [];
|
||||
|
||||
public override void _EnterTree()
|
||||
|
|
@ -51,7 +53,6 @@ public partial class GameManager : Node3D
|
|||
private void OnPlayerReady()
|
||||
{
|
||||
playerReady = true;
|
||||
SpawnClose();
|
||||
}
|
||||
|
||||
public override void _ExitTree()
|
||||
|
|
@ -71,11 +72,12 @@ public partial class GameManager : Node3D
|
|||
if (closeTickTimer >= closeTickInterval && !simulatingClose)
|
||||
{
|
||||
SimulateClose(closeTickTimer);
|
||||
SyncClose();
|
||||
closeTickTimer = 0;
|
||||
}
|
||||
|
||||
farTickTimer += delta;
|
||||
if (farTickTimer >= farTickInterval && !simulatingFar)
|
||||
if (farTickTimer >= farTickInterval && !simulatingFar && QueueManager.SectorReassignQueue.IsEmpty)
|
||||
{
|
||||
double taskFarTickTimer = farTickTimer;
|
||||
Task.Run(() =>
|
||||
|
|
@ -102,123 +104,131 @@ public partial class GameManager : Node3D
|
|||
{
|
||||
simulatingClose = true;
|
||||
|
||||
List<Sector> sectorsClose = GetCurrentSector().GetNeighbouringSectors();
|
||||
|
||||
List<Task> tasks = [];
|
||||
sectorsClose.ForEach(sector =>
|
||||
{
|
||||
Task simulateTask = Task.Run(() =>
|
||||
{
|
||||
sector.Simulate(delta);
|
||||
});
|
||||
|
||||
tasks.Add(simulateTask);
|
||||
});
|
||||
Task.WaitAll([.. tasks]);
|
||||
HashSet<GameObject> objectsToSimulate = [];
|
||||
|
||||
foreach (KeyValuePair<long, Player> player in Players)
|
||||
{
|
||||
List<Sector> playerSectors = player.Value.PlayerData.CurrentSector.GetNeighbouringSectors();
|
||||
Character character = player.Value.PlayerData;
|
||||
|
||||
List<Sector> playerSectors = character.CurrentSector.GetNeighbouringSectors();
|
||||
playerSectors.ForEach(sector =>
|
||||
{
|
||||
if (player.Key != 1)
|
||||
sector.GameObjects.ForEach(gameObject =>
|
||||
{
|
||||
sector.NetworkSync(player.Key);
|
||||
}
|
||||
double distance = character.GetDistanceToObject(gameObject);
|
||||
if (distance <= closeDistance)
|
||||
{
|
||||
objectsToSimulate.Add(gameObject);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
foreach (GameObject gameObject in objectsToSimulate)
|
||||
{
|
||||
gameObject.Simulate(delta);
|
||||
}
|
||||
|
||||
simulatingClose = false;
|
||||
}
|
||||
|
||||
private void SyncClose()
|
||||
{
|
||||
foreach (KeyValuePair<long, Player> player in Players)
|
||||
{
|
||||
bool isHostPlayer = player.Key == 1;
|
||||
long playerKey = player.Key;
|
||||
Character character = player.Value.PlayerData;
|
||||
|
||||
PlayerSpawnedObjects.TryGetValue(playerKey, out HashSet<GameObject> playerSpawnedObjects);
|
||||
playerSpawnedObjects ??= [];
|
||||
HashSet<GameObject> playerNewSpawnedObjects = [];
|
||||
|
||||
List<Sector> playerSectors = character.CurrentSector.GetNeighbouringSectors();
|
||||
|
||||
// Spawn/Send object
|
||||
playerSectors.ForEach(sector =>
|
||||
{
|
||||
sector.GameObjects.ForEach(gameObject =>
|
||||
{
|
||||
double distance = character.GetDistanceToObject(gameObject);
|
||||
if (distance <= closeDistance)
|
||||
{
|
||||
if (isHostPlayer)
|
||||
Spawn(gameObject);
|
||||
else
|
||||
gameObject.NetworkWrite(playerKey, !playerSpawnedObjects.Contains(gameObject));
|
||||
|
||||
playerNewSpawnedObjects.Add(gameObject);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Despawn object
|
||||
foreach (GameObject gameObject in playerSpawnedObjects)
|
||||
{
|
||||
if (!playerNewSpawnedObjects.Contains(gameObject))
|
||||
{
|
||||
if (isHostPlayer)
|
||||
Despawn(gameObject);
|
||||
else
|
||||
RPCNode.Instance.RpcId(playerKey, nameof(RPCNode.RpcDespawnGameObject), gameObject.UUID.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
PlayerSpawnedObjects[playerKey] = playerNewSpawnedObjects;
|
||||
}
|
||||
}
|
||||
|
||||
private void SimulateFar(double delta)
|
||||
{
|
||||
simulatingFar = true;
|
||||
|
||||
List<Task> 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)
|
||||
HashSet<GameObject> allSpawnedObjects = [];
|
||||
foreach (HashSet<GameObject> item in PlayerSpawnedObjects.Values)
|
||||
{
|
||||
double taskDelta = delta;
|
||||
int taskStartX = x;
|
||||
int taskCountX = countX;
|
||||
|
||||
Task clusteredTask = Task.Run(() =>
|
||||
foreach (GameObject gameObject in item)
|
||||
{
|
||||
SimulateFarClustered(taskDelta, taskStartX, taskCountX);
|
||||
});
|
||||
|
||||
tasks.Add(clusteredTask);
|
||||
allSpawnedObjects.Add(gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
Task.WaitAll([.. tasks]);
|
||||
int sizeX = sectors.GetLength(0);
|
||||
|
||||
double taskDelta = delta;
|
||||
Parallel.For(0, sizeX, x =>
|
||||
{
|
||||
SimulateFarClustered(allSpawnedObjects, taskDelta, x);
|
||||
});
|
||||
|
||||
simulatingFar = false;
|
||||
}
|
||||
|
||||
private void SimulateFarClustered(double delta, int startX, int countX)
|
||||
private static void SimulateFarClustered(HashSet<GameObject> ignoreObjects, double delta, int x)
|
||||
{
|
||||
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++)
|
||||
for (int y = 0; y < sizeY; y++)
|
||||
{
|
||||
for (int y = 0; y < sizeY; y++)
|
||||
for (int z = 0; z < sizeZ; z++)
|
||||
{
|
||||
Thread.Sleep(5);
|
||||
for (int z = 0; z < sizeZ; z++)
|
||||
try
|
||||
{
|
||||
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);
|
||||
}
|
||||
sectors[x, y, z].Simulate(delta, ignoreObjects);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
QueueManager.LogQueue.Enqueue("EXCEPTION: " + e.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SpawnClose()
|
||||
{
|
||||
List<Sector> neighbours = GetCurrentSector().GetNeighbouringSectors();
|
||||
|
||||
neighbours.ForEach(sector => sector.SpawnObjects());
|
||||
}
|
||||
|
||||
public Player SpawnPlayer(Character character, long networkId, bool isMainPlayer = false)
|
||||
{
|
||||
Player player = character.InstantiatePlayer();
|
||||
|
|
@ -238,8 +248,10 @@ public partial class GameManager : Node3D
|
|||
return player;
|
||||
}
|
||||
|
||||
public void DespawnPlayer(Player player)
|
||||
public void DespawnPlayer(Player player, long id)
|
||||
{
|
||||
Players.Remove(id);
|
||||
|
||||
SpaceRoot.CallDeferred("remove_child", player);
|
||||
player.CallDeferred("queue_free");
|
||||
}
|
||||
|
|
@ -257,31 +269,43 @@ public partial class GameManager : Node3D
|
|||
{
|
||||
return;
|
||||
}
|
||||
if (spawnedObjects.ContainsKey(gameObject))
|
||||
if (localSpawnedObjects.ContainsKey(gameObject))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Node3D instance = gameObject.Instantiate(GetCurrentSector());
|
||||
spawnedObjects.Add(gameObject, instance);
|
||||
localSpawnedObjects.Add(gameObject, instance);
|
||||
|
||||
SpaceRoot.CallDeferred("add_child", instance);
|
||||
}
|
||||
|
||||
public void Despawn(GameObject gameObject)
|
||||
{
|
||||
if (!spawnedObjects.ContainsKey(gameObject))
|
||||
if (!localSpawnedObjects.ContainsKey(gameObject))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Node3D nodeToDespawn = spawnedObjects.GetValueOrDefault(gameObject);
|
||||
spawnedObjects.Remove(gameObject);
|
||||
Node3D nodeToDespawn = localSpawnedObjects.GetValueOrDefault(gameObject);
|
||||
localSpawnedObjects.Remove(gameObject);
|
||||
|
||||
SpaceRoot.CallDeferred("remove_child", nodeToDespawn);
|
||||
nodeToDespawn.CallDeferred("queue_free");
|
||||
}
|
||||
|
||||
public void Despawn(Guid uuid)
|
||||
{
|
||||
foreach (GameObject gameObject in localSpawnedObjects.Keys)
|
||||
{
|
||||
if (gameObject.UUID == uuid)
|
||||
{
|
||||
Despawn(gameObject);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ApplyOrigin()
|
||||
{
|
||||
Sector current = GetCurrentSector();
|
||||
|
|
@ -292,24 +316,14 @@ public partial class GameManager : Node3D
|
|||
player.Value.PlayerData.UpdateNodePosition();
|
||||
}
|
||||
|
||||
List<Sector> nearby = current.GetNeighbouringSectors();
|
||||
foreach (KeyValuePair<GameObject, Node3D> spawned in spawnedObjects)
|
||||
foreach (KeyValuePair<GameObject, Node3D> spawned in localSpawnedObjects)
|
||||
{
|
||||
GameObject gameObject = spawned.Key;
|
||||
Node3D node = spawned.Value;
|
||||
|
||||
if (!nearby.Contains(gameObject.CurrentSector))
|
||||
{
|
||||
Despawn(gameObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
gameObject.UpdateSectorOffset(current);
|
||||
node._Process(0);
|
||||
}
|
||||
gameObject.UpdateSectorOffset(current);
|
||||
node._Process(0);
|
||||
}
|
||||
|
||||
nearby.ForEach(sector => sector.SpawnObjects());
|
||||
}
|
||||
|
||||
public void SendUniverseToClient(long id)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue