Start hard-fork from Luminol

This commit is contained in:
Helvetica Volubi
2026-07-18 01:27:14 +08:00
parent f4aea025c1
commit 0678cc6e46
365 changed files with 28337 additions and 657 deletions
@@ -0,0 +1,44 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Sun, 14 Jun 2026 18:10:20 +0800
Subject: [PATCH] Luminol Config System
diff --git a/net/minecraft/server/Main.java b/net/minecraft/server/Main.java
index a4d608d64b7d3477c9144d93547fd3b4f39a1b02..2a75f8fbd2e5f41ca138740319d34683915e3b5b 100644
--- a/net/minecraft/server/Main.java
+++ b/net/minecraft/server/Main.java
@@ -107,6 +107,7 @@ public class Main {
JvmProfiler.INSTANCE.start(Environment.SERVER);
}
+ me.earthme.luminol.config.ConfigManager.initConfigs(); // Luminol - Luminol config
io.papermc.paper.plugin.PluginInitializerManager.load(options); // Paper
Bootstrap.bootStrap();
Bootstrap.validate();
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 6f13870a1b463049dd37cf1c65d0bdecfc35b5c4..a49c46eb7df50fc3c5587a2c3668ab6edc887107 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -1169,6 +1169,9 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
ca.spottedleaf.moonrise.common.util.MoonriseCommon.haltExecutors();
}
// Paper end - rewrite chunk system
+ // Luminol start - Config Systems
+ me.earthme.luminol.config.ConfigManager.saveConfigs(false);
+ // Luminol end
// Paper start - Improved watchdog support - move final shutdown items here
Util.shutdownExecutors();
this.onServerExit();
diff --git a/net/minecraft/server/dedicated/DedicatedServer.java b/net/minecraft/server/dedicated/DedicatedServer.java
index cd5551c0221ad2a7b5c7d452d8d91ffc9711c3ee..1ad4704523a3d90414ac514488207b1b5def9afd 100644
--- a/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/net/minecraft/server/dedicated/DedicatedServer.java
@@ -238,6 +238,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
this.paperConfigurations.initializeGlobalConfiguration(this.registryAccess());
this.paperConfigurations.initializeWorldDefaultsConfiguration(this.registryAccess());
// Paper end - initialize global and world-defaults configuration
+ me.earthme.luminol.config.ConfigManager.loadConfigFiles(); // Luminol - load config file
this.server.spark.enableEarlyIfRequested(); // Paper - spark
// Paper start - fix converting txt to json file; convert old users earlier after PlayerList creation but before file load/save
if (this.convertOldUsers()) {
@@ -0,0 +1,54 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Sun, 28 Jun 2026 17:56:30 +0800
Subject: [PATCH] Correct player respawn place
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index fbe0ef19bfabfc42d9e0e08e17b08159321b3804..44b3dfbf529630d8cca9a4270ae2a7c94c5a077c 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -511,8 +511,10 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
double amountX = selectMaxX - selectMinX;
double amountZ = selectMaxZ - selectMinZ;
- int selectX = amountX < 1.0 ? Mth.floor(worldBorder.getCenterX()) : (int)Mth.floor((amountX + 1.0) * random.nextDouble() + selectMinX);
- int selectZ = amountZ < 1.0 ? Mth.floor(worldBorder.getCenterZ()) : (int)Mth.floor((amountZ + 1.0) * random.nextDouble() + selectMinZ);
+ // Luminol start - Correct player respawn place
+ int selectX = amountX < 0.0 ? Mth.floor(worldBorder.getCenterX()) : (int)Mth.floor(amountX * random.nextDouble() + selectMinX);
+ int selectZ = amountZ < 0.0 ? Mth.floor(worldBorder.getCenterZ()) : (int)Mth.floor(amountZ * random.nextDouble() + selectMinZ);
+ // Luminol end - Correct player respawn place
return new BlockPos(selectX, 0, selectZ);
}
@@ -523,10 +525,20 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
}
private static BlockPos findSpawnAround(ServerLevel world, BlockPos selected) {
+ // Luminol start - Correct player respawn place
+ BlockPos inChunk = PlayerSpawnFinder.getLevelRespawnPos(world, selected.getX(), selected.getZ());
+ if (inChunk != null) {
+ AABB checkVolume = PlayerSpawnFinder.PLAYER_DIMENSIONS.makeBoundingBox((double)inChunk.getX() + 0.5, (double)inChunk.getY(), (double)inChunk.getZ() + 0.5);
+
+ if (world.noCollision(null, checkVolume, true)) {
+ return inChunk;
+ }
+ }
+ // Luminol end - Correct player respawn place
// try hard to find, so that we don't attempt another chunk load
for (int dz = -SPAWN_RADIUS_SELECTION_SEARCH; dz <= SPAWN_RADIUS_SELECTION_SEARCH; ++dz) {
for (int dx = -SPAWN_RADIUS_SELECTION_SEARCH; dx <= SPAWN_RADIUS_SELECTION_SEARCH; ++dx) {
- BlockPos inChunk = PlayerSpawnFinder.getLevelRespawnPos(world, selected.getX() + dx, selected.getZ() + dz);
+ inChunk = PlayerSpawnFinder.getLevelRespawnPos(world, selected.getX() + dx, selected.getZ() + dz); // Luminol - Correct player respawn place
if (inChunk == null) {
continue;
}
@@ -2018,7 +2030,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
}
if (newLevel.dimension() == lastDimension) {
- this.connection.internalTeleport(PositionMoveRotation.of(transition), transition.relatives()); // CraftBukkit
+ this.connection.internalTeleport(PositionMoveRotation.of(transition), transition.relatives()); // CraftBukkit // Luminol - Correct player respawn place
this.connection.resetPosition();
transition.postTeleportTransition().onTransition(this);
return this;
@@ -0,0 +1,19 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Fri, 17 Apr 2026 23:01:59 +0800
Subject: [PATCH] Correct volatile reference get in RegionizedTaskQueue
diff --git a/io/papermc/paper/threadedregions/RegionizedTaskQueue.java b/io/papermc/paper/threadedregions/RegionizedTaskQueue.java
index d1a045d47dcd12f246d95ecdfc202da357897cbf..b264dbd13a392cde3f24ff75c0034985fda68ccc 100644
--- a/io/papermc/paper/threadedregions/RegionizedTaskQueue.java
+++ b/io/papermc/paper/threadedregions/RegionizedTaskQueue.java
@@ -533,7 +533,7 @@ public final class RegionizedTaskQueue {
}
private ReferenceCountData getReferenceCounterVolatile() {
- return (ReferenceCountData)REFERENCE_COUNTER_HANDLE.get(this);
+ return (ReferenceCountData)REFERENCE_COUNTER_HANDLE.getVolatile(this); // Luminol - Correct volatile reference get in RegionizedTaskQueue
}
private ReferenceCountData compareAndExchangeReferenceCounter(final ReferenceCountData expect, final ReferenceCountData update) {
@@ -0,0 +1,32 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Tue, 30 Jun 2026 22:34:00 +0800
Subject: [PATCH] Correct thread unsafe random sources
diff --git a/net/minecraft/world/entity/npc/wanderingtrader/WanderingTraderSpawner.java b/net/minecraft/world/entity/npc/wanderingtrader/WanderingTraderSpawner.java
index 01b3cda3da79abd30860eb33491ac00f9e958767..dd35e72757b1be1a73e67b272c138bb4e3b6336a 100644
--- a/net/minecraft/world/entity/npc/wanderingtrader/WanderingTraderSpawner.java
+++ b/net/minecraft/world/entity/npc/wanderingtrader/WanderingTraderSpawner.java
@@ -30,7 +30,7 @@ public class WanderingTraderSpawner implements CustomSpawner {
private static final int SPAWN_CHANCE_INCREASE = 25;
private static final int SPAWN_ONE_IN_X_CHANCE = 10;
private static final int NUMBER_OF_SPAWN_ATTEMPTS = 10;
- private final RandomSource random = RandomSource.create();
+ private final RandomSource random = io.papermc.paper.threadedregions.util.ThreadLocalRandomSource.INSTANCE; // Luminol - Correct thread unsafe random sources
private final SavedDataStorage savedDataStorage;
// Folia - moved to global data
diff --git a/net/minecraft/world/level/block/entity/EnchantingTableBlockEntity.java b/net/minecraft/world/level/block/entity/EnchantingTableBlockEntity.java
index 22b9f70f76ff8f592359e958fa439e9806a29fcc..818598ed4e498bf3f177faac03336097b41915ee 100644
--- a/net/minecraft/world/level/block/entity/EnchantingTableBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/EnchantingTableBlockEntity.java
@@ -28,7 +28,7 @@ public class EnchantingTableBlockEntity extends BlockEntity implements Nameable
public float rot;
public float oRot;
public float tRot;
- private static final RandomSource RANDOM = RandomSource.create();
+ private static final RandomSource RANDOM = io.papermc.paper.threadedregions.util.ThreadLocalRandomSource.INSTANCE; // Luminol - Correct thread unsafe random sources
private @Nullable Component name;
public EnchantingTableBlockEntity(final BlockPos worldPosition, final BlockState blockState) {
@@ -0,0 +1,58 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Thu, 2 Jul 2026 22:31:15 +0800
Subject: [PATCH] Correct portal logic of some projectile entities
diff --git a/net/minecraft/world/entity/projectile/ShulkerBullet.java b/net/minecraft/world/entity/projectile/ShulkerBullet.java
index d5506bfc84912ec50cd5391225b9b5291435d3aa..6339a770526324bdc1bcc0c0e3bacc0a10f359ab 100644
--- a/net/minecraft/world/entity/projectile/ShulkerBullet.java
+++ b/net/minecraft/world/entity/projectile/ShulkerBullet.java
@@ -229,7 +229,7 @@ public class ShulkerBullet extends Projectile {
this.setPos(this.position().add(movement));
this.applyEffectsFromBlocks();
if (this.portalProcess != null && this.portalProcess.isInsidePortalThisTick()) {
- this.handlePortal();
+ if (this.handlePortal()) return; // Luminol - Correct portal logic of some projectile entities (Should not be ticking anymore after portal logics)
}
if (hitResult != null && this.isAlive() && hitResult.getType() != HitResult.Type.MISS) {
diff --git a/net/minecraft/world/entity/projectile/arrow/AbstractArrow.java b/net/minecraft/world/entity/projectile/arrow/AbstractArrow.java
index 88c2f0f927bd8376522eb9aed184abd6f09230f6..d66a4826400eca835645e1115c4fefc61715f336 100644
--- a/net/minecraft/world/entity/projectile/arrow/AbstractArrow.java
+++ b/net/minecraft/world/entity/projectile/arrow/AbstractArrow.java
@@ -276,7 +276,7 @@ public abstract class AbstractArrow extends Projectile {
.clipIncludingBorder(
new ClipContext(originalPosition, originalPosition.add(movement), ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, this)
);
- this.stepMoveAndHit(blockHitResult);
+ if (this.stepMoveAndHit(blockHitResult)) return; // Luminol - Correct portal logic of some projectile entities (Should not be ticking anymore after portal logics)
} else {
this.setPos(originalPosition.add(movement));
this.applyEffectsFromBlocks();
@@ -299,7 +299,7 @@ public abstract class AbstractArrow extends Projectile {
return 0.99F;
}
- private void stepMoveAndHit(final BlockHitResult blockHitResult) {
+ private boolean stepMoveAndHit(final BlockHitResult blockHitResult) { // Luminol - Correct portal logic of some projectile entities
while (this.isAlive()) {
Vec3 initialPosition = this.position();
ArrayList<EntityHitResult> entitiesHit = new ArrayList<>(this.findHitEntities(initialPosition, blockHitResult.getLocation()));
@@ -309,7 +309,7 @@ public abstract class AbstractArrow extends Projectile {
this.setPos(nextLocation);
this.applyEffectsFromBlocks(initialPosition, nextLocation);
if (this.portalProcess != null && this.portalProcess.isInsidePortalThisTick()) {
- this.handlePortal();
+ if (this.handlePortal()) return true; // Luminol - Correct portal logic of some projectile entities (Should not be ticking anymore after portal logics)
}
if (entitiesHit.isEmpty()) {
@@ -327,6 +327,7 @@ public abstract class AbstractArrow extends Projectile {
break;
}
}
+ return false; // Luminol - Correct portal logic of some projectile entities
}
private ProjectileDeflection hitTargetsOrDeflectSelf(final Collection<EntityHitResult> entityHitResults) {
@@ -0,0 +1,388 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Sun, 28 Jun 2026 18:54:50 +0800
Subject: [PATCH] Configurable region format framework
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/io/ChunkSystemRegionFileStorage.java b/ca/spottedleaf/moonrise/patches/chunk_system/io/ChunkSystemRegionFileStorage.java
index a814512fcfb85312474ae2c2c21443843bf57831..2e084a5b28cbe4737f48c25e10af589213525362 100644
--- a/ca/spottedleaf/moonrise/patches/chunk_system/io/ChunkSystemRegionFileStorage.java
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/io/ChunkSystemRegionFileStorage.java
@@ -8,9 +8,9 @@ public interface ChunkSystemRegionFileStorage {
public boolean moonrise$doesRegionFileNotExistNoIO(final int chunkX, final int chunkZ);
- public RegionFile moonrise$getRegionFileIfLoaded(final int chunkX, final int chunkZ);
+ public abomination.IRegionFile moonrise$getRegionFileIfLoaded(final int chunkX, final int chunkZ); // Luminol - Configurable region file format
- public RegionFile moonrise$getRegionFileIfExists(final int chunkX, final int chunkZ) throws IOException;
+ public abomination.IRegionFile moonrise$getRegionFileIfExists(final int chunkX, final int chunkZ) throws IOException; // Luminol - Configurable region file format
public MoonriseRegionFileIO.RegionDataController.WriteData moonrise$startWrite(
final int chunkX, final int chunkZ, final CompoundTag compound
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java b/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java
index 3bb9b58ee97464687e348e23b99159226415c267..fc264727141c3897e6c06819b9713d4f6395baf0 100644
--- a/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java
@@ -1273,7 +1273,7 @@ public final class MoonriseRegionFileIO {
this.regionDataController.finishWrite(this.chunkX, this.chunkZ, writeData);
// Paper start - flush regionfiles on save
if (this.world.paperConfig().chunks.flushRegionsOnSave) {
- final RegionFile regionFile = this.regionDataController.getCache().moonrise$getRegionFileIfLoaded(this.chunkX, this.chunkZ);
+ final abomination.IRegionFile regionFile = this.regionDataController.getCache().moonrise$getRegionFileIfLoaded(this.chunkX, this.chunkZ); // Luminol - Add configurable region file
if (regionFile != null) {
regionFile.flush();
} // else: evicted from cache, which should have called flush
@@ -1489,7 +1489,7 @@ public final class MoonriseRegionFileIO {
public static interface IORunnable {
- public void run(final RegionFile regionFile) throws IOException;
+ public void run(final abomination.IRegionFile regionFile) throws IOException; // Luminol - Configurable region file format
}
}
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/storage/ChunkSystemChunkBuffer.java b/ca/spottedleaf/moonrise/patches/chunk_system/storage/ChunkSystemChunkBuffer.java
index 51c126735ace8fdde89ad97b5cab62f244212db0..c7d4d944eb198ac53a3eeae717a25c7d5815c8c1 100644
--- a/ca/spottedleaf/moonrise/patches/chunk_system/storage/ChunkSystemChunkBuffer.java
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/storage/ChunkSystemChunkBuffer.java
@@ -8,5 +8,5 @@ public interface ChunkSystemChunkBuffer {
public void moonrise$setWriteOnClose(final boolean value);
- public void moonrise$write(final RegionFile regionFile) throws IOException;
+ public void moonrise$write(final abomination.IRegionFile regionFile) throws IOException; // Luminol - Configurable region file format
}
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index a49c46eb7df50fc3c5587a2c3668ab6edc887107..83b9aab16124ddcc416f7b4cd95084e1c64fe5b7 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -986,10 +986,10 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
if (flush) {
for (ServerLevel level : this.getAllLevels()) {
String storageName = level.getChunkSource().chunkMap.getStorageName();
- LOGGER.info("ThreadedAnvilChunkStorage ({}): All chunks are saved", LEGACY_WORLD_NAMES_FOR_REALMS_LOG.getOrDefault(storageName, storageName));
+ LOGGER.info("ThreadedChunkStorage ({}): All chunks are saved", LEGACY_WORLD_NAMES_FOR_REALMS_LOG.getOrDefault(storageName, storageName)); // Luminol - configurable region format
}
- LOGGER.info("ThreadedAnvilChunkStorage: All dimensions are saved");
+ LOGGER.info("ThreadedChunkStorage: All dimensions are saved"); // Luminol - configurable region format
}
return result;
diff --git a/net/minecraft/util/worldupdate/FileToUpgrade.java b/net/minecraft/util/worldupdate/FileToUpgrade.java
index a7f2cfa9277c898038f6b9e0a3401db51012b64c..db4dba8849eb27f8f84acc3297e342da4700d839 100644
--- a/net/minecraft/util/worldupdate/FileToUpgrade.java
+++ b/net/minecraft/util/worldupdate/FileToUpgrade.java
@@ -4,5 +4,5 @@ import java.util.List;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.storage.RegionFile;
-public record FileToUpgrade(RegionFile file, List<ChunkPos> chunksToUpgrade) {
+public record FileToUpgrade(abomination.IRegionFile file, List<ChunkPos> chunksToUpgrade) { // Luminol - Configurable region file format
}
diff --git a/net/minecraft/util/worldupdate/RegionStorageUpgrader.java b/net/minecraft/util/worldupdate/RegionStorageUpgrader.java
index b228cd6c54e8c8998923ab332882d99092769c67..140ef4959fa2fe5b9cd251955c93683e8e69d5ba 100644
--- a/net/minecraft/util/worldupdate/RegionStorageUpgrader.java
+++ b/net/minecraft/util/worldupdate/RegionStorageUpgrader.java
@@ -36,7 +36,7 @@ import org.slf4j.Logger;
public class RegionStorageUpgrader {
private static final Logger LOGGER = LogUtils.getLogger();
private static final String NEW_DIRECTORY_PREFIX = "new_";
- private static final Pattern REGEX = Pattern.compile("^r\\.(-?[0-9]+)\\.(-?[0-9]+)\\.mca$");
+ private static final Pattern REGEX = Pattern.compile("^r\\.(-?[0-9]+)\\.(-?[0-9]+)\\." + me.earthme.luminol.config.modules.function.RegionFormatConfig.regionFormat.getArgument() + "$"); // Luminol - Configurable region file format
private final DataFixer dataFixer;
private final UpgradeProgress upgradeProgress;
private final String type;
@@ -176,7 +176,8 @@ public class RegionStorageUpgrader {
int zOffset = Integer.parseInt(regex.group(2)) << 5;
List<ChunkPos> chunkPositions = Lists.newArrayList();
- try (RegionFile regionSource = new RegionFile(info, regionFile.toPath(), regionFolder, true)) {
+ var regionFileInfo = new me.earthme.luminol.utils.RegionCreatorInfo(info, regionFile.toPath(), regionFolder, true); // Luminol - Configurable region file format
+ try (abomination.IRegionFile regionSource = me.earthme.luminol.config.modules.function.RegionFormatConfig.regionFormat.getCreator().create(regionFileInfo)) { // Luminol - Configurable region file format
for (int x = 0; x < 32; x++) {
for (int z = 0; z < 32; z++) {
ChunkPos pos = new ChunkPos(x + xOffset, z + zOffset);
@@ -253,7 +254,7 @@ public class RegionStorageUpgrader {
return storage.upgradeChunkTag(chunkTag, this.defaultVersion, this.dataFixContextTag, targetVersion);
}
- private void onFileFinished(final RegionFile regionFile) {
+ private void onFileFinished(final abomination.IRegionFile regionFile) { // Luminol - Configurable region file format
if (this.recreateRegionFiles) {
if (this.previousWriteFuture != null) {
this.previousWriteFuture.join();
diff --git a/net/minecraft/world/level/chunk/storage/RegionFile.java b/net/minecraft/world/level/chunk/storage/RegionFile.java
index 3de7fd2b084c38e72d7a6bc416880a881f514ad3..d3350dee1d6d5bc15dcdfba3611f93addb767392 100644
--- a/net/minecraft/world/level/chunk/storage/RegionFile.java
+++ b/net/minecraft/world/level/chunk/storage/RegionFile.java
@@ -22,7 +22,7 @@ import net.minecraft.world.level.ChunkPos;
import org.jspecify.annotations.Nullable;
import org.slf4j.Logger;
-public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patches.chunk_system.storage.ChunkSystemRegionFile { // Paper - rewrite chunk system
+public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patches.chunk_system.storage.ChunkSystemRegionFile , abomination.IRegionFile{ // Paper - rewrite chunk system // Luminol - Configurable region file format
private static final Logger LOGGER = LogUtils.getLogger();
public static final int MAX_CHUNK_SIZE = 500 * 1024 * 1024; // Paper - don't write garbage data to disk if writing serialization fails
private static final int SECTOR_BYTES = 4096;
@@ -130,7 +130,7 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
return this.recalculateCount.get();
}
- boolean recalculateHeader() throws IOException {
+ public boolean recalculateHeader() throws IOException { // Luminol - Configurable region file format
if (!this.canRecalcHeader) {
return false;
}
@@ -789,7 +789,7 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
}
}
- protected synchronized void write(final ChunkPos pos, final ByteBuffer data) throws IOException {
+ public synchronized void write(final ChunkPos pos, final ByteBuffer data) throws IOException { // Luminol - Configurable region file format
int offsetIndex = getOffsetIndex(pos);
int offset = this.offsets.get(offsetIndex);
int sectorNumber = getSectorNumber(offset);
@@ -907,7 +907,7 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
}
@Override
- public final void moonrise$write(final RegionFile regionFile) throws IOException {
+ public final void moonrise$write(final abomination.IRegionFile regionFile) throws IOException { // Luminol - Configurable region file format
regionFile.write(this.pos, ByteBuffer.wrap(this.buf, 0, this.count));
}
// Paper end - rewrite chunk system
@@ -973,11 +973,11 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
return (x & 31) + (z & 31) * 32;
}
- synchronized boolean isOversized(int x, int z) {
+ public synchronized boolean isOversized(int x, int z) { // Luminol - Configurable region file format
return this.oversized[getChunkIndex(x, z)] == 1;
}
- synchronized void setOversized(int x, int z, boolean oversized) throws IOException {
+ public synchronized void setOversized(int x, int z, boolean oversized) throws IOException { // Luminol - Configurable region file format
final int offset = getChunkIndex(x, z);
boolean previous = this.oversized[offset] == 1;
this.oversized[offset] = (byte) (oversized ? 1 : 0);
@@ -1016,7 +1016,7 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
return this.path.getParent().resolve(this.path.getFileName().toString().replaceAll("\\.mca$", "") + "_oversized_" + x + "_" + z + ".nbt");
}
- synchronized net.minecraft.nbt.CompoundTag getOversizedData(int x, int z) throws IOException {
+ public synchronized net.minecraft.nbt.CompoundTag getOversizedData(int x, int z) throws IOException { // Luminol - Configurable region file format
Path file = getOversizedFile(x, z);
try (DataInputStream out = new DataInputStream(new java.io.BufferedInputStream(new java.util.zip.InflaterInputStream(Files.newInputStream(file))))) {
return net.minecraft.nbt.NbtIo.read((java.io.DataInput) out);
diff --git a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
index 63b40c420030d935bb81a219fb33defe0946e21f..2e67e69862cbf5d804de50193442977ebfb18517 100644
--- a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
+++ b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
@@ -19,7 +19,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
private static final org.slf4j.Logger LOGGER = com.mojang.logging.LogUtils.getLogger(); // Paper
public static final String ANVIL_EXTENSION = ".mca";
private static final int MAX_CACHE_SIZE = 256;
- private final Long2ObjectLinkedOpenHashMap<RegionFile> regionCache = new Long2ObjectLinkedOpenHashMap<>();
+ private final Long2ObjectLinkedOpenHashMap<abomination.IRegionFile> regionCache = new Long2ObjectLinkedOpenHashMap<>(); // Luminol - Configurable region file format
private final RegionStorageInfo info;
private final Path folder;
private final boolean sync;
@@ -30,7 +30,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
@Nullable
public static ChunkPos getRegionFileCoordinates(Path file) {
String fileName = file.getFileName().toString();
- if (!fileName.startsWith("r.") || !fileName.endsWith(".mca")) {
+ if (!fileName.startsWith("r.") || !fileName.endsWith(getExtensionName())) { // Luminol - Configurable region file format
return null;
}
@@ -55,8 +55,32 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
private static final int MAX_NON_EXISTING_CACHE = 1024 * 4;
private final it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet nonExistingRegionFiles = new it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet();
private static String getRegionFileName(final int chunkX, final int chunkZ) {
- return "r." + (chunkX >> REGION_SHIFT) + "." + (chunkZ >> REGION_SHIFT) + ".mca";
+ return "r." + (chunkX >> REGION_SHIFT) + "." + (chunkZ >> REGION_SHIFT) + getExtensionName(); // Luminol - Configurable region file format
}
+ // Luminol start - Configurable region file format
+ public static abomination.IRegionFile createNew(RegionStorageInfo info, Path filePath, Path folder, boolean sync) throws IOException{
+ final me.earthme.luminol.enums.EnumRegionFormat regionFormat = me.earthme.luminol.config.modules.function.RegionFormatConfig.regionFormat;
+ final String fullFileName = filePath.getFileName().toString();
+ final String[] fullNameSplit = fullFileName.split("\\.");
+ final String extensionName = fullNameSplit[fullNameSplit.length - 1];
+
+ if (!regionFormat.getArgument().equalsIgnoreCase(extensionName)) {
+ // delayed crash
+ io.papermc.paper.threadedregions.RegionizedServer.getInstance().addTask(() -> {
+ new RuntimeException("Invalid region file format: " + extensionName + " expected " + regionFormat.getArgument());
+ });
+
+
+ throw new IOException("Invalid region file format: " + extensionName + " expected " + regionFormat.getArgument());
+ }
+
+ return regionFormat.getCreator().create(new me.earthme.luminol.utils.RegionCreatorInfo(info, filePath, folder, sync));
+ }
+
+ public static String getExtensionName() {
+ return "." + me.earthme.luminol.config.modules.function.RegionFormatConfig.regionFormat.getArgument();
+ }
+ // Luminol end
private boolean doesRegionFilePossiblyExist(final long position) {
synchronized (this.nonExistingRegionFiles) {
@@ -90,15 +114,15 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
}
@Override
- public synchronized final RegionFile moonrise$getRegionFileIfLoaded(final int chunkX, final int chunkZ) {
+ public synchronized final abomination.IRegionFile moonrise$getRegionFileIfLoaded(final int chunkX, final int chunkZ) { // Luminol - Configurable region file format
return this.regionCache.getAndMoveToFirst(ChunkPos.pack(chunkX >> REGION_SHIFT, chunkZ >> REGION_SHIFT));
}
@Override
- public synchronized final RegionFile moonrise$getRegionFileIfExists(final int chunkX, final int chunkZ) throws IOException {
+ public synchronized final abomination.IRegionFile moonrise$getRegionFileIfExists(final int chunkX, final int chunkZ) throws IOException { // Luminol - Configurable region file format
final long key = ChunkPos.pack(chunkX >> REGION_SHIFT, chunkZ >> REGION_SHIFT);
- RegionFile ret = this.regionCache.getAndMoveToFirst(key);
+ abomination.IRegionFile ret = this.regionCache.getAndMoveToFirst(key); // Luminol - Configurable region file format
if (ret != null) {
return ret;
}
@@ -124,7 +148,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
FileUtil.createDirectoriesSafe(this.folder);
- ret = new RegionFile(this.info, regionPath, this.folder, this.sync);
+ ret = this.createNew(this.info, regionPath, this.folder, this.sync); // Luminol - Configurable region file format
this.regionCache.putAndMoveToFirst(key, ret);
@@ -143,7 +167,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
}
final ChunkPos pos = new ChunkPos(chunkX, chunkZ);
- final RegionFile regionFile = this.getRegionFile(pos);
+ final abomination.IRegionFile regionFile = this.getRegionFile(pos); // Luminol - Configurable region file format
// note: not required to keep regionfile loaded after this call, as the write param takes a regionfile as input
// (and, the regionfile parameter is unused for writing until the write call)
@@ -177,7 +201,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
) throws IOException {
final ChunkPos pos = new ChunkPos(chunkX, chunkZ);
if (writeData.result() == ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.WriteData.WriteResult.DELETE) {
- final RegionFile regionFile = this.moonrise$getRegionFileIfExists(chunkX, chunkZ);
+ final abomination.IRegionFile regionFile = this.moonrise$getRegionFileIfExists(chunkX, chunkZ); // Luminol - Configurable region file format
if (regionFile != null) {
regionFile.clear(pos);
} // else: didn't exist
@@ -192,7 +216,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
public final ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.ReadData moonrise$readData(
final int chunkX, final int chunkZ
) throws IOException {
- final RegionFile regionFile = this.moonrise$getRegionFileIfExists(chunkX, chunkZ);
+ final abomination.IRegionFile regionFile = this.moonrise$getRegionFileIfExists(chunkX, chunkZ); // Luminol - Configurable region file format
final DataInputStream input = regionFile == null ? null : regionFile.getChunkDataInputStream(new ChunkPos(chunkX, chunkZ));
@@ -237,7 +261,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
final ChunkPos pos = new ChunkPos(chunkX, chunkZ);
final ChunkPos headerChunkPos = SerializableChunkData.getChunkCoordinate(ret);
- final RegionFile regionFile = this.getRegionFile(pos);
+ final abomination.IRegionFile regionFile = this.getRegionFile(pos); // Luminol - Configurable region file format
if (regionFile.getRecalculateCount() != readData.recalculateCount()) {
return null;
@@ -261,7 +285,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
}
// Paper end - rewrite chunk system
// Paper start - rewrite chunk system
- public RegionFile getRegionFile(ChunkPos pos) throws IOException {
+ public abomination.IRegionFile getRegionFile(ChunkPos pos) throws IOException { // Luminol - Configurable region file format
return this.getRegionFile(pos, false);
}
// Paper end - rewrite chunk system
@@ -273,7 +297,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
this.isChunkData = info.dfuType()[0] == net.minecraft.util.datafix.DataFixTypes.CHUNK; // Paper - recalculate region file headers
}
- @org.jetbrains.annotations.Contract("_, false -> !null") private @Nullable RegionFile getRegionFile(final ChunkPos pos, boolean existingOnly) throws IOException { // CraftBukkit
+ @org.jetbrains.annotations.Contract("_, false -> !null") private abomination.@Nullable IRegionFile getRegionFile(final ChunkPos pos, boolean existingOnly) throws IOException { // CraftBukkit // Luminol - Configurable region file format
// Paper start - rewrite chunk system
if (existingOnly) {
return this.moonrise$getRegionFileIfExists(pos.x(), pos.z());
@@ -281,7 +305,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
synchronized (this) {
final long key = ChunkPos.pack(pos.x() >> REGION_SHIFT, pos.z() >> REGION_SHIFT);
- RegionFile ret = this.regionCache.getAndMoveToFirst(key);
+ abomination.IRegionFile ret = this.regionCache.getAndMoveToFirst(key); // Luminol - Configurable region file format
if (ret != null) {
return ret;
}
@@ -298,7 +322,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
FileUtil.createDirectoriesSafe(this.folder);
- ret = new RegionFile(this.info, regionPath, this.folder, this.sync);
+ ret = this.createNew(this.info, regionPath, this.folder, this.sync); // Luminol - Configurable region file format
this.regionCache.putAndMoveToFirst(key, ret);
@@ -312,7 +336,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
LOGGER.error("{} ({} - {},{}) Go clean it up to remove this message. /minecraft:tp {} 128 {} - DO NOT REPORT THIS TO PAPER - You may ask for help on Discord, but do not file an issue. These error messages can not be removed.", msg, file.toString().replaceAll(".+[\\\\/]", ""), x, z, x << 4, z << 4);
}
- private static CompoundTag readOversizedChunk(RegionFile regionfile, ChunkPos chunkCoordinate) throws IOException {
+ private static CompoundTag readOversizedChunk(abomination.IRegionFile regionfile, ChunkPos chunkCoordinate) throws IOException { // Luminol - Configurable region file format
synchronized (regionfile) {
try (DataInputStream datainputstream = regionfile.getChunkDataInputStream(chunkCoordinate)) {
CompoundTag oversizedData = regionfile.getOversizedData(chunkCoordinate.x(), chunkCoordinate.z());
@@ -346,7 +370,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
public @Nullable CompoundTag read(final ChunkPos pos) throws IOException {
// CraftBukkit start - SPIGOT-5680: There's no good reason to preemptively create files on read, save that for writing
- RegionFile region = this.getRegionFile(pos, true);
+ abomination.IRegionFile region = this.getRegionFile(pos, true); // Luminol - Configurable region file format
if (region == null) {
return null;
}
@@ -383,7 +407,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
public void scanChunk(final ChunkPos pos, final StreamTagVisitor scanner) throws IOException {
// CraftBukkit start - SPIGOT-5680: There's no good reason to preemptively create files on read, save that for writing
- RegionFile region = this.getRegionFile(pos, true);
+ abomination.IRegionFile region = this.getRegionFile(pos, true); // Luminol - Configurable region file format
if (region == null) {
return;
}
@@ -398,7 +422,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
public void write(final ChunkPos pos, final @Nullable CompoundTag value) throws IOException {
if (!SharedConstants.DEBUG_DONT_SAVE_WORLD) {
- RegionFile region = this.getRegionFile(pos, value == null); // CraftBukkit // Paper - rewrite chunk system
+ abomination.IRegionFile region = this.getRegionFile(pos, value == null); // CraftBukkit // Paper - rewrite chunk system // Luminol - Configurable region file format
// Paper start - rewrite chunk system
if (region == null) {
// if the RegionFile doesn't exist, no point in deleting from it
@@ -430,7 +454,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
// Paper start - rewrite chunk system
synchronized (this) {
final ExceptionCollector<IOException> exceptionCollector = new ExceptionCollector<>();
- for (final RegionFile regionFile : this.regionCache.values()) {
+ for (final abomination.IRegionFile regionFile : this.regionCache.values()) { // Luminol - Configurable region file format
try {
regionFile.close();
} catch (final IOException ex) {
@@ -446,7 +470,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
// Paper start - rewrite chunk system
synchronized (this) {
final ExceptionCollector<IOException> exceptionCollector = new ExceptionCollector<>();
- for (final RegionFile regionFile : this.regionCache.values()) {
+ for (final abomination.IRegionFile regionFile : this.regionCache.values()) { // Luminol - Configurable region file format
try {
regionFile.flush();
} catch (final IOException ex) {
@@ -0,0 +1,21 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Fri, 17 Apr 2026 23:00:48 +0800
Subject: [PATCH] Do not enable any debug subscriptions
Really this would really crash the server by accident when the operators used F3 + J or toggled the debug synchronizer
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index 44b3dfbf529630d8cca9a4270ae2a7c94c5a077c..34cc36f6bdf435593d16f2e7af280a27be048280 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -3504,7 +3504,8 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
}
public Set<DebugSubscription<?>> debugSubscriptions() {
- return !this.server.debugSubscribers().hasRequiredPermissions(this) ? Set.of() : this.requestedDebugSubscriptions;
+ // return !this.server.debugSubscribers().hasRequiredPermissions(this) ? Set.of() : this.requestedDebugSubscriptions; // Luminol - Do not enable any debug subscriptions
+ return Set.of(); // Luminol - Do not enable any debug subscriptions
}
public record RespawnConfig(LevelData.RespawnData respawnData, boolean forced) {
@@ -0,0 +1,32 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Fri, 17 Apr 2026 23:25:45 +0800
Subject: [PATCH] Do not load too far poi chunk in poi compete scan
Beeeeeeeeeeeeeeee
diff --git a/net/minecraft/world/entity/ai/behavior/PoiCompetitorScan.java b/net/minecraft/world/entity/ai/behavior/PoiCompetitorScan.java
index 53131c1b8d5562f9a1b45dfab4fdbdd37b655139..0e9c19a820aefa802d6df134b21f5128a5e8d656 100644
--- a/net/minecraft/world/entity/ai/behavior/PoiCompetitorScan.java
+++ b/net/minecraft/world/entity/ai/behavior/PoiCompetitorScan.java
@@ -24,8 +24,18 @@ public class PoiCompetitorScan {
return true;
}
// Folia end - region threading
- level.getPoiManager()
- .getType(pos.pos())
+ // Luminol start - POI fixes
+ var blockPosOfJobSite = pos.pos();
+ var sectionPosOfJobSite = net.minecraft.core.SectionPos.asLong(blockPosOfJobSite);
+ var poiManager = level.getPoiManager();
+ // we don't care if we should clear the memory of JOB_SITE
+ // as it will be automatically removed in SetWalkTargetFromBlockMemory
+ // so simply break down if it's not loaded
+ var poiChunk = me.earthme.luminol.config.modules.fixes.POIRangeFixes.doNotCompetePOIIfUnloaded ? poiManager.get(sectionPosOfJobSite) : poiManager.getOrLoad(sectionPosOfJobSite);
+ poiChunk.flatMap(poiSection -> poiSection.getType(blockPosOfJobSite))
+ // Luminol end - POI fixes
+ /*level.getPoiManager() // Luminol - POI fixes
+ .getType(pos.pos())*/ // Luminol - POI fixes
.ifPresent(
// Paper start - Improve performance of PoiCompetitorScan by unrolling stream
// The previous logic used Stream#reduce to simulate a form of single-iteration bubble sort
@@ -0,0 +1,287 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Sun, 28 Jun 2026 19:02:01 +0800
Subject: [PATCH] Fixes around entity brain and memories
diff --git a/net/minecraft/world/entity/Mob.java b/net/minecraft/world/entity/Mob.java
index 3b1c3e84ab24b6e3a0e4d129b3617b393b6d6651..9c8e8de53cbbd87ac7cb8483bd3c1af3fcf66b6e 100644
--- a/net/minecraft/world/entity/Mob.java
+++ b/net/minecraft/world/entity/Mob.java
@@ -299,6 +299,11 @@ public abstract class Mob extends LivingEntity implements Targeting, EquipmentUs
if (Objects.equals(currentTarget, target)) {
return false;
}
+ // Luminol start - Fix off-region targeting
+ if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(target)) {
+ return false;
+ }
+ // Luminol end
LivingEntity originalTarget = target;
target = asValidTarget(target);
if (reason != null) {
diff --git a/net/minecraft/world/entity/ai/Brain.java b/net/minecraft/world/entity/ai/Brain.java
index e097bc1268b4050e3948413c52dea69950858458..27894e2935e2afee8190708def4be3b00f922f64 100644
--- a/net/minecraft/world/entity/ai/Brain.java
+++ b/net/minecraft/world/entity/ai/Brain.java
@@ -383,7 +383,7 @@ public class Brain<E extends LivingEntity> {
}
public void tick(final ServerLevel level, final E body) {
- this.forgetOutdatedMemories();
+ this.forgetOutdatedMemories(body); // Luminol - Add config to force clean entity memory that don't belong to current tick region
this.tickSensors(level, body);
this.startEachNonRunningBehavior(level, body);
this.tickEachRunningBehavior(level, body);
@@ -395,8 +395,8 @@ public class Brain<E extends LivingEntity> {
}
}
- private void forgetOutdatedMemories() {
- this.memories.values().forEach(MemorySlot::tick);
+ private void forgetOutdatedMemories(E body) { // Luminol - Add config to force clean entity memory that don't belong to current tick region
+ this.memories.values().forEach(slot -> slot.tick(body)); // Luminol - Add config to force clean entity memory that don't belong to current tick region
}
public void stopAll(final ServerLevel level, final E body) {
diff --git a/net/minecraft/world/entity/ai/behavior/BehaviorUtils.java b/net/minecraft/world/entity/ai/behavior/BehaviorUtils.java
index f744ecd20dc70786311a7162b52aa4ceca0717db..aa60fbfbe7283f1ed9cf5343d59d1222380857dd 100644
--- a/net/minecraft/world/entity/ai/behavior/BehaviorUtils.java
+++ b/net/minecraft/world/entity/ai/behavior/BehaviorUtils.java
@@ -81,6 +81,11 @@ public class BehaviorUtils {
public static void setWalkAndLookTargetMemories(
final LivingEntity walker, final PositionTracker target, final float speedModifier, final int closeEnoughDistance
) {
+ // Luminol - Do not set walk target if target position is out of current tick region
+ if (!target.checkThread(walker.level())) {
+ return;
+ }
+ // Luminol end
WalkTarget walkTarget = new WalkTarget(target, speedModifier, closeEnoughDistance);
walker.getBrain().setMemory(MemoryModuleType.LOOK_TARGET, target);
walker.getBrain().setMemory(MemoryModuleType.WALK_TARGET, walkTarget);
diff --git a/net/minecraft/world/entity/ai/behavior/BlockPosTracker.java b/net/minecraft/world/entity/ai/behavior/BlockPosTracker.java
index 68251875edfa47ac34c64f99510998ad4bbb14b4..d83b353395bad7c6c41dfaa24018fd207904d995 100644
--- a/net/minecraft/world/entity/ai/behavior/BlockPosTracker.java
+++ b/net/minecraft/world/entity/ai/behavior/BlockPosTracker.java
@@ -37,4 +37,11 @@ public class BlockPosTracker implements PositionTracker {
public String toString() {
return "BlockPosTracker{blockPos=" + this.blockPos + ", centerPosition=" + this.centerPosition + "}";
}
+
+ // Luminol start - Fix a series issue around entity memory typed GlobalPos and WalkTarget
+ @Override
+ public boolean checkThread(net.minecraft.world.level.Level currOwnedByLevel) {
+ return ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(currOwnedByLevel, this.blockPos);
+ }
+ // Luminol end
}
diff --git a/net/minecraft/world/entity/ai/behavior/EntityTracker.java b/net/minecraft/world/entity/ai/behavior/EntityTracker.java
index 3ac52b025ac3e1a3f9135b8e593a385a847afe0f..29c13f18cff52e9950e8932c75cb12fb02f952cb 100644
--- a/net/minecraft/world/entity/ai/behavior/EntityTracker.java
+++ b/net/minecraft/world/entity/ai/behavior/EntityTracker.java
@@ -55,4 +55,11 @@ public class EntityTracker implements PositionTracker {
public String toString() {
return "EntityTracker for " + this.entity;
}
+
+ // Luminol start - Fix a series issue around entity memory typed GlobalPos and WalkTarget
+ @Override
+ public boolean checkThread(net.minecraft.world.level.Level currOwnedByLevel) {
+ return ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(this.entity);
+ }
+ // Luminol end
}
diff --git a/net/minecraft/world/entity/ai/behavior/PositionTracker.java b/net/minecraft/world/entity/ai/behavior/PositionTracker.java
index ce6cf5ecfb190428e3ef9b7dd39c98e3d27a7b9d..3334da2236fbe90cbdc3e67884f9c6ed80ac0f56 100644
--- a/net/minecraft/world/entity/ai/behavior/PositionTracker.java
+++ b/net/minecraft/world/entity/ai/behavior/PositionTracker.java
@@ -10,4 +10,7 @@ public interface PositionTracker {
BlockPos currentBlockPosition();
boolean isVisibleBy(final LivingEntity body);
+
+
+ boolean checkThread(net.minecraft.world.level.Level currOwnedByLevel); // Luminol - Fix a series issue around entity memory typed GlobalPos and WalkTarget
}
diff --git a/net/minecraft/world/entity/ai/behavior/SleepInBed.java b/net/minecraft/world/entity/ai/behavior/SleepInBed.java
index 16017d819c28b077f732e2ef571eac179d24e323..7a598aa1a0963fda3302946a30d0330c341da74f 100644
--- a/net/minecraft/world/entity/ai/behavior/SleepInBed.java
+++ b/net/minecraft/world/entity/ai/behavior/SleepInBed.java
@@ -57,6 +57,11 @@ public class SleepInBed extends Behavior<LivingEntity> {
}
}
+ // Luminol Start - Prevent off-tick-region chunk operations
+ if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(level, target.pos())) {
+ return false;
+ }
+ // Luminol End
BlockState blockState = level.getBlockStateIfLoaded(target.pos()); // Paper - Prevent sync chunk loads when villagers try to find beds
if (blockState == null) return false; // Paper - Prevent sync chunk loads when villagers try to find beds
return target.pos().closerToCenterThan(body.position(), 2.0) && blockState.is(BlockTags.BEDS) && !blockState.getValue(BedBlock.OCCUPIED);
diff --git a/net/minecraft/world/entity/ai/memory/MemorySlot.java b/net/minecraft/world/entity/ai/memory/MemorySlot.java
index 88a89b4c72cc99dc89d3f3cc928b2dfda4125759..3b51a44ab700740047c9e725547ceec65c0c80b1 100644
--- a/net/minecraft/world/entity/ai/memory/MemorySlot.java
+++ b/net/minecraft/world/entity/ai/memory/MemorySlot.java
@@ -13,7 +13,7 @@ public class MemorySlot<T> {
this.timeToLive = timeToLive;
}
- public void tick() {
+ public void tick(net.minecraft.world.entity.Entity owner) { // Luminol - Add config to force clean entity memory that don't belong to current tick region
if (this.hasValue() && this.canExpire()) {
if (this.hasExpired()) {
this.clear();
@@ -21,6 +21,41 @@ public class MemorySlot<T> {
this.timeToLive--;
}
}
+ // Luminol start - Add config to force clean entity memory that don't belong to current tick region
+ final net.minecraft.world.level.Level ownerLevel = owner.level();
+
+ // type: entity
+ if (me.earthme.luminol.config.modules.fixes.ForceCleanupEntityBrainMemoryConfig.enabledForEntity && this.value instanceof net.minecraft.world.entity.Entity entity) {
+ if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(entity)) {
+ this.clear();
+ }
+ }
+
+ // type: block_pos
+ if (me.earthme.luminol.config.modules.fixes.ForceCleanupEntityBrainMemoryConfig.enabledForBlockPos && this.value instanceof net.minecraft.core.BlockPos blockPos) {
+ if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(ownerLevel, blockPos)) {
+ this.clear();
+ }
+ }
+
+
+ //type: position_tracker and walk_target
+ if (me.earthme.luminol.config.modules.fixes.ForceCleanupEntityBrainMemoryConfig.enabledForPositionTracker) {
+ net.minecraft.world.entity.ai.behavior.PositionTracker tracker = null;
+
+ if (value instanceof net.minecraft.world.entity.ai.behavior.PositionTracker positionTracker) {
+ tracker = positionTracker;
+ }
+
+ if (value instanceof net.minecraft.world.entity.ai.memory.WalkTarget walkTarget) {
+ tracker = walkTarget.getTarget();
+ }
+
+ if (tracker != null && !tracker.checkThread(owner.level())) {
+ this.clear();
+ }
+ }
+ // Luminol end
}
public static <T> MemorySlot<T> create() {
diff --git a/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java b/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java
index e44814cfb6afb594456b8215bd13a92e93de2c85..c18e2071c699d9c5f4d46b7a58191df4f4d8ab5d 100644
--- a/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java
+++ b/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java
@@ -60,6 +60,17 @@ public class FlyingPathNavigation extends PathNavigation {
if (!this.isDone()) {
Vec3 target = this.path.getNextEntityPos(this.mob);
+ // Luminol - Recompute path when path finding out of current tick region
+ if (me.earthme.luminol.config.modules.fixes.PathfindingFixesConfig.breakDownPathfindingWhenOutOfRegion) {
+ // we assume that:
+ // 1. The code above doesn't touch the 'main thread context' with the position from 'this.path'
+ // 2. The pathfinder could correctly recompute or discard the incorrect target position and this situation is happening rarely
+ if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(this.mob.level(), target)) {
+ this.hasDelayedRecomputation = true;
+ return;
+ }
+ }
+ // Luminol end
this.mob.getMoveControl().setWantedPosition(target.x, target.y, target.z, this.speedModifier);
}
}
diff --git a/net/minecraft/world/entity/ai/navigation/PathNavigation.java b/net/minecraft/world/entity/ai/navigation/PathNavigation.java
index 2ea1ec39a37899ae1510d0036aa670e758077537..4c727e4f224f6a31a4cd9d024bdcfaf0fa1bc0e5 100644
--- a/net/minecraft/world/entity/ai/navigation/PathNavigation.java
+++ b/net/minecraft/world/entity/ai/navigation/PathNavigation.java
@@ -188,6 +188,18 @@ public abstract class PathNavigation {
}
}
// Paper end - EntityPathfindEvent
+ // Luminol start - Do not path find for targets out of current region
+ if (me.earthme.luminol.config.modules.fixes.PathfindingFixesConfig.doNotPathfindToNotOwnedTargets) {
+ // filter the targets not owned by current region
+ targets = new java.util.HashSet<>(targets); // well no idea about how to determine if this should be copied to a modifiable one
+ targets.removeIf(pos -> !ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(this.mob.level(), pos));
+
+ // return if no available (observe the logic in the first if block)
+ if (targets.isEmpty()) {
+ return null;
+ }
+ }
+ // Luminol end
ProfilerFiller profiler = Profiler.get();
profiler.push("pathfind");
BlockPos fromPos = above ? this.mob.blockPosition().above() : this.mob.blockPosition();
@@ -286,6 +298,17 @@ public abstract class PathNavigation {
if (!this.isDone()) {
Vec3 target = this.path.getNextEntityPos(this.mob);
+ // Luminol - Recompute path when path finding out of current tick region
+ if (me.earthme.luminol.config.modules.fixes.PathfindingFixesConfig.breakDownPathfindingWhenOutOfRegion) {
+ // we assume that:
+ // 1. The code above doesn't touch the 'main thread context' with the position from 'this.path'
+ // 2. The pathfinder could correctly recompute or discard the incorrect target position and this situation is happening rarely
+ if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(this.mob.level(), target)) {
+ this.hasDelayedRecomputation = true;
+ return;
+ }
+ }
+ // Luminol end
this.mob.getMoveControl().setWantedPosition(target.x, this.getGroundY(target), target.z, this.speedModifier);
}
}
diff --git a/net/minecraft/world/entity/animal/allay/AllayAi.java b/net/minecraft/world/entity/animal/allay/AllayAi.java
index c3667e7997551e0f5ce63bc6ee890082d1431400..2e2458535a8ec542d6371ceba2fff1260773c7f5 100644
--- a/net/minecraft/world/entity/animal/allay/AllayAi.java
+++ b/net/minecraft/world/entity/animal/allay/AllayAi.java
@@ -112,6 +112,17 @@ public class AllayAi {
Optional<GlobalPos> likedNoteblockPos = brain.getMemory(MemoryModuleType.LIKED_NOTEBLOCK_POSITION);
if (likedNoteblockPos.isPresent()) {
GlobalPos position = likedNoteblockPos.get();
+ // Luminol start - Do not like item if they were out of current tickregion
+ final Level targetLevel = allay.level().getServer().getLevel(position.dimension());
+ final BlockPos targetPos = position.pos();
+
+ // thread checks
+ if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(targetLevel, targetPos)) {
+ brain.eraseMemory(MemoryModuleType.LIKED_NOTEBLOCK_POSITION); // The memory value is not being belong to current tick region anymore
+ return Optional.empty();
+ }
+ // Luminol end
+
if (shouldDepositItemsAtLikedNoteblock(allay, brain, position)) {
return Optional.of(new BlockPosTracker(position.pos().above()));
}
diff --git a/net/minecraft/world/entity/animal/sniffer/Sniffer.java b/net/minecraft/world/entity/animal/sniffer/Sniffer.java
index d5394ae7ce56555ad21aeafb2d78c291c62e2988..a2873d8bbfa3e04678bc31637222b3a10cd6fedd 100644
--- a/net/minecraft/world/entity/animal/sniffer/Sniffer.java
+++ b/net/minecraft/world/entity/animal/sniffer/Sniffer.java
@@ -279,8 +279,18 @@ public class Sniffer extends Animal {
private boolean canDig(final BlockPos position) {
return this.level().getBlockState(position).is(BlockTags.SNIFFER_DIGGABLE_BLOCK)
- && this.getExploredPositions().noneMatch(explored -> GlobalPos.of(this.level().dimension(), position).equals(explored))
- && Optional.ofNullable(this.getNavigation().createPath(position, 1)).map(Path::canReach).orElse(false);
+ && this.getExploredPositions().noneMatch(explored -> { // Luminol start - Do not pathfind out of tickregion
+ // thread checks
+ final Level targetLevel = net.minecraft.server.MinecraftServer.getServer().getLevel(explored.dimension());
+ final BlockPos targetPos = explored.pos();
+
+ if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(targetLevel, targetPos)) {
+ return false;
+ }
+
+ return GlobalPos.of(this.level().dimension(), position).equals(explored); // Original logic
+ }) // Luminol end
+ && Optional.ofNullable(this.getNavigation().createPath(position, 1)).map(Path::canReach).orElse(false); // Luminol - Do not pathfind out of tickregion - diff on change
}
private void dropSeed() {
@@ -0,0 +1,38 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Fri, 17 Apr 2026 23:16:45 +0800
Subject: [PATCH] Fix off region leashing
PS: My friend reported me this bug and I still need to check the details of how the bug was caused
XD
diff --git a/net/minecraft/world/entity/Leashable.java b/net/minecraft/world/entity/Leashable.java
index 98034a4c96bf2972316078d3b3a49140921aab50..284ba0c3742d5d5ab0b344a84a9c8e2c2a98a74a 100644
--- a/net/minecraft/world/entity/Leashable.java
+++ b/net/minecraft/world/entity/Leashable.java
@@ -95,10 +95,24 @@ public interface Leashable {
if (leashUuid.isPresent()) {
Entity leasher = serverLevel.getEntity(leashUuid.get());
if (leasher != null) {
+ // Luminol start - Fix off region leashing
+ if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(leasher)) {
+ entity.spawnAtLocation(serverLevel, Items.LEAD);
+ entity.setLeashData(null);
+ return;
+ }
+ // Luminol end
setLeashedTo(entity, leasher, true);
return;
}
} else if (pos.isPresent()) {
+ // Luminol start - Fix off region leashing
+ if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(serverLevel, pos.get())) {
+ entity.spawnAtLocation(serverLevel, Items.LEAD);
+ entity.setLeashData(null);
+ return;
+ }
+ // Luminol end
setLeashedTo(entity, LeashFenceKnotEntity.getOrCreateKnot(serverLevel, pos.get()), true);
return;
}
@@ -0,0 +1,30 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Fri, 17 Apr 2026 23:21:30 +0800
Subject: [PATCH] Fix unpatched task returning of
ServerConfigurationPacketListenerImpl#disconnectAsync
Inspired by https://github.com/CraftCanvasMC/Canvas/blob/ver/1.21.8/canvas-server/minecraft-patches/sources/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java.patch
but Canvas loses its main thread checks so fixed that btw
diff --git a/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java b/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
index 40b4eeb561a91264b9ab6ddc49cb8a93daae84a4..97f4c546663fb19558246366a3d8d77f2b9327ef 100644
--- a/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
@@ -290,13 +290,14 @@ public class ServerConfigurationPacketListenerImpl extends ServerCommonPacketLis
// Paper start
@Override
public void disconnectAsync(final net.minecraft.network.DisconnectionDetails disconnectionInfo) {
- if (this.cserver.isPrimaryThread()) {
+ if (io.papermc.paper.threadedregions.RegionizedServer.isGlobalTickThread()) { // Luminol - Fix unpatched task returning of ServerConfigurationPacketListenerImpl#disconnectAsync
this.disconnect(disconnectionInfo);
return;
}
this.connection.setReadOnly();
- this.server.scheduleOnMain(() -> {
+ // this.server.scheduleOnMain(() -> { // Luminol - Fix unpatched task returning of ServerConfigurationPacketListenerImpl#disconnectAsync
+ io.papermc.paper.threadedregions.RegionizedServer.getInstance().addTask(() -> { // Luminol - Fix unpatched task returning of ServerConfigurationPacketListenerImpl#disconnectAsync
this.disconnect(disconnectionInfo); // Currently you cannot cancel disconnect during the config stage
});
}
@@ -0,0 +1,64 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Fri, 17 Apr 2026 23:24:07 +0800
Subject: [PATCH] Fix riding statistics desync
Referred to: https://github.com/CraftCanvasMC/Canvas/commit/057175b0c10d5a4d1d9059fd9d077750c32633b2
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index 34cc36f6bdf435593d16f2e7af280a27be048280..98d1ab3aa8dfbe571371d01544da2750339ce988 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -2537,7 +2537,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
}
}
- private void checkRidingStatistics(final double dx, final double dy, final double dz) {
+ public void checkRidingStatistics(final double dx, final double dy, final double dz) { // Luminol - Fix riding statics desync (make public)
if (this.isPassenger() && !didNotMove(dx, dy, dz)) {
int distance = Math.round((float)Math.sqrt(dx * dx + dy * dy + dz * dz) * 100.0F);
Entity vehicle = this.getVehicle();
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index a2b57f62b745dd21e215f919d335f14f5cb1706f..2cf8bdf6c4ab04ab42829b7af5eb89e6bd509d7a 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -4198,7 +4198,13 @@ public abstract class Entity
}
}
+ // Luminol start - Fix riding statics desync
public void adjustRiders(boolean teleport) {
+ this.adjustRiders(teleport, false);
+ }
+
+ public void adjustRiders(boolean teleport, boolean syncStatics) {
+ // Luminol end - Fix riding statics desync
java.util.ArrayDeque<EntityTreeNode> queue = new java.util.ArrayDeque<>();
queue.add(this);
@@ -4211,14 +4217,24 @@ public abstract class Entity
for (EntityTreeNode passenger : passengers) {
queue.add(passenger);
+ // Luminol start - Fix riding statics desync
+ final double oldX = passenger.root.getX();
+ final double oldY = passenger.root.getY();
+ final double oldZ = passenger.root.getZ();
+ // Luminol end - Fix riding statics desync
curr.root.positionRider(passenger.root, teleport ? Entity::snapTo : Entity::setPos);
+ // Luminol start - Fix riding statics desync
+ if (syncStatics && passenger.root instanceof net.minecraft.server.level.ServerPlayer serverPlayer) {
+ serverPlayer.checkRidingStatistics(serverPlayer.getX() - oldX, serverPlayer.getY() - oldY, serverPlayer.getZ() - oldZ);
+ }
+ // Luminol end - Fix riding statics desync
}
}
}
}
public void repositionAllPassengers(boolean teleport) {
- this.makePassengerTree().adjustRiders(teleport);
+ this.makePassengerTree().adjustRiders(teleport, true); // Luminol - Fix riding statics desync
}
protected EntityTreeNode makePassengerTree() {
@@ -0,0 +1,24 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Sat, 18 Apr 2026 10:45:19 +0800
Subject: [PATCH] Fix misbehaved ender pearls when player switched dimension
A fix of https://github.com/PaperMC/Folia/issues/421
Almost the same bug as:
https://github.com/PaperMC/Folia/pull/418 and https://github.com/PaperMC/Folia/issues/393
diff --git a/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownEnderpearl.java b/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownEnderpearl.java
index 501a0e89cc7bcfce6793f059b080b38666e0fb64..ad35eafa457f706879ba8a9c92b3a34e620ee2d6 100644
--- a/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownEnderpearl.java
+++ b/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownEnderpearl.java
@@ -264,7 +264,8 @@ public class ThrownEnderpearl extends ThrowableItemProjectile {
int previousChunkZ = SectionPos.blockToSectionCoord(this.position().z());
Entity owner = this.owner != null ? findOwnerIncludingDeadPlayer(serverLevel, this.owner.getUUID()) : null;
if (owner instanceof ServerPlayer serverPlayer
- && !owner.isAlive()
+ // && !owner.isAlive() // Luminol - Fix misbehaved ender pearls when player switched dimension
+ && (owner.getBukkitEntity().taskScheduler.isRetired() || serverPlayer.getHealth() <= 0.0D) // Luminol - Fix misbehaved ender pearls when player switched dimension
&& !serverPlayer.wonGame
&& serverPlayer.level().getGameRules().get(GameRules.ENDER_PEARLS_VANISH_ON_DEATH)) {
this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause
@@ -0,0 +1,21 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Thu, 2 Apr 2026 12:26:24 +0800
Subject: [PATCH] Fix long commands support
Some long commands can be run through the dialog command, but paper has prohibited it.
Revert to vanilla to fix it.
diff --git a/net/minecraft/network/protocol/game/ServerboundChatCommandPacket.java b/net/minecraft/network/protocol/game/ServerboundChatCommandPacket.java
index 491af2413a4e793a121fec368259ff8211ed031e..042cb4792b0084f143608f05dd08da133a31e435 100644
--- a/net/minecraft/network/protocol/game/ServerboundChatCommandPacket.java
+++ b/net/minecraft/network/protocol/game/ServerboundChatCommandPacket.java
@@ -12,7 +12,7 @@ public record ServerboundChatCommandPacket(String command) implements Packet<Ser
);
private ServerboundChatCommandPacket(final FriendlyByteBuf input) {
- this(input.readUtf(MAX_CHAT_PACKET_INPUT_SIZE)); // Paper - limit chat command inputs
+ this(me.earthme.luminol.config.modules.fixes.LongCommandSupportConfig.enabled ? input.readUtf() : input.readUtf(MAX_CHAT_PACKET_INPUT_SIZE)); // Paper - limit chat command inputs // Luminol - add support for long command inputs
}
private void write(final FriendlyByteBuf output) {
@@ -0,0 +1,19 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Mon, 20 Apr 2026 01:02:43 +0800
Subject: [PATCH] Fix creative player item pick
diff --git a/net/minecraft/world/entity/item/ItemEntity.java b/net/minecraft/world/entity/item/ItemEntity.java
index 223793bea54a483fefc66caa7e07b29ab351c339..303b9befb30d7156a9a71900ac1a6ea8f7e8d876 100644
--- a/net/minecraft/world/entity/item/ItemEntity.java
+++ b/net/minecraft/world/entity/item/ItemEntity.java
@@ -420,7 +420,7 @@ public class ItemEntity extends Entity implements TraceableEntity {
Item item = itemStack.getItem();
int orgCount = itemStack.getCount();
// CraftBukkit start - fire PlayerPickupItemEvent
- int canHold = player.getInventory().canHold(itemStack);
+ int canHold = player.hasInfiniteMaterials() ? orgCount : player.getInventory().canHold(itemStack); // Luminol - Fix creative item picking
int remaining = orgCount - canHold;
boolean flyAtPlayer = false; // Paper
@@ -0,0 +1,67 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Mon, 20 Apr 2026 01:10:30 +0800
Subject: [PATCH] Fix entity portal-teleport speed
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index c6434ff2e5c211887c2e98c8462a49e0e1cb5b21..534f103234eb92571c664cbcfea4ce4e990d3ea8 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -1492,7 +1492,27 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
foliaProfiler.startTimer(timerId);
try {
// Folia end - profiler
+ // Luminol start - Entity portal-teleport speed fix
if (isActive) { // Paper - EAR 2
+ if (!(entity instanceof Player) && entity.teleportTickType == 2) { // Luminol - after portal compensate tick
+ entity.tick();
+ entity.tick();
+ entity.teleportTickType = 0;
+ if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(entity)) {
+ return;
+ }
+ if (entity.handlePortal()) {
+ return;
+ }
+ } else if (!(entity instanceof Player) && entity.teleportTickType == 1) { // Luminol - portal teleport only
+ entity.teleportTickType++;
+ if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(entity)) {
+ return;
+ }
+ if (entity.handlePortal()) {
+ return;
+ }
+ } else {
entity.tick();
// Folia start - region threading
if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(entity)) {
@@ -1503,6 +1523,8 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
// portalled
return;
}
+ }
+ // Luminol end - Entity portal-teleport speed fix
// Folia end - region threading
} else {entity.inactiveTick();} // Paper - EAR 2
profiler.pop();
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index 2cf8bdf6c4ab04ab42829b7af5eb89e6bd509d7a..a52d5c0ba970d666fd57e075d14350ff07401bac 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -387,6 +387,7 @@ public abstract class Entity
public long activatedTick = Integer.MIN_VALUE;
public boolean isTemporarilyActive;
public long activatedImmunityTick = Integer.MIN_VALUE;
+ public int teleportTickType = 0;// Luminol - Entity portal-teleport speed fix
public void inactiveTick() {
}
@@ -3595,6 +3596,7 @@ public abstract class Entity
} else {
if (this.portalProcess == null || !this.portalProcess.isSamePortal(portal)) {
this.portalProcess = new PortalProcessor(portal, pos.immutable());
+ this.teleportTickType = 1; // Luminol - Entity portal-teleport speed fix
} else if (!this.portalProcess.isInsidePortalThisTick()) {
this.portalProcess.updateEntryPosition(pos.immutable());
this.portalProcess.setAsInsidePortalThisTick(true);
@@ -0,0 +1,19 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Wed, 24 Jun 2026 22:52:13 +0800
Subject: [PATCH] Fix player auto saving ignores interval
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index 98d1ab3aa8dfbe571371d01544da2750339ce988..264de9099b19779f20ad23c41d0967670759ae12 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -208,7 +208,7 @@ import org.slf4j.Logger;
public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patches.chunk_system.player.ChunkSystemServerPlayer { // Paper - rewrite chunk system
private static final Logger LOGGER = LogUtils.getLogger();
- public static final long LAST_SAVE_ABSENT = Long.MIN_VALUE; public long lastSave = LAST_SAVE_ABSENT; // Paper // Folia - threaded regions - changed to nanoTime
+ public static final long LAST_SAVE_ABSENT = Long.MIN_VALUE; public long lastSave = System.nanoTime(); // Paper // Folia - threaded regions - changed to nanoTime // Luminol - Fix player auto saving ignores interval
private static final int NEUTRAL_MOB_DEATH_NOTIFICATION_RADII_XZ = 32;
private static final int NEUTRAL_MOB_DEATH_NOTIFICATION_RADII_Y = 10;
private static final int FLY_STAT_RECORDING_SPEED = 25;
@@ -0,0 +1,98 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Bacteriawa <A3167717663@hotmail.com>
Date: Tue, 21 Apr 2026 01:15:09 +0800
Subject: [PATCH] Force disable builtin spark plugin
The spark passed down from paper has some memory leaking issue, so we fully removed it from the code to prevent that memory leaking issue.
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 83b9aab16124ddcc416f7b4cd95084e1c64fe5b7..7683c12d48d23dd43bce3d3b5633029c596930a5 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -672,8 +672,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
// Paper end - Configurable player collision; Handle collideRule team for player collision toggle
this.server.enablePlugins(org.bukkit.plugin.PluginLoadOrder.POSTWORLD);
- this.server.spark.registerCommandBeforePlugins(this.server); // Paper - spark
- this.server.spark.enableAfterPlugins(this.server); // Paper - spark
+ if (false) this.server.spark.registerCommandBeforePlugins(this.server); // Paper - spark // Luminol - Force disable builtin spark
+ if (false) this.server.spark.enableAfterPlugins(this.server); // Paper - spark // Luminol - Force disable builtin spark
io.papermc.paper.command.brigadier.PaperCommands.INSTANCE.setValid(); // Paper - reset invalid state for event fire below
io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner.INSTANCE.callReloadableRegistrarEvent(io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents.COMMANDS, io.papermc.paper.command.brigadier.PaperCommands.INSTANCE, org.bukkit.plugin.Plugin.class, io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.INITIAL); // Paper - call commands event for regular plugins
this.server.getCommandMap().registerServerAliases(); // Paper - relocate initial CommandMap#registerServerAliases() call
@@ -1087,7 +1087,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
Commands.COMMAND_SENDING_POOL.shutdownNow(); // Paper - Perf: Async command map building; Shutdown and don't bother finishing
// CraftBukkit start
if (this.server != null) {
- this.server.spark.disable(); // Paper - spark
+ if (false) this.server.spark.disable(); // Paper - spark // Luminol - Force disable builtin spark
this.server.disablePlugins();
this.server.waitForAsyncTasksShutdown(); // Paper - Wait for Async Tasks during shutdown
}
@@ -1352,7 +1352,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.statusIcon = this.loadStatusIcon().orElse(null);
this.status = this.buildServerStatus();
- this.server.spark.enableBeforePlugins(); // Paper - spark
+ if (false) this.server.spark.enableBeforePlugins(); // Paper - spark // Luminol - Force disable builtin spark
// Folia start - region threading
if (true) {
io.papermc.paper.threadedregions.RegionizedServer.getInstance().init(); // Folia - region threading - only after loading worlds
@@ -1666,7 +1666,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
if (this.emptyTicks >= emptyTickThreshold) {
- this.server.spark.tickStart(); // Paper - spark
+ if (false) this.server.spark.tickStart(); // Paper - spark // Luminol - Force disable builtin spark
if (this.emptyTicks == emptyTickThreshold) {
LOGGER.info("Server empty for {} seconds, pausing", this.pauseWhenEmptySeconds());
this.autoSave();
@@ -1685,7 +1685,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
// Paper end - avoid issues with certain tasks not processing during sleep
//this.server.spark.executeMainThreadTasks(); // Paper - spark // Folia - region threading
this.tickConnection();
- this.server.spark.tickEnd(((double)(System.nanoTime() - this.currentTickStart) / 1000000D)); // Paper - spark
+ if (false) this.server.spark.tickEnd(((double)(System.nanoTime() - this.currentTickStart) / 1000000D)); // Paper - spark // Luminol - Force disable builtin spark
return;
}
}
@@ -1698,7 +1698,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
};
// Folia end - region threading
- this.server.spark.tickStart(); // Paper - spark
+ if (false) this.server.spark.tickStart(); // Paper - spark // Luminol - Force disable builtin spark
new com.destroystokyo.paper.event.server.ServerTickStartEvent((int)region.getCurrentTick()).callEvent(); // Paper - Server Tick Events // Folia - region threading
// Folia start - region threading
if (region != null) {
@@ -1785,7 +1785,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
long remaining = scheduledEnd - endTime; // Folia - region ticking
new com.destroystokyo.paper.event.server.ServerTickEndEvent((int)io.papermc.paper.threadedregions.RegionizedServer.getCurrentTick(), ((double)(endTime - startTime) / 1000000D), remaining).callEvent(); // Folia - region ticking
// Paper end - Server Tick Events
- this.server.spark.tickEnd(((double)(endTime - startTime) / 1000000D)); // Paper - spark // Folia - region threading
+ if (false) this.server.spark.tickEnd(((double)(endTime - startTime) / 1000000D)); // Paper - spark // Folia - region threading // Luminol - Force disable builtin spark
// Folia - region threading
}
diff --git a/net/minecraft/server/dedicated/DedicatedServer.java b/net/minecraft/server/dedicated/DedicatedServer.java
index 1ad4704523a3d90414ac514488207b1b5def9afd..1145d5839e621a02b7972c2ff1142f51db2237de 100644
--- a/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/net/minecraft/server/dedicated/DedicatedServer.java
@@ -239,7 +239,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
this.paperConfigurations.initializeWorldDefaultsConfiguration(this.registryAccess());
// Paper end - initialize global and world-defaults configuration
me.earthme.luminol.config.ConfigManager.loadConfigFiles(); // Luminol - load config file
- this.server.spark.enableEarlyIfRequested(); // Paper - spark
+ if (false) this.server.spark.enableEarlyIfRequested(); // Paper - spark // Luminol - Force disable builtin spark
// Paper start - fix converting txt to json file; convert old users earlier after PlayerList creation but before file load/save
if (this.convertOldUsers()) {
this.services().nameToIdCache().save(false); // Paper
@@ -249,7 +249,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
org.spigotmc.WatchdogThread.doStart(org.spigotmc.SpigotConfig.timeoutTime, org.spigotmc.SpigotConfig.restartOnCrash); // Paper - start watchdog thread
consoleThread.start(); // Paper - Enhance console tab completions for brigadier commands; start console thread after MinecraftServer.console & PaperConfig are initialized
io.papermc.paper.command.PaperCommands.registerCommands(this); // Paper - setup /paper command
- this.server.spark.registerCommandBeforePlugins(this.server); // Paper - spark
+ if (false) this.server.spark.registerCommandBeforePlugins(this.server); // Paper - spark // Luminol - Force disable builtin spark
com.destroystokyo.paper.Metrics.PaperMetrics.startMetrics(); // Paper - start metrics
com.destroystokyo.paper.VersionHistoryManager.INSTANCE.getClass(); // Paper - load version history now
@@ -0,0 +1,24 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Sun, 28 Jun 2026 19:10:30 +0800
Subject: [PATCH] Prevent tamable animals check can teleport in an unloaded
chunk
Based on the implementation of Canvas(https://github.com/CraftCanvasMC/Canvas/commit/af2aacb12cbccfd328dda4961167786f5d9dad53)
We need to check canTeleport for TamableAnimal but this logic could do the check in another tick region itself, so we need to prevent that. But it needs to push off this check to schedule to the correct tickregion, which needs costly rewriting of this logic, so we could simply allow the read when the owner's position is loaded
diff --git a/net/minecraft/world/entity/TamableAnimal.java b/net/minecraft/world/entity/TamableAnimal.java
index 95ba821a895c7ea8fffcbf1df2e7b3c174463d9e..b0a01673224522552672b7c8c9aa219108e8010f 100644
--- a/net/minecraft/world/entity/TamableAnimal.java
+++ b/net/minecraft/world/entity/TamableAnimal.java
@@ -318,7 +318,8 @@ public abstract class TamableAnimal extends Animal implements OwnableEntity {
return false;
}
- BlockState blockStateBelow = this.level().getBlockState(pos.below());
+ BlockState blockStateBelow = this.level().getBlockStateIfLoaded(pos.below()); // Luminol - Prevent tamable animals check can teleport in an unloaded chunk
+ if (blockStateBelow == null) return false; // Luminol - Prevent tamable animals check can teleport in an unloaded chunk
if (!this.canFlyToOwner() && blockStateBelow.getBlock() instanceof LeavesBlock) {
return false;
}
@@ -0,0 +1,88 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Fri, 1 May 2026 21:52:07 +0800
Subject: [PATCH] Prevent teleprotAsync calls in move events
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index c1faaf7d514c23d756aa626fa93aabd8bb503463..0c5fb17e7cce18eac68183cc3b5de6fc6451dc50 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -730,7 +730,9 @@ public class ServerGamePacketListenerImpl
Location oldTo = to.clone();
PlayerMoveEvent event = new PlayerMoveEvent(player, from, to);
+ this.player.blockTeleportAsync = true; // Luminol - Prevent teleprotAsync calls during move events
this.cserver.getPluginManager().callEvent(event);
+ this.player.blockTeleportAsync = false; // Luminol - Prevent teleprotAsync calls during move events
// If the event is cancelled we move the player back to their old location.
if (event.isCancelled()) {
@@ -1740,7 +1742,9 @@ public class ServerGamePacketListenerImpl
Location oldTo = to.clone();
PlayerMoveEvent event = new PlayerMoveEvent(player, from, to);
+ this.player.blockTeleportAsync = true; // Luminol - Prevent teleprotAsync calls during move events
this.cserver.getPluginManager().callEvent(event);
+ this.player.blockTeleportAsync = false; // Luminol - Prevent teleprotAsync calls during move events
// If the event is cancelled we move the player back to their old location.
if (event.isCancelled()) {
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index a52d5c0ba970d666fd57e075d14350ff07401bac..72f89eaba2ca8b66fa07a6378d93917dec83c688 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -4476,12 +4476,25 @@ public abstract class Entity
teleportTarget.cause(), teleportFlags, teleportComplete
);
}
+ // Luminol start - Prevent teleprotAsync calls in move events
+ public boolean blockTeleportAsync = false;
+ // Luminol end
public final boolean teleportAsync(ServerLevel destination, Vec3 pos, Float yaw, Float pitch, Vec3 velocity,
org.bukkit.event.player.PlayerTeleportEvent.TeleportCause cause, long teleportFlags,
java.util.function.Consumer<Entity> teleportComplete) {
ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this, "Cannot teleport entity async");
+ // Luminol start - Prevent teleprotAsync calls in move events
+ if (this.blockTeleportAsync && me.earthme.luminol.config.modules.fixes.PreventIncorrectTeleportAsyncConfig.enabled) {
+ if (me.earthme.luminol.config.modules.fixes.PreventIncorrectTeleportAsyncConfig.throwWhenCaught) {
+ throw new IllegalStateException("Call teleportAsync during move events!");
+ }
+
+ LOGGER.error("Calling teleportAsync during move events!", new Throwable());
+ return false;
+ }
+ // Luminol end
if (!ServerLevel.isInSpawnableBounds(new BlockPos(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getBlockX(pos), ca.spottedleaf.moonrise.common.util.CoordinateUtils.getBlockY(pos), ca.spottedleaf.moonrise.common.util.CoordinateUtils.getBlockZ(pos)))) {
return false;
}
diff --git a/net/minecraft/world/entity/vehicle/boat/AbstractBoat.java b/net/minecraft/world/entity/vehicle/boat/AbstractBoat.java
index 3fcd534cee1f605a710ffc43eff4b5106c7dd40c..9b887b40236a212de513e3ceece1abfec5372ae2 100644
--- a/net/minecraft/world/entity/vehicle/boat/AbstractBoat.java
+++ b/net/minecraft/world/entity/vehicle/boat/AbstractBoat.java
@@ -282,7 +282,9 @@ public abstract class AbstractBoat extends VehicleEntity implements Leashable {
if (this.lastLocation != null && !this.lastLocation.equals(to)) {
org.bukkit.event.vehicle.VehicleMoveEvent event = new org.bukkit.event.vehicle.VehicleMoveEvent(vehicle, this.lastLocation, to);
+ this.blockTeleportAsync = true; // Luminol - Prevent teleprotAsync calls during move events
event.callEvent();
+ this.blockTeleportAsync = false; // Luminol - Prevent teleprotAsync calls during move events
}
this.lastLocation = vehicle.getLocation();
// CraftBukkit end
diff --git a/net/minecraft/world/entity/vehicle/minecart/AbstractMinecart.java b/net/minecraft/world/entity/vehicle/minecart/AbstractMinecart.java
index 5ad77636d0681d1f0341cff4482ba6ad52f3583b..0ae8b5222c5f2f608af352cbaa866c493dd065aa 100644
--- a/net/minecraft/world/entity/vehicle/minecart/AbstractMinecart.java
+++ b/net/minecraft/world/entity/vehicle/minecart/AbstractMinecart.java
@@ -318,7 +318,9 @@ public abstract class AbstractMinecart extends VehicleEntity {
new org.bukkit.event.vehicle.VehicleUpdateEvent(vehicle).callEvent();
if (!from.equals(to)) {
+ this.blockTeleportAsync = true; // Luminol - Prevent teleprotAsync calls during move events
new org.bukkit.event.vehicle.VehicleMoveEvent(vehicle, from, to).callEvent();
+ this.blockTeleportAsync = false; // Luminol - Prevent teleprotAsync calls during move events
}
// CraftBukkit end
this.updateFluidInteraction();
@@ -0,0 +1,47 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Sun, 28 Jun 2026 19:19:31 +0800
Subject: [PATCH] Sync dragon part when teleportation or firstly created
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/server/ServerEntityLookup.java b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/server/ServerEntityLookup.java
index b31b55f00e2ce1bd6c1011fe852bd1dbb37de524..75c36cbf86e56d4993169688a13f22ccef555d92 100644
--- a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/server/ServerEntityLookup.java
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/server/ServerEntityLookup.java
@@ -97,6 +97,7 @@ public final class ServerEntityLookup extends EntityLookup {
if (entity instanceof ThrownEnderpearl enderpearl) {
this.addEnderPearl(CoordinateUtils.getChunkKey(enderpearl.chunkPosition()), enderpearl.getId()); // Folia - region threading
}
+ if (entity instanceof net.minecraft.world.entity.boss.enderdragon.EnderDragon dragon) dragon.syncDragonPartsAfterTeleportTransform(); // Luminol - Sync dragon part when teleportation or firstly created
entity.registerScheduler(); // Paper - optimise Folia entity scheduler
}
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index 72f89eaba2ca8b66fa07a6378d93917dec83c688..6b7382f06a8c76b216b081e66789fe51466b1e29 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -4460,6 +4460,7 @@ public abstract class Entity
Entity copy = this.getType().create(destination, EntitySpawnReason.DIMENSION_TRAVEL);
copy.restoreFrom(this);
copy.transform(pos, yaw, pitch, velocity);
+ if (copy instanceof net.minecraft.world.entity.boss.enderdragon.EnderDragon dragon) dragon.syncDragonPartsAfterTeleportTransform(); // Luminol - Sync dragon part when teleportation or firstly created
// vanilla code used to call remove _after_ copying, and some stuff is required to be after copy - so add hook here
// for example, clearing of inventory after switching dimensions
this.postRemoveAfterChangingDimensions();
diff --git a/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java b/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java
index ab921ab4b4cc860abd77744f2bd201013e136e51..fd85816ec78c99f45c740ebf8052ff41f4859ed6 100644
--- a/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java
+++ b/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java
@@ -1011,4 +1011,12 @@ public class EnderDragon extends Mob implements Enemy {
return 500;
}
// Paper end - init expToDrop for already dying spawned dragon
+
+ // Luminol start - Sync dragon part when teleportation or firstly created
+ public void syncDragonPartsAfterTeleportTransform() {
+ for (EnderDragonPart part : this.subEntities) {
+ this.tickPart(part, 0.0, 0.0, 0.0); // offset -> 0.0
+ }
+ }
+ // Luminol end
}
@@ -0,0 +1,63 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Sun, 28 Jun 2026 19:32:34 +0800
Subject: [PATCH] Teleport async if entity was moving to another region at once
On folia, entity usually cannot move out of the tickregion, but sometimes it actually does(like some end pearl gun that can shoot an end pearl to the block faraway than 10000 blocks even more). To fix this, we added a temporary fix which teleport these entities to the destination instead running its move logics so that we could ensure anything is under control.But one thing need to consider is that teleportAsync is actually calling halfway of the entity tick and there is still something running when teleportAsync called, which is actually modified the entity in another thread, so there is still need an improvement
Reference from : https://github.com/KaiijuMC/Kaiiju/blob/ver/1.20.1/patches/server/0040-Teleport-async-if-we-cannot-move-entity-off-main.patch
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index 6b7382f06a8c76b216b081e66789fe51466b1e29..8c909108a0c468f353837787188c12f907369b9b 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -1163,6 +1163,19 @@ public abstract class Entity
this.moveStartZ = this.getZ();
this.moveVector = delta;
}
+ //Luminol start - Fix high position moving
+ // Filter the threads as it may be called by the chunk system worker thread
+ if (me.earthme.luminol.config.modules.fixes.FoliaEntityMovingFixConfig.enabled && ca.spottedleaf.moonrise.common.util.TickThread.isTickThread()){
+ var finalPosition = delta.add(this.position);
+ // not NaN (Prevent incorrect checks under NaN minecarts)
+ if (!Double.isNaN(finalPosition.x) && !Double.isNaN(finalPosition.y) && !Double.isNaN(finalPosition.z)) {
+ // kill tick passively if it's moving out of region and we'll catch this exception
+ if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(this.level,finalPosition)) {
+ throw new me.earthme.luminol.utils.EntityMoveOutOfRegionException(this, delta, moverType);
+ }
+ }
+ }
+ //Luminol end
try {
// Paper end - detailed watchdog information
if (this.noPhysics) {
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
index a0eb1f608a99301e0197ab1d1a6fbbb0a0be4ce0..dc28223684091cd0d8dbf7ff01626b20e4f1b662 100644
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
@@ -1571,6 +1571,25 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
public <T extends Entity> void guardEntityTick(final Consumer<T> tick, final T entity) {
try {
tick.accept(entity);
+ } catch (me.earthme.luminol.utils.EntityMoveOutOfRegionException moveOutOfRegionException) { // Luminol - Teleport async if entity was moving to another region at once
+ // Luminol start - Teleport async if entity was moving to another region at once
+ final Entity ent = moveOutOfRegionException.getEntity();
+ var currPosition = ent.position();
+ var toPosition = moveOutOfRegionException.getMovement().add(currPosition);
+
+ if (me.earthme.luminol.config.modules.fixes.FoliaEntityMovingFixConfig.warnOnDetected) {
+ MinecraftServer.LOGGER.warn("Entity {} with entityId {} has tried moving to another region!",ent, ent.getId());
+ }
+
+ ent.getBukkitEntity().taskScheduler.schedule(entityFresh -> entityFresh.teleportAsync(
+ (ServerLevel) entityFresh.level(),
+ toPosition,
+ entityFresh.getYRot(), entityFresh.getXRot(),
+ entityFresh.getDeltaMovement(), org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.UNKNOWN,
+ Entity.TELEPORT_FLAG_LOAD_CHUNK | Entity.TELEPORT_FLAG_TELEPORT_PASSENGERS,
+ null
+ ), null, 1L);
+ // Luminol end
} catch (Throwable t) {
// Paper start - Prevent block entity and entity crashes
final String msg = String.format("Entity threw exception at %s:%s,%s,%s", io.papermc.paper.util.MCUtil.getLevelName(entity.level()), entity.getX(), entity.getY(), entity.getZ());
@@ -0,0 +1,23 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Bacteriawa <A3167717663@hotmail.com>
Date: Tue, 21 Apr 2026 01:28:05 +0800
Subject: [PATCH] Temporarily fix teleport yam and pitch
diff --git a/net/minecraft/server/commands/TeleportCommand.java b/net/minecraft/server/commands/TeleportCommand.java
index 13d7965fd4f99f0848b080349df41f8b1c31a19b..37268fe5d1f21d5b08ed4a1c0e5777bcb062c8e6 100644
--- a/net/minecraft/server/commands/TeleportCommand.java
+++ b/net/minecraft/server/commands/TeleportCommand.java
@@ -248,8 +248,10 @@ public class TeleportCommand {
// Folia start - region threading
if (true) {
Vec3 posFinal = new Vec3(x, y, z);
- Float yawFinal = Float.valueOf(newYRot);
- Float pitchFinal = Float.valueOf(newXRot);
+ //Float yawFinal = Float.valueOf(newYRot); // Luminol - fix teleport yaw issue
+ //Float pitchFinal = Float.valueOf(newXRot); // Luminol - fix teleport yaw issue
+ Float yawFinal = Float.valueOf(newYRot + victim.getYRot()); // Luminol - fix teleport yaw issue
+ Float pitchFinal = Float.valueOf(newXRot + victim.getXRot()); // Luminol - fix teleport pitch issue
victim.getBukkitEntity().taskScheduler.schedule((Entity nmsEntity) -> {
nmsEntity.stopRiding();
nmsEntity.teleportAsync(
@@ -0,0 +1,288 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Sun, 28 Jun 2026 19:47:24 +0800
Subject: [PATCH] Improved Server Health Report
diff --git a/io/papermc/paper/threadedregions/commands/CommandServerHealth.java b/io/papermc/paper/threadedregions/commands/CommandServerHealth.java
index aa860cfe9de616f185163fe74e0f4f49bd13a805..05d4f73340412f737224cb1443ecf1e33586ed38 100644
--- a/io/papermc/paper/threadedregions/commands/CommandServerHealth.java
+++ b/io/papermc/paper/threadedregions/commands/CommandServerHealth.java
@@ -29,6 +29,14 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
+// Luminol start - Improved Server Health Report
+import java.util.concurrent.TimeUnit;
+import java.lang.management.ManagementFactory;
+import java.lang.management.MemoryMXBean;
+import java.lang.management.MemoryUsage;
+import java.lang.management.RuntimeMXBean;
+import org.jetbrains.annotations.NotNull;
+// Luminol end
public final class CommandServerHealth extends Command {
@@ -45,7 +53,7 @@ public final class CommandServerHealth extends Command {
private static final TextColor HEADER = TextColor.color(79, 164, 240);
private static final TextColor PRIMARY = TextColor.color(48, 145, 237);
private static final TextColor SECONDARY = TextColor.color(104, 177, 240);
- private static final TextColor INFORMATION = TextColor.color(145, 198, 243);
+ private static final TextColor INFORMATION = TextColor.color(180, 220, 255); // Luminol start - Improved Server Health Report
private static final TextColor LIST = TextColor.color(33, 97, 188);
public CommandServerHealth() {
@@ -60,9 +68,11 @@ public final class CommandServerHealth extends Command {
return Component.text()
.append(Component.text(prefix, PRIMARY, TextDecoration.BOLD))
.append(Component.text(ONE_DECIMAL_PLACES.get().format(util * 100.0), CommandUtil.getUtilisationColourRegion(util)))
- .append(Component.text("% util at ", PRIMARY))
+ .append(Component.text("% util", PRIMARY))
+ .append(Component.text(" | ", SECONDARY))
.append(Component.text(TWO_DECIMAL_PLACES.get().format(mspt), CommandUtil.getColourForMSPT(mspt)))
- .append(Component.text(" MSPT at ", PRIMARY))
+ .append(Component.text(" mspt", PRIMARY))
+ .append(Component.text(" | ", SECONDARY))
.append(Component.text(TWO_DECIMAL_PLACES.get().format(tps), CommandUtil.getColourForTPS(tps)))
.append(Component.text(" TPS" + (newline ? "\n" : ""), PRIMARY))
.build();
@@ -81,7 +91,7 @@ public final class CommandServerHealth extends Command {
private static boolean executeRegion(final CommandSender sender, final String commandLabel, final String[] args) {
final ThreadedRegionizer.ThreadedRegion<TickRegions.TickRegionData, TickRegions.TickRegionSectionData> region =
- TickRegionScheduler.getCurrentRegion();
+ TickRegionScheduler.getCurrentRegion();
if (region == null) {
sender.sendMessage(Component.text("You are not in a region currently", NamedTextColor.RED));
return true;
@@ -105,8 +115,7 @@ public final class CommandServerHealth extends Command {
final double tps1m = report1m.tpsData().segmentAll().average();
final double mspt1m = report1m.timePerTickData().segmentAll().average() / 1.0E6;
- final int yLoc = 80;
- final String location = "[w:'" + world.getWorld().getName() + "'," + centerBlockX + "," + yLoc + "," + centerBlockZ + "]";
+ final String location = world.getWorld().getName() + " (" + centerBlockX + ", " + centerBlockZ + ")";
final Component line = Component.text()
.append(Component.text("Region around block ", PRIMARY))
@@ -144,7 +153,7 @@ public final class CommandServerHealth extends Command {
}
final List<ThreadedRegionizer.ThreadedRegion<TickRegions.TickRegionData, TickRegions.TickRegionSectionData>> regions =
- new ArrayList<>();
+ new ArrayList<>();
for (final World bukkitWorld : Bukkit.getWorlds()) {
final ServerLevel world = ((CraftWorld)bukkitWorld).getHandle();
@@ -163,6 +172,18 @@ public final class CommandServerHealth extends Command {
final long currTime = System.nanoTime();
final TickData.TickReportData globalTickReport = RegionizedServer.getGlobalTickData().getTickReport15s(currTime);
+ final MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
+ final MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
+ final long usedMemory = heapUsage.getUsed() / (1024 * 1024);
+ final long maxMemory = heapUsage.getMax() / (1024 * 1024);
+
+ final double memPercent = (double) usedMemory / maxMemory * 100.0;
+ final TextColor memColor = memPercent < 60 ? CommandUtil.getUtilisationColourRegion(0.0) : (memPercent < 85 ? NamedTextColor.YELLOW : NamedTextColor.RED);
+
+ final RuntimeMXBean runtimeBean = ManagementFactory.getRuntimeMXBean();
+ final long uptime = runtimeBean.getUptime();
+ final String uptimeStr = formatUptime(uptime);
+
for (final ThreadedRegionizer.ThreadedRegion<TickRegions.TickRegionData, TickRegions.TickRegionSectionData> region : regions) {
final TickData.TickReportData report = region.getData().getRegionSchedulingHandle().getTickReport15s(currTime);
tpsByRegion.add(report == null ? 20.0 : report.tpsData().segmentAll().average());
@@ -174,31 +195,27 @@ public final class CommandServerHealth extends Command {
final double loadRate = ca.spottedleaf.moonrise.patches.chunk_system.scheduling.task.ChunkFullTask.loadRate(currTime);
totalUtil += globalTickReport.utilisation();
+ final TextColor utilisationColor = CommandUtil.getUtilisationColourRegion(totalUtil / (double)maxThreadCount);
tpsByRegion.sort(null);
if (!tpsByRegion.isEmpty()) {
minTps = tpsByRegion.getDouble(0);
maxTps = tpsByRegion.getDouble(tpsByRegion.size() - 1);
-
final int middle = tpsByRegion.size() >> 1;
if ((tpsByRegion.size() & 1) == 0) {
- // even, average the two middle points
medianTps = (tpsByRegion.getDouble(middle - 1) + tpsByRegion.getDouble(middle)) / 2.0;
} else {
- // odd, can just grab middle
medianTps = tpsByRegion.getDouble(middle);
}
} else {
- // no regions = green
minTps = medianTps = maxTps = 20.0;
}
final List<ObjectObjectImmutablePair<ThreadedRegionizer.ThreadedRegion<TickRegions.TickRegionData, TickRegions.TickRegionSectionData>, TickData.TickReportData>>
- regionsBelowThreshold = new ArrayList<>();
+ regionsBelowThreshold = new ArrayList<>();
for (int i = 0, len = regions.size(); i < len; ++i) {
final TickData.TickReportData report = reportsByRegion.get(i);
-
regionsBelowThreshold.add(new ObjectObjectImmutablePair<>(regions.get(i), report));
}
@@ -207,11 +224,18 @@ public final class CommandServerHealth extends Command {
final TickData.TickReportData report2 = p2.right();
final double util1 = report1 == null ? 0.0 : report1.utilisation();
final double util2 = report2 == null ? 0.0 : report2.utilisation();
-
- // we want the largest first
return Double.compare(util2, util1);
});
+ long totalChunks = 0;
+ long totalEntities = 0;
+
+ for (final ThreadedRegionizer.ThreadedRegion<TickRegions.TickRegionData, TickRegions.TickRegionSectionData> region : regions) {
+ final TickRegions.RegionStats stats = region.getData().getRegionStats();
+ totalChunks += stats.getChunkCount();
+ totalEntities += stats.getEntityCount();
+ }
+
final TextComponent.Builder lowestRegionsBuilder = Component.text();
if (sender instanceof Player) {
@@ -219,23 +243,18 @@ public final class CommandServerHealth extends Command {
}
for (int i = 0, len = Math.min(lowestRegionsCount, regionsBelowThreshold.size()); i < len; ++i) {
final ObjectObjectImmutablePair<ThreadedRegionizer.ThreadedRegion<TickRegions.TickRegionData, TickRegions.TickRegionSectionData>, TickData.TickReportData>
- pair = regionsBelowThreshold.get(i);
+ pair = regionsBelowThreshold.get(i);
final TickData.TickReportData report = pair.right();
final ThreadedRegionizer.ThreadedRegion<TickRegions.TickRegionData, TickRegions.TickRegionSectionData> region =
- pair.left();
+ pair.left();
- if (report == null) {
- // skip regions with no data
- continue;
- }
+ if (report == null) continue;
final ServerLevel world = region.regioniser.world;
final ChunkPos chunkCenter = region.getCenterChunk();
- if (chunkCenter == null) {
- // region does not exist anymore
- continue;
- }
+ if (chunkCenter == null) continue;
+
final int centerBlockX = ((chunkCenter.x() << 4) | 7);
final int centerBlockZ = ((chunkCenter.z() << 4) | 7);
final double util = report.utilisation();
@@ -243,18 +262,21 @@ public final class CommandServerHealth extends Command {
final double mspt = report.timePerTickData().segmentAll().average() / 1.0E6;
final int yLoc = 80;
- final String location = "[w:'" + world.getWorld().getName() + "'," + centerBlockX + "," + yLoc + "," + centerBlockZ + "]";
+ final String location = world.getWorld().getName() + " (" + centerBlockX + ", " + centerBlockZ + ")";
+
final Component line = Component.text()
.append(Component.text(" - ", LIST, TextDecoration.BOLD))
- .append(Component.text("Region around block ", PRIMARY))
+ .append(Component.text("Region at ", PRIMARY))
.append(Component.text(location, INFORMATION))
.append(Component.text(":\n", PRIMARY))
.append(Component.text(" ", PRIMARY))
.append(Component.text(ONE_DECIMAL_PLACES.get().format(util * 100.0), CommandUtil.getUtilisationColourRegion(util)))
- .append(Component.text("% util at ", PRIMARY))
+ .append(Component.text("% util", PRIMARY))
+ .append(Component.text(" | ", SECONDARY))
.append(Component.text(TWO_DECIMAL_PLACES.get().format(mspt), CommandUtil.getColourForMSPT(mspt)))
- .append(Component.text(" MSPT at ", PRIMARY))
+ .append(Component.text(" mspt", PRIMARY))
+ .append(Component.text(" | ", SECONDARY))
.append(Component.text(TWO_DECIMAL_PLACES.get().format(tps), CommandUtil.getColourForTPS(tps)))
.append(Component.text(" TPS\n", PRIMARY))
@@ -270,29 +292,53 @@ public final class CommandServerHealth extends Command {
sender.sendMessage(
Component.text()
- .append(Component.text("Server Health Report\n", HEADER, TextDecoration.BOLD))
+ .append(Component.text("Server Health Report", HEADER, TextDecoration.BOLD))
+ .append(Component.text(" (Uptime: ", SECONDARY))
+ .append(Component.text(uptimeStr, utilisationColor))
+ .append(Component.text(")\n", SECONDARY))
.append(Component.text(" - ", LIST, TextDecoration.BOLD))
.append(Component.text("Online Players: ", PRIMARY))
- .append(Component.text(Bukkit.getOnlinePlayers().size() + "\n", INFORMATION))
+ .append(Component.text(Bukkit.getOnlinePlayers().size(), INFORMATION))
+ .append(Component.newline())
.append(Component.text(" - ", LIST, TextDecoration.BOLD))
.append(Component.text("Total regions: ", PRIMARY))
- .append(Component.text(regions.size() + "\n", INFORMATION))
+ .append(Component.text(regions.size(), INFORMATION))
+ .append(Component.text(", ", PRIMARY))
+ .append(Component.text("Total Chunks: ", PRIMARY))
+ .append(Component.text(NO_DECIMAL_PLACES.get().format(totalChunks), INFORMATION))
+ .append(Component.text(", ", PRIMARY))
+ .append(Component.text("Total Entities: ", PRIMARY))
+ .append(Component.text(NO_DECIMAL_PLACES.get().format(totalEntities) + "\n", INFORMATION))
.append(Component.text(" - ", LIST, TextDecoration.BOLD))
.append(Component.text("Utilisation: ", PRIMARY))
- .append(Component.text(ONE_DECIMAL_PLACES.get().format(totalUtil * 100.0), CommandUtil.getUtilisationColourRegion(totalUtil / (double)maxThreadCount)))
- .append(Component.text("% / ", PRIMARY))
+ .append(Component.text(ONE_DECIMAL_PLACES.get().format(totalUtil * 100.0), utilisationColor))
+ .append(Component.text("%", PRIMARY))
+ .append(Component.text(" / ", SECONDARY))
.append(Component.text(ONE_DECIMAL_PLACES.get().format(maxThreadCount * 100.0), INFORMATION))
.append(Component.text("%\n", PRIMARY))
.append(Component.text(" - ", LIST, TextDecoration.BOLD))
.append(Component.text("Load rate: ", PRIMARY))
- .append(Component.text(TWO_DECIMAL_PLACES.get().format(loadRate) + ", ", INFORMATION))
+ .append(Component.text(TWO_DECIMAL_PLACES.get().format(loadRate), INFORMATION))
+ .append(Component.text(", ", PRIMARY))
.append(Component.text("Gen rate: ", PRIMARY))
.append(Component.text(TWO_DECIMAL_PLACES.get().format(genRate) + "\n", INFORMATION))
+ .append(Component.text(" - ", LIST, TextDecoration.BOLD))
+ .append(Component.text("Memory: ", PRIMARY))
+ .append(Component.text(NO_DECIMAL_PLACES.get().format(usedMemory), memColor))
+ .append(Component.text(" MB", memColor))
+ .append(Component.text(" / ", SECONDARY))
+ .append(Component.text(NO_DECIMAL_PLACES.get().format(maxMemory), INFORMATION))
+ .append(Component.text(" MB", INFORMATION))
+ .append(Component.text(" (", SECONDARY))
+ .append(Component.text(ONE_DECIMAL_PLACES.get().format(memPercent) + "%", memColor))
+ .append(Component.text(")", SECONDARY))
+ .append(Component.newline())
+
.append(Component.text(" - ", LIST, TextDecoration.BOLD))
.append(Component.text("Lowest Region TPS: ", PRIMARY))
.append(Component.text(TWO_DECIMAL_PLACES.get().format(minTps) + "\n", CommandUtil.getColourForTPS(minTps)))
@@ -361,4 +407,21 @@ public final class CommandServerHealth extends Command {
}
return new ArrayList<>();
}
+
+ // Luminol start - Improved Server Health Report
+ private static @NotNull String formatUptime(long uptimeMillis) {
+ long days = TimeUnit.MILLISECONDS.toDays(uptimeMillis);
+ long hours = TimeUnit.MILLISECONDS.toHours(uptimeMillis) % 24;
+ long minutes = TimeUnit.MILLISECONDS.toMinutes(uptimeMillis) % 60;
+ long seconds = TimeUnit.MILLISECONDS.toSeconds(uptimeMillis) % 60;
+
+ StringBuilder sb = new StringBuilder();
+ if (days > 0) sb.append(days).append("d ");
+ if (hours > 0) sb.append(hours).append("h ");
+ if (minutes > 0) sb.append(minutes).append("m ");
+ sb.append(seconds).append("s");
+
+ return sb.toString();
+ }
+ // Luminol end
}
\ No newline at end of file
@@ -0,0 +1,522 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Fri, 1 May 2026 18:37:50 +0800
Subject: [PATCH] World load/unload APIs
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
好吧这里这一坨需要说明的太多了而且我也不是很会翻译所以就用中文注释了)
这一整个实现使用了一种安全点的思路,相对于folia pr里和canvas的让每个region自己卸载, 我更偏向于将调度器拆分到每个level中然后实现类似于单个level停服的逻辑,这样会更加符合直觉而且实现要更加快速而且易于排查问题
由于世界卸载可能发生在玩家登录或者传送的过程中,所以我使用了引用计数器来避免这个竟态条件的发生
diff --git a/io/papermc/paper/threadedregions/RegionShutdownThread.java b/io/papermc/paper/threadedregions/RegionShutdownThread.java
index f2208bcd5ccf3bd5a428e8ea3bf3ff26b3c9483b..d5c4e9b82f2ebc135b6747d19678b1a15e808ec8 100644
--- a/io/papermc/paper/threadedregions/RegionShutdownThread.java
+++ b/io/papermc/paper/threadedregions/RegionShutdownThread.java
@@ -14,11 +14,11 @@ import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
-public final class RegionShutdownThread extends ca.spottedleaf.moonrise.common.util.TickThread {
+public class RegionShutdownThread extends ca.spottedleaf.moonrise.common.util.TickThread { // Luminol - Level schedulers - Make public
- private static final Logger LOGGER = LogUtils.getClassLogger();
+ public static final Logger LOGGER = LogUtils.getClassLogger(); // Luminol - Level schedulers - Make public
- ThreadedRegionizer.ThreadedRegion<TickRegions.TickRegionData, TickRegions.TickRegionSectionData> shuttingDown;
+ public ThreadedRegionizer.ThreadedRegion<TickRegions.TickRegionData, TickRegions.TickRegionSectionData> shuttingDown; // Luminol - Level schedulers - Make public
public RegionShutdownThread(final String name) {
super(name);
@@ -52,7 +52,7 @@ public final class RegionShutdownThread extends ca.spottedleaf.moonrise.common.u
// the data required to do that is regionised, and we can only access it when we OWN the region, and we do not.
// Thus, the only operation that the shutdown thread will perform
- private void saveLevelData(final ServerLevel world) {
+ public void saveLevelData(final ServerLevel world) { // Luminol - Level hot unload apis - Make public
try {
world.saveLevelData(true);
} catch (final Throwable thr) {
@@ -60,7 +60,7 @@ public final class RegionShutdownThread extends ca.spottedleaf.moonrise.common.u
}
}
- private void finishTeleportations(final ThreadedRegionizer.ThreadedRegion<TickRegions.TickRegionData, TickRegions.TickRegionSectionData> region,
+ public void finishTeleportations(final ThreadedRegionizer.ThreadedRegion<TickRegions.TickRegionData, TickRegions.TickRegionSectionData> region, // Luminol - Level hot unload apis - Make public
final ServerLevel world) {
try {
this.shuttingDown = region;
@@ -94,7 +94,7 @@ public final class RegionShutdownThread extends ca.spottedleaf.moonrise.common.u
}
}
- private void saveRegionChunks(final ThreadedRegionizer.ThreadedRegion<TickRegions.TickRegionData, TickRegions.TickRegionSectionData> region,
+ public void saveRegionChunks(final ThreadedRegionizer.ThreadedRegion<TickRegions.TickRegionData, TickRegions.TickRegionSectionData> region, // Luminol - Level hot unload apis - Make public
final boolean last) {
ChunkPos center = null;
try {
@@ -109,7 +109,7 @@ public final class RegionShutdownThread extends ca.spottedleaf.moonrise.common.u
}
}
- private void haltChunkSystem(final ServerLevel world) {
+ public void haltChunkSystem(final ServerLevel world) { // Luminol - Level hot unload apis - Make public
try {
world.moonrise$getChunkTaskScheduler().chunkHolderManager.close(false, true, true, false, false);
} catch (final Throwable thr) {
@@ -117,7 +117,7 @@ public final class RegionShutdownThread extends ca.spottedleaf.moonrise.common.u
}
}
- private void closePlayerInventories(final ThreadedRegionizer.ThreadedRegion<TickRegions.TickRegionData, TickRegions.TickRegionSectionData> region) {
+ public void closePlayerInventories(final ThreadedRegionizer.ThreadedRegion<TickRegions.TickRegionData, TickRegions.TickRegionSectionData> region) { // Luminol - Level hot unload apis - Make public
ChunkPos center = null;
try {
this.shuttingDown = region;
@@ -150,7 +150,7 @@ public final class RegionShutdownThread extends ca.spottedleaf.moonrise.common.u
}
@Override
- public final void run() {
+ public void run() { // Luminol - Level hot unload apis - Make public
// await scheduler termination
LOGGER.info("Awaiting scheduler termination for 60s...");
if (TickRegions.getScheduler().halt(true, TimeUnit.SECONDS.toNanos(60L))) {
diff --git a/io/papermc/paper/threadedregions/RegionizedServer.java b/io/papermc/paper/threadedregions/RegionizedServer.java
index aa575f3b76ef70ffb9f0410e7e5cfe7af384bfbd..ff90abb47203c03636d3175f2b3efcf43517b3b0 100644
--- a/io/papermc/paper/threadedregions/RegionizedServer.java
+++ b/io/papermc/paper/threadedregions/RegionizedServer.java
@@ -63,6 +63,12 @@ public final class RegionizedServer {
this.worlds.add(world);
}
+ // Luminol start - Level hot unload apis
+ public void removeWorld(final ServerLevel world) {
+ this.worlds.remove(world);
+ }
+ // Luminol end
+
public void init() {
// call init event _before_ scheduling anything
new RegionizedServerInitEvent().callEvent();
diff --git a/io/papermc/paper/threadedregions/TickRegionScheduler.java b/io/papermc/paper/threadedregions/TickRegionScheduler.java
index 5a72fa0d72bfdb927293abe921c57f6ae964a6ec..f8344f640f119e3865f2718d1abd0ede9bdb8aa8 100644
--- a/io/papermc/paper/threadedregions/TickRegionScheduler.java
+++ b/io/papermc/paper/threadedregions/TickRegionScheduler.java
@@ -49,7 +49,7 @@ public final class TickRegionScheduler {
}
// Folia end - watchdog
- private final Scheduler scheduler;
+ public final Scheduler scheduler; // Luminol - Level schedulers - Make public
public static enum SchedulerType {
EDF,
@@ -220,7 +220,15 @@ public final class TickRegionScheduler {
*/
public void scheduleRegion(final RegionScheduleHandle region) {
region.scheduler = this;
+ // this.scheduler.schedule(region); // Luminol - Level schedulers
+ // Luminol start - Level schedulers
+ if(region.region != null) {
+ region.region.world.levelScheduler.schedule(region);
+ return;
+ }
+
this.scheduler.schedule(region);
+ // Luminol end
}
/**
@@ -233,13 +241,77 @@ public final class TickRegionScheduler {
region.markNonSchedulable();
}
+ // Luminol start - Level schedulers
+ private void handleParentSchedulerDead() {
+ for (ServerLevel level : MinecraftServer.getServer().getAllLevels()) {
+ level.levelScheduler.onParentHalted();
+ }
+ }
+
+ public boolean haltAndWaitLevelSchedulerHalt(final boolean sync, final long maxWaitNS) {
+ final long deadline = System.nanoTime() + maxWaitNS;
+
+ boolean hasUnhaltedLevelScheduler = false;
+ for (net.minecraft.server.level.ServerLevel serverLevel : MinecraftServer.getServer().getAllLevels()) {
+ serverLevel.levelScheduler.halt(); // ensure it's halted so that we could skip running any other new tasks
+
+ // block any new reading reference acquiring going into this lock
+ serverLevel.levelUnloadStateLock.blockReadingReferencing();
+
+ // sync not required, directly go next
+ if (!sync) {
+ hasUnhaltedLevelScheduler |= !serverLevel.levelScheduler.isFullyExited();
+ continue;
+ }
+
+ final boolean writeLockHeld = serverLevel.levelUnloadStateLock.acquireWrite(deadline);
+ // we got the write lock, abort the others unloading logics
+ if (writeLockHeld) {
+ serverLevel.levelUnloadStateLock.acquireUnreachable();
+
+ // since there is no unloading tasks, so the callback would be always empty, just directly go next,
+ // and we'll handle the final saving in global shutdown logics
+ continue;
+ }
+
+ // cannot get write lock, might be marked as unreachable
+ final boolean isUnreachable = serverLevel.levelUnloadStateLock.isUnreachable();
+ // already fully terminated (we mark it as unreachable after all separate unload saving logics), check the scheduler state
+ if (isUnreachable) {
+ continue;
+ }
+
+ // try linearly block wait for halt
+ long failures = 0L;
+ while (!serverLevel.levelScheduler.isFullyExited()) {
+ // check deadline
+ if (deadline - System.nanoTime() <= 0) {
+ return false;
+ }
+
+ failures = ca.spottedleaf.concurrentutil.util.ConcurrentUtil.linearLongBackoffDeadline(failures, 100, 1000L, deadline);
+ }
+ }
+
+ return !hasUnhaltedLevelScheduler;
+ }
+ // Luminol end - Level schedulers
+
public boolean halt(final boolean sync, final long maxWaitNS) {
- this.scheduler.halt();
+ final boolean allLevelSchedulersExited = this.haltAndWaitLevelSchedulerHalt(sync, maxWaitNS); // Luminol - Level schedulers
+ if (allLevelSchedulersExited) this.scheduler.halt(); // Luminol - Level schedulers
if (!sync) {
return this.scheduler.getAliveThreads().length == 0;
}
- return this.scheduler.join(maxWaitNS == 0L ? 0L : Math.max(1L, TimeUnit.NANOSECONDS.toMillis(maxWaitNS)));
+ // return this.scheduler.join(maxWaitNS == 0L ? 0L : Math.max(1L, TimeUnit.NANOSECONDS.toMillis(maxWaitNS))); // Luminol - Level schedulers
+ // Luminol start - Level schedulers
+ final boolean waitResult = allLevelSchedulersExited && this.scheduler.join(maxWaitNS == 0L ? 0L : Math.max(1L, TimeUnit.NANOSECONDS.toMillis(maxWaitNS)));
+
+ this.handleParentSchedulerDead();
+
+ return waitResult;
+ // Luminol - end
}
void dumpAliveThreadTraces(final String reason) {
@@ -251,7 +323,15 @@ public final class TickRegionScheduler {
}
public void setHasTasks(final RegionScheduleHandle region) {
+ //this.scheduler.notifyTasks(region); // Luminol - Level schedulers
+ // Luminol start - Level schedulers
+ if (region.region != null) {
+ region.region.world.levelScheduler.notifyTasks(region);
+ return;
+ }
+
this.scheduler.notifyTasks(region);
+ // Luminol end
}
private void uncaughtException(final Thread thread, final Throwable thr) {
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 7683c12d48d23dd43bce3d3b5633029c596930a5..ab61d18845827ac835edf92651d0b510358106ff 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -236,7 +236,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
private String localIp;
private int port = -1;
private final LayeredRegistryAccess<RegistryLayer> registries;
- private Map<ResourceKey<Level>, ServerLevel> levels = Maps.newLinkedHashMap();
+ private volatile Map<ResourceKey<Level>, ServerLevel> levels = Maps.newLinkedHashMap(); // Luminol - World hot load/unload apis
private PlayerList playerList;
private volatile boolean running = true;
private volatile boolean isRestarting = false; // Paper - flag to signify we're attempting to restart
@@ -2034,14 +2034,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
// CraftBukkit start
- public void addLevel(ServerLevel level) {
+ public synchronized void addLevel(ServerLevel level) { // Luminol - World hot load/unload apis
Map<ResourceKey<Level>, ServerLevel> oldLevels = this.levels;
Map<ResourceKey<Level>, ServerLevel> newLevels = Maps.newLinkedHashMap(oldLevels);
newLevels.put(level.dimension(), level);
this.levels = Collections.unmodifiableMap(newLevels);
}
- public void removeLevel(ServerLevel level) {
+ public synchronized void removeLevel(ServerLevel level) { // Luminol - World hot load/unload apis
Map<ResourceKey<Level>, ServerLevel> oldLevels = this.levels;
Map<ResourceKey<Level>, ServerLevel> newLevels = Maps.newLinkedHashMap(oldLevels);
newLevels.remove(level.dimension());
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 534f103234eb92571c664cbcfea4ce4e990d3ea8..c4d9d890db784d376b876d70f0468df6edb75168 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -221,6 +221,12 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
private final StructureCheck structureCheck;
public final boolean tickTime; // Folia - region threading
private final LevelDebugSynchronizers debugSynchronizers = new LevelDebugSynchronizers(this);
+ // Luminol start - Level schedulers
+ private static final java.util.concurrent.atomic.AtomicInteger UNLOADER_ID_GEN = new java.util.concurrent.atomic.AtomicInteger(0);
+ public final me.earthme.luminol.utils.thread.LevelAwareRegionScheduler levelScheduler = new me.earthme.luminol.utils.thread.LevelAwareRegionScheduler(io.papermc.paper.threadedregions.TickRegions.getScheduler().scheduler);
+ public final me.earthme.luminol.utils.thread.SimpleReferenceRWLock levelUnloadStateLock = new me.earthme.luminol.utils.thread.SimpleReferenceRWLock();
+ public final me.earthme.luminol.utils.thread.LevelSubRegionShutdownThread levelUnloader = new me.earthme.luminol.utils.thread.LevelSubRegionShutdownThread("Level Unload Thread - " + UNLOADER_ID_GEN.getAndIncrement(), this);
+ // Luminol end
// CraftBukkit start
private final ResourceKey<LevelStem> typeKey;
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index 264de9099b19779f20ad23c41d0967670759ae12..ab6229ee412cf2aa5affc87510230c587765080b 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -1705,7 +1705,30 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
ServerLevel origin = this.level();
ServerPlayer.RespawnConfig respawnConfig = this.getRespawnConfig();
- ServerLevel respawnWorld = this.server.getLevel(ServerPlayer.RespawnConfig.getDimensionOrDefault(respawnConfig));
+ ServerLevel respawnWorldLookup = this.server.getLevel(ServerPlayer.RespawnConfig.getDimensionOrDefault(respawnConfig)); // Luminol - Level hot unload apis
+ // Luminol start - Level hot unload apis
+ var toLockRelease = (ServerLevel)null;
+ if (respawnWorldLookup != null) {
+ if (respawnWorldLookup.levelUnloadStateLock.acquireRead()) {
+ toLockRelease = respawnWorldLookup;
+ } else {
+ respawnWorldLookup = null;
+ }
+ }
+
+ final ServerLevel respawnWorld = respawnWorldLookup;
+ final ServerLevel finalToLockRelease = toLockRelease;
+
+ var finalRespawnCompleteCallback = (java.util.function.Consumer<ServerPlayer>) player -> {
+ if (finalToLockRelease != null) {
+ finalToLockRelease.levelUnloadStateLock.releaseRead();
+ }
+
+ if (respawnComplete != null) {
+ respawnComplete.accept(player);
+ }
+ };
+ // Luminol end
// modified based off PlayerList#respawn
@@ -1756,8 +1779,8 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
// now the respawn logic is complete
// last, call the function callback
- if (respawnComplete != null) {
- respawnComplete.accept(ServerPlayer.this);
+ if (finalRespawnCompleteCallback != null) { // Luminol - Level hot unload apis
+ finalRespawnCompleteCallback.accept(ServerPlayer.this); // Luminol - Level hot unload apis
}
}
);
diff --git a/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java b/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
index 97f4c546663fb19558246366a3d8d77f2b9327ef..e5304e9d1ea57c01a1147621a42a9b584878f765 100644
--- a/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
@@ -210,8 +210,23 @@ public class ServerConfigurationPacketListenerImpl extends ServerCommonPacketLis
this.switchToMain = serverPlayer;
// now the connection responsibility is transferred to the region
- final net.minecraft.server.level.ServerLevel world = this.prepareSpawnTask.getSpawnWorld();
+ net.minecraft.server.level.ServerLevel worldLookup = this.prepareSpawnTask.getSpawnWorld(); // Luminol - Level hot unload apis - world -> worldLookup, make mutable
final net.minecraft.world.level.ChunkPos chunkPos = net.minecraft.world.level.ChunkPos.containing(net.minecraft.core.BlockPos.containing(this.prepareSpawnTask.getSpawnPosition()));
+ // Luminol start - Level hot unload apis
+ if (!worldLookup.levelUnloadStateLock.acquireRead()) {
+ LOGGER.warn("Failed to acquire read reference for level {} for player placement, sending back to overworld!", worldLookup.dimension());
+
+ worldLookup = net.minecraft.server.MinecraftServer.getServer().overworld();
+ if (!worldLookup.levelUnloadStateLock.acquireRead()) {
+ throw new java.lang.IllegalStateException("Reference read for overworld should be always acquirable!");
+ }
+
+ serverPlayer.setServerLevel(worldLookup);
+ }
+ var world = worldLookup;
+ // note: the following logics would always be successfully executed, otherwise the server go crashed
+ // so do not care the exception handling of the callbacks following
+ // Luminol end
world.moonrise$getChunkTaskScheduler().scheduleTickingState(
chunkPos.x(), chunkPos.z(), net.minecraft.server.level.FullChunkStatus.ENTITY_TICKING, true,
ca.spottedleaf.concurrentutil.util.Priority.HIGHER,
@@ -223,9 +238,15 @@ public class ServerConfigurationPacketListenerImpl extends ServerCommonPacketLis
);
io.papermc.paper.threadedregions.RegionizedServer.getInstance().taskQueue.queueTickTaskQueue(
world, chunkPos.x(), chunkPos.z(), () -> {
+ try { // Luminol - Level hot unload apis
net.minecraft.server.network.ServerConfigurationPacketListenerImpl.this.prepareSpawnTask.spawnPlayer(
this.connection, commonListenerCookie, serverPlayer
);
+ // Luminol start - Level hot unload apis
+ } finally {
+ world.levelUnloadStateLock.releaseRead();
+ }
+ // Luminol end
}
);
}
diff --git a/net/minecraft/server/network/config/PrepareSpawnTask.java b/net/minecraft/server/network/config/PrepareSpawnTask.java
index 48f51b0f94df3e59c9410129489c63a4287bc0bc..2682f8702785b1e501748a308c5e394b8b664428 100644
--- a/net/minecraft/server/network/config/PrepareSpawnTask.java
+++ b/net/minecraft/server/network/config/PrepareSpawnTask.java
@@ -104,6 +104,16 @@ public class PrepareSpawnTask implements ConfigurationTask {
spawnLevel = spawnDataLevel;
}
}
+ // Luminol start - Level hot unload apis
+ if (!spawnLevel.levelUnloadStateLock.acquireRead()) {
+ spawnLevel = spawnDataLevel;
+
+ // the apis don't allow devs to unload overworld, so just simply do a check withoud fail-processing logics on it
+ if (!spawnLevel.levelUnloadStateLock.acquireRead()) {
+ throw new java.lang.IllegalStateException("Default level should be acquirable!");
+ }
+ }
+ // Luminol end
// Folia start - region threading
CompletableFuture<Vec3> spawnPosition = new java.util.concurrent.CompletableFuture<>();
if (loadedPosition.position().isPresent()) {
@@ -123,6 +133,10 @@ public class PrepareSpawnTask implements ConfigurationTask {
}
// Folia end - region threading
// Paper end - move logic in Entity to here, to use bukkit supplied world UUID & reset to main world spawn if no valid world is found
+ // Luminol start - Level hot unload apis
+ final ServerLevel finalSpawnLevel = spawnLevel;
+ spawnPosition.whenComplete((_, _) -> finalSpawnLevel.levelUnloadStateLock.releaseRead());
+ // Luminol end
Vec2 spawnAngle = loadedPosition.rotation().orElse(new Vec2(respawnData.yaw(), respawnData.pitch()));
this.state = new PrepareSpawnTask.Preparing(spawnLevel, spawnPosition, spawnAngle);
}
@@ -272,9 +286,20 @@ public class PrepareSpawnTask implements ConfigurationTask {
this.spawnPosition = CompletableFuture.completedFuture(spawnPosition);
this.spawnAngle = new Vec2(location.getYaw(), location.getPitch());
}
+ // Luminol start - Level hot unload apis
+ if (!this.spawnLevel.levelUnloadStateLock.acquireRead()) {
+ LOGGER.warn("Could not fetch read reference lock for level {}! Falling back to overworld!", this.spawnLevel);
+
+ this.spawnLevel = net.minecraft.server.MinecraftServer.getServer().overworld();
+ if (!this.spawnLevel.levelUnloadStateLock.acquireRead()) {
+ throw new java.lang.IllegalStateException();
+ }
+ }
+ // Luminol end
// Paper end - PlayerSpawnLocationEvent
ChunkPos spawnChunk = ChunkPos.containing(BlockPos.containing(spawnPosition));
this.chunkLoadFuture = ((ca.spottedleaf.moonrise.patches.chunk_system.MoonriseChunkLoadCounter)this.chunkLoadCounter).trackLoadWithRadius(this.spawnLevel, spawnChunk, 3, net.minecraft.world.level.chunk.status.ChunkStatus.FULL, ca.spottedleaf.concurrentutil.util.Priority.HIGH, () -> { Preparing.this.spawnLevel.getChunkSource().addTicketWithRadius(TicketType.PLAYER_SPAWN, spawnChunk, 3); }); // Paper - rewrite chunk system
+ this.chunkLoadFuture.whenComplete((_,_) -> this.spawnLevel.levelUnloadStateLock.releaseRead()); // Luminol - Level hot unload apis
PrepareSpawnTask.this.loadListener.start(LevelLoadListener.Stage.LOAD_PLAYER_CHUNKS, this.chunkLoadCounter.totalChunks());
PrepareSpawnTask.this.loadListener.updateFocus(this.spawnLevel.dimension(), spawnChunk);
}
@@ -291,7 +316,7 @@ public class PrepareSpawnTask implements ConfigurationTask {
}
private final class Ready implements PrepareSpawnTask.State {
- private final ServerLevel spawnLevel;
+ private ServerLevel spawnLevel; // Luminol - Level hot unloaad apis - Make public
private final Vec3 spawnPosition;
private final Vec2 spawnAngle;
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index 8c909108a0c468f353837787188c12f907369b9b..1dd0ce3f8af52f5ba0da1d6617489a0de2c971b2 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -392,6 +392,9 @@ public abstract class Entity
public void inactiveTick() {
}
// Paper end - EAR 2
+ // Luminol start - Level hot unload apis
+ public me.earthme.luminol.utils.TeleportRecord lastTeleportRecord;
+ // Luminol end
// CraftBukkit end
// Paper start
@@ -4538,6 +4541,17 @@ public abstract class Entity
}
}
+ // Luminol start - Level hot unload apis
+ if (!destination.levelUnloadStateLock.acquireRead()) {
+ return false;
+ }
+ final java.util.function.Consumer<Entity> finalTeleportComplete = entity -> {
+ destination.levelUnloadStateLock.releaseRead();
+
+ if (teleportComplete != null) teleportComplete.accept(entity);
+ };
+ // Luminol end
+
// TODO any events that can modify go HERE
// check for same region
@@ -4556,6 +4570,14 @@ public abstract class Entity
}
for (EntityTreeNode entity : passengerTree.getFullTree()) {
+ // Luminol start - Level hot unload apis
+ entity.root.lastTeleportRecord = me.earthme.luminol.utils.TeleportRecord.create(
+ (ServerLevel) entity.root.level,
+ entity.root.position,
+ entity.root.yRot,
+ entity.root.xRot
+ );
+ // Luminol end
entity.root.teleportSyncSameRegion(pos, yaw, pitch, velocity);
}
@@ -4571,8 +4593,8 @@ public abstract class Entity
// performs add/remove from world logic which will also perform add/remove tracker logic
}
- if (teleportComplete != null) {
- teleportComplete.accept(this);
+ if (finalTeleportComplete != null) { // Luminol - Level hot unload apis
+ finalTeleportComplete.accept(this); // Luminol - Level hot unload apis
}
return true;
}
@@ -4589,7 +4611,7 @@ public abstract class Entity
node.root = node.root.transformForAsyncTeleport(destination, pos, yaw, pitch, velocity);
}
- passengerTree.root.placeInAsync(originWorld, destination, teleportFlags, passengerTree, teleportComplete);
+ passengerTree.root.placeInAsync(originWorld, destination, teleportFlags, passengerTree, finalTeleportComplete); // Luminol - Level hot unload apis
return true;
}
@@ -4864,6 +4886,13 @@ public abstract class Entity
ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkX(initialPosition),
ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkZ(initialPosition)
);
+ // Luminol start - Level hot load/unload apis
+ // let's acquire read reference first
+ if (!destination.levelUnloadStateLock.acquireRead()) {
+ // failed, destination is under unloading process
+ return false;
+ }
+ // Luminol end
// first, remove entity/passengers from world
EntityTreeNode passengerTree = this.detachPassengers();
@@ -4921,6 +4950,10 @@ public abstract class Entity
if (info.postTeleportTransition() != null) {
info.postTeleportTransition().onTransition(teleported);
}
+ // Luminol start - Level hot load/unload apis
+ // all done, release the read reference
+ destination.levelUnloadStateLock.releaseRead();
+ // Luminol end
if (teleportComplete != null) {
teleportComplete.accept(teleported);
@@ -0,0 +1,23 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Bacteriawa <A3167717663@hotmail.com>
Date: Tue, 21 Apr 2026 01:32:38 +0800
Subject: [PATCH] Kaiiju: Do not pathfind outside region
Co-authored by: Sofiane H. Djerbi <46628754+kugge@users.noreply.github.com>
As part of: Kaiiju (https://github.com/KaiijuMC/Kaiiju/blob/c2b7aec8f7b418a39a2ec408e6411e6f752379da/patches/server/0028-Don-t-pathfind-outside-region.patch)
Licensed under: GPL-3.0 (https://github.com/KaiijuMC/Kaiiju/blob/c2b7aec8f7b418a39a2ec408e6411e6f752379da/LICENSE)
diff --git a/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java b/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java
index b40da004b5281a041ee896ae176bfb9c4660f353..ffb8a725c41d49a0f0e023ab37bb77062d67eccc 100644
--- a/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java
+++ b/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java
@@ -117,7 +117,9 @@ public class MoveToTargetSink extends Behavior<Mob> {
private boolean tryComputePath(final Mob body, final WalkTarget walkTarget, final long timestamp) {
BlockPos targetPos = walkTarget.getTarget().currentBlockPosition();
+ if (ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(body.level(), targetPos)) // Kaiiju - Don't pathfind outside region
this.path = body.getNavigation().createPath(targetPos, 0);
+ else this.path = null; // Kaiiju - Don't pathfind outside region
this.speedModifier = walkTarget.getSpeedModifier();
Brain<?> brain = body.getBrain();
if (this.reachedTarget(body, walkTarget)) {
@@ -0,0 +1,55 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Bacteriawa <A3167717663@hotmail.com>
Date: Tue, 21 Apr 2026 01:41:14 +0800
Subject: [PATCH] Kaiiju: Entity tick and removal limiter
Co-authored by: Xymb <xymb@endcrystal.me>
As part of: Kaiiju (https://github.com/KaiijuMC/Kaiiju/blob/c2b7aec8f7b418a39a2ec408e6411e6f752379da/patches/server/0021-Entity-ticking-throttling-removal-to-prevent-lag.patch)
Licensed under: GPL-3.0 (https://github.com/KaiijuMC/Kaiiju/blob/c2b7aec8f7b418a39a2ec408e6411e6f752379da/LICENSE)
diff --git a/io/papermc/paper/threadedregions/RegionizedWorldData.java b/io/papermc/paper/threadedregions/RegionizedWorldData.java
index cb15cfbc684ae9e9d9634886f707c2df10063139..f32d118e1e925b2155cef3fcdcb1e194c541e242 100644
--- a/io/papermc/paper/threadedregions/RegionizedWorldData.java
+++ b/io/papermc/paper/threadedregions/RegionizedWorldData.java
@@ -354,6 +354,7 @@ public final class RegionizedWorldData {
private final IteratorSafeOrderedReferenceSet<Mob> navigatingMobs = new IteratorSafeOrderedReferenceSet<>();
public final ReferenceList<Entity> trackerEntities = new ReferenceList<>(EMPTY_ENTITY_ARRAY); // Moonrise - entity tracker
public final ReferenceList<Entity> trackerUnloadedEntities = new ReferenceList<>(EMPTY_ENTITY_ARRAY); // Moonrise - entity tracker
+ public final dev.kaiijumc.kaiiju.KaiijuEntityThrottler entityThrottler = new dev.kaiijumc.kaiiju.KaiijuEntityThrottler(); // Kaiiju
// block ticking
private final ObjectLinkedOpenHashSet<BlockEventData> blockEvents = new ObjectLinkedOpenHashSet<>();
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index c4d9d890db784d376b876d70f0468df6edb75168..0641b98ef3298eb682ceae83f60b730f3bf90103 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -917,6 +917,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
}
foliaProfiler.startTimer(ca.spottedleaf.leafprofiler.LProfilerRegistry.ACTIVATE_ENTITIES); try { // Folia - profiler
+ if (dev.kaiijumc.kaiiju.KaiijuEntityLimits.enabled) regionizedWorldData.entityThrottler.tickLimiterStart(); // Kaiiju
io.papermc.paper.entity.activation.ActivationRange.activateEntities(this); // Paper - EAR
} finally { foliaProfiler.stopTimer(ca.spottedleaf.leafprofiler.LProfilerRegistry.ACTIVATE_ENTITIES); } // Folia - profiler
foliaProfiler.startTimer(ca.spottedleaf.leafprofiler.LProfilerRegistry.ENTITY_TICK); try { // Folia - profiler
@@ -938,6 +939,13 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
entity.stopRiding();
}
+ // Kaiiju start
+ if (dev.kaiijumc.kaiiju.KaiijuEntityLimits.enabled) {
+ dev.kaiijumc.kaiiju.KaiijuEntityThrottler.EntityThrottlerReturn throttle = regionizedWorldData.entityThrottler.tickLimiterShouldSkip(entity);
+ if (throttle.remove && !entity.hasCustomName()) entity.remove(Entity.RemovalReason.DISCARDED);
+ if (throttle.skip) return;
+ }
+ // Kaiiju end
profiler.push("tick");
this.guardEntityTick(this::tickNonPassenger, entity);
@@ -947,6 +955,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
}
}
);
+ if (dev.kaiijumc.kaiiju.KaiijuEntityLimits.enabled) regionizedWorldData.entityThrottler.tickLimiterFinish(regionizedWorldData); // Kaiiju
} finally { foliaProfiler.stopTimer(ca.spottedleaf.leafprofiler.LProfilerRegistry.ENTITY_TICK); } // Folia - profiler
if (this.paperConfig().unsupportedSettings.ticking.blockEntities) { // Paper - option to disable ticking
profiler.popPush("blockEntities");
@@ -0,0 +1,81 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Sat, 18 Apr 2026 09:21:31 +0800
Subject: [PATCH] Kaiiju: Vanilla end portal teleportation
Co-authored by: Sofiane H. Djerbi <46628754+kugge@users.noreply.github.com>
As part of: Kaiiju (https://github.com/KaiijuMC/Kaiiju/blob/c2b7aec8f7b418a39a2ec408e6411e6f752379da/patches/server/0024-Vanilla-end-portal-teleportation.patch)
Licensed under: GPL-3.0 (https://github.com/KaiijuMC/Kaiiju/blob/c2b7aec8f7b418a39a2ec408e6411e6f752379da/LICENSE)
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index 1dd0ce3f8af52f5ba0da1d6617489a0de2c971b2..cf70a8e2fe2fe70bcdf71f8112479031d80d8b75 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -4676,14 +4676,18 @@ public abstract class Entity
targetPos, 16, // load 16 blocks to be safe from block physics
ca.spottedleaf.concurrentutil.util.Priority.HIGH,
(chunks) -> {
- net.minecraft.world.level.levelgen.feature.EndPlatformFeature.createEndPlatform(destination, targetPos.below(), true, null);
-
+ //net.minecraft.world.level.levelgen.feature.EndPlatformFeature.createEndPlatform(destination, targetPos.below(), true, null); // Kaiiju - Vanilla end teleportation - moved down
+ // Kaiiju start - Vanilla end teleportation
+ Vec3 finalPos;
+ if (this instanceof Player) finalPos = Vec3.atBottomCenterOf(targetPos.below());
+ else finalPos = Vec3.atBottomCenterOf(targetPos);
+ // Kaiiju end
// the portal obsidian is placed at targetPos.y - 2, so if we want to place the entity
// on the obsidian, we need to spawn at targetPos.y - 1
portalInfoCompletable.complete(
new net.minecraft.world.level.portal.TeleportTransition(
- destination, Vec3.atBottomCenterOf(targetPos.below()), Vec3.ZERO, Direction.WEST.toYRot(), 0.0f,
- Relative.union(Relative.DELTA, Set.of(Relative.X_ROT)),
+ destination, finalPos, this.getDeltaMovement(), Direction.WEST.toYRot(), 0.0f, // Kaiiju - Vanilla end teleportation
+ /*Relative.union(Relative.DELTA, Set.of(Relative.X_ROT))*/Set.of(), // Kaiiju - Vanilla end teleportation
TeleportTransition.PLAY_PORTAL_SOUND.then(TeleportTransition.PLACE_PORTAL_TICKET),
org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.END_PORTAL
)
@@ -4698,11 +4702,15 @@ public abstract class Entity
ca.spottedleaf.concurrentutil.util.Priority.HIGH,
(chunks) -> {
BlockPos adjustedSpawn = destination.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, spawnPos);
-
+ // Kaiiju start - Vanilla end teleportation
+ Vec3 finalPos;
+ if (this instanceof Player) finalPos = Vec3.atBottomCenterOf(adjustedSpawn.below());
+ else finalPos = Vec3.atBottomCenterOf(adjustedSpawn);
+ // Kaiiju end
// done
portalInfoCompletable.complete(
new net.minecraft.world.level.portal.TeleportTransition(
- destination, Vec3.atBottomCenterOf(adjustedSpawn), Vec3.ZERO, 0.0f, 0.0f,
+ destination, finalPos, this.getDeltaMovement(), 0.0f, 0.0f, // Kaiiju - Vanilla end teleportation
Relative.union(Relative.DELTA, Relative.ROTATION),
TeleportTransition.PLAY_PORTAL_SOUND.then(TeleportTransition.PLACE_PORTAL_TICKET),
org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.END_PORTAL
@@ -4881,6 +4889,10 @@ public abstract class Entity
return false;
}
+ // Kaiiju start - sync end platform spawning & entity teleportation
+ final java.util.function.Consumer<Entity> tpComplete = type == PortalType.END && destination.getTypeKey() == net.minecraft.world.level.dimension.LevelStem.END ?
+ e -> {net.minecraft.world.level.levelgen.feature.EndPlatformFeature.createEndPlatform(destination, ServerLevel.END_SPAWN_POINT.below(), true, null); if (teleportComplete != null) {teleportComplete.accept(e);}} : teleportComplete;
+ // Kaiiju end
Vec3 initialPosition = this.position();
ChunkPos initialPositionChunk = new ChunkPos(
ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkX(initialPosition),
@@ -4955,9 +4967,14 @@ public abstract class Entity
destination.levelUnloadStateLock.releaseRead();
// Luminol end
- if (teleportComplete != null) {
+ // Kaiiju start - vanilla end teleportation
+ /*if (teleportComplete != null) {
teleportComplete.accept(teleported);
+ }*/
+ if (tpComplete != null){
+ tpComplete.accept(teleported);
}
+ // Kaiiju end
}
);
});
@@ -0,0 +1,288 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Sun, 28 Jun 2026 19:58:46 +0800
Subject: [PATCH] Async protocol switching optimization
diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java
index 40f54536422fa38bd84017fc634808016b4de58b..ecaa296f576c5c90fda89e0406d6552628ef5de3 100644
--- a/net/minecraft/network/Connection.java
+++ b/net/minecraft/network/Connection.java
@@ -1067,4 +1067,127 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
}
}
// Paper end - Optimize network
+ // Luminol start - async protocol switcher
+ public <T extends PacketListener> void setupInboundProtocolAsync(
+ ProtocolInfo<T> protocol,
+ T packetListener,
+ @Nullable Runnable callback,
+ boolean resumeAutoReading
+ ) {
+ this.validateListener(protocol, packetListener);
+ if (protocol.flow() != this.getReceiving()) {
+ throw new IllegalStateException("Invalid inbound protocol: " + protocol.id());
+ } else {
+ this.packetListener = packetListener;
+ this.disconnectListener = null;
+
+ UnconfiguredPipelineHandler.InboundConfigurationTask configMessage = UnconfiguredPipelineHandler.setupInboundProtocol(protocol);
+ BundlerInfo bundlerInfo = protocol.bundlerInfo();
+ if (bundlerInfo != null) {
+ PacketBundlePacker newBundler = new PacketBundlePacker(bundlerInfo);
+ configMessage = configMessage.andThen(context -> context.pipeline().addAfter("decoder", "bundler", newBundler));
+ }
+
+ // Here we could execute it in event loop async to prevent waiting io task on main
+ // stop reading new packets to prevent some packets came into pipeline too early
+ this.channel.config().setAutoRead(false);
+
+ // do our configuration task
+ final UnconfiguredPipelineHandler.InboundConfigurationTask finalInboundConfigurationTask = configMessage;
+ Runnable toExecute = () -> this.channel.writeAndFlush(finalInboundConfigurationTask).addListener(future -> {
+ try {
+ if (future.isSuccess()) {
+ if (callback != null) callback.run(); // retire callback if there have one
+ return;
+ }
+
+ final Throwable ex = future.cause();
+
+ // here we process our exceptions like that blocking one
+ if (ex instanceof ClosedChannelException) {
+ LOGGER.info("Connection closed during protocol change");
+ } else {
+ this.channel.pipeline().fireExceptionCaught(ex);
+ }
+ }finally {
+ // reset auto back and resume reading if needed
+ if (resumeAutoReading) {
+ this.channel.config().setAutoRead(true);
+ this.channel.read();
+ }
+ }
+ });
+
+ // we need to do this inside the event loop
+ if (!this.channel.eventLoop().inEventLoop()) {
+ this.channel.eventLoop().execute(toExecute);
+ return;
+ }
+
+ toExecute.run();
+ }
+ }
+
+ public void setupOutboundProtocolAsync(
+ ProtocolInfo<?> protocol,
+ @Nullable Runnable callback,
+ boolean resumeAutoReading
+ ) {
+ if (protocol.flow() != this.getSending()) {
+ throw new IllegalStateException("Invalid outbound protocol: " + protocol.id());
+ } else {
+ UnconfiguredPipelineHandler.OutboundConfigurationTask configMessage = UnconfiguredPipelineHandler.setupOutboundProtocol(protocol);
+ BundlerInfo bundlerInfo = protocol.bundlerInfo();
+ if (bundlerInfo != null) {
+ PacketBundleUnpacker newUnbundler = new PacketBundleUnpacker(bundlerInfo);
+ configMessage = configMessage.andThen(
+ context -> context.pipeline().addAfter("encoder", "unbundler", newUnbundler)
+ );
+ }
+
+ boolean isLoginProtocol = protocol.id() == ConnectionProtocol.LOGIN;
+
+ // Here we could execute it in event loop async to prevent waiting io task on main
+ // stop reading new packets to prevent some packets came into pipeline too early
+ this.channel.config().setAutoRead(false);
+
+ // do our configuration task
+ final UnconfiguredPipelineHandler.OutboundConfigurationTask finalOutboundConfigurationTask = configMessage;
+ final Runnable writeTask = () -> this.channel.writeAndFlush(
+ finalOutboundConfigurationTask.andThen(context -> this.sendLoginDisconnect = isLoginProtocol)
+ ).addListener(future -> {
+ try {
+ if (future.isSuccess()) {
+ if (callback != null) callback.run(); // retire callback if there have one
+ return;
+ }
+
+ final Throwable ex = future.cause();
+
+ // here we process our exceptions like that blocking one
+ if (ex instanceof ClosedChannelException) {
+ LOGGER.info("Connection closed during protocol change");
+ } else {
+ this.channel.pipeline().fireExceptionCaught(ex);
+ }
+ }finally {
+ // reset auto back and resume reading if needed
+ if (resumeAutoReading) {
+ this.channel.config().setAutoRead(true);
+ this.channel.read(); // read once
+ }
+ }
+ });
+
+ // we need to do this inside the event loop
+ if (!this.channel.eventLoop().inEventLoop()) {
+ this.channel.eventLoop().execute(writeTask);
+ return;
+ }
+
+ writeTask.run();
+ }
+ }
+ // Luminol end
+
}
diff --git a/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java b/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
index e5304e9d1ea57c01a1147621a42a9b584878f765..af337f77545df94760b12a2b2f5eaf43d18e3774 100644
--- a/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
@@ -188,8 +188,9 @@ public class ServerConfigurationPacketListenerImpl extends ServerCommonPacketLis
public void handleConfigurationFinished(final ServerboundFinishConfigurationPacket packet) {
PacketUtils.ensureRunningOnSameThread(packet, this, this.server.packetProcessor());
this.finishCurrentTask(JoinWorldTask.TYPE);
- this.connection.setupOutboundProtocol(GameProtocols.CLIENTBOUND_TEMPLATE.bind(RegistryFriendlyByteBuf.decorator(this.server.registryAccess())));
+ // this.connection.setupOutboundProtocol(GameProtocols.CLIENTBOUND_TEMPLATE.bind(RegistryFriendlyByteBuf.decorator(this.server.registryAccess()))); // Luminol - Async protocol switch - move down
+ Runnable afterSwitch = () -> { // Luminol - Async protocol switch
try {
PlayerList playerList = this.server.getPlayerList();
if (playerList.getPlayer(this.gameProfile.id()) != null) {
@@ -256,6 +257,18 @@ public class ServerConfigurationPacketListenerImpl extends ServerCommonPacketLis
LOGGER.error("Couldn't place player in world", e);
this.disconnect(DISCONNECT_REASON_INVALID_DATA);
}
+ // Luminol start - Async protocol switch
+ };
+ if (!me.earthme.luminol.config.modules.optimizations.AsyncProtocolChangeConfig.enabled) {
+ this.connection.setupOutboundProtocol(GameProtocols.CLIENTBOUND_TEMPLATE.bind(RegistryFriendlyByteBuf.decorator(this.server.registryAccess())));
+ afterSwitch.run(); // directly run callback as we won't process any packet this time
+ } else {
+ this.connection.setupOutboundProtocolAsync(GameProtocols.CLIENTBOUND_TEMPLATE.bind(RegistryFriendlyByteBuf.decorator(this.server.registryAccess())), () -> {
+ // push back
+ io.papermc.paper.threadedregions.RegionizedServer.getInstance().addTask(afterSwitch);
+ }, false); // we will start auto read once we set up inbound handler at placeNewPlayer in PlayerList
+ }
+ // Luminol end
}
@Override
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 0c5fb17e7cce18eac68183cc3b5de6fc6451dc50..1ea8e03a4dc39486505d21bff1d15cb2339cafc2 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -2901,7 +2901,13 @@ public class ServerGamePacketListenerImpl
} // Folia end - rewrite login process - move connection ownership to global region
this.waitingForSwitchToConfig = true; // Folia - rewrite login process - fix bad ordering of this field write - moved down
this.send(ClientboundStartConfigurationPacket.INSTANCE);
+ if (!me.earthme.luminol.config.modules.optimizations.AsyncProtocolChangeConfig.enabled) { // Luminol - Async protocol switch
this.connection.setupOutboundProtocol(ConfigurationProtocols.CLIENTBOUND);
+ // Luminol start - Async protcol switch
+ } else {
+ this.connection.setupOutboundProtocolAsync(ConfigurationProtocols.CLIENTBOUND, null, true);
+ }
+ // Luminol end
}
@Override
@@ -3786,12 +3792,26 @@ public class ServerGamePacketListenerImpl
}
final ServerConfigurationPacketListenerImpl listener = new ServerConfigurationPacketListenerImpl(this.server, this.connection, this.createCookie(this.player.clientInformation())); // Paper
+ if (!me.earthme.luminol.config.modules.optimizations.AsyncProtocolChangeConfig.enabled) { // Luminol - Async protocol switch
this.connection
.setupInboundProtocol(
ConfigurationProtocols.SERVERBOUND,
listener // Paper
);
new io.papermc.paper.event.connection.configuration.PlayerConnectionReconfigureEvent(listener.paperConnection).callEvent(); // Paper
+ } // Luminol - Async protocol switch - add "{"
+ // Luminol start - Async protocol switch - move up
+ else
+ this.connection.setupInboundProtocolAsync(
+ ConfigurationProtocols.SERVERBOUND,
+ listener,
+ () -> {
+ new io.papermc.paper.event.connection.configuration.PlayerConnectionReconfigureEvent(listener.paperConnection).callEvent(); // Paper
+ },
+ true
+ );
+ // Luminol end
+ // new io.papermc.paper.event.connection.configuration.PlayerConnectionReconfigureEvent(listener.paperConnection).callEvent(); // Paper // Luminol - Async protocol switch - move up
}
@Override
diff --git a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
index f2329d1a9d9d4bf4c9d771e25e54f2f9ef65a76c..deee051c103f9797f4612e06c12f293ec54724ac 100644
--- a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
@@ -435,12 +435,30 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener,
public void handleLoginAcknowledgement(final ServerboundLoginAcknowledgedPacket packet) {
net.minecraft.network.protocol.PacketUtils.ensureRunningOnSameThread(packet, this, this.server.packetProcessor()); // CraftBukkit
Validate.validState(this.state == ServerLoginPacketListenerImpl.State.PROTOCOL_SWITCHING, "Unexpected login acknowledgement packet");
- this.connection.setupOutboundProtocol(ConfigurationProtocols.CLIENTBOUND);
+ /*this.connection.setupOutboundProtocol(ConfigurationProtocols.CLIENTBOUND); // Luminol - Async protocol switch - Rewrite
CommonListenerCookie cookie = CommonListenerCookie.createInitial(Objects.requireNonNull(this.authenticatedProfile), this.transferred);
ServerConfigurationPacketListenerImpl configPacketListener = new ServerConfigurationPacketListenerImpl(this.server, this.connection, cookie);
this.connection.setupInboundProtocol(ConfigurationProtocols.SERVERBOUND, configPacketListener);
configPacketListener.startConfiguration();
- this.state = ServerLoginPacketListenerImpl.State.ACCEPTED;
+ this.state = ServerLoginPacketListenerImpl.State.ACCEPTED;*/ // Luminol - Async protocol switch - Rewrite
+
+ // Luminol start - Async protocol switch
+ CommonListenerCookie cookie = CommonListenerCookie.createInitial(Objects.requireNonNull(this.authenticatedProfile), this.transferred);
+ ServerConfigurationPacketListenerImpl configPacketListener = new ServerConfigurationPacketListenerImpl(this.server, this.connection, cookie);
+
+ Runnable afterSwitch = () -> io.papermc.paper.threadedregions.RegionizedServer.getInstance().addTask(configPacketListener::startConfiguration); // push back to main thread
+
+ if (!me.earthme.luminol.config.modules.optimizations.AsyncProtocolChangeConfig.enabled) {
+ this.connection.setupOutboundProtocol(ConfigurationProtocols.CLIENTBOUND);
+ this.connection.setupInboundProtocol(ConfigurationProtocols.SERVERBOUND, configPacketListener);
+ afterSwitch.run();
+ return;
+ }
+
+ this.connection.setupInboundProtocolAsync(ConfigurationProtocols.SERVERBOUND, configPacketListener, () -> {
+ this.connection.setupOutboundProtocolAsync(ConfigurationProtocols.CLIENTBOUND, afterSwitch, true); // start auto read when everything is ready
+ }, false); // we will resume auto reading once the outbound protocol is also setup
+ // Luminol end
}
@Override
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
index e7579a9873362317dd82b63306cf650af9472574..53449af103c1774f68cb0c296d47e7032e4e3843 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
@@ -237,9 +237,11 @@ public abstract class PlayerList {
// only after setting the connection listener to game type, add the connection to this regions list
level.getCurrentWorldData().connections.add(connection);
// Folia end - rewrite login process
+ if (!me.earthme.luminol.config.modules.optimizations.AsyncProtocolChangeConfig.enabled) { // Luminol - Async protocol switch // we will run async switch once these main thread logics became done
connection.setupInboundProtocol(
GameProtocols.SERVERBOUND_TEMPLATE.bind(RegistryFriendlyByteBuf.decorator(this.server.registryAccess()), playerConnection), playerConnection
);
+ } // Luminol - Async protocol switch
playerConnection.suspendFlushing();
GameRules gameRules = level.getGameRules();
boolean immediateRespawn = gameRules.get(GameRules.IMMEDIATE_RESPAWN);
@@ -403,6 +405,17 @@ public abstract class PlayerList {
);
}
// Paper end - Send empty chunk
+ // Luminol start - Async protocol switch
+ if (me.earthme.luminol.config.modules.optimizations.AsyncProtocolChangeConfig.enabled) {
+ // auto read will be enabled once the async switch is done
+ connection.setupInboundProtocolAsync(
+ GameProtocols.SERVERBOUND_TEMPLATE.bind(RegistryFriendlyByteBuf.decorator(this.server.registryAccess()), playerConnection),
+ playerConnection,
+ null, // we don't need to do anything more
+ true // start auto read which we have disabled in configuration handler
+ );
+ }
+ // Luminol end
}
public void updateEntireScoreboard(final ServerScoreboard scoreboard, final ServerPlayer player) {
@@ -0,0 +1,19 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Sat, 18 Apr 2026 09:08:52 +0800
Subject: [PATCH] Add config for server mod name
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index ab61d18845827ac835edf92651d0b510358106ff..d4e31bb829451a687bf567fd0357f46fd7d46902 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -2072,7 +2072,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
public String getServerModName() {
- return io.papermc.paper.ServerBuildInfo.buildInfo().brandName(); // Paper
+ return me.earthme.luminol.config.modules.misc.ServerModNameConfig.fakeVanilla ? "vanilla" : me.earthme.luminol.config.modules.misc.ServerModNameConfig.serverModName; // Paper // Luminol - Add config for server mod name
}
public ServerClockManager clockManager() {
@@ -0,0 +1,19 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Sat, 18 Apr 2026 09:09:33 +0800
Subject: [PATCH] Add config for offline mode warning
diff --git a/net/minecraft/server/dedicated/DedicatedServer.java b/net/minecraft/server/dedicated/DedicatedServer.java
index 1145d5839e621a02b7972c2ff1142f51db2237de..81934fb56744dbbb3fdd100e8ec83cb634034d04 100644
--- a/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/net/minecraft/server/dedicated/DedicatedServer.java
@@ -304,7 +304,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
String proxyFlavor = (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.velocity.enabled) ? "Velocity" : "BungeeCord";
String proxyLink = (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.velocity.enabled) ? "https://docs.papermc.io/velocity/security" : "http://www.spigotmc.org/wiki/firewall-guide/";
// Paper end - Add Velocity IP Forwarding Support
- if (!this.usesAuthentication()) {
+ if (!this.usesAuthentication() && !me.earthme.luminol.config.modules.misc.DisableWarningConfig.disableOfflineModeWarning) { //Luminol - Add config for offline mod warning
LOGGER.warn("**** SERVER IS RUNNING IN OFFLINE/INSECURE MODE!");
LOGGER.warn("The server will make no attempt to authenticate usernames. Beware.");
// Spigot start
@@ -0,0 +1,19 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Fri, 17 Apr 2026 22:47:34 +0800
Subject: [PATCH] Add config for cpu affinity
diff --git a/io/papermc/paper/threadedregions/TickRegionScheduler.java b/io/papermc/paper/threadedregions/TickRegionScheduler.java
index f8344f640f119e3865f2718d1abd0ede9bdb8aa8..58e557923dfe6cee39ec45e7f5aa43a5ded9f107 100644
--- a/io/papermc/paper/threadedregions/TickRegionScheduler.java
+++ b/io/papermc/paper/threadedregions/TickRegionScheduler.java
@@ -66,7 +66,7 @@ public final class TickRegionScheduler {
@Override
public Thread newThread(final Runnable run) {
- final Thread ret = new TickThreadRunner(this.threadGroup, run, "Folia Region Scheduler Thread #" + this.idGenerator.getAndIncrement());
+ final Thread ret = new TickThreadRunner(this.threadGroup, me.earthme.luminol.config.modules.optimizations.CpuAffinityConfig.wrapForTickRegion(run), "Folia Region Scheduler Thread #" + this.idGenerator.getAndIncrement()); // Luminol - cpu affinity
ret.setUncaughtExceptionHandler(TickRegionScheduler.this::uncaughtException);
return ret;
}
@@ -0,0 +1,45 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Bacteriawa <A3167717663@hotmail.com>
Date: Tue, 21 Apr 2026 01:47:51 +0800
Subject: [PATCH] Add config for unsafe teleportation
diff --git a/net/minecraft/world/entity/item/FallingBlockEntity.java b/net/minecraft/world/entity/item/FallingBlockEntity.java
index 480b6b2405706fd4d7158d0e160adafd84099887..137389f7ee97fc3a15c8f5f87c058065af91264e 100644
--- a/net/minecraft/world/entity/item/FallingBlockEntity.java
+++ b/net/minecraft/world/entity/item/FallingBlockEntity.java
@@ -70,7 +70,7 @@ public class FallingBlockEntity extends Entity {
public int fallDamageMax = 40;
public float fallDamagePerDistance = 0.0F;
public @Nullable CompoundTag blockData;
- public boolean forceTickAfterTeleportToDuplicate;
+ public boolean forceTickAfterTeleportToDuplicate = me.earthme.luminol.config.modules.fixes.UnsafeTeleportationConfig.enabled; // Luminol - Unsafe teleportation
protected static final EntityDataAccessor<BlockPos> DATA_START_POS = SynchedEntityData.defineId(FallingBlockEntity.class, EntityDataSerializers.BLOCK_POS);
public boolean autoExpire = true; // Paper - Expand FallingBlock API
@@ -384,7 +384,7 @@ public class FallingBlockEntity extends Entity {
ResourceKey<Level> oldDimension = this.level().dimension();
boolean fromOrToEnd = (oldDimension == Level.END || newDimension == Level.END) && oldDimension != newDimension;
Entity newEntity = super.teleport(transition);
- this.forceTickAfterTeleportToDuplicate = newEntity != null && fromOrToEnd && io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowUnsafeEndPortalTeleportation; // Paper
+ this.forceTickAfterTeleportToDuplicate = newEntity != null && fromOrToEnd && (io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowUnsafeEndPortalTeleportation || me.earthme.luminol.config.modules.fixes.UnsafeTeleportationConfig.enabled); // Paper // Luminol - Unsafe teleportation
return newEntity;
}
}
diff --git a/net/minecraft/world/level/block/EndPortalBlock.java b/net/minecraft/world/level/block/EndPortalBlock.java
index d42bb2a1721e4ab8c9956e18c3ca418b8d648c59..4f06daf619596d4116140b0710ea92c2c8552fc0 100644
--- a/net/minecraft/world/level/block/EndPortalBlock.java
+++ b/net/minecraft/world/level/block/EndPortalBlock.java
@@ -76,6 +76,12 @@ public class EndPortalBlock extends BaseEntityBlock implements Portal {
if (level.paperConfig().misc.disableEndCredits) {player.seenCredits = true; return;} // Paper - Option to disable end credits
player.showEndCredits();
} else {
+ // Luminol start - unsafe teleportation
+ if (me.earthme.luminol.config.modules.fixes.UnsafeTeleportationConfig.enabled && !(entity instanceof net.minecraft.world.entity.player.Player)) {
+ entity.endPortalLogicAsync(pos);
+ return;
+ }
+ // Luminol end
entity.setAsInsidePortal(this, pos);
}
}
@@ -0,0 +1,19 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Sun, 28 Jun 2026 20:04:06 +0800
Subject: [PATCH] Add config for vanilla random
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index cf70a8e2fe2fe70bcdf71f8112479031d80d8b75..17650e196f1e5b79765651bf302291873a4eb509 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -298,7 +298,7 @@ public abstract class Entity
public double yOld;
public double zOld;
public boolean noPhysics;
- protected final RandomSource random = SHARED_RANDOM; // Paper - Share random for entities to make them more random
+ protected final RandomSource random = me.earthme.luminol.config.modules.fixes.VanillaRandomSourceConfig.useLegacyRandomSourceForPlayers ? RandomSource.create() : SHARED_RANDOM; // Paper - Share random for entities to make them more random // Luminol - Add config for vanilla random SHARED_RANDOM
public int tickCount;
private int remainingFireTicks;
private final EntityFluidInteraction fluidInteraction = new EntityFluidInteraction(Set.of(FluidTags.WATER, FluidTags.LAVA));
@@ -0,0 +1,19 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Sat, 18 Apr 2026 09:12:26 +0800
Subject: [PATCH] Add config for watchdog timeout
diff --git a/io/papermc/paper/threadedregions/FoliaWatchdogThread.java b/io/papermc/paper/threadedregions/FoliaWatchdogThread.java
index e9ca1a15049b0211d10401cb78e953b93afaf6c7..fe1560579f9c9572082d52cf910f5916957f5601 100644
--- a/io/papermc/paper/threadedregions/FoliaWatchdogThread.java
+++ b/io/papermc/paper/threadedregions/FoliaWatchdogThread.java
@@ -65,7 +65,7 @@ public final class FoliaWatchdogThread extends Thread {
for (final RunningTick tick : ticks) {
final long elapsed = now - tick.lastPrint;
- if (elapsed <= TimeUnit.SECONDS.toNanos(5L)) {
+ if (elapsed <= TimeUnit.MILLISECONDS.toNanos(me.earthme.luminol.config.modules.misc.FoliaWatchogConfig.tickRegionTimeOutMs)) { // Luminol - Add config for watchdog timeout
continue;
}
tick.lastPrint = now;
@@ -0,0 +1,18 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Sun, 28 Jun 2026 20:10:51 +0800
Subject: [PATCH] Add config to disable entity tick catchers
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
index dc28223684091cd0d8dbf7ff01626b20e4f1b662..6fd7bfa561f71cfd043d7a1092d77b01bb320640 100644
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
@@ -1591,6 +1591,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
), null, 1L);
// Luminol end
} catch (Throwable t) {
+ if (me.earthme.luminol.config.modules.experiment.DisableEntityCatchConfig.enabled) throw t; // Luminol
// Paper start - Prevent block entity and entity crashes
final String msg = String.format("Entity threw exception at %s:%s,%s,%s", io.papermc.paper.util.MCUtil.getLevelName(entity.level()), entity.getX(), entity.getY(), entity.getZ());
MinecraftServer.LOGGER.error(msg, t);
@@ -0,0 +1,21 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Sat, 18 Apr 2026 09:14:31 +0800
Subject: [PATCH] Add config for heightmap warning
diff --git a/net/minecraft/world/level/levelgen/Heightmap.java b/net/minecraft/world/level/levelgen/Heightmap.java
index dffa0d4859ecd7e21a9345f946083c56205145a3..21f0214992b82554c9532797cdc6fc593af1efe5 100644
--- a/net/minecraft/world/level/levelgen/Heightmap.java
+++ b/net/minecraft/world/level/levelgen/Heightmap.java
@@ -128,7 +128,9 @@ public class Heightmap {
if (rawData.length == data.length) {
System.arraycopy(data, 0, rawData, 0, data.length);
} else {
- LOGGER.warn("Ignoring heightmap data for chunk {}, size does not match; expected: {}, got: {}", chunk.getPos(), rawData.length, data.length);
+ // Luminol - Add config for heightmap warning
+ if (!me.earthme.luminol.config.modules.misc.DisableWarningConfig.disableHeightmapWarning)
+ LOGGER.warn("Ignoring heightmap data for chunk {}, size does not match; expected: {}, got: {}", chunk.getPos(), rawData.length, data.length); // Luminol - Add config for heightmap warning
primeHeightmaps(chunk, EnumSet.of(type));
}
}
@@ -0,0 +1,19 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Sat, 18 Apr 2026 09:15:04 +0800
Subject: [PATCH] Add config gui support
diff --git a/net/minecraft/server/dialog/action/ParsedTemplate.java b/net/minecraft/server/dialog/action/ParsedTemplate.java
index 8a720ef5cd10bf04e97fb34c1ca0f0b265e98468..f1807523e1e6dbd13093f195c368b43a92b82af9 100644
--- a/net/minecraft/server/dialog/action/ParsedTemplate.java
+++ b/net/minecraft/server/dialog/action/ParsedTemplate.java
@@ -13,7 +13,7 @@ public class ParsedTemplate {
private final String raw;
private final StringTemplate parsed;
- private ParsedTemplate(final String raw, final StringTemplate parsed) {
+ public ParsedTemplate(final String raw, final StringTemplate parsed) { // Luminol - make public
this.raw = raw;
this.parsed = parsed;
}
@@ -0,0 +1,21 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Sat, 18 Apr 2026 09:15:22 +0800
Subject: [PATCH] Add force the data command to be enabled config
diff --git a/net/minecraft/commands/Commands.java b/net/minecraft/commands/Commands.java
index bc9f6cc286536b87513de81855ad0b61cf787c84..afbaf2116930d72ebb0c77745d02115abab14dc4 100644
--- a/net/minecraft/commands/Commands.java
+++ b/net/minecraft/commands/Commands.java
@@ -202,7 +202,9 @@ public class Commands {
ClearInventoryCommands.register(this.dispatcher, context);
//CloneCommands.register(this.dispatcher, context); // Folia - region threading - TODO
DamageCommand.register(this.dispatcher, context);
- //DataCommands.register(this.dispatcher); // Folia - region threading - TODO
+ if(me.earthme.luminol.config.modules.experiment.CommandConfig.data) { // Luminol - Config for data command
+ DataCommands.register(this.dispatcher); // Folia - region threading - TODO // Luminol - Config for data command
+ } // Luminol - Config for data command
//DataPackCommand.register(this.dispatcher, context); // Folia - region threading - TODO
//DebugCommand.register(this.dispatcher); // Folia - region threading - TODO
DefaultGameModeCommands.register(this.dispatcher);
@@ -0,0 +1,19 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Sat, 18 Apr 2026 09:15:32 +0800
Subject: [PATCH] Add experimental config for command block command execution
diff --git a/net/minecraft/world/level/BaseCommandBlock.java b/net/minecraft/world/level/BaseCommandBlock.java
index 31f1583f29eebe5e13b5a7ac5116fdafc3592e5c..47fe63e49a7cfb77f8fe5cc95ab9faf46d244ba9 100644
--- a/net/minecraft/world/level/BaseCommandBlock.java
+++ b/net/minecraft/world/level/BaseCommandBlock.java
@@ -91,7 +91,7 @@ public abstract class BaseCommandBlock {
}
public boolean performCommand(final ServerLevel level) {
- if (true) return false; // Folia - region threading
+ if (!me.earthme.luminol.config.modules.experiment.CommandConfig.commandBlock) return false; // Folia - region threading // Luminol - Add experimental config for command block command execution
if (level.getGameTime() == this.lastExecution) {
return false;
}
@@ -0,0 +1,20 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Sat, 18 Apr 2026 09:16:40 +0800
Subject: [PATCH] Add config to verify signature only in online-mode
Co-authored-by: Stabrinai <wujiaxin752@outlook.com>
diff --git a/net/minecraft/world/entity/player/ProfilePublicKey.java b/net/minecraft/world/entity/player/ProfilePublicKey.java
index 5e29a9de070e9922513e972254fc7ec43e8774f2..2de1886f9d9f06f8b92755e8421c176679e8b5ce 100644
--- a/net/minecraft/world/entity/player/ProfilePublicKey.java
+++ b/net/minecraft/world/entity/player/ProfilePublicKey.java
@@ -23,7 +23,7 @@ public record ProfilePublicKey(ProfilePublicKey.Data data) {
public static final Codec<ProfilePublicKey> TRUSTED_CODEC = ProfilePublicKey.Data.CODEC.xmap(ProfilePublicKey::new, ProfilePublicKey::data);
public static ProfilePublicKey createValidated(final SignatureValidator validator, final UUID profileId, final ProfilePublicKey.Data data) throws ProfilePublicKey.ValidationException {
- if (!data.validateSignature(validator, profileId)) {
+ if (!data.validateSignature(validator, profileId) && (org.bukkit.Bukkit.getServer().getOnlineMode() && me.earthme.luminol.config.modules.misc.PublickeyVerifyConfig.enabled)) { // Luminol - Verify signature only in online-mode
throw new ProfilePublicKey.ValidationException(INVALID_SIGNATURE, org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_PUBLIC_KEY_SIGNATURE); // Paper - kick event causes
} else {
return new ProfilePublicKey(data);
@@ -0,0 +1,19 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Sat, 18 Apr 2026 09:17:02 +0800
Subject: [PATCH] Add config for whether to save portal tickets
diff --git a/net/minecraft/server/level/TicketType.java b/net/minecraft/server/level/TicketType.java
index 7e612e03087d63f9b500f7e9f3442b1931bae7ee..083a82b5200a251e85f155d1df016cd8b4f18db5 100644
--- a/net/minecraft/server/level/TicketType.java
+++ b/net/minecraft/server/level/TicketType.java
@@ -60,7 +60,7 @@ public final class TicketType<T> implements ca.spottedleaf.moonrise.patches.chun
public static final TicketType PLAYER_LOADING = register("player_loading", NO_TIMEOUT, FLAG_LOADING);
public static final TicketType PLAYER_SIMULATION = register("player_simulation", NO_TIMEOUT, FLAG_SIMULATION | FLAG_KEEP_DIMENSION_ACTIVE);
public static final TicketType FORCED = register("forced", NO_TIMEOUT, FLAG_PERSIST | FLAG_LOADING | FLAG_SIMULATION | FLAG_KEEP_DIMENSION_ACTIVE);
- public static final TicketType PORTAL = register("portal", 300L, FLAG_PERSIST | FLAG_LOADING | FLAG_SIMULATION | FLAG_KEEP_DIMENSION_ACTIVE);
+ public static final TicketType PORTAL = register("portal", 300L, me.earthme.luminol.config.modules.misc.SavePortalTicketsConfig.doSave ? FLAG_PERSIST | FLAG_LOADING | FLAG_SIMULATION | FLAG_KEEP_DIMENSION_ACTIVE : FLAG_LOADING | FLAG_SIMULATION | FLAG_KEEP_DIMENSION_ACTIVE);
public static final TicketType ENDER_PEARL = register("ender_pearl", 40L, FLAG_LOADING | FLAG_SIMULATION | FLAG_KEEP_DIMENSION_ACTIVE);
public static final TicketType UNKNOWN = register("unknown", 1L, FLAG_CAN_EXPIRE_IF_UNLOADED | FLAG_LOADING);
public static final TicketType PLUGIN = register("plugin", NO_TIMEOUT, FLAG_LOADING | FLAG_SIMULATION); // CraftBukkit
@@ -0,0 +1,61 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Sat, 18 Apr 2026 09:18:31 +0800
Subject: [PATCH] Add back read-only datapack command
diff --git a/net/minecraft/commands/Commands.java b/net/minecraft/commands/Commands.java
index afbaf2116930d72ebb0c77745d02115abab14dc4..6823c41c08ca1a1baf9257fc861ae97bbcbe3a50 100644
--- a/net/minecraft/commands/Commands.java
+++ b/net/minecraft/commands/Commands.java
@@ -205,7 +205,7 @@ public class Commands {
if(me.earthme.luminol.config.modules.experiment.CommandConfig.data) { // Luminol - Config for data command
DataCommands.register(this.dispatcher); // Folia - region threading - TODO // Luminol - Config for data command
} // Luminol - Config for data command
- //DataPackCommand.register(this.dispatcher, context); // Folia - region threading - TODO
+ DataPackCommand.register(this.dispatcher, context); // Folia - region threading - TODO // Luminol - Add back read-only datapack command
//DebugCommand.register(this.dispatcher); // Folia - region threading - TODO
DefaultGameModeCommands.register(this.dispatcher);
//DialogCommand.register(this.dispatcher, context); // Folia - region threading - TODO
diff --git a/net/minecraft/server/commands/DataPackCommand.java b/net/minecraft/server/commands/DataPackCommand.java
index 0da1e4ada8616af825ddd4678e61246c672f9c4f..80d9bc3d680c30dae15064e0e8c3d1379ccd52e6 100644
--- a/net/minecraft/server/commands/DataPackCommand.java
+++ b/net/minecraft/server/commands/DataPackCommand.java
@@ -99,7 +99,7 @@ public class DataPackCommand {
dispatcher.register(
Commands.literal("datapack")
.requires(Commands.hasPermission(Commands.LEVEL_GAMEMASTERS))
- .then(
+ /*.then( // Luminol - Add back read-only datapack command
Commands.literal("enable")
.then(
Commands.argument("name", StringArgumentType.string())
@@ -146,11 +146,11 @@ public class DataPackCommand {
.suggests(SELECTED_PACKS)
.executes(c -> disablePack(c.getSource(), getPack(c, "name", false)))
)
- )
+ )*/ // Luminol - Add back read-only datapack command
.then(
Commands.literal("list")
.executes(c -> listPacks(c.getSource()))
- .then(Commands.literal("available").executes(c -> listAvailablePacks(c.getSource())))
+ //.then(Commands.literal("available").executes(c -> listAvailablePacks(c.getSource()))) // Luminol - Add back read-only datapack command
.then(Commands.literal("enabled").executes(c -> listEnabledPacks(c.getSource())))
)
.then(
@@ -238,12 +238,12 @@ public class DataPackCommand {
}
private static int listPacks(final CommandSourceStack source) {
- return listEnabledPacks(source) + listAvailablePacks(source);
+ return listEnabledPacks(source) ;// + listAvailablePacks(source); // Luminol - Add back read-only datapack command
}
private static int listAvailablePacks(final CommandSourceStack source) {
PackRepository repository = source.getServer().getPackRepository();
- repository.reload();
+ //repository.reload(); // Luminol - Add back read-only datapack command
Collection<Pack> selectedPacks = repository.getSelectedPacks();
Collection<Pack> availablePacks = repository.getAvailablePacks();
FeatureFlagSet enabledFeatures = source.enabledFeatures();
@@ -0,0 +1,360 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Sun, 28 Jun 2026 20:16:33 +0800
Subject: [PATCH] Add tpsbar with chunkhot, membar and regionbar
diff --git a/ca/spottedleaf/moonrise/paper/util/BaseChunkSystemHooks.java b/ca/spottedleaf/moonrise/paper/util/BaseChunkSystemHooks.java
index 52dcbcf64c3a510d4a4524b7d0ee226d9a73511d..051d601e7e809518e8d82ae4ae3259054d704f51 100644
--- a/ca/spottedleaf/moonrise/paper/util/BaseChunkSystemHooks.java
+++ b/ca/spottedleaf/moonrise/paper/util/BaseChunkSystemHooks.java
@@ -122,6 +122,7 @@ public abstract class BaseChunkSystemHooks implements ca.spottedleaf.moonrise.co
@Override
public void onChunkNotTicking(final LevelChunk chunk, final ChunkHolder holder) {
+ chunk.getChunkHot().clear(); // KioCG
chunk.getLevel().getCurrentWorldData().removeTickingChunk(chunk); // Folia - region threading
}
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index d4e31bb829451a687bf567fd0357f46fd7d46902..350b788e17dc49118498541ad8ac54f6e0a59c9d 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -1743,7 +1743,46 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
// Folia end - region threading
//this.tickCount++; // Folia - region threading
//this.tickRateManager.tick(); // Folia - region threading
+ // KioCG start - ChunkHot
+ final ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet<net.minecraft.world.level.chunk.LevelChunk> chunks = new ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet<>();
+ if (region != null){
+ for (net.minecraft.world.level.chunk.LevelChunk chunk : region.world.getCurrentWorldData().getTickingChunks()) {
+ /* wait for rewrite - temporarily crash fix
+ for (net.minecraft.server.level.ServerChunkCache.ChunkAndHolder chunkAndHolder : region.world.getCurrentWorldData().getTickingChunks()){
+ final net.minecraft.world.level.chunk.LevelChunk chunk = chunkAndHolder.chunk();
+ */
+ if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(region.world, chunk.locX, chunk.locZ)){
+ continue;
+ }
+
+ chunks.add(chunk);
+ }
+ }
+ if (region != null && io.papermc.paper.threadedregions.RegionizedServer.getCurrentTick() % 20 == 0){
+ final java.util.Iterator<net.minecraft.world.level.chunk.LevelChunk> chunkIterator = chunks.unsafeIterator();
+ while (chunkIterator.hasNext()){
+ final net.minecraft.world.level.chunk.LevelChunk targetChunk = chunkIterator.next();
+
+ targetChunk.getChunkHot().nextTick();
+ targetChunk.getChunkHot().start();
+ }
+ }
+ //KioCG end
this.tickChildren(haveTime, region); // Folia - region threading
+ // KioCG start - ChunkHot
+ if (region != null && io.papermc.paper.threadedregions.RegionizedServer.getCurrentTick() % 20 == 0){
+ final java.util.Iterator<net.minecraft.world.level.chunk.LevelChunk> chunkIterator = chunks.unsafeIterator();
+ while (chunkIterator.hasNext()){
+ final net.minecraft.world.level.chunk.LevelChunk targetChunk = chunkIterator.next();
+
+ if (!targetChunk.getChunkHot().isStarted()){
+ continue;
+ }
+
+ targetChunk.getChunkHot().stop();
+ }
+ }
+ //KioCG end
if (false && nano - this.lastServerStatus >= STATUS_EXPIRE_TIME_NANOS) { // Folia - region threading
this.lastServerStatus = nano;
this.status = this.buildServerStatus();
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 0641b98ef3298eb682ceae83f60b730f3bf90103..cea4bc7036dfd8777c151a0dd811fc13579d6501 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -1491,6 +1491,8 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
public void tickNonPassenger(final Entity entity) {
// Paper start - log detailed entity tick information
ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread("Cannot tick an entity off-main");
+ LevelChunk levelChunk = entity.shouldTickHot() ? this.getChunkIfLoaded(entity.moonrise$getSectionX(),entity.moonrise$getSectionZ()) : null; // KioCG
+ if (levelChunk != null) levelChunk.getChunkHot().startTicking(); try { // KioCG
try {
// Folia - region threading
// Paper end - log detailed entity tick information
@@ -1552,6 +1554,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
} finally {
// Folia - region threading
}
+ } finally { if (levelChunk != null) levelChunk.getChunkHot().stopTickingAndCount(); } // KioCG
// Paper end - log detailed entity tick information
}
@@ -1563,6 +1566,8 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
final int timerId = isActive ? entity.getType().tickTimerId : entity.getType().inactiveTickTimerId;
final ca.spottedleaf.leafprofiler.RegionizedProfiler.Handle foliaProfiler = io.papermc.paper.threadedregions.TickRegionScheduler.getProfiler();
foliaProfiler.startTimer(timerId);
+ LevelChunk levelChunk = !(entity instanceof Player) ? this.getChunkIfLoaded(entity.blockPosition()) : null; // KioCG
+ if (levelChunk != null) levelChunk.getChunkHot().startTicking(); try { // KioCG
try {
// Folia end - profiler
entity.setOldPosAndRot();
@@ -1597,6 +1602,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
this.tickPassenger(entity, passenger, isActive); // Paper - EAR 2
}
} finally { foliaProfiler.stopTimer(timerId); } // Folia - profiler
+ } finally { if (levelChunk != null) levelChunk.getChunkHot().stopTickingAndCount(); } // KioCG
}
}
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index ab6229ee412cf2aa5affc87510230c587765080b..f0c574e039f80ff0227239356de8d61ef06a0936 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -441,7 +441,9 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
public boolean isRealPlayer; // Paper
public com.destroystokyo.paper.event.entity.@Nullable PlayerNaturallySpawnCreaturesEvent playerNaturallySpawnedEvent; // Paper - PlayerNaturallySpawnCreaturesEvent
public org.bukkit.event.player.PlayerQuitEvent.@Nullable QuitReason quitReason = null; // Paper - Add API for quit reason; there are a lot of changes to do if we change all methods leading to the event
-
+ public volatile boolean isTpsBarVisible = false; //Luminol - Tps bar
+ public volatile boolean isMemBarVisible = false; //Luminol - Memory bar
+ public volatile boolean isRegionBarVisible = false; //Luminol - Region bar
// Paper start - rewrite chunk system
private ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader.PlayerChunkLoaderData chunkLoader;
private final ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader.ViewDistanceHolder viewDistanceHolder = new ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader.ViewDistanceHolder();
@@ -889,6 +891,9 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
@Override
public void tick() {
+ // Luminol start - Status bars
+ this.statusBarList.tick();
+ // Luminol end - Status bars
// CraftBukkit start
if (this.joining) {
this.joining = false;
@@ -949,8 +954,35 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
this.trackEnteredOrExitedLavaOnVehicle();
this.updatePlayerAttributes();
this.advancements.flushDirty(this, true);
+
+ // KioCG start - ChunkHot
+ if (this.tickCount % 20 == 0){
+ this.nearbyChunkHot = this.refreshNearbyChunkHot();
+ }
+ // KioCG end
}
+ // KioCG start - ChunkHot
+ private volatile long nearbyChunkHot = 0;
+
+ public long getNearbyChunkHot() { return this.nearbyChunkHot; }
+
+ private long refreshNearbyChunkHot() {
+ long total = 0L;
+ int searchRadius = ((ServerLevel) this.level()).moonrise$getViewDistanceHolder().getViewDistances().tickViewDistance();
+ for (int i = this.moonrise$getSectionX() - searchRadius; i <= this.moonrise$getSectionX() + searchRadius; ++i) {
+ for (int j = this.moonrise$getSectionZ() - searchRadius; j <= this.moonrise$getSectionZ() + searchRadius; ++j) {
+ net.minecraft.world.level.chunk.LevelChunk targetChunk = this.level().getChunkIfLoaded(i, j);
+ if (targetChunk != null) {
+ total += targetChunk.getChunkHot().getAverage();
+ }
+ }
+ }
+ return total;
+ }
+ // KioCG end
+
+
private void updatePlayerAttributes() {
AttributeInstance blockInteractionRange = this.getAttribute(Attributes.BLOCK_INTERACTION_RANGE);
if (blockInteractionRange != null) {
diff --git a/net/minecraft/world/entity/AreaEffectCloud.java b/net/minecraft/world/entity/AreaEffectCloud.java
index f03fa06c0ba56f7f5e1e45bc1568a490751efde3..eee1c14249addbf4714df733870da9747bda2245 100644
--- a/net/minecraft/world/entity/AreaEffectCloud.java
+++ b/net/minecraft/world/entity/AreaEffectCloud.java
@@ -415,4 +415,11 @@ public class AreaEffectCloud extends Entity implements TraceableEntity {
return super.applyImplicitComponent(type, value);
}
}
+
+ // KioCG start
+ @Override
+ public boolean shouldTickHot() {
+ return false;
+ }
+ // KioCG end
}
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index 17650e196f1e5b79765651bf302291873a4eb509..f218b7f6ce32d7ac959255e59c620d6fd420bf8e 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -6468,4 +6468,6 @@ public abstract class Entity
return ((ServerLevel) this.level()).isPositionEntityTicking(this.blockPosition());
}
// Paper end
+
+ public boolean shouldTickHot() { return this.tickCount > 20 * 10 && this.isAlive(); } // KioCG
}
diff --git a/net/minecraft/world/entity/LightningBolt.java b/net/minecraft/world/entity/LightningBolt.java
index 5b19fee8a4861e5dbfb545bbe4dabda7787c430a..0afca6f5f5b02609fd55e9ea4d72b8862cc03420 100644
--- a/net/minecraft/world/entity/LightningBolt.java
+++ b/net/minecraft/world/entity/LightningBolt.java
@@ -283,4 +283,11 @@ public class LightningBolt extends Entity {
public final boolean hurtServer(final ServerLevel level, final DamageSource source, final float damage) {
return false;
}
+
+ // KioCG start
+ @Override
+ public boolean shouldTickHot() {
+ return false;
+ }
+ // KioCG end
}
diff --git a/net/minecraft/world/entity/Mob.java b/net/minecraft/world/entity/Mob.java
index 9c8e8de53cbbd87ac7cb8483bd3c1af3fcf66b6e..301e75393d1074eca0c8533219f516b6d625a38a 100644
--- a/net/minecraft/world/entity/Mob.java
+++ b/net/minecraft/world/entity/Mob.java
@@ -1735,4 +1735,11 @@ public abstract class Mob extends LivingEntity implements Targeting, EquipmentUs
public float chargeSpeedModifier() {
return 1.0F;
}
+
+ // KioCG start
+ @Override
+ public boolean shouldTickHot() {
+ return super.shouldTickHot() && (!this.removeWhenFarAway(0.0) || this.isPersistenceRequired() || this.requiresCustomPersistence());
+ }
+ // KioCG end
}
diff --git a/net/minecraft/world/entity/animal/equine/TraderLlama.java b/net/minecraft/world/entity/animal/equine/TraderLlama.java
index 7c92c9335997cee76ff360c51f704ee41a180796..fb4cab83fd23c5561326a58683558e9a4219b0e9 100644
--- a/net/minecraft/world/entity/animal/equine/TraderLlama.java
+++ b/net/minecraft/world/entity/animal/equine/TraderLlama.java
@@ -164,4 +164,11 @@ public class TraderLlama extends Llama {
super.start();
}
}
+
+ // KioCG start
+ @Override
+ public boolean shouldTickHot() {
+ return super.shouldTickHot() && !this.canDespawn();
+ }
+ // KioCG end
}
diff --git a/net/minecraft/world/entity/npc/wanderingtrader/WanderingTrader.java b/net/minecraft/world/entity/npc/wanderingtrader/WanderingTrader.java
index f79353aa58b03d00839e4e95d1fd2480a635a592..0a43947bcc9f505ad9bc4d54c564709c2c3e411a 100644
--- a/net/minecraft/world/entity/npc/wanderingtrader/WanderingTrader.java
+++ b/net/minecraft/world/entity/npc/wanderingtrader/WanderingTrader.java
@@ -271,4 +271,11 @@ public class WanderingTrader extends AbstractVillager implements Consumable.Over
return !pos.closerToCenterThan(this.trader.position(), distance);
}
}
+
+ // KioCG start
+ @Override
+ public boolean shouldTickHot() {
+ return false;
+ }
+ // KioCG end
}
diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java
index 2eb5fc819e2e17c69de87f8aa1a99e02bc5a62d5..a4bc767b799c050ce07a3685f655b4ba947d2466 100644
--- a/net/minecraft/world/entity/player/Player.java
+++ b/net/minecraft/world/entity/player/Player.java
@@ -170,6 +170,7 @@ public abstract class Player extends Avatar implements ContainerUser {
private ItemStack lastItemInMainHand = ItemStack.EMPTY;
private final ItemCooldowns cooldowns = this.createItemCooldowns();
private Optional<GlobalPos> lastDeathLocation = Optional.empty();
+ public me.earthme.luminol.functions.bars.TickableStatusBarList statusBarList = new me.earthme.luminol.functions.bars.TickableStatusBarList(this); // Luminol status bars
public @Nullable FishingHook fishing;
public float hurtDir;
public boolean affectsSpawning = true; // Paper - Affects Spawning API
@@ -679,6 +680,7 @@ public abstract class Player extends Avatar implements ContainerUser {
this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(this.abilities.getWalkingSpeed());
this.enderChestInventory.fromSlots(input.listOrEmpty("EnderItems", ItemStackWithSlot.CODEC));
this.setLastDeathLocation(input.read("LastDeathLocation", GlobalPos.CODEC));
+ this.statusBarList.load(input); // Luminol - Status bars
}
@Override
@@ -697,6 +699,7 @@ public abstract class Player extends Avatar implements ContainerUser {
output.store("abilities", Abilities.Packed.CODEC, this.abilities.pack());
this.enderChestInventory.storeAsSlots(output.list("EnderItems", ItemStackWithSlot.CODEC));
this.lastDeathLocation.ifPresent(pos -> output.store("LastDeathLocation", GlobalPos.CODEC, pos));
+ this.statusBarList.save(output); // Luminol - Status bars
}
@Override
@@ -2255,4 +2258,12 @@ public abstract class Player extends Avatar implements ContainerUser {
public static final Player.BedSleepingProblem NOT_SAFE = new Player.BedSleepingProblem(Component.translatable("block.minecraft.bed.not_safe"));
public static final Player.BedSleepingProblem EXPLOSION = new Player.BedSleepingProblem(null); // Paper - Added to properly handle explosions in bed events
}
+
+ // KioCG start
+ @Override
+ public boolean shouldTickHot() {
+ return false;
+ }
+ // KioCG end
+
}
diff --git a/net/minecraft/world/entity/projectile/Projectile.java b/net/minecraft/world/entity/projectile/Projectile.java
index 67d01bb84cd40a3f67de441e9dbdc2ac7638cada..721e3b083846fd363a31311196bc4c681969215c 100644
--- a/net/minecraft/world/entity/projectile/Projectile.java
+++ b/net/minecraft/world/entity/projectile/Projectile.java
@@ -512,4 +512,11 @@ public abstract class Projectile extends Entity implements TraceableEntity {
public interface ProjectileFactory<T extends Projectile> {
T create(final ServerLevel level, LivingEntity entity, ItemStack itemStack);
}
+
+ // KioCG start
+ @Override
+ public boolean shouldTickHot() {
+ return false;
+ }
+ // KioCG end
}
diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java
index 64e0c4a60a74cd33f2b99f34fa2a419abf3a0c02..0e84b695a677e71f443a29c78fc5025eec7ae7f4 100644
--- a/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/net/minecraft/world/level/chunk/LevelChunk.java
@@ -112,6 +112,7 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot
// Paper start - rewrite chunk system
private boolean postProcessingDone;
private ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder chunkAndHolder;
+ private final com.kiocg.ChunkHot chunkHot = new com.kiocg.ChunkHot(); public com.kiocg.ChunkHot getChunkHot() { return this.chunkHot; } // KioCG
@Override
public final boolean moonrise$isPostProcessingDone() {
@@ -960,6 +961,7 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot
ProfilerFiller profiler = Profiler.get();
profiler.push(this::getType);
foliaProfiler.startTimer(timerId); try { // Folia - profiler
+ LevelChunk.this.chunkHot.startTicking(); // KioCG
BlockState blockState = LevelChunk.this.getBlockState(pos);
if (this.blockEntity.getType().isValid(blockState)) {
this.ticker.tick(LevelChunk.this.level, this.blockEntity.getBlockPos(), blockState, this.blockEntity);
@@ -979,7 +981,7 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot
);
} // Paper - Remove the Block Entity if it's invalid
}
- } finally { foliaProfiler.stopTimer(timerId); } // Folia - profiler
+ } finally { foliaProfiler.stopTimer(timerId); LevelChunk.this.chunkHot.stopTickingAndCount(); } // Folia - profiler // KioCG
profiler.pop();
} catch (Throwable t) {
diff --git a/net/minecraft/world/level/redstone/NeighborUpdater.java b/net/minecraft/world/level/redstone/NeighborUpdater.java
index 041ed95948ff6bb4cf3610ce4522a6ba105b6812..44dbe6f83c8593c213132feabc7fe27615cb0049 100644
--- a/net/minecraft/world/level/redstone/NeighborUpdater.java
+++ b/net/minecraft/world/level/redstone/NeighborUpdater.java
@@ -81,7 +81,10 @@ public interface NeighborUpdater {
return;
}
// CraftBukkit end
+ net.minecraft.world.level.chunk.LevelChunk levelChunk = level.getChunkIfLoaded(pos); // KioCG
+ if (levelChunk != null) levelChunk.getChunkHot().startTicking(); try { // KioCG
state.handleNeighborChanged(level, pos, changedBlock, orientation, movedByPiston);
+ } finally { if (levelChunk != null) levelChunk.getChunkHot().stopTickingAndCount(); } // KioCG
// Spigot start
} catch (StackOverflowError ex) {
level.lastPhysicsProblem = pos.immutable();
@@ -0,0 +1,110 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Sun, 28 Jun 2026 20:30:46 +0800
Subject: [PATCH] Add config to modify tripwire behavior
diff --git a/net/minecraft/world/level/block/TripWireHookBlock.java b/net/minecraft/world/level/block/TripWireHookBlock.java
index 8bcc8b6f83ab4d5a472741185511b5d84f4974be..49b3f9e78ae9db257b653a6094619880784c6370 100644
--- a/net/minecraft/world/level/block/TripWireHookBlock.java
+++ b/net/minecraft/world/level/block/TripWireHookBlock.java
@@ -201,10 +201,17 @@ public class TripWireHookBlock extends Block {
BlockPos testPos = pos.relative(direction, i);
BlockState wireData = wireStates[i];
if (wireData != null) {
- BlockState testPosState = level.getBlockState(testPos);
- if (testPosState.is(Blocks.TRIPWIRE) || testPosState.is(Blocks.TRIPWIRE_HOOK)) {
- if (!io.papermc.paper.configuration.GlobalConfiguration.get().blockUpdates.disableTripwireUpdates || !testPosState.is(Blocks.TRIPWIRE)) level.setBlock(testPos, wireData.trySetValue(ATTACHED, attached), Block.UPDATE_ALL); // Paper - prevent tripwire from updating
+ // Luminol start - tripwire and tripwireHook dupe
+ if (me.earthme.luminol.config.modules.function.TripwireBehaviorConfig.enabled) {
+ level.setBlock(testPos, wireData.trySetValue(ATTACHED, attached), 3);
+ level.getBlockState(testPos);
+ } else {
+ BlockState testPosState = level.getBlockState(testPos);
+ if (testPosState.is(Blocks.TRIPWIRE) || testPosState.is(Blocks.TRIPWIRE_HOOK)) {
+ if (!io.papermc.paper.configuration.GlobalConfiguration.get().blockUpdates.disableTripwireUpdates || !testPosState.is(Blocks.TRIPWIRE)) level.setBlock(testPos, wireData.trySetValue(ATTACHED, attached), Block.UPDATE_ALL); // Paper - prevent tripwire from updating
+ }
}
+ // Luminol end - tripwire and tripwireHook dupe
}
}
}
diff --git a/net/minecraft/world/level/levelgen/feature/EndPlatformFeature.java b/net/minecraft/world/level/levelgen/feature/EndPlatformFeature.java
index b67f5f7b76037ab77548c37a83e4d0918915223a..a8e69343f47ba2383f9dcc2cf6328463892aaeec 100644
--- a/net/minecraft/world/level/levelgen/feature/EndPlatformFeature.java
+++ b/net/minecraft/world/level/levelgen/feature/EndPlatformFeature.java
@@ -28,14 +28,39 @@ public class EndPlatformFeature extends Feature<NoneFeatureConfiguration> {
// CraftBukkit end
BlockPos.MutableBlockPos pos = origin.mutable();
- for (int dz = -2; dz <= 2; dz++) {
+ // Luminol start - tripwire behavior modifier
+ java.util.List<BlockPos> blockList1 = new java.util.ArrayList<>();
+ java.util.List<BlockPos> blockList2 = new java.util.ArrayList<>();
+ boolean flag21 = me.earthme.luminol.config.modules.function.TripwireBehaviorConfig.behaviorMode == me.earthme.luminol.enums.EnumTripwireBehavior.VANILLA21; for (int dz = -2; dz <= 2; dz++) {
for (int dx = -2; dx <= 2; dx++) {
for (int dy = -1; dy < 3; dy++) {
BlockPos blockPos = pos.set(origin).move(dx, dy, dz);
Block block = dy == -1 ? Blocks.OBSIDIAN : Blocks.AIR;
if (!blockList.getBlockState(blockPos).is(block)) { // CraftBukkit
if (dropResources) {
- blockList.destroyBlock(blockPos, true, null); // CraftBukkit
+ boolean flag = false;
+ if (me.earthme.luminol.config.modules.function.TripwireBehaviorConfig.enabled) {
+ switch (me.earthme.luminol.config.modules.function.TripwireBehaviorConfig.behaviorMode) {
+ case me.earthme.luminol.enums.EnumTripwireBehavior.VANILLA20: {
+ flag = true;
+ }
+ case me.earthme.luminol.enums.EnumTripwireBehavior.MIXED: {
+ net.minecraft.world.level.block.state.BlockState state = newLevel.getBlockState(blockPos);
+ if (state.is(Blocks.TRIPWIRE)) {
+ if (state.getValue(net.minecraft.world.level.block.TripWireBlock.DISARMED)) {
+ flag = true;
+ blockList2.add(blockPos.immutable());
+ }
+ if (!flag) {
+ flag = checkString(blockList2, blockPos);
+ }
+ }
+ }
+ default: {} // Luminol - 1.21 & default Logic - default empty
+ }
+ }
+ if (flag) blockList1.add(blockPos.immutable());
+ else blockList.destroyBlock(blockPos, true, null); // CraftBukkit
}
blockList.setBlock(blockPos, block.defaultBlockState(), Block.UPDATE_ALL); // CraftBukkit
@@ -53,11 +78,30 @@ public class EndPlatformFeature extends Feature<NoneFeatureConfiguration> {
if (portalEvent.isCancelled()) return;
}
- if (dropResources) {
- blockList.placeBlocks(state -> newLevel.destroyBlock(state.getPosition(), true, null));
+ if (flag21 || !me.earthme.luminol.config.modules.function.TripwireBehaviorConfig.enabled) {
+ if (dropResources) {
+ blockList.placeBlocks(state -> newLevel.destroyBlock(state.getPosition(), true, null));
+ } else {
+ blockList.placeBlocks();
+ }
} else {
+ if (dropResources) {
+ blockList.getSnapshotBlocks().forEach((state) -> {
+ newLevel.destroyBlock(state.getPosition(), !blockList1.contains(state.getPosition()), null);
+ });
+ // Luminol - prevent tripwire dupe in end platform generate
+ }
blockList.placeBlocks();
}
// CraftBukkit end
}
+
+ private static boolean checkString(java.util.List<BlockPos> blockList, BlockPos blockPos) {
+ for (BlockPos pos : blockList) {
+ if (pos.getY() != blockPos.getY()) continue;
+ if (pos.getX() == blockPos.getX() || pos.getZ() == blockPos.getZ()) return true;
+ }
+ return false;
+ }
+ // Luminol end - tripwire behavior modifier
}
@@ -0,0 +1,104 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Fri, 5 Jun 2026 15:09:29 +0800
Subject: [PATCH] Add config to enable tick command
only freeze/unfreeze/step/query can run when enabled
diff --git a/io/papermc/paper/threadedregions/RegionizedServer.java b/io/papermc/paper/threadedregions/RegionizedServer.java
index ff90abb47203c03636d3175f2b3efcf43517b3b0..276349d778c2e6f0b1081eec4851bad5d25db237 100644
--- a/io/papermc/paper/threadedregions/RegionizedServer.java
+++ b/io/papermc/paper/threadedregions/RegionizedServer.java
@@ -239,6 +239,11 @@ public final class RegionizedServer {
private void globalTick(final long tickCount) {
++this.tickCount;
+ // Luminol start - Add a config to enable tick command
+ if (me.earthme.luminol.config.modules.experiment.CommandConfig.tick) {
+ MinecraftServer.getServer().tickRateManager().tick();
+ }
+ // Luminol end - Add a config to enable tick command
// expire invalid click command callbacks
io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.ADVENTURE_CLICK_MANAGER.handleQueue((int)this.tickCount); // Paper // Folia - region threading - moved to global tick
io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.DIALOG_CLICK_MANAGER.handleQueue((int)this.tickCount); // Paper // Folia - region threading - moved to global tick
@@ -415,7 +420,7 @@ public final class RegionizedServer {
}
private void tickTime(final ServerLevel world, final long tickCount) {
- if (world.tickTime) {
+ if ((!me.earthme.luminol.config.modules.experiment.CommandConfig.tick || world.tickRateManager().runsNormally()) && world.tickTime) { // Luminol - Add a config to enable tick command
world.serverLevelData.setGameTime(world.serverLevelData.getGameTime() + tickCount);
}
}
diff --git a/io/papermc/paper/threadedregions/TickRegionScheduler.java b/io/papermc/paper/threadedregions/TickRegionScheduler.java
index 58e557923dfe6cee39ec45e7f5aa43a5ded9f107..5f4f80c1d4003254fd840f71d86908603b57bfa2 100644
--- a/io/papermc/paper/threadedregions/TickRegionScheduler.java
+++ b/io/papermc/paper/threadedregions/TickRegionScheduler.java
@@ -559,6 +559,11 @@ public final class TickRegionScheduler {
try {
// next start isn't updated until the end of this tick
this.tickRegion(tickCount, tickStart, scheduledEnd);
+ // Luminol start - Add a config to enable tick command
+ if (me.earthme.luminol.config.modules.experiment.CommandConfig.tick) {
+ MinecraftServer.getServer().tickRateManager().endTickWork();
+ }
+ // Luminol end - Add a config to enable tick command
} catch (final Throwable thr) {
this.scheduler.regionFailed(this, false, thr);
// regionFailed will schedule a shutdown, so we should avoid letting this region tick further
diff --git a/net/minecraft/commands/Commands.java b/net/minecraft/commands/Commands.java
index 6823c41c08ca1a1baf9257fc861ae97bbcbe3a50..0fa14f60310b063f419620539fd40898627b46b9 100644
--- a/net/minecraft/commands/Commands.java
+++ b/net/minecraft/commands/Commands.java
@@ -259,7 +259,11 @@ public class Commands {
TeleportCommand.register(this.dispatcher);
TellRawCommand.register(this.dispatcher, context);
//TestCommand.register(this.dispatcher, context); // Folia - region threading
- //TickCommand.register(this.dispatcher); // Folia - region threading - TODO later
+ // Luminol start - Add a config to enable tick command
+ if (me.earthme.luminol.config.modules.experiment.CommandConfig.tick) {
+ TickCommand.register(this.dispatcher); // Folia - region threading - TODO later
+ }
+ // Luminol end - Add a config to enable tick command
TimeCommand.register(this.dispatcher, context);
TitleCommand.register(this.dispatcher, context);
//TriggerCommand.register(this.dispatcher); // Folia - region threading - TODO later
diff --git a/net/minecraft/server/commands/TickCommand.java b/net/minecraft/server/commands/TickCommand.java
index e8d6a67143f3f0b4813e51bb273498bc404899b9..64b685b219b930a1bb7f85db9947f4d1ca9df093 100644
--- a/net/minecraft/server/commands/TickCommand.java
+++ b/net/minecraft/server/commands/TickCommand.java
@@ -23,14 +23,14 @@ public class TickCommand {
Commands.literal("tick")
.requires(Commands.hasPermission(Commands.LEVEL_ADMINS))
.then(Commands.literal("query").executes(c -> tickQuery(c.getSource())))
- .then(
+/* .then(
Commands.literal("rate")
.then(
Commands.argument("rate", FloatArgumentType.floatArg(1.0F, 10000.0F))
.suggests((c, b) -> SharedSuggestionProvider.suggest(new String[]{DEFAULT_TICKRATE}, b))
.executes(c -> setTickingRate(c.getSource(), FloatArgumentType.getFloat(c, "rate")))
)
- )
+ )*/
.then(
Commands.literal("step")
.executes(c -> step(c.getSource(), 1))
@@ -41,7 +41,7 @@ public class TickCommand {
.executes(c -> step(c.getSource(), IntegerArgumentType.getInteger(c, "time")))
)
)
- .then(
+/* .then(
Commands.literal("sprint")
.then(Commands.literal("stop").executes(c -> stopSprinting(c.getSource())))
.then(
@@ -49,7 +49,7 @@ public class TickCommand {
.suggests((c, b) -> SharedSuggestionProvider.suggest(new String[]{"60s", "1d", "3d"}, b))
.executes(c -> sprint(c.getSource(), IntegerArgumentType.getInteger(c, "time")))
)
- )
+ )*/
.then(Commands.literal("unfreeze").executes(c -> setFreeze(c.getSource(), false)))
.then(Commands.literal("freeze").executes(c -> setFreeze(c.getSource(), true)))
);
@@ -0,0 +1,39 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Libalpm64 <Levamara@proton.me>
Date: Wed, 4 Mar 2026 00:00:00 +0000
Subject: [PATCH] Add config for item multitask
Allows players to use items while moving or switching hotbar slots. This
is for Anarchy servers or Crystal PVP servers this allows them to pvp
without stopping the item mid animation.
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 1ea8e03a4dc39486505d21bff1d15cb2339cafc2..a2146662c8ec83f62547bcd813c0e4dcb10efc07 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -2186,7 +2186,9 @@ public class ServerGamePacketListenerImpl
this.player.sendSpawnProtectionMessage(pos);
} else if (this.awaitingPositionFromClient == null && (level.mayInteract(this.player, pos) || passthroughSignInteraction)) {
// Paper end - Allow using signs inside spawn protection
+ if (!me.earthme.luminol.config.modules.fixes.ItemMultitaskConfig.enabled) { // Luminol - Add item multitask config
this.player.stopUsingItem(); // CraftBukkit - SPIGOT-4706
+ } // Luminol - Add item multitask config
InteractionResult interactionResult = this.player.gameMode.useItemOn(this.player, level, itemStack, hand, blockHit);
if (interactionResult.consumesAction()) {
CriteriaTriggers.ANY_BLOCK_USE.trigger(this.player, blockHit.getBlockPos(), itemStack);
@@ -2385,9 +2387,13 @@ public class ServerGamePacketListenerImpl
return;
}
// CraftBukkit end
- if (this.player.getInventory().getSelectedSlot() != packet.getSlot() && this.player.getUsedItemHand() == InteractionHand.MAIN_HAND) {
- this.player.stopUsingItem();
+ // Luminol start - Add item multitask config
+ if (!me.earthme.luminol.config.modules.fixes.ItemMultitaskConfig.enabled) {
+ if (this.player.getInventory().getSelectedSlot() != packet.getSlot() && this.player.getUsedItemHand() == InteractionHand.MAIN_HAND) {
+ this.player.stopUsingItem();
+ }
}
+ // Luminol end
this.player.getInventory().setSelectedSlot(packet.getSlot());
this.player.resetLastActionTime();
@@ -0,0 +1,63 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Sat, 18 Apr 2026 10:49:29 +0800
Subject: [PATCH] Add tick regions api
diff --git a/io/papermc/paper/threadedregions/ThreadedRegionizer.java b/io/papermc/paper/threadedregions/ThreadedRegionizer.java
index 3449918b5001bb39c541ce1168eac4acf9144e1f..a346b96500ae859e9469c80e2ff6b39c4d3d0b52 100644
--- a/io/papermc/paper/threadedregions/ThreadedRegionizer.java
+++ b/io/papermc/paper/threadedregions/ThreadedRegionizer.java
@@ -43,6 +43,7 @@ public final class ThreadedRegionizer<R extends ThreadedRegionizer.ThreadedRegio
private final RegionCallbacks<R, S> callbacks;
private final StampedLock regionLock = new StampedLock();
private Thread writeLockOwner;
+ public final me.earthme.luminol.api.ThreadedRegionizer threadedRegionizerAPI; // Luminol - Tick region API
/*
static final record Operation(String type, int chunkX, int chunkZ) {}
@@ -72,6 +73,7 @@ public final class ThreadedRegionizer<R extends ThreadedRegionizer.ThreadedRegio
this.world = world;
this.callbacks = callbacks;
//this.loadTestData();
+ this.threadedRegionizerAPI = new me.earthme.luminol.api.impl.ThreadedRegionizerImpl(this.world); // Luminol - Tick region API
}
/*
@@ -693,6 +695,7 @@ public final class ThreadedRegionizer<R extends ThreadedRegionizer.ThreadedRegio
private final ReferenceOpenHashSet<ThreadedRegion<R, S>> mergeIntoLater = new ReferenceOpenHashSet<>();
private final ReferenceOpenHashSet<ThreadedRegion<R, S>> expectingMergeFrom = new ReferenceOpenHashSet<>();
+ public final me.earthme.luminol.api.ThreadedRegion threadedRegionAPI = new me.earthme.luminol.api.impl.ThreadedRegionImpl((ThreadedRegion<TickRegions.TickRegionData, TickRegions.TickRegionSectionData>) this); // Luminol - Tickregion API
public ThreadedRegion(final ThreadedRegionizer<R, S> regioniser) {
this.regioniser = regioniser;
@@ -819,7 +822,7 @@ public final class ThreadedRegionizer<R extends ThreadedRegionizer.ThreadedRegio
return this.deadSections.size() == this.sectionByKey.size();
}
- private final double getDeadSectionPercent() {
+ public final double getDeadSectionPercent() { // Luminol - Threaded regions api
return (double)this.deadSections.size() / (double)this.sectionByKey.size();
}
diff --git a/io/papermc/paper/threadedregions/TickRegions.java b/io/papermc/paper/threadedregions/TickRegions.java
index a6ab9cb01f4f49f5f44b6ed324fa6d51139aabb6..97c514673a454b752ab86b944fd143725ca8090d 100644
--- a/io/papermc/paper/threadedregions/TickRegions.java
+++ b/io/papermc/paper/threadedregions/TickRegions.java
@@ -156,6 +156,7 @@ public final class TickRegions implements ThreadedRegionizer.RegionCallbacks<Tic
private final AtomicInteger entityCount = new AtomicInteger();
private final AtomicInteger playerCount = new AtomicInteger();
private final AtomicInteger chunkCount = new AtomicInteger();
+ public final me.earthme.luminol.api.RegionStats regionStatsAPI = new me.earthme.luminol.api.impl.RegionStatsImpl(this); // Luminol - Tickregion API
public int getEntityCount() {
return this.entityCount.get();
@@ -207,6 +208,7 @@ public final class TickRegions implements ThreadedRegionizer.RegionCallbacks<Tic
private final AtomicBoolean hasPackets = new AtomicBoolean(false);
public volatile ca.spottedleaf.leafprofiler.RegionizedProfiler.Handle profiler; // Folia - profiler
+ public final me.earthme.luminol.api.TickRegionData tickRegionDataAPI = new me.earthme.luminol.api.impl.TickRegionDataImpl(this); // Luminol - Tickregion API
private TickRegionData(final ThreadedRegionizer.ThreadedRegion<TickRegionData, TickRegionSectionData> region) {
this.region = region;
@@ -0,0 +1,149 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Sat, 18 Apr 2026 10:50:12 +0800
Subject: [PATCH] Add missing teleportation event APIs for folia
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index f0c574e039f80ff0227239356de8d61ef06a0936..093c48e2379c01f45277b88f6c1b19b107a46326 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -1814,6 +1814,9 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
if (finalRespawnCompleteCallback != null) { // Luminol - Level hot unload apis
finalRespawnCompleteCallback.accept(ServerPlayer.this); // Luminol - Level hot unload apis
}
+ // Luminol - Add missing teleportation apis
+ new me.earthme.luminol.api.entity.player.PostPlayerRespawnEvent(ServerPlayer.this.getBukkitEntity()).callEvent();
+ // Luminol end
}
);
});
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index f218b7f6ce32d7ac959255e59c620d6fd420bf8e..2ae0cf7c9a4febfa79a9eab2f0e9309a61a78f56 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -4553,6 +4553,31 @@ public abstract class Entity
// Luminol end
// TODO any events that can modify go HERE
+ // Luminol start - Add missing teleportation apis
+ org.bukkit.Location destinationLoc;
+
+ if (pitch == null) {
+ if (yaw == null) {
+ destinationLoc = io.papermc.paper.util.MCUtil.toLocation(destination, pos, Float.NaN, Float.NaN);
+ } else {
+ destinationLoc = io.papermc.paper.util.MCUtil.toLocation(destination, pos, yaw, Float.NaN);
+ }
+ }else {
+ if (yaw == null) {
+ destinationLoc = io.papermc.paper.util.MCUtil.toLocation(destination, pos, Float.NaN, pitch);
+ }else {
+ destinationLoc = io.papermc.paper.util.MCUtil.toLocation(destination, pos, yaw, pitch);
+ }
+ }
+
+ final me.earthme.luminol.api.entity.EntityTeleportAsyncEvent wrapped = new me.earthme.luminol.api.entity.EntityTeleportAsyncEvent(
+ this.getBukkitEntity(),
+ cause,
+ destinationLoc
+ );
+
+ wrapped.callEvent();
+ // Luminol end
// check for same region
if (destination == this.level()
@@ -4671,6 +4696,17 @@ public abstract class Entity
case END: {
if (destination.getTypeKey() == net.minecraft.world.level.dimension.LevelStem.END) {
BlockPos targetPos = ServerLevel.END_SPAWN_POINT;
+ // Luminol start - Add missing teleportation apis
+ final org.bukkit.Location orginalPortalLocation = io.papermc.paper.util.MCUtil.toLocation(origin, originPortal);
+ final org.bukkit.Location targetPortalLocation = io.papermc.paper.util.MCUtil.toLocation(destination, targetPos);
+
+ final me.earthme.luminol.api.portal.PortalLocateEvent portalLocateEvent = new me.earthme.luminol.api.portal.PortalLocateEvent(
+ orginalPortalLocation,
+ targetPortalLocation
+ );
+
+ portalLocateEvent.callEvent();
+ // Luminol end
// need to load chunks so we can create the platform
destination.moonrise$loadChunksAsync(
targetPos, 16, // load 16 blocks to be safe from block physics
@@ -4696,6 +4732,17 @@ public abstract class Entity
);
} else {
BlockPos spawnPos = destination.getRespawnData().pos();
+ // Luminol start - Add missing teleportation apis
+ final org.bukkit.Location orginalPortalLocation = io.papermc.paper.util.MCUtil.toLocation(origin, originPortal);
+ final org.bukkit.Location targetPortalLocation = io.papermc.paper.util.MCUtil.toLocation(destination, spawnPos);
+
+ final me.earthme.luminol.api.portal.PortalLocateEvent portalLocateEvent = new me.earthme.luminol.api.portal.PortalLocateEvent(
+ orginalPortalLocation,
+ targetPortalLocation
+ );
+
+ portalLocateEvent.callEvent();
+ // Luminol end
// need to load chunk for heightmap
destination.moonrise$loadChunksAsync(
spawnPos, 0,
@@ -4751,7 +4798,17 @@ public abstract class Entity
WorldBorder destinationBorder = destination.getWorldBorder();
double dimensionScale = net.minecraft.world.level.dimension.DimensionType.getTeleportationScale(origin.dimensionType(), destination.dimensionType());
BlockPos targetPos = destination.getWorldBorder().clampToBounds(this.getX() * dimensionScale, this.getY(), this.getZ() * dimensionScale);
+ // Luminol start - Add missing teleportation apis
+ final org.bukkit.Location orginalPortalLocation = io.papermc.paper.util.MCUtil.toLocation(origin, originPortal);
+ final org.bukkit.Location targetPortalLocation = io.papermc.paper.util.MCUtil.toLocation(destination, targetPos);
+ final me.earthme.luminol.api.portal.PortalLocateEvent portalLocateEvent = new me.earthme.luminol.api.portal.PortalLocateEvent(
+ orginalPortalLocation,
+ targetPortalLocation
+ );
+
+ portalLocateEvent.callEvent();
+ // Luminol end
ca.spottedleaf.concurrentutil.completable.CallbackCompletable<BlockUtil.FoundRectangle> portalFound
= new ca.spottedleaf.concurrentutil.completable.CallbackCompletable<>();
@@ -4888,6 +4945,15 @@ public abstract class Entity
if (!this.canPortalAsync(destination, takePassengers)) {
return false;
}
+ // Luminol start - Add missing teleportation events
+ if (!new me.earthme.luminol.api.entity.PreEntityPortalEvent(
+ this.getBukkitEntity(),
+ io.papermc.paper.util.MCUtil.toLocation(this.level, portalPos),
+ destination.getWorld()
+ ).callEvent()) {
+ return false;
+ }
+ // Luminol end
// Kaiiju start - sync end platform spawning & entity teleportation
final java.util.function.Consumer<Entity> tpComplete = type == PortalType.END && destination.getTypeKey() == net.minecraft.world.level.dimension.LevelStem.END ?
diff --git a/net/minecraft/world/level/block/entity/TheEndGatewayBlockEntity.java b/net/minecraft/world/level/block/entity/TheEndGatewayBlockEntity.java
index e29e379f9b2238eafe27545681bb19c710003bcc..fdf220424f7693a7ea3ae385ddd771720825b0f7 100644
--- a/net/minecraft/world/level/block/entity/TheEndGatewayBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/TheEndGatewayBlockEntity.java
@@ -192,6 +192,18 @@ public class TheEndGatewayBlockEntity extends TheEndPortalBlockEntity {
portalTile.trySearchForExit(portalWorld, portalPos);
return false;
}
+ // Luminol start - Add missing teleportation apis
+ final org.bukkit.Location orginalPortalLocation = io.papermc.paper.util.MCUtil.toLocation(toTeleport.level(), portalPos);
+ final org.bukkit.Location targetPortalLocation = io.papermc.paper.util.MCUtil.toLocation(portalWorld, teleportPos);
+
+ final me.earthme.luminol.api.portal.PortalLocateEvent portalLocateEvent = new me.earthme.luminol.api.portal.PortalLocateEvent(
+ orginalPortalLocation,
+ targetPortalLocation
+ );
+
+ portalLocateEvent.callEvent();
+ // Luminol end
+
// note: we handle the position from the TeleportTransition
net.minecraft.world.level.portal.TeleportTransition teleport = net.minecraft.world.level.block.EndGatewayBlock.getTeleportTransition(
@@ -0,0 +1,130 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Bacteriawa <A3167717663@hotmail.com>
Date: Tue, 21 Apr 2026 02:27:38 +0800
Subject: [PATCH] Add Portal rate limiter
diff --git a/io/papermc/paper/threadedregions/RegionizedWorldData.java b/io/papermc/paper/threadedregions/RegionizedWorldData.java
index f32d118e1e925b2155cef3fcdcb1e194c541e242..2d5d4170e1f45ae9113c37623b4066861dc79d64 100644
--- a/io/papermc/paper/threadedregions/RegionizedWorldData.java
+++ b/io/papermc/paper/threadedregions/RegionizedWorldData.java
@@ -157,6 +157,10 @@ public final class RegionizedWorldData {
for (final ChunkHolder chunkHolder : from.chunkHoldersToBroadcast) {
into.chunkHoldersToBroadcast.add(chunkHolder);
}
+ // Luminol start - Portal rate limiter
+ into.portalRateThrottler.mergeWith(from.portalRateThrottler);
+ from.portalRateThrottler.destroy();
+ // Luminol end
}
@Override
@@ -322,6 +326,12 @@ public final class RegionizedWorldData {
into.chunkHoldersToBroadcast.add(chunkHolder);
}
}
+ // Luminol start - Portal rate limiter
+ for (var worldData : dataSet) {
+ from.portalRateThrottler.splitInto(worldData.portalRateThrottler);
+ }
+ from.portalRateThrottler.destroy();
+ // Luminol end
}
};
@@ -343,6 +353,35 @@ public final class RegionizedWorldData {
return this.isHandlingTick;
}
+ // Luminol start - Portal rate limiter
+ public final me.earthme.luminol.utils.RateThrottler portalRateThrottler = new me.earthme.luminol.utils.RateThrottler();
+ public final net.objecthunter.exp4j.Expression portalRateCapExpression = me.earthme.luminol.config.modules.function.PortalRateLimiterConfig.getExpressionIfConfigured();
+
+ private int computePortalRateCap() {
+ // expression mode is disabled
+ if (this.portalRateCapExpression == null) {
+ return me.earthme.luminol.config.modules.function.PortalRateLimiterConfig.maxPortalTeleportsPerTick;
+ }
+
+ final int tickingEntityCount = this.entityTickList.size();
+ final int tickingChunkCount = this.tickingChunks.size();
+ final int playerCount = this.localPlayers.size();
+
+ return me.earthme.luminol.config.modules.function.PortalRateLimiterConfig.computeExpression(
+ this.portalRateCapExpression,
+ tickingEntityCount,
+ tickingChunkCount,
+ playerCount
+ );
+ }
+ public boolean isPortalTeleportationOutOfRate() {
+ if (!me.earthme.luminol.config.modules.function.PortalRateLimiterConfig.enabled) {
+ return false;
+ }
+
+ return this.portalRateThrottler.isOutOfRate(this.computePortalRateCap());
+ }
+ // Luminol end
// entities
// this is copy on write to allow packet processing to iterate safely
private final CopyOnWriteArrayList<ServerPlayer> localPlayers = new CopyOnWriteArrayList<>();
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 350b788e17dc49118498541ad8ac54f6e0a59c9d..dc83fea1f4b8a1d2a82a52b88ee5c3157653f6f8 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -1973,6 +1973,9 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
//net.minecraft.world.level.block.entity.HopperBlockEntity.skipHopperEvents = level.paperConfig().hopper.disableMoveEvent || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper - Perf: Optimize Hoppers // Folia - region threading
profiler.push(() -> level + " " + level.dimension().identifier());
profiler.push("tick");
+ // Luminol start - Portal rate limiter
+ regionizedWorldData.portalRateThrottler.begin();
+ // Luminol end
try {
foliaProfiler.startTimer(level.tickTimerId); try { // Folia - profiler
@@ -1987,6 +1990,9 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
profiler.pop();
profiler.pop();
regionizedWorldData.explosionDensityCache.clear(); // Paper - Optimize explosions // Folia - region threading
+ // Luminol start - Portal rate limiter
+ regionizedWorldData.portalRateThrottler.done();
+ // Luminol end
}
//this.isIteratingOverLevels = false; // Paper - Throw exception on world create while being ticked // Folia - region threading
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index cea4bc7036dfd8777c151a0dd811fc13579d6501..402e2f01f2b3a8e9ce5c818f07581b4dab38fc26 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -1536,8 +1536,15 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
// removed from region while ticking
return;
}
+ // Luminol start - Portal rate limiter
+ var worldData = entity.level().getCurrentWorldData();
+ if (entity.portalProcess != null && worldData.isPortalTeleportationOutOfRate()) {
+ return;
+ }
+ // Luminol end
if (entity.handlePortal()) {
// portalled
+ worldData.portalRateThrottler.increase(); // Luminol - Portal rate limiter
return;
}
}
@@ -1584,8 +1591,15 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
// removed from region while ticking
return;
}
+ // Luminol start - Portal rate limiter
+ var worldData = entity.level().getCurrentWorldData();
+ if (entity.portalProcess != null && worldData.isPortalTeleportationOutOfRate()) {
+ return;
+ }
+ // Luminol end
if (entity.handlePortal()) {
// portalled
+ worldData.portalRateThrottler.increase(); // Luminol - Portal rate limiter
return;
}
// Folia end - region threading
@@ -0,0 +1,161 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Fri, 1 May 2026 22:05:23 +0800
Subject: [PATCH] Add config for waypoint restoration
diff --git a/net/minecraft/commands/Commands.java b/net/minecraft/commands/Commands.java
index 0fa14f60310b063f419620539fd40898627b46b9..9800ae1d3c49800b0c7342d43d696109b5b72cf4 100644
--- a/net/minecraft/commands/Commands.java
+++ b/net/minecraft/commands/Commands.java
@@ -267,7 +267,7 @@ public class Commands {
TimeCommand.register(this.dispatcher, context);
TitleCommand.register(this.dispatcher, context);
//TriggerCommand.register(this.dispatcher); // Folia - region threading - TODO later
- //WaypointCommand.register(this.dispatcher, context); // Folia - region threading - TODO later
+ if (me.earthme.luminol.config.modules.experiment.CommandConfig.waypointsAndWaypointCommand) WaypointCommand.register(this.dispatcher, context); // Folia - region threading - TODO later // Luminol - Restore waypoints
WeatherCommand.register(this.dispatcher);
WorldBorderCommand.register(this.dispatcher);
if (JvmProfiler.INSTANCE.isAvailable()) {
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index dc83fea1f4b8a1d2a82a52b88ee5c3157653f6f8..7840b0e00ab84eab016e971ac0c2693056fb0721 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -3117,8 +3117,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
level.players().forEach(playerx -> playerx.connection.send(packet)); // Paper - per-world game rules
} else if (rule == GameRules.LOCATOR_BAR) {
// this.getAllLevels().forEach(level -> { // Paper - per-world game rules
- ServerWaypointManager waypointManager = level.getWaypointManager();
- waypointManager.locatorBarEnabled = (Boolean) value; // Paper - optimize ServerWaypointManager with locator bar disabled
+ ServerWaypointManager waypointManager = level.getWaypointManager(); // Luminol - Restore waypoints
+ //waypointManager.locatorBarEnabled = (Boolean) value; // Paper - optimize ServerWaypointManager with locator bar disabled // Luminol - Restore waypoints
if ((Boolean)value) {
level.players().forEach(waypointManager::updatePlayer);
} else {
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 402e2f01f2b3a8e9ce5c818f07581b4dab38fc26..a1c3f3e5c7c3f32e0bdead33ebd72c7601295291 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -690,7 +690,8 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
this.sleepStatus = new SleepStatus();
this.gameEventDispatcher = new GameEventDispatcher(this);
- this.waypointManager = new ServerWaypointManager(this); // Paper - optimize ServerWaypointManager with locator bar disabled
+ //this.waypointManager = new ServerWaypointManager(this); // Paper - optimize ServerWaypointManager with locator bar disabled // Luminol - Restore waypoints
+ this.waypointManager = new me.earthme.luminol.utils.FoliaServerWaypointManager(this); // Luminol - Restore waypoints
this.environmentAttributes = EnvironmentAttributeSystem.builder().addDefaultLayers(this).build();
//this.updateSkyBrightness(); // Folia - region threading - delay until first tick
// Paper start - rewrite chunk system
diff --git a/net/minecraft/world/waypoints/WaypointTransmitter.java b/net/minecraft/world/waypoints/WaypointTransmitter.java
index 2866b750a28cc32c2913d3c1afee95f05956982c..f0b62f49382eb808dcd6b6b654b99b9f7763c342 100644
--- a/net/minecraft/world/waypoints/WaypointTransmitter.java
+++ b/net/minecraft/world/waypoints/WaypointTransmitter.java
@@ -76,7 +76,8 @@ public interface WaypointTransmitter extends Waypoint {
private final LivingEntity source;
private final Waypoint.Icon icon;
private final ServerPlayer receiver;
- private float lastAngle;
+ private volatile float lastAngle; // Luminol - Restore waypoints
+ private final java.util.UUID sourceUUID; // Luminol - Restore waypoints (prevent UUID mutation)
public EntityAzimuthConnection(final LivingEntity source, final Waypoint.Icon icon, final ServerPlayer receiver) {
this.source = source;
@@ -84,6 +85,7 @@ public interface WaypointTransmitter extends Waypoint {
this.receiver = receiver;
Vec3 direction = receiver.position().subtract(source.position()).rotateClockwise90();
this.lastAngle = (float)Mth.atan2(direction.z(), direction.x());
+ this.sourceUUID = this.source.getUUID(); // Luminol - Restore waypoints (prevent UUID mutation)
}
@Override
@@ -95,16 +97,16 @@ public interface WaypointTransmitter extends Waypoint {
@Override
public void connect() {
- this.receiver.connection.send(ClientboundTrackedWaypointPacket.addWaypointAzimuth(this.source.getUUID(), this.icon, this.lastAngle));
+ this.receiver.connection.send(ClientboundTrackedWaypointPacket.addWaypointAzimuth(this.sourceUUID, this.icon, this.lastAngle)); // Luminol - Restore waypoints (prevent UUID mutation)
}
@Override
public void disconnect() {
- this.receiver.connection.send(ClientboundTrackedWaypointPacket.removeWaypoint(this.source.getUUID()));
+ this.receiver.connection.send(ClientboundTrackedWaypointPacket.removeWaypoint(this.sourceUUID)); // Luminol - Restore waypoints (prevent UUID mutation)
}
@Override
- public void update() {
+ public synchronized void update() { // Luminol - Restore waypoints
Vec3 direction = this.receiver.position().subtract(this.source.position()).rotateClockwise90();
float currentAngle = (float)Mth.atan2(direction.z(), direction.x());
if (Mth.abs(currentAngle - this.lastAngle) > 0.008726646F) {
@@ -118,27 +120,29 @@ public interface WaypointTransmitter extends Waypoint {
private final LivingEntity source;
private final Waypoint.Icon icon;
private final ServerPlayer receiver;
- private BlockPos lastPosition;
+ private volatile BlockPos lastPosition; // Luminol - Restore waypoints
+ private final java.util.UUID sourceUUID; // Luminol - Restore waypoints (prevent UUID mutation)
public EntityBlockConnection(final LivingEntity source, final Waypoint.Icon icon, final ServerPlayer receiver) {
this.source = source;
this.receiver = receiver;
this.icon = icon;
this.lastPosition = source.blockPosition();
+ this.sourceUUID = this.source.getUUID(); // Luminol - Restore waypoints (prevent UUID mutation)
}
@Override
public void connect() {
- this.receiver.connection.send(ClientboundTrackedWaypointPacket.addWaypointPosition(this.source.getUUID(), this.icon, this.lastPosition));
+ this.receiver.connection.send(ClientboundTrackedWaypointPacket.addWaypointPosition(this.sourceUUID, this.icon, this.lastPosition)); // Luminol - Restore waypoints (prevent UUID mutation)
}
@Override
public void disconnect() {
- this.receiver.connection.send(ClientboundTrackedWaypointPacket.removeWaypoint(this.source.getUUID()));
+ this.receiver.connection.send(ClientboundTrackedWaypointPacket.removeWaypoint(this.sourceUUID)); // Luminol - Restore waypoints (prevent UUID mutation)
}
@Override
- public void update() {
+ public synchronized void update() { // Luminol - Restore waypoints
BlockPos currentPosition = this.source.blockPosition();
if (currentPosition.distManhattan(this.lastPosition) > 0) {
this.receiver.connection.send(ClientboundTrackedWaypointPacket.updateWaypointPosition(this.source.getUUID(), this.icon, currentPosition));
@@ -161,13 +165,15 @@ public interface WaypointTransmitter extends Waypoint {
private final LivingEntity source;
private final Waypoint.Icon icon;
private final ServerPlayer receiver;
- private ChunkPos lastPosition;
+ private volatile ChunkPos lastPosition; // Luminol - Restore waypoints
+ private final java.util.UUID sourceUUID; // Luminol - Restore waypoints (prevent UUID mutation)
public EntityChunkConnection(final LivingEntity source, final Waypoint.Icon icon, final ServerPlayer receiver) {
this.source = source;
this.icon = icon;
this.receiver = receiver;
this.lastPosition = source.chunkPosition();
+ this.sourceUUID = this.source.getUUID(); // Luminol - Restore waypoints (prevent UUID mutation)
}
@Override
@@ -177,16 +183,16 @@ public interface WaypointTransmitter extends Waypoint {
@Override
public void connect() {
- this.receiver.connection.send(ClientboundTrackedWaypointPacket.addWaypointChunk(this.source.getUUID(), this.icon, this.lastPosition));
+ this.receiver.connection.send(ClientboundTrackedWaypointPacket.addWaypointChunk(this.sourceUUID, this.icon, this.lastPosition)); // Luminol - Restore waypoints (prevent UUID mutable)
}
@Override
public void disconnect() {
- this.receiver.connection.send(ClientboundTrackedWaypointPacket.removeWaypoint(this.source.getUUID()));
+ this.receiver.connection.send(ClientboundTrackedWaypointPacket.removeWaypoint(this.sourceUUID)); // Luminol - Restore waypoints (prevent UUID mutable)
}
@Override
- public void update() {
+ public synchronized void update() { // Luminol - Restore waypoints
ChunkPos currentPosition = this.source.chunkPosition();
if (currentPosition.getChessboardDistance(this.lastPosition) > 0) {
this.receiver.connection.send(ClientboundTrackedWaypointPacket.updateWaypointChunk(this.source.getUUID(), this.icon, currentPosition));
@@ -0,0 +1,423 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Sun, 28 Jun 2026 20:39:22 +0800
Subject: [PATCH] Leaf: Secure seed and matter seed command
Co-authored by: Apehum <apehumchik@gmail.com>
As part of: Leaf (https://github.com/Winds-Studio/Leaf/blob/7f3e240bbe0970683c40279a7a65f0fde47503b6/leaf-server/paper-patches/features/0019-Matter-Secure-Seed.patch and https://github.com/Winds-Studio/Leaf/blob/7f3e240bbe0970683c40279a7a65f0fde47503b6/leaf-server/minecraft-patches/features/0050-Matter-Secure-Seed-command.patch)
Licensed under: GPL-3.0 (https://github.com/Winds-Studio/Leaf/blob/7f3e240bbe0970683c40279a7a65f0fde47503b6/licenses/GPL-3.0.txt)
diff --git a/net/minecraft/server/commands/SeedCommand.java b/net/minecraft/server/commands/SeedCommand.java
index 86ee3b3ae028597576b9549bc4954dfcbaa6732c..2042dc0c7046dec25ff46ddd1e5bd45c3358040b 100644
--- a/net/minecraft/server/commands/SeedCommand.java
+++ b/net/minecraft/server/commands/SeedCommand.java
@@ -13,6 +13,15 @@ public class SeedCommand {
long seed = c.getSource().getLevel().getSeed();
Component seedText = ComponentUtils.copyOnClickText(String.valueOf(seed));
c.getSource().sendSuccess(() -> Component.translatable("commands.seed.success", seedText), false);
+ // Leaf start - Matter - SecureSeed Command
+ if (me.earthme.luminol.config.modules.function.SecureSeedConfig.enabled) {
+ su.plo.matter.Globals.setupGlobals(c.getSource().getLevel());
+ String seedStr = su.plo.matter.Globals.seedToString(su.plo.matter.Globals.worldSeed);
+ Component featureSeedComponent = ComponentUtils.copyOnClickText(seedStr);
+
+ c.getSource().sendSuccess(() -> Component.translatable(("Feature seed: %s"), featureSeedComponent), false);
+ }
+ // Leaf end - Matter - SecureSeed Command
return (int)seed;
})
);
diff --git a/net/minecraft/server/dedicated/DedicatedServerProperties.java b/net/minecraft/server/dedicated/DedicatedServerProperties.java
index 639a5400499266f83cf8b7c1251483cd8fb699a4..8c0bee7856c8ac62880038770c2f430ecd6c44d9 100644
--- a/net/minecraft/server/dedicated/DedicatedServerProperties.java
+++ b/net/minecraft/server/dedicated/DedicatedServerProperties.java
@@ -139,7 +139,17 @@ public class DedicatedServerProperties extends Settings<DedicatedServerPropertie
String levelSeed = this.get("level-seed", "");
boolean generateStructures = this.get("generate-structures", true);
long seed = WorldOptions.parseSeed(levelSeed).orElse(WorldOptions.randomSeed());
- this.worldOptions = new WorldOptions(seed, generateStructures, false);
+ // Leaf start - Matter - Secure Seed
+ if (me.earthme.luminol.config.modules.function.SecureSeedConfig.enabled) {
+ String featureSeedStr = this.get("feature-level-seed", "");
+ long[] featureSeed = su.plo.matter.Globals.parseSeed(featureSeedStr)
+ .orElse(su.plo.matter.Globals.createRandomWorldSeed());
+
+ this.worldOptions = new WorldOptions(seed, featureSeed, generateStructures, false);
+ } else {
+ this.worldOptions = new WorldOptions(seed, generateStructures, false);
+ }
+ // Leaf end - Matter - Secure Seed
this.worldDimensionData = new DedicatedServerProperties.WorldDimensionData(
this.get("generator-settings", s -> GsonHelper.parse(!s.isEmpty() ? s : "{}"), new JsonObject()),
this.get("level-type", v -> v.toLowerCase(Locale.ROOT), WorldPresets.NORMAL.identifier().toString())
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
index 7927769cf9bccb892745f4d1390eb83b2861d1bb..414c21345879f589fb61130180d0ca606e37a454 100644
--- a/net/minecraft/server/level/ServerChunkCache.java
+++ b/net/minecraft/server/level/ServerChunkCache.java
@@ -671,6 +671,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
}
public ChunkGenerator getGenerator() {
+ su.plo.matter.Globals.setupGlobals(level); // Leaf - Matter - Secure Seed
return this.chunkMap.generator();
}
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index a1c3f3e5c7c3f32e0bdead33ebd72c7601295291..85eed61919b3c2ef93943e1c1b50fbcbca6fb319 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -639,6 +639,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
generator = new org.bukkit.craftbukkit.generator.CustomChunkGenerator(this, generator, gen);
}
// CraftBukkit end
+ su.plo.matter.Globals.setupGlobals(this); // Leaf - Matter - Secure Seed
boolean syncWrites = server.forceSynchronousWrites();
DataFixer fixerUpper = server.getFixerUpper();
// Paper - rewrite chunk system
diff --git a/net/minecraft/world/entity/monster/cubemob/Slime.java b/net/minecraft/world/entity/monster/cubemob/Slime.java
index 984ec15bdebcb541cd6ee9014a654ea58988f6f1..da3bbf395ef5c173124fc564d0d7d9c96346436e 100644
--- a/net/minecraft/world/entity/monster/cubemob/Slime.java
+++ b/net/minecraft/world/entity/monster/cubemob/Slime.java
@@ -94,7 +94,12 @@ public class Slime extends AbstractCubeMob implements Enemy {
}
ChunkPos chunkPos = ChunkPos.containing(pos);
- boolean slimeChunk = level.getMinecraftWorld().paperConfig().entities.spawning.allChunksAreSlimeChunks || WorldgenRandom.seedSlimeChunk(chunkPos.x(), chunkPos.z(), worldGenLevel.getSeed(), level.getMinecraftWorld().spigotConfig.slimeSeed).nextInt(10) == 0; // Paper
+ // Leaf start - Matter - Secure Seed
+ boolean isSlimeChunk = me.earthme.luminol.config.modules.function.SecureSeedConfig.enabled
+ ? level.getChunk(chunkPos.x(), chunkPos.z()).isSlimeChunk()
+ : WorldgenRandom.seedSlimeChunk(chunkPos.x(), chunkPos.z(), worldGenLevel.getSeed(), level.getMinecraftWorld().spigotConfig.slimeSeed).nextInt(10) == 0; // Paper
+ boolean slimeChunk = level.getMinecraftWorld().paperConfig().entities.spawning.allChunksAreSlimeChunks || isSlimeChunk;
+ // Leaf end - Matter - Secure Seed
// Paper start - Replace rules for Height in Slime Chunks
final double maxHeightSlimeChunk = level.getMinecraftWorld().paperConfig().entities.spawning.slimeSpawnHeight.slimeChunk.maximum;
if (random.nextInt(10) == 0 && slimeChunk && pos.getY() < maxHeightSlimeChunk) {
diff --git a/net/minecraft/world/level/chunk/ChunkAccess.java b/net/minecraft/world/level/chunk/ChunkAccess.java
index 28f703204afd834cd50335346ef065670d6b37ad..f6cecf0ec1ebf9dd7028877dcadf191d1a2d954c 100644
--- a/net/minecraft/world/level/chunk/ChunkAccess.java
+++ b/net/minecraft/world/level/chunk/ChunkAccess.java
@@ -83,6 +83,10 @@ public abstract class ChunkAccess implements LightChunk, StructureAccess, BiomeM
private static final org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry();
public org.bukkit.craftbukkit.persistence.DirtyCraftPersistentDataContainer persistentDataContainer = new org.bukkit.craftbukkit.persistence.DirtyCraftPersistentDataContainer(ChunkAccess.DATA_TYPE_REGISTRY);
// CraftBukkit end
+ // Leaf start - Matter - Secure Seed
+ private boolean slimeChunk;
+ private boolean hasComputedSlimeChunk;
+ // Leaf end - Matter - Secure Seed
// Paper start - rewrite chunk system
private volatile ca.spottedleaf.moonrise.patches.starlight.light.SWMRNibbleArray[] blockNibbles;
@@ -186,6 +190,17 @@ public abstract class ChunkAccess implements LightChunk, StructureAccess, BiomeM
return GameEventListenerRegistry.NOOP;
}
+ // Leaf start - Matter - Secure Seed
+ public boolean isSlimeChunk() {
+ if (!hasComputedSlimeChunk) {
+ hasComputedSlimeChunk = true;
+ slimeChunk = su.plo.matter.WorldgenCryptoRandom.seedSlimeChunk(chunkPos.x(), chunkPos.z()).nextInt(10) == 0;
+ }
+
+ return slimeChunk;
+ }
+ // Leaf end - Matter - Secure Seed
+
public abstract BlockState getBlockState(final int x, final int y, final int z); // Paper
public @Nullable BlockState setBlockState(final BlockPos pos, final BlockState state) {
diff --git a/net/minecraft/world/level/chunk/ChunkGenerator.java b/net/minecraft/world/level/chunk/ChunkGenerator.java
index b7325170369299a74be35ecc66a6446c301605b8..bac2047025bf133d14438b5f667bb12401b5cdca 100644
--- a/net/minecraft/world/level/chunk/ChunkGenerator.java
+++ b/net/minecraft/world/level/chunk/ChunkGenerator.java
@@ -347,7 +347,11 @@ public abstract class ChunkGenerator {
Map<Integer, List<Structure>> structuresByStep = structuresRegistry.stream()
.collect(Collectors.groupingBy(structure -> structure.step().ordinal()));
List<FeatureSorter.StepFeatureData> featureList = this.featuresPerStep.get();
- WorldgenRandom random = new WorldgenRandom(new XoroshiroRandomSource(RandomSupport.generateUniqueSeed()));
+ // Leaf start - Matter - Secure Seed
+ WorldgenRandom random = me.earthme.luminol.config.modules.function.SecureSeedConfig.enabled
+ ? new su.plo.matter.WorldgenCryptoRandom(origin.getX(), origin.getZ(), su.plo.matter.Globals.Salt.UNDEFINED, 0)
+ : new WorldgenRandom(new XoroshiroRandomSource(RandomSupport.generateUniqueSeed()));
+ // Leaf end - Matter - Secure Seed
long decorationSeed = random.setDecorationSeed(level.getSeed(), origin.getX(), origin.getZ());
Set<Holder<Biome>> possibleBiomes = new ObjectArraySet<>();
ChunkPos.rangeClosed(sectionPos.chunk(), 1).forEach(chunkPos -> {
@@ -571,8 +575,15 @@ public abstract class ChunkGenerator {
} else {
ArrayList<StructureSet.StructureSelectionEntry> options = new ArrayList<>(structures.size());
options.addAll(structures);
- WorldgenRandom random = new WorldgenRandom(new LegacyRandomSource(0L));
- random.setLargeFeatureSeed(state.getLevelSeed(), sourceChunkPos.x(), sourceChunkPos.z());
+ // Leaf start - Matter - Secure Seed
+ WorldgenRandom random;
+ if (me.earthme.luminol.config.modules.function.SecureSeedConfig.enabled) {
+ random = new su.plo.matter.WorldgenCryptoRandom(sourceChunkPos.x(), sourceChunkPos.z(), su.plo.matter.Globals.Salt.GENERATE_FEATURE, 0);
+ } else {
+ random = new WorldgenRandom(new LegacyRandomSource(0L));
+ random.setLargeFeatureSeed(state.getLevelSeed(), sourceChunkPos.x(), sourceChunkPos.z());
+ }
+ // Leaf end - Matter - Secure Seed
int total = 0;
for (StructureSet.StructureSelectionEntry option : options) {
diff --git a/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java b/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java
index 8b4ea2b16da45bcefe743990866e43c94e121825..9d91679a2696c065cd99cd6ce395c0d00c9dd933 100644
--- a/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java
+++ b/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java
@@ -203,14 +203,20 @@ public class ChunkGeneratorStructureState {
List<CompletableFuture<ChunkPos>> tasks = new ArrayList<>(count);
int spread = placement.spread();
HolderSet<Biome> preferredBiomes = placement.preferredBiomes();
- RandomSource random = RandomSource.create();
- // Paper start - Add missing structure set seed configs
+ // Leaf start - Matter - Secure Seed
+ RandomSource random = me.earthme.luminol.config.modules.function.SecureSeedConfig.enabled
+ ? new su.plo.matter.WorldgenCryptoRandom(0, 0, su.plo.matter.Globals.Salt.STRONGHOLDS, 0)
+ :RandomSource.create();
+ // Leaf end - Matter - Secure Seed
+ if (!me.earthme.luminol.config.modules.function.SecureSeedConfig.enabled) {
+ //Paper start - Add missing structure set seed configs
if (this.conf.strongholdSeed != null && structureSet.is(net.minecraft.world.level.levelgen.structure.BuiltinStructureSets.STRONGHOLDS)) {
random.setSeed(this.conf.strongholdSeed);
} else {
// Paper end - Add missing structure set seed configs
random.setSeed(this.concentricRingsSeed);
} // Paper - Add missing structure set seed configs
+ } // Leaf - Matter - Secure Seed
double angle = random.nextDouble() * Math.PI * 2.0;
int positionInCircle = 0;
int circle = 0;
diff --git a/net/minecraft/world/level/chunk/status/ChunkStep.java b/net/minecraft/world/level/chunk/status/ChunkStep.java
index 9208924f54d5024bc50ad4501fbff9eb2a289181..01d87d9c474a5f3e14713579e6a1d1e17d2455ae 100644
--- a/net/minecraft/world/level/chunk/status/ChunkStep.java
+++ b/net/minecraft/world/level/chunk/status/ChunkStep.java
@@ -60,6 +60,7 @@ public final class ChunkStep implements ca.spottedleaf.moonrise.patches.chunk_sy
}
public CompletableFuture<ChunkAccess> apply(final WorldGenContext context, final StaticCache2D<GenerationChunkHolder> cache, final ChunkAccess chunk) {
+ su.plo.matter.Globals.setupGlobals(context.level()); // Leaf - Matter - Secure Seed
if (chunk.getPersistedStatus().isBefore(this.targetStatus)) {
ProfiledDuration profiledDuration = JvmProfiler.INSTANCE.onChunkGenerate(chunk.getPos(), context.level().dimension(), this.targetStatus.getName());
return this.task.doWork(context, this, cache, chunk).thenApply(newCenterChunk -> this.completeChunkGeneration(newCenterChunk, profiledDuration));
diff --git a/net/minecraft/world/level/levelgen/WorldOptions.java b/net/minecraft/world/level/levelgen/WorldOptions.java
index 4ad94af024d81b7dfe910dcee2a6ec7a9b200d56..38495536001442b1591251d7625ccdad4b73caf3 100644
--- a/net/minecraft/world/level/levelgen/WorldOptions.java
+++ b/net/minecraft/world/level/levelgen/WorldOptions.java
@@ -10,8 +10,22 @@ import net.minecraft.util.RandomSource;
import org.apache.commons.lang3.StringUtils;
public class WorldOptions {
+ // Leaf start - Matter - Secure Seed
+ private static final com.google.gson.Gson gson = new com.google.gson.Gson();
+ private static final boolean isSecureSeedEnabled = me.earthme.luminol.config.modules.function.SecureSeedConfig.enabled;
public static final MapCodec<WorldOptions> CODEC = RecordCodecBuilder.mapCodec(
- i -> i.group(
+ // Leaf start - Matter - Secure Seed
+ i -> isSecureSeedEnabled
+ ? i.group(
+ Codec.LONG.fieldOf("seed").stable().forGetter(WorldOptions::seed),
+ Codec.STRING.fieldOf("feature_seed").orElse(gson.toJson(su.plo.matter.Globals.createRandomWorldSeed())).stable().forGetter(WorldOptions::featureSeedSerialize),
+ Codec.BOOL.fieldOf("generate_features").orElse(true).stable().forGetter(WorldOptions::generateStructures),
+ Codec.BOOL.fieldOf("bonus_chest").orElse(false).stable().forGetter(WorldOptions::generateBonusChest),
+ Codec.STRING.lenientOptionalFieldOf("legacy_custom_options").stable().forGetter(worldOptions -> worldOptions.legacyCustomOptions)
+ )
+ .apply(i, i.stable(WorldOptions::new))
+ : i.group(
+ // Leaf end
Codec.LONG.fieldOf("seed").stable().forGetter(WorldOptions::seed),
ExtraCodecs.optionalAlwaysPresentFieldOf(Codec.BOOL, "generate_structures", true).stable().forGetter(WorldOptions::generateStructures),
ExtraCodecs.optionalAlwaysPresentFieldOf(Codec.BOOL, "bonus_chest", false).stable().forGetter(WorldOptions::generateBonusChest),
@@ -19,8 +33,14 @@ public class WorldOptions {
)
.apply(i, i.stable(WorldOptions::new))
);
- public static final WorldOptions DEMO_OPTIONS = new WorldOptions("North Carolina".hashCode(), true, true);
+ // Leaf end - Matter - Secure Seed
+ // Leaf start - Matter - Secure Seed
+ public static final WorldOptions DEMO_OPTIONS = isSecureSeedEnabled
+ ? new WorldOptions((long) "North Carolina".hashCode(), su.plo.matter.Globals.createRandomWorldSeed(), true, true)
+ : new WorldOptions("North Carolina".hashCode(), true, true);
+ // Leaf end - Matter - Secure Seed
private final long seed;
+ private long[] featureSeed = su.plo.matter.Globals.createRandomWorldSeed(); // Leaf - Matter - Secure Seed
private final boolean generateStructures;
private final boolean generateBonusChest;
private final Optional<String> legacyCustomOptions;
@@ -29,14 +49,35 @@ public class WorldOptions {
this(seed, generateStructures, generateBonusChest, Optional.empty());
}
+ // Leaf start - Matter - Secure Seed
+ public WorldOptions(long seed, long[] featureSeed, boolean generateStructures, boolean bonusChest) {
+ this(seed, featureSeed, generateStructures, bonusChest, Optional.empty());
+ }
+
+ private WorldOptions(long seed, String featureSeedJson, boolean generateStructures, boolean bonusChest, Optional<String> legacyCustomOptions) {
+ this(seed, gson.fromJson(featureSeedJson, long[].class), generateStructures, bonusChest, legacyCustomOptions);
+ }
+ // Leaf end - Matter - Secure Seed
+
public static WorldOptions defaultWithRandomSeed() {
- return new WorldOptions(randomSeed(), true, false);
+ // Leaf start - Matter - Secure Seed
+ return isSecureSeedEnabled
+ ? new WorldOptions(randomSeed(), su.plo.matter.Globals.createRandomWorldSeed(), true, false)
+ : new WorldOptions(randomSeed(), true, false);
+ // Leaf end - Matter - Secure Seed
}
public static WorldOptions testWorldWithRandomSeed() {
return new WorldOptions(randomSeed(), false, false);
}
+ // Leaf start - Matter - Secure Seed
+ private WorldOptions(long seed, long[] featureSeed, boolean generateStructures, boolean bonusChest, Optional<String> legacyCustomOptions) {
+ this(seed, generateStructures, bonusChest, legacyCustomOptions);
+ this.featureSeed = featureSeed;
+ }
+ // Leaf end - Matter - Secure Seed
+
private WorldOptions(final long seed, final boolean generateStructures, final boolean generateBonusChest, final Optional<String> legacyCustomOptions) {
this.seed = seed;
this.generateStructures = generateStructures;
@@ -48,6 +89,16 @@ public class WorldOptions {
return this.seed;
}
+ // Leaf start - Matter - Secure Seed
+ public long[] featureSeed() {
+ return this.featureSeed;
+ }
+
+ private String featureSeedSerialize() {
+ return gson.toJson(this.featureSeed);
+ }
+ // Leaf end - Matter - Secure Seed
+
public boolean generateStructures() {
return this.generateStructures;
}
@@ -60,17 +111,25 @@ public class WorldOptions {
return this.legacyCustomOptions.isPresent();
}
+ // Leaf start - Matter - Secure Seed
public WorldOptions withBonusChest(final boolean generateBonusChest) {
- return new WorldOptions(this.seed, this.generateStructures, generateBonusChest, this.legacyCustomOptions);
+ return isSecureSeedEnabled
+ ? new WorldOptions(this.seed, this.featureSeed, this.generateStructures, generateBonusChest, this.legacyCustomOptions)
+ : new WorldOptions(this.seed, this.generateStructures, generateBonusChest, this.legacyCustomOptions);
}
public WorldOptions withStructures(final boolean generateStructures) {
- return new WorldOptions(this.seed, generateStructures, this.generateBonusChest, this.legacyCustomOptions);
+ return isSecureSeedEnabled
+ ? new WorldOptions(this.seed, this.featureSeed, generateStructures, this.generateBonusChest, this.legacyCustomOptions)
+ : new WorldOptions(this.seed, generateStructures, this.generateBonusChest, this.legacyCustomOptions);
}
public WorldOptions withSeed(final OptionalLong seed) {
- return new WorldOptions(seed.orElse(randomSeed()), this.generateStructures, this.generateBonusChest, this.legacyCustomOptions);
+ return isSecureSeedEnabled
+ ? new WorldOptions(seed.orElse(randomSeed()), su.plo.matter.Globals.createRandomWorldSeed(), this.generateStructures, this.generateBonusChest, this.legacyCustomOptions)
+ : new WorldOptions(seed.orElse(randomSeed()), this.generateStructures, this.generateBonusChest, this.legacyCustomOptions);
}
+ // Leaf end - Matter - Secure Seed
public static OptionalLong parseSeed(String seedString) {
seedString = seedString.trim();
diff --git a/net/minecraft/world/level/levelgen/feature/GeodeFeature.java b/net/minecraft/world/level/levelgen/feature/GeodeFeature.java
index baed0479a4552676844989868ee4c37ffa344def..55abc8e32d9dabbb5699d6eff109f4067c452098 100644
--- a/net/minecraft/world/level/levelgen/feature/GeodeFeature.java
+++ b/net/minecraft/world/level/levelgen/feature/GeodeFeature.java
@@ -43,7 +43,11 @@ public class GeodeFeature extends Feature<GeodeConfiguration> {
int maxGenOffset = config.maxGenOffset();
List<Pair<BlockPos, Integer>> points = Lists.newLinkedList();
int numPoints = config.distributionPoints().sample(random);
- WorldgenRandom random1 = new WorldgenRandom(new LegacyRandomSource(level.getSeed()));
+ // Leaf start - Matter - Secure Seed
+ WorldgenRandom random1 = me.earthme.luminol.config.modules.function.SecureSeedConfig.enabled
+ ? new su.plo.matter.WorldgenCryptoRandom(0, 0, su.plo.matter.Globals.Salt.GEODE_FEATURE, 0)
+ : new WorldgenRandom(new LegacyRandomSource(level.getSeed()));
+ // Leaf end - Matter - Secure Seed
NormalNoise noise = NormalNoise.create(random1, -4, 1.0);
List<BlockPos> crackPoints = Lists.newLinkedList();
double crackSizeAdjustment = (double)numPoints / config.outerWallDistance().maxInclusive();
diff --git a/net/minecraft/world/level/levelgen/structure/Structure.java b/net/minecraft/world/level/levelgen/structure/Structure.java
index f1e13ede1f7345089ece8d8ef391f7fd5e55c445..a3a1ab1b610fd0f75c4d5cc96ab921e47d0cfea7 100644
--- a/net/minecraft/world/level/levelgen/structure/Structure.java
+++ b/net/minecraft/world/level/levelgen/structure/Structure.java
@@ -248,6 +248,11 @@ public abstract class Structure {
}
private static WorldgenRandom makeRandom(final long seed, final ChunkPos chunkPos) {
+ // Leaf start - Matter - Secure Seed
+ if (me.earthme.luminol.config.modules.function.SecureSeedConfig.enabled) {
+ return new su.plo.matter.WorldgenCryptoRandom(chunkPos.x(), chunkPos.z(), su.plo.matter.Globals.Salt.GENERATE_FEATURE, seed);
+ }
+ // Leaf end - Matter - Secure Seed
WorldgenRandom random = new WorldgenRandom(new LegacyRandomSource(0L));
random.setLargeFeatureSeed(seed, chunkPos.x(), chunkPos.z());
return random;
diff --git a/net/minecraft/world/level/levelgen/structure/placement/RandomSpreadStructurePlacement.java b/net/minecraft/world/level/levelgen/structure/placement/RandomSpreadStructurePlacement.java
index 1310b2ca994b8f6d3d92bdc676fe44de3619cc09..8fcb08d7ffc90d2d46375faecd17774483a4560a 100644
--- a/net/minecraft/world/level/levelgen/structure/placement/RandomSpreadStructurePlacement.java
+++ b/net/minecraft/world/level/levelgen/structure/placement/RandomSpreadStructurePlacement.java
@@ -67,8 +67,15 @@ public class RandomSpreadStructurePlacement extends StructurePlacement {
public ChunkPos getPotentialStructureChunk(final long seed, final int sourceX, final int sourceZ) {
int spacedGridX = Math.floorDiv(sourceX, this.spacing);
int spacedGridZ = Math.floorDiv(sourceZ, this.spacing);
- WorldgenRandom random = new WorldgenRandom(new LegacyRandomSource(0L));
- random.setLargeFeatureWithSalt(seed, spacedGridX, spacedGridZ, this.salt());
+ // Leaf start - Matter - Secure Seed
+ WorldgenRandom random;
+ if (me.earthme.luminol.config.modules.function.SecureSeedConfig.enabled) {
+ random = new su.plo.matter.WorldgenCryptoRandom(spacedGridX, spacedGridZ, su.plo.matter.Globals.Salt.POTENTIONAL_FEATURE, this.salt);
+ } else {
+ random = new WorldgenRandom(new LegacyRandomSource(0L));
+ random.setLargeFeatureWithSalt(seed, spacedGridX, spacedGridZ, this.salt());
+ }
+ // Leaf end - Matter - Secure Seed
int limit = this.spacing - this.separation;
int spreadX = this.spreadType.evaluate(random, limit);
int spreadZ = this.spreadType.evaluate(random, limit);
diff --git a/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java b/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java
index 646df0237a5a2863f75e6c5f9dfe9ddf512c540f..1a5690b793ad6c30690fc9659c7f1746c55e5997 100644
--- a/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java
+++ b/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java
@@ -137,8 +137,16 @@ public abstract class StructurePlacement {
}
private static boolean legacyArbitrarySaltProbabilityReducer(final long seed, final int salt, final int sourceX, final int sourceZ, final float probability, final @org.jspecify.annotations.Nullable Integer saltOverride) { // Paper - Add missing structure set seed configs
- WorldgenRandom random = new WorldgenRandom(new LegacyRandomSource(0L));
- random.setLargeFeatureWithSalt(seed, sourceX, sourceZ, saltOverride != null ? saltOverride : HIGHLY_ARBITRARY_RANDOM_SALT); // Paper - Add missing structure set seed configs
+ // Leaf start - Matter - Secure Seed
+ WorldgenRandom random;
+ if (me.earthme.luminol.config.modules.function.SecureSeedConfig.enabled) {
+ random = new su.plo.matter.WorldgenCryptoRandom(sourceX, sourceZ, su.plo.matter.Globals.Salt.UNDEFINED, saltOverride != null ? saltOverride : HIGHLY_ARBITRARY_RANDOM_SALT);
+ } else {
+ random = new WorldgenRandom(new LegacyRandomSource(0L));
+ random.setLargeFeatureWithSalt(seed, sourceX, sourceZ, saltOverride != null ? saltOverride : HIGHLY_ARBITRARY_RANDOM_SALT); // Paper - Add missing structure set seed configs
+ }
+ // Leaf end - Matter - Secure Seed
+
return random.nextFloat() < probability;
}
diff --git a/net/minecraft/world/level/levelgen/structure/pools/JigsawPlacement.java b/net/minecraft/world/level/levelgen/structure/pools/JigsawPlacement.java
index b92da7acae0789d955a9f71072b95ed030b879e2..484977ebf2cd7b75b228ebd7246fe602c2a271d1 100644
--- a/net/minecraft/world/level/levelgen/structure/pools/JigsawPlacement.java
+++ b/net/minecraft/world/level/levelgen/structure/pools/JigsawPlacement.java
@@ -64,7 +64,11 @@ public class JigsawPlacement {
ChunkGenerator chunkGenerator = context.chunkGenerator();
StructureTemplateManager structureTemplateManager = context.structureTemplateManager();
LevelHeightAccessor heightAccessor = context.heightAccessor();
- WorldgenRandom random = context.random();
+ // Leaf start - Matter - Secure Seed
+ WorldgenRandom random = me.earthme.luminol.config.modules.function.SecureSeedConfig.enabled
+ ? new su.plo.matter.WorldgenCryptoRandom(context.chunkPos().x(), context.chunkPos().z(), su.plo.matter.Globals.Salt.JIGSAW_PLACEMENT, 0)
+ : context.random();
+ // Leaf end - Matter - Secure Seed
Registry<StructureTemplatePool> pools = registryAccess.lookupOrThrow(Registries.TEMPLATE_POOL);
Rotation centerRotation = Rotation.getRandom(random);
StructureTemplatePool centerPool = startPool.unwrapKey()
@@ -0,0 +1,70 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Libalpm64 <libalpm@proton.me>
Date: Mon, 2 Mar 2026 00:00:00 +0000
Subject: [PATCH] Add secure seed V2 with Blake3
diff --git a/net/minecraft/world/level/levelgen/RandomState.java b/net/minecraft/world/level/levelgen/RandomState.java
index 7b74440fbbe79c018ad59d24b99da0c6d4a9d007..208d4b93ee47083bd9069aec710c6db27044203c 100644
--- a/net/minecraft/world/level/levelgen/RandomState.java
+++ b/net/minecraft/world/level/levelgen/RandomState.java
@@ -33,10 +33,28 @@ public final class RandomState {
}
private RandomState(final NoiseGeneratorSettings settings, final HolderGetter<NormalNoise.NoiseParameters> noises, final long seed) {
- this.random = settings.getRandomSource().newInstance(seed).forkPositional();
+ // this.random = settings.getRandomSource().newInstance(seed).forkPositional(); // Luminol - Add secure seed V2 with Blake3
+ // Luminol start - Add secure seed V2 with Blake3
+ final long[] secureWorldSeed = (me.earthme.luminol.config.modules.function.SecureSeedConfig.enabled && me.earthme.luminol.config.modules.function.SecureSeedConfig.version == 2)
+ ? su.plo.matter.HashingV2.expandLevelSeedTo1024Bits(seed)
+ : null;
+
+ long terrainSeed = (me.earthme.luminol.config.modules.function.SecureSeedConfig.enabled && me.earthme.luminol.config.modules.function.SecureSeedConfig.version == 2)
+ ? su.plo.matter.HashingV2.getTerrainSeed(secureWorldSeed, su.plo.matter.HashingV2.TerrainType.BASE_TERRAIN)
+ : seed;
+ long aquiferSeed = (me.earthme.luminol.config.modules.function.SecureSeedConfig.enabled && me.earthme.luminol.config.modules.function.SecureSeedConfig.version == 2)
+ ? su.plo.matter.HashingV2.getTerrainSeed(secureWorldSeed, su.plo.matter.HashingV2.TerrainType.AQUIFER)
+ : seed;
+ long oreSeed = (me.earthme.luminol.config.modules.function.SecureSeedConfig.enabled && me.earthme.luminol.config.modules.function.SecureSeedConfig.version == 2)
+ ? su.plo.matter.HashingV2.getTerrainSeed(secureWorldSeed, su.plo.matter.HashingV2.TerrainType.ORE)
+ : seed;
+ // Luminol end
+ this.random = settings.getRandomSource().newInstance(terrainSeed).forkPositional(); // Luminol - Add secure seed V2 with Blake3
this.noises = noises;
- this.aquiferRandom = this.random.fromHashOf(Identifier.withDefaultNamespace("aquifer")).forkPositional();
- this.oreRandom = this.random.fromHashOf(Identifier.withDefaultNamespace("ore")).forkPositional();
+ //this.aquiferRandom = this.random.fromHashOf(Identifier.withDefaultNamespace("aquifer")).forkPositional(); // Luminol - Add secure seed V2 with Blake3
+ //this.oreRandom = this.random.fromHashOf(Identifier.withDefaultNamespace("ore")).forkPositional(); // Luminol - Add secure seed V2 with Blake3
+ this.aquiferRandom = settings.getRandomSource().newInstance(aquiferSeed).forkPositional(); // Luminol - Add secure seed V2 with Blake3
+ this.oreRandom = settings.getRandomSource().newInstance(oreSeed).forkPositional(); // Luminol - Add secure seed V2 with Blake3
this.noiseIntances = new ConcurrentHashMap<>();
this.positionalRandoms = new ConcurrentHashMap<>();
this.surfaceSystem = new SurfaceSystem(this, settings.defaultBlock(), settings.seaLevel(), this.random);
@@ -46,6 +64,12 @@ public final class RandomState {
private final Map<DensityFunction, DensityFunction> wrapped = new HashMap<>();
private RandomSource newLegacyInstance(final long seedOffset) {
+ // Luminol start - Add secure seed V2 with Blake3
+ if (me.earthme.luminol.config.modules.function.SecureSeedConfig.enabled && me.earthme.luminol.config.modules.function.SecureSeedConfig.version == 2) {
+ long climateSeed = su.plo.matter.HashingV2.getTerrainSeed(secureWorldSeed, su.plo.matter.HashingV2.TerrainType.CLIMATE);
+ return new su.plo.matter.WorldgenCryptoRandom(0, 0, su.plo.matter.Globals.Salt.UNDEFINED, climateSeed + seed);
+ }
+ // Luminol end
return new LegacyRandomSource(seed + seedOffset);
}
@@ -71,7 +95,13 @@ public final class RandomState {
: RandomState.this.random.fromHashOf(Identifier.withDefaultNamespace("terrain"));
return noise.withNewRandom(terrainRandom);
} else {
- return function instanceof DensityFunctions.EndIslandDensityFunction ? new DensityFunctions.EndIslandDensityFunction(seed) : function;
+ return function instanceof DensityFunctions.EndIslandDensityFunction //? new DensityFunctions.EndIslandDensityFunction(seed)// Luminol - Add secure seed V2 with Blake3
+ // Luminol start - Add secure seed V2 with Blake3
+ ? new DensityFunctions.EndIslandDensityFunction((me.earthme.luminol.config.modules.function.SecureSeedConfig.enabled && me.earthme.luminol.config.modules.function.SecureSeedConfig.version == 2)
+ ? su.plo.matter.HashingV2.getTerrainSeed(secureWorldSeed, su.plo.matter.HashingV2.TerrainType.SURFACE)
+ : seed)
+ // Luminol end
+ : function;
}
}
@@ -0,0 +1,34 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Sat, 18 Apr 2026 12:27:21 +0800
Subject: [PATCH] Leaf: Replace brain maps with optimized collection
Co-authored by: HaHaWTH <102713261+HaHaWTH@users.noreply.github.com>
As part of: Leaf (https://github.com/Winds-Studio/Leaf/blob/7f3e240bbe0970683c40279a7a65f0fde47503b6/leaf-server/minecraft-patches/features/0070-Replace-brain-maps-with-optimized-collection.patch)
Licensed under: MIT (https://github.com/Winds-Studio/Leaf/blob/7f3e240bbe0970683c40279a7a65f0fde47503b6/licenses/MIT.txt)
diff --git a/net/minecraft/world/entity/ai/Brain.java b/net/minecraft/world/entity/ai/Brain.java
index 27894e2935e2afee8190708def4be3b00f922f64..71a11238835b1090c283d03cc30e09b8a65a9762 100644
--- a/net/minecraft/world/entity/ai/Brain.java
+++ b/net/minecraft/world/entity/ai/Brain.java
@@ -37,14 +37,14 @@ import org.jspecify.annotations.Nullable;
public class Brain<E extends LivingEntity> {
private static final int SCHEDULE_UPDATE_DELAY = 20;
- private final Map<MemoryModuleType<?>, MemorySlot<?>> memories = Maps.newHashMap();
- private final Map<SensorType<? extends Sensor<? super E>>, Sensor<? super E>> sensors = Maps.newLinkedHashMap();
+ private final Map<MemoryModuleType<?>, MemorySlot<?>> memories = new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(); // Leaf - Replace brain maps with optimized collection
+ private final Map<SensorType<? extends Sensor<? super E>>, Sensor<? super E>> sensors = new it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap<>(); // Leaf - Replace brain maps with optimized collection
private final Map<Integer, Map<Activity, Set<BehaviorControl<? super E>>>> availableBehaviorsByPriority = Maps.newTreeMap();
private @Nullable EnvironmentAttribute<Activity> schedule;
- private final Map<Activity, Set<Pair<MemoryModuleType<?>, MemoryStatus>>> activityRequirements = Maps.newHashMap();
- private final Map<Activity, Set<MemoryModuleType<?>>> activityMemoriesToEraseWhenStopped = Maps.newHashMap();
- private Set<Activity> coreActivities = Sets.newHashSet();
- private final Set<Activity> activeActivities = Sets.newHashSet();
+ private final Map<Activity, Set<Pair<MemoryModuleType<?>, MemoryStatus>>> activityRequirements = new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(); // Leaf - Replace brain maps with optimized collection
+ private final Map<Activity, Set<MemoryModuleType<?>>> activityMemoriesToEraseWhenStopped = new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(); // Leaf - Replace brain maps with optimized collection
+ private Set<Activity> coreActivities = new it.unimi.dsi.fastutil.objects.ObjectOpenHashSet<>(); // Leaf - Replace brain maps with optimized collection
+ private final Set<Activity> activeActivities = new it.unimi.dsi.fastutil.objects.ObjectOpenHashSet<>(); // Leaf - Replace brain maps with optimized collection
private Activity defaultActivity = Activity.IDLE;
private long lastScheduleUpdate = -9999L;
@@ -0,0 +1,54 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Sun, 28 Jun 2026 20:51:53 +0800
Subject: [PATCH] Leaf: Remove useless creating stats json bases on player name
logic
Co-authored by: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com>
As part of: Leaf (https://github.com/Winds-Studio/Leaf/blob/7f3e240bbe0970683c40279a7a65f0fde47503b6/leaf-server/minecraft-patches/features/0043-Remove-useless-creating-stats-json-bases-on-player-n.patch)
Licensed under: MIT (https://github.com/Winds-Studio/Leaf/blob/7f3e240bbe0970683c40279a7a65f0fde47503b6/licenses/MIT.txt)
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
index 53449af103c1774f68cb0c296d47e7032e4e3843..a8f051e6aff31a723a3ba59842fac3a43f4911fe 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
@@ -1294,22 +1294,26 @@ public abstract class PlayerList {
Path uuidStatsFile = statFolder.resolve(gameProfile.id() + ".json");
if (Files.exists(uuidStatsFile)) {
return uuidStatsFile;
- }
-
- String playerNameStatsFile = gameProfile.name() + ".json";
- if (FileUtil.isValidPathSegment(playerNameStatsFile)) {
- Path playerNameStatsPath = statFolder.resolve(playerNameStatsFile);
- if (Files.isRegularFile(playerNameStatsPath)) {
- try {
- return Files.move(playerNameStatsPath, uuidStatsFile);
- } catch (IOException e) {
- LOGGER.warn("Failed to copy file {} to {}", playerNameStatsFile, uuidStatsFile);
- return playerNameStatsPath;
+ } else {
+ // Leaf start - Remove useless creating stats json bases on player name logic
+ /*
+ String playerNameStatsFile = gameProfile.name() + ".json";
+ if (FileUtil.isValidPathSegment(playerNameStatsFile)) {
+ Path playerNameStatsPath = statFolder.resolve(playerNameStatsFile);
+ if (Files.isRegularFile(playerNameStatsPath)) {
+ try {
+ return Files.move(playerNameStatsPath, uuidStatsFile);
+ } catch (IOException e) {
+ LOGGER.warn("Failed to copy file {} to {}", playerNameStatsFile, uuidStatsFile);
+ return playerNameStatsPath;
+ }
}
}
- }
+ */
+ // Leaf end - Remove useless creating stats json bases on player name logic
- return uuidStatsFile;
+ return uuidStatsFile;
+ }
}
public PlayerAdvancements getPlayerAdvancements(final ServerPlayer player) {
@@ -0,0 +1,36 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Sat, 2 May 2026 22:26:57 +0800
Subject: [PATCH] Leaf Optimize PatchedDataComponentMap equals
This is a part of Leaf(https://github.com/Winds-Studio/Leaf/blob/db41340915c7768d726342fb29d16bc428081074/leaf-server/minecraft-patches/features/0300-Optimize-PatchedDataComponentMap-equals.patch)
Original author: HaHaWTH <102713261+HaHaWTH@users.noreply.github.com>
Original license: https://github.com/Winds-Studio/Leaf/blob/db41340915c7768d726342fb29d16bc428081074/LICENSE.md
diff --git a/net/minecraft/core/component/PatchedDataComponentMap.java b/net/minecraft/core/component/PatchedDataComponentMap.java
index 08ea8107fc8cbec3742278ab5cf03ef9048720f9..217e4fb33d2707c5e50c7ec2dd2aaee410154254 100644
--- a/net/minecraft/core/component/PatchedDataComponentMap.java
+++ b/net/minecraft/core/component/PatchedDataComponentMap.java
@@ -221,7 +221,19 @@ public final class PatchedDataComponentMap implements DataComponentMap {
@Override
public boolean equals(final Object obj) {
- return this == obj || obj instanceof PatchedDataComponentMap otherMap && this.prototype.equals(otherMap.prototype) && this.patch.equals(otherMap.patch);
+ // return this == obj || obj instanceof PatchedDataComponentMap otherMap && this.prototype.equals(otherMap.prototype) && this.patch.equals(otherMap.patch); // Leaf - Optimize PatchedDataComponentMap equals
+ // Leaf start - Optimize PatchedDataComponentMap equals
+ if (this == obj) return true;
+ if (!(obj instanceof PatchedDataComponentMap that)) return false;
+ if (this.patch.size() != that.patch.size()) {
+ return false;
+ }
+ if (!this.prototype.equals(that.prototype)) {
+ return false;
+ }
+ if (this.patch == that.patch) return true;
+ return this.patch.equals(that.patch);
+ // Leaf end - Optimize PatchedDataComponentMap equals
}
@Override
@@ -0,0 +1,53 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Mon, 4 May 2026 13:44:05 +0800
Subject: [PATCH] Leaf Better checking for useless move packets
A part of leaf(https://github.com/Winds-Studio/Leaf/blob/b794fca6080c47ff305b6d065a579171b1fe1e98/leaf-server/minecraft-patches/features/0018-Better-checking-for-useless-move-packets.patch)
Original license: https://github.com/Winds-Studio/Leaf/blob/b794fca6080c47ff305b6d065a579171b1fe1e98/LICENSE.md
diff --git a/net/minecraft/server/level/ServerEntity.java b/net/minecraft/server/level/ServerEntity.java
index 60af82925d2f7e605bf7340cf01f1a5668f4d725..2e71a1cbabf3405e3e4fd5349a1784d895e7e60c 100644
--- a/net/minecraft/server/level/ServerEntity.java
+++ b/net/minecraft/server/level/ServerEntity.java
@@ -186,18 +186,35 @@ public class ServerEntity {
packet = ClientboundEntityPositionSyncPacket.of(this.entity);
sentPosition = true;
sentRotation = true;
- } else if ((!pos || !shouldSendRotation) && !(this.entity instanceof AbstractArrow)) {
+ /*} else if ((!pos || !shouldSendRotation) && !(this.entity instanceof AbstractArrow)) { // Gale - Airplane - better checking for useless move packets
if (pos) {
packet = new ClientboundMoveEntityPacket.Pos(this.entity.getId(), (short)xa, (short)ya, (short)za, this.entity.onGround());
sentPosition = true;
} else if (shouldSendRotation) {
packet = new ClientboundMoveEntityPacket.Rot(this.entity.getId(), yRotn, xRotn, this.entity.onGround());
sentRotation = true;
- }
+ }*/ // Gale - Airplane - better checking for useless move packets
} else {
- packet = new ClientboundMoveEntityPacket.PosRot(this.entity.getId(), (short)xa, (short)ya, (short)za, yRotn, xRotn, this.entity.onGround());
+ /*packet = new ClientboundMoveEntityPacket.PosRot(this.entity.getId(), (short)xa, (short)ya, (short)za, yRotn, xRotn, this.entity.onGround()); // Gale - Airplane - better checking for useless move packets
sentPosition = true;
- sentRotation = true;
+ sentRotation = true;*/ // Gale - Airplane - better checking for useless move packets
+ // Gale start - Airplane - better checking for useless move packets
+ if (pos || shouldSendRotation || this.entity instanceof AbstractArrow) {
+ if ((!pos || !shouldSendRotation) && !(this.entity instanceof AbstractArrow)) {
+ if (pos) {
+ packet = new ClientboundMoveEntityPacket.Pos(this.entity.getId(), (short) xa, (short) ya, (short) za, this.entity.onGround());
+ sentPosition = true;
+ } else if (shouldSendRotation) {
+ packet = new ClientboundMoveEntityPacket.Rot(this.entity.getId(), yRotn, xRotn, this.entity.onGround());
+ sentRotation = true;
+ }
+ } else {
+ packet = new ClientboundMoveEntityPacket.PosRot(this.entity.getId(), (short) xa, (short) ya, (short) za, yRotn, xRotn, this.entity.onGround());
+ sentPosition = true;
+ sentRotation = true;
+ }
+ }
+ // Gale end - Airplane - better checking for useless move packets
}
if (this.entity.needsSync || this.trackDelta || this.entity instanceof LivingEntity livingEntity && livingEntity.isFallFlying()) {
@@ -0,0 +1,70 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Sun, 17 May 2026 10:10:21 +0800
Subject: [PATCH] Leaf fast bit radix sort
This is a part of Leaf(https://github.com/Winds-Studio/Leaf/blob/d0ed97cf45dfa94f686ef32f0eea1ec7323f78d5/leaf-server/minecraft-patches/features/0284-fast-bit-radix-sort.patch)
Original license: https://github.com/Winds-Studio/Leaf/blob/d0ed97cf45dfa94f686ef32f0eea1ec7323f78d5/LICENSE.md
diff --git a/io/papermc/paper/threadedregions/RegionizedWorldData.java b/io/papermc/paper/threadedregions/RegionizedWorldData.java
index 2d5d4170e1f45ae9113c37623b4066861dc79d64..48fea9051efe038a823d7c2c9ae3bf28078f2fcc 100644
--- a/io/papermc/paper/threadedregions/RegionizedWorldData.java
+++ b/io/papermc/paper/threadedregions/RegionizedWorldData.java
@@ -457,6 +457,7 @@ public final class RegionizedWorldData {
public final PathTypeCache pathTypesByPosCache = new PathTypeCache();
public final List<LevelChunk> temporaryChunkTickList = new java.util.ArrayList<>();
public final Set<ChunkHolder> chunkHoldersToBroadcast = new ReferenceLinkedOpenHashSet<>();
+ public final org.dreeam.leaf.util.FastBitRadixSort radixBitSorter = new org.dreeam.leaf.util.FastBitRadixSort();
// not transient
public java.util.ArrayDeque<net.minecraft.world.level.block.RedstoneTorchBlock.Toggle> redstoneUpdateInfos;
diff --git a/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java b/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java
index 8aeea55380462421cde43afb4b2fb0ce44b65195..73247b46d778dcb9b3be742e4e4c4c037e3ac0e5 100644
--- a/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java
+++ b/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java
@@ -15,6 +15,7 @@ public class NearestItemSensor extends Sensor<Mob> {
private static final long XZ_RANGE = 32L;
private static final long Y_RANGE = 16L;
public static final int MAX_DISTANCE_TO_WANTED_ITEM = 32;
+ private static final double MAX_DIST_SQ = (double) MAX_DISTANCE_TO_WANTED_ITEM * MAX_DISTANCE_TO_WANTED_ITEM; // Leaf - quick sort
@Override
public Set<MemoryModuleType<?>> requires() {
@@ -24,8 +25,16 @@ public class NearestItemSensor extends Sensor<Mob> {
@Override
protected void doTick(final ServerLevel level, final Mob body) {
Brain<?> brain = body.getBrain();
- List<ItemEntity> items = level.getEntitiesOfClass(ItemEntity.class, body.getBoundingBox().inflate(32.0, 16.0, 32.0), item -> item.closerThan(body, MAX_DISTANCE_TO_WANTED_ITEM) && body.wantsToPickUp(level, item.getItem())); // Paper - Perf: Move predicate into getEntities
- items.sort(Comparator.comparingDouble(body::distanceToSqr));
+ // Leaf start - fast bit radix sort
+ net.minecraft.core.Position pos = body.position();
+ double x = pos.x();
+ double y = pos.y();
+ double z = pos.z();
+ net.minecraft.world.phys.AABB boundingBox = body.getBoundingBox().inflate(32.0, 16.0, 32.0);
+ it.unimi.dsi.fastutil.objects.ObjectArrayList<ItemEntity> items = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>();
+ ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel) level).moonrise$getEntityLookup().getEntities(ItemEntity.class, null, boundingBox, items, (ItemEntity itemEntity) -> itemEntity.distanceToSqr(x, y, z) < MAX_DIST_SQ && body.wantsToPickUp(level, itemEntity.getItem())); // Paper - Perf: Move predicate into getEntities
+ level.getCurrentWorldData().radixBitSorter.sort(items.elements(), items.size(), pos);
+ // Leaf end - fast bit radix sort
// Paper start - Perf: remove streams from hot code
ItemEntity nearest = null;
for (final ItemEntity item : items) {
diff --git a/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java b/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java
index 3578ece216827fd5d8c1206c689fc608e97b50df..9fbe439663edf57e018369a44ed7576d947d1fbc 100644
--- a/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java
+++ b/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java
@@ -17,8 +17,11 @@ public class NearestLivingEntitySensor<T extends LivingEntity> extends Sensor<T>
protected void doTick(final ServerLevel level, final T body) {
double followRange = body.getAttributeValue(Attributes.FOLLOW_RANGE);
AABB boundingBox = body.getBoundingBox().inflate(followRange, followRange, followRange);
- List<LivingEntity> livingEntities = level.getEntitiesOfClass(LivingEntity.class, boundingBox, mob -> mob != body && mob.isAlive());
- livingEntities.sort(Comparator.comparingDouble(body::distanceToSqr));
+ // Leaf start - fast bit radix sort
+ it.unimi.dsi.fastutil.objects.ObjectArrayList<LivingEntity> livingEntities = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>();
+ ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel) level).moonrise$getEntityLookup().getEntities(LivingEntity.class, body, boundingBox, livingEntities, LivingEntity::isAlive);
+ level.getCurrentWorldData().radixBitSorter.sort(livingEntities.elements(), livingEntities.size(), body.position());
+ // Leaf end - fast bit radix sort
Brain<?> brain = body.getBrain();
brain.setMemory(MemoryModuleType.NEAREST_LIVING_ENTITIES, livingEntities);
brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES, new NearestVisibleLivingEntities(level, body, livingEntities));
@@ -0,0 +1,203 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Sat, 27 Jun 2026 17:23:22 +0800
Subject: [PATCH] Leaf Configurable vanilla username check
A part of Leaf(https://github.com/Winds-Studio/Leaf/blob/edb0504069139beaa6f39efa4702370c2576b3fc/leaf-server/minecraft-patches/features/0086-Configurable-vanilla-username-check.patch)
License: https://github.com/Winds-Studio/Leaf/blob/edb0504069139beaa6f39efa4702370c2576b3fc/LICENSE.md
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 7840b0e00ab84eab016e971ac0c2693056fb0721..d2303ad91c9634cdbf599552a6bce7279a1f0e0a 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -217,7 +217,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
public static final NameAndId ANONYMOUS_PLAYER_PROFILE = new NameAndId(Util.NIL_UUID, "Anonymous Player");
public static final String SERVER_THREAD_NAME = "Server thread";
public LevelStorageSource.LevelStorageAccess storageSource;
- protected final PlayerDataStorage playerDataStorage;
+ public final PlayerDataStorage playerDataStorage;
private final SavedDataStorage savedDataStorage;
private final List<Runnable> tickables = Lists.newArrayList();
// Paper - per-level GameRules
diff --git a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
index deee051c103f9797f4612e06c12f293ec54724ac..911fb0912c7bc6ab0e29c03a21c6bddb248bbc0a 100644
--- a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
@@ -167,11 +167,20 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener,
public void handleHello(final ServerboundHelloPacket packet) {
Validate.validState(this.state == ServerLoginPacketListenerImpl.State.HELLO, "Unexpected hello packet");
// Paper start - Validate usernames
- if (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode()
+ // Leaf start - Configurable vanilla username check
+ boolean allPrevChecksPassed;
+ if (me.earthme.luminol.config.modules.misc.UsernameCheckConfig.enabled
+ && io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode()
&& io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.performUsernameValidation
&& !this.iKnowThisMayNotBeTheBestIdeaButPleaseDisableUsernameValidation) {
- Validate.validState(StringUtil.isReasonablePlayerName(packet.name()), "Invalid characters in username");
+ allPrevChecksPassed = true;
+ if (!me.earthme.luminol.config.modules.misc.UsernameCheckConfig.allowOldPlayersJoin) {
+ Validate.validState(StringUtil.isReasonablePlayerName(packet.name()), "Invalid characters in username");
+ }
+ } else {
+ allPrevChecksPassed = false;
}
+ // Leaf end - Configurable vanilla username check
this.requestedUuid = packet.profileId();
// Paper end - Validate usernames
this.requestedUsername = packet.name();
@@ -197,6 +206,15 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener,
authenticatorPool.execute(() -> {
try {
GameProfile gameprofile = ServerLoginPacketListenerImpl.this.createOfflineProfile(ServerLoginPacketListenerImpl.this.requestedUsername); // Spigot
+ // Leaf start - Configurable vanilla username check
+ if (me.earthme.luminol.config.modules.misc.UsernameCheckConfig.allowOldPlayersJoin) {
+ if (server.playerDataStorage.load(new net.minecraft.server.players.NameAndId(gameprofile)).orElse(null) != null) {
+ server.getPlayerList().playedPlayers.add(packet.name());
+ } else if (allPrevChecksPassed) {
+ Validate.validState(StringUtil.isReasonablePlayerName(packet.name()), "Invalid characters in username");
+ }
+ }
+ // Leaf end - Configurable vanilla username check
gameprofile = ServerLoginPacketListenerImpl.this.callPlayerPreLoginEvents(gameprofile); // Paper - Add more fields to AsyncPlayerPreLoginEvent
ServerLoginPacketListenerImpl.LOGGER.info("UUID of player {} is {}", gameprofile.name(), gameprofile.id());
@@ -341,7 +359,7 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener,
server.getPluginManager().callEvent(asyncEvent);
profile = asyncEvent.getPlayerProfile();
profile.complete(true); // Paper - setPlayerProfileAPI
- gameprofile = com.destroystokyo.paper.profile.CraftPlayerProfile.asAuthlibCopy(profile);
+ gameprofile = com.destroystokyo.paper.profile.CraftPlayerProfile.asAuthlibCopyCustomValidation(profile); // Leaf - Configurable vanilla username check
playerName = gameprofile.name();
uniqueId = gameprofile.id();
// Paper end - Add more fields to AsyncPlayerPreLoginEvent
diff --git a/net/minecraft/server/players/CachedUserNameToIdResolver.java b/net/minecraft/server/players/CachedUserNameToIdResolver.java
index 7443744e3f256983e52a1cefcaf66082e4a46a7b..b7553611723f7120c66e1f3e51d89df28bd2756b 100644
--- a/net/minecraft/server/players/CachedUserNameToIdResolver.java
+++ b/net/minecraft/server/players/CachedUserNameToIdResolver.java
@@ -67,7 +67,7 @@ public class CachedUserNameToIdResolver implements UserNameToIdResolver {
}
private Optional<NameAndId> lookupGameProfile(final GameProfileRepository profileRepository, final String name) {
- if (!StringUtil.isValidPlayerName(name)) {
+ if (!StringUtil.isValidPlayerName(name, false)) { // Leaf - Configurable vanilla username check - Directly return, skip unnecessary following logic
return this.createUnknownProfile(name);
}
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
index a8f051e6aff31a723a3ba59842fac3a43f4911fe..2939aa1ecd68504424198dd7c90c71c6c1aa4c80 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
@@ -134,6 +134,7 @@ public abstract class PlayerList {
private org.bukkit.craftbukkit.CraftServer cserver;
private final Map<String,ServerPlayer> playersByName = new java.util.HashMap<>();
public @Nullable String collideRuleTeamName; // Paper - Configurable player collision
+ public final List<String> playedPlayers = new java.util.concurrent.CopyOnWriteArrayList<>(); // Leaf - Configurable vanilla username check
// Folia start - region threading
private final Object connectionsStateLock = new Object();
@@ -581,6 +582,7 @@ public abstract class PlayerList {
player.getAdvancements().clearTriggers();
this.players.remove(player);
this.playersByName.remove(player.getScoreboardName().toLowerCase(java.util.Locale.ROOT)); // Spigot
+ if (me.earthme.luminol.config.modules.misc.UsernameCheckConfig.allowOldPlayersJoin) this.playedPlayers.remove(player.getGameProfile().name()); // Leaf - Configurable vanilla username check
this.server.getCustomBossEvents().onPlayerDisconnect(player);
UUID uuid = player.getUUID();
ServerPlayer serverPlayer = this.playersByUUID.get(uuid);
diff --git a/net/minecraft/util/StringUtil.java b/net/minecraft/util/StringUtil.java
index 7957e0cfc43909c5268698a11c4933d20b4d9155..3de226b84f5c72ae277384b3fda4580a7fa2b43d 100644
--- a/net/minecraft/util/StringUtil.java
+++ b/net/minecraft/util/StringUtil.java
@@ -64,6 +64,15 @@ public class StringUtil {
}
public static boolean isValidPlayerName(final String name) {
+ // Leaf start - Configurable vanilla username check
+ return isValidPlayerName(name, me.earthme.luminol.config.modules.misc.UsernameCheckConfig.shouldSkipNonPlayerNameCheck());
+ }
+ public static boolean isValidPlayerNameVanilla(final String name) {
+ return name.length() <= 16 && name.chars().filter(i -> i <= 32 || i >= 127).findAny().isEmpty();
+ }
+ public static boolean isValidPlayerName(final String name, final boolean bypassCheck) {
+ if (bypassCheck) return name.length() <= 16;
+ // Leaf end - Configurable vanilla username check
return name.length() <= 16 && name.chars().filter(c -> c <= 32 || c >= 127).findAny().isEmpty();
}
@@ -87,6 +96,12 @@ public class StringUtil {
// Paper start - Username validation
public static boolean isReasonablePlayerName(final String name) {
+ // Leaf start - Configurable vanilla username check
+ if (me.earthme.luminol.config.modules.misc.UsernameCheckConfig.allowOldPlayersJoin && net.minecraft.server.MinecraftServer.getServer().getPlayerList().playedPlayers.contains(name)) return true;
+ if (me.earthme.luminol.config.modules.misc.UsernameCheckConfig.useCustomUsernameRegex()) {
+ return me.earthme.luminol.config.modules.misc.UsernameCheckConfig.usernameRegex.matcher(name).matches() && name.length() <= 16; // Leaf - Configurable username check
+ }
+ // Leaf end - Configurable vanilla username check
if (name.isEmpty() || name.length() > 16) {
return false;
}
diff --git a/net/minecraft/world/item/component/ResolvableProfile.java b/net/minecraft/world/item/component/ResolvableProfile.java
index f1b62a6e0d8176d8ba1875e78cc730ee55b1b0c1..3312a751bdd71080bf429990490061bfbf86b758 100644
--- a/net/minecraft/world/item/component/ResolvableProfile.java
+++ b/net/minecraft/world/item/component/ResolvableProfile.java
@@ -68,6 +68,30 @@ public abstract sealed class ResolvableProfile implements TooltipProvider permit
public abstract Either<GameProfile, ResolvableProfile.Partial> unpack();
+ // Leaf start - Configurable vanilla username check - Enforce skull validation
+ private static Either<String, UUID> sanitizeDynamicPlayerName(final Either<String, UUID> nameOrId) {
+ if (nameOrId.left().isEmpty()) {
+ return nameOrId;
+ }
+ return me.earthme.luminol.config.modules.misc.UsernameCheckConfig.enforceSkullValidation && !net.minecraft.util.StringUtil.isValidPlayerNameVanilla(nameOrId.left().get()) ? Either.left("INVALID_OWNER") : nameOrId;
+ }
+
+ private static Optional<String> sanitizePartialPlayerName(final Optional<String> name) {
+ if (name.isEmpty()) {
+ return name;
+ }
+ return me.earthme.luminol.config.modules.misc.UsernameCheckConfig.enforceSkullValidation && !net.minecraft.util.StringUtil.isValidPlayerNameVanilla(name.get()) ? Optional.of("INVALID_OWNER") : name;
+ }
+
+ private static Either<GameProfile, ResolvableProfile.Partial> sanitizeStaticPlayerName(final Either<GameProfile, ResolvableProfile.Partial> contents) {
+ if (contents.left().isEmpty()) {
+ return contents;
+ }
+ GameProfile gameProfile = contents.left().get();
+ return me.earthme.luminol.config.modules.misc.UsernameCheckConfig.enforceSkullValidation && !net.minecraft.util.StringUtil.isValidPlayerNameVanilla(gameProfile.name()) ? Either.left(new GameProfile(gameProfile.id(), "INVALID_OWNER", gameProfile.properties())) : contents;
+ }
+ // Leaf end - Configurable vanilla username check - Enforce skull validation
+
protected ResolvableProfile(final GameProfile partialProfile, final PlayerSkin.Patch skinPatch) {
this.partialProfile = partialProfile;
this.skinPatch = skinPatch;
@@ -96,6 +120,7 @@ public abstract sealed class ResolvableProfile implements TooltipProvider permit
private final Either<String, UUID> nameOrId;
public Dynamic(final Either<String, UUID> nameOrId, final PlayerSkin.Patch skinPatch) {
+ sanitizeDynamicPlayerName(nameOrId); // Leaf - Configurable vanilla username check
super(ResolvableProfile.createPartialProfile(nameOrId.left(), nameOrId.right(), PropertyMap.EMPTY), skinPatch);
this.nameOrId = nameOrId;
}
@@ -135,6 +160,11 @@ public abstract sealed class ResolvableProfile implements TooltipProvider permit
}
public record Partial(Optional<String> name, Optional<UUID> id, PropertyMap properties) {
+ // Leaf start - Configurable vanilla username check
+ public Partial {
+ name = ResolvableProfile.sanitizePartialPlayerName(name);
+ }
+ // Leaf end - Configurable vanilla username check
public static final ResolvableProfile.Partial EMPTY = new ResolvableProfile.Partial(Optional.empty(), Optional.empty(), PropertyMap.EMPTY);
public static final MapCodec<ResolvableProfile.Partial> MAP_CODEC = RecordCodecBuilder.mapCodec(
i -> i.group(
@@ -165,6 +195,7 @@ public abstract sealed class ResolvableProfile implements TooltipProvider permit
private final Either<GameProfile, ResolvableProfile.Partial> contents;
public Static(final Either<GameProfile, ResolvableProfile.Partial> contents, final PlayerSkin.Patch skinPatch) {
+ sanitizeStaticPlayerName(contents); // Leaf - Configurable vanilla username check
super(contents.map(gameProfile -> (GameProfile)gameProfile, ResolvableProfile.Partial::createProfile), skinPatch);
this.contents = contents;
}
@@ -0,0 +1,89 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Sat, 18 Apr 2026 10:48:14 +0800
Subject: [PATCH] Purpur: Lobotomize stuck villagers
Co-authored by: William Blake Galbreath <Blake.Galbreath@GMail.com>
As part of: Purpur (https://github.com/PurpurMC/Purpur/blob/09f547de09fc5d886f18f6d99ff389289766ec9d/purpur-server/minecraft-patches/features/0001-Ridables.patch)
Licensed under: MIT (https://github.com/PurpurMC/Purpur/blob/09f547de09fc5d886f18f6d99ff389289766ec9d/LICENSE)
diff --git a/net/minecraft/world/entity/npc/villager/Villager.java b/net/minecraft/world/entity/npc/villager/Villager.java
index f649c46cc9a34e646f8da3244866c6e011d30aa3..10715e891205eadd3774dc349c52963606ce1ab7 100644
--- a/net/minecraft/world/entity/npc/villager/Villager.java
+++ b/net/minecraft/world/entity/npc/villager/Villager.java
@@ -190,6 +190,53 @@ public class Villager extends AbstractVillager implements VillagerDataHolder, Re
this.setCanPickUpLoot(true);
}
+ // Purpur start
+ private boolean isLobotomized = false; public boolean isLobotomized() { return this.isLobotomized; } // Purpur
+ private int notLobotomizedCount = 0; // Purpur
+
+ private boolean checkLobotomized() {
+ int interval = me.earthme.luminol.config.modules.optimizations.LobotomizeVillageConfig.villagerLobotomizeCheckInterval;
+ boolean shouldCheckForTradeLocked = me.earthme.luminol.config.modules.optimizations.LobotomizeVillageConfig.villagerLobotomizeWaitUntilTradeLocked;
+ if (this.notLobotomizedCount > 3) {
+ // check half as often if not lobotomized for the last 3+ consecutive checks
+ interval *= 2;
+ }
+ if (this.level().getGameTime() % interval == 0) {
+ // offset Y for short blocks like dirt_path/farmland
+ this.isLobotomized = !(shouldCheckForTradeLocked && this.getVillagerXp() == 0) && !canTravelFrom(net.minecraft.core.BlockPos.containing(this.position().x, this.getBoundingBox().minY + 0.0625D, this.position().z));
+
+ if (this.isLobotomized) {
+ this.notLobotomizedCount = 0;
+ } else {
+ this.notLobotomizedCount++;
+ }
+ }
+ return this.isLobotomized;
+ }
+ // Purpur end
+
+ private boolean canTravelFrom(net.minecraft.core.BlockPos pos) {
+ return canTravelTo(pos.east()) || canTravelTo(pos.west()) || canTravelTo(pos.north()) || canTravelTo(pos.south());
+ }
+
+ private boolean canTravelTo(net.minecraft.core.BlockPos pos) {
+ net.minecraft.world.level.block.state.BlockState state = this.level().getBlockStateIfLoaded(pos);
+ if (state == null) {
+ // chunk not loaded
+ return false;
+ }
+ net.minecraft.world.level.block.Block bottom = state.getBlock();
+ if (bottom instanceof net.minecraft.world.level.block.FenceBlock ||
+ bottom instanceof net.minecraft.world.level.block.FenceGateBlock ||
+ bottom instanceof net.minecraft.world.level.block.WallBlock) {
+ // bottom block is too tall to get over
+ return false;
+ }
+ net.minecraft.world.level.block.Block top = level().getBlockState(pos.above()).getBlock();
+ // only if both blocks have no collision
+ return !bottom.hasCollision && !top.hasCollision;
+ }
+
@Override
public Brain<Villager> getBrain() {
return (Brain<Villager>)super.getBrain();
@@ -259,11 +306,20 @@ public class Villager extends AbstractVillager implements VillagerDataHolder, Re
// Paper start - EAR 2
this.customServerAiStep(level, false);
}
- protected void customServerAiStep(ServerLevel level, final boolean inactive) {
+ protected void customServerAiStep(ServerLevel level, boolean inactive) { // Purpur - not final
// Paper end - EAR 2
ProfilerFiller profiler = Profiler.get();
profiler.push("villagerBrain");
+ // Purpur start
+ if (me.earthme.luminol.config.modules.optimizations.LobotomizeVillageConfig.villagerLobotomizeEnabled) {
+ // treat as inactive if lobotomized
+ inactive = inactive || checkLobotomized();
+ } else {
+ this.isLobotomized = false;
+ }
+ // Purpur end
if (!inactive) this.getBrain().tick(level, this); // Paper - EAR 2
+ else if (this.isLobotomized && shouldRestock(level)) restock(); // Purpur - Lobotomize stuck villagers
profiler.pop();
if (this.assignProfessionWhenSpawned) {
this.assignProfessionWhenSpawned = false;
@@ -0,0 +1,68 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Fri, 1 May 2026 21:47:48 +0800
Subject: [PATCH] Pufferfish: Reduce projectile chunk loading
A part of Pufferfish(https://github.com/Pufferfish-gg/Pufferfish)
Co-authored-by: Paul Sauve <paul@technove.co>
Original patch: https://github.com/pufferfish-gg/Pufferfish/blob/ver/1.21/pufferfish-server/minecraft-patches/features/0006-Reduce-projectile-chunk-loading.patch
Original license(GPL-3.0): https://github.com/pufferfish-gg/Pufferfish/blob/ver/1.21/PATCH-LICENSE
diff --git a/io/papermc/paper/threadedregions/RegionizedWorldData.java b/io/papermc/paper/threadedregions/RegionizedWorldData.java
index 48fea9051efe038a823d7c2c9ae3bf28078f2fcc..c9f2d54be6f0699c2bf922c66ba0bafc7fb83ccc 100644
--- a/io/papermc/paper/threadedregions/RegionizedWorldData.java
+++ b/io/papermc/paper/threadedregions/RegionizedWorldData.java
@@ -339,6 +339,10 @@ public final class RegionizedWorldData {
private RegionizedServer.WorldLevelData tickData;
+ // Luminol start - Pufferfish projectile limiter
+ public long pufferfish$loadedThisTick = 0L;
+ public long pufferfish$loadedTick = 0L;
+ // Luminol end
// connections
public final List<Connection> connections = new ArrayList<>();
diff --git a/net/minecraft/world/entity/projectile/Projectile.java b/net/minecraft/world/entity/projectile/Projectile.java
index 721e3b083846fd363a31311196bc4c681969215c..81aafd64cc80038eaafba059373db3682afb8c2c 100644
--- a/net/minecraft/world/entity/projectile/Projectile.java
+++ b/net/minecraft/world/entity/projectile/Projectile.java
@@ -58,6 +58,38 @@ public abstract class Projectile extends Entity implements TraceableEntity {
this.setOwner(EntityReference.of(owner));
}
+ // Pufferfish start
+ private int loadedLifetime = 0;
+ @Override
+ public void setPos(double x, double y, double z) {
+ var currRegionData = io.papermc.paper.threadedregions.TickRegionScheduler.getCurrentRegionizedWorldData();
+ // we might run this on a chunk system worker(chunk gen), so skip this check if no world data was fetched
+ if (currRegionData == null || currRegionData.world != this.level()) {
+ return;
+ }
+ long currentTick = currRegionData.getRedstoneGameTime();
+ if (currRegionData.pufferfish$loadedTick != currentTick) {
+ currRegionData.pufferfish$loadedTick = currentTick;
+ currRegionData.pufferfish$loadedThisTick = 0L;
+ }
+ int previousX = Mth.floor(this.getX()) >> 4, previousZ = Mth.floor(this.getZ()) >> 4;
+ int newX = Mth.floor(x) >> 4, newZ = Mth.floor(z) >> 4;
+ if (previousX != newX || previousZ != newZ) {
+ boolean isLoaded = ((net.minecraft.server.level.ServerChunkCache) this.level().getChunkSource()).getChunkAtIfLoadedImmediately(newX, newZ) != null;
+ if (!isLoaded) {
+ if (currRegionData.pufferfish$loadedThisTick > me.earthme.luminol.config.modules.optimizations.ProjectileChunkReduceConfig.maxProjectileLoadsPerTick) {
+ if (++this.loadedLifetime > me.earthme.luminol.config.modules.optimizations.ProjectileChunkReduceConfig.maxProjectileLoadsPerProjectile) {
+ this.discard();
+ }
+ return;
+ }
+ currRegionData.pufferfish$loadedThisTick++;
+ }
+ }
+ super.setPos(x, y, z);
+ }
+ // Pufferfish end
+
// Folia start - region threading
// In general, this is an entire mess. At the time of writing, there are fifty usages of getOwner.
// Usage of this function is to avoid concurrency issues, even if it sacrifices behavior.
@@ -0,0 +1,30 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Fri, 1 May 2026 21:48:40 +0800
Subject: [PATCH] Pufferfish: Throttle goal selector during inactive ticking
A part of Pufferfish(https://github.com/Pufferfish-gg/Pufferfish)
Co-authored-by: Kevin Raneri <kevin.raneri@gmail.com>
Original patch: https://github.com/pufferfish-gg/Pufferfish/blob/ver/1.21/pufferfish-server/minecraft-patches/features/0015-Throttle-goal-selector-during-inactive-ticking.patch
Original license(GPL-3.0): https://github.com/pufferfish-gg/Pufferfish/blob/ver/1.21/PATCH-LICENSE
diff --git a/net/minecraft/world/entity/Mob.java b/net/minecraft/world/entity/Mob.java
index 301e75393d1074eca0c8533219f516b6d625a38a..24171563af7c629bf2bf492fdc575e2bc67c361e 100644
--- a/net/minecraft/world/entity/Mob.java
+++ b/net/minecraft/world/entity/Mob.java
@@ -215,12 +215,14 @@ public abstract class Mob extends LivingEntity implements Targeting, EquipmentUs
return this.lookControl;
}
+ int _pufferfish_inactiveTickDisableCounter = 0; // Pufferfish - throttle inactive goal selector ticking
// Paper start
@Override
public void inactiveTick() {
super.inactiveTick();
if (!this.aware) return; // Paper - Do not tick AI for inactive unaware mobs
- if (this.goalSelector.inactiveTick()) {
+ boolean isThrottled = me.earthme.luminol.config.modules.optimizations.EntityGoalSelectorInactiveTickConfig.enabled && _pufferfish_inactiveTickDisableCounter++ % 20 != 0; // Pufferfish - throttle inactive goal selector ticking
+ if (this.goalSelector.inactiveTick() && !isThrottled) { // Pufferfish
this.goalSelector.tick();
}
if (this.targetSelector.inactiveTick()) {
@@ -0,0 +1,28 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Sat, 18 Apr 2026 12:30:47 +0800
Subject: [PATCH] Petal: Reduce sensor work
Co-authored by: peaches94 <peachescu94@gmail.com>
As part of: Petal (https://github.com/Bloom-host/Petal/blob/cc691540fb48240f38b376f3d94c8b0db2b60d99/patches/server/0005-feat-reduce-sensor-work.patch)
Licensed under: GPL-3.0 (https://github.com/Bloom-host/Petal/blob/cc691540fb48240f38b376f3d94c8b0db2b60d99/LICENSE)
diff --git a/net/minecraft/world/entity/Mob.java b/net/minecraft/world/entity/Mob.java
index 24171563af7c629bf2bf492fdc575e2bc67c361e..e7ebdbb8d77f841551c5ebb0f8e7b4402f6a4751 100644
--- a/net/minecraft/world/entity/Mob.java
+++ b/net/minecraft/world/entity/Mob.java
@@ -842,11 +842,12 @@ public abstract class Mob extends LivingEntity implements Targeting, EquipmentUs
return;
}
// Paper end - Allow nerfed mobs to jump and float
+ int idBasedTickCount = this.tickCount + this.getId(); // Luminol - Petal - Move up
ProfilerFiller profiler = Profiler.get();
profiler.push("sensing");
- this.sensing.tick();
+ if (idBasedTickCount % me.earthme.luminol.config.modules.optimizations.PetalReduceSensorWorkConfig.delayTicks == 0 || !me.earthme.luminol.config.modules.optimizations.PetalReduceSensorWorkConfig.enabled) this.sensing.tick(); // Luminol - Petal - Reduce sensor work
profiler.pop();
- int idBasedTickCount = this.tickCount + this.getId();
+ //int idBasedTickCount = this.tickCount + this.getId(); // Luminol - Petal - Move up
if (idBasedTickCount % 2 != 0 && this.tickCount > 1) {
profiler.push("targetSelector");
this.targetSelector.tickRunningGoals(false);
@@ -0,0 +1,60 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Sun, 28 Jun 2026 20:57:48 +0800
Subject: [PATCH] Leaves: Disable packet limit
diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java
index ecaa296f576c5c90fda89e0406d6552628ef5de3..d2e9d46bbd9ee4ce45419b11499d7a7bc50f0219 100644
--- a/net/minecraft/network/Connection.java
+++ b/net/minecraft/network/Connection.java
@@ -258,8 +258,8 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
if (this.stopReadingPackets) {
return;
}
- if (this.allPacketCounts != null ||
- io.papermc.paper.configuration.GlobalConfiguration.get().packetLimiter.overrides.containsKey(packet.getClass())) {
+ if (!me.earthme.luminol.config.modules.misc.PaperPacketLimiterConfig.forceDisable && (this.allPacketCounts != null || // Luminol - Add config to force disable the packet limiter of Paper
+ io.papermc.paper.configuration.GlobalConfiguration.get().packetLimiter.overrides.containsKey(packet.getClass()))) { // Luminol - Add config to force disable the packet limiter of Paper
long time = System.nanoTime();
synchronized (PACKET_LIMIT_LOCK) {
if (this.allPacketCounts != null) {
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index a2146662c8ec83f62547bcd813c0e4dcb10efc07..31c6defd8941a2c9e55eca3c8ca5623111e98e51 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -892,7 +892,7 @@ public class ServerGamePacketListenerImpl
public void handleCustomCommandSuggestions(final ServerboundCommandSuggestionPacket packet) {
// PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); // Paper - AsyncTabCompleteEvent; run this async
// CraftBukkit start
- if (!this.tabSpamThrottler.isIncrementAndUnderThreshold() && !this.server.getPlayerList().isOp(this.player.nameAndId()) && !this.server.isSingleplayerOwner(this.player.nameAndId())) { // Paper - configurable tab spam limits
+ if (!me.earthme.luminol.config.modules.misc.PaperPacketLimiterConfig.forceDisable && !this.tabSpamThrottler.isIncrementAndUnderThreshold() && !this.server.getPlayerList().isOp(this.player.nameAndId()) && !this.server.isSingleplayerOwner(this.player.nameAndId())) { // Paper - configurable tab spam limits // Leaves - can disable
this.disconnectAsync(Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - Kick event cause // Paper - add proper async disconnect
return;
}
@@ -2128,6 +2128,7 @@ public class ServerGamePacketListenerImpl
private long lastLimitedPacket = -1;
private boolean checkLimit(long timestamp) {
+ if (me.earthme.luminol.config.modules.misc.PaperPacketLimiterConfig.forceDisable) return true; // Leaves - disable
if (!io.papermc.paper.configuration.GlobalConfiguration.get().spamLimiter.incomingPacketThreshold.enabled()) {
return true;
}
@@ -2694,6 +2695,8 @@ public class ServerGamePacketListenerImpl
// Spigot start - spam exclusions
private void detectRateSpam(final TickThrottler throttler, final String message) {
+ if (me.earthme.luminol.config.modules.misc.PaperPacketLimiterConfig.forceDisable) return; // Leaves - disable
+ // CraftBukkit start - replaced with thread safe throttle
if (org.spigotmc.SpigotConfig.enableSpamExclusions) {
for (String exclude : org.spigotmc.SpigotConfig.spamExclusions) {
if (exclude != null && message.startsWith(exclude)) {
@@ -3492,7 +3495,7 @@ public class ServerGamePacketListenerImpl
@Override
public void handlePlaceRecipe(final ServerboundPlaceRecipePacket packet) {
// Paper start - auto recipe limit
- if (!org.bukkit.Bukkit.isPrimaryThread()) {
+ if (!me.earthme.luminol.config.modules.misc.PaperPacketLimiterConfig.forceDisable && !org.bukkit.Bukkit.isPrimaryThread()) { // Leaves - can disable
if (!this.recipeSpamPackets.isIncrementAndUnderThreshold()) {
this.disconnectAsync(net.minecraft.network.chat.Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - kick event cause // Paper - add proper async disconnect
return;
@@ -0,0 +1,94 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Sat, 18 Apr 2026 12:04:49 +0800
Subject: [PATCH] Leaves Vanilla Hopper
A part from leaves
Origin patch link: https://github.com/LeavesMC/Leaves/blob/master/leaves-server/minecraft-patches/features/0092-Vanilla-hopper.patch
Origin license: https://github.com/LeavesMC/Leaves/blob/master/LICENSE.md
diff --git a/net/minecraft/world/level/block/entity/HopperBlockEntity.java b/net/minecraft/world/level/block/entity/HopperBlockEntity.java
index a858cb658af70f07614fa1f4d9e8a3435d5c161f..4c9d0bcd8820b250b51c06fff4531021ab57d408 100644
--- a/net/minecraft/world/level/block/entity/HopperBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/HopperBlockEntity.java
@@ -281,36 +281,55 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
ItemStack movedItem = origItemStack;
final int originalItemCount = origItemStack.getCount();
final int movedItemCount = Math.min(level.spigotConfig.hopperAmount, originalItemCount);
- container.setChanged(); // original logic always marks source inv as changed even if no move happens.
- movedItem.setCount(movedItemCount);
-
- if (!worldData.skipPullModeEventFire) { // Folia - region threading
- movedItem = callPullMoveEvent(hopper, container, movedItem);
- if (movedItem == null) { // cancelled
- origItemStack.setCount(originalItemCount);
- // Drastically improve performance by returning true.
- // No plugin could have relied on the behavior of false as the other call
- // site for IMIE did not exhibit the same behavior
+ // Leaves start - fix vanilla hopper
+ if (movedItem.getCount() <= movedItemCount) {
+ if (!worldData.skipPullModeEventFire) {
+ movedItem = callPullMoveEvent(hopper, container, movedItem);
+ if (movedItem == null) { // cancelled
+ origItemStack.setCount(originalItemCount);
+ return true;
+ }
+ }
+ movedItem = origItemStack.copy();
+ final ItemStack remainingItem = addItem(container, hopper, container.removeItem(i, movedItemCount), null);
+ if (remainingItem.isEmpty()) {
+ container.setChanged();
return true;
}
- }
+ container.setItem(i, movedItem);
+ } else {
+ container.setChanged(); // original logic always marks source inv as changed even if no move happens.
+ movedItem.setCount(movedItemCount);
- final ItemStack remainingItem = addItem(container, hopper, movedItem, null);
- final int remainingItemCount = remainingItem.getCount();
- if (remainingItemCount != movedItemCount) {
- origItemStack = origItemStack.copy(true);
- origItemStack.setCount(originalItemCount);
- if (!origItemStack.isEmpty()) {
- origItemStack.setCount(originalItemCount - movedItemCount + remainingItemCount);
+ if (!worldData.skipPullModeEventFire) {
+ movedItem = callPullMoveEvent(hopper, container, movedItem);
+ if (movedItem == null) { // cancelled
+ origItemStack.setCount(originalItemCount);
+ // Drastically improve performance by returning true.
+ // No plugin could have relied on the behavior of false as the other call
+ // site for IMIE did not exhibit the same behavior
+ return true;
+ }
}
- IGNORE_TILE_UPDATES.set(true); // Folia - region threading
- container.setItem(i, origItemStack);
- IGNORE_TILE_UPDATES.set(false); // Folia - region threading
- container.setChanged();
- return true;
+ final ItemStack remainingItem = addItem(container, hopper, movedItem, null);
+ final int remainingItemCount = remainingItem.getCount();
+ if (remainingItemCount != movedItemCount) {
+ origItemStack = origItemStack.copy(true);
+ origItemStack.setCount(originalItemCount);
+ if (!origItemStack.isEmpty()) {
+ origItemStack.setCount(originalItemCount - movedItemCount + remainingItemCount);
+ }
+
+ IGNORE_TILE_UPDATES.set(true);
+ container.setItem(i, origItemStack);
+ IGNORE_TILE_UPDATES.set(false);
+ container.setChanged();
+ return true;
+ }
+ origItemStack.setCount(originalItemCount);
}
- origItemStack.setCount(originalItemCount);
+ // Leaves end - fix vanilla hopper
if (level.paperConfig().hopper.cooldownWhenFull) {
applyCooldown(hopper);
@@ -0,0 +1,56 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Sat, 18 Apr 2026 12:26:25 +0800
Subject: [PATCH] Leaves: Configurable collision behavior
Co-authored by: Fortern <blueten.ki@gmail.com>
As part of: Leaves (https://github.com/LeavesMC/Leaves/blob/c5f18b7864206cea4411211b51787f10affbcb9c/leaves-server/minecraft-patches/features/0111-Configurable-collision-behavior.patch)
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
diff --git a/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java b/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java
index 8d2518600ad518999b75124f0a87db9efe541f2e..09e1d701038042052fcbde4b4686da5d6a15f695 100644
--- a/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java
+++ b/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java
@@ -101,6 +101,14 @@ public final class CollisionUtil {
(box1.minZ - box2.maxZ) < -COLLISION_EPSILON && (box1.maxZ - box2.minZ) > COLLISION_EPSILON;
}
+ // Leaves start - Configurable collision behavior
+ public static boolean voxelShapeIntersectVanilla(final AABB box1, final AABB box2) {
+ return box1.minX < box2.maxX && box1.maxX > box2.minX &&
+ box1.minY < box2.maxY && box1.maxY > box2.minY &&
+ box1.minZ < box2.maxZ && box1.maxZ > box2.minZ;
+ }
+ // Leaves end - Configurable collision behavior
+
// assume !isEmpty(target) && abs(source_move) >= COLLISION_EPSILON
public static double collideX(final AABB target, final AABB source, final double source_move) {
if ((source.minY - target.maxY) < -COLLISION_EPSILON && (source.maxY - target.minY) > COLLISION_EPSILON &&
@@ -2033,7 +2041,7 @@ public final class CollisionUtil {
continue;
}
} else {
- if (!voxelShapeIntersect(aabb, singleAABB)) {
+ if (shouldSkip(aabb, blockCollision, singleAABB)) { // Leaves - Configurable collision behavior
continue;
}
}
@@ -2087,6 +2095,18 @@ public final class CollisionUtil {
return ret;
}
+ // Leaves start - Configurable collision behavior
+ private static boolean shouldSkip(AABB aabb, VoxelShape blockCollision, AABB singleAABB) {
+ boolean isBlockShape = blockCollision == Shapes.block();
+ return switch (me.earthme.luminol.config.modules.fixes.CollisionBehaviorConfig.behaviorMode) {
+ case me.earthme.luminol.enums.EnumCollisionBehaviorMode.VANILLA -> !voxelShapeIntersectVanilla(aabb, singleAABB);
+ case me.earthme.luminol.enums.EnumCollisionBehaviorMode.PAPER -> !voxelShapeIntersect(aabb, singleAABB);
+ default -> isBlockShape && !voxelShapeIntersectVanilla(aabb, singleAABB) || !isBlockShape && !voxelShapeIntersect(aabb, singleAABB);
+ // All other value as BLOCK_SHAPE_VANILLA to process
+ };
+ }
+ // Leaves end - Configurable collision behavior
+
public static boolean getEntityHardCollisions(final Level world, final Entity entity, AABB aabb,
final List<AABB> into, final int collisionFlags, final Predicate<Entity> predicate) {
final boolean checkOnly = (collisionFlags & COLLISION_FLAG_CHECK_ONLY) != 0;
@@ -0,0 +1,50 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Tue, 21 Apr 2026 23:35:48 +0800
Subject: [PATCH] Leaves: Disable moved wrongly threshold
Co-authored by: violetc <58360096+s-yh-china@users.noreply.github.com>
As part of: Leaves (https://github.com/LeavesMC/Leaves/blob/f553c53e4230aa032e54a69b6479f1959ed24a60/patches/removed/server/0099-Disable-moved-wrongly-threshold.patch)
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 31c6defd8941a2c9e55eca3c8ca5623111e98e51..164e2101688a9f29ecfa83f62141d8cb9df22ed1 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -644,7 +644,7 @@ public class ServerGamePacketListenerImpl
return;
}
// Paper end - Prevent moving into unloaded chunks
- if (movedDist - expectedDist > Math.max(100.0, Mth.square(org.spigotmc.SpigotConfig.movedTooQuicklyMultiplier * (float) i * speed)) && !this.isSingleplayerOwner()) {
+ if (!me.earthme.luminol.config.modules.misc.DisableWarningConfig.disableMovedWronglyThresholdWarning && movedDist - expectedDist > Math.max(100.0, Mth.square(org.spigotmc.SpigotConfig.movedTooQuicklyMultiplier * (float) i * speed)) && !this.isSingleplayerOwner()) { // Leaves - disable can
// CraftBukkit end
LOGGER.warn(
"{} (vehicle of {}) moved too quickly! {},{},{}", vehicle.getPlainTextName(), this.player.getPlainTextName(), xDist, yDist, zDist
@@ -674,7 +674,7 @@ public class ServerGamePacketListenerImpl
zDist = targetZ - vehicle.getZ();
movedDist = xDist * xDist + yDist * yDist + zDist * zDist;
boolean fail = false;
- if (movedDist > org.spigotmc.SpigotConfig.movedWronglyThreshold) { // Spigot
+ if (!me.earthme.luminol.config.modules.misc.DisableWarningConfig.disableMovedWronglyThresholdWarning && movedDist > org.spigotmc.SpigotConfig.movedWronglyThreshold) { // Spigot // Leaves - disable can
fail = true;
LOGGER.warn("{} (vehicle of {}) moved wrongly! {}", vehicle.getPlainTextName(), this.player.getPlainTextName(), Math.sqrt(movedDist));
}
@@ -1614,7 +1614,7 @@ public class ServerGamePacketListenerImpl
if (this.shouldCheckPlayerMovement(isFallFlying)) {
float metersPerTick = isFallFlying ? 300.0F : 100.0F;
- if (movedDist - expectedDist > Math.max(metersPerTick, Mth.square(org.spigotmc.SpigotConfig.movedTooQuicklyMultiplier * (float) deltaPackets * speed))) {
+ if (!me.earthme.luminol.config.modules.misc.DisableWarningConfig.disableMovedWronglyThresholdWarning && movedDist - expectedDist > Math.max(metersPerTick, Mth.square(org.spigotmc.SpigotConfig.movedTooQuicklyMultiplier * (float) deltaPackets * speed))) { // Leaves - disable can
// CraftBukkit end
// Paper start - Add fail move event
io.papermc.paper.event.player.PlayerFailMoveEvent event = fireFailMove(io.papermc.paper.event.player.PlayerFailMoveEvent.FailReason.MOVED_TOO_QUICKLY,
@@ -1675,7 +1675,8 @@ public class ServerGamePacketListenerImpl
zDist = targetZ - this.player.getZ();
movedDist = xDist * xDist + yDist * yDist + zDist * zDist;
boolean movedWrongly = false; // Paper - Add fail move event; rename
- if (!this.player.isChangingDimension()
+ if (!me.earthme.luminol.config.modules.misc.DisableWarningConfig.disableMovedWronglyThresholdWarning // Leaves - disable can
+ && !this.player.isChangingDimension()
&& movedDist > org.spigotmc.SpigotConfig.movedWronglyThreshold // Spigot
&& !this.player.isSleeping()
&& !this.player.isCreative()
@@ -0,0 +1,95 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Tue, 21 Apr 2026 23:52:23 +0800
Subject: [PATCH] Leaves: Optimized dragon respawn
Co-authored by: violetc <58360096+s-yh-china@users.noreply.github.com>
As part of: Leaves (https://github.com/LeavesMC/Leaves/blob/4ade1001e4dd19c47d95c27f0b12df3175697f29/leaves-server/minecraft-patches/features/0049-Optimized-dragon-respawn.patch)
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
diff --git a/net/minecraft/world/level/dimension/end/EnderDragonFight.java b/net/minecraft/world/level/dimension/end/EnderDragonFight.java
index 824ef3f458505569fe5b8efb2055ecea7ba6cc56..3388bf5d2cff00b397af1dd7b9ab93669985b35c 100644
--- a/net/minecraft/world/level/dimension/end/EnderDragonFight.java
+++ b/net/minecraft/world/level/dimension/end/EnderDragonFight.java
@@ -317,7 +317,67 @@ public class EnderDragonFight extends SavedData {
return false;
}
+ // Leaves start - optimizedDragonRespawn
+ private int cachePortalChunkIteratorX = -8;
+ private int cachePortalChunkIteratorZ = -8;
+ private int cachePortalOriginIteratorY = -1;
+
public BlockPattern.@Nullable BlockPatternMatch findExitPortal() {
+ if (me.earthme.luminol.config.modules.optimizations.OptimizedDragonRespawnConfig.optimizedRespawn) {
+ int i, j;
+ for (i = cachePortalChunkIteratorX; i <= 8; ++i) {
+ for (j = cachePortalChunkIteratorZ; j <= 8; ++j) {
+ LevelChunk worldChunk = this.level.getChunk(i, j);
+ for (BlockEntity blockEntity : worldChunk.getBlockEntities().values()) {
+ if (blockEntity instanceof net.minecraft.world.level.block.entity.TheEndGatewayBlockEntity) {
+ continue;
+ }
+ if (blockEntity instanceof TheEndPortalBlockEntity) {
+ BlockPattern.BlockPatternMatch blockPatternMatch = this.exitPortalPattern.find(this.level, blockEntity.getBlockPos());
+ if (blockPatternMatch != null) {
+ BlockPos blockPos = blockPatternMatch.getBlock(3, 3, 3).getPos();
+ if (this.exitPortalLocation == null) {
+ this.exitPortalLocation = blockPos;
+ }
+ //No need to judge whether optimizing option is open
+ cachePortalChunkIteratorX = i;
+ cachePortalChunkIteratorZ = j;
+ return blockPatternMatch;
+ }
+ }
+ }
+ }
+ }
+
+ if (this.needsStateScanning || this.exitPortalLocation == null) {
+ if (cachePortalOriginIteratorY != -1) {
+ i = cachePortalOriginIteratorY;
+ } else {
+ i = this.level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, EndPodiumFeature.getLocation(BlockPos.ZERO)).getY();
+ }
+ boolean notFirstSearch = false;
+ for (j = i; j >= 0; --j) {
+ BlockPattern.BlockPatternMatch result2 = null;
+ if (notFirstSearch) {
+ result2 = org.leavesmc.leaves.util.BlockPatternHelper.partialSearchAround(this.exitPortalPattern, this.level, new BlockPos(EndPodiumFeature.getLocation(BlockPos.ZERO).getY(), j, EndPodiumFeature.getLocation(BlockPos.ZERO).getZ()));
+ } else {
+ result2 = this.exitPortalPattern.find(this.level, new BlockPos(EndPodiumFeature.getLocation(BlockPos.ZERO).getX(), j, EndPodiumFeature.getLocation(BlockPos.ZERO).getZ()));
+ }
+ if (result2 != null) {
+ if (this.exitPortalLocation == null) {
+ this.exitPortalLocation = result2.getBlock(3, 3, 3).getPos();
+ }
+ cachePortalOriginIteratorY = j;
+ return result2;
+ }
+ notFirstSearch = true;
+ }
+ }
+
+ return null;
+ }
+ // Leaves end - optimizedDragonRespawn
+
ChunkPos chunkOrigin = ChunkPos.containing(this.origin);
for (int x = -8 + chunkOrigin.x(); x <= 8 + chunkOrigin.x(); x++) {
@@ -623,8 +683,12 @@ public class EnderDragonFight extends SavedData {
}
return false; // CraftBukkit - return value
}
-
public boolean respawnDragon(final List<EndCrystal> crystals) { // CraftBukkit - return boolean
+ // Leaves - start optimizedDragonRespawn
+ cachePortalChunkIteratorX = -8;
+ cachePortalChunkIteratorZ = -8;
+ cachePortalOriginIteratorY = -1;
+ // Leaves - end optimizedDragonRespawn
if (this.dragonKilled && this.respawnStage == null) {
for (BlockPattern.BlockPatternMatch portal = this.findExitPortal(); portal != null; portal = this.findExitPortal()) {
for (int x = 0; x < this.exitPortalPattern.getWidth(); x++) {
@@ -0,0 +1,57 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Sat, 18 Apr 2026 12:22:30 +0800
Subject: [PATCH] Gale: Variable entity wake-up duration
Co-authored by: Martijn Muijsers <martijnmuijsers@live.nl>
As part of: Gale (https://github.com/GaleMC/Gale/blob/276e903b2688f23b19bdc8d493c0bf87656d2400/patches/server/0054-Variable-entity-wake-up-duration.patch)
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
diff --git a/io/papermc/paper/entity/activation/ActivationRange.java b/io/papermc/paper/entity/activation/ActivationRange.java
index b4f9f41a56bbc40e90217c0344c070ebdd08c119..6e1c2dcb1ef0b496e3d3bca9353d070488d16f44 100644
--- a/io/papermc/paper/entity/activation/ActivationRange.java
+++ b/io/papermc/paper/entity/activation/ActivationRange.java
@@ -61,27 +61,39 @@ public final class ActivationRange {
if (entity.activationType == ActivationType.VILLAGER) {
if (inactiveFor > config.wakeUpInactiveVillagersEvery && worldData.wakeupInactiveRemainingVillagers > 0) { // Folia - threaded regions
worldData.wakeupInactiveRemainingVillagers--; // Folia - threaded regions
- return config.wakeUpInactiveVillagersFor;
+ return getWakeUpDurationWithVariance(entity, config.wakeUpInactiveVillagersFor); // Gale - variable entity wake-up duration
}
} else if (entity.activationType == ActivationType.ANIMAL) {
if (inactiveFor > config.wakeUpInactiveAnimalsEvery && worldData.wakeupInactiveRemainingAnimals > 0) { // Folia - threaded regions
worldData.wakeupInactiveRemainingAnimals--; // Folia - threaded regions
- return config.wakeUpInactiveAnimalsFor;
+ return getWakeUpDurationWithVariance(entity, config.wakeUpInactiveAnimalsFor); // Gale - variable entity wake-up duration
}
} else if (entity.activationType == ActivationType.FLYING_MONSTER) {
if (inactiveFor > config.wakeUpInactiveFlyingEvery && worldData.wakeupInactiveRemainingFlying > 0) { // Folia - threaded regions
worldData.wakeupInactiveRemainingFlying--; // Folia - threaded regions
- return config.wakeUpInactiveFlyingFor;
+ return getWakeUpDurationWithVariance(entity, config.wakeUpInactiveFlyingFor); // Gale - variable entity wake-up duration
}
} else if (entity.activationType == ActivationType.MONSTER || entity.activationType == ActivationType.RAIDER) {
if (inactiveFor > config.wakeUpInactiveMonstersEvery && worldData.wakeupInactiveRemainingMonsters > 0) { // Folia - threaded regions
worldData.wakeupInactiveRemainingMonsters--; // Folia - threaded regions
- return config.wakeUpInactiveMonstersFor;
+ return getWakeUpDurationWithVariance(entity, config.wakeUpInactiveMonstersFor); // Gale - variable entity wake-up duration
}
}
return -1;
}
+ // Gale start - variable entity wake-up duration
+ private static final java.util.concurrent.ThreadLocalRandom wakeUpDurationRandom = java.util.concurrent.ThreadLocalRandom.current();
+
+ private static int getWakeUpDurationWithVariance(Entity entity, int wakeUpDuration) {
+ double deviation = me.earthme.luminol.config.modules.optimizations.GaleVariableEntityWakeupConfig.entityWakeUpDurationRatioStandardDeviation;
+ if (deviation <= 0) {
+ return wakeUpDuration;
+ }
+ return (int) Math.min(Integer.MAX_VALUE, Math.max(1, Math.round(wakeUpDuration * wakeUpDurationRandom.nextGaussian(1, deviation))));
+ }
+ // Gale end - variable entity wake-up duration
+
//static AABB maxBB = new AABB(0, 0, 0, 0, 0, 0); // Folia - threaded regions - replaced by local variable
/**
@@ -0,0 +1,30 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Sat, 18 Apr 2026 12:23:37 +0800
Subject: [PATCH] Gale: Replace AI attributes with optimized collections
Co-authored by: Martijn Muijsers <martijnmuijsers@live.nl>
2No2Name <2No2Name@web.de>
As part of: Gale (https://github.com/GaleMC/Gale/blob/276e903b2688f23b19bdc8d493c0bf87656d2400/patches/server/0087-Replace-AI-attributes-with-optimized-collections.patch)
Lithium (https://github.com/CaffeineMC/lithium-fabric)
Licensed under: LGPL-3.0 (https://www.gnu.org/licenses/lgpl-3.0.html)
diff --git a/net/minecraft/world/entity/ai/attributes/AttributeMap.java b/net/minecraft/world/entity/ai/attributes/AttributeMap.java
index 68bad6752bd313ca3ebde7fd4e3b2c46c5dee3ef..c951a04126a75c7caf046237ae00768a1114c03f 100644
--- a/net/minecraft/world/entity/ai/attributes/AttributeMap.java
+++ b/net/minecraft/world/entity/ai/attributes/AttributeMap.java
@@ -14,9 +14,11 @@ import net.minecraft.resources.Identifier;
import org.jspecify.annotations.Nullable;
public class AttributeMap {
- private final Map<Holder<Attribute>, AttributeInstance> attributes = new Object2ObjectOpenHashMap<>();
- private final Set<AttributeInstance> attributesToSync = new ObjectOpenHashSet<>();
- private final Set<AttributeInstance> attributesToUpdate = new ObjectOpenHashSet<>();
+ // Gale start - Lithium - replace AI attributes with optimized collections
+ private final Map<Holder<Attribute>, AttributeInstance> attributes = new it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<>(0);
+ private final Set<AttributeInstance> attributesToSync = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0);
+ private final Set<AttributeInstance> attributesToUpdate = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0);
+ // Gale end - Lithium - replace AI attributes with optimized collections
private final AttributeSupplier supplier;
public AttributeMap(final AttributeSupplier supplier) {
@@ -0,0 +1,42 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Sat, 18 Apr 2026 12:24:51 +0800
Subject: [PATCH] Gale: Skip entity move if movement is zero
Co-authored by: Martijn Muijsers <martijnmuijsers@live.nl>
ishland <ishlandmc@yeah.net>
A part of: Gale (https://github.com/GaleMC/Gale/blob/276e903b2688f23b19bdc8d493c0bf87656d2400/patches/server/0103-Skip-entity-move-if-movement-is-zero.patch)
VMP (https://github.com/RelativityMC/VMP-fabric)
Licensed under: MIT (https://opensource.org/licenses/MIT)
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index 6b3c691dcc9c9e4a8b2b43110aedd5d5131a9520..d1298c499f6551d7a084f2d1ccd2a2b8b9a2475b 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -1155,8 +1155,14 @@ public abstract class Entity
private double moveStartY;
private double moveStartZ;
// Paper end - detailed watchdog information
+ private boolean boundingBoxChanged = false; // Gale - VMP - skip entity move if movement is zero
public void move(final MoverType moverType, Vec3 delta) {
+ // Gale start - VMP - skip entity move if movement is zero
+ if (!this.boundingBoxChanged && delta.equals(Vec3.ZERO)) {
+ return;
+ }
+ // Gale end - VMP - skip entity move if movement is zero
final Vec3 originalMovement = delta; // Paper - Expose pre-collision velocity
// Paper start - detailed watchdog information
ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread("Cannot move an entity off-main");
@@ -5634,6 +5640,11 @@ public abstract class Entity
}
public final void setBoundingBox(final AABB bb) {
+ // Gale start - VMP - skip entity move if movement is zero
+ if (!this.bb.equals(bb)) {
+ this.boundingBoxChanged = true;
+ }
+ // Gale end - VMP - skip entity move if movement is zero
// CraftBukkit start - block invalid bounding boxes
double minX = bb.minX,
minY = bb.minY,
@@ -0,0 +1,36 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Sun, 17 May 2026 08:47:31 +0800
Subject: [PATCH] Gale Store mob counts in an array
License: MIT (https://opensource.org/licenses/MIT)
Gale - https://github.com/GaleMC/Gale
This patch is based on the following mixin:
"com/ishland/vmp/mixins/general/spawn_density_cap/MixinSpawnDensityCapperDensityCap.java"
By: ishland <ishlandmc@yeah.net>
As part of: VMP (https://github.com/RelativityMC/VMP-fabric)
Licensed under: MIT (https://opensource.org/licenses/MIT)
diff --git a/net/minecraft/world/level/LocalMobCapCalculator.java b/net/minecraft/world/level/LocalMobCapCalculator.java
index 5b3808e6ff58d350fe3fd65fb56e8f209e1b2c93..150dfb4c8465dea0139bdbf804a84ce28e8d9023 100644
--- a/net/minecraft/world/level/LocalMobCapCalculator.java
+++ b/net/minecraft/world/level/LocalMobCapCalculator.java
@@ -42,14 +42,14 @@ public class LocalMobCapCalculator {
}
private static class MobCounts {
- private final Object2IntMap<MobCategory> counts = new Object2IntOpenHashMap<>(MobCategory.values().length);
+ private final int[] counts = new int[MobCategory.values().length]; // Gale - VMP - store mob counts in an array
public void add(final MobCategory category) {
- this.counts.computeInt(category, (k, count) -> count == null ? 1 : count + 1);
+ this.counts[category.ordinal()]++; // Gale - VMP - store mob counts in an array
}
public boolean canSpawn(final MobCategory category) {
- return this.counts.getOrDefault(category, 0) < category.getMaxInstancesPerChunk();
+ return this.counts[category.ordinal()] < category.getMaxInstancesPerChunk(); // Gale - VMP - store mob counts in an array
}
}
}
@@ -0,0 +1,113 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Thu, 4 Jun 2026 17:15:00 +0800
Subject: [PATCH] Gale: Optimize random calls in chunk ticking
License: GPL-3.0-only (https://www.gnu.org/licenses/gpl-3.0.html)
Gale - https://github.com/GaleMC/Gale
This patch is based on the following patch:
"Optimize random calls in chunk ticking"
By: Paul Sauve <paul@technove.co>
As part of: Airplane (https://github.com/TECHNOVE/Airplane)
Licensed under: GPL-3.0-only (https://www.gnu.org/licenses/gpl-3.0.html)
The patch also received the following subsequent modification:
By: Kevin Raneri <kevin.raneri@gmail.com>
As part of: Pufferfish (https://github.com/pufferfish-gg/Pufferfish)
Licensed under: GPL-3.0-only (https://www.gnu.org/licenses/gpl-3.0.html)
* Description *
Throttling of ice and snow tick has been moved to another patch as
configurable ice and snow tick chance.
* Airplane description *
Especially at over 30,000 chunks these random calls are fairly heavy. We
use a different method here for checking lightning, and for checking
ice.
Lightning: Each chunk now keeps an int of how many ticks until the
lightning should strike. This int is a random number from 0 to 100000 * 2,
the multiplication is required to keep the probability the same.
Ice and snow: We just generate a single random number 0-16 and increment
it, while checking if it's 0 for the current chunk.
Depending on configuration for things that tick in a chunk, this is a
5-10% improvement.
* Airplane copyright *
Airplane
Copyright (C) 2020 Technove LLC
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 85eed61919b3c2ef93943e1c1b50fbcbca6fb319..3fda5043937af3d66ab44c41b7e76822a3ddf53b 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -1059,7 +1059,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
}
// Paper start - optimise random ticking
- private final io.papermc.paper.threadedregions.util.SimpleThreadLocalRandomSource simpleRandom = io.papermc.paper.threadedregions.util.SimpleThreadLocalRandomSource.INSTANCE; // Folia - region threading
+ public final io.papermc.paper.threadedregions.util.SimpleThreadLocalRandomSource simpleRandom = io.papermc.paper.threadedregions.util.SimpleThreadLocalRandomSource.INSTANCE; // Folia - region threading // Luminol - Make public for : Gale: Optimize random calls in chunk ticking
private void optimiseRandomTick(final LevelChunk chunk, final int tickSpeed) {
final LevelChunkSection[] sections = chunk.getSections();
@@ -1141,7 +1141,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
int minZ = chunkPos.getMinBlockZ();
ProfilerFiller profiler = Profiler.get();
profiler.push("thunder");
- if (!this.paperConfig().environment.disableThunder && raining && this.isThundering() && this.spigotConfig.thunderChance > 0 && this.random.nextInt(this.spigotConfig.thunderChance) == 0) { // Spigot // Paper - Option to disable thunder
+ if (!this.paperConfig().environment.disableThunder && raining && this.isThundering() && this.spigotConfig.thunderChance > 0 /*&& this.random.nextInt(this.spigotConfig.thunderChance) == 0*/ && chunk.shouldDoLightning(this.random)) { // Spigot // Paper - Option to disable thunder // Gale - Airplane - optimize random calls in chunk ticking - replace random with shouldDoLightning
BlockPos pos = this.findLightningTargetAround(this.getBlockRandomPos(minX, 0, minZ, 15));
if (this.isRainingAt(pos)) {
DifficultyInstance difficulty = this.getCurrentDifficultyAt(pos);
diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java
index a74eb2079863e4aaa8e982497c0fcd890d7de44b..3a727763cfee696c9fc87dda90260558936043bf 100644
--- a/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/net/minecraft/world/level/chunk/LevelChunk.java
@@ -144,6 +144,19 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot
}
// Paper end - get block chunk optimisation
+ // Gale start - Airplane - optimize random calls in chunk ticking - instead of using a random every time the chunk is ticked, define when lightning strikes preemptively
+ private int lightningTick;
+ // shouldDoLightning compiles down to 29 bytes, which with the default of 35 byte inlining should guarantee an inline
+ public final boolean shouldDoLightning(net.minecraft.util.RandomSource random) {
+ if (this.lightningTick-- <= 0) {
+ this.lightningTick = random.nextInt(this.level.spigotConfig.thunderChance) << 1;
+ return true;
+ }
+ return false;
+ }
+ // Gale end - Airplane - optimize random calls in chunk ticking - instead of using a random every time the chunk is ticked, define when lightning strikes preemptively
+
+
public LevelChunk(final Level level, final ChunkPos pos) {
this(level, pos, UpgradeData.EMPTY, new LevelChunkTicks<>(), new LevelChunkTicks<>(), 0L, null, null, null);
}
@@ -180,6 +193,8 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot
this.debug = !empty && this.level.isDebug();
this.defaultBlockState = empty ? VOID_AIR_BLOCKSTATE : AIR_BLOCKSTATE;
// Paper end - get block chunk optimisation
+
+ this.lightningTick = io.papermc.paper.threadedregions.util.SimpleThreadLocalRandomSource.INSTANCE.nextInt(100000) << 1; // Gale - Airplane - optimize random calls in chunk ticking - initialize lightning tick
}
public LevelChunk(final ServerLevel level, final ProtoChunk protoChunk, final LevelChunk.@Nullable PostLoadProcessor postLoad) {
@@ -0,0 +1,58 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Thu, 4 Jun 2026 17:22:55 +0800
Subject: [PATCH] Gale: Reduce enderman teleport chunk lookups
License: GPL-3.0-only (https://www.gnu.org/licenses/gpl-3.0.html)
Gale - https://github.com/GaleMC/Gale
This patch is based on the following patch:
"Reduce chunk loading & lookups"
By: Paul Sauve <paul@technove.co>
As part of: Airplane (https://github.com/TECHNOVE/Airplane)
Licensed under: GPL-3.0-only (https://www.gnu.org/licenses/gpl-3.0.html)
* Airplane copyright *
Airplane
Copyright (C) 2020 Technove LLC
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
diff --git a/net/minecraft/world/entity/monster/EnderMan.java b/net/minecraft/world/entity/monster/EnderMan.java
index 9b4ae8afda1f2202761cb49bc4f1181ce4eb55d5..d2ac2a0c5bb18c2bca18e5166ed65afd01166e04 100644
--- a/net/minecraft/world/entity/monster/EnderMan.java
+++ b/net/minecraft/world/entity/monster/EnderMan.java
@@ -298,11 +298,19 @@ public class EnderMan extends Monster implements NeutralMob {
private boolean teleport(final double x, final double y, final double z) {
BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(x, y, z);
- while (pos.getY() > this.level().getMinY() && !this.level().getBlockState(pos).blocksMotion()) {
+ // Gale start - Airplane - single chunk lookup
+ net.minecraft.world.level.chunk.LevelChunk chunk = this.level().getChunkIfLoaded(pos);
+
+ if (chunk == null) {
+ return false;
+ }
+
+ while (pos.getY() > this.level().getMinY() && !chunk.getBlockState(pos).blocksMotion()) {
+ // Gale end - Airplane - single chunk lookup
pos.move(Direction.DOWN);
}
- BlockState blockState = this.level().getBlockState(pos);
+ BlockState blockState = chunk.getBlockState(pos); // Gale - Airplane - single chunk lookup
boolean couldStandOn = blockState.blocksMotion();
boolean isWet = blockState.getFluidState().is(FluidTags.WATER);
if (couldStandOn && !isWet) {
@@ -0,0 +1,45 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Thu, 4 Jun 2026 17:28:24 +0800
Subject: [PATCH] Gale: Cache ShapePairKey hash
License: LGPL-3.0-only (https://www.gnu.org/licenses/lgpl-3.0.html)
Gale - https://github.com/GaleMC/Gale
The JMH benchmark of this patch can be found in SunBox's `RecordHashCode`
diff --git a/net/minecraft/world/level/block/Block.java b/net/minecraft/world/level/block/Block.java
index 423c45d7be8ac0c27fd3caa0aa8f15f25ad4a3a6..1af396a051cc47dccb973ca864cf1ec2e8b756ed 100644
--- a/net/minecraft/world/level/block/Block.java
+++ b/net/minecraft/world/level/block/Block.java
@@ -696,7 +696,20 @@ public class Block extends BlockBehaviour implements ItemLike {
}
// CraftBukkit end
- private record ShapePairKey(VoxelShape first, VoxelShape second) {
+ // Gale start - cache ShapePairKey hash
+ static class ShapePairKey {
+
+ private final VoxelShape first;
+ private final VoxelShape second;
+ private final int hash;
+
+ private ShapePairKey(VoxelShape first, VoxelShape second) {
+ this.first = first;
+ this.second = second;
+ this.hash = System.identityHashCode(this.first) * 31 + System.identityHashCode(this.second);
+ }
+ // Gale end - cache ShapePairKey hash
+
@Override
public boolean equals(final Object o) {
return o instanceof Block.ShapePairKey that && this.first == that.first && this.second == that.second;
@@ -704,7 +717,7 @@ public class Block extends BlockBehaviour implements ItemLike {
@Override
public int hashCode() {
- return System.identityHashCode(this.first) * 31 + System.identityHashCode(this.second);
+ return this.hash; // Gale - cache ShapePairKey hash
}
}
@@ -0,0 +1,35 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Thu, 4 Jun 2026 17:30:18 +0800
Subject: [PATCH] Gale: For collision check has physics before same vehicle
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
License: GPL-3.0-only (https://www.gnu.org/licenses/gpl-3.0.html)
Gale - https://github.com/GaleMC/Gale
This patch is based on the following patch:
"Swaps the predicate order of collision"
By: ㄗㄠˋ ㄑㄧˊ <tsao-chi@the-lingo.org>
As part of: Akarin (https://github.com/Akarin-project/Akarin)
Licensed under: GPL-3.0-only (https://www.gnu.org/licenses/gpl-3.0.html)
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index d1298c499f6551d7a084f2d1ccd2a2b8b9a2475b..1888210d014f9c00316059a7ccce086dfb0e3a0f 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -2441,8 +2441,8 @@ public abstract class Entity
}
public void push(final Entity entity) {
+ if (!entity.noPhysics && !this.noPhysics) { // Gale - Akarin - collision physics check before vehicle check
if (!this.isPassengerOfSameVehicle(entity)) {
- if (!entity.noPhysics && !this.noPhysics) {
if (this.level.paperConfig().collisions.onlyPlayersCollide && !(entity instanceof ServerPlayer || this instanceof ServerPlayer)) return; // Paper - Collision option for requiring a player participant
double xa = entity.getX() - this.getX();
double za = entity.getZ() - this.getZ();
@@ -0,0 +1,32 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Thu, 4 Jun 2026 17:32:39 +0800
Subject: [PATCH] Gale: Skip negligible planar movement multiplication
License: GPL-3.0-only (https://www.gnu.org/licenses/gpl-3.0.html)
Gale - https://github.com/GaleMC/Gale
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index 1888210d014f9c00316059a7ccce086dfb0e3a0f..00bc928a19cb877698b34b7c039bee5fe8b5cda5 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -1302,8 +1302,17 @@ public abstract class Entity
}
}
- float blockSpeedFactor = this.getBlockSpeedFactor();
- this.setDeltaMovement(this.getDeltaMovement().multiply(blockSpeedFactor, 1.0, blockSpeedFactor));
+ // Gale start - skip negligible planar movement multiplication
+ Vec3 oldDeltaMovement = this.getDeltaMovement();
+ if (oldDeltaMovement.x < -1e-6 || oldDeltaMovement.x > 1e-6 || oldDeltaMovement.z < -1e-6 || oldDeltaMovement.z > 1e-6) {
+ // Gale end - skip negligible planar movement multiplication
+ float blockSpeedFactor = this.getBlockSpeedFactor();
+ // Gale start - skip negligible planar movement multiplication
+ if (blockSpeedFactor < 1 - 1e-6 || blockSpeedFactor > 1 + 1e-6) {
+ this.setDeltaMovement(oldDeltaMovement.multiply(blockSpeedFactor, 1.0, blockSpeedFactor));
+ }
+ }
+ // Gale end - skip negligible planar movement multiplication
profiler.pop();
}
}
@@ -0,0 +1,26 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Thu, 4 Jun 2026 17:34:43 +0800
Subject: [PATCH] Gale: Optimize matching item checks
License: GPL-3.0-only (https://www.gnu.org/licenses/gpl-3.0.html)
Gale - https://github.com/GaleMC/Gale
diff --git a/net/minecraft/world/item/ItemStack.java b/net/minecraft/world/item/ItemStack.java
index 5cbfc9e178b5823f92c0afedb1b0248d34c96292..88a5453ac9e3415b5b5c5e81899b9b13627ca817 100644
--- a/net/minecraft/world/item/ItemStack.java
+++ b/net/minecraft/world/item/ItemStack.java
@@ -841,11 +841,11 @@ public final class ItemStack implements DataComponentHolder, ItemInstance, Chang
}
public static boolean isSameItem(final ItemStack a, final ItemStack b) {
- return a.is(b.getItem());
+ return a == b || a.is(b.getItem()); // Gale - optimize identical item checks
}
public static boolean isSameItemSameComponents(final ItemStack a, final ItemStack b) {
- return a.is(b.getItem()) && (a.isEmpty() && b.isEmpty() || Objects.equals(a.components, b.components));
+ return a == b || a.is(b.getItem()) && (a.isEmpty() && b.isEmpty() || Objects.equals(a.components, b.components)); // Gale - optimize identical item checks
}
public static boolean matchesIgnoringComponents(final ItemStack a, final ItemStack b, final Predicate<DataComponentType<?>> ignoredPredicate) {
@@ -0,0 +1,42 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Sat, 20 Jun 2026 23:16:15 +0800
Subject: [PATCH] Gale Reduce lambda and Optional allocation in
EntityBasedExplosionDamageCalculator
License: LGPL-3.0-only (https://www.gnu.org/licenses/lgpl-3.0.html)
Gale - https://github.com/GaleMC/Gale
This patch is based on the following mixin:
"net/caffeinemc/mods/lithium/mixin/alloc/explosion_behavior/EntityBasedExplosionDamageCalculatorMixin.java"
By: 2No2Name <2No2Name@web.de>
As part of: Lithium (https://github.com/CaffeineMC/lithium)
Licensed under: LGPL-3.0-only (https://www.gnu.org/licenses/lgpl-3.0.html)
diff --git a/net/minecraft/world/level/EntityBasedExplosionDamageCalculator.java b/net/minecraft/world/level/EntityBasedExplosionDamageCalculator.java
index 356a3a0dda09af007c3fbddfe360b38f4d7204ce..aff8d67c9b9797402a3bf6c5bce54af19af0ade7 100644
--- a/net/minecraft/world/level/EntityBasedExplosionDamageCalculator.java
+++ b/net/minecraft/world/level/EntityBasedExplosionDamageCalculator.java
@@ -17,8 +17,20 @@ public class EntityBasedExplosionDamageCalculator extends ExplosionDamageCalcula
public Optional<Float> getBlockExplosionResistance(
final Explosion explosion, final BlockGetter level, final BlockPos pos, final BlockState block, final FluidState fluid
) {
- return super.getBlockExplosionResistance(explosion, level, pos, block, fluid)
- .map(resistance -> this.source.getBlockExplosionResistance(explosion, level, pos, block, fluid, resistance));
+ // Gale start - Lithium - reduce lambda and Optional allocation in EntityBasedExplosionDamageCalculator
+ Optional<Float> optionalBlastResistance = super.getBlockExplosionResistance(explosion, level, pos, block, fluid);
+
+ if (optionalBlastResistance.isPresent()) {
+ float resistance = optionalBlastResistance.get();
+ float effectiveExplosionResistance = this.source.getBlockExplosionResistance(explosion, level, pos, block, fluid, resistance);
+
+ if (effectiveExplosionResistance != resistance) {
+ return Optional.of(effectiveExplosionResistance);
+ }
+ }
+
+ return optionalBlastResistance;
+ // Gale end - Lithium - reduce lambda and Optional allocation in EntityBasedExplosionDamageCalculator
}
@Override
@@ -0,0 +1,441 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Sun, 28 Jun 2026 22:45:55 +0800
Subject: [PATCH] Gale Reduce array allocations
License: GPL-3.0-only (https://www.gnu.org/licenses/gpl-3.0.html)
Gale - https://github.com/GaleMC/Gale
Enum's values returns anew array copy of the enums, this behavior is defined in
`src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java#visitEnumDef`
This is a defensive programming strategy to prevent enums from being modified. However,
copying is unnecessary if we only have read calls.
So we can cache the values result to avoid useless allocations.
Cached as the array since it does not create iterator on the enhanced for loop,
But the list does, and may spend more time than iterating using the array.
One-time calls are excluded from this patch, since no need.
The JMH benchmark of this patch can be found in SunBox's `CachedEnumValuesForLoop`
This patch is based on the following patch:
"reduce allocs"
By: Simon Gardling <titaniumtown@gmail.com>
As part of: JettPack (https://gitlab.com/Titaniumtown/JettPack)
Licensed under: GPL-3.0-only (https://www.gnu.org/licenses/gpl-3.0.html)
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java
index 8d9ce3d301d5f7e4106587ae00adb8dd5b8b6f88..dd8a4e445928d93667702dedbd35ab3e0f94f3be 100644
--- a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java
@@ -400,7 +400,6 @@ public final class ChunkEntitySlices {
private static final class BasicEntityList<E extends Entity> {
- private static final Entity[] EMPTY = new Entity[0];
private static final int DEFAULT_CAPACITY = 4;
private E[] storage;
@@ -411,7 +410,7 @@ public final class ChunkEntitySlices {
}
public BasicEntityList(final int cap) {
- this.storage = (E[])(cap <= 0 ? EMPTY : new Entity[cap]);
+ this.storage = (E[])(cap <= 0 ? me.titaniumtown.ArrayConstants.emptyEntityArray : new Entity[cap]);// Gale - JettPack - reduce array allocations
}
public boolean isEmpty() {
@@ -423,7 +422,7 @@ public final class ChunkEntitySlices {
}
private void resize() {
- if (this.storage == EMPTY) {
+ if (this.storage == me.titaniumtown.ArrayConstants.emptyEntityArray) { // Gale - JettPack - reduce array allocations
this.storage = (E[])new Entity[DEFAULT_CAPACITY];
} else {
this.storage = Arrays.copyOf(this.storage, this.storage.length * 2);
diff --git a/net/minecraft/nbt/ByteArrayTag.java b/net/minecraft/nbt/ByteArrayTag.java
index 4fc0cfeabf60d4bd0dcfd9814d13858454bc39da..0e4dbc0c736ec2ce4826a81ef561a4cd7a8b974d 100644
--- a/net/minecraft/nbt/ByteArrayTag.java
+++ b/net/minecraft/nbt/ByteArrayTag.java
@@ -144,7 +144,7 @@ public final class ByteArrayTag implements CollectionTag {
@Override
public void clear() {
- this.data = new byte[0];
+ this.data = me.titaniumtown.ArrayConstants.emptyByteArray; // Gale - JettPack - reduce array allocations
}
@Override
diff --git a/net/minecraft/nbt/IntArrayTag.java b/net/minecraft/nbt/IntArrayTag.java
index 8ed91b84eb90cfe31993fa32b35263e7adea918e..bf4fe7956ceb8c79427e4d89d458ed30ee1dcb54 100644
--- a/net/minecraft/nbt/IntArrayTag.java
+++ b/net/minecraft/nbt/IntArrayTag.java
@@ -151,7 +151,7 @@ public final class IntArrayTag implements CollectionTag {
@Override
public void clear() {
- this.data = new int[0];
+ this.data = me.titaniumtown.ArrayConstants.emptyIntArray; // Gale - JettPack - reduce array allocations
}
@Override
diff --git a/net/minecraft/nbt/LongArrayTag.java b/net/minecraft/nbt/LongArrayTag.java
index fe75d82cb43364ff58a531306bac934cbca4f729..5141d9765dc29f23a2a7f0918d2c76ddfe09fa6f 100644
--- a/net/minecraft/nbt/LongArrayTag.java
+++ b/net/minecraft/nbt/LongArrayTag.java
@@ -150,7 +150,7 @@ public final class LongArrayTag implements CollectionTag {
@Override
public void clear() {
- this.data = new long[0];
+ this.data = me.titaniumtown.ArrayConstants.emptyLongArray; // Gale - JettPack - reduce array allocations
}
@Override
diff --git a/net/minecraft/network/CipherBase.java b/net/minecraft/network/CipherBase.java
index 5a1fcd0a552dfcebff351c408a0403a8017930c1..1cdb3a6292133719b2e6b824ed9f78857f7b595e 100644
--- a/net/minecraft/network/CipherBase.java
+++ b/net/minecraft/network/CipherBase.java
@@ -7,8 +7,8 @@ import javax.crypto.ShortBufferException;
public class CipherBase {
private final Cipher cipher;
- private byte[] heapIn = new byte[0];
- private byte[] heapOut = new byte[0];
+ private byte[] heapIn = me.titaniumtown.ArrayConstants.emptyByteArray; // Gale - JettPack - reduce array allocations
+ private byte[] heapOut = me.titaniumtown.ArrayConstants.emptyByteArray; // Gale - JettPack - reduce array allocations
protected CipherBase(final Cipher cipher) {
this.cipher = cipher;
diff --git a/net/minecraft/network/chat/contents/TranslatableContents.java b/net/minecraft/network/chat/contents/TranslatableContents.java
index b6973f9b48092676029ff58485ec8b8dece7387b..785f5c6f683ffdb73a70afabc42d6c09abb1e1d8 100644
--- a/net/minecraft/network/chat/contents/TranslatableContents.java
+++ b/net/minecraft/network/chat/contents/TranslatableContents.java
@@ -28,7 +28,7 @@ import net.minecraft.util.ExtraCodecs;
import org.jspecify.annotations.Nullable;
public class TranslatableContents implements ComponentContents {
- public static final Object[] NO_ARGS = new Object[0];
+ public static final Object[] NO_ARGS = me.titaniumtown.ArrayConstants.emptyObjectArray; // Gale - JettPack - reduce array allocations
public static final Codec<Object> PRIMITIVE_ARG_CODEC = ExtraCodecs.JAVA.validate(TranslatableContents::filterAllowedArguments);
private static final Codec<Object> ARG_CODEC = Codec.either(PRIMITIVE_ARG_CODEC, ComponentSerialization.CODEC)
.xmap(
diff --git a/net/minecraft/server/level/ServerEntity.java b/net/minecraft/server/level/ServerEntity.java
index 2e71a1cbabf3405e3e4fd5349a1784d895e7e60c..42de919064680b1a464d813976ca5e69a0bd3b01 100644
--- a/net/minecraft/server/level/ServerEntity.java
+++ b/net/minecraft/server/level/ServerEntity.java
@@ -364,7 +364,7 @@ public class ServerEntity {
if (this.entity instanceof LivingEntity livingEntity) {
List<Pair<EquipmentSlot, ItemStack>> slots = Lists.newArrayList();
- for (EquipmentSlot slot : EquipmentSlot.VALUES) {
+ for (EquipmentSlot slot : EquipmentSlot.VALUES_ARRAY) { // Gale - JettPack - reduce array allocations
ItemStack itemStack = livingEntity.getItemBySlot(slot);
if (!itemStack.isEmpty()) {
slots.add(Pair.of(slot, itemStack.copy()));
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index 093c48e2379c01f45277b88f6c1b19b107a46326..b4d8cc8b520266abe1921b5e990c34fce3f1d982 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -1424,7 +1424,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
this.getInventory().getNonEquipmentItems().set(i, net.minecraft.world.item.ItemStack.EMPTY);
}
}
- for (final EquipmentSlot value : EquipmentSlot.VALUES) {
+ for (final EquipmentSlot value : EquipmentSlot.VALUES_ARRAY) { // Gale - JettPack - reduce array allocations
if (this.getInventory().equipment.has(value) && !shouldKeepDeathEventItem(event, this.getInventory().equipment.get(value))) {
this.getInventory().equipment.set(value, net.minecraft.world.item.ItemStack.EMPTY);
}
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 164e2101688a9f29ecfa83f62141d8cb9df22ed1..2a383eee714b7da08b552c5bfb3988fad3cadb93 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -3027,7 +3027,7 @@ public class ServerGamePacketListenerImpl
// SPIGOT-7136 - Allays
if (target instanceof net.minecraft.world.entity.animal.allay.Allay || target instanceof net.minecraft.world.entity.animal.equine.AbstractHorse) { // Paper - Fix horse armor desync
ServerGamePacketListenerImpl.this.send(new net.minecraft.network.protocol.game.ClientboundSetEquipmentPacket(
- target.getId(), java.util.Arrays.stream(net.minecraft.world.entity.EquipmentSlot.values())
+ target.getId(), java.util.Arrays.stream(net.minecraft.world.entity.EquipmentSlot.VALUES_ARRAY) // Gale - JettPack - reduce array allocations
.map((slot) -> com.mojang.datafixers.util.Pair.of(slot, ((LivingEntity) target).getItemBySlot(slot).copy()))
.collect(Collectors.toList()), true)); // Paper - sanitize
player.containerMenu.sendAllDataToRemote();
diff --git a/net/minecraft/server/players/StoredUserList.java b/net/minecraft/server/players/StoredUserList.java
index cf6c71db3dd39094608755998ceff6ca067238f3..1353fdfb68d8dbedafd64ac69d2869b22c6bfe09 100644
--- a/net/minecraft/server/players/StoredUserList.java
+++ b/net/minecraft/server/players/StoredUserList.java
@@ -96,7 +96,7 @@ public abstract class StoredUserList<K, V extends StoredUserEntry<K>> {
}
public String[] getUserList() {
- return this.map.keySet().toArray(new String[0]);
+ return this.map.keySet().toArray(me.titaniumtown.ArrayConstants.emptyStringArray); // Gale - JettPack - reduce array allocations
}
public boolean isEmpty() {
diff --git a/net/minecraft/util/NullOps.java b/net/minecraft/util/NullOps.java
index c7510c99c68c66a64d391b67d93f31cfcd3dccf0..5785ecf52bbd68e6df0f25b917722999addb6a20 100644
--- a/net/minecraft/util/NullOps.java
+++ b/net/minecraft/util/NullOps.java
@@ -171,7 +171,7 @@ public class NullOps implements DynamicOps<Unit> {
@Override
public DataResult<ByteBuffer> getByteBuffer(final Unit input) {
- return DataResult.success(ByteBuffer.wrap(new byte[0]));
+ return DataResult.success(ByteBuffer.wrap(me.titaniumtown.ArrayConstants.emptyByteArray)); // Gale - JettPack - reduce array allocations
}
@Override
diff --git a/net/minecraft/util/ZeroBitStorage.java b/net/minecraft/util/ZeroBitStorage.java
index 3666c3efd188508153b6482db425648e3ca77dc7..6c52fcac63fb2b79472ff10cb9f544f347a3fcdd 100644
--- a/net/minecraft/util/ZeroBitStorage.java
+++ b/net/minecraft/util/ZeroBitStorage.java
@@ -5,7 +5,7 @@ import java.util.function.IntConsumer;
import org.apache.commons.lang3.Validate;
public class ZeroBitStorage implements BitStorage {
- public static final long[] RAW = new long[0];
+ public static final long[] RAW = me.titaniumtown.ArrayConstants.emptyLongArray; // Gale - JettPack - reduce array allocations
private final int size;
public ZeroBitStorage(final int size) {
diff --git a/net/minecraft/world/entity/ConversionType.java b/net/minecraft/world/entity/ConversionType.java
index e5e268a1b2e8c1d728d39e66a53a5ad17fa6694f..59514750c3024d0f16512d2cd2c5b79f297c7beb 100644
--- a/net/minecraft/world/entity/ConversionType.java
+++ b/net/minecraft/world/entity/ConversionType.java
@@ -37,7 +37,7 @@ public enum ConversionType {
}
if (params.keepEquipment()) {
- for (EquipmentSlot slot : EquipmentSlot.VALUES) {
+ for (EquipmentSlot slot : EquipmentSlot.VALUES_ARRAY) { // Gale - JettPack - reduce array allocations
ItemStack itemStack = from.getItemBySlot(slot);
if (!itemStack.isEmpty()) {
to.setItemSlot(slot, itemStack.copyAndClear());
diff --git a/net/minecraft/world/entity/EquipmentSlot.java b/net/minecraft/world/entity/EquipmentSlot.java
index ffb5f07697e9f1fb07f83ea4739c6b9d5a622892..63a9956f4aaf6feb6de2ef59a2211448f816e4fb 100644
--- a/net/minecraft/world/entity/EquipmentSlot.java
+++ b/net/minecraft/world/entity/EquipmentSlot.java
@@ -20,6 +20,7 @@ public enum EquipmentSlot implements StringRepresentable {
SADDLE(EquipmentSlot.Type.SADDLE, 0, 1, 7, "saddle");
public static final int NO_COUNT_LIMIT = 0;
+ public static final EquipmentSlot[] VALUES_ARRAY = values(); // Gale - JettPack - reduce array allocations
public static final List<EquipmentSlot> VALUES = List.of(values());
public static final IntFunction<EquipmentSlot> BY_ID = ByIdMap.continuous(s -> s.id, values(), ByIdMap.OutOfBoundsStrategy.ZERO);
public static final StringRepresentable.EnumCodec<EquipmentSlot> CODEC = StringRepresentable.fromEnum(EquipmentSlot::values);
diff --git a/net/minecraft/world/entity/EquipmentSlotGroup.java b/net/minecraft/world/entity/EquipmentSlotGroup.java
index e9b1290f24ba30663110abc61310d0a1e784e5a1..0112c19b99c62dde19af1009030c1645297163d9 100644
--- a/net/minecraft/world/entity/EquipmentSlotGroup.java
+++ b/net/minecraft/world/entity/EquipmentSlotGroup.java
@@ -24,6 +24,7 @@ public enum EquipmentSlotGroup implements StringRepresentable, Iterable<Equipmen
BODY(9, "body", EquipmentSlot.BODY),
SADDLE(10, "saddle", EquipmentSlot.SADDLE);
+ public static final EquipmentSlotGroup[] VALUES_ARRAY = EquipmentSlotGroup.values(); // Gale - JettPack - reduce array allocations
public static final IntFunction<EquipmentSlotGroup> BY_ID = ByIdMap.continuous(s -> s.id, values(), ByIdMap.OutOfBoundsStrategy.ZERO);
public static final Codec<EquipmentSlotGroup> CODEC = StringRepresentable.fromEnum(EquipmentSlotGroup::values);
public static final StreamCodec<ByteBuf, EquipmentSlotGroup> STREAM_CODEC = ByteBufCodecs.idMapper(BY_ID, s -> s.id);
diff --git a/net/minecraft/world/entity/EquipmentTable.java b/net/minecraft/world/entity/EquipmentTable.java
index 4b018c64d3123bb7a0687491d2ac3a535302890c..0e4d6c9a35d29c240a0cf30694211ea3db66895c 100644
--- a/net/minecraft/world/entity/EquipmentTable.java
+++ b/net/minecraft/world/entity/EquipmentTable.java
@@ -35,7 +35,7 @@ public record EquipmentTable(ResourceKey<LootTable> lootTable, Map<EquipmentSlot
}
private static Map<EquipmentSlot, Float> createForAllSlots(final float dropChance) {
- return createForAllSlots(List.of(EquipmentSlot.values()), dropChance);
+ return createForAllSlots(List.of(EquipmentSlot.VALUES_ARRAY), dropChance); // Gale - JettPack - reduce array allocations
}
private static Map<EquipmentSlot, Float> createForAllSlots(final List<EquipmentSlot> slots, final float dropChance) {
diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java
index 1cb8265c1313ad4893d45c344b03007fc4e2de4b..fcaeb471dada57e9ba4d224693cefbff2fa631be 100644
--- a/net/minecraft/world/entity/LivingEntity.java
+++ b/net/minecraft/world/entity/LivingEntity.java
@@ -3609,7 +3609,7 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
Map<org.bukkit.inventory.EquipmentSlot, io.papermc.paper.event.entity.EntityEquipmentChangedEvent.EquipmentChange> equipmentChanges = null;
// Paper end - EntityEquipmentChangedEvent
- for (EquipmentSlot slot : EquipmentSlot.VALUES) {
+ for (EquipmentSlot slot : EquipmentSlot.VALUES_ARRAY) { // Gale - JettPack - reduce array allocations
ItemStack previous = lastEquipmentItems.get(slot);
ItemStack current = this.getItemBySlot(slot);
if (this.equipmentHasChanged(previous, current)) {
@@ -3892,7 +3892,7 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
protected boolean canGlide() {
if (!this.onGround() && !this.isPassenger() && !this.hasEffect(MobEffects.LEVITATION)) {
- for (EquipmentSlot slot : EquipmentSlot.VALUES) {
+ for (EquipmentSlot slot : EquipmentSlot.VALUES_ARRAY) { // Gale - JettPack - reduce array allocations
if (canGlideUsing(this.getItemBySlot(slot), slot)) {
return true;
}
diff --git a/net/minecraft/world/entity/decoration/ArmorStand.java b/net/minecraft/world/entity/decoration/ArmorStand.java
index 9e4293c6df1851a852423b469577388d2254e169..952b09fba4cf7de90110a7aa4b96dc03afab25e1 100644
--- a/net/minecraft/world/entity/decoration/ArmorStand.java
+++ b/net/minecraft/world/entity/decoration/ArmorStand.java
@@ -484,7 +484,7 @@ public class ArmorStand extends LivingEntity {
if (this.deathDropItems == null) this.deathDropItems = new java.util.ArrayList<>(); // Paper
this.dropAllDeathLoot(level, source);
- for (EquipmentSlot slot : EquipmentSlot.VALUES) {
+ for (EquipmentSlot slot : EquipmentSlot.VALUES_ARRAY) { // Gale - JettPack - reduce array allocations
this.postDeathEventTasks.add(() -> this.equipment.set(slot, ItemStack.EMPTY)); // Paper - move equipment removal past event call
ItemStack itemStack = this.equipment.get(slot); // Paper
if (!itemStack.isEmpty() && !EnchantmentHelper.has(itemStack, EnchantmentEffectComponents.PREVENT_EQUIPMENT_DROP)) {
diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java
index a4bc767b799c050ce07a3685f655b4ba947d2466..a447b9fbb579591c62b3d8f8e2527796feefc4a0 100644
--- a/net/minecraft/world/entity/player/Player.java
+++ b/net/minecraft/world/entity/player/Player.java
@@ -346,7 +346,7 @@ public abstract class Player extends Avatar implements ContainerUser {
}
private boolean isEquipped(final Item item) {
- for (EquipmentSlot slot : EquipmentSlot.VALUES) {
+ for (EquipmentSlot slot : EquipmentSlot.VALUES_ARRAY) { // Gale - JettPack - reduce array allocations
ItemStack itemStack = this.getItemBySlot(slot);
Equippable equippable = itemStack.get(DataComponents.EQUIPPABLE);
if (itemStack.is(item) && equippable != null && equippable.slot() == slot) {
diff --git a/net/minecraft/world/item/ItemStack.java b/net/minecraft/world/item/ItemStack.java
index 88a5453ac9e3415b5b5c5e81899b9b13627ca817..a6805d436fd3675e385cbf8a3e5b262ae694ed90 100644
--- a/net/minecraft/world/item/ItemStack.java
+++ b/net/minecraft/world/item/ItemStack.java
@@ -1182,7 +1182,7 @@ public final class ItemStack implements DataComponentHolder, ItemInstance, Chang
private void addAttributeTooltips(final Consumer<Component> consumer, final TooltipDisplay display, final @Nullable Player player) {
if (display.shows(DataComponents.ATTRIBUTE_MODIFIERS)) {
- for (EquipmentSlotGroup slot : EquipmentSlotGroup.values()) {
+ for (EquipmentSlotGroup slot : EquipmentSlotGroup.VALUES_ARRAY) { // Gale - JettPack - reduce array allocations
MutableBoolean first = new MutableBoolean(true);
this.forEachModifier(slot, (attribute, modifier, tooltip) -> {
if (tooltip != ItemAttributeModifiers.Display.hidden()) {
diff --git a/net/minecraft/world/item/crafting/ShapedRecipePattern.java b/net/minecraft/world/item/crafting/ShapedRecipePattern.java
index b2ca30b84a6f498a2b18145eb1efaea7c4c5c943..f60e6188d25c13fc433931b0e8760869ccee8fb0 100644
--- a/net/minecraft/world/item/crafting/ShapedRecipePattern.java
+++ b/net/minecraft/world/item/crafting/ShapedRecipePattern.java
@@ -121,7 +121,7 @@ public final class ShapedRecipePattern {
}
if (pattern.size() == bottom) {
- return new String[0];
+ return me.titaniumtown.ArrayConstants.emptyStringArray; // Gale - JettPack - reduce array allocations
}
String[] result = new String[pattern.size() - bottom - top];
diff --git a/net/minecraft/world/item/enchantment/Enchantment.java b/net/minecraft/world/item/enchantment/Enchantment.java
index 0d981b86fa7ee8713e2a419fc2f377defbc2ed9c..26d850a0ee40c5042e99689f8bd67d8859c42d40 100644
--- a/net/minecraft/world/item/enchantment/Enchantment.java
+++ b/net/minecraft/world/item/enchantment/Enchantment.java
@@ -107,7 +107,7 @@ public record Enchantment(Component description, Enchantment.EnchantmentDefiniti
public Map<EquipmentSlot, ItemStack> getSlotItems(final LivingEntity entity) {
Map<EquipmentSlot, ItemStack> itemStacks = Maps.newEnumMap(EquipmentSlot.class);
- for (EquipmentSlot slot : EquipmentSlot.VALUES) {
+ for (EquipmentSlot slot : EquipmentSlot.VALUES_ARRAY) { // Gale - JettPack - reduce array allocations
if (this.matchingSlot(slot)) {
ItemStack itemStack = entity.getItemBySlot(slot);
if (!itemStack.isEmpty()) {
diff --git a/net/minecraft/world/item/enchantment/EnchantmentHelper.java b/net/minecraft/world/item/enchantment/EnchantmentHelper.java
index ed84782f4fcaec8f12ab68641a42ad6e09079875..4f2017c4ca12dcedb9587e0d3d3c9a6be4996167 100644
--- a/net/minecraft/world/item/enchantment/EnchantmentHelper.java
+++ b/net/minecraft/world/item/enchantment/EnchantmentHelper.java
@@ -157,7 +157,7 @@ public class EnchantmentHelper {
}
private static void runIterationOnEquipment(final LivingEntity owner, final EnchantmentHelper.EnchantmentInSlotVisitor method) {
- for (EquipmentSlot slot : EquipmentSlot.VALUES) {
+ for (EquipmentSlot slot : EquipmentSlot.VALUES_ARRAY) { // Gale - JettPack - reduce array allocations
runIterationOnItem(owner.getItemBySlot(slot), slot, owner, method);
}
}
@@ -495,7 +495,7 @@ public class EnchantmentHelper {
) {
List<EnchantedItemInUse> items = new ArrayList<>();
- for (EquipmentSlot slot : EquipmentSlot.VALUES) {
+ for (EquipmentSlot slot : EquipmentSlot.VALUES_ARRAY) { // Gale - JettPack - reduce array allocations
ItemStack item = source.getItemBySlot(slot);
if (predicate.test(item)) {
ItemEnchantments enchantments = item.getOrDefault(DataComponents.ENCHANTMENTS, ItemEnchantments.EMPTY);
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
index 87fb17917f4cecfef2d5d03bf8fd6e61d075212e..000e4924e8e338728715a3946002903a3118a98a 100644
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
@@ -1966,7 +1966,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
public org.bukkit.entity.Entity[] getChunkEntities(int chunkX, int chunkZ) {
ca.spottedleaf.moonrise.patches.chunk_system.level.entity.ChunkEntitySlices slices = ((ServerLevel)this).moonrise$getEntityLookup().getChunk(chunkX, chunkZ);
if (slices == null) {
- return new org.bukkit.entity.Entity[0];
+ return me.titaniumtown.ArrayConstants.emptyBukkitEntityArray; // Gale - JettPack - reduce array allocations
}
List<org.bukkit.entity.Entity> ret = new java.util.ArrayList<>();
@@ -1977,7 +1977,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
}
}
- return ret.toArray(new org.bukkit.entity.Entity[0]);
+ return ret.toArray(me.titaniumtown.ArrayConstants.emptyBukkitEntityArray); // Gale - JettPack - reduce array allocations
}
// Paper end - rewrite chunk system
diff --git a/net/minecraft/world/level/block/ComposterBlock.java b/net/minecraft/world/level/block/ComposterBlock.java
index 0c8cc60efc8999e76632c605fa7d2b0cfa347e17..4e0c19970595dd2899a2e08ee122266c2338d84d 100644
--- a/net/minecraft/world/level/block/ComposterBlock.java
+++ b/net/minecraft/world/level/block/ComposterBlock.java
@@ -434,7 +434,7 @@ public class ComposterBlock extends Block implements WorldlyContainerHolder {
@Override
public int[] getSlotsForFace(final Direction direction) {
- return new int[0];
+ return me.titaniumtown.ArrayConstants.emptyIntArray; // Gale - JettPack - reduce array allocations
}
@Override
@@ -469,7 +469,7 @@ public class ComposterBlock extends Block implements WorldlyContainerHolder {
@Override
public int[] getSlotsForFace(final Direction direction) {
- return direction == Direction.UP ? new int[]{0} : new int[0];
+ return direction == Direction.UP ? me.titaniumtown.ArrayConstants.zeroSingletonIntArray : me.titaniumtown.ArrayConstants.emptyIntArray; // Gale - JettPack - reduce array allocations
}
@Override
@@ -521,7 +521,7 @@ public class ComposterBlock extends Block implements WorldlyContainerHolder {
@Override
public int[] getSlotsForFace(final Direction direction) {
- return direction == Direction.DOWN ? new int[]{0} : new int[0];
+ return direction == Direction.DOWN ? me.titaniumtown.ArrayConstants.zeroSingletonIntArray : me.titaniumtown.ArrayConstants.emptyIntArray; // Gale - JettPack - reduce array allocations
}
@Override
diff --git a/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java b/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java
index 9006438c36771794c9375babffa8c5944a38983d..ac93d2992fe780133439302ce5b851f9ac0c7353 100644
--- a/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java
@@ -44,7 +44,7 @@ public abstract class AbstractFurnaceBlockEntity extends BaseContainerBlockEntit
protected static final int SLOT_FUEL = 1;
protected static final int SLOT_RESULT = 2;
public static final int DATA_LIT_TIME = 0;
- private static final int[] SLOTS_FOR_UP = new int[]{0};
+ private static final int[] SLOTS_FOR_UP = me.titaniumtown.ArrayConstants.zeroSingletonIntArray; // Gale - JettPack - reduce array allocations
private static final int[] SLOTS_FOR_DOWN = new int[]{2, 1};
private static final int[] SLOTS_FOR_SIDES = new int[]{1};
public static final int DATA_LIT_DURATION = 1;
diff --git a/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java b/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
index e00caf8dc68ce49f4011f881f3558347fe848822..a9201522bdca5a6e6aae48624d3f95fcb0e3d941 100644
--- a/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
+++ b/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
@@ -268,7 +268,7 @@ public class MapItemSavedData extends SavedData {
}
private static boolean hasMapInvisibilityItemEquipped(final Player player) {
- for (EquipmentSlot equipmentSlot : EquipmentSlot.values()) {
+ for (EquipmentSlot equipmentSlot : EquipmentSlot.VALUES_ARRAY) { // Gale - JettPack - reduce array allocations
if (equipmentSlot != EquipmentSlot.MAINHAND
&& equipmentSlot != EquipmentSlot.OFFHAND
&& player.getItemBySlot(equipmentSlot).is(ItemTags.MAP_INVISIBILITY_EQUIPMENT)) {
@@ -0,0 +1,80 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Fri, 1 May 2026 21:51:11 +0800
Subject: [PATCH] Some optimizations from krypton
A part of krypton's mixin:
https://github.com/astei/krypton/blob/master/src/main/java/me/steinborn/krypton/mixin/shared/network/microopt/StringEncodingMixin.java and https://github.com/astei/krypton/blob/master/src/main/java/me/steinborn/krypton/mixin/shared/network/microopt/VarIntsMixin.java
Original project license: https://github.com/astei/krypton/blob/master/LICENSE
diff --git a/net/minecraft/network/Utf8String.java b/net/minecraft/network/Utf8String.java
index 8b778744a5f01943976f180ad340a1cbce7db506..c46bf9940d4275ac09e8c94c8ae1019312fa90b7 100644
--- a/net/minecraft/network/Utf8String.java
+++ b/net/minecraft/network/Utf8String.java
@@ -33,6 +33,22 @@ public class Utf8String {
}
public static void write(final ByteBuf output, final CharSequence value, final int maxLength) {
+ // Luminol start - Krypton optimizations
+ if (true) {
+ if (value.length() > maxLength) {
+ throw new EncoderException("String too big (was " + value.length() + " characters, max " + maxLength + ")");
+ }
+ int utf8Bytes = ByteBufUtil.utf8Bytes(value);
+ int maxBytesPermitted = ByteBufUtil.utf8MaxBytes(maxLength);
+ if (utf8Bytes > maxBytesPermitted) {
+ throw new EncoderException("String too big (was " + utf8Bytes + " bytes encoded, max " + maxBytesPermitted + ")");
+ } else {
+ VarInt.write(output, utf8Bytes);
+ output.writeCharSequence(value, StandardCharsets.UTF_8);
+ }
+ return;
+ }
+ // Luminol end
if (value.length() > maxLength) {
throw new EncoderException("String too big (was " + value.length() + " characters, max " + maxLength + ")");
}
diff --git a/net/minecraft/network/VarInt.java b/net/minecraft/network/VarInt.java
index 1428ce80e8316a24cfca7b6aafdea8588a5a790e..73b01c4ac8c69f6a984c8f3947cdd2688cd70c14 100644
--- a/net/minecraft/network/VarInt.java
+++ b/net/minecraft/network/VarInt.java
@@ -60,7 +60,8 @@ public class VarInt {
int s = (value & 0x7F | 0x80) << 8 | (value >>> 7);
output.writeShort(s);
} else {
- writeSlow(output, value);
+ // writeSlow(output, value); // Luminol - Krypton optimizations
+ writeVarIntFull(output, value); // Luminol - Krypton optimizations
}
return output;
}
@@ -74,4 +75,27 @@ public class VarInt {
output.writeByte(value);
return output;
}
+ // Luminol start - Krypton optimizations
+ private static void writeVarIntFull(ByteBuf buf, int value) {
+ // See https://steinborn.me/posts/performance/how-fast-can-you-write-a-varint/
+ if ((value & (0xFFFFFFFF << 7)) == 0) {
+ buf.writeByte(value);
+ } else if ((value & (0xFFFFFFFF << 14)) == 0) {
+ int w = (value & 0x7F | 0x80) << 8 | (value >>> 7);
+ buf.writeShort(w);
+ } else if ((value & (0xFFFFFFFF << 21)) == 0) {
+ int w = (value & 0x7F | 0x80) << 16 | ((value >>> 7) & 0x7F | 0x80) << 8 | (value >>> 14);
+ buf.writeMedium(w);
+ } else if ((value & (0xFFFFFFFF << 28)) == 0) {
+ int w = (value & 0x7F | 0x80) << 24 | (((value >>> 7) & 0x7F | 0x80) << 16)
+ | ((value >>> 14) & 0x7F | 0x80) << 8 | (value >>> 21);
+ buf.writeInt(w);
+ } else {
+ int w = (value & 0x7F | 0x80) << 24 | ((value >>> 7) & 0x7F | 0x80) << 16
+ | ((value >>> 14) & 0x7F | 0x80) << 8 | ((value >>> 21) & 0x7F | 0x80);
+ buf.writeInt(w);
+ buf.writeByte(value >>> 28);
+ }
+ }
+ // Luminol end
}
@@ -5,10 +5,10 @@ Subject: [PATCH] Fix datapack command save function
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index fb5f9176916931b23ebf4a5b7ecdfc5285ffbc2b..ecdcf0db97341d2cb460f86f451cecd5c40dbfee 100644
index d2303ad91c9634cdbf599552a6bce7279a1f0e0a..4b6f961e695155db1d6cc86496491017bf3a6383 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -2450,6 +2450,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -2460,6 +2460,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
return this.reloadResources(packsToEnable, io.papermc.paper.event.server.ServerResourcesReloadedEvent.Cause.PLUGIN);
}
@@ -5,7 +5,7 @@ Subject: [PATCH] Add config to disable some check for operators
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index ebede474ca98c82131215913865b9f7bb4d57313..fe6e1aea364620f82de44b942da0d3846c7d17b5 100644
index 2a383eee714b7da08b552c5bfb3988fad3cadb93..6f5d5b123f69b7bdc3960e0ad216ec291aa100c3 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -402,7 +402,7 @@ public class ServerGamePacketListenerImpl
@@ -35,7 +35,7 @@ index ebede474ca98c82131215913865b9f7bb4d57313..fe6e1aea364620f82de44b942da0d384
fail = true;
LOGGER.warn("{} (vehicle of {}) moved wrongly! {}", vehicle.getPlainTextName(), this.player.getPlainTextName(), Math.sqrt(movedDist));
}
@@ -1606,7 +1606,7 @@ public class ServerGamePacketListenerImpl
@@ -1619,7 +1619,7 @@ public class ServerGamePacketListenerImpl
// Paper start - Add fail move event
io.papermc.paper.event.player.PlayerFailMoveEvent event = fireFailMove(io.papermc.paper.event.player.PlayerFailMoveEvent.FailReason.MOVED_TOO_QUICKLY,
targetX, targetY, targetZ, targetYRot, targetXRot, true);
@@ -44,7 +44,7 @@ index ebede474ca98c82131215913865b9f7bb4d57313..fe6e1aea364620f82de44b942da0d384
if (event.getLogWarning()) {
LOGGER.warn("{} moved too quickly! {},{},{}", this.player.getPlainTextName(), xDist, yDist, zDist);
}
@@ -1672,7 +1672,7 @@ public class ServerGamePacketListenerImpl
@@ -1685,7 +1685,7 @@ public class ServerGamePacketListenerImpl
// Paper start - Add fail move event
io.papermc.paper.event.player.PlayerFailMoveEvent event = fireFailMove(io.papermc.paper.event.player.PlayerFailMoveEvent.FailReason.MOVED_WRONGLY,
targetX, targetY, targetZ, targetYRot, targetXRot, true);
@@ -48,7 +48,7 @@ index 9800ae1d3c49800b0c7342d43d696109b5b72cf4..f9a8c79640ed6ca381e4482eaeab6b23
SaveOnCommand.register(this.dispatcher);
SetPlayerIdleTimeoutCommand.register(this.dispatcher);
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index ecdcf0db97341d2cb460f86f451cecd5c40dbfee..437abeb7b2d021b38b438d0e046f05809e196930 100644
index 4b6f961e695155db1d6cc86496491017bf3a6383..e7f7f7927fc1e2ce45ea5dbf7cc9ec19197c599f 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -1652,6 +1652,11 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -5,10 +5,10 @@ Subject: [PATCH] Add config to enable raytracing tracker
diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java
index 849ad46b2941ebba91b428846b3b20d11ee5e1c9..6cd549849bd7fb5d72431f729229d1e967cf0d98 100644
index 163999d50213e0167eb09f0eae883388849f28fa..5196431f52e81608a5575f8a262cd5c855b06182 100644
--- a/net/minecraft/server/level/ChunkMap.java
+++ b/net/minecraft/server/level/ChunkMap.java
@@ -1350,7 +1350,7 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP
@@ -1351,7 +1351,7 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP
double distanceSquared = deltaToPlayerX * deltaToPlayerX + deltaToPlayerZ * deltaToPlayerZ; // Paper
double rangeSquared = visibleRange * visibleRange;
// Paper start - Configurable entity tracking range by Y
@@ -18,7 +18,7 @@ index 849ad46b2941ebba91b428846b3b20d11ee5e1c9..6cd549849bd7fb5d72431f729229d1e9
double rangeY = level.paperConfig().entities.trackingRangeY.get(this.entity, -1);
if (rangeY != -1) {
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index 65e2229f73a6f74c2271c9326aeb46feab51f818..b7cc122d45ecaf922ca1aca8ff5f1632965ad357 100644
index 00bc928a19cb877698b34b7c039bee5fe8b5cda5..db7281fcf1f4dc878a87ef9b8f24f2f9da12238a 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -164,7 +164,7 @@ public abstract class Entity
@@ -30,7 +30,7 @@ index 65e2229f73a6f74c2271c9326aeb46feab51f818..b7cc122d45ecaf922ca1aca8ff5f1632
// CraftBukkit start
private static final int CURRENT_LEVEL = 2;
static boolean isLevelAtLeast(ValueInput input, int level) {
@@ -6505,4 +6505,48 @@ public abstract class Entity
@@ -6579,4 +6579,48 @@ public abstract class Entity
// Paper end
public boolean shouldTickHot() { return this.tickCount > 20 * 10 && this.isAlive(); } // KioCG
@@ -38,10 +38,10 @@ index be357d315f5c83349a7f7287ff45e92137950a8c..a542e38ea6a1e8fc81466acbca071d89
if (count > maxAllowedCount) {
source.sendFailure(Component.translatable("commands.give.failed.toomanyitems", maxAllowedCount, prototypeItemStack.getDisplayName()));
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index fe6e1aea364620f82de44b942da0d3846c7d17b5..fd1ceb8c6f20b515e3a054050feeb443de33038c 100644
index 6f5d5b123f69b7bdc3960e0ad216ec291aa100c3..f036ef938bdd66ab87610e76c8c878bf3d3c86b1 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -3219,7 +3219,7 @@ public class ServerGamePacketListenerImpl
@@ -3232,7 +3232,7 @@ public class ServerGamePacketListenerImpl
} else if (slot.mayPlace(cursor)) {
if (ItemStack.isSameItemSameComponents(clickedItem, cursor)) {
int toPlace = packet.buttonNum() == 0 ? cursor.getCount() : 1;
@@ -50,7 +50,7 @@ index fe6e1aea364620f82de44b942da0d3846c7d17b5..fd1ceb8c6f20b515e3a054050feeb443
toPlace = Math.min(toPlace, slot.container.getMaxStackSize() - clickedItem.getCount());
if (toPlace == 1) {
action = InventoryAction.PLACE_ONE;
@@ -3255,7 +3255,7 @@ public class ServerGamePacketListenerImpl
@@ -3268,7 +3268,7 @@ public class ServerGamePacketListenerImpl
}
} else if (ItemStack.isSameItemSameComponents(cursor, clickedItem)) {
if (clickedItem.getCount() >= 0) {
@@ -59,7 +59,7 @@ index fe6e1aea364620f82de44b942da0d3846c7d17b5..fd1ceb8c6f20b515e3a054050feeb443
// As of 1.5, this is result slots only
action = InventoryAction.PICKUP_ALL;
}
@@ -3472,6 +3472,7 @@ public class ServerGamePacketListenerImpl
@@ -3485,6 +3485,7 @@ public class ServerGamePacketListenerImpl
this.player.containerMenu.broadcastFullState();
} else {
this.player.containerMenu.broadcastChanges();
@@ -67,7 +67,7 @@ index fe6e1aea364620f82de44b942da0d3846c7d17b5..fd1ceb8c6f20b515e3a054050feeb443
}
if (packet.buttonNum() == Inventory.SLOT_OFFHAND && this.player.containerMenu != this.player.inventoryMenu) this.player.containerSynchronizer.sendOffHandSlotChange(); // Paper - update offhand data when the player is clicking in an inventory not their own as the sychronizer does not include offhand slots
if (io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.updateEquipmentOnPlayerActions) this.player.detectEquipmentUpdates(); // Paper - Force update attributes.
@@ -3583,7 +3584,7 @@ public class ServerGamePacketListenerImpl
@@ -3596,7 +3597,7 @@ public class ServerGamePacketListenerImpl
}
boolean validSlot = packet.slotNum() >= 1 && packet.slotNum() <= 45;
@@ -76,7 +76,7 @@ index fe6e1aea364620f82de44b942da0d3846c7d17b5..fd1ceb8c6f20b515e3a054050feeb443
if (drop || (validSlot && !ItemStack.matches(this.player.inventoryMenu.getSlot(packet.slotNum()).getItem(), packet.itemStack()))) { // Insist on valid slot
// CraftBukkit start - Call click event
org.bukkit.inventory.InventoryView inventory = this.player.inventoryMenu.getBukkitView();
@@ -3625,6 +3626,7 @@ public class ServerGamePacketListenerImpl
@@ -3638,6 +3639,7 @@ public class ServerGamePacketListenerImpl
this.player.inventoryMenu.getSlot(packet.slotNum()).setByPlayer(itemStack);
this.player.inventoryMenu.setRemoteSlot(packet.slotNum(), itemStack);
this.player.inventoryMenu.broadcastChanges();

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