diff --git a/README.md b/README.md index a4210ed..f76a987 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,49 @@ # WHIMC-PositionTracker [![GitHub release (latest by date)](https://img.shields.io/github/v/release/whimc/Position-Tracker?label=download&logo=github)](https://github.com/whimc/Position-Tracker/releases/latest) -Track player positions to a database +PositionTracker is a Minecraft plugin that tracks player positions and stores them in an SQL database. + +--- ## Building Build the source with Maven: ``` $ mvn install -``` \ No newline at end of file +``` + +--- + +## Configuration + +`debug` is a `boolean` that toggles debug messages in the console. + +### MySQL +| Key | Type | Description | +|------------------|-----------|---------------------------------| +| `mysql.host` | `string` | The host of the database | +| `mysql.port` | `integer` | The port of the database | +| `mysql.database` | `string` | The name of the database to use | +| `mysql.username` | `string` | Username for credentials | +| `mysql.password` | `string` | Password for credentials | + +#### Example +```yaml +debug: false +mysql: + host: localhost + port: 3306 + database: minecraft + username: user + password: pass +``` + +--- + +## Commands +| Command | Description | +|---------------------------|--------------------------------------| +| `/positiontracker status` | Get the status of the tracker | +| `/positiontracker debug` | Toggle the debug messages in console | +| `/positiontracker start` | Start the tracker | +| `/positiontracker stop` | Stop the tracker | + diff --git a/pom.xml b/pom.xml index 560c72b..b5db782 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 edu.whimc WHIMC-PositionTracker - 2.0.5 + 2.1.0 WHIMC Position Tracker Track player positions to a database diff --git a/src/main/java/edu/whimc/positiontracker/JoinLeaveListener.java b/src/main/java/edu/whimc/positiontracker/JoinLeaveListener.java index 1e20340..b0f5ac8 100644 --- a/src/main/java/edu/whimc/positiontracker/JoinLeaveListener.java +++ b/src/main/java/edu/whimc/positiontracker/JoinLeaveListener.java @@ -6,20 +6,38 @@ import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerQuitEvent; +/** + * Handles toggling of data collection based on number of current online players. + */ public class JoinLeaveListener implements Listener { - + /** The instance of the plugin. */ private Tracker plugin; - + + /** + * Constructs a JoinLeaveListener. + * + * @param plugin + */ public JoinLeaveListener(Tracker plugin) { this.plugin = plugin; } - + + /** + * Starts data collection if it's not running already. + * + * @param event the event called when a player joins the server. + */ @EventHandler public void onJoin(PlayerJoinEvent event) { if (plugin.isRunning()) return; plugin.startRunner(); } - + + /** + * Stops data collection when there are no players online. + * + * @param event the event called when a player joins the server. + */ @EventHandler public void onLeave(PlayerQuitEvent event) { if (Bukkit.getOnlinePlayers().size() != 0) return; diff --git a/src/main/java/edu/whimc/positiontracker/Tracker.java b/src/main/java/edu/whimc/positiontracker/Tracker.java index fd79b04..a953170 100644 --- a/src/main/java/edu/whimc/positiontracker/Tracker.java +++ b/src/main/java/edu/whimc/positiontracker/Tracker.java @@ -5,12 +5,20 @@ import edu.whimc.positiontracker.sql.Queryer; +/** + * The main plugin class. + */ public class Tracker extends JavaPlugin { - + /** The current Task's ID. */ private int taskID = -1; + /** The debug mode status. */ private boolean debug = false; + /** The Queryer for the SQL database */ private Queryer queryer; - + + /** + * {@inheritDoc} + */ @Override public void onEnable() { saveDefaultConfig(); @@ -34,7 +42,13 @@ public void onEnable() { } + /** + * Starts player location data collection. + * + * @return whether or not the data collection has been successfully started. + */ public boolean startRunner() { + // check if already running if (taskID != -1) { this.getLogger().warning("The runner has already been started!"); return false; @@ -50,7 +64,13 @@ public boolean startRunner() { return true; } + /** + * Stops player data collection. + * + * @return whether or not the data collection has been successfully stopped. + */ public boolean stopRunner() { + // check if already stopped if (!isRunning()) { return false; } @@ -60,23 +80,41 @@ public boolean stopRunner() { return true; } + /** + * @return if the data collection is running. + */ public boolean isRunning() { return Bukkit.getScheduler().isQueued(taskID); } + /** + * @return the current task ID. + */ public int getTaskID() { return taskID; } + /** + * Prints the passed String into the server logs. + * + * @param str the String to print. + */ public void debugLog(String str) { + // check if in debug mode if (!this.debug) return; this.getLogger().info(str); } + /** + * @return whether or not debug mode is enabled. + */ public boolean getDebug() { return this.debug; } + /** + * @param bool the debug desired status (true = on, false = off). + */ public void setDebug(boolean bool) { this.debug = bool; } diff --git a/src/main/java/edu/whimc/positiontracker/TrackerCommand.java b/src/main/java/edu/whimc/positiontracker/TrackerCommand.java index 523eb18..4653798 100644 --- a/src/main/java/edu/whimc/positiontracker/TrackerCommand.java +++ b/src/main/java/edu/whimc/positiontracker/TrackerCommand.java @@ -5,27 +5,41 @@ import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; +/** + * Main handler for the /positiontracker root command. + */ public class TrackerCommand implements CommandExecutor{ - + /** The instance of the plugin. */ private Tracker plugin; - + + /** + * Constructs a TrackerCommand. + * + * @param plugin the instance of the plugin. + */ public TrackerCommand(Tracker plugin) { this.plugin = plugin; } - + + /** + * {@inheritDoc} + */ public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + // ensure sender is an operator if (!sender.isOp()) { sender.sendMessage(ChatColor.RED + "You must be OP to use this command!"); return true; } - + + // send valid command list if no arguments provided if (args.length == 0) { sendCommands(sender); return true; } String arg = args[0]; - + + // toggling debug mode if (arg.equalsIgnoreCase("debug")) { String message = ChatColor.YELLOW + "Console debug messages are now "; if (plugin.getDebug()) { @@ -41,7 +55,8 @@ public boolean onCommand(CommandSender sender, Command cmd, String label, String } boolean running = plugin.isRunning(); - + + // checking plugin status if (arg.equalsIgnoreCase("status")) { String message = ChatColor.YELLOW + "PositionTracker is currently "; @@ -56,8 +71,10 @@ public boolean onCommand(CommandSender sender, Command cmd, String label, String sender.sendMessage(message); return true; } - + + // starting the tracker if (arg.equalsIgnoreCase("start")) { + // notify sender if already running if (running) { sender.sendMessage(ChatColor.RED + "The tracker is already running!"); return true; @@ -72,8 +89,10 @@ public boolean onCommand(CommandSender sender, Command cmd, String label, String return true; } - + + // stopping the tracker if (arg.equalsIgnoreCase("stop")) { + // notify sender if already stopped if (!running) { sender.sendMessage(ChatColor.RED + "The tracker is already stopped!"); return true; @@ -88,11 +107,16 @@ public boolean onCommand(CommandSender sender, Command cmd, String label, String return true; } - + sendCommands(sender); return true; } - + + /** + * Sends the list of commands and their usage to the sender. + * + * @param sender the command's sender. + */ private void sendCommands(CommandSender sender) { sender.sendMessage(ChatColor.DARK_RED + "" + ChatColor.BOLD + "Position Tracker"); sender.sendMessage(ChatColor.RED + "" + ChatColor.ITALIC + diff --git a/src/main/java/edu/whimc/positiontracker/sql/MySQLConnection.java b/src/main/java/edu/whimc/positiontracker/sql/MySQLConnection.java index d937df2..4ad3957 100644 --- a/src/main/java/edu/whimc/positiontracker/sql/MySQLConnection.java +++ b/src/main/java/edu/whimc/positiontracker/sql/MySQLConnection.java @@ -7,10 +7,15 @@ import edu.whimc.positiontracker.Tracker; +/** + * Handles the connection to the SQL database. + */ public class MySQLConnection { - + /** The SQL Driver class package. */ public static final String DRIVER_CLASS = "com.mysql.jdbc.Driver"; + /** The template for the URL. */ public static final String URL_TEMPLATE = "jdbc:mysql://%s:%s/%s"; + /** The SQL command to create a table. */ public static final String CREATE_TABLE = "CREATE TABLE IF NOT EXISTS `whimc_player_positions` (" + " `rowid` BIGINT AUTO_INCREMENT NOT NULL," + @@ -23,27 +28,44 @@ public class MySQLConnection { " `uuid` VARCHAR(36) NOT NULL," + " `time` BIGINT NOT NULL," + " PRIMARY KEY (`rowid`));"; - + + /** The connection to the SQL database. */ private Connection connection; + /** The SQL database credentials. */ private String host, database, username, password, url; + /** The SQL database port. */ private int port; - + + /** + * Constructs a MySQLConnection. + * + * @param plugin The instance of the plugin. + */ public MySQLConnection(Tracker plugin) { + // fetch credentials and database info from config this.host = plugin.getConfig().getString("mysql.host", "localhost"); this.port = plugin.getConfig().getInt("mysql.port", 3306); this.database = plugin.getConfig().getString("mysql.database", "minecraft"); this.username = plugin.getConfig().getString("mysql.username", "user"); this.password = plugin.getConfig().getString("mysql.password", "pass"); - + + // create the URL with the fetched information this.url = String.format(URL_TEMPLATE, host, port, database); } - + + /** + * Initializes the MySQLConnection. + * + * @return true if the connection succeeds. + */ public boolean initialize() { + // ensure there is a connection if (getConnection() == null) { return false; } try { + // create a table PreparedStatement statement = this.connection.prepareStatement(CREATE_TABLE); statement.execute(); } catch (SQLException e) { @@ -52,13 +74,20 @@ public boolean initialize() { return true; } - + + /** + * Fetches the connection to the SQL database. + * + * @return the SQL database Connection. + */ public Connection getConnection() { try { + // ensure connection exists and is open if (this.connection != null && !this.connection.isClosed()) { return this.connection; } - + + // try connecting if a connection doesn't currently exist Class.forName(DRIVER_CLASS); this.connection = DriverManager.getConnection(this.url, this.username, this.password); } catch (SQLException | ClassNotFoundException e) { @@ -67,8 +96,12 @@ public Connection getConnection() { return this.connection; } - + + /** + * Closes the connection to the SQL database. + */ public void closeConnection() { + // ensure connection exists if (this.connection != null) { try { this.connection.close(); diff --git a/src/main/java/edu/whimc/positiontracker/sql/Queryer.java b/src/main/java/edu/whimc/positiontracker/sql/Queryer.java index b611dac..c5add41 100644 --- a/src/main/java/edu/whimc/positiontracker/sql/Queryer.java +++ b/src/main/java/edu/whimc/positiontracker/sql/Queryer.java @@ -15,15 +15,29 @@ import edu.whimc.positiontracker.Tracker; +/** + * Handles adding position data to the SQL Database. + */ public class Queryer { + /** + * An entry containing data on the current player's position and time it was created. + */ public class PositionEntry { - + /** The x, y, and z coordinates representing the position. */ private final int x, y, z; + /** The current world, biome, and player's username. */ private final String world, biome, username; + /** The unique ID for this entry. */ private final UUID uuid; + /** The time the entry was created. */ private final Timestamp time; + /** + * Constructs a PositionEntry. + * + * @param player the current player. + */ public PositionEntry(Player player) { Location loc = player.getLocation(); this.x = loc.getBlockX(); @@ -42,6 +56,12 @@ public PositionEntry(Player player) { this.time = new Timestamp(System.currentTimeMillis()); } + /** + * Adds this entry's data to the PreparedStatement's batch + * + * @param statement the SQL PreparedStatement. + * @throws SQLException when the statement cannot be added. + */ public void addInsertionToBatch(PreparedStatement statement) throws SQLException { statement.setInt(1, this.x); statement.setInt(2, this.y); @@ -56,14 +76,23 @@ public void addInsertionToBatch(PreparedStatement statement) throws SQLException } + /** SQL query to insert position data. */ private static final String INSERT_FORMAT = "INSERT INTO `whimc_player_positions` " + "(x, y, z, world, biome, username, uuid, time) " + "VALUES(?, ?, ?, ?, ?, ?, ?, ?)"; + /** The instance of the plugin. */ private Tracker plugin; + /** The connection to the SQL database. */ private MySQLConnection sqlConnection; + /** + * Constructs a Queryer. + * + * @param plugin the instance of the plugin. + * @param callback the event callback. + */ public Queryer(Tracker plugin, Consumer callback) { this.plugin = plugin; this.sqlConnection = new MySQLConnection(plugin); @@ -76,11 +105,16 @@ public Queryer(Tracker plugin, Consumer callback) { }); } + /** + * Stores the position data. + */ public void storePositionData() { + // get position data for all online players final List entries = Bukkit.getOnlinePlayers().stream() .map(PositionEntry::new) .collect(Collectors.toList()); + // add data to database asynchronously Bukkit.getScheduler().runTaskAsynchronously(this.plugin, () -> { try (Connection connection = this.sqlConnection.getConnection()) { connection.setAutoCommit(false);