diff --git a/src/EscapeMenuAppearance.cs b/src/EscapeMenuAppearance.cs index 6251ddf..2025ae9 100644 --- a/src/EscapeMenuAppearance.cs +++ b/src/EscapeMenuAppearance.cs @@ -20,8 +20,8 @@ public class EscapeMenuAppearance : CenterContainer ColorSlider.Value = GD.Randf(); ColorPreview.Modulate = Color.FromHsv((float)ColorSlider.Value, 1.0F, 1.0F); - this.GetClient().LocalPlayerSpawned += (player) => player.RpcId(1, - nameof(Player.ChangeAppearance), DisplayName.Text, ColorPreview.Modulate); + this.GetClient().LocalPlayerSpawned += (player) + => RPC.Reliable(1, player.ChangeAppearance, DisplayName.Text, ColorPreview.Modulate); } #pragma warning disable IDE0051 @@ -49,7 +49,6 @@ public class EscapeMenuAppearance : CenterContainer if (IsVisibleInTree()) return; var client = this.GetClient(); if (client.Status == NetworkedMultiplayerPeer.ConnectionStatus.Connected) - client.LocalPlayer.RpcId(1, nameof(Player.ChangeAppearance), - DisplayName.Text, ColorPreview.Modulate); + RPC.Reliable(1, client.LocalPlayer.ChangeAppearance, DisplayName.Text, ColorPreview.Modulate); } } diff --git a/src/EscapeMenuWorld.cs b/src/EscapeMenuWorld.cs index dd2cd27..82661da 100644 --- a/src/EscapeMenuWorld.cs +++ b/src/EscapeMenuWorld.cs @@ -105,6 +105,7 @@ public class EscapeMenuWorld : CenterContainer // Reset players' positions. foreach (var player in server.GetWorld().Players) + // Can't use RPC helper method here since player is not a LocalPlayer here. player.RpcId(player.NetworkID, nameof(LocalPlayer.ResetPosition), Vector2.Zero); save.ReadDataIntoWorld(server.GetWorld()); diff --git a/src/IO/WorldSave.cs b/src/IO/WorldSave.cs index c73857d..ff6c586 100644 --- a/src/IO/WorldSave.cs +++ b/src/IO/WorldSave.cs @@ -75,8 +75,8 @@ public class WorldSave public void ReadDataIntoWorld(World world) { - world.Rpc(nameof(World.ClearBlocks)); + RPC.Reliable(world.ClearBlocks); foreach (var (position, color, unbreakable) in Blocks) - world.Rpc(nameof(World.SpawnBlock), position.X, position.Y, color, unbreakable); + RPC.Reliable(world.SpawnBlock, position.X, position.Y, color, unbreakable); } } diff --git a/src/Items/CreativeBuilding.cs b/src/Items/CreativeBuilding.cs index 5009e31..725d84d 100644 --- a/src/Items/CreativeBuilding.cs +++ b/src/Items/CreativeBuilding.cs @@ -57,14 +57,14 @@ public class CreativeBuilding : Node2D if (_currentMode == BuildMode.Placing) { if (!_canBuild) _currentMode = null; else if (!Input.IsActionPressed("interact_primary")) { - RpcId(1, nameof(PlaceLine), _startPos.X, _startPos.Y, _direction, _length); + RPC.Reliable(1, PlaceLine, _startPos.X, _startPos.Y, _direction, _length); _currentMode = null; } } if (_currentMode == BuildMode.Breaking) { if (!Input.IsActionPressed("interact_secondary")) { - RpcId(1, nameof(BreakLine), _startPos.X, _startPos.Y, _direction, _length); + RPC.Reliable(1, BreakLine, _startPos.X, _startPos.Y, _direction, _length); _currentMode = null; } } @@ -121,7 +121,7 @@ public class CreativeBuilding : Node2D foreach (var pos in GetBlockPositions(start, direction, length)) { if (world.GetBlockAt(pos) != null) continue; var color = Player.Color.Blend(Color.FromHsv(0.0F, 0.0F, GD.Randf(), 0.2F)); - world.Rpc(nameof(World.SpawnBlock), pos.X, pos.Y, color, false); + RPC.Reliable(world.SpawnBlock, pos.X, pos.Y, color, false); } } @@ -137,7 +137,7 @@ public class CreativeBuilding : Node2D foreach (var pos in GetBlockPositions(start, direction, length)) { var block = world.GetBlockAt(pos); if (block?.Unbreakable != false) continue; - world.Rpc(nameof(World.Despawn), world.GetPathTo(block)); + RPC.Reliable(world.Despawn, world.GetPathTo(block)); } } } diff --git a/src/Items/Items.cs b/src/Items/Items.cs index 4fb9614..7bf91a4 100644 --- a/src/Items/Items.cs +++ b/src/Items/Items.cs @@ -39,16 +39,14 @@ public class Items : Node2D, IItems _current = node; if (sendRpc) { - if (this.GetGame() is Server) Rpc(nameof(DoSetCurrent), _current?.Name); - else RpcId(1, nameof(DoSetCurrent), _current?.Name); + if (this.GetGame() is Server) RPC.Reliable(DoSetCurrent, _current?.Name); + else RPC.Reliable(1, DoSetCurrent, _current?.Name); } } [Remote] public void DoSetCurrent(string name) { - if (this.GetGame() is Server) { - if (GetTree().GetRpcSenderId() != Player.NetworkID) return; - } + if ((this.GetGame() is Server) && (Player.NetworkID != GetTree().GetRpcSenderId())) return; var node = (name != null) ? GetNode(name) : null; SetCurrent(node, this.GetGame() is Server); } diff --git a/src/Items/Weapon.cs b/src/Items/Weapon.cs index cbae3f9..6aceded 100644 --- a/src/Items/Weapon.cs +++ b/src/Items/Weapon.cs @@ -125,7 +125,7 @@ public class Weapon : Sprite AimDirection = Cursor.Position.AngleToPoint(Player.Position) - angleC; // FIXME: Angle calculation when cursor is too close to player. - RpcUnreliableId(1, nameof(SendAimAngle), AimDirection); + RPC.Unreliable(1, SendAimAngle, AimDirection); Update(); } } else { @@ -147,7 +147,7 @@ public class Weapon : Sprite if (Player.NetworkID != GetTree().GetRpcSenderId()) return; // TODO: Verify input. // if ((value < 0) || (value > Mathf.Tau)) return; - Rpc(nameof(SendAimAngle), value); + RPC.Unreliable(SendAimAngle, value); } else if (!(Player is LocalPlayer)) AimDirection = value; } @@ -157,7 +157,7 @@ public class Weapon : Sprite { var seed = unchecked((int)GD.Randi()); if (!FireInternal(AimDirection, Scale.y > 0, seed)) return; - RpcId(1, nameof(SendFire), AimDirection, Scale.y > 0, seed); + RPC.Reliable(1, SendFire, AimDirection, Scale.y > 0, seed); ((LocalPlayer)Player).Velocity -= Mathf.Polar2Cartesian(Knockback, Rotation); } @@ -198,14 +198,14 @@ public class Weapon : Sprite if (Player.NetworkID != GetTree().GetRpcSenderId()) return; // TODO: Verify input. if (FireInternal(aimDirection, toRight, seed)) - Rpc(nameof(SendFire), aimDirection, toRight, seed); + RPC.Reliable(SendFire, aimDirection, toRight, seed); } else if (!(Player is LocalPlayer)) FireInternal(aimDirection, toRight, seed); } private void Reload() - { if (ReloadInternal()) RpcId(1, nameof(SendReload)); } + { if (ReloadInternal()) RPC.Reliable(1, SendReload); } private bool ReloadInternal() { @@ -220,7 +220,7 @@ public class Weapon : Sprite { if (this.GetGame() is Server) { if (Player.NetworkID != GetTree().GetRpcSenderId()) return; - if (ReloadInternal()) Rpc(nameof(SendReload)); + if (ReloadInternal()) RPC.Reliable(SendReload); } else if (!(Player is LocalPlayer)) ReloadInternal(); } diff --git a/src/Objects/LocalPlayer.cs b/src/Objects/LocalPlayer.cs index 00a89ec..e37b6f2 100644 --- a/src/Objects/LocalPlayer.cs +++ b/src/Objects/LocalPlayer.cs @@ -23,7 +23,7 @@ public class LocalPlayer : Player public override void _Process(float delta) { base._Process(delta); - RpcUnreliableId(1, nameof(Player.Move), Position); + RPC.Unreliable(1, Move, Position); } public override void _PhysicsProcess(float delta) diff --git a/src/Objects/Player.cs b/src/Objects/Player.cs index 35c33b9..fb8c3c4 100644 --- a/src/Objects/Player.cs +++ b/src/Objects/Player.cs @@ -31,6 +31,7 @@ public class Player : KinematicBody2D, IInitializable public override void _Process(float delta) { if ((Position.y > 9000) && (this.GetGame() is Server)) + // Can't use RPC helper method here since player is not a LocalPlayer here. RpcId(NetworkID, nameof(LocalPlayer.ResetPosition), Vector2.Zero); } diff --git a/src/Scenes/Server.cs b/src/Scenes/Server.cs index 5a5e277..c83eda8 100644 --- a/src/Scenes/Server.cs +++ b/src/Scenes/Server.cs @@ -83,23 +83,23 @@ public class Server : Game var world = this.GetWorld(); foreach (var player in world.Players) { - world.RpcId(networkID, nameof(World.SpawnPlayer), player.NetworkID, player.Position); + RPC.Reliable(networkID, world.SpawnPlayer, player.NetworkID, player.Position); // Send player's appearance. player.Rset(nameof(Player.DisplayName), player.DisplayName); player.Rset(nameof(Player.Color), player.Color); // Send player's currently equipped item. - if (player.Items.Current != null) ((Node2D)player.Items).RpcId( - networkID, nameof(Items.DoSetCurrent), player.Items.Current.Name); + if (player.Items.Current != null) RPC.Reliable(networkID, + ((Items)player.Items).DoSetCurrent, player.Items.Current.Name); } foreach (var block in world.Blocks) - world.RpcId(networkID, nameof(World.SpawnBlock), + RPC.Reliable(networkID, world.SpawnBlock, block.Position.X, block.Position.Y, block.Color, block.Unbreakable); - world.Rpc(nameof(World.SpawnPlayer), networkID, Vector2.Zero); + RPC.Reliable(world.SpawnPlayer, networkID, Vector2.Zero); if (IsSingleplayer) LocalPlayer = world.GetPlayer(networkID); } } @@ -112,6 +112,6 @@ public class Server : Game // Local player stays around for reconnecting. if (LocalPlayer == player) return; - world.Rpc(nameof(World.Despawn), world.GetPathTo(player)); + RPC.Reliable(world.Despawn, world.GetPathTo(player)); } } diff --git a/src/Utility/RPC.cs b/src/Utility/RPC.cs new file mode 100644 index 0000000..6efd90c --- /dev/null +++ b/src/Utility/RPC.cs @@ -0,0 +1,44 @@ +using System; +using Godot; + +public static class RPC +{ + public static void Reliable(Action action) => GetNode(action).Rpc(action.Method.Name); + public static void Reliable(Action action, T arg) => GetNode(action).Rpc(action.Method.Name, arg); + public static void Reliable(Action action, T0 arg0, T1 arg1) => GetNode(action).Rpc(action.Method.Name, arg0, arg1); + public static void Reliable(Action action, T0 arg0, T1 arg1, T2 arg2) => GetNode(action).Rpc(action.Method.Name, arg0, arg1, arg2); + public static void Reliable(Action action, T0 arg0, T1 arg1, T2 arg2, T3 arg3) => GetNode(action).Rpc(action.Method.Name, arg0, arg1, arg2, arg3); + public static void Reliable(Action action, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4) => GetNode(action).Rpc(action.Method.Name, arg0, arg1, arg2, arg3, arg4); + public static void Reliable(Action action, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) => GetNode(action).Rpc(action.Method.Name, arg0, arg1, arg2, arg3, arg4, arg5); + public static void Reliable(Action action, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) => GetNode(action).Rpc(action.Method.Name, arg0, arg1, arg2, arg3, arg4, arg5, arg6); + + public static void Reliable(int networkID, Action action) => GetNode(action).RpcId(networkID, action.Method.Name); + public static void Reliable(int networkID, Action action, T arg) => GetNode(action).RpcId(networkID, action.Method.Name, arg); + public static void Reliable(int networkID, Action action, T0 arg0, T1 arg1) => GetNode(action).RpcId(networkID, action.Method.Name, arg0, arg1); + public static void Reliable(int networkID, Action action, T0 arg0, T1 arg1, T2 arg2) => GetNode(action).RpcId(networkID, action.Method.Name, arg0, arg1, arg2); + public static void Reliable(int networkID, Action action, T0 arg0, T1 arg1, T2 arg2, T3 arg3) => GetNode(action).RpcId(networkID, action.Method.Name, arg0, arg1, arg2, arg3); + public static void Reliable(int networkID, Action action, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4) => GetNode(action).RpcId(networkID, action.Method.Name, arg0, arg1, arg2, arg3, arg4); + public static void Reliable(int networkID, Action action, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) => GetNode(action).RpcId(networkID, action.Method.Name, arg0, arg1, arg2, arg3, arg4, arg5); + public static void Reliable(int networkID, Action action, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) => GetNode(action).RpcId(networkID, action.Method.Name, arg0, arg1, arg2, arg3, arg4, arg5, arg6); + + public static void Unreliable(Action action) => GetNode(action).RpcUnreliable(action.Method.Name); + public static void Unreliable(Action action, T arg) => GetNode(action).RpcUnreliable(action.Method.Name, arg); + public static void Unreliable(Action action, T0 arg0, T1 arg1) => GetNode(action).RpcUnreliable(action.Method.Name, arg0, arg1); + public static void Unreliable(Action action, T0 arg0, T1 arg1, T2 arg2) => GetNode(action).RpcUnreliable(action.Method.Name, arg0, arg1, arg2); + public static void Unreliable(Action action, T0 arg0, T1 arg1, T2 arg2, T3 arg3) => GetNode(action).RpcUnreliable(action.Method.Name, arg0, arg1, arg2, arg3); + public static void Unreliable(Action action, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4) => GetNode(action).RpcUnreliable(action.Method.Name, arg0, arg1, arg2, arg3, arg4); + public static void Unreliable(Action action, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) => GetNode(action).RpcUnreliable(action.Method.Name, arg0, arg1, arg2, arg3, arg4, arg5); + public static void Unreliable(Action action, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) => GetNode(action).RpcUnreliable(action.Method.Name, arg0, arg1, arg2, arg3, arg4, arg5, arg6); + + public static void Unreliable(int networkID, Action action) => GetNode(action).RpcUnreliableId(networkID, action.Method.Name); + public static void Unreliable(int networkID, Action action, T arg) => GetNode(action).RpcUnreliableId(networkID, action.Method.Name, arg); + public static void Unreliable(int networkID, Action action, T0 arg0, T1 arg1) => GetNode(action).RpcUnreliableId(networkID, action.Method.Name, arg0, arg1); + public static void Unreliable(int networkID, Action action, T0 arg0, T1 arg1, T2 arg2) => GetNode(action).RpcUnreliableId(networkID, action.Method.Name, arg0, arg1, arg2); + public static void Unreliable(int networkID, Action action, T0 arg0, T1 arg1, T2 arg2, T3 arg3) => GetNode(action).RpcUnreliableId(networkID, action.Method.Name, arg0, arg1, arg2, arg3); + public static void Unreliable(int networkID, Action action, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4) => GetNode(action).RpcUnreliableId(networkID, action.Method.Name, arg0, arg1, arg2, arg3, arg4); + public static void Unreliable(int networkID, Action action, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) => GetNode(action).RpcUnreliableId(networkID, action.Method.Name, arg0, arg1, arg2, arg3, arg4, arg5); + public static void Unreliable(int networkID, Action action, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) => GetNode(action).RpcUnreliableId(networkID, action.Method.Name, arg0, arg1, arg2, arg3, arg4, arg5, arg6); + + private static Node GetNode(Delegate action) => (action.Target as Node) ?? throw new ArgumentException( + $"Target ({action.Target?.GetType().ToString() ?? "null"}) must be a Node", nameof(action)); +}