# Source RS — план проекта **Статус:** план зафиксирован (2026-10-11). Следующий шаг — спринт 0 (§7). **Каталог:** `~/garrysmod-rs` · **Цель №1:** ClassicBox (`194.147.90.27:24215`). --- ## 1. Видение Source RS — клиент Garry's Mod, написанный с нуля на Rust. Цель — комфортно и красиво играть на сандбокс-серверах с десятками игроков, тысячами сущностей (пропы, констрейны, эффекты) и кучей аддонов, оставаясь совместимым с реальными серверами эпохи Source 2013 (net protocol 24 + GMod-расширения). Сверх-цели (из постановки задачи пользователя): 1. **Сандбокс-масштаб**: оптимизация под много игроков / пропов / констрейнов. 2. **Современные технологии**: wgpu/Vulkan, Jolt, SDL3, LuaJIT (через mlua), steamworks. 3. **Современный рендеринг** (HDR, AA, SSAO, SSR, тени) — при строгой совместимости семантики материалов, иначе аддоны (PAC3/Starfall) визуально ломаются. 4. **Починка болячек** GMod-клиента — см. `PROBLEMS.md`. 5. **Быстрые заходы**: в игру и на сервер. 6. **Устойчивость к крашам** от зловредных/кривых аддонов и серверов (Starfall, PAC3...). 7. **Полноценные анимации** моделей игроков — главный видимый пробел Source.NET. ## 2. Рамки ### В рамках первой фазы - Клиент, подключающийся к реальным GMod-серверам (Source 2013 protocol 24 + GMod-расширения). - Рендер GMod-контента: BSP, модели, материалы, партиклы, декали, звук. - Полный анимационный стек студийных моделей (скелеты, IK, procedural bones, flex, blends). - Клиентский Lua (LuaJIT) с движковым API в объёме, достаточном для аддонов (порядок и объём — см. `LUA.md`). - Steam: тикет для серверов с VAC, workshop-контент, монтирование. - Современный рендер по умолчанию + строгий compat-слой семантики материалов. ### Вне рамок (первая фаза) - Свой dedicated-сервер и серверный Lua (геймоды, ULX, DarkRP...) — отдельная вселенная. - Нативные бинарные Lua-модули аддонов (`.dll`/`.so` внутри addons) — несовместимы by design. - Перераспространение ассетов и Lua-файлов Garry's Mod / Valve. - Windows-сборка (код кроссплатформенный, сборка и CI — позже). - HTML/Chromium-панели (небольшая часть дюжины аддонов). ## 3. Принципы 1. **Совместимость — контракт.** Форматы, сетевой протокол и Lua-семантика важнее «как удобнее написать код». 2. **Modern-by-default, compat-семантика всегда.** Освещение и пост — современные; render-state и семантика материалов (`$additive`, `$translucent`, `$alphatest`, render targets, lightwarps, eyes, skinning) — ровно как в Source. 3. **Принцип сандбокс-масштаба.** Мелкое и далёкое можно вообще не рисовать и не анимировать. Дефолты агрессивные, всё регулируется конварами. Детали — `PROBLEMS.md` §P2. 4. **Never-crash.** Никакой ввод из сети/диска и никакой аддон не может уронить клиент: bounded-парсеры, лимиты, `catch_unwind` на границах, safe mode. 5. **Speed-first.** Быстрый заход в игру и на сервер — фича №1: параллельный декод, дисковые кэши, монтирование локального контента, pipeline cache. 6. **Data-oriented hot paths.** ECS-подобное хранение сущностей, инстансинг, бюджетные очереди; без аллокаций в кадровом цикле. 7. **Clean-room.** SDK 2013 / Source.NET / прочие референсы — спецификация, не код. 8. **Linux-first.** wgpu/SDL3/Jolt кроссплатформенные, но отладка и CI — Linux. ## 4. Критерии успеха | Рубеж | Критерий | |---|---| | **M4 «Играбельно»** | Заход на ClassicBox, видим 36+ игроков, ходим, стабильный FPS | | **M6 «Как надо»** | Играем: движение, оружие, звук, HUD | | **M6+ «Сандбокс-масштаб»** | 64 игрока + 2000 пропов + 500 констрейнов → 60+ FPS на RTX 3060; карта без PVS не убивает кадр | | **M8 «Аддоны»** | HUD-аддоны, Derma, PAC3, Starfall работают; их сбои не крашат клиент | | **Итог** | Заходы заметно быстрее GMod; картинка современнее; ноль киллов клиента на Chaos-тестах | ## 5. Этапы (обзор) | Этап | Название | Главный результат | |---|---|---| | M0 | Каркас | Workspace, tier-слой, конфиг ассетов, окно+wгpu «пустое» | | M1 | Форматы и FS | VPK/GMA/BSP/VTF/VMT/MDL/PCF/PHY/SND парсятся, fuzz-тесты | | M2 | Сеть + Steam + dump/replay | Подключение к серверу, лог мира, Steam-тикет, workshop, replay-стенд | | M3 | Рендер мира + interest v1 | BSP-мир «как в GMod», современный конвейер, куллинг/инстансинг, pipeline cache | | M4 | Онлайн: мир и игроки | ClassicBox онлайн: 36 игроков, интерполяция, смена карт; стресс-полигон 2000 пропов | | M5 | StudioRender + анимации | Полноценные анимации игроков/рагов, animation LOD, статик-батчи | | M6 | Геймплей-клиент | Prediction, оружие, звук, физика/Jolt, база HUD | | M7 | Lua: машинерия + HUD/UI | LuaJIT-рантайм, sandbox, safe mode, серверные HUD-аддоны | | M8 | Derma → PAC3 → Starfall | Меню, PAC3-модели, Starfall-экраны, стойкость к Chaos | | M9 | Перф, кэши, полировка | Texture streaming, дисковые кэши, импосторы, тюнинг, бенчмарки CI | ## 6. Детализация этапов ### M0 — Каркас - Cargo workspace; `crates/tier`: `tracing`-логи, конвары, CommandLine, конфиг, время, interning строк. - Авто-детект корня ассетов: настроенный путь → `~/gmod_server/server` (DS) → Steam-инсталлы. - `.gitignore`, CI-скелет (fmt/clippy/test), README/docs, `AGENTS.md`. - `steamworks` в зависимостях (feature на будущее, выключен по умолчанию до M2). - Приёмка: `cargo run -p client` открывает окно SDL3 + wgpu (clear), в логе — найденные пути ассетов и список конваров. ### M1 — Форматы и файловая система **Сделано (2026-10-11/12):** - [x] KeyValues: толерантный bounded-парсер (VMT/gameinfo/entities), лимиты, тесты - [x] BSP: заголовок + 64 лумпа, LZMA-лумпы, ENTITIES (KeyValues), TEXDATA/имена текстур, game lumps, PAKFILE (ZIP: Store/Deflate, CRC) - [x] Геометрия BSP v20: вершины/рёбра/surfedges/грани/texinfo/узлы/листья/модели, `validate()` по перекрёстным ссылкам (gm_construct/gm_flatgrass — валидно) - [x] VTF 7.0–7.5: заголовок (+таблица ресурсов 7.3+), раскладка мипов, миниатюра, декод в RGBA8 (DXT1/3/5, RGBA/BGRA/ABGR/ARGB, RGB/BGR, I8/A8/IA88, RGB565/BGR565) - [x] VMT: шейдер, параметры, patch (include/replace/insert), Proxies, render-modes - [x] MDL: заголовок (v44–49, studiohdr2), кости, аттачменты, хитбоксы, текстуры, бодипарты, include-модели, `$keyvalues`, `surfaceprop` (проверено на реальных моделях) - [x] VVD/VTX: вершины (позиция/нормаль/UV/веса костей), fixup'ы, полное дерево BodyPart→Model→LOD→Mesh→StripGroup→Strip с валидацией индексов (51 пара на реальных моделях) - [x] CLI: `fs doctor`, `fs list`, `fs cat`, `fs inspect` (BSP/VTF/VMT/MDL + VVD/VTX) - [x] Тесты на реальных данных: 2 карты, 200 VTF, 200 VMT, 53 MDL, 51 пара VVD/VTX, pakfile 599 записей - [ ] MDL: ANIM (анимации) — следующая итерация - [ ] PCF, PHY (collision), WAV/MP3/OGG - [ ] `crates/fs`: GMA (workshop), `mount.cfg`, aliases `|all_source_engine_paths|`, mmap, кэш индексов VPK - [ ] fuzz-таргеты на kv/bsp/vtf/pakfile (пока есть VPK) Приёмка (частично достигнута): `fs doctor`/`fs inspect` работают на реальных ассетах. ### M2 — Сеть + Steam + dump/replay **Сделано (v0, 2026-10-12):** - [x] `crates/net`: битовые буферы (Source LSB-порядок), A2S (info с challenge), netchan-заголовок (флаги, checksum CRC32→u16, challenge, choked), разбор net/SVC-сообщений (tick, signon, serverinfo, print, setconvar) - [x] Хендшейк: `getchallenge` → `S2C.Challenge` → `connect` (с GMod `ChecksumXOR`) → `S2C.Connection`; повтор getchallenge раз в секунду как `CheckForResend` - [x] **Steam-аутентификация** (steamworks-rs): session ticket + steamid в connect; **подключение к ClassicBox принято** (VAC-сервер, auth_protocol=3) - [x] Версия клиента в connect берётся из A2S-версии сервера (иначе `#GameUI_ServerRejectNewVersion`) - [x] CLI: `net info `, `net connect ` - [x] Подканалы/фрагменты надёжного потока: `NetChan` (single/fragments, сжатие, varint, сборка потоков, разбор сообщений); GMod-флаги (Challenge=0x20, subchannel 4 бита) сверены с трафиком ClassicBox - [x] Steam-тикет: клиент живёт всю сессию, ждём `AuthSessionTicketResponse`, свежий тикет при повторных connect (challenge одноразовый) - [ ] **Нерешённое**: сервер после `S2C.Connection` шлёт challenge-пакеты и через ~1.5 с отключает (`NET_Disconnect: Client dropped by server`); кик не зависит от наших пакетов (подробности и все проверенные гипотезы — в docs/RECON.md §11) - [ ] Полный signon: stringtables, datatables, baselines, снапшоты - [ ] Packet dump/replay стенд - [ ] Workshop через ISteamUGC (подписка/скачивание/монтирование) Приёмка v0 достигнута: хендшейк и Steam-вход работают на живом ClassicBox (до кика сервером; причина кика исследуется). ### M3 — Рендер мира + interest-система (v1) **Сделано (v0/v1, 2026-10-11/12):** - [x] `engine::world`: браш-грани, displacement'ы (ориентация по startPosition + порядок обхода, нормали наружу), лайтмапы в вершинах, VMT/VTF-материалы, pakfile карты, скайбокс 2D (`skyname`) - [x] `render::WorldRenderer`: батчи по материалам, depth, PNG-захват (BGRA-фикс), debug-материалы, `--only-material`, `--dump-textures` - [x] Камера + ходьба: `engine::player` — AABB против брашей (BSP v20) и displacement-треугольников (SAT + сетка), гравитация/прыжок/присед/noclip - [x] Энтити-модели: `engine::model` (MDL+VVD+VTX, bind pose), prop_static/ prop_dynamic/prop_physics в общий меш мира - [x] 3D-скайбокс: выделение скайбокс-граней по area листа `sky_camera` (disp-грани в `leaf_faces` не попадают — выбор по листу центра грани), сжатие 1/scale; 2D-скайбокс рисуется фоном, мир поверх - [x] Вода: `SURF_WARP` + VMT шейдера Water (`$abovewater`/`$fogcolor`) — отдельный полупрозрачный проход с френелем (поверхность/изнанка) - [x] Лайтмапы: блок грани `(size+1)²` (индекс `v*width+u`); для displacement'ов плотная сетка по UV поверхности (`CalcDispSurfCoords`), а не проекция мира - [x] HUD: 2D-текст (bitmap 8x8, `render::text`), FPS/позиция/режим/лог - [x] Меню: `menu` (выбор карты из VFS, Enter — запуск) - [ ] Куллинг/инстансинг, атлас лайтмапов, прозрачность (стекло), туман - [ ] Анимации моделей (парсинг seqdesc/animdesc начат; форматы RLE/raw сверяются) - [ ] Звук, частицы, декали, оверлеи ### M7 — Lua: машинерия + HUD/UI (частично, 2026-10-12) **Сделано (v0):** - [x] `glua`: LuaJIT (mlua vendored), print/Msg, hook.Add/Remove/Call, timer.Simple, concommand.Add, CreateConVar/GetConVar, util/engine/game заглушки, FCVAR_* - [x] Препроцессор GMod-синтаксиса: `!` → `not`, `!=` → `~=`, `&&` → `and`, `||` → `or` (вне строк/комментариев) — оригинальные клиентские скрипты GMod (demo_recording.lua, gm_demo.lua) грузятся - [x] Автозагрузка `lua/autorun/client/*.lua` из VFS, Think-хук и таймеры в кадре, вывод в HUD-лог; `lua run `, `lua list` - [ ] surface/draw/render-таргеты, Derma, Entity/Player API, серверный Lua - [ ] Загрузка lua с сервера (download-кэш), safe mode ### M3 — Рендер мира + interest-система (v1) **Сделано (v0, 2026-10-11/12):** - [x] `crates/engine`: сборка мира из BSP (браши, displacement'ы, лайтмапы в вершинах, материалы через VMT (patch/include), общий кэш текстур, пропуск прозрачных материалов) - [x] `crates/render`: wgpu-рендерер мира (один VB/IB, батчи по материалам, глубина), камера-полёт, скриншоты PNG (с учётом BGRA), debug-режимы (материалы, only-material) - [x] `gmod-rs map <карта>`: загрузка с диска/из VPK, монтирование pakfile карты, полёт WASD+мышь - [x] Проверено: gm_construct (6838 граней + 110 disp, 41 324 треугольника, 47 материалов, 0 заглушек, 65 МиБ текстур, сборка ~0.5 с), gm_flatgrass - [x] Скайбокс: 6 граней из `skyname` worldspawn (куб вокруг камеры, глубина на дальней плоскости) - [ ] Вода (refract/reflect), прозрачность (glass), туман, detail-спрайты - [ ] Атлас лайтмапов (+2-lightmap), HDR-конвейер (RGBA16F, tonemap, bloom) - [ ] Compat-шейдеры: LightmappedGeneric (полный), VertexLitGeneric, UnlitGeneric, вода - [ ] Interest-система: PVS + frustum + octree, screen-size пороги, инстансинг, куллинг - [ ] Pipeline cache на диске Приёмка v0 достигнута: оффлайн-полёт по gm_construct/gm_flatgrass с текстурами и лайтмапами. ### M4 — Онлайн: мир и игроки - Применение снапшотов к сцене: модели/позиции/углы, интерполяция; скины, аттачменты. - Игроки как модели (пока без анимаций). - Смена карт, загрузочный экран, реконнект, базовые уведомления. - Стресс-полигон: локальный srcds + скрипт-спавнер (2000 пропов, 500 констрейнов); replay busy-сессий ClassicBox. - Приёмка: заход на ClassicBox; видим 36 игроков; стресс-полигон держит бюджет кадра (v1 — без анимаций и полной физики). ### M5 — StudioRender + анимации (главный фикс Source.NET) - Полный скелетный пайплайн: VVD/VTX/ANIM, blend-граф, `$sequence`, activity layers, gestures. - IK-цепочки, procedural bones (`$bonefollowers`), flex/eyeballs, аттачменты, bodygroups. - GPU-скиннинг (storage buffers), morph targets. - Animation LOD (60/20/0 Гц по важности), статик-батчи спящих моделей, начало авто-импосторов. - Приёмка: игроки ходят/прыгают/приседают/держат оружие; ragdoll-позы; анимации не бьют по FPS при 64 игроках. ### M6 — Геймплей-клиент - Prediction движения игрока (по SDK), usercmd, viewmodel, оружие, прицелы. - Звук: kira + WAV/MP3/OGG, пространственность, звуковые скрипты, шаги/выстрелы. - Физика: Jolt, hibernation островов, интерполяция физики, рагдоллы; визуальные ропы/бимы/эффекты констрейнов. - HUD-рамка: консоль, чат, уведомления (до Lua-аддонов — базовое). - Приёмка: полноценная игра на ClassicBox; 60+ FPS в замесе. ### M7 — Lua: машинерия + HUD/UI - mlua + vendored LuaJIT (семантика 5.1, как в GMod); загрузка lua с сервера из download-кэша. - Bootstrap геймода как в GMod; ядро API: `hook`, `net`, `timer`, `util`, `concommand`, `surface`, `draw`, `render`, `cam`, `input`; render targets. - Sandbox v1: лимиты памяти, instruction-watchdog, запреты (`io/os/ffi/debug`), изоляция ошибок; **safe mode** — чёрный список упавшего файла при следующем запуске. - Инструмент `lua-api-report`: скан дампов (gluasteal) → рейтинг недостающих API. - Приёмка: серверные HUD-аддоны (HP/чат/killfeed) работают; сбоящий аддон не роняет клиент и отключается при следующем запуске. ### M8 — Derma → PAC3 → Starfall - Derma/VGUI API: панели, layout, шрифты (TTF), анимации; «достаточно для типовых меню». - PAC3: compat API (кости, RT, материалы, вложения), бюджеты, safe mode. - Starfall: поддержка API, который использует аддон (он тащит свой Lua-интерпретатор внутрь GMod Lua), усиленный sandbox, лимиты. - Приёмка: F1-меню сервера открывается; PAC3-модели видны и анимируются; Starfall-экраны рисуются; Chaos-тесты (бесконечные циклы, гигантские меши/RT, битые данные) не крашат клиент. ### M9 — Перф, кэши, полировка - Texture streaming и VRAM-бюджет; дисковые кэши: VPK-индексы, обработанные текстуры/меши, импосторы, pipeline cache; инвалидация по content-hash + версии парсера. - Авто-импосторы и merge спящих пропов с дисковым кэшем. - Тюнинг конваров interest-системы; бенчмарки-регрессии в CI. - Опции рендера: SSAO, SMAA (TAA опцией), SSR воды, динамические тени пропов/игроков; дефолты. - Приёмка: второй запуск и заход — секунды; страница бенчмарков; гайд по конварам. ## 7. Спринт 0 - [x] `git init` + `.gitignore` + README + docs (первый коммит) - [x] Cargo workspace `~/garrysmod-rs`: `crates/tier` (логи/конвары/конфиг/CLI), `crates/fs` (VPK v1/v2, листинг, CRC-проверка) - [x] `crates/math` (базовый Vec3/Angle/Aabb) - [x] `client`: окно SDL3 + wgpu clear + FPS в заголовке и логе (проверено: NVIDIA RTX 3060, Vulkan, ~58–63 FPS, чистое завершение) - [x] Тесты: реальные VPK из `~/gmod_server/server` парсятся, CRC-выборка проходит; мусор не роняет парсер - [x] CI-скелет (fmt/clippy/test для tier/math/fs), fuzz-заглушка (`fuzz/fuzz_targets/vpk_parse.rs`) **Спринт 0 завершён (2026-10-11):** `fs doctor` — 9 VPK, 77 933 записи, 41 архив, 0 ошибок CRC; `fs cat` читает MDL/текст из VPK; окно открывается на Vulkan. ## 8. Тестовая стратегия - **Локальный srcds** (`~/gmod_server/server`) — контролируемые сценарии (sv_lan). - **Стресс-полигон**: консольный спавнер на srcds + replay: 2000 пропов, 500 констрейнов, боты/игроки; отдельный тест «карта без PVS». - **Packet dump/replay**: клиент пишет сырые сетевые потоки; replay в декодер без сервера; регрессии и стресс-сцены. - **Fuzz**: VPK/BSP/MDL/VTF/PCF/снапшоты — cargo-fuzz, ночные прогоны. - **Chaos-тесты**: зловредный Lua (бесконечный цикл, гигабайтные меши, битые RT, path traversal), кривые снапшоты, огромные строки — клиент обязан выживать. - **Бенчмарки**: сцены стресс-полигона, регрессии кадра, логи в CI. ## 9. Риски | Риск | Митигация | |---|---| | GMod-специфика протокола недокументирована | Референс Source.NET/leysourceengineclient/SDK, packet capture, replay-стенд; изоляция слоя net | | Огромный объём Lua API | Рельсы + приоритеты по аддонам; `lua-api-report` по дампам gluasteal | | Steam-лицензия/клиент на машине не установлены (только заглушка) | Проверка в M2; fallback noSteam; при необходимости установить Steam+лицензию | | 1:1 Derma сложен | Сначала «достаточно для типовых меню», итерации по реальным аддонам | | Производительность при 64 игроках | interest-система с M3, бенчмарки с M4, animation LOD с M5, физика-хайбернейт с M6 | | Юридические ограничения SDK | Код — свой; SDK/референсы только как спека; ассеты — локально | | Нет реального клиента GMod для captures | DS-ассеты + srcds; при появлении клиента (Proton) — tcpdump; иначе дампы нашего клиента | ## 10. Зафиксированные решения | # | Решение | Статус | |---|---|---| | D1 | Каталог проекта — `~/garrysmod-rs` | принято | | D2 | Первая фаза — только клиент (сервер — потом) | принято | | D3 | Linux-first, x86_64, wgpu/Vulkan | принято | | D4 | Lua: все 4 цели, порядок HUD/UI → Derma → PAC3 → Starfall | принято | | D5 | Рендер: современный по умолчанию; семантика материалов Source — контракт | принято | | D6 | Steam (`steamworks-rs`) с M2; noSteam fallback конваром | принято | | D7 | Стек: wgpu, SDL3, Jolt, mlua (LuaJIT), kira/symphonia, steamworks | принято | | D8 | Принцип сандбокс-масштаба: агрессивные дефолты, всё конварами | подтверждено | ## 11. Процесс - Каждый этап заканчивается демо + бенчмарком + обновлением `docs/`. - Коммиты — по этапам/подсистемам, сообщения на русском. - Отклонения от плана фиксируются в §10 и в `PROBLEMS.md`.