Skip to content

Commit

Permalink
improve keepstashloaded module
Browse files Browse the repository at this point in the history
  • Loading branch information
xGinko committed Jul 4, 2024
1 parent f5ef5e6 commit ca41aff
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 98 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,23 @@
import io.github.thatsmusic99.configurationmaster.api.ConfigSection;
import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
import me.xginko.aef.modules.AEFModule;
import me.xginko.aef.utils.LocationUtil;
import me.xginko.aef.utils.MaterialUtil;
import me.xginko.aef.utils.models.ChunkUID;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.BlockState;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.world.ChunkLoadEvent;
import org.bukkit.util.NumberConversions;

import java.util.*;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
Expand All @@ -24,11 +28,11 @@
public class KeepStashLoaded extends AEFModule implements Consumer<ScheduledTask>, Listener {

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 int stashCount;
private final boolean logIsEnabled;
private final Map<String, Integer> worldsAndTheirRadiuses = new HashMap<>();
private final boolean logIsEnabled, onlyTileEntities;

public KeepStashLoaded() {
super("lag-preventions.keep-stash-chunks-loaded");
Expand All @@ -47,6 +51,9 @@ public KeepStashLoaded() {
this.keepLoadedMillis = TimeUnit.MINUTES.toMillis(config.getInt(configPath + ".keep-loaded-minutes", 60, """
The time in minutes a stash chunks will be kept force loaded before\s
setting it back to normal."""));
this.onlyTileEntities = config.getBoolean(configPath + ".only-check-tile-entities", true, """
Set to false if you want to check more blocks than just tile entities.\s
Makes the overall speed of the module faster if set to true.""");
this.storageTypes = config.getList(configPath + ".container-types", MaterialUtil.INVENTORY_HOLDER_BLOCKS
.stream()
.map(Enum::name)
Expand All @@ -62,16 +69,17 @@ public KeepStashLoaded() {
})
.filter(Objects::nonNull)
.collect(Collectors.toCollection(() -> EnumSet.noneOf(Material.class)));

Map<String, Object> defaults = new HashMap<>();
defaults.put("world", 5000);
defaults.put("world_nether", 5000);
defaults.put("world_the_end", 5000);
ConfigSection section = config.getConfigSection(configPath + ".worlds", defaults, "Spawn radius in which stashes should not be checked");
defaults.put("world", 100);
defaults.put("world_nether", 100);
defaults.put("world_the_end", 100);
ConfigSection section = config.getConfigSection(configPath + ".worlds", defaults,
"Radiuses around spawn in chunks (not blocks) that should not be checked.\n" +
"Worlds not on this list are exempt from all checking.");
for (String world : section.getKeys(false)) {
try {
Integer radius = Integer.valueOf(section.getString(world));
this.worldsAndTheirRadiuses.put(world, radius);
int radius = Integer.parseInt(section.getString(world));
this.worldsAndTheirRadiuses.put(world, NumberConversions.square(radius));
} catch (NumberFormatException e) {
warn("Radius for world '" + world + "' is not a valid integer.");
}
Expand Down Expand Up @@ -127,47 +135,56 @@ public void accept(ScheduledTask task) {
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
private void onChunkLoad(ChunkLoadEvent event) {
if (event.isNewChunk()) return;
Chunk chunk = event.getChunk();
if (chunk.getInhabitedTime() < minInhabitedTime) return;

final String world = chunk.getWorld().getName();
final String world = event.getWorld().getName();
if (!worldsAndTheirRadiuses.containsKey(world)) return;

final Integer disabledRadius = worldsAndTheirRadiuses.get(world);
final Location chunkLocation = chunk.getWorld().getLocationAtKey(chunk.getChunkKey());
if (LocationUtil.getDistance2DTo00(chunkLocation) < disabledRadius) return;

final int minY = event.getWorld().getMinHeight();
final int maxY = event.getWorld().getMaxHeight();
Chunk chunk = event.getChunk();

boolean setForceLoaded = false;
if (chunk.getInhabitedTime() < minInhabitedTime) return;
if (NumberConversions.square(chunk.getZ() - 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;
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
for (int y = minY; y < maxY; y++) {
if (storageTypes.contains(chunk.getBlock(x, y, z).getType())) {
count++;
if (count > stashCount) {
setForceLoaded = true;
break;

if (onlyTileEntities) {
for (BlockState tileEntity : chunk.getTileEntities()) {
if (storageTypes.contains(tileEntity.getType())) {
count++;
if (count > stashCount) {
return true;
}
}
}
} else {
final int minY = chunk.getWorld().getMinHeight();
final int maxY = chunk.getWorld().getMaxHeight();

for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
for (int y = minY; y < maxY; y++) {
if (storageTypes.contains(chunk.getBlock(x, y, z).getType())) {
count++;
if (count > stashCount) {
return true;
}
}
}
}
}
}

if (!setForceLoaded) {
return;
}

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;
});
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import me.xginko.aef.utils.LocationUtil;
import me.xginko.aef.utils.models.Disableable;
import me.xginko.aef.utils.models.ExpiringSet;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
Expand All @@ -38,7 +37,7 @@ public class ElytraHelper extends AEFModule implements Disableable, Runnable, Pa
private static ElytraHelper instance;
private final Map<UUID, PlayerData> playerDataMap;
private final NewChunksListener newChunksListener;
private PacketListenerAbstract packetListener;
private final PacketListenerAbstract packetListener;
private BukkitTask scheduledTask;
private final long checkPeriod;

Expand Down Expand Up @@ -176,7 +175,7 @@ public NewChunksListener() {
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
private void onChunkLoad(ChunkLoadEvent event) {
for (Player player : event.getWorld().getPlayers()) {
if (chunkDistanceSquared(event.getChunk(), player.getLocation()) < NumberConversions.square(player.getViewDistance())) {
if (ChunkUtil.getChunkDistanceSquared(event.getChunk(), player.getLocation()) < NumberConversions.square(player.getViewDistance())) {
if (event.isNewChunk()) {
this.new_chunk_players.add(player.getUniqueId());
} else {
Expand All @@ -186,16 +185,6 @@ private void onChunkLoad(ChunkLoadEvent event) {
}
}

/**
* Since the distance here is only used to see whether a chunk is loaded roughly within the player's view distance,
* we can resort to comparing squared distances.
* This saves cpu usage as we don't have to use {@link Math#sqrt(double)} to get the accurate distance in chunks.
*/
private static double chunkDistanceSquared(Chunk chunk, Location location) {
return NumberConversions.square(chunk.getX() - (location.getBlockX() >> 4)) +
NumberConversions.square(chunk.getZ() - (location.getBlockZ() >> 4));
}

public boolean isInNewChunks(UUID player) {
return new_chunk_players.contains(player);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,35 +1,39 @@
package me.xginko.aef.modules.lagpreventions;

import io.github.thatsmusic99.configurationmaster.api.ConfigSection;
import me.xginko.aef.utils.ChunkUtil;
import me.xginko.aef.modules.AEFModule;
import me.xginko.aef.utils.LocationUtil;
import me.xginko.aef.utils.WorldUtil;
import me.xginko.aef.utils.ChunkUtil;
import me.xginko.aef.utils.MaterialUtil;
import me.xginko.aef.utils.WorldUtil;
import me.xginko.aef.utils.models.ChunkUID;
import me.xginko.aef.utils.models.Disableable;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.BlockState;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.world.ChunkLoadEvent;
import org.bukkit.util.NumberConversions;

import java.util.*;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

public class KeepStashLoaded extends AEFModule implements Disableable, 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 int stashCount;
private final boolean logIsEnabled;
private final Map<String, Integer> worldsAndTheirRadiuses = new HashMap<>();
private final boolean logIsEnabled, onlyTileEntities;

public KeepStashLoaded() {
super("lag-preventions.keep-stash-chunks-loaded");
Expand All @@ -47,6 +51,9 @@ public KeepStashLoaded() {
this.keepLoadedMillis = TimeUnit.MINUTES.toMillis(config.getInt(configPath + ".keep-loaded-minutes", 60,
"The time in minutes a stash chunks will be kept force loaded before\n"+
"setting it back to normal."));
this.onlyTileEntities = config.getBoolean(configPath + ".only-check-tile-entities", true,
"Set to false if you want to check more blocks than just tile entities.\n" +
"Makes the overall speed of the module faster if set to true.");
this.storageTypes = config.getList(configPath + ".container-types", MaterialUtil.INVENTORY_HOLDER_BLOCKS
.stream()
.map(Enum::name)
Expand All @@ -62,16 +69,17 @@ public KeepStashLoaded() {
})
.filter(Objects::nonNull)
.collect(Collectors.toCollection(() -> EnumSet.noneOf(Material.class)));

Map<String, Object> defaults = new HashMap<>();
defaults.put("world", 5000);
defaults.put("world_nether", 5000);
defaults.put("world_the_end", 5000);
ConfigSection section = config.getConfigSection(configPath + ".worlds", defaults, "Spawn radius in which stashes should not be checked");
defaults.put("world", 100);
defaults.put("world_nether", 100);
defaults.put("world_the_end", 100);
ConfigSection section = config.getConfigSection(configPath + ".worlds", defaults,
"Radiuses around spawn in chunks (not blocks) that should not be checked.\n" +
"Worlds not on this list are exempt from all checking.");
for (String world : section.getKeys(false)) {
try {
Integer radius = Integer.valueOf(section.getString(world));
this.worldsAndTheirRadiuses.put(world, radius);
int radius = Integer.parseInt(section.getString(world));
this.worldsAndTheirRadiuses.put(world, NumberConversions.square(radius));
} catch (NumberFormatException e) {
warn("Radius for world '" + world + "' is not a valid integer.");
}
Expand Down Expand Up @@ -122,47 +130,56 @@ public void run() {
}
}

@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
private void onChunkLoad(ChunkLoadEvent event) {
if (event.isNewChunk()) return;
Chunk chunk = event.getChunk();

final String world = chunk.getWorld().getName();
final String world = event.getWorld().getName();
if (!worldsAndTheirRadiuses.containsKey(world)) return;

final Integer disabledRadius = worldsAndTheirRadiuses.get(world);
final Location chunkLocation = chunk.getWorld().getLocationAtKey(chunk.getChunkKey());
if (LocationUtil.getDistance2DTo00(chunkLocation) < disabledRadius) return;
Chunk chunk = event.getChunk();

final int minY = WorldUtil.getMinWorldHeight(event.getWorld());
final int maxY = event.getWorld().getMaxHeight();
if (NumberConversions.square(chunk.getZ() - chunk.getZ()) < worldsAndTheirRadiuses.get(world)) return;

boolean setForceLoaded = false;
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;
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
for (int y = minY; y < maxY; y++) {
if (storageTypes.contains(chunk.getBlock(x, y, z).getType())) {
count++;
if (count > stashCount) {
setForceLoaded = true;
break;

if (onlyTileEntities) {
for (BlockState tileEntity : chunk.getTileEntities()) {
if (storageTypes.contains(tileEntity.getType())) {
count++;
if (count > stashCount) {
return true;
}
}
}
} else {
final int minY = WorldUtil.getMinWorldHeight(chunk.getWorld());
final int maxY = chunk.getWorld().getMaxHeight();

for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
for (int y = minY; y < maxY; y++) {
if (storageTypes.contains(chunk.getBlock(x, y, z).getType())) {
count++;
if (count > stashCount) {
return true;
}
}
}
}
}
}

if (!setForceLoaded) {
return;
}

forceLoadedChunks.computeIfAbsent(ChunkUID.of(chunk), chunkUID -> {
ChunkUtil.setForceLoaded(chunk, true);
if (logIsEnabled)
info("Set chunk "+chunkUID+" to force loaded.");
return System.currentTimeMillis() + keepLoadedMillis;
});
return false;
}
}
Loading

0 comments on commit ca41aff

Please sign in to comment.