Карта Race: гонка на ногах по киберпанк-антиутопии, 8 игроков
- maps/race: трасса 633 м из Blender (три развилки: срез по контейнерам, каньон со стенами против нижней дороги, труба с лазерами против коридора), запечённый свет, небо, дождь, гул; фоновая песня (Ogg Vorbis, ~0,6 МБ) включается на «СТАРТ!» - Правила (race_rules.gd): R — готов; когда готовы все — отсчёт 6 с, заморозка в слотах, ворота строго по порядку, сплиты, место, итоги, личный рекорд (user://race_best.cfg) - Сеть: Net.submit_race / recv_race / race_event (RACE_READY, START, CP, FINISH, STATE). ВНИМАНИЕ: набор RPC изменён — клиент и сервер выкладываются вместе, старый клиент с новым сервером не работает (rpc node checksum failed) - player.gd: input_frozen и place() для режимов карт; project.godot: действие ready (R) - Портал RACE в хабе, пресет экспорта Race (include_filter для ресурсов, грузимых строкой) - tools/test: двухклиентный тест гонки на локальном сервере, бот, облёт, проверка пакета - README: правила гонки, события, проверки, заметки Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WxZo3ekR5m1af437Qzqwc1
This commit is contained in:
Executable
+84
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Сверка логов race_game_test.sh: A, B (два круга), C (зашёл в разгар круга 1, участвует в круге 2 и выходит)."""
|
||||
import re, sys, os
|
||||
|
||||
W = sys.argv[1]
|
||||
logs = {n: open(os.path.join(W, n + ".log"), errors="replace").read().splitlines() for n in "ABC"}
|
||||
ok = True
|
||||
|
||||
def check(cond, text):
|
||||
global ok
|
||||
print((" ДА " if cond else " НЕТ ") + text)
|
||||
if not cond:
|
||||
ok = False
|
||||
|
||||
def rounds_of(lines):
|
||||
"""Таблицы итогов по кругам: список [ [(место, id, время, ворот)...], ... ]"""
|
||||
res, cur = [], []
|
||||
for l in lines:
|
||||
m = re.search(r"итог: место (\d+) игрок (-?\d+) время (-?[\d.]+) ворот (\d+)", l)
|
||||
if m:
|
||||
if cur and int(m.group(1)) == 1:
|
||||
res.append(cur); cur = []
|
||||
cur.append((int(m.group(1)), int(m.group(2)), float(m.group(3)), int(m.group(4))))
|
||||
elif cur and "фаза -> WAIT" in l:
|
||||
res.append(cur); cur = []
|
||||
if cur: res.append(cur)
|
||||
return res
|
||||
|
||||
ra, rb = rounds_of(logs["A"]), rounds_of(logs["B"])
|
||||
print("Итоговые таблицы A:", ra)
|
||||
print("Итоговые таблицы B:", rb)
|
||||
check(len(ra) == 2 and len(rb) == 2, "оба клиента показали итоги двух кругов (%d и %d)" % (len(ra), len(rb)))
|
||||
for i in range(min(len(ra), len(rb))):
|
||||
check(ra[i] == rb[i], "круг %d: таблица мест одинакова на A и B" % (i + 1))
|
||||
if ra[i]:
|
||||
times = [r[2] for r in ra[i]]
|
||||
check(len(ra[i]) == 2 and all(t > 0 for t in times) and len(set(times)) == len(times),
|
||||
"круг %d: два финиша с разным временем %s" % (i + 1, times))
|
||||
check([r[0] for r in ra[i]] == [1, 2] and ra[i][0][2] < ra[i][1][2], "круг %d: места 1 и 2 по возрастанию времени" % (i + 1))
|
||||
if len(ra) == 2:
|
||||
check(len(ra[1]) == 2, "круг 2: C вышел в разгар и убран из итогов (в таблице %d строк)" % len(ra[1]))
|
||||
|
||||
# один START на круг
|
||||
starts = sum(1 for n in "ABC" for l in logs[n] if "даю старт" in l)
|
||||
check(starts == 2, "команду START дал один клиент на круг (всего %d за 2 круга)" % starts)
|
||||
for n in "AB":
|
||||
senders = re.findall(r"старт от (\d+): слот (\d+) из (\d+)", "\n".join(logs[n]))
|
||||
print(" старт на %s: %s" % (n, senders))
|
||||
sa = re.findall(r"старт от (\d+): слот (\d+) из (\d+)", "\n".join(logs["A"]))
|
||||
sb = re.findall(r"старт от (\d+): слот (\d+) из (\d+)", "\n".join(logs["B"]))
|
||||
sc = re.findall(r"старт от (\d+): слот (\d+) из (\d+)", "\n".join(logs["C"]))
|
||||
check(len(sa) == 2 and len(sb) == 2 and [s[0] for s in sa] == [s[0] for s in sb], "A и B приняли одних и тех же отправителей START в обоих кругах")
|
||||
if len(sa) == 2 and len(sb) == 2:
|
||||
check(sa[0][2] == "2" and sa[1][2] == "3", "в круге 1 участвовали 2, в круге 2 — 3 игрока (A: %s)" % [s[2] for s in sa])
|
||||
check(sorted([sa[1][1], sb[1][1], sc[0][1] if sc else "?"]) == ["0", "1", "2"] and sa[0][1] != sb[0][1], "слоты в кругах различны и подряд")
|
||||
# синхронность старта по unix-времени (фаза RACE)
|
||||
def race_unix(lines):
|
||||
return [float(m.group(1)) for l in lines for m in [re.search(r"ФАЗА RACE, unix=([\d.]+)", l)] if m]
|
||||
ua, ub = race_unix(logs["A"]), race_unix(logs["B"])
|
||||
print(" unix начала гонки A:", ua, " B:", ub)
|
||||
check(len(ua) == 2 and len(ub) == 2 and all(abs(x - y) < 0.35 for x, y in zip(ua, ub)), "начало гонки на A и B совпадает в пределах 0,35 с: %s" % [round(x - y, 3) for x, y in zip(ua, ub)])
|
||||
# заморозка
|
||||
fr = [float(m.group(1)) for n in "ABC" for l in logs[n] for m in [re.search(r"ЗАМОРОЗКА: сдвиг за отсчёт ([\d.]+)", l)] if m]
|
||||
check(len(fr) >= 5 and max(fr) < 0.01, "заморозка на отсчёте: смещения %s" % fr)
|
||||
# музыка
|
||||
mus_after = [l for n in "AB" for l in logs[n] if "МУЗЫКА после старта" in l]
|
||||
mus_cd = [l for n in "AB" for l in logs[n] if "МУЗЫКА на отсчёте" in l]
|
||||
check(len(mus_after) >= 4 and all("играет=true" in l for l in mus_after), "музыка играет после старта (%d проверок)" % len(mus_after))
|
||||
check(len(mus_cd) >= 4 and all("false" in l for l in mus_cd), "на отсчёте музыки нет (%d проверок)" % len(mus_cd))
|
||||
# возрождение
|
||||
resp = [l for n in "AB" for l in logs[n] if "ВОЗРОЖДЕНИЕ" in l]
|
||||
for l in resp: print(" ", l)
|
||||
check(len(resp) >= 4 and all("-> ВЕРНО" in l for l in resp), "возрождение у ворот лицом по трассе (%d проверок, ошибок нет)" % len(resp))
|
||||
# C: зритель, потом участник
|
||||
c = "\n".join(logs["C"])
|
||||
check("ГОНКА_ИДЁТ" in c and "участник=false" in c, "C зашёл в разгар круга 1: «гонка идёт», без зачёта")
|
||||
check("УЧАСТНИК=true" in c, "C участвует в круге 2")
|
||||
check("ВЫХОЖУ из игры" in c, "C вышел в разгар круга 2")
|
||||
for n in "ABC":
|
||||
errs = [l for l in logs[n] if "SCRIPT ERROR" in l or (l.startswith("ERROR") and "still in use at exit" not in l) or "Parse Error" in l]
|
||||
check(not errs, "в логе %s нет ошибок скриптов %s" % (n, errs[:2]))
|
||||
check("ГОТОВО" in "\n".join(logs["A"]) and "ГОТОВО" in "\n".join(logs["B"]), "A и B дошли до конца теста")
|
||||
print("\nПРОВЕРКА ПРОЙДЕНА" if ok else "\nПРОВЕРКА ПРОВАЛЕНА")
|
||||
sys.exit(0 if ok else 1)
|
||||
Reference in New Issue
Block a user