@ -13,8 +13,11 @@ public static class NetworkSync
private static readonly List < SyncObjectInfo > _ infoByID = new List < SyncObjectInfo > ( ) ;
private static readonly Dictionary < Type , SyncObjectInfo > _ infoByType = new Dictionary < Type , SyncObjectInfo > ( ) ;
private static readonly Dictionary < ( Game , uint ) , SyncStatus > _ statusBySyncID = new Dictionary < ( Game , uint ) , SyncStatus > ( ) ;
private static readonly Dictionary < Node , SyncStatus > _ statusByObject = new Dictionary < Node , SyncStatus > ( ) ;
// TODO: Rework NetworkSync to be an instance on the Game object.
private static readonly Dictionary < uint , SyncStatus > _ serverStatusBySyncID = new Dictionary < uint , SyncStatus > ( ) ;
private static readonly Dictionary < Node , SyncStatus > _ serverStatusByObject = new Dictionary < Node , SyncStatus > ( ) ;
private static readonly Dictionary < uint , SyncStatus > _ clientStatusBySyncID = new Dictionary < uint , SyncStatus > ( ) ;
private static readonly Dictionary < Node , SyncStatus > _ clientStatusByObject = new Dictionary < Node , SyncStatus > ( ) ;
private static readonly HashSet < SyncStatus > _d irtyObjects = new HashSet < SyncStatus > ( ) ;
private static uint _ syncIDCounter = 1 ;
@ -33,8 +36,8 @@ public static class NetworkSync
var obj = info . InstanceScene . Init < T > ( ) ;
var status = new SyncStatus ( _ syncIDCounter + + , obj , info ) { Special = Special . Spawn } ;
_ statusBySyncID . Add ( ( server , status . SyncID ) , status ) ;
_ statusByObject . Add ( status . Object , status ) ;
_ serverS tatusBySyncID . Add ( status . SyncID , status ) ;
_ serverS tatusByObject . Add ( status . Object , status ) ;
_d irtyObjects . Add ( status ) ;
server . GetNode ( info . ContainerNodePath ) . AddChild ( obj ) ;
@ -47,8 +50,8 @@ public static class NetworkSync
if ( ! ( obj . GetGame ( ) is Server ) ) return ;
status . Special = Special . Destroy ;
_ statusBySyncID . Remove ( ( obj . GetGame ( ) , status . SyncID ) ) ;
_ statusByObject . Remove ( status . Object ) ;
_ serverS tatusBySyncID . Remove ( status . SyncID ) ;
_ serverS tatusByObject . Remove ( status . Object ) ;
_d irtyObjects . Add ( status ) ;
obj . GetParent ( ) . RemoveChild ( obj ) ;
@ -82,6 +85,8 @@ public static class NetworkSync
if ( ( status . DirtyProperties & ( 1 < < prop . ID ) ) ! = 0 )
values . Add ( ( prop . ID , prop . Getter ( status . Object ) ) ) ;
packet . Changes . Add ( new SyncPacket . Object ( status . Info . ID , status . SyncID , status . Special , values ) ) ;
// If the object has been newly spawned, now is the time to remove the "Spawn" flag.
if ( status . Special = = Special . Spawn ) status . Special = Special . None ;
}
// TODO: Need a different way to send packages to all *properly* connected peers.
NetworkPackets . Send ( server , server . CustomMultiplayer . GetNetworkConnectedPeers ( ) . Select ( id = > new NetworkID ( id ) ) , packet ) ;
@ -92,25 +97,28 @@ public static class NetworkSync
internal static void SendAllObjects ( Server server , NetworkID networkID )
{
var packet = new SyncPacket ( ) ;
foreach ( var status in _ statusByObject . Values ) {
foreach ( var status in _ serverS tatusByObject . Values ) {
var values = new List < ( byte , object ) > ( ) ;
foreach ( var prop in status . Info . PropertiesByID )
values . Add ( ( prop . ID , prop . Getter ( status . Object ) ) ) ;
packet . Changes . Add ( new SyncPacket . Object ( status . Info . ID , status . SyncID , status . Special , values ) ) ;
packet . Changes . Add ( new SyncPacket . Object ( status . Info . ID , status . SyncID , Special . Spawn , values ) ) ;
}
NetworkPackets . Send ( server , new [ ] { networkID } , packet ) ;
}
internal static void ClearAllObjects ( )
internal static void ClearAllObjects ( Game game )
{
foreach ( var ( node , _ ) in _ statusByObject ) {
var statusByObject = ( game is Server ) ? _ serverStatusByObject : _ clientStatusByObject ;
var statusBySyncID = ( game is Server ) ? _ serverStatusBySyncID : _ clientStatusBySyncID ;
foreach ( var ( node , _ ) in statusByObject ) {
if ( ! Godot . Object . IsInstanceValid ( node ) ) continue ;
node . GetParent ( ) . RemoveChild ( node ) ;
node . QueueFree ( ) ;
}
_ statusByObject . Clear ( ) ;
_ statusBySyncID . Clear ( ) ;
statusByObject . Clear ( ) ;
statusBySyncID . Clear ( ) ;
_d irtyObjects . Clear ( ) ;
_ syncIDCounter = 1 ;
}
@ -118,13 +126,17 @@ public static class NetworkSync
public static uint GetSyncID ( this Node obj )
= > GetSyncStatus ( obj ) . SyncID ;
public static Node GetObjectBySyncID ( this Game game , uint syncID )
= > _ statusBySyncID . TryGetValue ( ( game , syncID ) , out var value ) ? value . Object : null ;
{
var statusBySyncID = ( game is Server ) ? _ serverStatusBySyncID : _ clientStatusBySyncID ;
return statusBySyncID . TryGetValue ( syncID , out var value ) ? value . Object : null ;
}
private static SyncStatus GetSyncStatus ( Node obj )
{
if ( obj . GetType ( ) . GetCustomAttribute < SyncObjectAttribute > ( ) = = null )
throw new ArgumentException ( $"Type {obj.GetType()} is missing {nameof(SyncObjectAttribute)}" ) ;
if ( ! _ statusByObject . TryGetValue ( obj , out var value ) ) throw new Exception (
var statusByObject = ( obj . GetGame ( ) is Server ) ? _ serverStatusByObject : _ clientStatusByObject ;
if ( ! statusByObject . TryGetValue ( obj , out var value ) ) throw new Exception (
$"No {nameof(SyncStatus)} found for '{obj.Name}' ({obj.GetType()})" ) ;
return value ;
}
@ -238,22 +250,24 @@ public static class NetworkSync
$"Unknown {nameof(SyncObjectInfo)} with ID {packetObj.InfoID}" ) ;
var info = _ infoByID [ packetObj . InfoID ] ;
if ( ! _ s tatusBySyncID. TryGetValue ( ( game , packetObj . SyncID ) , out var status ) ) {
if ( packetObj . Special ! = Special . Spawn )
throw new Exception ( $"Unknown synced object with ID {packetObj.SyncID} ") ;
if ( ! _ clientS tatusBySyncID. TryGetValue ( packetObj . SyncID , out var status ) ) {
if ( packetObj . Special ! = Special . Spawn ) throw new Exception (
$"Unknown synced object {info.Name} (ID {packetObj.SyncID}) ") ;
var obj = info . InstanceScene . Init < Node > ( ) ;
status = new SyncStatus ( packetObj . SyncID , obj , info ) ;
_ s tatusBySyncID. Add ( ( game , status . SyncID ) , status ) ;
_ s tatusByObject. Add ( status . Object , status ) ;
_ clientS tatusBySyncID. Add ( status . SyncID , status ) ;
_ clientS tatusByObject. Add ( status . Object , status ) ;
game . GetNode ( info . ContainerNodePath ) . AddChild ( obj ) ;
} else {
if ( packetObj . Special = = Special . Spawn ) throw new Exception (
$"Spawning object {info.Name} with ID {packetObj.SyncID}, but it already exists" ) ;
if ( info ! = status . Info ) throw new Exception (
$"Info doesn't match ({info.Name} != {status.Info.Name})" ) ;
$"Info of synced object being modified doesn't match ({info.Name} != {status.Info.Name})" ) ;
if ( packetObj . Special = = Special . Destroy ) {
_ s tatusBySyncID. Remove ( ( game , status . SyncID ) ) ;
_ s tatusByObject. Remove ( status . Object ) ;
_ clientS tatusBySyncID. Remove ( status . SyncID ) ;
_ clientS tatusByObject. Remove ( status . Object ) ;
status . Object . GetParent ( ) . RemoveChild ( status . Object ) ;
status . Object . QueueFree ( ) ;