diff --git a/src/Features/Multiplayer/Contracts/Exceptions/MultiplayerAlreadyConnectedException.cs b/src/Features/Multiplayer/Contracts/Exceptions/MultiplayerAlreadyConnectedException.cs
index 88fb049..570bde6 100644
--- a/src/Features/Multiplayer/Contracts/Exceptions/MultiplayerAlreadyConnectedException.cs
+++ b/src/Features/Multiplayer/Contracts/Exceptions/MultiplayerAlreadyConnectedException.cs
@@ -1,6 +1,7 @@
namespace Game.Features.Multiplayer.Contracts.Exceptions;
///
-/// 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.
///
-public sealed class MultiplayerAlreadyConnectedException() : MultiplayerException("Already connected to a session.");
+public sealed class MultiplayerAlreadyConnectedException()
+ : MultiplayerException("Already connected to or connecting with a session.");
diff --git a/src/Features/Multiplayer/Contracts/Exceptions/MultiplayerInvalidPortException.cs b/src/Features/Multiplayer/Contracts/Exceptions/MultiplayerInvalidPortException.cs
new file mode 100644
index 0000000..0e76439
--- /dev/null
+++ b/src/Features/Multiplayer/Contracts/Exceptions/MultiplayerInvalidPortException.cs
@@ -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.");
diff --git a/src/Features/Multiplayer/Contracts/Exceptions/MultiplayerInvalidPortException.cs.uid b/src/Features/Multiplayer/Contracts/Exceptions/MultiplayerInvalidPortException.cs.uid
new file mode 100644
index 0000000..35fc532
--- /dev/null
+++ b/src/Features/Multiplayer/Contracts/Exceptions/MultiplayerInvalidPortException.cs.uid
@@ -0,0 +1 @@
+uid://d3n8p5w2y7c4h
diff --git a/src/Features/Multiplayer/Domain/MultiplayerSession.cs b/src/Features/Multiplayer/Domain/MultiplayerSession.cs
index 8c03f08..9f3c51d 100644
--- a/src/Features/Multiplayer/Domain/MultiplayerSession.cs
+++ b/src/Features/Multiplayer/Domain/MultiplayerSession.cs
@@ -8,8 +8,7 @@ public sealed class MultiplayerSession
public const int MaxPlayers = 4;
public IReadOnlyCollection 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 _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;
}
}
diff --git a/src/Features/Multiplayer/Domain/MultiplayerSessionState.cs b/src/Features/Multiplayer/Domain/MultiplayerSessionState.cs
new file mode 100644
index 0000000..f1ceada
--- /dev/null
+++ b/src/Features/Multiplayer/Domain/MultiplayerSessionState.cs
@@ -0,0 +1,9 @@
+namespace Game.Features.Multiplayer.Domain;
+
+public enum MultiplayerSessionState
+{
+ Disconnected,
+ Hosting,
+ Connecting,
+ Connected
+}
diff --git a/src/Features/Multiplayer/Domain/MultiplayerSessionState.cs.uid b/src/Features/Multiplayer/Domain/MultiplayerSessionState.cs.uid
new file mode 100644
index 0000000..7b3015b
--- /dev/null
+++ b/src/Features/Multiplayer/Domain/MultiplayerSessionState.cs.uid
@@ -0,0 +1 @@
+uid://b7m2q9r4v6x1k
diff --git a/src/Features/Multiplayer/Godot/MultiplayerController.cs b/src/Features/Multiplayer/Godot/MultiplayerController.cs
index f92042c..2c81630 100644
--- a/src/Features/Multiplayer/Godot/MultiplayerController.cs
+++ b/src/Features/Multiplayer/Godot/MultiplayerController.cs
@@ -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()