Optimize auto trim + command cost/confirmation

This commit is contained in:
Jesse Boyd
2016-05-22 05:29:19 +10:00
parent e2182260d9
commit b8afbe8a00
16 changed files with 325 additions and 745 deletions

View File

@ -44,7 +44,6 @@ import com.plotsquared.bukkit.listeners.PlayerEvents_1_8;
import com.plotsquared.bukkit.listeners.PlayerEvents_1_9;
import com.plotsquared.bukkit.listeners.PlotPlusListener;
import com.plotsquared.bukkit.listeners.WorldEvents;
import com.plotsquared.bukkit.listeners.worldedit.WEListener;
import com.plotsquared.bukkit.titles.DefaultTitle_19;
import com.plotsquared.bukkit.util.BukkitChatManager;
import com.plotsquared.bukkit.util.BukkitChunkManager;
@ -387,7 +386,6 @@ public final class BukkitMain extends JavaPlugin implements Listener, IPlotMain
public boolean initWorldEdit() {
if (getServer().getPluginManager().getPlugin("WorldEdit") != null) {
worldEdit = (WorldEditPlugin) getServer().getPluginManager().getPlugin("WorldEdit");
getServer().getPluginManager().registerEvents(new WEListener(), this);
return true;
}
return false;

View File

@ -1,18 +1,15 @@
package com.plotsquared.bukkit.listeners;
import static com.intellectualcrafters.plot.util.ReflectionUtils.getRefClass;
import com.intellectualcrafters.plot.PS;
import com.intellectualcrafters.plot.config.Settings;
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.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;
import java.lang.reflect.Method;
import java.util.HashSet;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.Material;
@ -31,9 +28,8 @@ import org.bukkit.event.entity.ItemSpawnEvent;
import org.bukkit.event.world.ChunkLoadEvent;
import org.bukkit.event.world.ChunkUnloadEvent;
import java.util.ArrayDeque;
import java.util.HashMap;
import java.util.Map.Entry;
import static com.intellectualcrafters.plot.util.ReflectionUtils.getRefClass;
public class ChunkListener implements Listener {
@ -43,7 +39,7 @@ public class ChunkListener implements Listener {
public ChunkListener() {
if (Settings.CHUNK_PROCESSOR_GC || Settings.CHUNK_PROCESSOR_TRIM_ON_SAVE) {
if (Settings.CHUNK_PROCESSOR_GC) {
try {
RefClass classChunk = getRefClass("{nms}.Chunk");
RefClass classCraftChunk = getRefClass("{cb}.CraftChunk");
@ -52,130 +48,60 @@ public class ChunkListener implements Listener {
} catch (Throwable ignored) {
PS.debug("PlotSquared/Server not compatible for chunk processor trim/gc");
Settings.CHUNK_PROCESSOR_GC = false;
Settings.CHUNK_PROCESSOR_TRIM_ON_SAVE = false;
}
}
if (!Settings.CHUNK_PROCESSOR_GC) {
return;
}
TaskManager.runTask(new Runnable() {
for (World world : Bukkit.getWorlds()) {
world.setAutoSave(false);
}
TaskManager.runTaskRepeat(new Runnable() {
@Override
public void run() {
int distance = Bukkit.getViewDistance() + 2;
HashMap<String, HashMap<ChunkLoc, Integer>> players = new HashMap<>();
for (Entry<String, PlotPlayer> entry : UUIDHandler.getPlayers().entrySet()) {
PlotPlayer pp = entry.getValue();
Location location = pp.getLocation();
String world = location.getWorld();
if (!PS.get().hasPlotArea(world)) {
continue;
}
HashMap<ChunkLoc, Integer> map = players.get(world);
if (map == null) {
map = new HashMap<>();
players.put(world, map);
}
ChunkLoc origin = new ChunkLoc(location.getX() >> 4, location.getZ() >> 4);
Integer val = map.get(origin);
int check;
if (val != null) {
if (val == distance) {
try {
HashSet<Chunk> toUnload = new HashSet<>();
for (World world : Bukkit.getWorlds()) {
String worldName = world.getName();
if (!PS.get().hasPlotArea(worldName)) {
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) {
Object w = world.getClass().getDeclaredMethod("getHandle").invoke(world);
Object chunkMap = w.getClass().getDeclaredMethod("getPlayerChunkMap").invoke(w);
Method methodIsChunkInUse = chunkMap.getClass().getDeclaredMethod("isChunkInUse", int.class, int.class);
Chunk[] chunks = world.getLoadedChunks();
for (Chunk chunk : chunks) {
if ((boolean) methodIsChunkInUse.invoke(chunkMap, chunk.getX(), chunk.getZ())) {
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);
int x = chunk.getX();
int z = chunk.getZ();
if (!shouldSave(worldName, x, z)) {
unloadChunk(worldName, chunk, false);
continue;
}
}
}
}
int time = 300;
for (World world : Bukkit.getWorlds()) {
String name = world.getName();
if (!PS.get().hasPlotArea(name)) {
continue;
}
boolean autoSave = world.isAutoSave();
if (autoSave) {
world.setAutoSave(false);
}
HashMap<ChunkLoc, Integer> map = players.get(name);
if (map == null || map.isEmpty()) {
continue;
}
Chunk[] chunks = world.getLoadedChunks();
ArrayDeque<Chunk> toUnload = new ArrayDeque<>();
for (Chunk chunk : chunks) {
int x = chunk.getX();
int z = chunk.getZ();
if (!map.containsKey(new ChunkLoc(x, z))) {
toUnload.add(chunk);
}
}
if (!toUnload.isEmpty()) {
long start = System.currentTimeMillis();
Chunk chunk;
while ((chunk = toUnload.poll()) != null && System.currentTimeMillis() - start < 5) {
if (!Settings.CHUNK_PROCESSOR_TRIM_ON_SAVE || !unloadChunk(name, chunk)) {
if (chunk.isLoaded()) {
chunk.unload(true, false);
}
}
}
if (!toUnload.isEmpty()) {
time = 1;
}
if (toUnload.isEmpty()) {
return;
}
if (!Settings.CHUNK_PROCESSOR_TRIM_ON_SAVE && autoSave) {
world.setAutoSave(true);
long start = System.currentTimeMillis();
for (Chunk chunk : toUnload) {
if (System.currentTimeMillis() - start > 5) {
return;
}
chunk.unload(true, false);
}
} catch (Throwable e) {
e.printStackTrace();
}
TaskManager.runTaskLater(this, time);
}
});
}, 1);
}
public boolean unloadChunk(String world, Chunk chunk) {
int X = chunk.getX();
int Z = chunk.getZ();
int x = X << 4;
int z = Z << 4;
int x2 = x + 15;
int z2 = z + 15;
Plot plot = new Location(world, x, 1, z).getOwnedPlotAbs();
if (plot != null && plot.hasOwner()) {
return false;
}
plot = new Location(world, x2, 1, z2).getOwnedPlotAbs();
if (plot != null && plot.hasOwner()) {
return false;
}
plot = new Location(world, x2, 1, z).getOwnedPlotAbs();
if (plot != null && plot.hasOwner()) {
return false;
}
plot = new Location(world, x, 1, z2).getOwnedPlotAbs();
if (plot != null && plot.hasOwner()) {
return false;
}
plot = new Location(world, x + 7, 1, z + 7).getOwnedPlotAbs();
if (plot != null && plot.hasOwner()) {
public boolean unloadChunk(String world, Chunk chunk, boolean safe) {
if (safe && shouldSave(world, chunk.getX(), chunk.getZ())) {
return false;
}
Object c = this.methodGetHandleChunk.of(chunk).call();
@ -185,14 +111,42 @@ public class ChunkListener implements Listener {
}
return true;
}
public boolean shouldSave(String world, int X, int Z) {
int x = X << 4;
int z = Z << 4;
int x2 = x + 15;
int z2 = z + 15;
Plot plot = new Location(world, x, 1, z).getOwnedPlotAbs();
if (plot != null && plot.hasOwner()) {
return true;
}
plot = new Location(world, x2, 1, z2).getOwnedPlotAbs();
if (plot != null && plot.hasOwner()) {
return true;
}
plot = new Location(world, x2, 1, z).getOwnedPlotAbs();
if (plot != null && plot.hasOwner()) {
return true;
}
plot = new Location(world, x, 1, z2).getOwnedPlotAbs();
if (plot != null && plot.hasOwner()) {
return true;
}
plot = new Location(world, x + 7, 1, z + 7).getOwnedPlotAbs();
if (plot != null && plot.hasOwner()) {
return true;
}
return false;
}
@EventHandler
public void onChunkUnload(ChunkUnloadEvent event) {
if (Settings.CHUNK_PROCESSOR_TRIM_ON_SAVE) {
if (Settings.CHUNK_PROCESSOR_GC) {
Chunk chunk = event.getChunk();
String world = chunk.getWorld().getName();
if (PS.get().hasPlotArea(world)) {
if (unloadChunk(world, chunk)) {
if (unloadChunk(world, chunk, true)) {
return;
}
}

View File

@ -1,330 +0,0 @@
package com.plotsquared.bukkit.listeners.worldedit;
import com.intellectualcrafters.plot.PS;
import com.intellectualcrafters.plot.config.C;
import com.intellectualcrafters.plot.config.Settings;
import com.intellectualcrafters.plot.object.Plot;
import com.intellectualcrafters.plot.object.PlotPlayer;
import com.intellectualcrafters.plot.object.RegionWrapper;
import com.intellectualcrafters.plot.util.MainUtil;
import com.intellectualcrafters.plot.util.Permissions;
import com.intellectualcrafters.plot.util.SetQueue;
import com.plotsquared.bukkit.BukkitMain;
import com.plotsquared.bukkit.util.BukkitUtil;
import com.plotsquared.listener.WEManager;
import com.sk89q.worldedit.BlockVector;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.selections.Selection;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
public class WEListener implements Listener {
public final Set<String> other = new HashSet<>(Arrays.asList("undo", "redo"));
private final Set<String> rad1 = new HashSet<>(
Arrays.asList("forestgen", "pumpkins", "drain", "fixwater", "fixlava", "replacenear", "snow", "thaw", "ex", "butcher", "size"));
private final Set<String> rad2 = new HashSet<>(Arrays.asList("fill", "fillr", "removenear", "remove"));
private final Set<String> rad2_1 = new HashSet<>(Arrays.asList("hcyl", "cyl"));
private final Set<String> rad2_2 = new HashSet<>(Arrays.asList("sphere", "pyramid"));
private final Set<String> rad2_3 = Collections.singleton("brush smooth");
private final Set<String> rad3_1 = Collections.singleton("brush gravity");
private final Set<String> rad3_2 = new HashSet<>(Arrays.asList("brush sphere", "brush cylinder"));
private final Set<String> region = new HashSet<>(
Arrays.asList("move", "set", "replace", "overlay", "walls", "outline", "deform", "hollow", "smooth", "naturalize", "paste", "count",
"distr",
"regen", "copy", "cut", "green", "setbiome"));
private final Set<String> regionExtend = Collections.singleton("stack");
private final Set<String> restricted = Collections.singleton("up");
public String reduceCmd(String cmd, boolean single) {
if (cmd.startsWith("/worldedit:/")) {
return cmd.substring(12);
}
if (cmd.startsWith("/worldedit:")) {
return cmd.substring(11);
}
if (cmd.startsWith("//")) {
return cmd.substring(2);
}
if (single && cmd.startsWith("/")) {
return cmd.substring(1);
}
return cmd;
}
public int getInt(String s) {
try {
int max = 0;
String[] split = s.split(",");
for (String rad : split) {
int val = Integer.parseInt(rad);
if (val > max) {
max = val;
}
}
return max;
} catch (NumberFormatException ignored) {
return 0;
}
}
public boolean checkVolume(PlotPlayer player, long volume, long max, Cancellable e) {
if (volume > max) {
MainUtil.sendMessage(player, C.WORLDEDIT_VOLUME.s().replaceAll("%current%", String.valueOf(volume)).replaceAll("%max%", String.valueOf(max)));
e.setCancelled(true);
}
if (Permissions.hasPermission(player, "plots.worldedit.bypass")) {
MainUtil.sendMessage(player, C.WORLDEDIT_BYPASS);
}
return true;
}
public boolean checkSelection(Player p, PlotPlayer pp, int modifier, long max, Cancellable e) {
Selection selection = BukkitMain.worldEdit.getSelection(p);
if (selection == null) {
return true;
}
BlockVector pos1 = selection.getNativeMinimumPoint().toBlockVector();
BlockVector pos2 = selection.getNativeMaximumPoint().toBlockVector();
HashSet<RegionWrapper> mask = WEManager.getMask(pp);
RegionWrapper region = new RegionWrapper(pos1.getBlockX(), pos2.getBlockX(), pos1.getBlockZ(), pos2.getBlockZ());
if (Settings.REQUIRE_SELECTION) {
String arg = null;
if (!WEManager.regionContains(region, mask)) {
arg = "pos1 + pos2";
} else if (!WEManager.maskContains(mask, pos1.getBlockX(), pos1.getBlockY(), pos1.getBlockZ())) {
arg = "pos1";
} else if (!WEManager.maskContains(mask, pos2.getBlockX(), pos2.getBlockY(), pos2.getBlockZ())) {
arg = "pos2";
}
if (arg != null) {
e.setCancelled(true);
MainUtil.sendMessage(pp, C.REQUIRE_SELECTION_IN_MASK, arg);
if (Permissions.hasPermission(pp, "plots.worldedit.bypass")) {
MainUtil.sendMessage(pp, C.WORLDEDIT_BYPASS);
}
return true;
}
if (!WEManager.regionContains(region, mask)) {
MainUtil.sendMessage(pp, C.REQUIRE_SELECTION_IN_MASK, "pos1 + pos2");
e.setCancelled(true);
if (Permissions.hasPermission(pp, "plots.worldedit.bypass")) {
MainUtil.sendMessage(pp, C.WORLDEDIT_BYPASS);
}
return true;
}
}
long volume = Math.abs((pos1.getBlockX() - pos2.getBlockX()) * (pos1.getBlockY() - pos2.getBlockY()) * (pos1.getBlockZ() - pos2.getBlockZ()))
* modifier;
return checkVolume(pp, volume, max, e);
}
public boolean delay(final Player player, final String command, boolean delayed) {
if (!Settings.QUEUE_COMMANDS || !Settings.EXPERIMENTAL_FAST_ASYNC_WORLDEDIT) {
return false;
}
boolean free = SetQueue.IMP.addTask(null);
if (free) {
if (delayed) {
MainUtil.sendMessage(BukkitUtil.getPlayer(player), C.WORLDEDIT_RUN, command);
Bukkit.getServer().dispatchCommand(player, command.substring(1));
} else {
return false;
}
} else {
if (!delayed) {
MainUtil.sendMessage(BukkitUtil.getPlayer(player), C.WORLDEDIT_DELAYED);
}
SetQueue.IMP.addTask(new Runnable() {
@Override
public void run() {
delay(player, command, true);
}
});
}
return true;
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public boolean onPlayerCommand(PlayerCommandPreprocessEvent e) {
WorldEditPlugin worldedit = BukkitMain.worldEdit;
if (worldedit == null) {
HandlerList.unregisterAll(this);
return true;
}
Player p = e.getPlayer();
PlotPlayer pp = BukkitUtil.getPlayer(p);
if (!PS.get().hasPlotArea(p.getWorld().getName())) {
return true;
}
String message = e.getMessage();
String cmd = message.toLowerCase();
String[] split = cmd.split(" ");
long maxVolume = Settings.WE_MAX_VOLUME;
long maxIterations = Settings.WE_MAX_ITERATIONS;
if (pp.getAttribute("worldedit")) {
return true;
}
boolean single = true;
if (split.length >= 2) {
String reduced = reduceCmd(split[0], single);
String reduced2 = reduceCmd(split[0] + ' ' + split[1], single);
if (this.rad1.contains(reduced)) {
if (delay(p, message, false)) {
e.setCancelled(true);
return true;
}
long volume = getInt(split[1]) * 256;
return checkVolume(pp, volume, maxVolume, e);
}
if (this.rad2.contains(reduced)) {
if (delay(p, message, false)) {
e.setCancelled(true);
return true;
}
if (split.length >= 3) {
long volume = getInt(split[2]) * 256;
return checkVolume(pp, volume, maxVolume, e);
}
return true;
}
if (this.rad2_1.contains(reduced)) {
if (delay(p, message, false)) {
e.setCancelled(true);
return true;
}
if (split.length >= 4) {
long volume = getInt(split[2]) * getInt(split[3]);
return checkVolume(pp, volume, maxVolume, e);
}
return true;
}
if (this.rad2_2.contains(reduced)) {
if (delay(p, message, false)) {
e.setCancelled(true);
return true;
}
if (split.length >= 3) {
long radius = getInt(split[2]);
long volume = radius * radius;
return checkVolume(pp, volume, maxVolume, e);
}
return true;
}
if (this.rad2_3.contains(reduced2)) {
if (delay(p, message, false)) {
e.setCancelled(true);
return true;
}
if (split.length >= 3) {
if (split.length == 4) {
int iterations = getInt(split[3]);
if (iterations > maxIterations) {
MainUtil.sendMessage(pp,
C.WORLDEDIT_ITERATIONS.s().replaceAll("%current%", String.valueOf(iterations)).replaceAll("%max%",
String.valueOf(maxIterations)));
e.setCancelled(true);
if (Permissions.hasPermission(pp, "plots.worldedit.bypass")) {
MainUtil.sendMessage(pp, C.WORLDEDIT_BYPASS);
}
return true;
}
}
long radius = getInt(split[2]);
long volume = radius * radius;
return checkVolume(pp, volume, maxVolume, e);
}
return true;
}
if (this.rad3_1.contains(reduced2)) {
if (delay(p, message, false)) {
e.setCancelled(true);
return true;
}
if (split.length >= 3) {
int i = 2;
if (split[i].equalsIgnoreCase("-h")) {
i = 3;
}
long radius = getInt(split[i]);
long volume = radius * radius;
return checkVolume(pp, volume, maxVolume, e);
}
return true;
}
if (this.rad3_2.contains(reduced2)) {
if (delay(p, message, false)) {
e.setCancelled(true);
return true;
}
if (split.length >= 4) {
int i = 3;
if (split[i].equalsIgnoreCase("-h")) {
i = 4;
}
long radius = getInt(split[i]);
long volume = radius * radius;
return checkVolume(pp, volume, maxVolume, e);
}
return true;
}
if (this.regionExtend.contains(reduced)) {
if (delay(p, message, false)) {
e.setCancelled(true);
return true;
}
return checkSelection(p, pp, getInt(split[1]), maxVolume, e);
}
}
String reduced = reduceCmd(split[0], single);
if (Settings.WE_BLACKLIST.contains(reduced)) {
MainUtil.sendMessage(pp, C.WORLDEDIT_UNSAFE);
e.setCancelled(true);
if (Permissions.hasPermission(pp, "plots.worldedit.bypass")) {
MainUtil.sendMessage(pp, C.WORLDEDIT_BYPASS);
}
}
if (this.restricted.contains(reduced)) {
Plot plot = pp.getCurrentPlot();
if ((plot != null) && plot.isAdded(pp.getUUID())) {
if (delay(p, message, false)) {
e.setCancelled(true);
return true;
}
return true;
}
e.setCancelled(true);
MainUtil.sendMessage(pp, C.NO_PLOT_PERMS);
if (Permissions.hasPermission(pp, "plots.worldedit.bypass")) {
MainUtil.sendMessage(pp, C.WORLDEDIT_BYPASS);
}
return true;
}
if (this.region.contains(reduced)) {
if (delay(p, message, false)) {
e.setCancelled(true);
return true;
}
return checkSelection(p, pp, 1, maxVolume, e);
}
if (this.other.contains(reduced)) {
if (delay(p, message, false)) {
e.setCancelled(true);
return true;
}
}
return true;
}
}