Compare commits
1 Commits
main
...
user-liste
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
70d97334cd |
@@ -1,5 +1,9 @@
|
||||
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.CoreProtectAPI;
|
||||
import net.coreprotect.CoreProtectAPI.ParseResult;
|
||||
@@ -28,6 +32,7 @@ import org.bukkit.inventory.EntityEquipment;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.sql.*;
|
||||
@@ -35,6 +40,10 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
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;
|
||||
|
||||
|
||||
@@ -48,6 +57,8 @@ public class GriefAlert extends JavaPlugin implements Listener {
|
||||
private String MAP_LINK;
|
||||
private Boolean ALLOW_STEALING;
|
||||
private Connection connection;
|
||||
private DatabaseService database;
|
||||
private Logger logger;
|
||||
|
||||
/*
|
||||
UTILITY FUNCTIONS
|
||||
@@ -155,6 +166,7 @@ public class GriefAlert extends JavaPlugin implements Listener {
|
||||
// Init GriefAlert
|
||||
@Override
|
||||
public void onEnable() {
|
||||
logger = getLogger();
|
||||
|
||||
coreProtectAPI = getCoreProtect();
|
||||
if (coreProtectAPI == null) {
|
||||
@@ -163,7 +175,6 @@ public class GriefAlert extends JavaPlugin implements Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
getServer().getPluginManager().registerEvents(this, this);
|
||||
|
||||
// Config
|
||||
saveDefaultConfig();
|
||||
@@ -176,9 +187,18 @@ public class GriefAlert extends JavaPlugin implements Listener {
|
||||
|
||||
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);
|
||||
|
||||
getLogger().info("GriefAlert has been enabled.");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -193,19 +213,19 @@ public class GriefAlert extends JavaPlugin implements Listener {
|
||||
}
|
||||
|
||||
private void setupDatabase() {
|
||||
ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
|
||||
String jdbcUrl = getConfig().getString("jdbc-url");
|
||||
try {
|
||||
connection = DriverManager.getConnection(jdbcUrl);
|
||||
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);
|
||||
stmt.close();
|
||||
getLogger().info("Connected to "+jdbcUrl);
|
||||
database = new DatabaseService(executor, connection);
|
||||
logger.info("Connected to " + jdbcUrl);
|
||||
|
||||
CompletableFuture<Void> migrationExecution = new Migrations(database, logger).execute();
|
||||
migrationExecution.join();
|
||||
logger.info("Database migration finished");
|
||||
} 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 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 (" +
|
||||
"x INTEGER, " +
|
||||
"y INTEGER, " +
|
||||
@@ -11,16 +24,16 @@ public class Migrations {
|
||||
"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 (" +
|
||||
"id TEXT PRIMARY KEY, " +
|
||||
"names TEXT NOT NULL, " +
|
||||
"playtime INTEGER NOT NULL DEFAULT 0, " +
|
||||
"firstseen INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, " +
|
||||
"lastseen INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP" +
|
||||
"firstseen INTEGER NOT NULL, " +
|
||||
"lastseen INTEGER NOT NULL, " +
|
||||
"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 (" +
|
||||
"id INTEGER PRIMARY KEY, " +
|
||||
"player TEXT NOT NULL, " +
|
||||
@@ -32,7 +45,7 @@ public class Migrations {
|
||||
"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 (" +
|
||||
"id INTEGER PRIMARY KEY, " +
|
||||
"player TEXT NOT NULL, " +
|
||||
@@ -52,7 +65,7 @@ public class Migrations {
|
||||
"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 (" +
|
||||
"id INTEGER PRIMARY KEY, " +
|
||||
"player TEXT NOT NULL, " +
|
||||
@@ -65,4 +78,19 @@ public class Migrations {
|
||||
"timestamp INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, " +
|
||||
"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