forked from LogBlock/LogBlock
223 lines
8.8 KiB
Java
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());
|
|
}
|
|
}
|
|
}
|