diff --git a/.github/workflows/pullrequest.yml b/.github/workflows/pullrequest.yml index 9621fa1d0..9d925c4dc 100644 --- a/.github/workflows/pullrequest.yml +++ b/.github/workflows/pullrequest.yml @@ -9,47 +9,43 @@ jobs: steps: - uses: actions/checkout@v2 - - uses: actions/cache@v2 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- - - name: Set up JDK 16 + - name: Set up JDK 17 uses: actions/setup-java@v1 with: - java-version: 16 + distribution: 'temurin' + java-version: 17 + cache: 'gradle' - name: submodules-init uses: snickerbockers/submodules-init@v4 - - name: Build with Maven - run: mvn -B package -T 2C + - name: Build with Gradle + run: ./gradlew build - name: Archive artifacts (Geyser Standalone) uses: actions/upload-artifact@v2 if: success() with: name: Geyser Standalone - path: bootstrap/standalone/target/Geyser.jar + path: bootstrap/standalone/build/libs/Geyser.jar - name: Archive artifacts (Geyser Spigot) uses: actions/upload-artifact@v2 if: success() with: name: Geyser Spigot - path: bootstrap/spigot/target/Geyser-Spigot.jar + path: bootstrap/spigot/build/libs/Geyser-Spigot.jar - name: Archive artifacts (Geyser BungeeCord) uses: actions/upload-artifact@v2 if: success() with: name: Geyser BungeeCord - path: bootstrap/bungeecord/target/Geyser-BungeeCord.jar + path: bootstrap/bungeecord/build/libs/Geyser-BungeeCord.jar - name: Archive artifacts (Geyser Sponge) uses: actions/upload-artifact@v2 if: success() with: name: Geyser Sponge - path: bootstrap/sponge/target/Geyser-Sponge.jar + path: bootstrap/sponge/build/libs/Geyser-Sponge.jar - name: Archive artifacts (Geyser Velocity) uses: actions/upload-artifact@v2 if: success() with: name: Geyser Velocity - path: bootstrap/velocity/target/Geyser-Velocity.jar + path: bootstrap/velocity/build/libs/Geyser-Velocity.jar diff --git a/.gitignore b/.gitignore index f1baa3abb..2b7e2972c 100644 --- a/.gitignore +++ b/.gitignore @@ -235,8 +235,12 @@ nbdist/ # End of https://www.gitignore.io/api/git,java,maven,eclipse,netbeans,jetbrains+all,visualstudiocode +### Gradle ### +.gradle + ### Geyser ### run/ +extensions/ config.yml logs/ key.pem @@ -245,4 +249,5 @@ locales/ /packs/ /dump.json /saved-refresh-tokens.json -/languages/ \ No newline at end of file +/custom_mappings/ +/languages/ diff --git a/Jenkinsfile b/Jenkinsfile index 1a98f47ad..28f9e7a37 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,8 +1,8 @@ pipeline { agent any tools { - maven 'Maven 3' - jdk 'Java 16' + gradle 'Gradle 7' + jdk 'Java 17' } options { buildDiscarder(logRotator(artifactNumToKeepStr: '20')) @@ -11,11 +11,16 @@ pipeline { stage ('Build') { steps { sh 'git submodule update --init --recursive' - sh 'mvn clean package' + rtGradleRun( + usesPlugin: true, + tool: 'Gradle 7', + buildFile: 'build.gradle.kts', + tasks: 'clean build', + ) } post { success { - archiveArtifacts artifacts: 'bootstrap/**/target/*.jar', excludes: 'bootstrap/**/target/original-*.jar', fingerprint: true + archiveArtifacts artifacts: 'bootstrap/**/build/libs/*.jar', excludes: 'bootstrap/**/build/libs/*-sources.jar,bootstrap/**/build/libs/*-unshaded.jar', fingerprint: true } } } @@ -24,27 +29,30 @@ pipeline { when { anyOf { branch "master" + branch "feature/extensions" } } steps { - rtMavenDeployer( - id: "maven-deployer", + rtGradleDeployer( + id: "GRADLE_DEPLOYER", serverId: "opencollab-artifactory", releaseRepo: "maven-releases", snapshotRepo: "maven-snapshots" ) - rtMavenResolver( - id: "maven-resolver", - serverId: "opencollab-artifactory", - releaseRepo: "maven-deploy-release", - snapshotRepo: "maven-deploy-snapshot" + rtGradleResolver( + id: "GRADLE_RESOLVER", + serverId: "opencollab-artifactory" ) - rtMavenRun( - pom: 'pom.xml', - goals: 'javadoc:jar source:jar install -pl :core -am -DskipTests', - deployerId: "maven-deployer", - resolverId: "maven-resolver" + rtGradleRun( + usesPlugin: true, + tool: 'Gradle 7', + rootDir: "", + useWrapper: true, + buildFile: 'build.gradle.kts', + tasks: 'build artifactoryPublish', + deployerId: "GRADLE_DEPLOYER", + resolverId: "GRADLE_RESOLVER" ) rtPublishBuildInfo( serverId: "opencollab-artifactory" diff --git a/README.md b/README.md index 464e67d76..d02d50d2b 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,6 @@ Take a look [here](https://wiki.geysermc.org/geyser/setup/) for how to set up Ge ## What's Left to be Added/Fixed - Near-perfect movement (to the point where anticheat on large servers is unlikely to ban you) -- Resource pack conversion/CustomModelData - Some Entity Flags - Structure block UI @@ -43,9 +42,8 @@ There are a few things Geyser is unable to support due to various differences be ## Compiling 1. Clone the repo to your computer -2. [Install Maven](https://maven.apache.org/install.html) -3. Navigate to the Geyser root directory and run `git submodule update --init --recursive`. This command downloads all the needed submodules for Geyser and is a crucial step in this process. -4. Run `mvn clean install` and locate to the `target` folder. +2. Navigate to the Geyser root directory and run `git submodule update --init --recursive`. This command downloads all the needed submodules for Geyser and is a crucial step in this process. +3. Run `gradlew build` and locate to `bootstrap/build` folder. ## Contributing Any contributions are appreciated. Please feel free to reach out to us on [Discord](http://discord.geysermc.org/) if diff --git a/ap/build.gradle.kts b/ap/build.gradle.kts new file mode 100644 index 000000000..e69de29bb diff --git a/ap/pom.xml b/ap/pom.xml deleted file mode 100644 index feb77e922..000000000 --- a/ap/pom.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - 4.0.0 - - org.geysermc - geyser-parent - 2.0.7-SNAPSHOT - - - ap - 2.0.7-SNAPSHOT - \ No newline at end of file diff --git a/ap/src/main/java/org/geysermc/processor/BlockEntityProcessor.java b/ap/src/main/java/org/geysermc/geyser/processor/BlockEntityProcessor.java similarity index 97% rename from ap/src/main/java/org/geysermc/processor/BlockEntityProcessor.java rename to ap/src/main/java/org/geysermc/geyser/processor/BlockEntityProcessor.java index 7ab760cec..f9ba68302 100644 --- a/ap/src/main/java/org/geysermc/processor/BlockEntityProcessor.java +++ b/ap/src/main/java/org/geysermc/geyser/processor/BlockEntityProcessor.java @@ -23,7 +23,7 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.processor; +package org.geysermc.geyser.processor; import javax.annotation.processing.SupportedAnnotationTypes; import javax.annotation.processing.SupportedSourceVersion; diff --git a/ap/src/main/java/org/geysermc/processor/ClassProcessor.java b/ap/src/main/java/org/geysermc/geyser/processor/ClassProcessor.java similarity index 96% rename from ap/src/main/java/org/geysermc/processor/ClassProcessor.java rename to ap/src/main/java/org/geysermc/geyser/processor/ClassProcessor.java index a6259a853..e1da50f25 100644 --- a/ap/src/main/java/org/geysermc/processor/ClassProcessor.java +++ b/ap/src/main/java/org/geysermc/geyser/processor/ClassProcessor.java @@ -23,7 +23,7 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.processor; +package org.geysermc.geyser.processor; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.ProcessingEnvironment; @@ -39,6 +39,7 @@ 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; @@ -163,10 +164,15 @@ public class ClassProcessor extends AbstractProcessor { this.processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Reading existing " + this.annotationClassName + " list from " + this.outputPath); return Files.newBufferedReader(this.outputPath); } + FileObject obj = this.processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT, "", this.annotationClassName); if (obj != null) { this.processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Reading existing " + this.annotationClassName + " list from " + obj.toUri()); - return new BufferedReader(obj.openReader(false)); + try { + return new BufferedReader(obj.openReader(false)); + } catch (NoSuchFileException ignored) { + return null; + } } return null; } diff --git a/ap/src/main/java/org/geysermc/processor/CollisionRemapperProcessor.java b/ap/src/main/java/org/geysermc/geyser/processor/CollisionRemapperProcessor.java similarity index 97% rename from ap/src/main/java/org/geysermc/processor/CollisionRemapperProcessor.java rename to ap/src/main/java/org/geysermc/geyser/processor/CollisionRemapperProcessor.java index 971abd984..84e2e2ffd 100644 --- a/ap/src/main/java/org/geysermc/processor/CollisionRemapperProcessor.java +++ b/ap/src/main/java/org/geysermc/geyser/processor/CollisionRemapperProcessor.java @@ -23,7 +23,7 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.processor; +package org.geysermc.geyser.processor; import javax.annotation.processing.SupportedAnnotationTypes; import javax.annotation.processing.SupportedSourceVersion; diff --git a/ap/src/main/java/org/geysermc/processor/ItemRemapperProcessor.java b/ap/src/main/java/org/geysermc/geyser/processor/ItemRemapperProcessor.java similarity index 97% rename from ap/src/main/java/org/geysermc/processor/ItemRemapperProcessor.java rename to ap/src/main/java/org/geysermc/geyser/processor/ItemRemapperProcessor.java index 39d5f9fdf..2dd00506d 100644 --- a/ap/src/main/java/org/geysermc/processor/ItemRemapperProcessor.java +++ b/ap/src/main/java/org/geysermc/geyser/processor/ItemRemapperProcessor.java @@ -23,7 +23,7 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.processor; +package org.geysermc.geyser.processor; import javax.annotation.processing.SupportedAnnotationTypes; import javax.annotation.processing.SupportedSourceVersion; diff --git a/ap/src/main/java/org/geysermc/processor/PacketTranslatorProcessor.java b/ap/src/main/java/org/geysermc/geyser/processor/PacketTranslatorProcessor.java similarity index 97% rename from ap/src/main/java/org/geysermc/processor/PacketTranslatorProcessor.java rename to ap/src/main/java/org/geysermc/geyser/processor/PacketTranslatorProcessor.java index 97687e981..9b99d679b 100644 --- a/ap/src/main/java/org/geysermc/processor/PacketTranslatorProcessor.java +++ b/ap/src/main/java/org/geysermc/geyser/processor/PacketTranslatorProcessor.java @@ -23,7 +23,7 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.processor; +package org.geysermc.geyser.processor; import javax.annotation.processing.SupportedAnnotationTypes; import javax.annotation.processing.SupportedSourceVersion; diff --git a/ap/src/main/java/org/geysermc/processor/SoundHandlerProcessor.java b/ap/src/main/java/org/geysermc/geyser/processor/SoundHandlerProcessor.java similarity index 97% rename from ap/src/main/java/org/geysermc/processor/SoundHandlerProcessor.java rename to ap/src/main/java/org/geysermc/geyser/processor/SoundHandlerProcessor.java index 3e6a7c412..c35c0ee4e 100644 --- a/ap/src/main/java/org/geysermc/processor/SoundHandlerProcessor.java +++ b/ap/src/main/java/org/geysermc/geyser/processor/SoundHandlerProcessor.java @@ -23,7 +23,7 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.processor; +package org.geysermc.geyser.processor; import javax.annotation.processing.SupportedAnnotationTypes; import javax.annotation.processing.SupportedSourceVersion; 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 000000000..1f6475b61 --- /dev/null +++ b/ap/src/main/resources/META-INF/services/javax.annotation.processing.Processor @@ -0,0 +1,5 @@ +org.geysermc.geyser.processor.BlockEntityProcessor +org.geysermc.geyser.processor.CollisionRemapperProcessor +org.geysermc.geyser.processor.ItemRemapperProcessor +org.geysermc.geyser.processor.PacketTranslatorProcessor +org.geysermc.geyser.processor.SoundHandlerProcessor \ No newline at end of file diff --git a/api/base/build.gradle.kts b/api/base/build.gradle.kts new file mode 100644 index 000000000..a6fa608cc --- /dev/null +++ b/api/base/build.gradle.kts @@ -0,0 +1,7 @@ +dependencies { + api("org.geysermc.cumulus", "cumulus", Versions.cumulusVersion) + api("org.geysermc.event", "events", Versions.eventsVersion) { + exclude(group = "com.google.guava", module = "guava") + exclude(group = "org.lanternpowered", module = "lmbda") + } +} \ No newline at end of file diff --git a/api/base/pom.xml b/api/base/pom.xml deleted file mode 100644 index 4e172650e..000000000 --- a/api/base/pom.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - org.geysermc - api-parent - 2.0.7-SNAPSHOT - - 4.0.0 - - base-api - - - 16 - 16 - - - - - org.checkerframework - checker-qual - 3.19.0 - provided - - - \ No newline at end of file diff --git a/api/base/src/main/java/org/geysermc/api/Geyser.java b/api/base/src/main/java/org/geysermc/api/Geyser.java index 9f315faf4..7543d1661 100644 --- a/api/base/src/main/java/org/geysermc/api/Geyser.java +++ b/api/base/src/main/java/org/geysermc/api/Geyser.java @@ -39,6 +39,7 @@ public class Geyser { * * @return the base api */ + @NonNull public static GeyserApiBase api() { if (api == null) { throw new RuntimeException("Api has not been registered yet!"); @@ -69,7 +70,7 @@ public class Geyser { /** * Registers the given api type. The api cannot be - * registered if {@link #registered()} is true as + * registered if {@link #isRegistered()} is true as * an api has already been specified. * * @param api the api @@ -88,7 +89,7 @@ public class Geyser { * * @return if the api has been registered */ - public static boolean registered() { + public static boolean isRegistered() { return api != null; } } diff --git a/api/base/src/main/java/org/geysermc/api/GeyserApiBase.java b/api/base/src/main/java/org/geysermc/api/GeyserApiBase.java index 3549a912a..a845e37fd 100644 --- a/api/base/src/main/java/org/geysermc/api/GeyserApiBase.java +++ b/api/base/src/main/java/org/geysermc/api/GeyserApiBase.java @@ -25,9 +25,13 @@ package org.geysermc.api; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; -import org.geysermc.api.session.Connection; +import org.checkerframework.common.value.qual.IntRange; +import org.geysermc.api.connection.Connection; +import org.geysermc.cumulus.form.Form; +import org.geysermc.cumulus.form.util.FormBuilder; import java.util.List; import java.util.UUID; @@ -37,52 +41,88 @@ import java.util.UUID; */ public interface GeyserApiBase { /** - * Gets the session from the given UUID, if applicable. The player must be logged in to the Java server + * Gets the connection from the given UUID, if applicable. The player must be logged in to the Java server * for this to return a non-null value. * - * @param uuid the UUID of the session - * @return the session from the given UUID, if applicable + * @param uuid the UUID of the connection + * @return the connection from the given UUID, if applicable */ @Nullable Connection connectionByUuid(@NonNull UUID uuid); /** - * Gets the session from the given - * XUID, if applicable. + * Gets the connection from the given XUID, if applicable. This method only works for online connections. * * @param xuid the XUID of the session - * @return the session from the given UUID, if applicable + * @return the connection from the given UUID, if applicable */ @Nullable Connection connectionByXuid(@NonNull String xuid); /** - * Gets the session from the given - * name, if applicable. + * Method to determine if the given online player is a Bedrock player. * - * @param name the uuid of the session - * @return the session from the given name, if applicable + * @param uuid the uuid of the online player + * @return true if the given online player is a Bedrock player */ - @Nullable - Connection connectionByName(@NonNull String name); + boolean isBedrockPlayer(@NonNull UUID uuid); /** - * Gets all the online sessions. + * Sends a form to the given connection and opens it. * - * @return all the online sessions + * @param uuid the uuid of the connection to open it on + * @param form the form to send + * @return whether the form was successfully sent + */ + boolean sendForm(@NonNull UUID uuid, @NonNull Form form); + + /** + * Sends a form to the given connection and opens it. + * + * @param uuid the uuid of the connection to open it on + * @param formBuilder the formBuilder to send + * @return whether the form was successfully sent + */ + boolean sendForm(@NonNull UUID uuid, @NonNull FormBuilder formBuilder); + + /** + * Transfer the given connection to a server. A Bedrock player can successfully transfer to the same server they are + * currently playing on. + * + * @param uuid the uuid of the connection + * @param address the address of the server + * @param port the port of the server + * @return true if the transfer was a success + */ + boolean transfer(@NonNull UUID uuid, @NonNull String address, @IntRange(from = 0, to = 65535) int port); + + + /** + * Returns all the online connections. */ @NonNull List onlineConnections(); /** - * @return the major API version. Bumped whenever a significant breaking change or feature addition is added. + * Returns the amount of online connections. + */ + int onlineConnectionsCount(); + + /** + * Returns the prefix used by Floodgate. Will be null when the auth-type isn't Floodgate. + */ + @MonotonicNonNull + String usernamePrefix(); + + /** + * Returns the major API version. Bumped whenever a significant breaking change or feature addition is added. */ default int majorApiVersion() { - return 0; + return 1; } /** - * @return the minor API version. May be bumped for new API additions. + * Returns the minor API version. May be bumped for new API additions. */ default int minorApiVersion() { return 0; diff --git a/api/base/src/main/java/org/geysermc/api/connection/Connection.java b/api/base/src/main/java/org/geysermc/api/connection/Connection.java new file mode 100644 index 000000000..1cd7a9d13 --- /dev/null +++ b/api/base/src/main/java/org/geysermc/api/connection/Connection.java @@ -0,0 +1,121 @@ +/* + * 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/Geyser + */ + +package org.geysermc.api.connection; + +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.common.value.qual.IntRange; +import org.geysermc.api.util.BedrockPlatform; +import org.geysermc.api.util.InputMode; +import org.geysermc.api.util.UiProfile; +import org.geysermc.cumulus.form.Form; +import org.geysermc.cumulus.form.util.FormBuilder; + +import java.util.UUID; + +/** + * Represents a player connection. + */ +public interface Connection { + /** + * Returns the bedrock name of the connection. + */ + @NonNull String bedrockUsername(); + + /** + * Returns the java name of the connection. + */ + @MonotonicNonNull + String javaUsername(); + + /** + * Returns the UUID of the connection. + */ + @MonotonicNonNull + UUID javaUuid(); + + /** + * Returns the XUID of the connection. + */ + @NonNull String xuid(); + + /** + * Returns the version of the Bedrock client. + */ + @NonNull String version(); + + /** + * Returns the platform that the connection is playing on. + */ + @NonNull BedrockPlatform platform(); + + /** + * Returns the language code of the connection. + */ + @NonNull String languageCode(); + + /** + * Returns the User Interface Profile of the connection. + */ + @NonNull UiProfile uiProfile(); + + /** + * Returns the Input Mode of the Bedrock client. + */ + @NonNull InputMode inputMode(); + + /** + * Returns whether the connection is linked. + * This will always return false when the auth-type isn't Floodgate. + */ + boolean isLinked(); + + /** + * Sends a form to the connection and opens it. + * + * @param form the form to send + * @return whether the form was successfully sent + */ + boolean sendForm(@NonNull Form form); + + /** + * Sends a form to the connection and opens it. + * + * @param formBuilder the formBuilder to send + * @return whether the form was successfully sent + */ + boolean sendForm(@NonNull FormBuilder formBuilder); + + /** + * Transfer the connection to a server. A Bedrock player can successfully transfer to the same server they are + * currently playing on. + * + * @param address the address of the server + * @param port the port of the server + * @return true if the transfer was a success + */ + boolean transfer(@NonNull String address, @IntRange(from = 0, to = 65535) int port); +} diff --git a/api/base/src/main/java/org/geysermc/api/util/BedrockPlatform.java b/api/base/src/main/java/org/geysermc/api/util/BedrockPlatform.java new file mode 100644 index 000000000..e486f73bc --- /dev/null +++ b/api/base/src/main/java/org/geysermc/api/util/BedrockPlatform.java @@ -0,0 +1,73 @@ +/* + * 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/Geyser + */ + +package org.geysermc.api.util; + +import org.checkerframework.checker.nullness.qual.NonNull; + +public enum BedrockPlatform { + UNKNOWN("Unknown"), + GOOGLE("Android"), + IOS("iOS"), + OSX("macOS"), + AMAZON("Amazon"), + GEARVR("Gear VR"), + HOLOLENS("Hololens"), + UWP("Windows 10"), + WIN32("Windows x86"), + DEDICATED("Dedicated"), + TVOS("Apple TV"), + PS4("PS4"), + NX("Switch"), + XBOX("Xbox One"), + WINDOWS_PHONE("Windows Phone"); + + private static final BedrockPlatform[] VALUES = values(); + + private final String displayName; + + BedrockPlatform(String displayName) { + this.displayName = displayName; + } + + /** + * Get the BedrockPlatform from the identifier. + * + * @param id the BedrockPlatform identifier + * @return The BedrockPlatform or {@link #UNKNOWN} if the platform wasn't found + */ + @NonNull + public static BedrockPlatform fromId(int id) { + return id < VALUES.length ? VALUES[id] : VALUES[0]; + } + + /** + * @return friendly display name of platform. + */ + @Override + public String toString() { + return displayName; + } +} diff --git a/api/base/src/main/java/org/geysermc/api/util/InputMode.java b/api/base/src/main/java/org/geysermc/api/util/InputMode.java new file mode 100644 index 000000000..70346ffa5 --- /dev/null +++ b/api/base/src/main/java/org/geysermc/api/util/InputMode.java @@ -0,0 +1,49 @@ +/* + * 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/Geyser + */ + +package org.geysermc.api.util; + +import org.checkerframework.checker.nullness.qual.NonNull; + +public enum InputMode { + UNKNOWN, + KEYBOARD_MOUSE, + TOUCH, + CONTROLLER, + VR; + + private static final InputMode[] VALUES = values(); + + /** + * Get the InputMode from the identifier. + * + * @param id the InputMode identifier + * @return The InputMode or {@link #UNKNOWN} if the mode wasn't found + */ + @NonNull + public static InputMode fromId(int id) { + return VALUES.length > id ? VALUES[id] : VALUES[0]; + } +} \ No newline at end of file diff --git a/api/base/src/main/java/org/geysermc/api/util/UiProfile.java b/api/base/src/main/java/org/geysermc/api/util/UiProfile.java new file mode 100644 index 000000000..cddb97260 --- /dev/null +++ b/api/base/src/main/java/org/geysermc/api/util/UiProfile.java @@ -0,0 +1,45 @@ +/* + * 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/Geyser + */ + +package org.geysermc.api.util; + +import org.checkerframework.checker.nullness.qual.NonNull; + +public enum UiProfile { + CLASSIC, POCKET; + + private static final UiProfile[] VALUES = values(); + + /** + * Get the UiProfile from the identifier. + * + * @param id the UiProfile identifier + * @return The UiProfile or {@link #CLASSIC} if the profile wasn't found + */ + @NonNull + public static UiProfile fromId(int id) { + return VALUES.length > id ? VALUES[id] : VALUES[0]; + } +} diff --git a/api/geyser/build.gradle.kts b/api/geyser/build.gradle.kts new file mode 100644 index 000000000..dcde85337 --- /dev/null +++ b/api/geyser/build.gradle.kts @@ -0,0 +1,14 @@ +plugins { + id("geyser.api-conventions") +} + +dependencies { + api(projects.api) +} + +publishing { + publications.named("mavenJava") { + groupId = rootProject.group as String + ".geyser" + artifactId = "api" + } +} \ No newline at end of file diff --git a/api/geyser/pom.xml b/api/geyser/pom.xml deleted file mode 100644 index 9aa8560d1..000000000 --- a/api/geyser/pom.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - org.geysermc - api-parent - 2.0.7-SNAPSHOT - - 4.0.0 - - geyser-api - - - 16 - 16 - - - - - org.checkerframework - checker-qual - 3.19.0 - provided - - - org.geysermc - base-api - 2.0.7-SNAPSHOT - compile - - - \ No newline at end of file diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java b/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java index 074918881..f86206d36 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/GeyserApi.java @@ -27,8 +27,14 @@ package org.geysermc.geyser.api; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import org.geysermc.api.Geyser; import org.geysermc.api.GeyserApiBase; import org.geysermc.geyser.api.connection.GeyserConnection; +import org.geysermc.geyser.api.event.EventBus; +import org.geysermc.geyser.api.event.EventRegistrar; +import org.geysermc.geyser.api.extension.ExtensionManager; +import org.geysermc.geyser.api.network.BedrockListener; +import org.geysermc.geyser.api.network.RemoteServer; import java.util.List; import java.util.UUID; @@ -37,24 +43,6 @@ import java.util.UUID; * Represents the API used in Geyser. */ public interface GeyserApi extends GeyserApiBase { - /** - * Shuts down the current Geyser instance. - */ - void shutdown(); - - /** - * Reloads the current Geyser instance. - */ - void reload(); - - /** - * Gets if this Geyser instance is running in an IDE. This only needs to be used in cases where files - * expected to be in a jarfile are not present. - * - * @return true if the version number is not 'DEV'. - */ - boolean productionEnvironment(); - /** * {@inheritDoc} */ @@ -67,15 +55,65 @@ public interface GeyserApi extends GeyserApiBase { @Override @Nullable GeyserConnection connectionByXuid(@NonNull String xuid); - /** - * {@inheritDoc} - */ - @Override - @Nullable GeyserConnection connectionByName(@NonNull String name); - /** * {@inheritDoc} */ @NonNull List onlineConnections(); + + /** + * Gets the {@link ExtensionManager}. + * + * @return the extension manager + */ + @NonNull + ExtensionManager extensionManager(); + + /** + * Provides an implementation for the specified API type. + * + * @param apiClass the builder class + * @param the implementation type + * @param the API type + * @return the builder instance + */ + @NonNull + R provider(@NonNull Class apiClass, @Nullable Object... args); + + /** + * Gets the {@link EventBus} for handling + * Geyser events. + * + * @return the event bus + */ + @NonNull + EventBus eventBus(); + + /** + * Gets the default {@link RemoteServer} configured + * within the config file that is used by default. + * + * @return the default remote server used within Geyser + */ + @NonNull + RemoteServer defaultRemoteServer(); + + /** + * Gets the {@link BedrockListener} used for listening + * for Minecraft: Bedrock Edition client connections. + * + * @return the listener used for Bedrock client connectins + */ + @NonNull + BedrockListener bedrockListener(); + + /** + * Gets the current {@link GeyserApiBase} instance. + * + * @return the current geyser api instance + */ + @NonNull + static GeyserApi api() { + return Geyser.api(GeyserApi.class); + } } diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/command/Command.java b/api/geyser/src/main/java/org/geysermc/geyser/api/command/Command.java new file mode 100644 index 000000000..2f1f2b24d --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/command/Command.java @@ -0,0 +1,215 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.api.command; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.api.GeyserApi; +import org.geysermc.geyser.api.connection.GeyserConnection; +import org.geysermc.geyser.api.extension.Extension; + +import java.util.Collections; +import java.util.List; + +/** + * Represents a command. + */ +public interface Command { + + /** + * Gets the command name. + * + * @return the command name + */ + @NonNull + String name(); + + /** + * Gets the command description. + * + * @return the command description + */ + @NonNull + String description(); + + /** + * Gets the permission node associated with + * this command. + * + * @return the permission node for this command + */ + @NonNull + String permission(); + + /** + * Gets the aliases for this command. + * + * @return the aliases for this command + */ + @NonNull + List aliases(); + + /** + * Gets if this command is designed to be used only by server operators. + * + * @return if this command is designated to be used only by server operators. + */ + boolean isSuggestedOpOnly(); + + /** + * Gets if this command is executable on console. + * + * @return if this command is executable on console + */ + boolean isExecutableOnConsole(); + + /** + * Gets the subcommands associated with this + * command. Mainly used within the Geyser Standalone + * GUI to know what subcommands are supported. + * + * @return the subcommands associated with this command + */ + @NonNull + default List subCommands() { + return Collections.emptyList(); + } + + /** + * Used to send a deny message to Java players if this command can only be used by Bedrock players. + * + * @return true if this command can only be used by Bedrock players. + */ + default boolean isBedrockOnly() { + return false; + } + + /** + * Creates a new {@link Command.Builder} used to construct commands. + * + * @param extension the extension + * @param the source type + * @return a new command builder used to construct commands + */ + static Command.Builder builder(@NonNull Extension extension) { + return GeyserApi.api().provider(Builder.class, extension); + } + + interface Builder { + + /** + * Defines the source type to use for this command. + *

+ * Command source types can be anything that extend + * {@link CommandSource}, such as {@link GeyserConnection}. + * This will guarantee that the source used in the executor + * is an instance of this source. + * + * @param sourceType the source type + * @return the builder + */ + Builder source(@NonNull Class sourceType); + + /** + * Sets the command name. + * + * @param name the command name + * @return the builder + */ + Builder name(@NonNull String name); + + /** + * Sets the command description. + * + * @param description the command description + * @return the builder + */ + Builder description(@NonNull String description); + + /** + * Sets the permission node. + * + * @param permission the permission node + * @return the builder + */ + Builder permission(@NonNull String permission); + + /** + * Sets the aliases. + * + * @param aliases the aliases + * @return the builder + */ + Builder aliases(@NonNull List aliases); + + /** + * Sets if this command is designed to be used only by server operators. + * + * @param suggestedOpOnly if this command is designed to be used only by server operators + * @return the builder + */ + Builder suggestedOpOnly(boolean suggestedOpOnly); + + /** + * Sets if this command is executable on console. + * + * @param executableOnConsole if this command is executable on console + * @return the builder + */ + Builder executableOnConsole(boolean executableOnConsole); + + /** + * Sets the subcommands. + * + * @param subCommands the subcommands + * @return the builder + */ + Builder subCommands(@NonNull List subCommands); + + /** + * Sets if this command is bedrock only. + * + * @param bedrockOnly if this command is bedrock only + * @return the builder + */ + Builder bedrockOnly(boolean bedrockOnly); + + /** + * Sets the {@link CommandExecutor} for this command. + * + * @param executor the command executor + * @return the builder + */ + Builder executor(@NonNull CommandExecutor executor); + + /** + * Builds the command. + * + * @return the command + */ + @NonNull + Command build(); + } +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandExecutor.java b/api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandExecutor.java new file mode 100644 index 000000000..12a54ee90 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandExecutor.java @@ -0,0 +1,45 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.api.command; + +import org.checkerframework.checker.nullness.qual.NonNull; + +/** + * Handles executing a command. + * + * @param the command source + */ +public interface CommandExecutor { + /** + * Executes the given {@link Command} with the given + * {@link CommandSource}. + * + * @param source the command source + * @param command the command + * @param args the arguments + */ + void execute(@NonNull T source, @NonNull Command command, @NonNull String[] args); +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandSource.java b/api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandSource.java new file mode 100644 index 000000000..45276e2c4 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/command/CommandSource.java @@ -0,0 +1,81 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.api.command; + +import org.checkerframework.checker.nullness.qual.NonNull; + +/** + * Represents an instance capable of sending commands. + */ +public interface CommandSource { + + /** + * The name of the command source. + * + * @return the name of the command source + */ + String name(); + + /** + * Sends the given message to the command source + * + * @param message the message to send + */ + void sendMessage(@NonNull String message); + + /** + * Sends the given messages to the command source + * + * @param messages the messages to send + */ + default void sendMessage(String[] messages) { + for (String message : messages) { + sendMessage(message); + } + } + + /** + * If this source is the console. + * + * @return true if this source is the console + */ + boolean isConsole(); + + /** + * Returns the locale of the command source. + * + * @return the locale of the command source. + */ + String locale(); + + /** + * Checks if this command source has the given permission + * + * @param permission The permission node to check + * @return true if this command source has a permission + */ + boolean hasPermission(String permission); +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/connection/GeyserConnection.java b/api/geyser/src/main/java/org/geysermc/geyser/api/connection/GeyserConnection.java index 79260ac95..13fd60407 100644 --- a/api/geyser/src/main/java/org/geysermc/geyser/api/connection/GeyserConnection.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/connection/GeyserConnection.java @@ -25,10 +25,11 @@ package org.geysermc.geyser.api.connection; -import org.geysermc.api.session.Connection; +import org.geysermc.api.connection.Connection; +import org.geysermc.geyser.api.command.CommandSource; /** - * Represents a player session used in Geyser. + * Represents a player connection used in Geyser. */ -public interface GeyserConnection extends Connection { +public interface GeyserConnection extends Connection, CommandSource { } diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventBus.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventBus.java new file mode 100644 index 000000000..801bfa45f --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventBus.java @@ -0,0 +1,43 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.api.event; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.event.Event; +import org.geysermc.event.bus.OwnedEventBus; +import org.geysermc.geyser.api.extension.Extension; + +import java.util.Set; + +/** + * Represents a bus capable of subscribing + * or "listening" to events and firing them. + */ +public interface EventBus extends OwnedEventBus> { + @Override + @NonNull + Set> subscribers(@NonNull Class eventClass); +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventRegistrar.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventRegistrar.java new file mode 100644 index 000000000..064dd55f6 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventRegistrar.java @@ -0,0 +1,47 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.api.event; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.api.GeyserApi; + +/** + * Represents an owner for an event that allows it + * to be registered through an {@link EventBus}. + */ +public interface EventRegistrar { + + /** + * Creates an {@link EventRegistrar} instance. + * + * @param object the object to wrap around + * @return an event registrar instance + */ + @NonNull + static EventRegistrar of(@NonNull Object object) { + return GeyserApi.api().provider(EventRegistrar.class, object); + } +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscriber.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscriber.java new file mode 100644 index 000000000..7f91d09a3 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/EventSubscriber.java @@ -0,0 +1,40 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.api.event; + +import org.geysermc.event.Event; +import org.geysermc.event.subscribe.OwnedSubscriber; +import org.geysermc.geyser.api.extension.Extension; + +/** + * Represents a subscribed listener to a {@link Event}. Wraps around + * the event and is capable of unsubscribing from the event or give + * information about it. + * + * @param the class of the event + */ +public interface EventSubscriber extends OwnedSubscriber { +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/ExtensionEventBus.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/ExtensionEventBus.java new file mode 100644 index 000000000..a58d35891 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/ExtensionEventBus.java @@ -0,0 +1,41 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.api.event; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.event.Event; +import org.geysermc.geyser.api.extension.Extension; + +import java.util.Set; + +/** + * An {@link EventBus} with additional methods that implicitly + * set the extension instance. + */ +public interface ExtensionEventBus extends org.geysermc.event.bus.EventBus> { + @Override + @NonNull Set> subscribers(@NonNull Class eventClass); +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/ExtensionEventSubscriber.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/ExtensionEventSubscriber.java new file mode 100644 index 000000000..9c5fffa2f --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/ExtensionEventSubscriber.java @@ -0,0 +1,32 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.api.event; + +import org.geysermc.event.Event; +import org.geysermc.event.subscribe.Subscriber; + +public interface ExtensionEventSubscriber extends Subscriber { +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/connection/ConnectionEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/connection/ConnectionEvent.java new file mode 100644 index 000000000..158f14d53 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/connection/ConnectionEvent.java @@ -0,0 +1,51 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.api.event.connection; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.event.Event; +import org.geysermc.geyser.api.connection.GeyserConnection; + +/** + * An event that contains a {@link GeyserConnection}. + */ +public abstract class ConnectionEvent implements Event { + private final GeyserConnection connection; + + public ConnectionEvent(@NonNull GeyserConnection connection) { + this.connection = connection; + } + + /** + * Gets the {@link GeyserConnection}. + * + * @return the connection + */ + @NonNull + public GeyserConnection connection() { + return this.connection; + } +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/downstream/ServerDefineCommandsEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/downstream/ServerDefineCommandsEvent.java new file mode 100644 index 000000000..e46492b36 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/downstream/ServerDefineCommandsEvent.java @@ -0,0 +1,85 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.api.event.downstream; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.event.Cancellable; +import org.geysermc.geyser.api.connection.GeyserConnection; +import org.geysermc.geyser.api.event.connection.ConnectionEvent; + +import java.util.Set; + +/** + * Called when the Java server defines the commands available on the server. + *
+ * This event is mapped to the existence of Brigadier on the server. + */ +public class ServerDefineCommandsEvent extends ConnectionEvent implements Cancellable { + private final Set commands; + private boolean cancelled; + + public ServerDefineCommandsEvent(@NonNull GeyserConnection connection, @NonNull Set commands) { + super(connection); + this.commands = commands; + } + + /** + * A collection of commands sent from the server. Any element in this collection can be removed, but no element can + * be added. + * + * @return a collection of the commands sent over + */ + @NonNull + public Set commands() { + return this.commands; + } + + @Override + public boolean isCancelled() { + return this.cancelled; + } + + @Override + public void setCancelled(boolean cancelled) { + this.cancelled = cancelled; + } + + public interface CommandInfo { + /** + * Gets the name of the command. + * + * @return the name of the command + */ + String name(); + + /** + * Gets the description of the command. + * + * @return the description of the command + */ + String description(); + } +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCommandsEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCommandsEvent.java new file mode 100644 index 000000000..77d5efa65 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCommandsEvent.java @@ -0,0 +1,57 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.api.event.lifecycle; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.event.Event; +import org.geysermc.geyser.api.command.Command; + +import java.util.Map; + +/** + * Called when commands are defined within Geyser. + * + * This event allows you to register new commands using the {@link #register(Command)} + * method and retrieve the default commands defined. + */ +public interface GeyserDefineCommandsEvent extends Event { + + /** + * Registers the given {@link Command} into the Geyser + * command manager. + * + * @param command the command to register + */ + void register(@NonNull Command command); + + /** + * Gets all the registered built-in {@link Command}s. + * + * @return all the registered built-in commands + */ + @NonNull + Map commands(); +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCustomItemsEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCustomItemsEvent.java new file mode 100644 index 000000000..0957b8551 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserDefineCustomItemsEvent.java @@ -0,0 +1,76 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.api.event.lifecycle; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.event.Event; +import org.geysermc.geyser.api.item.custom.CustomItemData; +import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * Called on Geyser's startup when looking for custom items. Custom items must be registered through this event. + * + * This event will not be called if the "add non-Bedrock items" setting is disabled in the Geyser config. + */ +public interface GeyserDefineCustomItemsEvent extends Event { + /** + * Gets a multimap of all the already registered custom items indexed by the item's extended java item's identifier. + * + * @return a multimap of all the already registered custom items + */ + @NonNull + Map> getExistingCustomItems(); + + /** + * Gets the list of the already registered non-vanilla custom items. + * + * @return the list of the already registered non-vanilla custom items + */ + @NonNull + List getExistingNonVanillaCustomItems(); + + /** + * Registers a custom item with a base Java item. This is used to register items with custom textures and properties + * based on NBT data. + * + * @param identifier the base (java) item + * @param customItemData the custom item data to register + * @return if the item was registered + */ + boolean register(@NonNull String identifier, @NonNull CustomItemData customItemData); + + /** + * Registers a custom item with no base item. This is used for mods. + * + * @param customItemData the custom item data to register + * @return if the item was registered + */ + boolean register(@NonNull NonVanillaCustomItemData customItemData); +} \ No newline at end of file diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserLoadResourcePacksEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserLoadResourcePacksEvent.java new file mode 100644 index 000000000..e9b283ecb --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserLoadResourcePacksEvent.java @@ -0,0 +1,40 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.api.event.lifecycle; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.event.Event; + +import java.nio.file.Path; +import java.util.List; + +/** + * Called when resource packs are loaded within Geyser. + * + * @param resourcePacks a mutable list of the currently listed resource packs + */ +public record GeyserLoadResourcePacksEvent(@NonNull List resourcePacks) implements Event { +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPostInitializeEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPostInitializeEvent.java new file mode 100644 index 000000000..8d145f615 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPostInitializeEvent.java @@ -0,0 +1,41 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.api.event.lifecycle; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.event.Event; +import org.geysermc.geyser.api.event.EventBus; +import org.geysermc.geyser.api.event.EventRegistrar; +import org.geysermc.geyser.api.extension.ExtensionManager; + +/** + * Called when Geyser has completed initializing. + * + * @param extensionManager the extension manager + * @param eventBus the event bus + */ +public record GeyserPostInitializeEvent(@NonNull ExtensionManager extensionManager, @NonNull EventBus eventBus) implements Event { +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPreInitializeEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPreInitializeEvent.java new file mode 100644 index 000000000..8be89dafd --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserPreInitializeEvent.java @@ -0,0 +1,41 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.api.event.lifecycle; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.event.Event; +import org.geysermc.geyser.api.event.EventBus; +import org.geysermc.geyser.api.event.EventRegistrar; +import org.geysermc.geyser.api.extension.ExtensionManager; + +/** + * Called when Geyser is starting to initialize. + * + * @param extensionManager the extension manager + * @param eventBus the event bus + */ +public record GeyserPreInitializeEvent(@NonNull ExtensionManager extensionManager, @NonNull EventBus eventBus) implements Event { +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserShutdownEvent.java b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserShutdownEvent.java new file mode 100644 index 000000000..7793ef997 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/event/lifecycle/GeyserShutdownEvent.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/Geyser + */ + +package org.geysermc.geyser.api.event.lifecycle; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.event.Event; +import org.geysermc.geyser.api.event.EventBus; +import org.geysermc.geyser.api.event.EventRegistrar; +import org.geysermc.geyser.api.extension.ExtensionManager; + +/** + * Called when Geyser is shutting down. + */ +public record GeyserShutdownEvent(@NonNull ExtensionManager extensionManager, @NonNull EventBus eventBus) implements Event { +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/Extension.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/Extension.java new file mode 100644 index 000000000..33fc159de --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/Extension.java @@ -0,0 +1,139 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.api.extension; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.api.GeyserApiBase; +import org.geysermc.geyser.api.GeyserApi; +import org.geysermc.geyser.api.event.EventRegistrar; +import org.geysermc.geyser.api.event.ExtensionEventBus; + +import java.nio.file.Path; +import java.util.Objects; + +/** + * Represents an extension within Geyser. + */ +public interface Extension extends EventRegistrar { + + /** + * Gets if the extension is enabled + * + * @return true if the extension is enabled + */ + default boolean isEnabled() { + return this.extensionLoader().isEnabled(this); + } + + /** + * Enables or disables the extension + * + * @param enabled if the extension should be enabled + */ + default void setEnabled(boolean enabled) { + this.extensionLoader().setEnabled(this, enabled); + } + + /** + * Gets the extension's data folder + * + * @return the extension's data folder + */ + @NonNull + default Path dataFolder() { + return this.extensionLoader().dataFolder(this); + } + + /** + * Gets the {@link ExtensionEventBus}. + * + * @return the extension event bus + */ + @NonNull + default ExtensionEventBus eventBus() { + return this.extensionLoader().eventBus(this); + } + + /** + * Gets the {@link ExtensionManager}. + * + * @return the extension manager + */ + @NonNull + default ExtensionManager extensionManager() { + return this.geyserApi().extensionManager(); + } + + /** + * Gets the extension's name + * + * @return the extension's name + */ + @NonNull + default String name() { + return this.description().name(); + } + + /** + * Gets this extension's {@link ExtensionDescription}. + * + * @return the extension's description + */ + @NonNull + default ExtensionDescription description() { + return this.extensionLoader().description(this); + } + + /** + * Gets the extension's logger + * + * @return the extension's logger + */ + @NonNull + default ExtensionLogger logger() { + return this.extensionLoader().logger(this); + } + + /** + * Gets the {@link ExtensionLoader}. + * + * @return the extension loader + */ + @NonNull + default ExtensionLoader extensionLoader() { + return Objects.requireNonNull(this.extensionManager().extensionLoader()); + } + + /** + * Gets the {@link GeyserApiBase} instance + * + * @return the geyser api instance + */ + @NonNull + default GeyserApi geyserApi() { + return GeyserApi.api(); + } +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionDescription.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionDescription.java new file mode 100644 index 000000000..2df3ee815 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionDescription.java @@ -0,0 +1,106 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.api.extension; + +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.List; + +/** + * Represents the description of an {@link Extension}. + */ +public interface ExtensionDescription { + + /** + * Gets the extension's id. + * + * @return the extension's id + */ + @NonNull + String id(); + + /** + * Gets the extension's name. + * + * @return the extension's name + */ + @NonNull + String name(); + + /** + * Gets the extension's main class. + * + * @return the extension's main class + */ + @NonNull + String main(); + + /** + * Gets the extension's major api version + * + * @return the extension's major api version + */ + int majorApiVersion(); + + /** + * Gets the extension's minor api version + * + * @return the extension's minor api version + */ + int minorApiVersion(); + + /** + * Gets the extension's patch api version + * + * @return the extension's patch api version + */ + int patchApiVersion(); + + /** + * Gets the extension's api version. + * + * @return the extension's api version + */ + default String apiVersion() { + return majorApiVersion() + "." + minorApiVersion() + "." + patchApiVersion(); + } + + /** + * Gets the extension's description. + * + * @return the extension's description + */ + @NonNull + String version(); + + /** + * Gets the extension's authors. + * + * @return the extension's authors + */ + @NonNull + List authors(); +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLoader.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLoader.java new file mode 100644 index 000000000..30414d500 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLoader.java @@ -0,0 +1,105 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.api.extension; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.api.event.ExtensionEventBus; + +import java.nio.file.Path; + +/** + * The extension loader is responsible for loading, unloading, enabling and disabling extensions + */ +public abstract class ExtensionLoader { + /** + * Gets if the given {@link Extension} is enabled. + * + * @param extension the extension + * @return if the extension is enabled + */ + protected abstract boolean isEnabled(@NonNull Extension extension); + + /** + * Sets if the given {@link Extension} is enabled. + * + * @param extension the extension to enable + * @param enabled if the extension should be enabled + */ + protected abstract void setEnabled(@NonNull Extension extension, boolean enabled); + + /** + * Gets the given {@link Extension}'s data folder. + * + * @param extension the extension + * @return the data folder of the given extension + */ + @NonNull + protected abstract Path dataFolder(@NonNull Extension extension); + + /** + * Gets the given {@link Extension}'s {@link ExtensionDescription}. + * + * @param extension the extension + * @return the description of the given extension + */ + @NonNull + protected abstract ExtensionDescription description(@NonNull Extension extension); + + /** + * Gets the given {@link Extension}'s {@link ExtensionEventBus}. + * + * @param extension the extension + * @return the extension's event bus + */ + @NonNull + protected abstract ExtensionEventBus eventBus(@NonNull Extension extension); + + /** + * Gets the {@link ExtensionLogger} for the given {@link Extension}. + * + * @param extension the extension + * @return the extension logger for the given extension + */ + @NonNull + protected abstract ExtensionLogger logger(@NonNull Extension extension); + + /** + * Loads all extensions. + * + * @param extensionManager the extension manager + */ + protected abstract void loadAllExtensions(@NonNull ExtensionManager extensionManager); + + /** + * Registers the given {@link Extension} with the given {@link ExtensionManager}. + * + * @param extension the extension + * @param extensionManager the extension manager + */ + protected void register(@NonNull Extension extension, @NonNull ExtensionManager extensionManager) { + extensionManager.register(extension); + } +} \ No newline at end of file diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLogger.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLogger.java new file mode 100644 index 000000000..17e108455 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionLogger.java @@ -0,0 +1,94 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.api.extension; + +/** + * This is the Geyser extension logger + */ +public interface ExtensionLogger { + /** + * Get the logger prefix + * + * @return the logger prefix + */ + String prefix(); + + /** + * Logs a severe message to console + * + * @param message the message to log + */ + void severe(String message); + + /** + * Logs a severe message and an exception to console + * + * @param message the message to log + * @param error the error to throw + */ + void severe(String message, Throwable error); + + /** + * Logs an error message to console + * + * @param message the message to log + */ + void error(String message); + + /** + * Logs an error message and an exception to console + * + * @param message the message to log + * @param error the error to throw + */ + void error(String message, Throwable error); + + /** + * Logs a warning message to console + * + * @param message the message to log + */ + void warning(String message); + + /** + * Logs an info message to console + * + * @param message the message to log + */ + void info(String message); + + /** + * Logs a debug message to console + * + * @param message the message to log + */ + void debug(String message); + + /** + * If debug is enabled for this logger + */ + boolean isDebug(); +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionManager.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionManager.java new file mode 100644 index 000000000..a9d0d7376 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/ExtensionManager.java @@ -0,0 +1,90 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.api.extension; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.Collection; + +/** + * Manages Geyser {@link Extension}s + */ +public abstract class ExtensionManager { + + /** + * Gets an extension with the given name. + * + * @param name the name of the extension + * @return an extension with the given name + */ + @Nullable + public abstract Extension extension(@NonNull String name); + + /** + * Enables the given {@link Extension}. + * + * @param extension the extension to enable + */ + public abstract void enable(@NonNull Extension extension); + + /** + * Disables the given {@link Extension}. + * + * @param extension the extension to disable + */ + public abstract void disable(@NonNull Extension extension); + + /** + * Gets all the {@link Extension}s currently loaded. + * + * @return all the extensions currently loaded + */ + @NonNull + public abstract Collection extensions(); + + /** + * Gets the {@link ExtensionLoader}. + * + * @return the extension loader + */ + @Nullable + public abstract ExtensionLoader extensionLoader(); + + /** + * Registers an {@link Extension} with the given {@link ExtensionLoader}. + * + * @param extension the extension + */ + public abstract void register(@NonNull Extension extension); + + /** + * Loads all extensions from the given {@link ExtensionLoader}. + */ + protected final void loadAllExtensions(@NonNull ExtensionLoader extensionLoader) { + extensionLoader.loadAllExtensions(this); + } +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/exception/InvalidDescriptionException.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/exception/InvalidDescriptionException.java new file mode 100644 index 000000000..1fe88e9e9 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/exception/InvalidDescriptionException.java @@ -0,0 +1,43 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.api.extension.exception; + +/** + * Thrown when an extension's description is invalid. + */ +public class InvalidDescriptionException extends Exception { + public InvalidDescriptionException(Throwable cause) { + super(cause); + } + + public InvalidDescriptionException(String message) { + super(message); + } + + public InvalidDescriptionException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/extension/exception/InvalidExtensionException.java b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/exception/InvalidExtensionException.java new file mode 100644 index 000000000..7fb6b6922 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/extension/exception/InvalidExtensionException.java @@ -0,0 +1,43 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.api.extension.exception; + +/** + * Thrown when an extension is invalid. + */ +public class InvalidExtensionException extends Exception { + public InvalidExtensionException(Throwable cause) { + super(cause); + } + + public InvalidExtensionException(String message) { + super(message); + } + + public InvalidExtensionException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemData.java b/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemData.java new file mode 100644 index 000000000..17763fb77 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemData.java @@ -0,0 +1,109 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.api.item.custom; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.geysermc.geyser.api.GeyserApi; + +/** + * This is used to store data for a custom item. + */ +public interface CustomItemData { + /** + * Gets the item's name. + * + * @return the item's name + */ + @NonNull String name(); + + /** + * Gets the custom item options of the item. + * + * @return the custom item options of the item. + */ + CustomItemOptions customItemOptions(); + + /** + * Gets the item's display name. By default, this is the item's name. + * + * @return the item's display name + */ + @NonNull String displayName(); + + /** + * Gets the item's icon. By default, this is the item's name. + * + * @return the item's icon + */ + @NonNull String icon(); + + /** + * Gets if the item is allowed to be put into the offhand. + * + * @return true if the item is allowed to be used in the offhand, false otherwise + */ + boolean allowOffhand(); + + /** + * Gets the item's texture size. This is to resize the item if the texture is not 16x16. + * + * @return the item's texture size + */ + int textureSize(); + + /** + * Gets the item's render offsets. If it is null, the item will be rendered normally, with no offsets. + * + * @return the item's render offsets + */ + @Nullable CustomRenderOffsets renderOffsets(); + + static CustomItemData.Builder builder() { + return GeyserApi.api().provider(CustomItemData.Builder.class); + } + + interface Builder { + /** + * Will also set the display name and icon to the provided parameter, if it is currently not set. + */ + Builder name(@NonNull String name); + + Builder customItemOptions(@NonNull CustomItemOptions customItemOptions); + + Builder displayName(@NonNull String displayName); + + Builder icon(@NonNull String icon); + + Builder allowOffhand(boolean allowOffhand); + + Builder textureSize(int textureSize); + + Builder renderOffsets(@Nullable CustomRenderOffsets renderOffsets); + + CustomItemData build(); + } +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemOptions.java b/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemOptions.java new file mode 100644 index 000000000..ec26a6e37 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomItemOptions.java @@ -0,0 +1,83 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.api.item.custom; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.api.GeyserApi; +import org.geysermc.geyser.api.util.TriState; + +import java.util.OptionalInt; + +/** + * This class represents the different ways you can register custom items + */ +public interface CustomItemOptions { + /** + * Gets if the item should be unbreakable. + * + * @return if the item should be unbreakable + */ + @NonNull TriState unbreakable(); + + /** + * Gets the item's custom model data predicate. + * + * @return the item's custom model data + */ + @NonNull OptionalInt customModelData(); + + /** + * Gets the item's damage predicate. + * + * @return the item's damage predicate + */ + @NonNull OptionalInt damagePredicate(); + + /** + * Checks if the item has at least one option set + * + * @return true if the item at least one options set + */ + default boolean hasCustomItemOptions() { + return this.unbreakable() != TriState.NOT_SET || + this.customModelData().isPresent() || + this.damagePredicate().isPresent(); + } + + static CustomItemOptions.Builder builder() { + return GeyserApi.api().provider(CustomItemOptions.Builder.class); + } + + interface Builder { + Builder unbreakable(boolean unbreakable); + + Builder customModelData(int customModelData); + + Builder damagePredicate(int damagePredicate); + + CustomItemOptions build(); + } +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomRenderOffsets.java b/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomRenderOffsets.java new file mode 100644 index 000000000..f81da0ae2 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/CustomRenderOffsets.java @@ -0,0 +1,51 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.api.item.custom; + +import org.checkerframework.checker.nullness.qual.Nullable; + +/** + * This class is used to store the render offsets of custom items. + */ +public record CustomRenderOffsets(@Nullable Hand mainHand, @Nullable Hand offhand) { + /** + * The hand that is used for the offset. + */ + public record Hand(@Nullable Offset firstPerson, @Nullable Offset thirdPerson) { + } + + /** + * The offset of the item. + */ + public record Offset(@Nullable OffsetXYZ position, @Nullable OffsetXYZ rotation, @Nullable OffsetXYZ scale) { + } + + /** + * X, Y and Z positions for the offset. + */ + public record OffsetXYZ(float x, float y, float z) { + } +} diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/NonVanillaCustomItemData.java b/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/NonVanillaCustomItemData.java new file mode 100644 index 000000000..d2cef637a --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/item/custom/NonVanillaCustomItemData.java @@ -0,0 +1,188 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.api.item.custom; + +import org.checkerframework.checker.index.qual.NonNegative; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.geysermc.geyser.api.GeyserApi; + +import java.util.OptionalInt; +import java.util.Set; + +/** + * Represents a completely custom item that is not based on an existing vanilla Minecraft item. + */ +public interface NonVanillaCustomItemData extends CustomItemData { + /** + * Gets the java identifier for this item. + * + * @return The java identifier for this item. + */ + @NonNull String identifier(); + + /** + * Gets the java item id of the item. + * + * @return the java item id of the item + */ + @NonNegative int javaId(); + + /** + * Gets the stack size of the item. + * + * @return the stack size of the item + */ + @NonNegative int stackSize(); + + /** + * Gets the max damage of the item. + * + * @return the max damage of the item + */ + int maxDamage(); + + /** + * Gets the tool type of the item. + * + * @return the tool type of the item + */ + @Nullable String toolType(); + + /** + * Gets the tool tier of the item. + * + * @return the tool tier of the item + */ + @Nullable String toolTier(); + + /** + * Gets the armor type of the item. + * + * @return the armor type of the item + */ + @Nullable String armorType(); + + /** + * Gets the armor protection value of the item. + * + * @return the armor protection value of the item + */ + int protectionValue(); + + /** + * Gets the item's translation string. + * + * @return the item's translation string + */ + @Nullable String translationString(); + + /** + * Gets the repair materials of the item. + * + * @return the repair materials of the item + */ + @Nullable Set repairMaterials(); + + /** + * Gets the item's creative category, or tab id. + * + * @return the item's creative category + */ + @NonNull OptionalInt creativeCategory(); + + /** + * Gets the item's creative group. + * + * @return the item's creative group + */ + @Nullable String creativeGroup(); + + /** + * Gets if the item is a hat. This is used to determine if the item should be rendered on the player's head, and + * normally allow the player to equip it. This is not meant for armor. + * + * @return if the item is a hat + */ + boolean isHat(); + + /** + * Gets if the item is a tool. This is used to set the render type of the item, if the item is handheld. + * + * @return if the item is a tool + */ + boolean isTool(); + + static NonVanillaCustomItemData.Builder builder() { + return GeyserApi.api().provider(NonVanillaCustomItemData.Builder.class); + } + + interface Builder extends CustomItemData.Builder { + Builder name(@NonNull String name); + + Builder identifier(@NonNull String identifier); + + Builder javaId(@NonNegative int javaId); + + Builder stackSize(@NonNegative int stackSize); + + Builder maxDamage(int maxDamage); + + Builder toolType(@Nullable String toolType); + + Builder toolTier(@Nullable String toolTier); + + Builder armorType(@Nullable String armorType); + + Builder protectionValue(int protectionValue); + + Builder translationString(@Nullable String translationString); + + Builder repairMaterials(@Nullable Set repairMaterials); + + Builder creativeCategory(int creativeCategory); + + Builder creativeGroup(@Nullable String creativeGroup); + + Builder hat(boolean isHat); + + Builder tool(boolean isTool); + + @Override + Builder displayName(@NonNull String displayName); + + @Override + Builder allowOffhand(boolean allowOffhand); + + @Override + Builder textureSize(int textureSize); + + @Override + Builder renderOffsets(@Nullable CustomRenderOffsets renderOffsets); + + NonVanillaCustomItemData build(); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/session/auth/AuthType.java b/api/geyser/src/main/java/org/geysermc/geyser/api/network/AuthType.java similarity index 69% rename from core/src/main/java/org/geysermc/geyser/session/auth/AuthType.java rename to api/geyser/src/main/java/org/geysermc/geyser/api/network/AuthType.java index 1edbd0f29..3176f3384 100644 --- a/core/src/main/java/org/geysermc/geyser/session/auth/AuthType.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/network/AuthType.java @@ -23,26 +23,24 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.geyser.session.auth; +package org.geysermc.geyser.api.network; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import lombok.Getter; +import java.util.Locale; -import java.io.IOException; - -@Getter +/** + * The authentication types that a Java server can be on connection. + */ public enum AuthType { OFFLINE, ONLINE, + /** + * The internal name for connecting to an online mode server without needing a Java account. The presence of this + * authentication type does not necessarily mean the Floodgate plugin is installed; it only means that this + * authentication type will be attempted. + */ FLOODGATE; - public static final AuthType[] VALUES = values(); - - public static AuthType getById(int id) { - return id < VALUES.length ? VALUES[id] : OFFLINE; - } + private static final AuthType[] VALUES = values(); /** * Convert the AuthType string (from config) to the enum, ONLINE on fail @@ -52,7 +50,7 @@ public enum AuthType { * @return The converted AuthType */ public static AuthType getByName(String name) { - String upperCase = name.toUpperCase(); + String upperCase = name.toUpperCase(Locale.ROOT); for (AuthType type : VALUES) { if (type.name().equals(upperCase)) { return type; @@ -60,11 +58,4 @@ public enum AuthType { } return ONLINE; } - - public static class Deserializer extends JsonDeserializer { - @Override - public AuthType deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - return getByName(p.getValueAsString()); - } - } } \ No newline at end of file diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/network/BedrockListener.java b/api/geyser/src/main/java/org/geysermc/geyser/api/network/BedrockListener.java new file mode 100644 index 000000000..61fe286aa --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/network/BedrockListener.java @@ -0,0 +1,77 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.api.network; + +import org.checkerframework.checker.nullness.qual.NonNull; + +/** + * The listener that handles connections from Minecraft: + * Bedrock Edition. + */ +public interface BedrockListener { + + /** + * Gets the address used for listening for Bedrock + * connections from. + * + * @return the listening address + */ + @NonNull + String address(); + + /** + * Gets the port used for listening for Bedrock + * connections from. + * + * @return the listening port + */ + int port(); + + /** + * Gets the primary MOTD shown to Bedrock players if a ping passthrough setting is not enabled. + *

+ * This is the first line that will be displayed. + * + * @return the primary MOTD shown to Bedrock players. + */ + String primaryMotd(); + + /** + * Gets the secondary MOTD shown to Bedrock players if a ping passthrough setting is not enabled. + *

+ * This is the second line that will be displayed. + * + * @return the secondary MOTD shown to Bedrock players. + */ + String secondaryMotd(); + + /** + * Gets the server name that is sent to Bedrock clients. + * + * @return the server sent to Bedrock clients + */ + String serverName(); +} diff --git a/api/base/src/main/java/org/geysermc/api/session/Connection.java b/api/geyser/src/main/java/org/geysermc/geyser/api/network/RemoteServer.java similarity index 61% rename from api/base/src/main/java/org/geysermc/api/session/Connection.java rename to api/geyser/src/main/java/org/geysermc/geyser/api/network/RemoteServer.java index 3e997912b..8ac5d8a03 100644 --- a/api/base/src/main/java/org/geysermc/api/session/Connection.java +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/network/RemoteServer.java @@ -23,46 +23,48 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.api.session; +package org.geysermc.geyser.api.network; import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.common.value.qual.IntRange; - -import java.util.UUID; /** - * Represents a player connection. + * Represents the Java server that Geyser is connecting to. */ -@NonNull -public interface Connection { - /** - * Gets the name of the connection. - * - * @return the name of the connection - */ - String name(); +public interface RemoteServer { /** - * Gets the {@link UUID} of the connection. + * Gets the IP address of the remote server. * - * @return the UUID of the connection + * @return the IP address of the remote server */ - UUID uuid(); + String address(); /** - * Gets the XUID of the connection. + * Gets the port of the remote server. * - * @return the XUID of the connection + * @return the port of the remote server */ - String xuid(); + int port(); /** - * Transfer the connection to a server. A Bedrock player can successfully transfer to the same server they are - * currently playing on. + * Gets the protocol version of the remote server. * - * @param address The address of the server - * @param port The port of the server - * @return true if the transfer was a success + * @return the protocol version of the remote server */ - boolean transfer(@NonNull String address, @IntRange(from = 0, to = 65535) int port); + int protocolVersion(); + + /** + * Gets the Minecraft version of the remote server. + * + * @return the Minecraft version of the remote server + */ + String minecraftVersion(); + + /** + * Gets the {@link AuthType} required by the remote server. + * + * @return the auth type required by the remote server + */ + @NonNull + AuthType authType(); } diff --git a/api/geyser/src/main/java/org/geysermc/geyser/api/util/TriState.java b/api/geyser/src/main/java/org/geysermc/geyser/api/util/TriState.java new file mode 100644 index 000000000..457a38e32 --- /dev/null +++ b/api/geyser/src/main/java/org/geysermc/geyser/api/util/TriState.java @@ -0,0 +1,83 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.api.util; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + +/** + * This is a way to represent a boolean, but with a non set value added. + * This class was inspired by adventure's version https://github.com/KyoriPowered/adventure/blob/main/4/api/src/main/java/net/kyori/adventure/util/TriState.java + */ +public enum TriState { + /** + * Describes a value that is not set, null, or not present. + */ + NOT_SET, + + /** + * Describes a true value. + */ + TRUE, + + /** + * Describes a false value. + */ + FALSE; + + /** + * Converts the TriState to a boolean. + * + * @return the boolean value of the TriState + */ + public @Nullable Boolean toBoolean() { + return switch (this) { + case TRUE -> true; + case FALSE -> false; + default -> null; + }; + } + + /** + * Creates a TriState from a boolean. + * + * @param value the Boolean value + * @return the created TriState + */ + public static @NonNull TriState fromBoolean(@Nullable Boolean value) { + return value == null ? NOT_SET : fromBoolean(value.booleanValue()); + } + + /** + * Creates a TriState from a primitive boolean. + * + * @param value the boolean value + * @return the created TriState + */ + public @NonNull static TriState fromBoolean(boolean value) { + return value ? TRUE : FALSE; + } +} diff --git a/api/pom.xml b/api/pom.xml deleted file mode 100644 index 79e999c16..000000000 --- a/api/pom.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - 4.0.0 - - org.geysermc - geyser-parent - 2.0.7-SNAPSHOT - - - api-parent - pom - - - 16 - 16 - - - - base - geyser - - \ No newline at end of file diff --git a/bootstrap/bungeecord/build.gradle.kts b/bootstrap/bungeecord/build.gradle.kts new file mode 100644 index 000000000..9f3b49b67 --- /dev/null +++ b/bootstrap/bungeecord/build.gradle.kts @@ -0,0 +1,37 @@ +val bungeeVersion = "a7c6ede"; + +dependencies { + api(projects.core) + + implementation("net.kyori", "adventure-text-serializer-bungeecord", Versions.adventurePlatformVersion) +} + +platformRelocate("net.md_5.bungee.jni") +platformRelocate("com.fasterxml.jackson") +platformRelocate("io.netty.channel.kqueue") // This is not used because relocating breaks natives, but we must include it or else we get ClassDefNotFound +platformRelocate("net.kyori") + +// These dependencies are already present on the platform +provided("com.github.SpigotMC.BungeeCord", "bungeecord-proxy", bungeeVersion) + +application { + mainClass.set("org.geysermc.geyser.platform.bungeecord.GeyserBungeeMain") +} + +tasks.withType { + archiveBaseName.set("Geyser-BungeeCord") + + dependencies { + exclude(dependency("com.google.*:.*")) + exclude(dependency("org.yaml:.*")) + exclude(dependency("io.netty:netty-transport-native-epoll:.*")) + exclude(dependency("io.netty:netty-transport-native-unix-common:.*")) + exclude(dependency("io.netty:netty-handler:.*")) + exclude(dependency("io.netty:netty-common:.*")) + exclude(dependency("io.netty:netty-buffer:.*")) + exclude(dependency("io.netty:netty-resolver:.*")) + exclude(dependency("io.netty:netty-transport:.*")) + exclude(dependency("io.netty:netty-codec:.*")) + exclude(dependency("io.netty:netty-resolver-dns:.*")) + } +} \ No newline at end of file diff --git a/bootstrap/bungeecord/pom.xml b/bootstrap/bungeecord/pom.xml deleted file mode 100644 index d71a20f42..000000000 --- a/bootstrap/bungeecord/pom.xml +++ /dev/null @@ -1,109 +0,0 @@ - - - 4.0.0 - - org.geysermc - bootstrap-parent - 2.0.7-SNAPSHOT - - bootstrap-bungeecord - - - - org.geysermc - core - 2.0.7-SNAPSHOT - compile - - - - com.github.SpigotMC.BungeeCord - bungeecord-proxy - a7c6ede - provided - - - net.kyori - adventure-text-serializer-bungeecord - ${adventure-platform.version} - compile - - - - ${outputName}-BungeeCord - - - src/main/resources/ - true - - - - - org.apache.maven.plugins - maven-jar-plugin - 3.2.0 - - - - org.geysermc.geyser.platform.bungeecord.GeyserBungeeMain - - - - - - org.apache.maven.plugins - maven-shade-plugin - 3.3.0 - - - package - - shade - - - - - net.md_5.bungee.jni - org.geysermc.geyser.platform.bungeecord.shaded.jni - - - com.fasterxml.jackson - org.geysermc.geyser.platform.bungeecord.shaded.jackson - - - - io.netty.channel.kqueue - org.geysermc.geyser.platform.bungeecord.shaded.io.netty.channel.kqueue - - - net.kyori - org.geysermc.geyser.platform.bungeecord.shaded.kyori - - - - - - - - - com.google.*:* - org.yaml:* - io.netty:netty-transport-native-epoll:* - io.netty:netty-transport-native-unix-common:* - io.netty:netty-handler:* - io.netty:netty-common:* - io.netty:netty-buffer:* - io.netty:netty-resolver:* - io.netty:netty-transport:* - io.netty:netty-codec:* - io.netty:netty-resolver-dns:* - - - - - - - diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeDumpInfo.java b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeDumpInfo.java index 54cb16edb..938e2fc3a 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeDumpInfo.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeDumpInfo.java @@ -28,8 +28,8 @@ package org.geysermc.geyser.platform.bungeecord; import lombok.Getter; import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.plugin.Plugin; -import org.geysermc.geyser.text.AsteriskSerializer; import org.geysermc.geyser.dump.BootstrapDumpInfo; +import org.geysermc.geyser.text.AsteriskSerializer; import java.util.ArrayList; import java.util.Collections; diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeInjector.java b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeInjector.java index 2b1fa10c0..cef430bd6 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeInjector.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeInjector.java @@ -39,8 +39,8 @@ import net.md_5.bungee.api.plugin.Listener; import net.md_5.bungee.api.plugin.Plugin; import net.md_5.bungee.event.EventHandler; import net.md_5.bungee.netty.PipelineUtils; -import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserBootstrap; +import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.network.netty.GeyserInjector; import org.geysermc.geyser.network.netty.LocalServerChannelWrapper; import org.geysermc.geyser.network.netty.LocalSession; diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java index e8d44b02f..13604a3d4 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java @@ -25,31 +25,39 @@ package org.geysermc.geyser.platform.bungeecord; +import io.netty.channel.Channel; +import net.md_5.bungee.BungeeCord; import net.md_5.bungee.api.config.ListenerInfo; import net.md_5.bungee.api.plugin.Plugin; import net.md_5.bungee.protocol.ProtocolConstants; +import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.common.PlatformType; -import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserBootstrap; -import org.geysermc.geyser.command.CommandManager; -import org.geysermc.geyser.session.auth.AuthType; +import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.command.Command; +import org.geysermc.geyser.api.extension.Extension; +import org.geysermc.geyser.api.network.AuthType; +import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough; import org.geysermc.geyser.ping.IGeyserPingPassthrough; -import org.geysermc.geyser.util.FileUtils; -import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.platform.bungeecord.command.GeyserBungeeCommandExecutor; import org.geysermc.geyser.platform.bungeecord.command.GeyserBungeeCommandManager; -import org.jetbrains.annotations.Nullable; +import org.geysermc.geyser.text.GeyserLocale; +import org.geysermc.geyser.util.FileUtils; import java.io.File; import java.io.IOException; +import java.lang.reflect.Field; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Collection; +import java.util.Map; import java.util.UUID; +import java.util.concurrent.TimeUnit; import java.util.logging.Level; public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { @@ -63,9 +71,7 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { private GeyserImpl geyser; @Override - public void onEnable() { - GeyserLocale.init(this); - + public void onLoad() { // Copied from ViaVersion. // https://github.com/ViaVersion/ViaVersion/blob/b8072aad86695cc8ec6f5e4103e43baf3abf6cc5/bungee/src/main/java/us/myles/ViaVersion/BungeePlugin.java#L43 try { @@ -80,6 +86,8 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { getLogger().warning("/_____________\\"); } + GeyserLocale.init(this); + if (!getDataFolder().exists()) getDataFolder().mkdir(); @@ -95,13 +103,38 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { return; } + this.geyserLogger = new GeyserBungeeLogger(getLogger(), geyserConfig.isDebugMode()); + GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); + + this.geyser = GeyserImpl.load(PlatformType.BUNGEECORD, this); + } + + @Override + public void onEnable() { + // Remove this in like a year + if (getProxy().getPluginManager().getPlugin("floodgate-bungee") != null) { + geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.outdated", "https://ci.opencollab.dev/job/GeyserMC/job/Floodgate/job/master/")); + return; + } + + if (geyserConfig.getRemote().authType() == AuthType.FLOODGATE && getProxy().getPluginManager().getPlugin("floodgate") == null) { + geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.disabling")); + return; + } else if (geyserConfig.isAutoconfiguredRemote() && getProxy().getPluginManager().getPlugin("floodgate") != null) { + // Floodgate installed means that the user wants Floodgate authentication + geyserLogger.debug("Auto-setting to Floodgate authentication."); + geyserConfig.getRemote().setAuthType(AuthType.FLOODGATE); + } + + geyserConfig.loadFloodgate(this); + if (getProxy().getConfig().getListeners().size() == 1) { ListenerInfo listener = getProxy().getConfig().getListeners().toArray(new ListenerInfo[0])[0]; InetSocketAddress javaAddr = listener.getHost(); // By default this should be localhost but may need to be changed in some circumstances - if (this.geyserConfig.getRemote().getAddress().equalsIgnoreCase("auto")) { + if (this.geyserConfig.getRemote().address().equalsIgnoreCase("auto")) { this.geyserConfig.setAutoconfiguredRemote(true); // Don't use localhost if not listening on all interfaces if (!javaAddr.getHostString().equals("0.0.0.0") && !javaAddr.getHostString().equals("")) { @@ -115,42 +148,63 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { } } - this.geyserLogger = new GeyserBungeeLogger(getLogger(), geyserConfig.isDebugMode()); - GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); + // Big hack - Bungee does not provide us an event to listen to, so schedule a repeating + // task that waits for a field to be filled which is set after the plugin enable + // process is complete + this.awaitStartupCompletion(0); + } - // Remove this in like a year - if (getProxy().getPluginManager().getPlugin("floodgate-bungee") != null) { - geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.outdated", "https://ci.opencollab.dev/job/GeyserMC/job/Floodgate/job/master/")); + @SuppressWarnings("unchecked") + private void awaitStartupCompletion(int tries) { + // After 20 tries give up waiting. This will happen + // just after 3 minutes approximately + if (tries >= 20) { + this.geyserLogger.warning("BungeeCord plugin startup is taking abnormally long, so Geyser is starting now. " + + "If all your plugins are loaded properly, this is a bug! " + + "If not, consider cutting down the amount of plugins on your proxy as it is causing abnormally slow starting times."); + this.postStartup(); return; } - if (geyserConfig.getRemote().getAuthType() == AuthType.FLOODGATE && getProxy().getPluginManager().getPlugin("floodgate") == null) { - geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.disabling")); - return; - } else if (geyserConfig.isAutoconfiguredRemote() && getProxy().getPluginManager().getPlugin("floodgate") != null) { - // Floodgate installed means that the user wants Floodgate authentication - geyserLogger.debug("Auto-setting to Floodgate authentication."); - geyserConfig.getRemote().setAuthType(AuthType.FLOODGATE); + try { + Field listenersField = BungeeCord.getInstance().getClass().getDeclaredField("listeners"); + listenersField.setAccessible(true); + + Collection listeners = (Collection) listenersField.get(BungeeCord.getInstance()); + if (listeners.isEmpty()) { + this.getProxy().getScheduler().schedule(this, this::postStartup, tries, TimeUnit.SECONDS); + } else { + this.awaitStartupCompletion(++tries); + } + } catch (NoSuchFieldException | IllegalAccessException ex) { + ex.printStackTrace(); } + } - geyserConfig.loadFloodgate(this); - - this.geyser = GeyserImpl.start(PlatformType.BUNGEECORD, this); + private void postStartup() { + GeyserImpl.start(); this.geyserInjector = new GeyserBungeeInjector(this); this.geyserInjector.initializeLocalChannel(this); this.geyserCommandManager = new GeyserBungeeCommandManager(geyser); + this.geyserCommandManager.init(); + + this.getProxy().getPluginManager().registerCommand(this, new GeyserBungeeCommandExecutor("geyser", this.geyser, this.geyserCommandManager.getCommands())); + for (Map.Entry> entry : this.geyserCommandManager.extensionCommands().entrySet()) { + Map commands = entry.getValue(); + if (commands.isEmpty()) { + continue; + } + + this.getProxy().getPluginManager().registerCommand(this, new GeyserBungeeCommandExecutor(entry.getKey().description().id(), this.geyser, commands)); + } if (geyserConfig.isLegacyPingPassthrough()) { this.geyserBungeePingPassthrough = GeyserLegacyPingPassthrough.init(geyser); } else { this.geyserBungeePingPassthrough = new GeyserBungeePingPassthrough(getProxy()); } - - this.getProxy().getPluginManager().registerCommand(this, new GeyserBungeeCommandExecutor(geyser)); - - this.getProxy().getPluginManager().registerListener(this, new GeyserBungeeUpdateListener()); } @Override @@ -174,7 +228,7 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { } @Override - public CommandManager getGeyserCommandManager() { + public GeyserCommandManager getGeyserCommandManager() { return this.geyserCommandManager; } diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeUpdateListener.java b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeUpdateListener.java index bbde8771e..c68839b20 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeUpdateListener.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeUpdateListener.java @@ -31,7 +31,7 @@ import net.md_5.bungee.api.plugin.Listener; import net.md_5.bungee.event.EventHandler; import org.geysermc.geyser.Constants; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.platform.bungeecord.command.BungeeCommandSender; +import org.geysermc.geyser.platform.bungeecord.command.BungeeCommandSource; import org.geysermc.geyser.util.VersionCheckUtils; public final class GeyserBungeeUpdateListener implements Listener { @@ -41,7 +41,7 @@ public final class GeyserBungeeUpdateListener implements Listener { if (GeyserImpl.getInstance().getConfig().isNotifyOnNewBedrockUpdate()) { final ProxiedPlayer player = event.getPlayer(); if (player.hasPermission(Constants.UPDATE_PERMISSION)) { - VersionCheckUtils.checkForGeyserUpdate(() -> new BungeeCommandSender(player)); + VersionCheckUtils.checkForGeyserUpdate(() -> new BungeeCommandSource(player)); } } } diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/command/BungeeCommandSender.java b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/command/BungeeCommandSource.java similarity index 91% rename from bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/command/BungeeCommandSender.java rename to bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/command/BungeeCommandSource.java index dcf5bd689..f65377643 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/command/BungeeCommandSender.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/command/BungeeCommandSource.java @@ -29,19 +29,19 @@ import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer; import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.connection.ProxiedPlayer; -import org.geysermc.geyser.command.CommandSender; +import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.text.GeyserLocale; import java.util.Locale; -public class BungeeCommandSender implements CommandSender { +public class BungeeCommandSource implements GeyserCommandSource { private final net.md_5.bungee.api.CommandSender handle; - public BungeeCommandSender(net.md_5.bungee.api.CommandSender handle) { + public BungeeCommandSource(net.md_5.bungee.api.CommandSender handle) { this.handle = handle; // Ensure even Java players' languages are loaded - GeyserLocale.loadGeyserLocale(getLocale()); + GeyserLocale.loadGeyserLocale(this.locale()); } @Override @@ -72,7 +72,7 @@ public class BungeeCommandSender implements CommandSender { } @Override - public String getLocale() { + public String locale() { if (handle instanceof ProxiedPlayer player) { Locale locale = player.getLocale(); if (locale != null) { diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/command/GeyserBungeeCommandExecutor.java b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/command/GeyserBungeeCommandExecutor.java index 5bb323aac..6575f047c 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/command/GeyserBungeeCommandExecutor.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/command/GeyserBungeeCommandExecutor.java @@ -30,39 +30,40 @@ import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.plugin.Command; import net.md_5.bungee.api.plugin.TabExecutor; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandExecutor; import org.geysermc.geyser.command.GeyserCommand; +import org.geysermc.geyser.command.GeyserCommandExecutor; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.GeyserLocale; import java.util.Arrays; import java.util.Collections; +import java.util.Map; public class GeyserBungeeCommandExecutor extends Command implements TabExecutor { - private final CommandExecutor commandExecutor; + private final GeyserCommandExecutor commandExecutor; - public GeyserBungeeCommandExecutor(GeyserImpl geyser) { - super("geyser"); + public GeyserBungeeCommandExecutor(String name, GeyserImpl geyser, Map commands) { + super(name); - this.commandExecutor = new CommandExecutor(geyser); + this.commandExecutor = new GeyserCommandExecutor(geyser, commands); } @Override public void execute(CommandSender sender, String[] args) { - BungeeCommandSender commandSender = new BungeeCommandSender(sender); + BungeeCommandSource commandSender = new BungeeCommandSource(sender); GeyserSession session = this.commandExecutor.getGeyserSession(commandSender); if (args.length > 0) { GeyserCommand command = this.commandExecutor.getCommand(args[0]); if (command != null) { - if (!sender.hasPermission(command.getPermission())) { - String message = GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", commandSender.getLocale()); + if (!sender.hasPermission(command.permission())) { + String message = GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", commandSender.locale()); commandSender.sendMessage(ChatColor.RED + message); return; } if (command.isBedrockOnly() && session == null) { - String message = GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.bedrock_only", commandSender.getLocale()); + String message = GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.bedrock_only", commandSender.locale()); commandSender.sendMessage(ChatColor.RED + message); return; @@ -77,7 +78,7 @@ public class GeyserBungeeCommandExecutor extends Command implements TabExecutor @Override public Iterable onTabComplete(CommandSender sender, String[] args) { if (args.length == 1) { - return commandExecutor.tabComplete(new BungeeCommandSender(sender)); + return commandExecutor.tabComplete(new BungeeCommandSource(sender)); } else { return Collections.emptyList(); } diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/command/GeyserBungeeCommandManager.java b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/command/GeyserBungeeCommandManager.java index 019544c28..e0fd7a4ac 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/command/GeyserBungeeCommandManager.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/command/GeyserBungeeCommandManager.java @@ -26,16 +26,16 @@ package org.geysermc.geyser.platform.bungeecord.command; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandManager; +import org.geysermc.geyser.command.GeyserCommandManager; -public class GeyserBungeeCommandManager extends CommandManager { +public class GeyserBungeeCommandManager extends GeyserCommandManager { public GeyserBungeeCommandManager(GeyserImpl geyser) { super(geyser); } @Override - public String getDescription(String command) { + public String description(String command) { return ""; // no support for command descriptions in bungee } } diff --git a/bootstrap/bungeecord/src/main/resources/bungee.yml b/bootstrap/bungeecord/src/main/resources/bungee.yml index 7390a4623..1e18b8da4 100644 --- a/bootstrap/bungeecord/src/main/resources/bungee.yml +++ b/bootstrap/bungeecord/src/main/resources/bungee.yml @@ -1,5 +1,5 @@ main: org.geysermc.geyser.platform.bungeecord.GeyserBungeePlugin -name: ${outputName}-BungeeCord -author: ${project.organization.name} -website: ${project.organization.url} -version: ${project.version} \ No newline at end of file +name: ${name}-BungeeCord +author: ${author} +website: ${url} +version: ${version} \ No newline at end of file diff --git a/bootstrap/pom.xml b/bootstrap/pom.xml deleted file mode 100644 index 35ec15abe..000000000 --- a/bootstrap/pom.xml +++ /dev/null @@ -1,53 +0,0 @@ - - - 4.0.0 - - org.geysermc - geyser-parent - 2.0.7-SNAPSHOT - - bootstrap-parent - pom - - - 4.1.2 - - - - - spigot-public - https://hub.spigotmc.org/nexus/content/repositories/public/ - - - sponge-repo - https://repo.spongepowered.org/repository/maven-public/ - - - bungeecord-repo - https://oss.sonatype.org/content/repositories/snapshots - - - velocity-repo - https://repo.velocitypowered.com/snapshots/ - - - - - - org.geysermc - ap - 2.0.7-SNAPSHOT - provided - - - - - bungeecord - spigot - sponge - standalone - velocity - - \ No newline at end of file diff --git a/bootstrap/spigot/build.gradle.kts b/bootstrap/spigot/build.gradle.kts new file mode 100644 index 000000000..5a459a09b --- /dev/null +++ b/bootstrap/spigot/build.gradle.kts @@ -0,0 +1,68 @@ +val paperVersion = "1.19-R0.1-SNAPSHOT" +val viaVersion = "4.0.0" +val adaptersVersion = "1.5-SNAPSHOT" +val commodoreVersion = "2.2" + +dependencies { + api(projects.core) + + implementation("org.geysermc.geyser.adapters", "spigot-all", adaptersVersion) + + implementation("me.lucko", "commodore", commodoreVersion) + + implementation("net.kyori", "adventure-text-serializer-bungeecord", Versions.adventurePlatformVersion) + + // Both paper-api and paper-mojangapi only provide Java 17 versions for 1.19 + compileOnly("io.papermc.paper", "paper-api", paperVersion) { + attributes { + attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17) + } + } + compileOnly("io.papermc.paper", "paper-mojangapi", paperVersion) { + attributes { + attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17) + } + } +} + +platformRelocate("it.unimi.dsi.fastutil") +platformRelocate("com.fasterxml.jackson") +// Relocate net.kyori but exclude the component logger +platformRelocate("net.kyori", "net.kyori.adventure.text.logger.slf4j.ComponentLogger") +platformRelocate("org.objectweb.asm") +platformRelocate("me.lucko.commodore") +platformRelocate("io.netty.channel.kqueue") + +// These dependencies are already present on the platform +provided("com.viaversion", "viaversion", viaVersion) + +application { + mainClass.set("org.geysermc.geyser.platform.spigot.GeyserSpigotMain") +} + +tasks.withType { + archiveBaseName.set("Geyser-Spigot") + + dependencies { + exclude(dependency("com.google.*:.*")) + exclude(dependency("org.yaml:.*")) + + // We cannot shade Netty, or else native libraries will not load + // Needed because older Spigot builds do not provide the haproxy module + exclude(dependency("io.netty:netty-transport-native-epoll:.*")) + exclude(dependency("io.netty:netty-transport-native-unix-common:.*")) + exclude(dependency("io.netty:netty-transport-native-kqueue:.*")) + exclude(dependency("io.netty:netty-handler:.*")) + exclude(dependency("io.netty:netty-common:.*")) + exclude(dependency("io.netty:netty-buffer:.*")) + exclude(dependency("io.netty:netty-resolver:.*")) + exclude(dependency("io.netty:netty-transport:.*")) + exclude(dependency("io.netty:netty-codec:.*")) + exclude(dependency("io.netty:netty-codec-dns:.*")) + exclude(dependency("io.netty:netty-resolver-dns:.*")) + exclude(dependency("io.netty:netty-resolver-dns-native-macos:.*")) + + // Commodore includes Brigadier + exclude(dependency("com.mojang:.*")) + } +} \ No newline at end of file diff --git a/bootstrap/spigot/pom.xml b/bootstrap/spigot/pom.xml deleted file mode 100644 index ad4b58fe2..000000000 --- a/bootstrap/spigot/pom.xml +++ /dev/null @@ -1,163 +0,0 @@ - - - 4.0.0 - - org.geysermc - bootstrap-parent - 2.0.7-SNAPSHOT - - bootstrap-spigot - - - - papermc - https://repo.papermc.io/repository/maven-public/ - - - viaversion-repo - https://repo.viaversion.com - - - - minecraft-repo - https://libraries.minecraft.net/ - - - - - - org.geysermc - core - 2.0.7-SNAPSHOT - compile - - - io.papermc.paper - paper-api - 1.19-R0.1-SNAPSHOT - provided - - - io.papermc.paper - paper-mojangapi - 1.19-R0.1-SNAPSHOT - provided - - - com.viaversion - viaversion - 4.0.0 - provided - - - org.geysermc.geyser.adapters - spigot-all - 1.5-SNAPSHOT - - - me.lucko - commodore - 2.2 - compile - - - net.kyori - adventure-text-serializer-bungeecord - ${adventure-platform.version} - compile - - - - ${outputName}-Spigot - - - src/main/resources/ - true - - - - - org.apache.maven.plugins - maven-jar-plugin - 3.2.0 - - - - org.geysermc.geyser.platform.spigot.GeyserSpigotMain - - - - - - org.apache.maven.plugins - maven-shade-plugin - 3.3.0 - - - package - - shade - - - - - it.unimi.dsi.fastutil - org.geysermc.geyser.platform.spigot.shaded.fastutil - - - com.fasterxml.jackson - org.geysermc.geyser.platform.spigot.shaded.jackson - - - net.kyori - org.geysermc.geyser.platform.spigot.shaded.kyori - - net.kyori.adventure.text.logger.slf4j.ComponentLogger - - - - org.objectweb.asm - org.geysermc.geyser.platform.spigot.shaded.asm - - - me.lucko.commodore - org.geysermc.geyser.platform.spigot.shaded.commodore - - - - io.netty.channel.kqueue - org.geysermc.geyser.platform.spigot.shaded.io.netty.channel.kqueue - - - - - - - - - com.google.*:* - org.yaml:* - - - io.netty:netty-transport-native-epoll:* - io.netty:netty-transport-native-unix-common:* - io.netty:netty-handler:* - io.netty:netty-common:* - io.netty:netty-buffer:* - io.netty:netty-resolver:* - io.netty:netty-transport:* - io.netty:netty-codec:* - io.netty:netty-codec-dns:* - io.netty:netty-resolver-dns:* - io.netty:netty-resolver-dns-native-macos:* - com.mojang:* - - - - - - - \ No newline at end of file diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserPaperPingPassthrough.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserPaperPingPassthrough.java index 15bd6bde1..36dd81d44 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserPaperPingPassthrough.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserPaperPingPassthrough.java @@ -29,7 +29,7 @@ import com.destroystokyo.paper.event.server.PaperServerListPingEvent; import com.destroystokyo.paper.network.StatusClient; import com.destroystokyo.paper.profile.PlayerProfile; import org.bukkit.Bukkit; -import org.geysermc.geyser.network.MinecraftProtocol; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.ping.GeyserPingInfo; import org.geysermc.geyser.ping.IGeyserPingPassthrough; import org.jetbrains.annotations.NotNull; @@ -62,11 +62,11 @@ public final class GeyserPaperPingPassthrough implements IGeyserPingPassthrough // Approximately pre-1.19 event = OLD_CONSTRUCTOR.newInstance(new GeyserStatusClient(inetSocketAddress), Bukkit.getMotd(), Bukkit.getOnlinePlayers().size(), - Bukkit.getMaxPlayers(), Bukkit.getVersion(), MinecraftProtocol.getJavaProtocolVersion(), null); + Bukkit.getMaxPlayers(), Bukkit.getVersion(), GameProtocol.getJavaProtocolVersion(), null); } else { event = new PaperServerListPingEvent(new GeyserStatusClient(inetSocketAddress), Bukkit.getMotd(), Bukkit.shouldSendChatPreviews(), Bukkit.getOnlinePlayers().size(), - Bukkit.getMaxPlayers(), Bukkit.getVersion(), MinecraftProtocol.getJavaProtocolVersion(), null); + Bukkit.getMaxPlayers(), Bukkit.getVersion(), GameProtocol.getJavaProtocolVersion(), null); } Bukkit.getPluginManager().callEvent(event); if (event.isCancelled()) { @@ -82,7 +82,7 @@ public final class GeyserPaperPingPassthrough implements IGeyserPingPassthrough } GeyserPingInfo geyserPingInfo = new GeyserPingInfo(event.getMotd(), players, - new GeyserPingInfo.Version(Bukkit.getVersion(), MinecraftProtocol.getJavaProtocolVersion())); + new GeyserPingInfo.Version(Bukkit.getVersion(), GameProtocol.getJavaProtocolVersion())); if (!event.shouldHidePlayers()) { for (PlayerProfile profile : event.getPlayerSample()) { @@ -105,7 +105,7 @@ public final class GeyserPaperPingPassthrough implements IGeyserPingPassthrough @Override public int getProtocolVersion() { - return MinecraftProtocol.getJavaProtocolVersion(); + return GameProtocol.getJavaProtocolVersion(); } @Override diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotDumpInfo.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotDumpInfo.java index 7f8213155..8055a375f 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotDumpInfo.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotDumpInfo.java @@ -28,8 +28,8 @@ package org.geysermc.geyser.platform.spigot; import lombok.Getter; import org.bukkit.Bukkit; import org.bukkit.plugin.Plugin; -import org.geysermc.geyser.text.AsteriskSerializer; import org.geysermc.geyser.dump.BootstrapDumpInfo; +import org.geysermc.geyser.text.AsteriskSerializer; import java.util.ArrayList; import java.util.List; diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotInjector.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotInjector.java index 0fd8d849b..c1d3b6871 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotInjector.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotInjector.java @@ -170,8 +170,8 @@ public class GeyserSpigotInjector extends GeyserInjector { */ private void workAroundWeirdBug(GeyserBootstrap bootstrap) { MinecraftProtocol protocol = new MinecraftProtocol(); - LocalSession session = new LocalSession(bootstrap.getGeyserConfig().getRemote().getAddress(), - bootstrap.getGeyserConfig().getRemote().getPort(), this.serverSocketAddress, + LocalSession session = new LocalSession(bootstrap.getGeyserConfig().getRemote().address(), + bootstrap.getGeyserConfig().getRemote().port(), this.serverSocketAddress, InetAddress.getLoopbackAddress().getHostAddress(), protocol, protocol.createHelper()); session.connect(); } diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPingPassthrough.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPingPassthrough.java index db5a0a1e1..634d1f8a8 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPingPassthrough.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPingPassthrough.java @@ -30,7 +30,7 @@ import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.server.ServerListPingEvent; import org.bukkit.util.CachedServerIcon; -import org.geysermc.geyser.network.MinecraftProtocol; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.ping.GeyserPingInfo; import org.geysermc.geyser.ping.IGeyserPingPassthrough; @@ -52,7 +52,7 @@ public class GeyserSpigotPingPassthrough implements IGeyserPingPassthrough { Bukkit.getPluginManager().callEvent(event); GeyserPingInfo geyserPingInfo = new GeyserPingInfo(event.getMotd(), new GeyserPingInfo.Players(event.getMaxPlayers(), event.getNumPlayers()), - new GeyserPingInfo.Version(Bukkit.getVersion(), MinecraftProtocol.getJavaProtocolVersion()) // thanks Spigot for not exposing this, just default to latest + new GeyserPingInfo.Version(Bukkit.getVersion(), GameProtocol.getJavaProtocolVersion()) // thanks Spigot for not exposing this, just default to latest ); Bukkit.getOnlinePlayers().stream().map(Player::getName).forEach(geyserPingInfo.getPlayerList()::add); return geyserPingInfo; diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java index a1d9245e8..60b1cfa21 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java @@ -32,36 +32,44 @@ import com.viaversion.viaversion.api.protocol.version.ProtocolVersion; import io.netty.buffer.ByteBuf; import me.lucko.commodore.CommodoreProvider; import org.bukkit.Bukkit; +import org.bukkit.command.CommandMap; import org.bukkit.command.PluginCommand; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.server.ServerLoadEvent; import org.bukkit.permissions.Permission; import org.bukkit.permissions.PermissionDefault; +import org.bukkit.plugin.Plugin; import org.bukkit.plugin.java.JavaPlugin; import org.geysermc.common.PlatformType; import org.geysermc.geyser.Constants; import org.geysermc.geyser.GeyserBootstrap; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.adapters.spigot.SpigotAdapters; -import org.geysermc.geyser.command.CommandManager; -import org.geysermc.geyser.command.GeyserCommand; +import org.geysermc.geyser.api.command.Command; +import org.geysermc.geyser.api.extension.Extension; +import org.geysermc.geyser.api.network.AuthType; +import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.level.WorldManager; -import org.geysermc.geyser.network.MinecraftProtocol; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough; import org.geysermc.geyser.ping.IGeyserPingPassthrough; import org.geysermc.geyser.platform.spigot.command.GeyserBrigadierSupport; import org.geysermc.geyser.platform.spigot.command.GeyserSpigotCommandExecutor; import org.geysermc.geyser.platform.spigot.command.GeyserSpigotCommandManager; -import org.geysermc.geyser.platform.spigot.command.SpigotCommandSender; +import org.geysermc.geyser.platform.spigot.command.SpigotCommandSource; import org.geysermc.geyser.platform.spigot.world.GeyserPistonListener; import org.geysermc.geyser.platform.spigot.world.GeyserSpigotBlockPlaceListener; import org.geysermc.geyser.platform.spigot.world.manager.*; -import org.geysermc.geyser.session.auth.AuthType; import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.util.FileUtils; import java.io.File; import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.net.SocketAddress; import java.nio.file.Path; import java.util.List; @@ -90,24 +98,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { private String minecraftVersion; @Override - public void onEnable() { - GeyserLocale.init(this); - - // This is manually done instead of using Bukkit methods to save the config because otherwise comments get removed - try { - if (!getDataFolder().exists()) { - getDataFolder().mkdir(); - } - File configFile = FileUtils.fileOrCopiedFromResource(new File(getDataFolder(), "config.yml"), "config.yml", - (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this); - this.geyserConfig = FileUtils.loadConfig(configFile, GeyserSpigotConfiguration.class); - } catch (IOException ex) { - getLogger().log(Level.SEVERE, GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex); - ex.printStackTrace(); - Bukkit.getPluginManager().disablePlugin(this); - return; - } - + public void onLoad() { try { // AvailableCommandsSerializer_v291 complains otherwise ByteBuf.class.getMethod("writeShortLE", int.class); @@ -139,8 +130,51 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { } } + GeyserLocale.init(this); + + // This is manually done instead of using Bukkit methods to save the config because otherwise comments get removed + try { + if (!getDataFolder().exists()) { + getDataFolder().mkdir(); + } + File configFile = FileUtils.fileOrCopiedFromResource(new File(getDataFolder(), "config.yml"), "config.yml", + (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this); + this.geyserConfig = FileUtils.loadConfig(configFile, GeyserSpigotConfiguration.class); + } catch (IOException ex) { + getLogger().log(Level.SEVERE, GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex); + ex.printStackTrace(); + Bukkit.getPluginManager().disablePlugin(this); + return; + } + + this.geyserLogger = GeyserPaperLogger.supported() ? new GeyserPaperLogger(this, getLogger(), geyserConfig.isDebugMode()) + : new GeyserSpigotLogger(getLogger(), geyserConfig.isDebugMode()); + + GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); + + this.geyser = GeyserImpl.load(PlatformType.SPIGOT, this); + } + + @Override + public void onEnable() { + // Remove this in like a year + if (Bukkit.getPluginManager().getPlugin("floodgate-bukkit") != null) { + geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.outdated", Constants.FLOODGATE_DOWNLOAD_LOCATION)); + this.getPluginLoader().disablePlugin(this); + return; + } + + if (geyserConfig.getRemote().authType() == AuthType.FLOODGATE && Bukkit.getPluginManager().getPlugin("floodgate") == null) { + geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.disabling")); + this.getPluginLoader().disablePlugin(this); + } else if (geyserConfig.isAutoconfiguredRemote() && Bukkit.getPluginManager().getPlugin("floodgate") != null) { + // Floodgate installed means that the user wants Floodgate authentication + geyserLogger.debug("Auto-setting to Floodgate authentication."); + geyserConfig.getRemote().setAuthType(AuthType.FLOODGATE); + } + // By default this should be localhost but may need to be changed in some circumstances - if (this.geyserConfig.getRemote().getAddress().equalsIgnoreCase("auto")) { + if (this.geyserConfig.getRemote().address().equalsIgnoreCase("auto")) { geyserConfig.setAutoconfiguredRemote(true); // Don't use localhost if not listening on all interfaces if (!Bukkit.getIp().equals("0.0.0.0") && !Bukkit.getIp().equals("")) { @@ -153,34 +187,47 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { geyserConfig.getBedrock().setPort(Bukkit.getPort()); } - this.geyserLogger = GeyserPaperLogger.supported() ? new GeyserPaperLogger(this, getLogger(), geyserConfig.isDebugMode()) - : new GeyserSpigotLogger(getLogger(), geyserConfig.isDebugMode()); - GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); - - // Remove this in like a year - if (Bukkit.getPluginManager().getPlugin("floodgate-bukkit") != null) { - geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.outdated", Constants.FLOODGATE_DOWNLOAD_LOCATION)); - this.getPluginLoader().disablePlugin(this); - return; - } - - if (geyserConfig.getRemote().getAuthType() == AuthType.FLOODGATE && Bukkit.getPluginManager().getPlugin("floodgate") == null) { - geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.disabling")); - this.getPluginLoader().disablePlugin(this); - return; - } else if (geyserConfig.isAutoconfiguredRemote() && Bukkit.getPluginManager().getPlugin("floodgate") != null) { - // Floodgate installed means that the user wants Floodgate authentication - geyserLogger.debug("Auto-setting to Floodgate authentication."); - geyserConfig.getRemote().setAuthType(AuthType.FLOODGATE); - } - geyserConfig.loadFloodgate(this); + // Needs to be an anonymous inner class otherwise Bukkit complains about missing classes + Bukkit.getPluginManager().registerEvents(new Listener() { + + @EventHandler + public void onServerLoaded(ServerLoadEvent event) { + // Wait until all plugins have loaded so Geyser can start + postStartup(); + } + }, this); + + this.geyserCommandManager = new GeyserSpigotCommandManager(geyser); + this.geyserCommandManager.init(); + + // Because Bukkit locks its command map upon startup, we need to + // add our plugin commands in onEnable, but populating the executor + // can happen at any time + CommandMap commandMap = GeyserSpigotCommandManager.getCommandMap(); + for (Extension extension : this.geyserCommandManager.extensionCommands().keySet()) { + // Thanks again, Bukkit + try { + Constructor constructor = PluginCommand.class.getDeclaredConstructor(String.class, Plugin.class); + constructor.setAccessible(true); + + PluginCommand pluginCommand = constructor.newInstance(extension.description().id(), this); + pluginCommand.setDescription("The main command for the " + extension.name() + " Geyser extension!"); + + commandMap.register(extension.description().id(), "geyserext", pluginCommand); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException ex) { + this.geyserLogger.error("Failed to construct PluginCommand for extension " + extension.description().name(), ex); + } + } + } + + private void postStartup() { + GeyserImpl.start(); + // Turn "(MC: 1.16.4)" into 1.16.4. this.minecraftVersion = Bukkit.getServer().getVersion().split("\\(MC: ")[1].split("\\)")[0]; - this.geyser = GeyserImpl.start(PlatformType.SPIGOT, this); - if (geyserConfig.isLegacyPingPassthrough()) { this.geyserSpigotPingPassthrough = GeyserLegacyPingPassthrough.init(geyser); } else { @@ -195,8 +242,6 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { } geyserLogger.debug("Spigot ping passthrough type: " + (this.geyserSpigotPingPassthrough == null ? null : this.geyserSpigotPingPassthrough.getClass())); - this.geyserCommandManager = new GeyserSpigotCommandManager(geyser); - boolean isViaVersion = Bukkit.getPluginManager().getPlugin("ViaVersion") != null; if (isViaVersion) { try { @@ -217,7 +262,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { boolean isPre1_12 = !isCompatible(Bukkit.getServer().getVersion(), "1.12.0"); // Set if we need to use a different method for getting a player's locale - SpigotCommandSender.setUseLegacyLocaleMethod(isPre1_12); + SpigotCommandSource.setUseLegacyLocaleMethod(isPre1_12); // We want to do this late in the server startup process to allow plugins such as ViaVersion and ProtocolLib // To do their job injecting, then connect into *that* @@ -266,23 +311,57 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { geyserLogger.debug("Using default world manager: " + this.geyserWorldManager.getClass()); } - PluginCommand pluginCommand = this.getCommand("geyser"); - pluginCommand.setExecutor(new GeyserSpigotCommandExecutor(geyser)); + PluginCommand geyserCommand = this.getCommand("geyser"); + geyserCommand.setExecutor(new GeyserSpigotCommandExecutor(geyser, geyserCommandManager.getCommands())); + + for (Map.Entry> entry : this.geyserCommandManager.extensionCommands().entrySet()) { + Map commands = entry.getValue(); + if (commands.isEmpty()) { + continue; + } + + PluginCommand command = this.getCommand(entry.getKey().description().id()); + if (command == null) { + continue; + } + + command.setExecutor(new GeyserSpigotCommandExecutor(this.geyser, commands)); + } if (!INITIALIZED) { // Register permissions so they appear in, for example, LuckPerms' UI // Re-registering permissions throws an error - for (Map.Entry entry : geyserCommandManager.getCommands().entrySet()) { - GeyserCommand command = entry.getValue(); - if (command.getAliases().contains(entry.getKey())) { + for (Map.Entry entry : geyserCommandManager.commands().entrySet()) { + Command command = entry.getValue(); + if (command.aliases().contains(entry.getKey())) { // Don't register aliases continue; } - Bukkit.getPluginManager().addPermission(new Permission(command.getPermission(), - GeyserLocale.getLocaleStringLog(command.getDescription()), + Bukkit.getPluginManager().addPermission(new Permission(command.permission(), + GeyserLocale.getLocaleStringLog(command.description()), command.isSuggestedOpOnly() ? PermissionDefault.OP : PermissionDefault.TRUE)); } + + // Register permissions for extension commands + for (Map.Entry> commandEntry : this.geyserCommandManager.extensionCommands().entrySet()) { + for (Map.Entry entry : commandEntry.getValue().entrySet()) { + Command command = entry.getValue(); + if (command.aliases().contains(entry.getKey())) { + // Don't register aliases + continue; + } + + if (command.permission().isBlank()) { + continue; + } + + Bukkit.getPluginManager().addPermission(new Permission(command.permission(), + GeyserLocale.getLocaleStringLog(command.description()), + command.isSuggestedOpOnly() ? PermissionDefault.OP : PermissionDefault.TRUE)); + } + } + Bukkit.getPluginManager().addPermission(new Permission(Constants.UPDATE_PERMISSION, "Whether update notifications can be seen", PermissionDefault.OP)); @@ -298,7 +377,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { boolean brigadierSupported = CommodoreProvider.isSupported(); geyserLogger.debug("Brigadier supported? " + brigadierSupported); if (brigadierSupported) { - GeyserBrigadierSupport.loadBrigadier(this, pluginCommand); + GeyserBrigadierSupport.loadBrigadier(this, geyserCommand); } // Check to ensure the current setup can support the protocol version Geyser uses @@ -328,7 +407,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { } @Override - public CommandManager getGeyserCommandManager() { + public GeyserCommandManager getGeyserCommandManager() { return this.geyserCommandManager; } @@ -410,7 +489,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { */ private boolean isViaVersionNeeded() { ProtocolVersion serverVersion = getServerProtocolVersion(); - List protocolList = Via.getManager().getProtocolManager().getProtocolPath(MinecraftProtocol.getJavaProtocolVersion(), + List protocolList = Via.getManager().getProtocolManager().getProtocolPath(GameProtocol.getJavaProtocolVersion(), serverVersion.getVersion()); if (protocolList == null) { // No translation needed! diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotUpdateListener.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotUpdateListener.java index 02f5367b3..5e3c4def8 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotUpdateListener.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotUpdateListener.java @@ -31,7 +31,7 @@ import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerJoinEvent; import org.geysermc.geyser.Constants; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.platform.spigot.command.SpigotCommandSender; +import org.geysermc.geyser.platform.spigot.command.SpigotCommandSource; import org.geysermc.geyser.util.VersionCheckUtils; public final class GeyserSpigotUpdateListener implements Listener { @@ -41,7 +41,7 @@ public final class GeyserSpigotUpdateListener implements Listener { if (GeyserImpl.getInstance().getConfig().isNotifyOnNewBedrockUpdate()) { final Player player = event.getPlayer(); if (player.hasPermission(Constants.UPDATE_PERMISSION)) { - VersionCheckUtils.checkForGeyserUpdate(() -> new SpigotCommandSender(player)); + VersionCheckUtils.checkForGeyserUpdate(() -> new SpigotCommandSource(player)); } } } diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotVersionChecker.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotVersionChecker.java index 923209e59..0212ff9b0 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotVersionChecker.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotVersionChecker.java @@ -29,7 +29,7 @@ import com.viaversion.viaversion.api.Via; import org.bukkit.Bukkit; import org.bukkit.UnsafeValues; import org.geysermc.geyser.GeyserLogger; -import org.geysermc.geyser.network.MinecraftProtocol; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.text.GeyserLocale; import java.lang.reflect.InvocationTargetException; @@ -48,7 +48,7 @@ public final class GeyserSpigotVersionChecker { try { // This method is only present on later versions of Paper UnsafeValues.class.getMethod("getProtocolVersion"); - if (Bukkit.getUnsafe().getProtocolVersion() != MinecraftProtocol.getJavaProtocolVersion()) { + if (Bukkit.getUnsafe().getProtocolVersion() != GameProtocol.getJavaProtocolVersion()) { sendOutdatedMessage(logger); } return; @@ -82,7 +82,7 @@ public final class GeyserSpigotVersionChecker { } return; } - if (protocolVersion != MinecraftProtocol.getJavaProtocolVersion()) { + if (protocolVersion != GameProtocol.getJavaProtocolVersion()) { sendOutdatedMessage(logger); } return; @@ -94,13 +94,13 @@ public final class GeyserSpigotVersionChecker { private static void checkViaVersionSupportedVersions(GeyserLogger logger) { // Run after ViaVersion has obtained the server protocol version Via.getPlatform().runSync(() -> { - if (Via.getAPI().getSupportedVersions().contains(MinecraftProtocol.getJavaProtocolVersion())) { + if (Via.getAPI().getSupportedVersions().contains(GameProtocol.getJavaProtocolVersion())) { // Via supports this protocol version; we will be able to connect. return; } - if (Via.getAPI().getFullSupportedVersions().contains(MinecraftProtocol.getJavaProtocolVersion())) { + if (Via.getAPI().getFullSupportedVersions().contains(GameProtocol.getJavaProtocolVersion())) { // ViaVersion supports our protocol, but the user has blocked them from connecting. - logger.warning(GeyserLocale.getLocaleStringLog("geyser.bootstrap.viaversion.blocked", MinecraftProtocol.getAllSupportedJavaVersions())); + logger.warning(GeyserLocale.getLocaleStringLog("geyser.bootstrap.viaversion.blocked", GameProtocol.getAllSupportedJavaVersions())); return; } // Else, presumably, ViaVersion is not updated. @@ -114,7 +114,7 @@ public final class GeyserSpigotVersionChecker { } private static void sendOutdatedMessage(GeyserLogger logger) { - logger.warning(GeyserLocale.getLocaleStringLog("geyser.bootstrap.no_supported_protocol", MinecraftProtocol.getAllSupportedJavaVersions(), VIAVERSION_DOWNLOAD_URL)); + logger.warning(GeyserLocale.getLocaleStringLog("geyser.bootstrap.no_supported_protocol", GameProtocol.getAllSupportedJavaVersions(), VIAVERSION_DOWNLOAD_URL)); } private GeyserSpigotVersionChecker() { diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserPaperCommandListener.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserPaperCommandListener.java index 00c1ba58d..9375e3a62 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserPaperCommandListener.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserPaperCommandListener.java @@ -31,7 +31,7 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.GeyserCommand; +import org.geysermc.geyser.api.command.Command; import java.net.InetSocketAddress; import java.util.Iterator; @@ -49,14 +49,14 @@ public final class GeyserPaperCommandListener implements Listener { if (geyserBrigadier != null) { Player player = event.getPlayer(); boolean isJavaPlayer = isProbablyJavaPlayer(player); - Map commands = GeyserImpl.getInstance().getCommandManager().getCommands(); + Map commands = GeyserImpl.getInstance().commandManager().getCommands(); Iterator> it = geyserBrigadier.getChildren().iterator(); while (it.hasNext()) { CommandNode subnode = it.next(); - GeyserCommand command = commands.get(subnode.getName()); + Command command = commands.get(subnode.getName()); if (command != null) { - if ((command.isBedrockOnly() && isJavaPlayer) || !player.hasPermission(command.getPermission())) { + if ((command.isBedrockOnly() && isJavaPlayer) || !player.hasPermission(command.permission())) { // Remove this from the node as we don't have permission to use it it.remove(); } diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserSpigotCommandExecutor.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserSpigotCommandExecutor.java index b1bcfcaf8..52779db23 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserSpigotCommandExecutor.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserSpigotCommandExecutor.java @@ -30,37 +30,38 @@ import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.command.TabExecutor; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandExecutor; import org.geysermc.geyser.command.GeyserCommand; +import org.geysermc.geyser.command.GeyserCommandExecutor; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.GeyserLocale; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Map; -public class GeyserSpigotCommandExecutor extends CommandExecutor implements TabExecutor { +public class GeyserSpigotCommandExecutor extends GeyserCommandExecutor implements TabExecutor { - public GeyserSpigotCommandExecutor(GeyserImpl geyser) { - super(geyser); + public GeyserSpigotCommandExecutor(GeyserImpl geyser, Map commands) { + super(geyser, commands); } @Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { - SpigotCommandSender commandSender = new SpigotCommandSender(sender); + SpigotCommandSource commandSender = new SpigotCommandSource(sender); GeyserSession session = getGeyserSession(commandSender); if (args.length > 0) { GeyserCommand geyserCommand = getCommand(args[0]); if (geyserCommand != null) { - if (!sender.hasPermission(geyserCommand.getPermission())) { - String message = GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", commandSender.getLocale()); + if (!sender.hasPermission(geyserCommand.permission())) { + String message = GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", commandSender.locale()); commandSender.sendMessage(ChatColor.RED + message); return true; } if (geyserCommand.isBedrockOnly() && session == null) { - sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.bedrock_only", commandSender.getLocale())); + sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.bedrock_only", commandSender.locale())); return true; } geyserCommand.execute(session, commandSender, args.length > 1 ? Arrays.copyOfRange(args, 1, args.length) : new String[0]); @@ -76,7 +77,7 @@ public class GeyserSpigotCommandExecutor extends CommandExecutor implements TabE @Override public List onTabComplete(CommandSender sender, Command command, String label, String[] args) { if (args.length == 1) { - return tabComplete(new SpigotCommandSender(sender)); + return tabComplete(new SpigotCommandSource(sender)); } return Collections.emptyList(); } diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserSpigotCommandManager.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserSpigotCommandManager.java index 6107d5b47..655d3be23 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserSpigotCommandManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserSpigotCommandManager.java @@ -30,11 +30,11 @@ import org.bukkit.Server; import org.bukkit.command.Command; import org.bukkit.command.CommandMap; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandManager; +import org.geysermc.geyser.command.GeyserCommandManager; import java.lang.reflect.Field; -public class GeyserSpigotCommandManager extends CommandManager { +public class GeyserSpigotCommandManager extends GeyserCommandManager { private static final CommandMap COMMAND_MAP; @@ -61,8 +61,12 @@ public class GeyserSpigotCommandManager extends CommandManager { } @Override - public String getDescription(String command) { + public String description(String command) { Command cmd = COMMAND_MAP.getCommand(command.replace("/", "")); return cmd != null ? cmd.getDescription() : ""; } + + public static CommandMap getCommandMap() { + return COMMAND_MAP; + } } diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/SpigotCommandSender.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/SpigotCommandSource.java similarity index 95% rename from bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/SpigotCommandSender.java rename to bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/SpigotCommandSource.java index cef92f744..8deddd8e6 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/SpigotCommandSender.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/SpigotCommandSource.java @@ -30,14 +30,14 @@ import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer; import org.bukkit.command.ConsoleCommandSender; import org.bukkit.entity.Player; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandSender; +import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.platform.spigot.PaperAdventure; import org.geysermc.geyser.text.GeyserLocale; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -public class SpigotCommandSender implements CommandSender { +public class SpigotCommandSource implements GeyserCommandSource { /** * Whether to use {@code Player.getLocale()} or {@code Player.spigot().getLocale()}, depending on version. @@ -49,7 +49,7 @@ public class SpigotCommandSender implements CommandSender { private final org.bukkit.command.CommandSender handle; private final String locale; - public SpigotCommandSender(org.bukkit.command.CommandSender handle) { + public SpigotCommandSource(org.bukkit.command.CommandSender handle) { this.handle = handle; this.locale = getSpigotLocale(); // Ensure even Java players' languages are loaded @@ -83,7 +83,7 @@ public class SpigotCommandSender implements CommandSender { } @Override - public String getLocale() { + public String locale() { return locale; } diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/GeyserPistonListener.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/GeyserPistonListener.java index 981d00b97..8be1cb84e 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/GeyserPistonListener.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/GeyserPistonListener.java @@ -41,12 +41,12 @@ import org.bukkit.event.block.BlockPistonEvent; import org.bukkit.event.block.BlockPistonExtendEvent; import org.bukkit.event.block.BlockPistonRetractEvent; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.session.cache.PistonCache; import org.geysermc.geyser.level.block.BlockStateValues; -import org.geysermc.geyser.translator.level.block.entity.PistonBlockEntity; import org.geysermc.geyser.level.physics.Direction; import org.geysermc.geyser.platform.spigot.world.manager.GeyserSpigotWorldManager; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.session.cache.PistonCache; +import org.geysermc.geyser.translator.level.block.entity.PistonBlockEntity; import java.util.List; import java.util.Map; diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/GeyserSpigotBlockPlaceListener.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/GeyserSpigotBlockPlaceListener.java index 62a56bd2d..d486501de 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/GeyserSpigotBlockPlaceListener.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/GeyserSpigotBlockPlaceListener.java @@ -33,10 +33,10 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockPlaceEvent; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.level.block.BlockStateValues; -import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.platform.spigot.world.manager.GeyserSpigotWorldManager; +import org.geysermc.geyser.registry.BlockRegistries; +import org.geysermc.geyser.session.GeyserSession; @AllArgsConstructor public class GeyserSpigotBlockPlaceListener implements Listener { diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigot1_12NativeWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigot1_12NativeWorldManager.java index 670070a68..0ac8d6856 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigot1_12NativeWorldManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigot1_12NativeWorldManager.java @@ -30,10 +30,10 @@ import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.storage.BlockSto import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; -import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.adapters.spigot.SpigotAdapters; import org.geysermc.geyser.adapters.spigot.SpigotWorldAdapter; +import org.geysermc.geyser.level.block.BlockStateValues; +import org.geysermc.geyser.session.GeyserSession; /** * Used with ViaVersion and pre-1.13. diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigot1_12WorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigot1_12WorldManager.java index 1936d608f..2ca024abf 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigot1_12WorldManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigot1_12WorldManager.java @@ -36,8 +36,8 @@ import org.bukkit.Bukkit; import org.bukkit.block.Block; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.level.block.BlockStateValues; +import org.geysermc.geyser.session.GeyserSession; import java.util.List; diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotFallbackWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotFallbackWorldManager.java index 3079c523f..fa78a671c 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotFallbackWorldManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotFallbackWorldManager.java @@ -26,8 +26,8 @@ package org.geysermc.geyser.platform.spigot.world.manager; import org.bukkit.plugin.Plugin; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.level.block.BlockStateValues; +import org.geysermc.geyser.session.GeyserSession; /** * Should only be used when we know {@link GeyserSpigotWorldManager#getBlockAt(GeyserSession, int, int, int)} diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotLegacyNativeWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotLegacyNativeWorldManager.java index 2e0491db8..baffc9679 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotLegacyNativeWorldManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotLegacyNativeWorldManager.java @@ -32,9 +32,9 @@ import com.viaversion.viaversion.api.protocol.version.ProtocolVersion; import it.unimi.dsi.fastutil.ints.Int2IntMap; import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; import it.unimi.dsi.fastutil.ints.IntList; -import org.geysermc.geyser.network.MinecraftProtocol; -import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.platform.spigot.GeyserSpigotPlugin; +import org.geysermc.geyser.session.GeyserSession; import java.util.List; @@ -50,7 +50,7 @@ public class GeyserSpigotLegacyNativeWorldManager extends GeyserSpigotNativeWorl IntList allBlockStates = adapter.getAllBlockStates(); oldToNewBlockId = new Int2IntOpenHashMap(allBlockStates.size()); ProtocolVersion serverVersion = plugin.getServerProtocolVersion(); - List protocolList = Via.getManager().getProtocolManager().getProtocolPath(MinecraftProtocol.getJavaProtocolVersion(), + List protocolList = Via.getManager().getProtocolManager().getProtocolPath(GameProtocol.getJavaProtocolVersion(), serverVersion.getVersion()); for (int oldBlockId : allBlockStates) { int newBlockId = oldBlockId; diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotNativeWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotNativeWorldManager.java index 2db01ab4f..bf9085979 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotNativeWorldManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotNativeWorldManager.java @@ -28,10 +28,10 @@ package org.geysermc.geyser.platform.spigot.world.manager; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; -import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.adapters.spigot.SpigotAdapters; import org.geysermc.geyser.adapters.spigot.SpigotWorldAdapter; +import org.geysermc.geyser.level.block.BlockStateValues; +import org.geysermc.geyser.session.GeyserSession; public class GeyserSpigotNativeWorldManager extends GeyserSpigotWorldManager { protected final SpigotWorldAdapter adapter; diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotWorldManager.java index 0a6117b43..093e28794 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotWorldManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotWorldManager.java @@ -41,7 +41,7 @@ import org.bukkit.plugin.Plugin; import org.geysermc.geyser.level.GameRule; import org.geysermc.geyser.level.GeyserWorldManager; import org.geysermc.geyser.level.block.BlockStateValues; -import org.geysermc.geyser.network.MinecraftProtocol; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.inventory.LecternInventoryTranslator; @@ -57,7 +57,7 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager { /** * The current client protocol version for ViaVersion usage. */ - protected static final int CLIENT_PROTOCOL_VERSION = MinecraftProtocol.getJavaProtocolVersion(); + protected static final int CLIENT_PROTOCOL_VERSION = GameProtocol.getJavaProtocolVersion(); private final Plugin plugin; diff --git a/bootstrap/spigot/src/main/resources/plugin.yml b/bootstrap/spigot/src/main/resources/plugin.yml index aa2747979..e28b8981d 100644 --- a/bootstrap/spigot/src/main/resources/plugin.yml +++ b/bootstrap/spigot/src/main/resources/plugin.yml @@ -1,11 +1,11 @@ main: org.geysermc.geyser.platform.spigot.GeyserSpigotPlugin -name: ${outputName}-Spigot -author: ${project.organization.name} -website: ${project.organization.url} -version: ${project.version} +name: ${name}-Spigot +author: ${author} +website: ${url} +version: ${version} softdepend: ["ViaVersion", "floodgate"] api-version: 1.13 commands: geyser: description: The main command for Geyser. - usage: /geyser + usage: /geyser \ No newline at end of file diff --git a/bootstrap/sponge/build.gradle.kts b/bootstrap/sponge/build.gradle.kts new file mode 100644 index 000000000..2850b2c5e --- /dev/null +++ b/bootstrap/sponge/build.gradle.kts @@ -0,0 +1,36 @@ +val spongeVersion = "7.1.0" + +dependencies { + api(projects.core) +} + +platformRelocate("com.fasterxml.jackson") +platformRelocate("io.netty") +platformRelocate("it.unimi.dsi.fastutil") +platformRelocate("com.google.common") +platformRelocate("com.google.guava") +platformRelocate("net.kyori") + +// Exclude these dependencies +exclude("com.google.code.gson:*") +exclude("org.yaml:*") +exclude("org.slf4j:*") +exclude("org.ow2.asm:*") + +// These dependencies are already present on the platform +provided("org.spongepowered", "spongeapi", spongeVersion) + +application { + mainClass.set("org.geysermc.geyser.platform.sponge.GeyserSpongeMain") +} + +tasks.withType { + archiveBaseName.set("Geyser-Sponge") + + dependencies { + exclude(dependency("com.google.code.gson:.*")) + exclude(dependency("org.yaml:.*")) + exclude(dependency("org.slf4j:.*")) + exclude(dependency("org.ow2.asm:.*")) + } +} \ No newline at end of file diff --git a/bootstrap/sponge/pom.xml b/bootstrap/sponge/pom.xml deleted file mode 100644 index fc7bbc624..000000000 --- a/bootstrap/sponge/pom.xml +++ /dev/null @@ -1,101 +0,0 @@ - - - 4.0.0 - - org.geysermc - bootstrap-parent - 2.0.7-SNAPSHOT - - bootstrap-sponge - - - - org.geysermc - core - 2.0.7-SNAPSHOT - compile - - - org.spongepowered - spongeapi - 7.1.0 - provided - - - - ${outputName}-Sponge - - - src/main/resources/ - true - - - - - org.apache.maven.plugins - maven-jar-plugin - 3.2.0 - - - - org.geysermc.geyser.platform.sponge.GeyserSpongeMain - - - - - - org.apache.maven.plugins - maven-shade-plugin - 3.3.0 - - - package - - shade - - - - - com.fasterxml.jackson - org.geysermc.geyser.platform.sponge.shaded.jackson - - - io.netty - org.geysermc.geyser.platform.sponge.shaded.netty - - - it.unimi.dsi.fastutil - org.geysermc.geyser.platform.sponge.shaded.fastutil - - - com.google.common - org.geysermc.geyser.platform.sponge.shaded.google.common - - - com.google.guava - org.geysermc.geyser.platform.sponge.shaded.google.guava - - - net.kyori - org.geysermc.geyser.platform.sponge.shaded.kyori - - - - - - - - - com.google.code.gson:* - org.yaml:* - org.slf4j:* - org.ow2.asm:* - - - - - - - \ No newline at end of file diff --git a/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/GeyserSpongePingPassthrough.java b/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/GeyserSpongePingPassthrough.java index 7c01f18ce..a661061e2 100644 --- a/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/GeyserSpongePingPassthrough.java +++ b/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/GeyserSpongePingPassthrough.java @@ -25,8 +25,8 @@ package org.geysermc.geyser.platform.sponge; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.ping.GeyserPingInfo; -import org.geysermc.geyser.network.MinecraftProtocol; import org.geysermc.geyser.ping.IGeyserPingPassthrough; import org.spongepowered.api.MinecraftVersion; import org.spongepowered.api.Sponge; @@ -73,7 +73,7 @@ public class GeyserSpongePingPassthrough implements IGeyserPingPassthrough { ), new GeyserPingInfo.Version( event.getResponse().getVersion().getName(), - MinecraftProtocol.getJavaProtocolVersion()) // thanks for also not exposing this sponge + GameProtocol.getJavaProtocolVersion()) // thanks for also not exposing this sponge ); event.getResponse().getPlayers().get().getProfiles().stream() .map(GameProfile::getName) diff --git a/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/GeyserSpongePlugin.java b/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/GeyserSpongePlugin.java index f5d6613c7..42040f6ab 100644 --- a/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/GeyserSpongePlugin.java +++ b/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/GeyserSpongePlugin.java @@ -27,17 +27,19 @@ package org.geysermc.geyser.platform.sponge; import com.google.inject.Inject; import org.geysermc.common.PlatformType; -import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserBootstrap; -import org.geysermc.geyser.command.CommandManager; +import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.command.Command; +import org.geysermc.geyser.api.extension.Extension; +import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough; import org.geysermc.geyser.ping.IGeyserPingPassthrough; -import org.geysermc.geyser.util.FileUtils; -import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.platform.sponge.command.GeyserSpongeCommandExecutor; import org.geysermc.geyser.platform.sponge.command.GeyserSpongeCommandManager; +import org.geysermc.geyser.text.GeyserLocale; +import org.geysermc.geyser.util.FileUtils; import org.slf4j.Logger; import org.spongepowered.api.Sponge; import org.spongepowered.api.config.ConfigDir; @@ -50,6 +52,7 @@ import java.io.File; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.file.Path; +import java.util.Map; import java.util.UUID; @Plugin(id = "geyser", name = GeyserImpl.NAME + "-Sponge", version = GeyserImpl.VERSION, url = "https://geysermc.org", authors = "GeyserMC") @@ -69,8 +72,7 @@ public class GeyserSpongePlugin implements GeyserBootstrap { private GeyserImpl geyser; - @Override - public void onEnable() { + public void onLoad() { GeyserLocale.init(this); if (!configDir.exists()) @@ -99,19 +101,25 @@ public class GeyserSpongePlugin implements GeyserBootstrap { // Don't change the ip if its listening on all interfaces // By default this should be 127.0.0.1 but may need to be changed in some circumstances - if (this.geyserConfig.getRemote().getAddress().equalsIgnoreCase("auto")) { + if (this.geyserConfig.getRemote().address().equalsIgnoreCase("auto")) { this.geyserConfig.setAutoconfiguredRemote(true); geyserConfig.getRemote().setPort(javaAddr.getPort()); } } if (geyserConfig.getBedrock().isCloneRemotePort()) { - geyserConfig.getBedrock().setPort(geyserConfig.getRemote().getPort()); + geyserConfig.getBedrock().setPort(geyserConfig.getRemote().port()); } this.geyserLogger = new GeyserSpongeLogger(logger, geyserConfig.isDebugMode()); GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); - this.geyser = GeyserImpl.start(PlatformType.SPONGE, this); + + this.geyser = GeyserImpl.load(PlatformType.SPONGE, this); + } + + @Override + public void onEnable() { + GeyserImpl.start(); if (geyserConfig.isLegacyPingPassthrough()) { this.geyserSpongePingPassthrough = GeyserLegacyPingPassthrough.init(geyser); @@ -120,7 +128,18 @@ public class GeyserSpongePlugin implements GeyserBootstrap { } this.geyserCommandManager = new GeyserSpongeCommandManager(Sponge.getCommandManager(), geyser); - Sponge.getCommandManager().register(this, new GeyserSpongeCommandExecutor(geyser), "geyser"); + this.geyserCommandManager.init(); + + Sponge.getCommandManager().register(this, new GeyserSpongeCommandExecutor(geyser, geyserCommandManager.getCommands()), "geyser"); + + for (Map.Entry> entry : this.geyserCommandManager.extensionCommands().entrySet()) { + Map commands = entry.getValue(); + if (commands.isEmpty()) { + continue; + } + + Sponge.getCommandManager().register(this, new GeyserSpongeCommandExecutor(this.geyser, commands), entry.getKey().description().id()); + } } @Override @@ -139,7 +158,7 @@ public class GeyserSpongePlugin implements GeyserBootstrap { } @Override - public CommandManager getGeyserCommandManager() { + public GeyserCommandManager getGeyserCommandManager() { return this.geyserCommandManager; } @@ -153,6 +172,11 @@ public class GeyserSpongePlugin implements GeyserBootstrap { return configDir.toPath(); } + @Listener + public void onServerStarting() { + onLoad(); + } + @Listener public void onServerStart(GameStartedServerEvent event) { onEnable(); diff --git a/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/command/GeyserSpongeCommandExecutor.java b/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/command/GeyserSpongeCommandExecutor.java index 825d0bf78..3598ea8c2 100644 --- a/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/command/GeyserSpongeCommandExecutor.java +++ b/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/command/GeyserSpongeCommandExecutor.java @@ -26,11 +26,12 @@ package org.geysermc.geyser.platform.sponge.command; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandExecutor; -import org.geysermc.geyser.command.CommandSender; +import org.geysermc.geyser.api.command.Command; import org.geysermc.geyser.command.GeyserCommand; -import org.geysermc.geyser.text.ChatColor; +import org.geysermc.geyser.command.GeyserCommandExecutor; +import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.text.ChatColor; import org.geysermc.geyser.text.GeyserLocale; import org.spongepowered.api.command.CommandCallable; import org.spongepowered.api.command.CommandResult; @@ -40,27 +41,24 @@ import org.spongepowered.api.world.Location; import org.spongepowered.api.world.World; import javax.annotation.Nullable; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Optional; +import java.util.*; -public class GeyserSpongeCommandExecutor extends CommandExecutor implements CommandCallable { +public class GeyserSpongeCommandExecutor extends GeyserCommandExecutor implements CommandCallable { - public GeyserSpongeCommandExecutor(GeyserImpl geyser) { - super(geyser); + public GeyserSpongeCommandExecutor(GeyserImpl geyser, Map commands) { + super(geyser, commands); } @Override public CommandResult process(CommandSource source, String arguments) { - CommandSender commandSender = new SpongeCommandSender(source); + GeyserCommandSource commandSender = new SpongeCommandSource(source); GeyserSession session = getGeyserSession(commandSender); String[] args = arguments.split(" "); if (args.length > 0) { GeyserCommand command = getCommand(args[0]); if (command != null) { - if (!source.hasPermission(command.getPermission())) { + if (!source.hasPermission(command.permission())) { // Not ideal to use log here but we dont get a session source.sendMessage(Text.of(ChatColor.RED + GeyserLocale.getLocaleStringLog("geyser.bootstrap.command.permission_fail"))); return CommandResult.success(); @@ -80,7 +78,7 @@ public class GeyserSpongeCommandExecutor extends CommandExecutor implements Comm @Override public List getSuggestions(CommandSource source, String arguments, @Nullable Location targetPosition) { if (arguments.split(" ").length == 1) { - return tabComplete(new SpongeCommandSender(source)); + return tabComplete(new SpongeCommandSource(source)); } return Collections.emptyList(); } diff --git a/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/command/GeyserSpongeCommandManager.java b/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/command/GeyserSpongeCommandManager.java index dce39870d..8e981f72a 100644 --- a/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/command/GeyserSpongeCommandManager.java +++ b/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/command/GeyserSpongeCommandManager.java @@ -26,12 +26,12 @@ package org.geysermc.geyser.platform.sponge.command; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandManager; +import org.geysermc.geyser.command.GeyserCommandManager; import org.spongepowered.api.Sponge; import org.spongepowered.api.command.CommandMapping; import org.spongepowered.api.text.Text; -public class GeyserSpongeCommandManager extends CommandManager { +public class GeyserSpongeCommandManager extends GeyserCommandManager { private final org.spongepowered.api.command.CommandManager handle; public GeyserSpongeCommandManager(org.spongepowered.api.command.CommandManager handle, GeyserImpl geyser) { @@ -41,7 +41,7 @@ public class GeyserSpongeCommandManager extends CommandManager { } @Override - public String getDescription(String command) { + public String description(String command) { return handle.get(command).map(CommandMapping::getCallable) .map(callable -> callable.getShortDescription(Sponge.getServer().getConsole()).orElse(Text.EMPTY)) .orElse(Text.EMPTY).toPlain(); diff --git a/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/command/SpongeCommandSender.java b/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/command/SpongeCommandSource.java similarity index 94% rename from bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/command/SpongeCommandSender.java rename to bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/command/SpongeCommandSource.java index f57f3e276..12fdcb989 100644 --- a/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/command/SpongeCommandSender.java +++ b/bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/command/SpongeCommandSource.java @@ -26,14 +26,13 @@ package org.geysermc.geyser.platform.sponge.command; import lombok.AllArgsConstructor; - -import org.geysermc.geyser.command.CommandSender; +import org.geysermc.geyser.command.GeyserCommandSource; import org.spongepowered.api.command.CommandSource; import org.spongepowered.api.command.source.ConsoleSource; import org.spongepowered.api.text.Text; @AllArgsConstructor -public class SpongeCommandSender implements CommandSender { +public class SpongeCommandSource implements GeyserCommandSource { private CommandSource handle; diff --git a/bootstrap/standalone/build.gradle.kts b/bootstrap/standalone/build.gradle.kts new file mode 100644 index 000000000..3c1a10b09 --- /dev/null +++ b/bootstrap/standalone/build.gradle.kts @@ -0,0 +1,33 @@ +import com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCacheFileTransformer + +val terminalConsoleVersion = "1.2.0" +val jlineVersion = "3.21.0" + +dependencies { + api(projects.core) + + implementation("net.minecrell", "terminalconsoleappender", terminalConsoleVersion) { + exclude("org.apache.logging.log4j", "log4j-core") + exclude("org.jline", "jline-reader") + exclude("org.jline", "jline-terminal") + exclude("org.jline", "jline-terminal-jna") + } + + implementation("org.jline", "jline-terminal", jlineVersion) + implementation("org.jline", "jline-terminal-jna", jlineVersion) + implementation("org.jline", "jline-reader", jlineVersion) + + implementation("org.apache.logging.log4j", "log4j-api", Versions.log4jVersion) + implementation("org.apache.logging.log4j", "log4j-core", Versions.log4jVersion) + implementation("org.apache.logging.log4j", "log4j-slf4j18-impl", Versions.log4jVersion) +} + +application { + mainClass.set("org.geysermc.geyser.platform.standalone.GeyserStandaloneBootstrap") +} + +tasks.withType { + archiveBaseName.set("Geyser-Standalone") + + transform(Log4j2PluginsCacheFileTransformer()) +} \ No newline at end of file diff --git a/bootstrap/standalone/pom.xml b/bootstrap/standalone/pom.xml deleted file mode 100644 index 5577f9206..000000000 --- a/bootstrap/standalone/pom.xml +++ /dev/null @@ -1,139 +0,0 @@ - - - 4.0.0 - - org.geysermc - bootstrap-parent - 2.0.7-SNAPSHOT - - bootstrap-standalone - - - 2.17.1 - - - - - org.geysermc - core - 2.0.7-SNAPSHOT - compile - - - net.minecrell - terminalconsoleappender - 1.2.0 - - - org.apache.logging.log4j - log4j-core - - - org.jline - jline-reader - - - org.jline - jline-terminal-jna - - - org.jline - jline-terminal - - - - - org.jline - jline-terminal - 3.21.0 - - - org.jline - jline-terminal-jna - 3.21.0 - - - org.jline - jline-reader - 3.21.0 - - - org.apache.logging.log4j - log4j-api - ${log4j.version} - - - org.apache.logging.log4j - log4j-core - ${log4j.version} - - - org.apache.logging.log4j - log4j-slf4j18-impl - ${log4j.version} - - - - ${outputName} - - - org.apache.maven.plugins - maven-jar-plugin - 3.2.0 - - - - org.geysermc.geyser.platform.standalone.GeyserStandaloneBootstrap - - - - - - org.apache.maven.plugins - maven-shade-plugin - 3.3.0 - - - com.github.edwgiz - maven-shade-plugin.log4j2-cachefile-transformer - 2.8.1 - - - - - package - - shade - - - false - - - - - - - *:* - - META-INF/versions/9/module-info.class - - - - - - org.geysermc.geyser.platform.standalone.GeyserStandaloneBootstrap - - true - - - - - - - - - - diff --git a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java index ca41fbd72..80d17f6a7 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java @@ -39,18 +39,18 @@ import org.apache.logging.log4j.core.Appender; import org.apache.logging.log4j.core.Logger; import org.apache.logging.log4j.core.appender.ConsoleAppender; import org.geysermc.common.PlatformType; -import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserBootstrap; -import org.geysermc.geyser.command.CommandManager; +import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.configuration.GeyserJacksonConfiguration; import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough; import org.geysermc.geyser.ping.IGeyserPingPassthrough; -import org.geysermc.geyser.util.FileUtils; -import org.geysermc.geyser.text.GeyserLocale; -import org.geysermc.geyser.platform.standalone.command.GeyserCommandManager; +import org.geysermc.geyser.platform.standalone.command.GeyserStandaloneCommandManager; import org.geysermc.geyser.platform.standalone.gui.GeyserStandaloneGUI; +import org.geysermc.geyser.text.GeyserLocale; +import org.geysermc.geyser.util.FileUtils; import org.geysermc.geyser.util.LoopbackUtil; import java.io.File; @@ -64,7 +64,7 @@ import java.util.stream.Collectors; public class GeyserStandaloneBootstrap implements GeyserBootstrap { - private GeyserCommandManager geyserCommandManager; + private GeyserStandaloneCommandManager geyserCommandManager; private GeyserStandaloneConfiguration geyserConfig; private GeyserStandaloneLogger geyserLogger; private IGeyserPingPassthrough geyserPingPassthrough; @@ -180,6 +180,7 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap { logger.removeAppender(appender); } } + if (useGui && gui == null) { gui = new GeyserStandaloneGUI(); gui.redirectSystemStreams(); @@ -197,7 +198,7 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap { handleArgsConfigOptions(); - if (this.geyserConfig.getRemote().getAddress().equalsIgnoreCase("auto")) { + if (this.geyserConfig.getRemote().address().equalsIgnoreCase("auto")) { geyserConfig.setAutoconfiguredRemote(true); // Doesn't really need to be set but /shrug geyserConfig.getRemote().setAddress("127.0.0.1"); } @@ -216,8 +217,11 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap { // Allow libraries like Protocol to have their debug information passthrough logger.get().setLevel(geyserConfig.isDebugMode() ? Level.DEBUG : Level.INFO); - geyser = GeyserImpl.start(PlatformType.STANDALONE, this); - geyserCommandManager = new GeyserCommandManager(geyser); + geyser = GeyserImpl.load(PlatformType.STANDALONE, this); + GeyserImpl.start(); + + geyserCommandManager = new GeyserStandaloneCommandManager(geyser); + geyserCommandManager.init(); if (gui != null) { gui.setupInterface(geyserLogger, geyserCommandManager); @@ -262,7 +266,7 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap { } @Override - public CommandManager getGeyserCommandManager() { + public GeyserCommandManager getGeyserCommandManager() { return geyserCommandManager; } diff --git a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneLogger.java b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneLogger.java index 78e603d7c..e7e24a465 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneLogger.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneLogger.java @@ -25,16 +25,17 @@ package org.geysermc.geyser.platform.standalone; -import lombok.extern.log4j.Log4j2; +import lombok.extern.slf4j.Slf4j; import net.minecrell.terminalconsole.SimpleTerminalConsole; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.config.Configurator; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserLogger; +import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.text.ChatColor; -@Log4j2 -public class GeyserStandaloneLogger extends SimpleTerminalConsole implements GeyserLogger { +@Slf4j +public class GeyserStandaloneLogger extends SimpleTerminalConsole implements GeyserLogger, GeyserCommandSource { @Override protected boolean isRunning() { @@ -43,7 +44,7 @@ public class GeyserStandaloneLogger extends SimpleTerminalConsole implements Gey @Override protected void runCommand(String line) { - GeyserImpl.getInstance().getCommandManager().runCommand(this, line); + GeyserImpl.getInstance().commandManager().runCommand(this, line); } @Override @@ -53,12 +54,12 @@ public class GeyserStandaloneLogger extends SimpleTerminalConsole implements Gey @Override public void severe(String message) { - log.fatal(ChatColor.DARK_RED + message); + log.error(ChatColor.DARK_RED + message); } @Override public void severe(String message, Throwable error) { - log.fatal(ChatColor.DARK_RED + message, error); + log.error(ChatColor.DARK_RED + message, error); } @Override diff --git a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/command/GeyserCommandManager.java b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/command/GeyserStandaloneCommandManager.java similarity index 85% rename from bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/command/GeyserCommandManager.java rename to bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/command/GeyserStandaloneCommandManager.java index 03d780f3c..e7b4cbe37 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/command/GeyserCommandManager.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/command/GeyserStandaloneCommandManager.java @@ -26,16 +26,16 @@ package org.geysermc.geyser.platform.standalone.command; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandManager; +import org.geysermc.geyser.command.GeyserCommandManager; -public class GeyserCommandManager extends CommandManager { +public class GeyserStandaloneCommandManager extends GeyserCommandManager { - public GeyserCommandManager(GeyserImpl geyser) { + public GeyserStandaloneCommandManager(GeyserImpl geyser) { super(geyser); } @Override - public String getDescription(String command) { + public String description(String command) { return ""; // this is not sent over the protocol, so we return none } } diff --git a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/gui/GeyserStandaloneGUI.java b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/gui/GeyserStandaloneGUI.java index 44faabdf5..a8bce303f 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/gui/GeyserStandaloneGUI.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/gui/GeyserStandaloneGUI.java @@ -26,11 +26,12 @@ package org.geysermc.geyser.platform.standalone.gui; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.command.Command; import org.geysermc.geyser.command.GeyserCommand; +import org.geysermc.geyser.platform.standalone.GeyserStandaloneLogger; +import org.geysermc.geyser.platform.standalone.command.GeyserStandaloneCommandManager; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.GeyserLocale; -import org.geysermc.geyser.platform.standalone.GeyserStandaloneLogger; -import org.geysermc.geyser.platform.standalone.command.GeyserCommandManager; import javax.swing.*; import javax.swing.table.DefaultTableModel; @@ -255,33 +256,34 @@ public class GeyserStandaloneGUI { * @param geyserStandaloneLogger The current logger * @param geyserCommandManager The commands manager */ - public void setupInterface(GeyserStandaloneLogger geyserStandaloneLogger, GeyserCommandManager geyserCommandManager) { + public void setupInterface(GeyserStandaloneLogger geyserStandaloneLogger, GeyserStandaloneCommandManager geyserCommandManager) { commandsMenu.removeAll(); optionsMenu.removeAll(); - for (Map.Entry command : geyserCommandManager.getCommands().entrySet()) { + for (Map.Entry entry : geyserCommandManager.getCommands().entrySet()) { // Remove the offhand command and any alias commands to prevent duplicates in the list - if (!command.getValue().isExecutableOnConsole() || command.getValue().getAliases().contains(command.getKey())) { + if (!entry.getValue().isExecutableOnConsole() || entry.getValue().aliases().contains(entry.getKey())) { continue; } + GeyserCommand command = (GeyserCommand) entry.getValue(); // Create the button that runs the command - boolean hasSubCommands = command.getValue().hasSubCommands(); + boolean hasSubCommands = !entry.getValue().subCommands().isEmpty(); // Add an extra menu if there are more commands that can be run - JMenuItem commandButton = hasSubCommands ? new JMenu(command.getValue().getName()) : new JMenuItem(command.getValue().getName()); - commandButton.getAccessibleContext().setAccessibleDescription(command.getValue().getDescription()); + JMenuItem commandButton = hasSubCommands ? new JMenu(entry.getValue().name()) : new JMenuItem(entry.getValue().name()); + commandButton.getAccessibleContext().setAccessibleDescription(entry.getValue().description()); if (!hasSubCommands) { - commandButton.addActionListener(e -> command.getValue().execute(null, geyserStandaloneLogger, new String[]{ })); + commandButton.addActionListener(e -> command.execute(null, geyserStandaloneLogger, new String[]{ })); } else { // Add a submenu that's the same name as the menu can't be pressed - JMenuItem otherCommandButton = new JMenuItem(command.getValue().getName()); - otherCommandButton.getAccessibleContext().setAccessibleDescription(command.getValue().getDescription()); - otherCommandButton.addActionListener(e -> command.getValue().execute(null, geyserStandaloneLogger, new String[]{ })); + JMenuItem otherCommandButton = new JMenuItem(entry.getValue().name()); + otherCommandButton.getAccessibleContext().setAccessibleDescription(entry.getValue().description()); + otherCommandButton.addActionListener(e -> command.execute(null, geyserStandaloneLogger, new String[]{ })); commandButton.add(otherCommandButton); // Add a menu option for all possible subcommands - for (String subCommandName : command.getValue().getSubCommands()) { + for (String subCommandName : entry.getValue().subCommands()) { JMenuItem item = new JMenuItem(subCommandName); - item.addActionListener(e -> command.getValue().execute(null, geyserStandaloneLogger, new String[]{subCommandName})); + item.addActionListener(e -> command.execute(null, geyserStandaloneLogger, new String[]{subCommandName})); commandButton.add(item); } } diff --git a/bootstrap/standalone/src/main/resources/log4j2.xml b/bootstrap/standalone/src/main/resources/log4j2.xml index cd101f306..0738acdcd 100644 --- a/bootstrap/standalone/src/main/resources/log4j2.xml +++ b/bootstrap/standalone/src/main/resources/log4j2.xml @@ -16,7 +16,7 @@ - + diff --git a/bootstrap/velocity/build.gradle.kts b/bootstrap/velocity/build.gradle.kts new file mode 100644 index 000000000..ab2f85b85 --- /dev/null +++ b/bootstrap/velocity/build.gradle.kts @@ -0,0 +1,69 @@ +val velocityVersion = "3.0.0" + +dependencies { + annotationProcessor("com.velocitypowered", "velocity-api", velocityVersion) + api(projects.core) +} + +platformRelocate("com.fasterxml.jackson") +platformRelocate("it.unimi.dsi.fastutil") +platformRelocate("net.kyori.adventure.text.serializer.gson.legacyimpl") + +exclude("com.google.*:*") + +// Needed because Velocity provides every dependency except netty-resolver-dns +exclude("io.netty:netty-transport-native-epoll:*") +exclude("io.netty:netty-transport-native-unix-common:*") +exclude("io.netty:netty-transport-native-kqueue:*") +exclude("io.netty:netty-handler:*") +exclude("io.netty:netty-common:*") +exclude("io.netty:netty-buffer:*") +exclude("io.netty:netty-resolver:*") +exclude("io.netty:netty-transport:*") +exclude("io.netty:netty-codec:*") +exclude("io.netty:netty-codec-haproxy:*") +exclude("org.slf4j:*") +exclude("org.ow2.asm:*") + +// Exclude all Kyori dependencies except the legacy NBT serializer +exclude("net.kyori:adventure-api:*") +exclude("net.kyori:examination-api:*") +exclude("net.kyori:examination-string:*") +exclude("net.kyori:adventure-text-serializer-gson:*") +exclude("net.kyori:adventure-text-serializer-legacy:*") +exclude("net.kyori:adventure-nbt:*") + +// These dependencies are already present on the platform +provided("com.velocitypowered", "velocity-api", velocityVersion) + +application { + mainClass.set("org.geysermc.geyser.platform.velocity.GeyserVelocityMain") +} + +tasks.withType { + archiveBaseName.set("Geyser-Velocity") + + dependencies { + exclude(dependency("com.google.*:.*")) + // Needed because Velocity provides every dependency except netty-resolver-dns + exclude(dependency("io.netty:netty-transport-native-epoll:.*")) + exclude(dependency("io.netty:netty-transport-native-unix-common:.*")) + exclude(dependency("io.netty:netty-transport-native-kqueue:.*")) + exclude(dependency("io.netty:netty-handler:.*")) + exclude(dependency("io.netty:netty-common:.*")) + exclude(dependency("io.netty:netty-buffer:.*")) + exclude(dependency("io.netty:netty-resolver:.*")) + exclude(dependency("io.netty:netty-transport:.*")) + exclude(dependency("io.netty:netty-codec:.*")) + exclude(dependency("io.netty:netty-codec-haproxy:.*")) + exclude(dependency("org.slf4j:.*")) + exclude(dependency("org.ow2.asm:.*")) + // Exclude all Kyori dependencies except the legacy NBT serializer + exclude(dependency("net.kyori:adventure-api:.*")) + exclude(dependency("net.kyori:examination-api:.*")) + exclude(dependency("net.kyori:examination-string:.*")) + exclude(dependency("net.kyori:adventure-text-serializer-gson:.*")) + exclude(dependency("net.kyori:adventure-text-serializer-legacy:.*")) + exclude(dependency("net.kyori:adventure-nbt:.*")) + } +} \ No newline at end of file diff --git a/bootstrap/velocity/pom.xml b/bootstrap/velocity/pom.xml deleted file mode 100644 index 35e6df15b..000000000 --- a/bootstrap/velocity/pom.xml +++ /dev/null @@ -1,106 +0,0 @@ - - - 4.0.0 - - org.geysermc - bootstrap-parent - 2.0.7-SNAPSHOT - - bootstrap-velocity - - - - org.geysermc - core - 2.0.7-SNAPSHOT - compile - - - com.velocitypowered - velocity-api - 3.0.0 - provided - - - - ${outputName}-Velocity - - - src/main/resources/ - true - - - - - org.apache.maven.plugins - maven-jar-plugin - 3.2.0 - - - - org.geysermc.geyser.platform.velocity.GeyserVelocityMain - - - - - - org.apache.maven.plugins - maven-shade-plugin - 3.3.0 - - - package - - shade - - - - - com.fasterxml.jackson - org.geysermc.geyser.platform.velocity.shaded.jackson - - - it.unimi.dsi.fastutil - org.geysermc.geyser.platform.velocity.shaded.fastutil - - - net.kyori.adventure.text.serializer.gson.legacyimpl - org.geysermc.geyser.platform.velocity.shaded.kyori.legacyimpl - - - - - - - - - com.google.*:* - - io.netty:netty-transport-native-epoll:* - io.netty:netty-transport-native-unix-common:* - io.netty:netty-transport-native-kqueue:* - io.netty:netty-handler:* - io.netty:netty-common:* - io.netty:netty-buffer:* - io.netty:netty-resolver:* - io.netty:netty-transport:* - io.netty:netty-codec:* - io.netty:netty-codec-haproxy:* - org.slf4j:* - org.ow2.asm:* - - net.kyori:adventure-api:* - net.kyori:examination-api:* - net.kyori:examination-string:* - net.kyori:adventure-text-serializer-gson:* - net.kyori:adventure-text-serializer-legacy:* - net.kyori:adventure-nbt:* - - - - - - - \ No newline at end of file diff --git a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityDumpInfo.java b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityDumpInfo.java index ffc7db291..9f429cc83 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityDumpInfo.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityDumpInfo.java @@ -28,8 +28,8 @@ package org.geysermc.geyser.platform.velocity; import com.velocitypowered.api.plugin.PluginContainer; import com.velocitypowered.api.proxy.ProxyServer; import lombok.Getter; -import org.geysermc.geyser.text.AsteriskSerializer; import org.geysermc.geyser.dump.BootstrapDumpInfo; +import org.geysermc.geyser.text.AsteriskSerializer; import java.util.ArrayList; import java.util.List; diff --git a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java index 13a07121e..dc31b3fdd 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java @@ -39,13 +39,16 @@ import net.kyori.adventure.util.Codec; import org.geysermc.common.PlatformType; import org.geysermc.geyser.GeyserBootstrap; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.command.Command; +import org.geysermc.geyser.api.extension.Extension; +import org.geysermc.geyser.api.network.AuthType; +import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough; import org.geysermc.geyser.ping.IGeyserPingPassthrough; import org.geysermc.geyser.platform.velocity.command.GeyserVelocityCommandExecutor; import org.geysermc.geyser.platform.velocity.command.GeyserVelocityCommandManager; -import org.geysermc.geyser.session.auth.AuthType; import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.util.FileUtils; import org.jetbrains.annotations.Nullable; @@ -57,6 +60,7 @@ import java.net.InetSocketAddress; import java.net.SocketAddress; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Map; import java.util.UUID; @Plugin(id = "geyser", name = GeyserImpl.NAME + "-Velocity", version = GeyserImpl.VERSION, url = "https://geysermc.org", authors = "GeyserMC") @@ -84,15 +88,6 @@ public class GeyserVelocityPlugin implements GeyserBootstrap { @Override public void onEnable() { - try { - Codec.class.getMethod("codec", Codec.Decoder.class, Codec.Encoder.class); - } catch (NoSuchMethodException e) { - // velocitypowered.com has a build that is very outdated - logger.error("Please download Velocity from https://papermc.io/downloads#Velocity - the 'stable' Velocity version " + - "that has likely been downloaded is very outdated and does not support 1.19."); - return; - } - GeyserLocale.init(this); try { @@ -111,7 +106,7 @@ public class GeyserVelocityPlugin implements GeyserBootstrap { InetSocketAddress javaAddr = proxyServer.getBoundAddress(); // By default this should be localhost but may need to be changed in some circumstances - if (this.geyserConfig.getRemote().getAddress().equalsIgnoreCase("auto")) { + if (this.geyserConfig.getRemote().address().equalsIgnoreCase("auto")) { this.geyserConfig.setAutoconfiguredRemote(true); // Don't use localhost if not listening on all interfaces if (!javaAddr.getHostString().equals("0.0.0.0") && !javaAddr.getHostString().equals("")) { @@ -127,6 +122,17 @@ public class GeyserVelocityPlugin implements GeyserBootstrap { this.geyserLogger = new GeyserVelocityLogger(logger, geyserConfig.isDebugMode()); GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); + this.geyser = GeyserImpl.load(PlatformType.VELOCITY, this); + + try { + Codec.class.getMethod("codec", Codec.Decoder.class, Codec.Encoder.class); + } catch (NoSuchMethodException e) { + // velocitypowered.com has a build that is very outdated + logger.error("Please download Velocity from https://papermc.io/downloads#Velocity - the 'stable' Velocity version " + + "that has likely been downloaded is very outdated and does not support 1.19."); + return; + } + // Remove this in like a year try { // Should only exist on 1.0 @@ -137,7 +143,7 @@ public class GeyserVelocityPlugin implements GeyserBootstrap { } catch (ClassNotFoundException ignored) { } - if (geyserConfig.getRemote().getAuthType() == AuthType.FLOODGATE && proxyServer.getPluginManager().getPlugin("floodgate").isEmpty()) { + if (geyserConfig.getRemote().authType() == AuthType.FLOODGATE && proxyServer.getPluginManager().getPlugin("floodgate").isEmpty()) { geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.disabling")); return; @@ -149,13 +155,27 @@ public class GeyserVelocityPlugin implements GeyserBootstrap { geyserConfig.loadFloodgate(this, proxyServer, configFolder.toFile()); - this.geyser = GeyserImpl.start(PlatformType.VELOCITY, this); + } + + private void postStartup() { + GeyserImpl.start(); this.geyserInjector = new GeyserVelocityInjector(proxyServer); // Will be initialized after the proxy has been bound this.geyserCommandManager = new GeyserVelocityCommandManager(geyser); - this.commandManager.register("geyser", new GeyserVelocityCommandExecutor(geyser)); + this.geyserCommandManager.init(); + + this.commandManager.register("geyser", new GeyserVelocityCommandExecutor(geyser, geyserCommandManager.getCommands())); + for (Map.Entry> entry : this.geyserCommandManager.extensionCommands().entrySet()) { + Map commands = entry.getValue(); + if (commands.isEmpty()) { + continue; + } + + this.commandManager.register(entry.getKey().description().id(), new GeyserVelocityCommandExecutor(this.geyser, commands)); + } + if (geyserConfig.isLegacyPingPassthrough()) { this.geyserPingPassthrough = GeyserLegacyPingPassthrough.init(geyser); } else { @@ -186,7 +206,7 @@ public class GeyserVelocityPlugin implements GeyserBootstrap { } @Override - public org.geysermc.geyser.command.CommandManager getGeyserCommandManager() { + public GeyserCommandManager getGeyserCommandManager() { return this.geyserCommandManager; } @@ -207,9 +227,14 @@ public class GeyserVelocityPlugin implements GeyserBootstrap { @Subscribe public void onProxyBound(ListenerBoundEvent event) { - if (event.getListenerType() == ListenerType.MINECRAFT && geyserInjector != null) { - // After this bound, we know that the channel initializer cannot change without it being ineffective for Velocity, too - geyserInjector.initializeLocalChannel(this); + if (event.getListenerType() == ListenerType.MINECRAFT) { + // Once listener is bound, do our startup process + this.postStartup(); + + if (geyserInjector != null) { + // After this bound, we know that the channel initializer cannot change without it being ineffective for Velocity, too + geyserInjector.initializeLocalChannel(this); + } } } diff --git a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityUpdateListener.java b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityUpdateListener.java index 506dfff71..31e584612 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityUpdateListener.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityUpdateListener.java @@ -30,7 +30,7 @@ import com.velocitypowered.api.event.connection.PostLoginEvent; import com.velocitypowered.api.proxy.Player; import org.geysermc.geyser.Constants; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.platform.velocity.command.VelocityCommandSender; +import org.geysermc.geyser.platform.velocity.command.VelocityCommandSource; import org.geysermc.geyser.util.VersionCheckUtils; public final class GeyserVelocityUpdateListener { @@ -40,7 +40,7 @@ public final class GeyserVelocityUpdateListener { if (GeyserImpl.getInstance().getConfig().isNotifyOnNewBedrockUpdate()) { final Player player = event.getPlayer(); if (player.hasPermission(Constants.UPDATE_PERMISSION)) { - VersionCheckUtils.checkForGeyserUpdate(() -> new VelocityCommandSender(player)); + VersionCheckUtils.checkForGeyserUpdate(() -> new VelocityCommandSource(player)); } } } diff --git a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/command/GeyserVelocityCommandExecutor.java b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/command/GeyserVelocityCommandExecutor.java index 30f6c2efd..c77a3daef 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/command/GeyserVelocityCommandExecutor.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/command/GeyserVelocityCommandExecutor.java @@ -27,37 +27,39 @@ package org.geysermc.geyser.platform.velocity.command; import com.velocitypowered.api.command.SimpleCommand; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandExecutor; -import org.geysermc.geyser.command.CommandSender; +import org.geysermc.geyser.api.command.Command; import org.geysermc.geyser.command.GeyserCommand; -import org.geysermc.geyser.text.ChatColor; +import org.geysermc.geyser.command.GeyserCommandExecutor; +import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.text.ChatColor; import org.geysermc.geyser.text.GeyserLocale; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Map; -public class GeyserVelocityCommandExecutor extends CommandExecutor implements SimpleCommand { +public class GeyserVelocityCommandExecutor extends GeyserCommandExecutor implements SimpleCommand { - public GeyserVelocityCommandExecutor(GeyserImpl geyser) { - super(geyser); + public GeyserVelocityCommandExecutor(GeyserImpl geyser, Map commands) { + super(geyser, commands); } @Override public void execute(Invocation invocation) { - CommandSender sender = new VelocityCommandSender(invocation.source()); + GeyserCommandSource sender = new VelocityCommandSource(invocation.source()); GeyserSession session = getGeyserSession(sender); if (invocation.arguments().length > 0) { GeyserCommand command = getCommand(invocation.arguments()[0]); if (command != null) { - if (!invocation.source().hasPermission(getCommand(invocation.arguments()[0]).getPermission())) { - sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", sender.getLocale())); + if (!invocation.source().hasPermission(getCommand(invocation.arguments()[0]).permission())) { + sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", sender.locale())); return; } if (command.isBedrockOnly() && session == null) { - sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.bedrock_only", sender.getLocale())); + sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.bedrock_only", sender.locale())); return; } command.execute(session, sender, invocation.arguments().length > 1 ? Arrays.copyOfRange(invocation.arguments(), 1, invocation.arguments().length) : new String[0]); @@ -71,7 +73,7 @@ public class GeyserVelocityCommandExecutor extends CommandExecutor implements Si public List suggest(Invocation invocation) { // Velocity seems to do the splitting a bit differently. This results in the same behaviour in bungeecord/spigot. if (invocation.arguments().length == 0 || invocation.arguments().length == 1) { - return tabComplete(new VelocityCommandSender(invocation.source())); + return tabComplete(new VelocityCommandSource(invocation.source())); } return Collections.emptyList(); } diff --git a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/command/GeyserVelocityCommandManager.java b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/command/GeyserVelocityCommandManager.java index b42c8f76e..6f9faba8f 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/command/GeyserVelocityCommandManager.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/command/GeyserVelocityCommandManager.java @@ -26,16 +26,16 @@ package org.geysermc.geyser.platform.velocity.command; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandManager; +import org.geysermc.geyser.command.GeyserCommandManager; -public class GeyserVelocityCommandManager extends CommandManager { +public class GeyserVelocityCommandManager extends GeyserCommandManager { public GeyserVelocityCommandManager(GeyserImpl geyser) { super(geyser); } @Override - public String getDescription(String command) { + public String description(String command) { return ""; // no support for command descriptions in velocity } } diff --git a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/command/VelocityCommandSender.java b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/command/VelocityCommandSource.java similarity index 91% rename from bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/command/VelocityCommandSender.java rename to bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/command/VelocityCommandSource.java index a5474c3e0..00c99e92b 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/command/VelocityCommandSender.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/command/VelocityCommandSource.java @@ -30,19 +30,19 @@ import com.velocitypowered.api.proxy.ConsoleCommandSource; import com.velocitypowered.api.proxy.Player; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; -import org.geysermc.geyser.command.CommandSender; +import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.text.GeyserLocale; import java.util.Locale; -public class VelocityCommandSender implements CommandSender { +public class VelocityCommandSource implements GeyserCommandSource { private final CommandSource handle; - public VelocityCommandSender(CommandSource handle) { + public VelocityCommandSource(CommandSource handle) { this.handle = handle; // Ensure even Java players' languages are loaded - GeyserLocale.loadGeyserLocale(getLocale()); + GeyserLocale.loadGeyserLocale(this.locale()); } @Override @@ -72,7 +72,7 @@ public class VelocityCommandSender implements CommandSender { } @Override - public String getLocale() { + public String locale() { if (handle instanceof Player) { Locale locale = ((Player) handle).getPlayerSettings().getLocale(); return GeyserLocale.formatLocale(locale.getLanguage() + "_" + locale.getCountry()); diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts new file mode 100644 index 000000000..25cbfe9de --- /dev/null +++ b/build-logic/build.gradle.kts @@ -0,0 +1,21 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + `kotlin-dsl` +} + +repositories { + gradlePluginPortal() +} + +dependencies { + implementation("net.kyori", "indra-common", "2.0.6") + implementation("org.jfrog.buildinfo", "build-info-extractor-gradle", "4.26.1") + implementation("gradle.plugin.com.github.johnrengelman", "shadow", "7.1.1") +} + +tasks.withType { + kotlinOptions { + jvmTarget = "16" + } +} \ No newline at end of file diff --git a/build-logic/src/main/kotlin/Versions.kt b/build-logic/src/main/kotlin/Versions.kt new file mode 100644 index 000000000..47f35726d --- /dev/null +++ b/build-logic/src/main/kotlin/Versions.kt @@ -0,0 +1,46 @@ +/* + * 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/Geyser + */ + +object Versions { + const val jacksonVersion = "2.13.2" + const val fastutilVersion = "8.5.2" + const val nettyVersion = "4.1.80.Final" + const val guavaVersion = "29.0-jre" + const val gsonVersion = "2.3.1" // Provided by Spigot 1.8.8 + const val nbtVersion = "2.1.0" + const val websocketVersion = "1.5.1" + const val protocolVersion = "fed46166" + const val raknetVersion = "1.6.28-20220125.214016-6" + const val mcauthlibVersion = "d9d773e" + const val mcprotocollibversion = "9f78bd5" + const val packetlibVersion = "3.0" + const val adventureVersion = "4.12.0-20220629.025215-9" + const val adventurePlatformVersion = "4.1.2" + const val junitVersion = "4.13.1" + const val checkerQualVersion = "3.19.0" + const val cumulusVersion = "1.1.1" + const val eventsVersion = "1.0-SNAPSHOT" + const val log4jVersion = "2.17.1" +} \ No newline at end of file diff --git a/build-logic/src/main/kotlin/extensions.kt b/build-logic/src/main/kotlin/extensions.kt new file mode 100644 index 000000000..43cdafdcc --- /dev/null +++ b/build-logic/src/main/kotlin/extensions.kt @@ -0,0 +1,68 @@ +/* + * 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/Geyser + */ + +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import org.gradle.api.Project +import org.gradle.api.artifacts.ProjectDependency +import org.gradle.kotlin.dsl.named + +fun Project.isSnapshot(): Boolean = + version.toString().endsWith("-SNAPSHOT") + +fun Project.relocate(pattern: String) { + tasks.named("shadowJar") { + relocate(pattern, "org.geysermc.geyser.shaded.$pattern") + } +} + +fun Project.exclude(group: String) { + tasks.named("shadowJar") { + exclude(group) + } +} + +fun Project.platformRelocate(pattern: String, exclusion: String = "") { + tasks.named("shadowJar") { + relocate(pattern, "org.geysermc.geyser.platform.${project.name}.shaded.$pattern") { + exclude(exclusion) + } + } +} + +val providedDependencies = mutableMapOf>() + +fun Project.provided(pattern: String, name: String, version: String, excludedOn: Int = 0b110) { + providedDependencies.getOrPut(project.name) { mutableSetOf() } + .add("${calcExclusion(pattern, 0b100, excludedOn)}:" + + "${calcExclusion(name, 0b10, excludedOn)}:" + + calcExclusion(version, 0b1, excludedOn)) + dependencies.add("compileOnlyApi", "$pattern:$name:$version") +} + +fun Project.provided(dependency: ProjectDependency) = + provided(dependency.group!!, dependency.name, dependency.version!!) + +private fun calcExclusion(section: String, bit: Int, excludedOn: Int): String = + if (excludedOn and bit > 0) section else "" \ No newline at end of file diff --git a/build-logic/src/main/kotlin/geyser.api-conventions.gradle.kts b/build-logic/src/main/kotlin/geyser.api-conventions.gradle.kts new file mode 100644 index 000000000..7c8f9a3d7 --- /dev/null +++ b/build-logic/src/main/kotlin/geyser.api-conventions.gradle.kts @@ -0,0 +1,9 @@ +plugins { + id("geyser.publish-conventions") +} + +tasks { + shadowJar { + archiveBaseName.set(archiveBaseName.get() + "-api") + } +} \ No newline at end of file diff --git a/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts b/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts new file mode 100644 index 000000000..2ea5d88a4 --- /dev/null +++ b/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts @@ -0,0 +1,33 @@ +plugins { + `java-library` + `maven-publish` +} + +dependencies { + compileOnly("org.checkerframework", "checker-qual", Versions.checkerQualVersion) +} + +tasks { + processResources { + filesMatching(listOf("plugin.yml", "bungee.yml", "velocity-plugin.json")) { + expand( + "id" to "Geyser", + "name" to "Geyser", + "version" to project.version, + "description" to project.description, + "url" to "https://geysermc.org", + "author" to "GeyserMC" + ) + } + } + compileJava { + options.encoding = Charsets.UTF_8.name() + } +} + +java { + sourceCompatibility = JavaVersion.VERSION_16 + targetCompatibility = JavaVersion.VERSION_16 + + withSourcesJar() +} \ No newline at end of file diff --git a/build-logic/src/main/kotlin/geyser.build-logic.gradle.kts b/build-logic/src/main/kotlin/geyser.build-logic.gradle.kts new file mode 100644 index 000000000..e69de29bb diff --git a/build-logic/src/main/kotlin/geyser.platform-conventions.gradle.kts b/build-logic/src/main/kotlin/geyser.platform-conventions.gradle.kts new file mode 100644 index 000000000..81d224906 --- /dev/null +++ b/build-logic/src/main/kotlin/geyser.platform-conventions.gradle.kts @@ -0,0 +1,4 @@ +plugins { + application + id("geyser.publish-conventions") +} \ No newline at end of file diff --git a/build-logic/src/main/kotlin/geyser.publish-conventions.gradle.kts b/build-logic/src/main/kotlin/geyser.publish-conventions.gradle.kts new file mode 100644 index 000000000..f1cb8b139 --- /dev/null +++ b/build-logic/src/main/kotlin/geyser.publish-conventions.gradle.kts @@ -0,0 +1,31 @@ +plugins { + id("geyser.shadow-conventions") + id("com.jfrog.artifactory") + id("maven-publish") +} + +publishing { + publications.create("mavenJava") { + groupId = project.group as String + artifactId = project.name + version = project.version as String + + artifact(tasks["shadowJar"]) + artifact(tasks["sourcesJar"]) + } +} + +artifactory { + publish { + repository { + setRepoKey(if (isSnapshot()) "maven-snapshots" else "maven-releases") + setMavenCompatible(true) + } + defaults { + publishConfigs("archives") + setPublishArtifacts(true) + setPublishPom(true) + setPublishIvy(false) + } + } +} \ No newline at end of file diff --git a/build-logic/src/main/kotlin/geyser.shadow-conventions.gradle.kts b/build-logic/src/main/kotlin/geyser.shadow-conventions.gradle.kts new file mode 100644 index 000000000..395beb104 --- /dev/null +++ b/build-logic/src/main/kotlin/geyser.shadow-conventions.gradle.kts @@ -0,0 +1,32 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar + +plugins { + id("geyser.base-conventions") + id("com.github.johnrengelman.shadow") +} + +tasks { + named("jar") { + archiveClassifier.set("unshaded") + from(project.rootProject.file("LICENSE")) + } + val shadowJar = named("shadowJar") { + archiveBaseName.set(project.name) + archiveVersion.set("") + archiveClassifier.set("") + + val sJar: ShadowJar = this + + doFirst { + providedDependencies[project.name]?.forEach { string -> + sJar.dependencies { + println("Excluding $string from ${project.name}") + exclude(dependency(string)) + } + } + } + } + named("build") { + dependsOn(shadowJar) + } +} \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 000000000..7371978d3 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,46 @@ +plugins { + `java-library` + id("geyser.build-logic") + id("io.freefair.lombok") version "6.3.0" apply false +} + +allprojects { + group = "org.geysermc" + version = "2.1.0-SNAPSHOT" + description = "Allows for players from Minecraft: Bedrock Edition to join Minecraft: Java Edition servers." + + tasks.withType { + options.encoding = "UTF-8" + } +} + +val platforms = setOf( + projects.bungeecord, + projects.spigot, + projects.sponge, + projects.standalone, + projects.velocity +).map { it.dependencyProject } + +val api: Project = projects.api.dependencyProject + +subprojects { + apply { + plugin("java-library") + plugin("io.freefair.lombok") + plugin("geyser.build-logic") + } + + val relativePath = projectDir.relativeTo(rootProject.projectDir).path + + if (relativePath.contains("api")) { + plugins.apply("geyser.api-conventions") + } else { + group = rootProject.group as String + ".geyser" + when (this) { + in platforms -> plugins.apply("geyser.platform-conventions") + api -> plugins.apply("geyser.publish-conventions") + else -> plugins.apply("geyser.base-conventions") + } + } +} \ No newline at end of file diff --git a/common/build.gradle.kts b/common/build.gradle.kts new file mode 100644 index 000000000..6c1414105 --- /dev/null +++ b/common/build.gradle.kts @@ -0,0 +1,4 @@ +dependencies { + api("org.geysermc.cumulus", "cumulus", Versions.cumulusVersion) + api("com.google.code.gson", "gson", Versions.gsonVersion) +} \ No newline at end of file diff --git a/common/pom.xml b/common/pom.xml deleted file mode 100644 index 67b77a98a..000000000 --- a/common/pom.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - 4.0.0 - - org.geysermc - geyser-parent - 2.0.7-SNAPSHOT - - common - - - - 8 - 8 - - - - - org.geysermc.cumulus - cumulus - 1.1.1 - - - com.google.code.gson - gson - 2.8.9 - - - \ No newline at end of file diff --git a/core/build.gradle.kts b/core/build.gradle.kts new file mode 100644 index 000000000..49ce2fbff --- /dev/null +++ b/core/build.gradle.kts @@ -0,0 +1,142 @@ +import net.kyori.blossom.BlossomExtension + +plugins { + id("net.kyori.blossom") + id("net.kyori.indra.git") + id("geyser.publish-conventions") +} + +dependencies { + api(projects.geyserApi) + api(projects.common) + + // Jackson JSON and YAML serialization + api("com.fasterxml.jackson.core", "jackson-annotations", Versions.jacksonVersion) + api("com.fasterxml.jackson.core", "jackson-databind", Versions.jacksonVersion + ".1") // Extra .1 as databind is a slightly different version + api("com.fasterxml.jackson.dataformat", "jackson-dataformat-yaml", Versions.jacksonVersion) + api("com.google.guava", "guava", Versions.guavaVersion) + + api("com.nukkitx", "nbt", Versions.nbtVersion) + + // Fastutil Maps + implementation("com.nukkitx.fastutil", "fastutil-int-int-maps", Versions.fastutilVersion) + implementation("com.nukkitx.fastutil", "fastutil-int-long-maps", Versions.fastutilVersion) + implementation("com.nukkitx.fastutil", "fastutil-int-byte-maps", Versions.fastutilVersion) + implementation("com.nukkitx.fastutil", "fastutil-int-boolean-maps", Versions.fastutilVersion) + implementation("com.nukkitx.fastutil", "fastutil-object-int-maps", Versions.fastutilVersion) + implementation("com.nukkitx.fastutil", "fastutil-object-object-maps", Versions.fastutilVersion) + + // Network libraries + implementation("org.java-websocket", "Java-WebSocket", Versions.websocketVersion) + + api("com.github.CloudburstMC.Protocol", "bedrock-v554", Versions.protocolVersion) { + exclude("com.nukkitx.network", "raknet") + exclude("com.nukkitx", "nbt") + } + + api("com.github.GeyserMC", "MCAuthLib", Versions.mcauthlibVersion) + api("com.github.GeyserMC", "MCProtocolLib", Versions.mcprotocollibversion) { + exclude("com.github.GeyserMC", "packetlib") + exclude("com.github.GeyserMC", "mcauthlib") + } + + api("com.github.steveice10", "packetlib", Versions.packetlibVersion) { + exclude("io.netty", "netty-all") + } + + implementation("com.nukkitx.network", "raknet", Versions.raknetVersion) { + exclude("io.netty", "*"); + } + + implementation("io.netty", "netty-resolver-dns", Versions.nettyVersion) + implementation("io.netty", "netty-resolver-dns-native-macos", Versions.nettyVersion, null, "osx-x86_64") + implementation("io.netty", "netty-codec-haproxy", Versions.nettyVersion) + + // Network dependencies we are updating ourselves + api("io.netty", "netty-handler", Versions.nettyVersion) + + implementation("io.netty", "netty-transport-native-epoll", Versions.nettyVersion, null, "linux-x86_64") + implementation("io.netty", "netty-transport-native-epoll", Versions.nettyVersion, null, "linux-aarch_64") + implementation("io.netty", "netty-transport-native-kqueue", Versions.nettyVersion, null, "osx-x86_64") + + // Adventure text serialization + implementation("net.kyori", "adventure-text-serializer-gson", Versions.adventureVersion) // Remove when we remove our Adventure bump + implementation("net.kyori", "adventure-text-serializer-legacy", Versions.adventureVersion) + implementation("net.kyori", "adventure-text-serializer-plain", Versions.adventureVersion) + + // Test + testImplementation("junit", "junit", Versions.junitVersion) + + // Annotation Processors + compileOnly(projects.ap) + + annotationProcessor(projects.ap) +} + +configurations.api { + // This is still experimental - additionally, it could only really benefit standalone + exclude(group = "io.netty.incubator", module = "netty-incubator-transport-native-io_uring") +} + +tasks.processResources { + // This is solely for backwards compatibility for other programs that used this file before the switch to gradle. + // It used to be generated by the maven Git-Commit-Id-Plugin + filesMatching("git.properties") { + val info = GitInfo() + expand( + "branch" to info.branch, + "buildNumber" to info.buildNumber, + "projectVersion" to project.version, + "commit" to info.commit, + "commitAbbrev" to info.commitAbbrev, + "commitMessage" to info.commitMessage, + "repository" to info.repository + ) + } +} + +configure { + val mainFile = "src/main/java/org/geysermc/geyser/GeyserImpl.java" + val info = GitInfo() + + replaceToken("\${version}", "${project.version} (${info.gitVersion})", mainFile) + replaceToken("\${gitVersion}", info.gitVersion, mainFile) + replaceToken("\${buildNumber}", info.buildNumber, mainFile) + replaceToken("\${branch}", info.branch, mainFile) + replaceToken("\${commit}", info.commit, mainFile) + replaceToken("\${repository}", info.repository, mainFile) +} + +fun Project.buildNumber(): Int = + System.getenv("BUILD_NUMBER")?.let { Integer.parseInt(it) } ?: -1 + +inner class GitInfo { + val branch: String + val commit: String + val commitAbbrev: String + + val gitVersion: String + val version: String + val buildNumber: Int + + val commitMessage: String + val repository: String + + init { + // On Jenkins, a detached head is checked out, so indra cannot determine the branch. + // Fortunately, this environment variable is available. + branch = indraGit.branchName() ?: System.getenv("BRANCH_NAME") ?: "DEV" + + val commit = indraGit.commit() + this.commit = commit?.name ?: "0".repeat(40) + commitAbbrev = commit?.name?.substring(0, 7) ?: "0".repeat(7) + + gitVersion = "git-${branch}-${commitAbbrev}" + version = "${project.version} ($gitVersion)" + buildNumber = buildNumber() + + val git = indraGit.git() + commitMessage = git?.commit()?.message ?: "" + repository = git?.repository?.config?.getString("remote", "origin", "url") ?: "" + } +} diff --git a/core/pom.xml b/core/pom.xml deleted file mode 100644 index ca6d4d370..000000000 --- a/core/pom.xml +++ /dev/null @@ -1,394 +0,0 @@ - - - 4.0.0 - - org.geysermc - geyser-parent - 2.0.7-SNAPSHOT - - core - - - 4.12.0-20220629.025215-9 - 8.5.2 - 2.13.2 - 4.1.80.Final - - - - - - sonatype-s01 - https://s01.oss.sonatype.org/content/repositories/snapshots/ - - - - - - org.geysermc - ap - 2.0.7-SNAPSHOT - provided - - - org.geysermc - geyser-api - 2.0.7-SNAPSHOT - compile - - - org.geysermc - common - 2.0.7-SNAPSHOT - compile - - - - com.fasterxml.jackson.core - jackson-annotations - ${jackson.version} - compile - - - com.fasterxml.jackson.core - jackson-core - ${jackson.version} - compile - - - com.fasterxml.jackson.core - jackson-databind - ${jackson.version}.1 - compile - - - com.fasterxml.jackson.dataformat - jackson-dataformat-yaml - ${jackson.version} - compile - - - com.google.guava - guava - 29.0-jre - compile - - - - com.nukkitx - nbt - - 2.2.1 - compile - - - com.nukkitx.fastutil - fastutil-int-int-maps - ${fastutil.version} - compile - - - com.nukkitx.fastutil - fastutil-int-long-maps - ${fastutil.version} - compile - - - com.nukkitx.fastutil - fastutil-int-byte-maps - ${fastutil.version} - compile - - - com.nukkitx.fastutil - fastutil-int-boolean-maps - ${fastutil.version} - compile - - - com.nukkitx.fastutil - fastutil-object-int-maps - ${fastutil.version} - compile - - - com.nukkitx.fastutil - fastutil-object-object-maps - ${fastutil.version} - compile - - - - org.java-websocket - Java-WebSocket - 1.5.1 - compile - - - com.github.CloudburstMC.Protocol - bedrock-v544 - 0bd459f - compile - - - com.nukkitx.network - raknet - - - com.nukkitx - nbt - - - - - com.nukkitx.network - raknet - 1.6.28-20220125.214016-6 - compile - - - io.netty - * - - - - - com.github.GeyserMC - MCAuthLib - d9d773e - compile - - - com.github.GeyserMC - MCProtocolLib - 9f78bd5 - compile - - - com.github.GeyserMC - packetlib - - - com.github.GeyserMC - mcauthlib - - - - net.kyori - * - - - - - com.github.steveice10 - packetlib - 3.0 - compile - - - io.netty - netty-all - - - - io.netty.incubator - netty-incubator-transport-native-io_uring - - - - - io.netty - netty-resolver-dns - ${netty.version} - compile - - - io.netty - netty-resolver-dns-native-macos - ${netty.version} - compile - osx-x86_64 - - - io.netty - netty-codec-haproxy - ${netty.version} - compile - - - - io.netty - netty-handler - ${netty.version} - compile - - - io.netty - netty-transport-native-epoll - ${netty.version} - compile - linux-x86_64 - - - io.netty - netty-transport-native-epoll - ${netty.version} - compile - linux-aarch_64 - - - io.netty - netty-transport-native-kqueue - ${netty.version} - compile - osx-x86_64 - - - - net.kyori - adventure-text-serializer-legacy - ${adventure.version} - compile - - - net.kyori - adventure-text-serializer-plain - ${adventure.version} - compile - - - - net.kyori - adventure-text-serializer-gson - ${adventure.version} - compile - - - net.kyori - adventure-text-serializer-gson-legacy-impl - ${adventure.version} - compile - - - - junit - junit - 4.13.1 - test - - - - - - - org.apache.maven.plugins - maven-jar-plugin - 3.2.0 - - - **/services/javax.annotation.processing.Processor - - - - - pl.project13.maven - git-commit-id-plugin - 4.0.0 - - - get-the-git-infos - - revision - - - - - true - ${project.build.outputDirectory}/git.properties - properties - false - false - false - true - false - - git.user.* - git.*.user.* - git.closest.* - git.commit.id.describe - git.commit.id.describe-short - git.commit.message.short - - flat - - true - - - - - com.google.code.maven-replacer-plugin - replacer - 1.5.3 - - - add-version - process-sources - - replace - - - - ${project.basedir}/src/main/java/org/geysermc/geyser/GeyserImpl.java - - - - String VERSION = ".*" - String VERSION = "${project.version} (" + GIT_VERSION + ")" - - - String GIT_VERSION = ".*" - - String GIT_VERSION = "git-${git.branch}-${git.commit.id.abbrev}" - - - - - - - remove-version - process-classes - - replace - - - - ${project.basedir}/src/main/java/org/geysermc/geyser/GeyserImpl.java - - - - String VERSION = ".*" - String VERSION = "DEV" - - - String GIT_VERSION = ".*" - String GIT_VERSION = "DEV" - - - - - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.22.0 - - - -Dfile.encoding=${project.build.sourceEncoding} - - - - - diff --git a/core/src/main/java/org/geysermc/connector/GeyserConnector.java b/core/src/main/java/org/geysermc/connector/GeyserConnector.java index b3307a134..bd14ebb25 100644 --- a/core/src/main/java/org/geysermc/connector/GeyserConnector.java +++ b/core/src/main/java/org/geysermc/connector/GeyserConnector.java @@ -26,10 +26,10 @@ package org.geysermc.connector; import com.nukkitx.protocol.bedrock.BedrockServer; +import org.geysermc.api.Geyser; import org.geysermc.common.PlatformType; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.api.Geyser; import java.util.UUID; @@ -91,6 +91,6 @@ public class GeyserConnector { } public boolean isProductionEnvironment() { - return GeyserImpl.getInstance().productionEnvironment(); + return GeyserImpl.getInstance().isProductionEnvironment(); } } diff --git a/core/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/core/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index 890290a01..258787e78 100644 --- a/core/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -56,11 +56,11 @@ public class GeyserSession { } public String getRemoteAddress() { - return this.handle.getRemoteAddress(); + return this.handle.remoteServer().address(); } public int getRemotePort() { - return this.handle.getRemotePort(); + return this.handle.remoteServer().port(); } public int getRenderDistance() { @@ -128,7 +128,7 @@ public class GeyserSession { } public String getName() { - return this.handle.name(); + return this.handle.bedrockUsername(); } public boolean isConsole() { @@ -136,7 +136,7 @@ public class GeyserSession { } public String getLocale() { - return this.handle.getLocale(); + return this.handle.locale(); } public void sendUpstreamPacket(BedrockPacket packet) { diff --git a/core/src/main/java/org/geysermc/connector/network/session/auth/AuthData.java b/core/src/main/java/org/geysermc/connector/network/session/auth/AuthData.java index 6b8e53d8e..cca7aa48c 100644 --- a/core/src/main/java/org/geysermc/connector/network/session/auth/AuthData.java +++ b/core/src/main/java/org/geysermc/connector/network/session/auth/AuthData.java @@ -33,6 +33,7 @@ import java.util.UUID; * * @deprecated legacy code */ +@Deprecated public class AuthData { private final org.geysermc.geyser.session.auth.AuthData handle; diff --git a/core/src/main/java/org/geysermc/geyser/FloodgateKeyLoader.java b/core/src/main/java/org/geysermc/geyser/FloodgateKeyLoader.java index 0aa1d39c3..8b51228c8 100644 --- a/core/src/main/java/org/geysermc/geyser/FloodgateKeyLoader.java +++ b/core/src/main/java/org/geysermc/geyser/FloodgateKeyLoader.java @@ -25,8 +25,8 @@ package org.geysermc.geyser; +import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.configuration.GeyserJacksonConfiguration; -import org.geysermc.geyser.session.auth.AuthType; import org.geysermc.geyser.text.GeyserLocale; import java.nio.file.Files; @@ -34,7 +34,7 @@ import java.nio.file.Path; public class FloodgateKeyLoader { public static Path getKeyPath(GeyserJacksonConfiguration config, Path floodgateDataFolder, Path geyserDataFolder, GeyserLogger logger) { - if (config.getRemote().getAuthType() != AuthType.FLOODGATE) { + if (config.getRemote().authType() != AuthType.FLOODGATE) { return geyserDataFolder.resolve(config.getFloodgateKeyFile()); } diff --git a/core/src/main/java/org/geysermc/geyser/GeyserBootstrap.java b/core/src/main/java/org/geysermc/geyser/GeyserBootstrap.java index d40060310..261c7416b 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserBootstrap.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserBootstrap.java @@ -25,7 +25,7 @@ package org.geysermc.geyser; -import org.geysermc.geyser.command.CommandManager; +import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.level.GeyserWorldManager; @@ -72,7 +72,7 @@ public interface GeyserBootstrap { * * @return The current CommandManager */ - CommandManager getGeyserCommandManager(); + GeyserCommandManager getGeyserCommandManager(); /** * Returns the current PingPassthrough manager diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index d9f4d8a15..a10e54f90 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -43,19 +43,32 @@ import lombok.Getter; import lombok.Setter; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.api.Geyser; import org.geysermc.common.PlatformType; +import org.geysermc.cumulus.form.Form; +import org.geysermc.cumulus.form.util.FormBuilder; import org.geysermc.floodgate.crypto.AesCipher; import org.geysermc.floodgate.crypto.AesKeyProducer; import org.geysermc.floodgate.crypto.Base64Topping; import org.geysermc.floodgate.crypto.FloodgateCipher; import org.geysermc.floodgate.news.NewsItemAction; import org.geysermc.geyser.api.GeyserApi; -import org.geysermc.geyser.command.CommandManager; +import org.geysermc.geyser.api.event.EventBus; +import org.geysermc.geyser.api.event.EventRegistrar; +import org.geysermc.geyser.api.event.lifecycle.GeyserPostInitializeEvent; +import org.geysermc.geyser.api.event.lifecycle.GeyserPreInitializeEvent; +import org.geysermc.geyser.api.event.lifecycle.GeyserShutdownEvent; +import org.geysermc.geyser.api.network.AuthType; +import org.geysermc.geyser.api.network.BedrockListener; +import org.geysermc.geyser.api.network.RemoteServer; +import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.entity.EntityDefinitions; +import org.geysermc.geyser.event.GeyserEventBus; +import org.geysermc.geyser.extension.GeyserExtensionManager; import org.geysermc.geyser.level.WorldManager; import org.geysermc.geyser.network.ConnectorServerEventHandler; import org.geysermc.geyser.pack.ResourcePack; @@ -65,7 +78,6 @@ import org.geysermc.geyser.scoreboard.ScoreboardUpdater; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.PendingMicrosoftAuthentication; import org.geysermc.geyser.session.SessionManager; -import org.geysermc.geyser.session.auth.AuthType; import org.geysermc.geyser.skin.FloodgateSkinUploader; import org.geysermc.geyser.skin.SkinProvider; import org.geysermc.geyser.text.GeyserLocale; @@ -77,7 +89,6 @@ import org.geysermc.geyser.util.*; import java.io.File; import java.io.FileWriter; import java.io.IOException; -import java.io.InputStream; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; @@ -100,8 +111,13 @@ public class GeyserImpl implements GeyserApi { .enable(JsonParser.Feature.ALLOW_SINGLE_QUOTES); public static final String NAME = "Geyser"; - public static final String GIT_VERSION = "DEV"; // A fallback for running in IDEs - public static final String VERSION = "DEV"; // A fallback for running in IDEs + public static final String GIT_VERSION = "${gitVersion}"; // A fallback for running in IDEs + public static final String VERSION = "${version}"; // A fallback for running in IDEs + + public static final String BUILD_NUMBER = "${buildNumber}"; + public static final String BRANCH = "${branch}"; + public static final String COMMIT = "${commit}"; + public static final String REPOSITORY = "${repository}"; /** * Oauth client ID for Microsoft authentication @@ -130,6 +146,9 @@ public class GeyserImpl implements GeyserApi { private final PlatformType platformType; private final GeyserBootstrap bootstrap; + private final EventBus eventBus; + private final GeyserExtensionManager extensionManager; + private Metrics metrics; private PendingMicrosoftAuthentication pendingMicrosoftAuthentication; @@ -146,9 +165,20 @@ public class GeyserImpl implements GeyserApi { this.platformType = platformType; this.bootstrap = bootstrap; + GeyserLocale.finalizeDefaultLocale(this); + + /* Initialize event bus */ + this.eventBus = new GeyserEventBus(); + + /* Load Extensions */ + this.extensionManager = new GeyserExtensionManager(); + this.extensionManager.init(); + this.eventBus.fire(new GeyserPreInitializeEvent(this.extensionManager, this.eventBus)); + } + + public void initialize() { long startupTime = System.currentTimeMillis(); - GeyserLocale.finalizeDefaultLocale(this); GeyserLogger logger = bootstrap.getGeyserLogger(); logger.info("******************************************"); @@ -157,16 +187,17 @@ public class GeyserImpl implements GeyserApi { logger.info(""); logger.info("******************************************"); - /* Initialize translators and registries */ - BlockRegistries.init(); + /* Initialize registries */ Registries.init(); + BlockRegistries.init(); + /* Initialize translators */ EntityDefinitions.init(); ItemTranslator.init(); MessageTranslator.init(); MinecraftLocale.init(); - start(); + startInstance(); GeyserConfiguration config = bootstrap.getGeyserConfig(); @@ -193,12 +224,12 @@ public class GeyserImpl implements GeyserApi { if (platformType == PlatformType.STANDALONE) { logger.warning(GeyserLocale.getLocaleStringLog("geyser.core.movement_warn")); - } else if (config.getRemote().getAuthType() == AuthType.FLOODGATE) { + } else if (config.getRemote().authType() == AuthType.FLOODGATE) { VersionCheckUtils.checkForOutdatedFloodgate(logger); } } - private void start() { + private void startInstance() { this.scheduledThread = Executors.newSingleThreadScheduledExecutor(new DefaultThreadFactory("Geyser Scheduled Thread")); GeyserLogger logger = bootstrap.getGeyserLogger(); @@ -210,7 +241,7 @@ public class GeyserImpl implements GeyserApi { ResourcePack.loadPacks(); - if (platformType != PlatformType.STANDALONE && config.getRemote().getAddress().equals("auto")) { + if (platformType != PlatformType.STANDALONE && config.getRemote().address().equals("auto")) { // Set the remote address to localhost since that is where we are always connecting try { config.getRemote().setAddress(InetAddress.getLocalHost().getHostAddress()); @@ -222,7 +253,7 @@ public class GeyserImpl implements GeyserApi { config.getRemote().setAddress(InetAddress.getLoopbackAddress().getHostAddress()); } } - String remoteAddress = config.getRemote().getAddress(); + String remoteAddress = config.getRemote().address(); // Filters whether it is not an IP address or localhost, because otherwise it is not possible to find out an SRV entry. if (!remoteAddress.matches(IP_REGEX) && !remoteAddress.equalsIgnoreCase("localhost")) { String[] record = WebUtils.findSrvRecord(this, remoteAddress); @@ -237,27 +268,9 @@ public class GeyserImpl implements GeyserApi { // Ensure that PacketLib does not create an event loop for handling packets; we'll do that ourselves TcpSession.USE_EVENT_LOOP_FOR_PACKETS = false; - String branch = "unknown"; - int buildNumber = -1; - if (this.productionEnvironment()) { - try (InputStream stream = bootstrap.getResource("git.properties")) { - Properties gitProperties = new Properties(); - gitProperties.load(stream); - branch = gitProperties.getProperty("git.branch"); - String build = gitProperties.getProperty("git.build.number"); - if (build != null) { - buildNumber = Integer.parseInt(build); - } - } catch (Throwable e) { - logger.error("Failed to read git.properties", e); - } - } else { - logger.debug("Not getting git properties for the news handler as we are in a development environment."); - } - pendingMicrosoftAuthentication = new PendingMicrosoftAuthentication(config.getPendingAuthenticationTimeout()); - this.newsHandler = new NewsHandler(branch, buildNumber); + this.newsHandler = new NewsHandler(BRANCH, this.buildNumber()); CooldownUtils.setDefaultShowCooldown(config.getShowCooldown()); DimensionUtils.changeBedrockNetherId(config.isAboveBedrockNetherBuilding()); // Apply End dimension ID workaround to Nether @@ -274,7 +287,7 @@ public class GeyserImpl implements GeyserApi { boolean enableProxyProtocol = config.getBedrock().isEnableProxyProtocol(); bedrockServer = new BedrockServer( - new InetSocketAddress(config.getBedrock().getAddress(), config.getBedrock().getPort()), + new InetSocketAddress(config.getBedrock().address(), config.getBedrock().port()), bedrockThreadCount, EventLoops.commonGroup(), enableProxyProtocol @@ -297,11 +310,11 @@ public class GeyserImpl implements GeyserApi { if (shouldStartListener) { bedrockServer.bind().whenComplete((avoid, throwable) -> { if (throwable == null) { - logger.info(GeyserLocale.getLocaleStringLog("geyser.core.start", config.getBedrock().getAddress(), - String.valueOf(config.getBedrock().getPort()))); + logger.info(GeyserLocale.getLocaleStringLog("geyser.core.start", config.getBedrock().address(), + String.valueOf(config.getBedrock().port()))); } else { - String address = config.getBedrock().getAddress(); - int port = config.getBedrock().getPort(); + String address = config.getBedrock().address(); + int port = config.getBedrock().port(); logger.severe(GeyserLocale.getLocaleStringLog("geyser.core.fail", address, String.valueOf(port))); if (!"0.0.0.0".equals(address)) { logger.info(Component.text("Suggestion: try setting `address` under `bedrock` in the Geyser config back to 0.0.0.0", NamedTextColor.GREEN)); @@ -311,7 +324,7 @@ public class GeyserImpl implements GeyserApi { }).join(); } - if (config.getRemote().getAuthType() == AuthType.FLOODGATE) { + if (config.getRemote().authType() == AuthType.FLOODGATE) { try { Key key = new AesKeyProducer().produceFrom(config.getFloodgateKeyPath()); cipher = new AesCipher(new Base64Topping()); @@ -329,7 +342,7 @@ public class GeyserImpl implements GeyserApi { metrics = new Metrics(this, "GeyserMC", config.getMetrics().getUniqueId(), false, java.util.logging.Logger.getLogger("")); metrics.addCustomChart(new Metrics.SingleLineChart("players", sessionManager::size)); // Prevent unwanted words best we can - metrics.addCustomChart(new Metrics.SimplePie("authMode", () -> config.getRemote().getAuthType().toString().toLowerCase(Locale.ROOT))); + metrics.addCustomChart(new Metrics.SimplePie("authMode", () -> config.getRemote().authType().toString().toLowerCase(Locale.ROOT))); metrics.addCustomChart(new Metrics.SimplePie("platform", platformType::getPlatformName)); metrics.addCustomChart(new Metrics.SimplePie("defaultLocale", GeyserLocale::getDefaultLocale)); metrics.addCustomChart(new Metrics.SimplePie("version", () -> GeyserImpl.VERSION)); @@ -413,7 +426,7 @@ public class GeyserImpl implements GeyserApi { metrics = null; } - if (config.getRemote().getAuthType() == AuthType.ONLINE) { + if (config.getRemote().authType() == AuthType.ONLINE) { if (config.getUserAuths() != null && !config.getUserAuths().isEmpty()) { getLogger().warning("The 'userAuths' config section is now deprecated, and will be removed in the near future! " + "Please migrate to the new 'saved-user-logins' config option: " + @@ -455,25 +468,26 @@ public class GeyserImpl implements GeyserApi { } newsHandler.handleNews(null, NewsItemAction.ON_SERVER_STARTED); + + this.eventBus.fire(new GeyserPostInitializeEvent(this.extensionManager, this.eventBus)); if (config.isNotifyOnNewBedrockUpdate()) { VersionCheckUtils.checkForGeyserUpdate(this::getLogger); } } @Override - public @Nullable GeyserSession connectionByName(@NonNull String name) { - for (GeyserSession session : sessionManager.getAllSessions()) { - if (session.name().equals(name) || session.getProtocol().getProfile().getName().equals(name)) { - return session; - } - } - - return null; + public @NonNull List onlineConnections() { + return sessionManager.getAllSessions(); } @Override - public @NonNull List onlineConnections() { - return this.sessionManager.getAllSessions(); + public int onlineConnectionsCount() { + return sessionManager.size(); + } + + @Override + public @MonotonicNonNull String usernamePrefix() { + return null; } @Override @@ -483,16 +497,40 @@ public class GeyserImpl implements GeyserApi { @Override public @Nullable GeyserSession connectionByXuid(@NonNull String xuid) { - for (GeyserSession session : sessionManager.getAllSessions()) { - if (session.xuid().equals(xuid)) { - return session; - } - } - - return null; + return sessionManager.sessionByXuid(xuid); } @Override + public boolean isBedrockPlayer(@NonNull UUID uuid) { + return connectionByUuid(uuid) != null; + } + + @Override + public boolean sendForm(@NonNull UUID uuid, @NonNull Form form) { + Objects.requireNonNull(uuid); + Objects.requireNonNull(form); + GeyserSession session = connectionByUuid(uuid); + if (session == null) { + return false; + } + return session.sendForm(form); + } + + @Override + public boolean sendForm(@NonNull UUID uuid, @NonNull FormBuilder formBuilder) { + return sendForm(uuid, formBuilder.build()); + } + + @Override + public boolean transfer(@NonNull UUID uuid, @NonNull String address, int port) { + Objects.requireNonNull(uuid); + GeyserSession session = connectionByUuid(uuid); + if (session == null) { + return false; + } + return session.transfer(address, port); + } + public void shutdown() { bootstrap.getGeyserLogger().info(GeyserLocale.getLocaleStringLog("geyser.core.shutdown")); shuttingDown = true; @@ -509,16 +547,19 @@ public class GeyserImpl implements GeyserApi { skinUploader.close(); } newsHandler.shutdown(); - this.getCommandManager().getCommands().clear(); + this.commandManager().getCommands().clear(); ResourcePack.PACKS.clear(); + this.eventBus.fire(new GeyserShutdownEvent(this.extensionManager, this.eventBus)); + this.extensionManager.disableExtensions(); + bootstrap.getGeyserLogger().info(GeyserLocale.getLocaleStringLog("geyser.core.shutdown.done")); } - @Override public void reload() { shutdown(); + this.extensionManager.enableExtensions(); bootstrap.onEnable(); } @@ -528,24 +569,73 @@ public class GeyserImpl implements GeyserApi { * * @return true if the version number is not 'DEV'. */ - @Override - public boolean productionEnvironment() { - //noinspection ConstantConditions - changes in production - return !"DEV".equals(GeyserImpl.VERSION); + public boolean isProductionEnvironment() { + // First is if Blossom runs, second is if Blossom doesn't run + // noinspection ConstantConditions - changes in production + return !("git-local/dev-0000000".equals(GeyserImpl.GIT_VERSION) || "${gitVersion}".equals(GeyserImpl.GIT_VERSION)); } - public static GeyserImpl start(PlatformType platformType, GeyserBootstrap bootstrap) { + @Override + @NonNull + public GeyserExtensionManager extensionManager() { + return this.extensionManager; + } + + @NonNull + public GeyserCommandManager commandManager() { + return this.bootstrap.getGeyserCommandManager(); + } + + @Override + public @NonNull R provider(@NonNull Class apiClass, @Nullable Object... args) { + return (R) Registries.PROVIDERS.get(apiClass).create(args); + } + + @Override + @NonNull + public EventBus eventBus() { + return this.eventBus; + } + + @NonNull + public RemoteServer defaultRemoteServer() { + return getConfig().getRemote(); + } + + @Override + @NonNull + public BedrockListener bedrockListener() { + return getConfig().getBedrock(); + } + + public int buildNumber() { + if (!this.isProductionEnvironment()) { + return 0; + } + + return Integer.parseInt(BUILD_NUMBER); + } + + public static GeyserImpl load(PlatformType platformType, GeyserBootstrap bootstrap) { if (instance == null) { return new GeyserImpl(platformType, bootstrap); } + return instance; + } + + public static void start() { + if (instance == null) { + throw new RuntimeException("Geyser has not been loaded yet!"); + } + // We've been reloaded if (instance.isShuttingDown()) { instance.shuttingDown = false; - instance.start(); + instance.startInstance(); + } else { + instance.initialize(); } - - return instance; } public GeyserLogger getLogger() { @@ -556,10 +646,6 @@ public class GeyserImpl implements GeyserApi { return bootstrap.getGeyserConfig(); } - public CommandManager getCommandManager() { - return bootstrap.getGeyserCommandManager(); - } - public WorldManager getWorldManager() { return bootstrap.getWorldManager(); } diff --git a/core/src/main/java/org/geysermc/geyser/GeyserLogger.java b/core/src/main/java/org/geysermc/geyser/GeyserLogger.java index 197a031dd..88220eec9 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserLogger.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserLogger.java @@ -26,11 +26,11 @@ package org.geysermc.geyser; import net.kyori.adventure.text.Component; -import org.geysermc.geyser.command.CommandSender; +import org.geysermc.geyser.command.GeyserCommandSource; import javax.annotation.Nullable; -public interface GeyserLogger extends CommandSender { +public interface GeyserLogger extends GeyserCommandSource { /** * Logs a severe message to console diff --git a/core/src/main/java/org/geysermc/geyser/command/CommandManager.java b/core/src/main/java/org/geysermc/geyser/command/CommandManager.java deleted file mode 100644 index 38a86fdd0..000000000 --- a/core/src/main/java/org/geysermc/geyser/command/CommandManager.java +++ /dev/null @@ -1,123 +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/Geyser - */ - -package org.geysermc.geyser.command; - -import lombok.Getter; - -import org.geysermc.common.PlatformType; -import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.defaults.*; -import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.text.GeyserLocale; - -import java.util.*; - -public abstract class CommandManager { - - @Getter - private final Map commands = new HashMap<>(); - - private final GeyserImpl geyser; - - public CommandManager(GeyserImpl geyser) { - this.geyser = geyser; - - registerCommand(new HelpCommand(geyser, "help", "geyser.commands.help.desc", "geyser.command.help")); - registerCommand(new ListCommand(geyser, "list", "geyser.commands.list.desc", "geyser.command.list")); - registerCommand(new ReloadCommand(geyser, "reload", "geyser.commands.reload.desc", "geyser.command.reload")); - registerCommand(new OffhandCommand(geyser, "offhand", "geyser.commands.offhand.desc", "geyser.command.offhand")); - registerCommand(new DumpCommand(geyser, "dump", "geyser.commands.dump.desc", "geyser.command.dump")); - registerCommand(new VersionCommand(geyser, "version", "geyser.commands.version.desc", "geyser.command.version")); - registerCommand(new SettingsCommand(geyser, "settings", "geyser.commands.settings.desc", "geyser.command.settings")); - registerCommand(new StatisticsCommand(geyser, "statistics", "geyser.commands.statistics.desc", "geyser.command.statistics")); - registerCommand(new AdvancementsCommand("advancements", "geyser.commands.advancements.desc", "geyser.command.advancements")); - registerCommand(new AdvancedTooltipsCommand("tooltips", "geyser.commands.advancedtooltips.desc", "geyser.command.tooltips")); - registerCommand(new ConnectionTestCommand(geyser, "connectiontest", "geyser.commands.connectiontest.desc", "geyser.command.connectiontest")); - if (GeyserImpl.getInstance().getPlatformType() == PlatformType.STANDALONE) { - registerCommand(new StopCommand(geyser, "stop", "geyser.commands.stop.desc", "geyser.command.stop")); - } - } - - public void registerCommand(GeyserCommand command) { - commands.put(command.getName(), command); - geyser.getLogger().debug(GeyserLocale.getLocaleStringLog("geyser.commands.registered", command.getName())); - - if (command.getAliases().isEmpty()) - return; - - for (String alias : command.getAliases()) - commands.put(alias, command); - } - - public void runCommand(CommandSender sender, String command) { - if (!command.startsWith("geyser ")) - return; - - command = command.trim().replace("geyser ", ""); - String label; - String[] args; - - if (!command.contains(" ")) { - label = command.toLowerCase(); - args = new String[0]; - } else { - label = command.substring(0, command.indexOf(" ")).toLowerCase(); - String argLine = command.substring(command.indexOf(" ") + 1); - args = argLine.contains(" ") ? argLine.split(" ") : new String[] { argLine }; - } - - GeyserCommand cmd = commands.get(label); - if (cmd == null) { - geyser.getLogger().error(GeyserLocale.getLocaleStringLog("geyser.commands.invalid")); - return; - } - - if (sender instanceof GeyserSession) { - cmd.execute((GeyserSession) sender, sender, args); - } else { - if (!cmd.isBedrockOnly()) { - cmd.execute(null, sender, args); - } else { - geyser.getLogger().error(GeyserLocale.getLocaleStringLog("geyser.bootstrap.command.bedrock_only")); - } - } - } - - /** - * @return a list of all subcommands under {@code /geyser}. - */ - public List getCommandNames() { - return Arrays.asList(geyser.getCommandManager().getCommands().keySet().toArray(new String[0])); - } - - /** - * Returns the description of the given command - * - * @param command Command to get the description for - * @return Command description - */ - public abstract String getDescription(String command); -} diff --git a/core/src/main/java/org/geysermc/geyser/command/GeyserCommand.java b/core/src/main/java/org/geysermc/geyser/command/GeyserCommand.java index a22c69c04..5808dbc2c 100644 --- a/core/src/main/java/org/geysermc/geyser/command/GeyserCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/GeyserCommand.java @@ -27,17 +27,19 @@ package org.geysermc.geyser.command; import lombok.Getter; import lombok.RequiredArgsConstructor; -import lombok.Setter; +import lombok.experimental.Accessors; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.api.command.Command; import org.geysermc.geyser.session.GeyserSession; import javax.annotation.Nullable; -import java.util.ArrayList; import java.util.Collections; import java.util.List; +@Accessors(fluent = true) @Getter @RequiredArgsConstructor -public abstract class GeyserCommand { +public abstract class GeyserCommand implements Command { protected final String name; /** @@ -46,16 +48,16 @@ public abstract class GeyserCommand { protected final String description; protected final String permission; - @Setter - private List aliases = new ArrayList<>(); + private List aliases = Collections.emptyList(); - public abstract void execute(@Nullable GeyserSession session, CommandSender sender, String[] args); + public abstract void execute(@Nullable GeyserSession session, GeyserCommandSource sender, String[] args); /** * If false, hides the command from being shown on the Geyser Standalone GUI. * * @return true if the command can be run on the server console */ + @Override public boolean isExecutableOnConsole() { return true; } @@ -65,26 +67,23 @@ public abstract class GeyserCommand { * * @return a list of all possible subcommands, or empty if none. */ - public List getSubCommands() { + @NonNull + @Override + public List subCommands() { return Collections.emptyList(); } /** - * Shortcut to {@link #getSubCommands()}{@code .isEmpty()}. + * Shortcut to {@link #subCommands()} ()}{@code .isEmpty()}. * * @return true if there are subcommand present for this command. */ public boolean hasSubCommands() { - return !getSubCommands().isEmpty(); + return !this.subCommands().isEmpty(); } - /** - * Used to send a deny message to Java players if this command can only be used by Bedrock players. - * - * @return true if this command can only be used by Bedrock players. - */ - public boolean isBedrockOnly() { - return false; + public void setAliases(List aliases) { + this.aliases = aliases; } /** @@ -92,6 +91,7 @@ public abstract class GeyserCommand { * * @return if this command is designated to be used only by server operators. */ + @Override public boolean isSuggestedOpOnly() { return false; } diff --git a/core/src/main/java/org/geysermc/geyser/command/CommandExecutor.java b/core/src/main/java/org/geysermc/geyser/command/GeyserCommandExecutor.java similarity index 85% rename from core/src/main/java/org/geysermc/geyser/command/CommandExecutor.java rename to core/src/main/java/org/geysermc/geyser/command/GeyserCommandExecutor.java index 5fa5f688b..a9b1c734f 100644 --- a/core/src/main/java/org/geysermc/geyser/command/CommandExecutor.java +++ b/core/src/main/java/org/geysermc/geyser/command/GeyserCommandExecutor.java @@ -27,6 +27,7 @@ package org.geysermc.geyser.command; import lombok.AllArgsConstructor; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.command.Command; import org.geysermc.geyser.session.GeyserSession; import javax.annotation.Nullable; @@ -36,19 +37,20 @@ import java.util.List; import java.util.Map; /** - * Represents helper functions for listening to {@code /geyser} commands. + * Represents helper functions for listening to {@code /geyser} or {@code /geyserext} commands. */ @AllArgsConstructor -public class CommandExecutor { +public class GeyserCommandExecutor { protected final GeyserImpl geyser; + private final Map commands; public GeyserCommand getCommand(String label) { - return geyser.getCommandManager().getCommands().get(label); + return (GeyserCommand) commands.get(label); } @Nullable - public GeyserSession getGeyserSession(CommandSender sender) { + public GeyserSession getGeyserSession(GeyserCommandSource sender) { if (sender.isConsole()) { return null; } @@ -70,20 +72,18 @@ public class CommandExecutor { * If the command sender does not have the permission for a given command, the command will not be shown. * @return A list of command names to include in the tab complete */ - public List tabComplete(CommandSender sender) { + public List tabComplete(GeyserCommandSource sender) { if (getGeyserSession(sender) != null) { // Bedrock doesn't get tab completions or argument suggestions return Collections.emptyList(); } List availableCommands = new ArrayList<>(); - Map commands = geyser.getCommandManager().getCommands(); // Only show commands they have permission to use - for (Map.Entry entry : commands.entrySet()) { - GeyserCommand geyserCommand = entry.getValue(); - if (sender.hasPermission(geyserCommand.getPermission())) { - + for (Map.Entry entry : commands.entrySet()) { + Command geyserCommand = entry.getValue(); + if (sender.hasPermission(geyserCommand.permission())) { if (geyserCommand.isBedrockOnly()) { // Don't show commands the JE player can't run continue; diff --git a/core/src/main/java/org/geysermc/geyser/command/GeyserCommandManager.java b/core/src/main/java/org/geysermc/geyser/command/GeyserCommandManager.java new file mode 100644 index 000000000..7c5bd6580 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/command/GeyserCommandManager.java @@ -0,0 +1,328 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.command; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.common.PlatformType; +import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.command.Command; +import org.geysermc.geyser.api.command.CommandExecutor; +import org.geysermc.geyser.api.command.CommandSource; +import org.geysermc.geyser.api.event.lifecycle.GeyserDefineCommandsEvent; +import org.geysermc.geyser.api.extension.Extension; +import org.geysermc.geyser.command.defaults.AdvancedTooltipsCommand; +import org.geysermc.geyser.command.defaults.AdvancementsCommand; +import org.geysermc.geyser.command.defaults.ConnectionTestCommand; +import org.geysermc.geyser.command.defaults.DumpCommand; +import org.geysermc.geyser.command.defaults.ExtensionsCommand; +import org.geysermc.geyser.command.defaults.HelpCommand; +import org.geysermc.geyser.command.defaults.ListCommand; +import org.geysermc.geyser.command.defaults.OffhandCommand; +import org.geysermc.geyser.command.defaults.ReloadCommand; +import org.geysermc.geyser.command.defaults.SettingsCommand; +import org.geysermc.geyser.command.defaults.StatisticsCommand; +import org.geysermc.geyser.command.defaults.StopCommand; +import org.geysermc.geyser.command.defaults.VersionCommand; +import org.geysermc.geyser.event.type.GeyserDefineCommandsEventImpl; +import org.geysermc.geyser.extension.command.GeyserExtensionCommand; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.text.GeyserLocale; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +@RequiredArgsConstructor +public abstract class GeyserCommandManager { + + @Getter + private final Map commands = new Object2ObjectOpenHashMap<>(12); + private final Map> extensionCommands = new Object2ObjectOpenHashMap<>(0); + + private final GeyserImpl geyser; + + public void init() { + registerBuiltInCommand(new HelpCommand(geyser, "help", "geyser.commands.help.desc", "geyser.command.help", "geyser", this.commands)); + registerBuiltInCommand(new ListCommand(geyser, "list", "geyser.commands.list.desc", "geyser.command.list")); + registerBuiltInCommand(new ReloadCommand(geyser, "reload", "geyser.commands.reload.desc", "geyser.command.reload")); + registerBuiltInCommand(new OffhandCommand(geyser, "offhand", "geyser.commands.offhand.desc", "geyser.command.offhand")); + registerBuiltInCommand(new DumpCommand(geyser, "dump", "geyser.commands.dump.desc", "geyser.command.dump")); + registerBuiltInCommand(new VersionCommand(geyser, "version", "geyser.commands.version.desc", "geyser.command.version")); + registerBuiltInCommand(new SettingsCommand(geyser, "settings", "geyser.commands.settings.desc", "geyser.command.settings")); + registerBuiltInCommand(new StatisticsCommand(geyser, "statistics", "geyser.commands.statistics.desc", "geyser.command.statistics")); + registerBuiltInCommand(new AdvancementsCommand("advancements", "geyser.commands.advancements.desc", "geyser.command.advancements")); + registerBuiltInCommand(new AdvancedTooltipsCommand("tooltips", "geyser.commands.advancedtooltips.desc", "geyser.command.tooltips")); + registerBuiltInCommand(new ConnectionTestCommand(geyser, "connectiontest", "geyser.commands.connectiontest.desc", "geyser.command.connectiontest")); + if (this.geyser.getPlatformType() == PlatformType.STANDALONE) { + registerBuiltInCommand(new StopCommand(geyser, "stop", "geyser.commands.stop.desc", "geyser.command.stop")); + } + + if (this.geyser.extensionManager().extensions().size() > 0) { + registerBuiltInCommand(new ExtensionsCommand(this.geyser, "extensions", "geyser.commands.extensions.desc", "geyser.command.extensions")); + } + + GeyserDefineCommandsEvent defineCommandsEvent = new GeyserDefineCommandsEventImpl(this.commands) { + + @Override + public void register(@NonNull Command command) { + if (!(command instanceof GeyserExtensionCommand extensionCommand)) { + throw new IllegalArgumentException("Expected GeyserExtensionCommand as part of command registration but got " + command + "! Did you use the Command builder properly?"); + } + + registerExtensionCommand(extensionCommand.extension(), extensionCommand); + } + }; + + this.geyser.eventBus().fire(defineCommandsEvent); + + // Register help commands for all extensions with commands + for (Map.Entry> entry : this.extensionCommands.entrySet()) { + registerExtensionCommand(entry.getKey(), new HelpCommand(this.geyser, "help", "geyser.commands.exthelp.desc", "geyser.command.exthelp", entry.getKey().description().id(), entry.getValue())); + } + } + + /** + * For internal Geyser commands + */ + public void registerBuiltInCommand(GeyserCommand command) { + register(command, this.commands); + } + + public void registerExtensionCommand(@NonNull Extension extension, @NonNull Command command) { + register(command, this.extensionCommands.computeIfAbsent(extension, e -> new HashMap<>())); + } + + private void register(Command command, Map commands) { + commands.put(command.name(), command); + geyser.getLogger().debug(GeyserLocale.getLocaleStringLog("geyser.commands.registered", command.name())); + + if (command.aliases().isEmpty()) { + return; + } + + for (String alias : command.aliases()) { + commands.put(alias, command); + } + } + + @NotNull + public Map commands() { + return Collections.unmodifiableMap(this.commands); + } + + @NotNull + public Map> extensionCommands() { + return Collections.unmodifiableMap(this.extensionCommands); + } + + public boolean runCommand(GeyserCommandSource sender, String command) { + Extension extension = null; + for (Extension loopedExtension : this.extensionCommands.keySet()) { + if (command.startsWith(loopedExtension.description().id() + " ")) { + extension = loopedExtension; + break; + } + } + + if (!command.startsWith("geyser ") && extension == null) { + return false; + } + + command = command.trim().replace(extension != null ? extension.description().id() + " " : "geyser ", ""); + String label; + String[] args; + + if (!command.contains(" ")) { + label = command.toLowerCase(Locale.ROOT); + args = new String[0]; + } else { + label = command.substring(0, command.indexOf(" ")).toLowerCase(Locale.ROOT); + String argLine = command.substring(command.indexOf(" ") + 1); + args = argLine.contains(" ") ? argLine.split(" ") : new String[] { argLine }; + } + + Command cmd = (extension != null ? this.extensionCommands.getOrDefault(extension, Collections.emptyMap()) : this.commands).get(label); + if (cmd == null) { + sender.sendMessage(GeyserLocale.getLocaleStringLog("geyser.commands.invalid")); + return false; + } + + if (cmd instanceof GeyserCommand) { + if (sender instanceof GeyserSession) { + ((GeyserCommand) cmd).execute((GeyserSession) sender, sender, args); + } else { + if (!cmd.isBedrockOnly()) { + ((GeyserCommand) cmd).execute(null, sender, args); + } else { + geyser.getLogger().error(GeyserLocale.getLocaleStringLog("geyser.bootstrap.command.bedrock_only")); + } + } + } + + return true; + } + + /** + * Returns the description of the given command + * + * @param command Command to get the description for + * @return Command description + */ + public abstract String description(String command); + + @RequiredArgsConstructor + public static class CommandBuilder implements Command.Builder { + private final Extension extension; + private Class sourceType; + private String name; + private String description = ""; + private String permission = ""; + private List aliases; + private boolean suggestedOpOnly = false; + private boolean executableOnConsole = true; + private List subCommands; + private boolean bedrockOnly; + private CommandExecutor executor; + + @Override + public Command.Builder source(@NonNull Class sourceType) { + this.sourceType = sourceType; + return this; + } + + public CommandBuilder name(@NonNull String name) { + this.name = name; + return this; + } + + public CommandBuilder description(@NonNull String description) { + this.description = description; + return this; + } + + public CommandBuilder permission(@NonNull String permission) { + this.permission = permission; + return this; + } + + public CommandBuilder aliases(@NonNull List aliases) { + this.aliases = aliases; + return this; + } + + @Override + public Command.Builder suggestedOpOnly(boolean suggestedOpOnly) { + this.suggestedOpOnly = suggestedOpOnly; + return this; + } + + public CommandBuilder executableOnConsole(boolean executableOnConsole) { + this.executableOnConsole = executableOnConsole; + return this; + } + + public CommandBuilder subCommands(@NonNull List subCommands) { + this.subCommands = subCommands; + return this; + } + + public CommandBuilder bedrockOnly(boolean bedrockOnly) { + this.bedrockOnly = bedrockOnly; + return this; + } + + public CommandBuilder executor(@NonNull CommandExecutor executor) { + this.executor = executor; + return this; + } + + @NonNull + public GeyserExtensionCommand build() { + if (this.name == null || this.name.isBlank()) { + throw new IllegalArgumentException("Command cannot be null or blank!"); + } + + if (this.sourceType == null) { + throw new IllegalArgumentException("Source type was not defined for command " + this.name + " in extension " + this.extension.name()); + } + + return new GeyserExtensionCommand(this.extension, this.name, this.description, this.permission) { + + @SuppressWarnings("unchecked") + @Override + public void execute(@Nullable GeyserSession session, GeyserCommandSource sender, String[] args) { + Class sourceType = CommandBuilder.this.sourceType; + CommandExecutor executor = CommandBuilder.this.executor; + if (sourceType.isInstance(session)) { + executor.execute((T) session, this, args); + return; + } + + if (sourceType.isInstance(sender)) { + executor.execute((T) sender, this, args); + return; + } + + GeyserImpl.getInstance().getLogger().debug("Ignoring command " + this.name + " due to no suitable sender."); + } + + @NonNull + @Override + public List aliases() { + return CommandBuilder.this.aliases == null ? Collections.emptyList() : CommandBuilder.this.aliases; + } + + @Override + public boolean isSuggestedOpOnly() { + return CommandBuilder.this.suggestedOpOnly; + } + + @NonNull + @Override + public List subCommands() { + return CommandBuilder.this.subCommands == null ? Collections.emptyList() : CommandBuilder.this.subCommands; + } + + @Override + public boolean isBedrockOnly() { + return CommandBuilder.this.bedrockOnly; + } + + @Override + public boolean isExecutableOnConsole() { + return CommandBuilder.this.executableOnConsole; + } + }; + } + } +} diff --git a/core/src/main/java/org/geysermc/geyser/command/CommandSender.java b/core/src/main/java/org/geysermc/geyser/command/GeyserCommandSource.java similarity index 68% rename from core/src/main/java/org/geysermc/geyser/command/CommandSender.java rename to core/src/main/java/org/geysermc/geyser/command/GeyserCommandSource.java index 61adad717..88d148b11 100644 --- a/core/src/main/java/org/geysermc/geyser/command/CommandSender.java +++ b/core/src/main/java/org/geysermc/geyser/command/GeyserCommandSource.java @@ -25,49 +25,25 @@ package org.geysermc.geyser.command; +import org.geysermc.geyser.api.command.CommandSource; +import org.geysermc.geyser.text.GeyserLocale; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; -import org.geysermc.geyser.text.GeyserLocale; /** * Implemented on top of any class that can send a command. * For example, it wraps around Spigot's CommandSender class. */ -public interface CommandSender { +public interface GeyserCommandSource extends CommandSource { - String name(); - - default void sendMessage(String[] messages) { - for (String message : messages) { - sendMessage(message); - } + /** + * {@inheritDoc} + */ + default String locale() { + return GeyserLocale.getDefaultLocale(); } - void sendMessage(String message); - default void sendMessage(Component message) { sendMessage(LegacyComponentSerializer.legacySection().serialize(message)); } - - /** - * @return true if the specified sender is from the console. - */ - boolean isConsole(); - - /** - * Returns the locale of the command sender. Defaults to the default locale at {@link GeyserLocale#getDefaultLocale()}. - * - * @return the locale of the command sender. - */ - default String getLocale() { - return GeyserLocale.getDefaultLocale(); - } - - /** - * Checks if the CommandSender has a permission - * - * @param permission The permission node to check - * @return true if the CommandSender has the requested permission, false if not - */ - boolean hasPermission(String permission); } diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/AdvancedTooltipsCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/AdvancedTooltipsCommand.java index 18546c914..466515b3f 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/AdvancedTooltipsCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/AdvancedTooltipsCommand.java @@ -25,8 +25,8 @@ package org.geysermc.geyser.command.defaults; -import org.geysermc.geyser.command.CommandSender; import org.geysermc.geyser.command.GeyserCommand; +import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.MinecraftLocale; @@ -36,11 +36,11 @@ public class AdvancedTooltipsCommand extends GeyserCommand { } @Override - public void execute(GeyserSession session, CommandSender sender, String[] args) { + public void execute(GeyserSession session, GeyserCommandSource sender, String[] args) { if (session != null) { String onOrOff = session.isAdvancedTooltips() ? "off" : "on"; session.setAdvancedTooltips(!session.isAdvancedTooltips()); - session.sendMessage("§l§e" + MinecraftLocale.getLocaleString("debug.prefix", session.getLocale()) + " §r" + MinecraftLocale.getLocaleString("debug.advanced_tooltips." + onOrOff, session.getLocale())); + session.sendMessage("§l§e" + MinecraftLocale.getLocaleString("debug.prefix", session.locale()) + " §r" + MinecraftLocale.getLocaleString("debug.advanced_tooltips." + onOrOff, session.locale())); session.getInventoryTranslator().updateInventory(session, session.getPlayerInventory()); } } diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/AdvancementsCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/AdvancementsCommand.java index 169158572..28253433f 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/AdvancementsCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/AdvancementsCommand.java @@ -25,8 +25,8 @@ package org.geysermc.geyser.command.defaults; -import org.geysermc.geyser.command.CommandSender; import org.geysermc.geyser.command.GeyserCommand; +import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.session.GeyserSession; public class AdvancementsCommand extends GeyserCommand { @@ -35,7 +35,7 @@ public class AdvancementsCommand extends GeyserCommand { } @Override - public void execute(GeyserSession session, CommandSender sender, String[] args) { + public void execute(GeyserSession session, GeyserCommandSource sender, String[] args) { if (session != null) { session.getAdvancementsCache().buildAndShowMenuForm(); } diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/ConnectionTestCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/ConnectionTestCommand.java index 576d17128..95c115769 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/ConnectionTestCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/ConnectionTestCommand.java @@ -28,8 +28,8 @@ package org.geysermc.geyser.command.defaults; import com.fasterxml.jackson.databind.JsonNode; import org.geysermc.common.PlatformType; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandSender; import org.geysermc.geyser.command.GeyserCommand; +import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.util.LoopbackUtil; @@ -47,10 +47,10 @@ public class ConnectionTestCommand extends GeyserCommand { } @Override - public void execute(@Nullable GeyserSession session, CommandSender sender, String[] args) { + public void execute(@Nullable GeyserSession session, GeyserCommandSource sender, String[] args) { // Only allow the console to create dumps on Geyser Standalone if (!sender.isConsole() && geyser.getPlatformType() == PlatformType.STANDALONE) { - sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", sender.getLocale())); + sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", sender.locale())); return; } @@ -69,13 +69,13 @@ public class ConnectionTestCommand extends GeyserCommand { } // Issue: do the ports not line up? - if (port != geyser.getConfig().getBedrock().getPort()) { + if (port != geyser.getConfig().getBedrock().port()) { sender.sendMessage("The port you supplied (" + port + ") does not match the port supplied in Geyser's configuration (" - + geyser.getConfig().getBedrock().getPort() + "). You can change it under `bedrock` `port`."); + + geyser.getConfig().getBedrock().port() + "). You can change it under `bedrock` `port`."); } // Issue: is the `bedrock` `address` in the config different? - if (!geyser.getConfig().getBedrock().getAddress().equals("0.0.0.0")) { + if (!geyser.getConfig().getBedrock().address().equals("0.0.0.0")) { sender.sendMessage("The address specified in `bedrock` `address` is not \"0.0.0.0\" - this may cause issues unless this is deliberate and intentional."); } @@ -129,7 +129,7 @@ public class ConnectionTestCommand extends GeyserCommand { }); } - private void sendLinks(CommandSender sender) { + private void sendLinks(GeyserCommandSource sender) { sender.sendMessage("If you still have issues, check to see if your hosting provider has a specific setup: " + "https://wiki.geysermc.org/geyser/supported-hosting-providers/" + ", see this page: " + "https://wiki.geysermc.org/geyser/fixing-unable-to-connect-to-world/" + ", or contact us on our Discord: " + "https://discord.gg/geysermc"); diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/DumpCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/DumpCommand.java index 0bac381ba..60683d34a 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/DumpCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/DumpCommand.java @@ -29,14 +29,15 @@ import com.fasterxml.jackson.core.util.DefaultIndenter; import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.common.PlatformType; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandSender; import org.geysermc.geyser.command.GeyserCommand; -import org.geysermc.geyser.text.ChatColor; -import org.geysermc.geyser.text.AsteriskSerializer; +import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.dump.DumpInfo; import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.text.AsteriskSerializer; +import org.geysermc.geyser.text.ChatColor; import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.util.WebUtils; @@ -58,10 +59,10 @@ public class DumpCommand extends GeyserCommand { } @Override - public void execute(GeyserSession session, CommandSender sender, String[] args) { + public void execute(GeyserSession session, GeyserCommandSource sender, String[] args) { // Only allow the console to create dumps on Geyser Standalone if (!sender.isConsole() && geyser.getPlatformType() == PlatformType.STANDALONE) { - sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", sender.getLocale())); + sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", sender.locale())); return; } @@ -80,7 +81,7 @@ public class DumpCommand extends GeyserCommand { AsteriskSerializer.showSensitive = showSensitive; - sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.collecting", sender.getLocale())); + sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.collecting", sender.locale())); String dumpData; try { if (offlineDump) { @@ -92,7 +93,7 @@ public class DumpCommand extends GeyserCommand { dumpData = MAPPER.writeValueAsString(new DumpInfo(addLog)); } } catch (IOException e) { - sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.collect_error", sender.getLocale())); + sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.collect_error", sender.locale())); geyser.getLogger().error(GeyserLocale.getLocaleStringLog("geyser.commands.dump.collect_error_short"), e); return; } @@ -100,21 +101,21 @@ public class DumpCommand extends GeyserCommand { String uploadedDumpUrl = ""; if (offlineDump) { - sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.writing", sender.getLocale())); + sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.writing", sender.locale())); try { FileOutputStream outputStream = new FileOutputStream(GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("dump.json").toFile()); outputStream.write(dumpData.getBytes()); outputStream.close(); } catch (IOException e) { - sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.write_error", sender.getLocale())); + sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.write_error", sender.locale())); geyser.getLogger().error(GeyserLocale.getLocaleStringLog("geyser.commands.dump.write_error_short"), e); return; } uploadedDumpUrl = "dump.json"; } else { - sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.uploading", sender.getLocale())); + sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.uploading", sender.locale())); String response; JsonNode responseNode; @@ -122,27 +123,28 @@ public class DumpCommand extends GeyserCommand { response = WebUtils.post(DUMP_URL + "documents", dumpData); responseNode = MAPPER.readTree(response); } catch (IOException e) { - sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.upload_error", sender.getLocale())); + sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.upload_error", sender.locale())); geyser.getLogger().error(GeyserLocale.getLocaleStringLog("geyser.commands.dump.upload_error_short"), e); return; } if (!responseNode.has("key")) { - sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.upload_error_short", sender.getLocale()) + ": " + (responseNode.has("message") ? responseNode.get("message").asText() : response)); + sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.upload_error_short", sender.locale()) + ": " + (responseNode.has("message") ? responseNode.get("message").asText() : response)); return; } uploadedDumpUrl = DUMP_URL + responseNode.get("key").asText(); } - sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.message", sender.getLocale()) + " " + ChatColor.DARK_AQUA + uploadedDumpUrl); + sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.message", sender.locale()) + " " + ChatColor.DARK_AQUA + uploadedDumpUrl); if (!sender.isConsole()) { geyser.getLogger().info(GeyserLocale.getLocaleStringLog("geyser.commands.dump.created", sender.name(), uploadedDumpUrl)); } } + @NonNull @Override - public List getSubCommands() { + public List subCommands() { return Arrays.asList("offline", "full", "logs"); } diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/ExtensionsCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/ExtensionsCommand.java new file mode 100644 index 000000000..30d422b23 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/ExtensionsCommand.java @@ -0,0 +1,66 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.command.defaults; + +import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.extension.Extension; +import org.geysermc.geyser.command.GeyserCommand; +import org.geysermc.geyser.command.GeyserCommandSource; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.text.ChatColor; +import org.geysermc.geyser.text.GeyserLocale; +import org.jetbrains.annotations.Nullable; + +import java.util.Comparator; +import java.util.List; + +public class ExtensionsCommand extends GeyserCommand { + private final GeyserImpl geyser; + + public ExtensionsCommand(GeyserImpl geyser, String name, String description, String permission) { + super(name, description, permission); + + this.geyser = geyser; + } + + @Override + public void execute(@Nullable GeyserSession session, GeyserCommandSource sender, String[] args) { + // TODO: Pagination + int page = 1; + int maxPage = 1; + String header = GeyserLocale.getPlayerLocaleString("geyser.commands.extensions.header", sender.locale(), page, maxPage); + sender.sendMessage(header); + + this.geyser.extensionManager().extensions().stream().sorted(Comparator.comparing(Extension::name)).forEach(extension -> { + String extensionName = (extension.isEnabled() ? ChatColor.GREEN : ChatColor.RED) + extension.name(); + sender.sendMessage("- " + extensionName + ChatColor.RESET + " v" + extension.description().version() + formatAuthors(extension.description().authors())); + }); + } + + private String formatAuthors(List authors) { + return authors.isEmpty() ? "" : " by: " + String.join(", ", authors); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/HelpCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/HelpCommand.java index 85682b294..6e7ad2f04 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/HelpCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/HelpCommand.java @@ -27,10 +27,11 @@ package org.geysermc.geyser.command.defaults; import org.geysermc.common.PlatformType; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandSender; +import org.geysermc.geyser.api.command.Command; import org.geysermc.geyser.command.GeyserCommand; -import org.geysermc.geyser.text.ChatColor; +import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.text.ChatColor; import org.geysermc.geyser.text.GeyserLocale; import java.util.Collections; @@ -38,10 +39,15 @@ import java.util.Map; public class HelpCommand extends GeyserCommand { private final GeyserImpl geyser; + private final String baseCommand; + private final Map commands; - public HelpCommand(GeyserImpl geyser, String name, String description, String permission) { + public HelpCommand(GeyserImpl geyser, String name, String description, String permission, + String baseCommand, Map commands) { super(name, description, permission); this.geyser = geyser; + this.baseCommand = baseCommand; + this.commands = commands; this.setAliases(Collections.singletonList("?")); } @@ -54,26 +60,25 @@ public class HelpCommand extends GeyserCommand { * @param args Not used. */ @Override - public void execute(GeyserSession session, CommandSender sender, String[] args) { + public void execute(GeyserSession session, GeyserCommandSource sender, String[] args) { int page = 1; int maxPage = 1; - String header = GeyserLocale.getPlayerLocaleString("geyser.commands.help.header", sender.getLocale(), page, maxPage); + String header = GeyserLocale.getPlayerLocaleString("geyser.commands.help.header", sender.locale(), page, maxPage); sender.sendMessage(header); - Map cmds = geyser.getCommandManager().getCommands(); - for (Map.Entry entry : cmds.entrySet()) { - GeyserCommand cmd = entry.getValue(); + this.commands.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(entry -> { + Command cmd = entry.getValue(); // Standalone hack-in since it doesn't have a concept of permissions - if (geyser.getPlatformType() == PlatformType.STANDALONE || sender.hasPermission(cmd.getPermission())) { + if (geyser.getPlatformType() == PlatformType.STANDALONE || sender.hasPermission(cmd.permission())) { // Only list commands the player can actually run if (cmd.isBedrockOnly() && session == null) { - continue; + return; } - sender.sendMessage(ChatColor.YELLOW + "/geyser " + entry.getKey() + ChatColor.WHITE + ": " + - GeyserLocale.getPlayerLocaleString(cmd.getDescription(), sender.getLocale())); + sender.sendMessage(ChatColor.YELLOW + "/" + baseCommand + " " + entry.getKey() + ChatColor.WHITE + ": " + + GeyserLocale.getPlayerLocaleString(cmd.description(), sender.locale())); } - } + }); } } diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/ListCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/ListCommand.java index 0a4cfa023..90446fbb6 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/ListCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/ListCommand.java @@ -26,8 +26,8 @@ package org.geysermc.geyser.command.defaults; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandSender; import org.geysermc.geyser.command.GeyserCommand; +import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.GeyserLocale; @@ -44,10 +44,10 @@ public class ListCommand extends GeyserCommand { } @Override - public void execute(GeyserSession session, CommandSender sender, String[] args) { - String message = GeyserLocale.getPlayerLocaleString("geyser.commands.list.message", sender.getLocale(), + public void execute(GeyserSession session, GeyserCommandSource sender, String[] args) { + String message = GeyserLocale.getPlayerLocaleString("geyser.commands.list.message", sender.locale(), geyser.getSessionManager().size(), - geyser.getSessionManager().getAllSessions().stream().map(GeyserSession::name).collect(Collectors.joining(" "))); + geyser.getSessionManager().getAllSessions().stream().map(GeyserSession::bedrockUsername).collect(Collectors.joining(" "))); sender.sendMessage(message); } diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/OffhandCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/OffhandCommand.java index 48afd21fe..bba2e8d21 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/OffhandCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/OffhandCommand.java @@ -30,8 +30,8 @@ import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundPlayerActionPacket; import com.nukkitx.math.vector.Vector3i; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandSender; import org.geysermc.geyser.command.GeyserCommand; +import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.session.GeyserSession; public class OffhandCommand extends GeyserCommand { @@ -41,7 +41,7 @@ public class OffhandCommand extends GeyserCommand { } @Override - public void execute(GeyserSession session, CommandSender sender, String[] args) { + public void execute(GeyserSession session, GeyserCommandSource sender, String[] args) { if (session == null) { return; } diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/ReloadCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/ReloadCommand.java index e970e5d3d..843e93de0 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/ReloadCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/ReloadCommand.java @@ -27,8 +27,8 @@ package org.geysermc.geyser.command.defaults; import org.geysermc.common.PlatformType; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandSender; import org.geysermc.geyser.command.GeyserCommand; +import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.GeyserLocale; @@ -42,12 +42,12 @@ public class ReloadCommand extends GeyserCommand { } @Override - public void execute(GeyserSession session, CommandSender sender, String[] args) { + public void execute(GeyserSession session, GeyserCommandSource sender, String[] args) { if (!sender.isConsole() && geyser.getPlatformType() == PlatformType.STANDALONE) { return; } - String message = GeyserLocale.getPlayerLocaleString("geyser.commands.reload.message", sender.getLocale()); + String message = GeyserLocale.getPlayerLocaleString("geyser.commands.reload.message", sender.locale()); sender.sendMessage(message); diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/SettingsCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/SettingsCommand.java index 58d778ba9..7828cf1d2 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/SettingsCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/SettingsCommand.java @@ -26,8 +26,8 @@ package org.geysermc.geyser.command.defaults; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandSender; import org.geysermc.geyser.command.GeyserCommand; +import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.util.SettingsUtils; @@ -37,7 +37,7 @@ public class SettingsCommand extends GeyserCommand { } @Override - public void execute(GeyserSession session, CommandSender sender, String[] args) { + public void execute(GeyserSession session, GeyserCommandSource sender, String[] args) { if (session != null) { session.sendForm(SettingsUtils.buildForm(session)); } diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/StatisticsCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/StatisticsCommand.java index e54b0fb9b..ea2da51df 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/StatisticsCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/StatisticsCommand.java @@ -28,8 +28,8 @@ package org.geysermc.geyser.command.defaults; import com.github.steveice10.mc.protocol.data.game.ClientCommand; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.ServerboundClientCommandPacket; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandSender; import org.geysermc.geyser.command.GeyserCommand; +import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.session.GeyserSession; public class StatisticsCommand extends GeyserCommand { @@ -39,7 +39,7 @@ public class StatisticsCommand extends GeyserCommand { } @Override - public void execute(GeyserSession session, CommandSender sender, String[] args) { + public void execute(GeyserSession session, GeyserCommandSource sender, String[] args) { if (session == null) return; session.setWaitingForStatistics(true); diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/StopCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/StopCommand.java index 9c7bd8140..151aa2d84 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/StopCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/StopCommand.java @@ -27,8 +27,8 @@ package org.geysermc.geyser.command.defaults; import org.geysermc.common.PlatformType; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandSender; import org.geysermc.geyser.command.GeyserCommand; +import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.GeyserLocale; @@ -46,9 +46,9 @@ public class StopCommand extends GeyserCommand { } @Override - public void execute(GeyserSession session, CommandSender sender, String[] args) { + public void execute(GeyserSession session, GeyserCommandSource sender, String[] args) { if (!sender.isConsole() && geyser.getPlatformType() == PlatformType.STANDALONE) { - sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", sender.getLocale())); + sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", sender.locale())); return; } diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/VersionCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/VersionCommand.java index f4f62892a..fbe4fb4f6 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/VersionCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/VersionCommand.java @@ -28,20 +28,18 @@ package org.geysermc.geyser.command.defaults; import com.nukkitx.protocol.bedrock.BedrockPacketCodec; import org.geysermc.common.PlatformType; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandSender; import org.geysermc.geyser.command.GeyserCommand; -import org.geysermc.geyser.network.MinecraftProtocol; +import org.geysermc.geyser.command.GeyserCommandSource; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.ChatColor; import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.util.WebUtils; import java.io.IOException; -import java.io.InputStream; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.List; -import java.util.Properties; public class VersionCommand extends GeyserCommand { @@ -54,49 +52,46 @@ public class VersionCommand extends GeyserCommand { } @Override - public void execute(GeyserSession session, CommandSender sender, String[] args) { + public void execute(GeyserSession session, GeyserCommandSource sender, String[] args) { String bedrockVersions; - List supportedCodecs = MinecraftProtocol.SUPPORTED_BEDROCK_CODECS; + List supportedCodecs = GameProtocol.SUPPORTED_BEDROCK_CODECS; if (supportedCodecs.size() > 1) { bedrockVersions = supportedCodecs.get(0).getMinecraftVersion() + " - " + supportedCodecs.get(supportedCodecs.size() - 1).getMinecraftVersion(); } else { - bedrockVersions = MinecraftProtocol.SUPPORTED_BEDROCK_CODECS.get(0).getMinecraftVersion(); + bedrockVersions = GameProtocol.SUPPORTED_BEDROCK_CODECS.get(0).getMinecraftVersion(); } String javaVersions; - List supportedJavaVersions = MinecraftProtocol.getJavaVersions(); + List supportedJavaVersions = GameProtocol.getJavaVersions(); if (supportedJavaVersions.size() > 1) { javaVersions = supportedJavaVersions.get(0) + " - " + supportedJavaVersions.get(supportedJavaVersions.size() - 1); } else { javaVersions = supportedJavaVersions.get(0); } - sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.version", sender.getLocale(), + sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.version", sender.locale(), GeyserImpl.NAME, GeyserImpl.VERSION, javaVersions, bedrockVersions)); // Disable update checking in dev mode and for players in Geyser Standalone - if (GeyserImpl.getInstance().productionEnvironment() && !(!sender.isConsole() && geyser.getPlatformType() == PlatformType.STANDALONE)) { - sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.checking", sender.getLocale())); - try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResource("git.properties")) { - Properties gitProp = new Properties(); - gitProp.load(stream); - + if (GeyserImpl.getInstance().isProductionEnvironment() && !(!sender.isConsole() && geyser.getPlatformType() == PlatformType.STANDALONE)) { + sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.checking", sender.locale())); + try { String buildXML = WebUtils.getBody("https://ci.opencollab.dev/job/GeyserMC/job/Geyser/job/" + - URLEncoder.encode(gitProp.getProperty("git.branch"), StandardCharsets.UTF_8.toString()) + "/lastSuccessfulBuild/api/xml?xpath=//buildNumber"); + URLEncoder.encode(GeyserImpl.BRANCH, StandardCharsets.UTF_8.toString()) + "/lastSuccessfulBuild/api/xml?xpath=//buildNumber"); if (buildXML.startsWith("")) { int latestBuildNum = Integer.parseInt(buildXML.replaceAll("<(\\\\)?(/)?buildNumber>", "").trim()); - int buildNum = Integer.parseInt(gitProp.getProperty("git.build.number")); + int buildNum = this.geyser.buildNumber(); if (latestBuildNum == buildNum) { - sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.no_updates", sender.getLocale())); + sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.no_updates", sender.locale())); } else { sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.outdated", - sender.getLocale(), (latestBuildNum - buildNum), "https://ci.geysermc.org/")); + sender.locale(), (latestBuildNum - buildNum), "https://ci.geysermc.org/")); } } else { throw new AssertionError("buildNumber missing"); } - } catch (IOException | AssertionError | NumberFormatException e) { + } catch (IOException e) { GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.commands.version.failed"), e); - sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.version.failed", sender.getLocale())); + sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.version.failed", sender.locale())); } } } diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java index 4d9b3f2a4..109ad3211 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java @@ -27,8 +27,10 @@ package org.geysermc.geyser.configuration; import com.fasterxml.jackson.annotation.JsonIgnore; import org.geysermc.geyser.GeyserLogger; -import org.geysermc.geyser.session.auth.AuthType; +import org.geysermc.geyser.api.network.BedrockListener; +import org.geysermc.geyser.api.network.RemoteServer; import org.geysermc.geyser.network.CIDRMatcher; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.text.GeyserLocale; import java.nio.file.Path; @@ -113,20 +115,10 @@ public interface GeyserConfiguration { int getPendingAuthenticationTimeout(); - interface IBedrockConfiguration { - - String getAddress(); - - int getPort(); + interface IBedrockConfiguration extends BedrockListener { boolean isCloneRemotePort(); - String getMotd1(); - - String getMotd2(); - - String getServerName(); - int getCompressionLevel(); boolean isEnableProxyProtocol(); @@ -139,23 +131,25 @@ public interface GeyserConfiguration { List getWhitelistedIPsMatchers(); } - interface IRemoteConfiguration { - - String getAddress(); - - int getPort(); + interface IRemoteConfiguration extends RemoteServer { void setAddress(String address); void setPort(int port); - AuthType getAuthType(); - boolean isPasswordAuthentication(); boolean isUseProxyProtocol(); boolean isForwardHost(); + + default String minecraftVersion() { + return GameProtocol.getJavaMinecraftVersion(); + } + + default int protocolVersion() { + return GameProtocol.getJavaProtocolVersion(); + } } interface IUserAuthenticationInfo { diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java index b93b9a6a0..73e208963 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java @@ -35,8 +35,8 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import lombok.Getter; import lombok.Setter; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.network.CIDRMatcher; -import org.geysermc.geyser.session.auth.AuthType; import org.geysermc.geyser.text.AsteriskSerializer; import org.geysermc.geyser.text.GeyserLocale; @@ -159,24 +159,54 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration @JsonProperty("pending-authentication-timeout") private int pendingAuthenticationTimeout = 120; - @Getter @JsonIgnoreProperties(ignoreUnknown = true) public static class BedrockConfiguration implements IBedrockConfiguration { @AsteriskSerializer.Asterisk(isIp = true) + @JsonProperty("address") private String address = "0.0.0.0"; + @Override + public String address() { + return address; + } + @Setter + @JsonProperty("port") private int port = 19132; + @Override + public int port() { + return port; + } + + @Getter @JsonProperty("clone-remote-port") private boolean cloneRemotePort = false; + @JsonProperty("motd1") private String motd1 = "GeyserMC"; + + @Override + public String primaryMotd() { + return motd1; + } + + @JsonProperty("motd2") private String motd2 = "Geyser"; + @Override + public String secondaryMotd() { + return motd2; + } + @JsonProperty("server-name") private String serverName = GeyserImpl.NAME; + @Override + public String serverName() { + return serverName; + } + @JsonProperty("compression-level") private int compressionLevel = 6; @@ -184,9 +214,11 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration return Math.max(-1, Math.min(compressionLevel, 9)); } + @Getter @JsonProperty("enable-proxy-protocol") private boolean enableProxyProtocol = false; + @Getter @JsonProperty("proxy-protocol-whitelisted-ips") private List proxyProtocolWhitelistedIPs = Collections.emptyList(); @@ -208,28 +240,47 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration } } - @Getter @JsonIgnoreProperties(ignoreUnknown = true) public static class RemoteConfiguration implements IRemoteConfiguration { @Setter @AsteriskSerializer.Asterisk(isIp = true) + @JsonProperty("address") private String address = "auto"; + @Override + public String address() { + return address; + } + @JsonDeserialize(using = PortDeserializer.class) @Setter + @JsonProperty("port") private int port = 25565; + @Override + public int port() { + return port; + } + @Setter - @JsonDeserialize(using = AuthType.Deserializer.class) + @JsonDeserialize(using = AuthTypeDeserializer.class) @JsonProperty("auth-type") private AuthType authType = AuthType.ONLINE; + @Override + public AuthType authType() { + return authType; + } + + @Getter @JsonProperty("allow-password-authentication") private boolean passwordAuthentication = true; + @Getter @JsonProperty("use-proxy-protocol") private boolean useProxyProtocol = false; + @Getter @JsonProperty("forward-hostname") private boolean forwardHost = false; } @@ -299,4 +350,11 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration } } } + + public static class AuthTypeDeserializer extends JsonDeserializer { + @Override + public AuthType deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + return AuthType.getByName(p.getValueAsString()); + } + } } diff --git a/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java b/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java index 1c9be8c3e..5197f2107 100644 --- a/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java +++ b/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java @@ -26,6 +26,7 @@ package org.geysermc.geyser.dump; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.JsonNode; import com.google.common.hash.Hashing; import com.google.common.io.ByteSource; @@ -35,20 +36,21 @@ import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import lombok.AllArgsConstructor; import lombok.Getter; +import org.geysermc.floodgate.util.DeviceOs; +import org.geysermc.floodgate.util.FloodgateInfoHolder; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.text.AsteriskSerializer; +import org.geysermc.geyser.api.GeyserApi; +import org.geysermc.geyser.api.extension.Extension; import org.geysermc.geyser.configuration.GeyserConfiguration; -import org.geysermc.geyser.network.MinecraftProtocol; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.text.AsteriskSerializer; import org.geysermc.geyser.util.CpuUtils; import org.geysermc.geyser.util.FileUtils; import org.geysermc.geyser.util.WebUtils; -import org.geysermc.floodgate.util.DeviceOs; -import org.geysermc.floodgate.util.FloodgateInfoHolder; import java.io.File; import java.io.IOException; -import java.io.InputStream; import java.lang.management.ManagementFactory; import java.net.InetAddress; import java.net.InetSocketAddress; @@ -68,7 +70,7 @@ public class DumpInfo { private final String cpuName; private final Locale systemLocale; private final String systemEncoding; - private Properties gitInfo; + private final GitInfo gitInfo; private final GeyserConfiguration config; private final Floodgate floodgate; private final Object2IntMap userPlatforms; @@ -77,6 +79,7 @@ public class DumpInfo { private LogsInfo logsInfo; private final BootstrapDumpInfo bootstrapInfo; private final FlagsInfo flagsInfo; + private final List extensionInfo; public DumpInfo(boolean addLog) { this.versionInfo = new VersionInfo(); @@ -86,11 +89,7 @@ public class DumpInfo { this.systemLocale = Locale.getDefault(); this.systemEncoding = System.getProperty("file.encoding"); - try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResource("git.properties")) { - this.gitInfo = new Properties(); - this.gitInfo.load(stream); - } catch (IOException ignored) { - } + this.gitInfo = new GitInfo(GeyserImpl.BUILD_NUMBER, GeyserImpl.COMMIT.substring(0, 7), GeyserImpl.COMMIT, GeyserImpl.BRANCH, GeyserImpl.REPOSITORY); this.config = GeyserImpl.getInstance().getConfig(); this.floodgate = new Floodgate(); @@ -129,6 +128,11 @@ public class DumpInfo { this.bootstrapInfo = GeyserImpl.getInstance().getBootstrap().getDumpInfo(); this.flagsInfo = new FlagsInfo(); + + this.extensionInfo = new ArrayList<>(); + for (Extension extension : GeyserApi.api().extensionManager().extensions()) { + this.extensionInfo.add(new ExtensionInfo(extension.isEnabled(), extension.name(), extension.description().version(), extension.description().apiVersion(), extension.description().main(), extension.description().authors())); + } } @Getter @@ -215,11 +219,11 @@ public class DumpInfo { private final int javaProtocol; MCInfo() { - this.bedrockVersions = MinecraftProtocol.SUPPORTED_BEDROCK_CODECS.stream().map(BedrockPacketCodec::getMinecraftVersion).toList(); - this.bedrockProtocols = MinecraftProtocol.SUPPORTED_BEDROCK_CODECS.stream().map(BedrockPacketCodec::getProtocolVersion).toList(); - this.defaultBedrockProtocol = MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion(); - this.javaVersions = MinecraftProtocol.getJavaVersions(); - this.javaProtocol = MinecraftProtocol.getJavaProtocolVersion(); + this.bedrockVersions = GameProtocol.SUPPORTED_BEDROCK_CODECS.stream().map(BedrockPacketCodec::getMinecraftVersion).toList(); + this.bedrockProtocols = GameProtocol.SUPPORTED_BEDROCK_CODECS.stream().map(BedrockPacketCodec::getProtocolVersion).toList(); + this.defaultBedrockProtocol = GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion(); + this.javaVersions = GameProtocol.getJavaVersions(); + this.javaProtocol = GameProtocol.getJavaProtocolVersion(); } } @@ -281,4 +285,29 @@ public class DumpInfo { this.flags = ManagementFactory.getRuntimeMXBean().getInputArguments(); } } + + @Getter + @AllArgsConstructor + public static class ExtensionInfo { + public boolean enabled; + public String name; + public String version; + public String apiVersion; + public String main; + public List authors; + } + + @Getter + @AllArgsConstructor + public static class GitInfo { + private final String buildNumber; + @JsonProperty("git.commit.id.abbrev") + private final String commitHashAbbrev; + @JsonProperty("git.commit.id") + private final String commitHash; + @JsonProperty("git.branch") + private final String branchName; + @JsonProperty("git.remote.origin.url") + private final String originUrl; + } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/AreaEffectCloudEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/AreaEffectCloudEntity.java index 164fbf705..a38a4dd16 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/AreaEffectCloudEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/AreaEffectCloudEntity.java @@ -32,8 +32,8 @@ import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.EntityDefinition; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.registry.Registries; +import org.geysermc.geyser.session.GeyserSession; import java.util.UUID; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java b/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java index 144d1cbf9..1db2e6117 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java @@ -366,7 +366,7 @@ public class Entity { public void setDisplayName(EntityMetadata, ?> entityMetadata) { Optional name = entityMetadata.getValue(); if (name.isPresent()) { - nametag = MessageTranslator.convertMessage(name.get(), session.getLocale()); + nametag = MessageTranslator.convertMessage(name.get(), session.locale()); dirtyMetadata.put(EntityData.NAMETAG, nametag); } else if (!nametag.isEmpty()) { // Clear nametag diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/FireworkEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/FireworkEntity.java index fa22422ba..12498f752 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/FireworkEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/FireworkEntity.java @@ -36,12 +36,12 @@ import com.nukkitx.nbt.NbtMapBuilder; import com.nukkitx.nbt.NbtType; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.packet.SetEntityMotionPacket; +import org.geysermc.floodgate.util.DeviceOs; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.entity.type.player.PlayerEntity; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.level.FireworkColor; +import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.util.MathUtils; -import org.geysermc.floodgate.util.DeviceOs; import java.util.ArrayList; import java.util.List; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/FurnaceMinecartEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/FurnaceMinecartEntity.java index dbd9bf91f..8074cd5ab 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/FurnaceMinecartEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/FurnaceMinecartEntity.java @@ -30,8 +30,8 @@ import com.github.steveice10.mc.protocol.data.game.entity.player.Hand; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import org.geysermc.geyser.entity.EntityDefinition; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.level.block.BlockStateValues; +import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.util.InteractionResult; import java.util.UUID; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/ItemEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/ItemEntity.java index f36a7c732..89db9b0c8 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/ItemEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/ItemEntity.java @@ -35,9 +35,9 @@ import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import com.nukkitx.protocol.bedrock.packet.AddItemEntityPacket; import com.nukkitx.protocol.bedrock.packet.EntityEventPacket; import org.geysermc.geyser.entity.EntityDefinition; +import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.inventory.item.ItemTranslator; -import org.geysermc.geyser.level.block.BlockStateValues; import java.util.UUID; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/ItemFrameEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/ItemFrameEntity.java index bc7736e9b..8e4a5323a 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/ItemFrameEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/ItemFrameEntity.java @@ -40,7 +40,6 @@ import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket; import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket; import lombok.Getter; import org.geysermc.geyser.entity.EntityDefinition; -import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.inventory.item.ItemTranslator; import org.geysermc.geyser.util.InteractionResult; @@ -114,7 +113,9 @@ public class ItemFrameEntity extends Entity { if (entityMetadata.getValue() != null) { this.heldItem = entityMetadata.getValue(); ItemData itemData = ItemTranslator.translateToBedrock(session, heldItem); - ItemMapping mapping = session.getItemMappings().getMapping(entityMetadata.getValue()); + + String customIdentifier = session.getItemMappings().getCustomIdMappings().get(itemData.getId()); + NbtMapBuilder builder = NbtMap.builder(); builder.putByte("Count", (byte) itemData.getCount()); @@ -122,7 +123,7 @@ public class ItemFrameEntity extends Entity { builder.put("tag", itemData.getTag()); } builder.putShort("Damage", (short) itemData.getDamage()); - builder.putString("Name", mapping.getBedrockIdentifier()); + builder.putString("Name", customIdentifier != null ? customIdentifier : session.getItemMappings().getMapping(entityMetadata.getValue()).getBedrockIdentifier()); NbtMapBuilder tag = getDefaultTag().toBuilder(); tag.put("Item", builder.build()); tag.putFloat("ItemDropChance", 1.0f); diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/SpawnerMinecartEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/SpawnerMinecartEntity.java index 5f7c906e9..cd5df1bf4 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/SpawnerMinecartEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/SpawnerMinecartEntity.java @@ -28,8 +28,8 @@ package org.geysermc.geyser.entity.type; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import org.geysermc.geyser.entity.EntityDefinition; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.level.block.BlockStateValues; +import org.geysermc.geyser.session.GeyserSession; import java.util.UUID; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableEntity.java index ad8b60bdb..3652860b3 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/ThrowableEntity.java @@ -32,8 +32,8 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.packet.LevelEventPacket; import com.nukkitx.protocol.bedrock.packet.MoveEntityDeltaPacket; import org.geysermc.geyser.entity.EntityDefinition; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.level.block.BlockStateValues; +import org.geysermc.geyser.session.GeyserSession; import java.util.UUID; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/ThrownPotionEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/ThrownPotionEntity.java index 6f6125f2d..fcfc4ff12 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/ThrownPotionEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/ThrownPotionEntity.java @@ -34,9 +34,9 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.entity.EntityDefinition; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.inventory.item.Potion; import org.geysermc.geyser.registry.type.ItemMapping; +import org.geysermc.geyser.session.GeyserSession; import java.util.EnumSet; import java.util.UUID; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/TippedArrowEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/TippedArrowEntity.java index d296019c1..95118f928 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/TippedArrowEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/TippedArrowEntity.java @@ -29,8 +29,8 @@ import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntit import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import org.geysermc.geyser.entity.EntityDefinition; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.inventory.item.TippedArrowPotion; +import org.geysermc.geyser.session.GeyserSession; import java.util.UUID; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/SquidEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/SquidEntity.java index 552f6a46c..6b235a8e5 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/SquidEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/SquidEntity.java @@ -30,8 +30,8 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.packet.MoveEntityDeltaPacket; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.entity.type.Tickable; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.level.block.BlockStateValues; +import org.geysermc.geyser.session.GeyserSession; import java.util.UUID; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/AnimalEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/AnimalEntity.java index 0da53b7c6..16a72a235 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/AnimalEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/AnimalEntity.java @@ -32,8 +32,8 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.entity.type.living.AgeableEntity; import org.geysermc.geyser.inventory.GeyserItemStack; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.registry.type.ItemMapping; +import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.util.InteractionResult; import org.geysermc.geyser.util.InteractiveTag; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/AxolotlEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/AxolotlEntity.java index ca7ee57a2..74652da80 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/AxolotlEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/AxolotlEntity.java @@ -33,8 +33,8 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.inventory.GeyserItemStack; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.registry.type.ItemMapping; +import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.util.EntityUtils; import org.geysermc.geyser.util.InteractionResult; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/BeeEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/BeeEntity.java index 09b1b73c5..ce02905b9 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/BeeEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/BeeEntity.java @@ -33,8 +33,8 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityEventType; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.packet.EntityEventPacket; import org.geysermc.geyser.entity.EntityDefinition; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.registry.type.ItemMapping; +import org.geysermc.geyser.session.GeyserSession; import java.util.UUID; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/ChickenEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/ChickenEntity.java index c5fad8bb8..2185d158b 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/ChickenEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/ChickenEntity.java @@ -27,8 +27,8 @@ package org.geysermc.geyser.entity.type.living.animal; import com.nukkitx.math.vector.Vector3f; import org.geysermc.geyser.entity.EntityDefinition; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.registry.type.ItemMapping; +import org.geysermc.geyser.session.GeyserSession; import java.util.UUID; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/FoxEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/FoxEntity.java index 5ae3bd524..8e350e685 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/FoxEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/FoxEntity.java @@ -31,8 +31,8 @@ import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.EntityDefinition; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.registry.type.ItemMapping; +import org.geysermc.geyser.session.GeyserSession; import java.util.UUID; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/OcelotEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/OcelotEntity.java index af1fe0bad..a44a0e9f9 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/OcelotEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/OcelotEntity.java @@ -30,8 +30,8 @@ import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.inventory.GeyserItemStack; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.registry.type.ItemMapping; +import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.util.InteractionResult; import org.geysermc.geyser.util.InteractiveTag; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PandaEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PandaEntity.java index 51f595526..5e8d9c16f 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PandaEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PandaEntity.java @@ -35,8 +35,8 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.packet.EntityEventPacket; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.inventory.GeyserItemStack; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.registry.type.ItemMapping; +import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.util.InteractionResult; import org.geysermc.geyser.util.InteractiveTag; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PigEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PigEntity.java index db8a1ccd8..3b424b456 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PigEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PigEntity.java @@ -30,8 +30,8 @@ import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.inventory.GeyserItemStack; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.registry.type.ItemMapping; +import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.util.EntityUtils; import org.geysermc.geyser.util.InteractionResult; import org.geysermc.geyser.util.InteractiveTag; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PolarBearEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PolarBearEntity.java index b677c135e..1c5c47261 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PolarBearEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/PolarBearEntity.java @@ -27,8 +27,8 @@ package org.geysermc.geyser.entity.type.living.animal; import com.nukkitx.math.vector.Vector3f; import org.geysermc.geyser.entity.EntityDefinition; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.registry.type.ItemMapping; +import org.geysermc.geyser.session.GeyserSession; import java.util.UUID; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/RabbitEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/RabbitEntity.java index 966e500b4..c49c9beb3 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/RabbitEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/RabbitEntity.java @@ -31,8 +31,8 @@ import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.EntityDefinition; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.registry.type.ItemMapping; +import org.geysermc.geyser.session.GeyserSession; import java.util.UUID; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java index f8d549299..fdbaad997 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/StriderEntity.java @@ -29,11 +29,11 @@ import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanE import com.github.steveice10.mc.protocol.data.game.entity.player.Hand; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.EntityDefinition; +import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.inventory.GeyserItemStack; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.registry.type.ItemMapping; +import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.util.EntityUtils; import org.geysermc.geyser.util.InteractionResult; import org.geysermc.geyser.util.InteractiveTag; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/TurtleEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/TurtleEntity.java index 79a7b8f50..1aa0d4fc9 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/TurtleEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/TurtleEntity.java @@ -29,8 +29,8 @@ import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanE import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.EntityDefinition; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.registry.type.ItemMapping; +import org.geysermc.geyser.session.GeyserSession; import java.util.UUID; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/tameable/CatEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/tameable/CatEntity.java index 022535592..f700f6951 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/tameable/CatEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/tameable/CatEntity.java @@ -34,8 +34,8 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.inventory.GeyserItemStack; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.registry.type.ItemMapping; +import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.util.InteractionResult; import org.geysermc.geyser.util.InteractiveTag; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/tameable/ParrotEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/tameable/ParrotEntity.java index 2b49168dd..51582e087 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/tameable/ParrotEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/tameable/ParrotEntity.java @@ -30,8 +30,8 @@ import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.inventory.GeyserItemStack; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.registry.type.ItemMapping; +import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.util.InteractionResult; import org.geysermc.geyser.util.InteractiveTag; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/tameable/TameableEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/tameable/TameableEntity.java index 33b2144e8..c95556cb4 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/tameable/TameableEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/tameable/TameableEntity.java @@ -31,8 +31,8 @@ import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import lombok.Getter; -import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.EntityDefinition; +import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.type.living.animal.AnimalEntity; import org.geysermc.geyser.session.GeyserSession; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/tameable/WolfEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/tameable/WolfEntity.java index bc5209bcb..d6825e8a1 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/tameable/WolfEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/tameable/WolfEntity.java @@ -34,8 +34,8 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.inventory.GeyserItemStack; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.registry.type.ItemMapping; +import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.util.InteractionResult; import org.geysermc.geyser.util.InteractiveTag; import org.geysermc.geyser.util.ItemUtils; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/EnderDragonPartEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/EnderDragonPartEntity.java index a169081fc..5631a68c9 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/EnderDragonPartEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/EnderDragonPartEntity.java @@ -28,8 +28,8 @@ package org.geysermc.geyser.entity.type.living.monster; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.EntityDefinitions; +import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.session.GeyserSession; public class EnderDragonPartEntity extends Entity { diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/GuardianEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/GuardianEntity.java index fe1f3038b..e2454123f 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/GuardianEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/GuardianEntity.java @@ -28,8 +28,8 @@ package org.geysermc.geyser.entity.type.living.monster; import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; -import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.EntityDefinition; +import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.session.GeyserSession; import java.util.UUID; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/WitherEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/WitherEntity.java index d6926996e..81aa1ed99 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/WitherEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/WitherEntity.java @@ -28,8 +28,8 @@ package org.geysermc.geyser.entity.type.living.monster; import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; -import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.EntityDefinition; +import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.session.GeyserSession; import java.util.UUID; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/raid/PillagerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/raid/PillagerEntity.java index 4359c4254..716c54de1 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/raid/PillagerEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/raid/PillagerEntity.java @@ -28,8 +28,8 @@ package org.geysermc.geyser.entity.type.living.monster.raid; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.geyser.entity.EntityDefinition; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.registry.type.ItemMapping; +import org.geysermc.geyser.session.GeyserSession; import java.util.UUID; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java index f16f46e2e..7a5d34973 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java @@ -73,7 +73,7 @@ public class SessionPlayerEntity extends PlayerEntity { private int fakeTradeXp; public SessionPlayerEntity(GeyserSession session) { - super(session, -1, 1, UUID.randomUUID(), Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0, "unknown", null); + super(session, -1, 1, null, Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0, null, null); valid = true; } diff --git a/core/src/main/java/org/geysermc/geyser/event/GeyserEventBus.java b/core/src/main/java/org/geysermc/geyser/event/GeyserEventBus.java new file mode 100644 index 000000000..9593e327e --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/event/GeyserEventBus.java @@ -0,0 +1,72 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.event; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.event.Event; +import org.geysermc.event.PostOrder; +import org.geysermc.event.bus.impl.OwnedEventBusImpl; +import org.geysermc.event.subscribe.OwnedSubscriber; +import org.geysermc.event.subscribe.Subscribe; +import org.geysermc.geyser.api.event.EventBus; +import org.geysermc.geyser.api.event.EventRegistrar; +import org.geysermc.geyser.api.event.EventSubscriber; +import org.geysermc.geyser.api.extension.Extension; + +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +@SuppressWarnings("unchecked") +public final class GeyserEventBus extends OwnedEventBusImpl> + implements EventBus { + @Override + protected > B makeSubscription( + @NonNull EventRegistrar owner, + @NonNull Class eventClass, + @NonNull Subscribe subscribe, + @NonNull L listener, + @NonNull BiConsumer handler) { + return (B) new GeyserEventSubscriber<>( + owner, eventClass, subscribe.postOrder(), subscribe.ignoreCancelled(), listener, handler + ); + } + + @Override + protected > B makeSubscription( + @NonNull EventRegistrar owner, + @NonNull Class eventClass, + @NonNull Consumer handler, + @NonNull PostOrder postOrder) { + return (B) new GeyserEventSubscriber<>(owner, eventClass, handler, postOrder); + } + + @Override + @NonNull + public Set> subscribers(@NonNull Class eventClass) { + return castGenericSet(super.subscribers(eventClass)); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/event/GeyserEventRegistrar.java b/core/src/main/java/org/geysermc/geyser/event/GeyserEventRegistrar.java new file mode 100644 index 000000000..85c36a132 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/event/GeyserEventRegistrar.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/Geyser + */ + +package org.geysermc.geyser.event; + +import org.geysermc.geyser.api.event.EventRegistrar; + +public record GeyserEventRegistrar(Object owner) implements EventRegistrar { + + @Override + public String toString() { + return "GeyserEventRegistrar{" + + "owner=" + this.owner + + '}'; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/event/GeyserEventSubscriber.java b/core/src/main/java/org/geysermc/geyser/event/GeyserEventSubscriber.java new file mode 100644 index 000000000..d33de8cdd --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/event/GeyserEventSubscriber.java @@ -0,0 +1,58 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.event; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.event.Event; +import org.geysermc.event.PostOrder; +import org.geysermc.event.subscribe.impl.OwnedSubscriberImpl; +import org.geysermc.geyser.api.event.EventRegistrar; +import org.geysermc.geyser.api.event.ExtensionEventSubscriber; +import org.geysermc.geyser.api.extension.Extension; + +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +public final class GeyserEventSubscriber extends OwnedSubscriberImpl + implements ExtensionEventSubscriber { + GeyserEventSubscriber( + @NonNull R owner, + @NonNull Class eventClass, + @NonNull Consumer handler, + @NonNull PostOrder postOrder) { + super(owner, eventClass, handler, postOrder); + } + + GeyserEventSubscriber( + @NonNull R owner, + @NonNull Class eventClass, + @NonNull PostOrder postOrder, + boolean ignoreCancelled, + @NonNull H handlerInstance, + @NonNull BiConsumer handler) { + super(owner, eventClass, postOrder, ignoreCancelled, handlerInstance, handler); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/event/type/GeyserDefineCommandsEventImpl.java b/core/src/main/java/org/geysermc/geyser/event/type/GeyserDefineCommandsEventImpl.java new file mode 100644 index 000000000..e07a62d8a --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/event/type/GeyserDefineCommandsEventImpl.java @@ -0,0 +1,46 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.event.type; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.api.command.Command; +import org.geysermc.geyser.api.event.lifecycle.GeyserDefineCommandsEvent; + +import java.util.Collections; +import java.util.Map; + +public abstract class GeyserDefineCommandsEventImpl implements GeyserDefineCommandsEvent { + private final Map commands; + + public GeyserDefineCommandsEventImpl(Map commands) { + this.commands = commands; + } + + @Override + public @NonNull Map commands() { + return Collections.unmodifiableMap(this.commands); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/event/type/GeyserDefineCustomItemsEventImpl.java b/core/src/main/java/org/geysermc/geyser/event/type/GeyserDefineCustomItemsEventImpl.java new file mode 100644 index 000000000..65fd7ea0d --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/event/type/GeyserDefineCustomItemsEventImpl.java @@ -0,0 +1,85 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.event.type; + +import com.google.common.collect.Multimap; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.api.event.lifecycle.GeyserDefineCustomItemsEvent; +import org.geysermc.geyser.api.item.custom.CustomItemData; +import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public abstract class GeyserDefineCustomItemsEventImpl implements GeyserDefineCustomItemsEvent { + private final Multimap customItems; + private final List nonVanillaCustomItems; + + public GeyserDefineCustomItemsEventImpl(Multimap customItems, List nonVanillaCustomItems) { + this.customItems = customItems; + this.nonVanillaCustomItems = nonVanillaCustomItems; + } + + /** + * Gets a multimap of all the already registered custom items indexed by the item's extended java item's identifier. + * + * @return a multimap of all the already registered custom items + */ + @Override + public Map> getExistingCustomItems() { + return Collections.unmodifiableMap(this.customItems.asMap()); + } + + /** + * Gets the list of the already registered non-vanilla custom items. + * + * @return the list of the already registered non-vanilla custom items + */ + @Override + public List getExistingNonVanillaCustomItems() { + return Collections.unmodifiableList(this.nonVanillaCustomItems); + } + + /** + * Registers a custom item with a base Java item. This is used to register items with custom textures and properties + * based on NBT data. + * + * @param identifier the base (java) item + * @param customItemData the custom item data to register + * @return if the item was registered + */ + public abstract boolean register(@NonNull String identifier, @NonNull CustomItemData customItemData); + + /** + * Registers a custom item with no base item. This is used for mods. + * + * @param customItemData the custom item data to register + * @return if the item was registered + */ + public abstract boolean register(@NonNull NonVanillaCustomItemData customItemData); +} diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionClassLoader.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionClassLoader.java new file mode 100644 index 000000000..b94e70ed0 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionClassLoader.java @@ -0,0 +1,100 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.extension; + +import it.unimi.dsi.fastutil.objects.Object2ObjectMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import org.geysermc.geyser.api.extension.Extension; +import org.geysermc.geyser.api.extension.ExtensionDescription; +import org.geysermc.geyser.api.extension.exception.InvalidExtensionException; + +import java.lang.reflect.InvocationTargetException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Path; + +public class GeyserExtensionClassLoader extends URLClassLoader { + private final GeyserExtensionLoader loader; + private final Object2ObjectMap> classes = new Object2ObjectOpenHashMap<>(); + + public GeyserExtensionClassLoader(GeyserExtensionLoader loader, ClassLoader parent, Path path) throws MalformedURLException { + super(new URL[] { path.toUri().toURL() }, parent); + this.loader = loader; + } + + public Extension load(ExtensionDescription description) throws InvalidExtensionException { + try { + Class jarClass; + try { + jarClass = Class.forName(description.main(), true, this); + } catch (ClassNotFoundException ex) { + throw new InvalidExtensionException("Class " + description.main() + " not found, extension cannot be loaded", ex); + } + + Class extensionClass; + try { + extensionClass = jarClass.asSubclass(Extension.class); + } catch (ClassCastException ex) { + throw new InvalidExtensionException("Main class " + description.main() + " should implement Extension, but extends " + jarClass.getSuperclass().getSimpleName(), ex); + } + + return extensionClass.getConstructor().newInstance(); + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException ex) { + throw new InvalidExtensionException("No public constructor", ex); + } catch (InstantiationException ex) { + throw new InvalidExtensionException("Abnormal extension type", ex); + } + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + return this.findClass(name, true); + } + + protected Class findClass(String name, boolean checkGlobal) throws ClassNotFoundException { + if (name.startsWith("org.geysermc.geyser.") || name.startsWith("net.minecraft.")) { + throw new ClassNotFoundException(name); + } + + Class result = this.classes.get(name); + if (result == null) { + if (checkGlobal) { + result = this.loader.classByName(name); + } + + if (result == null) { + result = super.findClass(name); + if (result != null) { + this.loader.setClass(name, result); + } + } + + this.classes.put(name, result); + } + return result; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionContainer.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionContainer.java new file mode 100644 index 000000000..a26415a13 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionContainer.java @@ -0,0 +1,52 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.extension; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.experimental.Accessors; +import org.geysermc.geyser.api.event.ExtensionEventBus; +import org.geysermc.geyser.api.extension.Extension; +import org.geysermc.geyser.api.extension.ExtensionDescription; +import org.geysermc.geyser.api.extension.ExtensionLoader; +import org.geysermc.geyser.api.extension.ExtensionLogger; + +import java.nio.file.Path; + +@Accessors(fluent = true) +@Getter +@RequiredArgsConstructor +public class GeyserExtensionContainer { + private final Extension extension; + private final Path dataFolder; + private final ExtensionDescription description; + private final ExtensionLoader loader; + private final ExtensionLogger logger; + private final ExtensionEventBus eventBus; + + @Getter(AccessLevel.NONE) protected boolean enabled; +} diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java new file mode 100644 index 000000000..716b763f5 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionDescription.java @@ -0,0 +1,119 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.extension; + +import lombok.Getter; +import lombok.Setter; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.api.extension.ExtensionDescription; +import org.geysermc.geyser.api.extension.exception.InvalidDescriptionException; +import org.geysermc.geyser.text.GeyserLocale; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.CustomClassLoaderConstructor; + +import java.io.Reader; +import java.util.*; +import java.util.function.Supplier; +import java.util.regex.Pattern; + +public record GeyserExtensionDescription(@NonNull String id, + @NonNull String name, + @NonNull String main, + int majorApiVersion, + int minorApiVersion, + int patchApiVersion, + @NonNull String version, + @NonNull List authors) implements ExtensionDescription { + + private static final Yaml YAML = new Yaml(new CustomClassLoaderConstructor(Source.class.getClassLoader())); + + public static final Pattern ID_PATTERN = Pattern.compile("[a-z][a-z0-9-_]{0,63}"); + public static final Pattern NAME_PATTERN = Pattern.compile("^[A-Za-z_.-]+$"); + public static final Pattern API_VERSION_PATTERN = Pattern.compile("^\\d+\\.\\d+\\.\\d+$"); + + @NonNull + public static GeyserExtensionDescription fromYaml(Reader reader) throws InvalidDescriptionException { + Source source; + try { + source = YAML.loadAs(reader, Source.class); + } catch (Exception e) { + throw new InvalidDescriptionException(e); + } + + String id = require(source::getId, "id"); + if (!ID_PATTERN.matcher(id).matches()) { + throw new InvalidDescriptionException("Invalid extension id, must match: " + ID_PATTERN.pattern()); + } + + String name = require(source::getName, "name"); + if (!NAME_PATTERN.matcher(name).matches()) { + throw new InvalidDescriptionException("Invalid extension name, must match: " + NAME_PATTERN.pattern()); + } + + String version = String.valueOf(source.version); + String main = require(source::getMain, "main"); + + String apiVersion = require(source::getApi, "api"); + if (!API_VERSION_PATTERN.matcher(apiVersion).matches()) { + throw new InvalidDescriptionException(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_api_format", name, apiVersion)); + } + String[] api = apiVersion.split("\\."); + int majorApi = Integer.parseUnsignedInt(api[0]); + int minorApi = Integer.parseUnsignedInt(api[1]); + int patchApi = Integer.parseUnsignedInt(api[2]); + + List authors = new ArrayList<>(); + if (source.author != null) { + authors.add(source.author); + } + if (source.authors != null) { + authors.addAll(source.authors); + } + + return new GeyserExtensionDescription(id, name, main, majorApi, minorApi, patchApi, version, authors); + } + + @NonNull + private static String require(Supplier supplier, String name) throws InvalidDescriptionException { + String value = supplier.get(); + if (value == null) { + throw new InvalidDescriptionException("Extension description is missing string property '" + name + "'"); + } + return value; + } + + @Getter + @Setter + public static class Source { + String id; + String name; + String main; + String api; + String version; + String author; + List authors; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java new file mode 100644 index 000000000..7e998e413 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java @@ -0,0 +1,222 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.extension; + +import it.unimi.dsi.fastutil.objects.Object2ObjectMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import lombok.RequiredArgsConstructor; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.api.Geyser; +import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.event.ExtensionEventBus; +import org.geysermc.geyser.api.extension.*; +import org.geysermc.geyser.api.extension.exception.InvalidDescriptionException; +import org.geysermc.geyser.api.extension.exception.InvalidExtensionException; +import org.geysermc.geyser.extension.event.GeyserExtensionEventBus; +import org.geysermc.geyser.text.GeyserLocale; + +import java.io.IOException; +import java.io.Reader; +import java.nio.file.*; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.regex.Pattern; +import java.util.stream.Stream; + +@RequiredArgsConstructor +public class GeyserExtensionLoader extends ExtensionLoader { + private static final Pattern[] EXTENSION_FILTERS = new Pattern[] { Pattern.compile("^.+\\.jar$") }; + + private final Object2ObjectMap> classes = new Object2ObjectOpenHashMap<>(); + private final Map classLoaders = new HashMap<>(); + private final Map extensionContainers = new HashMap<>(); + private final Path extensionsDirectory = GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("extensions"); + + public GeyserExtensionContainer loadExtension(Path path, GeyserExtensionDescription description) throws InvalidExtensionException { + if (path == null) { + throw new InvalidExtensionException("Path is null"); + } + + if (Files.notExists(path)) { + throw new InvalidExtensionException(new NoSuchFileException(path.toString()) + " does not exist"); + } + + Path parentFile = path.getParent(); + Path dataFolder = parentFile.resolve(description.name()); + if (Files.exists(dataFolder) && !Files.isDirectory(dataFolder)) { + throw new InvalidExtensionException("The folder " + dataFolder + " is not a directory and is the data folder for the extension " + description.name() + "!"); + } + + final GeyserExtensionClassLoader loader; + try { + loader = new GeyserExtensionClassLoader(this, getClass().getClassLoader(), path); + } catch (Throwable e) { + throw new InvalidExtensionException(e); + } + + this.classLoaders.put(description.name(), loader); + + final Extension extension = loader.load(description); + return this.setup(extension, description, dataFolder, new GeyserExtensionEventBus(GeyserImpl.getInstance().eventBus(), extension)); + } + + private GeyserExtensionContainer setup(Extension extension, GeyserExtensionDescription description, Path dataFolder, ExtensionEventBus eventBus) { + GeyserExtensionLogger logger = new GeyserExtensionLogger(GeyserImpl.getInstance().getLogger(), description.name()); + return new GeyserExtensionContainer(extension, dataFolder, description, this, logger, eventBus); + } + + public GeyserExtensionDescription extensionDescription(Path path) throws InvalidDescriptionException { + Map environment = new HashMap<>(); + try (FileSystem fileSystem = FileSystems.newFileSystem(path, environment, null)) { + Path extensionYml = fileSystem.getPath("extension.yml"); + try (Reader reader = Files.newBufferedReader(extensionYml)) { + return GeyserExtensionDescription.fromYaml(reader); + } + } catch (IOException ex) { + throw new InvalidDescriptionException("Failed to load extension description for " + path, ex); + } + } + + public Pattern[] extensionFilters() { + return EXTENSION_FILTERS; + } + + public Class classByName(final String name) throws ClassNotFoundException{ + Class clazz = this.classes.get(name); + if (clazz != null) { + return clazz; + } + + for (GeyserExtensionClassLoader loader : this.classLoaders.values()) { + clazz = loader.findClass(name, false); + if (clazz != null) { + break; + } + } + + return clazz; + } + + void setClass(String name, final Class clazz) { + this.classes.putIfAbsent(name, clazz); + } + + @Override + protected void loadAllExtensions(@NonNull ExtensionManager extensionManager) { + try { + if (Files.notExists(extensionsDirectory)) { + Files.createDirectory(extensionsDirectory); + } + + Map extensions = new LinkedHashMap<>(); + Map loadedExtensions = new LinkedHashMap<>(); + + Pattern[] extensionFilters = this.extensionFilters(); + try (Stream entries = Files.walk(extensionsDirectory)) { + entries.forEach(path -> { + if (Files.isDirectory(path)) { + return; + } + + for (Pattern filter : extensionFilters) { + if (!filter.matcher(path.getFileName().toString()).matches()) { + return; + } + } + + try { + GeyserExtensionDescription description = this.extensionDescription(path); + + String name = description.name(); + if (extensions.containsKey(name) || extensionManager.extension(name) != null) { + GeyserImpl.getInstance().getLogger().warning(GeyserLocale.getLocaleStringLog("geyser.extensions.load.duplicate", name, path.toString())); + return; + } + + // Completely different API version + if (description.majorApiVersion() != Geyser.api().majorApiVersion()) { + GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_api_version", name, description.apiVersion())); + return; + } + + // If the extension requires new API features, being backwards compatible + if (description.minorApiVersion() > Geyser.api().minorApiVersion()) { + GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_api_version", name, description.apiVersion())); + return; + } + + extensions.put(name, path); + loadedExtensions.put(name, this.loadExtension(path, description)); + } catch (Exception e) { + GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_with_name", path.getFileName(), path.toAbsolutePath()), e); + } + }); + } + + for (GeyserExtensionContainer container : loadedExtensions.values()) { + this.extensionContainers.put(container.extension(), container); + this.register(container.extension(), extensionManager); + } + } catch (IOException ex) { + ex.printStackTrace(); + } + } + + @Override + protected boolean isEnabled(@NonNull Extension extension) { + return this.extensionContainers.get(extension).enabled; + } + + @Override + protected void setEnabled(@NonNull Extension extension, boolean enabled) { + this.extensionContainers.get(extension).enabled = enabled; + } + + @NonNull + @Override + protected Path dataFolder(@NonNull Extension extension) { + return this.extensionContainers.get(extension).dataFolder(); + } + + @NonNull + @Override + protected ExtensionDescription description(@NonNull Extension extension) { + return this.extensionContainers.get(extension).description(); + } + + @NonNull + @Override + protected ExtensionEventBus eventBus(@NonNull Extension extension) { + return this.extensionContainers.get(extension).eventBus(); + } + + @NonNull + @Override + protected ExtensionLogger logger(@NonNull Extension extension) { + return this.extensionContainers.get(extension).logger(); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLogger.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLogger.java new file mode 100644 index 000000000..fe23417f8 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLogger.java @@ -0,0 +1,88 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.extension; + +import org.geysermc.geyser.GeyserLogger; +import org.geysermc.geyser.api.extension.ExtensionLogger; + +public class GeyserExtensionLogger implements ExtensionLogger { + private final GeyserLogger logger; + private final String loggerPrefix; + + public GeyserExtensionLogger(GeyserLogger logger, String prefix) { + this.logger = logger; + this.loggerPrefix = prefix; + } + + @Override + public String prefix() { + return this.loggerPrefix; + } + + private String addPrefix(String message) { + return "[" + this.loggerPrefix + "] " + message; + } + + @Override + public void severe(String message) { + this.logger.severe(this.addPrefix(message)); + } + + @Override + public void severe(String message, Throwable error) { + this.logger.severe(this.addPrefix(message), error); + } + + @Override + public void error(String message) { + this.logger.error(this.addPrefix(message)); + } + + @Override + public void error(String message, Throwable error) { + this.logger.error(this.addPrefix(message), error); + } + + @Override + public void warning(String message) { + this.logger.warning(this.addPrefix(message)); + } + + @Override + public void info(String message) { + this.logger.info(this.addPrefix(message)); + } + + @Override + public void debug(String message) { + this.logger.debug(this.addPrefix(message)); + } + + @Override + public boolean isDebug() { + return this.logger.isDebug(); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java new file mode 100644 index 000000000..5dd924301 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionManager.java @@ -0,0 +1,126 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.extension; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.extension.Extension; +import org.geysermc.geyser.api.extension.ExtensionLoader; +import org.geysermc.geyser.api.extension.ExtensionManager; +import org.geysermc.geyser.text.GeyserLocale; + +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +public class GeyserExtensionManager extends ExtensionManager { + private final GeyserExtensionLoader extensionLoader = new GeyserExtensionLoader(); + private final Map extensions = new LinkedHashMap<>(); + + public void init() { + GeyserImpl.getInstance().getLogger().info(GeyserLocale.getLocaleStringLog("geyser.extensions.load.loading")); + + loadAllExtensions(this.extensionLoader); + enableExtensions(); + + GeyserImpl.getInstance().getLogger().info(GeyserLocale.getLocaleStringLog("geyser.extensions.load.done", this.extensions.size())); + } + + @Override + public Extension extension(@NonNull String name) { + return this.extensions.get(name); + } + + @Override + public void enable(@NonNull Extension extension) { + if (!extension.isEnabled()) { + try { + this.enableExtension(extension); + } catch (Exception e) { + GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.enable.failed", extension.name()), e); + this.disable(extension); + } + } + } + + @Override + public void disable(@NonNull Extension extension) { + if (extension.isEnabled()) { + try { + this.disableExtension(extension); + } catch (Exception e) { + GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.disable.failed", extension.name()), e); + } + } + } + + public void enableExtension(Extension extension) { + if (!extension.isEnabled()) { + extension.setEnabled(true); + GeyserImpl.getInstance().eventBus().register(extension, extension); + GeyserImpl.getInstance().getLogger().info(GeyserLocale.getLocaleStringLog("geyser.extensions.enable.success", extension.description().name())); + } + } + + public void enableExtensions() { + for (Extension extension : this.extensions()) { + this.enable(extension); + } + } + + private void disableExtension(@NonNull Extension extension) { + if (extension.isEnabled()) { + GeyserImpl.getInstance().eventBus().unregisterAll(extension); + + extension.setEnabled(false); + GeyserImpl.getInstance().getLogger().info(GeyserLocale.getLocaleStringLog("geyser.extensions.disable.success", extension.description().name())); + } + } + + public void disableExtensions() { + for (Extension extension : this.extensions()) { + this.disable(extension); + } + } + + @NonNull + @Override + public Collection extensions() { + return Collections.unmodifiableCollection(this.extensions.values()); + } + + @Override + public @Nullable ExtensionLoader extensionLoader() { + return this.extensionLoader; + } + + @Override + public void register(@NonNull Extension extension) { + this.extensions.put(extension.name(), extension); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/extension/command/GeyserExtensionCommand.java b/core/src/main/java/org/geysermc/geyser/extension/command/GeyserExtensionCommand.java new file mode 100644 index 000000000..4a7830c90 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/extension/command/GeyserExtensionCommand.java @@ -0,0 +1,43 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.extension.command; + +import org.geysermc.geyser.api.extension.Extension; +import org.geysermc.geyser.command.GeyserCommand; + +public abstract class GeyserExtensionCommand extends GeyserCommand { + private final Extension extension; + + public GeyserExtensionCommand(Extension extension, String name, String description, String permission) { + super(name, description, permission); + + this.extension = extension; + } + + public Extension extension() { + return this.extension; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/extension/event/GeyserExtensionEventBus.java b/core/src/main/java/org/geysermc/geyser/extension/event/GeyserExtensionEventBus.java new file mode 100644 index 000000000..f56b254a6 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/extension/event/GeyserExtensionEventBus.java @@ -0,0 +1,85 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.extension.event; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.event.Event; +import org.geysermc.event.PostOrder; +import org.geysermc.event.subscribe.Subscriber; +import org.geysermc.geyser.api.event.EventBus; +import org.geysermc.geyser.api.event.EventRegistrar; +import org.geysermc.geyser.api.event.EventSubscriber; +import org.geysermc.geyser.api.event.ExtensionEventBus; +import org.geysermc.geyser.api.extension.Extension; + +import java.util.Set; +import java.util.function.Consumer; + +public record GeyserExtensionEventBus(EventBus eventBus, Extension extension) implements ExtensionEventBus { + + @SuppressWarnings({"rawtypes", "unchecked"}) + @Override + public void unsubscribe(@NonNull EventSubscriber subscription) { + eventBus.unsubscribe((EventSubscriber) subscription); + } + + @Override + public boolean fire(@NonNull Event event) { + return eventBus.fire(event); + } + + @Override + public @NonNull Set> subscribers(@NonNull Class eventClass) { + return eventBus.subscribers(eventClass); + } + + @Override + public void register(@NonNull Object listener) { + eventBus.register(extension, listener); + } + + @Override + @SuppressWarnings("unchecked") + public > @NonNull U subscribe( + @NonNull Class eventClass, @NonNull Consumer consumer) { + return eventBus.subscribe(extension, eventClass, consumer); + } + + @Override + @SuppressWarnings("unchecked") + public > @NonNull U subscribe( + @NonNull Class eventClass, + @NonNull Consumer consumer, + @NonNull PostOrder postOrder + ) { + return eventBus.subscribe(extension, eventClass, consumer, postOrder); + } + + @Override + public void unregisterAll() { + eventBus.unregisterAll(extension); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/inventory/AnvilContainer.java b/core/src/main/java/org/geysermc/geyser/inventory/AnvilContainer.java index 688151a9e..141f2b6f2 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/AnvilContainer.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/AnvilContainer.java @@ -75,8 +75,8 @@ public class AnvilContainer extends Container { String originalName = ItemUtils.getCustomName(getInput().getNbt()); - String plainOriginalName = MessageTranslator.convertToPlainText(originalName, session.getLocale()); - String plainNewName = MessageTranslator.convertToPlainText(rename, session.getLocale()); + String plainOriginalName = MessageTranslator.convertToPlainText(originalName, session.locale()); + String plainNewName = MessageTranslator.convertToPlainText(rename, session.locale()); if (!plainOriginalName.equals(plainNewName)) { // Strip out formatting since Java Edition does not allow it correctRename = plainNewName; diff --git a/core/src/main/java/org/geysermc/geyser/inventory/GeyserItemStack.java b/core/src/main/java/org/geysermc/geyser/inventory/GeyserItemStack.java index e249f0167..0fb70a1a2 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/GeyserItemStack.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/GeyserItemStack.java @@ -29,9 +29,9 @@ import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import lombok.Data; +import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.inventory.item.ItemTranslator; -import org.geysermc.geyser.registry.type.ItemMapping; import javax.annotation.Nonnull; diff --git a/core/src/main/java/org/geysermc/geyser/inventory/Inventory.java b/core/src/main/java/org/geysermc/geyser/inventory/Inventory.java index 29233a2e7..f01d242ad 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/Inventory.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/Inventory.java @@ -34,7 +34,6 @@ import lombok.Getter; import lombok.Setter; import lombok.ToString; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.inventory.item.ItemTranslator; import org.jetbrains.annotations.Range; @@ -144,9 +143,9 @@ public abstract class Inventory { protected void updateItemNetId(GeyserItemStack oldItem, GeyserItemStack newItem, GeyserSession session) { if (!newItem.isEmpty()) { - ItemMapping oldMapping = ItemTranslator.getBedrockItemMapping(session, oldItem); - ItemMapping newMapping = ItemTranslator.getBedrockItemMapping(session, newItem); - if (oldMapping.getBedrockId() == newMapping.getBedrockId()) { + int oldMapping = ItemTranslator.getBedrockItemId(session, oldItem); + int newMapping = ItemTranslator.getBedrockItemId(session, newItem); + if (oldMapping == newMapping) { newItem.setNetId(oldItem.getNetId()); } else { newItem.setNetId(session.getNextItemNetId()); diff --git a/core/src/main/java/org/geysermc/geyser/inventory/MerchantContainer.java b/core/src/main/java/org/geysermc/geyser/inventory/MerchantContainer.java index 315e6cb18..93c1917d2 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/MerchantContainer.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/MerchantContainer.java @@ -25,8 +25,8 @@ package org.geysermc.geyser.inventory; -import com.github.steveice10.mc.protocol.data.game.inventory.VillagerTrade; import com.github.steveice10.mc.protocol.data.game.inventory.ContainerType; +import com.github.steveice10.mc.protocol.data.game.inventory.VillagerTrade; import com.github.steveice10.mc.protocol.packet.ingame.clientbound.inventory.ClientboundMerchantOffersPacket; import lombok.Getter; import lombok.Setter; diff --git a/core/src/main/java/org/geysermc/geyser/inventory/click/Click.java b/core/src/main/java/org/geysermc/geyser/inventory/click/Click.java index 027c7a7ce..d7068920e 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/click/Click.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/click/Click.java @@ -25,8 +25,6 @@ package org.geysermc.geyser.inventory.click; -import com.github.steveice10.mc.protocol.data.game.inventory.ClickItemAction; -import com.github.steveice10.mc.protocol.data.game.inventory.ContainerActionType; import com.github.steveice10.mc.protocol.data.game.inventory.*; import lombok.AllArgsConstructor; diff --git a/core/src/main/java/org/geysermc/geyser/inventory/recipe/GeyserShapedRecipe.java b/core/src/main/java/org/geysermc/geyser/inventory/recipe/GeyserShapedRecipe.java index a011fef6d..d61945ad8 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/recipe/GeyserShapedRecipe.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/recipe/GeyserShapedRecipe.java @@ -27,7 +27,6 @@ package org.geysermc.geyser.inventory.recipe; import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; import com.github.steveice10.mc.protocol.data.game.recipe.Ingredient; -import com.github.steveice10.mc.protocol.data.game.recipe.RecipeType; import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapedRecipeData; public record GeyserShapedRecipe(int width, int height, Ingredient[] ingredients, ItemStack result) implements GeyserRecipe { diff --git a/core/src/main/java/org/geysermc/geyser/inventory/updater/AnvilInventoryUpdater.java b/core/src/main/java/org/geysermc/geyser/inventory/updater/AnvilInventoryUpdater.java index 655d0f215..e83971443 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/updater/AnvilInventoryUpdater.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/updater/AnvilInventoryUpdater.java @@ -27,7 +27,10 @@ package org.geysermc.geyser.inventory.updater; import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.inventory.ServerboundRenameItemPacket; -import com.github.steveice10.opennbt.tag.builtin.*; +import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.github.steveice10.opennbt.tag.builtin.ListTag; +import com.github.steveice10.opennbt.tag.builtin.StringTag; +import com.github.steveice10.opennbt.tag.builtin.Tag; import com.nukkitx.nbt.NbtMap; import com.nukkitx.nbt.NbtMapBuilder; import com.nukkitx.protocol.bedrock.data.inventory.ContainerId; @@ -40,12 +43,12 @@ import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.inventory.AnvilContainer; import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.inventory.Inventory; -import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.translator.text.MessageTranslator; -import org.geysermc.geyser.translator.inventory.InventoryTranslator; import org.geysermc.geyser.inventory.item.Enchantment.JavaEnchantment; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.type.EnchantmentData; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.translator.inventory.InventoryTranslator; +import org.geysermc.geyser.translator.text.MessageTranslator; import org.geysermc.geyser.util.ItemUtils; import java.util.Objects; @@ -115,7 +118,7 @@ public class AnvilInventoryUpdater extends InventoryUpdater { // Changing the item in the input slot resets the name field on Bedrock, but // does not result in a FilterTextPacket - String originalName = MessageTranslator.convertToPlainText(ItemUtils.getCustomName(input.getNbt()), session.getLocale()); + String originalName = MessageTranslator.convertToPlainText(ItemUtils.getCustomName(input.getNbt()), session.locale()); ServerboundRenameItemPacket renameItemPacket = new ServerboundRenameItemPacket(originalName); session.sendDownstreamPacket(renameItemPacket); @@ -427,7 +430,7 @@ public class AnvilInventoryUpdater extends InventoryUpdater { String originalName = ItemUtils.getCustomName(anvilContainer.getInput().getNbt()); if (bedrock && originalName != null && anvilContainer.getNewName() != null) { // Check text and formatting - String legacyOriginalName = MessageTranslator.convertMessageLenient(originalName, session.getLocale()); + String legacyOriginalName = MessageTranslator.convertMessageLenient(originalName, session.locale()); return !legacyOriginalName.equals(anvilContainer.getNewName()); } return !Objects.equals(originalName, ItemUtils.getCustomName(anvilContainer.getResult().getNbt())); diff --git a/core/src/main/java/org/geysermc/geyser/inventory/updater/ChestInventoryUpdater.java b/core/src/main/java/org/geysermc/geyser/inventory/updater/ChestInventoryUpdater.java index a468e53bc..8a174c2c5 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/updater/ChestInventoryUpdater.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/updater/ChestInventoryUpdater.java @@ -31,9 +31,9 @@ import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket; import lombok.AllArgsConstructor; import org.geysermc.geyser.inventory.Inventory; import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.translator.inventory.InventoryTranslator; import org.geysermc.geyser.util.InventoryUtils; -import org.geysermc.geyser.text.GeyserLocale; import java.util.ArrayList; import java.util.List; diff --git a/core/src/main/java/org/geysermc/geyser/item/GeyserCustomItemData.java b/core/src/main/java/org/geysermc/geyser/item/GeyserCustomItemData.java new file mode 100644 index 000000000..ddea9937c --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/item/GeyserCustomItemData.java @@ -0,0 +1,158 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.item; + +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.api.item.custom.CustomItemData; +import org.geysermc.geyser.api.item.custom.CustomItemOptions; +import org.geysermc.geyser.api.item.custom.CustomRenderOffsets; +import org.jetbrains.annotations.NotNull; + +@EqualsAndHashCode +@ToString +public class GeyserCustomItemData implements CustomItemData { + private final String name; + private final CustomItemOptions customItemOptions; + private final String displayName; + private final String icon; + private final boolean allowOffhand; + private final int textureSize; + private final CustomRenderOffsets renderOffsets; + + public GeyserCustomItemData(String name, + CustomItemOptions customItemOptions, + String displayName, + String icon, + boolean allowOffhand, + int textureSize, + CustomRenderOffsets renderOffsets) { + this.name = name; + this.customItemOptions = customItemOptions; + this.displayName = displayName; + this.icon = icon; + this.allowOffhand = allowOffhand; + this.textureSize = textureSize; + this.renderOffsets = renderOffsets; + } + + public @NotNull String name() { + return name; + } + + public CustomItemOptions customItemOptions() { + return customItemOptions; + } + + public @NotNull String displayName() { + return displayName; + } + + public @NotNull String icon() { + return icon; + } + + public boolean allowOffhand() { + return allowOffhand; + } + + public int textureSize() { + return textureSize; + } + + public CustomRenderOffsets renderOffsets() { + return renderOffsets; + } + + public static class CustomItemDataBuilder implements Builder { + protected String name = null; + protected CustomItemOptions customItemOptions = null; + + protected String displayName = null; + protected String icon = null; + protected boolean allowOffhand = true; // Bedrock doesn't give items offhand allowance unless they serve gameplay purpose, but we want to be friendly with Java + protected int textureSize = 16; + protected CustomRenderOffsets renderOffsets = null; + + @Override + public Builder name(@NonNull String name) { + this.name = name; + return this; + } + + @Override + public Builder customItemOptions(@NonNull CustomItemOptions customItemOptions) { + this.customItemOptions = customItemOptions; + return this; + } + + @Override + public Builder displayName(@NonNull String displayName) { + this.displayName = displayName; + return this; + } + + @Override + public Builder icon(@NonNull String icon) { + this.icon = icon; + return this; + } + + @Override + public Builder allowOffhand(boolean allowOffhand) { + this.allowOffhand = allowOffhand; + return this; + } + + @Override + public Builder textureSize(int textureSize) { + this.textureSize = textureSize; + return this; + } + + @Override + public Builder renderOffsets(CustomRenderOffsets renderOffsets) { + this.renderOffsets = renderOffsets; + return this; + } + + @Override + public CustomItemData build() { + if (this.name == null || this.customItemOptions == null) { + throw new IllegalArgumentException("Name and custom item options must be set"); + } + + if (this.displayName == null) { + this.displayName = this.name; + } + if (this.icon == null) { + this.icon = this.name; + } + return new GeyserCustomItemData(this.name, this.customItemOptions, this.displayName, this.icon, this.allowOffhand, this.textureSize, this.renderOffsets); + } + } +} diff --git a/core/src/main/java/org/geysermc/geyser/item/GeyserCustomItemOptions.java b/core/src/main/java/org/geysermc/geyser/item/GeyserCustomItemOptions.java new file mode 100644 index 000000000..dd4ae01de --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/item/GeyserCustomItemOptions.java @@ -0,0 +1,69 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.item; + +import org.geysermc.geyser.api.item.custom.CustomItemOptions; +import org.geysermc.geyser.api.util.TriState; + +import java.util.OptionalInt; + +public record GeyserCustomItemOptions(TriState unbreakable, + OptionalInt customModelData, + OptionalInt damagePredicate) implements CustomItemOptions { + + public static class CustomItemOptionsBuilder implements CustomItemOptions.Builder { + private TriState unbreakable = TriState.NOT_SET; + private OptionalInt customModelData = OptionalInt.empty(); + private OptionalInt damagePredicate = OptionalInt.empty(); + + @Override + public Builder unbreakable(boolean unbreakable) { + if (unbreakable) { + this.unbreakable = TriState.TRUE; + } else { + this.unbreakable = TriState.FALSE; + } + return this; + } + + @Override + public Builder customModelData(int customModelData) { + this.customModelData = OptionalInt.of(customModelData); + return this; + } + + @Override + public Builder damagePredicate(int damagePredicate) { + this.damagePredicate = OptionalInt.of(damagePredicate); + return this; + } + + @Override + public CustomItemOptions build() { + return new GeyserCustomItemOptions(unbreakable, customModelData, damagePredicate); + } + } +} diff --git a/core/src/main/java/org/geysermc/geyser/item/GeyserCustomMappingData.java b/core/src/main/java/org/geysermc/geyser/item/GeyserCustomMappingData.java new file mode 100644 index 000000000..3829db3c3 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/item/GeyserCustomMappingData.java @@ -0,0 +1,32 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.item; + +import com.nukkitx.protocol.bedrock.data.inventory.ComponentItemData; +import com.nukkitx.protocol.bedrock.packet.StartGamePacket; + +public record GeyserCustomMappingData(ComponentItemData componentItemData, StartGamePacket.ItemEntry startGamePacketItemEntry, String stringId, int integerId) { +} diff --git a/core/src/main/java/org/geysermc/geyser/item/GeyserNonVanillaCustomItemData.java b/core/src/main/java/org/geysermc/geyser/item/GeyserNonVanillaCustomItemData.java new file mode 100644 index 000000000..efdc1fdcf --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/item/GeyserNonVanillaCustomItemData.java @@ -0,0 +1,303 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.item; + +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.geysermc.geyser.api.item.custom.CustomItemOptions; +import org.geysermc.geyser.api.item.custom.CustomRenderOffsets; +import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData; +import org.jetbrains.annotations.NotNull; + +import java.util.OptionalInt; +import java.util.Set; + +@EqualsAndHashCode(callSuper = true) +@ToString +public final class GeyserNonVanillaCustomItemData extends GeyserCustomItemData implements NonVanillaCustomItemData { + private final String identifier; + private final int javaId; + private final int stackSize; + private final int maxDamage; + private final String toolType; + private final String toolTier; + private final String armorType; + private final int protectionValue; + private final String translationString; + private final Set repairMaterials; + private final OptionalInt creativeCategory; + private final String creativeGroup; + private final boolean isHat; + private final boolean isTool; + + public GeyserNonVanillaCustomItemData(NonVanillaCustomItemDataBuilder builder) { + super(builder.name, builder.customItemOptions, builder.displayName, builder.icon, builder.allowOffhand, + builder.textureSize, builder.renderOffsets); + + this.identifier = builder.identifier; + this.javaId = builder.javaId; + this.stackSize = builder.stackSize; + this.maxDamage = builder.maxDamage; + this.toolType = builder.toolType; + this.toolTier = builder.toolTier; + this.armorType = builder.armorType; + this.protectionValue = builder.protectionValue; + this.translationString = builder.translationString; + this.repairMaterials = builder.repairMaterials; + this.creativeCategory = builder.creativeCategory; + this.creativeGroup = builder.creativeGroup; + this.isHat = builder.hat; + this.isTool = builder.tool; + } + + @Override + public @NotNull String identifier() { + return identifier; + } + + @Override + public int javaId() { + return javaId; + } + + @Override + public int stackSize() { + return stackSize; + } + + @Override + public int maxDamage() { + return maxDamage; + } + + @Override + public String toolType() { + return toolType; + } + + @Override + public String toolTier() { + return toolTier; + } + + @Override + public @Nullable String armorType() { + return armorType; + } + + @Override + public int protectionValue() { + return protectionValue; + } + + @Override + public String translationString() { + return translationString; + } + + @Override + public Set repairMaterials() { + return repairMaterials; + } + + @Override + public @NotNull OptionalInt creativeCategory() { + return creativeCategory; + } + + @Override + public String creativeGroup() { + return creativeGroup; + } + + @Override + public boolean isHat() { + return isHat; + } + + @Override + public boolean isTool() { + return isTool; + } + + public static class NonVanillaCustomItemDataBuilder extends GeyserCustomItemData.CustomItemDataBuilder implements NonVanillaCustomItemData.Builder { + private String identifier = null; + private int javaId = -1; + + private int stackSize = 64; + + private int maxDamage = 0; + + private String toolType = null; + private String toolTier = null; + + private String armorType = null; + private int protectionValue = 0; + + private String translationString; + + private Set repairMaterials; + + private OptionalInt creativeCategory = OptionalInt.empty(); + private String creativeGroup = null; + + private boolean hat = false; + private boolean tool = false; + + @Override + public NonVanillaCustomItemData.Builder name(@NonNull String name) { + return (NonVanillaCustomItemData.Builder) super.name(name); + } + + @Override + public NonVanillaCustomItemData.Builder customItemOptions(@NonNull CustomItemOptions customItemOptions) { + //Do nothing, as that value won't be read + return this; + } + + @Override + public NonVanillaCustomItemData.Builder allowOffhand(boolean allowOffhand) { + return (NonVanillaCustomItemData.Builder) super.allowOffhand(allowOffhand); + } + + @Override + public NonVanillaCustomItemData.Builder displayName(@NonNull String displayName) { + return (NonVanillaCustomItemData.Builder) super.displayName(displayName); + } + + @Override + public NonVanillaCustomItemData.Builder icon(@NonNull String icon) { + return (NonVanillaCustomItemData.Builder) super.icon(icon); + } + + @Override + public NonVanillaCustomItemData.Builder textureSize(int textureSize) { + return (NonVanillaCustomItemData.Builder) super.textureSize(textureSize); + } + + @Override + public NonVanillaCustomItemData.Builder renderOffsets(CustomRenderOffsets renderOffsets) { + return (NonVanillaCustomItemData.Builder) super.renderOffsets(renderOffsets); + } + + @Override + public NonVanillaCustomItemData.Builder identifier(@NonNull String identifier) { + this.identifier = identifier; + return this; + } + + @Override + public NonVanillaCustomItemData.Builder javaId(int javaId) { + this.javaId = javaId; + return this; + } + + @Override + public NonVanillaCustomItemData.Builder stackSize(int stackSize) { + this.stackSize = stackSize; + return this; + } + + @Override + public NonVanillaCustomItemData.Builder maxDamage(int maxDamage) { + this.maxDamage = maxDamage; + return this; + } + + @Override + public NonVanillaCustomItemData.Builder toolType(@Nullable String toolType) { + this.toolType = toolType; + return this; + } + + @Override + public NonVanillaCustomItemData.Builder toolTier(@Nullable String toolTier) { + this.toolTier = toolTier; + return this; + } + + @Override + public NonVanillaCustomItemData.Builder armorType(@Nullable String armorType) { + this.armorType = armorType; + return this; + } + + @Override + public NonVanillaCustomItemData.Builder protectionValue(int protectionValue) { + this.protectionValue = protectionValue; + return this; + } + + @Override + public NonVanillaCustomItemData.Builder translationString(@Nullable String translationString) { + this.translationString = translationString; + return this; + } + + @Override + public NonVanillaCustomItemData.Builder repairMaterials(@Nullable Set repairMaterials) { + this.repairMaterials = repairMaterials; + return this; + } + + @Override + public NonVanillaCustomItemData.Builder creativeCategory(int creativeCategory) { + this.creativeCategory = OptionalInt.of(creativeCategory); + return this; + } + + @Override + public NonVanillaCustomItemData.Builder creativeGroup(@Nullable String creativeGroup) { + this.creativeGroup = creativeGroup; + return this; + } + + @Override + public NonVanillaCustomItemData.Builder hat(boolean isHat) { + this.hat = isHat; + return this; + } + + @Override + public NonVanillaCustomItemData.Builder tool(boolean isTool) { + this.tool = isTool; + return this; + } + + @Override + public NonVanillaCustomItemData build() { + if (identifier == null || javaId == -1) { + throw new IllegalArgumentException("Identifier and javaId must be set"); + } + + super.customItemOptions(CustomItemOptions.builder().build()); + super.build(); + return new GeyserNonVanillaCustomItemData(this); + } + } +} diff --git a/core/src/main/java/org/geysermc/geyser/item/components/ToolBreakSpeedsUtils.java b/core/src/main/java/org/geysermc/geyser/item/components/ToolBreakSpeedsUtils.java new file mode 100644 index 000000000..6330043e5 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/item/components/ToolBreakSpeedsUtils.java @@ -0,0 +1,174 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.item.components; + +import com.nukkitx.nbt.NbtMap; +import com.nukkitx.nbt.NbtType; + +import java.util.ArrayList; +import java.util.List; + +public class ToolBreakSpeedsUtils { + public static int toolTierToSpeed(String toolTier) { + ToolTier tier = ToolTier.getByName(toolTier); + if (tier != null) { + return tier.getSpeed(); + } + + return 0; + } + + private static NbtMap createTagBreakSpeed(int speed, String... tags) { + StringBuilder builder = new StringBuilder("query.any_tag('"); + builder.append(tags[0]); + for (int i = 1; i < tags.length; i++) { + builder.append("', '").append(tags[i]); + } + builder.append("')"); + + return NbtMap.builder() + .putCompound("block", NbtMap.builder() + .putString("tags", builder.toString()) + .build()) + .putCompound("on_dig", NbtMap.builder() + .putCompound("condition", NbtMap.builder() + .putString("expression", "") + .putInt("version", -1) + .build()) + .putString("event", "tool_durability") + .putString("target", "self") + .build()) + .putInt("speed", speed) + .build(); + } + + private static NbtMap createBreakSpeed(int speed, String block) { + return NbtMap.builder() + .putCompound("block", NbtMap.builder() + .putString("name", block).build()) + .putCompound("on_dig", NbtMap.builder() + .putCompound("condition", NbtMap.builder() + .putString("expression", "") + .putInt("version", -1) + .build()) + .putString("event", "tool_durability") + .putString("target", "self") + .build()) + .putInt("speed", speed) + .build(); + } + + private static NbtMap createDigger(List speeds) { + return NbtMap.builder() + .putList("destroy_speeds", NbtType.COMPOUND, speeds) + .putCompound("on_dig", NbtMap.builder() + .putCompound("condition", NbtMap.builder() + .putString("expression", "") + .putInt("version", -1) + .build()) + .putString("event", "tool_durability") + .putString("target", "self") + .build()) + .putBoolean("use_efficiency", true) + .build(); + } + + public static NbtMap getAxeDigger(int speed) { + List speeds = new ArrayList<>(); + speeds.add(createTagBreakSpeed(speed, "wood", "pumpkin", "plant")); + + return createDigger(speeds); + } + + public static NbtMap getPickaxeDigger(int speed, String toolTier) { + List speeds = new ArrayList<>(); + if (toolTier.equals(ToolTier.DIAMOND.toString()) || toolTier.equals(ToolTier.NETHERITE.toString())) { + speeds.add(createTagBreakSpeed(speed, "iron_pick_diggable", "diamond_pick_diggable")); + } else { + speeds.add(createTagBreakSpeed(speed, "iron_pick_diggable")); + } + speeds.add(createTagBreakSpeed(speed, "stone", "metal", "rail", "mob_spawner")); + + return createDigger(speeds); + } + + public static NbtMap getShovelDigger(int speed) { + List speeds = new ArrayList<>(); + speeds.add(createTagBreakSpeed(speed, "dirt", "sand", "gravel", "grass", "snow")); + + return createDigger(speeds); + } + + public static NbtMap getSwordDigger(int speed) { + List speeds = new ArrayList<>(); + speeds.add(createBreakSpeed(speed, "minecraft:web")); + speeds.add(createBreakSpeed(speed, "minecraft:bamboo")); + + return createDigger(speeds); + } + + public static NbtMap getHoeDigger(int speed) { + List speeds = new ArrayList<>(); + speeds.add(createBreakSpeed(speed, "minecraft:leaves")); + speeds.add(createBreakSpeed(speed, "minecraft:leaves2")); + speeds.add(createBreakSpeed(speed, "minecraft:azalea_leaves")); + speeds.add(createBreakSpeed(speed, "minecraft:azalea_leaves_flowered")); + + speeds.add(createBreakSpeed(speed, "minecraft:sculk")); + speeds.add(createBreakSpeed(speed, "minecraft:sculk_catalyst")); + speeds.add(createBreakSpeed(speed, "minecraft:sculk_sensor")); + speeds.add(createBreakSpeed(speed, "minecraft:sculk_shrieker")); + speeds.add(createBreakSpeed(speed, "minecraft:sculk_vein")); + + speeds.add(createBreakSpeed(speed, "minecraft:nether_wart_block")); + speeds.add(createBreakSpeed(speed, "minecraft:warped_wart_block")); + + speeds.add(createBreakSpeed(speed, "minecraft:hay_block")); + speeds.add(createBreakSpeed(speed, "minecraft:moss_block")); + speeds.add(createBreakSpeed(speed, "minecraft:shroomlight")); + speeds.add(createBreakSpeed(speed, "minecraft:sponge")); + speeds.add(createBreakSpeed(speed, "minecraft:target")); + + return createDigger(speeds); + } + + public static NbtMap getShearsDigger(int speed) { + List speeds = new ArrayList<>(); + speeds.add(createBreakSpeed(speed, "minecraft:web")); + + speeds.add(createBreakSpeed(speed, "minecraft:leaves")); + speeds.add(createBreakSpeed(speed, "minecraft:leaves2")); + speeds.add(createBreakSpeed(speed, "minecraft:azalea_leaves")); + speeds.add(createBreakSpeed(speed, "minecraft:azalea_leaves_flowered")); + + speeds.add(createBreakSpeed(speed, "minecraft:wool")); + + speeds.add(createBreakSpeed(speed, "minecraft:glow_lichen")); + speeds.add(createBreakSpeed(speed, "minecraft:vine")); + + return createDigger(speeds); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/item/components/ToolTier.java b/core/src/main/java/org/geysermc/geyser/item/components/ToolTier.java new file mode 100644 index 000000000..37e581682 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/item/components/ToolTier.java @@ -0,0 +1,66 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.item.components; + +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.Locale; + +public enum ToolTier { + WOODEN(2), + STONE(4), + IRON(6), + GOLDEN(12), + DIAMOND(8), + NETHERITE(9); + + public static final ToolTier[] VALUES = values(); + + private final int speed; + + ToolTier(int speed) { + this.speed = speed; + } + + public int getSpeed() { + return speed; + } + + @Override + public String toString() { + return this.name().toLowerCase(Locale.ROOT); + } + + public static ToolTier getByName(@NonNull String name) { + String upperCase = name.toUpperCase(Locale.ROOT); + for (ToolTier tier : VALUES) { + if (tier.name().equals(upperCase)) { + return tier; + } + } + return null; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/item/components/WearableSlot.java b/core/src/main/java/org/geysermc/geyser/item/components/WearableSlot.java new file mode 100644 index 000000000..a4479f871 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/item/components/WearableSlot.java @@ -0,0 +1,47 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.item.components; + +import com.nukkitx.nbt.NbtMap; + +import java.util.Locale; + +public enum WearableSlot { + HEAD, + CHEST, + LEGS, + FEET; + + private final NbtMap slotNbt; + + WearableSlot() { + this.slotNbt = NbtMap.builder().putString("slot", "slot.armor." + this.name().toLowerCase(Locale.ROOT)).build(); + } + + public NbtMap getSlotNbt() { + return slotNbt; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/item/exception/InvalidCustomMappingsFileException.java b/core/src/main/java/org/geysermc/geyser/item/exception/InvalidCustomMappingsFileException.java new file mode 100644 index 000000000..5878f5cc7 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/item/exception/InvalidCustomMappingsFileException.java @@ -0,0 +1,40 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.item.exception; + +public class InvalidCustomMappingsFileException extends Exception { + public InvalidCustomMappingsFileException(Throwable cause) { + super(cause); + } + + public InvalidCustomMappingsFileException(String message) { + super(message); + } + + public InvalidCustomMappingsFileException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/item/mappings/MappingsConfigReader.java b/core/src/main/java/org/geysermc/geyser/item/mappings/MappingsConfigReader.java new file mode 100644 index 000000000..eaf07c382 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/item/mappings/MappingsConfigReader.java @@ -0,0 +1,98 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.item.mappings; + +import com.fasterxml.jackson.databind.JsonNode; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.item.custom.CustomItemData; +import org.geysermc.geyser.item.mappings.versions.MappingsReader; +import org.geysermc.geyser.item.mappings.versions.MappingsReader_v1; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.function.BiConsumer; + +public class MappingsConfigReader { + private final Int2ObjectMap mappingReaders = new Int2ObjectOpenHashMap<>(); + private final Path customMappingsDirectory = GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("custom_mappings"); + + public MappingsConfigReader() { + this.mappingReaders.put(1, new MappingsReader_v1()); + } + + public Path[] getCustomMappingsFiles() { + try { + return Files.walk(this.customMappingsDirectory) + .filter(child -> child.toString().endsWith(".json")) + .toArray(Path[]::new); + } catch (IOException e) { + return new Path[0]; + } + } + + public void loadMappingsFromJson(BiConsumer consumer) { + Path customMappingsDirectory = this.customMappingsDirectory; + if (!Files.exists(customMappingsDirectory)) { + try { + Files.createDirectories(customMappingsDirectory); + } catch (IOException e) { + GeyserImpl.getInstance().getLogger().error("Failed to create custom mappings directory", e); + return; + } + } + + Path[] mappingsFiles = this.getCustomMappingsFiles(); + for (Path mappingsFile : mappingsFiles) { + this.readMappingsFromJson(mappingsFile, consumer); + } + } + + public void readMappingsFromJson(Path file, BiConsumer consumer) { + JsonNode mappingsRoot; + try { + mappingsRoot = GeyserImpl.JSON_MAPPER.readTree(file.toFile()); + } catch (IOException e) { + GeyserImpl.getInstance().getLogger().error("Failed to read custom mapping file: " + file, e); + return; + } + + if (!mappingsRoot.has("format_version")) { + GeyserImpl.getInstance().getLogger().error("Mappings file " + file + " is missing the format version field!"); + return; + } + + int formatVersion = mappingsRoot.get("format_version").asInt(); + if (!this.mappingReaders.containsKey(formatVersion)) { + GeyserImpl.getInstance().getLogger().error("Mappings file " + file + " has an unknown format version: " + formatVersion); + return; + } + + this.mappingReaders.get(formatVersion).readMappings(file, mappingsRoot, consumer); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/item/mappings/versions/MappingsReader.java b/core/src/main/java/org/geysermc/geyser/item/mappings/versions/MappingsReader.java new file mode 100644 index 000000000..ef553f488 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/item/mappings/versions/MappingsReader.java @@ -0,0 +1,93 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.item.mappings.versions; + +import com.fasterxml.jackson.databind.JsonNode; +import org.geysermc.geyser.api.item.custom.CustomItemData; +import org.geysermc.geyser.api.item.custom.CustomRenderOffsets; +import org.geysermc.geyser.item.exception.InvalidCustomMappingsFileException; + +import java.nio.file.Path; +import java.util.function.BiConsumer; + +public abstract class MappingsReader { + public abstract void readMappings(Path file, JsonNode mappingsRoot, BiConsumer consumer); + + public abstract CustomItemData readItemMappingEntry(JsonNode node) throws InvalidCustomMappingsFileException; + + protected CustomRenderOffsets fromJsonNode(JsonNode node) { + if (node == null || !node.isObject()) { + return null; + } + + return new CustomRenderOffsets( + getHandOffsets(node, "main_hand"), + getHandOffsets(node, "off_hand") + ); + } + + protected CustomRenderOffsets.Hand getHandOffsets(JsonNode node, String hand) { + JsonNode tmpNode = node.get(hand); + if (tmpNode == null || !tmpNode.isObject()) { + return null; + } + + return new CustomRenderOffsets.Hand( + getPerspectiveOffsets(tmpNode, "first_person"), + getPerspectiveOffsets(tmpNode, "third_person") + ); + } + + protected CustomRenderOffsets.Offset getPerspectiveOffsets(JsonNode node, String perspective) { + JsonNode tmpNode = node.get(perspective); + if (tmpNode == null || !tmpNode.isObject()) { + return null; + } + + return new CustomRenderOffsets.Offset( + getOffsetXYZ(tmpNode, "position"), + getOffsetXYZ(tmpNode, "rotation"), + getOffsetXYZ(tmpNode, "scale") + ); + } + + protected CustomRenderOffsets.OffsetXYZ getOffsetXYZ(JsonNode node, String offsetType) { + JsonNode tmpNode = node.get(offsetType); + if (tmpNode == null || !tmpNode.isObject()) { + return null; + } + + if (!tmpNode.has("x") || !tmpNode.has("y") || !tmpNode.has("z")) { + return null; + } + + return new CustomRenderOffsets.OffsetXYZ( + tmpNode.get("x").floatValue(), + tmpNode.get("y").floatValue(), + tmpNode.get("z").floatValue() + ); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/item/mappings/versions/MappingsReader_v1.java b/core/src/main/java/org/geysermc/geyser/item/mappings/versions/MappingsReader_v1.java new file mode 100644 index 000000000..217ff844e --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/item/mappings/versions/MappingsReader_v1.java @@ -0,0 +1,123 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.item.mappings.versions; + +import com.fasterxml.jackson.databind.JsonNode; +import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.item.custom.CustomItemData; +import org.geysermc.geyser.api.item.custom.CustomItemOptions; +import org.geysermc.geyser.item.exception.InvalidCustomMappingsFileException; + +import java.nio.file.Path; +import java.util.function.BiConsumer; + +public class MappingsReader_v1 extends MappingsReader { + @Override + public void readMappings(Path file, JsonNode mappingsRoot, BiConsumer consumer) { + this.readItemMappings(file, mappingsRoot, consumer); + } + + public void readItemMappings(Path file, JsonNode mappingsRoot, BiConsumer consumer) { + JsonNode itemsNode = mappingsRoot.get("items"); + + if (itemsNode != null && itemsNode.isObject()) { + itemsNode.fields().forEachRemaining(entry -> { + if (entry.getValue().isArray()) { + entry.getValue().forEach(data -> { + try { + CustomItemData customItemData = this.readItemMappingEntry(data); + consumer.accept(entry.getKey(), customItemData); + } catch (InvalidCustomMappingsFileException e) { + GeyserImpl.getInstance().getLogger().error("Error in custom mapping file: " + file.toString(), e); + } + }); + } + }); + } + } + + private CustomItemOptions readItemCustomItemOptions(JsonNode node) { + CustomItemOptions.Builder customItemOptions = CustomItemOptions.builder(); + + JsonNode customModelData = node.get("custom_model_data"); + if (customModelData != null && customModelData.isInt()) { + customItemOptions.customModelData(customModelData.asInt()); + } + + JsonNode damagePredicate = node.get("damage_predicate"); + if (damagePredicate != null && damagePredicate.isInt()) { + customItemOptions.damagePredicate(damagePredicate.asInt()); + } + + JsonNode unbreakable = node.get("unbreakable"); + if (unbreakable != null && unbreakable.isBoolean()) { + customItemOptions.unbreakable(unbreakable.asBoolean()); + } + + return customItemOptions.build(); + } + + @Override + public CustomItemData readItemMappingEntry(JsonNode node) throws InvalidCustomMappingsFileException { + if (node == null || !node.isObject()) { + throw new InvalidCustomMappingsFileException("Invalid item mappings entry"); + } + + String name = node.get("name").asText(); + if (name == null || name.isEmpty()) { + throw new InvalidCustomMappingsFileException("An item entry has no name"); + } + + CustomItemData.Builder customItemData = CustomItemData.builder() + .name(name) + .customItemOptions(this.readItemCustomItemOptions(node)); + + //The next entries are optional + if (node.has("display_name")) { + customItemData.displayName(node.get("display_name").asText()); + } + + if (node.has("icon")) { + customItemData.icon(node.get("icon").asText()); + } + + if (node.has("allow_offhand")) { + customItemData.allowOffhand(node.get("allow_offhand").asBoolean()); + } + + if (node.has("texture_size")) { + customItemData.textureSize(node.get("texture_size").asInt()); + } + + if (node.has("render_offsets")) { + JsonNode tmpNode = node.get("render_offsets"); + + customItemData.renderOffsets(fromJsonNode(tmpNode)); + } + + return customItemData.build(); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/level/block/BlockStateValues.java b/core/src/main/java/org/geysermc/geyser/level/block/BlockStateValues.java index e4772df80..58cbce77f 100644 --- a/core/src/main/java/org/geysermc/geyser/level/block/BlockStateValues.java +++ b/core/src/main/java/org/geysermc/geyser/level/block/BlockStateValues.java @@ -29,11 +29,11 @@ import com.fasterxml.jackson.databind.JsonNode; import it.unimi.dsi.fastutil.ints.*; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; -import org.geysermc.geyser.translator.level.block.entity.PistonBlockEntityTranslator; -import org.geysermc.geyser.registry.BlockRegistries; -import org.geysermc.geyser.registry.type.BlockMapping; import org.geysermc.geyser.level.physics.Direction; import org.geysermc.geyser.level.physics.PistonBehavior; +import org.geysermc.geyser.registry.BlockRegistries; +import org.geysermc.geyser.registry.type.BlockMapping; +import org.geysermc.geyser.translator.level.block.entity.PistonBlockEntityTranslator; import org.geysermc.geyser.util.collection.FixedInt2ByteMap; import org.geysermc.geyser.util.collection.FixedInt2IntMap; import org.geysermc.geyser.util.collection.LecternHasBookMap; diff --git a/core/src/main/java/org/geysermc/geyser/level/physics/BoundingBox.java b/core/src/main/java/org/geysermc/geyser/level/physics/BoundingBox.java index 108982a32..d6913d6c0 100644 --- a/core/src/main/java/org/geysermc/geyser/level/physics/BoundingBox.java +++ b/core/src/main/java/org/geysermc/geyser/level/physics/BoundingBox.java @@ -26,7 +26,9 @@ package org.geysermc.geyser.level.physics; import com.nukkitx.math.vector.Vector3d; -import lombok.*; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.SneakyThrows; @Data @AllArgsConstructor diff --git a/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java b/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java index a91c2b083..2a830cd70 100644 --- a/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java +++ b/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java @@ -33,15 +33,15 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket; import lombok.Getter; import lombok.Setter; -import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.EntityDefinitions; +import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.type.player.PlayerEntity; +import org.geysermc.geyser.level.block.BlockPositionIterator; +import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.PistonCache; import org.geysermc.geyser.translator.collision.BlockCollision; import org.geysermc.geyser.translator.collision.ScaffoldingCollision; -import org.geysermc.geyser.level.block.BlockStateValues; -import org.geysermc.geyser.level.block.BlockPositionIterator; import org.geysermc.geyser.util.BlockUtils; import java.text.DecimalFormat; diff --git a/core/src/main/java/org/geysermc/geyser/network/ConnectorServerEventHandler.java b/core/src/main/java/org/geysermc/geyser/network/ConnectorServerEventHandler.java index 1c6f9db88..c9a3201c1 100644 --- a/core/src/main/java/org/geysermc/geyser/network/ConnectorServerEventHandler.java +++ b/core/src/main/java/org/geysermc/geyser/network/ConnectorServerEventHandler.java @@ -28,18 +28,19 @@ package org.geysermc.geyser.network; import com.nukkitx.protocol.bedrock.BedrockPong; import com.nukkitx.protocol.bedrock.BedrockServerEventHandler; import com.nukkitx.protocol.bedrock.BedrockServerSession; +import com.nukkitx.protocol.bedrock.v554.Bedrock_v554; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.DefaultEventLoopGroup; import io.netty.channel.socket.DatagramPacket; import io.netty.util.concurrent.DefaultThreadFactory; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.ping.GeyserPingInfo; import org.geysermc.geyser.configuration.GeyserConfiguration; -import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.translator.text.MessageTranslator; +import org.geysermc.geyser.ping.GeyserPingInfo; import org.geysermc.geyser.ping.IGeyserPingPassthrough; +import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.GeyserLocale; +import org.geysermc.geyser.translator.text.MessageTranslator; import javax.annotation.Nonnull; import java.net.InetSocketAddress; @@ -52,7 +53,7 @@ public class ConnectorServerEventHandler implements BedrockServerEventHandler { /* The following constants are all used to ensure the ping does not reach a length where it is unparsable by the Bedrock client */ - private static final int MINECRAFT_VERSION_BYTES_LENGTH = MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion().getBytes(StandardCharsets.UTF_8).length; + private static final int MINECRAFT_VERSION_BYTES_LENGTH = GameProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion().getBytes(StandardCharsets.UTF_8).length; private static final int BRAND_BYTES_LENGTH = GeyserImpl.NAME.getBytes(StandardCharsets.UTF_8).length; /** * The MOTD, sub-MOTD and Minecraft version ({@link #MINECRAFT_VERSION_BYTES_LENGTH}) combined cannot reach this length. @@ -108,9 +109,9 @@ public class ConnectorServerEventHandler implements BedrockServerEventHandler { pong.setEdition("MCPE"); pong.setGameType("Survival"); // Can only be Survival or Creative as of 1.16.210.59 pong.setNintendoLimited(false); - pong.setProtocolVersion(MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()); - pong.setVersion(MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion()); // Required to not be empty as of 1.16.210.59. Can only contain . and numbers. - pong.setIpv4Port(config.getBedrock().getPort()); + pong.setProtocolVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()); + pong.setVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion()); // Required to not be empty as of 1.16.210.59. Can only contain . and numbers. + pong.setIpv4Port(config.getBedrock().port()); if (config.isPassthroughMotd() && pingInfo != null && pingInfo.getDescription() != null) { String[] motd = MessageTranslator.convertMessageLenient(pingInfo.getDescription()).split("\n"); @@ -120,8 +121,8 @@ public class ConnectorServerEventHandler implements BedrockServerEventHandler { pong.setMotd(mainMotd.trim()); pong.setSubMotd(subMotd.trim()); // Trimmed to shift it to the left, prevents the universe from collapsing on us just because we went 2 characters over the text box's limit. } else { - pong.setMotd(config.getBedrock().getMotd1()); - pong.setSubMotd(config.getBedrock().getMotd2()); + pong.setMotd(config.getBedrock().primaryMotd()); + pong.setSubMotd(config.getBedrock().secondaryMotd()); } if (config.isPassthroughPlayerCounts() && pingInfo != null) { @@ -171,7 +172,7 @@ public class ConnectorServerEventHandler implements BedrockServerEventHandler { @Override public void onSessionCreation(@Nonnull BedrockServerSession bedrockServerSession) { try { - bedrockServerSession.setPacketCodec(MinecraftProtocol.DEFAULT_BEDROCK_CODEC); + bedrockServerSession.setPacketCodec(Bedrock_v554.V554_CODEC); // Has the RequestNetworkSettingsPacket bedrockServerSession.setLogging(true); bedrockServerSession.setCompressionLevel(geyser.getConfig().getBedrock().getCompressionLevel()); bedrockServerSession.setPacketHandler(new UpstreamPacketHandler(geyser, new GeyserSession(geyser, bedrockServerSession, eventLoopGroup.next()))); diff --git a/core/src/main/java/org/geysermc/geyser/network/MinecraftProtocol.java b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java similarity index 89% rename from core/src/main/java/org/geysermc/geyser/network/MinecraftProtocol.java rename to core/src/main/java/org/geysermc/geyser/network/GameProtocol.java index cec5c5ce6..f31d800c7 100644 --- a/core/src/main/java/org/geysermc/geyser/network/MinecraftProtocol.java +++ b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java @@ -31,6 +31,7 @@ import com.nukkitx.protocol.bedrock.BedrockPacketCodec; import com.nukkitx.protocol.bedrock.v527.Bedrock_v527; import com.nukkitx.protocol.bedrock.v534.Bedrock_v534; import com.nukkitx.protocol.bedrock.v544.Bedrock_v544; +import com.nukkitx.protocol.bedrock.v554.Bedrock_v554; import org.geysermc.geyser.session.GeyserSession; import java.util.ArrayList; @@ -40,7 +41,7 @@ import java.util.StringJoiner; /** * Contains information about the supported protocols in Geyser. */ -public final class MinecraftProtocol { +public final class GameProtocol { /** * Default Bedrock codec that should act as a fallback. Should represent the latest available * release of the game that Geyser supports. @@ -71,6 +72,7 @@ public final class MinecraftProtocol { SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC.toBuilder() .minecraftVersion("1.19.21/1.19.22") .build()); + SUPPORTED_BEDROCK_CODECS.add(Bedrock_v554.V554_CODEC); } /** @@ -93,6 +95,10 @@ public final class MinecraftProtocol { return session.getUpstream().getProtocolVersion() >= Bedrock_v534.V534_CODEC.getProtocolVersion(); } + public static boolean supports1_19_30(GeyserSession session) { + return session.getUpstream().getProtocolVersion() >= Bedrock_v554.V554_CODEC.getProtocolVersion(); + } + /** * Gets the {@link PacketCodec} for Minecraft: Java Edition. * @@ -120,6 +126,15 @@ public final class MinecraftProtocol { return DEFAULT_JAVA_CODEC.getProtocolVersion(); } + /** + * Gets the supported Minecraft: Java Edition version. + * + * @return the supported Minecraft: Java Edition version + */ + public static String getJavaMinecraftVersion() { + return DEFAULT_JAVA_CODEC.getMinecraftVersion(); + } + /** * @return a string showing all supported Bedrock versions for this Geyser instance */ @@ -144,6 +159,6 @@ public final class MinecraftProtocol { return joiner.toString(); } - private MinecraftProtocol() { + private GameProtocol() { } } diff --git a/core/src/main/java/org/geysermc/geyser/network/LoggingPacketHandler.java b/core/src/main/java/org/geysermc/geyser/network/LoggingPacketHandler.java index b0b707ee0..8d2db081a 100644 --- a/core/src/main/java/org/geysermc/geyser/network/LoggingPacketHandler.java +++ b/core/src/main/java/org/geysermc/geyser/network/LoggingPacketHandler.java @@ -856,4 +856,18 @@ public class LoggingPacketHandler implements BedrockPacketHandler { public boolean handle(FilterTextPacket packet) { return defaultHandler(packet); } + + // 1.19.0 new packet + + @Override + public boolean handle(RequestAbilityPacket packet) { + return defaultHandler(packet); + } + + // 1.19.30 new packet + + @Override + public boolean handle(RequestNetworkSettingsPacket packet) { + return defaultHandler(packet); + } } \ No newline at end of file diff --git a/core/src/main/java/org/geysermc/geyser/network/QueryPacketHandler.java b/core/src/main/java/org/geysermc/geyser/network/QueryPacketHandler.java index 0caa6fac7..d7daa9260 100644 --- a/core/src/main/java/org/geysermc/geyser/network/QueryPacketHandler.java +++ b/core/src/main/java/org/geysermc/geyser/network/QueryPacketHandler.java @@ -153,7 +153,7 @@ public class QueryPacketHandler { String[] javaMotd = MessageTranslator.convertMessageLenient(pingInfo.getDescription()).split("\n"); motd = javaMotd[0].trim(); // First line of the motd. } else { - motd = geyser.getConfig().getBedrock().getMotd1(); + motd = geyser.getConfig().getBedrock().primaryMotd(); } // If passthrough player counts is enabled lets get players from the server @@ -177,13 +177,13 @@ public class QueryPacketHandler { gameData.put("hostname", motd); gameData.put("gametype", "SMP"); gameData.put("game_id", "MINECRAFT"); - gameData.put("version", GeyserImpl.NAME + " (" + GeyserImpl.GIT_VERSION + ") " + MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion()); + gameData.put("version", GeyserImpl.NAME + " (" + GeyserImpl.GIT_VERSION + ") " + GameProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion()); gameData.put("plugins", ""); gameData.put("map", map); gameData.put("numplayers", currentPlayerCount); gameData.put("maxplayers", maxPlayerCount); - gameData.put("hostport", String.valueOf(geyser.getConfig().getBedrock().getPort())); - gameData.put("hostip", geyser.getConfig().getBedrock().getAddress()); + gameData.put("hostport", String.valueOf(geyser.getConfig().getBedrock().port())); + gameData.put("hostip", geyser.getConfig().getBedrock().address()); try { writeString(query, "GeyserMC"); diff --git a/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java b/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java index b4c4ae471..c2a91fd75 100644 --- a/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java +++ b/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java @@ -28,9 +28,11 @@ package org.geysermc.geyser.network; import com.nukkitx.protocol.bedrock.BedrockPacket; import com.nukkitx.protocol.bedrock.BedrockPacketCodec; import com.nukkitx.protocol.bedrock.data.ExperimentData; +import com.nukkitx.protocol.bedrock.data.PacketCompressionAlgorithm; import com.nukkitx.protocol.bedrock.data.ResourcePackType; import com.nukkitx.protocol.bedrock.packet.*; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.pack.ResourcePack; import org.geysermc.geyser.pack.ResourcePackManifest; @@ -38,7 +40,6 @@ import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.PendingMicrosoftAuthentication; -import org.geysermc.geyser.session.auth.AuthType; import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.util.LoginEncryptionUtils; import org.geysermc.geyser.util.MathUtils; @@ -61,6 +62,46 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { return translateAndDefault(packet); } + private boolean newProtocol = false; // TEMPORARY + + private boolean setCorrectCodec(int protocolVersion) { + BedrockPacketCodec packetCodec = GameProtocol.getBedrockCodec(protocolVersion); + if (packetCodec == null) { + String supportedVersions = GameProtocol.getAllSupportedBedrockVersions(); + if (protocolVersion > GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) { + // Too early to determine session locale + session.disconnect(GeyserLocale.getLocaleStringLog("geyser.network.outdated.server", supportedVersions)); + return false; + } else if (protocolVersion < GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) { + session.disconnect(GeyserLocale.getLocaleStringLog("geyser.network.outdated.client", supportedVersions)); + return false; + } + } + + session.getUpstream().getSession().setPacketCodec(packetCodec); + return true; + } + + @Override + public boolean handle(RequestNetworkSettingsPacket packet) { + if (setCorrectCodec(packet.getProtocolVersion())) { + newProtocol = true; + } else { + return true; + } + + // New since 1.19.30 - sent before login packet + PacketCompressionAlgorithm algorithm = PacketCompressionAlgorithm.ZLIB; + + NetworkSettingsPacket responsePacket = new NetworkSettingsPacket(); + responsePacket.setCompressionAlgorithm(algorithm); + responsePacket.setCompressionThreshold(512); + session.sendUpstreamPacketImmediately(responsePacket); + + session.getUpstream().getSession().setCompression(algorithm); + return true; + } + @Override public boolean handle(LoginPacket loginPacket) { if (geyser.isShuttingDown()) { @@ -69,21 +110,12 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { return true; } - BedrockPacketCodec packetCodec = MinecraftProtocol.getBedrockCodec(loginPacket.getProtocolVersion()); - if (packetCodec == null) { - String supportedVersions = MinecraftProtocol.getAllSupportedBedrockVersions(); - if (loginPacket.getProtocolVersion() > MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) { - // Too early to determine session locale - session.disconnect(GeyserLocale.getLocaleStringLog("geyser.network.outdated.server", supportedVersions)); - return true; - } else if (loginPacket.getProtocolVersion() < MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) { - session.disconnect(GeyserLocale.getLocaleStringLog("geyser.network.outdated.client", supportedVersions)); + if (!newProtocol) { + if (!setCorrectCodec(loginPacket.getProtocolVersion())) { // REMOVE WHEN ONLY 1.19.30 IS SUPPORTED OR 1.20 return true; } } - session.getUpstream().getSession().setPacketCodec(packetCodec); - // Set the block translation based off of version session.setBlockMappings(BlockRegistries.BLOCKS.forVersion(loginPacket.getProtocolVersion())); session.setItemMappings(Registries.ITEMS.forVersion(loginPacket.getProtocolVersion())); @@ -111,7 +143,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { resourcePacksInfo.setForcedToAccept(GeyserImpl.getInstance().getConfig().isForceResourcePacks()); session.sendUpstreamPacket(resourcePacksInfo); - GeyserLocale.loadGeyserLocale(session.getLocale()); + GeyserLocale.loadGeyserLocale(session.locale()); return true; } @@ -119,7 +151,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { public boolean handle(ResourcePackClientResponsePacket packet) { switch (packet.getStatus()) { case COMPLETED: - if (geyser.getConfig().getRemote().getAuthType() != AuthType.ONLINE) { + if (geyser.getConfig().getRemote().authType() != AuthType.ONLINE) { session.authenticate(session.getAuthData().name()); } else if (!couldLoginUserByName(session.getAuthData().name())) { // We must spawn the white world @@ -160,7 +192,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { stackPacket.getResourcePacks().add(new ResourcePackStackPacket.Entry(header.getUuid().toString(), header.getVersionString(), "")); } - if (session.getItemMappings().getFurnaceMinecartData() != null) { + if (GeyserImpl.getInstance().getConfig().isAddNonBedrockItems()) { // Allow custom items to work stackPacket.getExperiments().add(new ExperimentData("data_driven_items", true)); } @@ -216,7 +248,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { if (session.isLoggingIn()) { SetTitlePacket titlePacket = new SetTitlePacket(); titlePacket.setType(SetTitlePacket.Type.ACTIONBAR); - titlePacket.setText(GeyserLocale.getPlayerLocaleString("geyser.auth.login.wait", session.getLocale())); + titlePacket.setText(GeyserLocale.getPlayerLocaleString("geyser.auth.login.wait", session.locale())); titlePacket.setFadeInTime(0); titlePacket.setFadeOutTime(1); titlePacket.setStayTime(2); diff --git a/core/src/main/java/org/geysermc/geyser/network/netty/LocalSession.java b/core/src/main/java/org/geysermc/geyser/network/netty/LocalSession.java index 0781a04b2..370604db9 100644 --- a/core/src/main/java/org/geysermc/geyser/network/netty/LocalSession.java +++ b/core/src/main/java/org/geysermc/geyser/network/netty/LocalSession.java @@ -28,7 +28,9 @@ package org.geysermc.geyser.network.netty; import com.github.steveice10.packetlib.BuiltinFlags; import com.github.steveice10.packetlib.codec.PacketCodecHelper; import com.github.steveice10.packetlib.packet.PacketProtocol; -import com.github.steveice10.packetlib.tcp.*; +import com.github.steveice10.packetlib.tcp.TcpPacketCodec; +import com.github.steveice10.packetlib.tcp.TcpPacketSizer; +import com.github.steveice10.packetlib.tcp.TcpSession; import io.netty.bootstrap.Bootstrap; import io.netty.buffer.ByteBufAllocator; import io.netty.channel.*; diff --git a/core/src/main/java/org/geysermc/geyser/pack/ResourcePack.java b/core/src/main/java/org/geysermc/geyser/pack/ResourcePack.java index c0913f31c..6df1a0c0e 100644 --- a/core/src/main/java/org/geysermc/geyser/pack/ResourcePack.java +++ b/core/src/main/java/org/geysermc/geyser/pack/ResourcePack.java @@ -25,21 +25,25 @@ package org.geysermc.geyser.pack; +import lombok.Getter; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.event.lifecycle.GeyserLoadResourcePacksEvent; import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.util.FileUtils; import java.io.File; +import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.Path; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; -import lombok.Getter; - /** * This represents a resource pack and all the data relevant to it */ @@ -66,16 +70,33 @@ public class ResourcePack { * Loop through the packs directory and locate valid resource pack files */ public static void loadPacks() { - File directory = GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("packs").toFile(); + Path directory = GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("packs"); - if (!directory.exists()) { - directory.mkdir(); + if (!Files.exists(directory)) { + try { + Files.createDirectory(directory); + } catch (IOException e) { + GeyserImpl.getInstance().getLogger().error("Could not create packs directory", e); + } // As we just created the directory it will be empty return; } - for (File file : directory.listFiles()) { + List resourcePacks; + try { + resourcePacks = Files.walk(directory).collect(Collectors.toList()); + } catch (IOException e) { + GeyserImpl.getInstance().getLogger().error("Could not list packs directory", e); + return; + } + + GeyserLoadResourcePacksEvent event = new GeyserLoadResourcePacksEvent(resourcePacks); + GeyserImpl.getInstance().eventBus().fire(event); + + for (Path path : event.resourcePacks()) { + File file = path.toFile(); + if (file.getName().endsWith(".zip") || file.getName().endsWith(".mcpack")) { ResourcePack pack = new ResourcePack(); diff --git a/core/src/main/java/org/geysermc/geyser/ping/GeyserLegacyPingPassthrough.java b/core/src/main/java/org/geysermc/geyser/ping/GeyserLegacyPingPassthrough.java index 7db571be0..a69d9bc3e 100644 --- a/core/src/main/java/org/geysermc/geyser/ping/GeyserLegacyPingPassthrough.java +++ b/core/src/main/java/org/geysermc/geyser/ping/GeyserLegacyPingPassthrough.java @@ -32,7 +32,7 @@ import io.netty.handler.codec.haproxy.HAProxyCommand; import io.netty.handler.codec.haproxy.HAProxyProxiedProtocol; import io.netty.util.NetUtil; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.network.MinecraftProtocol; +import org.geysermc.geyser.network.GameProtocol; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; @@ -77,14 +77,14 @@ public class GeyserLegacyPingPassthrough implements IGeyserPingPassthrough, Runn @Override public void run() { try (Socket socket = new Socket()) { - String address = geyser.getConfig().getRemote().getAddress(); - int port = geyser.getConfig().getRemote().getPort(); + String address = geyser.getConfig().getRemote().address(); + int port = geyser.getConfig().getRemote().port(); socket.connect(new InetSocketAddress(address, port), 5000); ByteArrayOutputStream byteArrayStream = new ByteArrayOutputStream(); try (DataOutputStream handshake = new DataOutputStream(byteArrayStream)) { handshake.write(0x0); - VarInts.writeUnsignedInt(handshake, MinecraftProtocol.getJavaProtocolVersion()); + VarInts.writeUnsignedInt(handshake, GameProtocol.getJavaProtocolVersion()); VarInts.writeUnsignedInt(handshake, address.length()); handshake.writeBytes(address); handshake.writeShort(port); diff --git a/core/src/main/java/org/geysermc/geyser/registry/PacketTranslatorRegistry.java b/core/src/main/java/org/geysermc/geyser/registry/PacketTranslatorRegistry.java index 3f7d88031..bf412bfaf 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/PacketTranslatorRegistry.java +++ b/core/src/main/java/org/geysermc/geyser/registry/PacketTranslatorRegistry.java @@ -31,10 +31,10 @@ import com.nukkitx.protocol.bedrock.BedrockPacket; import io.netty.channel.EventLoop; import org.geysermc.common.PlatformType; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.registry.loader.RegistryLoaders; -import org.geysermc.geyser.translator.protocol.PacketTranslator; +import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.GeyserLocale; +import org.geysermc.geyser.translator.protocol.PacketTranslator; import java.util.Collections; import java.util.IdentityHashMap; diff --git a/core/src/main/java/org/geysermc/geyser/registry/Registries.java b/core/src/main/java/org/geysermc/geyser/registry/Registries.java index 8f2a9775a..4b361ba4f 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/Registries.java +++ b/core/src/main/java/org/geysermc/geyser/registry/Registries.java @@ -47,6 +47,7 @@ import org.geysermc.geyser.registry.loader.*; import org.geysermc.geyser.registry.populator.ItemRegistryPopulator; import org.geysermc.geyser.registry.populator.PacketRegistryPopulator; import org.geysermc.geyser.registry.populator.RecipeRegistryPopulator; +import org.geysermc.geyser.registry.provider.ProviderSupplier; import org.geysermc.geyser.registry.type.EnchantmentData; import org.geysermc.geyser.registry.type.ItemMappings; import org.geysermc.geyser.registry.type.ParticleMapping; @@ -57,10 +58,7 @@ import org.geysermc.geyser.translator.level.event.LevelEventTranslator; import org.geysermc.geyser.translator.sound.SoundInteractionTranslator; import org.geysermc.geyser.translator.sound.SoundTranslator; -import java.util.EnumMap; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; /** * Holds all the common registries in Geyser. @@ -138,6 +136,11 @@ public final class Registries { */ public static final SimpleRegistry> POTION_MIXES; + /** + * A registry holding all the + */ + public static final SimpleMappedRegistry, ProviderSupplier> PROVIDERS = SimpleMappedRegistry.create(new IdentityHashMap<>(), ProviderRegistryLoader::new); + /** * A versioned registry holding all the recipes, with the net ID being the key, and {@link GeyserRecipe} as the value. */ diff --git a/core/src/main/java/org/geysermc/geyser/registry/loader/CollisionRegistryLoader.java b/core/src/main/java/org/geysermc/geyser/registry/loader/CollisionRegistryLoader.java index b74573a4e..69ad16743 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/loader/CollisionRegistryLoader.java +++ b/core/src/main/java/org/geysermc/geyser/registry/loader/CollisionRegistryLoader.java @@ -34,12 +34,12 @@ import it.unimi.dsi.fastutil.objects.ObjectArrayList; import lombok.AllArgsConstructor; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.level.physics.BoundingBox; -import org.geysermc.geyser.translator.collision.CollisionRemapper; -import org.geysermc.geyser.translator.collision.BlockCollision; -import org.geysermc.geyser.translator.collision.OtherCollision; -import org.geysermc.geyser.translator.collision.SolidCollision; import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.type.BlockMapping; +import org.geysermc.geyser.translator.collision.BlockCollision; +import org.geysermc.geyser.translator.collision.CollisionRemapper; +import org.geysermc.geyser.translator.collision.OtherCollision; +import org.geysermc.geyser.translator.collision.SolidCollision; import org.geysermc.geyser.util.FileUtils; import java.io.InputStream; diff --git a/core/src/main/java/org/geysermc/geyser/registry/loader/EnchantmentRegistryLoader.java b/core/src/main/java/org/geysermc/geyser/registry/loader/EnchantmentRegistryLoader.java index e566ff37c..8ad09bf88 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/loader/EnchantmentRegistryLoader.java +++ b/core/src/main/java/org/geysermc/geyser/registry/loader/EnchantmentRegistryLoader.java @@ -30,7 +30,7 @@ import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.inventory.item.Enchantment.JavaEnchantment; -import org.geysermc.geyser.network.MinecraftProtocol; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.type.EnchantmentData; import org.geysermc.geyser.registry.type.ItemMapping; @@ -77,7 +77,7 @@ public class EnchantmentRegistryLoader implements RegistryLoader, ProviderSupplier>, Map, ProviderSupplier>> { + + @Override + public Map, ProviderSupplier> load(Map, ProviderSupplier> providers) { + providers.put(Command.Builder.class, args -> new GeyserCommandManager.CommandBuilder<>((Extension) args[0])); + providers.put(CustomItemData.Builder.class, args -> new GeyserCustomItemData.CustomItemDataBuilder()); + providers.put(CustomItemOptions.Builder.class, args -> new GeyserCustomItemOptions.CustomItemOptionsBuilder()); + providers.put(NonVanillaCustomItemData.Builder.class, args -> new GeyserNonVanillaCustomItemData.NonVanillaCustomItemDataBuilder()); + providers.put(EventRegistrar.class, args -> new GeyserEventRegistrar(args[0])); + + return providers; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/registry/loader/SoundTranslatorRegistryLoader.java b/core/src/main/java/org/geysermc/geyser/registry/loader/SoundTranslatorRegistryLoader.java index 359cd112e..558864b35 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/loader/SoundTranslatorRegistryLoader.java +++ b/core/src/main/java/org/geysermc/geyser/registry/loader/SoundTranslatorRegistryLoader.java @@ -25,8 +25,8 @@ package org.geysermc.geyser.registry.loader; -import org.geysermc.geyser.translator.sound.SoundTranslator; import org.geysermc.geyser.translator.sound.SoundInteractionTranslator; +import org.geysermc.geyser.translator.sound.SoundTranslator; import java.util.function.Function; diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/CustomItemRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/CustomItemRegistryPopulator.java new file mode 100644 index 000000000..64543272e --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/CustomItemRegistryPopulator.java @@ -0,0 +1,360 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.registry.populator; + +import com.nukkitx.nbt.NbtMap; +import com.nukkitx.nbt.NbtMapBuilder; +import com.nukkitx.nbt.NbtType; +import com.nukkitx.protocol.bedrock.data.inventory.ComponentItemData; +import com.nukkitx.protocol.bedrock.packet.StartGamePacket; +import it.unimi.dsi.fastutil.objects.Object2IntMaps; +import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.item.custom.CustomItemData; +import org.geysermc.geyser.api.item.custom.CustomRenderOffsets; +import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData; +import org.geysermc.geyser.item.GeyserCustomMappingData; +import org.geysermc.geyser.item.components.ToolBreakSpeedsUtils; +import org.geysermc.geyser.item.components.WearableSlot; +import org.geysermc.geyser.registry.type.GeyserMappingItem; +import org.geysermc.geyser.registry.type.ItemMapping; +import org.geysermc.geyser.registry.type.NonVanillaItemRegistration; + +import javax.annotation.Nullable; +import java.util.List; +import java.util.Map; +import java.util.OptionalInt; + +public class CustomItemRegistryPopulator { + public static GeyserCustomMappingData registerCustomItem(String customItemName, GeyserMappingItem javaItem, CustomItemData customItemData, int bedrockId) { + StartGamePacket.ItemEntry startGamePacketItemEntry = new StartGamePacket.ItemEntry(customItemName, (short) bedrockId, true); + + NbtMapBuilder builder = createComponentNbt(customItemData, javaItem, customItemName, bedrockId); + ComponentItemData componentItemData = new ComponentItemData(customItemName, builder.build()); + + return new GeyserCustomMappingData(componentItemData, startGamePacketItemEntry, customItemName, bedrockId); + } + + static boolean initialCheck(String identifier, CustomItemData item, Map mappings) { + if (!mappings.containsKey(identifier)) { + GeyserImpl.getInstance().getLogger().error("Could not find the Java item to add custom item properties to for " + item.name()); + return false; + } + if (!item.customItemOptions().hasCustomItemOptions()) { + GeyserImpl.getInstance().getLogger().error("The custom item " + item.name() + " has no registration types"); + } + return true; + } + + public static NonVanillaItemRegistration registerCustomItem(NonVanillaCustomItemData customItemData, int customItemId) { + String customIdentifier = customItemData.identifier(); + + ItemMapping customItemMapping = ItemMapping.builder() + .javaIdentifier(customIdentifier) + .bedrockIdentifier(customIdentifier) + .javaId(customItemData.javaId()) + .bedrockId(customItemId) + .bedrockData(0) + .bedrockBlockId(0) + .stackSize(customItemData.stackSize()) + .toolType(customItemData.toolType()) + .toolTier(customItemData.toolTier()) + .translationString(customItemData.translationString()) + .maxDamage(customItemData.maxDamage()) + .repairMaterials(customItemData.repairMaterials()) + .hasSuspiciousStewEffect(false) + .customItemOptions(Object2IntMaps.emptyMap()) + .build(); + + NbtMapBuilder builder = createComponentNbt(customItemData, customItemData.identifier(), customItemId, + customItemData.creativeCategory(), customItemData.creativeGroup(), customItemData.isHat(), customItemData.isTool()); + ComponentItemData componentItemData = new ComponentItemData(customIdentifier, builder.build()); + + return new NonVanillaItemRegistration(componentItemData, customItemMapping); + } + + private static NbtMapBuilder createComponentNbt(CustomItemData customItemData, GeyserMappingItem mapping, + String customItemName, int customItemId) { + NbtMapBuilder builder = NbtMap.builder(); + builder.putString("name", customItemName) + .putInt("id", customItemId); + + NbtMapBuilder itemProperties = NbtMap.builder(); + NbtMapBuilder componentBuilder = NbtMap.builder(); + + setupBasicItemInfo(mapping.getMaxDamage(), mapping.getStackSize(), mapping.getToolType() != null, customItemData, itemProperties, componentBuilder); + + boolean canDestroyInCreative = true; + if (mapping.getToolType() != null) { // This is not using the isTool boolean because it is not just a render type here. + canDestroyInCreative = computeToolProperties(mapping.getToolTier(), mapping.getToolType(), itemProperties, componentBuilder); + } + itemProperties.putBoolean("can_destroy_in_creative", canDestroyInCreative); + + if (mapping.getArmorType() != null) { + computeArmorProperties(mapping.getArmorType(), mapping.getProtectionValue(), componentBuilder); + } + + computeRenderOffsets(false, customItemData, componentBuilder); + + componentBuilder.putCompound("item_properties", itemProperties.build()); + builder.putCompound("components", componentBuilder.build()); + + return builder; + } + + private static NbtMapBuilder createComponentNbt(NonVanillaCustomItemData customItemData, String customItemName, + int customItemId, OptionalInt creativeCategory, + String creativeGroup, boolean isHat, boolean isTool) { + NbtMapBuilder builder = NbtMap.builder(); + builder.putString("name", customItemName) + .putInt("id", customItemId); + + NbtMapBuilder itemProperties = NbtMap.builder(); + NbtMapBuilder componentBuilder = NbtMap.builder(); + + setupBasicItemInfo(customItemData.maxDamage(), customItemData.stackSize(), isTool, customItemData, itemProperties, componentBuilder); + + boolean canDestroyInCreative = true; + if (customItemData.toolType() != null) { // This is not using the isTool boolean because it is not just a render type here. + canDestroyInCreative = computeToolProperties(customItemData.toolTier(), customItemData.toolType(), itemProperties, componentBuilder); + } + itemProperties.putBoolean("can_destroy_in_creative", canDestroyInCreative); + + String armorType = customItemData.armorType(); + if (armorType != null) { + computeArmorProperties(armorType, customItemData.protectionValue(), componentBuilder); + } + + computeRenderOffsets(isHat, customItemData, componentBuilder); + + if (creativeGroup != null) { + itemProperties.putString("creative_group", creativeGroup); + } + if (creativeCategory.isPresent()) { + itemProperties.putInt("creative_category", creativeCategory.getAsInt()); + } + + componentBuilder.putCompound("item_properties", itemProperties.build()); + builder.putCompound("components", componentBuilder.build()); + + return builder; + } + + private static void setupBasicItemInfo(int maxDamage, int stackSize, boolean isTool, CustomItemData customItemData, NbtMapBuilder itemProperties, NbtMapBuilder componentBuilder) { + itemProperties.putCompound("minecraft:icon", NbtMap.builder() + .putString("texture", customItemData.icon()) + .build()); + componentBuilder.putCompound("minecraft:display_name", NbtMap.builder().putString("value", customItemData.displayName()).build()); + + itemProperties.putBoolean("allow_off_hand", customItemData.allowOffhand()); + itemProperties.putBoolean("hand_equipped", isTool); + itemProperties.putInt("max_stack_size", stackSize); + if (maxDamage > 0) { + componentBuilder.putCompound("minecraft:durability", NbtMap.builder() + .putCompound("damage_chance", NbtMap.builder() + .putInt("max", 1) + .putInt("min", 1) + .build()) + .putInt("max_durability", maxDamage) + .build()); + itemProperties.putBoolean("use_duration", true); + } + } + + /** + * @return can destroy in creative + */ + private static boolean computeToolProperties(String toolTier, String toolType, NbtMapBuilder itemProperties, NbtMapBuilder componentBuilder) { + boolean canDestroyInCreative = true; + float miningSpeed = 1.0f; + + if (toolType.equals("shears")) { + componentBuilder.putCompound("minecraft:digger", ToolBreakSpeedsUtils.getShearsDigger(15)); + } else { + int toolSpeed = ToolBreakSpeedsUtils.toolTierToSpeed(toolTier); + switch (toolType) { + case "sword" -> { + miningSpeed = 1.5f; + canDestroyInCreative = false; + componentBuilder.putCompound("minecraft:digger", ToolBreakSpeedsUtils.getSwordDigger(toolSpeed)); + componentBuilder.putCompound("minecraft:weapon", NbtMap.EMPTY); + } + case "pickaxe" -> { + componentBuilder.putCompound("minecraft:digger", ToolBreakSpeedsUtils.getPickaxeDigger(toolSpeed, toolTier)); + setItemTag(componentBuilder, "pickaxe"); + } + case "axe" -> { + componentBuilder.putCompound("minecraft:digger", ToolBreakSpeedsUtils.getAxeDigger(toolSpeed)); + setItemTag(componentBuilder, "axe"); + } + case "shovel" -> { + componentBuilder.putCompound("minecraft:digger", ToolBreakSpeedsUtils.getShovelDigger(toolSpeed)); + setItemTag(componentBuilder, "shovel"); + } + case "hoe" -> { + componentBuilder.putCompound("minecraft:digger", ToolBreakSpeedsUtils.getHoeDigger(toolSpeed)); + setItemTag(componentBuilder, "hoe"); + } + } + } + + itemProperties.putBoolean("hand_equipped", true); + itemProperties.putFloat("mining_speed", miningSpeed); + + return canDestroyInCreative; + } + + private static void computeArmorProperties(String armorType, int protectionValue, NbtMapBuilder componentBuilder) { + switch (armorType) { + case "boots" -> { + componentBuilder.putString("minecraft:render_offsets", "boots"); + componentBuilder.putCompound("minecraft:wearable", WearableSlot.FEET.getSlotNbt()); + componentBuilder.putCompound("minecraft:armor", NbtMap.builder().putInt("protection", protectionValue).build()); + } + case "chestplate" -> { + componentBuilder.putString("minecraft:render_offsets", "chestplates"); + componentBuilder.putCompound("minecraft:wearable", WearableSlot.CHEST.getSlotNbt()); + componentBuilder.putCompound("minecraft:armor", NbtMap.builder().putInt("protection", protectionValue).build()); + } + case "leggings" -> { + componentBuilder.putString("minecraft:render_offsets", "leggings"); + componentBuilder.putCompound("minecraft:wearable", WearableSlot.LEGS.getSlotNbt()); + componentBuilder.putCompound("minecraft:armor", NbtMap.builder().putInt("protection", protectionValue).build()); + } + case "helmet" -> { + componentBuilder.putString("minecraft:render_offsets", "helmets"); + componentBuilder.putCompound("minecraft:wearable", WearableSlot.HEAD.getSlotNbt()); + componentBuilder.putCompound("minecraft:armor", NbtMap.builder().putInt("protection", protectionValue).build()); + } + } + } + + private static void computeRenderOffsets(boolean isHat, CustomItemData customItemData, NbtMapBuilder componentBuilder) { + if (isHat) { + componentBuilder.remove("minecraft:render_offsets"); + componentBuilder.putString("minecraft:render_offsets", "helmets"); + + componentBuilder.remove("minecraft:wearable"); + componentBuilder.putCompound("minecraft:wearable", WearableSlot.HEAD.getSlotNbt()); + } + + CustomRenderOffsets renderOffsets = customItemData.renderOffsets(); + if (renderOffsets != null) { + componentBuilder.remove("minecraft:render_offsets"); + componentBuilder.putCompound("minecraft:render_offsets", toNbtMap(renderOffsets)); + } else if (customItemData.textureSize() != 16 && !componentBuilder.containsKey("minecraft:render_offsets")) { + float scale1 = (float) (0.075 / (customItemData.textureSize() / 16f)); + float scale2 = (float) (0.125 / (customItemData.textureSize() / 16f)); + float scale3 = (float) (0.075 / (customItemData.textureSize() / 16f * 2.4f)); + + componentBuilder.putCompound("minecraft:render_offsets", + NbtMap.builder().putCompound("main_hand", NbtMap.builder() + .putCompound("first_person", xyzToScaleList(scale3, scale3, scale3)) + .putCompound("third_person", xyzToScaleList(scale1, scale2, scale1)).build()) + .putCompound("off_hand", NbtMap.builder() + .putCompound("first_person", xyzToScaleList(scale1, scale2, scale1)) + .putCompound("third_person", xyzToScaleList(scale1, scale2, scale1)).build()).build()); + } + } + + private static NbtMap toNbtMap(CustomRenderOffsets renderOffsets) { + NbtMapBuilder builder = NbtMap.builder(); + + CustomRenderOffsets.Hand mainHand = renderOffsets.mainHand(); + if (mainHand != null) { + NbtMap nbt = toNbtMap(mainHand); + if (nbt != null) { + builder.putCompound("main_hand", nbt); + } + } + CustomRenderOffsets.Hand offhand = renderOffsets.offhand(); + if (offhand != null) { + NbtMap nbt = toNbtMap(offhand); + if (nbt != null) { + builder.putCompound("off_hand", nbt); + } + } + + return builder.build(); + } + + private static NbtMap toNbtMap(CustomRenderOffsets.Hand hand) { + NbtMap firstPerson = toNbtMap(hand.firstPerson()); + NbtMap thirdPerson = toNbtMap(hand.thirdPerson()); + + if (firstPerson == null && thirdPerson == null) { + return null; + } + + NbtMapBuilder builder = NbtMap.builder(); + if (firstPerson != null) { + builder.putCompound("first_person", firstPerson); + } + if (thirdPerson != null) { + builder.putCompound("third_person", thirdPerson); + } + + return builder.build(); + } + + private static NbtMap toNbtMap(@Nullable CustomRenderOffsets.Offset offset) { + if (offset == null) { + return null; + } + + CustomRenderOffsets.OffsetXYZ position = offset.position(); + CustomRenderOffsets.OffsetXYZ rotation = offset.rotation(); + CustomRenderOffsets.OffsetXYZ scale = offset.scale(); + + if (position == null && rotation == null && scale == null) { + return null; + } + + NbtMapBuilder builder = NbtMap.builder(); + if (position != null) { + builder.putList("position", NbtType.FLOAT, toList(position)); + } + if (rotation != null) { + builder.putList("rotation", NbtType.FLOAT, toList(rotation)); + } + if (scale != null) { + builder.putList("scale", NbtType.FLOAT, toList(scale)); + } + + return builder.build(); + } + + private static List toList(CustomRenderOffsets.OffsetXYZ xyz) { + return List.of(xyz.x(), xyz.y(), xyz.z()); + } + + private static void setItemTag(NbtMapBuilder builder, String tag) { + builder.putList("item_tags", NbtType.STRING, List.of("minecraft:is_" + tag)); + } + + private static NbtMap xyzToScaleList(float x, float y, float z) { + return NbtMap.builder().putList("scale", NbtType.FLOAT, List.of(x, y, z)).build(); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java index ad1020e9b..60a16245c 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java @@ -27,6 +27,8 @@ package org.geysermc.geyser.registry.populator; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.collect.Multimap; +import com.google.common.collect.MultimapBuilder; import com.nukkitx.nbt.NbtMap; import com.nukkitx.nbt.NbtMapBuilder; import com.nukkitx.nbt.NbtType; @@ -35,6 +37,7 @@ import com.nukkitx.protocol.bedrock.data.SoundEvent; import com.nukkitx.protocol.bedrock.data.inventory.ComponentItemData; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import com.nukkitx.protocol.bedrock.packet.StartGamePacket; +import it.unimi.dsi.fastutil.ints.*; import com.nukkitx.protocol.bedrock.v527.Bedrock_v527; import com.nukkitx.protocol.bedrock.v534.Bedrock_v534; import com.nukkitx.protocol.bedrock.v544.Bedrock_v544; @@ -42,9 +45,16 @@ import it.unimi.dsi.fastutil.ints.Int2IntMap; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; import it.unimi.dsi.fastutil.objects.*; +import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.GeyserBootstrap; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.item.custom.CustomItemData; +import org.geysermc.geyser.api.item.custom.CustomItemOptions; +import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData; +import org.geysermc.geyser.event.type.GeyserDefineCustomItemsEventImpl; import org.geysermc.geyser.inventory.item.StoredItemMappings; +import org.geysermc.geyser.item.GeyserCustomMappingData; +import org.geysermc.geyser.item.mappings.MappingsConfigReader; import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.type.*; @@ -83,6 +93,58 @@ public class ItemRegistryPopulator { throw new AssertionError("Unable to load Java runtime item IDs", e); } + boolean customItemsAllowed = GeyserImpl.getInstance().getConfig().isAddNonBedrockItems(); + + Multimap customItems = MultimapBuilder.hashKeys().hashSetValues().build(); + List nonVanillaCustomItems; + + MappingsConfigReader mappingsConfigReader = new MappingsConfigReader(); + if (customItemsAllowed) { + // Load custom items from mappings files + mappingsConfigReader.loadMappingsFromJson((key, item) -> { + if (CustomItemRegistryPopulator.initialCheck(key, item, items)) { + customItems.get(key).add(item); + } + }); + + nonVanillaCustomItems = new ObjectArrayList<>(); + GeyserImpl.getInstance().eventBus().fire(new GeyserDefineCustomItemsEventImpl(customItems, nonVanillaCustomItems) { + @Override + public boolean register(@NonNull String identifier, @NonNull CustomItemData customItemData) { + if (CustomItemRegistryPopulator.initialCheck(identifier, customItemData, items)) { + customItems.get(identifier).add(customItemData); + return true; + } + return false; + } + + @Override + public boolean register(@NonNull NonVanillaCustomItemData customItemData) { + if (customItemData.identifier().startsWith("minecraft:")) { + GeyserImpl.getInstance().getLogger().error("The custom item " + customItemData.identifier() + + " is attempting to masquerade as a vanilla Minecraft item!"); + return false; + } + + if (customItemData.javaId() < items.size()) { + // Attempting to overwrite an item that already exists in the protocol + GeyserImpl.getInstance().getLogger().error("The custom item " + customItemData.identifier() + + " is attempting to overwrite a vanilla Minecraft item!"); + return false; + } + nonVanillaCustomItems.add(customItemData); + return true; + } + }); + } else { + nonVanillaCustomItems = Collections.emptyList(); + } + + int customItemCount = customItems.size() + nonVanillaCustomItems.size(); + if (customItemCount > 0) { + GeyserImpl.getInstance().getLogger().info("Registered " + customItemCount + " custom items"); + } + // We can reduce some operations as Java information is the same across all palette versions boolean firstMappingsPass = true; Int2IntMap dyeColors = new FixedInt2IntMap(); @@ -104,11 +166,20 @@ public class ItemRegistryPopulator { throw new AssertionError("Unable to load Bedrock runtime item IDs", e); } + // Used for custom items + int nextFreeBedrockId = 0; + List componentItemData = new ObjectArrayList<>(); + Map entries = new Object2ObjectOpenHashMap<>(); for (PaletteItem entry : itemEntries) { - entries.put(entry.getName(), new StartGamePacket.ItemEntry(entry.getName(), (short) entry.getId())); - bedrockIdentifierToId.put(entry.getName(), entry.getId()); + int id = entry.getId(); + if (id >= nextFreeBedrockId) { + nextFreeBedrockId = id + 1; + } + + entries.put(entry.getName(), new StartGamePacket.ItemEntry(entry.getName(), (short) id)); + bedrockIdentifierToId.put(entry.getName(), id); } Object2IntMap bedrockBlockIdOverrides = new Object2IntOpenHashMap<>(); @@ -211,17 +282,19 @@ public class ItemRegistryPopulator { int itemIndex = 0; int javaFurnaceMinecartId = 0; - boolean usingFurnaceMinecart = GeyserImpl.getInstance().getConfig().isAddNonBedrockItems(); Set javaOnlyItems = new ObjectOpenHashSet<>(); Collections.addAll(javaOnlyItems, "minecraft:spectral_arrow", "minecraft:debug_stick", "minecraft:knowledge_book", "minecraft:tipped_arrow", "minecraft:bundle"); - if (!usingFurnaceMinecart) { + if (!customItemsAllowed) { javaOnlyItems.add("minecraft:furnace_minecart"); } // Java-only items for this version javaOnlyItems.addAll(palette.getValue().additionalTranslatedItems().keySet()); + Int2ObjectMap customIdMappings = new Int2ObjectOpenHashMap<>(); + Set registeredItemNames = new ObjectOpenHashSet<>(); // This is used to check for duplicate item names + for (Map.Entry entry : items.entrySet()) { String javaIdentifier = entry.getKey().intern(); GeyserMappingItem mappingItem; @@ -233,7 +306,7 @@ public class ItemRegistryPopulator { mappingItem = entry.getValue(); } - if (usingFurnaceMinecart && javaIdentifier.equals("minecraft:furnace_minecart")) { + if (customItemsAllowed && javaIdentifier.equals("minecraft:furnace_minecart")) { javaFurnaceMinecartId = itemIndex; itemIndex++; // Will be added later @@ -387,12 +460,46 @@ public class ItemRegistryPopulator { .toolTier(""); } } + if (javaOnlyItems.contains(javaIdentifier)) { // These items don't exist on Bedrock, so set up a variable that indicates they should have custom names mappingBuilder = mappingBuilder.translationString((bedrockBlockId != -1 ? "block." : "item.") + entry.getKey().replace(":", ".")); GeyserImpl.getInstance().getLogger().debug("Adding " + entry.getKey() + " as an item that needs to be translated."); } + // Add the custom item properties, if applicable + Object2IntMap customItemOptions; + Collection customItemsToLoad = customItems.get(javaIdentifier); + if (customItemsAllowed && !customItemsToLoad.isEmpty()) { + customItemOptions = new Object2IntOpenHashMap<>(customItemsToLoad.size()); + + for (CustomItemData customItem : customItemsToLoad) { + int customProtocolId = nextFreeBedrockId++; + + String customItemName = "geyser_custom:" + customItem.name(); + if (!registeredItemNames.add(customItemName)) { + if (firstMappingsPass) { + GeyserImpl.getInstance().getLogger().error("Custom item name '" + customItem.name() + "' already exists and was registered again! Skipping..."); + } + continue; + } + + GeyserCustomMappingData customMapping = CustomItemRegistryPopulator.registerCustomItem( + customItemName, mappingItem, customItem, customProtocolId + ); + // StartGamePacket entry - needed for Bedrock to recognize the item through the protocol + entries.put(customMapping.stringId(), customMapping.startGamePacketItemEntry()); + // ComponentItemData - used to register some custom properties + componentItemData.add(customMapping.componentItemData()); + customItemOptions.put(customItem.customItemOptions(), customProtocolId); + + customIdMappings.put(customMapping.integerId(), customMapping.stringId()); + } + } else { + customItemOptions = Object2IntMaps.emptyMap(); + } + mappingBuilder.customItemOptions(customItemOptions); + ItemMapping mapping = mappingBuilder.build(); if (javaIdentifier.contains("boat")) { @@ -443,12 +550,12 @@ public class ItemRegistryPopulator { .bedrockData(0) .bedrockBlockId(-1) .stackSize(1) + .customItemOptions(Object2IntMaps.emptyMap()) .build(); - ComponentItemData furnaceMinecartData = null; - if (usingFurnaceMinecart) { + if (customItemsAllowed) { // Add the furnace minecart as a custom item - int furnaceMinecartId = mappings.size() + 1; + int furnaceMinecartId = nextFreeBedrockId++; entries.put("geysermc:furnace_minecart", new StartGamePacket.ItemEntry("geysermc:furnace_minecart", (short) furnaceMinecartId, true)); @@ -463,7 +570,7 @@ public class ItemRegistryPopulator { .build()); creativeItems.add(ItemData.builder() - .netId(netId) + .netId(netId++) .id(furnaceMinecartId) .count(1).build()); @@ -499,7 +606,36 @@ public class ItemRegistryPopulator { componentBuilder.putCompound("item_properties", itemProperties.build()); builder.putCompound("components", componentBuilder.build()); - furnaceMinecartData = new ComponentItemData("geysermc:furnace_minecart", builder.build()); + componentItemData.add(new ComponentItemData("geysermc:furnace_minecart", builder.build())); + + // Register any completely custom items given to us + IntSet registeredJavaIds = new IntOpenHashSet(); // Used to check for duplicate item java ids + for (NonVanillaCustomItemData customItem : nonVanillaCustomItems) { + if (!registeredJavaIds.add(customItem.javaId())) { + if (firstMappingsPass) { + GeyserImpl.getInstance().getLogger().error("Custom item java id " + customItem.javaId() + " already exists and was registered again! Skipping..."); + } + continue; + } + + int customItemId = nextFreeBedrockId++; + NonVanillaItemRegistration registration = CustomItemRegistryPopulator.registerCustomItem(customItem, customItemId); + + componentItemData.add(registration.componentItemData()); + ItemMapping mapping = registration.mapping(); + while (mapping.getJavaId() >= mappings.size()) { + // Fill with empty to get to the correct size + mappings.add(ItemMapping.AIR); + } + mappings.set(mapping.getJavaId(), mapping); + + if (customItem.creativeGroup() != null || customItem.creativeCategory().isPresent()) { + creativeItems.add(ItemData.builder() + .id(customItemId) + .netId(netId++) + .count(1).build()); + } + } } ItemMappings itemMappings = ItemMappings.builder() @@ -513,8 +649,9 @@ public class ItemRegistryPopulator { .boatIds(boats) .spawnEggIds(spawnEggs) .carpets(carpets) - .furnaceMinecartData(furnaceMinecartData) + .componentItemData(componentItemData) .lodestoneCompass(lodestoneEntry) + .customIdMappings(customIdMappings) .build(); Registries.ITEMS.register(palette.getValue().protocolVersion(), itemMappings); diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/RecipeRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/RecipeRegistryPopulator.java index f0a215f2a..920ada5fb 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/RecipeRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/RecipeRegistryPopulator.java @@ -33,6 +33,7 @@ import com.nukkitx.nbt.NbtMap; import com.nukkitx.nbt.NbtUtils; import com.nukkitx.protocol.bedrock.data.inventory.CraftingData; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; +import com.nukkitx.protocol.bedrock.data.inventory.descriptor.ItemDescriptorWithCount; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectArrayList; @@ -171,7 +172,7 @@ public class RecipeRegistryPopulator { /* Convert end */ return CraftingData.fromShaped(uuid.toString(), shape.get(0).length(), shape.size(), - inputs, Collections.singletonList(output), uuid, "crafting_table", 0, netId); + inputs.stream().map(ItemDescriptorWithCount::fromItem).toList(), Collections.singletonList(output), uuid, "crafting_table", 0, netId); } List inputs = new ObjectArrayList<>(); for (JsonNode entry : node.get("inputs")) { @@ -191,10 +192,10 @@ public class RecipeRegistryPopulator { if (type == 5) { // Shulker box return CraftingData.fromShulkerBox(uuid.toString(), - inputs, Collections.singletonList(output), uuid, "crafting_table", 0, netId); + inputs.stream().map(ItemDescriptorWithCount::fromItem).toList(), Collections.singletonList(output), uuid, "crafting_table", 0, netId); } return CraftingData.fromShapeless(uuid.toString(), - inputs, Collections.singletonList(output), uuid, "crafting_table", 0, netId); + inputs.stream().map(ItemDescriptorWithCount::fromItem).toList(), Collections.singletonList(output), uuid, "crafting_table", 0, netId); } private static ItemData getBedrockItemFromIdentifierJson(ItemMapping mapping, JsonNode itemNode) { diff --git a/core/src/main/java/org/geysermc/geyser/registry/provider/ProviderSupplier.java b/core/src/main/java/org/geysermc/geyser/registry/provider/ProviderSupplier.java new file mode 100644 index 000000000..6cb220ce4 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/registry/provider/ProviderSupplier.java @@ -0,0 +1,31 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.registry.provider; + +public interface ProviderSupplier { + + Object create(Object... args); +} diff --git a/core/src/main/java/org/geysermc/geyser/registry/type/BlockMapping.java b/core/src/main/java/org/geysermc/geyser/registry/type/BlockMapping.java index 3fadcf5e5..cd91f64d1 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/type/BlockMapping.java +++ b/core/src/main/java/org/geysermc/geyser/registry/type/BlockMapping.java @@ -27,8 +27,8 @@ package org.geysermc.geyser.registry.type; import lombok.Builder; import lombok.Value; -import org.geysermc.geyser.util.BlockUtils; import org.geysermc.geyser.level.physics.PistonBehavior; +import org.geysermc.geyser.util.BlockUtils; import javax.annotation.Nonnull; import javax.annotation.Nullable; diff --git a/core/src/main/java/org/geysermc/geyser/registry/type/GeyserMappingItem.java b/core/src/main/java/org/geysermc/geyser/registry/type/GeyserMappingItem.java index 9d06fd3a9..6c65f1c34 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/type/GeyserMappingItem.java +++ b/core/src/main/java/org/geysermc/geyser/registry/type/GeyserMappingItem.java @@ -42,6 +42,8 @@ public class GeyserMappingItem { @JsonProperty("stack_size") int stackSize = 64; @JsonProperty("tool_type") String toolType; @JsonProperty("tool_tier") String toolTier; + @JsonProperty("armor_type") String armorType; + @JsonProperty("protection_value") int protectionValue; @JsonProperty("max_damage") int maxDamage = 0; @JsonProperty("repair_materials") List repairMaterials; @JsonProperty("has_suspicious_stew_effect") boolean hasSuspiciousStewEffect = false; diff --git a/core/src/main/java/org/geysermc/geyser/registry/type/ItemMapping.java b/core/src/main/java/org/geysermc/geyser/registry/type/ItemMapping.java index 28d41ba46..12ba7d208 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/type/ItemMapping.java +++ b/core/src/main/java/org/geysermc/geyser/registry/type/ItemMapping.java @@ -25,10 +25,13 @@ package org.geysermc.geyser.registry.type; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntMaps; import lombok.Builder; import lombok.EqualsAndHashCode; import lombok.Value; -import org.geysermc.geyser.network.MinecraftProtocol; +import org.geysermc.geyser.api.item.custom.CustomItemOptions; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.registry.BlockRegistries; import java.util.Set; @@ -38,8 +41,8 @@ import java.util.Set; @EqualsAndHashCode public class ItemMapping { public static final ItemMapping AIR = new ItemMapping("minecraft:air", "minecraft:air", 0, 0, 0, - BlockRegistries.BLOCKS.forVersion(MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()).getBedrockAirId(), - 64, null, null, null, 0, null, false); + BlockRegistries.BLOCKS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()).getBedrockAirId(), + 64, null, null, null, Object2IntMaps.emptyMap(), 0, null, false); String javaIdentifier; String bedrockIdentifier; @@ -59,6 +62,8 @@ public class ItemMapping { String translationString; + Object2IntMap customItemOptions; + int maxDamage; Set repairMaterials; @@ -91,4 +96,4 @@ public class ItemMapping { public boolean isTool() { return this.toolType != null; } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/geysermc/geyser/registry/type/ItemMappings.java b/core/src/main/java/org/geysermc/geyser/registry/type/ItemMappings.java index ef1a8bc77..ce7ac0b07 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/type/ItemMappings.java +++ b/core/src/main/java/org/geysermc/geyser/registry/type/ItemMappings.java @@ -29,6 +29,7 @@ import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; import com.nukkitx.protocol.bedrock.data.inventory.ComponentItemData; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import com.nukkitx.protocol.bedrock.packet.StartGamePacket; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.IntList; import lombok.Builder; import lombok.Value; @@ -36,7 +37,6 @@ import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.inventory.item.StoredItemMappings; import javax.annotation.Nonnull; -import javax.annotation.Nullable; import java.util.List; import java.util.Map; import java.util.Set; @@ -67,7 +67,8 @@ public class ItemMappings { IntList spawnEggIds; List carpets; - @Nullable ComponentItemData furnaceMinecartData; + List componentItemData; + Int2ObjectMap customIdMappings; /** * Gets an {@link ItemMapping} from the given {@link ItemStack}. diff --git a/core/src/main/java/org/geysermc/geyser/registry/type/NonVanillaItemRegistration.java b/core/src/main/java/org/geysermc/geyser/registry/type/NonVanillaItemRegistration.java new file mode 100644 index 000000000..e2063f41a --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/registry/type/NonVanillaItemRegistration.java @@ -0,0 +1,34 @@ +/* + * 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/Geyser + */ + +package org.geysermc.geyser.registry.type; + +import com.nukkitx.protocol.bedrock.data.inventory.ComponentItemData; + +/** + * The return data of a successful registration of a custom item. + */ +public record NonVanillaItemRegistration(ComponentItemData componentItemData, ItemMapping mapping) { +} \ No newline at end of file diff --git a/core/src/main/java/org/geysermc/geyser/scoreboard/ScoreboardUpdater.java b/core/src/main/java/org/geysermc/geyser/scoreboard/ScoreboardUpdater.java index 45ae7eff2..fed3054b4 100644 --- a/core/src/main/java/org/geysermc/geyser/scoreboard/ScoreboardUpdater.java +++ b/core/src/main/java/org/geysermc/geyser/scoreboard/ScoreboardUpdater.java @@ -118,7 +118,7 @@ public final class ScoreboardUpdater extends Thread { FIRST_SCORE_PACKETS_PER_SECOND_THRESHOLD; geyser.getLogger().info( - GeyserLocale.getLocaleStringLog("geyser.scoreboard.updater.threshold_reached.log", session.name(), threshold, pps) + + GeyserLocale.getLocaleStringLog("geyser.scoreboard.updater.threshold_reached.log", session.bedrockUsername(), threshold, pps) + GeyserLocale.getLocaleStringLog("geyser.scoreboard.updater.threshold_reached", (millisBetweenUpdates / 1000.0)) ); diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index 4e00294a8..2fd1edd44 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -88,8 +88,12 @@ import lombok.AccessLevel; import lombok.Getter; import lombok.NonNull; import lombok.Setter; +import lombok.experimental.Accessors; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.common.value.qual.IntRange; +import org.geysermc.api.util.BedrockPlatform; +import org.geysermc.api.util.InputMode; +import org.geysermc.api.util.UiProfile; import org.geysermc.common.PlatformType; import org.geysermc.cumulus.form.Form; import org.geysermc.cumulus.form.util.FormBuilder; @@ -98,7 +102,9 @@ import org.geysermc.floodgate.util.BedrockData; import org.geysermc.geyser.Constants; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.connection.GeyserConnection; -import org.geysermc.geyser.command.CommandSender; +import org.geysermc.geyser.api.network.AuthType; +import org.geysermc.geyser.api.network.RemoteServer; +import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.configuration.EmoteOffhandWorkaroundOption; import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.attribute.GeyserAttributeType; @@ -113,13 +119,13 @@ import org.geysermc.geyser.inventory.recipe.GeyserStonecutterData; import org.geysermc.geyser.level.JavaDimension; import org.geysermc.geyser.level.WorldManager; import org.geysermc.geyser.level.physics.CollisionManager; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.network.netty.LocalSession; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.type.BlockMappings; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.ItemMappings; import org.geysermc.geyser.session.auth.AuthData; -import org.geysermc.geyser.session.auth.AuthType; import org.geysermc.geyser.session.auth.BedrockClientData; import org.geysermc.geyser.session.cache.*; import org.geysermc.geyser.skin.FloodgateSkinUploader; @@ -133,7 +139,6 @@ import org.geysermc.geyser.util.DimensionUtils; import org.geysermc.geyser.util.LoginEncryptionUtils; import org.geysermc.geyser.util.MathUtils; -import javax.annotation.Nonnull; import java.net.ConnectException; import java.net.InetSocketAddress; import java.nio.charset.StandardCharsets; @@ -145,15 +150,15 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @Getter -public class GeyserSession implements GeyserConnection, CommandSender { +public class GeyserSession implements GeyserConnection, GeyserCommandSource { - private final @Nonnull GeyserImpl geyser; - private final @Nonnull UpstreamSession upstream; + private final @NonNull GeyserImpl geyser; + private final @NonNull UpstreamSession upstream; /** * The loop where all packets and ticking is processed to prevent concurrency issues. * If this is manually called, ensure that any exceptions are properly handled. */ - private final @Nonnull EventLoop eventLoop; + private final @NonNull EventLoop eventLoop; private TcpSession downstream; @Setter private AuthData authData; @@ -165,14 +170,9 @@ public class GeyserSession implements GeyserConnection, CommandSender { @Setter private JsonNode certChainData; - /* Setter for GeyserConnect */ + @Accessors(fluent = true) @Setter - private String remoteAddress; - @Setter - private int remotePort; - @Setter - private AuthType remoteAuthType; - /* Setter for GeyserConnect */ + private RemoteServer remoteServer; @Deprecated @Setter @@ -596,9 +596,7 @@ public class GeyserSession implements GeyserConnection, CommandSender { disconnect(message); }); - this.remoteAddress = geyser.getConfig().getRemote().getAddress(); - this.remotePort = geyser.getConfig().getRemote().getPort(); - this.remoteAuthType = geyser.getConfig().getRemote().getAuthType(); + this.remoteServer = geyser.defaultRemoteServer(); } /** @@ -611,9 +609,9 @@ public class GeyserSession implements GeyserConnection, CommandSender { // Set the hardcoded shield ID to the ID we just defined in StartGamePacket upstream.getSession().getHardcodedBlockingId().set(this.itemMappings.getStoredItems().shield().getBedrockId()); - if (this.itemMappings.getFurnaceMinecartData() != null) { + if (GeyserImpl.getInstance().getConfig().isAddNonBedrockItems()) { ItemComponentPacket componentPacket = new ItemComponentPacket(); - componentPacket.getItems().add(this.itemMappings.getFurnaceMinecartData()); + componentPacket.getItems().addAll(itemMappings.getComponentItemData()); upstream.sendPacket(componentPacket); } @@ -695,7 +693,7 @@ public class GeyserSession implements GeyserConnection, CommandSender { // However, this doesn't affect the final username as Floodgate is still in charge of that. // So if you have (for example) replace spaces enabled on Floodgate the spaces will re-appear. String validUsername = username; - if (remoteAuthType == AuthType.FLOODGATE) { + if (this.remoteServer.authType() == AuthType.FLOODGATE) { validUsername = username.replace(' ', '_'); } @@ -738,7 +736,7 @@ public class GeyserSession implements GeyserConnection, CommandSender { try { service.login(); } catch (RequestException e) { - geyser.getLogger().error("Error while attempting to use refresh token for " + name() + "!", e); + geyser.getLogger().error("Error while attempting to use refresh token for " + bedrockUsername() + "!", e); return Boolean.FALSE; } @@ -750,7 +748,7 @@ public class GeyserSession implements GeyserConnection, CommandSender { } protocol = new MinecraftProtocol(profile, service.getAccessToken()); - geyser.saveRefreshToken(name(), service.getRefreshToken()); + geyser.saveRefreshToken(bedrockUsername(), service.getRefreshToken()); return Boolean.TRUE; }).whenComplete((successful, ex) -> { if (this.closed) { @@ -841,7 +839,7 @@ public class GeyserSession implements GeyserConnection, CommandSender { connectDownstream(); // Save our refresh token for later use - geyser.saveRefreshToken(name(), service.getRefreshToken()); + geyser.saveRefreshToken(bedrockUsername(), service.getRefreshToken()); return true; } } @@ -852,18 +850,18 @@ public class GeyserSession implements GeyserConnection, CommandSender { * After getting whatever credentials needed, we attempt to join the Java server. */ private void connectDownstream() { - boolean floodgate = this.remoteAuthType == AuthType.FLOODGATE; + boolean floodgate = this.remoteServer.authType() == AuthType.FLOODGATE; // Start ticking tickThread = eventLoop.scheduleAtFixedRate(this::tick, 50, 50, TimeUnit.MILLISECONDS); if (geyser.getBootstrap().getSocketAddress() != null) { // We're going to connect through the JVM and not through TCP - downstream = new LocalSession(this.remoteAddress, this.remotePort, + downstream = new LocalSession(this.remoteServer.address(), this.remoteServer.port(), geyser.getBootstrap().getSocketAddress(), upstream.getAddress().getAddress().getHostAddress(), this.protocol, this.protocol.createHelper()); } else { - downstream = new TcpClientSession(this.remoteAddress, this.remotePort, this.protocol); + downstream = new TcpClientSession(this.remoteServer.address(), this.remoteServer.port(), this.protocol); disableSrvResolving(); } @@ -943,13 +941,13 @@ public class GeyserSession implements GeyserConnection, CommandSender { } else { // Connected to an IP address geyser.getLogger().info(GeyserLocale.getLocaleStringLog("geyser.network.remote.connect", - authData.name(), protocol.getProfile().getName(), remoteAddress)); + authData.name(), protocol.getProfile().getName(), remoteServer.address())); } UUID uuid = protocol.getProfile().getId(); if (uuid == null) { // Set what our UUID *probably* is going to be - if (remoteAuthType == AuthType.FLOODGATE) { + if (remoteServer.authType() == AuthType.FLOODGATE) { uuid = new UUID(0, Long.parseLong(authData.xuid())); } else { uuid = UUID.nameUUIDFromBytes(("OfflinePlayer:" + protocol.getProfile().getName()).getBytes(StandardCharsets.UTF_8)); @@ -979,9 +977,9 @@ public class GeyserSession implements GeyserConnection, CommandSender { String disconnectMessage; Throwable cause = event.getCause(); if (cause instanceof UnexpectedEncryptionException) { - if (remoteAuthType != AuthType.FLOODGATE) { + if (remoteServer.authType() != AuthType.FLOODGATE) { // Server expects online mode - disconnectMessage = GeyserLocale.getPlayerLocaleString("geyser.network.remote.authentication_type_mismatch", getLocale()); + disconnectMessage = GeyserLocale.getPlayerLocaleString("geyser.network.remote.authentication_type_mismatch", locale()); // Explain that they may be looking for Floodgate. geyser.getLogger().warning(GeyserLocale.getLocaleStringLog( geyser.getPlatformType() == PlatformType.STANDALONE ? @@ -991,14 +989,14 @@ public class GeyserSession implements GeyserConnection, CommandSender { )); } else { // Likely that Floodgate is not configured correctly. - disconnectMessage = GeyserLocale.getPlayerLocaleString("geyser.network.remote.floodgate_login_error", getLocale()); + disconnectMessage = GeyserLocale.getPlayerLocaleString("geyser.network.remote.floodgate_login_error", locale()); if (geyser.getPlatformType() == PlatformType.STANDALONE) { geyser.getLogger().warning(GeyserLocale.getLocaleStringLog("geyser.network.remote.floodgate_login_error_standalone")); } } } else if (cause instanceof ConnectException) { // Server is offline, probably - disconnectMessage = GeyserLocale.getPlayerLocaleString("geyser.network.remote.server_offline", getLocale()); + disconnectMessage = GeyserLocale.getPlayerLocaleString("geyser.network.remote.server_offline", locale()); } else { disconnectMessage = MessageTranslator.convertMessageLenient(event.getReason()); } @@ -1006,7 +1004,7 @@ public class GeyserSession implements GeyserConnection, CommandSender { if (downstream instanceof LocalSession) { geyser.getLogger().info(GeyserLocale.getLocaleStringLog("geyser.network.remote.disconnect_internal", authData.name(), disconnectMessage)); } else { - geyser.getLogger().info(GeyserLocale.getLocaleStringLog("geyser.network.remote.disconnect", authData.name(), remoteAddress, disconnectMessage)); + geyser.getLogger().info(GeyserLocale.getLocaleStringLog("geyser.network.remote.disconnect", authData.name(), remoteServer.address(), disconnectMessage)); } if (cause != null) { cause.printStackTrace(); @@ -1074,7 +1072,7 @@ public class GeyserSession implements GeyserConnection, CommandSender { try { runnable.run(); } catch (Throwable e) { - geyser.getLogger().error("Error thrown in " + this.name() + "'s event loop!", e); + geyser.getLogger().error("Error thrown in " + this.bedrockUsername() + "'s event loop!", e); } }); } @@ -1087,7 +1085,7 @@ public class GeyserSession implements GeyserConnection, CommandSender { try { runnable.run(); } catch (Throwable e) { - geyser.getLogger().error("Error thrown in " + this.name() + "'s event loop!", e); + geyser.getLogger().error("Error thrown in " + this.bedrockUsername() + "'s event loop!", e); } }, duration, timeUnit); } @@ -1330,32 +1328,7 @@ public class GeyserSession implements GeyserConnection, CommandSender { @Override public String name() { - return authData.name(); - } - - @Override - public UUID uuid() { - return authData.uuid(); - } - - @Override - public String xuid() { - return authData.xuid(); - } - - @SuppressWarnings("ConstantConditions") // Need to enforce the parameter annotations - @Override - public boolean transfer(@NonNull String address, @IntRange(from = 0, to = 65535) int port) { - if (address == null || address.isBlank()) { - throw new IllegalArgumentException("Server address cannot be null or blank"); - } else if (port < 0 || port > 65535) { - throw new IllegalArgumentException("Server port must be between 0 and 65535, was " + port); - } - TransferPacket transferPacket = new TransferPacket(); - transferPacket.setAddress(address); - transferPacket.setPort(port); - sendUpstreamPacket(transferPacket); - return true; + return null; } @Override @@ -1377,7 +1350,7 @@ public class GeyserSession implements GeyserConnection, CommandSender { } @Override - public String getLocale() { + public String locale() { return clientData.getLanguageCode(); } @@ -1408,12 +1381,14 @@ public class GeyserSession implements GeyserConnection, CommandSender { return this.upstream.getAddress(); } - public void sendForm(Form form) { + public boolean sendForm(@NonNull Form form) { formCache.showForm(form); + return true; } - public void sendForm(FormBuilder formBuilder) { + public boolean sendForm(@NonNull FormBuilder formBuilder) { formCache.showForm(formBuilder.build()); + return true; } /** @@ -1474,7 +1449,7 @@ public class GeyserSession implements GeyserConnection, CommandSender { startGamePacket.setFromWorldTemplate(false); startGamePacket.setWorldTemplateOptionLocked(false); - String serverName = geyser.getConfig().getBedrock().getServerName(); + String serverName = geyser.getConfig().getBedrock().serverName(); startGamePacket.setLevelId(serverName); startGamePacket.setLevelName(serverName); @@ -1482,7 +1457,9 @@ public class GeyserSession implements GeyserConnection, CommandSender { // startGamePacket.setCurrentTick(0); startGamePacket.setEnchantmentSeed(0); startGamePacket.setMultiplayerCorrelationId(""); + startGamePacket.setItemEntries(this.itemMappings.getItemEntries()); + startGamePacket.setVanillaVersion("*"); startGamePacket.setInventoriesServerAuthoritative(true); startGamePacket.setServerEngine(""); // Do we want to fill this in? @@ -1645,7 +1622,7 @@ public class GeyserSession implements GeyserConnection, CommandSender { boolean spectator = gameMode == GameMode.SPECTATOR; boolean worldImmutable = gameMode == GameMode.ADVENTURE || spectator; - if (org.geysermc.geyser.network.MinecraftProtocol.supports1_19_10(this)) { + if (GameProtocol.supports1_19_10(this)) { UpdateAdventureSettingsPacket adventureSettingsPacket = new UpdateAdventureSettingsPacket(); adventureSettingsPacket.setNoMvP(false); adventureSettingsPacket.setNoPvM(false); @@ -1755,7 +1732,7 @@ public class GeyserSession implements GeyserConnection, CommandSender { * Send a packet to the server to indicate client render distance, locale, skin parts, and hand preference. */ public void sendJavaClientSettings() { - ServerboundClientInformationPacket clientSettingsPacket = new ServerboundClientInformationPacket(getLocale(), + ServerboundClientInformationPacket clientSettingsPacket = new ServerboundClientInformationPacket(locale(), getRenderDistance(), ChatVisibility.FULL, true, SKIN_PARTS, HandPreference.RIGHT_HAND, false, true); sendDownstreamPacket(clientSettingsPacket); @@ -1766,7 +1743,7 @@ public class GeyserSession implements GeyserConnection, CommandSender { * * @param statistics Updated statistics values */ - public void updateStatistics(@Nonnull Object2IntMap statistics) { + public void updateStatistics(@NonNull Object2IntMap statistics) { if (this.statistics.isEmpty()) { // Initialize custom statistics to 0, so that they appear in the form for (CustomStatistic customStatistic : CustomStatistic.values()) { @@ -1852,4 +1829,69 @@ public class GeyserSession implements GeyserConnection, CommandSender { public MinecraftCodecHelper getCodecHelper() { return (MinecraftCodecHelper) this.downstream.getCodecHelper(); } + + @Override + public String bedrockUsername() { + return authData.name(); + } + + @Override + public @MonotonicNonNull String javaUsername() { + return playerEntity.getUsername(); + } + + @Override + public UUID javaUuid() { + return playerEntity.getUuid(); + } + + @Override + public String xuid() { + return authData.xuid(); + } + + @Override + public @NonNull String version() { + return clientData.getGameVersion(); + } + + @Override + public @NonNull BedrockPlatform platform() { + return BedrockPlatform.values()[clientData.getDeviceOs().ordinal()]; //todo + } + + @Override + public @NonNull String languageCode() { + return locale(); + } + + @Override + public @NonNull UiProfile uiProfile() { + return UiProfile.values()[clientData.getUiProfile().ordinal()]; //todo + } + + @Override + public @NonNull InputMode inputMode() { + return InputMode.values()[clientData.getCurrentInputMode().ordinal()]; //todo + } + + @Override + public boolean isLinked() { + return false; //todo + } + + @SuppressWarnings("ConstantConditions") // Need to enforce the parameter annotations + @Override + public boolean transfer(@NonNull String address, @IntRange(from = 0, to = 65535) int port) { + if (address == null || address.isBlank()) { + throw new IllegalArgumentException("Server address cannot be null or blank"); + } else if (port < 0 || port > 65535) { + throw new IllegalArgumentException("Server port must be between 0 and 65535, was " + port); + } + TransferPacket transferPacket = new TransferPacket(); + transferPacket.setAddress(address); + transferPacket.setPort(port); + sendUpstreamPacket(transferPacket); + return true; + } } diff --git a/core/src/main/java/org/geysermc/geyser/session/SessionManager.java b/core/src/main/java/org/geysermc/geyser/session/SessionManager.java index 8cfc73d7e..02940e00c 100644 --- a/core/src/main/java/org/geysermc/geyser/session/SessionManager.java +++ b/core/src/main/java/org/geysermc/geyser/session/SessionManager.java @@ -28,6 +28,7 @@ package org.geysermc.geyser.session; import com.google.common.collect.ImmutableList; import lombok.AccessLevel; import lombok.Getter; +import lombok.NonNull; import org.geysermc.geyser.text.GeyserLocale; import java.util.*; @@ -61,12 +62,23 @@ public final class SessionManager { } public void removeSession(GeyserSession session) { - if (sessions.remove(session.getPlayerEntity().getUuid()) == null) { + UUID uuid = session.getPlayerEntity().getUuid(); + if (uuid == null || sessions.remove(uuid) == null) { // Connection was likely pending pendingSessions.remove(session); } } + public GeyserSession sessionByXuid(@NonNull String xuid) { + Objects.requireNonNull(xuid); + for (GeyserSession session : sessions.values()) { + if (session.xuid().equals(xuid)) { + return session; + } + } + return null; + } + /** * Creates a new, immutable list containing all pending and active sessions. */ @@ -80,7 +92,7 @@ public final class SessionManager { public void disconnectAll(String message) { Collection sessions = getAllSessions(); for (GeyserSession session : sessions) { - session.disconnect(GeyserLocale.getPlayerLocaleString(message, session.getLocale())); + session.disconnect(GeyserLocale.getPlayerLocaleString(message, session.locale())); } } diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/AdvancementsCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/AdvancementsCache.java index f5801ed2b..00b186292 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/AdvancementsCache.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/AdvancementsCache.java @@ -73,13 +73,13 @@ public class AdvancementsCache { public void buildAndShowMenuForm() { SimpleForm.Builder builder = SimpleForm.builder() - .translator(MinecraftLocale::getLocaleString, session.getLocale()) + .translator(MinecraftLocale::getLocaleString, session.locale()) .title("gui.advancements"); List rootAdvancementIds = new ArrayList<>(); for (Map.Entry advancement : storedAdvancements.entrySet()) { if (advancement.getValue().getParentId() == null) { // No parent means this is a root advancement - builder.button(MessageTranslator.convertMessage(advancement.getValue().getDisplayData().getTitle(), session.getLocale())); + builder.button(MessageTranslator.convertMessage(advancement.getValue().getDisplayData().getTitle(), session.locale())); rootAdvancementIds.add(advancement.getKey()); } } @@ -111,7 +111,7 @@ public class AdvancementsCache { */ public void buildAndShowListForm() { GeyserAdvancement categoryAdvancement = storedAdvancements.get(currentAdvancementCategoryId); - String language = session.getLocale(); + String language = session.locale(); SimpleForm.Builder builder = SimpleForm.builder() @@ -160,7 +160,7 @@ public class AdvancementsCache { */ public void buildAndShowInfoForm(GeyserAdvancement advancement) { // Cache language for easier access - String language = session.getLocale(); + String language = session.locale(); String earned = isEarned(advancement) ? "yes" : "no"; diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/BossBar.java b/core/src/main/java/org/geysermc/geyser/session/cache/BossBar.java index 7cfeaa165..cd1bc4c98 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/BossBar.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/BossBar.java @@ -57,7 +57,7 @@ public class BossBar { BossEventPacket bossEventPacket = new BossEventPacket(); bossEventPacket.setBossUniqueEntityId(entityId); bossEventPacket.setAction(BossEventPacket.Action.CREATE); - bossEventPacket.setTitle(MessageTranslator.convertMessage(title, session.getLocale())); + bossEventPacket.setTitle(MessageTranslator.convertMessage(title, session.locale())); bossEventPacket.setHealthPercentage(health); bossEventPacket.setColor(color); bossEventPacket.setOverlay(overlay); @@ -71,7 +71,7 @@ public class BossBar { BossEventPacket bossEventPacket = new BossEventPacket(); bossEventPacket.setBossUniqueEntityId(entityId); bossEventPacket.setAction(BossEventPacket.Action.UPDATE_NAME); - bossEventPacket.setTitle(MessageTranslator.convertMessage(title, session.getLocale())); + bossEventPacket.setTitle(MessageTranslator.convertMessage(title, session.locale())); session.sendUpstreamPacket(bossEventPacket); } diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/ChunkCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/ChunkCache.java index 91d6b33d6..d2c1415a3 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/ChunkCache.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/ChunkCache.java @@ -30,10 +30,10 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import lombok.Getter; import lombok.Setter; -import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.level.BedrockDimension; import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.level.chunk.GeyserChunk; -import org.geysermc.geyser.level.BedrockDimension; +import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.util.MathUtils; public class ChunkCache { diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/PistonCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/PistonCache.java index 03785de16..07ccd6280 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/PistonCache.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/PistonCache.java @@ -34,10 +34,10 @@ import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; -import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.level.physics.BoundingBox; -import org.geysermc.geyser.translator.level.block.entity.PistonBlockEntity; import org.geysermc.geyser.level.physics.Axis; +import org.geysermc.geyser.level.physics.BoundingBox; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.translator.level.block.entity.PistonBlockEntity; import java.util.Map; diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/TagCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/TagCache.java index ac0c93204..9cd5b2ef6 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/TagCache.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/TagCache.java @@ -89,7 +89,7 @@ public class TagCache { boolean emulatePost1_18Logic = convertableToMud != null && convertableToMud.length != 0; session.setEmulatePost1_18Logic(emulatePost1_18Logic); if (logger.isDebug()) { - logger.debug("Emulating post 1.18 block predication logic for " + session.name() + "? " + emulatePost1_18Logic); + logger.debug("Emulating post 1.18 block predication logic for " + session.bedrockUsername() + "? " + emulatePost1_18Logic); } Map itemTags = packet.getTags().get("minecraft:item"); @@ -104,7 +104,7 @@ public class TagCache { boolean emulatePost1_13Logic = itemTags.get("minecraft:signs").length > 1; session.setEmulatePost1_13Logic(emulatePost1_13Logic); if (logger.isDebug()) { - logger.debug("Emulating post 1.13 villager logic for " + session.name() + "? " + emulatePost1_13Logic); + logger.debug("Emulating post 1.13 villager logic for " + session.bedrockUsername() + "? " + emulatePost1_13Logic); } } diff --git a/core/src/main/java/org/geysermc/geyser/skin/FloodgateSkinUploader.java b/core/src/main/java/org/geysermc/geyser/skin/FloodgateSkinUploader.java index 7a800890b..7b6dacd16 100644 --- a/core/src/main/java/org/geysermc/geyser/skin/FloodgateSkinUploader.java +++ b/core/src/main/java/org/geysermc/geyser/skin/FloodgateSkinUploader.java @@ -31,12 +31,12 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.Getter; import org.geysermc.floodgate.pluginmessage.PluginMessageChannels; +import org.geysermc.floodgate.util.WebsocketEventType; +import org.geysermc.geyser.Constants; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserLogger; import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.Constants; import org.geysermc.geyser.util.PluginMessageUtils; -import org.geysermc.floodgate.util.WebsocketEventType; import org.java_websocket.client.WebSocketClient; import org.java_websocket.handshake.ServerHandshake; @@ -114,7 +114,7 @@ public final class FloodgateSkinUploader { if (session != null) { if (!node.get("success").asBoolean()) { - logger.info("Failed to upload skin for " + session.name()); + logger.info("Failed to upload skin for " + session.bedrockUsername()); return; } diff --git a/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java b/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java index 4eb92c3ac..992835a2b 100644 --- a/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java +++ b/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java @@ -33,9 +33,9 @@ import com.nukkitx.protocol.bedrock.data.skin.ImageData; import com.nukkitx.protocol.bedrock.data.skin.SerializedSkin; import com.nukkitx.protocol.bedrock.packet.PlayerListPacket; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.entity.type.player.PlayerEntity; import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.session.auth.AuthType; import org.geysermc.geyser.session.auth.BedrockClientData; import org.geysermc.geyser.text.GeyserLocale; @@ -286,7 +286,7 @@ public class SkinManager { String skinUrl = isAlex ? SkinProvider.EMPTY_SKIN_ALEX.getTextureUrl() : SkinProvider.EMPTY_SKIN.getTextureUrl(); String capeUrl = SkinProvider.EMPTY_CAPE.getTextureUrl(); - if (("steve".equals(skinUrl) || "alex".equals(skinUrl)) && GeyserImpl.getInstance().getConfig().getRemote().getAuthType() != AuthType.ONLINE) { + if (("steve".equals(skinUrl) || "alex".equals(skinUrl)) && GeyserImpl.getInstance().getConfig().getRemote().authType() != AuthType.ONLINE) { GeyserSession session = GeyserImpl.getInstance().connectionByUuid(uuid); if (session != null) { diff --git a/core/src/main/java/org/geysermc/geyser/text/MinecraftLocale.java b/core/src/main/java/org/geysermc/geyser/text/MinecraftLocale.java index 93150942c..94ad5eead 100644 --- a/core/src/main/java/org/geysermc/geyser/text/MinecraftLocale.java +++ b/core/src/main/java/org/geysermc/geyser/text/MinecraftLocale.java @@ -30,7 +30,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.JsonNode; import lombok.Getter; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.network.MinecraftProtocol; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.util.FileUtils; import org.geysermc.geyser.util.WebUtils; @@ -71,7 +71,7 @@ public class MinecraftLocale { // Get the url for the latest version of the games manifest String latestInfoURL = ""; for (Version version : versionManifest.getVersions()) { - if (version.getId().equals(MinecraftProtocol.getJavaCodec().getMinecraftVersion())) { + if (version.getId().equals(GameProtocol.getJavaCodec().getMinecraftVersion())) { latestInfoURL = version.getUrl(); break; } diff --git a/core/src/main/java/org/geysermc/geyser/translator/collision/BlockCollision.java b/core/src/main/java/org/geysermc/geyser/translator/collision/BlockCollision.java index 3d2cc563e..1dc6cd4e9 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/collision/BlockCollision.java +++ b/core/src/main/java/org/geysermc/geyser/translator/collision/BlockCollision.java @@ -29,10 +29,10 @@ import com.nukkitx.math.vector.Vector3d; import com.nukkitx.math.vector.Vector3i; import lombok.EqualsAndHashCode; import lombok.Getter; -import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.level.physics.Axis; import org.geysermc.geyser.level.physics.BoundingBox; import org.geysermc.geyser.level.physics.CollisionManager; -import org.geysermc.geyser.level.physics.Axis; +import org.geysermc.geyser.session.GeyserSession; @EqualsAndHashCode public class BlockCollision { diff --git a/core/src/main/java/org/geysermc/geyser/translator/collision/DoorCollision.java b/core/src/main/java/org/geysermc/geyser/translator/collision/DoorCollision.java index c101fcdfb..b47b187c4 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/collision/DoorCollision.java +++ b/core/src/main/java/org/geysermc/geyser/translator/collision/DoorCollision.java @@ -26,8 +26,8 @@ package org.geysermc.geyser.translator.collision; import lombok.EqualsAndHashCode; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.level.physics.BoundingBox; +import org.geysermc.geyser.session.GeyserSession; @EqualsAndHashCode(callSuper = true) @CollisionRemapper(regex = "_door$", usesParams = true, passDefaultBoxes = true) diff --git a/core/src/main/java/org/geysermc/geyser/translator/collision/ScaffoldingCollision.java b/core/src/main/java/org/geysermc/geyser/translator/collision/ScaffoldingCollision.java index 2aa74499a..dfbd1c8b8 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/collision/ScaffoldingCollision.java +++ b/core/src/main/java/org/geysermc/geyser/translator/collision/ScaffoldingCollision.java @@ -26,8 +26,8 @@ package org.geysermc.geyser.translator.collision; import lombok.EqualsAndHashCode; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.level.physics.BoundingBox; +import org.geysermc.geyser.session.GeyserSession; /** * In order for scaffolding to work on Bedrock, entity flags need to be sent to the player diff --git a/core/src/main/java/org/geysermc/geyser/translator/collision/SnowCollision.java b/core/src/main/java/org/geysermc/geyser/translator/collision/SnowCollision.java index 998e15ded..fb83e357d 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/collision/SnowCollision.java +++ b/core/src/main/java/org/geysermc/geyser/translator/collision/SnowCollision.java @@ -26,8 +26,8 @@ package org.geysermc.geyser.translator.collision; import lombok.EqualsAndHashCode; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.level.physics.BoundingBox; +import org.geysermc.geyser.session.GeyserSession; @EqualsAndHashCode(callSuper = true) @CollisionRemapper(regex = "^snow$", passDefaultBoxes = true, usesParams = true) diff --git a/core/src/main/java/org/geysermc/geyser/translator/collision/TrapdoorCollision.java b/core/src/main/java/org/geysermc/geyser/translator/collision/TrapdoorCollision.java index 0660c3cf6..836c05711 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/collision/TrapdoorCollision.java +++ b/core/src/main/java/org/geysermc/geyser/translator/collision/TrapdoorCollision.java @@ -26,9 +26,9 @@ package org.geysermc.geyser.translator.collision; import lombok.EqualsAndHashCode; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.level.physics.BoundingBox; import org.geysermc.geyser.level.physics.CollisionManager; +import org.geysermc.geyser.session.GeyserSession; @EqualsAndHashCode(callSuper = true) @CollisionRemapper(regex = "_trapdoor$", usesParams = true, passDefaultBoxes = true) diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/AbstractBlockInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/AbstractBlockInventoryTranslator.java index bf806bd06..c1fabcf0f 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/AbstractBlockInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/AbstractBlockInventoryTranslator.java @@ -27,10 +27,10 @@ package org.geysermc.geyser.translator.inventory; import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; import org.geysermc.geyser.inventory.Inventory; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.inventory.holder.BlockInventoryHolder; import org.geysermc.geyser.inventory.holder.InventoryHolder; import org.geysermc.geyser.inventory.updater.InventoryUpdater; +import org.geysermc.geyser.session.GeyserSession; /** * Provided as a base for any inventory that requires a block for opening it diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/AnvilInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/AnvilInventoryTranslator.java index e56586b14..956fdeae0 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/AnvilInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/AnvilInventoryTranslator.java @@ -34,11 +34,11 @@ import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.StackRequ import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.StackRequestActionType; import com.nukkitx.protocol.bedrock.packet.ItemStackResponsePacket; import org.geysermc.geyser.inventory.AnvilContainer; +import org.geysermc.geyser.inventory.BedrockContainerSlot; import org.geysermc.geyser.inventory.Inventory; import org.geysermc.geyser.inventory.PlayerInventory; -import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.inventory.BedrockContainerSlot; import org.geysermc.geyser.inventory.updater.AnvilInventoryUpdater; +import org.geysermc.geyser.session.GeyserSession; import java.util.Objects; diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/BaseInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/BaseInventoryTranslator.java index 8016ca24f..9b6e6df56 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/BaseInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/BaseInventoryTranslator.java @@ -28,12 +28,8 @@ package org.geysermc.geyser.translator.inventory; import com.github.steveice10.mc.protocol.data.game.inventory.ContainerType; import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData; -import org.geysermc.geyser.inventory.Container; -import org.geysermc.geyser.inventory.Inventory; -import org.geysermc.geyser.inventory.PlayerInventory; +import org.geysermc.geyser.inventory.*; import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.inventory.BedrockContainerSlot; -import org.geysermc.geyser.inventory.SlotType; public abstract class BaseInventoryTranslator extends InventoryTranslator { public BaseInventoryTranslator(int size) { diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/BrewingInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/BrewingInventoryTranslator.java index b12cd8354..69ad41f97 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/BrewingInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/BrewingInventoryTranslator.java @@ -29,10 +29,10 @@ import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData; import com.nukkitx.protocol.bedrock.packet.ContainerSetDataPacket; -import org.geysermc.geyser.inventory.Inventory; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.inventory.BedrockContainerSlot; +import org.geysermc.geyser.inventory.Inventory; import org.geysermc.geyser.inventory.updater.ContainerInventoryUpdater; +import org.geysermc.geyser.session.GeyserSession; public class BrewingInventoryTranslator extends AbstractBlockInventoryTranslator { public BrewingInventoryTranslator() { diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/CartographyInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/CartographyInventoryTranslator.java index 226abe157..c796ab5e3 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/CartographyInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/CartographyInventoryTranslator.java @@ -28,13 +28,9 @@ package org.geysermc.geyser.translator.inventory; import com.github.steveice10.mc.protocol.data.game.inventory.ContainerType; import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData; -import org.geysermc.geyser.inventory.CartographyContainer; -import org.geysermc.geyser.inventory.GeyserItemStack; -import org.geysermc.geyser.inventory.Inventory; -import org.geysermc.geyser.inventory.PlayerInventory; -import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.inventory.BedrockContainerSlot; +import org.geysermc.geyser.inventory.*; import org.geysermc.geyser.inventory.updater.UIInventoryUpdater; +import org.geysermc.geyser.session.GeyserSession; public class CartographyInventoryTranslator extends AbstractBlockInventoryTranslator { public CartographyInventoryTranslator() { diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/Generic3X3InventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/Generic3X3InventoryTranslator.java index 9f7a52107..3ca8f165f 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/Generic3X3InventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/Generic3X3InventoryTranslator.java @@ -28,12 +28,12 @@ package org.geysermc.geyser.translator.inventory; import com.github.steveice10.mc.protocol.data.game.inventory.ContainerType; import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket; +import org.geysermc.geyser.inventory.BedrockContainerSlot; import org.geysermc.geyser.inventory.Generic3X3Container; import org.geysermc.geyser.inventory.Inventory; import org.geysermc.geyser.inventory.PlayerInventory; -import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.inventory.BedrockContainerSlot; import org.geysermc.geyser.inventory.updater.ContainerInventoryUpdater; +import org.geysermc.geyser.session.GeyserSession; /** * Droppers and dispensers diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/InventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/InventoryTranslator.java index 394a394ed..8c7ee1c80 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/InventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/InventoryTranslator.java @@ -201,7 +201,7 @@ public abstract class InventoryTranslator { TransferStackRequestActionData transferAction = (TransferStackRequestActionData) action; if (!(checkNetId(session, inventory, transferAction.getSource()) && checkNetId(session, inventory, transferAction.getDestination()))) { if (session.getGeyser().getConfig().isDebugMode()) { - session.getGeyser().getLogger().error("DEBUG: About to reject TAKE/PLACE request made by " + session.name()); + session.getGeyser().getLogger().error("DEBUG: About to reject TAKE/PLACE request made by " + session.bedrockUsername()); dumpStackRequestDetails(session, inventory, transferAction.getSource(), transferAction.getDestination()); } return rejectRequest(request); @@ -292,7 +292,7 @@ public abstract class InventoryTranslator { if (!(checkNetId(session, inventory, source) && checkNetId(session, inventory, destination))) { if (session.getGeyser().getConfig().isDebugMode()) { - session.getGeyser().getLogger().error("DEBUG: About to reject SWAP request made by " + session.name()); + session.getGeyser().getLogger().error("DEBUG: About to reject SWAP request made by " + session.bedrockUsername()); dumpStackRequestDetails(session, inventory, source, destination); } return rejectRequest(request); diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/LecternInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/LecternInventoryTranslator.java index f6d24363a..7b2f861f5 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/LecternInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/LecternInventoryTranslator.java @@ -39,8 +39,8 @@ import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.inventory.Inventory; import org.geysermc.geyser.inventory.LecternContainer; import org.geysermc.geyser.inventory.PlayerInventory; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.inventory.updater.InventoryUpdater; +import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.util.BlockEntityUtils; import org.geysermc.geyser.util.InventoryUtils; diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/MerchantInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/MerchantInventoryTranslator.java index 031fb606e..5e9c99ae9 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/MerchantInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/MerchantInventoryTranslator.java @@ -37,16 +37,12 @@ import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.AutoCraft import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.CraftRecipeStackRequestActionData; import com.nukkitx.protocol.bedrock.packet.ItemStackResponsePacket; import com.nukkitx.protocol.bedrock.packet.SetEntityLinkPacket; -import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.EntityDefinitions; -import org.geysermc.geyser.inventory.Inventory; -import org.geysermc.geyser.inventory.MerchantContainer; -import org.geysermc.geyser.inventory.PlayerInventory; -import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.inventory.BedrockContainerSlot; -import org.geysermc.geyser.inventory.SlotType; +import org.geysermc.geyser.entity.type.Entity; +import org.geysermc.geyser.inventory.*; import org.geysermc.geyser.inventory.updater.InventoryUpdater; import org.geysermc.geyser.inventory.updater.UIInventoryUpdater; +import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.util.InventoryUtils; import java.util.concurrent.TimeUnit; diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/PlayerInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/PlayerInventoryTranslator.java index e2349e5a5..ee7d6a7c6 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/PlayerInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/PlayerInventoryTranslator.java @@ -371,7 +371,7 @@ public class PlayerInventoryTranslator extends InventoryTranslator { } } default -> { - session.getGeyser().getLogger().error("Unknown crafting state induced by " + session.name()); + session.getGeyser().getLogger().error("Unknown crafting state induced by " + session.bedrockUsername()); return rejectRequest(request); } } diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/ShulkerInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/ShulkerInventoryTranslator.java index f77ff2229..a055d3b5d 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/ShulkerInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/ShulkerInventoryTranslator.java @@ -32,13 +32,13 @@ import com.nukkitx.nbt.NbtMapBuilder; import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket; -import org.geysermc.geyser.inventory.Inventory; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.inventory.BedrockContainerSlot; +import org.geysermc.geyser.inventory.Inventory; import org.geysermc.geyser.inventory.holder.BlockInventoryHolder; import org.geysermc.geyser.inventory.updater.ContainerInventoryUpdater; -import org.geysermc.geyser.translator.level.block.entity.BlockEntityTranslator; import org.geysermc.geyser.registry.Registries; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.translator.level.block.entity.BlockEntityTranslator; public class ShulkerInventoryTranslator extends AbstractBlockInventoryTranslator { public ShulkerInventoryTranslator() { diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/chest/ChestInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/chest/ChestInventoryTranslator.java index 65d789c0b..548e9e6e3 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/chest/ChestInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/chest/ChestInventoryTranslator.java @@ -26,12 +26,12 @@ package org.geysermc.geyser.translator.inventory.chest; import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; -import org.geysermc.geyser.inventory.Inventory; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.inventory.BedrockContainerSlot; -import org.geysermc.geyser.translator.inventory.BaseInventoryTranslator; +import org.geysermc.geyser.inventory.Inventory; import org.geysermc.geyser.inventory.updater.ChestInventoryUpdater; import org.geysermc.geyser.inventory.updater.InventoryUpdater; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.translator.inventory.BaseInventoryTranslator; public abstract class ChestInventoryTranslator extends BaseInventoryTranslator { private final InventoryUpdater updater; diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/chest/DoubleChestInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/chest/DoubleChestInventoryTranslator.java index ec5c882c3..0dd8553fd 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/chest/DoubleChestInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/chest/DoubleChestInventoryTranslator.java @@ -35,11 +35,11 @@ import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket; import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket; import org.geysermc.geyser.inventory.Container; import org.geysermc.geyser.inventory.Inventory; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.level.block.DoubleChestValue; -import org.geysermc.geyser.translator.level.block.entity.DoubleChestBlockEntityTranslator; import org.geysermc.geyser.registry.BlockRegistries; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.translator.level.block.entity.DoubleChestBlockEntityTranslator; public class DoubleChestInventoryTranslator extends ChestInventoryTranslator { private final int defaultJavaBlockState; diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/chest/SingleChestInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/chest/SingleChestInventoryTranslator.java index 4d158c4fe..41e7bfb9f 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/chest/SingleChestInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/chest/SingleChestInventoryTranslator.java @@ -27,9 +27,9 @@ package org.geysermc.geyser.translator.inventory.chest; import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; import org.geysermc.geyser.inventory.Inventory; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.inventory.holder.BlockInventoryHolder; import org.geysermc.geyser.inventory.holder.InventoryHolder; +import org.geysermc.geyser.session.GeyserSession; public class SingleChestInventoryTranslator extends ChestInventoryTranslator { private final InventoryHolder holder; diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/furnace/AbstractFurnaceInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/furnace/AbstractFurnaceInventoryTranslator.java index 6794b17e4..764ab0a33 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/furnace/AbstractFurnaceInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/furnace/AbstractFurnaceInventoryTranslator.java @@ -28,12 +28,12 @@ package org.geysermc.geyser.translator.inventory.furnace; import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; import com.nukkitx.protocol.bedrock.packet.ContainerSetDataPacket; -import org.geysermc.geyser.inventory.Inventory; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.inventory.BedrockContainerSlot; +import org.geysermc.geyser.inventory.Inventory; import org.geysermc.geyser.inventory.SlotType; -import org.geysermc.geyser.translator.inventory.AbstractBlockInventoryTranslator; import org.geysermc.geyser.inventory.updater.ContainerInventoryUpdater; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.translator.inventory.AbstractBlockInventoryTranslator; public abstract class AbstractFurnaceInventoryTranslator extends AbstractBlockInventoryTranslator { AbstractFurnaceInventoryTranslator(String javaBlockIdentifier, ContainerType containerType) { diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/horse/AbstractHorseInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/horse/AbstractHorseInventoryTranslator.java index 064793d29..0ad6ba137 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/horse/AbstractHorseInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/horse/AbstractHorseInventoryTranslator.java @@ -26,10 +26,10 @@ package org.geysermc.geyser.translator.inventory.horse; import org.geysermc.geyser.inventory.Inventory; -import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.translator.inventory.BaseInventoryTranslator; import org.geysermc.geyser.inventory.updater.HorseInventoryUpdater; import org.geysermc.geyser.inventory.updater.InventoryUpdater; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.translator.inventory.BaseInventoryTranslator; public abstract class AbstractHorseInventoryTranslator extends BaseInventoryTranslator { private final InventoryUpdater updater; diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/horse/ChestedHorseInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/horse/ChestedHorseInventoryTranslator.java index 08462249e..4930c6b60 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/horse/ChestedHorseInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/horse/ChestedHorseInventoryTranslator.java @@ -30,9 +30,9 @@ import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData; import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket; +import org.geysermc.geyser.inventory.BedrockContainerSlot; import org.geysermc.geyser.inventory.Inventory; import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.inventory.BedrockContainerSlot; import java.util.Arrays; diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/CompassTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/CompassTranslator.java index 4c2978082..a0da82648 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/CompassTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/CompassTranslator.java @@ -30,7 +30,7 @@ import com.github.steveice10.opennbt.tag.builtin.ByteTag; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.Tag; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; -import org.geysermc.geyser.network.MinecraftProtocol; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.ItemMappings; @@ -79,8 +79,7 @@ public class CompassTranslator extends ItemTranslator { @Override public List getAppliedItems() { - return Arrays.stream(Registries.ITEMS.forVersion(MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) - .getItems()) + return Arrays.stream(Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()).getItems()) .filter(entry -> entry.getJavaIdentifier().endsWith("compass")) .collect(Collectors.toList()); } diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/FilledMapTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/FilledMapTranslator.java index 3dfa2d82f..b5dbefc3a 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/FilledMapTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/FilledMapTranslator.java @@ -29,7 +29,7 @@ import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.Tag; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; -import org.geysermc.geyser.network.MinecraftProtocol; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.ItemMappings; @@ -61,7 +61,7 @@ public class FilledMapTranslator extends ItemTranslator { @Override public List getAppliedItems() { return Collections.singletonList( - Registries.ITEMS.forVersion(MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) + Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) .getMapping("minecraft:filled_map") ); } diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/GoatHornTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/GoatHornTranslator.java index 08e8534af..2cb9d7ec7 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/GoatHornTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/GoatHornTranslator.java @@ -29,7 +29,7 @@ import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; import com.github.steveice10.opennbt.tag.builtin.StringTag; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.network.MinecraftProtocol; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.ItemMappings; @@ -91,7 +91,7 @@ public class GoatHornTranslator extends ItemTranslator { @Override public List getAppliedItems() { return Collections.singletonList( - Registries.ITEMS.forVersion(MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) + Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) .getMapping("minecraft:goat_horn") ); } diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/ItemTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/ItemTranslator.java index 539d20207..b36833cb1 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/ItemTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/ItemTranslator.java @@ -34,9 +34,12 @@ import com.nukkitx.nbt.NbtType; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2IntMap; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.item.custom.CustomItemOptions; +import org.geysermc.geyser.api.util.TriState; import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.type.ItemMapping; @@ -122,7 +125,7 @@ public abstract class ItemTranslator { } } if (itemStack.getNbt().isEmpty()) { - // Otherwise, seems to causes issues with villagers accepting books, and I don't see how this will break anything else. - Camotoy + // Otherwise, seems to cause issues with villagers accepting books, and I don't see how this will break anything else. - Camotoy itemStack = new ItemStack(itemStack.getId(), itemStack.getAmount(), null); } } @@ -159,7 +162,7 @@ public abstract class ItemTranslator { nbt = translateDisplayProperties(session, nbt, bedrockItem); if (session.isAdvancedTooltips()) { - nbt = addAdvancedTooltips(nbt, bedrockItem, session.getLocale()); + nbt = addAdvancedTooltips(nbt, bedrockItem, session.locale()); } ItemStack itemStack = new ItemStack(stack.getId(), stack.getAmount(), nbt); @@ -170,6 +173,8 @@ public abstract class ItemTranslator { builder.blockRuntimeId(bedrockItem.getBedrockBlockId()); } + translateCustomItem(nbt, builder, bedrockItem); + if (nbt != null) { // Translate the canDestroy and canPlaceOn Java NBT ListTag canDestroy = nbt.get("CanDestroy"); @@ -261,16 +266,23 @@ public abstract class ItemTranslator { } /** - * Given an item stack, determine the item mapping that should be applied to Bedrock players. + * Given an item stack, determine the Bedrock item ID that should be applied to Bedrock players. */ - @Nonnull - public static ItemMapping getBedrockItemMapping(GeyserSession session, @Nonnull GeyserItemStack itemStack) { + public static int getBedrockItemId(GeyserSession session, @Nonnull GeyserItemStack itemStack) { if (itemStack.isEmpty()) { - return ItemMapping.AIR; + return ItemMapping.AIR.getJavaId(); } int javaId = itemStack.getJavaId(); - return ITEM_STACK_TRANSLATORS.getOrDefault(javaId, DEFAULT_TRANSLATOR) + ItemMapping mapping = ITEM_STACK_TRANSLATORS.getOrDefault(javaId, DEFAULT_TRANSLATOR) .getItemMapping(javaId, itemStack.getNbt(), session.getItemMappings()); + + int customItemId = getCustomItem(itemStack.getNbt(), mapping); + if (customItemId == -1) { + // No custom item + return mapping.getBedrockId(); + } else { + return customItemId; + } } private static final ItemTranslator DEFAULT_TRANSLATOR = new ItemTranslator() { @@ -292,6 +304,10 @@ public abstract class ItemTranslator { if (itemStack.getNbt() != null) { builder.tag(this.translateNbtToBedrock(itemStack.getNbt())); } + + CompoundTag nbt = itemStack.getNbt(); + translateCustomItem(nbt, builder, mapping); + return builder; } @@ -416,7 +432,7 @@ public abstract class ItemTranslator { if (object instanceof byte[]) { return new ByteArrayTag(name, (byte[]) object); } - + if (object instanceof Byte) { return new ByteTag(name, (byte) object); } @@ -490,7 +506,7 @@ public abstract class ItemTranslator { String name = tagName.getValue(); // Get the translated name and prefix it with a reset char - name = MessageTranslator.convertMessageLenient(name, session.getLocale()); + name = MessageTranslator.convertMessageLenient(name, session.locale()); // Add the new name tag display.put(new StringTag("Name", name)); @@ -518,12 +534,54 @@ public abstract class ItemTranslator { String translationKey = mapping.getTranslationString(); // Reset formatting since Bedrock defaults to italics - display.put(new StringTag("Name", "§r§" + translationColor + MinecraftLocale.getLocaleString(translationKey, session.getLocale()))); + display.put(new StringTag("Name", "§r§" + translationColor + MinecraftLocale.getLocaleString(translationKey, session.locale()))); } return tag; } + /** + * Translates the custom model data of an item + */ + private static void translateCustomItem(CompoundTag nbt, ItemData.Builder builder, ItemMapping mapping) { + int bedrockId = getCustomItem(nbt, mapping); + if (bedrockId != -1) { + builder.id(bedrockId); + } + } + + private static int getCustomItem(CompoundTag nbt, ItemMapping mapping) { + if (nbt == null) { + return -1; + } + Object2IntMap customMappings = mapping.getCustomItemOptions(); + if (customMappings.isEmpty()) { + return -1; + } + int customModelData = nbt.get("CustomModelData") instanceof IntTag customModelDataTag ? customModelDataTag.getValue() : 0; + TriState unbreakable = TriState.fromBoolean(nbt.get("Unbreakable") instanceof ByteTag unbreakableTag && unbreakableTag.getValue() == 1); + int damage = nbt.get("Damage") instanceof IntTag damageTag ? damageTag.getValue() : 0; + for (Object2IntMap.Entry mappingTypes : customMappings.object2IntEntrySet()) { + CustomItemOptions options = mappingTypes.getKey(); + + TriState unbreakableOption = options.unbreakable(); + if (unbreakableOption == unbreakable) { // Implementation note: if the option is NOT_SET then this comparison will always be false because of how the item unbreaking TriState is created + return mappingTypes.getIntValue(); + } + + OptionalInt customModelDataOption = options.customModelData(); + if (customModelDataOption.isPresent() && customModelDataOption.getAsInt() == customModelData) { + return mappingTypes.getIntValue(); + } + + OptionalInt damagePredicate = options.damagePredicate(); + if (damagePredicate.isPresent() && damagePredicate.getAsInt() == damage) { + return mappingTypes.getIntValue(); + } + } + return -1; + } + /** * Checks if an {@link ItemStack} is equal to another item stack * diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/NbtItemStackTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/NbtItemStackTranslator.java index bfa7ebc2e..5f22668df 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/NbtItemStackTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/NbtItemStackTranslator.java @@ -26,8 +26,8 @@ package org.geysermc.geyser.translator.inventory.item; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.registry.type.ItemMapping; +import org.geysermc.geyser.session.GeyserSession; public abstract class NbtItemStackTranslator { diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/PotionTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/PotionTranslator.java index bf16af38f..3e814a098 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/PotionTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/PotionTranslator.java @@ -30,8 +30,8 @@ import com.github.steveice10.opennbt.tag.builtin.StringTag; import com.github.steveice10.opennbt.tag.builtin.Tag; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.network.MinecraftProtocol; import org.geysermc.geyser.inventory.item.Potion; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.ItemMappings; @@ -74,8 +74,7 @@ public class PotionTranslator extends ItemTranslator { @Override public List getAppliedItems() { - return Arrays.stream(Registries.ITEMS.forVersion(MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) - .getItems()) + return Arrays.stream(Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()).getItems()) .filter(entry -> entry.getJavaIdentifier().endsWith("potion")) .collect(Collectors.toList()); } diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/TippedArrowTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/TippedArrowTranslator.java index d831ce586..bbf598ecd 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/TippedArrowTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/TippedArrowTranslator.java @@ -30,8 +30,8 @@ import com.github.steveice10.opennbt.tag.builtin.StringTag; import com.github.steveice10.opennbt.tag.builtin.Tag; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.network.MinecraftProtocol; import org.geysermc.geyser.inventory.item.TippedArrowPotion; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.ItemMappings; @@ -42,7 +42,7 @@ import java.util.stream.Collectors; @ItemRemapper public class TippedArrowTranslator extends ItemTranslator { - private static final int TIPPED_ARROW_JAVA_ID = Registries.ITEMS.forVersion(MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) + private static final int TIPPED_ARROW_JAVA_ID = Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) .getMapping("minecraft:tipped_arrow") .getJavaId(); @@ -81,8 +81,7 @@ public class TippedArrowTranslator extends ItemTranslator { @Override public List getAppliedItems() { - return Arrays.stream(Registries.ITEMS.forVersion(MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) - .getItems()) + return Arrays.stream(Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()).getItems()) .filter(entry -> entry.getJavaIdentifier().contains("arrow") && !entry.getJavaIdentifier().contains("spectral")) .collect(Collectors.toList()); diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/AxolotlBucketTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/AxolotlBucketTranslator.java index c3abf2495..19809c12f 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/AxolotlBucketTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/AxolotlBucketTranslator.java @@ -28,11 +28,11 @@ package org.geysermc.geyser.translator.inventory.item.nbt; import com.github.steveice10.opennbt.tag.builtin.ByteTag; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.StringTag; +import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.text.MinecraftLocale; import org.geysermc.geyser.translator.inventory.item.ItemRemapper; import org.geysermc.geyser.translator.inventory.item.NbtItemStackTranslator; -import org.geysermc.geyser.registry.type.ItemMapping; -import org.geysermc.geyser.text.MinecraftLocale; @ItemRemapper public class AxolotlBucketTranslator extends NbtItemStackTranslator { @@ -42,7 +42,7 @@ public class AxolotlBucketTranslator extends NbtItemStackTranslator { // Bedrock Edition displays the properties of the axolotl. Java does not. // To work around this, set the custom name to the Axolotl translation and it's displayed correctly itemTag.put(new ByteTag("AppendCustomName", (byte) 1)); - itemTag.put(new StringTag("CustomName", MinecraftLocale.getLocaleString("entity.minecraft.axolotl", session.getLocale()))); + itemTag.put(new StringTag("CustomName", MinecraftLocale.getLocaleString("entity.minecraft.axolotl", session.locale()))); // Boilerplate required so the nametag does not appear as "Bucket of " itemTag.put(new StringTag("ColorID", "")); itemTag.put(new StringTag("BodyID", "")); diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/BannerTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/BannerTranslator.java index ed4865411..95dd07f22 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/BannerTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/BannerTranslator.java @@ -29,7 +29,7 @@ import com.github.steveice10.opennbt.tag.builtin.*; import com.nukkitx.nbt.NbtList; import com.nukkitx.nbt.NbtMap; import com.nukkitx.nbt.NbtType; -import org.geysermc.geyser.network.MinecraftProtocol; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.session.GeyserSession; @@ -76,8 +76,7 @@ public class BannerTranslator extends NbtItemStackTranslator { } public BannerTranslator() { - appliedItems = Arrays.stream(Registries.ITEMS.forVersion(MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) - .getItems()) + appliedItems = Arrays.stream(Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()).getItems()) .filter(entry -> entry.getJavaIdentifier().endsWith("banner")) .collect(Collectors.toList()); } diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/BasicItemTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/BasicItemTranslator.java index a507d02cc..5dcc76b49 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/BasicItemTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/BasicItemTranslator.java @@ -26,11 +26,11 @@ package org.geysermc.geyser.translator.inventory.item.nbt; import com.github.steveice10.opennbt.tag.builtin.*; +import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.inventory.item.ItemRemapper; -import org.geysermc.geyser.translator.text.MessageTranslator; import org.geysermc.geyser.translator.inventory.item.NbtItemStackTranslator; -import org.geysermc.geyser.registry.type.ItemMapping; +import org.geysermc.geyser.translator.text.MessageTranslator; import org.geysermc.geyser.util.ItemUtils; import java.util.ArrayList; @@ -59,7 +59,7 @@ public class BasicItemTranslator extends NbtItemStackTranslator { List lore = new ArrayList<>(); for (Tag tag : listTag.getValue()) { if (!(tag instanceof StringTag)) continue; - lore.add(new StringTag("", MessageTranslator.convertMessageLenient(((StringTag) tag).getValue(), session.getLocale()))); + lore.add(new StringTag("", MessageTranslator.convertMessageLenient(((StringTag) tag).getValue(), session.locale()))); } displayTag.put(new ListTag("Lore", lore)); } diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/BookPagesTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/BookPagesTranslator.java index ec741f261..652d804fe 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/BookPagesTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/BookPagesTranslator.java @@ -29,11 +29,11 @@ import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.ListTag; import com.github.steveice10.opennbt.tag.builtin.StringTag; import com.github.steveice10.opennbt.tag.builtin.Tag; +import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.inventory.item.ItemRemapper; import org.geysermc.geyser.translator.inventory.item.NbtItemStackTranslator; import org.geysermc.geyser.translator.text.MessageTranslator; -import org.geysermc.geyser.registry.type.ItemMapping; import java.util.ArrayList; import java.util.List; diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/CrossbowTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/CrossbowTranslator.java index 723798c89..0a4ca0686 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/CrossbowTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/CrossbowTranslator.java @@ -28,11 +28,11 @@ package org.geysermc.geyser.translator.inventory.item.nbt; import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; import com.github.steveice10.opennbt.tag.builtin.*; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; +import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.inventory.item.ItemRemapper; import org.geysermc.geyser.translator.inventory.item.ItemTranslator; import org.geysermc.geyser.translator.inventory.item.NbtItemStackTranslator; -import org.geysermc.geyser.registry.type.ItemMapping; @ItemRemapper public class CrossbowTranslator extends NbtItemStackTranslator { diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/EnchantedBookTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/EnchantedBookTranslator.java index 9b1d423c1..ad6c2e9f1 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/EnchantedBookTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/EnchantedBookTranslator.java @@ -28,10 +28,10 @@ package org.geysermc.geyser.translator.inventory.item.nbt; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.ListTag; import com.github.steveice10.opennbt.tag.builtin.Tag; +import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.inventory.item.ItemRemapper; import org.geysermc.geyser.translator.inventory.item.NbtItemStackTranslator; -import org.geysermc.geyser.registry.type.ItemMapping; @ItemRemapper(priority = 1) public class EnchantedBookTranslator extends NbtItemStackTranslator { diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/EnchantmentTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/EnchantmentTranslator.java index cd6d5d6ff..204981965 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/EnchantmentTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/EnchantmentTranslator.java @@ -27,11 +27,11 @@ package org.geysermc.geyser.translator.inventory.item.nbt; import com.github.steveice10.opennbt.tag.builtin.*; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.inventory.item.Enchantment; +import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.inventory.item.ItemRemapper; import org.geysermc.geyser.translator.inventory.item.NbtItemStackTranslator; -import org.geysermc.geyser.inventory.item.Enchantment; -import org.geysermc.geyser.registry.type.ItemMapping; import java.util.ArrayList; import java.util.List; diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/FireworkBaseTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/FireworkBaseTranslator.java index 6a4438358..b74a4f61e 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/FireworkBaseTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/FireworkBaseTranslator.java @@ -29,8 +29,8 @@ import com.github.steveice10.opennbt.tag.builtin.ByteArrayTag; import com.github.steveice10.opennbt.tag.builtin.ByteTag; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.IntArrayTag; -import org.geysermc.geyser.translator.inventory.item.NbtItemStackTranslator; import org.geysermc.geyser.level.FireworkColor; +import org.geysermc.geyser.translator.inventory.item.NbtItemStackTranslator; import org.geysermc.geyser.util.MathUtils; /** diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/FireworkRocketTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/FireworkRocketTranslator.java index 566b0ac2b..fdf898273 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/FireworkRocketTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/FireworkRocketTranslator.java @@ -29,9 +29,9 @@ import com.github.steveice10.opennbt.tag.builtin.ByteTag; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.ListTag; import com.github.steveice10.opennbt.tag.builtin.Tag; +import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.inventory.item.ItemRemapper; -import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.util.MathUtils; @ItemRemapper diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/FireworkStarTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/FireworkStarTranslator.java index c907375b9..eca3272d1 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/FireworkStarTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/FireworkStarTranslator.java @@ -29,9 +29,9 @@ import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.IntArrayTag; import com.github.steveice10.opennbt.tag.builtin.IntTag; import com.github.steveice10.opennbt.tag.builtin.Tag; +import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.inventory.item.ItemRemapper; -import org.geysermc.geyser.registry.type.ItemMapping; @ItemRemapper public class FireworkStarTranslator extends FireworkBaseTranslator { diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/LeatherArmorTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/LeatherArmorTranslator.java index 9c74e7123..2fb5ec6cb 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/LeatherArmorTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/LeatherArmorTranslator.java @@ -27,10 +27,10 @@ package org.geysermc.geyser.translator.inventory.item.nbt; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.IntTag; +import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.inventory.item.ItemRemapper; import org.geysermc.geyser.translator.inventory.item.NbtItemStackTranslator; -import org.geysermc.geyser.registry.type.ItemMapping; import java.util.Arrays; import java.util.List; diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/LodestoneCompassTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/LodestoneCompassTranslator.java index f4b91165d..8025817f7 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/LodestoneCompassTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/LodestoneCompassTranslator.java @@ -25,11 +25,14 @@ package org.geysermc.geyser.translator.inventory.item.nbt; -import com.github.steveice10.opennbt.tag.builtin.*; +import com.github.steveice10.opennbt.tag.builtin.ByteTag; +import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.github.steveice10.opennbt.tag.builtin.IntTag; +import com.github.steveice10.opennbt.tag.builtin.Tag; +import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.inventory.item.ItemRemapper; import org.geysermc.geyser.translator.inventory.item.NbtItemStackTranslator; -import org.geysermc.geyser.registry.type.ItemMapping; @ItemRemapper public class LodestoneCompassTranslator extends NbtItemStackTranslator { diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/MapItemTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/MapItemTranslator.java index 80b22dafb..8fd44ef65 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/MapItemTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/MapItemTranslator.java @@ -26,10 +26,10 @@ package org.geysermc.geyser.translator.inventory.item.nbt; import com.github.steveice10.opennbt.tag.builtin.*; +import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.inventory.item.ItemRemapper; import org.geysermc.geyser.translator.inventory.item.NbtItemStackTranslator; -import org.geysermc.geyser.registry.type.ItemMapping; @ItemRemapper public class MapItemTranslator extends NbtItemStackTranslator { diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/PlayerHeadTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/PlayerHeadTranslator.java index 680be00fd..d4975f81a 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/PlayerHeadTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/PlayerHeadTranslator.java @@ -28,11 +28,11 @@ package org.geysermc.geyser.translator.inventory.item.nbt; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.StringTag; import com.github.steveice10.opennbt.tag.builtin.Tag; +import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.text.MinecraftLocale; import org.geysermc.geyser.translator.inventory.item.ItemRemapper; import org.geysermc.geyser.translator.inventory.item.NbtItemStackTranslator; -import org.geysermc.geyser.registry.type.ItemMapping; -import org.geysermc.geyser.text.MinecraftLocale; @ItemRemapper public class PlayerHeadTranslator extends NbtItemStackTranslator { @@ -56,7 +56,7 @@ public class PlayerHeadTranslator extends NbtItemStackTranslator { } // Add correct name of player skull // TODO: It's always yellow, even with a custom name. Handle? - String displayName = "\u00a7r\u00a7e" + MinecraftLocale.getLocaleString("block.minecraft.player_head.named", session.getLocale()).replace("%s", name.getValue()); + String displayName = "\u00a7r\u00a7e" + MinecraftLocale.getLocaleString("block.minecraft.player_head.named", session.locale()).replace("%s", name.getValue()); if (!itemTag.contains("display")) { itemTag.put(new CompoundTag("display")); } diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/ShulkerBoxItemTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/ShulkerBoxItemTranslator.java index 1b9acdb96..f95c54e18 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/ShulkerBoxItemTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/ShulkerBoxItemTranslator.java @@ -27,9 +27,9 @@ package org.geysermc.geyser.translator.inventory.item.nbt; import com.github.steveice10.mc.protocol.data.game.Identifier; import com.github.steveice10.opennbt.tag.builtin.*; +import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.inventory.item.ItemRemapper; -import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.translator.inventory.item.ItemTranslator; import org.geysermc.geyser.translator.inventory.item.NbtItemStackTranslator; import org.geysermc.geyser.util.MathUtils; diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/TropicalFishBucketTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/TropicalFishBucketTranslator.java index dbacc75fe..6313dc362 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/TropicalFishBucketTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/TropicalFishBucketTranslator.java @@ -31,12 +31,12 @@ import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.Style; import net.kyori.adventure.text.format.TextDecoration; import org.geysermc.geyser.entity.type.living.animal.TropicalFishEntity; -import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.translator.inventory.item.ItemRemapper; -import org.geysermc.geyser.translator.text.MessageTranslator; -import org.geysermc.geyser.translator.inventory.item.NbtItemStackTranslator; import org.geysermc.geyser.registry.type.ItemMapping; +import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.MinecraftLocale; +import org.geysermc.geyser.translator.inventory.item.ItemRemapper; +import org.geysermc.geyser.translator.inventory.item.NbtItemStackTranslator; +import org.geysermc.geyser.translator.text.MessageTranslator; import java.util.ArrayList; import java.util.List; @@ -50,7 +50,7 @@ public class TropicalFishBucketTranslator extends NbtItemStackTranslator { public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemMapping mapping) { // Prevent name from appearing as "Bucket of" itemTag.put(new ByteTag("AppendCustomName", (byte) 1)); - itemTag.put(new StringTag("CustomName", MinecraftLocale.getLocaleString("entity.minecraft.tropical_fish", session.getLocale()))); + itemTag.put(new StringTag("CustomName", MinecraftLocale.getLocaleString("entity.minecraft.tropical_fish", session.locale()))); // Add Java's client side lore tag Tag bucketVariantTag = itemTag.get("BucketVariantTag"); if (bucketVariantTag instanceof IntTag) { @@ -66,10 +66,10 @@ public class TropicalFishBucketTranslator extends NbtItemStackTranslator { int predefinedVariantId = TropicalFishEntity.getPredefinedId(varNumber); if (predefinedVariantId != -1) { Component tooltip = Component.translatable("entity.minecraft.tropical_fish.predefined." + predefinedVariantId, LORE_STYLE); - lore.add(0, new StringTag("", MessageTranslator.convertMessage(tooltip, session.getLocale()))); + lore.add(0, new StringTag("", MessageTranslator.convertMessage(tooltip, session.locale()))); } else { Component typeTooltip = Component.translatable("entity.minecraft.tropical_fish.type." + TropicalFishEntity.getVariantName(varNumber), LORE_STYLE); - lore.add(0, new StringTag("", MessageTranslator.convertMessage(typeTooltip, session.getLocale()))); + lore.add(0, new StringTag("", MessageTranslator.convertMessage(typeTooltip, session.locale()))); byte baseColor = TropicalFishEntity.getBaseColor(varNumber); byte patternColor = TropicalFishEntity.getPatternColor(varNumber); @@ -78,7 +78,7 @@ public class TropicalFishBucketTranslator extends NbtItemStackTranslator { colorTooltip = colorTooltip.append(Component.text(", ", LORE_STYLE)) .append(Component.translatable("color.minecraft." + TropicalFishEntity.getColorName(patternColor), LORE_STYLE)); } - lore.add(1, new StringTag("", MessageTranslator.convertMessage(colorTooltip, session.getLocale()))); + lore.add(1, new StringTag("", MessageTranslator.convertMessage(colorTooltip, session.locale()))); } ListTag loreTag = displayTag.get("Lore"); diff --git a/core/src/main/java/org/geysermc/geyser/translator/level/BiomeTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/level/BiomeTranslator.java index 537c93a41..3e47bfc37 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/level/BiomeTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/level/BiomeTranslator.java @@ -30,18 +30,21 @@ import com.github.steveice10.mc.protocol.data.game.chunk.DataPalette; import com.github.steveice10.mc.protocol.data.game.chunk.palette.GlobalPalette; import com.github.steveice10.mc.protocol.data.game.chunk.palette.Palette; import com.github.steveice10.mc.protocol.data.game.chunk.palette.SingletonPalette; -import com.github.steveice10.opennbt.tag.builtin.*; +import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.github.steveice10.opennbt.tag.builtin.IntTag; +import com.github.steveice10.opennbt.tag.builtin.ListTag; +import com.github.steveice10.opennbt.tag.builtin.StringTag; import it.unimi.dsi.fastutil.ints.Int2IntMap; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; import it.unimi.dsi.fastutil.ints.IntLists; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.level.chunk.BlockStorage; import org.geysermc.geyser.level.chunk.GeyserChunkSection; import org.geysermc.geyser.level.chunk.bitarray.BitArray; import org.geysermc.geyser.level.chunk.bitarray.BitArrayVersion; import org.geysermc.geyser.level.chunk.bitarray.SingletonBitArray; import org.geysermc.geyser.registry.Registries; +import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.util.JavaCodecEntry; import org.geysermc.geyser.util.MathUtils; diff --git a/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/CampfireBlockEntityTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/CampfireBlockEntityTranslator.java index 53e1af8a5..6ec0effca 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/CampfireBlockEntityTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/CampfireBlockEntityTranslator.java @@ -30,7 +30,7 @@ import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.ListTag; import com.nukkitx.nbt.NbtMap; import com.nukkitx.nbt.NbtMapBuilder; -import org.geysermc.geyser.network.MinecraftProtocol; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.type.ItemMapping; @@ -48,7 +48,7 @@ public class CampfireBlockEntityTranslator extends BlockEntityTranslator { protected NbtMap getItem(CompoundTag tag) { // TODO: Version independent mappings - ItemMapping mapping = Registries.ITEMS.forVersion(MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()).getMapping((String) tag.get("id").getValue()); + ItemMapping mapping = Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()).getMapping((String) tag.get("id").getValue()); NbtMapBuilder tagBuilder = NbtMap.builder() .putString("Name", mapping.getBedrockIdentifier()) .putByte("Count", (byte) tag.get("Count").getValue()) diff --git a/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/DoubleChestBlockEntityTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/DoubleChestBlockEntityTranslator.java index f5ec3607c..0836b1e59 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/DoubleChestBlockEntityTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/DoubleChestBlockEntityTranslator.java @@ -29,9 +29,9 @@ import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityType; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.nukkitx.math.vector.Vector3i; import com.nukkitx.nbt.NbtMapBuilder; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.level.block.DoubleChestValue; +import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.util.BlockEntityUtils; /** diff --git a/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/FlowerPotBlockEntityTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/FlowerPotBlockEntityTranslator.java index 845e2e429..ed1a9e82b 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/FlowerPotBlockEntityTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/FlowerPotBlockEntityTranslator.java @@ -29,8 +29,8 @@ import com.nukkitx.math.vector.Vector3i; import com.nukkitx.nbt.NbtMap; import com.nukkitx.nbt.NbtMapBuilder; import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.level.block.BlockStateValues; +import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.util.BlockEntityUtils; public class FlowerPotBlockEntityTranslator implements BedrockOnlyBlockEntity { diff --git a/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/PistonBlockEntity.java b/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/PistonBlockEntity.java index f6561ccbe..28e30d6be 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/PistonBlockEntity.java +++ b/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/PistonBlockEntity.java @@ -37,16 +37,18 @@ import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import lombok.Getter; import org.geysermc.common.PlatformType; +import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.level.physics.Axis; -import org.geysermc.geyser.level.physics.Direction; -import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.session.cache.PistonCache; import org.geysermc.geyser.level.physics.BoundingBox; import org.geysermc.geyser.level.physics.CollisionManager; -import org.geysermc.geyser.translator.collision.BlockCollision; -import org.geysermc.geyser.level.block.BlockStateValues; +import org.geysermc.geyser.level.physics.Direction; import org.geysermc.geyser.registry.Registries; -import org.geysermc.geyser.util.*; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.session.cache.PistonCache; +import org.geysermc.geyser.translator.collision.BlockCollision; +import org.geysermc.geyser.util.BlockEntityUtils; +import org.geysermc.geyser.util.BlockUtils; +import org.geysermc.geyser.util.ChunkUtils; import java.util.LinkedList; import java.util.Map; diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockAdventureSettingsTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockAdventureSettingsTranslator.java index 641161127..aabc39e12 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockAdventureSettingsTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockAdventureSettingsTranslator.java @@ -25,10 +25,7 @@ package org.geysermc.geyser.translator.protocol.bedrock; -import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; -import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundPlayerAbilitiesPacket; import com.nukkitx.protocol.bedrock.data.AdventureSetting; -import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.packet.AdventureSettingsPacket; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; @@ -40,19 +37,6 @@ public class BedrockAdventureSettingsTranslator extends PacketTranslator { + + @Override + public void translate(GeyserSession session, RequestAbilityPacket packet) { + // Gatekeep to 1.19.30 so older versions don't fire twice + if (!GameProtocol.supports1_19_30(session)) { + return; + } + + if (packet.getAbility() == Ability.FLYING) { + handle(session, packet.isBoolValue()); + } + } + + //FIXME remove after pre-1.19.30 support is dropped and merge into main method + static void handle(GeyserSession session, boolean isFlying) { + if (!isFlying && session.getGameMode() == GameMode.SPECTATOR) { + // We should always be flying in spectator mode + session.sendAdventureSettings(); + return; + } else if (isFlying && session.getPlayerEntity().getFlag(EntityFlag.SWIMMING) && session.getCollisionManager().isPlayerInWater()) { + // As of 1.18.1, Java Edition cannot fly while in water, but it can fly while crawling + // If this isn't present, swimming on a 1.13.2 server and then attempting to fly will put you into a flying/swimming state that is invalid on JE + session.sendAdventureSettings(); + return; + } + + session.setFlying(isFlying); + ServerboundPlayerAbilitiesPacket abilitiesPacket = new ServerboundPlayerAbilitiesPacket(isFlying); + session.sendDownstreamPacket(abilitiesPacket); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockSetLocalPlayerAsInitializedTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockSetLocalPlayerAsInitializedTranslator.java index 8641a35ff..121bfd065 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockSetLocalPlayerAsInitializedTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockSetLocalPlayerAsInitializedTranslator.java @@ -26,7 +26,7 @@ package org.geysermc.geyser.translator.protocol.bedrock; import com.nukkitx.protocol.bedrock.packet.SetLocalPlayerAsInitializedPacket; -import org.geysermc.geyser.session.auth.AuthType; +import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; @@ -41,10 +41,10 @@ public class BedrockSetLocalPlayerAsInitializedTranslator extends PacketTranslat if (!session.getUpstream().isInitialized()) { session.getUpstream().setInitialized(true); - if (session.getRemoteAuthType() == AuthType.ONLINE) { + if (session.remoteServer().authType() == AuthType.ONLINE) { if (!session.isLoggedIn()) { - if (session.getGeyser().getConfig().getSavedUserLogins().contains(session.name())) { - if (session.getGeyser().refreshTokenFor(session.name()) == null) { + if (session.getGeyser().getConfig().getSavedUserLogins().contains(session.bedrockUsername())) { + if (session.getGeyser().refreshTokenFor(session.bedrockUsername()) == null) { LoginEncryptionUtils.buildAndShowConsentWindow(session); } else { // If the refresh token is not null and we're here, then the refresh token expired diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockShowCreditsTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockShowCreditsTranslator.java index ca6ac09dd..6fa271010 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockShowCreditsTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockShowCreditsTranslator.java @@ -25,13 +25,12 @@ package org.geysermc.geyser.translator.protocol.bedrock; -import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.translator.protocol.PacketTranslator; -import org.geysermc.geyser.translator.protocol.Translator; - import com.github.steveice10.mc.protocol.data.game.ClientCommand; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.ServerboundClientCommandPacket; import com.nukkitx.protocol.bedrock.packet.ShowCreditsPacket; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.translator.protocol.PacketTranslator; +import org.geysermc.geyser.translator.protocol.Translator; @Translator(packet = ShowCreditsPacket.class) public class BedrockShowCreditsTranslator extends PacketTranslator { diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockMovePlayerTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockMovePlayerTranslator.java index 17d424b00..6078b7ebd 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockMovePlayerTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockMovePlayerTranslator.java @@ -163,7 +163,7 @@ public class BedrockMovePlayerTranslator extends PacketTranslator 300) { - session.getGeyser().getLogger().debug(ChatColor.RED + session.name() + " moved too quickly." + + session.getGeyser().getLogger().debug(ChatColor.RED + session.bedrockUsername() + " moved too quickly." + " current position: " + currentPosition + ", new position: " + newPosition); return false; diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaBossEventTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaBossEventTranslator.java index 1011eb739..30d6aa017 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaBossEventTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaBossEventTranslator.java @@ -25,13 +25,12 @@ package org.geysermc.geyser.translator.protocol.java; +import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundBossEventPacket; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.BossBar; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; -import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundBossEventPacket; - @Translator(packet = ClientboundBossEventPacket.class) public class JavaBossEventTranslator extends PacketTranslator { diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaChangeDifficultyTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaChangeDifficultyTranslator.java index fdc2fa2fb..6504959dc 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaChangeDifficultyTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaChangeDifficultyTranslator.java @@ -26,10 +26,10 @@ package org.geysermc.geyser.translator.protocol.java; import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundChangeDifficultyPacket; +import com.nukkitx.protocol.bedrock.packet.SetDifficultyPacket; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; -import com.nukkitx.protocol.bedrock.packet.SetDifficultyPacket; @Translator(packet = ClientboundChangeDifficultyPacket.class) public class JavaChangeDifficultyTranslator extends PacketTranslator { diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaCommandsTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaCommandsTranslator.java index c9f192d3f..3fa43c788 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaCommandsTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaCommandsTranslator.java @@ -45,13 +45,14 @@ import lombok.Getter; import lombok.ToString; import net.kyori.adventure.text.format.NamedTextColor; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.command.CommandManager; -import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.translator.protocol.PacketTranslator; -import org.geysermc.geyser.translator.protocol.Translator; +import org.geysermc.geyser.api.event.downstream.ServerDefineCommandsEvent; +import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.inventory.item.Enchantment; import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.Registries; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.translator.protocol.PacketTranslator; +import org.geysermc.geyser.translator.protocol.Translator; import org.geysermc.geyser.util.EntityUtils; import java.util.*; @@ -115,7 +116,7 @@ public class JavaCommandsTranslator extends PacketTranslator commandData = new ArrayList<>(); IntSet commandNodes = new IntOpenHashSet(); @@ -144,15 +145,20 @@ public class JavaCommandsTranslator extends PacketTranslator new HashSet<>()).add(node.getName().toLowerCase()); } + ServerDefineCommandsEvent event = new ServerDefineCommandsEvent(session, commands.keySet()); + session.getGeyser().eventBus().fire(event); + if (event.isCancelled()) { + return; + } + // The command flags, not sure what these do apart from break things List flags = Collections.emptyList(); // Loop through all the found commands - for (Map.Entry> entry : commands.entrySet()) { String commandName = entry.getValue().iterator().next(); // We know this has a value @@ -248,7 +254,7 @@ public class JavaCommandsTranslator extends PacketTranslator { @@ -40,7 +40,7 @@ public class JavaGameProfileTranslator extends PacketTranslator, IntSet> squashedOptions = new HashMap<>(); + private ItemDescriptorWithCount[][] combinations(GeyserSession session, Ingredient[] ingredients) { + Map, IntSet> squashedOptions = new HashMap<>(); for (int i = 0; i < ingredients.length; i++) { if (ingredients[i].getOptions().length == 0) { - squashedOptions.computeIfAbsent(Collections.singleton(ItemData.AIR), k -> new IntOpenHashSet()).add(i); + squashedOptions.computeIfAbsent(Collections.singleton(ItemDescriptorWithCount.EMPTY), k -> new IntOpenHashSet()).add(i); continue; } Ingredient ingredient = ingredients[i]; - Map> groupedByIds = Arrays.stream(ingredient.getOptions()) - .map(item -> ItemTranslator.translateToBedrock(session, item)) - .collect(Collectors.groupingBy(item -> new GroupedItem(item.getId(), item.getCount(), item.getTag()))); - Set optionSet = new HashSet<>(groupedByIds.size()); - for (Map.Entry> entry : groupedByIds.entrySet()) { + Map> groupedByIds = Arrays.stream(ingredient.getOptions()) + .map(item -> ItemDescriptorWithCount.fromItem(ItemTranslator.translateToBedrock(session, item))) + .collect(Collectors.groupingBy(item -> new GroupedItem(((DefaultDescriptor) item.getDescriptor()).getItemId(), item.getCount()))); + Set optionSet = new HashSet<>(groupedByIds.size()); + for (Map.Entry> entry : groupedByIds.entrySet()) { if (entry.getValue().size() > 1) { GroupedItem groupedItem = entry.getKey(); int idCount = 0; @@ -231,42 +236,38 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator new IntOpenHashSet()).add(i); } int totalCombinations = 1; - for (Set optionSet : squashedOptions.keySet()) { + for (Set optionSet : squashedOptions.keySet()) { totalCombinations *= optionSet.size(); } if (totalCombinations > 500) { - ItemData[] translatedItems = new ItemData[ingredients.length]; + ItemDescriptorWithCount[] translatedItems = new ItemDescriptorWithCount[ingredients.length]; for (int i = 0; i < ingredients.length; i++) { if (ingredients[i].getOptions().length > 0) { - translatedItems[i] = ItemTranslator.translateToBedrock(session, ingredients[i].getOptions()[0]); + translatedItems[i] = ItemDescriptorWithCount.fromItem(ItemTranslator.translateToBedrock(session, ingredients[i].getOptions()[0])); } else { - translatedItems[i] = ItemData.AIR; + translatedItems[i] = ItemDescriptorWithCount.EMPTY; } } - return new ItemData[][]{translatedItems}; + return new ItemDescriptorWithCount[][]{translatedItems}; } - List> sortedSets = new ArrayList<>(squashedOptions.keySet()); + List> sortedSets = new ArrayList<>(squashedOptions.keySet()); sortedSets.sort(Comparator.comparing(Set::size, Comparator.reverseOrder())); - ItemData[][] combinations = new ItemData[totalCombinations][ingredients.length]; + ItemDescriptorWithCount[][] combinations = new ItemDescriptorWithCount[totalCombinations][ingredients.length]; int x = 1; - for (Set set : sortedSets) { + for (Set set : sortedSets) { IntSet slotSet = squashedOptions.get(set); int i = 0; - for (ItemData item : set) { + for (ItemDescriptorWithCount item : set) { for (int j = 0; j < totalCombinations / set.size(); j++) { final int comboIndex = (i * x) + (j % x) + ((j / x) * set.size() * x); for (int slot : slotSet) { @@ -285,6 +286,5 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator { diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerCombatKillTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerCombatKillTranslator.java index 0f1fd4d1b..89be26e4a 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerCombatKillTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerCombatKillTranslator.java @@ -28,7 +28,7 @@ package org.geysermc.geyser.translator.protocol.java.entity.player; import com.github.steveice10.mc.protocol.packet.ingame.clientbound.entity.player.ClientboundPlayerCombatKillPacket; import com.nukkitx.protocol.bedrock.packet.DeathInfoPacket; import net.kyori.adventure.text.Component; -import org.geysermc.geyser.network.MinecraftProtocol; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; @@ -39,11 +39,11 @@ public class JavaPlayerCombatKillTranslator extends PacketTranslator { diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/spawn/JavaAddPlayerTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/spawn/JavaAddPlayerTranslator.java index c54b75f4f..c4b5aae2d 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/spawn/JavaAddPlayerTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/spawn/JavaAddPlayerTranslator.java @@ -30,10 +30,10 @@ import com.nukkitx.math.vector.Vector3f; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.entity.type.player.PlayerEntity; import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.skin.SkinManager; +import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; -import org.geysermc.geyser.text.GeyserLocale; -import org.geysermc.geyser.skin.SkinManager; @Translator(packet = ClientboundAddPlayerPacket.class) public class JavaAddPlayerTranslator extends PacketTranslator { diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaContainerSetContentTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaContainerSetContentTranslator.java index f905cd661..619825338 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaContainerSetContentTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaContainerSetContentTranslator.java @@ -30,9 +30,9 @@ import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.inventory.Inventory; import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.translator.inventory.InventoryTranslator; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; -import org.geysermc.geyser.translator.inventory.InventoryTranslator; import org.geysermc.geyser.util.InventoryUtils; @Translator(packet = ClientboundContainerSetContentPacket.class) @@ -48,7 +48,7 @@ public class JavaContainerSetContentTranslator extends PacketTranslator inventorySize) { GeyserImpl geyser = session.getGeyser(); - geyser.getLogger().warning("ClientboundContainerSetContentPacket sent to " + session.name() + geyser.getLogger().warning("ClientboundContainerSetContentPacket sent to " + session.bedrockUsername() + " that exceeds inventory size!"); if (geyser.getConfig().isDebugMode()) { geyser.getLogger().debug(packet); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaContainerSetDataTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaContainerSetDataTranslator.java index 30c2abe25..923b10a26 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaContainerSetDataTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaContainerSetDataTranslator.java @@ -28,9 +28,9 @@ package org.geysermc.geyser.translator.protocol.java.inventory; import com.github.steveice10.mc.protocol.packet.ingame.clientbound.inventory.ClientboundContainerSetDataPacket; import org.geysermc.geyser.inventory.Inventory; import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.translator.inventory.InventoryTranslator; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; -import org.geysermc.geyser.translator.inventory.InventoryTranslator; import org.geysermc.geyser.util.InventoryUtils; @Translator(packet = ClientboundContainerSetDataPacket.class) diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaContainerSetSlotTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaContainerSetSlotTranslator.java index aef8cf8b2..869f0cede 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaContainerSetSlotTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaContainerSetSlotTranslator.java @@ -31,6 +31,7 @@ import com.github.steveice10.mc.protocol.packet.ingame.clientbound.inventory.Cli import com.nukkitx.protocol.bedrock.data.inventory.ContainerId; import com.nukkitx.protocol.bedrock.data.inventory.CraftingData; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; +import com.nukkitx.protocol.bedrock.data.inventory.descriptor.ItemDescriptorWithCount; import com.nukkitx.protocol.bedrock.packet.CraftingDataPacket; import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket; import org.geysermc.geyser.GeyserImpl; @@ -76,7 +77,7 @@ public class JavaContainerSetSlotTranslator extends PacketTranslator= inventory.getSize()) { GeyserImpl geyser = session.getGeyser(); - geyser.getLogger().warning("ClientboundContainerSetSlotPacket sent to " + session.name() + geyser.getLogger().warning("ClientboundContainerSetSlotPacket sent to " + session.bedrockUsername() + " that exceeds inventory size!"); if (geyser.getConfig().isDebugMode()) { geyser.getLogger().debug(packet); @@ -186,7 +187,7 @@ public class JavaContainerSetSlotTranslator extends PacketTranslator { @@ -153,7 +153,7 @@ public class JavaGameEventTranslator extends PacketTranslator { diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaSetChunkCacheCenterTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaSetChunkCacheCenterTranslator.java index 7b7223316..13e8cd62b 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaSetChunkCacheCenterTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaSetChunkCacheCenterTranslator.java @@ -26,11 +26,11 @@ package org.geysermc.geyser.translator.protocol.java.level; import com.github.steveice10.mc.protocol.packet.ingame.clientbound.level.ClientboundSetChunkCacheCenterPacket; +import com.nukkitx.math.vector.Vector3i; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; import org.geysermc.geyser.util.ChunkUtils; -import com.nukkitx.math.vector.Vector3i; @Translator(packet = ClientboundSetChunkCacheCenterPacket.class) public class JavaSetChunkCacheCenterTranslator extends PacketTranslator { diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/scoreboard/JavaSetDisplayObjectiveTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/scoreboard/JavaSetDisplayObjectiveTranslator.java index b34e54db6..74f063e44 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/scoreboard/JavaSetDisplayObjectiveTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/scoreboard/JavaSetDisplayObjectiveTranslator.java @@ -26,12 +26,12 @@ package org.geysermc.geyser.translator.protocol.java.scoreboard; import com.github.steveice10.mc.protocol.packet.ingame.clientbound.scoreboard.ClientboundSetDisplayObjectivePacket; +import org.geysermc.geyser.scoreboard.Scoreboard; +import org.geysermc.geyser.scoreboard.ScoreboardUpdater; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.WorldCache; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; -import org.geysermc.geyser.scoreboard.Scoreboard; -import org.geysermc.geyser.scoreboard.ScoreboardUpdater; @Translator(packet = ClientboundSetDisplayObjectivePacket.class) public class JavaSetDisplayObjectiveTranslator extends PacketTranslator { diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/scoreboard/JavaSetObjectiveTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/scoreboard/JavaSetObjectiveTranslator.java index 33da27a88..3b009a2a5 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/scoreboard/JavaSetObjectiveTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/scoreboard/JavaSetObjectiveTranslator.java @@ -31,15 +31,15 @@ import com.github.steveice10.mc.protocol.packet.ingame.clientbound.scoreboard.Cl import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserLogger; import org.geysermc.geyser.entity.type.player.PlayerEntity; +import org.geysermc.geyser.scoreboard.Objective; +import org.geysermc.geyser.scoreboard.Scoreboard; +import org.geysermc.geyser.scoreboard.ScoreboardUpdater; +import org.geysermc.geyser.scoreboard.UpdateType; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.WorldCache; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; import org.geysermc.geyser.translator.text.MessageTranslator; -import org.geysermc.geyser.scoreboard.Objective; -import org.geysermc.geyser.scoreboard.Scoreboard; -import org.geysermc.geyser.scoreboard.ScoreboardUpdater; -import org.geysermc.geyser.scoreboard.UpdateType; @Translator(packet = ClientboundSetObjectivePacket.class) public class JavaSetObjectiveTranslator extends PacketTranslator { diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/scoreboard/JavaSetPlayerTeamTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/scoreboard/JavaSetPlayerTeamTranslator.java index 40129701d..f942b6f09 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/scoreboard/JavaSetPlayerTeamTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/scoreboard/JavaSetPlayerTeamTranslator.java @@ -31,14 +31,14 @@ import com.github.steveice10.mc.protocol.data.game.scoreboard.TeamColor; import com.github.steveice10.mc.protocol.packet.ingame.clientbound.scoreboard.ClientboundSetPlayerTeamPacket; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserLogger; -import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.translator.protocol.PacketTranslator; -import org.geysermc.geyser.translator.protocol.Translator; -import org.geysermc.geyser.translator.text.MessageTranslator; import org.geysermc.geyser.scoreboard.Scoreboard; import org.geysermc.geyser.scoreboard.ScoreboardUpdater; import org.geysermc.geyser.scoreboard.Team; import org.geysermc.geyser.scoreboard.UpdateType; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.translator.protocol.PacketTranslator; +import org.geysermc.geyser.translator.protocol.Translator; +import org.geysermc.geyser.translator.text.MessageTranslator; import java.util.Arrays; import java.util.Set; @@ -67,8 +67,8 @@ public class JavaSetPlayerTeamTranslator extends PacketTranslator { diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/title/JavaSetActionBarTextTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/title/JavaSetActionBarTextTranslator.java index c6473e375..a534ec372 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/title/JavaSetActionBarTextTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/title/JavaSetActionBarTextTranslator.java @@ -41,7 +41,7 @@ public class JavaSetActionBarTextTranslator extends PacketTranslator 256) { - session.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.chat.too_long", session.getLocale(), message.length())); + session.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.chat.too_long", session.locale(), message.length())); return true; } diff --git a/core/src/main/java/org/geysermc/geyser/util/ItemUtils.java b/core/src/main/java/org/geysermc/geyser/util/ItemUtils.java index 37c4609fe..92967a10b 100644 --- a/core/src/main/java/org/geysermc/geyser/util/ItemUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/ItemUtils.java @@ -25,7 +25,10 @@ package org.geysermc.geyser.util; -import com.github.steveice10.opennbt.tag.builtin.*; +import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.github.steveice10.opennbt.tag.builtin.ListTag; +import com.github.steveice10.opennbt.tag.builtin.StringTag; +import com.github.steveice10.opennbt.tag.builtin.Tag; import it.unimi.dsi.fastutil.ints.Int2IntMap; import org.geysermc.geyser.session.GeyserSession; diff --git a/core/src/main/java/org/geysermc/geyser/util/LoginEncryptionUtils.java b/core/src/main/java/org/geysermc/geyser/util/LoginEncryptionUtils.java index 372d40258..8d832f8fa 100644 --- a/core/src/main/java/org/geysermc/geyser/util/LoginEncryptionUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/LoginEncryptionUtils.java @@ -230,7 +230,7 @@ public class LoginEncryptionUtils { session.sendForm( SimpleForm.builder() - .translator(GeyserLocale::getPlayerLocaleString, session.getLocale()) + .translator(GeyserLocale::getPlayerLocaleString, session.locale()) .title("geyser.auth.login.form.notice.title") .content("geyser.auth.login.form.notice.desc") .optionalButton("geyser.auth.login.form.notice.btn_login.mojang", isPasswordAuthEnabled) @@ -245,17 +245,11 @@ public class LoginEncryptionUtils { } if (response.clickedButtonId() == 1) { - if (isPasswordAuthEnabled) { - session.setMicrosoftAccount(true); - buildAndShowMicrosoftAuthenticationWindow(session); - } else { - // Just show the OAuth code - session.authenticateWithMicrosoftCode(); - } + session.authenticateWithMicrosoftCode(); return; } - session.disconnect(GeyserLocale.getPlayerLocaleString("geyser.auth.login.form.disconnect", session.getLocale())); + session.disconnect(GeyserLocale.getPlayerLocaleString("geyser.auth.login.form.disconnect", session.locale())); })); } @@ -263,9 +257,11 @@ public class LoginEncryptionUtils { * Build a window that explains the user's credentials will be saved to the system. */ public static void buildAndShowConsentWindow(GeyserSession session) { + String locale = session.locale(); + session.sendForm( SimpleForm.builder() - .translator(LoginEncryptionUtils::translate, session.getLocale()) + .translator(LoginEncryptionUtils::translate, locale) .title("%gui.signIn") .content(""" geyser.auth.login.save_token.warning @@ -278,9 +274,11 @@ public class LoginEncryptionUtils { } public static void buildAndShowTokenExpiredWindow(GeyserSession session) { + String locale = session.locale(); + session.sendForm( SimpleForm.builder() - .translator(LoginEncryptionUtils::translate, session.getLocale()) + .translator(LoginEncryptionUtils::translate, locale) .title("geyser.auth.login.form.expired") .content(""" geyser.auth.login.save_token.expired @@ -305,49 +303,22 @@ public class LoginEncryptionUtils { public static void buildAndShowLoginDetailsWindow(GeyserSession session) { session.sendForm( CustomForm.builder() - .translator(GeyserLocale::getPlayerLocaleString, session.getLocale()) + .translator(GeyserLocale::getPlayerLocaleString, session.locale()) .title("geyser.auth.login.form.details.title") .label("geyser.auth.login.form.details.desc") .input("geyser.auth.login.form.details.email", "account@geysermc.org", "") .input("geyser.auth.login.form.details.pass", "123456", "") .invalidResultHandler(() -> buildAndShowLoginDetailsWindow(session)) - .closedResultHandler(() -> { - if (session.isMicrosoftAccount()) { - buildAndShowMicrosoftAuthenticationWindow(session); - } else { - buildAndShowLoginWindow(session); - } - }) + .closedResultHandler(() -> buildAndShowLoginWindow(session)) .validResultHandler((response) -> session.authenticate(response.next(), response.next()))); } - /** - * Prompts the user between either OAuth code login or manual password authentication - */ - public static void buildAndShowMicrosoftAuthenticationWindow(GeyserSession session) { - session.sendForm( - SimpleForm.builder() - .translator(GeyserLocale::getPlayerLocaleString, session.getLocale()) - .title("geyser.auth.login.form.notice.btn_login.microsoft") - .button("geyser.auth.login.method.browser") - .button("geyser.auth.login.method.password") - .button("geyser.auth.login.form.notice.btn_disconnect") - .closedOrInvalidResultHandler(() -> buildAndShowLoginWindow(session)) - .validResultHandler((response) -> { - if (response.clickedButtonId() == 0) { - session.authenticateWithMicrosoftCode(); - } else if (response.clickedButtonId() == 1) { - buildAndShowLoginDetailsWindow(session); - } else { - session.disconnect(GeyserLocale.getPlayerLocaleString("geyser.auth.login.form.disconnect", session.getLocale())); - } - })); - } - /** * Shows the code that a user must input into their browser */ public static void buildAndShowMicrosoftCodeWindow(GeyserSession session, MsaAuthenticationService.MsCodeResponse msCode) { + String locale = session.locale(); + StringBuilder message = new StringBuilder("%xbox.signin.website\n") .append(ChatColor.AQUA) .append("%xbox.signin.url") @@ -359,7 +330,7 @@ public class LoginEncryptionUtils { if (timeout != 0) { message.append("\n\n") .append(ChatColor.RESET) - .append(GeyserLocale.getPlayerLocaleString("geyser.auth.login.timeout", session.getLocale(), String.valueOf(timeout))); + .append(GeyserLocale.getPlayerLocaleString("geyser.auth.login.timeout", session.locale(), String.valueOf(timeout))); } session.sendForm( @@ -368,10 +339,10 @@ public class LoginEncryptionUtils { .content(message.toString()) .button1("%gui.done") .button2("%menu.disconnect") - .closedOrInvalidResultHandler(() -> buildAndShowMicrosoftAuthenticationWindow(session)) + .closedOrInvalidResultHandler(() -> buildAndShowLoginWindow(session)) .validResultHandler((response) -> { if (response.clickedButtonId() == 1) { - session.disconnect(GeyserLocale.getPlayerLocaleString("geyser.auth.login.form.disconnect", session.getLocale())); + session.disconnect(GeyserLocale.getPlayerLocaleString("geyser.auth.login.form.disconnect", locale)); } }) ); diff --git a/core/src/main/java/org/geysermc/geyser/util/NewsHandler.java b/core/src/main/java/org/geysermc/geyser/util/NewsHandler.java index 31e3116d5..71e7c99c1 100644 --- a/core/src/main/java/org/geysermc/geyser/util/NewsHandler.java +++ b/core/src/main/java/org/geysermc/geyser/util/NewsHandler.java @@ -29,15 +29,15 @@ import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonSyntaxException; -import org.geysermc.geyser.Constants; -import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.GeyserLogger; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.floodgate.news.NewsItem; import org.geysermc.floodgate.news.NewsItemAction; import org.geysermc.floodgate.news.data.AnnouncementData; import org.geysermc.floodgate.news.data.BuildSpecificData; import org.geysermc.floodgate.news.data.CheckAfterData; +import org.geysermc.geyser.Constants; +import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.GeyserLogger; +import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.ChatColor; import java.util.*; diff --git a/core/src/main/java/org/geysermc/geyser/util/SettingsUtils.java b/core/src/main/java/org/geysermc/geyser/util/SettingsUtils.java index 10c4b863c..483a4372f 100644 --- a/core/src/main/java/org/geysermc/geyser/util/SettingsUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/SettingsUtils.java @@ -44,7 +44,7 @@ public class SettingsUtils { */ public static CustomForm buildForm(GeyserSession session) { // Cache the language for cleaner access - String language = session.getLocale(); + String language = session.locale(); CustomForm.Builder builder = CustomForm.builder() .translator(SettingsUtils::translateEntry, language) diff --git a/core/src/main/java/org/geysermc/geyser/util/StatisticsUtils.java b/core/src/main/java/org/geysermc/geyser/util/StatisticsUtils.java index 149656fd9..f01670106 100644 --- a/core/src/main/java/org/geysermc/geyser/util/StatisticsUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/StatisticsUtils.java @@ -52,7 +52,7 @@ public class StatisticsUtils { */ public static void buildAndSendStatisticsMenu(GeyserSession session) { // Cache the language for cleaner access - String language = session.getLocale(); + String language = session.locale(); session.sendForm( SimpleForm.builder() diff --git a/core/src/main/java/org/geysermc/geyser/util/VersionCheckUtils.java b/core/src/main/java/org/geysermc/geyser/util/VersionCheckUtils.java index b1f97989f..049d78619 100644 --- a/core/src/main/java/org/geysermc/geyser/util/VersionCheckUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/VersionCheckUtils.java @@ -34,8 +34,8 @@ import net.kyori.adventure.text.format.TextDecoration; import org.geysermc.geyser.Constants; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserLogger; -import org.geysermc.geyser.command.CommandSender; -import org.geysermc.geyser.network.MinecraftProtocol; +import org.geysermc.geyser.command.GeyserCommandSource; +import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.text.GeyserLocale; import java.util.concurrent.CompletableFuture; @@ -54,13 +54,13 @@ public final class VersionCheckUtils { } } - public static void checkForGeyserUpdate(Supplier recipient) { + public static void checkForGeyserUpdate(Supplier recipient) { CompletableFuture.runAsync(() -> { try { JsonNode json = WebUtils.getJson("https://api.geysermc.org/v2/versions/geyser"); JsonNode bedrock = json.get("bedrock").get("protocol"); int protocolVersion = bedrock.get("id").asInt(); - if (MinecraftProtocol.getBedrockCodec(protocolVersion) != null) { + if (GameProtocol.getBedrockCodec(protocolVersion) != null) { // We support the latest version! No need to print a message. return; } @@ -68,11 +68,11 @@ public final class VersionCheckUtils { final String newBedrockVersion = bedrock.get("name").asText(); // Delayed for two reasons: save unnecessary processing, and wait to load locale if this is on join. - CommandSender sender = recipient.get(); + GeyserCommandSource sender = recipient.get(); // Overarching component is green - geyser.version.new component cannot be green or else the link blue is overshadowed Component message = Component.text().color(NamedTextColor.GREEN) - .append(Component.text(GeyserLocale.getPlayerLocaleString("geyser.version.new", sender.getLocale(), newBedrockVersion)) + .append(Component.text(GeyserLocale.getPlayerLocaleString("geyser.version.new", sender.locale(), newBedrockVersion)) .replaceText(TextReplacementConfig.builder() .match("\\{1\\}") // Replace "Download here: {1}" so we can use fancy text component yesyes .replacement(Component.text() diff --git a/core/src/main/java/org/geysermc/geyser/util/collection/LecternHasBookMap.java b/core/src/main/java/org/geysermc/geyser/util/collection/LecternHasBookMap.java index 73cb68df1..913ea44d5 100644 --- a/core/src/main/java/org/geysermc/geyser/util/collection/LecternHasBookMap.java +++ b/core/src/main/java/org/geysermc/geyser/util/collection/LecternHasBookMap.java @@ -27,9 +27,9 @@ package org.geysermc.geyser.util.collection; import com.nukkitx.math.vector.Vector3i; import com.nukkitx.nbt.NbtMap; +import org.geysermc.geyser.level.WorldManager; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.inventory.LecternInventoryTranslator; -import org.geysermc.geyser.level.WorldManager; import org.geysermc.geyser.util.BlockEntityUtils; /** diff --git a/core/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/core/src/main/resources/META-INF/services/javax.annotation.processing.Processor deleted file mode 100644 index 463d1efad..000000000 --- a/core/src/main/resources/META-INF/services/javax.annotation.processing.Processor +++ /dev/null @@ -1,5 +0,0 @@ -org.geysermc.processor.BlockEntityProcessor -org.geysermc.processor.CollisionRemapperProcessor -org.geysermc.processor.ItemRemapperProcessor -org.geysermc.processor.PacketTranslatorProcessor -org.geysermc.processor.SoundHandlerProcessor \ No newline at end of file diff --git a/core/src/main/resources/git.properties b/core/src/main/resources/git.properties new file mode 100644 index 000000000..f14e55623 --- /dev/null +++ b/core/src/main/resources/git.properties @@ -0,0 +1,7 @@ +git.branch=${branch} +git.build.number=${buildNumber} +git.build.version=${projectVersion} +git.commit.id=${commit} +git.commit.id.abbrev=${commitAbbrev} +git.commit.message.full=${commitMessage} +git.remote.origin.url=${repository} diff --git a/core/src/main/resources/languages b/core/src/main/resources/languages index ad92d550b..51d6f5ba7 160000 --- a/core/src/main/resources/languages +++ b/core/src/main/resources/languages @@ -1 +1 @@ -Subproject commit ad92d550bab49bc46f17db6aa0042035b66a1a10 +Subproject commit 51d6f5ba7d85bfda318879dad34481d9ef4d488d diff --git a/core/src/main/resources/mappings b/core/src/main/resources/mappings index f1c9c2fbb..2c68dab9d 160000 --- a/core/src/main/resources/mappings +++ b/core/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit f1c9c2fbba0e102dc4f8c96dd9485f7ec9768174 +Subproject commit 2c68dab9d751f78b2f5b0298da5e338ad6bc07ca diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 000000000..8f6ac8e85 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,6 @@ +group=org.geysermc +version=2.1.0-SNAPSHOT + +org.gradle.caching=true +org.gradle.parallel=true +org.gradle.vfs.watch=false \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 000000000..41d9927a4 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..41dfb8790 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 000000000..1b6c78733 --- /dev/null +++ b/gradlew @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 000000000..107acd32c --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="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 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/pom.xml b/pom.xml deleted file mode 100644 index c5b293c43..000000000 --- a/pom.xml +++ /dev/null @@ -1,82 +0,0 @@ - - - 4.0.0 - org.geysermc - geyser-parent - 2.0.7-SNAPSHOT - pom - Geyser - Allows for players from Minecraft Bedrock Edition to join Minecraft Java Edition servers. - https://geysermc.org - - - Geyser - UTF-8 - UTF-8 - 16 - 16 - geysermc - https://sonarcloud.io - - - - GeyserMC - https://github.com/GeyserMC/Geyser/blob/master/pom.xml - - - - scm:git:https://github.com/GeyserMC/Geyser.git - scm:git:git@github.com:GeyserMC/Geyser.git - https://github.com/GeyserMC/Geyser - - - - ap - api - bootstrap - common - core - - - - - jitpack.io - https://jitpack.io - - - opencollab-release-repo - https://repo.opencollab.dev/maven-releases/ - - true - - - false - - - - opencollab-snapshot-repo - https://repo.opencollab.dev/maven-snapshots/ - - false - - - true - - - - sonatype - https://oss.sonatype.org/content/repositories/snapshots/ - - - - - - org.projectlombok - lombok - 1.18.20 - provided - - - diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 000000000..f6d13ad0d --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,79 @@ +enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") + +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + // Floodgate, Cumulus etc. + maven("https://repo.opencollab.dev/main") + + // Paper, Velocity + maven("https://repo.papermc.io/repository/maven-public") + // Spigot + maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots") { + mavenContent { snapshotsOnly() } + } + + // BungeeCord + maven("https://oss.sonatype.org/content/repositories/snapshots") { + mavenContent { snapshotsOnly() } + } + + // Minecraft + maven("https://libraries.minecraft.net") { + name = "minecraft" + mavenContent { releasesOnly() } + } + + mavenLocal() + mavenCentral() + + // ViaVersion + maven("https://repo.viaversion.com") { + name = "viaversion" + } + + // Sponge + maven("https://repo.spongepowered.org/repository/maven-public/") + + maven("https://jitpack.io") { + content { includeGroupByRegex("com\\.github\\..*") } + } + + // For Adventure snapshots + maven("https://s01.oss.sonatype.org/content/repositories/snapshots/") + } +} + +pluginManagement { + repositories { + gradlePluginPortal() + } + plugins { + id("net.kyori.blossom") version "1.2.0" + id("net.kyori.indra") + id("net.kyori.indra.git") + } + includeBuild("build-logic") +} + +rootProject.name = "geyser-parent" + +include(":ap") +include(":api") +include(":geyser-api") +include(":bungeecord") +include(":spigot") +include(":sponge") +include(":standalone") +include(":velocity") +include(":common") +include(":core") + +// Specify project dirs +project(":api").projectDir = file("api/base") +project(":geyser-api").projectDir = file("api/geyser") +project(":bungeecord").projectDir = file("bootstrap/bungeecord") +project(":spigot").projectDir = file("bootstrap/spigot") +project(":sponge").projectDir = file("bootstrap/sponge") +project(":standalone").projectDir = file("bootstrap/standalone") +project(":velocity").projectDir = file("bootstrap/velocity") \ No newline at end of file