Stats: playtime and PvP statistics
This commit is contained in:
parent
dd9828f257
commit
4f44c24ef1
8
Stats/resources/activity_update.sql
Normal file
8
Stats/resources/activity_update.sql
Normal file
@ -0,0 +1,8 @@
|
||||
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`);
|
@ -1,9 +1,19 @@
|
||||
CREATE TABLE IF NOT EXISTS `{prefix}players` (
|
||||
`id` int UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`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,
|
||||
`kills` int DEFAULT 0,
|
||||
`deaths` int UNSIGNED DEFAULT 0,
|
||||
`voteCount` int UNSIGNED DEFAULT 0,
|
||||
PRIMARY KEY `id` (`id`)
|
||||
`playtime` mediumint UNSIGNED DEFAULT 0,
|
||||
PRIMARY KEY `id` (`id`),
|
||||
UNIQUE KEY `date_player` (`uuid`, `date`)
|
||||
) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
7
Stats/resources/players_update.sql
Normal file
7
Stats/resources/players_update.sql
Normal file
@ -0,0 +1,7 @@
|
||||
INSERT INTO `{prefix}players`
|
||||
(`uuid`, `serverNick`) VALUES
|
||||
(?, ?)
|
||||
ON DUPLICATE KEY
|
||||
UPDATE
|
||||
`serverNick` = VALUES(`serverNick`),
|
||||
`voteCount` = `voteCount` + VALUES(`voteCount`);
|
@ -1,30 +1,45 @@
|
||||
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;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
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;
|
||||
|
||||
public class Stats extends Feature {
|
||||
|
||||
private final Class<?> parentClass = this.getClass();
|
||||
private final SQLStorage database = MarwCore.getInstance().getStorage();
|
||||
public HashMap<Player, PlayerCounter> counters = new HashMap<>();
|
||||
private final long WRITE_PERIOD_TICKS = 20 * 15;
|
||||
private String playersUpdateQuery;
|
||||
private BukkitTask recordTask;
|
||||
|
||||
public Stats() {
|
||||
super();
|
||||
this.addCommand(new BukkitCommand(
|
||||
"mwstats",
|
||||
"Manages player's statistics.",
|
||||
"/mwstats",
|
||||
Arrays.asList() ) {
|
||||
this.addCommand(new BukkitCommand("mwstats", "Manages player's statistics.", "/mwstats", Arrays.asList("mws")) {
|
||||
@Override
|
||||
public boolean execute(CommandSender sender, String commandLabel, String[] args) {
|
||||
|
||||
if (!permissionMissingCheck(sender, this.getPermission()))
|
||||
return true;
|
||||
|
||||
@ -40,15 +55,137 @@ public class Stats extends Feature {
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
super.onDisable();
|
||||
recordTask.cancel();
|
||||
for (Player p : MarwCore.getInstance().getServer().getOnlinePlayers()) {
|
||||
this.stopCountingPlayer(p);
|
||||
}
|
||||
}
|
||||
|
||||
public void createTables() {
|
||||
try {
|
||||
database.executeRaw(parentClass.getResourceAsStream("/resources/create.sql"));
|
||||
database.executeRaw(this.getClass().getResourceAsStream("/resources/create.sql"));
|
||||
} catch (SQLException | IOException e) {
|
||||
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());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerQuit(PlayerQuitEvent event) {
|
||||
this.stopCountingPlayer(event.getPlayer());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerDeath(PlayerDeathEvent event) {
|
||||
PlayerCounter targetPC = this.counters.get(event.getEntity());
|
||||
if (targetPC != null) {
|
||||
targetPC.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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
87
Stats/src/cz/marwland/mc/features/stats/PlayerCounter.java
Normal file
87
Stats/src/cz/marwland/mc/features/stats/PlayerCounter.java
Normal file
@ -0,0 +1,87 @@
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user