first commit
This commit is contained in:
@@ -0,0 +1,186 @@
|
||||
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<string> 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<PlayerPreferenceManager>();
|
||||
activePlayerPreference = preferencesManager.GetPreference(serverIp);
|
||||
multiplayerSession = NitroxServiceLocator.LocateService<IMultiplayerSession>();
|
||||
|
||||
if (!multiplayerClient)
|
||||
{
|
||||
multiplayerClient = new GameObject("Nitrox Multiplayer Client");
|
||||
multiplayerClient.AddComponent<Multiplayer>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method starts a connection with the provided server but leaves handling the session negotiation for the caller.
|
||||
/// </summary>
|
||||
public static async Task StartDetachedMultiplayerClientAsync(IPAddress ip, int port, MultiplayerSessionConnectionStateChangedEventHandler sessionHandler)
|
||||
{
|
||||
multiplayerClient = new GameObject("Nitrox Multiplayer Client");
|
||||
Task task = StartMultiplayerClientAsync(ip, port);
|
||||
multiplayerClient.AddComponent<Multiplayer>();
|
||||
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;
|
||||
}
|
||||
}
|
@@ -0,0 +1,59 @@
|
||||
using NitroxClient.Unity.Helper;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace NitroxClient.MonoBehaviours.Gui.MainMenu.ServerJoin;
|
||||
|
||||
public class MainMenuColorPickerPreview : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
|
||||
{
|
||||
private Image previewImage;
|
||||
private CanvasGroup cg;
|
||||
|
||||
public void Init(uGUI_ColorPicker colorPicker)
|
||||
{
|
||||
GameObject colorPreview = new("ColorPreview");
|
||||
colorPreview.transform.SetParent(colorPicker.pointer.transform);
|
||||
colorPreview.transform.localPosition = new Vector3(-30, 30, 0);
|
||||
colorPreview.transform.localRotation = Quaternion.identity;
|
||||
colorPreview.transform.localScale = new Vector3(0.4f, 0.4f, 0.4f);
|
||||
previewImage = colorPreview.AddComponent<Image>();
|
||||
previewImage.sprite = CreateCircleSprite();
|
||||
cg = colorPreview.AddComponent<CanvasGroup>();
|
||||
cg.alpha = 0;
|
||||
|
||||
colorPicker.onColorChange.AddListener(OnColorPickerDrag);
|
||||
}
|
||||
|
||||
private static Sprite CreateCircleSprite()
|
||||
{
|
||||
const int HALF_SIZE = 50;
|
||||
const int RADIUS = 42;
|
||||
Texture2D tex = new(HALF_SIZE * 2, HALF_SIZE * 2);
|
||||
for (int y = -HALF_SIZE; y <= HALF_SIZE; y++)
|
||||
{
|
||||
for (int x = -HALF_SIZE; x <= HALF_SIZE; x++)
|
||||
{
|
||||
bool isInsideCircle = x * x + y * y <= RADIUS * RADIUS;
|
||||
tex.SetPixel(HALF_SIZE + x, HALF_SIZE + y, isInsideCircle ? Color.white : Color.clear);
|
||||
}
|
||||
}
|
||||
|
||||
tex.Apply();
|
||||
return Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f), 200);
|
||||
}
|
||||
|
||||
private void OnColorPickerDrag(ColorChangeEventData data) => previewImage.color = data.color;
|
||||
|
||||
public void OnPointerDown(PointerEventData _)
|
||||
{
|
||||
StopAllCoroutines();
|
||||
StartCoroutine(cg.ShiftAlpha(1, 0.25f, 1.5f, true));
|
||||
}
|
||||
|
||||
public void OnPointerUp(PointerEventData _)
|
||||
{
|
||||
StopAllCoroutines();
|
||||
StartCoroutine(cg.ShiftAlpha(0, 0.25f, 1.5f, false));
|
||||
}
|
||||
}
|
@@ -0,0 +1,251 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using FMODUnity;
|
||||
using NitroxClient.MonoBehaviours.Gui.MainMenu.ServersList;
|
||||
using NitroxClient.Unity.Helper;
|
||||
using NitroxModel.DataStructures.Util;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace NitroxClient.MonoBehaviours.Gui.MainMenu.ServerJoin;
|
||||
|
||||
public class MainMenuEnterPasswordPanel : MonoBehaviour, uGUI_INavigableIconGrid, uGUI_IButtonReceiver
|
||||
{
|
||||
public const string NAME = "MultiplayerEnterPassword";
|
||||
|
||||
public static MainMenuEnterPasswordPanel Instance { get; private set; }
|
||||
|
||||
private TMP_InputField passwordInput;
|
||||
private mGUI_Change_Legend_On_Select legendChange;
|
||||
|
||||
private GameObject selectedItem;
|
||||
private GameObject[] selectableItems;
|
||||
|
||||
private static string lastEnteredPassword;
|
||||
public static Optional<string> LastEnteredPassword => lastEnteredPassword != null ? Optional.Of(lastEnteredPassword) : Optional.Empty;
|
||||
public static void ResetLastEnteredPassword() => lastEnteredPassword = null;
|
||||
|
||||
public void Setup(GameObject savedGamesRef)
|
||||
{
|
||||
Instance = this;
|
||||
|
||||
GameObject multiplayerButtonRef = savedGamesRef.RequireGameObject("Scroll View/Viewport/SavedGameAreaContent/NewGame");
|
||||
GameObject generalTextRef = multiplayerButtonRef.GetComponentInChildren<TextMeshProUGUI>().gameObject;
|
||||
GameObject inputFieldRef = GameObject.Find("/Menu canvas/Panel/MainMenu/RightSide/Home/EmailBox/InputField");
|
||||
|
||||
GameObject passwordInputGameObject = Instantiate(inputFieldRef, transform, false);
|
||||
passwordInputGameObject.transform.localPosition = new Vector3(-160, 300, 0);
|
||||
passwordInputGameObject.GetComponent<RectTransform>().sizeDelta = new Vector2(300, 40);
|
||||
passwordInput = passwordInputGameObject.GetComponent<TMP_InputField>();
|
||||
passwordInput.characterValidation = TMP_InputField.CharacterValidation.None;
|
||||
passwordInput.onSubmit = new TMP_InputField.SubmitEvent();
|
||||
passwordInput.onSubmit.AddListener(_ => OnConfirmButtonClicked());
|
||||
passwordInput.placeholder.GetComponent<TranslationLiveUpdate>().translationKey = Language.main.Get("Nitrox_JoinServerPlaceholder");
|
||||
GameObject passwordInputDesc = Instantiate(generalTextRef, passwordInputGameObject.transform, false);
|
||||
passwordInputDesc.transform.localPosition = new Vector3(-200, 0, 0);
|
||||
passwordInputDesc.GetComponent<TextMeshProUGUI>().text = Language.main.Get("Nitrox_JoinServerPassword");
|
||||
|
||||
GameObject confirmButton = Instantiate(multiplayerButtonRef, transform, false);
|
||||
confirmButton.transform.localPosition = new Vector3(-200, 90, 0);
|
||||
confirmButton.transform.GetChild(0).GetComponent<RectTransform>().sizeDelta = new Vector2(200, 40);
|
||||
confirmButton.GetComponentInChildren<TextMeshProUGUI>().text = Language.main.Get("Nitrox_Confirm");
|
||||
Button confirmButtonButton = confirmButton.RequireTransform("NewGameButton").GetComponent<Button>();
|
||||
confirmButtonButton.onClick = new Button.ButtonClickedEvent();
|
||||
confirmButtonButton.onClick.AddListener(OnConfirmButtonClicked);
|
||||
|
||||
GameObject backButton = Instantiate(multiplayerButtonRef, transform, false);
|
||||
backButton.transform.localPosition = new Vector3(-200, 40, 0);
|
||||
backButton.transform.GetChild(0).GetComponent<RectTransform>().sizeDelta = new Vector2(200, 40);
|
||||
backButton.GetComponentInChildren<TextMeshProUGUI>().text = Language.main.Get("Nitrox_Cancel");
|
||||
Button backButtonButton = backButton.RequireTransform("NewGameButton").GetComponent<Button>();
|
||||
backButtonButton.onClick = new Button.ButtonClickedEvent();
|
||||
backButtonButton.onClick.AddListener(OnCancelClick);
|
||||
|
||||
selectableItems = [passwordInputGameObject, confirmButton, backButton];
|
||||
Destroy(transform.Find("Scroll View").gameObject);
|
||||
|
||||
legendChange = gameObject.AddComponent<mGUI_Change_Legend_On_Select>();
|
||||
legendChange.legendButtonConfiguration = confirmButtonButton.GetComponent<mGUI_Change_Legend_On_Select>().legendButtonConfiguration.Take(1).ToArray();
|
||||
}
|
||||
|
||||
public void FocusPasswordField()
|
||||
{
|
||||
StartCoroutine(Coroutine());
|
||||
|
||||
IEnumerator Coroutine()
|
||||
{
|
||||
passwordInput.Select();
|
||||
EventSystem.current.SetSelectedGameObject(passwordInput.gameObject);
|
||||
yield return null;
|
||||
passwordInput.MoveToEndOfLine(false, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnConfirmButtonClicked()
|
||||
{
|
||||
lastEnteredPassword = passwordInput.text;
|
||||
MainMenuRightSide.main.OpenGroup(MainMenuJoinServerPanel.NAME);
|
||||
MainMenuJoinServerPanel.Instance.FocusNameInputField();
|
||||
}
|
||||
|
||||
private static void OnCancelClick()
|
||||
{
|
||||
JoinServerBackend.StopMultiplayerClient();
|
||||
MainMenuRightSide.main.OpenGroup(MainMenuServerListPanel.NAME);
|
||||
}
|
||||
|
||||
public bool OnButtonDown(GameInput.Button button)
|
||||
{
|
||||
switch (button)
|
||||
{
|
||||
case GameInput.Button.UISubmit:
|
||||
OnConfirm();
|
||||
return true;
|
||||
case GameInput.Button.UICancel:
|
||||
OnBack();
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnConfirm()
|
||||
{
|
||||
if (selectedItem.TryGetComponentInChildren(out TMP_InputField inputField))
|
||||
{
|
||||
inputField.ActivateInputField();
|
||||
}
|
||||
|
||||
if (selectedItem.TryGetComponentInChildren(out Button button))
|
||||
{
|
||||
button.onClick.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnBack()
|
||||
{
|
||||
passwordInput.text = string.Empty;
|
||||
ResetLastEnteredPassword();
|
||||
DeselectAllItems();
|
||||
MainMenuRightSide.main.OpenGroup(MainMenuServerListPanel.NAME);
|
||||
}
|
||||
|
||||
bool uGUI_INavigableIconGrid.ShowSelector => false;
|
||||
bool uGUI_INavigableIconGrid.EmulateRaycast => false;
|
||||
bool uGUI_INavigableIconGrid.SelectItemClosestToPosition(Vector3 worldPos) => false;
|
||||
uGUI_INavigableIconGrid uGUI_INavigableIconGrid.GetNavigableGridInDirection(int dirX, int dirY) => null;
|
||||
Graphic uGUI_INavigableIconGrid.GetSelectedIcon() => null;
|
||||
|
||||
object uGUI_INavigableIconGrid.GetSelectedItem() => selectedItem;
|
||||
|
||||
public void SelectItem(object item)
|
||||
{
|
||||
DeselectItem();
|
||||
selectedItem = item as GameObject;
|
||||
|
||||
legendChange.SyncLegendBarToGUISelection();
|
||||
|
||||
if (!selectedItem)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedItem.TryGetComponent(out TMP_InputField selectedInputField))
|
||||
{
|
||||
selectedInputField.Select();
|
||||
}
|
||||
else // Buttons
|
||||
{
|
||||
selectedItem.GetComponentInChildren<uGUI_BasicColorSwap>().makeTextBlack();
|
||||
selectedItem.transform.GetChild(0).GetComponent<Image>().sprite = MainMenuServerListPanel.SelectedSprite;
|
||||
}
|
||||
|
||||
if (!EventSystem.current.alreadySelecting)
|
||||
{
|
||||
EventSystem.current.SetSelectedGameObject(selectedItem);
|
||||
}
|
||||
|
||||
RuntimeManager.PlayOneShot(MainMenuServerListPanel.HoverSound.path);
|
||||
}
|
||||
|
||||
public void DeselectItem()
|
||||
{
|
||||
if (!selectedItem)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedItem.TryGetComponent(out TMP_InputField selectedInputField))
|
||||
{
|
||||
selectedInputField.DeactivateInputField();
|
||||
selectedInputField.ReleaseSelection();
|
||||
}
|
||||
else // Buttons
|
||||
{
|
||||
selectedItem.GetComponentInChildren<uGUI_BasicColorSwap>().makeTextWhite();
|
||||
selectedItem.transform.GetChild(0).GetComponent<Image>().sprite = MainMenuServerListPanel.NormalSprite;
|
||||
}
|
||||
|
||||
if (!EventSystem.current.alreadySelecting)
|
||||
{
|
||||
EventSystem.current.SetSelectedGameObject(null);
|
||||
}
|
||||
|
||||
selectedItem = null;
|
||||
}
|
||||
|
||||
public void DeselectAllItems()
|
||||
{
|
||||
foreach (GameObject child in selectableItems)
|
||||
{
|
||||
selectedItem = child;
|
||||
DeselectItem();
|
||||
}
|
||||
}
|
||||
|
||||
public bool SelectFirstItem()
|
||||
{
|
||||
SelectItem(selectableItems[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool SelectItemInDirection(int dirX, int dirY)
|
||||
{
|
||||
if (!selectedItem)
|
||||
{
|
||||
return SelectFirstItem();
|
||||
}
|
||||
|
||||
if (dirX == dirY)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int dir = dirX + dirY > 0 ? 1 : -1;
|
||||
for (int newIndex = GetSelectedIndex() + dir; newIndex >= 0 && newIndex < selectableItems.Length; newIndex += dir)
|
||||
{
|
||||
if (SelectItemByIndex(newIndex))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private int GetSelectedIndex() => selectedItem ? Array.IndexOf(selectableItems, selectedItem) : -1;
|
||||
|
||||
private bool SelectItemByIndex(int selectedIndex)
|
||||
{
|
||||
if (selectedIndex >= 0 && selectedIndex < selectableItems.Length)
|
||||
{
|
||||
SelectItem(selectableItems[selectedIndex]);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@@ -0,0 +1,363 @@
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using FMODUnity;
|
||||
using NitroxClient.MonoBehaviours.Gui.MainMenu.ServersList;
|
||||
using NitroxClient.Unity.Helper;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
using UnityEngine.UI;
|
||||
using UWE;
|
||||
|
||||
namespace NitroxClient.MonoBehaviours.Gui.MainMenu.ServerJoin;
|
||||
|
||||
public class MainMenuJoinServerPanel : MonoBehaviour, uGUI_INavigableIconGrid, uGUI_IButtonReceiver, uGUI_IScrollReceiver, uGUI_IAdjustReceiver
|
||||
{
|
||||
public const string NAME = "MultiplayerJoinServer";
|
||||
|
||||
public static MainMenuJoinServerPanel Instance { get; private set; }
|
||||
|
||||
private GameObject playerSettingsPanel;
|
||||
private TextMeshProUGUI header;
|
||||
private uGUI_ColorPicker colorPicker;
|
||||
private MainMenuColorPickerPreview colorPickerPreview;
|
||||
private Slider saturationSlider;
|
||||
private uGUI_InputField playerNameInputField;
|
||||
|
||||
private GameObject selectedItem;
|
||||
private GameObject[] selectableItems;
|
||||
|
||||
public void Setup(GameObject savedGamesRef)
|
||||
{
|
||||
Instance = this;
|
||||
Destroy(transform.RequireGameObject("Scroll View"));
|
||||
Destroy(GetComponentInChildren<TranslationLiveUpdate>());
|
||||
header = GetComponentInChildren<TextMeshProUGUI>();
|
||||
|
||||
CoroutineHost.StartCoroutine(AsyncSetup(savedGamesRef)); // As JoinServer waits for AsyncSetup to be completed we can't use normal Unity IEnumerator Start()
|
||||
}
|
||||
|
||||
private IEnumerator AsyncSetup(GameObject savedGamesRef)
|
||||
{
|
||||
AsyncOperationHandle<GameObject> request = AddressablesUtility.LoadAsync<GameObject>("Assets/Prefabs/Base/GeneratorPieces/BaseMoonpoolUpgradeConsole.prefab");
|
||||
yield return request;
|
||||
GameObject colorPickerPanelPrototype = request.Result.RequireGameObject("EditScreen/Active");
|
||||
|
||||
RectTransform parent = GetComponent<RectTransform>();
|
||||
|
||||
GameObject newGameButtonRef = savedGamesRef.RequireGameObject("Scroll View/Viewport/SavedGameAreaContent/NewGame/NewGameButton");
|
||||
LegendButtonData[] defaultLegend = newGameButtonRef.GetComponent<mGUI_Change_Legend_On_Select>().legendButtonConfiguration.Take(1).ToArray();
|
||||
|
||||
//Create a clone of the RocketBase color picker panel.
|
||||
playerSettingsPanel = Instantiate(colorPickerPanelPrototype, parent);
|
||||
|
||||
//Prepares name input field
|
||||
GameObject inputField = playerSettingsPanel.RequireGameObject("InputField");
|
||||
inputField.transform.SetParent(parent);
|
||||
inputField.transform.localPosition = new Vector3(-200, 310, 0);
|
||||
inputField.transform.localScale = Vector3.one;
|
||||
inputField.AddComponent<mGUI_Change_Legend_On_Select>().legendButtonConfiguration = defaultLegend;
|
||||
playerNameInputField = inputField.GetComponent<uGUI_InputField>();
|
||||
((TextMeshProUGUI)playerNameInputField.placeholder).text = Language.main.Get("Nitrox_EnterName");
|
||||
playerNameInputField.textComponent.fontSizeMin = 17;
|
||||
playerNameInputField.textComponent.fontSizeMax = 21;
|
||||
playerNameInputField.textComponent.GetComponent<RectTransform>().sizeDelta = new Vector2(-20, 42);
|
||||
playerNameInputField.characterLimit = 25; // See this.OnJoinClick()
|
||||
playerNameInputField.onFocusSelectAll = false;
|
||||
playerNameInputField.onSubmit.AddListener(_ => OnJoinClick());
|
||||
playerNameInputField.onSubmit.AddListener(_ => DeselectAllItems());
|
||||
playerNameInputField.ActivateInputField();
|
||||
|
||||
//Prepares player color picker
|
||||
GameObject colorPickerObject = playerSettingsPanel.RequireGameObject("ColorPicker");
|
||||
colorPickerObject.transform.SetParent(parent);
|
||||
colorPickerObject.transform.localPosition = new Vector3(-268, 175, 0);
|
||||
colorPickerObject.transform.localScale = new Vector3(1.1f, 0.75f, 1);
|
||||
colorPicker = colorPickerObject.GetComponentInChildren<uGUI_ColorPicker>();
|
||||
colorPicker.pointer.localScale = new Vector3(1f, 1.46f, 1);
|
||||
saturationSlider = colorPicker.saturationSlider;
|
||||
saturationSlider.transform.localPosition = new Vector3(197, 0, 0);
|
||||
colorPickerPreview = colorPicker.gameObject.AddComponent<MainMenuColorPickerPreview>();
|
||||
colorPickerPreview.Init(colorPicker);
|
||||
|
||||
GameObject buttonLeft = Instantiate(newGameButtonRef, parent);
|
||||
buttonLeft.GetComponent<RectTransform>().sizeDelta = new Vector2(160, 45);
|
||||
buttonLeft.GetComponent<mGUI_Change_Legend_On_Select>().legendButtonConfiguration = defaultLegend;
|
||||
GameObject buttonRight = Instantiate(buttonLeft, parent);
|
||||
|
||||
//Prepares cancel button
|
||||
buttonLeft.transform.SetParent(parent);
|
||||
buttonLeft.transform.localPosition = new Vector3(-285, 40, 0);
|
||||
buttonLeft.GetComponentInChildren<TextMeshProUGUI>().text = Language.main.Get("Nitrox_Cancel");
|
||||
Button cancelButton = buttonLeft.GetComponent<Button>();
|
||||
cancelButton.onClick = new Button.ButtonClickedEvent();
|
||||
cancelButton.onClick.AddListener(OnCancelClick);
|
||||
cancelButton.onClick.AddListener(DeselectAllItems);
|
||||
|
||||
//Prepares join button
|
||||
buttonRight.transform.localPosition = new Vector3(-115, 40, 0);
|
||||
buttonRight.GetComponentInChildren<TextMeshProUGUI>().text = Language.main.Get("Nitrox_Join");
|
||||
Button joinButton = buttonRight.GetComponent<Button>();
|
||||
joinButton.onClick = new Button.ButtonClickedEvent();
|
||||
joinButton.onClick.AddListener(OnJoinClick);
|
||||
joinButton.onClick.AddListener(DeselectAllItems);
|
||||
|
||||
selectableItems = [inputField, colorPicker.gameObject, saturationSlider.gameObject, buttonLeft, buttonRight];
|
||||
Destroy(playerSettingsPanel);
|
||||
}
|
||||
|
||||
private void OnJoinClick()
|
||||
{
|
||||
string playerName = playerNameInputField.text;
|
||||
|
||||
//https://regex101.com/r/eTWiEs/2/
|
||||
if (!Regex.IsMatch(playerName, "^[a-zA-Z0-9._-]{3,25}$"))
|
||||
{
|
||||
MainMenuNotificationPanel.ShowMessage(Language.main.Get("Nitrox_InvalidUserName"), NAME);
|
||||
return;
|
||||
}
|
||||
|
||||
JoinServerBackend.RequestSessionReservation(playerName, colorPicker.currentColor);
|
||||
}
|
||||
|
||||
private static void OnCancelClick()
|
||||
{
|
||||
JoinServerBackend.StopMultiplayerClient();
|
||||
MainMenuRightSide.main.OpenGroup(MainMenuServerListPanel.NAME);
|
||||
}
|
||||
|
||||
public void UpdatePanelValues(string serverName) => header.text = $" {Language.main.Get("Nitrox_JoinServer")} {serverName}";
|
||||
|
||||
public void UpdatePlayerPanelValues(string playerName, Vector3 hsb)
|
||||
{
|
||||
playerNameInputField.text = playerName;
|
||||
colorPicker.SetHSB(hsb);
|
||||
}
|
||||
|
||||
public void FocusNameInputField()
|
||||
{
|
||||
StartCoroutine(Coroutine());
|
||||
IEnumerator Coroutine()
|
||||
{
|
||||
SelectFirstItem();
|
||||
yield return new WaitForEndOfFrame();
|
||||
playerNameInputField.MoveToEndOfLine(false, true);
|
||||
}
|
||||
}
|
||||
|
||||
public bool OnButtonDown(GameInput.Button button)
|
||||
{
|
||||
if (button != GameInput.Button.UISubmit || !selectedItem)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (selectedItem.TryGetComponentInChildren(out TMP_InputField inputField))
|
||||
{
|
||||
inputField.Select();
|
||||
inputField.ActivateInputField();
|
||||
}
|
||||
else if (selectedItem.TryGetComponentInChildren(out Button buttonComponent))
|
||||
{
|
||||
buttonComponent.onClick.Invoke();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool OnScroll(float scrollDelta, float speedMultiplier)
|
||||
{
|
||||
if (EventSystem.current != null &&
|
||||
EventSystem.current.currentSelectedGameObject == selectedItem &&
|
||||
selectedItem.TryGetComponent(out Slider slider))
|
||||
{
|
||||
slider.value += scrollDelta * speedMultiplier * 0.01f;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool OnAdjust(Vector2 adjustDelta)
|
||||
{
|
||||
if (selectedItem && selectedItem.TryGetComponent(out uGUI_ColorPicker selectedColorPicker))
|
||||
{
|
||||
return selectedColorPicker.OnAdjust(adjustDelta);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
object uGUI_INavigableIconGrid.GetSelectedItem() => selectedItem;
|
||||
|
||||
bool uGUI_INavigableIconGrid.ShowSelector => false;
|
||||
bool uGUI_INavigableIconGrid.EmulateRaycast => false;
|
||||
bool uGUI_INavigableIconGrid.SelectItemClosestToPosition(Vector3 worldPos) => false;
|
||||
uGUI_INavigableIconGrid uGUI_INavigableIconGrid.GetNavigableGridInDirection(int dirX, int dirY) => null;
|
||||
Graphic uGUI_INavigableIconGrid.GetSelectedIcon() => null;
|
||||
|
||||
public void SelectItem(object item)
|
||||
{
|
||||
DeselectItem();
|
||||
selectedItem = item as GameObject;
|
||||
|
||||
if (!selectedItem)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedItem.TryGetComponent(out mGUI_Change_Legend_On_Select changeLegend))
|
||||
{
|
||||
changeLegend.SyncLegendBarToGUISelection();
|
||||
}
|
||||
else
|
||||
{
|
||||
uGUI_LegendBar.ClearButtons();
|
||||
}
|
||||
|
||||
if (selectedItem == selectableItems[1])
|
||||
{
|
||||
colorPicker.pointer.GetComponent<Image>().color = Color.cyan;
|
||||
if (GameInput.GetPrimaryDevice() == GameInput.Device.Controller)
|
||||
{
|
||||
colorPickerPreview.OnPointerDown(null);
|
||||
}
|
||||
}
|
||||
else if (selectedItem == selectableItems[3] || selectedItem == selectableItems[4])
|
||||
{
|
||||
selectedItem.GetComponentInChildren<uGUI_BasicColorSwap>().makeTextBlack();
|
||||
}
|
||||
|
||||
if (selectedItem.TryGetComponentInChildren(out Selectable selectable))
|
||||
{
|
||||
selectable.Select();
|
||||
}
|
||||
|
||||
if (!EventSystem.current.alreadySelecting)
|
||||
{
|
||||
EventSystem.current.SetSelectedGameObject(selectedItem);
|
||||
}
|
||||
|
||||
RuntimeManager.PlayOneShot(MainMenuServerListPanel.HoverSound.path);
|
||||
}
|
||||
|
||||
public void DeselectItem()
|
||||
{
|
||||
if (!selectedItem)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedItem.TryGetComponent(out TMP_InputField selectedInputField))
|
||||
{
|
||||
//This line need to be before selectedInputField.ReleaseSelection() as it will call this method recursive leading to NRE
|
||||
selectedInputField.DeactivateInputField();
|
||||
selectedInputField.ReleaseSelection();
|
||||
}
|
||||
else if (selectedItem.TryGetComponent(out uGUI_ColorPicker selectedColorPicker))
|
||||
{
|
||||
Image colorPickerPointer = selectedColorPicker.pointer.GetComponent<Image>();
|
||||
|
||||
if (colorPickerPointer.color != Color.white &&
|
||||
GameInput.GetPrimaryDevice() == GameInput.Device.Controller)
|
||||
{
|
||||
colorPickerPreview.OnPointerUp(null);
|
||||
}
|
||||
colorPickerPointer.color = Color.white;
|
||||
}
|
||||
else if (selectedItem.TryGetComponentInChildren(out uGUI_BasicColorSwap colorSwap))
|
||||
{
|
||||
colorSwap.makeTextWhite();
|
||||
}
|
||||
|
||||
if (!EventSystem.current.alreadySelecting)
|
||||
{
|
||||
EventSystem.current.SetSelectedGameObject(null);
|
||||
}
|
||||
|
||||
selectedItem = null;
|
||||
}
|
||||
|
||||
public void DeselectAllItems()
|
||||
{
|
||||
foreach (GameObject item in selectableItems)
|
||||
{
|
||||
selectedItem = item;
|
||||
DeselectItem();
|
||||
}
|
||||
}
|
||||
|
||||
public bool SelectFirstItem()
|
||||
{
|
||||
SelectItem(selectableItems[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool SelectItemInDirection(int dirX, int dirY)
|
||||
{
|
||||
if (!selectedItem)
|
||||
{
|
||||
return SelectFirstItem();
|
||||
}
|
||||
|
||||
if (selectedItem == selectableItems[0]) //Name input
|
||||
{
|
||||
switch (dirY)
|
||||
{
|
||||
case < 0:
|
||||
SelectItem(selectableItems[^2]);
|
||||
return true;
|
||||
case > 0:
|
||||
SelectItem(selectableItems[1]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedItem == selectableItems[1] || selectedItem == selectableItems[2]) // ColorPicker and SaturationSlider
|
||||
{
|
||||
switch (dirY)
|
||||
{
|
||||
case < 0:
|
||||
SelectItem(selectableItems[0]);
|
||||
return true;
|
||||
case > 0:
|
||||
SelectItem(selectableItems[3]);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (dirX != 0)
|
||||
{
|
||||
int direction = selectedItem == selectableItems[1] ? 0 : 1;
|
||||
direction = (direction + dirX) % 2;
|
||||
|
||||
SelectItem(selectableItems[1 + direction]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedItem == selectableItems[3] || selectedItem == selectableItems[4]) // CancelButton and ConfirmButton
|
||||
{
|
||||
switch (dirY)
|
||||
{
|
||||
case < 0:
|
||||
SelectItem(selectableItems[1]);
|
||||
return true;
|
||||
case > 0:
|
||||
SelectItem(selectableItems[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (dirX != 0)
|
||||
{
|
||||
int direction = selectedItem == selectableItems[3] ? 0 : 1;
|
||||
direction = (direction + dirX) % 2;
|
||||
|
||||
SelectItem(selectableItems[3 + direction]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user