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

351 lines
14 KiB
Java

package de.diddiz.LogBlock;
import de.diddiz.LogBlock.config.Config;
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.WorldEditHelper;
import de.diddiz.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 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 onEnable() {
logblock = this;
BukkitUtils.isDoublePlant(Material.AIR); // Force static code to run
final PluginManager pm = getPluginManager();
consumer = new Consumer(this);
try {
Config.load(this);
} catch (final Exception ex) {
getLogger().log(Level.SEVERE, "Could not load LogBlock config! " + ex.getMessage());
pm.disablePlugin(this);
return;
}
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.executeQuery("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);
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.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) || isLogging(Logging.BONEMEALSTRUCTUREGROW)) {
pm.registerEvents(new StructureGrowLogging(this), this);
}
if (isLogging(Logging.GRASSGROWTH) || isLogging(Logging.MYCELIUMSPREAD) || isLogging(Logging.VINEGROWTH) || isLogging(Logging.MUSHROOMSPREAD)) {
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 (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 <i>query-parameters</i>
*
* 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<BlockChange> 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<BlockChange> 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;
}
}