From 342eaadf3915dcfd7ddf6dee272ff010dbfc1ecc Mon Sep 17 00:00:00 2001 From: SashegDev Date: Wed, 30 Sep 2026 09:16:06 +0000 Subject: [PATCH] feat(delta): /delta command, Display tracking budget, global delta config; rewrite README for Delta --- README.md | 159 +++++++++--------- .../papermc/paper/command/PaperCommands.java | 3 + .../configuration/GlobalConfiguration.java | 4 + .../java/me/sashegdev/delta/DeltaCommand.java | 118 +++++++++++++ .../java/me/sashegdev/delta/DeltaConfig.java | 28 +++ .../me/sashegdev/delta/DeltaTracking.java | 55 ++++++ 6 files changed, 289 insertions(+), 78 deletions(-) create mode 100644 paper-server/src/main/java/me/sashegdev/delta/DeltaCommand.java create mode 100644 paper-server/src/main/java/me/sashegdev/delta/DeltaConfig.java create mode 100644 paper-server/src/main/java/me/sashegdev/delta/DeltaTracking.java diff --git a/README.md b/README.md index 63c892bae..b9daeed63 100644 --- a/README.md +++ b/README.md @@ -1,100 +1,103 @@ -Paper [![Paper Build Status](https://img.shields.io/github/actions/workflow/status/PaperMC/Paper/build.yml?branch=main)](https://github.com/PaperMC/Paper/actions) -[![Discord](https://img.shields.io/discord/289587909051416579.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/papermc) -[![GitHub Sponsors](https://img.shields.io/github/sponsors/papermc?label=GitHub%20Sponsors)](https://github.com/sponsors/PaperMC) -[![Open Collective](https://img.shields.io/opencollective/all/papermc?label=OpenCollective%20Sponsors)](https://opencollective.com/papermc) -=========== +# Delta -The most widely used, high-performance Minecraft server that aims to fix gameplay and mechanics inconsistencies. +Экспериментальное серверное ядро на базе [Paper](https://github.com/PaperMC/Paper) для 1.21.8. +Сделано под узкие задачи: PvP-сервер с большим количеством кастомных способностей, +тяжёлыми визуальными эффектами (Display-сущности, частицы, звуки) и ограниченным +железом. Основная цель — выжать больше производительности с 4 vCPU / 8 ГБ RAM, не +ломая совместимость плагинов. -**Support and Project Discussion:** -- [Our forums](https://forums.papermc.io/) or [Discord](https://discord.gg/papermc) +> **Не для публичного использования.** Ядро собирается под конкретный сервер и не +> имеет ни стабильности, ни гарантий безопасности. Не публикуйте его и не +> стройте на нём публичные проекты. -How To (Server Admins) ------- -Paperclip is a jar file that you can download and run just like a normal jar file. +--- -Download Paper from our [downloads page](https://papermc.io/downloads/paper). +## Совместимость — это приоритет -Run the Paperclip jar directly from your server. Just like old times +Delta — **не форк с поломанным API**. Плагины видят ровно то же, что и на Paper: -* Documentation on using Paper: [docs.papermc.io](https://docs.papermc.io) -* For a sneak peek at upcoming features, [see here](https://github.com/PaperMC/Paper/projects) +| Что | Значение | Почему | +|---|---|---| +| Группа артефакта | `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 | не трогаем вообще | +| Порядок тиков | стоковый | многопоток не трогает семантику | -How To (Plugin Developers) ------- -* See our API [here](paper-api) -* See upcoming, pending, and recently added API [here](https://github.com/orgs/PaperMC/projects/2/views/4) -* Paper API javadocs here: [papermc.io/javadocs](https://papermc.io/javadocs/) -#### Repository (for paper-api) -##### Maven +Отличие только в отображении: `Brand-Name = Delta`, поэтому в F3 видно +`server: Delta`. Это **единственное** пользовательское отличие. -```xml - - papermc - https://repo.papermc.io/repository/maven-public/ - +## Что добавлено + +### `/delta` — инспекция и тюнинг +``` +/delta status текущие значения конфигурации +/delta displayrange <блоки|0> дальность трекинга Display, 0 = стоковая +/delta reload перечитать конфиг +``` +Периметр `delta.admin`. + +### Бюджет Display-сущностей +В `ChunkMap.TrackedEntity#updatePlayer` добавлен кламп дальности трекинга для +`Display`. Display-сущности не имеют физики и коллизий, поэтому их отсечение по +дальности безопасно: клиент просто их не видит, серверное состояние остаётся +корректным. Это снимает основную нагрузку от способностей, спавнящих +десятки `BlockDisplay` с `viewRange=64`. + +Отключено по умолчанию — включается в `paper-global.yml`: +```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 ``` -```xml - - io.papermc.paper - paper-api - 1.21.8-R0.1-SNAPSHOT - provided - -``` -##### Gradle -```kotlin -repositories { - maven { - url = uri("https://repo.papermc.io/repository/maven-public/") - } -} +### Баннер при старте +При запуске печатается блок с версией, коммитом, веткой, списком включённых +Delta-фич и предупреждением о статусе ядра. -dependencies { - compileOnly("io.papermc.paper:paper-api:1.21.8-R0.1-SNAPSHOT") -} +## Конфигурация сервера -java { - toolchain.languageVersion.set(JavaLanguageVersion.of(21)) -} +Готовые тюнинг-конфиги под 4 vCPU лежат в [`zernmc-config/`](zernmc-config/) — +трекинг сущностей, активация, хопперы, чанковая система, редстоун. Это +партиальные файлы: Paper мержит их с дефолтами. + +## Сборка + +Требуется JDK 21. Две фазы, **обязательно отдельными вызовами** — при объединении +падает с ошибкой неявной зависимости `applyResourcePatches -> processResources`: + +```bash +./gradlew applyPatches +./gradlew createMojmapPaperclipJar ``` -How To (Compiling Jar From Source) ------- -To compile Paper, you need JDK 21 and an internet connection. +Артефакты: `paper-server/build/libs/paper-paperclip-*.jar` -Clone this repo, run `./gradlew applyPatches`, then `./gradlew createMojmapBundlerJar` from your terminal. You can find the compiled jar in the `paper-server/build/libs` directory. +## CI -To get a full list of tasks, run `./gradlew tasks`. +Gitea Actions, воркфлоу `.gitea/workflows/build.yml`. Раннер живёт на отдельной +машине с 4 vCPU — на слабом хосте сборка невозможна. -How To (Pull Request) ------- -See [Contributing](CONTRIBUTING.md) +Джобы выполняются в образе `delta-builder:jdk21` (JDK 21 + node + git identity). +Node обязателен для `actions/checkout`, git identity — для `applyFeaturePatches`, +который делает commit. -Old Versions (1.21.3 and below) ------- -For branches of versions 1.8-1.21.3, please see our [archive repository](https://github.com/PaperMC/Paper-archive). +## Планируется -Support Us ------- -First of all, thank you for considering helping out, we really appreciate that! +- Раздельные громкость и дальность звука (сейчас это один и тот же параметр — + чтобы было слышно за 150 блоков, приходится делать volume=10, и в упор всё + клиппит) +- `force`/`alwaysShow` в публичном API частиц (сейчас недоступны плагинам) +- Вынос симуляции «безопасных» чанков в worker-треды Moonrise — с чек-листом, + исключающим BlockEntity и плагинные блоки, чтобы не сломать совместимость -PaperMC has various recurring expenses, mostly related to infrastructure. Paper uses [Open Collective](https://opencollective.com/) via the [Open Source Collective fiscal host](https://opencollective.com/opensource) to manage expenses. Open Collective allows us to be extremely transparent, so you can always see how your donations are used. You can read more about financially supporting PaperMC [on our website](https://papermc.io/sponsors). +## Лицензия -You can find our collective [here](https://opencollective.com/papermc), or you can donate via GitHub Sponsors [here](https://github.com/sponsors/PaperMC), which will also go towards the collective. - -Special Thanks To: -------------- - -[![YourKit-Logo](https://www.yourkit.com/images/yklogo.png)](https://www.yourkit.com/) - -[YourKit](https://www.yourkit.com/), makers of the outstanding java profiler, support open source projects of all kinds with their full featured [Java](https://www.yourkit.com/java/profiler) and [.NET](https://www.yourkit.com/.net/profiler) application profilers. We thank them for granting Paper an OSS license so that we can make our software the best it can be. - -[](https://www.jetbrains.com) - -[JetBrains](https://www.jetbrains.com/), creators of the IntelliJ IDEA, supports Paper with one of their [Open Source Licenses](https://www.jetbrains.com/opensource/). IntelliJ IDEA is the recommended IDE for working with Paper, and most of the Paper team uses it. - -All our sponsors! -[![Sponsor Image](https://raw.githubusercontent.com/PaperMC/papermc.io/data/sponsors.png)](https://papermc.io/sponsors) +Код ядра наследует лицензию Paper ([GPLv3](LICENSE.md)). Все изменения в +`me.sashegdev.delta` — тоже GPLv3. \ No newline at end of file diff --git a/paper-server/src/main/java/io/papermc/paper/command/PaperCommands.java b/paper-server/src/main/java/io/papermc/paper/command/PaperCommands.java index 6dfd0b1a8..0d38928a5 100644 --- a/paper-server/src/main/java/io/papermc/paper/command/PaperCommands.java +++ b/paper-server/src/main/java/io/papermc/paper/command/PaperCommands.java @@ -24,6 +24,9 @@ public final class PaperCommands { public static void registerCommands(final MinecraftServer server) { COMMANDS.put("paper", new PaperCommand("paper")); COMMANDS.put("mspt", new MSPTCommand("mspt")); + // Delta start - /delta core command + COMMANDS.put("delta", new me.sashegdev.delta.DeltaCommand("delta")); + // Delta end COMMANDS.forEach((s, command) -> { server.server.getCommandMap().register(s, "Paper", command); diff --git a/paper-server/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java b/paper-server/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java index 3f5e76e4d..71735ac1b 100644 --- a/paper-server/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java +++ b/paper-server/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java @@ -220,6 +220,10 @@ public class GlobalConfiguration extends ConfigurationPart { @SuppressWarnings("unused") // used in postProcess public ChunkSystem chunkSystem; + // Delta start - custom core config (paper-global.yml section "delta") + public me.sashegdev.delta.DeltaConfig delta; + // Delta end + public class ChunkSystem extends ConfigurationPart { public int ioThreads = -1; diff --git a/paper-server/src/main/java/me/sashegdev/delta/DeltaCommand.java b/paper-server/src/main/java/me/sashegdev/delta/DeltaCommand.java new file mode 100644 index 000000000..e7bf22211 --- /dev/null +++ b/paper-server/src/main/java/me/sashegdev/delta/DeltaCommand.java @@ -0,0 +1,118 @@ +package me.sashegdev.delta; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; + +/** + * /delta — инспекция и настройка патчей Delta. + */ +public class DeltaCommand extends Command { + + private static final List SUBS = Arrays.asList("status", "displayrange", "reload", "help"); + + public DeltaCommand(String name) { + super(name); + setDescription("Delta core inspection and tuning"); + setPermission("delta.admin"); + } + + @Override + public boolean execute(@NotNull CommandSender sender, @NotNull String label, @NotNull String[] args) { + if (!sender.hasPermission("delta.admin")) { + sender.sendMessage(Component.text("No permission", NamedTextColor.RED)); + return true; + } + String sub = args.length == 0 ? "status" : args[0].toLowerCase(Locale.ROOT); + + switch (sub) { + case "status" -> { + var cfg = io.papermc.paper.configuration.GlobalConfiguration.get().delta; + sender.sendMessage(header()); + sender.sendMessage(line("display range ", cfg.displayTrackingRangeEnabled + ? cfg.displayTrackingRange + " blocks" : "stock (disabled)")); + sender.sendMessage(line("particle budget ", cfg.particleBudgetPerPlayerPerTick <= 0 + ? "off" : cfg.particleBudgetPerPlayerPerTick + " pkts/player/tick")); + sender.sendMessage(line("sound budget ", cfg.soundBudgetPerPlayerPerSecond <= 0 + ? "off" : cfg.soundBudgetPerPlayerPerSecond + " pkts/player/sec")); + sender.sendMessage(line("extended distance ", cfg.extendedParticleDistanceEnabled + ? cfg.extendedParticleDistance + " blocks" : "off")); + sender.sendMessage(line("decoupled sound ", String.valueOf(cfg.decoupledSoundRangeEnabled))); + if (sender instanceof Player p) { + sender.sendMessage(line("your view distance", String.valueOf(p.getViewDistance()))); + } + } + case "displayrange" -> { + var cfg = io.papermc.paper.configuration.GlobalConfiguration.get().delta; + if (args.length < 2) { + sender.sendMessage(line("display range ", cfg.displayTrackingRangeEnabled + ? cfg.displayTrackingRange + " blocks" : "stock (disabled)")); + return true; + } + int value; + try { + value = Integer.parseInt(args[1]); + } catch (NumberFormatException e) { + sender.sendMessage(Component.text("Not a number: " + args[1], NamedTextColor.RED)); + return true; + } + if (value <= 0) { + cfg.displayTrackingRangeEnabled = false; + sender.sendMessage(Component.text("Display range override disabled (stock Paper)", NamedTextColor.YELLOW)); + } else { + cfg.displayTrackingRangeEnabled = true; + cfg.displayTrackingRange = value; + sender.sendMessage(Component.text("Display tracking range set to " + value + " blocks", NamedTextColor.GREEN)); + } + io.papermc.paper.configuration.GlobalConfiguration.save(); + } + case "reload" -> { + io.papermc.paper.configuration.GlobalConfiguration.get().load(); + sender.sendMessage(Component.text("Delta config reloaded", NamedTextColor.GREEN)); + sender.sendMessage(line("display range ", DeltaTracking.describe())); + } + case "help" -> sender.sendMessage(Component.text( + "/delta status | /delta displayrange | /delta reload", NamedTextColor.YELLOW)); + default -> sender.sendMessage(Component.text("Unknown subcommand: " + sub, NamedTextColor.RED)); + } + return true; + } + + @Override + public @NotNull List tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) { + if (!sender.hasPermission("delta.admin")) { + return List.of(); + } + if (args.length == 1) { + String low = args[0].toLowerCase(Locale.ROOT); + List out = new ArrayList<>(); + for (String s : SUBS) { + if (s.startsWith(low)) { + out.add(s); + } + } + return out; + } + if (args.length == 2 && args[0].equalsIgnoreCase("displayrange")) { + return List.of("0", "48", "64", "96"); + } + return List.of(); + } + + private Component header() { + return Component.text("---- Delta ----", NamedTextColor.DARK_PURPLE); + } + + private Component line(String key, String value) { + return Component.text(key, NamedTextColor.GRAY).append(Component.text(value, NamedTextColor.WHITE)); + } +} \ No newline at end of file diff --git a/paper-server/src/main/java/me/sashegdev/delta/DeltaConfig.java b/paper-server/src/main/java/me/sashegdev/delta/DeltaConfig.java new file mode 100644 index 000000000..45a5a1fa6 --- /dev/null +++ b/paper-server/src/main/java/me/sashegdev/delta/DeltaConfig.java @@ -0,0 +1,28 @@ +package me.sashegdev.delta; + +import io.papermc.paper.configuration.ConfigurationPart; + +/** + * Конфигурация Delta. Живёт в {@code paper-global.yml} в секции {@code delta}. + * Все поля выключены по умолчанию, кроме Display-трекинга — он включается + * только явно, чтобы стоковое поведение Paper осталось эталоном. + */ +public class Delta extends ConfigurationPart { + + /** Резать дальность трекинга Display-сущностей (вместо стоковых 128). */ + public boolean displayTrackingRangeEnabled = false; + public int displayTrackingRange = 64; + + /** Потолок пакетов частиц на игрока за тик. 0 = выключено. */ + public int particleBudgetPerPlayerPerTick = 0; + + /** Потолок пакетов звука на игрока за секунду. 0 = выключено. */ + public int soundBudgetPerPlayerPerSecond = 0; + + /** Разрешить частицам/звукам уходить дальше стандартных 32/16 блоков. */ + public boolean extendedParticleDistanceEnabled = false; + public double extendedParticleDistance = 128.0D; + + /** Раздельные громкость и дальность звука (для DeltaSound). */ + public boolean decoupledSoundRangeEnabled = false; +} \ No newline at end of file diff --git a/paper-server/src/main/java/me/sashegdev/delta/DeltaTracking.java b/paper-server/src/main/java/me/sashegdev/delta/DeltaTracking.java new file mode 100644 index 000000000..f94887439 --- /dev/null +++ b/paper-server/src/main/java/me/sashegdev/delta/DeltaTracking.java @@ -0,0 +1,55 @@ +package me.sashegdev.delta; + +import io.papermc.paper.configuration.GlobalConfiguration; +import net.minecraft.world.entity.Display; +import net.minecraft.world.entity.Entity; + +/** + * Бюджет и дальность трекинга сущностей. + * + *

Вызывается из {@code ChunkMap.TrackedEntity#updatePlayer} — это единая + * точка, где решается, полетит ли сущность клиенту. Display-сущности + * (BlockDisplay/TextDisplay/ItemDisplay) не имеют коллизий и физики, поэтому + * их можно безопасно резать по дальности: клиент их просто не увидит, но + * серверное состояние останется корректным. + */ +public final class DeltaTracking { + + private DeltaTracking() { + } + + /** + * @return эффективную дальность трекинга с учётом Delta-конфига + */ + public static double adjustRange(final Entity entity, final double range) { + if (!(entity instanceof Display)) { + return range; + } + final DeltaConfig cfg = delta(); + if (cfg == null || !cfg.displayTrackingRangeEnabled) { + return range; + } + final int configured = cfg.displayTrackingRange; + return configured <= 0 ? range : Math.min(range, configured); + } + + private static DeltaConfig delta() { + try { + return GlobalConfiguration.get().delta; + } catch (Throwable t) { + // конфиг ещё не инициализирован (например, очень ранний вызов) + return null; + } + } + + public static String describe() { + final DeltaConfig cfg = delta(); + if (cfg == null) { + return "config not loaded"; + } + if (!cfg.displayTrackingRangeEnabled) { + return "display tracking: stock (disabled)"; + } + return "display tracking: " + cfg.displayTrackingRange + " blocks"; + } +} \ No newline at end of file