Merge pull request 'Refactor and optimization' (#26) from features/jant/20-optimize-simulation into main
Reviewed-on: #26
This commit is contained in:
commit
bd0aeeb159
16 changed files with 508 additions and 651 deletions
6
global.json
Normal file
6
global.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"sdk": {
|
||||
"version": "8.0.123",
|
||||
"rollForward": "disable"
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -1,332 +1,295 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Godot;
|
||||
|
||||
public abstract class GameObject
|
||||
{
|
||||
[Flags]
|
||||
public enum DirtyFlags : ulong
|
||||
{
|
||||
None = 0,
|
||||
UUID = 1UL << 0,
|
||||
Sector = 1UL << 1,
|
||||
LocalCoordinates = 1UL << 2,
|
||||
Rotation = 1UL << 3,
|
||||
Velocity = 1UL << 4,
|
||||
AngularVelocity = 1UL << 5
|
||||
}
|
||||
public DirtyFlags DirtyBits { get; protected set; } = DirtyFlags.None;
|
||||
[Flags]
|
||||
public enum DirtyFlags : ulong
|
||||
{
|
||||
None = 0,
|
||||
UUID = 1UL << 0,
|
||||
Sector = 1UL << 1,
|
||||
LocalCoordinates = 1UL << 2,
|
||||
Rotation = 1UL << 3,
|
||||
Velocity = 1UL << 4,
|
||||
AngularVelocity = 1UL << 5
|
||||
}
|
||||
public DirtyFlags DirtyBits { get; protected set; } = DirtyFlags.None;
|
||||
|
||||
protected Guid _uuid;
|
||||
public Guid UUID
|
||||
{
|
||||
get => _uuid;
|
||||
set
|
||||
{
|
||||
if (_uuid != value)
|
||||
{
|
||||
_uuid = value;
|
||||
DirtyBits |= DirtyFlags.UUID;
|
||||
}
|
||||
}
|
||||
}
|
||||
protected Guid _uuid;
|
||||
public Guid UUID
|
||||
{
|
||||
get => _uuid;
|
||||
set
|
||||
{
|
||||
if (_uuid != value)
|
||||
{
|
||||
_uuid = value;
|
||||
DirtyBits |= DirtyFlags.UUID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Vector3I _currentSectorCoordinates;
|
||||
protected Sector _currentSector;
|
||||
public Sector CurrentSector
|
||||
{
|
||||
get => _currentSector;
|
||||
protected set
|
||||
{
|
||||
if (_currentSector != value)
|
||||
{
|
||||
_currentSector = value;
|
||||
_currentSectorCoordinates = value.Coordinates;
|
||||
DirtyBits |= DirtyFlags.Sector;
|
||||
}
|
||||
}
|
||||
}
|
||||
protected Vector3I _currentSectorCoordinates;
|
||||
protected Sector _currentSector;
|
||||
public Sector CurrentSector
|
||||
{
|
||||
get => _currentSector;
|
||||
protected set
|
||||
{
|
||||
if (_currentSector != value)
|
||||
{
|
||||
_currentSector = value;
|
||||
_currentSectorCoordinates = value.Coordinates;
|
||||
DirtyBits |= DirtyFlags.Sector;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Vector3 _localCoordinates;
|
||||
public Vector3 LocalCoordinates
|
||||
{
|
||||
get => _localCoordinates;
|
||||
protected set
|
||||
{
|
||||
if (_localCoordinates != value)
|
||||
{
|
||||
_localCoordinates = value;
|
||||
DirtyBits |= DirtyFlags.LocalCoordinates;
|
||||
}
|
||||
}
|
||||
}
|
||||
protected Vector3 _localCoordinates;
|
||||
public Vector3 LocalCoordinates
|
||||
{
|
||||
get => _localCoordinates;
|
||||
protected set
|
||||
{
|
||||
if (_localCoordinates != value)
|
||||
{
|
||||
_localCoordinates = value;
|
||||
DirtyBits |= DirtyFlags.LocalCoordinates;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Vector3 _rotation;
|
||||
public Vector3 Rotation
|
||||
{
|
||||
get => _rotation;
|
||||
protected set
|
||||
{
|
||||
if (_rotation != value)
|
||||
{
|
||||
_rotation = value;
|
||||
DirtyBits |= DirtyFlags.Rotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
protected Vector3 _rotation;
|
||||
public Vector3 Rotation
|
||||
{
|
||||
get => _rotation;
|
||||
protected set
|
||||
{
|
||||
if (_rotation != value)
|
||||
{
|
||||
_rotation = value;
|
||||
DirtyBits |= DirtyFlags.Rotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Vector3 _velocity;
|
||||
public Vector3 Velocity
|
||||
{
|
||||
get => _velocity;
|
||||
protected set
|
||||
{
|
||||
if (_velocity != value)
|
||||
{
|
||||
_velocity = value;
|
||||
DirtyBits |= DirtyFlags.Velocity;
|
||||
}
|
||||
}
|
||||
}
|
||||
protected Vector3 _velocity;
|
||||
public Vector3 Velocity
|
||||
{
|
||||
get => _velocity;
|
||||
protected set
|
||||
{
|
||||
if (_velocity != value)
|
||||
{
|
||||
_velocity = value;
|
||||
DirtyBits |= DirtyFlags.Velocity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Vector3 _angularVelocity;
|
||||
public Vector3 AngularVelocity
|
||||
{
|
||||
get => _angularVelocity;
|
||||
protected set
|
||||
{
|
||||
if (_angularVelocity != value)
|
||||
{
|
||||
_angularVelocity = value;
|
||||
DirtyBits |= DirtyFlags.AngularVelocity;
|
||||
}
|
||||
}
|
||||
}
|
||||
protected Vector3 _angularVelocity;
|
||||
public Vector3 AngularVelocity
|
||||
{
|
||||
get => _angularVelocity;
|
||||
protected set
|
||||
{
|
||||
if (_angularVelocity != value)
|
||||
{
|
||||
_angularVelocity = value;
|
||||
DirtyBits |= DirtyFlags.AngularVelocity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Vector3Dec GlobalCoordinates { get; protected set; }
|
||||
public Vector3 SectorOffset { get; protected set; }
|
||||
public Vector3 SectorOffset { get; protected set; }
|
||||
|
||||
protected bool reassigning = false;
|
||||
protected bool reassigning = false;
|
||||
|
||||
public GameObject(Sector sector, Vector3 localCoordinates)
|
||||
{
|
||||
UUID = Guid.NewGuid();
|
||||
public GameObject(Sector sector, Vector3 localCoordinates)
|
||||
{
|
||||
UUID = Guid.NewGuid();
|
||||
|
||||
CurrentSector = sector;
|
||||
LocalCoordinates = localCoordinates;
|
||||
CurrentSector = sector;
|
||||
LocalCoordinates = localCoordinates;
|
||||
|
||||
Velocity = new(0, 0, 1);
|
||||
AngularVelocity = Vector3.Zero;
|
||||
Rotation = Vector3.Zero;
|
||||
Velocity = new(0, 0, 1);
|
||||
AngularVelocity = Vector3.Zero;
|
||||
Rotation = Vector3.Zero;
|
||||
}
|
||||
|
||||
GlobalCoordinates = CalculateGlobalCoordinates(sector.GlobalCenterCoordinates, localCoordinates);
|
||||
}
|
||||
public void ApplyVelocity(double delta)
|
||||
{
|
||||
SetCoordinates(LocalCoordinates + Velocity * delta);
|
||||
Rotation += AngularVelocity * delta;
|
||||
}
|
||||
|
||||
public GameObject(Vector3Dec coordinates)
|
||||
{
|
||||
GlobalCoordinates = coordinates;
|
||||
UpdateSector();
|
||||
}
|
||||
public bool IsInCurrentSector()
|
||||
{
|
||||
return Helpers.IsInsideArea(GameManager.Generator.GetSectorSize() / 2, LocalCoordinates);
|
||||
}
|
||||
|
||||
public GameObject(decimal x, decimal y, decimal z)
|
||||
{
|
||||
GlobalCoordinates = new(x, y, z);
|
||||
UpdateSector();
|
||||
}
|
||||
public void UpdateSector()
|
||||
{
|
||||
if (!GameManager.GameUniverse.IsInside(CurrentSector.Coordinates, LocalCoordinates))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
public Vector3Dec CalculateGlobalCoordinates(Vector3Dec sectorCenter, Vector3 local)
|
||||
{
|
||||
return new
|
||||
(
|
||||
sectorCenter.X + (decimal)local.X,
|
||||
sectorCenter.Y + (decimal)local.Y,
|
||||
sectorCenter.Z + (decimal)local.Z
|
||||
);
|
||||
}
|
||||
Vector3 sectorSize = GameManager.Generator.GetSectorSize() / 2;
|
||||
|
||||
public void ApplyVelocity(double delta)
|
||||
{
|
||||
SetCoordinatesFromLocal(LocalCoordinates + Velocity * delta);
|
||||
Rotation += AngularVelocity * delta;
|
||||
}
|
||||
bool? x = null;
|
||||
bool? y = null;
|
||||
bool? z = null;
|
||||
|
||||
public bool IsInCurrentSector()
|
||||
{
|
||||
return Helpers.IsInsideArea(CurrentSector.Size / 2, LocalCoordinates);
|
||||
}
|
||||
if (LocalCoordinates.X > sectorSize.X) x = true;
|
||||
else if (LocalCoordinates.X < -sectorSize.X) x = false;
|
||||
|
||||
public void UpdateSector()
|
||||
{
|
||||
List<Sector> neighbours = CurrentSector.GetNeighbouringSectors();
|
||||
foreach (Sector sector in neighbours)
|
||||
{
|
||||
if (sector.IsObjectInSector(this))
|
||||
{
|
||||
reassigning = true;
|
||||
sector.AssignObject(this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (LocalCoordinates.Y > sectorSize.Y) y = true;
|
||||
else if (LocalCoordinates.Y < -sectorSize.Y) y = false;
|
||||
|
||||
if (!GameManager.GameUniverse.IsInside(CurrentSector.Coordinates, LocalCoordinates))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (LocalCoordinates.Z > sectorSize.Z) z = true;
|
||||
else if (LocalCoordinates.Z < -sectorSize.Z) z = false;
|
||||
|
||||
foreach (Sector sector in GameManager.GameUniverse.Sectors)
|
||||
{
|
||||
if (sector.IsObjectInSector(this))
|
||||
{
|
||||
reassigning = true;
|
||||
sector.AssignObject(this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
Sector sector = GameManager.GameUniverse.GetNeighbouringSector(CurrentSector, x, y, z);
|
||||
if (sector != null)
|
||||
{
|
||||
reassigning = true;
|
||||
sector.AssignObject(this);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void AssignSector(Sector sector)
|
||||
{
|
||||
CurrentSector = sector;
|
||||
ResetLocalCoordinates();
|
||||
public virtual void AssignSector(Sector sector)
|
||||
{
|
||||
Vector3 sectorOffset = GetSectorOffset(sector);
|
||||
|
||||
UpdateSectorOffsetToMainPlayer();
|
||||
CurrentSector = sector;
|
||||
LocalCoordinates += sectorOffset;
|
||||
|
||||
List<Sector> neighbours = GameManager.Instance.GetCurrentSector().GetNeighbouringSectors();
|
||||
UpdateSectorOffsetToMainPlayer();
|
||||
|
||||
if (neighbours.Contains(sector))
|
||||
{
|
||||
GameManager.Instance.Spawn(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
GameManager.Instance.Despawn(this);
|
||||
}
|
||||
reassigning = false;
|
||||
}
|
||||
|
||||
reassigning = false;
|
||||
}
|
||||
public Vector3 GetSectorOffset(Sector sector)
|
||||
{
|
||||
Vector3I relative = CurrentSector.Coordinates - sector.Coordinates;
|
||||
return relative * GameManager.Generator.GetSectorSize();
|
||||
}
|
||||
|
||||
public Vector3 GetSectorOffset(Sector sector)
|
||||
{
|
||||
Vector3Dec relative = CurrentSector.GlobalCenterCoordinates - sector.GlobalCenterCoordinates;
|
||||
return relative.ToVector3();
|
||||
}
|
||||
public void UpdateSectorOffset(Sector sector)
|
||||
{
|
||||
SectorOffset = GetSectorOffset(sector);
|
||||
}
|
||||
|
||||
public void UpdateSectorOffset(Sector sector)
|
||||
{
|
||||
SectorOffset = GetSectorOffset(sector);
|
||||
}
|
||||
public void UpdateSectorOffsetToMainPlayer()
|
||||
{
|
||||
UpdateSectorOffset(GameManager.Instance.GetCurrentSector());
|
||||
}
|
||||
|
||||
public void UpdateSectorOffsetToMainPlayer()
|
||||
{
|
||||
UpdateSectorOffset(GameManager.Instance.GetCurrentSector());
|
||||
}
|
||||
public void SetCoordinates(Vector3 localCoordinates)
|
||||
{
|
||||
LocalCoordinates = localCoordinates;
|
||||
|
||||
public void SetCoordinatesFromGlobal(Vector3Dec globalCoordinates)
|
||||
{
|
||||
GlobalCoordinates = globalCoordinates;
|
||||
LocalCoordinates = (globalCoordinates - CurrentSector.GlobalCenterCoordinates).ToVector3();
|
||||
UpdateNodePosition();
|
||||
}
|
||||
|
||||
UpdateNodePosition();
|
||||
}
|
||||
public double GetDistanceToObject(GameObject gameObject)
|
||||
{
|
||||
Sector sector = gameObject.CurrentSector;
|
||||
Vector3 sectorOffset = GetSectorOffset(sector);
|
||||
|
||||
public void SetCoordinatesFromLocal(Vector3 localCoordinates)
|
||||
{
|
||||
LocalCoordinates = localCoordinates;
|
||||
GlobalCoordinates = Vector3Dec.FromVector3(localCoordinates) + CurrentSector.GlobalCenterCoordinates;
|
||||
Vector3 position1 = LocalCoordinates;
|
||||
Vector3 position2 = gameObject.LocalCoordinates - sectorOffset;
|
||||
|
||||
UpdateNodePosition();
|
||||
}
|
||||
return Helpers.GetDistance(position1, position2);
|
||||
}
|
||||
|
||||
public void ResetLocalCoordinates()
|
||||
{
|
||||
SetCoordinatesFromGlobal(GlobalCoordinates);
|
||||
}
|
||||
public virtual void UpdateNodePosition() { }
|
||||
|
||||
public virtual void UpdateNodePosition() { }
|
||||
public virtual void Simulate(double delta)
|
||||
{
|
||||
ApplyVelocity(delta);
|
||||
|
||||
public virtual void Simulate(double delta)
|
||||
{
|
||||
ApplyVelocity(delta);
|
||||
if (!reassigning && !IsInCurrentSector())
|
||||
{
|
||||
UpdateSector();
|
||||
}
|
||||
}
|
||||
|
||||
if (!reassigning && !IsInCurrentSector())
|
||||
{
|
||||
UpdateSector();
|
||||
}
|
||||
}
|
||||
public virtual Node3D Instantiate(Sector sector)
|
||||
{
|
||||
PackedScene modulePrefab = ResourceLoader.Load<PackedScene>("res://prefabs/gameObjects/node.tscn");
|
||||
Node3D instance = modulePrefab.Instantiate<Node3D>();
|
||||
|
||||
public virtual Node3D Instantiate(Sector sector)
|
||||
{
|
||||
PackedScene modulePrefab = ResourceLoader.Load<PackedScene>("res://prefabs/gameObjects/node.tscn");
|
||||
Node3D instance = modulePrefab.Instantiate<Node3D>();
|
||||
instance.Name = $"GameObject-{UUID}";
|
||||
|
||||
instance.Name = $"GameObject-{UUID}";
|
||||
return instance;
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
public virtual Godot.Collections.Dictionary NetworkWrite(long id, bool full)
|
||||
{
|
||||
Godot.Collections.Dictionary gameObjectData = [];
|
||||
|
||||
public virtual Godot.Collections.Dictionary NetworkWrite(long id, bool full)
|
||||
{
|
||||
Godot.Collections.Dictionary gameObjectData = [];
|
||||
if (full)
|
||||
gameObjectData.Add("type", GetType().ToString());
|
||||
|
||||
if (full)
|
||||
gameObjectData.Add("type", GetType().ToString());
|
||||
if (DirtyBits.HasFlag(DirtyFlags.Sector) || full)
|
||||
gameObjectData.Add("sectorCoordinates", _currentSectorCoordinates);
|
||||
|
||||
if (DirtyBits.HasFlag(DirtyFlags.Sector) || full)
|
||||
gameObjectData.Add("sectorCoordinates", _currentSectorCoordinates);
|
||||
if (DirtyBits.HasFlag(DirtyFlags.LocalCoordinates) || full)
|
||||
gameObjectData.Add("localCoordinates", _localCoordinates);
|
||||
|
||||
if (DirtyBits.HasFlag(DirtyFlags.LocalCoordinates) || full)
|
||||
gameObjectData.Add("localCoordinates", _localCoordinates);
|
||||
if (DirtyBits.HasFlag(DirtyFlags.Rotation) || full)
|
||||
gameObjectData.Add("rotation", _rotation);
|
||||
|
||||
if (DirtyBits.HasFlag(DirtyFlags.Rotation) || full)
|
||||
gameObjectData.Add("rotation", _rotation);
|
||||
if (DirtyBits.HasFlag(DirtyFlags.Velocity) || full)
|
||||
gameObjectData.Add("velocity", _velocity);
|
||||
|
||||
if (DirtyBits.HasFlag(DirtyFlags.Velocity) || full)
|
||||
gameObjectData.Add("velocity", _velocity);
|
||||
if (DirtyBits.HasFlag(DirtyFlags.AngularVelocity) || full)
|
||||
gameObjectData.Add("angularVelocity", _angularVelocity);
|
||||
|
||||
if (DirtyBits.HasFlag(DirtyFlags.AngularVelocity) || full)
|
||||
gameObjectData.Add("angularVelocity", _angularVelocity);
|
||||
if (gameObjectData.Count > 0)
|
||||
{
|
||||
gameObjectData.Add("uuid", UUID.ToString());
|
||||
return gameObjectData;
|
||||
}
|
||||
|
||||
if (gameObjectData.Count > 0)
|
||||
{
|
||||
gameObjectData.Add("uuid", UUID.ToString());
|
||||
return gameObjectData;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
public virtual void NetworkRead(Godot.Collections.Dictionary gameObjectData)
|
||||
{
|
||||
if (gameObjectData.TryGetValue("sectorCoordinates", out var sectorCoordinatesData))
|
||||
{
|
||||
Sector newSector = GameManager.GameUniverse.GetSector((Vector3I)sectorCoordinatesData);
|
||||
CurrentSector.GameObjects.Remove(this);
|
||||
newSector.GameObjects.Add(this);
|
||||
|
||||
public virtual void NetworkRead(Godot.Collections.Dictionary gameObjectData)
|
||||
{
|
||||
if (gameObjectData.TryGetValue("sectorCoordinates", out var sectorCoordinatesData))
|
||||
{
|
||||
Sector newSector = GameManager.GameUniverse.GetSector((Vector3I)sectorCoordinatesData);
|
||||
CurrentSector.GameObjects.Remove(this);
|
||||
newSector.GameObjects.Add(this);
|
||||
AssignSector(newSector);
|
||||
}
|
||||
|
||||
AssignSector(newSector);
|
||||
}
|
||||
if (gameObjectData.TryGetValue("localCoordinates", out var localCoordinatesData))
|
||||
LocalCoordinates = (Vector3)localCoordinatesData;
|
||||
|
||||
if (gameObjectData.TryGetValue("localCoordinates", out var localCoordinatesData))
|
||||
LocalCoordinates = (Vector3)localCoordinatesData;
|
||||
if (gameObjectData.TryGetValue("rotation", out var rotationData))
|
||||
Rotation = (Vector3)rotationData;
|
||||
|
||||
if (gameObjectData.TryGetValue("rotation", out var rotationData))
|
||||
Rotation = (Vector3)rotationData;
|
||||
if (gameObjectData.TryGetValue("velocity", out var velocityData))
|
||||
Velocity = (Vector3)velocityData;
|
||||
|
||||
if (gameObjectData.TryGetValue("velocity", out var velocityData))
|
||||
Velocity = (Vector3)velocityData;
|
||||
if (gameObjectData.TryGetValue("angularVelocity", out var angularVelocityData))
|
||||
AngularVelocity = (Vector3)angularVelocityData;
|
||||
}
|
||||
|
||||
if (gameObjectData.TryGetValue("angularVelocity", out var angularVelocityData))
|
||||
AngularVelocity = (Vector3)angularVelocityData;
|
||||
}
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
GameObject gameObj = (GameObject)obj;
|
||||
return gameObj.UUID == UUID;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
GameObject gameObj = (GameObject)obj;
|
||||
return gameObj.UUID == UUID;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return UUID.GetHashCode();
|
||||
}
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return UUID.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,9 +8,14 @@ public class Character(Sector sector, Vector3 localCoordinates) : GameObject(sec
|
|||
|
||||
public override void AssignSector(Sector sector)
|
||||
{
|
||||
CurrentSector = sector;
|
||||
Vector3 sectorOffset = GetSectorOffset(sector);
|
||||
|
||||
CurrentSector = sector;
|
||||
if (IsMainPlayer())
|
||||
{
|
||||
LocalCoordinates += sectorOffset;
|
||||
}
|
||||
|
||||
ResetLocalCoordinates();
|
||||
UpdateSectorOffsetToMainPlayer();
|
||||
UpdateNodePosition();
|
||||
|
||||
|
|
@ -48,13 +53,6 @@ public class Character(Sector sector, Vector3 localCoordinates) : GameObject(sec
|
|||
|
||||
public override void UpdateNodePosition()
|
||||
{
|
||||
if (IsMainPlayer())
|
||||
{
|
||||
player.GlobalPosition = LocalCoordinates;
|
||||
}
|
||||
else
|
||||
{
|
||||
player.GlobalPosition = LocalCoordinates + SectorOffset;
|
||||
}
|
||||
player.GlobalPosition = LocalCoordinates + SectorOffset;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ public class Star(Sector sector, Vector3 localCoordinates) : GameObject(sector,
|
|||
|
||||
if (gameObjectData != null)
|
||||
{
|
||||
QueueManager.NetworkSyncQueue.Enqueue((id, gameObjectData));
|
||||
QueueManager.NetworkSyncQueue.Enqueue((id, full, gameObjectData));
|
||||
}
|
||||
DirtyBits = DirtyFlags.None;
|
||||
|
||||
|
|
|
|||
|
|
@ -7,12 +7,12 @@ public interface IGenerator
|
|||
|
||||
public Universe InitializeEmptyUniverse(Vector3I universeSize, Vector3 sectorSize);
|
||||
public Universe GenerateUniverse();
|
||||
public Sector GenerateSector(Vector3I coordinates);
|
||||
public Sector GenerateSector(Vector3I coordinates, RandomNumberGenerator rng);
|
||||
|
||||
public Star GenerateStar(Sector sector, Vector3 localCoordinates);
|
||||
public Star GenerateStar(Sector sector);
|
||||
public Star GenerateStar(Sector sector, RandomNumberGenerator rng);
|
||||
public Vessel GenerateShip(Sector sector, Vector3 localCoordinates);
|
||||
public Vessel GenerateShip(Sector sector);
|
||||
public Vessel GenerateShip(Sector sector, RandomNumberGenerator rng);
|
||||
public Vessel GenerateStation(Sector sector, Vector3 localCoordinates);
|
||||
public Vessel GenerateStation(Sector sector);
|
||||
public Vessel GenerateStation(Sector sector, RandomNumberGenerator rng);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,16 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Godot;
|
||||
|
||||
public class TestGenerator : IGenerator
|
||||
{
|
||||
public static Vector3I UNIVERSE_SIZE = new(10, 10, 10);
|
||||
public static Vector3 SECTOR_SIZE = new(50, 50, 50);
|
||||
public static int STARS_PER_SECTOR = 1;
|
||||
public static int STARS_PER_SECTOR_VARIANCE = 0;
|
||||
public static int SHIPS_PER_SECTOR = 0;
|
||||
public static int SHIPS_PER_SECTOR_VARIANCE = 0;
|
||||
public static int STATIONS_PER_SECTOR = 0;
|
||||
public static int STATIONS_PER_SECTOR_VARIANCE = 0;
|
||||
|
||||
private readonly int threads = 16;
|
||||
|
||||
private RandomNumberGenerator rng;
|
||||
public readonly static Vector3I UNIVERSE_SIZE = new(10, 10, 10);
|
||||
public readonly static Vector3 SECTOR_SIZE = new(50, 50, 50);
|
||||
public readonly static int STARS_PER_SECTOR = 1;
|
||||
public readonly static int STARS_PER_SECTOR_VARIANCE = 0;
|
||||
public readonly static int SHIPS_PER_SECTOR = 0;
|
||||
public readonly static int SHIPS_PER_SECTOR_VARIANCE = 0;
|
||||
public readonly static int STATIONS_PER_SECTOR = 0;
|
||||
public readonly static int STATIONS_PER_SECTOR_VARIANCE = 0;
|
||||
|
||||
public Vector3I GetUniverseSize()
|
||||
{
|
||||
|
|
@ -27,7 +22,7 @@ public class TestGenerator : IGenerator
|
|||
return SECTOR_SIZE;
|
||||
}
|
||||
|
||||
public Vector3I GetSectorOffset(Vector3I universeSize)
|
||||
public static Vector3I GetSectorOffset(Vector3I universeSize)
|
||||
{
|
||||
return new(
|
||||
(int)Mathf.Floor(universeSize.X / 2),
|
||||
|
|
@ -36,7 +31,7 @@ public class TestGenerator : IGenerator
|
|||
);
|
||||
}
|
||||
|
||||
public Vector3I GetSectorOffset()
|
||||
public static Vector3I GetSectorOffset()
|
||||
{
|
||||
return GetSectorOffset(UNIVERSE_SIZE);
|
||||
}
|
||||
|
|
@ -52,9 +47,7 @@ public class TestGenerator : IGenerator
|
|||
for (int z = 0; z < universeSize.Z; z++)
|
||||
{
|
||||
universe.Sectors[x, y, z] = new Sector(
|
||||
new(x, y, z),
|
||||
GetSectorOffset(universeSize),
|
||||
sectorSize
|
||||
new(x, y, z)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -65,50 +58,33 @@ public class TestGenerator : IGenerator
|
|||
|
||||
public Universe GenerateUniverse()
|
||||
{
|
||||
rng = new();
|
||||
|
||||
List<Task> tasks = [];
|
||||
|
||||
Universe universe = new(UNIVERSE_SIZE);
|
||||
int countX = Mathf.Clamp(UNIVERSE_SIZE.X / threads, 1, int.MaxValue);
|
||||
|
||||
for (int x = 0; x < UNIVERSE_SIZE.X; x += countX)
|
||||
Parallel.For(0, UNIVERSE_SIZE.X, x =>
|
||||
{
|
||||
int taskStartX = x;
|
||||
int taskCountX = countX;
|
||||
|
||||
Task clusteredTask = Task.Run(() =>
|
||||
{
|
||||
GenerateClustered(universe, taskStartX, taskCountX);
|
||||
});
|
||||
|
||||
tasks.Add(clusteredTask);
|
||||
}
|
||||
|
||||
Task.WaitAll([.. tasks]);
|
||||
GenerateClustered(universe, x);
|
||||
});
|
||||
|
||||
return universe;
|
||||
}
|
||||
|
||||
private void GenerateClustered(Universe universe, int startX, int countX)
|
||||
private void GenerateClustered(Universe universe, int x)
|
||||
{
|
||||
int endX = Mathf.Clamp(startX + countX, 0, UNIVERSE_SIZE.X);
|
||||
RandomNumberGenerator rng = new();
|
||||
rng.Randomize();
|
||||
|
||||
for (int x = startX; x < endX; x++)
|
||||
for (int y = 0; y < UNIVERSE_SIZE.Y; y++)
|
||||
{
|
||||
for (int y = 0; y < UNIVERSE_SIZE.Y; y++)
|
||||
for (int z = 0; z < UNIVERSE_SIZE.Z; z++)
|
||||
{
|
||||
for (int z = 0; z < UNIVERSE_SIZE.Z; z++)
|
||||
{
|
||||
universe.Sectors[x, y, z] = GenerateSector(new(x, y, z));
|
||||
}
|
||||
universe.Sectors[x, y, z] = GenerateSector(new(x, y, z), rng);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Sector GenerateSector(Vector3I coordinates)
|
||||
public Sector GenerateSector(Vector3I coordinates, RandomNumberGenerator rng)
|
||||
{
|
||||
Sector sector = new(coordinates, GetSectorOffset(), SECTOR_SIZE);
|
||||
Sector sector = new(coordinates);
|
||||
|
||||
int starCount = rng.RandiRange(
|
||||
STARS_PER_SECTOR - STARS_PER_SECTOR_VARIANCE,
|
||||
|
|
@ -120,7 +96,7 @@ public class TestGenerator : IGenerator
|
|||
{
|
||||
if (coordinates.X == 5 && coordinates.Y == 5 && coordinates.Z == 5)
|
||||
{
|
||||
Vector3 localCoordinates = GenerateLocalCoordinates();
|
||||
Vector3 localCoordinates = GenerateLocalCoordinates(rng);
|
||||
Star star = GenerateStar(sector, localCoordinates);
|
||||
|
||||
sector.GameObjects.Add(star);
|
||||
|
|
@ -135,7 +111,7 @@ public class TestGenerator : IGenerator
|
|||
|
||||
for (int i = 0; i < shipCount; i++)
|
||||
{
|
||||
Vector3 localCoordinates = GenerateLocalCoordinates();
|
||||
Vector3 localCoordinates = GenerateLocalCoordinates(rng);
|
||||
Vessel ship = GenerateShip(sector, localCoordinates);
|
||||
|
||||
sector.GameObjects.Add(ship);
|
||||
|
|
@ -149,7 +125,7 @@ public class TestGenerator : IGenerator
|
|||
|
||||
for (int i = 0; i < stationCount; i++)
|
||||
{
|
||||
Vector3 localCoordinates = GenerateLocalCoordinates();
|
||||
Vector3 localCoordinates = GenerateLocalCoordinates(rng);
|
||||
Vessel station = GenerateStation(sector, localCoordinates);
|
||||
|
||||
sector.GameObjects.Add(station);
|
||||
|
|
@ -163,9 +139,9 @@ public class TestGenerator : IGenerator
|
|||
return new Star(sector, localCoordinates);
|
||||
}
|
||||
|
||||
public Star GenerateStar(Sector sector)
|
||||
public Star GenerateStar(Sector sector, RandomNumberGenerator rng)
|
||||
{
|
||||
return GenerateStar(sector, GenerateLocalCoordinates());
|
||||
return GenerateStar(sector, GenerateLocalCoordinates(rng));
|
||||
}
|
||||
|
||||
public Vessel GenerateShip(Sector sector, Vector3 localCoordinates)
|
||||
|
|
@ -173,9 +149,9 @@ public class TestGenerator : IGenerator
|
|||
return new Vessel(sector, localCoordinates);
|
||||
}
|
||||
|
||||
public Vessel GenerateShip(Sector sector)
|
||||
public Vessel GenerateShip(Sector sector, RandomNumberGenerator rng)
|
||||
{
|
||||
return GenerateShip(sector, GenerateLocalCoordinates());
|
||||
return GenerateShip(sector, GenerateLocalCoordinates(rng));
|
||||
}
|
||||
|
||||
public Vessel GenerateStation(Sector sector, Vector3 localCoordinates)
|
||||
|
|
@ -183,12 +159,12 @@ public class TestGenerator : IGenerator
|
|||
return new Vessel(sector, localCoordinates);
|
||||
}
|
||||
|
||||
public Vessel GenerateStation(Sector sector)
|
||||
public Vessel GenerateStation(Sector sector, RandomNumberGenerator rng)
|
||||
{
|
||||
return GenerateStation(sector, GenerateLocalCoordinates());
|
||||
return GenerateStation(sector, GenerateLocalCoordinates(rng));
|
||||
}
|
||||
|
||||
public Vector3 GenerateLocalCoordinates()
|
||||
public static Vector3 GenerateLocalCoordinates(RandomNumberGenerator rng)
|
||||
{
|
||||
double x = (rng.Randf() - 0.5) * SECTOR_SIZE.X;
|
||||
double y = (rng.Randf() - 0.5) * SECTOR_SIZE.Y;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using System;
|
||||
using Godot;
|
||||
|
||||
public static class Helpers
|
||||
|
|
@ -27,18 +28,12 @@ public static class Helpers
|
|||
return true;
|
||||
}
|
||||
|
||||
public static bool IsInsideGlobalArea(Vector3Dec areaStart, Vector3Dec areaEnd, Vector3Dec coordinates)
|
||||
public static double GetDistance(Vector3 position1, Vector3 position2)
|
||||
{
|
||||
if (coordinates.X >= areaEnd.X || coordinates.Y >= areaEnd.Y || coordinates.Z >= areaEnd.Z)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
double diffX = Math.Abs(position1.X - position2.X);
|
||||
double diffY = Math.Abs(position1.Y - position2.Y);
|
||||
double diffZ = Math.Abs(position1.Z - position2.Z);
|
||||
|
||||
if (coordinates.X < areaStart.X || coordinates.Y < areaStart.Y || coordinates.Z < areaStart.Z)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return Math.Sqrt(diffX * diffX + diffY * diffY + diffZ * diffZ);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ public partial class NetworkManager : Node
|
|||
public void OnPlayerDisconnected(long id)
|
||||
{
|
||||
Player player = GetNode<Player>($"/root/Game/Space/Player-{id}");
|
||||
GameManager.Instance.DespawnPlayer(player);
|
||||
GameManager.Instance.DespawnPlayer(player, id);
|
||||
}
|
||||
|
||||
private async Task SpawnNetPlayer(long id)
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ public partial class Player : CharacterBody3D
|
|||
rpc.Rpc(nameof(rpc.RpcSyncPlayer), GetMultiplayerAuthority(), GlobalPosition, PlayerData.CurrentSector.Coordinates);
|
||||
}
|
||||
|
||||
PlayerData.SetCoordinatesFromLocal(GlobalPosition);
|
||||
PlayerData.SetCoordinates(GlobalPosition);
|
||||
}
|
||||
|
||||
PlayerData.Simulate(delta);
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@ using Godot;
|
|||
|
||||
public partial class QueueManager : Node
|
||||
{
|
||||
public static ConcurrentQueue<string> LogQueue = new();
|
||||
public static ConcurrentQueue<Action> ActionQueue = new();
|
||||
public static readonly ConcurrentQueue<string> LogQueue = new();
|
||||
public static readonly ConcurrentQueue<Action> ActionQueue = new();
|
||||
|
||||
public static ConcurrentQueue<(Sector, GameObject)> SectorReassignQueue = new();
|
||||
public static ConcurrentQueue<(long, Godot.Collections.Dictionary)> NetworkSyncQueue = new();
|
||||
public static readonly ConcurrentQueue<(Sector, GameObject)> SectorReassignQueue = new();
|
||||
public static readonly ConcurrentQueue<(long, bool, Godot.Collections.Dictionary)> NetworkSyncQueue = new();
|
||||
|
||||
private readonly int sectorReassignQueueRateLimit = 500;
|
||||
private readonly int sectorReassignQueueRateLimit = 5000;
|
||||
private readonly int networkSyncQueueRateLimit = 10;
|
||||
|
||||
public override void _Process(double delta)
|
||||
|
|
@ -51,9 +51,12 @@ public partial class QueueManager : Node
|
|||
&& NetworkSyncQueue.TryDequeue(out var item)
|
||||
)
|
||||
{
|
||||
var (clientId, gameObjectData) = item;
|
||||
var (clientId, full, gameObjectData) = item;
|
||||
|
||||
RPCNode.Instance.RpcId(clientId, nameof(RPCNode.RpcSyncGameObject), gameObjectData);
|
||||
if (full)
|
||||
RPCNode.Instance.RpcId(clientId, nameof(RPCNode.RpcSpawnGameObject), gameObjectData);
|
||||
else
|
||||
RPCNode.Instance.RpcId(clientId, nameof(RPCNode.RpcSyncGameObject), gameObjectData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,8 +6,6 @@ public partial class RPCNode : Node
|
|||
{
|
||||
public static RPCNode Instance { get; private set; }
|
||||
|
||||
private readonly HashSet<Guid> requestedFullGameObjects = [];
|
||||
|
||||
public override void _EnterTree()
|
||||
{
|
||||
Instance = this;
|
||||
|
|
@ -18,6 +16,31 @@ public partial class RPCNode : Node
|
|||
Instance = null;
|
||||
}
|
||||
|
||||
[Rpc(MultiplayerApi.RpcMode.Authority)]
|
||||
public void RpcDespawnGameObject(string uuidData)
|
||||
{
|
||||
GD.Print("DESPAWNING: " + uuidData);
|
||||
|
||||
if (!GameManager.Instance.playerReady)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Guid uuid = Guid.Parse(uuidData);
|
||||
|
||||
List<Sector> sectors = GameManager.Instance.GetCurrentSector().GetNeighbouringSectors();
|
||||
foreach (Sector sector in sectors)
|
||||
{
|
||||
bool removed = sector.RemoveObjectById(uuid);
|
||||
if (removed)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
GameManager.Instance.Despawn(uuid);
|
||||
}
|
||||
|
||||
[Rpc(MultiplayerApi.RpcMode.Authority)]
|
||||
public void RpcSyncGameObject(Godot.Collections.Dictionary gameObjectData)
|
||||
{
|
||||
|
|
@ -43,28 +66,22 @@ public partial class RPCNode : Node
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!TrySyncFullGameObject(gameObjectData))
|
||||
{
|
||||
if (requestedFullGameObjects.Contains(uuid))
|
||||
{
|
||||
return;
|
||||
}
|
||||
requestedFullGameObjects.Add(uuid);
|
||||
RpcId(1, nameof(RequestFullGameObject), NetworkManager.Instance.LocalNetId, (string)uuidData);
|
||||
}
|
||||
}
|
||||
|
||||
public bool TrySyncFullGameObject(Godot.Collections.Dictionary gameObjectData)
|
||||
|
||||
[Rpc(MultiplayerApi.RpcMode.Authority)]
|
||||
public void RpcSpawnGameObject(Godot.Collections.Dictionary gameObjectData)
|
||||
{
|
||||
GD.Print("SPAWNING: " + gameObjectData);
|
||||
|
||||
if (!gameObjectData.TryGetValue("type", out var typeData))
|
||||
return false;
|
||||
return;
|
||||
if (!gameObjectData.TryGetValue("sectorCoordinates", out var sectorCoordinatesData))
|
||||
return false;
|
||||
return;
|
||||
if (!gameObjectData.TryGetValue("localCoordinates", out var localCoordinatesData))
|
||||
return false;
|
||||
return;
|
||||
if (!gameObjectData.TryGetValue("uuid", out var uuidData))
|
||||
return false;
|
||||
return;
|
||||
|
||||
string type = (string)typeData;
|
||||
Vector3I sectorCoordinates = (Vector3I)sectorCoordinatesData;
|
||||
|
|
@ -73,7 +90,7 @@ public partial class RPCNode : Node
|
|||
|
||||
Sector sector = GameManager.GameUniverse.GetSector(sectorCoordinates);
|
||||
if (sector == null)
|
||||
return false;
|
||||
return;
|
||||
|
||||
GameObject gameObject;
|
||||
switch (type)
|
||||
|
|
@ -83,38 +100,15 @@ public partial class RPCNode : Node
|
|||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
gameObject.UUID = uuid;
|
||||
gameObject.NetworkRead(gameObjectData);
|
||||
requestedFullGameObjects.Remove(uuid);
|
||||
|
||||
sector.AssignObject(gameObject);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[Rpc(MultiplayerApi.RpcMode.AnyPeer)]
|
||||
public void RequestFullGameObject(long id, string uuidString)
|
||||
{
|
||||
if (!Global.IsGameHost)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Guid uuid = Guid.Parse(uuidString);
|
||||
|
||||
List<Sector> sectors = GameManager.Instance.GetPlayer(id).PlayerData.CurrentSector.GetNeighbouringSectors();
|
||||
foreach (Sector sector in sectors)
|
||||
{
|
||||
GameObject gameObject = sector.GetObjectById(uuid);
|
||||
if (gameObject != null)
|
||||
{
|
||||
gameObject.NetworkWrite(id, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
GameManager.Instance.Spawn(gameObject);
|
||||
}
|
||||
|
||||
[Rpc(MultiplayerApi.RpcMode.AnyPeer)]
|
||||
|
|
@ -138,8 +132,7 @@ public partial class RPCNode : Node
|
|||
return;
|
||||
}
|
||||
|
||||
Vector3Dec newGlobal = playerData.CalculateGlobalCoordinates(sector.GlobalCenterCoordinates, position);
|
||||
playerData.SetCoordinatesFromGlobal(newGlobal);
|
||||
playerData.SetCoordinates(position);
|
||||
|
||||
if (playerData.CurrentSector.Coordinates != sectorCoordinates)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5,45 +5,22 @@ using Godot;
|
|||
public class Sector
|
||||
{
|
||||
public Vector3I Coordinates;
|
||||
public Vector3Dec GlobalStartCoordinates;
|
||||
public Vector3Dec GlobalCenterCoordinates;
|
||||
public Vector3Dec GlobalEndCoordinates;
|
||||
public Vector3 Size;
|
||||
|
||||
public FastUniqueList<GameObject> GameObjects = new();
|
||||
|
||||
public Sector(Vector3I coordinates, Vector3I offset, Vector3 size)
|
||||
public Sector(Vector3I coordinates)
|
||||
{
|
||||
Coordinates = coordinates;
|
||||
|
||||
Vector3Dec sizeDec = Vector3Dec.FromVector3(size);
|
||||
|
||||
decimal startX = (coordinates.X - offset.X) * sizeDec.X;
|
||||
decimal startY = (coordinates.Y - offset.Y) * sizeDec.Y;
|
||||
decimal startZ = (coordinates.Z - offset.Z) * sizeDec.Z;
|
||||
|
||||
GlobalStartCoordinates = new(startX, startY, startZ);
|
||||
|
||||
GlobalCenterCoordinates = new(
|
||||
startX + sizeDec.X / 2,
|
||||
startY + sizeDec.Y / 2,
|
||||
startZ + sizeDec.Z / 2
|
||||
);
|
||||
|
||||
GlobalEndCoordinates = new(
|
||||
startX + sizeDec.X,
|
||||
startY + sizeDec.Y,
|
||||
startZ + sizeDec.Z
|
||||
);
|
||||
|
||||
Size = size;
|
||||
}
|
||||
|
||||
public void Simulate(double delta)
|
||||
public void Simulate(double delta, HashSet<GameObject> ignoreObjects = null)
|
||||
{
|
||||
GameObjects.ForEach(gameObject =>
|
||||
{
|
||||
gameObject.Simulate(delta);
|
||||
if (ignoreObjects == null || !ignoreObjects.Contains(gameObject))
|
||||
{
|
||||
gameObject.Simulate(delta);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -55,11 +32,6 @@ public class Sector
|
|||
});
|
||||
}
|
||||
|
||||
public bool IsObjectInSector(GameObject gameObject)
|
||||
{
|
||||
return Helpers.IsInsideGlobalArea(GlobalStartCoordinates, GlobalEndCoordinates, gameObject.GlobalCoordinates);
|
||||
}
|
||||
|
||||
public void AssignObject(GameObject gameObject)
|
||||
{
|
||||
QueueManager.SectorReassignQueue.Enqueue((this, gameObject));
|
||||
|
|
@ -83,6 +55,20 @@ public class Sector
|
|||
return null;
|
||||
}
|
||||
|
||||
public bool RemoveObjectById(Guid id)
|
||||
{
|
||||
foreach (GameObject gameObject in GameObjects)
|
||||
{
|
||||
if (gameObject.UUID.Equals(id))
|
||||
{
|
||||
GameObjects.Remove(gameObject);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public List<Sector> GetNeighbouringSectors()
|
||||
{
|
||||
List<Sector> neighbours = [];
|
||||
|
|
@ -114,4 +100,15 @@ public class Sector
|
|||
|
||||
return neighbours;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
Sector sector = (Sector)obj;
|
||||
return sector.Coordinates == Coordinates;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Coordinates.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,10 +28,29 @@ public class Universe
|
|||
return Sectors[x, y, z];
|
||||
}
|
||||
|
||||
public Sector GetNeighbouringSector(Sector sector, bool? x, bool? y, bool? z)
|
||||
{
|
||||
Vector3I coordinates = sector.Coordinates;
|
||||
|
||||
if (x == true) coordinates.X++;
|
||||
else if (x == false) coordinates.X--;
|
||||
|
||||
if (y == true) coordinates.Y++;
|
||||
else if (y == false) coordinates.Y--;
|
||||
|
||||
if (z == true) coordinates.Z++;
|
||||
else if (z == false) coordinates.Z--;
|
||||
|
||||
coordinates.X = Math.Clamp(coordinates.X, 0, Size.X);
|
||||
coordinates.Y = Math.Clamp(coordinates.Y, 0, Size.Y);
|
||||
coordinates.Z = Math.Clamp(coordinates.Z, 0, Size.Z);
|
||||
|
||||
return GetSector(coordinates);
|
||||
}
|
||||
|
||||
public bool IsInside(Vector3I sectorCoordinates, Vector3 localCoordinates)
|
||||
{
|
||||
Vector3 sectorSize = GameManager.Generator.GetSectorSize() / 2;
|
||||
Vector3I universeSize = GameManager.GameUniverse.Size;
|
||||
|
||||
if (
|
||||
sectorCoordinates.X == 0 && localCoordinates.X < -sectorSize.X ||
|
||||
|
|
@ -43,9 +62,9 @@ public class Universe
|
|||
}
|
||||
|
||||
if (
|
||||
sectorCoordinates.X == universeSize.X - 1 && localCoordinates.X >= sectorSize.X ||
|
||||
sectorCoordinates.Y == universeSize.Y - 1 && localCoordinates.Y >= sectorSize.Y ||
|
||||
sectorCoordinates.Z == universeSize.Z - 1 && localCoordinates.Z >= sectorSize.Z
|
||||
sectorCoordinates.X == Size.X - 1 && localCoordinates.X >= sectorSize.X ||
|
||||
sectorCoordinates.Y == Size.Y - 1 && localCoordinates.Y >= sectorSize.Y ||
|
||||
sectorCoordinates.Z == Size.Z - 1 && localCoordinates.Z >= sectorSize.Z
|
||||
)
|
||||
{
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -1,106 +0,0 @@
|
|||
using System;
|
||||
using Godot;
|
||||
|
||||
public readonly struct Vector3Dec(decimal x, decimal y, decimal z)
|
||||
{
|
||||
public static Vector3Dec Zero { get; } = new(0, 0, 0);
|
||||
|
||||
public decimal X { get; } = x;
|
||||
public decimal Y { get; } = y;
|
||||
public decimal Z { get; } = z;
|
||||
|
||||
public Vector3 ToVector3()
|
||||
{
|
||||
return new((double)X, (double)Y, (double)Z);
|
||||
}
|
||||
|
||||
public static Vector3Dec FromVector3(Vector3 vec)
|
||||
{
|
||||
return new((decimal)vec.X, (decimal)vec.Y, (decimal)vec.Z);
|
||||
}
|
||||
|
||||
public static Vector3Dec operator +(Vector3Dec vec1, Vector3Dec vec2)
|
||||
{
|
||||
return new(vec1.X + vec2.X, vec1.Y + vec2.Y, vec1.Z + vec2.Z);
|
||||
}
|
||||
|
||||
public static Vector3Dec operator -(Vector3Dec vec1, Vector3Dec vec2)
|
||||
{
|
||||
return new(vec1.X - vec2.X, vec1.Y - vec2.Y, vec1.Z - vec2.Z);
|
||||
}
|
||||
|
||||
public static Vector3Dec operator *(Vector3Dec vec1, Vector3Dec vec2)
|
||||
{
|
||||
return new(vec1.X * vec2.X, vec1.Y * vec2.Y, vec1.Z * vec2.Z);
|
||||
}
|
||||
|
||||
public static Vector3Dec operator /(Vector3Dec vec1, Vector3Dec vec2)
|
||||
{
|
||||
return new(vec1.X / vec2.X, vec1.Y / vec2.Y, vec1.Z / vec2.Z);
|
||||
}
|
||||
|
||||
public static Vector3Dec operator +(Vector3Dec vec, decimal value)
|
||||
{
|
||||
return new(vec.X + value, vec.Y + value, vec.Z + value);
|
||||
}
|
||||
|
||||
public static Vector3Dec operator +(decimal value, Vector3Dec vec)
|
||||
{
|
||||
return vec + value;
|
||||
}
|
||||
|
||||
public static Vector3Dec operator -(Vector3Dec vec, decimal value)
|
||||
{
|
||||
return new(vec.X - value, vec.Y - value, vec.Z - value);
|
||||
}
|
||||
|
||||
public static Vector3Dec operator -(decimal value, Vector3Dec vec)
|
||||
{
|
||||
return vec - value;
|
||||
}
|
||||
|
||||
public static Vector3Dec operator *(Vector3Dec vec, decimal value)
|
||||
{
|
||||
return new(vec.X * value, vec.Y * value, vec.Z * value);
|
||||
}
|
||||
|
||||
public static Vector3Dec operator *(decimal value, Vector3Dec vec)
|
||||
{
|
||||
return vec * value;
|
||||
}
|
||||
|
||||
public static Vector3Dec operator /(Vector3Dec vec, decimal value)
|
||||
{
|
||||
return new(vec.X / value, vec.Y / value, vec.Z / value);
|
||||
}
|
||||
|
||||
public static Vector3Dec operator /(decimal value, Vector3Dec vec)
|
||||
{
|
||||
return vec / value;
|
||||
}
|
||||
|
||||
public static Vector3Dec operator -(Vector3Dec vec)
|
||||
{
|
||||
return new(-vec.X, -vec.Y, -vec.Z);
|
||||
}
|
||||
|
||||
public static bool operator ==(Vector3Dec vec1, Vector3Dec vec2)
|
||||
{
|
||||
return vec1.X == vec2.X && vec1.Y == vec2.Y && vec1.Z == vec2.Z;
|
||||
}
|
||||
|
||||
public static bool operator !=(Vector3Dec vec1, Vector3Dec vec2)
|
||||
{
|
||||
return !(vec1 == vec2);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj != null && obj is Vector3Dec vec && this == vec;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(X, Y, Z);
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://def7cpbdn6gjm
|
||||
Loading…
Add table
Add a link
Reference in a new issue