Initial commit: Client Doc docs Server Tools

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
ud18010
2026-07-10 10:24:29 +08:00
co-authored by Cursor
commit 7e35d8da31
3374 changed files with 680813 additions and 0 deletions
@@ -0,0 +1,269 @@
using System;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace XGame
{
public class AuthRegisterLoginController : MonoBehaviour
{
private TMP_InputField loginUsernameField;
private TMP_InputField loginPasswordField;
private TMP_InputField registerUsernameField;
private TMP_InputField registerPasswordField;
private TMP_InputField registerConfirmField;
private Toggle toggleRemember;
private Button btnLogin;
private Button btnRegister;
private Button btnGuest;
private Button btnTabLogin;
private Button btnTabRegister;
private GameObject loginForm;
private GameObject registerForm;
private GameObject rememberRow;
private TMP_Text tipsText;
private TMP_Text titleText;
private TMP_Text rememberText;
private string luaModule = "client/login";
private bool isRegisterMode;
private bool isLoading;
public Action<string, string, bool, bool> OnLogin;
public Action<string, string, string> OnRegister;
public Action<string, string, bool, bool> OnGuestLogin;
private void Awake()
{
CacheControls();
WireEvents();
SwitchLogin();
}
public void Bind(string module)
{
if (!string.IsNullOrEmpty(module))
{
luaModule = module;
}
}
public void SetInitialState(string account, bool remember, bool autoLogin, bool agreement)
{
SetInitialState(account, string.Empty, remember, autoLogin, agreement);
}
public void SetInitialState(string account, string password, bool rememberPassword, bool autoLogin, bool agreement)
{
CacheControls();
SetInputText(loginUsernameField, account);
SetInputText(loginPasswordField, rememberPassword ? password : string.Empty);
SetInputText(registerUsernameField, account);
if (toggleRemember != null)
{
toggleRemember.isOn = rememberPassword;
}
if (rememberText != null)
{
rememberText.text = "记住密码";
}
}
public void SetServerInfo(string serverName, string status)
{
}
public void SetVersion(string version)
{
}
public void SetLoading(bool loading)
{
isLoading = loading;
SetButtonEnabled(btnLogin, !loading);
SetButtonEnabled(btnRegister, !loading);
SetButtonEnabled(btnGuest, !loading);
SetButtonEnabled(btnTabLogin, !loading);
SetButtonEnabled(btnTabRegister, !loading);
if (loading)
{
ShowError(isRegisterMode ? "Registering..." : "Logging in...");
}
}
public void ShowError(string message)
{
CacheControls();
if (tipsText != null)
{
tipsText.text = message ?? string.Empty;
}
}
public void Close()
{
Destroy(gameObject);
}
public void ToggleBaseAtlas()
{
}
public void SwitchLogin()
{
isRegisterMode = false;
SetActive(loginForm, true);
SetActive(registerForm, false);
SetActive(rememberRow, true);
SetActive(btnLogin, true);
SetActive(btnGuest, true);
SetActive(btnRegister, false);
if (titleText != null)
{
titleText.text = "Login";
}
ShowError(string.Empty);
}
public void SwitchRegister()
{
isRegisterMode = true;
SetActive(loginForm, false);
SetActive(registerForm, true);
SetActive(rememberRow, false);
SetActive(btnLogin, false);
SetActive(btnGuest, false);
SetActive(btnRegister, true);
if (titleText != null)
{
titleText.text = "Register";
}
ShowError(string.Empty);
}
private void OnLoginClicked()
{
if (isLoading)
{
return;
}
OnLogin?.Invoke(
GetInputText(loginUsernameField),
GetInputText(loginPasswordField),
toggleRemember != null && toggleRemember.isOn,
false);
}
private void OnRegisterClicked()
{
if (isLoading)
{
return;
}
OnRegister?.Invoke(
GetInputText(registerUsernameField),
GetInputText(registerPasswordField),
GetInputText(registerConfirmField));
}
private void OnGuestClicked()
{
if (isLoading)
{
return;
}
OnGuestLogin?.Invoke(
GetInputText(loginUsernameField),
GetInputText(loginPasswordField),
toggleRemember != null && toggleRemember.isOn,
false);
}
private void CacheControls()
{
loginUsernameField = loginUsernameField ? loginUsernameField : Find<TMP_InputField>("LoginUsernameField");
loginPasswordField = loginPasswordField ? loginPasswordField : Find<TMP_InputField>("LoginPasswordField");
registerUsernameField = registerUsernameField ? registerUsernameField : Find<TMP_InputField>("RegisterUsernameField");
registerPasswordField = registerPasswordField ? registerPasswordField : Find<TMP_InputField>("RegisterPasswordField");
registerConfirmField = registerConfirmField ? registerConfirmField : Find<TMP_InputField>("RegisterConfirmField");
toggleRemember = toggleRemember ? toggleRemember : Find<Toggle>("ToggleRemember");
btnLogin = btnLogin ? btnLogin : Find<Button>("BtnLogin");
btnRegister = btnRegister ? btnRegister : Find<Button>("BtnRegister");
btnGuest = btnGuest ? btnGuest : Find<Button>("BtnGuest");
btnTabLogin = btnTabLogin ? btnTabLogin : Find<Button>("BtnTabLogin");
btnTabRegister = btnTabRegister ? btnTabRegister : Find<Button>("BtnTabRegister");
loginForm = loginForm ? loginForm : FindGameObject("LoginForm");
registerForm = registerForm ? registerForm : FindGameObject("RegisterForm");
rememberRow = rememberRow ? rememberRow : FindGameObject("RememberRow");
tipsText = tipsText ? tipsText : Find<TMP_Text>("TextTips");
titleText = titleText ? titleText : Find<TMP_Text>("TextPanelTitle");
rememberText = rememberText ? rememberText : Find<TMP_Text>("TextRemember");
}
private void WireEvents()
{
AddClick(btnLogin, OnLoginClicked);
AddClick(btnRegister, OnRegisterClicked);
AddClick(btnGuest, OnGuestClicked);
AddClick(btnTabLogin, SwitchLogin);
AddClick(btnTabRegister, SwitchRegister);
}
private T Find<T>(string name) where T : Component
{
Transform target = transform.FindTransform(name);
return target == null ? null : target.GetComponent<T>();
}
private GameObject FindGameObject(string name)
{
Transform target = transform.FindTransform(name);
return target == null ? null : target.gameObject;
}
private void AddClick(Button button, UnityEngine.Events.UnityAction action)
{
if (button == null)
{
return;
}
button.onClick.RemoveListener(action);
button.onClick.AddListener(action);
}
private static string GetInputText(TMP_InputField input)
{
return input == null ? string.Empty : input.text;
}
private static void SetInputText(TMP_InputField input, string text)
{
if (input != null)
{
input.text = text ?? string.Empty;
}
}
private static void SetButtonEnabled(Button button, bool enabled)
{
if (button != null)
{
button.interactable = enabled;
}
}
private static void SetActive(Component component, bool active)
{
if (component != null)
{
component.gameObject.SetActive(active);
}
}
private static void SetActive(GameObject gameObject, bool active)
{
if (gameObject != null)
{
gameObject.SetActive(active);
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b356a758f3ce46d5bfdb3b544f8f6a07
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,419 @@
using System;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace XGame
{
// 匹配大厅运行时控制器。沿用 AuthRegisterLoginController 模式:
// 按节点名缓存控件、克隆模板行、回调 Lua。只传基本类型跨 XLua。
public class GameLobbyController : MonoBehaviour
{
private Transform listGames;
private GameObject itemTemplate;
private TMP_Text statusText;
private RectTransform panelLobby;
private RectTransform collapseButtonRect;
private Button collapseButton;
private TMP_Text collapseText;
private GameObject titleObject;
private GameObject scrollObject;
private Vector2 expandedPanelSize;
private Vector2 expandedButtonPosition;
private bool hasCollapseMetrics;
private bool leftLayoutApplied;
private bool rootMatchButtonBound;
private bool collapsed;
private string currentStatus = string.Empty;
private string luaModule = "client/baseworld";
public Action<string> OnMatch;
private const float LobbyPanelWidth = 520f;
private const float LobbyPanelHeight = 296f;
private const float LobbyPanelLeft = 24f;
private const float LobbyPanelTop = -24f;
private const float CollapsedPanelWidth = 56f;
private void Awake()
{
CacheControls();
}
public void Bind(string module)
{
if (!string.IsNullOrEmpty(module))
{
luaModule = module;
}
}
// 清空旧克隆行
public void BeginGames()
{
CacheControls();
if (listGames == null)
{
return;
}
for (int i = listGames.childCount - 1; i >= 0; i--)
{
Transform child = listGames.GetChild(i);
if (itemTemplate != null && child == itemTemplate.transform)
{
continue;
}
Destroy(child.gameObject);
}
}
// 逐行追加。gameId 按行捕获用于点击回调
public void AddGame(string gameId, string name, string players)
{
CacheControls();
if (itemTemplate == null || listGames == null)
{
return;
}
GameObject row = Instantiate(itemTemplate, listGames);
row.name = "Item_Game_" + gameId;
row.SetActive(true);
SetChildText(row.transform, "Txt_GameName", name);
SetChildText(row.transform, "Txt_Players", players);
Transform btnTf = row.transform.FindTransform("Btn_Match");
if (btnTf != null)
{
Button btn = btnTf.GetComponent<Button>();
if (btn != null)
{
string captured = gameId;
btn.onClick.RemoveAllListeners();
btn.onClick.AddListener(() => OnMatch?.Invoke(captured));
}
}
}
public void EndGames()
{
}
public void SetStatus(string message)
{
CacheControls();
currentStatus = message ?? string.Empty;
if (statusText == null)
{
return;
}
statusText.text = currentStatus;
statusText.gameObject.SetActive(!collapsed && !string.IsNullOrEmpty(currentStatus));
}
public void Close()
{
Destroy(gameObject);
}
private void CacheControls()
{
if (panelLobby == null)
{
Transform t = transform.FindTransform("Panel_Lobby");
panelLobby = t == null ? null : t.GetComponent<RectTransform>();
}
EnsureCollapseButton();
if (collapseButton == null)
{
Transform t = transform.FindTransform("Btn_Collapse");
if (t != null)
{
collapseButtonRect = t.GetComponent<RectTransform>();
collapseButton = t.GetComponent<Button>();
if (collapseButton != null)
{
collapseButton.onClick.RemoveAllListeners();
collapseButton.onClick.AddListener(ToggleCollapsed);
}
}
}
if (collapseText == null)
{
Transform t = transform.FindTransform("Txt_Collapse");
collapseText = t == null ? null : t.GetComponent<TMP_Text>();
}
if (titleObject == null)
{
Transform t = transform.FindTransform("Txt_Title");
titleObject = t == null ? null : t.gameObject;
}
if (scrollObject == null)
{
Transform t = transform.FindTransform("Scroll_Games");
scrollObject = t == null ? null : t.gameObject;
}
if (listGames == null)
{
listGames = transform.FindTransform("List_Games");
}
if (itemTemplate == null)
{
Transform t = transform.FindTransform("Item_Game");
itemTemplate = t == null ? null : t.gameObject;
}
if (statusText == null)
{
Transform t = transform.FindTransform("Txt_Status");
statusText = t == null ? null : t.GetComponent<TMP_Text>();
}
BindRootMatchButton();
ApplyLeftDockLayout();
if (!hasCollapseMetrics && panelLobby != null && collapseButtonRect != null)
{
expandedPanelSize = panelLobby.sizeDelta;
expandedButtonPosition = collapseButtonRect.anchoredPosition;
hasCollapseMetrics = true;
ApplyCollapsedState();
}
}
private void BindRootMatchButton()
{
if (rootMatchButtonBound)
{
return;
}
Transform matchButton = transform.FindTransform("Btn_Match");
if (matchButton == null || itemTemplate != null && matchButton.IsChildOf(itemTemplate.transform))
{
return;
}
Button btn = matchButton.GetComponent<Button>();
if (btn == null)
{
return;
}
rootMatchButtonBound = true;
btn.onClick.RemoveAllListeners();
btn.onClick.AddListener(() => OnMatch?.Invoke(string.Empty));
}
private void EnsureCollapseButton()
{
if (transform.FindTransform("Btn_Collapse") != null)
{
return;
}
Transform parent = panelLobby != null && panelLobby.parent != null ? panelLobby.parent : transform;
GameObject buttonGo = new GameObject("Btn_Collapse", typeof(RectTransform), typeof(Image), typeof(Button));
buttonGo.transform.SetParent(parent, false);
RectTransform rt = buttonGo.GetComponent<RectTransform>();
rt.anchorMin = new Vector2(0f, 1f);
rt.anchorMax = new Vector2(0f, 1f);
rt.pivot = new Vector2(0f, 1f);
rt.anchoredPosition = new Vector2(LobbyPanelLeft + LobbyPanelWidth, LobbyPanelTop);
rt.sizeDelta = new Vector2(48f, 72f);
Image img = buttonGo.GetComponent<Image>();
Image matchImage = null;
Transform matchButton = transform.FindTransform("Btn_Match");
if (matchButton != null)
{
matchImage = matchButton.GetComponent<Image>();
}
img.sprite = matchImage != null ? matchImage.sprite : null;
img.type = matchImage != null ? matchImage.type : Image.Type.Sliced;
img.color = new Color(0.239f, 0.482f, 1f, 1f);
img.raycastTarget = true;
GameObject textGo = new GameObject("Txt_Collapse", typeof(RectTransform), typeof(TextMeshProUGUI));
textGo.transform.SetParent(buttonGo.transform, false);
RectTransform textRt = textGo.GetComponent<RectTransform>();
textRt.anchorMin = Vector2.zero;
textRt.anchorMax = Vector2.one;
textRt.pivot = new Vector2(0.5f, 0.5f);
textRt.offsetMin = Vector2.zero;
textRt.offsetMax = Vector2.zero;
TextMeshProUGUI text = textGo.GetComponent<TextMeshProUGUI>();
text.text = "<";
text.fontSize = 34f;
text.fontStyle = FontStyles.Bold;
text.alignment = TextAlignmentOptions.Center;
text.color = Color.white;
text.raycastTarget = false;
}
private void ApplyLeftDockLayout()
{
if (leftLayoutApplied)
{
return;
}
leftLayoutApplied = true;
if (panelLobby != null)
{
panelLobby.anchorMin = new Vector2(0f, 1f);
panelLobby.anchorMax = new Vector2(0f, 1f);
panelLobby.pivot = new Vector2(0f, 1f);
panelLobby.anchoredPosition = new Vector2(LobbyPanelLeft, LobbyPanelTop);
panelLobby.sizeDelta = new Vector2(LobbyPanelWidth, LobbyPanelHeight);
}
if (collapseButtonRect != null)
{
collapseButtonRect.anchorMin = new Vector2(0f, 1f);
collapseButtonRect.anchorMax = new Vector2(0f, 1f);
collapseButtonRect.pivot = new Vector2(0f, 1f);
collapseButtonRect.anchoredPosition = new Vector2(LobbyPanelLeft + LobbyPanelWidth, LobbyPanelTop);
collapseButtonRect.sizeDelta = new Vector2(48f, 72f);
}
SetRect("Txt_Title", new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -14f), new Vector2(448f, 36f));
SetOffsets("Scroll_Games", new Vector2(16f, 52f), new Vector2(-16f, -58f));
SetRect("Txt_Status", new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 16f), new Vector2(448f, 28f));
SetRect("Item_Game", new Vector2(0f, 1f), new Vector2(1f, 1f), new Vector2(0.5f, 1f), Vector2.zero, new Vector2(0f, 76f));
SetLayoutElement("Item_Game", 76f);
SetRect("Txt_GameName", new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(18f, 12f), new Vector2(280f, 28f));
SetRect("Txt_Players", new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(18f, -14f), new Vector2(280f, 22f));
SetRect("Btn_Match", new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), new Vector2(-10f, 0f), new Vector2(104f, 46f));
Transform titleText = transform.FindTransform("Txt_Title");
if (titleText != null)
{
TMP_Text text = titleText.GetComponent<TMP_Text>();
if (text != null)
{
text.fontSize = 28f;
}
}
Transform gameNameText = transform.FindTransform("Txt_GameName");
if (gameNameText != null)
{
TMP_Text text = gameNameText.GetComponent<TMP_Text>();
if (text != null)
{
text.fontSize = 22f;
}
}
Transform matchText = transform.FindTransform("Txt_Match");
if (matchText != null)
{
TMP_Text text = matchText.GetComponent<TMP_Text>();
if (text != null)
{
text.fontSize = 24f;
}
}
}
private void ToggleCollapsed()
{
collapsed = !collapsed;
ApplyCollapsedState();
}
private void ApplyCollapsedState()
{
if (!hasCollapseMetrics)
{
return;
}
if (panelLobby != null)
{
panelLobby.sizeDelta = collapsed ? new Vector2(CollapsedPanelWidth, expandedPanelSize.y) : expandedPanelSize;
}
if (collapseButtonRect != null)
{
collapseButtonRect.anchoredPosition = collapsed
? new Vector2(panelLobby != null ? panelLobby.anchoredPosition.x + CollapsedPanelWidth : LobbyPanelLeft + CollapsedPanelWidth, expandedButtonPosition.y)
: expandedButtonPosition;
}
if (collapseText != null)
{
collapseText.text = collapsed ? ">" : "<";
}
if (titleObject != null)
{
titleObject.SetActive(!collapsed);
}
if (scrollObject != null)
{
scrollObject.SetActive(!collapsed);
}
if (statusText != null)
{
statusText.gameObject.SetActive(!collapsed && !string.IsNullOrEmpty(currentStatus));
}
}
private void SetRect(string nodeName, Vector2 anchorMin, Vector2 anchorMax, Vector2 pivot, Vector2 anchoredPosition, Vector2 sizeDelta)
{
Transform t = transform.FindTransform(nodeName);
if (t == null)
{
return;
}
RectTransform rt = t.GetComponent<RectTransform>();
if (rt == null)
{
return;
}
rt.anchorMin = anchorMin;
rt.anchorMax = anchorMax;
rt.pivot = pivot;
rt.anchoredPosition = anchoredPosition;
rt.sizeDelta = sizeDelta;
}
private void SetOffsets(string nodeName, Vector2 offsetMin, Vector2 offsetMax)
{
Transform t = transform.FindTransform(nodeName);
if (t == null)
{
return;
}
RectTransform rt = t.GetComponent<RectTransform>();
if (rt == null)
{
return;
}
rt.anchorMin = Vector2.zero;
rt.anchorMax = Vector2.one;
rt.pivot = new Vector2(0.5f, 0.5f);
rt.offsetMin = offsetMin;
rt.offsetMax = offsetMax;
}
private void SetLayoutElement(string nodeName, float height)
{
Transform t = transform.FindTransform(nodeName);
if (t == null)
{
return;
}
LayoutElement layout = t.GetComponent<LayoutElement>();
if (layout == null)
{
return;
}
layout.minHeight = height;
layout.preferredHeight = height;
}
private static void SetChildText(Transform root, string childName, string text)
{
Transform t = root.FindTransform(childName);
if (t != null)
{
TMP_Text label = t.GetComponent<TMP_Text>();
if (label != null)
{
label.text = text ?? string.Empty;
}
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: edc88be0ce043d84280fa00680180db4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,136 @@
using System;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace XGame
{
public class MatchWaitingController : MonoBehaviour
{
private RectTransform spinnerRect;
private TMP_Text titleText;
private TMP_Text gameText;
private TMP_Text statusText;
private TMP_Text elapsedText;
private TMP_Text closeText;
private Button closeButton;
private string luaModule = "client/baseworld";
private string currentGameId = string.Empty;
private float startedAt;
public Action OnCloseRequested;
private void Awake()
{
CacheControls();
WireEvents();
startedAt = Time.realtimeSinceStartup;
UpdateElapsedText();
}
private void Update()
{
if (spinnerRect != null)
{
spinnerRect.Rotate(0f, 0f, -180f * Time.unscaledDeltaTime);
}
if (elapsedText != null && startedAt > 0f)
{
UpdateElapsedText();
}
}
public void Bind(string module)
{
if (!string.IsNullOrEmpty(module))
{
luaModule = module;
}
}
public void SetWaiting(string gameId, string message)
{
CacheControls();
currentGameId = gameId ?? string.Empty;
startedAt = Time.realtimeSinceStartup;
SetText(titleText, "匹配等待");
SetText(gameText, string.IsNullOrEmpty(currentGameId) ? "正在寻找对局" : currentGameId);
UpdateElapsedText();
SetStatus(string.IsNullOrEmpty(message) ? "正在匹配,请稍候..." : message, false);
}
public void SetStatus(string message)
{
SetStatus(message, false);
}
public void SetStatus(string message, bool canClose)
{
CacheControls();
SetText(statusText, message);
if (closeText != null)
{
closeText.text = canClose ? "返回大厅" : "退出匹配";
}
}
public void Close()
{
Destroy(gameObject);
}
private void CacheControls()
{
spinnerRect = spinnerRect ? spinnerRect : Find<RectTransform>("Img_Spinner");
titleText = titleText ? titleText : Find<TMP_Text>("Txt_Title");
gameText = gameText ? gameText : Find<TMP_Text>("Txt_Game");
statusText = statusText ? statusText : Find<TMP_Text>("Txt_Status");
elapsedText = elapsedText ? elapsedText : Find<TMP_Text>("Txt_Elapsed");
closeButton = closeButton ? closeButton : Find<Button>("Btn_Close");
closeText = closeText ? closeText : Find<TMP_Text>("Txt_Close");
if (closeText == null && closeButton != null)
{
closeText = closeButton.transform.FindTransform("Text")?.GetComponent<TMP_Text>();
}
}
private void WireEvents()
{
if (closeButton == null)
{
return;
}
closeButton.onClick.RemoveListener(OnCloseClicked);
closeButton.onClick.AddListener(OnCloseClicked);
}
private void OnCloseClicked()
{
OnCloseRequested?.Invoke();
}
private T Find<T>(string name) where T : Component
{
Transform target = transform.FindTransform(name);
return target == null ? null : target.GetComponent<T>();
}
private static void SetText(TMP_Text text, string value)
{
if (text != null)
{
text.text = value ?? string.Empty;
}
}
private void UpdateElapsedText()
{
if (elapsedText == null || startedAt <= 0f)
{
return;
}
int seconds = Mathf.Max(0, Mathf.FloorToInt(Time.realtimeSinceStartup - startedAt));
elapsedText.text = string.Format("已匹配时间 {0:00}:{1:00}", seconds / 60, seconds % 60);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5fcb2b9a9dce4b24a8df7d942f7d5a63
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: