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.Injector;
|
||||
import com.google.inject.Singleton;
|
||||
import com.google.inject.name.Named;
|
||||
import com.google.inject.name.Names;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -42,18 +43,23 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.inject.Named;
|
||||
import java.util.stream.Stream;
|
||||
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.logger.FloodgateLogger;
|
||||
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.InjectorHolder;
|
||||
import org.geysermc.floodgate.util.Utils;
|
||||
|
||||
@Listener
|
||||
@Singleton
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class PlayerLinkLoader {
|
||||
public final class PlayerLinkHolder {
|
||||
@Inject private Injector injector;
|
||||
@Inject private FloodgateConfig config;
|
||||
@Inject private FloodgateLogger logger;
|
||||
@@ -62,30 +68,38 @@ public final class PlayerLinkLoader {
|
||||
@Named("dataDirectory")
|
||||
private Path dataDirectory;
|
||||
|
||||
@Nonnull
|
||||
private URLClassLoader classLoader;
|
||||
private PlayerLink instance;
|
||||
|
||||
@NonNull
|
||||
public PlayerLink load() {
|
||||
if (instance != null) {
|
||||
return instance;
|
||||
}
|
||||
|
||||
if (config == null) {
|
||||
throw new IllegalStateException("Config cannot be null!");
|
||||
}
|
||||
|
||||
FloodgateConfig.PlayerLinkConfig lConfig = config.getPlayerLink();
|
||||
if (!lConfig.isEnabled()) {
|
||||
PlayerLinkConfig linkConfig = config.getPlayerLink();
|
||||
if (!linkConfig.isEnabled()) {
|
||||
return new DisabledPlayerLink();
|
||||
}
|
||||
|
||||
List<Path> files;
|
||||
try {
|
||||
files = Files.list(dataDirectory)
|
||||
.filter(path -> Files.isRegularFile(path) && path.toString().endsWith("jar"))
|
||||
try (Stream<Path> list = Files.list(dataDirectory)) {
|
||||
files = list
|
||||
.filter(path -> Files.isRegularFile(path) && path.toString().endsWith(".jar"))
|
||||
.collect(Collectors.toList());
|
||||
} catch (IOException exception) {
|
||||
logger.error("Failed to list possible database implementations", exception);
|
||||
return new DisabledPlayerLink();
|
||||
}
|
||||
|
||||
// we can skip the rest if global linking is enabled and no database implementations has been
|
||||
// found, or when global linking is enabled and own player linking is disabled.
|
||||
if (lConfig.isEnableGlobalLinking() && (files.isEmpty() || !lConfig.isEnableOwnLinking())) {
|
||||
// we can skip the rest if global linking is enabled and no database implementations has
|
||||
// been found, or when global linking is enabled and own player linking is disabled.
|
||||
if (linkConfig.isEnableGlobalLinking() &&
|
||||
(files.isEmpty() || !linkConfig.isEnableOwnLinking())) {
|
||||
return injector.getInstance(GlobalPlayerLinking.class);
|
||||
}
|
||||
|
||||
@@ -100,7 +114,7 @@ public final class PlayerLinkLoader {
|
||||
// We only want to load one database implementation
|
||||
if (files.size() > 1) {
|
||||
boolean found = false;
|
||||
databaseName = lConfig.getType();
|
||||
databaseName = linkConfig.getType();
|
||||
|
||||
String expectedName = "floodgate-" + databaseName + "-database.jar";
|
||||
for (Path path : files) {
|
||||
@@ -111,14 +125,18 @@ public final class PlayerLinkLoader {
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
} else {
|
||||
String name = implementationPath.getFileName().toString();
|
||||
if (!Utils.isValidDatabaseName(name)) {
|
||||
logger.error("Found database {} but the name doesn't match {}",
|
||||
name, Constants.DATABASE_NAME_FORMAT);
|
||||
logger.error(
|
||||
"Found database {} but the name doesn't match {}",
|
||||
name, Constants.DATABASE_NAME_FORMAT
|
||||
);
|
||||
return new DisabledPlayerLink();
|
||||
}
|
||||
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
|
||||
// to be able to load classes on the fly, but that doesn't matter anyway since Floodgate
|
||||
// doesn't support reloading
|
||||
URLClassLoader classLoader = new URLClassLoader(
|
||||
classLoader = new URLClassLoader(
|
||||
new URL[]{pluginUrl},
|
||||
PlayerLinkLoader.class.getClassLoader()
|
||||
PlayerLinkHolder.class.getClassLoader()
|
||||
);
|
||||
|
||||
String mainClassName;
|
||||
JsonObject linkConfig;
|
||||
|
||||
try (InputStream linkConfigStream =
|
||||
classLoader.getResourceAsStream("init.json")) {
|
||||
JsonObject dbInitConfig;
|
||||
|
||||
try (InputStream linkConfigStream = classLoader.getResourceAsStream("init.json")) {
|
||||
requireNonNull(linkConfigStream, "Implementation should have an init file");
|
||||
|
||||
linkConfig = new Gson().fromJson(
|
||||
dbInitConfig = new Gson().fromJson(
|
||||
new InputStreamReader(linkConfigStream), JsonObject.class
|
||||
);
|
||||
|
||||
mainClassName = linkConfig.get("mainClass").getAsString();
|
||||
mainClassName = dbInitConfig.get("mainClass").getAsString();
|
||||
}
|
||||
|
||||
Class<? extends PlayerLink> mainClass =
|
||||
@@ -167,16 +183,16 @@ public final class PlayerLinkLoader {
|
||||
Names.named("databaseClassLoader")).toInstance(classLoader);
|
||||
binder.bind(JsonObject.class)
|
||||
.annotatedWith(Names.named("databaseInitData"))
|
||||
.toInstance(linkConfig);
|
||||
.toInstance(dbInitConfig);
|
||||
binder.bind(InjectorHolder.class)
|
||||
.toInstance(injectorHolder);
|
||||
});
|
||||
injectorHolder.set(linkInjector);
|
||||
|
||||
PlayerLink instance = linkInjector.getInstance(mainClass);
|
||||
instance = linkInjector.getInstance(mainClass);
|
||||
|
||||
// we use our own internal PlayerLinking when global linking is enabled
|
||||
if (lConfig.isEnableGlobalLinking()) {
|
||||
if (linkConfig.isEnableGlobalLinking()) {
|
||||
GlobalPlayerLinking linking = linkInjector.getInstance(GlobalPlayerLinking.class);
|
||||
linking.setDatabaseImpl(instance);
|
||||
linking.load();
|
||||
@@ -186,8 +202,10 @@ public final class PlayerLinkLoader {
|
||||
return instance;
|
||||
}
|
||||
} catch (ClassCastException exception) {
|
||||
logger.error("The database implementation ({}) doesn't extend the PlayerLink class!",
|
||||
implementationPath.getFileName().toString(), exception);
|
||||
logger.error(
|
||||
"The database implementation ({}) doesn't extend the PlayerLink class!",
|
||||
implementationPath.getFileName().toString(), exception
|
||||
);
|
||||
return new DisabledPlayerLink();
|
||||
} catch (Exception exception) {
|
||||
if (init) {
|
||||
@@ -198,4 +216,10 @@ public final class PlayerLinkLoader {
|
||||
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.util.ListenerAnnotationMatcher;
|
||||
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.player.FloodgateHandshakeHandler;
|
||||
import org.geysermc.floodgate.pluginmessage.PluginMessageManager;
|
||||
@@ -102,7 +102,7 @@ public class CommonModule extends AbstractModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public PlayerLink playerLink(PlayerLinkLoader linkLoader) {
|
||||
public PlayerLink playerLink(PlayerLinkHolder linkLoader) {
|
||||
return linkLoader.load();
|
||||
}
|
||||
|
||||
|
||||
@@ -26,13 +26,13 @@
|
||||
package org.geysermc.floodgate.util;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.name.Named;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.inject.Named;
|
||||
import org.bstats.MetricsBase;
|
||||
import org.bstats.charts.DrilldownPie;
|
||||
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 {
|
||||
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"
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
|
||||
package org.geysermc.floodgate.database;
|
||||
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
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.LinkRequestImpl;
|
||||
import org.geysermc.floodgate.util.LinkedPlayer;
|
||||
import org.mariadb.jdbc.MariaDbPoolDataSource;
|
||||
|
||||
public class MysqlDatabase extends CommonPlayerLink {
|
||||
private MariaDbPoolDataSource pool;
|
||||
private HikariDataSource dataSource;
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
getLogger().info("Connecting to a MySQL-like database...");
|
||||
try {
|
||||
Class.forName("org.mariadb.jdbc.Driver");
|
||||
MysqlConfig databaseconfig = getConfig(MysqlConfig.class);
|
||||
@Override
|
||||
public void load() {
|
||||
getLogger().info("Connecting to a MySQL-like database...");
|
||||
try {
|
||||
MysqlConfig config = 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();
|
||||
if (hostname.contains(":")) {
|
||||
String[] split = hostname.split(":");
|
||||
dataSource = new HikariDataSource(hikariConfig);
|
||||
|
||||
pool.setServerName(split[0]);
|
||||
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()) {
|
||||
statement.executeUpdate(
|
||||
"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);
|
||||
try (Connection connection = dataSource.getConnection()) {
|
||||
try (Statement statement = connection.createStatement()) {
|
||||
statement.executeUpdate(
|
||||
"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 (SQLException exception) {
|
||||
getLogger().error("Error while loading database", exception);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
super.stop();
|
||||
pool.close();
|
||||
}
|
||||
@Override
|
||||
public void stop() {
|
||||
super.stop();
|
||||
dataSource.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public CompletableFuture<LinkedPlayer> getLinkedPlayer(@NonNull UUID bedrockId) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try (Connection connection = pool.getConnection()) {
|
||||
try (PreparedStatement query = connection.prepareStatement(
|
||||
"SELECT * FROM `LinkedPlayers` WHERE `bedrockId` = ?"
|
||||
)) {
|
||||
query.setBytes(1, uuidToBytes(bedrockId));
|
||||
try (ResultSet result = query.executeQuery()) {
|
||||
if (!result.next()) {
|
||||
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);
|
||||
@Override
|
||||
@NonNull
|
||||
public CompletableFuture<LinkedPlayer> getLinkedPlayer(@NonNull UUID bedrockId) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try (Connection connection = dataSource.getConnection()) {
|
||||
try (PreparedStatement query = connection.prepareStatement(
|
||||
"SELECT * FROM `LinkedPlayers` WHERE `bedrockId` = ?"
|
||||
)) {
|
||||
query.setBytes(1, uuidToBytes(bedrockId));
|
||||
try (ResultSet result = query.executeQuery()) {
|
||||
if (!result.next()) {
|
||||
return null;
|
||||
}
|
||||
}, getExecutorService());
|
||||
}
|
||||
|
||||
@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);
|
||||
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());
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public CompletableFuture<Void> unlinkPlayer(@NonNull UUID javaId) {
|
||||
return CompletableFuture.runAsync(() -> {
|
||||
try (Connection connection = pool.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 = 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);
|
||||
@Override
|
||||
@NonNull
|
||||
public CompletableFuture<Boolean> isLinkedPlayer(@NonNull UUID playerId) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try (Connection connection = dataSource.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());
|
||||
}
|
||||
|
||||
private void removeLinkRequest(String javaUsername) {
|
||||
try (Connection connection = pool.getConnection()) {
|
||||
try (PreparedStatement query = connection.prepareStatement(
|
||||
"DELETE FROM `LinkedPlayersRequest` WHERE `javaUsername` = ?"
|
||||
)) {
|
||||
query.setString(1, javaUsername);
|
||||
query.executeUpdate();
|
||||
}
|
||||
} catch (SQLException exception) {
|
||||
getLogger().error("Error while cleaning up LinkRequest", exception);
|
||||
@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 = 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
|
||||
@NonNull
|
||||
public CompletableFuture<LinkRequestResult> verifyLinkRequest(
|
||||
@NonNull UUID bedrockId,
|
||||
@NonNull String javaUsername,
|
||||
@NonNull String bedrockUsername,
|
||||
@NonNull String code) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
LinkRequest request = getLinkRequest0(javaUsername);
|
||||
|
||||
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 void removeLinkRequest(String javaUsername) {
|
||||
try (Connection connection = dataSource.getConnection()) {
|
||||
try (PreparedStatement query = connection.prepareStatement(
|
||||
"DELETE FROM `LinkedPlayersRequest` WHERE `javaUsername` = ?"
|
||||
)) {
|
||||
query.setString(1, javaUsername);
|
||||
query.executeUpdate();
|
||||
}
|
||||
} catch (SQLException exception) {
|
||||
getLogger().error("Error while cleaning up LinkRequest", exception);
|
||||
}
|
||||
}
|
||||
|
||||
private LinkRequest getLinkRequest0(String javaUsername) {
|
||||
try (Connection connection = pool.getConnection()) {
|
||||
try (PreparedStatement query = connection.prepareStatement(
|
||||
"SELECT * FROM `LinkedPlayersRequest` WHERE `javaUsername` = ?"
|
||||
)) {
|
||||
query.setString(1, javaUsername);
|
||||
@Override
|
||||
@NonNull
|
||||
public CompletableFuture<LinkRequestResult> verifyLinkRequest(
|
||||
@NonNull UUID bedrockId,
|
||||
@NonNull String javaUsername,
|
||||
@NonNull String bedrockUsername,
|
||||
@NonNull String code
|
||||
) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
LinkRequest request = getLinkRequest0(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (SQLException exception) {
|
||||
getLogger().error("Error while getLinkRequest", exception);
|
||||
throw new CompletionException("Error while getLinkRequest", 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) {
|
||||
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() {
|
||||
try (Connection connection = pool.getConnection()) {
|
||||
try (PreparedStatement query = connection.prepareStatement(
|
||||
"DELETE FROM `LinkedPlayersRequest` WHERE `requestTime` < ?"
|
||||
)) {
|
||||
query.setLong(1, Instant.now().getEpochSecond() - getVerifyLinkTimeout());
|
||||
query.executeUpdate();
|
||||
}
|
||||
} catch (SQLException exception) {
|
||||
getLogger().error("Error while cleaning up link requests", exception);
|
||||
}
|
||||
public void cleanLinkRequests() {
|
||||
try (Connection connection = dataSource.getConnection()) {
|
||||
try (PreparedStatement query = connection.prepareStatement(
|
||||
"DELETE FROM `LinkedPlayersRequest` WHERE `requestTime` < ?"
|
||||
)) {
|
||||
query.setLong(1, Instant.now().getEpochSecond() - getVerifyLinkTimeout());
|
||||
query.executeUpdate();
|
||||
}
|
||||
} catch (SQLException exception) {
|
||||
getLogger().error("Error while cleaning up link requests", exception);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] uuidToBytes(UUID uuid) {
|
||||
byte[] uuidBytes = new byte[16];
|
||||
ByteBuffer.wrap(uuidBytes)
|
||||
.order(ByteOrder.BIG_ENDIAN)
|
||||
.putLong(uuid.getMostSignificantBits())
|
||||
.putLong(uuid.getLeastSignificantBits());
|
||||
return uuidBytes;
|
||||
}
|
||||
|
||||
private UUID bytesToUUID(byte[] uuidBytes) {
|
||||
ByteBuffer buf = ByteBuffer.wrap(uuidBytes);
|
||||
return new UUID(buf.getLong(), buf.getLong());
|
||||
}
|
||||
private byte[] uuidToBytes(UUID uuid) {
|
||||
byte[] uuidBytes = new byte[16];
|
||||
ByteBuffer.wrap(uuidBytes)
|
||||
.order(ByteOrder.BIG_ENDIAN)
|
||||
.putLong(uuid.getMostSignificantBits())
|
||||
.putLong(uuid.getLeastSignificantBits());
|
||||
return uuidBytes;
|
||||
}
|
||||
|
||||
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
|
||||
public class MysqlConfig implements DatabaseConfig {
|
||||
private String hostname = "localhost";
|
||||
private String database = "floodgate";
|
||||
private String username = "floodgate";
|
||||
private String password;
|
||||
private String hostname = "localhost";
|
||||
private String database = "floodgate";
|
||||
private String username = "floodgate";
|
||||
private String password;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,19 @@ dependencyResolutionManagement {
|
||||
}
|
||||
|
||||
// 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
|
||||
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots") {
|
||||
mavenContent { snapshotsOnly() }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
var velocityVersion = "3.0.1"
|
||||
var velocityVersion = "3.1.1"
|
||||
var log4jVersion = "2.11.2"
|
||||
var gsonVersion = "2.8.8"
|
||||
var guavaVersion = "25.1-jre"
|
||||
|
||||
Reference in New Issue
Block a user