TH1/Unity/Assets/Scripts/TH1_UI/View/Common/UINetErrorAreaMono.cs
2026-05-29 15:21:41 +08:00

245 lines
5.9 KiB
C#

using System;
using System.Collections.Generic;
using System.Text;
using Logic.Multilingual;
using TH1_Logic.Net;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace TH1_UI.View.Common
{
public class UINetErrorAreaMono : MonoBehaviour
{
[Header("Toggle")]
public Button ToggleButton;
public GameObject LogArea;
[Header("Log List")]
public RectTransform Content;
public GameObject LogItemPrefab;
public ScrollRect ScrollRect;
[Header("Config")]
public int MaxLogCount = 100;
private readonly Queue<GameObject> _logItems = new Queue<GameObject>();
private bool _initialized;
private bool _logAreaVisible;
private void OnDestroy()
{
Shutdown();
}
public void Init()
{
if (_initialized) return;
_initialized = true;
if (ToggleButton != null)
{
ToggleButton.gameObject.SetActive(true);
ToggleButton.onClick.RemoveListener(ToggleLogArea);
ToggleButton.onClick.AddListener(ToggleLogArea);
}
ResetLogItemTemplate();
SetLogAreaVisible(false);
foreach (var payload in NetworkPlayerTipManager.Instance.GetRecentPayloads())
AddLog(payload, false);
ValidateLogContentState("Init");
RebuildLogLayout();
NetworkPlayerTipManager.Instance.OnTipRequested += OnNetworkPlayerTipRequested;
}
public void Shutdown()
{
if (!_initialized) return;
_initialized = false;
NetworkPlayerTipManager.Instance.OnTipRequested -= OnNetworkPlayerTipRequested;
if (ToggleButton != null)
ToggleButton.onClick.RemoveListener(ToggleLogArea);
ClearLogs();
}
private void OnNetworkPlayerTipRequested(NetworkPlayerTipPayload payload)
{
AddLog(payload, true);
}
private void ToggleLogArea()
{
SetLogAreaVisible(!_logAreaVisible);
}
private void SetLogAreaVisible(bool visible)
{
_logAreaVisible = visible;
if (LogArea != null)
LogArea.SetActive(visible);
}
private void AddLog(NetworkPlayerTipPayload payload, bool rebuildLayout)
{
if (Content == null || LogItemPrefab == null)
{
Debug.LogWarning("[NetErrorArea] Log prefab or content is not assigned.");
return;
}
while (_logItems.Count >= Mathf.Max(1, MaxLogCount))
{
var oldest = _logItems.Dequeue();
if (oldest != null)
Destroy(oldest);
}
var logText = BuildLogText(payload);
var go = Instantiate(LogItemPrefab, Content);
DisableDynamicLogMultilingual(go);
var text = go.GetComponentInChildren<TMP_Text>(true);
if (text != null)
text.text = logText;
else
Debug.LogWarning("[NetErrorArea] Log item prefab missing TMP_Text.");
go.SetActive(true);
if (text != null && text.text != logText)
text.text = logText;
_logItems.Enqueue(go);
if (rebuildLayout)
RebuildLogLayout();
ValidateLogContentState("AddLog");
}
private void RebuildLogLayout()
{
if (Content != null)
LayoutRebuilder.ForceRebuildLayoutImmediate(Content);
if (ScrollRect != null)
{
Canvas.ForceUpdateCanvases();
ScrollRect.verticalNormalizedPosition = 0f;
}
}
private static string BuildLogText(NetworkPlayerTipPayload payload)
{
var sb = new StringBuilder();
sb.Append('[').Append(DateTime.Now.ToString("HH:mm:ss")).Append("] ");
sb.Append(payload?.TipType.ToString() ?? "NetworkTip");
if (!string.IsNullOrEmpty(payload?.Title))
sb.Append(" | ").Append(payload.Title);
if (!string.IsNullOrEmpty(payload?.Message))
sb.Append('\n').Append(payload.Message);
return sb.ToString();
}
private void ResetLogItemTemplate()
{
if (LogItemPrefab == null)
return;
var sentinel = LogItemPrefab.GetComponent<LogItemTemplateActivationSentinel>();
if (sentinel == null)
sentinel = LogItemPrefab.AddComponent<LogItemTemplateActivationSentinel>();
sentinel.Setup(this, LogItemPrefab);
DisableDynamicLogMultilingual(LogItemPrefab);
var templateText = LogItemPrefab.GetComponentInChildren<TMP_Text>(true);
if (templateText != null)
templateText.text = string.Empty;
if (Content != null && LogItemPrefab.transform.IsChildOf(Content))
{
if (LogItemPrefab.activeSelf)
{
Debug.LogWarning("[NetErrorArea] Log item template was active before reset. Check prefab saved state or code that enables Content children before Init.");
}
LogItemPrefab.SetActive(false);
}
}
private static void DisableDynamicLogMultilingual(GameObject go)
{
if (go == null)
return;
var multilingualTexts = go.GetComponentsInChildren<MultilingualTextMono>(true);
foreach (var multilingualText in multilingualTexts)
{
multilingualText.Ban = true;
multilingualText.NoExport = true;
multilingualText.ID = 0;
multilingualText.enabled = false;
}
}
private void ValidateLogContentState(string context)
{
if (Content == null)
return;
if (LogItemPrefab != null && LogItemPrefab.activeSelf)
{
Debug.LogWarning($"[NetErrorArea] Log item template is active during {context}. The template must stay hidden.");
}
for (var i = 0; i < Content.childCount; i++)
{
var child = Content.GetChild(i).gameObject;
if (child == LogItemPrefab || !child.activeSelf || _logItems.Contains(child))
continue;
Debug.LogWarning($"[NetErrorArea] Active untracked log item under Content during {context}: {child.name}");
}
}
private class LogItemTemplateActivationSentinel : MonoBehaviour
{
private UINetErrorAreaMono _owner;
private GameObject _template;
public void Setup(UINetErrorAreaMono owner, GameObject template)
{
_owner = owner;
_template = template;
}
private void OnEnable()
{
if (_owner == null || _template == null || gameObject != _template)
return;
Debug.LogWarning("[NetErrorArea] Log item template was enabled. This object is only a hidden template and should not be shown as a record.");
}
}
private void ClearLogs()
{
while (_logItems.Count > 0)
{
var go = _logItems.Dequeue();
if (go != null)
Destroy(go);
}
RebuildLogLayout();
}
}
}