Files
mtgodot-poc/project/p10_test.gd
T

142 lines
5.7 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# p10_test —— P10serverinfo + loading 遮罩 + 断线重连 + 串场状态机 headless 自检。
# godot --headless --path project --script p10_test.gd
extends SceneTree
const ServerInfoRes = preload("res://net/serverinfo.gd")
const LoadingScreen = preload("res://ui/loading_screen.gd")
const ReconnectUI = preload("res://ui/reconnect_ui.gd")
const AppFlow = preload("res://app_flow.gd")
class FakeClient extends Node:
signal phase_changed(phase: String)
signal char_list(characters: Array)
signal entered_game()
signal disconnected(reason: String)
signal login_failed(reason: String)
var reconnects := 0
var connects := []
var selects := []
func connect_to_server(ah, ap, gh, gp, id, pw) -> void:
connects.append([ah, ap, gh, gp, id, pw])
func select_character(i) -> bool: selects.append(i); return true
func reconnect() -> bool: reconnects += 1; return true
var _fail := 0
func _ck(c: bool, m: String) -> void:
if not c:
_fail += 1
printerr("FAIL: " + m)
func _init() -> void:
_run()
if _fail == 0:
print("PASS: p10_test (serverinfo + loading + reconnect + app flow)")
quit(0)
else:
printerr("%d check(s) failed" % _fail)
quit(1)
func _run() -> void:
# --- serverinfo ---
var si: RefCounted = ServerInfoRes.new()
_ck(si.count() == 1 and si.server(0)["auth_host"] == "192.168.21.203", "serverinfo: built-in default")
var addr: Dictionary = si.address(0, 3)
_ck(addr["game_port"] == 13022, "serverinfo: 内置 CH3 -> 13022 (step 10)")
# user:// may be read-only in the managed headless runner; the parser only
# needs a temporary text file, so keep this fixture outside the project.
var tmp := "/tmp/mtgodot-p10-servers.txt"
var f := FileAccess.open(tmp, FileAccess.WRITE)
f.store_line("# test")
f.store_line("欧服\teu.example.com\t13000\teu.example.com\t13011\t1,2\t10")
f.close()
_ck(si.load_file(tmp) and si.count() == 1 and si.server(0)["name"] == "欧服", "serverinfo: parse file")
_ck(si.address(0, 2)["game_port"] == 13021, "serverinfo: file port_step 10 覆盖 -> CH2 = 13021")
# --- loading screen ---
var ls: Node = LoadingScreen.new()
get_root().add_child(ls)
ls.setup(null)
_ck(not ls.is_showing(), "loading: hidden initially")
ls._on_phase("loading")
_ck(ls.is_showing() and ls._label.text.contains("地图"), "loading: shows on phase=loading")
ls.set_progress(0.5)
_ck(ls._bar.visible and ls._bar.value == 50.0, "loading: progress bar")
ls._on_phase("select")
_ck(not ls.is_showing(), "loading: hides on phase=select (选人界面要露出来)")
ls._on_phase("loading")
ls._on_phase("game")
_ck(not ls.is_showing(), "loading: hides on phase=game")
ls._on_phase("login")
_ck(not ls.is_showing(), "loading: hides on phase=login")
# --- reconnect ---
var fc := FakeClient.new()
get_root().add_child(fc)
var rc: Node = ReconnectUI.new()
get_root().add_child(rc)
rc.setup(fc)
_ck(not rc.is_showing(), "reconnect: hidden initially")
fc.disconnected.emit("peer closed")
_ck(rc.is_showing() and rc._msg.text.contains("peer closed"), "reconnect: shows on disconnect")
# 倒计时耗尽 -> reconnect()
rc._left = 0.05
rc._process(0.1)
_ck(fc.reconnects == 1, "reconnect: auto-retry fired")
# 手动
rc._retry_now()
_ck(fc.reconnects == 2, "reconnect: manual retry")
# entered_game -> 收起
fc.entered_game.emit()
_ck(not rc.is_showing(), "reconnect: dismissed on entered_game")
# --- app flow 状态机 ---
var fc2 := FakeClient.new()
var af: Node = AppFlow.new()
af.build_game_scene = false
get_root().add_child(af)
af.start("", fc2)
_ck(af.state() == AppFlow.LOGIN, "flow: starts at LOGIN")
# 检测频道按钮存在 + 探测不可达端口快速返回 false
var has_probe := false
for b in af._ui.find_children("*", "Button", true, false):
if String(b.text) == "检测频道":
has_probe = true
_ck(has_probe, "flow: 检测频道 button present")
var reachable: bool = await af._probe_tcp("127.0.0.1", 1, 0.3)
_ck(reachable == false, "flow: _probe_tcp unreachable -> false")
af._do_connect()
_ck(fc2.connects.size() == 1 and fc2.connects[0][3] == 13002, "flow: connect uses serverinfo addr")
if af._loading and af._loading.has_method("show_for"):
af._loading.show_for("载入角色…") # 模拟 phase=select 时遮罩已亮
fc2.char_list.emit([{"index": 0, "name": "Hero", "level": 42}])
_ck(af.state() == AppFlow.SELECT, "flow: char_list -> SELECT")
_ck(af._loading == null or not af._loading.is_showing(),
"flow: _goto_select 收起读取遮罩(选人界面不被盖)")
# 新版选人页:默认已选 slot 0,点“开始”进游戏
var picked := false
for b in af._ui.find_children("*", "Button", true, false):
if String(b.text) == "开始":
b.pressed.emit()
picked = true
_ck(picked and fc2.selects == [0], "flow: pick char -> select_character(0)")
fc2.entered_game.emit()
_ck(af.state() == AppFlow.GAME, "flow: entered_game -> GAME")
# build_game_scene=false skips the real GameScene in this headless state
# machine test; provide its live-scene sentinel so disconnect handling is
# exercised with the same ownership contract as the real client.
af._game = Node.new()
af._game.name = "FakeGameScene"
af.add_child(af._game)
# 在 GAME 时断线 -> 保留 GAME,显示重连遮罩;重连后的角色列表静默选回角色
fc2.disconnected.emit("game dropped")
_ck(af.state() == AppFlow.GAME and af._reconnecting_game,
"flow: disconnect in GAME -> reconnecting GAME")
fc2.char_list.emit([{"index": 0, "name": "Hero", "level": 42}])
_ck(fc2.selects == [0, 0], "flow: reconnect re-enters previous character")
fc2.entered_game.emit()
_ck(af.state() == AppFlow.GAME and not af._reconnecting_game,
"flow: reconnect entered_game -> GAME")
fc2.disconnected.emit("dropped")
_ck(af.state() == AppFlow.GAME and af._reconnecting_game,
"flow: second game disconnect keeps GAME")