gmod-rs/docs/ARCHITECTURE.md
Niko Marmeladkov 8d572b8190 Android: ландшафт, HUD над системными панелями, прыжок по тапу
- MainActivity фиксирует ландшафт (SDL перебивал манифест FULL_USER) и
  передаёт путь к ассетам через getExternalFilesDir;
- короткий тап по JUMP не теряется между кадрами (jump_pending);
- HUD сдвинут внутрь от статус-бара и нижней панели;
- логгер на Android собирается без неиспользуемой ветки.
2026-10-23 14:18:45 +03:00

14 KiB
Raw Permalink Blame History

Архитектура Source RS

1. Workspace

crates/
  tier        — tracing-логи, конвары, CommandLine, конфиг, время, пулы, interning
  math        — Vector/Angle (Source-семантика углов), Matrix, Quaternion, AABB, plane
  fs          — поисковые пути и порядок монтирования: dir, VPK v1/v2, GMA,
                BSP pakfile, mount.cfg, mmap, кэш индексов (rkyv)
  formats     — kv (KeyValues), bsp, vtf, vmt, mdl (+vvd/vtx/anim), pcf, phy, snd, demo
  net         — netchan (UDP, фрагментация, bz2), dt (send/recv tables), stringtable,
                usermessages, GMod-расширения, мост к Steam
  engine      — мир, entity-стор (архетипы/пулы), интерполяция, prediction, usercmd,
                interest-система (классификация, важность, бюджеты)
  render      — wgpu: device/swapchain, HDR-таргет, material system (compat-семантика +
                современный shading), шейдеры, инстансинг, RT-менеджер, post, pipeline cache
  studio      — StudioRender: mdl-инстансы, кости, blend-граф, IK, procedural bones, flex,
                LOD, аттачменты
  physics     — Jolt (C-API + свой безопасный слой), hibernation, prediction-мир, рагдоллы
  audio       — kira (микс) + symphonia (декод), пространственность, звуковые скрипты
  ui          — нативный UI: меню, консоль, шрифты, HUD-рамка, загрузочные экраны
  lua         — mlua (vendored LuaJIT), sandbox, рельсы API, мосты к engine/render/studio
  steam       — фичи steamworks-rs (тикет, UGC/workshop), отдельный steam-поток
  client      — бинарь `gmod-rs` и библиотека: сборка всего, главный цикл,
                конфиг, CLI; на Android — cdylib с `SDL_main` (см. `ANDROID.md`)

Правило зависимостей

Строго «вниз по списку»:

tier, math → fs, formats → net → engine → render, studio, physics, audio, ui → lua → client
  • lua и client вправе зависеть от всего (lua — движковый API, client — композиция).
  • Обратные зависимости запрещены; render может зависеть от studio (модели), engine — от net (типы снапшотов), но не наоборот.

2. Технологический стек и почему

Задача Выбор Почему
GPU wgpu (Vulkan на Linux) Современные фичи, кроссплатформенность, pipeline cache, compute
Окно/ввод SDL3 (при проблемах — winit) Геймпады, IME, зрелость, кроссплатформенность
Физика Jolt через C-API Де-факто замена VPhysics в современных Source-подобных; job-friendly
Lua mlua + vendored LuaJIT Семантика 5.1 как в GMod + производительность
Звук kira + symphonia Гибкий микс + декод mp3/ogg/wav
Steam steamworks-rs Тикет, UGC/workshop, браузер позже
Форматы Свои парсеры + bzip2, LZMA (raw LZMA1) Полный контроль, bounded, fuzzable
Кэши rkyv/bincode, mmap (memmap2) Быстрый старт, индекс VPK, обработанные ассеты
Логи tracing Каналы, фильтры, интеграция с dev-тулингом
Ошибки thiserror (крейты) + anyhow (границы приложения) Явные типы, удобный верхний уровень

3. Подсистемы

3.1 FileSystem (fs)

Порядок монтирования (уточняется в M1 по поведению GMod):

platform → sourceengine (hl2-база) → garrysmod_*.vpk → garrysmod/addons/*
→ workshop (по списку сервера, через Steam) → download-кэш (lua/контент с сервера)
→ BSP pakfile (мапа) → write-пути (наш конфиг/cache)
  • mount.cfg пользователя учитывается (~/gmod_server/server/garrysmod/cfg/mount.cfg).
  • Все открытия — через единый интерфейс с нормализованными путями (защита от traversal).
  • Индекс VPK кэшируется на диск (content-hash + версия парсера).

3.2 Сеть (net)

Конвейер:

UDP → netchan (сборка фрагментов, bz2) → потоки сообщений → signon/delta-декод
→ очередь «снапшот готов» → применение на главном потоке (тик интерполяции)
  • Никаких блокировок в горячих путях: net-поток кладёт пакеты в lock-free очередь, декод — в worker-пулах, применение — двойная буферизация.
  • Все ограничения входных данных — явные (размеры строк/массивов, глубина, количество энтити).
  • Dump/replay: net умеет писать/читать сырые потоки с метаданными (см. NETWORK.md).

3.3 Entity-система (engine)

  • Хранение — архетипами/пулами (SoA-ish), стабильные ID, без аллокаций на сущность в кадре.
  • Клиентские энтити — прокси для рендера/физики; Lua-фасад создаётся лениво, только если аддоны реально трогают сущность.
  • Интерполяция: буфер из 2–3 снапшотов, рендер с задержкой (как в Source MP).
  • Prediction: локальный игрок (movement по SDK), usercmd, реконсиляция.

3.4 Interest & Visibility (engine + render)

«Принцип сандбокс-масштаба» (детали — PROBLEMS.md §P2):

  • Состояния: Static → Sleeping → Dynamic → Impostor → Skipped (обратимо).
  • Важность: экранный размер, дистанция, движение, свежесть сети, взаимодействие.
  • Бюджеты: анимации (60/20/0 Гц), скиннинг, тени, партиклы, обновление костей.
  • Инстансинг повторяющихся моделей; merge спящих пропов в статические батчи.
  • Куллинг: PVS + frustum + octree, многопоточно; fallback без PVS.
  • Правило совместимости: куллинг не влияет на существование сущностей и на Lua-draw (PAC3/Starfall/HUD видят всё; у них свои бюджеты).

3.5 Рендер (render)

Два слоя:

  1. Compat-семантика — точный render state и поведение материалов Source: render modes ($additive, $translucent, $alphatest), RT, lightwarps, cubemaps, eyes, skinning, decals, halos. Контракт: аддоны не должны отличать нашу картинку от GMod.
  2. Современный shading — HDR-конвейер (RGBA16F), линейное освещение, tonemap, bloom, SSAO, SMAA (TAA опцией), SSR воды, динамические тени моделей (опцией), анизотропия.

Прочее: pipeline cache на диске; параллельная запись команд; shadow/RT-бюджеты; texture streaming и VRAM-бюджет (с M9).

Шейдеры по приоритету: LightmappedGeneric → VertexLitGeneric → UnlitGeneric → вода → спрайты → декали/эффекты → прочее. Аддонские DXBC — вне первой фазы.

3.6 StudioRender (studio)

  • Загрузка: MDL/VVD/VTX/ANIM, $keyvalues, bodygroups, аттачменты.
  • Анимация: blend-граф (секвенции/жесты/activity layers), IK, procedural bones ($bonefollowers), flex/eyeballs.
  • LOD: по важности; GPU-скиннинг в storage buffers; фриз костей для дальних.
  • Ragdoll-позы + клиентская физика (Jolt).

3.7 Потоки и конкурентность

  • Main — ввод, применение снапшотов, Lua (как в GMod), запись кадра.
  • Pool — куллинг, скиннинг-подготовка, декод ассетов, копирование данных на GPU.
  • Net — сокет и netchan; декод в отдельных worker'ах.
  • Physics — Jolt jobs (многоядерно), hibernation дальних островов.
  • Steam — отдельный поток: Steam API не потокобезопасен, все вызовы там; наружу — каналы.
  • Lua — главный поток (как в GMod); тяжёлые вычисления — только через явные API; watchdog следит за зависаниями.

3.8 Ассет-пайплайн и кэши

Каталог кэша (по умолчанию ~/.cache/source-rs, настраивается):

Кэш Содержимое Инвалидция
vpk-index/ Индексы VPK/GMA content-hash + версия парсера
textures/ Обработанные текстурные блобы+mips хэш исходника + версия
meshes/ Готовые vertex/index-буферы, импосторы хэш + версия
pipelines/ wgpu pipeline cache версия драйвера/шейдеров
lua-bc/ Байткод LuaJIT (опц.) хэш + версия LuaJIT
downloads/ Файлы, скачанные с серверов хэш (вечный кэш)

Цель: второй и последующие запуски — секунды; серверные файлы не качаются повторно.

3.9 Конфигурация

  • ~/.config/source-rs/config.toml (или --config): корни ассетов, каталог кэша, серверы, фичи.
  • Авто-детект ассетов: (1) явный путь; (2) ~/gmod_server/server; (3) Steam common.
  • Внутриигровые настройки — конвары (как в Source), сохраняются в cfg/.

3.10 Android

  • Тот же клиент собирается как cdylib и грузится штатной SDLActivity из SDL3; экспорт — SDL_main. Сборка APK — android/build.sh (aapt2 + d8, без Gradle), debug-подпись.
  • Целевые ABI: arm64-v8a (телефон) и x86_64 (эмулятор). minSdk 31, targetSdk 35, фиксированный ландшафт.
  • target.'cfg(target_os = "android")': без steamworks, Lua 5.4 вместо LuaJIT; логирование в logcat.
  • Ассеты — во внешнем каталоге приложения; касания (FingerDown/Motion/Up) переведены в те же wish_forward/right, прыжок и присед, что клавиатура.
  • Детали и ограничения — docs/ANDROID.md, команды установки — android/README.md.

4. Конвенции кода

  1. unwrap/expect/panic запрещены в путях обработки внешних данных (сеть/диск/Lua). Для внутренних инвариантов — debug_assert и явные ошибки.
  2. FFI-границы (Jolt, Steam, Lua) оборачиваются в безопасные слои; все входы проверяются.
  3. catch_unwind — на границах Lua/FFI/worker; паника одного аддона/задачи не убивает клиент.
  4. Без unsafe вне FFI-обёрток; FFI-крейты изолированы.
  5. Горячие пути — без аллокаций; interning строк; пулы.
  6. Ошибки — типизированные (thiserror) в крейтах, anyhow на верхнем уровне.
  7. Тесты — рядом с кодом; fuzz-таргеты для всех парсеров; бенчмарки для hot paths.

5. Бюджеты производительности (ориентиры для M9)

Стадия Бюджет (мс/кадр, RTX 3060, стресс-сцена)
Сеть/декод (не main) ≤ 2
Применение снапшотов ≤ 1
Interest/куллинг ≤ 1
Анимации (подготовка) ≤ 2
Запись рендер-команд ≤ 3
Main total ≤ 8 (цель 60+ FPS со запасом)

6. Схема кадра (упрощённо)

Input → Usercmd → Prediction(local)
Net queue → Snapshot apply → Entities → Interp states
Interest/Visibility → cull → instancing/static batches → anim LOD → render graph → present
Lua: Frame → HUD/Paint → Render hooks (в порядке как в GMod; sandbox)
Physics (client-side) → интерполяция/рагдоллы