Skip to content

Commit

Permalink
Update to 0.3.0
Browse files Browse the repository at this point in the history
Improve cross-language implementation.
Support JSON serialization.
Scene change implementation.
Normalize network nodes.
More lifecycle methods.
  • Loading branch information
warent committed Jul 2, 2024
1 parent f2b8d19 commit 0745235
Show file tree
Hide file tree
Showing 17 changed files with 814 additions and 401 deletions.
419 changes: 260 additions & 159 deletions addons/HLNC/NetworkNode3D.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
class_name GDNetworkNode3D extends Node3D
class_name NetworkNode3D extends Node3D

signal NetworkPropertyChanged(node_path: String, property_name: String)

var parent_scene = null
var dynamic_spawn: bool = false

func _init():
set_meta("is_network_node", true)

func _ready():
func _network_prepare():
parent_scene = self
while parent_scene != null:
if parent_scene.has_meta("is_network_scene"):
Expand Down
208 changes: 208 additions & 0 deletions addons/HLNC/NetworkNodeWrapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Metadata.Ecma335;
using Godot;
using HLNC.Serialization;
using HLNC.Serialization.Serializers;

namespace HLNC {
public partial class NetworkNodeWrapper {

// Custom operator overload to validate null
public static bool operator ==(NetworkNodeWrapper a, NetworkNodeWrapper b) {
if (!ReferenceEquals(a, null)) {
if (ReferenceEquals(b, null)) {
return a.Node == null;
}
}
if (!ReferenceEquals(b, null)) {
if (ReferenceEquals(a, null)) {
return b.Node == null;
}
}
if (ReferenceEquals(a, null) && ReferenceEquals(b, null)) {
return true;
}
return a.Equals(b) || a.Node == b.Node;
}

public static bool operator !=(NetworkNodeWrapper a, NetworkNodeWrapper b) {
return !(a == b);
}

public Node Node { get; }
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;
var requiredProperties = new HashSet<string> {
"InputAuthority",
"NetworkId",
"NetworkParentId",
"DynamicSpawn",
};
foreach (var prop in node.GetPropertyList()) {
var pascalName = ToPascalCase(prop["name"].AsString());

if (requiredProperties.Contains(pascalName)) {
properties[pascalName] = new StringName(prop["name"].AsString());
}

foreach (var requiredProperty in requiredProperties) {
if (!properties.ContainsKey(requiredProperty)) {
// Assume the default property is an non-exported PascalCase
properties[requiredProperty] = new StringName(requiredProperty);
}
}
}
}

// Convert from snake_case to PascalCase
private static string ToPascalCase(string name) {
var pascalCase = "";
var capitalize = true;
for (int i = 0; i < name.Length; i++)
{
if (name[i] == '_')
{
capitalize = true;
}
else if (capitalize)
{
pascalCase += char.ToUpper(name[i]);
capitalize = false;
}
else
{
pascalCase += name[i];
}
}
return pascalCase;
}

private static string ToSnakeCase(string name) {
var snakeCase = "";
for (int i = 0; i < name.Length; i++)
{
if (char.IsUpper(name[i]))
{
if (i > 0 && name[i - 1] != '_')
{
snakeCase += "_";
}
snakeCase += char.ToLower(name[i]);
}
else
{
snakeCase += name[i];
}
}
return snakeCase;
}
private Variant Get(string name) {
return Node.Get(properties[name]);
}

private void Set(string name, Variant value) {
Node.Set(properties[name], value);
}

private Variant Call(string name, params Variant[] args) {
if (methods.ContainsKey(name)) {
return Node.Call(methods[name], args);
}

if (Node.HasMethod(name)) {
var result = Node.Call(name, args);
methods[name] = new StringName(name);
return result;
}

var snakeCase = ToSnakeCase(name);
if (Node.HasMethod(snakeCase)) {
methods[name] = new StringName(snakeCase);
return Node.Call(snakeCase, args);
}

throw new Exception($"Method {snakeCase} not found on {Node.GetPath()}");
}

public PeerId InputAuthority {
get {
return Get("InputAuthority").AsInt64();
}

internal set {
Set("InputAuthority", value);
}
}

public NetworkId NetworkId {
get {
return Get("NetworkId").AsInt64();
}

internal set {
Set("NetworkId", value);
}
}

public NetworkNodeWrapper NetworkParent => NetworkRunner.Instance.GetFromNetworkId(NetworkParentId);

// TODO: Handle null?
internal byte NetworkSceneId => NetworkScenesRegister.SCENES_PACK[Node.SceneFilePath];

public NetworkId NetworkParentId {
get {
return Get("NetworkParentId").AsInt64();
}

internal set {
Set("NetworkParentId", value);
}
}

public bool DynamicSpawn {
get {
return Get("DynamicSpawn").AsBool();
}

internal set {
Set("DynamicSpawn", value);
}
}

public IStateSerailizer[] Serializers {
get {
// TODO: Support serializers across other languages / node types
if (Node is NetworkNode3D networkNode) {
return networkNode.Serializers;
}
return [];
}
}

internal void _NetworkPrepare() {
Call("_NetworkPrepare");
}

public void _NetworkProcess(Tick tick) {
Call("_NetworkProcess", tick);
}

public List<NetworkNodeWrapper> StaticNetworkChildren {
get {
return NetworkScenesRegister.STATIC_NETWORK_NODE_PATHS[Node.SceneFilePath].Aggregate(new List<NetworkNodeWrapper>(), (acc, path) => {
var child = Node.GetNodeOrNull(path);
if (child != null) {
acc.Add(new NetworkNodeWrapper(child));
}
return acc;
});
}
}
}
}
14 changes: 8 additions & 6 deletions addons/HLNC/NetworkPeerManager/IPeerController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,21 @@

namespace HLNC
{
public interface IPeerController
public interface IPeerStateController
{
public enum PeerSyncState
{
INITIAL
}
public byte TryRegisterPeerNode(NetworkNode3D node, PeerId? peer = null);
public void DeregisterPeerNode(NetworkNode3D node, PeerId? peer = null);
public byte TryRegisterPeerNode(NetworkNodeWrapper node, PeerId? peer = null);
public void DeregisterPeerNode(NetworkNodeWrapper node, PeerId? peer = null);
public PeerId LocalPlayerId { get; }
public Tick CurrentTick { get; }
public void ChangeScene(NetworkNode3D node);
public byte GetPeerNodeId(PeerId peer, NetworkNode3D node);
public NetworkNode3D GetNetworkNode(NetworkId networkId);
public void ChangeScene(NetworkNodeWrapper node);
public byte GetPeerNodeId(PeerId peer, NetworkNodeWrapper node);
public NetworkNodeWrapper GetNetworkNode(NetworkId networkId);
public bool HasSpawnedForClient(NetworkId networkId, PeerId peer);
public void SetSpawnedForClient(NetworkId networkId, PeerId peer);
}

}
Loading

0 comments on commit 0745235

Please sign in to comment.