- Ники: профиль 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
112 lines
4.8 KiB
GDScript
112 lines
4.8 KiB
GDScript
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()
|