add storage for reports

This commit is contained in:
Telesphoreo 2024-05-26 13:16:46 -05:00
parent 85cef1be11
commit f579e65462
11 changed files with 350 additions and 7 deletions

View File

@ -26,8 +26,11 @@ java {
} }
dependencies { dependencies {
library(libs.lombok)
library(libs.hikari)
compileOnly(libs.paperApi) compileOnly(libs.paperApi)
implementation(libs.bundles.bstats) { isTransitive = false } implementation(libs.bundles.bstats) { isTransitive = false }
annotationProcessor(libs.lombok)
} }
tasks { tasks {

View File

@ -1,11 +1,15 @@
[versions] [versions]
paper = "1.20.4-R0.1-SNAPSHOT" paper = "1.20.4-R0.1-SNAPSHOT"
bstats = "3.0.2" bstats = "3.0.2"
lombok = "1.18.32"
hikari = "5.1.0"
[libraries] [libraries]
paperApi = { group = "io.papermc.paper", name = "paper-api", version.ref = "paper" } paperApi = { group = "io.papermc.paper", name = "paper-api", version.ref = "paper" }
bstatsBase = { group = "org.bstats", name = "bstats-base", version.ref = "bstats" } bstatsBase = { group = "org.bstats", name = "bstats-base", version.ref = "bstats" }
bstatsBukkit = { group = "org.bstats", name = "bstats-bukkit", version.ref = "bstats" } bstatsBukkit = { group = "org.bstats", name = "bstats-bukkit", version.ref = "bstats" }
lombok = { group = "org.projectlombok", name = "lombok", version.ref = "lombok" }
hikari = { group = "com.zaxxer", name = "HikariCP", version.ref = "hikari" }
[bundles] [bundles]
bstats = ["bstatsBase", "bstatsBukkit"] bstats = ["bstatsBase", "bstatsBukkit"]

View File

@ -2,17 +2,24 @@ package dev.plex.medina;
import dev.plex.medina.config.Config; import dev.plex.medina.config.Config;
import dev.plex.medina.registration.CommandRegistration; import dev.plex.medina.registration.CommandRegistration;
import dev.plex.medina.storage.SQLConnection;
import dev.plex.medina.util.MedinaUtils;
import lombok.Getter;
import org.bstats.bukkit.Metrics; import org.bstats.bukkit.Metrics;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
public class Medina extends JavaPlugin public class Medina extends JavaPlugin
{ {
@Getter
private static Medina plugin; private static Medina plugin;
public Config config; public Config config;
public Config messages; public Config messages;
@Getter
private SQLConnection sqlConnection;
@Override @Override
public void onLoad() public void onLoad()
{ {
@ -30,12 +37,9 @@ public class Medina extends JavaPlugin
// Metrics @ https://bstats.org/plugin/bukkit/Medina/22026 // Metrics @ https://bstats.org/plugin/bukkit/Medina/22026
Metrics metrics = new Metrics(this, 22026); Metrics metrics = new Metrics(this, 22026);
sqlConnection = new SQLConnection();
MedinaUtils.testConnection();
new CommandRegistration(); new CommandRegistration();
} }
public static Medina getPlugin()
{
return plugin;
}
} }

View File

@ -0,0 +1,38 @@
package dev.plex.medina.data;
import com.google.gson.GsonBuilder;
import dev.plex.medina.storage.annotation.PrimaryKey;
import dev.plex.medina.storage.annotation.TableName;
import dev.plex.medina.util.adapter.ZonedDateTimeAdapter;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import java.time.ZonedDateTime;
import java.util.UUID;
@Data
@TableName("reports")
public class Report
{
@PrimaryKey
private int reportId; // This will be automatically set from addReport
@Getter
private final UUID reporterUUID;
private final String reporterName;
private final UUID reportedUUID;
private final String reportedName;
private final ZonedDateTime timestamp;
private final String reason;
private final boolean deleted;
public String toJSON()
{
return new GsonBuilder().registerTypeAdapter(ZonedDateTime.class, new ZonedDateTimeAdapter()).create().toJson(this);
}
}

View File

@ -0,0 +1,104 @@
package dev.plex.medina.storage;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import dev.plex.medina.MedinaBase;
import lombok.Getter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@Getter
public class SQLConnection implements MedinaBase
{
private HikariDataSource dataSource;
public SQLConnection()
{
String host = plugin.config.getString("database.hostname");
int port = plugin.config.getInt("database.port");
String username = plugin.config.getString("database.username");
String password = plugin.config.getString("database.password");
String database = plugin.config.getString("database.name");
HikariConfig config = new HikariConfig();
config.addDataSourceProperty("cachePrepStmts", "true");
config.addDataSourceProperty("prepStmtCacheSize", "250");
config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
this.dataSource = new HikariDataSource();
dataSource.setMaxLifetime(15000);
dataSource.setIdleTimeout(15000 * 2);
dataSource.setConnectionTimeout(15000 * 4);
dataSource.setMinimumIdle(2);
dataSource.setMaximumPoolSize(10);
try
{
Class.forName("org.mariadb.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mariadb://" + host + ":" + port + "/" + database);
dataSource.setUsername(username);
dataSource.setPassword(password);
}
catch (ClassNotFoundException throwables)
{
throwables.printStackTrace();
}
try (Connection con = getCon())
{
con.prepareStatement("CREATE TABLE IF NOT EXISTS `reports` (" +
"`reportId` INT NOT NULL AUTOINCREMENT, " +
"`reporterUUID` VARCHAR(46) NOT NULL, " +
"`reporterName` VARCHAR(18), " +
"`reportedUUID` VARCHAR(46) NOT NULL, " +
"`reportedName` VARCHAR(18), " +
"`timestamp` BIGINT, " +
"`reason` VARCHAR(2000), " +
"`deleted` BOOLEAN, " +
"PRIMARY KEY (`reportId`));").execute();
}
catch (SQLException throwables)
{
throwables.printStackTrace();
}
}
private boolean tableExistsSQL(String tableName) throws SQLException
{
try (Connection connection = getCon())
{
PreparedStatement preparedStatement = connection.prepareStatement("SELECT count(*) "
+ "FROM information_schema.tables "
+ "WHERE table_name = ?"
+ "LIMIT 1;");
preparedStatement.setString(1, tableName);
ResultSet resultSet = preparedStatement.executeQuery();
resultSet.next();
return resultSet.getInt(1) != 0;
}
catch (SQLException ignored)
{
return false;
}
}
public Connection getCon()
{
if (this.dataSource == null)
{
return null;
}
try
{
return dataSource.getConnection();
}
catch (SQLException e)
{
e.printStackTrace();
}
return null;
}
}

View File

@ -0,0 +1,101 @@
package dev.plex.medina.storage;
import com.google.common.collect.Lists;
import dev.plex.medina.Medina;
import dev.plex.medina.MedinaBase;
import dev.plex.medina.data.Report;
import dev.plex.medina.util.MedinaUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
public class SQLReports implements MedinaBase
{
private static final String SELECT = "SELECT * FROM `reports` WHERE reportedUUID=?";
private static final String INSERT = "INSERT INTO `reports` (`reportId`, `reporterUUID`, `reporterName`, `reportedUUID`, `reportedName`, `timestamp`, `reason`, `deleted`) VALUES(?, ?, ?, ?, ?, ?, ?, ?)";
private static final String DELETE = "DELETE FROM `reports` WHERE reportId=? AND reportedUUID=?";
public CompletableFuture<List<Report>> getReports(UUID reportedUUID)
{
return CompletableFuture.supplyAsync(() ->
{
List<Report> reports = Lists.newArrayList();
try (Connection con = plugin.getSqlConnection().getCon())
{
PreparedStatement statement = con.prepareStatement(SELECT);
statement.setString(1, reportedUUID.toString());
ResultSet set = statement.executeQuery();
while (set.next())
{
Report report = new Report(
UUID.fromString(set.getString("reporterUUID")),
set.getString("reporterName"),
reportedUUID,
set.getString("reportedName"),
ZonedDateTime.ofInstant(Instant.ofEpochMilli(set.getLong("timestamp")), ZoneId.of(MedinaUtils.TIMEZONE)),
set.getString("reason"),
set.getBoolean("deleted"));
reports.add(report);
}
}
catch (SQLException e)
{
e.printStackTrace();
return reports;
}
return reports;
});
}
public CompletableFuture<Void> deleteReport(int reportId, UUID reportedUUID)
{
return CompletableFuture.runAsync(() ->
{
try (Connection con = plugin.getSqlConnection().getCon())
{
PreparedStatement statement = con.prepareStatement(DELETE);
statement.setInt(1, reportId);
statement.setString(2, reportedUUID.toString());
statement.execute();
}
catch (SQLException e)
{
e.printStackTrace();
}
});
}
public CompletableFuture<Void> addReport(Report report)
{
return CompletableFuture.runAsync(() ->
{
getReports(report.getReportedUUID()).whenComplete((reports, throwable) ->
{
try (Connection con = plugin.getSqlConnection().getCon())
{
PreparedStatement statement = con.prepareStatement(INSERT);
statement.setString(1, report.getReporterUUID().toString());
statement.setString(2, report.getReporterName());
statement.setString(3, report.getReportedUUID().toString());
statement.setString(4, report.getReportedName());
statement.setLong(5, report.getTimestamp().toInstant().toEpochMilli());
statement.setString(6, report.getReason());
statement.setBoolean(7, report.isDeleted());
statement.execute();
}
catch (SQLException e)
{
e.printStackTrace();
}
});
});
}
}

View File

@ -0,0 +1,13 @@
package dev.plex.medina.storage.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface PrimaryKey
{
boolean dontSet() default false;
}

View File

@ -0,0 +1,13 @@
package dev.plex.medina.storage.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface TableName
{
String value();
}

View File

@ -1,14 +1,19 @@
package dev.plex.medina.util; package dev.plex.medina.util;
import dev.plex.medina.Medina;
import dev.plex.medina.MedinaBase; import dev.plex.medina.MedinaBase;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import java.sql.Connection;
import java.sql.SQLException;
public class MedinaUtils implements MedinaBase public class MedinaUtils implements MedinaBase
{ {
private static final MiniMessage MINI_MESSAGE = MiniMessage.miniMessage(); private static final MiniMessage MINI_MESSAGE = MiniMessage.miniMessage();
public static String TIMEZONE = plugin.config.getString("timezone");
public static Component mmDeserialize(String input) public static Component mmDeserialize(String input)
{ {
@ -54,4 +59,24 @@ public class MedinaUtils implements MedinaBase
} }
return f; return f;
} }
public static void testConnection()
{
MedinaLog.log("Attempting to connect to DB: {0}", plugin.config.getString("database.name"));
if (plugin.getSqlConnection().getDataSource() != null)
{
try (Connection ignored = plugin.getSqlConnection().getCon())
{
MedinaLog.log("Connected to " + plugin.config.getString("database.name"));
}
catch (SQLException e)
{
MedinaLog.error("Failed to connect to " + plugin.config.getString("database.name"));
}
}
else
{
MedinaLog.error("Unable to initialize Hikari data source!");
}
}
} }

View File

@ -0,0 +1,27 @@
package dev.plex.medina.util.adapter;
import com.google.gson.*;
import dev.plex.medina.Medina;
import java.lang.reflect.Type;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class ZonedDateTimeAdapter implements JsonSerializer<ZonedDateTime>, JsonDeserializer<ZonedDateTime>
{
private static final String TIMEZONE = Medina.getPlugin().config.getString("timezone");
@Override
public JsonElement serialize(ZonedDateTime src, Type typeOfSrc, JsonSerializationContext context)
{
return new JsonPrimitive(src.toInstant().toEpochMilli());
}
@Override
public ZonedDateTime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
{
Instant instant = Instant.ofEpochMilli(json.getAsJsonPrimitive().getAsLong());
return ZonedDateTime.ofInstant(instant, ZoneId.of(TIMEZONE));
}
}

View File

@ -1 +1,12 @@
# Medina Configuration File # Medina Configuration File
# Database configuration
database:
name: medina
hostname: 127.0.0.1
port: 3306
username: minecraft
password: medina
# The timezone the reports will show up as
timezone: Etc/UTC