mirror of
https://github.com/GeyserMC/Floodgate.git
synced 2025-12-19 14:59:20 +00:00
Gradle 8, platform isolation, bundled libraries, work on local linking
This commit is contained in:
@@ -1,9 +0,0 @@
|
||||
plugins {
|
||||
id("floodgate.shadow-conventions")
|
||||
}
|
||||
|
||||
tasks {
|
||||
shadowJar {
|
||||
archiveBaseName.set(archiveBaseName.get() + "-database")
|
||||
}
|
||||
}
|
||||
@@ -17,10 +17,9 @@ tasks.register("listDependencyInfo") {
|
||||
|
||||
tasks.register("writeDependencyInfo") {
|
||||
doLast {
|
||||
Files.writeString(
|
||||
dependenciesInfoFile(),
|
||||
dependenciesToString(listDependencies())
|
||||
)
|
||||
val path = dependenciesInfoFile()
|
||||
Files.createDirectories(path.parent)
|
||||
Files.writeString(path, dependenciesToString(listDependencies()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,9 +33,9 @@ tasks.register("checkDependencyInfoFile") {
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named("build") {
|
||||
dependsOn("checkDependencyHashesFile")
|
||||
}
|
||||
//tasks.named("build") {
|
||||
// dependsOn("checkDependencyInfoFile")
|
||||
//}
|
||||
|
||||
fun dependenciesToString(dependencies: Collection<Dependency>): String {
|
||||
val builder = StringBuilder()
|
||||
|
||||
@@ -10,6 +10,7 @@ tasks {
|
||||
archiveClassifier.set("unshaded")
|
||||
from(project.rootProject.file("LICENSE"))
|
||||
}
|
||||
|
||||
val shadowJar = named<ShadowJar>("shadowJar") {
|
||||
archiveBaseName.set("floodgate-${project.name}")
|
||||
archiveVersion.set("")
|
||||
@@ -39,6 +40,7 @@ tasks {
|
||||
destinationDirectory.set(file(destinationDir))
|
||||
}
|
||||
}
|
||||
|
||||
named("build") {
|
||||
dependsOn(shadowJar)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
plugins {
|
||||
`java-library`
|
||||
id("floodgate.build-logic")
|
||||
id("io.freefair.lombok") version "6.3.0" apply false
|
||||
id("io.freefair.lombok") version "8.0.1" apply false
|
||||
id("io.micronaut.library") version "3.7.8" apply false
|
||||
}
|
||||
|
||||
allprojects {
|
||||
@@ -16,7 +17,7 @@ val deployProjects = setOf(
|
||||
projects.core,
|
||||
projects.bungee,
|
||||
projects.spigot,
|
||||
projects.velocity,
|
||||
projects.velocityIsolated,
|
||||
projects.universal
|
||||
).map { it.dependencyProject }
|
||||
|
||||
@@ -30,13 +31,6 @@ subprojects {
|
||||
plugin("floodgate.build-logic")
|
||||
}
|
||||
|
||||
val relativePath = projectDir.relativeTo(rootProject.projectDir).path
|
||||
|
||||
if (relativePath.startsWith("database" + File.separator)) {
|
||||
group = rootProject.group as String + ".database"
|
||||
plugins.apply("floodgate.database-conventions")
|
||||
}
|
||||
|
||||
when (this) {
|
||||
in deployProjects -> plugins.apply("floodgate.publish-conventions")
|
||||
else -> plugins.apply("floodgate.base-conventions")
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
plugins {
|
||||
id("floodgate.generate-templates")
|
||||
id("floodgate.dependency-hash")
|
||||
id("io.micronaut.library") version "3.7.4"
|
||||
id("io.micronaut.library")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(projects.api)
|
||||
compileOnlyApi(projects.isolation)
|
||||
api("org.geysermc.configutils", "configutils", Versions.configUtilsVersion)
|
||||
|
||||
api("com.google.inject", "guice", Versions.guiceVersion)
|
||||
@@ -23,9 +24,17 @@ dependencies {
|
||||
api("io.micronaut", "micronaut-context")
|
||||
|
||||
annotationProcessor("io.micronaut.data:micronaut-data-processor")
|
||||
implementation("io.micronaut.data:micronaut-data-hibernate-jpa")
|
||||
implementation("io.micronaut.sql:micronaut-jdbc-hikari")
|
||||
runtimeOnly("com.h2database:h2")
|
||||
implementation("io.micronaut.data:micronaut-data-model")
|
||||
implementation("jakarta.persistence:jakarta.persistence-api:2.2.3")
|
||||
|
||||
// compileOnlyApi("io.micronaut.data:micronaut-data-hibernate-jpa")
|
||||
//todo add these as libs
|
||||
//compileOnly("io.micronaut.data:micronaut-data-hibernate-jpa")
|
||||
//compileOnly("io.micronaut.sql:micronaut-jdbc-hikari")
|
||||
//compileOnly("com.h2database:h2")
|
||||
//implementation("io.micronaut.data:micronaut-data-hibernate-jpa")
|
||||
//implementation("io.micronaut.sql:micronaut-jdbc-hikari")
|
||||
//runtimeOnly("com.h2database:h2")
|
||||
|
||||
// annotationProcessor("io.micronaut.data:micronaut-data-document-processor")
|
||||
// compileOnly("io.micronaut.data:micronaut-data-mongodb")
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
package org.geysermc.floodgate.core;
|
||||
|
||||
import io.micronaut.context.ApplicationContext;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import org.geysermc.floodgate.api.FloodgateApi;
|
||||
@@ -43,18 +42,23 @@ import org.geysermc.floodgate.core.database.entity.LinkedPlayer;
|
||||
import org.geysermc.floodgate.core.event.EventBus;
|
||||
import org.geysermc.floodgate.core.event.lifecycle.PostEnableEvent;
|
||||
import org.geysermc.floodgate.core.event.lifecycle.ShutdownEvent;
|
||||
import org.geysermc.floodgate.core.library.Library;
|
||||
import org.geysermc.floodgate.core.library.LibraryManager;
|
||||
import org.geysermc.floodgate.core.library.Repository;
|
||||
import org.geysermc.floodgate.core.library.info.DependencyInfoLoader;
|
||||
import org.geysermc.floodgate.core.util.EagerSingleton;
|
||||
import org.geysermc.floodgate.isolation.library.Library;
|
||||
import org.geysermc.floodgate.isolation.library.LibraryManager;
|
||||
import org.geysermc.floodgate.isolation.library.Repository;
|
||||
import org.geysermc.floodgate.isolation.library.info.DependencyInfoLoader;
|
||||
|
||||
public abstract class FloodgatePlatform {
|
||||
private static final UUID KEY = UUID.randomUUID();
|
||||
|
||||
private final LibraryManager manager;
|
||||
private ApplicationContext context;
|
||||
private PlatformInjector injector;
|
||||
|
||||
protected FloodgatePlatform(LibraryManager manager) {
|
||||
this.manager = manager;
|
||||
}
|
||||
|
||||
protected void onContextCreated(ApplicationContext context) {
|
||||
}
|
||||
|
||||
@@ -65,19 +69,28 @@ public abstract class FloodgatePlatform {
|
||||
getClass().getClassLoader().getResource("dependencyInfo.txt")
|
||||
);
|
||||
|
||||
new LibraryManager(ClassLoader.getSystemClassLoader().getParent(), Paths.get("./libs"))
|
||||
manager
|
||||
// .addLibrary(
|
||||
// Library.builder(infoLoader)
|
||||
// .id("guava")
|
||||
// .repository(Repository.MAVEN_CENTRAL)
|
||||
// .groupId("com.google.guava")
|
||||
// .artifactId("guava")
|
||||
// .build()
|
||||
// )
|
||||
.addLibrary(
|
||||
Library.builder(infoLoader)
|
||||
.id("guava")
|
||||
.repository(Repository.MAVEN_CENTRAL)
|
||||
.groupId("com.google.guava")
|
||||
.artifactId("guava")
|
||||
Library.builder()
|
||||
.id("local-linking")
|
||||
.repository(Repository.OPEN_COLLAB)
|
||||
.groupId("org.geysermc.floodgate")
|
||||
.artifactId("database")
|
||||
.version("a-version")
|
||||
.build()
|
||||
)
|
||||
.apply();
|
||||
|
||||
//noinspection unchecked
|
||||
context = ApplicationContext.builder()
|
||||
context = ApplicationContext.builder(manager.classLoader())
|
||||
.properties(Map.of(
|
||||
"platform.proxy", isProxy()
|
||||
))
|
||||
@@ -140,8 +153,6 @@ public abstract class FloodgatePlatform {
|
||||
throw new RuntimeException("Failed to inject the packet listener!", exception);
|
||||
}
|
||||
|
||||
// this.guice = guice.createChildInjector(new PostEnableModules(postEnableStageModules()));
|
||||
|
||||
context.getBean(EventBus.class).fire(new PostEnableEvent());
|
||||
}
|
||||
|
||||
|
||||
@@ -30,12 +30,9 @@ import com.google.gson.JsonObject;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
import jakarta.inject.Singleton;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.URL;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
@@ -97,26 +94,6 @@ public class HttpClient {
|
||||
return connection;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public HttpResponse<byte[]> getRawData(String urlString) throws IOException {
|
||||
HttpURLConnection connection = request(urlString);
|
||||
|
||||
try (InputStream inputStream = connection.getInputStream()) {
|
||||
int responseCode = connection.getResponseCode();
|
||||
|
||||
byte[] buffer = new byte[8196];
|
||||
int len;
|
||||
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
||||
while ((len = inputStream.read(buffer, 0, buffer.length)) != -1) {
|
||||
outputStream.write(buffer, 0, len);
|
||||
}
|
||||
return new HttpResponse<>(responseCode, outputStream.toByteArray());
|
||||
}
|
||||
} catch (SocketTimeoutException | NullPointerException exception) {
|
||||
return new HttpResponse<>(-1, null);
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private <T> HttpResponse<T> readResponse(HttpURLConnection connection, Class<T> clazz) {
|
||||
InputStreamReader streamReader = createReader(connection);
|
||||
|
||||
@@ -34,7 +34,6 @@ import java.io.InputStreamReader;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
import java.util.UUID;
|
||||
@@ -134,8 +133,4 @@ public class Utils {
|
||||
future.completeExceptionally(ex);
|
||||
return future;
|
||||
}
|
||||
|
||||
public static String base64Encode(byte[] data) {
|
||||
return Base64.getEncoder().encodeToString(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
org.geysermc.configutils:configutils:1.0-SNAPSHOT:Xyh6oCOT1RwP5hw1gRM77M0mOJ86nNPf4J9Hs9PL4Ek=
|
||||
org.geysermc.configutils:configutils:2.0-SNAPSHOT:HiYf2qgXat5WOQOScSXPk8FBIKZDWYowCJPVJ+Uuv+0=
|
||||
com.google.inject:guice:5.1.0:QTDlC/rEgJnIYPDZA7kYYMgaJJyQ84JF+P7Vj8gXvCY=
|
||||
com.nukkitx.fastutil:fastutil-short-object-maps:8.5.3:nTF5o2PCLMeBm8gaPeU4Q41FG2CNpmC9PxeJVHZot+U=
|
||||
com.nukkitx.fastutil:fastutil-int-object-maps:8.5.3:Z0iKxf9OmoIcQtmqV9Lo9Z39XSpBXKjL8tavAdVCKBY=
|
||||
org.java-websocket:Java-WebSocket:1.5.2:/4adGYqNxdAJZzkuGHrtqTuDO3zdO8zvs9f7WDQJi4U=
|
||||
cloud.commandframework:cloud-core:1.5.0:+hDJlLwU84P6sf0gkDq+xStIqAHhMXUCI+Wb8VsD1Zk=
|
||||
io.micronaut.data:micronaut-data-model:3.9.6:Hdt2G2LRuAtIs1K2DayRz7OoiqZcbIfCUEstVIpQnUE=
|
||||
io.micronaut.sql:micronaut-jdbc-hikari:4.7.2:w2N1dH4vY/QjjQGsViMOwgVYxAnZNGgMm1doCa19wOA=
|
||||
io.micronaut.data:micronaut-data-hibernate-jpa:3.9.6:kFYugtQeXfLjqUCWrf3MFFDoUdYhXrPQmSh0pu2Zxzo=
|
||||
io.micronaut:micronaut-context:3.8.7:ir020Tq3UavnPZhXOUNHCl/XdGcVrOzhM1lU5NT6YEc=
|
||||
|
||||
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,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,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"
|
||||
}
|
||||
@@ -3,4 +3,4 @@ org.gradle.caching=true
|
||||
org.gradle.parallel=true
|
||||
|
||||
version=2.2.2-SNAPSHOT
|
||||
micronautVersion=3.8.7
|
||||
micronautVersion=3.9.0
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
3
gradle/wrapper/gradle-wrapper.properties
vendored
3
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip
|
||||
networkTimeout=10000
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
28
gradlew
vendored
28
gradlew
vendored
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright <EFBFBD> 2015-2021 the original authors.
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -32,10 +32,10 @@
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions <EFBFBD>$var<EFBFBD>, <EFBFBD>${var}<EFBFBD>, <EFBFBD>${var:-default}<EFBFBD>, <EFBFBD>${var+SET}<EFBFBD>,
|
||||
# <EFBFBD>${var#prefix}<EFBFBD>, <EFBFBD>${var%suffix}<EFBFBD>, and <EFBFBD>$( cmd )<EFBFBD>;
|
||||
# * compound commands having a testable exit status, especially <EFBFBD>case<EFBFBD>;
|
||||
# * various built-in commands including <EFBFBD>command<EFBFBD>, <EFBFBD>set<EFBFBD>, and <EFBFBD>ulimit<EFBFBD>.
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
@@ -55,7 +55,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
@@ -80,10 +80,10 @@ do
|
||||
esac
|
||||
done
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
@@ -143,12 +143,16 @@ fi
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
@@ -205,6 +209,12 @@ set -- \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
|
||||
15
gradlew.bat
vendored
15
gradlew.bat
vendored
@@ -14,7 +14,7 @@
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@@ -25,7 +25,8 @@
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
@@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
7
isolation/build.gradle.kts
Normal file
7
isolation/build.gradle.kts
Normal file
@@ -0,0 +1,7 @@
|
||||
plugins {
|
||||
id("floodgate.base-conventions")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(projects.api)
|
||||
}
|
||||
@@ -23,7 +23,7 @@
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.core.library;
|
||||
package org.geysermc.floodgate.isolation.library;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.security.MessageDigest;
|
||||
@@ -32,24 +32,26 @@ import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.Objects;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.floodgate.core.library.info.DependencyInfo;
|
||||
import org.geysermc.floodgate.core.library.info.DependencyInfoLoader;
|
||||
import org.geysermc.floodgate.core.util.Utils;
|
||||
import org.geysermc.floodgate.isolation.library.info.DependencyInfo;
|
||||
import org.geysermc.floodgate.isolation.library.info.DependencyInfoLoader;
|
||||
|
||||
public record Library(
|
||||
@NonNull String id,
|
||||
@NonNull Repository repository,
|
||||
@NonNull String groupId,
|
||||
String groupId,
|
||||
@NonNull String artifactId,
|
||||
@NonNull String version,
|
||||
byte[] sha256
|
||||
String version,
|
||||
byte[] sha256,
|
||||
boolean forceOverride
|
||||
) {
|
||||
public Library {
|
||||
Objects.requireNonNull(id);
|
||||
Objects.requireNonNull(repository);
|
||||
Objects.requireNonNull(groupId);
|
||||
Objects.requireNonNull(artifactId);
|
||||
Objects.requireNonNull(version);
|
||||
if (repository != Repository.BUNDLED) {
|
||||
Objects.requireNonNull(groupId);
|
||||
Objects.requireNonNull(version);
|
||||
}
|
||||
}
|
||||
|
||||
public String path() {
|
||||
@@ -71,10 +73,18 @@ public record Library(
|
||||
}
|
||||
|
||||
public Path filePath() {
|
||||
return Path.of(id(), id() + "-" + version() + ".jar");
|
||||
var fileName = id();
|
||||
if (!forceOverride) {
|
||||
fileName += "-" + version();
|
||||
}
|
||||
return Path.of(id(), fileName + ".jar");
|
||||
}
|
||||
|
||||
public void validateChecksum(byte[] data) {
|
||||
if (sha256 == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] hash;
|
||||
try {
|
||||
hash = MessageDigest.getInstance("SHA-256").digest(data);
|
||||
@@ -88,8 +98,8 @@ public record Library(
|
||||
|
||||
throw new IllegalStateException(String.format(
|
||||
"Downloaded library hash (%s) didn't match expected hash (%s)!",
|
||||
Utils.base64Encode(data),
|
||||
Utils.base64Encode(sha256())
|
||||
Base64.getEncoder().encodeToString(data),
|
||||
Base64.getEncoder().encodeToString(sha256())
|
||||
));
|
||||
}
|
||||
|
||||
@@ -103,6 +113,7 @@ public record Library(
|
||||
|
||||
private String version;
|
||||
private byte[] sha256;
|
||||
private boolean forceOverride;
|
||||
|
||||
LibraryBuilder(DependencyInfoLoader info) {
|
||||
this.dependencyInfoLoader = info;
|
||||
@@ -142,18 +153,18 @@ public record Library(
|
||||
return sha256(Base64.getDecoder().decode(sha256));
|
||||
}
|
||||
|
||||
public LibraryBuilder forceOverride(boolean forceOverride) {
|
||||
this.forceOverride = forceOverride;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Library build() {
|
||||
if (version == null || sha256 == null) {
|
||||
if (dependencyInfoLoader == null) {
|
||||
throw new IllegalStateException(
|
||||
"Provide a version and hash or provide a DependencyInfoLoader"
|
||||
);
|
||||
}
|
||||
DependencyInfo dependencyInfo = dependencyInfoLoader.byCombinedId(groupId, artifactId);
|
||||
version = dependencyInfo.version();
|
||||
sha256 = dependencyInfo.sha256();
|
||||
if (dependencyInfoLoader != null) {
|
||||
DependencyInfo info = dependencyInfoLoader.byCombinedId(groupId, artifactId);
|
||||
version = info.version();
|
||||
sha256 = info.sha256();
|
||||
}
|
||||
return new Library(id, repository, groupId, artifactId, version, sha256);
|
||||
return new Library(id, repository, groupId, artifactId, version, sha256, forceOverride);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,14 +23,15 @@
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.core.library;
|
||||
package org.geysermc.floodgate.isolation.library;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.geysermc.floodgate.core.library.classloader.LibraryClassLoader;
|
||||
import org.geysermc.floodgate.isolation.library.classloader.LibraryClassLoader;
|
||||
import org.geysermc.floodgate.isolation.util.ChildFirstClassLoader;
|
||||
|
||||
// Credits for the idea of downloading dependencies on runtime go to LuckPerms
|
||||
public final class LibraryManager {
|
||||
@@ -39,8 +40,10 @@ public final class LibraryManager {
|
||||
|
||||
private final Set<Library> toApply = new HashSet<>();
|
||||
|
||||
public LibraryManager(ClassLoader parent, Path cacheDirectory) {
|
||||
this.classLoader = new LibraryClassLoader(parent);
|
||||
public LibraryManager(ClassLoader parent, Path cacheDirectory, boolean childFirst) {
|
||||
this.classLoader = childFirst ?
|
||||
new ChildFirstClassLoader(parent) :
|
||||
new LibraryClassLoader(parent);
|
||||
this.cacheDirectory = cacheDirectory;
|
||||
}
|
||||
|
||||
@@ -49,12 +52,14 @@ public final class LibraryManager {
|
||||
return this;
|
||||
}
|
||||
|
||||
public void apply() {
|
||||
public LibraryManager apply() {
|
||||
CompletableFuture.allOf(
|
||||
toApply.stream()
|
||||
.map(library -> CompletableFuture.runAsync(() -> loadLibrary(library)))
|
||||
.toArray(CompletableFuture[]::new)
|
||||
).join();
|
||||
toApply.clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
private void loadLibrary(Library library) {
|
||||
@@ -63,10 +68,14 @@ public final class LibraryManager {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Files.exists(libPath)) {
|
||||
if (library.forceOverride() || !Files.exists(libPath)) {
|
||||
library.repository().downloadTo(library, libPath);
|
||||
}
|
||||
|
||||
classLoader.addPath(libPath);
|
||||
}
|
||||
|
||||
public LibraryClassLoader classLoader() {
|
||||
return classLoader;
|
||||
}
|
||||
}
|
||||
@@ -23,16 +23,32 @@
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.core.library;
|
||||
package org.geysermc.floodgate.isolation.library;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import org.geysermc.floodgate.core.util.HttpClient;
|
||||
import org.geysermc.floodgate.core.util.HttpClient.HttpResponse;
|
||||
import org.geysermc.floodgate.isolation.util.HttpUtil;
|
||||
import org.geysermc.floodgate.isolation.util.HttpUtil.HttpResponse;
|
||||
import org.geysermc.floodgate.isolation.util.StreamUtil;
|
||||
|
||||
public enum Repository {
|
||||
MAVEN_CENTRAL("https://repo1.maven.org/maven2/");
|
||||
MAVEN_CENTRAL("https://repo1.maven.org/maven2/"),
|
||||
OPEN_COLLAB("https://repo.opencollab.dev/main/"),
|
||||
BUNDLED(null) {
|
||||
@Override
|
||||
public byte[] download(Library library) {
|
||||
var loader = getClass().getClassLoader();
|
||||
try (var resource = loader.getResourceAsStream("bundled/" + library.id() + ".jar")) {
|
||||
return StreamUtil.readStream(resource);
|
||||
} catch (Exception exception) {
|
||||
throw new IllegalStateException(
|
||||
"Could not load bundled jar " + library.id(),
|
||||
exception
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final String baseUrl;
|
||||
|
||||
@@ -42,16 +58,16 @@ public enum Repository {
|
||||
|
||||
private byte[] justDownload(Library library) {
|
||||
try {
|
||||
HttpResponse<byte[]> response = new HttpClient().getRawData(baseUrl + library.path());
|
||||
HttpResponse<byte[]> result = HttpUtil.getRawData(baseUrl + library.path());
|
||||
|
||||
if (!response.isCodeOk()) {
|
||||
if (!result.isCodeOk()) {
|
||||
throw new RuntimeException(String.format(
|
||||
"Got an invalid response code (%s) while downloading library %s",
|
||||
response.getHttpCode(), library.id()
|
||||
result.httpCode(), library.id()
|
||||
));
|
||||
}
|
||||
|
||||
return response.getResponse();
|
||||
return result.response();
|
||||
} catch (IOException exception) {
|
||||
throw new IllegalStateException("Failed to download library " + library.id(), exception);
|
||||
}
|
||||
@@ -65,6 +81,22 @@ public enum Repository {
|
||||
|
||||
public void downloadTo(Library library, Path location) {
|
||||
try {
|
||||
// delete old versions of a given library
|
||||
if (Files.isDirectory(location.getParent())) {
|
||||
try (var list = Files.list(location.getParent())) {
|
||||
list.forEach(path -> {
|
||||
try {
|
||||
Files.delete(path);
|
||||
} catch (IOException exception) {
|
||||
throw new IllegalStateException(
|
||||
"Could not delete old version of library " + library.id(),
|
||||
exception
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Files.createDirectories(location.getParent());
|
||||
Files.write(location, download(library));
|
||||
} catch (IOException exception) {
|
||||
@@ -23,7 +23,7 @@
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.core.library.classloader;
|
||||
package org.geysermc.floodgate.isolation.library.classloader;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
@@ -49,8 +49,8 @@ public class LibraryClassLoader extends URLClassLoader {
|
||||
try {
|
||||
addURL(path.toUri().toURL());
|
||||
loadedPaths.add(path);
|
||||
} catch (MalformedURLException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
} catch (MalformedURLException exception) {
|
||||
throw new IllegalArgumentException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.core.library.info;
|
||||
package org.geysermc.floodgate.isolation.library.info;
|
||||
|
||||
import java.util.Base64;
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.core.library.info;
|
||||
package org.geysermc.floodgate.isolation.library.info;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2023 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
|
||||
@@ -23,15 +23,32 @@
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.database.config;
|
||||
package org.geysermc.floodgate.isolation.loader;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.geysermc.floodgate.core.database.config.DatabaseConfig;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
@Getter
|
||||
public class MysqlConfig implements DatabaseConfig {
|
||||
private String hostname = "localhost";
|
||||
private String database = "floodgate";
|
||||
private String username = "floodgate";
|
||||
private String password;
|
||||
public class LoaderUtil {
|
||||
public static void invokeLoad(Object platform) {
|
||||
try {
|
||||
platform.getClass().getMethod("load").invoke(platform);
|
||||
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException exception) {
|
||||
throw new RuntimeException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
public static void invokeEnable(Object platform) {
|
||||
try {
|
||||
platform.getClass().getMethod("enable").invoke(platform);
|
||||
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException exception) {
|
||||
throw new RuntimeException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
public static void invokeDisable(Object platform) {
|
||||
try {
|
||||
platform.getClass().getMethod("disable").invoke(platform);
|
||||
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException exception) {
|
||||
throw new RuntimeException(exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2023 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.isolation.loader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import org.geysermc.floodgate.isolation.library.LibraryManager;
|
||||
|
||||
public class PlatformHolder {
|
||||
private final Class<?> platformClass;
|
||||
private final LibraryManager manager;
|
||||
|
||||
private Object platformInstance;
|
||||
|
||||
public PlatformHolder(Class<?> platformClass, LibraryManager manager) {
|
||||
this.platformClass = platformClass;
|
||||
this.manager = manager;
|
||||
}
|
||||
|
||||
public void init(Class<?>[] argumentTypes, Object... argumentValues)
|
||||
throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
|
||||
platformInstance = platformClass
|
||||
.getConstructor(argumentTypes)
|
||||
.newInstance(argumentValues);
|
||||
}
|
||||
|
||||
public void load() {
|
||||
LoaderUtil.invokeLoad(platformInstance);
|
||||
}
|
||||
|
||||
public void enable() {
|
||||
LoaderUtil.invokeEnable(platformInstance);
|
||||
}
|
||||
|
||||
public void disable() {
|
||||
LoaderUtil.invokeDisable(platformInstance);
|
||||
close();
|
||||
}
|
||||
|
||||
public void close() {
|
||||
try {
|
||||
manager.classLoader().close();
|
||||
} catch (IOException exception) {
|
||||
throw new IllegalStateException("Failed to close classloader!", exception);
|
||||
}
|
||||
}
|
||||
|
||||
public PlatformHolder platformInstance(Object platformInstance) {
|
||||
if (this.platformInstance == null) {
|
||||
this.platformInstance = platformInstance;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public LibraryManager manager() {
|
||||
return manager;
|
||||
}
|
||||
|
||||
public Class<?> platformClass() {
|
||||
return platformClass;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2023 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.isolation.loader;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import org.geysermc.floodgate.isolation.library.Library;
|
||||
import org.geysermc.floodgate.isolation.library.LibraryManager;
|
||||
import org.geysermc.floodgate.isolation.library.Repository;
|
||||
import org.geysermc.floodgate.isolation.util.UrlUtil;
|
||||
|
||||
public class PlatformLoader {
|
||||
public static LibraryManager createLibraryManager(ClassLoader loader, Path cacheDirectory) {
|
||||
return new LibraryManager(loader, cacheDirectory, true)
|
||||
.addLibrary(
|
||||
Library.builder()
|
||||
.id("platform-base")
|
||||
.repository(Repository.BUNDLED)
|
||||
.artifactId("platform-base")
|
||||
.forceOverride(true)
|
||||
.build()
|
||||
)
|
||||
.apply();
|
||||
}
|
||||
|
||||
public static PlatformHolder load(LibraryManager manager) {
|
||||
var classLoader = manager.classLoader();
|
||||
|
||||
String mainClassString =
|
||||
UrlUtil.readSingleLine(classLoader.getResource("org.geysermc.mainClass"));
|
||||
|
||||
try {
|
||||
var mainClass = classLoader.loadClass(mainClassString);
|
||||
return new PlatformHolder(mainClass, manager);
|
||||
} catch (Exception exception) {
|
||||
throw new IllegalStateException("Failed to load platform!", exception);
|
||||
}
|
||||
}
|
||||
|
||||
public static PlatformHolder loadDefault(ClassLoader pluginClassLoader, Path cacheDirectory) {
|
||||
return load(createLibraryManager(pluginClassLoader, cacheDirectory));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2023 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.isolation.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import org.geysermc.floodgate.isolation.library.classloader.LibraryClassLoader;
|
||||
|
||||
// Based of a Medium article https://medium.com/@isuru89/java-a-child-first-class-loader-cbd9c3d0305
|
||||
public class ChildFirstClassLoader extends LibraryClassLoader {
|
||||
private final ClassLoader systemClassLoader;
|
||||
|
||||
public ChildFirstClassLoader(ClassLoader parent) {
|
||||
super(Objects.requireNonNull(parent));
|
||||
systemClassLoader = getSystemClassLoader();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
||||
Class<?> loadedClass = findLoadedClass(name);
|
||||
if (loadedClass == null) {
|
||||
try {
|
||||
if (systemClassLoader != null) {
|
||||
loadedClass = systemClassLoader.loadClass(name);
|
||||
}
|
||||
} catch (ClassNotFoundException ignored) {}
|
||||
|
||||
try {
|
||||
if (loadedClass == null) {
|
||||
loadedClass = findClass(name);
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
loadedClass = super.loadClass(name, resolve);
|
||||
}
|
||||
}
|
||||
|
||||
if (resolve) {
|
||||
resolveClass(loadedClass);
|
||||
}
|
||||
return loadedClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enumeration<URL> getResources(String name) throws IOException {
|
||||
List<URL> allResources = new LinkedList<>();
|
||||
|
||||
Enumeration<URL> systemResources = systemClassLoader.getResources(name);
|
||||
if (systemResources != null) {
|
||||
while (systemResources.hasMoreElements()) {
|
||||
allResources.add(systemResources.nextElement());
|
||||
}
|
||||
}
|
||||
|
||||
Enumeration<URL> thisResources = findResources(name);
|
||||
if (thisResources != null) {
|
||||
while (thisResources.hasMoreElements()) {
|
||||
allResources.add(thisResources.nextElement());
|
||||
}
|
||||
}
|
||||
|
||||
Enumeration<URL> parentResources = getParent().getResources(name);
|
||||
if (parentResources != null) {
|
||||
while (parentResources.hasMoreElements()) {
|
||||
allResources.add(parentResources.nextElement());
|
||||
}
|
||||
}
|
||||
|
||||
return new Enumeration<>() {
|
||||
final Iterator<URL> it = allResources.iterator();
|
||||
|
||||
@Override
|
||||
public boolean hasMoreElements() {
|
||||
return it.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL nextElement() {
|
||||
return it.next();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getResource(String name) {
|
||||
URL resource = null;
|
||||
if (systemClassLoader != null) {
|
||||
resource = systemClassLoader.getResource(name);
|
||||
}
|
||||
if (resource == null) {
|
||||
resource = findResource(name);
|
||||
}
|
||||
if (resource == null) {
|
||||
resource = getParent().getResource(name);
|
||||
}
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2023 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.isolation.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.URL;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
public class HttpUtil {
|
||||
private static final String USER_AGENT = "GeyserMC-isolation";
|
||||
|
||||
public static @NonNull HttpResponse<byte[]> getRawData(String urlString) throws IOException {
|
||||
HttpURLConnection connection = request(urlString);
|
||||
|
||||
try (InputStream inputStream = connection.getInputStream()) {
|
||||
return new HttpResponse<>(
|
||||
connection.getResponseCode(),
|
||||
StreamUtil.readStream(inputStream)
|
||||
);
|
||||
} catch (SocketTimeoutException | NullPointerException exception) {
|
||||
return new HttpResponse<>(-1, null);
|
||||
}
|
||||
}
|
||||
|
||||
private static HttpURLConnection request(String urlString) {
|
||||
HttpURLConnection connection;
|
||||
|
||||
try {
|
||||
URL url = new URL(urlString.replace(" ", "%20")); // Encode spaces correctly
|
||||
connection = (HttpURLConnection) url.openConnection();
|
||||
} catch (Exception exception) {
|
||||
throw new RuntimeException("Failed to create connection", exception);
|
||||
}
|
||||
|
||||
try {
|
||||
connection.setRequestMethod("GET");
|
||||
connection.setUseCaches(false);
|
||||
connection.setRequestProperty("User-Agent", USER_AGENT);
|
||||
connection.setConnectTimeout(3000);
|
||||
connection.setReadTimeout(10_000);
|
||||
} catch (Exception exception) {
|
||||
throw new RuntimeException("Failed to create request", exception);
|
||||
}
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
public static class HttpResponse<T> {
|
||||
private final int httpCode;
|
||||
private final T response;
|
||||
|
||||
private HttpResponse(int httpCode, T response) {
|
||||
this.httpCode = httpCode;
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
public int httpCode() {
|
||||
return httpCode;
|
||||
}
|
||||
|
||||
public T response() {
|
||||
return response;
|
||||
}
|
||||
|
||||
public boolean isCodeOk() {
|
||||
return httpCode >= 200 && httpCode < 300;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2023 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
|
||||
@@ -23,16 +23,21 @@
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.database.config;
|
||||
package org.geysermc.floodgate.isolation.util;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.geysermc.floodgate.core.database.config.DatabaseConfig;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
@Getter
|
||||
public class MongoConfig implements DatabaseConfig {
|
||||
private String hostname = "localhost";
|
||||
private String database = "floodgate";
|
||||
private String username = "floodgate";
|
||||
private String password;
|
||||
private String mongouri = "";
|
||||
public class StreamUtil {
|
||||
public static byte[] readStream(InputStream inputStream) throws IOException {
|
||||
byte[] buffer = new byte[8196];
|
||||
int len;
|
||||
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
||||
while ((len = inputStream.read(buffer, 0, buffer.length)) != -1) {
|
||||
outputStream.write(buffer, 0, len);
|
||||
}
|
||||
return outputStream.toByteArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2023 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.isolation.util;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class UrlUtil {
|
||||
public static List<String> readAllLines(URL url) {
|
||||
try {
|
||||
var connection = url.openConnection();
|
||||
var reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
|
||||
|
||||
var list = new ArrayList<String>();
|
||||
String line;
|
||||
|
||||
while ((line = reader.readLine()) != null) {
|
||||
list.add(line);
|
||||
}
|
||||
|
||||
reader.close();
|
||||
return list;
|
||||
} catch (IOException exception) {
|
||||
throw new IllegalStateException("Unable to read url %s".formatted(url), exception);
|
||||
}
|
||||
}
|
||||
public static String readSingleLine(URL url) {
|
||||
var lines = readAllLines(url);
|
||||
if (lines.size() != 1) {
|
||||
throw new IllegalStateException(
|
||||
"Url %s didn't return one line of data, got %s".formatted(url, lines.size())
|
||||
);
|
||||
}
|
||||
return lines.get(0);
|
||||
}
|
||||
}
|
||||
@@ -68,11 +68,16 @@ include(":api")
|
||||
include(":core")
|
||||
include(":bungee")
|
||||
include(":spigot")
|
||||
include(":velocity")
|
||||
include(":universal")
|
||||
include(":sqlite")
|
||||
include(":mysql")
|
||||
include(":mongo")
|
||||
project(":sqlite").projectDir = file("database/sqlite")
|
||||
project(":mysql").projectDir = file("database/mysql")
|
||||
project(":mongo").projectDir = file("database/mongo")
|
||||
include(":database")
|
||||
include(":isolation")
|
||||
|
||||
arrayOf("velocity").forEach { platform ->
|
||||
arrayOf("base", "isolated").forEach {
|
||||
include(":$platform-$it")
|
||||
project(":$platform-$it").projectDir = file("$platform/$it")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import io.micronaut.inject.qualifiers.Qualifiers;
|
||||
import java.nio.file.Path;
|
||||
import org.geysermc.floodgate.core.FloodgatePlatform;
|
||||
import org.geysermc.floodgate.core.util.ReflectionUtils;
|
||||
import org.geysermc.floodgate.isolation.library.LibraryManager;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
public class VelocityPlatform extends FloodgatePlatform {
|
||||
@@ -46,7 +47,9 @@ public class VelocityPlatform extends FloodgatePlatform {
|
||||
@Inject PluginContainer container;
|
||||
@Inject Logger logger;
|
||||
|
||||
public VelocityPlatform() {
|
||||
@Inject
|
||||
public VelocityPlatform(LibraryManager manager) {
|
||||
super(manager);
|
||||
ReflectionUtils.setPrefix("com.velocitypowered.proxy");
|
||||
}
|
||||
|
||||
36
velocity/isolated/build.gradle.kts
Normal file
36
velocity/isolated/build.gradle.kts
Normal file
@@ -0,0 +1,36 @@
|
||||
var log4jVersion = "2.11.2"
|
||||
var gsonVersion = "2.8.8"
|
||||
var guavaVersion = "25.1-jre"
|
||||
|
||||
plugins {
|
||||
java
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(projects.isolation)
|
||||
}
|
||||
|
||||
tasks {
|
||||
jar {
|
||||
dependsOn(":velocity-base:build", configurations.runtimeClasspath)
|
||||
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
from (configurations.runtimeClasspath.get().map { if (it.isDirectory) it else zipTree(it) })
|
||||
|
||||
archiveBaseName.set("floodgate-${project.name}")
|
||||
archiveVersion.set("")
|
||||
archiveClassifier.set("")
|
||||
|
||||
val velocityBaseJar = project.projects
|
||||
.velocityBase.dependencyProject
|
||||
.buildDir
|
||||
.resolve("libs")
|
||||
.resolve("floodgate-velocity-base.jar")
|
||||
|
||||
from(velocityBaseJar.parentFile) {
|
||||
include(velocityBaseJar.name)
|
||||
rename("floodgate-velocity-base.jar", "platform-base.jar")
|
||||
into("bundled/")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,28 +25,45 @@
|
||||
|
||||
package org.geysermc.floodgate.velocity;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Injector;
|
||||
import com.velocitypowered.api.event.Subscribe;
|
||||
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
|
||||
import com.velocitypowered.api.event.proxy.ProxyShutdownEvent;
|
||||
import java.nio.file.Paths;
|
||||
import org.geysermc.floodgate.isolation.library.LibraryManager;
|
||||
import org.geysermc.floodgate.isolation.loader.PlatformHolder;
|
||||
import org.geysermc.floodgate.isolation.loader.PlatformLoader;
|
||||
|
||||
public final class VelocityPlugin {
|
||||
private final VelocityPlatform platform;
|
||||
public final class IsolatedVelocityPlugin {
|
||||
private final PlatformHolder holder;
|
||||
|
||||
@Inject
|
||||
public VelocityPlugin(Injector guice) {
|
||||
platform = guice.getInstance(VelocityPlatform.class);
|
||||
platform.load();
|
||||
public IsolatedVelocityPlugin(Injector guice) {
|
||||
try {
|
||||
holder = PlatformLoader.loadDefault(getClass().getClassLoader(), Paths.get("./libs"));
|
||||
Injector child = guice.createChildInjector(new AbstractModule() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(LibraryManager.class).toInstance(holder.manager());
|
||||
}
|
||||
});
|
||||
|
||||
holder.platformInstance(child.getInstance(holder.platformClass()));
|
||||
holder.load();
|
||||
} catch (Exception exception) {
|
||||
throw new RuntimeException("Failed to load Floodgate", exception);
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onProxyInitialization(ProxyInitializeEvent event) {
|
||||
platform.enable();
|
||||
holder.enable();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onProxyShutdown(ProxyShutdownEvent event) {
|
||||
platform.disable();
|
||||
holder.disable();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
org.geysermc.floodgate.velocity.VelocityPlatform
|
||||
@@ -1 +1 @@
|
||||
{"id": "${id}", "name": "${name}", "version": "${version}", "description": "${description}", "url": "$url}", "authors": ["${author}"], "main": "org.geysermc.floodgate.velocity.VelocityPlugin"}
|
||||
{"id": "${id}", "name": "${name}", "version": "${version}", "description": "${description}", "url": "$url}", "authors": ["${author}"], "main": "org.geysermc.floodgate.velocity.IsolatedVelocityPlugin"}
|
||||
Reference in New Issue
Block a user