# Delta Экспериментальное серверное ядро на базе [Paper](https://github.com/PaperMC/Paper) для 1.21.8. Сделано под узкие задачи: PvP-сервер с большим количеством кастомных способностей, тяжёлыми визуальными эффектами (Display-сущности, частицы, звуки) и ограниченным железом. Основная цель — выжать больше производительности с 4 vCPU / 8 ГБ RAM, не ломая совместимость плагинов. > **Не для публичного использования.** Ядро собирается под конкретный сервер и не > имеет ни стабильности, ни гарантий безопасности. Не публикуйте его и не > стройте на нём публичные проекты. --- ## Совместимость — это приоритет Delta — **не форк с поломанным API**. Плагины видят ровно то же, что и на Paper: | Что | Значение | Почему | |---|---|---| | Группа артефакта | `io.papermc.paper` | `Versioning` читает `pom.properties` по пути группы — смена ломает `Bukkit.getBukkitVersion()` | | Версия | `1.21.8-R0.1-SNAPSHOT` | плагины парсят эту строку под API-фичи | | `Brand-Id` | `papermc:paper` | по нему работает `ServerBuildInfo#isBrandCompatible` | | Пакеты | `io.papermc.paper.*`, `net.minecraft.*`, Bukkit API | не трогаем вообще | | Порядок тиков | стоковый | многопоток не трогает семантику | Отличие только в отображении: `Brand-Name = Delta`, поэтому в F3 видно `server: Delta`. Это **единственное** пользовательское отличие. Все новые классы лежат в `me.sashegdev.delta`. Методы Bukkit API, которых Paper не хватает, добавлены как **default-методы** — так ничего не ломается бинарно, и плагин, который на них ссылается, продолжает работать на стоковом Paper через fallback. ## Что добавлено ### `/delta` — инспекция и тюнинг ``` /delta status текущие значения конфигурации /delta displayrange <блоки|0> дальность трекинга Display, 0 = стоковая /delta displaylead <тики|0> предсказание позиции Display, 0 = стоковое /delta clear сбросить Display-настройки к стоку /delta help ``` Периметр `delta.admin`. Всё, что меняется через `/delta`, **пишется в `paper-global.yml`** и переживает рестарт. Paper не даёт публичного API для записи глобального конфига, поэтому добавлен `saveGlobalSection()`: он пишет только секцию `delta` и не трогает остальные узлы файла, так что сломать чужие настройки невозможно. ### Бюджет Display-сущностей Кламп дальности трекинга для `Display` живёт в `org.spigotmc.TrackingRange#getEntityTrackingRange` — это git-отслеживаемый исходник Paper, поэтому декомпилированный код Minecraft патчить не пришлось. Display-сущности не имеют физики и коллизий, поэтому их отсечение по дальности безопасно: клиент просто их не видит, серверное состояние остаётся корректным. Это снимает основную нагрузку от способностей, спавнящих десятки `BlockDisplay` с `viewRange=64`. Отключено по умолчанию: ```yaml delta: display-tracking-range-enabled: false display-tracking-range: 64 particle-budget-per-player-per-tick: 0 sound-budget-per-player-per-second: 0 extended-particle-distance-enabled: false extended-particle-distance: 128.0 decoupled-sound-range-enabled: false display: predictive-lead-ticks: 3 sound: propagation-blocks-per-tick: 10.0 decay-exponent: 4.0 client-compensation: false min-audible-gain: 0.004 ``` ### Звук: раздельные громкость и дальность В ванилле слышимая дальность выводится из громкости (`SoundEvent#getRange = 16 * volume`). Единственный способ сделать звук слышимым за 150 блоков — `volume=10`, и тогда всё клиппит в упор. Delta разделывает эти параметры: ```java world.playSound(loc, sound, category, volume, pitch, range); world.playSoundAttenuated(loc, sound, category, volume, pitch, range); ``` `playSoundAttenuated` отправляет **ровно один пакет на игрока**, отложенный до тика прихода волны. Кадрового обновления громкости нет вообще — иначе при десятке источников это N пакетов × M игроков каждый тик, то есть ровно тот трафик, который здесь и убирается. Кривая нормализованная: `gain = volume * (1 - d/range)^exponent`. Ноль ровно на `range`, поэтому параметр дистанции осмыслен. Физический `1/r` не годится: с порогом слышимости он срезал бы всё дальше ~1000 блоков и `range` просто не влиял бы на результат. Скорость волны — 10 блоков/тик, то есть 100 блоков за 0.5 с. ### Плавность быстрых визуалов `DeltaDisplay` держит политику предсказывающей телепортации. Display двигается пакетами позиции, и клиент интерполирует их у себя — на реальном пинге визуал отстаёт и дёргается. Лечится не частыми пакетами (это ровно тот трафик, который мы убираем), а сдвигом старта: визуал рождается сразу на N тиков вперёд по своей же траектории, и `teleport_duration = N` говорит клиенту растянуть телепорт на эти же N тиков. Клиент никогда не отстаёт, пакетов столько же. Диапазон 0–10 тиков. `teleport_duration` — уже стоковый Bukkit API, поэтому Delta не добавляет плагинам новых методов: политика централизована, сдвиг позиции делает сам плагин по траектории каждого визуала. ### Баннер при старте При запуске печатается блок с версией, коммитом, веткой, списком Delta-фич и предупреждением о статусе ядра. Баннер выводится из статического инициализатора `PaperBootstrap` **до** парсинга `paper-global.yml`, поэтому строки описывают механизм, а фактические значения показывает `/delta status` — иначе баннер печатал бы дефолты как будто это текущая конфигурация. ## Конфигурация сервера Готовые тюнинг-конфиги под 4 vCPU лежат в [`zernmc-config/`](zernmc-config/) — трекинг сущностей, активация, хопперы, чанковая система, редстоун. Это партиальные файлы: Paper мержит их с дефолтами. ## Сборка Требуется JDK 21. Две фазы, **обязательно отдельными вызовами** — при объединении падает с ошибкой неявной зависимости `applyResourcePatches -> processResources`: ```bash ./gradlew applyPatches ./gradlew createMojmapPaperclipJar ``` Артефакт: `paper-server/build/libs/paper-paperclip-1.21.8-R0.1-SNAPSHOT-mojmap.jar` Имя `delta` в `fill { project("delta") }` влияет только на публикуемые в Maven артефакты, локальный jar всегда остаётся `paper-*`. ## CI Gitea Actions, воркфлоу `.gitea/workflows/build.yml`. Раннер живёт на отдельной машине с 4 vCPU — на слабом хосте сборка невозможна. Джобы выполняются в образе `delta-builder:jdk21` (JDK 21 + node + git identity). Node обязателен для `actions/checkout`, git identity — для `applyFeaturePatches`, который делает commit. Воркфлоу делает обе фазы отдельными шагами: объединённый `createMojmapPaperclipJar` падает на неявной зависимости. ## Планируется - `force`/`alwaysShow` в публичном API частиц (сейчас недоступны плагинам) - Вынос симуляции «безопасных» чанков в worker-треды Moonrise — с чек-листом, исключающим BlockEntity и плагинные блоки, чтобы не сломать совместимость - Калибровка `client-compensation` для затухающего звука: клиент сам гасит позиционный звук, поэтому без компенсации итог тише расчётного. Формула подгоняется по замеру, поэтому вынесена под отдельный тумблер ## Лицензия Код ядра наследует лицензию Paper ([GPLv3](LICENSE.md)). Все изменения в `me.sashegdev.delta` — тоже GPLv3.