Implement RPC Node
This commit is contained in:
parent
f7ee533d5a
commit
605e43273e
13 changed files with 524 additions and 206 deletions
|
|
@ -16,25 +16,29 @@ public partial class GameManager : Node3D
|
|||
[Export] public int maxFarThreads = 16;
|
||||
|
||||
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 Universe GameUniverse { 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 farTickTimer = 0;
|
||||
|
||||
public bool simulating = false;
|
||||
public bool simulatingClose = false;
|
||||
public bool simulatingFar = false;
|
||||
public bool playerReady = false;
|
||||
|
||||
private readonly Dictionary<GameObject, Node3D> spawnedObjects = [];
|
||||
private readonly ConcurrentQueue<GameObject> spawnQueue = [];
|
||||
|
||||
public override void _EnterTree()
|
||||
{
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
Singleton = this;
|
||||
|
||||
Generator = new TestGenerator();
|
||||
|
||||
if (Global.IsGameHost)
|
||||
|
|
@ -53,7 +57,7 @@ public partial class GameManager : Node3D
|
|||
public override void _ExitTree()
|
||||
{
|
||||
Loading = true;
|
||||
Singleton = null;
|
||||
Instance = null;
|
||||
}
|
||||
|
||||
public override void _Process(double delta)
|
||||
|
|
@ -64,20 +68,14 @@ public partial class GameManager : Node3D
|
|||
}
|
||||
|
||||
closeTickTimer += delta;
|
||||
farTickTimer += delta;
|
||||
|
||||
if (closeTickTimer >= closeTickInterval)
|
||||
if (closeTickTimer >= closeTickInterval && !simulatingClose)
|
||||
{
|
||||
SimulateClose(closeTickTimer);
|
||||
closeTickTimer = 0;
|
||||
}
|
||||
|
||||
if (simulating)
|
||||
{
|
||||
farTickTimer = 0;
|
||||
}
|
||||
|
||||
if (farTickTimer >= farTickInterval)
|
||||
farTickTimer += delta;
|
||||
if (farTickTimer >= farTickInterval && !simulatingFar)
|
||||
{
|
||||
double taskFarTickTimer = farTickTimer;
|
||||
Task.Run(() =>
|
||||
|
|
@ -94,22 +92,48 @@ public partial class GameManager : Node3D
|
|||
return MainPlayer.PlayerData.CurrentSector;
|
||||
}
|
||||
|
||||
public Player GetPlayer(long id)
|
||||
{
|
||||
Players.TryGetValue(id, out Player player);
|
||||
return player;
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
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)
|
||||
{
|
||||
simulating = true;
|
||||
simulatingFar = true;
|
||||
|
||||
List<Task> tasks = [];
|
||||
|
||||
|
|
@ -134,7 +158,7 @@ public partial class GameManager : Node3D
|
|||
|
||||
Task.WaitAll([.. tasks]);
|
||||
|
||||
simulating = false;
|
||||
simulatingFar = false;
|
||||
}
|
||||
|
||||
private void SimulateFarClustered(double delta, int startX, int countX)
|
||||
|
|
@ -195,7 +219,7 @@ public partial class GameManager : Node3D
|
|||
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.GameMenu = GameMenu;
|
||||
|
|
@ -206,7 +230,7 @@ public partial class GameManager : Node3D
|
|||
OnPlayerReady();
|
||||
}
|
||||
|
||||
Players.Add(player);
|
||||
Players.Add(networkId, player);
|
||||
|
||||
character.UpdateSector();
|
||||
|
||||
|
|
@ -262,10 +286,10 @@ public partial class GameManager : Node3D
|
|||
{
|
||||
Sector current = GetCurrentSector();
|
||||
|
||||
foreach (Player player in Players)
|
||||
foreach (KeyValuePair<long, Player> player in Players)
|
||||
{
|
||||
player.PlayerData.UpdateSectorOffset(current);
|
||||
player.PlayerData.UpdateNodePosition();
|
||||
player.Value.PlayerData.UpdateSectorOffset(current);
|
||||
player.Value.PlayerData.UpdateNodePosition();
|
||||
}
|
||||
|
||||
List<Sector> nearby = current.GetNeighbouringSectors();
|
||||
|
|
@ -281,20 +305,18 @@ public partial class GameManager : Node3D
|
|||
else
|
||||
{
|
||||
gameObject.UpdateSectorOffset(current);
|
||||
node.GlobalPosition = gameObject.LocalCoordinates + gameObject.SectorOffset;
|
||||
node._Process(0);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
|
@ -302,96 +324,4 @@ public partial class GameManager : Node3D
|
|||
|
||||
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 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 Vector3 SectorOffset { get; set; }
|
||||
public Vector3 SectorOffset { get; protected set; }
|
||||
|
||||
protected bool reassigning = false;
|
||||
|
||||
|
|
@ -21,6 +115,10 @@ public abstract class GameObject
|
|||
CurrentSector = sector;
|
||||
LocalCoordinates = localCoordinates;
|
||||
|
||||
Velocity = new(0, 0, 1);
|
||||
AngularVelocity = Vector3.Zero;
|
||||
Rotation = Vector3.Zero;
|
||||
|
||||
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()
|
||||
{
|
||||
return Helpers.IsInsideArea(CurrentSector.Size / 2, LocalCoordinates);
|
||||
|
|
@ -87,15 +191,15 @@ public abstract class GameObject
|
|||
|
||||
UpdateSectorOffsetToMainPlayer();
|
||||
|
||||
List<Sector> neighbours = GameManager.Singleton.GetCurrentSector().GetNeighbouringSectors();
|
||||
List<Sector> neighbours = GameManager.Instance.GetCurrentSector().GetNeighbouringSectors();
|
||||
|
||||
if (neighbours.Contains(sector))
|
||||
{
|
||||
GameManager.Singleton.Spawn(this);
|
||||
GameManager.Instance.Spawn(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
GameManager.Singleton.Despawn(this);
|
||||
GameManager.Instance.Despawn(this);
|
||||
}
|
||||
|
||||
reassigning = false;
|
||||
|
|
@ -114,7 +218,7 @@ public abstract class GameObject
|
|||
|
||||
public void UpdateSectorOffsetToMainPlayer()
|
||||
{
|
||||
UpdateSectorOffset(GameManager.Singleton.GetCurrentSector());
|
||||
UpdateSectorOffset(GameManager.Instance.GetCurrentSector());
|
||||
}
|
||||
|
||||
public void SetCoordinatesFromGlobal(Vector3Dec globalCoordinates)
|
||||
|
|
@ -142,6 +246,8 @@ public abstract class GameObject
|
|||
|
||||
public virtual void Simulate(double delta)
|
||||
{
|
||||
ApplyVelocity(delta);
|
||||
|
||||
if (!reassigning && !IsInCurrentSector())
|
||||
{
|
||||
UpdateSector();
|
||||
|
|
@ -157,4 +263,70 @@ public abstract class GameObject
|
|||
|
||||
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())
|
||||
{
|
||||
GameManager.Singleton.ApplyOrigin();
|
||||
|
||||
if (!Global.IsGameHost)
|
||||
{
|
||||
GameManager.Singleton.RpcId(
|
||||
1,
|
||||
nameof(GameManager.Singleton.RpcSendNearbySectors),
|
||||
CurrentSector.Coordinates,
|
||||
NetworkManager.Singleton.LocalNetId
|
||||
);
|
||||
}
|
||||
GameManager.Instance.ApplyOrigin();
|
||||
}
|
||||
|
||||
reassigning = false;
|
||||
|
|
@ -34,7 +24,7 @@ public class Character(Sector sector, Vector3 localCoordinates) : GameObject(sec
|
|||
|
||||
public bool IsMainPlayer()
|
||||
{
|
||||
return player == GameManager.Singleton.MainPlayer;
|
||||
return player == GameManager.Instance.MainPlayer;
|
||||
}
|
||||
|
||||
public Player InstantiatePlayer()
|
||||
|
|
@ -48,6 +38,14 @@ public class Character(Sector sector, Vector3 localCoordinates) : GameObject(sec
|
|||
return instance;
|
||||
}
|
||||
|
||||
public override void Simulate(double delta)
|
||||
{
|
||||
if (!reassigning && !IsInCurrentSector())
|
||||
{
|
||||
UpdateSector();
|
||||
}
|
||||
}
|
||||
|
||||
public override void UpdateNodePosition()
|
||||
{
|
||||
if (IsMainPlayer())
|
||||
|
|
|
|||
|
|
@ -4,13 +4,41 @@ public partial class StarNode : StaticBody3D
|
|||
{
|
||||
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()
|
||||
{
|
||||
GlobalPosition = StarData.LocalCoordinates + StarData.SectorOffset;
|
||||
GlobalRotation = StarData.Rotation;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
base.Simulate(delta);
|
||||
|
||||
//SetCoordinatesFromGlobal(new(GlobalCoordinates.X, GlobalCoordinates.Y, GlobalCoordinates.Z + 1 * (decimal)delta));
|
||||
}
|
||||
|
||||
public override Node3D Instantiate(Sector sector)
|
||||
|
|
@ -20,4 +18,22 @@ public class Star(Sector sector, Vector3 localCoordinates) : GameObject(sector,
|
|||
|
||||
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)
|
||||
{
|
||||
return new(
|
||||
(int)Mathf.Floor(UNIVERSE_SIZE.X / 2),
|
||||
(int)Mathf.Floor(UNIVERSE_SIZE.X / 2),
|
||||
(int)Mathf.Floor(UNIVERSE_SIZE.X / 2)
|
||||
(int)Mathf.Floor(universeSize.X / 2),
|
||||
(int)Mathf.Floor(universeSize.X / 2),
|
||||
(int)Mathf.Floor(universeSize.X / 2)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -118,10 +118,13 @@ public class TestGenerator : IGenerator
|
|||
|
||||
for (int i = 0; i < starCount; i++)
|
||||
{
|
||||
Vector3 localCoordinates = GenerateLocalCoordinates();
|
||||
Star star = GenerateStar(sector, localCoordinates);
|
||||
if (coordinates.X == 5 && coordinates.Y == 5 && coordinates.Z == 5)
|
||||
{
|
||||
Vector3 localCoordinates = GenerateLocalCoordinates();
|
||||
Star star = GenerateStar(sector, localCoordinates);
|
||||
|
||||
sector.GameObjects.Add(star);
|
||||
sector.GameObjects.Add(star);
|
||||
}
|
||||
}
|
||||
|
||||
int shipCount = rng.RandiRange(
|
||||
|
|
|
|||
|
|
@ -3,15 +3,13 @@ using Godot;
|
|||
|
||||
public partial class NetworkManager : Node
|
||||
{
|
||||
[Export] public RPCNode RPCNode { get; private set; }
|
||||
|
||||
public static NetworkManager Singleton { get; private set; }
|
||||
public static NetworkManager Instance { get; private set; }
|
||||
|
||||
public int LocalNetId { get; private set; } = -1;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
Singleton = this;
|
||||
Instance = this;
|
||||
|
||||
if (Global.IsGameHost)
|
||||
{
|
||||
|
|
@ -66,7 +64,7 @@ public partial class NetworkManager : Node
|
|||
{
|
||||
if (Global.IsGameHost)
|
||||
{
|
||||
GameManager.Singleton.SendUniverseToClient(id);
|
||||
GameManager.Instance.SendUniverseToClient(id);
|
||||
}
|
||||
|
||||
_ = SpawnNetPlayer(id);
|
||||
|
|
@ -75,7 +73,7 @@ public partial class NetworkManager : Node
|
|||
public void OnPlayerDisconnected(long id)
|
||||
{
|
||||
Player player = GetNode<Player>($"/root/Game/Space/Player-{id}");
|
||||
GameManager.Singleton.DespawnPlayer(player);
|
||||
GameManager.Instance.DespawnPlayer(player);
|
||||
}
|
||||
|
||||
private async Task SpawnNetPlayer(long id)
|
||||
|
|
@ -88,14 +86,9 @@ public partial class NetworkManager : Node
|
|||
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 player = GameManager.Instance.SpawnPlayer(character, id, 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,11 +10,11 @@ public partial class Player : CharacterBody3D
|
|||
[Export] public double NetworkPhysicsSyncInterval = 0.05;
|
||||
|
||||
public Control GameMenu { get; set; }
|
||||
|
||||
public Character PlayerData { get; set; }
|
||||
|
||||
private Vector3 gravityVelocity = Vector3.Zero;
|
||||
private Vector3 movementVelocity = Vector3.Zero;
|
||||
public Vector3 GravityVelocity { get; set; } = Vector3.Zero;
|
||||
public Vector3 MovementVelocity { get; set; } = Vector3.Zero;
|
||||
|
||||
private GravityReceiver gravityReceiver;
|
||||
private double cameraPitch = 0;
|
||||
private Camera3D camera;
|
||||
|
|
@ -35,18 +35,20 @@ public partial class Player : CharacterBody3D
|
|||
{
|
||||
if (IsMultiplayerAuthority())
|
||||
{
|
||||
RPCNode rpc = RPCNode.Instance;
|
||||
|
||||
networkPhysicsSyncCounter += delta;
|
||||
if (networkPhysicsSyncCounter >= NetworkPhysicsSyncInterval)
|
||||
{
|
||||
networkPhysicsSyncCounter = 0;
|
||||
Rpc(nameof(RpcSyncPhysics), movementVelocity, gravityVelocity, GlobalRotation);
|
||||
rpc.Rpc(nameof(rpc.RpcSyncPlayerPhysics), GetMultiplayerAuthority(), MovementVelocity, GravityVelocity, GlobalRotation);
|
||||
}
|
||||
|
||||
networkSyncCounter += delta;
|
||||
if (networkSyncCounter >= NetworkSyncInterval)
|
||||
{
|
||||
networkSyncCounter = 0;
|
||||
Rpc(nameof(RpcSync), GlobalPosition, PlayerData.CurrentSector.Coordinates);
|
||||
rpc.Rpc(nameof(rpc.RpcSyncPlayer), GetMultiplayerAuthority(), GlobalPosition, PlayerData.CurrentSector.Coordinates);
|
||||
}
|
||||
|
||||
PlayerData.SetCoordinatesFromLocal(GlobalPosition);
|
||||
|
|
@ -57,8 +59,8 @@ public partial class Player : CharacterBody3D
|
|||
|
||||
public override void _PhysicsProcess(double delta)
|
||||
{
|
||||
Vector3 newGravityVelocity = gravityVelocity;
|
||||
Vector3 newMovementVelocity = movementVelocity;
|
||||
Vector3 newGravityVelocity = GravityVelocity;
|
||||
Vector3 newMovementVelocity = MovementVelocity;
|
||||
|
||||
if (!GameMenu.Visible && IsMultiplayerAuthority())
|
||||
{
|
||||
|
|
@ -76,8 +78,8 @@ public partial class Player : CharacterBody3D
|
|||
}
|
||||
}
|
||||
|
||||
gravityVelocity = newGravityVelocity;
|
||||
movementVelocity = newMovementVelocity;
|
||||
GravityVelocity = newGravityVelocity;
|
||||
MovementVelocity = newMovementVelocity;
|
||||
Velocity = newGravityVelocity + newMovementVelocity;
|
||||
|
||||
MoveAndSlide();
|
||||
|
|
@ -237,33 +239,4 @@ 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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,11 +8,18 @@ public partial class QueueManager : Node
|
|||
public static ConcurrentQueue<Action> ActionQueue = 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 networkSyncQueueRateLimit = 10;
|
||||
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
if (!GameManager.Instance.playerReady)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while (LogQueue.TryDequeue(out string text))
|
||||
{
|
||||
GD.Print(text);
|
||||
|
|
@ -25,7 +32,7 @@ public partial class QueueManager : Node
|
|||
|
||||
int sectorReassignQueueProcessed = 0;
|
||||
while (
|
||||
!GameManager.Singleton.simulating
|
||||
!GameManager.Instance.simulatingFar
|
||||
&& sectorReassignQueueProcessed++ < sectorReassignQueueRateLimit
|
||||
&& SectorReassignQueue.TryDequeue(out var item)
|
||||
)
|
||||
|
|
@ -37,5 +44,16 @@ public partial class QueueManager : Node
|
|||
|
||||
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;
|
||||
|
||||
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 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)
|
||||
{
|
||||
return Helpers.IsInsideGlobalArea(GlobalStartCoordinates, GlobalEndCoordinates, gameObject.GlobalCoordinates);
|
||||
|
|
@ -58,7 +67,20 @@ public class Sector
|
|||
|
||||
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()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue