Last active
May 21, 2023 23:33
-
-
Save zeplar-exe/5eb164530f2cde095a0fe4b12a863155 to your computer and use it in GitHub Desktop.
A limited but working implementation of a node property synchronizer for Godot multiplayer (supports client->server and server->client replication)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections.Generic; | |
using Godot; | |
using Godot.Collections; | |
public partial class WorkingMultiplayerSynchronizer : Node | |
{ | |
private Array<NodePath> ServerToClientProperties { get; } | |
private Array<NodePath> ClientToServerProperties { get; set; } | |
private double TimeElapsed { get; set; } | |
[Export] public Node? ReplicationRelativeNode { get; set; } | |
[Export] public double ReplicationInterval { get; set; } | |
public WorkingMultiplayerSynchronizer() | |
{ | |
ServerToClientProperties = new Array<NodePath>(); | |
ClientToServerProperties = new Array<NodePath>(); | |
} | |
// RpcContext.Server | |
public void AddServerToClientReplicate(NodePath propertyPath) | |
{ | |
ServerToClientProperties.Add(propertyPath); | |
} | |
// RpcContext.Server | |
public void RemoveServerToClientReplicate(NodePath propertyPath) | |
{ | |
ServerToClientProperties.Remove(propertyPath); | |
} | |
// RpcContext.Server | |
public void AddClientToServerReplicate(NodePath propertyPath) | |
{ | |
ClientToServerProperties.Add(propertyPath); | |
Rpc(nameof(UpdateClientToServerProperties), ClientToServerProperties); | |
} | |
// RpcContext.Server | |
public void RemoveClientToServerReplicate(NodePath propertyPath) | |
{ | |
if (!ClientToServerProperties.Remove(propertyPath)) | |
return; | |
Rpc(nameof(UpdateClientToServerProperties), ClientToServerProperties); | |
} | |
[Rpc] | |
// RpcContext.Client | |
private void UpdateClientToServerProperties(Array<NodePath> array) | |
{ | |
ClientToServerProperties = array; | |
} | |
public override void _Process(double delta) | |
{ | |
if (ReplicationInterval != 0) // 0 = Every _process call | |
{ | |
TimeElapsed += delta; | |
if (TimeElapsed < ReplicationInterval) | |
return; | |
} | |
var syncDictionary = new Godot.Collections.Dictionary<NodePath, Variant>(); | |
var properties = Multiplayer.IsServer() ? ServerToClientProperties : ClientToServerProperties; | |
foreach (var propertyPath in properties) | |
{ | |
var value = GetValueOfPropertyPath(propertyPath); | |
if (value == null) | |
continue; | |
syncDictionary[propertyPath] = value.Value; | |
} | |
if (Multiplayer.IsServer()) | |
{ | |
Rpc(nameof(ReplicateClient), syncDictionary); | |
} | |
else | |
{ | |
RpcId(1, nameof(ReplicateServer), syncDictionary); | |
} | |
} | |
[Rpc] | |
// RpcContext.Client | |
private void ReplicateClient(Dictionary properties) | |
{ | |
foreach (var pair in properties) | |
{ | |
var propertyPath = pair.Key.AsNodePath(); | |
var value = pair.Value; | |
SetValueOfPropertyPath(propertyPath, value); | |
} | |
} | |
[Rpc(MultiplayerApi.RpcMode.AnyPeer)] | |
// RpcContext.Server | |
private void ReplicateServer(Dictionary properties) | |
{ | |
foreach (var pair in properties) | |
{ | |
var propertyPath = pair.Key.AsNodePath(); | |
if (!ClientToServerProperties.Contains(propertyPath)) // Disallow client->server replication not enabled by the server | |
continue; | |
var value = pair.Value; | |
SetValueOfPropertyPath(propertyPath, value); | |
} | |
} | |
private Variant? GetValueOfPropertyPath(NodePath path) | |
{ | |
var parts = path.ToString().Split(':'); | |
return (ReplicationRelativeNode ?? this).GetNode(parts[0])?.Get(parts[1]); | |
} | |
private void SetValueOfPropertyPath(NodePath path, Variant value) | |
{ | |
var parts = path.ToString().Split(':'); | |
(ReplicationRelativeNode ?? this).GetNode(parts[0])?.Set(parts[1], value); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment