Add SQL Generators

Add cache for table strings generated
Add legacy support for Tags and login messages by converting legacy color codes to minimessage tags
This commit is contained in:
Taah 2023-08-26 06:59:29 -07:00
parent 1f48dc1e60
commit b0a8b463cb
20 changed files with 384 additions and 26 deletions

View File

@ -11,6 +11,7 @@ dependencies {
library "org.mariadb.jdbc:mariadb-java-client:3.1.4"
library "com.zaxxer:HikariCP:5.0.1"
library "org.apache.maven.resolver:maven-resolver-transport-http:1.9.15"
library "org.jetbrains:annotations:24.0.1"
compileOnly "dev.folia:folia-api:1.20.1-R0.1-SNAPSHOT"
compileOnly("com.github.MilkBowl:VaultAPI:1.7.1") {
exclude group: "org.bukkit", module: "bukkit"
@ -58,6 +59,10 @@ paper {
required = false
load = 'BEFORE'
}
'SlimeWorldManager' {
required = false
load = 'AFTER'
}
}
}

View File

@ -1,5 +1,6 @@
package dev.plex;
import com.google.common.collect.Lists;
import dev.plex.cache.DataUtils;
import dev.plex.cache.PlayerCache;
import dev.plex.config.Config;
@ -17,11 +18,13 @@ import dev.plex.storage.punishment.SQLNotes;
import dev.plex.storage.punishment.SQLPunishment;
import dev.plex.util.*;
import dev.plex.util.redis.MessageUtil;
import dev.plex.util.sql.SQLUtil;
import dev.plex.world.CustomWorld;
import lombok.Getter;
import lombok.Setter;
import net.milkbowl.vault.chat.Chat;
import net.milkbowl.vault.permission.Permission;
import org.apache.commons.lang3.StringUtils;
import org.bstats.bukkit.Metrics;
import org.bukkit.Bukkit;
import org.bukkit.plugin.RegisteredServiceProvider;

View File

@ -8,6 +8,7 @@ import dev.plex.command.exception.CommandFailException;
import dev.plex.command.source.RequiredCommandSource;
import dev.plex.player.PlexPlayer;
import dev.plex.util.PlexLog;
import dev.plex.util.PlexUtils;
import net.kyori.adventure.text.Component;
import org.apache.commons.lang3.StringUtils;
import org.bukkit.command.CommandSender;
@ -53,8 +54,7 @@ public class SetLoginMessageCMD extends PlexCommand
validateMessage(message);
plexPlayer.setLoginMessage(message);
return messageComponent("setOtherPlayersLoginMessage", plexPlayer.getName(),
message.replace("%player%", plexPlayer.getName())
.replace("%rank%", plexPlayer.getRank()));
message.replace("%player%", plexPlayer.getName()));
}
if (isConsole(sender))
{
@ -63,11 +63,11 @@ public class SetLoginMessageCMD extends PlexCommand
PlexPlayer plexPlayer = plugin.getPlayerCache().getPlexPlayer(playerSender.getUniqueId());
String message = StringUtils.join(args, " ", 0, args.length);
message = message.replace(plexPlayer.getName(), "%player%");
message = PlexUtils.legacyToMiniString(message);
validateMessage(message);
plexPlayer.setLoginMessage(message);
return messageComponent("setOwnLoginMessage",
message.replace("%player%", plexPlayer.getName())
.replace("%rank%", plexPlayer.getRank()));
message.replace("%player%", plexPlayer.getName()));
}
return null;
}

View File

@ -47,7 +47,7 @@ public class TagCMD extends PlexCommand
{
return usage("/tag set <prefix>");
}
String prefix = PlexUtils.cleanString(StringUtils.join(args, " ", 1, args.length));
String prefix = PlexUtils.legacyToMiniString(StringUtils.join(args, " ", 1, args.length));
Component convertedComponent = SafeMiniMessage.mmDeserializeWithoutEvents(prefix);

View File

@ -101,7 +101,7 @@ public class ChatListener extends PlexListener
private static void defaultChatProcessing(AsyncChatEvent event, PlexPlayer plexPlayer)
{
String text = PlexUtils.cleanString(PlexUtils.getTextFromComponent(event.message()));
String text = PlexUtils.legacyToMiniString(PlexUtils.getTextFromComponent(event.message()));
event.message(SafeMiniMessage.mmDeserializeWithoutEvents(text));
}
}

View File

@ -37,8 +37,7 @@ public class PlayerMeta
if (!plexPlayer.getLoginMessage().isEmpty())
{
return plexPlayer.getLoginMessage()
.replace("%player%", plexPlayer.getName())
.replace("%rank%", plexPlayer.getRank());
.replace("%player%", plexPlayer.getName());
}
else
{

View File

@ -5,7 +5,10 @@ import com.google.gson.GsonBuilder;
import dev.plex.Plex;
import dev.plex.punishment.Punishment;
import dev.plex.punishment.extra.Note;
import dev.plex.storage.annotation.MapObjectList;
import dev.plex.storage.annotation.PrimaryKey;
import dev.plex.storage.annotation.SQLTable;
import dev.plex.storage.annotation.VarcharLimit;
import dev.plex.util.adapter.ZonedDateTimeAdapter;
import lombok.AccessLevel;
import lombok.Getter;
@ -14,6 +17,7 @@ import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.permissions.PermissionAttachment;
import org.jetbrains.annotations.NotNull;
import java.time.ZonedDateTime;
import java.util.List;
@ -21,11 +25,16 @@ import java.util.UUID;
@Getter
@Setter
@SQLTable("players")
public class PlexPlayer
{
@Setter(AccessLevel.NONE)
@PrimaryKey
@NotNull
private UUID uuid;
@VarcharLimit(16)
@NotNull
private String name;
private String loginMessage;
@ -44,13 +53,13 @@ public class PlexPlayer
private long coins;
private String rank;
private List<String> ips = Lists.newArrayList();
private List<Punishment> punishments = Lists.newArrayList();
private List<Note> notes = Lists.newArrayList();
private transient PermissionAttachment permissionAttachment;
@MapObjectList
private List<Punishment> punishments = Lists.newArrayList();
@MapObjectList
private List<Note> notes = Lists.newArrayList();
public PlexPlayer()
{
@ -69,7 +78,6 @@ public class PlexPlayer
this.coins = 0;
this.rank = "";
if (loadExtraData)
{
this.loadPunishments();

View File

@ -3,6 +3,7 @@ package dev.plex.punishment;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import dev.plex.Plex;
import dev.plex.storage.annotation.SQLTable;
import dev.plex.util.MojangUtils;
import dev.plex.util.PlexUtils;
import dev.plex.util.TimeUtils;
@ -10,16 +11,19 @@ import dev.plex.util.adapter.ZonedDateTimeAdapter;
import lombok.Getter;
import lombok.Setter;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.NotNull;
import java.time.ZonedDateTime;
import java.util.UUID;
@Getter
@Setter
@SQLTable("punishments")
public class Punishment
{
private static final Gson gson = new GsonBuilder().registerTypeAdapter(ZonedDateTime.class, new ZonedDateTimeAdapter()).create();
private static final String banUrl = Plex.get().config.getString("banning.ban_url");
@NotNull
private final UUID punished;
private final UUID punisher;
private String ip;

View File

@ -1,6 +1,8 @@
package dev.plex.punishment.extra;
import com.google.gson.GsonBuilder;
import dev.plex.storage.annotation.NoLimit;
import dev.plex.storage.annotation.SQLTable;
import dev.plex.util.adapter.ZonedDateTimeAdapter;
import lombok.Data;
@ -8,9 +10,12 @@ import java.time.ZonedDateTime;
import java.util.UUID;
@Data
@SQLTable("notes")
public class Note
{
private final UUID uuid;
@NoLimit
private final String note;
private final UUID writtenBy;
private final ZonedDateTime timestamp;

View File

@ -0,0 +1,17 @@
package dev.plex.storage.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author Taah
* @since 1:42 AM [25-08-2023]
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface MapObjectList
{
}

View File

@ -0,0 +1,17 @@
package dev.plex.storage.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author Taah
* @since 1:42 AM [25-08-2023]
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface NoLimit
{
}

View File

@ -14,4 +14,5 @@ import java.lang.annotation.Target;
@Target(ElementType.FIELD)
public @interface PrimaryKey
{
boolean dontSet() default false;
}

View File

@ -0,0 +1,18 @@
package dev.plex.storage.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author Taah
* @since 4:27 AM [25-08-2023]
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface SQLTable
{
String value();
}

View File

@ -0,0 +1,18 @@
package dev.plex.storage.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author Taah
* @since 1:42 AM [25-08-2023]
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface VarcharLimit
{
int value();
}

View File

@ -86,7 +86,6 @@ public class SQLPlayerData
String name = set.getString("name");
String loginMSG = set.getString("login_msg");
String prefix = set.getString("prefix");
String rankName = set.getString("rank").toUpperCase();
boolean adminActive = set.getBoolean("adminActive");
long coins = set.getLong("coins");
boolean vanished = set.getBoolean("vanished");
@ -97,7 +96,6 @@ public class SQLPlayerData
plexPlayer.setName(name);
plexPlayer.setLoginMessage(loginMSG);
plexPlayer.setPrefix(prefix);
plexPlayer.setRank(rankName);
plexPlayer.setAdminActive(adminActive);
plexPlayer.setIps(ips);
plexPlayer.setCoins(coins);
@ -135,7 +133,6 @@ public class SQLPlayerData
PlexPlayer plexPlayer = new PlexPlayer(UUID.fromString(set.getString("uuid")), loadExtraData);
String loginMSG = set.getString("login_msg");
String prefix = set.getString("prefix");
String rankName = set.getString("rank").toUpperCase();
boolean adminActive = set.getBoolean("adminActive");
long coins = set.getLong("coins");
boolean vanished = set.getBoolean("vanished");
@ -146,7 +143,6 @@ public class SQLPlayerData
plexPlayer.setName(username);
plexPlayer.setLoginMessage(loginMSG);
plexPlayer.setPrefix(prefix);
plexPlayer.setRank(rankName);
plexPlayer.setAdminActive(adminActive);
plexPlayer.setIps(ips);
plexPlayer.setCoins(coins);
@ -211,7 +207,6 @@ public class SQLPlayerData
plexPlayer.setName(name);
plexPlayer.setLoginMessage(loginMSG);
plexPlayer.setPrefix(prefix);
plexPlayer.setRank(rankName);
plexPlayer.setAdminActive(adminActive);
plexPlayer.setIps(ips);
plexPlayer.setCoins(coins);
@ -257,7 +252,6 @@ public class SQLPlayerData
plexPlayer.setName(name);
plexPlayer.setLoginMessage(loginMSG);
plexPlayer.setPrefix(prefix);
plexPlayer.setRank(rankName);
plexPlayer.setAdminActive(adminActive);
plexPlayer.setIps(ips);
plexPlayer.setCoins(coins);
@ -288,7 +282,7 @@ public class SQLPlayerData
statement.setString(1, player.getName());
statement.setString(2, player.getLoginMessage());
statement.setString(3, player.getPrefix());
statement.setString(4, player.getRank().toLowerCase());
statement.setString(4, null); // rank
statement.setBoolean(5, player.isAdminActive());
statement.setString(6, new Gson().toJson(player.getIps()));
statement.setLong(7, player.getCoins());
@ -318,7 +312,7 @@ public class SQLPlayerData
statement.setString(2, player.getName());
statement.setString(3, player.getLoginMessage());
statement.setString(4, player.getPrefix());
statement.setString(5, player.getRank().toLowerCase());
statement.setString(5, null); //rank
statement.setBoolean(6, player.isAdminActive());
statement.setString(7, new Gson().toJson(player.getIps()));
statement.setLong(8, player.getCoins());

View File

@ -13,6 +13,7 @@ public class PlexLog
{
for (int i = 0; i < strings.length; i++)
{
if (strings[i] == null) continue;
if (message.contains("{" + i + "}"))
{
message = message.replace("{" + i + "}", strings[i].toString());
@ -30,6 +31,7 @@ public class PlexLog
{
for (int i = 0; i < strings.length; i++)
{
if (strings[i] == null) continue;
if (message.contains("{" + i + "}"))
{
message = message.replace("{" + i + "}", strings[i].toString());
@ -42,6 +44,7 @@ public class PlexLog
{
for (int i = 0; i < strings.length; i++)
{
if (strings[i] == null) continue;
if (message.contains("{" + i + "}"))
{
message = message.replace("{" + i + "}", strings[i].toString());
@ -56,6 +59,7 @@ public class PlexLog
{
for (int i = 0; i < strings.length; i++)
{
if (strings[i] == null) continue;
if (message.contains("{" + i + "}"))
{
message = message.replace("{" + i + "}", strings[i].toString());

View File

@ -5,7 +5,6 @@ import com.google.common.collect.Lists;
import dev.plex.Plex;
import dev.plex.PlexBase;
import dev.plex.listener.impl.ChatListener;
import dev.plex.player.PlexPlayer;
import dev.plex.storage.StorageType;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
@ -27,6 +26,7 @@ import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Stack;
import java.util.UUID;
import java.util.stream.Collectors;
@ -98,11 +98,14 @@ public class PlexUtils implements PlexBase
}
}
public static boolean isFolia() {
try {
public static boolean isFolia()
{
try
{
Class.forName("io.papermc.paper.threadedregions.ThreadedRegionizer");
}
catch (Exception e) {
catch (Exception e)
{
return false;
}
@ -129,6 +132,45 @@ public class PlexUtils implements PlexBase
return false;
}
public static Component removeHoverAndClick(Component component)
{
Stack<Component> components = new Stack<>();
components.push(component);
while (!components.isEmpty())
{
Component curr = components.pop();
curr.clickEvent(null).hoverEvent(null);
curr.children().forEach(components::push);
}
return component;
}
public static String legacyToMiniString(String input)
{
return cleanString(input.replace("&a", "<green>")
.replace("&b", "<aqua>")
.replace("&c", "<red>")
.replace("&d", "<light_purple>")
.replace("&e", "<yellow>")
.replace("&f", "<white>")
.replace("&1", "<dark_blue>")
.replace("&2", "<dark_green>")
.replace("&3", "<dark_aqua>")
.replace("&4", "<dark_red>")
.replace("&5", "<dark_purple>")
.replace("&6", "<gold>")
.replace("&7", "<gray>")
.replace("&8", "<dark_gray>")
.replace("&9", "<blue>")
.replace("&0", "<black>")
.replace("&r", "<reset>")
.replace("&l", "<bold>")
.replace("&o", "<italic>")
.replace("&n", "<underlined>")
.replace("&m", "<strikethrough>")
.replace("&k", "<obf>"));
}
public static String mmStripColor(String input)
{
return PlainTextComponentSerializer.plainText().serialize(mmDeserialize(input));

View File

@ -2,9 +2,13 @@ package dev.plex.util;
import com.google.common.collect.ImmutableSet;
import com.google.common.reflect.ClassPath;
import com.google.common.reflect.TypeToken;
import dev.plex.Plex;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
@ -54,4 +58,14 @@ public class ReflectionsUtil
});
return Collections.unmodifiableSet(classes);
}
public static Class<?> getGenericField(Field field)
{
Type type = field.getGenericType();
if (type instanceof ParameterizedType parameterizedType)
{
return TypeToken.of(parameterizedType.getActualTypeArguments()[0]).getRawType();
}
return field.getType();
}
}

View File

@ -0,0 +1,186 @@
package dev.plex.util.sql;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import dev.plex.punishment.PunishmentType;
import dev.plex.storage.annotation.*;
import dev.plex.util.PlexLog;
import dev.plex.util.ReflectionsUtil;
import lombok.experimental.Accessors;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.time.ZonedDateTime;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
/**
* @author Taah
* @since 4:28 AM [25-08-2023]
*/
public class SQLUtil
{
public static final List<Table> TABLES = Lists.newArrayList();
public static List<String> createTable(List<String> result, Class<?> clazz)
{
if (!clazz.isAnnotationPresent(SQLTable.class))
{
PlexLog.error("Unable to map {0} to a table, it is missing the SQLTable's annotation", clazz.getName());
return null;
}
final List<Field> collectionFields = Lists.newArrayList();
final Table table = new Table(clazz.getAnnotation(SQLTable.class).value());
final StringBuilder mainResult = new StringBuilder("CREATE TABLE IF NOT EXISTS `" + table.name() + "` (");
final List<Field> declaredFields = Arrays.stream(clazz.getDeclaredFields()).filter(field -> !Modifier.isTransient(field.getModifiers()) && !Modifier.isStatic(field.getModifiers())).collect(Collectors.toList());
final List<Field> iterating = declaredFields.stream().toList();
for (Field value : iterating)
{
if (Collection.class.isAssignableFrom(value.getType()))
{
collectionFields.add(value);
declaredFields.remove(value);
}
}
Field primaryKey = null;
for (int i = 0; i < declaredFields.size(); i++)
{
Field declaredField = declaredFields.get(i);
final Mapper mapped = Mapper.getByClass(declaredField.getType());
if (mapped == null)
{
PlexLog.warn("Could not map field {0} for class {1}", declaredField.getName(), clazz.getName());
continue;
}
if (declaredField.isAnnotationPresent(PrimaryKey.class))
{
if (primaryKey != null)
{
PlexLog.error("You can only have one primary key for a table! The class {0} has more than one!", clazz.getName());
return ImmutableList.of();
}
primaryKey = declaredField;
}
writeFieldToSQL(table, mainResult, mapped, declaredField);
if (i < declaredFields.size() - 1)
{
mainResult.append(", ");
}
}
if (primaryKey != null && !primaryKey.getAnnotation(PrimaryKey.class).dontSet())
{
mainResult.append(", PRIMARY KEY (`").append(primaryKey.getName()).append("`)");
}
mainResult.append(");");
result.add(mainResult.toString());
TABLES.add(table);
if (primaryKey == null && !collectionFields.isEmpty())
{
PlexLog.error("You must define a primary key to point to if you wish to have a list saved. You can use @PrimaryKey(dontSet = true) to make sure that SQL does not save it as a primary key.");
return ImmutableList.of();
}
Field finalPrimaryKey = primaryKey;
collectionFields.forEach(field ->
{
final String tableName = field.getName() + "To" + StringUtils.capitalize(clazz.getSimpleName());
StringBuilder sql = new StringBuilder("CREATE TABLE IF NOT EXISTS `" + tableName + "` (");
if (field.isAnnotationPresent(MapObjectList.class))
{
createTable(result, ReflectionsUtil.getGenericField(field));
return;
}
final Mapper mapped = Mapper.getByClass(ReflectionsUtil.getGenericField(field));
if (mapped == null)
{
PlexLog.warn("Could not map collection field {0} for class {1}", field.getName(), clazz.getName());
return;
}
final Table listTable = new Table(tableName);
writeFieldToSQL(listTable, sql, mapped, field);
sql.append(", ");
writeFieldToSQL(listTable, sql, Mapper.getByClass(finalPrimaryKey.getType()), finalPrimaryKey);
sql.append(");");
result.add(sql.toString());
table.mappedTables().add(listTable);
});
return result;
}
private static void writeFieldToSQL(Table table, StringBuilder sb, Mapper mapped, Field field)
{
sb.append("`").append(field.getName()).append("` ");
if (mapped == Mapper.VARCHAR)
{
if (field.isAnnotationPresent(NoLimit.class))
{
sb.append("TEXT");
table.columns().put(field.getName(), Mapper.TEXT);
}
else
{
sb.append(mapped.name());
table.columns().put(field.getName(), mapped);
}
}
else
{
sb.append(mapped.name());
table.columns().put(field.getName(), mapped);
}
if (mapped == Mapper.VARCHAR && !field.isAnnotationPresent(NoLimit.class))
{
if (UUID.class.isAssignableFrom(field.getType()))
{
sb.append(" (").append(36).append(")");
}
else if (field.isAnnotationPresent(VarcharLimit.class))
{
int limit = field.getAnnotation(VarcharLimit.class).value();
sb.append(" (").append(limit).append(")");
}
else
{
sb.append("(65535)");
}
}
if (field.isAnnotationPresent(NotNull.class))
{
sb.append(" NOT NULL");
}
}
@Accessors(fluent = true)
public enum Mapper
{
VARCHAR(String.class, UUID.class, PunishmentType.class),
BOOLEAN(Boolean.class, boolean.class),
BIGINT(Long.class, long.class, ZonedDateTime.class),
INT(Integer.class, int.class),
TEXT;
private final Class<?>[] clazz;
Mapper(Class<?>... clazz)
{
this.clazz = clazz;
}
public static Mapper getByClass(Class<?> clazz)
{
return Arrays.stream(values()).filter(mapper -> mapper.clazz != null && Arrays.asList(mapper.clazz).contains(clazz)).findFirst().orElse(null);
}
}
}

View File

@ -0,0 +1,23 @@
package dev.plex.util.sql;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
import java.util.Map;
/**
* @author Taah
* @since 5:30 AM [26-08-2023]
*/
@Data
@Accessors(fluent = true)
public class Table
{
private final String name;
private final Map<String, SQLUtil.Mapper> columns = Maps.newHashMap();
private final List<Table> mappedTables = Lists.newArrayList();
}