feat: implement multiplayer session state management and add invalid port exception
This commit is contained in:
+3
-2
@@ -1,6 +1,7 @@
|
|||||||
namespace Game.Features.Multiplayer.Contracts.Exceptions;
|
namespace Game.Features.Multiplayer.Contracts.Exceptions;
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </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 const int MaxPlayers = 4;
|
||||||
public IReadOnlyCollection<PeerId> PeerIds => _peerIds;
|
public IReadOnlyCollection<PeerId> PeerIds => _peerIds;
|
||||||
public bool IsFull => _peerIds.Count >= MaxPlayers;
|
public bool IsFull => _peerIds.Count >= MaxPlayers;
|
||||||
public bool IsHosting { get; set; }
|
public MultiplayerSessionState State { get; private set; } = MultiplayerSessionState.Disconnected;
|
||||||
public bool IsConnected { get; set; }
|
|
||||||
|
|
||||||
private readonly HashSet<PeerId> _peerIds = [];
|
private readonly HashSet<PeerId> _peerIds = [];
|
||||||
|
|
||||||
@@ -17,10 +16,25 @@ public sealed class MultiplayerSession
|
|||||||
|
|
||||||
public void RemovePeer(PeerId peerId) => _peerIds.Remove(peerId);
|
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()
|
public void Reset()
|
||||||
{
|
{
|
||||||
_peerIds.Clear();
|
_peerIds.Clear();
|
||||||
IsHosting = false;
|
State = MultiplayerSessionState.Disconnected;
|
||||||
IsConnected = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
public partial class MultiplayerController : Node, IMultiplayerPeer
|
||||||
{
|
{
|
||||||
|
private const int MinPort = 1;
|
||||||
|
private const int MaxPort = 65535;
|
||||||
|
|
||||||
private readonly MultiplayerSession _session = new();
|
private readonly MultiplayerSession _session = new();
|
||||||
private IMultiplayerPeerFactory _multiplayerPeerFactory;
|
private IMultiplayerPeerFactory _multiplayerPeerFactory;
|
||||||
|
|
||||||
@@ -33,29 +36,45 @@ public partial class MultiplayerController : Node, IMultiplayerPeer
|
|||||||
|
|
||||||
private void OnConnectedToServer()
|
private void OnConnectedToServer()
|
||||||
{
|
{
|
||||||
|
_session.MarkConnected();
|
||||||
Connected?.Invoke();
|
Connected?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnConnectionFailed()
|
private void OnConnectionFailed()
|
||||||
{
|
{
|
||||||
_session.Reset();
|
ResetConnection();
|
||||||
ConnectionFailed?.Invoke();
|
ConnectionFailed?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnServerDisconnected()
|
private void OnServerDisconnected()
|
||||||
{
|
{
|
||||||
_session.Reset();
|
ResetConnection();
|
||||||
Disconnected?.Invoke();
|
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()
|
private void AssertNoActiveSession()
|
||||||
{
|
{
|
||||||
if (_session.IsHosting)
|
if (_session.State == MultiplayerSessionState.Hosting)
|
||||||
{
|
{
|
||||||
throw new MultiplayerAlreadyHostingException();
|
throw new MultiplayerAlreadyHostingException();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_session.IsConnected)
|
if (_session.State is MultiplayerSessionState.Connecting or MultiplayerSessionState.Connected)
|
||||||
{
|
{
|
||||||
throw new MultiplayerAlreadyConnectedException();
|
throw new MultiplayerAlreadyConnectedException();
|
||||||
}
|
}
|
||||||
@@ -68,26 +87,25 @@ public partial class MultiplayerController : Node, IMultiplayerPeer
|
|||||||
|
|
||||||
public void Host(int port)
|
public void Host(int port)
|
||||||
{
|
{
|
||||||
|
ValidatePort(port);
|
||||||
AssertNoActiveSession();
|
AssertNoActiveSession();
|
||||||
|
|
||||||
Multiplayer.MultiplayerPeer = _multiplayerPeerFactory.CreateHost(port, MultiplayerSession.MaxPlayers - 1);
|
Multiplayer.MultiplayerPeer = _multiplayerPeerFactory.CreateHost(port, MultiplayerSession.MaxPlayers - 1);
|
||||||
_session.AddPeer(new PeerId(Multiplayer.GetUniqueId()));
|
_session.StartHosting(new PeerId(Multiplayer.GetUniqueId()));
|
||||||
_session.IsHosting = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Join(string address, int port)
|
public void Join(string address, int port)
|
||||||
{
|
{
|
||||||
|
ValidatePort(port);
|
||||||
AssertNoActiveSession();
|
AssertNoActiveSession();
|
||||||
|
|
||||||
Multiplayer.MultiplayerPeer = _multiplayerPeerFactory.CreateClient(address, port);
|
Multiplayer.MultiplayerPeer = _multiplayerPeerFactory.CreateClient(address, port);
|
||||||
_session.IsConnected = true;
|
_session.StartConnecting();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Disconnect()
|
public void Disconnect()
|
||||||
{
|
{
|
||||||
Multiplayer.MultiplayerPeer?.Close();
|
ResetConnection();
|
||||||
Multiplayer.MultiplayerPeer = null;
|
|
||||||
_session.Reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void _Ready()
|
public override void _Ready()
|
||||||
|
|||||||
Reference in New Issue
Block a user