mirror of
https://github.com/plexusorg/Plex.git
synced 2026-06-04 05:26:55 +00:00
New database API
This commit is contained in:
@@ -13,6 +13,7 @@ import dev.plex.hook.CoreProtectHook;
|
||||
import dev.plex.hook.PrismHook;
|
||||
import dev.plex.hook.RollbackManager;
|
||||
import dev.plex.module.ModuleManager;
|
||||
import dev.plex.player.PlayerNameResolver;
|
||||
import dev.plex.player.PlayerService;
|
||||
import dev.plex.player.PlexPlayer;
|
||||
import dev.plex.punishment.PunishmentManager;
|
||||
@@ -21,6 +22,8 @@ import dev.plex.storage.RedisConnection;
|
||||
import dev.plex.storage.SQLConnection;
|
||||
import dev.plex.storage.StorageType;
|
||||
import dev.plex.storage.player.SQLPlayerData;
|
||||
import dev.plex.storage.player.PlayerModuleDataRepository;
|
||||
import dev.plex.storage.player.SQLPlayerModuleData;
|
||||
import dev.plex.storage.punishment.SQLNotes;
|
||||
import dev.plex.storage.punishment.SQLPunishment;
|
||||
import dev.plex.storage.repository.NoteRepository;
|
||||
@@ -65,7 +68,9 @@ public class Plex extends JavaPlugin
|
||||
|
||||
private PlayerCache playerCache;
|
||||
private PlayerRepository playerRepository;
|
||||
private PlayerModuleDataRepository playerModuleDataRepository;
|
||||
private PlayerService playerService;
|
||||
private PlayerNameResolver playerNameResolver;
|
||||
|
||||
private PunishmentRepository punishmentRepository;
|
||||
private NoteRepository noteRepository;
|
||||
@@ -217,8 +222,10 @@ public class Plex extends JavaPlugin
|
||||
|
||||
punishmentRepository = new SQLPunishment(sqlConnection.getConnectionSource(), api.scheduler().asyncExecutor());
|
||||
playerRepository = new SQLPlayerData(sqlConnection.getConnectionSource(), punishmentRepository);
|
||||
playerModuleDataRepository = new SQLPlayerModuleData(sqlConnection, storageType);
|
||||
noteRepository = new SQLNotes(sqlConnection.getConnectionSource(), api.scheduler().asyncExecutor());
|
||||
playerService = new PlayerService(playerCache, playerRepository);
|
||||
playerNameResolver = new PlayerNameResolver(playerService);
|
||||
|
||||
new ListenerHandler(this);
|
||||
commandHandler = new CommandHandler(this);
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
package dev.plex.api.impl;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import dev.plex.api.player.PlayerModuleData;
|
||||
import dev.plex.storage.player.PlayerModuleDataRepository;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class DefaultPlayerModuleData implements PlayerModuleData
|
||||
{
|
||||
private static final Gson GSON = new Gson();
|
||||
private static final Pattern KEY_PATTERN = Pattern.compile("^[a-z][a-z0-9_]{0,63}$");
|
||||
|
||||
private final PlayerModuleDataRepository repository;
|
||||
private final String modulePrefix;
|
||||
private final UUID playerUuid;
|
||||
|
||||
public DefaultPlayerModuleData(PlayerModuleDataRepository repository, String modulePrefix, UUID playerUuid)
|
||||
{
|
||||
this.repository = repository;
|
||||
this.modulePrefix = modulePrefix;
|
||||
this.playerUuid = playerUuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<JsonElement> get(String key)
|
||||
{
|
||||
return repository.get(playerUuid, modulePrefix, validateKey(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Optional<T> get(String key, Class<T> type)
|
||||
{
|
||||
return get(key).map(element -> GSON.fromJson(element, type));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(String key, String fallback)
|
||||
{
|
||||
return get(key)
|
||||
.filter(JsonElement::isJsonPrimitive)
|
||||
.map(JsonElement::getAsJsonPrimitive)
|
||||
.filter(primitive -> primitive.isString())
|
||||
.map(primitive -> primitive.getAsString())
|
||||
.orElse(fallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong(String key, long fallback)
|
||||
{
|
||||
return get(key)
|
||||
.filter(JsonElement::isJsonPrimitive)
|
||||
.map(JsonElement::getAsJsonPrimitive)
|
||||
.filter(primitive -> primitive.isNumber())
|
||||
.map(primitive -> primitive.getAsLong())
|
||||
.orElse(fallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBoolean(String key, boolean fallback)
|
||||
{
|
||||
return get(key)
|
||||
.filter(JsonElement::isJsonPrimitive)
|
||||
.map(JsonElement::getAsJsonPrimitive)
|
||||
.filter(primitive -> primitive.isBoolean())
|
||||
.map(primitive -> primitive.getAsBoolean())
|
||||
.orElse(fallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(String key, JsonElement value)
|
||||
{
|
||||
repository.set(playerUuid, modulePrefix, validateKey(key), Objects.requireNonNull(value, "value"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(String key, Object value)
|
||||
{
|
||||
set(key, GSON.toJsonTree(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(String key)
|
||||
{
|
||||
repository.remove(playerUuid, modulePrefix, validateKey(key));
|
||||
}
|
||||
|
||||
private String validateKey(String key)
|
||||
{
|
||||
if (key == null || !KEY_PATTERN.matcher(key).matches())
|
||||
{
|
||||
throw new IllegalArgumentException("Invalid player module data key: " + key);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
package dev.plex.api.impl;
|
||||
|
||||
import dev.plex.Plex;
|
||||
import dev.plex.api.player.PlayerModuleData;
|
||||
import dev.plex.api.player.PlayersApi;
|
||||
import dev.plex.api.player.PlexPlayerView;
|
||||
import dev.plex.module.PlexModule;
|
||||
import dev.plex.player.PlexPlayer;
|
||||
import dev.plex.storage.module.ModuleNames;
|
||||
import dev.plex.util.PlexUtils;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@@ -15,9 +18,10 @@ final class DefaultPlayersApi implements PlayersApi
|
||||
|
||||
DefaultPlayersApi(Plex plugin) { this.plugin = plugin; }
|
||||
|
||||
@Override public Optional<? extends PlexPlayerView> byUuid(UUID uuid) { return Optional.ofNullable(plugin.getPlayerService().getPlayer(uuid)).map(DefaultPlexPlayerView::new); }
|
||||
@Override public Optional<? extends PlexPlayerView> byName(String name) { return Optional.ofNullable(plugin.getPlayerService().getPlayer(name)).map(DefaultPlexPlayerView::new); }
|
||||
@Override public Optional<? extends PlexPlayerView> player(UUID uuid) { return Optional.ofNullable(plugin.getPlayerService().getPlayer(uuid)).map(player -> new DefaultPlexPlayerView(player, plugin.getPlayerNameResolver())); }
|
||||
@Override public Optional<? extends PlexPlayerView> byName(String name) { return Optional.ofNullable(plugin.getPlayerService().getPlayer(name)).map(player -> new DefaultPlexPlayerView(player, plugin.getPlayerNameResolver())); }
|
||||
@Override public List<String> onlineNames() { return PlexUtils.getPlayerNameList(); }
|
||||
@Override public PlayerModuleData moduleData(PlexModule module, UUID playerUuid) { return new DefaultPlayerModuleData(plugin.getPlayerModuleDataRepository(), ModuleNames.prefix(module), playerUuid); }
|
||||
|
||||
static PlexPlayer unwrap(PlexPlayerView view)
|
||||
{
|
||||
|
||||
@@ -2,17 +2,18 @@ package dev.plex.api.impl;
|
||||
|
||||
import dev.plex.api.player.PlexPlayerView;
|
||||
import dev.plex.api.punishment.PunishmentView;
|
||||
import dev.plex.player.PlayerNameResolver;
|
||||
import dev.plex.player.PlexPlayer;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
record DefaultPlexPlayerView(PlexPlayer player) implements PlexPlayerView
|
||||
record DefaultPlexPlayerView(PlexPlayer player, PlayerNameResolver playerNameResolver) implements PlexPlayerView
|
||||
{
|
||||
@Override public UUID uuid() { return player.getUuid(); }
|
||||
@Override public String name() { return player.getName(); }
|
||||
@Override public List<String> ips() { return List.copyOf(player.getIps()); }
|
||||
@Override public List<? extends PunishmentView> punishments() { return player.getPunishments().stream().map(DefaultPunishmentView::new).toList(); }
|
||||
@Override public List<? extends PunishmentView> punishments() { return player.getPunishments().stream().map(punishment -> new DefaultPunishmentView(punishment, playerNameResolver)).toList(); }
|
||||
@Override public boolean frozen() { return player.isFrozen(); }
|
||||
@Override public boolean muted() { return player.isMuted(); }
|
||||
@Override public boolean lockedUp() { return player.isLockedUp(); }
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
package dev.plex.api.impl;
|
||||
|
||||
import dev.plex.api.punishment.PunishmentSource;
|
||||
import dev.plex.api.punishment.PunishmentType;
|
||||
import dev.plex.api.punishment.PunishmentView;
|
||||
import dev.plex.player.PlayerNameResolver;
|
||||
import dev.plex.punishment.Punishment;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
record DefaultPunishmentView(Punishment punishment) implements PunishmentView
|
||||
record DefaultPunishmentView(Punishment punishment, PlayerNameResolver playerNameResolver) implements PunishmentView
|
||||
{
|
||||
@Override public UUID punished() { return punishment.getPunished(); }
|
||||
@Override public UUID punisher() { return punishment.getPunisher(); }
|
||||
@Override public String punisherName() { return punishment.getPunisherName(); }
|
||||
@Override public PunishmentSource source() { return punishment.getSource() == null ? (punishment.getPunisher() == null ? PunishmentSource.CONSOLE : PunishmentSource.PLAYER) : punishment.getSource(); }
|
||||
@Override public String punisherReference() { return punishment.getPunisherReference(); }
|
||||
@Override public String punisherDisplayName() { return Punishment.punisherDisplayName(punishment, playerNameResolver); }
|
||||
@Override public String ip() { return punishment.getIp(); }
|
||||
@Override public String punishedUsername() { return punishment.getPunishedUsername(); }
|
||||
@Override public PunishmentType type() { return PunishmentType.valueOf(punishment.getType().name()); }
|
||||
@Override public String reason() { return punishment.getReason(); }
|
||||
@Override public boolean customTime() { return punishment.isCustomTime(); }
|
||||
|
||||
@@ -27,9 +27,9 @@ final class DefaultPunishmentsApi implements PunishmentsApi
|
||||
PlexPlayer player = DefaultPlayersApi.unwrap(playerView);
|
||||
if (player == null) player = plugin.getPlayerService().getPlayer(playerView.uuid());
|
||||
Punishment punishment = new Punishment(request.punished(), request.punisher());
|
||||
punishment.setPunisherName(request.punisherName());
|
||||
punishment.setSource(request.source());
|
||||
punishment.setPunisherReference(request.punisherReference());
|
||||
punishment.setIp(request.ip());
|
||||
punishment.setPunishedUsername(request.punishedUsername());
|
||||
punishment.setType(PunishmentType.valueOf(request.type().name()));
|
||||
punishment.setReason(request.reason());
|
||||
punishment.setCustomTime(request.customTime());
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
package dev.plex.api.impl;
|
||||
|
||||
import dev.plex.Plex;
|
||||
import dev.plex.api.storage.ModuleStorage;
|
||||
import dev.plex.api.storage.SqlDialect;
|
||||
import dev.plex.api.storage.StorageApi;
|
||||
import dev.plex.module.PlexModule;
|
||||
import dev.plex.storage.module.ServerModuleStorage;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
@@ -19,4 +23,16 @@ final class DefaultStorageApi implements StorageApi
|
||||
return function.apply(connection);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModuleStorage forModule(PlexModule module)
|
||||
{
|
||||
return new ServerModuleStorage(plugin, module);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlDialect dialect()
|
||||
{
|
||||
return plugin.getStorageType().dialect();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +97,6 @@ public class BanCMD extends ServerCommand
|
||||
{
|
||||
punishment.setReason(context.messageString("noReasonProvided"));
|
||||
}
|
||||
punishment.setPunishedUsername(plexPlayer.getName());
|
||||
ZonedDateTime date = ZonedDateTime.now(ZoneId.of(TimeUtils.TIMEZONE));
|
||||
punishment.setEndDate(date.plusDays(1));
|
||||
punishment.setCustomTime(false);
|
||||
@@ -107,7 +106,7 @@ public class BanCMD extends ServerCommand
|
||||
PlexUtils.broadcast(context.messageComponent("banningPlayer", sender.getName(), plexPlayer.getName()));
|
||||
if (player != null)
|
||||
{
|
||||
plugin.getApi().scheduler().runEntity(player, () -> BungeeUtil.kickPlayer(plugin, player, Punishment.generateBanMessage(punishment, plugin.config.getString("banning.ban_url"), plugin.getPlayerService())));
|
||||
plugin.getApi().scheduler().runEntity(player, () -> BungeeUtil.kickPlayer(plugin, player, Punishment.generateBanMessage(punishment, plugin.config.getString("banning.ban_url"), plugin.getPlayerNameResolver())));
|
||||
}
|
||||
PlexLog.debug("(From /ban command) PunishedPlayer UUID: " + plexPlayer.getUuid());
|
||||
|
||||
|
||||
@@ -3,9 +3,6 @@ package dev.plex.command.impl;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import dev.plex.command.ServerCommand;
|
||||
import dev.plex.command.ServerCommandContext;
|
||||
import dev.plex.punishment.Punishment;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.papermc.paper.command.brigadier.CommandSourceStack;
|
||||
import net.kyori.adventure.text.Component;
|
||||
@@ -44,7 +41,7 @@ public class BanListCommand extends ServerCommand
|
||||
{
|
||||
plugin.getPunishmentManager().getActiveBans().whenComplete((punishments, throwable) ->
|
||||
{
|
||||
context.send(sender, context.messageComponent("activeBansList", punishments.size(), StringUtils.join(punishments.stream().map(Punishment::getPunishedUsername).collect(Collectors.toList()), ", ")));
|
||||
context.send(sender, context.messageComponent("activeBansList", punishments.size(), StringUtils.join(punishments.stream().map(punishment -> plugin.getPlayerNameResolver().resolve(punishment.getPunished())).toList(), ", ")));
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -60,7 +60,6 @@ public class FreezeCMD extends ServerCommand
|
||||
ZonedDateTime date = ZonedDateTime.now(ZoneId.of(TimeUtils.TIMEZONE));
|
||||
punishment.setEndDate(date.plusSeconds(plugin.config.getInt("punishments.freeze-timer", 300)));
|
||||
punishment.setType(PunishmentType.FREEZE);
|
||||
punishment.setPunishedUsername(player.getName());
|
||||
punishment.setIp(player.getAddress().getAddress().getHostAddress().trim());
|
||||
punishment.setReason("");
|
||||
punishment.setActive(true);
|
||||
|
||||
@@ -76,14 +76,13 @@ public class KickCMD extends ServerCommand
|
||||
}
|
||||
|
||||
punishment.setReason(reason);
|
||||
punishment.setPunishedUsername(plexPlayer.getName());
|
||||
punishment.setEndDate(ZonedDateTime.now(ZoneId.of(TimeUtils.TIMEZONE)));
|
||||
punishment.setCustomTime(false);
|
||||
punishment.setActive(false);
|
||||
punishment.setIp(player.getAddress().getAddress().getHostAddress().trim());
|
||||
plugin.getPunishmentManager().punish(plexPlayer, punishment);
|
||||
PlexUtils.broadcast(context.messageComponent("kickedPlayer", sender.getName(), plexPlayer.getName()));
|
||||
BungeeUtil.kickPlayer(plugin, player, Punishment.generateKickMessage(punishment, plugin.getPlayerService()));
|
||||
BungeeUtil.kickPlayer(plugin, player, Punishment.generateKickMessage(punishment, plugin.getPlayerNameResolver()));
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,6 @@ public class MuteCMD extends ServerCommand
|
||||
ZonedDateTime date = ZonedDateTime.now(ZoneId.of(TimeUtils.TIMEZONE));
|
||||
punishment.setEndDate(date.plusSeconds(plugin.config.getInt("punishments.mute-timer", 300)));
|
||||
punishment.setType(PunishmentType.MUTE);
|
||||
punishment.setPunishedUsername(player.getName());
|
||||
punishment.setIp(player.getAddress().getAddress().getHostAddress().trim());
|
||||
punishment.setReason("");
|
||||
punishment.setActive(true);
|
||||
|
||||
@@ -150,7 +150,8 @@ public class NotesCMD extends ServerCommand
|
||||
AtomicReference<Component> noteList = new AtomicReference<>(context.messageComponent("notesHeader", plexPlayer.getName()));
|
||||
for (Note note : notes)
|
||||
{
|
||||
Component noteLine = context.messageComponent("notePrefix", note.getId(), plugin.getPlayerService().getPlayer(note.getWrittenBy()).getName(), TimeUtils.useTimezone(note.getTimestamp()));
|
||||
String author = plugin.getPlayerNameResolver().resolve(note.getWrittenBy());
|
||||
Component noteLine = context.messageComponent("notePrefix", note.getId(), author, TimeUtils.useTimezone(note.getTimestamp()));
|
||||
noteLine = noteLine.append(context.messageComponent("noteLine", note.getNote()));
|
||||
noteList.set(noteList.get().append(Component.newline()));
|
||||
noteList.set(noteList.get().append(noteLine));
|
||||
|
||||
@@ -128,7 +128,6 @@ public class SmiteCMD extends ServerCommand
|
||||
punishment.setCustomTime(false);
|
||||
punishment.setEndDate(ZonedDateTime.now(ZoneId.of(TimeUtils.TIMEZONE)));
|
||||
punishment.setType(PunishmentType.SMITE);
|
||||
punishment.setPunishedUsername(player.getName());
|
||||
punishment.setIp(player.getAddress().getAddress().getHostAddress().trim());
|
||||
|
||||
if (reason != null)
|
||||
|
||||
@@ -83,7 +83,6 @@ public class TempbanCMD extends ServerCommand
|
||||
{
|
||||
punishment.setReason(context.messageString("noReasonProvided"));
|
||||
}
|
||||
punishment.setPunishedUsername(target.getName());
|
||||
punishment.setEndDate(TimeUtils.createDate(args[1]));
|
||||
punishment.setCustomTime(false);
|
||||
punishment.setActive(true);
|
||||
@@ -92,7 +91,7 @@ public class TempbanCMD extends ServerCommand
|
||||
PlexUtils.broadcast(context.messageComponent("banningPlayer", sender.getName(), target.getName()));
|
||||
if (player != null)
|
||||
{
|
||||
plugin.getApi().scheduler().runEntity(player, () -> BungeeUtil.kickPlayer(plugin, player, Punishment.generateBanMessage(punishment, plugin.config.getString("banning.ban_url"), plugin.getPlayerService())));
|
||||
plugin.getApi().scheduler().runEntity(player, () -> BungeeUtil.kickPlayer(plugin, player, Punishment.generateBanMessage(punishment, plugin.config.getString("banning.ban_url"), plugin.getPlayerNameResolver())));
|
||||
}
|
||||
if (rollBack)
|
||||
{
|
||||
|
||||
@@ -93,7 +93,6 @@ public class TempmuteCMD extends ServerCommand
|
||||
punishment.setCustomTime(true);
|
||||
punishment.setEndDate(endDate);
|
||||
punishment.setType(PunishmentType.MUTE);
|
||||
punishment.setPunishedUsername(player.getName());
|
||||
punishment.setIp(player.getAddress().getAddress().getHostAddress().trim());
|
||||
punishment.setReason(reason);
|
||||
punishment.setActive(true);
|
||||
|
||||
@@ -54,7 +54,7 @@ public class BanListener extends ServerListenerBase
|
||||
PlexPlayer player = plugin.getPlayerService().getPlayer(event.getUniqueId());
|
||||
player.getPunishments().stream().filter(punishment -> (punishment.getType() == PunishmentType.BAN || punishment.getType() == PunishmentType.TEMPBAN) && punishment.isActive()).findFirst().ifPresent(punishment ->
|
||||
event.disallow(AsyncPlayerPreLoginEvent.Result.KICK_BANNED,
|
||||
Punishment.generateBanMessage(punishment, plugin.config.getString("banning.ban_url"), plugin.getPlayerService())));
|
||||
Punishment.generateBanMessage(punishment, plugin.config.getString("banning.ban_url"), plugin.getPlayerNameResolver())));
|
||||
return;
|
||||
}
|
||||
Punishment ipBannedPunishment = plugin.getPunishmentManager().getBanByIP(event.getAddress().getHostAddress());
|
||||
@@ -66,7 +66,7 @@ public class BanListener extends ServerListenerBase
|
||||
return;
|
||||
}
|
||||
event.disallow(AsyncPlayerPreLoginEvent.Result.KICK_BANNED,
|
||||
Punishment.generateBanMessage(ipBannedPunishment, plugin.config.getString("banning.ban_url"), plugin.getPlayerService()));
|
||||
Punishment.generateBanMessage(ipBannedPunishment, plugin.config.getString("banning.ban_url"), plugin.getPlayerNameResolver()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ public class PlayerListener extends ServerListenerBase
|
||||
}
|
||||
if (!plexPlayer.getName().equals(player.getName()))
|
||||
{
|
||||
PlexLog.log(plexPlayer.getName() + " has a new name. Changing it to " + player.getName());
|
||||
PlexLog.log(plexPlayer.getName() + " has a new last known name. Changing it to " + player.getName());
|
||||
plexPlayer.setName(player.getName());
|
||||
plugin.getPlayerService().update(plexPlayer);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
package dev.plex.player;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.time.Duration;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class PlayerNameResolver
|
||||
{
|
||||
private final PlayerService playerService;
|
||||
private final HttpClient httpClient = HttpClient.newBuilder()
|
||||
.connectTimeout(Duration.ofSeconds(3))
|
||||
.build();
|
||||
private final Map<UUID, String> profileCache = new ConcurrentHashMap<>();
|
||||
|
||||
public PlayerNameResolver(PlayerService playerService)
|
||||
{
|
||||
this.playerService = playerService;
|
||||
}
|
||||
|
||||
public String resolve(UUID uuid)
|
||||
{
|
||||
if (uuid == null)
|
||||
{
|
||||
return "CONSOLE";
|
||||
}
|
||||
|
||||
Player online = Bukkit.getPlayer(uuid);
|
||||
if (online != null)
|
||||
{
|
||||
return online.getName();
|
||||
}
|
||||
|
||||
String local = playerService.getNameByUUID(uuid);
|
||||
if (local != null && !local.isBlank())
|
||||
{
|
||||
return local;
|
||||
}
|
||||
|
||||
String cached = profileCache.get(uuid);
|
||||
if (cached != null && !cached.isBlank())
|
||||
{
|
||||
return cached;
|
||||
}
|
||||
|
||||
return lookupMojangName(uuid)
|
||||
.map(name ->
|
||||
{
|
||||
profileCache.put(uuid, name);
|
||||
return name;
|
||||
})
|
||||
.orElse(uuid.toString());
|
||||
}
|
||||
|
||||
private Optional<String> lookupMojangName(UUID uuid)
|
||||
{
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create("https://sessionserver.mojang.com/session/minecraft/profile/" + uuid.toString().replace("-", "")))
|
||||
.timeout(Duration.ofSeconds(5))
|
||||
.GET()
|
||||
.build();
|
||||
try
|
||||
{
|
||||
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
if (response.statusCode() != 200)
|
||||
{
|
||||
return Optional.empty();
|
||||
}
|
||||
JsonObject object = JsonParser.parseString(response.body()).getAsJsonObject();
|
||||
if (!object.has("name") || !object.get("name").isJsonPrimitive())
|
||||
{
|
||||
return Optional.empty();
|
||||
}
|
||||
return Optional.ofNullable(object.get("name").getAsString()).filter(name -> !name.isBlank());
|
||||
}
|
||||
catch (IOException | InterruptedException | RuntimeException e)
|
||||
{
|
||||
if (e instanceof InterruptedException)
|
||||
{
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,6 @@ public class PlexPlayer
|
||||
private String prefix;
|
||||
|
||||
private boolean staffChat;
|
||||
private boolean vanished;
|
||||
private boolean commandSpy;
|
||||
|
||||
// These fields are transient so MongoDB doesn't automatically drop them in.
|
||||
@@ -42,8 +41,6 @@ public class PlexPlayer
|
||||
private transient boolean muted;
|
||||
private transient boolean lockedUp;
|
||||
|
||||
private long coins;
|
||||
|
||||
private List<String> ips = Lists.newArrayList();
|
||||
|
||||
private List<Punishment> punishments = Lists.newArrayList();
|
||||
@@ -62,11 +59,8 @@ public class PlexPlayer
|
||||
this.loginMessage = "";
|
||||
this.prefix = "";
|
||||
|
||||
this.vanished = false;
|
||||
this.commandSpy = false;
|
||||
|
||||
this.coins = 0;
|
||||
|
||||
if (loadPunishments)
|
||||
{
|
||||
this.checkMutesAndFreeze();
|
||||
|
||||
@@ -2,7 +2,8 @@ package dev.plex.punishment;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import dev.plex.player.PlayerService;
|
||||
import dev.plex.api.punishment.PunishmentSource;
|
||||
import dev.plex.player.PlayerNameResolver;
|
||||
import dev.plex.util.PlexUtils;
|
||||
import dev.plex.util.TimeUtils;
|
||||
import dev.plex.util.adapter.ZonedDateTimeAdapter;
|
||||
@@ -24,12 +25,9 @@ public class Punishment
|
||||
@NotNull
|
||||
private final UUID punished;
|
||||
private final UUID punisher;
|
||||
// Optional display attribution for punishers without a Minecraft UUID
|
||||
// (e.g. web staff signed in via XenForo). When non-null, render this in
|
||||
// place of the UUID-based name lookup.
|
||||
private String punisherName;
|
||||
private PunishmentSource source;
|
||||
private String punisherReference;
|
||||
private String ip;
|
||||
private String punishedUsername;
|
||||
private PunishmentType type;
|
||||
private String reason;
|
||||
private boolean customTime;
|
||||
@@ -41,32 +39,33 @@ public class Punishment
|
||||
{
|
||||
this.punished = punished;
|
||||
this.punisher = punisher;
|
||||
this.source = punisher == null ? PunishmentSource.CONSOLE : PunishmentSource.PLAYER;
|
||||
this.issueDate = ZonedDateTime.now(ZoneId.of(TimeUtils.TIMEZONE));
|
||||
}
|
||||
|
||||
public static Component generateBanMessage(Punishment punishment, String banUrl, PlayerService playerService)
|
||||
public static Component generateBanMessage(Punishment punishment, String banUrl, PlayerNameResolver playerNameResolver)
|
||||
{
|
||||
return PlexUtils.messageComponent("banMessage", banUrl, punishment.getReason(), TimeUtils.useTimezone(punishment.getEndDate()), punisherDisplayName(punishment, playerService));
|
||||
return PlexUtils.messageComponent("banMessage", banUrl, punishment.getReason(), TimeUtils.useTimezone(punishment.getEndDate()), punisherDisplayName(punishment, playerNameResolver));
|
||||
}
|
||||
|
||||
public static Component generateKickMessage(Punishment punishment, PlayerService playerService)
|
||||
public static Component generateKickMessage(Punishment punishment, PlayerNameResolver playerNameResolver)
|
||||
{
|
||||
return PlexUtils.messageComponent("kickMessage", punishment.getReason(), punisherDisplayName(punishment, playerService));
|
||||
return PlexUtils.messageComponent("kickMessage", punishment.getReason(), punisherDisplayName(punishment, playerNameResolver));
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the human-readable punisher attribution for display.
|
||||
* Prefers the explicit {@link #punisherName} (used for off-server
|
||||
* sources such as XenForo staff acting via the web HTTPD), falling
|
||||
* back to a UUID lookup, and finally "CONSOLE" when the punisher is
|
||||
* truly unknown.
|
||||
*/
|
||||
public static String punisherDisplayName(Punishment punishment, PlayerService playerService)
|
||||
public static String punisherDisplayName(Punishment punishment, PlayerNameResolver playerNameResolver)
|
||||
{
|
||||
String explicit = punishment.getPunisherName();
|
||||
if (explicit != null && !explicit.isEmpty()) return explicit;
|
||||
if (punishment.getPunisher() == null) return "CONSOLE";
|
||||
return playerService.getNameByUUID(punishment.getPunisher());
|
||||
PunishmentSource source = punishment.getSource();
|
||||
if (source == null)
|
||||
{
|
||||
source = punishment.getPunisher() == null ? PunishmentSource.CONSOLE : PunishmentSource.PLAYER;
|
||||
}
|
||||
return switch (source)
|
||||
{
|
||||
case PLAYER -> punishment.getPunisher() == null ? "CONSOLE" : playerNameResolver.resolve(punishment.getPunisher());
|
||||
case CONSOLE -> "CONSOLE";
|
||||
case WEB -> punishment.getPunisherReference() == null || punishment.getPunisherReference().isBlank() ? "WEB" : punishment.getPunisherReference();
|
||||
};
|
||||
}
|
||||
|
||||
public static Component generateIndefBanMessageWithReason(String type, String banUrl, String reason)
|
||||
|
||||
@@ -55,7 +55,6 @@ public class TimingService extends AbstractService
|
||||
punishment.setReason(PlexUtils.messageString("nukerTempbanReason"));
|
||||
if (player != null)
|
||||
{
|
||||
punishment.setPunishedUsername(player.getName());
|
||||
punishment.setIp(player.getAddress().getAddress().getHostAddress());
|
||||
}
|
||||
punishment.setEndDate(TimeUtils.createDate("5m"));
|
||||
|
||||
@@ -2,6 +2,7 @@ package dev.plex.storage;
|
||||
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
import dev.plex.Plex;
|
||||
import dev.plex.api.storage.SqlDialect;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
@@ -24,7 +25,7 @@ public enum StorageType
|
||||
@Override
|
||||
public String migrationHistoryTableSql(String tableName)
|
||||
{
|
||||
return "CREATE TABLE IF NOT EXISTS " + tableName + " (version VARCHAR(100) NOT NULL PRIMARY KEY, installed_at INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000))";
|
||||
return "CREATE TABLE IF NOT EXISTS " + quoteIdentifier(tableName) + " (scope VARCHAR(100) NOT NULL, version VARCHAR(100) NOT NULL, installed_at INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000), PRIMARY KEY (scope, version))";
|
||||
}
|
||||
},
|
||||
|
||||
@@ -42,7 +43,7 @@ public enum StorageType
|
||||
@Override
|
||||
public String migrationHistoryTableSql(String tableName)
|
||||
{
|
||||
return "CREATE TABLE IF NOT EXISTS `" + tableName + "` (`version` VARCHAR(100) NOT NULL PRIMARY KEY, `installed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP)";
|
||||
return "CREATE TABLE IF NOT EXISTS " + quoteIdentifier(tableName) + " (`scope` VARCHAR(100) NOT NULL, `version` VARCHAR(100) NOT NULL, `installed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`scope`, `version`))";
|
||||
}
|
||||
},
|
||||
|
||||
@@ -81,9 +82,56 @@ public enum StorageType
|
||||
|
||||
public abstract void configure(HikariConfig config, Plex plugin);
|
||||
|
||||
public SqlDialect dialect()
|
||||
{
|
||||
return switch (this)
|
||||
{
|
||||
case SQLITE -> SqlDialect.SQLITE;
|
||||
case MARIADB -> SqlDialect.MARIADB;
|
||||
case POSTGRES -> SqlDialect.POSTGRES;
|
||||
};
|
||||
}
|
||||
|
||||
public String quoteIdentifier(String identifier)
|
||||
{
|
||||
return switch (this)
|
||||
{
|
||||
case MARIADB -> "`" + identifier + "`";
|
||||
case SQLITE, POSTGRES -> "\"" + identifier + "\"";
|
||||
};
|
||||
}
|
||||
|
||||
public String migrationHistoryTableSql(String tableName)
|
||||
{
|
||||
return "CREATE TABLE IF NOT EXISTS " + tableName + " (version VARCHAR(100) NOT NULL PRIMARY KEY, installed_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP)";
|
||||
return "CREATE TABLE IF NOT EXISTS " + quoteIdentifier(tableName) + " (scope VARCHAR(100) NOT NULL, version VARCHAR(100) NOT NULL, installed_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (scope, version))";
|
||||
}
|
||||
|
||||
public String playerModuleDataUpsertSql()
|
||||
{
|
||||
return switch (this)
|
||||
{
|
||||
case SQLITE -> """
|
||||
INSERT INTO player_module_data (player_uuid, module, data_key, value_json, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
ON CONFLICT(player_uuid, module, data_key) DO UPDATE SET
|
||||
value_json = excluded.value_json,
|
||||
updated_at = excluded.updated_at
|
||||
""";
|
||||
case MARIADB -> """
|
||||
INSERT INTO `player_module_data` (`player_uuid`, `module`, `data_key`, `value_json`, `updated_at`)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`value_json` = VALUES(`value_json`),
|
||||
`updated_at` = VALUES(`updated_at`)
|
||||
""";
|
||||
case POSTGRES -> """
|
||||
INSERT INTO player_module_data (player_uuid, module, data_key, value_json, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
ON CONFLICT(player_uuid, module, data_key) DO UPDATE SET
|
||||
value_json = excluded.value_json,
|
||||
updated_at = excluded.updated_at
|
||||
""";
|
||||
};
|
||||
}
|
||||
|
||||
public String getDisplayName()
|
||||
|
||||
@@ -9,24 +9,16 @@ import dev.plex.storage.StorageType;
|
||||
import dev.plex.util.PlexLog;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
public class Database
|
||||
{
|
||||
private static final String MIGRATION_TABLE = "plex_schema_history";
|
||||
|
||||
protected final Plex plugin;
|
||||
private final HikariDataSource dataSource;
|
||||
private final ConnectionSource connectionSource;
|
||||
private final StorageType storageType;
|
||||
private final MigrationRunner migrationRunner;
|
||||
|
||||
public Database(Plex plugin)
|
||||
{
|
||||
@@ -48,7 +40,8 @@ public class Database
|
||||
try
|
||||
{
|
||||
this.connectionSource = new DataSourceConnectionSource(dataSource, config.getJdbcUrl());
|
||||
runMigrations();
|
||||
this.migrationRunner = new MigrationRunner(storageType);
|
||||
this.migrationRunner.runCore(dataSource, getClass().getClassLoader(), List.of("001_initial_schema"));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -57,105 +50,7 @@ public class Database
|
||||
}
|
||||
}
|
||||
|
||||
private void runMigrations() throws Exception
|
||||
{
|
||||
try (Connection connection = dataSource.getConnection())
|
||||
{
|
||||
ensureMigrationTable(connection);
|
||||
for (String migration : List.of("001_initial_schema"))
|
||||
{
|
||||
if (hasMigration(connection, migration))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
executeMigration(connection, migration);
|
||||
try (Statement statement = connection.createStatement())
|
||||
{
|
||||
statement.executeUpdate("INSERT INTO " + MIGRATION_TABLE + " (version) VALUES ('" + migration + "')");
|
||||
}
|
||||
PlexLog.log("Applied database migration " + migration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureMigrationTable(Connection connection) throws SQLException
|
||||
{
|
||||
try (Statement statement = connection.createStatement())
|
||||
{
|
||||
statement.execute(storageType.migrationHistoryTableSql(MIGRATION_TABLE));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasMigration(Connection connection, String migration) throws SQLException
|
||||
{
|
||||
try (Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery("SELECT version FROM " + MIGRATION_TABLE + " WHERE version = '" + migration + "'"))
|
||||
{
|
||||
return resultSet.next();
|
||||
}
|
||||
}
|
||||
|
||||
private void executeMigration(Connection connection, String migration) throws Exception
|
||||
{
|
||||
String resource = "db/migration/" + storageType.getMigrationDirectory() + "/" + migration + ".sql";
|
||||
try (InputStream stream = getClass().getClassLoader().getResourceAsStream(resource))
|
||||
{
|
||||
if (stream == null)
|
||||
{
|
||||
throw new IllegalStateException("Missing database migration resource: " + resource);
|
||||
}
|
||||
|
||||
for (String sql : splitStatements(new String(stream.readAllBytes(), StandardCharsets.UTF_8)))
|
||||
{
|
||||
try (Statement statement = connection.createStatement())
|
||||
{
|
||||
statement.execute(sql);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> splitStatements(String script)
|
||||
{
|
||||
List<String> statements = new ArrayList<>();
|
||||
StringBuilder current = new StringBuilder();
|
||||
boolean inSingleQuote = false;
|
||||
boolean inDoubleQuote = false;
|
||||
|
||||
for (int i = 0; i < script.length(); i++)
|
||||
{
|
||||
char c = script.charAt(i);
|
||||
if (c == '\'' && !inDoubleQuote)
|
||||
{
|
||||
inSingleQuote = !inSingleQuote;
|
||||
}
|
||||
else if (c == '"' && !inSingleQuote)
|
||||
{
|
||||
inDoubleQuote = !inDoubleQuote;
|
||||
}
|
||||
|
||||
if (c == ';' && !inSingleQuote && !inDoubleQuote)
|
||||
{
|
||||
addStatement(statements, current);
|
||||
current.setLength(0);
|
||||
continue;
|
||||
}
|
||||
current.append(c);
|
||||
}
|
||||
addStatement(statements, current);
|
||||
return statements;
|
||||
}
|
||||
|
||||
private void addStatement(List<String> statements, StringBuilder statement)
|
||||
{
|
||||
String sql = statement.toString().replaceAll("(?m)^\\s*--.*$", "").trim();
|
||||
if (!sql.isEmpty())
|
||||
{
|
||||
statements.add(sql);
|
||||
}
|
||||
}
|
||||
|
||||
public Connection getConnection() throws SQLException
|
||||
public java.sql.Connection getConnection() throws java.sql.SQLException
|
||||
{
|
||||
return dataSource.getConnection();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
package dev.plex.storage.database;
|
||||
|
||||
import dev.plex.module.PlexModule;
|
||||
import dev.plex.storage.StorageType;
|
||||
import dev.plex.util.PlexLog;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class MigrationRunner
|
||||
{
|
||||
private static final String MIGRATION_TABLE = "plex_schema_history";
|
||||
private static final Pattern VERSION_PATTERN = Pattern.compile("^[0-9]{3}_[a-z0-9_]+$");
|
||||
private static final Pattern TABLE_TOKEN_PATTERN = Pattern.compile("\\{\\{table:([a-z0-9_]+)}}");
|
||||
|
||||
private final StorageType storageType;
|
||||
|
||||
public MigrationRunner(StorageType storageType)
|
||||
{
|
||||
this.storageType = storageType;
|
||||
}
|
||||
|
||||
public void runCore(DataSource dataSource, ClassLoader classLoader, List<String> versions) throws SQLException
|
||||
{
|
||||
run(dataSource, "core", versions, version -> readCore(classLoader, version), Function.identity());
|
||||
}
|
||||
|
||||
public void runModule(DataSource dataSource, PlexModule module, String scope, String resourceRoot, List<String> versions, Function<String, String> tableResolver) throws SQLException
|
||||
{
|
||||
run(dataSource, scope, versions, version -> readModule(module, resourceRoot, version), tableResolver);
|
||||
}
|
||||
|
||||
private void run(DataSource dataSource, String scope, List<String> versions, ResourceReader reader, Function<String, String> tableResolver) throws SQLException
|
||||
{
|
||||
try (Connection connection = dataSource.getConnection())
|
||||
{
|
||||
ensureMigrationTable(connection);
|
||||
for (String version : versions)
|
||||
{
|
||||
validateVersion(version);
|
||||
if (hasMigration(connection, scope, version))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
String script = replaceTableTokens(reader.read(version), tableResolver);
|
||||
for (String sql : splitStatements(script))
|
||||
{
|
||||
try (Statement statement = connection.createStatement())
|
||||
{
|
||||
statement.execute(sql);
|
||||
}
|
||||
}
|
||||
insertMigration(connection, scope, version);
|
||||
PlexLog.log("Applied database migration " + scope + ":" + version);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureMigrationTable(Connection connection) throws SQLException
|
||||
{
|
||||
try (Statement statement = connection.createStatement())
|
||||
{
|
||||
statement.execute(storageType.migrationHistoryTableSql(MIGRATION_TABLE));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasMigration(Connection connection, String scope, String version) throws SQLException
|
||||
{
|
||||
try (PreparedStatement statement = connection.prepareStatement("SELECT version FROM " + MIGRATION_TABLE + " WHERE scope = ? AND version = ?"))
|
||||
{
|
||||
statement.setString(1, scope);
|
||||
statement.setString(2, version);
|
||||
try (ResultSet resultSet = statement.executeQuery())
|
||||
{
|
||||
return resultSet.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void insertMigration(Connection connection, String scope, String version) throws SQLException
|
||||
{
|
||||
try (PreparedStatement statement = connection.prepareStatement("INSERT INTO " + MIGRATION_TABLE + " (scope, version) VALUES (?, ?)"))
|
||||
{
|
||||
statement.setString(1, scope);
|
||||
statement.setString(2, version);
|
||||
statement.executeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
private void validateVersion(String version) throws SQLException
|
||||
{
|
||||
if (!VERSION_PATTERN.matcher(version).matches())
|
||||
{
|
||||
throw new SQLException("Invalid migration version: " + version);
|
||||
}
|
||||
}
|
||||
|
||||
private String readCore(ClassLoader classLoader, String version) throws SQLException
|
||||
{
|
||||
String resource = "db/migration/" + storageType.dialect().migrationDirectory() + "/" + version + ".sql";
|
||||
try (InputStream stream = classLoader.getResourceAsStream(resource))
|
||||
{
|
||||
if (stream == null)
|
||||
{
|
||||
throw new SQLException("Missing database migration resource: " + resource);
|
||||
}
|
||||
return new String(stream.readAllBytes(), StandardCharsets.UTF_8);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new SQLException("Failed to read database migration resource: " + resource, e);
|
||||
}
|
||||
}
|
||||
|
||||
private String readModule(PlexModule module, String resourceRoot, String version) throws SQLException
|
||||
{
|
||||
String resource = resourceRoot + "/" + storageType.dialect().migrationDirectory() + "/" + version + ".sql";
|
||||
try (InputStream stream = module.getResource(resource))
|
||||
{
|
||||
if (stream == null)
|
||||
{
|
||||
throw new SQLException("Missing module migration resource: " + resource);
|
||||
}
|
||||
return new String(stream.readAllBytes(), StandardCharsets.UTF_8);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new SQLException("Failed to read module migration resource: " + resource, e);
|
||||
}
|
||||
}
|
||||
|
||||
private String replaceTableTokens(String script, Function<String, String> tableResolver) throws SQLException
|
||||
{
|
||||
Matcher matcher = TABLE_TOKEN_PATTERN.matcher(script);
|
||||
StringBuilder replaced = new StringBuilder();
|
||||
while (matcher.find())
|
||||
{
|
||||
matcher.appendReplacement(replaced, Matcher.quoteReplacement(tableResolver.apply(matcher.group(1))));
|
||||
}
|
||||
matcher.appendTail(replaced);
|
||||
if (replaced.toString().contains("{{table:"))
|
||||
{
|
||||
throw new SQLException("Unsupported table token in migration");
|
||||
}
|
||||
return replaced.toString();
|
||||
}
|
||||
|
||||
private List<String> splitStatements(String script)
|
||||
{
|
||||
List<String> statements = new ArrayList<>();
|
||||
StringBuilder current = new StringBuilder();
|
||||
boolean inSingleQuote = false;
|
||||
boolean inDoubleQuote = false;
|
||||
|
||||
for (int i = 0; i < script.length(); i++)
|
||||
{
|
||||
char c = script.charAt(i);
|
||||
if (c == '\'' && !inDoubleQuote)
|
||||
{
|
||||
inSingleQuote = !inSingleQuote;
|
||||
}
|
||||
else if (c == '"' && !inSingleQuote)
|
||||
{
|
||||
inDoubleQuote = !inDoubleQuote;
|
||||
}
|
||||
|
||||
if (c == ';' && !inSingleQuote && !inDoubleQuote)
|
||||
{
|
||||
addStatement(statements, current);
|
||||
current.setLength(0);
|
||||
continue;
|
||||
}
|
||||
current.append(c);
|
||||
}
|
||||
addStatement(statements, current);
|
||||
return statements;
|
||||
}
|
||||
|
||||
private void addStatement(List<String> statements, StringBuilder statement)
|
||||
{
|
||||
String sql = statement.toString().replaceAll("(?m)^\\s*--.*$", "").trim();
|
||||
if (!sql.isEmpty())
|
||||
{
|
||||
statements.add(sql);
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface ResourceReader
|
||||
{
|
||||
String read(String version) throws SQLException;
|
||||
}
|
||||
}
|
||||
@@ -19,8 +19,8 @@ public class NoteEntity
|
||||
@DatabaseField(columnName = "uuid", canBeNull = false, index = true, width = 46)
|
||||
private String uuid;
|
||||
|
||||
@DatabaseField(columnName = "written_by", width = 46)
|
||||
private String writtenBy;
|
||||
@DatabaseField(columnName = "written_by_uuid", width = 46)
|
||||
private String writtenByUuid;
|
||||
|
||||
@DatabaseField(columnName = "note", width = 2000)
|
||||
private String note;
|
||||
|
||||
@@ -13,8 +13,8 @@ public class PlayerEntity
|
||||
@DatabaseField(id = true, columnName = "uuid", width = 46)
|
||||
private String uuid;
|
||||
|
||||
@DatabaseField(columnName = "name", width = 18)
|
||||
private String name;
|
||||
@DatabaseField(columnName = "last_known_name", width = 18, index = true)
|
||||
private String lastKnownName;
|
||||
|
||||
@DatabaseField(columnName = "login_msg", width = 2000)
|
||||
private String loginMessage;
|
||||
@@ -28,12 +28,6 @@ public class PlayerEntity
|
||||
@DatabaseField(columnName = "ips", width = 2000)
|
||||
private String ips;
|
||||
|
||||
@DatabaseField(columnName = "coins")
|
||||
private long coins;
|
||||
|
||||
@DatabaseField(columnName = "vanished")
|
||||
private boolean vanished;
|
||||
|
||||
@DatabaseField(columnName = "commandspy")
|
||||
private boolean commandSpy;
|
||||
|
||||
|
||||
@@ -13,17 +13,17 @@ public class PunishmentEntity
|
||||
@DatabaseField(generatedId = true, columnName = "id")
|
||||
private long id;
|
||||
|
||||
@DatabaseField(columnName = "punished", canBeNull = false, index = true, width = 46)
|
||||
private String punished;
|
||||
@DatabaseField(columnName = "punished_uuid", canBeNull = false, index = true, width = 46)
|
||||
private String punishedUuid;
|
||||
|
||||
@DatabaseField(columnName = "punisher", width = 46)
|
||||
private String punisher;
|
||||
@DatabaseField(columnName = "punisher_uuid", width = 46)
|
||||
private String punisherUuid;
|
||||
|
||||
@DatabaseField(columnName = "punisherName", width = 64)
|
||||
private String punisherName;
|
||||
@DatabaseField(columnName = "source", width = 20)
|
||||
private String source;
|
||||
|
||||
@DatabaseField(columnName = "punishedUsername", width = 16)
|
||||
private String punishedUsername;
|
||||
@DatabaseField(columnName = "punisher_reference", width = 200)
|
||||
private String punisherReference;
|
||||
|
||||
@DatabaseField(columnName = "ip", width = 2000, index = true)
|
||||
private String ip;
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package dev.plex.storage.module;
|
||||
|
||||
import dev.plex.module.PlexModule;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public final class ModuleNames
|
||||
{
|
||||
private static final int MAX_PREFIX_LENGTH = 40;
|
||||
|
||||
private ModuleNames()
|
||||
{
|
||||
}
|
||||
|
||||
public static String prefix(PlexModule module)
|
||||
{
|
||||
String name = module.getPlexModuleFile().getName().toLowerCase(Locale.ROOT);
|
||||
if (name.startsWith("module-"))
|
||||
{
|
||||
name = name.substring("module-".length());
|
||||
}
|
||||
name = name.replaceAll("[^a-z0-9]+", "_").replaceAll("^_+|_+$", "");
|
||||
if (name.isBlank())
|
||||
{
|
||||
throw new IllegalArgumentException("Module name does not produce a valid storage prefix");
|
||||
}
|
||||
if (name.length() > MAX_PREFIX_LENGTH)
|
||||
{
|
||||
name = name.substring(0, MAX_PREFIX_LENGTH).replaceAll("_+$", "");
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
public static String table(String prefix, String localName)
|
||||
{
|
||||
return prefix + "_" + validateLocalName(localName);
|
||||
}
|
||||
|
||||
public static String validateLocalName(String localName)
|
||||
{
|
||||
if (localName == null || !localName.matches("[a-z][a-z0-9_]{0,47}"))
|
||||
{
|
||||
throw new IllegalArgumentException("Invalid module table name: " + localName);
|
||||
}
|
||||
return localName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package dev.plex.storage.module;
|
||||
|
||||
import dev.plex.Plex;
|
||||
import dev.plex.api.storage.ModuleMigrations;
|
||||
import dev.plex.module.PlexModule;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
public class ServerModuleMigrations implements ModuleMigrations
|
||||
{
|
||||
private final Plex plugin;
|
||||
private final PlexModule module;
|
||||
private final ServerModuleStorage storage;
|
||||
|
||||
public ServerModuleMigrations(Plex plugin, PlexModule module, ServerModuleStorage storage)
|
||||
{
|
||||
this.plugin = plugin;
|
||||
this.module = module;
|
||||
this.storage = storage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(List<String> versions) throws SQLException
|
||||
{
|
||||
run("db/migration", versions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String resourceRoot, List<String> versions) throws SQLException
|
||||
{
|
||||
plugin.getSqlConnection().getMigrationRunner().runModule(
|
||||
plugin.getSqlConnection().getDataSource(),
|
||||
module,
|
||||
storage.scope(),
|
||||
resourceRoot,
|
||||
versions,
|
||||
storage::quotedTable
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package dev.plex.storage.module;
|
||||
|
||||
import com.j256.ormlite.dao.Dao;
|
||||
import com.j256.ormlite.dao.DaoManager;
|
||||
import com.j256.ormlite.support.ConnectionSource;
|
||||
import com.j256.ormlite.table.DatabaseTableConfig;
|
||||
import dev.plex.api.storage.ModuleOrm;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class ServerModuleOrm implements ModuleOrm
|
||||
{
|
||||
private final ConnectionSource connectionSource;
|
||||
private final ServerModuleStorage storage;
|
||||
private final Map<String, Dao<?, ?>> daos = new ConcurrentHashMap<>();
|
||||
|
||||
public ServerModuleOrm(ConnectionSource connectionSource, ServerModuleStorage storage)
|
||||
{
|
||||
this.connectionSource = connectionSource;
|
||||
this.storage = storage;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T, ID> Dao<T, ID> dao(Class<T> entityClass, String localTableName) throws SQLException
|
||||
{
|
||||
String key = entityClass.getName() + ":" + localTableName;
|
||||
Dao<?, ?> existing = daos.get(key);
|
||||
if (existing != null)
|
||||
{
|
||||
return (Dao<T, ID>) existing;
|
||||
}
|
||||
|
||||
DatabaseTableConfig<T> tableConfig = DatabaseTableConfig.fromClass(connectionSource.getDatabaseType(), entityClass);
|
||||
tableConfig.setTableName(storage.table(localTableName));
|
||||
Dao<T, ID> dao = DaoManager.createDao(connectionSource, tableConfig);
|
||||
daos.put(key, dao);
|
||||
return dao;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package dev.plex.storage.module;
|
||||
|
||||
import com.j256.ormlite.misc.TransactionManager;
|
||||
import dev.plex.Plex;
|
||||
import dev.plex.api.storage.ModuleMigrations;
|
||||
import dev.plex.api.storage.ModuleOrm;
|
||||
import dev.plex.api.storage.ModuleStorage;
|
||||
import dev.plex.api.storage.SqlCallable;
|
||||
import dev.plex.module.PlexModule;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class ServerModuleStorage implements ModuleStorage
|
||||
{
|
||||
private final Plex plugin;
|
||||
private final PlexModule module;
|
||||
private final String prefix;
|
||||
private final ModuleMigrations migrations;
|
||||
private final ModuleOrm orm;
|
||||
|
||||
public ServerModuleStorage(Plex plugin, PlexModule module)
|
||||
{
|
||||
this.plugin = plugin;
|
||||
this.module = module;
|
||||
this.prefix = ModuleNames.prefix(module);
|
||||
this.migrations = new ServerModuleMigrations(plugin, module, this);
|
||||
this.orm = new ServerModuleOrm(plugin.getSqlConnection().getConnectionSource(), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String prefix()
|
||||
{
|
||||
return prefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String table(String localName)
|
||||
{
|
||||
return ModuleNames.table(prefix, localName);
|
||||
}
|
||||
|
||||
String quotedTable(String localName)
|
||||
{
|
||||
return plugin.getStorageType().quoteIdentifier(table(localName));
|
||||
}
|
||||
|
||||
String scope()
|
||||
{
|
||||
return "module:" + prefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModuleMigrations migrations()
|
||||
{
|
||||
return migrations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModuleOrm orm()
|
||||
{
|
||||
return orm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T transaction(SqlCallable<T> callable) throws SQLException
|
||||
{
|
||||
return TransactionManager.callInTransaction(plugin.getSqlConnection().getConnectionSource(), callable::call);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package dev.plex.storage.player;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface PlayerModuleDataRepository
|
||||
{
|
||||
Optional<JsonElement> get(UUID playerUuid, String module, String key);
|
||||
|
||||
void set(UUID playerUuid, String module, String key, JsonElement value);
|
||||
|
||||
void remove(UUID playerUuid, String module, String key);
|
||||
}
|
||||
@@ -57,7 +57,7 @@ public class SQLPlayerData implements PlayerRepository
|
||||
{
|
||||
try
|
||||
{
|
||||
return players.queryBuilder().where().eq("name", username).queryForFirst() != null;
|
||||
return players.queryBuilder().where().eq("last_known_name", username).queryForFirst() != null;
|
||||
}
|
||||
catch (SQLException e)
|
||||
{
|
||||
@@ -84,7 +84,7 @@ public class SQLPlayerData implements PlayerRepository
|
||||
try
|
||||
{
|
||||
PlayerEntity entity = players.queryForId(uuid.toString());
|
||||
return entity == null ? null : entity.getName();
|
||||
return entity == null ? null : entity.getLastKnownName();
|
||||
}
|
||||
catch (SQLException e)
|
||||
{
|
||||
@@ -102,7 +102,7 @@ public class SQLPlayerData implements PlayerRepository
|
||||
{
|
||||
try
|
||||
{
|
||||
return toPlayer(players.queryBuilder().limit(1L).where().eq("name", username).queryForFirst(), loadExtraData);
|
||||
return toPlayer(players.queryBuilder().limit(1L).where().eq("last_known_name", username).queryForFirst(), loadExtraData);
|
||||
}
|
||||
catch (SQLException e)
|
||||
{
|
||||
@@ -169,13 +169,11 @@ public class SQLPlayerData implements PlayerRepository
|
||||
}
|
||||
|
||||
PlexPlayer plexPlayer = new PlexPlayer(UUID.fromString(entity.getUuid()), false);
|
||||
plexPlayer.setName(entity.getName());
|
||||
plexPlayer.setName(entity.getLastKnownName());
|
||||
plexPlayer.setLoginMessage(entity.getLoginMessage());
|
||||
plexPlayer.setPrefix(entity.getPrefix());
|
||||
plexPlayer.setStaffChat(entity.isStaffChat());
|
||||
plexPlayer.setIps(parseIps(entity.getIps()));
|
||||
plexPlayer.setCoins(entity.getCoins());
|
||||
plexPlayer.setVanished(entity.isVanished());
|
||||
plexPlayer.setCommandSpy(entity.isCommandSpy());
|
||||
if (loadExtraData)
|
||||
{
|
||||
@@ -189,13 +187,11 @@ public class SQLPlayerData implements PlayerRepository
|
||||
{
|
||||
PlayerEntity entity = new PlayerEntity();
|
||||
entity.setUuid(player.getUuid().toString());
|
||||
entity.setName(player.getName());
|
||||
entity.setLastKnownName(player.getName());
|
||||
entity.setLoginMessage(player.getLoginMessage());
|
||||
entity.setPrefix(player.getPrefix());
|
||||
entity.setStaffChat(player.isStaffChat());
|
||||
entity.setIps(GSON.toJson(player.getIps()));
|
||||
entity.setCoins(player.getCoins());
|
||||
entity.setVanished(player.isVanished());
|
||||
entity.setCommandSpy(player.isCommandSpy());
|
||||
return entity;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
package dev.plex.storage.player;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import dev.plex.storage.SQLConnection;
|
||||
import dev.plex.storage.StorageType;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public class SQLPlayerModuleData implements PlayerModuleDataRepository
|
||||
{
|
||||
private final SQLConnection sqlConnection;
|
||||
private final StorageType storageType;
|
||||
|
||||
public SQLPlayerModuleData(SQLConnection sqlConnection, StorageType storageType)
|
||||
{
|
||||
this.sqlConnection = sqlConnection;
|
||||
this.storageType = storageType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<JsonElement> get(UUID playerUuid, String module, String key)
|
||||
{
|
||||
String sql = "SELECT value_json FROM player_module_data WHERE player_uuid = ? AND module = ? AND data_key = ?";
|
||||
try (Connection connection = sqlConnection.getConnection(); PreparedStatement statement = connection.prepareStatement(sql))
|
||||
{
|
||||
statement.setString(1, playerUuid.toString());
|
||||
statement.setString(2, module);
|
||||
statement.setString(3, key);
|
||||
try (ResultSet resultSet = statement.executeQuery())
|
||||
{
|
||||
if (!resultSet.next())
|
||||
{
|
||||
return Optional.empty();
|
||||
}
|
||||
return Optional.of(JsonParser.parseString(resultSet.getString("value_json")));
|
||||
}
|
||||
}
|
||||
catch (SQLException | JsonSyntaxException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(UUID playerUuid, String module, String key, JsonElement value)
|
||||
{
|
||||
try (Connection connection = sqlConnection.getConnection(); PreparedStatement statement = connection.prepareStatement(storageType.playerModuleDataUpsertSql()))
|
||||
{
|
||||
statement.setString(1, playerUuid.toString());
|
||||
statement.setString(2, module);
|
||||
statement.setString(3, key);
|
||||
statement.setString(4, value.toString());
|
||||
statement.setLong(5, System.currentTimeMillis());
|
||||
statement.executeUpdate();
|
||||
}
|
||||
catch (SQLException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(UUID playerUuid, String module, String key)
|
||||
{
|
||||
String sql = "DELETE FROM player_module_data WHERE player_uuid = ? AND module = ? AND data_key = ?";
|
||||
try (Connection connection = sqlConnection.getConnection(); PreparedStatement statement = connection.prepareStatement(sql))
|
||||
{
|
||||
statement.setString(1, playerUuid.toString());
|
||||
statement.setString(2, module);
|
||||
statement.setString(3, key);
|
||||
statement.executeUpdate();
|
||||
}
|
||||
catch (SQLException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
@@ -47,6 +48,7 @@ public class SQLNotes implements NoteRepository
|
||||
return notes.queryForEq("uuid", uuid.toString()).stream()
|
||||
.sorted(Comparator.comparingInt(NoteEntity::getId))
|
||||
.map(this::toNote)
|
||||
.flatMap(Optional::stream)
|
||||
.toList();
|
||||
}
|
||||
catch (SQLException e)
|
||||
@@ -96,16 +98,23 @@ public class SQLNotes implements NoteRepository
|
||||
}, executor);
|
||||
}
|
||||
|
||||
private Note toNote(NoteEntity entity)
|
||||
private Optional<Note> toNote(NoteEntity entity)
|
||||
{
|
||||
Note note = new Note(
|
||||
UUID.fromString(entity.getUuid()),
|
||||
entity.getNote(),
|
||||
UUID.fromString(entity.getWrittenBy()),
|
||||
ZonedDateTime.ofInstant(Instant.ofEpochMilli(entity.getTimestamp()), ZoneId.of(TimeUtils.TIMEZONE))
|
||||
);
|
||||
note.setId(entity.getId());
|
||||
return note;
|
||||
try
|
||||
{
|
||||
Note note = new Note(
|
||||
UUID.fromString(entity.getUuid()),
|
||||
entity.getNote(),
|
||||
UUID.fromString(entity.getWrittenByUuid()),
|
||||
ZonedDateTime.ofInstant(Instant.ofEpochMilli(entity.getTimestamp()), ZoneId.of(TimeUtils.TIMEZONE))
|
||||
);
|
||||
note.setId(entity.getId());
|
||||
return Optional.of(note);
|
||||
}
|
||||
catch (IllegalArgumentException | NullPointerException e)
|
||||
{
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
private NoteEntity toEntity(Note note)
|
||||
@@ -113,7 +122,7 @@ public class SQLNotes implements NoteRepository
|
||||
NoteEntity entity = new NoteEntity();
|
||||
entity.setId(note.getId());
|
||||
entity.setUuid(note.getUuid().toString());
|
||||
entity.setWrittenBy(note.getWrittenBy().toString());
|
||||
entity.setWrittenByUuid(note.getWrittenBy().toString());
|
||||
entity.setNote(note.getNote());
|
||||
entity.setTimestamp(note.getTimestamp().toInstant().toEpochMilli());
|
||||
return entity;
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.j256.ormlite.dao.Dao;
|
||||
import com.j256.ormlite.dao.DaoManager;
|
||||
import com.j256.ormlite.support.ConnectionSource;
|
||||
import com.j256.ormlite.stmt.UpdateBuilder;
|
||||
import dev.plex.api.punishment.PunishmentSource;
|
||||
import dev.plex.punishment.Punishment;
|
||||
import dev.plex.punishment.PunishmentType;
|
||||
import dev.plex.storage.database.entity.PunishmentEntity;
|
||||
@@ -59,7 +60,7 @@ public class SQLPunishment implements PunishmentRepository
|
||||
{
|
||||
try
|
||||
{
|
||||
return punishments.queryForEq("punished", uuid.toString()).stream().map(this::toPunishment).toList();
|
||||
return punishments.queryForEq("punished_uuid", uuid.toString()).stream().map(this::toPunishment).toList();
|
||||
}
|
||||
catch (SQLException e)
|
||||
{
|
||||
@@ -119,7 +120,7 @@ public class SQLPunishment implements PunishmentRepository
|
||||
{
|
||||
UpdateBuilder<PunishmentEntity, Long> update = punishments.updateBuilder();
|
||||
update.updateColumnValue("active", active);
|
||||
update.where().eq("punished", punished.toString()).and().eq("type", type.name());
|
||||
update.where().eq("punished_uuid", punished.toString()).and().eq("type", type.name());
|
||||
update.update();
|
||||
}
|
||||
catch (SQLException e)
|
||||
@@ -130,13 +131,13 @@ public class SQLPunishment implements PunishmentRepository
|
||||
|
||||
private Punishment toPunishment(PunishmentEntity entity)
|
||||
{
|
||||
UUID punisher = entity.getPunisher() == null || entity.getPunisher().isBlank() ? null : UUID.fromString(entity.getPunisher());
|
||||
Punishment punishment = new Punishment(UUID.fromString(entity.getPunished()), punisher);
|
||||
UUID punisher = entity.getPunisherUuid() == null || entity.getPunisherUuid().isBlank() ? null : UUID.fromString(entity.getPunisherUuid());
|
||||
Punishment punishment = new Punishment(UUID.fromString(entity.getPunishedUuid()), punisher);
|
||||
punishment.setActive(entity.isActive());
|
||||
punishment.setType(PunishmentType.valueOf(entity.getType()));
|
||||
punishment.setCustomTime(entity.isCustomTime());
|
||||
punishment.setPunishedUsername(entity.getPunishedUsername());
|
||||
punishment.setPunisherName(entity.getPunisherName());
|
||||
punishment.setSource(entity.getSource() == null ? punishment.getSource() : PunishmentSource.valueOf(entity.getSource()));
|
||||
punishment.setPunisherReference(entity.getPunisherReference());
|
||||
punishment.setIssueDate(ZonedDateTime.ofInstant(Instant.ofEpochMilli(entity.getIssueDate()), ZoneId.of(TimeUtils.TIMEZONE)));
|
||||
punishment.setEndDate(ZonedDateTime.ofInstant(Instant.ofEpochMilli(entity.getEndDate()), ZoneId.of(TimeUtils.TIMEZONE)));
|
||||
punishment.setReason(entity.getReason());
|
||||
@@ -147,10 +148,11 @@ public class SQLPunishment implements PunishmentRepository
|
||||
private PunishmentEntity toEntity(Punishment punishment)
|
||||
{
|
||||
PunishmentEntity entity = new PunishmentEntity();
|
||||
entity.setPunished(punishment.getPunished().toString());
|
||||
entity.setPunisher(punishment.getPunisher() == null ? null : punishment.getPunisher().toString());
|
||||
entity.setPunisherName(punishment.getPunisherName());
|
||||
entity.setPunishedUsername(punishment.getPunishedUsername());
|
||||
entity.setPunishedUuid(punishment.getPunished().toString());
|
||||
entity.setPunisherUuid(punishment.getPunisher() == null ? null : punishment.getPunisher().toString());
|
||||
PunishmentSource source = punishment.getSource() == null ? (punishment.getPunisher() == null ? PunishmentSource.CONSOLE : PunishmentSource.PLAYER) : punishment.getSource();
|
||||
entity.setSource(source.name());
|
||||
entity.setPunisherReference(punishment.getPunisherReference());
|
||||
entity.setIp(punishment.getIp());
|
||||
entity.setType(punishment.getType().name());
|
||||
entity.setReason(punishment.getReason());
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
CREATE TABLE IF NOT EXISTS `players` (
|
||||
`uuid` VARCHAR(46) NOT NULL,
|
||||
`name` VARCHAR(18),
|
||||
`last_known_name` VARCHAR(18),
|
||||
`login_msg` VARCHAR(2000),
|
||||
`prefix` VARCHAR(2000),
|
||||
`staffChat` BOOLEAN,
|
||||
`ips` VARCHAR(2000),
|
||||
`coins` BIGINT,
|
||||
`vanished` BOOLEAN,
|
||||
`commandspy` BOOLEAN,
|
||||
PRIMARY KEY (`uuid`),
|
||||
INDEX `idx_players_name` (`name`)
|
||||
INDEX `idx_players_last_known_name` (`last_known_name`)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `punishments` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||
`punished` VARCHAR(46) NOT NULL,
|
||||
`punisher` VARCHAR(46),
|
||||
`punisherName` VARCHAR(64),
|
||||
`punishedUsername` VARCHAR(16),
|
||||
`punished_uuid` VARCHAR(46) NOT NULL,
|
||||
`punisher_uuid` VARCHAR(46),
|
||||
`source` VARCHAR(30),
|
||||
`punisher_reference` VARCHAR(200),
|
||||
`ip` VARCHAR(2000),
|
||||
`type` VARCHAR(30),
|
||||
`reason` VARCHAR(2000),
|
||||
@@ -26,7 +24,7 @@ CREATE TABLE IF NOT EXISTS `punishments` (
|
||||
`issueDate` BIGINT NOT NULL,
|
||||
`endDate` BIGINT,
|
||||
PRIMARY KEY (`id`),
|
||||
INDEX `idx_punishments_punished` (`punished`),
|
||||
INDEX `idx_punishments_punished` (`punished_uuid`),
|
||||
INDEX `idx_punishments_ip` (`ip`(64))
|
||||
);
|
||||
|
||||
@@ -34,7 +32,7 @@ CREATE TABLE IF NOT EXISTS `notes` (
|
||||
`row_id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||
`id` INT NOT NULL,
|
||||
`uuid` VARCHAR(46) NOT NULL,
|
||||
`written_by` VARCHAR(46),
|
||||
`written_by_uuid` VARCHAR(46),
|
||||
`note` VARCHAR(2000),
|
||||
`timestamp` BIGINT,
|
||||
PRIMARY KEY (`row_id`),
|
||||
@@ -49,3 +47,12 @@ CREATE TABLE IF NOT EXISTS `player_ips` (
|
||||
UNIQUE KEY `uq_player_ips_player_ip` (`player_uuid`, `ip`),
|
||||
INDEX `idx_player_ips_ip` (`ip`)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `player_module_data` (
|
||||
`player_uuid` VARCHAR(46) NOT NULL,
|
||||
`module` VARCHAR(100) NOT NULL,
|
||||
`data_key` VARCHAR(64) NOT NULL,
|
||||
`value_json` LONGTEXT NOT NULL,
|
||||
`updated_at` BIGINT NOT NULL,
|
||||
PRIMARY KEY (`player_uuid`, `module`, `data_key`)
|
||||
);
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
CREATE TABLE IF NOT EXISTS players (
|
||||
uuid VARCHAR(46) NOT NULL PRIMARY KEY,
|
||||
name VARCHAR(18),
|
||||
last_known_name VARCHAR(18),
|
||||
login_msg VARCHAR(2000),
|
||||
prefix VARCHAR(2000),
|
||||
staffChat BOOLEAN,
|
||||
ips VARCHAR(2000),
|
||||
coins BIGINT,
|
||||
vanished BOOLEAN,
|
||||
commandspy BOOLEAN
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_players_name ON players(name);
|
||||
CREATE INDEX IF NOT EXISTS idx_players_last_known_name ON players(last_known_name);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS punishments (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
punished VARCHAR(46) NOT NULL,
|
||||
punisher VARCHAR(46),
|
||||
punisherName VARCHAR(64),
|
||||
punishedUsername VARCHAR(16),
|
||||
punished_uuid VARCHAR(46) NOT NULL,
|
||||
punisher_uuid VARCHAR(46),
|
||||
source VARCHAR(30),
|
||||
punisher_reference VARCHAR(200),
|
||||
ip VARCHAR(2000),
|
||||
type VARCHAR(30),
|
||||
reason VARCHAR(2000),
|
||||
@@ -27,14 +25,14 @@ CREATE TABLE IF NOT EXISTS punishments (
|
||||
endDate BIGINT
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_punishments_punished ON punishments(punished);
|
||||
CREATE INDEX IF NOT EXISTS idx_punishments_punished ON punishments(punished_uuid);
|
||||
CREATE INDEX IF NOT EXISTS idx_punishments_ip ON punishments(ip);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS notes (
|
||||
row_id BIGSERIAL PRIMARY KEY,
|
||||
id INT NOT NULL,
|
||||
uuid VARCHAR(46) NOT NULL,
|
||||
written_by VARCHAR(46),
|
||||
written_by_uuid VARCHAR(46),
|
||||
note VARCHAR(2000),
|
||||
timestamp BIGINT
|
||||
);
|
||||
@@ -49,3 +47,12 @@ CREATE TABLE IF NOT EXISTS player_ips (
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_player_ips_ip ON player_ips(ip);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS player_module_data (
|
||||
player_uuid VARCHAR(46) NOT NULL,
|
||||
module VARCHAR(100) NOT NULL,
|
||||
data_key VARCHAR(64) NOT NULL,
|
||||
value_json TEXT NOT NULL,
|
||||
updated_at BIGINT NOT NULL,
|
||||
PRIMARY KEY (player_uuid, module, data_key)
|
||||
);
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
CREATE TABLE IF NOT EXISTS players (
|
||||
uuid VARCHAR(46) NOT NULL PRIMARY KEY,
|
||||
name VARCHAR(18),
|
||||
last_known_name VARCHAR(18),
|
||||
login_msg VARCHAR(2000),
|
||||
prefix VARCHAR(2000),
|
||||
staffChat BOOLEAN,
|
||||
ips VARCHAR(2000),
|
||||
coins BIGINT,
|
||||
vanished BOOLEAN,
|
||||
commandspy BOOLEAN
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS punishments (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
punished VARCHAR(46) NOT NULL,
|
||||
punisher VARCHAR(46),
|
||||
punisherName VARCHAR(64),
|
||||
punishedUsername VARCHAR(16),
|
||||
punished_uuid VARCHAR(46) NOT NULL,
|
||||
punisher_uuid VARCHAR(46),
|
||||
source VARCHAR(30),
|
||||
punisher_reference VARCHAR(200),
|
||||
ip VARCHAR(2000),
|
||||
type VARCHAR(30),
|
||||
reason VARCHAR(2000),
|
||||
@@ -29,7 +27,7 @@ CREATE TABLE IF NOT EXISTS notes (
|
||||
row_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
id INT NOT NULL,
|
||||
uuid VARCHAR(46) NOT NULL,
|
||||
written_by VARCHAR(46),
|
||||
written_by_uuid VARCHAR(46),
|
||||
note VARCHAR(2000),
|
||||
timestamp BIGINT
|
||||
);
|
||||
@@ -40,8 +38,17 @@ CREATE TABLE IF NOT EXISTS player_ips (
|
||||
ip VARCHAR(64) NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_players_name ON players(name);
|
||||
CREATE INDEX IF NOT EXISTS idx_punishments_punished ON punishments(punished);
|
||||
CREATE TABLE IF NOT EXISTS player_module_data (
|
||||
player_uuid VARCHAR(46) NOT NULL,
|
||||
module VARCHAR(100) NOT NULL,
|
||||
data_key VARCHAR(64) NOT NULL,
|
||||
value_json TEXT NOT NULL,
|
||||
updated_at INTEGER NOT NULL,
|
||||
PRIMARY KEY (player_uuid, module, data_key)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_players_last_known_name ON players(last_known_name);
|
||||
CREATE INDEX IF NOT EXISTS idx_punishments_punished ON punishments(punished_uuid);
|
||||
CREATE INDEX IF NOT EXISTS idx_punishments_ip ON punishments(ip);
|
||||
CREATE INDEX IF NOT EXISTS idx_notes_uuid ON notes(uuid);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_player_ips_player_ip ON player_ips(player_uuid, ip);
|
||||
|
||||
Reference in New Issue
Block a user