- MainActivity фиксирует ландшафт (SDL перебивал манифест FULL_USER) и передаёт путь к ассетам через getExternalFilesDir; - короткий тап по JUMP не теряется между кадрами (jump_pending); - HUD сдвинут внутрь от статус-бара и нижней панели; - логгер на Android собирается без неиспользуемой ветки.
205 lines
14 KiB
Markdown
205 lines
14 KiB
Markdown
# Архитектура 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) → интерполяция/рагдоллы
|
||
```
|