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,244 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using NitroxClient.Communication.Abstract;
using NitroxClient.GameLogic.HUD.Components;
using NitroxClient.GameLogic.PlayerLogic.PlayerModel.Abstract;
using NitroxModel.Core;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using static NitroxClient.Unity.Helper.AssetBundleLoader;
namespace NitroxClient.GameLogic.HUD.PdaTabs;
/// <summary>
/// The component containing a new PDA tab based on ping manager tab
/// </summary>
public class uGUI_PlayerListTab : uGUI_PingTab
{
private NitroxPDATabManager nitroxPDATabManager;
private PlayerManager playerManager;
private LocalPlayer localPlayer;
private IPacketSender packetSender;
private readonly Dictionary<string, Sprite> assets = new();
public bool FinishedLoadingAssets { get; private set; }
private new readonly Dictionary<string, uGUI_PlayerPingEntry> entries = new();
private PrefabPool<uGUI_PlayerPingEntry> pool;
private new readonly Dictionary<string, uGUI_PlayerPingEntry> tempSort = new();
public override void Awake()
{
// Copied from uGUI_PingTab.Awake but we don't want it to be executed because it creates a PrefabPool
selectableVisibilityToggle = new SelectableWrapper(visibilityToggle, delegate (GameInput.Button button)
{
if (button == GameInput.Button.UISubmit)
{
visibilityToggle.isOn = !visibilityToggle.isOn;
return true;
}
return false;
});
nitroxPDATabManager = NitroxServiceLocator.LocateService<NitroxPDATabManager>();
playerManager = NitroxServiceLocator.LocateService<PlayerManager>();
localPlayer = NitroxServiceLocator.LocateService<LocalPlayer>();
packetSender = NitroxServiceLocator.LocateService<IPacketSender>();
// Need to reassign manually these variables and get rid of the objects we don't need
content = gameObject.FindChild("Content").GetComponent<CanvasGroup>();
pingManagerLabel = gameObject.GetComponentInChildren<TextMeshProUGUI>();
scrollRect = gameObject.GetComponentInChildren<ScrollRect>();
pingCanvas = (RectTransform)content.transform.Find("ScrollView/Viewport/ScrollCanvas");
pool = new PrefabPool<uGUI_PlayerPingEntry>(prefabEntry, pingCanvas, 8, 4, delegate (uGUI_PlayerPingEntry entry)
{
entry.Uninitialize();
}, delegate (uGUI_PlayerPingEntry entry)
{
entry.Uninitialize();
});
}
public IEnumerator Start()
{
Transform buttonAll = content.transform.Find("ButtonAll");
DestroyImmediate(buttonAll.gameObject);
yield return LoadAllAssets(NitroxAssetBundle.PLAYER_LIST_TAB);
foreach (Object asset in NitroxAssetBundle.PLAYER_LIST_TAB.LoadedAssets)
{
if (asset is Sprite sprite)
{
if (asset.name.Equals("player_list_tab@3x"))
{
nitroxPDATabManager.AddTabSprite(asset.name, new Atlas.Sprite(sprite));
}
assets.Add(asset.name, sprite);
}
}
FinishedLoadingAssets = true;
_isDirty = true;
}
public Sprite GetSprite(string assetName)
{
if (assets.TryGetValue(assetName, out Sprite sprite))
{
return sprite;
}
return Sprite.Create(new Texture2D(100, 100), new Rect(0, 0, 100, 100), new Vector2(50, 50), 100);
}
public new void OnEnable()
{
// Enter events for player join and disconnect
playerManager.OnCreate += OnAdd;
playerManager.OnRemove += OnRemove;
}
public new void OnDestroy()
{
playerManager.OnCreate -= OnAdd;
playerManager.OnRemove -= OnRemove;
}
public override void OnLanguageChanged()
{
pingManagerLabel.text = Language.main.Get("Nitrox_PlayerListTabName");
entries.Values.ForEach(entry => entry.OnLanguageChanged());
}
public override void OnLateUpdate(bool _)
{
UpdateEntries();
}
public new void UpdateEntries()
{
if (!_isDirty)
{
return;
}
_isDirty = false;
Dictionary<string, INitroxPlayer> players = playerManager.GetAll().ToDictionary<RemotePlayer, string, INitroxPlayer>(player => player.PlayerId.ToString(), player => player);
players.Add(localPlayer.PlayerId.ToString(), localPlayer);
foreach (KeyValuePair<string, INitroxPlayer> entry in players)
{
if (!entries.ContainsKey(entry.Key))
{
// Sets up a new entry for the player
AddNewEntry(entry.Key, entry.Value);
}
}
// Sort the items by alphabetical order (based on SN's code)
tempSort.Clear();
foreach (KeyValuePair<string, uGUI_PlayerPingEntry> entry in entries)
{
if (!entry.Value.IsLocalPlayer)
{
tempSort.Add(entry.Value.PlayerName, entry.Value);
}
}
List<string> sorted = new(tempSort.Keys);
sorted.Sort();
entries[localPlayer.PlayerId.ToString()].rectTransform.SetSiblingIndex(0);
for (int j = 0; j < sorted.Count; j++)
{
string id = tempSort[sorted[j]].id;
entries[id].rectTransform.SetSiblingIndex(j + 1);
}
}
public uGUI_PlayerPingEntry GetEntry()
{
uGUI_PlayerPingEntry uGUI_PlayerEntry;
if (pool.pool.Count == 0)
{
for (int i = 0; i < 4; i++)
{
uGUI_PlayerEntry = Instantiate(prefabEntry).GetComponent<uGUI_PlayerPingEntry>();
uGUI_PlayerEntry.rectTransform.SetParent(pingCanvas, false);
uGUI_PlayerEntry.Uninitialize();
pool.pool.Add(uGUI_PlayerEntry);
}
}
int index = pool.pool.Count - 1;
uGUI_PlayerEntry = pool.pool[index];
pool.pool.RemoveAt(index);
return uGUI_PlayerEntry;
}
public void MakePrefab(GameObject basePrefab)
{
// We need to instantiate the prefab as we cannot directly make modifications in it
GameObject newPrefab = Instantiate(basePrefab);
newPrefab.name = "PlayerEntry";
// We never want this to appear
DestroyImmediate(newPrefab.FindChild("ColorToggle"));
// Need to modify the pingTab's script from uGUI_PingEntry to uGUI_PlayerEntry
uGUI_PingEntry pingEntry = newPrefab.GetComponent<uGUI_PingEntry>();
uGUI_PlayerPingEntry playerEntry = newPrefab.AddComponent<uGUI_PlayerPingEntry>();
playerEntry.visibility = pingEntry.visibility;
playerEntry.visibilityIcon = pingEntry.visibilityIcon;
playerEntry.icon = pingEntry.icon;
playerEntry.label = pingEntry.label;
playerEntry._rectTransform = pingEntry._rectTransform;
playerEntry.id = pingEntry.id;
playerEntry.spriteVisible = pingEntry.spriteVisible;
playerEntry.spriteHidden = pingEntry.spriteHidden;
DestroyImmediate(pingEntry);
// Make buttons for mute, kick, tp
Transform container = newPrefab.transform;
playerEntry.ShowObject = newPrefab.FindChild("ButtonVisibility");
playerEntry.ShowObject.AddComponent<ButtonTooltip>();
playerEntry.MuteObject = Instantiate(playerEntry.ShowObject, container);
playerEntry.KickObject = Instantiate(playerEntry.ShowObject, container);
playerEntry.TeleportToObject = Instantiate(playerEntry.ShowObject, container);
playerEntry.TeleportToMeObject = Instantiate(playerEntry.ShowObject, container);
playerEntry.MuteObject.name = "MuteObject";
playerEntry.KickObject.name = "KickObject";
playerEntry.TeleportToObject.name = "TeleportToObject";
playerEntry.TeleportToMeObject.name = "TeleportToMeObject";
prefabEntry = newPrefab;
}
private void AddNewEntry(string playerId, INitroxPlayer player)
{
uGUI_PlayerPingEntry entry = GetEntry();
entry.Initialize(playerId, player.PlayerName, this);
entry.UpdateEntryForNewPlayer(player, localPlayer, packetSender);
entries.Add(playerId, entry);
}
private void OnAdd(ushort playerId, RemotePlayer remotePlayer)
{
_isDirty = true;
}
private void OnRemove(ushort playerId, RemotePlayer remotePlayers)
{
string playerIdString = playerId.ToString();
if (!entries.ContainsKey(playerIdString))
{
return;
}
uGUI_PlayerPingEntry entry = entries[playerIdString];
entries.Remove(playerIdString);
pool.Release(entry);
_isDirty = true;
}
}

View File

@@ -0,0 +1,244 @@
using System.Collections;
using System.Collections.Generic;
using NitroxClient.Communication.Abstract;
using NitroxClient.Communication.Packets.Processors;
using NitroxClient.GameLogic.HUD.Components;
using NitroxClient.GameLogic.PlayerLogic.PlayerModel.Abstract;
using NitroxClient.MonoBehaviours.Gui.Modals;
using NitroxModel_Subnautica.DataStructures;
using NitroxModel.Core;
using NitroxModel.DataStructures.GameLogic;
using NitroxModel.Packets;
using UnityEngine;
using UnityEngine.UI;
using UWE;
namespace NitroxClient.GameLogic.HUD.PdaTabs;
public class uGUI_PlayerPingEntry : uGUI_PingEntry
{
private uGUI_PlayerListTab parent;
private INitroxPlayer player;
public string PlayerName => player?.PlayerName ?? string.Empty;
public bool IsLocalPlayer => player is LocalPlayer;
private bool showPing;
private bool muted
{
get
{
if (player is RemotePlayer remotePlayer && remotePlayer.PlayerContext != null)
{
return remotePlayer.PlayerContext.IsMuted;
}
// By default we don't care about the local state
return false;
}
}
public GameObject ShowObject;
public GameObject MuteObject;
public GameObject KickObject;
public GameObject TeleportToObject;
public GameObject TeleportToMeObject;
public Sprite MutedSprite;
public Sprite UnmutedSprite;
public Sprite KickSprite;
public Sprite TeleportToSprite;
public Sprite TeleportToMeSprite;
public new void Awake()
{
NitroxServiceLocator.LocateService<MutePlayerProcessor>().OnPlayerMuted += (playerId, _) =>
{
if (player is RemotePlayer remotePlayer && remotePlayer.PlayerId == playerId)
{
RefreshMuteButton();
}
};
NitroxServiceLocator.LocateService<PermsChangedProcessor>().OnPermissionsChanged += (perms) => RefreshButtonsVisibility();
}
public IEnumerator Start()
{
// We must one frame so that the UI elements are initialized properly
yield return null;
// This action must happen after the yield so that they're correctly placed
UpdateButtonsPosition();
// We trigger it at least once so that the localizations are updated with the PlayerName
OnLanguageChanged();
}
public void Initialize(string id, string name, uGUI_PlayerListTab parent)
{
this.id = id;
this.parent = parent;
gameObject.SetActive(true);
visibilityIcon.sprite = spriteVisible;
icon.SetForegroundSprite(SpriteManager.Get(SpriteManager.Group.Tab, "TabInventory"));
showPing = true;
UpdateLabel(name);
OnLanguageChanged();
CoroutineHost.StartCoroutine(AssignSprites());
}
public void OnLanguageChanged()
{
GetTooltip(ShowObject).TooltipText = GetLocalizedText(showPing ? "Nitrox_HidePing" : "Nitrox_ShowPing");
GetTooltip(MuteObject).TooltipText = GetLocalizedText(muted ? "Nitrox_Unmute" : "Nitrox_Mute");
GetTooltip(KickObject).TooltipText = GetLocalizedText("Nitrox_Kick");
GetTooltip(TeleportToObject).TooltipText = GetLocalizedText("Nitrox_TeleportTo");
GetTooltip(TeleportToMeObject).TooltipText = GetLocalizedText("Nitrox_TeleportToMe");
}
public new void Uninitialize()
{
base.Uninitialize();
player = null;
}
public void UpdateLabel(string text)
{
label.text = text;
}
public void UpdateEntryForNewPlayer(INitroxPlayer newPlayer, LocalPlayer localPlayer, IPacketSender packetSender)
{
player = newPlayer;
UpdateLabel(player.PlayerName);
Color playerColor = player.PlayerSettings.PlayerColor.ToUnity();
icon.SetColors(playerColor, playerColor, playerColor);
RefreshMuteButton();
// We need to update each button's listener whether or not they have enough perms because they may become OP during playtime
ClearButtonListeners();
GetToggle(ShowObject).onValueChanged.AddListener(delegate (bool toggled)
{
if (player is RemotePlayer remotePlayer)
{
PingInstance pingInstance = remotePlayer.PlayerModel.GetComponentInChildren<PingInstance>();
pingInstance.SetVisible(toggled);
GetTooltip(ShowObject).TooltipText = GetLocalizedText(toggled ? "Nitrox_HidePing" : "Nitrox_ShowPing");
visibilityIcon.sprite = toggled ? spriteVisible : spriteHidden;
}
});
// Each of those clicks involves a confirmation modal
GetToggle(MuteObject).onValueChanged.AddListener(delegate (bool toggled)
{
Modal.Get<ConfirmModal>()?.Show(GetLocalizedText(muted ? "Nitrox_Unmute" : "Nitrox_Mute", true), () =>
{
GetToggle(MuteObject).SetIsOnWithoutNotify(!toggled);
if (player is RemotePlayer remotePlayer)
{
packetSender.Send(new ServerCommand($"{(toggled ? "" : "un")}mute {player.PlayerName}"));
}
});
});
GetToggle(KickObject).onValueChanged.AddListener(delegate (bool toggled)
{
Modal.Get<ConfirmModal>()?.Show(GetLocalizedText("Nitrox_Kick", true), () =>
{
packetSender.Send(new ServerCommand($"kick {player.PlayerName}"));
});
});
GetToggle(TeleportToObject).onValueChanged.AddListener(delegate (bool toggled)
{
Modal.Get<ConfirmModal>()?.Show(GetLocalizedText("Nitrox_TeleportTo", true), () =>
{
packetSender.Send(new ServerCommand($"warp {player.PlayerName}"));
});
});
GetToggle(TeleportToMeObject).onValueChanged.AddListener(delegate (bool toggled)
{
Modal.Get<ConfirmModal>()?.Show(GetLocalizedText("Nitrox_TeleportToMe", true), () =>
{
packetSender.Send(new ServerCommand($"warp {player.PlayerName} {localPlayer.PlayerName}"));
});
});
RefreshButtonsVisibility();
}
private string GetLocalizedText(string key, bool isQuestion = false)
{
return Language.main.Get(isQuestion ? $"{key}Question" : key).Replace("{PLAYER}", PlayerName);
}
public void UpdateButtonsPosition()
{
float OFFSET = 0f;
List<GameObject> buttonsToAlign = new() { MuteObject, KickObject, TeleportToObject, TeleportToMeObject };
foreach (GameObject buttonObject in buttonsToAlign)
{
RectTransform buttonRect = buttonObject.GetComponent<RectTransform>();
buttonRect.anchoredPosition = new Vector2(rectTransform.anchoredPosition.x, 0f);
buttonRect.localPosition = new Vector2(OFFSET, 0);
OFFSET += 80f;
}
}
private void ClearButtonListeners()
{
GetToggle(MuteObject).onValueChanged = new Toggle.ToggleEvent();
GetToggle(KickObject).onValueChanged = new Toggle.ToggleEvent();
GetToggle(TeleportToObject).onValueChanged = new Toggle.ToggleEvent();
GetToggle(TeleportToMeObject).onValueChanged = new Toggle.ToggleEvent();
}
private IEnumerator AssignSprites()
{
yield return new WaitUntil(() => parent.FinishedLoadingAssets);
// NB: Those textures MUST be exported with a Texture Type of "Sprite (2D and UI)", else they will look blurry not matter what
// NB 2: Those textures for the buttons are scaled 68x61 but the image inside but not hit the borders to have a better render
MutedSprite = parent.GetSprite("muted@3x");
UnmutedSprite = parent.GetSprite("unmuted@3x");
KickSprite = parent.GetSprite("kick@3x");
TeleportToSprite = parent.GetSprite("teleport_to@3x");
TeleportToMeSprite = parent.GetSprite("teleport_to_me@3x");
MuteObject.FindChild("Eye").GetComponent<Image>().sprite = muted ? MutedSprite : UnmutedSprite;
KickObject.FindChild("Eye").GetComponent<Image>().sprite = KickSprite;
TeleportToObject.FindChild("Eye").GetComponent<Image>().sprite = TeleportToSprite;
TeleportToMeObject.FindChild("Eye").GetComponent<Image>().sprite = TeleportToMeSprite;
}
private void RefreshMuteButton()
{
GetToggle(MuteObject).SetIsOnWithoutNotify(muted);
GetTooltip(MuteObject).TooltipText = GetLocalizedText(muted ? "Nitrox_Unmute" : "Nitrox_Mute");
MuteObject.FindChild("Eye").GetComponent<Image>().sprite = muted ? MutedSprite : UnmutedSprite;
}
private void RefreshButtonsVisibility()
{
LocalPlayer localPlayer = NitroxServiceLocator.LocateService<LocalPlayer>();
bool isNotLocalPlayer = !IsLocalPlayer;
// We don't want any control buttons to appear for the local player
ShowObject.SetActive(isNotLocalPlayer);
// The perms here should be the same as the perm each command asks for
MuteObject.SetActive(isNotLocalPlayer && localPlayer.Permissions >= Perms.MODERATOR);
KickObject.SetActive(isNotLocalPlayer && localPlayer.Permissions >= Perms.MODERATOR);
TeleportToObject.SetActive(isNotLocalPlayer && localPlayer.Permissions >= Perms.MODERATOR);
TeleportToMeObject.SetActive(isNotLocalPlayer && localPlayer.Permissions >= Perms.MODERATOR);
}
private Toggle GetToggle(GameObject gameObject)
{
return gameObject.GetComponent<Toggle>();
}
private ButtonTooltip GetTooltip(GameObject gameObject)
{
return gameObject.GetComponent<ButtonTooltip>();
}
}