Not every material has a BlockData

This commit is contained in:
Brokkonaut
2018-07-21 14:30:44 +02:00
parent 51505cf34a
commit 0792fbd32a
2 changed files with 306 additions and 289 deletions

View File

@ -20,6 +20,16 @@ public class MaterialConverter {
private static String[] idToBlockState = new String[10]; private static String[] idToBlockState = new String[10];
private static HashMap<String, Integer> blockStateToID = new HashMap<>(); private static HashMap<String, Integer> blockStateToID = new HashMap<>();
private static int nextBlockStateId; private static int nextBlockStateId;
private static HashMap<String, Material> materialKeyToMaterial = new HashMap<>();
static {
for (Material m : Material.values()) {
if (!m.name().startsWith("LEGACY_") && m.getKey() != null) {
materialKeyToMaterial.put(m.getKey().toString(), m);
}
}
}
public synchronized static int getOrAddMaterialId(NamespacedKey nameSpaceKey) { public synchronized static int getOrAddMaterialId(NamespacedKey nameSpaceKey) {
return getOrAddMaterialId(nameSpaceKey.toString()); return getOrAddMaterialId(nameSpaceKey.toString());
@ -80,7 +90,11 @@ public class MaterialConverter {
} }
public static Material getMaterial(int materialId) { public static Material getMaterial(int materialId) {
return getBlockData(materialId, -1).getMaterial(); String material = idToMaterial[materialId];
if (materialId >= 0) {
material = material + idToBlockState[materialId];
}
return materialKeyToMaterial.get(material);
} }
public static void initializeMaterials(Connection connection) throws SQLException { public static void initializeMaterials(Connection connection) throws SQLException {

View File

@ -1,288 +1,291 @@
package de.diddiz.LogBlock; package de.diddiz.LogBlock;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.BlockFace; import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState; import org.bukkit.block.BlockState;
import org.bukkit.block.Sign; import org.bukkit.block.Sign;
import org.bukkit.block.data.Bisected.Half; import org.bukkit.block.data.Bisected.Half;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.type.Bed; import org.bukkit.block.data.type.Bed;
import org.bukkit.block.data.type.Bed.Part; import org.bukkit.block.data.type.Bed.Part;
import org.bukkit.block.data.type.Door; import org.bukkit.block.data.type.Door;
import org.bukkit.block.data.type.Piston; import org.bukkit.block.data.type.Piston;
import org.bukkit.block.data.type.PistonHead; import org.bukkit.block.data.type.PistonHead;
import org.bukkit.block.data.type.TechnicalPiston.Type; import org.bukkit.block.data.type.TechnicalPiston.Type;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import java.io.File; import java.io.File;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Queue; import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Level; import java.util.logging.Level;
import static de.diddiz.LogBlock.config.Config.dontRollback; import static de.diddiz.LogBlock.config.Config.dontRollback;
import static de.diddiz.LogBlock.config.Config.replaceAnyway; import static de.diddiz.LogBlock.config.Config.replaceAnyway;
import static de.diddiz.util.BukkitUtils.*; import static de.diddiz.util.BukkitUtils.*;
import static org.bukkit.Bukkit.getLogger; import static org.bukkit.Bukkit.getLogger;
public class WorldEditor implements Runnable { public class WorldEditor implements Runnable {
private final LogBlock logblock; private final LogBlock logblock;
private final Queue<Edit> edits = new LinkedBlockingQueue<Edit>(); private final Queue<Edit> edits = new LinkedBlockingQueue<Edit>();
private final World world; private final World world;
/** /**
* The player responsible for editing the world, used to report progress * The player responsible for editing the world, used to report progress
*/ */
private CommandSender sender; private CommandSender sender;
private int taskID; private int taskID;
private int successes = 0, blacklistCollisions = 0; private int successes = 0, blacklistCollisions = 0;
private long elapsedTime = 0; private long elapsedTime = 0;
public LookupCacheElement[] errors; public LookupCacheElement[] errors;
public WorldEditor(LogBlock logblock, World world) { public WorldEditor(LogBlock logblock, World world) {
this.logblock = logblock; this.logblock = logblock;
this.world = world; this.world = world;
} }
public int getSize() { public int getSize() {
return edits.size(); return edits.size();
} }
public int getSuccesses() { public int getSuccesses() {
return successes; return successes;
} }
public int getErrors() { public int getErrors() {
return errors.length; return errors.length;
} }
public int getBlacklistCollisions() { public int getBlacklistCollisions() {
return blacklistCollisions; return blacklistCollisions;
} }
public void setSender(CommandSender sender) { public void setSender(CommandSender sender) {
this.sender = sender; this.sender = sender;
} }
public void queueEdit(int x, int y, int z, int replaced, int replaceData, int type, int typeData, String signtext, ChestAccess item) { public void queueEdit(int x, int y, int z, int replaced, int replaceData, int type, int typeData, String signtext, ChestAccess item) {
edits.add(new Edit(0, new Location(world, x, y, z), null, replaced, replaceData, type, typeData, signtext, item)); edits.add(new Edit(0, new Location(world, x, y, z), null, replaced, replaceData, type, typeData, signtext, item));
} }
public long getElapsedTime() { public long getElapsedTime() {
return elapsedTime; return elapsedTime;
} }
synchronized public void start() throws Exception { synchronized public void start() throws Exception {
final long start = System.currentTimeMillis(); final long start = System.currentTimeMillis();
taskID = logblock.getServer().getScheduler().scheduleSyncRepeatingTask(logblock, this, 0, 1); taskID = logblock.getServer().getScheduler().scheduleSyncRepeatingTask(logblock, this, 0, 1);
if (taskID == -1) { if (taskID == -1) {
throw new Exception("Failed to schedule task"); throw new Exception("Failed to schedule task");
} }
try { try {
this.wait(); this.wait();
} catch (final InterruptedException ex) { } catch (final InterruptedException ex) {
throw new Exception("Interrupted"); throw new Exception("Interrupted");
} }
elapsedTime = System.currentTimeMillis() - start; elapsedTime = System.currentTimeMillis() - start;
} }
@Override @Override
public synchronized void run() { public synchronized void run() {
final List<WorldEditorException> errorList = new ArrayList<WorldEditorException>(); final List<WorldEditorException> errorList = new ArrayList<WorldEditorException>();
int counter = 0; int counter = 0;
float size = edits.size(); float size = edits.size();
while (!edits.isEmpty() && counter < 100) { while (!edits.isEmpty() && counter < 100) {
try { try {
switch (edits.poll().perform()) { switch (edits.poll().perform()) {
case SUCCESS: case SUCCESS:
successes++; successes++;
break; break;
case BLACKLISTED: case BLACKLISTED:
blacklistCollisions++; blacklistCollisions++;
break; break;
case NO_ACTION: case NO_ACTION:
break; break;
} }
} catch (final WorldEditorException ex) { } catch (final WorldEditorException ex) {
errorList.add(ex); errorList.add(ex);
} catch (final Exception ex) { } catch (final Exception ex) {
getLogger().log(Level.WARNING, "[WorldEditor] Exeption: ", ex); getLogger().log(Level.WARNING, "[WorldEditor] Exeption: ", ex);
} }
counter++; counter++;
if (sender != null) { if (sender != null) {
float percentage = ((size - edits.size()) / size) * 100.0F; float percentage = ((size - edits.size()) / size) * 100.0F;
if (percentage % 20 == 0) { if (percentage % 20 == 0) {
sender.sendMessage(ChatColor.GOLD + "[LogBlock]" + ChatColor.YELLOW + " Rollback progress: " + percentage + "%" + sender.sendMessage(ChatColor.GOLD + "[LogBlock]" + ChatColor.YELLOW + " Rollback progress: " + percentage + "%" +
" Blocks edited: " + counter); " Blocks edited: " + counter);
} }
} }
} }
if (edits.isEmpty()) { if (edits.isEmpty()) {
logblock.getServer().getScheduler().cancelTask(taskID); logblock.getServer().getScheduler().cancelTask(taskID);
if (errorList.size() > 0) { if (errorList.size() > 0) {
try { try {
final File file = new File("plugins/LogBlock/error/WorldEditor-" + new SimpleDateFormat("yy-MM-dd-HH-mm-ss").format(System.currentTimeMillis()) + ".log"); final File file = new File("plugins/LogBlock/error/WorldEditor-" + new SimpleDateFormat("yy-MM-dd-HH-mm-ss").format(System.currentTimeMillis()) + ".log");
file.getParentFile().mkdirs(); file.getParentFile().mkdirs();
final PrintWriter writer = new PrintWriter(file); final PrintWriter writer = new PrintWriter(file);
for (final LookupCacheElement err : errorList) { for (final LookupCacheElement err : errorList) {
writer.println(err.getMessage()); writer.println(err.getMessage());
} }
writer.close(); writer.close();
} catch (final Exception ex) { } catch (final Exception ex) {
} }
} }
errors = errorList.toArray(new WorldEditorException[errorList.size()]); errors = errorList.toArray(new WorldEditorException[errorList.size()]);
notify(); notify();
} }
} }
private static enum PerformResult { private static enum PerformResult {
SUCCESS, BLACKLISTED, NO_ACTION SUCCESS, BLACKLISTED, NO_ACTION
} }
private class Edit extends BlockChange { private class Edit extends BlockChange {
public Edit(long time, Location loc, Actor actor, int replaced, int replaceData, int type, int typeData, String signtext, ChestAccess ca) { public Edit(long time, Location loc, Actor actor, int replaced, int replaceData, int type, int typeData, String signtext, ChestAccess ca) {
super(time, loc, actor, replaced, replaceData, type, typeData, signtext, ca); super(time, loc, actor, replaced, replaceData, type, typeData, signtext, ca);
} }
PerformResult perform() throws WorldEditorException { PerformResult perform() throws WorldEditorException {
BlockData replacedBlock = MaterialConverter.getBlockData(this.replacedMaterial, replacedData); BlockData replacedBlock = MaterialConverter.getBlockData(this.replacedMaterial, replacedData);
BlockData setBlock = MaterialConverter.getBlockData(this.typeMaterial, typeData); BlockData setBlock = MaterialConverter.getBlockData(this.typeMaterial, typeData);
// action: set to replaced if (replacedBlock == null || setBlock == null) {
throw new WorldEditorException("Could not parse the material", loc.clone());
if (dontRollback.contains(replacedBlock.getMaterial())) { }
return PerformResult.BLACKLISTED; // action: set to replaced
}
final Block block = loc.getBlock(); if (dontRollback.contains(replacedBlock.getMaterial())) {
if (replacedBlock.getMaterial() == Material.AIR && block.getType() == Material.AIR) { return PerformResult.BLACKLISTED;
return PerformResult.NO_ACTION; }
} final Block block = loc.getBlock();
final BlockState state = block.getState(); if (replacedBlock.getMaterial() == Material.AIR && block.getType() == Material.AIR) {
if (!world.isChunkLoaded(block.getChunk())) { return PerformResult.NO_ACTION;
world.loadChunk(block.getChunk()); }
} final BlockState state = block.getState();
if (setBlock.equals(replacedBlock)) { if (!world.isChunkLoaded(block.getChunk())) {
if (setBlock.getMaterial() == Material.AIR) { world.loadChunk(block.getChunk());
block.setType(Material.AIR); }
} else if (ca != null) { if (setBlock.equals(replacedBlock)) {
if (state instanceof InventoryHolder) { if (setBlock.getMaterial() == Material.AIR) {
int leftover; block.setType(Material.AIR);
try { } else if (ca != null) {
leftover = modifyContainer(state, new ItemStack(ca.itemStack), !ca.remove); if (state instanceof InventoryHolder) {
// Special-case blocks which might be double chests int leftover;
if (leftover > 0 && (setBlock.getMaterial() == Material.CHEST || setBlock.getMaterial() == Material.TRAPPED_CHEST)) { try {
for (final BlockFace face : new BlockFace[]{BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST}) { leftover = modifyContainer(state, new ItemStack(ca.itemStack), !ca.remove);
if (block.getRelative(face).getType() == setBlock.getMaterial()) { // Special-case blocks which might be double chests
ItemStack remaining = new ItemStack(ca.itemStack); if (leftover > 0 && (setBlock.getMaterial() == Material.CHEST || setBlock.getMaterial() == Material.TRAPPED_CHEST)) {
remaining.setAmount(leftover); for (final BlockFace face : new BlockFace[]{BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST}) {
leftover = modifyContainer(block.getRelative(face).getState(), remaining, !ca.remove); if (block.getRelative(face).getType() == setBlock.getMaterial()) {
} ItemStack remaining = new ItemStack(ca.itemStack);
} remaining.setAmount(leftover);
} leftover = modifyContainer(block.getRelative(face).getState(), remaining, !ca.remove);
} catch (final Exception ex) { }
throw new WorldEditorException(ex.getMessage(), block.getLocation()); }
} }
if (leftover > 0 && ca.remove) { } catch (final Exception ex) {
throw new WorldEditorException("Not enough space left in " + block.getType(), block.getLocation()); throw new WorldEditorException(ex.getMessage(), block.getLocation());
} }
} if (leftover > 0 && ca.remove) {
} else if (!block.getBlockData().equals(replacedBlock)) { throw new WorldEditorException("Not enough space left in " + block.getType(), block.getLocation());
block.setBlockData(replacedBlock); }
} else { }
return PerformResult.NO_ACTION; } else if (!block.getBlockData().equals(replacedBlock)) {
} block.setBlockData(replacedBlock);
return PerformResult.SUCCESS; } else {
} return PerformResult.NO_ACTION;
if (block.getType() != setBlock.getMaterial() && !replaceAnyway.contains(block.getType())) { }
return PerformResult.NO_ACTION; return PerformResult.SUCCESS;
} }
if (state instanceof InventoryHolder) { if (block.getType() != setBlock.getMaterial() && !replaceAnyway.contains(block.getType())) {
((InventoryHolder) state).getInventory().clear(); return PerformResult.NO_ACTION;
state.update(); }
} if (state instanceof InventoryHolder) {
block.setBlockData(replacedBlock); ((InventoryHolder) state).getInventory().clear();
BlockData newData = block.getBlockData(); state.update();
}
final Material curtype = block.getType(); block.setBlockData(replacedBlock);
if (signtext != null && (curtype == Material.SIGN || curtype == Material.WALL_SIGN)) { BlockData newData = block.getBlockData();
final Sign sign = (Sign) block.getState();
final String[] lines = signtext.split("\0", 4); final Material curtype = block.getType();
if (lines.length < 4) { if (signtext != null && (curtype == Material.SIGN || curtype == Material.WALL_SIGN)) {
return PerformResult.NO_ACTION; final Sign sign = (Sign) block.getState();
} final String[] lines = signtext.split("\0", 4);
for (int i = 0; i < 4; i++) { if (lines.length < 4) {
sign.setLine(i, lines[i]); return PerformResult.NO_ACTION;
} }
if (!sign.update()) { for (int i = 0; i < 4; i++) {
throw new WorldEditorException("Failed to update signtext of " + block.getType(), block.getLocation()); sign.setLine(i, lines[i]);
} }
} else if (newData instanceof Bed) { if (!sign.update()) {
final Bed bed = (Bed) newData; throw new WorldEditorException("Failed to update signtext of " + block.getType(), block.getLocation());
final Block secBlock = bed.getPart() == Part.HEAD ? block.getRelative(bed.getFacing().getOppositeFace()) : block.getRelative(bed.getFacing()); }
if (secBlock.isEmpty()) { } else if (newData instanceof Bed) {
Bed bed2 = (Bed) bed.clone(); final Bed bed = (Bed) newData;
bed2.setPart(bed.getPart() == Part.HEAD ? Part.FOOT : Part.HEAD); final Block secBlock = bed.getPart() == Part.HEAD ? block.getRelative(bed.getFacing().getOppositeFace()) : block.getRelative(bed.getFacing());
secBlock.setBlockData(bed2); if (secBlock.isEmpty()) {
} Bed bed2 = (Bed) bed.clone();
} else if (newData instanceof Door) { bed2.setPart(bed.getPart() == Part.HEAD ? Part.FOOT : Part.HEAD);
final Door door = (Door) newData; secBlock.setBlockData(bed2);
final Block secBlock = door.getHalf() == Half.TOP ? block.getRelative(BlockFace.DOWN) : block.getRelative(BlockFace.UP); }
if (secBlock.isEmpty()) { } else if (newData instanceof Door) {
Door door2 = (Door) door.clone(); final Door door = (Door) newData;
door2.setHalf(door.getHalf() == Half.TOP ? Half.BOTTOM : Half.TOP); final Block secBlock = door.getHalf() == Half.TOP ? block.getRelative(BlockFace.DOWN) : block.getRelative(BlockFace.UP);
secBlock.setBlockData(door2); if (secBlock.isEmpty()) {
} Door door2 = (Door) door.clone();
} else if ((curtype == Material.PISTON || curtype == Material.STICKY_PISTON)) { door2.setHalf(door.getHalf() == Half.TOP ? Half.BOTTOM : Half.TOP);
Piston piston = (Piston) newData; secBlock.setBlockData(door2);
if (piston.isExtended()) { }
final Block secBlock = block.getRelative(piston.getFacing()); } else if ((curtype == Material.PISTON || curtype == Material.STICKY_PISTON)) {
if (secBlock.isEmpty()) { Piston piston = (Piston) newData;
PistonHead head = (PistonHead) Material.PISTON_HEAD.createBlockData(); if (piston.isExtended()) {
head.setFacing(piston.getFacing()); final Block secBlock = block.getRelative(piston.getFacing());
head.setType(curtype == Material.PISTON ? Type.NORMAL : Type.STICKY); if (secBlock.isEmpty()) {
secBlock.setBlockData(head); PistonHead head = (PistonHead) Material.PISTON_HEAD.createBlockData();
} head.setFacing(piston.getFacing());
} head.setType(curtype == Material.PISTON ? Type.NORMAL : Type.STICKY);
} else if (curtype == Material.PISTON_HEAD) { secBlock.setBlockData(head);
PistonHead head = (PistonHead) newData; }
final Block secBlock = block.getRelative(head.getFacing().getOppositeFace()); }
if (secBlock.isEmpty()) { } else if (curtype == Material.PISTON_HEAD) {
Piston piston = (Piston) (head.getType() == Type.NORMAL ? Material.PISTON : Material.STICKY_PISTON).createBlockData(); PistonHead head = (PistonHead) newData;
piston.setFacing(head.getFacing()); final Block secBlock = block.getRelative(head.getFacing().getOppositeFace());
piston.setExtended(true); if (secBlock.isEmpty()) {
secBlock.setBlockData(piston); Piston piston = (Piston) (head.getType() == Type.NORMAL ? Material.PISTON : Material.STICKY_PISTON).createBlockData();
} piston.setFacing(head.getFacing());
} piston.setExtended(true);
return PerformResult.SUCCESS; secBlock.setBlockData(piston);
} }
} }
return PerformResult.SUCCESS;
@SuppressWarnings("serial") }
public static class WorldEditorException extends Exception implements LookupCacheElement { }
private final Location loc;
@SuppressWarnings("serial")
public WorldEditorException(Material typeBefore, Material typeAfter, Location loc) { public static class WorldEditorException extends Exception implements LookupCacheElement {
this("Failed to replace " + typeBefore.name() + " with " + typeAfter.name(), loc); private final Location loc;
}
public WorldEditorException(Material typeBefore, Material typeAfter, Location loc) {
public WorldEditorException(String msg, Location loc) { this("Failed to replace " + typeBefore.name() + " with " + typeAfter.name(), loc);
super(msg + " at " + loc.getWorld().getName() + ":" + loc.getBlockX() + ":" + loc.getBlockY() + ":" + loc.getBlockZ()); }
this.loc = loc;
} public WorldEditorException(String msg, Location loc) {
super(msg + " at " + loc.getWorld().getName() + ":" + loc.getBlockX() + ":" + loc.getBlockY() + ":" + loc.getBlockZ());
@Override this.loc = loc;
public Location getLocation() { }
return loc;
} @Override
} public Location getLocation() {
} return loc;
}
}
}