first commit

This commit is contained in:
2025-07-06 00:23:46 +02:00
commit 38f50c8819
1788 changed files with 112878 additions and 0 deletions

View File

@@ -0,0 +1,87 @@
using System;
using System.Threading.Tasks;
using NitroxClient.Communication.Abstract;
using NitroxModel.Helper;
using NitroxModel.MultiplayerSession;
using NitroxModel.Packets;
namespace NitroxClient.Communication.MultiplayerSession.ConnectionState
{
public class AwaitingReservationCredentials : ConnectionNegotiatingState
{
public override MultiplayerSessionConnectionStage CurrentStage => MultiplayerSessionConnectionStage.AWAITING_RESERVATION_CREDENTIALS;
public override Task NegotiateReservationAsync(IMultiplayerSessionConnectionContext sessionConnectionContext)
{
try
{
ValidateState(sessionConnectionContext);
string reservationCorrelationId = Guid.NewGuid().ToString();
RequestSessionReservation(sessionConnectionContext, reservationCorrelationId);
AwaitSessionReservation(sessionConnectionContext, reservationCorrelationId);
}
catch (Exception)
{
Disconnect(sessionConnectionContext);
throw;
}
return Task.CompletedTask;
}
private void RequestSessionReservation(IMultiplayerSessionConnectionContext sessionConnectionContext, string reservationCorrelationId)
{
IClient client = sessionConnectionContext.Client;
PlayerSettings playerSettings = sessionConnectionContext.PlayerSettings;
AuthenticationContext authenticationContext = sessionConnectionContext.AuthenticationContext;
MultiplayerSessionReservationRequest requestPacket = new(reservationCorrelationId, playerSettings, authenticationContext);
client.Send(requestPacket);
}
private void AwaitSessionReservation(IMultiplayerSessionConnectionContext sessionConnectionContext, string reservationCorrelationId)
{
AwaitingSessionReservation nextState = new AwaitingSessionReservation(reservationCorrelationId);
sessionConnectionContext.UpdateConnectionState(nextState);
}
private static void ValidateState(IMultiplayerSessionConnectionContext sessionConnectionContext)
{
ClientIsConnected(sessionConnectionContext);
PlayerSettingsIsNotNull(sessionConnectionContext);
AuthenticationContextIsNotNull(sessionConnectionContext);
}
private static void ClientIsConnected(IMultiplayerSessionConnectionContext sessionConnectionContext)
{
if (!sessionConnectionContext.Client.IsConnected)
{
throw new InvalidOperationException("The client is not connected.");
}
}
private static void PlayerSettingsIsNotNull(IMultiplayerSessionConnectionContext sessionConnectionContext)
{
try
{
Validate.NotNull(sessionConnectionContext.PlayerSettings);
}
catch (ArgumentNullException ex)
{
throw new InvalidOperationException("The context does not contain player settings.", ex);
}
}
private static void AuthenticationContextIsNotNull(IMultiplayerSessionConnectionContext sessionConnectionContext)
{
try
{
Validate.NotNull(sessionConnectionContext.AuthenticationContext);
}
catch (ArgumentNullException ex)
{
throw new InvalidOperationException("The context does not contain an authentication context.", ex);
}
}
}
}

View File

@@ -0,0 +1,74 @@
using System;
using System.Threading.Tasks;
using NitroxClient.Communication.Abstract;
using NitroxModel.Helper;
using NitroxModel.MultiplayerSession;
using NitroxModel.Packets.Exceptions;
namespace NitroxClient.Communication.MultiplayerSession.ConnectionState
{
public class AwaitingSessionReservation : ConnectionNegotiatingState
{
private readonly string reservationCorrelationId;
public AwaitingSessionReservation(string reservationCorrelationId)
{
Validate.NotNull(reservationCorrelationId);
this.reservationCorrelationId = reservationCorrelationId;
}
public override MultiplayerSessionConnectionStage CurrentStage => MultiplayerSessionConnectionStage.AWAITING_SESSION_RESERVATION;
public override Task NegotiateReservationAsync(IMultiplayerSessionConnectionContext sessionConnectionContext)
{
try
{
ValidateState(sessionConnectionContext);
HandleReservation(sessionConnectionContext);
}
catch (Exception)
{
Disconnect(sessionConnectionContext);
throw;
}
return Task.CompletedTask;
}
private static void HandleReservation(IMultiplayerSessionConnectionContext sessionConnectionContext)
{
IMultiplayerSessionConnectionState nextState = sessionConnectionContext.Reservation.ReservationState switch
{
MultiplayerSessionReservationState.RESERVED => new SessionReserved(),
_ => new SessionReservationRejected(),
};
sessionConnectionContext.UpdateConnectionState(nextState);
}
private void ValidateState(IMultiplayerSessionConnectionContext sessionConnectionContext)
{
ReservationIsNotNull(sessionConnectionContext);
ReservationPacketIsCorrelated(sessionConnectionContext);
}
private static void ReservationIsNotNull(IMultiplayerSessionConnectionContext sessionConnectionContext)
{
try
{
Validate.NotNull(sessionConnectionContext.Reservation);
}
catch (ArgumentNullException ex)
{
throw new InvalidOperationException("The context does not have a reservation.", ex);
}
}
private void ReservationPacketIsCorrelated(IMultiplayerSessionConnectionContext sessionConnectionContext)
{
if (!reservationCorrelationId.Equals(sessionConnectionContext.Reservation.CorrelationId))
{
throw new UncorrelatedPacketException(sessionConnectionContext.Reservation, reservationCorrelationId);
}
}
}
}

View File

@@ -0,0 +1,22 @@
using System.Threading.Tasks;
using NitroxClient.Communication.Abstract;
namespace NitroxClient.Communication.MultiplayerSession.ConnectionState
{
public abstract class CommunicatingState : IMultiplayerSessionConnectionState
{
public abstract MultiplayerSessionConnectionStage CurrentStage { get; }
public abstract Task NegotiateReservationAsync(IMultiplayerSessionConnectionContext sessionConnectionContext);
public abstract void JoinSession(IMultiplayerSessionConnectionContext sessionConnectionContext);
public virtual void Disconnect(IMultiplayerSessionConnectionContext sessionConnectionContext)
{
sessionConnectionContext.ClearSessionState();
sessionConnectionContext.Client.Stop();
Disconnected newConnectionState = new Disconnected();
sessionConnectionContext.UpdateConnectionState(newConnectionState);
}
}
}

View File

@@ -0,0 +1,14 @@
using System;
using System.Threading.Tasks;
using NitroxClient.Communication.Abstract;
namespace NitroxClient.Communication.MultiplayerSession.ConnectionState
{
public abstract class ConnectionNegotiatedState : CommunicatingState
{
public override Task NegotiateReservationAsync(IMultiplayerSessionConnectionContext sessionConnectionContext)
{
throw new InvalidOperationException("Unable to negotiate a session connection in the current state.");
}
}
}

View File

@@ -0,0 +1,13 @@
using System;
using NitroxClient.Communication.Abstract;
namespace NitroxClient.Communication.MultiplayerSession.ConnectionState
{
public abstract class ConnectionNegotiatingState : CommunicatingState
{
public override void JoinSession(IMultiplayerSessionConnectionContext sessionConnectionContext)
{
throw new InvalidOperationException("Cannot join a session until a reservation has been negotiated with the server.");
}
}
}

View File

@@ -0,0 +1,85 @@
using System;
using System.Threading.Tasks;
using NitroxClient.Communication.Abstract;
using NitroxClient.Communication.Exceptions;
using NitroxModel.Helper;
using NitroxModel.Packets;
namespace NitroxClient.Communication.MultiplayerSession.ConnectionState
{
public class Disconnected : IMultiplayerSessionConnectionState
{
public MultiplayerSessionConnectionStage CurrentStage => MultiplayerSessionConnectionStage.DISCONNECTED;
public async Task NegotiateReservationAsync(IMultiplayerSessionConnectionContext sessionConnectionContext)
{
ValidateState(sessionConnectionContext);
IClient client = sessionConnectionContext.Client;
string ipAddress = sessionConnectionContext.IpAddress;
int port = sessionConnectionContext.ServerPort;
await StartClientAsync(ipAddress, client, port);
EstablishSessionPolicy(sessionConnectionContext, client);
}
private void ValidateState(IMultiplayerSessionConnectionContext sessionConnectionContext)
{
ValidateClient(sessionConnectionContext);
try
{
Validate.NotNull(sessionConnectionContext.IpAddress);
}
catch (ArgumentNullException ex)
{
throw new InvalidOperationException("The context is missing an IP address.", ex);
}
}
private static void ValidateClient(IMultiplayerSessionConnectionContext sessionConnectionContext)
{
try
{
Validate.NotNull(sessionConnectionContext.Client);
}
catch (ArgumentNullException ex)
{
throw new InvalidOperationException("The client must be set on the connection context before trying to negotiate a session reservation.", ex);
}
}
private static async Task StartClientAsync(string ipAddress, IClient client, int port)
{
if (!client.IsConnected)
{
await client.StartAsync(ipAddress, port);
if (!client.IsConnected)
{
throw new ClientConnectionFailedException("The client failed to connect without providing a reason why.");
}
}
}
private static void EstablishSessionPolicy(IMultiplayerSessionConnectionContext sessionConnectionContext, IClient client)
{
string policyRequestCorrelationId = Guid.NewGuid().ToString();
MultiplayerSessionPolicyRequest requestPacket = new MultiplayerSessionPolicyRequest(policyRequestCorrelationId);
client.Send(requestPacket);
EstablishingSessionPolicy nextState = new EstablishingSessionPolicy(policyRequestCorrelationId);
sessionConnectionContext.UpdateConnectionState(nextState);
}
public void JoinSession(IMultiplayerSessionConnectionContext sessionConnectionContext)
{
throw new InvalidOperationException("Cannot join a session until a reservation has been negotiated with the server.");
}
public void Disconnect(IMultiplayerSessionConnectionContext sessionConnectionContext)
{
throw new InvalidOperationException("Not connected to a multiplayer server.");
}
}
}

View File

@@ -0,0 +1,68 @@
using System;
using System.Threading.Tasks;
using NitroxClient.Communication.Abstract;
using NitroxModel.Helper;
using NitroxModel.Packets.Exceptions;
namespace NitroxClient.Communication.MultiplayerSession.ConnectionState
{
public class EstablishingSessionPolicy : ConnectionNegotiatingState
{
private readonly string policyRequestCorrelationId;
public EstablishingSessionPolicy(string policyRequestCorrelationId)
{
Validate.NotNull(policyRequestCorrelationId);
this.policyRequestCorrelationId = policyRequestCorrelationId;
}
public override MultiplayerSessionConnectionStage CurrentStage => MultiplayerSessionConnectionStage.ESTABLISHING_SERVER_POLICY;
public override Task NegotiateReservationAsync(IMultiplayerSessionConnectionContext sessionConnectionContext)
{
try
{
ValidateState(sessionConnectionContext);
AwaitReservationCredentials(sessionConnectionContext);
}
catch (Exception)
{
Disconnect(sessionConnectionContext);
throw;
}
return Task.CompletedTask;
}
private void ValidateState(IMultiplayerSessionConnectionContext sessionConnectionContext)
{
SessionPolicyIsNotNull(sessionConnectionContext);
SessionPolicyPacketCorrelation(sessionConnectionContext);
}
private static void SessionPolicyIsNotNull(IMultiplayerSessionConnectionContext sessionConnectionContext)
{
try
{
Validate.NotNull(sessionConnectionContext.SessionPolicy);
}
catch (ArgumentNullException ex)
{
throw new InvalidOperationException("The context is missing a session policy.", ex);
}
}
private void SessionPolicyPacketCorrelation(IMultiplayerSessionConnectionContext sessionConnectionContext)
{
if (!policyRequestCorrelationId.Equals(sessionConnectionContext.SessionPolicy.CorrelationId))
{
throw new UncorrelatedPacketException(sessionConnectionContext.SessionPolicy, policyRequestCorrelationId);
}
}
private void AwaitReservationCredentials(IMultiplayerSessionConnectionContext sessionConnectionContext)
{
AwaitingReservationCredentials nextState = new AwaitingReservationCredentials();
sessionConnectionContext.UpdateConnectionState(nextState);
}
}
}

View File

@@ -0,0 +1,15 @@
using System;
using NitroxClient.Communication.Abstract;
namespace NitroxClient.Communication.MultiplayerSession.ConnectionState
{
public class SessionJoined : ConnectionNegotiatedState
{
public override MultiplayerSessionConnectionStage CurrentStage => MultiplayerSessionConnectionStage.SESSION_JOINED;
public override void JoinSession(IMultiplayerSessionConnectionContext sessionConnectionContext)
{
throw new InvalidOperationException("The session is already in progress.");
}
}
}

View File

@@ -0,0 +1,15 @@
using System;
using NitroxClient.Communication.Abstract;
namespace NitroxClient.Communication.MultiplayerSession.ConnectionState
{
public class SessionReservationRejected : ConnectionNegotiatedState
{
public override MultiplayerSessionConnectionStage CurrentStage => MultiplayerSessionConnectionStage.SESSION_RESERVATION_REJECTED;
public override void JoinSession(IMultiplayerSessionConnectionContext sessionConnectionContext)
{
throw new InvalidOperationException("The session has rejected the reserveration request.");
}
}
}

View File

@@ -0,0 +1,51 @@
using System;
using NitroxClient.Communication.Abstract;
using NitroxModel.Packets;
namespace NitroxClient.Communication.MultiplayerSession.ConnectionState
{
public class SessionReserved : ConnectionNegotiatedState
{
public override MultiplayerSessionConnectionStage CurrentStage => MultiplayerSessionConnectionStage.SESSION_RESERVED;
public override void JoinSession(IMultiplayerSessionConnectionContext sessionConnectionContext)
{
try
{
ValidateState(sessionConnectionContext);
EnterMultiplayerSession(sessionConnectionContext);
ChangeState(sessionConnectionContext);
}
catch (Exception)
{
Disconnect(sessionConnectionContext);
throw;
}
}
private static void ValidateState(IMultiplayerSessionConnectionContext sessionConnectionContext)
{
if (!sessionConnectionContext.Client.IsConnected)
{
throw new InvalidOperationException("The client is not connected.");
}
}
private void EnterMultiplayerSession(IMultiplayerSessionConnectionContext sessionConnectionContext)
{
IClient client = sessionConnectionContext.Client;
MultiplayerSessionReservation reservation = sessionConnectionContext.Reservation;
string correlationId = reservation.CorrelationId;
string reservationKey = reservation.ReservationKey;
PlayerJoiningMultiplayerSession packet = new PlayerJoiningMultiplayerSession(correlationId, reservationKey);
client.Send(packet);
}
private void ChangeState(IMultiplayerSessionConnectionContext sessionConnectionContext)
{
SessionJoined nextState = new SessionJoined();
sessionConnectionContext.UpdateConnectionState(nextState);
}
}
}