Ники, общий рейтинг (топ-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,111 @@
|
||||
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()
|
||||
Reference in New Issue
Block a user