add player listener
All checks were successful
Build / Explore-Gitea-Actions (push) Successful in 44s
All checks were successful
Build / Explore-Gitea-Actions (push) Successful in 44s
This commit is contained in:
@@ -1,5 +1,9 @@
|
|||||||
package net.ardakaz.griefalert;
|
package net.ardakaz.griefalert;
|
||||||
|
|
||||||
|
import net.ardakaz.griefalert.database.DatabaseService;
|
||||||
|
import net.ardakaz.griefalert.database.Migrations;
|
||||||
|
import net.ardakaz.griefalert.database.PlayerRepository;
|
||||||
|
import net.ardakaz.griefalert.listener.PlayerListener;
|
||||||
import net.coreprotect.CoreProtect;
|
import net.coreprotect.CoreProtect;
|
||||||
import net.coreprotect.CoreProtectAPI;
|
import net.coreprotect.CoreProtectAPI;
|
||||||
import net.coreprotect.CoreProtectAPI.ParseResult;
|
import net.coreprotect.CoreProtectAPI.ParseResult;
|
||||||
@@ -28,6 +32,7 @@ import org.bukkit.inventory.EntityEquipment;
|
|||||||
import org.bukkit.inventory.Inventory;
|
import org.bukkit.inventory.Inventory;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.bukkit.plugin.Plugin;
|
import org.bukkit.plugin.Plugin;
|
||||||
|
import org.bukkit.plugin.PluginManager;
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
import java.sql.*;
|
import java.sql.*;
|
||||||
@@ -35,6 +40,10 @@ import java.util.Arrays;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.logging.Logger;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
|
||||||
@@ -48,6 +57,8 @@ public class GriefAlert extends JavaPlugin implements Listener {
|
|||||||
private String MAP_LINK;
|
private String MAP_LINK;
|
||||||
private Boolean ALLOW_STEALING;
|
private Boolean ALLOW_STEALING;
|
||||||
private Connection connection;
|
private Connection connection;
|
||||||
|
private DatabaseService database;
|
||||||
|
private Logger logger;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
UTILITY FUNCTIONS
|
UTILITY FUNCTIONS
|
||||||
@@ -155,6 +166,7 @@ public class GriefAlert extends JavaPlugin implements Listener {
|
|||||||
// Init GriefAlert
|
// Init GriefAlert
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
|
logger = getLogger();
|
||||||
|
|
||||||
coreProtectAPI = getCoreProtect();
|
coreProtectAPI = getCoreProtect();
|
||||||
if (coreProtectAPI == null) {
|
if (coreProtectAPI == null) {
|
||||||
@@ -163,7 +175,6 @@ public class GriefAlert extends JavaPlugin implements Listener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
getServer().getPluginManager().registerEvents(this, this);
|
|
||||||
|
|
||||||
// Config
|
// Config
|
||||||
saveDefaultConfig();
|
saveDefaultConfig();
|
||||||
@@ -176,9 +187,18 @@ public class GriefAlert extends JavaPlugin implements Listener {
|
|||||||
|
|
||||||
setupDatabase();
|
setupDatabase();
|
||||||
|
|
||||||
|
PlayerRepository playerRepository = new PlayerRepository(database, logger);
|
||||||
|
PlayerListener playerListener = new PlayerListener(playerRepository, logger);
|
||||||
|
|
||||||
|
|
||||||
|
PluginManager pluginManager = getServer().getPluginManager();
|
||||||
|
pluginManager.registerEvents(this, this);
|
||||||
|
pluginManager.registerEvents(playerListener, this);
|
||||||
|
|
||||||
getCommand("griefalert").setTabCompleter(this);
|
getCommand("griefalert").setTabCompleter(this);
|
||||||
|
|
||||||
getLogger().info("GriefAlert has been enabled.");
|
getLogger().info("GriefAlert has been enabled.");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -193,19 +213,19 @@ public class GriefAlert extends JavaPlugin implements Listener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setupDatabase() {
|
private void setupDatabase() {
|
||||||
|
ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||||
|
|
||||||
String jdbcUrl = getConfig().getString("jdbc-url");
|
String jdbcUrl = getConfig().getString("jdbc-url");
|
||||||
try {
|
try {
|
||||||
connection = DriverManager.getConnection(jdbcUrl);
|
connection = DriverManager.getConnection(jdbcUrl);
|
||||||
Statement stmt = connection.createStatement();
|
database = new DatabaseService(executor, connection);
|
||||||
stmt.executeUpdate(Migrations.CREATE_TABLE_IGNORED_LOCATIONS);
|
logger.info("Connected to " + jdbcUrl);
|
||||||
stmt.executeUpdate(Migrations.CREATE_TABLE_PLAYERS);
|
|
||||||
stmt.executeUpdate(Migrations.CREATE_TABLE_ACTIONS);
|
CompletableFuture<Void> migrationExecution = new Migrations(database, logger).execute();
|
||||||
stmt.executeUpdate(Migrations.CREATE_TABLE_ALERTS);
|
migrationExecution.join();
|
||||||
stmt.executeUpdate(Migrations.CREATE_TABLE_REPORTS);
|
logger.info("Database migration finished");
|
||||||
stmt.close();
|
|
||||||
getLogger().info("Connected to "+jdbcUrl);
|
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
getLogger().severe("Could not set up SQLite database: " + e.getMessage());
|
logger.severe("Could not set up SQLite database: " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package net.ardakaz.griefalert.database;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface DatabaseOperation<T> {
|
||||||
|
T execute(Connection connection) throws SQLException;
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package net.ardakaz.griefalert.database;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
|
||||||
|
public class DatabaseService {
|
||||||
|
|
||||||
|
private final ExecutorService executor;
|
||||||
|
private final Connection connection;
|
||||||
|
|
||||||
|
public DatabaseService(ExecutorService executor, Connection connection) {
|
||||||
|
this.executor = executor;
|
||||||
|
this.connection = connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> CompletableFuture<T> submit(DatabaseOperation<T> operation) {
|
||||||
|
return CompletableFuture.supplyAsync(() -> {
|
||||||
|
try {
|
||||||
|
return operation.execute(connection);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}, executor);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,21 @@
|
|||||||
package net.ardakaz.griefalert;
|
package net.ardakaz.griefalert.database;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Statement;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
public class Migrations {
|
public class Migrations {
|
||||||
|
|
||||||
public static final String CREATE_TABLE_IGNORED_LOCATIONS =
|
private DatabaseService database;
|
||||||
|
private Logger logger;
|
||||||
|
|
||||||
|
public Migrations(DatabaseService database, Logger logger) {
|
||||||
|
this.database = database;
|
||||||
|
this.logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String CREATE_TABLE_IGNORED_LOCATIONS =
|
||||||
"CREATE TABLE IF NOT EXISTS ignored_locations (" +
|
"CREATE TABLE IF NOT EXISTS ignored_locations (" +
|
||||||
"x INTEGER, " +
|
"x INTEGER, " +
|
||||||
"y INTEGER, " +
|
"y INTEGER, " +
|
||||||
@@ -11,16 +24,16 @@ public class Migrations {
|
|||||||
"PRIMARY KEY (x, y, z, world)" +
|
"PRIMARY KEY (x, y, z, world)" +
|
||||||
")";
|
")";
|
||||||
|
|
||||||
public static final String CREATE_TABLE_PLAYERS =
|
private static final String CREATE_TABLE_PLAYERS =
|
||||||
"CREATE TABLE IF NOT EXISTS players (" +
|
"CREATE TABLE IF NOT EXISTS players (" +
|
||||||
"id TEXT PRIMARY KEY, " +
|
"id TEXT PRIMARY KEY, " +
|
||||||
"names TEXT NOT NULL, " +
|
"names TEXT NOT NULL, " +
|
||||||
"playtime INTEGER NOT NULL DEFAULT 0, " +
|
"firstseen INTEGER NOT NULL, " +
|
||||||
"firstseen INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, " +
|
"lastseen INTEGER NOT NULL, " +
|
||||||
"lastseen INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP" +
|
"playtime INTEGER NOT NULL DEFAULT 0 " +
|
||||||
")";
|
")";
|
||||||
|
|
||||||
public static final String CREATE_TABLE_ACTIONS =
|
private static final String CREATE_TABLE_ACTIONS =
|
||||||
"CREATE TABLE IF NOT EXISTS actions (" +
|
"CREATE TABLE IF NOT EXISTS actions (" +
|
||||||
"id INTEGER PRIMARY KEY, " +
|
"id INTEGER PRIMARY KEY, " +
|
||||||
"player TEXT NOT NULL, " +
|
"player TEXT NOT NULL, " +
|
||||||
@@ -32,7 +45,7 @@ public class Migrations {
|
|||||||
"FOREIGN KEY(player) REFERENCES players(id)" +
|
"FOREIGN KEY(player) REFERENCES players(id)" +
|
||||||
")";
|
")";
|
||||||
|
|
||||||
public static final String CREATE_TABLE_ALERTS =
|
private static final String CREATE_TABLE_ALERTS =
|
||||||
"CREATE TABLE IF NOT EXISTS alerts (" +
|
"CREATE TABLE IF NOT EXISTS alerts (" +
|
||||||
"id INTEGER PRIMARY KEY, " +
|
"id INTEGER PRIMARY KEY, " +
|
||||||
"player TEXT NOT NULL, " +
|
"player TEXT NOT NULL, " +
|
||||||
@@ -52,7 +65,7 @@ public class Migrations {
|
|||||||
"FOREIGN KEY(player) REFERENCES players(id)" +
|
"FOREIGN KEY(player) REFERENCES players(id)" +
|
||||||
")";
|
")";
|
||||||
|
|
||||||
public static final String CREATE_TABLE_REPORTS =
|
private static final String CREATE_TABLE_REPORTS =
|
||||||
"CREATE TABLE IF NOT EXISTS reports (" +
|
"CREATE TABLE IF NOT EXISTS reports (" +
|
||||||
"id INTEGER PRIMARY KEY, " +
|
"id INTEGER PRIMARY KEY, " +
|
||||||
"player TEXT NOT NULL, " +
|
"player TEXT NOT NULL, " +
|
||||||
@@ -65,4 +78,19 @@ public class Migrations {
|
|||||||
"timestamp INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, " +
|
"timestamp INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, " +
|
||||||
"state TEXT NOT NULL DEFAULT 'UNRESOLVED'" +
|
"state TEXT NOT NULL DEFAULT 'UNRESOLVED'" +
|
||||||
")";
|
")";
|
||||||
|
|
||||||
|
public CompletableFuture<Void> execute() {
|
||||||
|
return database.submit(connection -> {
|
||||||
|
try(Statement stmt = connection.createStatement()){
|
||||||
|
stmt.executeUpdate(Migrations.CREATE_TABLE_IGNORED_LOCATIONS);
|
||||||
|
stmt.executeUpdate(Migrations.CREATE_TABLE_PLAYERS);
|
||||||
|
stmt.executeUpdate(Migrations.CREATE_TABLE_ACTIONS);
|
||||||
|
stmt.executeUpdate(Migrations.CREATE_TABLE_ALERTS);
|
||||||
|
stmt.executeUpdate(Migrations.CREATE_TABLE_REPORTS);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
logger.severe("Failed to execute database migrations: " + e.getMessage());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package net.ardakaz.griefalert.database;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public record PlayerEntity(String id, List<String> names, long playtime, long firstSeen, long lastSeen) {
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
package net.ardakaz.griefalert.database;
|
||||||
|
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
public class PlayerRepository {
|
||||||
|
private Logger logger;
|
||||||
|
private final DatabaseService database;
|
||||||
|
|
||||||
|
public PlayerRepository(DatabaseService database, Logger logger) {
|
||||||
|
this.database = database;
|
||||||
|
this.logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompletableFuture<PlayerEntity> get(String uuid) {
|
||||||
|
return database.submit(connection -> {
|
||||||
|
|
||||||
|
try (PreparedStatement ps = connection.prepareStatement("SELECT * FROM players WHERE id=?")) {
|
||||||
|
ps.setString(1, uuid);
|
||||||
|
|
||||||
|
try (ResultSet rs = ps.executeQuery()) {
|
||||||
|
if (rs.next()) {
|
||||||
|
return new PlayerEntity(
|
||||||
|
rs.getString("id"),
|
||||||
|
Arrays.stream(rs.getString("names").split(",")).toList(),
|
||||||
|
rs.getLong("playtime"),
|
||||||
|
rs.getLong("firstseen"),
|
||||||
|
rs.getLong("lastseen")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
logger.severe("DB error: " + e.getMessage());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompletableFuture<Void> upsert(PlayerEntity entity) {
|
||||||
|
return database.submit(connection -> {
|
||||||
|
String upsert = """
|
||||||
|
INSERT INTO players(id, names, firstseen, lastseen) VALUES(?,?,?,?)
|
||||||
|
ON CONFLICT(id)
|
||||||
|
DO UPDATE SET names=excluded.names, playtime=?, lastseen=excluded.lastseen;
|
||||||
|
""";
|
||||||
|
|
||||||
|
try (PreparedStatement ps = connection.prepareStatement(upsert)) {
|
||||||
|
ps.setString(1, entity.id());
|
||||||
|
String names = String.join(",", entity.names());
|
||||||
|
ps.setString(2, names);
|
||||||
|
ps.setLong(3, entity.firstSeen());
|
||||||
|
ps.setLong(4, entity.lastSeen());
|
||||||
|
ps.setLong(5, entity.playtime());
|
||||||
|
|
||||||
|
ps.executeUpdate();
|
||||||
|
|
||||||
|
} catch (SQLException e) {
|
||||||
|
logger.severe("Failed to save player: " + entity);
|
||||||
|
logger.severe(e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompletableFuture<Void> updateNames(String id, List<String> names) {
|
||||||
|
return database.submit(connection -> {
|
||||||
|
String update = """
|
||||||
|
UPDATE players
|
||||||
|
SET names = ?
|
||||||
|
WHERE id = ?
|
||||||
|
""";
|
||||||
|
|
||||||
|
try (PreparedStatement ps = connection.prepareStatement(update)) {
|
||||||
|
ps.setString(1, String.join(",", names));
|
||||||
|
ps.setString(2, id);
|
||||||
|
|
||||||
|
ps.executeUpdate();
|
||||||
|
|
||||||
|
} catch (SQLException e) {
|
||||||
|
logger.severe("Failed to update names for playerID: " + id);
|
||||||
|
logger.severe(e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package net.ardakaz.griefalert.listener;
|
||||||
|
|
||||||
|
import net.ardakaz.griefalert.database.PlayerEntity;
|
||||||
|
import net.ardakaz.griefalert.database.PlayerRepository;
|
||||||
|
import org.bukkit.Statistic;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.player.PlayerJoinEvent;
|
||||||
|
import org.bukkit.event.player.PlayerQuitEvent;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
public class PlayerListener implements Listener {
|
||||||
|
|
||||||
|
private Logger logger;
|
||||||
|
private PlayerRepository repository;
|
||||||
|
|
||||||
|
public PlayerListener(PlayerRepository repository, Logger logger) {
|
||||||
|
this.logger = logger;
|
||||||
|
this.repository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onPlayerLogin(PlayerJoinEvent event) {
|
||||||
|
Player eventPlayer = event.getPlayer();
|
||||||
|
repository.get(
|
||||||
|
eventPlayer.getUniqueId().toString()
|
||||||
|
).thenAccept(dbPlayer -> {
|
||||||
|
if (dbPlayer == null) {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
repository.upsert(new PlayerEntity(
|
||||||
|
eventPlayer.getUniqueId().toString(),
|
||||||
|
List.of(eventPlayer.getName()),
|
||||||
|
0,
|
||||||
|
now,
|
||||||
|
now
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
List<String> newNames = updateNames(eventPlayer.getName(), dbPlayer.names());
|
||||||
|
repository.updateNames(dbPlayer.id(), newNames);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onPlayerLogout(PlayerQuitEvent event) {
|
||||||
|
Player eventPlayer = event.getPlayer();
|
||||||
|
long playtimeSeconds = eventPlayer.getStatistic(Statistic.PLAY_ONE_MINUTE) / 20L;
|
||||||
|
repository.get(
|
||||||
|
eventPlayer.getUniqueId().toString()
|
||||||
|
).thenAccept(dbPlayer -> {
|
||||||
|
if (dbPlayer != null) {
|
||||||
|
PlayerEntity updatedPlayer = new PlayerEntity(
|
||||||
|
dbPlayer.id(),
|
||||||
|
dbPlayer.names(),
|
||||||
|
playtimeSeconds,
|
||||||
|
dbPlayer.firstSeen(),
|
||||||
|
System.currentTimeMillis()
|
||||||
|
);
|
||||||
|
repository.upsert(updatedPlayer);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> updateNames(String currentName, List<String> historicalNames) {
|
||||||
|
List<String> newNames = new ArrayList<>(historicalNames);
|
||||||
|
while (newNames.contains(currentName)) {
|
||||||
|
newNames.remove(currentName);
|
||||||
|
}
|
||||||
|
newNames.add(currentName);
|
||||||
|
return newNames;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user