From 97d90f64a31201a1f064eb4e68fede8d26f4b231 Mon Sep 17 00:00:00 2001 From: xGinko Date: Thu, 12 Sep 2024 19:15:38 +0200 Subject: [PATCH] improve lag caused by operations happening on chunk load --- .../modules/chunklimits/DroppedItemLimit.java | 14 ++--- .../lagpreventions/KeepStashLoaded.java | 60 ++++++++++--------- .../agelimits/CustomAgeLimits.java | 10 ++-- .../withers/RemoveSkullsOnChunkload.java | 14 ++--- .../lagpreventions/KeepStashLoaded.java | 55 +++++++++-------- .../withers/RemoveSkullsOnChunkload.java | 13 ++-- .../java/me/xginko/aef/utils/ChunkUtil.java | 9 ++- 7 files changed, 94 insertions(+), 81 deletions(-) diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/chunklimits/DroppedItemLimit.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/chunklimits/DroppedItemLimit.java index aa35d74f3..bfd62e7d1 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/chunklimits/DroppedItemLimit.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/chunklimits/DroppedItemLimit.java @@ -18,7 +18,7 @@ import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.entity.ItemSpawnEvent; -import org.bukkit.event.world.ChunkLoadEvent; +import org.bukkit.event.world.EntitiesLoadEvent; import java.time.Duration; import java.util.EnumSet; @@ -35,7 +35,7 @@ public class DroppedItemLimit extends AEFModule implements Consumer whitelistedTypes; private final long checkPeriod, cleanupDelay; private final int maxDroppedItemsPerChunk; - private final boolean logIsEnabled, usingWhitelist, onChunkLoad; + private final boolean logIsEnabled, usingWhitelist, onEntitiesLoad; public DroppedItemLimit() { super("chunk-limits.entity-limits.dropped-item-limit"); @@ -56,8 +56,8 @@ public DroppedItemLimit() { checked. Keep in mind: A lower number provides more accuracy but is\s also worse for performance."""); this.scheduledChecks = Caffeine.newBuilder().expireAfterWrite(Duration.ofMillis(cleanupDelay * 50L)).build(); - this.onChunkLoad = config.getBoolean(configPath + ".check-on-chunk-load", true, """ - Runs item check when a chunk is loaded."""); + this.onEntitiesLoad = config.getBoolean(configPath + ".check-on-entities-load", true, """ + Runs item check when a chunk's entities are loaded."""); this.usingWhitelist = config.getBoolean(configPath + ".whitelist-specific-item-types", false); this.whitelistedTypes = config.getList(configPath + ".whitelisted-types", MaterialTags.SHULKER_BOXES.getValues().stream().map(Enum::name).sorted().toList(), """ @@ -123,12 +123,12 @@ private void onItemDrop(ItemSpawnEvent event) { } @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - private void onChunkLoad(ChunkLoadEvent event) { - if (!onChunkLoad || event.isNewChunk()) return; + private void onEntitiesLoad(EntitiesLoadEvent event) { + if (!onEntitiesLoad) return; int droppedItemCount = 0; - for (Entity entity : event.getChunk().getEntities()) { + for (Entity entity : event.getEntities()) { if (entity.getType() != XEntityType.ITEM.get()) continue; droppedItemCount++; diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/KeepStashLoaded.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/KeepStashLoaded.java index 8c2a7ad5b..c4c378856 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/KeepStashLoaded.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/KeepStashLoaded.java @@ -34,7 +34,7 @@ public class KeepStashLoaded extends AEFModule implements Consumer forceLoadedChunks; private final Map worldsAndTheirRadiuses = new HashMap<>(); private final Set storageTypes; - private final long minInhabitedTime, keepLoadedMillis; + private final long minInhabitedTime, keepLoadedMillis, checkDelayTicks; private final int stashCount; private final boolean logIsEnabled, onlyTileEntities; @@ -50,6 +50,9 @@ public KeepStashLoaded() { this.stashCount = config.getInt(configPath + ".container-block-threshold", 50, """ How many container blocks have to be in a chunk for it to be seen\s as a stash chunk to keep force loaded."""); + this.checkDelayTicks = config.getInt(configPath + ".check-delay-ticks", 200, """ + Ticks to wait after a chunk is loaded before it will be checked.\s + Reduces lag by fast travelling players."""); this.minInhabitedTime = config.getInt(configPath + ".min-chunk-inhabited-time-ticks", 1000, """ The minimum time in ticks a chunk has to have been inhabited to be checked."""); this.keepLoadedMillis = TimeUnit.MINUTES.toMillis(config.getInt(configPath + ".keep-loaded-minutes", 120, """ @@ -141,6 +144,7 @@ public void accept(ScheduledTask task) { @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) private void onChunkLoad(ChunkLoadEvent event) { if (event.isNewChunk()) return; + final String world = event.getWorld().getName(); if (!worldsAndTheirRadiuses.containsKey(world)) return; @@ -149,31 +153,28 @@ private void onChunkLoad(ChunkLoadEvent event) { if (chunk.getInhabitedTime() < minInhabitedTime) return; if (NumberConversions.square(chunk.getX()) + NumberConversions.square(chunk.getZ()) < worldsAndTheirRadiuses.get(world)) return; - if (isStashChunk(chunk)) { - forceLoadedChunks.computeIfAbsent(ChunkUID.of(chunk), chunkUID -> { - plugin.getServer().getGlobalRegionScheduler().execute(plugin, () -> { - chunk.setForceLoaded(true); - if (logIsEnabled) - info("Set chunk " + chunkUID + " to force loaded."); - }); - return System.currentTimeMillis() + keepLoadedMillis; - }); - } - } - - private boolean isStashChunk(Chunk chunk) { - int count = 0; - - if (onlyTileEntities) { - for (BlockState tileEntity : chunk.getTileEntities()) { - if (storageTypes.contains(tileEntity.getType())) { - count++; - if (count > stashCount) { - return true; + plugin.getServer().getRegionScheduler().runDelayed(plugin, chunk.getWorld(), chunk.getX(), chunk.getZ(), delayedCheck -> { + if (!chunk.isLoaded()) return; + + int count = 0; + + if (onlyTileEntities) { + for (BlockState tileEntity : chunk.getTileEntities()) { + if (storageTypes.contains(tileEntity.getType())) { + count++; + if (count > stashCount) { + chunk.setForceLoaded(true); + forceLoadedChunks.computeIfAbsent(ChunkUID.of(chunk), chunkUID -> { + if (logIsEnabled) info("Set chunk " + chunkUID + " to force loaded."); + return System.currentTimeMillis() + keepLoadedMillis; + }); + return; + } } } + return; } - } else { + final int minY = chunk.getWorld().getMinHeight(); final int maxY = chunk.getWorld().getMaxHeight(); @@ -183,14 +184,17 @@ private boolean isStashChunk(Chunk chunk) { if (storageTypes.contains(chunk.getBlock(x, y, z).getType())) { count++; if (count > stashCount) { - return true; + chunk.setForceLoaded(true); + forceLoadedChunks.computeIfAbsent(ChunkUID.of(chunk), chunkUID -> { + if (logIsEnabled) info("Set chunk " + chunkUID + " to force loaded."); + return System.currentTimeMillis() + keepLoadedMillis; + }); + return; } } } } } - } - - return false; + }, checkDelayTicks); } -} +} \ No newline at end of file diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/agelimits/CustomAgeLimits.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/agelimits/CustomAgeLimits.java index 3ed74dd8d..33058892d 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/agelimits/CustomAgeLimits.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/lagpreventions/agelimits/CustomAgeLimits.java @@ -13,7 +13,7 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; -import org.bukkit.event.world.ChunkLoadEvent; +import org.bukkit.event.world.EntitiesLoadEvent; import java.util.EnumMap; import java.util.Map; @@ -112,12 +112,10 @@ public void accept(ScheduledTask task) { } @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - private void onChunkLoad(ChunkLoadEvent event) { - if (event.isNewChunk()) return; - - for (Entity entity : event.getChunk().getEntities()) { + private void onEntitiesLoad(EntitiesLoadEvent event) { + for (Entity entity : event.getEntities()) { if (entityLimits.containsKey(entity.getType()) && entity.getTicksLived() >= entityLimits.get(entity.getType())) { - entity.remove(); + entity.getScheduler().execute(plugin, entity::remove, null, 1L); if (logIsEnabled) info("Removed " + entity.getType().name() + " due to old age."); } diff --git a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/preventions/withers/RemoveSkullsOnChunkload.java b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/preventions/withers/RemoveSkullsOnChunkload.java index 9c206304c..1413344c4 100755 --- a/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/preventions/withers/RemoveSkullsOnChunkload.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/xginko/aef/modules/preventions/withers/RemoveSkullsOnChunkload.java @@ -8,14 +8,14 @@ import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.entity.ProjectileLaunchEvent; -import org.bukkit.event.world.ChunkLoadEvent; +import org.bukkit.event.world.EntitiesLoadEvent; public class RemoveSkullsOnChunkload extends AEFModule implements Listener { public RemoveSkullsOnChunkload() { - super("preventions.withers.remove-skulls-on-chunk-load"); + super("preventions.withers.remove-skulls-on-load"); config.addComment(configPath, """ - Removes wither skulls when the chunk gets loaded.\s + Removes wither skulls when entities get loaded.\s Use if you have a ton of them at spawn and they are causing lag."""); } @@ -41,11 +41,9 @@ private void onProjectileLaunch(ProjectileLaunchEvent event) { } } - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - private void onChunkLoad(ChunkLoadEvent event) { - if (event.isNewChunk()) return; - - for (Entity entity : event.getChunk().getEntities()) { + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onEntitiesLoad(EntitiesLoadEvent event) { + for (Entity entity : event.getEntities()) { if (entity.getType() == XEntityType.WITHER_SKULL.get()) { entity.getScheduler().execute(plugin, entity::remove, null, 1L); } diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/KeepStashLoaded.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/KeepStashLoaded.java index 7d770ac13..3fe0f2a45 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/KeepStashLoaded.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/lagpreventions/KeepStashLoaded.java @@ -34,7 +34,7 @@ public class KeepStashLoaded extends AEFModule implements Runnable, Listener { private final Map forceLoadedChunks; private final Map worldsAndTheirRadiuses = new HashMap<>(); private final Set storageTypes; - private final long keepLoadedMillis; + private final long keepLoadedMillis, checkDelayTicks; private final int stashCount; private final boolean logIsEnabled, onlyTileEntities; @@ -51,6 +51,9 @@ public KeepStashLoaded() { this.stashCount = config.getInt(configPath + ".container-block-threshold", 50, "How many container blocks have to be in a chunk for it to be seen\n"+ "as a stash chunk to keep force loaded."); + this.checkDelayTicks = config.getInt(configPath + ".check-delay-ticks", 200, + "Ticks to wait after a chunk is loaded before it will be checked.\n" + + "Reduces lag by fast travelling players."); this.keepLoadedMillis = TimeUnit.MINUTES.toMillis(config.getInt(configPath + ".keep-loaded-minutes", 120, "The time in minutes a stash chunks will be kept force loaded before\n"+ "setting it back to normal.")); @@ -146,29 +149,28 @@ private void onChunkLoad(ChunkLoadEvent event) { if (NumberConversions.square(chunk.getX()) + NumberConversions.square(chunk.getZ()) < worldsAndTheirRadiuses.get(world)) return; - if (isStashChunk(chunk)) { - forceLoadedChunks.computeIfAbsent(ChunkUID.of(chunk), chunkUID -> { - ChunkUtil.setForceLoaded(chunk, true); - if (logIsEnabled) - info("Set chunk " + chunkUID + " to force loaded."); - return System.currentTimeMillis() + keepLoadedMillis; - }); - } - } - - private boolean isStashChunk(Chunk chunk) { - int count = 0; - - if (onlyTileEntities) { - for (BlockState tileEntity : chunk.getTileEntities()) { - if (storageTypes.contains(tileEntity.getType())) { - count++; - if (count > stashCount) { - return true; + plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> { + if (!chunk.isLoaded()) return; + + int count = 0; + + if (onlyTileEntities) { + for (BlockState tileEntity : chunk.getTileEntities()) { + if (storageTypes.contains(tileEntity.getType())) { + count++; + if (count > stashCount) { + ChunkUtil.setForceLoaded(chunk, true); + forceLoadedChunks.computeIfAbsent(ChunkUID.of(chunk), chunkUID -> { + if (logIsEnabled) info("Set chunk " + chunkUID + " to force loaded."); + return System.currentTimeMillis() + keepLoadedMillis; + }); + return; + } } } + return; } - } else { + final int minY = WorldUtil.getMinWorldHeight(chunk.getWorld()); final int maxY = chunk.getWorld().getMaxHeight(); @@ -178,14 +180,17 @@ private boolean isStashChunk(Chunk chunk) { if (storageTypes.contains(chunk.getBlock(x, y, z).getType())) { count++; if (count > stashCount) { - return true; + ChunkUtil.setForceLoaded(chunk, true); + forceLoadedChunks.computeIfAbsent(ChunkUID.of(chunk), chunkUID -> { + if (logIsEnabled) info("Set chunk " + chunkUID + " to force loaded."); + return System.currentTimeMillis() + keepLoadedMillis; + }); + return; } } } } } - } - - return false; + }, checkDelayTicks); } } diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/withers/RemoveSkullsOnChunkload.java b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/withers/RemoveSkullsOnChunkload.java index 4a359eae9..cf30d403a 100755 --- a/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/withers/RemoveSkullsOnChunkload.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/xginko/aef/modules/preventions/withers/RemoveSkullsOnChunkload.java @@ -2,6 +2,7 @@ import com.cryptomorin.xseries.XEntityType; import me.xginko.aef.modules.AEFModule; +import me.xginko.aef.utils.ChunkUtil; import me.xginko.aef.utils.EntityUtil; import org.bukkit.entity.Entity; import org.bukkit.event.EventHandler; @@ -9,14 +10,14 @@ import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.entity.ProjectileLaunchEvent; -import org.bukkit.event.world.ChunkLoadEvent; +import org.bukkit.event.world.ChunkUnloadEvent; public class RemoveSkullsOnChunkload extends AEFModule implements Listener { public RemoveSkullsOnChunkload() { - super("preventions.withers.remove-flying-wither-skulls.on-chunk-load"); + super("preventions.withers.remove-flying-wither-skulls.on-chunk-unload"); config.addComment(configPath, - "Removes wither skulls when the chunk gets loaded.\n" + + "Removes wither skulls when the chunk gets unloaded.\n" + "Use if you have a ton of them at spawn and they are causing lag."); } @@ -42,9 +43,9 @@ private void onProjectileLaunch(ProjectileLaunchEvent event) { } } - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - private void onChunkLoad(ChunkLoadEvent event) { - if (event.isNewChunk()) return; + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onChunkUnload(ChunkUnloadEvent event) { + if (!ChunkUtil.isEntitiesLoaded(event.getChunk())) return; for (Entity entity : event.getChunk().getEntities()) { if (entity.getType() == XEntityType.WITHER_SKULL.get()) { diff --git a/shared/src/main/java/me/xginko/aef/utils/ChunkUtil.java b/shared/src/main/java/me/xginko/aef/utils/ChunkUtil.java index 0e393cc61..14366633f 100644 --- a/shared/src/main/java/me/xginko/aef/utils/ChunkUtil.java +++ b/shared/src/main/java/me/xginko/aef/utils/ChunkUtil.java @@ -7,13 +7,16 @@ public class ChunkUtil { - private static final boolean SET_FORCE_LOADED_AVAILABLE, GET_INHABITED_TIME_AVAILABLE; + private static final boolean SET_FORCE_LOADED_AVAILABLE, GET_INHABITED_TIME_AVAILABLE, + IS_ENTITIES_LOADED_AVAILABLE; static { SET_FORCE_LOADED_AVAILABLE = Crafty.hasMethod(Chunk.class, "setForceLoaded", boolean.class); GET_INHABITED_TIME_AVAILABLE = Crafty.hasMethod(Chunk.class, "getInhabitedTime"); + IS_ENTITIES_LOADED_AVAILABLE + = Crafty.hasMethod(Chunk.class, "isEntitiesLoaded"); } public static boolean canSetChunksForceLoaded() { @@ -34,6 +37,10 @@ public static long getInhabitedTime(Chunk chunk) { return GET_INHABITED_TIME_AVAILABLE ? chunk.getInhabitedTime() : 0L; } + public static boolean isEntitiesLoaded(Chunk chunk) { + return IS_ENTITIES_LOADED_AVAILABLE ? chunk.isEntitiesLoaded() : chunk.isLoaded(); + } + public static void createBedrockLayer(Chunk chunk, int y) { for (int x = 0; x < 16; x++) { for (int z = 0; z < 16; z++) {