Files
LogBlock/src/de/diddiz/LogBlock/WorldEditor.java
T

223 lines
8.8 KiB
Java

package de.diddiz.LogBlock;
import static de.diddiz.util.BukkitUtils.equalTypes;
import static de.diddiz.util.BukkitUtils.materialName;
import static de.diddiz.util.BukkitUtils.modifyContainer;
import java.io.File;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState;
import org.bukkit.block.ContainerBlock;
import org.bukkit.block.Sign;
import org.bukkit.inventory.ItemStack;
import org.bukkit.material.Bed;
import org.bukkit.material.PistonBaseMaterial;
import org.bukkit.material.PistonExtensionMaterial;
public class WorldEditor implements Runnable
{
private final Logger log;
private final LogBlock logblock;
private final Config config;
private final Queue<Edit> edits = new LinkedBlockingQueue<Edit>();
private final World world;
private int taskID;
private int successes = 0, errors = 0, blacklistCollisions = 0;
private long elapsedTime = 0;
final List<String> errorList = new ArrayList<String>();
public WorldEditor(LogBlock logblock, World world) {
log = logblock.getServer().getLogger();
this.logblock = logblock;
config = logblock.getConfig();
this.world = world;
}
public int getSize() {
return edits.size();
}
public int getSuccesses() {
return successes;
}
public int getErrors() {
return errors;
}
public int getBlacklistCollisions() {
return blacklistCollisions;
}
public void queueEdit(int x, int y, int z, int replaced, int type, byte data, String signtext, short itemType, short itemAmount, byte itemData) {
edits.add(new Edit(0, new Location(world, x, y, z), null, replaced, type, data, signtext, new ChestAccess(itemType, itemAmount, itemData)));
}
public long getElapsedTime() {
return elapsedTime;
}
synchronized public void start() throws WorldEditorException {
final long start = System.currentTimeMillis();
taskID = logblock.getServer().getScheduler().scheduleSyncRepeatingTask(logblock, this, 0, 1);
if (taskID == -1)
throw new WorldEditorException("Failed to schedule task");
try {
this.wait();
} catch (final InterruptedException ex) {
throw new WorldEditorException("Interrupted");
}
elapsedTime = System.currentTimeMillis() - start;
}
@Override
public synchronized void run() {
int counter = 0;
while (!edits.isEmpty() && counter < 1000) {
try {
switch (edits.poll().perform()) {
case SUCCESS:
successes++;
break;
case BLACKLISTED:
blacklistCollisions++;
break;
}
} catch (final WorldEditorException ex) {
errors++;
errorList.add(ex.getMessage());
} catch (final Exception ex) {
errors++;
errorList.add(ex.getMessage());
log.log(Level.WARNING, "[LogBlock WorldEditor] Exeption: ", ex);
}
counter++;
}
if (edits.isEmpty()) {
logblock.getServer().getScheduler().cancelTask(taskID);
if (errorList.size() > 0)
try {
final File file = new File("plugins/LogBlock/error/WorldEditor-" + new SimpleDateFormat("yy-MM-dd-HH-mm-ss").format(System.currentTimeMillis()) + ".log");
file.getParentFile().mkdirs();
final PrintWriter writer = new PrintWriter(file);
for (final String err : errorList)
writer.println(err);
writer.close();
} catch (final Exception ex) {}
notify();
}
}
private static enum PerformResult {
SUCCESS, BLACKLISTED, NO_ACTION
}
private class Edit extends BlockChange
{
public Edit(long time, Location loc, String playerName, int replaced, int type, byte data, String signtext, ChestAccess ca) {
super(time, loc, playerName, replaced, type, data, signtext, ca);
}
PerformResult perform() throws WorldEditorException {
if (config.dontRollback.contains(replaced))
return PerformResult.BLACKLISTED;
final Block block = loc.getBlock();
if (replaced == 0 && block.getTypeId() == 0)
return PerformResult.SUCCESS;
final BlockState state = block.getState();
if (!world.isChunkLoaded(block.getChunk()))
world.loadChunk(block.getChunk());
if (type == replaced) {
if (type == 0) {
if (!block.setTypeId(0))
throw new WorldEditorException(block.getTypeId(), 0, block.getLocation());
} else if (ca != null && (type == 23 || type == 54 || type == 61 || type == 62)) {
int leftover = 0;
try {
leftover = modifyContainer(state, new ItemStack(ca.itemType, -ca.itemAmount, (short)0, ca.itemData));
if (leftover > 0)
for (final BlockFace face : new BlockFace[]{BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST})
if (block.getFace(face).getTypeId() == 54)
leftover = modifyContainer(block.getFace(face).getState(), new ItemStack(ca.itemType, ca.itemAmount < 0 ? leftover : -leftover, (short)0, ca.itemData));
} catch (final Exception ex) {
throw new WorldEditorException(ex.getMessage(), block.getLocation());
}
if (!state.update())
throw new WorldEditorException("Failed to update inventory of " + materialName(block.getTypeId()), block.getLocation());
if (leftover > 0 && ca.itemAmount < 0)
throw new WorldEditorException("Not enough space left in " + materialName(block.getTypeId()), block.getLocation());
} else
return PerformResult.NO_ACTION;
return PerformResult.SUCCESS;
}
if (!(equalTypes(block.getTypeId(), type) || config.replaceAnyway.contains(block.getTypeId())))
return PerformResult.NO_ACTION;
if (state instanceof ContainerBlock) {
((ContainerBlock)state).getInventory().clear();
state.update();
}
if (!block.setTypeIdAndData(replaced, data, true))
throw new WorldEditorException(block.getTypeId(), replaced, block.getLocation());
final int curtype = block.getTypeId();
if (signtext != null && (curtype == 63 || curtype == 68)) {
final Sign sign = (Sign)block.getState();
final String[] lines = signtext.split("\0", 4);
if (lines.length < 4)
return PerformResult.NO_ACTION;
for (int i = 0; i < 4; i++)
sign.setLine(i, lines[i]);
if (!sign.update())
throw new WorldEditorException("Failed to update signtext of " + materialName(block.getTypeId()), block.getLocation());
} else if (curtype == 26) {
final Bed bed = (Bed)block.getState().getData();
final Block secBlock = bed.isHeadOfBed() ? block.getFace(bed.getFacing().getOppositeFace()) : block.getFace(bed.getFacing());
if (secBlock.getTypeId() == 0 && !secBlock.setTypeIdAndData(26, (byte)(bed.getData() | 8), true))
throw new WorldEditorException(secBlock.getTypeId(), 26, secBlock.getLocation());
} else if (curtype == 64 || curtype == 71) {
final byte blockData = block.getData();
final Block secBlock = (blockData & 8) == 8 ? block.getFace(BlockFace.DOWN) : block.getFace(BlockFace.UP);
if (secBlock.getTypeId() == 0 && !secBlock.setTypeIdAndData(curtype, (byte)(blockData | 8), true))
throw new WorldEditorException(secBlock.getTypeId(), curtype, secBlock.getLocation());
} else if ((curtype == 29 || curtype == 33) && (block.getData() & 8) > 0) {
final PistonBaseMaterial piston = (PistonBaseMaterial)block.getState().getData();
final Block secBlock = block.getFace(piston.getFacing());
if (secBlock.getTypeId() == 0 && !secBlock.setTypeIdAndData(34, curtype == 29 ? (byte)(block.getData() | 8) : (byte)(block.getData() & ~8), true))
throw new WorldEditorException(secBlock.getTypeId(), 34, secBlock.getLocation());
} else if (curtype == 34) {
final PistonExtensionMaterial piston = (PistonExtensionMaterial)block.getState().getData();
final Block secBlock = block.getFace(piston.getFacing().getOppositeFace());
if (secBlock.getTypeId() == 0 && !secBlock.setTypeIdAndData(piston.isSticky() ? 29 : 33, (byte)(block.getData() | 8), true))
throw new WorldEditorException(secBlock.getTypeId(), piston.isSticky() ? 29 : 33, secBlock.getLocation());
} else if (curtype == 18 && (block.getData() & 8) > 0)
block.setData((byte)(block.getData() & 0xF7));
return PerformResult.SUCCESS;
}
}
@SuppressWarnings("serial")
public static class WorldEditorException extends Exception
{
public WorldEditorException(String msg) {
super(msg);
}
public WorldEditorException(int typeBefore, int typeAfter, Location loc) {
this("Failed to replace " + materialName(typeBefore) + " with " + materialName(typeAfter), loc);
}
public WorldEditorException(String msg, Location loc) {
super(msg + " at " + loc.getBlockX() + ":" + loc.getBlockY() + ":" + loc.getBlockZ());
}
}
}