diff --git a/Imperfect Space.csproj.old b/Imperfect Space.csproj.old
deleted file mode 100644
index d7ef75c..0000000
--- a/Imperfect Space.csproj.old
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
- net8.0
- net9.0
- true
- ImperfectSpace
- true
-
-
diff --git a/Imperfect Space.csproj.old.1 b/Imperfect Space.csproj.old.1
deleted file mode 100644
index 787950a..0000000
--- a/Imperfect Space.csproj.old.1
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
- net8.0
- net9.0
- true
- ImperfectSpace
- true
-
-
diff --git a/prefabs/gameObjects/node.tscn b/prefabs/gameObjects/node.tscn
index cf463cc..9b9bf2d 100644
--- a/prefabs/gameObjects/node.tscn
+++ b/prefabs/gameObjects/node.tscn
@@ -1,6 +1,3 @@
[gd_scene format=3 uid="uid://b6c17xutiecq5"]
-[ext_resource type="Script" uid="uid://dknsws58ej0bp" path="res://scripts/GameObject.cs" id="1_gt1sn"]
-
[node name="Node" type="StaticBody3D" unique_id=1306600716]
-script = ExtResource("1_gt1sn")
diff --git a/prefabs/gameObjects/player.tscn b/prefabs/gameObjects/player.tscn
index eb5183f..01f9a65 100644
--- a/prefabs/gameObjects/player.tscn
+++ b/prefabs/gameObjects/player.tscn
@@ -8,8 +8,9 @@
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_uwrxv"]
[node name="Player" type="CharacterBody3D" unique_id=1391068938]
+transform = Transform3D(-1, 0, 1.2246467991473532e-16, 0, 1, 0, -1.2246467991473532e-16, 0, -1, 0, 0, 0)
script = ExtResource("1_74mkb")
-SprintMultiplier = 500.0
+SprintMultiplier = 5.0
[node name="Camera" type="Camera3D" parent="." unique_id=1639995310]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.6, 0)
@@ -28,4 +29,5 @@ script = ExtResource("2_y1ton")
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.06334634196567002, -0.29036251889898157)
shadow_enabled = true
spot_range = 25.0
-spot_angle = 90.0
+spot_angle = 85.0
+spot_angle_attenuation = 0.73204285
diff --git a/scenes/game.tscn b/scenes/game.tscn
index c268d1a..75fb9b1 100644
--- a/scenes/game.tscn
+++ b/scenes/game.tscn
@@ -5,6 +5,8 @@
[ext_resource type="Script" uid="uid://n557xfrv0i6x" path="res://scripts/GameControlManager.cs" id="4_lbhrr"]
[ext_resource type="Script" uid="uid://bwgjvm21oi3d6" path="res://scripts/GameManager.cs" id="4_p57ef"]
[ext_resource type="Script" uid="uid://betypbypf6bf2" path="res://scripts/GameMenuController.cs" id="5_iywne"]
+[ext_resource type="Script" uid="uid://bo57chobyuegg" path="res://scripts/NetworkManager.cs" id="5_p57ef"]
+[ext_resource type="Script" uid="uid://by0357lop0qge" path="res://scripts/RPCNode.cs" id="6_u5sy4"]
[sub_resource type="BoxMesh" id="BoxMesh_p57ef"]
size = Vector3(2, 0.1, 2)
@@ -15,31 +17,37 @@ size = Vector3(10, 0.5, 10)
[sub_resource type="BoxShape3D" id="BoxShape3D_uwrxv"]
size = Vector3(10, 5, 10)
-[node name="Game" type="Node3D" unique_id=1201210338 node_paths=PackedStringArray("GameMenu", "QueueManager")]
+[node name="Game" type="Node3D" unique_id=1201210338 node_paths=PackedStringArray("GameMenu", "QueueManager", "SpaceRoot")]
script = ExtResource("4_p57ef")
GameMenu = NodePath("GameMenu")
QueueManager = NodePath("QueueManager")
-closeTickInterval = 0.01
+SpaceRoot = NodePath("Space")
+closeTickInterval = 0.1
-[node name="Plane" type="StaticBody3D" parent="." unique_id=1260154250]
+[node name="Space" type="Node3D" parent="." unique_id=1006247987]
+
+[node name="Plane" type="StaticBody3D" parent="Space" unique_id=1260154250]
transform = Transform3D(1, 0, 0, 0, 0.99999994, 0, 0, 0, 0.99999994, 0, -10, 0)
-[node name="PlaneMesh" type="MeshInstance3D" parent="Plane" unique_id=107049489]
+[node name="PlaneMesh" type="MeshInstance3D" parent="Space/Plane" unique_id=107049489]
transform = Transform3D(5, 0, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0)
mesh = SubResource("BoxMesh_p57ef")
-[node name="PlaneCollider" type="CollisionShape3D" parent="Plane" unique_id=970373853]
+[node name="PlaneCollider" type="CollisionShape3D" parent="Space/Plane" unique_id=970373853]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.2, 0)
shape = SubResource("BoxShape3D_8cj0n")
-[node name="GravityZone" type="Area3D" parent="Plane" unique_id=1326543003]
+[node name="GravityZone" type="Area3D" parent="Space/Plane" unique_id=1326543003]
gravity_space_override = 2
script = ExtResource("1_yqjtg")
-[node name="CollisionShape3D" type="CollisionShape3D" parent="Plane/GravityZone" unique_id=772916098]
+[node name="CollisionShape3D" type="CollisionShape3D" parent="Space/Plane/GravityZone" unique_id=772916098]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2.5, 0)
shape = SubResource("BoxShape3D_uwrxv")
+[node name="DirectionalLight3D" type="DirectionalLight3D" parent="Space/Plane" unique_id=226559047]
+transform = Transform3D(1, 0, 0, 0, 1.0000000600000036, 0, 0, 0, 1.0000000600000036, 0, 10.000000600000035, 0)
+
[node name="GameControlManager" type="Node" parent="." unique_id=1751385863 node_paths=PackedStringArray("GameMenu")]
script = ExtResource("4_lbhrr")
GameMenu = NodePath("../GameMenu")
@@ -47,6 +55,13 @@ GameMenu = NodePath("../GameMenu")
[node name="QueueManager" type="Node" parent="." unique_id=355148200]
script = ExtResource("4_iywne")
+[node name="NetworkManager" type="Node" parent="." unique_id=1765485895 node_paths=PackedStringArray("RPCNode")]
+script = ExtResource("5_p57ef")
+RPCNode = NodePath("../RPC")
+
+[node name="RPC" type="Node" parent="." unique_id=498537245]
+script = ExtResource("6_u5sy4")
+
[node name="GameMenu" type="Control" parent="." unique_id=223510406]
visible = false
layout_mode = 3
@@ -77,8 +92,6 @@ offset_bottom = 63.0
layout_mode = 2
text = "Main Menu"
-[node name="DirectionalLight3D" type="DirectionalLight3D" parent="." unique_id=226559047]
-
-[connection signal="body_entered" from="Plane/GravityZone" to="Plane/GravityZone" method="OnBodyEntered"]
-[connection signal="body_exited" from="Plane/GravityZone" to="Plane/GravityZone" method="OnBodyExited"]
+[connection signal="body_entered" from="Space/Plane/GravityZone" to="Space/Plane/GravityZone" method="OnBodyEntered"]
+[connection signal="body_exited" from="Space/Plane/GravityZone" to="Space/Plane/GravityZone" method="OnBodyExited"]
[connection signal="pressed" from="GameMenu/VBoxContainer/MainMenuButton" to="GameMenu" method="OnMainMenu"]
diff --git a/scenes/main_menu.tscn b/scenes/main_menu.tscn
index 31a8acf..848042c 100644
--- a/scenes/main_menu.tscn
+++ b/scenes/main_menu.tscn
@@ -23,13 +23,18 @@ grow_vertical = 2
layout_mode = 2
alignment = 1
-[node name="StartGameButton" type="Button" parent="CenterContainer/VBoxContainer" unique_id=364461644]
+[node name="HostGameButton" type="Button" parent="CenterContainer/VBoxContainer" unique_id=364461644]
layout_mode = 2
-text = "Start Game"
+text = "Host Game"
+
+[node name="JoinGameButton" type="Button" parent="CenterContainer/VBoxContainer" unique_id=1719078976]
+layout_mode = 2
+text = "Join Game"
[node name="ExitGameButton" type="Button" parent="CenterContainer/VBoxContainer" unique_id=1050417063]
layout_mode = 2
text = "Exit Game"
-[connection signal="pressed" from="CenterContainer/VBoxContainer/StartGameButton" to="." method="OnStartGame"]
+[connection signal="pressed" from="CenterContainer/VBoxContainer/HostGameButton" to="." method="OnHostGame"]
+[connection signal="pressed" from="CenterContainer/VBoxContainer/JoinGameButton" to="." method="OnJoinGame"]
[connection signal="pressed" from="CenterContainer/VBoxContainer/ExitGameButton" to="." method="OnExitGame"]
diff --git a/scripts/GameManager.cs b/scripts/GameManager.cs
index aeddbec..a83eaef 100644
--- a/scripts/GameManager.cs
+++ b/scripts/GameManager.cs
@@ -9,22 +9,24 @@ public partial class GameManager : Node3D
{
[Export] public Control GameMenu { get; private set; }
[Export] public QueueManager QueueManager { get; private set; }
+ [Export] public Node3D SpaceRoot { get; private set; }
[Export] public double closeTickInterval = 1;
[Export] public double farTickInterval = 10;
[Export] public int maxFarThreads = 16;
- public Player MainPlayer { get; private set; }
-
+ public static bool Loading { get; private set; } = true;
public static GameManager Singleton { get; private set; }
- public IGenerator Generator { get; private set; }
-
- public Universe GameUniverse { get; private set; }
+ public static IGenerator Generator { get; private set; }
+ public static Universe GameUniverse { get; private set; }
+ public Player MainPlayer { get; private set; }
+ public List Players { get; private set; } = [];
private double closeTickTimer = 0;
private double farTickTimer = 0;
public bool simulating = false;
+ public bool playerReady = false;
private readonly Dictionary spawnedObjects = [];
private readonly ConcurrentQueue spawnQueue = [];
@@ -34,19 +36,33 @@ public partial class GameManager : Node3D
Singleton = this;
Generator = new TestGenerator();
- GameUniverse = Generator.GenerateUniverse();
- Character character = new(GameUniverse.Sectors[50, 50, 50], new(0, 0, 0));
- MainPlayer = character.InstantiatePlayer();
- MainPlayer.GameMenu = GameMenu;
- spawnedObjects.Add(character, MainPlayer);
- CallDeferred("add_child", MainPlayer);
+ if (Global.IsGameHost)
+ {
+ GameUniverse = Generator.GenerateUniverse();
+ Loading = false;
+ }
+ }
+ private void OnPlayerReady()
+ {
+ playerReady = true;
SpawnClose();
}
+ public override void _ExitTree()
+ {
+ Loading = true;
+ Singleton = null;
+ }
+
public override void _Process(double delta)
{
+ if (!playerReady || !Global.IsGameHost)
+ {
+ return;
+ }
+
closeTickTimer += delta;
farTickTimer += delta;
@@ -143,7 +159,6 @@ public partial class GameManager : Node3D
for (int x = startX; x < endX; x++)
{
- QueueManager.LogQueue.Enqueue("Simulating: " + x);
for (int y = 0; y < sizeY; y++)
{
Thread.Sleep(5);
@@ -180,10 +195,34 @@ public partial class GameManager : Node3D
neighbours.ForEach(sector => sector.SpawnObjects());
}
+ public Player SpawnPlayer(Character character, bool isMainPlayer = false)
+ {
+ Player player = character.InstantiatePlayer();
+ player.GameMenu = GameMenu;
+
+ if (isMainPlayer)
+ {
+ MainPlayer = player;
+ OnPlayerReady();
+ }
+
+ Players.Add(player);
+
+ character.UpdateSector();
+
+ SpaceRoot.CallDeferred("add_child", player);
+ return player;
+ }
+
+ public void DespawnPlayer(Player player)
+ {
+ SpaceRoot.CallDeferred("remove_child", player);
+ player.CallDeferred("queue_free");
+ }
+
public void Spawn(GameObject gameObject)
{
spawnQueue.Enqueue(gameObject);
-
CallDeferred(nameof(ProcessSpawnQueue));
}
@@ -202,7 +241,7 @@ public partial class GameManager : Node3D
Node3D instance = gameObject.Instantiate(GetCurrentSector());
spawnedObjects.Add(gameObject, instance);
- CallDeferred("add_child", instance);
+ SpaceRoot.CallDeferred("add_child", instance);
}
public void Despawn(GameObject gameObject)
@@ -215,7 +254,7 @@ public partial class GameManager : Node3D
Node3D nodeToDespawn = spawnedObjects.GetValueOrDefault(gameObject);
spawnedObjects.Remove(gameObject);
- CallDeferred("remove_child", nodeToDespawn);
+ SpaceRoot.CallDeferred("remove_child", nodeToDespawn);
nodeToDespawn.CallDeferred("queue_free");
}
@@ -223,24 +262,136 @@ public partial class GameManager : Node3D
{
Sector current = GetCurrentSector();
- List nearby = current.GetNeighbouringSectors();
-
- foreach (Sector sector in nearby)
+ foreach (Player player in Players)
{
- foreach (GameObject gameObject in sector.GameObjects)
- {
- gameObject.UpdateSectorOffset(current);
- }
+ player.PlayerData.UpdateSectorOffset(current);
+ player.PlayerData.UpdateNodePosition();
}
- foreach (GameObject spawned in spawnedObjects.Keys)
+ List nearby = current.GetNeighbouringSectors();
+ foreach (KeyValuePair spawned in spawnedObjects)
{
- if (!nearby.Contains(spawned.CurrentSector))
+ GameObject gameObject = spawned.Key;
+ Node3D node = spawned.Value;
+
+ if (!nearby.Contains(gameObject.CurrentSector))
{
- Despawn(spawned);
+ Despawn(gameObject);
+ }
+ else
+ {
+ gameObject.UpdateSectorOffset(current);
+ node.GlobalPosition = gameObject.LocalCoordinates + gameObject.SectorOffset;
}
}
nearby.ForEach(sector => sector.SpawnObjects());
}
+
+ // These should be in RPCNode and should be queued
+ public void SendUniverseToClient(long id)
+ {
+ RpcId(id, nameof(RpcDownloadUniverse), Generator.GetUniverseSize(), Generator.GetSectorSize());
+ }
+
+ // These should be in RPCNode and should be queued
+ [Rpc(MultiplayerApi.RpcMode.Authority)]
+ public void RpcDownloadUniverse(Vector3I universeSize, Vector3 sectorSize)
+ {
+ GameUniverse = Generator.InitializeEmptyUniverse(universeSize, sectorSize);
+
+ Loading = false;
+ }
+
+ // These should be in RPCNode and should be queued
+ [Rpc(MultiplayerApi.RpcMode.AnyPeer)]
+ public void RpcSendNearbySectors(Vector3I coordinates, long id = -1)
+ {
+ Sector sector = GameUniverse.GetSector(coordinates);
+ if (sector == null)
+ {
+ return;
+ }
+
+ List sectors = sector.GetNeighbouringSectors();
+ sectors.ForEach(sector => SendSectorToClients(sector, id == -1 ? null : id));
+ }
+
+ // These should be in RPCNode and should be queued
+ public void SendSectorToClients(Sector sector, long? id = null)
+ {
+ foreach (GameObject gameObject in sector.GameObjects)
+ {
+ if (gameObject is Character)
+ {
+ continue;
+ }
+
+ Godot.Collections.Dictionary gameObjectData = new() {
+ { "type", gameObject.GetType().Name },
+ { "sectorCoordinates", sector.Coordinates },
+ { "coordinates", gameObject.LocalCoordinates },
+ { "uuid", gameObject.UUID.ToString() },
+ };
+
+ if (id.HasValue)
+ {
+ RpcId(id.Value, nameof(RpcDownloadGameObject), gameObjectData);
+ }
+ else
+ {
+ Rpc(nameof(RpcDownloadGameObject), gameObjectData);
+ }
+ }
+ }
+
+ // These should be in RPCNode and should be queued
+ [Rpc(MultiplayerApi.RpcMode.Authority)]
+ public void RpcDownloadGameObject(Godot.Collections.Dictionary gameObjectData)
+ {
+ if (!gameObjectData.TryGetValue("type", out var typeData))
+ {
+ return;
+ }
+ if (!gameObjectData.TryGetValue("sectorCoordinates", out var sectorCoordinatesData))
+ {
+ return;
+ }
+ if (!gameObjectData.TryGetValue("coordinates", out var coordinatesData))
+ {
+ return;
+ }
+ if (!gameObjectData.TryGetValue("uuid", out var uuidData))
+ {
+ return;
+ }
+
+ string type = (string)typeData;
+ Vector3I sectorCoordinates = (Vector3I)sectorCoordinatesData;
+ Vector3 coordinates = (Vector3)coordinatesData;
+ Guid uuid = Guid.Parse((string)uuidData);
+
+ Sector sector = GameUniverse.GetSector(sectorCoordinates);
+ if (sector == null)
+ {
+ return;
+ }
+
+ GameObject gameObject;
+ switch (type)
+ {
+ case "Star":
+ gameObject = new Star(sector, coordinates);
+ break;
+
+ default:
+ return;
+ }
+
+ gameObject.UUID = uuid;
+
+ Spawn(gameObject);
+
+ sector.AssignObject(gameObject);
+ }
}
diff --git a/scripts/GameObject.cs b/scripts/GameObject.cs
index fbc26aa..a689a96 100644
--- a/scripts/GameObject.cs
+++ b/scripts/GameObject.cs
@@ -1,157 +1,160 @@
+using System;
using System.Collections.Generic;
using Godot;
public abstract class GameObject
{
- public Sector CurrentSector { get; protected set; }
- public Vector3 LocalCoordinates { get; protected set; }
- public Vector3Dec GlobalCoordinates { get; protected set; }
+ public Guid UUID { get; set; }
- public Vector3 SectorOffset { get; set; }
+ public Sector CurrentSector { get; protected set; }
+ public Vector3 LocalCoordinates { get; protected set; }
+ public Vector3Dec GlobalCoordinates { get; protected set; }
- private bool reassigning = false;
+ public Vector3 SectorOffset { get; set; }
- public GameObject(Sector sector, Vector3 localCoordinates)
- {
- CurrentSector = sector;
- LocalCoordinates = localCoordinates;
+ protected bool reassigning = false;
- GlobalCoordinates = new
- (
- sector.GlobalCenterCoordinates.X + (decimal)localCoordinates.X,
- sector.GlobalCenterCoordinates.Y + (decimal)localCoordinates.Y,
- sector.GlobalCenterCoordinates.Z + (decimal)localCoordinates.Z
- );
- }
+ public GameObject(Sector sector, Vector3 localCoordinates)
+ {
+ UUID = Guid.NewGuid();
- public GameObject(Sector sector, double localX, double localY, double localZ)
- {
- CurrentSector = sector;
- LocalCoordinates = new(localX, localY, localZ);
+ CurrentSector = sector;
+ LocalCoordinates = localCoordinates;
- GlobalCoordinates = new
- (
- sector.GlobalStartCoordinates.X + (decimal)localX,
- sector.GlobalStartCoordinates.Y + (decimal)localY,
- sector.GlobalStartCoordinates.Z + (decimal)localZ
- );
- }
+ GlobalCoordinates = CalculateGlobalCoordinates(sector.GlobalCenterCoordinates, localCoordinates);
+ }
- public GameObject(Vector3Dec coordinates)
- {
- GlobalCoordinates = coordinates;
- UpdateSector();
- }
+ public GameObject(Vector3Dec coordinates)
+ {
+ GlobalCoordinates = coordinates;
+ UpdateSector();
+ }
- public GameObject(decimal x, decimal y, decimal z)
- {
- GlobalCoordinates = new(x, y, z);
- UpdateSector();
- }
+ public GameObject(decimal x, decimal y, decimal z)
+ {
+ GlobalCoordinates = new(x, y, z);
+ UpdateSector();
+ }
- public bool IsInCurrentSector()
- {
- return Helpers.IsInsideArea(CurrentSector.Size / 2, LocalCoordinates);
- }
+ public Vector3Dec CalculateGlobalCoordinates(Vector3Dec sectorCenter, Vector3 local)
+ {
+ return new
+ (
+ sectorCenter.X + (decimal)local.X,
+ sectorCenter.Y + (decimal)local.Y,
+ sectorCenter.Z + (decimal)local.Z
+ );
+ }
- public void UpdateSector()
- {
- List neighbours = CurrentSector.GetNeighbouringSectors();
- foreach (Sector sector in neighbours)
- {
- if (sector.IsObjectInSector(this))
- {
- sector.AssignObject(this);
- reassigning = true;
- return;
- }
- }
+ public bool IsInCurrentSector()
+ {
+ return Helpers.IsInsideArea(CurrentSector.Size / 2, LocalCoordinates);
+ }
- foreach (Sector sector in GameManager.Singleton.GameUniverse.Sectors)
- {
- if (sector.IsObjectInSector(this))
- {
- sector.AssignObject(this);
- reassigning = true;
- return;
- }
- }
- }
+ public void UpdateSector()
+ {
+ List neighbours = CurrentSector.GetNeighbouringSectors();
+ foreach (Sector sector in neighbours)
+ {
+ if (sector.IsObjectInSector(this))
+ {
+ reassigning = true;
+ sector.AssignObject(this);
+ return;
+ }
+ }
- public void AssignSector(Sector sector)
- {
- CurrentSector = sector;
- ResetLocalCoordinates();
+ if (!GameManager.GameUniverse.IsInside(CurrentSector.Coordinates, LocalCoordinates))
+ {
+ return;
+ }
- SectorOffset = GetSectorOffset(GameManager.Singleton.GetCurrentSector());
+ foreach (Sector sector in GameManager.GameUniverse.Sectors)
+ {
+ if (sector.IsObjectInSector(this))
+ {
+ reassigning = true;
+ sector.AssignObject(this);
+ return;
+ }
+ }
+ }
- if (this is Character)
- {
- GameManager.Singleton.ApplyOrigin();
- }
+ public virtual void AssignSector(Sector sector)
+ {
+ CurrentSector = sector;
+ ResetLocalCoordinates();
- if (this is not Character)
- {
- List neighbours = GameManager.Singleton.GetCurrentSector().GetNeighbouringSectors();
+ UpdateSectorOffsetToMainPlayer();
- if (neighbours.Contains(sector))
- {
- GameManager.Singleton.Spawn(this);
- }
- else
- {
- GameManager.Singleton.Despawn(this);
- }
- }
+ List neighbours = GameManager.Singleton.GetCurrentSector().GetNeighbouringSectors();
- reassigning = false;
- }
+ if (neighbours.Contains(sector))
+ {
+ GameManager.Singleton.Spawn(this);
+ }
+ else
+ {
+ GameManager.Singleton.Despawn(this);
+ }
- public Vector3 GetSectorOffset(Sector sector)
- {
- Vector3Dec relative = CurrentSector.GlobalCenterCoordinates - sector.GlobalCenterCoordinates;
- return relative.ToVector3();
- }
+ reassigning = false;
+ }
- public virtual void UpdateSectorOffset(Sector sector)
- {
- SectorOffset = GetSectorOffset(sector);
- }
+ public Vector3 GetSectorOffset(Sector sector)
+ {
+ Vector3Dec relative = CurrentSector.GlobalCenterCoordinates - sector.GlobalCenterCoordinates;
+ return relative.ToVector3();
+ }
- public void SetCoordinatesFromGlobal(Vector3Dec globalCoordinates)
- {
- GlobalCoordinates = globalCoordinates;
- LocalCoordinates = (globalCoordinates - CurrentSector.GlobalCenterCoordinates).ToVector3();
+ public void UpdateSectorOffset(Sector sector)
+ {
+ SectorOffset = GetSectorOffset(sector);
+ }
- UpdateNodePosition();
- }
+ public void UpdateSectorOffsetToMainPlayer()
+ {
+ UpdateSectorOffset(GameManager.Singleton.GetCurrentSector());
+ }
- public void SetCoordinatesFromLocal(Vector3 localCoordinates)
- {
- LocalCoordinates = localCoordinates;
- GlobalCoordinates = Vector3Dec.FromVector3(localCoordinates) + CurrentSector.GlobalCenterCoordinates;
+ public void SetCoordinatesFromGlobal(Vector3Dec globalCoordinates)
+ {
+ GlobalCoordinates = globalCoordinates;
+ LocalCoordinates = (globalCoordinates - CurrentSector.GlobalCenterCoordinates).ToVector3();
- UpdateNodePosition();
- }
+ UpdateNodePosition();
+ }
- public void ResetLocalCoordinates()
- {
- SetCoordinatesFromGlobal(GlobalCoordinates);
- }
+ public void SetCoordinatesFromLocal(Vector3 localCoordinates)
+ {
+ LocalCoordinates = localCoordinates;
+ GlobalCoordinates = Vector3Dec.FromVector3(localCoordinates) + CurrentSector.GlobalCenterCoordinates;
- public virtual void UpdateNodePosition() { }
+ UpdateNodePosition();
+ }
- public virtual void Simulate(double delta)
- {
- if (!reassigning && !IsInCurrentSector())
- {
- UpdateSector();
- }
- }
+ public void ResetLocalCoordinates()
+ {
+ SetCoordinatesFromGlobal(GlobalCoordinates);
+ }
- public virtual Node3D Instantiate(Sector sector)
- {
- PackedScene modulePrefab = ResourceLoader.Load("res://prefabs/gameObjects/node.tscn");
- return modulePrefab.Instantiate();
- }
+ public virtual void UpdateNodePosition() { }
+
+ public virtual void Simulate(double delta)
+ {
+ if (!reassigning && !IsInCurrentSector())
+ {
+ UpdateSector();
+ }
+ }
+
+ public virtual Node3D Instantiate(Sector sector)
+ {
+ PackedScene modulePrefab = ResourceLoader.Load("res://prefabs/gameObjects/node.tscn");
+ Node3D instance = modulePrefab.Instantiate();
+
+ instance.Name = $"GameObject-{UUID}";
+
+ return instance;
+ }
}
diff --git a/scripts/GameObjects/Character.cs b/scripts/GameObjects/Character.cs
index 7930bf6..2b7ceef 100644
--- a/scripts/GameObjects/Character.cs
+++ b/scripts/GameObjects/Character.cs
@@ -4,6 +4,39 @@ public class Character(Sector sector, Vector3 localCoordinates) : GameObject(sec
{
private Player player;
+ public bool SheduledUpdateNode = false;
+
+ public override void AssignSector(Sector sector)
+ {
+ CurrentSector = sector;
+
+ ResetLocalCoordinates();
+ UpdateSectorOffsetToMainPlayer();
+ UpdateNodePosition();
+
+ if (IsMainPlayer())
+ {
+ GameManager.Singleton.ApplyOrigin();
+
+ if (!Global.IsGameHost)
+ {
+ GameManager.Singleton.RpcId(
+ 1,
+ nameof(GameManager.Singleton.RpcSendNearbySectors),
+ CurrentSector.Coordinates,
+ NetworkManager.Singleton.LocalNetId
+ );
+ }
+ }
+
+ reassigning = false;
+ }
+
+ public bool IsMainPlayer()
+ {
+ return player == GameManager.Singleton.MainPlayer;
+ }
+
public Player InstantiatePlayer()
{
PackedScene prefab = ResourceLoader.Load("res://prefabs/gameObjects/player.tscn");
@@ -17,6 +50,13 @@ public class Character(Sector sector, Vector3 localCoordinates) : GameObject(sec
public override void UpdateNodePosition()
{
- player.GlobalPosition = LocalCoordinates;
+ if (IsMainPlayer())
+ {
+ player.GlobalPosition = LocalCoordinates;
+ }
+ else
+ {
+ player.GlobalPosition = LocalCoordinates + SectorOffset;
+ }
}
}
diff --git a/scripts/GameObjects/Star.cs b/scripts/GameObjects/Star.cs
index 7e2e143..16a7914 100644
--- a/scripts/GameObjects/Star.cs
+++ b/scripts/GameObjects/Star.cs
@@ -6,7 +6,7 @@ public class Star(Sector sector, Vector3 localCoordinates) : GameObject(sector,
{
base.Simulate(delta);
- SetCoordinatesFromGlobal(new(GlobalCoordinates.X, GlobalCoordinates.Y, GlobalCoordinates.Z + 1 * (decimal)delta));
+ //SetCoordinatesFromGlobal(new(GlobalCoordinates.X, GlobalCoordinates.Y, GlobalCoordinates.Z + 1 * (decimal)delta));
}
public override Node3D Instantiate(Sector sector)
@@ -14,8 +14,9 @@ public class Star(Sector sector, Vector3 localCoordinates) : GameObject(sector,
PackedScene prefab = ResourceLoader.Load("res://prefabs/gameObjects/star.tscn");
StarNode instance = prefab.Instantiate();
+ instance.Name = $"Star-{UUID}";
instance.StarData = this;
- SectorOffset = GetSectorOffset(sector);
+ UpdateSectorOffsetToMainPlayer();
return instance;
}
diff --git a/scripts/Generator/IGenerator.cs b/scripts/Generator/IGenerator.cs
index d95ce7f..796d4ea 100644
--- a/scripts/Generator/IGenerator.cs
+++ b/scripts/Generator/IGenerator.cs
@@ -1,4 +1,3 @@
-using System;
using Godot;
public interface IGenerator
@@ -6,6 +5,7 @@ public interface IGenerator
public Vector3I GetUniverseSize();
public Vector3 GetSectorSize();
+ public Universe InitializeEmptyUniverse(Vector3I universeSize, Vector3 sectorSize);
public Universe GenerateUniverse();
public Sector GenerateSector(Vector3I coordinates);
diff --git a/scripts/Generator/TestGenerator.cs b/scripts/Generator/TestGenerator.cs
index 285c33f..8a54986 100644
--- a/scripts/Generator/TestGenerator.cs
+++ b/scripts/Generator/TestGenerator.cs
@@ -4,10 +4,10 @@ using Godot;
public class TestGenerator : IGenerator
{
- public static Vector3I UNIVERSE_SIZE = new(100, 100, 100);
- public static Vector3 SECTOR_SIZE = new(5000, 5000, 5000);
- public static int STARS_PER_SECTOR = 100;
- public static int STARS_PER_SECTOR_VARIANCE = 5;
+ 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;
@@ -22,7 +22,12 @@ public class TestGenerator : IGenerator
return UNIVERSE_SIZE;
}
- public Vector3I GetSectorOffset()
+ public Vector3 GetSectorSize()
+ {
+ return SECTOR_SIZE;
+ }
+
+ public Vector3I GetSectorOffset(Vector3I universeSize)
{
return new(
(int)Mathf.Floor(UNIVERSE_SIZE.X / 2),
@@ -31,9 +36,31 @@ public class TestGenerator : IGenerator
);
}
- public Vector3 GetSectorSize()
+ public Vector3I GetSectorOffset()
{
- return SECTOR_SIZE;
+ return GetSectorOffset(UNIVERSE_SIZE);
+ }
+
+ public Universe InitializeEmptyUniverse(Vector3I universeSize, Vector3 sectorSize)
+ {
+ Universe universe = new(universeSize);
+
+ for (int x = 0; x < universeSize.X; x++)
+ {
+ for (int y = 0; y < universeSize.Y; y++)
+ {
+ for (int z = 0; z < universeSize.Z; z++)
+ {
+ universe.Sectors[x, y, z] = new Sector(
+ new(x, y, z),
+ GetSectorOffset(universeSize),
+ sectorSize
+ );
+ }
+ }
+ }
+
+ return universe;
}
public Universe GenerateUniverse()
diff --git a/scripts/Global.cs b/scripts/Global.cs
new file mode 100644
index 0000000..b13a22f
--- /dev/null
+++ b/scripts/Global.cs
@@ -0,0 +1,4 @@
+public static class Global
+{
+ public static bool IsGameHost = false;
+}
diff --git a/scripts/Global.cs.uid b/scripts/Global.cs.uid
new file mode 100644
index 0000000..1230bb2
--- /dev/null
+++ b/scripts/Global.cs.uid
@@ -0,0 +1 @@
+uid://dufeaph62nh7y
diff --git a/scripts/GravityReceiver.cs b/scripts/GravityReceiver.cs
index e7f3583..83f7706 100644
--- a/scripts/GravityReceiver.cs
+++ b/scripts/GravityReceiver.cs
@@ -16,7 +16,7 @@ public partial class GravityReceiver : Node3D
public override void _Ready()
{
owner = GetParent();
- root = GetTree().Root.GetNode("Game");
+ root = GetTree().Root.GetNode("Game/Space");
}
public override void _PhysicsProcess(double delta)
diff --git a/scripts/Helpers.cs b/scripts/Helpers.cs
index 7858291..3b1add7 100644
--- a/scripts/Helpers.cs
+++ b/scripts/Helpers.cs
@@ -7,6 +7,11 @@ public static class Helpers
return value >= min && value <= max;
}
+ public static bool IsBetween(double value, double min, int max)
+ {
+ return value >= min && value < max;
+ }
+
public static bool IsInsideArea(Vector3 area, Vector3 coordinates)
{
if (coordinates.X >= area.X || coordinates.Y >= area.Y || coordinates.Z >= area.Z)
diff --git a/scripts/MainMenu/MainMenuController.cs b/scripts/MainMenu/MainMenuController.cs
index 970fe6a..5b2b14f 100644
--- a/scripts/MainMenu/MainMenuController.cs
+++ b/scripts/MainMenu/MainMenuController.cs
@@ -2,8 +2,17 @@ using Godot;
public partial class MainMenuController : Node
{
- public void OnStartGame()
+ public void OnHostGame()
{
+ Global.IsGameHost = true;
+
+ GetTree().ChangeSceneToFile("res://scenes/game.tscn");
+ }
+
+ public void OnJoinGame()
+ {
+ Global.IsGameHost = false;
+
GetTree().ChangeSceneToFile("res://scenes/game.tscn");
}
diff --git a/scripts/NetworkManager.cs b/scripts/NetworkManager.cs
new file mode 100644
index 0000000..7ab4303
--- /dev/null
+++ b/scripts/NetworkManager.cs
@@ -0,0 +1,101 @@
+using System.Threading.Tasks;
+using Godot;
+
+public partial class NetworkManager : Node
+{
+ [Export] public RPCNode RPCNode { get; private set; }
+
+ public static NetworkManager Singleton { get; private set; }
+
+ public int LocalNetId { get; private set; } = -1;
+
+ public override void _Ready()
+ {
+ Singleton = this;
+
+ if (Global.IsGameHost)
+ {
+ CreateServer();
+ }
+ else
+ {
+ CreateClient();
+ }
+
+ GetTree().GetMultiplayer().PeerConnected += OnPlayerConnected;
+ GetTree().GetMultiplayer().PeerDisconnected += OnPlayerDisconnected;
+ GetTree().GetMultiplayer().ServerDisconnected += OnServerClosed;
+ }
+
+ public override void _ExitTree()
+ {
+ GetTree().GetMultiplayer().MultiplayerPeer.Close();
+ GetTree().GetMultiplayer().MultiplayerPeer = null;
+ }
+
+ public void CreateServer()
+ {
+ ENetMultiplayerPeer peer = new();
+ peer.CreateServer(2142, 128);
+
+ GetTree().GetMultiplayer().MultiplayerPeer = peer;
+
+ LocalNetId = GetTree().GetMultiplayer().GetUniqueId();
+
+ _ = SpawnNetPlayer(LocalNetId);
+ }
+
+ public void CreateClient()
+ {
+ ENetMultiplayerPeer peer = new();
+ peer.CreateClient("127.0.0.1", 2142);
+
+ GetTree().GetMultiplayer().MultiplayerPeer = peer;
+
+ LocalNetId = GetTree().GetMultiplayer().GetUniqueId();
+
+ _ = SpawnNetPlayer(LocalNetId);
+ }
+
+ public void OnServerClosed()
+ {
+ GetTree().ChangeSceneToFile("res://scenes/main_menu.tscn");
+ }
+
+ public void OnPlayerConnected(long id)
+ {
+ if (Global.IsGameHost)
+ {
+ GameManager.Singleton.SendUniverseToClient(id);
+ }
+
+ _ = SpawnNetPlayer(id);
+ }
+
+ public void OnPlayerDisconnected(long id)
+ {
+ Player player = GetNode($"/root/Game/Space/Player-{id}");
+ GameManager.Singleton.DespawnPlayer(player);
+ }
+
+ private async Task SpawnNetPlayer(long id)
+ {
+ while (GameManager.Loading)
+ {
+ await Task.Delay(10);
+ }
+
+ bool isMainPlayer = LocalNetId == id;
+
+ Character character = new(GameManager.GameUniverse.Sectors[5, 5, 5], new(0, 0, 0));
+ Player player = GameManager.Singleton.SpawnPlayer(character, isMainPlayer);
+ player.Name = $"Player-{id}";
+
+ player.SetMultiplayerAuthority((int)id);
+
+ if (isMainPlayer && !Global.IsGameHost)
+ {
+ GameManager.Singleton.RpcId(1, nameof(GameManager.Singleton.RpcSendNearbySectors), character.CurrentSector.Coordinates, id);
+ }
+ }
+}
diff --git a/scripts/NetworkManager.cs.uid b/scripts/NetworkManager.cs.uid
new file mode 100644
index 0000000..16dd97f
--- /dev/null
+++ b/scripts/NetworkManager.cs.uid
@@ -0,0 +1 @@
+uid://bo57chobyuegg
diff --git a/scripts/Player.cs b/scripts/Player.cs
index e6a4136..121c6c6 100644
--- a/scripts/Player.cs
+++ b/scripts/Player.cs
@@ -2,11 +2,12 @@ using Godot;
public partial class Player : CharacterBody3D
{
-
[Export] public double Speed = 5;
[Export] public double SprintMultiplier = 2;
[Export] public double JumpForce = 5;
[Export] public double MouseSensitivity = 0.2;
+ [Export] public double NetworkSyncInterval = 0.2;
+ [Export] public double NetworkPhysicsSyncInterval = 0.05;
public Control GameMenu { get; set; }
@@ -14,13 +15,17 @@ public partial class Player : CharacterBody3D
private Vector3 gravityVelocity = Vector3.Zero;
private Vector3 movementVelocity = Vector3.Zero;
+ private GravityReceiver gravityReceiver;
private double cameraPitch = 0;
private Camera3D camera;
- private GravityReceiver gravityReceiver;
+ private double networkSyncCounter = 0;
+ private double networkPhysicsSyncCounter = 0;
public override void _Ready()
{
camera = GetNode("Camera");
+ camera.Current = IsMultiplayerAuthority();
+
gravityReceiver = GetNode("GravityReceiver");
PlayerData.UpdateNodePosition();
@@ -28,7 +33,25 @@ public partial class Player : CharacterBody3D
public override void _Process(double delta)
{
- PlayerData.SetCoordinatesFromLocal(GlobalPosition);
+ if (IsMultiplayerAuthority())
+ {
+ networkPhysicsSyncCounter += delta;
+ if (networkPhysicsSyncCounter >= NetworkPhysicsSyncInterval)
+ {
+ networkPhysicsSyncCounter = 0;
+ Rpc(nameof(RpcSyncPhysics), movementVelocity, gravityVelocity, GlobalRotation);
+ }
+
+ networkSyncCounter += delta;
+ if (networkSyncCounter >= NetworkSyncInterval)
+ {
+ networkSyncCounter = 0;
+ Rpc(nameof(RpcSync), GlobalPosition, PlayerData.CurrentSector.Coordinates);
+ }
+
+ PlayerData.SetCoordinatesFromLocal(GlobalPosition);
+ }
+
PlayerData.Simulate(delta);
}
@@ -37,21 +60,18 @@ public partial class Player : CharacterBody3D
Vector3 newGravityVelocity = gravityVelocity;
Vector3 newMovementVelocity = movementVelocity;
- if (IsInGravity())
+ if (!GameMenu.Visible && IsMultiplayerAuthority())
{
- ProcessGravity(delta, ref newGravityVelocity);
- if (!GameMenu.Visible)
+ if (IsInGravity())
{
+ ProcessGravity(delta, ref newGravityVelocity);
ProcessControlsGravity(ref newGravityVelocity, ref newMovementVelocity);
}
- }
- else
- {
- newGravityVelocity = Vector3.Zero;
-
- ProcessCamera(delta);
- if (!GameMenu.Visible)
+ else
{
+ newGravityVelocity = Vector3.Zero;
+
+ ProcessCamera(delta);
ProcessControls(ref newMovementVelocity);
}
}
@@ -69,6 +89,10 @@ public partial class Player : CharacterBody3D
{
return;
}
+ if (!IsMultiplayerAuthority())
+ {
+ return;
+ }
if (@event is InputEventMouseMotion motion)
{
@@ -213,4 +237,33 @@ public partial class Player : CharacterBody3D
RotateObjectLocal(Vector3.Forward, rotateAmountZ);
}
+
+ [Rpc(MultiplayerApi.RpcMode.AnyPeer)]
+ public void RpcSync(Vector3 position, Vector3I sectorCoordinates)
+ {
+ Sector sector = GameManager.GameUniverse.GetSector(sectorCoordinates);
+ if (sector == null)
+ {
+ return;
+ }
+
+ Vector3Dec newGlobal = PlayerData.CalculateGlobalCoordinates(sector.GlobalCenterCoordinates, position);
+ PlayerData.SetCoordinatesFromGlobal(newGlobal);
+
+ if (PlayerData.CurrentSector.Coordinates != sectorCoordinates)
+ {
+ sector.AssignObject(PlayerData);
+ }
+
+ GlobalPosition = position + PlayerData.SectorOffset;
+ }
+
+ [Rpc(MultiplayerApi.RpcMode.AnyPeer)]
+ public void RpcSyncPhysics(Vector3 _movementVelocity, Vector3 _gravityVelocity, Vector3 rotation)
+ {
+ movementVelocity = _movementVelocity;
+ gravityVelocity = _gravityVelocity;
+
+ GlobalRotation = rotation;
+ }
}
diff --git a/scripts/QueueManager.cs b/scripts/QueueManager.cs
index 7c6f07f..fdbb2da 100644
--- a/scripts/QueueManager.cs
+++ b/scripts/QueueManager.cs
@@ -23,8 +23,6 @@ public partial class QueueManager : Node
action();
}
- GD.Print(SectorReassignQueue.Count);
-
int sectorReassignQueueProcessed = 0;
while (
!GameManager.Singleton.simulating
diff --git a/scripts/RPCNode.cs b/scripts/RPCNode.cs
new file mode 100644
index 0000000..5397ef2
--- /dev/null
+++ b/scripts/RPCNode.cs
@@ -0,0 +1,5 @@
+using Godot;
+
+public partial class RPCNode : Node
+{
+}
diff --git a/scripts/RPCNode.cs.uid b/scripts/RPCNode.cs.uid
new file mode 100644
index 0000000..c9959c2
--- /dev/null
+++ b/scripts/RPCNode.cs.uid
@@ -0,0 +1 @@
+uid://by0357lop0qge
diff --git a/scripts/Sector.cs b/scripts/Sector.cs
index 224531d..077d196 100644
--- a/scripts/Sector.cs
+++ b/scripts/Sector.cs
@@ -65,7 +65,7 @@ public class Sector
{
List neighbours = [];
- Sector[,,] allSectors = GameManager.Singleton.GameUniverse.Sectors;
+ Sector[,,] allSectors = GameManager.GameUniverse.Sectors;
int sizeX = allSectors.GetLength(0);
int sizeY = allSectors.GetLength(1);
diff --git a/scripts/Universe.cs b/scripts/Universe.cs
index aa137d6..f75ba77 100644
--- a/scripts/Universe.cs
+++ b/scripts/Universe.cs
@@ -4,15 +4,18 @@ using Godot;
public class Universe
{
public Sector[,,] Sectors;
+ public Vector3I Size;
public Universe(Vector3I size)
{
Sectors = new Sector[size.X, size.Y, size.Z];
+ Size = size;
}
public Universe(int sizeX, int sizeY, int sizeZ)
{
Sectors = new Sector[sizeX, sizeY, sizeZ];
+ Size = new(sizeX, sizeY, sizeZ);
}
public Sector GetSector(Vector3I coordinates)
@@ -25,6 +28,32 @@ public class Universe
return Sectors[x, y, z];
}
+ 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 ||
+ sectorCoordinates.Y == 0 && localCoordinates.Y < -sectorSize.Y ||
+ sectorCoordinates.Z == 0 && localCoordinates.Z < -sectorSize.Z
+ )
+ {
+ return false;
+ }
+
+ 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
+ )
+ {
+ return false;
+ }
+
+ return true;
+ }
+
public void ForEachSector(Action action)
{
int sizeX = Sectors.GetLength(0);