Ники, общий рейтинг (топ-10 по картам), таблица Tab, сдача в гонке, таймер Parkour
- Ники: профиль user://profile.cfg (client_id + ник), панель при первом запуске и по N; Net.nick_of(id); ники над призраками, в HUD/итогах гонки и таблицах - Рейтинг на сервере: лучшее время каждого игрока по картам race/parkour/metro, топ-10, user://leaderboard.json (атомарная запись, лимиты, проверка карты/комнаты/времени) - Таблица лидеров по Tab, доски топ-3 над порталами в хабе, итоги гонки с общим рейтингом - Гонка: сдача (G, удержание 1,5 с), RACE_GIVEUP; Parkour: таймер, финиш на вершине, R — перезапуск - Новые RPC (submit_profile, recv_nick, submit_record, recv_record_result, request_leaderboard, recv_leaderboard): клиент и сервер выкладываются вместе - Тесты: nicks_lb_test, race_giveup_test, parkour_run_test, nick_ui_test, ui_tour; README обновлён Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WxZo3ekR5m1af437Qzqwc1
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
extends Node
|
||||
# Панель ника и таблица лидеров «по существу» (без сети и окна): настоящая карта Race, игрок, Overlay, события клавиш.
|
||||
# godot --headless --path <проект> res://tools/test/nick_ui_test.tscn -- --client --profile=/tmp/ui_profile.cfg
|
||||
# Проверяет: игрок двигается; после N панель открыта, а «вперёд» не двигает; R/N/Tab/G в поле — не действия (R не меняет готовность,
|
||||
# Tab не открывает таблицу), буквы попадают в поле, открывающая клавиша — нет; кириллица и вставка; предел 16 символов;
|
||||
# Enter сохраняет ник в профиль; после закрытия игрок снова двигается; Esc при первом запуске — пропуск без ника и «больше не спрашивать»;
|
||||
# Tab открывает/закрывает таблицу, игрок при этом не заблокирован. Выводит «ДА/НЕТ», в конце «UI ПРОЙДЕН / ПРОВАЛЕН».
|
||||
|
||||
var ok := true
|
||||
var level
|
||||
var p
|
||||
var rules
|
||||
|
||||
func check(cond: bool, text: String) -> void:
|
||||
print((" ДА " if cond else " НЕТ ") + text)
|
||||
if not cond:
|
||||
ok = false
|
||||
|
||||
func key(code: int, pressed: bool, unicode := 0) -> void:
|
||||
var ev := InputEventKey.new()
|
||||
ev.physical_keycode = code as Key
|
||||
ev.keycode = code as Key
|
||||
ev.unicode = unicode
|
||||
ev.pressed = pressed
|
||||
Input.parse_input_event(ev)
|
||||
|
||||
func tap(code: int, unicode := 0) -> void:
|
||||
key(code, true, unicode)
|
||||
await get_tree().process_frame
|
||||
key(code, false, unicode)
|
||||
await get_tree().process_frame
|
||||
|
||||
func hold_forward(sec: float) -> float:
|
||||
var start: Vector3 = p.global_position
|
||||
Input.action_press("move_forward")
|
||||
await get_tree().create_timer(sec).timeout
|
||||
Input.action_release("move_forward")
|
||||
await get_tree().create_timer(0.2).timeout
|
||||
var d: Vector3 = p.global_position - start
|
||||
return Vector2(d.x, d.z).length()
|
||||
|
||||
func _ready() -> void:
|
||||
_run.call_deferred()
|
||||
|
||||
func _run() -> void:
|
||||
level = (load("res://maps/race/race.tscn") as PackedScene).instantiate()
|
||||
add_child(level)
|
||||
await get_tree().create_timer(1.0).timeout
|
||||
p = level.get_node("Player")
|
||||
rules = level.get_node("Rules")
|
||||
check(Net.nick_asked == false and Net.my_nick == "", "свежий профиль: ник не задан, не спрашивали")
|
||||
check(Net.my_client_id.length() == 32, "создан client_id из 32 hex-символов (128 бит)")
|
||||
var mouse_works := Input.mouse_mode == Input.MOUSE_MODE_CAPTURED
|
||||
var moved := await hold_forward(0.6)
|
||||
check(moved > 1.0, "без панели игрок бежит вперёд (%.2f м)" % moved)
|
||||
|
||||
print("-- открываем панель клавишей N")
|
||||
await tap(KEY_N, 110)
|
||||
await get_tree().create_timer(0.2).timeout
|
||||
check(Overlay.nick_open and Net.input_blocked, "N открыла панель и поставила флаг блокировки")
|
||||
check(not p.input_frozen, "флаг гонки input_frozen не тронут")
|
||||
check(Overlay._edit.has_focus() and Overlay._edit.text == "", "поле в фокусе, открывающая клавиша «n» в поле не попала (текст «%s»)" % Overlay._edit.text)
|
||||
moved = await hold_forward(0.7)
|
||||
check(moved < 0.05, "при открытой панели «вперёд» не двигает игрока (%.3f м)" % moved)
|
||||
var was_ready: bool = rules.flags_ready.get(rules.my_id, false)
|
||||
await tap(KEY_R, 114)
|
||||
await tap(KEY_N, 110)
|
||||
await tap(KEY_TAB)
|
||||
await tap(KEY_G, 103)
|
||||
check(rules.flags_ready.get(rules.my_id, false) == was_ready, "R в поле не переключает «готов» в гонке")
|
||||
check(Overlay.nick_open and not Overlay.lb_open, "N и Tab в поле не закрыли панель и не открыли таблицу")
|
||||
check(Overlay._edit.text == "rng", "R, N, G попали в поле как буквы, Tab — нет (текст «%s»)" % Overlay._edit.text)
|
||||
Overlay._edit.text = ""
|
||||
Overlay._edit.insert_text_at_caret("Привет, [b]мир[/b] и ещё длинный хвост") # как вставка из буфера
|
||||
check(Overlay._edit.text.length() <= 16 and Overlay._edit.text.begins_with("Привет"), "вставка с кириллицей обрезана до 16 символов: «%s»" % Overlay._edit.text)
|
||||
Overlay._edit.text = ""
|
||||
await tap(KEY_B, 1080) # «и» с unicode — как набор на русской раскладке
|
||||
await tap(KEY_K, 1082)
|
||||
await tap(KEY_SPACE, 32)
|
||||
await tap(KEY_A, 1103)
|
||||
check(Overlay._edit.text == "ик я", "набор кириллицы клавишами: «%s»" % Overlay._edit.text)
|
||||
Overlay._edit.text = " Мир [b]x "
|
||||
await tap(KEY_ENTER)
|
||||
await get_tree().create_timer(0.2).timeout
|
||||
check(not Overlay.nick_open and not Net.input_blocked, "Enter закрыл панель, блокировка снята")
|
||||
check(Net.my_nick == "Мир [b]x" and Net.nick_of(Net.own_id()) == "Мир [b]x", "ник сохранён и обрезан по краям: «%s»" % Net.my_nick)
|
||||
var cf := ConfigFile.new()
|
||||
cf.load("user://profile.cfg" if not _prof().begins_with("--") else _prof().substr(10))
|
||||
check(cf.get_value("profile", "nick", "") == "Мир [b]x" and cf.get_value("profile", "asked", false) == true and str(cf.get_value("profile", "client_id", "")).length() == 32, "профиль записан в файл (ник, asked, client_id)")
|
||||
if mouse_works:
|
||||
check(Input.mouse_mode == Input.MOUSE_MODE_CAPTURED, "после закрытия мышь снова захвачена")
|
||||
else:
|
||||
print(" -- режим мыши в headless не хранится: проверка захвата мыши пропущена (смотреть руками)")
|
||||
moved = await hold_forward(0.6)
|
||||
check(moved > 1.0, "после закрытия игрок снова бежит (%.2f м)" % moved)
|
||||
|
||||
print("-- смена ника: в поле стоит текущий")
|
||||
await tap(KEY_N, 110)
|
||||
await get_tree().create_timer(0.2).timeout
|
||||
check(Overlay.nick_open and Overlay._edit.text == "Мир [b]x" and Overlay._nick_title.text == "Смена ника", "N снова открывает панель, ник в поле, заголовок «Смена ника»")
|
||||
await tap(KEY_ESCAPE)
|
||||
await get_tree().create_timer(0.2).timeout
|
||||
check(not Overlay.nick_open and Net.my_nick == "Мир [b]x", "Esc при смене — отмена, ник прежний")
|
||||
|
||||
print("-- первый запуск, Esc: пропуск")
|
||||
Net.my_nick = ""
|
||||
Net.nick_asked = false
|
||||
Overlay.open_nick(true)
|
||||
await get_tree().create_timer(0.2).timeout
|
||||
await tap(KEY_ESCAPE)
|
||||
await get_tree().create_timer(0.2).timeout
|
||||
check(not Overlay.nick_open and Net.nick_asked and Net.my_nick == "" and Net.nick_of(Net.own_id()).begins_with("Игрок "), "Esc: панель закрыта, больше не спрашиваем, ник «%s»" % Net.nick_of(Net.own_id()))
|
||||
|
||||
print("-- таблица лидеров (Tab)")
|
||||
await tap(KEY_TAB)
|
||||
check(Overlay.lb_open and not Net.input_blocked, "Tab открывает таблицу, игрок не заблокирован")
|
||||
moved = await hold_forward(0.5)
|
||||
check(moved > 0.8, "с таблицей игрок двигается (%.2f м)" % moved)
|
||||
await tap(KEY_TAB)
|
||||
check(not Overlay.lb_open, "повторный Tab закрывает")
|
||||
await tap(KEY_TAB)
|
||||
await tap(KEY_ESCAPE)
|
||||
check(not Overlay.lb_open, "Esc закрывает таблицу")
|
||||
print("\nUI ПРОЙДЕН" if ok else "\nUI ПРОВАЛЕН")
|
||||
get_tree().quit(0 if ok else 1)
|
||||
|
||||
func _prof() -> String:
|
||||
for a in OS.get_cmdline_user_args():
|
||||
if a.begins_with("--profile="):
|
||||
return a
|
||||
return "user://profile.cfg"
|
||||
Reference in New Issue
Block a user