Implement RPC Node #25
13 changed files with 524 additions and 206 deletions
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://cbt6p1bhh4o8q" path="res://scripts/GameObjects/Nodes/StarNode.cs" id="1_o2f8k"]
|
[ext_resource type="Script" uid="uid://cbt6p1bhh4o8q" path="res://scripts/GameObjects/Nodes/StarNode.cs" id="1_o2f8k"]
|
||||||
|
|
||||||
[sub_resource type="SphereMesh" id="SphereMesh_o2f8k"]
|
[sub_resource type="SphereMesh" id="SphereMesh_2pyv3"]
|
||||||
radius = 5.0
|
radius = 5.0
|
||||||
height = 10.0
|
height = 10.0
|
||||||
|
|
||||||
|
|
@ -13,7 +13,7 @@ radius = 5.0
|
||||||
script = ExtResource("1_o2f8k")
|
script = ExtResource("1_o2f8k")
|
||||||
|
|
||||||
[node name="MeshInstance3D" type="MeshInstance3D" parent="." unique_id=1423841842]
|
[node name="MeshInstance3D" type="MeshInstance3D" parent="." unique_id=1423841842]
|
||||||
mesh = SubResource("SphereMesh_o2f8k")
|
mesh = SubResource("SphereMesh_2pyv3")
|
||||||
|
|
||||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="." unique_id=1898956937]
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="." unique_id=1898956937]
|
||||||
shape = SubResource("SphereShape3D_o2f8k")
|
shape = SubResource("SphereShape3D_o2f8k")
|
||||||
|
|
|
||||||
|
|
@ -55,9 +55,8 @@ GameMenu = NodePath("../GameMenu")
|
||||||
[node name="QueueManager" type="Node" parent="." unique_id=355148200]
|
[node name="QueueManager" type="Node" parent="." unique_id=355148200]
|
||||||
script = ExtResource("4_iywne")
|
script = ExtResource("4_iywne")
|
||||||
|
|
||||||
[node name="NetworkManager" type="Node" parent="." unique_id=1765485895 node_paths=PackedStringArray("RPCNode")]
|
[node name="NetworkManager" type="Node" parent="." unique_id=1765485895]
|
||||||
script = ExtResource("5_p57ef")
|
script = ExtResource("5_p57ef")
|
||||||
RPCNode = NodePath("../RPC")
|
|
||||||
|
|
||||||
[node name="RPC" type="Node" parent="." unique_id=498537245]
|
[node name="RPC" type="Node" parent="." unique_id=498537245]
|
||||||
script = ExtResource("6_u5sy4")
|
script = ExtResource("6_u5sy4")
|
||||||
|
|
|
||||||
|
|
@ -16,25 +16,29 @@ public partial class GameManager : Node3D
|
||||||
[Export] public int maxFarThreads = 16;
|
[Export] public int maxFarThreads = 16;
|
||||||
|
|
||||||
public static bool Loading { get; private set; } = true;
|
public static bool Loading { get; private set; } = true;
|
||||||
public static GameManager Singleton { get; private set; }
|
public static GameManager Instance { get; private set; }
|
||||||
public static IGenerator Generator { get; private set; }
|
public static IGenerator Generator { get; private set; }
|
||||||
public static Universe GameUniverse { get; private set; }
|
public static Universe GameUniverse { get; private set; }
|
||||||
public Player MainPlayer { get; private set; }
|
public Player MainPlayer { get; private set; }
|
||||||
public List<Player> Players { get; private set; } = [];
|
public Dictionary<long, Player> Players { get; private set; } = [];
|
||||||
|
|
||||||
private double closeTickTimer = 0;
|
private double closeTickTimer = 0;
|
||||||
private double farTickTimer = 0;
|
private double farTickTimer = 0;
|
||||||
|
|
||||||
public bool simulating = false;
|
public bool simulatingClose = false;
|
||||||
|
public bool simulatingFar = false;
|
||||||
public bool playerReady = false;
|
public bool playerReady = false;
|
||||||
|
|
||||||
private readonly Dictionary<GameObject, Node3D> spawnedObjects = [];
|
private readonly Dictionary<GameObject, Node3D> spawnedObjects = [];
|
||||||
private readonly ConcurrentQueue<GameObject> spawnQueue = [];
|
private readonly ConcurrentQueue<GameObject> spawnQueue = [];
|
||||||
|
|
||||||
|
public override void _EnterTree()
|
||||||
|
{
|
||||||
|
Instance = this;
|
||||||
|
}
|
||||||
|
|
||||||
public override void _Ready()
|
public override void _Ready()
|
||||||
{
|
{
|
||||||
Singleton = this;
|
|
||||||
|
|
||||||
Generator = new TestGenerator();
|
Generator = new TestGenerator();
|
||||||
|
|
||||||
if (Global.IsGameHost)
|
if (Global.IsGameHost)
|
||||||
|
|
@ -53,7 +57,7 @@ public partial class GameManager : Node3D
|
||||||
public override void _ExitTree()
|
public override void _ExitTree()
|
||||||
{
|
{
|
||||||
Loading = true;
|
Loading = true;
|
||||||
Singleton = null;
|
Instance = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void _Process(double delta)
|
public override void _Process(double delta)
|
||||||
|
|
@ -64,20 +68,14 @@ public partial class GameManager : Node3D
|
||||||
}
|
}
|
||||||
|
|
||||||
closeTickTimer += delta;
|
closeTickTimer += delta;
|
||||||
farTickTimer += delta;
|
if (closeTickTimer >= closeTickInterval && !simulatingClose)
|
||||||
|
|
||||||
if (closeTickTimer >= closeTickInterval)
|
|
||||||
{
|
{
|
||||||
SimulateClose(closeTickTimer);
|
SimulateClose(closeTickTimer);
|
||||||
closeTickTimer = 0;
|
closeTickTimer = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (simulating)
|
farTickTimer += delta;
|
||||||
{
|
if (farTickTimer >= farTickInterval && !simulatingFar)
|
||||||
farTickTimer = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (farTickTimer >= farTickInterval)
|
|
||||||
{
|
{
|
||||||
double taskFarTickTimer = farTickTimer;
|
double taskFarTickTimer = farTickTimer;
|
||||||
Task.Run(() =>
|
Task.Run(() =>
|
||||||
|
|
@ -94,22 +92,48 @@ public partial class GameManager : Node3D
|
||||||
return MainPlayer.PlayerData.CurrentSector;
|
return MainPlayer.PlayerData.CurrentSector;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Player GetPlayer(long id)
|
||||||
|
{
|
||||||
|
Players.TryGetValue(id, out Player player);
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
|
||||||
private void SimulateClose(double delta)
|
private void SimulateClose(double delta)
|
||||||
{
|
{
|
||||||
List<Sector> neighbours = GetCurrentSector().GetNeighbouringSectors();
|
simulatingClose = true;
|
||||||
|
|
||||||
neighbours.ForEach(sector =>
|
List<Sector> sectorsClose = GetCurrentSector().GetNeighbouringSectors();
|
||||||
|
|
||||||
|
List<Task> tasks = [];
|
||||||
|
sectorsClose.ForEach(sector =>
|
||||||
{
|
{
|
||||||
Task.Run(() =>
|
Task simulateTask = Task.Run(() =>
|
||||||
{
|
{
|
||||||
sector.Simulate(delta);
|
sector.Simulate(delta);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
tasks.Add(simulateTask);
|
||||||
});
|
});
|
||||||
|
Task.WaitAll([.. tasks]);
|
||||||
|
|
||||||
|
foreach (KeyValuePair<long, Player> player in Players)
|
||||||
|
{
|
||||||
|
List<Sector> playerSectors = player.Value.PlayerData.CurrentSector.GetNeighbouringSectors();
|
||||||
|
playerSectors.ForEach(sector =>
|
||||||
|
{
|
||||||
|
if (player.Key != 1)
|
||||||
|
{
|
||||||
|
sector.NetworkSync(player.Key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
simulatingClose = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SimulateFar(double delta)
|
private void SimulateFar(double delta)
|
||||||
{
|
{
|
||||||
simulating = true;
|
simulatingFar = true;
|
||||||
|
|
||||||
List<Task> tasks = [];
|
List<Task> tasks = [];
|
||||||
|
|
||||||
|
|
@ -134,7 +158,7 @@ public partial class GameManager : Node3D
|
||||||
|
|
||||||
Task.WaitAll([.. tasks]);
|
Task.WaitAll([.. tasks]);
|
||||||
|
|
||||||
simulating = false;
|
simulatingFar = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SimulateFarClustered(double delta, int startX, int countX)
|
private void SimulateFarClustered(double delta, int startX, int countX)
|
||||||
|
|
@ -195,7 +219,7 @@ public partial class GameManager : Node3D
|
||||||
neighbours.ForEach(sector => sector.SpawnObjects());
|
neighbours.ForEach(sector => sector.SpawnObjects());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Player SpawnPlayer(Character character, bool isMainPlayer = false)
|
public Player SpawnPlayer(Character character, long networkId, bool isMainPlayer = false)
|
||||||
{
|
{
|
||||||
Player player = character.InstantiatePlayer();
|
Player player = character.InstantiatePlayer();
|
||||||
player.GameMenu = GameMenu;
|
player.GameMenu = GameMenu;
|
||||||
|
|
@ -206,7 +230,7 @@ public partial class GameManager : Node3D
|
||||||
OnPlayerReady();
|
OnPlayerReady();
|
||||||
}
|
}
|
||||||
|
|
||||||
Players.Add(player);
|
Players.Add(networkId, player);
|
||||||
|
|
||||||
character.UpdateSector();
|
character.UpdateSector();
|
||||||
|
|
||||||
|
|
@ -262,10 +286,10 @@ public partial class GameManager : Node3D
|
||||||
{
|
{
|
||||||
Sector current = GetCurrentSector();
|
Sector current = GetCurrentSector();
|
||||||
|
|
||||||
foreach (Player player in Players)
|
foreach (KeyValuePair<long, Player> player in Players)
|
||||||
{
|
{
|
||||||
player.PlayerData.UpdateSectorOffset(current);
|
player.Value.PlayerData.UpdateSectorOffset(current);
|
||||||
player.PlayerData.UpdateNodePosition();
|
player.Value.PlayerData.UpdateNodePosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Sector> nearby = current.GetNeighbouringSectors();
|
List<Sector> nearby = current.GetNeighbouringSectors();
|
||||||
|
|
@ -281,20 +305,18 @@ public partial class GameManager : Node3D
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
gameObject.UpdateSectorOffset(current);
|
gameObject.UpdateSectorOffset(current);
|
||||||
node.GlobalPosition = gameObject.LocalCoordinates + gameObject.SectorOffset;
|
node._Process(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nearby.ForEach(sector => sector.SpawnObjects());
|
nearby.ForEach(sector => sector.SpawnObjects());
|
||||||
}
|
}
|
||||||
|
|
||||||
// These should be in RPCNode and should be queued
|
|
||||||
public void SendUniverseToClient(long id)
|
public void SendUniverseToClient(long id)
|
||||||
{
|
{
|
||||||
RpcId(id, nameof(RpcDownloadUniverse), Generator.GetUniverseSize(), Generator.GetSectorSize());
|
RpcId(id, nameof(RpcDownloadUniverse), Generator.GetUniverseSize(), Generator.GetSectorSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
// These should be in RPCNode and should be queued
|
|
||||||
[Rpc(MultiplayerApi.RpcMode.Authority)]
|
[Rpc(MultiplayerApi.RpcMode.Authority)]
|
||||||
public void RpcDownloadUniverse(Vector3I universeSize, Vector3 sectorSize)
|
public void RpcDownloadUniverse(Vector3I universeSize, Vector3 sectorSize)
|
||||||
{
|
{
|
||||||
|
|
@ -302,96 +324,4 @@ public partial class GameManager : Node3D
|
||||||
|
|
||||||
Loading = false;
|
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<Sector> 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,107 @@ using Godot;
|
||||||
|
|
||||||
public abstract class GameObject
|
public abstract class GameObject
|
||||||
{
|
{
|
||||||
public Guid UUID { get; set; }
|
[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 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 _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 _angularVelocity;
|
||||||
|
public Vector3 AngularVelocity
|
||||||
|
{
|
||||||
|
get => _angularVelocity;
|
||||||
|
protected set
|
||||||
|
{
|
||||||
|
if (_angularVelocity != value)
|
||||||
|
{
|
||||||
|
_angularVelocity = value;
|
||||||
|
DirtyBits |= DirtyFlags.AngularVelocity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Sector CurrentSector { get; protected set; }
|
|
||||||
public Vector3 LocalCoordinates { get; protected set; }
|
|
||||||
public Vector3Dec GlobalCoordinates { get; protected set; }
|
public Vector3Dec GlobalCoordinates { get; protected set; }
|
||||||
|
public Vector3 SectorOffset { get; protected set; }
|
||||||
public Vector3 SectorOffset { get; set; }
|
|
||||||
|
|
||||||
protected bool reassigning = false;
|
protected bool reassigning = false;
|
||||||
|
|
||||||
|
|
@ -21,6 +115,10 @@ public abstract class GameObject
|
||||||
CurrentSector = sector;
|
CurrentSector = sector;
|
||||||
LocalCoordinates = localCoordinates;
|
LocalCoordinates = localCoordinates;
|
||||||
|
|
||||||
|
Velocity = new(0, 0, 1);
|
||||||
|
AngularVelocity = Vector3.Zero;
|
||||||
|
Rotation = Vector3.Zero;
|
||||||
|
|
||||||
GlobalCoordinates = CalculateGlobalCoordinates(sector.GlobalCenterCoordinates, localCoordinates);
|
GlobalCoordinates = CalculateGlobalCoordinates(sector.GlobalCenterCoordinates, localCoordinates);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -46,6 +144,12 @@ public abstract class GameObject
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ApplyVelocity(double delta)
|
||||||
|
{
|
||||||
|
SetCoordinatesFromLocal(LocalCoordinates + Velocity * delta);
|
||||||
|
Rotation += AngularVelocity * delta;
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsInCurrentSector()
|
public bool IsInCurrentSector()
|
||||||
{
|
{
|
||||||
return Helpers.IsInsideArea(CurrentSector.Size / 2, LocalCoordinates);
|
return Helpers.IsInsideArea(CurrentSector.Size / 2, LocalCoordinates);
|
||||||
|
|
@ -87,15 +191,15 @@ public abstract class GameObject
|
||||||
|
|
||||||
UpdateSectorOffsetToMainPlayer();
|
UpdateSectorOffsetToMainPlayer();
|
||||||
|
|
||||||
List<Sector> neighbours = GameManager.Singleton.GetCurrentSector().GetNeighbouringSectors();
|
List<Sector> neighbours = GameManager.Instance.GetCurrentSector().GetNeighbouringSectors();
|
||||||
|
|
||||||
if (neighbours.Contains(sector))
|
if (neighbours.Contains(sector))
|
||||||
{
|
{
|
||||||
GameManager.Singleton.Spawn(this);
|
GameManager.Instance.Spawn(this);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
GameManager.Singleton.Despawn(this);
|
GameManager.Instance.Despawn(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
reassigning = false;
|
reassigning = false;
|
||||||
|
|
@ -114,7 +218,7 @@ public abstract class GameObject
|
||||||
|
|
||||||
public void UpdateSectorOffsetToMainPlayer()
|
public void UpdateSectorOffsetToMainPlayer()
|
||||||
{
|
{
|
||||||
UpdateSectorOffset(GameManager.Singleton.GetCurrentSector());
|
UpdateSectorOffset(GameManager.Instance.GetCurrentSector());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetCoordinatesFromGlobal(Vector3Dec globalCoordinates)
|
public void SetCoordinatesFromGlobal(Vector3Dec globalCoordinates)
|
||||||
|
|
@ -142,6 +246,8 @@ public abstract class GameObject
|
||||||
|
|
||||||
public virtual void Simulate(double delta)
|
public virtual void Simulate(double delta)
|
||||||
{
|
{
|
||||||
|
ApplyVelocity(delta);
|
||||||
|
|
||||||
if (!reassigning && !IsInCurrentSector())
|
if (!reassigning && !IsInCurrentSector())
|
||||||
{
|
{
|
||||||
UpdateSector();
|
UpdateSector();
|
||||||
|
|
@ -157,4 +263,70 @@ public abstract class GameObject
|
||||||
|
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual Godot.Collections.Dictionary NetworkWrite(long id, bool full)
|
||||||
|
{
|
||||||
|
Godot.Collections.Dictionary gameObjectData = [];
|
||||||
|
|
||||||
|
if (full)
|
||||||
|
gameObjectData.Add("type", GetType().ToString());
|
||||||
|
|
||||||
|
if (DirtyBits.HasFlag(DirtyFlags.Sector) || full)
|
||||||
|
gameObjectData.Add("sectorCoordinates", _currentSectorCoordinates);
|
||||||
|
|
||||||
|
if (DirtyBits.HasFlag(DirtyFlags.LocalCoordinates) || full)
|
||||||
|
gameObjectData.Add("localCoordinates", _localCoordinates);
|
||||||
|
|
||||||
|
if (DirtyBits.HasFlag(DirtyFlags.Rotation) || full)
|
||||||
|
gameObjectData.Add("rotation", _rotation);
|
||||||
|
|
||||||
|
if (DirtyBits.HasFlag(DirtyFlags.Velocity) || full)
|
||||||
|
gameObjectData.Add("velocity", _velocity);
|
||||||
|
|
||||||
|
if (DirtyBits.HasFlag(DirtyFlags.AngularVelocity) || full)
|
||||||
|
gameObjectData.Add("angularVelocity", _angularVelocity);
|
||||||
|
|
||||||
|
if (gameObjectData.Count > 0)
|
||||||
|
{
|
||||||
|
gameObjectData.Add("uuid", UUID.ToString());
|
||||||
|
return gameObjectData;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
AssignSector(newSector);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gameObjectData.TryGetValue("localCoordinates", out var localCoordinatesData))
|
||||||
|
LocalCoordinates = (Vector3)localCoordinatesData;
|
||||||
|
|
||||||
|
if (gameObjectData.TryGetValue("rotation", out var rotationData))
|
||||||
|
Rotation = (Vector3)rotationData;
|
||||||
|
|
||||||
|
if (gameObjectData.TryGetValue("velocity", out var velocityData))
|
||||||
|
Velocity = (Vector3)velocityData;
|
||||||
|
|
||||||
|
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 int GetHashCode()
|
||||||
|
{
|
||||||
|
return UUID.GetHashCode();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,17 +16,7 @@ public class Character(Sector sector, Vector3 localCoordinates) : GameObject(sec
|
||||||
|
|
||||||
if (IsMainPlayer())
|
if (IsMainPlayer())
|
||||||
{
|
{
|
||||||
GameManager.Singleton.ApplyOrigin();
|
GameManager.Instance.ApplyOrigin();
|
||||||
|
|
||||||
if (!Global.IsGameHost)
|
|
||||||
{
|
|
||||||
GameManager.Singleton.RpcId(
|
|
||||||
1,
|
|
||||||
nameof(GameManager.Singleton.RpcSendNearbySectors),
|
|
||||||
CurrentSector.Coordinates,
|
|
||||||
NetworkManager.Singleton.LocalNetId
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
reassigning = false;
|
reassigning = false;
|
||||||
|
|
@ -34,7 +24,7 @@ public class Character(Sector sector, Vector3 localCoordinates) : GameObject(sec
|
||||||
|
|
||||||
public bool IsMainPlayer()
|
public bool IsMainPlayer()
|
||||||
{
|
{
|
||||||
return player == GameManager.Singleton.MainPlayer;
|
return player == GameManager.Instance.MainPlayer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Player InstantiatePlayer()
|
public Player InstantiatePlayer()
|
||||||
|
|
@ -48,6 +38,14 @@ public class Character(Sector sector, Vector3 localCoordinates) : GameObject(sec
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Simulate(double delta)
|
||||||
|
{
|
||||||
|
if (!reassigning && !IsInCurrentSector())
|
||||||
|
{
|
||||||
|
UpdateSector();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override void UpdateNodePosition()
|
public override void UpdateNodePosition()
|
||||||
{
|
{
|
||||||
if (IsMainPlayer())
|
if (IsMainPlayer())
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,41 @@ public partial class StarNode : StaticBody3D
|
||||||
{
|
{
|
||||||
public Star StarData { get; set; }
|
public Star StarData { get; set; }
|
||||||
|
|
||||||
|
private Vector3 lastFrameCoordinates = Vector3.Zero;
|
||||||
|
private Vector3 lastFrameRotation = Vector3.Zero;
|
||||||
|
|
||||||
|
private Vector3 velocityOffset = Vector3.Zero;
|
||||||
|
private Vector3 angularVelocityOffset = Vector3.Zero;
|
||||||
|
|
||||||
public override void _Ready()
|
public override void _Ready()
|
||||||
{
|
{
|
||||||
GlobalPosition = StarData.LocalCoordinates + StarData.SectorOffset;
|
GlobalPosition = StarData.LocalCoordinates + StarData.SectorOffset;
|
||||||
|
GlobalRotation = StarData.Rotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void _Process(double delta)
|
public override void _Process(double delta)
|
||||||
{
|
{
|
||||||
GlobalPosition = StarData.LocalCoordinates + StarData.SectorOffset;
|
if (StarData.LocalCoordinates == lastFrameCoordinates)
|
||||||
|
{
|
||||||
|
velocityOffset += StarData.Velocity * delta;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lastFrameCoordinates = StarData.LocalCoordinates;
|
||||||
|
velocityOffset = Vector3.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StarData.Rotation == lastFrameRotation)
|
||||||
|
{
|
||||||
|
angularVelocityOffset += StarData.AngularVelocity * delta;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lastFrameRotation = StarData.Rotation;
|
||||||
|
angularVelocityOffset = Vector3.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
GlobalPosition = StarData.LocalCoordinates + StarData.SectorOffset + velocityOffset;
|
||||||
|
GlobalRotation = StarData.Rotation + angularVelocityOffset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,6 @@ public class Star(Sector sector, Vector3 localCoordinates) : GameObject(sector,
|
||||||
public override void Simulate(double delta)
|
public override void Simulate(double delta)
|
||||||
{
|
{
|
||||||
base.Simulate(delta);
|
base.Simulate(delta);
|
||||||
|
|
||||||
//SetCoordinatesFromGlobal(new(GlobalCoordinates.X, GlobalCoordinates.Y, GlobalCoordinates.Z + 1 * (decimal)delta));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Node3D Instantiate(Sector sector)
|
public override Node3D Instantiate(Sector sector)
|
||||||
|
|
@ -20,4 +18,22 @@ public class Star(Sector sector, Vector3 localCoordinates) : GameObject(sector,
|
||||||
|
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override Godot.Collections.Dictionary NetworkWrite(long id, bool full)
|
||||||
|
{
|
||||||
|
Godot.Collections.Dictionary gameObjectData = base.NetworkWrite(id, full);
|
||||||
|
|
||||||
|
if (gameObjectData != null)
|
||||||
|
{
|
||||||
|
QueueManager.NetworkSyncQueue.Enqueue((id, gameObjectData));
|
||||||
|
}
|
||||||
|
DirtyBits = DirtyFlags.None;
|
||||||
|
|
||||||
|
return gameObjectData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void NetworkRead(Godot.Collections.Dictionary gameObjectData)
|
||||||
|
{
|
||||||
|
base.NetworkRead(gameObjectData);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,9 +30,9 @@ public class TestGenerator : IGenerator
|
||||||
public Vector3I GetSectorOffset(Vector3I universeSize)
|
public Vector3I GetSectorOffset(Vector3I universeSize)
|
||||||
{
|
{
|
||||||
return new(
|
return new(
|
||||||
(int)Mathf.Floor(UNIVERSE_SIZE.X / 2),
|
(int)Mathf.Floor(universeSize.X / 2),
|
||||||
(int)Mathf.Floor(UNIVERSE_SIZE.X / 2),
|
(int)Mathf.Floor(universeSize.X / 2),
|
||||||
(int)Mathf.Floor(UNIVERSE_SIZE.X / 2)
|
(int)Mathf.Floor(universeSize.X / 2)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -118,10 +118,13 @@ public class TestGenerator : IGenerator
|
||||||
|
|
||||||
for (int i = 0; i < starCount; i++)
|
for (int i = 0; i < starCount; i++)
|
||||||
{
|
{
|
||||||
Vector3 localCoordinates = GenerateLocalCoordinates();
|
if (coordinates.X == 5 && coordinates.Y == 5 && coordinates.Z == 5)
|
||||||
Star star = GenerateStar(sector, localCoordinates);
|
{
|
||||||
|
Vector3 localCoordinates = GenerateLocalCoordinates();
|
||||||
|
Star star = GenerateStar(sector, localCoordinates);
|
||||||
|
|
||||||
sector.GameObjects.Add(star);
|
sector.GameObjects.Add(star);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int shipCount = rng.RandiRange(
|
int shipCount = rng.RandiRange(
|
||||||
|
|
|
||||||
|
|
@ -3,15 +3,13 @@ using Godot;
|
||||||
|
|
||||||
public partial class NetworkManager : Node
|
public partial class NetworkManager : Node
|
||||||
{
|
{
|
||||||
[Export] public RPCNode RPCNode { get; private set; }
|
public static NetworkManager Instance { get; private set; }
|
||||||
|
|
||||||
public static NetworkManager Singleton { get; private set; }
|
|
||||||
|
|
||||||
public int LocalNetId { get; private set; } = -1;
|
public int LocalNetId { get; private set; } = -1;
|
||||||
|
|
||||||
public override void _Ready()
|
public override void _Ready()
|
||||||
{
|
{
|
||||||
Singleton = this;
|
Instance = this;
|
||||||
|
|
||||||
if (Global.IsGameHost)
|
if (Global.IsGameHost)
|
||||||
{
|
{
|
||||||
|
|
@ -66,7 +64,7 @@ public partial class NetworkManager : Node
|
||||||
{
|
{
|
||||||
if (Global.IsGameHost)
|
if (Global.IsGameHost)
|
||||||
{
|
{
|
||||||
GameManager.Singleton.SendUniverseToClient(id);
|
GameManager.Instance.SendUniverseToClient(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = SpawnNetPlayer(id);
|
_ = SpawnNetPlayer(id);
|
||||||
|
|
@ -75,7 +73,7 @@ public partial class NetworkManager : Node
|
||||||
public void OnPlayerDisconnected(long id)
|
public void OnPlayerDisconnected(long id)
|
||||||
{
|
{
|
||||||
Player player = GetNode<Player>($"/root/Game/Space/Player-{id}");
|
Player player = GetNode<Player>($"/root/Game/Space/Player-{id}");
|
||||||
GameManager.Singleton.DespawnPlayer(player);
|
GameManager.Instance.DespawnPlayer(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SpawnNetPlayer(long id)
|
private async Task SpawnNetPlayer(long id)
|
||||||
|
|
@ -88,14 +86,9 @@ public partial class NetworkManager : Node
|
||||||
bool isMainPlayer = LocalNetId == id;
|
bool isMainPlayer = LocalNetId == id;
|
||||||
|
|
||||||
Character character = new(GameManager.GameUniverse.Sectors[5, 5, 5], new(0, 0, 0));
|
Character character = new(GameManager.GameUniverse.Sectors[5, 5, 5], new(0, 0, 0));
|
||||||
Player player = GameManager.Singleton.SpawnPlayer(character, isMainPlayer);
|
Player player = GameManager.Instance.SpawnPlayer(character, id, isMainPlayer);
|
||||||
player.Name = $"Player-{id}";
|
player.Name = $"Player-{id}";
|
||||||
|
|
||||||
player.SetMultiplayerAuthority((int)id);
|
player.SetMultiplayerAuthority((int)id);
|
||||||
|
|
||||||
if (isMainPlayer && !Global.IsGameHost)
|
|
||||||
{
|
|
||||||
GameManager.Singleton.RpcId(1, nameof(GameManager.Singleton.RpcSendNearbySectors), character.CurrentSector.Coordinates, id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,11 @@ public partial class Player : CharacterBody3D
|
||||||
[Export] public double NetworkPhysicsSyncInterval = 0.05;
|
[Export] public double NetworkPhysicsSyncInterval = 0.05;
|
||||||
|
|
||||||
public Control GameMenu { get; set; }
|
public Control GameMenu { get; set; }
|
||||||
|
|
||||||
public Character PlayerData { get; set; }
|
public Character PlayerData { get; set; }
|
||||||
|
|
||||||
private Vector3 gravityVelocity = Vector3.Zero;
|
public Vector3 GravityVelocity { get; set; } = Vector3.Zero;
|
||||||
private Vector3 movementVelocity = Vector3.Zero;
|
public Vector3 MovementVelocity { get; set; } = Vector3.Zero;
|
||||||
|
|
||||||
private GravityReceiver gravityReceiver;
|
private GravityReceiver gravityReceiver;
|
||||||
private double cameraPitch = 0;
|
private double cameraPitch = 0;
|
||||||
private Camera3D camera;
|
private Camera3D camera;
|
||||||
|
|
@ -35,18 +35,20 @@ public partial class Player : CharacterBody3D
|
||||||
{
|
{
|
||||||
if (IsMultiplayerAuthority())
|
if (IsMultiplayerAuthority())
|
||||||
{
|
{
|
||||||
|
RPCNode rpc = RPCNode.Instance;
|
||||||
|
|
||||||
networkPhysicsSyncCounter += delta;
|
networkPhysicsSyncCounter += delta;
|
||||||
if (networkPhysicsSyncCounter >= NetworkPhysicsSyncInterval)
|
if (networkPhysicsSyncCounter >= NetworkPhysicsSyncInterval)
|
||||||
{
|
{
|
||||||
networkPhysicsSyncCounter = 0;
|
networkPhysicsSyncCounter = 0;
|
||||||
Rpc(nameof(RpcSyncPhysics), movementVelocity, gravityVelocity, GlobalRotation);
|
rpc.Rpc(nameof(rpc.RpcSyncPlayerPhysics), GetMultiplayerAuthority(), MovementVelocity, GravityVelocity, GlobalRotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
networkSyncCounter += delta;
|
networkSyncCounter += delta;
|
||||||
if (networkSyncCounter >= NetworkSyncInterval)
|
if (networkSyncCounter >= NetworkSyncInterval)
|
||||||
{
|
{
|
||||||
networkSyncCounter = 0;
|
networkSyncCounter = 0;
|
||||||
Rpc(nameof(RpcSync), GlobalPosition, PlayerData.CurrentSector.Coordinates);
|
rpc.Rpc(nameof(rpc.RpcSyncPlayer), GetMultiplayerAuthority(), GlobalPosition, PlayerData.CurrentSector.Coordinates);
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayerData.SetCoordinatesFromLocal(GlobalPosition);
|
PlayerData.SetCoordinatesFromLocal(GlobalPosition);
|
||||||
|
|
@ -57,8 +59,8 @@ public partial class Player : CharacterBody3D
|
||||||
|
|
||||||
public override void _PhysicsProcess(double delta)
|
public override void _PhysicsProcess(double delta)
|
||||||
{
|
{
|
||||||
Vector3 newGravityVelocity = gravityVelocity;
|
Vector3 newGravityVelocity = GravityVelocity;
|
||||||
Vector3 newMovementVelocity = movementVelocity;
|
Vector3 newMovementVelocity = MovementVelocity;
|
||||||
|
|
||||||
if (!GameMenu.Visible && IsMultiplayerAuthority())
|
if (!GameMenu.Visible && IsMultiplayerAuthority())
|
||||||
{
|
{
|
||||||
|
|
@ -76,8 +78,8 @@ public partial class Player : CharacterBody3D
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gravityVelocity = newGravityVelocity;
|
GravityVelocity = newGravityVelocity;
|
||||||
movementVelocity = newMovementVelocity;
|
MovementVelocity = newMovementVelocity;
|
||||||
Velocity = newGravityVelocity + newMovementVelocity;
|
Velocity = newGravityVelocity + newMovementVelocity;
|
||||||
|
|
||||||
MoveAndSlide();
|
MoveAndSlide();
|
||||||
|
|
@ -237,33 +239,4 @@ public partial class Player : CharacterBody3D
|
||||||
|
|
||||||
RotateObjectLocal(Vector3.Forward, rotateAmountZ);
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,18 @@ public partial class QueueManager : Node
|
||||||
public static ConcurrentQueue<Action> ActionQueue = new();
|
public static ConcurrentQueue<Action> ActionQueue = new();
|
||||||
|
|
||||||
public static ConcurrentQueue<(Sector, GameObject)> SectorReassignQueue = new();
|
public static ConcurrentQueue<(Sector, GameObject)> SectorReassignQueue = new();
|
||||||
|
public static ConcurrentQueue<(long, Godot.Collections.Dictionary)> NetworkSyncQueue = new();
|
||||||
|
|
||||||
private readonly int sectorReassignQueueRateLimit = 500;
|
private readonly int sectorReassignQueueRateLimit = 500;
|
||||||
|
private readonly int networkSyncQueueRateLimit = 10;
|
||||||
|
|
||||||
public override void _Process(double delta)
|
public override void _Process(double delta)
|
||||||
{
|
{
|
||||||
|
if (!GameManager.Instance.playerReady)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
while (LogQueue.TryDequeue(out string text))
|
while (LogQueue.TryDequeue(out string text))
|
||||||
{
|
{
|
||||||
GD.Print(text);
|
GD.Print(text);
|
||||||
|
|
@ -25,7 +32,7 @@ public partial class QueueManager : Node
|
||||||
|
|
||||||
int sectorReassignQueueProcessed = 0;
|
int sectorReassignQueueProcessed = 0;
|
||||||
while (
|
while (
|
||||||
!GameManager.Singleton.simulating
|
!GameManager.Instance.simulatingFar
|
||||||
&& sectorReassignQueueProcessed++ < sectorReassignQueueRateLimit
|
&& sectorReassignQueueProcessed++ < sectorReassignQueueRateLimit
|
||||||
&& SectorReassignQueue.TryDequeue(out var item)
|
&& SectorReassignQueue.TryDequeue(out var item)
|
||||||
)
|
)
|
||||||
|
|
@ -37,5 +44,16 @@ public partial class QueueManager : Node
|
||||||
|
|
||||||
gameObject.AssignSector(sector);
|
gameObject.AssignSector(sector);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int networkSyncQueueProcessed = 0;
|
||||||
|
while (
|
||||||
|
networkSyncQueueProcessed++ < networkSyncQueueRateLimit
|
||||||
|
&& NetworkSyncQueue.TryDequeue(out var item)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var (clientId, gameObjectData) = item;
|
||||||
|
|
||||||
|
RPCNode.Instance.RpcId(clientId, nameof(RPCNode.RpcSyncGameObject), gameObjectData);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,171 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using Godot;
|
using Godot;
|
||||||
|
|
||||||
public partial class RPCNode : Node
|
public partial class RPCNode : Node
|
||||||
{
|
{
|
||||||
|
public static RPCNode Instance { get; private set; }
|
||||||
|
|
||||||
|
private readonly HashSet<Guid> requestedFullGameObjects = [];
|
||||||
|
|
||||||
|
public override void _EnterTree()
|
||||||
|
{
|
||||||
|
Instance = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void _ExitTree()
|
||||||
|
{
|
||||||
|
Instance = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Rpc(MultiplayerApi.RpcMode.Authority)]
|
||||||
|
public void RpcSyncGameObject(Godot.Collections.Dictionary gameObjectData)
|
||||||
|
{
|
||||||
|
GD.Print("READING: " + gameObjectData);
|
||||||
|
|
||||||
|
if (!GameManager.Instance.playerReady)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gameObjectData.TryGetValue("uuid", out var uuidData))
|
||||||
|
return;
|
||||||
|
|
||||||
|
Guid uuid = Guid.Parse((string)uuidData);
|
||||||
|
|
||||||
|
List<Sector> sectors = GameManager.Instance.GetCurrentSector().GetNeighbouringSectors();
|
||||||
|
foreach (Sector sector in sectors)
|
||||||
|
{
|
||||||
|
GameObject gameObject = sector.GetObjectById(uuid);
|
||||||
|
if (gameObject != null)
|
||||||
|
{
|
||||||
|
gameObject.NetworkRead(gameObjectData);
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
if (!gameObjectData.TryGetValue("type", out var typeData))
|
||||||
|
return false;
|
||||||
|
if (!gameObjectData.TryGetValue("sectorCoordinates", out var sectorCoordinatesData))
|
||||||
|
return false;
|
||||||
|
if (!gameObjectData.TryGetValue("localCoordinates", out var localCoordinatesData))
|
||||||
|
return false;
|
||||||
|
if (!gameObjectData.TryGetValue("uuid", out var uuidData))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
string type = (string)typeData;
|
||||||
|
Vector3I sectorCoordinates = (Vector3I)sectorCoordinatesData;
|
||||||
|
Vector3 localCoordinates = (Vector3)localCoordinatesData;
|
||||||
|
Guid uuid = Guid.Parse((string)uuidData);
|
||||||
|
|
||||||
|
Sector sector = GameManager.GameUniverse.GetSector(sectorCoordinates);
|
||||||
|
if (sector == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
GameObject gameObject;
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case "Star":
|
||||||
|
gameObject = new Star(sector, localCoordinates);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Rpc(MultiplayerApi.RpcMode.AnyPeer)]
|
||||||
|
public void RpcSyncPlayer(long id, Vector3 position, Vector3I sectorCoordinates)
|
||||||
|
{
|
||||||
|
if (!GameManager.Instance.playerReady)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GameManager.Instance.Players.TryGetValue(id, out Player player);
|
||||||
|
if (player == null || !player.IsInsideTree())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Character playerData = player.PlayerData;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
player.GlobalPosition = position + playerData.SectorOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Rpc(MultiplayerApi.RpcMode.AnyPeer)]
|
||||||
|
public void RpcSyncPlayerPhysics(long id, Vector3 _movementVelocity, Vector3 _gravityVelocity, Vector3 rotation)
|
||||||
|
{
|
||||||
|
if (!GameManager.Instance.playerReady)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GameManager.Instance.Players.TryGetValue(id, out Player player);
|
||||||
|
if (player == null || !player.IsInsideTree())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
player.MovementVelocity = _movementVelocity;
|
||||||
|
player.GravityVelocity = _gravityVelocity;
|
||||||
|
|
||||||
|
player.GlobalRotation = rotation;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Godot;
|
using Godot;
|
||||||
|
|
||||||
|
|
@ -46,6 +47,14 @@ public class Sector
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void NetworkSync(long id)
|
||||||
|
{
|
||||||
|
GameObjects.ForEach(gameObject =>
|
||||||
|
{
|
||||||
|
gameObject.NetworkWrite(id, false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsObjectInSector(GameObject gameObject)
|
public bool IsObjectInSector(GameObject gameObject)
|
||||||
{
|
{
|
||||||
return Helpers.IsInsideGlobalArea(GlobalStartCoordinates, GlobalEndCoordinates, gameObject.GlobalCoordinates);
|
return Helpers.IsInsideGlobalArea(GlobalStartCoordinates, GlobalEndCoordinates, gameObject.GlobalCoordinates);
|
||||||
|
|
@ -58,7 +67,20 @@ public class Sector
|
||||||
|
|
||||||
public void SpawnObjects()
|
public void SpawnObjects()
|
||||||
{
|
{
|
||||||
GameObjects.ForEach(GameManager.Singleton.Spawn);
|
GameObjects.ForEach(GameManager.Instance.Spawn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameObject GetObjectById(Guid id)
|
||||||
|
{
|
||||||
|
foreach (GameObject gameObject in GameObjects)
|
||||||
|
{
|
||||||
|
if (gameObject.UUID.Equals(id))
|
||||||
|
{
|
||||||
|
return gameObject;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Sector> GetNeighbouringSectors()
|
public List<Sector> GetNeighbouringSectors()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue