diff --git a/.github/workflows/pullrequest.yml b/.github/workflows/pullrequest.yml index 37e8877a..080b54fd 100644 --- a/.github/workflows/pullrequest.yml +++ b/.github/workflows/pullrequest.yml @@ -4,39 +4,39 @@ on: [ pull_request ] jobs: build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - - name: Set up JDK 1.8 - uses: actions/setup-java@v2 + - name: Set up JDK + uses: actions/setup-java@v3 with: - distribution: 'temurin' - java-version: '8' - cache: 'gradle' + distribution: 'corretto' + java-version: '17' + + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 - - name: Build with Maven + - name: Build with Gradle run: ./gradlew build - - name: Archive artifacts (Floodgate Bungee) + - name: Archive artifacts (Connect Bungee) uses: actions/upload-artifact@v2 if: success() with: - name: Floodgate Bungee + name: Connect Bungee path: bungee/build/libs/connect-bungee.jar - - name: Archive artifacts (Floodgate Spigot) + - name: Archive artifacts (Connect Spigot) uses: actions/upload-artifact@v2 if: success() with: - name: Floodgate Spigot + name: Connect Spigot path: spigot/build/libs/connect-spigot.jar - - name: Archive artifacts (Floodgate Velocity) + - name: Archive artifacts (Connect Velocity) uses: actions/upload-artifact@v2 if: success() with: - name: Floodgate Velocity + name: Connect Velocity path: velocity/build/libs/connect-velocity.jar \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ccc4517a..56fda2c7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,16 +18,16 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Set up JDK 1.8 + - name: Set up JDK uses: actions/setup-java@v3 with: - distribution: 'temurin' - java-version: '8' + distribution: 'corretto' + java-version: '17' - name: Setup Gradle uses: gradle/gradle-build-action@v2 - - name: Build with Maven + - name: Build with Gradle run: ./gradlew build - name: Rename JARs for Latest Pre-Release diff --git a/ap/build.gradle.kts b/ap/build.gradle.kts new file mode 100644 index 00000000..e69de29b diff --git a/spigot/src/main/java/com/minekube/connect/SpigotPlatform.java b/ap/src/main/java/com/minekube/connect/ap/AutoBindProcessor.java similarity index 56% rename from spigot/src/main/java/com/minekube/connect/SpigotPlatform.java rename to ap/src/main/java/com/minekube/connect/ap/AutoBindProcessor.java index 6af100a1..8d8e94c9 100644 --- a/spigot/src/main/java/com/minekube/connect/SpigotPlatform.java +++ b/ap/src/main/java/com/minekube/connect/ap/AutoBindProcessor.java @@ -23,33 +23,16 @@ * @link https://github.com/GeyserMC/Floodgate */ -package com.minekube.connect; +package com.minekube.connect.ap; -import com.google.inject.Inject; -import com.google.inject.Injector; -import com.google.inject.Module; -import com.minekube.connect.api.ConnectApi; -import com.minekube.connect.api.inject.PlatformInjector; -import com.minekube.connect.api.logger.ConnectLogger; -import org.bukkit.Bukkit; -import org.bukkit.plugin.java.JavaPlugin; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.annotation.processing.SupportedSourceVersion; +import javax.lang.model.SourceVersion; -public final class SpigotPlatform extends ConnectPlatform { - @Inject private JavaPlugin plugin; - - @Inject - public SpigotPlatform(ConnectApi api, PlatformInjector platformInjector, - ConnectLogger logger, Injector injector) { - super(api, platformInjector, logger, injector); - } - - @Override - public boolean enable(Module... postInitializeModules) { - boolean success = super.enable(postInitializeModules); - if (!success) { - Bukkit.getPluginManager().disablePlugin(plugin); - return false; - } - return true; +@SupportedAnnotationTypes("*") +@SupportedSourceVersion(SourceVersion.RELEASE_8) +public class AutoBindProcessor extends ClassProcessor { + public AutoBindProcessor() { + super("com.minekube.connect.util.AutoBind"); } -} +} \ No newline at end of file diff --git a/ap/src/main/java/com/minekube/connect/ap/ClassProcessor.java b/ap/src/main/java/com/minekube/connect/ap/ClassProcessor.java new file mode 100644 index 00000000..bf7c450b --- /dev/null +++ b/ap/src/main/java/com/minekube/connect/ap/ClassProcessor.java @@ -0,0 +1,221 @@ +/* + * 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 com.minekube.connect.ap; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.ProcessingEnvironment; +import javax.annotation.processing.RoundEnvironment; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.TypeElement; +import javax.tools.Diagnostic.Kind; +import javax.tools.FileObject; +import javax.tools.StandardLocation; + +/* + * Copied from Geyser + */ +public class ClassProcessor extends AbstractProcessor { + private final String annotationClassName; + + private Path outputPath; + + private final Set locations = new HashSet<>(); + + public ClassProcessor(String annotationClassName) { + this.annotationClassName = annotationClassName; + } + + @Override + public synchronized void init(ProcessingEnvironment processingEnv) { + super.init(processingEnv); + + processingEnv.getMessager().printMessage( + Kind.NOTE, "Initializing processor " + annotationClassName + ); + + String outputFile = processingEnv.getOptions().get("metadataOutputFile"); + if (outputFile != null && !outputFile.isEmpty()) { + outputPath = Paths.get(outputFile); + } + } + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + if (roundEnv.processingOver()) { + if (!roundEnv.errorRaised()) { + complete(); + } + + return false; + } + + if (!contains(annotations, annotationClassName)) { + return false; + } + + for (Element element : roundEnv.getRootElements()) { + if (element.getKind() != ElementKind.CLASS) { + continue; + } + + if (!contains(element.getAnnotationMirrors(), annotationClassName)) { + continue; + } + + TypeElement typeElement = (TypeElement) element; + locations.add(typeElement.getQualifiedName().toString()); + } + return false; + } + + public boolean contains(Collection elements, String className) { + if (elements.isEmpty()) { + return false; + } + + for (TypeElement element : elements) { + if (element.getQualifiedName().contentEquals(className)) { + return true; + } + } + + return false; + } + + public boolean contains(List elements, String className) { + if (elements.isEmpty()) { + return false; + } + + for (AnnotationMirror element : elements) { + if (element.getAnnotationType().toString().equals(className)) { + return true; + } + } + + return false; + } + + public void complete() { + // Read existing annotation list and verify each class still has this annotation + try (BufferedReader reader = createReader()) { + if (reader != null) { + reader.lines().forEach(canonicalName -> { + if (!locations.contains(canonicalName)) { + + TypeElement element = + processingEnv.getElementUtils().getTypeElement(canonicalName); + + if (element != null && element.getKind() == ElementKind.CLASS && + contains(element.getAnnotationMirrors(), annotationClassName)) { + locations.add(canonicalName); + } + } + }); + } + } catch (IOException e) { + e.printStackTrace(); + } + + if (!locations.isEmpty()) { + try (BufferedWriter writer = createWriter()) { + for (String location : locations) { + writer.write(location); + writer.newLine(); + } + } catch (IOException ex) { + ex.printStackTrace(); + } + } else { + processingEnv.getMessager().printMessage(Kind.NOTE, + "Did not find any classes annotated with " + annotationClassName + ); + } + + processingEnv.getMessager().printMessage( + Kind.NOTE, "Completed processing for " + annotationClassName + ); + } + + private BufferedReader createReader() throws IOException { + if (outputPath != null) { + processingEnv.getMessager().printMessage(Kind.NOTE, + "Reading existing " + annotationClassName + " list from " + outputPath + ); + + return Files.newBufferedReader(outputPath); + } + + FileObject obj = processingEnv.getFiler().getResource( + StandardLocation.CLASS_OUTPUT, "", annotationClassName + ); + + if (obj != null) { + processingEnv.getMessager().printMessage( + Kind.NOTE, + "Reading existing " + annotationClassName + " list from " + obj.toUri() + ); + + try { + return new BufferedReader(obj.openReader(false)); + } catch (NoSuchFileException ignored) {} + } + return null; + } + + private BufferedWriter createWriter() throws IOException { + if (outputPath != null) { + processingEnv.getMessager().printMessage( + Kind.NOTE, "Writing " + annotationClassName + " to " + outputPath + ); + + return Files.newBufferedWriter(outputPath); + } + + FileObject obj = processingEnv.getFiler().createResource( + StandardLocation.CLASS_OUTPUT, "", annotationClassName + ); + + processingEnv.getMessager().printMessage( + Kind.NOTE, "Writing " + annotationClassName + " to " + obj.toUri() + ); + + return new BufferedWriter(obj.openWriter()); + } +} \ No newline at end of file diff --git a/ap/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/ap/src/main/resources/META-INF/services/javax.annotation.processing.Processor new file mode 100644 index 00000000..93eea630 --- /dev/null +++ b/ap/src/main/resources/META-INF/services/javax.annotation.processing.Processor @@ -0,0 +1 @@ +com.minekube.connect.ap.AutoBindProcessor \ No newline at end of file diff --git a/api/build.gradle.kts b/api/build.gradle.kts index 7349a879..60fc527c 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -1,5 +1,13 @@ +plugins { + idea // used to let Intellij recognize protobuf generated sources +} + dependencies { api("com.google.code.gson", "gson", Versions.gsonVersion) compileOnly("io.netty", "netty-transport", Versions.nettyVersion) + api("build.buf", "connect-kotlin-okhttp", Versions.connectKotlinVersion) + api("build.buf.gen", "minekube_connect_grpc_java", Versions.minekube_connect_grpc_javaVersion) + api("build.buf.gen", "minekube_connect_bufbuild_connect-kotlin", Versions.minekube_connect_bufbuild_connectKotlinVersion) + api("build.buf.gen", "minekube_connect_protocolbuffers_java", Versions.minekube_connect_protocolbuffers_javaVersion) } diff --git a/api/src/main/java/com/minekube/connect/api/Clients.java b/api/src/main/java/com/minekube/connect/api/Clients.java new file mode 100644 index 00000000..f27c5b65 --- /dev/null +++ b/api/src/main/java/com/minekube/connect/api/Clients.java @@ -0,0 +1,12 @@ +package com.minekube.connect.api; + + +import build.buf.gen.minekube.connect.v1alpha1.ConnectServiceGrpc.ConnectServiceBlockingStub; +import build.buf.gen.minekube.connect.v1alpha1.ConnectServiceGrpc.ConnectServiceFutureStub; +import build.buf.gen.minekube.connect.v1alpha1.ConnectServiceGrpc.ConnectServiceStub; + +public interface Clients { + ConnectServiceBlockingStub getConnectServiceBlockingStub(); + ConnectServiceFutureStub getConnectServiceFutureStub(); + ConnectServiceStub getConnectServiceStub(); +} \ No newline at end of file diff --git a/api/src/main/java/com/minekube/connect/api/ConnectApi.java b/api/src/main/java/com/minekube/connect/api/ConnectApi.java index c29c3171..a42f451a 100644 --- a/api/src/main/java/com/minekube/connect/api/ConnectApi.java +++ b/api/src/main/java/com/minekube/connect/api/ConnectApi.java @@ -62,4 +62,13 @@ static ConnectApi getInstance() { * @return ConnectPlayer if the given uuid is a player tunneled by Connect */ ConnectPlayer getPlayer(UUID uuid); + + /** + * Get authenticated Connect API Services clients ready to use. + * + * @return Clients + */ + default Clients getClients() { + return InstanceHolder.getClients(); + } } diff --git a/api/src/main/java/com/minekube/connect/api/InstanceHolder.java b/api/src/main/java/com/minekube/connect/api/InstanceHolder.java index f5c08550..326db267 100644 --- a/api/src/main/java/com/minekube/connect/api/InstanceHolder.java +++ b/api/src/main/java/com/minekube/connect/api/InstanceHolder.java @@ -35,12 +35,14 @@ public final class InstanceHolder { @Getter private static PlatformInjector injector; @Getter private static PacketHandlers packetHandlers; + @Getter private static Clients clients; private static UUID storedKey; public static boolean set( ConnectApi connectApi, PlatformInjector platformInjector, PacketHandlers packetHandlers, + Clients clients, UUID key) { if (storedKey != null) { @@ -51,14 +53,11 @@ public static boolean set( storedKey = key; } - api = connectApi; - injector = platformInjector; + InstanceHolder.api = connectApi; + InstanceHolder.injector = platformInjector; InstanceHolder.packetHandlers = packetHandlers; + InstanceHolder.clients = clients; return true; } - @SuppressWarnings("unchecked") - public static T castApi(Class cast) { - return (T) api; - } } diff --git a/api/src/main/java/com/minekube/connect/api/inject/PlatformInjector.java b/api/src/main/java/com/minekube/connect/api/inject/PlatformInjector.java index 40a50553..30d5fca4 100644 --- a/api/src/main/java/com/minekube/connect/api/inject/PlatformInjector.java +++ b/api/src/main/java/com/minekube/connect/api/inject/PlatformInjector.java @@ -43,10 +43,9 @@ public interface PlatformInjector { /** * Injects the server connection. * - * @return true if the connection has successfully been injected * @throws Exception if something went wrong while injecting the server connection */ - boolean inject() throws Exception; + void inject() throws Exception; /** * If the server connection is currently injected. diff --git a/api/src/main/java/com/minekube/connect/api/logger/ConnectLogger.java b/api/src/main/java/com/minekube/connect/api/logger/ConnectLogger.java index b0dd3037..9d91a8f8 100644 --- a/api/src/main/java/com/minekube/connect/api/logger/ConnectLogger.java +++ b/api/src/main/java/com/minekube/connect/api/logger/ConnectLogger.java @@ -80,18 +80,7 @@ public interface ConnectLogger { void trace(String message, Object... args); /** - * Enables debug mode for the logger. - */ - void enableDebug(); - - /** - * Disables debug mode for the logger. Debug messages can still be sent after running - * this method, but they will be hidden from the console. - */ - void disableDebug(); - - /** - * Returns if debugging is enabled + * Returns true if debugging is enabled */ boolean isDebug(); } diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts index 64ac14db..314e62d5 100644 --- a/build-logic/build.gradle.kts +++ b/build-logic/build.gradle.kts @@ -9,13 +9,19 @@ repositories { } dependencies { - implementation("net.kyori", "indra-common", "2.0.6") - implementation("org.jfrog.buildinfo", "build-info-extractor-gradle", "4.26.1") + implementation("net.kyori", "indra-common", "3.0.1") + implementation("net.kyori", "indra-git", "3.0.1") implementation("gradle.plugin.com.github.johnrengelman", "shadow", "7.1.1") + implementation("gradle.plugin.org.jetbrains.gradle.plugin.idea-ext", "gradle-idea-ext", "1.1.7") } tasks.withType { kotlinOptions { - jvmTarget = "1.8" + jvmTarget = JavaVersion.VERSION_1_8.toString() } } + +tasks.withType { + sourceCompatibility = JavaVersion.VERSION_1_8.toString() + targetCompatibility = JavaVersion.VERSION_1_8.toString() +} \ No newline at end of file diff --git a/build-logic/src/main/kotlin/Versions.kt b/build-logic/src/main/kotlin/Versions.kt index 9aeae160..050ecc1b 100644 --- a/build-logic/src/main/kotlin/Versions.kt +++ b/build-logic/src/main/kotlin/Versions.kt @@ -26,16 +26,21 @@ object Versions { const val spigotVersion = "1.13-R0.1-SNAPSHOT" const val configUtilsVersion = "1.0-SNAPSHOT" - const val guiceVersion = "5.0.1" + const val guiceVersion = "5.1.0" const val nettyVersion = "4.1.49.Final" const val snakeyamlVersion = "1.28" const val cloudVersion = "1.5.0" const val adventureApiVersion = "4.10.0" const val adventurePlatformVersion = "4.0.0" const val viaVersionVersion = "4.0.0" - const val gRPCVersion = "1.44.0" + const val gRPCVersion = "1.55.1" + const val connectKotlinVersion = "0.1.7" + const val minekube_connect_bufbuild_connectKotlinVersion = "${connectKotlinVersion}.2.20230517110945.04c17e7d2fd9" + const val minekube_connect_grpc_javaVersion = "1.55.1.1.20230517110945.04c17e7d2fd9" + const val minekube_connect_protocolbuffers_javaVersion = "23.0.0.1.20230517110945.04c17e7d2fd9" + const val okHttpVersion = "4.9.3" const val protocVersion = "3.19.4" - const val bstatsVersion = "3.0.0" + const val bstatsVersion = "3.0.2" const val gsonVersion = "2.8.6" const val checkerQual = "3.19.0" diff --git a/build-logic/src/main/kotlin/connect.base-conventions.gradle.kts b/build-logic/src/main/kotlin/connect.base-conventions.gradle.kts index b1039e5a..1b9ffc71 100644 --- a/build-logic/src/main/kotlin/connect.base-conventions.gradle.kts +++ b/build-logic/src/main/kotlin/connect.base-conventions.gradle.kts @@ -1,7 +1,6 @@ plugins { `java-library` - `maven-publish` -// id("net.ltgt.errorprone") + id("net.kyori.indra") id("net.kyori.indra.git") } @@ -9,6 +8,21 @@ dependencies { compileOnly("org.checkerframework", "checker-qual", Versions.checkerQual) } +indra { + github("minekube", "connect-java") { + ci(true) + issues(true) + scm(true) + } + mitLicense() + + javaVersions { + // without toolchain & strictVersion sun.misc.Unsafe won't be found + minimumToolchain(8) + strictVersions(true) + } +} + tasks { processResources { filesMatching(listOf("plugin.yml", "bungee.yml", "velocity-plugin.json")) { @@ -22,14 +36,4 @@ tasks { ) } } - compileJava { - options.encoding = Charsets.UTF_8.name() - } } - -java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 - - withSourcesJar() -} \ No newline at end of file diff --git a/build-logic/src/main/kotlin/connect.generate-templates.gradle.kts b/build-logic/src/main/kotlin/connect.generate-templates.gradle.kts new file mode 100644 index 00000000..edd058de --- /dev/null +++ b/build-logic/src/main/kotlin/connect.generate-templates.gradle.kts @@ -0,0 +1,93 @@ +import org.apache.tools.ant.filters.ReplaceTokens +import org.gradle.plugins.ide.eclipse.model.EclipseModel +import org.gradle.plugins.ide.idea.model.IdeaModel +import org.jetbrains.gradle.ext.ProjectSettings +import org.jetbrains.gradle.ext.TaskTriggersConfig + +plugins { + id("org.jetbrains.gradle.plugin.idea-ext") +} + +registerGenerateTemplateTasks() + +fun Project.registerGenerateTemplateTasks() { + // main and test + extensions.getByType().all { + val javaDestination = layout.buildDirectory.dir("generated/sources/templates/$name") + val javaSrcDir = layout.projectDirectory.dir("src/$name/templates") + val javaGenerateTask = tasks.register( + getTaskName("template", "sources") + ) { + filteringCharset = Charsets.UTF_8.name() + from(javaSrcDir) + into(javaDestination) + filter("tokens" to replacements()) + } + java.srcDir(javaGenerateTask.map { it.outputs }) + + val resourcesDestination = layout.buildDirectory.dir("generated/resources/templates/$name") + val resourcesSrcDir = layout.projectDirectory.dir("src/$name/resourceTemplates") + val resourcesGenerateTask = tasks.register( + getTaskName("template", "resources") + ) { + filteringCharset = Charsets.UTF_8.name() + from(resourcesSrcDir) + into(resourcesDestination) + filter("tokens" to replacements()) + } + resources.srcDir(resourcesGenerateTask.map { it.outputs }) + } + + return configureIdeSync( + tasks.register("allTemplateSources") { + dependsOn(tasks.withType()) + }, + tasks.register("allTemplateResources") { + dependsOn(tasks.withType()) + } + ) +} + +fun Project.configureIdeSync(vararg generateAllTasks: TaskProvider) { + extensions.findByType { + synchronizationTasks(generateAllTasks) + } + + extensions.findByType { + if (project != null) { + (project as ExtensionAware).extensions.configure { + (this as ExtensionAware).extensions.configure { + afterSync(generateAllTasks) + } + } + } + } + + //todo wasn't able to find something for VS(Code) +} + +inline fun ExtensionContainer.findByType(noinline action: T.() -> Unit) { + val extension = findByType(T::class) + if (extension != null) { + action.invoke(extension) + } +} + +abstract class GenerateAnyTemplates : Copy() { + private val replacements = mutableMapOf() + + fun replaceToken(key: String, value: () -> Any) { + replaceToken(key, value.invoke()) + } + + fun replaceToken(key: String, value: Any) { + replacements[key] = value.toString() + } + + fun replacements(): Map { + return replacements + } +} + +open class GenerateResourceTemplates : GenerateAnyTemplates() +open class GenerateSourceTemplates : GenerateAnyTemplates() \ No newline at end of file diff --git a/build-logic/src/main/kotlin/connect.publish-conventions.gradle.kts b/build-logic/src/main/kotlin/connect.publish-conventions.gradle.kts index b20b5b51..823fe3d7 100644 --- a/build-logic/src/main/kotlin/connect.publish-conventions.gradle.kts +++ b/build-logic/src/main/kotlin/connect.publish-conventions.gradle.kts @@ -1,34 +1,16 @@ plugins { id("connect.shadow-conventions") - id("com.jfrog.artifactory") - id("maven-publish") + id("net.kyori.indra.publishing") } -publishing { - publications { - create("mavenJava") { - groupId = project.group as String - artifactId = project.name - version = project.version as String - - artifact(tasks["shadowJar"]) - artifact(tasks["sourcesJar"]) +indra { + configurePublications { + if (shouldAddBranchName()) { + version = versionWithBranchName() } } -} -artifactory { - setContextUrl("https://repo.opencollab.dev/artifactory") - publish { - repository { - setRepoKey(if (isSnapshot()) "maven-snapshots" else "maven-releases") - setMavenCompatible(true) - } - defaults { - publications("mavenJava") - setPublishArtifacts(true) - setPublishPom(true) - setPublishIvy(false) - } - } -} \ No newline at end of file + publishSnapshotsTo("minekube", "http://localhost/snapshots") + publishReleasesTo("minekube", "http://localhost/releases") + +} diff --git a/build-logic/src/main/kotlin/connect.shadow-conventions.gradle.kts b/build-logic/src/main/kotlin/connect.shadow-conventions.gradle.kts index 28cdeb50..9868593c 100644 --- a/build-logic/src/main/kotlin/connect.shadow-conventions.gradle.kts +++ b/build-logic/src/main/kotlin/connect.shadow-conventions.gradle.kts @@ -15,6 +15,9 @@ tasks { archiveVersion.set("") archiveClassifier.set("") + exclude("**/*.proto") + includeEmptyDirs = false + val sJar: ShadowJar = this doFirst { diff --git a/build-logic/src/main/kotlin/extensions.kt b/build-logic/src/main/kotlin/extensions.kt index 54158d84..e0553fdd 100644 --- a/build-logic/src/main/kotlin/extensions.kt +++ b/build-logic/src/main/kotlin/extensions.kt @@ -28,9 +28,6 @@ import org.gradle.api.Project import org.gradle.api.artifacts.ProjectDependency import org.gradle.kotlin.dsl.the -fun Project.isSnapshot(): Boolean = - version.toString().endsWith("-SNAPSHOT") - fun Project.fullVersion(): String { var version = version.toString() if (version.endsWith("-SNAPSHOT")) { @@ -42,14 +39,19 @@ fun Project.fullVersion(): String { fun Project.lastCommitHash(): String? = the().commit()?.name?.substring(0, 7) -// retrieved from https://wiki.jenkins-ci.org/display/JENKINS/Building+a+software+project -// some properties might be specific to Jenkins fun Project.branchName(): String = - System.getenv("GIT_BRANCH") ?: "local/dev" -fun Project.buildNumber(): Int = - Integer.parseInt(System.getenv("BUILD_NUMBER") ?: "-1") + the().branchName() ?: System.getenv("BRANCH_NAME") ?: "local/dev" + +fun Project.shouldAddBranchName(): Boolean = + System.getenv("IGNORE_BRANCH")?.toBoolean() ?: (branchName() !in arrayOf("connect", "master", "local/dev")) + +fun Project.versionWithBranchName(): String = + branchName().replace(Regex("[^0-9A-Za-z-_]"), "-") + '-' + version + +fun buildNumber(): Int = + System.getenv("BUILD_NUMBER")?.let { Integer.parseInt(it) } ?: -1 -fun Project.buildNumberAsString(): String = +fun buildNumberAsString(): String = buildNumber().takeIf { it != -1 }?.toString() ?: "??" val providedDependencies = mutableMapOf>>() diff --git a/build.gradle.kts b/build.gradle.kts index f7dd2333..e6e47bbc 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,14 +1,15 @@ plugins { `java-library` + id("connect.build-logic") id("io.freefair.lombok") version "6.3.0" apply false } allprojects { group = "com.minekube.connect" - version = "0.4.0" + version = property("version")!! description = - "Connects the server/proxy to the global Connect network to reach more players while also supporting online mode server, bungee or velocity mode. Visit https://minekube.com/connect" + "Connects the server/proxy to the global Connect network to reach more players while also supporting online mode server, bungee or velocity mode. Visit https://connect.minekube.com" } val deployProjects = setOf( diff --git a/bungee/build.gradle.kts b/bungee/build.gradle.kts index 6f1fa312..c3869893 100644 --- a/bungee/build.gradle.kts +++ b/bungee/build.gradle.kts @@ -1,4 +1,4 @@ -var bungeeCommit = "ff5727c" +var bungeeCommit = "188d502" var gsonVersion = "2.8.0" var guavaVersion = "21.0" diff --git a/bungee/src/main/java/com/minekube/connect/inject/bungee/BungeeInjector.java b/bungee/src/main/java/com/minekube/connect/inject/bungee/BungeeInjector.java index 078e4bd0..85946914 100644 --- a/bungee/src/main/java/com/minekube/connect/inject/bungee/BungeeInjector.java +++ b/bungee/src/main/java/com/minekube/connect/inject/bungee/BungeeInjector.java @@ -66,9 +66,8 @@ public final class BungeeInjector extends CommonPlatformInjector implements List @Override - public boolean inject() { - try { - // Can everyone just switch to Velocity please :) + public void inject() throws Exception { + // Can everyone just switch to Velocity please :) // Field framePrepender = ReflectionUtils.getField(PipelineUtils.class, "framePrepender"); // @@ -80,13 +79,8 @@ public boolean inject() { // ); // // BungeeReflectionUtils.setFieldValue(null, framePrepender, customPrepender); - initializeLocalChannel0(); - injected = true; - return true; - } catch (Exception e) { - e.printStackTrace(); - return false; - } + initializeLocalChannel0(); + injected = true; } public void initializeLocalChannel() { diff --git a/bungee/src/main/java/com/minekube/connect/module/BungeePlatformModule.java b/bungee/src/main/java/com/minekube/connect/module/BungeePlatformModule.java index 3004a3ee..c6fd8d17 100644 --- a/bungee/src/main/java/com/minekube/connect/module/BungeePlatformModule.java +++ b/bungee/src/main/java/com/minekube/connect/module/BungeePlatformModule.java @@ -32,6 +32,7 @@ import com.google.inject.Provides; import com.google.inject.Singleton; import com.google.inject.name.Named; +import com.google.inject.name.Names; import com.minekube.connect.BungeePlugin; import com.minekube.connect.api.ConnectApi; import com.minekube.connect.api.logger.ConnectLogger; @@ -49,6 +50,7 @@ import com.minekube.connect.util.BungeeCommandUtil; import com.minekube.connect.util.BungeePlatformUtils; import com.minekube.connect.util.LanguageManager; +import java.util.logging.Logger; import lombok.RequiredArgsConstructor; import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.ProxyServer; @@ -62,19 +64,10 @@ public final class BungeePlatformModule extends AbstractModule { @Override protected void configure() { bind(PlatformUtils.class).to(BungeePlatformUtils.class); + bind(Logger.class).annotatedWith(Names.named("logger")).toInstance(plugin.getLogger()); + bind(ConnectLogger.class).to(JavaUtilConnectLogger.class); bind(ProxyServer.class).toInstance(plugin.getProxy()); - } - - @Provides - @Singleton - public Plugin bungeePlugin() { - return plugin; - } - - @Provides - @Singleton - public ConnectLogger logger(LanguageManager languageManager) { - return new JavaUtilConnectLogger(plugin.getLogger(), languageManager); + bind(Plugin.class).toInstance(plugin); } /* diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 8dc2d2de..3689a9a1 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -1,61 +1,47 @@ -import com.google.protobuf.gradle.* -import net.kyori.blossom.BlossomExtension - plugins { - idea // used to let Intellij recognize protobuf generated sources - id("net.kyori.blossom") - id("com.google.protobuf") + id("connect.generate-templates") } dependencies { api(projects.api) api("org.geysermc.configutils", "configutils", Versions.configUtilsVersion) + compileOnly(projects.ap) + annotationProcessor(projects.ap) + api("com.google.inject", "guice", Versions.guiceVersion) api("cloud.commandframework", "cloud-core", Versions.cloudVersion) api("org.yaml", "snakeyaml", Versions.snakeyamlVersion) api("org.bstats", "bstats-base", Versions.bstatsVersion) - implementation("com.squareup.okhttp3:okhttp:4.9.3") - runtimeOnly("io.grpc", "grpc-netty-shaded", Versions.gRPCVersion) - implementation("io.grpc", "grpc-protobuf", Versions.gRPCVersion) - implementation("io.grpc", "grpc-stub", Versions.gRPCVersion) implementation("javax.annotation", "javax.annotation-api", "1.3.2") + + implementation("com.squareup.okhttp3", "okhttp", Versions.okHttpVersion) + implementation("io.grpc", "grpc-okhttp", Versions.gRPCVersion) + + implementation("build.buf", "connect-kotlin-okhttp", Versions.connectKotlinVersion) + implementation("build.buf", "connect-kotlin-google-java-ext", Versions.connectKotlinVersion) + + implementation("build.buf.gen", "minekube_connect_grpc_java", Versions.minekube_connect_grpc_javaVersion) + implementation("build.buf.gen", "minekube_connect_bufbuild_connect-kotlin", Versions.minekube_connect_bufbuild_connectKotlinVersion) + implementation("build.buf.gen", "minekube_connect_protocolbuffers_java", Versions.minekube_connect_protocolbuffers_javaVersion) + } +relocate("org.bstats") +relocate("com.squareup.okhttp3") + + // present on all platforms provided("io.netty", "netty-transport", Versions.nettyVersion) provided("io.netty", "netty-codec", Versions.nettyVersion) provided("io.netty", "netty-transport-native-unix-common", Versions.nettyVersion) -relocate("org.bstats") - -configure { - val constantsFile = "src/main/java/com/minekube/connect/util/Constants.java" - replaceToken("\${connectVersion}", fullVersion(), constantsFile) - replaceToken("\${branch}", branchName(), constantsFile) - replaceToken("\${buildNumber}", buildNumber(), constantsFile) -} -protobuf { - protoc { - // The artifact spec for the Protobuf Compiler - artifact = "com.google.protobuf:protoc:${Versions.protocVersion}" +tasks { + templateSources { + replaceToken("connectVersion", fullVersion()) + replaceToken("branch", branchName()) + replaceToken("buildNumber", buildNumber()) } - plugins { - // Optional: an artifact spec for a protoc plugin, with "grpc" as - // the identifier, which can be referred to in the "plugins" - // container of the "generateProtoTasks" closure. - id("grpc") { - artifact = "io.grpc:protoc-gen-grpc-java:${Versions.gRPCVersion}" - } - } - generateProtoTasks { - ofSourceSet("main").forEach { - it.plugins { - // Apply the "grpc" plugin whose spec is defined above, without options. - id("grpc") - } - } - } -} +} \ No newline at end of file diff --git a/core/src/main/java/com/minekube/connect/ConnectPlatform.java b/core/src/main/java/com/minekube/connect/ConnectPlatform.java index 89a428da..1bcbc242 100644 --- a/core/src/main/java/com/minekube/connect/ConnectPlatform.java +++ b/core/src/main/java/com/minekube/connect/ConnectPlatform.java @@ -28,92 +28,54 @@ import com.google.inject.Inject; import com.google.inject.Injector; import com.google.inject.Module; -import com.google.inject.name.Named; +import com.minekube.connect.api.Clients; import com.minekube.connect.api.ConnectApi; import com.minekube.connect.api.InstanceHolder; import com.minekube.connect.api.inject.PlatformInjector; import com.minekube.connect.api.logger.ConnectLogger; import com.minekube.connect.api.packet.PacketHandlers; -import com.minekube.connect.config.ConfigHolder; -import com.minekube.connect.config.ConfigLoader; import com.minekube.connect.config.ConnectConfig; import com.minekube.connect.inject.CommonPlatformInjector; +import com.minekube.connect.module.ClientsModule; import com.minekube.connect.module.ConfigLoadedModule; import com.minekube.connect.module.PostInitializeModule; import com.minekube.connect.register.WatcherRegister; import com.minekube.connect.tunnel.Tunneler; import com.minekube.connect.util.Metrics; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; import java.util.UUID; public class ConnectPlatform { private static final String DOMAIN_SUFFIX = ".play.minekube.net"; private static final UUID KEY = UUID.randomUUID(); - private final ConnectApi api; - private final PlatformInjector injector; - - private final ConnectLogger logger; - - private ConnectConfig config; - private Injector guice; - - @Inject - public ConnectPlatform( - ConnectApi api, - PlatformInjector platformInjector, - ConnectLogger logger, - Injector guice) { - - this.api = api; - this.injector = platformInjector; - this.logger = logger; - this.guice = guice; - } + @Inject private PlatformInjector injector; + @Inject private ConnectConfig config; + @Inject private Injector guice; + @Inject private ConnectLogger logger; @Inject public void init( - @Named("dataDirectory") Path dataDirectory, - ConfigLoader configLoader, - ConfigHolder configHolder, + ConnectApi api, PacketHandlers packetHandlers) { - if (!Files.isDirectory(dataDirectory)) { - try { - Files.createDirectory(dataDirectory); - } catch (IOException exception) { - logger.error("Failed to create the data folder", exception); - throw new RuntimeException("Failed to create the data folder", exception); - } - } - - config = configLoader.load(); - if (config.isDebug()) { - logger.enableDebug(); - } + guice = guice.createChildInjector( + new ConfigLoadedModule(config), + new ClientsModule() + ); - configHolder.set(config); - guice = guice.createChildInjector(new ConfigLoadedModule(config)); - - InstanceHolder.set(api, this.injector, packetHandlers, KEY); + Clients clients = guice.getInstance(Clients.class); + InstanceHolder.set(api, this.injector, packetHandlers, clients, KEY); } - public boolean enable(Module... postInitializeModules) { + public void enable(Module... postInitializeModules) throws RuntimeException { if (injector == null) { - logger.error("Failed to find the platform injector!"); - return false; + throw new RuntimeException("Failed to find the platform injector!"); } try { - if (!injector.inject()) { // TODO && !bootstrap.getGeyserConfig().isUseDirectConnection() - logger.error("Failed to inject the packet listener!"); - return false; - } + injector.inject(); } catch (Exception exception) { - logger.error("Failed to inject the packet listener!", exception); - return false; + throw new RuntimeException("Failed to inject the packet listener!", exception); } this.guice = guice.createChildInjector(new PostInitializeModule(postInitializeModules)); @@ -125,8 +87,6 @@ public boolean enable(Module... postInitializeModules) { logger.info("Super endpoints: " + String.join(", ", config.getSuperEndpoints())); } logger.info("Your public address: " + config.getEndpoint() + DOMAIN_SUFFIX); - - return true; } public boolean disable() { diff --git a/core/src/main/java/com/minekube/connect/api/ProxyConnectApi.java b/core/src/main/java/com/minekube/connect/api/ProxyConnectApi.java index 4e9d00a0..50b9d761 100644 --- a/core/src/main/java/com/minekube/connect/api/ProxyConnectApi.java +++ b/core/src/main/java/com/minekube/connect/api/ProxyConnectApi.java @@ -25,12 +25,9 @@ package com.minekube.connect.api; -import com.minekube.connect.api.logger.ConnectLogger; - - public final class ProxyConnectApi extends SimpleConnectApi { - public ProxyConnectApi(ConnectLogger logger) { - super(logger); + public ProxyConnectApi() { + super(); } } diff --git a/core/src/main/java/com/minekube/connect/api/SimpleConnectApi.java b/core/src/main/java/com/minekube/connect/api/SimpleConnectApi.java index 81668342..380718f1 100644 --- a/core/src/main/java/com/minekube/connect/api/SimpleConnectApi.java +++ b/core/src/main/java/com/minekube/connect/api/SimpleConnectApi.java @@ -29,7 +29,6 @@ import com.google.common.cache.CacheBuilder; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; -import com.minekube.connect.api.logger.ConnectLogger; import com.minekube.connect.api.player.ConnectPlayer; import java.util.Collection; import java.util.Map; @@ -45,8 +44,6 @@ public class SimpleConnectApi implements ConnectApi { .expireAfterWrite(20, TimeUnit.SECONDS) .build(); - private final ConnectLogger logger; - @Override public Collection getPlayers() { return ImmutableSet.copyOf(players.values()); diff --git a/core/src/main/java/com/minekube/connect/command/ConnectSubCommand.java b/core/src/main/java/com/minekube/connect/command/ConnectSubCommand.java new file mode 100644 index 00000000..d330a924 --- /dev/null +++ b/core/src/main/java/com/minekube/connect/command/ConnectSubCommand.java @@ -0,0 +1,12 @@ +package com.minekube.connect.command; + +import cloud.commandframework.context.CommandContext; +import com.minekube.connect.command.util.Permission; +import com.minekube.connect.player.UserAudience; + +public abstract class ConnectSubCommand { + public abstract String name(); + public abstract String description(); + public abstract Permission permission(); + public abstract void execute(CommandContext context); +} diff --git a/core/src/main/java/com/minekube/connect/command/main/FirewallCheckSubcommand.java b/core/src/main/java/com/minekube/connect/command/main/FirewallCheckSubcommand.java deleted file mode 100644 index dcc8749c..00000000 --- a/core/src/main/java/com/minekube/connect/command/main/FirewallCheckSubcommand.java +++ /dev/null @@ -1,108 +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 com.minekube.connect.command.main; - -import static com.minekube.connect.util.Constants.COLOR_CHAR; - -import cloud.commandframework.context.CommandContext; -import cloud.commandframework.types.tuples.Pair; -import com.google.gson.JsonElement; -import com.minekube.connect.player.UserAudience; -import com.minekube.connect.util.Constants; -import com.minekube.connect.util.HttpUtils; -import com.minekube.connect.util.HttpUtils.HttpResponse; -import com.minekube.connect.util.Utils; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.BooleanSupplier; - -final class FirewallCheckSubcommand { - private FirewallCheckSubcommand() { - } - - static void executeFirewall(CommandContext context) { - UserAudience sender = context.getSender(); - executeChecks( - globalApiCheck(sender) - ).whenComplete((response, $) -> - sender.sendMessage(String.format( - COLOR_CHAR + "eThe checks have finished. %s/%s were successful", - response.getFirst(), response.getFirst() + response.getSecond() - )) - ); - } - - private static BooleanSupplier globalApiCheck(UserAudience sender) { - return executeFirewallText( - sender, "global api", - () -> { - HttpResponse response = - HttpUtils.get(Constants.HEALTH_URL, JsonElement.class); - - if (!response.isCodeOk()) { - throw new IllegalStateException(String.format( - "Didn't receive an 'ok' http code. Got: %s, response: %s", - response.getHttpCode(), response.getResponse() - )); - } - }); - } - - private static BooleanSupplier executeFirewallText( - UserAudience sender, String name, Runnable runnable) { - return () -> { - sender.sendMessage(COLOR_CHAR + "eTesting " + name + "..."); - try { - runnable.run(); - sender.sendMessage(COLOR_CHAR + "aWas able to connect to " + name + "!"); - return true; - } catch (Exception e) { - sender.sendMessage(COLOR_CHAR + "cFailed to connect:"); - sender.sendMessage(Utils.getStackTrace(e)); - return false; - } - }; - } - - private static CompletableFuture> executeChecks( - BooleanSupplier... checks) { - - return CompletableFuture.supplyAsync(() -> { - AtomicInteger okCount = new AtomicInteger(); - AtomicInteger failCount = new AtomicInteger(); - - for (BooleanSupplier check : checks) { - if (check.getAsBoolean()) { - okCount.getAndIncrement(); - continue; - } - failCount.getAndIncrement(); - } - - return Pair.of(okCount.get(), failCount.get()); - }); - } -} diff --git a/core/src/main/java/com/minekube/connect/command/main/MainCommand.java b/core/src/main/java/com/minekube/connect/command/main/MainCommand.java index 7cd1025b..f5f30cff 100644 --- a/core/src/main/java/com/minekube/connect/command/main/MainCommand.java +++ b/core/src/main/java/com/minekube/connect/command/main/MainCommand.java @@ -79,8 +79,9 @@ public void execute(CommandContext context) { @RequiredArgsConstructor enum SubCommand { - FIREWALL("Check if your outgoing firewall allows Floodgate to work properly", - Permission.COMMAND_MAIN_FIREWALL, FirewallCheckSubcommand::executeFirewall); + VERSION("Shows the current version of Connect plugin", + Permission.COMMAND_MAIN_VERSION, + userAudienceCommandContext -> userAudienceCommandContext.getSender().sendMessage("This is a demo command")); static final SubCommand[] VALUES = values(); diff --git a/core/src/main/java/com/minekube/connect/command/main/VersionSubcommand.java b/core/src/main/java/com/minekube/connect/command/main/VersionSubcommand.java new file mode 100644 index 00000000..2c1f5d59 --- /dev/null +++ b/core/src/main/java/com/minekube/connect/command/main/VersionSubcommand.java @@ -0,0 +1,42 @@ +package com.minekube.connect.command.main; + +import static com.minekube.connect.util.Constants.COLOR_CHAR; + +import cloud.commandframework.context.CommandContext; +import com.google.inject.Inject; +import com.minekube.connect.api.logger.ConnectLogger; +import com.minekube.connect.command.ConnectSubCommand; +import com.minekube.connect.command.util.Permission; +import com.minekube.connect.player.UserAudience; +import com.minekube.connect.util.Constants; + +public class VersionSubcommand extends ConnectSubCommand { + @Inject + private ConnectLogger logger; + + @Override + public String name() { + return "version"; + } + + @Override + public String description() { + return "Displays version information about Connect"; + } + + @Override + public Permission permission() { + return Permission.COMMAND_MAIN_VERSION; + } + + @Override + public void execute(CommandContext context) { + UserAudience sender = context.getSender(); + sender.sendMessage(String.format( + COLOR_CHAR + "7You're currently on " + COLOR_CHAR + "b%s" + + COLOR_CHAR + "7 (branch: " + COLOR_CHAR + "b%s" + COLOR_CHAR + "7)", + Constants.VERSION, Constants.GIT_BRANCH + // TODO check for new version + )); + } +} \ No newline at end of file diff --git a/core/src/main/java/com/minekube/connect/command/util/Permission.java b/core/src/main/java/com/minekube/connect/command/util/Permission.java index 59ff163d..3b9524c1 100644 --- a/core/src/main/java/com/minekube/connect/command/util/Permission.java +++ b/core/src/main/java/com/minekube/connect/command/util/Permission.java @@ -30,7 +30,7 @@ public enum Permission { COMMAND_MAIN("connect.command.connect", TRUE), - COMMAND_MAIN_FIREWALL(COMMAND_MAIN, "firewall", OP), + COMMAND_MAIN_VERSION(COMMAND_MAIN, "version", OP), COMMAND_LINK("connect.command.linkaccount", TRUE), COMMAND_UNLINK("connect.command.unlinkaccount", TRUE), COMMAND_WHITELIST("connect.command.fwhitelist", OP), diff --git a/core/src/main/java/com/minekube/connect/config/ConfigLoader.java b/core/src/main/java/com/minekube/connect/config/ConfigLoader.java index b53a778b..a589894a 100644 --- a/core/src/main/java/com/minekube/connect/config/ConfigLoader.java +++ b/core/src/main/java/com/minekube/connect/config/ConfigLoader.java @@ -28,6 +28,7 @@ import com.google.inject.Inject; import com.google.inject.name.Named; import com.minekube.connect.api.logger.ConnectLogger; +import com.minekube.connect.util.Constants; import com.minekube.connect.util.Utils; import java.io.File; import java.io.IOException; @@ -52,7 +53,7 @@ @Getter @RequiredArgsConstructor public final class ConfigLoader { - private final Path dataFolder; + private final Path dataDirectory; private final Class configClass; private final EndpointNameGenerator endpointNameGenerator; @@ -80,7 +81,7 @@ public T load() { ConfigUtilities utilities = ConfigUtilities.builder() - .fileCodec(PathFileCodec.of(dataFolder)) + .fileCodec(PathFileCodec.of(dataDirectory)) .configFile("config.yml") .templateReader(ResourceTemplateReader.of(getClass())) .template(templateFile) @@ -95,7 +96,7 @@ public T load() { try { // temporary placeholder fix - File config = new File(dataFolder.toFile(), "config.yml"); + File config = new File(dataDirectory.toFile(), "config.yml"); if (config.exists()) { fixPlaceholderIssue(config.toPath()); } else { @@ -114,8 +115,6 @@ public T load() { } public static class EndpointNameGenerator { - private static final String URL = "https://randomname.minekube.net"; - final private OkHttpClient client; @Inject @@ -127,7 +126,7 @@ public EndpointNameGenerator(@Named("defaultHttpClient") OkHttpClient client) { String get() { Request req = new Builder() - .url(URL) + .url(Constants.CONNECT_RANDOM_NAME_URL) .build(); String name; diff --git a/core/src/main/java/com/minekube/connect/inject/CommonPlatformInjector.java b/core/src/main/java/com/minekube/connect/inject/CommonPlatformInjector.java index 9dd033cc..ec19ccdb 100644 --- a/core/src/main/java/com/minekube/connect/inject/CommonPlatformInjector.java +++ b/core/src/main/java/com/minekube/connect/inject/CommonPlatformInjector.java @@ -30,11 +30,11 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import java.net.SocketAddress; +import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; import java.util.Set; -import lombok.AccessLevel; +import java.util.WeakHashMap; import lombok.Getter; /** @@ -65,8 +65,7 @@ public void shutdown() { } } - @Getter(AccessLevel.PROTECTED) - private final Set injectedClients = new HashSet<>(); + private final Set injectedClients = Collections.synchronizedSet(Collections.newSetFromMap(new WeakHashMap<>())); private final Map, InjectorAddon> addons = new HashMap<>(); @@ -78,6 +77,10 @@ public boolean removeInjectedClient(Channel channel) { return injectedClients.remove(channel); } + public Set injectedClients() { + return injectedClients; + } + @Override public boolean addAddon(InjectorAddon addon) { return addons.putIfAbsent(addon.getClass(), addon) == null; diff --git a/core/src/main/java/com/minekube/connect/logger/JavaUtilConnectLogger.java b/core/src/main/java/com/minekube/connect/logger/JavaUtilConnectLogger.java index ee3cdcdd..6bd212c6 100644 --- a/core/src/main/java/com/minekube/connect/logger/JavaUtilConnectLogger.java +++ b/core/src/main/java/com/minekube/connect/logger/JavaUtilConnectLogger.java @@ -27,17 +27,28 @@ import static com.minekube.connect.util.MessageFormatter.format; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import com.google.inject.name.Named; import com.minekube.connect.api.logger.ConnectLogger; +import com.minekube.connect.config.ConnectConfig; import com.minekube.connect.util.LanguageManager; import java.util.logging.Level; import java.util.logging.Logger; -import lombok.RequiredArgsConstructor; -@RequiredArgsConstructor +@Singleton public final class JavaUtilConnectLogger implements ConnectLogger { - private final Logger logger; - private final LanguageManager languageManager; - private Level originLevel; + @Inject + @Named("logger") + private Logger logger; + private LanguageManager languageManager; + + private void init(LanguageManager languageManager, ConnectConfig config) { + this.languageManager = languageManager; + if (config.isDebug()) { + logger.setLevel(Level.ALL); + } + } @Override public void error(String message, Object... args) { @@ -74,18 +85,6 @@ public void trace(String message, Object... args) { logger.finer(format(message, args)); } - @Override - public void enableDebug() { - originLevel = logger.getLevel(); - logger.setLevel(Level.ALL); - } - - @Override - public void disableDebug() { - if (originLevel != null) { - logger.setLevel(originLevel); - } - } @Override public boolean isDebug() { diff --git a/core/src/main/java/com/minekube/connect/module/AutoBindModule.java b/core/src/main/java/com/minekube/connect/module/AutoBindModule.java new file mode 100644 index 00000000..c2900e76 --- /dev/null +++ b/core/src/main/java/com/minekube/connect/module/AutoBindModule.java @@ -0,0 +1,14 @@ +package com.minekube.connect.module; + +import com.google.inject.AbstractModule; +import com.minekube.connect.util.AutoBind; +import com.minekube.connect.util.Utils; + +public class AutoBindModule extends AbstractModule { + @Override + protected void configure() { + for (Class clazz : Utils.getGeneratedClassesForAnnotation(AutoBind.class)) { + bind(clazz).asEagerSingleton(); + } + } +} diff --git a/core/src/main/java/com/minekube/connect/module/ClientsModule.java b/core/src/main/java/com/minekube/connect/module/ClientsModule.java new file mode 100644 index 00000000..2ee8cfc5 --- /dev/null +++ b/core/src/main/java/com/minekube/connect/module/ClientsModule.java @@ -0,0 +1,141 @@ +package com.minekube.connect.module; + +import build.buf.connect.ProtocolClientConfig; +import build.buf.connect.extensions.GoogleJavaProtobufStrategy; +import build.buf.connect.impl.ProtocolClient; +import build.buf.connect.okhttp.ConnectOkHttpClient; +import build.buf.connect.protocols.NetworkProtocol; +import build.buf.gen.minekube.connect.v1alpha1.ConnectServiceClient; +import build.buf.gen.minekube.connect.v1alpha1.ConnectServiceClientInterface; +import build.buf.gen.minekube.connect.v1alpha1.ConnectServiceGrpc; +import build.buf.gen.minekube.connect.v1alpha1.ConnectServiceGrpc.ConnectServiceBlockingStub; +import build.buf.gen.minekube.connect.v1alpha1.ConnectServiceGrpc.ConnectServiceFutureStub; +import build.buf.gen.minekube.connect.v1alpha1.ConnectServiceGrpc.ConnectServiceStub; +import com.google.common.collect.ImmutableMap; +import com.google.inject.AbstractModule; +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.Provides; +import com.google.inject.Singleton; +import com.google.inject.name.Named; +import com.minekube.connect.api.Clients; +import com.minekube.connect.config.ConfigHolder; +import com.minekube.connect.util.Constants; +import com.minekube.connect.util.HeaderClientInterceptor; +import com.minekube.connect.watch.WatchClient; +import io.grpc.Channel; +import io.grpc.InsecureChannelCredentials; +import io.grpc.ManagedChannelBuilder; +import io.grpc.okhttp.OkHttpChannelBuilder; +import lombok.RequiredArgsConstructor; +import okhttp3.OkHttpClient; + +@RequiredArgsConstructor +public class ClientsModule extends AbstractModule { + + @Override + protected void configure() { + bind(Clients.class).to(ClientsImpl.class); + bind(ClientsImpl.class).in(Singleton.class); + } + + @Provides + @Singleton + public ProtocolClient protocolClient( + @Named("connectHttpClient") OkHttpClient connectOkHttpClient + ) { + final String connectServiceHost = System.getenv().getOrDefault( + "CONNECT_SERVICE_HOST", "https://" + Constants.CONNECT_API_TARGET); + return new ProtocolClient( + new ConnectOkHttpClient(connectOkHttpClient), + new ProtocolClientConfig( + connectServiceHost, + new GoogleJavaProtobufStrategy(), + NetworkProtocol.CONNECT + ) + ); + } + + @Provides + @Singleton + public ConnectServiceClientInterface connectServiceClient(ProtocolClient protocolClient) { + return new ConnectServiceClient(protocolClient); + } + + @Provides + @Singleton + public ConnectServiceFutureStub connectServiceFutureStub( + @Named("connectGrpcChannel") Channel channel + ) { + return ConnectServiceGrpc.newFutureStub(channel); + } + + @Provides + @Singleton + public ConnectServiceStub connectServiceStub( + @Named("connectGrpcChannel") Channel channel + ) { + return ConnectServiceGrpc.newStub(channel); + } + + @Provides + @Singleton + public ConnectServiceBlockingStub connectServiceBlockingStub( + @Named("connectGrpcChannel") Channel channel + ) { + return ConnectServiceGrpc.newBlockingStub(channel); + } + + @Provides + @Singleton + @Named("connectGrpcChannel") + public Channel connectServiceChannel( + @Named("connectGrpcChannelBuilder") ManagedChannelBuilder managedChannelBuilder + ) { + return managedChannelBuilder.build(); + } + + @Provides + @Singleton + @Named("connectGrpcChannelBuilder") + public ManagedChannelBuilder connectServiceChannelBuilder( + @Named("endpointName") String endpointName, + @Named("connectToken") String connectToken + ) { +// return ManagedChannelBuilder.forTarget(CONNECT_API_TARGET) +// return Grpc.newChannelBuilder(CONNECT_API_TARGET, InsecureChannelCredentials.create()) + return OkHttpChannelBuilder.forTarget( Constants.CONNECT_API_TARGET, InsecureChannelCredentials.create()) + .intercept(new HeaderClientInterceptor(ImmutableMap.of( + WatchClient.ENDPOINT_HEADER, endpointName, + "Authorization", "Bearer " + connectToken + ))); + } + + @Provides + @Singleton + @Named("endpointName") + public String endpointName(ConfigHolder configHolder) { + return configHolder.get().getEndpoint(); + } + + static class ClientsImpl implements Clients { + @Inject Provider connectServiceBlockingStub; + @Inject Provider connectServiceFutureStub; + @Inject Provider connectServiceStub; + + @Override + public ConnectServiceBlockingStub getConnectServiceBlockingStub() { + return connectServiceBlockingStub.get(); + } + + @Override + public ConnectServiceFutureStub getConnectServiceFutureStub() { + return connectServiceFutureStub.get(); + } + + @Override + public ConnectServiceStub getConnectServiceStub() { + return connectServiceStub.get(); + } + } +} diff --git a/core/src/main/java/com/minekube/connect/module/CommonModule.java b/core/src/main/java/com/minekube/connect/module/CommonModule.java index ffe8a081..6989b25c 100644 --- a/core/src/main/java/com/minekube/connect/module/CommonModule.java +++ b/core/src/main/java/com/minekube/connect/module/CommonModule.java @@ -66,10 +66,20 @@ public class CommonModule extends AbstractModule { @Override protected void configure() { bind(ConnectApi.class).to(SimpleConnectApi.class); + bind(SimpleConnectApi.class).in(Singleton.class); + bind(PlatformInjector.class).to(CommonPlatformInjector.class); bind(PacketHandlers.class).to(PacketHandlersImpl.class); bind(PacketHandlersImpl.class).asEagerSingleton(); + + install(new AutoBindModule()); + } + + @Provides + @Singleton + public ConnectConfig connectConfig(ConfigLoader configLoader) { + return configLoader.load(); } @Provides @@ -113,15 +123,9 @@ public OkHttpClient defaultOkHttpClient() { @Provides @Singleton - @Named("connectHttpClient") - public OkHttpClient connectOkHttpClient( - @Named("defaultHttpClient") OkHttpClient defaultOkHttpClient, - PlatformUtils platformUtils, - @Named("platformName") String implementationName, - ConnectApi api - ) throws IOException { + @Named("connectToken") + public String connectToken() throws IOException { Path tokenFile = dataDirectory.resolve("token.json"); - Optional token = Token.load(tokenFile); if (!token.isPresent()) { // Generate and save new token @@ -129,12 +133,23 @@ public OkHttpClient connectOkHttpClient( Token.save(tokenFile, t); token = Optional.of(t); } - final String apiToken = token.get(); + return token.get(); + } + @Provides + @Singleton + @Named("connectHttpClient") + public OkHttpClient connectOkHttpClient( + @Named("defaultHttpClient") OkHttpClient defaultOkHttpClient, + PlatformUtils platformUtils, + @Named("platformName") String implementationName, + ConnectApi api, + @Named("connectToken") String connectToken + ) { return defaultOkHttpClient.newBuilder() .addInterceptor(chain -> chain.proceed(chain.request().newBuilder() // Add authorization token to every request - .addHeader("Authorization", "Bearer " + apiToken) + .addHeader("Authorization", "Bearer " + connectToken) // Add Connect Metadata to every request .addHeader("Connect-TotalPlayers", String.valueOf(platformUtils.getPlayerCount())) @@ -152,6 +167,8 @@ public OkHttpClient connectOkHttpClient( .build(); } + + @RequiredArgsConstructor private static class Token { @SerializedName("token") final String token; diff --git a/core/src/main/java/com/minekube/connect/module/ProxyCommonModule.java b/core/src/main/java/com/minekube/connect/module/ProxyCommonModule.java index 4a61120c..413b1cf4 100644 --- a/core/src/main/java/com/minekube/connect/module/ProxyCommonModule.java +++ b/core/src/main/java/com/minekube/connect/module/ProxyCommonModule.java @@ -30,7 +30,6 @@ import com.google.inject.name.Named; import com.minekube.connect.api.ProxyConnectApi; import com.minekube.connect.api.SimpleConnectApi; -import com.minekube.connect.api.logger.ConnectLogger; import com.minekube.connect.config.ConnectConfig; import com.minekube.connect.config.ProxyConnectConfig; import java.nio.file.Path; @@ -44,20 +43,19 @@ public ProxyCommonModule(Path dataDirectory) { protected void configure() { super.configure(); bind(SimpleConnectApi.class).to(ProxyConnectApi.class); + bind(ProxyConnectApi.class).in(Singleton.class); } @Provides @Singleton - @Named("configClass") - public Class configClass() { - return ProxyConnectConfig.class; + public ProxyConnectConfig proxyConnectConfig(ConnectConfig config) { + return (ProxyConnectConfig) config; } @Provides @Singleton - public ProxyConnectApi proxyApi( - ConnectLogger logger - ) { - return new ProxyConnectApi(logger); + @Named("configClass") + public Class connectConfigClass() { + return ProxyConnectConfig.class; } } diff --git a/core/src/main/java/com/minekube/connect/module/ServerCommonModule.java b/core/src/main/java/com/minekube/connect/module/ServerCommonModule.java index 336c068f..4ab8bbf6 100644 --- a/core/src/main/java/com/minekube/connect/module/ServerCommonModule.java +++ b/core/src/main/java/com/minekube/connect/module/ServerCommonModule.java @@ -29,7 +29,6 @@ import com.google.inject.Singleton; import com.google.inject.name.Named; import com.minekube.connect.api.SimpleConnectApi; -import com.minekube.connect.api.logger.ConnectLogger; import com.minekube.connect.config.ConnectConfig; import java.nio.file.Path; @@ -38,16 +37,16 @@ public ServerCommonModule(Path dataDirectory) { super(dataDirectory); } + @Override + protected void configure() { + super.configure(); + bind(SimpleConnectApi.class).in(Singleton.class); + } + @Provides @Singleton @Named("configClass") public Class configClass() { return ConnectConfig.class; } - - @Provides - @Singleton - public SimpleConnectApi api(ConnectLogger logger) { - return new SimpleConnectApi(logger); - } } diff --git a/core/src/main/java/com/minekube/connect/network/netty/LocalChannelInboundHandler.java b/core/src/main/java/com/minekube/connect/network/netty/LocalChannelInboundHandler.java index 5cf8c621..b3677c04 100644 --- a/core/src/main/java/com/minekube/connect/network/netty/LocalChannelInboundHandler.java +++ b/core/src/main/java/com/minekube/connect/network/netty/LocalChannelInboundHandler.java @@ -25,18 +25,18 @@ package com.minekube.connect.network.netty; -import com.google.rpc.Code; -import com.google.rpc.Status; +import build.buf.connect.Code; +import build.buf.gen.google.rpc.Status; import com.minekube.connect.api.SimpleConnectApi; import com.minekube.connect.api.logger.ConnectLogger; import com.minekube.connect.network.netty.LocalSession.Context; import com.minekube.connect.tunnel.TunnelConn; import com.minekube.connect.tunnel.Tunneler; -import io.grpc.protobuf.StatusProto; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; +import java.util.stream.Stream; import lombok.RequiredArgsConstructor; import org.jetbrains.annotations.NotNull; @@ -71,7 +71,7 @@ public static void onChannelClosed(Context context, } Status reason = Status.newBuilder() - .setCode(Code.UNKNOWN_VALUE) + .setCode(Code.UNKNOWN.getValue()) .setMessage("local connection closed") .build(); @@ -93,8 +93,16 @@ static void rejectProposal(Context context, Status reason) { @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + Status reason = null; + if (cause != null) { + reason = Status.newBuilder() + .setCode(Code.UNKNOWN.getValue()) + .setMessage(cause.getMessage()) + .build(); + } + // Reject session proposal in case we are still able to and connection was stopped very early. - rejectProposal(context, StatusProto.fromThrowable(cause)); + rejectProposal(context, reason); ctx.close(); super.exceptionCaught(ctx, cause); } diff --git a/core/src/main/java/com/minekube/connect/network/netty/LocalSession.java b/core/src/main/java/com/minekube/connect/network/netty/LocalSession.java index 51bb2185..2887811b 100644 --- a/core/src/main/java/com/minekube/connect/network/netty/LocalSession.java +++ b/core/src/main/java/com/minekube/connect/network/netty/LocalSession.java @@ -25,6 +25,10 @@ package com.minekube.connect.network.netty; +import build.buf.connect.Code; +import build.buf.gen.google.rpc.Status; +import build.buf.gen.minekube.connect.v1alpha1.Player; +import build.buf.gen.minekube.connect.v1alpha1.Session; import com.google.common.base.Preconditions; import com.minekube.connect.api.SimpleConnectApi; import com.minekube.connect.api.logger.ConnectLogger; @@ -37,7 +41,6 @@ import com.minekube.connect.tunnel.Tunneler; import com.minekube.connect.watch.SessionProposal; import com.minekube.connect.watch.SessionProposal.State; -import io.grpc.protobuf.StatusProto; import io.netty.bootstrap.Bootstrap; import io.netty.buffer.ByteBufAllocator; import io.netty.channel.Channel; @@ -59,8 +62,6 @@ import lombok.AccessLevel; import lombok.Getter; import lombok.RequiredArgsConstructor; -import minekube.connect.v1alpha1.WatchServiceOuterClass.Player; -import minekube.connect.v1alpha1.WatchServiceOuterClass.Session; import org.jetbrains.annotations.NotNull; /** @@ -185,8 +186,15 @@ public void initChannel(@NotNull LocalChannelWithSessionContext channel) { private void exceptionCaught(Throwable cause) { cause.printStackTrace(); + Status reason = null; + if (cause != null) { + reason = Status.newBuilder() + .setCode(Code.UNKNOWN.getValue()) + .setMessage(cause.getMessage()) + .build(); + } // Reject session proposal in case we are still able to. - sessionProposal.reject(StatusProto.fromThrowable(cause)); + sessionProposal.reject(reason); } /** diff --git a/core/src/main/java/com/minekube/connect/network/netty/TunnelHandler.java b/core/src/main/java/com/minekube/connect/network/netty/TunnelHandler.java index 0f725425..583ba989 100644 --- a/core/src/main/java/com/minekube/connect/network/netty/TunnelHandler.java +++ b/core/src/main/java/com/minekube/connect/network/netty/TunnelHandler.java @@ -27,8 +27,6 @@ import com.minekube.connect.api.logger.ConnectLogger; import com.minekube.connect.tunnel.TunnelConn.Handler; -import io.grpc.Status; -import io.grpc.Status.Code; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; @@ -48,11 +46,6 @@ public void onReceive(byte[] data) { @Override public void onError(Throwable t) { - // error connecting to tunnel service - Status status = Status.fromThrowable(t); - if (status.getCode() == Code.CANCELLED) { - return; - } logger.error("Connection error with TunnelService: " + t + ( t.getCause() == null ? "" diff --git a/core/src/main/java/com/minekube/connect/register/WatcherRegister.java b/core/src/main/java/com/minekube/connect/register/WatcherRegister.java index 68b2eef3..6aabc574 100644 --- a/core/src/main/java/com/minekube/connect/register/WatcherRegister.java +++ b/core/src/main/java/com/minekube/connect/register/WatcherRegister.java @@ -25,9 +25,9 @@ package com.minekube.connect.register; +import build.buf.connect.Code; +import build.buf.gen.google.rpc.Status; import com.google.inject.Inject; -import com.google.rpc.Code; -import com.google.rpc.Status; import com.minekube.connect.api.SimpleConnectApi; import com.minekube.connect.api.inject.PlatformInjector; import com.minekube.connect.api.logger.ConnectLogger; @@ -157,7 +157,7 @@ public void onProposal(SessionProposal proposal) { logger.info("Got session proposal with empty tunnel service address " + "from WatchService, rejecting it"); proposal.reject(Status.newBuilder() - .setCode(Code.INVALID_ARGUMENT_VALUE) + .setCode(Code.INVALID_ARGUMENT.getValue()) .setMessage("tunnel service address must not be empty") .build()); return; @@ -166,7 +166,7 @@ public void onProposal(SessionProposal proposal) { logger.info("Got session proposal with empty player address " + "from WatchService, rejecting it"); proposal.reject(Status.newBuilder() - .setCode(Code.INVALID_ARGUMENT_VALUE) + .setCode(Code.INVALID_ARGUMENT.getValue()) .setMessage("player address must not be empty") .build()); return; diff --git a/core/src/main/java/com/minekube/connect/util/AutoBind.java b/core/src/main/java/com/minekube/connect/util/AutoBind.java new file mode 100644 index 00000000..f86d9521 --- /dev/null +++ b/core/src/main/java/com/minekube/connect/util/AutoBind.java @@ -0,0 +1,38 @@ +/* + * 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 com.minekube.connect.util; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Automatically binds an instance of itself as an eager singleton during the post-initialise stage + * of the FloodgatePlatform. Add the annotation to a class, and it should automatically create an + * instance and inject it for you. + */ +@Retention(RetentionPolicy.RUNTIME) +public @interface AutoBind { +} \ No newline at end of file diff --git a/core/src/main/java/com/minekube/connect/util/HeaderClientInterceptor.java b/core/src/main/java/com/minekube/connect/util/HeaderClientInterceptor.java new file mode 100644 index 00000000..71cf9912 --- /dev/null +++ b/core/src/main/java/com/minekube/connect/util/HeaderClientInterceptor.java @@ -0,0 +1,33 @@ +package com.minekube.connect.util; + +import com.google.common.collect.Maps; +import io.grpc.CallOptions; +import io.grpc.Channel; +import io.grpc.ClientCall; +import io.grpc.ClientInterceptor; +import io.grpc.ForwardingClientCall.SimpleForwardingClientCall; +import io.grpc.ForwardingClientCallListener.SimpleForwardingClientCallListener; +import io.grpc.Metadata; +import io.grpc.Metadata.Key; +import io.grpc.MethodDescriptor; +import java.util.Map; +import lombok.RequiredArgsConstructor; + +public class HeaderClientInterceptor implements ClientInterceptor { + public HeaderClientInterceptor(Map headers) { + headers.forEach((k, v) -> putHeaders.put(Key.of(k, Metadata.ASCII_STRING_MARSHALLER), v)); + } + public final Map, String> putHeaders = Maps.newHashMap(); + + @Override + public ClientCall interceptCall(MethodDescriptor method, + CallOptions callOptions, Channel next) { + return new SimpleForwardingClientCall(next.newCall(method, callOptions)) { + @Override + public void start(Listener responseListener, Metadata headers) { + putHeaders.forEach(headers::put); + super.start(new SimpleForwardingClientCallListener(responseListener) {}, headers); + } + }; + } +} diff --git a/core/src/main/java/com/minekube/connect/util/Metrics.java b/core/src/main/java/com/minekube/connect/util/Metrics.java index ec1c9a7d..8dcdfe54 100644 --- a/core/src/main/java/com/minekube/connect/util/Metrics.java +++ b/core/src/main/java/com/minekube/connect/util/Metrics.java @@ -1,6 +1,7 @@ package com.minekube.connect.util; import com.google.inject.Inject; +import com.google.inject.name.Named; import com.minekube.connect.api.ConnectApi; import com.minekube.connect.api.logger.ConnectLogger; import com.minekube.connect.config.ConnectConfig; @@ -10,10 +11,8 @@ import java.util.HashMap; import java.util.Locale; import java.util.Map; -import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; -import javax.inject.Named; import org.bstats.MetricsBase; import org.bstats.charts.AdvancedPie; import org.bstats.charts.DrilldownPie; @@ -127,7 +126,7 @@ public final class Metrics { private void appendPlatformData(JsonObjectBuilder builder) { builder.appendField("osName", OS_NAME); - builder.appendField("osArch",OS_ARCH); + builder.appendField("osArch", OS_ARCH); builder.appendField("osVersion", OS_VERSION); builder.appendField("coreCount", CORE_COUNT); } diff --git a/core/src/main/java/com/minekube/connect/util/ReflectionUtils.java b/core/src/main/java/com/minekube/connect/util/ReflectionUtils.java index 33e8a2e1..5954894e 100644 --- a/core/src/main/java/com/minekube/connect/util/ReflectionUtils.java +++ b/core/src/main/java/com/minekube/connect/util/ReflectionUtils.java @@ -291,7 +291,7 @@ public static Object getValue(Object instance, String fieldName) { } /** - * Get the value of a field and cast it to . + * Get the value of a field and cast it to T. * * @param instance the instance to get the value from * @param field the field to get the value from @@ -305,7 +305,7 @@ public static T getCastedValue(Object instance, Field field) { } /** - * Get the value of a field and cast it to . + * Get the value of a field and cast it to T. * * @param instance the instance to get the value from * @param fieldName the field to get the value from diff --git a/core/src/main/java/com/minekube/connect/util/Utils.java b/core/src/main/java/com/minekube/connect/util/Utils.java index 9281162d..8dcf1db6 100644 --- a/core/src/main/java/com/minekube/connect/util/Utils.java +++ b/core/src/main/java/com/minekube/connect/util/Utils.java @@ -37,6 +37,7 @@ import java.io.PrintWriter; import java.io.Reader; import java.io.StringWriter; +import java.lang.annotation.Annotation; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.charset.StandardCharsets; @@ -46,9 +47,11 @@ import java.util.List; import java.util.Locale; import java.util.Properties; +import java.util.Set; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.regex.Pattern; +import java.util.stream.Collectors; public class Utils { private static final Pattern NON_UNIQUE_PREFIX = Pattern.compile("^[a-zA-Z0-9_]{0,16}$"); @@ -255,4 +258,43 @@ public static String humanReadableFormat(Duration duration) { .replaceAll("(\\d[HMS])(?!$)", "$1 ") .toLowerCase(); } + + + /** + * Returns a set of all the classes that are annotated by a given annotation. + * Keep in mind that these are from a set of generated annotations generated + * at compile time by the annotation processor, meaning that arbitrary annotations + * cannot be passed into this method and expected to get a set of classes back. + * + * @param annotationClass the annotation class + * @return a set of all the classes annotated by the given annotation + */ + public static Set> getGeneratedClassesForAnnotation(Class annotationClass) { + return getGeneratedClassesForAnnotation(annotationClass.getName()); + } + + /** + * Returns a set of all the classes that are annotated by a given annotation. + * Keep in mind that these are from a set of generated annotations generated + * at compile time by the annotation processor, meaning that arbitrary annotations + * cannot be passed into this method and expected to have a set of classes + * returned back. + * + * @param input the fully qualified name of the annotation + * @return a set of all the classes annotated by the given annotation + */ + public static Set> getGeneratedClassesForAnnotation(String input) { + try (InputStream annotatedClass = Utils.class.getClassLoader().getResourceAsStream(input); + BufferedReader reader = new BufferedReader(new InputStreamReader(annotatedClass))) { + return reader.lines().map(className -> { + try { + return Class.forName(className); + } catch (ClassNotFoundException ex) { + throw new RuntimeException("Failed to find class for annotation " + input, ex); + } + }).collect(Collectors.toSet()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } } diff --git a/core/src/main/java/com/minekube/connect/watch/SessionProposal.java b/core/src/main/java/com/minekube/connect/watch/SessionProposal.java index a51aa742..72dd68cc 100644 --- a/core/src/main/java/com/minekube/connect/watch/SessionProposal.java +++ b/core/src/main/java/com/minekube/connect/watch/SessionProposal.java @@ -25,19 +25,19 @@ package com.minekube.connect.watch; +import build.buf.gen.minekube.connect.v1alpha1.Session; +import build.buf.gen.google.rpc.Status; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import lombok.Getter; import lombok.RequiredArgsConstructor; -import lombok.Setter; -import minekube.connect.v1alpha1.WatchServiceOuterClass.Session; @RequiredArgsConstructor public class SessionProposal { @Getter private final Session session; - private final Consumer reject; + private final Consumer reject; private final AtomicReference state = new AtomicReference<>(State.ACCEPTED); @@ -46,7 +46,7 @@ public enum State { REJECTED } - public void reject(com.google.rpc.Status reason) { + public void reject(Status reason) { if (state.compareAndSet(State.ACCEPTED, State.REJECTED)) { reject.accept(reason); } diff --git a/core/src/main/java/com/minekube/connect/watch/WatchClient.java b/core/src/main/java/com/minekube/connect/watch/WatchClient.java index eaae047c..f8c66210 100644 --- a/core/src/main/java/com/minekube/connect/watch/WatchClient.java +++ b/core/src/main/java/com/minekube/connect/watch/WatchClient.java @@ -25,15 +25,15 @@ package com.minekube.connect.watch; +import build.buf.gen.minekube.connect.v1alpha1.SessionRejection; +import build.buf.gen.minekube.connect.v1alpha1.SessionRejection.Builder; +import build.buf.gen.minekube.connect.v1alpha1.WatchRequest; +import build.buf.gen.minekube.connect.v1alpha1.WatchResponse; import com.google.inject.Inject; import com.google.inject.name.Named; import com.google.protobuf.InvalidProtocolBufferException; import com.minekube.connect.config.ConnectConfig; import java.io.IOException; -import minekube.connect.v1alpha1.WatchServiceOuterClass.SessionRejection; -import minekube.connect.v1alpha1.WatchServiceOuterClass.SessionRejection.Builder; -import minekube.connect.v1alpha1.WatchServiceOuterClass.WatchRequest; -import minekube.connect.v1alpha1.WatchServiceOuterClass.WatchResponse; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; @@ -44,7 +44,7 @@ import org.jetbrains.annotations.Nullable; public class WatchClient { - private static final String ENDPOINT_HEADER = "Connect-Endpoint"; + public static final String ENDPOINT_HEADER = "Connect-Endpoint"; private static final String ENDPOINT_OFFLINE_MODE_HEADER = ENDPOINT_HEADER + "-Offline-Mode"; private static final String ENDPOINT_PARENTS_HEADER = ENDPOINT_HEADER + "-Parents"; private static final String WATCH_URL = System.getenv().getOrDefault( diff --git a/core/src/main/proto/README.md b/core/src/main/proto/README.md deleted file mode 100644 index 370bf6a5..00000000 --- a/core/src/main/proto/README.md +++ /dev/null @@ -1,3 +0,0 @@ -ProtoBuf definitions are taken from Connect main -repo https://github.com/minekube/connect/tree/main/api. We keep them in sync manually until we have -a more clean way, though changes in our proto are rare. \ No newline at end of file diff --git a/core/src/main/proto/com/minekube/connect/v1alpha1/tunnel_service.proto b/core/src/main/proto/com/minekube/connect/v1alpha1/tunnel_service.proto deleted file mode 100644 index 9ae9bc28..00000000 --- a/core/src/main/proto/com/minekube/connect/v1alpha1/tunnel_service.proto +++ /dev/null @@ -1,24 +0,0 @@ -syntax = "proto3"; - -package minekube.connect.v1alpha1; - -service TunnelService { - // Tunnel the connection of a session. - rpc Tunnel(stream TunnelRequest) returns (stream TunnelResponse); -} - -message TunnelRequest { - // This is a raw client bound chunk of data. - // - // The data is just a chunk of the stream and it should - // never be assumed it contains a complete single packet. - bytes data = 1; -} - -message TunnelResponse { - // This is a raw server bound chunk of data. - // - // The data is just a chunk of the stream and it should - // never be assumed it contains a complete single packet. - bytes data = 1; -} diff --git a/core/src/main/proto/com/minekube/connect/v1alpha1/watch_service.proto b/core/src/main/proto/com/minekube/connect/v1alpha1/watch_service.proto deleted file mode 100644 index 22fb1f03..00000000 --- a/core/src/main/proto/com/minekube/connect/v1alpha1/watch_service.proto +++ /dev/null @@ -1,141 +0,0 @@ -syntax = "proto3"; - -package minekube.connect.v1alpha1; - -import "google/rpc/status.proto"; - -service WatchService { - // Watch watches for session proposals for taking player connections. - rpc Watch(stream WatchRequest) returns (stream WatchResponse); -} - -message WatchRequest { - // Sending this message rejects a session proposed by the WatchService. This message should be sent to inform - // the WatchService that the server will not try to make a take the proposed session. The only purpose of - // this message is to provide quicker feedback to the player that he will not be connected with an optional - // localized reason. See https://github.com/grpc/grpc/blob/master/src/proto/grpc/status/status.proto. - // If the session is not rejected the watcher should establish the connection for the proposed session. - // If neither of these actions happen the proposal times out out and the player receives a connection timeout - // error indicating that the endpoint is currently unavailable. - SessionRejection session_rejection = 1; -} - -message WatchResponse { - // The proposed session that intents to connect. - Session session = 1; -} - -message SessionRejection { - // The id of the proposed session. - string id = 1; - // The optional reason why the proposed session was rejected. - // To specify a user facing localized message refer to - // https://github.com/grpc/grpc/blob/master/src/proto/grpc/status/status.proto - google.rpc.Status reason = 2; -} - -message Session { - // The unique session id. - string id = 1; - // The address of the TunnelService to establish the player connection. - // This allows to establish a direct connection to the TunnelService - // that hosts the player connection and maintain the lowest latency. - string tunnel_service_addr = 2; - // The player of this session that will be connected. - // - // Note that only the profile name might be available if auth.passthrough is enabled - // since the username is sent by the player initially (https://wiki.vg/Protocol#Login_Start). - Player player = 3; - // The authentication termination of the session connection. - // See Authentication for more documentation. - Authentication auth = 4; -} - -message Player { - // The profile of the player. - GameProfile profile = 1; - // The optional IP address of the player. - // This field may be empty or is a fake address - // generated for this particular player. - string addr = 2; -} - -message GameProfile { - // The Minecraft UUID of the player. - string id = 1; - // The Minecraft name of the player. - string name = 2; - // The profile properties that may contain skin data and more. - repeated GameProfileProperty properties = 3; -} - -message GameProfileProperty { - // The name of this property. - string name = 1; - // The value of this property - string value = 2; - // The signature of this property. - string signature = 3; -} - -// The authentication details of a session specifies how the connecting player is authenticated. -// -// The Minecraft account of every session is authenticated using an external auth service that can confirm -// a connecting player is who he claims he is. -// -// The official Minecraft session server (https://wiki.vg/Protocol_Encryption#Authentication) -// is the most commonly used API to authenticate online mode players with a real Minecraft account. -message Authentication { - // Whether authentication is terminated by the TunnelService or the client that calls the TunnelService. - // A TunnelService client can be the Minecraft server that receives the player connection. - // - // True means a player connection is in login state and has not yet received the encryption request packet. - // Whether and how the authentication is done is the responsibility of the target Minecraft server that - // establishes the tunnel to the TunnelService to receive the connecting player. - // Players with a pass-through session cannot be rerouted to another server by the TunnelService, - // but only by the server that established the tunnel because the connection might be encrypted and not - // readable by the TunnelService. - // - // False means a player authentication is offloaded externally (e.g. by the TunnelService that sends the - // player connection) and is not done by the Minecraft server that establishes the tunnel to the TunnelService. - // Players without a pass-through session can be rerouted to another server by the TunnelService as well as - // by the server that established the tunnel. - // - // - // Summary of Pros and Cons: - // - // passthrough=false - // + Maximum flexibility: TunnelService can route players between servers on demand - // + Maximum feature support: TunnelService can fully engage with the player (e.g. send messages) - // - Less authenticity: player authentication is offloaded to the trusted TunnelService - // - Less security: TunnelService has full control over the connection io - // - // passthrough=true - // + Maximum authenticity: player authentication is done locally by the TunnelService client - // + Maximum security: TunnelService cannot spy on the encrypted player connection - // - Less flexibility: TunnelService cannot route a player connection to a different server - // - Less features available: TunnelService only acts as a lightweight reverse proxy with limited features - // - // - // It is a security/authenticity decision whether to accept sessions in non-passthrough - // mode if you cannot trust that the TunnelService the player connection origins from. - // - // This model compares best with a https://en.wikipedia.org/wiki/TLS_termination_proxy. - bool passthrough = 1; - - // The session authentication service used to authenticate a Minecraft player. - // This field serves for session validation and metrics. - // - // In the future there might be a managed service that allows cracked players - // with consistent GameProfile UUIDs and skins where players don't authenticate - // with official Minecraft session server and don't need an official Minecraft - // account. - // - // This feature would invite the whole Minecraft cracked community to play online mode - // servers without a premium account and depends on how legal such service offer is since - // players would no longer need to pay for an account anymore to play on servers in the - // Connect network. - // - // Example value: "sessionserver.mojang.com" - //string auth_service = 2; -} diff --git a/core/src/main/java/com/minekube/connect/util/Constants.java b/core/src/main/templates/com/minekube/connect/util/Constants.java similarity index 79% rename from core/src/main/java/com/minekube/connect/util/Constants.java rename to core/src/main/templates/com/minekube/connect/util/Constants.java index 31c97238..1ab570f7 100644 --- a/core/src/main/java/com/minekube/connect/util/Constants.java +++ b/core/src/main/templates/com/minekube/connect/util/Constants.java @@ -26,19 +26,19 @@ package com.minekube.connect.util; public final class Constants { - public static final String VERSION = "${connectVersion}"; - public static final int BUILD_NUMBER = Integer.parseInt("${buildNumber}"); - public static final String GIT_BRANCH = "${branch}"; + public static final String VERSION = "@connectVersion@"; + public static final int BUILD_NUMBER = Integer.parseInt("@buildNumber@"); + public static final String GIT_BRANCH = "@branch@"; public static final int METRICS_ID = 14794; - public static final char COLOR_CHAR = '§'; + public static final char COLOR_CHAR = '\u00A7'; // § public static final boolean DEBUG_MODE = false; public static final boolean PRINT_ALL_PACKETS = false; - private static final String API_BASE_URL = "s://api.geysermc.org"; - public static final String HEALTH_URL = "http" + API_BASE_URL + "/health"; + public static final String CONNECT_API_TARGET = "connect-api.minekube.com"; + public static final String CONNECT_RANDOM_NAME_URL = "https://randomname.minekube.net"; public static final int HANDSHAKE_PACKET_ID = 0; public static final int LOGIN_SUCCESS_PACKET_ID = 2; diff --git a/gradle.properties b/gradle.properties index af7d8325..9077317b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,6 @@ org.gradle.configureondemand=true org.gradle.caching=true -org.gradle.parallel=true \ No newline at end of file +org.gradle.parallel=true + +# Connect plugin version +version=0.5.0 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7454180f..249e5832 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 2e6e5897..070cb702 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 1b6c7873..a69d9cb6 100755 --- a/gradlew +++ b/gradlew @@ -205,6 +205,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..f127cfd4 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,7 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,7 +40,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 +75,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/settings.gradle.kts b/settings.gradle.kts index 72878ade..67050969 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,9 +1,12 @@ +@file:Suppress("UnstableApiUsage") enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { - // Geyser, Cumulus etc. TODO remove + mavenLocal() + + // Geyser, Cumulus etc. maven("https://repo.opencollab.dev/maven-releases") { mavenContent { releasesOnly() } } @@ -12,10 +15,12 @@ dependencyResolutionManagement { } // Paper, Velocity - maven("https://papermc.io/repo/repository/maven-public") - // Spigot - maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots") { - mavenContent { snapshotsOnly() } + maven("https://repo.papermc.io/repository/maven-public") { + content { + includeGroupByRegex( + "(io\\.papermc\\..*|com\\.destroystokyo\\..*|com\\.velocitypowered)" + ) + } } // BungeeCord @@ -32,12 +37,17 @@ dependencyResolutionManagement { maven("https://repo.viaversion.com") { name = "viaversion-repo" + content { includeGroupByRegex("com\\.viaversion\\.*") } } maven("https://jitpack.io") { content { includeGroupByRegex("com\\.github\\..*") } } + maven("https://buf.build/gen/maven") { + name = "buf" + } + } } @@ -45,14 +55,9 @@ pluginManagement { repositories { gradlePluginPortal() } - repositories { - maven("https://plugins.gradle.org/m2/") - } plugins { - id("net.kyori.blossom") version "1.2.0" id("net.kyori.indra") id("net.kyori.indra.git") - id("com.google.protobuf") version "0.8.18" } includeBuild("build-logic") } @@ -60,6 +65,7 @@ pluginManagement { rootProject.name = "connect-parent" include(":api") +include(":ap") include(":core") include(":bungee") include(":spigot") diff --git a/spigot/src/main/java/com/minekube/connect/SpigotPlugin.java b/spigot/src/main/java/com/minekube/connect/SpigotPlugin.java index 16dc3177..6210ae05 100644 --- a/spigot/src/main/java/com/minekube/connect/SpigotPlugin.java +++ b/spigot/src/main/java/com/minekube/connect/SpigotPlugin.java @@ -42,7 +42,7 @@ import org.bukkit.plugin.java.JavaPlugin; public final class SpigotPlugin extends JavaPlugin { - private SpigotPlatform platform; + private ConnectPlatform platform; private Injector injector; @Override @@ -53,7 +53,7 @@ public void onLoad() { new SpigotPlatformModule(this) ); - platform = injector.getInstance(SpigotPlatform.class); + platform = injector.getInstance(ConnectPlatform.class); long endCtm = System.currentTimeMillis(); injector.getInstance(ConnectLogger.class) @@ -65,14 +65,17 @@ public void onEnable() { boolean usePaperListener = ReflectionUtils.getClassSilently( "com.destroystokyo.paper.event.profile.PreFillProfileEvent") != null; - platform.enable( - new SpigotCommandModule(this), - new SpigotAddonModule(), - (usePaperListener ? new PaperListenerModule() : new SpigotListenerModule()), - new WatcherModule() - ); - - //todo add proper support for disabling things on shutdown and enabling this on enable + try { + platform.enable( + new SpigotCommandModule(this), + new SpigotAddonModule(), + (usePaperListener ? new PaperListenerModule() : new SpigotListenerModule()), + new WatcherModule() + ); + } catch (Exception exception) { + Bukkit.getPluginManager().disablePlugin(this); + throw exception; + } // add ProtocolSupport support (hack) if (isProtocolSupport()) { diff --git a/spigot/src/main/java/com/minekube/connect/inject/spigot/SpigotInjector.java b/spigot/src/main/java/com/minekube/connect/inject/spigot/SpigotInjector.java index c852fb6a..875e062d 100644 --- a/spigot/src/main/java/com/minekube/connect/inject/spigot/SpigotInjector.java +++ b/spigot/src/main/java/com/minekube/connect/inject/spigot/SpigotInjector.java @@ -25,6 +25,9 @@ package com.minekube.connect.inject.spigot; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import com.google.inject.name.Named; import com.minekube.connect.api.logger.ConnectLogger; import com.minekube.connect.inject.CommonPlatformInjector; import com.minekube.connect.network.netty.LocalServerChannelWrapper; @@ -52,17 +55,18 @@ import java.util.List; import java.util.NoSuchElementException; import lombok.Getter; -import lombok.RequiredArgsConstructor; import org.checkerframework.checker.nullness.qual.NonNull; -@RequiredArgsConstructor +@Singleton public final class SpigotInjector extends CommonPlatformInjector { - private final ConnectLogger logger; + @Inject private ConnectLogger logger; /** * Used to determine if ViaVersion is set up to a state where Connect players will fail at * joining if injection is enabled */ - private final boolean isViaVersion; + @Inject + @Named("isViaVersion") + private boolean isViaVersion; /** * Used to uninject ourselves on shutdown. */ @@ -90,9 +94,9 @@ public final class SpigotInjector extends CommonPlatformInjector { @Override @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter") - public boolean inject() throws Exception { + public void inject() throws Exception { if (isInjected()) { - return true; + return; } // disableEnforceSecureProfile(); @@ -137,11 +141,10 @@ public void onAdd(Object object) { field.set(serverConnection, newList); injected = true; - return true; + return; } } } - return false; } public void injectClient(ChannelFuture future) { diff --git a/spigot/src/main/java/com/minekube/connect/module/SpigotPlatformModule.java b/spigot/src/main/java/com/minekube/connect/module/SpigotPlatformModule.java index 9f38b641..f8fd4504 100644 --- a/spigot/src/main/java/com/minekube/connect/module/SpigotPlatformModule.java +++ b/spigot/src/main/java/com/minekube/connect/module/SpigotPlatformModule.java @@ -31,6 +31,7 @@ import com.google.inject.Provides; import com.google.inject.Singleton; import com.google.inject.name.Named; +import com.google.inject.name.Names; import com.minekube.connect.SpigotPlugin; import com.minekube.connect.api.ConnectApi; import com.minekube.connect.api.logger.ConnectLogger; @@ -47,6 +48,7 @@ import com.minekube.connect.util.SpigotCommandUtil; import com.minekube.connect.util.SpigotPlatformUtils; import com.minekube.connect.util.SpigotVersionSpecificMethods; +import java.util.logging.Logger; import lombok.RequiredArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.event.Listener; @@ -58,7 +60,11 @@ public final class SpigotPlatformModule extends AbstractModule { @Override protected void configure() { + bind(SpigotPlugin.class).toInstance(plugin); bind(PlatformUtils.class).to(SpigotPlatformUtils.class); + bind(CommonPlatformInjector.class).to(SpigotInjector.class); + bind(Logger.class).annotatedWith(Names.named("logger")).toInstance(plugin.getLogger()); + bind(ConnectLogger.class).to(JavaUtilConnectLogger.class); } @Provides @@ -67,12 +73,6 @@ public JavaPlugin javaPlugin() { return plugin; } - @Provides - @Singleton - public ConnectLogger logger(LanguageManager languageManager) { - return new JavaUtilConnectLogger(plugin.getLogger(), languageManager); - } - /* Commands / Listeners */ @@ -103,7 +103,8 @@ public ListenerRegistration listenerRegistration() { @Provides @Singleton - public CommonPlatformInjector platformInjector(ConnectLogger logger) { + @Named("isViaVersion") + public boolean isViaVersion(ConnectLogger logger) { final String VIAVERSION_DOWNLOAD_URL = "https://ci.viaversion.com/job/ViaVersion/"; boolean isViaVersion = Bukkit.getPluginManager().getPlugin("ViaVersion") != null; if (isViaVersion) { @@ -115,8 +116,7 @@ public CommonPlatformInjector platformInjector(ConnectLogger logger) { isViaVersion = false; } } - - return new SpigotInjector(logger, isViaVersion); + return isViaVersion; } @Provides diff --git a/velocity/build.gradle.kts b/velocity/build.gradle.kts index 2ddcfae5..5cd7064a 100644 --- a/velocity/build.gradle.kts +++ b/velocity/build.gradle.kts @@ -1,4 +1,4 @@ -var velocityVersion = "3.0.1" +var velocityVersion = "3.1.1" var log4jVersion = "2.11.2" var gsonVersion = "2.8.8" var guavaVersion = "25.1-jre" diff --git a/velocity/src/main/java/com/minekube/connect/VelocityPlugin.java b/velocity/src/main/java/com/minekube/connect/VelocityPlugin.java index 5fbac9f8..f8813249 100644 --- a/velocity/src/main/java/com/minekube/connect/VelocityPlugin.java +++ b/velocity/src/main/java/com/minekube/connect/VelocityPlugin.java @@ -36,6 +36,7 @@ import com.minekube.connect.util.ReflectionUtils; import com.velocitypowered.api.event.Subscribe; import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; +import com.velocitypowered.api.event.proxy.ProxyShutdownEvent; import com.velocitypowered.api.plugin.annotation.DataDirectory; import java.nio.file.Path; @@ -68,4 +69,9 @@ public void onProxyInitialization(ProxyInitializeEvent event) { new WatcherModule() ); } + + @Subscribe + public void onProxyShutdown(ProxyShutdownEvent event) { + platform.disable(); + } } diff --git a/velocity/src/main/java/com/minekube/connect/inject/velocity/VelocityInjector.java b/velocity/src/main/java/com/minekube/connect/inject/velocity/VelocityInjector.java index 6f3797e0..3453edc1 100644 --- a/velocity/src/main/java/com/minekube/connect/inject/velocity/VelocityInjector.java +++ b/velocity/src/main/java/com/minekube/connect/inject/velocity/VelocityInjector.java @@ -54,9 +54,9 @@ public final class VelocityInjector extends CommonPlatformInjector { @Override @SuppressWarnings("rawtypes") - public boolean inject() { + public void inject() { if (isInjected()) { - return true; + return; } Object connectionManager = getValue(server, "cm"); @@ -104,7 +104,7 @@ public boolean inject() { // End of logic from GeyserMC - return injected = true; + injected = true; } @RequiredArgsConstructor diff --git a/velocity/src/main/java/com/minekube/connect/logger/Slf4JConnectLogger.java b/velocity/src/main/java/com/minekube/connect/logger/Slf4JConnectLogger.java index 23e06f1c..67605fcc 100644 --- a/velocity/src/main/java/com/minekube/connect/logger/Slf4JConnectLogger.java +++ b/velocity/src/main/java/com/minekube/connect/logger/Slf4JConnectLogger.java @@ -27,17 +27,27 @@ import static com.minekube.connect.util.MessageFormatter.format; +import com.google.inject.Inject; +import com.google.inject.Singleton; import com.minekube.connect.api.logger.ConnectLogger; +import com.minekube.connect.config.ConnectConfig; import com.minekube.connect.util.LanguageManager; -import lombok.RequiredArgsConstructor; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.config.Configurator; import org.slf4j.Logger; -@RequiredArgsConstructor +@Singleton public final class Slf4JConnectLogger implements ConnectLogger { - private final Logger logger; - private final LanguageManager languageManager; + @Inject private Logger logger; + private LanguageManager languageManager; + + @Inject + private void init(LanguageManager languageManager, ConnectConfig config) { + this.languageManager = languageManager; + if (config.isDebug() && !logger.isDebugEnabled()) { + Configurator.setLevel(logger.getName(), Level.DEBUG); + } + } @Override public void error(String message, Object... args) { @@ -74,20 +84,6 @@ public void trace(String message, Object... args) { logger.trace(message, args); } - @Override - public void enableDebug() { - if (!logger.isDebugEnabled()) { - Configurator.setLevel(logger.getName(), Level.DEBUG); - } - } - - @Override - public void disableDebug() { - if (logger.isDebugEnabled()) { - Configurator.setLevel(logger.getName(), Level.INFO); - } - } - @Override public boolean isDebug() { return logger.isDebugEnabled(); diff --git a/velocity/src/main/java/com/minekube/connect/module/VelocityPlatformModule.java b/velocity/src/main/java/com/minekube/connect/module/VelocityPlatformModule.java index cfa336a5..e50aedf3 100644 --- a/velocity/src/main/java/com/minekube/connect/module/VelocityPlatformModule.java +++ b/velocity/src/main/java/com/minekube/connect/module/VelocityPlatformModule.java @@ -47,7 +47,6 @@ import com.minekube.connect.player.ConnectCommandPreprocessor; import com.minekube.connect.player.UserAudience; import com.minekube.connect.skin.SkinApplier; -import com.minekube.connect.util.LanguageManager; import com.minekube.connect.util.VelocityCommandUtil; import com.minekube.connect.util.VelocityPlatformUtils; import com.minekube.connect.util.VelocitySkinApplier; @@ -55,7 +54,6 @@ import com.velocitypowered.api.event.EventManager; import com.velocitypowered.api.proxy.ProxyServer; import lombok.RequiredArgsConstructor; -import org.slf4j.Logger; @RequiredArgsConstructor public final class VelocityPlatformModule extends AbstractModule { @@ -65,6 +63,8 @@ public final class VelocityPlatformModule extends AbstractModule { protected void configure() { bind(CommandUtil.class).to(VelocityCommandUtil.class); bind(PlatformUtils.class).to(VelocityPlatformUtils.class); + bind(ConnectLogger.class).to(Slf4JConnectLogger.class); + bind(SkinApplier.class).to(VelocitySkinApplier.class); } @Provides @@ -84,12 +84,6 @@ public CommandManager commandManager(CommandUtil commandUtil) { return commandManager; } - @Provides - @Singleton - public ConnectLogger logger(Logger logger, LanguageManager languageManager) { - return new Slf4JConnectLogger(logger, languageManager); - } - /* Commands / Listeners */