diff --git a/src/main/java/com/intellectualcrafters/plot/PS.java b/src/main/java/com/intellectualcrafters/plot/PS.java index 6a4d0ca11..b75135473 100644 --- a/src/main/java/com/intellectualcrafters/plot/PS.java +++ b/src/main/java/com/intellectualcrafters/plot/PS.java @@ -1838,6 +1838,8 @@ public class PS { // Chunk processor options.put("chunk-processor.enabled", Settings.CHUNK_PROCESSOR); + options.put("chunk-processor.auto-unload", Settings.CHUNK_PROCESSOR_GC); + options.put("chunk-processor.auto-trim", Settings.CHUNK_PROCESSOR_TRIM_ON_SAVE); options.put("chunk-processor.max-blockstates", Settings.CHUNK_PROCESSOR_MAX_BLOCKSTATES); options.put("chunk-processor.max-entities", Settings.CHUNK_PROCESSOR_MAX_ENTITIES); options.put("chunk-processor.disable-physics", Settings.CHUNK_PROCESSOR_DISABLE_PHYSICS); @@ -1951,6 +1953,10 @@ public class PS { // Chunk processor Settings.CHUNK_PROCESSOR = config.getBoolean("chunk-processor.enabled"); + + Settings.CHUNK_PROCESSOR_GC = config.getBoolean("chunk-processor.auto-unload"); + Settings.CHUNK_PROCESSOR_TRIM_ON_SAVE = config.getBoolean("chunk-processor.auto-trim"); + Settings.CHUNK_PROCESSOR_MAX_BLOCKSTATES = config.getInt("chunk-processor.max-blockstates"); Settings.CHUNK_PROCESSOR_MAX_ENTITIES = config.getInt("chunk-processor.max-entities"); Settings.CHUNK_PROCESSOR_DISABLE_PHYSICS = config.getBoolean("chunk-processor.disable-physics"); diff --git a/src/main/java/com/intellectualcrafters/plot/config/Settings.java b/src/main/java/com/intellectualcrafters/plot/config/Settings.java index 7ba3a18fe..10c5d970f 100644 --- a/src/main/java/com/intellectualcrafters/plot/config/Settings.java +++ b/src/main/java/com/intellectualcrafters/plot/config/Settings.java @@ -68,6 +68,8 @@ public class Settings { * Chunk processor */ public static boolean CHUNK_PROCESSOR = false; + public static boolean CHUNK_PROCESSOR_TRIM_ON_SAVE = false; + public static boolean CHUNK_PROCESSOR_GC = false; public static int CHUNK_PROCESSOR_MAX_BLOCKSTATES = 4096; public static int CHUNK_PROCESSOR_MAX_ENTITIES = 512; public static boolean CHUNK_PROCESSOR_DISABLE_PHYSICS = false; diff --git a/src/main/java/com/plotsquared/bukkit/listeners/ChunkListener.java b/src/main/java/com/plotsquared/bukkit/listeners/ChunkListener.java index 1af4c639e..bee07e4e0 100644 --- a/src/main/java/com/plotsquared/bukkit/listeners/ChunkListener.java +++ b/src/main/java/com/plotsquared/bukkit/listeners/ChunkListener.java @@ -1,10 +1,16 @@ package com.plotsquared.bukkit.listeners; +import static com.intellectualcrafters.plot.util.ReflectionUtils.getRefClass; + +import java.util.HashMap; +import java.util.Map.Entry; + import org.bukkit.Bukkit; import org.bukkit.Chunk; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.block.BlockState; +import org.bukkit.craftbukkit.v1_8_R2.CraftChunk; import org.bukkit.entity.Entity; import org.bukkit.entity.Item; import org.bukkit.entity.LivingEntity; @@ -17,21 +23,143 @@ import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.event.entity.ItemSpawnEvent; import org.bukkit.event.world.ChunkLoadEvent; import org.bukkit.event.world.ChunkUnloadEvent; +import org.bukkit.event.world.WorldSaveEvent; import com.intellectualcrafters.plot.PS; import com.intellectualcrafters.plot.config.Settings; -import com.intellectualcrafters.plot.object.PseudoRandom; -import com.intellectualcrafters.plot.util.ChunkManager; +import com.intellectualcrafters.plot.object.ChunkLoc; +import com.intellectualcrafters.plot.object.Location; +import com.intellectualcrafters.plot.object.Plot; +import com.intellectualcrafters.plot.object.PlotPlayer; +import com.intellectualcrafters.plot.util.MainUtil; +import com.intellectualcrafters.plot.util.ReflectionUtils.RefClass; +import com.intellectualcrafters.plot.util.ReflectionUtils.RefField; +import com.intellectualcrafters.plot.util.ReflectionUtils.RefMethod; import com.intellectualcrafters.plot.util.TaskManager; +import com.intellectualcrafters.plot.util.UUIDHandler; public class ChunkListener implements Listener { private Chunk lastChunk = null; - private long last = 0; - private int count = 0; + + final RefClass classChunk = getRefClass("{nms}.Chunk"); + final RefClass classCraftChunk = getRefClass("{cb}.CraftChunk"); + final RefMethod methodGetHandleChunk; + final RefField mustSave = classChunk.getField("mustSave"); + + public ChunkListener() { + RefMethod method; + try { + method = classCraftChunk.getMethod("getHandle"); + } + catch (Exception e) { + method = null; + e.printStackTrace(); + } + methodGetHandleChunk = method; + + if (!Settings.CHUNK_PROCESSOR_GC) { + return; + } + TaskManager.runTaskRepeat(new Runnable() { + @Override + public void run() { + int distance = Bukkit.getViewDistance() + 1; + HashMap> players = new HashMap<>(); + for (Entry entry : UUIDHandler.getPlayers().entrySet()) { + PlotPlayer pp = entry.getValue(); + Location loc = pp.getLocation(); + String world = loc.getWorld(); + HashMap map = players.get(world); + if (map == null) { + map = new HashMap<>(); + players.put(world, map); + } + ChunkLoc origin = new ChunkLoc(loc.getX() >> 4, loc.getZ() >> 4); + Integer val = map.get(origin); + int check; + if (val != null) { + if (val == distance) { + continue; + } + check = distance - val; + } + else { + check = distance; + map.put(origin, distance); + } + for (int x = -distance; x <= distance; x++) { + if (x >= check || -x >= check) { + continue; + } + for (int z = -distance; z <= distance; z++) { + if (z >= check || -z >= check) { + continue; + } + int weight = distance - Math.max(Math.abs(x), Math.abs(z)); + ChunkLoc chunk = new ChunkLoc(x + origin.x, z + origin.z); + val = map.get(chunk); + if (val == null || val < weight) { + map.put(chunk, weight); + } + + } + } + } + for (World world : Bukkit.getWorlds()) { + String name = world.getName(); + boolean autosave = world.isAutoSave(); + boolean plotworld = PS.get().isPlotWorld(name); + if (autosave && plotworld) { + world.setAutoSave(false); + } + HashMap map = players.get(name); + if (map == null || map.size() == 0) { + continue; + } + for (Chunk chunk : world.getLoadedChunks()) { + int x = chunk.getX(); + int z = chunk.getZ(); + if (!map.containsKey(new ChunkLoc(x, z))) { + Plot plot = MainUtil.getPlot(new Location(name, x << 4, 1, z << 4)); + if (Settings.CHUNK_PROCESSOR_TRIM_ON_SAVE && plot == null || plot.owner == null && plotworld) { + unloadChunk(chunk); + CraftChunk c = null; + } + else { + chunk.unload(true, false); + } + } + } + if (!Settings.CHUNK_PROCESSOR_TRIM_ON_SAVE && autosave && plotworld) { + world.setAutoSave(true); + } + } + } + }, 300); + } + + public void unloadChunk(Chunk chunk) { + Object c = methodGetHandleChunk.of(chunk).call(); + mustSave.of(c).set(false); + chunk.unload(false, false); + } @EventHandler public void onChunkUnload(ChunkUnloadEvent event) { + if (Settings.CHUNK_PROCESSOR_TRIM_ON_SAVE) { + Chunk chunk = event.getChunk(); + String world = chunk.getWorld().getName(); + if (PS.get().isPlotWorld(world)) { + int x = chunk.getX(); + int z = chunk.getZ(); + Plot plot = MainUtil.getPlot(new Location(world, x << 4, 1, z << 4)); + if (plot == null || plot.owner == null && PS.get().isPlotWorld(world)) { + unloadChunk(chunk); + return; + } + } + } if (processChunk(event.getChunk(), true)) { event.setCancelled(true); } diff --git a/target/PlotSquared-Bukkit.jar b/target/PlotSquared-Bukkit.jar index 26d85bca2..0775d0f3b 100644 Binary files a/target/PlotSquared-Bukkit.jar and b/target/PlotSquared-Bukkit.jar differ diff --git a/target/PlotSquared-Sponge.jar b/target/PlotSquared-Sponge.jar index 2210a0333..c5da13ab9 100644 Binary files a/target/PlotSquared-Sponge.jar and b/target/PlotSquared-Sponge.jar differ