- og:title/description/image + canonical + twitter:card в index.html,
нарисован assets/og.png (1200x630, тема сайта). CSP не трогали — картинка
того же происхождения.
- Переписан текст «обо мне» и формулировка про текущую работу в
Уралэнергоаква (убраны двусмысленности "неофициально возглавлял" и
"продолжаю искать"), Go убран из title/skills-тега как отдельная
"изучаю" пометка — теперь просто в стеке, раз есть реальный Go-код в
home_automatization.
- resume.js/projects.js больше не перерисовывают контент через JS при
языке по умолчанию (ru) — index.html/projects.html содержат готовый
серверный HTML, JS перерисовывает только при переключении на EN. Раньше
весь текст резюме подгружался пустым в #resume-root, curl без JS ничего
не видел.
- Добавлены кнопка «скачать резюме (PDF)» и ссылка на git.cactoz.su в
первый экран резюме.
- home_automatization на /projects честно помечен «в разработке» вместо
описания как готового продукта.
- Игра переименована из BattleCity (товарный знак Namco) в ./tanks,
убрана из основного меню во все страницы — ссылка теперь только в
подвале страницы whoami.
Новая шапка (sticky, blur, мигающий курсор), нумерованные секции резюме
с полным опытом работы и новым разделом «Образование», карточки
проектов в стиле drwxr-xr-x, более яркий дождь на фоне. Добавлен
i18n-модуль (frontend/js/i18n.js) с переключателем EN/RU: резюме и
проекты полностью переведены, в игре переведены статичные подписи
интерфейса лобби и боя (данные от сервера остаются на русском).
Клиент: ники других игроков рендерились через innerHTML без экранирования — XSS.
Добавлен escapeHtml() для nickname и в списке игроков комнаты.
Сервер:
- Санитайзинг ника и на бэкенде (обрезка длины, вырезание control/HTML
символов) — защита не только для этого клиента, но и для любых будущих
потребителей (логи, другие рендеры).
- onMessage обёрнут в try/catch: payload.value<T>() кидает json::type_error
на несовпадении типа поля — одно кривое сообщение могло уронить процесс
со всеми активными играми.
- Лимит на общее число комнат (200) — иначе клиент, спамящий
lobby.create_room, разгонял rooms_ до исчерпания памяти.
- Fix: одна из раскладок coop-карты клала укрытие поверх стартовой позиции
игрока — танк рождался внутри кирпича и не мог выехать. Тайл под каждым
стартовым танком теперь расчищается после построения карты.
Docker: gameserver теперь работает от непривилегированного пользователя,
а не от root.
nginx: rate-limiting на /ws (5 подключений/с на IP, до 20 одновременных) —
защита от флуда соединениями/комнатами; CSP и security-заголовки
(X-Content-Type-Options, X-Frame-Options, Referrer-Policy).
Клиент: audio.js синтезирует эффекты без внешних файлов (выстрел,
взрыв, разрушение кирпича, победа/поражение) — вписывается в
"чистый HTML/CSS/JS" стек. AudioContext разблокируется по клику
"готов" (браузеры блокируют автовоспроизведение без жеста).
Сервер: 3 варианта раскладки coop-карты (случайный выбор при старте
матча) вместо одной фиксированной.
Fix: сервер разрушал кирпичи в своей копии карты, но никогда не сообщал
об этом клиенту — стена оставалась видимой навсегда, хотя сквозь неё уже
можно было стрелять. Теперь game.state включает destroyed_tiles,
клиент убирает соответствующий спрайт стены.
Сервер (Game.hpp/cpp):
- coop-режим: AI-противники (волна 8, до 3 одновременно), простое
преследование базы с рандомизацией, база с укрытием из кирпича.
- Комната стартует соло (1 игрок), второй может подключиться позже.
- Fix: хитбокс пули по базе брал радиус танка вместо пули.
- Fix: AI при столкновении со стеной теперь сразу перерешает направление,
а не долбит в одну точку до 3с — без этого база падала за секунды.
Клиент:
- Танк: гусеницы, башня, заметный ствол (было — незаметный нубик).
- Рендер базы (укрытие/флаг), AI-танки — отдельным оттенком зелёного.
- Экран победы/поражения — крупный glitch-текст на весь оверлей вместо
строки текста; кнопки "играть ещё" (без reload) и "в лобби".
- Блок кнопок управления (D-pad + огонь) справа от игрового поля.
- Лобби: coop стартует по кнопке "готов" в одиночку.
nginx: try_files с fallback на .html — чистые URL (/projects, /game)
без расширения, старые /projects.html и т.п. продолжают работать.
В pvp-комнате с одним игроком кнопка "готов" ничего не делала (сервер
не стартует матч без 2 игроков) без единого объяснения. Теперь кнопка
задизейблена и показывается статус, пока не зайдёт второй игрок.
Сервер: Game — authoritative-тик 20 Гц через uSockets timer, движение/коллизии
танков и пуль, разрушаемые кирпичные стены, respawn, победа/поражение/ничья,
технический проигрыш при дисконнекте. game.ready запускает матч, когда оба
игрока в pvp-комнате готовы.
Клиент: заведён Quintus.js (MIT, vendored) — Tank/Bullet/Wall спрайты,
рендер по снапшотам game.state, keyboard-инпут (WASD/стрелки/space),
экран результата матча.
Найден и исправлен use-after-free: колбэк onFinished_ синхронно удалял
Game изнутри его же метода — перенесено на uWS::Loop::defer.