Skip to content

Commit

Permalink
Update to 0.4
Browse files Browse the repository at this point in the history
Replace Godot's Multiplayer API with pure ENet networking. No more RPCs (for now)
  • Loading branch information
warent committed Jul 5, 2024
1 parent 0745235 commit 829c9ca
Show file tree
Hide file tree
Showing 12 changed files with 375 additions and 368 deletions.
156 changes: 90 additions & 66 deletions addons/HLNC/NetworkNode3D.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using Godot;
using HLNC.Serialization;
Expand All @@ -21,10 +20,6 @@ Network properties can only update on the server side.
*/
public partial class NetworkNode3D : Node3D, IStateSerializable, INotifyPropertyChanged
{
private static PackedScene NetworkNode3DBlankScene = GD.Load<PackedScene>("res://addons/HLNC/network_node_3d.tscn");
public static NetworkNode3D Instantiate() {
return NetworkNode3DBlankScene.Instantiate() as NetworkNode3D;
}
public bool IsNetworkScene => GetMeta("is_network_scene", false).AsBool();

internal List<NetworkNodeWrapper> NetworkSceneChildren = [];
Expand All @@ -42,27 +37,37 @@ public override void _ExitTree()

public bool IsNetworkReady { get; internal set; } = false;

private NetworkNodeWrapper _networkParent = null;
internal NetworkNodeWrapper NetworkParent
private NetworkId _networkParentId;
public NetworkId NetworkParentId
{
get => _networkParent;
get
{
return _networkParentId;
}
set
{
{
if (IsNetworkScene && _networkParent != null && _networkParent.Node is NetworkNode3D _networkNodeParent) {
if (IsNetworkScene && NetworkParent != null && NetworkParent.Node is NetworkNode3D _networkNodeParent)
{
_networkNodeParent.NetworkSceneChildren.Remove(
_networkNodeParent.NetworkSceneChildren.Find((NetworkNodeWrapper child) => child.Node == this)
);
}
}
_networkParent = value;
_networkParentId = value;
{
if (IsNetworkScene && value != null && value.Node is NetworkNode3D _networkNodeParent) {
if (IsNetworkScene && value != 0 && NetworkRunner.Instance.GetFromNetworkId(value).Node is NetworkNode3D _networkNodeParent)
{
_networkNodeParent.NetworkSceneChildren.Add(new NetworkNodeWrapper(this));
}
}
}
}
public NetworkNodeWrapper NetworkParent { get {
return NetworkRunner.Instance.GetFromNetworkId(NetworkParentId);
} internal set {
NetworkParentId = value?.NetworkId ?? 0;
} }
public bool DynamicSpawn { get; internal set; } = false;

// Cannot have more than 8 serializers
Expand All @@ -72,12 +77,13 @@ public JObject ToJSON(bool recurse = true)
{
if (!IsNetworkScene)
{
throw new System.Exception("Only scenes can be converted to JSON.");
throw new System.Exception("Only scenes can be converted to JSON: " + GetPath());
}

var result = new JObject();
result["data"] = new JObject();
if (IsNetworkScene) {
if (IsNetworkScene)
{
result["scenePack"] = NetworkScenesRegister.SCENES_PACK[SceneFilePath];
}
// We retain this for debugging purposes.
Expand Down Expand Up @@ -122,7 +128,8 @@ public JObject ToJSON(bool recurse = true)
}
}

if (!nodeData.HasValues) {
if (!nodeData.HasValues)
{
(result["data"] as JObject).Remove(nodePath);
}
}
Expand Down Expand Up @@ -151,17 +158,25 @@ public JObject ToJSON(bool recurse = true)
public static async Task<NetworkNode3D> FromJSON(JObject data)
{
NetworkNode3D node;
if (data.ContainsKey("scenePack")) {
if (data.ContainsKey("scenePack"))
{
node = NetworkScenesRegister.SCENES_MAP[(byte)data["scenePack"]].Instantiate<NetworkNode3D>();
} else {
node = Instantiate();
}
else
{
node = new NetworkNode3D();
}
var tcs = new TaskCompletionSource<bool>();
node.Ready += () => {
foreach (var child in node.GetNetworkChildren(NetworkChildrenSearchToggle.INCLUDE_SCENES)) {
if (child.Node.GetMeta("is_network_scene", false).AsBool()) {
node.Ready += () =>
{
foreach (var child in node.GetNetworkChildren(NetworkChildrenSearchToggle.INCLUDE_SCENES))
{
if (child.Node.GetMeta("is_network_scene", false).AsBool())
{
child.Node.QueueFree();
} else {
}
else
{
child.Node.SetMeta("import_from_json", true);
}
}
Expand All @@ -171,41 +186,59 @@ public static async Task<NetworkNode3D> FromJSON(JObject data)
NetworkRunner.Instance.AddChild(node);
await tcs.Task;
NetworkRunner.Instance.RemoveChild(node);
foreach (var networkNodePathAndProps in data["data"] as JObject) {
foreach (var networkNodePathAndProps in data["data"] as JObject)
{
var nodePath = networkNodePathAndProps.Key;
var nodeProps = networkNodePathAndProps.Value as JObject;
var targetNode = node.GetNodeOrNull(nodePath);
if (targetNode == null) {
if (targetNode == null)
{
GD.PrintErr("Node not found for: ", nodePath);
continue;
}
foreach (var prop in nodeProps) {
foreach (var prop in nodeProps)
{
var variantType = targetNode.Get(prop.Key).VariantType;
if (variantType == Variant.Type.String) {
if (variantType == Variant.Type.String)
{
targetNode.Set(prop.Key, prop.Value.ToString());
} else if (variantType == Variant.Type.Float) {
}
else if (variantType == Variant.Type.Float)
{
targetNode.Set(prop.Key, (float)prop.Value);
} else if (variantType == Variant.Type.Int) {
}
else if (variantType == Variant.Type.Int)
{
targetNode.Set(prop.Key, (int)prop.Value);
} else if (variantType == Variant.Type.Bool) {
}
else if (variantType == Variant.Type.Bool)
{
targetNode.Set(prop.Key, (bool)prop.Value);
} else if (variantType == Variant.Type.Vector2) {
}
else if (variantType == Variant.Type.Vector2)
{
var vec = prop.Value as JArray;
targetNode.Set(prop.Key, new Vector2((float)vec[0], (float)vec[1]));
} else if (variantType == Variant.Type.Vector3) {
}
else if (variantType == Variant.Type.Vector3)
{
var vec = prop.Value as JArray;
targetNode.Set(prop.Key, new Vector3((float)vec[0], (float)vec[1], (float)vec[2]));
}
}
}
if (data.ContainsKey("children")) {
foreach (var child in data["children"] as JObject) {
if (data.ContainsKey("children"))
{
foreach (var child in data["children"] as JObject)
{
var nodePath = child.Key;
var children = child.Value as JArray;
foreach (var childData in children) {
foreach (var childData in children)
{
var childNode = await FromJSON(childData as JObject);
var parent = node.GetNodeOrNull(nodePath);
if (parent == null) {
if (parent == null)
{
GD.PrintErr("Parent node not found for: ", nodePath);
continue;
}
Expand All @@ -219,8 +252,10 @@ public static async Task<NetworkNode3D> FromJSON(JObject data)
[Signal]
public delegate void NetworkPropertyChangedEventHandler(string nodePath, StringName propertyName);

public NetworkNode3D() {
if (GetType().GetCustomAttributes(typeof(NetworkScenes), true).Length > 0) {
public NetworkNode3D()
{
if (GetType().GetCustomAttributes(typeof(NetworkScenes), true).Length > 0)
{
SetMeta("is_network_scene", true);
}
SetMeta("is_network_node", true);
Expand All @@ -234,14 +269,14 @@ public NetworkNode3D() {
];
}
public NetworkId NetworkId { get; internal set; } = -1;
public PeerId InputAuthority { get; internal set; } = -1;
public NetPeer InputAuthority { get; internal set; } = null;

public bool IsCurrentOwner
{
get { return NetworkRunner.Instance.IsServer || InputAuthority == NetworkRunner.Instance.LocalPlayerId; }
get { return NetworkRunner.Instance.IsServer || (!NetworkRunner.Instance.IsServer && InputAuthority == NetworkRunner.Instance.ENetHost); }
}

public Dictionary<long, bool> Interest = [];
public Dictionary<NetPeer, bool> Interest = [];

public static NetworkNode3D FindFromChild(Node node)
{
Expand All @@ -255,7 +290,7 @@ public static NetworkNode3D FindFromChild(Node node)
}

public enum NetworkChildrenSearchToggle { INCLUDE_SCENES, EXCLUDE_SCENES, ONLY_SCENES }
public IEnumerable<NetworkNodeWrapper> GetNetworkChildren(NetworkChildrenSearchToggle searchToggle = NetworkChildrenSearchToggle.EXCLUDE_SCENES)
public IEnumerable<NetworkNodeWrapper> GetNetworkChildren(NetworkChildrenSearchToggle searchToggle = NetworkChildrenSearchToggle.EXCLUDE_SCENES, bool nestedSceneChildren = true)
{
var children = GetChildren();
while (children.Count > 0)
Expand All @@ -267,7 +302,10 @@ public IEnumerable<NetworkNodeWrapper> GetNetworkChildren(NetworkChildrenSearchT
{
continue;
}
children.AddRange(child.GetChildren());
if (nestedSceneChildren || (!nestedSceneChildren && !isNetworkScene))
{
children.AddRange(child.GetChildren());
}
if (!child.GetMeta("is_network_node", false).AsBool())
{
continue;
Expand Down Expand Up @@ -305,29 +343,9 @@ public void _NetworkPrepare()
// Clients dequeue network scenes and prepare them later via serializers triggered by the server.
return;
}
if (!DynamicSpawn)
foreach (var child in GetNetworkChildren(NetworkChildrenSearchToggle.INCLUDE_SCENES, false))
{
// The network parent is defined on spawn for the client
var parentScene = GetParent();
while (parentScene != null)
{
if (parentScene.GetMeta("is_network_scene", false).AsBool())
{
break;
}
parentScene = parentScene.GetParent();
}
if (parentScene == null && !GetMeta("is_network_scene", false).AsBool())
{
throw new System.Exception("NetworkNode3D has no associated network scene: " + GetPath());
}
if (parentScene == null && this != NetworkRunner.Instance.CurrentScene.Node)
{
throw new System.Exception("Scene not associated with parent. Only one root scene allowed at a time: " + GetPath());
}
if (parentScene != null) {
NetworkParent = new NetworkNodeWrapper(parentScene);
}
child.NetworkParentId = NetworkId;
}

if (NetworkRunner.Instance.IsServer)
Expand Down Expand Up @@ -376,7 +394,7 @@ public virtual void _NetworkProcess(int _tick)
INetworkInputHandler inputHandler = (INetworkInputHandler)this;
if (inputHandler.InputBuffer.Count > 0)
{
NetworkRunner.Instance.RpcId(1, "TransferInput", NetworkRunner.Instance.CurrentTick, (byte)NetworkId, inputHandler.InputBuffer);
// NetworkRunner.Instance.RpcId(1, "TransferInput", NetworkRunner.Instance.CurrentTick, (byte)NetworkId, inputHandler.InputBuffer);
inputHandler.InputBuffer.Clear();
}
}
Expand All @@ -385,10 +403,16 @@ public virtual void _NetworkProcess(int _tick)
public Godot.Collections.Dictionary<int, Variant> GetInput()
{
if (!IsCurrentOwner) return null;

byte netId = NetworkRunner.Instance.LocalPlayerId == InputAuthority ? (byte)NetworkId : NetworkPeerManager.Instance.GetPeerNodeId(InputAuthority, new NetworkNodeWrapper(this));
if (!NetworkRunner.Instance.InputStore.ContainsKey(InputAuthority))
return null;

byte netId;
if (NetworkRunner.Instance.IsServer) {
netId = NetworkPeerManager.Instance.GetPeerNodeId(InputAuthority, new NetworkNodeWrapper(this));
} else {
netId = (byte)NetworkId;
}

if (!NetworkRunner.Instance.InputStore[InputAuthority].ContainsKey(netId))
return null;

Expand Down
14 changes: 9 additions & 5 deletions addons/HLNC/NetworkNodeWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,17 @@ public partial class NetworkNodeWrapper {
return !(a == b);
}

public Node Node { get; }
public Node Node { get; private set; } = null;
private Dictionary<string, StringName> properties = new Dictionary<string, StringName>();
private Dictionary<string, StringName> methods = new Dictionary<string, StringName>();
public NetworkNodeWrapper(Node node) {
// TODO: Validate the node implements the interface correctly
Node = node;
if (node == null) return;
if (!node.GetMeta("is_network_node", false).AsBool()) {
// The node will remain null if it is not a NetworkNode
return;
}
Node = node;
// TODO: Validate the node implements the interface correctly
var requiredProperties = new HashSet<string> {
"InputAuthority",
"NetworkId",
Expand Down Expand Up @@ -130,9 +134,9 @@ private Variant Call(string name, params Variant[] args) {
throw new Exception($"Method {snakeCase} not found on {Node.GetPath()}");
}

public PeerId InputAuthority {
public ENetPacketPeer InputAuthority {
get {
return Get("InputAuthority").AsInt64();
return Get("InputAuthority").As<ENetPacketPeer>();
}

internal set {
Expand Down
11 changes: 5 additions & 6 deletions addons/HLNC/NetworkPeerManager/IPeerController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,14 @@ public enum PeerSyncState
{
INITIAL
}
public byte TryRegisterPeerNode(NetworkNodeWrapper node, PeerId? peer = null);
public void DeregisterPeerNode(NetworkNodeWrapper node, PeerId? peer = null);
public PeerId LocalPlayerId { get; }
public byte TryRegisterPeerNode(NetworkNodeWrapper node, NetPeer? peer = null);
public void DeregisterPeerNode(NetworkNodeWrapper node, NetPeer? peer = null);
public Tick CurrentTick { get; }
public void ChangeScene(NetworkNodeWrapper node);
public byte GetPeerNodeId(PeerId peer, NetworkNodeWrapper node);
public byte GetPeerNodeId(NetPeer peer, NetworkNodeWrapper node);
public NetworkNodeWrapper GetNetworkNode(NetworkId networkId);
public bool HasSpawnedForClient(NetworkId networkId, PeerId peer);
public void SetSpawnedForClient(NetworkId networkId, PeerId peer);
public bool HasSpawnedForClient(NetworkId networkId, NetPeer peer);
public void SetSpawnedForClient(NetworkId networkId, NetPeer peer);
}

}
Loading

0 comments on commit 829c9ca

Please sign in to comment.