Split network code into its own Node

main
copygirl 5 years ago
parent 8338716de0
commit b6f62633e6
  1. 4
      scene/GameScene.tscn
  2. 209
      src/EscapeMenu.cs
  3. 161
      src/Network.cs

@ -1,4 +1,4 @@
[gd_scene load_steps=8 format=2] [gd_scene load_steps=9 format=2]
[ext_resource path="res://scene/EscapeMenu.tscn" type="PackedScene" id=1] [ext_resource path="res://scene/EscapeMenu.tscn" type="PackedScene" id=1]
[ext_resource path="res://src/Cursor.cs" type="Script" id=2] [ext_resource path="res://src/Cursor.cs" type="Script" id=2]
@ -7,6 +7,7 @@
[ext_resource path="res://scene/Player.tscn" type="PackedScene" id=5] [ext_resource path="res://scene/Player.tscn" type="PackedScene" id=5]
[ext_resource path="res://scene/Block.tscn" type="PackedScene" id=6] [ext_resource path="res://scene/Block.tscn" type="PackedScene" id=6]
[ext_resource path="res://src/Viewport.cs" type="Script" id=7] [ext_resource path="res://src/Viewport.cs" type="Script" id=7]
[ext_resource path="res://src/Network.cs" type="Script" id=8]
[node name="Game" type="Node"] [node name="Game" type="Node"]
script = ExtResource( 3 ) script = ExtResource( 3 )
@ -17,6 +18,7 @@ Block = ExtResource( 6 )
script = ExtResource( 7 ) script = ExtResource( 7 )
[node name="Network" type="Node" parent="."] [node name="Network" type="Node" parent="."]
script = ExtResource( 8 )
[node name="HUD" type="CanvasLayer" parent="."] [node name="HUD" type="CanvasLayer" parent="."]

@ -1,14 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Godot; using Godot;
// TODO: Split network and escape menu logic. // TODO: Split network and escape menu logic.
public class EscapeMenu : Container public class EscapeMenu : Container
{ {
[Export] public ushort DefaultPort { get; set; } = 42005;
[Export] public string DefaultAddress { get; set; } = "localhost";
[Export] public NodePath StatusPath { get; set; } [Export] public NodePath StatusPath { get; set; }
[Export] public NodePath ServerStartStopPath { get; set; } [Export] public NodePath ServerStartStopPath { get; set; }
[Export] public NodePath ServerPortPath { get; set; } [Export] public NodePath ServerPortPath { get; set; }
@ -23,14 +17,7 @@ public class EscapeMenu : Container
public LineEdit ClientAddress { get; private set; } public LineEdit ClientAddress { get; private set; }
public Button Return { get; private set; } public Button Return { get; private set; }
public Node PlayerContainer { get; private set; } public Network Network { get; private set; }
public Player OwnPlayer { get; private set; }
public PackedScene OtherPlayer { get; private set; }
public override void _Ready()
{
OtherPlayer = GD.Load<PackedScene>("res://scene/OtherPlayer.tscn");
}
public override void _EnterTree() public override void _EnterTree()
{ {
@ -40,23 +27,11 @@ public class EscapeMenu : Container
ClientDisConnect = GetNode<Button>(ClientDisConnectPath); ClientDisConnect = GetNode<Button>(ClientDisConnectPath);
ClientAddress = GetNode<LineEdit>(ClientAddressPath); ClientAddress = GetNode<LineEdit>(ClientAddressPath);
Return = GetNode<Button>(ReturnPath); Return = GetNode<Button>(ReturnPath);
PlayerContainer = GetNode("/root/Game");
ServerPort.PlaceholderText = DefaultPort.ToString(); Network = GetNode<Network>("/root/Game/Network");
ClientAddress.PlaceholderText = $"{DefaultAddress}:{DefaultPort}"; Network.Connect("StatusChanged", this, "OnNetworkStatusChanged");
ServerPort.PlaceholderText = Network.DefaultPort.ToString();
GetTree().Connect("connected_to_server", this, "OnClientConnected"); ClientAddress.PlaceholderText = $"{Network.DefaultAddress}:{Network.DefaultPort}";
GetTree().Connect("connection_failed", this, "DisconnectFromServer");
GetTree().Connect("server_disconnected", this, "DisconnectFromServer");
GetTree().Connect("network_peer_connected", this, "OnPeerConnected");
GetTree().Connect("network_peer_disconnected", this, "OnPeerDisconnected");
}
public override void _Process(float delta)
{
if (OwnPlayer == null) return;
RpcUnreliable("OnPlayerMoved", OwnPlayer.Position);
} }
public override void _Input(InputEvent @event) public override void _Input(InputEvent @event)
@ -88,138 +63,35 @@ public class EscapeMenu : Container
} }
public void StartServer(ushort port)
{
if (GetTree().NetworkPeer != null) throw new InvalidOperationException();
var peer = new NetworkedMultiplayerENet();
// TODO: Somehow show there was an error.
if (peer.CreateServer(port) != Error.Ok) return;
GetTree().NetworkPeer = peer;
OwnPlayer = FindOwnPlayer();
Status.Text = "Server Running";
Status.Modulate = Colors.Green;
ServerPort.Editable = false;
ServerStartStop.Text = "Stop Server";
ClientAddress.Editable = false;
ClientDisConnect.Disabled = true;
if (Visible) GetTree().Paused = false;
}
public void StopServer()
{
if ((GetTree().NetworkPeer == null) || !GetTree().IsNetworkServer()) throw new InvalidOperationException();
// TODO: Disconnect players gracefully.
((NetworkedMultiplayerENet)GetTree().NetworkPeer).CloseConnection();
GetTree().NetworkPeer = null;
OwnPlayer = null;
foreach (var player in GetOtherPlayers())
player.RemoveFromParent();
Status.Text = "No Connection";
Status.Modulate = Colors.Red;
ServerPort.Editable = true;
ServerStartStop.Text = "Start Server";
ClientAddress.Editable = true;
ClientDisConnect.Disabled = false;
if (Visible) GetTree().Paused = true;
}
public void ConnectToServer(string address, ushort port)
{
if (GetTree().NetworkPeer != null) throw new InvalidOperationException();
var peer = new NetworkedMultiplayerENet();
// TODO: Somehow show there was an error.
if (peer.CreateClient(address, port) != Error.Ok) return;
GetTree().NetworkPeer = peer;
Status.Text = "Connecting ...";
Status.Modulate = Colors.Yellow;
ServerPort.Editable = false;
ServerStartStop.Disabled = true;
ClientAddress.Editable = false;
ClientDisConnect.Text = "Disconnect";
if (Visible) GetTree().Paused = false;
}
public void DisconnectFromServer()
{
if ((GetTree().NetworkPeer == null) || GetTree().IsNetworkServer()) throw new InvalidOperationException();
// TODO: Disconnect from server gracefully.
((NetworkedMultiplayerENet)GetTree().NetworkPeer).CloseConnection();
GetTree().NetworkPeer = null;
OwnPlayer = null;
foreach (var player in GetOtherPlayers())
player.RemoveFromParent();
Status.Text = "No Connection";
Status.Modulate = Colors.Red;
ServerPort.Editable = true;
ServerStartStop.Disabled = false;
ClientAddress.Editable = true;
ClientDisConnect.Disabled = false;
ClientDisConnect.Text = "Connect";
if (Visible) GetTree().Paused = true;
}
private Player FindOwnPlayer()
=> GetTree().Root.GetChild(0).GetChildren().OfType<Player>().First();
private Node2D GetPlayerWithId(int id)
=> PlayerContainer.GetNodeOrNull<Node2D>(id.ToString());
private Node2D GetOrCreatePlayerWithId(int id)
{
var player = GetPlayerWithId(id);
if (player == null) {
player = (Node2D)OtherPlayer.Instance();
// TODO: Use "set_network_master".
player.Name = id.ToString();
PlayerContainer.AddChild(player);
}
return player;
}
// TODO: This assumes that any node whose name starts with a digit is a player.
private IEnumerable<Node2D> GetOtherPlayers()
=> PlayerContainer.GetChildren().OfType<Node2D>()
.Where(node => char.IsDigit(node.Name[0]));
#pragma warning disable IDE0051 #pragma warning disable IDE0051
private void OnClientConnected() private void OnNetworkStatusChanged(Network.Status status)
{ {
OwnPlayer = FindOwnPlayer(); switch (status) {
case Network.Status.NoConnection:
Status.Text = "Connected to Server"; Status.Text = "No Connection";
Status.Modulate = Colors.Green; Status.Modulate = Colors.Red;
} break;
case Network.Status.ServerRunning:
Status.Text = "Server Running";
private void OnPeerConnected(int id) Status.Modulate = Colors.Green;
{ break;
case Network.Status.Connecting:
} Status.Text = "Connecting ...";
Status.Modulate = Colors.Yellow;
private void OnPeerDisconnected(int id) break;
=> GetPlayerWithId(id)?.RemoveFromParent(); case Network.Status.ConnectedToServer:
Status.Text = "Connected to Server";
Status.Modulate = Colors.Green;
break;
}
[Remote] ServerPort.Editable = status == Network.Status.NoConnection;
private void OnPlayerMoved(Vector2 position) ServerStartStop.Text = (status == Network.Status.ServerRunning) ? "Stop Server" : "Start Server";
{ ClientAddress.Editable = status == Network.Status.NoConnection;
var id = GetTree().GetRpcSenderId(); ClientDisConnect.Text = (status < Network.Status.Connecting) ? "Connect" : "Disconnect";
var player = GetOrCreatePlayerWithId(id); ClientDisConnect.Disabled = status == Network.Status.ServerRunning;
player.Position = position; if (Visible) GetTree().Paused = status == Network.Status.NoConnection;
} }
@ -227,26 +99,27 @@ public class EscapeMenu : Container
private void _on_ServerStartStop_pressed() private void _on_ServerStartStop_pressed()
{ {
if (GetTree().NetworkPeer == null) if (GetTree().NetworkPeer == null) {
StartServer((ServerPort.Text.Length > 0) ? ushort.Parse(ServerPort.Text) : DefaultPort); var port = Network.DefaultPort;
else StopServer(); if (ServerPort.Text.Length > 0)
port = ushort.Parse(ServerPort.Text);
Network.StartServer(port);
} else Network.StopServer();
} }
private void _on_ClientDisConnect_pressed() private void _on_ClientDisConnect_pressed()
{ {
if (GetTree().NetworkPeer == null) { if (GetTree().NetworkPeer == null) {
var address = DefaultAddress; var address = Network.DefaultAddress;
var port = DefaultPort; var port = Network.DefaultPort;
if (ClientAddress.Text.Length > 0) { if (ClientAddress.Text.Length > 0) {
// TODO: Verify input some more, support IPv6? // TODO: Verify input some more, support IPv6?
var split = address.Split(':'); var split = address.Split(':');
address = (split.Length > 1) ? split[0] : address; address = (split.Length > 1) ? split[0] : address;
port = (split.Length > 1) ? ushort.Parse(split[1]) : port; port = (split.Length > 1) ? ushort.Parse(split[1]) : port;
} }
Network.ConnectToServer(address, port);
ConnectToServer(address, port); } else Network.DisconnectFromServer();
} else DisconnectFromServer();
} }
private void _on_HideAddress_toggled(bool pressed) private void _on_HideAddress_toggled(bool pressed)

@ -0,0 +1,161 @@
using Godot;
using System;
using System.Collections.Generic;
using System.Linq;
public class Network : Node
{
public enum Status
{
NoConnection,
ServerRunning,
Connecting,
ConnectedToServer,
}
[Export] public ushort DefaultPort { get; set; } = 42005;
[Export] public string DefaultAddress { get; set; } = "localhost";
public Node PlayerContainer { get; private set; }
public Player OwnPlayer { get; private set; }
public PackedScene OtherPlayer { get; private set; }
public Status CurrentStatus { get; private set; } = Status.NoConnection;
[Signal] public delegate void StatusChanged(Status status);
public override void _Ready()
{
OtherPlayer = GD.Load<PackedScene>("res://scene/OtherPlayer.tscn");
PlayerContainer = GetParent();
GetTree().Connect("connected_to_server", this, "OnClientConnected");
GetTree().Connect("connection_failed", this, "DisconnectFromServer");
GetTree().Connect("server_disconnected", this, "DisconnectFromServer");
GetTree().Connect("network_peer_connected", this, "OnPeerConnected");
GetTree().Connect("network_peer_disconnected", this, "OnPeerDisconnected");
}
public override void _Process(float delta)
{
if (OwnPlayer == null) return;
RpcUnreliable("OnPlayerMoved", OwnPlayer.Position);
}
public Error StartServer(ushort port)
{
if (GetTree().NetworkPeer != null) throw new InvalidOperationException();
var peer = new NetworkedMultiplayerENet();
// TODO: Somehow show there was an error.
var error = peer.CreateServer(port);
if (error != Error.Ok) return error;
GetTree().NetworkPeer = peer;
OwnPlayer = FindOwnPlayer();
CurrentStatus = Status.ServerRunning;
EmitSignal(nameof(StatusChanged), CurrentStatus);
return Error.Ok;
}
public void StopServer()
{
if ((GetTree().NetworkPeer == null) || !GetTree().IsNetworkServer()) throw new InvalidOperationException();
// TODO: Disconnect players gracefully.
((NetworkedMultiplayerENet)GetTree().NetworkPeer).CloseConnection();
GetTree().NetworkPeer = null;
OwnPlayer = null;
foreach (var player in GetOtherPlayers())
player.RemoveFromParent();
CurrentStatus = Status.NoConnection;
EmitSignal(nameof(StatusChanged), CurrentStatus);
}
public Error ConnectToServer(string address, ushort port)
{
if (GetTree().NetworkPeer != null) throw new InvalidOperationException();
var peer = new NetworkedMultiplayerENet();
// TODO: Somehow show there was an error.
var error = peer.CreateClient(address, port);
if (error != Error.Ok) return error;
GetTree().NetworkPeer = peer;
CurrentStatus = Status.Connecting;
EmitSignal(nameof(StatusChanged), CurrentStatus);
return Error.Ok;
}
public void DisconnectFromServer()
{
if ((GetTree().NetworkPeer == null) || GetTree().IsNetworkServer()) throw new InvalidOperationException();
// TODO: Disconnect from server gracefully.
((NetworkedMultiplayerENet)GetTree().NetworkPeer).CloseConnection();
GetTree().NetworkPeer = null;
OwnPlayer = null;
foreach (var player in GetOtherPlayers())
player.RemoveFromParent();
CurrentStatus = Status.NoConnection;
EmitSignal(nameof(StatusChanged), CurrentStatus);
}
private Player FindOwnPlayer()
=> GetTree().Root.GetChild(0).GetChildren().OfType<Player>().First();
private Node2D GetPlayerWithId(int id)
=> PlayerContainer.GetNodeOrNull<Node2D>(id.ToString());
private Node2D GetOrCreatePlayerWithId(int id)
{
var player = GetPlayerWithId(id);
if (player == null) {
player = (Node2D)OtherPlayer.Instance();
// TODO: Use "set_network_master".
player.Name = id.ToString();
PlayerContainer.AddChild(player);
}
return player;
}
// TODO: This assumes that any node whose name starts with a digit is a player.
private IEnumerable<Node2D> GetOtherPlayers()
=> PlayerContainer.GetChildren().OfType<Node2D>()
.Where(node => char.IsDigit(node.Name[0]));
#pragma warning disable IDE0051
private void OnClientConnected()
{
OwnPlayer = FindOwnPlayer();
CurrentStatus = Status.ConnectedToServer;
EmitSignal(nameof(StatusChanged), CurrentStatus);
}
private void OnPeerConnected(int id)
{
}
private void OnPeerDisconnected(int id)
=> GetPlayerWithId(id)?.RemoveFromParent();
[Remote]
private void OnPlayerMoved(Vector2 position)
{
var id = GetTree().GetRpcSenderId();
var player = GetOrCreatePlayerWithId(id);
player.Position = position;
}
}
Loading…
Cancel
Save