SashegDev 42f48fea43
Build Delta / Build paperclip jar (push) Successful in 7m3s
perf(config): document and justify the heap-shrink flags
Добавлен -XX:MaxHeapFreeRatio=30 -XX:MinHeapFreeRatio=10 в start.sh на проде
и зафиксирован в README с замерами, потому что флаги JVM не переносятся
копированием конфигов — при переезде они теряются молча.

Замер, мир 18 МБ, 0 игроков, 100 с простоя:

              коммит   живые   RSS
  без флагов   630 МБ  278 МБ  0.99 ГБ
  с флагами    389 МБ  274 МБ  0.69 ГБ

Минус 300 МБ RSS без единого ручного GC.

Причина была не в отсутствии Full GC: jcmd GC.run отрабатывает, живые падают
289 -> 248 МБ, но коммит остаётся на 725 МБ. G1 сжимает кучу только когда
свободно выше MaxHeapFreeRatio, а он по умолчанию 70. При 630 МБ коммита
свободно было 56% — ниже порога, и 350 МБ держались вечно.

Паузы Mixed (то, что ест кадр в рантайме) не ухудшились: median 11.0 -> 9.8,
p95 28.3 -> 28.1, max 38.1 -> 28.7 мс. Concurrent-циклов стало больше
(11 -> 18), общий p95 по всем паузам в одном прогоне вырос 126 -> 270 мс —
это замер без игроков, под нагрузкой флаг ещё предстоит перемерить.

DisableExplicitGC оставлен намеренно: он делает System.gc() no-op, и именно
поэтому освобождение сделано флагом, а не вызовом GC. README теперь
описывает профиль как 15-25 игроков с пиком 34, а не 30-40.
2026-10-04 13:55:14 +00:00
2024-12-12 10:58:58 +01:00
2025-06-17 15:45:25 +02:00
2025-04-12 17:27:00 +02:00
2025-04-12 17:27:00 +02:00
2024-12-12 10:58:58 +01:00

Delta

Экспериментальное серверное ядро на базе 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.

Отключено по умолчанию:

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 разделывает эти параметры:

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/ — трекинг сущностей, активация, хопперы, чанковая система, редстоун. Это партиальные файлы: Paper мержит их с дефолтами.

Сборка

Требуется JDK 21. Две фазы, обязательно отдельными вызовами — при объединении падает с ошибкой неявной зависимости applyResourcePatches -> processResources:

./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). Все изменения в me.sashegdev.delta — тоже GPLv3.

S
Description
Tschhh....
Readme 52 MiB
Languages
Java 100%