using System; using System.Threading.Tasks; using NitroxClient.Communication.Abstract; using NitroxClient.Communication.MultiplayerSession.ConnectionState; using NitroxClient.GameLogic; using NitroxModel.DataStructures; using NitroxModel.Helper; using NitroxModel.MultiplayerSession; using NitroxModel.Packets; using NitroxModel.Serialization; namespace NitroxClient.Communication.MultiplayerSession { public class MultiplayerSessionManager : IMultiplayerSession, IMultiplayerSessionConnectionContext { private static readonly Task initSerializerTask; static MultiplayerSessionManager() { initSerializerTask = Task.Run(Packet.InitSerializer); } public IClient Client { get; } public string IpAddress { get; private set; } public int ServerPort { get; private set; } public MultiplayerSessionPolicy SessionPolicy { get; private set; } public PlayerSettings PlayerSettings { get; private set; } public AuthenticationContext AuthenticationContext { get; private set; } public IMultiplayerSessionConnectionState CurrentState { get; private set; } public MultiplayerSessionReservation Reservation { get; private set; } public MultiplayerSessionManager(IClient client) { Log.Info("Initializing MultiplayerSessionManager..."); Client = client; CurrentState = new Disconnected(); } // Testing entry point internal MultiplayerSessionManager(IClient client, IMultiplayerSessionConnectionState initialState) { Client = client; CurrentState = initialState; } public event MultiplayerSessionConnectionStateChangedEventHandler ConnectionStateChanged; public async Task ConnectAsync(string ipAddress, int port) { IpAddress = ipAddress; ServerPort = port; await initSerializerTask; await CurrentState.NegotiateReservationAsync(this); } public void ProcessSessionPolicy(MultiplayerSessionPolicy policy) { SessionPolicy = policy; NitroxConsole.DisableConsole = SessionPolicy.DisableConsole; Version localVersion = NitroxEnvironment.Version; NitroxVersion nitroxVersion = new(localVersion.Major, localVersion.Minor); switch (nitroxVersion.CompareTo(SessionPolicy.NitroxVersionAllowed)) { case -1: Log.Error($"Client is out of date. Server: {SessionPolicy.NitroxVersionAllowed}, Client: {localVersion}"); Log.InGame(Language.main.Get("Nitrox_OutOfDateClient") .Replace("{serverVersion}", SessionPolicy.NitroxVersionAllowed.ToString()) .Replace("{localVersion}", localVersion.ToString())); CurrentState.Disconnect(this); return; case 1: Log.Error($"Server is out of date. Server: {SessionPolicy.NitroxVersionAllowed}, Client: {localVersion}"); Log.InGame(Language.main.Get("Nitrox_OutOfDateServer") .Replace("{serverVersion}", SessionPolicy.NitroxVersionAllowed.ToString()) .Replace("{localVersion}", localVersion.ToString())); CurrentState.Disconnect(this); return; } CurrentState.NegotiateReservationAsync(this); } public void RequestSessionReservation(PlayerSettings playerSettings, AuthenticationContext authenticationContext) { // If a reservation has already been sent (in which case the client is enqueued in the join queue) if (CurrentState.CurrentStage == MultiplayerSessionConnectionStage.AWAITING_SESSION_RESERVATION) { Log.Info("Waiting in join queue…"); Log.InGame(Language.main.Get("Nitrox_Waiting")); return; } PlayerSettings = playerSettings; AuthenticationContext = authenticationContext; CurrentState.NegotiateReservationAsync(this); } public void ProcessReservationResponsePacket(MultiplayerSessionReservation reservation) { if (reservation.ReservationState == MultiplayerSessionReservationState.ENQUEUED_IN_JOIN_QUEUE) { Log.Info("Waiting in join queue…"); Log.InGame(Language.main.Get("Nitrox_Waiting")); return; } Reservation = reservation; CurrentState.NegotiateReservationAsync(this); } public void JoinSession() { CurrentState.JoinSession(this); } public void Disconnect() { if (CurrentState.CurrentStage != MultiplayerSessionConnectionStage.DISCONNECTED) { CurrentState.Disconnect(this); } } public bool Send(T packet) where T : Packet { if (!PacketSuppressor.IsSuppressed) { Client.Send(packet); return true; } return false; } public void UpdateConnectionState(IMultiplayerSessionConnectionState sessionConnectionState) { Validate.NotNull(sessionConnectionState); string fromStage = CurrentState == null ? "null" : CurrentState.CurrentStage.ToString(); string username = AuthenticationContext == null ? "" : AuthenticationContext.Username; Log.Debug($"Updating session stage from '{fromStage}' to '{sessionConnectionState.CurrentStage}' for '{username}'"); CurrentState = sessionConnectionState; // Last connection state changed will not have any handlers ConnectionStateChanged?.Invoke(CurrentState); if (sessionConnectionState.CurrentStage == MultiplayerSessionConnectionStage.SESSION_RESERVED) { Log.PlayerName = username; } } public void ClearSessionState() { IpAddress = null; ServerPort = ServerList.DEFAULT_PORT; SessionPolicy = null; PlayerSettings = null; AuthenticationContext = null; Reservation = null; } } }