尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证制作者的介绍仅有上面的一句话。 更多详情请阅读仓库 README。 仓库 README ↗
正在加载完整 README。
查看完整 README ↗Кооперативный top-down шутер в духе BulletEcho: пули летят по-настоящему, свет и тени решают всё, а рассвет наступает один раз. Сайт: https://evntary.ru.
Одиночная игра работает целиком в браузере. Все сетевые режимы (комнаты по коду, кооператив, арены с друзьями, командные режимы, рейтинговые бои) ведёт наш авторитетный игровой сервер: браузер — только клиент. Вход через Яндекс ID нужен для рейтинга и для опыта на аккаунте; в комнаты можно и гостем.
| Обзор | Как в Bullet Echo: видно только то, что в конусе взгляда и в паре метров вокруг, стены перекрывают обзор. Всё остальное погружено во тьму, враги вне обзора не рисуются (и на миникарте тоже), а их выстрелы оставляют красные маркеры. |
| Солдаты | Модели из пака ассетов с процедурной анимацией: шаги по скорости, руки держат оружие через IK, отдача, реакция на попадание, падение при смерти. |
| Свет | Фонарь игрока — настоящий SpotLight с shadow map: стены отбрасывают реальные тени. В темноте врагов почти не видно, выстрелы и взрывы освещают комнату. |
| Пули | Реальное время полёта и падение урона по дистанции, пули останавливаются стенами. Пушка летит мимо — можно уклониться. |
| Физика | Свойки-бочки взрываются цепной реакцией, ящики и столы толкаются, обломки отлетают. |
| Режимы | Кооператив: 6–8 процедурных комнат за бронированными дверями, стартовая комната без врагов. На аренах «Склад», «Двор», «Бункер», «Лес», «Офис», «Порт», «Завод», «Катакомбы», «Окопы», «Лабиринт» (боты добирают до 2–8 бойцов, таблица на Tab): Deathmatch — каждый сам за себя, 15 фрагов или 5 минут; Гонка вооружений — каждые 2 убийства дают следующее оружие (пистолет → ПП → дробовик → винтовка → снайперка), кто прошёл лестницу — победил; Соревнование — раунды до последнего выжившего, старт с пистолетом и $800, закупка на B в первые 15 с раунда, деньги за убийства и итог раунда, выжившие сохраняют снаряжение. |
| Новые режимы | Заражение — через 10 с один из бойцов заражён (на каждые 4 — ещё один): только нож, +50% здоровья, быстрее, без фонаря; убитый человек через 3 с встаёт заражённым, люди не возрождаются; продержались 3 минуты — победа людей. Эвакуация — кооп на арене: 5 грузов у дальних точек под охраной, нести к точке эвакуации (груз замедляет, раненый роняет), подкрепления охраны каждые 45 с, за 6 минут нужно вынести хотя бы половину (всё — победа сразу), павшие возвращаются у точки через 15 с. Сопровождение VIP — раунды по 90 с: атака ведёт VIP (пистолет, броня, +60% здоровья) к точке, защита мешает; без возрождения внутри раунда, стороны меняются каждый раунд, до 4 побед. Сужающаяся зона — одна жизнь, на старте нож и пистолет, остальное оружие разбросано по карте; круг пять раз сжимается (30 с держится, 18 с сжимается), снаружи темно и 9 урона в секунду; последний выживший побеждает. |
| Командные режимы | Командный бой — синие против красных до 30 фрагов, без огня по своим, обзор союзника общий. Захват флага — унести чужой флаг к себе, пока свой на базе; упавший флаг возвращают касанием; три захвата — победа. Закладка бомбы — раунды: синие закладывают бомбу на A или B (зажать E, 3 с), красные обезвреживают (E, 5 с); бомба взрывается через 35 с; закупка и деньги как в «Соревновании», пять раундов — победа. Волны — кооп против волн охраны, которые растут и идут на шум и свет; погибшие возвращаются после волны. |
| Фонарь | Включён — обзор 24 м, но ИИ замечает издалека, а луч виден врагам сквозь темноту. Выключен — обзор 13 м, зато скрытно. Боты гасят фонарь, когда подкрадываются. |
| Гранаты | G — бросок к курсору по дуге, отскок от стен, взрыв через 2 с, стены защищают. Красный круг показывает зону поражения. Боты закидывают гранатами тех, кто прячется. |
| Уровни | Темы комнат — склад, офис, двор, бункер, ночной лес (густые кусты, стены — линии деревьев), поле с зигзагами окопов и мешками с песком: свой пол, свет, разметка и набор укрытий (машины, паллеты, бочки, электрощиты, покрышки, бетонные блоки). Колонны в больших комнатах. Неподвижные укрытия никогда не перекрывают проход. |
| Тактика | Лампы простреливаются: пуля игрока гасит свет, место становится тёмным и для ботов. Дымовая граната (Q, затем G) ставит облако на 14 с — сквозь него не видят ни игроки, ни боты, видны только трассеры. Светошумовая ослепляет всех, кто видит вспышку: дольше, если смотреть на неё. Коктейль Молотова разбивается там, где упал (или о стену), и пол горит 7 с: кто в огне — горит и после, бочки с топливом в пламени взрываются, огонь освещает место для ботов, а стены его не пропускают. В кустах (дворы, арена «Двор») не видно того, кто стоит, идёт шагом или сидит, — пока не выстрелит или не побежит. Окопы (комнаты-«поля» в коопе, арена «Окопы») замедляют на 15%; пригнувшегося в окопе не видно, а пули стрелка снаружи окопа издалека (больше 5 м) бьют того, кто в окопе, на 35% силы; охрана ждёт в окопах пригнувшись. FPV-дрон (X; у инженера один на жизнь, в магазине $900): солдат стоит, дрон летит над укрытиями 12 с. Управление как у настоящего квадрокоптера: мышь разворачивает нос (с ограниченной скоростью поворота), W/S — тангаж вперёд/назад, A/D — крен вбок относительно носа; дрон набирает скорость до ~60 км/ч, по инерции скользит и тормозит, наклоняется в сторону движения, а в стену на полной скорости разбивается; ЛКМ или X — подрыв (4,5 м, 120 урона), села батарея — тоже взрыв. Дрон жужжит (его слышат боты), камера и обзор — с дрона; его можно сбить (30 hp, боты стреляют по дрону рядом), а ранение пилота рвёт связь; сбитый, разбитый о стену или потерявший связь дрон падает и взрывается там, где упал. |
| Бонусы | Аптечка, броня (поглощает 60% урона), усиленный урон, скорость, скорострельность, ПНВ (10 с видно далеко без фонаря, но вспышка слепит сильнее). Враги иногда роняют бонусы; на аренах предметы возвращаются через 18 с. Оружие подбирается, просто пройдя по нему, и исчезает с карты; погибшие роняют своё оружие (30 с). Переключение — 1–5. |
| Оружие | Пистолет, ПП, дробовик, винтовка, снайперка — у каждого свои темп, разброс, отдача и урон. |
| Враги | ИИ с патрулированием, реакцией на шум, поиском по последней известной позиции, стрельбой очередями и перемещением в укрытии. В темноте реагируют хуже. |
| Кооператив | Два и больше операторов на один объект. Матч ведёт игровой сервер: снимки состояния 20 Гц, предикт своего персонажа с откатом, интерполяция остальных. |
| Объекты (E) | Рубильник гасит или включает лампы в 13 м (простреленные остаются тёмными); ящик патронов — полный магазин и гранаты, откат 20 с; медпункт лечит, пока держите E (15 hp/с, запас 150 hp, восполняется); двери на аренах открываются и закрываются (держат пули и обзор, боты открывают сами). |
| Сброс снаряжения | Раз в минуту (в коопе — 90 с, в «Волнах» — 75 с) на парашюте падает ящик с красным дымом, место видно на миникарте. Зажать E на 2 с — топовый ствол и два бонуса; урон сбивает вскрытие. |
| Серии убийств | 3 подряд — радар на 8 с, 5 подряд (и каждые следующие 5) — миномётный удар: T ставит три мины по курсору, через 2 с свиста они падают. Смерть обнуляет серию. |
| Проволока, ракеты, мины | Колючая проволока замедляет вдвое и ранит, пули её пролетают; режется ножом или зажатой E. Осветительная ракета (граната, Q) 12 с освещает 9 м — там не спрятаться ни в кусте, ни в окопе, ни под маскировкой. Мины инженера (N, две на жизнь, $300 в магазине): взрываются под врагом, врагам видны только вблизи, расстреливаются. |
| Бонусы | Кроме аптечки, брони, урона, скорости, темпа и ПНВ: боеприпасы, регенерация (вне перестрелки), сверхщит, маскировка, радар. На каждой арене есть новые бонусы, в коопе — до двух на комнату; убитые на аренах иногда роняют бонус. |
| «Лабиринт» | Арена-лабиринт из окопов: тыловая и передняя линии зигзагом, ходы сообщения, тупики, два блиндажа (дверь, медпункт, патроны, рубильник), пулемётные гнёзда и ничейная земля с воронками и проволокой. Окопы теперь выглядят вырытыми: дощатые стенки, столбы, мешки с песком по брустверу. |
Боты в сетевой комнате. Переключатель «Боты в сетевой комнате» в меню, под числом бойцов на арене. Если боты включены, они появляются, только когда в комнате двое игроков или больше: создатель комнаты, который ждёт друзей, не попадёт в бой с ботами в одиночку. Когда остаётся один игрок, боты уходят. Если переключатель выключен, в комнате играют только люди. Выбор запоминается. В одиночной игре боты есть всегда, в рейтинговом бою их нет никогда.
В меню есть «Справка» (и в паузе): классы, режимы, оружие с характеристиками, бонусы, объекты и все клавиши. Под кнопками классов — карточка выбранного класса.
/play?room=КОД сразу заводит друга в комнату; её копирует кнопка под кодом и клик по коду в игре.Со входом через Яндекс: три задания дня и три задания недели (у каждого игрока свои, обновляются в полночь и в понедельник по Москве), достижения за всё время игры и косметика за них — камуфляжи (узор на форме бойца) и значки у имени, их видят все. Всё считает сервер по итогам онлайн-матчей; выполненное задание сразу даёт опыт. На экране итогов — награды матча (MVP, ангел-хранитель, тактик, тихий клинок, сборщик), выполненные задания и достижения.
| Клавиша | Действие |
|---|---|
WASD | движение по экрану: W вверх, S вниз, A/D влево-вправо; в паузе можно «по прицелу» (W к курсору, S от него) |
Shift | шаг: медленнее, тихо (шум 4 м) |
C | присед: ещё медленнее, бесшумно, точнее |
| мышь | прицел |
ЛКМ | огонь |
R | перезарядка |
F | фонарь вкл/выкл |
1–9 | оружие из подобранного (8 — нож, 9 — огнемёт) |
G | бросить выбранную гранату |
Q | сменить гранату: осколочная → дымовая → светошумовая → молотов |
E (зажать) | заложить / обезвредить бомбу |
Z / колёсико | метка для союзников |
V | инженер: поставить щит |
X | запустить FPV-дрон; в полёте ЛКМ или X — подрыв |
E | использовать: рубильник, ящик, дверь, медпункт, сброс, проволока |
N | инженер: поставить мину |
T | миномётный удар (серия из 5 убийств) |
B | магазин (соревнование, начало раунда) |
Tab | таблица счёта (арены) |
npm install --legacy-peer-deps # на этом Mac npm спотыкается о peer-дерево
npm run dev # http://localhost:5173 — соло работает и без сервисов
npm run dev:services # для сетевой игры и входа (см. «Архитектура»)
Если
npm installпадает сCannot read properties of null (reading 'edgesOut')— это баг npm 10 при разрешении peer-зависимостей. Флаг--legacy-peer-depsего обходит.
Модели, текстуры и звуки лежат в apps/web/public/assets/ (~2.7 МБ) и собираются из
библиотеки ассетов frontline скриптом:
FRONTLINE_ASSETS=~/frontline/assets npm run assets
Скрипт упрощает сетки, пережимает текстуры в WebP и сжимает геометрию meshopt.
Результат коммитится, поэтому деплой от frontline не зависит. Проверить, как
легли модели: /test/models.html, анимацию — /test/anim.html (на dev-сервере).
браузер ──https──▶ nginx ─┬─ статика: apps/web/dist (текущий релиз)
├─ /api, /auth, /callback, /rating ─▶ api :8090 (SQLite)
├─ /meta (WebSocket) ─────────────▶ meta :8091
└─ /game (WebSocket) ─────────────▶ game :8092
браузер ◀──UDP (WebRTC, 40000–40199)──────────────────────────▶ game
meta ◀── heartbeat ── game ── итоги матча ──▶ api
| Сервис | Что делает |
|---|---|
web (apps/web) | Сайт и игра: посадочная /, игра /play, рейтинг /rating. Соло считается в браузере, сетевые режимы — клиент сервера. |
api (apps/api) | Вход через Яндекс ID, сессии, профиль и настройки аккаунта, опыт, рейтинг Эло, история боёв, страница рейтинга для поисковиков. Единственный, кто работает с базой. |
meta (apps/meta) | Входная дверь онлайна: кто игрок (аккаунт или гость), создание комнаты, вход по коду, очередь рейтинговых боёв, реестр игровых серверов. Отвечает подписанным тикетом на место в комнате. |
game (apps/game) | Ведёт матчи всех режимов той же симуляцией, что и браузер (packages/shared): разминка и старт матча, боты, места при обрыве, проверка ввода, итоги и опыт → api. |
shared (packages/shared) | Симуляция, протоколы, правила прогресса. Чистый TypeScript без DOM. |
node-kit (packages/node-kit) | Общее для сервисов: настройки, HTTP, тикеты (HMAC), остановка. |
Как идёт сетевая игра. Браузер открывает /meta, говорит, кто он, и просит комнату
(create), вход по коду (join) или рейтинговый бой (queue). Meta выбирает игровой
сервер, создаёт на нём комнату и выдаёт тикет: кто, в какую комнату, с каким классом и
цветом, хост или нет, на 2 минуты. С тикетом браузер открывает /game, получает карту и
дальше играет как клиент. Гость опознаётся случайным id вкладки, аккаунт — cookie входа.
Транспорт с игровым сервером.
Клиент интерполирует чужих бойцов со сдвигом ~110 мс, а своего предсказывает (с учётом оружия и класса) и сверяет со снапшотами. Сервер компенсирует лаг по пингу клиента (не больше 150 мс) и чистит ввод: длина стика не больше 1, никаких NaN, только известные покупки.
Обрыв связи. Место игрока держится 30 с: браузер сам просит у meta новый тикет и возвращается тем же бойцом. Если сервер закрыл комнату (пуста минуту, обновление), браузер показывает причину.
Рейтинговый бой. Очередь в meta: бой начинается сразу, когда набирается 6 игроков, или через 20 с после того, как в очереди появились двое. Deathmatch на случайной арене без ботов. Выбывший может вернуться за 30 с, иначе занимает последнее место; если в бою остался один игрок, через 20 с бой засчитывается. Рейтинг — многопользовательский Эло: каждый сравнивается с каждым по месту, K = 32 делится на число соперников, старт 1000.
npm install --legacy-peer-deps # на этом Mac npm спотыкается о peer-дерево
npm run dev:services # api :8090, meta :8091, game :8092; вход /auth/dev?name=… без Яндекса
npm run dev # Vite на :5173 проксирует /api, /auth, /meta, /game на сервисы
Секреты Яндекса для локального входа можно положить в apps/api/.env (в git не попадает):
YANDEX_CLIENT_ID=…
YANDEX_CLIENT_SECRET=…
YANDEX_REDIRECT=http://127.0.0.1:5173/callback
Сервер 157.228.137.18, Ubuntu 24.04, nginx и certbot, Node 20.19+ (на сервере 20.20). Хост общий с другими сайтами: скрипты трогают только свой server-блок nginx.
Разовая настройка (deploy/setup.sh, идемпотентна, переводит и со старой схемы):
YANDEX_CLIENT_ID=… YANDEX_CLIENT_SECRET=… bash /var/www/northecho/deploy/setup.sh
northecho;/etc/northecho/env (права 640): секреты Яндекса, SERVICE_SECRET
для тикетов и внутренних вызовов, ORIGINS, путь к базе;sqlite3 .backup) рядом с ней перед миграцией схемы;northecho-api, northecho-meta, northecho-game (вместе — northecho.target)
и northecho-deploy.timer, останавливает старый northecho.service;deploy/nginx.conf.Автодеплой. Раз в минуту northecho-deploy смотрит ветку (main; другая — в
/etc/northecho/deploy.env). Если там новый коммит:
releases/<sha> — живой релиз не трогается;npm ci и сборка (типы, сайт, три сервиса) там же;current одним rename и перезапускает только изменившиеся сервисы;
игровой сервер перед остановкой даёт идущим матчам доиграть (до 120 с);/healthz у api, meta и game. Не ответили — возвращает прошлый релиз, а коммит
записывает в releases/.failed и больше не пробует (до нового коммита или FORCE=1);nginx -t прошёл) и сам скрипт.Хранятся три последних релиза. Логи:
journalctl -u northecho-deploy -f # деплой
journalctl -u 'northecho-*' -f # сервисы
Ручной деплой ветки: BRANCH=имя FORCE=1 /usr/local/sbin/northecho-deploy.
UDP-порты 40000–40199 должны быть открыты во входящих, если у хостинга есть свой файрвол.
apps/
web/ сайт и игра: render (Three.js), ui, game.ts (клиент и соло), net (meta, game-сервер)
api/ вход, аккаунт, опыт, рейтинг, история; SQLite
meta/ комнаты, коды, очередь, реестр игровых серверов, тикеты
game/ комнаты и матчи на сервере (Room), транспорт WebSocket + WebRTC
packages/
shared/ детерминированная симуляция, протоколы, проверка ввода, правила прогресса
node-kit/ настройки, HTTP, тикеты, остановка — общее для сервисов
deploy/ nginx, systemd, setup.sh, deploy.sh
tests/ сервисы целиком без браузера
tools/ проверка битых символов, запуск сервисов для разработки
Физика и правила боя живут в плоскости XZ — как 2D, но в 3D-сцене. Это даёт детерминированную симуляцию и дешёвые расчёты; одна и та же симуляция работает в браузере и на сервере.
npm run typecheck # все пакеты и сервисы
npm run check:text # битые символы (U+FFFD) в исходниках — входит в сборку
npm run test:services # api + meta + game без браузера: комната по коду, лобби и старт,
# переподключение, чистка ввода, рейтинговый бой, опыт, остановка
npm run test:online # два браузера: комната, вход по коду без чёрного экрана, движение,
# предикт против сервера, старт хостом, переподключение
npm run test:server # рейтинговый бой в браузерах: очередь, бой по UDP, рейтинг
npm run test:arenas # арены и режимы на ботах, дрон, бонусы, прогресс (соло)
test:services поднимает сервисы сам (сначала npm run build). Браузерные тесты ходят на
запущенные npm run dev:services и npm run dev (адреса — переменные ONLINE_URL,
SERVER_TEST_URL, DM_URL).
Профиль кадров: node apps/web/tools/perf.mjs [арена] [режим] [секунды] (PERF_CPU=4
замедляет процессор вчетверо). Всё, что появляется в бою, прогревается в Renderer.warmup.
Vite · TypeScript · Three.js · Node.js (ws, node-datachannel, better-sqlite3) · npm workspaces · systemd · nginx.