I'm at the end of my rope here, I'm trying to create a client-server architecture using the NetworkedMultiplayerEnet API from the high level multiplayer section in the docs with mono by using this post from Ryan Forrester as a guide: https://ryanforrester.ca/godot/2020/02/02/godot-mono-high-level-multiplayer-example-3-2/
Despite following a similar structure (the only parts I think I modified being related to specific implementation) I manage to host a server and connect to it through a client in Ryan's example, yet I cannot even trigger a single network related signal in my own project.
The project compiles and runs normally up until the point where I attempt to call an RPC, at which point the program fails and throws the following error:
'E 0:00:06.906 rpcp: Trying to call an RPC via a network peer which is not connected.
<C++ Error>Condition "network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED" is true.
<C++ Source> core/io/multiplayer_api.cpp:625 @ rpcp()
<Traceback>:0 @ System.Object Godot.NativeCalls.godot_icall_3_686(IntPtr , IntPtr , Int32 , System.String , System.Object[] )()
Node.cs:1699 @ System.Object Godot.Node.RpcId(Int32 , System.String , System.Object[] )()
RegisterButton.cs:98 @ void RegisterButton._on_RegisterButton_pressed()()
'
Here's the relevant code:
using Godot;
using System;
using System.Collections.Generic;
public class Lobby : Control
{
private readonly int DEFAULT_PORT = 4321;
private readonly int MAX_PLAYERS = 30;
private readonly string ADDRESS = "localhost";
public string playerName { get ; set ;}
private Dictionary<int, string> players = new Dictionary<int, string>();
public override void _Ready()
{
GetTree().Connect("network_peer_connected", this, nameof(PlayerConnected));
GetTree().Connect("network_peer_disconnected", this, nameof(PlayerConnected));
GetTree().Connect("connected_to_server", this, nameof(ConnectedToServer));
GetTree().Connect("connection_failed", this, nameof(ConnectionFailed));
GetTree().Connect("server_disconnected", this, nameof(ServerDisconnected));
}
public bool HostLobby()
{
var peer = new NetworkedMultiplayerENet();
var result = peer.CreateServer(DEFAULT_PORT, MAX_PLAYERS);
if (result == 0)
{
GetTree().NetworkPeer = peer;
GD.Print($"Hosting server at {ADDRESS}:{DEFAULT_PORT}.");
return true;
}
else
{
return false;
}
}
public bool IsHosting()
{
if(GetTree().NetworkPeer != null)
{
return true;
}
else
{
return false;
}
}
public void JoinGame()
{
GD.Print($"Joining lobby with address {ADDRESS}:{DEFAULT_PORT}");
var clientPeer = new NetworkedMultiplayerENet();
var result = clientPeer.CreateClient(ADDRESS, DEFAULT_PORT);
GetTree().NetworkPeer = clientPeer;
}
public void LeaveGame()
{
GD.Print("Leaving current game");
players.Clear();
GetNode(GetTree().GetNetworkUniqueId().ToString()).QueueFree();
Rpc(nameof(RemovePlayer), GetTree().GetNetworkUniqueId());
((NetworkedMultiplayerENet)GetTree().NetworkPeer).CloseConnection();
GetTree().NetworkPeer = null;
}
private void PlayerConnected(int peerId)
{
GD.Print($"player {peerId} has connected.");
Rpc(nameof(RegisterPlayer), playerName);
}
private void PlayerDisconnected(int peerId)
{
GD.Print("Player disconnected");
RemovePlayer(peerId);
}
private void ConnectedToServer()
{
GD.Print("Successfully connected to the server");
}
private void ConnectionFailed()
{
GetTree().NetworkPeer = null;
GD.Print("Failed to connect.");
}
private void ServerDisconnected()
{
GD.Print($"Disconnected from the server");
}
[Remote]
private void RegisterPlayer(string playerName)
{
var peerId = GetTree().GetRpcSenderId();
players.Add(peerId, playerName);
GD.Print($"player {playerName} added with peer ID {peerId}");
}
[Remote]
private void RemovePlayer(int peerId)
{
if (players.ContainsKey(peerId))
{
players.Remove(peerId);
}
}
}
The HostLobby()
and JoinGame()
methods are called through two other classes, which are instanced in the server or client as necessary:
using Godot;
using System;
public class ServerLobby : Lobby
{
public override void _Ready()
{
if(!IsHosting())
{
if(!HostLobby())
{
GD.Print("Failed to start server, shutting down");
GetTree().Quit();
return;
}
}
}
}
using Godot;
using System;
public class ClientLobby : Lobby
{
public override void _Ready()
{
JoinGame();
}
}
Thanks for your help in advance, and I apologize for any glaring flaws or defects I may have missed.