- Препроцессор GMod: continue внутри for/while/repeat -> goto + уникальная метка; учтены строки/комментарии, вложенные циклы и функции, завершающие break/return (оборачиваются в do ... end). widget_bones.lua грузится: энтити 6 из 6. - include(): поиск рядом с текущим файлом и в lua/ игры, тот же препроцессор, лимиты размера/глубины, защита от циклов и выхода за каталог игры. - LuaRuntime::load_gamemode(): init.lua и cl_init.lua, сбор ошибок без падения. Sandbox init.lua выполняется, cl_init.lua упирается в VGUI (AccessorFunc); реестры list/scripted_ents после загрузки целы. - Безопасные заглушки загрузочного слоя Sandbox: CLIENT/SERVER, is*, FindMetaTable, cleanup, drive, duplicator, net, cvars, player_manager. - Тесты: юнит-тесты препроцессора, include на временных файлах и реальных lua/includes, загрузка sandbox на ассетах.
27 KiB
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-расширения).
Сверх-цели (из постановки задачи пользователя):
- Сандбокс-масштаб: оптимизация под много игроков / пропов / констрейнов.
- Современные технологии: wgpu/Vulkan, Jolt, SDL3, LuaJIT (через mlua), steamworks.
- Современный рендеринг (HDR, AA, SSAO, SSR, тени) — при строгой совместимости семантики материалов, иначе аддоны (PAC3/Starfall) визуально ломаются.
- Починка болячек GMod-клиента — см.
PROBLEMS.md. - Быстрые заходы: в игру и на сервер.
- Устойчивость к крашам от зловредных/кривых аддонов и серверов (Starfall, PAC3...).
- Полноценные анимации моделей игроков — главный видимый пробел 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. Принципы
- Совместимость — контракт. Форматы, сетевой протокол и Lua-семантика важнее «как удобнее написать код».
- Modern-by-default, compat-семантика всегда. Освещение и пост — современные;
render-state и семантика материалов (
$additive,$translucent,$alphatest, render targets, lightwarps, eyes, skinning) — ровно как в Source. - Принцип сандбокс-масштаба. Мелкое и далёкое можно вообще не рисовать и не
анимировать. Дефолты агрессивные, всё регулируется конварами. Детали —
PROBLEMS.md§P2. - Never-crash. Никакой ввод из сети/диска и никакой аддон не может уронить
клиент: bounded-парсеры, лимиты,
catch_unwindна границах, safe mode. - Speed-first. Быстрый заход в игру и на сервер — фича №1: параллельный декод, дисковые кэши, монтирование локального контента, pipeline cache.
- Data-oriented hot paths. ECS-подобное хранение сущностей, инстансинг, бюджетные очереди; без аллокаций в кадровом цикле.
- Clean-room. SDK 2013 / Source.NET / прочие референсы — спецификация, не код.
- 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):
- KeyValues: толерантный bounded-парсер (VMT/gameinfo/entities), лимиты, тесты
- BSP: заголовок + 64 лумпа, LZMA-лумпы, ENTITIES (KeyValues), TEXDATA/имена текстур, game lumps, PAKFILE (ZIP: Store/Deflate, CRC)
- Геометрия BSP v20: вершины/рёбра/surfedges/грани/texinfo/узлы/листья/модели,
validate()по перекрёстным ссылкам (gm_construct/gm_flatgrass — валидно) - VTF 7.0–7.5: заголовок (+таблица ресурсов 7.3+), раскладка мипов, миниатюра, декод в RGBA8 (DXT1/3/5, RGBA/BGRA/ABGR/ARGB, RGB/BGR, I8/A8/IA88, RGB565/BGR565)
- VMT: шейдер, параметры, patch (include/replace/insert), Proxies, render-modes
- MDL: заголовок (v44–49, studiohdr2), кости, аттачменты, хитбоксы, текстуры,
бодипарты, include-модели,
$keyvalues,surfaceprop(проверено на реальных моделях) - VVD/VTX: вершины (позиция/нормаль/UV/веса костей), fixup'ы, полное дерево BodyPart→Model→LOD→Mesh→StripGroup→Strip с валидацией индексов (51 пара на реальных моделях)
- CLI:
fs doctor,fs list,fs cat,fs inspect(BSP/VTF/VMT/MDL + VVD/VTX) - Тесты на реальных данных: 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):
crates/net: битовые буферы (Source LSB-порядок), A2S (info с challenge), netchan-заголовок (флаги, checksum CRC32→u16, challenge, choked), разбор net/SVC-сообщений (tick, signon, serverinfo, print, setconvar)- Хендшейк:
getchallenge→S2C.Challenge→connect(с GModChecksumXOR) →S2C.Connection; повтор getchallenge раз в секунду какCheckForResend - Steam-аутентификация (steamworks-rs): session ticket + steamid в connect; подключение к ClassicBox принято (VAC-сервер, auth_protocol=3)
- Версия клиента в connect берётся из A2S-версии сервера (иначе
#GameUI_ServerRejectNewVersion) - CLI:
net info <addr>,net connect <addr> - Подканалы/фрагменты надёжного потока:
NetChan(single/fragments, сжатие, varint, сборка потоков, разбор сообщений); GMod-флаги (Challenge=0x20, subchannel 4 бита) сверены с трафиком ClassicBox - 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):
engine::world: браш-грани, displacement'ы (ориентация по startPosition + порядок обхода, нормали наружу), лайтмапы в вершинах, VMT/VTF-материалы, pakfile карты, скайбокс 2D (skyname)render::WorldRenderer: батчи по материалам, depth, PNG-захват (BGRA-фикс), debug-материалы,--only-material,--dump-textures- Камера + ходьба:
engine::player— AABB против брашей (BSP v20) и displacement-треугольников (SAT + сетка), гравитация/прыжок/присед/noclip - Энтити-модели:
engine::model(MDL+VVD+VTX, bind pose), prop_static/ prop_dynamic/prop_physics в общий меш мира - 3D-скайбокс: выделение скайбокс-граней по area листа
sky_camera(disp-грани вleaf_facesне попадают — выбор по листу центра грани), сжатие 1/scale; 2D-скайбокс рисуется фоном, мир поверх - Вода:
SURF_WARP+ VMT шейдера Water ($abovewater/$fogcolor) — отдельный полупрозрачный проход с френелем (поверхность/изнанка) - Лайтмапы: блок грани
(size+1)²(индексv*width+u); для displacement'ов плотная сетка по UV поверхности (CalcDispSurfCoords), а не проекция мира - HUD: 2D-текст (bitmap 8x8,
render::text), FPS/позиция/режим/лог - Меню:
menu(выбор карты из VFS, Enter — запуск) - Куллинг/инстансинг, прозрачность (стекло), туман
- Атлас лайтмапов:
Rgba16Float2048², UV блока в вершине, билинейная выборка на пиксель (A10) - Анимации моделей (парсинг seqdesc/animdesc начат; форматы RLE/raw сверяются)
- Звук, частицы, декали, оверлеи
M7 — Lua: машинерия + HUD/UI (частично, 2026-10-12)
Сделано (v0):
glua: LuaJIT (mlua vendored), print/Msg, hook.Add/Remove/Call, timer.Simple, concommand.Add, CreateConVar/GetConVar, util/engine/game заглушки, FCVAR_*- Препроцессор GMod-синтаксиса:
!→not,!=→~=,&&→and,||→or,continue→goto+ метка в конце тела цикла (вне строк/комментариев) — оригинальные клиентские скрипты GMod (demo_recording.lua, gm_demo.lua, widget_bones.lua) грузятся include(): поиск рядом с текущим файлом и вlua/, тот же препроцессор, лимиты размера/глубины, защита от циклов и выхода за каталог игрыLuaRuntime::load_gamemode:gamemodes/<mode>/gamemode/init.luaиcl_init.lua, сбор ошибок без падения; Sandboxinit.luaвыполняется,cl_init.luaупирается в VGUI (AccessorFunc)- Автозагрузка
lua/autorun/client/*.luaиз VFS, Think-хук и таймеры в кадре, вывод в HUD-лог;lua run <file>,lua list - surface/draw/render-таргеты, Derma, Entity/Player API, серверный Lua
- Загрузка lua с сервера (download-кэш), safe mode
M3 — Рендер мира + interest-система (v1)
Сделано (v0, 2026-10-11/12):
crates/engine: сборка мира из BSP (браши, displacement'ы, лайтмапы в вершинах, материалы через VMT (patch/include), общий кэш текстур, пропуск прозрачных материалов)crates/render: wgpu-рендерер мира (один VB/IB, батчи по материалам, глубина), камера-полёт, скриншоты PNG (с учётом BGRA), debug-режимы (материалы, only-material)gmod-rs map <карта>: загрузка с диска/из VPK, монтирование pakfile карты, полёт WASD+мышь- Проверено: gm_construct (6838 граней + 110 disp, 41 324 треугольника, 47 материалов, 0 заглушек, 65 МиБ текстур, сборка ~0.5 с), gm_flatgrass
- Скайбокс: 6 граней из
skynameworldspawn (куб вокруг камеры, глубина на дальней плоскости) - Вода (refract/reflect), прозрачность (glass), туман, detail-спрайты
- HDR-конвейер: мир в
Rgba16Float, экспозиция, ACES, bloom (порог → размытие → аддитивно); UI по-прежнему в sRGB после тон-маппинга - Второй слой лайтмапы (+2-lightmap)
- 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
git init+.gitignore+ README + docs (первый коммит)- Cargo workspace
~/garrysmod-rs:crates/tier(логи/конвары/конфиг/CLI),crates/fs(VPK v1/v2, листинг, CRC-проверка) crates/math(базовый Vec3/Angle/Aabb)client: окно SDL3 + wgpu clear + FPS в заголовке и логе (проверено: NVIDIA RTX 3060, Vulkan, ~58–63 FPS, чистое завершение)- Тесты: реальные VPK из
~/gmod_server/serverпарсятся, CRC-выборка проходит; мусор не роняет парсер - 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.