1
0
mirror of https://github.com/GeyserMC/Floodgate.git synced 2025-12-22 16:29:25 +00:00

Switched to Hikari for MySQL

This commit is contained in:
Tim203
2022-08-31 03:03:42 +02:00
parent eca042dc82
commit 0f152141a2
9 changed files with 349 additions and 318 deletions

View File

@@ -32,6 +32,7 @@ import com.google.gson.JsonObject;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Injector; import com.google.inject.Injector;
import com.google.inject.Singleton; import com.google.inject.Singleton;
import com.google.inject.name.Named;
import com.google.inject.name.Names; import com.google.inject.name.Names;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@@ -42,18 +43,23 @@ import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.annotation.Nonnull; import java.util.stream.Stream;
import javax.inject.Named; import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.event.Listener;
import org.geysermc.event.subscribe.Subscribe;
import org.geysermc.floodgate.api.link.PlayerLink; import org.geysermc.floodgate.api.link.PlayerLink;
import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.api.logger.FloodgateLogger;
import org.geysermc.floodgate.config.FloodgateConfig; import org.geysermc.floodgate.config.FloodgateConfig;
import org.geysermc.floodgate.config.FloodgateConfig.PlayerLinkConfig;
import org.geysermc.floodgate.event.ShutdownEvent;
import org.geysermc.floodgate.util.Constants; import org.geysermc.floodgate.util.Constants;
import org.geysermc.floodgate.util.InjectorHolder; import org.geysermc.floodgate.util.InjectorHolder;
import org.geysermc.floodgate.util.Utils; import org.geysermc.floodgate.util.Utils;
@Listener
@Singleton @Singleton
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public final class PlayerLinkLoader { public final class PlayerLinkHolder {
@Inject private Injector injector; @Inject private Injector injector;
@Inject private FloodgateConfig config; @Inject private FloodgateConfig config;
@Inject private FloodgateLogger logger; @Inject private FloodgateLogger logger;
@@ -62,30 +68,38 @@ public final class PlayerLinkLoader {
@Named("dataDirectory") @Named("dataDirectory")
private Path dataDirectory; private Path dataDirectory;
@Nonnull private URLClassLoader classLoader;
private PlayerLink instance;
@NonNull
public PlayerLink load() { public PlayerLink load() {
if (instance != null) {
return instance;
}
if (config == null) { if (config == null) {
throw new IllegalStateException("Config cannot be null!"); throw new IllegalStateException("Config cannot be null!");
} }
FloodgateConfig.PlayerLinkConfig lConfig = config.getPlayerLink(); PlayerLinkConfig linkConfig = config.getPlayerLink();
if (!lConfig.isEnabled()) { if (!linkConfig.isEnabled()) {
return new DisabledPlayerLink(); return new DisabledPlayerLink();
} }
List<Path> files; List<Path> files;
try { try (Stream<Path> list = Files.list(dataDirectory)) {
files = Files.list(dataDirectory) files = list
.filter(path -> Files.isRegularFile(path) && path.toString().endsWith("jar")) .filter(path -> Files.isRegularFile(path) && path.toString().endsWith(".jar"))
.collect(Collectors.toList()); .collect(Collectors.toList());
} catch (IOException exception) { } catch (IOException exception) {
logger.error("Failed to list possible database implementations", exception); logger.error("Failed to list possible database implementations", exception);
return new DisabledPlayerLink(); return new DisabledPlayerLink();
} }
// we can skip the rest if global linking is enabled and no database implementations has been // we can skip the rest if global linking is enabled and no database implementations has
// found, or when global linking is enabled and own player linking is disabled. // been found, or when global linking is enabled and own player linking is disabled.
if (lConfig.isEnableGlobalLinking() && (files.isEmpty() || !lConfig.isEnableOwnLinking())) { if (linkConfig.isEnableGlobalLinking() &&
(files.isEmpty() || !linkConfig.isEnableOwnLinking())) {
return injector.getInstance(GlobalPlayerLinking.class); return injector.getInstance(GlobalPlayerLinking.class);
} }
@@ -100,7 +114,7 @@ public final class PlayerLinkLoader {
// We only want to load one database implementation // We only want to load one database implementation
if (files.size() > 1) { if (files.size() > 1) {
boolean found = false; boolean found = false;
databaseName = lConfig.getType(); databaseName = linkConfig.getType();
String expectedName = "floodgate-" + databaseName + "-database.jar"; String expectedName = "floodgate-" + databaseName + "-database.jar";
for (Path path : files) { for (Path path : files) {
@@ -111,14 +125,18 @@ public final class PlayerLinkLoader {
} }
if (!found) { if (!found) {
logger.error("Failed to find an implementation for type: {}", lConfig.getType()); logger.error(
"Failed to find an implementation for type: {}", linkConfig.getType()
);
return new DisabledPlayerLink(); return new DisabledPlayerLink();
} }
} else { } else {
String name = implementationPath.getFileName().toString(); String name = implementationPath.getFileName().toString();
if (!Utils.isValidDatabaseName(name)) { if (!Utils.isValidDatabaseName(name)) {
logger.error("Found database {} but the name doesn't match {}", logger.error(
name, Constants.DATABASE_NAME_FORMAT); "Found database {} but the name doesn't match {}",
name, Constants.DATABASE_NAME_FORMAT
);
return new DisabledPlayerLink(); return new DisabledPlayerLink();
} }
int firstSplit = name.indexOf('-') + 1; int firstSplit = name.indexOf('-') + 1;
@@ -133,24 +151,22 @@ public final class PlayerLinkLoader {
// we don't have a way to close this properly since we have no stop method and we have // we don't have a way to close this properly since we have no stop method and we have
// to be able to load classes on the fly, but that doesn't matter anyway since Floodgate // to be able to load classes on the fly, but that doesn't matter anyway since Floodgate
// doesn't support reloading // doesn't support reloading
URLClassLoader classLoader = new URLClassLoader( classLoader = new URLClassLoader(
new URL[]{pluginUrl}, new URL[]{pluginUrl},
PlayerLinkLoader.class.getClassLoader() PlayerLinkHolder.class.getClassLoader()
); );
String mainClassName; String mainClassName;
JsonObject linkConfig; JsonObject dbInitConfig;
try (InputStream linkConfigStream =
classLoader.getResourceAsStream("init.json")) {
try (InputStream linkConfigStream = classLoader.getResourceAsStream("init.json")) {
requireNonNull(linkConfigStream, "Implementation should have an init file"); requireNonNull(linkConfigStream, "Implementation should have an init file");
linkConfig = new Gson().fromJson( dbInitConfig = new Gson().fromJson(
new InputStreamReader(linkConfigStream), JsonObject.class new InputStreamReader(linkConfigStream), JsonObject.class
); );
mainClassName = linkConfig.get("mainClass").getAsString(); mainClassName = dbInitConfig.get("mainClass").getAsString();
} }
Class<? extends PlayerLink> mainClass = Class<? extends PlayerLink> mainClass =
@@ -167,16 +183,16 @@ public final class PlayerLinkLoader {
Names.named("databaseClassLoader")).toInstance(classLoader); Names.named("databaseClassLoader")).toInstance(classLoader);
binder.bind(JsonObject.class) binder.bind(JsonObject.class)
.annotatedWith(Names.named("databaseInitData")) .annotatedWith(Names.named("databaseInitData"))
.toInstance(linkConfig); .toInstance(dbInitConfig);
binder.bind(InjectorHolder.class) binder.bind(InjectorHolder.class)
.toInstance(injectorHolder); .toInstance(injectorHolder);
}); });
injectorHolder.set(linkInjector); injectorHolder.set(linkInjector);
PlayerLink instance = linkInjector.getInstance(mainClass); instance = linkInjector.getInstance(mainClass);
// we use our own internal PlayerLinking when global linking is enabled // we use our own internal PlayerLinking when global linking is enabled
if (lConfig.isEnableGlobalLinking()) { if (linkConfig.isEnableGlobalLinking()) {
GlobalPlayerLinking linking = linkInjector.getInstance(GlobalPlayerLinking.class); GlobalPlayerLinking linking = linkInjector.getInstance(GlobalPlayerLinking.class);
linking.setDatabaseImpl(instance); linking.setDatabaseImpl(instance);
linking.load(); linking.load();
@@ -186,8 +202,10 @@ public final class PlayerLinkLoader {
return instance; return instance;
} }
} catch (ClassCastException exception) { } catch (ClassCastException exception) {
logger.error("The database implementation ({}) doesn't extend the PlayerLink class!", logger.error(
implementationPath.getFileName().toString(), exception); "The database implementation ({}) doesn't extend the PlayerLink class!",
implementationPath.getFileName().toString(), exception
);
return new DisabledPlayerLink(); return new DisabledPlayerLink();
} catch (Exception exception) { } catch (Exception exception) {
if (init) { if (init) {
@@ -198,4 +216,10 @@ public final class PlayerLinkLoader {
return new DisabledPlayerLink(); return new DisabledPlayerLink();
} }
} }
@Subscribe
public void onShutdown(ShutdownEvent ignored) throws Exception {
instance.stop();
classLoader.close();
}
} }

View File

@@ -55,7 +55,7 @@ import org.geysermc.floodgate.crypto.KeyProducer;
import org.geysermc.floodgate.event.EventBus; import org.geysermc.floodgate.event.EventBus;
import org.geysermc.floodgate.event.util.ListenerAnnotationMatcher; import org.geysermc.floodgate.event.util.ListenerAnnotationMatcher;
import org.geysermc.floodgate.inject.CommonPlatformInjector; import org.geysermc.floodgate.inject.CommonPlatformInjector;
import org.geysermc.floodgate.link.PlayerLinkLoader; import org.geysermc.floodgate.link.PlayerLinkHolder;
import org.geysermc.floodgate.packet.PacketHandlersImpl; import org.geysermc.floodgate.packet.PacketHandlersImpl;
import org.geysermc.floodgate.player.FloodgateHandshakeHandler; import org.geysermc.floodgate.player.FloodgateHandshakeHandler;
import org.geysermc.floodgate.pluginmessage.PluginMessageManager; import org.geysermc.floodgate.pluginmessage.PluginMessageManager;
@@ -102,7 +102,7 @@ public class CommonModule extends AbstractModule {
@Provides @Provides
@Singleton @Singleton
public PlayerLink playerLink(PlayerLinkLoader linkLoader) { public PlayerLink playerLink(PlayerLinkHolder linkLoader) {
return linkLoader.load(); return linkLoader.load();
} }

View File

@@ -26,13 +26,13 @@
package org.geysermc.floodgate.util; package org.geysermc.floodgate.util;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.name.Named;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import javax.inject.Named;
import org.bstats.MetricsBase; import org.bstats.MetricsBase;
import org.bstats.charts.DrilldownPie; import org.bstats.charts.DrilldownPie;
import org.bstats.charts.SimplePie; import org.bstats.charts.SimplePie;

View File

@@ -0,0 +1,4 @@
[*]
indent_size = 2
tab_width = 2
ij_continuation_indent_size = 4

View File

@@ -1,8 +1,10 @@
val mariadbClientVersion = "2.7.4" val mysqlClientVersion = "8.0.30"
val hikariVersion = "4.0.3"
dependencies { dependencies {
provided(projects.core) provided(projects.core)
implementation("org.mariadb.jdbc", "mariadb-java-client" , mariadbClientVersion) implementation("mysql", "mysql-connector-java", mysqlClientVersion)
implementation("com.zaxxer", "HikariCP", hikariVersion)
} }
description = "The Floodgate database extension for MySQL" description = "The Floodgate database extension for MySQL"

View File

@@ -25,6 +25,8 @@
package org.geysermc.floodgate.database; package org.geysermc.floodgate.database;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.sql.Connection; import java.sql.Connection;
@@ -43,41 +45,28 @@ import org.geysermc.floodgate.database.config.MysqlConfig;
import org.geysermc.floodgate.link.CommonPlayerLink; import org.geysermc.floodgate.link.CommonPlayerLink;
import org.geysermc.floodgate.link.LinkRequestImpl; import org.geysermc.floodgate.link.LinkRequestImpl;
import org.geysermc.floodgate.util.LinkedPlayer; import org.geysermc.floodgate.util.LinkedPlayer;
import org.mariadb.jdbc.MariaDbPoolDataSource;
public class MysqlDatabase extends CommonPlayerLink { public class MysqlDatabase extends CommonPlayerLink {
private MariaDbPoolDataSource pool; private HikariDataSource dataSource;
@Override @Override
public void load() { public void load() {
getLogger().info("Connecting to a MySQL-like database..."); getLogger().info("Connecting to a MySQL-like database...");
try { try {
Class.forName("org.mariadb.jdbc.Driver"); MysqlConfig config = getConfig(MysqlConfig.class);
MysqlConfig databaseconfig = getConfig(MysqlConfig.class);
pool = new MariaDbPoolDataSource(); HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setDriverClassName("com.mysql.cj.jdbc.Driver");
hikariConfig.setJdbcUrl("jdbc:mysql://" + config.getHostname() + "/" + config.getDatabase());
hikariConfig.setUsername(config.getUsername());
hikariConfig.setPassword(config.getPassword());
hikariConfig.setPoolName("floodgate-linking-mysql");
hikariConfig.setMinimumIdle(5);
hikariConfig.setMaximumPoolSize(10);
String hostname = databaseconfig.getHostname(); dataSource = new HikariDataSource(hikariConfig);
if (hostname.contains(":")) {
String[] split = hostname.split(":");
pool.setServerName(split[0]); try (Connection connection = dataSource.getConnection()) {
try {
pool.setPortNumber(Integer.parseInt(split[1]));
} catch (NumberFormatException exception) {
getLogger().info("{} is not a valid port! Will use the default port", split[1]);
}
} else {
pool.setServerName(hostname);
}
pool.setUser(databaseconfig.getUsername());
pool.setPassword(databaseconfig.getPassword());
pool.setDatabaseName(databaseconfig.getDatabase());
pool.setMinPoolSize(2);
pool.setMaxPoolSize(10);
try (Connection connection = pool.getConnection()) {
try (Statement statement = connection.createStatement()) { try (Statement statement = connection.createStatement()) {
statement.executeUpdate( statement.executeUpdate(
"CREATE TABLE IF NOT EXISTS `LinkedPlayers` ( " + "CREATE TABLE IF NOT EXISTS `LinkedPlayers` ( " +
@@ -100,8 +89,6 @@ public class MysqlDatabase extends CommonPlayerLink {
} }
} }
getLogger().info("Connected to MySQL-like database."); getLogger().info("Connected to MySQL-like database.");
} catch (ClassNotFoundException exception) {
getLogger().error("The required class to load the MySQL database wasn't found");
} catch (SQLException exception) { } catch (SQLException exception) {
getLogger().error("Error while loading database", exception); getLogger().error("Error while loading database", exception);
} }
@@ -110,14 +97,14 @@ public class MysqlDatabase extends CommonPlayerLink {
@Override @Override
public void stop() { public void stop() {
super.stop(); super.stop();
pool.close(); dataSource.close();
} }
@Override @Override
@NonNull @NonNull
public CompletableFuture<LinkedPlayer> getLinkedPlayer(@NonNull UUID bedrockId) { public CompletableFuture<LinkedPlayer> getLinkedPlayer(@NonNull UUID bedrockId) {
return CompletableFuture.supplyAsync(() -> { return CompletableFuture.supplyAsync(() -> {
try (Connection connection = pool.getConnection()) { try (Connection connection = dataSource.getConnection()) {
try (PreparedStatement query = connection.prepareStatement( try (PreparedStatement query = connection.prepareStatement(
"SELECT * FROM `LinkedPlayers` WHERE `bedrockId` = ?" "SELECT * FROM `LinkedPlayers` WHERE `bedrockId` = ?"
)) { )) {
@@ -142,7 +129,7 @@ public class MysqlDatabase extends CommonPlayerLink {
@NonNull @NonNull
public CompletableFuture<Boolean> isLinkedPlayer(@NonNull UUID playerId) { public CompletableFuture<Boolean> isLinkedPlayer(@NonNull UUID playerId) {
return CompletableFuture.supplyAsync(() -> { return CompletableFuture.supplyAsync(() -> {
try (Connection connection = pool.getConnection()) { try (Connection connection = dataSource.getConnection()) {
try (PreparedStatement query = connection.prepareStatement( try (PreparedStatement query = connection.prepareStatement(
"SELECT * FROM `LinkedPlayers` WHERE `bedrockId` = ? OR `javaUniqueId` = ?" "SELECT * FROM `LinkedPlayers` WHERE `bedrockId` = ? OR `javaUniqueId` = ?"
)) { )) {
@@ -174,7 +161,7 @@ public class MysqlDatabase extends CommonPlayerLink {
} }
private void linkPlayer0(UUID bedrockId, UUID javaId, String javaUsername) { private void linkPlayer0(UUID bedrockId, UUID javaId, String javaUsername) {
try (Connection connection = pool.getConnection()) { try (Connection connection = dataSource.getConnection()) {
try (PreparedStatement query = connection.prepareStatement( try (PreparedStatement query = connection.prepareStatement(
"INSERT INTO `LinkedPlayers` VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE " + "INSERT INTO `LinkedPlayers` VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE " +
"`javaUniqueId`=VALUES(`javaUniqueId`), " + "`javaUniqueId`=VALUES(`javaUniqueId`), " +
@@ -195,7 +182,7 @@ public class MysqlDatabase extends CommonPlayerLink {
@NonNull @NonNull
public CompletableFuture<Void> unlinkPlayer(@NonNull UUID javaId) { public CompletableFuture<Void> unlinkPlayer(@NonNull UUID javaId) {
return CompletableFuture.runAsync(() -> { return CompletableFuture.runAsync(() -> {
try (Connection connection = pool.getConnection()) { try (Connection connection = dataSource.getConnection()) {
try (PreparedStatement query = connection.prepareStatement( try (PreparedStatement query = connection.prepareStatement(
"DELETE FROM `LinkedPlayers` WHERE `javaUniqueId` = ? OR `bedrockId` = ?" "DELETE FROM `LinkedPlayers` WHERE `javaUniqueId` = ? OR `bedrockId` = ?"
)) { )) {
@@ -216,7 +203,8 @@ public class MysqlDatabase extends CommonPlayerLink {
public CompletableFuture<String> createLinkRequest( public CompletableFuture<String> createLinkRequest(
@NonNull UUID javaId, @NonNull UUID javaId,
@NonNull String javaUsername, @NonNull String javaUsername,
@NonNull String bedrockUsername) { @NonNull String bedrockUsername
) {
return CompletableFuture.supplyAsync(() -> { return CompletableFuture.supplyAsync(() -> {
String linkCode = createCode(); String linkCode = createCode();
@@ -230,8 +218,9 @@ public class MysqlDatabase extends CommonPlayerLink {
String javaUsername, String javaUsername,
UUID javaId, UUID javaId,
String linkCode, String linkCode,
String bedrockUsername) { String bedrockUsername
try (Connection connection = pool.getConnection()) { ) {
try (Connection connection = dataSource.getConnection()) {
try (PreparedStatement query = connection.prepareStatement( try (PreparedStatement query = connection.prepareStatement(
"INSERT INTO `LinkedPlayersRequest` VALUES (?, ?, ?, ?, ?) " + "INSERT INTO `LinkedPlayersRequest` VALUES (?, ?, ?, ?, ?) " +
"ON DUPLICATE KEY UPDATE " + "ON DUPLICATE KEY UPDATE " +
@@ -254,7 +243,7 @@ public class MysqlDatabase extends CommonPlayerLink {
} }
private void removeLinkRequest(String javaUsername) { private void removeLinkRequest(String javaUsername) {
try (Connection connection = pool.getConnection()) { try (Connection connection = dataSource.getConnection()) {
try (PreparedStatement query = connection.prepareStatement( try (PreparedStatement query = connection.prepareStatement(
"DELETE FROM `LinkedPlayersRequest` WHERE `javaUsername` = ?" "DELETE FROM `LinkedPlayersRequest` WHERE `javaUsername` = ?"
)) { )) {
@@ -272,7 +261,8 @@ public class MysqlDatabase extends CommonPlayerLink {
@NonNull UUID bedrockId, @NonNull UUID bedrockId,
@NonNull String javaUsername, @NonNull String javaUsername,
@NonNull String bedrockUsername, @NonNull String bedrockUsername,
@NonNull String code) { @NonNull String code
) {
return CompletableFuture.supplyAsync(() -> { return CompletableFuture.supplyAsync(() -> {
LinkRequest request = getLinkRequest0(javaUsername); LinkRequest request = getLinkRequest0(javaUsername);
@@ -297,7 +287,7 @@ public class MysqlDatabase extends CommonPlayerLink {
} }
private LinkRequest getLinkRequest0(String javaUsername) { private LinkRequest getLinkRequest0(String javaUsername) {
try (Connection connection = pool.getConnection()) { try (Connection connection = dataSource.getConnection()) {
try (PreparedStatement query = connection.prepareStatement( try (PreparedStatement query = connection.prepareStatement(
"SELECT * FROM `LinkedPlayersRequest` WHERE `javaUsername` = ?" "SELECT * FROM `LinkedPlayersRequest` WHERE `javaUsername` = ?"
)) { )) {
@@ -322,7 +312,7 @@ public class MysqlDatabase extends CommonPlayerLink {
} }
public void cleanLinkRequests() { public void cleanLinkRequests() {
try (Connection connection = pool.getConnection()) { try (Connection connection = dataSource.getConnection()) {
try (PreparedStatement query = connection.prepareStatement( try (PreparedStatement query = connection.prepareStatement(
"DELETE FROM `LinkedPlayersRequest` WHERE `requestTime` < ?" "DELETE FROM `LinkedPlayersRequest` WHERE `requestTime` < ?"
)) { )) {
@@ -347,5 +337,4 @@ public class MysqlDatabase extends CommonPlayerLink {
ByteBuffer buf = ByteBuffer.wrap(uuidBytes); ByteBuffer buf = ByteBuffer.wrap(uuidBytes);
return new UUID(buf.getLong(), buf.getLong()); return new UUID(buf.getLong(), buf.getLong());
} }
} }

View File

@@ -14,7 +14,19 @@ dependencyResolutionManagement {
} }
// Paper, Velocity // Paper, Velocity
maven("https://papermc.io/repo/repository/maven-public") // maven("https://repo.papermc.io/repository/maven-releases") {
// mavenContent { releasesOnly() }
// }
// maven("https://repo.papermc.io/repository/maven-snapshots") {
// mavenContent { snapshotsOnly() }
// }
maven("https://repo.papermc.io/repository/maven-public") {
content {
includeGroupByRegex(
"(io\\.papermc\\..*|com\\.destroystokyo\\..*|com\\.velocitypowered)"
)
}
}
// Spigot // Spigot
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots") { maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots") {
mavenContent { snapshotsOnly() } mavenContent { snapshotsOnly() }

View File

@@ -1,4 +1,4 @@
var velocityVersion = "3.0.1" var velocityVersion = "3.1.1"
var log4jVersion = "2.11.2" var log4jVersion = "2.11.2"
var gsonVersion = "2.8.8" var gsonVersion = "2.8.8"
var guavaVersion = "25.1-jre" var guavaVersion = "25.1-jre"