mirror of
https://github.com/GeyserMC/Floodgate.git
synced 2025-12-19 14:59:20 +00:00
Switched to Hikari for MySQL
This commit is contained in:
@@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
4
database/mysql/.editorconfig
Normal file
4
database/mysql/.editorconfig
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
[*]
|
||||||
|
indent_size = 2
|
||||||
|
tab_width = 2
|
||||||
|
ij_continuation_indent_size = 4
|
||||||
@@ -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"
|
||||||
|
|||||||
@@ -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,309 +45,296 @@ 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 {
|
try (Statement statement = connection.createStatement()) {
|
||||||
pool.setPortNumber(Integer.parseInt(split[1]));
|
statement.executeUpdate(
|
||||||
} catch (NumberFormatException exception) {
|
"CREATE TABLE IF NOT EXISTS `LinkedPlayers` ( " +
|
||||||
getLogger().info("{} is not a valid port! Will use the default port", split[1]);
|
"`bedrockId` BINARY(16) NOT NULL , " +
|
||||||
}
|
"`javaUniqueId` BINARY(16) NOT NULL , " +
|
||||||
} else {
|
"`javaUsername` VARCHAR(16) NOT NULL , " +
|
||||||
pool.setServerName(hostname);
|
" PRIMARY KEY (`bedrockId`) , " +
|
||||||
}
|
" INDEX (`bedrockId`, `javaUniqueId`)" +
|
||||||
|
") ENGINE = InnoDB;"
|
||||||
pool.setUser(databaseconfig.getUsername());
|
);
|
||||||
pool.setPassword(databaseconfig.getPassword());
|
statement.executeUpdate(
|
||||||
pool.setDatabaseName(databaseconfig.getDatabase());
|
"CREATE TABLE IF NOT EXISTS `LinkedPlayersRequest` ( " +
|
||||||
pool.setMinPoolSize(2);
|
"`javaUsername` VARCHAR(16) NOT NULL , `javaUniqueId` BINARY(16) NOT NULL , " +
|
||||||
pool.setMaxPoolSize(10);
|
"`linkCode` VARCHAR(16) NOT NULL , " +
|
||||||
|
"`bedrockUsername` VARCHAR(16) NOT NULL ," +
|
||||||
try (Connection connection = pool.getConnection()) {
|
"`requestTime` BIGINT NOT NULL , " +
|
||||||
try (Statement statement = connection.createStatement()) {
|
" PRIMARY KEY (`javaUsername`), INDEX(`requestTime`)" +
|
||||||
statement.executeUpdate(
|
" ) ENGINE = InnoDB;"
|
||||||
"CREATE TABLE IF NOT EXISTS `LinkedPlayers` ( " +
|
);
|
||||||
"`bedrockId` BINARY(16) NOT NULL , " +
|
|
||||||
"`javaUniqueId` BINARY(16) NOT NULL , " +
|
|
||||||
"`javaUsername` VARCHAR(16) NOT NULL , " +
|
|
||||||
" PRIMARY KEY (`bedrockId`) , " +
|
|
||||||
" INDEX (`bedrockId`, `javaUniqueId`)" +
|
|
||||||
") ENGINE = InnoDB;"
|
|
||||||
);
|
|
||||||
statement.executeUpdate(
|
|
||||||
"CREATE TABLE IF NOT EXISTS `LinkedPlayersRequest` ( " +
|
|
||||||
"`javaUsername` VARCHAR(16) NOT NULL , `javaUniqueId` BINARY(16) NOT NULL , " +
|
|
||||||
"`linkCode` VARCHAR(16) NOT NULL , " +
|
|
||||||
"`bedrockUsername` VARCHAR(16) NOT NULL ," +
|
|
||||||
"`requestTime` BIGINT NOT NULL , " +
|
|
||||||
" PRIMARY KEY (`javaUsername`), INDEX(`requestTime`)" +
|
|
||||||
" ) ENGINE = InnoDB;"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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) {
|
|
||||||
getLogger().error("Error while loading database", exception);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
getLogger().info("Connected to MySQL-like database.");
|
||||||
|
} catch (SQLException exception) {
|
||||||
|
getLogger().error("Error while loading database", exception);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@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` = ?"
|
||||||
)) {
|
)) {
|
||||||
query.setBytes(1, uuidToBytes(bedrockId));
|
query.setBytes(1, uuidToBytes(bedrockId));
|
||||||
try (ResultSet result = query.executeQuery()) {
|
try (ResultSet result = query.executeQuery()) {
|
||||||
if (!result.next()) {
|
if (!result.next()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
|
||||||
String javaUsername = result.getString("javaUsername");
|
|
||||||
UUID javaUniqueId = bytesToUUID(result.getBytes("javaUniqueId"));
|
|
||||||
return LinkedPlayer.of(javaUsername, javaUniqueId, bedrockId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (SQLException exception) {
|
|
||||||
getLogger().error("Error while getting LinkedPlayer", exception);
|
|
||||||
throw new CompletionException("Error while getting LinkedPlayer", exception);
|
|
||||||
}
|
}
|
||||||
}, getExecutorService());
|
String javaUsername = result.getString("javaUsername");
|
||||||
}
|
UUID javaUniqueId = bytesToUUID(result.getBytes("javaUniqueId"));
|
||||||
|
return LinkedPlayer.of(javaUsername, javaUniqueId, bedrockId);
|
||||||
@Override
|
}
|
||||||
@NonNull
|
|
||||||
public CompletableFuture<Boolean> isLinkedPlayer(@NonNull UUID playerId) {
|
|
||||||
return CompletableFuture.supplyAsync(() -> {
|
|
||||||
try (Connection connection = pool.getConnection()) {
|
|
||||||
try (PreparedStatement query = connection.prepareStatement(
|
|
||||||
"SELECT * FROM `LinkedPlayers` WHERE `bedrockId` = ? OR `javaUniqueId` = ?"
|
|
||||||
)) {
|
|
||||||
byte[] uuidBytes = uuidToBytes(playerId);
|
|
||||||
query.setBytes(1, uuidBytes);
|
|
||||||
query.setBytes(2, uuidBytes);
|
|
||||||
try (ResultSet result = query.executeQuery()) {
|
|
||||||
return result.next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (SQLException exception) {
|
|
||||||
getLogger().error("Error while checking if player is a LinkedPlayer", exception);
|
|
||||||
throw new CompletionException(
|
|
||||||
"Error while checking if player is a LinkedPlayer", exception
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}, getExecutorService());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@NonNull
|
|
||||||
public CompletableFuture<Void> linkPlayer(
|
|
||||||
@NonNull UUID bedrockId,
|
|
||||||
@NonNull UUID javaId,
|
|
||||||
@NonNull String javaUsername) {
|
|
||||||
return CompletableFuture.runAsync(
|
|
||||||
() -> linkPlayer0(bedrockId, javaId, javaUsername),
|
|
||||||
getExecutorService());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void linkPlayer0(UUID bedrockId, UUID javaId, String javaUsername) {
|
|
||||||
try (Connection connection = pool.getConnection()) {
|
|
||||||
try (PreparedStatement query = connection.prepareStatement(
|
|
||||||
"INSERT INTO `LinkedPlayers` VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE " +
|
|
||||||
"`javaUniqueId`=VALUES(`javaUniqueId`), " +
|
|
||||||
"`javaUsername`=VALUES(`javaUsername`);"
|
|
||||||
)) {
|
|
||||||
query.setBytes(1, uuidToBytes(bedrockId));
|
|
||||||
query.setBytes(2, uuidToBytes(javaId));
|
|
||||||
query.setString(3, javaUsername);
|
|
||||||
query.executeUpdate();
|
|
||||||
}
|
|
||||||
} catch (SQLException exception) {
|
|
||||||
getLogger().error("Error while linking player", exception);
|
|
||||||
throw new CompletionException("Error while linking player", exception);
|
|
||||||
}
|
}
|
||||||
}
|
} catch (SQLException exception) {
|
||||||
|
getLogger().error("Error while getting LinkedPlayer", exception);
|
||||||
|
throw new CompletionException("Error while getting LinkedPlayer", exception);
|
||||||
|
}
|
||||||
|
}, getExecutorService());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@NonNull
|
@NonNull
|
||||||
public CompletableFuture<Void> unlinkPlayer(@NonNull UUID javaId) {
|
public CompletableFuture<Boolean> isLinkedPlayer(@NonNull UUID playerId) {
|
||||||
return CompletableFuture.runAsync(() -> {
|
return CompletableFuture.supplyAsync(() -> {
|
||||||
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` = ?"
|
"SELECT * FROM `LinkedPlayers` WHERE `bedrockId` = ? OR `javaUniqueId` = ?"
|
||||||
)) {
|
)) {
|
||||||
byte[] uuidBytes = uuidToBytes(javaId);
|
byte[] uuidBytes = uuidToBytes(playerId);
|
||||||
query.setBytes(1, uuidBytes);
|
query.setBytes(1, uuidBytes);
|
||||||
query.setBytes(2, uuidBytes);
|
query.setBytes(2, uuidBytes);
|
||||||
query.executeUpdate();
|
try (ResultSet result = query.executeQuery()) {
|
||||||
}
|
return result.next();
|
||||||
} catch (SQLException exception) {
|
}
|
||||||
getLogger().error("Error while unlinking player", exception);
|
|
||||||
throw new CompletionException("Error while unlinking player", exception);
|
|
||||||
}
|
|
||||||
}, getExecutorService());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@NonNull
|
|
||||||
public CompletableFuture<String> createLinkRequest(
|
|
||||||
@NonNull UUID javaId,
|
|
||||||
@NonNull String javaUsername,
|
|
||||||
@NonNull String bedrockUsername) {
|
|
||||||
return CompletableFuture.supplyAsync(() -> {
|
|
||||||
String linkCode = createCode();
|
|
||||||
|
|
||||||
createLinkRequest0(javaUsername, javaId, linkCode, bedrockUsername);
|
|
||||||
|
|
||||||
return linkCode;
|
|
||||||
}, getExecutorService());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createLinkRequest0(
|
|
||||||
String javaUsername,
|
|
||||||
UUID javaId,
|
|
||||||
String linkCode,
|
|
||||||
String bedrockUsername) {
|
|
||||||
try (Connection connection = pool.getConnection()) {
|
|
||||||
try (PreparedStatement query = connection.prepareStatement(
|
|
||||||
"INSERT INTO `LinkedPlayersRequest` VALUES (?, ?, ?, ?, ?) " +
|
|
||||||
"ON DUPLICATE KEY UPDATE " +
|
|
||||||
"`javaUniqueId`=VALUES(`javaUniqueId`), " +
|
|
||||||
"`linkCode`=VALUES(`linkCode`), " +
|
|
||||||
"`bedrockUsername`=VALUES(`bedrockUsername`), " +
|
|
||||||
"`requestTime`=VALUES(`requestTime`);"
|
|
||||||
)) {
|
|
||||||
query.setString(1, javaUsername);
|
|
||||||
query.setBytes(2, uuidToBytes(javaId));
|
|
||||||
query.setString(3, linkCode);
|
|
||||||
query.setString(4, bedrockUsername);
|
|
||||||
query.setLong(5, Instant.now().getEpochSecond());
|
|
||||||
query.executeUpdate();
|
|
||||||
}
|
|
||||||
} catch (SQLException exception) {
|
|
||||||
getLogger().error("Error while linking player", exception);
|
|
||||||
throw new CompletionException("Error while linking player", exception);
|
|
||||||
}
|
}
|
||||||
}
|
} catch (SQLException exception) {
|
||||||
|
getLogger().error("Error while checking if player is a LinkedPlayer", exception);
|
||||||
|
throw new CompletionException(
|
||||||
|
"Error while checking if player is a LinkedPlayer", exception
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, getExecutorService());
|
||||||
|
}
|
||||||
|
|
||||||
private void removeLinkRequest(String javaUsername) {
|
@Override
|
||||||
try (Connection connection = pool.getConnection()) {
|
@NonNull
|
||||||
try (PreparedStatement query = connection.prepareStatement(
|
public CompletableFuture<Void> linkPlayer(
|
||||||
"DELETE FROM `LinkedPlayersRequest` WHERE `javaUsername` = ?"
|
@NonNull UUID bedrockId,
|
||||||
)) {
|
@NonNull UUID javaId,
|
||||||
query.setString(1, javaUsername);
|
@NonNull String javaUsername) {
|
||||||
query.executeUpdate();
|
return CompletableFuture.runAsync(
|
||||||
}
|
() -> linkPlayer0(bedrockId, javaId, javaUsername),
|
||||||
} catch (SQLException exception) {
|
getExecutorService());
|
||||||
getLogger().error("Error while cleaning up LinkRequest", exception);
|
}
|
||||||
|
|
||||||
|
private void linkPlayer0(UUID bedrockId, UUID javaId, String javaUsername) {
|
||||||
|
try (Connection connection = dataSource.getConnection()) {
|
||||||
|
try (PreparedStatement query = connection.prepareStatement(
|
||||||
|
"INSERT INTO `LinkedPlayers` VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE " +
|
||||||
|
"`javaUniqueId`=VALUES(`javaUniqueId`), " +
|
||||||
|
"`javaUsername`=VALUES(`javaUsername`);"
|
||||||
|
)) {
|
||||||
|
query.setBytes(1, uuidToBytes(bedrockId));
|
||||||
|
query.setBytes(2, uuidToBytes(javaId));
|
||||||
|
query.setString(3, javaUsername);
|
||||||
|
query.executeUpdate();
|
||||||
|
}
|
||||||
|
} catch (SQLException exception) {
|
||||||
|
getLogger().error("Error while linking player", exception);
|
||||||
|
throw new CompletionException("Error while linking player", exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@NonNull
|
||||||
|
public CompletableFuture<Void> unlinkPlayer(@NonNull UUID javaId) {
|
||||||
|
return CompletableFuture.runAsync(() -> {
|
||||||
|
try (Connection connection = dataSource.getConnection()) {
|
||||||
|
try (PreparedStatement query = connection.prepareStatement(
|
||||||
|
"DELETE FROM `LinkedPlayers` WHERE `javaUniqueId` = ? OR `bedrockId` = ?"
|
||||||
|
)) {
|
||||||
|
byte[] uuidBytes = uuidToBytes(javaId);
|
||||||
|
query.setBytes(1, uuidBytes);
|
||||||
|
query.setBytes(2, uuidBytes);
|
||||||
|
query.executeUpdate();
|
||||||
}
|
}
|
||||||
|
} catch (SQLException exception) {
|
||||||
|
getLogger().error("Error while unlinking player", exception);
|
||||||
|
throw new CompletionException("Error while unlinking player", exception);
|
||||||
|
}
|
||||||
|
}, getExecutorService());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@NonNull
|
||||||
|
public CompletableFuture<String> createLinkRequest(
|
||||||
|
@NonNull UUID javaId,
|
||||||
|
@NonNull String javaUsername,
|
||||||
|
@NonNull String bedrockUsername
|
||||||
|
) {
|
||||||
|
return CompletableFuture.supplyAsync(() -> {
|
||||||
|
String linkCode = createCode();
|
||||||
|
|
||||||
|
createLinkRequest0(javaUsername, javaId, linkCode, bedrockUsername);
|
||||||
|
|
||||||
|
return linkCode;
|
||||||
|
}, getExecutorService());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createLinkRequest0(
|
||||||
|
String javaUsername,
|
||||||
|
UUID javaId,
|
||||||
|
String linkCode,
|
||||||
|
String bedrockUsername
|
||||||
|
) {
|
||||||
|
try (Connection connection = dataSource.getConnection()) {
|
||||||
|
try (PreparedStatement query = connection.prepareStatement(
|
||||||
|
"INSERT INTO `LinkedPlayersRequest` VALUES (?, ?, ?, ?, ?) " +
|
||||||
|
"ON DUPLICATE KEY UPDATE " +
|
||||||
|
"`javaUniqueId`=VALUES(`javaUniqueId`), " +
|
||||||
|
"`linkCode`=VALUES(`linkCode`), " +
|
||||||
|
"`bedrockUsername`=VALUES(`bedrockUsername`), " +
|
||||||
|
"`requestTime`=VALUES(`requestTime`);"
|
||||||
|
)) {
|
||||||
|
query.setString(1, javaUsername);
|
||||||
|
query.setBytes(2, uuidToBytes(javaId));
|
||||||
|
query.setString(3, linkCode);
|
||||||
|
query.setString(4, bedrockUsername);
|
||||||
|
query.setLong(5, Instant.now().getEpochSecond());
|
||||||
|
query.executeUpdate();
|
||||||
|
}
|
||||||
|
} catch (SQLException exception) {
|
||||||
|
getLogger().error("Error while linking player", exception);
|
||||||
|
throw new CompletionException("Error while linking player", exception);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
private void removeLinkRequest(String javaUsername) {
|
||||||
@NonNull
|
try (Connection connection = dataSource.getConnection()) {
|
||||||
public CompletableFuture<LinkRequestResult> verifyLinkRequest(
|
try (PreparedStatement query = connection.prepareStatement(
|
||||||
@NonNull UUID bedrockId,
|
"DELETE FROM `LinkedPlayersRequest` WHERE `javaUsername` = ?"
|
||||||
@NonNull String javaUsername,
|
)) {
|
||||||
@NonNull String bedrockUsername,
|
query.setString(1, javaUsername);
|
||||||
@NonNull String code) {
|
query.executeUpdate();
|
||||||
return CompletableFuture.supplyAsync(() -> {
|
}
|
||||||
LinkRequest request = getLinkRequest0(javaUsername);
|
} catch (SQLException exception) {
|
||||||
|
getLogger().error("Error while cleaning up LinkRequest", exception);
|
||||||
if (request == null || !isRequestedPlayer(request, bedrockId)) {
|
|
||||||
return LinkRequestResult.NO_LINK_REQUESTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!request.getLinkCode().equals(code)) {
|
|
||||||
return LinkRequestResult.INVALID_CODE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// link request can be removed. Doesn't matter if the request is expired or not
|
|
||||||
removeLinkRequest(javaUsername);
|
|
||||||
|
|
||||||
if (request.isExpired(getVerifyLinkTimeout())) {
|
|
||||||
return LinkRequestResult.REQUEST_EXPIRED;
|
|
||||||
}
|
|
||||||
|
|
||||||
linkPlayer0(bedrockId, request.getJavaUniqueId(), javaUsername);
|
|
||||||
return LinkRequestResult.LINK_COMPLETED;
|
|
||||||
}, getExecutorService());
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private LinkRequest getLinkRequest0(String javaUsername) {
|
@Override
|
||||||
try (Connection connection = pool.getConnection()) {
|
@NonNull
|
||||||
try (PreparedStatement query = connection.prepareStatement(
|
public CompletableFuture<LinkRequestResult> verifyLinkRequest(
|
||||||
"SELECT * FROM `LinkedPlayersRequest` WHERE `javaUsername` = ?"
|
@NonNull UUID bedrockId,
|
||||||
)) {
|
@NonNull String javaUsername,
|
||||||
query.setString(1, javaUsername);
|
@NonNull String bedrockUsername,
|
||||||
|
@NonNull String code
|
||||||
|
) {
|
||||||
|
return CompletableFuture.supplyAsync(() -> {
|
||||||
|
LinkRequest request = getLinkRequest0(javaUsername);
|
||||||
|
|
||||||
try (ResultSet result = query.executeQuery()) {
|
if (request == null || !isRequestedPlayer(request, bedrockId)) {
|
||||||
if (result.next()) {
|
return LinkRequestResult.NO_LINK_REQUESTED;
|
||||||
UUID javaId = bytesToUUID(result.getBytes(2));
|
}
|
||||||
String linkCode = result.getString(3);
|
|
||||||
String bedrockUsername = result.getString(4);
|
if (!request.getLinkCode().equals(code)) {
|
||||||
long requestTime = result.getLong(5);
|
return LinkRequestResult.INVALID_CODE;
|
||||||
return new LinkRequestImpl(javaUsername, javaId, linkCode, bedrockUsername,
|
}
|
||||||
requestTime);
|
|
||||||
}
|
// link request can be removed. Doesn't matter if the request is expired or not
|
||||||
}
|
removeLinkRequest(javaUsername);
|
||||||
}
|
|
||||||
} catch (SQLException exception) {
|
if (request.isExpired(getVerifyLinkTimeout())) {
|
||||||
getLogger().error("Error while getLinkRequest", exception);
|
return LinkRequestResult.REQUEST_EXPIRED;
|
||||||
throw new CompletionException("Error while getLinkRequest", exception);
|
}
|
||||||
|
|
||||||
|
linkPlayer0(bedrockId, request.getJavaUniqueId(), javaUsername);
|
||||||
|
return LinkRequestResult.LINK_COMPLETED;
|
||||||
|
}, getExecutorService());
|
||||||
|
}
|
||||||
|
|
||||||
|
private LinkRequest getLinkRequest0(String javaUsername) {
|
||||||
|
try (Connection connection = dataSource.getConnection()) {
|
||||||
|
try (PreparedStatement query = connection.prepareStatement(
|
||||||
|
"SELECT * FROM `LinkedPlayersRequest` WHERE `javaUsername` = ?"
|
||||||
|
)) {
|
||||||
|
query.setString(1, javaUsername);
|
||||||
|
|
||||||
|
try (ResultSet result = query.executeQuery()) {
|
||||||
|
if (result.next()) {
|
||||||
|
UUID javaId = bytesToUUID(result.getBytes(2));
|
||||||
|
String linkCode = result.getString(3);
|
||||||
|
String bedrockUsername = result.getString(4);
|
||||||
|
long requestTime = result.getLong(5);
|
||||||
|
return new LinkRequestImpl(javaUsername, javaId, linkCode, bedrockUsername,
|
||||||
|
requestTime);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null;
|
}
|
||||||
|
} catch (SQLException exception) {
|
||||||
|
getLogger().error("Error while getLinkRequest", exception);
|
||||||
|
throw new CompletionException("Error while getLinkRequest", exception);
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
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` < ?"
|
||||||
)) {
|
)) {
|
||||||
query.setLong(1, Instant.now().getEpochSecond() - getVerifyLinkTimeout());
|
query.setLong(1, Instant.now().getEpochSecond() - getVerifyLinkTimeout());
|
||||||
query.executeUpdate();
|
query.executeUpdate();
|
||||||
}
|
}
|
||||||
} catch (SQLException exception) {
|
} catch (SQLException exception) {
|
||||||
getLogger().error("Error while cleaning up link requests", exception);
|
getLogger().error("Error while cleaning up link requests", exception);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private byte[] uuidToBytes(UUID uuid) {
|
private byte[] uuidToBytes(UUID uuid) {
|
||||||
byte[] uuidBytes = new byte[16];
|
byte[] uuidBytes = new byte[16];
|
||||||
ByteBuffer.wrap(uuidBytes)
|
ByteBuffer.wrap(uuidBytes)
|
||||||
.order(ByteOrder.BIG_ENDIAN)
|
.order(ByteOrder.BIG_ENDIAN)
|
||||||
.putLong(uuid.getMostSignificantBits())
|
.putLong(uuid.getMostSignificantBits())
|
||||||
.putLong(uuid.getLeastSignificantBits());
|
.putLong(uuid.getLeastSignificantBits());
|
||||||
return uuidBytes;
|
return uuidBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
private UUID bytesToUUID(byte[] uuidBytes) {
|
|
||||||
ByteBuffer buf = ByteBuffer.wrap(uuidBytes);
|
|
||||||
return new UUID(buf.getLong(), buf.getLong());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
private UUID bytesToUUID(byte[] uuidBytes) {
|
||||||
|
ByteBuffer buf = ByteBuffer.wrap(uuidBytes);
|
||||||
|
return new UUID(buf.getLong(), buf.getLong());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ import lombok.Getter;
|
|||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
public class MysqlConfig implements DatabaseConfig {
|
public class MysqlConfig implements DatabaseConfig {
|
||||||
private String hostname = "localhost";
|
private String hostname = "localhost";
|
||||||
private String database = "floodgate";
|
private String database = "floodgate";
|
||||||
private String username = "floodgate";
|
private String username = "floodgate";
|
||||||
private String password;
|
private String password;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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() }
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user