ChunkProcessor auto trim and chunk unloading

- The auto trim drastically reduces the cost of players exploring on
speed 10, as it prevents chunks of unowned plots from saving to disk
when the chunk is unloaded
- It is recommended to disable world auto saving, or also enable the
chunk processor GC, as otherwise minecraft will also save chunks as they
are loaded.
This commit is contained in:
boy0001 2015-08-24 03:29:59 +10:00
parent e228c00d87
commit 79b16b8040
5 changed files with 140 additions and 4 deletions

View File

@ -1838,6 +1838,8 @@ public class PS {
// Chunk processor // Chunk processor
options.put("chunk-processor.enabled", Settings.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-blockstates", Settings.CHUNK_PROCESSOR_MAX_BLOCKSTATES);
options.put("chunk-processor.max-entities", Settings.CHUNK_PROCESSOR_MAX_ENTITIES); options.put("chunk-processor.max-entities", Settings.CHUNK_PROCESSOR_MAX_ENTITIES);
options.put("chunk-processor.disable-physics", Settings.CHUNK_PROCESSOR_DISABLE_PHYSICS); options.put("chunk-processor.disable-physics", Settings.CHUNK_PROCESSOR_DISABLE_PHYSICS);
@ -1951,6 +1953,10 @@ public class PS {
// Chunk processor // Chunk processor
Settings.CHUNK_PROCESSOR = config.getBoolean("chunk-processor.enabled"); 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_BLOCKSTATES = config.getInt("chunk-processor.max-blockstates");
Settings.CHUNK_PROCESSOR_MAX_ENTITIES = config.getInt("chunk-processor.max-entities"); Settings.CHUNK_PROCESSOR_MAX_ENTITIES = config.getInt("chunk-processor.max-entities");
Settings.CHUNK_PROCESSOR_DISABLE_PHYSICS = config.getBoolean("chunk-processor.disable-physics"); Settings.CHUNK_PROCESSOR_DISABLE_PHYSICS = config.getBoolean("chunk-processor.disable-physics");

View File

@ -68,6 +68,8 @@ public class Settings {
* Chunk processor * Chunk processor
*/ */
public static boolean CHUNK_PROCESSOR = false; 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_BLOCKSTATES = 4096;
public static int CHUNK_PROCESSOR_MAX_ENTITIES = 512; public static int CHUNK_PROCESSOR_MAX_ENTITIES = 512;
public static boolean CHUNK_PROCESSOR_DISABLE_PHYSICS = false; public static boolean CHUNK_PROCESSOR_DISABLE_PHYSICS = false;

View File

@ -1,10 +1,16 @@
package com.plotsquared.bukkit.listeners; 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.Bukkit;
import org.bukkit.Chunk; import org.bukkit.Chunk;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.block.BlockState; import org.bukkit.block.BlockState;
import org.bukkit.craftbukkit.v1_8_R2.CraftChunk;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.Item; import org.bukkit.entity.Item;
import org.bukkit.entity.LivingEntity; 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.entity.ItemSpawnEvent;
import org.bukkit.event.world.ChunkLoadEvent; import org.bukkit.event.world.ChunkLoadEvent;
import org.bukkit.event.world.ChunkUnloadEvent; import org.bukkit.event.world.ChunkUnloadEvent;
import org.bukkit.event.world.WorldSaveEvent;
import com.intellectualcrafters.plot.PS; import com.intellectualcrafters.plot.PS;
import com.intellectualcrafters.plot.config.Settings; import com.intellectualcrafters.plot.config.Settings;
import com.intellectualcrafters.plot.object.PseudoRandom; import com.intellectualcrafters.plot.object.ChunkLoc;
import com.intellectualcrafters.plot.util.ChunkManager; 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.TaskManager;
import com.intellectualcrafters.plot.util.UUIDHandler;
public class ChunkListener implements Listener { public class ChunkListener implements Listener {
private Chunk lastChunk = null; 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<String, HashMap<ChunkLoc, Integer>> players = new HashMap<>();
for (Entry<String, PlotPlayer> entry : UUIDHandler.getPlayers().entrySet()) {
PlotPlayer pp = entry.getValue();
Location loc = pp.getLocation();
String world = loc.getWorld();
HashMap<ChunkLoc, Integer> 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<ChunkLoc, Integer> 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 @EventHandler
public void onChunkUnload(ChunkUnloadEvent event) { 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)) { if (processChunk(event.getChunk(), true)) {
event.setCancelled(true); event.setCancelled(true);
} }

Binary file not shown.

Binary file not shown.