Files
Nitrox/NitroxClient/MonoBehaviours/Gui/MainMenu/ServersList/MainMenuServerListPanel.cs
2025-07-06 00:23:46 +02:00

356 lines
12 KiB
C#

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using FMODUnity;
using NitroxClient.Communication;
using NitroxClient.GameLogic.Settings;
using NitroxClient.Unity.Helper;
using NitroxModel;
using NitroxModel.Serialization;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using UWE;
namespace NitroxClient.MonoBehaviours.Gui.MainMenu.ServersList;
public class MainMenuServerListPanel : MonoBehaviour, uGUI_INavigableIconGrid, uGUI_IButtonReceiver
{
public const string NAME = "MultiplayerServerList";
public static MainMenuServerListPanel Main;
public static Sprite NormalSprite;
public static Sprite SelectedSprite;
public static FMODAsset HoverSound;
private GameObject multiplayerNewServerButtonRef;
private GameObject multiplayerServerButtonRef;
private Transform serverAreaContent;
private GameObject selectedServerItem;
private ScrollRect scrollRect;
private GameObject scrollBar;
public bool IsJoining { get; set; }
public void Setup(GameObject savedGamesRef)
{
Main = this;
MainMenuLoadMenu loadMenu = savedGamesRef.GetComponentInChildren<MainMenuLoadMenu>();
NormalSprite = loadMenu.normalSprite;
SelectedSprite = loadMenu.selectedSprite;
HoverSound = loadMenu.hoverSound;
multiplayerNewServerButtonRef = savedGamesRef.RequireGameObject("Scroll View/Viewport/SavedGameAreaContent/NewGame");
serverAreaContent = transform.RequireTransform("Scroll View/Viewport/SavedGameAreaContent");
serverAreaContent.gameObject.name = "ServerAreaContent";
serverAreaContent.GetComponent<GridLayoutGroup>().spacing = new Vector2(0, 5);
scrollRect = transform.RequireGameObject("Scroll View").GetComponent<ScrollRect>();
scrollBar = scrollRect.RequireGameObject("Scrollbar");
multiplayerServerButtonRef = savedGamesRef.GetComponent<MainMenuLoadPanel>().saveInstance;
MainMenuServerButton.Setup(multiplayerServerButtonRef.GetComponent<MainMenuLoadButton>());
RefreshServerEntries();
}
public bool OnButtonDown(GameInput.Button button)
{
switch (button)
{
case GameInput.Button.UISubmit:
OnConfirm();
return true;
case GameInput.Button.UICancel:
OnBack();
return true;
case GameInput.Button.UIClear:
OnClear();
return true;
default:
return false;
}
}
public void OnBack()
{
DeselectAllItems();
MainMenuRightSide.main.OpenGroup("Home");
}
public void OnClear()
{
if (selectedServerItem && selectedServerItem.TryGetComponent(out MainMenuServerButton serverButton))
{
serverButton.RequestDelete();
}
}
public void OnConfirm()
{
if (!selectedServerItem)
{
return;
}
if (selectedServerItem.gameObject.name == "NewServer")
{
DeselectAllItems();
MainMenuRightSide.main.OpenGroup(MainMenuCreateServerPanel.NAME);
}
else if (selectedServerItem.TryGetComponent(out MainMenuServerButton serverButton))
{
_ = serverButton.OnJoinButtonClicked().ContinueWithHandleError(Log.Error);
}
}
object uGUI_INavigableIconGrid.GetSelectedItem() => selectedServerItem;
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();
selectedServerItem = item as GameObject;
if (!selectedServerItem)
{
return;
}
if (selectedServerItem.TryGetComponentInChildren(out mGUI_Change_Legend_On_Select componentInChildren))
{
componentInChildren.SyncLegendBarToGUISelection();
}
if (selectedServerItem == serverAreaContent.GetChild(0).gameObject) // Server Create Button
{
selectedServerItem.transform.Find("NewGameButton").GetComponent<Image>().sprite = SelectedSprite;
}
else
{
selectedServerItem.transform.Find("Load/NewGameButton").GetComponent<Image>().sprite = SelectedSprite;
}
selectedServerItem.GetComponentInChildren<uGUI_BasicColorSwap>();
UIUtils.ScrollToShowItemInCenter(selectedServerItem.transform);
RuntimeManager.PlayOneShot(HoverSound.path);
}
public void DeselectItem()
{
if (!selectedServerItem)
{
return;
}
if (selectedServerItem == serverAreaContent.GetChild(0).gameObject) // Server Create Button
{
selectedServerItem.transform.Find("NewGameButton").GetComponent<Image>().sprite = NormalSprite;
}
else
{
selectedServerItem.transform.Find("Load/NewGameButton").GetComponent<Image>().sprite = NormalSprite;
}
selectedServerItem.GetComponentInChildren<uGUI_BasicColorSwap>().makeTextWhite();
selectedServerItem = null;
}
public void DeselectAllItems()
{
// Create ServerEntry button
serverAreaContent.GetChild(0).Find("NewGameButton").GetComponent<Image>().sprite = NormalSprite;
serverAreaContent.GetChild(0).GetComponentInChildren<uGUI_BasicColorSwap>().makeTextWhite();
// Server buttons
for (int i = 1; i < serverAreaContent.childCount; i++)
{
Transform child = serverAreaContent.GetChild(i);
child.Find("Load/NewGameButton").GetComponent<Image>().sprite = NormalSprite;
child.GetComponentInChildren<uGUI_BasicColorSwap>().makeTextWhite();
child.GetComponent<MainMenuServerButton>().ResetLoadDeleteView();
}
}
public bool SelectFirstItem()
{
MainMenuServerButton firstServerObject = serverAreaContent.GetComponentInChildren<MainMenuServerButton>();
if (firstServerObject)
{
SelectItem(firstServerObject.gameObject);
return true;
}
Transform serverCreationButton = serverAreaContent.GetChild(0);
if (serverCreationButton && serverCreationButton.name == "NewServer")
{
SelectItem(serverCreationButton.gameObject);
return true;
}
return false;
}
public bool SelectItemInDirection(int dirX, int dirY)
{
if (selectedServerItem)
{
return dirY != 0 && SelectItemInYDirection(GetSelectedIndex(), dirY);
}
return SelectFirstItem();
}
public int GetSelectedIndex() => selectedServerItem ? selectedServerItem.transform.GetSiblingIndex() : -1;
public bool SelectItemInYDirection(int selectedIndex, int dirY)
{
int dir = dirY > 0 ? 1 : -1;
for (int newIndex = selectedIndex + dir; newIndex >= 0 && newIndex < serverAreaContent.childCount; newIndex += dir)
{
if (SelectItemByIndex(newIndex))
{
return true;
}
}
return false;
}
public bool SelectItemByIndex(int selectedIndex)
{
if (selectedIndex < serverAreaContent.childCount && selectedIndex >= 0)
{
SelectItem(serverAreaContent.GetChild(selectedIndex).gameObject);
return true;
}
return false;
}
private void LoadSavedServers()
{
ServerList.Refresh();
foreach (ServerList.Entry entry in ServerList.Instance.Entries)
{
CreateServerButton(entry.Name, entry.Address, entry.Port);
}
}
private IEnumerator FindLANServers()
{
void LateAddButton(IPEndPoint serverEndPoint)
{
if (!ServerList.Instance.Entries.Any(e => e.Address == serverEndPoint.Address.ToString() && e.Port == serverEndPoint.Port))
{
Log.Info($"Adding LAN server: {serverEndPoint}");
// Add ServerList entry to keep indices in sync with servers UI, to enable removal by index
ServerList.Instance.Add(new ServerList.Entry("LAN Server", serverEndPoint.Address, serverEndPoint.Port, false));
CreateServerButton("LAN Server", serverEndPoint.Address.ToString(), serverEndPoint.Port, true);
}
}
using Task<IEnumerable<IPEndPoint>> searchTask = LANBroadcastClient.SearchAsync();
while (!searchTask.IsCompleted)
{
while (LANBroadcastClient.DiscoveredServers.TryDequeue(out IPEndPoint endPoint))
{
LateAddButton(endPoint);
}
yield return null;
}
while (LANBroadcastClient.DiscoveredServers.TryDequeue(out IPEndPoint endPoint))
{
LateAddButton(endPoint);
}
ServerList.Instance.Save();
}
public GameObject CreateServerButton(string serverName, string address, int port, bool isReadOnly = false)
{
GameObject multiplayerButtonInst = Instantiate(multiplayerServerButtonRef, serverAreaContent, false);
multiplayerButtonInst.name = $"NitroxServer_{serverAreaContent.childCount - 2}";
DestroyImmediate(multiplayerButtonInst.RequireGameObject("Load")); // Needs to be deleted before MainMenuServerButton.Init() below
Destroy(multiplayerButtonInst.GetComponent<MainMenuLoadButton>());
GameObject multiplayerLoadButtonInst = Instantiate(multiplayerNewServerButtonRef, multiplayerButtonInst.transform, false);
multiplayerLoadButtonInst.name = "Load";
MainMenuServerButton serverButton = multiplayerButtonInst.AddComponent<MainMenuServerButton>();
serverButton.Init(serverName, address, port, isReadOnly);
scrollBar.SetActive(serverAreaContent.childCount >= 4);
foreach (EventTrigger eventTrigger in multiplayerButtonInst.GetComponentsInChildren<EventTrigger>(true))
{
ForwardTriggerScrollToScrollRect(eventTrigger);
}
return multiplayerButtonInst;
}
private void CreateAddServerButton()
{
GameObject multiplayerButtonInst = Instantiate(multiplayerNewServerButtonRef, serverAreaContent, false);
multiplayerButtonInst.name = "NewServer"; // "NewServer" is important, see OnConfirm()
TextMeshProUGUI txt = multiplayerButtonInst.RequireTransform("NewGameButton/Text").GetComponent<TextMeshProUGUI>();
txt.text = "Nitrox_AddServer";
txt.fontSize *= 1.5f;
txt.fontStyle = FontStyles.Bold;
Button multiplayerButtonButton = multiplayerButtonInst.RequireTransform("NewGameButton").GetComponent<Button>();
multiplayerButtonButton.onClick = new Button.ButtonClickedEvent();
multiplayerButtonButton.onClick.AddListener(OpenAddServerGroup);
ForwardTriggerScrollToScrollRect(multiplayerButtonButton.GetComponent<EventTrigger>());
}
private void ForwardTriggerScrollToScrollRect(EventTrigger eventTrigger)
{
eventTrigger.triggers.RemoveAll(trigger => trigger.eventID == EventTriggerType.Scroll);
EventTrigger.TriggerEvent callback = new();
callback.AddListener(x => scrollRect.Scroll(((PointerEventData)x).scrollDelta.y, 5f));
eventTrigger.triggers.Add(new EventTrigger.Entry
{
eventID = EventTriggerType.Scroll,
callback = callback
});
}
public void OpenAddServerGroup()
{
DeselectAllItems();
MainMenuRightSide.main.OpenGroup(MainMenuCreateServerPanel.NAME);
}
public void RefreshServerEntries()
{
if (!serverAreaContent)
{
return;
}
foreach (Transform child in serverAreaContent)
{
Destroy(child.gameObject);
}
CreateAddServerButton();
LoadSavedServers();
CoroutineHost.StartCoroutine(FindLANServers());
}
}