Add MainHUD gameplay UI, net handshake updates, and server maintenance mode.

Introduce UiManager/MainHUD prefab flow, strengthen GameNet/WsClient, and add AIServer maintenance endpoint support.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
udx
2026-09-21 17:34:42 +08:00
co-authored by Cursor
parent 10d9b98637
commit c94b8fefeb
32 changed files with 1907 additions and 74 deletions
+16 -1
View File
@@ -19,7 +19,8 @@ Base: `https://game.xworld.ren`(Caddy → 后端)
| --- | --- | --- | --- | --- |
| POST | `/register` | `{"username","password"}` | `{token,pid}` | `invalid_input` / `user_exists` |
| POST | `/login` | `{"username","password"}` | `{token,pid}` | `invalid_credentials` |
| GET | `/health` | — | `{ok,service}` | — |
| GET | `/health` | — | `{ok,service,maintenance}` | — |
| GET | `/maintenance` | — | `{enabled,message}` | — |
## WebSocket
@@ -29,8 +30,22 @@ Base: `https://game.xworld.ren`(Caddy → 后端)
- 无 token / token 无效 → HTTP 401
- 应用层:`{"op":"hello"}` → `{"op":"hello_ack","ok":true}`;`{"op":"ping"}` → `{"op":"pong"}`
- 维护:若 `data/maintenance.json` 中 `enabled=true`,升级后立刻下发 `{"op":"maintenance","message":"..."}` 并断开
- 协议层 Ping/Pong 由服务端应答
## 维护开关
文件:`AIServer/data/maintenance.json`(与 `--db` 同目录,可用 `--maintenance` 覆盖)
```json
{ "enabled": true, "message": "今晚 2:00-4:00 维护" }
```
| GET | `/maintenance` | — | `{enabled,message}` |
| GET | `/health` | — | 增加 `maintenance` 字段 |
CDN `version.json.notice` 也会在客户端启动时弹公告(不依赖游戏服)。
## 客户端
- `HotUpdate.Net.AuthApi` / `GameNet` / `WsClient`
+1
View File
@@ -16,3 +16,4 @@
| `Doc/ui_design.jpg` | UI 视觉基准图;糖果休闲风控件参考(`Doc/Design/ui_design.jpg` 为同内容副本) |
| `Doc/UI_AutoGen_Overview.md` | UI 自动生成方案总览(JSON→Prefab + 图集) |
| `Doc/UIPrefabCreater/UI_ConfirmDialog.json` | 竖屏确认弹窗样例 JSON;对应 Prefab 路径 `Assets/Bundles/UI/Prefab/UI_ConfirmDialog.prefab` |
| `Doc/UIPrefabCreater/UI_MainHUD.json` | M4 HUD(顶栏状态 + 断线重连条);Prefab `Assets/Bundles/UI/Prefab/UI_MainHUD.prefab` |
+228
View File
@@ -0,0 +1,228 @@
{
"schemaVersion": 1,
"prefabName": "UI_MainHUD",
"prefabPath": "Assets/Bundles/UI/Prefab/UI_MainHUD.prefab",
"description": "HUD 层:顶栏状态 + 底部断线重连条。竖屏 1080x1920。运行时绑定 Txt_Status/Txt_Pid/Panel_Reconnect/Txt_Reconnect/Btn_Reconnect。",
"canvas": {
"canvasScaler": {
"referenceResolution": [1080, 1920],
"matchWidthOrHeight": 0.5
}
},
"assets": {
"sprites": {
"ui_panel_header": "Assets/Bundles/UI/Texture/Common/ui_panel_header.png",
"toast_bg": "Assets/Bundles/UI/Texture/Common/toast_bg.png",
"btn_primary_normal": "Assets/Bundles/UI/Texture/Common/btn_primary_normal.png",
"btn_primary_pressed": "Assets/Bundles/UI/Texture/Common/btn_primary_pressed.png",
"ico_refresh": "Assets/Bundles/UI/Texture/Common/ico_refresh.png"
}
},
"nodes": [
{
"name": "UI_MainHUD",
"parent": null,
"active": true,
"rect": {
"anchorMin": [0, 0],
"anchorMax": [1, 1],
"offsetMin": [0, 0],
"offsetMax": [0, 0]
},
"components": [
{ "type": "Canvas", "sortingOrder": 100 },
{
"type": "CanvasScaler",
"referenceResolution": [1080, 1920],
"matchWidthOrHeight": 0.5
},
{ "type": "GraphicRaycaster" }
]
},
{
"name": "Panel_Top",
"parent": "UI_MainHUD",
"active": true,
"rect": {
"anchorMin": [0, 1],
"anchorMax": [1, 1],
"pivot": [0.5, 1],
"anchoredPosition": [0, -48],
"sizeDelta": [-48, 120]
},
"components": [
{
"type": "Image",
"sprite": "ui_panel_header",
"imageType": "Sliced",
"raycastTarget": false,
"color": "#FFFFFFFF"
}
]
},
{
"name": "Txt_Status",
"parent": "Panel_Top",
"active": true,
"rect": {
"anchorMin": [0, 0],
"anchorMax": [1, 1],
"offsetMin": [32, 16],
"offsetMax": [-32, -40]
},
"components": [
{
"type": "TextMeshProUGUI",
"text": "连接中…",
"fontSize": 30,
"alignment": "MidlineLeft",
"color": "#5A4632FF",
"raycastTarget": false
}
]
},
{
"name": "Txt_Pid",
"parent": "Panel_Top",
"active": true,
"rect": {
"anchorMin": [0, 0],
"anchorMax": [1, 0],
"pivot": [0, 0],
"anchoredPosition": [32, 12],
"sizeDelta": [-64, 36]
},
"components": [
{
"type": "TextMeshProUGUI",
"text": "pid=-",
"fontSize": 24,
"alignment": "MidlineLeft",
"color": "#6B7A94FF",
"raycastTarget": false
}
]
},
{
"name": "Panel_Reconnect",
"parent": "UI_MainHUD",
"active": false,
"rect": {
"anchorMin": [0.5, 0],
"anchorMax": [0.5, 0],
"pivot": [0.5, 0],
"anchoredPosition": [0, 160],
"sizeDelta": [920, 160]
},
"components": [
{
"type": "Image",
"sprite": "toast_bg",
"imageType": "Sliced",
"raycastTarget": true,
"color": "#FFFFFFFF"
}
]
},
{
"name": "Txt_Reconnect",
"parent": "Panel_Reconnect",
"active": true,
"rect": {
"anchorMin": [0, 0.45],
"anchorMax": [1, 1],
"offsetMin": [28, 8],
"offsetMax": [-28, -16]
},
"components": [
{
"type": "TextMeshProUGUI",
"text": "网络已断开,正在重连…",
"fontSize": 28,
"alignment": "Center",
"color": "#33405EFF",
"raycastTarget": false
}
]
},
{
"name": "Btn_Reconnect",
"parent": "Panel_Reconnect",
"active": true,
"rect": {
"anchorMin": [0.5, 0],
"anchorMax": [0.5, 0],
"pivot": [0.5, 0],
"anchoredPosition": [0, 20],
"sizeDelta": [280, 72]
},
"components": [
{
"type": "Image",
"sprite": "btn_primary_normal",
"imageType": "Sliced",
"raycastTarget": true,
"color": "#FFFFFFFF"
},
{
"type": "Button",
"transition": "SpriteSwap",
"spritePressed": "btn_primary_pressed",
"onClick": "MainHUD.Reconnect"
}
]
},
{
"name": "Txt_ReconnectBtn",
"parent": "Btn_Reconnect",
"active": true,
"rect": {
"anchorMin": [0, 0],
"anchorMax": [1, 1],
"offsetMin": [0, 0],
"offsetMax": [0, 0]
},
"components": [
{
"type": "TextMeshProUGUI",
"text": "立即重连",
"fontSize": 30,
"fontStyle": "Bold",
"alignment": "Center",
"color": "#F6F8FFFF",
"raycastTarget": false
},
{
"type": "Outline",
"effectColor": "#102033AA",
"effectDistance": [1, -1]
}
]
},
{
"name": "Img_Refresh",
"parent": "Btn_Reconnect",
"active": true,
"rect": {
"anchorMin": [0, 0.5],
"anchorMax": [0, 0.5],
"pivot": [0, 0.5],
"anchoredPosition": [18, 0],
"sizeDelta": [40, 40]
},
"components": [
{
"type": "Image",
"sprite": "ico_refresh",
"imageType": "Simple",
"raycastTarget": false,
"color": "#FFFFFFFF"
}
]
}
],
"bindings": ["Txt_Status", "Txt_Pid", "Panel_Reconnect", "Txt_Reconnect", "Btn_Reconnect"],
"events": [
{ "name": "MainHUD.Reconnect", "node": "Btn_Reconnect" }
]
}