Files
my-world/tools/test/lb_client.gd
T
aibrashandClaude Sonnet 5.5 666bebfb60 Ники, общий рейтинг (топ-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
2026-10-29 12:09:33 +05:00

112 lines
4.8 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.
extends Node
# Клиент-сценарий для проверки ников и рейтинга (см. nicks_lb_test.py): выполняет команды из --cmds=a;b;c
# и печатает факты строками «[имя] ФАКТ …». Без игрока и карты, если не вызвана команда scene:hub.
# godot --headless --path <копия> res://tools/test/lb_client.tscn -- --client --name=A --profile=/tmp/a.cfg --cmds="nick:Алиса;map:race;sleep:2;show;quit"
# Команды: wait_connected, nick:<текст>, map:<id>, scene:hub, sleep:<с>, waitghosts:<n>, show, rec:<карта>:<время>,
# recn:<карта>:<сколько>:<время0> (подряд, время каждый раз на 1 с меньше), req:<карта>, reqn:<карта>:<сколько>,
# bad (заведомо неверные submit_record), last:<карта>, quit.
var label := "?"
var cmds: Array = []
var results := 0
var board_updates := 0
func _ready() -> void:
for a in OS.get_cmdline_user_args():
if a.begins_with("--name="): label = a.substr(7)
if a.begins_with("--cmds="): cmds = a.substr(7).split(";")
Net.record_result.connect(func(m, t, r, pb, mr): results += 1; say("ФАКТ РЕЗУЛЬТАТ %s время=%.3f место=%d личный=%s рекорд_карты=%s" % [m, t, r, pb, mr]))
Net.leaderboard_updated.connect(func(_m): board_updates += 1)
_run.call_deferred()
func say(text: String) -> void:
print("[%s] %s" % [label, text])
func _wait_until(cond: Callable, timeout := 8.0) -> bool:
var t := 0.0
while not cond.call():
await get_tree().create_timer(0.1).timeout
t += 0.1
if t > timeout:
return false
return true
func _board_text(m: String) -> String:
var b = Net.boards.get(m)
if b == null:
return "нет данных"
var parts := []
for e in b.top:
parts.append("%s:%.3f" % [e[0], e[1]])
return "n=%d место=%d моё=%.3f топ=%s" % [b.top.size(), b.my_rank, b.my_time, "|".join(parts)]
func _run() -> void:
for c in cmds:
var p: PackedStringArray = c.split(":", true, 3)
match p[0]:
"wait_connected":
say("ФАКТ ПОДКЛЮЧЁН %s" % await _wait_until(func(): return Net.connected))
"nick":
Net.set_nick(c.substr(5))
"map":
Net.enter_map(p[1])
say("ФАКТ КАРТА %s готова=%s" % [p[1], await _wait_until(func(): return Net.map_ready)])
"scene":
add_child((load("res://maps/hub/hub.tscn") as PackedScene).instantiate())
say("ФАКТ ХАБ готов=%s" % await _wait_until(func(): return Net.map_ready))
"sleep":
await get_tree().create_timer(float(p[1])).timeout
"waitghosts":
say("ФАКТ ПРИЗРАКИ %s" % await _wait_until(func(): return Net.ghosts.size() >= int(p[1]), 10.0))
await get_tree().create_timer(0.7).timeout
"show":
var gl := []
for id in Net.ghosts:
var g = Net.ghosts[id]
var l: Label3D = g.label
gl.append("%d=%s(%s)" % [id, l.text, Net.nick_of(id)])
var ps := []
for id in Net.nicks:
ps.append("%d=%s" % [id, Net.nicks[id]])
ps.sort()
gl.sort()
say("ФАКТ НИКИ свой=%s ники=%s призраки=%s" % [Net.nick_of(Net.own_id()), ",".join(ps), ",".join(gl)])
"rec":
Net.submit_my_record(p[1], float(p[2]))
await get_tree().create_timer(0.6).timeout
"recn":
for i in int(p[2]):
Net.submit_my_record(p[1], float(p[3]) - i)
await get_tree().create_timer(1.5).timeout
say("ФАКТ ОТВЕТОВ_НА_ЗАПИСИ %d" % results)
"req":
var before := board_updates
Net.request_board(p[1])
await _wait_until(func(): return board_updates > before, 3.0)
say("ФАКТ ТАБЛИЦА %s %s" % [p[1], _board_text(p[1])])
"reqn":
var before := board_updates
for i in int(p[2]):
Net.request_leaderboard.rpc_id(1, p[1])
await get_tree().create_timer(1.5).timeout
say("ФАКТ ОТВЕТОВ_НА_ЗАПРОСЫ %d" % (board_updates - before))
"bad":
var before := results
Net.submit_record.rpc_id(1, "zzz", 60.0) # нет такой карты
Net.submit_record.rpc_id(1, "race", 5.0) # быстрее допустимого
Net.submit_record.rpc_id(1, "race", 99999.0) # дольше допустимого
Net.submit_record.rpc_id(1, "race", NAN)
Net.submit_record.rpc_id(1, "race", -3.0)
Net.submit_record.rpc_id(1, "parkour", 90.0) # карта, на которой мы не находимся
Net.request_leaderboard.rpc_id(1, "zzz")
await get_tree().create_timer(1.5).timeout
say("ФАКТ НЕВЕРНЫЕ_ПРИНЯТЫ %d" % (results - before))
"last":
say("ФАКТ ПОСЛЕДНИЙ %s" % str(Net.last_record.get(p[1])))
"quit":
say("ГОТОВО")
get_tree().quit()
return
say("ГОТОВО")
get_tree().quit()