mirror of
https://github.com/GeyserMC/Floodgate.git
synced 2026-01-04 15:31:48 +00:00
Gradle 8, platform isolation, bundled libraries, work on local linking
This commit is contained in:
20
database/build.gradle.kts
Normal file
20
database/build.gradle.kts
Normal file
@@ -0,0 +1,20 @@
|
||||
plugins {
|
||||
id("floodgate.shadow-conventions")
|
||||
id("io.micronaut.library")
|
||||
id("floodgate.dependency-hash")
|
||||
}
|
||||
|
||||
configurations.runtimeClasspath.get()
|
||||
.exclude("org.slf4j", "slf4j-api")
|
||||
.exclude("javax.validation", "validation-api")
|
||||
.exclude("io.micronaut", "micronaut-aop")
|
||||
.exclude("io.micronaut", "micronaut-core")
|
||||
.exclude("io.micronaut", "micronaut-runtime")
|
||||
.exclude("io.micronaut", "micronaut-inject")
|
||||
.exclude("io.micronaut", "micronaut-context")
|
||||
|
||||
dependencies {
|
||||
implementation("io.micronaut.data:micronaut-data-hibernate-jpa")
|
||||
implementation("io.micronaut.sql:micronaut-jdbc-hikari")
|
||||
runtimeOnly("com.h2database:h2")
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
val mongoClientVersion = "4.4.1"
|
||||
|
||||
dependencies {
|
||||
provided(projects.core)
|
||||
implementation("org.mongodb", "mongodb-driver-sync" , mongoClientVersion)
|
||||
}
|
||||
|
||||
description = "The Floodgate database extension for MongoDB"
|
||||
|
||||
relocate("com.mongodb")
|
||||
relocate("org.bson")
|
||||
@@ -1,343 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.database;
|
||||
|
||||
import com.mongodb.ConnectionString;
|
||||
import com.mongodb.MongoClientSettings;
|
||||
import com.mongodb.MongoCredential;
|
||||
import com.mongodb.client.MongoClient;
|
||||
import com.mongodb.client.MongoClients;
|
||||
import com.mongodb.client.MongoCollection;
|
||||
import com.mongodb.client.MongoCursor;
|
||||
import com.mongodb.client.MongoDatabase;
|
||||
import com.mongodb.client.model.Filters;
|
||||
import com.mongodb.client.model.IndexOptions;
|
||||
import com.mongodb.client.model.Indexes;
|
||||
import com.mongodb.client.model.UpdateOptions;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import org.bson.Document;
|
||||
import org.bson.conversions.Bson;
|
||||
import org.bson.internal.Base64;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.floodgate.api.link.LinkRequest;
|
||||
import org.geysermc.floodgate.api.link.LinkRequestResult;
|
||||
import org.geysermc.floodgate.core.link.CommonPlayerLink;
|
||||
import org.geysermc.floodgate.core.link.LinkRequestImpl;
|
||||
import org.geysermc.floodgate.database.config.MongoConfig;
|
||||
import org.geysermc.floodgate.util.LinkedPlayer;
|
||||
|
||||
public class MongoDbDatabase extends CommonPlayerLink {
|
||||
private MongoClient client;
|
||||
private MongoDatabase database;
|
||||
private MongoCollection<Document> linkedPlayer;
|
||||
private MongoCollection<Document> linkedPlayerRequests;
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
getLogger().info("Connecting to MongoDB database...");
|
||||
try {
|
||||
MongoConfig databaseConfig = getConfig(MongoConfig.class);
|
||||
|
||||
MongoClientSettings.Builder settings = MongoClientSettings.builder();
|
||||
settings.applyToConnectionPoolSettings(builder -> {
|
||||
builder.maxSize(10);
|
||||
builder.minSize(2);
|
||||
});
|
||||
|
||||
if (databaseConfig.getMongouri().isEmpty()) {
|
||||
settings.credential(
|
||||
MongoCredential.createCredential(
|
||||
databaseConfig.getUsername(),
|
||||
databaseConfig.getDatabase(),
|
||||
databaseConfig.getPassword().toCharArray()
|
||||
)
|
||||
);
|
||||
} else {
|
||||
settings.applyConnectionString(new ConnectionString(databaseConfig.getMongouri()));
|
||||
}
|
||||
|
||||
client = MongoClients.create(settings.build());
|
||||
|
||||
database = client.getDatabase(databaseConfig.getDatabase());
|
||||
|
||||
linkedPlayer = database.getCollection("LinkedPlayers");
|
||||
if (collectionNotExists("LinkedPlayers")) {
|
||||
database.createCollection("LinkedPlayers");
|
||||
|
||||
linkedPlayer.createIndex(new Document("bedrockId", 1),
|
||||
new IndexOptions().unique(true)); // primary key equivalent
|
||||
linkedPlayer.createIndex(Indexes.ascending("javaUniqueId"));
|
||||
}
|
||||
|
||||
linkedPlayerRequests = database.getCollection("LinkedPlayerRequests");
|
||||
if (collectionNotExists("LinkedPlayerRequests")) {
|
||||
database.createCollection("LinkedPlayerRequests");
|
||||
|
||||
linkedPlayerRequests.createIndex(new Document("bedrockId", 1),
|
||||
new IndexOptions().unique(true)); // primary key equivalent
|
||||
linkedPlayerRequests.createIndex(Indexes.ascending("requestTime"));
|
||||
}
|
||||
|
||||
getLogger().info("Connected to MongoDB database.");
|
||||
} catch (Exception exception) {
|
||||
getLogger().error("Error while loading database", exception);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
super.stop();
|
||||
client.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public CompletableFuture<LinkedPlayer> getLinkedPlayer(@NonNull UUID bedrockId) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
Bson filter = Filters.eq("bedrockId", uuidToBytes(bedrockId));
|
||||
|
||||
try (MongoCursor<Document> cursor = linkedPlayer.find(filter).cursor()) {
|
||||
if (cursor.hasNext()) {
|
||||
Document document = cursor.next();
|
||||
String javaUsername = document.getString("javaUsername");
|
||||
UUID javaUniqueId = bytesToUUID(document.getString("javaUniqueId"));
|
||||
|
||||
return LinkedPlayer.of(javaUsername, javaUniqueId, bedrockId);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (Exception exception) {
|
||||
getLogger().error("Error while getting LinkedPlayer", exception);
|
||||
throw new CompletionException("Error while getting LinkedPlayer", exception);
|
||||
}
|
||||
}, getExecutorService());
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public CompletableFuture<Boolean> isLinkedPlayer(@NonNull UUID playerId) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
String uuidBytes = uuidToBytes(playerId);
|
||||
Bson filter = Filters.or(
|
||||
Filters.eq("bedrockId", uuidBytes),
|
||||
Filters.eq("javaUniqueId", uuidBytes)
|
||||
);
|
||||
try (MongoCursor<Document> cursor = linkedPlayer.find(filter).cursor()) {
|
||||
return cursor.hasNext();
|
||||
}
|
||||
} catch (Exception 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 {
|
||||
Bson filter = Filters.eq("javaUsername", javaUsername);
|
||||
Document create = new Document("bedrockId", uuidToBytes(bedrockId))
|
||||
.append("javaUniqueId", uuidToBytes(javaId))
|
||||
.append("javaUsername", javaUsername);
|
||||
Document update = new Document("$set", create);
|
||||
|
||||
linkedPlayer.updateOne(filter, update, new UpdateOptions().upsert(true));
|
||||
// The upsert option will create a new document if the filter doesn't match anything.
|
||||
// Or will update the document if it does match.
|
||||
} catch (Exception 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 {
|
||||
String uuidBytes = uuidToBytes(javaId);
|
||||
|
||||
Bson filter = Filters.and(
|
||||
Filters.eq("javaUniqueId", uuidBytes),
|
||||
Filters.eq("bedrockId", uuidBytes)
|
||||
);
|
||||
|
||||
linkedPlayer.deleteMany(filter);
|
||||
} catch (Exception 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 {
|
||||
Bson filter = Filters.eq("javaUsername", javaUsername);
|
||||
Document create = new Document("javaUsername", javaUsername)
|
||||
.append("javaUniqueId", uuidToBytes(javaId))
|
||||
.append("linkCode", linkCode)
|
||||
.append("bedrockUsername", bedrockUsername)
|
||||
.append("requestTime", Instant.now().getEpochSecond());
|
||||
Document update = new Document("$set", create);
|
||||
|
||||
linkedPlayerRequests.updateOne(filter, update, new UpdateOptions().upsert(true));
|
||||
// The upsert option will create a new document if the filter doesn't match anything.
|
||||
// Or will update the document if it does match.
|
||||
} catch (Exception exception) {
|
||||
getLogger().error("Error while linking player", exception);
|
||||
throw new CompletionException("Error while linking player", exception);
|
||||
}
|
||||
}
|
||||
|
||||
private void removeLinkRequest(String javaUsername) {
|
||||
try {
|
||||
Document filter = new Document("javaUsername", javaUsername);
|
||||
linkedPlayerRequests.deleteMany(filter);
|
||||
} catch (Exception exception) {
|
||||
getLogger().error("Error while cleaning up LinkRequest", 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 LinkRequest getLinkRequest0(String javaUsername) {
|
||||
try {
|
||||
Bson filter = Filters.eq("javaUsername", javaUsername);
|
||||
try (MongoCursor<Document> cursor = linkedPlayerRequests.find(filter).cursor()) {
|
||||
if (cursor.hasNext()) {
|
||||
Document document = cursor.next();
|
||||
UUID javaId = bytesToUUID(document.getString("javaUniqueId"));
|
||||
String linkCode = document.getString("linkCode");
|
||||
String bedrockUsername = document.getString("bedrockUsername");
|
||||
long requestTime = document.getLong("requestTime");
|
||||
return new LinkRequestImpl(javaUsername, javaId, linkCode, bedrockUsername,
|
||||
requestTime);
|
||||
}
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
getLogger().error("Error while getLinkRequest", exception);
|
||||
throw new CompletionException("Error while getLinkRequest", exception);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void cleanLinkRequests() {
|
||||
try {
|
||||
Document filter = new Document("requestTime",
|
||||
new Document("$lt", Instant.now().getEpochSecond() - getVerifyLinkTimeout()));
|
||||
linkedPlayerRequests.deleteMany(filter);
|
||||
} catch (Exception exception) {
|
||||
getLogger().error("Error while cleaning up link requests", exception);
|
||||
}
|
||||
}
|
||||
|
||||
private String uuidToBytes(UUID uuid) {
|
||||
byte[] uuidBytes = new byte[16];
|
||||
ByteBuffer.wrap(uuidBytes)
|
||||
.order(ByteOrder.BIG_ENDIAN)
|
||||
.putLong(uuid.getMostSignificantBits())
|
||||
.putLong(uuid.getLeastSignificantBits());
|
||||
return Base64.encode(uuidBytes);
|
||||
}
|
||||
|
||||
private UUID bytesToUUID(String uuidBytes) {
|
||||
ByteBuffer buf = ByteBuffer.wrap(Base64.decode(uuidBytes));
|
||||
return new UUID(buf.getLong(), buf.getLong());
|
||||
}
|
||||
|
||||
public boolean collectionNotExists(final String collectionName) {
|
||||
return !database.listCollectionNames().into(new ArrayList<>()).contains(collectionName);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.database.config;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.geysermc.floodgate.core.database.config.DatabaseConfig;
|
||||
|
||||
@Getter
|
||||
public class MongoConfig implements DatabaseConfig {
|
||||
private String hostname = "localhost";
|
||||
private String database = "floodgate";
|
||||
private String username = "floodgate";
|
||||
private String password;
|
||||
private String mongouri = "";
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"mainClass": "org.geysermc.floodgate.database.MongoDbDatabase",
|
||||
"config": "mongo.yml"
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
hostname: "localhost"
|
||||
database: "floodgate"
|
||||
username: "floodgate"
|
||||
password: ""
|
||||
mongouri: ""
|
||||
@@ -1,4 +0,0 @@
|
||||
[*]
|
||||
indent_size = 2
|
||||
tab_width = 2
|
||||
ij_continuation_indent_size = 4
|
||||
@@ -1,12 +0,0 @@
|
||||
val mysqlClientVersion = "8.0.30"
|
||||
val hikariVersion = "4.0.3"
|
||||
|
||||
dependencies {
|
||||
provided(projects.core)
|
||||
implementation("mysql", "mysql-connector-java", mysqlClientVersion)
|
||||
implementation("com.zaxxer", "HikariCP", hikariVersion)
|
||||
}
|
||||
|
||||
description = "The Floodgate database extension for MySQL"
|
||||
|
||||
relocate("org.mariadb")
|
||||
@@ -1,340 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
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;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.time.Instant;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.floodgate.api.link.LinkRequest;
|
||||
import org.geysermc.floodgate.api.link.LinkRequestResult;
|
||||
import org.geysermc.floodgate.core.link.CommonPlayerLink;
|
||||
import org.geysermc.floodgate.core.link.LinkRequestImpl;
|
||||
import org.geysermc.floodgate.database.config.MysqlConfig;
|
||||
import org.geysermc.floodgate.util.LinkedPlayer;
|
||||
|
||||
public class MysqlDatabase extends CommonPlayerLink {
|
||||
private HikariDataSource dataSource;
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
getLogger().info("Connecting to a MySQL-like database...");
|
||||
try {
|
||||
MysqlConfig config = getConfig(MysqlConfig.class);
|
||||
|
||||
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);
|
||||
|
||||
dataSource = new HikariDataSource(hikariConfig);
|
||||
|
||||
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();
|
||||
dataSource.close();
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
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<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());
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@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 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (SQLException exception) {
|
||||
getLogger().error("Error while getLinkRequest", exception);
|
||||
throw new CompletionException("Error while getLinkRequest", exception);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.database.config;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.geysermc.floodgate.core.database.config.DatabaseConfig;
|
||||
|
||||
@Getter
|
||||
public class MysqlConfig implements DatabaseConfig {
|
||||
private String hostname = "localhost";
|
||||
private String database = "floodgate";
|
||||
private String username = "floodgate";
|
||||
private String password;
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"mainClass": "org.geysermc.floodgate.database.MysqlDatabase",
|
||||
"config": "mysql.yml"
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
hostname: "localhost"
|
||||
database: "floodgate"
|
||||
username: "floodgate"
|
||||
password: ""
|
||||
@@ -1,8 +0,0 @@
|
||||
val sqliteJdbcVersion = "3.36.0.3"
|
||||
|
||||
dependencies {
|
||||
provided(projects.core)
|
||||
implementation("org.xerial", "sqlite-jdbc", sqliteJdbcVersion)
|
||||
}
|
||||
|
||||
description = "The Floodgate database extension for SQLite"
|
||||
@@ -1,217 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.database;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.floodgate.api.link.LinkRequest;
|
||||
import org.geysermc.floodgate.api.link.LinkRequestResult;
|
||||
import org.geysermc.floodgate.core.link.CommonPlayerLink;
|
||||
import org.geysermc.floodgate.core.link.LinkRequestImpl;
|
||||
import org.geysermc.floodgate.util.LinkedPlayer;
|
||||
|
||||
public class SqliteDatabase extends CommonPlayerLink {
|
||||
private final Map<String, LinkRequest> activeLinkRequests = new HashMap<>();
|
||||
private Connection connection;
|
||||
|
||||
/* These are DELIBERATELY javax imports so Guice relocations can't break it */
|
||||
@Inject
|
||||
@Named("dataDirectory")
|
||||
private Path dataDirectory;
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
Path databasePath = dataDirectory.resolve("linked-players.db");
|
||||
try {
|
||||
Class.forName("org.sqlite.JDBC");
|
||||
connection = DriverManager.getConnection("jdbc:sqlite:" + databasePath);
|
||||
try (Statement statement = connection.createStatement()) {
|
||||
statement.executeUpdate(
|
||||
"create table if not exists LinkedPlayers (bedrockId string, javaUniqueId string, javaUsername string)"
|
||||
);
|
||||
}
|
||||
} catch (ClassNotFoundException exception) {
|
||||
getLogger().error("The required class to load the SQLite database wasn't found");
|
||||
} catch (SQLException exception) {
|
||||
getLogger().error("Error while loading database", exception);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
super.stop();
|
||||
try {
|
||||
connection.close();
|
||||
} catch (SQLException exception) {
|
||||
getLogger().error("Error while closing database connection", exception);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public CompletableFuture<LinkedPlayer> getLinkedPlayer(@NonNull UUID bedrockId) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try (PreparedStatement query = connection.prepareStatement(
|
||||
"select * from LinkedPlayers where bedrockId = ?")) {
|
||||
|
||||
query.setString(1, bedrockId.toString());
|
||||
try (ResultSet result = query.executeQuery()) {
|
||||
if (!result.next()) {
|
||||
return null;
|
||||
}
|
||||
String javaUsername = result.getString("javaUsername");
|
||||
UUID javaUniqueId = UUID.fromString(result.getString("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<Boolean> isLinkedPlayer(@NonNull UUID playerId) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try (PreparedStatement query = connection.prepareStatement(
|
||||
"select javaUniqueId from LinkedPlayers where bedrockId = ? or javaUniqueId = ?")) {
|
||||
|
||||
query.setString(1, playerId.toString());
|
||||
query.setString(2, playerId.toString());
|
||||
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 username) {
|
||||
return CompletableFuture.runAsync(
|
||||
() -> linkPlayer0(bedrockId, javaId, username),
|
||||
getExecutorService());
|
||||
}
|
||||
|
||||
private void linkPlayer0(UUID bedrockId, UUID javaId, String username) {
|
||||
try (PreparedStatement query =
|
||||
connection.prepareStatement("insert into LinkedPlayers values(?, ?, ?)")) {
|
||||
|
||||
query.setString(1, bedrockId.toString());
|
||||
query.setString(2, javaId.toString());
|
||||
query.setString(3, username);
|
||||
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 (PreparedStatement query = connection.prepareStatement(
|
||||
"delete from LinkedPlayers where javaUniqueId = ? or bedrockId = ?")) {
|
||||
|
||||
query.setString(1, javaId.toString());
|
||||
query.setString(2, javaId.toString());
|
||||
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(() -> {
|
||||
LinkRequest request =
|
||||
new LinkRequestImpl(javaUsername, javaId, createCode(), bedrockUsername);
|
||||
|
||||
activeLinkRequests.put(javaUsername, request);
|
||||
|
||||
return request.getLinkCode();
|
||||
}, getExecutorService());
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public CompletableFuture<LinkRequestResult> verifyLinkRequest(
|
||||
@NonNull UUID bedrockId,
|
||||
@NonNull String javaUsername,
|
||||
@NonNull String bedrockUsername,
|
||||
@NonNull String code) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
LinkRequest request = activeLinkRequests.get(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
|
||||
activeLinkRequests.remove(javaUsername);
|
||||
|
||||
if (request.isExpired(getVerifyLinkTimeout())) {
|
||||
return LinkRequestResult.REQUEST_EXPIRED;
|
||||
}
|
||||
|
||||
linkPlayer0(bedrockId, request.getJavaUniqueId(), javaUsername);
|
||||
return LinkRequestResult.LINK_COMPLETED;
|
||||
}, getExecutorService());
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"mainClass": "org.geysermc.floodgate.database.SqliteDatabase"
|
||||
}
|
||||
Reference in New Issue
Block a user