feat: implement multiplayer session state management and add invalid port exception

This commit is contained in:
2026-08-25 02:10:54 +02:00
parent 988f1102f8
commit 1445fc014f
7 changed files with 64 additions and 16 deletions
@@ -1,6 +1,7 @@
namespace Game.Features.Multiplayer.Contracts.Exceptions;
/// <summary>
/// Thrown when trying to host or join while already connected to a session.
/// Thrown when trying to host or join while connecting or already connected to a session.
/// </summary>
public sealed class MultiplayerAlreadyConnectedException() : MultiplayerException("Already connected to a session.");
public sealed class MultiplayerAlreadyConnectedException()
: MultiplayerException("Already connected to or connecting with a session.");
@@ -0,0 +1,4 @@
namespace Game.Features.Multiplayer.Contracts.Exceptions;
public sealed class MultiplayerInvalidPortException(int port)
: MultiplayerException($"Invalid port {port}. Port must be between 1 and 65535.");
@@ -0,0 +1 @@
uid://d3n8p5w2y7c4h
@@ -8,8 +8,7 @@ public sealed class MultiplayerSession
public const int MaxPlayers = 4;
public IReadOnlyCollection<PeerId> PeerIds => _peerIds;
public bool IsFull => _peerIds.Count >= MaxPlayers;
public bool IsHosting { get; set; }
public bool IsConnected { get; set; }
public MultiplayerSessionState State { get; private set; } = MultiplayerSessionState.Disconnected;
private readonly HashSet<PeerId> _peerIds = [];
@@ -17,10 +16,25 @@ public sealed class MultiplayerSession
public void RemovePeer(PeerId peerId) => _peerIds.Remove(peerId);
public void StartHosting(PeerId hostPeerId)
{
_peerIds.Add(hostPeerId);
State = MultiplayerSessionState.Hosting;
}
public void StartConnecting()
{
State = MultiplayerSessionState.Connecting;
}
public void MarkConnected()
{
State = MultiplayerSessionState.Connected;
}
public void Reset()
{
_peerIds.Clear();
IsHosting = false;
IsConnected = false;
State = MultiplayerSessionState.Disconnected;
}
}
@@ -0,0 +1,9 @@
namespace Game.Features.Multiplayer.Domain;
public enum MultiplayerSessionState
{
Disconnected,
Hosting,
Connecting,
Connected
}
@@ -0,0 +1 @@
uid://b7m2q9r4v6x1k
@@ -8,6 +8,9 @@ namespace Game.Features.Multiplayer.Godot;
public partial class MultiplayerController : Node, IMultiplayerPeer
{
private const int MinPort = 1;
private const int MaxPort = 65535;
private readonly MultiplayerSession _session = new();
private IMultiplayerPeerFactory _multiplayerPeerFactory;
@@ -33,29 +36,45 @@ public partial class MultiplayerController : Node, IMultiplayerPeer
private void OnConnectedToServer()
{
_session.MarkConnected();
Connected?.Invoke();
}
private void OnConnectionFailed()
{
_session.Reset();
ResetConnection();
ConnectionFailed?.Invoke();
}
private void OnServerDisconnected()
{
_session.Reset();
ResetConnection();
Disconnected?.Invoke();
}
private static void ValidatePort(int port)
{
if (port is < MinPort or > MaxPort)
{
throw new MultiplayerInvalidPortException(port);
}
}
private void ResetConnection()
{
Multiplayer.MultiplayerPeer?.Close();
Multiplayer.MultiplayerPeer = null;
_session.Reset();
}
private void AssertNoActiveSession()
{
if (_session.IsHosting)
if (_session.State == MultiplayerSessionState.Hosting)
{
throw new MultiplayerAlreadyHostingException();
}
if (_session.IsConnected)
if (_session.State is MultiplayerSessionState.Connecting or MultiplayerSessionState.Connected)
{
throw new MultiplayerAlreadyConnectedException();
}
@@ -68,26 +87,25 @@ public partial class MultiplayerController : Node, IMultiplayerPeer
public void Host(int port)
{
ValidatePort(port);
AssertNoActiveSession();
Multiplayer.MultiplayerPeer = _multiplayerPeerFactory.CreateHost(port, MultiplayerSession.MaxPlayers - 1);
_session.AddPeer(new PeerId(Multiplayer.GetUniqueId()));
_session.IsHosting = true;
_session.StartHosting(new PeerId(Multiplayer.GetUniqueId()));
}
public void Join(string address, int port)
{
ValidatePort(port);
AssertNoActiveSession();
Multiplayer.MultiplayerPeer = _multiplayerPeerFactory.CreateClient(address, port);
_session.IsConnected = true;
_session.StartConnecting();
}
public void Disconnect()
{
Multiplayer.MultiplayerPeer?.Close();
Multiplayer.MultiplayerPeer = null;
_session.Reset();
ResetConnection();
}
public override void _Ready()