Files
Shiroha/shiroha-server/minecraft-patches/features/0052-Add-portal-rate-limiter.patch
T
NanaChiyo0721 1bb895c165
Shiroha CI / build (push) Canceled after 0s
Shiroha CI / Event File (push) Canceled after 0s
Fix incorrect ticket lock size computing
Yeah this would fully solve the issue of chunk unloading race condition

The same fix was already initially added at "Force clamp grid-exponent to 1~6"
2026-08-09 14:57:32 +08:00

131 lines
6.4 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Thu, 9 Jul 2026 10:17:22 +0800
Subject: [PATCH] Add portal rate limiter
diff --git a/io/papermc/paper/threadedregions/RegionizedWorldData.java b/io/papermc/paper/threadedregions/RegionizedWorldData.java
index fbbc05e946b72da1079271810fdb3034510c6280..c04dbcd615c97cb6dcb5f14fec88daf73e6e9b4a 100644
--- a/io/papermc/paper/threadedregions/RegionizedWorldData.java
+++ b/io/papermc/paper/threadedregions/RegionizedWorldData.java
@@ -154,6 +154,10 @@ public final class RegionizedWorldData {
for (final ChunkHolder chunkHolder : from.chunkHoldersToBroadcast) {
into.chunkHoldersToBroadcast.add(chunkHolder);
}
+ // Shiroha start - Add portal rate limiter
+ into.portalRateThrottler.mergeWith(from.portalRateThrottler);
+ from.portalRateThrottler.destroy();
+ // Shiroha end
}
@Override
@@ -316,6 +320,12 @@ public final class RegionizedWorldData {
into.chunkHoldersToBroadcast.add(chunkHolder);
}
}
+ // Shiroha start - Add portal rate limiter
+ for (var worldData : dataSet) {
+ from.portalRateThrottler.splitInto(worldData.portalRateThrottler);
+ }
+ from.portalRateThrottler.destroy();
+ // Shiroha end
}
};
@@ -338,6 +348,35 @@ public final class RegionizedWorldData {
return this.isHandlingTick;
}
+ // Shiroha start - Add portal rate limiter
+ public final io.nanachiyo0721.shiroha.utils.RateThrottler portalRateThrottler = new io.nanachiyo0721.shiroha.utils.RateThrottler();
+ public final net.objecthunter.exp4j.Expression portalRateCapExpression = io.nanachiyo0721.shiroha.config.modules.function.PortalRateLimiterConfig.getExpressionIfConfigured();
+
+ private int computePortalRateCap() {
+ // expression mode is disabled
+ if (this.portalRateCapExpression == null) {
+ return io.nanachiyo0721.shiroha.config.modules.function.PortalRateLimiterConfig.maxPortalTeleportsPerTick;
+ }
+
+ final int tickingEntityCount = this.entityTickList.size();
+ final int tickingChunkCount = this.tickingChunks.size();
+ final int playerCount = this.localPlayers.size();
+
+ return io.nanachiyo0721.shiroha.config.modules.function.PortalRateLimiterConfig.computeExpression(
+ this.portalRateCapExpression,
+ tickingEntityCount,
+ tickingChunkCount,
+ playerCount
+ );
+ }
+ public boolean isPortalTeleportationOutOfRate() {
+ if (!io.nanachiyo0721.shiroha.config.modules.function.PortalRateLimiterConfig.enabled) {
+ return false;
+ }
+
+ return this.portalRateThrottler.isOutOfRate(this.computePortalRateCap());
+ }
+ // Shiroha 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 28f5c28bdf9a3090b98451e559f807c4364ea259..170ff99a055d7ea582d6a18ef6acd57269d943c4 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -1938,6 +1938,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");
+ // Shiroha start - Add portal rate limiter
+ regionizedWorldData.portalRateThrottler.begin();
+ // Shiroha end
try {
foliaProfiler.startTimer(level.tickTimerId); try { // Folia - profiler
@@ -1952,6 +1955,9 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
profiler.pop();
profiler.pop();
regionizedWorldData.explosionDensityCache.clear(); // Paper - Optimize explosions // Folia - region threading
+ // Shiroha start - Add portal rate limiter
+ regionizedWorldData.portalRateThrottler.done();
+ // Shiroha 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 674ebe647a5e44bf7819e1e7a234164476b31e3d..2670e265d1ef57371224d831a0c8478dbe8bd43c 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -1538,8 +1538,15 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
// removed from region while ticking
return;
}
+ // Shiroha start - Add portal rate limiter
+ var worldData = entity.level().getCurrentWorldData();
+ if (entity.portalProcess != null && worldData.isPortalTeleportationOutOfRate()) {
+ return;
+ }
+ // Shiroha end
if (entity.handlePortal()) {
// portalled
+ worldData.portalRateThrottler.increase(); // Shiroha - Add portal rate limiter
return;
}
}
@@ -1583,8 +1590,15 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
// removed from region while ticking
return;
}
+ // Shiroha start - Add portal rate limiter
+ var worldData = entity.level().getCurrentWorldData();
+ if (entity.portalProcess != null && worldData.isPortalTeleportationOutOfRate()) {
+ return;
+ }
+ // Shiroha end
if (entity.handlePortal()) {
// portalled
+ worldData.portalRateThrottler.increase(); // Shiroha - Add portal rate limiter
return;
}
// Folia end - region threading