Merge branch 'master' into feature/new-custom-entity-api

This commit is contained in:
rtm516 2024-03-24 15:38:46 +00:00
commit 086431573b
No known key found for this signature in database
GPG Key ID: 331715B8B007C67A
632 changed files with 62218 additions and 53249 deletions

View File

@ -3,10 +3,12 @@ name: Build
on:
workflow_dispatch:
push:
branches-ignore:
- 'gh-readonly-queue/**'
paths-ignore:
- '.github/ISSUE_TEMPLATE/*.yml'
- '.github/actions/pullrequest.yml'
- '.idea/copyright/*.xml'
- '.idea/copyright/*.xml'
- '.gitignore'
- 'CONTRIBUTING.md'
- 'LICENSE'
@ -19,70 +21,82 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository and submodules
uses: actions/checkout@v3
# See https://github.com/actions/checkout/commits
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
submodules: recursive
- name: Validate Gradle Wrapper
uses: gradle/wrapper-validation-action@v1
# See https://github.com/gradle/wrapper-validation-action/commits
uses: gradle/wrapper-validation-action@699bb18358f12c5b78b37bb0111d3a0e2276e0e2 # v2.1.1
- uses: actions/setup-java@v3
# See https://github.com/actions/setup-java/commits
- uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0
with:
java-version: 17
distribution: temurin
- name: Build
uses: gradle/gradle-build-action@v2
# See https://github.com/gradle/gradle-build-action/commits
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 from https://github.com/gradle/actions/commits
with:
arguments: build
gradle-home-cache-cleanup: true
- name: Archive artifacts (Geyser Fabric)
uses: actions/upload-artifact@v3
# See https://github.com/actions/upload-artifact/commits
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
if: success()
with:
name: Geyser Fabric
path: bootstrap/fabric/build/libs/Geyser-Fabric.jar
path: bootstrap/mod/fabric/build/libs/Geyser-Fabric.jar
if-no-files-found: error
- name: Archive artifacts (Geyser NeoForge)
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
if: success()
with:
name: Geyser NeoForge
path: bootstrap/mod/neoforge/build/libs/Geyser-NeoForge.jar
if-no-files-found: error
- name: Archive artifacts (Geyser Standalone)
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
if: success()
with:
name: Geyser Standalone
path: bootstrap/standalone/build/libs/Geyser-Standalone.jar
if-no-files-found: error
- name: Archive artifacts (Geyser Spigot)
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
if: success()
with:
name: Geyser Spigot
path: bootstrap/spigot/build/libs/Geyser-Spigot.jar
if-no-files-found: error
- name: Archive artifacts (Geyser BungeeCord)
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
if: success()
with:
name: Geyser BungeeCord
path: bootstrap/bungeecord/build/libs/Geyser-BungeeCord.jar
if-no-files-found: error
- name: Archive artifacts (Geyser Sponge)
uses: actions/upload-artifact@v3
if: success()
with:
name: Geyser Sponge
path: bootstrap/sponge/build/libs/Geyser-Sponge.jar
if-no-files-found: error
- name: Archive artifacts (Geyser Velocity)
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
if: success()
with:
name: Geyser Velocity
path: bootstrap/velocity/build/libs/Geyser-Velocity.jar
if-no-files-found: error
- name: Archive artifacts (Geyser ViaProxy)
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
if: success()
with:
name: Geyser ViaProxy
path: bootstrap/viaproxy/build/libs/Geyser-ViaProxy.jar
if-no-files-found: error
- name: Publish to Maven Repository
if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
uses: gradle/gradle-build-action@v2
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
env:
ORG_GRADLE_PROJECT_geysermcUsername: ${{ vars.DEPLOY_USER }}
ORG_GRADLE_PROJECT_geysermcPassword: ${{ secrets.DEPLOY_PASS }}
@ -108,23 +122,34 @@ jobs:
ssh -o StrictHostKeyChecking=no -i id_ecdsa $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP mkdir -p "~/uploads/$project/$GITHUB_RUN_NUMBER/"
# Copy over artifacts
rsync -P -e "ssh -o StrictHostKeyChecking=no -i id_ecdsa" bootstrap/**/build/libs/Geyser-*.jar $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP:~/uploads/$project/$GITHUB_RUN_NUMBER/
rsync -P -e "ssh -o StrictHostKeyChecking=no -i id_ecdsa" bootstrap/mod/**/build/libs/Geyser-*.jar $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP:~/uploads/$project/$GITHUB_RUN_NUMBER/
# Run the build script
# Push the metadata
echo "{\"project\": \"$project\", \"version\": \"$version\", \"id\": $GITHUB_RUN_NUMBER, \"commit\": \"$GITHUB_SHA\"}" > metadata.json
rsync -P -e "ssh -o StrictHostKeyChecking=no -i id_ecdsa" metadata.json $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP:~/uploads/$project/$GITHUB_RUN_NUMBER/
- name: Publish to Modrinth
uses: gradle/gradle-build-action@v2
- name: Publish to Modrinth (Fabric)
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
env:
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
with:
arguments: fabric:modrinth
gradle-home-cache-cleanup: true
- name: Publish to Modrinth (NeoForge)
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
env:
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
with:
arguments: neoforge:modrinth
gradle-home-cache-cleanup: true
- name: Notify Discord
if: ${{ (success() || failure()) && github.repository == 'GeyserMC/Geyser' }}
uses: Tim203/actions-git-discord-webhook@main
# See https://github.com/Tim203/actions-git-discord-webhook/commits
uses: Tim203/actions-git-discord-webhook@70f38ded3aca51635ec978ab4e1a58cd4cd0c2ff
with:
webhook_url: ${{ secrets.DISCORD_WEBHOOK }}
status: ${{ job.status }}

View File

@ -2,28 +2,22 @@ name: Build Pull Request
on:
pull_request:
paths-ignore:
- '.github/ISSUE_TEMPLATE/*.yml'
- '.idea/copyright/*.xml'
- '.gitignore'
- 'CONTRIBUTING.md'
- 'LICENSE'
- 'Jenkinsfile '
- 'README.md'
- 'licenseheader.txt'
merge_group:
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Set up JDK 17
uses: actions/setup-java@v1
# See https://github.com/actions/setup-java/commits
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0
with:
java-version: 17
distribution: temurin
- name: Check if the author has forked the API repo
uses: Kas-tle/ForkFinder@v1.0.1
# See https://github.com/Kas-tle/find-forks-action/commits
uses: Kas-tle/find-forks-action@1b5447d1e3c7a8ed79583dd817cc5399686eed3a
id: find_forks
with:
owner: GeyserMC
@ -31,7 +25,7 @@ jobs:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Use author's API repo if it exists
if: steps.find_forks.outputs.target_branch_found == 'true'
if: ${{ steps.find_forks.outputs.target_branch_found == 'true' }}
env:
API_FORK_URL: ${{ steps.find_forks.outputs.user_fork_url }}
API_FORK_BRANCH: ${{ github.event.pull_request.head.ref }}
@ -41,56 +35,70 @@ jobs:
./gradlew publishToMavenLocal
- name: Checkout repository and submodules
uses: actions/checkout@v3
# See https://github.com/actions/checkout/commits
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
submodules: recursive
path: geyser
- name: Validate Gradle Wrapper
# See https://github.com/gradle/wrapper-validation-action/commits
uses: gradle/wrapper-validation-action@699bb18358f12c5b78b37bb0111d3a0e2276e0e2 # v2.1.1
- name: Build Geyser
uses: gradle/gradle-build-action@v2
# See https://github.com/gradle/gradle-build-action/commits
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 from https://github.com/gradle/actions/commits
with:
arguments: build
build-root-directory: geyser
- name: Archive artifacts (Geyser Fabric)
uses: actions/upload-artifact@v3
# See https://github.com/actions/upload-artifact/commits
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
if: success()
with:
name: Geyser Fabric
path: geyser/bootstrap/fabric/build/libs/Geyser-Fabric.jar
path: geyser/bootstrap/mod/fabric/build/libs/Geyser-Fabric.jar
if-no-files-found: error
- name: Archive artifacts (Geyser NeoForge)
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
if: success()
with:
name: Geyser NeoForge
path: geyser/bootstrap/mod/neoforge/build/libs/Geyser-NeoForge.jar
if-no-files-found: error
- name: Archive artifacts (Geyser Standalone)
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
if: success()
with:
name: Geyser Standalone
path: geyser/bootstrap/standalone/build/libs/Geyser-Standalone.jar
if-no-files-found: error
- name: Archive artifacts (Geyser Spigot)
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
if: success()
with:
name: Geyser Spigot
path: geyser/bootstrap/spigot/build/libs/Geyser-Spigot.jar
if-no-files-found: error
- name: Archive artifacts (Geyser BungeeCord)
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
if: success()
with:
name: Geyser BungeeCord
path: geyser/bootstrap/bungeecord/build/libs/Geyser-BungeeCord.jar
if-no-files-found: error
- name: Archive artifacts (Geyser Sponge)
uses: actions/upload-artifact@v3
if: success()
with:
name: Geyser Sponge
path: geyser/bootstrap/sponge/build/libs/Geyser-Sponge.jar
if-no-files-found: error
- name: Archive artifacts (Geyser Velocity)
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
if: success()
with:
name: Geyser Velocity
path: geyser/bootstrap/velocity/build/libs/Geyser-Velocity.jar
if-no-files-found: error
- name: Archive artifacts (Geyser ViaProxy)
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
if: success()
with:
name: Geyser ViaProxy
path: geyser/bootstrap/viaproxy/build/libs/Geyser-ViaProxy.jar
if-no-files-found: error

1
.gitignore vendored
View File

@ -251,3 +251,4 @@ locales/
/saved-refresh-tokens.json
/custom_mappings/
/languages/
/custom-skulls.yml

View File

@ -19,32 +19,28 @@ public class LongClassName {
public int nameWithMultipleWords = 0;
/**
* Javadoc comment to explain what a function does.
*/
* Javadoc comment to explain what a function does.
*/
@RandomAnnotation(stuff = true, moreStuff = "might exist")
public void applyStuff() {
Variable variable = new Variable();
Variable otherVariable = new Variable();
if (condition) {
// Do stuff.
// Do stuff.
} else if (anotherCondition) {
// Do something else.
// Do something else.
}
switch (value) {
case 0:
stuff();
break;
case 1:
differentStuff();
break;
case 0 -> stuff();
case 1 -> differentStuff();
}
}
}
```
Make sure to comment your code where possible.
Make sure to comment your code where possible. To mark nullable methods, use `@Nullable` (and subsequently, `@NonNull`) from the `org.checkerframework.checker.nullness.qual` package.
The nature of our software requires a lot of arrays and maps to be stored - where possible, use Fastutil's specialized maps. For example, if you're storing block state translations, use an `Int2IntMap`.

28
Jenkinsfile vendored
View File

@ -1,28 +0,0 @@
pipeline {
agent any
tools {
gradle 'Gradle 7'
jdk 'Java 17'
}
options {
buildDiscarder(logRotator(artifactNumToKeepStr: '20'))
}
stages {
stage ('Build') {
steps {
sh 'git submodule update --init --recursive'
rtGradleRun(
usesPlugin: true,
tool: 'Gradle 7',
buildFile: 'build.gradle.kts',
tasks: 'clean build',
)
}
post {
success {
archiveArtifacts artifacts: 'bootstrap/**/build/libs/Geyser-*.jar', fingerprint: true
}
}
}
}
}

View File

@ -14,7 +14,7 @@ The ultimate goal of this project is to allow Minecraft: Bedrock Edition users t
Special thanks to the DragonProxy project for being a trailblazer in protocol translation and for all the team members who have joined us here!
### Currently supporting Minecraft Bedrock 1.19.40 - 1.19.81 and Minecraft Java 1.19.4.
### Currently supporting Minecraft Bedrock 1.20.40 - 1.20.72 and Minecraft Java 1.20.4
## Setting Up
Take a look [here](https://wiki.geysermc.org/geyser/setup/) for how to set up Geyser.

View File

@ -30,7 +30,7 @@ import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
@SupportedAnnotationTypes("*")
@SupportedSourceVersion(SourceVersion.RELEASE_16)
@SupportedSourceVersion(SourceVersion.RELEASE_17)
public class BlockEntityProcessor extends ClassProcessor {
public BlockEntityProcessor() {
super("org.geysermc.geyser.translator.level.block.entity.BlockEntity");

View File

@ -25,6 +25,8 @@
package org.geysermc.geyser.processor;
import org.checkerframework.checker.nullness.qual.Nullable;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
@ -159,7 +161,7 @@ public class ClassProcessor extends AbstractProcessor {
this.processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Completed processing for " + this.annotationClassName);
}
private BufferedReader createReader() throws IOException {
private @Nullable BufferedReader createReader() throws IOException {
if (this.outputPath != null) {
this.processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Reading existing " + this.annotationClassName + " list from " + this.outputPath);
return Files.newBufferedReader(this.outputPath);

View File

@ -30,7 +30,7 @@ import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
@SupportedAnnotationTypes("*")
@SupportedSourceVersion(SourceVersion.RELEASE_16)
@SupportedSourceVersion(SourceVersion.RELEASE_17)
public class CollisionRemapperProcessor extends ClassProcessor {
public CollisionRemapperProcessor() {
super("org.geysermc.geyser.translator.collision.CollisionRemapper");

View File

@ -30,7 +30,7 @@ import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
@SupportedAnnotationTypes("*")
@SupportedSourceVersion(SourceVersion.RELEASE_16)
@SupportedSourceVersion(SourceVersion.RELEASE_17)
public class PacketTranslatorProcessor extends ClassProcessor {
public PacketTranslatorProcessor() {
super("org.geysermc.geyser.translator.protocol.Translator");

View File

@ -30,7 +30,7 @@ import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
@SupportedAnnotationTypes("*")
@SupportedSourceVersion(SourceVersion.RELEASE_16)
@SupportedSourceVersion(SourceVersion.RELEASE_17)
public class SoundHandlerProcessor extends ClassProcessor {
public SoundHandlerProcessor() {
super("org.geysermc.geyser.translator.sound.SoundTranslator");

View File

@ -4,4 +4,5 @@ plugins {
dependencies {
api(libs.base.api)
api(libs.math)
}

View File

@ -29,13 +29,17 @@ 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.command.CommandSource;
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 org.geysermc.geyser.api.util.MinecraftVersion;
import org.geysermc.geyser.api.util.PlatformType;
import java.nio.file.Path;
import java.util.List;
import java.util.UUID;
@ -75,6 +79,7 @@ public interface GeyserApi extends GeyserApiBase {
* @param apiClass the builder class
* @param <R> the implementation type
* @param <T> the API type
* @throws IllegalArgumentException if there is no provider for the specified API class
* @return the builder instance
*/
@NonNull
@ -107,6 +112,54 @@ public interface GeyserApi extends GeyserApiBase {
@NonNull
BedrockListener bedrockListener();
/**
* Gets the {@link Path} to the Geyser config directory.
*
* @return the path to the Geyser config directory
*/
@NonNull
Path configDirectory();
/**
* Gets the {@link Path} to the Geyser packs directory.
*
* @return the path to the Geyser packs directory
*/
@NonNull
Path packDirectory();
/**
* Gets {@link PlatformType} the extension is running on
*
* @return type of platform
*/
@NonNull
PlatformType platformType();
/**
* Gets the version of Java Minecraft that is supported.
*
* @return the supported version of Java Minecraft
*/
@NonNull
MinecraftVersion supportedJavaVersion();
/**
* Gets a list of Bedrock Minecraft versions that are supported.
*
* @return the list of supported Bedrock Minecraft versions
*/
@NonNull
List<MinecraftVersion> supportedBedrockVersions();
/**
* Gets the {@link CommandSource} for the console.
*
* @return the console command source
*/
@NonNull
CommandSource consoleCommandSource();
/**
* Gets the current {@link GeyserApiBase} instance.
*

View File

@ -0,0 +1,148 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.api.bedrock.camera;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.api.connection.GeyserConnection;
import java.util.Set;
import java.util.UUID;
/**
* This interface holds all the methods that relate to a client's camera.
* Can be accessed through {@link GeyserConnection#camera()}.
*/
public interface CameraData {
/**
* Sends a camera fade instruction to the client.
* If an existing camera fade is already in progress, the current fade will be prolonged.
* Can be built using {@link CameraFade.Builder}.
* To stop a fade early, use {@link #clearCameraInstructions()}.
*
* @param fade the camera fade instruction to send
*/
void sendCameraFade(@NonNull CameraFade fade);
/**
* Sends a camera position instruction to the client.
* If an existing camera movement is already in progress,
* the final camera position will be the one of the latest instruction, and
* the (optional) camera fade will be added on top of the existing fade.
* Can be built using {@link CameraPosition.Builder}.
* To stop reset the camera position/stop ongoing instructions, use {@link #clearCameraInstructions()}.
*
* @param position the camera position instruction to send
*/
void sendCameraPosition(@NonNull CameraPosition position);
/**
* Stops all sent camera instructions (fades, movements, and perspective locks).
* This will not stop any camera shakes/input locks/fog effects, use the respective methods for those.
*/
void clearCameraInstructions();
/**
* Forces a {@link CameraPerspective} on the client. This will prevent the client
* from changing their camera perspective until it is unlocked via {@link #clearCameraInstructions()}.
* <p>
* Note: You cannot force a client into a free camera perspective with this method.
* To do that, send a {@link CameraPosition} via {@link #sendCameraPosition(CameraPosition)} - it requires a set position
* instead of being relative to the player.
*
* @param perspective the {@link CameraPerspective} to force
*/
void forceCameraPerspective(@NonNull CameraPerspective perspective);
/**
* Gets the client's current {@link CameraPerspective}, if one is currently forced.
* This will return {@code null} if the client is not currently forced into a perspective.
* If a perspective is forced, the client will not be able to change their camera perspective until it is unlocked.
*
* @return the forced perspective, or {@code null} if none is forced
*/
@Nullable CameraPerspective forcedCameraPerspective();
/**
* Shakes the client's camera.
* <p>
* If the camera is already shaking with the same {@link CameraShake} type, then the additional intensity
* will be layered on top of the existing intensity, with their own distinct durations.<br>
* If the existing shake type is different and the new intensity/duration are not positive, the existing shake only
* switches to the new type. Otherwise, the existing shake is completely overridden.
*
* @param intensity the intensity of the shake. The client has a maximum total intensity of 4.
* @param duration the time in seconds that the shake will occur for
* @param type the type of shake
*/
void shakeCamera(float intensity, float duration, @NonNull CameraShake type);
/**
* Stops all camera shakes of any type.
*/
void stopCameraShake();
/**
* Adds the given fog IDs to the fog cache, then sends all fog IDs in the cache to the client.
* <p>
* Fog IDs can be found <a href="https://wiki.bedrock.dev/documentation/fog-ids.html">here</a>
*
* @param fogNameSpaces the fog IDs to add. If empty, the existing cached IDs will still be sent.
*/
void sendFog(String... fogNameSpaces);
/**
* Removes the given fog IDs from the fog cache, then sends all fog IDs in the cache to the client.
*
* @param fogNameSpaces the fog IDs to remove. If empty, all fog IDs will be removed.
*/
void removeFog(String... fogNameSpaces);
/**
* Returns an immutable copy of all fog affects currently applied to this client.
*/
@NonNull
Set<String> fogEffects();
/**
* (Un)locks the client's camera, so that they cannot look around.
* To ensure the camera is only unlocked when all locks are released, you must supply
* a UUID when using method, and use the same UUID to unlock the camera.
*
* @param lock whether to lock the camera
* @param owner the owner of the lock, represented with a UUID
* @return if the camera is locked after this method call
*/
boolean lockCamera(boolean lock, @NonNull UUID owner);
/**
* Returns whether the client's camera is locked.
*
* @return whether the camera is currently locked
*/
boolean isCameraLocked();
}

View File

@ -0,0 +1,77 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.api.bedrock.camera;
/**
* These are all the easing types that can be used when sending a {@link CameraPosition} instruction.
* When using these, the client won't teleport to the new camera position, but instead transition to it.
* <p>
* See <a href="https://easings.net/">https://easings.net/</a> for more information.
*/
public enum CameraEaseType {
LINEAR("linear"),
SPRING("spring"),
EASE_IN_SINE("in_sine"),
EASE_OUT_SINE("out_sine"),
EASE_IN_OUT_SINE("in_out_sine"),
EASE_IN_QUAD("in_quad"),
EASE_OUT_QUAD("out_quad"),
EASE_IN_OUT_QUAD("in_out_quad"),
EASE_IN_CUBIC("in_cubic"),
EASE_OUT_CUBIC("out_cubic"),
EASE_IN_OUT_CUBIC("in_out_cubic"),
EASE_IN_QUART("in_quart"),
EASE_OUT_QUART("out_quart"),
EASE_IN_OUT_QUART("in_out_quart"),
EASE_IN_QUINT("in_quint"),
EASE_OUT_QUINT("out_quint"),
EASE_IN_OUT_QUINT("in_out_quint"),
EASE_IN_EXPO("in_expo"),
EASE_OUT_EXPO("out_expo"),
EASE_IN_OUT_EXPO("in_out_expo"),
EASE_IN_CIRC("in_circ"),
EASE_OUT_CIRC("out_circ"),
EASE_IN_OUT_CIRC("in_out_circ"),
EASE_IN_BACK("in_back"),
EASE_OUT_BACK("out_back"),
EASE_IN_OUT_BACK("in_out_back"),
EASE_IN_ELASTIC("in_elastic"),
EASE_OUT_ELASTIC("out_elastic"),
EASE_IN_OUT_ELASTIC("in_out_elastic"),
EASE_IN_BOUNCE("in_bounce"),
EASE_OUT_BOUNCE("out_bounce"),
EASE_IN_OUT_BOUNCE("in_out_bounce");
private final String id;
CameraEaseType(String id) {
this.id = id;
}
public String id() {
return this.id;
}
}

View File

@ -0,0 +1,94 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.api.bedrock.camera;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.common.value.qual.IntRange;
import org.geysermc.geyser.api.GeyserApi;
import java.awt.Color;
/**
* Represents a coloured fade overlay on the camera.
* <p>
* Can be sent with {@link CameraData#sendCameraFade(CameraFade)}, or with a {@link CameraPosition} instruction.
*/
public interface CameraFade {
/**
* Gets the color overlay of the camera.
* Bedrock uses an RGB color system.
*
* @return the color of the fade
*/
@NonNull Color color();
/**
* Gets the seconds it takes to fade in.
* All fade times combined must take at least 0.5 seconds, and at most 30 seconds.
*
* @return the seconds it takes to fade in
*/
float fadeInSeconds();
/**
* Gets the seconds the overlay is held.
* All fade times combined must take at least 0.5 seconds, and at most 30 seconds.
*
* @return the seconds the overlay is held
*/
float fadeHoldSeconds();
/**
* Gets the seconds it takes to fade out.
* All fade times combined must take at least 0.5 seconds, and at most 30 seconds.
*
* @return the seconds it takes to fade out
*/
float fadeOutSeconds();
/**
* Creates a Builder for CameraFade
*
* @return a CameraFade Builder
*/
static CameraFade.Builder builder() {
return GeyserApi.api().provider(CameraFade.Builder.class);
}
interface Builder {
Builder color(@NonNull Color color);
Builder fadeInSeconds(@IntRange(from = 0, to = 10) float fadeInSeconds);
Builder fadeHoldSeconds(@IntRange(from = 0, to = 10) float fadeHoldSeconds);
Builder fadeOutSeconds(@IntRange(from = 0, to = 10) float fadeOutSeconds);
CameraFade build();
}
}

View File

@ -0,0 +1,48 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.api.bedrock.camera;
/**
* Represents a camera perspective that a player's camera can take.
* All perspectives except for {@link #FREE} are locked to the player's head,
* and are therefore relative to the player's position and rotation.
*/
public enum CameraPerspective {
FIRST_PERSON("minecraft:first_person"),
FREE("minecraft:free"),
THIRD_PERSON("minecraft:third_person"),
THIRD_PERSON_FRONT("minecraft:third_person_front");
private final String id;
CameraPerspective(String id) {
this.id = id;
}
public String id() {
return this.id;
}
}

View File

@ -0,0 +1,150 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.api.bedrock.camera;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.common.value.qual.IntRange;
import org.cloudburstmc.math.vector.Vector3f;
import org.geysermc.geyser.api.GeyserApi;
/**
* This interface represents a camera position instruction. Can be built with the {@link #builder()}.
* <p>
* Any camera position instruction pins the client camera to a specific position and rotation.
* You can set {@link CameraEaseType} to ensure a smooth transition that will last {@link #easeSeconds()} seconds.
* A {@link CameraFade} can also be sent, which will transition the player to a coloured transition during the transition.
* <p>
* Use {@link CameraData#sendCameraPosition(CameraPosition)} to send such an instruction to any connection.
*/
public interface CameraPosition {
/**
* Gets the camera's position.
*
* @return camera position vector
*/
@NonNull Vector3f position();
/**
* Gets the {@link CameraEaseType} of the camera.
* If not set, there is no easing.
*
* @return camera ease type
*/
@Nullable CameraEaseType easeType();
/**
* Gets the {@link CameraFade} to be sent along the camera position instruction.
* If set, they will run at once.
*
* @return camera fade, or null if not present
*/
@Nullable CameraFade cameraFade();
/**
* Gets the easing duration of the camera, in seconds.
* Is only used if a {@link CameraEaseType} is set.
*
* @return camera easing duration in seconds
*/
float easeSeconds();
/**
* Gets the x-axis rotation of the camera.
* To prevent the camera from being upside down, Bedrock limits the range to -90 to 90.
* Will be overridden if {@link #facingPosition()} is set.
*
* @return camera x-axis rotation
*/
@IntRange(from = -90, to = 90) int rotationX();
/**
* Gets the y-axis rotation of the camera.
* Will be overridden if {@link #facingPosition()} is set.
*
* @return camera y-axis rotation
*/
int rotationY();
/**
* Gets the position that the camera is facing.
* Can be used instead of manually setting rotation values.
* <p>
* If set, the rotation values set via {@link #rotationX()} and {@link #rotationY()} will be ignored.
*
* @return Camera's facing position
*/
@Nullable Vector3f facingPosition();
/**
* Controls whether player effects, such as night vision or blindness, should be rendered on the camera.
* Defaults to false.
*
* @return whether player effects should be rendered
*/
boolean renderPlayerEffects();
/**
* Controls whether the player position should be used for directional audio.
* If false, the camera position will be used instead.
*
* @return whether the players position should be used for directional audio
*/
boolean playerPositionForAudio();
/**
* Creates a Builder for CameraPosition
*
* @return a CameraPosition Builder
*/
static CameraPosition.Builder builder() {
return GeyserApi.api().provider(CameraPosition.Builder.class);
}
interface Builder {
Builder cameraFade(@Nullable CameraFade cameraFade);
Builder renderPlayerEffects(boolean renderPlayerEffects);
Builder playerPositionForAudio(boolean playerPositionForAudio);
Builder easeType(@Nullable CameraEaseType easeType);
Builder easeSeconds(float easeSeconds);
Builder position(@NonNull Vector3f position);
Builder rotationX(@IntRange(from = -90, to = 90) int rotationX);
Builder rotationY(int rotationY);
Builder facingPosition(@Nullable Vector3f facingPosition);
CameraPosition build();
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -23,21 +23,12 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.common;
package org.geysermc.geyser.api.bedrock.camera;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum PlatformType {
ANDROID("Android"),
BUNGEECORD("BungeeCord"),
FABRIC("Fabric"),
SPIGOT("Spigot"),
SPONGE("Sponge"),
STANDALONE("Standalone"),
VELOCITY("Velocity");
private final String platformName;
/**
* Represents a camera shake instruction. Can be sent in {@link CameraData#shakeCamera(float, float, CameraShake)}
*/
public enum CameraShake {
POSITIONAL,
ROTATIONAL
}

View File

@ -0,0 +1,143 @@
/*
* 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.block.custom;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.api.GeyserApi;
import org.geysermc.geyser.api.block.custom.component.CustomBlockComponents;
import org.geysermc.geyser.api.block.custom.property.CustomBlockProperty;
import org.geysermc.geyser.api.util.CreativeCategory;
import java.util.List;
import java.util.Map;
/**
* This class is used to store data for a custom block.
*/
public interface CustomBlockData {
/**
* Gets the name of the custom block
*
* @return The name of the custom block.
*/
@NonNull String name();
/**
* Gets the identifier of the custom block
*
* @return The identifier of the custom block.
*/
@NonNull String identifier();
/**
* Gets if the custom block is included in the creative inventory
*
* @return If the custom block is included in the creative inventory.
*/
boolean includedInCreativeInventory();
/**
* Gets the block's creative category, or tab id.
*
* @return the block's creative category
*/
@Nullable CreativeCategory creativeCategory();
/**
* Gets the block's creative group.
*
* @return the block's creative group
*/
@Nullable String creativeGroup();
/**
* Gets the components of the custom block
*
* @return The components of the custom block.
*/
@Nullable CustomBlockComponents components();
/**
* Gets the custom block's map of block property names to CustomBlockProperty
* objects
*
* @return The custom block's map of block property names to CustomBlockProperty objects.
*/
@NonNull Map<String, CustomBlockProperty<?>> properties();
/**
* Gets the list of the custom block's permutations
*
* @return The permutations of the custom block.
*/
@NonNull List<CustomBlockPermutation> permutations();
/**
* Gets the custom block's default block state
*
* @return The default block state of the custom block.
*/
@NonNull CustomBlockState defaultBlockState();
/**
* Gets a builder for a custom block state
*
* @return The builder for a custom block state.
*/
CustomBlockState.@NonNull Builder blockStateBuilder();
/**
* Create a Builder for CustomBlockData
*
* @return A CustomBlockData Builder
*/
static CustomBlockData.Builder builder() {
return GeyserApi.api().provider(CustomBlockData.Builder.class);
}
interface Builder {
Builder name(@NonNull String name);
Builder includedInCreativeInventory(boolean includedInCreativeInventory);
Builder creativeCategory(@Nullable CreativeCategory creativeCategory);
Builder creativeGroup(@Nullable String creativeGroup);
Builder components(@NonNull CustomBlockComponents components);
Builder booleanProperty(@NonNull String propertyName);
Builder intProperty(@NonNull String propertyName, List<Integer> values);
Builder stringProperty(@NonNull String propertyName, List<String> values);
Builder permutations(@NonNull List<CustomBlockPermutation> permutations);
CustomBlockData build();
}
}

View File

@ -0,0 +1,39 @@
/*
* 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.block.custom;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.api.block.custom.component.CustomBlockComponents;
/**
* This class is used to store a custom block permutations, which contain custom
* block components mapped to a Molang query that should return true or false
*
* @param components The components of the block
* @param condition The Molang query that should return true or false
*/
public record CustomBlockPermutation(@NonNull CustomBlockComponents components, @NonNull String condition) {
}

View File

@ -0,0 +1,75 @@
/*
* 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.block.custom;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Map;
/**
* This class is used to store a custom block state, which contains CustomBlockData
* tied to defined properties and values
*/
public interface CustomBlockState {
/**
* Gets the custom block data associated with the state
*
* @return The custom block data for the state.
*/
@NonNull CustomBlockData block();
/**
* Gets the name of the state
*
* @return The name of the state.
*/
@NonNull String name();
/**
* Gets the given property for the state
*
* @param propertyName the property name
* @return the boolean, int, or string property.
*/
@NonNull <T> T property(@NonNull String propertyName);
/**
* Gets a map of the properties for the state
*
* @return The properties for the state.
*/
@NonNull Map<String, Object> properties();
interface Builder {
Builder booleanProperty(@NonNull String propertyName, boolean value);
Builder intProperty(@NonNull String propertyName, int value);
Builder stringProperty(@NonNull String propertyName, @NonNull String value);
CustomBlockState build();
}
}

View File

@ -0,0 +1,91 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.api.block.custom;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.api.GeyserApi;
import org.geysermc.geyser.api.block.custom.component.CustomBlockComponents;
import org.geysermc.geyser.api.util.CreativeCategory;
import java.util.List;
/**
* Represents a completely custom block that is not based on an existing vanilla Minecraft block.
*/
public interface NonVanillaCustomBlockData extends CustomBlockData {
/**
* Gets the namespace of the custom block
*
* @return The namespace of the custom block.
*/
@NonNull String namespace();
/**
* Create a Builder for NonVanillaCustomBlockData
*
* @return A NonVanillaCustomBlockData Builder
*/
static NonVanillaCustomBlockData.Builder builder() {
return GeyserApi.api().provider(NonVanillaCustomBlockData.Builder.class);
}
interface Builder extends CustomBlockData.Builder {
Builder namespace(@NonNull String namespace);
@Override
Builder name(@NonNull String name);
@Override
Builder includedInCreativeInventory(boolean includedInCreativeInventory);
@Override
Builder creativeCategory(@Nullable CreativeCategory creativeCategory);
@Override
Builder creativeGroup(@Nullable String creativeGroup);
@Override
Builder components(@NonNull CustomBlockComponents components);
@Override
Builder booleanProperty(@NonNull String propertyName);
@Override
Builder intProperty(@NonNull String propertyName, List<Integer> values);
@Override
Builder stringProperty(@NonNull String propertyName, List<String> values);
@Override
Builder permutations(@NonNull List<CustomBlockPermutation> permutations);
@Override
NonVanillaCustomBlockData build();
}
}

View File

@ -23,42 +23,47 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.translator.inventory.item;
package org.geysermc.geyser.api.block.custom.component;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.session.GeyserSession;
public abstract class NbtItemStackTranslator {
/**
* This class is used to store a box component for the selection and
* collision boxes of a custom block.
*
* @param originX The origin X of the box
* @param originY The origin Y of the box
* @param originZ The origin Z of the box
* @param sizeX The size X of the box
* @param sizeY The size Y of the box
* @param sizeZ The size Z of the box
*/
public record BoxComponent(float originX, float originY, float originZ, float sizeX, float sizeY, float sizeZ) {
private static final BoxComponent FULL_BOX = new BoxComponent(-8, 0, -8, 16, 16, 16);
private static final BoxComponent EMPTY_BOX = new BoxComponent(0, 0, 0, 0, 0, 0);
/**
* Translate the item NBT to Bedrock
* @param session the client's current session
* @param itemTag the item's CompoundTag (cloned from Geyser's cached copy)
* @param mapping Geyser's item mapping
*/
public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemMapping mapping) {
}
/**
* Translate the item NBT to Java.
* @param itemTag the item's CompoundTag
* @param mapping Geyser's item mapping
*/
public void translateToJava(CompoundTag itemTag, ItemMapping mapping) {
}
/**
* Gets whether this nbt translator takes in this item.
* Gets a full box component
*
* @param item Geyser's item mapping
* @return if the item should be processed under this class
* @return A full box component
*/
public boolean acceptItem(Item item) {
return true;
} // TODO
public static BoxComponent fullBox() {
return FULL_BOX;
}
/**
* Gets an empty box component
*
* @return An empty box component
*/
public static BoxComponent emptyBox() {
return EMPTY_BOX;
}
/**
* Gets if the box component is empty
*
* @return If the box component is empty.
*/
public boolean isEmpty() {
return sizeX == 0 && sizeY == 0 && sizeZ == 0;
}
}

View File

@ -0,0 +1,199 @@
/*
* 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.block.custom.component;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.api.GeyserApi;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* This class is used to store components for a custom block or custom block permutation.
*/
public interface CustomBlockComponents {
/**
* Gets the selection box component
* Equivalent to "minecraft:selection_box"
*
* @return The selection box.
*/
@Nullable BoxComponent selectionBox();
/**
* Gets the collision box component
* Equivalent to "minecraft:collision_box"
* @return The collision box.
*/
@Nullable BoxComponent collisionBox();
/**
* Gets the display name component
* Equivalent to "minecraft:display_name"
*
* @return The display name.
*/
@Nullable String displayName();
/**
* Gets the geometry component
* Equivalent to "minecraft:geometry"
*
* @return The geometry.
*/
@Nullable GeometryComponent geometry();
/**
* Gets the material instances component
* Equivalent to "minecraft:material_instances"
*
* @return The material instances.
*/
@NonNull Map<String, MaterialInstance> materialInstances();
/**
* Gets the placement filter component
* Equivalent to "minecraft:placement_filter"
*
* @return The placement filter.
*/
@Nullable List<PlacementConditions> placementFilter();
/**
* Gets the destructible by mining component
* Equivalent to "minecraft:destructible_by_mining"
*
* @return The destructible by mining value.
*/
@Nullable Float destructibleByMining();
/**
* Gets the friction component
* Equivalent to "minecraft:friction"
*
* @return The friction value.
*/
@Nullable Float friction();
/**
* Gets the light emission component
* Equivalent to "minecraft:light_emission"
*
* @return The light emission value.
*/
@Nullable Integer lightEmission();
/**
* Gets the light dampening component
* Equivalent to "minecraft:light_dampening"
*
* @return The light dampening value.
*/
@Nullable Integer lightDampening();
/**
* Gets the transformation component
* Equivalent to "minecraft:transformation"
*
* @return The transformation.
*/
@Nullable TransformationComponent transformation();
/**
* Gets the unit cube component
* Equivalent to "minecraft:unit_cube"
*
* @deprecated Use {@link #geometry()} and compare with `minecraft:geometry.full_block` instead.
*
* @return The rotation.
*/
@Deprecated
boolean unitCube();
/**
* Gets if the block should place only air
* Equivalent to setting a dummy event to run on "minecraft:on_player_placing"
*
* @return If the block should place only air.
*/
boolean placeAir();
/**
* Gets the set of tags
* Equivalent to "tag:some_tag"
*
* @return The set of tags.
*/
@NonNull Set<String> tags();
/**
* Create a Builder for CustomBlockComponents
*
* @return A CustomBlockComponents Builder
*/
static CustomBlockComponents.Builder builder() {
return GeyserApi.api().provider(CustomBlockComponents.Builder.class);
}
interface Builder {
Builder selectionBox(BoxComponent selectionBox);
Builder collisionBox(BoxComponent collisionBox);
Builder displayName(String displayName);
Builder geometry(GeometryComponent geometry);
Builder materialInstance(@NonNull String name, @NonNull MaterialInstance materialInstance);
Builder placementFilter(List<PlacementConditions> placementConditions);
Builder destructibleByMining(Float destructibleByMining);
Builder friction(Float friction);
Builder lightEmission(Integer lightEmission);
Builder lightDampening(Integer lightDampening);
Builder transformation(TransformationComponent transformation);
/**
* @deprecated Use {@link #geometry(GeometryComponent)} with `minecraft:geometry.full_block` instead.
*/
@Deprecated
Builder unitCube(boolean unitCube);
Builder placeAir(boolean placeAir);
Builder tags(@Nullable Set<String> tags);
CustomBlockComponents build();
}
}

View File

@ -0,0 +1,69 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.api.block.custom.component;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.api.GeyserApi;
import java.util.Map;
/**
* This class is used to store data for a geometry component.
*/
public interface GeometryComponent {
/**
* Gets the identifier of the geometry
*
* @return The identifier of the geometry.
*/
@NonNull String identifier();
/**
* Gets the bone visibility of the geometry
*
* @return The bone visibility of the geometry.
*/
@Nullable Map<String, String> boneVisibility();
/**
* Creates a builder for GeometryComponent
*
* @return a builder for GeometryComponent.
*/
static GeometryComponent.Builder builder() {
return GeyserApi.api().provider(GeometryComponent.Builder.class);
}
interface Builder {
Builder identifier(@NonNull String identifier);
Builder boneVisibility(@Nullable Map<String, String> boneVisibility);
GeometryComponent build();
}
}

View File

@ -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.block.custom.component;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.api.GeyserApi;
/**
* This class is used to store data for a material instance.
*/
public interface MaterialInstance {
/**
* Gets the texture of the block
*
* @return The texture of the block.
*/
@Nullable String texture();
/**
* Gets the render method of the block
*
* @return The render method of the block.
*/
@Nullable String renderMethod();
/**
* Gets if the block should be dimmed on certain faces
*
* @return If the block should be dimmed on certain faces.
*/
boolean faceDimming();
/**
* Gets if the block should have ambient occlusion
*
* @return If the block should have ambient occlusion.
*/
boolean ambientOcclusion();
/**
* Creates a builder for MaterialInstance.
*
* @return a builder for MaterialInstance
*/
static MaterialInstance.Builder builder() {
return GeyserApi.api().provider(MaterialInstance.Builder.class);
}
interface Builder {
Builder texture(@Nullable String texture);
Builder renderMethod(@Nullable String renderMethod);
Builder faceDimming(boolean faceDimming);
Builder ambientOcclusion(boolean ambientOcclusion);
MaterialInstance build();
}
}

View File

@ -0,0 +1,53 @@
/*
* 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.block.custom.component;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.LinkedHashMap;
import java.util.Set;
/**
* This class is used to store conditions for a placement filter for a custom block.
*
* @param allowedFaces The faces that the block can be placed on
* @param blockFilters The block filters that control what blocks the block can be placed on
*/
public record PlacementConditions(@NonNull Set<Face> allowedFaces, @NonNull LinkedHashMap<String, BlockFilterType> blockFilters) {
public enum Face {
DOWN,
UP,
NORTH,
SOUTH,
WEST,
EAST
}
public enum BlockFilterType {
BLOCK,
TAG
}
}

View File

@ -0,0 +1,67 @@
/*
* 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.block.custom.component;
/**
* This class is used to store the transformation component of a block
*
* @param rx The rotation on the x axis
* @param ry The rotation on the y axis
* @param rz The rotation on the z axis
* @param sx The scale on the x axis
* @param sy The scale on the y axis
* @param sz The scale on the z axis
* @param tx The translation on the x axis
* @param ty The translation on the y axis
* @param tz The translation on the z axis
*/
public record TransformationComponent(int rx, int ry, int rz, float sx, float sy, float sz, float tx, float ty, float tz) {
/**
* Constructs a new TransformationComponent with the rotation values and assumes default scale and translation
*
* @param rx The rotation on the x axis
* @param ry The rotation on the y axis
* @param rz The rotation on the z axis
*/
public TransformationComponent(int rx, int ry, int rz) {
this(rx, ry, rz, 1, 1, 1, 0, 0, 0);
}
/**
* Constructs a new TransformationComponent with the rotation and scale values and assumes default translation
*
* @param rx The rotation on the x axis
* @param ry The rotation on the y axis
* @param rz The rotation on the z axis
* @param sx The scale on the x axis
* @param sy The scale on the y axis
* @param sz The scale on the z axis
*/
public TransformationComponent(int rx, int ry, int rz, float sx, float sy, float sz) {
this(rx, ry, rz, sx, sy, sz, 0, 0, 0);
}
}

View File

@ -0,0 +1,7 @@
package org.geysermc.geyser.api.block.custom.nonvanilla;
import org.checkerframework.checker.index.qual.NonNegative;
import org.checkerframework.checker.nullness.qual.NonNull;
public record JavaBlockItem(@NonNull String identifier, @NonNegative int javaId, @NonNegative int stackSize) {
}

View File

@ -0,0 +1,111 @@
package org.geysermc.geyser.api.block.custom.nonvanilla;
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;
public interface JavaBlockState {
/**
* Gets the identifier of the block state
*
* @return the identifier of the block state
*/
@NonNull String identifier();
/**
* Gets the Java ID of the block state
*
* @return the Java ID of the block state
*/
@NonNegative int javaId();
/**
* Gets the state group ID of the block state
*
* @return the state group ID of the block state
*/
@NonNegative int stateGroupId();
/**
* Gets the block hardness of the block state
*
* @return the block hardness of the block state
*/
@NonNegative float blockHardness();
/**
* Gets whether the block state is waterlogged
*
* @return whether the block state is waterlogged
*/
boolean waterlogged();
/**
* Gets the collision of the block state
*
* @return the collision of the block state
*/
@NonNull JavaBoundingBox[] collision();
/**
* Gets whether the block state can be broken with hand
*
* @return whether the block state can be broken with hand
*/
boolean canBreakWithHand();
/**
* Gets the pick item of the block state
*
* @return the pick item of the block state
*/
@Nullable String pickItem();
/**
* Gets the piston behavior of the block state
*
* @return the piston behavior of the block state
*/
@Nullable String pistonBehavior();
/**
* Gets whether the block state has block entity
*
* @return whether the block state has block entity
*/
boolean hasBlockEntity();
/**
* Creates a new {@link JavaBlockState.Builder} instance
*
* @return a new {@link JavaBlockState.Builder} instance
*/
static JavaBlockState.Builder builder() {
return GeyserApi.api().provider(JavaBlockState.Builder.class);
}
interface Builder {
Builder identifier(@NonNull String identifier);
Builder javaId(@NonNegative int javaId);
Builder stateGroupId(@NonNegative int stateGroupId);
Builder blockHardness(@NonNegative float blockHardness);
Builder waterlogged(boolean waterlogged);
Builder collision(@NonNull JavaBoundingBox[] collision);
Builder canBreakWithHand(boolean canBreakWithHand);
Builder pickItem(@Nullable String pickItem);
Builder pistonBehavior(@Nullable String pistonBehavior);
Builder hasBlockEntity(boolean hasBlockEntity);
JavaBlockState build();
}
}

View File

@ -0,0 +1,4 @@
package org.geysermc.geyser.api.block.custom.nonvanilla;
public record JavaBoundingBox(double middleX, double middleY, double middleZ, double sizeX, double sizeY, double sizeZ) {
}

View File

@ -0,0 +1,56 @@
/*
* 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.block.custom.property;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.List;
/**
* This class is used to store a property of a custom block of a generic type.
*/
public interface CustomBlockProperty<T> {
/**
* Gets the name of the property
*
* @return The name of the property.
*/
@NonNull String name();
/**
* Gets the values of the property
*
* @return The values of the property.
*/
@NonNull List<T> values();
/**
* Gets the type of the property
*
* @return The type of the property.
*/
@NonNull PropertyType type();
}

View File

@ -0,0 +1,79 @@
/*
* 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.block.custom.property;
import org.checkerframework.checker.nullness.qual.NonNull;
/**
* This class is used to define a custom block property's type.
*/
public class PropertyType {
private static final PropertyType BOOLEAN = new PropertyType(Boolean.class);
private static final PropertyType INTEGER = new PropertyType(Integer.class);
private static final PropertyType STRING = new PropertyType(String.class);
/**
* Gets the property type for a boolean.
*
* @return The property type for a boolean.
*/
@NonNull public static PropertyType booleanProp() {
return BOOLEAN;
}
/**
* Gets the property type for an integer.
*
* @return The property type for an integer.
*/
@NonNull public static PropertyType integerProp() {
return INTEGER;
}
/**
* Gets the property type for a string.
*
* @return The property type for a string.
*/
@NonNull public static PropertyType stringProp() {
return STRING;
}
private final Class<?> typeClass;
/**
* Gets the class of the property type
*
* @return The class of the property type.
*/
@NonNull public Class<?> typeClass() {
return typeClass;
}
private PropertyType(Class<?> typeClass) {
this.typeClass = typeClass;
}
}

View File

@ -29,27 +29,107 @@ import org.checkerframework.checker.index.qual.NonNegative;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.api.connection.Connection;
import org.geysermc.geyser.api.bedrock.camera.CameraData;
import org.geysermc.geyser.api.bedrock.camera.CameraShake;
import org.geysermc.geyser.api.command.CommandSource;
import org.geysermc.geyser.api.entity.EntityData;
import org.geysermc.geyser.api.entity.type.GeyserEntity;
import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
/**
* Represents a player connection used in Geyser.
*/
public interface GeyserConnection extends Connection, CommandSource {
/**
* Exposes the {@link CameraData} for this connection.
* It allows you to send fogs, camera shakes, force camera perspectives, and more.
*
* @return the CameraData for this connection.
*/
@NonNull CameraData camera();
/**
* Exposes the {@link EntityData} for this connection.
* It allows you to get entities by their Java entity ID, show emotes, and get the player entity.
*
* @return the EntityData for this connection.
*/
@NonNull EntityData entities();
/**
* @param javaId the Java entity ID to look up.
* @return a {@link GeyserEntity} if present in this connection's entity tracker.
* @deprecated Use {@link EntityData#entityByJavaId(int)} instead
*/
@Deprecated
@NonNull
CompletableFuture<@Nullable GeyserEntity> entityByJavaId(@NonNegative int javaId);
/**
* Displays a player entity as emoting to this client.
*
* @param emoter the player entity emoting.
* @param emoteId the emote ID to send to the client.
* @param emoteId the emote ID to send to this client.
* @deprecated use {@link EntityData#showEmote(GeyserPlayerEntity, String)} instead
*/
@Deprecated
void showEmote(@NonNull GeyserPlayerEntity emoter, @NonNull String emoteId);
/**
* Shakes the client's camera.
* <p>
* If the camera is already shaking with the same {@link CameraShake} type, then the additional intensity
* will be layered on top of the existing intensity, with their own distinct durations.<br>
* If the existing shake type is different and the new intensity/duration are not positive, the existing shake only
* switches to the new type. Otherwise, the existing shake is completely overridden.
*
* @param intensity the intensity of the shake. The client has a maximum total intensity of 4.
* @param duration the time in seconds that the shake will occur for
* @param type the type of shake
*
* @deprecated Use {@link CameraData#shakeCamera(float, float, CameraShake)} instead.
*/
@Deprecated
void shakeCamera(float intensity, float duration, @NonNull CameraShake type);
/**
* Stops all camera shake of any type.
*
* @deprecated Use {@link CameraData#stopCameraShake()} instead.
*/
@Deprecated
void stopCameraShake();
/**
* Adds the given fog IDs to the fog cache, then sends all fog IDs in the cache to the client.
* <p>
* Fog IDs can be found <a href="https://wiki.bedrock.dev/documentation/fog-ids.html">here</a>
*
* @param fogNameSpaces the fog IDs to add. If empty, the existing cached IDs will still be sent.
* @deprecated Use {@link CameraData#sendFog(String...)} instead.
*/
@Deprecated
void sendFog(String... fogNameSpaces);
/**
* Removes the given fog IDs from the fog cache, then sends all fog IDs in the cache to the client.
*
* @param fogNameSpaces the fog IDs to remove. If empty, all fog IDs will be removed.
* @deprecated Use {@link CameraData#removeFog(String...)} instead.
*/
@Deprecated
void removeFog(String... fogNameSpaces);
/**
* Returns an immutable copy of all fog affects currently applied to this client.
*
* @deprecated Use {@link CameraData#fogEffects()} instead.
*/
@Deprecated
@NonNull
Set<String> fogEffects();
}

View File

@ -0,0 +1,84 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.api.entity;
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.connection.GeyserConnection;
import org.geysermc.geyser.api.entity.type.GeyserEntity;
import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
/**
* This class holds all the methods that relate to entities.
* Can be accessed through {@link GeyserConnection#entities()}.
*/
public interface EntityData {
/**
* Returns a {@link GeyserEntity} to e.g. make them play an emote.
*
* @param javaId the Java entity ID to look up
* @return a {@link GeyserEntity} if present in this connection's entity tracker
*/
@NonNull CompletableFuture<@Nullable GeyserEntity> entityByJavaId(@NonNegative int javaId);
/**
* Displays a player entity as emoting to this client.
*
* @param emoter the player entity emoting
* @param emoteId the emote ID to send to this client
*/
void showEmote(@NonNull GeyserPlayerEntity emoter, @NonNull String emoteId);
/**
* Gets the {@link GeyserPlayerEntity} of this connection.
*
* @return the {@link GeyserPlayerEntity} of this connection
*/
@NonNull GeyserPlayerEntity playerEntity();
/**
* (Un)locks the client's movement inputs, so that they cannot move.
* To ensure that movement is only unlocked when all locks are released, you must supply
* a UUID with this method, and use the same UUID to unlock the camera.
*
* @param lock whether to lock the movement
* @param owner the owner of the lock
* @return if the movement is locked after this method call
*/
boolean lockMovement(boolean lock, @NonNull UUID owner);
/**
* Returns whether the client's movement is currently locked.
*
* @return whether the movement is locked
*/
boolean isMovementLocked();
}

View File

@ -25,7 +25,15 @@
package org.geysermc.geyser.api.entity.type.player;
import org.cloudburstmc.math.vector.Vector3f;
import org.geysermc.geyser.api.entity.type.GeyserEntity;
public interface GeyserPlayerEntity extends GeyserEntity {
/**
* Gets the position of the player.
*
* @return the position of the player.
*/
Vector3f position();
}

View File

@ -28,7 +28,6 @@ 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;

View File

@ -27,7 +27,6 @@ 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

View File

@ -0,0 +1,61 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.api.event.bedrock;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.api.connection.GeyserConnection;
import org.geysermc.geyser.api.event.connection.ConnectionEvent;
/**
* Called when a Geyser session disconnects.
*/
public class SessionDisconnectEvent extends ConnectionEvent {
private String disconnectReason;
public SessionDisconnectEvent(@NonNull GeyserConnection connection, @NonNull String reason) {
super(connection);
this.disconnectReason = reason;
}
/**
* Gets the disconnect reason.
*
* @return the reason for the disconnect
*/
public @NonNull String disconnectReason() {
return disconnectReason;
}
/**
* Sets the disconnect reason, thereby overriding th original reason.
*
* @param disconnectReason the reason for the disconnect
*/
public void disconnectReason(@NonNull String disconnectReason) {
this.disconnectReason = disconnectReason;
}
}

View File

@ -0,0 +1,39 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.api.event.bedrock;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.api.connection.GeyserConnection;
import org.geysermc.geyser.api.event.connection.ConnectionEvent;
/**
* Called when Geyser session connected to a Java remote server and is in a play-ready state.
*/
public final class SessionJoinEvent extends ConnectionEvent {
public SessionJoinEvent(@NonNull GeyserConnection connection) {
super(connection);
}
}

View File

@ -0,0 +1,67 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.api.event.bedrock;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.api.connection.GeyserConnection;
import org.geysermc.geyser.api.event.connection.ConnectionEvent;
import org.geysermc.geyser.api.pack.ResourcePack;
import java.util.List;
import java.util.UUID;
/**
* Called when Geyser initializes a session for a new Bedrock client and is in the process of sending resource packs.
*/
public abstract class SessionLoadResourcePacksEvent extends ConnectionEvent {
public SessionLoadResourcePacksEvent(@NonNull GeyserConnection connection) {
super(connection);
}
/**
* Gets an unmodifiable list of {@link ResourcePack}s that will be sent to the client.
*
* @return an unmodifiable list of resource packs that will be sent to the client.
*/
public abstract @NonNull List<ResourcePack> resourcePacks();
/**
* Registers a {@link ResourcePack} to be sent to the client.
*
* @param resourcePack a resource pack that will be sent to the client.
* @return true if the resource pack was added successfully,
* or false if already present
*/
public abstract boolean register(@NonNull ResourcePack resourcePack);
/**
* Unregisters a resource pack from being sent to the client.
*
* @param uuid the UUID of the resource pack
* @return true whether the resource pack was removed from the list of resource packs.
*/
public abstract boolean unregister(@NonNull UUID uuid);
}

View File

@ -0,0 +1,109 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.api.event.bedrock;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.event.Cancellable;
import org.geysermc.geyser.api.connection.GeyserConnection;
import org.geysermc.geyser.api.event.connection.ConnectionEvent;
import org.geysermc.geyser.api.network.RemoteServer;
/**
* Called when a session has logged in, and is about to connect to a remote java server.
* This event is cancellable, and can be used to prevent the player from connecting to the remote server.
*/
public final class SessionLoginEvent extends ConnectionEvent implements Cancellable {
private RemoteServer remoteServer;
private boolean cancelled;
private String disconnectReason;
public SessionLoginEvent(@NonNull GeyserConnection connection, @NonNull RemoteServer remoteServer) {
super(connection);
this.remoteServer = remoteServer;
}
/**
* Returns whether the event is cancelled.
*
* @return The cancel status of the event.
*/
@Override
public boolean isCancelled() {
return this.cancelled;
}
/**
* Cancels the login event, and disconnects the player.
* If cancelled, the player disconnects without connecting to the remote server.
* This method will use a default disconnect reason. To specify one, use {@link #setCancelled(boolean, String)}.
*
* @param cancelled If the login event should be cancelled.
*/
@Override
public void setCancelled(boolean cancelled) {
this.cancelled = cancelled;
}
/**
* Cancels the login event, and disconnects the player with the specified reason.
* If cancelled, the player disconnects without connecting to the remote server.
*
* @param cancelled If the login event should be cancelled.
* @param disconnectReason The reason for the cancellation.
*/
public void setCancelled(boolean cancelled, @NonNull String disconnectReason) {
this.cancelled = cancelled;
this.disconnectReason = disconnectReason;
}
/**
* Returns the reason for the cancellation, or null if there is no reason given.
*
* @return The reason for the cancellation.
*/
public @Nullable String disconnectReason() {
return this.disconnectReason;
}
/**
* Gets the {@link RemoteServer} the section will attempt to connect to.
*
* @return the {@link RemoteServer} the section will attempt to connect to.
*/
public @NonNull RemoteServer remoteServer() {
return this.remoteServer;
}
/**
* Sets the {@link RemoteServer} to connect the session to.
*
* @param remoteServer Sets the {@link RemoteServer} to connect to.
*/
public void remoteServer(@NonNull RemoteServer remoteServer) {
this.remoteServer = remoteServer;
}
}

View File

@ -0,0 +1,110 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.api.event.connection;
import org.checkerframework.checker.index.qual.NonNegative;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.event.Event;
import java.net.InetSocketAddress;
/**
* Called whenever Geyser gets pinged
* <p>
* This event allows you to modify/obtain the MOTD, maximum player count, and current number of players online,
* Geyser will reply to the client with what was given.
*/
public interface GeyserBedrockPingEvent extends Event {
/**
* Sets the given string as the primary motd, the given string cannot be null.
*
* @param primary the string to set as the primary motd
*/
void primaryMotd(@NonNull String primary);
/**
* Sets the given string as the secondary motd, the given string cannot be null.
* Note: the secondary motd is only used for the LAN game entry.
*
* @param secondary the string to set as the secondary motd
*/
void secondaryMotd(@NonNull String secondary);
/**
* Sets how many players are currently online, the given number cannot be below 0.
*
* @param count the number to set
*/
void playerCount(int count);
/**
* Sets the maximum number of players that can join this server, the given number cannot be below 1.
*
* @param max the number to set
*/
void maxPlayerCount(int max);
/**
* Gets the primary motd.
*
* @return the primary motd string
*/
@Nullable
String primaryMotd();
/**
* Gets the secondary motd.
*
* @return the secondary motd string
*/
@Nullable
String secondaryMotd();
/**
* Gets the current number of players.
*
* @return number of players online
*/
@NonNegative
int playerCount();
/**
* Gets the maximum number of players that can join this server
*
* @return maximum number of players that can join
*/
int maxPlayerCount();
/**
* Gets the {@link InetSocketAddress} of the client pinging us.
*
* @return a {@link InetSocketAddress}
*/
@NonNull
InetSocketAddress address();
}

View File

@ -33,7 +33,7 @@ import java.util.Map;
/**
* Called when commands are defined within Geyser.
*
* <p>
* This event allows you to register new commands using the {@link #register(Command)}
* method and retrieve the default commands defined.
*/

View File

@ -0,0 +1,75 @@
/*
* 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.geyser.api.block.custom.CustomBlockData;
import org.geysermc.geyser.api.block.custom.CustomBlockState;
import org.geysermc.geyser.api.block.custom.nonvanilla.JavaBlockState;
import org.geysermc.event.Event;
/**
* Called on Geyser's startup when looking for custom blocks. Custom blocks must be registered through this event.
* <p>
* This event will not be called if the "add-non-bedrock-items" setting is disabled in the Geyser config.
*/
public abstract class GeyserDefineCustomBlocksEvent implements Event {
/**
* Registers the given {@link CustomBlockData} as a custom block
*
* @param customBlockData the custom block to register
*/
public abstract void register(@NonNull CustomBlockData customBlockData);
/**
* Registers the given {@link CustomBlockState} as an override for the
* given java state identifier
* Java state identifiers are listed
* <a href="https://raw.githubusercontent.com/GeyserMC/mappings/master/blocks.json">here</a>
*
* @param javaIdentifier the java state identifier to override
* @param customBlockState the custom block state with which to override java state identifier
*/
public abstract void registerOverride(@NonNull String javaIdentifier, @NonNull CustomBlockState customBlockState);
/**
* Registers the given {@link CustomBlockData} as an override for the
* given java item identifier
*
* @param javaIdentifier the java item identifier to override
* @param customBlockData the custom block data with which to override java item identifier
*/
public abstract void registerItemOverride(@NonNull String javaIdentifier, @NonNull CustomBlockData customBlockData);
/**
* Registers the given {@link CustomBlockState} as an override for the
* given {@link JavaBlockState}
*
* @param javaBlockState the java block state for the non-vanilla block
* @param customBlockState the custom block state with which to override java state identifier
*/
public abstract void registerOverride(@NonNull JavaBlockState javaBlockState, @NonNull CustomBlockState customBlockState);
}

View File

@ -0,0 +1,28 @@
package org.geysermc.geyser.api.event.lifecycle;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.event.Event;
/**
* Called on Geyser's startup when looking for custom skulls. Custom skulls must be registered through this event.
* <p>
* This event will not be called if the "add-non-bedrock-items" setting is disabled in the Geyser config.
*/
public abstract class GeyserDefineCustomSkullsEvent implements Event {
/**
* The type of texture provided
*/
public enum SkullTextureType {
USERNAME,
UUID,
PROFILE,
SKIN_HASH
}
/**
* Registers the given username, UUID, base64 encoded profile, or skin hash as a custom skull blocks
* @param texture the username, UUID, base64 encoded profile, or skin hash
* @param type the type of texture provided
*/
public abstract void register(@NonNull String texture, @NonNull SkullTextureType type);
}

View File

@ -0,0 +1,42 @@
/*
* Copyright (c) 2019-2024 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 finished reloading and is accepting Bedrock connections again.
* Equivalent to the {@link GeyserPostInitializeEvent}
*
* @param extensionManager the extension manager
* @param eventBus the event bus
*/
public record GeyserPostReloadEvent(@NonNull ExtensionManager extensionManager, @NonNull EventBus<EventRegistrar> eventBus) implements Event {
}

View File

@ -0,0 +1,42 @@
/*
* Copyright (c) 2019-2024 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 about to reload. Primarily aimed at extensions, so they can decide on their own what to reload.
* After this event is fired, some lifecycle events can be fired again - such as the {@link GeyserLoadResourcePacksEvent}.
*
* @param extensionManager the extension manager
* @param eventBus the event bus
*/
public record GeyserPreReloadEvent(@NonNull ExtensionManager extensionManager, @NonNull EventBus<EventRegistrar> eventBus) implements Event {
}

View File

@ -136,4 +136,12 @@ public interface Extension extends EventRegistrar {
default GeyserApi geyserApi() {
return GeyserApi.api();
}
/**
* Disable the extension.
*/
default void disable() {
this.setEnabled(false);
this.eventBus().unregisterAll();
}
}

View File

@ -36,13 +36,13 @@ import java.util.Collection;
public abstract class ExtensionManager {
/**
* Gets an extension with the given name.
* Gets an extension by the given ID.
*
* @param name the name of the extension
* @return an extension with the given name
* @param id the ID of the extension
* @return an extension with the given ID
*/
@Nullable
public abstract Extension extension(@NonNull String name);
public abstract Extension extension(@NonNull String id);
/**
* Enables the given {@link Extension}.

View File

@ -25,10 +25,16 @@
package org.geysermc.geyser.api.extension.exception;
import java.io.Serial;
/**
* Thrown when an extension's description is invalid.
*/
public class InvalidDescriptionException extends Exception {
@Serial
private static final long serialVersionUID = 1L;
public InvalidDescriptionException(Throwable cause) {
super(cause);
}

View File

@ -25,10 +25,16 @@
package org.geysermc.geyser.api.extension.exception;
import java.io.Serial;
/**
* Thrown when an extension is invalid.
*/
public class InvalidExtensionException extends Exception {
@Serial
private static final long serialVersionUID = 1L;
public InvalidExtensionException(Throwable cause) {
super(cause);
}

View File

@ -29,6 +29,9 @@ 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;
/**
* This is used to store data for a custom item.
*/
@ -75,6 +78,20 @@ public interface CustomItemData {
*/
boolean displayHandheld();
/**
* 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 the item's texture size. This is to resize the item if the texture is not 16x16.
*
@ -89,6 +106,14 @@ public interface CustomItemData {
*/
@Nullable CustomRenderOffsets renderOffsets();
/**
* Gets the item's set of tags that can be used in Molang.
* Equivalent to "tag:some_tag"
*
* @return the item's tags, if they exist
*/
@NonNull Set<String> tags();
static CustomItemData.Builder builder() {
return GeyserApi.api().provider(CustomItemData.Builder.class);
}
@ -109,10 +134,16 @@ public interface CustomItemData {
Builder displayHandheld(boolean displayHandheld);
Builder creativeCategory(int creativeCategory);
Builder creativeGroup(@Nullable String creativeGroup);
Builder textureSize(int textureSize);
Builder renderOffsets(@Nullable CustomRenderOffsets renderOffsets);
Builder tags(@Nullable Set<String> tags);
CustomItemData build();
}
}

View File

@ -30,7 +30,6 @@ 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;
/**
@ -65,6 +64,14 @@ public interface NonVanillaCustomItemData extends CustomItemData {
*/
int maxDamage();
/**
* Gets the attack damage of the item.
* This is purely visual, and only applied to tools
*
* @return the attack damage of the item
*/
int attackDamage();
/**
* Gets the tool type of the item.
*
@ -107,20 +114,6 @@ public interface NonVanillaCustomItemData extends CustomItemData {
*/
@Nullable Set<String> 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.
@ -129,6 +122,34 @@ public interface NonVanillaCustomItemData extends CustomItemData {
*/
boolean isHat();
/**
* Gets if the item is a foil. This is used to determine if the item should be rendered with an enchantment glint effect.
*
* @return if the item is a foil
*/
boolean isFoil();
/**
* Gets if the item is edible.
*
* @return if the item is edible
*/
boolean isEdible();
/**
* Gets if the food item can always be eaten.
*
* @return if the item is allowed to be eaten all the time
*/
boolean canAlwaysEat();
/**
* Gets if the item is chargable, like a bow.
*
* @return if the item should act like a chargable item
*/
boolean isChargeable();
/**
* @deprecated Use {@link #displayHandheld()} instead.
* Gets if the item is a tool. This is used to set the render type of the item, if the item is handheld.
@ -156,6 +177,8 @@ public interface NonVanillaCustomItemData extends CustomItemData {
Builder maxDamage(int maxDamage);
Builder attackDamage(int attackDamage);
Builder toolType(@Nullable String toolType);
Builder toolTier(@Nullable String toolTier);
@ -168,12 +191,16 @@ public interface NonVanillaCustomItemData extends CustomItemData {
Builder repairMaterials(@Nullable Set<String> repairMaterials);
Builder creativeCategory(int creativeCategory);
Builder creativeGroup(@Nullable String creativeGroup);
Builder hat(boolean isHat);
Builder foil(boolean isFoil);
Builder edible(boolean isEdible);
Builder canAlwaysEat(boolean canAlwaysEat);
Builder chargeable(boolean isChargeable);
/**
* @deprecated Use {@link #displayHandheld(boolean)} instead.
*/
@ -182,6 +209,12 @@ public interface NonVanillaCustomItemData extends CustomItemData {
return displayHandheld(isTool);
}
@Override
Builder creativeCategory(int creativeCategory);
@Override
Builder creativeGroup(@Nullable String creativeGroup);
@Override
Builder customItemOptions(@NonNull CustomItemOptions customItemOptions);
@ -203,6 +236,9 @@ public interface NonVanillaCustomItemData extends CustomItemData {
@Override
Builder renderOffsets(@Nullable CustomRenderOffsets renderOffsets);
@Override
Builder tags(@Nullable Set<String> tags);
NonVanillaCustomItemData build();
}
}

View File

@ -50,6 +50,14 @@ public interface BedrockListener {
*/
int port();
/**
* Gets the broadcast port that's sent to Bedrock clients with the motd.
* This is the port that Bedrock clients will connect with. It usually does not differ from the listening port.
*
* @return the broadcast port
*/
int broadcastPort();
/**
* Gets the primary MOTD shown to Bedrock players if a ping passthrough setting is not enabled.
* <p>

View File

@ -67,4 +67,11 @@ public interface RemoteServer {
*/
@NonNull
AuthType authType();
/**
* Gets if we should attempt to resolve the SRV record for this server.
*
* @return if we should attempt to resolve the SRV record for this server
*/
boolean resolveSrv();
}

View File

@ -0,0 +1,82 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.api.pack;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.api.GeyserApi;
import java.io.IOException;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.Path;
/**
* Represents a pack codec that can be used
* to provide resource packs to clients.
*/
public abstract class PackCodec {
/**
* Gets the sha256 hash of the resource pack.
*
* @return the hash of the resource pack
*/
public abstract byte @NonNull [] sha256();
/**
* Gets the resource pack size.
*
* @return the resource pack file size
*/
public abstract long size();
/**
* Serializes the given resource pack into a byte buffer.
*
* @param resourcePack the resource pack to serialize
* @return the serialized resource pack
*/
@NonNull
public abstract SeekableByteChannel serialize(@NonNull ResourcePack resourcePack) throws IOException;
/**
* Creates a new resource pack from this codec.
*
* @return the new resource pack
*/
@NonNull
protected abstract ResourcePack create();
/**
* Creates a new pack provider from the given path.
*
* @param path the path to create the pack provider from
* @return the new pack provider
*/
@NonNull
public static PackCodec path(@NonNull Path path) {
return GeyserApi.api().provider(PathPackCodec.class, path);
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.api.pack;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.nio.file.Path;
/**
* Represents a pack codec that creates a resource
* pack from a path on the filesystem.
*/
public abstract class PathPackCodec extends PackCodec {
/**
* Gets the path of the resource pack.
*
* @return the path of the resource pack
*/
@NonNull
public abstract Path path();
}

View File

@ -0,0 +1,72 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.api.pack;
import org.checkerframework.checker.nullness.qual.NonNull;
/**
* Represents a resource pack sent to Bedrock clients
* <p>
* This representation of a resource pack only contains what
* Geyser requires to send it to the client.
*/
public interface ResourcePack {
/**
* The {@link PackCodec codec} for this pack.
*
* @return the codec for this pack
*/
@NonNull
PackCodec codec();
/**
* Gets the resource pack manifest.
*
* @return the resource pack manifest
*/
@NonNull
ResourcePackManifest manifest();
/**
* Gets the content key of the resource pack. Lack of a content key is represented by an empty String.
*
* @return the content key of the resource pack
*/
@NonNull
String contentKey();
/**
* Creates a resource pack with the given {@link PackCodec}.
*
* @param codec the pack codec
* @return the resource pack
*/
@NonNull
static ResourcePack create(@NonNull PackCodec codec) {
return codec.create();
}
}

View File

@ -0,0 +1,209 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.api.pack;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Collection;
import java.util.UUID;
/**
* Represents a resource pack manifest.
*/
public interface ResourcePackManifest {
/**
* Gets the format version of the resource pack.
*
* @return the format version
*/
int formatVersion();
/**
* Gets the header of the resource pack.
*
* @return the header
*/
@NonNull
Header header();
/**
* Gets the modules of the resource pack.
*
* @return the modules
*/
@NonNull
Collection<? extends Module> modules();
/**
* Gets the dependencies of the resource pack.
*
* @return the dependencies
*/
@NonNull
Collection<? extends Dependency> dependencies();
/**
* Represents the header of a resource pack.
*/
interface Header {
/**
* Gets the UUID of the resource pack.
*
* @return the UUID
*/
@NonNull
UUID uuid();
/**
* Gets the version of the resource pack.
*
* @return the version
*/
@NonNull
Version version();
/**
* Gets the name of the resource pack.
*
* @return the name
*/
@NonNull
String name();
/**
* Gets the description of the resource pack.
*
* @return the description
*/
@NonNull
String description();
/**
* Gets the minimum supported Minecraft version of the resource pack.
*
* @return the minimum supported Minecraft version
*/
@NonNull
Version minimumSupportedMinecraftVersion();
}
/**
* Represents a module of a resource pack.
*/
interface Module {
/**
* Gets the UUID of the module.
*
* @return the UUID
*/
@NonNull
UUID uuid();
/**
* Gets the version of the module.
*
* @return the version
*/
@NonNull
Version version();
/**
* Gets the type of the module.
*
* @return the type
*/
@NonNull
String type();
/**
* Gets the description of the module.
*
* @return the description
*/
@NonNull
String description();
}
/**
* Represents a dependency of a resource pack.
*/
interface Dependency {
/**
* Gets the UUID of the dependency.
*
* @return the uuid
*/
@NonNull
UUID uuid();
/**
* Gets the version of the dependency.
*
* @return the version
*/
@NonNull
Version version();
}
/**
* Represents a version of a resource pack.
*/
interface Version {
/**
* Gets the major version.
*
* @return the major version
*/
int major();
/**
* Gets the minor version.
*
* @return the minor version
*/
int minor();
/**
* Gets the patch version.
*
* @return the patch version
*/
int patch();
/**
* Gets the version formatted as a String.
*
* @return the version string
*/
@NonNull String toString();
}
}

View File

@ -0,0 +1,66 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.api.util;
import org.checkerframework.checker.nullness.qual.NonNull;
/**
* Represents the creative menu categories or tabs.
*/
public enum CreativeCategory {
COMMANDS("commands", 1),
CONSTRUCTION("construction", 2),
EQUIPMENT("equipment", 3),
ITEMS("items", 4),
NATURE("nature", 5),
NONE("none", 6);
private final String internalName;
private final int id;
CreativeCategory(String internalName, int id) {
this.internalName = internalName;
this.id = id;
}
/**
* Gets the internal name of the category.
*
* @return the name of the category
*/
public @NonNull String internalName() {
return internalName;
}
/**
* Gets the internal ID of the category.
*
* @return the ID of the category
*/
public int id() {
return id;
}
}

View File

@ -0,0 +1,49 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.api.util;
import org.checkerframework.checker.nullness.qual.NonNull;
/**
* Represents a Minecraft version.
*/
public interface MinecraftVersion {
/**
* Gets the Minecraft version as a String.
* Example: "1.20.2", or "1.20.40/1.20.41"
*
* @return the version string
*/
@NonNull String versionString();
/**
* Gets the protocol version of this Minecraft version.
*
* @return the protocol version
*/
int protocolVersion();
}

View File

@ -0,0 +1,45 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.api.util;
/**
* Represents the platform Geyser is running on.
*/
public record PlatformType(String platformName) {
@Deprecated
public static final PlatformType ANDROID = new PlatformType("Android");
public static final PlatformType BUNGEECORD = new PlatformType("BungeeCord");
public static final PlatformType FABRIC = new PlatformType("Fabric");
public static final PlatformType NEOFORGE = new PlatformType("NeoForge");
public static final PlatformType SPIGOT = new PlatformType("Spigot");
@Deprecated
public static final PlatformType SPONGE = new PlatformType("Sponge");
public static final PlatformType STANDALONE = new PlatformType("Standalone");
public static final PlatformType VELOCITY = new PlatformType("Velocity");
public static final PlatformType VIAPROXY = new PlatformType("ViaProxy");
}

View File

@ -30,7 +30,7 @@ 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
* This class was inspired by adventure's <a href="https://github.com/KyoriPowered/adventure/blob/main/4/api/src/main/java/net/kyori/adventure/util/TriState.java">TriState</a>
*/
public enum TriState {
/**

View File

@ -1,13 +1,14 @@
dependencies {
api(projects.core)
implementation(libs.adventure.text.serializer.bungeecord)
compileOnlyApi(libs.bungeecord.proxy)
}
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")
platformRelocate("org.yaml") // Broken as of 1.20
// These dependencies are already present on the platform
provided(libs.bungeecord.proxy)
@ -21,7 +22,6 @@ tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
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:.*"))
@ -32,4 +32,4 @@ tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
exclude(dependency("io.netty:netty-codec:.*"))
exclude(dependency("io.netty:netty-resolver-dns:.*"))
}
}
}

View File

@ -32,18 +32,26 @@ import net.md_5.bungee.protocol.packet.LoginSuccess;
import net.md_5.bungee.protocol.packet.SetCompression;
public class GeyserBungeeCompressionDisabler extends ChannelOutboundHandlerAdapter {
private boolean compressionDisabled = false;
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
if (!(msg instanceof SetCompression)) {
if (msg instanceof LoginSuccess) {
// We're past the point that compression can be enabled
// Fixes https://github.com/GeyserMC/Geyser/issues/4281
// The server may send a LoginDisconnect packet after compression is set.
if (!compressionDisabled) {
if (ctx.pipeline().get("compress") != null) {
ctx.pipeline().remove("compress");
compressionDisabled = true;
}
if (ctx.pipeline().get("decompress") != null) {
ctx.pipeline().remove("decompress");
compressionDisabled = true;
}
}
if (msg instanceof LoginSuccess) {
// We're past the point that compression can be enabled
ctx.pipeline().remove(this);
}
super.write(ctx, msg, promise);

View File

@ -30,6 +30,7 @@ import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.plugin.Plugin;
import org.geysermc.geyser.dump.BootstrapDumpInfo;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ -51,7 +52,8 @@ public class GeyserBungeeDumpInfo extends BootstrapDumpInfo {
this.plugins = new ArrayList<>();
for (net.md_5.bungee.api.config.ListenerInfo listener : proxy.getConfig().getListeners()) {
this.listeners.add(new ListenerInfo(listener.getHost().getHostString(), listener.getHost().getPort()));
InetSocketAddress address = (InetSocketAddress) listener.getSocketAddress();
this.listeners.add(new ListenerInfo(address.getHostString(), address.getPort()));
}
for (Plugin plugin : proxy.getPluginManager().getPlugins()) {

View File

@ -39,6 +39,7 @@ 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.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.GeyserBootstrap;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.network.netty.GeyserInjector;
@ -125,7 +126,7 @@ public class GeyserBungeeInjector extends GeyserInjector implements Listener {
.channel(LocalServerChannelWrapper.class)
.childHandler(new ChannelInitializer<>() {
@Override
protected void initChannel(Channel ch) throws Exception {
protected void initChannel(@NonNull Channel ch) throws Exception {
if (proxy.getConfig().getServers() == null) {
// Proxy hasn't finished loading all plugins - it loads the config after all plugins
// Probably doesn't need to be translatable?

View File

@ -35,12 +35,12 @@ import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.event.ProxyPingEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.protocol.ProtocolConstants;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.ping.GeyserPingInfo;
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.Arrays;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
@ -61,16 +61,11 @@ public class GeyserBungeePingPassthrough implements IGeyserPingPassthrough, List
}));
ProxyPingEvent event = future.join();
ServerPing response = event.getResponse();
GeyserPingInfo geyserPingInfo = new GeyserPingInfo(
return new GeyserPingInfo(
response.getDescriptionComponent().toLegacyText(),
new GeyserPingInfo.Players(response.getPlayers().getMax(), response.getPlayers().getOnline()),
new GeyserPingInfo.Version(response.getVersion().getName(), response.getVersion().getProtocol())
response.getPlayers().getMax(),
response.getPlayers().getOnline()
);
if (event.getResponse().getPlayers().getSample() != null) {
Arrays.stream(event.getResponse().getPlayers().getSample()).forEach(proxiedPlayer ->
geyserPingInfo.getPlayerList().add(proxiedPlayer.getName()));
}
return geyserPingInfo;
}
// This is static so pending connection can use it
@ -110,7 +105,7 @@ public class GeyserBungeePingPassthrough implements IGeyserPingPassthrough, List
}
@Override
public InetSocketAddress getVirtualHost() {
public @Nullable InetSocketAddress getVirtualHost() {
return null;
}

View File

@ -30,12 +30,13 @@ 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.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
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.util.PlatformType;
import org.geysermc.geyser.command.GeyserCommandManager;
import org.geysermc.geyser.configuration.GeyserConfiguration;
import org.geysermc.geyser.dump.BootstrapDumpInfo;
@ -44,7 +45,6 @@ import org.geysermc.geyser.ping.IGeyserPingPassthrough;
import org.geysermc.geyser.platform.bungeecord.command.GeyserBungeeCommandExecutor;
import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.util.FileUtils;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.IOException;
@ -72,12 +72,17 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
@Override
public void onLoad() {
onGeyserInitialize();
}
@Override
public void onGeyserInitialize() {
GeyserLocale.init(this);
// Copied from ViaVersion.
// https://github.com/ViaVersion/ViaVersion/blob/b8072aad86695cc8ec6f5e4103e43baf3abf6cc5/bungee/src/main/java/us/myles/ViaVersion/BungeePlugin.java#L43
try {
ProtocolConstants.class.getField("MINECRAFT_1_19_3");
ProtocolConstants.class.getField("MINECRAFT_1_20_3");
} catch (NoSuchFieldException e) {
getLogger().warning(" / \\");
getLogger().warning(" / \\");
@ -88,35 +93,63 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
getLogger().warning("/_____________\\");
}
if (!getDataFolder().exists())
getDataFolder().mkdir();
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, GeyserBungeeConfiguration.class);
} catch (IOException ex) {
getLogger().log(Level.SEVERE, GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
ex.printStackTrace();
if (!this.loadConfig()) {
return;
}
this.geyserLogger = new GeyserBungeeLogger(getLogger(), geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
this.geyser = GeyserImpl.load(PlatformType.BUNGEECORD, this);
this.geyserInjector = new GeyserBungeeInjector(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/"));
// 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);
}
@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.onGeyserEnable();
return;
}
try {
Field listenersField = BungeeCord.getInstance().getClass().getDeclaredField("listeners");
listenersField.setAccessible(true);
Collection<Channel> listeners = (Collection<Channel>) listenersField.get(BungeeCord.getInstance());
if (listeners.isEmpty()) {
this.getProxy().getScheduler().schedule(this, this::onGeyserEnable, tries, TimeUnit.SECONDS);
} else {
this.awaitStartupCompletion(++tries);
}
} catch (NoSuchFieldException | IllegalAccessException ex) {
ex.printStackTrace();
}
}
public void onGeyserEnable() {
if (GeyserImpl.getInstance().isReloading()) {
if (!loadConfig()) {
return;
}
this.geyserLogger.setDebug(geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
} else {
// For consistency with other platforms - create command manager before GeyserImpl#start()
// This ensures the command events are called before the item/block ones are
this.geyserCommandManager = new GeyserCommandManager(geyser);
this.geyserCommandManager.init();
}
// Force-disable query if enabled, or else Geyser won't enable
for (ListenerInfo info : getProxy().getConfig().getListeners()) {
if (info.isQueryEnabled() && info.getQueryPort() == geyserConfig.getBedrock().port()) {
@ -135,48 +168,21 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
}
}
// 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);
}
GeyserImpl.start();
@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();
if (geyserConfig.isLegacyPingPassthrough()) {
this.geyserBungeePingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
} else {
this.geyserBungeePingPassthrough = new GeyserBungeePingPassthrough(getProxy());
}
// No need to re-register commands or re-init injector when reloading
if (GeyserImpl.getInstance().isReloading()) {
return;
}
try {
Field listenersField = BungeeCord.getInstance().getClass().getDeclaredField("listeners");
listenersField.setAccessible(true);
Collection<Channel> listeners = (Collection<Channel>) 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();
}
}
private void postStartup() {
GeyserImpl.start();
this.geyserInjector = new GeyserBungeeInjector(this);
this.geyserInjector.initializeLocalChannel(this);
this.geyserCommandManager = new GeyserCommandManager(geyser);
this.geyserCommandManager.init();
this.getProxy().getPluginManager().registerCommand(this, new GeyserBungeeCommandExecutor("geyser", this.geyser, this.geyserCommandManager.getCommands()));
for (Map.Entry<Extension, Map<String, Command>> entry : this.geyserCommandManager.extensionCommands().entrySet()) {
Map<String, Command> commands = entry.getValue();
@ -186,16 +192,17 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
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());
@Override
public void onGeyserDisable() {
if (geyser != null) {
geyser.disable();
}
}
@Override
public void onDisable() {
public void onGeyserShutdown() {
if (geyser != null) {
geyser.shutdown();
}
@ -204,6 +211,11 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
}
}
@Override
public void onDisable() {
this.onGeyserShutdown();
}
@Override
public GeyserBungeeConfiguration getGeyserConfig() {
return geyserConfig;
@ -245,7 +257,7 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
return this.geyserInjector.getServerSocketAddress();
}
@NotNull
@NonNull
@Override
public String getServerBindAddress() {
return findCompatibleListener().map(InetSocketAddress::getHostString).orElse("");
@ -271,4 +283,20 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
.map(info -> (InetSocketAddress) info.getSocketAddress())
.findFirst();
}
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
private boolean loadConfig() {
try {
if (!getDataFolder().exists()) //noinspection ResultOfMethodCallIgnored
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, GeyserBungeeConfiguration.class);
} catch (IOException ex) {
getLogger().log(Level.SEVERE, GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
ex.printStackTrace();
return false;
}
return true;
}
}

View File

@ -29,6 +29,7 @@ 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.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.command.GeyserCommandSource;
import org.geysermc.geyser.text.GeyserLocale;
@ -50,7 +51,7 @@ public class BungeeCommandSource implements GeyserCommandSource {
}
@Override
public void sendMessage(String message) {
public void sendMessage(@NonNull String message) {
handle.sendMessage(TextComponent.fromLegacyText(message));
}

View File

@ -1,114 +0,0 @@
plugins {
id("fabric-loom") version "1.0-SNAPSHOT"
id("com.modrinth.minotaur") version "2.+"
}
java {
targetCompatibility = JavaVersion.VERSION_17
sourceCompatibility = JavaVersion.VERSION_17
}
dependencies {
//to change the versions see the gradle.properties file
minecraft(libs.fabric.minecraft)
mappings(loom.officialMojangMappings())
modImplementation(libs.fabric.loader)
// Fabric API. This is technically optional, but you probably want it anyway.
modImplementation(libs.fabric.api)
// This should be in the libs TOML, but something about modImplementation AND include just doesn't work
include(modImplementation("me.lucko", "fabric-permissions-api", "0.2-SNAPSHOT"))
// PSA: Some older mods, compiled on Loom 0.2.1, might have outdated Maven POMs.
// You may need to force-disable transitiveness on them.
api(projects.core)
shadow(projects.core) {
exclude(group = "com.google.guava", module = "guava")
exclude(group = "com.google.code.gson", module = "gson")
exclude(group = "org.slf4j")
exclude(group = "com.nukkitx.fastutil")
exclude(group = "io.netty.incubator")
}
}
repositories {
mavenLocal()
maven("https://repo.opencollab.dev/maven-releases/")
maven("https://repo.opencollab.dev/maven-snapshots/")
maven("https://jitpack.io")
maven("https://oss.sonatype.org/content/repositories/snapshots/")
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
}
application {
mainClass.set("org.geysermc.geyser.platform.fabric.GeyserFabricMain")
}
tasks {
// Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task
// if it is present.
// If you remove this task, sources will not be generated.
sourcesJar {
archiveClassifier.set("sources")
from(sourceSets.main.get().allSource)
}
shadowJar {
// Mirrors the example fabric project, otherwise tons of dependencies are shaded that shouldn't be
configurations = listOf(project.configurations.shadow.get())
// The remapped shadowJar is the final desired Geyser-Fabric.jar
archiveVersion.set(project.version.toString())
archiveClassifier.set("shaded")
relocate("org.objectweb.asm", "org.geysermc.relocate.asm")
relocate("org.yaml", "org.geysermc.relocate.yaml") // https://github.com/CardboardPowered/cardboard/issues/139
relocate("com.fasterxml.jackson", "org.geysermc.relocate.jackson")
relocate("net.kyori", "org.geysermc.relocate.kyori")
dependencies {
// Exclude everything EXCEPT some DNS stuff required for HAProxy
exclude(dependency("io.netty:netty-transport-classes-epoll:.*"))
exclude(dependency("io.netty:netty-transport-native-epoll:.*"))
exclude(dependency("io.netty:netty-transport-native-unix-common:.*"))
exclude(dependency("io.netty:netty-transport-classes-kqueue:.*"))
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-resolver-dns:.*"))
exclude(dependency("io.netty:netty-resolver-dns-native-macos:.*"))
}
}
remapJar {
dependsOn(shadowJar)
inputFile.set(shadowJar.get().archiveFile)
archiveBaseName.set("Geyser-Fabric")
archiveClassifier.set("")
archiveVersion.set("")
}
}
modrinth {
token.set(System.getenv("MODRINTH_TOKEN")) // Even though this is the default value, apparently this prevents GitHub Actions caching the token?
projectId.set("wKkoqHrH")
versionNumber.set(project.version as String + "-" + System.getenv("GITHUB_RUN_NUMBER"))
versionType.set("beta")
changelog.set("A changelog can be found at https://github.com/GeyserMC/Geyser/commits")
syncBodyFrom.set(rootProject.file("README.md").readText())
uploadFile.set(tasks.getByPath("remapJar"))
gameVersions.addAll("1.19", "1.19.1", "1.19.2", "1.19.3", "1.19.4")
loaders.add("fabric")
dependencies {
required.project("fabric-api")
}
}

View File

@ -1,266 +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.platform.fabric;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.ModContainer;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.server.MinecraftServer;
import org.apache.logging.log4j.LogManager;
import org.geysermc.common.PlatformType;
import org.geysermc.geyser.GeyserBootstrap;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.GeyserLogger;
import org.geysermc.geyser.api.command.Command;
import org.geysermc.geyser.command.GeyserCommand;
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.ping.GeyserLegacyPingPassthrough;
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
import org.geysermc.geyser.platform.fabric.command.GeyserFabricCommandExecutor;
import org.geysermc.geyser.platform.fabric.world.GeyserFabricWorldManager;
import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.util.FileUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
public class GeyserFabricMod implements ModInitializer, GeyserBootstrap {
private static GeyserFabricMod instance;
private boolean reloading;
private GeyserImpl geyser;
private ModContainer mod;
private Path dataFolder;
private MinecraftServer server;
private GeyserCommandManager geyserCommandManager;
private GeyserFabricConfiguration geyserConfig;
private GeyserFabricLogger geyserLogger;
private IGeyserPingPassthrough geyserPingPassthrough;
private WorldManager geyserWorldManager;
@Override
public void onInitialize() {
instance = this;
mod = FabricLoader.getInstance().getModContainer("geyser-fabric").orElseThrow();
this.onEnable();
if (FabricLoader.getInstance().getEnvironmentType() == EnvType.SERVER) {
// Set as an event so we can get the proper IP and port if needed
ServerLifecycleEvents.SERVER_STARTED.register(this::startGeyser);
}
}
@Override
public void onEnable() {
dataFolder = FabricLoader.getInstance().getConfigDir().resolve("Geyser-Fabric");
if (!dataFolder.toFile().exists()) {
//noinspection ResultOfMethodCallIgnored
dataFolder.toFile().mkdir();
}
// Init dataFolder first as local language overrides call getConfigFolder()
GeyserLocale.init(this);
try {
File configFile = FileUtils.fileOrCopiedFromResource(dataFolder.resolve("config.yml").toFile(), "config.yml",
(x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this);
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserFabricConfiguration.class);
} catch (IOException ex) {
LogManager.getLogger("geyser-fabric").error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
ex.printStackTrace();
return;
}
this.geyserLogger = new GeyserFabricLogger(geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
this.geyser = GeyserImpl.load(PlatformType.FABRIC, this);
if (server == null) {
// Server has yet to start
// Register onDisable so players are properly kicked
ServerLifecycleEvents.SERVER_STOPPING.register((server) -> onDisable());
ServerPlayConnectionEvents.JOIN.register((handler, $, $$) -> GeyserFabricUpdateListener.onPlayReady(handler));
} else {
// Server has started and this is a reload
startGeyser(this.server);
reloading = false;
}
}
/**
* Initialize core Geyser.
* A function, as it needs to be called in different places depending on if Geyser is being reloaded or not.
*
* @param server The minecraft server.
*/
public void startGeyser(MinecraftServer server) {
this.server = server;
GeyserImpl.start();
this.geyserPingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
this.geyserCommandManager = new GeyserCommandManager(geyser);
this.geyserCommandManager.init();
this.geyserWorldManager = new GeyserFabricWorldManager(server);
// Start command building
// Set just "geyser" as the help command
GeyserFabricCommandExecutor helpExecutor = new GeyserFabricCommandExecutor(geyser,
(GeyserCommand) geyser.commandManager().getCommands().get("help"));
LiteralArgumentBuilder<CommandSourceStack> builder = Commands.literal("geyser").executes(helpExecutor);
// Register all subcommands as valid
for (Map.Entry<String, Command> command : geyser.commandManager().getCommands().entrySet()) {
GeyserFabricCommandExecutor executor = new GeyserFabricCommandExecutor(geyser, (GeyserCommand) command.getValue());
builder.then(Commands.literal(command.getKey())
.executes(executor)
// Could also test for Bedrock but depending on when this is called it may backfire
.requires(executor::testPermission));
}
server.getCommands().getDispatcher().register(builder);
}
@Override
public void onDisable() {
if (geyser != null) {
geyser.shutdown();
geyser = null;
}
if (!reloading) {
this.server = null;
}
}
@Override
public GeyserConfiguration getGeyserConfig() {
return geyserConfig;
}
@Override
public GeyserLogger getGeyserLogger() {
return geyserLogger;
}
@Override
public GeyserCommandManager getGeyserCommandManager() {
return geyserCommandManager;
}
@Override
public IGeyserPingPassthrough getGeyserPingPassthrough() {
return geyserPingPassthrough;
}
@Override
public WorldManager getWorldManager() {
return geyserWorldManager;
}
@Override
public Path getConfigFolder() {
return dataFolder;
}
@Override
public BootstrapDumpInfo getDumpInfo() {
return new GeyserFabricDumpInfo(server);
}
@Override
public String getMinecraftServerVersion() {
return this.server.getServerVersion();
}
@NotNull
@Override
public String getServerBindAddress() {
return this.server.getLocalIp();
}
@Override
public int getServerPort() {
return ((GeyserServerPortGetter) server).geyser$getServerPort();
}
@Override
public boolean testFloodgatePluginPresent() {
Optional<ModContainer> floodgate = FabricLoader.getInstance().getModContainer("floodgate");
if (floodgate.isPresent()) {
geyserConfig.loadFloodgate(this, floodgate.orElse(null));
return true;
}
return false;
}
@Nullable
@Override
public InputStream getResourceOrNull(String resource) {
// We need to handle this differently, because Fabric shares the classloader across multiple mods
Path path = this.mod.findPath(resource).orElse(null);
if (path == null) {
return null;
}
try {
return path.getFileSystem()
.provider()
.newInputStream(path);
} catch (IOException e) {
return null;
}
}
public void setReloading(boolean reloading) {
this.reloading = reloading;
}
public static GeyserFabricMod getInstance() {
return instance;
}
}

View File

@ -1,14 +0,0 @@
{
"required": true,
"package": "org.geysermc.geyser.platform.fabric.mixin",
"compatibilityLevel": "JAVA_16",
"client": [
"client.IntegratedServerMixin"
],
"server": [
"server.MinecraftDedicatedServerMixin"
],
"injectors": {
"defaultRequire": 1
}
}

View File

@ -0,0 +1,15 @@
architectury {
common("neoforge", "fabric")
}
loom {
mixin.defaultRefmapName.set("geyser-refmap.json")
}
dependencies {
api(projects.core)
compileOnly(libs.mixin)
// Only here to suppress "unknown enum constant EnvType.CLIENT" warnings. DO NOT USE!
compileOnly(libs.fabric.loader)
}

View File

@ -0,0 +1,76 @@
plugins {
application
}
architectury {
platformSetupLoomIde()
fabric()
}
val includeTransitive: Configuration = configurations.getByName("includeTransitive")
dependencies {
modImplementation(libs.fabric.loader)
modApi(libs.fabric.api)
api(project(":mod", configuration = "namedElements"))
shadow(project(path = ":mod", configuration = "transformProductionFabric")) {
isTransitive = false
}
shadow(projects.core) { isTransitive = false }
includeTransitive(projects.core)
// These are NOT transitively included, and instead shadowed + relocated.
// Avoids fabric complaining about non-SemVer versioning
// TODO: re-evaluate after loom 1.6 (https://github.com/FabricMC/fabric-loom/pull/1075)
shadow(libs.protocol.connection) { isTransitive = false }
shadow(libs.protocol.common) { isTransitive = false }
shadow(libs.protocol.codec) { isTransitive = false }
shadow(libs.mcauthlib) { isTransitive = false }
shadow(libs.raknet) { isTransitive = false }
shadow(libs.netty.codec.haproxy) { isTransitive = false }
shadow("org.cloudburstmc:nbt:3.0.2.Final") { isTransitive = false }
shadow("io.netty:netty-codec-dns:4.1.103.Final") { isTransitive = false }
shadow("io.netty:netty-resolver-dns-classes-macos:4.1.103.Final") { isTransitive = false }
// Consequences of shading + relocating mcauthlib: shadow/relocate mcpl!
shadow(libs.mcprotocollib) { isTransitive = false }
// Since we also relocate cloudburst protocol: shade erosion common
shadow(libs.erosion.common) { isTransitive = false }
// Permissions
modImplementation(libs.fabric.permissions)
include(libs.fabric.permissions)
}
application {
mainClass.set("org.geysermc.geyser.platform.fabric.GeyserFabricMain")
}
relocate("org.cloudburstmc.nbt")
relocate("org.cloudburstmc.netty")
relocate("org.cloudburstmc.protocol")
relocate("io.netty.handler.codec.dns")
relocate("io.netty.handler.codec.haproxy")
relocate("io.netty.resolver.dns.macos")
relocate("com.github.steveice10.mc.protocol")
relocate("com.github.steveice10.mc.auth")
relocate("com.github.steveice10.packetlib")
tasks {
remapJar {
archiveBaseName.set("Geyser-Fabric")
}
remapModrinthJar {
archiveBaseName.set("geyser-fabric")
}
}
modrinth {
loaders.add("fabric")
dependencies {
required.project("fabric-api")
}
}

View File

@ -0,0 +1 @@
loom.platform=fabric

View File

@ -0,0 +1,72 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.fabric;
import me.lucko.fabric.api.permissions.v0.Permissions;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.world.entity.player.Player;
import org.geysermc.geyser.platform.mod.GeyserModBootstrap;
import org.geysermc.geyser.platform.mod.GeyserModUpdateListener;
import org.checkerframework.checker.nullness.qual.NonNull;
public class GeyserFabricBootstrap extends GeyserModBootstrap implements ModInitializer {
public GeyserFabricBootstrap() {
super(new GeyserFabricPlatform());
}
@Override
public void onInitialize() {
if (FabricLoader.getInstance().getEnvironmentType() == EnvType.SERVER) {
// Set as an event, so we can get the proper IP and port if needed
ServerLifecycleEvents.SERVER_STARTED.register((server) -> {
this.setServer(server);
onGeyserEnable();
});
}
// These are only registered once
ServerLifecycleEvents.SERVER_STOPPING.register((server) -> onGeyserShutdown());
ServerPlayConnectionEvents.JOIN.register((handler, $, $$) -> GeyserModUpdateListener.onPlayReady(handler.getPlayer()));
this.onGeyserInitialize();
}
@Override
public boolean hasPermission(@NonNull Player source, @NonNull String permissionNode) {
return Permissions.check(source, permissionNode);
}
@Override
public boolean hasPermission(@NonNull CommandSourceStack source, @NonNull String permissionNode, int permissionLevel) {
return Permissions.check(source, permissionNode, permissionLevel);
}
}

View File

@ -43,21 +43,27 @@ import java.util.stream.Collectors;
@Getter
public class GeyserFabricDumpInfo extends BootstrapDumpInfo {
private String platformVersion = null;
private final String platformName;
private String platformVersion;
private final String minecraftVersion;
private final EnvType environmentType;
@AsteriskSerializer.Asterisk(isIp = true)
private final String serverIP;
private final int serverPort;
private final boolean onlineMode;
private final List<ModInfo> mods;
public GeyserFabricDumpInfo(MinecraftServer server) {
this.platformName = server.getServerModName();
FabricLoader.getInstance().getModContainer("fabricloader").ifPresent(mod ->
this.platformVersion = mod.getMetadata().getVersion().getFriendlyString());
this.minecraftVersion = server.getServerVersion();
this.environmentType = FabricLoader.getInstance().getEnvironmentType();
this.serverIP = server.getLocalIp() == null ? "unknown" : server.getLocalIp();
this.serverPort = server.getPort();
this.onlineMode = server.usesAuthentication();
this.mods = new ArrayList<>();
for (ModContainer mod : FabricLoader.getInstance().getAllMods()) {

View File

@ -0,0 +1,99 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.fabric;
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.ModContainer;
import net.minecraft.server.MinecraftServer;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.api.util.PlatformType;
import org.geysermc.geyser.dump.BootstrapDumpInfo;
import org.geysermc.geyser.platform.mod.GeyserModBootstrap;
import org.geysermc.geyser.platform.mod.platform.GeyserModPlatform;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.Optional;
public class GeyserFabricPlatform implements GeyserModPlatform {
private final ModContainer mod;
public GeyserFabricPlatform() {
this.mod = FabricLoader.getInstance().getModContainer("geyser-fabric").orElseThrow();
}
@Override
public @NonNull PlatformType platformType() {
return PlatformType.FABRIC;
}
@Override
public @NonNull String configPath() {
return "Geyser-Fabric";
}
@Override
public @NonNull Path dataFolder(@NonNull String modId) {
return FabricLoader.getInstance().getConfigDir().resolve(modId);
}
@Override
public @NonNull BootstrapDumpInfo dumpInfo(@NonNull MinecraftServer server) {
return new GeyserFabricDumpInfo(server);
}
@Override
public boolean testFloodgatePluginPresent(@NonNull GeyserModBootstrap bootstrap) {
Optional<ModContainer> floodgate = FabricLoader.getInstance().getModContainer("floodgate");
if (floodgate.isPresent()) {
Path floodgateDataFolder = FabricLoader.getInstance().getConfigDir().resolve("floodgate");
bootstrap.getGeyserConfig().loadFloodgate(bootstrap, floodgateDataFolder);
return true;
}
return false;
}
@Override
public @Nullable InputStream resolveResource(@NonNull String resource) {
// We need to handle this differently, because Fabric shares the classloader across multiple mods
Path path = this.mod.findPath(resource).orElse(null);
if (path == null) {
return null;
}
try {
return path.getFileSystem()
.provider()
.newInputStream(path);
} catch (IOException e) {
return null;
}
}
}

View File

@ -9,23 +9,23 @@
],
"contact": {
"website": "${url}",
"repo": "https://github.com/GeyserMC/Geyser-Fabric"
"repo": "https://github.com/GeyserMC/Geyser"
},
"license": "MIT",
"icon": "assets/geyser-fabric/icon.png",
"icon": "assets/geyser/icon.png",
"environment": "*",
"entrypoints": {
"main": [
"org.geysermc.geyser.platform.fabric.GeyserFabricMod"
"org.geysermc.geyser.platform.fabric.GeyserFabricBootstrap"
]
},
"mixins": [
"geyser-fabric.mixins.json"
"geyser.mixins.json"
],
"depends": {
"fabricloader": ">=0.14.8",
"fabricloader": ">=0.15.2",
"fabric": "*",
"minecraft": ">=1.19",
"minecraft": ">=1.20.4",
"fabric-permissions-api-v0": "*"
}
}

View File

@ -0,0 +1,51 @@
plugins {
application
}
// This is provided by "org.cloudburstmc.math.mutable" too, so yeet.
// NeoForge's class loader is *really* annoying.
provided("org.cloudburstmc.math", "api")
architectury {
platformSetupLoomIde()
neoForge()
}
val includeTransitive: Configuration = configurations.getByName("includeTransitive")
dependencies {
// See https://github.com/google/guava/issues/6618
modules {
module("com.google.guava:listenablefuture") {
replacedBy("com.google.guava:guava", "listenablefuture is part of guava")
}
}
neoForge(libs.neoforge.minecraft)
api(project(":mod", configuration = "namedElements"))
shadow(project(path = ":mod", configuration = "transformProductionNeoForge")) {
isTransitive = false
}
shadow(project(path = ":core")) { isTransitive = false }
includeTransitive(projects.core)
}
application {
mainClass.set("org.geysermc.geyser.platform.forge.GeyserNeoForgeMain")
}
tasks {
remapJar {
archiveBaseName.set("Geyser-NeoForge")
}
remapModrinthJar {
archiveBaseName.set("geyser-neoforge")
}
}
modrinth {
loaders.add("neoforge")
}

View File

@ -0,0 +1 @@
loom.platform=neoforge

View File

@ -0,0 +1,83 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.neoforge;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.world.entity.player.Player;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.fml.common.Mod;
import net.neoforged.fml.loading.FMLLoader;
import net.neoforged.neoforge.common.NeoForge;
import net.neoforged.neoforge.event.entity.player.PlayerEvent;
import net.neoforged.neoforge.event.server.ServerStartedEvent;
import net.neoforged.neoforge.event.server.ServerStoppingEvent;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.platform.mod.GeyserModBootstrap;
import org.geysermc.geyser.platform.mod.GeyserModUpdateListener;
@Mod(ModConstants.MOD_ID)
public class GeyserNeoForgeBootstrap extends GeyserModBootstrap {
private final GeyserNeoForgePermissionHandler permissionHandler = new GeyserNeoForgePermissionHandler();
public GeyserNeoForgeBootstrap() {
super(new GeyserNeoForgePlatform());
if (FMLLoader.getDist() == Dist.DEDICATED_SERVER) {
// Set as an event so we can get the proper IP and port if needed
NeoForge.EVENT_BUS.addListener(this::onServerStarted);
}
NeoForge.EVENT_BUS.addListener(this::onServerStopping);
NeoForge.EVENT_BUS.addListener(this::onPlayerJoin);
NeoForge.EVENT_BUS.addListener(this.permissionHandler::onPermissionGather);
this.onGeyserInitialize();
}
private void onServerStarted(ServerStartedEvent event) {
this.setServer(event.getServer());
this.onGeyserEnable();
}
private void onServerStopping(ServerStoppingEvent event) {
this.onGeyserShutdown();
}
private void onPlayerJoin(PlayerEvent.PlayerLoggedInEvent event) {
GeyserModUpdateListener.onPlayReady(event.getEntity());
}
@Override
public boolean hasPermission(@NonNull Player source, @NonNull String permissionNode) {
return this.permissionHandler.hasPermission(source, permissionNode);
}
@Override
public boolean hasPermission(@NonNull CommandSourceStack source, @NonNull String permissionNode, int permissionLevel) {
return this.permissionHandler.hasPermission(source, permissionNode, permissionLevel);
}
}

View File

@ -0,0 +1,84 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.neoforge;
import lombok.AllArgsConstructor;
import lombok.Getter;
import net.minecraft.server.MinecraftServer;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.fml.ModList;
import net.neoforged.fml.loading.FMLLoader;
import net.neoforged.neoforgespi.language.IModInfo;
import org.geysermc.geyser.dump.BootstrapDumpInfo;
import org.geysermc.geyser.text.AsteriskSerializer;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
@Getter
public class GeyserNeoForgeDumpInfo extends BootstrapDumpInfo {
private final String platformName;
private final String platformVersion;
private final String minecraftVersion;
private final Dist dist;
@AsteriskSerializer.Asterisk(isIp = true)
private final String serverIP;
private final int serverPort;
private final boolean onlineMode;
private final List<ModInfo> mods;
public GeyserNeoForgeDumpInfo(MinecraftServer server) {
this.platformName = FMLLoader.launcherHandlerName();
this.platformVersion = FMLLoader.versionInfo().neoForgeVersion();
this.minecraftVersion = FMLLoader.versionInfo().mcVersion();
this.dist = FMLLoader.getDist();
this.serverIP = server.getLocalIp() == null ? "unknown" : server.getLocalIp();
this.serverPort = server.getPort();
this.onlineMode = server.usesAuthentication();
this.mods = new ArrayList<>();
for (IModInfo mod : ModList.get().getMods()) {
this.mods.add(new ModInfo(
ModList.get().isLoaded(mod.getModId()),
mod.getModId(),
mod.getVersion().toString(),
mod.getModURL().map(URL::toString).orElse("")
));
}
}
@Getter
@AllArgsConstructor
public static class ModInfo {
public boolean enabled;
public String name;
public String version;
public String url;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -23,20 +23,22 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.sponge;
package org.geysermc.geyser.platform.neoforge;
import org.geysermc.geyser.GeyserMain;
public class GeyserSpongeMain extends GeyserMain {
public class GeyserNeoForgeMain extends GeyserMain {
public static void main(String[] args) {
new GeyserSpongeMain().displayMessage();
new GeyserNeoForgeMain().displayMessage();
}
@Override
public String getPluginType() {
return "Sponge";
return "NeoForge";
}
@Override
public String getPluginFolder() {
return "mods";
}

View File

@ -0,0 +1,149 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.neoforge;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.player.Player;
import net.neoforged.neoforge.server.permission.PermissionAPI;
import net.neoforged.neoforge.server.permission.events.PermissionGatherEvent;
import net.neoforged.neoforge.server.permission.nodes.PermissionDynamicContextKey;
import net.neoforged.neoforge.server.permission.nodes.PermissionNode;
import net.neoforged.neoforge.server.permission.nodes.PermissionType;
import net.neoforged.neoforge.server.permission.nodes.PermissionTypes;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.Constants;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.command.Command;
import org.geysermc.geyser.command.GeyserCommandManager;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
public class GeyserNeoForgePermissionHandler {
private static final Constructor<?> PERMISSION_NODE_CONSTRUCTOR;
static {
try {
@SuppressWarnings("rawtypes")
Constructor<PermissionNode> constructor = PermissionNode.class.getDeclaredConstructor(
String.class,
PermissionType.class,
PermissionNode.PermissionResolver.class,
PermissionDynamicContextKey[].class
);
constructor.setAccessible(true);
PERMISSION_NODE_CONSTRUCTOR = constructor;
} catch (NoSuchMethodException e) {
throw new RuntimeException("Unable to construct PermissionNode!", e);
}
}
private final Map<String, PermissionNode<Boolean>> permissionNodes = new HashMap<>();
public void onPermissionGather(PermissionGatherEvent.Nodes event) {
this.registerNode(Constants.UPDATE_PERMISSION, event);
GeyserCommandManager commandManager = GeyserImpl.getInstance().commandManager();
for (Map.Entry<String, Command> entry : commandManager.commands().entrySet()) {
Command command = entry.getValue();
// Don't register aliases
if (!command.name().equals(entry.getKey())) {
continue;
}
this.registerNode(command.permission(), event);
}
for (Map<String, Command> commands : commandManager.extensionCommands().values()) {
for (Map.Entry<String, Command> entry : commands.entrySet()) {
Command command = entry.getValue();
// Don't register aliases
if (!command.name().equals(entry.getKey())) {
continue;
}
this.registerNode(command.permission(), event);
}
}
}
public boolean hasPermission(@NonNull Player source, @NonNull String permissionNode) {
PermissionNode<Boolean> node = this.permissionNodes.get(permissionNode);
if (node == null) {
GeyserImpl.getInstance().getLogger().warning("Unable to find permission node " + permissionNode);
return false;
}
return PermissionAPI.getPermission((ServerPlayer) source, node);
}
public boolean hasPermission(@NonNull CommandSourceStack source, @NonNull String permissionNode, int permissionLevel) {
if (!source.isPlayer()) {
return true;
}
assert source.getPlayer() != null;
boolean permission = this.hasPermission(source.getPlayer(), permissionNode);
if (!permission) {
return source.getPlayer().hasPermissions(permissionLevel);
}
return true;
}
private void registerNode(String node, PermissionGatherEvent.Nodes event) {
PermissionNode<Boolean> permissionNode = this.createNode(node);
// NeoForge likes to crash if you try and register a duplicate node
if (!event.getNodes().contains(permissionNode)) {
event.addNodes(permissionNode);
this.permissionNodes.put(node, permissionNode);
}
}
@SuppressWarnings("unchecked")
private PermissionNode<Boolean> createNode(String node) {
// The typical constructors in PermissionNode require a
// mod id, which means our permission nodes end up becoming
// geyser_neoforge.<node> instead of just <node>. We work around
// this by using reflection to access the constructor that
// doesn't require a mod id or ResourceLocation.
try {
return (PermissionNode<Boolean>) PERMISSION_NODE_CONSTRUCTOR.newInstance(
node,
PermissionTypes.BOOLEAN,
(PermissionNode.PermissionResolver<Boolean>) (player, playerUUID, context) -> false,
new PermissionDynamicContextKey[0]
);
} catch (Exception e) {
throw new RuntimeException("Unable to create permission node " + node, e);
}
}
}

View File

@ -0,0 +1,72 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.neoforge;
import net.minecraft.server.MinecraftServer;
import net.neoforged.fml.loading.FMLPaths;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.GeyserBootstrap;
import org.geysermc.geyser.api.util.PlatformType;
import org.geysermc.geyser.dump.BootstrapDumpInfo;
import org.geysermc.geyser.platform.mod.GeyserModBootstrap;
import org.geysermc.geyser.platform.mod.platform.GeyserModPlatform;
import java.io.InputStream;
import java.nio.file.Path;
public class GeyserNeoForgePlatform implements GeyserModPlatform {
@Override
public @NonNull PlatformType platformType() {
return PlatformType.NEOFORGE;
}
@Override
public @NonNull String configPath() {
return "Geyser-NeoForge";
}
@Override
public @NonNull Path dataFolder(@NonNull String modId) {
return FMLPaths.CONFIGDIR.get().resolve(modId);
}
@Override
public @NonNull BootstrapDumpInfo dumpInfo(@NonNull MinecraftServer server) {
return new GeyserNeoForgeDumpInfo(server);
}
@Override
public boolean testFloodgatePluginPresent(@NonNull GeyserModBootstrap bootstrap) {
return false; // No Floodgate mod for NeoForge yet
}
@Override
public @Nullable InputStream resolveResource(@NonNull String resource) {
return GeyserBootstrap.class.getClassLoader().getResourceAsStream(resource);
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.neoforge;
public class ModConstants {
public static final String MOD_ID = "geyser_neoforge";
}

View File

@ -0,0 +1,25 @@
modLoader="javafml"
loaderVersion="[1,)"
license="MIT"
[[mods]]
modId="geyser_neoforge"
version="${version}"
displayName="Geyser"
displayURL="https://geysermc.org/"
logoFile= "../assets/geyser/icon.png"
authors="GeyserMC"
description="${description}"
[[mixins]]
config = "geyser.mixins.json"
[[dependencies.geyser_neoforge]]
modId="neoforge"
type="required"
versionRange="[20.4.48-beta,)"
ordering="NONE"
side="BOTH"
[[dependencies.geyser_neoforge]]
modId="minecraft"
type="required"
versionRange="[1.20,1.21)"
ordering="NONE"
side="BOTH"

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -23,16 +23,21 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.translator.collision;
package org.geysermc.geyser.platform.mod;
import lombok.EqualsAndHashCode;
import io.netty.channel.ChannelFuture;
@EqualsAndHashCode(callSuper = true)
@CollisionRemapper(regex = "^spawner$")
public class SpawnerCollision extends SolidCollision {
public SpawnerCollision(String params) {
super(params);
// Increase pushAwayTolerance to work around https://bugs.mojang.com/browse/MCPE-41996
pushAwayTolerance = 0.0002;
}
import java.util.List;
/**
* Represents a getter to the server channels in the connection listener class.
*/
public interface GeyserChannelGetter {
/**
* Returns the channels.
*
* @return The channels.
*/
List<ChannelFuture> geyser$getChannels();
}

View File

@ -0,0 +1,281 @@
/*
* 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.platform.mod;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.entity.player.Player;
import org.apache.logging.log4j.LogManager;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.GeyserBootstrap;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.GeyserLogger;
import org.geysermc.geyser.api.command.Command;
import org.geysermc.geyser.api.extension.Extension;
import org.geysermc.geyser.command.GeyserCommand;
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.ping.GeyserLegacyPingPassthrough;
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
import org.geysermc.geyser.platform.mod.command.GeyserModCommandExecutor;
import org.geysermc.geyser.platform.mod.platform.GeyserModPlatform;
import org.geysermc.geyser.platform.mod.world.GeyserModWorldManager;
import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.util.FileUtils;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.Map;
import java.util.UUID;
@RequiredArgsConstructor
public abstract class GeyserModBootstrap implements GeyserBootstrap {
@Getter
private static GeyserModBootstrap instance;
private final GeyserModPlatform platform;
private GeyserImpl geyser;
private Path dataFolder;
@Setter
private MinecraftServer server;
private GeyserCommandManager geyserCommandManager;
private GeyserModConfiguration geyserConfig;
private GeyserModInjector geyserInjector;
private GeyserModLogger geyserLogger;
private IGeyserPingPassthrough geyserPingPassthrough;
private WorldManager geyserWorldManager;
@Override
public void onGeyserInitialize() {
instance = this;
dataFolder = this.platform.dataFolder(this.platform.configPath());
GeyserLocale.init(this);
if (!loadConfig()) {
return;
}
this.geyserLogger = new GeyserModLogger(geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
this.geyser = GeyserImpl.load(this.platform.platformType(), this);
// Create command manager here, since the permission handler on neo needs it
this.geyserCommandManager = new GeyserCommandManager(geyser);
this.geyserCommandManager.init();
}
public void onGeyserEnable() {
if (GeyserImpl.getInstance().isReloading()) {
if (!loadConfig()) {
return;
}
this.geyserLogger.setDebug(geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
}
GeyserImpl.start();
if (geyserConfig.isLegacyPingPassthrough()) {
this.geyserPingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
} else {
this.geyserPingPassthrough = new ModPingPassthrough(server, geyserLogger);
}
// No need to re-register commands, or try to re-inject
if (GeyserImpl.getInstance().isReloading()) {
return;
}
this.geyserWorldManager = new GeyserModWorldManager(server);
// We want to do this late in the server startup process to allow other mods
// To do their job injecting, then connect into *that*
this.geyserInjector = new GeyserModInjector(server, this.platform);
this.geyserInjector.initializeLocalChannel(this);
// Start command building
// Set just "geyser" as the help command
GeyserModCommandExecutor helpExecutor = new GeyserModCommandExecutor(geyser,
(GeyserCommand) geyser.commandManager().getCommands().get("help"));
LiteralArgumentBuilder<CommandSourceStack> builder = Commands.literal("geyser").executes(helpExecutor);
// Register all subcommands as valid
for (Map.Entry<String, Command> command : geyser.commandManager().getCommands().entrySet()) {
GeyserModCommandExecutor executor = new GeyserModCommandExecutor(geyser, (GeyserCommand) command.getValue());
builder.then(Commands.literal(command.getKey())
.executes(executor)
// Could also test for Bedrock but depending on when this is called it may backfire
.requires(executor::testPermission)
// Allows parsing of arguments; e.g. for /geyser dump logs or the connectiontest command
.then(Commands.argument("args", StringArgumentType.greedyString())
.executes(context -> executor.runWithArgs(context, StringArgumentType.getString(context, "args")))
.requires(executor::testPermission)));
}
server.getCommands().getDispatcher().register(builder);
// Register extension commands
for (Map.Entry<Extension, Map<String, Command>> extensionMapEntry : geyser.commandManager().extensionCommands().entrySet()) {
Map<String, Command> extensionCommands = extensionMapEntry.getValue();
if (extensionCommands.isEmpty()) {
continue;
}
// Register help command for just "/<extensionId>"
GeyserModCommandExecutor extensionHelpExecutor = new GeyserModCommandExecutor(geyser,
(GeyserCommand) extensionCommands.get("help"));
LiteralArgumentBuilder<CommandSourceStack> extCmdBuilder = Commands.literal(extensionMapEntry.getKey().description().id()).executes(extensionHelpExecutor);
for (Map.Entry<String, Command> command : extensionCommands.entrySet()) {
GeyserModCommandExecutor executor = new GeyserModCommandExecutor(geyser, (GeyserCommand) command.getValue());
extCmdBuilder.then(Commands.literal(command.getKey())
.executes(executor)
.requires(executor::testPermission)
.then(Commands.argument("args", StringArgumentType.greedyString())
.executes(context -> executor.runWithArgs(context, StringArgumentType.getString(context, "args")))
.requires(executor::testPermission)));
}
server.getCommands().getDispatcher().register(extCmdBuilder);
}
}
@Override
public void onGeyserDisable() {
if (geyser != null) {
geyser.disable();
}
}
@Override
public void onGeyserShutdown() {
if (geyser != null) {
geyser.shutdown();
geyser = null;
}
if (geyserInjector != null) {
geyserInjector.shutdown();
this.server = null;
}
}
@Override
public GeyserModConfiguration getGeyserConfig() {
return geyserConfig;
}
@Override
public GeyserLogger getGeyserLogger() {
return geyserLogger;
}
@Override
public GeyserCommandManager getGeyserCommandManager() {
return geyserCommandManager;
}
@Override
public IGeyserPingPassthrough getGeyserPingPassthrough() {
return geyserPingPassthrough;
}
@Override
public WorldManager getWorldManager() {
return geyserWorldManager;
}
@Override
public Path getConfigFolder() {
return dataFolder;
}
@Override
public BootstrapDumpInfo getDumpInfo() {
return this.platform.dumpInfo(this.server);
}
@Override
public String getMinecraftServerVersion() {
return this.server.getServerVersion();
}
@NonNull
@Override
public String getServerBindAddress() {
String ip = this.server.getLocalIp();
return ip != null ? ip : ""; // See issue #3812
}
@Override
public int getServerPort() {
return ((GeyserServerPortGetter) server).geyser$getServerPort();
}
@Override
public boolean testFloodgatePluginPresent() {
return this.platform.testFloodgatePluginPresent(this);
}
@Nullable
@Override
public InputStream getResourceOrNull(String resource) {
return this.platform.resolveResource(resource);
}
public abstract boolean hasPermission(@NonNull Player source, @NonNull String permissionNode);
public abstract boolean hasPermission(@NonNull CommandSourceStack source, @NonNull String permissionNode, int permissionLevel);
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
private boolean loadConfig() {
try {
if (!dataFolder.toFile().exists()) {
//noinspection ResultOfMethodCallIgnored
dataFolder.toFile().mkdir();
}
File configFile = FileUtils.fileOrCopiedFromResource(dataFolder.resolve("config.yml").toFile(), "config.yml",
(x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this);
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserModConfiguration.class);
return true;
} catch (IOException ex) {
LogManager.getLogger("geyser").error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
ex.printStackTrace();
return false;
}
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright (c) 2019-2021 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.platform.mod;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPromise;
import net.minecraft.network.protocol.login.ClientboundGameProfilePacket;
import net.minecraft.network.protocol.login.ClientboundLoginCompressionPacket;
/**
* Disables the compression packet (and the compression handlers from being added to the pipeline) for Geyser clients
* that won't be receiving the data over the network.
* <p>
* As of 1.8 - 1.17.1, compression is enabled in the Netty pipeline by adding a listener after a packet is written.
* If we simply "cancel" or don't forward the packet, then the listener is never called.
*/
public class GeyserModCompressionDisabler extends ChannelOutboundHandlerAdapter {
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
Class<?> msgClass = msg.getClass();
// Don't let any compression packet get through
if (!ClientboundLoginCompressionPacket.class.isAssignableFrom(msgClass)) {
if (ClientboundGameProfilePacket.class.isAssignableFrom(msgClass)) {
// We're past the point that a compression packet can be sent, so we can safely yeet ourselves away
ctx.channel().pipeline().remove(this);
}
super.write(ctx, msg, promise);
}
}
}

View File

@ -23,23 +23,20 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.fabric;
package org.geysermc.geyser.platform.mod;
import com.fasterxml.jackson.annotation.JsonIgnore;
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.ModContainer;
import org.geysermc.geyser.FloodgateKeyLoader;
import org.geysermc.geyser.configuration.GeyserJacksonConfiguration;
import java.nio.file.Path;
public class GeyserFabricConfiguration extends GeyserJacksonConfiguration {
public class GeyserModConfiguration extends GeyserJacksonConfiguration {
@JsonIgnore
private Path floodgateKeyPath;
public void loadFloodgate(GeyserFabricMod geyser, ModContainer floodgate) {
public void loadFloodgate(GeyserModBootstrap geyser, Path floodgateDataFolder) {
Path geyserDataFolder = geyser.getConfigFolder();
Path floodgateDataFolder = floodgate != null ? FabricLoader.getInstance().getConfigDir().resolve("floodgate") : null;
floodgateKeyPath = FloodgateKeyLoader.getKeyPath(this, floodgateDataFolder, geyserDataFolder, geyser.getGeyserLogger());
}

View File

@ -0,0 +1,156 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.mod;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.DefaultEventLoopGroup;
import io.netty.channel.local.LocalAddress;
import io.netty.util.concurrent.DefaultThreadFactory;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerConnectionListener;
import org.checkerframework.checker.nullness.qual.NonNull;
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.platform.mod.platform.GeyserModPlatform;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
public class GeyserModInjector extends GeyserInjector {
private final MinecraftServer server;
private final GeyserModPlatform platform;
private DefaultEventLoopGroup eventLoopGroup;
/**
* Used to uninject ourselves on shutdown.
*/
private List<ChannelFuture> allServerChannels;
public GeyserModInjector(MinecraftServer server, GeyserModPlatform platform) {
this.server = server;
this.platform = platform;
}
@Override
protected void initializeLocalChannel0(GeyserBootstrap bootstrap) throws Exception {
ServerConnectionListener connection = this.server.getConnection();
// Find the channel that Minecraft uses to listen to connections
ChannelFuture listeningChannel = null;
this.allServerChannels = ((GeyserChannelGetter) connection).geyser$getChannels();
for (ChannelFuture o : allServerChannels) {
listeningChannel = o;
break;
}
if (listeningChannel == null) {
throw new RuntimeException("Unable to find listening channel!");
}
// Making this a function prevents childHandler from being treated as a non-final variable
ChannelInitializer<Channel> childHandler = getChildHandler(bootstrap, listeningChannel);
// This method is what initializes the connection in Java Edition, after Netty is all set.
Method initChannel = childHandler.getClass().getDeclaredMethod("initChannel", Channel.class);
initChannel.setAccessible(true);
// Separate variable so we can shut it down later
eventLoopGroup = new DefaultEventLoopGroup(0, new DefaultThreadFactory("Geyser " + this.platform.platformType().platformName() + " connection thread", Thread.MAX_PRIORITY));
ChannelFuture channelFuture = (new ServerBootstrap()
.channel(LocalServerChannelWrapper.class)
.childHandler(new ChannelInitializer<>() {
@Override
protected void initChannel(@NonNull Channel ch) throws Exception {
initChannel.invoke(childHandler, ch);
if (bootstrap.getGeyserConfig().isDisableCompression()) {
ch.pipeline().addAfter("encoder", "geyser-compression-disabler", new GeyserModCompressionDisabler());
}
}
})
// Set to MAX_PRIORITY as MultithreadEventLoopGroup#newDefaultThreadFactory which DefaultEventLoopGroup implements does by default
.group(eventLoopGroup)
.localAddress(LocalAddress.ANY))
.bind()
.syncUninterruptibly();
// We don't need to add to the list, but plugins like ProtocolSupport and ProtocolLib that add to the main pipeline
// will work when we add to the list.
allServerChannels.add(channelFuture);
this.localChannel = channelFuture;
this.serverSocketAddress = channelFuture.channel().localAddress();
}
@SuppressWarnings("unchecked")
private ChannelInitializer<Channel> getChildHandler(GeyserBootstrap bootstrap, ChannelFuture listeningChannel) {
List<String> names = listeningChannel.channel().pipeline().names();
ChannelInitializer<Channel> childHandler = null;
for (String name : names) {
ChannelHandler handler = listeningChannel.channel().pipeline().get(name);
try {
Field childHandlerField = handler.getClass().getDeclaredField("childHandler");
childHandlerField.setAccessible(true);
childHandler = (ChannelInitializer<Channel>) childHandlerField.get(handler);
break;
} catch (Exception e) {
if (bootstrap.getGeyserConfig().isDebugMode()) {
bootstrap.getGeyserLogger().debug("The handler " + name + " isn't a ChannelInitializer. THIS ERROR IS SAFE TO IGNORE!");
e.printStackTrace();
}
}
}
if (childHandler == null) {
throw new RuntimeException();
}
return childHandler;
}
@Override
public void shutdown() {
if (this.allServerChannels != null) {
this.allServerChannels.remove(this.localChannel);
this.allServerChannels = null;
}
if (eventLoopGroup != null) {
try {
eventLoopGroup.shutdownGracefully().sync();
eventLoopGroup = null;
} catch (Exception e) {
GeyserImpl.getInstance().getLogger().error("Unable to shut down injector! " + e.getMessage());
e.printStackTrace();
}
}
super.shutdown();
}
}

View File

@ -23,7 +23,7 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.fabric;
package org.geysermc.geyser.platform.mod;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
@ -32,12 +32,12 @@ import org.apache.logging.log4j.Logger;
import org.geysermc.geyser.GeyserLogger;
import org.geysermc.geyser.text.ChatColor;
public class GeyserFabricLogger implements GeyserLogger {
private final Logger logger = LogManager.getLogger("geyser-fabric");
public class GeyserModLogger implements GeyserLogger {
private final Logger logger = LogManager.getLogger("geyser");
private boolean debug;
public GeyserFabricLogger(boolean isDebug) {
public GeyserModLogger(boolean isDebug) {
debug = isDebug;
}
@ -73,7 +73,7 @@ public class GeyserFabricLogger implements GeyserLogger {
@Override
public void sendMessage(Component message) {
// As of Java Edition 1.19.2, Fabric's console doesn't natively support legacy format
// As of Java Edition 1.19.2, Minecraft's console doesn't natively support legacy format
String flattened = LegacyComponentSerializer.legacySection().serialize(message);
// Add the reset at the end, or else format will persist... forever.
// https://cdn.discordapp.com/attachments/573909525132738590/1033904509170225242/unknown.png

View File

@ -23,21 +23,22 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.fabric;
package org.geysermc.geyser.platform.mod;
import me.lucko.fabric.api.permissions.v0.Permissions;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.world.entity.player.Player;
import org.geysermc.geyser.Constants;
import org.geysermc.geyser.platform.fabric.command.FabricCommandSender;
import org.geysermc.geyser.platform.mod.command.ModCommandSender;
import org.geysermc.geyser.util.VersionCheckUtils;
public final class GeyserFabricUpdateListener {
public static void onPlayReady(ServerGamePacketListenerImpl handler) {
if (Permissions.check(handler.player, Constants.UPDATE_PERMISSION, 2)) {
VersionCheckUtils.checkForGeyserUpdate(() -> new FabricCommandSender(handler.player.createCommandSourceStack()));
public final class GeyserModUpdateListener {
public static void onPlayReady(Player player) {
CommandSourceStack stack = player.createCommandSourceStack();
if (GeyserModBootstrap.getInstance().hasPermission(stack, Constants.UPDATE_PERMISSION, 2)) {
VersionCheckUtils.checkForGeyserUpdate(() -> new ModCommandSender(stack));
}
}
private GeyserFabricUpdateListener() {
private GeyserModUpdateListener() {
}
}

View File

@ -23,7 +23,7 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.fabric;
package org.geysermc.geyser.platform.mod;
import net.minecraft.server.MinecraftServer;

Some files were not shown because too many files have changed in this diff Show More