split stats and sessions
This commit is contained in:
@ -1,12 +1,9 @@
|
||||
CREATE TABLE IF NOT EXISTS `{prefix}sessions` (
|
||||
CREATE TABLE IF NOT EXISTS `{prefix}stats` (
|
||||
`id` bigint UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`uuid` binary(16) NOT NULL,
|
||||
`serverNick` varchar(32) NOT NULL,
|
||||
`ip` int UNSIGNED NOT NULL,
|
||||
`date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`date` date NOT NULL,
|
||||
`kills` int DEFAULT 0,
|
||||
`deaths` int UNSIGNED DEFAULT 0,
|
||||
`playtime` mediumint UNSIGNED DEFAULT 0,
|
||||
PRIMARY KEY `id` (`id`),
|
||||
UNIQUE KEY `date_player` (`uuid`, `date`)
|
||||
) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
@ -1,9 +0,0 @@
|
||||
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`);
|
7
Stats/resources/stats.sql
Normal file
7
Stats/resources/stats.sql
Normal file
@ -0,0 +1,7 @@
|
||||
INSERT INTO `{prefix}stats`
|
||||
(`uuid`, `date`, `kills`, `deaths`) VALUES
|
||||
(?, ?, ?, ?)
|
||||
ON DUPLICATE KEY
|
||||
UPDATE
|
||||
`kills` = `kills` + VALUES(`kills`),
|
||||
`deaths` = `deaths` + VALUES(`deaths`);
|
@ -3,6 +3,9 @@ package cz.marwland.mc.features;
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.defaults.BukkitCommand;
|
||||
@ -16,12 +19,17 @@ import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import cz.marwland.mc.core.MarwCore;
|
||||
import cz.marwland.mc.core.features.Feature;
|
||||
import cz.marwland.mc.core.storage.SQLStorage;
|
||||
import cz.marwland.mc.features.stats.SessionManager;
|
||||
import cz.marwland.mc.features.stats.IStatsSaver;
|
||||
import cz.marwland.mc.features.stats.PlayerStats;
|
||||
import cz.marwland.mc.features.stats.SQLStatsSaver;
|
||||
|
||||
public class Stats extends Feature {
|
||||
|
||||
private final SQLStorage database = MarwCore.getInstance().getStorage();
|
||||
public SessionManager manager;
|
||||
private HashMap<Player, PlayerStats> tracked = new HashMap<>();
|
||||
private IStatsSaver saver;
|
||||
private final long WRITE_PERIOD = 15;
|
||||
private ScheduledFuture<?> recordTask;
|
||||
|
||||
public Stats() {
|
||||
super();
|
||||
@ -40,20 +48,30 @@ public class Stats extends Feature {
|
||||
return getPermissionPath();
|
||||
}
|
||||
});
|
||||
manager = new SessionManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
super.onEnable();
|
||||
createTables();
|
||||
manager.start();
|
||||
try {
|
||||
saver = new SQLStatsSaver();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
recordTask = MarwCore.getInstance().getStorageExecutor().scheduleAtFixedRate(() -> this.save(), WRITE_PERIOD, WRITE_PERIOD, TimeUnit.SECONDS);
|
||||
for (Player p : MarwCore.getInstance().getServer().getOnlinePlayers()) {
|
||||
this.startPlayer(p);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
recordTask.cancel(false);
|
||||
for (Player p : MarwCore.getInstance().getServer().getOnlinePlayers()) {
|
||||
this.endPlayer(p);
|
||||
}
|
||||
super.onDisable();
|
||||
manager.shutdown();
|
||||
}
|
||||
|
||||
public void createTables() {
|
||||
@ -66,24 +84,40 @@ public class Stats extends Feature {
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||
manager.startPlayer(event.getPlayer());
|
||||
this.startPlayer(event.getPlayer());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerQuit(PlayerQuitEvent event) {
|
||||
manager.endPlayer(event.getPlayer());
|
||||
this.endPlayer(event.getPlayer());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerDeath(PlayerDeathEvent event) {
|
||||
this.manager.getPlayerSession(event.getEntity()).addDeath();
|
||||
this.getTracker(event.getEntity()).addDeath();
|
||||
|
||||
Entity killerEntity = event.getEntity().getLastDamageCause().getEntity();
|
||||
if (killerEntity instanceof Player) {
|
||||
Player killerPlayer = (Player) killerEntity;
|
||||
if (!killerPlayer.equals(killerEntity) && killerPlayer.isOnline())
|
||||
this.manager.getPlayerSession(killerPlayer).addKill();
|
||||
this.getTracker(killerPlayer).addKill();
|
||||
}
|
||||
}
|
||||
|
||||
public void startPlayer(Player player) {
|
||||
this.tracked.put(player, new PlayerStats(player));
|
||||
}
|
||||
|
||||
public void endPlayer(Player player) {
|
||||
this.tracked.remove(player);
|
||||
}
|
||||
|
||||
public PlayerStats getTracker(Player player) {
|
||||
return this.tracked.get(player);
|
||||
}
|
||||
|
||||
public void save() {
|
||||
saver.save(this.tracked.values());
|
||||
}
|
||||
|
||||
}
|
||||
|
9
Stats/src/cz/marwland/mc/features/stats/IStatsSaver.java
Normal file
9
Stats/src/cz/marwland/mc/features/stats/IStatsSaver.java
Normal file
@ -0,0 +1,9 @@
|
||||
package cz.marwland.mc.features.stats;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public interface IStatsSaver {
|
||||
|
||||
void save(Collection<PlayerStats> pstats);
|
||||
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
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++;
|
||||
}
|
||||
|
||||
}
|
41
Stats/src/cz/marwland/mc/features/stats/PlayerStats.java
Normal file
41
Stats/src/cz/marwland/mc/features/stats/PlayerStats.java
Normal file
@ -0,0 +1,41 @@
|
||||
package cz.marwland.mc.features.stats;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class PlayerStats {
|
||||
|
||||
private final Player player;
|
||||
|
||||
private volatile int killsSinceRecord = 0;
|
||||
private volatile int deathsSinceRecord = 0;
|
||||
|
||||
public PlayerStats(Player player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
public void addKill() {
|
||||
this.killsSinceRecord++;
|
||||
}
|
||||
|
||||
public void addDeath() {
|
||||
this.deathsSinceRecord++;
|
||||
}
|
||||
|
||||
public int getNewKills() {
|
||||
return this.killsSinceRecord;
|
||||
}
|
||||
|
||||
public int getNewDeaths() {
|
||||
return this.deathsSinceRecord;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
this.deathsSinceRecord = 0;
|
||||
this.killsSinceRecord = 0;
|
||||
}
|
||||
|
||||
public Player getPlayer() {
|
||||
return this.player;
|
||||
}
|
||||
|
||||
}
|
55
Stats/src/cz/marwland/mc/features/stats/SQLStatsSaver.java
Normal file
55
Stats/src/cz/marwland/mc/features/stats/SQLStatsSaver.java
Normal file
@ -0,0 +1,55 @@
|
||||
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.Collection;
|
||||
|
||||
import cz.marwland.mc.core.MarwCore;
|
||||
import cz.marwland.mc.core.storage.SQLStorage;
|
||||
import cz.marwland.mc.core.util.FileUtil;
|
||||
import cz.marwland.mc.core.util.UuidUtils;
|
||||
|
||||
public class SQLStatsSaver implements IStatsSaver {
|
||||
|
||||
private final SQLStorage database = MarwCore.getInstance().getStorage();
|
||||
private final String statsQuery;
|
||||
|
||||
public SQLStatsSaver() throws IOException {
|
||||
String query = FileUtil.readFileFromJAR(this.getClass(), "/resources/stats.sql");
|
||||
query = this.database.getStatementProcessor().apply(query);
|
||||
this.statsQuery = query;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(Collection<PlayerStats> pstats) {
|
||||
try (Connection conn = this.database.getConnectionFactory().getConnection()) {
|
||||
PreparedStatement prepst = conn.prepareStatement(this.statsQuery);
|
||||
int i = 0;
|
||||
for (PlayerStats ps : pstats) {
|
||||
this.addPlayer(prepst, ps);
|
||||
ps.reset();
|
||||
i++;
|
||||
}
|
||||
if (i > 0)
|
||||
prepst.executeBatch();
|
||||
prepst.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void addPlayer(PreparedStatement prepst, PlayerStats ps) {
|
||||
try {
|
||||
int i = 1;
|
||||
prepst.setBytes(i++, UuidUtils.asBytes(ps.getPlayer().getUniqueId())); // uuid
|
||||
prepst.setInt(i++, ps.getNewKills()); // kills
|
||||
prepst.setInt(i++, ps.getNewDeaths()); // deaths
|
||||
prepst.addBatch();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,110 +0,0 @@
|
||||
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<Player, PlayerSession> 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();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user