Files
g-world/docs/ARCHITECTURE.md

96 lines
6.2 KiB
Markdown

# Архитектура
Документ описывает, как устроен проект: какие есть слои, кто кому имеет право
звонить и как данные ходят по системе. Структура папок — прямое отражение этих
слоёв, см. [README](../README.md#структура-репозитория).
## Слои и правила зависимостей
Стрелка «→» читается как «может знать о». Всё, что не указано, — запрещено.
```
main ──→ autoload ──→ core
├───→ ui ────────→ core, data
├───→ world ─────→ core, data, entities
├───→ systems ───→ core, data
├───→ entities ──→ core, data
└───→ debug ─────→ (всё, только для разработки)
```
| Слой | Зачем | Чего в нём быть не должно |
| --- | --- | --- |
| `core` | Утилиты, базовые типы, математика сетки | Ссылок на конкретные сцены и геймплей |
| `data` | Описания контента: схемы (`Resource`) и их экземпляры (`.tres`) | Логики поведения — только данные |
| `systems` | Симуляция мира: время, климат, погода, экономика | Прямого доступа к нодам сцены |
| `world` | Визуализация и взаимодействие с убежищем | Правил симуляции — они в `systems` |
| `entities` | Жители, предметы; их состояние и поведение | Знания о конкретном UI |
| `ui` | Экраны, HUD, контролы | Изменения состояния мира напрямую |
| `debug` | Дев-консоль, оверлеи, читы | Кода, от которого зависит релизная логика |
Ключевое правило: **`ui` и `world` не меняют состояние мира сами** — они шлют
намерение (сигнал/вызов системы), а изменение делает `systems`. Обратно
`systems` не дёргает ноды: он сообщает об изменении сигналом.
## Данные и рендер разделены
Симуляция не должна зависеть от того, нарисовано ли что-то на экране.
- **Данные** (`systems`, `data`) — чистая логика: считаются от `delta`, полностью
сериализуемы, тестируются без дерева сцены.
- **Рендер** (`world`, `ui`) — читает данные и отображает, подписавшись на сигналы.
Практический критерий: любой класс из `systems` должен работать в юнит-тесте,
где никакой сцены нет.
## Обмен сообщениями
Три способа, в порядке предпочтения:
1. **Сигнал вверх, вызов вниз.** Родитель знает о детях и вызывает их методы;
ребёнок о родителе не знает и только испускает сигналы.
2. **Ссылка на систему**, полученную из автозагрузки — когда нужен ответ здесь и
сейчас (запрос состояния, валидация постройки).
3. **Глобальная шина (`EventBus` в `autoload`)** — только для событий, которые
слушают несколько несвязанных слоёв (например, «время перескочило»,
«погода сменилась»). Не превращать её в свалку: каждый сигнал документируем.
## Контент — это ресурсы, а не код
Новая комната, предмет или тип породы добавляются как `.tres` в `data/tables/`
по схеме из `data/resources/`, без правки кода. Если для добавления контента
приходится менять `.gd` — схема неполная, чинить надо схему.
## Автозагрузки
Автозагрузка — дорогая вещь: она живёт всю сессию и её видно отовсюду. Заводим
только для того, что действительно глобально и одно на игру. Каждая новая
автозагрузка регистрируется через *Project Settings → Autoload* и упоминается
здесь.
Сейчас в проекте:
| Автозагрузка | Что делает |
| --- | --- |
| `EventBus` (`src/autoload/event_bus.gd`) | Глобальная шина событий, пока пустая |
| `_mcp_game_helper` | Служебная, часть плагина `godot_ai`; к игре отношения не имеет |
## Точка входа
`src/main/main.tscn` — главная сцена проекта:
```
Main (Node2D) # main.gd: только сборка, никаких игровых правил
├── World (Node2D) # убежище: порода, сетка, комнаты, жители
└── UI (CanvasLayer) # интерфейс поверх мира, не зависит от камеры
```
`World` и `UI` разделены слоями сразу, чтобы интерфейс не ездил вместе с
камерой убежища и не масштабировался вместе с зумом.
## Решения
Существенные развилки (выбор подхода к сохранению, к сетке, к тайм-степу)
фиксируем как ADR в [`adr/`](adr/) — короткая заметка «контекст → решение →
последствия». Это дешевле, чем через полгода восстанавливать мотивацию по коду.