Skip to content

Commit

Permalink
improve lag caused by operations happening on chunk load
Browse files Browse the repository at this point in the history
  • Loading branch information
xGinko committed Sep 12, 2024
1 parent edbdefb commit 97d90f6
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -35,7 +35,7 @@ public class DroppedItemLimit extends AEFModule implements Consumer<ScheduledTas
private final Set<Material> 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");
Expand All @@ -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(), """
Expand Down Expand Up @@ -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++;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public class KeepStashLoaded extends AEFModule implements Consumer<ScheduledTask
private final Map<ChunkUID, Long> forceLoadedChunks;
private final Map<String, Double> worldsAndTheirRadiuses = new HashMap<>();
private final Set<Material> storageTypes;
private final long minInhabitedTime, keepLoadedMillis;
private final long minInhabitedTime, keepLoadedMillis, checkDelayTicks;
private final int stashCount;
private final boolean logIsEnabled, onlyTileEntities;

Expand All @@ -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, """
Expand Down Expand Up @@ -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;

Expand All @@ -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();

Expand All @@ -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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.""");
}

Expand All @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public class KeepStashLoaded extends AEFModule implements Runnable, Listener {
private final Map<ChunkUID, Long> forceLoadedChunks;
private final Map<String, Double> worldsAndTheirRadiuses = new HashMap<>();
private final Set<Material> storageTypes;
private final long keepLoadedMillis;
private final long keepLoadedMillis, checkDelayTicks;
private final int stashCount;
private final boolean logIsEnabled, onlyTileEntities;

Expand All @@ -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."));
Expand Down Expand Up @@ -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();

Expand All @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,22 @@

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;
import org.bukkit.event.EventPriority;
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.");
}

Expand All @@ -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()) {
Expand Down
9 changes: 8 additions & 1 deletion shared/src/main/java/me/xginko/aef/utils/ChunkUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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++) {
Expand Down

0 comments on commit 97d90f6

Please sign in to comment.