diff --git a/build-logic/src/main/kotlin/floodgate.database-conventions.gradle.kts b/build-logic/src/main/kotlin/floodgate.database-conventions.gradle.kts deleted file mode 100644 index 2012ba65..00000000 --- a/build-logic/src/main/kotlin/floodgate.database-conventions.gradle.kts +++ /dev/null @@ -1,9 +0,0 @@ -plugins { - id("floodgate.shadow-conventions") -} - -tasks { - shadowJar { - archiveBaseName.set(archiveBaseName.get() + "-database") - } -} \ No newline at end of file diff --git a/build-logic/src/main/kotlin/floodgate.dependency-hash.gradle.kts b/build-logic/src/main/kotlin/floodgate.dependency-hash.gradle.kts index 700e37ee..1678de26 100644 --- a/build-logic/src/main/kotlin/floodgate.dependency-hash.gradle.kts +++ b/build-logic/src/main/kotlin/floodgate.dependency-hash.gradle.kts @@ -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): String { val builder = StringBuilder() diff --git a/build-logic/src/main/kotlin/floodgate.shadow-conventions.gradle.kts b/build-logic/src/main/kotlin/floodgate.shadow-conventions.gradle.kts index 38cdbbcb..e99dfaf5 100644 --- a/build-logic/src/main/kotlin/floodgate.shadow-conventions.gradle.kts +++ b/build-logic/src/main/kotlin/floodgate.shadow-conventions.gradle.kts @@ -10,6 +10,7 @@ tasks { archiveClassifier.set("unshaded") from(project.rootProject.file("LICENSE")) } + val shadowJar = named("shadowJar") { archiveBaseName.set("floodgate-${project.name}") archiveVersion.set("") @@ -39,6 +40,7 @@ tasks { destinationDirectory.set(file(destinationDir)) } } + named("build") { dependsOn(shadowJar) } diff --git a/build.gradle.kts b/build.gradle.kts index c56da57b..3ca9c913 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -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") diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 6541274f..892e6850 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -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") diff --git a/core/src/main/java/org/geysermc/floodgate/core/FloodgatePlatform.java b/core/src/main/java/org/geysermc/floodgate/core/FloodgatePlatform.java index 2f246332..04ef2fc7 100644 --- a/core/src/main/java/org/geysermc/floodgate/core/FloodgatePlatform.java +++ b/core/src/main/java/org/geysermc/floodgate/core/FloodgatePlatform.java @@ -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()); } diff --git a/core/src/main/java/org/geysermc/floodgate/core/util/HttpClient.java b/core/src/main/java/org/geysermc/floodgate/core/util/HttpClient.java index 126086a6..39003c04 100644 --- a/core/src/main/java/org/geysermc/floodgate/core/util/HttpClient.java +++ b/core/src/main/java/org/geysermc/floodgate/core/util/HttpClient.java @@ -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 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 HttpResponse readResponse(HttpURLConnection connection, Class clazz) { InputStreamReader streamReader = createReader(connection); diff --git a/core/src/main/java/org/geysermc/floodgate/core/util/Utils.java b/core/src/main/java/org/geysermc/floodgate/core/util/Utils.java index 0d8dcf34..d19be8e0 100644 --- a/core/src/main/java/org/geysermc/floodgate/core/util/Utils.java +++ b/core/src/main/java/org/geysermc/floodgate/core/util/Utils.java @@ -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); - } } diff --git a/core/src/main/resources/dependencyInfo.txt b/core/src/main/resources/dependencyInfo.txt index 62a7f400..4c0079d2 100644 --- a/core/src/main/resources/dependencyInfo.txt +++ b/core/src/main/resources/dependencyInfo.txt @@ -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= diff --git a/database/build.gradle.kts b/database/build.gradle.kts new file mode 100644 index 00000000..865bd515 --- /dev/null +++ b/database/build.gradle.kts @@ -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") +} diff --git a/database/mongo/build.gradle.kts b/database/mongo/build.gradle.kts deleted file mode 100644 index e20d1f12..00000000 --- a/database/mongo/build.gradle.kts +++ /dev/null @@ -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") \ No newline at end of file diff --git a/database/mongo/src/main/java/org/geysermc/floodgate/database/MongoDbDatabase.java b/database/mongo/src/main/java/org/geysermc/floodgate/database/MongoDbDatabase.java deleted file mode 100644 index dcd8164b..00000000 --- a/database/mongo/src/main/java/org/geysermc/floodgate/database/MongoDbDatabase.java +++ /dev/null @@ -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 linkedPlayer; - private MongoCollection 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 getLinkedPlayer(@NonNull UUID bedrockId) { - return CompletableFuture.supplyAsync(() -> { - try { - Bson filter = Filters.eq("bedrockId", uuidToBytes(bedrockId)); - - try (MongoCursor 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 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 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 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 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 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 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 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); - } - -} diff --git a/database/mongo/src/main/resources/init.json b/database/mongo/src/main/resources/init.json deleted file mode 100644 index f161937f..00000000 --- a/database/mongo/src/main/resources/init.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "mainClass": "org.geysermc.floodgate.database.MongoDbDatabase", - "config": "mongo.yml" -} \ No newline at end of file diff --git a/database/mongo/src/main/resources/mongo.yml b/database/mongo/src/main/resources/mongo.yml deleted file mode 100644 index 719af986..00000000 --- a/database/mongo/src/main/resources/mongo.yml +++ /dev/null @@ -1,5 +0,0 @@ -hostname: "localhost" -database: "floodgate" -username: "floodgate" -password: "" -mongouri: "" \ No newline at end of file diff --git a/database/mysql/.editorconfig b/database/mysql/.editorconfig deleted file mode 100644 index 1f0feacf..00000000 --- a/database/mysql/.editorconfig +++ /dev/null @@ -1,4 +0,0 @@ -[*] -indent_size = 2 -tab_width = 2 -ij_continuation_indent_size = 4 \ No newline at end of file diff --git a/database/mysql/build.gradle.kts b/database/mysql/build.gradle.kts deleted file mode 100644 index 3b6f25a3..00000000 --- a/database/mysql/build.gradle.kts +++ /dev/null @@ -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") diff --git a/database/mysql/src/main/java/org/geysermc/floodgate/database/MysqlDatabase.java b/database/mysql/src/main/java/org/geysermc/floodgate/database/MysqlDatabase.java deleted file mode 100644 index 66a8499e..00000000 --- a/database/mysql/src/main/java/org/geysermc/floodgate/database/MysqlDatabase.java +++ /dev/null @@ -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 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 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 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 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 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 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()); - } -} diff --git a/database/mysql/src/main/resources/init.json b/database/mysql/src/main/resources/init.json deleted file mode 100644 index 3eb3da1b..00000000 --- a/database/mysql/src/main/resources/init.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "mainClass": "org.geysermc.floodgate.database.MysqlDatabase", - "config": "mysql.yml" -} \ No newline at end of file diff --git a/database/mysql/src/main/resources/mysql.yml b/database/mysql/src/main/resources/mysql.yml deleted file mode 100644 index 134ba060..00000000 --- a/database/mysql/src/main/resources/mysql.yml +++ /dev/null @@ -1,4 +0,0 @@ -hostname: "localhost" -database: "floodgate" -username: "floodgate" -password: "" diff --git a/database/sqlite/build.gradle.kts b/database/sqlite/build.gradle.kts deleted file mode 100644 index 2e7882d8..00000000 --- a/database/sqlite/build.gradle.kts +++ /dev/null @@ -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" diff --git a/database/sqlite/src/main/java/org/geysermc/floodgate/database/SqliteDatabase.java b/database/sqlite/src/main/java/org/geysermc/floodgate/database/SqliteDatabase.java deleted file mode 100644 index 505b1204..00000000 --- a/database/sqlite/src/main/java/org/geysermc/floodgate/database/SqliteDatabase.java +++ /dev/null @@ -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 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 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 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 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 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 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 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()); - } -} diff --git a/database/sqlite/src/main/resources/init.json b/database/sqlite/src/main/resources/init.json deleted file mode 100644 index 8d17bcbc..00000000 --- a/database/sqlite/src/main/resources/init.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "mainClass": "org.geysermc.floodgate.database.SqliteDatabase" -} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index e4b30c65..7b5c2e70 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,4 +3,4 @@ org.gradle.caching=true org.gradle.parallel=true version=2.2.2-SNAPSHOT -micronautVersion=3.8.7 \ No newline at end of file +micronautVersion=3.9.0 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7454180f..ccebba77 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 070cb702..bdc9a83b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -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 diff --git a/gradlew b/gradlew index c53aefaa..79a61d42 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 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 «$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». +# * 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. diff --git a/gradlew.bat b/gradlew.bat index 107acd32..93e3f59f 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -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 diff --git a/isolation/build.gradle.kts b/isolation/build.gradle.kts new file mode 100644 index 00000000..8e431724 --- /dev/null +++ b/isolation/build.gradle.kts @@ -0,0 +1,7 @@ +plugins { + id("floodgate.base-conventions") +} + +dependencies { + api(projects.api) +} \ No newline at end of file diff --git a/core/src/main/java/org/geysermc/floodgate/core/library/Library.java b/isolation/src/main/java/org/geysermc/floodgate/isolation/library/Library.java similarity index 77% rename from core/src/main/java/org/geysermc/floodgate/core/library/Library.java rename to isolation/src/main/java/org/geysermc/floodgate/isolation/library/Library.java index 1c613d15..8c610ad7 100644 --- a/core/src/main/java/org/geysermc/floodgate/core/library/Library.java +++ b/isolation/src/main/java/org/geysermc/floodgate/isolation/library/Library.java @@ -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); } } } diff --git a/core/src/main/java/org/geysermc/floodgate/core/library/LibraryManager.java b/isolation/src/main/java/org/geysermc/floodgate/isolation/library/LibraryManager.java similarity index 80% rename from core/src/main/java/org/geysermc/floodgate/core/library/LibraryManager.java rename to isolation/src/main/java/org/geysermc/floodgate/isolation/library/LibraryManager.java index 7c0245d4..ea6e6dc2 100644 --- a/core/src/main/java/org/geysermc/floodgate/core/library/LibraryManager.java +++ b/isolation/src/main/java/org/geysermc/floodgate/isolation/library/LibraryManager.java @@ -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 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; + } } diff --git a/core/src/main/java/org/geysermc/floodgate/core/library/Repository.java b/isolation/src/main/java/org/geysermc/floodgate/isolation/library/Repository.java similarity index 56% rename from core/src/main/java/org/geysermc/floodgate/core/library/Repository.java rename to isolation/src/main/java/org/geysermc/floodgate/isolation/library/Repository.java index f9aa2bbb..18450be9 100644 --- a/core/src/main/java/org/geysermc/floodgate/core/library/Repository.java +++ b/isolation/src/main/java/org/geysermc/floodgate/isolation/library/Repository.java @@ -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 response = new HttpClient().getRawData(baseUrl + library.path()); + HttpResponse 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) { diff --git a/core/src/main/java/org/geysermc/floodgate/core/library/classloader/LibraryClassLoader.java b/isolation/src/main/java/org/geysermc/floodgate/isolation/library/classloader/LibraryClassLoader.java similarity index 92% rename from core/src/main/java/org/geysermc/floodgate/core/library/classloader/LibraryClassLoader.java rename to isolation/src/main/java/org/geysermc/floodgate/isolation/library/classloader/LibraryClassLoader.java index 3a4df109..eea69c95 100644 --- a/core/src/main/java/org/geysermc/floodgate/core/library/classloader/LibraryClassLoader.java +++ b/isolation/src/main/java/org/geysermc/floodgate/isolation/library/classloader/LibraryClassLoader.java @@ -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); } } diff --git a/core/src/main/java/org/geysermc/floodgate/core/library/info/DependencyInfo.java b/isolation/src/main/java/org/geysermc/floodgate/isolation/library/info/DependencyInfo.java similarity index 96% rename from core/src/main/java/org/geysermc/floodgate/core/library/info/DependencyInfo.java rename to isolation/src/main/java/org/geysermc/floodgate/isolation/library/info/DependencyInfo.java index d13869d8..1b539338 100644 --- a/core/src/main/java/org/geysermc/floodgate/core/library/info/DependencyInfo.java +++ b/isolation/src/main/java/org/geysermc/floodgate/isolation/library/info/DependencyInfo.java @@ -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; diff --git a/core/src/main/java/org/geysermc/floodgate/core/library/info/DependencyInfoLoader.java b/isolation/src/main/java/org/geysermc/floodgate/isolation/library/info/DependencyInfoLoader.java similarity index 97% rename from core/src/main/java/org/geysermc/floodgate/core/library/info/DependencyInfoLoader.java rename to isolation/src/main/java/org/geysermc/floodgate/isolation/library/info/DependencyInfoLoader.java index 537d89b3..76143c8d 100644 --- a/core/src/main/java/org/geysermc/floodgate/core/library/info/DependencyInfoLoader.java +++ b/isolation/src/main/java/org/geysermc/floodgate/isolation/library/info/DependencyInfoLoader.java @@ -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; diff --git a/database/mysql/src/main/java/org/geysermc/floodgate/database/config/MysqlConfig.java b/isolation/src/main/java/org/geysermc/floodgate/isolation/loader/LoaderUtil.java similarity index 50% rename from database/mysql/src/main/java/org/geysermc/floodgate/database/config/MysqlConfig.java rename to isolation/src/main/java/org/geysermc/floodgate/isolation/loader/LoaderUtil.java index 0af667ac..820be20a 100644 --- a/database/mysql/src/main/java/org/geysermc/floodgate/database/config/MysqlConfig.java +++ b/isolation/src/main/java/org/geysermc/floodgate/isolation/loader/LoaderUtil.java @@ -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); + } + } } diff --git a/isolation/src/main/java/org/geysermc/floodgate/isolation/loader/PlatformHolder.java b/isolation/src/main/java/org/geysermc/floodgate/isolation/loader/PlatformHolder.java new file mode 100644 index 00000000..e839a229 --- /dev/null +++ b/isolation/src/main/java/org/geysermc/floodgate/isolation/loader/PlatformHolder.java @@ -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; + } +} diff --git a/isolation/src/main/java/org/geysermc/floodgate/isolation/loader/PlatformLoader.java b/isolation/src/main/java/org/geysermc/floodgate/isolation/loader/PlatformLoader.java new file mode 100644 index 00000000..133930c6 --- /dev/null +++ b/isolation/src/main/java/org/geysermc/floodgate/isolation/loader/PlatformLoader.java @@ -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)); + } +} diff --git a/isolation/src/main/java/org/geysermc/floodgate/isolation/util/ChildFirstClassLoader.java b/isolation/src/main/java/org/geysermc/floodgate/isolation/util/ChildFirstClassLoader.java new file mode 100644 index 00000000..115d4e68 --- /dev/null +++ b/isolation/src/main/java/org/geysermc/floodgate/isolation/util/ChildFirstClassLoader.java @@ -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 getResources(String name) throws IOException { + List allResources = new LinkedList<>(); + + Enumeration systemResources = systemClassLoader.getResources(name); + if (systemResources != null) { + while (systemResources.hasMoreElements()) { + allResources.add(systemResources.nextElement()); + } + } + + Enumeration thisResources = findResources(name); + if (thisResources != null) { + while (thisResources.hasMoreElements()) { + allResources.add(thisResources.nextElement()); + } + } + + Enumeration parentResources = getParent().getResources(name); + if (parentResources != null) { + while (parentResources.hasMoreElements()) { + allResources.add(parentResources.nextElement()); + } + } + + return new Enumeration<>() { + final Iterator 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; + } +} diff --git a/isolation/src/main/java/org/geysermc/floodgate/isolation/util/HttpUtil.java b/isolation/src/main/java/org/geysermc/floodgate/isolation/util/HttpUtil.java new file mode 100644 index 00000000..e3dad16c --- /dev/null +++ b/isolation/src/main/java/org/geysermc/floodgate/isolation/util/HttpUtil.java @@ -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 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 { + 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; + } + } +} diff --git a/database/mongo/src/main/java/org/geysermc/floodgate/database/config/MongoConfig.java b/isolation/src/main/java/org/geysermc/floodgate/isolation/util/StreamUtil.java similarity index 63% rename from database/mongo/src/main/java/org/geysermc/floodgate/database/config/MongoConfig.java rename to isolation/src/main/java/org/geysermc/floodgate/isolation/util/StreamUtil.java index cec6a324..985b1ada 100644 --- a/database/mongo/src/main/java/org/geysermc/floodgate/database/config/MongoConfig.java +++ b/isolation/src/main/java/org/geysermc/floodgate/isolation/util/StreamUtil.java @@ -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(); + } + } } diff --git a/isolation/src/main/java/org/geysermc/floodgate/isolation/util/UrlUtil.java b/isolation/src/main/java/org/geysermc/floodgate/isolation/util/UrlUtil.java new file mode 100644 index 00000000..3a41020a --- /dev/null +++ b/isolation/src/main/java/org/geysermc/floodgate/isolation/util/UrlUtil.java @@ -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 readAllLines(URL url) { + try { + var connection = url.openConnection(); + var reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); + + var list = new ArrayList(); + 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); + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 25701fbe..1b67a026 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -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") + } +} diff --git a/velocity/build.gradle.kts b/velocity/base/build.gradle.kts similarity index 100% rename from velocity/build.gradle.kts rename to velocity/base/build.gradle.kts diff --git a/velocity/src/main/java/org/geysermc/floodgate/velocity/VelocityPlatform.java b/velocity/base/src/main/java/org/geysermc/floodgate/velocity/VelocityPlatform.java similarity index 94% rename from velocity/src/main/java/org/geysermc/floodgate/velocity/VelocityPlatform.java rename to velocity/base/src/main/java/org/geysermc/floodgate/velocity/VelocityPlatform.java index 05d719fe..09b0dbeb 100644 --- a/velocity/src/main/java/org/geysermc/floodgate/velocity/VelocityPlatform.java +++ b/velocity/base/src/main/java/org/geysermc/floodgate/velocity/VelocityPlatform.java @@ -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"); } diff --git a/velocity/src/main/java/org/geysermc/floodgate/velocity/addon/data/VelocityDataAddon.java b/velocity/base/src/main/java/org/geysermc/floodgate/velocity/addon/data/VelocityDataAddon.java similarity index 100% rename from velocity/src/main/java/org/geysermc/floodgate/velocity/addon/data/VelocityDataAddon.java rename to velocity/base/src/main/java/org/geysermc/floodgate/velocity/addon/data/VelocityDataAddon.java diff --git a/velocity/src/main/java/org/geysermc/floodgate/velocity/addon/data/VelocityProxyDataHandler.java b/velocity/base/src/main/java/org/geysermc/floodgate/velocity/addon/data/VelocityProxyDataHandler.java similarity index 100% rename from velocity/src/main/java/org/geysermc/floodgate/velocity/addon/data/VelocityProxyDataHandler.java rename to velocity/base/src/main/java/org/geysermc/floodgate/velocity/addon/data/VelocityProxyDataHandler.java diff --git a/velocity/src/main/java/org/geysermc/floodgate/velocity/addon/data/VelocityServerDataHandler.java b/velocity/base/src/main/java/org/geysermc/floodgate/velocity/addon/data/VelocityServerDataHandler.java similarity index 100% rename from velocity/src/main/java/org/geysermc/floodgate/velocity/addon/data/VelocityServerDataHandler.java rename to velocity/base/src/main/java/org/geysermc/floodgate/velocity/addon/data/VelocityServerDataHandler.java diff --git a/velocity/src/main/java/org/geysermc/floodgate/velocity/inject/VelocityInjector.java b/velocity/base/src/main/java/org/geysermc/floodgate/velocity/inject/VelocityInjector.java similarity index 100% rename from velocity/src/main/java/org/geysermc/floodgate/velocity/inject/VelocityInjector.java rename to velocity/base/src/main/java/org/geysermc/floodgate/velocity/inject/VelocityInjector.java diff --git a/velocity/src/main/java/org/geysermc/floodgate/velocity/listener/VelocityListener.java b/velocity/base/src/main/java/org/geysermc/floodgate/velocity/listener/VelocityListener.java similarity index 100% rename from velocity/src/main/java/org/geysermc/floodgate/velocity/listener/VelocityListener.java rename to velocity/base/src/main/java/org/geysermc/floodgate/velocity/listener/VelocityListener.java diff --git a/velocity/src/main/java/org/geysermc/floodgate/velocity/listener/VelocityListenerRegistration.java b/velocity/base/src/main/java/org/geysermc/floodgate/velocity/listener/VelocityListenerRegistration.java similarity index 100% rename from velocity/src/main/java/org/geysermc/floodgate/velocity/listener/VelocityListenerRegistration.java rename to velocity/base/src/main/java/org/geysermc/floodgate/velocity/listener/VelocityListenerRegistration.java diff --git a/velocity/src/main/java/org/geysermc/floodgate/velocity/logger/Slf4jFloodgateLogger.java b/velocity/base/src/main/java/org/geysermc/floodgate/velocity/logger/Slf4jFloodgateLogger.java similarity index 100% rename from velocity/src/main/java/org/geysermc/floodgate/velocity/logger/Slf4jFloodgateLogger.java rename to velocity/base/src/main/java/org/geysermc/floodgate/velocity/logger/Slf4jFloodgateLogger.java diff --git a/velocity/src/main/java/org/geysermc/floodgate/velocity/module/VelocityPlatformModule.java b/velocity/base/src/main/java/org/geysermc/floodgate/velocity/module/VelocityPlatformModule.java similarity index 100% rename from velocity/src/main/java/org/geysermc/floodgate/velocity/module/VelocityPlatformModule.java rename to velocity/base/src/main/java/org/geysermc/floodgate/velocity/module/VelocityPlatformModule.java diff --git a/velocity/src/main/java/org/geysermc/floodgate/velocity/pluginmessage/VelocityPluginMessageRegistration.java b/velocity/base/src/main/java/org/geysermc/floodgate/velocity/pluginmessage/VelocityPluginMessageRegistration.java similarity index 100% rename from velocity/src/main/java/org/geysermc/floodgate/velocity/pluginmessage/VelocityPluginMessageRegistration.java rename to velocity/base/src/main/java/org/geysermc/floodgate/velocity/pluginmessage/VelocityPluginMessageRegistration.java diff --git a/velocity/src/main/java/org/geysermc/floodgate/velocity/pluginmessage/VelocityPluginMessageUtils.java b/velocity/base/src/main/java/org/geysermc/floodgate/velocity/pluginmessage/VelocityPluginMessageUtils.java similarity index 100% rename from velocity/src/main/java/org/geysermc/floodgate/velocity/pluginmessage/VelocityPluginMessageUtils.java rename to velocity/base/src/main/java/org/geysermc/floodgate/velocity/pluginmessage/VelocityPluginMessageUtils.java diff --git a/velocity/src/main/java/org/geysermc/floodgate/velocity/util/VelocityCommandUtil.java b/velocity/base/src/main/java/org/geysermc/floodgate/velocity/util/VelocityCommandUtil.java similarity index 100% rename from velocity/src/main/java/org/geysermc/floodgate/velocity/util/VelocityCommandUtil.java rename to velocity/base/src/main/java/org/geysermc/floodgate/velocity/util/VelocityCommandUtil.java diff --git a/velocity/src/main/java/org/geysermc/floodgate/velocity/util/VelocityPlatformUtils.java b/velocity/base/src/main/java/org/geysermc/floodgate/velocity/util/VelocityPlatformUtils.java similarity index 100% rename from velocity/src/main/java/org/geysermc/floodgate/velocity/util/VelocityPlatformUtils.java rename to velocity/base/src/main/java/org/geysermc/floodgate/velocity/util/VelocityPlatformUtils.java diff --git a/velocity/src/main/java/org/geysermc/floodgate/velocity/util/VelocitySkinApplier.java b/velocity/base/src/main/java/org/geysermc/floodgate/velocity/util/VelocitySkinApplier.java similarity index 100% rename from velocity/src/main/java/org/geysermc/floodgate/velocity/util/VelocitySkinApplier.java rename to velocity/base/src/main/java/org/geysermc/floodgate/velocity/util/VelocitySkinApplier.java diff --git a/velocity/isolated/build.gradle.kts b/velocity/isolated/build.gradle.kts new file mode 100644 index 00000000..4ac9b268 --- /dev/null +++ b/velocity/isolated/build.gradle.kts @@ -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/") + } + } +} \ No newline at end of file diff --git a/velocity/src/main/java/org/geysermc/floodgate/velocity/VelocityPlugin.java b/velocity/isolated/src/main/java/org/geysermc/floodgate/velocity/IsolatedVelocityPlugin.java similarity index 61% rename from velocity/src/main/java/org/geysermc/floodgate/velocity/VelocityPlugin.java rename to velocity/isolated/src/main/java/org/geysermc/floodgate/velocity/IsolatedVelocityPlugin.java index 1ed861d5..0b3b3ee9 100644 --- a/velocity/src/main/java/org/geysermc/floodgate/velocity/VelocityPlugin.java +++ b/velocity/isolated/src/main/java/org/geysermc/floodgate/velocity/IsolatedVelocityPlugin.java @@ -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(); } } diff --git a/velocity/isolated/src/main/resources/org.geysermc.mainClass b/velocity/isolated/src/main/resources/org.geysermc.mainClass new file mode 100644 index 00000000..f1eba777 --- /dev/null +++ b/velocity/isolated/src/main/resources/org.geysermc.mainClass @@ -0,0 +1 @@ +org.geysermc.floodgate.velocity.VelocityPlatform \ No newline at end of file diff --git a/velocity/src/main/resources/velocity-plugin.json b/velocity/isolated/src/main/resources/velocity-plugin.json similarity index 64% rename from velocity/src/main/resources/velocity-plugin.json rename to velocity/isolated/src/main/resources/velocity-plugin.json index 8f4d70b0..48e339c3 100644 --- a/velocity/src/main/resources/velocity-plugin.json +++ b/velocity/isolated/src/main/resources/velocity-plugin.json @@ -1 +1 @@ -{"id": "${id}", "name": "${name}", "version": "${version}", "description": "${description}", "url": "$url}", "authors": ["${author}"], "main": "org.geysermc.floodgate.velocity.VelocityPlugin"} \ No newline at end of file +{"id": "${id}", "name": "${name}", "version": "${version}", "description": "${description}", "url": "$url}", "authors": ["${author}"], "main": "org.geysermc.floodgate.velocity.IsolatedVelocityPlugin"} \ No newline at end of file