From c4279130f711465400371330c6a59c4fbace4c79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bro=C4=8Dko?= Date: Sun, 7 Oct 2018 03:06:17 +0200 Subject: [PATCH] Stats: finished --- Stats/resources/activity_update.sql | 8 -- Stats/resources/create.sql | 13 +- Stats/resources/players_update.sql | 7 - Stats/resources/session.sql | 9 ++ Stats/src/cz/marwland/mc/features/Stats.java | 126 ++---------------- .../mc/features/stats/PlayerCounter.java | 87 ------------ .../mc/features/stats/PlayerSession.java | 68 ++++++++++ .../mc/features/stats/SessionManager.java | 110 +++++++++++++++ 8 files changed, 202 insertions(+), 226 deletions(-) delete mode 100644 Stats/resources/activity_update.sql delete mode 100644 Stats/resources/players_update.sql create mode 100644 Stats/resources/session.sql delete mode 100644 Stats/src/cz/marwland/mc/features/stats/PlayerCounter.java create mode 100644 Stats/src/cz/marwland/mc/features/stats/PlayerSession.java create mode 100644 Stats/src/cz/marwland/mc/features/stats/SessionManager.java diff --git a/Stats/resources/activity_update.sql b/Stats/resources/activity_update.sql deleted file mode 100644 index 80a5763..0000000 --- a/Stats/resources/activity_update.sql +++ /dev/null @@ -1,8 +0,0 @@ -INSERT INTO `{prefix}activity` - (`uuid`, `date`, `kills`, `deaths`, `playtime`) VALUES - (?, ?, ?, ?, ?) - ON DUPLICATE KEY - UPDATE - `kills` = `kills` + VALUES(`kills`), - `deaths` = `deaths` + VALUES(`deaths`), - `playtime` = `playtime` + VALUES(`playtime`); \ No newline at end of file diff --git a/Stats/resources/create.sql b/Stats/resources/create.sql index f1ac1e2..1d32f43 100644 --- a/Stats/resources/create.sql +++ b/Stats/resources/create.sql @@ -1,16 +1,9 @@ -CREATE TABLE IF NOT EXISTS `{prefix}players` ( +CREATE TABLE IF NOT EXISTS `{prefix}sessions` ( `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, `uuid` binary(16) NOT NULL, `serverNick` varchar(32) NOT NULL, - `voteCount` int UNSIGNED DEFAULT 0, - PRIMARY KEY `id` (`id`), - UNIQUE KEY `uuid` (`uuid`) -) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; - -CREATE TABLE IF NOT EXISTS `{prefix}activity` ( - `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, - `uuid` binary(16) NOT NULL, - `date` DATE NOT NULL, + `ip` int UNSIGNED NOT NULL, + `date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `kills` int DEFAULT 0, `deaths` int UNSIGNED DEFAULT 0, `playtime` mediumint UNSIGNED DEFAULT 0, diff --git a/Stats/resources/players_update.sql b/Stats/resources/players_update.sql deleted file mode 100644 index 99a7b7b..0000000 --- a/Stats/resources/players_update.sql +++ /dev/null @@ -1,7 +0,0 @@ -INSERT INTO `{prefix}players` - (`uuid`, `serverNick`) VALUES - (?, ?) - ON DUPLICATE KEY - UPDATE - `serverNick` = VALUES(`serverNick`), - `voteCount` = `voteCount` + VALUES(`voteCount`); \ No newline at end of file diff --git a/Stats/resources/session.sql b/Stats/resources/session.sql new file mode 100644 index 0000000..16b451d --- /dev/null +++ b/Stats/resources/session.sql @@ -0,0 +1,9 @@ +INSERT INTO `{prefix}sessions` + (`uuid`, `serverNick`, `ip`, `date`, `kills`, `deaths`, `playtime`) VALUES + (?, ?, ?, ?, ?, ?, ?) + ON DUPLICATE KEY + UPDATE + `kills` = `kills` + VALUES(`kills`), + `deaths` = `deaths` + VALUES(`deaths`), + `playtime` = `playtime` + VALUES(`playtime`), + `date` = VALUES(`date`); \ No newline at end of file diff --git a/Stats/src/cz/marwland/mc/features/Stats.java b/Stats/src/cz/marwland/mc/features/Stats.java index a9f004a..285ac59 100644 --- a/Stats/src/cz/marwland/mc/features/Stats.java +++ b/Stats/src/cz/marwland/mc/features/Stats.java @@ -1,12 +1,8 @@ package cz.marwland.mc.features; import java.io.IOException; -import java.sql.Connection; -import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.Arrays; -import java.util.HashMap; -import java.util.logging.Level; import org.bukkit.command.CommandSender; import org.bukkit.command.defaults.BukkitCommand; @@ -16,23 +12,16 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerQuitEvent; -import org.bukkit.scheduler.BukkitRunnable; -import org.bukkit.scheduler.BukkitTask; import cz.marwland.mc.core.MarwCore; import cz.marwland.mc.core.features.Feature; import cz.marwland.mc.core.storage.SQLStorage; -import cz.marwland.mc.core.util.FileUtil; -import cz.marwland.mc.core.util.UuidUtils; -import cz.marwland.mc.features.stats.PlayerCounter; +import cz.marwland.mc.features.stats.SessionManager; public class Stats extends Feature { - + private final SQLStorage database = MarwCore.getInstance().getStorage(); - public HashMap counters = new HashMap<>(); - private final long WRITE_PERIOD_TICKS = 20 * 15; - private String playersUpdateQuery; - private BukkitTask recordTask; + public SessionManager manager; public Stats() { super(); @@ -51,42 +40,20 @@ public class Stats extends Feature { return getPermissionPath(); } }); + manager = new SessionManager(); } @Override public void onEnable() { super.onEnable(); createTables(); - loadSQLQueries(); - PlayerCounter.init(this.database); - - new BukkitRunnable() { - @Override - public void run() { - for (Player p : MarwCore.getInstance().getServer().getOnlinePlayers()) { - recordPlayer(p); - } - } - }.runTaskAsynchronously(MarwCore.getInstance()); - for (Player p : MarwCore.getInstance().getServer().getOnlinePlayers()) { - this.startCountingPlayer(p); - } - - recordTask = new BukkitRunnable() { - @Override - public void run() { - pushActivityToSql(); - } - }.runTaskTimerAsynchronously(MarwCore.getInstance(), 5, WRITE_PERIOD_TICKS); + manager.start(); } @Override public void onDisable() { super.onDisable(); - recordTask.cancel(); - for (Player p : MarwCore.getInstance().getServer().getOnlinePlayers()) { - this.stopCountingPlayer(p); - } + manager.shutdown(); } public void createTables() { @@ -96,95 +63,26 @@ public class Stats extends Feature { e.printStackTrace(); } } - - public void loadSQLQueries() { - if (playersUpdateQuery == null) { - try { - playersUpdateQuery = FileUtil.readFileFromJAR(PlayerCounter.class, "/resources/players_update.sql"); - playersUpdateQuery = this.database.getStatementProcessor().apply(playersUpdateQuery); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - - public void recordPlayer(Player p) { - try(Connection conn = this.database.getConnectionFactory().getConnection()) { - PreparedStatement prepst = conn.prepareStatement(this.playersUpdateQuery); - prepst.setBytes(1, UuidUtils.asBytes(p.getUniqueId())); - prepst.setString(2, p.getName()); - prepst.execute(); - } catch (SQLException e) { - e.printStackTrace(); - } - } @EventHandler public void onPlayerJoin(PlayerJoinEvent event) { - Player p = event.getPlayer(); - new BukkitRunnable() { - @Override - public void run() { - recordPlayer(p); - } - }.runTaskAsynchronously(MarwCore.getInstance()); - this.startCountingPlayer(event.getPlayer()); + manager.startPlayer(event.getPlayer()); } @EventHandler public void onPlayerQuit(PlayerQuitEvent event) { - this.stopCountingPlayer(event.getPlayer()); + manager.endPlayer(event.getPlayer()); } @EventHandler public void onPlayerDeath(PlayerDeathEvent event) { - PlayerCounter targetPC = this.counters.get(event.getEntity()); - if (targetPC != null) { - targetPC.addDeath(); - } + this.manager.getPlayerSession(event.getEntity()).addDeath(); + Entity killerEntity = event.getEntity().getLastDamageCause().getEntity(); if (killerEntity instanceof Player) { Player killerPlayer = (Player) killerEntity; - PlayerCounter killerPC = this.counters.get(killerPlayer); - if (killerPC != null) { - killerPC.addKill(); - } // else should never happen in both cases - players are always tracked - } - } - - public void startCountingPlayer(Player p) { - if (this.counters.containsKey(p)) - this.counters.get(p).start(); - else { - PlayerCounter pc = new PlayerCounter(p); - this.counters.put(p, pc); - pc.start(); - } - } - - public void stopCountingPlayer(Player p) { - if (!this.counters.containsKey(p)) - return; - PlayerCounter pc = this.counters.get(p); - pc.stop(); - this.counters.remove(p); - } - - public void pushActivityToSql() { - try(Connection conn = this.database.getConnectionFactory().getConnection();) { - PreparedStatement prepst = conn.prepareStatement(PlayerCounter.getRawQuery()); - int i = 0; - for (PlayerCounter pc : this.counters.values()) { - if (!pc.shouldRecord()) - continue; - pc.addToSql(prepst); - pc.start(); - i++; - } - if (i > 0) - prepst.execute(); - } catch (SQLException e) { - e.printStackTrace(); + if (!killerPlayer.equals(killerEntity) && killerPlayer.isOnline()) + this.manager.getPlayerSession(killerPlayer).addKill(); } } diff --git a/Stats/src/cz/marwland/mc/features/stats/PlayerCounter.java b/Stats/src/cz/marwland/mc/features/stats/PlayerCounter.java deleted file mode 100644 index 6a3db98..0000000 --- a/Stats/src/cz/marwland/mc/features/stats/PlayerCounter.java +++ /dev/null @@ -1,87 +0,0 @@ -package cz.marwland.mc.features.stats; - -import java.io.IOException; -import java.sql.PreparedStatement; -import java.sql.SQLException; - -import org.bukkit.entity.Player; - -import cz.marwland.mc.core.storage.SQLStorage; -import cz.marwland.mc.core.util.FileUtil; -import cz.marwland.mc.core.util.UuidUtils; - -public class PlayerCounter { - - private static String updateQuery; - - private Player player; - - private long lastRecorded = 0; - private boolean doRecord = false; - private int killsSinceRecord = 0; - private int deathsSinceRecord = 0; - - - public PlayerCounter(Player player) { - this.player = player; - } - - public static void init(SQLStorage db) { - if (updateQuery == null) { - try { - updateQuery = FileUtil.readFileFromJAR(PlayerCounter.class, "/resources/activity_update.sql"); - updateQuery = db.getStatementProcessor().apply(updateQuery); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - - public void start() { - this.lastRecorded = System.currentTimeMillis(); - this.killsSinceRecord = 0; - this.deathsSinceRecord = 0; - this.doRecord = true; - } - - public void stop() { - this.doRecord = false; - } - - public Player getPlayer() { - return this.player; - } - - public void addToSql(PreparedStatement prepst) { - long playtime = (System.currentTimeMillis() - this.lastRecorded) / 1000; - try { - prepst.setBytes(1, UuidUtils.asBytes(player.getUniqueId())); - prepst.setDate(2, new java.sql.Date(System.currentTimeMillis())); - prepst.setInt(3, this.killsSinceRecord); - prepst.setInt(4, this.deathsSinceRecord); - prepst.setLong(5, playtime); - prepst.addBatch(); - } catch (SQLException e) { - // TODO: Notify admins about errors. - e.printStackTrace(); - } - - } - - public static String getRawQuery() { - return updateQuery; - } - - public void addKill() { - this.killsSinceRecord++; - } - - public void addDeath() { - this.deathsSinceRecord++; - } - - public boolean shouldRecord() { - return this.doRecord; - } - -} diff --git a/Stats/src/cz/marwland/mc/features/stats/PlayerSession.java b/Stats/src/cz/marwland/mc/features/stats/PlayerSession.java new file mode 100644 index 0000000..34ae37c --- /dev/null +++ b/Stats/src/cz/marwland/mc/features/stats/PlayerSession.java @@ -0,0 +1,68 @@ +package cz.marwland.mc.features.stats; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.sql.PreparedStatement; +import java.sql.SQLException; + +import org.bukkit.entity.Player; + +import cz.marwland.mc.core.util.UuidUtils; + +public class PlayerSession { + + private Player player; + + private volatile long lastRecorded = 0; + private volatile int killsSinceRecord = 0; + private volatile int deathsSinceRecord = 0; + private java.sql.Timestamp dateJoined; + private int ipAddress; + + public PlayerSession(Player player) { + this.player = player; + this.lastRecorded = System.currentTimeMillis(); + this.dateJoined = new java.sql.Timestamp(System.currentTimeMillis()); + ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES).order(ByteOrder.BIG_ENDIAN); + buffer.put(player.getAddress().getAddress().getAddress()); + buffer.position(0); + this.ipAddress = buffer.getInt(); + } + + public Player getPlayer() { + return this.player; + } + + public void update(PreparedStatement prepst) { + try { + int i = 1; + + long playtime = (System.currentTimeMillis() - this.lastRecorded) / 1000; + + prepst.setBytes(i++, UuidUtils.asBytes(player.getUniqueId())); // uuid + prepst.setString(i++, player.getName()); // serverNick + prepst.setInt(i++, this.ipAddress); // ip + prepst.setTimestamp(i++, this.dateJoined); // date + prepst.setInt(i++, this.killsSinceRecord); // kills + prepst.setInt(i++, this.deathsSinceRecord); // deaths + prepst.setLong(i++, playtime); // playtime + prepst.addBatch(); + System.out.println("Recording: " + this.dateJoined + ", " + playtime); + } catch (SQLException e) { + // TODO: Notify admins about errors. + e.printStackTrace(); + } + this.lastRecorded = System.currentTimeMillis(); + this.killsSinceRecord = 0; + this.deathsSinceRecord = 0; + } + + public void addKill() { + this.killsSinceRecord++; + } + + public void addDeath() { + this.deathsSinceRecord++; + } + +} diff --git a/Stats/src/cz/marwland/mc/features/stats/SessionManager.java b/Stats/src/cz/marwland/mc/features/stats/SessionManager.java new file mode 100644 index 0000000..8b528fd --- /dev/null +++ b/Stats/src/cz/marwland/mc/features/stats/SessionManager.java @@ -0,0 +1,110 @@ +package cz.marwland.mc.features.stats; + +import java.io.IOException; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.bukkit.entity.Player; + +import cz.marwland.mc.core.MarwCore; +import cz.marwland.mc.core.storage.SQLStorage; +import cz.marwland.mc.core.util.FileUtil; + +public class SessionManager { + + private String sessionQuery; + private final SQLStorage database = MarwCore.getInstance().getStorage(); + + public HashMap sessions = new HashMap<>(); + private final long WRITE_PERIOD = 15; + private ScheduledFuture recordTask; + + public SessionManager() { + + } + + public void start() { + loadSQLQueries(); + recordTask = MarwCore.getInstance().getStorageExecutor().scheduleAtFixedRate(() -> this.recordSessions(), WRITE_PERIOD, WRITE_PERIOD, TimeUnit.SECONDS); + for (Player p : MarwCore.getInstance().getServer().getOnlinePlayers()) { + this.startPlayer(p); + } + recordSessions(); + } + + public void shutdown() { + recordTask.cancel(false); + for (Player p : MarwCore.getInstance().getServer().getOnlinePlayers()) { + this.endPlayer(p); + } + } + + public void loadSQLQueries() { + try { + sessionQuery = FileUtil.readFileFromJAR(PlayerSession.class, "/resources/session.sql"); + sessionQuery = this.database.getStatementProcessor().apply(sessionQuery); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public void startPlayer(Player p) { + PlayerSession ps = new PlayerSession(p); + this.sessions.put(p, ps); + } + + public void endPlayer(Player p) { + PlayerSession ps = this.sessions.get(p); + // TODO: null handling - cannot happen + if (ps == null) + return; + this.recordSinglePlayer(p); + this.sessions.remove(p); + } + + public PlayerSession getPlayerSession(Player p) { + return this.sessions.get(p); + } + + private void recordSessions() { + try (Connection conn = this.database.getConnectionFactory().getConnection();) { + PreparedStatement prepst = conn.prepareStatement(this.sessionQuery); + int i = 0; + for (PlayerSession ps : this.sessions.values()) { + ps.update(prepst); + i++; + } + if (i > 0) + prepst.execute(); + prepst.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + private void recordSinglePlayer(Player p) { + PlayerSession ps = this.getPlayerSession(p); + if (ps == null) + return; + this.recordSinglePlayer(ps); + } + + private void recordSinglePlayer(PlayerSession ps) { + MarwCore.getInstance().getStorageExecutor().execute(() -> { + try (Connection conn = this.database.getConnectionFactory().getConnection();) { + PreparedStatement prepst = conn.prepareStatement(this.sessionQuery); + ps.update(prepst); + prepst.executeBatch(); + conn.commit(); + prepst.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + }); + } +} +