split stats and sessions

This commit is contained in:
Erik Bročko 2018-10-12 21:49:10 +02:00
parent 78597f6a91
commit 1204423f2c
18 changed files with 354 additions and 42 deletions

21
Auth/.classpath Normal file
View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-10">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/classes" path="src">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" path="resources"/>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

2
Auth/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/bin/
/target/

23
Auth/.project Normal file
View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>Auth</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View File

@ -0,0 +1,13 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=10
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=10
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.release=enabled
org.eclipse.jdt.core.compiler.source=10

View File

@ -0,0 +1,4 @@
activeProfiles=
eclipse.preferences.version=1
resolveWorkspaceProjects=true
version=1

56
Auth/pom.xml Normal file
View File

@ -0,0 +1,56 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cz.marwland.mc.features</groupId>
<artifactId>Auth</artifactId>
<parent>
<groupId>cz.marwland.mc</groupId>
<artifactId>MarwStuff</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../../MarwStuff</relativePath>
</parent>
<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<release>10</release>
</configuration>
</plugin>
<plugin>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<groupId>org.codehaus.mojo</groupId>
<executions>
<execution>
<phase>deploy</phase>
<goals>
<goal>exec</goal>
</goals>
</execution>
</executions>
<configuration>
<executable>${project.parent.basedir}/deploy_feature.sh</executable>
<arguments>
<argument>${project.build.directory}/${project.build.finalName}.jar</argument>
<argument>${project.artifactId}</argument>
</arguments>
</configuration>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<outputDirectory>${project.build.outputDirectory}/resources</outputDirectory>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>cz.marwland.mc</groupId>
<artifactId>MarwCore</artifactId>
<version>${project.parent.version}</version>
</dependency>
</dependencies>
</project>

12
Auth/resources/create.sql Normal file
View File

@ -0,0 +1,12 @@
CREATE TABLE IF NOT EXISTS `{prefix}sessions` (
`id` bigint UNSIGNED NOT NULL AUTO_INCREMENT,
`server` bigint UNSIGNED NOT NULL,
`uuid` binary(16) NOT NULL,
`serverNick` varchar(32) NOT NULL,
`ip` int UNSIGNED NOT NULL,
`date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`playtime` mediumint UNSIGNED DEFAULT 0,
`server`
PRIMARY KEY `id` (`id`),
UNIQUE KEY `date_player` (`uuid`, `date`)
) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

View File

@ -0,0 +1,7 @@
INSERT INTO `{prefix}sessions`
(`uuid`, `server`, `serverNick`, `ip`, `date`, `playtime`) VALUES
(?, ?, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY
UPDATE
`playtime` = `playtime` + VALUES(`playtime`),
`date` = VALUES(`date`);

View File

@ -0,0 +1,56 @@
package cz.marwland.mc.features;
import java.io.IOException;
import java.sql.SQLException;
import org.bukkit.event.EventHandler;
import org.bukkit.event.player.PlayerJoinEvent;
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.auth.SessionManager;
public class Auth extends Feature {
private final SQLStorage database = MarwCore.getInstance().getStorage();
public SessionManager manager;
public Auth() {
super();
manager = new SessionManager();
}
@Override
public void onEnable() {
super.onEnable();
createTables();
manager.start();
}
@Override
public void onDisable() {
super.onDisable();
manager.shutdown();
}
public void createTables() {
try {
database.executeRaw(this.getClass().getResourceAsStream("/resources/create.sql"));
} catch (SQLException | IOException e) {
e.printStackTrace();
}
}
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
manager.startPlayer(event.getPlayer());
}
@EventHandler
public void onPlayerQuit(PlayerQuitEvent event) {
manager.endPlayer(event.getPlayer());
}
}

View File

@ -1,4 +1,4 @@
package cz.marwland.mc.features.stats; package cz.marwland.mc.features.auth;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
@ -14,8 +14,6 @@ public class PlayerSession {
private Player player; private Player player;
private volatile long lastRecorded = 0; private volatile long lastRecorded = 0;
private volatile int killsSinceRecord = 0;
private volatile int deathsSinceRecord = 0;
private java.sql.Timestamp dateJoined; private java.sql.Timestamp dateJoined;
private int ipAddress; private int ipAddress;
@ -43,26 +41,12 @@ public class PlayerSession {
prepst.setString(i++, player.getName()); // serverNick prepst.setString(i++, player.getName()); // serverNick
prepst.setInt(i++, this.ipAddress); // ip prepst.setInt(i++, this.ipAddress); // ip
prepst.setTimestamp(i++, this.dateJoined); // date prepst.setTimestamp(i++, this.dateJoined); // date
prepst.setInt(i++, this.killsSinceRecord); // kills
prepst.setInt(i++, this.deathsSinceRecord); // deaths
prepst.setLong(i++, playtime); // playtime prepst.setLong(i++, playtime); // playtime
prepst.addBatch(); prepst.addBatch();
System.out.println("Recording: " + this.dateJoined + ", " + playtime);
} catch (SQLException e) { } catch (SQLException e) {
// TODO: Notify admins about errors.
e.printStackTrace(); e.printStackTrace();
} }
this.lastRecorded = System.currentTimeMillis(); this.lastRecorded = System.currentTimeMillis();
this.killsSinceRecord = 0;
this.deathsSinceRecord = 0;
}
public void addKill() {
this.killsSinceRecord++;
}
public void addDeath() {
this.deathsSinceRecord++;
} }
} }

View File

@ -1,4 +1,4 @@
package cz.marwland.mc.features.stats; package cz.marwland.mc.features.auth;
import java.io.IOException; import java.io.IOException;
import java.sql.Connection; import java.sql.Connection;
@ -71,7 +71,7 @@ public class SessionManager {
} }
private void recordSessions() { private void recordSessions() {
try (Connection conn = this.database.getConnectionFactory().getConnection();) { try (Connection conn = this.database.getConnectionFactory().getConnection()) {
PreparedStatement prepst = conn.prepareStatement(this.sessionQuery); PreparedStatement prepst = conn.prepareStatement(this.sessionQuery);
int i = 0; int i = 0;
for (PlayerSession ps : this.sessions.values()) { for (PlayerSession ps : this.sessions.values()) {

View File

@ -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, `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT,
`uuid` binary(16) NOT NULL, `uuid` binary(16) NOT NULL,
`serverNick` varchar(32) NOT NULL, `date` date NOT NULL,
`ip` int UNSIGNED NOT NULL,
`date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`kills` int DEFAULT 0, `kills` int DEFAULT 0,
`deaths` int UNSIGNED DEFAULT 0, `deaths` int UNSIGNED DEFAULT 0,
`playtime` mediumint UNSIGNED DEFAULT 0,
PRIMARY KEY `id` (`id`), PRIMARY KEY `id` (`id`),
UNIQUE KEY `date_player` (`uuid`, `date`) UNIQUE KEY `date_player` (`uuid`, `date`)
) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; ) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

View File

@ -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`);

View 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`);

View File

@ -3,6 +3,9 @@ package cz.marwland.mc.features;
import java.io.IOException; import java.io.IOException;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Arrays; 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.CommandSender;
import org.bukkit.command.defaults.BukkitCommand; 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.MarwCore;
import cz.marwland.mc.core.features.Feature; import cz.marwland.mc.core.features.Feature;
import cz.marwland.mc.core.storage.SQLStorage; 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 { public class Stats extends Feature {
private final SQLStorage database = MarwCore.getInstance().getStorage(); 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() { public Stats() {
super(); super();
@ -40,20 +48,30 @@ public class Stats extends Feature {
return getPermissionPath(); return getPermissionPath();
} }
}); });
manager = new SessionManager();
} }
@Override @Override
public void onEnable() { public void onEnable() {
super.onEnable(); super.onEnable();
createTables(); 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 @Override
public void onDisable() { public void onDisable() {
recordTask.cancel(false);
for (Player p : MarwCore.getInstance().getServer().getOnlinePlayers()) {
this.endPlayer(p);
}
super.onDisable(); super.onDisable();
manager.shutdown();
} }
public void createTables() { public void createTables() {
@ -66,24 +84,40 @@ public class Stats extends Feature {
@EventHandler @EventHandler
public void onPlayerJoin(PlayerJoinEvent event) { public void onPlayerJoin(PlayerJoinEvent event) {
manager.startPlayer(event.getPlayer()); this.startPlayer(event.getPlayer());
} }
@EventHandler @EventHandler
public void onPlayerQuit(PlayerQuitEvent event) { public void onPlayerQuit(PlayerQuitEvent event) {
manager.endPlayer(event.getPlayer()); this.endPlayer(event.getPlayer());
} }
@EventHandler @EventHandler
public void onPlayerDeath(PlayerDeathEvent event) { public void onPlayerDeath(PlayerDeathEvent event) {
this.manager.getPlayerSession(event.getEntity()).addDeath(); this.getTracker(event.getEntity()).addDeath();
Entity killerEntity = event.getEntity().getLastDamageCause().getEntity(); Entity killerEntity = event.getEntity().getLastDamageCause().getEntity();
if (killerEntity instanceof Player) { if (killerEntity instanceof Player) {
Player killerPlayer = (Player) killerEntity; Player killerPlayer = (Player) killerEntity;
if (!killerPlayer.equals(killerEntity) && killerPlayer.isOnline()) 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());
}
} }

View File

@ -0,0 +1,9 @@
package cz.marwland.mc.features.stats;
import java.util.Collection;
public interface IStatsSaver {
void save(Collection<PlayerStats> pstats);
}

View 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;
}
}

View 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();
}
}
}