131 lines
11 KiB
Markdown
131 lines
11 KiB
Markdown
# My World
|
||
|
||
Репо самого проекта Godot (все файлы проекта). Киберпанк-мир от первого лица с паркур-управлением
|
||
и простым мультиплеером: игроки, зашедшие на сайт, бегают вместе и видят друг друга.
|
||
|
||
- Движок: Godot **4.7.2**, рендерер **GL Compatibility** (нужен для Web), физика Jolt
|
||
- Платформа: Web (экспорт в WebAssembly), без потоков (`variant/thread_support=false`),
|
||
поэтому заголовки COOP/COEP браузеру не требуются, но на сервере они всё равно стоят
|
||
- Язык интерфейса и комментариев в коде: русский
|
||
|
||
## Структура проекта
|
||
|
||
```
|
||
main.tscn главная сцена (мир, окружение, свет, постройки, лес, птицы, игрок)
|
||
Player/ игрок от первого лица (CharacterBody3D) и всё паркур-управление
|
||
character_body_3d.gd ~800 строк: бег, рывок, слайд, бег по стенам, прыжки, подтягивание
|
||
World/ мир: forest.gd (лес на MultiMesh), birds.gd, levitate.gd, пол, небо (HDR), музыка
|
||
models/ .glb-модели: здания, башня, обелиск, деревья, Эмпайр-стейт, кибер-офис, скала
|
||
Net/ мультиплеер (см. ниже)
|
||
net.gd автолоад "Net": и клиент, и сервер (один скрипт)
|
||
ghost.gd чужой игрок: капсула, ник, сглаженное движение
|
||
server.tscn пустая сцена для запуска сервера
|
||
deploy/ примеры nginx-конфига, systemd-юнита, инструкция
|
||
export_presets.cfg пресет «Web», экспорт в ../grid-world/index.html
|
||
```
|
||
|
||
Соседние папки на компьютере разработчика (`/home/aibrash/Projects/My_World/`):
|
||
`my-world` (этот проект), `grid-world` (репо с готовым Web-экспортом, см. деплой), `Blender models`.
|
||
|
||
## Управление (в игре)
|
||
|
||
WASD бег, Shift спринт, Пробел прыжок (держать у уступа — подтянуться), Q рывок,
|
||
C слайд (на бегу, вместе с Shift), ЛКМ (зажать) зум, Esc отпускает мышь, клик по окну захватывает.
|
||
Бег по стене — в воздухе у стены с зажатым W на скорости.
|
||
|
||
## Мультиплеер
|
||
|
||
**Архитектура:** выделенный сервер на Godot (WebSocket), клиенты подключаются как `WebSocketMultiplayerPeer`.
|
||
Сервер ничего не считает и не проверяет, только пересылает состояния игроков остальным.
|
||
Мир, физика и управление работают локально у каждого. Игроки друг с другом не сталкиваются.
|
||
|
||
- `Net/net.gd` — автолоад `Net`. Если `DisplayServer.get_name() == "headless"` — это **сервер**
|
||
(порт **9080**, plain `ws://`), иначе **клиент**. Клиент переподключается каждые 3 с.
|
||
- Клиент шлёт 20 раз/с `submit_state(pos, yaw, pitch, flags)` (RPC на сервер, `unreliable_ordered`),
|
||
сервер пересылает остальным `recv_state(id, ...)`, при выходе — `recv_left(id)` (reliable).
|
||
- Флаги состояния (`Net.FLAG_*`): подкат, бег по стене, рывок, подтягивание.
|
||
Собираются в `Player/character_body_3d.gd` → `get_net_flags()`. Игрок находится в группе `local_player`.
|
||
- Клиент-призрак: `Net/ghost.gd` — капсула с цветом от id, «визор» показывает направление взгляда, ник «Игрок N».
|
||
- **Адрес сервера в клиенте:** в Web — из `window.location` (`wss://<домен>/ws` для https, `ws://<домен>/ws` для http);
|
||
на десктопе — `ws://127.0.0.1:9080` или аргумент `godot -- --server-url=wss://...`.
|
||
- **RPC требуют одинаковый путь узла и скрипт на сервере и клиенте.** Поэтому один скрипт `net.gd` в автолоаде.
|
||
Менять сетевые сообщения нужно синхронно: клиент (экспорт) + сервер (`/opt/my-world-server`), иначе игроки не увидят друг друга.
|
||
|
||
### Запуск локально
|
||
|
||
```bash
|
||
# сервер
|
||
godot --headless --path . res://Net/server.tscn
|
||
# клиент (можно запустить несколько окон)
|
||
godot --path . --windowed --resolution 960x540
|
||
```
|
||
Осторожность: `pkill -f` / `pgrep -f` с шаблоном `godot --headless` убивает и сам шелл,
|
||
потому что шаблон встречается в его командной строке. Находить процесс лучше через `ss -ltnp | grep 9080`.
|
||
|
||
## Инфраструктура и деплой
|
||
|
||
```
|
||
Браузер ──https/wss──▶ Nginx Proxy Manager (SSL, домен на duckdns) ──http──▶ контейнер 192.168.10.110
|
||
nginx :80
|
||
├─ / → /opt/grid-world (статика игры)
|
||
└─ /ws → 127.0.0.1:9080 (игровой сервер)
|
||
```
|
||
|
||
**Сервер:** `root@192.168.10.110`, Debian 12, контейнер LXC в Proxmox, 1 ядро, 1 ГБ RAM. Ключи ssh настроены.
|
||
Нет `curl`, `rsync`, `ufw`; есть `python3`, `perl`. Домен и SSL на Nginx Proxy Manager выше по сети; в его proxy host
|
||
должна быть включена опция **Websockets Support** (иначе `/ws` не работает; сейчас работает, проверено в браузере).
|
||
|
||
### Обновить клиент (изменился мир, модель, игровой код)
|
||
|
||
```bash
|
||
cd ~/Projects/My_World/my-world
|
||
godot --headless --path . --export-release Web ../grid-world/index.html
|
||
cd ../grid-world && git add -A && git commit -m "..." && git push origin main
|
||
```
|
||
Gitea (`git.ibrash.duckdns.org`, репо `aibrash/grid-world`) по пушу в `main` запускает workflow
|
||
`.gitea/workflows/deploy.yml`: заходит по ssh и делает `git pull` в `/opt/grid-world`. Экспорт при выходе
|
||
печатает безвредную ошибку `Parameter "singleton" is null`.
|
||
|
||
### Обновить игровой сервер (изменился `Net/*`)
|
||
|
||
Игровой сервер — отдельный минимальный проект в `/opt/my-world-server/` (`project.godot` + `Net/` + официальный
|
||
бинарник Godot 4.7.2 linux x86_64, файл `godot`). Бинарник из Fedora на Debian не подходит, взят официальный из
|
||
`github.com/godotengine/godot-builds` (релиз `4.7.2-stable`). Сервис systemd: `my-world.service`
|
||
(DynamicUser, `StateDirectory=my-world`, автозапуск, `Restart=always`).
|
||
|
||
```bash
|
||
scp Net/net.gd Net/ghost.gd Net/server.tscn root@192.168.10.110:/opt/my-world-server/Net/
|
||
ssh root@192.168.10.110 'systemctl restart my-world'
|
||
ssh root@192.168.10.110 'journalctl -u my-world -f' # лог: «+ игрок …» / «- игрок …»
|
||
```
|
||
При правках модели/мира серверную часть трогать не нужно. Игровой сервер слушает порт 9080 на всех интерфейсах
|
||
контейнера (наружу не проброшен). Можно сузить до `127.0.0.1` через `create_server(port, "127.0.0.1")`.
|
||
|
||
nginx контейнера: `/etc/nginx/sites-enabled/godot_game` (резервная копия до правок: `/root/godot_game.bak`);
|
||
в нём блок `location /ws` с заголовками `Upgrade`/`Connection`.
|
||
|
||
### Git
|
||
|
||
- Репо проекта: `git@git.ibrash.duckdns.org:aibrash/my-world.git`, ветка `master` (основная), используются
|
||
коммиты на русском с трейлером `Co-Authored-By`. Для экспериментов создаётся ветка `dev` (была слита и удалена).
|
||
- Пушить `master` и выкладывать на сайт — только по прямой просьбе владельца.
|
||
|
||
## Что выяснено по ходу работы (полезно не повторять)
|
||
|
||
- **Окружение (`main.tscn`):** в рендерере Compatibility **объёмный туман не работает** (ни кадр не меняется, ни память), поэтому он удалён.
|
||
**SSAO и glow работают**: glow даёт неоновое свечение, SSAO — затемнение в стыках; оба оставлены. Отключение всех трёх
|
||
экономило лишь около 4 МБ видеопамяти и убирало вид, поэтому это не причина высокой памяти.
|
||
- **Память:** вкладка в браузере показывала около **1,6 ГБ**, хотя на десктопе сцена занимает ~115 МБ (статика 83 МБ + видеопамять 30 МБ).
|
||
Причина не найдена. Нужны данные из Chrome (`Shift+Esc`: память / память GPU / JS-память и растёт ли цифра со временем).
|
||
Гипотезы: рост WASM-памяти и GPU-процесс Chrome, несжатые текстуры (для Web выключено `vram_texture_compression/for_mobile`),
|
||
HDR-небо 2k, звук `.ogg`, распаковываемый целиком, `dlink` (`extensions_support=true`, `index.side.wasm` 44 МБ).
|
||
- Пресет «Web» экспортирует в `../grid-world/index.html`. Шаблоны экспорта лежат в `~/.local/share/godot/export_templates/4.7.2.stable/`
|
||
(есть только Web; Linux-шаблонов нет, поэтому серверный бинарник — официальный редактор в режиме headless).
|
||
|
||
## Идеи на потом
|
||
|
||
- Скрипт `deploy.sh`: экспорт + пуш клиента + при необходимости обновление сервера одной командой.
|
||
- Ники по выбору игрока, текстовый чат, анимации персонажа вместо капсулы.
|
||
- Серверные проверки (антиспуфинг позиций), комнаты, если игроков станет много.
|
||
- Разобраться с потреблением памяти в браузере (см. выше).
|