Compare commits

..
3 Commits
Author SHA1 Message Date
aibrashandClaude Sonnet 5.5 fc0139dc05 Структура проекта: папки по назначению, карты самодостаточны, инструменты, README
- Перенос: Net→net, Player→player, World/*→maps/{hub,sandbox,backrooms}, core, objects,
  models/*→chasers/{rock,verity} и maps/*/models. Имена внутри папок с .glb не менялись
  (рядом лежат извлечённые текстуры). Все пути, пресеты экспорта и автолоады обновлены.
- Преследователи: rock-follow.gd → chasers/chaser.gd; камень в песочнице теперь инстанс
  chasers/rock/rock.tscn (как Верити).
- Удалён мусор: неиспользуемое небо sunflowers, Tree_01.glb, rock-path.gd, старая
  модель Верити, unwrap_cache (теперь в .gitignore).
- Основной пакет игры 70 КБ (камень и Верити уехали в пакеты карт).
- tools/: export_all.sh, serve_web.py (nginx-подобный dev-сервер), deploy_server.sh,
  test/ (боты, временный проект и сервер). Net: флаг --client для headless-клиента.
- deploy/: боевые nginx.conf и my-world.service, project.godot выделенного сервера.
- README переписан: что есть и чего нет, структура, карты, преследователи, сеть, пакеты,
  деплой, грабли, дорожная карта (телефон).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-27 17:47:47 +05:00
aibrashandClaude Sonnet 5.5 5ba6466113 Автосохранение редактора (порядок свойств, импорт)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-27 17:39:45 +05:00
aibrashandClaude Sonnet 5.5 307c62b62e Ввод: действия на физические клавиши (работает в любой раскладке)
W/A/S/D и стрелки, Пробел, Shift, C, Q — действия InputMap (move_*, jump, sprint,
crouch, dash). Игрок использует их вместо is_key_pressed(KEY_*): на русской раскладке
символ на клавише другой, а физическая клавиша та же. Сенсорные кнопки смогут
нажимать те же действия.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-27 17:39:05 +05:00
158 changed files with 766 additions and 580 deletions
+3
View File
@@ -1,3 +1,6 @@
# Godot 4+ specific ignores # Godot 4+ specific ignores
.godot/ .godot/
/android/ /android/
# Кэш UV-разворачивания, создаётся при импорте
*.unwrap_cache
+266 -93
View File
@@ -1,68 +1,227 @@
# My World # My World
Репо самого проекта Godot (все файлы проекта). Киберпанк-мир от первого лица с паркур-управлением Мультиплеерная игра от первого лица в браузере (Godot 4.7.2, Web). Игроки собираются в стартовой
и простым мультиплеером: игроки, зашедшие на сайт, бегают вместе и видят друг друга. комнате-хабе, заходят через порталы на разные карты и убегают там от преследователей — камня и Верити.
Паркур-управление: спринт, рывок, подкат, присед, бег по стенам, подтягивание на уступы.
- Движок: Godot **4.7.2**, рендерер **GL Compatibility** (нужен для Web), физика Jolt - **Движок:** Godot **4.7.2**, рендерер **GL Compatibility** (нужен для Web), физика **Jolt**
- Платформа: Web (экспорт в WebAssembly), без потоков (`variant/thread_support=false`), - **Платформа:** Web (WebAssembly), без потоков (`thread_support=false`), без GDExtension (`extensions_support=false`)
поэтому заголовки COOP/COEP браузеру не требуются, но на сервере они всё равно стоят - **Язык** комментариев в коде, интерфейса и документации: русский
- Язык интерфейса и комментариев в коде: русский - **Репозитории** (соседние папки в `/home/aibrash/Projects/My_World/`):
`my-world` — этот проект; `grid-world` — готовый веб-экспорт, который CI выкладывает на сайт;
`Blender models` — исходники моделей; `source-assets` — исходные файлы вне игры (например, видео, из которого сделан звук Верити)
## Состояние: что есть и чего нет
| | Что |
|---|---|
| ✅ | Хаб с порталами; карты **Песочница** и **Backrooms**; выход из карт обратно в хаб |
| ✅ | Карты качаются отдельными пакетами `.pck` при первом входе в портал (экран загрузки с прогрессом) |
| ✅ | Мультиплеер: выделенный сервер, игроки видят друг друга, **комнаты по картам** (в разных картах не пересекаются) |
| ✅ | Преследователи: **камень** и **Верити**; общие для всех игроков комнаты, ходят по навмешу, «видят» только вблизи или в поле зрения, могут потерять игрока |
| ✅ | Смерть при поимке и возрождение, красный экран «ПОЙМАЛ!» |
| ✅ | Паркур: спринт, рывок, подкат, **присед (C)**, бег по стенам, прыжок от стены, двойной прыжок, подтягивание на уступ, зум (ЛКМ) |
| ✅ | Управление **не зависит от раскладки** (русская и любая другая): действия `InputMap` на физические клавиши |
| ✅ | Деплой: CI из `grid-world` на сайт; скрипты экспорта, dev-сервера, тестов без окна, выкладки сервера |
| ⏳ | Телефон: **пока не поддерживается** (нет сенсорного управления, не проверена производительность) — план в разделе «Дорожная карта» |
| ❌ | Чата, ников, настроек (чувствительность мыши, громкость), меню паузы |
| ❌ | Серверных проверок (антиспуфинг позиций, античит): сервер только пересылает |
| ❌ | Сохранения прогресса, аккаунтов |
| ❌ | Автоматических тестов в CI (есть боты для ручного прогона, см. «Тесты без окна») |
| ❌ | Анимаций персонажа: чужой игрок — просто капсула (`net/ghost.gd`) |
## Быстрый старт
```bash
# запуск игры локально (стартует в хабе; без сервера — одиночная игра, клиент переподключается каждые 3 с)
godot --path . --windowed --resolution 960x540
# с локальным сервером (в другом терминале)
tools/test/make_test_server.sh /tmp/test-server && godot --headless --path /tmp/test-server
godot --path . # можно запустить несколько окон
```
Карты в редакторе запускаются отдельно (F6 на сцене карты): `maps/sandbox/sandbox.tscn`,
`maps/backrooms/backrooms.tscn`. В редакторе и при запуске с диска порталы ведут на сцены напрямую (пакеты не качаются).
## Управление
Клавиши привязаны к **физическому расположению** (так работает и на русской раскладке). Действия лежат в
`project.godot → [input]`: `move_forward/back/left/right`, `jump`, `sprint`, `crouch`, `dash`.
| Клавиша | Действие |
|---|---|
| W A S D, стрелки | движение |
| Shift | спринт |
| Пробел | прыжок; держать у уступа в воздухе — подтянуться; у стены — прыжок от стены; в воздухе — двойной прыжок |
| C (держать) | **присед** (скорость 3,5, капсула вдвое ниже, пролезает под низкое) |
| Shift + C на бегу | **подкат** |
| Q | рывок |
| ЛКМ (зажать) | зум |
| Esc / клик | отпустить / захватить мышь |
Бег по стене — в воздухе у стены с зажатым «вперёд» на скорости. Ctrl не используется: в браузере `Ctrl+W` закрывает вкладку.
## Структура проекта ## Структура проекта
``` ```
main.tscn главная сцена (мир, окружение, свет, постройки, лес, птицы, игрок) project.godot, export_presets.cfg, icon.svg
Player/ игрок от первого лица (CharacterBody3D) и всё паркур-управление net/ сеть. Net — автолоад: и клиент, и сервер (один скрипт)
character_body_3d.gd ~800 строк: бег, рывок, слайд, бег по стенам, прыжки, подтягивание net.gd состояние комнат, преследователей, RPC (см. «Сеть»)
World/ мир: forest.gd (лес на MultiMesh), birds.gd, levitate.gd, пол, небо (HDR), музыка ghost.gd чужой игрок: капсула с цветом от id и ником
models/ .glb-модели: здания, башня, обелиск, деревья, Эмпайр-стейт, кибер-офис, скала server.tscn пустая сцена для запуска выделенного сервера
Net/ мультиплеер (см. ниже) core/
net.gd автолоад "Net": и клиент, и сервер (один скрипт) map_loader.gd автолоад MapLoader: скачивает пакет карты и подключает его (см. «Загрузка карт»)
ghost.gd чужой игрок: капсула, ник, сглаженное движение player/
server.tscn пустая сцена для запуска сервера player.tscn, player.gd игрок от первого лица: всё паркур-управление, смерть и возрождение
deploy/ примеры nginx-конфига, systemd-юнита, инструкция objects/
export_presets.cfg пресет «Web», экспорт в ../grid-world/index.html portal.gd портал на другую карту (Area3D, рисуется кодом)
chasers/
chaser.gd общий «мозг» преследователя: навмеш, зрение, погоня, блуждание, поимка, синхронизация
rock/ камень: rock.tscn, Rock.glb (+ извлечённая текстура), Guitar.ogg
verity/ Верити: verity.tscn, verity2.glb (+ текстура), verity.ogg (звук из видео)
maps/ у каждой карты своя папка — она самодостаточна и экспортируется отдельным пакетом
hub/ стартовая комната: hub.tscn, hub.gd (порталы строятся из списка PORTALS)
sandbox/ песочница: sandbox.tscn, forest.gd, birds.gd, levitate.gd, nav_bake.gd,
floor/ sky/ audio/ models/ (здания, башня, обелиск, деревья, Эмпайр-стейт…)
backrooms/ backrooms.tscn, backrooms.gd, backrooms_nav.res (запечённый навмеш),
model/ (готовая модель «Backrooms VR» + 27 извлечённых текстур + CREDITS.md)
deploy/ боевой конфиг nginx, systemd-юнит, project.godot выделенного сервера
tools/ export_all.sh, serve_web.py, deploy_server.sh, test/ (боты)
``` ```
Соседние папки на компьютере разработчика (`/home/aibrash/Projects/My_World/`): **Правила, которые стоит соблюдать**
`my-world` (этот проект), `grid-world` (репо с готовым Web-экспортом, см. деплой), `Blender models`.
## Управление (в игре) - **Папки и файлы — в `snake_case`.** Каждая карта — в `maps/<имя>/`. Всё, что нужно только ей, лежит внутри.
Общее (игрок, преследователи, порталы, сеть) — вне `maps/`: оно входит в основной пакет игры.
- **Файлы рядом с `.glb` не переименовывать и не удалять.** Это текстуры, которые Godot вынул из модели при импорте
(`gltf/embedded_image_handling=Extract`), их имена зависят от имени `.glb`. Переименуешь `.glb` — импорт создаст дубли.
Папки с моделями переносить целиком можно (я так и делал).
- **`.import` и `.uid` коммитятся** и переносятся вместе с файлом (`git mv`). Кэш `.godot/` не коммитится; после переноса
файлов вне редактора его удаляют и делают `godot --headless --import --path .`.
- Пути в сценах — через `res://`; ссылки на карты (`PORTALS` в `hub.gd`, `target_scene` у порталов) — строками.
WASD бег, Shift спринт, Пробел прыжок (держать у уступа — подтянуться), Q рывок, ## Карты и порталы
C слайд (на бегу, вместе с Shift), ЛКМ (зажать) зум, Esc отпускает мышь, клик по окну захватывает.
Бег по стене — в воздухе у стены с зажатым W на скорости.
## Мультиплеер | Карта | Сцена | Пакет | Что там |
|---|---|---|---|
| Хаб | `maps/hub/hub.tscn` | в основном пакете | Комната 28×18 м, строится кодом (пол-грид, стены, свет). Порталы SANDBOX и BACKROOMS в ряд по центру, влезает 6. Безопасна, преследователей нет |
| Песочница | `maps/sandbox/sandbox.tscn` | `world.pck` | Киберпанк-мир: пол-шахматка, здания, 60 деревьев (MultiMesh), птицы, парящий обелиск, звук `tron_01`. Преследователи: **камень** и **Верити**. Портал «HUB» возвращает в хаб |
| Backrooms | `maps/backrooms/backrooms.tscn` | `backrooms.pck` | Модель «Backrooms VR» (×1,4). Преследователь: **Верити** (масштаб 0,5). Выход «EXIT» в дальнем конце ведёт в хаб. Гул ламп генерируется кодом |
**Архитектура:** выделенный сервер на Godot (WebSocket), клиенты подключаются как `WebSocketMultiplayerPeer`. **Добавить карту:**
Сервер ничего не считает и не проверяет, только пересылает состояния игроков остальным. 1. Создай `maps/<имя>/<имя>.tscn`: корень Node3D, внутри игрок (`player/player.tscn`), портал обратно (`objects/portal.gd`,
Мир, физика и управление работают локально у каждого. Игроки друг с другом не сталкиваются. `target_map="hub"`, `target_scene="res://maps/hub/hub.tscn"`), при необходимости преследователи.
В `_ready()` вызови `Net.enter_map("<имя>")` (идемпотентно; так сервер знает, в какой ты комнате).
2. Если нужны преследователи — навмеш: либо `NavigationRegion3D` + `maps/sandbox/nav_bake.gd` (запекание при старте, как в песочнице),
либо заранее запечённый `.res` (как у Backrooms: в вебе без потоков запекать на лету долго).
3. В `export_presets.cfg` добавь пресет-копию «World» (`export_filter="resources"`, `export_files=["res://maps/<имя>/<имя>.tscn"]`,
`exclude_filter="player/*, objects/*, chasers/chaser.gd"`, `export_path="../grid-world/<имя>.pck"`),
а в пресет «Web» — `maps/<имя>/*` в `exclude_filter`. Добавь вызов в `tools/export_all.sh`.
4. В `PORTALS` (`maps/hub/hub.gd`) допиши запись `{title, map, scene, pack, color}` — портал появится сам.
- `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`), иначе игроки не увидят друг друга.
### Запуск локально `chasers/chaser.gd` — один скрипт для всех. Сцены (`rock.tscn`, `verity.tscn`) — это просто модель + звук + `NavigationAgent3D`.
- **Поведение:** гонится за ближайшим живым игроком по навмешу, ловит на `catch_distance` (по горизонтали, не выше `catch_height`).
После поимки игрок умирает и возрождается через 2,5 с в стартовой точке; если преследователь рядом со стартом — уходит на свой старт.
- **Зрение** (`sight_limited = true`): замечает игрока, если он ближе `hearing_radius` (слышит даже за стеной) **или** в конусе `sight_fov`
до `sight_range` и луч до него не упирается в стену. Потерял из виду — идёт к последнему месту `lose_time` секунд, потом блуждает (`patrol_speed`, `wander_radius`).
Без `sight_limited` знает, где игрок, из любой точки.
- **Параметры** (экспорты узла): `chaser_id` (имя в сети), `speed`, `turn_speed`, `catch_distance`, `catch_height`, `sight_*`, `hearing_radius`,
`lose_time`, `patrol_speed`, `wander_radius`, `model_scale` (масштаб дочернего узла `Model`), `emission_energy` (чисто светящийся материал; у Верити 1,0).
- **Значения сейчас:** камень в песочнице — скорость 15, зрение 45 м / 130°, слух 8 м, лимит потери 5 с. Верити в песочнице — скорость 12, зрение 55 м / 110°, слух 10 м, потеря 6 с.
Верити в Backrooms — скорость 10, масштаб 0,5, зрение по умолчанию (30 м / 120° / слух 6 м / потеря 4 с).
- **Модели — плоские «карточки»** вдоль оси X; в сцене повёрнуты на 90° и подняты, чтобы стоять на полу. У `verity2` лицо нарисовано с одной стороны
(свечением), при погоне оно повёрнуто к игроку.
- **Добавить преследователя:** новая сцена по образцу `verity.tscn` (корень со скриптом `chaser.gd`, `chaser_id="имя"`, `NavigationAgent3D`),
инстанс в карту. Больше ничего: сеть синхронизирует всех по имени.
## Сеть
**Архитектура:** выделенный сервер на Godot (WebSocket, порт **9080**), клиенты — `WebSocketMultiplayerPeer`. Сервер **не знает про уровни**
и ничего не считает, только пересылает сообщения внутри «комнаты» (карты). Физика, управление, **преследователи** считаются на клиентах.
- `net/net.gd` — автолоад `Net`. `DisplayServer.get_name() == "headless"` → **сервер**, иначе клиент; флаг `--client` делает клиентом и headless (боты).
Клиент переподключается каждые 3 с. Адрес в вебе берётся из `window.location` (`wss://<домен>/ws` или `ws://`), на десктопе — `ws://127.0.0.1:9080` или `-- --server-url=...`.
- **Комнаты по картам.** Клиент при входе на карту шлёт `submit_map(имя)`, сервер отвечает `recv_map_ack`. Пока подтверждения нет (`Net.map_ready == false`),
клиент игнорирует чужие сообщения — так при смене карты не просачиваются старые пакеты. Игроки разных карт друг друга не видят.
- **Владелец преследователей.** В каждой комнате один клиент (первый вошедший, затем наименьший `peer_id` из оставшихся) симулирует **всех** преследователей и
шлёт их позиции 20 раз/с; остальные клиенты плавно повторяют. Если владелец молчит `ROCK_TIMEOUT` = 3 с (вкладка в фоне, лаги) или вышел — сервер передаёт роль другому;
преследователь продолжает с того же места. Поимку «своего» игрока всегда решает его собственный клиент по тому, что он видит.
- **Смерть и возрождение** шлются отдельно (`submit_life`), чтобы владелец не гнался за мёртвыми и мог увести преследователя от точки возрождения.
| RPC | Кто → кому | Что |
|---|---|---|
| `submit_map(map)` / `recv_map_ack(map)` | клиент → сервер / обратно | вход в комнату |
| `recv_rock_owner(id)` | сервер → клиенты | кто симулирует преследователей |
| `submit_state(pos, yaw, pitch, flags)` / `recv_state(id, …)` | клиент → сервер → остальные | состояние игрока, 20 Гц, `unreliable_ordered` |
| `submit_chasers(ids, positions, yaws)` / `recv_chasers(…)` | владелец → сервер → остальные | позиции всех преследователей, 20 Гц |
| `submit_life(dead, pos)` / `recv_life(id, dead, pos)` | клиент → сервер → остальные | смерть/возрождение, `reliable` |
| `recv_left(id)` | сервер → комната | игрок вышел |
Флаги состояния (`Net.FLAG_*`): подкат, бег по стене, рывок, подтягивание. Присед передаётся как подкат (у призрака сплюснутая капсула).
**RPC требуют одинаковые путь узла и скрипт на сервере и клиенте**, поэтому `net.gd` один. Любое изменение RPC — **клиент и сервер выкладываются вместе**
(старый клиент с новым сервером не работает). Имена вроде `rock_owner`/`recv_rock_owner` остались от времени, когда преследователь был один.
## Загрузка карт пакетами (веб)
Основной пакет игры (`index.pck`) — только хаб, игрок, сеть и порталы (около 70 КБ). Движок — `index.wasm` (39,5 МБ, ~10 МБ с gzip).
Карты — отдельные `.pck`: `world.pck` (13,6 МБ), `backrooms.pck` (21,3 МБ). Портал вызывает `MapLoader.enter(map, scene, pack, title)`:
1. если сцена уже есть в проекте (редактор, десктоп, пакет уже скачан) — переход сразу;
2. иначе игра встаёт на паузу (преследователь не должен убить игрока на экране загрузки), показывается «Загрузка: <имя> N / M МБ»;
3. пакет качается `HTTPRequest` с `Cache-Control: no-cache` (браузер сверяет файл с сервером), записывается в `user://` и подключается через
`ProjectSettings.load_resource_pack(path, false)` (**`false`** — файлы основной игры в приоритете, устаревший пакет не затрёт код);
4. при ошибке на экране и в консоли пишется причина, портал можно повторить.
**Грабли, на которые уже наступили:**
- `HTTPRequest` в браузере: **`accept_gzip = false`** обязательно. Браузер сам распаковывает gzip, а движок видит `Content-Encoding: gzip` и пытается распаковать ещё раз (ошибка 8).
- **`download_file` в вебе не создаёт файл** — тело берём в память и пишем в `user://` сами.
- Файлы-«не ресурсы» (`.mp4`, `.md`) не экспортируются; потому исходное видео Верити лежит вне проекта (`source-assets`).
- Предупреждения `invalid UID … using text path instead` при загрузке пакета безвредны.
- **Кэш зависимостей редактора** у недавно созданных/изменённых сцен может устареть, и экспорт пакета карты пропустит модели и звуки (пакет «слишком маленький»).
`tools/export_all.sh` делает `touch` сцен перед экспортом. Признак беды: размер пакета не вырос после добавления ресурсов.
## Экспорт, тесты и деплой
### Экспорт
```bash ```bash
# сервер tools/export_all.sh # веб-клиент + backrooms.pck + world.pck в ../grid-world
godot --headless --path . res://Net/server.tscn
# клиент (можно запустить несколько окон)
godot --path . --windowed --resolution 960x540
``` ```
Осторожность: `pkill -f` / `pgrep -f` с шаблоном `godot --headless` убивает и сам шелл, Три пресета в `export_presets.cfg`: **Web** (хаб + общее; из него исключены `maps/sandbox`, `maps/backrooms`, `chasers/rock`, `chasers/verity`, `tools`),
потому что шаблон встречается в его командной строке. Находить процесс лучше через `ss -ltnp | grep 9080`. **World** и **Backrooms** (`export_filter=resources` — сцена карты и всё, на что она ссылается, кроме общего из основного пакета). Шаблоны экспорта —
`~/.local/share/godot/export_templates/4.7.2.stable/` (есть только Web). Экспорт при выходе печатает безвредную ошибку `Parameter "singleton" is null`.
## Инфраструктура и деплой ### Проверка сборки как на сервере
```bash
python3 tools/serve_web.py ../grid-world 8080 # nginx-подобно: COOP/COEP и gzip чанками
```
Открыть `http://127.0.0.1:8080`. На десктопном `godot --main-pack` пакеты карт качаются плоским `python3 -m http.server` (без gzip) через `-- --pack-url=http://127.0.0.1:8777/`.
### Тесты без окна (боты)
Автотестов в CI нет; для проверок есть боты. `tools/test/make_test_project.sh /tmp/tp` делает временную копию проекта с автозагрузкой `tools/test/bot.gd`
(телепортирует игрока по расписанию и печатает состояние раз в секунду), `tools/test/make_test_server.sh /tmp/ts` — локальный сервер в боевой раскладке.
```bash
godot --headless --path /tmp/ts & # сервер
godot --headless --path /tmp/tp res://maps/sandbox/sandbox.tscn -- --client --name=A &
godot --headless --path /tmp/tp res://maps/sandbox/sandbox.tscn -- --client --name=B
# цепочка карт на реальных пакетах (портал SANDBOX в хабе на (-2.25, -7.5), BACKROOMS на (2.25, -7.5)):
/tmp/tp/tools/export_all.sh /tmp/site && (cd /tmp/site && python3 -m http.server 8777 --bind 127.0.0.1 &)
godot --headless --main-pack /tmp/site/index.pck -- --client --name=T --pack-url=http://127.0.0.1:8777/ --tp=2:-2.25,1,-7.5
```
Для картинок: `godot --path . --resolution 1152x648 --fixed-fps 10 --write-movie /tmp/shots/a.png --quit-after 25 res://maps/hub/hub.tscn` (окно мелькнёт; кадры — `a00000020.png`).
В браузере: Chrome (flatpak `com.google.Chrome`) `--headless=new --enable-logging=stderr --v=0 http://127.0.0.1:8080` — вывод Godot виден в строках `CONSOLE`.
**Осторожно:** `pkill -f`/`pgrep -f` с шаблоном, который есть и в твоей же командной строке, убивает собственный шелл; не используй `pkill chrome` — закроет браузер владельца. Останавливай по PID или по точному признаку.
### Деплой
``` ```
Браузер ──https/wss──▶ Nginx Proxy Manager (SSL, домен на duckdns) ──http──▶ контейнер 192.168.10.110 Браузер ──https/wss──▶ Nginx Proxy Manager (SSL, домен на duckdns) ──http──▶ контейнер 192.168.10.110
@@ -70,61 +229,75 @@ godot --path . --windowed --resolution 960x540
├─ / → /opt/grid-world (статика игры) ├─ / → /opt/grid-world (статика игры)
└─ /ws → 127.0.0.1:9080 (игровой сервер) └─ /ws → 127.0.0.1:9080 (игровой сервер)
``` ```
**Сервер:** `root@192.168.10.110`, Debian 12, контейнер LXC в Proxmox, 1 ядро, 1 ГБ RAM, ssh по ключам. Нет `curl`, `rsync`, `ufw`; есть `python3`.
В Nginx Proxy Manager включено **Websockets Support** (без него `/ws` не работает). Боевой конфиг nginx: `deploy/nginx.conf`
(`/etc/nginx/sites-enabled/godot_game`); обязателен **gzip для `.wasm`/`.pck`** (иначе 39,5 + 21 МБ уходят как есть) и заголовки COOP/COEP.
**Сервер:** `root@192.168.10.110`, Debian 12, контейнер LXC в Proxmox, 1 ядро, 1 ГБ RAM. Ключи ssh настроены. **Клиент:** `tools/export_all.sh`, затем коммит и пуш в `grid-world` (`main`). Gitea (`git.ibrash.duckdns.org`, `aibrash/grid-world`) по пушу запускает
Нет `curl`, `rsync`, `ufw`; есть `python3`, `perl`. Домен и SSL на Nginx Proxy Manager выше по сети; в его proxy host `.gitea/workflows/deploy.yml`: ssh на сервер и `git pull` в `/opt/grid-world`. Пакеты карт выкладываются вместе с `index.*`.
должна быть включена опция **Websockets Support** (иначе `/ws` не работает; сейчас работает, проверено в браузере).
### Обновить клиент (изменился мир, модель, игровой код)
**Сервер:** отдельный минимальный проект в `/opt/my-world-server/` (`project.godot` + `net/` + официальный бинарник Godot 4.7.2 linux x86_64 `godot`; бинарник из Fedora на Debian не подходит,
берётся из `github.com/godotengine/godot-builds`). Служба systemd `my-world.service` (`deploy/my-world.service`, DynamicUser, `Restart=always`).
```bash ```bash
cd ~/Projects/My_World/my-world tools/deploy_server.sh root@192.168.10.110 # бэкап, копирование net/ и project.godot, пересборка кэша классов, перезапуск
godot --headless --path . --export-release Web ../grid-world/index.html ssh root@192.168.10.110 'journalctl -u my-world -f' # лог: «+ игрок …», «[карта] камень считает игрок …»
cd ../grid-world && git add -A && git commit -m "..." && git push origin main
``` ```
Gitea (`git.ibrash.duckdns.org`, репо `aibrash/grid-world`) по пушу в `main` запускает workflow Трогать сервер нужно только при изменении `net/*`. **Порядок при изменении RPC:** сначала пуш `grid-world`, сразу следом `deploy_server.sh`; игрокам — Ctrl+F5.
`.gitea/workflows/deploy.yml`: заходит по ssh и делает `git pull` в `/opt/grid-world`. Экспорт при выходе Откат сервера: бэкап в `/opt/backup-my-world-server-<дата>/`.
печатает безвредную ошибку `Parameter "singleton" is null`. > Сейчас на сервере ещё **старая раскладка** (`/opt/my-world-server/Net/`, заглавная). `deploy_server.sh` при следующем запуске переведёт её на `net/`.
> Код сети при этом по протоколу не изменился.
### Обновить игровой сервер (изменился `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@git.ibrash.duckdns.org:aibrash/my-world.git`, ветка `master` (основная), используются - Репо проекта: `git@git.ibrash.duckdns.org:aibrash/my-world.git`, основная ветка `master`; коммиты на русском с трейлером `Co-Authored-By`.
коммиты на русском с трейлером `Co-Authored-By`. Для экспериментов создаётся ветка `dev` (была слита и удалена). - **Пушить и выкладывать на сайт — только по прямой просьбе владельца.** Коммитить локально можно.
- Пушить `master` и выкладывать на сайт — только по прямой просьбе владельца.
## Что выяснено по ходу работы (полезно не повторять) ## Что выяснено по ходу работы
- **Окружение (`main.tscn`):** в рендерере Compatibility **объёмный туман не работает** (ни кадр не меняется, ни память), поэтому он удалён. - **Окружение:** в рендерере Compatibility **объёмный туман не работает** (удалён из песочницы). SSAO и glow работают (неоновое свечение), оставлены.
**SSAO и glow работают**: glow даёт неоновое свечение, SSAO — затемнение в стыках; оба оставлены. Отключение всех трёх В Backrooms включён обычный туман (`Environment.fog_*`), он работает.
экономило лишь около 4 МБ видеопамяти и убирало вид, поэтому это не причина высокой памяти. - **Память:** вкладка в браузере показывала около **1,6 ГБ**, хотя на десктопе сцена занимает ~115 МБ. Причина не найдена (нужны данные Chrome: `Shift+Esc`).
- **Память:** вкладка в браузере показывала около **1,6 ГБ**, хотя на десктопе сцена занимает ~115 МБ (статика 83 МБ + видеопамять 30 МБ). Гипотезы: рост WASM-памяти, несжатые текстуры, HDR-небо 2k, `.ogg`, распаковываемый целиком. Измерения с тех пор не повторялись.
Причина не найдена. Нужны данные из Chrome (`Shift+Esc`: память / память GPU / JS-память и растёт ли цифра со временем). - **Материал Верити** — две поверхности: жёлтая основа и диск с лицом (текстура свечения на белой основе). Белая основа на ярком свету даёт бледность,
Гипотезы: рост WASM-памяти и GPU-процесс Chrome, несжатые текстуры (для Web выключено `vram_texture_compression/for_mobile`), поэтому `emission_energy` делает материал чисто светящимся (чёрная основа).
HDR-небо 2k, звук `.ogg`, распаковываемый целиком, `dlink` (`extensions_support=true`, `index.side.wasm` 44 МБ). - **Backrooms:** двери ~1,8 м, поэтому преследователь там масштабом 0,5 и навмеш запечён для агента радиусом 0,6 (иначе проход закрыт). Самый дальний зал за узкой дверью недостижим.
- Пресет «Web» экспортирует в `../grid-world/index.html`. Шаблоны экспорта лежат в `~/.local/share/godot/export_templates/4.7.2.stable/` - **Ввод:** `Input.is_key_pressed(KEY_W)` проверяет символ, на русской раскладке это «Ц». Только действия `InputMap` на физические клавиши.
(есть только Web; Linux-шаблонов нет, поэтому серверный бинарник — официальный редактор в режиме headless). - **Пресеты:** при отключённом `extensions_support` — один `index.wasm`; включение даёт ещё `index.side.wasm` 44 МБ.
## Идеи на потом ## Дорожная карта
- Скрипт `deploy.sh`: экспорт + пуш клиента + при необходимости обновление сервера одной командой. ### Цель: играть с телефона
- Ники по выбору игрока, текстовый чат, анимации персонажа вместо капсулы.
- Серверные проверки (антиспуфинг позиций), комнаты, если игроков станет много. Сейчас игра рассчитана на клавиатуру и мышь. Что нужно сделать, по порядку:
- Разобраться с потреблением памяти в браузере (см. выше).
1. **Сенсорное управление.** Основа уже есть: ввод игрока сведён к действиям `InputMap`, поэтому сенсорные кнопки просто вызывают
`Input.action_press/release("move_forward", strength)` и т. д. Нужна сцена `ui/touch_controls.tscn` (CanvasLayer): виртуальный стик слева (движение),
зона перетаскивания справа (обзор), кнопки прыжка, спринта (переключатель), приседа (удержание), рывка, зума. Показывать только на сенсорных устройствах
(`DisplayServer.is_touchscreen_available()` и feature-теги `web_android`/`web_ios`). В `player/player.gd` обзор сейчас считается из `InputEventMouseMotion` при захваченной мыши —
добавить обработку `InputEventScreenDrag`; зум по ЛКМ заменить действием `zoom`.
2. **Ориентация и экран:** принудительно альбомная (`display/window/handheld/orientation`), полноэкранный режим по первому касанию (браузеры требуют жест),
масштаб интерфейса (надписи «ПОЙМАЛ!», экран загрузки сейчас рассчитаны на десктоп), безопасные зоны.
3. **Звук:** браузер не стартует `AudioContext` без касания. Добавить экран «Нажми, чтобы начать» перед хабом (заодно решает и предупреждение на десктопе).
4. **Текстуры (главное узкое место):** мобильные GPU не поддерживают S3TC/BPTC, которыми сейчас сжаты текстуры (`vram_texture_compression/for_desktop=true`, `for_mobile=false`).
Нужно включить `for_mobile` (ETC2/ASTC) — это **удваивает размер пакетов**, если держать оба формата в одном `.pck`. Варианты: отдельные пакеты `*.mobile.pck`,
выбираемые по feature-тегу, и уменьшенные текстуры (27 текстур Backrooms по 2К — тяжёлые).
5. **Производительность и память:** на телефоне отключить SSAO/glow (в Compatibility они дорогие), уменьшить `Viewport.scaling_3d_scale` (0,6–0,75),
меньше деревьев и птиц, теней нет у деревьев уже сейчас. Мобильный Safari убивает вкладки около 1–1,5 ГБ, а на десктопе она уже занимала 1,6 ГБ — **сначала разобраться с памятью**
(см. выше). Проверить размер `.wasm` и распаковку (на сотовой сети 40 МБ чувствительны; `index.wasm` уже жмётся gzip).
6. **Сеть:** вкладка уходит в фон и замирает — владельца преследователей сервер уже передаёт другому через 3 с; клиенту при возвращении стоит
показывать «переподключение» и заново входить в комнату (`submit_map`), если соединение сброшено.
7. **Тестирование:** режим устройства в Chrome DevTools; на реальном телефоне — `tools/serve_web.py` по локальной сети (`http://<IP-компьютера>:8080`, привязать сервер к `0.0.0.0`).
Минимальная планка: 30 FPS в хабе и песочнице на среднем Android.
### Другое
- Ники по выбору игрока, текстовый чат, анимации персонажа вместо капсулы, настройки (чувствительность, громкость), меню паузы.
- Серверные проверки (антиспуфинг позиций), если игроков станет много.
- Новые карты (добавляются по инструкции выше) и новые преследователи.
- Автоматизировать прогон ботов в CI и проверку «пакет не пустой» после экспорта.
## Лицензии и авторство
- Модель карты Backrooms — «Backrooms VR», автор VTX (https://sketchfab.com/VTX_car), **CC-BY 4.0** (`maps/backrooms/model/CREDITS.md`).
При публикации нужно указывать автора.
- Остальные модели и звуки — материалы владельца проекта.
Binary file not shown.
@@ -1,41 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bhoaea7w0r382"
path.bptc="res://.godot/imported/sunflowers_puresky_2k.hdr-e6836c1a1deabbee5439c7c137a6328a.bptc.ctex"
metadata={
"imported_formats": ["s3tc_bptc"],
"vram_texture": true
}
[deps]
source_file="res://World/Sky/sunflowers_puresky_2k.hdr"
dest_files=["res://.godot/imported/sunflowers_puresky_2k.hdr-e6836c1a1deabbee5439c7c137a6328a.bptc.ctex"]
[params]
compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0
-19
View File
@@ -1,19 +0,0 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://ddsynauqeoppw"
path="res://.godot/imported/tron_01.ogg-0e18fb85b15fd0798053a5bd0a4a9def.oggvorbisstr"
[deps]
source_file="res://World/tron_01.ogg"
dest_files=["res://.godot/imported/tron_01.ogg-0e18fb85b15fd0798053a5bd0a4a9def.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4
@@ -1,9 +1,9 @@
extends Node3D extends Node3D
# Преследователь (камень, Верити…): гонится за ближайшим живым игроком, обходя препятствия по навмешу, и ловит его. # Преследователь (камень, Верити…): гонится за ближайшим живым игроком, обходя препятствия по навмешу, и ловит его.
# С sight_limited он замечает игрока лишь вблизи или в поле зрения без стен между ними; # С sight_limited замечает игрока лишь вблизи или в поле зрения без стен между ними;
# потеряв из виду, идёт к последнему месту, где видел, и дальше бродит по карте. # потеряв из виду, идёт к последнему месту, где видел, и дальше бродит по карте.
# В сети камень считает один клиент (владелец, его назначает сервер), остальные # В сети всех преследователей комнаты считает один клиент (владелец, его назначает сервер, см. net/net.gd),
# плавно повторяют его позицию — поэтому у всех игроков камень один и тот же. # остальные плавно повторяют их позиции — поэтому у всех игроков преследователи одни и те же.
@export var chaser_id := "rock" # Имя в сети: по нему владелец и остальные клиенты сопоставляют преследователей @export var chaser_id := "rock" # Имя в сети: по нему владелец и остальные клиенты сопоставляют преследователей
@export var model_scale := 1.0 # Масштаб узла Model (если он есть): уменьшаем в тесных картах @export var model_scale := 1.0 # Масштаб узла Model (если он есть): уменьшаем в тесных картах
@@ -13,7 +13,7 @@ extends Node3D
@export var catch_distance := 3.0 # С какого расстояния (по горизонтали) ловит игрока @export var catch_distance := 3.0 # С какого расстояния (по горизонтали) ловит игрока
@export var catch_height := 4.0 # Игрока выше этого над камнем не достать @export var catch_height := 4.0 # Игрока выше этого над камнем не достать
@export var repath_distance := 1.0 # Пересчитываем путь, когда игрок сместился на столько @export var repath_distance := 1.0 # Пересчитываем путь, когда игрок сместился на столько
@export var respawn_safe_radius := 25.0 # Игрок возродился ближе этого к камню — камень уходит на старт @export var respawn_safe_radius := 25.0 # Игрок возродился ближе этого — преследователь уходит на свой старт
@export var sync_smoothing := 12.0 # Плавность повтора чужого камня @export var sync_smoothing := 12.0 # Плавность повтора чужого камня
@export var snap_distance := 20.0 # Дальше этого чужой камень не едет, а прыгает (сброс на старт) @export var snap_distance := 20.0 # Дальше этого чужой камень не едет, а прыгает (сброс на старт)
+19
View File
@@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://280pvcvxt2n2"
path="res://.godot/imported/Guitar.ogg-a9c649256b934e43412c17ea4e1fd302.oggvorbisstr"
[deps]
source_file="res://chasers/rock/Guitar.ogg"
dest_files=["res://.godot/imported/Guitar.ogg-a9c649256b934e43412c17ea4e1fd302.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4
@@ -4,12 +4,12 @@ importer="scene"
importer_version=1 importer_version=1
type="PackedScene" type="PackedScene"
uid="uid://cowp33ejxcfcl" uid="uid://cowp33ejxcfcl"
path="res://.godot/imported/Rock.glb-4f2376a14ce37861c14e06a47e678e34.scn" path="res://.godot/imported/Rock.glb-d2a8f3a84a2ecdbbc2d73c9b22190137.scn"
[deps] [deps]
source_file="res://models/Bochi-Rock/Rock.glb" source_file="res://chasers/rock/Rock.glb"
dest_files=["res://.godot/imported/Rock.glb-4f2376a14ce37861c14e06a47e678e34.scn"] dest_files=["res://.godot/imported/Rock.glb-d2a8f3a84a2ecdbbc2d73c9b22190137.scn"]
[params] [params]

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

@@ -3,7 +3,7 @@
importer="texture" importer="texture"
type="CompressedTexture2D" type="CompressedTexture2D"
uid="uid://bx6t5621iqw7f" uid="uid://bx6t5621iqw7f"
path.s3tc="res://.godot/imported/Rock_22ee8cdbd4ccc631cd7c804ab4ebca15.jpg-fad3b4615e62f76758b5970cba2671a7.s3tc.ctex" path.s3tc="res://.godot/imported/Rock_22ee8cdbd4ccc631cd7c804ab4ebca15.jpg-4f14ab3f29d2ef2273e9da4765cef118.s3tc.ctex"
metadata={ metadata={
"imported_formats": ["s3tc_bptc"], "imported_formats": ["s3tc_bptc"],
"vram_texture": true "vram_texture": true
@@ -14,8 +14,8 @@ generator_parameters={
[deps] [deps]
source_file="res://models/Bochi-Rock/Rock_22ee8cdbd4ccc631cd7c804ab4ebca15.jpg" source_file="res://chasers/rock/Rock_22ee8cdbd4ccc631cd7c804ab4ebca15.jpg"
dest_files=["res://.godot/imported/Rock_22ee8cdbd4ccc631cd7c804ab4ebca15.jpg-fad3b4615e62f76758b5970cba2671a7.s3tc.ctex"] dest_files=["res://.godot/imported/Rock_22ee8cdbd4ccc631cd7c804ab4ebca15.jpg-4f14ab3f29d2ef2273e9da4765cef118.s3tc.ctex"]
[params] [params]
@@ -1,8 +1,8 @@
[gd_scene format=3] [gd_scene format=3]
[ext_resource type="Script" path="res://models/Bochi-Rock/rock-follow.gd" id="1_script"] [ext_resource type="Script" path="res://chasers/chaser.gd" id="1_script"]
[ext_resource type="PackedScene" uid="uid://cowp33ejxcfcl" path="res://models/Bochi-Rock/Rock.glb" id="2_model"] [ext_resource type="PackedScene" uid="uid://cowp33ejxcfcl" path="res://chasers/rock/Rock.glb" id="2_model"]
[ext_resource type="AudioStream" uid="uid://280pvcvxt2n2" path="res://models/Bochi-Rock/Guitar.ogg" id="3_audio"] [ext_resource type="AudioStream" uid="uid://280pvcvxt2n2" path="res://chasers/rock/Guitar.ogg" id="3_audio"]
[node name="Rock" type="Node3D"] [node name="Rock" type="Node3D"]
script = ExtResource("1_script") script = ExtResource("1_script")
+19
View File
@@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://b7qmenkjh1wdk"
path="res://.godot/imported/verity.ogg-e0f71c02d742979c6aefd117ffe3f1b5.oggvorbisstr"
[deps]
source_file="res://chasers/verity/verity.ogg"
dest_files=["res://.godot/imported/verity.ogg-e0f71c02d742979c6aefd117ffe3f1b5.oggvorbisstr"]
[params]
loop=true
loop_offset=0
bpm=0
beat_count=0
bar_beats=4
@@ -1,8 +1,8 @@
[gd_scene format=3] [gd_scene format=3]
[ext_resource type="Script" path="res://models/Bochi-Rock/rock-follow.gd" id="1_script"] [ext_resource type="Script" path="res://chasers/chaser.gd" id="1_script"]
[ext_resource type="PackedScene" path="res://models/verity/verity2.glb" id="2_model"] [ext_resource type="PackedScene" path="res://chasers/verity/verity2.glb" id="2_model"]
[ext_resource type="AudioStream" path="res://models/verity/verity.ogg" id="3_audio"] [ext_resource type="AudioStream" path="res://chasers/verity/verity.ogg" id="3_audio"]
[node name="Verity" type="Node3D"] [node name="Verity" type="Node3D"]
script = ExtResource("1_script") script = ExtResource("1_script")
@@ -4,12 +4,12 @@ importer="scene"
importer_version=1 importer_version=1
type="PackedScene" type="PackedScene"
uid="uid://wup5p3waumce" uid="uid://wup5p3waumce"
path="res://.godot/imported/verity2.glb-74448d25644050663fdf5f7cb5b2b561.scn" path="res://.godot/imported/verity2.glb-60af6cc5d69d9f8af83072155580b49b.scn"
[deps] [deps]
source_file="res://models/verity/verity2.glb" source_file="res://chasers/verity/verity2.glb"
dest_files=["res://.godot/imported/verity2.glb-74448d25644050663fdf5f7cb5b2b561.scn"] dest_files=["res://.godot/imported/verity2.glb-60af6cc5d69d9f8af83072155580b49b.scn"]
[params] [params]

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 59 KiB

@@ -2,8 +2,8 @@
importer="texture" importer="texture"
type="CompressedTexture2D" type="CompressedTexture2D"
uid="uid://bxiwv7ayauag5" uid="uid://c4fe3eo5c2w6g"
path.s3tc="res://.godot/imported/verity_unnamed.webp-57398071db19959d3dcf9d9f175390fc.s3tc.ctex" path.s3tc="res://.godot/imported/verity2_unnamed.webp-1aa0cf701c3ad8dae1575d20b6b42222.s3tc.ctex"
metadata={ metadata={
"imported_formats": ["s3tc_bptc"], "imported_formats": ["s3tc_bptc"],
"vram_texture": true "vram_texture": true
@@ -14,8 +14,8 @@ generator_parameters={
[deps] [deps]
source_file="res://models/verity/verity_unnamed.webp" source_file="res://chasers/verity/verity2_unnamed.webp"
dest_files=["res://.godot/imported/verity_unnamed.webp-57398071db19959d3dcf9d9f175390fc.s3tc.ctex"] dest_files=["res://.godot/imported/verity2_unnamed.webp-1aa0cf701c3ad8dae1575d20b6b42222.s3tc.ctex"]
[params] [params]
+1 -17
View File
@@ -1,17 +1 @@
# Мультиплеер: деплой Деплой описан в корневом [README.md](../README.md#деплой).
## Сервер игры
Выделенный сервер — тот же проект, запущенный headless (порт 9080, plain WebSocket):
godot --headless --path /путь/к/my-world res://Net/server.tscn
Пример systemd-юнита: `deploy/my-world.service`.
## nginx (TLS + статика)
См. `deploy/nginx.conf`. Страница по https подключается к `wss://<домен>/ws`,
nginx проксирует `/ws` на `127.0.0.1:9080`.
## Клиент
Web-экспорт (пресет «Web») кладётся в корень сайта. Адрес сервера берётся из
`window.location`, на десктопе — `ws://127.0.0.1:9080` или
`godot -- --server-url=wss://example.com/ws`.
+4 -2
View File
@@ -3,9 +3,11 @@ Description=My World game server
After=network.target After=network.target
[Service] [Service]
ExecStart=/usr/bin/godot --headless --path /opt/my-world res://Net/server.tscn ExecStart=/opt/my-world-server/godot --headless --path /opt/my-world-server
Restart=always Restart=always
User=nobody DynamicUser=yes
StateDirectory=my-world
Environment=HOME=/var/lib/my-world
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target
+19 -4
View File
@@ -1,11 +1,22 @@
# Боевой конфиг сайта (/etc/nginx/sites-enabled/godot_game).
# Статику клиента кладёт CI (git pull в /opt/grid-world), /ws проксируется на игровой сервер.
server { server {
listen 443 ssl; listen 80;
server_name example.com; server_name _;
# ssl_certificate / ssl_certificate_key — например, от certbot
root /var/www/my-world; # сюда экспорт Web (index.html, .wasm, .pck) root /opt/grid-world/;
index index.html; index index.html;
# Нужны Godot web (SharedArrayBuffer/изоляция страницы)
add_header Cross-Origin-Opener-Policy "same-origin";
add_header Cross-Origin-Embedder-Policy "require-corp";
# .wasm и .pck хорошо жмутся: 39,5 МБ -> ~10 МБ. Браузер распаковывает сам (см. core/map_loader.gd)
gzip on;
gzip_comp_level 6;
gzip_min_length 1024;
gzip_types application/wasm application/octet-stream application/javascript text/javascript text/css application/json image/svg+xml;
location /ws { location /ws {
proxy_pass http://127.0.0.1:9080; proxy_pass http://127.0.0.1:9080;
proxy_http_version 1.1; proxy_http_version 1.1;
@@ -14,4 +25,8 @@ server {
proxy_set_header Host $host; proxy_set_header Host $host;
proxy_read_timeout 1h; proxy_read_timeout 1h;
} }
location / {
try_files $uri $uri/ =404;
}
} }
+13
View File
@@ -0,0 +1,13 @@
; Проект выделенного сервера (headless). Кладётся рядом с папкой net/ из репозитория.
; На сервере: /opt/my-world-server/{project.godot, net/, godot}. См. tools/deploy_server.sh
config_version=5
[application]
config/name="world2-server"
run/main_scene="res://net/server.tscn"
config/features=PackedStringArray("4.7")
[autoload]
Net="*res://net/net.gd"
+5 -5
View File
@@ -10,7 +10,7 @@ dedicated_server=false
custom_features="" custom_features=""
export_filter="all_resources" export_filter="all_resources"
include_filter="" include_filter=""
exclude_filter="models/verity/*, models/backrooms/*, World/Backrooms/*, main.tscn, World/Floor/*, World/Sky/*, World/tron_01.ogg, World/forest.gd, World/birds.gd, World/levitate.gd, World/nav-bake.gd, models/building_02*, models/tower_01/*, models/house_02/*, models/cyber-office/*, models/obelisk_01/*, models/Tree_01/*, models/empire-state/*" exclude_filter="tools/*, maps/sandbox/*, maps/backrooms/*, chasers/rock/*, chasers/verity/*"
export_path="../grid-world/index.html" export_path="../grid-world/index.html"
patches=PackedStringArray() patches=PackedStringArray()
patch_delta_encoding=false patch_delta_encoding=false
@@ -58,9 +58,9 @@ platform="Web"
dedicated_server=false dedicated_server=false
custom_features="" custom_features=""
export_filter="resources" export_filter="resources"
export_files=PackedStringArray("res://World/Backrooms/backrooms.tscn") export_files=PackedStringArray("res://maps/backrooms/backrooms.tscn")
include_filter="" include_filter=""
exclude_filter="Player/*, models/Bochi-Rock/*, World/portal.gd" exclude_filter="player/*, objects/*, chasers/chaser.gd"
export_path="../grid-world/backrooms.pck" export_path="../grid-world/backrooms.pck"
patches=PackedStringArray() patches=PackedStringArray()
patch_delta_encoding=false patch_delta_encoding=false
@@ -108,9 +108,9 @@ platform="Web"
dedicated_server=false dedicated_server=false
custom_features="" custom_features=""
export_filter="resources" export_filter="resources"
export_files=PackedStringArray("res://main.tscn") export_files=PackedStringArray("res://maps/sandbox/sandbox.tscn")
include_filter="" include_filter=""
exclude_filter="Player/*, models/Bochi-Rock/*, World/portal.gd" exclude_filter="player/*, objects/*, chasers/chaser.gd"
export_path="../grid-world/world.pck" export_path="../grid-world/world.pck"
patches=PackedStringArray() patches=PackedStringArray()
patch_delta_encoding=false patch_delta_encoding=false
@@ -1,11 +1,11 @@
[gd_scene format=3 uid="uid://djgkggvwiaaoy"] [gd_scene format=3 uid="uid://djgkggvwiaaoy"]
[ext_resource type="Script" uid="uid://gmpkjl6m3415" path="res://World/Backrooms/backrooms.gd" id="1_level"] [ext_resource type="Script" uid="uid://gmpkjl6m3415" path="res://maps/backrooms/backrooms.gd" id="1_level"]
[ext_resource type="PackedScene" uid="uid://b8ef3wy1pthgm" path="res://models/backrooms/backrooms_vr.glb" id="2_map"] [ext_resource type="PackedScene" uid="uid://b8ef3wy1pthgm" path="res://maps/backrooms/model/backrooms_vr.glb" id="2_map"]
[ext_resource type="PackedScene" uid="uid://c3efk6k8xnf57" path="res://Player/Player.tscn" id="3_player"] [ext_resource type="PackedScene" uid="uid://c3efk6k8xnf57" path="res://player/player.tscn" id="3_player"]
[ext_resource type="PackedScene" path="res://models/verity/verity.tscn" id="4_rock"] [ext_resource type="PackedScene" path="res://chasers/verity/verity.tscn" id="4_rock"]
[ext_resource type="Script" uid="uid://bu8v0ivhmnykt" path="res://World/portal.gd" id="5_portal"] [ext_resource type="Script" uid="uid://bu8v0ivhmnykt" path="res://objects/portal.gd" id="5_portal"]
[ext_resource type="NavigationMesh" path="res://World/Backrooms/backrooms_nav.res" id="6_nav"] [ext_resource type="NavigationMesh" path="res://maps/backrooms/backrooms_nav.res" id="6_nav"]
[node name="Backrooms" type="Node3D" unique_id=511186617] [node name="Backrooms" type="Node3D" unique_id=511186617]
script = ExtResource("1_level") script = ExtResource("1_level")
@@ -18,17 +18,17 @@ transform = Transform3D(0, 0, 1, 0, 1, 0, -1, 0, 0, -0.4838078, 1.2, 2.0468445)
[node name="Verity" parent="." unique_id=311663607 instance=ExtResource("4_rock")] [node name="Verity" parent="." unique_id=311663607 instance=ExtResource("4_rock")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -48, 0, 3) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -48, 0, 3)
model_scale = 0.5
speed = 10.0 speed = 10.0
catch_distance = 2.0 catch_distance = 2.0
catch_height = 3.0 catch_height = 3.0
model_scale = 0.5
sight_limited = true sight_limited = true
[node name="Exit" type="Area3D" parent="." unique_id=1540431614] [node name="Exit" type="Area3D" parent="." unique_id=1540431614]
transform = Transform3D(0, 0, 1, 0, 1, 0, -1, 0, 0, -92.93203, -0.15, 10.390619) transform = Transform3D(0, 0, 1, 0, 1, 0, -1, 0, 0, -92.93203, -0.15, 10.390619)
script = ExtResource("5_portal") script = ExtResource("5_portal")
target_map = "hub" target_map = "hub"
target_scene = "res://World/Hub/hub.tscn" target_scene = "res://maps/hub/hub.tscn"
title = "EXIT" title = "EXIT"
color = Color(0.3, 1, 0.45, 1) color = Color(0.3, 1, 0.45, 1)
@@ -4,12 +4,12 @@ importer="scene"
importer_version=1 importer_version=1
type="PackedScene" type="PackedScene"
uid="uid://b8ef3wy1pthgm" uid="uid://b8ef3wy1pthgm"
path="res://.godot/imported/backrooms_vr.glb-0d2b88698924672a51ea9e43fb3ac8a4.scn" path="res://.godot/imported/backrooms_vr.glb-a44db63c437b581310f90765822ced16.scn"
[deps] [deps]
source_file="res://models/backrooms/backrooms_vr.glb" source_file="res://maps/backrooms/model/backrooms_vr.glb"
dest_files=["res://.godot/imported/backrooms_vr.glb-0d2b88698924672a51ea9e43fb3ac8a4.scn"] dest_files=["res://.godot/imported/backrooms_vr.glb-a44db63c437b581310f90765822ced16.scn"]
[params] [params]

Before

Width:  |  Height:  |  Size: 687 KiB

After

Width:  |  Height:  |  Size: 687 KiB

@@ -3,7 +3,7 @@
importer="texture" importer="texture"
type="CompressedTexture2D" type="CompressedTexture2D"
uid="uid://b6weikavgdbap" uid="uid://b6weikavgdbap"
path.s3tc="res://.godot/imported/backrooms_vr_0.png-f4c02e53a6cca71b1adac7615326ab0e.s3tc.ctex" path.s3tc="res://.godot/imported/backrooms_vr_0.png-2be6c844f99fa11d85c6f7f7ea9febaf.s3tc.ctex"
metadata={ metadata={
"imported_formats": ["s3tc_bptc"], "imported_formats": ["s3tc_bptc"],
"vram_texture": true "vram_texture": true
@@ -14,8 +14,8 @@ generator_parameters={
[deps] [deps]
source_file="res://models/backrooms/backrooms_vr_0.png" source_file="res://maps/backrooms/model/backrooms_vr_0.png"
dest_files=["res://.godot/imported/backrooms_vr_0.png-f4c02e53a6cca71b1adac7615326ab0e.s3tc.ctex"] dest_files=["res://.godot/imported/backrooms_vr_0.png-2be6c844f99fa11d85c6f7f7ea9febaf.s3tc.ctex"]
[params] [params]

Before

Width:  |  Height:  |  Size: 549 KiB

After

Width:  |  Height:  |  Size: 549 KiB

@@ -3,7 +3,7 @@
importer="texture" importer="texture"
type="CompressedTexture2D" type="CompressedTexture2D"
uid="uid://dpud2naqkm4xs" uid="uid://dpud2naqkm4xs"
path.s3tc="res://.godot/imported/backrooms_vr_1.png-d716f61def983b3d40016ab77643deb5.s3tc.ctex" path.s3tc="res://.godot/imported/backrooms_vr_1.png-58eff1a40efb7f44ec9e5051210a83fb.s3tc.ctex"
metadata={ metadata={
"imported_formats": ["s3tc_bptc"], "imported_formats": ["s3tc_bptc"],
"vram_texture": true "vram_texture": true
@@ -14,8 +14,8 @@ generator_parameters={
[deps] [deps]
source_file="res://models/backrooms/backrooms_vr_1.png" source_file="res://maps/backrooms/model/backrooms_vr_1.png"
dest_files=["res://.godot/imported/backrooms_vr_1.png-d716f61def983b3d40016ab77643deb5.s3tc.ctex"] dest_files=["res://.godot/imported/backrooms_vr_1.png-58eff1a40efb7f44ec9e5051210a83fb.s3tc.ctex"]
[params] [params]

Before

Width:  |  Height:  |  Size: 609 KiB

After

Width:  |  Height:  |  Size: 609 KiB

@@ -3,7 +3,7 @@
importer="texture" importer="texture"
type="CompressedTexture2D" type="CompressedTexture2D"
uid="uid://b8nb23j42isps" uid="uid://b8nb23j42isps"
path.s3tc="res://.godot/imported/backrooms_vr_10.png-6693254cade6e5be230303d4e03a4118.s3tc.ctex" path.s3tc="res://.godot/imported/backrooms_vr_10.png-66735878c6a889d3b78eb86dd9d140a6.s3tc.ctex"
metadata={ metadata={
"imported_formats": ["s3tc_bptc"], "imported_formats": ["s3tc_bptc"],
"vram_texture": true "vram_texture": true
@@ -14,8 +14,8 @@ generator_parameters={
[deps] [deps]
source_file="res://models/backrooms/backrooms_vr_10.png" source_file="res://maps/backrooms/model/backrooms_vr_10.png"
dest_files=["res://.godot/imported/backrooms_vr_10.png-6693254cade6e5be230303d4e03a4118.s3tc.ctex"] dest_files=["res://.godot/imported/backrooms_vr_10.png-66735878c6a889d3b78eb86dd9d140a6.s3tc.ctex"]
[params] [params]

Before

Width:  |  Height:  |  Size: 1018 KiB

After

Width:  |  Height:  |  Size: 1018 KiB

@@ -3,7 +3,7 @@
importer="texture" importer="texture"
type="CompressedTexture2D" type="CompressedTexture2D"
uid="uid://djx5e3wiwgqex" uid="uid://djx5e3wiwgqex"
path.s3tc="res://.godot/imported/backrooms_vr_11.png-a17fa98ada75a88222940123fa22d2c4.s3tc.ctex" path.s3tc="res://.godot/imported/backrooms_vr_11.png-1b4bc124dd509717784a19ff35407041.s3tc.ctex"
metadata={ metadata={
"imported_formats": ["s3tc_bptc"], "imported_formats": ["s3tc_bptc"],
"vram_texture": true "vram_texture": true
@@ -14,8 +14,8 @@ generator_parameters={
[deps] [deps]
source_file="res://models/backrooms/backrooms_vr_11.png" source_file="res://maps/backrooms/model/backrooms_vr_11.png"
dest_files=["res://.godot/imported/backrooms_vr_11.png-a17fa98ada75a88222940123fa22d2c4.s3tc.ctex"] dest_files=["res://.godot/imported/backrooms_vr_11.png-1b4bc124dd509717784a19ff35407041.s3tc.ctex"]
[params] [params]

Before

Width:  |  Height:  |  Size: 897 KiB

After

Width:  |  Height:  |  Size: 897 KiB

@@ -3,7 +3,7 @@
importer="texture" importer="texture"
type="CompressedTexture2D" type="CompressedTexture2D"
uid="uid://djyh6v1wlujbx" uid="uid://djyh6v1wlujbx"
path.s3tc="res://.godot/imported/backrooms_vr_12.png-d78f07b6d7d3138c20741e40ee92edfa.s3tc.ctex" path.s3tc="res://.godot/imported/backrooms_vr_12.png-4b9b409931a846aa266212c354316816.s3tc.ctex"
metadata={ metadata={
"imported_formats": ["s3tc_bptc"], "imported_formats": ["s3tc_bptc"],
"vram_texture": true "vram_texture": true
@@ -14,8 +14,8 @@ generator_parameters={
[deps] [deps]
source_file="res://models/backrooms/backrooms_vr_12.png" source_file="res://maps/backrooms/model/backrooms_vr_12.png"
dest_files=["res://.godot/imported/backrooms_vr_12.png-d78f07b6d7d3138c20741e40ee92edfa.s3tc.ctex"] dest_files=["res://.godot/imported/backrooms_vr_12.png-4b9b409931a846aa266212c354316816.s3tc.ctex"]
[params] [params]

Before

Width:  |  Height:  |  Size: 457 KiB

After

Width:  |  Height:  |  Size: 457 KiB

@@ -3,7 +3,7 @@
importer="texture" importer="texture"
type="CompressedTexture2D" type="CompressedTexture2D"
uid="uid://dw68nfgta46bg" uid="uid://dw68nfgta46bg"
path.s3tc="res://.godot/imported/backrooms_vr_13.png-5712190a97c0176b90edbead990dd794.s3tc.ctex" path.s3tc="res://.godot/imported/backrooms_vr_13.png-0fc1704bbe3dc42d01705e5699474f84.s3tc.ctex"
metadata={ metadata={
"imported_formats": ["s3tc_bptc"], "imported_formats": ["s3tc_bptc"],
"vram_texture": true "vram_texture": true
@@ -14,8 +14,8 @@ generator_parameters={
[deps] [deps]
source_file="res://models/backrooms/backrooms_vr_13.png" source_file="res://maps/backrooms/model/backrooms_vr_13.png"
dest_files=["res://.godot/imported/backrooms_vr_13.png-5712190a97c0176b90edbead990dd794.s3tc.ctex"] dest_files=["res://.godot/imported/backrooms_vr_13.png-0fc1704bbe3dc42d01705e5699474f84.s3tc.ctex"]
[params] [params]

Before

Width:  |  Height:  |  Size: 722 KiB

After

Width:  |  Height:  |  Size: 722 KiB

@@ -3,7 +3,7 @@
importer="texture" importer="texture"
type="CompressedTexture2D" type="CompressedTexture2D"
uid="uid://cgx5ip8cpgxly" uid="uid://cgx5ip8cpgxly"
path.s3tc="res://.godot/imported/backrooms_vr_14.png-bbec912f44ebcc9f8e50c98b8f9abaad.s3tc.ctex" path.s3tc="res://.godot/imported/backrooms_vr_14.png-ef53db6b8d8b9de93738fb3214c4a754.s3tc.ctex"
metadata={ metadata={
"imported_formats": ["s3tc_bptc"], "imported_formats": ["s3tc_bptc"],
"vram_texture": true "vram_texture": true
@@ -14,8 +14,8 @@ generator_parameters={
[deps] [deps]
source_file="res://models/backrooms/backrooms_vr_14.png" source_file="res://maps/backrooms/model/backrooms_vr_14.png"
dest_files=["res://.godot/imported/backrooms_vr_14.png-bbec912f44ebcc9f8e50c98b8f9abaad.s3tc.ctex"] dest_files=["res://.godot/imported/backrooms_vr_14.png-ef53db6b8d8b9de93738fb3214c4a754.s3tc.ctex"]
[params] [params]

Before

Width:  |  Height:  |  Size: 376 KiB

After

Width:  |  Height:  |  Size: 376 KiB

@@ -3,7 +3,7 @@
importer="texture" importer="texture"
type="CompressedTexture2D" type="CompressedTexture2D"
uid="uid://dxvxjwfnpxqyb" uid="uid://dxvxjwfnpxqyb"
path.s3tc="res://.godot/imported/backrooms_vr_15.png-a20bd15c0b1b28d9456bc931978a218d.s3tc.ctex" path.s3tc="res://.godot/imported/backrooms_vr_15.png-bdd518883610319d83796f212056da76.s3tc.ctex"
metadata={ metadata={
"imported_formats": ["s3tc_bptc"], "imported_formats": ["s3tc_bptc"],
"vram_texture": true "vram_texture": true
@@ -14,8 +14,8 @@ generator_parameters={
[deps] [deps]
source_file="res://models/backrooms/backrooms_vr_15.png" source_file="res://maps/backrooms/model/backrooms_vr_15.png"
dest_files=["res://.godot/imported/backrooms_vr_15.png-a20bd15c0b1b28d9456bc931978a218d.s3tc.ctex"] dest_files=["res://.godot/imported/backrooms_vr_15.png-bdd518883610319d83796f212056da76.s3tc.ctex"]
[params] [params]

Before

Width:  |  Height:  |  Size: 814 KiB

After

Width:  |  Height:  |  Size: 814 KiB

@@ -3,7 +3,7 @@
importer="texture" importer="texture"
type="CompressedTexture2D" type="CompressedTexture2D"
uid="uid://yjj1swxlp5le" uid="uid://yjj1swxlp5le"
path.s3tc="res://.godot/imported/backrooms_vr_16.png-07a4cbc529fe565672c305dfd8e573d6.s3tc.ctex" path.s3tc="res://.godot/imported/backrooms_vr_16.png-a9e77912985bd57ee651e6170a096b24.s3tc.ctex"
metadata={ metadata={
"imported_formats": ["s3tc_bptc"], "imported_formats": ["s3tc_bptc"],
"vram_texture": true "vram_texture": true
@@ -14,8 +14,8 @@ generator_parameters={
[deps] [deps]
source_file="res://models/backrooms/backrooms_vr_16.png" source_file="res://maps/backrooms/model/backrooms_vr_16.png"
dest_files=["res://.godot/imported/backrooms_vr_16.png-07a4cbc529fe565672c305dfd8e573d6.s3tc.ctex"] dest_files=["res://.godot/imported/backrooms_vr_16.png-a9e77912985bd57ee651e6170a096b24.s3tc.ctex"]
[params] [params]

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 81 KiB

@@ -3,7 +3,7 @@
importer="texture" importer="texture"
type="CompressedTexture2D" type="CompressedTexture2D"
uid="uid://decg78uck2mqa" uid="uid://decg78uck2mqa"
path.s3tc="res://.godot/imported/backrooms_vr_17.png-5a591d4362230f749be443a19b09de4f.s3tc.ctex" path.s3tc="res://.godot/imported/backrooms_vr_17.png-19b989596eb512a37eeb8e755657cf6c.s3tc.ctex"
metadata={ metadata={
"imported_formats": ["s3tc_bptc"], "imported_formats": ["s3tc_bptc"],
"vram_texture": true "vram_texture": true
@@ -14,8 +14,8 @@ generator_parameters={
[deps] [deps]
source_file="res://models/backrooms/backrooms_vr_17.png" source_file="res://maps/backrooms/model/backrooms_vr_17.png"
dest_files=["res://.godot/imported/backrooms_vr_17.png-5a591d4362230f749be443a19b09de4f.s3tc.ctex"] dest_files=["res://.godot/imported/backrooms_vr_17.png-19b989596eb512a37eeb8e755657cf6c.s3tc.ctex"]
[params] [params]

Before

Width:  |  Height:  |  Size: 727 KiB

After

Width:  |  Height:  |  Size: 727 KiB

@@ -3,7 +3,7 @@
importer="texture" importer="texture"
type="CompressedTexture2D" type="CompressedTexture2D"
uid="uid://brljydqpvgko0" uid="uid://brljydqpvgko0"
path.s3tc="res://.godot/imported/backrooms_vr_18.png-137b67a1864cee9569a91581bee56ca4.s3tc.ctex" path.s3tc="res://.godot/imported/backrooms_vr_18.png-715bde8336c095d043c0cd3f1be4f502.s3tc.ctex"
metadata={ metadata={
"imported_formats": ["s3tc_bptc"], "imported_formats": ["s3tc_bptc"],
"vram_texture": true "vram_texture": true
@@ -14,8 +14,8 @@ generator_parameters={
[deps] [deps]
source_file="res://models/backrooms/backrooms_vr_18.png" source_file="res://maps/backrooms/model/backrooms_vr_18.png"
dest_files=["res://.godot/imported/backrooms_vr_18.png-137b67a1864cee9569a91581bee56ca4.s3tc.ctex"] dest_files=["res://.godot/imported/backrooms_vr_18.png-715bde8336c095d043c0cd3f1be4f502.s3tc.ctex"]
[params] [params]

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

@@ -3,7 +3,7 @@
importer="texture" importer="texture"
type="CompressedTexture2D" type="CompressedTexture2D"
uid="uid://dciao7v0fkii1" uid="uid://dciao7v0fkii1"
path.s3tc="res://.godot/imported/backrooms_vr_19.png-cdf77da915a8406d23a1f971a1d765fe.s3tc.ctex" path.s3tc="res://.godot/imported/backrooms_vr_19.png-49227045cdea27d1b381e3b9a831ee24.s3tc.ctex"
metadata={ metadata={
"imported_formats": ["s3tc_bptc"], "imported_formats": ["s3tc_bptc"],
"vram_texture": true "vram_texture": true
@@ -14,8 +14,8 @@ generator_parameters={
[deps] [deps]
source_file="res://models/backrooms/backrooms_vr_19.png" source_file="res://maps/backrooms/model/backrooms_vr_19.png"
dest_files=["res://.godot/imported/backrooms_vr_19.png-cdf77da915a8406d23a1f971a1d765fe.s3tc.ctex"] dest_files=["res://.godot/imported/backrooms_vr_19.png-49227045cdea27d1b381e3b9a831ee24.s3tc.ctex"]
[params] [params]

Before

Width:  |  Height:  |  Size: 206 KiB

After

Width:  |  Height:  |  Size: 206 KiB

@@ -3,7 +3,7 @@
importer="texture" importer="texture"
type="CompressedTexture2D" type="CompressedTexture2D"
uid="uid://eorb1lixjtbv" uid="uid://eorb1lixjtbv"
path.s3tc="res://.godot/imported/backrooms_vr_2.png-4f70d8bd72d6cc50f9b317463f7076b9.s3tc.ctex" path.s3tc="res://.godot/imported/backrooms_vr_2.png-f1a53db9b9d3fd8a5ada6b370f18b647.s3tc.ctex"
metadata={ metadata={
"imported_formats": ["s3tc_bptc"], "imported_formats": ["s3tc_bptc"],
"vram_texture": true "vram_texture": true
@@ -14,8 +14,8 @@ generator_parameters={
[deps] [deps]
source_file="res://models/backrooms/backrooms_vr_2.png" source_file="res://maps/backrooms/model/backrooms_vr_2.png"
dest_files=["res://.godot/imported/backrooms_vr_2.png-4f70d8bd72d6cc50f9b317463f7076b9.s3tc.ctex"] dest_files=["res://.godot/imported/backrooms_vr_2.png-f1a53db9b9d3fd8a5ada6b370f18b647.s3tc.ctex"]
[params] [params]

Before

Width:  |  Height:  |  Size: 288 KiB

After

Width:  |  Height:  |  Size: 288 KiB

@@ -3,7 +3,7 @@
importer="texture" importer="texture"
type="CompressedTexture2D" type="CompressedTexture2D"
uid="uid://xbcxlgw40igt" uid="uid://xbcxlgw40igt"
path.s3tc="res://.godot/imported/backrooms_vr_20.png-4ac1f7f9258502dc9178e35c5602f20d.s3tc.ctex" path.s3tc="res://.godot/imported/backrooms_vr_20.png-1d10fd330e1a8c0fc61d7486329ee063.s3tc.ctex"
metadata={ metadata={
"imported_formats": ["s3tc_bptc"], "imported_formats": ["s3tc_bptc"],
"vram_texture": true "vram_texture": true
@@ -14,8 +14,8 @@ generator_parameters={
[deps] [deps]
source_file="res://models/backrooms/backrooms_vr_20.png" source_file="res://maps/backrooms/model/backrooms_vr_20.png"
dest_files=["res://.godot/imported/backrooms_vr_20.png-4ac1f7f9258502dc9178e35c5602f20d.s3tc.ctex"] dest_files=["res://.godot/imported/backrooms_vr_20.png-1d10fd330e1a8c0fc61d7486329ee063.s3tc.ctex"]
[params] [params]

Before

Width:  |  Height:  |  Size: 971 KiB

After

Width:  |  Height:  |  Size: 971 KiB

@@ -3,7 +3,7 @@
importer="texture" importer="texture"
type="CompressedTexture2D" type="CompressedTexture2D"
uid="uid://crx06wyll47ou" uid="uid://crx06wyll47ou"
path.s3tc="res://.godot/imported/backrooms_vr_21.png-99c9bdd476eb15db8a1993bf96f942b2.s3tc.ctex" path.s3tc="res://.godot/imported/backrooms_vr_21.png-222e2c54fbdeedd6c0487b075650545f.s3tc.ctex"
metadata={ metadata={
"imported_formats": ["s3tc_bptc"], "imported_formats": ["s3tc_bptc"],
"vram_texture": true "vram_texture": true
@@ -14,8 +14,8 @@ generator_parameters={
[deps] [deps]
source_file="res://models/backrooms/backrooms_vr_21.png" source_file="res://maps/backrooms/model/backrooms_vr_21.png"
dest_files=["res://.godot/imported/backrooms_vr_21.png-99c9bdd476eb15db8a1993bf96f942b2.s3tc.ctex"] dest_files=["res://.godot/imported/backrooms_vr_21.png-222e2c54fbdeedd6c0487b075650545f.s3tc.ctex"]
[params] [params]

Before

Width:  |  Height:  |  Size: 219 KiB

After

Width:  |  Height:  |  Size: 219 KiB

@@ -3,7 +3,7 @@
importer="texture" importer="texture"
type="CompressedTexture2D" type="CompressedTexture2D"
uid="uid://cqareamgsajs" uid="uid://cqareamgsajs"
path.s3tc="res://.godot/imported/backrooms_vr_22.png-a69fa573ac9737ff98f9e3a9cb90184f.s3tc.ctex" path.s3tc="res://.godot/imported/backrooms_vr_22.png-b8965588f76146db08bcab06a453e84c.s3tc.ctex"
metadata={ metadata={
"imported_formats": ["s3tc_bptc"], "imported_formats": ["s3tc_bptc"],
"vram_texture": true "vram_texture": true
@@ -14,8 +14,8 @@ generator_parameters={
[deps] [deps]
source_file="res://models/backrooms/backrooms_vr_22.png" source_file="res://maps/backrooms/model/backrooms_vr_22.png"
dest_files=["res://.godot/imported/backrooms_vr_22.png-a69fa573ac9737ff98f9e3a9cb90184f.s3tc.ctex"] dest_files=["res://.godot/imported/backrooms_vr_22.png-b8965588f76146db08bcab06a453e84c.s3tc.ctex"]
[params] [params]

Before

Width:  |  Height:  |  Size: 547 KiB

After

Width:  |  Height:  |  Size: 547 KiB

@@ -3,7 +3,7 @@
importer="texture" importer="texture"
type="CompressedTexture2D" type="CompressedTexture2D"
uid="uid://dy57qg8yhs4cx" uid="uid://dy57qg8yhs4cx"
path.s3tc="res://.godot/imported/backrooms_vr_23.png-1d7ffb8aa29d441bde425ede0413250c.s3tc.ctex" path.s3tc="res://.godot/imported/backrooms_vr_23.png-4090a5fff0cc06e162bacc68b3fd83ce.s3tc.ctex"
metadata={ metadata={
"imported_formats": ["s3tc_bptc"], "imported_formats": ["s3tc_bptc"],
"vram_texture": true "vram_texture": true
@@ -14,8 +14,8 @@ generator_parameters={
[deps] [deps]
source_file="res://models/backrooms/backrooms_vr_23.png" source_file="res://maps/backrooms/model/backrooms_vr_23.png"
dest_files=["res://.godot/imported/backrooms_vr_23.png-1d7ffb8aa29d441bde425ede0413250c.s3tc.ctex"] dest_files=["res://.godot/imported/backrooms_vr_23.png-4090a5fff0cc06e162bacc68b3fd83ce.s3tc.ctex"]
[params] [params]

Before

Width:  |  Height:  |  Size: 463 KiB

After

Width:  |  Height:  |  Size: 463 KiB

@@ -3,7 +3,7 @@
importer="texture" importer="texture"
type="CompressedTexture2D" type="CompressedTexture2D"
uid="uid://bt548bd7s4get" uid="uid://bt548bd7s4get"
path.s3tc="res://.godot/imported/backrooms_vr_24.png-584f508479d8bb3725096fabaf30673a.s3tc.ctex" path.s3tc="res://.godot/imported/backrooms_vr_24.png-bf9b213fc302459f00c11b5b6a489a2d.s3tc.ctex"
metadata={ metadata={
"imported_formats": ["s3tc_bptc"], "imported_formats": ["s3tc_bptc"],
"vram_texture": true "vram_texture": true
@@ -14,8 +14,8 @@ generator_parameters={
[deps] [deps]
source_file="res://models/backrooms/backrooms_vr_24.png" source_file="res://maps/backrooms/model/backrooms_vr_24.png"
dest_files=["res://.godot/imported/backrooms_vr_24.png-584f508479d8bb3725096fabaf30673a.s3tc.ctex"] dest_files=["res://.godot/imported/backrooms_vr_24.png-bf9b213fc302459f00c11b5b6a489a2d.s3tc.ctex"]
[params] [params]

Before

Width:  |  Height:  |  Size: 210 KiB

After

Width:  |  Height:  |  Size: 210 KiB

@@ -3,7 +3,7 @@
importer="texture" importer="texture"
type="CompressedTexture2D" type="CompressedTexture2D"
uid="uid://doneeapv77gj1" uid="uid://doneeapv77gj1"
path.s3tc="res://.godot/imported/backrooms_vr_25.png-9c0c3ba7ed9c43115c11c01e6ca76972.s3tc.ctex" path.s3tc="res://.godot/imported/backrooms_vr_25.png-7986d2b08dc2f459f7a7b7872e6a16e1.s3tc.ctex"
metadata={ metadata={
"imported_formats": ["s3tc_bptc"], "imported_formats": ["s3tc_bptc"],
"vram_texture": true "vram_texture": true
@@ -14,8 +14,8 @@ generator_parameters={
[deps] [deps]
source_file="res://models/backrooms/backrooms_vr_25.png" source_file="res://maps/backrooms/model/backrooms_vr_25.png"
dest_files=["res://.godot/imported/backrooms_vr_25.png-9c0c3ba7ed9c43115c11c01e6ca76972.s3tc.ctex"] dest_files=["res://.godot/imported/backrooms_vr_25.png-7986d2b08dc2f459f7a7b7872e6a16e1.s3tc.ctex"]
[params] [params]

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

@@ -3,7 +3,7 @@
importer="texture" importer="texture"
type="CompressedTexture2D" type="CompressedTexture2D"
uid="uid://cg4knissk32f0" uid="uid://cg4knissk32f0"
path.s3tc="res://.godot/imported/backrooms_vr_26.png-d404be6f8ec663c23a65f332e8e41866.s3tc.ctex" path.s3tc="res://.godot/imported/backrooms_vr_26.png-258a94dd265e35223b36b7a90dfee344.s3tc.ctex"
metadata={ metadata={
"imported_formats": ["s3tc_bptc"], "imported_formats": ["s3tc_bptc"],
"vram_texture": true "vram_texture": true
@@ -14,8 +14,8 @@ generator_parameters={
[deps] [deps]
source_file="res://models/backrooms/backrooms_vr_26.png" source_file="res://maps/backrooms/model/backrooms_vr_26.png"
dest_files=["res://.godot/imported/backrooms_vr_26.png-d404be6f8ec663c23a65f332e8e41866.s3tc.ctex"] dest_files=["res://.godot/imported/backrooms_vr_26.png-258a94dd265e35223b36b7a90dfee344.s3tc.ctex"]
[params] [params]

Before

Width:  |  Height:  |  Size: 815 KiB

After

Width:  |  Height:  |  Size: 815 KiB

@@ -3,7 +3,7 @@
importer="texture" importer="texture"
type="CompressedTexture2D" type="CompressedTexture2D"
uid="uid://3gy68ac2rkdx" uid="uid://3gy68ac2rkdx"
path.s3tc="res://.godot/imported/backrooms_vr_3.png-afd56e334c7320959e31f3683f2ccc4f.s3tc.ctex" path.s3tc="res://.godot/imported/backrooms_vr_3.png-4355c26a3df456240cd49a319f1cd5de.s3tc.ctex"
metadata={ metadata={
"imported_formats": ["s3tc_bptc"], "imported_formats": ["s3tc_bptc"],
"vram_texture": true "vram_texture": true
@@ -14,8 +14,8 @@ generator_parameters={
[deps] [deps]
source_file="res://models/backrooms/backrooms_vr_3.png" source_file="res://maps/backrooms/model/backrooms_vr_3.png"
dest_files=["res://.godot/imported/backrooms_vr_3.png-afd56e334c7320959e31f3683f2ccc4f.s3tc.ctex"] dest_files=["res://.godot/imported/backrooms_vr_3.png-4355c26a3df456240cd49a319f1cd5de.s3tc.ctex"]
[params] [params]

Before

Width:  |  Height:  |  Size: 714 KiB

After

Width:  |  Height:  |  Size: 714 KiB

@@ -3,7 +3,7 @@
importer="texture" importer="texture"
type="CompressedTexture2D" type="CompressedTexture2D"
uid="uid://d3verocm47dx8" uid="uid://d3verocm47dx8"
path.s3tc="res://.godot/imported/backrooms_vr_4.png-93a12f44d34aa99fbfe8479f9f95dda0.s3tc.ctex" path.s3tc="res://.godot/imported/backrooms_vr_4.png-4a8967702e5eee6c2b19c6c5d1c0c555.s3tc.ctex"
metadata={ metadata={
"imported_formats": ["s3tc_bptc"], "imported_formats": ["s3tc_bptc"],
"vram_texture": true "vram_texture": true
@@ -14,8 +14,8 @@ generator_parameters={
[deps] [deps]
source_file="res://models/backrooms/backrooms_vr_4.png" source_file="res://maps/backrooms/model/backrooms_vr_4.png"
dest_files=["res://.godot/imported/backrooms_vr_4.png-93a12f44d34aa99fbfe8479f9f95dda0.s3tc.ctex"] dest_files=["res://.godot/imported/backrooms_vr_4.png-4a8967702e5eee6c2b19c6c5d1c0c555.s3tc.ctex"]
[params] [params]

Before

Width:  |  Height:  |  Size: 635 KiB

After

Width:  |  Height:  |  Size: 635 KiB

@@ -3,7 +3,7 @@
importer="texture" importer="texture"
type="CompressedTexture2D" type="CompressedTexture2D"
uid="uid://cnhk7o54biy8d" uid="uid://cnhk7o54biy8d"
path.s3tc="res://.godot/imported/backrooms_vr_5.png-5c53a0e2465ec929aaf203e355069c55.s3tc.ctex" path.s3tc="res://.godot/imported/backrooms_vr_5.png-92bd0164b916a3cf31302520ddb50d1d.s3tc.ctex"
metadata={ metadata={
"imported_formats": ["s3tc_bptc"], "imported_formats": ["s3tc_bptc"],
"vram_texture": true "vram_texture": true
@@ -14,8 +14,8 @@ generator_parameters={
[deps] [deps]
source_file="res://models/backrooms/backrooms_vr_5.png" source_file="res://maps/backrooms/model/backrooms_vr_5.png"
dest_files=["res://.godot/imported/backrooms_vr_5.png-5c53a0e2465ec929aaf203e355069c55.s3tc.ctex"] dest_files=["res://.godot/imported/backrooms_vr_5.png-92bd0164b916a3cf31302520ddb50d1d.s3tc.ctex"]
[params] [params]

Before

Width:  |  Height:  |  Size: 888 KiB

After

Width:  |  Height:  |  Size: 888 KiB

@@ -3,7 +3,7 @@
importer="texture" importer="texture"
type="CompressedTexture2D" type="CompressedTexture2D"
uid="uid://cjglgsq24tgpr" uid="uid://cjglgsq24tgpr"
path.s3tc="res://.godot/imported/backrooms_vr_6.png-9ef6d8013d662a2fe026a614c10ff766.s3tc.ctex" path.s3tc="res://.godot/imported/backrooms_vr_6.png-087f2ed0886aa3d81ebc9b5b30ba5bb4.s3tc.ctex"
metadata={ metadata={
"imported_formats": ["s3tc_bptc"], "imported_formats": ["s3tc_bptc"],
"vram_texture": true "vram_texture": true
@@ -14,8 +14,8 @@ generator_parameters={
[deps] [deps]
source_file="res://models/backrooms/backrooms_vr_6.png" source_file="res://maps/backrooms/model/backrooms_vr_6.png"
dest_files=["res://.godot/imported/backrooms_vr_6.png-9ef6d8013d662a2fe026a614c10ff766.s3tc.ctex"] dest_files=["res://.godot/imported/backrooms_vr_6.png-087f2ed0886aa3d81ebc9b5b30ba5bb4.s3tc.ctex"]
[params] [params]

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 37 KiB

@@ -3,7 +3,7 @@
importer="texture" importer="texture"
type="CompressedTexture2D" type="CompressedTexture2D"
uid="uid://7ctu8yeskavl" uid="uid://7ctu8yeskavl"
path.s3tc="res://.godot/imported/backrooms_vr_7.png-08770a42a1db34f7b66ca5ade29b3692.s3tc.ctex" path.s3tc="res://.godot/imported/backrooms_vr_7.png-6349183f10a2172a16f16a287d42d208.s3tc.ctex"
metadata={ metadata={
"imported_formats": ["s3tc_bptc"], "imported_formats": ["s3tc_bptc"],
"vram_texture": true "vram_texture": true
@@ -14,8 +14,8 @@ generator_parameters={
[deps] [deps]
source_file="res://models/backrooms/backrooms_vr_7.png" source_file="res://maps/backrooms/model/backrooms_vr_7.png"
dest_files=["res://.godot/imported/backrooms_vr_7.png-08770a42a1db34f7b66ca5ade29b3692.s3tc.ctex"] dest_files=["res://.godot/imported/backrooms_vr_7.png-6349183f10a2172a16f16a287d42d208.s3tc.ctex"]
[params] [params]

Before

Width:  |  Height:  |  Size: 898 KiB

After

Width:  |  Height:  |  Size: 898 KiB

@@ -3,7 +3,7 @@
importer="texture" importer="texture"
type="CompressedTexture2D" type="CompressedTexture2D"
uid="uid://dsjo2hg0a0tel" uid="uid://dsjo2hg0a0tel"
path.s3tc="res://.godot/imported/backrooms_vr_8.png-d336a3b519d985285c5ac1f5b88e7302.s3tc.ctex" path.s3tc="res://.godot/imported/backrooms_vr_8.png-8b4d0917952d2be94fc1f557297f0e1b.s3tc.ctex"
metadata={ metadata={
"imported_formats": ["s3tc_bptc"], "imported_formats": ["s3tc_bptc"],
"vram_texture": true "vram_texture": true
@@ -14,8 +14,8 @@ generator_parameters={
[deps] [deps]
source_file="res://models/backrooms/backrooms_vr_8.png" source_file="res://maps/backrooms/model/backrooms_vr_8.png"
dest_files=["res://.godot/imported/backrooms_vr_8.png-d336a3b519d985285c5ac1f5b88e7302.s3tc.ctex"] dest_files=["res://.godot/imported/backrooms_vr_8.png-8b4d0917952d2be94fc1f557297f0e1b.s3tc.ctex"]
[params] [params]

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

@@ -3,7 +3,7 @@
importer="texture" importer="texture"
type="CompressedTexture2D" type="CompressedTexture2D"
uid="uid://lg637sqtxhyx" uid="uid://lg637sqtxhyx"
path.s3tc="res://.godot/imported/backrooms_vr_9.png-84b2fc2b5b2304a3aaac1bf9ef43ec9b.s3tc.ctex" path.s3tc="res://.godot/imported/backrooms_vr_9.png-b548090fbf6542758b82625ab5195ef5.s3tc.ctex"
metadata={ metadata={
"imported_formats": ["s3tc_bptc"], "imported_formats": ["s3tc_bptc"],
"vram_texture": true "vram_texture": true
@@ -14,8 +14,8 @@ generator_parameters={
[deps] [deps]
source_file="res://models/backrooms/backrooms_vr_9.png" source_file="res://maps/backrooms/model/backrooms_vr_9.png"
dest_files=["res://.godot/imported/backrooms_vr_9.png-84b2fc2b5b2304a3aaac1bf9ef43ec9b.s3tc.ctex"] dest_files=["res://.godot/imported/backrooms_vr_9.png-b548090fbf6542758b82625ab5195ef5.s3tc.ctex"]
[params] [params]
+3 -3
View File
@@ -4,8 +4,8 @@ extends Node3D
# Чтобы добавить карту — допиши запись в PORTALS: порталы встанут в ряд по центру (влезает 6). # Чтобы добавить карту — допиши запись в PORTALS: порталы встанут в ряд по центру (влезает 6).
const PORTALS := [ const PORTALS := [
{"title": "SANDBOX", "map": "world", "scene": "res://main.tscn", "pack": "world.pck", "color": Color(0.2, 0.9, 1.0)}, {"title": "SANDBOX", "map": "world", "scene": "res://maps/sandbox/sandbox.tscn", "pack": "world.pck", "color": Color(0.2, 0.9, 1.0)},
{"title": "BACKROOMS", "map": "backrooms", "scene": "res://World/Backrooms/backrooms.tscn", "pack": "backrooms.pck", "color": Color(1.0, 0.85, 0.25)}, {"title": "BACKROOMS", "map": "backrooms", "scene": "res://maps/backrooms/backrooms.tscn", "pack": "backrooms.pck", "color": Color(1.0, 0.85, 0.25)},
] ]
const ROOM_WIDTH := 28.0 const ROOM_WIDTH := 28.0
@@ -76,7 +76,7 @@ func _add_box(body: StaticBody3D, size: Vector3, pos: Vector3, mat: Material) ->
# Порталы вдоль северной стены лицом в комнату, по центру # Порталы вдоль северной стены лицом в комнату, по центру
func _build_portals() -> void: func _build_portals() -> void:
var portal_script := load("res://World/portal.gd") var portal_script := load("res://objects/portal.gd")
var z := -ROOM_DEPTH * 0.5 + 1.0 var z := -ROOM_DEPTH * 0.5 + 1.0
for i in PORTALS.size(): for i in PORTALS.size():
var info: Dictionary = PORTALS[i] var info: Dictionary = PORTALS[i]
+2 -2
View File
@@ -1,7 +1,7 @@
[gd_scene format=3] [gd_scene format=3]
[ext_resource type="Script" path="res://World/Hub/hub.gd" id="1_hub"] [ext_resource type="Script" path="res://maps/hub/hub.gd" id="1_hub"]
[ext_resource type="PackedScene" uid="uid://c3efk6k8xnf57" path="res://Player/Player.tscn" id="2_player"] [ext_resource type="PackedScene" uid="uid://c3efk6k8xnf57" path="res://player/player.tscn" id="2_player"]
[node name="Hub" type="Node3D"] [node name="Hub" type="Node3D"]
script = ExtResource("1_hub") script = ExtResource("1_hub")
+19
View File
@@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://ddsynauqeoppw"
path="res://.godot/imported/tron_01.ogg-e0fa4b053d4048643e525d26032ad38e.oggvorbisstr"
[deps]
source_file="res://maps/sandbox/audio/tron_01.ogg"
dest_files=["res://.godot/imported/tron_01.ogg-e0fa4b053d4048643e525d26032ad38e.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4
@@ -4,12 +4,12 @@ importer="scene"
importer_version=1 importer_version=1
type="PackedScene" type="PackedScene"
uid="uid://cvrj2ay2fq4x8" uid="uid://cvrj2ay2fq4x8"
path="res://.godot/imported/Floor.glb-40cfa4cba9194cac4f12dba26232903b.scn" path="res://.godot/imported/Floor.glb-4d6193c3b6fd9b68511a715ce5a0aefb.scn"
[deps] [deps]
source_file="res://World/Floor/main_floor/Floor.glb" source_file="res://maps/sandbox/floor/Floor.glb"
dest_files=["res://.godot/imported/Floor.glb-40cfa4cba9194cac4f12dba26232903b.scn"] dest_files=["res://.godot/imported/Floor.glb-4d6193c3b6fd9b68511a715ce5a0aefb.scn"]
[params] [params]
@@ -3,7 +3,7 @@
importer="texture" importer="texture"
type="CompressedTexture2D" type="CompressedTexture2D"
uid="uid://b882qi7w2eavh" uid="uid://b882qi7w2eavh"
path.s3tc="res://.godot/imported/Floor_Вставленное изображение.png-ff0a799dad6a9da0f6663bac92570f5c.s3tc.ctex" path.s3tc="res://.godot/imported/Floor_Вставленное изображение.png-91526e6b18f388bd08e95ab0add9fc52.s3tc.ctex"
metadata={ metadata={
"imported_formats": ["s3tc_bptc"], "imported_formats": ["s3tc_bptc"],
"vram_texture": true "vram_texture": true
@@ -14,8 +14,8 @@ generator_parameters={
[deps] [deps]
source_file="res://World/Floor/main_floor/Floor_Вставленное изображение.png" source_file="res://maps/sandbox/floor/Floor_Вставленное изображение.png"
dest_files=["res://.godot/imported/Floor_Вставленное изображение.png-ff0a799dad6a9da0f6663bac92570f5c.s3tc.ctex"] dest_files=["res://.godot/imported/Floor_Вставленное изображение.png-91526e6b18f388bd08e95ab0add9fc52.s3tc.ctex"]
[params] [params]

Some files were not shown because too many files have changed in this diff Show More