From 0c21d821d9c10ba9ac0e57170dfb90b02a1e3e3e Mon Sep 17 00:00:00 2001 From: Brokkonaut Date: Mon, 9 Oct 2023 08:52:12 +0200 Subject: [PATCH] Fix line endings --- src/main/java/de/diddiz/LogBlock/Actor.java | 364 +-- .../java/de/diddiz/LogBlock/AutoClearLog.java | 64 +- .../java/de/diddiz/LogBlock/BlockChange.java | 544 ++-- .../java/de/diddiz/LogBlock/ChatMessage.java | 118 +- .../java/de/diddiz/LogBlock/ChestAccess.java | 30 +- .../java/de/diddiz/LogBlock/Consumer.java | 2442 +++++++-------- .../de/diddiz/LogBlock/DumpedLogImporter.java | 294 +- .../java/de/diddiz/LogBlock/LogBlock.java | 756 ++--- .../diddiz/LogBlock/LookupCacheElement.java | 36 +- .../LogBlock/LookupCacheElementFactory.java | 80 +- src/main/java/de/diddiz/LogBlock/Session.java | 98 +- .../diddiz/LogBlock/SummedBlockChanges.java | 86 +- src/main/java/de/diddiz/LogBlock/Tool.java | 70 +- .../java/de/diddiz/LogBlock/ToolBehavior.java | 14 +- .../java/de/diddiz/LogBlock/ToolData.java | 30 +- .../java/de/diddiz/LogBlock/ToolMode.java | 36 +- src/main/java/de/diddiz/LogBlock/Updater.java | 2320 +++++++-------- .../diddiz/LogBlock/config/WorldConfig.java | 342 +-- .../LogBlock/listeners/BanListener.java | 96 +- .../LogBlock/listeners/BlockBreakLogging.java | 148 +- .../LogBlock/listeners/BlockBurnLogging.java | 144 +- .../LogBlock/listeners/BlockPlaceLogging.java | 134 +- .../LogBlock/listeners/ChatLogging.java | 116 +- .../listeners/ChestAccessLogging.java | 606 ++-- .../LogBlock/listeners/EndermenLogging.java | 48 +- .../LogBlock/listeners/ExplosionLogging.java | 458 +-- .../LogBlock/listeners/FluidFlowLogging.java | 254 +- .../LogBlock/listeners/InteractLogging.java | 552 ++-- .../LogBlock/listeners/KillLogging.java | 102 +- .../listeners/LeavesDecayLogging.java | 52 +- .../LogBlock/listeners/LoggingListener.java | 26 +- .../LogBlock/listeners/PlayerInfoLogging.java | 86 +- .../LogBlock/listeners/SignChangeLogging.java | 66 +- .../LogBlock/listeners/SnowFadeLogging.java | 56 +- .../LogBlock/listeners/SnowFormLogging.java | 56 +- .../listeners/StructureGrowLogging.java | 68 +- .../LogBlock/listeners/ToolListener.java | 302 +- .../LogBlock/listeners/WitherLogging.java | 48 +- .../de/diddiz/LogBlock/util/BukkitUtils.java | 2624 ++++++++--------- .../LogBlock/util/ComparableVersion.java | 848 +++--- .../LogBlock/util/MySQLConnectionPool.java | 102 +- .../de/diddiz/LogBlock/util/UUIDFetcher.java | 122 +- .../java/de/diddiz/LogBlock/util/Utils.java | 596 ++-- 43 files changed, 7717 insertions(+), 7717 deletions(-) diff --git a/src/main/java/de/diddiz/LogBlock/Actor.java b/src/main/java/de/diddiz/LogBlock/Actor.java index 6364ab9..b13a464 100644 --- a/src/main/java/de/diddiz/LogBlock/Actor.java +++ b/src/main/java/de/diddiz/LogBlock/Actor.java @@ -1,182 +1,182 @@ -package de.diddiz.LogBlock; - -import org.bukkit.entity.Entity; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.Player; -import org.bukkit.entity.Projectile; -import org.bukkit.projectiles.BlockProjectileSource; -import org.bukkit.projectiles.ProjectileSource; - -import static de.diddiz.LogBlock.util.BukkitUtils.entityName; - -import java.sql.ResultSet; -import java.sql.SQLException; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.block.Block; - -public class Actor { - - @Override - public int hashCode() { - return this.UUID != null ? this.UUID.hashCode() : 0; - } - - @Override - public boolean equals(Object obj) { - if (obj == null || getClass() != obj.getClass()) { - return false; - } - final Actor other = (Actor) obj; - return (this.UUID == null) ? (other.UUID == null) : this.UUID.equals(other.UUID); - } - - final String name; - final String UUID; - final Location blockLocation; - final Entity entity; - - public Actor(String name, String UUID) { - this.name = name; - this.UUID = UUID == null ? "unknown" : (UUID.length() > 36 ? UUID.substring(0, 36) : UUID); - this.blockLocation = null; - this.entity = null; - } - - public Actor(String name, String UUID, Block block) { - this.name = name; - this.UUID = UUID == null ? "unknown" : (UUID.length() > 36 ? UUID.substring(0, 36) : UUID); - this.blockLocation = block == null ? null : block.getLocation(); - this.entity = null; - } - - public Actor(String name, java.util.UUID UUID) { - this.name = name; - this.UUID = UUID.toString(); - this.blockLocation = null; - this.entity = null; - } - - public Actor(String name, java.util.UUID UUID, Block block) { - this.name = name; - this.UUID = UUID.toString(); - this.blockLocation = block == null ? null : block.getLocation(); - this.entity = null; - } - - public Actor(String name, java.util.UUID UUID, Entity entity) { - this.name = name; - this.UUID = UUID.toString(); - this.blockLocation = null; - this.entity = entity; - } - - public Actor(String name) { - this(name, generateUUID(name)); - } - - public Actor(String name, Block block) { - this(name, generateUUID(name), block); - } - - public Actor(String name, Entity entity) { - this.name = name; - this.UUID = generateUUID(name); - this.blockLocation = null; - this.entity = entity; - } - - public Actor(ResultSet rs) throws SQLException { - this(rs.getString("playername"), rs.getString("UUID")); - } - - public String getName() { - return name; - } - - public String getUUID() { - return UUID; - } - - public Location getBlockLocation() { - return blockLocation; - } - - /** - * The acting entity object (if known) - */ - public Entity getEntity() { - return entity; - } - - public static Actor actorFromEntity(Entity entity) { - if (entity instanceof Player) { - return new Actor(entityName(entity), entity.getUniqueId(), entity); - } - if (entity instanceof Projectile) { - ProjectileSource shooter = ((Projectile) entity).getShooter(); - if (shooter != null) { - return actorFromProjectileSource(shooter); - } - } - return new Actor(entityName(entity), entity); - } - - @Deprecated - public static Actor actorFromEntity(EntityType entity) { - return new Actor(entity.name()); - } - - public static Actor actorFromProjectileSource(ProjectileSource psource) { - if (psource instanceof Entity) { - return actorFromEntity((Entity) psource); - } - if (psource instanceof BlockProjectileSource) { - return new Actor(((BlockProjectileSource) psource).getBlock().getType().toString()); - } else { - return new Actor(psource.toString()); - } - - } - - /** - * Generate an Actor object from a String name, trying to guess if it's an online player - * and if so, setting the UUID accordingly. This only checks against currently online - * players and is a "best effort" attempt for use with the pre-UUID API - *

- * If you know something is an entity (player or otherwise) use the {@link #actorFromEntity(org.bukkit.entity.Entity) } - * or {@link #actorFromEntity(org.bukkit.entity.EntityType) } methods - *

- * If you know something is a server effect (like gravity) use {@link #Actor(java.lang.String)} - * - * @deprecated Only use this if you have a String of unknown origin - * - * @param actorName - * String of unknown origin - * @return - */ - @Deprecated - public static Actor actorFromString(String actorName) { - Player p = Bukkit.getServer().getPlayerExact(actorName); - if (p != null) { - return actorFromEntity(p); - } - // No player found online with that name, assuming non-player entity/effect - return new Actor(actorName); - } - - public static boolean isValidUUID(String uuid) { - try { - java.util.UUID.fromString(uuid); - return true; - } catch (IllegalArgumentException e) { - return false; - } - } - - public static String generateUUID(String name) { - return "log_" + name; - - } - -} +package de.diddiz.LogBlock; + +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.entity.Projectile; +import org.bukkit.projectiles.BlockProjectileSource; +import org.bukkit.projectiles.ProjectileSource; + +import static de.diddiz.LogBlock.util.BukkitUtils.entityName; + +import java.sql.ResultSet; +import java.sql.SQLException; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.block.Block; + +public class Actor { + + @Override + public int hashCode() { + return this.UUID != null ? this.UUID.hashCode() : 0; + } + + @Override + public boolean equals(Object obj) { + if (obj == null || getClass() != obj.getClass()) { + return false; + } + final Actor other = (Actor) obj; + return (this.UUID == null) ? (other.UUID == null) : this.UUID.equals(other.UUID); + } + + final String name; + final String UUID; + final Location blockLocation; + final Entity entity; + + public Actor(String name, String UUID) { + this.name = name; + this.UUID = UUID == null ? "unknown" : (UUID.length() > 36 ? UUID.substring(0, 36) : UUID); + this.blockLocation = null; + this.entity = null; + } + + public Actor(String name, String UUID, Block block) { + this.name = name; + this.UUID = UUID == null ? "unknown" : (UUID.length() > 36 ? UUID.substring(0, 36) : UUID); + this.blockLocation = block == null ? null : block.getLocation(); + this.entity = null; + } + + public Actor(String name, java.util.UUID UUID) { + this.name = name; + this.UUID = UUID.toString(); + this.blockLocation = null; + this.entity = null; + } + + public Actor(String name, java.util.UUID UUID, Block block) { + this.name = name; + this.UUID = UUID.toString(); + this.blockLocation = block == null ? null : block.getLocation(); + this.entity = null; + } + + public Actor(String name, java.util.UUID UUID, Entity entity) { + this.name = name; + this.UUID = UUID.toString(); + this.blockLocation = null; + this.entity = entity; + } + + public Actor(String name) { + this(name, generateUUID(name)); + } + + public Actor(String name, Block block) { + this(name, generateUUID(name), block); + } + + public Actor(String name, Entity entity) { + this.name = name; + this.UUID = generateUUID(name); + this.blockLocation = null; + this.entity = entity; + } + + public Actor(ResultSet rs) throws SQLException { + this(rs.getString("playername"), rs.getString("UUID")); + } + + public String getName() { + return name; + } + + public String getUUID() { + return UUID; + } + + public Location getBlockLocation() { + return blockLocation; + } + + /** + * The acting entity object (if known) + */ + public Entity getEntity() { + return entity; + } + + public static Actor actorFromEntity(Entity entity) { + if (entity instanceof Player) { + return new Actor(entityName(entity), entity.getUniqueId(), entity); + } + if (entity instanceof Projectile) { + ProjectileSource shooter = ((Projectile) entity).getShooter(); + if (shooter != null) { + return actorFromProjectileSource(shooter); + } + } + return new Actor(entityName(entity), entity); + } + + @Deprecated + public static Actor actorFromEntity(EntityType entity) { + return new Actor(entity.name()); + } + + public static Actor actorFromProjectileSource(ProjectileSource psource) { + if (psource instanceof Entity) { + return actorFromEntity((Entity) psource); + } + if (psource instanceof BlockProjectileSource) { + return new Actor(((BlockProjectileSource) psource).getBlock().getType().toString()); + } else { + return new Actor(psource.toString()); + } + + } + + /** + * Generate an Actor object from a String name, trying to guess if it's an online player + * and if so, setting the UUID accordingly. This only checks against currently online + * players and is a "best effort" attempt for use with the pre-UUID API + *

+ * If you know something is an entity (player or otherwise) use the {@link #actorFromEntity(org.bukkit.entity.Entity) } + * or {@link #actorFromEntity(org.bukkit.entity.EntityType) } methods + *

+ * If you know something is a server effect (like gravity) use {@link #Actor(java.lang.String)} + * + * @deprecated Only use this if you have a String of unknown origin + * + * @param actorName + * String of unknown origin + * @return + */ + @Deprecated + public static Actor actorFromString(String actorName) { + Player p = Bukkit.getServer().getPlayerExact(actorName); + if (p != null) { + return actorFromEntity(p); + } + // No player found online with that name, assuming non-player entity/effect + return new Actor(actorName); + } + + public static boolean isValidUUID(String uuid) { + try { + java.util.UUID.fromString(uuid); + return true; + } catch (IllegalArgumentException e) { + return false; + } + } + + public static String generateUUID(String name) { + return "log_" + name; + + } + +} diff --git a/src/main/java/de/diddiz/LogBlock/AutoClearLog.java b/src/main/java/de/diddiz/LogBlock/AutoClearLog.java index a57f357..c7c28cf 100644 --- a/src/main/java/de/diddiz/LogBlock/AutoClearLog.java +++ b/src/main/java/de/diddiz/LogBlock/AutoClearLog.java @@ -1,32 +1,32 @@ -package de.diddiz.LogBlock; - -import java.util.Arrays; -import java.util.logging.Level; - -import static de.diddiz.LogBlock.config.Config.autoClearLog; -import static org.bukkit.Bukkit.*; - -public class AutoClearLog implements Runnable { - private final LogBlock logblock; - - AutoClearLog(LogBlock logblock) { - this.logblock = logblock; - } - - @Override - public void run() { - final CommandsHandler handler = logblock.getCommandsHandler(); - for (final String paramStr : autoClearLog) { - if (!logblock.isCompletelyEnabled()) { - return; // do not try when plugin is disabled - } - try { - final QueryParams params = new QueryParams(logblock, getConsoleSender(), Arrays.asList(paramStr.split(" "))); - params.noForcedLimit = true; - handler.new CommandClearLog(getServer().getConsoleSender(), params, false); - } catch (final Exception ex) { - getLogger().log(Level.SEVERE, "Failed to schedule auto ClearLog: ", ex); - } - } - } -} +package de.diddiz.LogBlock; + +import java.util.Arrays; +import java.util.logging.Level; + +import static de.diddiz.LogBlock.config.Config.autoClearLog; +import static org.bukkit.Bukkit.*; + +public class AutoClearLog implements Runnable { + private final LogBlock logblock; + + AutoClearLog(LogBlock logblock) { + this.logblock = logblock; + } + + @Override + public void run() { + final CommandsHandler handler = logblock.getCommandsHandler(); + for (final String paramStr : autoClearLog) { + if (!logblock.isCompletelyEnabled()) { + return; // do not try when plugin is disabled + } + try { + final QueryParams params = new QueryParams(logblock, getConsoleSender(), Arrays.asList(paramStr.split(" "))); + params.noForcedLimit = true; + handler.new CommandClearLog(getServer().getConsoleSender(), params, false); + } catch (final Exception ex) { + getLogger().log(Level.SEVERE, "Failed to schedule auto ClearLog: ", ex); + } + } + } +} diff --git a/src/main/java/de/diddiz/LogBlock/BlockChange.java b/src/main/java/de/diddiz/LogBlock/BlockChange.java index 83ae93e..79dcf54 100644 --- a/src/main/java/de/diddiz/LogBlock/BlockChange.java +++ b/src/main/java/de/diddiz/LogBlock/BlockChange.java @@ -1,272 +1,272 @@ -package de.diddiz.LogBlock; - -import static de.diddiz.LogBlock.util.ActionColor.CREATE; -import static de.diddiz.LogBlock.util.ActionColor.DESTROY; -import static de.diddiz.LogBlock.util.ActionColor.INTERACT; -import static de.diddiz.LogBlock.util.MessagingUtil.createTextComponentWithColor; -import static de.diddiz.LogBlock.util.MessagingUtil.prettyDate; -import static de.diddiz.LogBlock.util.MessagingUtil.prettyLocation; -import static de.diddiz.LogBlock.util.MessagingUtil.prettyMaterial; -import static de.diddiz.LogBlock.util.MessagingUtil.prettyState; -import static de.diddiz.LogBlock.util.TypeColor.DEFAULT; - -import de.diddiz.LogBlock.blockstate.BlockStateCodecs; -import de.diddiz.LogBlock.util.BukkitUtils; -import de.diddiz.LogBlock.util.Utils; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.logging.Level; -import net.md_5.bungee.api.chat.BaseComponent; -import net.md_5.bungee.api.chat.TextComponent; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.Note; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.Lightable; -import org.bukkit.block.data.Openable; -import org.bukkit.block.data.Powerable; -import org.bukkit.block.data.Waterlogged; -import org.bukkit.block.data.type.Candle; -import org.bukkit.block.data.type.Comparator; -import org.bukkit.block.data.type.DaylightDetector; -import org.bukkit.block.data.type.Lectern; -import org.bukkit.block.data.type.NoteBlock; -import org.bukkit.block.data.type.Repeater; -import org.bukkit.block.data.type.Sign; -import org.bukkit.block.data.type.Switch; -import org.bukkit.block.data.type.WallSign; -import org.bukkit.inventory.ItemStack; - -public class BlockChange implements LookupCacheElement { - public final long id, date; - public final Location loc; - public final Actor actor; - public final String playerName; - public final int replacedMaterial, replacedData, typeMaterial, typeData; - public final byte[] replacedState, typeState; - public final ChestAccess ca; - - public BlockChange(long date, Location loc, Actor actor, int replaced, int replacedData, byte[] replacedState, int type, int typeData, byte[] typeState, ChestAccess ca) { - id = 0; - this.date = date; - this.loc = loc; - this.actor = actor; - this.replacedMaterial = replaced; - this.replacedData = replacedData; - this.replacedState = replacedState; - this.typeMaterial = type; - this.typeData = typeData; - this.typeState = typeState; - this.ca = ca; - this.playerName = actor == null ? null : actor.getName(); - } - - public BlockChange(ResultSet rs, QueryParams p) throws SQLException { - id = p.needId ? rs.getLong("id") : 0; - date = p.needDate ? rs.getTimestamp("date").getTime() : 0; - loc = p.needCoords ? new Location(p.world, rs.getInt("x"), rs.getInt("y"), rs.getInt("z")) : null; - actor = p.needPlayer ? new Actor(rs) : null; - playerName = p.needPlayer ? rs.getString("playername") : null; - replacedMaterial = p.needType ? rs.getInt("replaced") : 0; - replacedData = p.needType ? rs.getInt("replacedData") : -1; - typeMaterial = p.needType ? rs.getInt("type") : 0; - typeData = p.needType ? rs.getInt("typeData") : -1; - replacedState = p.needType ? rs.getBytes("replacedState") : null; - typeState = p.needType ? rs.getBytes("typeState") : null; - ChestAccess catemp = null; - if (p.needChestAccess) { - ItemStack stack = Utils.loadItemStack(rs.getBytes("item")); - if (stack != null) { - catemp = new ChestAccess(stack, rs.getBoolean("itemremove"), rs.getInt("itemtype")); - } - } - ca = catemp; - } - - private BaseComponent getTypeDetails(BlockData type, byte[] typeState) { - return getTypeDetails(type, typeState, null, null); - } - - private BaseComponent getTypeDetails(BlockData type, byte[] typeState, BlockData oldType, byte[] oldTypeState) { - BaseComponent typeDetails = null; - - if (BlockStateCodecs.hasCodec(type.getMaterial())) { - try { - typeDetails = BlockStateCodecs.getChangesAsComponent(type.getMaterial(), Utils.deserializeYamlConfiguration(typeState), type.equals(oldType) ? Utils.deserializeYamlConfiguration(oldTypeState) : null); - } catch (Exception e) { - LogBlock.getInstance().getLogger().log(Level.SEVERE, "Could not parse BlockState for " + type.getMaterial(), e); - } - } - - if (typeDetails == null) { - return new TextComponent(""); - } else { - TextComponent component = new TextComponent(" "); - component.addExtra(typeDetails); - return component; - } - } - - @Override - public String toString() { - return BaseComponent.toPlainText(getLogMessage(-1)); - } - - @Override - public BaseComponent[] getLogMessage(int entry) { - TextComponent msg = new TextComponent(); - if (date > 0) { - msg.addExtra(prettyDate(date)); - msg.addExtra(" "); - } - if (actor != null) { - msg.addExtra(actor.getName()); - msg.addExtra(" "); - } - BlockData type = getBlockSet(); - BlockData replaced = getBlockReplaced(); - if (type == null || replaced == null) { - msg.addExtra("did an unknown block modification"); - return new BaseComponent[] { msg }; - } - - // Process type details once for later use. - BaseComponent typeDetails = getTypeDetails(type, typeState, replaced, replacedState); - BaseComponent replacedDetails = getTypeDetails(replaced, replacedState); - - if (type.getMaterial().equals(replaced.getMaterial()) || (type.getMaterial() == Material.CAKE && BukkitUtils.isCandleCake(replaced.getMaterial()))) { - if (BukkitUtils.isEmpty(type.getMaterial())) { - msg.addExtra(createTextComponentWithColor("did an unspecified action", INTERACT.getColor())); - } else if (ca != null) { - if (ca.itemStack == null) { - msg.addExtra(createTextComponentWithColor("looked inside ", INTERACT.getColor())); - msg.addExtra(prettyMaterial(type)); - } else if (ca.remove) { - msg.addExtra(createTextComponentWithColor("took ", DESTROY.getColor())); - msg.addExtra(BukkitUtils.toString(ca.itemStack)); - msg.addExtra(createTextComponentWithColor(" from ", DESTROY.getColor())); - msg.addExtra(prettyMaterial(type)); - } else { - msg.addExtra(createTextComponentWithColor("put ", CREATE.getColor())); - msg.addExtra(BukkitUtils.toString(ca.itemStack)); - msg.addExtra(createTextComponentWithColor(" into ", CREATE.getColor())); - msg.addExtra(prettyMaterial(type)); - } - } else if (type instanceof Waterlogged && ((Waterlogged) type).isWaterlogged() != ((Waterlogged) replaced).isWaterlogged()) { - if (((Waterlogged) type).isWaterlogged()) { - msg.addExtra(createTextComponentWithColor("waterlogged ", CREATE.getColor())); - msg.addExtra(prettyMaterial(type)); - } else { - msg.addExtra(createTextComponentWithColor("dried ", DESTROY.getColor())); - msg.addExtra(prettyMaterial(type)); - } - } else if (BukkitUtils.getContainerBlocks().contains(type.getMaterial())) { - msg.addExtra(createTextComponentWithColor("opened ", INTERACT.getColor())); - msg.addExtra(prettyMaterial(type)); - } else if (type instanceof Openable && ((Openable) type).isOpen() != ((Openable) replaced).isOpen()) { - // Door, Trapdoor, Fence gate - msg.addExtra(createTextComponentWithColor(((Openable) type).isOpen() ? "opened " : "closed ", INTERACT.getColor())); - msg.addExtra(prettyMaterial(type)); - } else if (type.getMaterial() == Material.LEVER && ((Switch) type).isPowered() != ((Switch) replaced).isPowered()) { - msg.addExtra(createTextComponentWithColor("switched ", INTERACT.getColor())); - msg.addExtra(prettyMaterial(type)); - msg.addExtra(prettyState(((Switch) type).isPowered() ? " on" : " off")); - } else if (type instanceof Switch && ((Switch) type).isPowered() != ((Switch) replaced).isPowered()) { - msg.addExtra(createTextComponentWithColor("pressed ", INTERACT.getColor())); - msg.addExtra(prettyMaterial(type)); - } else if (type.getMaterial() == Material.CAKE) { - msg.addExtra(createTextComponentWithColor("ate a piece of ", DESTROY.getColor())); - msg.addExtra(prettyMaterial(type)); - } else if (type.getMaterial() == Material.NOTE_BLOCK) { - Note note = ((NoteBlock) type).getNote(); - msg.addExtra(createTextComponentWithColor("set ", INTERACT.getColor())); - msg.addExtra(prettyMaterial(type)); - msg.addExtra(" to "); - msg.addExtra(prettyState(note.getTone().name() + (note.isSharped() ? "#" : ""))); - } else if (type.getMaterial() == Material.REPEATER) { - msg.addExtra(createTextComponentWithColor("set ", INTERACT.getColor())); - msg.addExtra(prettyMaterial(type)); - msg.addExtra(" to "); - msg.addExtra(prettyState(((Repeater) type).getDelay())); - msg.addExtra(createTextComponentWithColor(" ticks delay", DEFAULT.getColor())); - } else if (type.getMaterial() == Material.COMPARATOR) { - msg.addExtra(createTextComponentWithColor("set ", INTERACT.getColor())); - msg.addExtra(prettyMaterial(type)); - msg.addExtra(" to "); - msg.addExtra(prettyState(((Comparator) type).getMode())); - } else if (type.getMaterial() == Material.DAYLIGHT_DETECTOR) { - msg.addExtra(createTextComponentWithColor("set ", INTERACT.getColor())); - msg.addExtra(prettyMaterial(type)); - msg.addExtra(" to "); - msg.addExtra(prettyState(((DaylightDetector) type).isInverted() ? "inverted" : "normal")); - } else if (type instanceof Lectern) { - msg.addExtra(createTextComponentWithColor("changed the book on a ", INTERACT.getColor())); - msg.addExtra(prettyMaterial(type)); - msg.addExtra(" to"); - msg.addExtra(prettyState(typeDetails)); - } else if (type instanceof Powerable) { - msg.addExtra(createTextComponentWithColor("stepped on ", INTERACT.getColor())); - msg.addExtra(prettyMaterial(type)); - } else if (type.getMaterial() == Material.TRIPWIRE) { - msg.addExtra(createTextComponentWithColor("ran into ", INTERACT.getColor())); - msg.addExtra(prettyMaterial(type)); - } else if (type instanceof Sign || type instanceof WallSign) { - msg.addExtra(createTextComponentWithColor("edited a ", CREATE.getColor())); - msg.addExtra(prettyMaterial(type)); - msg.addExtra(createTextComponentWithColor(" to", CREATE.getColor())); - msg.addExtra(prettyState(typeDetails)); - } else if (type instanceof Candle && ((Candle) type).getCandles() != ((Candle) replaced).getCandles()) { - msg.addExtra(createTextComponentWithColor("added a candle to ", CREATE.getColor())); - msg.addExtra(prettyMaterial(type)); - } else if ((type instanceof Candle || BukkitUtils.isCandleCake(type.getMaterial())) && ((Lightable) type).isLit() != ((Lightable) replaced).isLit()) { - if (((Lightable) type).isLit()) { - msg.addExtra(createTextComponentWithColor("lit a ", CREATE.getColor())); - msg.addExtra(prettyMaterial(type)); - } else { - msg.addExtra(createTextComponentWithColor("extinguished a ", CREATE.getColor())); - msg.addExtra(prettyMaterial(type)); - } - } else { - msg.addExtra(createTextComponentWithColor("replaced ", CREATE.getColor())); - msg.addExtra(prettyMaterial(replaced)); - msg.addExtra(prettyState(replacedDetails)); - msg.addExtra(createTextComponentWithColor(" with ", CREATE.getColor())); - msg.addExtra(prettyMaterial(type)); - msg.addExtra(prettyState(typeDetails)); - } - } else if (BukkitUtils.isEmpty(type.getMaterial())) { - msg.addExtra(createTextComponentWithColor("destroyed ", DESTROY.getColor())); - msg.addExtra(prettyMaterial(replaced)); - msg.addExtra(prettyState(replacedDetails)); - } else if (BukkitUtils.isEmpty(replaced.getMaterial())) { - msg.addExtra(createTextComponentWithColor("created ", CREATE.getColor())); - msg.addExtra(prettyMaterial(type)); - msg.addExtra(prettyState(typeDetails)); - } else { - msg.addExtra(createTextComponentWithColor("replaced ", CREATE.getColor())); - msg.addExtra(prettyMaterial(replaced)); - msg.addExtra(prettyState(replacedDetails)); - msg.addExtra(createTextComponentWithColor(" with ", CREATE.getColor())); - msg.addExtra(prettyMaterial(type)); - msg.addExtra(prettyState(typeDetails)); - } - if (loc != null) { - msg.addExtra(" at "); - msg.addExtra(prettyLocation(loc, entry)); - } - return new BaseComponent[] { msg }; - } - - public BlockData getBlockReplaced() { - return MaterialConverter.getBlockData(replacedMaterial, replacedData); - } - - public BlockData getBlockSet() { - return MaterialConverter.getBlockData(typeMaterial, typeData); - } - - @Override - public Location getLocation() { - return loc; - } -} +package de.diddiz.LogBlock; + +import static de.diddiz.LogBlock.util.ActionColor.CREATE; +import static de.diddiz.LogBlock.util.ActionColor.DESTROY; +import static de.diddiz.LogBlock.util.ActionColor.INTERACT; +import static de.diddiz.LogBlock.util.MessagingUtil.createTextComponentWithColor; +import static de.diddiz.LogBlock.util.MessagingUtil.prettyDate; +import static de.diddiz.LogBlock.util.MessagingUtil.prettyLocation; +import static de.diddiz.LogBlock.util.MessagingUtil.prettyMaterial; +import static de.diddiz.LogBlock.util.MessagingUtil.prettyState; +import static de.diddiz.LogBlock.util.TypeColor.DEFAULT; + +import de.diddiz.LogBlock.blockstate.BlockStateCodecs; +import de.diddiz.LogBlock.util.BukkitUtils; +import de.diddiz.LogBlock.util.Utils; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.logging.Level; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.TextComponent; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Note; +import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.Lightable; +import org.bukkit.block.data.Openable; +import org.bukkit.block.data.Powerable; +import org.bukkit.block.data.Waterlogged; +import org.bukkit.block.data.type.Candle; +import org.bukkit.block.data.type.Comparator; +import org.bukkit.block.data.type.DaylightDetector; +import org.bukkit.block.data.type.Lectern; +import org.bukkit.block.data.type.NoteBlock; +import org.bukkit.block.data.type.Repeater; +import org.bukkit.block.data.type.Sign; +import org.bukkit.block.data.type.Switch; +import org.bukkit.block.data.type.WallSign; +import org.bukkit.inventory.ItemStack; + +public class BlockChange implements LookupCacheElement { + public final long id, date; + public final Location loc; + public final Actor actor; + public final String playerName; + public final int replacedMaterial, replacedData, typeMaterial, typeData; + public final byte[] replacedState, typeState; + public final ChestAccess ca; + + public BlockChange(long date, Location loc, Actor actor, int replaced, int replacedData, byte[] replacedState, int type, int typeData, byte[] typeState, ChestAccess ca) { + id = 0; + this.date = date; + this.loc = loc; + this.actor = actor; + this.replacedMaterial = replaced; + this.replacedData = replacedData; + this.replacedState = replacedState; + this.typeMaterial = type; + this.typeData = typeData; + this.typeState = typeState; + this.ca = ca; + this.playerName = actor == null ? null : actor.getName(); + } + + public BlockChange(ResultSet rs, QueryParams p) throws SQLException { + id = p.needId ? rs.getLong("id") : 0; + date = p.needDate ? rs.getTimestamp("date").getTime() : 0; + loc = p.needCoords ? new Location(p.world, rs.getInt("x"), rs.getInt("y"), rs.getInt("z")) : null; + actor = p.needPlayer ? new Actor(rs) : null; + playerName = p.needPlayer ? rs.getString("playername") : null; + replacedMaterial = p.needType ? rs.getInt("replaced") : 0; + replacedData = p.needType ? rs.getInt("replacedData") : -1; + typeMaterial = p.needType ? rs.getInt("type") : 0; + typeData = p.needType ? rs.getInt("typeData") : -1; + replacedState = p.needType ? rs.getBytes("replacedState") : null; + typeState = p.needType ? rs.getBytes("typeState") : null; + ChestAccess catemp = null; + if (p.needChestAccess) { + ItemStack stack = Utils.loadItemStack(rs.getBytes("item")); + if (stack != null) { + catemp = new ChestAccess(stack, rs.getBoolean("itemremove"), rs.getInt("itemtype")); + } + } + ca = catemp; + } + + private BaseComponent getTypeDetails(BlockData type, byte[] typeState) { + return getTypeDetails(type, typeState, null, null); + } + + private BaseComponent getTypeDetails(BlockData type, byte[] typeState, BlockData oldType, byte[] oldTypeState) { + BaseComponent typeDetails = null; + + if (BlockStateCodecs.hasCodec(type.getMaterial())) { + try { + typeDetails = BlockStateCodecs.getChangesAsComponent(type.getMaterial(), Utils.deserializeYamlConfiguration(typeState), type.equals(oldType) ? Utils.deserializeYamlConfiguration(oldTypeState) : null); + } catch (Exception e) { + LogBlock.getInstance().getLogger().log(Level.SEVERE, "Could not parse BlockState for " + type.getMaterial(), e); + } + } + + if (typeDetails == null) { + return new TextComponent(""); + } else { + TextComponent component = new TextComponent(" "); + component.addExtra(typeDetails); + return component; + } + } + + @Override + public String toString() { + return BaseComponent.toPlainText(getLogMessage(-1)); + } + + @Override + public BaseComponent[] getLogMessage(int entry) { + TextComponent msg = new TextComponent(); + if (date > 0) { + msg.addExtra(prettyDate(date)); + msg.addExtra(" "); + } + if (actor != null) { + msg.addExtra(actor.getName()); + msg.addExtra(" "); + } + BlockData type = getBlockSet(); + BlockData replaced = getBlockReplaced(); + if (type == null || replaced == null) { + msg.addExtra("did an unknown block modification"); + return new BaseComponent[] { msg }; + } + + // Process type details once for later use. + BaseComponent typeDetails = getTypeDetails(type, typeState, replaced, replacedState); + BaseComponent replacedDetails = getTypeDetails(replaced, replacedState); + + if (type.getMaterial().equals(replaced.getMaterial()) || (type.getMaterial() == Material.CAKE && BukkitUtils.isCandleCake(replaced.getMaterial()))) { + if (BukkitUtils.isEmpty(type.getMaterial())) { + msg.addExtra(createTextComponentWithColor("did an unspecified action", INTERACT.getColor())); + } else if (ca != null) { + if (ca.itemStack == null) { + msg.addExtra(createTextComponentWithColor("looked inside ", INTERACT.getColor())); + msg.addExtra(prettyMaterial(type)); + } else if (ca.remove) { + msg.addExtra(createTextComponentWithColor("took ", DESTROY.getColor())); + msg.addExtra(BukkitUtils.toString(ca.itemStack)); + msg.addExtra(createTextComponentWithColor(" from ", DESTROY.getColor())); + msg.addExtra(prettyMaterial(type)); + } else { + msg.addExtra(createTextComponentWithColor("put ", CREATE.getColor())); + msg.addExtra(BukkitUtils.toString(ca.itemStack)); + msg.addExtra(createTextComponentWithColor(" into ", CREATE.getColor())); + msg.addExtra(prettyMaterial(type)); + } + } else if (type instanceof Waterlogged && ((Waterlogged) type).isWaterlogged() != ((Waterlogged) replaced).isWaterlogged()) { + if (((Waterlogged) type).isWaterlogged()) { + msg.addExtra(createTextComponentWithColor("waterlogged ", CREATE.getColor())); + msg.addExtra(prettyMaterial(type)); + } else { + msg.addExtra(createTextComponentWithColor("dried ", DESTROY.getColor())); + msg.addExtra(prettyMaterial(type)); + } + } else if (BukkitUtils.getContainerBlocks().contains(type.getMaterial())) { + msg.addExtra(createTextComponentWithColor("opened ", INTERACT.getColor())); + msg.addExtra(prettyMaterial(type)); + } else if (type instanceof Openable && ((Openable) type).isOpen() != ((Openable) replaced).isOpen()) { + // Door, Trapdoor, Fence gate + msg.addExtra(createTextComponentWithColor(((Openable) type).isOpen() ? "opened " : "closed ", INTERACT.getColor())); + msg.addExtra(prettyMaterial(type)); + } else if (type.getMaterial() == Material.LEVER && ((Switch) type).isPowered() != ((Switch) replaced).isPowered()) { + msg.addExtra(createTextComponentWithColor("switched ", INTERACT.getColor())); + msg.addExtra(prettyMaterial(type)); + msg.addExtra(prettyState(((Switch) type).isPowered() ? " on" : " off")); + } else if (type instanceof Switch && ((Switch) type).isPowered() != ((Switch) replaced).isPowered()) { + msg.addExtra(createTextComponentWithColor("pressed ", INTERACT.getColor())); + msg.addExtra(prettyMaterial(type)); + } else if (type.getMaterial() == Material.CAKE) { + msg.addExtra(createTextComponentWithColor("ate a piece of ", DESTROY.getColor())); + msg.addExtra(prettyMaterial(type)); + } else if (type.getMaterial() == Material.NOTE_BLOCK) { + Note note = ((NoteBlock) type).getNote(); + msg.addExtra(createTextComponentWithColor("set ", INTERACT.getColor())); + msg.addExtra(prettyMaterial(type)); + msg.addExtra(" to "); + msg.addExtra(prettyState(note.getTone().name() + (note.isSharped() ? "#" : ""))); + } else if (type.getMaterial() == Material.REPEATER) { + msg.addExtra(createTextComponentWithColor("set ", INTERACT.getColor())); + msg.addExtra(prettyMaterial(type)); + msg.addExtra(" to "); + msg.addExtra(prettyState(((Repeater) type).getDelay())); + msg.addExtra(createTextComponentWithColor(" ticks delay", DEFAULT.getColor())); + } else if (type.getMaterial() == Material.COMPARATOR) { + msg.addExtra(createTextComponentWithColor("set ", INTERACT.getColor())); + msg.addExtra(prettyMaterial(type)); + msg.addExtra(" to "); + msg.addExtra(prettyState(((Comparator) type).getMode())); + } else if (type.getMaterial() == Material.DAYLIGHT_DETECTOR) { + msg.addExtra(createTextComponentWithColor("set ", INTERACT.getColor())); + msg.addExtra(prettyMaterial(type)); + msg.addExtra(" to "); + msg.addExtra(prettyState(((DaylightDetector) type).isInverted() ? "inverted" : "normal")); + } else if (type instanceof Lectern) { + msg.addExtra(createTextComponentWithColor("changed the book on a ", INTERACT.getColor())); + msg.addExtra(prettyMaterial(type)); + msg.addExtra(" to"); + msg.addExtra(prettyState(typeDetails)); + } else if (type instanceof Powerable) { + msg.addExtra(createTextComponentWithColor("stepped on ", INTERACT.getColor())); + msg.addExtra(prettyMaterial(type)); + } else if (type.getMaterial() == Material.TRIPWIRE) { + msg.addExtra(createTextComponentWithColor("ran into ", INTERACT.getColor())); + msg.addExtra(prettyMaterial(type)); + } else if (type instanceof Sign || type instanceof WallSign) { + msg.addExtra(createTextComponentWithColor("edited a ", CREATE.getColor())); + msg.addExtra(prettyMaterial(type)); + msg.addExtra(createTextComponentWithColor(" to", CREATE.getColor())); + msg.addExtra(prettyState(typeDetails)); + } else if (type instanceof Candle && ((Candle) type).getCandles() != ((Candle) replaced).getCandles()) { + msg.addExtra(createTextComponentWithColor("added a candle to ", CREATE.getColor())); + msg.addExtra(prettyMaterial(type)); + } else if ((type instanceof Candle || BukkitUtils.isCandleCake(type.getMaterial())) && ((Lightable) type).isLit() != ((Lightable) replaced).isLit()) { + if (((Lightable) type).isLit()) { + msg.addExtra(createTextComponentWithColor("lit a ", CREATE.getColor())); + msg.addExtra(prettyMaterial(type)); + } else { + msg.addExtra(createTextComponentWithColor("extinguished a ", CREATE.getColor())); + msg.addExtra(prettyMaterial(type)); + } + } else { + msg.addExtra(createTextComponentWithColor("replaced ", CREATE.getColor())); + msg.addExtra(prettyMaterial(replaced)); + msg.addExtra(prettyState(replacedDetails)); + msg.addExtra(createTextComponentWithColor(" with ", CREATE.getColor())); + msg.addExtra(prettyMaterial(type)); + msg.addExtra(prettyState(typeDetails)); + } + } else if (BukkitUtils.isEmpty(type.getMaterial())) { + msg.addExtra(createTextComponentWithColor("destroyed ", DESTROY.getColor())); + msg.addExtra(prettyMaterial(replaced)); + msg.addExtra(prettyState(replacedDetails)); + } else if (BukkitUtils.isEmpty(replaced.getMaterial())) { + msg.addExtra(createTextComponentWithColor("created ", CREATE.getColor())); + msg.addExtra(prettyMaterial(type)); + msg.addExtra(prettyState(typeDetails)); + } else { + msg.addExtra(createTextComponentWithColor("replaced ", CREATE.getColor())); + msg.addExtra(prettyMaterial(replaced)); + msg.addExtra(prettyState(replacedDetails)); + msg.addExtra(createTextComponentWithColor(" with ", CREATE.getColor())); + msg.addExtra(prettyMaterial(type)); + msg.addExtra(prettyState(typeDetails)); + } + if (loc != null) { + msg.addExtra(" at "); + msg.addExtra(prettyLocation(loc, entry)); + } + return new BaseComponent[] { msg }; + } + + public BlockData getBlockReplaced() { + return MaterialConverter.getBlockData(replacedMaterial, replacedData); + } + + public BlockData getBlockSet() { + return MaterialConverter.getBlockData(typeMaterial, typeData); + } + + @Override + public Location getLocation() { + return loc; + } +} diff --git a/src/main/java/de/diddiz/LogBlock/ChatMessage.java b/src/main/java/de/diddiz/LogBlock/ChatMessage.java index 7f94027..43d9358 100644 --- a/src/main/java/de/diddiz/LogBlock/ChatMessage.java +++ b/src/main/java/de/diddiz/LogBlock/ChatMessage.java @@ -1,59 +1,59 @@ -package de.diddiz.LogBlock; - -import static de.diddiz.LogBlock.util.LoggingUtil.checkText; -import static de.diddiz.LogBlock.util.MessagingUtil.brackets; -import static de.diddiz.LogBlock.util.MessagingUtil.prettyDate; - -import de.diddiz.LogBlock.util.MessagingUtil; -import de.diddiz.LogBlock.util.MessagingUtil.BracketType; -import java.sql.ResultSet; -import java.sql.SQLException; -import net.md_5.bungee.api.chat.BaseComponent; -import net.md_5.bungee.api.chat.TextComponent; -import org.bukkit.Location; - -public class ChatMessage implements LookupCacheElement { - final long id, date; - final String playerName, message; - final Actor player; - - public ChatMessage(Actor player, String message) { - id = 0; - date = System.currentTimeMillis() / 1000; - this.player = player; - this.message = checkText(message); - this.playerName = player == null ? null : player.getName(); - } - - public ChatMessage(ResultSet rs, QueryParams p) throws SQLException { - id = p.needId ? rs.getLong("id") : 0; - date = p.needDate ? rs.getTimestamp("date").getTime() : 0; - player = p.needPlayer ? new Actor(rs) : null; - playerName = p.needPlayer ? rs.getString("playername") : null; - message = p.needMessage ? rs.getString("message") : null; - } - - @Override - public Location getLocation() { - return null; - } - - @Override - public BaseComponent[] getLogMessage(int entry) { - TextComponent msg = new TextComponent(); - if (date > 0) { - msg.addExtra(prettyDate(date)); - msg.addExtra(" "); - } - if (playerName != null) { - msg.addExtra(brackets(BracketType.ANGLE, MessagingUtil.createTextComponentWithColor(playerName, net.md_5.bungee.api.ChatColor.WHITE))); - msg.addExtra(" "); - } - if (message != null) { - for (BaseComponent messageComponent : TextComponent.fromLegacyText(message)) { - msg.addExtra(messageComponent); - } - } - return new BaseComponent[] { msg }; - } -} +package de.diddiz.LogBlock; + +import static de.diddiz.LogBlock.util.LoggingUtil.checkText; +import static de.diddiz.LogBlock.util.MessagingUtil.brackets; +import static de.diddiz.LogBlock.util.MessagingUtil.prettyDate; + +import de.diddiz.LogBlock.util.MessagingUtil; +import de.diddiz.LogBlock.util.MessagingUtil.BracketType; +import java.sql.ResultSet; +import java.sql.SQLException; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.TextComponent; +import org.bukkit.Location; + +public class ChatMessage implements LookupCacheElement { + final long id, date; + final String playerName, message; + final Actor player; + + public ChatMessage(Actor player, String message) { + id = 0; + date = System.currentTimeMillis() / 1000; + this.player = player; + this.message = checkText(message); + this.playerName = player == null ? null : player.getName(); + } + + public ChatMessage(ResultSet rs, QueryParams p) throws SQLException { + id = p.needId ? rs.getLong("id") : 0; + date = p.needDate ? rs.getTimestamp("date").getTime() : 0; + player = p.needPlayer ? new Actor(rs) : null; + playerName = p.needPlayer ? rs.getString("playername") : null; + message = p.needMessage ? rs.getString("message") : null; + } + + @Override + public Location getLocation() { + return null; + } + + @Override + public BaseComponent[] getLogMessage(int entry) { + TextComponent msg = new TextComponent(); + if (date > 0) { + msg.addExtra(prettyDate(date)); + msg.addExtra(" "); + } + if (playerName != null) { + msg.addExtra(brackets(BracketType.ANGLE, MessagingUtil.createTextComponentWithColor(playerName, net.md_5.bungee.api.ChatColor.WHITE))); + msg.addExtra(" "); + } + if (message != null) { + for (BaseComponent messageComponent : TextComponent.fromLegacyText(message)) { + msg.addExtra(messageComponent); + } + } + return new BaseComponent[] { msg }; + } +} diff --git a/src/main/java/de/diddiz/LogBlock/ChestAccess.java b/src/main/java/de/diddiz/LogBlock/ChestAccess.java index c9d7882..7157516 100644 --- a/src/main/java/de/diddiz/LogBlock/ChestAccess.java +++ b/src/main/java/de/diddiz/LogBlock/ChestAccess.java @@ -1,15 +1,15 @@ -package de.diddiz.LogBlock; - -import org.bukkit.inventory.ItemStack; - -public class ChestAccess { - public final ItemStack itemStack; - public final boolean remove; - public final int itemType; - - public ChestAccess(ItemStack itemStack, boolean remove, int itemType) { - this.itemStack = itemStack; - this.remove = remove; - this.itemType = itemType; - } -} +package de.diddiz.LogBlock; + +import org.bukkit.inventory.ItemStack; + +public class ChestAccess { + public final ItemStack itemStack; + public final boolean remove; + public final int itemType; + + public ChestAccess(ItemStack itemStack, boolean remove, int itemType) { + this.itemStack = itemStack; + this.remove = remove; + this.itemType = itemType; + } +} diff --git a/src/main/java/de/diddiz/LogBlock/Consumer.java b/src/main/java/de/diddiz/LogBlock/Consumer.java index 827b1c4..c07db64 100644 --- a/src/main/java/de/diddiz/LogBlock/Consumer.java +++ b/src/main/java/de/diddiz/LogBlock/Consumer.java @@ -1,1221 +1,1221 @@ -package de.diddiz.LogBlock; - -import static de.diddiz.LogBlock.config.Config.getWorldConfig; -import static de.diddiz.LogBlock.config.Config.hiddenBlocks; -import static de.diddiz.LogBlock.config.Config.hiddenPlayers; -import static de.diddiz.LogBlock.config.Config.isLogged; -import static de.diddiz.LogBlock.config.Config.logPlayerInfo; -import static de.diddiz.LogBlock.util.BukkitUtils.compressInventory; -import static de.diddiz.LogBlock.util.BukkitUtils.itemIDfromProjectileEntity; -import static de.diddiz.LogBlock.util.Utils.mysqlTextEscape; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.PrintWriter; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Deque; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.UUID; -import java.util.logging.Level; - -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.block.BlockState; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.Waterlogged; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Entity; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.Player; -import org.bukkit.entity.Projectile; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.InventoryHolder; -import org.bukkit.inventory.ItemStack; -import org.bukkit.projectiles.ProjectileSource; - -import de.diddiz.LogBlock.EntityChange.EntityChangeType; -import de.diddiz.LogBlock.blockstate.BlockStateCodecs; -import de.diddiz.LogBlock.config.Config; -import de.diddiz.LogBlock.events.BlockChangePreLogEvent; -import de.diddiz.LogBlock.events.EntityChangePreLogEvent; -import de.diddiz.LogBlock.util.BukkitUtils; -import de.diddiz.LogBlock.util.Utils; - -public class Consumer extends Thread { - private static final int MAX_SHUTDOWN_TIME_MILLIS = 20000; - private static final int WAIT_FOR_CONNECTION_TIME_MILLIS = 10000; - private static final int RETURN_IDLE_CONNECTION_TIME_MILLIS = 120000; - private static final int RETRIES_ON_UNKNOWN_CONNECTION_ERROR = 2; - - private final Deque queue = new ArrayDeque<>(); - private final LogBlock logblock; - private final Map playerIds = new HashMap<>(); - private final Map uncommitedPlayerIds = new HashMap<>(); - private final Map> uncommitedEntityIds = new HashMap<>(); - - private long addEntryCounter; - private long nextWarnCounter; - - private boolean shutdown; - private long shutdownInitialized; - - Consumer(LogBlock logblock) { - this.logblock = logblock; - PlayerLeaveRow.class.getName(); // preload this class - setName("Logblock-Consumer"); - } - - public LogBlock getLogblock() { - return logblock; - } - - /** - * Logs any block change. Don't try to combine broken and placed blocks. Queue two block changes or use the queueBLockReplace methods. - * - * @param actor - * Actor responsible for making the change - * @param loc - * Location of the block change - * @param typeBefore - * BlockData of the block before the change - * @param typeAfter - * BlockData of the block after the change - */ - public void queueBlock(Actor actor, Location loc, BlockData typeBefore, BlockData typeAfter) { - queueBlock(actor, loc, typeBefore, typeAfter, null, null, null); - } - - /** - * Logs a block break. The type afterwards is assumed to be air. - * - * @param actor - * Actor responsible for breaking the block - * @param before - * BlockState of the block before actually being destroyed. - */ - public void queueBlockBreak(Actor actor, BlockState before) { - queueBlock(actor, new Location(before.getWorld(), before.getX(), before.getY(), before.getZ()), before.getBlockData(), null, BlockStateCodecs.serialize(before), null, null); - } - - /** - * Logs a block break. The block type afterwards is assumed to be air. - * - * @param actor - * Actor responsible for the block break - * @param loc - * Location of the broken block - * @param typeBefore - * BlockData of the block before the break - */ - public void queueBlockBreak(Actor actor, Location loc, BlockData typeBefore) { - queueBlock(actor, loc, typeBefore, null); - } - - /** - * Logs a block place. The block type before is assumed to be air. - * - * @param actor - * Actor responsible for placing the block - * @param after - * BlockState of the block after actually being placed. - */ - public void queueBlockPlace(Actor actor, BlockState after) { - queueBlock(actor, new Location(after.getWorld(), after.getX(), after.getY(), after.getZ()), null, after.getBlockData(), null, BlockStateCodecs.serialize(after), null); - } - - /** - * Logs a block place. The block type before is assumed to be air. - * - * @param actor - * Actor responsible for placing the block - * @param loc - * Location of the placed block - * @param type - * BlockData of the placed block - */ - public void queueBlockPlace(Actor actor, Location loc, BlockData type) { - queueBlock(actor, loc, null, type); - } - - /** - * Logs a block being replaced from the before and after {@link org.bukkit.block.BlockState}s - * - * @param actor - * Actor responsible for replacing the block - * @param before - * BlockState of the block before actually being destroyed. - * @param after - * BlockState of the block after actually being placed. - */ - public void queueBlockReplace(Actor actor, BlockState before, BlockState after) { - queueBlock(actor, new Location(before.getWorld(), before.getX(), before.getY(), before.getZ()), before.getBlockData(), after.getBlockData(), BlockStateCodecs.serialize(before), BlockStateCodecs.serialize(after), null); - } - - /** - * Logs a block being replaced from the before {@link org.bukkit.block.BlockState} and the type and data after - * - * @param actor - * Actor responsible for replacing the block - * @param before - * BlockState of the block before being replaced. - * @param typeAfter - * BlockData of the block after being replaced - */ - public void queueBlockReplace(Actor actor, BlockState before, BlockData typeAfter) { - queueBlock(actor, new Location(before.getWorld(), before.getX(), before.getY(), before.getZ()), before.getBlockData(), typeAfter, BlockStateCodecs.serialize(before), null, null); - } - - /** - * Logs a block being replaced from the type and data before and the {@link org.bukkit.block.BlockState} after - * - * @param actor - * Actor responsible for replacing the block - * @param typeBefore - * BlockData of the block before being replaced - * @param after - * BlockState of the block after actually being placed. - */ - public void queueBlockReplace(Actor actor, BlockData typeBefore, BlockState after) { - queueBlock(actor, new Location(after.getWorld(), after.getX(), after.getY(), after.getZ()), typeBefore, after.getBlockData(), null, BlockStateCodecs.serialize(after), null); - } - - public void queueBlockReplace(Actor actor, Location loc, BlockData typeBefore, BlockData typeAfter) { - queueBlock(actor, loc, typeBefore, typeAfter, null, null, null); - } - - /** - * Logs an actor interacting with a container block's inventory - * - * @param actor - * The actor interacting with the container - * @param container - * The respective container. Must be an instance of an InventoryHolder. - * @param itemStack - * Item taken/stored, including amount - * @param remove - * true if the item was removed - */ - public void queueChestAccess(Actor actor, BlockState container, ItemStack itemStack, boolean remove) { - if (!(container instanceof InventoryHolder)) { - throw new IllegalArgumentException("Container must be instanceof InventoryHolder"); - } - queueChestAccess(actor, new Location(container.getWorld(), container.getX(), container.getY(), container.getZ()), container.getBlockData(), itemStack, remove); - } - - /** - * Logs an actor interacting with a container block's inventory - * - * @param actor - * The actor interacting with the container - * @param loc - * The location of the container block - * @param type - * BlockData of the container. - * @param itemStack - * Item taken/stored, including amount - * @param remove - * true if the item was removed - */ - public void queueChestAccess(Actor actor, Location loc, BlockData type, ItemStack itemStack, boolean remove) { - queueBlock(actor, loc, type, type, null, null, new ChestAccess(itemStack, remove, MaterialConverter.getOrAddMaterialId(itemStack.getType()))); - } - - /** - * Logs a container block break. The block type before is assumed to be air. All content is assumed to be taken. - * - * @param actor - * The actor breaking the container - * @param container - * Must be an instance of InventoryHolder - */ - public void queueContainerBreak(Actor actor, BlockState container) { - if (!(container instanceof InventoryHolder)) { - return; - } - queueContainerBreak(actor, new Location(container.getWorld(), container.getX(), container.getY(), container.getZ()), container.getBlockData(), ((InventoryHolder) container).getInventory()); - } - - /** - * Logs a container block break. The block type before is assumed to be air. All content is assumed to be taken. - * - * @param actor - * The actor responsible for breaking the container - * @param loc - * The location of the inventory block - * @param type - * BlockData of the container block - * @param inv - * The inventory of the container block - */ - public void queueContainerBreak(Actor actor, Location loc, BlockData type, Inventory inv) { - final ItemStack[] items = compressInventory(inv.getContents()); - for (final ItemStack item : items) { - queueChestAccess(actor, loc, type, item, true); - } - queueBlockBreak(actor, loc, type); - } - - /** - * @param killer - * Can't be null - * @param victim - * Can't be null - */ - public void queueKill(Entity killer, Entity victim) { - if (killer == null || victim == null) { - return; - } - ItemStack weapon = null; - Actor killerActor = Actor.actorFromEntity(killer); - // If it's a projectile kill we want to manually assign the weapon, so check for player before converting a projectile to its source - if (killer instanceof Player && ((Player) killer).getInventory().getItemInMainHand() != null) { - weapon = ((Player) killer).getInventory().getItemInMainHand(); - } - if (killer instanceof Projectile) { - Material projectileMaterial = itemIDfromProjectileEntity(killer); - weapon = projectileMaterial == null ? null : new ItemStack(projectileMaterial); - ProjectileSource ps = ((Projectile) killer).getShooter(); - if (ps == null) { - killerActor = Actor.actorFromEntity(killer); - } else { - killerActor = Actor.actorFromProjectileSource(ps); - } - } - - queueKill(victim.getLocation(), killerActor, Actor.actorFromEntity(victim), weapon); - } - - /** - * This form should only be used when the killer is not an entity e.g. for fall or suffocation damage - * - * @param killer - * Can't be null - * @param victim - * Can't be null - */ - public void queueKill(Actor killer, Entity victim) { - if (killer == null || victim == null) { - return; - } - queueKill(victim.getLocation(), killer, Actor.actorFromEntity(victim), null); - } - - /** - * @param location - * Location of the victim. - * @param killer - * Killer Actor. Can be null. - * @param victim - * Victim Actor. Can't be null. - * @param weapon - * Item of the weapon. null for no weapon. - */ - public void queueKill(Location location, Actor killer, Actor victim, ItemStack weapon) { - if (victim == null || !isLogged(location.getWorld())) { - return; - } - addQueueLast(new KillRow(location, killer == null ? null : killer, victim, weapon == null ? 0 : MaterialConverter.getOrAddMaterialId(weapon.getType()))); - } - - public void queueChat(Actor player, String message) { - if (!Config.ignoredChat.isEmpty()) { - String lowerCaseMessage = message.toLowerCase(); - for (String ignored : Config.ignoredChat) { - if (lowerCaseMessage.startsWith(ignored)) { - return; - } - } - } - if (hiddenPlayers.contains(player.getName().toLowerCase())) { - return; - } - while (message.length() > 256) { - addQueueLast(new ChatRow(player, message.substring(0, 256))); - message = message.substring(256); - } - addQueueLast(new ChatRow(player, message)); - } - - public void queueJoin(Player player) { - addQueueLast(new PlayerJoinRow(player)); - } - - public void queueLeave(Player player, long onlineTime) { - addQueueLast(new PlayerLeaveRow(player, onlineTime)); - } - - public void shutdown() { - synchronized (queue) { - shutdown = true; - shutdownInitialized = System.currentTimeMillis(); - queue.notifyAll(); - } - while (isAlive()) { - try { - join(); - } catch (InterruptedException e) { - // ignore - } - } - } - - @Override - public void run() { - ArrayList currentRows = new ArrayList<>(); - Connection conn = null; - BatchHelper batchHelper = new BatchHelper(); - int lastCommitsFailed = 0; - while (true) { - try { - if (conn == null) { - batchHelper.reset(); - conn = logblock.getConnection(); - if (conn != null) { - // initialize connection - conn.setAutoCommit(false); - } else { - // we did not get a connection - boolean wantsShutdown; - synchronized (queue) { - wantsShutdown = shutdown; - } - if (wantsShutdown) { - // lets give up - break; - } - // wait for a connection - logblock.getLogger().severe("[Consumer] Could not connect to the database!"); - try { - Thread.sleep(WAIT_FOR_CONNECTION_TIME_MILLIS); - } catch (InterruptedException e) { - // ignore - } - continue; - } - } - Row r; - boolean processBatch = false; - synchronized (queue) { - if (shutdown) { - // Give this thread some time to process the remaining entries - if (queue.isEmpty() || System.currentTimeMillis() - shutdownInitialized > MAX_SHUTDOWN_TIME_MILLIS) { - if (currentRows.isEmpty()) { - break; - } else { - processBatch = true; - } - } - } - r = queue.pollFirst(); - if (r == null) { - try { - if (currentRows.isEmpty() && !shutdown) { - // nothing to do for us - // wait some time before closing the connection - queue.wait(RETURN_IDLE_CONNECTION_TIME_MILLIS); - // if there is still nothing to do, close the connection and go to sleep - if (queue.isEmpty() && !shutdown) { - try { - conn.close(); - } catch (Exception e) { - // ignored - } - conn = null; - queue.wait(); - } - } else { - processBatch = true; - } - } catch (InterruptedException e) { - // ignore - } - } - } - if (r != null) { - boolean failOnActors = false; - for (final Actor actor : r.getActors()) { - if (playerIDAsIntIncludeUncommited(actor) == null) { - if (!addPlayer(conn, actor)) { - failOnActors = true; // skip this row - } - } - } - if (!failOnActors) { - currentRows.add(r); - r.process(conn, batchHelper); - } - } - if (currentRows.size() >= Math.max((processBatch ? 1 : (Config.forceToProcessAtLeast * 10)), 1)) { - batchHelper.processStatements(conn); - conn.commit(); - currentRows.clear(); - playerIds.putAll(uncommitedPlayerIds); - uncommitedPlayerIds.clear(); - uncommitedEntityIds.clear(); - lastCommitsFailed = 0; - } - } catch (Exception e) { - boolean retry = lastCommitsFailed < RETRIES_ON_UNKNOWN_CONNECTION_ERROR; - String state = "unknown"; - if (e instanceof SQLException) { - // Retry on network errors: SQLSTATE = 08S01 08001 08004 HY000 40001 - state = ((SQLException) e).getSQLState(); - retry = retry || (state != null && (state.equals("08S01") || state.equals("08001") || state.equals("08004") || state.equals("HY000") || state.equals("40001"))); - } - lastCommitsFailed += 1; - if (retry) { - logblock.getLogger().log(Level.WARNING, "[Consumer] Database connection lost, reconnecting! SQLState: " + state); - // readd rows to the queue - synchronized (queue) { - while (!currentRows.isEmpty()) { - queue.addFirst(currentRows.remove(currentRows.size() - 1)); - } - } - } else { - logblock.getLogger().log(Level.SEVERE, "[Consumer] Could not insert entries! SQLState: " + state, e); - } - currentRows.clear(); - batchHelper.reset(); - uncommitedPlayerIds.clear(); - uncommitedEntityIds.clear(); - if (conn != null) { - try { - conn.close(); - } catch (SQLException e1) { - // ignore - } - } - conn = null; - } - } - if (conn != null) { - try { - conn.close(); - } catch (SQLException e1) { - // ignore - } - } - - // readd to queue - this can be saved later - synchronized (queue) { - while (!currentRows.isEmpty()) { - queue.addFirst(currentRows.remove(currentRows.size() - 1)); - } - } - } - - public void writeToFile() throws FileNotFoundException { - final long time = System.currentTimeMillis(); - final Set insertedPlayers = new HashSet<>(); - int counter = 0; - final File importDir = new File(logblock.getDataFolder(), "import"); - importDir.mkdirs(); - PrintWriter writer = new PrintWriter(new File(importDir, "queue-" + time + "-0.sql")); - while (!isQueueEmpty()) { - final Row r = pollQueueFirst(); - if (r == null) { - continue; - } - for (final Actor actor : r.getActors()) { - if (!playerIds.containsKey(actor) && !insertedPlayers.contains(actor)) { - // Odd query contruction is to work around innodb auto increment behaviour - bug #492 - writer.println("INSERT IGNORE INTO `lb-players` (playername,UUID) SELECT '" + mysqlTextEscape(actor.getName()) + "','" + mysqlTextEscape(actor.getUUID()) + "' FROM `lb-players` WHERE NOT EXISTS (SELECT NULL FROM `lb-players` WHERE UUID = '" + mysqlTextEscape(actor.getUUID()) + "') LIMIT 1;"); - insertedPlayers.add(actor); - } - } - for (final String insert : r.getInserts()) { - writer.println(insert); - } - counter++; - if (counter % 1000 == 0) { - writer.close(); - writer = new PrintWriter(new File(importDir, "queue-" + time + "-" + counter / 1000 + ".sql")); - } - } - writer.close(); - } - - int getQueueSize() { - synchronized (queue) { - return queue.size(); - } - } - - private boolean isQueueEmpty() { - synchronized (queue) { - return queue.isEmpty(); - } - } - - private void addQueueLast(Row row) { - synchronized (queue) { - boolean wasEmpty = queue.isEmpty(); - queue.addLast(row); - addEntryCounter++; - if (Config.queueWarningSize > 0 && queue.size() >= Config.queueWarningSize && addEntryCounter >= nextWarnCounter) { - logblock.getLogger().warning("[Consumer] Queue overloaded. Size: " + queue.size()); - nextWarnCounter = addEntryCounter + 1000; - } - if (wasEmpty) { - queue.notifyAll(); - } - } - } - - private Row pollQueueFirst() { - synchronized (queue) { - return queue.pollFirst(); - } - } - - static void hide(Player player) { - hiddenPlayers.add(player.getName().toLowerCase()); - } - - static void unHide(Player player) { - hiddenPlayers.remove(player.getName().toLowerCase()); - } - - static boolean toggleHide(Player player) { - final String playerName = player.getName().toLowerCase(); - if (hiddenPlayers.contains(playerName)) { - hiddenPlayers.remove(playerName); - return false; - } - hiddenPlayers.add(playerName); - return true; - } - - private boolean addPlayer(Connection conn, Actor actor) throws SQLException { - // Odd query contruction is to work around innodb auto increment behaviour - bug #492 - String name = actor.getName(); - String uuid = actor.getUUID(); - Statement state = conn.createStatement(); - String q1 = "INSERT IGNORE INTO `lb-players` (playername,UUID) SELECT '" + mysqlTextEscape(name) + "','" + mysqlTextEscape(uuid) + "' FROM `lb-players` WHERE NOT EXISTS (SELECT NULL FROM `lb-players` WHERE UUID = '" + mysqlTextEscape(uuid) + "') LIMIT 1"; - String q2 = "SELECT playerid FROM `lb-players` WHERE UUID = '" + mysqlTextEscape(uuid) + "'"; - int q1Result = state.executeUpdate(q1); - ResultSet rs = state.executeQuery(q2); - if (rs.next()) { - uncommitedPlayerIds.put(actor, rs.getInt(1)); - } - rs.close(); - if (!uncommitedPlayerIds.containsKey(actor)) { - state.executeUpdate("INSERT IGNORE INTO `lb-players` (playername,UUID) VALUES ('" + mysqlTextEscape(name) + "','" + mysqlTextEscape(uuid) + "')"); - rs = state.executeQuery(q2); - if (rs.next()) { - uncommitedPlayerIds.put(actor, rs.getInt(1)); - } else { - logblock.getLogger().warning("[Consumer] Failed to add player " + actor.getName()); - logblock.getLogger().warning("[Consumer-Debug] Query 1: " + q1); - logblock.getLogger().warning("[Consumer-Debug] Query 1 - Result: " + q1Result); - logblock.getLogger().warning("[Consumer-Debug] Query 2: " + q2); - } - rs.close(); - } - state.close(); - return uncommitedPlayerIds.containsKey(actor); - } - - private long getEntityUUID(Connection conn, World world, UUID uuid) throws SQLException { - Map uncommitedEntityIdsHere = uncommitedEntityIds.get(world); - if (uncommitedEntityIdsHere == null) { - uncommitedEntityIdsHere = new HashMap<>(); - uncommitedEntityIds.put(world, uncommitedEntityIdsHere); - } - Long existing = uncommitedEntityIdsHere.get(uuid); - if (existing != null) { - return existing; - } - - // Odd query contruction is to work around innodb auto increment behaviour - bug #492 - final String table = getWorldConfig(world).table; - String uuidString = uuid.toString(); - Statement state = conn.createStatement(); - String q1 = "INSERT IGNORE INTO `" + table + "-entityids` (entityuuid) SELECT '" + mysqlTextEscape(uuidString) + "' FROM `" + table + "-entityids` WHERE NOT EXISTS (SELECT NULL FROM `" + table + "-entityids` WHERE entityuuid = '" + mysqlTextEscape(uuidString) + "') LIMIT 1"; - String q2 = "SELECT entityid FROM `" + table + "-entityids` WHERE entityuuid = '" + mysqlTextEscape(uuidString) + "'"; - int q1Result = state.executeUpdate(q1); - ResultSet rs = state.executeQuery(q2); - if (rs.next()) { - uncommitedEntityIdsHere.put(uuid, rs.getLong(1)); - } - rs.close(); - // if there was not any row in the table the query above does not work, so we need to try this one - if (!uncommitedEntityIdsHere.containsKey(uuid)) { - state.executeUpdate("INSERT IGNORE INTO `" + table + "-entityids` (entityuuid) VALUES ('" + mysqlTextEscape(uuidString) + "')"); - rs = state.executeQuery(q2); - if (rs.next()) { - uncommitedEntityIdsHere.put(uuid, rs.getLong(1)); - } else { - logblock.getLogger().warning("[Consumer] Failed to add entity uuid " + uuidString.toString()); - logblock.getLogger().warning("[Consumer-Debug] World: " + world.getName()); - logblock.getLogger().warning("[Consumer-Debug] Query 1: " + q1); - logblock.getLogger().warning("[Consumer-Debug] Query 1 - Result: " + q1Result); - logblock.getLogger().warning("[Consumer-Debug] Query 2: " + q2); - } - rs.close(); - } - state.close(); - return uncommitedEntityIdsHere.get(uuid); - } - - private void queueBlock(Actor actor, Location loc, BlockData typeBefore, BlockData typeAfter, YamlConfiguration stateBefore, YamlConfiguration stateAfter, ChestAccess ca) { - if (typeBefore == null || typeBefore.getMaterial() == Material.CAVE_AIR || typeBefore.getMaterial() == Material.VOID_AIR) { - typeBefore = Bukkit.createBlockData(Material.AIR); - } - if (typeAfter == null && ((typeBefore instanceof Waterlogged && ((Waterlogged) typeBefore).isWaterlogged()) || BukkitUtils.isAlwaysWaterlogged(typeBefore.getMaterial()))) { - typeAfter = Bukkit.createBlockData(Material.WATER); - } - if (typeAfter == null || typeAfter.getMaterial() == Material.CAVE_AIR || typeAfter.getMaterial() == Material.VOID_AIR) { - typeAfter = Bukkit.createBlockData(Material.AIR); - } - if (BlockChangePreLogEvent.getHandlerList().getRegisteredListeners().length > 0) { - // Create and call the event - BlockChangePreLogEvent event = new BlockChangePreLogEvent(actor, loc, typeBefore, typeAfter, stateBefore, stateAfter, ca); - logblock.getServer().getPluginManager().callEvent(event); - if (event.isCancelled()) { - return; - } - - // Update variables - actor = event.getOwnerActor(); - loc = event.getLocation(); - typeBefore = event.getTypeBefore(); - typeAfter = event.getTypeAfter(); - stateBefore = event.getStateBefore(); - stateAfter = event.getStateAfter(); - ca = event.getChestAccess(); - } - // Do this last so LogBlock still has final say in what is being added - if (actor == null || loc == null || typeBefore == null || typeAfter == null || hiddenPlayers.contains(actor.getName().toLowerCase()) || !isLogged(loc.getWorld()) || typeBefore != typeAfter && hiddenBlocks.contains(typeBefore.getMaterial()) && hiddenBlocks.contains(typeAfter.getMaterial())) { - return; - } - - int replacedMaterialId = MaterialConverter.getOrAddMaterialId(typeBefore); - int replacedStateId = MaterialConverter.getOrAddBlockStateId(typeBefore); - int typeMaterialId = MaterialConverter.getOrAddMaterialId(typeAfter); - int typeStateId = MaterialConverter.getOrAddBlockStateId(typeAfter); - - addQueueLast(new BlockRow(loc, actor, replacedMaterialId, replacedStateId, Utils.serializeYamlConfiguration(stateBefore), typeMaterialId, typeStateId, Utils.serializeYamlConfiguration(stateAfter), ca)); - } - - public void queueEntityModification(Actor actor, Entity entity, EntityChangeType changeType, YamlConfiguration data) { - if (actor == null || changeType == null || entity == null || hiddenPlayers.contains(actor.getName().toLowerCase()) || !isLogged(entity.getWorld())) { - return; - } - UUID entityId = entity.getUniqueId(); - EntityType entityType = entity.getType(); - Location loc = entity.getLocation(); - - if (EntityChangePreLogEvent.getHandlerList().getRegisteredListeners().length > 0) { - // Create and call the event - EntityChangePreLogEvent event = new EntityChangePreLogEvent(actor, loc, entity, changeType, data); - logblock.getServer().getPluginManager().callEvent(event); - if (event.isCancelled()) { - return; - } - - // Update variables - actor = event.getOwnerActor(); - loc = event.getLocation(); - } - // Do this last so LogBlock still has final say in what is being added - if (actor == null || loc == null || hiddenPlayers.contains(actor.getName().toLowerCase()) || !isLogged(loc.getWorld())) { - return; - } - - addQueueLast(new EntityRow(loc, actor, entityType, entityId, changeType, Utils.serializeYamlConfiguration(data))); - } - - /** - * Change the UUID that is stored for an entity in the database. This is needed when an entity is respawned - * and now has a different UUID. - * - * @param world the world that contains the entity - * @param entityId the database id of the entity - * @param entityUUID the new UUID of the entity - */ - public void queueEntityUUIDChange(World world, int entityId, UUID entityUUID) { - addQueueLast(new EntityUUIDChange(world, entityId, entityUUID)); - } - - private String playerID(Actor actor) { - if (actor == null) { - return "NULL"; - } - final Integer id = playerIds.get(actor); - if (id != null) { - return id.toString(); - } - return "(SELECT playerid FROM `lb-players` WHERE UUID = '" + mysqlTextEscape(actor.getUUID()) + "')"; - } - - private Integer playerIDAsIntIncludeUncommited(Actor actor) { - if (actor == null) { - return null; - } - Integer id = playerIds.get(actor); - if (id != null) { - return id; - } - return uncommitedPlayerIds.get(actor); - } - - private static interface Row { - String[] getInserts(); - - void process(Connection conn, BatchHelper batchHelper) throws SQLException; - - Actor[] getActors(); - } - - private class BlockRow extends BlockChange implements Row { - final String statementString; - final String selectActorIdStatementString; - - public BlockRow(Location loc, Actor actor, int replaced, int replacedData, byte[] replacedState, int type, int typeData, byte[] typeState, ChestAccess ca) { - super(System.currentTimeMillis() / 1000, loc, actor, replaced, replacedData, replacedState, type, typeData, typeState, ca); - - statementString = getWorldConfig(loc.getWorld()).insertBlockStatementString; - selectActorIdStatementString = getWorldConfig(loc.getWorld()).selectBlockActorIdStatementString; - } - - @Override - public String[] getInserts() { - final String table = getWorldConfig(loc.getWorld()).table; - final String[] inserts = new String[ca != null || replacedState != null || typeState != null ? 2 : 1]; - - inserts[0] = "INSERT INTO `" + table + "-blocks` (date, playerid, replaced, replaceddata, type, typedata, x, y, z) VALUES (FROM_UNIXTIME(" + date + "), " + playerID(actor) + ", " + replacedMaterial + ", " + replacedData + ", " + typeMaterial + ", " + typeData + ", '" + loc.getBlockX() - + "', " + safeY(loc) + ", '" + loc.getBlockZ() + "');"; - if (replacedState != null || typeState != null) { - inserts[1] = "INSERT INTO `" + table + "-state` (replacedState, typeState, id) VALUES(" + Utils.mysqlPrepareBytesForInsertAllowNull(replacedState) + ", " + Utils.mysqlPrepareBytesForInsertAllowNull(typeState) + ", LAST_INSERT_ID());"; - } else if (ca != null) { - try { - inserts[1] = "INSERT INTO `" + table + "-chestdata` (id, item, itemremove, itemtype) values (LAST_INSERT_ID(), '" + Utils.mysqlEscapeBytes(Utils.saveItemStack(ca.itemStack)) + "', " + (ca.remove ? 1 : 0) + ", " + ca.itemType + ");"; - } catch (Exception e) { - LogBlock.getInstance().getLogger().log(Level.SEVERE, "Could not serialize ItemStack " + e.getMessage(), e); - LogBlock.getInstance().getLogger().log(Level.SEVERE, "Problematic row: " + toString()); - return new String[0]; - } - } - return inserts; - } - - @Override - public Actor[] getActors() { - return new Actor[] { actor }; - } - - @Override - public void process(Connection conn, BatchHelper batchHelper) throws SQLException { - byte[] serializedItemStack = null; - if (ca != null) { - try { - serializedItemStack = Utils.saveItemStack(ca.itemStack); - } catch (Exception e) { - LogBlock.getInstance().getLogger().log(Level.SEVERE, "Could not serialize ItemStack " + e.getMessage(), e); - LogBlock.getInstance().getLogger().log(Level.SEVERE, "Problematic row: " + toString()); - return; - } - } - final byte[] finalSerializedItemStack = serializedItemStack; - int sourceActor = playerIDAsIntIncludeUncommited(actor); - Location actorBlockLocation = actor.getBlockLocation(); - if (actorBlockLocation != null) { - Integer tempSourceActor = batchHelper.getUncommitedBlockActor(actorBlockLocation); - if (tempSourceActor != null) { - sourceActor = tempSourceActor; - } else { - PreparedStatement smt = batchHelper.getOrPrepareStatement(conn, selectActorIdStatementString, Statement.NO_GENERATED_KEYS); - smt.setInt(1, actorBlockLocation.getBlockX()); - smt.setInt(2, safeY(actorBlockLocation)); - smt.setInt(3, actorBlockLocation.getBlockZ()); - ResultSet rs = smt.executeQuery(); - if (rs.next()) { - sourceActor = rs.getInt(1); - } - rs.close(); - } - } - PreparedStatement smt = batchHelper.getOrPrepareStatement(conn, statementString, Statement.RETURN_GENERATED_KEYS); - smt.setLong(1, date); - smt.setInt(2, sourceActor); - smt.setInt(3, replacedMaterial); - smt.setInt(4, replacedData); - smt.setInt(5, typeMaterial); - smt.setInt(6, typeData); - smt.setInt(7, loc.getBlockX()); - smt.setInt(8, safeY(loc)); - smt.setInt(9, loc.getBlockZ()); - batchHelper.addUncommitedBlockActorId(loc, sourceActor); - batchHelper.addBatch(smt, new LongCallback() { - @Override - public void call(long id) throws SQLException { - PreparedStatement ps; - if (typeState != null || replacedState != null) { - ps = batchHelper.getOrPrepareStatement(conn, getWorldConfig(loc.getWorld()).insertBlockStateStatementString, Statement.NO_GENERATED_KEYS); - ps.setBytes(1, replacedState); - ps.setBytes(2, typeState); - ps.setLong(3, id); - batchHelper.addBatch(ps, null); - } - if (ca != null) { - ps = batchHelper.getOrPrepareStatement(conn, getWorldConfig(loc.getWorld()).insertBlockChestDataStatementString, Statement.NO_GENERATED_KEYS); - ps.setBytes(1, finalSerializedItemStack); - ps.setInt(2, ca.remove ? 1 : 0); - ps.setLong(3, id); - ps.setInt(4, ca.itemType); - batchHelper.addBatch(ps, null); - } - } - }); - } - } - - private class KillRow implements Row { - final long date; - final Actor killer, victim; - final int weapon; - final Location loc; - final String statementString; - - KillRow(Location loc, Actor attacker, Actor defender, int weapon) { - date = System.currentTimeMillis() / 1000; - this.loc = loc; - killer = attacker; - victim = defender; - this.weapon = weapon; - - statementString = "INSERT INTO `" + getWorldConfig(loc.getWorld()).table + "-kills` (date, killer, victim, weapon, x, y, z) VALUES (FROM_UNIXTIME(?), ?, ?, ?, ?, ?, ?)"; - } - - @Override - public String[] getInserts() { - return new String[] { "INSERT INTO `" + getWorldConfig(loc.getWorld()).table + "-kills` (date, killer, victim, weapon, x, y, z) VALUES (FROM_UNIXTIME(" + date + "), " + playerID(killer) + ", " + playerID(victim) + ", " + weapon + ", " + loc.getBlockX() + ", " + safeY(loc) + ", " - + loc.getBlockZ() + ");" }; - } - - @Override - public Actor[] getActors() { - return new Actor[] { killer, victim }; - } - - @Override - public void process(Connection conn, BatchHelper batchHelper) throws SQLException { - PreparedStatement smt = batchHelper.getOrPrepareStatement(conn, statementString, Statement.NO_GENERATED_KEYS); - smt.setLong(1, date); - smt.setInt(2, playerIDAsIntIncludeUncommited(killer)); - smt.setInt(3, playerIDAsIntIncludeUncommited(victim)); - smt.setInt(4, weapon); - smt.setInt(5, loc.getBlockX()); - smt.setInt(6, safeY(loc)); - smt.setInt(7, loc.getBlockZ()); - batchHelper.addBatch(smt, null); - } - } - - private class ChatRow extends ChatMessage implements Row { - private String statementString; - - ChatRow(Actor player, String message) { - super(player, message); - - statementString = "INSERT INTO `lb-chat` (date, playerid, message) VALUES (FROM_UNIXTIME(?), ?, ?)"; - } - - @Override - public String[] getInserts() { - return new String[] { "INSERT INTO `lb-chat` (date, playerid, message) VALUES (FROM_UNIXTIME(" + date + "), " + playerID(player) + ", '" + mysqlTextEscape(message) + "');" }; - } - - @Override - public Actor[] getActors() { - return new Actor[] { player }; - } - - @Override - public void process(Connection conn, BatchHelper batchHelper) throws SQLException { - PreparedStatement smt = batchHelper.getOrPrepareStatement(conn, statementString, Statement.NO_GENERATED_KEYS); - smt.setLong(1, date); - smt.setInt(2, playerIDAsIntIncludeUncommited(player)); - smt.setString(3, message); - batchHelper.addBatch(smt, null); - } - } - - private class PlayerJoinRow implements Row { - private final Actor player; - private final long lastLogin; - private final String ip; - private String statementString; - - PlayerJoinRow(Player player) { - this.player = Actor.actorFromEntity(player); - lastLogin = System.currentTimeMillis() / 1000; - ip = player.getAddress().toString().replace("'", "\\'"); - - if (logPlayerInfo) { - statementString = "UPDATE `lb-players` SET lastlogin = FROM_UNIXTIME(?), firstlogin = IF(firstlogin = 0, FROM_UNIXTIME(?), firstlogin), ip = ?, playername = ? WHERE UUID = ?"; - } else { - statementString = "UPDATE `lb-players` SET playername = ? WHERE UUID = ?"; - } - } - - @Override - public String[] getInserts() { - if (logPlayerInfo) { - return new String[] { - "UPDATE `lb-players` SET lastlogin = FROM_UNIXTIME(" + lastLogin + "), firstlogin = IF(firstlogin = 0, FROM_UNIXTIME(" + lastLogin + "), firstlogin), ip = '" + ip + "', playername = '" + mysqlTextEscape(player.getName()) + "' WHERE UUID = '" + player.getUUID() + "';" }; - } - return new String[] { "UPDATE `lb-players` SET playername = '" + mysqlTextEscape(player.getName()) + "' WHERE UUID = '" + mysqlTextEscape(player.getUUID()) + "';" }; - } - - @Override - public Actor[] getActors() { - return new Actor[] { player }; - } - - @Override - public void process(Connection conn, BatchHelper batchHelper) throws SQLException { - PreparedStatement smt = batchHelper.getOrPrepareStatement(conn, statementString, Statement.NO_GENERATED_KEYS); - if (logPlayerInfo) { - smt.setLong(1, lastLogin); - smt.setLong(2, lastLogin); - smt.setString(3, ip); - smt.setString(4, player.getName()); - smt.setString(5, player.getUUID()); - } else { - smt.setString(1, player.getName()); - smt.setString(2, player.getUUID()); - } - batchHelper.addBatch(smt, null); - } - } - - private class PlayerLeaveRow implements Row { - private final long onlineTime; - private final Actor actor; - private String statementString; - - PlayerLeaveRow(Player player, long onlineTime) { - this.onlineTime = onlineTime; - actor = Actor.actorFromEntity(player); - statementString = "UPDATE `lb-players` SET onlinetime = onlinetime + ? WHERE lastlogin > 0 && UUID = ?"; - } - - @Override - public String[] getInserts() { - if (logPlayerInfo) { - return new String[] { "UPDATE `lb-players` SET onlinetime = onlinetime + " + onlineTime + " WHERE lastlogin > 0 && UUID = '" + mysqlTextEscape(actor.getUUID()) + "';" }; - } - return new String[0]; - } - - @Override - public Actor[] getActors() { - return new Actor[] { actor }; - } - - @Override - public void process(Connection conn, BatchHelper batchHelper) throws SQLException { - PreparedStatement smt = batchHelper.getOrPrepareStatement(conn, statementString, Statement.NO_GENERATED_KEYS); - smt.setLong(1, onlineTime); - smt.setString(2, actor.getUUID()); - batchHelper.addBatch(smt, null); - } - } - - private class EntityRow extends EntityChange implements Row { - final String statementString; - final String selectActorIdStatementString; - - public EntityRow(Location loc, Actor actor, EntityType type, UUID entityid, EntityChangeType changeType, byte[] data) { - super(System.currentTimeMillis() / 1000, loc, actor, type, entityid, changeType, data); - statementString = getWorldConfig(loc.getWorld()).insertEntityStatementString; - selectActorIdStatementString = getWorldConfig(loc.getWorld()).selectBlockActorIdStatementString; - } - - @Override - public String[] getInserts() { - final String table = getWorldConfig(loc.getWorld()).table; - final String[] inserts = new String[2]; - - inserts[0] = "INSERT IGNORE INTO `" + table + "-entityids` (entityuuid) SELECT '" + mysqlTextEscape(entityUUID.toString()) + "' FROM `" + table + "-entityids` WHERE NOT EXISTS (SELECT NULL FROM `" + table + "-entityids` WHERE entityuuid = '" + mysqlTextEscape(entityUUID.toString()) + "') LIMIT 1"; - int entityTypeId = EntityTypeConverter.getOrAddEntityTypeId(type); - inserts[1] = "INSERT INTO `" + table + "-entities` (date, playerid, entityid, entitytypeid, x, y, z, action, data) VALUES (FROM_UNIXTIME(" + date + "), " + playerID(actor) + ", " + "(SELECT entityid FROM `" + table + "-entityids` WHERE entityuuid = '" + mysqlTextEscape(entityUUID.toString()) + "')" - + ", " + entityTypeId + ", '" + loc.getBlockX() + "', " + safeY(loc) + ", '" + loc.getBlockZ() + "', " + changeType.ordinal() + ", " + Utils.mysqlPrepareBytesForInsertAllowNull(data) + ");"; - return inserts; - } - - @Override - public Actor[] getActors() { - return new Actor[] { actor }; - } - - @Override - public void process(Connection conn, BatchHelper batchHelper) throws SQLException { - int sourceActor = playerIDAsIntIncludeUncommited(actor); - Location actorBlockLocation = actor.getBlockLocation(); - if (actorBlockLocation != null) { - Integer tempSourceActor = batchHelper.getUncommitedBlockActor(actorBlockLocation); - if (tempSourceActor != null) { - sourceActor = tempSourceActor; - } else { - PreparedStatement smt = batchHelper.getOrPrepareStatement(conn, selectActorIdStatementString, Statement.NO_GENERATED_KEYS); - smt.setInt(1, actorBlockLocation.getBlockX()); - smt.setInt(2, safeY(actorBlockLocation)); - smt.setInt(3, actorBlockLocation.getBlockZ()); - ResultSet rs = smt.executeQuery(); - if (rs.next()) { - sourceActor = rs.getInt(1); - } - rs.close(); - } - } - PreparedStatement smt = batchHelper.getOrPrepareStatement(conn, statementString, Statement.NO_GENERATED_KEYS); - smt.setLong(1, date); - smt.setInt(2, sourceActor); - smt.setLong(3, getEntityUUID(conn, loc.getWorld(), entityUUID)); - smt.setInt(4, EntityTypeConverter.getOrAddEntityTypeId(type)); - smt.setInt(5, loc.getBlockX()); - smt.setInt(6, safeY(loc)); - smt.setInt(7, loc.getBlockZ()); - smt.setInt(8, changeType.ordinal()); - smt.setBytes(9, data); - batchHelper.addBatch(smt, null); - } - } - - private class EntityUUIDChange implements Row { - private final World world; - private final long entityId; - private final UUID entityUUID; - final String updateEntityUUIDString; - - public EntityUUIDChange(World world, long entityId, UUID entityUUID) { - this.world = world; - this.entityId = entityId; - this.entityUUID = entityUUID; - updateEntityUUIDString = getWorldConfig(world).updateEntityUUIDString; - } - - @Override - public String[] getInserts() { - final String table = getWorldConfig(world).table; - final String[] inserts = new String[1]; - - inserts[0] = "UPDATE `" + table + "-entityids` SET entityuuid = '" + mysqlTextEscape(entityUUID.toString()) + "' WHERE entityid = " + entityId; - return inserts; - } - - @Override - public Actor[] getActors() { - return new Actor[0]; - } - - @Override - public void process(Connection conn, BatchHelper batchHelper) throws SQLException { - PreparedStatement smt = batchHelper.getOrPrepareStatement(conn, updateEntityUUIDString, Statement.NO_GENERATED_KEYS); - smt.setString(1, entityUUID.toString()); - smt.setLong(2, entityId); - smt.executeUpdate(); - } - } - - private int safeY(Location loc) { - int safeY = loc.getBlockY(); - if (safeY < Short.MIN_VALUE) { - safeY = Short.MIN_VALUE; - } - if (safeY > Short.MAX_VALUE) { - safeY = Short.MAX_VALUE; - } - return safeY; - } - - private class BatchHelper { - private HashMap preparedStatements = new HashMap<>(); - private HashSet preparedStatementsWithGeneratedKeys = new HashSet<>(); - private LinkedHashMap> generatedKeyHandler = new LinkedHashMap<>(); - private HashMap uncommitedBlockActors = new HashMap<>(); - - public void reset() { - preparedStatements.clear(); - preparedStatementsWithGeneratedKeys.clear(); - generatedKeyHandler.clear(); - uncommitedBlockActors.clear(); - } - - public void addUncommitedBlockActorId(Location loc, int actorId) { - uncommitedBlockActors.put(loc, actorId); - } - - public Integer getUncommitedBlockActor(Location loc) { - return uncommitedBlockActors.get(loc); - } - - public void processStatements(Connection conn) throws SQLException { - while (!generatedKeyHandler.isEmpty()) { - Entry> entry = generatedKeyHandler.entrySet().iterator().next(); - PreparedStatement smt = entry.getKey(); - ArrayList callbackList = entry.getValue(); - generatedKeyHandler.remove(smt); - smt.executeBatch(); - if (preparedStatementsWithGeneratedKeys.contains(smt)) { - ResultSet keys = smt.getGeneratedKeys(); - long[] results = new long[callbackList.size()]; - int pos = 0; - while (keys.next() && pos < results.length) { - results[pos++] = keys.getLong(1); - } - keys.close(); - for (int i = 0; i < results.length; i++) { - LongCallback callback = callbackList.get(i); - if (callback != null) { - callback.call(results[i]); - } - } - } - } - uncommitedBlockActors.clear(); - } - - public PreparedStatement getOrPrepareStatement(Connection conn, String sql, int autoGeneratedKeys) throws SQLException { - PreparedStatement smt = preparedStatements.get(sql); - if (smt == null) { - smt = conn.prepareStatement(sql, autoGeneratedKeys); - preparedStatements.put(sql, smt); - if (autoGeneratedKeys == Statement.RETURN_GENERATED_KEYS) { - preparedStatementsWithGeneratedKeys.add(smt); - } - } - return smt; - } - - public void addBatch(PreparedStatement smt, LongCallback generatedKeysCallback) throws SQLException { - smt.addBatch(); - ArrayList callbackList = generatedKeyHandler.get(smt); - if (callbackList == null) { - callbackList = new ArrayList<>(); - generatedKeyHandler.put(smt, callbackList); - } - callbackList.add(generatedKeysCallback); - } - } - - protected interface LongCallback { - public void call(long value) throws SQLException; - } -} +package de.diddiz.LogBlock; + +import static de.diddiz.LogBlock.config.Config.getWorldConfig; +import static de.diddiz.LogBlock.config.Config.hiddenBlocks; +import static de.diddiz.LogBlock.config.Config.hiddenPlayers; +import static de.diddiz.LogBlock.config.Config.isLogged; +import static de.diddiz.LogBlock.config.Config.logPlayerInfo; +import static de.diddiz.LogBlock.util.BukkitUtils.compressInventory; +import static de.diddiz.LogBlock.util.BukkitUtils.itemIDfromProjectileEntity; +import static de.diddiz.LogBlock.util.Utils.mysqlTextEscape; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.PrintWriter; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.UUID; +import java.util.logging.Level; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.BlockState; +import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.Waterlogged; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.entity.Projectile; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.ItemStack; +import org.bukkit.projectiles.ProjectileSource; + +import de.diddiz.LogBlock.EntityChange.EntityChangeType; +import de.diddiz.LogBlock.blockstate.BlockStateCodecs; +import de.diddiz.LogBlock.config.Config; +import de.diddiz.LogBlock.events.BlockChangePreLogEvent; +import de.diddiz.LogBlock.events.EntityChangePreLogEvent; +import de.diddiz.LogBlock.util.BukkitUtils; +import de.diddiz.LogBlock.util.Utils; + +public class Consumer extends Thread { + private static final int MAX_SHUTDOWN_TIME_MILLIS = 20000; + private static final int WAIT_FOR_CONNECTION_TIME_MILLIS = 10000; + private static final int RETURN_IDLE_CONNECTION_TIME_MILLIS = 120000; + private static final int RETRIES_ON_UNKNOWN_CONNECTION_ERROR = 2; + + private final Deque queue = new ArrayDeque<>(); + private final LogBlock logblock; + private final Map playerIds = new HashMap<>(); + private final Map uncommitedPlayerIds = new HashMap<>(); + private final Map> uncommitedEntityIds = new HashMap<>(); + + private long addEntryCounter; + private long nextWarnCounter; + + private boolean shutdown; + private long shutdownInitialized; + + Consumer(LogBlock logblock) { + this.logblock = logblock; + PlayerLeaveRow.class.getName(); // preload this class + setName("Logblock-Consumer"); + } + + public LogBlock getLogblock() { + return logblock; + } + + /** + * Logs any block change. Don't try to combine broken and placed blocks. Queue two block changes or use the queueBLockReplace methods. + * + * @param actor + * Actor responsible for making the change + * @param loc + * Location of the block change + * @param typeBefore + * BlockData of the block before the change + * @param typeAfter + * BlockData of the block after the change + */ + public void queueBlock(Actor actor, Location loc, BlockData typeBefore, BlockData typeAfter) { + queueBlock(actor, loc, typeBefore, typeAfter, null, null, null); + } + + /** + * Logs a block break. The type afterwards is assumed to be air. + * + * @param actor + * Actor responsible for breaking the block + * @param before + * BlockState of the block before actually being destroyed. + */ + public void queueBlockBreak(Actor actor, BlockState before) { + queueBlock(actor, new Location(before.getWorld(), before.getX(), before.getY(), before.getZ()), before.getBlockData(), null, BlockStateCodecs.serialize(before), null, null); + } + + /** + * Logs a block break. The block type afterwards is assumed to be air. + * + * @param actor + * Actor responsible for the block break + * @param loc + * Location of the broken block + * @param typeBefore + * BlockData of the block before the break + */ + public void queueBlockBreak(Actor actor, Location loc, BlockData typeBefore) { + queueBlock(actor, loc, typeBefore, null); + } + + /** + * Logs a block place. The block type before is assumed to be air. + * + * @param actor + * Actor responsible for placing the block + * @param after + * BlockState of the block after actually being placed. + */ + public void queueBlockPlace(Actor actor, BlockState after) { + queueBlock(actor, new Location(after.getWorld(), after.getX(), after.getY(), after.getZ()), null, after.getBlockData(), null, BlockStateCodecs.serialize(after), null); + } + + /** + * Logs a block place. The block type before is assumed to be air. + * + * @param actor + * Actor responsible for placing the block + * @param loc + * Location of the placed block + * @param type + * BlockData of the placed block + */ + public void queueBlockPlace(Actor actor, Location loc, BlockData type) { + queueBlock(actor, loc, null, type); + } + + /** + * Logs a block being replaced from the before and after {@link org.bukkit.block.BlockState}s + * + * @param actor + * Actor responsible for replacing the block + * @param before + * BlockState of the block before actually being destroyed. + * @param after + * BlockState of the block after actually being placed. + */ + public void queueBlockReplace(Actor actor, BlockState before, BlockState after) { + queueBlock(actor, new Location(before.getWorld(), before.getX(), before.getY(), before.getZ()), before.getBlockData(), after.getBlockData(), BlockStateCodecs.serialize(before), BlockStateCodecs.serialize(after), null); + } + + /** + * Logs a block being replaced from the before {@link org.bukkit.block.BlockState} and the type and data after + * + * @param actor + * Actor responsible for replacing the block + * @param before + * BlockState of the block before being replaced. + * @param typeAfter + * BlockData of the block after being replaced + */ + public void queueBlockReplace(Actor actor, BlockState before, BlockData typeAfter) { + queueBlock(actor, new Location(before.getWorld(), before.getX(), before.getY(), before.getZ()), before.getBlockData(), typeAfter, BlockStateCodecs.serialize(before), null, null); + } + + /** + * Logs a block being replaced from the type and data before and the {@link org.bukkit.block.BlockState} after + * + * @param actor + * Actor responsible for replacing the block + * @param typeBefore + * BlockData of the block before being replaced + * @param after + * BlockState of the block after actually being placed. + */ + public void queueBlockReplace(Actor actor, BlockData typeBefore, BlockState after) { + queueBlock(actor, new Location(after.getWorld(), after.getX(), after.getY(), after.getZ()), typeBefore, after.getBlockData(), null, BlockStateCodecs.serialize(after), null); + } + + public void queueBlockReplace(Actor actor, Location loc, BlockData typeBefore, BlockData typeAfter) { + queueBlock(actor, loc, typeBefore, typeAfter, null, null, null); + } + + /** + * Logs an actor interacting with a container block's inventory + * + * @param actor + * The actor interacting with the container + * @param container + * The respective container. Must be an instance of an InventoryHolder. + * @param itemStack + * Item taken/stored, including amount + * @param remove + * true if the item was removed + */ + public void queueChestAccess(Actor actor, BlockState container, ItemStack itemStack, boolean remove) { + if (!(container instanceof InventoryHolder)) { + throw new IllegalArgumentException("Container must be instanceof InventoryHolder"); + } + queueChestAccess(actor, new Location(container.getWorld(), container.getX(), container.getY(), container.getZ()), container.getBlockData(), itemStack, remove); + } + + /** + * Logs an actor interacting with a container block's inventory + * + * @param actor + * The actor interacting with the container + * @param loc + * The location of the container block + * @param type + * BlockData of the container. + * @param itemStack + * Item taken/stored, including amount + * @param remove + * true if the item was removed + */ + public void queueChestAccess(Actor actor, Location loc, BlockData type, ItemStack itemStack, boolean remove) { + queueBlock(actor, loc, type, type, null, null, new ChestAccess(itemStack, remove, MaterialConverter.getOrAddMaterialId(itemStack.getType()))); + } + + /** + * Logs a container block break. The block type before is assumed to be air. All content is assumed to be taken. + * + * @param actor + * The actor breaking the container + * @param container + * Must be an instance of InventoryHolder + */ + public void queueContainerBreak(Actor actor, BlockState container) { + if (!(container instanceof InventoryHolder)) { + return; + } + queueContainerBreak(actor, new Location(container.getWorld(), container.getX(), container.getY(), container.getZ()), container.getBlockData(), ((InventoryHolder) container).getInventory()); + } + + /** + * Logs a container block break. The block type before is assumed to be air. All content is assumed to be taken. + * + * @param actor + * The actor responsible for breaking the container + * @param loc + * The location of the inventory block + * @param type + * BlockData of the container block + * @param inv + * The inventory of the container block + */ + public void queueContainerBreak(Actor actor, Location loc, BlockData type, Inventory inv) { + final ItemStack[] items = compressInventory(inv.getContents()); + for (final ItemStack item : items) { + queueChestAccess(actor, loc, type, item, true); + } + queueBlockBreak(actor, loc, type); + } + + /** + * @param killer + * Can't be null + * @param victim + * Can't be null + */ + public void queueKill(Entity killer, Entity victim) { + if (killer == null || victim == null) { + return; + } + ItemStack weapon = null; + Actor killerActor = Actor.actorFromEntity(killer); + // If it's a projectile kill we want to manually assign the weapon, so check for player before converting a projectile to its source + if (killer instanceof Player && ((Player) killer).getInventory().getItemInMainHand() != null) { + weapon = ((Player) killer).getInventory().getItemInMainHand(); + } + if (killer instanceof Projectile) { + Material projectileMaterial = itemIDfromProjectileEntity(killer); + weapon = projectileMaterial == null ? null : new ItemStack(projectileMaterial); + ProjectileSource ps = ((Projectile) killer).getShooter(); + if (ps == null) { + killerActor = Actor.actorFromEntity(killer); + } else { + killerActor = Actor.actorFromProjectileSource(ps); + } + } + + queueKill(victim.getLocation(), killerActor, Actor.actorFromEntity(victim), weapon); + } + + /** + * This form should only be used when the killer is not an entity e.g. for fall or suffocation damage + * + * @param killer + * Can't be null + * @param victim + * Can't be null + */ + public void queueKill(Actor killer, Entity victim) { + if (killer == null || victim == null) { + return; + } + queueKill(victim.getLocation(), killer, Actor.actorFromEntity(victim), null); + } + + /** + * @param location + * Location of the victim. + * @param killer + * Killer Actor. Can be null. + * @param victim + * Victim Actor. Can't be null. + * @param weapon + * Item of the weapon. null for no weapon. + */ + public void queueKill(Location location, Actor killer, Actor victim, ItemStack weapon) { + if (victim == null || !isLogged(location.getWorld())) { + return; + } + addQueueLast(new KillRow(location, killer == null ? null : killer, victim, weapon == null ? 0 : MaterialConverter.getOrAddMaterialId(weapon.getType()))); + } + + public void queueChat(Actor player, String message) { + if (!Config.ignoredChat.isEmpty()) { + String lowerCaseMessage = message.toLowerCase(); + for (String ignored : Config.ignoredChat) { + if (lowerCaseMessage.startsWith(ignored)) { + return; + } + } + } + if (hiddenPlayers.contains(player.getName().toLowerCase())) { + return; + } + while (message.length() > 256) { + addQueueLast(new ChatRow(player, message.substring(0, 256))); + message = message.substring(256); + } + addQueueLast(new ChatRow(player, message)); + } + + public void queueJoin(Player player) { + addQueueLast(new PlayerJoinRow(player)); + } + + public void queueLeave(Player player, long onlineTime) { + addQueueLast(new PlayerLeaveRow(player, onlineTime)); + } + + public void shutdown() { + synchronized (queue) { + shutdown = true; + shutdownInitialized = System.currentTimeMillis(); + queue.notifyAll(); + } + while (isAlive()) { + try { + join(); + } catch (InterruptedException e) { + // ignore + } + } + } + + @Override + public void run() { + ArrayList currentRows = new ArrayList<>(); + Connection conn = null; + BatchHelper batchHelper = new BatchHelper(); + int lastCommitsFailed = 0; + while (true) { + try { + if (conn == null) { + batchHelper.reset(); + conn = logblock.getConnection(); + if (conn != null) { + // initialize connection + conn.setAutoCommit(false); + } else { + // we did not get a connection + boolean wantsShutdown; + synchronized (queue) { + wantsShutdown = shutdown; + } + if (wantsShutdown) { + // lets give up + break; + } + // wait for a connection + logblock.getLogger().severe("[Consumer] Could not connect to the database!"); + try { + Thread.sleep(WAIT_FOR_CONNECTION_TIME_MILLIS); + } catch (InterruptedException e) { + // ignore + } + continue; + } + } + Row r; + boolean processBatch = false; + synchronized (queue) { + if (shutdown) { + // Give this thread some time to process the remaining entries + if (queue.isEmpty() || System.currentTimeMillis() - shutdownInitialized > MAX_SHUTDOWN_TIME_MILLIS) { + if (currentRows.isEmpty()) { + break; + } else { + processBatch = true; + } + } + } + r = queue.pollFirst(); + if (r == null) { + try { + if (currentRows.isEmpty() && !shutdown) { + // nothing to do for us + // wait some time before closing the connection + queue.wait(RETURN_IDLE_CONNECTION_TIME_MILLIS); + // if there is still nothing to do, close the connection and go to sleep + if (queue.isEmpty() && !shutdown) { + try { + conn.close(); + } catch (Exception e) { + // ignored + } + conn = null; + queue.wait(); + } + } else { + processBatch = true; + } + } catch (InterruptedException e) { + // ignore + } + } + } + if (r != null) { + boolean failOnActors = false; + for (final Actor actor : r.getActors()) { + if (playerIDAsIntIncludeUncommited(actor) == null) { + if (!addPlayer(conn, actor)) { + failOnActors = true; // skip this row + } + } + } + if (!failOnActors) { + currentRows.add(r); + r.process(conn, batchHelper); + } + } + if (currentRows.size() >= Math.max((processBatch ? 1 : (Config.forceToProcessAtLeast * 10)), 1)) { + batchHelper.processStatements(conn); + conn.commit(); + currentRows.clear(); + playerIds.putAll(uncommitedPlayerIds); + uncommitedPlayerIds.clear(); + uncommitedEntityIds.clear(); + lastCommitsFailed = 0; + } + } catch (Exception e) { + boolean retry = lastCommitsFailed < RETRIES_ON_UNKNOWN_CONNECTION_ERROR; + String state = "unknown"; + if (e instanceof SQLException) { + // Retry on network errors: SQLSTATE = 08S01 08001 08004 HY000 40001 + state = ((SQLException) e).getSQLState(); + retry = retry || (state != null && (state.equals("08S01") || state.equals("08001") || state.equals("08004") || state.equals("HY000") || state.equals("40001"))); + } + lastCommitsFailed += 1; + if (retry) { + logblock.getLogger().log(Level.WARNING, "[Consumer] Database connection lost, reconnecting! SQLState: " + state); + // readd rows to the queue + synchronized (queue) { + while (!currentRows.isEmpty()) { + queue.addFirst(currentRows.remove(currentRows.size() - 1)); + } + } + } else { + logblock.getLogger().log(Level.SEVERE, "[Consumer] Could not insert entries! SQLState: " + state, e); + } + currentRows.clear(); + batchHelper.reset(); + uncommitedPlayerIds.clear(); + uncommitedEntityIds.clear(); + if (conn != null) { + try { + conn.close(); + } catch (SQLException e1) { + // ignore + } + } + conn = null; + } + } + if (conn != null) { + try { + conn.close(); + } catch (SQLException e1) { + // ignore + } + } + + // readd to queue - this can be saved later + synchronized (queue) { + while (!currentRows.isEmpty()) { + queue.addFirst(currentRows.remove(currentRows.size() - 1)); + } + } + } + + public void writeToFile() throws FileNotFoundException { + final long time = System.currentTimeMillis(); + final Set insertedPlayers = new HashSet<>(); + int counter = 0; + final File importDir = new File(logblock.getDataFolder(), "import"); + importDir.mkdirs(); + PrintWriter writer = new PrintWriter(new File(importDir, "queue-" + time + "-0.sql")); + while (!isQueueEmpty()) { + final Row r = pollQueueFirst(); + if (r == null) { + continue; + } + for (final Actor actor : r.getActors()) { + if (!playerIds.containsKey(actor) && !insertedPlayers.contains(actor)) { + // Odd query contruction is to work around innodb auto increment behaviour - bug #492 + writer.println("INSERT IGNORE INTO `lb-players` (playername,UUID) SELECT '" + mysqlTextEscape(actor.getName()) + "','" + mysqlTextEscape(actor.getUUID()) + "' FROM `lb-players` WHERE NOT EXISTS (SELECT NULL FROM `lb-players` WHERE UUID = '" + mysqlTextEscape(actor.getUUID()) + "') LIMIT 1;"); + insertedPlayers.add(actor); + } + } + for (final String insert : r.getInserts()) { + writer.println(insert); + } + counter++; + if (counter % 1000 == 0) { + writer.close(); + writer = new PrintWriter(new File(importDir, "queue-" + time + "-" + counter / 1000 + ".sql")); + } + } + writer.close(); + } + + int getQueueSize() { + synchronized (queue) { + return queue.size(); + } + } + + private boolean isQueueEmpty() { + synchronized (queue) { + return queue.isEmpty(); + } + } + + private void addQueueLast(Row row) { + synchronized (queue) { + boolean wasEmpty = queue.isEmpty(); + queue.addLast(row); + addEntryCounter++; + if (Config.queueWarningSize > 0 && queue.size() >= Config.queueWarningSize && addEntryCounter >= nextWarnCounter) { + logblock.getLogger().warning("[Consumer] Queue overloaded. Size: " + queue.size()); + nextWarnCounter = addEntryCounter + 1000; + } + if (wasEmpty) { + queue.notifyAll(); + } + } + } + + private Row pollQueueFirst() { + synchronized (queue) { + return queue.pollFirst(); + } + } + + static void hide(Player player) { + hiddenPlayers.add(player.getName().toLowerCase()); + } + + static void unHide(Player player) { + hiddenPlayers.remove(player.getName().toLowerCase()); + } + + static boolean toggleHide(Player player) { + final String playerName = player.getName().toLowerCase(); + if (hiddenPlayers.contains(playerName)) { + hiddenPlayers.remove(playerName); + return false; + } + hiddenPlayers.add(playerName); + return true; + } + + private boolean addPlayer(Connection conn, Actor actor) throws SQLException { + // Odd query contruction is to work around innodb auto increment behaviour - bug #492 + String name = actor.getName(); + String uuid = actor.getUUID(); + Statement state = conn.createStatement(); + String q1 = "INSERT IGNORE INTO `lb-players` (playername,UUID) SELECT '" + mysqlTextEscape(name) + "','" + mysqlTextEscape(uuid) + "' FROM `lb-players` WHERE NOT EXISTS (SELECT NULL FROM `lb-players` WHERE UUID = '" + mysqlTextEscape(uuid) + "') LIMIT 1"; + String q2 = "SELECT playerid FROM `lb-players` WHERE UUID = '" + mysqlTextEscape(uuid) + "'"; + int q1Result = state.executeUpdate(q1); + ResultSet rs = state.executeQuery(q2); + if (rs.next()) { + uncommitedPlayerIds.put(actor, rs.getInt(1)); + } + rs.close(); + if (!uncommitedPlayerIds.containsKey(actor)) { + state.executeUpdate("INSERT IGNORE INTO `lb-players` (playername,UUID) VALUES ('" + mysqlTextEscape(name) + "','" + mysqlTextEscape(uuid) + "')"); + rs = state.executeQuery(q2); + if (rs.next()) { + uncommitedPlayerIds.put(actor, rs.getInt(1)); + } else { + logblock.getLogger().warning("[Consumer] Failed to add player " + actor.getName()); + logblock.getLogger().warning("[Consumer-Debug] Query 1: " + q1); + logblock.getLogger().warning("[Consumer-Debug] Query 1 - Result: " + q1Result); + logblock.getLogger().warning("[Consumer-Debug] Query 2: " + q2); + } + rs.close(); + } + state.close(); + return uncommitedPlayerIds.containsKey(actor); + } + + private long getEntityUUID(Connection conn, World world, UUID uuid) throws SQLException { + Map uncommitedEntityIdsHere = uncommitedEntityIds.get(world); + if (uncommitedEntityIdsHere == null) { + uncommitedEntityIdsHere = new HashMap<>(); + uncommitedEntityIds.put(world, uncommitedEntityIdsHere); + } + Long existing = uncommitedEntityIdsHere.get(uuid); + if (existing != null) { + return existing; + } + + // Odd query contruction is to work around innodb auto increment behaviour - bug #492 + final String table = getWorldConfig(world).table; + String uuidString = uuid.toString(); + Statement state = conn.createStatement(); + String q1 = "INSERT IGNORE INTO `" + table + "-entityids` (entityuuid) SELECT '" + mysqlTextEscape(uuidString) + "' FROM `" + table + "-entityids` WHERE NOT EXISTS (SELECT NULL FROM `" + table + "-entityids` WHERE entityuuid = '" + mysqlTextEscape(uuidString) + "') LIMIT 1"; + String q2 = "SELECT entityid FROM `" + table + "-entityids` WHERE entityuuid = '" + mysqlTextEscape(uuidString) + "'"; + int q1Result = state.executeUpdate(q1); + ResultSet rs = state.executeQuery(q2); + if (rs.next()) { + uncommitedEntityIdsHere.put(uuid, rs.getLong(1)); + } + rs.close(); + // if there was not any row in the table the query above does not work, so we need to try this one + if (!uncommitedEntityIdsHere.containsKey(uuid)) { + state.executeUpdate("INSERT IGNORE INTO `" + table + "-entityids` (entityuuid) VALUES ('" + mysqlTextEscape(uuidString) + "')"); + rs = state.executeQuery(q2); + if (rs.next()) { + uncommitedEntityIdsHere.put(uuid, rs.getLong(1)); + } else { + logblock.getLogger().warning("[Consumer] Failed to add entity uuid " + uuidString.toString()); + logblock.getLogger().warning("[Consumer-Debug] World: " + world.getName()); + logblock.getLogger().warning("[Consumer-Debug] Query 1: " + q1); + logblock.getLogger().warning("[Consumer-Debug] Query 1 - Result: " + q1Result); + logblock.getLogger().warning("[Consumer-Debug] Query 2: " + q2); + } + rs.close(); + } + state.close(); + return uncommitedEntityIdsHere.get(uuid); + } + + private void queueBlock(Actor actor, Location loc, BlockData typeBefore, BlockData typeAfter, YamlConfiguration stateBefore, YamlConfiguration stateAfter, ChestAccess ca) { + if (typeBefore == null || typeBefore.getMaterial() == Material.CAVE_AIR || typeBefore.getMaterial() == Material.VOID_AIR) { + typeBefore = Bukkit.createBlockData(Material.AIR); + } + if (typeAfter == null && ((typeBefore instanceof Waterlogged && ((Waterlogged) typeBefore).isWaterlogged()) || BukkitUtils.isAlwaysWaterlogged(typeBefore.getMaterial()))) { + typeAfter = Bukkit.createBlockData(Material.WATER); + } + if (typeAfter == null || typeAfter.getMaterial() == Material.CAVE_AIR || typeAfter.getMaterial() == Material.VOID_AIR) { + typeAfter = Bukkit.createBlockData(Material.AIR); + } + if (BlockChangePreLogEvent.getHandlerList().getRegisteredListeners().length > 0) { + // Create and call the event + BlockChangePreLogEvent event = new BlockChangePreLogEvent(actor, loc, typeBefore, typeAfter, stateBefore, stateAfter, ca); + logblock.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return; + } + + // Update variables + actor = event.getOwnerActor(); + loc = event.getLocation(); + typeBefore = event.getTypeBefore(); + typeAfter = event.getTypeAfter(); + stateBefore = event.getStateBefore(); + stateAfter = event.getStateAfter(); + ca = event.getChestAccess(); + } + // Do this last so LogBlock still has final say in what is being added + if (actor == null || loc == null || typeBefore == null || typeAfter == null || hiddenPlayers.contains(actor.getName().toLowerCase()) || !isLogged(loc.getWorld()) || typeBefore != typeAfter && hiddenBlocks.contains(typeBefore.getMaterial()) && hiddenBlocks.contains(typeAfter.getMaterial())) { + return; + } + + int replacedMaterialId = MaterialConverter.getOrAddMaterialId(typeBefore); + int replacedStateId = MaterialConverter.getOrAddBlockStateId(typeBefore); + int typeMaterialId = MaterialConverter.getOrAddMaterialId(typeAfter); + int typeStateId = MaterialConverter.getOrAddBlockStateId(typeAfter); + + addQueueLast(new BlockRow(loc, actor, replacedMaterialId, replacedStateId, Utils.serializeYamlConfiguration(stateBefore), typeMaterialId, typeStateId, Utils.serializeYamlConfiguration(stateAfter), ca)); + } + + public void queueEntityModification(Actor actor, Entity entity, EntityChangeType changeType, YamlConfiguration data) { + if (actor == null || changeType == null || entity == null || hiddenPlayers.contains(actor.getName().toLowerCase()) || !isLogged(entity.getWorld())) { + return; + } + UUID entityId = entity.getUniqueId(); + EntityType entityType = entity.getType(); + Location loc = entity.getLocation(); + + if (EntityChangePreLogEvent.getHandlerList().getRegisteredListeners().length > 0) { + // Create and call the event + EntityChangePreLogEvent event = new EntityChangePreLogEvent(actor, loc, entity, changeType, data); + logblock.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return; + } + + // Update variables + actor = event.getOwnerActor(); + loc = event.getLocation(); + } + // Do this last so LogBlock still has final say in what is being added + if (actor == null || loc == null || hiddenPlayers.contains(actor.getName().toLowerCase()) || !isLogged(loc.getWorld())) { + return; + } + + addQueueLast(new EntityRow(loc, actor, entityType, entityId, changeType, Utils.serializeYamlConfiguration(data))); + } + + /** + * Change the UUID that is stored for an entity in the database. This is needed when an entity is respawned + * and now has a different UUID. + * + * @param world the world that contains the entity + * @param entityId the database id of the entity + * @param entityUUID the new UUID of the entity + */ + public void queueEntityUUIDChange(World world, int entityId, UUID entityUUID) { + addQueueLast(new EntityUUIDChange(world, entityId, entityUUID)); + } + + private String playerID(Actor actor) { + if (actor == null) { + return "NULL"; + } + final Integer id = playerIds.get(actor); + if (id != null) { + return id.toString(); + } + return "(SELECT playerid FROM `lb-players` WHERE UUID = '" + mysqlTextEscape(actor.getUUID()) + "')"; + } + + private Integer playerIDAsIntIncludeUncommited(Actor actor) { + if (actor == null) { + return null; + } + Integer id = playerIds.get(actor); + if (id != null) { + return id; + } + return uncommitedPlayerIds.get(actor); + } + + private static interface Row { + String[] getInserts(); + + void process(Connection conn, BatchHelper batchHelper) throws SQLException; + + Actor[] getActors(); + } + + private class BlockRow extends BlockChange implements Row { + final String statementString; + final String selectActorIdStatementString; + + public BlockRow(Location loc, Actor actor, int replaced, int replacedData, byte[] replacedState, int type, int typeData, byte[] typeState, ChestAccess ca) { + super(System.currentTimeMillis() / 1000, loc, actor, replaced, replacedData, replacedState, type, typeData, typeState, ca); + + statementString = getWorldConfig(loc.getWorld()).insertBlockStatementString; + selectActorIdStatementString = getWorldConfig(loc.getWorld()).selectBlockActorIdStatementString; + } + + @Override + public String[] getInserts() { + final String table = getWorldConfig(loc.getWorld()).table; + final String[] inserts = new String[ca != null || replacedState != null || typeState != null ? 2 : 1]; + + inserts[0] = "INSERT INTO `" + table + "-blocks` (date, playerid, replaced, replaceddata, type, typedata, x, y, z) VALUES (FROM_UNIXTIME(" + date + "), " + playerID(actor) + ", " + replacedMaterial + ", " + replacedData + ", " + typeMaterial + ", " + typeData + ", '" + loc.getBlockX() + + "', " + safeY(loc) + ", '" + loc.getBlockZ() + "');"; + if (replacedState != null || typeState != null) { + inserts[1] = "INSERT INTO `" + table + "-state` (replacedState, typeState, id) VALUES(" + Utils.mysqlPrepareBytesForInsertAllowNull(replacedState) + ", " + Utils.mysqlPrepareBytesForInsertAllowNull(typeState) + ", LAST_INSERT_ID());"; + } else if (ca != null) { + try { + inserts[1] = "INSERT INTO `" + table + "-chestdata` (id, item, itemremove, itemtype) values (LAST_INSERT_ID(), '" + Utils.mysqlEscapeBytes(Utils.saveItemStack(ca.itemStack)) + "', " + (ca.remove ? 1 : 0) + ", " + ca.itemType + ");"; + } catch (Exception e) { + LogBlock.getInstance().getLogger().log(Level.SEVERE, "Could not serialize ItemStack " + e.getMessage(), e); + LogBlock.getInstance().getLogger().log(Level.SEVERE, "Problematic row: " + toString()); + return new String[0]; + } + } + return inserts; + } + + @Override + public Actor[] getActors() { + return new Actor[] { actor }; + } + + @Override + public void process(Connection conn, BatchHelper batchHelper) throws SQLException { + byte[] serializedItemStack = null; + if (ca != null) { + try { + serializedItemStack = Utils.saveItemStack(ca.itemStack); + } catch (Exception e) { + LogBlock.getInstance().getLogger().log(Level.SEVERE, "Could not serialize ItemStack " + e.getMessage(), e); + LogBlock.getInstance().getLogger().log(Level.SEVERE, "Problematic row: " + toString()); + return; + } + } + final byte[] finalSerializedItemStack = serializedItemStack; + int sourceActor = playerIDAsIntIncludeUncommited(actor); + Location actorBlockLocation = actor.getBlockLocation(); + if (actorBlockLocation != null) { + Integer tempSourceActor = batchHelper.getUncommitedBlockActor(actorBlockLocation); + if (tempSourceActor != null) { + sourceActor = tempSourceActor; + } else { + PreparedStatement smt = batchHelper.getOrPrepareStatement(conn, selectActorIdStatementString, Statement.NO_GENERATED_KEYS); + smt.setInt(1, actorBlockLocation.getBlockX()); + smt.setInt(2, safeY(actorBlockLocation)); + smt.setInt(3, actorBlockLocation.getBlockZ()); + ResultSet rs = smt.executeQuery(); + if (rs.next()) { + sourceActor = rs.getInt(1); + } + rs.close(); + } + } + PreparedStatement smt = batchHelper.getOrPrepareStatement(conn, statementString, Statement.RETURN_GENERATED_KEYS); + smt.setLong(1, date); + smt.setInt(2, sourceActor); + smt.setInt(3, replacedMaterial); + smt.setInt(4, replacedData); + smt.setInt(5, typeMaterial); + smt.setInt(6, typeData); + smt.setInt(7, loc.getBlockX()); + smt.setInt(8, safeY(loc)); + smt.setInt(9, loc.getBlockZ()); + batchHelper.addUncommitedBlockActorId(loc, sourceActor); + batchHelper.addBatch(smt, new LongCallback() { + @Override + public void call(long id) throws SQLException { + PreparedStatement ps; + if (typeState != null || replacedState != null) { + ps = batchHelper.getOrPrepareStatement(conn, getWorldConfig(loc.getWorld()).insertBlockStateStatementString, Statement.NO_GENERATED_KEYS); + ps.setBytes(1, replacedState); + ps.setBytes(2, typeState); + ps.setLong(3, id); + batchHelper.addBatch(ps, null); + } + if (ca != null) { + ps = batchHelper.getOrPrepareStatement(conn, getWorldConfig(loc.getWorld()).insertBlockChestDataStatementString, Statement.NO_GENERATED_KEYS); + ps.setBytes(1, finalSerializedItemStack); + ps.setInt(2, ca.remove ? 1 : 0); + ps.setLong(3, id); + ps.setInt(4, ca.itemType); + batchHelper.addBatch(ps, null); + } + } + }); + } + } + + private class KillRow implements Row { + final long date; + final Actor killer, victim; + final int weapon; + final Location loc; + final String statementString; + + KillRow(Location loc, Actor attacker, Actor defender, int weapon) { + date = System.currentTimeMillis() / 1000; + this.loc = loc; + killer = attacker; + victim = defender; + this.weapon = weapon; + + statementString = "INSERT INTO `" + getWorldConfig(loc.getWorld()).table + "-kills` (date, killer, victim, weapon, x, y, z) VALUES (FROM_UNIXTIME(?), ?, ?, ?, ?, ?, ?)"; + } + + @Override + public String[] getInserts() { + return new String[] { "INSERT INTO `" + getWorldConfig(loc.getWorld()).table + "-kills` (date, killer, victim, weapon, x, y, z) VALUES (FROM_UNIXTIME(" + date + "), " + playerID(killer) + ", " + playerID(victim) + ", " + weapon + ", " + loc.getBlockX() + ", " + safeY(loc) + ", " + + loc.getBlockZ() + ");" }; + } + + @Override + public Actor[] getActors() { + return new Actor[] { killer, victim }; + } + + @Override + public void process(Connection conn, BatchHelper batchHelper) throws SQLException { + PreparedStatement smt = batchHelper.getOrPrepareStatement(conn, statementString, Statement.NO_GENERATED_KEYS); + smt.setLong(1, date); + smt.setInt(2, playerIDAsIntIncludeUncommited(killer)); + smt.setInt(3, playerIDAsIntIncludeUncommited(victim)); + smt.setInt(4, weapon); + smt.setInt(5, loc.getBlockX()); + smt.setInt(6, safeY(loc)); + smt.setInt(7, loc.getBlockZ()); + batchHelper.addBatch(smt, null); + } + } + + private class ChatRow extends ChatMessage implements Row { + private String statementString; + + ChatRow(Actor player, String message) { + super(player, message); + + statementString = "INSERT INTO `lb-chat` (date, playerid, message) VALUES (FROM_UNIXTIME(?), ?, ?)"; + } + + @Override + public String[] getInserts() { + return new String[] { "INSERT INTO `lb-chat` (date, playerid, message) VALUES (FROM_UNIXTIME(" + date + "), " + playerID(player) + ", '" + mysqlTextEscape(message) + "');" }; + } + + @Override + public Actor[] getActors() { + return new Actor[] { player }; + } + + @Override + public void process(Connection conn, BatchHelper batchHelper) throws SQLException { + PreparedStatement smt = batchHelper.getOrPrepareStatement(conn, statementString, Statement.NO_GENERATED_KEYS); + smt.setLong(1, date); + smt.setInt(2, playerIDAsIntIncludeUncommited(player)); + smt.setString(3, message); + batchHelper.addBatch(smt, null); + } + } + + private class PlayerJoinRow implements Row { + private final Actor player; + private final long lastLogin; + private final String ip; + private String statementString; + + PlayerJoinRow(Player player) { + this.player = Actor.actorFromEntity(player); + lastLogin = System.currentTimeMillis() / 1000; + ip = player.getAddress().toString().replace("'", "\\'"); + + if (logPlayerInfo) { + statementString = "UPDATE `lb-players` SET lastlogin = FROM_UNIXTIME(?), firstlogin = IF(firstlogin = 0, FROM_UNIXTIME(?), firstlogin), ip = ?, playername = ? WHERE UUID = ?"; + } else { + statementString = "UPDATE `lb-players` SET playername = ? WHERE UUID = ?"; + } + } + + @Override + public String[] getInserts() { + if (logPlayerInfo) { + return new String[] { + "UPDATE `lb-players` SET lastlogin = FROM_UNIXTIME(" + lastLogin + "), firstlogin = IF(firstlogin = 0, FROM_UNIXTIME(" + lastLogin + "), firstlogin), ip = '" + ip + "', playername = '" + mysqlTextEscape(player.getName()) + "' WHERE UUID = '" + player.getUUID() + "';" }; + } + return new String[] { "UPDATE `lb-players` SET playername = '" + mysqlTextEscape(player.getName()) + "' WHERE UUID = '" + mysqlTextEscape(player.getUUID()) + "';" }; + } + + @Override + public Actor[] getActors() { + return new Actor[] { player }; + } + + @Override + public void process(Connection conn, BatchHelper batchHelper) throws SQLException { + PreparedStatement smt = batchHelper.getOrPrepareStatement(conn, statementString, Statement.NO_GENERATED_KEYS); + if (logPlayerInfo) { + smt.setLong(1, lastLogin); + smt.setLong(2, lastLogin); + smt.setString(3, ip); + smt.setString(4, player.getName()); + smt.setString(5, player.getUUID()); + } else { + smt.setString(1, player.getName()); + smt.setString(2, player.getUUID()); + } + batchHelper.addBatch(smt, null); + } + } + + private class PlayerLeaveRow implements Row { + private final long onlineTime; + private final Actor actor; + private String statementString; + + PlayerLeaveRow(Player player, long onlineTime) { + this.onlineTime = onlineTime; + actor = Actor.actorFromEntity(player); + statementString = "UPDATE `lb-players` SET onlinetime = onlinetime + ? WHERE lastlogin > 0 && UUID = ?"; + } + + @Override + public String[] getInserts() { + if (logPlayerInfo) { + return new String[] { "UPDATE `lb-players` SET onlinetime = onlinetime + " + onlineTime + " WHERE lastlogin > 0 && UUID = '" + mysqlTextEscape(actor.getUUID()) + "';" }; + } + return new String[0]; + } + + @Override + public Actor[] getActors() { + return new Actor[] { actor }; + } + + @Override + public void process(Connection conn, BatchHelper batchHelper) throws SQLException { + PreparedStatement smt = batchHelper.getOrPrepareStatement(conn, statementString, Statement.NO_GENERATED_KEYS); + smt.setLong(1, onlineTime); + smt.setString(2, actor.getUUID()); + batchHelper.addBatch(smt, null); + } + } + + private class EntityRow extends EntityChange implements Row { + final String statementString; + final String selectActorIdStatementString; + + public EntityRow(Location loc, Actor actor, EntityType type, UUID entityid, EntityChangeType changeType, byte[] data) { + super(System.currentTimeMillis() / 1000, loc, actor, type, entityid, changeType, data); + statementString = getWorldConfig(loc.getWorld()).insertEntityStatementString; + selectActorIdStatementString = getWorldConfig(loc.getWorld()).selectBlockActorIdStatementString; + } + + @Override + public String[] getInserts() { + final String table = getWorldConfig(loc.getWorld()).table; + final String[] inserts = new String[2]; + + inserts[0] = "INSERT IGNORE INTO `" + table + "-entityids` (entityuuid) SELECT '" + mysqlTextEscape(entityUUID.toString()) + "' FROM `" + table + "-entityids` WHERE NOT EXISTS (SELECT NULL FROM `" + table + "-entityids` WHERE entityuuid = '" + mysqlTextEscape(entityUUID.toString()) + "') LIMIT 1"; + int entityTypeId = EntityTypeConverter.getOrAddEntityTypeId(type); + inserts[1] = "INSERT INTO `" + table + "-entities` (date, playerid, entityid, entitytypeid, x, y, z, action, data) VALUES (FROM_UNIXTIME(" + date + "), " + playerID(actor) + ", " + "(SELECT entityid FROM `" + table + "-entityids` WHERE entityuuid = '" + mysqlTextEscape(entityUUID.toString()) + "')" + + ", " + entityTypeId + ", '" + loc.getBlockX() + "', " + safeY(loc) + ", '" + loc.getBlockZ() + "', " + changeType.ordinal() + ", " + Utils.mysqlPrepareBytesForInsertAllowNull(data) + ");"; + return inserts; + } + + @Override + public Actor[] getActors() { + return new Actor[] { actor }; + } + + @Override + public void process(Connection conn, BatchHelper batchHelper) throws SQLException { + int sourceActor = playerIDAsIntIncludeUncommited(actor); + Location actorBlockLocation = actor.getBlockLocation(); + if (actorBlockLocation != null) { + Integer tempSourceActor = batchHelper.getUncommitedBlockActor(actorBlockLocation); + if (tempSourceActor != null) { + sourceActor = tempSourceActor; + } else { + PreparedStatement smt = batchHelper.getOrPrepareStatement(conn, selectActorIdStatementString, Statement.NO_GENERATED_KEYS); + smt.setInt(1, actorBlockLocation.getBlockX()); + smt.setInt(2, safeY(actorBlockLocation)); + smt.setInt(3, actorBlockLocation.getBlockZ()); + ResultSet rs = smt.executeQuery(); + if (rs.next()) { + sourceActor = rs.getInt(1); + } + rs.close(); + } + } + PreparedStatement smt = batchHelper.getOrPrepareStatement(conn, statementString, Statement.NO_GENERATED_KEYS); + smt.setLong(1, date); + smt.setInt(2, sourceActor); + smt.setLong(3, getEntityUUID(conn, loc.getWorld(), entityUUID)); + smt.setInt(4, EntityTypeConverter.getOrAddEntityTypeId(type)); + smt.setInt(5, loc.getBlockX()); + smt.setInt(6, safeY(loc)); + smt.setInt(7, loc.getBlockZ()); + smt.setInt(8, changeType.ordinal()); + smt.setBytes(9, data); + batchHelper.addBatch(smt, null); + } + } + + private class EntityUUIDChange implements Row { + private final World world; + private final long entityId; + private final UUID entityUUID; + final String updateEntityUUIDString; + + public EntityUUIDChange(World world, long entityId, UUID entityUUID) { + this.world = world; + this.entityId = entityId; + this.entityUUID = entityUUID; + updateEntityUUIDString = getWorldConfig(world).updateEntityUUIDString; + } + + @Override + public String[] getInserts() { + final String table = getWorldConfig(world).table; + final String[] inserts = new String[1]; + + inserts[0] = "UPDATE `" + table + "-entityids` SET entityuuid = '" + mysqlTextEscape(entityUUID.toString()) + "' WHERE entityid = " + entityId; + return inserts; + } + + @Override + public Actor[] getActors() { + return new Actor[0]; + } + + @Override + public void process(Connection conn, BatchHelper batchHelper) throws SQLException { + PreparedStatement smt = batchHelper.getOrPrepareStatement(conn, updateEntityUUIDString, Statement.NO_GENERATED_KEYS); + smt.setString(1, entityUUID.toString()); + smt.setLong(2, entityId); + smt.executeUpdate(); + } + } + + private int safeY(Location loc) { + int safeY = loc.getBlockY(); + if (safeY < Short.MIN_VALUE) { + safeY = Short.MIN_VALUE; + } + if (safeY > Short.MAX_VALUE) { + safeY = Short.MAX_VALUE; + } + return safeY; + } + + private class BatchHelper { + private HashMap preparedStatements = new HashMap<>(); + private HashSet preparedStatementsWithGeneratedKeys = new HashSet<>(); + private LinkedHashMap> generatedKeyHandler = new LinkedHashMap<>(); + private HashMap uncommitedBlockActors = new HashMap<>(); + + public void reset() { + preparedStatements.clear(); + preparedStatementsWithGeneratedKeys.clear(); + generatedKeyHandler.clear(); + uncommitedBlockActors.clear(); + } + + public void addUncommitedBlockActorId(Location loc, int actorId) { + uncommitedBlockActors.put(loc, actorId); + } + + public Integer getUncommitedBlockActor(Location loc) { + return uncommitedBlockActors.get(loc); + } + + public void processStatements(Connection conn) throws SQLException { + while (!generatedKeyHandler.isEmpty()) { + Entry> entry = generatedKeyHandler.entrySet().iterator().next(); + PreparedStatement smt = entry.getKey(); + ArrayList callbackList = entry.getValue(); + generatedKeyHandler.remove(smt); + smt.executeBatch(); + if (preparedStatementsWithGeneratedKeys.contains(smt)) { + ResultSet keys = smt.getGeneratedKeys(); + long[] results = new long[callbackList.size()]; + int pos = 0; + while (keys.next() && pos < results.length) { + results[pos++] = keys.getLong(1); + } + keys.close(); + for (int i = 0; i < results.length; i++) { + LongCallback callback = callbackList.get(i); + if (callback != null) { + callback.call(results[i]); + } + } + } + } + uncommitedBlockActors.clear(); + } + + public PreparedStatement getOrPrepareStatement(Connection conn, String sql, int autoGeneratedKeys) throws SQLException { + PreparedStatement smt = preparedStatements.get(sql); + if (smt == null) { + smt = conn.prepareStatement(sql, autoGeneratedKeys); + preparedStatements.put(sql, smt); + if (autoGeneratedKeys == Statement.RETURN_GENERATED_KEYS) { + preparedStatementsWithGeneratedKeys.add(smt); + } + } + return smt; + } + + public void addBatch(PreparedStatement smt, LongCallback generatedKeysCallback) throws SQLException { + smt.addBatch(); + ArrayList callbackList = generatedKeyHandler.get(smt); + if (callbackList == null) { + callbackList = new ArrayList<>(); + generatedKeyHandler.put(smt, callbackList); + } + callbackList.add(generatedKeysCallback); + } + } + + protected interface LongCallback { + public void call(long value) throws SQLException; + } +} diff --git a/src/main/java/de/diddiz/LogBlock/DumpedLogImporter.java b/src/main/java/de/diddiz/LogBlock/DumpedLogImporter.java index f430bb4..0a0d9d2 100644 --- a/src/main/java/de/diddiz/LogBlock/DumpedLogImporter.java +++ b/src/main/java/de/diddiz/LogBlock/DumpedLogImporter.java @@ -1,147 +1,147 @@ -package de.diddiz.LogBlock; - -import static de.diddiz.LogBlock.util.Utils.newline; - -import de.diddiz.LogBlock.util.Utils.ExtensionFilenameFilter; -import java.io.*; -import java.sql.Connection; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.Arrays; -import java.util.Comparator; -import java.util.logging.Level; -import java.util.regex.Pattern; - -public class DumpedLogImporter implements Runnable { - private final LogBlock logblock; - - DumpedLogImporter(LogBlock logblock) { - this.logblock = logblock; - } - - @Override - public void run() { - final File[] imports = new File(logblock.getDataFolder(), "import").listFiles(new ExtensionFilenameFilter("sql")); - if (imports != null && imports.length > 0) { - logblock.getLogger().info("Found " + imports.length + " imports."); - Arrays.sort(imports, new ImportsComparator()); - Connection conn = null; - try { - conn = logblock.getConnection(); - if (conn == null) { - return; - } - conn.setAutoCommit(false); - final Statement st = conn.createStatement(); - final BufferedWriter writer = new BufferedWriter(new FileWriter(new File(logblock.getDataFolder(), "import/failed.txt"))); - int successes = 0, errors = 0; - try { - for (final File sqlFile : imports) { - String line = null; - try { - logblock.getLogger().info("Trying to import " + sqlFile.getName() + " ..."); - // first try batch import the whole file - final BufferedReader reader = new BufferedReader(new FileReader(sqlFile)); - int statements = 0; - while ((line = reader.readLine()) != null) { - if (line.endsWith(";")) { - line = line.substring(0, line.length() - 1); - } - if (!line.isEmpty()) { - statements++; - st.addBatch(line); - } - } - st.executeBatch(); - conn.commit(); - reader.close(); - sqlFile.delete(); - successes += statements; - logblock.getLogger().info("Successfully imported " + sqlFile.getName() + "."); - } catch (final Exception ignored) { - // if the batch import did not work, retry line by line - try { - final BufferedReader reader = new BufferedReader(new FileReader(sqlFile)); - while ((line = reader.readLine()) != null) { - if (line.endsWith(";")) { - line = line.substring(0, line.length() - 1); - } - if (!line.isEmpty()) { - try { - st.execute(line); - successes++; - } catch (final SQLException ex) { - logblock.getLogger().severe("Error while importing: '" + line + "': " + ex.getMessage()); - writer.write(line + newline); - errors++; - } - } - } - conn.commit(); - reader.close(); - sqlFile.delete(); - logblock.getLogger().info("Successfully imported " + sqlFile.getName() + "."); - } catch (final Exception ex) { - logblock.getLogger().severe("Error while importing " + sqlFile.getName() + ": " + ex.getMessage()); - errors++; - } - } - } - } finally { - writer.close(); - } - st.close(); - logblock.getLogger().info("Successfully imported stored queue. (" + successes + " rows imported, " + errors + " errors)"); - } catch (final Exception ex) { - logblock.getLogger().log(Level.WARNING, "Error while importing: ", ex); - } finally { - if (conn != null) { - try { - conn.close(); - } catch (final SQLException ex) { - } - } - } - } - } - - private static class ImportsComparator implements Comparator { - private final Pattern splitPattern = Pattern.compile("[\\-\\.]"); - - @Override - public int compare(File o1, File o2) { - String[] name1 = splitPattern.split(o1.getName()); - String[] name2 = splitPattern.split(o2.getName()); - if (name1.length > name2.length) { - return 1; - } else if (name1.length < name2.length) { - return -1; - } - for (int i = 0; i < name1.length; i++) { - String part1 = name1[i]; - String part2 = name2[i]; - if (part1.length() > 0 && part2.length() > 0) { - char first1 = part1.charAt(0); - char first2 = part2.charAt(0); - if (first1 >= '0' && first1 <= '9' && first2 >= '0' && first2 <= '9') { - try { - long long1 = Long.parseLong(part1); - long long2 = Long.parseLong(part2); - if (long1 == long2) { - continue; - } - return long1 > long2 ? 1 : -1; - } catch (NumberFormatException e) { - // fallthrough to string compare - } - } - } - int compareString = part1.compareTo(part2); - if (compareString != 0) { - return compareString; - } - } - return 0; - } - } -} +package de.diddiz.LogBlock; + +import static de.diddiz.LogBlock.util.Utils.newline; + +import de.diddiz.LogBlock.util.Utils.ExtensionFilenameFilter; +import java.io.*; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Arrays; +import java.util.Comparator; +import java.util.logging.Level; +import java.util.regex.Pattern; + +public class DumpedLogImporter implements Runnable { + private final LogBlock logblock; + + DumpedLogImporter(LogBlock logblock) { + this.logblock = logblock; + } + + @Override + public void run() { + final File[] imports = new File(logblock.getDataFolder(), "import").listFiles(new ExtensionFilenameFilter("sql")); + if (imports != null && imports.length > 0) { + logblock.getLogger().info("Found " + imports.length + " imports."); + Arrays.sort(imports, new ImportsComparator()); + Connection conn = null; + try { + conn = logblock.getConnection(); + if (conn == null) { + return; + } + conn.setAutoCommit(false); + final Statement st = conn.createStatement(); + final BufferedWriter writer = new BufferedWriter(new FileWriter(new File(logblock.getDataFolder(), "import/failed.txt"))); + int successes = 0, errors = 0; + try { + for (final File sqlFile : imports) { + String line = null; + try { + logblock.getLogger().info("Trying to import " + sqlFile.getName() + " ..."); + // first try batch import the whole file + final BufferedReader reader = new BufferedReader(new FileReader(sqlFile)); + int statements = 0; + while ((line = reader.readLine()) != null) { + if (line.endsWith(";")) { + line = line.substring(0, line.length() - 1); + } + if (!line.isEmpty()) { + statements++; + st.addBatch(line); + } + } + st.executeBatch(); + conn.commit(); + reader.close(); + sqlFile.delete(); + successes += statements; + logblock.getLogger().info("Successfully imported " + sqlFile.getName() + "."); + } catch (final Exception ignored) { + // if the batch import did not work, retry line by line + try { + final BufferedReader reader = new BufferedReader(new FileReader(sqlFile)); + while ((line = reader.readLine()) != null) { + if (line.endsWith(";")) { + line = line.substring(0, line.length() - 1); + } + if (!line.isEmpty()) { + try { + st.execute(line); + successes++; + } catch (final SQLException ex) { + logblock.getLogger().severe("Error while importing: '" + line + "': " + ex.getMessage()); + writer.write(line + newline); + errors++; + } + } + } + conn.commit(); + reader.close(); + sqlFile.delete(); + logblock.getLogger().info("Successfully imported " + sqlFile.getName() + "."); + } catch (final Exception ex) { + logblock.getLogger().severe("Error while importing " + sqlFile.getName() + ": " + ex.getMessage()); + errors++; + } + } + } + } finally { + writer.close(); + } + st.close(); + logblock.getLogger().info("Successfully imported stored queue. (" + successes + " rows imported, " + errors + " errors)"); + } catch (final Exception ex) { + logblock.getLogger().log(Level.WARNING, "Error while importing: ", ex); + } finally { + if (conn != null) { + try { + conn.close(); + } catch (final SQLException ex) { + } + } + } + } + } + + private static class ImportsComparator implements Comparator { + private final Pattern splitPattern = Pattern.compile("[\\-\\.]"); + + @Override + public int compare(File o1, File o2) { + String[] name1 = splitPattern.split(o1.getName()); + String[] name2 = splitPattern.split(o2.getName()); + if (name1.length > name2.length) { + return 1; + } else if (name1.length < name2.length) { + return -1; + } + for (int i = 0; i < name1.length; i++) { + String part1 = name1[i]; + String part2 = name2[i]; + if (part1.length() > 0 && part2.length() > 0) { + char first1 = part1.charAt(0); + char first2 = part2.charAt(0); + if (first1 >= '0' && first1 <= '9' && first2 >= '0' && first2 <= '9') { + try { + long long1 = Long.parseLong(part1); + long long2 = Long.parseLong(part2); + if (long1 == long2) { + continue; + } + return long1 > long2 ? 1 : -1; + } catch (NumberFormatException e) { + // fallthrough to string compare + } + } + } + int compareString = part1.compareTo(part2); + if (compareString != 0) { + return compareString; + } + } + return 0; + } + } +} diff --git a/src/main/java/de/diddiz/LogBlock/LogBlock.java b/src/main/java/de/diddiz/LogBlock/LogBlock.java index 4ea95c6..212129f 100644 --- a/src/main/java/de/diddiz/LogBlock/LogBlock.java +++ b/src/main/java/de/diddiz/LogBlock/LogBlock.java @@ -1,378 +1,378 @@ -package de.diddiz.LogBlock; - -import de.diddiz.LogBlock.addons.worldguard.WorldGuardLoggingFlagsAddon; -import de.diddiz.LogBlock.config.Config; -import de.diddiz.LogBlock.listeners.*; -import de.diddiz.LogBlock.questioner.Questioner; -import de.diddiz.LogBlock.util.BukkitUtils; -import de.diddiz.LogBlock.util.MySQLConnectionPool; -import de.diddiz.LogBlock.worldedit.WorldEditHelper; -import de.diddiz.LogBlock.worldedit.WorldEditLoggingHook; -import org.bukkit.ChatColor; -import org.bukkit.Material; -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.bukkit.permissions.Permission; -import org.bukkit.plugin.PluginManager; -import org.bukkit.plugin.java.JavaPlugin; - -import java.io.File; -import java.io.FileNotFoundException; -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; - -import static de.diddiz.LogBlock.config.Config.*; -import static org.bukkit.Bukkit.getPluginManager; - -public class LogBlock extends JavaPlugin { - private static LogBlock logblock = null; - private MySQLConnectionPool pool; - private Consumer consumer = null; - private CommandsHandler commandsHandler; - private boolean noDb = false, connected = true; - private PlayerInfoLogging playerInfoLogging; - private ScaffoldingLogging scaffoldingLogging; - private Questioner questioner; - private WorldGuardLoggingFlagsAddon worldGuardLoggingFlagsAddon; - private boolean isConfigLoaded; - private volatile boolean isCompletelyEnabled; - - public static LogBlock getInstance() { - return logblock; - } - - public boolean isCompletelyEnabled() { - return isCompletelyEnabled; - } - - public Consumer getConsumer() { - return consumer; - } - - public CommandsHandler getCommandsHandler() { - return commandsHandler; - } - - @Override - public void onLoad() { - logblock = this; - BukkitUtils.isDoublePlant(Material.AIR); // Force static code to run - try { - Config.load(this); - isConfigLoaded = true; - } catch (final Exception ex) { - getLogger().log(Level.SEVERE, "Could not load LogBlock config! " + ex.getMessage(), ex); - } - if (Config.worldGuardLoggingFlags) { - if (getServer().getPluginManager().getPlugin("WorldGuard") == null) { - getLogger().log(Level.SEVERE, "Invalid config! addons.worldguardLoggingFlags is set to true, but WorldGuard is not loaded."); - } else { - worldGuardLoggingFlagsAddon = new WorldGuardLoggingFlagsAddon(this); - worldGuardLoggingFlagsAddon.onPluginLoad(); - } - } - } - - @Override - public void onEnable() { - final PluginManager pm = getPluginManager(); - if (!isConfigLoaded) { - pm.disablePlugin(this); - return; - } - consumer = new Consumer(this); - try { - getLogger().info("Connecting to " + user + "@" + url + "..."); - try { - Class.forName("com.mysql.cj.jdbc.Driver"); - } catch (ClassNotFoundException ignored) { - Class.forName("com.mysql.jdbc.Driver"); - } - pool = new MySQLConnectionPool(url, user, password, mysqlUseSSL, mysqlRequireSSL); - final Connection conn = getConnection(true); - if (conn == null) { - noDb = true; - return; - } - final Statement st = conn.createStatement(); - final ResultSet rs = st.executeQuery("SHOW CHARACTER SET where charset='utf8mb4';"); - if (rs.next()) { - Config.mb4 = true; - // Allegedly JDBC driver since 2010 hasn't needed this. I did. - st.executeUpdate("SET NAMES utf8mb4;"); - } - conn.close(); - Updater updater = new Updater(this); - updater.checkTables(); - MaterialConverter.initializeMaterials(getConnection()); - MaterialConverter.getOrAddMaterialId(Material.AIR); // AIR must be the first entry - EntityTypeConverter.initializeEntityTypes(getConnection()); - if (updater.update()) { - load(this); - } - } catch (final NullPointerException ex) { - getLogger().log(Level.SEVERE, "Error while loading: ", ex); - } catch (final Exception ex) { - getLogger().log(Level.SEVERE, "Error while loading: " + ex.getMessage(), ex); - pm.disablePlugin(this); - return; - } - - if (WorldEditHelper.hasWorldEdit()) { - new WorldEditLoggingHook(this).hook(); - } - commandsHandler = new CommandsHandler(this); - getCommand("lb").setExecutor(commandsHandler); - if (enableAutoClearLog && autoClearLogDelay > 0) { - getServer().getScheduler().runTaskTimerAsynchronously(this, new AutoClearLog(this), 6000, autoClearLogDelay * 60 * 20); - } - new DumpedLogImporter(this).run(); - registerEvents(); - consumer.start(); - for (final Tool tool : toolsByType.values()) { - if (pm.getPermission("logblock.tools." + tool.name) == null) { - final Permission perm = new Permission("logblock.tools." + tool.name, tool.permissionDefault); - pm.addPermission(perm); - } - } - questioner = new Questioner(this); - if (worldGuardLoggingFlagsAddon != null) { - worldGuardLoggingFlagsAddon.onPluginEnable(); - } - isCompletelyEnabled = true; - getServer().getScheduler().runTaskAsynchronously(this, new Updater.PlayerCountChecker(this)); - } - - private void registerEvents() { - final PluginManager pm = getPluginManager(); - pm.registerEvents(new ToolListener(this), this); - pm.registerEvents(playerInfoLogging = new PlayerInfoLogging(this), this); - if (askRollbackAfterBan) { - pm.registerEvents(new BanListener(this), this); - } - if (isLogging(Logging.BLOCKPLACE)) { - pm.registerEvents(new BlockPlaceLogging(this), this); - } - if (isLogging(Logging.LAVAFLOW) || isLogging(Logging.WATERFLOW)) { - pm.registerEvents(new FluidFlowLogging(this), this); - } - if (isLogging(Logging.BLOCKBREAK)) { - pm.registerEvents(new BlockBreakLogging(this), this); - } - if (isLogging(Logging.SIGNTEXT)) { - pm.registerEvents(new SignChangeLogging(this), this); - } - if (isLogging(Logging.FIRE)) { - pm.registerEvents(new BlockBurnLogging(this), this); - } - if (isLogging(Logging.SNOWFORM)) { - pm.registerEvents(new SnowFormLogging(this), this); - } - if (isLogging(Logging.SNOWFADE)) { - pm.registerEvents(new SnowFadeLogging(this), this); - } - if (isLogging(Logging.SCAFFOLDING)) { - pm.registerEvents(scaffoldingLogging = new ScaffoldingLogging(this), this); - } - if (isLogging(Logging.CAULDRONINTERACT)) { - pm.registerEvents(new CauldronLogging(this), this); - } - if (isLogging(Logging.CREEPEREXPLOSION) || isLogging(Logging.TNTEXPLOSION) || isLogging(Logging.GHASTFIREBALLEXPLOSION) || isLogging(Logging.ENDERDRAGON) || isLogging(Logging.MISCEXPLOSION)) { - pm.registerEvents(new ExplosionLogging(this), this); - } - if (isLogging(Logging.LEAVESDECAY)) { - pm.registerEvents(new LeavesDecayLogging(this), this); - } - if (isLogging(Logging.CHESTACCESS)) { - pm.registerEvents(new ChestAccessLogging(this), this); - } - if (isLogging(Logging.BLOCKBREAK) || isLogging(Logging.BLOCKPLACE) || isLogging(Logging.SWITCHINTERACT) || isLogging(Logging.DOORINTERACT) || isLogging(Logging.CAKEEAT) || isLogging(Logging.DIODEINTERACT) || isLogging(Logging.COMPARATORINTERACT) || isLogging(Logging.NOTEBLOCKINTERACT) - || isLogging(Logging.PRESUREPLATEINTERACT) || isLogging(Logging.TRIPWIREINTERACT) || isLogging(Logging.CROPTRAMPLE)) { - pm.registerEvents(new InteractLogging(this), this); - } - if (isLogging(Logging.CREATURECROPTRAMPLE)) { - pm.registerEvents(new CreatureInteractLogging(this), this); - } - if (isLogging(Logging.KILL)) { - pm.registerEvents(new KillLogging(this), this); - } - if (isLogging(Logging.CHAT) || isLogging(Logging.PLAYER_COMMANDS) || isLogging(Logging.CONSOLE_COMMANDS) || isLogging(Logging.COMMANDBLOCK_COMMANDS)) { - pm.registerEvents(new ChatLogging(this), this); - } - if (isLogging(Logging.ENDERMEN)) { - pm.registerEvents(new EndermenLogging(this), this); - } - if (isLogging(Logging.WITHER)) { - pm.registerEvents(new WitherLogging(this), this); - } - if (isLogging(Logging.NATURALSTRUCTUREGROW)) { - pm.registerEvents(new StructureGrowLogging(this), this); - } - if (isLogging(Logging.BONEMEALSTRUCTUREGROW)) { - pm.registerEvents(new BlockFertilizeLogging(this), this); - } - if (isLogging(Logging.GRASSGROWTH) || isLogging(Logging.MYCELIUMSPREAD) || isLogging(Logging.VINEGROWTH) || isLogging(Logging.MUSHROOMSPREAD) || isLogging(Logging.BAMBOOGROWTH) || isLogging(Logging.DRIPSTONEGROWTH) || isLogging(Logging.SCULKSPREAD)) { - pm.registerEvents(new BlockSpreadLogging(this), this); - } - if (isLogging(Logging.DRAGONEGGTELEPORT)) { - pm.registerEvents(new DragonEggLogging(this), this); - } - if (isLogging(Logging.LECTERNBOOKCHANGE)) { - pm.registerEvents(new LecternLogging(this), this); - } - if (isLogging(Logging.OXIDIZATION)) { - pm.registerEvents(new OxidizationLogging(this), this); - } - if (Config.isLoggingAnyEntities()) { - if (!WorldEditHelper.hasFullWorldEdit()) { - getLogger().severe("No compatible WorldEdit found, entity logging will not work!"); - } else { - pm.registerEvents(new AdvancedEntityLogging(this), this); - getLogger().info("Entity logging enabled!"); - } - } - } - - @Override - public void onDisable() { - isCompletelyEnabled = false; - getServer().getScheduler().cancelTasks(this); - if (consumer != null) { - if (logPlayerInfo && playerInfoLogging != null) { - for (final Player player : getServer().getOnlinePlayers()) { - playerInfoLogging.onPlayerQuit(player); - } - } - getLogger().info("Waiting for consumer ..."); - consumer.shutdown(); - if (consumer.getQueueSize() > 0) { - getLogger().info("Remaining queue size: " + consumer.getQueueSize() + ". Trying to write to a local file."); - try { - consumer.writeToFile(); - getLogger().info("Successfully dumped queue."); - } catch (final FileNotFoundException ex) { - getLogger().info("Failed to write. Given up."); - } - } - } - if (pool != null) { - pool.close(); - } - } - - @Override - public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args) { - if (noDb) { - sender.sendMessage(ChatColor.RED + "No database connected. Check your MySQL user/pw and database for typos. Start/restart your MySQL server."); - } - return true; - } - - public boolean hasPermission(CommandSender sender, String permission) { - return sender.hasPermission(permission); - } - - public Connection getConnection() { - return getConnection(false); - } - - public Connection getConnection(boolean testConnection) { - try { - final Connection conn = pool.getConnection(); - if (!connected) { - getLogger().info("MySQL connection rebuild"); - connected = true; - } - return conn; - } catch (final Exception ex) { - if (testConnection) { - getLogger().log(Level.SEVERE, "Could not connect to the Database! Please check your config! " + ex.getMessage()); - } else if (connected) { - getLogger().log(Level.SEVERE, "Error while fetching connection: ", ex); - connected = false; - } else { - getLogger().log(Level.SEVERE, "MySQL connection lost", ex); - } - return null; - } - } - - /** - * Returns a list of block changes based on the given query parameters, the query parameters - * are essentially programmatic versions of the parameters a player would pass - * to the logblock lookup command i.e /lb lookup query-parameters - * - * Note: this method directly calls a SQL query and is hence a slow blocking function, avoid running - * it on the main game thread - * - * @param params QueryParams that contains the needed columns (all other will be filled with default values) and the params. World is required. - * @return Returns a list of block changes based on the given query parameters - * @throws SQLException if a sql exception occurs while looking up the block changes - */ - public List getBlockChanges(QueryParams params) throws SQLException { - final Connection conn = getConnection(); - Statement state = null; - if (conn == null) { - throw new SQLException("No connection"); - } - try { - state = conn.createStatement(); - final ResultSet rs = state.executeQuery(params.getQuery()); - final List blockchanges = new ArrayList<>(); - while (rs.next()) { - blockchanges.add(new BlockChange(rs, params)); - } - return blockchanges; - } finally { - if (state != null) { - state.close(); - } - conn.close(); - } - } - - public int getCount(QueryParams params) throws SQLException { - if (params == null || params.world == null || !Config.isLogged(params.world)) { - throw new IllegalArgumentException("World is not logged: " + ((params == null || params.world == null) ? "null" : params.world.getName())); - } - final Connection conn = getConnection(); - Statement state = null; - if (conn == null) { - throw new SQLException("No connection"); - } - try { - state = conn.createStatement(); - final QueryParams p = params.clone(); - p.needCount = true; - final ResultSet rs = state.executeQuery(p.getQuery()); - if (!rs.next()) { - return 0; - } - return rs.getInt(1); - } finally { - if (state != null) { - state.close(); - } - conn.close(); - } - } - - @Override - public File getFile() { - return super.getFile(); - } - - public Questioner getQuestioner() { - return questioner; - } - - public ScaffoldingLogging getScaffoldingLogging() { - return scaffoldingLogging; - } -} +package de.diddiz.LogBlock; + +import de.diddiz.LogBlock.addons.worldguard.WorldGuardLoggingFlagsAddon; +import de.diddiz.LogBlock.config.Config; +import de.diddiz.LogBlock.listeners.*; +import de.diddiz.LogBlock.questioner.Questioner; +import de.diddiz.LogBlock.util.BukkitUtils; +import de.diddiz.LogBlock.util.MySQLConnectionPool; +import de.diddiz.LogBlock.worldedit.WorldEditHelper; +import de.diddiz.LogBlock.worldedit.WorldEditLoggingHook; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.permissions.Permission; +import org.bukkit.plugin.PluginManager; +import org.bukkit.plugin.java.JavaPlugin; + +import java.io.File; +import java.io.FileNotFoundException; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; + +import static de.diddiz.LogBlock.config.Config.*; +import static org.bukkit.Bukkit.getPluginManager; + +public class LogBlock extends JavaPlugin { + private static LogBlock logblock = null; + private MySQLConnectionPool pool; + private Consumer consumer = null; + private CommandsHandler commandsHandler; + private boolean noDb = false, connected = true; + private PlayerInfoLogging playerInfoLogging; + private ScaffoldingLogging scaffoldingLogging; + private Questioner questioner; + private WorldGuardLoggingFlagsAddon worldGuardLoggingFlagsAddon; + private boolean isConfigLoaded; + private volatile boolean isCompletelyEnabled; + + public static LogBlock getInstance() { + return logblock; + } + + public boolean isCompletelyEnabled() { + return isCompletelyEnabled; + } + + public Consumer getConsumer() { + return consumer; + } + + public CommandsHandler getCommandsHandler() { + return commandsHandler; + } + + @Override + public void onLoad() { + logblock = this; + BukkitUtils.isDoublePlant(Material.AIR); // Force static code to run + try { + Config.load(this); + isConfigLoaded = true; + } catch (final Exception ex) { + getLogger().log(Level.SEVERE, "Could not load LogBlock config! " + ex.getMessage(), ex); + } + if (Config.worldGuardLoggingFlags) { + if (getServer().getPluginManager().getPlugin("WorldGuard") == null) { + getLogger().log(Level.SEVERE, "Invalid config! addons.worldguardLoggingFlags is set to true, but WorldGuard is not loaded."); + } else { + worldGuardLoggingFlagsAddon = new WorldGuardLoggingFlagsAddon(this); + worldGuardLoggingFlagsAddon.onPluginLoad(); + } + } + } + + @Override + public void onEnable() { + final PluginManager pm = getPluginManager(); + if (!isConfigLoaded) { + pm.disablePlugin(this); + return; + } + consumer = new Consumer(this); + try { + getLogger().info("Connecting to " + user + "@" + url + "..."); + try { + Class.forName("com.mysql.cj.jdbc.Driver"); + } catch (ClassNotFoundException ignored) { + Class.forName("com.mysql.jdbc.Driver"); + } + pool = new MySQLConnectionPool(url, user, password, mysqlUseSSL, mysqlRequireSSL); + final Connection conn = getConnection(true); + if (conn == null) { + noDb = true; + return; + } + final Statement st = conn.createStatement(); + final ResultSet rs = st.executeQuery("SHOW CHARACTER SET where charset='utf8mb4';"); + if (rs.next()) { + Config.mb4 = true; + // Allegedly JDBC driver since 2010 hasn't needed this. I did. + st.executeUpdate("SET NAMES utf8mb4;"); + } + conn.close(); + Updater updater = new Updater(this); + updater.checkTables(); + MaterialConverter.initializeMaterials(getConnection()); + MaterialConverter.getOrAddMaterialId(Material.AIR); // AIR must be the first entry + EntityTypeConverter.initializeEntityTypes(getConnection()); + if (updater.update()) { + load(this); + } + } catch (final NullPointerException ex) { + getLogger().log(Level.SEVERE, "Error while loading: ", ex); + } catch (final Exception ex) { + getLogger().log(Level.SEVERE, "Error while loading: " + ex.getMessage(), ex); + pm.disablePlugin(this); + return; + } + + if (WorldEditHelper.hasWorldEdit()) { + new WorldEditLoggingHook(this).hook(); + } + commandsHandler = new CommandsHandler(this); + getCommand("lb").setExecutor(commandsHandler); + if (enableAutoClearLog && autoClearLogDelay > 0) { + getServer().getScheduler().runTaskTimerAsynchronously(this, new AutoClearLog(this), 6000, autoClearLogDelay * 60 * 20); + } + new DumpedLogImporter(this).run(); + registerEvents(); + consumer.start(); + for (final Tool tool : toolsByType.values()) { + if (pm.getPermission("logblock.tools." + tool.name) == null) { + final Permission perm = new Permission("logblock.tools." + tool.name, tool.permissionDefault); + pm.addPermission(perm); + } + } + questioner = new Questioner(this); + if (worldGuardLoggingFlagsAddon != null) { + worldGuardLoggingFlagsAddon.onPluginEnable(); + } + isCompletelyEnabled = true; + getServer().getScheduler().runTaskAsynchronously(this, new Updater.PlayerCountChecker(this)); + } + + private void registerEvents() { + final PluginManager pm = getPluginManager(); + pm.registerEvents(new ToolListener(this), this); + pm.registerEvents(playerInfoLogging = new PlayerInfoLogging(this), this); + if (askRollbackAfterBan) { + pm.registerEvents(new BanListener(this), this); + } + if (isLogging(Logging.BLOCKPLACE)) { + pm.registerEvents(new BlockPlaceLogging(this), this); + } + if (isLogging(Logging.LAVAFLOW) || isLogging(Logging.WATERFLOW)) { + pm.registerEvents(new FluidFlowLogging(this), this); + } + if (isLogging(Logging.BLOCKBREAK)) { + pm.registerEvents(new BlockBreakLogging(this), this); + } + if (isLogging(Logging.SIGNTEXT)) { + pm.registerEvents(new SignChangeLogging(this), this); + } + if (isLogging(Logging.FIRE)) { + pm.registerEvents(new BlockBurnLogging(this), this); + } + if (isLogging(Logging.SNOWFORM)) { + pm.registerEvents(new SnowFormLogging(this), this); + } + if (isLogging(Logging.SNOWFADE)) { + pm.registerEvents(new SnowFadeLogging(this), this); + } + if (isLogging(Logging.SCAFFOLDING)) { + pm.registerEvents(scaffoldingLogging = new ScaffoldingLogging(this), this); + } + if (isLogging(Logging.CAULDRONINTERACT)) { + pm.registerEvents(new CauldronLogging(this), this); + } + if (isLogging(Logging.CREEPEREXPLOSION) || isLogging(Logging.TNTEXPLOSION) || isLogging(Logging.GHASTFIREBALLEXPLOSION) || isLogging(Logging.ENDERDRAGON) || isLogging(Logging.MISCEXPLOSION)) { + pm.registerEvents(new ExplosionLogging(this), this); + } + if (isLogging(Logging.LEAVESDECAY)) { + pm.registerEvents(new LeavesDecayLogging(this), this); + } + if (isLogging(Logging.CHESTACCESS)) { + pm.registerEvents(new ChestAccessLogging(this), this); + } + if (isLogging(Logging.BLOCKBREAK) || isLogging(Logging.BLOCKPLACE) || isLogging(Logging.SWITCHINTERACT) || isLogging(Logging.DOORINTERACT) || isLogging(Logging.CAKEEAT) || isLogging(Logging.DIODEINTERACT) || isLogging(Logging.COMPARATORINTERACT) || isLogging(Logging.NOTEBLOCKINTERACT) + || isLogging(Logging.PRESUREPLATEINTERACT) || isLogging(Logging.TRIPWIREINTERACT) || isLogging(Logging.CROPTRAMPLE)) { + pm.registerEvents(new InteractLogging(this), this); + } + if (isLogging(Logging.CREATURECROPTRAMPLE)) { + pm.registerEvents(new CreatureInteractLogging(this), this); + } + if (isLogging(Logging.KILL)) { + pm.registerEvents(new KillLogging(this), this); + } + if (isLogging(Logging.CHAT) || isLogging(Logging.PLAYER_COMMANDS) || isLogging(Logging.CONSOLE_COMMANDS) || isLogging(Logging.COMMANDBLOCK_COMMANDS)) { + pm.registerEvents(new ChatLogging(this), this); + } + if (isLogging(Logging.ENDERMEN)) { + pm.registerEvents(new EndermenLogging(this), this); + } + if (isLogging(Logging.WITHER)) { + pm.registerEvents(new WitherLogging(this), this); + } + if (isLogging(Logging.NATURALSTRUCTUREGROW)) { + pm.registerEvents(new StructureGrowLogging(this), this); + } + if (isLogging(Logging.BONEMEALSTRUCTUREGROW)) { + pm.registerEvents(new BlockFertilizeLogging(this), this); + } + if (isLogging(Logging.GRASSGROWTH) || isLogging(Logging.MYCELIUMSPREAD) || isLogging(Logging.VINEGROWTH) || isLogging(Logging.MUSHROOMSPREAD) || isLogging(Logging.BAMBOOGROWTH) || isLogging(Logging.DRIPSTONEGROWTH) || isLogging(Logging.SCULKSPREAD)) { + pm.registerEvents(new BlockSpreadLogging(this), this); + } + if (isLogging(Logging.DRAGONEGGTELEPORT)) { + pm.registerEvents(new DragonEggLogging(this), this); + } + if (isLogging(Logging.LECTERNBOOKCHANGE)) { + pm.registerEvents(new LecternLogging(this), this); + } + if (isLogging(Logging.OXIDIZATION)) { + pm.registerEvents(new OxidizationLogging(this), this); + } + if (Config.isLoggingAnyEntities()) { + if (!WorldEditHelper.hasFullWorldEdit()) { + getLogger().severe("No compatible WorldEdit found, entity logging will not work!"); + } else { + pm.registerEvents(new AdvancedEntityLogging(this), this); + getLogger().info("Entity logging enabled!"); + } + } + } + + @Override + public void onDisable() { + isCompletelyEnabled = false; + getServer().getScheduler().cancelTasks(this); + if (consumer != null) { + if (logPlayerInfo && playerInfoLogging != null) { + for (final Player player : getServer().getOnlinePlayers()) { + playerInfoLogging.onPlayerQuit(player); + } + } + getLogger().info("Waiting for consumer ..."); + consumer.shutdown(); + if (consumer.getQueueSize() > 0) { + getLogger().info("Remaining queue size: " + consumer.getQueueSize() + ". Trying to write to a local file."); + try { + consumer.writeToFile(); + getLogger().info("Successfully dumped queue."); + } catch (final FileNotFoundException ex) { + getLogger().info("Failed to write. Given up."); + } + } + } + if (pool != null) { + pool.close(); + } + } + + @Override + public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args) { + if (noDb) { + sender.sendMessage(ChatColor.RED + "No database connected. Check your MySQL user/pw and database for typos. Start/restart your MySQL server."); + } + return true; + } + + public boolean hasPermission(CommandSender sender, String permission) { + return sender.hasPermission(permission); + } + + public Connection getConnection() { + return getConnection(false); + } + + public Connection getConnection(boolean testConnection) { + try { + final Connection conn = pool.getConnection(); + if (!connected) { + getLogger().info("MySQL connection rebuild"); + connected = true; + } + return conn; + } catch (final Exception ex) { + if (testConnection) { + getLogger().log(Level.SEVERE, "Could not connect to the Database! Please check your config! " + ex.getMessage()); + } else if (connected) { + getLogger().log(Level.SEVERE, "Error while fetching connection: ", ex); + connected = false; + } else { + getLogger().log(Level.SEVERE, "MySQL connection lost", ex); + } + return null; + } + } + + /** + * Returns a list of block changes based on the given query parameters, the query parameters + * are essentially programmatic versions of the parameters a player would pass + * to the logblock lookup command i.e /lb lookup query-parameters + * + * Note: this method directly calls a SQL query and is hence a slow blocking function, avoid running + * it on the main game thread + * + * @param params QueryParams that contains the needed columns (all other will be filled with default values) and the params. World is required. + * @return Returns a list of block changes based on the given query parameters + * @throws SQLException if a sql exception occurs while looking up the block changes + */ + public List getBlockChanges(QueryParams params) throws SQLException { + final Connection conn = getConnection(); + Statement state = null; + if (conn == null) { + throw new SQLException("No connection"); + } + try { + state = conn.createStatement(); + final ResultSet rs = state.executeQuery(params.getQuery()); + final List blockchanges = new ArrayList<>(); + while (rs.next()) { + blockchanges.add(new BlockChange(rs, params)); + } + return blockchanges; + } finally { + if (state != null) { + state.close(); + } + conn.close(); + } + } + + public int getCount(QueryParams params) throws SQLException { + if (params == null || params.world == null || !Config.isLogged(params.world)) { + throw new IllegalArgumentException("World is not logged: " + ((params == null || params.world == null) ? "null" : params.world.getName())); + } + final Connection conn = getConnection(); + Statement state = null; + if (conn == null) { + throw new SQLException("No connection"); + } + try { + state = conn.createStatement(); + final QueryParams p = params.clone(); + p.needCount = true; + final ResultSet rs = state.executeQuery(p.getQuery()); + if (!rs.next()) { + return 0; + } + return rs.getInt(1); + } finally { + if (state != null) { + state.close(); + } + conn.close(); + } + } + + @Override + public File getFile() { + return super.getFile(); + } + + public Questioner getQuestioner() { + return questioner; + } + + public ScaffoldingLogging getScaffoldingLogging() { + return scaffoldingLogging; + } +} diff --git a/src/main/java/de/diddiz/LogBlock/LookupCacheElement.java b/src/main/java/de/diddiz/LogBlock/LookupCacheElement.java index 0bffb6b..ba3c55c 100644 --- a/src/main/java/de/diddiz/LogBlock/LookupCacheElement.java +++ b/src/main/java/de/diddiz/LogBlock/LookupCacheElement.java @@ -1,18 +1,18 @@ -package de.diddiz.LogBlock; - -import net.md_5.bungee.api.chat.BaseComponent; -import org.bukkit.Location; - -public interface LookupCacheElement { - public Location getLocation(); - - public default BaseComponent[] getLogMessage() { - return getLogMessage(-1); - } - - public BaseComponent[] getLogMessage(int entry); - - public default int getNumChanges() { - return 1; - } -} +package de.diddiz.LogBlock; + +import net.md_5.bungee.api.chat.BaseComponent; +import org.bukkit.Location; + +public interface LookupCacheElement { + public Location getLocation(); + + public default BaseComponent[] getLogMessage() { + return getLogMessage(-1); + } + + public BaseComponent[] getLogMessage(int entry); + + public default int getNumChanges() { + return 1; + } +} diff --git a/src/main/java/de/diddiz/LogBlock/LookupCacheElementFactory.java b/src/main/java/de/diddiz/LogBlock/LookupCacheElementFactory.java index cc579e2..6bbbc45 100755 --- a/src/main/java/de/diddiz/LogBlock/LookupCacheElementFactory.java +++ b/src/main/java/de/diddiz/LogBlock/LookupCacheElementFactory.java @@ -1,40 +1,40 @@ -package de.diddiz.LogBlock; - -import de.diddiz.LogBlock.QueryParams.BlockChangeType; -import de.diddiz.LogBlock.QueryParams.SummarizationMode; - -import java.sql.ResultSet; -import java.sql.SQLException; - -public class LookupCacheElementFactory { - private final QueryParams params; - private final float spaceFactor; - - public LookupCacheElementFactory(QueryParams params, float spaceFactor) { - this.params = params; - this.spaceFactor = spaceFactor; - } - - public LookupCacheElement getLookupCacheElement(ResultSet rs) throws SQLException { - if (params.bct == BlockChangeType.CHAT) { - return new ChatMessage(rs, params); - } - if (params.bct == BlockChangeType.KILLS) { - if (params.sum == SummarizationMode.NONE) { - return new Kill(rs, params); - } else if (params.sum == SummarizationMode.PLAYERS) { - return new SummedKills(rs, params, spaceFactor); - } - } - if (params.bct == BlockChangeType.ENTITIES || params.bct == BlockChangeType.ENTITIES_CREATED || params.bct == BlockChangeType.ENTITIES_KILLED) { - if (params.sum == SummarizationMode.NONE) { - return new EntityChange(rs, params); - } - return new SummedEntityChanges(rs, params, spaceFactor); - } - if (params.sum == SummarizationMode.NONE) { - return new BlockChange(rs, params); - } - return new SummedBlockChanges(rs, params, spaceFactor); - } -} +package de.diddiz.LogBlock; + +import de.diddiz.LogBlock.QueryParams.BlockChangeType; +import de.diddiz.LogBlock.QueryParams.SummarizationMode; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class LookupCacheElementFactory { + private final QueryParams params; + private final float spaceFactor; + + public LookupCacheElementFactory(QueryParams params, float spaceFactor) { + this.params = params; + this.spaceFactor = spaceFactor; + } + + public LookupCacheElement getLookupCacheElement(ResultSet rs) throws SQLException { + if (params.bct == BlockChangeType.CHAT) { + return new ChatMessage(rs, params); + } + if (params.bct == BlockChangeType.KILLS) { + if (params.sum == SummarizationMode.NONE) { + return new Kill(rs, params); + } else if (params.sum == SummarizationMode.PLAYERS) { + return new SummedKills(rs, params, spaceFactor); + } + } + if (params.bct == BlockChangeType.ENTITIES || params.bct == BlockChangeType.ENTITIES_CREATED || params.bct == BlockChangeType.ENTITIES_KILLED) { + if (params.sum == SummarizationMode.NONE) { + return new EntityChange(rs, params); + } + return new SummedEntityChanges(rs, params, spaceFactor); + } + if (params.sum == SummarizationMode.NONE) { + return new BlockChange(rs, params); + } + return new SummedBlockChanges(rs, params, spaceFactor); + } +} diff --git a/src/main/java/de/diddiz/LogBlock/Session.java b/src/main/java/de/diddiz/LogBlock/Session.java index d13ff78..b145d4e 100644 --- a/src/main/java/de/diddiz/LogBlock/Session.java +++ b/src/main/java/de/diddiz/LogBlock/Session.java @@ -1,49 +1,49 @@ -package de.diddiz.LogBlock; - -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; - -import java.util.HashMap; -import java.util.Map; - -import static de.diddiz.LogBlock.config.Config.toolsByType; -import static org.bukkit.Bukkit.getServer; - -public class Session { - private static final Map sessions = new HashMap<>(); - public QueryParams lastQuery = null; - public LookupCacheElement[] lookupCache = null; - public int page = 1; - public Map toolData; - - private Session(Player player) { - toolData = new HashMap<>(); - final LogBlock logblock = LogBlock.getInstance(); - if (player != null) { - for (final Tool tool : toolsByType.values()) { - toolData.put(tool, new ToolData(tool, logblock, player)); - } - } - } - - public static boolean hasSession(CommandSender sender) { - return sessions.containsKey(sender.getName().toLowerCase()); - } - - public static boolean hasSession(String playerName) { - return sessions.containsKey(playerName.toLowerCase()); - } - - public static Session getSession(CommandSender sender) { - return getSession(sender.getName()); - } - - public static Session getSession(String playerName) { - Session session = sessions.get(playerName.toLowerCase()); - if (session == null) { - session = new Session(getServer().getPlayer(playerName)); - sessions.put(playerName.toLowerCase(), session); - } - return session; - } -} +package de.diddiz.LogBlock; + +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.Map; + +import static de.diddiz.LogBlock.config.Config.toolsByType; +import static org.bukkit.Bukkit.getServer; + +public class Session { + private static final Map sessions = new HashMap<>(); + public QueryParams lastQuery = null; + public LookupCacheElement[] lookupCache = null; + public int page = 1; + public Map toolData; + + private Session(Player player) { + toolData = new HashMap<>(); + final LogBlock logblock = LogBlock.getInstance(); + if (player != null) { + for (final Tool tool : toolsByType.values()) { + toolData.put(tool, new ToolData(tool, logblock, player)); + } + } + } + + public static boolean hasSession(CommandSender sender) { + return sessions.containsKey(sender.getName().toLowerCase()); + } + + public static boolean hasSession(String playerName) { + return sessions.containsKey(playerName.toLowerCase()); + } + + public static Session getSession(CommandSender sender) { + return getSession(sender.getName()); + } + + public static Session getSession(String playerName) { + Session session = sessions.get(playerName.toLowerCase()); + if (session == null) { + session = new Session(getServer().getPlayer(playerName)); + sessions.put(playerName.toLowerCase(), session); + } + return session; + } +} diff --git a/src/main/java/de/diddiz/LogBlock/SummedBlockChanges.java b/src/main/java/de/diddiz/LogBlock/SummedBlockChanges.java index 2e357c1..871666d 100644 --- a/src/main/java/de/diddiz/LogBlock/SummedBlockChanges.java +++ b/src/main/java/de/diddiz/LogBlock/SummedBlockChanges.java @@ -1,43 +1,43 @@ -package de.diddiz.LogBlock; - -import static de.diddiz.LogBlock.util.MessagingUtil.prettyMaterial; - -import de.diddiz.LogBlock.QueryParams.SummarizationMode; -import de.diddiz.LogBlock.util.MessagingUtil; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.Objects; -import net.md_5.bungee.api.chat.BaseComponent; -import net.md_5.bungee.api.chat.TextComponent; -import org.bukkit.Location; - -public class SummedBlockChanges implements LookupCacheElement { - private final int type; - private final int created, destroyed; - private final float spaceFactor; - private final Actor actor; - - public SummedBlockChanges(ResultSet rs, QueryParams p, float spaceFactor) throws SQLException { - // Actor currently useless here as we don't yet output UUID in results anywhere - actor = p.sum == SummarizationMode.PLAYERS ? new Actor(rs) : null; - type = p.sum == SummarizationMode.TYPES ? rs.getInt("type") : 0; - created = rs.getInt("created"); - destroyed = rs.getInt("destroyed"); - this.spaceFactor = spaceFactor; - } - - @Override - public Location getLocation() { - return null; - } - - @Override - public BaseComponent[] getLogMessage(int entry) { - return MessagingUtil.formatSummarizedChanges(created, destroyed, actor != null ? new TextComponent(actor.getName()) : prettyMaterial(Objects.toString(MaterialConverter.getMaterial(type))), 10, 10, spaceFactor); - } - - @Override - public int getNumChanges() { - return created + destroyed; - } -} +package de.diddiz.LogBlock; + +import static de.diddiz.LogBlock.util.MessagingUtil.prettyMaterial; + +import de.diddiz.LogBlock.QueryParams.SummarizationMode; +import de.diddiz.LogBlock.util.MessagingUtil; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Objects; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.TextComponent; +import org.bukkit.Location; + +public class SummedBlockChanges implements LookupCacheElement { + private final int type; + private final int created, destroyed; + private final float spaceFactor; + private final Actor actor; + + public SummedBlockChanges(ResultSet rs, QueryParams p, float spaceFactor) throws SQLException { + // Actor currently useless here as we don't yet output UUID in results anywhere + actor = p.sum == SummarizationMode.PLAYERS ? new Actor(rs) : null; + type = p.sum == SummarizationMode.TYPES ? rs.getInt("type") : 0; + created = rs.getInt("created"); + destroyed = rs.getInt("destroyed"); + this.spaceFactor = spaceFactor; + } + + @Override + public Location getLocation() { + return null; + } + + @Override + public BaseComponent[] getLogMessage(int entry) { + return MessagingUtil.formatSummarizedChanges(created, destroyed, actor != null ? new TextComponent(actor.getName()) : prettyMaterial(Objects.toString(MaterialConverter.getMaterial(type))), 10, 10, spaceFactor); + } + + @Override + public int getNumChanges() { + return created + destroyed; + } +} diff --git a/src/main/java/de/diddiz/LogBlock/Tool.java b/src/main/java/de/diddiz/LogBlock/Tool.java index 7a9c828..5123409 100644 --- a/src/main/java/de/diddiz/LogBlock/Tool.java +++ b/src/main/java/de/diddiz/LogBlock/Tool.java @@ -1,35 +1,35 @@ -package de.diddiz.LogBlock; - -import org.bukkit.Material; -import org.bukkit.permissions.PermissionDefault; - -import java.util.List; - -public class Tool { - public final String name; - public final List aliases; - public final ToolBehavior leftClickBehavior, rightClickBehavior; - public final boolean defaultEnabled; - public final Material item; - public final boolean canDrop; - public final QueryParams params; - public final ToolMode mode; - public final PermissionDefault permissionDefault; - public final boolean removeOnDisable; - public final boolean dropToDisable; - - public Tool(String name, List aliases, ToolBehavior leftClickBehavior, ToolBehavior rightClickBehavior, boolean defaultEnabled, Material item, boolean canDrop, QueryParams params, ToolMode mode, PermissionDefault permissionDefault, boolean removeOnDisable, boolean dropToDisable) { - this.name = name; - this.aliases = aliases; - this.leftClickBehavior = leftClickBehavior; - this.rightClickBehavior = rightClickBehavior; - this.defaultEnabled = defaultEnabled; - this.item = item; - this.canDrop = canDrop; - this.params = params; - this.mode = mode; - this.permissionDefault = permissionDefault; - this.removeOnDisable = removeOnDisable; - this.dropToDisable = dropToDisable; - } -} +package de.diddiz.LogBlock; + +import org.bukkit.Material; +import org.bukkit.permissions.PermissionDefault; + +import java.util.List; + +public class Tool { + public final String name; + public final List aliases; + public final ToolBehavior leftClickBehavior, rightClickBehavior; + public final boolean defaultEnabled; + public final Material item; + public final boolean canDrop; + public final QueryParams params; + public final ToolMode mode; + public final PermissionDefault permissionDefault; + public final boolean removeOnDisable; + public final boolean dropToDisable; + + public Tool(String name, List aliases, ToolBehavior leftClickBehavior, ToolBehavior rightClickBehavior, boolean defaultEnabled, Material item, boolean canDrop, QueryParams params, ToolMode mode, PermissionDefault permissionDefault, boolean removeOnDisable, boolean dropToDisable) { + this.name = name; + this.aliases = aliases; + this.leftClickBehavior = leftClickBehavior; + this.rightClickBehavior = rightClickBehavior; + this.defaultEnabled = defaultEnabled; + this.item = item; + this.canDrop = canDrop; + this.params = params; + this.mode = mode; + this.permissionDefault = permissionDefault; + this.removeOnDisable = removeOnDisable; + this.dropToDisable = dropToDisable; + } +} diff --git a/src/main/java/de/diddiz/LogBlock/ToolBehavior.java b/src/main/java/de/diddiz/LogBlock/ToolBehavior.java index bbeac79..5c75895 100644 --- a/src/main/java/de/diddiz/LogBlock/ToolBehavior.java +++ b/src/main/java/de/diddiz/LogBlock/ToolBehavior.java @@ -1,7 +1,7 @@ -package de.diddiz.LogBlock; - -public enum ToolBehavior { - TOOL, - BLOCK, - NONE -} +package de.diddiz.LogBlock; + +public enum ToolBehavior { + TOOL, + BLOCK, + NONE +} diff --git a/src/main/java/de/diddiz/LogBlock/ToolData.java b/src/main/java/de/diddiz/LogBlock/ToolData.java index f524393..ee75dcc 100644 --- a/src/main/java/de/diddiz/LogBlock/ToolData.java +++ b/src/main/java/de/diddiz/LogBlock/ToolData.java @@ -1,15 +1,15 @@ -package de.diddiz.LogBlock; - -import org.bukkit.entity.Player; - -public class ToolData { - public boolean enabled; - public QueryParams params; - public ToolMode mode; - - public ToolData(Tool tool, LogBlock logblock, Player player) { - enabled = tool.defaultEnabled && logblock.hasPermission(player, "logblock.tools." + tool.name); - params = tool.params.clone(); - mode = tool.mode; - } -} +package de.diddiz.LogBlock; + +import org.bukkit.entity.Player; + +public class ToolData { + public boolean enabled; + public QueryParams params; + public ToolMode mode; + + public ToolData(Tool tool, LogBlock logblock, Player player) { + enabled = tool.defaultEnabled && logblock.hasPermission(player, "logblock.tools." + tool.name); + params = tool.params.clone(); + mode = tool.mode; + } +} diff --git a/src/main/java/de/diddiz/LogBlock/ToolMode.java b/src/main/java/de/diddiz/LogBlock/ToolMode.java index 25fc249..3a90137 100644 --- a/src/main/java/de/diddiz/LogBlock/ToolMode.java +++ b/src/main/java/de/diddiz/LogBlock/ToolMode.java @@ -1,18 +1,18 @@ -package de.diddiz.LogBlock; - -public enum ToolMode { - CLEARLOG("logblock.clearlog"), - LOOKUP("logblock.lookup"), - REDO("logblock.rollback"), - ROLLBACK("logblock.rollback"), - WRITELOGFILE("logblock.rollback"); - private final String permission; - - private ToolMode(String permission) { - this.permission = permission; - } - - public String getPermission() { - return permission; - } -} +package de.diddiz.LogBlock; + +public enum ToolMode { + CLEARLOG("logblock.clearlog"), + LOOKUP("logblock.lookup"), + REDO("logblock.rollback"), + ROLLBACK("logblock.rollback"), + WRITELOGFILE("logblock.rollback"); + private final String permission; + + private ToolMode(String permission) { + this.permission = permission; + } + + public String getPermission() { + return permission; + } +} diff --git a/src/main/java/de/diddiz/LogBlock/Updater.java b/src/main/java/de/diddiz/LogBlock/Updater.java index 8cf3897..34653c0 100644 --- a/src/main/java/de/diddiz/LogBlock/Updater.java +++ b/src/main/java/de/diddiz/LogBlock/Updater.java @@ -1,1160 +1,1160 @@ -package de.diddiz.LogBlock; - -import de.diddiz.LogBlock.blockstate.BlockStateCodecSign; -import de.diddiz.LogBlock.config.Config; -import de.diddiz.LogBlock.config.WorldConfig; -import de.diddiz.LogBlock.util.ComparableVersion; -import de.diddiz.LogBlock.util.UUIDFetcher; -import de.diddiz.LogBlock.util.Utils; -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.sign.Side; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.inventory.ItemStack; - -import java.io.BufferedInputStream; -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStreamReader; -import java.sql.*; -import java.util.*; -import java.util.jar.JarFile; -import java.util.logging.Level; - -import static de.diddiz.LogBlock.config.Config.getLoggedWorlds; -import static de.diddiz.LogBlock.config.Config.isLogging; -import static de.diddiz.LogBlock.util.BukkitUtils.friendlyWorldname; - -class Updater { - private final LogBlock logblock; - final int UUID_CONVERT_BATCH_SIZE = 100; - final int BLOCKS_CONVERT_BATCH_SIZE = 100000; - final int OTHER_CONVERT_BATCH_SIZE = 20000; - - Updater(LogBlock logblock) { - this.logblock = logblock; - } - - boolean update() { - final ConfigurationSection config = logblock.getConfig(); - String versionString = config.getString("version"); - ComparableVersion configVersion = new ComparableVersion(versionString); - // if (configVersion.compareTo(new ComparableVersion(logblock.getDescription().getVersion().replace(" (manually compiled)", ""))) >= 0) { - // return false; - // } - if (configVersion.compareTo(new ComparableVersion("1.2.7")) < 0) { - logblock.getLogger().info("Updating tables to 1.2.7 ..."); - if (isLogging(Logging.CHAT) || isLogging(Logging.PLAYER_COMMANDS) || isLogging(Logging.CONSOLE_COMMANDS) || isLogging(Logging.COMMANDBLOCK_COMMANDS)) { - final Connection conn = logblock.getConnection(); - try { - conn.setAutoCommit(true); - final Statement st = conn.createStatement(); - st.execute("ALTER TABLE `lb-chat` ADD FULLTEXT message (message)"); - st.close(); - conn.close(); - } catch (final SQLException ex) { - logblock.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); - return false; - } - } - config.set("version", "1.2.7"); - } - if (configVersion.compareTo(new ComparableVersion("1.3")) < 0) { - logblock.getLogger().info("Updating config to 1.3.0 ..."); - for (final String tool : config.getConfigurationSection("tools").getKeys(false)) { - if (config.get("tools." + tool + ".permissionDefault") == null) { - config.set("tools." + tool + ".permissionDefault", "OP"); - } - } - config.set("version", "1.3.0"); - } - if (configVersion.compareTo(new ComparableVersion("1.3.1")) < 0) { - logblock.getLogger().info("Updating tables to 1.3.1 ..."); - final Connection conn = logblock.getConnection(); - try { - conn.setAutoCommit(true); - final Statement st = conn.createStatement(); - st.execute("ALTER TABLE `lb-players` ADD COLUMN lastlogin DATETIME NOT NULL, ADD COLUMN onlinetime TIME NOT NULL, ADD COLUMN ip VARCHAR(255) NOT NULL"); - st.close(); - conn.close(); - } catch (final SQLException ex) { - logblock.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); - return false; - } - config.set("version", "1.3.1"); - } - if (configVersion.compareTo(new ComparableVersion("1.3.2")) < 0) { - logblock.getLogger().info("Updating tables to 1.3.2 ..."); - final Connection conn = logblock.getConnection(); - try { - conn.setAutoCommit(true); - final Statement st = conn.createStatement(); - st.execute("ALTER TABLE `lb-players` ADD COLUMN firstlogin DATETIME NOT NULL AFTER playername"); - st.close(); - conn.close(); - } catch (final SQLException ex) { - logblock.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); - return false; - } - config.set("version", "1.3.2"); - } - if (configVersion.compareTo(new ComparableVersion("1.4")) < 0) { - logblock.getLogger().info("Updating config to 1.4.0 ..."); - config.set("clearlog.keepLogDays", null); - config.set("version", "1.4.0"); - } - if (configVersion.compareTo(new ComparableVersion("1.4.2")) < 0) { - logblock.getLogger().info("Updating config to 1.4.2 ..."); - for (final String world : config.getStringList("loggedWorlds")) { - final File file = new File(logblock.getDataFolder(), friendlyWorldname(world) + ".yml"); - final YamlConfiguration wcfg = YamlConfiguration.loadConfiguration(file); - if (wcfg.contains("logBlockCreations")) { - wcfg.set("logging.BLOCKPLACE", wcfg.getBoolean("logBlockCreations")); - } - if (wcfg.contains("logBlockDestroyings")) { - wcfg.set("logging.BLOCKBREAK", wcfg.getBoolean("logBlockDestroyings")); - } - if (wcfg.contains("logSignTexts")) { - wcfg.set("logging.SIGNTEXT", wcfg.getBoolean("logSignTexts")); - } - if (wcfg.contains("logFire")) { - wcfg.set("logging.FIRE", wcfg.getBoolean("logFire")); - } - if (wcfg.contains("logLeavesDecay")) { - wcfg.set("logging.LEAVESDECAY", wcfg.getBoolean("logLeavesDecay")); - } - if (wcfg.contains("logLavaFlow")) { - wcfg.set("logging.LAVAFLOW", wcfg.getBoolean("logLavaFlow")); - } - if (wcfg.contains("logWaterFlow")) { - wcfg.set("logging.WATERFLOW", wcfg.getBoolean("logWaterFlow")); - } - if (wcfg.contains("logChestAccess")) { - wcfg.set("logging.CHESTACCESS", wcfg.getBoolean("logChestAccess")); - } - if (wcfg.contains("logButtonsAndLevers")) { - wcfg.set("logging.SWITCHINTERACT", wcfg.getBoolean("logButtonsAndLevers")); - } - if (wcfg.contains("logKills")) { - wcfg.set("logging.KILL", wcfg.getBoolean("logKills")); - } - if (wcfg.contains("logChat")) { - wcfg.set("logging.CHAT", wcfg.getBoolean("logChat")); - } - if (wcfg.contains("logSnowForm")) { - wcfg.set("logging.SNOWFORM", wcfg.getBoolean("logSnowForm")); - } - if (wcfg.contains("logSnowFade")) { - wcfg.set("logging.SNOWFADE", wcfg.getBoolean("logSnowFade")); - } - if (wcfg.contains("logDoors")) { - wcfg.set("logging.DOORINTERACT", wcfg.getBoolean("logDoors")); - } - if (wcfg.contains("logCakes")) { - wcfg.set("logging.CAKEEAT", wcfg.getBoolean("logCakes")); - } - if (wcfg.contains("logEndermen")) { - wcfg.set("logging.ENDERMEN", wcfg.getBoolean("logEndermen")); - } - if (wcfg.contains("logExplosions")) { - final boolean logExplosions = wcfg.getBoolean("logExplosions"); - wcfg.set("logging.TNTEXPLOSION", logExplosions); - wcfg.set("logging.MISCEXPLOSION", logExplosions); - wcfg.set("logging.CREEPEREXPLOSION", logExplosions); - wcfg.set("logging.GHASTFIREBALLEXPLOSION", logExplosions); - } - wcfg.set("logBlockCreations", null); - wcfg.set("logBlockDestroyings", null); - wcfg.set("logSignTexts", null); - wcfg.set("logExplosions", null); - wcfg.set("logFire", null); - wcfg.set("logLeavesDecay", null); - wcfg.set("logLavaFlow", null); - wcfg.set("logWaterFlow", null); - wcfg.set("logChestAccess", null); - wcfg.set("logButtonsAndLevers", null); - wcfg.set("logKills", null); - wcfg.set("logChat", null); - wcfg.set("logSnowForm", null); - wcfg.set("logSnowFade", null); - wcfg.set("logDoors", null); - wcfg.set("logCakes", null); - wcfg.set("logEndermen", null); - try { - wcfg.save(file); - } catch (final IOException ex) { - logblock.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); - } - } - config.set("clearlog.keepLogDays", null); - config.set("version", "1.4.2"); - } - if (configVersion.compareTo(new ComparableVersion("1.5.1")) < 0) { - logblock.getLogger().info("Updating tables to 1.5.1 ..."); - final Connection conn = logblock.getConnection(); - try { - conn.setAutoCommit(true); - final Statement st = conn.createStatement(); - for (final WorldConfig wcfg : getLoggedWorlds()) { - if (wcfg.isLogging(Logging.KILL)) { - st.execute("ALTER TABLE `" + wcfg.table + "-kills` ADD (x MEDIUMINT NOT NULL DEFAULT 0, y SMALLINT NOT NULL DEFAULT 0, z MEDIUMINT NOT NULL DEFAULT 0)"); - } - } - st.close(); - conn.close(); - } catch (final SQLException ex) { - logblock.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); - return false; - } - config.set("version", "1.5.1"); - } - if (configVersion.compareTo(new ComparableVersion("1.5.2")) < 0) { - logblock.getLogger().info("Updating tables to 1.5.2 ..."); - final Connection conn = logblock.getConnection(); - try { - conn.setAutoCommit(true); - final Statement st = conn.createStatement(); - final ResultSet rs = st.executeQuery("SHOW COLUMNS FROM `lb-players` WHERE field = 'onlinetime'"); - if (rs.next() && rs.getString("Type").equalsIgnoreCase("time")) { - st.execute("ALTER TABLE `lb-players` ADD onlinetime2 INT UNSIGNED NOT NULL"); - st.execute("UPDATE `lb-players` SET onlinetime2 = HOUR(onlinetime) * 3600 + MINUTE(onlinetime) * 60 + SECOND(onlinetime)"); - st.execute("ALTER TABLE `lb-players` DROP onlinetime"); - st.execute("ALTER TABLE `lb-players` CHANGE onlinetime2 onlinetime INT UNSIGNED NOT NULL"); - } else { - logblock.getLogger().info("Column lb-players was already modified, skipping it."); - } - st.close(); - conn.close(); - } catch (final SQLException ex) { - logblock.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); - return false; - } - config.set("version", "1.5.2"); - } - if (configVersion.compareTo(new ComparableVersion("1.8.1")) < 0) { - logblock.getLogger().info("Updating tables to 1.8.1 ..."); - final Connection conn = logblock.getConnection(); - try { - conn.setAutoCommit(true); - final Statement st = conn.createStatement(); - for (final WorldConfig wcfg : getLoggedWorlds()) { - if (wcfg.isLogging(Logging.CHESTACCESS)) { - st.execute("ALTER TABLE `" + wcfg.table + "-chest` CHANGE itemdata itemdata SMALLINT NOT NULL"); - logblock.getLogger().info("Table " + wcfg.table + "-chest modified"); - } - } - st.close(); - conn.close(); - } catch (final SQLException ex) { - logblock.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); - return false; - } - config.set("version", "1.8.1"); - } - - if (configVersion.compareTo(new ComparableVersion("1.9")) < 0) { - logblock.getLogger().info("Updating tables to 1.9.0 ..."); - logblock.getLogger().info("Importing UUIDs for large databases may take some time"); - final Connection conn = logblock.getConnection(); - try { - conn.setAutoCommit(true); - final Statement st = conn.createStatement(); - st.execute("ALTER TABLE `lb-players` ADD `UUID` VARCHAR(36) NOT NULL"); - } catch (final SQLException ex) { - // Error 1060 is MySQL error "column already exists". We want to continue with import if we get that error - if (ex.getErrorCode() != 1060) { - logblock.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); - return false; - } - } - try { - String unimportedPrefix = "noimport_"; - ResultSet rs; - conn.setAutoCommit(true); - final Statement st = conn.createStatement(); - if (config.getBoolean("logging.logPlayerInfo")) { - // Start by assuming anything with no onlinetime is not a player - st.execute("UPDATE `lb-players` SET UUID = CONCAT ('log_',playername) WHERE onlinetime=0 AND LENGTH(UUID) = 0"); - } else { - // If we can't assume that, we must assume anything we can't look up is not a player - unimportedPrefix = "log_"; - } - // Tell people how many are needing converted - rs = st.executeQuery("SELECT COUNT(playername) FROM `lb-players` WHERE LENGTH(UUID)=0"); - rs.next(); - String total = Integer.toString(rs.getInt(1)); - logblock.getLogger().info(total + " players to convert"); - int done = 0; - - conn.setAutoCommit(false); - Map players = new HashMap<>(); - List names = new ArrayList<>(UUID_CONVERT_BATCH_SIZE); - Map response; - rs = st.executeQuery("SELECT playerid,playername FROM `lb-players` WHERE LENGTH(UUID)=0 LIMIT " + Integer.toString(UUID_CONVERT_BATCH_SIZE)); - while (rs.next()) { - do { - players.put(rs.getString(2), rs.getInt(1)); - names.add(rs.getString(2)); - } while (rs.next()); - if (names.size() > 0) { - String theUUID; - response = UUIDFetcher.getUUIDs(names); - for (Map.Entry entry : players.entrySet()) { - if (response.get(entry.getKey()) == null) { - theUUID = unimportedPrefix + entry.getKey(); - logblock.getLogger().warning(entry.getKey() + " not found - giving UUID of " + theUUID); - } else { - theUUID = response.get(entry.getKey()).toString(); - } - String thePID = entry.getValue().toString(); - st.execute("UPDATE `lb-players` SET UUID = '" + theUUID + "' WHERE playerid = " + thePID); - done++; - } - conn.commit(); - players.clear(); - names.clear(); - logblock.getLogger().info("Processed " + Integer.toString(done) + " out of " + total); - rs.close(); - rs = st.executeQuery("SELECT playerid,playername FROM `lb-players` WHERE LENGTH(UUID)=0 LIMIT " + Integer.toString(UUID_CONVERT_BATCH_SIZE)); - } - } - rs.close(); - st.close(); - conn.close(); - - } catch (final SQLException ex) { - logblock.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); - return false; - } catch (Exception ex) { - logblock.getLogger().log(Level.SEVERE, "[UUID importer]", ex); - return false; - } - config.set("version", "1.9.0"); - } - if (configVersion.compareTo(new ComparableVersion("1.9.4")) < 0) { - logblock.getLogger().info("Updating tables to 1.9.4 ..."); - final Connection conn = logblock.getConnection(); - try { - conn.setAutoCommit(true); - final Statement st = conn.createStatement(); - // Need to wrap both these next two inside individual try/catch statements in case index does not exist - try { - st.execute("DROP INDEX UUID ON `lb-players`"); - } catch (final SQLException ex) { - if (ex.getErrorCode() != 1091) { - logblock.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); - return false; - } - } - try { - st.execute("DROP INDEX playername ON `lb-players`"); - } catch (final SQLException ex) { - if (ex.getErrorCode() != 1091) { - logblock.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); - return false; - } - } - st.execute("CREATE INDEX UUID ON `lb-players` (UUID);"); - st.execute("CREATE INDEX playername ON `lb-players` (playername);"); - st.close(); - conn.close(); - } catch (final SQLException ex) { - logblock.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); - return false; - } - config.set("version", "1.9.4"); - } - // Ensure charset for free-text fields is UTF-8, or UTF8-mb4 if possible - // As this may be an expensive operation and the database default may already be this, check on a table-by-table basis before converting - if (configVersion.compareTo(new ComparableVersion("1.10.0")) < 0) { - logblock.getLogger().info("Updating tables to 1.10.0 ..."); - final Connection conn = logblock.getConnection(); - try { - conn.setAutoCommit(true); - final Statement st = conn.createStatement(); - checkCharset("lb-players", "name", st, false); - if (isLogging(Logging.CHAT) || isLogging(Logging.PLAYER_COMMANDS) || isLogging(Logging.CONSOLE_COMMANDS) || isLogging(Logging.COMMANDBLOCK_COMMANDS)) { - checkCharset("lb-chat", "message", st, false); - } - for (final WorldConfig wcfg : getLoggedWorlds()) { - if (wcfg.isLogging(Logging.SIGNTEXT)) { - // checkCharset(wcfg.table + "-sign","signtext",st); - } - } - st.close(); - conn.close(); - } catch (final SQLException ex) { - logblock.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); - return false; - } - config.set("version", "1.10.0"); - } - - if (configVersion.compareTo(new ComparableVersion("1.12.0")) < 0) { - logblock.getLogger().info("Updating tables to 1.12.0 ..."); - if (isLogging(Logging.CHAT) || isLogging(Logging.PLAYER_COMMANDS) || isLogging(Logging.CONSOLE_COMMANDS) || isLogging(Logging.COMMANDBLOCK_COMMANDS)) { - final Connection conn = logblock.getConnection(); - try { - conn.setAutoCommit(true); - final Statement st = conn.createStatement(); - st.execute("ALTER TABLE `lb-chat` MODIFY COLUMN `message` VARCHAR(256) NOT NULL"); - st.close(); - conn.close(); - } catch (final SQLException ex) { - logblock.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); - return false; - } - } - config.set("version", "1.12.0"); - } - if (configVersion.compareTo(new ComparableVersion("1.13.0")) < 0) { - logblock.getLogger().info("Updating tables to 1.13.0 ..."); - try { - MaterialUpdater1_13 materialUpdater = new MaterialUpdater1_13(logblock); - logblock.getLogger().info("Convertig BlockId to BlockData. This can take a while ..."); - final Connection conn = logblock.getConnection(); - conn.setAutoCommit(false); - final Statement st = conn.createStatement(); - for (final WorldConfig wcfg : getLoggedWorlds()) { - logblock.getLogger().info("Processing world " + wcfg.world + "..."); - logblock.getLogger().info("Processing block changes..."); - boolean hadRow = true; - long rowsToConvert = 0; - long done = 0; - try { - ResultSet rs = st.executeQuery("SELECT count(*) as rowcount FROM `" + wcfg.table + "`"); - if (rs.next()) { - rowsToConvert = rs.getLong(1); - logblock.getLogger().info("Converting " + rowsToConvert + " entries in " + wcfg.table); - } - rs.close(); - - PreparedStatement deleteStatement = conn.prepareStatement("DELETE FROM `" + wcfg.table + "` WHERE id = ?"); - PreparedStatement insertStatement = conn.prepareStatement("INSERT INTO `" + wcfg.table + "-blocks` (id, date, playerid, replaced, replacedData, type, typeData, x, y, z) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS); - - while (hadRow) { - hadRow = false; - ResultSet entries = st.executeQuery("SELECT id, date, playerid, replaced, type, data, x, y, z FROM `" + wcfg.table + "` ORDER BY id ASC LIMIT " + BLOCKS_CONVERT_BATCH_SIZE); - while (entries.next()) { - hadRow = true; - long id = entries.getLong("id"); - Timestamp date = entries.getTimestamp("date"); - int playerid = entries.getInt("playerid"); - int replaced = entries.getInt("replaced"); - int type = entries.getInt("type"); - int data = entries.getInt("data"); - int x = entries.getInt("x"); - int y = entries.getInt("y"); - int z = entries.getInt("z"); - if (data == 16) { - data = 0; - } - - try { - BlockData replacedBlockData = materialUpdater.getBlockData(replaced, data); - BlockData setBlockData = materialUpdater.getBlockData(type, data); - - int newReplacedId = MaterialConverter.getOrAddMaterialId(replacedBlockData); - int newReplacedData = MaterialConverter.getOrAddBlockStateId(replacedBlockData); - - int newSetId = MaterialConverter.getOrAddMaterialId(setBlockData); - int newSetData = MaterialConverter.getOrAddBlockStateId(setBlockData); - - insertStatement.setLong(1, id); - insertStatement.setTimestamp(2, date); - insertStatement.setInt(3, playerid); - insertStatement.setInt(4, newReplacedId); - insertStatement.setInt(5, newReplacedData); - insertStatement.setInt(6, newSetId); - insertStatement.setInt(7, newSetData); - insertStatement.setInt(8, x); - insertStatement.setInt(9, y); - insertStatement.setInt(10, z); - insertStatement.addBatch(); - } catch (Exception e) { - logblock.getLogger().info("Exception in entry " + id + " (" + replaced + ":" + data + "->" + type + ":" + data + "): " + e.getMessage()); - } - deleteStatement.setLong(1, id); - deleteStatement.addBatch(); - - done++; - } - entries.close(); - int failedRows = 0; - if (hadRow) { - try { - insertStatement.executeBatch(); - } catch (BatchUpdateException e) { - for (int result : e.getUpdateCounts()) { - if (result == Statement.EXECUTE_FAILED) { - failedRows++; - } - } - } - deleteStatement.executeBatch(); - } - conn.commit(); - logblock.getLogger().info("Done: " + done + "/" + rowsToConvert + " " + (failedRows > 0 ? "Duplicates: " + failedRows + " " : "") + "(" + (rowsToConvert > 0 ? (done * 100 / rowsToConvert) : 100) + "%)"); - } - insertStatement.close(); - deleteStatement.close(); - } catch (SQLException e) { - logblock.getLogger().info("Could not convert " + wcfg.table + ": " + e.getMessage()); - } - - logblock.getLogger().info("Processing chests..."); - rowsToConvert = 0; - done = 0; - try { - ResultSet rs = st.executeQuery("SELECT count(*) as rowcount FROM `" + wcfg.table + "-chest`"); - if (rs.next()) { - rowsToConvert = rs.getLong(1); - logblock.getLogger().info("Converting " + rowsToConvert + " entries in " + wcfg.table + "-chest"); - } - rs.close(); - - PreparedStatement insertChestData = conn.prepareStatement("INSERT INTO `" + wcfg.table + "-chestdata` (id, item, itemremove, itemtype) VALUES (?, ?, ?, ?)"); - PreparedStatement deleteChest = conn.prepareStatement("DELETE FROM `" + wcfg.table + "-chest` WHERE id = ?"); - while (true) { - rs = st.executeQuery("SELECT id, itemtype, itemamount, itemdata FROM `" + wcfg.table + "-chest` ORDER BY id ASC LIMIT " + OTHER_CONVERT_BATCH_SIZE); - boolean anyRow = false; - while (rs.next()) { - anyRow = true; - long id = rs.getLong("id"); - int itemtype = rs.getInt("itemtype"); - int itemdata = rs.getInt("itemdata"); - int amount = rs.getInt("itemamount"); - Material weaponMaterial = materialUpdater.getMaterial(itemtype, itemdata); - if (weaponMaterial == null) { - weaponMaterial = Material.AIR; - } - @SuppressWarnings("deprecation") - ItemStack stack = weaponMaterial.getMaxDurability() > 0 ? new ItemStack(weaponMaterial, Math.abs(amount), (short) itemdata) : new ItemStack(weaponMaterial, Math.abs(amount)); - insertChestData.setLong(1, id); - insertChestData.setBytes(2, Utils.saveItemStack(stack)); - insertChestData.setInt(3, amount >= 0 ? 0 : 1); - insertChestData.setInt(4, MaterialConverter.getOrAddMaterialId(weaponMaterial)); - insertChestData.addBatch(); - - deleteChest.setLong(1, id); - deleteChest.addBatch(); - done++; - } - rs.close(); - if (!anyRow) { - break; - } - int failedRows = 0; - try { - insertChestData.executeBatch(); - } catch (BatchUpdateException e) { - for (int result : e.getUpdateCounts()) { - if (result == Statement.EXECUTE_FAILED) { - failedRows++; - } - } - } - deleteChest.executeBatch(); - conn.commit(); - logblock.getLogger().info("Done: " + done + "/" + rowsToConvert + " " + (failedRows > 0 ? "Duplicates: " + failedRows + " " : "") + "(" + (rowsToConvert > 0 ? (done * 100 / rowsToConvert) : 100) + "%)"); - } - insertChestData.close(); - deleteChest.close(); - } catch (SQLException e) { - logblock.getLogger().info("Could not convert " + wcfg.table + "-chest: " + e.getMessage()); - } - - if (wcfg.isLogging(Logging.KILL)) { - logblock.getLogger().info("Processing kills..."); - rowsToConvert = 0; - done = 0; - try { - ResultSet rs = st.executeQuery("SELECT count(*) as rowcount FROM `" + wcfg.table + "-kills`"); - if (rs.next()) { - rowsToConvert = rs.getLong(1); - logblock.getLogger().info("Converting " + rowsToConvert + " entries in " + wcfg.table + "-kills"); - } - rs.close(); - - PreparedStatement updateWeaponStatement = conn.prepareStatement("UPDATE `" + wcfg.table + "-kills` SET weapon = ? WHERE id = ?"); - for (int start = 0;; start += OTHER_CONVERT_BATCH_SIZE) { - rs = st.executeQuery("SELECT id, weapon FROM `" + wcfg.table + "-kills` ORDER BY id ASC LIMIT " + start + "," + OTHER_CONVERT_BATCH_SIZE); - boolean anyUpdate = false; - boolean anyRow = false; - while (rs.next()) { - anyRow = true; - long id = rs.getLong("id"); - int weapon = rs.getInt("weapon"); - Material weaponMaterial = materialUpdater.getMaterial(weapon, 0); - if (weaponMaterial == null) { - weaponMaterial = Material.AIR; - } - int newWeapon = MaterialConverter.getOrAddMaterialId(weaponMaterial); - if (newWeapon != weapon) { - anyUpdate = true; - updateWeaponStatement.setInt(1, newWeapon); - updateWeaponStatement.setLong(2, id); - updateWeaponStatement.addBatch(); - } - done++; - } - rs.close(); - if (anyUpdate) { - updateWeaponStatement.executeBatch(); - conn.commit(); - } - logblock.getLogger().info("Done: " + done + "/" + rowsToConvert + " (" + (rowsToConvert > 0 ? (done * 100 / rowsToConvert) : 100) + "%)"); - if (!anyRow) { - break; - } - } - updateWeaponStatement.close(); - } catch (SQLException e) { - logblock.getLogger().info("Could not convert " + wcfg.table + "-kills: " + e.getMessage()); - } - } - } - st.close(); - conn.close(); - - logblock.getLogger().info("Updating config to 1.13.0 ..."); - config.set("logging.hiddenBlocks", materialUpdater.convertMaterials(config.getStringList("logging.hiddenBlocks"))); - config.set("rollback.dontRollback", materialUpdater.convertMaterials(config.getStringList("rollback.dontRollback"))); - config.set("rollback.replaceAnyway", materialUpdater.convertMaterials(config.getStringList("rollback.replaceAnyway"))); - final ConfigurationSection toolsSec = config.getConfigurationSection("tools"); - for (final String toolName : toolsSec.getKeys(false)) { - final ConfigurationSection tSec = toolsSec.getConfigurationSection(toolName); - tSec.set("item", materialUpdater.convertMaterial(tSec.getString("item", "OAK_LOG"))); - } - } catch (final SQLException | IOException ex) { - logblock.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); - return false; - } - config.set("version", "1.13.0"); - } - - if (configVersion.compareTo(new ComparableVersion("1.13.1")) < 0) { - logblock.getLogger().info("Updating tables to 1.13.1 ..."); - BlockStateCodecSign signCodec = new BlockStateCodecSign(); - try (Connection conn = logblock.getConnection()) { - conn.setAutoCommit(false); - final Statement st = conn.createStatement(); - for (final WorldConfig wcfg : getLoggedWorlds()) { - logblock.getLogger().info("Processing world " + wcfg.world + "..."); - ResultSet rsCol = st.executeQuery("SHOW COLUMNS FROM `" + wcfg.table + "-chestdata` LIKE 'itemtype'"); - if (!rsCol.next()) { - st.execute("ALTER TABLE `" + wcfg.table + "-chestdata` ADD COLUMN `itemtype` SMALLINT NOT NULL DEFAULT '0'"); - } - rsCol.close(); - conn.commit(); - if (wcfg.isLogging(Logging.SIGNTEXT)) { - long rowsToConvert = 0; - long done = 0; - try { - ResultSet rs = st.executeQuery("SELECT count(*) as rowcount FROM `" + wcfg.table + "-sign`"); - if (rs.next()) { - rowsToConvert = rs.getLong(1); - logblock.getLogger().info("Converting " + rowsToConvert + " entries in " + wcfg.table + "-sign"); - } - rs.close(); - - PreparedStatement insertSignState = conn.prepareStatement("INSERT INTO `" + wcfg.table + "-state` (id, replacedState, typeState) VALUES (?, ?, ?)"); - PreparedStatement deleteSign = conn.prepareStatement("DELETE FROM `" + wcfg.table + "-sign` WHERE id = ?"); - while (true) { - rs = st.executeQuery("SELECT id, signtext, replaced, type FROM `" + wcfg.table + "-sign` LEFT JOIN `" + wcfg.table + "-blocks` USING (id) ORDER BY id ASC LIMIT " + OTHER_CONVERT_BATCH_SIZE); - boolean anyRow = false; - while (rs.next()) { - anyRow = true; - long id = rs.getLong("id"); - String signText = rs.getString("signtext"); - int replaced = rs.getInt("replaced"); - boolean nullBlock = rs.wasNull(); - int type = rs.getInt("type"); - - if (!nullBlock && signText != null) { - String[] lines = signText.split("\0", 4); - byte[] bytes = Utils.serializeYamlConfiguration(signCodec.serialize(null, Side.FRONT, lines)); - - Material replacedMaterial = MaterialConverter.getBlockData(replaced, -1).getMaterial(); - Material typeMaterial = MaterialConverter.getBlockData(type, -1).getMaterial(); - boolean wasSign = replacedMaterial == Material.OAK_SIGN || replacedMaterial == Material.OAK_WALL_SIGN; - boolean isSign = typeMaterial == Material.OAK_SIGN || typeMaterial == Material.OAK_WALL_SIGN; - - insertSignState.setLong(1, id); - insertSignState.setBytes(2, wasSign ? bytes : null); - insertSignState.setBytes(3, isSign ? bytes : null); - insertSignState.addBatch(); - } - - deleteSign.setLong(1, id); - deleteSign.addBatch(); - done++; - } - rs.close(); - if (!anyRow) { - break; - } - int failedRows = 0; - try { - insertSignState.executeBatch(); - } catch (BatchUpdateException e) { - for (int result : e.getUpdateCounts()) { - if (result == Statement.EXECUTE_FAILED) { - failedRows++; - } - } - } - deleteSign.executeBatch(); - conn.commit(); - logblock.getLogger().info("Done: " + done + "/" + rowsToConvert + " " + (failedRows > 0 ? "Duplicates: " + failedRows + " " : "") + "(" + (rowsToConvert > 0 ? (done * 100 / rowsToConvert) : 100) + "%)"); - } - insertSignState.close(); - deleteSign.close(); - } catch (SQLException e) { - logblock.getLogger().info("Could not convert " + wcfg.table + "-sign: " + e.getMessage()); - } - } - } - - st.close(); - } catch (final SQLException ex) { - logblock.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); - return false; - } - - config.set("version", "1.13.1"); - } - - if (configVersion.compareTo(new ComparableVersion("1.16.0")) < 0) { - logblock.getLogger().info("Updating tables to 1.16.0 ..."); - try (Connection conn = logblock.getConnection()) { - conn.setAutoCommit(true); - final Statement st = conn.createStatement(); - for (final WorldConfig wcfg : getLoggedWorlds()) { - createIndexIfDoesNotExist(wcfg.table + "-entities", "entityid", "KEY `entityid` (entityid)", st, false); - } - st.close(); - } catch (final SQLException ex) { - logblock.getLogger().log(Level.SEVERE, "[Updater] Warning: Could not add index", ex); - } - config.set("version", "1.16.0"); - } - if (configVersion.compareTo(new ComparableVersion("1.17.0")) < 0) { - logblock.getLogger().info("Updating tables to 1.17.0 ..."); - logblock.getLogger().warning("The updating process might take several minutes if you have a huge log table! Please do not shutdown your server until it is completed."); - try (Connection conn = logblock.getConnection()) { - conn.setAutoCommit(true); - final Statement st = conn.createStatement(); - for (final WorldConfig wcfg : getLoggedWorlds()) { - st.executeUpdate("ALTER TABLE `" + wcfg.table + "-blocks` CHANGE `y` `y` SMALLINT(5) NOT NULL"); - } - st.close(); - } catch (final SQLException ex) { - logblock.getLogger().log(Level.SEVERE, "[Updater] Warning: Could not alter table", ex); - } - logblock.getLogger().info("Update to 1.17.0 completed."); - config.set("version", "1.17.0"); - } - - if (configVersion.compareTo(new ComparableVersion(Config.CURRENT_CONFIG_VERSION)) < 0) { - config.set("version", Config.CURRENT_CONFIG_VERSION); - } - - // this can always be checked - try (Connection conn = logblock.getConnection()) { - conn.setAutoCommit(true); - final Statement st = conn.createStatement(); - checkCharset("lb-players", "name", st, true); - if (isLogging(Logging.CHAT) || isLogging(Logging.PLAYER_COMMANDS) || isLogging(Logging.CONSOLE_COMMANDS) || isLogging(Logging.COMMANDBLOCK_COMMANDS)) { - checkCharset("lb-chat", "message", st, true); - } - createIndexIfDoesNotExist("lb-materials", "name", "UNIQUE KEY `name` (`name`(150))", st, true); - createIndexIfDoesNotExist("lb-blockstates", "name", "UNIQUE KEY `name` (`name`(150))", st, true); - - st.close(); - } catch (final SQLException ex) { - logblock.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); - return false; - } - try (Connection conn = logblock.getConnection()) { - conn.setAutoCommit(true); - final Statement st = conn.createStatement(); - PreparedStatement stSelectColumnType = conn.prepareStatement("SELECT `TABLE_NAME`, `COLUMN_TYPE` FROM information_schema.`COLUMNS` WHERE `TABLE_SCHEMA` = ? AND `COLUMN_NAME` = ?"); - stSelectColumnType.setString(1, Config.mysqlDatabase); - stSelectColumnType.setString(2, "y"); - HashMap tablesAndYColumnType = new HashMap<>(); - try (ResultSet rs = stSelectColumnType.executeQuery()) { - while (rs.next()) { - String table = rs.getString("TABLE_NAME").toLowerCase(); - String type = rs.getString("COLUMN_TYPE").toLowerCase(); - tablesAndYColumnType.put(table, type); - } - } - for (final WorldConfig wcfg : getLoggedWorlds()) { - String type = tablesAndYColumnType.get((wcfg.table + "-blocks").toLowerCase()); - if (type != null) { - if (type.contains("tinyint") || type.contains("unsigned")) { - logblock.getLogger().info("Fixing y column type for table " + wcfg.table + "-blocks ..."); - logblock.getLogger().warning("The updating process might take several minutes if you have a huge log table! Please do not shutdown your server until it is completed."); - st.executeUpdate("ALTER TABLE `" + wcfg.table + "-blocks` CHANGE `y` `y` SMALLINT(5) NOT NULL"); - } - } - type = tablesAndYColumnType.get((wcfg.table + "-entities").toLowerCase()); - if (type != null) { - if (type.contains("tinyint") || type.contains("unsigned")) { - logblock.getLogger().info("Fixing y column type for table " + wcfg.table + "-entities ..."); - logblock.getLogger().warning("The updating process might take several minutes if you have a huge log table! Please do not shutdown your server until it is completed."); - st.executeUpdate("ALTER TABLE `" + wcfg.table + "-entities` CHANGE `y` `y` SMALLINT(5) NOT NULL"); - } - } - type = tablesAndYColumnType.get((wcfg.table + "-kills").toLowerCase()); - if (type != null) { - if (type.contains("tinyint") || type.contains("unsigned")) { - logblock.getLogger().info("Fixing y column type for table " + wcfg.table + "-kills ..."); - logblock.getLogger().warning("The updating process might take several minutes if you have a huge log table! Please do not shutdown your server until it is completed."); - st.executeUpdate("ALTER TABLE `" + wcfg.table + "-kills` CHANGE `y` `y` SMALLINT(5) NOT NULL"); - } - } - } - st.close(); - conn.close(); - } catch (final SQLException ex) { - logblock.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); - return false; - } - - updateMaterialsPost1_13(); - - logblock.saveConfig(); - return true; - } - - void createIndexIfDoesNotExist(String table, String indexName, String definition, Statement st, boolean silent) throws SQLException { - final ResultSet rs = st.executeQuery("SHOW INDEX FROM `" + table + "` WHERE Key_name = '" + indexName + "'"); - if (!rs.next()) { - st.execute("ALTER TABLE `" + table + "` ADD " + definition); - logblock.getLogger().info("Add index " + indexName + " to table " + table + ": Table modified"); - } else if (!silent) { - logblock.getLogger().info("Add index " + indexName + " to table " + table + ": Already fine, skipping it"); - } - rs.close(); - } - - void checkCharset(String table, String column, Statement st, boolean silent) throws SQLException { - final ResultSet rs = st.executeQuery("SHOW FULL COLUMNS FROM `" + table + "` WHERE field = '" + column + "'"); - String charset = "utf8"; - if (Config.mb4) { - charset = "utf8mb4"; - } - if (rs.next() && !rs.getString("Collation").substring(0, charset.length()).equalsIgnoreCase(charset)) { - st.execute("ALTER TABLE `" + table + "` CONVERT TO CHARSET " + charset); - logblock.getLogger().info("Table " + table + " modified"); - } else if (!silent) { - logblock.getLogger().info("Table " + table + " already fine, skipping it"); - } - rs.close(); - } - - void checkTables() throws SQLException { - String charset = "utf8"; - if (Config.mb4) { - charset = "utf8mb4"; - } - final Connection conn = logblock.getConnection(); - if (conn == null) { - throw new SQLException("No connection"); - } - final Statement state = conn.createStatement(); - conn.setAutoCommit(true); - createTable(state, "lb-players", "(playerid INT UNSIGNED NOT NULL AUTO_INCREMENT, UUID varchar(36) NOT NULL, playername varchar(32) NOT NULL, firstlogin DATETIME NOT NULL, lastlogin DATETIME NOT NULL, onlinetime INT UNSIGNED NOT NULL, ip varchar(255) NOT NULL, PRIMARY KEY (playerid), INDEX (UUID), INDEX (playername)) DEFAULT CHARSET " + charset); - // Players table must not be empty or inserts won't work - bug #492 - final ResultSet rs = state.executeQuery("SELECT NULL FROM `lb-players` LIMIT 1;"); - if (!rs.next()) { - state.execute("INSERT IGNORE INTO `lb-players` (UUID,playername) VALUES ('log_dummy_record','dummy_record')"); - } - if (isLogging(Logging.CHAT) || isLogging(Logging.PLAYER_COMMANDS) || isLogging(Logging.CONSOLE_COMMANDS) || isLogging(Logging.COMMANDBLOCK_COMMANDS)) { - try { - createTable(state, "lb-chat", "(id INT UNSIGNED NOT NULL AUTO_INCREMENT, date DATETIME NOT NULL, playerid INT UNSIGNED NOT NULL, message VARCHAR(256) NOT NULL, PRIMARY KEY (id), KEY playerid (playerid), FULLTEXT message (message)) DEFAULT CHARSET " + charset); - } catch (SQLException e) { - createTable(state, "lb-chat", "(id INT UNSIGNED NOT NULL AUTO_INCREMENT, date DATETIME NOT NULL, playerid INT UNSIGNED NOT NULL, message VARCHAR(256) NOT NULL, PRIMARY KEY (id), KEY playerid (playerid)) DEFAULT CHARSET " + charset); - } - } - createTable(state, "lb-materials", "(id INT UNSIGNED NOT NULL, name VARCHAR(255) NOT NULL, PRIMARY KEY (id)) DEFAULT CHARSET " + charset); - createTable(state, "lb-blockstates", "(id INT UNSIGNED NOT NULL, name VARCHAR(255) NOT NULL, PRIMARY KEY (id)) DEFAULT CHARSET " + charset); - createTable(state, "lb-entitytypes", "(id INT UNSIGNED NOT NULL, name VARCHAR(255) NOT NULL, PRIMARY KEY (id)) DEFAULT CHARSET " + charset); - - for (final WorldConfig wcfg : getLoggedWorlds()) { - createTable(state, wcfg.table + "-blocks", "(id INT UNSIGNED NOT NULL AUTO_INCREMENT, date DATETIME NOT NULL, playerid INT UNSIGNED NOT NULL, replaced SMALLINT UNSIGNED NOT NULL, replacedData SMALLINT NOT NULL, type SMALLINT UNSIGNED NOT NULL, typeData SMALLINT NOT NULL, x MEDIUMINT NOT NULL, y SMALLINT NOT NULL, z MEDIUMINT NOT NULL, PRIMARY KEY (id), KEY coords (x, z, y), KEY date (date), KEY playerid (playerid))"); - createTable(state, wcfg.table + "-chestdata", "(id INT UNSIGNED NOT NULL, item MEDIUMBLOB, itemremove TINYINT, itemtype SMALLINT NOT NULL DEFAULT '0', PRIMARY KEY (id))"); - createTable(state, wcfg.table + "-state", "(id INT UNSIGNED NOT NULL, replacedState MEDIUMBLOB NULL, typeState MEDIUMBLOB NULL, PRIMARY KEY (id))"); - if (wcfg.isLogging(Logging.KILL)) { - createTable(state, wcfg.table + "-kills", "(id INT UNSIGNED NOT NULL AUTO_INCREMENT, date DATETIME NOT NULL, killer INT UNSIGNED, victim INT UNSIGNED NOT NULL, weapon SMALLINT UNSIGNED NOT NULL, x MEDIUMINT NOT NULL, y SMALLINT NOT NULL, z MEDIUMINT NOT NULL, PRIMARY KEY (id))"); - } - createTable(state, wcfg.table + "-entityids", "(entityid INT UNSIGNED NOT NULL AUTO_INCREMENT, entityuuid VARCHAR(36) CHARACTER SET ascii COLLATE ascii_bin NOT NULL, PRIMARY KEY (entityid), UNIQUE KEY (entityuuid))"); - createTable(state, wcfg.table + "-entities", "(id INT UNSIGNED NOT NULL AUTO_INCREMENT, date DATETIME NOT NULL, playerid INT UNSIGNED NOT NULL, entityid INT UNSIGNED NOT NULL, entitytypeid INT UNSIGNED NOT NULL, x MEDIUMINT NOT NULL, y SMALLINT NOT NULL, z MEDIUMINT NOT NULL, action TINYINT UNSIGNED NOT NULL, data MEDIUMBLOB NULL, PRIMARY KEY (id), KEY coords (x, z, y), KEY date (date), KEY playerid (playerid), KEY entityid (entityid))"); - } - state.close(); - conn.close(); - } - - private void createTable(Statement state, String table, String query) throws SQLException { - try (ResultSet tableResult = state.executeQuery("SHOW TABLES LIKE '" + table + "'")) { - if (!tableResult.next()) { - logblock.getLogger().log(Level.INFO, "Creating table " + table + "."); - state.execute("CREATE TABLE `" + table + "` " + query); - try (ResultSet tableResultNew = state.executeQuery("SHOW TABLES LIKE '" + table + "'")) { - if (!tableResultNew.next()) { - throw new SQLException("Table " + table + " not found and failed to create"); - } - } - } - } - } - - /** - * Update materials that were renamed - */ - private void updateMaterialsPost1_13() { - final ConfigurationSection config = logblock.getConfig(); - String previousMinecraftVersion = config.getString("previousMinecraftVersion"); - if (previousMinecraftVersion == null) { - previousMinecraftVersion = "1.13"; - } - ComparableVersion comparablePreviousMinecraftVersion = new ComparableVersion(previousMinecraftVersion); - String currentMinecraftVersion = logblock.getServer().getVersion(); - currentMinecraftVersion = currentMinecraftVersion.substring(currentMinecraftVersion.indexOf("(MC: ") + 5); - int currentVersionEnd = currentMinecraftVersion.indexOf(" "); - int currentVersionEnd2 = currentMinecraftVersion.indexOf(")"); - if (currentVersionEnd2 >= 0 && (currentVersionEnd < 0 || currentVersionEnd2 < currentVersionEnd)) { - currentVersionEnd = currentVersionEnd2; - } - currentMinecraftVersion = currentMinecraftVersion.substring(0, currentVersionEnd); - logblock.getLogger().info("[Updater] Current Minecraft Version: '" + currentMinecraftVersion + "'"); - ComparableVersion comparableCurrentMinecraftVersion = new ComparableVersion(currentMinecraftVersion); - - if (comparablePreviousMinecraftVersion.compareTo("1.14") < 0 && comparableCurrentMinecraftVersion.compareTo("1.14") >= 0) { - logblock.getLogger().info("[Updater] Upgrading Materials to 1.14"); - renameMaterial("minecraft:sign", Material.OAK_SIGN); - renameMaterial("minecraft:wall_sign", Material.OAK_WALL_SIGN); - renameMaterial("minecraft:stone_slab", Material.SMOOTH_STONE_SLAB); - renameMaterial("minecraft:rose_red", Material.RED_DYE); - renameMaterial("minecraft:dandelion_yellow", Material.YELLOW_DYE); - renameMaterial("minecraft:cactus_green", Material.GREEN_DYE); - } - - if (comparablePreviousMinecraftVersion.compareTo("1.17") < 0 && comparableCurrentMinecraftVersion.compareTo("1.17") >= 0) { - logblock.getLogger().info("[Updater] Upgrading Materials to 1.17"); - renameMaterial("minecraft:grass_path", Material.DIRT_PATH); - } - - config.set("previousMinecraftVersion", currentMinecraftVersion); - logblock.saveConfig(); - } - - private void renameMaterial(String oldName, Material newName) { - final Connection conn = logblock.getConnection(); - try { - conn.setAutoCommit(false); - PreparedStatement stSelectMaterial = conn.prepareStatement("SELECT id FROM `lb-materials` WHERE name = ?"); - stSelectMaterial.setString(1, oldName); - ResultSet rs = stSelectMaterial.executeQuery(); - if (rs.next()) { - logblock.getLogger().info("[Updater] Updating " + oldName + " to " + newName); - int oldId = rs.getInt(1); - int newId = MaterialConverter.getOrAddMaterialId(newName); - - Statement st = conn.createStatement(); - int rows = 0; - for (final WorldConfig wcfg : getLoggedWorlds()) { - rows += st.executeUpdate("UPDATE `" + wcfg.table + "-blocks` SET replaced = " + newId + " WHERE replaced = " + oldId); - rows += st.executeUpdate("UPDATE `" + wcfg.table + "-blocks` SET type = " + newId + " WHERE type = " + oldId); - rows += st.executeUpdate("UPDATE `" + wcfg.table + "-chestdata` SET itemtype = " + newId + " WHERE itemtype = " + oldId); - if (wcfg.isLogging(Logging.KILL)) { - rows += st.executeUpdate("UPDATE `" + wcfg.table + "-kills` SET weapon = " + newId + " WHERE weapon = " + oldId); - } - } - st.close(); - if (rows > 0) { - logblock.getLogger().info("[Updater] Successfully updated " + rows + " entries.."); - } - } - stSelectMaterial.close(); - conn.commit(); - conn.close(); - } catch (final SQLException ex) { - logblock.getLogger().log(Level.SEVERE, "[Updater] Error: " + ex.getMessage(), ex); - } - } - - public static class PlayerCountChecker implements Runnable { - - private LogBlock logblock; - - public PlayerCountChecker(LogBlock logblock) { - this.logblock = logblock; - } - - @Override - public void run() { - final Connection conn = logblock.getConnection(); - try { - conn.setAutoCommit(true); - final Statement st = conn.createStatement(); - ResultSet rs = st.executeQuery("SELECT auto_increment FROM information_schema.columns AS col join information_schema.tables AS tab ON (col.table_schema=tab.table_schema AND col.table_name=tab.table_name) WHERE col.table_name = 'lb-players' AND col.column_name = 'playerid' AND col.data_type = 'smallint' AND col.table_schema = DATABASE() AND auto_increment > 65000;"); - if (rs.next()) { - for (int i = 0; i < 6; i++) { - logblock.getLogger().warning("Your server reached 65000 players. You should soon update your database table schema - see FAQ: https://github.com/LogBlock/LogBlock/wiki/FAQ#logblock-your-server-reached-65000-players-"); - } - } - st.close(); - conn.close(); - } catch (final SQLException ex) { - if (logblock.isCompletelyEnabled()) { - logblock.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); - } - } - } - } - - public static class MaterialUpdater1_13 { - BlockData[][] blockDataMapping; - Material[][] itemMapping = new Material[10][]; - - public MaterialUpdater1_13(LogBlock plugin) throws IOException { - blockDataMapping = new BlockData[256][16]; - try (JarFile file = new JarFile(plugin.getFile())) { - BufferedReader reader = new BufferedReader(new InputStreamReader(new BufferedInputStream(file.getInputStream(file.getJarEntry("blockdata.txt"))), "UTF-8")); - while (true) { - String line = reader.readLine(); - if (line == null) { - break; - } - int splitter1 = line.indexOf(":"); - int splitter2 = line.indexOf(","); - if (splitter1 >= 0 && splitter2 >= 0) { - int blockid = Integer.parseInt(line.substring(0, splitter1)); - int blockdata = Integer.parseInt(line.substring(splitter1 + 1, splitter2)); - BlockData newBlockData = Bukkit.createBlockData(line.substring(splitter2 + 1)); - - if (blockdata == 0) { - for (int i = 0; i < 16; i++) { - if (blockDataMapping[blockid][i] == null) { - blockDataMapping[blockid][i] = newBlockData; - } - } - } else { - blockDataMapping[blockid][blockdata] = newBlockData; - } - } - } - reader.close(); - - HashMap materialKeysToMaterial = new HashMap<>(); - for (Material material : Material.values()) { - materialKeysToMaterial.put(material.getKey().toString(), material); - } - - reader = new BufferedReader(new InputStreamReader(new BufferedInputStream(file.getInputStream(file.getJarEntry("itemdata.txt"))), "UTF-8")); - while (true) { - String line = reader.readLine(); - if (line == null) { - break; - } - int splitter1 = line.indexOf(":"); - int splitter2 = line.indexOf(","); - if (splitter1 >= 0 && splitter2 >= 0) { - int itemid = Integer.parseInt(line.substring(0, splitter1)); - int itemdata = Integer.parseInt(line.substring(splitter1 + 1, splitter2)); - Material newMaterial = materialKeysToMaterial.get(line.substring(splitter2 + 1)); - if (newMaterial == null) { - throw new IOException("Unknown item: " + line.substring(splitter2 + 1)); - } - if (itemid >= itemMapping.length) { - itemMapping = Arrays.copyOf(itemMapping, Math.max(itemMapping.length * 3 / 2, itemid + 1)); - } - - Material[] itemValues = itemMapping[itemid]; - if (itemValues == null) { - itemValues = new Material[itemdata + 1]; - itemMapping[itemid] = itemValues; - } else if (itemValues.length <= itemdata) { - itemValues = Arrays.copyOf(itemValues, itemdata + 1); - itemMapping[itemid] = itemValues; - } - itemValues[itemdata] = newMaterial; - } - } - reader.close(); - } - } - - public BlockData getBlockData(int id, int data) { - return id >= 0 && id < 256 && data >= 0 && data < 16 ? blockDataMapping[id][data] : null; - } - - public Material getMaterial(int id, int data) { - Material[] materials = id >= 0 && id < itemMapping.length ? itemMapping[id] : null; - if (materials != null && materials.length > 0) { - if (materials[0] != null && materials[0].getMaxDurability() == 0 && data >= 0 && data < materials.length && materials[data] != null) { - return materials[data]; - } - return materials[0]; - } - return null; - } - - public Material getMaterial(String id) { - int item = 0; - int data = 0; - int seperator = id.indexOf(':'); - if (seperator < 0) { - item = Integer.parseInt(id); - } else { - item = Integer.parseInt(id.substring(0, seperator)); - data = Integer.parseInt(id.substring(seperator + 1)); - } - return getMaterial(item, data); - } - - public String convertMaterial(String oldEntry) { - if (oldEntry == null) { - return null; - } - try { - Material newMaterial = getMaterial(oldEntry); - if (newMaterial != null) { - return newMaterial.name(); - } - } catch (Exception e) { - Material newMaterial = Material.matchMaterial(oldEntry, true); - if (newMaterial != null) { - return newMaterial.name(); - } else { - newMaterial = Material.matchMaterial(oldEntry); - if (newMaterial != null) { - return newMaterial.name(); - } - } - } - return null; - } - - public List convertMaterials(Collection oldEntries) { - Set newEntries = new LinkedHashSet<>(); - for (String oldEntry : oldEntries) { - String newEntry = convertMaterial(oldEntry); - if (newEntry != null) { - newEntries.add(newEntry); - if (newEntry.equals(Material.AIR.name())) { - newEntries.add(Material.CAVE_AIR.name()); - newEntries.add(Material.VOID_AIR.name()); - } - } - } - return new ArrayList<>(newEntries); - } - } -} +package de.diddiz.LogBlock; + +import de.diddiz.LogBlock.blockstate.BlockStateCodecSign; +import de.diddiz.LogBlock.config.Config; +import de.diddiz.LogBlock.config.WorldConfig; +import de.diddiz.LogBlock.util.ComparableVersion; +import de.diddiz.LogBlock.util.UUIDFetcher; +import de.diddiz.LogBlock.util.Utils; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.block.data.BlockData; +import org.bukkit.block.sign.Side; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.inventory.ItemStack; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.sql.*; +import java.util.*; +import java.util.jar.JarFile; +import java.util.logging.Level; + +import static de.diddiz.LogBlock.config.Config.getLoggedWorlds; +import static de.diddiz.LogBlock.config.Config.isLogging; +import static de.diddiz.LogBlock.util.BukkitUtils.friendlyWorldname; + +class Updater { + private final LogBlock logblock; + final int UUID_CONVERT_BATCH_SIZE = 100; + final int BLOCKS_CONVERT_BATCH_SIZE = 100000; + final int OTHER_CONVERT_BATCH_SIZE = 20000; + + Updater(LogBlock logblock) { + this.logblock = logblock; + } + + boolean update() { + final ConfigurationSection config = logblock.getConfig(); + String versionString = config.getString("version"); + ComparableVersion configVersion = new ComparableVersion(versionString); + // if (configVersion.compareTo(new ComparableVersion(logblock.getDescription().getVersion().replace(" (manually compiled)", ""))) >= 0) { + // return false; + // } + if (configVersion.compareTo(new ComparableVersion("1.2.7")) < 0) { + logblock.getLogger().info("Updating tables to 1.2.7 ..."); + if (isLogging(Logging.CHAT) || isLogging(Logging.PLAYER_COMMANDS) || isLogging(Logging.CONSOLE_COMMANDS) || isLogging(Logging.COMMANDBLOCK_COMMANDS)) { + final Connection conn = logblock.getConnection(); + try { + conn.setAutoCommit(true); + final Statement st = conn.createStatement(); + st.execute("ALTER TABLE `lb-chat` ADD FULLTEXT message (message)"); + st.close(); + conn.close(); + } catch (final SQLException ex) { + logblock.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); + return false; + } + } + config.set("version", "1.2.7"); + } + if (configVersion.compareTo(new ComparableVersion("1.3")) < 0) { + logblock.getLogger().info("Updating config to 1.3.0 ..."); + for (final String tool : config.getConfigurationSection("tools").getKeys(false)) { + if (config.get("tools." + tool + ".permissionDefault") == null) { + config.set("tools." + tool + ".permissionDefault", "OP"); + } + } + config.set("version", "1.3.0"); + } + if (configVersion.compareTo(new ComparableVersion("1.3.1")) < 0) { + logblock.getLogger().info("Updating tables to 1.3.1 ..."); + final Connection conn = logblock.getConnection(); + try { + conn.setAutoCommit(true); + final Statement st = conn.createStatement(); + st.execute("ALTER TABLE `lb-players` ADD COLUMN lastlogin DATETIME NOT NULL, ADD COLUMN onlinetime TIME NOT NULL, ADD COLUMN ip VARCHAR(255) NOT NULL"); + st.close(); + conn.close(); + } catch (final SQLException ex) { + logblock.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); + return false; + } + config.set("version", "1.3.1"); + } + if (configVersion.compareTo(new ComparableVersion("1.3.2")) < 0) { + logblock.getLogger().info("Updating tables to 1.3.2 ..."); + final Connection conn = logblock.getConnection(); + try { + conn.setAutoCommit(true); + final Statement st = conn.createStatement(); + st.execute("ALTER TABLE `lb-players` ADD COLUMN firstlogin DATETIME NOT NULL AFTER playername"); + st.close(); + conn.close(); + } catch (final SQLException ex) { + logblock.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); + return false; + } + config.set("version", "1.3.2"); + } + if (configVersion.compareTo(new ComparableVersion("1.4")) < 0) { + logblock.getLogger().info("Updating config to 1.4.0 ..."); + config.set("clearlog.keepLogDays", null); + config.set("version", "1.4.0"); + } + if (configVersion.compareTo(new ComparableVersion("1.4.2")) < 0) { + logblock.getLogger().info("Updating config to 1.4.2 ..."); + for (final String world : config.getStringList("loggedWorlds")) { + final File file = new File(logblock.getDataFolder(), friendlyWorldname(world) + ".yml"); + final YamlConfiguration wcfg = YamlConfiguration.loadConfiguration(file); + if (wcfg.contains("logBlockCreations")) { + wcfg.set("logging.BLOCKPLACE", wcfg.getBoolean("logBlockCreations")); + } + if (wcfg.contains("logBlockDestroyings")) { + wcfg.set("logging.BLOCKBREAK", wcfg.getBoolean("logBlockDestroyings")); + } + if (wcfg.contains("logSignTexts")) { + wcfg.set("logging.SIGNTEXT", wcfg.getBoolean("logSignTexts")); + } + if (wcfg.contains("logFire")) { + wcfg.set("logging.FIRE", wcfg.getBoolean("logFire")); + } + if (wcfg.contains("logLeavesDecay")) { + wcfg.set("logging.LEAVESDECAY", wcfg.getBoolean("logLeavesDecay")); + } + if (wcfg.contains("logLavaFlow")) { + wcfg.set("logging.LAVAFLOW", wcfg.getBoolean("logLavaFlow")); + } + if (wcfg.contains("logWaterFlow")) { + wcfg.set("logging.WATERFLOW", wcfg.getBoolean("logWaterFlow")); + } + if (wcfg.contains("logChestAccess")) { + wcfg.set("logging.CHESTACCESS", wcfg.getBoolean("logChestAccess")); + } + if (wcfg.contains("logButtonsAndLevers")) { + wcfg.set("logging.SWITCHINTERACT", wcfg.getBoolean("logButtonsAndLevers")); + } + if (wcfg.contains("logKills")) { + wcfg.set("logging.KILL", wcfg.getBoolean("logKills")); + } + if (wcfg.contains("logChat")) { + wcfg.set("logging.CHAT", wcfg.getBoolean("logChat")); + } + if (wcfg.contains("logSnowForm")) { + wcfg.set("logging.SNOWFORM", wcfg.getBoolean("logSnowForm")); + } + if (wcfg.contains("logSnowFade")) { + wcfg.set("logging.SNOWFADE", wcfg.getBoolean("logSnowFade")); + } + if (wcfg.contains("logDoors")) { + wcfg.set("logging.DOORINTERACT", wcfg.getBoolean("logDoors")); + } + if (wcfg.contains("logCakes")) { + wcfg.set("logging.CAKEEAT", wcfg.getBoolean("logCakes")); + } + if (wcfg.contains("logEndermen")) { + wcfg.set("logging.ENDERMEN", wcfg.getBoolean("logEndermen")); + } + if (wcfg.contains("logExplosions")) { + final boolean logExplosions = wcfg.getBoolean("logExplosions"); + wcfg.set("logging.TNTEXPLOSION", logExplosions); + wcfg.set("logging.MISCEXPLOSION", logExplosions); + wcfg.set("logging.CREEPEREXPLOSION", logExplosions); + wcfg.set("logging.GHASTFIREBALLEXPLOSION", logExplosions); + } + wcfg.set("logBlockCreations", null); + wcfg.set("logBlockDestroyings", null); + wcfg.set("logSignTexts", null); + wcfg.set("logExplosions", null); + wcfg.set("logFire", null); + wcfg.set("logLeavesDecay", null); + wcfg.set("logLavaFlow", null); + wcfg.set("logWaterFlow", null); + wcfg.set("logChestAccess", null); + wcfg.set("logButtonsAndLevers", null); + wcfg.set("logKills", null); + wcfg.set("logChat", null); + wcfg.set("logSnowForm", null); + wcfg.set("logSnowFade", null); + wcfg.set("logDoors", null); + wcfg.set("logCakes", null); + wcfg.set("logEndermen", null); + try { + wcfg.save(file); + } catch (final IOException ex) { + logblock.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); + } + } + config.set("clearlog.keepLogDays", null); + config.set("version", "1.4.2"); + } + if (configVersion.compareTo(new ComparableVersion("1.5.1")) < 0) { + logblock.getLogger().info("Updating tables to 1.5.1 ..."); + final Connection conn = logblock.getConnection(); + try { + conn.setAutoCommit(true); + final Statement st = conn.createStatement(); + for (final WorldConfig wcfg : getLoggedWorlds()) { + if (wcfg.isLogging(Logging.KILL)) { + st.execute("ALTER TABLE `" + wcfg.table + "-kills` ADD (x MEDIUMINT NOT NULL DEFAULT 0, y SMALLINT NOT NULL DEFAULT 0, z MEDIUMINT NOT NULL DEFAULT 0)"); + } + } + st.close(); + conn.close(); + } catch (final SQLException ex) { + logblock.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); + return false; + } + config.set("version", "1.5.1"); + } + if (configVersion.compareTo(new ComparableVersion("1.5.2")) < 0) { + logblock.getLogger().info("Updating tables to 1.5.2 ..."); + final Connection conn = logblock.getConnection(); + try { + conn.setAutoCommit(true); + final Statement st = conn.createStatement(); + final ResultSet rs = st.executeQuery("SHOW COLUMNS FROM `lb-players` WHERE field = 'onlinetime'"); + if (rs.next() && rs.getString("Type").equalsIgnoreCase("time")) { + st.execute("ALTER TABLE `lb-players` ADD onlinetime2 INT UNSIGNED NOT NULL"); + st.execute("UPDATE `lb-players` SET onlinetime2 = HOUR(onlinetime) * 3600 + MINUTE(onlinetime) * 60 + SECOND(onlinetime)"); + st.execute("ALTER TABLE `lb-players` DROP onlinetime"); + st.execute("ALTER TABLE `lb-players` CHANGE onlinetime2 onlinetime INT UNSIGNED NOT NULL"); + } else { + logblock.getLogger().info("Column lb-players was already modified, skipping it."); + } + st.close(); + conn.close(); + } catch (final SQLException ex) { + logblock.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); + return false; + } + config.set("version", "1.5.2"); + } + if (configVersion.compareTo(new ComparableVersion("1.8.1")) < 0) { + logblock.getLogger().info("Updating tables to 1.8.1 ..."); + final Connection conn = logblock.getConnection(); + try { + conn.setAutoCommit(true); + final Statement st = conn.createStatement(); + for (final WorldConfig wcfg : getLoggedWorlds()) { + if (wcfg.isLogging(Logging.CHESTACCESS)) { + st.execute("ALTER TABLE `" + wcfg.table + "-chest` CHANGE itemdata itemdata SMALLINT NOT NULL"); + logblock.getLogger().info("Table " + wcfg.table + "-chest modified"); + } + } + st.close(); + conn.close(); + } catch (final SQLException ex) { + logblock.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); + return false; + } + config.set("version", "1.8.1"); + } + + if (configVersion.compareTo(new ComparableVersion("1.9")) < 0) { + logblock.getLogger().info("Updating tables to 1.9.0 ..."); + logblock.getLogger().info("Importing UUIDs for large databases may take some time"); + final Connection conn = logblock.getConnection(); + try { + conn.setAutoCommit(true); + final Statement st = conn.createStatement(); + st.execute("ALTER TABLE `lb-players` ADD `UUID` VARCHAR(36) NOT NULL"); + } catch (final SQLException ex) { + // Error 1060 is MySQL error "column already exists". We want to continue with import if we get that error + if (ex.getErrorCode() != 1060) { + logblock.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); + return false; + } + } + try { + String unimportedPrefix = "noimport_"; + ResultSet rs; + conn.setAutoCommit(true); + final Statement st = conn.createStatement(); + if (config.getBoolean("logging.logPlayerInfo")) { + // Start by assuming anything with no onlinetime is not a player + st.execute("UPDATE `lb-players` SET UUID = CONCAT ('log_',playername) WHERE onlinetime=0 AND LENGTH(UUID) = 0"); + } else { + // If we can't assume that, we must assume anything we can't look up is not a player + unimportedPrefix = "log_"; + } + // Tell people how many are needing converted + rs = st.executeQuery("SELECT COUNT(playername) FROM `lb-players` WHERE LENGTH(UUID)=0"); + rs.next(); + String total = Integer.toString(rs.getInt(1)); + logblock.getLogger().info(total + " players to convert"); + int done = 0; + + conn.setAutoCommit(false); + Map players = new HashMap<>(); + List names = new ArrayList<>(UUID_CONVERT_BATCH_SIZE); + Map response; + rs = st.executeQuery("SELECT playerid,playername FROM `lb-players` WHERE LENGTH(UUID)=0 LIMIT " + Integer.toString(UUID_CONVERT_BATCH_SIZE)); + while (rs.next()) { + do { + players.put(rs.getString(2), rs.getInt(1)); + names.add(rs.getString(2)); + } while (rs.next()); + if (names.size() > 0) { + String theUUID; + response = UUIDFetcher.getUUIDs(names); + for (Map.Entry entry : players.entrySet()) { + if (response.get(entry.getKey()) == null) { + theUUID = unimportedPrefix + entry.getKey(); + logblock.getLogger().warning(entry.getKey() + " not found - giving UUID of " + theUUID); + } else { + theUUID = response.get(entry.getKey()).toString(); + } + String thePID = entry.getValue().toString(); + st.execute("UPDATE `lb-players` SET UUID = '" + theUUID + "' WHERE playerid = " + thePID); + done++; + } + conn.commit(); + players.clear(); + names.clear(); + logblock.getLogger().info("Processed " + Integer.toString(done) + " out of " + total); + rs.close(); + rs = st.executeQuery("SELECT playerid,playername FROM `lb-players` WHERE LENGTH(UUID)=0 LIMIT " + Integer.toString(UUID_CONVERT_BATCH_SIZE)); + } + } + rs.close(); + st.close(); + conn.close(); + + } catch (final SQLException ex) { + logblock.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); + return false; + } catch (Exception ex) { + logblock.getLogger().log(Level.SEVERE, "[UUID importer]", ex); + return false; + } + config.set("version", "1.9.0"); + } + if (configVersion.compareTo(new ComparableVersion("1.9.4")) < 0) { + logblock.getLogger().info("Updating tables to 1.9.4 ..."); + final Connection conn = logblock.getConnection(); + try { + conn.setAutoCommit(true); + final Statement st = conn.createStatement(); + // Need to wrap both these next two inside individual try/catch statements in case index does not exist + try { + st.execute("DROP INDEX UUID ON `lb-players`"); + } catch (final SQLException ex) { + if (ex.getErrorCode() != 1091) { + logblock.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); + return false; + } + } + try { + st.execute("DROP INDEX playername ON `lb-players`"); + } catch (final SQLException ex) { + if (ex.getErrorCode() != 1091) { + logblock.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); + return false; + } + } + st.execute("CREATE INDEX UUID ON `lb-players` (UUID);"); + st.execute("CREATE INDEX playername ON `lb-players` (playername);"); + st.close(); + conn.close(); + } catch (final SQLException ex) { + logblock.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); + return false; + } + config.set("version", "1.9.4"); + } + // Ensure charset for free-text fields is UTF-8, or UTF8-mb4 if possible + // As this may be an expensive operation and the database default may already be this, check on a table-by-table basis before converting + if (configVersion.compareTo(new ComparableVersion("1.10.0")) < 0) { + logblock.getLogger().info("Updating tables to 1.10.0 ..."); + final Connection conn = logblock.getConnection(); + try { + conn.setAutoCommit(true); + final Statement st = conn.createStatement(); + checkCharset("lb-players", "name", st, false); + if (isLogging(Logging.CHAT) || isLogging(Logging.PLAYER_COMMANDS) || isLogging(Logging.CONSOLE_COMMANDS) || isLogging(Logging.COMMANDBLOCK_COMMANDS)) { + checkCharset("lb-chat", "message", st, false); + } + for (final WorldConfig wcfg : getLoggedWorlds()) { + if (wcfg.isLogging(Logging.SIGNTEXT)) { + // checkCharset(wcfg.table + "-sign","signtext",st); + } + } + st.close(); + conn.close(); + } catch (final SQLException ex) { + logblock.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); + return false; + } + config.set("version", "1.10.0"); + } + + if (configVersion.compareTo(new ComparableVersion("1.12.0")) < 0) { + logblock.getLogger().info("Updating tables to 1.12.0 ..."); + if (isLogging(Logging.CHAT) || isLogging(Logging.PLAYER_COMMANDS) || isLogging(Logging.CONSOLE_COMMANDS) || isLogging(Logging.COMMANDBLOCK_COMMANDS)) { + final Connection conn = logblock.getConnection(); + try { + conn.setAutoCommit(true); + final Statement st = conn.createStatement(); + st.execute("ALTER TABLE `lb-chat` MODIFY COLUMN `message` VARCHAR(256) NOT NULL"); + st.close(); + conn.close(); + } catch (final SQLException ex) { + logblock.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); + return false; + } + } + config.set("version", "1.12.0"); + } + if (configVersion.compareTo(new ComparableVersion("1.13.0")) < 0) { + logblock.getLogger().info("Updating tables to 1.13.0 ..."); + try { + MaterialUpdater1_13 materialUpdater = new MaterialUpdater1_13(logblock); + logblock.getLogger().info("Convertig BlockId to BlockData. This can take a while ..."); + final Connection conn = logblock.getConnection(); + conn.setAutoCommit(false); + final Statement st = conn.createStatement(); + for (final WorldConfig wcfg : getLoggedWorlds()) { + logblock.getLogger().info("Processing world " + wcfg.world + "..."); + logblock.getLogger().info("Processing block changes..."); + boolean hadRow = true; + long rowsToConvert = 0; + long done = 0; + try { + ResultSet rs = st.executeQuery("SELECT count(*) as rowcount FROM `" + wcfg.table + "`"); + if (rs.next()) { + rowsToConvert = rs.getLong(1); + logblock.getLogger().info("Converting " + rowsToConvert + " entries in " + wcfg.table); + } + rs.close(); + + PreparedStatement deleteStatement = conn.prepareStatement("DELETE FROM `" + wcfg.table + "` WHERE id = ?"); + PreparedStatement insertStatement = conn.prepareStatement("INSERT INTO `" + wcfg.table + "-blocks` (id, date, playerid, replaced, replacedData, type, typeData, x, y, z) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS); + + while (hadRow) { + hadRow = false; + ResultSet entries = st.executeQuery("SELECT id, date, playerid, replaced, type, data, x, y, z FROM `" + wcfg.table + "` ORDER BY id ASC LIMIT " + BLOCKS_CONVERT_BATCH_SIZE); + while (entries.next()) { + hadRow = true; + long id = entries.getLong("id"); + Timestamp date = entries.getTimestamp("date"); + int playerid = entries.getInt("playerid"); + int replaced = entries.getInt("replaced"); + int type = entries.getInt("type"); + int data = entries.getInt("data"); + int x = entries.getInt("x"); + int y = entries.getInt("y"); + int z = entries.getInt("z"); + if (data == 16) { + data = 0; + } + + try { + BlockData replacedBlockData = materialUpdater.getBlockData(replaced, data); + BlockData setBlockData = materialUpdater.getBlockData(type, data); + + int newReplacedId = MaterialConverter.getOrAddMaterialId(replacedBlockData); + int newReplacedData = MaterialConverter.getOrAddBlockStateId(replacedBlockData); + + int newSetId = MaterialConverter.getOrAddMaterialId(setBlockData); + int newSetData = MaterialConverter.getOrAddBlockStateId(setBlockData); + + insertStatement.setLong(1, id); + insertStatement.setTimestamp(2, date); + insertStatement.setInt(3, playerid); + insertStatement.setInt(4, newReplacedId); + insertStatement.setInt(5, newReplacedData); + insertStatement.setInt(6, newSetId); + insertStatement.setInt(7, newSetData); + insertStatement.setInt(8, x); + insertStatement.setInt(9, y); + insertStatement.setInt(10, z); + insertStatement.addBatch(); + } catch (Exception e) { + logblock.getLogger().info("Exception in entry " + id + " (" + replaced + ":" + data + "->" + type + ":" + data + "): " + e.getMessage()); + } + deleteStatement.setLong(1, id); + deleteStatement.addBatch(); + + done++; + } + entries.close(); + int failedRows = 0; + if (hadRow) { + try { + insertStatement.executeBatch(); + } catch (BatchUpdateException e) { + for (int result : e.getUpdateCounts()) { + if (result == Statement.EXECUTE_FAILED) { + failedRows++; + } + } + } + deleteStatement.executeBatch(); + } + conn.commit(); + logblock.getLogger().info("Done: " + done + "/" + rowsToConvert + " " + (failedRows > 0 ? "Duplicates: " + failedRows + " " : "") + "(" + (rowsToConvert > 0 ? (done * 100 / rowsToConvert) : 100) + "%)"); + } + insertStatement.close(); + deleteStatement.close(); + } catch (SQLException e) { + logblock.getLogger().info("Could not convert " + wcfg.table + ": " + e.getMessage()); + } + + logblock.getLogger().info("Processing chests..."); + rowsToConvert = 0; + done = 0; + try { + ResultSet rs = st.executeQuery("SELECT count(*) as rowcount FROM `" + wcfg.table + "-chest`"); + if (rs.next()) { + rowsToConvert = rs.getLong(1); + logblock.getLogger().info("Converting " + rowsToConvert + " entries in " + wcfg.table + "-chest"); + } + rs.close(); + + PreparedStatement insertChestData = conn.prepareStatement("INSERT INTO `" + wcfg.table + "-chestdata` (id, item, itemremove, itemtype) VALUES (?, ?, ?, ?)"); + PreparedStatement deleteChest = conn.prepareStatement("DELETE FROM `" + wcfg.table + "-chest` WHERE id = ?"); + while (true) { + rs = st.executeQuery("SELECT id, itemtype, itemamount, itemdata FROM `" + wcfg.table + "-chest` ORDER BY id ASC LIMIT " + OTHER_CONVERT_BATCH_SIZE); + boolean anyRow = false; + while (rs.next()) { + anyRow = true; + long id = rs.getLong("id"); + int itemtype = rs.getInt("itemtype"); + int itemdata = rs.getInt("itemdata"); + int amount = rs.getInt("itemamount"); + Material weaponMaterial = materialUpdater.getMaterial(itemtype, itemdata); + if (weaponMaterial == null) { + weaponMaterial = Material.AIR; + } + @SuppressWarnings("deprecation") + ItemStack stack = weaponMaterial.getMaxDurability() > 0 ? new ItemStack(weaponMaterial, Math.abs(amount), (short) itemdata) : new ItemStack(weaponMaterial, Math.abs(amount)); + insertChestData.setLong(1, id); + insertChestData.setBytes(2, Utils.saveItemStack(stack)); + insertChestData.setInt(3, amount >= 0 ? 0 : 1); + insertChestData.setInt(4, MaterialConverter.getOrAddMaterialId(weaponMaterial)); + insertChestData.addBatch(); + + deleteChest.setLong(1, id); + deleteChest.addBatch(); + done++; + } + rs.close(); + if (!anyRow) { + break; + } + int failedRows = 0; + try { + insertChestData.executeBatch(); + } catch (BatchUpdateException e) { + for (int result : e.getUpdateCounts()) { + if (result == Statement.EXECUTE_FAILED) { + failedRows++; + } + } + } + deleteChest.executeBatch(); + conn.commit(); + logblock.getLogger().info("Done: " + done + "/" + rowsToConvert + " " + (failedRows > 0 ? "Duplicates: " + failedRows + " " : "") + "(" + (rowsToConvert > 0 ? (done * 100 / rowsToConvert) : 100) + "%)"); + } + insertChestData.close(); + deleteChest.close(); + } catch (SQLException e) { + logblock.getLogger().info("Could not convert " + wcfg.table + "-chest: " + e.getMessage()); + } + + if (wcfg.isLogging(Logging.KILL)) { + logblock.getLogger().info("Processing kills..."); + rowsToConvert = 0; + done = 0; + try { + ResultSet rs = st.executeQuery("SELECT count(*) as rowcount FROM `" + wcfg.table + "-kills`"); + if (rs.next()) { + rowsToConvert = rs.getLong(1); + logblock.getLogger().info("Converting " + rowsToConvert + " entries in " + wcfg.table + "-kills"); + } + rs.close(); + + PreparedStatement updateWeaponStatement = conn.prepareStatement("UPDATE `" + wcfg.table + "-kills` SET weapon = ? WHERE id = ?"); + for (int start = 0;; start += OTHER_CONVERT_BATCH_SIZE) { + rs = st.executeQuery("SELECT id, weapon FROM `" + wcfg.table + "-kills` ORDER BY id ASC LIMIT " + start + "," + OTHER_CONVERT_BATCH_SIZE); + boolean anyUpdate = false; + boolean anyRow = false; + while (rs.next()) { + anyRow = true; + long id = rs.getLong("id"); + int weapon = rs.getInt("weapon"); + Material weaponMaterial = materialUpdater.getMaterial(weapon, 0); + if (weaponMaterial == null) { + weaponMaterial = Material.AIR; + } + int newWeapon = MaterialConverter.getOrAddMaterialId(weaponMaterial); + if (newWeapon != weapon) { + anyUpdate = true; + updateWeaponStatement.setInt(1, newWeapon); + updateWeaponStatement.setLong(2, id); + updateWeaponStatement.addBatch(); + } + done++; + } + rs.close(); + if (anyUpdate) { + updateWeaponStatement.executeBatch(); + conn.commit(); + } + logblock.getLogger().info("Done: " + done + "/" + rowsToConvert + " (" + (rowsToConvert > 0 ? (done * 100 / rowsToConvert) : 100) + "%)"); + if (!anyRow) { + break; + } + } + updateWeaponStatement.close(); + } catch (SQLException e) { + logblock.getLogger().info("Could not convert " + wcfg.table + "-kills: " + e.getMessage()); + } + } + } + st.close(); + conn.close(); + + logblock.getLogger().info("Updating config to 1.13.0 ..."); + config.set("logging.hiddenBlocks", materialUpdater.convertMaterials(config.getStringList("logging.hiddenBlocks"))); + config.set("rollback.dontRollback", materialUpdater.convertMaterials(config.getStringList("rollback.dontRollback"))); + config.set("rollback.replaceAnyway", materialUpdater.convertMaterials(config.getStringList("rollback.replaceAnyway"))); + final ConfigurationSection toolsSec = config.getConfigurationSection("tools"); + for (final String toolName : toolsSec.getKeys(false)) { + final ConfigurationSection tSec = toolsSec.getConfigurationSection(toolName); + tSec.set("item", materialUpdater.convertMaterial(tSec.getString("item", "OAK_LOG"))); + } + } catch (final SQLException | IOException ex) { + logblock.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); + return false; + } + config.set("version", "1.13.0"); + } + + if (configVersion.compareTo(new ComparableVersion("1.13.1")) < 0) { + logblock.getLogger().info("Updating tables to 1.13.1 ..."); + BlockStateCodecSign signCodec = new BlockStateCodecSign(); + try (Connection conn = logblock.getConnection()) { + conn.setAutoCommit(false); + final Statement st = conn.createStatement(); + for (final WorldConfig wcfg : getLoggedWorlds()) { + logblock.getLogger().info("Processing world " + wcfg.world + "..."); + ResultSet rsCol = st.executeQuery("SHOW COLUMNS FROM `" + wcfg.table + "-chestdata` LIKE 'itemtype'"); + if (!rsCol.next()) { + st.execute("ALTER TABLE `" + wcfg.table + "-chestdata` ADD COLUMN `itemtype` SMALLINT NOT NULL DEFAULT '0'"); + } + rsCol.close(); + conn.commit(); + if (wcfg.isLogging(Logging.SIGNTEXT)) { + long rowsToConvert = 0; + long done = 0; + try { + ResultSet rs = st.executeQuery("SELECT count(*) as rowcount FROM `" + wcfg.table + "-sign`"); + if (rs.next()) { + rowsToConvert = rs.getLong(1); + logblock.getLogger().info("Converting " + rowsToConvert + " entries in " + wcfg.table + "-sign"); + } + rs.close(); + + PreparedStatement insertSignState = conn.prepareStatement("INSERT INTO `" + wcfg.table + "-state` (id, replacedState, typeState) VALUES (?, ?, ?)"); + PreparedStatement deleteSign = conn.prepareStatement("DELETE FROM `" + wcfg.table + "-sign` WHERE id = ?"); + while (true) { + rs = st.executeQuery("SELECT id, signtext, replaced, type FROM `" + wcfg.table + "-sign` LEFT JOIN `" + wcfg.table + "-blocks` USING (id) ORDER BY id ASC LIMIT " + OTHER_CONVERT_BATCH_SIZE); + boolean anyRow = false; + while (rs.next()) { + anyRow = true; + long id = rs.getLong("id"); + String signText = rs.getString("signtext"); + int replaced = rs.getInt("replaced"); + boolean nullBlock = rs.wasNull(); + int type = rs.getInt("type"); + + if (!nullBlock && signText != null) { + String[] lines = signText.split("\0", 4); + byte[] bytes = Utils.serializeYamlConfiguration(signCodec.serialize(null, Side.FRONT, lines)); + + Material replacedMaterial = MaterialConverter.getBlockData(replaced, -1).getMaterial(); + Material typeMaterial = MaterialConverter.getBlockData(type, -1).getMaterial(); + boolean wasSign = replacedMaterial == Material.OAK_SIGN || replacedMaterial == Material.OAK_WALL_SIGN; + boolean isSign = typeMaterial == Material.OAK_SIGN || typeMaterial == Material.OAK_WALL_SIGN; + + insertSignState.setLong(1, id); + insertSignState.setBytes(2, wasSign ? bytes : null); + insertSignState.setBytes(3, isSign ? bytes : null); + insertSignState.addBatch(); + } + + deleteSign.setLong(1, id); + deleteSign.addBatch(); + done++; + } + rs.close(); + if (!anyRow) { + break; + } + int failedRows = 0; + try { + insertSignState.executeBatch(); + } catch (BatchUpdateException e) { + for (int result : e.getUpdateCounts()) { + if (result == Statement.EXECUTE_FAILED) { + failedRows++; + } + } + } + deleteSign.executeBatch(); + conn.commit(); + logblock.getLogger().info("Done: " + done + "/" + rowsToConvert + " " + (failedRows > 0 ? "Duplicates: " + failedRows + " " : "") + "(" + (rowsToConvert > 0 ? (done * 100 / rowsToConvert) : 100) + "%)"); + } + insertSignState.close(); + deleteSign.close(); + } catch (SQLException e) { + logblock.getLogger().info("Could not convert " + wcfg.table + "-sign: " + e.getMessage()); + } + } + } + + st.close(); + } catch (final SQLException ex) { + logblock.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); + return false; + } + + config.set("version", "1.13.1"); + } + + if (configVersion.compareTo(new ComparableVersion("1.16.0")) < 0) { + logblock.getLogger().info("Updating tables to 1.16.0 ..."); + try (Connection conn = logblock.getConnection()) { + conn.setAutoCommit(true); + final Statement st = conn.createStatement(); + for (final WorldConfig wcfg : getLoggedWorlds()) { + createIndexIfDoesNotExist(wcfg.table + "-entities", "entityid", "KEY `entityid` (entityid)", st, false); + } + st.close(); + } catch (final SQLException ex) { + logblock.getLogger().log(Level.SEVERE, "[Updater] Warning: Could not add index", ex); + } + config.set("version", "1.16.0"); + } + if (configVersion.compareTo(new ComparableVersion("1.17.0")) < 0) { + logblock.getLogger().info("Updating tables to 1.17.0 ..."); + logblock.getLogger().warning("The updating process might take several minutes if you have a huge log table! Please do not shutdown your server until it is completed."); + try (Connection conn = logblock.getConnection()) { + conn.setAutoCommit(true); + final Statement st = conn.createStatement(); + for (final WorldConfig wcfg : getLoggedWorlds()) { + st.executeUpdate("ALTER TABLE `" + wcfg.table + "-blocks` CHANGE `y` `y` SMALLINT(5) NOT NULL"); + } + st.close(); + } catch (final SQLException ex) { + logblock.getLogger().log(Level.SEVERE, "[Updater] Warning: Could not alter table", ex); + } + logblock.getLogger().info("Update to 1.17.0 completed."); + config.set("version", "1.17.0"); + } + + if (configVersion.compareTo(new ComparableVersion(Config.CURRENT_CONFIG_VERSION)) < 0) { + config.set("version", Config.CURRENT_CONFIG_VERSION); + } + + // this can always be checked + try (Connection conn = logblock.getConnection()) { + conn.setAutoCommit(true); + final Statement st = conn.createStatement(); + checkCharset("lb-players", "name", st, true); + if (isLogging(Logging.CHAT) || isLogging(Logging.PLAYER_COMMANDS) || isLogging(Logging.CONSOLE_COMMANDS) || isLogging(Logging.COMMANDBLOCK_COMMANDS)) { + checkCharset("lb-chat", "message", st, true); + } + createIndexIfDoesNotExist("lb-materials", "name", "UNIQUE KEY `name` (`name`(150))", st, true); + createIndexIfDoesNotExist("lb-blockstates", "name", "UNIQUE KEY `name` (`name`(150))", st, true); + + st.close(); + } catch (final SQLException ex) { + logblock.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); + return false; + } + try (Connection conn = logblock.getConnection()) { + conn.setAutoCommit(true); + final Statement st = conn.createStatement(); + PreparedStatement stSelectColumnType = conn.prepareStatement("SELECT `TABLE_NAME`, `COLUMN_TYPE` FROM information_schema.`COLUMNS` WHERE `TABLE_SCHEMA` = ? AND `COLUMN_NAME` = ?"); + stSelectColumnType.setString(1, Config.mysqlDatabase); + stSelectColumnType.setString(2, "y"); + HashMap tablesAndYColumnType = new HashMap<>(); + try (ResultSet rs = stSelectColumnType.executeQuery()) { + while (rs.next()) { + String table = rs.getString("TABLE_NAME").toLowerCase(); + String type = rs.getString("COLUMN_TYPE").toLowerCase(); + tablesAndYColumnType.put(table, type); + } + } + for (final WorldConfig wcfg : getLoggedWorlds()) { + String type = tablesAndYColumnType.get((wcfg.table + "-blocks").toLowerCase()); + if (type != null) { + if (type.contains("tinyint") || type.contains("unsigned")) { + logblock.getLogger().info("Fixing y column type for table " + wcfg.table + "-blocks ..."); + logblock.getLogger().warning("The updating process might take several minutes if you have a huge log table! Please do not shutdown your server until it is completed."); + st.executeUpdate("ALTER TABLE `" + wcfg.table + "-blocks` CHANGE `y` `y` SMALLINT(5) NOT NULL"); + } + } + type = tablesAndYColumnType.get((wcfg.table + "-entities").toLowerCase()); + if (type != null) { + if (type.contains("tinyint") || type.contains("unsigned")) { + logblock.getLogger().info("Fixing y column type for table " + wcfg.table + "-entities ..."); + logblock.getLogger().warning("The updating process might take several minutes if you have a huge log table! Please do not shutdown your server until it is completed."); + st.executeUpdate("ALTER TABLE `" + wcfg.table + "-entities` CHANGE `y` `y` SMALLINT(5) NOT NULL"); + } + } + type = tablesAndYColumnType.get((wcfg.table + "-kills").toLowerCase()); + if (type != null) { + if (type.contains("tinyint") || type.contains("unsigned")) { + logblock.getLogger().info("Fixing y column type for table " + wcfg.table + "-kills ..."); + logblock.getLogger().warning("The updating process might take several minutes if you have a huge log table! Please do not shutdown your server until it is completed."); + st.executeUpdate("ALTER TABLE `" + wcfg.table + "-kills` CHANGE `y` `y` SMALLINT(5) NOT NULL"); + } + } + } + st.close(); + conn.close(); + } catch (final SQLException ex) { + logblock.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); + return false; + } + + updateMaterialsPost1_13(); + + logblock.saveConfig(); + return true; + } + + void createIndexIfDoesNotExist(String table, String indexName, String definition, Statement st, boolean silent) throws SQLException { + final ResultSet rs = st.executeQuery("SHOW INDEX FROM `" + table + "` WHERE Key_name = '" + indexName + "'"); + if (!rs.next()) { + st.execute("ALTER TABLE `" + table + "` ADD " + definition); + logblock.getLogger().info("Add index " + indexName + " to table " + table + ": Table modified"); + } else if (!silent) { + logblock.getLogger().info("Add index " + indexName + " to table " + table + ": Already fine, skipping it"); + } + rs.close(); + } + + void checkCharset(String table, String column, Statement st, boolean silent) throws SQLException { + final ResultSet rs = st.executeQuery("SHOW FULL COLUMNS FROM `" + table + "` WHERE field = '" + column + "'"); + String charset = "utf8"; + if (Config.mb4) { + charset = "utf8mb4"; + } + if (rs.next() && !rs.getString("Collation").substring(0, charset.length()).equalsIgnoreCase(charset)) { + st.execute("ALTER TABLE `" + table + "` CONVERT TO CHARSET " + charset); + logblock.getLogger().info("Table " + table + " modified"); + } else if (!silent) { + logblock.getLogger().info("Table " + table + " already fine, skipping it"); + } + rs.close(); + } + + void checkTables() throws SQLException { + String charset = "utf8"; + if (Config.mb4) { + charset = "utf8mb4"; + } + final Connection conn = logblock.getConnection(); + if (conn == null) { + throw new SQLException("No connection"); + } + final Statement state = conn.createStatement(); + conn.setAutoCommit(true); + createTable(state, "lb-players", "(playerid INT UNSIGNED NOT NULL AUTO_INCREMENT, UUID varchar(36) NOT NULL, playername varchar(32) NOT NULL, firstlogin DATETIME NOT NULL, lastlogin DATETIME NOT NULL, onlinetime INT UNSIGNED NOT NULL, ip varchar(255) NOT NULL, PRIMARY KEY (playerid), INDEX (UUID), INDEX (playername)) DEFAULT CHARSET " + charset); + // Players table must not be empty or inserts won't work - bug #492 + final ResultSet rs = state.executeQuery("SELECT NULL FROM `lb-players` LIMIT 1;"); + if (!rs.next()) { + state.execute("INSERT IGNORE INTO `lb-players` (UUID,playername) VALUES ('log_dummy_record','dummy_record')"); + } + if (isLogging(Logging.CHAT) || isLogging(Logging.PLAYER_COMMANDS) || isLogging(Logging.CONSOLE_COMMANDS) || isLogging(Logging.COMMANDBLOCK_COMMANDS)) { + try { + createTable(state, "lb-chat", "(id INT UNSIGNED NOT NULL AUTO_INCREMENT, date DATETIME NOT NULL, playerid INT UNSIGNED NOT NULL, message VARCHAR(256) NOT NULL, PRIMARY KEY (id), KEY playerid (playerid), FULLTEXT message (message)) DEFAULT CHARSET " + charset); + } catch (SQLException e) { + createTable(state, "lb-chat", "(id INT UNSIGNED NOT NULL AUTO_INCREMENT, date DATETIME NOT NULL, playerid INT UNSIGNED NOT NULL, message VARCHAR(256) NOT NULL, PRIMARY KEY (id), KEY playerid (playerid)) DEFAULT CHARSET " + charset); + } + } + createTable(state, "lb-materials", "(id INT UNSIGNED NOT NULL, name VARCHAR(255) NOT NULL, PRIMARY KEY (id)) DEFAULT CHARSET " + charset); + createTable(state, "lb-blockstates", "(id INT UNSIGNED NOT NULL, name VARCHAR(255) NOT NULL, PRIMARY KEY (id)) DEFAULT CHARSET " + charset); + createTable(state, "lb-entitytypes", "(id INT UNSIGNED NOT NULL, name VARCHAR(255) NOT NULL, PRIMARY KEY (id)) DEFAULT CHARSET " + charset); + + for (final WorldConfig wcfg : getLoggedWorlds()) { + createTable(state, wcfg.table + "-blocks", "(id INT UNSIGNED NOT NULL AUTO_INCREMENT, date DATETIME NOT NULL, playerid INT UNSIGNED NOT NULL, replaced SMALLINT UNSIGNED NOT NULL, replacedData SMALLINT NOT NULL, type SMALLINT UNSIGNED NOT NULL, typeData SMALLINT NOT NULL, x MEDIUMINT NOT NULL, y SMALLINT NOT NULL, z MEDIUMINT NOT NULL, PRIMARY KEY (id), KEY coords (x, z, y), KEY date (date), KEY playerid (playerid))"); + createTable(state, wcfg.table + "-chestdata", "(id INT UNSIGNED NOT NULL, item MEDIUMBLOB, itemremove TINYINT, itemtype SMALLINT NOT NULL DEFAULT '0', PRIMARY KEY (id))"); + createTable(state, wcfg.table + "-state", "(id INT UNSIGNED NOT NULL, replacedState MEDIUMBLOB NULL, typeState MEDIUMBLOB NULL, PRIMARY KEY (id))"); + if (wcfg.isLogging(Logging.KILL)) { + createTable(state, wcfg.table + "-kills", "(id INT UNSIGNED NOT NULL AUTO_INCREMENT, date DATETIME NOT NULL, killer INT UNSIGNED, victim INT UNSIGNED NOT NULL, weapon SMALLINT UNSIGNED NOT NULL, x MEDIUMINT NOT NULL, y SMALLINT NOT NULL, z MEDIUMINT NOT NULL, PRIMARY KEY (id))"); + } + createTable(state, wcfg.table + "-entityids", "(entityid INT UNSIGNED NOT NULL AUTO_INCREMENT, entityuuid VARCHAR(36) CHARACTER SET ascii COLLATE ascii_bin NOT NULL, PRIMARY KEY (entityid), UNIQUE KEY (entityuuid))"); + createTable(state, wcfg.table + "-entities", "(id INT UNSIGNED NOT NULL AUTO_INCREMENT, date DATETIME NOT NULL, playerid INT UNSIGNED NOT NULL, entityid INT UNSIGNED NOT NULL, entitytypeid INT UNSIGNED NOT NULL, x MEDIUMINT NOT NULL, y SMALLINT NOT NULL, z MEDIUMINT NOT NULL, action TINYINT UNSIGNED NOT NULL, data MEDIUMBLOB NULL, PRIMARY KEY (id), KEY coords (x, z, y), KEY date (date), KEY playerid (playerid), KEY entityid (entityid))"); + } + state.close(); + conn.close(); + } + + private void createTable(Statement state, String table, String query) throws SQLException { + try (ResultSet tableResult = state.executeQuery("SHOW TABLES LIKE '" + table + "'")) { + if (!tableResult.next()) { + logblock.getLogger().log(Level.INFO, "Creating table " + table + "."); + state.execute("CREATE TABLE `" + table + "` " + query); + try (ResultSet tableResultNew = state.executeQuery("SHOW TABLES LIKE '" + table + "'")) { + if (!tableResultNew.next()) { + throw new SQLException("Table " + table + " not found and failed to create"); + } + } + } + } + } + + /** + * Update materials that were renamed + */ + private void updateMaterialsPost1_13() { + final ConfigurationSection config = logblock.getConfig(); + String previousMinecraftVersion = config.getString("previousMinecraftVersion"); + if (previousMinecraftVersion == null) { + previousMinecraftVersion = "1.13"; + } + ComparableVersion comparablePreviousMinecraftVersion = new ComparableVersion(previousMinecraftVersion); + String currentMinecraftVersion = logblock.getServer().getVersion(); + currentMinecraftVersion = currentMinecraftVersion.substring(currentMinecraftVersion.indexOf("(MC: ") + 5); + int currentVersionEnd = currentMinecraftVersion.indexOf(" "); + int currentVersionEnd2 = currentMinecraftVersion.indexOf(")"); + if (currentVersionEnd2 >= 0 && (currentVersionEnd < 0 || currentVersionEnd2 < currentVersionEnd)) { + currentVersionEnd = currentVersionEnd2; + } + currentMinecraftVersion = currentMinecraftVersion.substring(0, currentVersionEnd); + logblock.getLogger().info("[Updater] Current Minecraft Version: '" + currentMinecraftVersion + "'"); + ComparableVersion comparableCurrentMinecraftVersion = new ComparableVersion(currentMinecraftVersion); + + if (comparablePreviousMinecraftVersion.compareTo("1.14") < 0 && comparableCurrentMinecraftVersion.compareTo("1.14") >= 0) { + logblock.getLogger().info("[Updater] Upgrading Materials to 1.14"); + renameMaterial("minecraft:sign", Material.OAK_SIGN); + renameMaterial("minecraft:wall_sign", Material.OAK_WALL_SIGN); + renameMaterial("minecraft:stone_slab", Material.SMOOTH_STONE_SLAB); + renameMaterial("minecraft:rose_red", Material.RED_DYE); + renameMaterial("minecraft:dandelion_yellow", Material.YELLOW_DYE); + renameMaterial("minecraft:cactus_green", Material.GREEN_DYE); + } + + if (comparablePreviousMinecraftVersion.compareTo("1.17") < 0 && comparableCurrentMinecraftVersion.compareTo("1.17") >= 0) { + logblock.getLogger().info("[Updater] Upgrading Materials to 1.17"); + renameMaterial("minecraft:grass_path", Material.DIRT_PATH); + } + + config.set("previousMinecraftVersion", currentMinecraftVersion); + logblock.saveConfig(); + } + + private void renameMaterial(String oldName, Material newName) { + final Connection conn = logblock.getConnection(); + try { + conn.setAutoCommit(false); + PreparedStatement stSelectMaterial = conn.prepareStatement("SELECT id FROM `lb-materials` WHERE name = ?"); + stSelectMaterial.setString(1, oldName); + ResultSet rs = stSelectMaterial.executeQuery(); + if (rs.next()) { + logblock.getLogger().info("[Updater] Updating " + oldName + " to " + newName); + int oldId = rs.getInt(1); + int newId = MaterialConverter.getOrAddMaterialId(newName); + + Statement st = conn.createStatement(); + int rows = 0; + for (final WorldConfig wcfg : getLoggedWorlds()) { + rows += st.executeUpdate("UPDATE `" + wcfg.table + "-blocks` SET replaced = " + newId + " WHERE replaced = " + oldId); + rows += st.executeUpdate("UPDATE `" + wcfg.table + "-blocks` SET type = " + newId + " WHERE type = " + oldId); + rows += st.executeUpdate("UPDATE `" + wcfg.table + "-chestdata` SET itemtype = " + newId + " WHERE itemtype = " + oldId); + if (wcfg.isLogging(Logging.KILL)) { + rows += st.executeUpdate("UPDATE `" + wcfg.table + "-kills` SET weapon = " + newId + " WHERE weapon = " + oldId); + } + } + st.close(); + if (rows > 0) { + logblock.getLogger().info("[Updater] Successfully updated " + rows + " entries.."); + } + } + stSelectMaterial.close(); + conn.commit(); + conn.close(); + } catch (final SQLException ex) { + logblock.getLogger().log(Level.SEVERE, "[Updater] Error: " + ex.getMessage(), ex); + } + } + + public static class PlayerCountChecker implements Runnable { + + private LogBlock logblock; + + public PlayerCountChecker(LogBlock logblock) { + this.logblock = logblock; + } + + @Override + public void run() { + final Connection conn = logblock.getConnection(); + try { + conn.setAutoCommit(true); + final Statement st = conn.createStatement(); + ResultSet rs = st.executeQuery("SELECT auto_increment FROM information_schema.columns AS col join information_schema.tables AS tab ON (col.table_schema=tab.table_schema AND col.table_name=tab.table_name) WHERE col.table_name = 'lb-players' AND col.column_name = 'playerid' AND col.data_type = 'smallint' AND col.table_schema = DATABASE() AND auto_increment > 65000;"); + if (rs.next()) { + for (int i = 0; i < 6; i++) { + logblock.getLogger().warning("Your server reached 65000 players. You should soon update your database table schema - see FAQ: https://github.com/LogBlock/LogBlock/wiki/FAQ#logblock-your-server-reached-65000-players-"); + } + } + st.close(); + conn.close(); + } catch (final SQLException ex) { + if (logblock.isCompletelyEnabled()) { + logblock.getLogger().log(Level.SEVERE, "[Updater] Error: ", ex); + } + } + } + } + + public static class MaterialUpdater1_13 { + BlockData[][] blockDataMapping; + Material[][] itemMapping = new Material[10][]; + + public MaterialUpdater1_13(LogBlock plugin) throws IOException { + blockDataMapping = new BlockData[256][16]; + try (JarFile file = new JarFile(plugin.getFile())) { + BufferedReader reader = new BufferedReader(new InputStreamReader(new BufferedInputStream(file.getInputStream(file.getJarEntry("blockdata.txt"))), "UTF-8")); + while (true) { + String line = reader.readLine(); + if (line == null) { + break; + } + int splitter1 = line.indexOf(":"); + int splitter2 = line.indexOf(","); + if (splitter1 >= 0 && splitter2 >= 0) { + int blockid = Integer.parseInt(line.substring(0, splitter1)); + int blockdata = Integer.parseInt(line.substring(splitter1 + 1, splitter2)); + BlockData newBlockData = Bukkit.createBlockData(line.substring(splitter2 + 1)); + + if (blockdata == 0) { + for (int i = 0; i < 16; i++) { + if (blockDataMapping[blockid][i] == null) { + blockDataMapping[blockid][i] = newBlockData; + } + } + } else { + blockDataMapping[blockid][blockdata] = newBlockData; + } + } + } + reader.close(); + + HashMap materialKeysToMaterial = new HashMap<>(); + for (Material material : Material.values()) { + materialKeysToMaterial.put(material.getKey().toString(), material); + } + + reader = new BufferedReader(new InputStreamReader(new BufferedInputStream(file.getInputStream(file.getJarEntry("itemdata.txt"))), "UTF-8")); + while (true) { + String line = reader.readLine(); + if (line == null) { + break; + } + int splitter1 = line.indexOf(":"); + int splitter2 = line.indexOf(","); + if (splitter1 >= 0 && splitter2 >= 0) { + int itemid = Integer.parseInt(line.substring(0, splitter1)); + int itemdata = Integer.parseInt(line.substring(splitter1 + 1, splitter2)); + Material newMaterial = materialKeysToMaterial.get(line.substring(splitter2 + 1)); + if (newMaterial == null) { + throw new IOException("Unknown item: " + line.substring(splitter2 + 1)); + } + if (itemid >= itemMapping.length) { + itemMapping = Arrays.copyOf(itemMapping, Math.max(itemMapping.length * 3 / 2, itemid + 1)); + } + + Material[] itemValues = itemMapping[itemid]; + if (itemValues == null) { + itemValues = new Material[itemdata + 1]; + itemMapping[itemid] = itemValues; + } else if (itemValues.length <= itemdata) { + itemValues = Arrays.copyOf(itemValues, itemdata + 1); + itemMapping[itemid] = itemValues; + } + itemValues[itemdata] = newMaterial; + } + } + reader.close(); + } + } + + public BlockData getBlockData(int id, int data) { + return id >= 0 && id < 256 && data >= 0 && data < 16 ? blockDataMapping[id][data] : null; + } + + public Material getMaterial(int id, int data) { + Material[] materials = id >= 0 && id < itemMapping.length ? itemMapping[id] : null; + if (materials != null && materials.length > 0) { + if (materials[0] != null && materials[0].getMaxDurability() == 0 && data >= 0 && data < materials.length && materials[data] != null) { + return materials[data]; + } + return materials[0]; + } + return null; + } + + public Material getMaterial(String id) { + int item = 0; + int data = 0; + int seperator = id.indexOf(':'); + if (seperator < 0) { + item = Integer.parseInt(id); + } else { + item = Integer.parseInt(id.substring(0, seperator)); + data = Integer.parseInt(id.substring(seperator + 1)); + } + return getMaterial(item, data); + } + + public String convertMaterial(String oldEntry) { + if (oldEntry == null) { + return null; + } + try { + Material newMaterial = getMaterial(oldEntry); + if (newMaterial != null) { + return newMaterial.name(); + } + } catch (Exception e) { + Material newMaterial = Material.matchMaterial(oldEntry, true); + if (newMaterial != null) { + return newMaterial.name(); + } else { + newMaterial = Material.matchMaterial(oldEntry); + if (newMaterial != null) { + return newMaterial.name(); + } + } + } + return null; + } + + public List convertMaterials(Collection oldEntries) { + Set newEntries = new LinkedHashSet<>(); + for (String oldEntry : oldEntries) { + String newEntry = convertMaterial(oldEntry); + if (newEntry != null) { + newEntries.add(newEntry); + if (newEntry.equals(Material.AIR.name())) { + newEntries.add(Material.CAVE_AIR.name()); + newEntries.add(Material.VOID_AIR.name()); + } + } + } + return new ArrayList<>(newEntries); + } + } +} diff --git a/src/main/java/de/diddiz/LogBlock/config/WorldConfig.java b/src/main/java/de/diddiz/LogBlock/config/WorldConfig.java index 1a9e04e..06a6e90 100644 --- a/src/main/java/de/diddiz/LogBlock/config/WorldConfig.java +++ b/src/main/java/de/diddiz/LogBlock/config/WorldConfig.java @@ -1,171 +1,171 @@ -package de.diddiz.LogBlock.config; - -import de.diddiz.LogBlock.LogBlock; -import de.diddiz.LogBlock.Logging; -import de.diddiz.LogBlock.util.BukkitUtils; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Animals; -import org.bukkit.entity.ArmorStand; -import org.bukkit.entity.Entity; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.LivingEntity; -import org.bukkit.entity.Monster; -import org.bukkit.entity.Player; -import org.bukkit.entity.WaterMob; - -import java.io.File; -import java.io.IOException; -import java.util.EnumMap; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.logging.Level; - -public class WorldConfig extends LoggingEnabledMapping { - public final String world; - public final String table; - public final String insertBlockStatementString; - public final String selectBlockActorIdStatementString; - public final String insertBlockStateStatementString; - public final String insertBlockChestDataStatementString; - public final String insertEntityStatementString; - public final String updateEntityUUIDString; - - private final EnumMap entityLogging = new EnumMap<>(EntityLogging.class); - public final boolean logNaturalEntitySpawns; - public final boolean logAllNamedEntityKills; - - public WorldConfig(String world, File file) throws IOException { - this.world = world; - final Map def = new HashMap<>(); - // "Before MySQL 5.1.6, database and table names cannot contain "/", "\", ".", or characters that are not permitted in file names" - MySQL manual - // They _can_ contain spaces, but replace them as well - def.put("table", "lb-" + file.getName().substring(0, file.getName().length() - 4).replaceAll("[ ./\\\\]", "_")); - for (final Logging l : Logging.values()) { - def.put("logging." + l.toString(), l.isDefaultEnabled()); - } - final YamlConfiguration config = YamlConfiguration.loadConfiguration(file); - for (final Entry e : def.entrySet()) { - if (config.get(e.getKey()) == null) { - config.set(e.getKey(), e.getValue()); - } - } - for (EntityLogging el : EntityLogging.values()) { - if (!(config.get("entity." + el.name().toLowerCase()) instanceof List)) { - config.set("entity." + el.name().toLowerCase(), el.getDefaultEnabled()); - } - entityLogging.put(el, new EntityLoggingList(el, config.getStringList("entity." + el.name().toLowerCase()))); - } - if (!config.isBoolean("entity.logNaturalSpawns")) { - config.set("entity.logNaturalSpawns", false); - } - logNaturalEntitySpawns = config.getBoolean("entity.logNaturalSpawns"); - - if (!config.isBoolean("entity.logAllNamedEntityKills")) { - config.set("entity.logAllNamedEntityKills", true); - } - logAllNamedEntityKills = config.getBoolean("entity.logAllNamedEntityKills"); - - config.save(file); - table = config.getString("table"); - for (final Logging l : Logging.values()) { - setLogging(l, config.getBoolean("logging." + l.toString())); - } - - insertBlockStatementString = "INSERT INTO `" + table + "-blocks` (date, playerid, replaced, replaceddata, type, typedata, x, y, z) VALUES (FROM_UNIXTIME(?), ?, ?, ?, ?, ?, ?, ?, ?)"; - selectBlockActorIdStatementString = "SELECT playerid FROM `" + table + "-blocks` WHERE x = ? AND y = ? AND z = ? ORDER BY date DESC LIMIT 1"; - insertBlockStateStatementString = "INSERT INTO `" + table + "-state` (replacedState, typeState, id) VALUES(?, ?, ?)"; - insertBlockChestDataStatementString = "INSERT INTO `" + table + "-chestdata` (item, itemremove, id, itemtype) values (?, ?, ?, ?)"; - insertEntityStatementString = "INSERT INTO `" + table + "-entities` (date, playerid, entityid, entitytypeid, x, y, z, action, data) VALUES (FROM_UNIXTIME(?), ?, ?, ?, ?, ?, ?, ?, ?)"; - updateEntityUUIDString = "UPDATE `" + table + "-entityids` SET entityuuid = ? WHERE entityid = ?"; - } - - public boolean isLogging(EntityLogging logging, Entity entity) { - return entityLogging.get(logging).isLogging(entity); - } - - public boolean isLoggingAnyEntities() { - for (EntityLoggingList list : entityLogging.values()) { - if (list.isLoggingAnyEntities()) { - return true; - } - } - return false; - } - - private class EntityLoggingList { - private final EntityLogging entityAction; - private final HashSet logged = new HashSet<>(); - private final boolean logAll; - private final boolean logAnimals; - private final boolean logWateranimals; - private final boolean logMonsters; - private final boolean logLiving; - - public EntityLoggingList(EntityLogging entityAction, List types) { - this.entityAction = entityAction; - boolean all = false; - boolean animals = false; - boolean wateranimals = false; - boolean monsters = false; - boolean living = false; - for (String type : types) { - EntityType et = BukkitUtils.matchEntityType(type); - if (et != null) { - logged.add(et); - } else { - if (type.equalsIgnoreCase("all")) { - all = true; - } else if (type.equalsIgnoreCase("animal") || type.equalsIgnoreCase("animals")) { - animals = true; - } else if (type.equalsIgnoreCase("wateranimal") || type.equalsIgnoreCase("wateranimals")) { - wateranimals = true; - } else if (type.equalsIgnoreCase("monster") || type.equalsIgnoreCase("monsters")) { - monsters = true; - } else if (type.equalsIgnoreCase("living")) { - living = true; - } else { - LogBlock.getInstance().getLogger().log(Level.WARNING, "Unkown entity type in config for " + world + ": " + type); - } - } - } - logAll = all; - logAnimals = animals; - logWateranimals = wateranimals; - logMonsters = monsters; - logLiving = living; - } - - public boolean isLogging(Entity entity) { - if (entity == null || (entity instanceof Player)) { - return false; - } - EntityType type = entity.getType(); - if (logAll || logged.contains(type)) { - return true; - } - if (logLiving && LivingEntity.class.isAssignableFrom(entity.getClass()) && !(entity instanceof ArmorStand)) { - return true; - } - if (logAnimals && Animals.class.isAssignableFrom(entity.getClass())) { - return true; - } - if (logWateranimals && WaterMob.class.isAssignableFrom(entity.getClass())) { - return true; - } - if (logMonsters && (Monster.class.isAssignableFrom(entity.getClass()) || entity.getType() == EntityType.SLIME || entity.getType() == EntityType.WITHER || entity.getType() == EntityType.ENDER_DRAGON || entity.getType() == EntityType.SHULKER || entity.getType() == EntityType.GHAST)) { - return true; - } - if (entityAction == EntityLogging.DESTROY && logAllNamedEntityKills && entity.getCustomName() != null) { - return true; - } - return false; - } - - public boolean isLoggingAnyEntities() { - return logAll || logAnimals || logLiving || logMonsters || !logged.isEmpty() || (entityAction == EntityLogging.DESTROY && logAllNamedEntityKills); - } - } -} +package de.diddiz.LogBlock.config; + +import de.diddiz.LogBlock.LogBlock; +import de.diddiz.LogBlock.Logging; +import de.diddiz.LogBlock.util.BukkitUtils; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Animals; +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Monster; +import org.bukkit.entity.Player; +import org.bukkit.entity.WaterMob; + +import java.io.File; +import java.io.IOException; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.logging.Level; + +public class WorldConfig extends LoggingEnabledMapping { + public final String world; + public final String table; + public final String insertBlockStatementString; + public final String selectBlockActorIdStatementString; + public final String insertBlockStateStatementString; + public final String insertBlockChestDataStatementString; + public final String insertEntityStatementString; + public final String updateEntityUUIDString; + + private final EnumMap entityLogging = new EnumMap<>(EntityLogging.class); + public final boolean logNaturalEntitySpawns; + public final boolean logAllNamedEntityKills; + + public WorldConfig(String world, File file) throws IOException { + this.world = world; + final Map def = new HashMap<>(); + // "Before MySQL 5.1.6, database and table names cannot contain "/", "\", ".", or characters that are not permitted in file names" - MySQL manual + // They _can_ contain spaces, but replace them as well + def.put("table", "lb-" + file.getName().substring(0, file.getName().length() - 4).replaceAll("[ ./\\\\]", "_")); + for (final Logging l : Logging.values()) { + def.put("logging." + l.toString(), l.isDefaultEnabled()); + } + final YamlConfiguration config = YamlConfiguration.loadConfiguration(file); + for (final Entry e : def.entrySet()) { + if (config.get(e.getKey()) == null) { + config.set(e.getKey(), e.getValue()); + } + } + for (EntityLogging el : EntityLogging.values()) { + if (!(config.get("entity." + el.name().toLowerCase()) instanceof List)) { + config.set("entity." + el.name().toLowerCase(), el.getDefaultEnabled()); + } + entityLogging.put(el, new EntityLoggingList(el, config.getStringList("entity." + el.name().toLowerCase()))); + } + if (!config.isBoolean("entity.logNaturalSpawns")) { + config.set("entity.logNaturalSpawns", false); + } + logNaturalEntitySpawns = config.getBoolean("entity.logNaturalSpawns"); + + if (!config.isBoolean("entity.logAllNamedEntityKills")) { + config.set("entity.logAllNamedEntityKills", true); + } + logAllNamedEntityKills = config.getBoolean("entity.logAllNamedEntityKills"); + + config.save(file); + table = config.getString("table"); + for (final Logging l : Logging.values()) { + setLogging(l, config.getBoolean("logging." + l.toString())); + } + + insertBlockStatementString = "INSERT INTO `" + table + "-blocks` (date, playerid, replaced, replaceddata, type, typedata, x, y, z) VALUES (FROM_UNIXTIME(?), ?, ?, ?, ?, ?, ?, ?, ?)"; + selectBlockActorIdStatementString = "SELECT playerid FROM `" + table + "-blocks` WHERE x = ? AND y = ? AND z = ? ORDER BY date DESC LIMIT 1"; + insertBlockStateStatementString = "INSERT INTO `" + table + "-state` (replacedState, typeState, id) VALUES(?, ?, ?)"; + insertBlockChestDataStatementString = "INSERT INTO `" + table + "-chestdata` (item, itemremove, id, itemtype) values (?, ?, ?, ?)"; + insertEntityStatementString = "INSERT INTO `" + table + "-entities` (date, playerid, entityid, entitytypeid, x, y, z, action, data) VALUES (FROM_UNIXTIME(?), ?, ?, ?, ?, ?, ?, ?, ?)"; + updateEntityUUIDString = "UPDATE `" + table + "-entityids` SET entityuuid = ? WHERE entityid = ?"; + } + + public boolean isLogging(EntityLogging logging, Entity entity) { + return entityLogging.get(logging).isLogging(entity); + } + + public boolean isLoggingAnyEntities() { + for (EntityLoggingList list : entityLogging.values()) { + if (list.isLoggingAnyEntities()) { + return true; + } + } + return false; + } + + private class EntityLoggingList { + private final EntityLogging entityAction; + private final HashSet logged = new HashSet<>(); + private final boolean logAll; + private final boolean logAnimals; + private final boolean logWateranimals; + private final boolean logMonsters; + private final boolean logLiving; + + public EntityLoggingList(EntityLogging entityAction, List types) { + this.entityAction = entityAction; + boolean all = false; + boolean animals = false; + boolean wateranimals = false; + boolean monsters = false; + boolean living = false; + for (String type : types) { + EntityType et = BukkitUtils.matchEntityType(type); + if (et != null) { + logged.add(et); + } else { + if (type.equalsIgnoreCase("all")) { + all = true; + } else if (type.equalsIgnoreCase("animal") || type.equalsIgnoreCase("animals")) { + animals = true; + } else if (type.equalsIgnoreCase("wateranimal") || type.equalsIgnoreCase("wateranimals")) { + wateranimals = true; + } else if (type.equalsIgnoreCase("monster") || type.equalsIgnoreCase("monsters")) { + monsters = true; + } else if (type.equalsIgnoreCase("living")) { + living = true; + } else { + LogBlock.getInstance().getLogger().log(Level.WARNING, "Unkown entity type in config for " + world + ": " + type); + } + } + } + logAll = all; + logAnimals = animals; + logWateranimals = wateranimals; + logMonsters = monsters; + logLiving = living; + } + + public boolean isLogging(Entity entity) { + if (entity == null || (entity instanceof Player)) { + return false; + } + EntityType type = entity.getType(); + if (logAll || logged.contains(type)) { + return true; + } + if (logLiving && LivingEntity.class.isAssignableFrom(entity.getClass()) && !(entity instanceof ArmorStand)) { + return true; + } + if (logAnimals && Animals.class.isAssignableFrom(entity.getClass())) { + return true; + } + if (logWateranimals && WaterMob.class.isAssignableFrom(entity.getClass())) { + return true; + } + if (logMonsters && (Monster.class.isAssignableFrom(entity.getClass()) || entity.getType() == EntityType.SLIME || entity.getType() == EntityType.WITHER || entity.getType() == EntityType.ENDER_DRAGON || entity.getType() == EntityType.SHULKER || entity.getType() == EntityType.GHAST)) { + return true; + } + if (entityAction == EntityLogging.DESTROY && logAllNamedEntityKills && entity.getCustomName() != null) { + return true; + } + return false; + } + + public boolean isLoggingAnyEntities() { + return logAll || logAnimals || logLiving || logMonsters || !logged.isEmpty() || (entityAction == EntityLogging.DESTROY && logAllNamedEntityKills); + } + } +} diff --git a/src/main/java/de/diddiz/LogBlock/listeners/BanListener.java b/src/main/java/de/diddiz/LogBlock/listeners/BanListener.java index 9f50afb..63ab4ac 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/BanListener.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/BanListener.java @@ -1,48 +1,48 @@ -package de.diddiz.LogBlock.listeners; - -import de.diddiz.LogBlock.CommandsHandler; -import de.diddiz.LogBlock.LogBlock; -import de.diddiz.LogBlock.QueryParams; -import org.bukkit.World; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerCommandPreprocessEvent; - -import static de.diddiz.LogBlock.config.Config.banPermission; -import static de.diddiz.LogBlock.config.Config.isLogged; -import static org.bukkit.Bukkit.getScheduler; - -public class BanListener implements Listener { - private final CommandsHandler handler; - private final LogBlock logblock; - - public BanListener(LogBlock logblock) { - this.logblock = logblock; - handler = logblock.getCommandsHandler(); - } - - @EventHandler - public void onPlayerCommandPreprocess(final PlayerCommandPreprocessEvent event) { - final String[] split = event.getMessage().split(" "); - if (split.length > 1 && split[0].equalsIgnoreCase("/ban") && logblock.hasPermission(event.getPlayer(), banPermission)) { - final QueryParams p = new QueryParams(logblock); - p.setPlayer(split[1].equalsIgnoreCase("g") ? split[2] : split[1]); - p.since = 0; - p.silent = false; - getScheduler().runTaskAsynchronously(logblock, new Runnable() { - @Override - public void run() { - for (final World world : logblock.getServer().getWorlds()) { - if (isLogged(world)) { - p.world = world; - try { - handler.new CommandRollback(event.getPlayer(), p, false); - } catch (final Exception ex) { - } - } - } - } - }); - } - } -} +package de.diddiz.LogBlock.listeners; + +import de.diddiz.LogBlock.CommandsHandler; +import de.diddiz.LogBlock.LogBlock; +import de.diddiz.LogBlock.QueryParams; +import org.bukkit.World; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerCommandPreprocessEvent; + +import static de.diddiz.LogBlock.config.Config.banPermission; +import static de.diddiz.LogBlock.config.Config.isLogged; +import static org.bukkit.Bukkit.getScheduler; + +public class BanListener implements Listener { + private final CommandsHandler handler; + private final LogBlock logblock; + + public BanListener(LogBlock logblock) { + this.logblock = logblock; + handler = logblock.getCommandsHandler(); + } + + @EventHandler + public void onPlayerCommandPreprocess(final PlayerCommandPreprocessEvent event) { + final String[] split = event.getMessage().split(" "); + if (split.length > 1 && split[0].equalsIgnoreCase("/ban") && logblock.hasPermission(event.getPlayer(), banPermission)) { + final QueryParams p = new QueryParams(logblock); + p.setPlayer(split[1].equalsIgnoreCase("g") ? split[2] : split[1]); + p.since = 0; + p.silent = false; + getScheduler().runTaskAsynchronously(logblock, new Runnable() { + @Override + public void run() { + for (final World world : logblock.getServer().getWorlds()) { + if (isLogged(world)) { + p.world = world; + try { + handler.new CommandRollback(event.getPlayer(), p, false); + } catch (final Exception ex) { + } + } + } + } + }); + } + } +} diff --git a/src/main/java/de/diddiz/LogBlock/listeners/BlockBreakLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/BlockBreakLogging.java index c561456..12c0b2b 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/BlockBreakLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/BlockBreakLogging.java @@ -1,74 +1,74 @@ -package de.diddiz.LogBlock.listeners; - -import de.diddiz.LogBlock.Actor; -import de.diddiz.LogBlock.LogBlock; -import de.diddiz.LogBlock.Logging; -import de.diddiz.LogBlock.config.WorldConfig; -import de.diddiz.LogBlock.util.BukkitUtils; -import org.bukkit.Bukkit; -import org.bukkit.GameMode; -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.Waterlogged; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.block.BlockBreakEvent; -import org.bukkit.event.player.PlayerBucketFillEvent; - -import static de.diddiz.LogBlock.config.Config.getWorldConfig; -import static de.diddiz.LogBlock.config.Config.isLogging; -import static de.diddiz.LogBlock.util.LoggingUtil.smartLogBlockBreak; -import static de.diddiz.LogBlock.util.LoggingUtil.smartLogBlockReplace; -import static de.diddiz.LogBlock.util.LoggingUtil.smartLogFallables; - -public class BlockBreakLogging extends LoggingListener { - public BlockBreakLogging(LogBlock lb) { - super(lb); - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onBlockBreak(BlockBreakEvent event) { - if (isLogging(event.getBlock().getWorld(), Logging.BLOCKBREAK)) { - WorldConfig wcfg = getWorldConfig(event.getBlock().getWorld()); - if (wcfg == null) { - return; - } - - final Actor actor = Actor.actorFromEntity(event.getPlayer()); - final Block origin = event.getBlock(); - final Material type = origin.getType(); - - if (wcfg.isLogging(Logging.CHESTACCESS) && BukkitUtils.getContainerBlocks().contains(type) && !BukkitUtils.getShulkerBoxBlocks().contains(type)) { - consumer.queueContainerBreak(actor, origin.getState()); - } else if (type == Material.ICE) { - // When in creative mode ice doesn't form water - if (event.getPlayer().getGameMode().equals(GameMode.CREATIVE)) { - smartLogBlockBreak(consumer, actor, origin); - } else { - smartLogBlockReplace(consumer, actor, origin, Bukkit.createBlockData(Material.WATER)); - } - } else { - smartLogBlockBreak(consumer, actor, origin); - } - smartLogFallables(consumer, actor, origin); - } - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onPlayerBucketFill(PlayerBucketFillEvent event) { - if (isLogging(event.getBlockClicked().getWorld(), Logging.BLOCKBREAK)) { - BlockData clickedBlockData = event.getBlockClicked().getBlockData(); - if (clickedBlockData instanceof Waterlogged) { - Waterlogged clickedWaterlogged = (Waterlogged) clickedBlockData; - if (clickedWaterlogged.isWaterlogged()) { - Waterlogged clickedWaterloggedWithoutWater = (Waterlogged) clickedWaterlogged.clone(); - clickedWaterloggedWithoutWater.setWaterlogged(false); - consumer.queueBlockReplace(Actor.actorFromEntity(event.getPlayer()), event.getBlockClicked().getLocation(), clickedWaterlogged, clickedWaterloggedWithoutWater); - } - } else { - consumer.queueBlockBreak(Actor.actorFromEntity(event.getPlayer()), event.getBlockClicked().getState()); - } - } - } -} +package de.diddiz.LogBlock.listeners; + +import de.diddiz.LogBlock.Actor; +import de.diddiz.LogBlock.LogBlock; +import de.diddiz.LogBlock.Logging; +import de.diddiz.LogBlock.config.WorldConfig; +import de.diddiz.LogBlock.util.BukkitUtils; +import org.bukkit.Bukkit; +import org.bukkit.GameMode; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.Waterlogged; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.player.PlayerBucketFillEvent; + +import static de.diddiz.LogBlock.config.Config.getWorldConfig; +import static de.diddiz.LogBlock.config.Config.isLogging; +import static de.diddiz.LogBlock.util.LoggingUtil.smartLogBlockBreak; +import static de.diddiz.LogBlock.util.LoggingUtil.smartLogBlockReplace; +import static de.diddiz.LogBlock.util.LoggingUtil.smartLogFallables; + +public class BlockBreakLogging extends LoggingListener { + public BlockBreakLogging(LogBlock lb) { + super(lb); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onBlockBreak(BlockBreakEvent event) { + if (isLogging(event.getBlock().getWorld(), Logging.BLOCKBREAK)) { + WorldConfig wcfg = getWorldConfig(event.getBlock().getWorld()); + if (wcfg == null) { + return; + } + + final Actor actor = Actor.actorFromEntity(event.getPlayer()); + final Block origin = event.getBlock(); + final Material type = origin.getType(); + + if (wcfg.isLogging(Logging.CHESTACCESS) && BukkitUtils.getContainerBlocks().contains(type) && !BukkitUtils.getShulkerBoxBlocks().contains(type)) { + consumer.queueContainerBreak(actor, origin.getState()); + } else if (type == Material.ICE) { + // When in creative mode ice doesn't form water + if (event.getPlayer().getGameMode().equals(GameMode.CREATIVE)) { + smartLogBlockBreak(consumer, actor, origin); + } else { + smartLogBlockReplace(consumer, actor, origin, Bukkit.createBlockData(Material.WATER)); + } + } else { + smartLogBlockBreak(consumer, actor, origin); + } + smartLogFallables(consumer, actor, origin); + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onPlayerBucketFill(PlayerBucketFillEvent event) { + if (isLogging(event.getBlockClicked().getWorld(), Logging.BLOCKBREAK)) { + BlockData clickedBlockData = event.getBlockClicked().getBlockData(); + if (clickedBlockData instanceof Waterlogged) { + Waterlogged clickedWaterlogged = (Waterlogged) clickedBlockData; + if (clickedWaterlogged.isWaterlogged()) { + Waterlogged clickedWaterloggedWithoutWater = (Waterlogged) clickedWaterlogged.clone(); + clickedWaterloggedWithoutWater.setWaterlogged(false); + consumer.queueBlockReplace(Actor.actorFromEntity(event.getPlayer()), event.getBlockClicked().getLocation(), clickedWaterlogged, clickedWaterloggedWithoutWater); + } + } else { + consumer.queueBlockBreak(Actor.actorFromEntity(event.getPlayer()), event.getBlockClicked().getState()); + } + } + } +} diff --git a/src/main/java/de/diddiz/LogBlock/listeners/BlockBurnLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/BlockBurnLogging.java index 5ba55c6..1ee7ed1 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/BlockBurnLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/BlockBurnLogging.java @@ -1,72 +1,72 @@ -package de.diddiz.LogBlock.listeners; - -import de.diddiz.LogBlock.Actor; -import de.diddiz.LogBlock.LogBlock; -import de.diddiz.LogBlock.Logging; -import de.diddiz.LogBlock.config.Config; - -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.block.Action; -import org.bukkit.event.block.BlockBurnEvent; -import org.bukkit.event.block.BlockIgniteEvent; -import org.bukkit.event.block.BlockIgniteEvent.IgniteCause; -import org.bukkit.event.player.PlayerInteractEvent; - -import static de.diddiz.LogBlock.config.Config.isLogging; -import static de.diddiz.LogBlock.util.LoggingUtil.smartLogBlockBreak; -import static de.diddiz.LogBlock.util.LoggingUtil.smartLogBlockReplace; -import static de.diddiz.LogBlock.util.LoggingUtil.smartLogFallables; - -public class BlockBurnLogging extends LoggingListener { - public BlockBurnLogging(LogBlock lb) { - super(lb); - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onBlockBurn(BlockBurnEvent event) { - if (isLogging(event.getBlock().getWorld(), Logging.FIRE)) { - smartLogBlockReplace(consumer, new Actor("Fire", Config.logFireSpreadAsPlayerWhoCreatedIt ? event.getIgnitingBlock() : null), event.getBlock(), Material.FIRE.createBlockData()); - smartLogFallables(consumer, new Actor("Fire", Config.logFireSpreadAsPlayerWhoCreatedIt ? event.getIgnitingBlock() : null), event.getBlock()); - } - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onBlockIgnite(BlockIgniteEvent event) { - Actor actor = new Actor("Fire", Config.logFireSpreadAsPlayerWhoCreatedIt ? event.getIgnitingBlock() : null); - if (event.getCause() == IgniteCause.FLINT_AND_STEEL) { - if (event.getIgnitingEntity() != null) { - return; // handled in block place - } else { - actor = new Actor("Dispenser"); - } - } else if (event.getCause() == IgniteCause.LIGHTNING) { - actor = new Actor("Lightning"); - } else if (event.getCause() == IgniteCause.EXPLOSION) { - actor = new Actor("Explosion"); - } else if (event.getCause() == IgniteCause.LAVA) { - actor = new Actor("Lava"); - } else if (event.getCause() == IgniteCause.ENDER_CRYSTAL) { - actor = new Actor("EnderCrystal"); - } - if (isLogging(event.getBlock().getWorld(), Logging.FIRE)) { - consumer.queueBlockPlace(actor, event.getBlock().getLocation(), Material.FIRE.createBlockData()); - } - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onExtinguish(PlayerInteractEvent event) { - if (event.getAction().equals(Action.LEFT_CLICK_BLOCK)) { - Player player = event.getPlayer(); - Block block = event.getClickedBlock().getRelative(event.getBlockFace()); - if (block.getType().equals(Material.FIRE) && isLogging(player.getWorld(), Logging.FIRE)) { - Actor actor = Actor.actorFromEntity(player); - smartLogBlockBreak(consumer, actor, block); - smartLogFallables(consumer, actor, block); - } - } - } -} +package de.diddiz.LogBlock.listeners; + +import de.diddiz.LogBlock.Actor; +import de.diddiz.LogBlock.LogBlock; +import de.diddiz.LogBlock.Logging; +import de.diddiz.LogBlock.config.Config; + +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.Action; +import org.bukkit.event.block.BlockBurnEvent; +import org.bukkit.event.block.BlockIgniteEvent; +import org.bukkit.event.block.BlockIgniteEvent.IgniteCause; +import org.bukkit.event.player.PlayerInteractEvent; + +import static de.diddiz.LogBlock.config.Config.isLogging; +import static de.diddiz.LogBlock.util.LoggingUtil.smartLogBlockBreak; +import static de.diddiz.LogBlock.util.LoggingUtil.smartLogBlockReplace; +import static de.diddiz.LogBlock.util.LoggingUtil.smartLogFallables; + +public class BlockBurnLogging extends LoggingListener { + public BlockBurnLogging(LogBlock lb) { + super(lb); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onBlockBurn(BlockBurnEvent event) { + if (isLogging(event.getBlock().getWorld(), Logging.FIRE)) { + smartLogBlockReplace(consumer, new Actor("Fire", Config.logFireSpreadAsPlayerWhoCreatedIt ? event.getIgnitingBlock() : null), event.getBlock(), Material.FIRE.createBlockData()); + smartLogFallables(consumer, new Actor("Fire", Config.logFireSpreadAsPlayerWhoCreatedIt ? event.getIgnitingBlock() : null), event.getBlock()); + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onBlockIgnite(BlockIgniteEvent event) { + Actor actor = new Actor("Fire", Config.logFireSpreadAsPlayerWhoCreatedIt ? event.getIgnitingBlock() : null); + if (event.getCause() == IgniteCause.FLINT_AND_STEEL) { + if (event.getIgnitingEntity() != null) { + return; // handled in block place + } else { + actor = new Actor("Dispenser"); + } + } else if (event.getCause() == IgniteCause.LIGHTNING) { + actor = new Actor("Lightning"); + } else if (event.getCause() == IgniteCause.EXPLOSION) { + actor = new Actor("Explosion"); + } else if (event.getCause() == IgniteCause.LAVA) { + actor = new Actor("Lava"); + } else if (event.getCause() == IgniteCause.ENDER_CRYSTAL) { + actor = new Actor("EnderCrystal"); + } + if (isLogging(event.getBlock().getWorld(), Logging.FIRE)) { + consumer.queueBlockPlace(actor, event.getBlock().getLocation(), Material.FIRE.createBlockData()); + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onExtinguish(PlayerInteractEvent event) { + if (event.getAction().equals(Action.LEFT_CLICK_BLOCK)) { + Player player = event.getPlayer(); + Block block = event.getClickedBlock().getRelative(event.getBlockFace()); + if (block.getType().equals(Material.FIRE) && isLogging(player.getWorld(), Logging.FIRE)) { + Actor actor = Actor.actorFromEntity(player); + smartLogBlockBreak(consumer, actor, block); + smartLogFallables(consumer, actor, block); + } + } + } +} diff --git a/src/main/java/de/diddiz/LogBlock/listeners/BlockPlaceLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/BlockPlaceLogging.java index b24cfcf..e6f7261 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/BlockPlaceLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/BlockPlaceLogging.java @@ -1,67 +1,67 @@ -package de.diddiz.LogBlock.listeners; - -import de.diddiz.LogBlock.Actor; -import de.diddiz.LogBlock.LogBlock; -import de.diddiz.LogBlock.Logging; -import de.diddiz.LogBlock.config.Config; -import de.diddiz.LogBlock.util.LoggingUtil; -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.block.BlockState; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.Waterlogged; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.block.BlockPlaceEvent; -import org.bukkit.event.player.PlayerBucketEmptyEvent; - -import static de.diddiz.LogBlock.config.Config.isLogging; - -public class BlockPlaceLogging extends LoggingListener { - public BlockPlaceLogging(LogBlock lb) { - super(lb); - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onBlockPlace(BlockPlaceEvent event) { - if (Config.isLogging(event.getBlock().getWorld(), Logging.BLOCKPLACE)) { - final BlockState before = event.getBlockReplacedState(); - final BlockState after = event.getBlockPlaced().getState(); - final Actor actor = Actor.actorFromEntity(event.getPlayer()); - if (before.getType() == Material.LECTERN && after.getType() == Material.LECTERN) { - return; - } - LoggingUtil.smartLogBlockPlace(consumer, actor, before, after); - } - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onPlayerBucketEmpty(PlayerBucketEmptyEvent event) { - if (isLogging(event.getPlayer().getWorld(), Logging.BLOCKPLACE)) { - Material placedMaterial = event.getBucket() == Material.LAVA_BUCKET ? Material.LAVA : Material.WATER; - BlockData clickedBlockData = event.getBlockClicked().getBlockData(); - if (placedMaterial == Material.WATER && clickedBlockData instanceof Waterlogged) { - Waterlogged clickedWaterlogged = (Waterlogged) clickedBlockData; - if (!clickedWaterlogged.isWaterlogged()) { - Waterlogged clickedWaterloggedWithWater = (Waterlogged) clickedWaterlogged.clone(); - clickedWaterloggedWithWater.setWaterlogged(true); - consumer.queueBlockReplace(Actor.actorFromEntity(event.getPlayer()), event.getBlockClicked().getLocation(), clickedWaterlogged, clickedWaterloggedWithWater); - return; - } - } - Block placedAt = event.getBlockClicked().getRelative(event.getBlockFace()); - if (placedAt.isEmpty()) { - consumer.queueBlockPlace(Actor.actorFromEntity(event.getPlayer()), placedAt.getLocation(), placedMaterial.createBlockData()); - } else { - BlockData placedAtBlock = placedAt.getBlockData(); - if (placedAtBlock instanceof Waterlogged && !(((Waterlogged) placedAtBlock).isWaterlogged())) { - Waterlogged clickedWaterloggedWithWater = (Waterlogged) placedAtBlock.clone(); - clickedWaterloggedWithWater.setWaterlogged(true); - consumer.queueBlockReplace(Actor.actorFromEntity(event.getPlayer()), placedAt.getLocation(), placedAtBlock, clickedWaterloggedWithWater); - } else { - consumer.queueBlockReplace(Actor.actorFromEntity(event.getPlayer()), placedAt.getLocation(), placedAtBlock, placedMaterial.createBlockData()); - } - } - } - } -} +package de.diddiz.LogBlock.listeners; + +import de.diddiz.LogBlock.Actor; +import de.diddiz.LogBlock.LogBlock; +import de.diddiz.LogBlock.Logging; +import de.diddiz.LogBlock.config.Config; +import de.diddiz.LogBlock.util.LoggingUtil; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.Waterlogged; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.player.PlayerBucketEmptyEvent; + +import static de.diddiz.LogBlock.config.Config.isLogging; + +public class BlockPlaceLogging extends LoggingListener { + public BlockPlaceLogging(LogBlock lb) { + super(lb); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onBlockPlace(BlockPlaceEvent event) { + if (Config.isLogging(event.getBlock().getWorld(), Logging.BLOCKPLACE)) { + final BlockState before = event.getBlockReplacedState(); + final BlockState after = event.getBlockPlaced().getState(); + final Actor actor = Actor.actorFromEntity(event.getPlayer()); + if (before.getType() == Material.LECTERN && after.getType() == Material.LECTERN) { + return; + } + LoggingUtil.smartLogBlockPlace(consumer, actor, before, after); + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onPlayerBucketEmpty(PlayerBucketEmptyEvent event) { + if (isLogging(event.getPlayer().getWorld(), Logging.BLOCKPLACE)) { + Material placedMaterial = event.getBucket() == Material.LAVA_BUCKET ? Material.LAVA : Material.WATER; + BlockData clickedBlockData = event.getBlockClicked().getBlockData(); + if (placedMaterial == Material.WATER && clickedBlockData instanceof Waterlogged) { + Waterlogged clickedWaterlogged = (Waterlogged) clickedBlockData; + if (!clickedWaterlogged.isWaterlogged()) { + Waterlogged clickedWaterloggedWithWater = (Waterlogged) clickedWaterlogged.clone(); + clickedWaterloggedWithWater.setWaterlogged(true); + consumer.queueBlockReplace(Actor.actorFromEntity(event.getPlayer()), event.getBlockClicked().getLocation(), clickedWaterlogged, clickedWaterloggedWithWater); + return; + } + } + Block placedAt = event.getBlockClicked().getRelative(event.getBlockFace()); + if (placedAt.isEmpty()) { + consumer.queueBlockPlace(Actor.actorFromEntity(event.getPlayer()), placedAt.getLocation(), placedMaterial.createBlockData()); + } else { + BlockData placedAtBlock = placedAt.getBlockData(); + if (placedAtBlock instanceof Waterlogged && !(((Waterlogged) placedAtBlock).isWaterlogged())) { + Waterlogged clickedWaterloggedWithWater = (Waterlogged) placedAtBlock.clone(); + clickedWaterloggedWithWater.setWaterlogged(true); + consumer.queueBlockReplace(Actor.actorFromEntity(event.getPlayer()), placedAt.getLocation(), placedAtBlock, clickedWaterloggedWithWater); + } else { + consumer.queueBlockReplace(Actor.actorFromEntity(event.getPlayer()), placedAt.getLocation(), placedAtBlock, placedMaterial.createBlockData()); + } + } + } + } +} diff --git a/src/main/java/de/diddiz/LogBlock/listeners/ChatLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/ChatLogging.java index baafe63..11b1a75 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/ChatLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/ChatLogging.java @@ -1,58 +1,58 @@ -package de.diddiz.LogBlock.listeners; - -import de.diddiz.LogBlock.Actor; -import de.diddiz.LogBlock.LogBlock; -import de.diddiz.LogBlock.Logging; -import org.bukkit.command.BlockCommandSender; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.minecart.CommandMinecart; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.player.AsyncPlayerChatEvent; -import org.bukkit.event.player.PlayerCommandPreprocessEvent; -import org.bukkit.event.server.ServerCommandEvent; - -import static de.diddiz.LogBlock.config.Config.isLogging; - -public class ChatLogging extends LoggingListener { - public ChatLogging(LogBlock lb) { - super(lb); - } - - @EventHandler(priority = EventPriority.MONITOR) - public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) { - if (isLogging(event.getPlayer().getWorld(), Logging.PLAYER_COMMANDS)) { - consumer.queueChat(Actor.actorFromEntity(event.getPlayer()), event.getMessage()); - } - } - - @EventHandler(priority = EventPriority.MONITOR) - public void onPlayerChat(AsyncPlayerChatEvent event) { - if (isLogging(event.getPlayer().getWorld(), Logging.CHAT)) { - consumer.queueChat(Actor.actorFromEntity(event.getPlayer()), event.getMessage()); - } - } - - @EventHandler(priority = EventPriority.MONITOR) - public void onServerCommand(ServerCommandEvent event) { - CommandSender sender = event.getSender(); - Actor actor; - if (sender instanceof BlockCommandSender) { - if (!isLogging(((BlockCommandSender) sender).getBlock().getWorld(), Logging.COMMANDBLOCK_COMMANDS)) { - return; - } - actor = new Actor("CommandBlock"); - } else if (sender instanceof CommandMinecart) { - if (!isLogging(((CommandMinecart) sender).getWorld(), Logging.COMMANDBLOCK_COMMANDS)) { - return; - } - actor = new Actor("CommandMinecart"); - } else { - if (!isLogging(Logging.CONSOLE_COMMANDS)) { - return; - } - actor = new Actor("Console"); - } - consumer.queueChat(actor, "/" + event.getCommand()); - } -} +package de.diddiz.LogBlock.listeners; + +import de.diddiz.LogBlock.Actor; +import de.diddiz.LogBlock.LogBlock; +import de.diddiz.LogBlock.Logging; +import org.bukkit.command.BlockCommandSender; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.minecart.CommandMinecart; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.AsyncPlayerChatEvent; +import org.bukkit.event.player.PlayerCommandPreprocessEvent; +import org.bukkit.event.server.ServerCommandEvent; + +import static de.diddiz.LogBlock.config.Config.isLogging; + +public class ChatLogging extends LoggingListener { + public ChatLogging(LogBlock lb) { + super(lb); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) { + if (isLogging(event.getPlayer().getWorld(), Logging.PLAYER_COMMANDS)) { + consumer.queueChat(Actor.actorFromEntity(event.getPlayer()), event.getMessage()); + } + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onPlayerChat(AsyncPlayerChatEvent event) { + if (isLogging(event.getPlayer().getWorld(), Logging.CHAT)) { + consumer.queueChat(Actor.actorFromEntity(event.getPlayer()), event.getMessage()); + } + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onServerCommand(ServerCommandEvent event) { + CommandSender sender = event.getSender(); + Actor actor; + if (sender instanceof BlockCommandSender) { + if (!isLogging(((BlockCommandSender) sender).getBlock().getWorld(), Logging.COMMANDBLOCK_COMMANDS)) { + return; + } + actor = new Actor("CommandBlock"); + } else if (sender instanceof CommandMinecart) { + if (!isLogging(((CommandMinecart) sender).getWorld(), Logging.COMMANDBLOCK_COMMANDS)) { + return; + } + actor = new Actor("CommandMinecart"); + } else { + if (!isLogging(Logging.CONSOLE_COMMANDS)) { + return; + } + actor = new Actor("Console"); + } + consumer.queueChat(actor, "/" + event.getCommand()); + } +} diff --git a/src/main/java/de/diddiz/LogBlock/listeners/ChestAccessLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/ChestAccessLogging.java index 15606ec..319b66b 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/ChestAccessLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/ChestAccessLogging.java @@ -1,303 +1,303 @@ -package de.diddiz.LogBlock.listeners; - -import de.diddiz.LogBlock.Actor; -import de.diddiz.LogBlock.LogBlock; -import de.diddiz.LogBlock.Logging; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.block.BlockState; -import org.bukkit.block.DoubleChest; -import org.bukkit.entity.HumanEntity; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.event.inventory.InventoryCloseEvent; -import org.bukkit.event.inventory.InventoryDragEvent; -import org.bukkit.event.inventory.InventoryOpenEvent; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.InventoryHolder; -import org.bukkit.inventory.ItemStack; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; - -import static de.diddiz.LogBlock.config.Config.isLogging; -import static de.diddiz.LogBlock.util.BukkitUtils.*; - -public class ChestAccessLogging extends LoggingListener { - private class PlayerActiveInventoryModifications { - private final HumanEntity actor; - private final Location location; - private final HashMap modifications; - - public PlayerActiveInventoryModifications(HumanEntity actor, Location location) { - this.actor = actor; - this.location = location; - this.modifications = new HashMap<>(); - } - - public void addModification(ItemStack stack, int amount) { - if (amount == 0) { - return; - } - // if we have other viewers, we have to flush their changes - ArrayList allViewers = containersByLocation.get(location); - if (allViewers.size() > 1) { - for (PlayerActiveInventoryModifications other : allViewers) { - if (other != this) { - other.flush(); - } - } - } - - // consumer.getLogblock().getLogger().info("Modify container: " + stack + " change: " + amount); - stack = new ItemStack(stack); - stack.setAmount(1); - Integer existing = modifications.get(stack); - int newTotal = amount + (existing == null ? 0 : existing); - if (newTotal == 0) { - modifications.remove(stack); - } else { - modifications.put(stack, newTotal); - } - } - - public void flush() { - if (!modifications.isEmpty()) { - for (Entry e : modifications.entrySet()) { - ItemStack stack = e.getKey(); - int amount = e.getValue(); - stack.setAmount(Math.abs(amount)); - // consumer.getLogblock().getLogger().info("Store container: " + stack + " take: " + (amount < 0)); - consumer.queueChestAccess(Actor.actorFromEntity(actor), location, location.getWorld().getBlockAt(location).getBlockData(), stack, amount < 0); - } - modifications.clear(); - } - } - - public HumanEntity getActor() { - return actor; - } - - public Location getLocation() { - return location; - } - } - - private final Map containersByOwner = new HashMap<>(); - private final Map> containersByLocation = new HashMap<>(); - - public ChestAccessLogging(LogBlock lb) { - super(lb); - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onInventoryClose(InventoryCloseEvent event) { - final HumanEntity player = event.getPlayer(); - if (!isLogging(player.getWorld(), Logging.CHESTACCESS)) { - return; - } - InventoryHolder holder = event.getInventory().getHolder(); - if (holder instanceof BlockState || holder instanceof DoubleChest) { - final PlayerActiveInventoryModifications modifications = containersByOwner.remove(player); - if (modifications != null) { - final Location loc = modifications.getLocation(); - ArrayList atLocation = containersByLocation.get(loc); - atLocation.remove(modifications); - if (atLocation.isEmpty()) { - containersByLocation.remove(loc); - } - modifications.flush(); - } - } - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onInventoryOpen(InventoryOpenEvent event) { - final HumanEntity player = event.getPlayer(); - if (!isLogging(player.getWorld(), Logging.CHESTACCESS)) { - return; - } - if (event.getInventory() != null) { - InventoryHolder holder = event.getInventory().getHolder(); - if (holder instanceof BlockState || holder instanceof DoubleChest) { - if (getInventoryHolderType(holder) != Material.CRAFTING_TABLE) { - PlayerActiveInventoryModifications modifications = new PlayerActiveInventoryModifications(event.getPlayer(), getInventoryHolderLocation(holder)); - containersByOwner.put(modifications.getActor(), modifications); - containersByLocation.compute(modifications.getLocation(), (k, v) -> { - if (v == null) { - v = new ArrayList<>(); - } - v.add(modifications); - return v; - }); - } - } - } - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onInventoryClick(InventoryClickEvent event) { - final HumanEntity player = event.getWhoClicked(); - if (!isLogging(player.getWorld(), Logging.CHESTACCESS)) { - return; - } - InventoryHolder holder = event.getInventory().getHolder(); - if (holder instanceof BlockState || holder instanceof DoubleChest) { - final PlayerActiveInventoryModifications modifications = containersByOwner.get(player); - if (modifications != null) { - switch (event.getAction()) { - case PICKUP_ONE: - case DROP_ONE_SLOT: - if (event.getRawSlot() < event.getView().getTopInventory().getSize()) { - modifications.addModification(event.getCurrentItem(), -1); - } - break; - case PICKUP_HALF: - // server behaviour: round up - if (event.getRawSlot() < event.getView().getTopInventory().getSize()) { - modifications.addModification(event.getCurrentItem(), -(event.getCurrentItem().getAmount() + 1) / 2); - } - break; - case PICKUP_SOME: // oversized stack - can not take all when clicking - // server behaviour: leave a full stack in the slot, take everything else - if (event.getRawSlot() < event.getView().getTopInventory().getSize()) { - int taken = event.getCurrentItem().getAmount() - event.getCurrentItem().getMaxStackSize(); - modifications.addModification(event.getCursor(), -taken); - } - break; - case PICKUP_ALL: - case DROP_ALL_SLOT: - if (event.getRawSlot() < event.getView().getTopInventory().getSize()) { - modifications.addModification(event.getCurrentItem(), -event.getCurrentItem().getAmount()); - } - break; - case PLACE_ONE: - if (event.getRawSlot() < event.getView().getTopInventory().getSize()) { - modifications.addModification(event.getCursor(), 1); - } - break; - case PLACE_SOME: // not enough free place in target slot - // server behaviour: place as much as possible - if (event.getRawSlot() < event.getView().getTopInventory().getSize()) { - int placeable = event.getCurrentItem().getMaxStackSize() - event.getCurrentItem().getAmount(); - modifications.addModification(event.getCursor(), placeable); - } - break; - case PLACE_ALL: - if (event.getRawSlot() < event.getView().getTopInventory().getSize()) { - modifications.addModification(event.getCursor(), event.getCursor().getAmount()); - } - break; - case SWAP_WITH_CURSOR: - if (event.getRawSlot() < event.getView().getTopInventory().getSize()) { - modifications.addModification(event.getCursor(), event.getCursor().getAmount()); - modifications.addModification(event.getCurrentItem(), -event.getCurrentItem().getAmount()); - } - break; - case MOVE_TO_OTHER_INVENTORY: // shift + click - boolean removed = event.getRawSlot() < event.getView().getTopInventory().getSize(); - modifications.addModification(event.getCurrentItem(), event.getCurrentItem().getAmount() * (removed ? -1 : 1)); - break; - case COLLECT_TO_CURSOR: // double click - // server behaviour: first collect all with an amount != maxstacksize, then others, starting from slot 0 (container) - ItemStack cursor = event.getCursor(); - if (cursor == null) { - return; - } - int toPickUp = cursor.getMaxStackSize() - cursor.getAmount(); - int takenFromContainer = 0; - boolean takeFromFullStacks = false; - Inventory top = event.getView().getTopInventory(); - Inventory bottom = event.getView().getBottomInventory(); - while (toPickUp > 0) { - for (ItemStack stack : top.getStorageContents()) { - if (cursor.isSimilar(stack)) { - if (takeFromFullStacks == (stack.getAmount() == stack.getMaxStackSize())) { - int take = Math.min(toPickUp, stack.getAmount()); - toPickUp -= take; - takenFromContainer += take; - if (toPickUp <= 0) { - break; - } - } - } - } - if (toPickUp <= 0) { - break; - } - for (ItemStack stack : bottom.getStorageContents()) { - if (cursor.isSimilar(stack)) { - if (takeFromFullStacks == (stack.getAmount() == stack.getMaxStackSize())) { - int take = Math.min(toPickUp, stack.getAmount()); - toPickUp -= take; - if (toPickUp <= 0) { - break; - } - } - } - } - if (takeFromFullStacks) { - break; - } else { - takeFromFullStacks = true; - } - } - if (takenFromContainer > 0) { - modifications.addModification(event.getCursor(), -takenFromContainer); - } - break; - case HOTBAR_SWAP: // number key or offhand key - case HOTBAR_MOVE_AND_READD: // something was in the other slot - if (event.getRawSlot() < event.getView().getTopInventory().getSize()) { - ItemStack otherSlot = (event.getClick() == ClickType.SWAP_OFFHAND) ? event.getWhoClicked().getInventory().getItemInOffHand() : event.getWhoClicked().getInventory().getItem(event.getHotbarButton()); - if (event.getCurrentItem() != null && event.getCurrentItem().getType() != Material.AIR) { - modifications.addModification(event.getCurrentItem(), -event.getCurrentItem().getAmount()); - } - if (otherSlot != null && otherSlot.getType() != Material.AIR) { - modifications.addModification(otherSlot, otherSlot.getAmount()); - } - } - break; - case DROP_ALL_CURSOR: - case DROP_ONE_CURSOR: - case CLONE_STACK: - case NOTHING: - // only the cursor or nothing (but not the inventory) was modified - break; - case UNKNOWN: - default: - // unable to log something we don't know - consumer.getLogblock().getLogger().warning("Unknown inventory action by " + event.getWhoClicked().getName() + ": " + event.getAction() + " Slot: " + event.getSlot() + " Slot type: " + event.getSlotType()); - break; - } - } - } - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onInventoryDrag(InventoryDragEvent event) { - final HumanEntity player = event.getWhoClicked(); - if (!isLogging(player.getWorld(), Logging.CHESTACCESS)) { - return; - } - InventoryHolder holder = event.getInventory().getHolder(); - if (holder instanceof BlockState || holder instanceof DoubleChest) { - final PlayerActiveInventoryModifications modifications = containersByOwner.get(player); - if (modifications != null) { - Inventory container = event.getView().getTopInventory(); - int containerSize = container.getSize(); - for (Entry e : event.getNewItems().entrySet()) { - int slot = e.getKey(); - if (slot < containerSize) { - ItemStack old = container.getItem(slot); - int oldAmount = (old == null || old.getType() == Material.AIR) ? 0 : old.getAmount(); - modifications.addModification(e.getValue(), e.getValue().getAmount() - oldAmount); - } - } - } - } - } -} +package de.diddiz.LogBlock.listeners; + +import de.diddiz.LogBlock.Actor; +import de.diddiz.LogBlock.LogBlock; +import de.diddiz.LogBlock.Logging; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.BlockState; +import org.bukkit.block.DoubleChest; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.event.inventory.InventoryDragEvent; +import org.bukkit.event.inventory.InventoryOpenEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.ItemStack; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import static de.diddiz.LogBlock.config.Config.isLogging; +import static de.diddiz.LogBlock.util.BukkitUtils.*; + +public class ChestAccessLogging extends LoggingListener { + private class PlayerActiveInventoryModifications { + private final HumanEntity actor; + private final Location location; + private final HashMap modifications; + + public PlayerActiveInventoryModifications(HumanEntity actor, Location location) { + this.actor = actor; + this.location = location; + this.modifications = new HashMap<>(); + } + + public void addModification(ItemStack stack, int amount) { + if (amount == 0) { + return; + } + // if we have other viewers, we have to flush their changes + ArrayList allViewers = containersByLocation.get(location); + if (allViewers.size() > 1) { + for (PlayerActiveInventoryModifications other : allViewers) { + if (other != this) { + other.flush(); + } + } + } + + // consumer.getLogblock().getLogger().info("Modify container: " + stack + " change: " + amount); + stack = new ItemStack(stack); + stack.setAmount(1); + Integer existing = modifications.get(stack); + int newTotal = amount + (existing == null ? 0 : existing); + if (newTotal == 0) { + modifications.remove(stack); + } else { + modifications.put(stack, newTotal); + } + } + + public void flush() { + if (!modifications.isEmpty()) { + for (Entry e : modifications.entrySet()) { + ItemStack stack = e.getKey(); + int amount = e.getValue(); + stack.setAmount(Math.abs(amount)); + // consumer.getLogblock().getLogger().info("Store container: " + stack + " take: " + (amount < 0)); + consumer.queueChestAccess(Actor.actorFromEntity(actor), location, location.getWorld().getBlockAt(location).getBlockData(), stack, amount < 0); + } + modifications.clear(); + } + } + + public HumanEntity getActor() { + return actor; + } + + public Location getLocation() { + return location; + } + } + + private final Map containersByOwner = new HashMap<>(); + private final Map> containersByLocation = new HashMap<>(); + + public ChestAccessLogging(LogBlock lb) { + super(lb); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onInventoryClose(InventoryCloseEvent event) { + final HumanEntity player = event.getPlayer(); + if (!isLogging(player.getWorld(), Logging.CHESTACCESS)) { + return; + } + InventoryHolder holder = event.getInventory().getHolder(); + if (holder instanceof BlockState || holder instanceof DoubleChest) { + final PlayerActiveInventoryModifications modifications = containersByOwner.remove(player); + if (modifications != null) { + final Location loc = modifications.getLocation(); + ArrayList atLocation = containersByLocation.get(loc); + atLocation.remove(modifications); + if (atLocation.isEmpty()) { + containersByLocation.remove(loc); + } + modifications.flush(); + } + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onInventoryOpen(InventoryOpenEvent event) { + final HumanEntity player = event.getPlayer(); + if (!isLogging(player.getWorld(), Logging.CHESTACCESS)) { + return; + } + if (event.getInventory() != null) { + InventoryHolder holder = event.getInventory().getHolder(); + if (holder instanceof BlockState || holder instanceof DoubleChest) { + if (getInventoryHolderType(holder) != Material.CRAFTING_TABLE) { + PlayerActiveInventoryModifications modifications = new PlayerActiveInventoryModifications(event.getPlayer(), getInventoryHolderLocation(holder)); + containersByOwner.put(modifications.getActor(), modifications); + containersByLocation.compute(modifications.getLocation(), (k, v) -> { + if (v == null) { + v = new ArrayList<>(); + } + v.add(modifications); + return v; + }); + } + } + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onInventoryClick(InventoryClickEvent event) { + final HumanEntity player = event.getWhoClicked(); + if (!isLogging(player.getWorld(), Logging.CHESTACCESS)) { + return; + } + InventoryHolder holder = event.getInventory().getHolder(); + if (holder instanceof BlockState || holder instanceof DoubleChest) { + final PlayerActiveInventoryModifications modifications = containersByOwner.get(player); + if (modifications != null) { + switch (event.getAction()) { + case PICKUP_ONE: + case DROP_ONE_SLOT: + if (event.getRawSlot() < event.getView().getTopInventory().getSize()) { + modifications.addModification(event.getCurrentItem(), -1); + } + break; + case PICKUP_HALF: + // server behaviour: round up + if (event.getRawSlot() < event.getView().getTopInventory().getSize()) { + modifications.addModification(event.getCurrentItem(), -(event.getCurrentItem().getAmount() + 1) / 2); + } + break; + case PICKUP_SOME: // oversized stack - can not take all when clicking + // server behaviour: leave a full stack in the slot, take everything else + if (event.getRawSlot() < event.getView().getTopInventory().getSize()) { + int taken = event.getCurrentItem().getAmount() - event.getCurrentItem().getMaxStackSize(); + modifications.addModification(event.getCursor(), -taken); + } + break; + case PICKUP_ALL: + case DROP_ALL_SLOT: + if (event.getRawSlot() < event.getView().getTopInventory().getSize()) { + modifications.addModification(event.getCurrentItem(), -event.getCurrentItem().getAmount()); + } + break; + case PLACE_ONE: + if (event.getRawSlot() < event.getView().getTopInventory().getSize()) { + modifications.addModification(event.getCursor(), 1); + } + break; + case PLACE_SOME: // not enough free place in target slot + // server behaviour: place as much as possible + if (event.getRawSlot() < event.getView().getTopInventory().getSize()) { + int placeable = event.getCurrentItem().getMaxStackSize() - event.getCurrentItem().getAmount(); + modifications.addModification(event.getCursor(), placeable); + } + break; + case PLACE_ALL: + if (event.getRawSlot() < event.getView().getTopInventory().getSize()) { + modifications.addModification(event.getCursor(), event.getCursor().getAmount()); + } + break; + case SWAP_WITH_CURSOR: + if (event.getRawSlot() < event.getView().getTopInventory().getSize()) { + modifications.addModification(event.getCursor(), event.getCursor().getAmount()); + modifications.addModification(event.getCurrentItem(), -event.getCurrentItem().getAmount()); + } + break; + case MOVE_TO_OTHER_INVENTORY: // shift + click + boolean removed = event.getRawSlot() < event.getView().getTopInventory().getSize(); + modifications.addModification(event.getCurrentItem(), event.getCurrentItem().getAmount() * (removed ? -1 : 1)); + break; + case COLLECT_TO_CURSOR: // double click + // server behaviour: first collect all with an amount != maxstacksize, then others, starting from slot 0 (container) + ItemStack cursor = event.getCursor(); + if (cursor == null) { + return; + } + int toPickUp = cursor.getMaxStackSize() - cursor.getAmount(); + int takenFromContainer = 0; + boolean takeFromFullStacks = false; + Inventory top = event.getView().getTopInventory(); + Inventory bottom = event.getView().getBottomInventory(); + while (toPickUp > 0) { + for (ItemStack stack : top.getStorageContents()) { + if (cursor.isSimilar(stack)) { + if (takeFromFullStacks == (stack.getAmount() == stack.getMaxStackSize())) { + int take = Math.min(toPickUp, stack.getAmount()); + toPickUp -= take; + takenFromContainer += take; + if (toPickUp <= 0) { + break; + } + } + } + } + if (toPickUp <= 0) { + break; + } + for (ItemStack stack : bottom.getStorageContents()) { + if (cursor.isSimilar(stack)) { + if (takeFromFullStacks == (stack.getAmount() == stack.getMaxStackSize())) { + int take = Math.min(toPickUp, stack.getAmount()); + toPickUp -= take; + if (toPickUp <= 0) { + break; + } + } + } + } + if (takeFromFullStacks) { + break; + } else { + takeFromFullStacks = true; + } + } + if (takenFromContainer > 0) { + modifications.addModification(event.getCursor(), -takenFromContainer); + } + break; + case HOTBAR_SWAP: // number key or offhand key + case HOTBAR_MOVE_AND_READD: // something was in the other slot + if (event.getRawSlot() < event.getView().getTopInventory().getSize()) { + ItemStack otherSlot = (event.getClick() == ClickType.SWAP_OFFHAND) ? event.getWhoClicked().getInventory().getItemInOffHand() : event.getWhoClicked().getInventory().getItem(event.getHotbarButton()); + if (event.getCurrentItem() != null && event.getCurrentItem().getType() != Material.AIR) { + modifications.addModification(event.getCurrentItem(), -event.getCurrentItem().getAmount()); + } + if (otherSlot != null && otherSlot.getType() != Material.AIR) { + modifications.addModification(otherSlot, otherSlot.getAmount()); + } + } + break; + case DROP_ALL_CURSOR: + case DROP_ONE_CURSOR: + case CLONE_STACK: + case NOTHING: + // only the cursor or nothing (but not the inventory) was modified + break; + case UNKNOWN: + default: + // unable to log something we don't know + consumer.getLogblock().getLogger().warning("Unknown inventory action by " + event.getWhoClicked().getName() + ": " + event.getAction() + " Slot: " + event.getSlot() + " Slot type: " + event.getSlotType()); + break; + } + } + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onInventoryDrag(InventoryDragEvent event) { + final HumanEntity player = event.getWhoClicked(); + if (!isLogging(player.getWorld(), Logging.CHESTACCESS)) { + return; + } + InventoryHolder holder = event.getInventory().getHolder(); + if (holder instanceof BlockState || holder instanceof DoubleChest) { + final PlayerActiveInventoryModifications modifications = containersByOwner.get(player); + if (modifications != null) { + Inventory container = event.getView().getTopInventory(); + int containerSize = container.getSize(); + for (Entry e : event.getNewItems().entrySet()) { + int slot = e.getKey(); + if (slot < containerSize) { + ItemStack old = container.getItem(slot); + int oldAmount = (old == null || old.getType() == Material.AIR) ? 0 : old.getAmount(); + modifications.addModification(e.getValue(), e.getValue().getAmount() - oldAmount); + } + } + } + } + } +} diff --git a/src/main/java/de/diddiz/LogBlock/listeners/EndermenLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/EndermenLogging.java index 5b89ad4..b7fe92a 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/EndermenLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/EndermenLogging.java @@ -1,24 +1,24 @@ -package de.diddiz.LogBlock.listeners; - -import de.diddiz.LogBlock.Actor; -import de.diddiz.LogBlock.LogBlock; -import de.diddiz.LogBlock.Logging; -import org.bukkit.entity.Enderman; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.entity.EntityChangeBlockEvent; - -import static de.diddiz.LogBlock.config.Config.isLogging; - -public class EndermenLogging extends LoggingListener { - public EndermenLogging(LogBlock lb) { - super(lb); - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onEntityChangeBlock(EntityChangeBlockEvent event) { - if (event.getEntity() instanceof Enderman && isLogging(event.getBlock().getWorld(), Logging.ENDERMEN)) { - consumer.queueBlockReplace(new Actor("Enderman"), event.getBlock().getState(), event.getBlockData()); // Figure out how to get the data of the placed block; - } - } -} +package de.diddiz.LogBlock.listeners; + +import de.diddiz.LogBlock.Actor; +import de.diddiz.LogBlock.LogBlock; +import de.diddiz.LogBlock.Logging; +import org.bukkit.entity.Enderman; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.EntityChangeBlockEvent; + +import static de.diddiz.LogBlock.config.Config.isLogging; + +public class EndermenLogging extends LoggingListener { + public EndermenLogging(LogBlock lb) { + super(lb); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onEntityChangeBlock(EntityChangeBlockEvent event) { + if (event.getEntity() instanceof Enderman && isLogging(event.getBlock().getWorld(), Logging.ENDERMEN)) { + consumer.queueBlockReplace(new Actor("Enderman"), event.getBlock().getState(), event.getBlockData()); // Figure out how to get the data of the placed block; + } + } +} diff --git a/src/main/java/de/diddiz/LogBlock/listeners/ExplosionLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/ExplosionLogging.java index 20fd01d..62801a6 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/ExplosionLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/ExplosionLogging.java @@ -1,229 +1,229 @@ -package de.diddiz.LogBlock.listeners; - -import de.diddiz.LogBlock.Actor; -import de.diddiz.LogBlock.LogBlock; -import de.diddiz.LogBlock.Logging; -import de.diddiz.LogBlock.config.Config; -import de.diddiz.LogBlock.config.WorldConfig; -import de.diddiz.LogBlock.util.BukkitUtils; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.block.data.type.RespawnAnchor; -import org.bukkit.entity.*; -import org.bukkit.entity.minecart.ExplosiveMinecart; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.block.Action; -import org.bukkit.event.block.BlockExplodeEvent; -import org.bukkit.event.entity.EntityExplodeEvent; -import org.bukkit.event.player.PlayerInteractEvent; -import org.bukkit.inventory.ItemStack; -import org.bukkit.projectiles.ProjectileSource; -import org.bukkit.scheduler.BukkitRunnable; - -import static de.diddiz.LogBlock.config.Config.getWorldConfig; -import static de.diddiz.LogBlock.config.Config.logCreeperExplosionsAsPlayerWhoTriggeredThese; -import static de.diddiz.LogBlock.util.BukkitUtils.getContainerBlocks; - -import java.util.UUID; - -public class ExplosionLogging extends LoggingListener { - - private UUID lastBedInteractionPlayer; - private Location lastBedInteractionLocation; - private UUID lastRespawnAnchorInteractionPlayer; - private Location lastRespawnAnchorInteractionLocation; - - public ExplosionLogging(LogBlock lb) { - super(lb); - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onEntityExplode(EntityExplodeEvent event) { - final WorldConfig wcfg = getWorldConfig(event.getLocation().getWorld()); - if (wcfg != null) { - Actor actor = new Actor("Explosion"); - Entity source = event.getEntity(); - if (source == null) { - if (!wcfg.isLogging(Logging.MISCEXPLOSION)) { - return; - } - } else if (source instanceof TNTPrimed) { - if (!wcfg.isLogging(Logging.TNTEXPLOSION)) { - return; - } - actor = new Actor("TNT"); - } else if (source instanceof ExplosiveMinecart) { - if (!wcfg.isLogging(Logging.TNTMINECARTEXPLOSION)) { - return; - } - actor = new Actor("TNTMinecart"); - } else if (source instanceof Creeper) { - if (!wcfg.isLogging(Logging.CREEPEREXPLOSION)) { - return; - } - if (logCreeperExplosionsAsPlayerWhoTriggeredThese) { - final Entity target = ((Creeper) source).getTarget(); - actor = target instanceof Player ? Actor.actorFromEntity(target) : new Actor("Creeper"); - } else { - actor = new Actor("Creeper"); - } - } else if (source instanceof Wither) { - if (!wcfg.isLogging(Logging.WITHER)) { - return; - } - actor = Actor.actorFromEntity(source); - } else if (source instanceof WitherSkull) { - if (!wcfg.isLogging(Logging.WITHER_SKULL)) { - return; - } - actor = Actor.actorFromEntity(source); - } else if (source instanceof Fireball) { - Fireball fireball = (Fireball) source; - ProjectileSource shooter = fireball.getShooter(); - if (shooter == null) { - if (!wcfg.isLogging(Logging.MISCEXPLOSION)) { - return; - } - actor = Actor.actorFromEntity(source); - } else if (shooter instanceof Ghast) { - if (!wcfg.isLogging(Logging.GHASTFIREBALLEXPLOSION)) { - return; - } - actor = Actor.actorFromProjectileSource(shooter); - } else if (shooter instanceof Wither) { - if (!wcfg.isLogging(Logging.WITHER)) { - return; - } - actor = Actor.actorFromProjectileSource(shooter); - } - } else if (source instanceof EnderDragon) { - if (!wcfg.isLogging(Logging.ENDERDRAGON)) { - return; - } - actor = Actor.actorFromEntity(source); - } else if (source instanceof EnderCrystal) { - if (!wcfg.isLogging(Logging.ENDERCRYSTALEXPLOSION)) { - return; - } - actor = Actor.actorFromEntity(source); - - } else { - if (!wcfg.isLogging(Logging.MISCEXPLOSION)) { - return; - } - } - for (final Block block : event.blockList()) { - final Material type = block.getType(); - if (wcfg.isLogging(Logging.CHESTACCESS) && getContainerBlocks().contains(type) && !BukkitUtils.getShulkerBoxBlocks().contains(type)) { - consumer.queueContainerBreak(actor, block.getState()); - } else { - consumer.queueBlockBreak(actor, block.getState()); - } - } - } - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onPlayerInteract(PlayerInteractEvent event) { - if (event.getAction() == Action.RIGHT_CLICK_BLOCK && event.hasBlock()) { - Block block = event.getClickedBlock(); - if (BukkitUtils.isBed(block.getType()) && !block.getWorld().isBedWorks()) { - if (!Config.isLogging(block.getWorld(), Logging.BEDEXPLOSION)) { - return; - } - lastBedInteractionPlayer = event.getPlayer().getUniqueId(); - lastBedInteractionLocation = block.getLocation(); - new BukkitRunnable() { - @Override - public void run() { - lastBedInteractionPlayer = null; - lastBedInteractionLocation = null; - } - }.runTask(LogBlock.getInstance()); - } else if (block.getType() == Material.RESPAWN_ANCHOR && block.getBlockData() instanceof RespawnAnchor data) { - if (!Config.isLogging(block.getWorld(), Logging.RESPAWNANCHOREXPLOSION)) { - return; - } - ItemStack inHand = event.getItem(); - int charges = data.getCharges(); - if (charges < data.getMaximumCharges() && inHand != null && inHand.getType() == Material.GLOWSTONE) { - // charge - Actor actor = Actor.actorFromEntity(event.getPlayer()); - RespawnAnchor blockNew = (RespawnAnchor) data.clone(); - blockNew.setCharges(charges + 1); - consumer.queueBlockReplace(actor, block.getState(), blockNew); - } else if (charges > 0 && !block.getWorld().isRespawnAnchorWorks()) { - // explode - Actor actor = Actor.actorFromEntity(event.getPlayer()); - consumer.queueBlockBreak(actor, block.getState()); - lastRespawnAnchorInteractionPlayer = event.getPlayer().getUniqueId(); - lastRespawnAnchorInteractionLocation = block.getLocation(); - new BukkitRunnable() { - @Override - public void run() { - lastRespawnAnchorInteractionPlayer = null; - lastRespawnAnchorInteractionLocation = null; - } - }.runTask(LogBlock.getInstance()); - } - } - } - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onBlockExplode(BlockExplodeEvent event) { - Player bedCause = null; - if (lastBedInteractionPlayer != null && lastBedInteractionLocation != null) { - Location block = event.getBlock().getLocation(); - if (lastBedInteractionLocation.getWorld() == block.getWorld() && block.distanceSquared(lastBedInteractionLocation) <= 1) { - bedCause = Bukkit.getPlayer(lastBedInteractionPlayer); - } - } - Player respawnAnchorCause = null; - if (lastRespawnAnchorInteractionPlayer != null && lastRespawnAnchorInteractionLocation != null) { - Location block = event.getBlock().getLocation(); - if (lastRespawnAnchorInteractionLocation.equals(block)) { - respawnAnchorCause = Bukkit.getPlayer(lastRespawnAnchorInteractionPlayer); - } - } - - for (final Block block : event.blockList()) { - final WorldConfig wcfg = getWorldConfig(block.getLocation().getWorld()); - - if (wcfg != null) { - Actor actor = new Actor("Explosion"); - if (bedCause != null) { - if (!wcfg.isLogging(Logging.BEDEXPLOSION)) { - return; - } - if (Config.logBedExplosionsAsPlayerWhoTriggeredThese) { - actor = Actor.actorFromEntity(bedCause); - } else { - actor = new Actor("BedExplosion"); - } - } else if (respawnAnchorCause != null) { - if (!wcfg.isLogging(Logging.RESPAWNANCHOREXPLOSION)) { - return; - } - if (Config.logBedExplosionsAsPlayerWhoTriggeredThese) { - actor = Actor.actorFromEntity(respawnAnchorCause); - } else { - actor = new Actor("RespawnAnchorExplosion"); - } - } else if (!wcfg.isLogging(Logging.MISCEXPLOSION)) { - return; - } - - final Material type = block.getType(); - if (wcfg.isLogging(Logging.CHESTACCESS) && getContainerBlocks().contains(type) && !BukkitUtils.getShulkerBoxBlocks().contains(type)) { - consumer.queueContainerBreak(actor, block.getState()); - } else { - consumer.queueBlockBreak(actor, block.getState()); - } - } - } - } -} +package de.diddiz.LogBlock.listeners; + +import de.diddiz.LogBlock.Actor; +import de.diddiz.LogBlock.LogBlock; +import de.diddiz.LogBlock.Logging; +import de.diddiz.LogBlock.config.Config; +import de.diddiz.LogBlock.config.WorldConfig; +import de.diddiz.LogBlock.util.BukkitUtils; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.data.type.RespawnAnchor; +import org.bukkit.entity.*; +import org.bukkit.entity.minecart.ExplosiveMinecart; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.Action; +import org.bukkit.event.block.BlockExplodeEvent; +import org.bukkit.event.entity.EntityExplodeEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.projectiles.ProjectileSource; +import org.bukkit.scheduler.BukkitRunnable; + +import static de.diddiz.LogBlock.config.Config.getWorldConfig; +import static de.diddiz.LogBlock.config.Config.logCreeperExplosionsAsPlayerWhoTriggeredThese; +import static de.diddiz.LogBlock.util.BukkitUtils.getContainerBlocks; + +import java.util.UUID; + +public class ExplosionLogging extends LoggingListener { + + private UUID lastBedInteractionPlayer; + private Location lastBedInteractionLocation; + private UUID lastRespawnAnchorInteractionPlayer; + private Location lastRespawnAnchorInteractionLocation; + + public ExplosionLogging(LogBlock lb) { + super(lb); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onEntityExplode(EntityExplodeEvent event) { + final WorldConfig wcfg = getWorldConfig(event.getLocation().getWorld()); + if (wcfg != null) { + Actor actor = new Actor("Explosion"); + Entity source = event.getEntity(); + if (source == null) { + if (!wcfg.isLogging(Logging.MISCEXPLOSION)) { + return; + } + } else if (source instanceof TNTPrimed) { + if (!wcfg.isLogging(Logging.TNTEXPLOSION)) { + return; + } + actor = new Actor("TNT"); + } else if (source instanceof ExplosiveMinecart) { + if (!wcfg.isLogging(Logging.TNTMINECARTEXPLOSION)) { + return; + } + actor = new Actor("TNTMinecart"); + } else if (source instanceof Creeper) { + if (!wcfg.isLogging(Logging.CREEPEREXPLOSION)) { + return; + } + if (logCreeperExplosionsAsPlayerWhoTriggeredThese) { + final Entity target = ((Creeper) source).getTarget(); + actor = target instanceof Player ? Actor.actorFromEntity(target) : new Actor("Creeper"); + } else { + actor = new Actor("Creeper"); + } + } else if (source instanceof Wither) { + if (!wcfg.isLogging(Logging.WITHER)) { + return; + } + actor = Actor.actorFromEntity(source); + } else if (source instanceof WitherSkull) { + if (!wcfg.isLogging(Logging.WITHER_SKULL)) { + return; + } + actor = Actor.actorFromEntity(source); + } else if (source instanceof Fireball) { + Fireball fireball = (Fireball) source; + ProjectileSource shooter = fireball.getShooter(); + if (shooter == null) { + if (!wcfg.isLogging(Logging.MISCEXPLOSION)) { + return; + } + actor = Actor.actorFromEntity(source); + } else if (shooter instanceof Ghast) { + if (!wcfg.isLogging(Logging.GHASTFIREBALLEXPLOSION)) { + return; + } + actor = Actor.actorFromProjectileSource(shooter); + } else if (shooter instanceof Wither) { + if (!wcfg.isLogging(Logging.WITHER)) { + return; + } + actor = Actor.actorFromProjectileSource(shooter); + } + } else if (source instanceof EnderDragon) { + if (!wcfg.isLogging(Logging.ENDERDRAGON)) { + return; + } + actor = Actor.actorFromEntity(source); + } else if (source instanceof EnderCrystal) { + if (!wcfg.isLogging(Logging.ENDERCRYSTALEXPLOSION)) { + return; + } + actor = Actor.actorFromEntity(source); + + } else { + if (!wcfg.isLogging(Logging.MISCEXPLOSION)) { + return; + } + } + for (final Block block : event.blockList()) { + final Material type = block.getType(); + if (wcfg.isLogging(Logging.CHESTACCESS) && getContainerBlocks().contains(type) && !BukkitUtils.getShulkerBoxBlocks().contains(type)) { + consumer.queueContainerBreak(actor, block.getState()); + } else { + consumer.queueBlockBreak(actor, block.getState()); + } + } + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onPlayerInteract(PlayerInteractEvent event) { + if (event.getAction() == Action.RIGHT_CLICK_BLOCK && event.hasBlock()) { + Block block = event.getClickedBlock(); + if (BukkitUtils.isBed(block.getType()) && !block.getWorld().isBedWorks()) { + if (!Config.isLogging(block.getWorld(), Logging.BEDEXPLOSION)) { + return; + } + lastBedInteractionPlayer = event.getPlayer().getUniqueId(); + lastBedInteractionLocation = block.getLocation(); + new BukkitRunnable() { + @Override + public void run() { + lastBedInteractionPlayer = null; + lastBedInteractionLocation = null; + } + }.runTask(LogBlock.getInstance()); + } else if (block.getType() == Material.RESPAWN_ANCHOR && block.getBlockData() instanceof RespawnAnchor data) { + if (!Config.isLogging(block.getWorld(), Logging.RESPAWNANCHOREXPLOSION)) { + return; + } + ItemStack inHand = event.getItem(); + int charges = data.getCharges(); + if (charges < data.getMaximumCharges() && inHand != null && inHand.getType() == Material.GLOWSTONE) { + // charge + Actor actor = Actor.actorFromEntity(event.getPlayer()); + RespawnAnchor blockNew = (RespawnAnchor) data.clone(); + blockNew.setCharges(charges + 1); + consumer.queueBlockReplace(actor, block.getState(), blockNew); + } else if (charges > 0 && !block.getWorld().isRespawnAnchorWorks()) { + // explode + Actor actor = Actor.actorFromEntity(event.getPlayer()); + consumer.queueBlockBreak(actor, block.getState()); + lastRespawnAnchorInteractionPlayer = event.getPlayer().getUniqueId(); + lastRespawnAnchorInteractionLocation = block.getLocation(); + new BukkitRunnable() { + @Override + public void run() { + lastRespawnAnchorInteractionPlayer = null; + lastRespawnAnchorInteractionLocation = null; + } + }.runTask(LogBlock.getInstance()); + } + } + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onBlockExplode(BlockExplodeEvent event) { + Player bedCause = null; + if (lastBedInteractionPlayer != null && lastBedInteractionLocation != null) { + Location block = event.getBlock().getLocation(); + if (lastBedInteractionLocation.getWorld() == block.getWorld() && block.distanceSquared(lastBedInteractionLocation) <= 1) { + bedCause = Bukkit.getPlayer(lastBedInteractionPlayer); + } + } + Player respawnAnchorCause = null; + if (lastRespawnAnchorInteractionPlayer != null && lastRespawnAnchorInteractionLocation != null) { + Location block = event.getBlock().getLocation(); + if (lastRespawnAnchorInteractionLocation.equals(block)) { + respawnAnchorCause = Bukkit.getPlayer(lastRespawnAnchorInteractionPlayer); + } + } + + for (final Block block : event.blockList()) { + final WorldConfig wcfg = getWorldConfig(block.getLocation().getWorld()); + + if (wcfg != null) { + Actor actor = new Actor("Explosion"); + if (bedCause != null) { + if (!wcfg.isLogging(Logging.BEDEXPLOSION)) { + return; + } + if (Config.logBedExplosionsAsPlayerWhoTriggeredThese) { + actor = Actor.actorFromEntity(bedCause); + } else { + actor = new Actor("BedExplosion"); + } + } else if (respawnAnchorCause != null) { + if (!wcfg.isLogging(Logging.RESPAWNANCHOREXPLOSION)) { + return; + } + if (Config.logBedExplosionsAsPlayerWhoTriggeredThese) { + actor = Actor.actorFromEntity(respawnAnchorCause); + } else { + actor = new Actor("RespawnAnchorExplosion"); + } + } else if (!wcfg.isLogging(Logging.MISCEXPLOSION)) { + return; + } + + final Material type = block.getType(); + if (wcfg.isLogging(Logging.CHESTACCESS) && getContainerBlocks().contains(type) && !BukkitUtils.getShulkerBoxBlocks().contains(type)) { + consumer.queueContainerBreak(actor, block.getState()); + } else { + consumer.queueBlockBreak(actor, block.getState()); + } + } + } + } +} diff --git a/src/main/java/de/diddiz/LogBlock/listeners/FluidFlowLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/FluidFlowLogging.java index 9e85a5b..9c9e1b3 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/FluidFlowLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/FluidFlowLogging.java @@ -1,127 +1,127 @@ -package de.diddiz.LogBlock.listeners; - -import de.diddiz.LogBlock.Actor; -import de.diddiz.LogBlock.LogBlock; -import de.diddiz.LogBlock.Logging; -import de.diddiz.LogBlock.config.Config; -import de.diddiz.LogBlock.config.WorldConfig; -import de.diddiz.LogBlock.util.BukkitUtils; -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.Levelled; -import org.bukkit.block.data.Waterlogged; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.block.BlockFormEvent; -import org.bukkit.event.block.BlockFromToEvent; - -import static de.diddiz.LogBlock.config.Config.getWorldConfig; - -public class FluidFlowLogging extends LoggingListener { - - public FluidFlowLogging(LogBlock lb) { - super(lb); - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onBlockFromTo(BlockFromToEvent event) { - final WorldConfig wcfg = getWorldConfig(event.getBlock().getWorld()); - if (wcfg != null && (wcfg.isLogging(Logging.WATERFLOW) || wcfg.isLogging(Logging.LAVAFLOW))) { - final BlockData blockDataFrom = event.getBlock().getBlockData(); - Material typeFrom = blockDataFrom.getMaterial(); - boolean fromWaterlogged = false; - if (blockDataFrom instanceof Waterlogged) { - typeFrom = Material.WATER; - fromWaterlogged = true; - } - if (typeFrom == Material.SEAGRASS || typeFrom == Material.KELP_PLANT || typeFrom == Material.KELP) { - typeFrom = Material.WATER; - fromWaterlogged = true; - } - - Block source = Config.logFluidFlowAsPlayerWhoTriggeredIt ? event.getBlock() : null; - final Block to = event.getToBlock(); - final Material typeTo = to.getType(); - boolean down = event.getFace() == BlockFace.DOWN; - final boolean canFlow = BukkitUtils.isEmpty(typeTo) || BukkitUtils.getNonFluidProofBlocks().contains(typeTo); - if (typeFrom == Material.LAVA && wcfg.isLogging(Logging.LAVAFLOW)) { - Levelled levelledFrom = (Levelled) blockDataFrom; - if (canFlow) { - if (isSurroundedByWater(to) && levelledFrom.getLevel() <= 2) { - consumer.queueBlockReplace(new Actor("LavaFlow", source), to.getState(), Material.COBBLESTONE.createBlockData()); - } else { - Levelled newBlock = (Levelled) blockDataFrom.clone(); - newBlock.setLevel(down ? 1 : Math.min(levelledFrom.getLevel() + 1, levelledFrom.getMaximumLevel())); - if (BukkitUtils.isEmpty(typeTo)) { - consumer.queueBlockPlace(new Actor("LavaFlow", source), to.getLocation(), newBlock); - } else { - consumer.queueBlockReplace(new Actor("LavaFlow", source), to.getState(), newBlock); - } - } - } else if (typeTo == Material.WATER) { - if (down) { - consumer.queueBlockReplace(new Actor("LavaFlow", source), to.getState(), Material.STONE.createBlockData()); - } else { - consumer.queueBlockReplace(new Actor("LavaFlow", source), to.getState(), Material.COBBLESTONE.createBlockData()); - } - } - } else if ((typeFrom == Material.WATER) && wcfg.isLogging(Logging.WATERFLOW)) { - Levelled levelledFrom = fromWaterlogged ? null : (Levelled) blockDataFrom; - Levelled newBlock = (Levelled) Material.WATER.createBlockData(); - newBlock.setLevel(fromWaterlogged || down ? 1 : Math.min(levelledFrom.getLevel() + 1, levelledFrom.getMaximumLevel())); - if (BukkitUtils.isEmpty(typeTo)) { - consumer.queueBlockPlace(new Actor("WaterFlow", source), to.getLocation(), newBlock); - } else if (BukkitUtils.getNonFluidProofBlocks().contains(typeTo)) { - consumer.queueBlockReplace(new Actor("WaterFlow", source), to.getState(), newBlock); - } else if (typeTo == Material.LAVA) { - int toLevel = ((Levelled) to.getBlockData()).getLevel(); - if (toLevel == 0) { - consumer.queueBlockReplace(new Actor("WaterFlow", source), to.getState(), Material.OBSIDIAN.createBlockData()); - } else if (event.getFace() == BlockFace.DOWN) { - consumer.queueBlockReplace(new Actor("WaterFlow", source), to.getState(), Material.STONE.createBlockData()); - } - } - if (BukkitUtils.isEmpty(typeTo) || BukkitUtils.getNonFluidProofBlocks().contains(typeTo)) { - for (final BlockFace face : new BlockFace[] { BlockFace.DOWN, BlockFace.NORTH, BlockFace.WEST, BlockFace.EAST, BlockFace.SOUTH }) { - final Block lower = to.getRelative(face); - if (lower.getType() == Material.LAVA) { - int toLevel = ((Levelled) lower.getBlockData()).getLevel(); - if (toLevel == 0) { - consumer.queueBlockReplace(new Actor("WaterFlow", source), lower.getState(), Material.OBSIDIAN.createBlockData()); - } else if (event.getFace() == BlockFace.DOWN) { - consumer.queueBlockReplace(new Actor("WaterFlow", source), lower.getState(), Material.STONE.createBlockData()); - } - } - } - } - } - } - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onBlockForm(BlockFormEvent event) { - final WorldConfig wcfg = getWorldConfig(event.getBlock().getWorld()); - if (wcfg != null && (wcfg.isLogging(Logging.WATERFLOW) || wcfg.isLogging(Logging.LAVAFLOW))) { - if (wcfg.isLogging(Logging.LAVAFLOW) && event.getBlock().getType() == Material.WATER && event.getNewState().getType() == Material.COBBLESTONE) { - consumer.queueBlockReplace(new Actor("LavaFlow"), event.getBlock().getBlockData(), event.getNewState()); - } - if (wcfg.isLogging(Logging.WATERFLOW) && event.getBlock().getType() == Material.LAVA) { - consumer.queueBlockReplace(new Actor("WaterFlow"), event.getBlock().getBlockData(), event.getNewState()); - } - if (wcfg.isLogging(Logging.WATERFLOW) && BukkitUtils.isConcreteBlock(event.getNewState().getType())) { - consumer.queueBlockReplace(new Actor("WaterFlow"), event.getBlock().getBlockData(), event.getNewState()); - } - } - } - - private static boolean isSurroundedByWater(Block block) { - for (final BlockFace face : new BlockFace[] { BlockFace.NORTH, BlockFace.WEST, BlockFace.EAST, BlockFace.SOUTH }) { - if (block.getRelative(face).getType() == Material.WATER) { - return true; - } - } - return false; - } -} +package de.diddiz.LogBlock.listeners; + +import de.diddiz.LogBlock.Actor; +import de.diddiz.LogBlock.LogBlock; +import de.diddiz.LogBlock.Logging; +import de.diddiz.LogBlock.config.Config; +import de.diddiz.LogBlock.config.WorldConfig; +import de.diddiz.LogBlock.util.BukkitUtils; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.Levelled; +import org.bukkit.block.data.Waterlogged; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockFormEvent; +import org.bukkit.event.block.BlockFromToEvent; + +import static de.diddiz.LogBlock.config.Config.getWorldConfig; + +public class FluidFlowLogging extends LoggingListener { + + public FluidFlowLogging(LogBlock lb) { + super(lb); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onBlockFromTo(BlockFromToEvent event) { + final WorldConfig wcfg = getWorldConfig(event.getBlock().getWorld()); + if (wcfg != null && (wcfg.isLogging(Logging.WATERFLOW) || wcfg.isLogging(Logging.LAVAFLOW))) { + final BlockData blockDataFrom = event.getBlock().getBlockData(); + Material typeFrom = blockDataFrom.getMaterial(); + boolean fromWaterlogged = false; + if (blockDataFrom instanceof Waterlogged) { + typeFrom = Material.WATER; + fromWaterlogged = true; + } + if (typeFrom == Material.SEAGRASS || typeFrom == Material.KELP_PLANT || typeFrom == Material.KELP) { + typeFrom = Material.WATER; + fromWaterlogged = true; + } + + Block source = Config.logFluidFlowAsPlayerWhoTriggeredIt ? event.getBlock() : null; + final Block to = event.getToBlock(); + final Material typeTo = to.getType(); + boolean down = event.getFace() == BlockFace.DOWN; + final boolean canFlow = BukkitUtils.isEmpty(typeTo) || BukkitUtils.getNonFluidProofBlocks().contains(typeTo); + if (typeFrom == Material.LAVA && wcfg.isLogging(Logging.LAVAFLOW)) { + Levelled levelledFrom = (Levelled) blockDataFrom; + if (canFlow) { + if (isSurroundedByWater(to) && levelledFrom.getLevel() <= 2) { + consumer.queueBlockReplace(new Actor("LavaFlow", source), to.getState(), Material.COBBLESTONE.createBlockData()); + } else { + Levelled newBlock = (Levelled) blockDataFrom.clone(); + newBlock.setLevel(down ? 1 : Math.min(levelledFrom.getLevel() + 1, levelledFrom.getMaximumLevel())); + if (BukkitUtils.isEmpty(typeTo)) { + consumer.queueBlockPlace(new Actor("LavaFlow", source), to.getLocation(), newBlock); + } else { + consumer.queueBlockReplace(new Actor("LavaFlow", source), to.getState(), newBlock); + } + } + } else if (typeTo == Material.WATER) { + if (down) { + consumer.queueBlockReplace(new Actor("LavaFlow", source), to.getState(), Material.STONE.createBlockData()); + } else { + consumer.queueBlockReplace(new Actor("LavaFlow", source), to.getState(), Material.COBBLESTONE.createBlockData()); + } + } + } else if ((typeFrom == Material.WATER) && wcfg.isLogging(Logging.WATERFLOW)) { + Levelled levelledFrom = fromWaterlogged ? null : (Levelled) blockDataFrom; + Levelled newBlock = (Levelled) Material.WATER.createBlockData(); + newBlock.setLevel(fromWaterlogged || down ? 1 : Math.min(levelledFrom.getLevel() + 1, levelledFrom.getMaximumLevel())); + if (BukkitUtils.isEmpty(typeTo)) { + consumer.queueBlockPlace(new Actor("WaterFlow", source), to.getLocation(), newBlock); + } else if (BukkitUtils.getNonFluidProofBlocks().contains(typeTo)) { + consumer.queueBlockReplace(new Actor("WaterFlow", source), to.getState(), newBlock); + } else if (typeTo == Material.LAVA) { + int toLevel = ((Levelled) to.getBlockData()).getLevel(); + if (toLevel == 0) { + consumer.queueBlockReplace(new Actor("WaterFlow", source), to.getState(), Material.OBSIDIAN.createBlockData()); + } else if (event.getFace() == BlockFace.DOWN) { + consumer.queueBlockReplace(new Actor("WaterFlow", source), to.getState(), Material.STONE.createBlockData()); + } + } + if (BukkitUtils.isEmpty(typeTo) || BukkitUtils.getNonFluidProofBlocks().contains(typeTo)) { + for (final BlockFace face : new BlockFace[] { BlockFace.DOWN, BlockFace.NORTH, BlockFace.WEST, BlockFace.EAST, BlockFace.SOUTH }) { + final Block lower = to.getRelative(face); + if (lower.getType() == Material.LAVA) { + int toLevel = ((Levelled) lower.getBlockData()).getLevel(); + if (toLevel == 0) { + consumer.queueBlockReplace(new Actor("WaterFlow", source), lower.getState(), Material.OBSIDIAN.createBlockData()); + } else if (event.getFace() == BlockFace.DOWN) { + consumer.queueBlockReplace(new Actor("WaterFlow", source), lower.getState(), Material.STONE.createBlockData()); + } + } + } + } + } + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onBlockForm(BlockFormEvent event) { + final WorldConfig wcfg = getWorldConfig(event.getBlock().getWorld()); + if (wcfg != null && (wcfg.isLogging(Logging.WATERFLOW) || wcfg.isLogging(Logging.LAVAFLOW))) { + if (wcfg.isLogging(Logging.LAVAFLOW) && event.getBlock().getType() == Material.WATER && event.getNewState().getType() == Material.COBBLESTONE) { + consumer.queueBlockReplace(new Actor("LavaFlow"), event.getBlock().getBlockData(), event.getNewState()); + } + if (wcfg.isLogging(Logging.WATERFLOW) && event.getBlock().getType() == Material.LAVA) { + consumer.queueBlockReplace(new Actor("WaterFlow"), event.getBlock().getBlockData(), event.getNewState()); + } + if (wcfg.isLogging(Logging.WATERFLOW) && BukkitUtils.isConcreteBlock(event.getNewState().getType())) { + consumer.queueBlockReplace(new Actor("WaterFlow"), event.getBlock().getBlockData(), event.getNewState()); + } + } + } + + private static boolean isSurroundedByWater(Block block) { + for (final BlockFace face : new BlockFace[] { BlockFace.NORTH, BlockFace.WEST, BlockFace.EAST, BlockFace.SOUTH }) { + if (block.getRelative(face).getType() == Material.WATER) { + return true; + } + } + return false; + } +} diff --git a/src/main/java/de/diddiz/LogBlock/listeners/InteractLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/InteractLogging.java index 88e68ff..55dd713 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/InteractLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/InteractLogging.java @@ -1,276 +1,276 @@ -package de.diddiz.LogBlock.listeners; - -import de.diddiz.LogBlock.Actor; -import de.diddiz.LogBlock.LogBlock; -import de.diddiz.LogBlock.Logging; -import de.diddiz.LogBlock.config.WorldConfig; -import de.diddiz.LogBlock.util.BukkitUtils; -import de.diddiz.LogBlock.util.Reflections; -import java.util.UUID; -import org.bukkit.DyeColor; -import org.bukkit.GameEvent; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.Note; -import org.bukkit.Note.Tone; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.block.BlockState; -import org.bukkit.block.Sign; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.Directional; -import org.bukkit.block.data.Lightable; -import org.bukkit.block.data.Openable; -import org.bukkit.block.data.type.Cake; -import org.bukkit.block.data.type.Candle; -import org.bukkit.block.data.type.Comparator; -import org.bukkit.block.data.type.Comparator.Mode; -import org.bukkit.block.data.type.DaylightDetector; -import org.bukkit.block.data.type.Door; -import org.bukkit.block.data.type.NoteBlock; -import org.bukkit.block.data.type.Repeater; -import org.bukkit.block.data.type.Switch; -import org.bukkit.block.data.type.TurtleEgg; -import org.bukkit.block.sign.Side; -import org.bukkit.block.sign.SignSide; -import org.bukkit.entity.Player; -import org.bukkit.event.Event.Result; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.block.Action; -import org.bukkit.event.player.PlayerInteractEvent; -import org.bukkit.event.world.GenericGameEvent; -import org.bukkit.inventory.ItemStack; - -import static de.diddiz.LogBlock.config.Config.getWorldConfig; - -public class InteractLogging extends LoggingListener { - public InteractLogging(LogBlock lb) { - super(lb); - } - - private UUID lastInteractionPlayer; - private BlockData lastInteractionBlockData; - private Location lastInteractionLocation; - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onPlayerInteract(PlayerInteractEvent event) { - final WorldConfig wcfg = getWorldConfig(event.getPlayer().getWorld()); - if (wcfg != null) { - final Block clicked = event.getClickedBlock(); - if (clicked == null) { - return; - } - final BlockData blockData = clicked.getBlockData(); - final Material type = blockData.getMaterial(); - final Player player = event.getPlayer(); - final Location loc = clicked.getLocation(); - lastInteractionPlayer = player.getUniqueId(); - lastInteractionBlockData = blockData; - lastInteractionLocation = loc; - - if (BukkitUtils.isFenceGate(type) || BukkitUtils.isWoodenTrapdoor(type)) { - if (wcfg.isLogging(Logging.DOORINTERACT) && event.getAction() == Action.RIGHT_CLICK_BLOCK) { - Openable newBlockData = (Openable) blockData.clone(); - newBlockData.setOpen(!newBlockData.isOpen()); - consumer.queueBlock(Actor.actorFromEntity(player), loc, blockData, newBlockData); - } - } else if (BukkitUtils.isPressurePlate(type)) { - if (wcfg.isLogging(Logging.PRESUREPLATEINTERACT) && event.getAction() == Action.PHYSICAL) { - consumer.queueBlock(Actor.actorFromEntity(player), loc, blockData, blockData); - } - } else if (BukkitUtils.isWoodenDoor(type)) { - if (wcfg.isLogging(Logging.DOORINTERACT) && event.getAction() == Action.RIGHT_CLICK_BLOCK) { - Door newBlockData = (Door) blockData.clone(); - newBlockData.setOpen(!newBlockData.isOpen()); - consumer.queueBlock(Actor.actorFromEntity(player), loc, blockData, newBlockData); - } - } else if (BukkitUtils.isButton(type) || type == Material.LEVER) { - if (wcfg.isLogging(Logging.SWITCHINTERACT) && event.getAction() == Action.RIGHT_CLICK_BLOCK) { - Switch newBlockData = (Switch) blockData.clone(); - if (!newBlockData.isPowered() || type == Material.LEVER) { - newBlockData.setPowered(!newBlockData.isPowered()); - } - consumer.queueBlock(Actor.actorFromEntity(player), loc, blockData, newBlockData); - } - } else if (BukkitUtils.isSign(type)) { - if (wcfg.isLogging(Logging.SIGNTEXT) && event.getAction() == Action.RIGHT_CLICK_BLOCK && event.getItem() != null) { - Material itemType = event.getItem().getType(); - if (BukkitUtils.isDye(itemType) || itemType == Material.GLOW_INK_SAC || itemType == Material.INK_SAC || itemType == Material.HONEYCOMB) { - final BlockState before = event.getClickedBlock().getState(); - if (before instanceof Sign signBefore) { - boolean waxed = Reflections.isSignWaxed(signBefore); - if (!waxed) { - final Sign signAfter = (Sign) event.getClickedBlock().getState(); - Side side = BukkitUtils.getFacingSignSide(player, clicked); - SignSide signSideBefore = signBefore.getSide(side); - SignSide signSideAfter = signAfter.getSide(side); - if (itemType == Material.GLOW_INK_SAC) { - if (!signSideBefore.isGlowingText() && hasText(signSideBefore)) { - signSideAfter.setGlowingText(true); - consumer.queueBlockReplace(Actor.actorFromEntity(player), signBefore, signAfter); - } - } else if (itemType == Material.INK_SAC) { - if (signSideBefore.isGlowingText() && hasText(signSideBefore)) { - signSideAfter.setGlowingText(false); - consumer.queueBlockReplace(Actor.actorFromEntity(player), signBefore, signAfter); - } - } else if (itemType == Material.HONEYCOMB) { - signAfter.setEditable(false); - consumer.queueBlockReplace(Actor.actorFromEntity(player), signBefore, signAfter); - } else if (BukkitUtils.isDye(itemType) && hasText(signSideBefore)) { - DyeColor newColor = BukkitUtils.dyeToDyeColor(itemType); - if (newColor != null && signSideBefore.getColor() != newColor) { - signSideAfter.setColor(newColor); - consumer.queueBlockReplace(Actor.actorFromEntity(player), signBefore, signAfter); - } - } - } - } - } - } - } else if (type == Material.CAKE) { - if (event.hasItem() && BukkitUtils.isCandle(event.getItem().getType()) && event.useItemInHand() != Result.DENY) { - BlockData newBlockData = Material.valueOf(event.getItem().getType().name() + "_CAKE").createBlockData(); - consumer.queueBlock(Actor.actorFromEntity(player), loc, blockData, newBlockData); - } else if (wcfg.isLogging(Logging.CAKEEAT) && event.getAction() == Action.RIGHT_CLICK_BLOCK && player.getFoodLevel() < 20) { - Cake newBlockData = (Cake) blockData.clone(); - if (newBlockData.getBites() < 6) { - newBlockData.setBites(newBlockData.getBites() + 1); - consumer.queueBlock(Actor.actorFromEntity(player), loc, blockData, newBlockData); - } else { - consumer.queueBlock(Actor.actorFromEntity(player), loc, blockData, Material.AIR.createBlockData()); - } - } - } else if (type == Material.NOTE_BLOCK) { - if (wcfg.isLogging(Logging.NOTEBLOCKINTERACT) && event.getAction() == Action.RIGHT_CLICK_BLOCK) { - NoteBlock newBlockData = (NoteBlock) blockData.clone(); - if (newBlockData.getNote().getOctave() == 2) { - newBlockData.setNote(new Note(0, Tone.F, true)); - } else { - newBlockData.setNote(newBlockData.getNote().sharped()); - } - consumer.queueBlock(Actor.actorFromEntity(player), loc, blockData, newBlockData); - } - } else if (type == Material.REPEATER) { - if (wcfg.isLogging(Logging.DIODEINTERACT) && event.getAction() == Action.RIGHT_CLICK_BLOCK) { - Repeater newBlockData = (Repeater) blockData.clone(); - newBlockData.setDelay((newBlockData.getDelay() % 4) + 1); - consumer.queueBlock(Actor.actorFromEntity(player), loc, blockData, newBlockData); - } - } else if (type == Material.COMPARATOR) { - if (wcfg.isLogging(Logging.COMPARATORINTERACT) && event.getAction() == Action.RIGHT_CLICK_BLOCK) { - Comparator newBlockData = (Comparator) blockData.clone(); - newBlockData.setMode(newBlockData.getMode() == Mode.COMPARE ? Mode.SUBTRACT : Mode.COMPARE); - consumer.queueBlock(Actor.actorFromEntity(player), loc, blockData, newBlockData); - } - } else if (type == Material.DAYLIGHT_DETECTOR) { - if (wcfg.isLogging(Logging.DAYLIGHTDETECTORINTERACT) && event.getAction() == Action.RIGHT_CLICK_BLOCK) { - DaylightDetector newBlockData = (DaylightDetector) blockData.clone(); - newBlockData.setInverted(!newBlockData.isInverted()); - consumer.queueBlock(Actor.actorFromEntity(player), loc, blockData, newBlockData); - } - } else if (type == Material.TRIPWIRE) { - if (wcfg.isLogging(Logging.TRIPWIREINTERACT) && event.getAction() == Action.PHYSICAL) { - consumer.queueBlock(Actor.actorFromEntity(player), loc, blockData, blockData); - } - } else if (type == Material.FARMLAND) { - if (wcfg.isLogging(Logging.CROPTRAMPLE) && event.getAction() == Action.PHYSICAL) { - // 3 = Dirt ID - consumer.queueBlock(Actor.actorFromEntity(player), loc, blockData, Material.DIRT.createBlockData()); - // Log the crop on top as being broken - Block trampledCrop = clicked.getRelative(BlockFace.UP); - if (BukkitUtils.getCropBlocks().contains(trampledCrop.getType())) { - consumer.queueBlockBreak(Actor.actorFromEntity(player), trampledCrop.getState()); - } - } - } else if (type == Material.TURTLE_EGG) { - if (wcfg.isLogging(Logging.BLOCKBREAK) && event.getAction() == Action.PHYSICAL) { - TurtleEgg turtleEggData = (TurtleEgg) blockData; - int eggs = turtleEggData.getEggs(); - if (eggs > 1) { - TurtleEgg turtleEggData2 = (TurtleEgg) turtleEggData.clone(); - turtleEggData2.setEggs(eggs - 1); - consumer.queueBlock(Actor.actorFromEntity(player), loc, turtleEggData, turtleEggData2); - } else { - consumer.queueBlock(Actor.actorFromEntity(player), loc, turtleEggData, Material.AIR.createBlockData()); - } - } - } else if (type == Material.PUMPKIN) { - if ((wcfg.isLogging(Logging.BLOCKBREAK) || wcfg.isLogging(Logging.BLOCKPLACE)) && event.getAction() == Action.RIGHT_CLICK_BLOCK) { - ItemStack inHand = event.getItem(); - if (inHand != null && inHand.getType() == Material.SHEARS) { - BlockFace clickedFace = event.getBlockFace(); - Directional newBlockData = (Directional) Material.CARVED_PUMPKIN.createBlockData(); - if (clickedFace == BlockFace.NORTH || clickedFace == BlockFace.SOUTH || clickedFace == BlockFace.EAST || clickedFace == BlockFace.WEST) { - newBlockData.setFacing(clickedFace); - } else { - // use player distance to calculate the facing - Location playerLoc = player.getLocation(); - playerLoc.subtract(0.5, 0, 0.5); - double dx = playerLoc.getX() - loc.getX(); - double dz = playerLoc.getZ() - loc.getZ(); - if (Math.abs(dx) > Math.abs(dz)) { - newBlockData.setFacing(dx > 0 ? BlockFace.EAST : BlockFace.WEST); - } else { - newBlockData.setFacing(dz > 0 ? BlockFace.SOUTH : BlockFace.NORTH); - } - } - consumer.queueBlock(Actor.actorFromEntity(player), loc, blockData, newBlockData); - } - } - } - } - } - - private boolean hasText(SignSide signSide) { - for (int i = 0; i < 4; i++) { - if (!signSide.getLine(i).isEmpty()) { - return true; - } - } - return false; - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onGenericGameEvent(GenericGameEvent event) { - if (lastInteractionPlayer != null && event.getEntity() != null && event.getEntity().getUniqueId().equals(lastInteractionPlayer) && lastInteractionLocation != null && event.getLocation().equals(lastInteractionLocation)) { - if (lastInteractionBlockData instanceof Candle) { - Candle previousCandle = (Candle) lastInteractionBlockData; - if (previousCandle.isLit()) { - BlockData newData = lastInteractionLocation.getBlock().getBlockData(); - if (newData instanceof Candle) { - Candle newCandle = (Candle) newData; - if (!newCandle.isLit() && !newCandle.isWaterlogged()) { - // log candle extinguish - consumer.queueBlockReplace(Actor.actorFromEntity(event.getEntity()), lastInteractionLocation, lastInteractionBlockData, newData); - } - } - } - } else if (lastInteractionBlockData instanceof Lightable && BukkitUtils.isCandleCake(lastInteractionBlockData.getMaterial())) { - Lightable previousLightable = (Lightable) lastInteractionBlockData; - BlockData newData = lastInteractionLocation.getBlock().getBlockData(); - if (event.getEvent().equals(GameEvent.EAT)) { - final WorldConfig wcfg = getWorldConfig(event.getLocation().getWorld()); - if (wcfg.isLogging(Logging.CAKEEAT)) { - // nom nom (don't know why newData is incorrect here) - newData = Material.CAKE.createBlockData(); - ((Cake) newData).setBites(1); - consumer.queueBlockReplace(Actor.actorFromEntity(event.getEntity()), lastInteractionLocation, lastInteractionBlockData, newData); - } - } else if (previousLightable.isLit()) { - if (newData instanceof Lightable) { - Lightable newLightable = (Lightable) newData; - if (!newLightable.isLit()) { - // log cake extinguish - consumer.queueBlockReplace(Actor.actorFromEntity(event.getEntity()), lastInteractionLocation, lastInteractionBlockData, newData); - } - } - } - } - } - lastInteractionPlayer = null; - lastInteractionBlockData = null; - lastInteractionLocation = null; - } -} +package de.diddiz.LogBlock.listeners; + +import de.diddiz.LogBlock.Actor; +import de.diddiz.LogBlock.LogBlock; +import de.diddiz.LogBlock.Logging; +import de.diddiz.LogBlock.config.WorldConfig; +import de.diddiz.LogBlock.util.BukkitUtils; +import de.diddiz.LogBlock.util.Reflections; +import java.util.UUID; +import org.bukkit.DyeColor; +import org.bukkit.GameEvent; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Note; +import org.bukkit.Note.Tone; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.BlockState; +import org.bukkit.block.Sign; +import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.Directional; +import org.bukkit.block.data.Lightable; +import org.bukkit.block.data.Openable; +import org.bukkit.block.data.type.Cake; +import org.bukkit.block.data.type.Candle; +import org.bukkit.block.data.type.Comparator; +import org.bukkit.block.data.type.Comparator.Mode; +import org.bukkit.block.data.type.DaylightDetector; +import org.bukkit.block.data.type.Door; +import org.bukkit.block.data.type.NoteBlock; +import org.bukkit.block.data.type.Repeater; +import org.bukkit.block.data.type.Switch; +import org.bukkit.block.data.type.TurtleEgg; +import org.bukkit.block.sign.Side; +import org.bukkit.block.sign.SignSide; +import org.bukkit.entity.Player; +import org.bukkit.event.Event.Result; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.Action; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.world.GenericGameEvent; +import org.bukkit.inventory.ItemStack; + +import static de.diddiz.LogBlock.config.Config.getWorldConfig; + +public class InteractLogging extends LoggingListener { + public InteractLogging(LogBlock lb) { + super(lb); + } + + private UUID lastInteractionPlayer; + private BlockData lastInteractionBlockData; + private Location lastInteractionLocation; + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onPlayerInteract(PlayerInteractEvent event) { + final WorldConfig wcfg = getWorldConfig(event.getPlayer().getWorld()); + if (wcfg != null) { + final Block clicked = event.getClickedBlock(); + if (clicked == null) { + return; + } + final BlockData blockData = clicked.getBlockData(); + final Material type = blockData.getMaterial(); + final Player player = event.getPlayer(); + final Location loc = clicked.getLocation(); + lastInteractionPlayer = player.getUniqueId(); + lastInteractionBlockData = blockData; + lastInteractionLocation = loc; + + if (BukkitUtils.isFenceGate(type) || BukkitUtils.isWoodenTrapdoor(type)) { + if (wcfg.isLogging(Logging.DOORINTERACT) && event.getAction() == Action.RIGHT_CLICK_BLOCK) { + Openable newBlockData = (Openable) blockData.clone(); + newBlockData.setOpen(!newBlockData.isOpen()); + consumer.queueBlock(Actor.actorFromEntity(player), loc, blockData, newBlockData); + } + } else if (BukkitUtils.isPressurePlate(type)) { + if (wcfg.isLogging(Logging.PRESUREPLATEINTERACT) && event.getAction() == Action.PHYSICAL) { + consumer.queueBlock(Actor.actorFromEntity(player), loc, blockData, blockData); + } + } else if (BukkitUtils.isWoodenDoor(type)) { + if (wcfg.isLogging(Logging.DOORINTERACT) && event.getAction() == Action.RIGHT_CLICK_BLOCK) { + Door newBlockData = (Door) blockData.clone(); + newBlockData.setOpen(!newBlockData.isOpen()); + consumer.queueBlock(Actor.actorFromEntity(player), loc, blockData, newBlockData); + } + } else if (BukkitUtils.isButton(type) || type == Material.LEVER) { + if (wcfg.isLogging(Logging.SWITCHINTERACT) && event.getAction() == Action.RIGHT_CLICK_BLOCK) { + Switch newBlockData = (Switch) blockData.clone(); + if (!newBlockData.isPowered() || type == Material.LEVER) { + newBlockData.setPowered(!newBlockData.isPowered()); + } + consumer.queueBlock(Actor.actorFromEntity(player), loc, blockData, newBlockData); + } + } else if (BukkitUtils.isSign(type)) { + if (wcfg.isLogging(Logging.SIGNTEXT) && event.getAction() == Action.RIGHT_CLICK_BLOCK && event.getItem() != null) { + Material itemType = event.getItem().getType(); + if (BukkitUtils.isDye(itemType) || itemType == Material.GLOW_INK_SAC || itemType == Material.INK_SAC || itemType == Material.HONEYCOMB) { + final BlockState before = event.getClickedBlock().getState(); + if (before instanceof Sign signBefore) { + boolean waxed = Reflections.isSignWaxed(signBefore); + if (!waxed) { + final Sign signAfter = (Sign) event.getClickedBlock().getState(); + Side side = BukkitUtils.getFacingSignSide(player, clicked); + SignSide signSideBefore = signBefore.getSide(side); + SignSide signSideAfter = signAfter.getSide(side); + if (itemType == Material.GLOW_INK_SAC) { + if (!signSideBefore.isGlowingText() && hasText(signSideBefore)) { + signSideAfter.setGlowingText(true); + consumer.queueBlockReplace(Actor.actorFromEntity(player), signBefore, signAfter); + } + } else if (itemType == Material.INK_SAC) { + if (signSideBefore.isGlowingText() && hasText(signSideBefore)) { + signSideAfter.setGlowingText(false); + consumer.queueBlockReplace(Actor.actorFromEntity(player), signBefore, signAfter); + } + } else if (itemType == Material.HONEYCOMB) { + signAfter.setEditable(false); + consumer.queueBlockReplace(Actor.actorFromEntity(player), signBefore, signAfter); + } else if (BukkitUtils.isDye(itemType) && hasText(signSideBefore)) { + DyeColor newColor = BukkitUtils.dyeToDyeColor(itemType); + if (newColor != null && signSideBefore.getColor() != newColor) { + signSideAfter.setColor(newColor); + consumer.queueBlockReplace(Actor.actorFromEntity(player), signBefore, signAfter); + } + } + } + } + } + } + } else if (type == Material.CAKE) { + if (event.hasItem() && BukkitUtils.isCandle(event.getItem().getType()) && event.useItemInHand() != Result.DENY) { + BlockData newBlockData = Material.valueOf(event.getItem().getType().name() + "_CAKE").createBlockData(); + consumer.queueBlock(Actor.actorFromEntity(player), loc, blockData, newBlockData); + } else if (wcfg.isLogging(Logging.CAKEEAT) && event.getAction() == Action.RIGHT_CLICK_BLOCK && player.getFoodLevel() < 20) { + Cake newBlockData = (Cake) blockData.clone(); + if (newBlockData.getBites() < 6) { + newBlockData.setBites(newBlockData.getBites() + 1); + consumer.queueBlock(Actor.actorFromEntity(player), loc, blockData, newBlockData); + } else { + consumer.queueBlock(Actor.actorFromEntity(player), loc, blockData, Material.AIR.createBlockData()); + } + } + } else if (type == Material.NOTE_BLOCK) { + if (wcfg.isLogging(Logging.NOTEBLOCKINTERACT) && event.getAction() == Action.RIGHT_CLICK_BLOCK) { + NoteBlock newBlockData = (NoteBlock) blockData.clone(); + if (newBlockData.getNote().getOctave() == 2) { + newBlockData.setNote(new Note(0, Tone.F, true)); + } else { + newBlockData.setNote(newBlockData.getNote().sharped()); + } + consumer.queueBlock(Actor.actorFromEntity(player), loc, blockData, newBlockData); + } + } else if (type == Material.REPEATER) { + if (wcfg.isLogging(Logging.DIODEINTERACT) && event.getAction() == Action.RIGHT_CLICK_BLOCK) { + Repeater newBlockData = (Repeater) blockData.clone(); + newBlockData.setDelay((newBlockData.getDelay() % 4) + 1); + consumer.queueBlock(Actor.actorFromEntity(player), loc, blockData, newBlockData); + } + } else if (type == Material.COMPARATOR) { + if (wcfg.isLogging(Logging.COMPARATORINTERACT) && event.getAction() == Action.RIGHT_CLICK_BLOCK) { + Comparator newBlockData = (Comparator) blockData.clone(); + newBlockData.setMode(newBlockData.getMode() == Mode.COMPARE ? Mode.SUBTRACT : Mode.COMPARE); + consumer.queueBlock(Actor.actorFromEntity(player), loc, blockData, newBlockData); + } + } else if (type == Material.DAYLIGHT_DETECTOR) { + if (wcfg.isLogging(Logging.DAYLIGHTDETECTORINTERACT) && event.getAction() == Action.RIGHT_CLICK_BLOCK) { + DaylightDetector newBlockData = (DaylightDetector) blockData.clone(); + newBlockData.setInverted(!newBlockData.isInverted()); + consumer.queueBlock(Actor.actorFromEntity(player), loc, blockData, newBlockData); + } + } else if (type == Material.TRIPWIRE) { + if (wcfg.isLogging(Logging.TRIPWIREINTERACT) && event.getAction() == Action.PHYSICAL) { + consumer.queueBlock(Actor.actorFromEntity(player), loc, blockData, blockData); + } + } else if (type == Material.FARMLAND) { + if (wcfg.isLogging(Logging.CROPTRAMPLE) && event.getAction() == Action.PHYSICAL) { + // 3 = Dirt ID + consumer.queueBlock(Actor.actorFromEntity(player), loc, blockData, Material.DIRT.createBlockData()); + // Log the crop on top as being broken + Block trampledCrop = clicked.getRelative(BlockFace.UP); + if (BukkitUtils.getCropBlocks().contains(trampledCrop.getType())) { + consumer.queueBlockBreak(Actor.actorFromEntity(player), trampledCrop.getState()); + } + } + } else if (type == Material.TURTLE_EGG) { + if (wcfg.isLogging(Logging.BLOCKBREAK) && event.getAction() == Action.PHYSICAL) { + TurtleEgg turtleEggData = (TurtleEgg) blockData; + int eggs = turtleEggData.getEggs(); + if (eggs > 1) { + TurtleEgg turtleEggData2 = (TurtleEgg) turtleEggData.clone(); + turtleEggData2.setEggs(eggs - 1); + consumer.queueBlock(Actor.actorFromEntity(player), loc, turtleEggData, turtleEggData2); + } else { + consumer.queueBlock(Actor.actorFromEntity(player), loc, turtleEggData, Material.AIR.createBlockData()); + } + } + } else if (type == Material.PUMPKIN) { + if ((wcfg.isLogging(Logging.BLOCKBREAK) || wcfg.isLogging(Logging.BLOCKPLACE)) && event.getAction() == Action.RIGHT_CLICK_BLOCK) { + ItemStack inHand = event.getItem(); + if (inHand != null && inHand.getType() == Material.SHEARS) { + BlockFace clickedFace = event.getBlockFace(); + Directional newBlockData = (Directional) Material.CARVED_PUMPKIN.createBlockData(); + if (clickedFace == BlockFace.NORTH || clickedFace == BlockFace.SOUTH || clickedFace == BlockFace.EAST || clickedFace == BlockFace.WEST) { + newBlockData.setFacing(clickedFace); + } else { + // use player distance to calculate the facing + Location playerLoc = player.getLocation(); + playerLoc.subtract(0.5, 0, 0.5); + double dx = playerLoc.getX() - loc.getX(); + double dz = playerLoc.getZ() - loc.getZ(); + if (Math.abs(dx) > Math.abs(dz)) { + newBlockData.setFacing(dx > 0 ? BlockFace.EAST : BlockFace.WEST); + } else { + newBlockData.setFacing(dz > 0 ? BlockFace.SOUTH : BlockFace.NORTH); + } + } + consumer.queueBlock(Actor.actorFromEntity(player), loc, blockData, newBlockData); + } + } + } + } + } + + private boolean hasText(SignSide signSide) { + for (int i = 0; i < 4; i++) { + if (!signSide.getLine(i).isEmpty()) { + return true; + } + } + return false; + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onGenericGameEvent(GenericGameEvent event) { + if (lastInteractionPlayer != null && event.getEntity() != null && event.getEntity().getUniqueId().equals(lastInteractionPlayer) && lastInteractionLocation != null && event.getLocation().equals(lastInteractionLocation)) { + if (lastInteractionBlockData instanceof Candle) { + Candle previousCandle = (Candle) lastInteractionBlockData; + if (previousCandle.isLit()) { + BlockData newData = lastInteractionLocation.getBlock().getBlockData(); + if (newData instanceof Candle) { + Candle newCandle = (Candle) newData; + if (!newCandle.isLit() && !newCandle.isWaterlogged()) { + // log candle extinguish + consumer.queueBlockReplace(Actor.actorFromEntity(event.getEntity()), lastInteractionLocation, lastInteractionBlockData, newData); + } + } + } + } else if (lastInteractionBlockData instanceof Lightable && BukkitUtils.isCandleCake(lastInteractionBlockData.getMaterial())) { + Lightable previousLightable = (Lightable) lastInteractionBlockData; + BlockData newData = lastInteractionLocation.getBlock().getBlockData(); + if (event.getEvent().equals(GameEvent.EAT)) { + final WorldConfig wcfg = getWorldConfig(event.getLocation().getWorld()); + if (wcfg.isLogging(Logging.CAKEEAT)) { + // nom nom (don't know why newData is incorrect here) + newData = Material.CAKE.createBlockData(); + ((Cake) newData).setBites(1); + consumer.queueBlockReplace(Actor.actorFromEntity(event.getEntity()), lastInteractionLocation, lastInteractionBlockData, newData); + } + } else if (previousLightable.isLit()) { + if (newData instanceof Lightable) { + Lightable newLightable = (Lightable) newData; + if (!newLightable.isLit()) { + // log cake extinguish + consumer.queueBlockReplace(Actor.actorFromEntity(event.getEntity()), lastInteractionLocation, lastInteractionBlockData, newData); + } + } + } + } + } + lastInteractionPlayer = null; + lastInteractionBlockData = null; + lastInteractionLocation = null; + } +} diff --git a/src/main/java/de/diddiz/LogBlock/listeners/KillLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/KillLogging.java index 3011fc2..ac6d3dd 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/KillLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/KillLogging.java @@ -1,51 +1,51 @@ -package de.diddiz.LogBlock.listeners; - -import de.diddiz.LogBlock.Actor; -import de.diddiz.LogBlock.LogBlock; -import de.diddiz.LogBlock.Logging; -import de.diddiz.LogBlock.config.Config.*; -import org.bukkit.entity.Entity; -import org.bukkit.entity.LivingEntity; -import org.bukkit.entity.Monster; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.entity.EntityDamageByEntityEvent; -import org.bukkit.event.entity.EntityDamageEvent; -import org.bukkit.event.entity.EntityDeathEvent; - -import static de.diddiz.LogBlock.config.Config.*; - -public class KillLogging extends LoggingListener { - - public KillLogging(LogBlock lb) { - super(lb); - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onEntityDeath(EntityDeathEvent deathEvent) { - EntityDamageEvent event = deathEvent.getEntity().getLastDamageCause(); - // For a death event, there should always be a damage event and it should not be cancelled. Check anyway. - if (event != null && event.isCancelled() == false && isLogging(event.getEntity().getWorld(), Logging.KILL) && event.getEntity() instanceof LivingEntity) { - final LivingEntity victim = (LivingEntity) event.getEntity(); - if (event instanceof EntityDamageByEntityEvent) { - final Entity killer = ((EntityDamageByEntityEvent) event).getDamager(); - if (logKillsLevel == LogKillsLevel.PLAYERS && !(victim instanceof Player && killer instanceof Player)) { - return; - } else if (logKillsLevel == LogKillsLevel.MONSTERS && !((victim instanceof Player || victim instanceof Monster) && killer instanceof Player || killer instanceof Monster)) { - return; - } - consumer.queueKill(killer, victim); - } else if (deathEvent.getEntity().getKiller() != null) { - consumer.queueKill(deathEvent.getEntity().getKiller(), victim); - } else if (logEnvironmentalKills) { - if (logKillsLevel == LogKillsLevel.PLAYERS && !(victim instanceof Player)) { - return; - } else if (logKillsLevel == LogKillsLevel.MONSTERS && !((victim instanceof Player || victim instanceof Monster))) { - return; - } - consumer.queueKill(new Actor(event.getCause().toString()), victim); - } - } - } -} +package de.diddiz.LogBlock.listeners; + +import de.diddiz.LogBlock.Actor; +import de.diddiz.LogBlock.LogBlock; +import de.diddiz.LogBlock.Logging; +import de.diddiz.LogBlock.config.Config.*; +import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Monster; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.entity.EntityDeathEvent; + +import static de.diddiz.LogBlock.config.Config.*; + +public class KillLogging extends LoggingListener { + + public KillLogging(LogBlock lb) { + super(lb); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onEntityDeath(EntityDeathEvent deathEvent) { + EntityDamageEvent event = deathEvent.getEntity().getLastDamageCause(); + // For a death event, there should always be a damage event and it should not be cancelled. Check anyway. + if (event != null && event.isCancelled() == false && isLogging(event.getEntity().getWorld(), Logging.KILL) && event.getEntity() instanceof LivingEntity) { + final LivingEntity victim = (LivingEntity) event.getEntity(); + if (event instanceof EntityDamageByEntityEvent) { + final Entity killer = ((EntityDamageByEntityEvent) event).getDamager(); + if (logKillsLevel == LogKillsLevel.PLAYERS && !(victim instanceof Player && killer instanceof Player)) { + return; + } else if (logKillsLevel == LogKillsLevel.MONSTERS && !((victim instanceof Player || victim instanceof Monster) && killer instanceof Player || killer instanceof Monster)) { + return; + } + consumer.queueKill(killer, victim); + } else if (deathEvent.getEntity().getKiller() != null) { + consumer.queueKill(deathEvent.getEntity().getKiller(), victim); + } else if (logEnvironmentalKills) { + if (logKillsLevel == LogKillsLevel.PLAYERS && !(victim instanceof Player)) { + return; + } else if (logKillsLevel == LogKillsLevel.MONSTERS && !((victim instanceof Player || victim instanceof Monster))) { + return; + } + consumer.queueKill(new Actor(event.getCause().toString()), victim); + } + } + } +} diff --git a/src/main/java/de/diddiz/LogBlock/listeners/LeavesDecayLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/LeavesDecayLogging.java index 5b1a9d8..c75ea06 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/LeavesDecayLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/LeavesDecayLogging.java @@ -1,26 +1,26 @@ -package de.diddiz.LogBlock.listeners; - -import de.diddiz.LogBlock.Actor; -import de.diddiz.LogBlock.LogBlock; -import de.diddiz.LogBlock.Logging; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.block.LeavesDecayEvent; - -import static de.diddiz.LogBlock.config.Config.isLogging; -import static de.diddiz.LogBlock.util.LoggingUtil.smartLogBlockBreak; -import static de.diddiz.LogBlock.util.LoggingUtil.smartLogFallables; - -public class LeavesDecayLogging extends LoggingListener { - public LeavesDecayLogging(LogBlock lb) { - super(lb); - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onLeavesDecay(LeavesDecayEvent event) { - if (isLogging(event.getBlock().getWorld(), Logging.LEAVESDECAY)) { - smartLogBlockBreak(consumer, new Actor("LeavesDecay"), event.getBlock()); - smartLogFallables(consumer, new Actor("LeavesDecay"), event.getBlock()); - } - } -} +package de.diddiz.LogBlock.listeners; + +import de.diddiz.LogBlock.Actor; +import de.diddiz.LogBlock.LogBlock; +import de.diddiz.LogBlock.Logging; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.LeavesDecayEvent; + +import static de.diddiz.LogBlock.config.Config.isLogging; +import static de.diddiz.LogBlock.util.LoggingUtil.smartLogBlockBreak; +import static de.diddiz.LogBlock.util.LoggingUtil.smartLogFallables; + +public class LeavesDecayLogging extends LoggingListener { + public LeavesDecayLogging(LogBlock lb) { + super(lb); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onLeavesDecay(LeavesDecayEvent event) { + if (isLogging(event.getBlock().getWorld(), Logging.LEAVESDECAY)) { + smartLogBlockBreak(consumer, new Actor("LeavesDecay"), event.getBlock()); + smartLogFallables(consumer, new Actor("LeavesDecay"), event.getBlock()); + } + } +} diff --git a/src/main/java/de/diddiz/LogBlock/listeners/LoggingListener.java b/src/main/java/de/diddiz/LogBlock/listeners/LoggingListener.java index 4f91089..ca5db00 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/LoggingListener.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/LoggingListener.java @@ -1,13 +1,13 @@ -package de.diddiz.LogBlock.listeners; - -import de.diddiz.LogBlock.Consumer; -import de.diddiz.LogBlock.LogBlock; -import org.bukkit.event.Listener; - -public class LoggingListener implements Listener { - protected final Consumer consumer; - - public LoggingListener(LogBlock lb) { - consumer = lb.getConsumer(); - } -} +package de.diddiz.LogBlock.listeners; + +import de.diddiz.LogBlock.Consumer; +import de.diddiz.LogBlock.LogBlock; +import org.bukkit.event.Listener; + +public class LoggingListener implements Listener { + protected final Consumer consumer; + + public LoggingListener(LogBlock lb) { + consumer = lb.getConsumer(); + } +} diff --git a/src/main/java/de/diddiz/LogBlock/listeners/PlayerInfoLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/PlayerInfoLogging.java index fbdf9c4..f10d65d 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/PlayerInfoLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/PlayerInfoLogging.java @@ -1,43 +1,43 @@ -package de.diddiz.LogBlock.listeners; - -import de.diddiz.LogBlock.LogBlock; -import de.diddiz.LogBlock.config.Config; - -import java.util.HashMap; -import java.util.UUID; - -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.player.PlayerJoinEvent; -import org.bukkit.event.player.PlayerQuitEvent; - -public class PlayerInfoLogging extends LoggingListener { - - private final HashMap playerLogins = new HashMap<>(); - - public PlayerInfoLogging(LogBlock lb) { - super(lb); - } - - @EventHandler(priority = EventPriority.MONITOR) - public void onPlayerJoin(PlayerJoinEvent event) { - playerLogins.put(event.getPlayer().getUniqueId(), System.currentTimeMillis()); - consumer.queueJoin(event.getPlayer()); - } - - @EventHandler(priority = EventPriority.MONITOR) - public void onPlayerQuit(PlayerQuitEvent event) { - onPlayerQuit(event.getPlayer()); - } - - public void onPlayerQuit(Player player) { - Long joinTime = playerLogins.remove(player.getUniqueId()); - if (Config.logPlayerInfo && joinTime != null) { - long onlineTime = (System.currentTimeMillis() - joinTime) / 1000; - if (onlineTime > 0) { - consumer.queueLeave(player, onlineTime); - } - } - } -} +package de.diddiz.LogBlock.listeners; + +import de.diddiz.LogBlock.LogBlock; +import de.diddiz.LogBlock.config.Config; + +import java.util.HashMap; +import java.util.UUID; + +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +public class PlayerInfoLogging extends LoggingListener { + + private final HashMap playerLogins = new HashMap<>(); + + public PlayerInfoLogging(LogBlock lb) { + super(lb); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onPlayerJoin(PlayerJoinEvent event) { + playerLogins.put(event.getPlayer().getUniqueId(), System.currentTimeMillis()); + consumer.queueJoin(event.getPlayer()); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onPlayerQuit(PlayerQuitEvent event) { + onPlayerQuit(event.getPlayer()); + } + + public void onPlayerQuit(Player player) { + Long joinTime = playerLogins.remove(player.getUniqueId()); + if (Config.logPlayerInfo && joinTime != null) { + long onlineTime = (System.currentTimeMillis() - joinTime) / 1000; + if (onlineTime > 0) { + consumer.queueLeave(player, onlineTime); + } + } + } +} diff --git a/src/main/java/de/diddiz/LogBlock/listeners/SignChangeLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/SignChangeLogging.java index ce84676..d74e753 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/SignChangeLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/SignChangeLogging.java @@ -1,33 +1,33 @@ -package de.diddiz.LogBlock.listeners; - -import de.diddiz.LogBlock.Actor; -import de.diddiz.LogBlock.LogBlock; -import de.diddiz.LogBlock.Logging; -import org.bukkit.block.BlockState; -import org.bukkit.block.Sign; -import org.bukkit.block.sign.SignSide; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.block.SignChangeEvent; - -import static de.diddiz.LogBlock.config.Config.isLogging; - -public class SignChangeLogging extends LoggingListener { - public SignChangeLogging(LogBlock lb) { - super(lb); - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onSignChange(SignChangeEvent event) { - if (isLogging(event.getBlock().getWorld(), Logging.SIGNTEXT)) { - BlockState newState = event.getBlock().getState(); - if (newState instanceof Sign sign) { - SignSide signSide = sign.getSide(event.getSide()); - for (int i = 0; i < 4; i++) { - signSide.setLine(i, event.getLine(i)); - } - consumer.queueBlockReplace(Actor.actorFromEntity(event.getPlayer()), event.getBlock().getState(), newState); - } - } - } -} +package de.diddiz.LogBlock.listeners; + +import de.diddiz.LogBlock.Actor; +import de.diddiz.LogBlock.LogBlock; +import de.diddiz.LogBlock.Logging; +import org.bukkit.block.BlockState; +import org.bukkit.block.Sign; +import org.bukkit.block.sign.SignSide; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.SignChangeEvent; + +import static de.diddiz.LogBlock.config.Config.isLogging; + +public class SignChangeLogging extends LoggingListener { + public SignChangeLogging(LogBlock lb) { + super(lb); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onSignChange(SignChangeEvent event) { + if (isLogging(event.getBlock().getWorld(), Logging.SIGNTEXT)) { + BlockState newState = event.getBlock().getState(); + if (newState instanceof Sign sign) { + SignSide signSide = sign.getSide(event.getSide()); + for (int i = 0; i < 4; i++) { + signSide.setLine(i, event.getLine(i)); + } + consumer.queueBlockReplace(Actor.actorFromEntity(event.getPlayer()), event.getBlock().getState(), newState); + } + } + } +} diff --git a/src/main/java/de/diddiz/LogBlock/listeners/SnowFadeLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/SnowFadeLogging.java index 1223893..9c411ba 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/SnowFadeLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/SnowFadeLogging.java @@ -1,28 +1,28 @@ -package de.diddiz.LogBlock.listeners; - -import de.diddiz.LogBlock.Actor; -import de.diddiz.LogBlock.LogBlock; -import de.diddiz.LogBlock.Logging; - -import org.bukkit.Material; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.block.BlockFadeEvent; - -import static de.diddiz.LogBlock.config.Config.isLogging; - -public class SnowFadeLogging extends LoggingListener { - public SnowFadeLogging(LogBlock lb) { - super(lb); - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onBlockFade(BlockFadeEvent event) { - if (isLogging(event.getBlock().getWorld(), Logging.SNOWFADE)) { - final Material type = event.getBlock().getType(); - if (type == Material.SNOW || type == Material.ICE) { - consumer.queueBlockReplace(new Actor("SnowFade"), event.getBlock().getState(), event.getNewState()); - } - } - } -} +package de.diddiz.LogBlock.listeners; + +import de.diddiz.LogBlock.Actor; +import de.diddiz.LogBlock.LogBlock; +import de.diddiz.LogBlock.Logging; + +import org.bukkit.Material; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockFadeEvent; + +import static de.diddiz.LogBlock.config.Config.isLogging; + +public class SnowFadeLogging extends LoggingListener { + public SnowFadeLogging(LogBlock lb) { + super(lb); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onBlockFade(BlockFadeEvent event) { + if (isLogging(event.getBlock().getWorld(), Logging.SNOWFADE)) { + final Material type = event.getBlock().getType(); + if (type == Material.SNOW || type == Material.ICE) { + consumer.queueBlockReplace(new Actor("SnowFade"), event.getBlock().getState(), event.getNewState()); + } + } + } +} diff --git a/src/main/java/de/diddiz/LogBlock/listeners/SnowFormLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/SnowFormLogging.java index b269f02..6d4a247 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/SnowFormLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/SnowFormLogging.java @@ -1,28 +1,28 @@ -package de.diddiz.LogBlock.listeners; - -import de.diddiz.LogBlock.Actor; -import de.diddiz.LogBlock.LogBlock; -import de.diddiz.LogBlock.Logging; - -import org.bukkit.Material; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.block.BlockFormEvent; - -import static de.diddiz.LogBlock.config.Config.isLogging; - -public class SnowFormLogging extends LoggingListener { - public SnowFormLogging(LogBlock lb) { - super(lb); - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onBlockForm(BlockFormEvent event) { - if (isLogging(event.getBlock().getWorld(), Logging.SNOWFORM)) { - final Material type = event.getNewState().getType(); - if (type == Material.SNOW || type == Material.ICE) { - consumer.queueBlockReplace(new Actor("SnowForm"), event.getBlock().getState(), event.getNewState()); - } - } - } -} +package de.diddiz.LogBlock.listeners; + +import de.diddiz.LogBlock.Actor; +import de.diddiz.LogBlock.LogBlock; +import de.diddiz.LogBlock.Logging; + +import org.bukkit.Material; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockFormEvent; + +import static de.diddiz.LogBlock.config.Config.isLogging; + +public class SnowFormLogging extends LoggingListener { + public SnowFormLogging(LogBlock lb) { + super(lb); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onBlockForm(BlockFormEvent event) { + if (isLogging(event.getBlock().getWorld(), Logging.SNOWFORM)) { + final Material type = event.getNewState().getType(); + if (type == Material.SNOW || type == Material.ICE) { + consumer.queueBlockReplace(new Actor("SnowForm"), event.getBlock().getState(), event.getNewState()); + } + } + } +} diff --git a/src/main/java/de/diddiz/LogBlock/listeners/StructureGrowLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/StructureGrowLogging.java index 62f4357..93b395f 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/StructureGrowLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/StructureGrowLogging.java @@ -1,34 +1,34 @@ -package de.diddiz.LogBlock.listeners; - -import de.diddiz.LogBlock.Actor; -import de.diddiz.LogBlock.LogBlock; -import de.diddiz.LogBlock.Logging; -import de.diddiz.LogBlock.config.WorldConfig; -import org.bukkit.block.BlockState; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.world.StructureGrowEvent; - -import static de.diddiz.LogBlock.config.Config.getWorldConfig; - -public class StructureGrowLogging extends LoggingListener { - public StructureGrowLogging(LogBlock lb) { - super(lb); - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onStructureGrow(StructureGrowEvent event) { - final WorldConfig wcfg = getWorldConfig(event.getWorld()); - if (wcfg != null) { - if (!wcfg.isLogging(Logging.NATURALSTRUCTUREGROW)) { - return; - } - if (!event.isFromBonemeal()) { - final Actor actor = new Actor("NaturalGrow"); - for (final BlockState state : event.getBlocks()) { - consumer.queueBlockReplace(actor, state.getBlock().getState(), state); - } - } - } - } -} +package de.diddiz.LogBlock.listeners; + +import de.diddiz.LogBlock.Actor; +import de.diddiz.LogBlock.LogBlock; +import de.diddiz.LogBlock.Logging; +import de.diddiz.LogBlock.config.WorldConfig; +import org.bukkit.block.BlockState; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.world.StructureGrowEvent; + +import static de.diddiz.LogBlock.config.Config.getWorldConfig; + +public class StructureGrowLogging extends LoggingListener { + public StructureGrowLogging(LogBlock lb) { + super(lb); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onStructureGrow(StructureGrowEvent event) { + final WorldConfig wcfg = getWorldConfig(event.getWorld()); + if (wcfg != null) { + if (!wcfg.isLogging(Logging.NATURALSTRUCTUREGROW)) { + return; + } + if (!event.isFromBonemeal()) { + final Actor actor = new Actor("NaturalGrow"); + for (final BlockState state : event.getBlocks()) { + consumer.queueBlockReplace(actor, state.getBlock().getState(), state); + } + } + } + } +} diff --git a/src/main/java/de/diddiz/LogBlock/listeners/ToolListener.java b/src/main/java/de/diddiz/LogBlock/listeners/ToolListener.java index 7a192c5..9e46867 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/ToolListener.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/ToolListener.java @@ -1,151 +1,151 @@ -package de.diddiz.LogBlock.listeners; - -import de.diddiz.LogBlock.*; -import de.diddiz.LogBlock.events.ToolUseEvent; -import de.diddiz.LogBlock.util.BukkitUtils; -import de.diddiz.LogBlock.util.CuboidRegion; -import org.bukkit.ChatColor; -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.block.Action; -import org.bukkit.event.player.PlayerChangedWorldEvent; -import org.bukkit.event.player.PlayerDropItemEvent; -import org.bukkit.event.player.PlayerInteractEvent; -import org.bukkit.inventory.ItemStack; - -import java.util.Map.Entry; - -import static de.diddiz.LogBlock.Session.getSession; -import static de.diddiz.LogBlock.Session.hasSession; -import static de.diddiz.LogBlock.config.Config.isLogged; -import static de.diddiz.LogBlock.config.Config.toolsByType; - -public class ToolListener implements Listener { - private final CommandsHandler handler; - private final LogBlock logblock; - - public ToolListener(LogBlock logblock) { - this.logblock = logblock; - handler = logblock.getCommandsHandler(); - } - - @EventHandler - public void onPlayerInteract(PlayerInteractEvent event) { - if (event.getMaterial() != null) { - final Action action = event.getAction(); - final Material type = event.getMaterial(); - final Tool tool = toolsByType.get(type); - final Player player = event.getPlayer(); - if (tool != null && (action == Action.RIGHT_CLICK_BLOCK || action == Action.LEFT_CLICK_BLOCK) && logblock.hasPermission(player, "logblock.tools." + tool.name)) { - final ToolBehavior behavior = action == Action.RIGHT_CLICK_BLOCK ? tool.rightClickBehavior : tool.leftClickBehavior; - final ToolData toolData = getSession(player).toolData.get(tool); - if (behavior != ToolBehavior.NONE && toolData.enabled) { - if (!isLogged(player.getWorld())) { - player.sendMessage(ChatColor.RED + "This world is not currently logged."); - event.setCancelled(true); - return; - } - final Block block = event.getClickedBlock(); - final QueryParams params = toolData.params.clone(); - params.loc = null; - params.sel = null; - if (behavior == ToolBehavior.BLOCK) { - params.setLocation(block.getRelative(event.getBlockFace()).getLocation()); - } else if (tool.params.radius != 0) { - params.setLocation(block.getLocation()); - } else { - Block otherHalfChest = BukkitUtils.getConnectedChest(block); - if (otherHalfChest == null) { - params.setLocation(block.getLocation()); - } else { - params.setSelection(CuboidRegion.fromCorners(block.getLocation().getWorld(), block.getLocation(), otherHalfChest.getLocation())); - } - } - try { - params.validate(); - if (this.callToolUseEvent(new ToolUseEvent(player, tool, behavior, params))) { - return; - } - if (toolData.mode == ToolMode.ROLLBACK) { - handler.new CommandRollback(player, params, true); - } else if (toolData.mode == ToolMode.REDO) { - handler.new CommandRedo(player, params, true); - } else if (toolData.mode == ToolMode.CLEARLOG) { - handler.new CommandClearLog(player, params, true); - } else if (toolData.mode == ToolMode.WRITELOGFILE) { - handler.new CommandWriteLogFile(player, params, true); - } else { - handler.new CommandLookup(player, params, true); - } - } catch (final Exception ex) { - player.sendMessage(ChatColor.RED + ex.getMessage()); - } - event.setCancelled(true); - } - } - } - } - - private boolean callToolUseEvent(ToolUseEvent event) { - this.logblock.getServer().getPluginManager().callEvent(event); - return event.isCancelled(); - } - - @EventHandler - public void onPlayerChangedWorld(PlayerChangedWorldEvent event) { - final Player player = event.getPlayer(); - if (hasSession(player)) { - final Session session = getSession(player); - for (final Entry entry : session.toolData.entrySet()) { - final Tool tool = entry.getKey(); - final ToolData toolData = entry.getValue(); - if (toolData.enabled && !logblock.hasPermission(player, "logblock.tools." + tool.name)) { - toolData.enabled = false; - if (tool.removeOnDisable && logblock.hasPermission(player, "logblock.spawnTools")) { - player.getInventory().removeItem(new ItemStack(tool.item, 1)); - } - player.sendMessage(ChatColor.GREEN + "Tool disabled."); - } - } - } - } - - @EventHandler - public void onPlayerDropItem(PlayerDropItemEvent event) { - final Player player = event.getPlayer(); - if (hasSession(player)) { - final Session session = getSession(player); - for (final Entry entry : session.toolData.entrySet()) { - final Tool tool = entry.getKey(); - final ToolData toolData = entry.getValue(); - final Material item = event.getItemDrop().getItemStack().getType(); - if (item == tool.item && toolData.enabled) { - if (tool.dropToDisable) { - toolData.enabled = false; - ItemStack stack = event.getItemDrop().getItemStack(); - if (tool.removeOnDisable && logblock.hasPermission(player, "logblock.spawnTools")) { - if (stack.isSimilar(new ItemStack(item))) { - if (stack.getAmount() > 1) { - stack.setAmount(stack.getAmount() - 1); - event.getItemDrop().setItemStack(stack); - } else { - event.getItemDrop().remove(); - } - } - } - if (BukkitUtils.hasInventoryStorageSpaceFor(player.getInventory(), stack)) { - event.setCancelled(true); - } - player.sendMessage(ChatColor.GREEN + "Tool disabled."); - } else if (!tool.canDrop) { - player.sendMessage(ChatColor.RED + "You cannot drop this tool."); - event.setCancelled(true); - } - } - } - } - } -} +package de.diddiz.LogBlock.listeners; + +import de.diddiz.LogBlock.*; +import de.diddiz.LogBlock.events.ToolUseEvent; +import de.diddiz.LogBlock.util.BukkitUtils; +import de.diddiz.LogBlock.util.CuboidRegion; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.player.PlayerChangedWorldEvent; +import org.bukkit.event.player.PlayerDropItemEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.Map.Entry; + +import static de.diddiz.LogBlock.Session.getSession; +import static de.diddiz.LogBlock.Session.hasSession; +import static de.diddiz.LogBlock.config.Config.isLogged; +import static de.diddiz.LogBlock.config.Config.toolsByType; + +public class ToolListener implements Listener { + private final CommandsHandler handler; + private final LogBlock logblock; + + public ToolListener(LogBlock logblock) { + this.logblock = logblock; + handler = logblock.getCommandsHandler(); + } + + @EventHandler + public void onPlayerInteract(PlayerInteractEvent event) { + if (event.getMaterial() != null) { + final Action action = event.getAction(); + final Material type = event.getMaterial(); + final Tool tool = toolsByType.get(type); + final Player player = event.getPlayer(); + if (tool != null && (action == Action.RIGHT_CLICK_BLOCK || action == Action.LEFT_CLICK_BLOCK) && logblock.hasPermission(player, "logblock.tools." + tool.name)) { + final ToolBehavior behavior = action == Action.RIGHT_CLICK_BLOCK ? tool.rightClickBehavior : tool.leftClickBehavior; + final ToolData toolData = getSession(player).toolData.get(tool); + if (behavior != ToolBehavior.NONE && toolData.enabled) { + if (!isLogged(player.getWorld())) { + player.sendMessage(ChatColor.RED + "This world is not currently logged."); + event.setCancelled(true); + return; + } + final Block block = event.getClickedBlock(); + final QueryParams params = toolData.params.clone(); + params.loc = null; + params.sel = null; + if (behavior == ToolBehavior.BLOCK) { + params.setLocation(block.getRelative(event.getBlockFace()).getLocation()); + } else if (tool.params.radius != 0) { + params.setLocation(block.getLocation()); + } else { + Block otherHalfChest = BukkitUtils.getConnectedChest(block); + if (otherHalfChest == null) { + params.setLocation(block.getLocation()); + } else { + params.setSelection(CuboidRegion.fromCorners(block.getLocation().getWorld(), block.getLocation(), otherHalfChest.getLocation())); + } + } + try { + params.validate(); + if (this.callToolUseEvent(new ToolUseEvent(player, tool, behavior, params))) { + return; + } + if (toolData.mode == ToolMode.ROLLBACK) { + handler.new CommandRollback(player, params, true); + } else if (toolData.mode == ToolMode.REDO) { + handler.new CommandRedo(player, params, true); + } else if (toolData.mode == ToolMode.CLEARLOG) { + handler.new CommandClearLog(player, params, true); + } else if (toolData.mode == ToolMode.WRITELOGFILE) { + handler.new CommandWriteLogFile(player, params, true); + } else { + handler.new CommandLookup(player, params, true); + } + } catch (final Exception ex) { + player.sendMessage(ChatColor.RED + ex.getMessage()); + } + event.setCancelled(true); + } + } + } + } + + private boolean callToolUseEvent(ToolUseEvent event) { + this.logblock.getServer().getPluginManager().callEvent(event); + return event.isCancelled(); + } + + @EventHandler + public void onPlayerChangedWorld(PlayerChangedWorldEvent event) { + final Player player = event.getPlayer(); + if (hasSession(player)) { + final Session session = getSession(player); + for (final Entry entry : session.toolData.entrySet()) { + final Tool tool = entry.getKey(); + final ToolData toolData = entry.getValue(); + if (toolData.enabled && !logblock.hasPermission(player, "logblock.tools." + tool.name)) { + toolData.enabled = false; + if (tool.removeOnDisable && logblock.hasPermission(player, "logblock.spawnTools")) { + player.getInventory().removeItem(new ItemStack(tool.item, 1)); + } + player.sendMessage(ChatColor.GREEN + "Tool disabled."); + } + } + } + } + + @EventHandler + public void onPlayerDropItem(PlayerDropItemEvent event) { + final Player player = event.getPlayer(); + if (hasSession(player)) { + final Session session = getSession(player); + for (final Entry entry : session.toolData.entrySet()) { + final Tool tool = entry.getKey(); + final ToolData toolData = entry.getValue(); + final Material item = event.getItemDrop().getItemStack().getType(); + if (item == tool.item && toolData.enabled) { + if (tool.dropToDisable) { + toolData.enabled = false; + ItemStack stack = event.getItemDrop().getItemStack(); + if (tool.removeOnDisable && logblock.hasPermission(player, "logblock.spawnTools")) { + if (stack.isSimilar(new ItemStack(item))) { + if (stack.getAmount() > 1) { + stack.setAmount(stack.getAmount() - 1); + event.getItemDrop().setItemStack(stack); + } else { + event.getItemDrop().remove(); + } + } + } + if (BukkitUtils.hasInventoryStorageSpaceFor(player.getInventory(), stack)) { + event.setCancelled(true); + } + player.sendMessage(ChatColor.GREEN + "Tool disabled."); + } else if (!tool.canDrop) { + player.sendMessage(ChatColor.RED + "You cannot drop this tool."); + event.setCancelled(true); + } + } + } + } + } +} diff --git a/src/main/java/de/diddiz/LogBlock/listeners/WitherLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/WitherLogging.java index 3f8c9a8..04c452c 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/WitherLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/WitherLogging.java @@ -1,24 +1,24 @@ -package de.diddiz.LogBlock.listeners; - -import de.diddiz.LogBlock.Actor; -import de.diddiz.LogBlock.LogBlock; -import de.diddiz.LogBlock.Logging; -import org.bukkit.entity.Wither; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.entity.EntityChangeBlockEvent; - -import static de.diddiz.LogBlock.config.Config.isLogging; - -public class WitherLogging extends LoggingListener { - public WitherLogging(LogBlock lb) { - super(lb); - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onEntityChangeBlock(EntityChangeBlockEvent event) { - if (event.getEntity() instanceof Wither && isLogging(event.getBlock().getWorld(), Logging.WITHER)) { - consumer.queueBlockReplace(Actor.actorFromEntity(event.getEntity()), event.getBlock().getState(), event.getBlockData()); // Wither walked through a block. - } - } -} +package de.diddiz.LogBlock.listeners; + +import de.diddiz.LogBlock.Actor; +import de.diddiz.LogBlock.LogBlock; +import de.diddiz.LogBlock.Logging; +import org.bukkit.entity.Wither; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.EntityChangeBlockEvent; + +import static de.diddiz.LogBlock.config.Config.isLogging; + +public class WitherLogging extends LoggingListener { + public WitherLogging(LogBlock lb) { + super(lb); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onEntityChangeBlock(EntityChangeBlockEvent event) { + if (event.getEntity() instanceof Wither && isLogging(event.getBlock().getWorld(), Logging.WITHER)) { + consumer.queueBlockReplace(Actor.actorFromEntity(event.getEntity()), event.getBlock().getState(), event.getBlockData()); // Wither walked through a block. + } + } +} diff --git a/src/main/java/de/diddiz/LogBlock/util/BukkitUtils.java b/src/main/java/de/diddiz/LogBlock/util/BukkitUtils.java index 91d19c0..96fb085 100644 --- a/src/main/java/de/diddiz/LogBlock/util/BukkitUtils.java +++ b/src/main/java/de/diddiz/LogBlock/util/BukkitUtils.java @@ -1,1312 +1,1312 @@ -package de.diddiz.LogBlock.util; - -import static de.diddiz.LogBlock.util.MessagingUtil.prettyMaterial; - -import de.diddiz.LogBlock.LogBlock; -import java.io.File; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; -import java.util.Set; -import java.util.UUID; -import net.md_5.bungee.api.chat.BaseComponent; -import net.md_5.bungee.api.chat.HoverEvent; -import net.md_5.bungee.api.chat.HoverEvent.Action; -import net.md_5.bungee.api.chat.ItemTag; -import net.md_5.bungee.api.chat.TextComponent; -import net.md_5.bungee.api.chat.hover.content.Item; -import net.md_5.bungee.api.chat.hover.content.Text; -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.Chunk; -import org.bukkit.DyeColor; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.block.BlockState; -import org.bukkit.block.DoubleChest; -import org.bukkit.block.data.Bisected; -import org.bukkit.block.data.Bisected.Half; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.Directional; -import org.bukkit.block.data.Rotatable; -import org.bukkit.block.data.type.HangingSign; -import org.bukkit.block.data.type.Sign; -import org.bukkit.block.data.type.Slab; -import org.bukkit.block.data.type.Slab.Type; -import org.bukkit.block.data.type.Stairs; -import org.bukkit.block.data.type.WallHangingSign; -import org.bukkit.block.data.type.WallSign; -import org.bukkit.block.sign.Side; -import org.bukkit.entity.ArmorStand; -import org.bukkit.entity.Entity; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.Player; -import org.bukkit.entity.TNTPrimed; -import org.bukkit.inventory.EquipmentSlot; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.InventoryHolder; -import org.bukkit.inventory.ItemStack; - -public class BukkitUtils { - private static final Set> blockEquivalents; - private static final Set relativeBreakable; - private static final Set relativeTopBreakable; - private static final Set fallingEntityKillers; - - private static final Set cropBlocks; - private static final Set containerBlocks; - private static final Set shulkerBoxBlocks; - - private static final Set singleBlockPlants; - private static final Set doublePlants; - - private static final Set nonFluidProofBlocks; - - private static final Set bedBlocks; - - private static final Map projectileItems; - private static final HashSet signs; - private static final HashSet wallSigns; - private static final HashSet hangingSigns; - private static final HashSet hangingWallSigns; - private static final HashSet allSigns; - private static final Set unmodifiableSigns; - private static final HashSet buttons; - private static final HashSet pressurePlates; - private static final HashSet woodenDoors; - private static final HashSet slabs; - private static final HashSet concreteBlocks; - private static final HashMap dyes; - private static final HashSet alwaysWaterlogged; - private static final HashSet candles; - private static final HashSet candleCakes; - private static final HashSet fenceGates; - private static final HashSet woodenTrapdoors; - - static { - fenceGates = new HashSet<>(); - fenceGates.add(Material.OAK_FENCE_GATE); - fenceGates.add(Material.SPRUCE_FENCE_GATE); - fenceGates.add(Material.BIRCH_FENCE_GATE); - fenceGates.add(Material.JUNGLE_FENCE_GATE); - fenceGates.add(Material.ACACIA_FENCE_GATE); - fenceGates.add(Material.DARK_OAK_FENCE_GATE); - fenceGates.add(Material.WARPED_FENCE_GATE); - fenceGates.add(Material.CRIMSON_FENCE_GATE); - fenceGates.add(Material.MANGROVE_FENCE_GATE); - fenceGates.add(Material.BAMBOO_FENCE_GATE); - fenceGates.add(Material.CHERRY_FENCE_GATE); - - woodenTrapdoors = new HashSet<>(); - woodenTrapdoors.add(Material.OAK_TRAPDOOR); - woodenTrapdoors.add(Material.SPRUCE_TRAPDOOR); - woodenTrapdoors.add(Material.BIRCH_TRAPDOOR); - woodenTrapdoors.add(Material.JUNGLE_TRAPDOOR); - woodenTrapdoors.add(Material.ACACIA_TRAPDOOR); - woodenTrapdoors.add(Material.DARK_OAK_TRAPDOOR); - woodenTrapdoors.add(Material.WARPED_TRAPDOOR); - woodenTrapdoors.add(Material.CRIMSON_TRAPDOOR); - woodenTrapdoors.add(Material.MANGROVE_TRAPDOOR); - woodenTrapdoors.add(Material.BAMBOO_TRAPDOOR); - woodenTrapdoors.add(Material.CHERRY_TRAPDOOR); - - pressurePlates = new HashSet<>(); - pressurePlates.add(Material.OAK_PRESSURE_PLATE); - pressurePlates.add(Material.SPRUCE_PRESSURE_PLATE); - pressurePlates.add(Material.BIRCH_PRESSURE_PLATE); - pressurePlates.add(Material.JUNGLE_PRESSURE_PLATE); - pressurePlates.add(Material.ACACIA_PRESSURE_PLATE); - pressurePlates.add(Material.DARK_OAK_PRESSURE_PLATE); - pressurePlates.add(Material.WARPED_PRESSURE_PLATE); - pressurePlates.add(Material.CRIMSON_PRESSURE_PLATE); - pressurePlates.add(Material.MANGROVE_PRESSURE_PLATE); - pressurePlates.add(Material.BAMBOO_PRESSURE_PLATE); - pressurePlates.add(Material.CHERRY_PRESSURE_PLATE); - pressurePlates.add(Material.STONE_PRESSURE_PLATE); - pressurePlates.add(Material.LIGHT_WEIGHTED_PRESSURE_PLATE); - pressurePlates.add(Material.HEAVY_WEIGHTED_PRESSURE_PLATE); - - woodenDoors = new HashSet<>(); - woodenDoors.add(Material.OAK_DOOR); - woodenDoors.add(Material.SPRUCE_DOOR); - woodenDoors.add(Material.BIRCH_DOOR); - woodenDoors.add(Material.JUNGLE_DOOR); - woodenDoors.add(Material.ACACIA_DOOR); - woodenDoors.add(Material.DARK_OAK_DOOR); - woodenDoors.add(Material.WARPED_DOOR); - woodenDoors.add(Material.CRIMSON_DOOR); - woodenDoors.add(Material.MANGROVE_DOOR); - woodenDoors.add(Material.BAMBOO_DOOR); - woodenDoors.add(Material.CHERRY_DOOR); - - HashSet saplings = new HashSet<>(); - saplings.add(Material.OAK_SAPLING); - saplings.add(Material.SPRUCE_SAPLING); - saplings.add(Material.BIRCH_SAPLING); - saplings.add(Material.JUNGLE_SAPLING); - saplings.add(Material.ACACIA_SAPLING); - saplings.add(Material.DARK_OAK_SAPLING); - saplings.add(Material.CHERRY_SAPLING); - saplings.add(Material.WARPED_FUNGUS); - saplings.add(Material.CRIMSON_FUNGUS); - saplings.add(Material.MANGROVE_PROPAGULE); - - HashSet carpets = new HashSet<>(); - carpets.add(Material.BLACK_CARPET); - carpets.add(Material.BLUE_CARPET); - carpets.add(Material.LIGHT_GRAY_CARPET); - carpets.add(Material.BROWN_CARPET); - carpets.add(Material.CYAN_CARPET); - carpets.add(Material.GRAY_CARPET); - carpets.add(Material.GREEN_CARPET); - carpets.add(Material.LIGHT_BLUE_CARPET); - carpets.add(Material.MAGENTA_CARPET); - carpets.add(Material.LIME_CARPET); - carpets.add(Material.ORANGE_CARPET); - carpets.add(Material.PINK_CARPET); - carpets.add(Material.PURPLE_CARPET); - carpets.add(Material.RED_CARPET); - carpets.add(Material.WHITE_CARPET); - carpets.add(Material.YELLOW_CARPET); - - slabs = new HashSet<>(); - slabs.add(Material.OAK_SLAB); - slabs.add(Material.SPRUCE_SLAB); - slabs.add(Material.BIRCH_SLAB); - slabs.add(Material.JUNGLE_SLAB); - slabs.add(Material.ACACIA_SLAB); - slabs.add(Material.DARK_OAK_SLAB); - slabs.add(Material.WARPED_SLAB); - slabs.add(Material.CRIMSON_SLAB); - slabs.add(Material.STONE_SLAB); - slabs.add(Material.STONE_BRICK_SLAB); - slabs.add(Material.COBBLESTONE_SLAB); - slabs.add(Material.PETRIFIED_OAK_SLAB); - slabs.add(Material.SANDSTONE_SLAB); - slabs.add(Material.RED_SANDSTONE_SLAB); - slabs.add(Material.NETHER_BRICK_SLAB); - slabs.add(Material.PURPUR_SLAB); - slabs.add(Material.QUARTZ_SLAB); - slabs.add(Material.BRICK_SLAB); - slabs.add(Material.PRISMARINE_SLAB); - slabs.add(Material.DARK_PRISMARINE_SLAB); - slabs.add(Material.PRISMARINE_BRICK_SLAB); - slabs.add(Material.BLACKSTONE_SLAB); - slabs.add(Material.POLISHED_BLACKSTONE_SLAB); - slabs.add(Material.DEEPSLATE_BRICK_SLAB); - slabs.add(Material.DEEPSLATE_TILE_SLAB); - slabs.add(Material.COBBLED_DEEPSLATE_SLAB); - slabs.add(Material.POLISHED_DEEPSLATE_SLAB); - slabs.add(Material.MANGROVE_SLAB); - slabs.add(Material.BAMBOO_SLAB); - slabs.add(Material.CHERRY_SLAB); - - buttons = new HashSet<>(); - buttons.add(Material.STONE_BUTTON); - buttons.add(Material.OAK_BUTTON); - buttons.add(Material.SPRUCE_BUTTON); - buttons.add(Material.BIRCH_BUTTON); - buttons.add(Material.JUNGLE_BUTTON); - buttons.add(Material.ACACIA_BUTTON); - buttons.add(Material.DARK_OAK_BUTTON); - buttons.add(Material.WARPED_BUTTON); - buttons.add(Material.CRIMSON_BUTTON); - buttons.add(Material.MANGROVE_BUTTON); - buttons.add(Material.BAMBOO_BUTTON); - buttons.add(Material.CHERRY_BUTTON); - buttons.add(Material.POLISHED_BLACKSTONE_BUTTON); - - signs = new HashSet<>(); - signs.add(Material.OAK_SIGN); - signs.add(Material.SPRUCE_SIGN); - signs.add(Material.BIRCH_SIGN); - signs.add(Material.JUNGLE_SIGN); - signs.add(Material.DARK_OAK_SIGN); - signs.add(Material.ACACIA_SIGN); - signs.add(Material.WARPED_SIGN); - signs.add(Material.CRIMSON_SIGN); - signs.add(Material.MANGROVE_SIGN); - signs.add(Material.BAMBOO_SIGN); - signs.add(Material.CHERRY_SIGN); - - wallSigns = new HashSet<>(); - wallSigns.add(Material.OAK_WALL_SIGN); - wallSigns.add(Material.SPRUCE_WALL_SIGN); - wallSigns.add(Material.BIRCH_WALL_SIGN); - wallSigns.add(Material.JUNGLE_WALL_SIGN); - wallSigns.add(Material.DARK_OAK_WALL_SIGN); - wallSigns.add(Material.ACACIA_WALL_SIGN); - wallSigns.add(Material.WARPED_WALL_SIGN); - wallSigns.add(Material.CRIMSON_WALL_SIGN); - wallSigns.add(Material.MANGROVE_WALL_SIGN); - wallSigns.add(Material.BAMBOO_WALL_SIGN); - wallSigns.add(Material.CHERRY_WALL_SIGN); - - hangingSigns = new HashSet<>(); - hangingSigns.add(Material.OAK_HANGING_SIGN); - hangingSigns.add(Material.SPRUCE_HANGING_SIGN); - hangingSigns.add(Material.BIRCH_HANGING_SIGN); - hangingSigns.add(Material.JUNGLE_HANGING_SIGN); - hangingSigns.add(Material.DARK_OAK_HANGING_SIGN); - hangingSigns.add(Material.ACACIA_HANGING_SIGN); - hangingSigns.add(Material.WARPED_HANGING_SIGN); - hangingSigns.add(Material.CRIMSON_HANGING_SIGN); - hangingSigns.add(Material.MANGROVE_HANGING_SIGN); - hangingSigns.add(Material.BAMBOO_HANGING_SIGN); - hangingSigns.add(Material.CHERRY_HANGING_SIGN); - - hangingWallSigns = new HashSet<>(); - hangingWallSigns.add(Material.OAK_WALL_HANGING_SIGN); - hangingWallSigns.add(Material.SPRUCE_WALL_HANGING_SIGN); - hangingWallSigns.add(Material.BIRCH_WALL_HANGING_SIGN); - hangingWallSigns.add(Material.JUNGLE_WALL_HANGING_SIGN); - hangingWallSigns.add(Material.DARK_OAK_WALL_HANGING_SIGN); - hangingWallSigns.add(Material.ACACIA_WALL_HANGING_SIGN); - hangingWallSigns.add(Material.WARPED_WALL_HANGING_SIGN); - hangingWallSigns.add(Material.CRIMSON_WALL_HANGING_SIGN); - hangingWallSigns.add(Material.MANGROVE_WALL_HANGING_SIGN); - hangingWallSigns.add(Material.BAMBOO_WALL_HANGING_SIGN); - hangingWallSigns.add(Material.CHERRY_WALL_HANGING_SIGN); - - allSigns = new HashSet<>(); - allSigns.addAll(signs); - allSigns.addAll(wallSigns); - allSigns.addAll(hangingSigns); - allSigns.addAll(hangingWallSigns); - unmodifiableSigns = Collections.unmodifiableSet(allSigns); - - singleBlockPlants = new HashSet<>(); - singleBlockPlants.add(Material.GRASS); - singleBlockPlants.add(Material.FERN); - singleBlockPlants.add(Material.DEAD_BUSH); - singleBlockPlants.add(Material.DANDELION); - singleBlockPlants.add(Material.POPPY); - singleBlockPlants.add(Material.BLUE_ORCHID); - singleBlockPlants.add(Material.ALLIUM); - singleBlockPlants.add(Material.AZURE_BLUET); - singleBlockPlants.add(Material.ORANGE_TULIP); - singleBlockPlants.add(Material.WHITE_TULIP); - singleBlockPlants.add(Material.PINK_TULIP); - singleBlockPlants.add(Material.RED_TULIP); - singleBlockPlants.add(Material.OXEYE_DAISY); - singleBlockPlants.add(Material.BROWN_MUSHROOM); - singleBlockPlants.add(Material.RED_MUSHROOM); - singleBlockPlants.add(Material.SWEET_BERRY_BUSH); - singleBlockPlants.add(Material.LILY_OF_THE_VALLEY); - singleBlockPlants.add(Material.CORNFLOWER); - singleBlockPlants.add(Material.WITHER_ROSE); - singleBlockPlants.add(Material.CRIMSON_FUNGUS); - singleBlockPlants.add(Material.WARPED_FUNGUS); - singleBlockPlants.add(Material.CRIMSON_ROOTS); - singleBlockPlants.add(Material.WARPED_ROOTS); - singleBlockPlants.add(Material.NETHER_SPROUTS); - singleBlockPlants.add(Material.AZALEA); - singleBlockPlants.add(Material.FLOWERING_AZALEA); - singleBlockPlants.add(Material.PINK_PETALS); - singleBlockPlants.add(Material.TORCHFLOWER); - singleBlockPlants.add(Material.PITCHER_CROP); - - doublePlants = new HashSet<>(); - doublePlants.add(Material.TALL_GRASS); - doublePlants.add(Material.LARGE_FERN); - doublePlants.add(Material.TALL_SEAGRASS); - doublePlants.add(Material.ROSE_BUSH); - doublePlants.add(Material.LILAC); - doublePlants.add(Material.SUNFLOWER); - doublePlants.add(Material.PEONY); - doublePlants.add(Material.SMALL_DRIPLEAF); - doublePlants.add(Material.PITCHER_PLANT); - - blockEquivalents = new HashSet<>(7); - blockEquivalents.add(new HashSet<>(Arrays.asList(2, 3, 60))); - blockEquivalents.add(new HashSet<>(Arrays.asList(8, 9, 79))); - blockEquivalents.add(new HashSet<>(Arrays.asList(10, 11))); - blockEquivalents.add(new HashSet<>(Arrays.asList(61, 62))); - blockEquivalents.add(new HashSet<>(Arrays.asList(73, 74))); - blockEquivalents.add(new HashSet<>(Arrays.asList(75, 76))); - blockEquivalents.add(new HashSet<>(Arrays.asList(93, 94))); - - // Blocks that break when they are attached to a block - relativeBreakable = new HashSet<>(); - relativeBreakable.addAll(wallSigns); - relativeBreakable.add(Material.LADDER); - relativeBreakable.addAll(buttons); - relativeBreakable.add(Material.REDSTONE_WALL_TORCH); - relativeBreakable.add(Material.LEVER); - relativeBreakable.add(Material.WALL_TORCH); - relativeBreakable.add(Material.TRIPWIRE_HOOK); - relativeBreakable.add(Material.COCOA); - relativeBreakable.add(Material.BELL); - relativeBreakable.add(Material.AMETHYST_CLUSTER); - relativeBreakable.add(Material.SMALL_AMETHYST_BUD); - relativeBreakable.add(Material.MEDIUM_AMETHYST_BUD); - relativeBreakable.add(Material.LARGE_AMETHYST_BUD); - - // Blocks that break when they are on top of a block - relativeTopBreakable = new HashSet<>(); - relativeTopBreakable.addAll(saplings); - relativeTopBreakable.addAll(singleBlockPlants); - relativeTopBreakable.add(Material.WHEAT); - relativeTopBreakable.add(Material.POTATO); - relativeTopBreakable.add(Material.CARROT); - relativeTopBreakable.add(Material.LILY_PAD); - relativeTopBreakable.add(Material.CACTUS); - relativeTopBreakable.add(Material.SUGAR_CANE); - relativeTopBreakable.add(Material.FLOWER_POT); - relativeTopBreakable.add(Material.POWERED_RAIL); - relativeTopBreakable.add(Material.DETECTOR_RAIL); - relativeTopBreakable.add(Material.ACTIVATOR_RAIL); - relativeTopBreakable.add(Material.RAIL); - relativeTopBreakable.add(Material.REDSTONE_WIRE); - relativeTopBreakable.addAll(signs); - relativeTopBreakable.addAll(pressurePlates); - relativeTopBreakable.add(Material.SNOW); - relativeTopBreakable.add(Material.REPEATER); - relativeTopBreakable.add(Material.COMPARATOR); - relativeTopBreakable.add(Material.TORCH); - relativeTopBreakable.add(Material.SOUL_TORCH); - relativeTopBreakable.add(Material.REDSTONE_TORCH); - relativeTopBreakable.addAll(woodenDoors); - relativeTopBreakable.add(Material.IRON_DOOR); - relativeTopBreakable.addAll(carpets); - relativeTopBreakable.addAll(doublePlants); - relativeTopBreakable.add(Material.BAMBOO); - relativeTopBreakable.add(Material.BAMBOO_SAPLING); - relativeTopBreakable.add(Material.TWISTING_VINES); - relativeTopBreakable.add(Material.TWISTING_VINES_PLANT); - relativeTopBreakable.add(Material.BIG_DRIPLEAF); - relativeTopBreakable.add(Material.BIG_DRIPLEAF_STEM); - for (Material m : Material.values()) { - if (m.name().startsWith("POTTED_")) { - relativeTopBreakable.add(m); - } - if (m.name().endsWith("CANDLE_CAKE")) { - relativeTopBreakable.add(m); - } - } - - // Blocks that break falling entities - fallingEntityKillers = new HashSet<>(); - fallingEntityKillers.addAll(signs); - fallingEntityKillers.addAll(wallSigns); - fallingEntityKillers.addAll(pressurePlates); - fallingEntityKillers.addAll(saplings); - fallingEntityKillers.addAll(singleBlockPlants); - fallingEntityKillers.remove(Material.GRASS); - fallingEntityKillers.remove(Material.NETHER_SPROUTS); - fallingEntityKillers.addAll(doublePlants); - fallingEntityKillers.add(Material.WHEAT); - fallingEntityKillers.add(Material.CARROT); - fallingEntityKillers.add(Material.POTATO); - fallingEntityKillers.add(Material.BEETROOT); - fallingEntityKillers.add(Material.NETHER_WART); - fallingEntityKillers.add(Material.COCOA); - fallingEntityKillers.addAll(slabs); - fallingEntityKillers.add(Material.TORCH); - fallingEntityKillers.add(Material.WALL_TORCH); - fallingEntityKillers.add(Material.SOUL_TORCH); - fallingEntityKillers.add(Material.SOUL_WALL_TORCH); - fallingEntityKillers.add(Material.FLOWER_POT); - fallingEntityKillers.add(Material.POWERED_RAIL); - fallingEntityKillers.add(Material.DETECTOR_RAIL); - fallingEntityKillers.add(Material.ACTIVATOR_RAIL); - fallingEntityKillers.add(Material.RAIL); - fallingEntityKillers.add(Material.LEVER); - fallingEntityKillers.add(Material.REDSTONE_WIRE); - fallingEntityKillers.add(Material.REDSTONE_TORCH); - fallingEntityKillers.add(Material.REDSTONE_WALL_TORCH); - fallingEntityKillers.add(Material.REPEATER); - fallingEntityKillers.add(Material.COMPARATOR); - fallingEntityKillers.add(Material.DAYLIGHT_DETECTOR); - fallingEntityKillers.addAll(carpets); - fallingEntityKillers.add(Material.PLAYER_HEAD); - fallingEntityKillers.add(Material.PLAYER_WALL_HEAD); - fallingEntityKillers.add(Material.CREEPER_HEAD); - fallingEntityKillers.add(Material.CREEPER_WALL_HEAD); - fallingEntityKillers.add(Material.DRAGON_HEAD); - fallingEntityKillers.add(Material.DRAGON_WALL_HEAD); - fallingEntityKillers.add(Material.ZOMBIE_HEAD); - fallingEntityKillers.add(Material.ZOMBIE_WALL_HEAD); - fallingEntityKillers.add(Material.SKELETON_SKULL); - fallingEntityKillers.add(Material.SKELETON_WALL_SKULL); - fallingEntityKillers.add(Material.WITHER_SKELETON_SKULL); - fallingEntityKillers.add(Material.WITHER_SKELETON_WALL_SKULL); - for (Material m : Material.values()) { - if (m.name().contains("CANDLE")) { - fallingEntityKillers.add(m); - } - } - - // Crop Blocks - cropBlocks = new HashSet<>(); - cropBlocks.add(Material.WHEAT); - cropBlocks.add(Material.MELON_STEM); - cropBlocks.add(Material.PUMPKIN_STEM); - cropBlocks.add(Material.CARROT); - cropBlocks.add(Material.POTATO); - cropBlocks.add(Material.BEETROOT); - cropBlocks.add(Material.TORCHFLOWER_CROP); - - // Shulker Boxes - shulkerBoxBlocks = new HashSet<>(); - shulkerBoxBlocks.add(Material.SHULKER_BOX); - shulkerBoxBlocks.add(Material.BLACK_SHULKER_BOX); - shulkerBoxBlocks.add(Material.BLUE_SHULKER_BOX); - shulkerBoxBlocks.add(Material.LIGHT_GRAY_SHULKER_BOX); - shulkerBoxBlocks.add(Material.BROWN_SHULKER_BOX); - shulkerBoxBlocks.add(Material.CYAN_SHULKER_BOX); - shulkerBoxBlocks.add(Material.GRAY_SHULKER_BOX); - shulkerBoxBlocks.add(Material.GREEN_SHULKER_BOX); - shulkerBoxBlocks.add(Material.LIGHT_BLUE_SHULKER_BOX); - shulkerBoxBlocks.add(Material.MAGENTA_SHULKER_BOX); - shulkerBoxBlocks.add(Material.LIME_SHULKER_BOX); - shulkerBoxBlocks.add(Material.ORANGE_SHULKER_BOX); - shulkerBoxBlocks.add(Material.PINK_SHULKER_BOX); - shulkerBoxBlocks.add(Material.PURPLE_SHULKER_BOX); - shulkerBoxBlocks.add(Material.RED_SHULKER_BOX); - shulkerBoxBlocks.add(Material.WHITE_SHULKER_BOX); - shulkerBoxBlocks.add(Material.YELLOW_SHULKER_BOX); - - // Container Blocks - containerBlocks = new HashSet<>(); - containerBlocks.add(Material.CHEST); - containerBlocks.add(Material.TRAPPED_CHEST); - containerBlocks.add(Material.DISPENSER); - containerBlocks.add(Material.DROPPER); - containerBlocks.add(Material.HOPPER); - containerBlocks.add(Material.BREWING_STAND); - containerBlocks.add(Material.FURNACE); - containerBlocks.addAll(shulkerBoxBlocks); - containerBlocks.add(Material.BARREL); - containerBlocks.add(Material.BLAST_FURNACE); - containerBlocks.add(Material.SMOKER); - containerBlocks.add(Material.CHISELED_BOOKSHELF); - // Doesn't actually have a block inventory - // containerBlocks.add(Material.ENDER_CHEST); - - // It doesn't seem like you could injure people with some of these, but they exist, so.... - projectileItems = new HashMap<>(); - projectileItems.put(EntityType.ARROW, Material.ARROW); - projectileItems.put(EntityType.EGG, Material.EGG); - projectileItems.put(EntityType.ENDER_PEARL, Material.ENDER_PEARL); - projectileItems.put(EntityType.SMALL_FIREBALL, Material.FIRE_CHARGE); // Fire charge - projectileItems.put(EntityType.FIREBALL, Material.FIRE_CHARGE); // Fire charge - projectileItems.put(EntityType.FISHING_HOOK, Material.FISHING_ROD); - projectileItems.put(EntityType.SNOWBALL, Material.SNOWBALL); - projectileItems.put(EntityType.SPLASH_POTION, Material.SPLASH_POTION); - projectileItems.put(EntityType.THROWN_EXP_BOTTLE, Material.EXPERIENCE_BOTTLE); - projectileItems.put(EntityType.WITHER_SKULL, Material.WITHER_SKELETON_SKULL); - projectileItems.put(EntityType.FIREWORK, Material.FIREWORK_ROCKET); - - nonFluidProofBlocks = new HashSet<>(); - nonFluidProofBlocks.addAll(singleBlockPlants); - nonFluidProofBlocks.addAll(doublePlants); - nonFluidProofBlocks.add(Material.REDSTONE_WALL_TORCH); - nonFluidProofBlocks.add(Material.LEVER); - nonFluidProofBlocks.add(Material.WALL_TORCH); - nonFluidProofBlocks.add(Material.SOUL_WALL_TORCH); - nonFluidProofBlocks.add(Material.TRIPWIRE_HOOK); - nonFluidProofBlocks.add(Material.COCOA); - nonFluidProofBlocks.addAll(pressurePlates); - nonFluidProofBlocks.addAll(saplings); - nonFluidProofBlocks.add(Material.WHEAT); - nonFluidProofBlocks.add(Material.CARROT); - nonFluidProofBlocks.add(Material.POTATO); - nonFluidProofBlocks.add(Material.BEETROOT); - nonFluidProofBlocks.add(Material.NETHER_WART); - nonFluidProofBlocks.add(Material.TORCH); - nonFluidProofBlocks.add(Material.SOUL_TORCH); - nonFluidProofBlocks.add(Material.FLOWER_POT); - // nonFluidProofBlocks.add(Material.POWERED_RAIL); - // nonFluidProofBlocks.add(Material.DETECTOR_RAIL); - // nonFluidProofBlocks.add(Material.ACTIVATOR_RAIL); - // nonFluidProofBlocks.add(Material.RAIL); - nonFluidProofBlocks.add(Material.LEVER); - nonFluidProofBlocks.add(Material.REDSTONE_WIRE); - nonFluidProofBlocks.add(Material.REDSTONE_TORCH); - nonFluidProofBlocks.add(Material.REPEATER); - nonFluidProofBlocks.add(Material.COMPARATOR); - nonFluidProofBlocks.add(Material.DAYLIGHT_DETECTOR); - nonFluidProofBlocks.addAll(carpets); - - alwaysWaterlogged = new HashSet<>(); - alwaysWaterlogged.add(Material.SEAGRASS); - alwaysWaterlogged.add(Material.TALL_SEAGRASS); - alwaysWaterlogged.add(Material.KELP); - alwaysWaterlogged.add(Material.KELP_PLANT); - - bedBlocks = new HashSet<>(); - bedBlocks.add(Material.BLACK_BED); - bedBlocks.add(Material.BLUE_BED); - bedBlocks.add(Material.LIGHT_GRAY_BED); - bedBlocks.add(Material.BROWN_BED); - bedBlocks.add(Material.CYAN_BED); - bedBlocks.add(Material.GRAY_BED); - bedBlocks.add(Material.GREEN_BED); - bedBlocks.add(Material.LIGHT_BLUE_BED); - bedBlocks.add(Material.MAGENTA_BED); - bedBlocks.add(Material.LIME_BED); - bedBlocks.add(Material.ORANGE_BED); - bedBlocks.add(Material.PINK_BED); - bedBlocks.add(Material.PURPLE_BED); - bedBlocks.add(Material.RED_BED); - bedBlocks.add(Material.WHITE_BED); - bedBlocks.add(Material.YELLOW_BED); - - concreteBlocks = new HashSet<>(); - concreteBlocks.add(Material.BLACK_CONCRETE); - concreteBlocks.add(Material.BLUE_CONCRETE); - concreteBlocks.add(Material.LIGHT_GRAY_CONCRETE); - concreteBlocks.add(Material.BROWN_CONCRETE); - concreteBlocks.add(Material.CYAN_CONCRETE); - concreteBlocks.add(Material.GRAY_CONCRETE); - concreteBlocks.add(Material.GREEN_CONCRETE); - concreteBlocks.add(Material.LIGHT_BLUE_CONCRETE); - concreteBlocks.add(Material.MAGENTA_CONCRETE); - concreteBlocks.add(Material.LIME_CONCRETE); - concreteBlocks.add(Material.ORANGE_CONCRETE); - concreteBlocks.add(Material.PINK_CONCRETE); - concreteBlocks.add(Material.PURPLE_CONCRETE); - concreteBlocks.add(Material.RED_CONCRETE); - concreteBlocks.add(Material.WHITE_CONCRETE); - concreteBlocks.add(Material.YELLOW_CONCRETE); - - candles = new HashSet<>(); - candles.add(Material.CANDLE); - candles.add(Material.BLACK_CANDLE); - candles.add(Material.BLUE_CANDLE); - candles.add(Material.LIGHT_GRAY_CANDLE); - candles.add(Material.BROWN_CANDLE); - candles.add(Material.CYAN_CANDLE); - candles.add(Material.GRAY_CANDLE); - candles.add(Material.GREEN_CANDLE); - candles.add(Material.LIGHT_BLUE_CANDLE); - candles.add(Material.MAGENTA_CANDLE); - candles.add(Material.LIME_CANDLE); - candles.add(Material.ORANGE_CANDLE); - candles.add(Material.PINK_CANDLE); - candles.add(Material.PURPLE_CANDLE); - candles.add(Material.RED_CANDLE); - candles.add(Material.WHITE_CANDLE); - candles.add(Material.YELLOW_CANDLE); - - candleCakes = new HashSet<>(); - candleCakes.add(Material.CANDLE_CAKE); - candleCakes.add(Material.BLACK_CANDLE_CAKE); - candleCakes.add(Material.BLUE_CANDLE_CAKE); - candleCakes.add(Material.LIGHT_GRAY_CANDLE_CAKE); - candleCakes.add(Material.BROWN_CANDLE_CAKE); - candleCakes.add(Material.CYAN_CANDLE_CAKE); - candleCakes.add(Material.GRAY_CANDLE_CAKE); - candleCakes.add(Material.GREEN_CANDLE_CAKE); - candleCakes.add(Material.LIGHT_BLUE_CANDLE_CAKE); - candleCakes.add(Material.MAGENTA_CANDLE_CAKE); - candleCakes.add(Material.LIME_CANDLE_CAKE); - candleCakes.add(Material.ORANGE_CANDLE_CAKE); - candleCakes.add(Material.PINK_CANDLE_CAKE); - candleCakes.add(Material.PURPLE_CANDLE_CAKE); - candleCakes.add(Material.RED_CANDLE_CAKE); - candleCakes.add(Material.WHITE_CANDLE_CAKE); - candleCakes.add(Material.YELLOW_CANDLE_CAKE); - - dyes = new HashMap<>(); - dyes.put(Material.BLACK_DYE, DyeColor.BLACK); - dyes.put(Material.BLUE_DYE, DyeColor.BLUE); - dyes.put(Material.LIGHT_GRAY_DYE, DyeColor.LIGHT_GRAY); - dyes.put(Material.BROWN_DYE, DyeColor.BROWN); - dyes.put(Material.CYAN_DYE, DyeColor.CYAN); - dyes.put(Material.GRAY_DYE, DyeColor.GRAY); - dyes.put(Material.GREEN_DYE, DyeColor.GREEN); - dyes.put(Material.LIGHT_BLUE_DYE, DyeColor.LIGHT_BLUE); - dyes.put(Material.MAGENTA_DYE, DyeColor.MAGENTA); - dyes.put(Material.LIME_DYE, DyeColor.LIME); - dyes.put(Material.ORANGE_DYE, DyeColor.ORANGE); - dyes.put(Material.PINK_DYE, DyeColor.PINK); - dyes.put(Material.PURPLE_DYE, DyeColor.PURPLE); - dyes.put(Material.RED_DYE, DyeColor.RED); - dyes.put(Material.WHITE_DYE, DyeColor.WHITE); - dyes.put(Material.YELLOW_DYE, DyeColor.YELLOW); - } - - private static final BlockFace[] relativeBlockFaces = new BlockFace[] { - BlockFace.EAST, BlockFace.WEST, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.UP, BlockFace.DOWN - }; - - /** - * Returns a list of block locations around the block that are of the type specified by the integer list parameter - * - * @param block The central block to get the blocks around - * @param type The type of blocks around the center block to return - * @return List of block locations around the block that are of the type specified by the integer list parameter - */ - public static List getBlocksNearby(org.bukkit.block.Block block, Set type) { - ArrayList blocks = new ArrayList<>(); - for (BlockFace blockFace : relativeBlockFaces) { - if (type.contains(block.getRelative(blockFace).getType())) { - blocks.add(block.getRelative(blockFace).getLocation()); - } - } - return blocks; - } - - public static boolean isTop(BlockData data) { - if (data instanceof Bisected && !(data instanceof Stairs)) { - return ((Bisected) data).getHalf() == Half.TOP; - } - return false; - } - - public static Material getInventoryHolderType(InventoryHolder holder) { - if (holder instanceof DoubleChest) { - return getInventoryHolderType(((DoubleChest) holder).getLeftSide()); - } else if (holder instanceof BlockState) { - return ((BlockState) holder).getType(); - } else { - return null; - } - } - - public static Location getInventoryHolderLocation(InventoryHolder holder) { - if (holder instanceof DoubleChest) { - return getInventoryHolderLocation(((DoubleChest) holder).getLeftSide()); - } else if (holder instanceof BlockState) { - return ((BlockState) holder).getLocation(); - } else { - return null; - } - } - - public static ItemStack[] compareInventories(ItemStack[] items1, ItemStack[] items2) { - final ArrayList diff = new ArrayList<>(); - for (ItemStack current : items2) { - try { - diff.add(new ItemStack(current)); - } catch (NullPointerException e) { - LogBlock.getInstance().getLogger().log(Level.SEVERE, "Could not clone ItemStack, probably Spigot bug SPIGOT-6025", e); // SPIGOT-6025 - } - } - for (ItemStack previous : items1) { - boolean found = false; - for (ItemStack current : diff) { - if (current.isSimilar(previous)) { - int newAmount = current.getAmount() - previous.getAmount(); - if (newAmount == 0) { - diff.remove(current); - } else { - current.setAmount(newAmount); - } - found = true; - break; - } - } - if (!found) { - try { - ItemStack subtracted = new ItemStack(previous); - subtracted.setAmount(-subtracted.getAmount()); - diff.add(subtracted); - } catch (NullPointerException e) { - LogBlock.getInstance().getLogger().log(Level.SEVERE, "Could not clone ItemStack, probably Spigot bug SPIGOT-6025", e); // SPIGOT-6025 - } - } - } - return diff.toArray(new ItemStack[diff.size()]); - } - - public static ItemStack[] compressInventory(ItemStack[] items) { - final ArrayList compressed = new ArrayList<>(); - for (final ItemStack item : items) { - if (item != null) { - boolean found = false; - for (final ItemStack item2 : compressed) { - if (item2.isSimilar(item)) { - item2.setAmount(item2.getAmount() + item.getAmount()); - found = true; - break; - } - } - if (!found) { - compressed.add(item.clone()); - } - } - } - return compressed.toArray(new ItemStack[compressed.size()]); - } - - public static boolean equalTypes(int type1, int type2) { - if (type1 == type2) { - return true; - } - for (final Set equivalent : blockEquivalents) { - if (equivalent.contains(type1) && equivalent.contains(type2)) { - return true; - } - } - return false; - } - - public static String friendlyWorldname(String worldName) { - return new File(worldName).getName(); - } - - public static Set> getBlockEquivalents() { - return blockEquivalents; - } - - public static Set getRelativeBreakables() { - return relativeBreakable; - } - - public static Set getRelativeTopBreakabls() { - return relativeTopBreakable; - } - - public static Set getFallingEntityKillers() { - return fallingEntityKillers; - } - - public static Set getNonFluidProofBlocks() { - return nonFluidProofBlocks; - } - - public static Set getCropBlocks() { - return cropBlocks; - } - - public static Set getContainerBlocks() { - return containerBlocks; - } - - public static Set getShulkerBoxBlocks() { - return shulkerBoxBlocks; - } - - public static boolean isConcreteBlock(Material m) { - return concreteBlocks.contains(m); - } - - public static String entityName(Entity entity) { - if (entity instanceof Player) { - return ((Player) entity).getName(); - } - if (entity instanceof TNTPrimed) { - return "TNT"; - } - return entity.getClass().getSimpleName().substring(5); - } - - public static void giveTool(Player player, Material type) { - final Inventory inv = player.getInventory(); - if (inv.contains(type)) { - player.sendMessage(ChatColor.RED + "You have already a " + type.name()); - } else { - final int free = inv.firstEmpty(); - if (free >= 0) { - if (player.getInventory().getItemInMainHand() != null && player.getInventory().getItemInMainHand().getType() != Material.AIR) { - inv.setItem(free, player.getInventory().getItemInMainHand()); - } - player.getInventory().setItemInMainHand(new ItemStack(type)); - player.sendMessage(ChatColor.GREEN + "Here's your " + type.name()); - } else { - player.sendMessage(ChatColor.RED + "You have no empty slot in your inventory"); - } - } - } - - public static int safeSpawnHeight(Location loc) { - final World world = loc.getWorld(); - world.getChunkAt(loc); - final int x = loc.getBlockX(), z = loc.getBlockZ(); - int y = loc.getBlockY(); - boolean lower = world.getBlockAt(x, y, z).isEmpty(), upper = world.getBlockAt(x, y + 1, z).isEmpty(); - while ((!lower || !upper) && y != world.getMaxHeight()) { - lower = upper; - upper = world.getBlockAt(x, ++y, z).isEmpty(); - } - while (world.getBlockAt(x, y - 1, z).isEmpty() && y != world.getMinHeight()) { - y--; - } - return y; - } - - public static int modifyContainer(BlockState b, ItemStack item, boolean remove) { - if (b instanceof InventoryHolder) { - final Inventory inv = ((InventoryHolder) b).getInventory(); - if (remove) { - final ItemStack tmp = inv.removeItem(item).get(0); - return tmp != null ? tmp.getAmount() : 0; - } else if (item.getAmount() > 0) { - final ItemStack tmp = inv.addItem(item).get(0); - return tmp != null ? tmp.getAmount() : 0; - } - } - return 0; - } - - public static boolean canFallIn(World world, int x, int y, int z) { - Block block = world.getBlockAt(x, y, z); - Material mat = block.getType(); - if (canDirectlyFallIn(mat)) { - return true; - } else if (getFallingEntityKillers().contains(mat) || singleBlockPlants.contains(mat) || mat == Material.VINE) { - if (slabs.contains(mat)) { - if (((Slab) block.getBlockData()).getType() != Type.BOTTOM) { - return false; - } - } - return true; - } - return false; - } - - public static boolean canDirectlyFallIn(Material m) { - return isEmpty(m) || m == Material.WATER || m == Material.LAVA || m == Material.FIRE; - } - - public static Material itemIDfromProjectileEntity(Entity e) { - return projectileItems.get(e.getType()); - } - - public static boolean isDoublePlant(Material m) { - return doublePlants.contains(m); - } - - public static boolean isWoodenDoor(Material m) { - return woodenDoors.contains(m); - } - - public static boolean isButton(Material m) { - return buttons.contains(m); - } - - public static boolean isEmpty(Material m) { - return m == Material.AIR || m == Material.CAVE_AIR || m == Material.VOID_AIR; - } - - public static TextComponent toString(ItemStack stack) { - if (stack == null || stack.getAmount() == 0 || isEmpty(stack.getType())) { - return prettyMaterial("nothing"); - } - TextComponent msg = MessagingUtil.createTextComponentWithColor(stack.getAmount() + "x ", TypeColor.DEFAULT.getColor()); - msg.addExtra(prettyMaterial(stack.getType())); - - try { - String itemTag = getItemTag(stack); - msg.setHoverEvent(new HoverEvent(Action.SHOW_ITEM, new Item(stack.getType().getKey().toString(), 1, itemTag != null ? ItemTag.ofNbt(itemTag) : null))); - } catch (Exception e) { - LogBlock.getInstance().getLogger().log(Level.SEVERE, "Failed to convert Itemstack to JSON", e); - msg.setHoverEvent(new HoverEvent(Action.SHOW_TEXT, new Text(new BaseComponent[] { MessagingUtil.createTextComponentWithColor("Error", TypeColor.ERROR.getColor()) }))); - } - - return msg; - } - - public static String getItemTag(ItemStack itemStack) throws ReflectiveOperationException { - Class craftItemStackClazz = ReflectionUtil.getCraftBukkitClass("inventory.CraftItemStack"); - Method asNMSCopyMethod = craftItemStackClazz.getMethod("asNMSCopy", ItemStack.class); - - Class nmsItemStackClazz = ReflectionUtil.getMinecraftClass("world.item.ItemStack"); - Method getTagMethod = nmsItemStackClazz.getDeclaredMethod("getTagClone"); - getTagMethod.setAccessible(true); - - Object nmsItemStack = asNMSCopyMethod.invoke(null, itemStack); - Object itemTag = getTagMethod.invoke(nmsItemStack); - - return itemTag != null ? itemTag.toString() : null; - } - - public static String formatMinecraftKey(String s) { - char[] cap = s.toCharArray(); - boolean lastSpace = true; - for (int i = 0; i < cap.length; i++) { - char c = cap[i]; - if (c == '_') { - c = ' '; - lastSpace = true; - } else if (c >= '0' && c <= '9' || c == '(' || c == ')') { - lastSpace = true; - } else { - if (lastSpace) { - c = Character.toUpperCase(c); - } else { - c = Character.toLowerCase(c); - } - lastSpace = false; - } - cap[i] = c; - } - return new String(cap); - } - - public static boolean isBed(Material type) { - return bedBlocks.contains(type); - } - - public static boolean isDye(Material type) { - return dyes.containsKey(type); - } - - public static DyeColor dyeToDyeColor(Material type) { - return dyes.get(type); - } - - public static Block getConnectedChest(Block chestBlock) { - // is this a chest? - BlockData blockData = chestBlock.getBlockData(); - if (!(blockData instanceof org.bukkit.block.data.type.Chest)) { - return null; - } - // so check if is should have a neighbour - org.bukkit.block.data.type.Chest chestData = (org.bukkit.block.data.type.Chest) blockData; - org.bukkit.block.data.type.Chest.Type chestType = chestData.getType(); - if (chestType != org.bukkit.block.data.type.Chest.Type.SINGLE) { - // check if the neighbour exists - BlockFace chestFace = chestData.getFacing(); - BlockFace faceToSecondChest; - if (chestFace == BlockFace.WEST) { - faceToSecondChest = BlockFace.NORTH; - } else if (chestFace == BlockFace.NORTH) { - faceToSecondChest = BlockFace.EAST; - } else if (chestFace == BlockFace.EAST) { - faceToSecondChest = BlockFace.SOUTH; - } else if (chestFace == BlockFace.SOUTH) { - faceToSecondChest = BlockFace.WEST; - } else { - return null; - } - org.bukkit.block.data.type.Chest.Type wantedChestType = org.bukkit.block.data.type.Chest.Type.RIGHT; - if (chestType == org.bukkit.block.data.type.Chest.Type.RIGHT) { - faceToSecondChest = faceToSecondChest.getOppositeFace(); - wantedChestType = org.bukkit.block.data.type.Chest.Type.LEFT; - } - Block face = chestBlock.getRelative(faceToSecondChest); - if (face.getType() == chestBlock.getType()) { - // check is the neighbour connects to this chest - org.bukkit.block.data.type.Chest otherChestData = (org.bukkit.block.data.type.Chest) face.getBlockData(); - if (otherChestData.getType() != wantedChestType || otherChestData.getFacing() != chestFace) { - return null; - } - return face; - } - } - return null; - } - - public static Entity loadEntityAround(Chunk chunk, UUID uuid) { - Entity e = Bukkit.getEntity(uuid); - if (e != null) { - return e; - } - if (!chunk.isLoaded()) { - chunk.getWorld().getChunkAt(chunk.getX(), chunk.getZ()); - e = Bukkit.getEntity(uuid); - if (e != null) { - return e; - } - } - int chunkx = chunk.getX(); - int chunkz = chunk.getZ(); - for (int i = 0; i < 8; i++) { - int x = i < 3 ? chunkx - 1 : (i < 5 ? chunkx : chunkx + 1); - int z = i == 0 || i == 3 || i == 5 ? chunkz - 1 : (i == 1 || i == 6 ? chunkz : chunkz + 1); - if (!chunk.getWorld().isChunkLoaded(x, z)) { - chunk.getWorld().getChunkAt(x, z); - e = Bukkit.getEntity(uuid); - if (e != null) { - return e; - } - } - } - return null; - } - - private static final HashMap types = new HashMap<>(); - static { - for (EntityType t : EntityType.values()) { - if (t != EntityType.UNKNOWN) { - types.put(t.name().toLowerCase(), t); - @SuppressWarnings("deprecation") - String typeName = t.getName(); - if (typeName != null) { - types.put(typeName.toLowerCase(), t); - } - Class ec = t.getEntityClass(); - if (ec != null) { - types.put(ec.getSimpleName().toLowerCase(), t); - } - types.put(t.getKey().getKey(), t); - types.put(t.getKey().toString(), t); - } - } - } - - public static EntityType matchEntityType(String typeName) { - return types.get(typeName.toLowerCase()); - } - - public static ItemStack getItemInSlot(ArmorStand stand, EquipmentSlot slot) { - if (slot == EquipmentSlot.HAND) { - return stand.getEquipment().getItemInMainHand(); - } else if (slot == EquipmentSlot.OFF_HAND) { - return stand.getEquipment().getItemInOffHand(); - } else if (slot == EquipmentSlot.FEET) { - return stand.getEquipment().getBoots(); - } else if (slot == EquipmentSlot.LEGS) { - return stand.getEquipment().getLeggings(); - } else if (slot == EquipmentSlot.CHEST) { - return stand.getEquipment().getChestplate(); - } else if (slot == EquipmentSlot.HEAD) { - return stand.getEquipment().getHelmet(); - } - return null; - } - - public static void setItemInSlot(ArmorStand stand, EquipmentSlot slot, ItemStack stack) { - if (slot == EquipmentSlot.HAND) { - stand.getEquipment().setItemInMainHand(stack); - } else if (slot == EquipmentSlot.OFF_HAND) { - stand.getEquipment().setItemInOffHand(stack); - } else if (slot == EquipmentSlot.FEET) { - stand.getEquipment().setBoots(stack); - } else if (slot == EquipmentSlot.LEGS) { - stand.getEquipment().setLeggings(stack); - } else if (slot == EquipmentSlot.CHEST) { - stand.getEquipment().setChestplate(stack); - } else if (slot == EquipmentSlot.HEAD) { - stand.getEquipment().setHelmet(stack); - } - } - - public static ItemStack[] deepCopy(ItemStack[] of) { - ItemStack[] result = new ItemStack[of.length]; - for (int i = 0; i < result.length; i++) { - result[i] = of[i] == null ? null : new ItemStack(of[i]); - } - return result; - } - - private static int getFirstPartialItemStack(ItemStack item, ItemStack[] contents, int start) { - for (int i = start; i < contents.length; i++) { - ItemStack content = contents[i]; - if (content != null && content.isSimilar(item) && content.getAmount() < content.getMaxStackSize()) { - return i; - } - } - return -1; - } - - private static int getFirstFreeItemStack(ItemStack[] contents, int start) { - for (int i = start; i < contents.length; i++) { - ItemStack content = contents[i]; - if (content == null || content.getAmount() == 0 || content.getType() == Material.AIR) { - return i; - } - } - return -1; - } - - public static boolean hasInventoryStorageSpaceFor(Inventory inv, ItemStack... items) { - ItemStack[] contents = deepCopy(inv.getStorageContents()); - for (ItemStack item : items) { - if (item != null && item.getType() != Material.AIR) { - int remaining = item.getAmount(); - // fill partial stacks - int firstPartial = -1; - while (remaining > 0) { - firstPartial = getFirstPartialItemStack(item, contents, firstPartial + 1); - if (firstPartial < 0) { - break; - } - ItemStack content = contents[firstPartial]; - int add = Math.min(content.getMaxStackSize() - content.getAmount(), remaining); - content.setAmount(content.getAmount() + add); - remaining -= add; - } - // create new stacks - int firstFree = -1; - while (remaining > 0) { - firstFree = getFirstFreeItemStack(contents, firstFree + 1); - if (firstFree < 0) { - return false; // no free place found - } - ItemStack content = new ItemStack(item); - contents[firstFree] = content; - // max stack size might return -1, in this case assume 1 - int add = Math.min(Math.max(content.getMaxStackSize(), 1), remaining); - content.setAmount(add); - remaining -= add; - } - } - } - return true; - } - - public static boolean isSimilarForRollback(Material expected, Material found) { - if (expected == found) { - return true; - } - if (expected == Material.DIRT || expected == Material.MYCELIUM || expected == Material.FARMLAND || expected == Material.GRASS_BLOCK || expected == Material.PODZOL || expected == Material.DIRT_PATH) { - return found == Material.DIRT || found == Material.MYCELIUM || found == Material.FARMLAND || found == Material.GRASS_BLOCK || found == Material.PODZOL || found == Material.DIRT_PATH; - } - if (expected == Material.BAMBOO || expected == Material.BAMBOO_SAPLING) { - return found == Material.BAMBOO || found == Material.BAMBOO_SAPLING; - } - if (expected == Material.SPONGE || expected == Material.WET_SPONGE) { - return found == Material.SPONGE || found == Material.WET_SPONGE; - } - if (expected == Material.MELON_STEM || expected == Material.ATTACHED_MELON_STEM) { - return found == Material.MELON_STEM || found == Material.ATTACHED_MELON_STEM; - } - if (expected == Material.PUMPKIN_STEM || expected == Material.ATTACHED_PUMPKIN_STEM) { - return found == Material.PUMPKIN_STEM || found == Material.ATTACHED_PUMPKIN_STEM; - } - if (expected == Material.TWISTING_VINES || expected == Material.TWISTING_VINES_PLANT) { - return found == Material.TWISTING_VINES || found == Material.TWISTING_VINES_PLANT; - } - if (expected == Material.WEEPING_VINES || expected == Material.WEEPING_VINES_PLANT) { - return found == Material.WEEPING_VINES || found == Material.WEEPING_VINES_PLANT; - } - if (expected == Material.CAVE_VINES || expected == Material.CAVE_VINES_PLANT) { - return found == Material.CAVE_VINES || found == Material.CAVE_VINES_PLANT; - } - if (expected == Material.BIG_DRIPLEAF || expected == Material.BIG_DRIPLEAF_STEM) { - return found == Material.BIG_DRIPLEAF || found == Material.BIG_DRIPLEAF_STEM; - } - if (expected == Material.COPPER_BLOCK || expected == Material.EXPOSED_COPPER || expected == Material.WEATHERED_COPPER || expected == Material.OXIDIZED_COPPER) { - return found == Material.COPPER_BLOCK || found == Material.EXPOSED_COPPER || found == Material.WEATHERED_COPPER || found == Material.OXIDIZED_COPPER; - } - if (expected == Material.CUT_COPPER || expected == Material.EXPOSED_CUT_COPPER || expected == Material.WEATHERED_CUT_COPPER || expected == Material.OXIDIZED_CUT_COPPER) { - return found == Material.CUT_COPPER || found == Material.EXPOSED_CUT_COPPER || found == Material.WEATHERED_CUT_COPPER || found == Material.OXIDIZED_CUT_COPPER; - } - if (expected == Material.CUT_COPPER_STAIRS || expected == Material.EXPOSED_CUT_COPPER_STAIRS || expected == Material.WEATHERED_CUT_COPPER_STAIRS || expected == Material.OXIDIZED_CUT_COPPER_STAIRS) { - return found == Material.CUT_COPPER_STAIRS || found == Material.EXPOSED_CUT_COPPER_STAIRS || found == Material.WEATHERED_CUT_COPPER_STAIRS || found == Material.OXIDIZED_CUT_COPPER_STAIRS; - } - if (expected == Material.CUT_COPPER_SLAB || expected == Material.EXPOSED_CUT_COPPER_SLAB || expected == Material.WEATHERED_CUT_COPPER_SLAB || expected == Material.OXIDIZED_CUT_COPPER_SLAB) { - return found == Material.CUT_COPPER_SLAB || found == Material.EXPOSED_CUT_COPPER_SLAB || found == Material.WEATHERED_CUT_COPPER_SLAB || found == Material.OXIDIZED_CUT_COPPER_SLAB; - } - return false; - } - - public static Set getAllSignMaterials() { - return unmodifiableSigns; - } - - public static boolean isAlwaysWaterlogged(Material m) { - return alwaysWaterlogged.contains(m); - } - - public static boolean isCandle(Material m) { - return candles.contains(m); - } - - public static boolean isCandleCake(Material m) { - return candleCakes.contains(m); - } - - public static boolean isHangingSign(Material m) { - return hangingSigns.contains(m); - } - - public static boolean isFenceGate(Material m) { - return fenceGates.contains(m); - } - - public static boolean isWoodenTrapdoor(Material m) { - return woodenTrapdoors.contains(m); - } - - public static boolean isPressurePlate(Material m) { - return pressurePlates.contains(m); - } - - public static boolean isSign(Material m) { - return allSigns.contains(m); - } - - public static Side getFacingSignSide(Entity entity, Block sign) { - BlockData data = sign.getBlockData(); - Material type = data.getMaterial(); - BlockFace signFace = null; - double centerx = 0.5; - double centerz = 0.5; - double yRotationDegree = 0; - if (type.data == Sign.class || type.data == HangingSign.class) { - Rotatable rotatableData = (Rotatable) data; - signFace = rotatableData.getRotation(); - if (signFace == BlockFace.SOUTH) { - yRotationDegree = 360 * 0.0 / 16.0; - } else if (signFace == BlockFace.SOUTH_SOUTH_WEST) { - yRotationDegree = 360 * 1.0 / 16.0; - } else if (signFace == BlockFace.SOUTH_WEST) { - yRotationDegree = 360 * 2.0 / 16.0; - } else if (signFace == BlockFace.WEST_SOUTH_WEST) { - yRotationDegree = 360 * 3.0 / 16.0; - } else if (signFace == BlockFace.WEST) { - yRotationDegree = 360 * 4.0 / 16.0; - } else if (signFace == BlockFace.WEST_NORTH_WEST) { - yRotationDegree = 360 * 5.0 / 16.0; - } else if (signFace == BlockFace.NORTH_WEST) { - yRotationDegree = 360 * 6.0 / 16.0; - } else if (signFace == BlockFace.NORTH_NORTH_WEST) { - yRotationDegree = 360 * 7.0 / 16.0; - } else if (signFace == BlockFace.NORTH) { - yRotationDegree = 360 * 8.0 / 16.0; - } else if (signFace == BlockFace.NORTH_NORTH_EAST) { - yRotationDegree = 360 * 9.0 / 16.0; - } else if (signFace == BlockFace.NORTH_EAST) { - yRotationDegree = 360 * 10.0 / 16.0; - } else if (signFace == BlockFace.EAST_NORTH_EAST) { - yRotationDegree = 360 * 11.0 / 16.0; - } else if (signFace == BlockFace.EAST) { - yRotationDegree = 360 * 12.0 / 16.0; - } else if (signFace == BlockFace.EAST_SOUTH_EAST) { - yRotationDegree = 360 * 13.0 / 16.0; - } else if (signFace == BlockFace.SOUTH_EAST) { - yRotationDegree = 360 * 14.0 / 16.0; - } else if (signFace == BlockFace.SOUTH_SOUTH_EAST) { - yRotationDegree = 360 * 15.0 / 16.0; - } - } else if (type.data == WallSign.class || type.data == WallHangingSign.class) { - Directional directionalData = (Directional) data; - signFace = directionalData.getFacing(); - if (signFace == BlockFace.SOUTH) { - yRotationDegree = 0; - } else if (signFace == BlockFace.WEST) { - yRotationDegree = 90; - } else if (signFace == BlockFace.NORTH) { - yRotationDegree = 180; - } else if (signFace == BlockFace.EAST) { - yRotationDegree = 270; - } - // wall signs are not centered on the block (but hanging wall signs are) - if (type.data == WallSign.class) { - if (signFace == BlockFace.NORTH) { - centerz = 15.0 / 16.0; - } else if (signFace == BlockFace.SOUTH) { - centerz = 1.0 / 16.0; - } else if (signFace == BlockFace.WEST) { - centerx = 15.0 / 16.0; - } else if (signFace == BlockFace.EAST) { - centerx = 1.0 / 16.0; - } - } - } else { - throw new IllegalArgumentException("block is not a sign"); - } - - Location entityLoc = entity.getLocation(); - double relativeX = entityLoc.getX() - (sign.getX() + centerx); - double relativeZ = entityLoc.getZ() - (sign.getZ() + centerz); - double f = Math.atan2(relativeZ, relativeX) * 180.0 / Math.PI - 90.0; - - return Math.abs(Utils.warpDegrees(f - yRotationDegree)) <= 90.0 ? Side.FRONT : Side.BACK; - } -} +package de.diddiz.LogBlock.util; + +import static de.diddiz.LogBlock.util.MessagingUtil.prettyMaterial; + +import de.diddiz.LogBlock.LogBlock; +import java.io.File; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.Set; +import java.util.UUID; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.HoverEvent; +import net.md_5.bungee.api.chat.HoverEvent.Action; +import net.md_5.bungee.api.chat.ItemTag; +import net.md_5.bungee.api.chat.TextComponent; +import net.md_5.bungee.api.chat.hover.content.Item; +import net.md_5.bungee.api.chat.hover.content.Text; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Chunk; +import org.bukkit.DyeColor; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.BlockState; +import org.bukkit.block.DoubleChest; +import org.bukkit.block.data.Bisected; +import org.bukkit.block.data.Bisected.Half; +import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.Directional; +import org.bukkit.block.data.Rotatable; +import org.bukkit.block.data.type.HangingSign; +import org.bukkit.block.data.type.Sign; +import org.bukkit.block.data.type.Slab; +import org.bukkit.block.data.type.Slab.Type; +import org.bukkit.block.data.type.Stairs; +import org.bukkit.block.data.type.WallHangingSign; +import org.bukkit.block.data.type.WallSign; +import org.bukkit.block.sign.Side; +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.entity.TNTPrimed; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.ItemStack; + +public class BukkitUtils { + private static final Set> blockEquivalents; + private static final Set relativeBreakable; + private static final Set relativeTopBreakable; + private static final Set fallingEntityKillers; + + private static final Set cropBlocks; + private static final Set containerBlocks; + private static final Set shulkerBoxBlocks; + + private static final Set singleBlockPlants; + private static final Set doublePlants; + + private static final Set nonFluidProofBlocks; + + private static final Set bedBlocks; + + private static final Map projectileItems; + private static final HashSet signs; + private static final HashSet wallSigns; + private static final HashSet hangingSigns; + private static final HashSet hangingWallSigns; + private static final HashSet allSigns; + private static final Set unmodifiableSigns; + private static final HashSet buttons; + private static final HashSet pressurePlates; + private static final HashSet woodenDoors; + private static final HashSet slabs; + private static final HashSet concreteBlocks; + private static final HashMap dyes; + private static final HashSet alwaysWaterlogged; + private static final HashSet candles; + private static final HashSet candleCakes; + private static final HashSet fenceGates; + private static final HashSet woodenTrapdoors; + + static { + fenceGates = new HashSet<>(); + fenceGates.add(Material.OAK_FENCE_GATE); + fenceGates.add(Material.SPRUCE_FENCE_GATE); + fenceGates.add(Material.BIRCH_FENCE_GATE); + fenceGates.add(Material.JUNGLE_FENCE_GATE); + fenceGates.add(Material.ACACIA_FENCE_GATE); + fenceGates.add(Material.DARK_OAK_FENCE_GATE); + fenceGates.add(Material.WARPED_FENCE_GATE); + fenceGates.add(Material.CRIMSON_FENCE_GATE); + fenceGates.add(Material.MANGROVE_FENCE_GATE); + fenceGates.add(Material.BAMBOO_FENCE_GATE); + fenceGates.add(Material.CHERRY_FENCE_GATE); + + woodenTrapdoors = new HashSet<>(); + woodenTrapdoors.add(Material.OAK_TRAPDOOR); + woodenTrapdoors.add(Material.SPRUCE_TRAPDOOR); + woodenTrapdoors.add(Material.BIRCH_TRAPDOOR); + woodenTrapdoors.add(Material.JUNGLE_TRAPDOOR); + woodenTrapdoors.add(Material.ACACIA_TRAPDOOR); + woodenTrapdoors.add(Material.DARK_OAK_TRAPDOOR); + woodenTrapdoors.add(Material.WARPED_TRAPDOOR); + woodenTrapdoors.add(Material.CRIMSON_TRAPDOOR); + woodenTrapdoors.add(Material.MANGROVE_TRAPDOOR); + woodenTrapdoors.add(Material.BAMBOO_TRAPDOOR); + woodenTrapdoors.add(Material.CHERRY_TRAPDOOR); + + pressurePlates = new HashSet<>(); + pressurePlates.add(Material.OAK_PRESSURE_PLATE); + pressurePlates.add(Material.SPRUCE_PRESSURE_PLATE); + pressurePlates.add(Material.BIRCH_PRESSURE_PLATE); + pressurePlates.add(Material.JUNGLE_PRESSURE_PLATE); + pressurePlates.add(Material.ACACIA_PRESSURE_PLATE); + pressurePlates.add(Material.DARK_OAK_PRESSURE_PLATE); + pressurePlates.add(Material.WARPED_PRESSURE_PLATE); + pressurePlates.add(Material.CRIMSON_PRESSURE_PLATE); + pressurePlates.add(Material.MANGROVE_PRESSURE_PLATE); + pressurePlates.add(Material.BAMBOO_PRESSURE_PLATE); + pressurePlates.add(Material.CHERRY_PRESSURE_PLATE); + pressurePlates.add(Material.STONE_PRESSURE_PLATE); + pressurePlates.add(Material.LIGHT_WEIGHTED_PRESSURE_PLATE); + pressurePlates.add(Material.HEAVY_WEIGHTED_PRESSURE_PLATE); + + woodenDoors = new HashSet<>(); + woodenDoors.add(Material.OAK_DOOR); + woodenDoors.add(Material.SPRUCE_DOOR); + woodenDoors.add(Material.BIRCH_DOOR); + woodenDoors.add(Material.JUNGLE_DOOR); + woodenDoors.add(Material.ACACIA_DOOR); + woodenDoors.add(Material.DARK_OAK_DOOR); + woodenDoors.add(Material.WARPED_DOOR); + woodenDoors.add(Material.CRIMSON_DOOR); + woodenDoors.add(Material.MANGROVE_DOOR); + woodenDoors.add(Material.BAMBOO_DOOR); + woodenDoors.add(Material.CHERRY_DOOR); + + HashSet saplings = new HashSet<>(); + saplings.add(Material.OAK_SAPLING); + saplings.add(Material.SPRUCE_SAPLING); + saplings.add(Material.BIRCH_SAPLING); + saplings.add(Material.JUNGLE_SAPLING); + saplings.add(Material.ACACIA_SAPLING); + saplings.add(Material.DARK_OAK_SAPLING); + saplings.add(Material.CHERRY_SAPLING); + saplings.add(Material.WARPED_FUNGUS); + saplings.add(Material.CRIMSON_FUNGUS); + saplings.add(Material.MANGROVE_PROPAGULE); + + HashSet carpets = new HashSet<>(); + carpets.add(Material.BLACK_CARPET); + carpets.add(Material.BLUE_CARPET); + carpets.add(Material.LIGHT_GRAY_CARPET); + carpets.add(Material.BROWN_CARPET); + carpets.add(Material.CYAN_CARPET); + carpets.add(Material.GRAY_CARPET); + carpets.add(Material.GREEN_CARPET); + carpets.add(Material.LIGHT_BLUE_CARPET); + carpets.add(Material.MAGENTA_CARPET); + carpets.add(Material.LIME_CARPET); + carpets.add(Material.ORANGE_CARPET); + carpets.add(Material.PINK_CARPET); + carpets.add(Material.PURPLE_CARPET); + carpets.add(Material.RED_CARPET); + carpets.add(Material.WHITE_CARPET); + carpets.add(Material.YELLOW_CARPET); + + slabs = new HashSet<>(); + slabs.add(Material.OAK_SLAB); + slabs.add(Material.SPRUCE_SLAB); + slabs.add(Material.BIRCH_SLAB); + slabs.add(Material.JUNGLE_SLAB); + slabs.add(Material.ACACIA_SLAB); + slabs.add(Material.DARK_OAK_SLAB); + slabs.add(Material.WARPED_SLAB); + slabs.add(Material.CRIMSON_SLAB); + slabs.add(Material.STONE_SLAB); + slabs.add(Material.STONE_BRICK_SLAB); + slabs.add(Material.COBBLESTONE_SLAB); + slabs.add(Material.PETRIFIED_OAK_SLAB); + slabs.add(Material.SANDSTONE_SLAB); + slabs.add(Material.RED_SANDSTONE_SLAB); + slabs.add(Material.NETHER_BRICK_SLAB); + slabs.add(Material.PURPUR_SLAB); + slabs.add(Material.QUARTZ_SLAB); + slabs.add(Material.BRICK_SLAB); + slabs.add(Material.PRISMARINE_SLAB); + slabs.add(Material.DARK_PRISMARINE_SLAB); + slabs.add(Material.PRISMARINE_BRICK_SLAB); + slabs.add(Material.BLACKSTONE_SLAB); + slabs.add(Material.POLISHED_BLACKSTONE_SLAB); + slabs.add(Material.DEEPSLATE_BRICK_SLAB); + slabs.add(Material.DEEPSLATE_TILE_SLAB); + slabs.add(Material.COBBLED_DEEPSLATE_SLAB); + slabs.add(Material.POLISHED_DEEPSLATE_SLAB); + slabs.add(Material.MANGROVE_SLAB); + slabs.add(Material.BAMBOO_SLAB); + slabs.add(Material.CHERRY_SLAB); + + buttons = new HashSet<>(); + buttons.add(Material.STONE_BUTTON); + buttons.add(Material.OAK_BUTTON); + buttons.add(Material.SPRUCE_BUTTON); + buttons.add(Material.BIRCH_BUTTON); + buttons.add(Material.JUNGLE_BUTTON); + buttons.add(Material.ACACIA_BUTTON); + buttons.add(Material.DARK_OAK_BUTTON); + buttons.add(Material.WARPED_BUTTON); + buttons.add(Material.CRIMSON_BUTTON); + buttons.add(Material.MANGROVE_BUTTON); + buttons.add(Material.BAMBOO_BUTTON); + buttons.add(Material.CHERRY_BUTTON); + buttons.add(Material.POLISHED_BLACKSTONE_BUTTON); + + signs = new HashSet<>(); + signs.add(Material.OAK_SIGN); + signs.add(Material.SPRUCE_SIGN); + signs.add(Material.BIRCH_SIGN); + signs.add(Material.JUNGLE_SIGN); + signs.add(Material.DARK_OAK_SIGN); + signs.add(Material.ACACIA_SIGN); + signs.add(Material.WARPED_SIGN); + signs.add(Material.CRIMSON_SIGN); + signs.add(Material.MANGROVE_SIGN); + signs.add(Material.BAMBOO_SIGN); + signs.add(Material.CHERRY_SIGN); + + wallSigns = new HashSet<>(); + wallSigns.add(Material.OAK_WALL_SIGN); + wallSigns.add(Material.SPRUCE_WALL_SIGN); + wallSigns.add(Material.BIRCH_WALL_SIGN); + wallSigns.add(Material.JUNGLE_WALL_SIGN); + wallSigns.add(Material.DARK_OAK_WALL_SIGN); + wallSigns.add(Material.ACACIA_WALL_SIGN); + wallSigns.add(Material.WARPED_WALL_SIGN); + wallSigns.add(Material.CRIMSON_WALL_SIGN); + wallSigns.add(Material.MANGROVE_WALL_SIGN); + wallSigns.add(Material.BAMBOO_WALL_SIGN); + wallSigns.add(Material.CHERRY_WALL_SIGN); + + hangingSigns = new HashSet<>(); + hangingSigns.add(Material.OAK_HANGING_SIGN); + hangingSigns.add(Material.SPRUCE_HANGING_SIGN); + hangingSigns.add(Material.BIRCH_HANGING_SIGN); + hangingSigns.add(Material.JUNGLE_HANGING_SIGN); + hangingSigns.add(Material.DARK_OAK_HANGING_SIGN); + hangingSigns.add(Material.ACACIA_HANGING_SIGN); + hangingSigns.add(Material.WARPED_HANGING_SIGN); + hangingSigns.add(Material.CRIMSON_HANGING_SIGN); + hangingSigns.add(Material.MANGROVE_HANGING_SIGN); + hangingSigns.add(Material.BAMBOO_HANGING_SIGN); + hangingSigns.add(Material.CHERRY_HANGING_SIGN); + + hangingWallSigns = new HashSet<>(); + hangingWallSigns.add(Material.OAK_WALL_HANGING_SIGN); + hangingWallSigns.add(Material.SPRUCE_WALL_HANGING_SIGN); + hangingWallSigns.add(Material.BIRCH_WALL_HANGING_SIGN); + hangingWallSigns.add(Material.JUNGLE_WALL_HANGING_SIGN); + hangingWallSigns.add(Material.DARK_OAK_WALL_HANGING_SIGN); + hangingWallSigns.add(Material.ACACIA_WALL_HANGING_SIGN); + hangingWallSigns.add(Material.WARPED_WALL_HANGING_SIGN); + hangingWallSigns.add(Material.CRIMSON_WALL_HANGING_SIGN); + hangingWallSigns.add(Material.MANGROVE_WALL_HANGING_SIGN); + hangingWallSigns.add(Material.BAMBOO_WALL_HANGING_SIGN); + hangingWallSigns.add(Material.CHERRY_WALL_HANGING_SIGN); + + allSigns = new HashSet<>(); + allSigns.addAll(signs); + allSigns.addAll(wallSigns); + allSigns.addAll(hangingSigns); + allSigns.addAll(hangingWallSigns); + unmodifiableSigns = Collections.unmodifiableSet(allSigns); + + singleBlockPlants = new HashSet<>(); + singleBlockPlants.add(Material.GRASS); + singleBlockPlants.add(Material.FERN); + singleBlockPlants.add(Material.DEAD_BUSH); + singleBlockPlants.add(Material.DANDELION); + singleBlockPlants.add(Material.POPPY); + singleBlockPlants.add(Material.BLUE_ORCHID); + singleBlockPlants.add(Material.ALLIUM); + singleBlockPlants.add(Material.AZURE_BLUET); + singleBlockPlants.add(Material.ORANGE_TULIP); + singleBlockPlants.add(Material.WHITE_TULIP); + singleBlockPlants.add(Material.PINK_TULIP); + singleBlockPlants.add(Material.RED_TULIP); + singleBlockPlants.add(Material.OXEYE_DAISY); + singleBlockPlants.add(Material.BROWN_MUSHROOM); + singleBlockPlants.add(Material.RED_MUSHROOM); + singleBlockPlants.add(Material.SWEET_BERRY_BUSH); + singleBlockPlants.add(Material.LILY_OF_THE_VALLEY); + singleBlockPlants.add(Material.CORNFLOWER); + singleBlockPlants.add(Material.WITHER_ROSE); + singleBlockPlants.add(Material.CRIMSON_FUNGUS); + singleBlockPlants.add(Material.WARPED_FUNGUS); + singleBlockPlants.add(Material.CRIMSON_ROOTS); + singleBlockPlants.add(Material.WARPED_ROOTS); + singleBlockPlants.add(Material.NETHER_SPROUTS); + singleBlockPlants.add(Material.AZALEA); + singleBlockPlants.add(Material.FLOWERING_AZALEA); + singleBlockPlants.add(Material.PINK_PETALS); + singleBlockPlants.add(Material.TORCHFLOWER); + singleBlockPlants.add(Material.PITCHER_CROP); + + doublePlants = new HashSet<>(); + doublePlants.add(Material.TALL_GRASS); + doublePlants.add(Material.LARGE_FERN); + doublePlants.add(Material.TALL_SEAGRASS); + doublePlants.add(Material.ROSE_BUSH); + doublePlants.add(Material.LILAC); + doublePlants.add(Material.SUNFLOWER); + doublePlants.add(Material.PEONY); + doublePlants.add(Material.SMALL_DRIPLEAF); + doublePlants.add(Material.PITCHER_PLANT); + + blockEquivalents = new HashSet<>(7); + blockEquivalents.add(new HashSet<>(Arrays.asList(2, 3, 60))); + blockEquivalents.add(new HashSet<>(Arrays.asList(8, 9, 79))); + blockEquivalents.add(new HashSet<>(Arrays.asList(10, 11))); + blockEquivalents.add(new HashSet<>(Arrays.asList(61, 62))); + blockEquivalents.add(new HashSet<>(Arrays.asList(73, 74))); + blockEquivalents.add(new HashSet<>(Arrays.asList(75, 76))); + blockEquivalents.add(new HashSet<>(Arrays.asList(93, 94))); + + // Blocks that break when they are attached to a block + relativeBreakable = new HashSet<>(); + relativeBreakable.addAll(wallSigns); + relativeBreakable.add(Material.LADDER); + relativeBreakable.addAll(buttons); + relativeBreakable.add(Material.REDSTONE_WALL_TORCH); + relativeBreakable.add(Material.LEVER); + relativeBreakable.add(Material.WALL_TORCH); + relativeBreakable.add(Material.TRIPWIRE_HOOK); + relativeBreakable.add(Material.COCOA); + relativeBreakable.add(Material.BELL); + relativeBreakable.add(Material.AMETHYST_CLUSTER); + relativeBreakable.add(Material.SMALL_AMETHYST_BUD); + relativeBreakable.add(Material.MEDIUM_AMETHYST_BUD); + relativeBreakable.add(Material.LARGE_AMETHYST_BUD); + + // Blocks that break when they are on top of a block + relativeTopBreakable = new HashSet<>(); + relativeTopBreakable.addAll(saplings); + relativeTopBreakable.addAll(singleBlockPlants); + relativeTopBreakable.add(Material.WHEAT); + relativeTopBreakable.add(Material.POTATO); + relativeTopBreakable.add(Material.CARROT); + relativeTopBreakable.add(Material.LILY_PAD); + relativeTopBreakable.add(Material.CACTUS); + relativeTopBreakable.add(Material.SUGAR_CANE); + relativeTopBreakable.add(Material.FLOWER_POT); + relativeTopBreakable.add(Material.POWERED_RAIL); + relativeTopBreakable.add(Material.DETECTOR_RAIL); + relativeTopBreakable.add(Material.ACTIVATOR_RAIL); + relativeTopBreakable.add(Material.RAIL); + relativeTopBreakable.add(Material.REDSTONE_WIRE); + relativeTopBreakable.addAll(signs); + relativeTopBreakable.addAll(pressurePlates); + relativeTopBreakable.add(Material.SNOW); + relativeTopBreakable.add(Material.REPEATER); + relativeTopBreakable.add(Material.COMPARATOR); + relativeTopBreakable.add(Material.TORCH); + relativeTopBreakable.add(Material.SOUL_TORCH); + relativeTopBreakable.add(Material.REDSTONE_TORCH); + relativeTopBreakable.addAll(woodenDoors); + relativeTopBreakable.add(Material.IRON_DOOR); + relativeTopBreakable.addAll(carpets); + relativeTopBreakable.addAll(doublePlants); + relativeTopBreakable.add(Material.BAMBOO); + relativeTopBreakable.add(Material.BAMBOO_SAPLING); + relativeTopBreakable.add(Material.TWISTING_VINES); + relativeTopBreakable.add(Material.TWISTING_VINES_PLANT); + relativeTopBreakable.add(Material.BIG_DRIPLEAF); + relativeTopBreakable.add(Material.BIG_DRIPLEAF_STEM); + for (Material m : Material.values()) { + if (m.name().startsWith("POTTED_")) { + relativeTopBreakable.add(m); + } + if (m.name().endsWith("CANDLE_CAKE")) { + relativeTopBreakable.add(m); + } + } + + // Blocks that break falling entities + fallingEntityKillers = new HashSet<>(); + fallingEntityKillers.addAll(signs); + fallingEntityKillers.addAll(wallSigns); + fallingEntityKillers.addAll(pressurePlates); + fallingEntityKillers.addAll(saplings); + fallingEntityKillers.addAll(singleBlockPlants); + fallingEntityKillers.remove(Material.GRASS); + fallingEntityKillers.remove(Material.NETHER_SPROUTS); + fallingEntityKillers.addAll(doublePlants); + fallingEntityKillers.add(Material.WHEAT); + fallingEntityKillers.add(Material.CARROT); + fallingEntityKillers.add(Material.POTATO); + fallingEntityKillers.add(Material.BEETROOT); + fallingEntityKillers.add(Material.NETHER_WART); + fallingEntityKillers.add(Material.COCOA); + fallingEntityKillers.addAll(slabs); + fallingEntityKillers.add(Material.TORCH); + fallingEntityKillers.add(Material.WALL_TORCH); + fallingEntityKillers.add(Material.SOUL_TORCH); + fallingEntityKillers.add(Material.SOUL_WALL_TORCH); + fallingEntityKillers.add(Material.FLOWER_POT); + fallingEntityKillers.add(Material.POWERED_RAIL); + fallingEntityKillers.add(Material.DETECTOR_RAIL); + fallingEntityKillers.add(Material.ACTIVATOR_RAIL); + fallingEntityKillers.add(Material.RAIL); + fallingEntityKillers.add(Material.LEVER); + fallingEntityKillers.add(Material.REDSTONE_WIRE); + fallingEntityKillers.add(Material.REDSTONE_TORCH); + fallingEntityKillers.add(Material.REDSTONE_WALL_TORCH); + fallingEntityKillers.add(Material.REPEATER); + fallingEntityKillers.add(Material.COMPARATOR); + fallingEntityKillers.add(Material.DAYLIGHT_DETECTOR); + fallingEntityKillers.addAll(carpets); + fallingEntityKillers.add(Material.PLAYER_HEAD); + fallingEntityKillers.add(Material.PLAYER_WALL_HEAD); + fallingEntityKillers.add(Material.CREEPER_HEAD); + fallingEntityKillers.add(Material.CREEPER_WALL_HEAD); + fallingEntityKillers.add(Material.DRAGON_HEAD); + fallingEntityKillers.add(Material.DRAGON_WALL_HEAD); + fallingEntityKillers.add(Material.ZOMBIE_HEAD); + fallingEntityKillers.add(Material.ZOMBIE_WALL_HEAD); + fallingEntityKillers.add(Material.SKELETON_SKULL); + fallingEntityKillers.add(Material.SKELETON_WALL_SKULL); + fallingEntityKillers.add(Material.WITHER_SKELETON_SKULL); + fallingEntityKillers.add(Material.WITHER_SKELETON_WALL_SKULL); + for (Material m : Material.values()) { + if (m.name().contains("CANDLE")) { + fallingEntityKillers.add(m); + } + } + + // Crop Blocks + cropBlocks = new HashSet<>(); + cropBlocks.add(Material.WHEAT); + cropBlocks.add(Material.MELON_STEM); + cropBlocks.add(Material.PUMPKIN_STEM); + cropBlocks.add(Material.CARROT); + cropBlocks.add(Material.POTATO); + cropBlocks.add(Material.BEETROOT); + cropBlocks.add(Material.TORCHFLOWER_CROP); + + // Shulker Boxes + shulkerBoxBlocks = new HashSet<>(); + shulkerBoxBlocks.add(Material.SHULKER_BOX); + shulkerBoxBlocks.add(Material.BLACK_SHULKER_BOX); + shulkerBoxBlocks.add(Material.BLUE_SHULKER_BOX); + shulkerBoxBlocks.add(Material.LIGHT_GRAY_SHULKER_BOX); + shulkerBoxBlocks.add(Material.BROWN_SHULKER_BOX); + shulkerBoxBlocks.add(Material.CYAN_SHULKER_BOX); + shulkerBoxBlocks.add(Material.GRAY_SHULKER_BOX); + shulkerBoxBlocks.add(Material.GREEN_SHULKER_BOX); + shulkerBoxBlocks.add(Material.LIGHT_BLUE_SHULKER_BOX); + shulkerBoxBlocks.add(Material.MAGENTA_SHULKER_BOX); + shulkerBoxBlocks.add(Material.LIME_SHULKER_BOX); + shulkerBoxBlocks.add(Material.ORANGE_SHULKER_BOX); + shulkerBoxBlocks.add(Material.PINK_SHULKER_BOX); + shulkerBoxBlocks.add(Material.PURPLE_SHULKER_BOX); + shulkerBoxBlocks.add(Material.RED_SHULKER_BOX); + shulkerBoxBlocks.add(Material.WHITE_SHULKER_BOX); + shulkerBoxBlocks.add(Material.YELLOW_SHULKER_BOX); + + // Container Blocks + containerBlocks = new HashSet<>(); + containerBlocks.add(Material.CHEST); + containerBlocks.add(Material.TRAPPED_CHEST); + containerBlocks.add(Material.DISPENSER); + containerBlocks.add(Material.DROPPER); + containerBlocks.add(Material.HOPPER); + containerBlocks.add(Material.BREWING_STAND); + containerBlocks.add(Material.FURNACE); + containerBlocks.addAll(shulkerBoxBlocks); + containerBlocks.add(Material.BARREL); + containerBlocks.add(Material.BLAST_FURNACE); + containerBlocks.add(Material.SMOKER); + containerBlocks.add(Material.CHISELED_BOOKSHELF); + // Doesn't actually have a block inventory + // containerBlocks.add(Material.ENDER_CHEST); + + // It doesn't seem like you could injure people with some of these, but they exist, so.... + projectileItems = new HashMap<>(); + projectileItems.put(EntityType.ARROW, Material.ARROW); + projectileItems.put(EntityType.EGG, Material.EGG); + projectileItems.put(EntityType.ENDER_PEARL, Material.ENDER_PEARL); + projectileItems.put(EntityType.SMALL_FIREBALL, Material.FIRE_CHARGE); // Fire charge + projectileItems.put(EntityType.FIREBALL, Material.FIRE_CHARGE); // Fire charge + projectileItems.put(EntityType.FISHING_HOOK, Material.FISHING_ROD); + projectileItems.put(EntityType.SNOWBALL, Material.SNOWBALL); + projectileItems.put(EntityType.SPLASH_POTION, Material.SPLASH_POTION); + projectileItems.put(EntityType.THROWN_EXP_BOTTLE, Material.EXPERIENCE_BOTTLE); + projectileItems.put(EntityType.WITHER_SKULL, Material.WITHER_SKELETON_SKULL); + projectileItems.put(EntityType.FIREWORK, Material.FIREWORK_ROCKET); + + nonFluidProofBlocks = new HashSet<>(); + nonFluidProofBlocks.addAll(singleBlockPlants); + nonFluidProofBlocks.addAll(doublePlants); + nonFluidProofBlocks.add(Material.REDSTONE_WALL_TORCH); + nonFluidProofBlocks.add(Material.LEVER); + nonFluidProofBlocks.add(Material.WALL_TORCH); + nonFluidProofBlocks.add(Material.SOUL_WALL_TORCH); + nonFluidProofBlocks.add(Material.TRIPWIRE_HOOK); + nonFluidProofBlocks.add(Material.COCOA); + nonFluidProofBlocks.addAll(pressurePlates); + nonFluidProofBlocks.addAll(saplings); + nonFluidProofBlocks.add(Material.WHEAT); + nonFluidProofBlocks.add(Material.CARROT); + nonFluidProofBlocks.add(Material.POTATO); + nonFluidProofBlocks.add(Material.BEETROOT); + nonFluidProofBlocks.add(Material.NETHER_WART); + nonFluidProofBlocks.add(Material.TORCH); + nonFluidProofBlocks.add(Material.SOUL_TORCH); + nonFluidProofBlocks.add(Material.FLOWER_POT); + // nonFluidProofBlocks.add(Material.POWERED_RAIL); + // nonFluidProofBlocks.add(Material.DETECTOR_RAIL); + // nonFluidProofBlocks.add(Material.ACTIVATOR_RAIL); + // nonFluidProofBlocks.add(Material.RAIL); + nonFluidProofBlocks.add(Material.LEVER); + nonFluidProofBlocks.add(Material.REDSTONE_WIRE); + nonFluidProofBlocks.add(Material.REDSTONE_TORCH); + nonFluidProofBlocks.add(Material.REPEATER); + nonFluidProofBlocks.add(Material.COMPARATOR); + nonFluidProofBlocks.add(Material.DAYLIGHT_DETECTOR); + nonFluidProofBlocks.addAll(carpets); + + alwaysWaterlogged = new HashSet<>(); + alwaysWaterlogged.add(Material.SEAGRASS); + alwaysWaterlogged.add(Material.TALL_SEAGRASS); + alwaysWaterlogged.add(Material.KELP); + alwaysWaterlogged.add(Material.KELP_PLANT); + + bedBlocks = new HashSet<>(); + bedBlocks.add(Material.BLACK_BED); + bedBlocks.add(Material.BLUE_BED); + bedBlocks.add(Material.LIGHT_GRAY_BED); + bedBlocks.add(Material.BROWN_BED); + bedBlocks.add(Material.CYAN_BED); + bedBlocks.add(Material.GRAY_BED); + bedBlocks.add(Material.GREEN_BED); + bedBlocks.add(Material.LIGHT_BLUE_BED); + bedBlocks.add(Material.MAGENTA_BED); + bedBlocks.add(Material.LIME_BED); + bedBlocks.add(Material.ORANGE_BED); + bedBlocks.add(Material.PINK_BED); + bedBlocks.add(Material.PURPLE_BED); + bedBlocks.add(Material.RED_BED); + bedBlocks.add(Material.WHITE_BED); + bedBlocks.add(Material.YELLOW_BED); + + concreteBlocks = new HashSet<>(); + concreteBlocks.add(Material.BLACK_CONCRETE); + concreteBlocks.add(Material.BLUE_CONCRETE); + concreteBlocks.add(Material.LIGHT_GRAY_CONCRETE); + concreteBlocks.add(Material.BROWN_CONCRETE); + concreteBlocks.add(Material.CYAN_CONCRETE); + concreteBlocks.add(Material.GRAY_CONCRETE); + concreteBlocks.add(Material.GREEN_CONCRETE); + concreteBlocks.add(Material.LIGHT_BLUE_CONCRETE); + concreteBlocks.add(Material.MAGENTA_CONCRETE); + concreteBlocks.add(Material.LIME_CONCRETE); + concreteBlocks.add(Material.ORANGE_CONCRETE); + concreteBlocks.add(Material.PINK_CONCRETE); + concreteBlocks.add(Material.PURPLE_CONCRETE); + concreteBlocks.add(Material.RED_CONCRETE); + concreteBlocks.add(Material.WHITE_CONCRETE); + concreteBlocks.add(Material.YELLOW_CONCRETE); + + candles = new HashSet<>(); + candles.add(Material.CANDLE); + candles.add(Material.BLACK_CANDLE); + candles.add(Material.BLUE_CANDLE); + candles.add(Material.LIGHT_GRAY_CANDLE); + candles.add(Material.BROWN_CANDLE); + candles.add(Material.CYAN_CANDLE); + candles.add(Material.GRAY_CANDLE); + candles.add(Material.GREEN_CANDLE); + candles.add(Material.LIGHT_BLUE_CANDLE); + candles.add(Material.MAGENTA_CANDLE); + candles.add(Material.LIME_CANDLE); + candles.add(Material.ORANGE_CANDLE); + candles.add(Material.PINK_CANDLE); + candles.add(Material.PURPLE_CANDLE); + candles.add(Material.RED_CANDLE); + candles.add(Material.WHITE_CANDLE); + candles.add(Material.YELLOW_CANDLE); + + candleCakes = new HashSet<>(); + candleCakes.add(Material.CANDLE_CAKE); + candleCakes.add(Material.BLACK_CANDLE_CAKE); + candleCakes.add(Material.BLUE_CANDLE_CAKE); + candleCakes.add(Material.LIGHT_GRAY_CANDLE_CAKE); + candleCakes.add(Material.BROWN_CANDLE_CAKE); + candleCakes.add(Material.CYAN_CANDLE_CAKE); + candleCakes.add(Material.GRAY_CANDLE_CAKE); + candleCakes.add(Material.GREEN_CANDLE_CAKE); + candleCakes.add(Material.LIGHT_BLUE_CANDLE_CAKE); + candleCakes.add(Material.MAGENTA_CANDLE_CAKE); + candleCakes.add(Material.LIME_CANDLE_CAKE); + candleCakes.add(Material.ORANGE_CANDLE_CAKE); + candleCakes.add(Material.PINK_CANDLE_CAKE); + candleCakes.add(Material.PURPLE_CANDLE_CAKE); + candleCakes.add(Material.RED_CANDLE_CAKE); + candleCakes.add(Material.WHITE_CANDLE_CAKE); + candleCakes.add(Material.YELLOW_CANDLE_CAKE); + + dyes = new HashMap<>(); + dyes.put(Material.BLACK_DYE, DyeColor.BLACK); + dyes.put(Material.BLUE_DYE, DyeColor.BLUE); + dyes.put(Material.LIGHT_GRAY_DYE, DyeColor.LIGHT_GRAY); + dyes.put(Material.BROWN_DYE, DyeColor.BROWN); + dyes.put(Material.CYAN_DYE, DyeColor.CYAN); + dyes.put(Material.GRAY_DYE, DyeColor.GRAY); + dyes.put(Material.GREEN_DYE, DyeColor.GREEN); + dyes.put(Material.LIGHT_BLUE_DYE, DyeColor.LIGHT_BLUE); + dyes.put(Material.MAGENTA_DYE, DyeColor.MAGENTA); + dyes.put(Material.LIME_DYE, DyeColor.LIME); + dyes.put(Material.ORANGE_DYE, DyeColor.ORANGE); + dyes.put(Material.PINK_DYE, DyeColor.PINK); + dyes.put(Material.PURPLE_DYE, DyeColor.PURPLE); + dyes.put(Material.RED_DYE, DyeColor.RED); + dyes.put(Material.WHITE_DYE, DyeColor.WHITE); + dyes.put(Material.YELLOW_DYE, DyeColor.YELLOW); + } + + private static final BlockFace[] relativeBlockFaces = new BlockFace[] { + BlockFace.EAST, BlockFace.WEST, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.UP, BlockFace.DOWN + }; + + /** + * Returns a list of block locations around the block that are of the type specified by the integer list parameter + * + * @param block The central block to get the blocks around + * @param type The type of blocks around the center block to return + * @return List of block locations around the block that are of the type specified by the integer list parameter + */ + public static List getBlocksNearby(org.bukkit.block.Block block, Set type) { + ArrayList blocks = new ArrayList<>(); + for (BlockFace blockFace : relativeBlockFaces) { + if (type.contains(block.getRelative(blockFace).getType())) { + blocks.add(block.getRelative(blockFace).getLocation()); + } + } + return blocks; + } + + public static boolean isTop(BlockData data) { + if (data instanceof Bisected && !(data instanceof Stairs)) { + return ((Bisected) data).getHalf() == Half.TOP; + } + return false; + } + + public static Material getInventoryHolderType(InventoryHolder holder) { + if (holder instanceof DoubleChest) { + return getInventoryHolderType(((DoubleChest) holder).getLeftSide()); + } else if (holder instanceof BlockState) { + return ((BlockState) holder).getType(); + } else { + return null; + } + } + + public static Location getInventoryHolderLocation(InventoryHolder holder) { + if (holder instanceof DoubleChest) { + return getInventoryHolderLocation(((DoubleChest) holder).getLeftSide()); + } else if (holder instanceof BlockState) { + return ((BlockState) holder).getLocation(); + } else { + return null; + } + } + + public static ItemStack[] compareInventories(ItemStack[] items1, ItemStack[] items2) { + final ArrayList diff = new ArrayList<>(); + for (ItemStack current : items2) { + try { + diff.add(new ItemStack(current)); + } catch (NullPointerException e) { + LogBlock.getInstance().getLogger().log(Level.SEVERE, "Could not clone ItemStack, probably Spigot bug SPIGOT-6025", e); // SPIGOT-6025 + } + } + for (ItemStack previous : items1) { + boolean found = false; + for (ItemStack current : diff) { + if (current.isSimilar(previous)) { + int newAmount = current.getAmount() - previous.getAmount(); + if (newAmount == 0) { + diff.remove(current); + } else { + current.setAmount(newAmount); + } + found = true; + break; + } + } + if (!found) { + try { + ItemStack subtracted = new ItemStack(previous); + subtracted.setAmount(-subtracted.getAmount()); + diff.add(subtracted); + } catch (NullPointerException e) { + LogBlock.getInstance().getLogger().log(Level.SEVERE, "Could not clone ItemStack, probably Spigot bug SPIGOT-6025", e); // SPIGOT-6025 + } + } + } + return diff.toArray(new ItemStack[diff.size()]); + } + + public static ItemStack[] compressInventory(ItemStack[] items) { + final ArrayList compressed = new ArrayList<>(); + for (final ItemStack item : items) { + if (item != null) { + boolean found = false; + for (final ItemStack item2 : compressed) { + if (item2.isSimilar(item)) { + item2.setAmount(item2.getAmount() + item.getAmount()); + found = true; + break; + } + } + if (!found) { + compressed.add(item.clone()); + } + } + } + return compressed.toArray(new ItemStack[compressed.size()]); + } + + public static boolean equalTypes(int type1, int type2) { + if (type1 == type2) { + return true; + } + for (final Set equivalent : blockEquivalents) { + if (equivalent.contains(type1) && equivalent.contains(type2)) { + return true; + } + } + return false; + } + + public static String friendlyWorldname(String worldName) { + return new File(worldName).getName(); + } + + public static Set> getBlockEquivalents() { + return blockEquivalents; + } + + public static Set getRelativeBreakables() { + return relativeBreakable; + } + + public static Set getRelativeTopBreakabls() { + return relativeTopBreakable; + } + + public static Set getFallingEntityKillers() { + return fallingEntityKillers; + } + + public static Set getNonFluidProofBlocks() { + return nonFluidProofBlocks; + } + + public static Set getCropBlocks() { + return cropBlocks; + } + + public static Set getContainerBlocks() { + return containerBlocks; + } + + public static Set getShulkerBoxBlocks() { + return shulkerBoxBlocks; + } + + public static boolean isConcreteBlock(Material m) { + return concreteBlocks.contains(m); + } + + public static String entityName(Entity entity) { + if (entity instanceof Player) { + return ((Player) entity).getName(); + } + if (entity instanceof TNTPrimed) { + return "TNT"; + } + return entity.getClass().getSimpleName().substring(5); + } + + public static void giveTool(Player player, Material type) { + final Inventory inv = player.getInventory(); + if (inv.contains(type)) { + player.sendMessage(ChatColor.RED + "You have already a " + type.name()); + } else { + final int free = inv.firstEmpty(); + if (free >= 0) { + if (player.getInventory().getItemInMainHand() != null && player.getInventory().getItemInMainHand().getType() != Material.AIR) { + inv.setItem(free, player.getInventory().getItemInMainHand()); + } + player.getInventory().setItemInMainHand(new ItemStack(type)); + player.sendMessage(ChatColor.GREEN + "Here's your " + type.name()); + } else { + player.sendMessage(ChatColor.RED + "You have no empty slot in your inventory"); + } + } + } + + public static int safeSpawnHeight(Location loc) { + final World world = loc.getWorld(); + world.getChunkAt(loc); + final int x = loc.getBlockX(), z = loc.getBlockZ(); + int y = loc.getBlockY(); + boolean lower = world.getBlockAt(x, y, z).isEmpty(), upper = world.getBlockAt(x, y + 1, z).isEmpty(); + while ((!lower || !upper) && y != world.getMaxHeight()) { + lower = upper; + upper = world.getBlockAt(x, ++y, z).isEmpty(); + } + while (world.getBlockAt(x, y - 1, z).isEmpty() && y != world.getMinHeight()) { + y--; + } + return y; + } + + public static int modifyContainer(BlockState b, ItemStack item, boolean remove) { + if (b instanceof InventoryHolder) { + final Inventory inv = ((InventoryHolder) b).getInventory(); + if (remove) { + final ItemStack tmp = inv.removeItem(item).get(0); + return tmp != null ? tmp.getAmount() : 0; + } else if (item.getAmount() > 0) { + final ItemStack tmp = inv.addItem(item).get(0); + return tmp != null ? tmp.getAmount() : 0; + } + } + return 0; + } + + public static boolean canFallIn(World world, int x, int y, int z) { + Block block = world.getBlockAt(x, y, z); + Material mat = block.getType(); + if (canDirectlyFallIn(mat)) { + return true; + } else if (getFallingEntityKillers().contains(mat) || singleBlockPlants.contains(mat) || mat == Material.VINE) { + if (slabs.contains(mat)) { + if (((Slab) block.getBlockData()).getType() != Type.BOTTOM) { + return false; + } + } + return true; + } + return false; + } + + public static boolean canDirectlyFallIn(Material m) { + return isEmpty(m) || m == Material.WATER || m == Material.LAVA || m == Material.FIRE; + } + + public static Material itemIDfromProjectileEntity(Entity e) { + return projectileItems.get(e.getType()); + } + + public static boolean isDoublePlant(Material m) { + return doublePlants.contains(m); + } + + public static boolean isWoodenDoor(Material m) { + return woodenDoors.contains(m); + } + + public static boolean isButton(Material m) { + return buttons.contains(m); + } + + public static boolean isEmpty(Material m) { + return m == Material.AIR || m == Material.CAVE_AIR || m == Material.VOID_AIR; + } + + public static TextComponent toString(ItemStack stack) { + if (stack == null || stack.getAmount() == 0 || isEmpty(stack.getType())) { + return prettyMaterial("nothing"); + } + TextComponent msg = MessagingUtil.createTextComponentWithColor(stack.getAmount() + "x ", TypeColor.DEFAULT.getColor()); + msg.addExtra(prettyMaterial(stack.getType())); + + try { + String itemTag = getItemTag(stack); + msg.setHoverEvent(new HoverEvent(Action.SHOW_ITEM, new Item(stack.getType().getKey().toString(), 1, itemTag != null ? ItemTag.ofNbt(itemTag) : null))); + } catch (Exception e) { + LogBlock.getInstance().getLogger().log(Level.SEVERE, "Failed to convert Itemstack to JSON", e); + msg.setHoverEvent(new HoverEvent(Action.SHOW_TEXT, new Text(new BaseComponent[] { MessagingUtil.createTextComponentWithColor("Error", TypeColor.ERROR.getColor()) }))); + } + + return msg; + } + + public static String getItemTag(ItemStack itemStack) throws ReflectiveOperationException { + Class craftItemStackClazz = ReflectionUtil.getCraftBukkitClass("inventory.CraftItemStack"); + Method asNMSCopyMethod = craftItemStackClazz.getMethod("asNMSCopy", ItemStack.class); + + Class nmsItemStackClazz = ReflectionUtil.getMinecraftClass("world.item.ItemStack"); + Method getTagMethod = nmsItemStackClazz.getDeclaredMethod("getTagClone"); + getTagMethod.setAccessible(true); + + Object nmsItemStack = asNMSCopyMethod.invoke(null, itemStack); + Object itemTag = getTagMethod.invoke(nmsItemStack); + + return itemTag != null ? itemTag.toString() : null; + } + + public static String formatMinecraftKey(String s) { + char[] cap = s.toCharArray(); + boolean lastSpace = true; + for (int i = 0; i < cap.length; i++) { + char c = cap[i]; + if (c == '_') { + c = ' '; + lastSpace = true; + } else if (c >= '0' && c <= '9' || c == '(' || c == ')') { + lastSpace = true; + } else { + if (lastSpace) { + c = Character.toUpperCase(c); + } else { + c = Character.toLowerCase(c); + } + lastSpace = false; + } + cap[i] = c; + } + return new String(cap); + } + + public static boolean isBed(Material type) { + return bedBlocks.contains(type); + } + + public static boolean isDye(Material type) { + return dyes.containsKey(type); + } + + public static DyeColor dyeToDyeColor(Material type) { + return dyes.get(type); + } + + public static Block getConnectedChest(Block chestBlock) { + // is this a chest? + BlockData blockData = chestBlock.getBlockData(); + if (!(blockData instanceof org.bukkit.block.data.type.Chest)) { + return null; + } + // so check if is should have a neighbour + org.bukkit.block.data.type.Chest chestData = (org.bukkit.block.data.type.Chest) blockData; + org.bukkit.block.data.type.Chest.Type chestType = chestData.getType(); + if (chestType != org.bukkit.block.data.type.Chest.Type.SINGLE) { + // check if the neighbour exists + BlockFace chestFace = chestData.getFacing(); + BlockFace faceToSecondChest; + if (chestFace == BlockFace.WEST) { + faceToSecondChest = BlockFace.NORTH; + } else if (chestFace == BlockFace.NORTH) { + faceToSecondChest = BlockFace.EAST; + } else if (chestFace == BlockFace.EAST) { + faceToSecondChest = BlockFace.SOUTH; + } else if (chestFace == BlockFace.SOUTH) { + faceToSecondChest = BlockFace.WEST; + } else { + return null; + } + org.bukkit.block.data.type.Chest.Type wantedChestType = org.bukkit.block.data.type.Chest.Type.RIGHT; + if (chestType == org.bukkit.block.data.type.Chest.Type.RIGHT) { + faceToSecondChest = faceToSecondChest.getOppositeFace(); + wantedChestType = org.bukkit.block.data.type.Chest.Type.LEFT; + } + Block face = chestBlock.getRelative(faceToSecondChest); + if (face.getType() == chestBlock.getType()) { + // check is the neighbour connects to this chest + org.bukkit.block.data.type.Chest otherChestData = (org.bukkit.block.data.type.Chest) face.getBlockData(); + if (otherChestData.getType() != wantedChestType || otherChestData.getFacing() != chestFace) { + return null; + } + return face; + } + } + return null; + } + + public static Entity loadEntityAround(Chunk chunk, UUID uuid) { + Entity e = Bukkit.getEntity(uuid); + if (e != null) { + return e; + } + if (!chunk.isLoaded()) { + chunk.getWorld().getChunkAt(chunk.getX(), chunk.getZ()); + e = Bukkit.getEntity(uuid); + if (e != null) { + return e; + } + } + int chunkx = chunk.getX(); + int chunkz = chunk.getZ(); + for (int i = 0; i < 8; i++) { + int x = i < 3 ? chunkx - 1 : (i < 5 ? chunkx : chunkx + 1); + int z = i == 0 || i == 3 || i == 5 ? chunkz - 1 : (i == 1 || i == 6 ? chunkz : chunkz + 1); + if (!chunk.getWorld().isChunkLoaded(x, z)) { + chunk.getWorld().getChunkAt(x, z); + e = Bukkit.getEntity(uuid); + if (e != null) { + return e; + } + } + } + return null; + } + + private static final HashMap types = new HashMap<>(); + static { + for (EntityType t : EntityType.values()) { + if (t != EntityType.UNKNOWN) { + types.put(t.name().toLowerCase(), t); + @SuppressWarnings("deprecation") + String typeName = t.getName(); + if (typeName != null) { + types.put(typeName.toLowerCase(), t); + } + Class ec = t.getEntityClass(); + if (ec != null) { + types.put(ec.getSimpleName().toLowerCase(), t); + } + types.put(t.getKey().getKey(), t); + types.put(t.getKey().toString(), t); + } + } + } + + public static EntityType matchEntityType(String typeName) { + return types.get(typeName.toLowerCase()); + } + + public static ItemStack getItemInSlot(ArmorStand stand, EquipmentSlot slot) { + if (slot == EquipmentSlot.HAND) { + return stand.getEquipment().getItemInMainHand(); + } else if (slot == EquipmentSlot.OFF_HAND) { + return stand.getEquipment().getItemInOffHand(); + } else if (slot == EquipmentSlot.FEET) { + return stand.getEquipment().getBoots(); + } else if (slot == EquipmentSlot.LEGS) { + return stand.getEquipment().getLeggings(); + } else if (slot == EquipmentSlot.CHEST) { + return stand.getEquipment().getChestplate(); + } else if (slot == EquipmentSlot.HEAD) { + return stand.getEquipment().getHelmet(); + } + return null; + } + + public static void setItemInSlot(ArmorStand stand, EquipmentSlot slot, ItemStack stack) { + if (slot == EquipmentSlot.HAND) { + stand.getEquipment().setItemInMainHand(stack); + } else if (slot == EquipmentSlot.OFF_HAND) { + stand.getEquipment().setItemInOffHand(stack); + } else if (slot == EquipmentSlot.FEET) { + stand.getEquipment().setBoots(stack); + } else if (slot == EquipmentSlot.LEGS) { + stand.getEquipment().setLeggings(stack); + } else if (slot == EquipmentSlot.CHEST) { + stand.getEquipment().setChestplate(stack); + } else if (slot == EquipmentSlot.HEAD) { + stand.getEquipment().setHelmet(stack); + } + } + + public static ItemStack[] deepCopy(ItemStack[] of) { + ItemStack[] result = new ItemStack[of.length]; + for (int i = 0; i < result.length; i++) { + result[i] = of[i] == null ? null : new ItemStack(of[i]); + } + return result; + } + + private static int getFirstPartialItemStack(ItemStack item, ItemStack[] contents, int start) { + for (int i = start; i < contents.length; i++) { + ItemStack content = contents[i]; + if (content != null && content.isSimilar(item) && content.getAmount() < content.getMaxStackSize()) { + return i; + } + } + return -1; + } + + private static int getFirstFreeItemStack(ItemStack[] contents, int start) { + for (int i = start; i < contents.length; i++) { + ItemStack content = contents[i]; + if (content == null || content.getAmount() == 0 || content.getType() == Material.AIR) { + return i; + } + } + return -1; + } + + public static boolean hasInventoryStorageSpaceFor(Inventory inv, ItemStack... items) { + ItemStack[] contents = deepCopy(inv.getStorageContents()); + for (ItemStack item : items) { + if (item != null && item.getType() != Material.AIR) { + int remaining = item.getAmount(); + // fill partial stacks + int firstPartial = -1; + while (remaining > 0) { + firstPartial = getFirstPartialItemStack(item, contents, firstPartial + 1); + if (firstPartial < 0) { + break; + } + ItemStack content = contents[firstPartial]; + int add = Math.min(content.getMaxStackSize() - content.getAmount(), remaining); + content.setAmount(content.getAmount() + add); + remaining -= add; + } + // create new stacks + int firstFree = -1; + while (remaining > 0) { + firstFree = getFirstFreeItemStack(contents, firstFree + 1); + if (firstFree < 0) { + return false; // no free place found + } + ItemStack content = new ItemStack(item); + contents[firstFree] = content; + // max stack size might return -1, in this case assume 1 + int add = Math.min(Math.max(content.getMaxStackSize(), 1), remaining); + content.setAmount(add); + remaining -= add; + } + } + } + return true; + } + + public static boolean isSimilarForRollback(Material expected, Material found) { + if (expected == found) { + return true; + } + if (expected == Material.DIRT || expected == Material.MYCELIUM || expected == Material.FARMLAND || expected == Material.GRASS_BLOCK || expected == Material.PODZOL || expected == Material.DIRT_PATH) { + return found == Material.DIRT || found == Material.MYCELIUM || found == Material.FARMLAND || found == Material.GRASS_BLOCK || found == Material.PODZOL || found == Material.DIRT_PATH; + } + if (expected == Material.BAMBOO || expected == Material.BAMBOO_SAPLING) { + return found == Material.BAMBOO || found == Material.BAMBOO_SAPLING; + } + if (expected == Material.SPONGE || expected == Material.WET_SPONGE) { + return found == Material.SPONGE || found == Material.WET_SPONGE; + } + if (expected == Material.MELON_STEM || expected == Material.ATTACHED_MELON_STEM) { + return found == Material.MELON_STEM || found == Material.ATTACHED_MELON_STEM; + } + if (expected == Material.PUMPKIN_STEM || expected == Material.ATTACHED_PUMPKIN_STEM) { + return found == Material.PUMPKIN_STEM || found == Material.ATTACHED_PUMPKIN_STEM; + } + if (expected == Material.TWISTING_VINES || expected == Material.TWISTING_VINES_PLANT) { + return found == Material.TWISTING_VINES || found == Material.TWISTING_VINES_PLANT; + } + if (expected == Material.WEEPING_VINES || expected == Material.WEEPING_VINES_PLANT) { + return found == Material.WEEPING_VINES || found == Material.WEEPING_VINES_PLANT; + } + if (expected == Material.CAVE_VINES || expected == Material.CAVE_VINES_PLANT) { + return found == Material.CAVE_VINES || found == Material.CAVE_VINES_PLANT; + } + if (expected == Material.BIG_DRIPLEAF || expected == Material.BIG_DRIPLEAF_STEM) { + return found == Material.BIG_DRIPLEAF || found == Material.BIG_DRIPLEAF_STEM; + } + if (expected == Material.COPPER_BLOCK || expected == Material.EXPOSED_COPPER || expected == Material.WEATHERED_COPPER || expected == Material.OXIDIZED_COPPER) { + return found == Material.COPPER_BLOCK || found == Material.EXPOSED_COPPER || found == Material.WEATHERED_COPPER || found == Material.OXIDIZED_COPPER; + } + if (expected == Material.CUT_COPPER || expected == Material.EXPOSED_CUT_COPPER || expected == Material.WEATHERED_CUT_COPPER || expected == Material.OXIDIZED_CUT_COPPER) { + return found == Material.CUT_COPPER || found == Material.EXPOSED_CUT_COPPER || found == Material.WEATHERED_CUT_COPPER || found == Material.OXIDIZED_CUT_COPPER; + } + if (expected == Material.CUT_COPPER_STAIRS || expected == Material.EXPOSED_CUT_COPPER_STAIRS || expected == Material.WEATHERED_CUT_COPPER_STAIRS || expected == Material.OXIDIZED_CUT_COPPER_STAIRS) { + return found == Material.CUT_COPPER_STAIRS || found == Material.EXPOSED_CUT_COPPER_STAIRS || found == Material.WEATHERED_CUT_COPPER_STAIRS || found == Material.OXIDIZED_CUT_COPPER_STAIRS; + } + if (expected == Material.CUT_COPPER_SLAB || expected == Material.EXPOSED_CUT_COPPER_SLAB || expected == Material.WEATHERED_CUT_COPPER_SLAB || expected == Material.OXIDIZED_CUT_COPPER_SLAB) { + return found == Material.CUT_COPPER_SLAB || found == Material.EXPOSED_CUT_COPPER_SLAB || found == Material.WEATHERED_CUT_COPPER_SLAB || found == Material.OXIDIZED_CUT_COPPER_SLAB; + } + return false; + } + + public static Set getAllSignMaterials() { + return unmodifiableSigns; + } + + public static boolean isAlwaysWaterlogged(Material m) { + return alwaysWaterlogged.contains(m); + } + + public static boolean isCandle(Material m) { + return candles.contains(m); + } + + public static boolean isCandleCake(Material m) { + return candleCakes.contains(m); + } + + public static boolean isHangingSign(Material m) { + return hangingSigns.contains(m); + } + + public static boolean isFenceGate(Material m) { + return fenceGates.contains(m); + } + + public static boolean isWoodenTrapdoor(Material m) { + return woodenTrapdoors.contains(m); + } + + public static boolean isPressurePlate(Material m) { + return pressurePlates.contains(m); + } + + public static boolean isSign(Material m) { + return allSigns.contains(m); + } + + public static Side getFacingSignSide(Entity entity, Block sign) { + BlockData data = sign.getBlockData(); + Material type = data.getMaterial(); + BlockFace signFace = null; + double centerx = 0.5; + double centerz = 0.5; + double yRotationDegree = 0; + if (type.data == Sign.class || type.data == HangingSign.class) { + Rotatable rotatableData = (Rotatable) data; + signFace = rotatableData.getRotation(); + if (signFace == BlockFace.SOUTH) { + yRotationDegree = 360 * 0.0 / 16.0; + } else if (signFace == BlockFace.SOUTH_SOUTH_WEST) { + yRotationDegree = 360 * 1.0 / 16.0; + } else if (signFace == BlockFace.SOUTH_WEST) { + yRotationDegree = 360 * 2.0 / 16.0; + } else if (signFace == BlockFace.WEST_SOUTH_WEST) { + yRotationDegree = 360 * 3.0 / 16.0; + } else if (signFace == BlockFace.WEST) { + yRotationDegree = 360 * 4.0 / 16.0; + } else if (signFace == BlockFace.WEST_NORTH_WEST) { + yRotationDegree = 360 * 5.0 / 16.0; + } else if (signFace == BlockFace.NORTH_WEST) { + yRotationDegree = 360 * 6.0 / 16.0; + } else if (signFace == BlockFace.NORTH_NORTH_WEST) { + yRotationDegree = 360 * 7.0 / 16.0; + } else if (signFace == BlockFace.NORTH) { + yRotationDegree = 360 * 8.0 / 16.0; + } else if (signFace == BlockFace.NORTH_NORTH_EAST) { + yRotationDegree = 360 * 9.0 / 16.0; + } else if (signFace == BlockFace.NORTH_EAST) { + yRotationDegree = 360 * 10.0 / 16.0; + } else if (signFace == BlockFace.EAST_NORTH_EAST) { + yRotationDegree = 360 * 11.0 / 16.0; + } else if (signFace == BlockFace.EAST) { + yRotationDegree = 360 * 12.0 / 16.0; + } else if (signFace == BlockFace.EAST_SOUTH_EAST) { + yRotationDegree = 360 * 13.0 / 16.0; + } else if (signFace == BlockFace.SOUTH_EAST) { + yRotationDegree = 360 * 14.0 / 16.0; + } else if (signFace == BlockFace.SOUTH_SOUTH_EAST) { + yRotationDegree = 360 * 15.0 / 16.0; + } + } else if (type.data == WallSign.class || type.data == WallHangingSign.class) { + Directional directionalData = (Directional) data; + signFace = directionalData.getFacing(); + if (signFace == BlockFace.SOUTH) { + yRotationDegree = 0; + } else if (signFace == BlockFace.WEST) { + yRotationDegree = 90; + } else if (signFace == BlockFace.NORTH) { + yRotationDegree = 180; + } else if (signFace == BlockFace.EAST) { + yRotationDegree = 270; + } + // wall signs are not centered on the block (but hanging wall signs are) + if (type.data == WallSign.class) { + if (signFace == BlockFace.NORTH) { + centerz = 15.0 / 16.0; + } else if (signFace == BlockFace.SOUTH) { + centerz = 1.0 / 16.0; + } else if (signFace == BlockFace.WEST) { + centerx = 15.0 / 16.0; + } else if (signFace == BlockFace.EAST) { + centerx = 1.0 / 16.0; + } + } + } else { + throw new IllegalArgumentException("block is not a sign"); + } + + Location entityLoc = entity.getLocation(); + double relativeX = entityLoc.getX() - (sign.getX() + centerx); + double relativeZ = entityLoc.getZ() - (sign.getZ() + centerz); + double f = Math.atan2(relativeZ, relativeX) * 180.0 / Math.PI - 90.0; + + return Math.abs(Utils.warpDegrees(f - yRotationDegree)) <= 90.0 ? Side.FRONT : Side.BACK; + } +} diff --git a/src/main/java/de/diddiz/LogBlock/util/ComparableVersion.java b/src/main/java/de/diddiz/LogBlock/util/ComparableVersion.java index 17e8824..5bdec9a 100644 --- a/src/main/java/de/diddiz/LogBlock/util/ComparableVersion.java +++ b/src/main/java/de/diddiz/LogBlock/util/ComparableVersion.java @@ -1,424 +1,424 @@ -package de.diddiz.LogBlock.util; - -// Taken from maven-artifact at -// http://grepcode.com/file_/repo1.maven.org/maven2/org.apache.maven/maven-artifact/3.2.3/org/apache/maven/artifact/versioning/ComparableVersion.java/?v=source - -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import java.math.BigInteger; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; -import java.util.Locale; -import java.util.Properties; -import java.util.Stack; - -/** - * Generic implementation of version comparison. - * - *

Features: - *

- * - * @see "Versioning" on Maven Wiki - * @author Kenney Westerhof - * @author Hervé Boutemy - */ -public class ComparableVersion implements Comparable { - private String value; - - private String canonical; - - private ListItem items; - - private interface Item { - int INTEGER_ITEM = 0; - int STRING_ITEM = 1; - int LIST_ITEM = 2; - - int compareTo(Item item); - - int getType(); - - boolean isNull(); - } - - /** - * Represents a numeric item in the version item list. - */ - private static class IntegerItem implements Item { - private static final BigInteger BIG_INTEGER_ZERO = new BigInteger("0"); - - private final BigInteger value; - - public static final IntegerItem ZERO = new IntegerItem(); - - private IntegerItem() { - this.value = BIG_INTEGER_ZERO; - } - - public IntegerItem(String str) { - this.value = new BigInteger(str); - } - - @Override - public int getType() { - return INTEGER_ITEM; - } - - @Override - public boolean isNull() { - return BIG_INTEGER_ZERO.equals(value); - } - - @Override - public int compareTo(Item item) { - if (item == null) { - return BIG_INTEGER_ZERO.equals(value) ? 0 : 1; // 1.0 == 1, 1.1 > 1 - } - - switch (item.getType()) { - case INTEGER_ITEM: - return value.compareTo(((IntegerItem) item).value); - - case STRING_ITEM: - return 1; // 1.1 > 1-sp - - case LIST_ITEM: - return 1; // 1.1 > 1-1 - - default: - throw new RuntimeException("invalid item: " + item.getClass()); - } - } - - @Override - public String toString() { - return value.toString(); - } - } - - /** - * Represents a string in the version item list, usually a qualifier. - */ - private static class StringItem implements Item { - private static final String[] QUALIFIERS = { "alpha", "beta", "milestone", "rc", "snapshot", "", "sp" }; - - private static final List _QUALIFIERS = Arrays.asList(QUALIFIERS); - - private static final Properties ALIASES = new Properties(); - static { - ALIASES.put("ga", ""); - ALIASES.put("final", ""); - ALIASES.put("cr", "rc"); - } - - /** - * A comparable value for the empty-string qualifier. This one is used to determine if a given qualifier makes - * the version older than one without a qualifier, or more recent. - */ - private static final String RELEASE_VERSION_INDEX = String.valueOf(_QUALIFIERS.indexOf("")); - - private String value; - - public StringItem(String value, boolean followedByDigit) { - if (followedByDigit && value.length() == 1) { - // a1 = alpha-1, b1 = beta-1, m1 = milestone-1 - switch (value.charAt(0)) { - case 'a': - value = "alpha"; - break; - case 'b': - value = "beta"; - break; - case 'm': - value = "milestone"; - break; - } - } - this.value = ALIASES.getProperty(value, value); - } - - @Override - public int getType() { - return STRING_ITEM; - } - - @Override - public boolean isNull() { - return (comparableQualifier(value).compareTo(RELEASE_VERSION_INDEX) == 0); - } - - /** - * Returns a comparable value for a qualifier. - * - * This method takes into account the ordering of known qualifiers then unknown qualifiers with lexical ordering. - * - * just returning an Integer with the index here is faster, but requires a lot of if/then/else to check for -1 - * or QUALIFIERS.size and then resort to lexical ordering. Most comparisons are decided by the first character, - * so this is still fast. If more characters are needed then it requires a lexical sort anyway. - * - * @param qualifier - * @return an equivalent value that can be used with lexical comparison - */ - public static String comparableQualifier(String qualifier) { - int i = _QUALIFIERS.indexOf(qualifier); - - return i == -1 ? (_QUALIFIERS.size() + "-" + qualifier) : String.valueOf(i); - } - - @Override - public int compareTo(Item item) { - if (item == null) { - // 1-rc < 1, 1-ga > 1 - return comparableQualifier(value).compareTo(RELEASE_VERSION_INDEX); - } - switch (item.getType()) { - case INTEGER_ITEM: - return -1; // 1.any < 1.1 ? - - case STRING_ITEM: - return comparableQualifier(value).compareTo(comparableQualifier(((StringItem) item).value)); - - case LIST_ITEM: - return -1; // 1.any < 1-1 - - default: - throw new RuntimeException("invalid item: " + item.getClass()); - } - } - - @Override - public String toString() { - return value; - } - } - - /** - * Represents a version list item. This class is used both for the global item list and for sub-lists (which start - * with '-(number)' in the version specification). - */ - private static class ListItem extends ArrayList implements Item { - private static final long serialVersionUID = 5914575811857700009L; - - @Override - public int getType() { - return LIST_ITEM; - } - - @Override - public boolean isNull() { - return (size() == 0); - } - - void normalize() { - for (ListIterator iterator = listIterator(size()); iterator.hasPrevious();) { - Item item = iterator.previous(); - if (item.isNull()) { - iterator.remove(); // remove null trailing items: 0, "", empty list - } else { - break; - } - } - } - - @Override - public int compareTo(Item item) { - if (item == null) { - if (size() == 0) { - return 0; // 1-0 = 1- (normalize) = 1 - } - Item first = get(0); - return first.compareTo(null); - } - switch (item.getType()) { - case INTEGER_ITEM: - return -1; // 1-1 < 1.0.x - - case STRING_ITEM: - return 1; // 1-1 > 1-sp - - case LIST_ITEM: - Iterator left = iterator(); - Iterator right = ((ListItem) item).iterator(); - - while (left.hasNext() || right.hasNext()) { - Item l = left.hasNext() ? left.next() : null; - Item r = right.hasNext() ? right.next() : null; - - // if this is shorter, then invert the compare and mul with -1 - int result = l == null ? (r == null ? 0 : -1 * r.compareTo(l)) : l.compareTo(r); - - if (result != 0) { - return result; - } - } - - return 0; - - default: - throw new RuntimeException("invalid item: " + item.getClass()); - } - } - - @Override - public String toString() { - StringBuilder buffer = new StringBuilder("("); - for (Iterator iter = iterator(); iter.hasNext();) { - buffer.append(iter.next()); - if (iter.hasNext()) { - buffer.append(','); - } - } - buffer.append(')'); - return buffer.toString(); - } - } - - public ComparableVersion(String version) { - parseVersion(version); - } - - public final void parseVersion(String version) { - this.value = version; - - items = new ListItem(); - - version = version.toLowerCase(Locale.ENGLISH); - - ListItem list = items; - - Stack stack = new Stack<>(); - stack.push(list); - - boolean isDigit = false; - - int startIndex = 0; - - for (int i = 0; i < version.length(); i++) { - char c = version.charAt(i); - - if (c == '.') { - if (i == startIndex) { - list.add(IntegerItem.ZERO); - } else { - list.add(parseItem(isDigit, version.substring(startIndex, i))); - } - startIndex = i + 1; - } else if (c == '-') { - if (i == startIndex) { - list.add(IntegerItem.ZERO); - } else { - list.add(parseItem(isDigit, version.substring(startIndex, i))); - } - startIndex = i + 1; - - if (isDigit) { - list.normalize(); // 1.0-* = 1-* - - if ((i + 1 < version.length()) && Character.isDigit(version.charAt(i + 1))) { - // new ListItem only if previous were digits and new char is a digit, - // ie need to differentiate only 1.1 from 1-1 - list.add(list = new ListItem()); - - stack.push(list); - } - } - } else if (Character.isDigit(c)) { - if (!isDigit && i > startIndex) { - list.add(new StringItem(version.substring(startIndex, i), true)); - startIndex = i; - } - - isDigit = true; - } else { - if (isDigit && i > startIndex) { - list.add(parseItem(true, version.substring(startIndex, i))); - startIndex = i; - } - - isDigit = false; - } - } - - if (version.length() > startIndex) { - list.add(parseItem(isDigit, version.substring(startIndex))); - } - - while (!stack.isEmpty()) { - list = (ListItem) stack.pop(); - list.normalize(); - } - - canonical = items.toString(); - } - - private static Item parseItem(boolean isDigit, String buf) { - return isDigit ? new IntegerItem(buf) : new StringItem(buf, false); - } - - @Override - public int compareTo(ComparableVersion o) { - return items.compareTo(o.items); - } - - public int compareTo(String version) { - return compareTo(new ComparableVersion(version)); - } - - @Override - public String toString() { - return value; - } - - public String toCanonicalString() { - return canonical; - } - - @Override - public boolean equals(Object o) { - return (o instanceof ComparableVersion) && canonical.equals(((ComparableVersion) o).canonical); - } - - @Override - public int hashCode() { - return canonical.hashCode(); - } -} +package de.diddiz.LogBlock.util; + +// Taken from maven-artifact at +// http://grepcode.com/file_/repo1.maven.org/maven2/org.apache.maven/maven-artifact/3.2.3/org/apache/maven/artifact/versioning/ComparableVersion.java/?v=source + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Locale; +import java.util.Properties; +import java.util.Stack; + +/** + * Generic implementation of version comparison. + * + *

Features: + *

    + *
  • mixing of '-' (dash) and '.' (dot) separators,
  • + *
  • transition between characters and digits also constitutes a separator: + * 1.0alpha1 => [1, 0, alpha, 1]
  • + *
  • unlimited number of version components,
  • + *
  • version components in the text can be digits or strings,
  • + *
  • strings are checked for well-known qualifiers and the qualifier ordering is used for version ordering. + * Well-known qualifiers (case insensitive) are:
      + *
    • alpha or a
    • + *
    • beta or b
    • + *
    • milestone or m
    • + *
    • rc or cr
    • + *
    • snapshot
    • + *
    • (the empty string) or ga or final
    • + *
    • sp
    • + *
    + * Unknown qualifiers are considered after known qualifiers, with lexical order (always case insensitive), + *
  • + *
  • a dash usually precedes a qualifier, and is always less important than something preceded with a dot.
  • + *

+ * + * @see "Versioning" on Maven Wiki + * @author Kenney Westerhof + * @author Hervé Boutemy + */ +public class ComparableVersion implements Comparable { + private String value; + + private String canonical; + + private ListItem items; + + private interface Item { + int INTEGER_ITEM = 0; + int STRING_ITEM = 1; + int LIST_ITEM = 2; + + int compareTo(Item item); + + int getType(); + + boolean isNull(); + } + + /** + * Represents a numeric item in the version item list. + */ + private static class IntegerItem implements Item { + private static final BigInteger BIG_INTEGER_ZERO = new BigInteger("0"); + + private final BigInteger value; + + public static final IntegerItem ZERO = new IntegerItem(); + + private IntegerItem() { + this.value = BIG_INTEGER_ZERO; + } + + public IntegerItem(String str) { + this.value = new BigInteger(str); + } + + @Override + public int getType() { + return INTEGER_ITEM; + } + + @Override + public boolean isNull() { + return BIG_INTEGER_ZERO.equals(value); + } + + @Override + public int compareTo(Item item) { + if (item == null) { + return BIG_INTEGER_ZERO.equals(value) ? 0 : 1; // 1.0 == 1, 1.1 > 1 + } + + switch (item.getType()) { + case INTEGER_ITEM: + return value.compareTo(((IntegerItem) item).value); + + case STRING_ITEM: + return 1; // 1.1 > 1-sp + + case LIST_ITEM: + return 1; // 1.1 > 1-1 + + default: + throw new RuntimeException("invalid item: " + item.getClass()); + } + } + + @Override + public String toString() { + return value.toString(); + } + } + + /** + * Represents a string in the version item list, usually a qualifier. + */ + private static class StringItem implements Item { + private static final String[] QUALIFIERS = { "alpha", "beta", "milestone", "rc", "snapshot", "", "sp" }; + + private static final List _QUALIFIERS = Arrays.asList(QUALIFIERS); + + private static final Properties ALIASES = new Properties(); + static { + ALIASES.put("ga", ""); + ALIASES.put("final", ""); + ALIASES.put("cr", "rc"); + } + + /** + * A comparable value for the empty-string qualifier. This one is used to determine if a given qualifier makes + * the version older than one without a qualifier, or more recent. + */ + private static final String RELEASE_VERSION_INDEX = String.valueOf(_QUALIFIERS.indexOf("")); + + private String value; + + public StringItem(String value, boolean followedByDigit) { + if (followedByDigit && value.length() == 1) { + // a1 = alpha-1, b1 = beta-1, m1 = milestone-1 + switch (value.charAt(0)) { + case 'a': + value = "alpha"; + break; + case 'b': + value = "beta"; + break; + case 'm': + value = "milestone"; + break; + } + } + this.value = ALIASES.getProperty(value, value); + } + + @Override + public int getType() { + return STRING_ITEM; + } + + @Override + public boolean isNull() { + return (comparableQualifier(value).compareTo(RELEASE_VERSION_INDEX) == 0); + } + + /** + * Returns a comparable value for a qualifier. + * + * This method takes into account the ordering of known qualifiers then unknown qualifiers with lexical ordering. + * + * just returning an Integer with the index here is faster, but requires a lot of if/then/else to check for -1 + * or QUALIFIERS.size and then resort to lexical ordering. Most comparisons are decided by the first character, + * so this is still fast. If more characters are needed then it requires a lexical sort anyway. + * + * @param qualifier + * @return an equivalent value that can be used with lexical comparison + */ + public static String comparableQualifier(String qualifier) { + int i = _QUALIFIERS.indexOf(qualifier); + + return i == -1 ? (_QUALIFIERS.size() + "-" + qualifier) : String.valueOf(i); + } + + @Override + public int compareTo(Item item) { + if (item == null) { + // 1-rc < 1, 1-ga > 1 + return comparableQualifier(value).compareTo(RELEASE_VERSION_INDEX); + } + switch (item.getType()) { + case INTEGER_ITEM: + return -1; // 1.any < 1.1 ? + + case STRING_ITEM: + return comparableQualifier(value).compareTo(comparableQualifier(((StringItem) item).value)); + + case LIST_ITEM: + return -1; // 1.any < 1-1 + + default: + throw new RuntimeException("invalid item: " + item.getClass()); + } + } + + @Override + public String toString() { + return value; + } + } + + /** + * Represents a version list item. This class is used both for the global item list and for sub-lists (which start + * with '-(number)' in the version specification). + */ + private static class ListItem extends ArrayList implements Item { + private static final long serialVersionUID = 5914575811857700009L; + + @Override + public int getType() { + return LIST_ITEM; + } + + @Override + public boolean isNull() { + return (size() == 0); + } + + void normalize() { + for (ListIterator iterator = listIterator(size()); iterator.hasPrevious();) { + Item item = iterator.previous(); + if (item.isNull()) { + iterator.remove(); // remove null trailing items: 0, "", empty list + } else { + break; + } + } + } + + @Override + public int compareTo(Item item) { + if (item == null) { + if (size() == 0) { + return 0; // 1-0 = 1- (normalize) = 1 + } + Item first = get(0); + return first.compareTo(null); + } + switch (item.getType()) { + case INTEGER_ITEM: + return -1; // 1-1 < 1.0.x + + case STRING_ITEM: + return 1; // 1-1 > 1-sp + + case LIST_ITEM: + Iterator left = iterator(); + Iterator right = ((ListItem) item).iterator(); + + while (left.hasNext() || right.hasNext()) { + Item l = left.hasNext() ? left.next() : null; + Item r = right.hasNext() ? right.next() : null; + + // if this is shorter, then invert the compare and mul with -1 + int result = l == null ? (r == null ? 0 : -1 * r.compareTo(l)) : l.compareTo(r); + + if (result != 0) { + return result; + } + } + + return 0; + + default: + throw new RuntimeException("invalid item: " + item.getClass()); + } + } + + @Override + public String toString() { + StringBuilder buffer = new StringBuilder("("); + for (Iterator iter = iterator(); iter.hasNext();) { + buffer.append(iter.next()); + if (iter.hasNext()) { + buffer.append(','); + } + } + buffer.append(')'); + return buffer.toString(); + } + } + + public ComparableVersion(String version) { + parseVersion(version); + } + + public final void parseVersion(String version) { + this.value = version; + + items = new ListItem(); + + version = version.toLowerCase(Locale.ENGLISH); + + ListItem list = items; + + Stack stack = new Stack<>(); + stack.push(list); + + boolean isDigit = false; + + int startIndex = 0; + + for (int i = 0; i < version.length(); i++) { + char c = version.charAt(i); + + if (c == '.') { + if (i == startIndex) { + list.add(IntegerItem.ZERO); + } else { + list.add(parseItem(isDigit, version.substring(startIndex, i))); + } + startIndex = i + 1; + } else if (c == '-') { + if (i == startIndex) { + list.add(IntegerItem.ZERO); + } else { + list.add(parseItem(isDigit, version.substring(startIndex, i))); + } + startIndex = i + 1; + + if (isDigit) { + list.normalize(); // 1.0-* = 1-* + + if ((i + 1 < version.length()) && Character.isDigit(version.charAt(i + 1))) { + // new ListItem only if previous were digits and new char is a digit, + // ie need to differentiate only 1.1 from 1-1 + list.add(list = new ListItem()); + + stack.push(list); + } + } + } else if (Character.isDigit(c)) { + if (!isDigit && i > startIndex) { + list.add(new StringItem(version.substring(startIndex, i), true)); + startIndex = i; + } + + isDigit = true; + } else { + if (isDigit && i > startIndex) { + list.add(parseItem(true, version.substring(startIndex, i))); + startIndex = i; + } + + isDigit = false; + } + } + + if (version.length() > startIndex) { + list.add(parseItem(isDigit, version.substring(startIndex))); + } + + while (!stack.isEmpty()) { + list = (ListItem) stack.pop(); + list.normalize(); + } + + canonical = items.toString(); + } + + private static Item parseItem(boolean isDigit, String buf) { + return isDigit ? new IntegerItem(buf) : new StringItem(buf, false); + } + + @Override + public int compareTo(ComparableVersion o) { + return items.compareTo(o.items); + } + + public int compareTo(String version) { + return compareTo(new ComparableVersion(version)); + } + + @Override + public String toString() { + return value; + } + + public String toCanonicalString() { + return canonical; + } + + @Override + public boolean equals(Object o) { + return (o instanceof ComparableVersion) && canonical.equals(((ComparableVersion) o).canonical); + } + + @Override + public int hashCode() { + return canonical.hashCode(); + } +} diff --git a/src/main/java/de/diddiz/LogBlock/util/MySQLConnectionPool.java b/src/main/java/de/diddiz/LogBlock/util/MySQLConnectionPool.java index 3a0f854..d2b8337 100644 --- a/src/main/java/de/diddiz/LogBlock/util/MySQLConnectionPool.java +++ b/src/main/java/de/diddiz/LogBlock/util/MySQLConnectionPool.java @@ -1,51 +1,51 @@ -package de.diddiz.LogBlock.util; - -import com.zaxxer.hikari.HikariDataSource; -import de.diddiz.LogBlock.config.Config; - -import java.io.Closeable; -import java.sql.Connection; -import java.sql.SQLException; - -public class MySQLConnectionPool implements Closeable { - - private final HikariDataSource ds; - - public MySQLConnectionPool(String url, String user, String password, boolean useSSL, boolean requireSSL) { - this.ds = new HikariDataSource(); - ds.setJdbcUrl(url); - ds.setUsername(user); - ds.setPassword(password); - - ds.setMinimumIdle(2); - ds.setMaximumPoolSize(15); - ds.setPoolName("LogBlock-Connection-Pool"); - - ds.addDataSourceProperty("useUnicode", "true"); - ds.addDataSourceProperty("characterEncoding", "utf-8"); - ds.addDataSourceProperty("rewriteBatchedStatements", "true"); - - ds.addDataSourceProperty("cachePrepStmts", "true"); - ds.addDataSourceProperty("prepStmtCacheSize", "250"); - ds.addDataSourceProperty("prepStmtCacheSqlLimit", "2048"); - ds.addDataSourceProperty("useServerPrepStmts", "true"); - - ds.addDataSourceProperty("useSSL", Boolean.toString(useSSL)); - ds.addDataSourceProperty("requireSSL", Boolean.toString(requireSSL)); - ds.addDataSourceProperty("verifyServerCertificate", "false"); - } - - @Override - public void close() { - ds.close(); - } - - public Connection getConnection() throws SQLException { - Connection connection = ds.getConnection(); - if (Config.mb4) { - connection.createStatement().executeUpdate("SET NAMES utf8mb4"); - } - return connection; - } - -} +package de.diddiz.LogBlock.util; + +import com.zaxxer.hikari.HikariDataSource; +import de.diddiz.LogBlock.config.Config; + +import java.io.Closeable; +import java.sql.Connection; +import java.sql.SQLException; + +public class MySQLConnectionPool implements Closeable { + + private final HikariDataSource ds; + + public MySQLConnectionPool(String url, String user, String password, boolean useSSL, boolean requireSSL) { + this.ds = new HikariDataSource(); + ds.setJdbcUrl(url); + ds.setUsername(user); + ds.setPassword(password); + + ds.setMinimumIdle(2); + ds.setMaximumPoolSize(15); + ds.setPoolName("LogBlock-Connection-Pool"); + + ds.addDataSourceProperty("useUnicode", "true"); + ds.addDataSourceProperty("characterEncoding", "utf-8"); + ds.addDataSourceProperty("rewriteBatchedStatements", "true"); + + ds.addDataSourceProperty("cachePrepStmts", "true"); + ds.addDataSourceProperty("prepStmtCacheSize", "250"); + ds.addDataSourceProperty("prepStmtCacheSqlLimit", "2048"); + ds.addDataSourceProperty("useServerPrepStmts", "true"); + + ds.addDataSourceProperty("useSSL", Boolean.toString(useSSL)); + ds.addDataSourceProperty("requireSSL", Boolean.toString(requireSSL)); + ds.addDataSourceProperty("verifyServerCertificate", "false"); + } + + @Override + public void close() { + ds.close(); + } + + public Connection getConnection() throws SQLException { + Connection connection = ds.getConnection(); + if (Config.mb4) { + connection.createStatement().executeUpdate("SET NAMES utf8mb4"); + } + return connection; + } + +} diff --git a/src/main/java/de/diddiz/LogBlock/util/UUIDFetcher.java b/src/main/java/de/diddiz/LogBlock/util/UUIDFetcher.java index d6bca85..baeaac6 100644 --- a/src/main/java/de/diddiz/LogBlock/util/UUIDFetcher.java +++ b/src/main/java/de/diddiz/LogBlock/util/UUIDFetcher.java @@ -1,61 +1,61 @@ -package de.diddiz.LogBlock.util; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.net.HttpURLConnection; -import java.net.URL; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -// Adapted from https://gist.github.com/evilmidget38/26d70114b834f71fb3b4 - -public class UUIDFetcher { - - private static final String PROFILE_URL = "https://api.mojang.com/profiles/minecraft"; - private static final Gson gson = new GsonBuilder().setLenient().create(); - - public static Map getUUIDs(List names) throws Exception { - Map uuidMap = new HashMap<>(); - HttpURLConnection connection = createConnection(); - String body = gson.toJson(names); - writeBody(connection, body); - JsonArray array = gson.fromJson(new InputStreamReader(connection.getInputStream()), JsonArray.class); - for (JsonElement profile : array) { - JsonObject jsonProfile = (JsonObject) profile; - String id = jsonProfile.get("id").getAsString(); - String name = jsonProfile.get("name").getAsString(); - UUID uuid = getUUID(id); - uuidMap.put(name, uuid); - } - return uuidMap; - } - - private static void writeBody(HttpURLConnection connection, String body) throws Exception { - OutputStream stream = connection.getOutputStream(); - stream.write(body.getBytes()); - stream.flush(); - stream.close(); - } - - private static HttpURLConnection createConnection() throws Exception { - URL url = new URL(PROFILE_URL); - HttpURLConnection connection = (HttpURLConnection) url.openConnection(); - connection.setRequestMethod("POST"); - connection.setRequestProperty("Content-Type", "application/json"); - connection.setUseCaches(false); - connection.setDoInput(true); - connection.setDoOutput(true); - return connection; - } - - private static UUID getUUID(String id) { - return UUID.fromString(id.substring(0, 8) + "-" + id.substring(8, 12) + "-" + id.substring(12, 16) + "-" + id.substring(16, 20) + "-" + id.substring(20, 32)); - } -} +package de.diddiz.LogBlock.util; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +// Adapted from https://gist.github.com/evilmidget38/26d70114b834f71fb3b4 + +public class UUIDFetcher { + + private static final String PROFILE_URL = "https://api.mojang.com/profiles/minecraft"; + private static final Gson gson = new GsonBuilder().setLenient().create(); + + public static Map getUUIDs(List names) throws Exception { + Map uuidMap = new HashMap<>(); + HttpURLConnection connection = createConnection(); + String body = gson.toJson(names); + writeBody(connection, body); + JsonArray array = gson.fromJson(new InputStreamReader(connection.getInputStream()), JsonArray.class); + for (JsonElement profile : array) { + JsonObject jsonProfile = (JsonObject) profile; + String id = jsonProfile.get("id").getAsString(); + String name = jsonProfile.get("name").getAsString(); + UUID uuid = getUUID(id); + uuidMap.put(name, uuid); + } + return uuidMap; + } + + private static void writeBody(HttpURLConnection connection, String body) throws Exception { + OutputStream stream = connection.getOutputStream(); + stream.write(body.getBytes()); + stream.flush(); + stream.close(); + } + + private static HttpURLConnection createConnection() throws Exception { + URL url = new URL(PROFILE_URL); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("POST"); + connection.setRequestProperty("Content-Type", "application/json"); + connection.setUseCaches(false); + connection.setDoInput(true); + connection.setDoOutput(true); + return connection; + } + + private static UUID getUUID(String id) { + return UUID.fromString(id.substring(0, 8) + "-" + id.substring(8, 12) + "-" + id.substring(12, 16) + "-" + id.substring(16, 20) + "-" + id.substring(20, 32)); + } +} diff --git a/src/main/java/de/diddiz/LogBlock/util/Utils.java b/src/main/java/de/diddiz/LogBlock/util/Utils.java index 2c62a90..353c4af 100644 --- a/src/main/java/de/diddiz/LogBlock/util/Utils.java +++ b/src/main/java/de/diddiz/LogBlock/util/Utils.java @@ -1,298 +1,298 @@ -package de.diddiz.LogBlock.util; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FilenameFilter; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.zip.GZIPInputStream; -import java.util.zip.GZIPOutputStream; -import java.util.zip.ZipException; - -import org.bukkit.configuration.InvalidConfigurationException; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.inventory.ItemStack; - -import de.diddiz.LogBlock.LogBlock; - -public class Utils { - public static String newline = System.getProperty("line.separator"); - - public static boolean isInt(String str) { - try { - Integer.parseInt(str); - return true; - } catch (final NumberFormatException ex) { - } - return false; - } - - public static boolean isShort(String str) { - try { - Short.parseShort(str); - return true; - } catch (final NumberFormatException ex) { - } - return false; - } - - public static boolean isByte(String str) { - try { - Byte.parseByte(str); - return true; - } catch (final NumberFormatException ex) { - } - return false; - } - - public static String listing(String[] entries, String delimiter, String finalDelimiter) { - final int len = entries.length; - if (len == 0) { - return ""; - } - if (len == 1) { - return entries[0]; - } - final StringBuilder builder = new StringBuilder(entries[0]); - for (int i = 1; i < len - 1; i++) { - builder.append(delimiter).append(entries[i]); - } - builder.append(finalDelimiter).append(entries[len - 1]); - return builder.toString(); - } - - public static String listing(List entries, String delimiter, String finalDelimiter) { - final int len = entries.size(); - if (len == 0) { - return ""; - } - if (len == 1) { - return entries.get(0).toString(); - } - final StringBuilder builder = new StringBuilder(entries.get(0).toString()); - for (int i = 1; i < len - 1; i++) { - builder.append(delimiter).append(entries.get(i).toString()); - } - builder.append(finalDelimiter).append(entries.get(len - 1).toString()); - return builder.toString(); - } - - public static int parseTimeSpec(String[] spec) { - if (spec == null || spec.length < 1 || spec.length > 2) { - return -1; - } - if (spec.length == 1 && isInt(spec[0])) { - return Integer.valueOf(spec[0]); - } - if (!spec[0].contains(":") && !spec[0].contains(".")) { - if (spec.length == 2) { - if (!isInt(spec[0])) { - return -1; - } - int min = Integer.parseInt(spec[0]); - if (spec[1].startsWith("h")) { - min *= 60; - } else if (spec[1].startsWith("d")) { - min *= 1440; - } - return min; - } else if (spec.length == 1) { - int days = 0, hours = 0, minutes = 0; - int lastIndex = 0, currIndex = 1; - while (currIndex <= spec[0].length()) { - while (currIndex <= spec[0].length() && isInt(spec[0].substring(lastIndex, currIndex))) { - currIndex++; - } - if (currIndex - 1 != lastIndex) { - if (currIndex > spec[0].length()) { - return -1; - } - final String param = spec[0].substring(currIndex - 1, currIndex).toLowerCase(); - if (param.equals("d")) { - days = Integer.parseInt(spec[0].substring(lastIndex, currIndex - 1)); - } else if (param.equals("h")) { - hours = Integer.parseInt(spec[0].substring(lastIndex, currIndex - 1)); - } else if (param.equals("m")) { - minutes = Integer.parseInt(spec[0].substring(lastIndex, currIndex - 1)); - } - } - lastIndex = currIndex; - currIndex++; - } - if (days == 0 && hours == 0 && minutes == 0) { - return -1; - } - return minutes + hours * 60 + days * 1440; - } else { - return -1; - } - } - final String timestamp; - if (spec.length == 1) { - if (spec[0].contains(":")) { - timestamp = new SimpleDateFormat("dd.MM.yyyy").format(System.currentTimeMillis()) + " " + spec[0]; - } else { - timestamp = spec[0] + " 00:00:00"; - } - } else { - timestamp = spec[0] + " " + spec[1]; - } - try { - return (int) ((System.currentTimeMillis() - new SimpleDateFormat("dd.MM.yyyy HH:mm:ss").parse(timestamp).getTime()) / 60000); - } catch (final ParseException ex) { - return -1; - } - } - - public static String spaces(int count) { - final StringBuilder filled = new StringBuilder(count); - for (int i = 0; i < count; i++) { - filled.append(' '); - } - return filled.toString(); - } - - public static String join(String[] s, String delimiter) { - if (s == null || s.length == 0) { - return ""; - } - final int len = s.length; - final StringBuilder builder = new StringBuilder(s[0]); - for (int i = 1; i < len; i++) { - builder.append(delimiter).append(s[i]); - } - return builder.toString(); - } - - /** - * Converts a list of arguments e.g ['lb', 'clearlog', 'world', '"my', 'world', 'of', 'swag"'] - * into a list of arguments with any text encapsulated by quotes treated as one word - * For this particular example: ['lb', 'clearlog', 'world', '"my world of swag"'] - * - * @param args The list of arguments - * @return A new list with the quoted arguments parsed to single values - */ - public static List parseQuotes(List args) { - List newArguments = new ArrayList<>(); - String subjectString = join(args.toArray(new String[args.size()]), " "); - - Pattern regex = Pattern.compile("[^\\s\"']+|\"[^\"]*\"|'[^']*'"); - Matcher regexMatcher = regex.matcher(subjectString); - while (regexMatcher.find()) { - newArguments.add(regexMatcher.group()); - } - - return newArguments; - } - - public static class ExtensionFilenameFilter implements FilenameFilter { - private final String ext; - - public ExtensionFilenameFilter(String ext) { - this.ext = "." + ext; - } - - @Override - public boolean accept(File dir, String name) { - return name.toLowerCase().endsWith(ext); - } - } - - private final static char[] hexArray = "0123456789ABCDEF".toCharArray(); - - public static String mysqlEscapeBytes(byte[] bytes) { - char[] hexChars = new char[bytes.length * 2 + 2]; - hexChars[0] = '0'; - hexChars[1] = 'x'; - for (int j = 0; j < bytes.length; j++) { - int v = bytes[j] & 0xFF; - hexChars[j * 2 + 2] = hexArray[v >>> 4]; - hexChars[j * 2 + 3] = hexArray[v & 0x0F]; - } - return new String(hexChars); - } - - public static String mysqlPrepareBytesForInsertAllowNull(byte[] bytes) { - if (bytes == null) { - return "null"; - } - return "'" + mysqlEscapeBytes(bytes) + "'"; - } - - public static String mysqlTextEscape(String untrusted) { - return untrusted.replace("\\", "\\\\").replace("'", "\\'"); - } - - public static ItemStack loadItemStack(byte[] data) { - if (data == null || data.length == 0) { - return null; - } - YamlConfiguration conf = deserializeYamlConfiguration(data); - return conf == null ? null : conf.getItemStack("stack"); - } - - public static byte[] saveItemStack(ItemStack stack) { - if (stack == null || BukkitUtils.isEmpty(stack.getType())) { - return null; - } - YamlConfiguration conf = new YamlConfiguration(); - conf.set("stack", stack); - return serializeYamlConfiguration(conf); - } - - public static YamlConfiguration deserializeYamlConfiguration(byte[] data) { - if (data == null || data.length == 0) { - return null; - } - YamlConfiguration conf = new YamlConfiguration(); - try { - InputStreamReader reader = new InputStreamReader(new GZIPInputStream(new ByteArrayInputStream(data)), "UTF-8"); - conf.load(reader); - reader.close(); - return conf; - } catch (ZipException | InvalidConfigurationException e) { - LogBlock.getInstance().getLogger().warning("Could not deserialize YamlConfiguration: " + e.getMessage()); - return conf; - } catch (IOException e) { - throw new RuntimeException("IOException should be impossible for ByteArrayInputStream", e); - } - } - - public static byte[] serializeYamlConfiguration(YamlConfiguration conf) { - if (conf == null || conf.getKeys(false).isEmpty()) { - return null; - } - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - OutputStreamWriter writer = new OutputStreamWriter(new GZIPOutputStream(baos), "UTF-8"); - writer.write(conf.saveToString()); - writer.close(); - return baos.toByteArray(); - } catch (IOException e) { - throw new RuntimeException("IOException should be impossible for ByteArrayOutputStream", e); - } - } - - public static String serializeForSQL(YamlConfiguration conf) { - return mysqlPrepareBytesForInsertAllowNull(serializeYamlConfiguration(conf)); - } - - public static double warpDegrees(double degrees) { - double d = degrees % 360.0; - if (d >= 180.0) { - d -= 360.0; - } - if (d < -180.0) { - d += 360.0; - } - return d; - } -} +package de.diddiz.LogBlock.util; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; +import java.util.zip.ZipException; + +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.inventory.ItemStack; + +import de.diddiz.LogBlock.LogBlock; + +public class Utils { + public static String newline = System.getProperty("line.separator"); + + public static boolean isInt(String str) { + try { + Integer.parseInt(str); + return true; + } catch (final NumberFormatException ex) { + } + return false; + } + + public static boolean isShort(String str) { + try { + Short.parseShort(str); + return true; + } catch (final NumberFormatException ex) { + } + return false; + } + + public static boolean isByte(String str) { + try { + Byte.parseByte(str); + return true; + } catch (final NumberFormatException ex) { + } + return false; + } + + public static String listing(String[] entries, String delimiter, String finalDelimiter) { + final int len = entries.length; + if (len == 0) { + return ""; + } + if (len == 1) { + return entries[0]; + } + final StringBuilder builder = new StringBuilder(entries[0]); + for (int i = 1; i < len - 1; i++) { + builder.append(delimiter).append(entries[i]); + } + builder.append(finalDelimiter).append(entries[len - 1]); + return builder.toString(); + } + + public static String listing(List entries, String delimiter, String finalDelimiter) { + final int len = entries.size(); + if (len == 0) { + return ""; + } + if (len == 1) { + return entries.get(0).toString(); + } + final StringBuilder builder = new StringBuilder(entries.get(0).toString()); + for (int i = 1; i < len - 1; i++) { + builder.append(delimiter).append(entries.get(i).toString()); + } + builder.append(finalDelimiter).append(entries.get(len - 1).toString()); + return builder.toString(); + } + + public static int parseTimeSpec(String[] spec) { + if (spec == null || spec.length < 1 || spec.length > 2) { + return -1; + } + if (spec.length == 1 && isInt(spec[0])) { + return Integer.valueOf(spec[0]); + } + if (!spec[0].contains(":") && !spec[0].contains(".")) { + if (spec.length == 2) { + if (!isInt(spec[0])) { + return -1; + } + int min = Integer.parseInt(spec[0]); + if (spec[1].startsWith("h")) { + min *= 60; + } else if (spec[1].startsWith("d")) { + min *= 1440; + } + return min; + } else if (spec.length == 1) { + int days = 0, hours = 0, minutes = 0; + int lastIndex = 0, currIndex = 1; + while (currIndex <= spec[0].length()) { + while (currIndex <= spec[0].length() && isInt(spec[0].substring(lastIndex, currIndex))) { + currIndex++; + } + if (currIndex - 1 != lastIndex) { + if (currIndex > spec[0].length()) { + return -1; + } + final String param = spec[0].substring(currIndex - 1, currIndex).toLowerCase(); + if (param.equals("d")) { + days = Integer.parseInt(spec[0].substring(lastIndex, currIndex - 1)); + } else if (param.equals("h")) { + hours = Integer.parseInt(spec[0].substring(lastIndex, currIndex - 1)); + } else if (param.equals("m")) { + minutes = Integer.parseInt(spec[0].substring(lastIndex, currIndex - 1)); + } + } + lastIndex = currIndex; + currIndex++; + } + if (days == 0 && hours == 0 && minutes == 0) { + return -1; + } + return minutes + hours * 60 + days * 1440; + } else { + return -1; + } + } + final String timestamp; + if (spec.length == 1) { + if (spec[0].contains(":")) { + timestamp = new SimpleDateFormat("dd.MM.yyyy").format(System.currentTimeMillis()) + " " + spec[0]; + } else { + timestamp = spec[0] + " 00:00:00"; + } + } else { + timestamp = spec[0] + " " + spec[1]; + } + try { + return (int) ((System.currentTimeMillis() - new SimpleDateFormat("dd.MM.yyyy HH:mm:ss").parse(timestamp).getTime()) / 60000); + } catch (final ParseException ex) { + return -1; + } + } + + public static String spaces(int count) { + final StringBuilder filled = new StringBuilder(count); + for (int i = 0; i < count; i++) { + filled.append(' '); + } + return filled.toString(); + } + + public static String join(String[] s, String delimiter) { + if (s == null || s.length == 0) { + return ""; + } + final int len = s.length; + final StringBuilder builder = new StringBuilder(s[0]); + for (int i = 1; i < len; i++) { + builder.append(delimiter).append(s[i]); + } + return builder.toString(); + } + + /** + * Converts a list of arguments e.g ['lb', 'clearlog', 'world', '"my', 'world', 'of', 'swag"'] + * into a list of arguments with any text encapsulated by quotes treated as one word + * For this particular example: ['lb', 'clearlog', 'world', '"my world of swag"'] + * + * @param args The list of arguments + * @return A new list with the quoted arguments parsed to single values + */ + public static List parseQuotes(List args) { + List newArguments = new ArrayList<>(); + String subjectString = join(args.toArray(new String[args.size()]), " "); + + Pattern regex = Pattern.compile("[^\\s\"']+|\"[^\"]*\"|'[^']*'"); + Matcher regexMatcher = regex.matcher(subjectString); + while (regexMatcher.find()) { + newArguments.add(regexMatcher.group()); + } + + return newArguments; + } + + public static class ExtensionFilenameFilter implements FilenameFilter { + private final String ext; + + public ExtensionFilenameFilter(String ext) { + this.ext = "." + ext; + } + + @Override + public boolean accept(File dir, String name) { + return name.toLowerCase().endsWith(ext); + } + } + + private final static char[] hexArray = "0123456789ABCDEF".toCharArray(); + + public static String mysqlEscapeBytes(byte[] bytes) { + char[] hexChars = new char[bytes.length * 2 + 2]; + hexChars[0] = '0'; + hexChars[1] = 'x'; + for (int j = 0; j < bytes.length; j++) { + int v = bytes[j] & 0xFF; + hexChars[j * 2 + 2] = hexArray[v >>> 4]; + hexChars[j * 2 + 3] = hexArray[v & 0x0F]; + } + return new String(hexChars); + } + + public static String mysqlPrepareBytesForInsertAllowNull(byte[] bytes) { + if (bytes == null) { + return "null"; + } + return "'" + mysqlEscapeBytes(bytes) + "'"; + } + + public static String mysqlTextEscape(String untrusted) { + return untrusted.replace("\\", "\\\\").replace("'", "\\'"); + } + + public static ItemStack loadItemStack(byte[] data) { + if (data == null || data.length == 0) { + return null; + } + YamlConfiguration conf = deserializeYamlConfiguration(data); + return conf == null ? null : conf.getItemStack("stack"); + } + + public static byte[] saveItemStack(ItemStack stack) { + if (stack == null || BukkitUtils.isEmpty(stack.getType())) { + return null; + } + YamlConfiguration conf = new YamlConfiguration(); + conf.set("stack", stack); + return serializeYamlConfiguration(conf); + } + + public static YamlConfiguration deserializeYamlConfiguration(byte[] data) { + if (data == null || data.length == 0) { + return null; + } + YamlConfiguration conf = new YamlConfiguration(); + try { + InputStreamReader reader = new InputStreamReader(new GZIPInputStream(new ByteArrayInputStream(data)), "UTF-8"); + conf.load(reader); + reader.close(); + return conf; + } catch (ZipException | InvalidConfigurationException e) { + LogBlock.getInstance().getLogger().warning("Could not deserialize YamlConfiguration: " + e.getMessage()); + return conf; + } catch (IOException e) { + throw new RuntimeException("IOException should be impossible for ByteArrayInputStream", e); + } + } + + public static byte[] serializeYamlConfiguration(YamlConfiguration conf) { + if (conf == null || conf.getKeys(false).isEmpty()) { + return null; + } + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + OutputStreamWriter writer = new OutputStreamWriter(new GZIPOutputStream(baos), "UTF-8"); + writer.write(conf.saveToString()); + writer.close(); + return baos.toByteArray(); + } catch (IOException e) { + throw new RuntimeException("IOException should be impossible for ByteArrayOutputStream", e); + } + } + + public static String serializeForSQL(YamlConfiguration conf) { + return mysqlPrepareBytesForInsertAllowNull(serializeYamlConfiguration(conf)); + } + + public static double warpDegrees(double degrees) { + double d = degrees % 360.0; + if (d >= 180.0) { + d -= 360.0; + } + if (d < -180.0) { + d += 360.0; + } + return d; + } +}