From 0ef5652ceacc292e1bea9dede594acaa25d04e16b7f482b8d18528b5b9c0f5ae Mon Sep 17 00:00:00 2001 From: Aslan Date: Sun, 22 Feb 2026 14:43:43 -0500 Subject: [PATCH] Refactor; Optimize simulation --- global.json | 6 + scripts/GameManager.cs | 212 ++++++------ scripts/GameObject.cs | 505 +++++++++++++---------------- scripts/GameObjects/Character.cs | 18 +- scripts/GameObjects/Star.cs | 2 +- scripts/Generator/IGenerator.cs | 8 +- scripts/Generator/TestGenerator.cs | 88 ++--- scripts/Helpers.cs | 17 +- scripts/NetworkManager.cs | 2 +- scripts/Player.cs | 2 +- scripts/QueueManager.cs | 17 +- scripts/RPCNode.cs | 83 +++-- scripts/Sector.cs | 65 ++-- scripts/Universe.cs | 27 +- scripts/Vector3Dec.cs | 106 ------ scripts/Vector3Dec.cs.uid | 1 - 16 files changed, 508 insertions(+), 651 deletions(-) create mode 100644 global.json delete mode 100644 scripts/Vector3Dec.cs delete mode 100644 scripts/Vector3Dec.cs.uid diff --git a/global.json b/global.json new file mode 100644 index 0000000..3c0982c --- /dev/null +++ b/global.json @@ -0,0 +1,6 @@ +{ + "sdk": { + "version": "8.0.123", + "rollForward": "disable" + } +} diff --git a/scripts/GameManager.cs b/scripts/GameManager.cs index 491646d..20d2c05 100644 --- a/scripts/GameManager.cs +++ b/scripts/GameManager.cs @@ -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 Players { get; private set; } = []; + public Dictionary> 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 spawnedObjects = []; + private readonly Dictionary localSpawnedObjects = []; private readonly ConcurrentQueue 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 sectorsClose = GetCurrentSector().GetNeighbouringSectors(); - - List tasks = []; - sectorsClose.ForEach(sector => - { - Task simulateTask = Task.Run(() => - { - sector.Simulate(delta); - }); - - tasks.Add(simulateTask); - }); - Task.WaitAll([.. tasks]); + HashSet objectsToSimulate = []; foreach (KeyValuePair player in Players) { - List playerSectors = player.Value.PlayerData.CurrentSector.GetNeighbouringSectors(); + Character character = player.Value.PlayerData; + + List 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 player in Players) + { + bool isHostPlayer = player.Key == 1; + long playerKey = player.Key; + Character character = player.Value.PlayerData; + + PlayerSpawnedObjects.TryGetValue(playerKey, out HashSet playerSpawnedObjects); + playerSpawnedObjects ??= []; + HashSet playerNewSpawnedObjects = []; + + List 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 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 allSpawnedObjects = []; + foreach (HashSet 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 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 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 nearby = current.GetNeighbouringSectors(); - foreach (KeyValuePair spawned in spawnedObjects) + foreach (KeyValuePair 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) diff --git a/scripts/GameObject.cs b/scripts/GameObject.cs index 8ad4333..472f24d 100644 --- a/scripts/GameObject.cs +++ b/scripts/GameObject.cs @@ -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 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 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("res://prefabs/gameObjects/node.tscn"); + Node3D instance = modulePrefab.Instantiate(); - public virtual Node3D Instantiate(Sector sector) - { - PackedScene modulePrefab = ResourceLoader.Load("res://prefabs/gameObjects/node.tscn"); - Node3D instance = modulePrefab.Instantiate(); + 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(); + } } diff --git a/scripts/GameObjects/Character.cs b/scripts/GameObjects/Character.cs index 7ed6327..642c8c2 100644 --- a/scripts/GameObjects/Character.cs +++ b/scripts/GameObjects/Character.cs @@ -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; } } diff --git a/scripts/GameObjects/Star.cs b/scripts/GameObjects/Star.cs index 337e436..c90b9f5 100644 --- a/scripts/GameObjects/Star.cs +++ b/scripts/GameObjects/Star.cs @@ -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; diff --git a/scripts/Generator/IGenerator.cs b/scripts/Generator/IGenerator.cs index 796d4ea..6ebc27b 100644 --- a/scripts/Generator/IGenerator.cs +++ b/scripts/Generator/IGenerator.cs @@ -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); } diff --git a/scripts/Generator/TestGenerator.cs b/scripts/Generator/TestGenerator.cs index 9565a43..1ffe68d 100644 --- a/scripts/Generator/TestGenerator.cs +++ b/scripts/Generator/TestGenerator.cs @@ -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 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; diff --git a/scripts/Helpers.cs b/scripts/Helpers.cs index 3b1add7..27375b9 100644 --- a/scripts/Helpers.cs +++ b/scripts/Helpers.cs @@ -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); } } diff --git a/scripts/NetworkManager.cs b/scripts/NetworkManager.cs index 328a324..08bbcc5 100644 --- a/scripts/NetworkManager.cs +++ b/scripts/NetworkManager.cs @@ -73,7 +73,7 @@ public partial class NetworkManager : Node public void OnPlayerDisconnected(long id) { Player player = GetNode($"/root/Game/Space/Player-{id}"); - GameManager.Instance.DespawnPlayer(player); + GameManager.Instance.DespawnPlayer(player, id); } private async Task SpawnNetPlayer(long id) diff --git a/scripts/Player.cs b/scripts/Player.cs index 955046c..441b8b7 100644 --- a/scripts/Player.cs +++ b/scripts/Player.cs @@ -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); diff --git a/scripts/QueueManager.cs b/scripts/QueueManager.cs index 4eb5cec..f33a2cb 100644 --- a/scripts/QueueManager.cs +++ b/scripts/QueueManager.cs @@ -4,13 +4,13 @@ using Godot; public partial class QueueManager : Node { - public static ConcurrentQueue LogQueue = new(); - public static ConcurrentQueue ActionQueue = new(); + public static readonly ConcurrentQueue LogQueue = new(); + public static readonly ConcurrentQueue 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); } } } diff --git a/scripts/RPCNode.cs b/scripts/RPCNode.cs index 93c659a..7a00d2e 100644 --- a/scripts/RPCNode.cs +++ b/scripts/RPCNode.cs @@ -6,8 +6,6 @@ public partial class RPCNode : Node { public static RPCNode Instance { get; private set; } - private readonly HashSet 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 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 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) { diff --git a/scripts/Sector.cs b/scripts/Sector.cs index 8c801e0..3f14bca 100644 --- a/scripts/Sector.cs +++ b/scripts/Sector.cs @@ -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 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 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 GetNeighbouringSectors() { List 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(); + } } diff --git a/scripts/Universe.cs b/scripts/Universe.cs index f75ba77..0194a82 100644 --- a/scripts/Universe.cs +++ b/scripts/Universe.cs @@ -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; diff --git a/scripts/Vector3Dec.cs b/scripts/Vector3Dec.cs deleted file mode 100644 index 032a058..0000000 --- a/scripts/Vector3Dec.cs +++ /dev/null @@ -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); - } -} diff --git a/scripts/Vector3Dec.cs.uid b/scripts/Vector3Dec.cs.uid deleted file mode 100644 index 1668d18..0000000 --- a/scripts/Vector3Dec.cs.uid +++ /dev/null @@ -1 +0,0 @@ -uid://def7cpbdn6gjm