Incomplete entity logging

This commit is contained in:
Brokkonaut
2018-11-08 05:16:21 +01:00
parent 5ad0f06d16
commit 707e0a1eed
11 changed files with 458 additions and 15 deletions

View File

@ -3,6 +3,7 @@ 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;
@ -86,9 +87,14 @@ public class Actor {
public static Actor actorFromEntity(Entity entity) {
if (entity instanceof Player) {
return new Actor(entityName(entity), entity.getUniqueId());
} else {
return new Actor(entityName(entity));
}
if (entity instanceof Projectile) {
ProjectileSource shooter = ((Projectile) entity).getShooter();
if (shooter != null) {
return actorFromProjectileSource(shooter);
}
}
return new Actor(entityName(entity));
}
public static Actor actorFromEntity(EntityType entity) {
@ -109,17 +115,19 @@ public class Actor {
/**
* 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
* 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
* <p>
* 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
* <p>
* 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
* @param actorName
* String of unknown origin
* @return
*/
public static Actor actorFromString(String actorName) {
Collection<? extends Player> players = Bukkit.getServer().getOnlinePlayers();

View File

@ -26,16 +26,19 @@ 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.Sign;
import org.bukkit.block.data.BlockData;
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;
@ -43,6 +46,7 @@ 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.BlockStateCodecSign;
import de.diddiz.LogBlock.blockstate.BlockStateCodecs;
import de.diddiz.LogBlock.config.Config;
@ -52,8 +56,10 @@ import de.diddiz.util.Utils;
public class Consumer extends Thread {
private final Deque<Row> queue = new ArrayDeque<Row>();
private final LogBlock logblock;
private final Map<Actor, Integer> playerIds = new HashMap<Actor, Integer>();
private final Map<Actor, Integer> uncommitedPlayerIds = new HashMap<Actor, Integer>();
private final Map<Actor, Integer> playerIds = new HashMap<>();
private final Map<Actor, Integer> uncommitedPlayerIds = new HashMap<>();
private final Map<World, Map<UUID, Integer>> uncommitedEntityIds = new HashMap<>();
private long addEntryCounter;
private long nextWarnCounter;
@ -495,6 +501,7 @@ public class Consumer extends Thread {
currentRows.clear();
playerIds.putAll(uncommitedPlayerIds);
uncommitedPlayerIds.clear();
uncommitedEntityIds.clear();
lastCommitsFailed = 0;
}
} catch (Exception e) {
@ -520,6 +527,7 @@ public class Consumer extends Thread {
currentRows.clear();
batchHelper.reset();
uncommitedPlayerIds.clear();
uncommitedEntityIds.clear();
if (conn != null) {
try {
conn.close();
@ -657,6 +665,48 @@ public class Consumer extends Thread {
return uncommitedPlayerIds.containsKey(actor);
}
private int getEntityUUID(Connection conn, World world, UUID uuid) throws SQLException {
Map<UUID, Integer> uncommitedEntityIdsHere = uncommitedEntityIds.get(world);
if (uncommitedEntityIdsHere == null) {
uncommitedEntityIdsHere = new HashMap<>();
uncommitedEntityIds.put(world, uncommitedEntityIdsHere);
}
Integer 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.getInt(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.getInt(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);
@ -696,6 +746,13 @@ public class Consumer extends Thread {
addQueueLast(new BlockRow(loc, actor, replacedMaterialId, replacedStateId, Utils.serializeYamlConfiguration(stateBefore), typeMaterialId, typeStateId, Utils.serializeYamlConfiguration(stateAfter), ca));
}
public void queueEntityModification(Actor actor, UUID entityId, EntityType entityType, Location loc, EntityChangeType changeType, YamlConfiguration data) {
if (actor == null || loc == null || changeType == null || entityId == null || entityType == null || hiddenPlayers.contains(actor.getName().toLowerCase()) || !isLogged(loc.getWorld())) {
return;
}
addQueueLast(new EntityRow(loc, actor, entityType, entityId, changeType, Utils.serializeYamlConfiguration(data)));
}
private String playerID(Actor actor) {
if (actor == null) {
return "NULL";
@ -965,6 +1022,67 @@ public class Consumer extends Thread {
}
}
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(entityid.toString()) + "' FROM `" + table + "-entityids` WHERE NOT EXISTS (SELECT NULL FROM `" + table + "-entityids` WHERE entityuuid = '" + mysqlTextEscape(entityid.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(entityid.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.RETURN_GENERATED_KEYS);
smt.setLong(1, date);
smt.setInt(2, sourceActor);
smt.setInt(3, getEntityUUID(conn, loc.getWorld(), entityid));
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 int safeY(Location loc) {
int safeY = loc.getBlockY();
if (safeY < 0)

View File

@ -0,0 +1,98 @@
package de.diddiz.LogBlock;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.UUID;
import org.bukkit.Location;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.LivingEntity;
import de.diddiz.LogBlock.config.Config;
public class EntityChange implements LookupCacheElement {
public static enum EntityChangeType {
CREATE,
KILL,
MODIFY,
ADDEQUIP,
REMOVEEQUIP;
private static EntityChangeType[] values = values();
public static EntityChangeType valueOf(int ordinal) {
return values[ordinal];
}
}
public final long id, date;
public final Location loc;
public final Actor actor;
public final EntityType type;
public final UUID entityid;
public final EntityChangeType changeType;
public final byte[] data;
public EntityChange(long date, Location loc, Actor actor, EntityType type, UUID entityid, EntityChangeType changeType, byte[] data) {
id = 0;
this.date = date;
this.loc = loc;
this.actor = actor;
this.type = type;
this.entityid = entityid;
this.changeType = changeType;
this.data = data;
}
public EntityChange(ResultSet rs, QueryParams p) throws SQLException {
id = p.needId ? rs.getInt("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;
type = p.needType ? EntityTypeConverter.getEntityType(rs.getInt("entitytypeid")) : null;
entityid = p.needType ? UUID.fromString(rs.getString("entityuuid")) : null;
changeType = p.needType ? EntityChangeType.valueOf(rs.getInt("action")) : null;
data = p.needType ? rs.getBytes("data") : null;
}
@Override
public String toString() {
final StringBuilder msg = new StringBuilder();
if (date > 0) {
msg.append(Config.formatter.format(date)).append(" ");
}
if (actor != null) {
msg.append(actor.getName()).append(" ");
}
if (type != null) {
boolean living = LivingEntity.class.isAssignableFrom(type.getEntityClass()) && !ArmorStand.class.isAssignableFrom(type.getDeclaringClass());
if (changeType == EntityChangeType.CREATE) {
msg.append("created ");
} else if (changeType == EntityChangeType.KILL) {
msg.append(living ? "killed " : "destroyed ");
} else if (changeType == EntityChangeType.ADDEQUIP) {
msg.append("added an item to ");
} else if (changeType == EntityChangeType.REMOVEEQUIP) {
msg.append("removed an item from ");
} else if (changeType == EntityChangeType.MODIFY) {
msg.append("modified ");
}
msg.append(type.name());
}
if (loc != null) {
msg.append(" at ").append(loc.getBlockX()).append(":").append(loc.getBlockY()).append(":").append(loc.getBlockZ());
}
return msg.toString();
}
@Override
public Location getLocation() {
return loc;
}
@Override
public String getMessage() {
return toString();
}
}

View File

@ -0,0 +1,82 @@
package de.diddiz.LogBlock;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Arrays;
import java.util.HashMap;
import java.util.logging.Level;
import org.bukkit.entity.EntityType;
public class EntityTypeConverter {
private static EntityType[] idToEntityType = new EntityType[10];
private static HashMap<EntityType, Integer> entityTypeToId = new HashMap<>();
private static int nextEntityTypeId;
public static int getOrAddEntityTypeId(EntityType entityType) {
Integer key = entityTypeToId.get(entityType);
while (key == null) {
key = nextEntityTypeId;
Connection conn = LogBlock.getInstance().getConnection();
try {
conn.setAutoCommit(false);
PreparedStatement smt = conn.prepareStatement("INSERT IGNORE INTO `lb-entitytypes` (id, name) VALUES (?, ?)");
smt.setInt(1, key);
smt.setString(2, entityType.name());
boolean couldAdd = smt.executeUpdate() > 0;
conn.commit();
smt.close();
if (couldAdd) {
internalAddEntityType(key, entityType);
} else {
initializeEntityTypes(conn);
}
} catch (SQLException e) {
LogBlock.getInstance().getLogger().log(Level.SEVERE, "Could not update lb-entitytypes", e);
} finally {
try {
conn.close();
} catch (SQLException e) {
// ignored
}
}
key = entityTypeToId.get(entityType);
}
return key.intValue();
}
public static EntityType getEntityType(int entityTypeId) {
return idToEntityType[entityTypeId];
}
public static void initializeEntityTypes(Connection connection) throws SQLException {
Statement smt = connection.createStatement();
ResultSet rs = smt.executeQuery("SELECT id, name FROM `lb-entitytypes`");
while (rs.next()) {
int key = rs.getInt(1);
EntityType entityType = EntityType.valueOf(rs.getString(2));
internalAddEntityType(key, entityType);
}
rs.close();
smt.close();
connection.close();
}
private synchronized static void internalAddEntityType(int key, EntityType entityType) {
entityTypeToId.put(entityType, key);
int length = idToEntityType.length;
while (length <= key) {
length = (length * 3 / 2) + 5;
}
if (length > idToEntityType.length) {
idToEntityType = Arrays.copyOf(idToEntityType, length);
}
idToEntityType[key] = entityType;
if (nextEntityTypeId <= key) {
nextEntityTypeId = key + 1;
}
}
}

View File

@ -5,6 +5,8 @@ import de.diddiz.LogBlock.listeners.*;
import de.diddiz.LogBlock.questioner.Questioner;
import de.diddiz.util.BukkitUtils;
import de.diddiz.util.MySQLConnectionPool;
import de.diddiz.worldedit.AdvancedKillLogging;
import de.diddiz.worldedit.WorldEditHelper;
import de.diddiz.worldedit.WorldEditLoggingHook;
import org.bukkit.ChatColor;
import org.bukkit.Material;
@ -89,6 +91,7 @@ public class LogBlock extends JavaPlugin {
updater.checkTables();
MaterialConverter.initializeMaterials(getConnection());
MaterialConverter.getOrAddMaterialId(Material.AIR.getKey()); // AIR must be the first entry
EntityTypeConverter.initializeEntityTypes(getConnection());
if (updater.update()) {
load(this);
}
@ -100,7 +103,7 @@ public class LogBlock extends JavaPlugin {
return;
}
if (pm.getPlugin("WorldEdit") != null) {
if (WorldEditHelper.hasWorldEdit()) {
new WorldEditLoggingHook(this).hook();
}
commandsHandler = new CommandsHandler(this);
@ -186,6 +189,7 @@ public class LogBlock extends JavaPlugin {
if (isLogging(Logging.DRAGONEGGTELEPORT)) {
pm.registerEvents(new DragonEggLogging(this), this);
}
pm.registerEvents(new AdvancedKillLogging(this), this);
}
@Override

View File

@ -3,12 +3,13 @@ package de.diddiz.LogBlock;
import de.diddiz.LogBlock.config.Config;
import de.diddiz.util.Utils;
import de.diddiz.worldedit.CuboidRegion;
import de.diddiz.worldedit.WorldEditHelper;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import java.util.*;
@ -120,6 +121,17 @@ public final class QueryParams implements Cloneable {
}
return from;
}
if (bct == BlockChangeType.ENTITIES) {
String from = "FROM `" + getTable() + "-entities` ";
if (needPlayer || players.size() > 0) {
from += "INNER JOIN `lb-players` USING (playerid) ";
}
if (!needCount && needType) {
from += "LEFT JOIN `" + getTable() + "-entityids` USING (entityid) ";
}
return from;
}
String from = "FROM `" + getTable() + "-blocks` ";
@ -262,6 +274,9 @@ public final class QueryParams implements Cloneable {
}
throw new IllegalStateException("Invalid summarization for kills");
}
if (bct == BlockChangeType.ENTITIES) {
throw new IllegalStateException("Not implemented yet");
}
if (sum == SummarizationMode.TYPES) {
return "SELECT type, SUM(created) AS created, SUM(destroyed) AS destroyed FROM ((SELECT type, count(*) AS created, 0 AS destroyed FROM `" + getTable() + "-blocks` INNER JOIN `lb-players` USING (playerid) " + getWhere(BlockChangeType.CREATED) + "GROUP BY type) UNION (SELECT replaced AS type, 0 AS created, count(*) AS destroyed FROM `" + getTable() + "-blocks` INNER JOIN `lb-players` USING (playerid) " + getWhere(BlockChangeType.DESTROYED) + "GROUP BY replaced)) AS t GROUP BY type ORDER BY SUM(created) + SUM(destroyed) " + order + " " + getLimit();
} else {
@ -281,6 +296,8 @@ public final class QueryParams implements Cloneable {
title.append("chat messages ");
} else if (bct == BlockChangeType.KILLS) {
title.append("kills ");
} else if (bct == BlockChangeType.ENTITIES) {
title.append("entity changes ");
} else {
if (!types.isEmpty()) {
if (excludeBlocksMode) {
@ -745,9 +762,8 @@ public final class QueryParams implements Cloneable {
if (player == null) {
throw new IllegalArgumentException("You have to be a player to use selection");
}
final Plugin we = player.getServer().getPluginManager().getPlugin("WorldEdit");
if (we != null) {
setSelection(CuboidRegion.fromPlayerSelection(player, we));
if (WorldEditHelper.hasWorldEdit()) {
setSelection(CuboidRegion.fromPlayerSelection(player));
} else {
throw new IllegalArgumentException("WorldEdit not found!");
}
@ -786,6 +802,8 @@ public final class QueryParams implements Cloneable {
bct = BlockChangeType.CHAT;
} else if (param.equals("kills")) {
bct = BlockChangeType.KILLS;
} else if (param.equals("entities")) {
bct = BlockChangeType.ENTITIES;
} else if (param.equals("all")) {
bct = BlockChangeType.ALL;
} else if (param.equals("limit")) {
@ -963,7 +981,7 @@ public final class QueryParams implements Cloneable {
}
public static enum BlockChangeType {
ALL, BOTH, CHESTACCESS, CREATED, DESTROYED, CHAT, KILLS
ALL, BOTH, CHESTACCESS, CREATED, DESTROYED, CHAT, KILLS, ENTITIES
}
public static enum Order {

View File

@ -779,15 +779,17 @@ class Updater {
}
createTable(dbm, state, "lb-materials", "(id INT UNSIGNED NOT NULL, name VARCHAR(255) NOT NULL, PRIMARY KEY (id)) DEFAULT CHARSET " + charset);
createTable(dbm, state, "lb-blockstates", "(id INT UNSIGNED NOT NULL, name VARCHAR(255) NOT NULL, PRIMARY KEY (id)) DEFAULT CHARSET " + charset);
createTable(dbm, 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(dbm, 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 UNSIGNED NOT NULL, z MEDIUMINT NOT NULL, PRIMARY KEY (id), KEY coords (x, z, y), KEY date (date), KEY playerid (playerid))");
// createTable(dbm, state, wcfg.table + "-sign", "(id INT UNSIGNED NOT NULL, signtext VARCHAR(255) NOT NULL, PRIMARY KEY (id)) DEFAULT CHARSET " + charset);
createTable(dbm, state, wcfg.table + "-chestdata", "(id INT UNSIGNED NOT NULL, item MEDIUMBLOB, itemremove TINYINT, itemtype SMALLINT NOT NULL DEFAULT '0', PRIMARY KEY (id))");
createTable(dbm, state, wcfg.table + "-state", "(id INT UNSIGNED NOT NULL, replacedState MEDIUMBLOB NULL, typeState MEDIUMBLOB NULL, PRIMARY KEY (id))");
if (wcfg.isLogging(Logging.KILL)) {
createTable(dbm, 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(dbm, 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(dbm, 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))");
}
state.close();
conn.close();

View File

@ -16,6 +16,7 @@ public class WorldConfig extends LoggingEnabledMapping {
public final String selectBlockActorIdStatementString;
public final String insertBlockStateStatementString;
public final String insertBlockChestDataStatementString;
public final String insertEntityStatementString;
public WorldConfig(String world, File file) throws IOException {
this.world = world;
@ -42,5 +43,6 @@ public class WorldConfig extends LoggingEnabledMapping {
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(?), ?, ?, ?, ?, ?, ?, ?, ?)";
}
}

View File

@ -0,0 +1,50 @@
package de.diddiz.worldedit;
import org.bukkit.Location;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Animals;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Villager;
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 de.diddiz.LogBlock.Actor;
import de.diddiz.LogBlock.EntityChange;
import de.diddiz.LogBlock.LogBlock;
import de.diddiz.LogBlock.listeners.LoggingListener;
public class AdvancedKillLogging extends LoggingListener {
public AdvancedKillLogging(LogBlock lb) {
super(lb);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onEntityDeath(EntityDeathEvent event) {
LivingEntity entity = event.getEntity();
if (!(entity instanceof Animals) && !(entity instanceof Villager)) {
return;
}
Actor killer;
EntityDamageEvent lastDamage = entity.getLastDamageCause();
if (lastDamage instanceof EntityDamageByEntityEvent) {
killer = Actor.actorFromEntity(((EntityDamageByEntityEvent) lastDamage).getDamager());
} else {
killer = new Actor(lastDamage.getCause().toString());
}
Location location = entity.getLocation();
YamlConfiguration data = new YamlConfiguration();
data.set("x", location.getX());
data.set("y", location.getX());
data.set("z", location.getX());
data.set("yaw", location.getYaw());
data.set("pitch", location.getPitch());
data.set("worldedit", WorldEditHelper.serializeEntity(entity));
consumer.queueEntityModification(killer, entity.getUniqueId(), entity.getType(), location, EntityChange.EntityChangeType.KILL, data);
}
}

View File

@ -29,7 +29,8 @@ public class CuboidRegion implements Cloneable {
this.max.setZ(Math.max(first.getBlockZ(),second.getBlockZ()));
}
public static CuboidRegion fromPlayerSelection(Player player, Plugin worldEditPlugin) {
public static CuboidRegion fromPlayerSelection(Player player) {
Plugin worldEditPlugin = player.getServer().getPluginManager().getPlugin("WorldEdit");
LocalSession session = ((WorldEditPlugin) worldEditPlugin).getSession(player);
World world = player.getWorld();
com.sk89q.worldedit.world.World weWorld = BukkitAdapter.adapt(world);

View File

@ -0,0 +1,60 @@
package de.diddiz.worldedit;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import org.bukkit.Bukkit;
import org.bukkit.entity.Entity;
import org.bukkit.plugin.Plugin;
import com.sk89q.jnbt.NBTOutputStream;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.entity.BaseEntity;
public class WorldEditHelper {
private static boolean checkedForWorldEdit;
private static boolean hasWorldEdit;
public static boolean hasWorldEdit() {
if (!checkedForWorldEdit) {
checkedForWorldEdit = true;
Plugin worldEdit = Bukkit.getPluginManager().getPlugin("WorldEdit");
hasWorldEdit = worldEdit != null;
Internal.setWorldEdit(worldEdit);
}
return hasWorldEdit;
}
public static byte[] serializeEntity(Entity entity) {
if (!hasWorldEdit()) {
return null;
}
return Internal.serializeEntity(entity);
}
private static class Internal {
// private static WorldEditPlugin worldEdit;
public static void setWorldEdit(Plugin worldEdit) {
// Internal.worldEdit = (WorldEditPlugin) worldEdit;
}
public static byte[] serializeEntity(Entity entity) {
com.sk89q.worldedit.entity.Entity weEntity = BukkitAdapter.adapt(entity);
BaseEntity state = weEntity.getState();
if (state != null) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
NBTOutputStream nbtos = new NBTOutputStream(baos);
nbtos.writeNamedTag("entity", state.getNbtData());
nbtos.close();
return baos.toByteArray();
} catch (IOException e) {
throw new RuntimeException("This IOException should be impossible", e);
}
}
return null;
}
}
}