Files
my-world/README.md
T

131 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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`: экспорт + пуш клиента + при необходимости обновление сервера одной командой.
- Ники по выбору игрока, текстовый чат, анимации персонажа вместо капсулы.
- Серверные проверки (антиспуфинг позиций), комнаты, если игроков станет много.
- Разобраться с потреблением памяти в браузере (см. выше).