using System.Collections; using System.Diagnostics; using System.Net; using System.Threading.Tasks; using NitroxClient.Communication.Abstract; using NitroxClient.Communication.Exceptions; using NitroxClient.Communication.MultiplayerSession; using NitroxClient.GameLogic.PlayerLogic.PlayerPreferences; using NitroxClient.MonoBehaviours.Gui.MainMenu.ServersList; using NitroxModel.Core; using NitroxModel.DataStructures.Util; using NitroxModel.Helper; using NitroxModel.MultiplayerSession; using NitroxModel_Subnautica.DataStructures; using UnityEngine; namespace NitroxClient.MonoBehaviours.Gui.MainMenu.ServerJoin; public static class JoinServerBackend { private static PlayerPreferenceManager preferencesManager; private static PlayerPreference activePlayerPreference; private static IMultiplayerSession multiplayerSession; private static GameObject multiplayerClient; private static string serverIp; private static int serverPort; public static void RequestSessionReservation(string playerName, Color playerColor) { preferencesManager.SetPreference(serverIp, new PlayerPreference(playerName, playerColor)); Optional opPassword = MainMenuEnterPasswordPanel.LastEnteredPassword; AuthenticationContext authenticationContext = new(playerName, opPassword); multiplayerSession.RequestSessionReservation(new PlayerSettings(playerColor.ToDto()), authenticationContext); } private static void SessionConnectionStateChangedHandler(IMultiplayerSessionConnectionState state) { switch (state.CurrentStage) { case MultiplayerSessionConnectionStage.ESTABLISHING_SERVER_POLICY: Log.Info("Requesting session policy info"); Log.InGame(Language.main.Get("Nitrox_RequestingSessionPolicy")); break; case MultiplayerSessionConnectionStage.AWAITING_RESERVATION_CREDENTIALS: Color.RGBToHSV(activePlayerPreference.PreferredColor(), out float hue, out float saturation, out float brightness); // HSV => Hue Saturation Value, HSB => Hue Saturation Brightness MainMenuJoinServerPanel.Instance.UpdatePlayerPanelValues(activePlayerPreference.PlayerName, new Vector3(hue, saturation, brightness)); if (multiplayerSession.SessionPolicy.RequiresServerPassword) { Log.Info("Waiting for server password input"); Log.InGame(Language.main.Get("Nitrox_WaitingPassword")); MainMenuEnterPasswordPanel.ResetLastEnteredPassword(); MainMenuRightSide.main.OpenGroup(MainMenuEnterPasswordPanel.NAME); MainMenuEnterPasswordPanel.Instance.FocusPasswordField(); break; } Log.Info("Waiting for user input"); Log.InGame(Language.main.Get("Nitrox_WaitingUserInput")); MainMenuRightSide.main.OpenGroup(MainMenuJoinServerPanel.NAME); MainMenuJoinServerPanel.Instance.FocusNameInputField(); break; case MultiplayerSessionConnectionStage.SESSION_RESERVED: Log.Info("Launching game"); Log.InGame(Language.main.Get("Nitrox_LaunchGame")); multiplayerSession.ConnectionStateChanged -= SessionConnectionStateChangedHandler; preferencesManager.Save(); StartGame(); break; case MultiplayerSessionConnectionStage.SESSION_RESERVATION_REJECTED: Log.Info("Reservation rejected"); Log.InGame(Language.main.Get("Nitrox_RejectedSessionPolicy")); MultiplayerSessionReservationState reservationState = multiplayerSession.Reservation.ReservationState; string reservationRejectionNotification = reservationState.Describe(); MainMenuNotificationPanel.ShowMessage(reservationRejectionNotification, null, () => { multiplayerSession.Disconnect(); multiplayerSession.ConnectAsync(serverIp, serverPort); }); break; case MultiplayerSessionConnectionStage.DISCONNECTED: Log.Info(Language.main.Get("Nitrox_DisconnectedSession")); break; } } public static async Task StartMultiplayerClientAsync(IPAddress ip, int port) { serverIp = ip.ToString(); serverPort = port; NitroxServiceLocator.BeginNewLifetimeScope(); preferencesManager = NitroxServiceLocator.LocateService(); activePlayerPreference = preferencesManager.GetPreference(serverIp); multiplayerSession = NitroxServiceLocator.LocateService(); if (!multiplayerClient) { multiplayerClient = new GameObject("Nitrox Multiplayer Client"); multiplayerClient.AddComponent(); multiplayerSession.ConnectionStateChanged += SessionConnectionStateChangedHandler; } try { await multiplayerSession.ConnectAsync(serverIp, serverPort); } catch (ClientConnectionFailedException ex) { Log.ErrorSensitive("Unable to contact the remote server at: {ip}:{port}", serverIp, serverPort); string msg = $"{Language.main.Get("Nitrox_UnableToConnect")} {serverIp}:{serverPort}"; if (ip.IsLocalhost()) { if (Process.GetProcessesByName("NitroxServer-Subnautica").Length == 0) { Log.Error("No server process was found while address was localhost"); msg += $"\n{Language.main.Get("Nitrox_StartServer")}"; } else { Log.Error(ex); msg += $"\n{Language.main.Get("Nitrox_FirewallInterfering")}"; } } Log.InGame(msg); StopMultiplayerClient(); MainMenuNotificationPanel.ShowMessage(msg, MainMenuServerListPanel.NAME); } } /// /// This method starts a connection with the provided server but leaves handling the session negotiation for the caller. /// public static async Task StartDetachedMultiplayerClientAsync(IPAddress ip, int port, MultiplayerSessionConnectionStateChangedEventHandler sessionHandler) { multiplayerClient = new GameObject("Nitrox Multiplayer Client"); Task task = StartMultiplayerClientAsync(ip, port); multiplayerClient.AddComponent(); multiplayerSession.ConnectionStateChanged += sessionHandler; await task; } public static void StartGame() { #pragma warning disable CS0618 // God Damn it UWE... Multiplayer.SubnauticaLoadingStarted(); IEnumerator startNewGame = uGUI_MainMenu.main.StartNewGame(GameMode.Survival); #pragma warning restore CS0618 // God damn it UWE... UWE.CoroutineHost.StartCoroutine(startNewGame); LoadingScreenVersionText.Initialize(); } public static void StopMultiplayerClient() { if (!multiplayerClient || !Multiplayer.Main) { return; } if (multiplayerSession.CurrentState.CurrentStage != MultiplayerSessionConnectionStage.DISCONNECTED) { multiplayerSession.Disconnect(); } multiplayerSession.ConnectionStateChanged -= SessionConnectionStateChangedHandler; Multiplayer.Main.StopCurrentSession(); NitroxServiceLocator.EndCurrentLifetimeScope(); //Always do this last. Object.Destroy(multiplayerClient); multiplayerClient = null; } }