SashegDev be3bfc2369
Build Delta / Build paperclip jar (push) Successful in 7m9s
perf(config): 2 chunk workers instead of 1, gen-parallelism on
worker-threads: -1 means "auto", and auto on 4 cores resolves to ONE worker.
MoonriseCommon.adjustWorkerThreads halves availableProcessors and then, if the
result is <= 4, collapses it further - 4/2=2, and 2<=3 becomes 1. That single
thread generated the spawn region serially.

Measured on zern.cc (4 vCPU, 8 GB), same jar, same flags, cold world vs warm:

                    before   after
  cold, overworld     6770ms  2944ms
  cold, nether        1368ms   888ms
  cold, total         38.5s   24.9s
  warm, overworld     1084ms   803ms
  warm, total         16.4s   12.2s

gen-parallelism "default" already resolved to true at startup, so it is now
spelled out rather than left to string comparison at boot.

3 workers is deliberately NOT set here. The main thread plus Netty share the
same 4 cores, and going to 3 trades main-thread headroom for generation that
does not happen every tick. That needs a profile, not a guess.

Also recorded the corollary: player-max-concurrent-chunk-generates is a rate
limiter, not an accelerator. Speeding up generation without raising it just
makes the engine faster behind a throttle that was sized for the old speed.
2026-10-03 11:58:59 +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%