using System.Collections.Generic; using TMPro; using UnityEngine; using UnityEngine.UI; /// /// uGUI binding for . Drives the server-browser canvas - Host / Refresh / /// pick a row / Join / Disconnect plus a status line - and mirrors the manager's state through its /// / events. /// Replaces the old IMGUI browser. Lives on the browser canvas; the session logic stays on the /// NetworkManager's . /// /// Selection model: Refresh fills the list with clones of (each a clickable row /// showing name + player count). Clicking a row selects it; Join connects to the selected session. While /// connected, only Disconnect is interactable. /// public class OnlineSessionBrowserUI : MonoBehaviour { [SerializeField] private OnlineSessionManager manager; [Header("Buttons")] [SerializeField] private Button hostButton; [SerializeField] private Button refreshButton; [SerializeField] private Button joinButton; [SerializeField] private Button disconnectButton; [Header("List")] [Tooltip("Content RectTransform of the session ScrollRect (rows are added here).")] [SerializeField] private RectTransform listContent; [Tooltip("A row prototype kept disabled; cloned once per session. Children named 'Name' and 'Players' carry the text.")] [SerializeField] private GameObject entryTemplate; [Header("Status")] [SerializeField] private TMP_Text statusLabel; [Tooltip("Background tint of the selected row.")] [SerializeField] private Color selectedTint = new Color(0.26f, 0.52f, 0.96f, 1f); [Tooltip("Background tint of an unselected row.")] [SerializeField] private Color normalTint = new Color(1f, 1f, 1f, 0.05f); private struct Row { public string id; public Image image; public GameObject go; } private readonly List _rows = new List(); private string _selectedId; private void Awake() { // The template is the prototype, never shown itself - layout ignores it while disabled. if (entryTemplate) entryTemplate.SetActive(false); } private void OnEnable() { if (hostButton) hostButton.onClick.AddListener(OnHost); if (refreshButton) refreshButton.onClick.AddListener(OnRefresh); if (joinButton) joinButton.onClick.AddListener(OnJoin); if (disconnectButton) disconnectButton.onClick.AddListener(OnDisconnect); if (manager) { manager.Changed += OnManagerChanged; manager.SessionsChanged += RebuildList; } RebuildList(); OnManagerChanged(); } private void OnDisable() { if (hostButton) hostButton.onClick.RemoveListener(OnHost); if (refreshButton) refreshButton.onClick.RemoveListener(OnRefresh); if (joinButton) joinButton.onClick.RemoveListener(OnJoin); if (disconnectButton) disconnectButton.onClick.RemoveListener(OnDisconnect); if (manager) { manager.Changed -= OnManagerChanged; manager.SessionsChanged -= RebuildList; } } private void OnHost() { if (manager) manager.Host(); } private void OnRefresh() { if (manager) manager.Refresh(); } private void OnJoin() { if (manager && !string.IsNullOrEmpty(_selectedId)) manager.Join(_selectedId); } private void OnDisconnect() { if (manager) manager.Disconnect(); } private void OnManagerChanged() { if (!manager) return; if (statusLabel) statusLabel.text = manager.Status; UpdateButtons(); } private void UpdateButtons() { if (!manager) return; bool busy = manager.IsBusy; bool connected = manager.IsConnected; bool hasSelection = !string.IsNullOrEmpty(_selectedId); if (hostButton) hostButton.interactable = !busy && !connected; if (refreshButton) refreshButton.interactable = !busy && !connected; if (joinButton) joinButton.interactable = !busy && !connected && hasSelection; if (disconnectButton) disconnectButton.interactable = !busy && connected; } private void RebuildList() { for (int i = 0; i < _rows.Count; i++) if (_rows[i].go) Destroy(_rows[i].go); _rows.Clear(); if (manager && entryTemplate && listContent) { var sessions = manager.Sessions; bool selectionStillPresent = false; for (int i = 0; i < sessions.Count; i++) { var s = sessions[i]; var go = Instantiate(entryTemplate, listContent); go.SetActive(true); var nameText = FindText(go.transform, "Name"); if (nameText) nameText.text = s.Name; var playersText = FindText(go.transform, "Players"); if (playersText) { int taken = Mathf.Max(0, s.MaxPlayers - s.AvailableSlots); playersText.text = $"{taken}/{s.MaxPlayers}"; } string id = s.Id; // capture per-row for the click closure var button = go.GetComponent