mirror of
https://github.com/GeyserMC/Geyser.git
synced 2025-12-23 16:59:33 +00:00
Merge remote-tracking branch 'upstream/master' into feature/new-custom-entity-api
# Conflicts: # core/src/main/java/org/geysermc/geyser/GeyserImpl.java # core/src/main/java/org/geysermc/geyser/entity/EntityDefinition.java # core/src/main/java/org/geysermc/geyser/registry/Registries.java # core/src/main/java/org/geysermc/geyser/registry/loader/ProviderRegistryLoader.java # core/src/main/java/org/geysermc/geyser/session/GeyserSession.java # core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaAddEntityTranslator.java # core/src/main/java/org/geysermc/geyser/util/EntityUtils.java
This commit is contained in:
14
.editorconfig
Normal file
14
.editorconfig
Normal file
@@ -0,0 +1,14 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
tab_width = 4
|
||||
max_line_length = off
|
||||
|
||||
[*.java]
|
||||
ij_java_class_count_to_use_import_on_demand = 9999
|
||||
ij_java_doc_align_exception_comments = false
|
||||
ij_java_doc_align_param_comments = false
|
||||
3
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
3
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,11 +1,12 @@
|
||||
name: Bug report
|
||||
description: Create a report to help us improve
|
||||
type: Bug
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this bug report for Geyser! Fill out the following form to your best ability to help us fix the problem.
|
||||
Only use this if you're absolutely sure that you found a bug and can reproduce it. For anything else, use: [our Discord server](https://discord.gg/geysermc), [the FAQ](https://github.com/GeyserMC/Geyser/wiki/FAQ) or the [Common Issues](https://github.com/GeyserMC/Geyser/wiki/Common-Issues).
|
||||
Only use this if you're absolutely sure that you found a bug and can reproduce it. For anything else, use: [our Discord server](https://discord.gg/geysermc), [the FAQ](https://geysermc.org/wiki/geyser/faq) or the [Common Issues](https://geysermc.org/wiki/geyser/common-issues).
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the bug
|
||||
|
||||
3
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
3
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -1,12 +1,13 @@
|
||||
name: Feature request
|
||||
description: Suggest an idea for this project
|
||||
labels: "Feature Request"
|
||||
type: Feature
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this feature request for Geyser! Please fill out the following form to your best ability to help us understand your feature request and significantly improve the chance of getting added.
|
||||
For anything else than a feature request, use: [our Discord server](https://discord.gg/geysermc), [the FAQ](https://github.com/GeyserMC/Geyser/wiki/FAQ) or [the Common Issues](https://github.com/GeyserMC/Geyser/wiki/Common-Issues).
|
||||
For anything else than a feature request, use: [our Discord server](https://discord.gg/geysermc), [the FAQ](https://geysermc.org/wiki/geyser/faq) or the [Common Issues](https://geysermc.org/wiki/geyser/common-issues).
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: What feature do you want to see added?
|
||||
|
||||
47
.github/workflows/build-remote.yml
vendored
Normal file
47
.github/workflows/build-remote.yml
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
name: Build Remote
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
repository:
|
||||
required: true
|
||||
description: 'The repo of the remote'
|
||||
type: string
|
||||
ref:
|
||||
required: true
|
||||
description: 'The ref of the remote'
|
||||
type: string
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set Build Number
|
||||
run: |
|
||||
echo "BUILD_NUMBER=${GITHUB_RUN_NUMBER}" >> $GITHUB_ENV
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: GeyserMC/actions/setup-gradle-composite@master
|
||||
with:
|
||||
checkout_repository: ${{ inputs.repository }}
|
||||
checkout_ref: ${{ inputs.ref }}
|
||||
setup-java_java-version: 21
|
||||
setup-gradle_cache-read-only: true
|
||||
|
||||
- name: Build Geyser
|
||||
run: ./gradlew build
|
||||
|
||||
- name: Archive Artifacts
|
||||
uses: GeyserMC/actions/upload-multi-artifact@master
|
||||
if: success()
|
||||
with:
|
||||
artifacts: |
|
||||
bootstrap/mod/fabric/build/libs/Geyser-Fabric.jar
|
||||
bootstrap/mod/neoforge/build/libs/Geyser-NeoForge.jar
|
||||
bootstrap/standalone/build/libs/Geyser-Standalone.jar
|
||||
bootstrap/spigot/build/libs/Geyser-Spigot.jar
|
||||
bootstrap/bungeecord/build/libs/Geyser-BungeeCord.jar
|
||||
bootstrap/velocity/build/libs/Geyser-Velocity.jar
|
||||
bootstrap/viaproxy/build/libs/Geyser-ViaProxy.jar
|
||||
185
.github/workflows/build.yml
vendored
185
.github/workflows/build.yml
vendored
@@ -7,149 +7,114 @@ on:
|
||||
- 'gh-readonly-queue/**'
|
||||
paths-ignore:
|
||||
- '.github/ISSUE_TEMPLATE/*.yml'
|
||||
- '.github/actions/pullrequest.yml'
|
||||
- '.github/workflows/build-remote.yml'
|
||||
- '.github/workflows/preview.yml'
|
||||
- '.github/workflows/pull-request.yml'
|
||||
- '.idea/copyright/*.xml'
|
||||
- '.gitignore'
|
||||
- 'CONTRIBUTING.md'
|
||||
- 'LICENSE'
|
||||
- 'Jenkinsfile '
|
||||
- 'README.md'
|
||||
- 'licenseheader.txt'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository and submodules
|
||||
# See https://github.com/actions/checkout/commits
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: Get Release Info
|
||||
id: release-info
|
||||
uses: GeyserMC/actions/previous-release@master
|
||||
with:
|
||||
submodules: recursive
|
||||
data: ${{ vars.RELEASEACTION_PREVRELEASE }}
|
||||
|
||||
- name: Validate Gradle Wrapper
|
||||
# See https://github.com/gradle/wrapper-validation-action/commits
|
||||
uses: gradle/wrapper-validation-action@699bb18358f12c5b78b37bb0111d3a0e2276e0e2 # v2.1.1
|
||||
- name: Setup Gradle
|
||||
uses: GeyserMC/actions/setup-gradle-composite@master
|
||||
with:
|
||||
setup-java_java-version: 21
|
||||
|
||||
# See https://github.com/actions/setup-java/commits
|
||||
- uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: temurin
|
||||
- name: Build Geyser
|
||||
run: ./gradlew build
|
||||
env:
|
||||
BUILD_NUMBER: ${{ steps.release-info.outputs.curentRelease }}
|
||||
|
||||
- name: Build
|
||||
# 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)
|
||||
# See https://github.com/actions/upload-artifact/commits
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
- name: Archive Artifacts
|
||||
uses: GeyserMC/actions/upload-multi-artifact@master
|
||||
if: success()
|
||||
with:
|
||||
name: Geyser Fabric
|
||||
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@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@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@5d5d22a31266ced268874388b861e4b58bb5c2f3
|
||||
if: success()
|
||||
with:
|
||||
name: Geyser BungeeCord
|
||||
path: bootstrap/bungeecord/build/libs/Geyser-BungeeCord.jar
|
||||
if-no-files-found: error
|
||||
- name: Archive artifacts (Geyser Velocity)
|
||||
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
|
||||
artifacts: |
|
||||
bootstrap/mod/fabric/build/libs/Geyser-Fabric.jar
|
||||
bootstrap/mod/neoforge/build/libs/Geyser-NeoForge.jar
|
||||
bootstrap/standalone/build/libs/Geyser-Standalone.jar
|
||||
bootstrap/spigot/build/libs/Geyser-Spigot.jar
|
||||
bootstrap/bungeecord/build/libs/Geyser-BungeeCord.jar
|
||||
bootstrap/velocity/build/libs/Geyser-Velocity.jar
|
||||
bootstrap/viaproxy/build/libs/Geyser-ViaProxy.jar
|
||||
|
||||
- name: Publish to Maven Repository
|
||||
if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
|
||||
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
|
||||
run: ./gradlew publish
|
||||
env:
|
||||
BUILD_NUMBER: ${{ steps.release-info.outputs.curentRelease }}
|
||||
ORG_GRADLE_PROJECT_geysermcUsername: ${{ vars.DEPLOY_USER }}
|
||||
ORG_GRADLE_PROJECT_geysermcPassword: ${{ secrets.DEPLOY_PASS }}
|
||||
|
||||
- name: Get Version
|
||||
if: ${{ (success() || failure()) && github.repository == 'GeyserMC/Geyser' }}
|
||||
id: get-version
|
||||
run: |
|
||||
version=$(cat gradle.properties | grep -o "version=[0-9\\.]*" | cut -d"=" -f2)
|
||||
echo "VERSION=${version}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Get Release Metadata
|
||||
if: ${{ (success() || failure()) && github.repository == 'GeyserMC/Geyser' }}
|
||||
uses: GeyserMC/actions/release@master
|
||||
id: metadata
|
||||
with:
|
||||
arguments: publish
|
||||
appID: ${{ secrets.RELEASE_APP_ID }}
|
||||
appPrivateKey: ${{ secrets.RELEASE_APP_PK }}
|
||||
files: |
|
||||
bungeecord:bootstrap/bungeecord/build/libs/Geyser-BungeeCord.jar
|
||||
fabric:bootstrap/mod/fabric/build/libs/Geyser-Fabric.jar
|
||||
neoforge:bootstrap/mod/neoforge/build/libs/Geyser-NeoForge.jar
|
||||
spigot:bootstrap/spigot/build/libs/Geyser-Spigot.jar
|
||||
standalone:bootstrap/standalone/build/libs/Geyser-Standalone.jar
|
||||
velocity:bootstrap/velocity/build/libs/Geyser-Velocity.jar
|
||||
viaproxy:bootstrap/viaproxy/build/libs/Geyser-ViaProxy.jar
|
||||
releaseEnabled: false
|
||||
saveMetadata: true
|
||||
releaseProject: 'geyser'
|
||||
releaseVersion: ${{ steps.get-version.outputs.VERSION }}
|
||||
|
||||
- name: Publish to Downloads API
|
||||
if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
|
||||
shell: bash
|
||||
env:
|
||||
DOWNLOADS_USERNAME: ${{ vars.DOWNLOADS_USERNAME }}
|
||||
DOWNLOADS_PRIVATE_KEY: ${{ secrets.DOWNLOADS_PRIVATE_KEY }}
|
||||
DOWNLOADS_SERVER_IP: ${{ secrets.DOWNLOADS_SERVER_IP }}
|
||||
run: |
|
||||
# Save the private key to a file
|
||||
echo "$DOWNLOADS_PRIVATE_KEY" > id_ecdsa
|
||||
chmod 600 id_ecdsa
|
||||
# Set the project
|
||||
project=geyser
|
||||
# Get the version from gradle.properties
|
||||
version=$(cat gradle.properties | grep -o "version=[0-9\\.]*" | cut -d"=" -f2)
|
||||
# Create the build folder
|
||||
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/
|
||||
uses: GeyserMC/actions/upload-release@master
|
||||
with:
|
||||
username: ${{ vars.DOWNLOADS_USERNAME }}
|
||||
privateKey: ${{ secrets.DOWNLOADS_PRIVATE_KEY }}
|
||||
host: ${{ secrets.DOWNLOADS_SERVER_IP }}
|
||||
files: |
|
||||
bootstrap/bungeecord/build/libs/Geyser-BungeeCord.jar
|
||||
bootstrap/mod/fabric/build/libs/Geyser-Fabric.jar
|
||||
bootstrap/mod/neoforge/build/libs/Geyser-NeoForge.jar
|
||||
bootstrap/spigot/build/libs/Geyser-Spigot.jar
|
||||
bootstrap/standalone/build/libs/Geyser-Standalone.jar
|
||||
bootstrap/velocity/build/libs/Geyser-Velocity.jar
|
||||
bootstrap/viaproxy/build/libs/Geyser-ViaProxy.jar
|
||||
changelog: ${{ steps.metadata.outputs.body }}
|
||||
|
||||
- name: Publish to Modrinth (Fabric)
|
||||
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
|
||||
- name: Publish to Modrinth
|
||||
if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
|
||||
env:
|
||||
CHANGELOG: ${{ steps.metadata.outputs.body }}
|
||||
BUILD_NUMBER: ${{ steps.release-info.outputs.curentRelease }}
|
||||
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
|
||||
run: ./gradlew modrinth
|
||||
|
||||
- name: Notify Discord
|
||||
if: ${{ (success() || failure()) && github.repository == 'GeyserMC/Geyser' }}
|
||||
# See https://github.com/Tim203/actions-git-discord-webhook/commits
|
||||
uses: Tim203/actions-git-discord-webhook@70f38ded3aca51635ec978ab4e1a58cd4cd0c2ff
|
||||
uses: GeyserMC/actions/notify-discord@master
|
||||
with:
|
||||
webhook_url: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
discordWebhook: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
status: ${{ job.status }}
|
||||
body: ${{ steps.metadata.outputs.body }}
|
||||
includeDownloads: ${{ github.ref_name == 'master' }}
|
||||
|
||||
33
.github/workflows/dispatch-preview.yml
vendored
Normal file
33
.github/workflows/dispatch-preview.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
name: Dispatch Preview
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
runId:
|
||||
required: true
|
||||
description: 'ID of the action to pull artifacts from'
|
||||
build:
|
||||
required: true
|
||||
description: 'Build number for the release'
|
||||
version:
|
||||
required: true
|
||||
description: 'Version under which to upload to the Downloads API'
|
||||
|
||||
jobs:
|
||||
dispatch-preview:
|
||||
# Allow access to secrets if we are uploading a preview
|
||||
secrets: inherit
|
||||
uses: GeyserMC/actions/.github/workflows/upload-preview.yml@master
|
||||
with:
|
||||
build: ${{ inputs.build }}
|
||||
version: ${{ inputs.version }}
|
||||
files: |
|
||||
bungeecord:Geyser-BungeeCord.jar
|
||||
fabric:Geyser-Fabric.jar
|
||||
neoforge:Geyser-NeoForge.jar
|
||||
spigot:Geyser-Spigot.jar
|
||||
standalone:Geyser-Standalone.jar
|
||||
velocity:Geyser-Velocity.jar
|
||||
viaproxy:Geyser-ViaProxy.jar
|
||||
project: geyserpreview
|
||||
runId: ${{ inputs.runId }}
|
||||
34
.github/workflows/pull-request.yml
vendored
Normal file
34
.github/workflows/pull-request.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
name: Process Pull Request
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
# Forbid access to secrets nor GH Token perms while building the PR
|
||||
permissions: {}
|
||||
secrets: {}
|
||||
uses: GeyserMC/Geyser/.github/workflows/build-remote.yml@master
|
||||
with:
|
||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
preview:
|
||||
needs: [build]
|
||||
if: >-
|
||||
contains(github.event.pull_request.labels.*.name, 'PR: Needs Testing')
|
||||
# Allow access to secrets if we are uploading a preview
|
||||
secrets: inherit
|
||||
uses: GeyserMC/actions/.github/workflows/upload-preview.yml@master
|
||||
with:
|
||||
build: ${{ github.run_number }}
|
||||
version: pr.${{ github.event.pull_request.number }}
|
||||
files: |
|
||||
bungeecord:Geyser-BungeeCord.jar
|
||||
fabric:Geyser-Fabric.jar
|
||||
neoforge:Geyser-NeoForge.jar
|
||||
spigot:Geyser-Spigot.jar
|
||||
standalone:Geyser-Standalone.jar
|
||||
velocity:Geyser-Velocity.jar
|
||||
viaproxy:Geyser-ViaProxy.jar
|
||||
project: geyserpreview
|
||||
runId: ${{ github.run_id }}
|
||||
104
.github/workflows/pullrequest.yml
vendored
104
.github/workflows/pullrequest.yml
vendored
@@ -1,104 +0,0 @@
|
||||
name: Build Pull Request
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
merge_group:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up JDK 17
|
||||
# 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
|
||||
# See https://github.com/Kas-tle/find-forks-action/commits
|
||||
uses: Kas-tle/find-forks-action@1b5447d1e3c7a8ed79583dd817cc5399686eed3a
|
||||
id: find_forks
|
||||
with:
|
||||
owner: GeyserMC
|
||||
repo: api
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Use author's API repo if it exists
|
||||
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 }}
|
||||
run: |
|
||||
git clone "${API_FORK_URL}" --single-branch --branch "${API_FORK_BRANCH}" api
|
||||
cd api
|
||||
./gradlew publishToMavenLocal
|
||||
|
||||
- name: Checkout repository and submodules
|
||||
# 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
|
||||
# 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)
|
||||
# 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/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@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@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@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 Velocity)
|
||||
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
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -249,6 +249,8 @@ locales/
|
||||
/packs/
|
||||
/dump.json
|
||||
/saved-refresh-tokens.json
|
||||
/saved-auth-chains.json
|
||||
/custom_mappings/
|
||||
/languages/
|
||||
/custom-skulls.yml
|
||||
/permissions.yml
|
||||
|
||||
3
.idea/copyright/Geyser.xml
generated
3
.idea/copyright/Geyser.xml
generated
@@ -1,6 +1,7 @@
|
||||
<component name="CopyrightManager">
|
||||
<copyright>
|
||||
<option name="notice" value="Copyright (c) 2019-&#36;today.year 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" />
|
||||
<option name="allowReplaceRegexp" value="Copyright" />
|
||||
<option name="notice" value="Copyright (c) &#36;originalComment.match("Copyright \(c\) (\d+)", 1, "-")&#36;today.year 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" />
|
||||
<option name="myName" value="Geyser" />
|
||||
</copyright>
|
||||
</component>
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||
Copyright (c) 2019-2025 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
|
||||
|
||||
20
README.md
20
README.md
@@ -2,7 +2,7 @@
|
||||
|
||||
[](LICENSE)
|
||||
[](https://discord.gg/geysermc)
|
||||
[](https://translate.geysermc.org/)
|
||||
[](https://translate.geysermc.org/)
|
||||
|
||||
Geyser is a bridge between Minecraft: Bedrock Edition and Minecraft: Java Edition, closing the gap from those wanting to play true cross-platform.
|
||||
|
||||
@@ -14,16 +14,15 @@ 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.20.40 - 1.20.72 and Minecraft Java 1.20.4
|
||||
## Supported Versions
|
||||
Geyser is currently supporting Minecraft Bedrock 1.21.90 - 1.21.113 and Minecraft Java 1.21.9 - 1.21.10. For more information, please see [here](https://geysermc.org/wiki/geyser/supported-versions/).
|
||||
|
||||
## Setting Up
|
||||
Take a look [here](https://wiki.geysermc.org/geyser/setup/) for how to set up Geyser.
|
||||
|
||||
[](https://www.youtube.com/watch?v=U7dZZ8w7Gi4)
|
||||
Take a look [here](https://geysermc.org/wiki/geyser/setup/) for how to set up Geyser.
|
||||
|
||||
## Links:
|
||||
- Website: https://geysermc.org
|
||||
- Docs: https://wiki.geysermc.org/geyser/
|
||||
- Docs: https://geysermc.org/wiki/geyser/
|
||||
- Download: https://geysermc.org/download
|
||||
- Discord: https://discord.gg/geysermc
|
||||
- Donate: https://opencollective.com/geysermc
|
||||
@@ -32,10 +31,9 @@ Take a look [here](https://wiki.geysermc.org/geyser/setup/) for how to set up Ge
|
||||
## What's Left to be Added/Fixed
|
||||
- Near-perfect movement (to the point where anticheat on large servers is unlikely to ban you)
|
||||
- Some Entity Flags
|
||||
- Structure block UI
|
||||
|
||||
## What can't be fixed
|
||||
There are a few things Geyser is unable to support due to various differences between Minecraft Bedrock and Java. For a list of these limitations, see the [Current Limitations](https://wiki.geysermc.org/geyser/current-limitations/) page.
|
||||
There are a few things Geyser is unable to support due to various differences between Minecraft Bedrock and Java. For a list of these limitations, see the [Current Limitations](https://geysermc.org/wiki/geyser/current-limitations/) page.
|
||||
|
||||
## Compiling
|
||||
1. Clone the repo to your computer
|
||||
@@ -43,12 +41,12 @@ There are a few things Geyser is unable to support due to various differences be
|
||||
3. Run `gradlew build` and locate to `bootstrap/build` folder.
|
||||
|
||||
## Contributing
|
||||
Any contributions are appreciated. Please feel free to reach out to us on [Discord](http://discord.geysermc.org/) if
|
||||
Any contributions are appreciated. Please feel free to reach out to us on [Discord](https://discord.gg/geysermc) if
|
||||
you're interested in helping out with Geyser.
|
||||
|
||||
## Libraries Used:
|
||||
- [Adventure Text Library](https://github.com/KyoriPowered/adventure)
|
||||
- [NukkitX Bedrock Protocol Library](https://github.com/NukkitX/Protocol)
|
||||
- [Steveice10's Java Protocol Library](https://github.com/Steveice10/MCProtocolLib)
|
||||
- [CloudburstMC Bedrock Protocol Library](https://github.com/CloudburstMC/Protocol)
|
||||
- [GeyserMC's Java Protocol Library](https://github.com/GeyserMC/MCProtocolLib)
|
||||
- [TerminalConsoleAppender](https://github.com/Minecrell/TerminalConsoleAppender)
|
||||
- [Simple Logging Facade for Java (slf4j)](https://github.com/qos-ch/slf4j)
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
plugins {
|
||||
id("geyser.base-conventions")
|
||||
}
|
||||
|
||||
@@ -1,8 +1,24 @@
|
||||
plugins {
|
||||
// Allow blossom to mark sources root of templates
|
||||
idea
|
||||
id("geyser.publish-conventions")
|
||||
alias(libs.plugins.blossom)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(libs.base.api)
|
||||
api(libs.math)
|
||||
}
|
||||
|
||||
version = property("version")!!
|
||||
val apiVersion = (version as String).removeSuffix("-SNAPSHOT")
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
blossom {
|
||||
javaSources {
|
||||
property("version", apiVersion)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 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;
|
||||
|
||||
import org.geysermc.api.util.ApiVersion;
|
||||
|
||||
/**
|
||||
* Not a public API. For internal use only. May change without notice.
|
||||
* This class is processed before compilation to insert build properties.
|
||||
*/
|
||||
class BuildData {
|
||||
static final String VERSION = "{{ version }}";
|
||||
static final ApiVersion API_VERSION;
|
||||
|
||||
static {
|
||||
String[] parts = VERSION.split("\\.");
|
||||
if (parts.length != 3) {
|
||||
throw new RuntimeException("Invalid api version: " + VERSION);
|
||||
}
|
||||
|
||||
try {
|
||||
int human = Integer.parseInt(parts[0]);
|
||||
int major = Integer.parseInt(parts[1]);
|
||||
int minor = Integer.parseInt(parts[2]);
|
||||
API_VERSION = new ApiVersion(human, major, minor);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Invalid api version: " + VERSION, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,7 @@ 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.api.util.ApiVersion;
|
||||
import org.geysermc.geyser.api.command.CommandSource;
|
||||
import org.geysermc.geyser.api.connection.GeyserConnection;
|
||||
import org.geysermc.geyser.api.event.EventBus;
|
||||
@@ -169,4 +170,14 @@ public interface GeyserApi extends GeyserApiBase {
|
||||
static GeyserApi api() {
|
||||
return Geyser.api(GeyserApi.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link ApiVersion} representing the current Geyser api version.
|
||||
* See the <a href="https://github.com/geysermc/api/blob/master/geyser-versioning.md">Geyser version outline</a>)
|
||||
*
|
||||
* @return the current geyser api version
|
||||
*/
|
||||
default ApiVersion geyserApiVersion() {
|
||||
return BuildData.API_VERSION;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,4 +145,36 @@ public interface CameraData {
|
||||
* @return whether the camera is currently locked
|
||||
*/
|
||||
boolean isCameraLocked();
|
||||
|
||||
/**
|
||||
* Hides a {@link GuiElement} on the client's side.
|
||||
*
|
||||
* @param element the {@link GuiElement} to hide
|
||||
*/
|
||||
void hideElement(@NonNull GuiElement... element);
|
||||
|
||||
/**
|
||||
* Resets a {@link GuiElement} on the client's side.
|
||||
* This makes the client decide on its own - e.g. based on client settings -
|
||||
* whether to show or hide the gui element.
|
||||
* <p>
|
||||
* If no elements are specified, this will reset all currently hidden elements
|
||||
*
|
||||
* @param element the {@link GuiElement} to reset
|
||||
*/
|
||||
void resetElement(@NonNull GuiElement @Nullable... element);
|
||||
|
||||
/**
|
||||
* Determines whether a {@link GuiElement} is currently hidden.
|
||||
*
|
||||
* @param element the {@link GuiElement} to check
|
||||
*/
|
||||
boolean isHudElementHidden(@NonNull GuiElement element);
|
||||
|
||||
/**
|
||||
* Returns the currently hidden {@link GuiElement}s.
|
||||
*
|
||||
* @return an unmodifiable view of all currently hidden {@link GuiElement}s
|
||||
*/
|
||||
@NonNull Set<GuiElement> hiddenElements();
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 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.bedrock.camera;
|
||||
|
||||
/**
|
||||
* Represent GUI elements on the players HUD display.
|
||||
* These can be hidden using {@link CameraData#hideElement(GuiElement...)},
|
||||
* and one can reset their visibility using {@link CameraData#resetElement(GuiElement...)}.
|
||||
*/
|
||||
public class GuiElement {
|
||||
public static final GuiElement PAPER_DOLL = new GuiElement(0);
|
||||
public static final GuiElement ARMOR = new GuiElement(1);
|
||||
public static final GuiElement TOOL_TIPS = new GuiElement(2);
|
||||
public static final GuiElement TOUCH_CONTROLS = new GuiElement(3);
|
||||
public static final GuiElement CROSSHAIR = new GuiElement(4);
|
||||
public static final GuiElement HOTBAR = new GuiElement(5);
|
||||
public static final GuiElement HEALTH = new GuiElement(6);
|
||||
public static final GuiElement PROGRESS_BAR = new GuiElement(7);
|
||||
public static final GuiElement FOOD_BAR = new GuiElement(8);
|
||||
public static final GuiElement AIR_BUBBLES_BAR = new GuiElement(9);
|
||||
public static final GuiElement VEHICLE_HEALTH = new GuiElement(10);
|
||||
public static final GuiElement EFFECTS_BAR = new GuiElement(11);
|
||||
public static final GuiElement ITEM_TEXT_POPUP = new GuiElement(12);
|
||||
|
||||
private GuiElement(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
private final int id;
|
||||
|
||||
/**
|
||||
* Internal use only; don't depend on these values being consistent.
|
||||
*/
|
||||
public int id() {
|
||||
return this.id;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
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) {
|
||||
}
|
||||
@@ -59,7 +59,9 @@ public interface JavaBlockState {
|
||||
* Gets the pick item of the block state
|
||||
*
|
||||
* @return the pick item of the block state
|
||||
* @deprecated the pick item is sent by the Java server
|
||||
*/
|
||||
@Deprecated
|
||||
@Nullable String pickItem();
|
||||
|
||||
/**
|
||||
@@ -70,10 +72,13 @@ public interface JavaBlockState {
|
||||
@Nullable String pistonBehavior();
|
||||
|
||||
/**
|
||||
* Gets whether the block state has block entity
|
||||
* Gets whether the block state has a block entity
|
||||
*
|
||||
* @return whether the block state has block entity
|
||||
* @deprecated Does not have an effect. If you were using this to
|
||||
* set piston behavior, use {@link #pistonBehavior()} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
boolean hasBlockEntity();
|
||||
|
||||
/**
|
||||
@@ -100,10 +105,16 @@ public interface JavaBlockState {
|
||||
|
||||
Builder canBreakWithHand(boolean canBreakWithHand);
|
||||
|
||||
@Deprecated
|
||||
Builder pickItem(@Nullable String pickItem);
|
||||
|
||||
Builder pistonBehavior(@Nullable String pistonBehavior);
|
||||
|
||||
/**
|
||||
* @deprecated Does not have an effect. If you were using this to
|
||||
* * set piston behavior, use {@link #pistonBehavior(String)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
Builder hasBlockEntity(boolean hasBlockEntity);
|
||||
|
||||
JavaBlockState build();
|
||||
|
||||
@@ -28,7 +28,9 @@ package org.geysermc.geyser.api.command;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.api.GeyserApi;
|
||||
import org.geysermc.geyser.api.connection.GeyserConnection;
|
||||
import org.geysermc.geyser.api.event.lifecycle.GeyserRegisterPermissionsEvent;
|
||||
import org.geysermc.geyser.api.extension.Extension;
|
||||
import org.geysermc.geyser.api.util.TriState;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -58,15 +60,15 @@ public interface Command {
|
||||
* Gets the permission node associated with
|
||||
* this command.
|
||||
*
|
||||
* @return the permission node for this command
|
||||
* @return the permission node for this command if defined, otherwise an empty string
|
||||
*/
|
||||
@NonNull
|
||||
String permission();
|
||||
|
||||
/**
|
||||
* Gets the aliases for this command.
|
||||
* Gets the aliases for this command, as an unmodifiable list
|
||||
*
|
||||
* @return the aliases for this command
|
||||
* @return the aliases for this command as an unmodifiable list
|
||||
*/
|
||||
@NonNull
|
||||
List<String> aliases();
|
||||
@@ -75,35 +77,39 @@ public interface Command {
|
||||
* Gets if this command is designed to be used only by server operators.
|
||||
*
|
||||
* @return if this command is designated to be used only by server operators.
|
||||
* @deprecated this method is not guaranteed to provide meaningful or expected results.
|
||||
*/
|
||||
boolean isSuggestedOpOnly();
|
||||
|
||||
/**
|
||||
* Gets if this command is executable on console.
|
||||
*
|
||||
* @return if this command is executable on console
|
||||
*/
|
||||
boolean isExecutableOnConsole();
|
||||
|
||||
/**
|
||||
* Gets the subcommands associated with this
|
||||
* command. Mainly used within the Geyser Standalone
|
||||
* GUI to know what subcommands are supported.
|
||||
*
|
||||
* @return the subcommands associated with this command
|
||||
*/
|
||||
@NonNull
|
||||
default List<String> subCommands() {
|
||||
return Collections.emptyList();
|
||||
@Deprecated(forRemoval = true)
|
||||
default boolean isSuggestedOpOnly() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to send a deny message to Java players if this command can only be used by Bedrock players.
|
||||
*
|
||||
* @return true if this command can only be used by Bedrock players.
|
||||
* @return true if this command is executable on console
|
||||
* @deprecated use {@link #isPlayerOnly()} instead (inverted)
|
||||
*/
|
||||
default boolean isBedrockOnly() {
|
||||
return false;
|
||||
@Deprecated(forRemoval = true)
|
||||
default boolean isExecutableOnConsole() {
|
||||
return !isPlayerOnly();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this command can only be used by players
|
||||
*/
|
||||
boolean isPlayerOnly();
|
||||
|
||||
/**
|
||||
* @return true if this command can only be used by Bedrock players
|
||||
*/
|
||||
boolean isBedrockOnly();
|
||||
|
||||
/**
|
||||
* @deprecated this method will always return an empty immutable list
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
@NonNull
|
||||
default List<String> subCommands() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,7 +134,7 @@ public interface Command {
|
||||
* is an instance of this source.
|
||||
*
|
||||
* @param sourceType the source type
|
||||
* @return the builder
|
||||
* @return this builder
|
||||
*/
|
||||
Builder<T> source(@NonNull Class<? extends T> sourceType);
|
||||
|
||||
@@ -136,7 +142,7 @@ public interface Command {
|
||||
* Sets the command name.
|
||||
*
|
||||
* @param name the command name
|
||||
* @return the builder
|
||||
* @return this builder
|
||||
*/
|
||||
Builder<T> name(@NonNull String name);
|
||||
|
||||
@@ -144,23 +150,40 @@ public interface Command {
|
||||
* Sets the command description.
|
||||
*
|
||||
* @param description the command description
|
||||
* @return the builder
|
||||
* @return this builder
|
||||
*/
|
||||
Builder<T> description(@NonNull String description);
|
||||
|
||||
/**
|
||||
* Sets the permission node.
|
||||
* Sets the permission node required to run this command. <br>
|
||||
* It will not be registered with any permission registries, such as an underlying server,
|
||||
* or a permissions Extension (unlike {@link #permission(String, TriState)}).
|
||||
*
|
||||
* @param permission the permission node
|
||||
* @return the builder
|
||||
* @return this builder
|
||||
*/
|
||||
Builder<T> permission(@NonNull String permission);
|
||||
|
||||
/**
|
||||
* Sets the permission node and its default value. The usage of the default value is platform dependant
|
||||
* and may or may not be used. For example, it may be registered to an underlying server.
|
||||
* <p>
|
||||
* Extensions may instead listen for {@link GeyserRegisterPermissionsEvent} to register permissions,
|
||||
* especially if the same permission is required by multiple commands. Also see this event for TriState meanings.
|
||||
*
|
||||
* @param permission the permission node
|
||||
* @param defaultValue the node's default value
|
||||
* @return this builder
|
||||
* @deprecated this method is experimental and may be removed in the future
|
||||
*/
|
||||
@Deprecated
|
||||
Builder<T> permission(@NonNull String permission, @NonNull TriState defaultValue);
|
||||
|
||||
/**
|
||||
* Sets the aliases.
|
||||
*
|
||||
* @param aliases the aliases
|
||||
* @return the builder
|
||||
* @return this builder
|
||||
*/
|
||||
Builder<T> aliases(@NonNull List<String> aliases);
|
||||
|
||||
@@ -168,46 +191,62 @@ public interface Command {
|
||||
* Sets if this command is designed to be used only by server operators.
|
||||
*
|
||||
* @param suggestedOpOnly if this command is designed to be used only by server operators
|
||||
* @return the builder
|
||||
* @return this builder
|
||||
* @deprecated this method is not guaranteed to produce meaningful or expected results
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
Builder<T> suggestedOpOnly(boolean suggestedOpOnly);
|
||||
|
||||
/**
|
||||
* Sets if this command is executable on console.
|
||||
*
|
||||
* @param executableOnConsole if this command is executable on console
|
||||
* @return the builder
|
||||
* @return this builder
|
||||
* @deprecated use {@link #isPlayerOnly()} instead (inverted)
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
Builder<T> executableOnConsole(boolean executableOnConsole);
|
||||
|
||||
/**
|
||||
* Sets if this command can only be executed by players.
|
||||
*
|
||||
* @param playerOnly if this command is player only
|
||||
* @return this builder
|
||||
*/
|
||||
Builder<T> playerOnly(boolean playerOnly);
|
||||
|
||||
/**
|
||||
* Sets if this command can only be executed by bedrock players.
|
||||
*
|
||||
* @param bedrockOnly if this command is bedrock only
|
||||
* @return this builder
|
||||
*/
|
||||
Builder<T> bedrockOnly(boolean bedrockOnly);
|
||||
|
||||
/**
|
||||
* Sets the subcommands.
|
||||
*
|
||||
* @param subCommands the subcommands
|
||||
* @return the builder
|
||||
* @return this builder
|
||||
* @deprecated this method has no effect
|
||||
*/
|
||||
Builder<T> subCommands(@NonNull List<String> subCommands);
|
||||
|
||||
/**
|
||||
* Sets if this command is bedrock only.
|
||||
*
|
||||
* @param bedrockOnly if this command is bedrock only
|
||||
* @return the builder
|
||||
*/
|
||||
Builder<T> bedrockOnly(boolean bedrockOnly);
|
||||
@Deprecated(forRemoval = true)
|
||||
default Builder<T> subCommands(@NonNull List<String> subCommands) {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link CommandExecutor} for this command.
|
||||
*
|
||||
* @param executor the command executor
|
||||
* @return the builder
|
||||
* @return this builder
|
||||
*/
|
||||
Builder<T> executor(@NonNull CommandExecutor<T> executor);
|
||||
|
||||
/**
|
||||
* Builds the command.
|
||||
*
|
||||
* @return the command
|
||||
* @return a new command from this builder
|
||||
*/
|
||||
@NonNull
|
||||
Command build();
|
||||
|
||||
@@ -26,6 +26,10 @@
|
||||
package org.geysermc.geyser.api.command;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.api.connection.GeyserConnection;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Represents an instance capable of sending commands.
|
||||
@@ -64,6 +68,17 @@ public interface CommandSource {
|
||||
*/
|
||||
boolean isConsole();
|
||||
|
||||
/**
|
||||
* @return a Java UUID if this source represents a player, otherwise null
|
||||
*/
|
||||
@Nullable UUID playerUuid();
|
||||
|
||||
/**
|
||||
* @return a GeyserConnection if this source represents a Bedrock player that is connected
|
||||
* to this Geyser instance, otherwise null
|
||||
*/
|
||||
@Nullable GeyserConnection connection();
|
||||
|
||||
/**
|
||||
* Returns the locale of the command source.
|
||||
*
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
package org.geysermc.geyser.api.connection;
|
||||
|
||||
import org.checkerframework.checker.index.qual.NonNegative;
|
||||
import org.checkerframework.checker.index.qual.Positive;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.api.connection.Connection;
|
||||
@@ -35,8 +36,11 @@ 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 org.geysermc.geyser.api.skin.SkinData;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
@@ -60,6 +64,108 @@ public interface GeyserConnection extends Connection, CommandSource {
|
||||
*/
|
||||
@NonNull EntityData entities();
|
||||
|
||||
/**
|
||||
* Returns the current ping of the connection.
|
||||
*/
|
||||
int ping();
|
||||
|
||||
/**
|
||||
* @return {@code true} if the client currently has a form open.
|
||||
* @since 2.8.0
|
||||
*/
|
||||
boolean hasFormOpen();
|
||||
|
||||
/**
|
||||
* Closes the currently open form on the client.
|
||||
*/
|
||||
void closeForm();
|
||||
|
||||
/**
|
||||
* Gets the Bedrock protocol version of the player.
|
||||
*/
|
||||
int protocolVersion();
|
||||
|
||||
/**
|
||||
* Attempts to open the {@code minecraft:pause_screen_additions} dialog tag. This method opens this dialog the same way Java does, that is:
|
||||
*
|
||||
* <ul>
|
||||
* <li>If there are multiple dialogs in the additions tag, the {@code minecraft:custom_options} dialog is opened to select a dialog.</li>
|
||||
* <li>If there is one dialog in the additions tag, that dialog is opened.</li>
|
||||
* <li>If there are no dialogs in the tag, but there are server links sent to the client, the {@code minecraft:server_links} dialog is opened.</li>
|
||||
* <li>If all of the above fails, no dialog is opened.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Use {@link GeyserConnection#hasFormOpen()} to check if a dialog was opened.</p>
|
||||
* @since 2.8.0
|
||||
*/
|
||||
void openPauseScreenAdditions();
|
||||
|
||||
/**
|
||||
* Attempts to open the {@code minecraft:quick_actions} dialog tag. This method opens this dialog the same way Java does, that is:
|
||||
*
|
||||
* <ul>
|
||||
* <li>If there are multiple dialogs in the actions tag, the {@code minecraft:quick_actions} dialog is opened to select a dialog.</li>
|
||||
* <li>If there is one dialog in the actions tag, that dialog is opened.</li>
|
||||
* <li>If there are no dialogs in the tag, no dialog is opened.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Use {@link GeyserConnection#hasFormOpen()} to check if a dialog was opened.</p>
|
||||
* @since 2.8.0
|
||||
*/
|
||||
void openQuickActions();
|
||||
|
||||
/**
|
||||
* Sends a command as if the player had executed it.
|
||||
*
|
||||
* @param command the command without the leading forward-slash
|
||||
* @since 2.8.0
|
||||
*/
|
||||
void sendCommand(String command);
|
||||
|
||||
/**
|
||||
* Gets the hostname or ip address the player used to join this Geyser instance.
|
||||
* Example:
|
||||
* <ul>
|
||||
* <li> {@code test.geysermc.org} </li>
|
||||
* <li> {@code 127.0.0.1} </li>
|
||||
* <li> {@code 06e9:c755:4eff:5f13:9b4c:4b21:9df2:6a73} </li>
|
||||
* </ul>
|
||||
*
|
||||
* @throws NoSuchElementException if called before the session is fully initialized
|
||||
* @return the ip address or hostname string the player used to join
|
||||
* @since 2.8.3
|
||||
*/
|
||||
@NonNull
|
||||
String joinAddress();
|
||||
|
||||
/**
|
||||
* Gets the port the player used to join this Geyser instance.
|
||||
* Example:
|
||||
* <ul>
|
||||
* <li> {@code 19132} </li>
|
||||
* <li> {@code 2202} </li>
|
||||
* </ul>
|
||||
*
|
||||
* @throws NoSuchElementException if called before the session is fully initialized
|
||||
* @return the port the player used to join
|
||||
* @since 2.8.3
|
||||
*/
|
||||
@Positive
|
||||
int joinPort();
|
||||
|
||||
/**
|
||||
* Applies a skin to a player seen by this Geyser connection.
|
||||
* If the uuid matches the {@link GeyserConnection#javaUuid()}, this
|
||||
* will update the skin of this Geyser connection.
|
||||
* If the player uuid provided is not known to this connection, this method
|
||||
* will silently return.
|
||||
*
|
||||
* @param player which player this skin should be applied to
|
||||
* @param skinData the skin data to apply
|
||||
* @since 2.8.3
|
||||
*/
|
||||
void sendSkin(@NonNull UUID player, @NonNull SkinData skinData);
|
||||
|
||||
/**
|
||||
* @param javaId the Java entity ID to look up.
|
||||
* @return a {@link GeyserEntity} if present in this connection's entity tracker.
|
||||
|
||||
@@ -81,4 +81,10 @@ public interface EntityData {
|
||||
* @return whether the movement is locked
|
||||
*/
|
||||
boolean isMovementLocked();
|
||||
|
||||
/**
|
||||
* Sends a request to the Java server to switch the items in the main and offhand.
|
||||
* There is no guarantee of the server accepting the request.
|
||||
*/
|
||||
void switchHands();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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.property;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.api.event.lifecycle.GeyserDefineEntityPropertiesEvent;
|
||||
|
||||
/**
|
||||
* Collects property changes to be applied as a single, batched update to an entity.
|
||||
* <p>
|
||||
* Notes:
|
||||
* <ul>
|
||||
* <li>Passing {@code null} as a value resets the property to its default.</li>
|
||||
* <li>Numeric properties must be within declared ranges; enum properties must use an allowed value.</li>
|
||||
* <li>Multiple updates to the same property within a single batch will result in the last value being applied.</li>
|
||||
* <li>The updater is short-lived and should not be retained outside the batching callback.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <pre>{@code
|
||||
* entity.updatePropertiesBatched(updater -> {
|
||||
* updater.update(SOME_FLOAT_PROPERTY, 0.15f);
|
||||
* updater.update(SOME_BOOLEAN_PROPERTY, true);
|
||||
* updater.update(SOME_INT_PROPERTY, null); // reset to default
|
||||
* });
|
||||
* }</pre>
|
||||
*
|
||||
* @since 2.9.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface BatchPropertyUpdater {
|
||||
|
||||
/**
|
||||
* Queues an update for the given property within the current batch.
|
||||
* <p>
|
||||
* If {@code value} is {@code null}, the property will be reset to its default value
|
||||
* as declared when the property was registered during the {@link GeyserDefineEntityPropertiesEvent}.
|
||||
*
|
||||
* @param property a {@link GeyserEntityProperty} registered for the target entity type
|
||||
* @param value the new value, or {@code null} to reset to the default
|
||||
* @param <T> the property's value type
|
||||
*
|
||||
* @since 2.9.0
|
||||
*/
|
||||
<T> void update(@NonNull GeyserEntityProperty<T> property, @Nullable T value);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2025 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,42 +23,43 @@
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.util;
|
||||
package org.geysermc.geyser.api.entity.property;
|
||||
|
||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.ListTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.Tag;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.api.util.Identifier;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
public final class JavaCodecUtil {
|
||||
/**
|
||||
* Represents a property that can be attached to an entity.
|
||||
* <p>
|
||||
* Entity properties are used to describe metadata about an entity, such as
|
||||
* integers, floats, booleans, or enums.
|
||||
* @see <a href="https://learn.microsoft.com/en-us/minecraft/creator/documents/introductiontoentityproperties?view=minecraft-bedrock-stable#number-of-entity-properties-per-entity-type">
|
||||
* Official documentation for info</a>
|
||||
*
|
||||
* @param <T> the type of value stored by this property
|
||||
*
|
||||
* @since 2.9.0
|
||||
*/
|
||||
public interface GeyserEntityProperty<T> {
|
||||
|
||||
/**
|
||||
* Iterate over a Java Edition codec and return each entry as a CompoundTag
|
||||
* Gets the unique name of this property.
|
||||
* Custom properties cannot use the vanilla namespace
|
||||
* to avoid collisions with vanilla entity properties.
|
||||
*
|
||||
* @return the property identifier
|
||||
* @since 2.9.0
|
||||
*/
|
||||
public static Iterable<CompoundTag> iterateAsTag(CompoundTag tag) {
|
||||
ListTag value = tag.get("value");
|
||||
Iterator<Tag> originalIterator = value.iterator();
|
||||
return new Iterable<>() {
|
||||
@NonNull
|
||||
@Override
|
||||
public Iterator<CompoundTag> iterator() {
|
||||
return new Iterator<>() {
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return originalIterator.hasNext();
|
||||
}
|
||||
Identifier identifier();
|
||||
|
||||
@Override
|
||||
public CompoundTag next() {
|
||||
return (CompoundTag) originalIterator.next();
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private JavaCodecUtil() {
|
||||
}
|
||||
/**
|
||||
* Gets the default value of this property which
|
||||
* is set upon spawning entities.
|
||||
*
|
||||
* @return the default value of this property
|
||||
* @since 2.9.0
|
||||
*/
|
||||
@NonNull
|
||||
T defaultValue();
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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.property.type;
|
||||
|
||||
import org.geysermc.geyser.api.entity.property.GeyserEntityProperty;
|
||||
|
||||
/**
|
||||
* Represents a boolean entity property.
|
||||
* @since 2.9.0
|
||||
*/
|
||||
public interface GeyserBooleanEntityProperty extends GeyserEntityProperty<Boolean> {
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2025 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,12 +23,20 @@
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.api.entity.property.type;
|
||||
|
||||
import org.geysermc.geyser.api.entity.property.GeyserEntityProperty;
|
||||
|
||||
/**
|
||||
* Contains useful collections for use in Geyser.
|
||||
* <p>
|
||||
* Of note are the fixed int maps. Designed for use with block states that are positive and sequential, they do not allow keys to be
|
||||
* added that are not greater by one versus the previous key. Because of this, speedy operations of {@link java.util.Map#get(java.lang.Object)}
|
||||
* and {@link java.util.Map#containsKey(java.lang.Object)} can be performed by simply checking the bounds of the map
|
||||
* size and its "start" integer.
|
||||
* Represents a Java enum-backed enum property.
|
||||
* There are a few key limitations:
|
||||
* <ul>
|
||||
* <li>There cannot be more than 16 values</li>
|
||||
* <li>Enum names cannot be longer than 32 chars, must start with a letter, and may contain numbers and underscores</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param <E> the enum type
|
||||
* @since 2.9.0
|
||||
*/
|
||||
package org.geysermc.geyser.util.collection;
|
||||
public interface GeyserEnumEntityProperty<E extends Enum<E>> extends GeyserEntityProperty<E> {
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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.property.type;
|
||||
|
||||
import org.geysermc.geyser.api.entity.property.GeyserEntityProperty;
|
||||
import org.geysermc.geyser.api.event.lifecycle.GeyserDefineEntityPropertiesEvent;
|
||||
import org.geysermc.geyser.api.util.Identifier;
|
||||
|
||||
/**
|
||||
* Represents a float-backed entity property with inclusive bounds.
|
||||
* Values associated with this property must be always within the {@code [min(), max()]} bounds.
|
||||
*
|
||||
* @see GeyserDefineEntityPropertiesEvent#registerFloatProperty(Identifier, Identifier, float, float, Float)
|
||||
* @since 2.9.0
|
||||
*/
|
||||
public interface GeyserFloatEntityProperty extends GeyserEntityProperty<Float> {
|
||||
|
||||
/**
|
||||
* @return the inclusive lower bound for this property
|
||||
* @since 2.9.0
|
||||
*/
|
||||
float min();
|
||||
|
||||
/**
|
||||
* @return the inclusive upper bound for this property
|
||||
* @since 2.9.0
|
||||
*/
|
||||
float max();
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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.property.type;
|
||||
|
||||
import org.geysermc.geyser.api.entity.property.GeyserEntityProperty;
|
||||
import org.geysermc.geyser.api.event.lifecycle.GeyserDefineEntityPropertiesEvent;
|
||||
import org.geysermc.geyser.api.util.Identifier;
|
||||
|
||||
/**
|
||||
* Represents an int-backed entity property with inclusive bounds.
|
||||
* There are a few key limitations:
|
||||
* <ul>
|
||||
* <li>Values must be always within the {@code [min(), max()]} bounds</li>
|
||||
* <li>Molang evaluation uses floats under the hood; very large integers can lose precision.
|
||||
* Prefer keeping values in a practical range to avoid rounding issues.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @see GeyserDefineEntityPropertiesEvent#registerIntegerProperty(Identifier, Identifier, int, int, Integer)
|
||||
* @since 2.9.0
|
||||
*/
|
||||
public interface GeyserIntEntityProperty extends GeyserEntityProperty<Integer> {
|
||||
|
||||
/**
|
||||
* @return the inclusive lower bound for this property
|
||||
* @since 2.9.0
|
||||
*/
|
||||
int min();
|
||||
|
||||
/**
|
||||
* @return the inclusive upper bound for this property
|
||||
* @since 2.9.0
|
||||
*/
|
||||
int max();
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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.property.type;
|
||||
|
||||
import org.geysermc.geyser.api.entity.property.GeyserEntityProperty;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a string-backed enum property.
|
||||
* There are a few key limitations:
|
||||
* <ul>
|
||||
* <li>There cannot be more than 16 values</li>
|
||||
* <li>The values' names cannot be longer than 32 chars, must start with a letter, and may contain numbers and underscores</li>
|
||||
* </ul>
|
||||
*
|
||||
* @since 2.9.0
|
||||
*/
|
||||
public interface GeyserStringEnumProperty extends GeyserEntityProperty<String> {
|
||||
|
||||
/**
|
||||
* @return an unmodifiable list of all registered values
|
||||
* @since 2.9.0
|
||||
*/
|
||||
List<String> values();
|
||||
}
|
||||
@@ -26,9 +26,17 @@
|
||||
package org.geysermc.geyser.api.entity.type;
|
||||
|
||||
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.property.BatchPropertyUpdater;
|
||||
import org.geysermc.geyser.api.entity.property.GeyserEntityProperty;
|
||||
import org.geysermc.geyser.api.event.lifecycle.GeyserDefineEntityPropertiesEvent;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Represents a unique instance of an entity. Each {@link org.geysermc.geyser.api.connection.GeyserConnection}
|
||||
* Represents a unique instance of an entity. Each {@link GeyserConnection}
|
||||
* have their own sets of entities - no two instances will share the same GeyserEntity instance.
|
||||
*/
|
||||
public interface GeyserEntity {
|
||||
@@ -37,4 +45,24 @@ public interface GeyserEntity {
|
||||
*/
|
||||
@NonNegative
|
||||
int javaId();
|
||||
|
||||
/**
|
||||
* Updates an entity property with a new value.
|
||||
* If the new value is null, the property is reset to the default value.
|
||||
*
|
||||
* @param property a {@link GeyserEntityProperty} registered for this type in the {@link GeyserDefineEntityPropertiesEvent}
|
||||
* @param value the new property value
|
||||
* @param <T> the type of the value
|
||||
* @since 2.9.0
|
||||
*/
|
||||
default <T> void updateProperty(@NonNull GeyserEntityProperty<T> property, @Nullable T value) {
|
||||
this.updatePropertiesBatched(consumer -> consumer.update(property, value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates multiple properties with just one update packet.
|
||||
* @see BatchPropertyUpdater
|
||||
* @since 2.9.0
|
||||
*/
|
||||
void updatePropertiesBatched(Consumer<BatchPropertyUpdater> consumer);
|
||||
}
|
||||
|
||||
@@ -31,9 +31,9 @@ import org.geysermc.geyser.api.entity.type.GeyserEntity;
|
||||
public interface GeyserPlayerEntity extends GeyserEntity {
|
||||
|
||||
/**
|
||||
* Gets the position of the player.
|
||||
* Gets the position of the player, as it is known to the Java server.
|
||||
*
|
||||
* @return the position of the player.
|
||||
* @return the player's position
|
||||
*/
|
||||
Vector3f position();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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.event.java.ServerCodeOfConductEvent;
|
||||
|
||||
/**
|
||||
* Fired when a player accepts a code of conduct sent by the Java server. API users can listen to this event
|
||||
* to store the acceptance in a cache, and tell Geyser not to do so.
|
||||
*
|
||||
* <p>Java clients cache acceptance locally, but bedrock clients don't. Normally Geyser uses a simple JSON file to implement this,
|
||||
* but an alternative solution may be preferred when using multiple Geyser instances. Such a solution can be implemented through this event and {@link ServerCodeOfConductEvent}.</p>
|
||||
*
|
||||
* @see ServerCodeOfConductEvent
|
||||
* @since 2.9.0
|
||||
*/
|
||||
public class SessionAcceptCodeOfConductEvent extends ConnectionEvent {
|
||||
private final String codeOfConduct;
|
||||
private boolean skipSaving = false;
|
||||
|
||||
public SessionAcceptCodeOfConductEvent(@NonNull GeyserConnection connection, String codeOfConduct) {
|
||||
super(connection);
|
||||
this.codeOfConduct = codeOfConduct;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the code of conduct sent by the server
|
||||
* @since 2.9.0
|
||||
*/
|
||||
public String codeOfConduct() {
|
||||
return codeOfConduct;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@code true} if Geyser should not save the acceptance of the code of conduct in its own cache (through a JSON file), because it was saved elsewhere
|
||||
* @since 2.9.0
|
||||
*/
|
||||
public boolean shouldSkipSaving() {
|
||||
return skipSaving;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link SessionAcceptCodeOfConductEvent#shouldSkipSaving()} to {@code true}.
|
||||
* @since 2.9.0
|
||||
*/
|
||||
public void skipSaving() {
|
||||
this.skipSaving = true;
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,7 @@ public class SessionDisconnectEvent extends ConnectionEvent {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the disconnect reason, thereby overriding th original reason.
|
||||
* Sets the disconnect message shown to the Bedrock client.
|
||||
*
|
||||
* @param disconnectReason the reason for the disconnect
|
||||
*/
|
||||
|
||||
@@ -31,6 +31,7 @@ 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.
|
||||
* @since 2.1.1
|
||||
*/
|
||||
public final class SessionJoinEvent extends ConnectionEvent {
|
||||
public SessionJoinEvent(@NonNull GeyserConnection connection) {
|
||||
|
||||
@@ -26,15 +26,20 @@
|
||||
package org.geysermc.geyser.api.event.bedrock;
|
||||
|
||||
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.event.connection.ConnectionEvent;
|
||||
import org.geysermc.geyser.api.pack.ResourcePack;
|
||||
import org.geysermc.geyser.api.pack.exception.ResourcePackException;
|
||||
import org.geysermc.geyser.api.pack.option.ResourcePackOption;
|
||||
|
||||
import java.util.Collection;
|
||||
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.
|
||||
* Called when Geyser initializes a session for a new Bedrock client and is in the process of sending {@link ResourcePack}'s.
|
||||
* @since 2.1.1
|
||||
*/
|
||||
public abstract class SessionLoadResourcePacksEvent extends ConnectionEvent {
|
||||
public SessionLoadResourcePacksEvent(@NonNull GeyserConnection connection) {
|
||||
@@ -42,26 +47,80 @@ public abstract class SessionLoadResourcePacksEvent extends ConnectionEvent {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an unmodifiable list of {@link ResourcePack}s that will be sent to the client.
|
||||
* Gets the {@link ResourcePack}'s that will be sent to this {@link GeyserConnection}.
|
||||
* To remove packs, use {@link #unregister(UUID)}, as the list returned
|
||||
* by this method is unmodifiable.
|
||||
*
|
||||
* @return an unmodifiable list of resource packs that will be sent to the client.
|
||||
* @return an unmodifiable list of {@link ResourcePack}'s
|
||||
* @since 2.1.1
|
||||
*/
|
||||
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
|
||||
* @deprecated Use {{@link #register(ResourcePack, ResourcePackOption[])}} instead
|
||||
*/
|
||||
public abstract boolean register(@NonNull ResourcePack resourcePack);
|
||||
@Deprecated
|
||||
public abstract boolean register(@NonNull ResourcePack pack);
|
||||
|
||||
/**
|
||||
* Unregisters a resource pack from being sent to the client.
|
||||
* Registers a {@link ResourcePack} to be sent to the client, optionally alongside
|
||||
* specific {@link ResourcePackOption}'s specifying how it will be applied by the client.
|
||||
*
|
||||
* @param uuid the UUID of the resource pack
|
||||
* @return true whether the resource pack was removed from the list of resource packs.
|
||||
* @param pack the {@link ResourcePack} that will be sent to the client
|
||||
* @param options {@link ResourcePackOption}'s that specify how the client loads the pack
|
||||
* @throws ResourcePackException if an issue occurred during pack registration
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public abstract void register(@NonNull ResourcePack pack, @Nullable ResourcePackOption<?>... options);
|
||||
|
||||
/**
|
||||
* Sets {@link ResourcePackOption}'s for a {@link ResourcePack}.
|
||||
* This method can also be used to override options for resource packs already registered in the
|
||||
* {@link org.geysermc.geyser.api.event.lifecycle.GeyserDefineResourcePacksEvent}.
|
||||
*
|
||||
* @param uuid the uuid of the resource pack to register the options for
|
||||
* @param options the {@link ResourcePackOption}'s to register for the resource pack
|
||||
* @throws ResourcePackException if an issue occurred during {@link ResourcePackOption} registration
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public abstract void registerOptions(@NonNull UUID uuid, @NonNull ResourcePackOption<?>... options);
|
||||
|
||||
/**
|
||||
* Returns a collection of {@link ResourcePackOption}'s for a registered {@link ResourcePack}.
|
||||
* The collection returned here is not modifiable.
|
||||
*
|
||||
* @param uuid the {@link ResourcePack} for which the options are set
|
||||
* @return a collection of {@link ResourcePackOption}'s
|
||||
* @throws ResourcePackException if the pack was not registered
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public abstract Collection<ResourcePackOption<?>> options(@NonNull UUID uuid);
|
||||
|
||||
/**
|
||||
* Returns the current {@link ResourcePackOption}, or null, for a given {@link ResourcePackOption.Type}.
|
||||
*
|
||||
* @param uuid the {@link ResourcePack} for which to query this option type
|
||||
* @param type the {@link ResourcePackOption.Type} of the option to query
|
||||
* @throws ResourcePackException if the queried option is invalid or not present on the resource pack
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public abstract @Nullable ResourcePackOption<?> option(@NonNull UUID uuid, ResourcePackOption.@NonNull Type type);
|
||||
|
||||
/**
|
||||
* Unregisters a {@link ResourcePack} from the list of packs sent to this {@link GeyserConnection}.
|
||||
*
|
||||
* @param uuid the UUID of the {@link ResourcePack} to be removed
|
||||
* @since 2.1.1
|
||||
*/
|
||||
public abstract boolean unregister(@NonNull UUID uuid);
|
||||
|
||||
/**
|
||||
* Whether to forcefully disable vibrant visuals for joining clients.
|
||||
* While vibrant visuals are nice to look at, they can cause issues with
|
||||
* some resource packs.
|
||||
*
|
||||
* @param enabled Whether vibrant visuals are allowed. This is true by default.
|
||||
* @since 2.7.2
|
||||
*/
|
||||
public abstract void allowVibrantVisuals(boolean enabled);
|
||||
}
|
||||
|
||||
@@ -31,19 +31,29 @@ 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;
|
||||
import org.geysermc.geyser.api.util.PlatformType;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Called when a session has logged in, and is about to connect to a remote java server.
|
||||
* 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;
|
||||
private Map<String, byte[]> cookies;
|
||||
private boolean transferring;
|
||||
|
||||
public SessionLoginEvent(@NonNull GeyserConnection connection, @NonNull RemoteServer remoteServer) {
|
||||
public SessionLoginEvent(@NonNull GeyserConnection connection,
|
||||
@NonNull RemoteServer remoteServer,
|
||||
@NonNull Map<String, byte[]> cookies) {
|
||||
super(connection);
|
||||
this.remoteServer = remoteServer;
|
||||
this.cookies = cookies;
|
||||
this.transferring = false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,9 +100,9 @@ public final class SessionLoginEvent extends ConnectionEvent implements Cancella
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link RemoteServer} the section will attempt to connect to.
|
||||
* Gets the {@link RemoteServer} the session will attempt to connect to.
|
||||
*
|
||||
* @return the {@link RemoteServer} the section will attempt to connect to.
|
||||
* @return the {@link RemoteServer} the session will attempt to connect to.
|
||||
*/
|
||||
public @NonNull RemoteServer remoteServer() {
|
||||
return this.remoteServer;
|
||||
@@ -100,10 +110,44 @@ public final class SessionLoginEvent extends ConnectionEvent implements Cancella
|
||||
|
||||
/**
|
||||
* Sets the {@link RemoteServer} to connect the session to.
|
||||
* This method will only work as expected on {@link PlatformType#STANDALONE},
|
||||
* as on other Geyser platforms, the remote server is not determined by Geyser.
|
||||
*
|
||||
* @param remoteServer Sets the {@link RemoteServer} to connect to.
|
||||
*/
|
||||
public void remoteServer(@NonNull RemoteServer remoteServer) {
|
||||
this.remoteServer = remoteServer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a map of cookies from a possible previous session. The Java server can send and request these
|
||||
* to store information on the client across server transfers.
|
||||
*/
|
||||
public void cookies(@NonNull Map<String, byte[]> cookies) {
|
||||
Objects.requireNonNull(cookies);
|
||||
this.cookies = cookies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a map of the sessions cookies, if set.
|
||||
* @return the connections cookies
|
||||
*/
|
||||
public @NonNull Map<String, byte[]> cookies() {
|
||||
return cookies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the connection intent of the connection
|
||||
*/
|
||||
public void transferring(boolean transferring) {
|
||||
this.transferring = transferring;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether this login attempt to the Java server
|
||||
* has the transfer intent
|
||||
*/
|
||||
public boolean transferring() {
|
||||
return this.transferring;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* 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.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.skin.Cape;
|
||||
import org.geysermc.geyser.api.skin.Skin;
|
||||
import org.geysermc.geyser.api.skin.SkinData;
|
||||
import org.geysermc.geyser.api.skin.SkinGeometry;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Called when a skin is applied to a player.
|
||||
* <p>
|
||||
* Won't be called when a fake player is spawned for a player skull.
|
||||
*/
|
||||
public abstract class SessionSkinApplyEvent extends ConnectionEvent {
|
||||
|
||||
private final String username;
|
||||
private final UUID uuid;
|
||||
private final boolean slim;
|
||||
private final boolean bedrock;
|
||||
private final SkinData originalSkinData;
|
||||
|
||||
public SessionSkinApplyEvent(@NonNull GeyserConnection connection, String username, UUID uuid, boolean slim, boolean bedrock, SkinData skinData) {
|
||||
super(connection);
|
||||
this.username = username;
|
||||
this.uuid = uuid;
|
||||
this.slim = slim;
|
||||
this.bedrock = bedrock;
|
||||
this.originalSkinData = skinData;
|
||||
}
|
||||
|
||||
/**
|
||||
* The username of the player.
|
||||
*
|
||||
* @return the username of the player
|
||||
*/
|
||||
public @NonNull String username() {
|
||||
return username;
|
||||
}
|
||||
|
||||
/**
|
||||
* The UUID of the player.
|
||||
*
|
||||
* @return the UUID of the player
|
||||
*/
|
||||
public @NonNull UUID uuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the player is using a slim model.
|
||||
*
|
||||
* @return if the player is using a slim model
|
||||
*/
|
||||
public boolean slim() {
|
||||
return slim;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the player is a Bedrock player.
|
||||
*
|
||||
* @return if the player is a Bedrock player
|
||||
*/
|
||||
public boolean bedrock() {
|
||||
return bedrock;
|
||||
}
|
||||
|
||||
/**
|
||||
* The original skin data of the player.
|
||||
*
|
||||
* @return the original skin data of the player
|
||||
*/
|
||||
public @NonNull SkinData originalSkin() {
|
||||
return originalSkinData;
|
||||
}
|
||||
|
||||
/**
|
||||
* The skin data of the player.
|
||||
*
|
||||
* @return the current skin data of the player
|
||||
*/
|
||||
public abstract @NonNull SkinData skinData();
|
||||
|
||||
/**
|
||||
* Change the skin of the player.
|
||||
*
|
||||
* @param newSkin the new skin
|
||||
*/
|
||||
public abstract void skin(@NonNull Skin newSkin);
|
||||
|
||||
/**
|
||||
* Change the cape of the player.
|
||||
*
|
||||
* @param newCape the new cape
|
||||
*/
|
||||
public abstract void cape(@NonNull Cape newCape);
|
||||
|
||||
/**
|
||||
* Change the geometry of the player.
|
||||
*
|
||||
* @param newGeometry the new geometry
|
||||
*/
|
||||
public abstract void geometry(@NonNull SkinGeometry newGeometry);
|
||||
|
||||
/**
|
||||
* Change the geometry of the player.
|
||||
* <p>
|
||||
* Constructs a generic {@link SkinGeometry} object with the given data.
|
||||
*
|
||||
* @param geometryName the name of the geometry
|
||||
* @param geometryData the data of the geometry
|
||||
*/
|
||||
public void geometry(@NonNull String geometryName, @NonNull String geometryData) {
|
||||
geometry(new SkinGeometry("{\"geometry\" :{\"default\" :\"" + geometryName + "\"}}", geometryData));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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.connection;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.event.Cancellable;
|
||||
import org.geysermc.event.Event;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
/**
|
||||
* Called whenever a client attempts to connect to the server, before the connection is accepted.
|
||||
*/
|
||||
public final class ConnectionRequestEvent implements Event, Cancellable {
|
||||
|
||||
private boolean cancelled;
|
||||
private final InetSocketAddress ip;
|
||||
private final InetSocketAddress proxyIp;
|
||||
|
||||
public ConnectionRequestEvent(@NonNull InetSocketAddress ip, @Nullable InetSocketAddress proxyIp) {
|
||||
this.ip = ip;
|
||||
this.proxyIp = proxyIp;
|
||||
}
|
||||
|
||||
/**
|
||||
* The IP address of the client attempting to connect
|
||||
*
|
||||
* @return the IP address of the client attempting to connect
|
||||
* @deprecated Use {@link #inetSocketAddress()} instead
|
||||
*/
|
||||
@NonNull @Deprecated(forRemoval = true)
|
||||
public InetSocketAddress getInetSocketAddress() {
|
||||
return ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* The IP address of the proxy handling the connection. It will return null if there is no proxy.
|
||||
*
|
||||
* @return the IP address of the proxy handling the connection
|
||||
* @deprecated Use {@link #proxyIp()} instead
|
||||
*/
|
||||
@Nullable @Deprecated(forRemoval = true)
|
||||
public InetSocketAddress getProxyIp() {
|
||||
return proxyIp;
|
||||
}
|
||||
|
||||
/**
|
||||
* The IP address of the client attempting to connect
|
||||
*
|
||||
* @return the IP address of the client attempting to connect
|
||||
*/
|
||||
@NonNull
|
||||
public InetSocketAddress inetSocketAddress() {
|
||||
return ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* The IP address of the proxy handling the connection. It will return null if there is no proxy.
|
||||
*
|
||||
* @return the IP address of the proxy handling the connection
|
||||
*/
|
||||
@Nullable
|
||||
public InetSocketAddress proxyIp() {
|
||||
return proxyIp;
|
||||
}
|
||||
|
||||
/**
|
||||
* The cancel status of this event. If this event is cancelled, the connection will be rejected.
|
||||
*
|
||||
* @return the cancel status of this event
|
||||
*/
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return cancelled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cancel status of this event. If this event is canceled, the connection will be rejected.
|
||||
*
|
||||
* @param cancelled the cancel status of this event.
|
||||
*/
|
||||
@Override
|
||||
public void setCancelled(boolean cancelled) {
|
||||
this.cancelled = cancelled;
|
||||
}
|
||||
}
|
||||
@@ -33,10 +33,10 @@ import org.geysermc.event.Event;
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
/**
|
||||
* Called whenever Geyser gets pinged
|
||||
* Called whenever Geyser gets pinged by a Bedrock client.
|
||||
* <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.
|
||||
* 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 the information provided in this event.
|
||||
*/
|
||||
public interface GeyserBedrockPingEvent extends Event {
|
||||
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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.java;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.api.connection.GeyserConnection;
|
||||
import org.geysermc.geyser.api.event.bedrock.SessionAcceptCodeOfConductEvent;
|
||||
import org.geysermc.geyser.api.event.connection.ConnectionEvent;
|
||||
|
||||
/**
|
||||
* Fired when the Java server sends a code of conduct during the configuration phase.
|
||||
* API users can listen to this event and tell Geyser the player has accepted the code of conduct before, which will result in the
|
||||
* code of conduct not being shown to the player.
|
||||
*
|
||||
* <p>Java clients cache this locally, but bedrock clients don't. Normally Geyser uses a simple JSON file to implement this,
|
||||
* but an alternative solution may be preferred when using multiple Geyser instances. Such a solution can be implemented through this event and {@link SessionAcceptCodeOfConductEvent}.</p>
|
||||
*
|
||||
* @see SessionAcceptCodeOfConductEvent
|
||||
* @since 2.9.0
|
||||
*/
|
||||
public final class ServerCodeOfConductEvent extends ConnectionEvent {
|
||||
private final String codeOfConduct;
|
||||
private boolean hasAccepted = false;
|
||||
|
||||
public ServerCodeOfConductEvent(@NonNull GeyserConnection connection, String codeOfConduct) {
|
||||
super(connection);
|
||||
this.codeOfConduct = codeOfConduct;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the code of conduct sent by the server
|
||||
* @since 2.9.0
|
||||
*/
|
||||
public String codeOfConduct() {
|
||||
return codeOfConduct;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@code true} if Geyser should not show the code of conduct to the player, because they have already accepted it
|
||||
* @since 2.9.0
|
||||
*/
|
||||
public boolean accepted() {
|
||||
return hasAccepted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link ServerCodeOfConductEvent#accepted()} to {@code true}.
|
||||
* @since 2.9.0
|
||||
*/
|
||||
public void accept() {
|
||||
this.hasAccepted = true;
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@ import java.util.Set;
|
||||
* <br>
|
||||
* This event is mapped to the existence of Brigadier on the server.
|
||||
*/
|
||||
public class ServerDefineCommandsEvent extends ConnectionEvent implements Cancellable {
|
||||
public final class ServerDefineCommandsEvent extends ConnectionEvent implements Cancellable {
|
||||
private final Set<? extends CommandInfo> commands;
|
||||
private boolean cancelled;
|
||||
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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.java;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.checkerframework.common.value.qual.IntRange;
|
||||
import org.geysermc.geyser.api.connection.GeyserConnection;
|
||||
import org.geysermc.geyser.api.event.connection.ConnectionEvent;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Fired when the Java server sends a transfer request to a different Java server.
|
||||
* Geyser Extensions can listen to this event and set a target server ip/port for Bedrock players to be transferred to.
|
||||
*/
|
||||
public final class ServerTransferEvent extends ConnectionEvent {
|
||||
|
||||
private final String host;
|
||||
private final int port;
|
||||
private String bedrockHost;
|
||||
private int bedrockPort;
|
||||
private final Map<String, byte[]> cookies;
|
||||
|
||||
public ServerTransferEvent(@NonNull GeyserConnection connection,
|
||||
@NonNull String host, int port, @NonNull Map<String, byte[]> cookies) {
|
||||
super(connection);
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.cookies = cookies;
|
||||
this.bedrockHost = null;
|
||||
this.bedrockPort = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* The host that the Java server requests a transfer to.
|
||||
*
|
||||
* @return the host
|
||||
*/
|
||||
public @NonNull String host() {
|
||||
return this.host;
|
||||
}
|
||||
|
||||
/**
|
||||
* The port that the Java server requests a transfer to.
|
||||
*
|
||||
* @return the port
|
||||
*/
|
||||
public int port() {
|
||||
return this.port;
|
||||
}
|
||||
|
||||
/**
|
||||
* The host that the Bedrock player should try and connect to.
|
||||
* If this is not set, the Bedrock player will just be disconnected.
|
||||
*
|
||||
* @return the host where the Bedrock client will be transferred to, or null if not set.
|
||||
*/
|
||||
public @Nullable String bedrockHost() {
|
||||
return this.bedrockHost;
|
||||
}
|
||||
|
||||
/**
|
||||
* The port that the Bedrock player should try and connect to.
|
||||
* If this is not set, the Bedrock player will just be disconnected.
|
||||
*
|
||||
* @return the port where the Bedrock client will be transferred to, or -1 if not set.
|
||||
*/
|
||||
public int bedrockPort() {
|
||||
return this.bedrockPort;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the host for the Bedrock player to be transferred to
|
||||
*/
|
||||
public void bedrockHost(@NonNull String host) {
|
||||
if (host == null || host.isBlank()) {
|
||||
throw new IllegalArgumentException("Server address cannot be null or blank");
|
||||
}
|
||||
this.bedrockHost = host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the port for the Bedrock player to be transferred to
|
||||
*/
|
||||
public void bedrockPort(@IntRange(from = 0, to = 65535) int port) {
|
||||
if (port < 0 || port > 65535) {
|
||||
throw new IllegalArgumentException("Server port must be between 0 and 65535, was " + port);
|
||||
}
|
||||
this.bedrockPort = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a map of the sessions current cookies.
|
||||
*
|
||||
* @return the connections cookies
|
||||
*/
|
||||
public @NonNull Map<String, byte[]> cookies() {
|
||||
return cookies;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -50,7 +50,7 @@ public interface GeyserDefineCommandsEvent extends Event {
|
||||
/**
|
||||
* Gets all the registered built-in {@link Command}s.
|
||||
*
|
||||
* @return all the registered built-in commands
|
||||
* @return all the registered built-in commands as an unmodifiable map
|
||||
*/
|
||||
@NonNull
|
||||
Map<String, Command> commands();
|
||||
|
||||
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.event.Event;
|
||||
import org.geysermc.geyser.api.entity.EntityData;
|
||||
import org.geysermc.geyser.api.entity.property.GeyserEntityProperty;
|
||||
import org.geysermc.geyser.api.entity.property.type.GeyserBooleanEntityProperty;
|
||||
import org.geysermc.geyser.api.entity.property.type.GeyserEnumEntityProperty;
|
||||
import org.geysermc.geyser.api.entity.property.type.GeyserFloatEntityProperty;
|
||||
import org.geysermc.geyser.api.entity.property.type.GeyserIntEntityProperty;
|
||||
import org.geysermc.geyser.api.entity.property.type.GeyserStringEnumProperty;
|
||||
import org.geysermc.geyser.api.entity.type.GeyserEntity;
|
||||
import org.geysermc.geyser.api.util.Identifier;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Lifecycle event fired during Geyser's startup to allow custom entity properties
|
||||
* to be registered for a specific entity type.
|
||||
* <p>
|
||||
* Listeners can add new properties for any entity by passing the target entity's
|
||||
* identifier (e.g., {@code Identifier.of("player")}) to the registration methods.
|
||||
* The returned {@link GeyserEntityProperty} is used to identify the properties and to
|
||||
* update the value of a specific entity instance.
|
||||
*
|
||||
* <h2>Example usage</h2>
|
||||
* <pre>{@code
|
||||
* public void onDefine(GeyserDefineEntityPropertiesEvent event) {
|
||||
* Identifier player = Identifier.of("player");
|
||||
* GeyserFloatEntityProperty ANIMATION_SPEED =
|
||||
* event.registerFloatProperty(player, Identifier.of("my_group:animation_speed"), 0.0f, 1.0f, 0.1f);
|
||||
* GeyserBooleanEntityProperty SHOW_SHORTS =
|
||||
* event.registerBooleanProperty(player, Identifier.of("my_group:show_shorts"), false);
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* Retrieving entity instances is possible with the {@link EntityData#entityByJavaId(int)} method, or
|
||||
* {@link EntityData#playerEntity()} for the connection player entity.
|
||||
* To update the value of a property on a specific entity, use {@link GeyserEntity#updateProperty(GeyserEntityProperty, Object)},
|
||||
* or {@link GeyserEntity#updatePropertiesBatched(Consumer)} to update multiple properties efficiently at once.
|
||||
*
|
||||
* <p><b>Notes:</b>
|
||||
* <ul>
|
||||
* <li>Default values must fall within the provided bounds.</li>
|
||||
* <li>There cannot be more than 32 properties registered per entity type in total</li>
|
||||
* <li>{@link #properties(Identifier)} returns properties registered for the given entity
|
||||
* (including those added earlier in the same callback), including vanilla properties.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @since 2.9.0
|
||||
*/
|
||||
public interface GeyserDefineEntityPropertiesEvent extends Event {
|
||||
|
||||
/**
|
||||
* Returns an <em>unmodifiable</em> view of all properties that have been registered
|
||||
* so far for the given entity type. This includes entity properties used for vanilla gameplay,
|
||||
* such as those used for creaking animations.
|
||||
*
|
||||
* @param entityType the Java edition entity type identifier
|
||||
* @return an unmodifiable collection of registered properties
|
||||
*
|
||||
* @since 2.9.0
|
||||
*/
|
||||
Collection<GeyserEntityProperty<?>> properties(@NonNull Identifier entityType);
|
||||
|
||||
/**
|
||||
* Registers a {@code float}-backed entity property.
|
||||
*
|
||||
* @param entityType the Java edition entity type identifier
|
||||
* @param propertyIdentifier the unique property identifier
|
||||
* @param min the minimum allowed value (inclusive)
|
||||
* @param max the maximum allowed value (inclusive)
|
||||
* @param defaultValue the default value assigned initially on entity spawn - if null, it will be the minimum value
|
||||
* @return the created float property
|
||||
*
|
||||
* @since 2.9.0
|
||||
*/
|
||||
GeyserFloatEntityProperty registerFloatProperty(@NonNull Identifier entityType, @NonNull Identifier propertyIdentifier, float min, float max, @Nullable Float defaultValue);
|
||||
|
||||
/**
|
||||
* Registers a {@code float}-backed entity property with a default value set to the minimum value.
|
||||
* @see #registerFloatProperty(Identifier, Identifier, float, float, Float)
|
||||
*
|
||||
* @param entityType the Java edition entity type identifier
|
||||
* @param propertyIdentifier the unique property identifier
|
||||
* @param min the minimum allowed value (inclusive)
|
||||
* @param max the maximum allowed value (inclusive)
|
||||
* @return the created float property
|
||||
*
|
||||
* @since 2.9.0
|
||||
*/
|
||||
default GeyserFloatEntityProperty registerFloatProperty(@NonNull Identifier entityType, @NonNull Identifier propertyIdentifier, float min, float max) {
|
||||
return registerFloatProperty(entityType, propertyIdentifier, min, max, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an {@code int}-backed entity property.
|
||||
*
|
||||
* @param entityType the Java edition entity type identifier
|
||||
* @param propertyIdentifier the unique property identifier
|
||||
* @param min the minimum allowed value (inclusive)
|
||||
* @param max the maximum allowed value (inclusive)
|
||||
* @param defaultValue the default value assigned initially on entity spawn - if null, it will be the minimum value
|
||||
* @return the created int property
|
||||
*
|
||||
* @since 2.9.0
|
||||
*/
|
||||
GeyserIntEntityProperty registerIntegerProperty(@NonNull Identifier entityType, @NonNull Identifier propertyIdentifier, int min, int max, @Nullable Integer defaultValue);
|
||||
|
||||
/**
|
||||
* Registers an {@code int}-backed entity property with a default value set to the minimum value.
|
||||
*
|
||||
* @param entityType the Java edition entity type identifier
|
||||
* @param propertyIdentifier the unique property identifier
|
||||
* @param min the minimum allowed value (inclusive)
|
||||
* @param max the maximum allowed value (inclusive)
|
||||
* @return the created int property
|
||||
*
|
||||
* @since 2.9.0
|
||||
*/
|
||||
default GeyserIntEntityProperty registerIntegerProperty(@NonNull Identifier entityType, @NonNull Identifier propertyIdentifier, int min, int max) {
|
||||
return registerIntegerProperty(entityType, propertyIdentifier, min, max, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a {@code boolean}-backed entity property.
|
||||
*
|
||||
* @param entityType the Java edition entity type identifier
|
||||
* @param propertyIdentifier the unique property identifier
|
||||
* @param defaultValue the default boolean value
|
||||
* @return the created boolean property handle
|
||||
*
|
||||
* @since 2.9.0
|
||||
*/
|
||||
GeyserBooleanEntityProperty registerBooleanProperty(@NonNull Identifier entityType, @NonNull Identifier propertyIdentifier, boolean defaultValue);
|
||||
|
||||
/**
|
||||
* Registers a {@code boolean}-backed entity property with a default of {@code false}.
|
||||
* @see #registerBooleanProperty(Identifier, Identifier, boolean)
|
||||
*
|
||||
* @param entityType the Java edition entity type identifier
|
||||
* @param propertyIdentifier the unique property identifier
|
||||
* @return the created boolean property
|
||||
* @since 2.9.0
|
||||
*/
|
||||
default GeyserBooleanEntityProperty registerBooleanProperty(@NonNull Identifier entityType, @NonNull Identifier propertyIdentifier) {
|
||||
return registerBooleanProperty(entityType, propertyIdentifier, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a typed {@linkplain Enum enum}-backed entity property.
|
||||
* <p>
|
||||
* The enum constants define the allowed values. If {@code defaultValue} is {@code null},
|
||||
* the first enum value is set as the default.
|
||||
* @see GeyserEnumEntityProperty for further limitations
|
||||
*
|
||||
* @param entityType the Java edition entity type identifier
|
||||
* @param propertyIdentifier the unique property identifier
|
||||
* @param enumClass the enum class that defines allowed values
|
||||
* @param defaultValue the default enum value, or {@code null} for the first enum value to be the default
|
||||
* @param <E> the enum type
|
||||
* @return the created enum property
|
||||
*
|
||||
* @since 2.9.0
|
||||
*/
|
||||
<E extends Enum<E>> GeyserEnumEntityProperty<E> registerEnumProperty(@NonNull Identifier entityType, @NonNull Identifier propertyIdentifier, @NonNull Class<E> enumClass, @Nullable E defaultValue);
|
||||
|
||||
/**
|
||||
* Registers a typed {@linkplain Enum enum}-backed entity property with the first value set as the default.
|
||||
* @see #registerEnumProperty(Identifier, Identifier, Class, Enum)
|
||||
*
|
||||
* @param entityType the Java edition entity type identifier
|
||||
* @param propertyIdentifier the unique property identifier
|
||||
* @param enumClass the enum class that defines allowed values
|
||||
* @param <E> the enum type
|
||||
* @return the created enum property
|
||||
*
|
||||
* @since 2.9.0
|
||||
*/
|
||||
default <E extends Enum<E>> GeyserEnumEntityProperty<E> registerEnumProperty(@NonNull Identifier entityType, @NonNull Identifier propertyIdentifier, @NonNull Class<E> enumClass) {
|
||||
return registerEnumProperty(entityType, propertyIdentifier, enumClass, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a string-backed "enum-like" entity property where the set of allowed values
|
||||
* is defined by the provided list. If {@code defaultValue} is {@code null}, the first value is used as the default
|
||||
* on entity spawn. The default must be one of the values in {@code values}.
|
||||
* @see GeyserStringEnumProperty
|
||||
*
|
||||
* @param entityType the Java edition entity type identifier
|
||||
* @param propertyIdentifier the unique property identifier
|
||||
* @param values the allowed string values
|
||||
* @param defaultValue the default string value, or {@code null} for the first value to be used
|
||||
* @return the created string-enum property
|
||||
*
|
||||
* @since 2.9.0
|
||||
*/
|
||||
GeyserStringEnumProperty registerEnumProperty(@NonNull Identifier entityType, @NonNull Identifier propertyIdentifier, @NonNull List<String> values, @Nullable String defaultValue);
|
||||
|
||||
/**
|
||||
* Registers a string-backed "enum-like" entity property with the first value as the default.
|
||||
* @see #registerEnumProperty(Identifier, Identifier, List, String)
|
||||
*
|
||||
* @param entityType the Java edition entity type identifier
|
||||
* @param propertyIdentifier the unique property identifier
|
||||
* @param values the allowed string values
|
||||
* @return the created string-enum property handle
|
||||
*
|
||||
* @since 2.9.0
|
||||
*/
|
||||
default GeyserStringEnumProperty registerEnumProperty(@NonNull Identifier entityType, @NonNull Identifier propertyIdentifier, @NonNull List<String> values) {
|
||||
return registerEnumProperty(entityType, propertyIdentifier, values, null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2025 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.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.event.Event;
|
||||
import org.geysermc.geyser.api.pack.ResourcePack;
|
||||
import org.geysermc.geyser.api.pack.exception.ResourcePackException;
|
||||
import org.geysermc.geyser.api.pack.option.ResourcePackOption;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Called when {@link ResourcePack}'s are loaded within Geyser.
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public abstract class GeyserDefineResourcePacksEvent implements Event {
|
||||
|
||||
/**
|
||||
* Gets the {@link ResourcePack}'s that will be sent to connecting Bedrock clients.
|
||||
* To remove packs, use {@link #unregister(UUID)}, as the list returned
|
||||
* by this method is unmodifiable.
|
||||
*
|
||||
* @return an unmodifiable list of {@link ResourcePack}'s
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public abstract @NonNull List<ResourcePack> resourcePacks();
|
||||
|
||||
/**
|
||||
* Registers a {@link ResourcePack} to be sent to the client, optionally alongside
|
||||
* {@link ResourcePackOption}'s specifying how it will be applied on clients.
|
||||
*
|
||||
* @param pack a resource pack that will be sent to the client
|
||||
* @param options {@link ResourcePackOption}'s that specify how clients load the pack
|
||||
* @throws ResourcePackException if an issue occurred during pack registration
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public abstract void register(@NonNull ResourcePack pack, @Nullable ResourcePackOption<?>... options);
|
||||
|
||||
/**
|
||||
* Sets {@link ResourcePackOption}'s for a {@link ResourcePack}.
|
||||
*
|
||||
* @param uuid the uuid of the resource pack to register the options for
|
||||
* @param options the {@link ResourcePackOption}'s to register for the resource pack
|
||||
* @throws ResourcePackException if an issue occurred during {@link ResourcePackOption} registration
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public abstract void registerOptions(@NonNull UUID uuid, @NonNull ResourcePackOption<?>... options);
|
||||
|
||||
/**
|
||||
* Returns a collection of {@link ResourcePackOption}'s for a registered {@link ResourcePack}.
|
||||
* The collection returned here is not modifiable.
|
||||
*
|
||||
* @param uuid the uuid of the {@link ResourcePack} for which the options are set
|
||||
* @return a collection of {@link ResourcePackOption}'s
|
||||
* @throws ResourcePackException if the pack was not registered
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public abstract Collection<ResourcePackOption<?>> options(@NonNull UUID uuid);
|
||||
|
||||
/**
|
||||
* Returns the current option, or null, for a given {@link ResourcePackOption.Type}.
|
||||
*
|
||||
* @param uuid the {@link ResourcePack} for which to query this option type
|
||||
* @param type the {@link ResourcePackOption.Type} of the option to query
|
||||
* @throws ResourcePackException if the queried option is invalid or not present on the resource pack
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public abstract @Nullable ResourcePackOption<?> option(@NonNull UUID uuid, ResourcePackOption.@NonNull Type type);
|
||||
|
||||
/**
|
||||
* Unregisters a {@link ResourcePack} from the list of packs sent to connecting Bedrock clients.
|
||||
*
|
||||
* @param uuid the UUID of the {@link ResourcePack} to be removed
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public abstract void unregister(@NonNull UUID uuid);
|
||||
}
|
||||
@@ -32,9 +32,8 @@ import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Called when resource packs are loaded within Geyser.
|
||||
*
|
||||
* @param resourcePacks a mutable list of the currently listed resource packs
|
||||
* @deprecated Use the {@link GeyserDefineResourcePacksEvent} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public record GeyserLoadResourcePacksEvent(@NonNull List<Path> resourcePacks) implements Event {
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ 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}.
|
||||
* After this event is fired, some lifecycle events can be fired again - such as the {@link GeyserDefineResourcePacksEvent}.
|
||||
*
|
||||
* @param extensionManager the extension manager
|
||||
* @param eventBus the event bus
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.lifecycle;
|
||||
|
||||
import org.geysermc.event.Event;
|
||||
import org.geysermc.event.PostOrder;
|
||||
import org.geysermc.geyser.api.permission.PermissionChecker;
|
||||
|
||||
/**
|
||||
* Fired by any permission manager implementations that wish to add support for custom permission checking.
|
||||
* This event is not guaranteed to be fired - it is currently only fired on Geyser-Standalone and ViaProxy.
|
||||
* <p>
|
||||
* Subscribing to this event with an earlier {@link PostOrder} and registering a {@link PermissionChecker}
|
||||
* will result in that checker having a higher priority than others.
|
||||
*/
|
||||
public interface GeyserRegisterPermissionCheckersEvent extends Event {
|
||||
|
||||
void register(PermissionChecker checker);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.lifecycle;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.event.Event;
|
||||
import org.geysermc.geyser.api.util.TriState;
|
||||
|
||||
/**
|
||||
* Fired by anything that wishes to gather permission nodes and defaults.
|
||||
* <p>
|
||||
* This event is not guaranteed to be fired, as certain Geyser platforms do not have a native permission system.
|
||||
* It can be expected to fire on Geyser-Spigot, Geyser-NeoForge, Geyser-Standalone, and Geyser-ViaProxy
|
||||
* It may be fired by a 3rd party regardless of the platform.
|
||||
*/
|
||||
public interface GeyserRegisterPermissionsEvent extends Event {
|
||||
|
||||
/**
|
||||
* Registers a permission node and its default value with the firer.<p>
|
||||
* {@link TriState#TRUE} corresponds to all players having the permission by default.<br>
|
||||
* {@link TriState#NOT_SET} corresponds to only server operators having the permission by default (if such a concept exists on the platform).<br>
|
||||
* {@link TriState#FALSE} corresponds to no players having the permission by default.<br>
|
||||
*
|
||||
* @param permission the permission node to register
|
||||
* @param defaultValue the default value of the node
|
||||
*/
|
||||
void register(@NonNull String permission, @NonNull TriState defaultValue);
|
||||
}
|
||||
@@ -107,6 +107,15 @@ public interface Extension extends EventRegistrar {
|
||||
return this.extensionLoader().description(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the root command that all of this extension's commands will stem from.
|
||||
* By default, this is the extension's id.
|
||||
*/
|
||||
@NonNull
|
||||
default String rootCommand() {
|
||||
return this.description().id();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the extension's logger
|
||||
*
|
||||
|
||||
@@ -59,33 +59,46 @@ public interface ExtensionDescription {
|
||||
String main();
|
||||
|
||||
/**
|
||||
* Gets the extension's major api version
|
||||
* Represents the human api version that the extension requires.
|
||||
* See the <a href="https://github.com/geysermc/api/blob/master/geyser-versioning.md">Geyser version outline</a>)
|
||||
* for more details on the Geyser API version.
|
||||
*
|
||||
* @return the extension's major api version
|
||||
* @return the extension's requested human api version
|
||||
*/
|
||||
int humanApiVersion();
|
||||
|
||||
/**
|
||||
* Represents the major api version that the extension requires.
|
||||
* See the <a href="https://github.com/geysermc/api/blob/master/geyser-versioning.md">Geyser version outline</a>)
|
||||
* for more details on the Geyser API version.
|
||||
*
|
||||
* @return the extension's requested major api version
|
||||
*/
|
||||
int majorApiVersion();
|
||||
|
||||
/**
|
||||
* Gets the extension's minor api version
|
||||
* Represents the minor api version that the extension requires.
|
||||
* See the <a href="https://github.com/geysermc/api/blob/master/geyser-versioning.md">Geyser version outline</a>)
|
||||
* for more details on the Geyser API version.
|
||||
*
|
||||
* @return the extension's minor api version
|
||||
* @return the extension's requested minor api version
|
||||
*/
|
||||
int minorApiVersion();
|
||||
|
||||
/**
|
||||
* Gets the extension's patch api version
|
||||
*
|
||||
* @return the extension's patch api version
|
||||
* No longer in use. Geyser is now using an adaption of the romantic versioning scheme.
|
||||
* See <a href="https://github.com/geysermc/api/blob/master/geyser-versioning.md">here</a> for details.
|
||||
*/
|
||||
int patchApiVersion();
|
||||
@Deprecated(forRemoval = true)
|
||||
default int patchApiVersion() {
|
||||
return minorApiVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the extension's api version.
|
||||
*
|
||||
* @return the extension's api version
|
||||
* Returns the extension's requested Geyser Api version.
|
||||
*/
|
||||
default String apiVersion() {
|
||||
return majorApiVersion() + "." + minorApiVersion() + "." + patchApiVersion();
|
||||
return humanApiVersion() + "." + majorApiVersion() + "." + minorApiVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
* 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
|
||||
@@ -80,10 +80,9 @@ public interface NonVanillaCustomItemData extends CustomItemData {
|
||||
@Nullable String toolType();
|
||||
|
||||
/**
|
||||
* Gets the tool tier of the item.
|
||||
*
|
||||
* @return the tool tier of the item
|
||||
* @deprecated no longer used
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
@Nullable String toolTier();
|
||||
|
||||
/**
|
||||
@@ -108,10 +107,9 @@ public interface NonVanillaCustomItemData extends CustomItemData {
|
||||
@Nullable String translationString();
|
||||
|
||||
/**
|
||||
* Gets the repair materials of the item.
|
||||
*
|
||||
* @return the repair materials of the item
|
||||
* @deprecated No longer used.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
@Nullable Set<String> repairMaterials();
|
||||
|
||||
/**
|
||||
@@ -161,6 +159,13 @@ public interface NonVanillaCustomItemData extends CustomItemData {
|
||||
return displayHandheld();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the block the item places.
|
||||
*
|
||||
* @return the block the item places
|
||||
*/
|
||||
String block();
|
||||
|
||||
static NonVanillaCustomItemData.Builder builder() {
|
||||
return GeyserApi.api().provider(NonVanillaCustomItemData.Builder.class);
|
||||
}
|
||||
@@ -201,6 +206,8 @@ public interface NonVanillaCustomItemData extends CustomItemData {
|
||||
|
||||
Builder chargeable(boolean isChargeable);
|
||||
|
||||
Builder block(String block);
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #displayHandheld(boolean)} instead.
|
||||
*/
|
||||
|
||||
@@ -35,6 +35,7 @@ import java.nio.file.Path;
|
||||
/**
|
||||
* Represents a pack codec that can be used
|
||||
* to provide resource packs to clients.
|
||||
* @since 2.1.1
|
||||
*/
|
||||
public abstract class PackCodec {
|
||||
|
||||
@@ -42,6 +43,7 @@ public abstract class PackCodec {
|
||||
* Gets the sha256 hash of the resource pack.
|
||||
*
|
||||
* @return the hash of the resource pack
|
||||
* @since 2.1.1
|
||||
*/
|
||||
public abstract byte @NonNull [] sha256();
|
||||
|
||||
@@ -49,34 +51,66 @@ public abstract class PackCodec {
|
||||
* Gets the resource pack size.
|
||||
*
|
||||
* @return the resource pack file size
|
||||
* @since 2.1.1
|
||||
*/
|
||||
public abstract long size();
|
||||
|
||||
/**
|
||||
* Serializes the given resource pack into a byte buffer.
|
||||
* @deprecated use {@link #serialize()} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
@NonNull
|
||||
public SeekableByteChannel serialize(@NonNull ResourcePack resourcePack) throws IOException {
|
||||
return serialize();
|
||||
};
|
||||
|
||||
/**
|
||||
* Serializes the given codec into a byte buffer.
|
||||
*
|
||||
* @param resourcePack the resource pack to serialize
|
||||
* @return the serialized resource pack
|
||||
* @since 2.6.2
|
||||
*/
|
||||
@NonNull
|
||||
public abstract SeekableByteChannel serialize(@NonNull ResourcePack resourcePack) throws IOException;
|
||||
public abstract SeekableByteChannel serialize() throws IOException;
|
||||
|
||||
/**
|
||||
* Creates a new resource pack from this codec.
|
||||
*
|
||||
* @return the new resource pack
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull
|
||||
protected abstract ResourcePack create();
|
||||
|
||||
/**
|
||||
* Creates a new resource pack builder from this codec.
|
||||
*
|
||||
* @return the new resource pack builder
|
||||
* @since 2.6.2
|
||||
*/
|
||||
protected abstract ResourcePack.@NonNull Builder createBuilder();
|
||||
|
||||
/**
|
||||
* Creates a new pack provider from the given path.
|
||||
*
|
||||
* @param path the path to create the pack provider from
|
||||
* @return the new pack provider
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull
|
||||
public static PackCodec path(@NonNull Path path) {
|
||||
return GeyserApi.api().provider(PathPackCodec.class, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new pack provider from the given url.
|
||||
*
|
||||
* @param url the url to create the pack provider from
|
||||
* @return the new pack provider
|
||||
* @since 2.6.2
|
||||
*/
|
||||
@NonNull
|
||||
public static PackCodec url(@NonNull String url) {
|
||||
return GeyserApi.api().provider(UrlPackCodec.class, url);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import java.nio.file.Path;
|
||||
/**
|
||||
* Represents a pack codec that creates a resource
|
||||
* pack from a path on the filesystem.
|
||||
* @since 2.1.1
|
||||
*/
|
||||
public abstract class PathPackCodec extends PackCodec {
|
||||
|
||||
@@ -39,6 +40,7 @@ public abstract class PathPackCodec extends PackCodec {
|
||||
* Gets the path of the resource pack.
|
||||
*
|
||||
* @return the path of the resource pack
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull
|
||||
public abstract Path path();
|
||||
|
||||
@@ -26,12 +26,16 @@
|
||||
package org.geysermc.geyser.api.pack;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.common.returnsreceiver.qual.This;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @since 2.1.1
|
||||
*/
|
||||
public interface ResourcePack {
|
||||
|
||||
@@ -39,6 +43,7 @@ public interface ResourcePack {
|
||||
* The {@link PackCodec codec} for this pack.
|
||||
*
|
||||
* @return the codec for this pack
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull
|
||||
PackCodec codec();
|
||||
@@ -47,6 +52,7 @@ public interface ResourcePack {
|
||||
* Gets the resource pack manifest.
|
||||
*
|
||||
* @return the resource pack manifest
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull
|
||||
ResourcePackManifest manifest();
|
||||
@@ -55,18 +61,83 @@ public interface ResourcePack {
|
||||
* 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
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull
|
||||
String contentKey();
|
||||
|
||||
/**
|
||||
* Shortcut for getting the UUID from the {@link ResourcePackManifest}.
|
||||
*
|
||||
* @return the resource pack uuid
|
||||
* @since 2.6.2
|
||||
*/
|
||||
@NonNull
|
||||
default UUID uuid() {
|
||||
return manifest().header().uuid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a resource pack with the given {@link PackCodec}.
|
||||
*
|
||||
* @param codec the pack codec
|
||||
* @return the resource pack
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull
|
||||
static ResourcePack create(@NonNull PackCodec codec) {
|
||||
return codec.create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Builder} for a resource pack.
|
||||
* It can be used to set a content key.
|
||||
*
|
||||
* @param codec the {@link PackCodec} to base the builder on
|
||||
* @return a {@link Builder} to build a resource pack
|
||||
* @since 2.6.2
|
||||
*/
|
||||
static Builder builder(@NonNull PackCodec codec) {
|
||||
return codec.createBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* A builder for a resource pack. It allows providing a content key manually.
|
||||
* @since 2.6.2
|
||||
*/
|
||||
interface Builder {
|
||||
|
||||
/**
|
||||
* @return the {@link ResourcePackManifest} of this resource pack
|
||||
* @since 2.6.2
|
||||
*/
|
||||
ResourcePackManifest manifest();
|
||||
|
||||
/**
|
||||
* @return the {@link PackCodec} of this resource pack
|
||||
* @since 2.6.2
|
||||
*/
|
||||
PackCodec codec();
|
||||
|
||||
/**
|
||||
* @return the current content key, or an empty string if not set
|
||||
* @since 2.6.2
|
||||
*/
|
||||
String contentKey();
|
||||
|
||||
/**
|
||||
* Sets a content key for this resource pack.
|
||||
*
|
||||
* @param contentKey the content key
|
||||
* @return this builder
|
||||
* @since 2.6.2
|
||||
*/
|
||||
@This Builder contentKey(@NonNull String contentKey);
|
||||
|
||||
/**
|
||||
* @return the resource pack
|
||||
* @since 2.6.2
|
||||
*/
|
||||
ResourcePack build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,55 +26,99 @@
|
||||
package org.geysermc.geyser.api.pack;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.api.event.bedrock.SessionLoadResourcePacksEvent;
|
||||
import org.geysermc.geyser.api.event.lifecycle.GeyserDefineResourcePacksEvent;
|
||||
import org.geysermc.geyser.api.pack.option.SubpackOption;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Represents a resource pack manifest.
|
||||
* Represents a Bedrock edition resource pack manifest (manifest.json).
|
||||
* All resource packs are required to have such a file as it identifies the resource pack.
|
||||
* See <a href="https://learn.microsoft.com/en-us/minecraft/creator/reference/content/addonsreference/examples/addonmanifest?view=minecraft-bedrock-stable">
|
||||
* Microsoft's docs for more info</a>.
|
||||
* @since 2.1.1
|
||||
*/
|
||||
public interface ResourcePackManifest {
|
||||
|
||||
/**
|
||||
* Gets the format version of the resource pack.
|
||||
* <p>
|
||||
* "1" is used for skin packs,
|
||||
* "2" is used for resource and behavior packs, and world templates.
|
||||
*
|
||||
* @return the format version
|
||||
* @since 2.1.1
|
||||
*/
|
||||
int formatVersion();
|
||||
|
||||
/**
|
||||
* Gets the header of the resource pack.
|
||||
* Gets the {@link Header} of the resource pack.
|
||||
*
|
||||
* @return the header
|
||||
* @return the {@link Header}
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull
|
||||
Header header();
|
||||
|
||||
/**
|
||||
* Gets the modules of the resource pack.
|
||||
* Gets the {@link Module}'s of the resource pack.
|
||||
*
|
||||
* @return the modules
|
||||
* @return a collection of modules
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull
|
||||
Collection<? extends Module> modules();
|
||||
|
||||
/**
|
||||
* Gets the dependencies of the resource pack.
|
||||
* Gets the {@link Dependency}'s of the resource pack.
|
||||
*
|
||||
* @return the dependencies
|
||||
* @return a collection of dependencies
|
||||
* @since 2.6.2
|
||||
*/
|
||||
@NonNull
|
||||
Collection<? extends Dependency> dependencies();
|
||||
|
||||
/**
|
||||
* Gets the {@link Subpack}'s of the resource pack.
|
||||
* See <a href="https://learn.microsoft.com/en-us/minecraft/creator/documents/utilizingsubpacks">Microsoft's docs on subpacks
|
||||
* </a> for more information.
|
||||
*
|
||||
* @return a collection of subpacks
|
||||
* @since 2.6.2
|
||||
*/
|
||||
@NonNull
|
||||
Collection<? extends Subpack> subpacks();
|
||||
|
||||
/**
|
||||
* Gets the {@link Setting}'s of the resource pack.
|
||||
* These are shown to Bedrock client's in the resource pack settings menu (<a href="https://learn.microsoft.com/en-us/minecraft/creator/documents/media/utilizingsubpacks/subpackgif.gif?view=minecraft-bedrock-stable">see here</a>)
|
||||
* to inform users about what the resource pack and sub-packs include.
|
||||
*
|
||||
* @return a collection of settings
|
||||
* @since 2.6.2
|
||||
*/
|
||||
@NonNull
|
||||
Collection<? extends Setting> settings();
|
||||
|
||||
/**
|
||||
* Represents the header of a resource pack.
|
||||
* It contains the main information about the resource pack, such as
|
||||
* the name, description, or uuid.
|
||||
* See <a href="https://learn.microsoft.com/en-us/minecraft/creator/reference/content/addonsreference/examples/addonmanifest?view=minecraft-bedrock-stable#header">
|
||||
* Microsoft's docs for further details on headers.</a>
|
||||
* @since 2.1.1
|
||||
*/
|
||||
interface Header {
|
||||
|
||||
/**
|
||||
* Gets the UUID of the resource pack.
|
||||
* Gets the UUID of the resource pack. It is a unique identifier that differentiates this resource pack from any other resource pack.
|
||||
* Bedrock clients will cache resource packs, and download resource packs when the uuid is new (or the version changes).
|
||||
*
|
||||
* @return the UUID
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull
|
||||
UUID uuid();
|
||||
@@ -83,6 +127,7 @@ public interface ResourcePackManifest {
|
||||
* Gets the version of the resource pack.
|
||||
*
|
||||
* @return the version
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull
|
||||
Version version();
|
||||
@@ -91,6 +136,7 @@ public interface ResourcePackManifest {
|
||||
* Gets the name of the resource pack.
|
||||
*
|
||||
* @return the name
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull
|
||||
String name();
|
||||
@@ -99,6 +145,7 @@ public interface ResourcePackManifest {
|
||||
* Gets the description of the resource pack.
|
||||
*
|
||||
* @return the description
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull
|
||||
String description();
|
||||
@@ -107,6 +154,7 @@ public interface ResourcePackManifest {
|
||||
* Gets the minimum supported Minecraft version of the resource pack.
|
||||
*
|
||||
* @return the minimum supported Minecraft version
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull
|
||||
Version minimumSupportedMinecraftVersion();
|
||||
@@ -114,21 +162,29 @@ public interface ResourcePackManifest {
|
||||
|
||||
/**
|
||||
* Represents a module of a resource pack.
|
||||
* It contains information about the content type that is
|
||||
* offered by this resource pack.
|
||||
* See <a href="https://learn.microsoft.com/en-us/minecraft/creator/reference/content/addonsreference/examples/addonmanifest?view=minecraft-bedrock-stable#modules">
|
||||
* Microsoft's docs for further details on modules.</a>
|
||||
* @since 2.1.1
|
||||
*/
|
||||
interface Module {
|
||||
|
||||
/**
|
||||
* Gets the UUID of the module.
|
||||
* This should usually be different from the UUID in the {@link Header}.
|
||||
*
|
||||
* @return the UUID
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull
|
||||
UUID uuid();
|
||||
|
||||
/**
|
||||
* Gets the version of the module.
|
||||
* Gets the {@link Version} of the module.
|
||||
*
|
||||
* @return the version
|
||||
* @return the {@link Version}
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull
|
||||
Version version();
|
||||
@@ -137,6 +193,7 @@ public interface ResourcePackManifest {
|
||||
* Gets the type of the module.
|
||||
*
|
||||
* @return the type
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull
|
||||
String type();
|
||||
@@ -145,6 +202,7 @@ public interface ResourcePackManifest {
|
||||
* Gets the description of the module.
|
||||
*
|
||||
* @return the description
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull
|
||||
String description();
|
||||
@@ -152,28 +210,102 @@ public interface ResourcePackManifest {
|
||||
|
||||
/**
|
||||
* Represents a dependency of a resource pack.
|
||||
* These are references to other resource packs that must be
|
||||
* present in order for this resource pack to apply.
|
||||
* See <a href="https://learn.microsoft.com/en-us/minecraft/creator/reference/content/addonsreference/examples/addonmanifest?view=minecraft-bedrock-stable#dependencies">
|
||||
* Microsoft's docs for further details on dependencies.</a>
|
||||
* @since 2.1.1
|
||||
*/
|
||||
interface Dependency {
|
||||
|
||||
/**
|
||||
* Gets the UUID of the dependency.
|
||||
* Gets the UUID of the resource pack dependency.
|
||||
*
|
||||
* @return the uuid
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull
|
||||
UUID uuid();
|
||||
|
||||
/**
|
||||
* Gets the version of the dependency.
|
||||
* Gets the {@link Version} of the dependency.
|
||||
*
|
||||
* @return the version
|
||||
* @return the {@link Version}
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull
|
||||
Version version();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a subpack of a resource pack. These are often used for "variants" of the resource pack,
|
||||
* such as lesser details, or additional features either to be determined by player's taste or adapted to the player device's performance.
|
||||
* See <a href="https://learn.microsoft.com/en-us/minecraft/creator/documents/utilizingsubpacks">Micoroft's docs</a> for more information.
|
||||
*/
|
||||
interface Subpack {
|
||||
|
||||
/**
|
||||
* Gets the folder name where this sub-pack is placed in.
|
||||
*
|
||||
* @return the folder name
|
||||
* @since 2.6.2
|
||||
*/
|
||||
@NonNull
|
||||
String folderName();
|
||||
|
||||
/**
|
||||
* Gets the name of this subpack. Required for each subpack to be valid.
|
||||
* To make a Bedrock client load any subpack, register the resource pack
|
||||
* in the {@link SessionLoadResourcePacksEvent} or {@link GeyserDefineResourcePacksEvent} and specify a
|
||||
* {@link SubpackOption} with the name of the subpack to load.
|
||||
*
|
||||
* @return the subpack name
|
||||
* @since 2.6.2
|
||||
*/
|
||||
@NonNull
|
||||
String name();
|
||||
|
||||
/**
|
||||
* Gets the memory tier of this Subpack, representing how much RAM a device must have to run it.
|
||||
* Each memory tier requires 0.25 GB of RAM. For example, a memory tier of 0 is no requirement,
|
||||
* and a memory tier of 4 requires 1GB of RAM.
|
||||
*
|
||||
* @return the memory tier
|
||||
* @since 2.6.2
|
||||
*/
|
||||
@Nullable
|
||||
Float memoryTier();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a setting that is shown client-side that describe what a pack does.
|
||||
* Multiple setting entries are shown in separate paragraphs.
|
||||
* @since 2.6.2
|
||||
*/
|
||||
interface Setting {
|
||||
|
||||
/**
|
||||
* The type of the setting. Usually just "label".
|
||||
*
|
||||
* @return the type
|
||||
* @since 2.6.2
|
||||
*/
|
||||
@NonNull
|
||||
String type();
|
||||
|
||||
/**
|
||||
* The text shown for the setting.
|
||||
*
|
||||
* @return the text content
|
||||
* @since 2.6.2
|
||||
*/
|
||||
@NonNull
|
||||
String text();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a version of a resource pack.
|
||||
* @since 2.1.1
|
||||
*/
|
||||
interface Version {
|
||||
|
||||
@@ -181,6 +313,7 @@ public interface ResourcePackManifest {
|
||||
* Gets the major version.
|
||||
*
|
||||
* @return the major version
|
||||
* @since 2.1.1
|
||||
*/
|
||||
int major();
|
||||
|
||||
@@ -188,6 +321,7 @@ public interface ResourcePackManifest {
|
||||
* Gets the minor version.
|
||||
*
|
||||
* @return the minor version
|
||||
* @since 2.1.1
|
||||
*/
|
||||
int minor();
|
||||
|
||||
@@ -195,6 +329,7 @@ public interface ResourcePackManifest {
|
||||
* Gets the patch version.
|
||||
*
|
||||
* @return the patch version
|
||||
* @since 2.1.1
|
||||
*/
|
||||
int patch();
|
||||
|
||||
@@ -202,6 +337,7 @@ public interface ResourcePackManifest {
|
||||
* Gets the version formatted as a String.
|
||||
*
|
||||
* @return the version string
|
||||
* @since 2.1.1
|
||||
*/
|
||||
@NonNull String toString();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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 pack codec that creates a resource
|
||||
* pack from a URL.
|
||||
* <p>
|
||||
* Due to Bedrock limitations, the URL must:
|
||||
* <ul>
|
||||
* <li>be a direct download link to a .zip or .mcpack resource pack</li>
|
||||
* <li>use the application type `application/zip` and set a correct content length</li>
|
||||
* </ul>
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public abstract class UrlPackCodec extends PackCodec {
|
||||
|
||||
/**
|
||||
* Gets the URL to the resource pack location.
|
||||
*
|
||||
* @return the URL of the resource pack
|
||||
* @since 2.6.2
|
||||
*/
|
||||
@NonNull
|
||||
public abstract String url();
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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.exception;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* Used to indicate an exception that occurred while handling resource pack registration,
|
||||
* or during resource pack option validation.
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public class ResourcePackException extends IllegalArgumentException {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* The {@link Cause} of this exception.
|
||||
*/
|
||||
private final Cause cause;
|
||||
|
||||
/**
|
||||
* @param cause the cause of this exception
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public ResourcePackException(Cause cause) {
|
||||
super(cause.message());
|
||||
this.cause = cause;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param cause the cause of this exception
|
||||
* @param message an additional, more in-depth message about the issue.
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public ResourcePackException(Cause cause, String message) {
|
||||
super(message);
|
||||
this.cause = cause;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the cause of this exception
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public Cause cause() {
|
||||
return cause;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents different causes with explanatory messages stating which issue occurred.
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public enum Cause {
|
||||
DUPLICATE("A resource pack with this UUID was already registered!"),
|
||||
INVALID_PACK("This resource pack is not a valid Bedrock edition resource pack!"),
|
||||
INVALID_PACK_OPTION("Attempted to register an invalid resource pack option!"),
|
||||
PACK_NOT_FOUND("No resource pack was found!"),
|
||||
UNKNOWN_IMPLEMENTATION("Use the resource pack codecs to create resource packs.");
|
||||
|
||||
private final String message;
|
||||
|
||||
/**
|
||||
* @return the message of this cause
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public String message() {
|
||||
return message;
|
||||
}
|
||||
|
||||
Cause(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 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.pack.option;
|
||||
|
||||
import org.geysermc.geyser.api.GeyserApi;
|
||||
|
||||
/**
|
||||
* Allows specifying a pack priority that decides the order on how packs are sent to the client.
|
||||
* If two resource packs modify the same texture - for example if one removes the pumpkin overlay and
|
||||
* the other is just making it translucent, one of the packs will override the other.
|
||||
* Specifically, the pack with the higher priority will override the pack changes of the lower priority.
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public interface PriorityOption extends ResourcePackOption<Integer> {
|
||||
|
||||
PriorityOption HIGHEST = PriorityOption.priority(100);
|
||||
PriorityOption HIGH = PriorityOption.priority(50);
|
||||
PriorityOption NORMAL = PriorityOption.priority(0);
|
||||
PriorityOption LOW = PriorityOption.priority(-50);
|
||||
PriorityOption LOWEST = PriorityOption.priority(-100);
|
||||
|
||||
/**
|
||||
* Constructs a priority option based on a value between 0 and 10.
|
||||
* The higher the number, the higher will this pack appear in the resource pack stack.
|
||||
*
|
||||
* @param priority an integer that is above 0, but smaller than 10
|
||||
* @return the priority option
|
||||
* @since 2.6.2
|
||||
*/
|
||||
static PriorityOption priority(int priority) {
|
||||
if (priority < -100 || priority > 100) {
|
||||
throw new IllegalArgumentException("Priority must be between 0 and 10 inclusive!");
|
||||
}
|
||||
return GeyserApi.api().provider(PriorityOption.class, priority);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (c) 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.pack.option;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.api.pack.ResourcePack;
|
||||
import org.geysermc.geyser.api.pack.exception.ResourcePackException;
|
||||
|
||||
/**
|
||||
* Represents a resource pack option that can be used to specify how a resource
|
||||
* pack is sent to Bedrock clients.
|
||||
* <p>
|
||||
* Not all options can be applied to all resource packs. For example, you cannot specify
|
||||
* a specific subpack to be loaded on resource packs that do not have subpacks.
|
||||
* To see which limitations apply to specific resource pack options, check the javadocs
|
||||
* or see the {@link #validate(ResourcePack)} method.
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public interface ResourcePackOption<T> {
|
||||
|
||||
/**
|
||||
* @return the option type
|
||||
* @since 2.6.2
|
||||
*/
|
||||
@NonNull Type type();
|
||||
|
||||
/**
|
||||
* @return the value of the option
|
||||
* @since 2.6.2
|
||||
*/
|
||||
@NonNull T value();
|
||||
|
||||
/**
|
||||
* Used to validate a specific options for a pack.
|
||||
* Some options are not applicable to some packs.
|
||||
*
|
||||
* @param pack the resource pack to validate the option for
|
||||
* @throws ResourcePackException with the {@link ResourcePackException.Cause#INVALID_PACK_OPTION} cause
|
||||
* @since 2.6.2
|
||||
*/
|
||||
void validate(@NonNull ResourcePack pack);
|
||||
|
||||
/**
|
||||
* Represents the different types of resource pack options.
|
||||
* @since 2.6.2
|
||||
*/
|
||||
enum Type {
|
||||
SUBPACK,
|
||||
PRIORITY,
|
||||
FALLBACK
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) 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.pack.option;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.api.GeyserApi;
|
||||
import org.geysermc.geyser.api.pack.ResourcePackManifest;
|
||||
|
||||
/**
|
||||
* Can be used to specify which subpack from a resource pack a player should load.
|
||||
* Available subpacks can be seen in a resource pack manifest {@link ResourcePackManifest#subpacks()}.
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public interface SubpackOption extends ResourcePackOption<String> {
|
||||
|
||||
/**
|
||||
* Creates a subpack option based on a {@link ResourcePackManifest.Subpack}.
|
||||
*
|
||||
* @param subpack the chosen subpack
|
||||
* @return a subpack option specifying that subpack
|
||||
* @since 2.6.2
|
||||
*/
|
||||
static SubpackOption subpack(ResourcePackManifest.@NonNull Subpack subpack) {
|
||||
return named(subpack.name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a subpack option based on a subpack name.
|
||||
*
|
||||
* @param subpackName the name of the subpack
|
||||
* @return a subpack option specifying a subpack with that name
|
||||
* @since 2.6.2
|
||||
*/
|
||||
static SubpackOption named(@NonNull String subpackName) {
|
||||
return GeyserApi.api().provider(SubpackOption.class, subpackName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a subpack option with no subpack specified.
|
||||
*
|
||||
* @return a subpack option specifying no subpack
|
||||
* @since 2.6.2
|
||||
*/
|
||||
static SubpackOption empty() {
|
||||
return GeyserApi.api().provider(SubpackOption.class, "");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) 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.pack.option;
|
||||
|
||||
import org.geysermc.geyser.api.GeyserApi;
|
||||
import org.geysermc.geyser.api.pack.PathPackCodec;
|
||||
import org.geysermc.geyser.api.pack.UrlPackCodec;
|
||||
|
||||
/**
|
||||
* Can be used for resource packs created with the {@link UrlPackCodec}.
|
||||
* When a Bedrock client is unable to download a resource pack from a URL, Geyser will, by default,
|
||||
* serve the resource pack over raknet (as packs are served with the {@link PathPackCodec}).
|
||||
* This option can be used to disable that behavior, and disconnect the player instead.
|
||||
* By default, the {@link UrlFallbackOption#TRUE} option is set.
|
||||
* @since 2.6.2
|
||||
*/
|
||||
public interface UrlFallbackOption extends ResourcePackOption<Boolean> {
|
||||
|
||||
UrlFallbackOption TRUE = fallback(true);
|
||||
UrlFallbackOption FALSE = fallback(false);
|
||||
|
||||
/**
|
||||
* Whether to fall back to serving packs over the raknet connection
|
||||
*
|
||||
* @param fallback whether to fall back
|
||||
* @return a UrlFallbackOption with the specified behavior
|
||||
* @since 2.6.2
|
||||
*/
|
||||
static UrlFallbackOption fallback(boolean fallback) {
|
||||
return GeyserApi.api().provider(UrlFallbackOption.class, fallback);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,31 +23,27 @@
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.item.type;
|
||||
package org.geysermc.geyser.api.permission;
|
||||
|
||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.api.command.CommandSource;
|
||||
import org.geysermc.geyser.api.util.TriState;
|
||||
|
||||
public class ChestItem extends BlockItem {
|
||||
/**
|
||||
* Something capable of checking if a {@link CommandSource} has a permission
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface PermissionChecker {
|
||||
|
||||
public ChestItem(String javaIdentifier, Builder builder) {
|
||||
super(javaIdentifier, builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void translateNbtToBedrock(@NonNull GeyserSession session, @NonNull CompoundTag tag) {
|
||||
super.translateNbtToBedrock(session, tag);
|
||||
|
||||
// Strip the BlockEntityTag from the chests contents
|
||||
// sent to the client. The client does not parse this
|
||||
// or use it for anything, as this tag is fully
|
||||
// server-side, so we remove it to reduce bandwidth and
|
||||
// solve potential issues with very large tags.
|
||||
|
||||
// There was a problem in the past where this would strip
|
||||
// NBT data in creative mode, however with the new server
|
||||
// authoritative inventories, this is no longer a concern.
|
||||
tag.remove("BlockEntityTag");
|
||||
}
|
||||
/**
|
||||
* Checks if the given source has a permission
|
||||
*
|
||||
* @param source the {@link CommandSource} whose permissions should be queried
|
||||
* @param permission the permission node to check
|
||||
* @return a {@link TriState} as the value of the node. {@link TriState#NOT_SET} generally means that the permission
|
||||
* node itself was not found, and the source does not have such permission.
|
||||
* {@link TriState#TRUE} and {@link TriState#FALSE} represent explicitly set values.
|
||||
*/
|
||||
@NonNull
|
||||
TriState hasPermission(@NonNull CommandSource source, @NonNull String permission);
|
||||
}
|
||||
40
api/src/main/java/org/geysermc/geyser/api/skin/Cape.java
Normal file
40
api/src/main/java/org/geysermc/geyser/api/skin/Cape.java
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 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.skin;
|
||||
|
||||
/**
|
||||
* Represents a cape.
|
||||
*
|
||||
* @param textureUrl The URL of the cape texture
|
||||
* @param capeId The ID of the cape
|
||||
* @param capeData The raw cape image data in ARGB format
|
||||
* @param failed If the cape failed to load, this is for things like fallback capes
|
||||
*/
|
||||
public record Cape(String textureUrl, String capeId, byte[] capeData, boolean failed) {
|
||||
public Cape(String textureUrl, String capeId, byte[] capeData) {
|
||||
this(textureUrl, capeId, capeData, false);
|
||||
}
|
||||
}
|
||||
39
api/src/main/java/org/geysermc/geyser/api/skin/Skin.java
Normal file
39
api/src/main/java/org/geysermc/geyser/api/skin/Skin.java
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 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.skin;
|
||||
|
||||
/**
|
||||
* Represents a skin.
|
||||
*
|
||||
* @param textureUrl The URL/ID of the skin texture
|
||||
* @param skinData The raw skin image data in ARGB
|
||||
* @param failed If the skin failed to load, this is for things like fallback skins
|
||||
*/
|
||||
public record Skin(String textureUrl, byte[] skinData, boolean failed) {
|
||||
public Skin(String textureUrl, byte[] skinData) {
|
||||
this(textureUrl, skinData, false);
|
||||
}
|
||||
}
|
||||
32
api/src/main/java/org/geysermc/geyser/api/skin/SkinData.java
Normal file
32
api/src/main/java/org/geysermc/geyser/api/skin/SkinData.java
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 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.skin;
|
||||
|
||||
/**
|
||||
* Represents a full package of {@link Skin}, {@link Cape}, and {@link SkinGeometry}.
|
||||
*/
|
||||
public record SkinData(Skin skin, Cape cape, SkinGeometry geometry) {
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 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.skin;
|
||||
|
||||
/**
|
||||
* Represents geometry of a skin.
|
||||
*
|
||||
* @param geometryName The name of the geometry (JSON)
|
||||
* @param geometryData The geometry data (JSON)
|
||||
*/
|
||||
public record SkinGeometry(String geometryName, String geometryData) {
|
||||
|
||||
public static SkinGeometry WIDE = getLegacy(false);
|
||||
public static SkinGeometry SLIM = getLegacy(true);
|
||||
|
||||
/**
|
||||
* Generate generic geometry
|
||||
*
|
||||
* @param isSlim if true, it will be the slimmer alex model
|
||||
* @return The generic geometry object
|
||||
*/
|
||||
private static SkinGeometry getLegacy(boolean isSlim) {
|
||||
return new SkinGeometry("{\"geometry\" :{\"default\" :\"geometry.humanoid.custom" + (isSlim ? "Slim" : "") + "\"}}", "");
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||
* 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
|
||||
@@ -31,11 +31,10 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
* Represents the creative menu categories or tabs.
|
||||
*/
|
||||
public enum CreativeCategory {
|
||||
COMMANDS("commands", 1),
|
||||
CONSTRUCTION("construction", 2),
|
||||
CONSTRUCTION("construction", 1),
|
||||
NATURE("nature", 2),
|
||||
EQUIPMENT("equipment", 3),
|
||||
ITEMS("items", 4),
|
||||
NATURE("nature", 5),
|
||||
NONE("none", 6);
|
||||
|
||||
private final String internalName;
|
||||
|
||||
116
api/src/main/java/org/geysermc/geyser/api/util/Identifier.java
Normal file
116
api/src/main/java/org/geysermc/geyser/api/util/Identifier.java
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright (c) 2024-2025 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.api.util;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.api.GeyserApi;
|
||||
|
||||
/**
|
||||
* An identifying object for representing unique objects.
|
||||
* This identifier consists of two parts:
|
||||
* <ul>
|
||||
* <li>
|
||||
* a namespace, which is usually a name identifying your work
|
||||
* </li>
|
||||
* <li>
|
||||
* a path, which holds a value.
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
* Examples of identifiers:
|
||||
* <ul>
|
||||
* <li>{@code minecraft:fox}</li>
|
||||
* <li>{@code geysermc:one_fun_example}</li>
|
||||
* </ul>
|
||||
*
|
||||
* If this identifier is referencing anything not in the
|
||||
* vanilla Minecraft game, the namespace cannot be "minecraft".
|
||||
* Further, paths cannot contain colons ({@code :}).
|
||||
*
|
||||
* @since 2.9.0
|
||||
*/
|
||||
public interface Identifier {
|
||||
|
||||
/**
|
||||
* The namespace for Minecraft.
|
||||
* @since 2.9.0
|
||||
*/
|
||||
String DEFAULT_NAMESPACE = "minecraft";
|
||||
|
||||
/**
|
||||
* Attempts to create a new identifier from a namespace and path.
|
||||
*
|
||||
* @return the identifier for this namespace and path
|
||||
* @throws IllegalArgumentException if either namespace or path are invalid.
|
||||
* @since 2.9.0
|
||||
*/
|
||||
static Identifier of(@NonNull String namespace, @NonNull String path) {
|
||||
return GeyserApi.api().provider(Identifier.class, namespace, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to create a new identifier from a string representation.
|
||||
*
|
||||
* @return the identifier for this namespace and path
|
||||
* @throws IllegalArgumentException if either the namespace or path are invalid
|
||||
* @since 2.9.0
|
||||
*/
|
||||
static Identifier of(String identifier) {
|
||||
String[] split = identifier.split(":");
|
||||
String namespace;
|
||||
String path;
|
||||
if (split.length == 1) {
|
||||
namespace = DEFAULT_NAMESPACE;
|
||||
path = split[0];
|
||||
} else if (split.length == 2) {
|
||||
namespace = split[0];
|
||||
path = split[1];
|
||||
} else {
|
||||
throw new IllegalArgumentException("':' in identifier path: " + identifier);
|
||||
}
|
||||
return of(namespace, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the namespace of this identifier.
|
||||
* @since 2.9.0
|
||||
*/
|
||||
String namespace();
|
||||
|
||||
/**
|
||||
* @return the path of this identifier.
|
||||
* @since 2.9.0
|
||||
*/
|
||||
String path();
|
||||
|
||||
/**
|
||||
* Checks whether this identifier is using the "minecraft" namespace.
|
||||
* @since 2.9.0
|
||||
*/
|
||||
default boolean vanilla() {
|
||||
return namespace().equals(DEFAULT_NAMESPACE);
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ public interface MinecraftVersion {
|
||||
|
||||
/**
|
||||
* Gets the Minecraft version as a String.
|
||||
* Example: "1.20.2", or "1.20.40/1.20.41"
|
||||
* Example formats: "1.21", "1.21.1", "1.21.22"
|
||||
*
|
||||
* @return the version string
|
||||
*/
|
||||
|
||||
@@ -1,20 +1,31 @@
|
||||
plugins {
|
||||
id("geyser.platform-conventions")
|
||||
id("geyser.modrinth-uploading-conventions")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(projects.core)
|
||||
|
||||
implementation(libs.cloud.bungee)
|
||||
implementation(libs.adventure.text.serializer.bungeecord)
|
||||
compileOnlyApi(libs.bungeecord.proxy)
|
||||
compileOnlyApi(libs.bungeecord.proxy) {
|
||||
isTransitive = false
|
||||
}
|
||||
compileOnlyApi(libs.bungeecord.api)
|
||||
}
|
||||
|
||||
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.incendo")
|
||||
platformRelocate("io.leangen.geantyref") // provided by cloud, should also be relocated
|
||||
platformRelocate("org.yaml") // Broken as of 1.20
|
||||
|
||||
// These dependencies are already present on the platform
|
||||
provided(libs.bungeecord.proxy)
|
||||
|
||||
application {
|
||||
mainClass.set("org.geysermc.geyser.platform.bungeecord.GeyserBungeeMain")
|
||||
tasks.withType<Jar> {
|
||||
manifest.attributes["Main-Class"] = "org.geysermc.geyser.platform.bungeecord.GeyserBungeeMain"
|
||||
}
|
||||
|
||||
tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
|
||||
@@ -22,14 +33,11 @@ tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
|
||||
|
||||
dependencies {
|
||||
exclude(dependency("com.google.*:.*"))
|
||||
exclude(dependency("io.netty:netty-transport-native-epoll:.*"))
|
||||
exclude(dependency("io.netty:netty-transport-native-unix-common:.*"))
|
||||
exclude(dependency("io.netty:netty-handler:.*"))
|
||||
exclude(dependency("io.netty:netty-common:.*"))
|
||||
exclude(dependency("io.netty:netty-buffer:.*"))
|
||||
exclude(dependency("io.netty:netty-resolver:.*"))
|
||||
exclude(dependency("io.netty:netty-transport:.*"))
|
||||
exclude(dependency("io.netty:netty-codec:.*"))
|
||||
exclude(dependency("io.netty:netty-resolver-dns:.*"))
|
||||
exclude(dependency("io.netty.*:.*"))
|
||||
}
|
||||
}
|
||||
|
||||
modrinth {
|
||||
uploadFile.set(tasks.getByPath("shadowJar"))
|
||||
loaders.add("bungeecord")
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ package org.geysermc.geyser.platform.bungeecord;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelOutboundHandlerAdapter;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import net.md_5.bungee.netty.LengthPrependerAndCompressor;
|
||||
import net.md_5.bungee.protocol.packet.LoginSuccess;
|
||||
import net.md_5.bungee.protocol.packet.SetCompression;
|
||||
|
||||
@@ -40,8 +41,9 @@ public class GeyserBungeeCompressionDisabler extends ChannelOutboundHandlerAdapt
|
||||
// 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");
|
||||
LengthPrependerAndCompressor compressor = ctx.pipeline().get(LengthPrependerAndCompressor.class);
|
||||
if (compressor.isCompress()) {
|
||||
compressor.setCompress(false);
|
||||
compressionDisabled = true;
|
||||
}
|
||||
if (ctx.pipeline().get("decompress") != null) {
|
||||
|
||||
@@ -30,8 +30,22 @@ import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.IoEventLoop;
|
||||
import io.netty.channel.IoEventLoopGroup;
|
||||
import io.netty.channel.IoHandler;
|
||||
import io.netty.channel.IoHandlerFactory;
|
||||
import io.netty.channel.MultiThreadIoEventLoopGroup;
|
||||
import io.netty.channel.SingleThreadIoEventLoop;
|
||||
import io.netty.channel.epoll.Epoll;
|
||||
import io.netty.channel.epoll.EpollIoHandler;
|
||||
import io.netty.channel.local.LocalAddress;
|
||||
import io.netty.channel.local.LocalIoHandler;
|
||||
import io.netty.channel.nio.NioIoHandler;
|
||||
import io.netty.channel.uring.IoUring;
|
||||
import io.netty.channel.uring.IoUringIoHandler;
|
||||
import io.netty.util.AttributeKey;
|
||||
import io.netty.util.concurrent.DefaultThreadFactory;
|
||||
import io.netty.util.concurrent.ThreadAwareExecutor;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.config.ListenerInfo;
|
||||
import net.md_5.bungee.api.event.ProxyReloadEvent;
|
||||
@@ -43,12 +57,15 @@ 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.IoHandlerWrapper;
|
||||
import org.geysermc.geyser.network.netty.LocalServerChannelWrapper;
|
||||
import org.geysermc.geyser.network.netty.LocalSession;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
public class GeyserBungeeInjector extends GeyserInjector implements Listener {
|
||||
private final Plugin plugin;
|
||||
@@ -73,26 +90,58 @@ public class GeyserBungeeInjector extends GeyserInjector implements Listener {
|
||||
throw new UnsupportedOperationException("Geyser does not currently support multiple listeners with injection! " +
|
||||
"Please reach out to us on our Discord at https://discord.gg/GeyserMC so we can hear feedback on your setup.");
|
||||
}
|
||||
|
||||
try {
|
||||
Class.forName("io.netty.channel.MultiThreadIoEventLoopGroup");
|
||||
} catch (ClassNotFoundException e) {
|
||||
bootstrap.getGeyserLogger().error("use-direct-connection disabled as BungeeCord is not up-to-date.");
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO remove
|
||||
try {
|
||||
ProxyServer.class.getMethod("unsafe");
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new UnsupportedOperationException("You're using an outdated version of BungeeCord - please update. Thank you!");
|
||||
}
|
||||
|
||||
ListenerInfo listenerInfo = proxy.getConfig().getListeners().stream().findFirst().orElseThrow(IllegalStateException::new);
|
||||
|
||||
Class<? extends ProxyServer> proxyClass = proxy.getClass();
|
||||
// Using the specified EventLoop is required, or else an error will be thrown
|
||||
EventLoopGroup bossGroup;
|
||||
EventLoopGroup workerGroup;
|
||||
MultiThreadIoEventLoopGroup workerGroup;
|
||||
try {
|
||||
EventLoopGroup eventLoops = (EventLoopGroup) proxyClass.getField("eventLoops").get(proxy);
|
||||
// Netty redirects ServerBootstrap#group(EventLoopGroup) to #group(EventLoopGroup, EventLoopGroup) and uses the same event loop for both.
|
||||
bossGroup = eventLoops;
|
||||
workerGroup = eventLoops;
|
||||
workerGroup = (MultiThreadIoEventLoopGroup) proxyClass.getField("eventLoops").get(proxy);
|
||||
bootstrap.getGeyserLogger().debug("BungeeCord event loop style detected.");
|
||||
} catch (NoSuchFieldException e) {
|
||||
// Waterfall uses two separate event loops
|
||||
// https://github.com/PaperMC/Waterfall/blob/fea7ec356dba6c6ac28819ff11be604af6eb484e/BungeeCord-Patches/0022-Use-a-worker-and-a-boss-event-loop-group.patch
|
||||
bossGroup = (EventLoopGroup) proxyClass.getField("bossEventLoopGroup").get(proxy);
|
||||
workerGroup = (EventLoopGroup) proxyClass.getField("workerEventLoopGroup").get(proxy);
|
||||
workerGroup = (MultiThreadIoEventLoopGroup) proxyClass.getField("workerEventLoopGroup").get(proxy);
|
||||
bootstrap.getGeyserLogger().debug("Waterfall event loop style detected.");
|
||||
}
|
||||
|
||||
final IoEventLoopGroup finalWorkerGroup = workerGroup;
|
||||
var factory = LocalIoHandler.newFactory();
|
||||
var nativeFactory = getNativeHandlerFactory();
|
||||
var wrapperFactory = new IoHandlerFactory() {
|
||||
@Override
|
||||
public IoHandler newHandler(ThreadAwareExecutor ioExecutor) {
|
||||
return new IoHandlerWrapper(factory.newHandler(ioExecutor), nativeFactory.newHandler(ioExecutor));
|
||||
}
|
||||
};
|
||||
|
||||
EventLoopGroup wrapperGroup = new MultiThreadIoEventLoopGroup(factory) {
|
||||
@Override
|
||||
protected ThreadFactory newDefaultThreadFactory() {
|
||||
return new DefaultThreadFactory("Geyser Backend Worker Group", Thread.MAX_PRIORITY);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IoEventLoop newChild(Executor executor, IoHandlerFactory ioHandlerFactory, Object... args) {
|
||||
return new SingleThreadIoEventLoop(finalWorkerGroup, executor, wrapperFactory);
|
||||
}
|
||||
};
|
||||
|
||||
// Is currently just AttributeKey.valueOf("ListerInfo") but we might as well copy the value itself.
|
||||
AttributeKey<ListenerInfo> listener = PipelineUtils.LISTENER;
|
||||
listenerInfo = new ListenerInfo(
|
||||
@@ -138,7 +187,7 @@ public class GeyserBungeeInjector extends GeyserInjector implements Listener {
|
||||
if (channelInitializer == null) {
|
||||
// Proxy has finished initializing; we can safely grab this variable without fear of plugins modifying it
|
||||
// (Older versions of ViaVersion replace this to inject)
|
||||
channelInitializer = PipelineUtils.SERVER_CHILD;
|
||||
channelInitializer = proxy.unsafe().getFrontendChannelInitializer().getChannelInitializer();
|
||||
}
|
||||
initChannel.invoke(channelInitializer, ch);
|
||||
|
||||
@@ -149,7 +198,7 @@ public class GeyserBungeeInjector extends GeyserInjector implements Listener {
|
||||
}
|
||||
})
|
||||
.childAttr(listener, listenerInfo)
|
||||
.group(bossGroup, workerGroup)
|
||||
.group(new MultiThreadIoEventLoopGroup(LocalIoHandler.newFactory()), wrapperGroup)
|
||||
.localAddress(LocalAddress.ANY))
|
||||
.bind()
|
||||
.syncUninterruptibly();
|
||||
@@ -194,4 +243,16 @@ public class GeyserBungeeInjector extends GeyserInjector implements Listener {
|
||||
initializeLocalChannel(GeyserImpl.getInstance().getBootstrap());
|
||||
}
|
||||
}
|
||||
|
||||
private static IoHandlerFactory getNativeHandlerFactory() {
|
||||
// Match whatever settings the Bungee proxy is using
|
||||
// https://github.com/SpigotMC/BungeeCord/blob/617c2728a25347487eee4e8649d52fe57f1ff6e2/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java#L139-L162
|
||||
if (Boolean.parseBoolean(System.getProperty("bungee.io_uring", "false")) && IoUring.isAvailable()) {
|
||||
return IoUringIoHandler.newFactory();
|
||||
}
|
||||
if (Boolean.parseBoolean(System.getProperty("bungee.epoll", "true")) && Epoll.isAvailable()) {
|
||||
return EpollIoHandler.newFactory();
|
||||
}
|
||||
return NioIoHandler.newFactory();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,22 +26,19 @@
|
||||
package org.geysermc.geyser.platform.bungeecord;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.geysermc.geyser.GeyserLogger;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class GeyserBungeeLogger implements GeyserLogger {
|
||||
private final Logger logger;
|
||||
@Getter @Setter
|
||||
private boolean debug;
|
||||
|
||||
public GeyserBungeeLogger(Logger logger, boolean debug) {
|
||||
this.logger = logger;
|
||||
this.debug = debug;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void severe(String message) {
|
||||
logger.severe(message);
|
||||
@@ -78,4 +75,11 @@ public class GeyserBungeeLogger implements GeyserLogger {
|
||||
info(message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String message, Object... arguments) {
|
||||
if (debug) {
|
||||
info(String.format(message, arguments));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
package org.geysermc.geyser.platform.bungeecord;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.ServerPing;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
@@ -36,6 +38,7 @@ 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.GeyserImpl;
|
||||
import org.geysermc.geyser.ping.GeyserPingInfo;
|
||||
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
|
||||
|
||||
@@ -43,6 +46,7 @@ import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class GeyserBungeePingPassthrough implements IGeyserPingPassthrough, Listener {
|
||||
@@ -59,10 +63,20 @@ public class GeyserBungeePingPassthrough implements IGeyserPingPassthrough, List
|
||||
future.complete(event);
|
||||
}
|
||||
}));
|
||||
ProxyPingEvent event = future.join();
|
||||
|
||||
ProxyPingEvent event;
|
||||
|
||||
try {
|
||||
event = future.get(100, TimeUnit.MILLISECONDS);
|
||||
} catch (Throwable cause) {
|
||||
String address = GeyserImpl.getInstance().getConfig().isLogPlayerIpAddresses() ? inetSocketAddress.toString() : "<IP address withheld>";
|
||||
GeyserImpl.getInstance().getLogger().error("Failed to get ping information for " + address, cause);
|
||||
return null;
|
||||
}
|
||||
|
||||
ServerPing response = event.getResponse();
|
||||
return new GeyserPingInfo(
|
||||
response.getDescriptionComponent().toLegacyText(),
|
||||
GsonComponentSerializer.gson().serialize(BungeeComponentSerializer.get().deserialize(new BaseComponent[]{ response.getDescriptionComponent() })),
|
||||
response.getPlayers().getMax(),
|
||||
response.getPlayers().getOnline()
|
||||
);
|
||||
@@ -174,6 +188,21 @@ public class GeyserBungeePingPassthrough implements IGeyserPingPassthrough, List
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTransferred() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<byte[]> retrieveCookie(String s) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<byte[]> sendData(String s, byte[] bytes) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Unsafe unsafe() {
|
||||
throw new UnsupportedOperationException();
|
||||
|
||||
@@ -25,8 +25,11 @@
|
||||
|
||||
package org.geysermc.geyser.platform.bungeecord;
|
||||
|
||||
import io.netty.buffer.AdaptiveByteBufAllocator;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.channel.Channel;
|
||||
import net.md_5.bungee.BungeeCord;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.config.ListenerInfo;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
import net.md_5.bungee.protocol.ProtocolConstants;
|
||||
@@ -34,17 +37,21 @@ 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.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.command.CommandRegistry;
|
||||
import org.geysermc.geyser.command.CommandSourceConverter;
|
||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
||||
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
||||
import org.geysermc.geyser.network.GameProtocol;
|
||||
import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough;
|
||||
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
|
||||
import org.geysermc.geyser.platform.bungeecord.command.GeyserBungeeCommandExecutor;
|
||||
import org.geysermc.geyser.platform.bungeecord.command.BungeeCommandSource;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
import org.geysermc.geyser.util.FileUtils;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.incendo.cloud.bungee.BungeeCommandManager;
|
||||
import org.incendo.cloud.execution.ExecutionCoordinator;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -54,20 +61,18 @@ import java.net.SocketAddress;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
||||
|
||||
private GeyserCommandManager geyserCommandManager;
|
||||
private CommandRegistry commandRegistry;
|
||||
private GeyserBungeeConfiguration geyserConfig;
|
||||
private GeyserBungeeInjector geyserInjector;
|
||||
private GeyserBungeeLogger geyserLogger;
|
||||
private final GeyserBungeeLogger geyserLogger = new GeyserBungeeLogger(getLogger());
|
||||
private IGeyserPingPassthrough geyserBungeePingPassthrough;
|
||||
|
||||
private GeyserImpl geyser;
|
||||
|
||||
@Override
|
||||
@@ -79,31 +84,61 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
||||
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
|
||||
// TODO remove when this isn't an issue anymore
|
||||
boolean adaptiveAllocatorUsed = System.getProperty("io.netty.allocator.type") == null && ByteBufAllocator.DEFAULT instanceof AdaptiveByteBufAllocator;
|
||||
|
||||
try {
|
||||
ProtocolConstants.class.getField("MINECRAFT_1_20_3");
|
||||
} catch (NoSuchFieldException e) {
|
||||
getLogger().warning(" / \\");
|
||||
getLogger().warning(" / \\");
|
||||
getLogger().warning(" / | \\");
|
||||
getLogger().warning(" / | \\ " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_proxy", getProxy().getName()));
|
||||
getLogger().warning(" / \\ " + GeyserLocale.getLocaleStringLog("geyser.may_not_work_as_intended_all_caps"));
|
||||
getLogger().warning(" / o \\");
|
||||
getLogger().warning("/_____________\\");
|
||||
List<Integer> supportedProtocols = ProtocolConstants.SUPPORTED_VERSION_IDS;
|
||||
if (!supportedProtocols.contains(GameProtocol.getJavaProtocolVersion()) || adaptiveAllocatorUsed) {
|
||||
geyserLogger.error(" / \\");
|
||||
geyserLogger.error(" / \\");
|
||||
geyserLogger.error(" / | \\");
|
||||
geyserLogger.error(" / | \\ " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_proxy", getProxy().getName()));
|
||||
geyserLogger.error(" / \\ " + GeyserLocale.getLocaleStringLog("geyser.may_not_work_as_intended_all_caps"));
|
||||
geyserLogger.error(" / o \\");
|
||||
geyserLogger.error("/_____________\\");
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
geyserLogger.warning("Unable to check the versions supported by this proxy! " + e.getMessage());
|
||||
}
|
||||
|
||||
// See https://github.com/SpigotMC/BungeeCord/blob/e62fc6c2916a991e00177c580986d8b1a22fdb41/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java#L138
|
||||
if (Boolean.getBoolean("bungee.io_uring")) {
|
||||
System.setProperty("Mcpl.io_uring", "true");
|
||||
}
|
||||
|
||||
if (!this.loadConfig()) {
|
||||
return;
|
||||
}
|
||||
this.geyserLogger = new GeyserBungeeLogger(getLogger(), geyserConfig.isDebugMode());
|
||||
this.geyserLogger.setDebug(geyserConfig.isDebugMode());
|
||||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
||||
this.geyser = GeyserImpl.load(PlatformType.BUNGEECORD, this);
|
||||
this.geyserInjector = new GeyserBungeeInjector(this);
|
||||
|
||||
// Registration of listeners occurs only once
|
||||
this.getProxy().getPluginManager().registerListener(this, new GeyserBungeeUpdateListener());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
if (geyser == null) {
|
||||
return; // Config did not load properly!
|
||||
}
|
||||
|
||||
// After Geyser initialize for parity with other platforms.
|
||||
var sourceConverter = new CommandSourceConverter<>(
|
||||
CommandSender.class,
|
||||
id -> getProxy().getPlayer(id),
|
||||
() -> getProxy().getConsole(),
|
||||
BungeeCommandSource::new
|
||||
);
|
||||
CommandManager<GeyserCommandSource> cloud = new BungeeCommandManager<>(
|
||||
this,
|
||||
ExecutionCoordinator.simpleCoordinator(),
|
||||
sourceConverter
|
||||
);
|
||||
this.commandRegistry = new CommandRegistry(geyser, cloud, false); // applying root permission would be a breaking change because we can't register permission defaults
|
||||
|
||||
// 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
|
||||
@@ -143,11 +178,6 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
||||
}
|
||||
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
|
||||
@@ -182,16 +212,6 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
||||
}
|
||||
|
||||
this.geyserInjector.initializeLocalChannel(this);
|
||||
|
||||
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();
|
||||
if (commands.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.getProxy().getPluginManager().registerCommand(this, new GeyserBungeeCommandExecutor(entry.getKey().description().id(), this.geyser, commands));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -227,8 +247,8 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeyserCommandManager getGeyserCommandManager() {
|
||||
return this.geyserCommandManager;
|
||||
public CommandRegistry getCommandRegistry() {
|
||||
return this.commandRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -251,6 +271,11 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
||||
return Paths.get(getProxy().getName().equals("BungeeCord") ? "proxy.log.0" : "logs/latest.log");
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull String getServerPlatform() {
|
||||
return getProxy().getName();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public SocketAddress getSocketAddress() {
|
||||
@@ -293,7 +318,7 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
||||
"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);
|
||||
geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
|
||||
ex.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -29,8 +29,8 @@ import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.event.PostLoginEvent;
|
||||
import net.md_5.bungee.api.plugin.Listener;
|
||||
import net.md_5.bungee.event.EventHandler;
|
||||
import org.geysermc.geyser.Constants;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.Permissions;
|
||||
import org.geysermc.geyser.platform.bungeecord.command.BungeeCommandSource;
|
||||
import org.geysermc.geyser.util.VersionCheckUtils;
|
||||
|
||||
@@ -40,7 +40,7 @@ public final class GeyserBungeeUpdateListener implements Listener {
|
||||
public void onPlayerJoin(final PostLoginEvent event) {
|
||||
if (GeyserImpl.getInstance().getConfig().isNotifyOnNewBedrockUpdate()) {
|
||||
final ProxiedPlayer player = event.getPlayer();
|
||||
if (player.hasPermission(Constants.UPDATE_PERMISSION)) {
|
||||
if (player.hasPermission(Permissions.CHECK_UPDATE)) {
|
||||
VersionCheckUtils.checkForGeyserUpdate(() -> new BungeeCommandSource(player));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,19 +27,22 @@ package org.geysermc.geyser.platform.bungeecord.command;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
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.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.UUID;
|
||||
|
||||
public class BungeeCommandSource implements GeyserCommandSource {
|
||||
|
||||
private final net.md_5.bungee.api.CommandSender handle;
|
||||
private final CommandSender handle;
|
||||
|
||||
public BungeeCommandSource(net.md_5.bungee.api.CommandSender handle) {
|
||||
public BungeeCommandSource(CommandSender handle) {
|
||||
this.handle = handle;
|
||||
// Ensure even Java players' languages are loaded
|
||||
GeyserLocale.loadGeyserLocale(this.locale());
|
||||
@@ -72,12 +75,20 @@ public class BungeeCommandSource implements GeyserCommandSource {
|
||||
return !(handle instanceof ProxiedPlayer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable UUID playerUuid() {
|
||||
if (handle instanceof ProxiedPlayer player) {
|
||||
return player.getUniqueId();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String locale() {
|
||||
if (handle instanceof ProxiedPlayer player) {
|
||||
Locale locale = player.getLocale();
|
||||
if (locale != null) {
|
||||
// Locale can be null early on in the conneciton
|
||||
// Locale can be null early on in the connection
|
||||
return GeyserLocale.formatLocale(locale.getLanguage() + "_" + locale.getCountry());
|
||||
}
|
||||
}
|
||||
@@ -86,6 +97,12 @@ public class BungeeCommandSource implements GeyserCommandSource {
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return handle.hasPermission(permission);
|
||||
// Handle blank permissions ourselves, as bungeecord only handles empty ones
|
||||
return permission.isBlank() || handle.hasPermission(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object handle() {
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,89 +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.bungeecord.command;
|
||||
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.plugin.Command;
|
||||
import net.md_5.bungee.api.plugin.TabExecutor;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.command.GeyserCommand;
|
||||
import org.geysermc.geyser.command.GeyserCommandExecutor;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
public class GeyserBungeeCommandExecutor extends Command implements TabExecutor {
|
||||
private final GeyserCommandExecutor commandExecutor;
|
||||
|
||||
public GeyserBungeeCommandExecutor(String name, GeyserImpl geyser, Map<String, org.geysermc.geyser.api.command.Command> commands) {
|
||||
super(name);
|
||||
|
||||
this.commandExecutor = new GeyserCommandExecutor(geyser, commands);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, String[] args) {
|
||||
BungeeCommandSource commandSender = new BungeeCommandSource(sender);
|
||||
GeyserSession session = this.commandExecutor.getGeyserSession(commandSender);
|
||||
|
||||
if (args.length > 0) {
|
||||
GeyserCommand command = this.commandExecutor.getCommand(args[0]);
|
||||
if (command != null) {
|
||||
if (!sender.hasPermission(command.permission())) {
|
||||
String message = GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", commandSender.locale());
|
||||
|
||||
commandSender.sendMessage(ChatColor.RED + message);
|
||||
return;
|
||||
}
|
||||
if (command.isBedrockOnly() && session == null) {
|
||||
String message = GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.bedrock_only", commandSender.locale());
|
||||
|
||||
commandSender.sendMessage(ChatColor.RED + message);
|
||||
return;
|
||||
}
|
||||
command.execute(session, commandSender, args.length > 1 ? Arrays.copyOfRange(args, 1, args.length) : new String[0]);
|
||||
} else {
|
||||
String message = GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.not_found", commandSender.locale());
|
||||
commandSender.sendMessage(ChatColor.RED + message);
|
||||
}
|
||||
} else {
|
||||
this.commandExecutor.getCommand("help").execute(session, commandSender, new String[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<String> onTabComplete(CommandSender sender, String[] args) {
|
||||
if (args.length == 1) {
|
||||
return commandExecutor.tabComplete(new BungeeCommandSource(sender));
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,7 @@
|
||||
plugins {
|
||||
id("geyser.modded-conventions")
|
||||
}
|
||||
|
||||
architectury {
|
||||
common("neoforge", "fabric")
|
||||
}
|
||||
@@ -6,9 +10,17 @@ loom {
|
||||
mixin.defaultRefmapName.set("geyser-refmap.json")
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
// We don't need these
|
||||
tasks.named("remapModrinthJar").configure {
|
||||
enabled = false
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(projects.core)
|
||||
compileOnly(libs.mixin)
|
||||
compileOnly(libs.mixinextras)
|
||||
|
||||
// Only here to suppress "unknown enum constant EnvType.CLIENT" warnings. DO NOT USE!
|
||||
compileOnly(libs.fabric.loader)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
plugins {
|
||||
application
|
||||
id("geyser.modded-conventions")
|
||||
id("geyser.modrinth-uploading-conventions")
|
||||
}
|
||||
|
||||
architectury {
|
||||
@@ -7,56 +8,41 @@ architectury {
|
||||
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 }
|
||||
shadowBundle(project(path = ":mod", configuration = "transformProductionFabric"))
|
||||
shadowBundle(projects.core)
|
||||
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 }
|
||||
shadowBundle(libs.protocol.connection)
|
||||
shadowBundle(libs.protocol.common)
|
||||
shadowBundle(libs.protocol.codec)
|
||||
shadowBundle(libs.raknet)
|
||||
shadowBundle(libs.mcprotocollib)
|
||||
|
||||
// Since we also relocate cloudburst protocol: shade erosion common
|
||||
shadow(libs.erosion.common) { isTransitive = false }
|
||||
shadowBundle(libs.erosion.common)
|
||||
|
||||
// Permissions
|
||||
modImplementation(libs.fabric.permissions)
|
||||
include(libs.fabric.permissions)
|
||||
// Let's shade in our own api/common module
|
||||
shadowBundle(projects.api)
|
||||
shadowBundle(projects.common)
|
||||
|
||||
modImplementation(libs.cloud.fabric)
|
||||
include(libs.cloud.fabric)
|
||||
include(libs.fabric.permissions.api)
|
||||
}
|
||||
|
||||
application {
|
||||
mainClass.set("org.geysermc.geyser.platform.fabric.GeyserFabricMain")
|
||||
tasks.withType<Jar> {
|
||||
manifest.attributes["Main-Class"] = "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 {
|
||||
@@ -70,6 +56,7 @@ tasks {
|
||||
|
||||
modrinth {
|
||||
loaders.add("fabric")
|
||||
uploadFile.set(tasks.getByPath("remapModrinthJar"))
|
||||
dependencies {
|
||||
required.project("fabric-api")
|
||||
}
|
||||
|
||||
@@ -25,17 +25,24 @@
|
||||
|
||||
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.client.event.lifecycle.v1.ClientLifecycleEvents;
|
||||
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 net.minecraft.server.level.ServerPlayer;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.command.CommandRegistry;
|
||||
import org.geysermc.geyser.command.CommandSourceConverter;
|
||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||
import org.geysermc.geyser.platform.mod.GeyserModBootstrap;
|
||||
import org.geysermc.geyser.platform.mod.GeyserModUpdateListener;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.platform.mod.command.ModCommandSource;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.incendo.cloud.execution.ExecutionCoordinator;
|
||||
import org.incendo.cloud.fabric.FabricServerCommandManager;
|
||||
|
||||
public class GeyserFabricBootstrap extends GeyserModBootstrap implements ModInitializer {
|
||||
|
||||
@@ -45,28 +52,47 @@ public class GeyserFabricBootstrap extends GeyserModBootstrap implements ModInit
|
||||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
if (FabricLoader.getInstance().getEnvironmentType() == EnvType.SERVER) {
|
||||
if (isServer()) {
|
||||
// Set as an event, so we can get the proper IP and port if needed
|
||||
ServerLifecycleEvents.SERVER_STARTED.register((server) -> {
|
||||
this.setServer(server);
|
||||
onGeyserEnable();
|
||||
});
|
||||
} else {
|
||||
ClientLifecycleEvents.CLIENT_STOPPING.register(($)-> {
|
||||
onGeyserShutdown();
|
||||
});
|
||||
}
|
||||
|
||||
// These are only registered once
|
||||
ServerLifecycleEvents.SERVER_STOPPING.register((server) -> onGeyserShutdown());
|
||||
ServerLifecycleEvents.SERVER_STOPPING.register((server) -> {
|
||||
if (isServer()) {
|
||||
onGeyserShutdown();
|
||||
} else {
|
||||
onGeyserDisable();
|
||||
}
|
||||
});
|
||||
|
||||
ServerPlayConnectionEvents.JOIN.register((handler, $, $$) -> GeyserModUpdateListener.onPlayReady(handler.getPlayer()));
|
||||
|
||||
this.onGeyserInitialize();
|
||||
|
||||
var sourceConverter = CommandSourceConverter.layered(
|
||||
CommandSourceStack.class,
|
||||
id -> getServer().getPlayerList().getPlayer(id),
|
||||
ServerPlayer::createCommandSourceStack,
|
||||
() -> getServer().createCommandSourceStack(), // NPE if method reference is used, since server is not available yet
|
||||
ModCommandSource::new
|
||||
);
|
||||
CommandManager<GeyserCommandSource> cloud = new FabricServerCommandManager<>(
|
||||
ExecutionCoordinator.simpleCoordinator(),
|
||||
sourceConverter
|
||||
);
|
||||
this.setCommandRegistry(new CommandRegistry(GeyserImpl.getInstance(), cloud, false)); // applying root permission would be a breaking change because we can't register permission defaults
|
||||
}
|
||||
|
||||
@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);
|
||||
public boolean isServer() {
|
||||
return FabricLoader.getInstance().getEnvironmentType().equals(EnvType.SERVER);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,9 +23,8 @@
|
||||
"geyser.mixins.json"
|
||||
],
|
||||
"depends": {
|
||||
"fabricloader": ">=0.15.2",
|
||||
"fabric": "*",
|
||||
"minecraft": ">=1.20.4",
|
||||
"fabric-permissions-api-v0": "*"
|
||||
"fabricloader": ">=0.17.2",
|
||||
"fabric-api": "*",
|
||||
"minecraft": ">=1.21.9"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
plugins {
|
||||
application
|
||||
id("geyser.modded-conventions")
|
||||
id("geyser.modrinth-uploading-conventions")
|
||||
}
|
||||
|
||||
// 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")
|
||||
// This is provided by "org.cloudburstmc.math.mutable" too, so yeet.
|
||||
// NeoForge's class loader is *really* annoying.
|
||||
provided("org.cloudburstmc.math", "api")
|
||||
provided("com.google.errorprone", "error_prone_annotations")
|
||||
|
||||
// Jackson shipped by Minecraft is too old, so we shade & relocate our newer version
|
||||
relocate("com.fasterxml.jackson")
|
||||
|
||||
dependencies {
|
||||
// See https://github.com/google/guava/issues/6618
|
||||
@@ -24,16 +27,30 @@ dependencies {
|
||||
neoForge(libs.neoforge.minecraft)
|
||||
|
||||
api(project(":mod", configuration = "namedElements"))
|
||||
shadow(project(path = ":mod", configuration = "transformProductionNeoForge")) {
|
||||
isTransitive = false
|
||||
}
|
||||
shadow(project(path = ":core")) { isTransitive = false }
|
||||
shadowBundle(project(path = ":mod", configuration = "transformProductionNeoForge"))
|
||||
shadowBundle(projects.core)
|
||||
|
||||
// Minecraft (1.21.2+) includes jackson. But an old version!
|
||||
shadowBundle(libs.jackson.core)
|
||||
shadowBundle(libs.jackson.databind)
|
||||
shadowBundle(libs.jackson.dataformat.yaml)
|
||||
shadowBundle(libs.jackson.annotations)
|
||||
|
||||
// Let's shade in our own api
|
||||
shadowBundle(projects.api)
|
||||
|
||||
// cannot be shaded, since neoforge will complain if floodgate-neoforge tries to provide this
|
||||
include(projects.common)
|
||||
|
||||
// Include all transitive deps of core via JiJ
|
||||
includeTransitive(projects.core)
|
||||
|
||||
modImplementation(libs.cloud.neoforge)
|
||||
include(libs.cloud.neoforge)
|
||||
}
|
||||
|
||||
application {
|
||||
mainClass.set("org.geysermc.geyser.platform.forge.GeyserNeoForgeMain")
|
||||
tasks.withType<Jar> {
|
||||
manifest.attributes["Main-Class"] = "org.geysermc.geyser.platform.neoforge.GeyserNeoForgeMain"
|
||||
}
|
||||
|
||||
tasks {
|
||||
@@ -44,8 +61,14 @@ tasks {
|
||||
remapModrinthJar {
|
||||
archiveBaseName.set("geyser-neoforge")
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
// Without this, jackson's service files are not relocated
|
||||
mergeServiceFiles()
|
||||
}
|
||||
}
|
||||
|
||||
modrinth {
|
||||
loaders.add("neoforge")
|
||||
uploadFile.set(tasks.getByPath("remapModrinthJar"))
|
||||
}
|
||||
@@ -26,36 +26,64 @@
|
||||
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.minecraft.server.level.ServerPlayer;
|
||||
import net.neoforged.bus.api.EventPriority;
|
||||
import net.neoforged.fml.ModContainer;
|
||||
import net.neoforged.fml.common.Mod;
|
||||
import net.neoforged.fml.loading.FMLLoader;
|
||||
import net.neoforged.neoforge.common.NeoForge;
|
||||
import net.neoforged.neoforge.event.GameShuttingDownEvent;
|
||||
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 net.neoforged.neoforge.server.permission.events.PermissionGatherEvent;
|
||||
import org.geysermc.geyser.api.event.lifecycle.GeyserRegisterPermissionsEvent;
|
||||
import org.geysermc.geyser.command.CommandSourceConverter;
|
||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||
import org.geysermc.geyser.platform.mod.GeyserModBootstrap;
|
||||
import org.geysermc.geyser.platform.mod.GeyserModUpdateListener;
|
||||
import org.geysermc.geyser.platform.mod.command.ModCommandSource;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.incendo.cloud.execution.ExecutionCoordinator;
|
||||
import org.incendo.cloud.neoforge.NeoForgeServerCommandManager;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@Mod(ModConstants.MOD_ID)
|
||||
public class GeyserNeoForgeBootstrap extends GeyserModBootstrap {
|
||||
|
||||
private final GeyserNeoForgePermissionHandler permissionHandler = new GeyserNeoForgePermissionHandler();
|
||||
public GeyserNeoForgeBootstrap(ModContainer container) {
|
||||
super(new GeyserNeoForgePlatform(container));
|
||||
|
||||
public GeyserNeoForgeBootstrap() {
|
||||
super(new GeyserNeoForgePlatform());
|
||||
|
||||
if (FMLLoader.getDist() == Dist.DEDICATED_SERVER) {
|
||||
if (isServer()) {
|
||||
// Set as an event so we can get the proper IP and port if needed
|
||||
NeoForge.EVENT_BUS.addListener(this::onServerStarted);
|
||||
} else {
|
||||
NeoForge.EVENT_BUS.addListener(this::onClientStopping);
|
||||
}
|
||||
|
||||
NeoForge.EVENT_BUS.addListener(this::onServerStopping);
|
||||
NeoForge.EVENT_BUS.addListener(this::onPlayerJoin);
|
||||
NeoForge.EVENT_BUS.addListener(this.permissionHandler::onPermissionGather);
|
||||
|
||||
NeoForge.EVENT_BUS.addListener(EventPriority.HIGHEST, this::onPermissionGather);
|
||||
|
||||
this.onGeyserInitialize();
|
||||
|
||||
var sourceConverter = CommandSourceConverter.layered(
|
||||
CommandSourceStack.class,
|
||||
id -> getServer().getPlayerList().getPlayer(id),
|
||||
ServerPlayer::createCommandSourceStack,
|
||||
() -> getServer().createCommandSourceStack(),
|
||||
ModCommandSource::new
|
||||
);
|
||||
CommandManager<GeyserCommandSource> cloud = new NeoForgeServerCommandManager<>(
|
||||
ExecutionCoordinator.simpleCoordinator(),
|
||||
sourceConverter
|
||||
);
|
||||
GeyserNeoForgeCommandRegistry registry = new GeyserNeoForgeCommandRegistry(getGeyser(), cloud);
|
||||
this.setCommandRegistry(registry);
|
||||
// An auxiliary listener for registering undefined permissions belonging to commands. See javadocs for more info.
|
||||
NeoForge.EVENT_BUS.addListener(EventPriority.LOWEST, registry::onPermissionGatherForUndefined);
|
||||
}
|
||||
|
||||
private void onServerStarted(ServerStartedEvent event) {
|
||||
@@ -64,20 +92,39 @@ public class GeyserNeoForgeBootstrap extends GeyserModBootstrap {
|
||||
}
|
||||
|
||||
private void onServerStopping(ServerStoppingEvent event) {
|
||||
if (isServer()) {
|
||||
this.onGeyserShutdown();
|
||||
} else {
|
||||
this.onGeyserDisable();
|
||||
}
|
||||
}
|
||||
|
||||
private void onClientStopping(GameShuttingDownEvent ignored) {
|
||||
this.onGeyserShutdown();
|
||||
}
|
||||
|
||||
private void onPlayerJoin(PlayerEvent.PlayerLoggedInEvent event) {
|
||||
GeyserModUpdateListener.onPlayReady(event.getEntity());
|
||||
if (event.getEntity() instanceof ServerPlayer player) {
|
||||
GeyserModUpdateListener.onPlayReady(player);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(@NonNull Player source, @NonNull String permissionNode) {
|
||||
return this.permissionHandler.hasPermission(source, permissionNode);
|
||||
public boolean isServer() {
|
||||
return FMLLoader.getCurrent().getDist().isDedicatedServer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(@NonNull CommandSourceStack source, @NonNull String permissionNode, int permissionLevel) {
|
||||
return this.permissionHandler.hasPermission(source, permissionNode, permissionLevel);
|
||||
private void onPermissionGather(PermissionGatherEvent.Nodes event) {
|
||||
getGeyser().eventBus().fire(
|
||||
(GeyserRegisterPermissionsEvent) (permission, defaultValue) -> {
|
||||
Objects.requireNonNull(permission, "permission");
|
||||
Objects.requireNonNull(defaultValue, "permission default for " + permission);
|
||||
|
||||
if (permission.isBlank()) {
|
||||
return;
|
||||
}
|
||||
PermissionUtils.register(permission, defaultValue, event);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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.platform.neoforge;
|
||||
|
||||
import net.neoforged.neoforge.server.permission.events.PermissionGatherEvent;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.event.lifecycle.GeyserRegisterPermissionsEvent;
|
||||
import org.geysermc.geyser.api.util.TriState;
|
||||
import org.geysermc.geyser.command.CommandRegistry;
|
||||
import org.geysermc.geyser.command.GeyserCommand;
|
||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.incendo.cloud.neoforge.PermissionNotRegisteredException;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class GeyserNeoForgeCommandRegistry extends CommandRegistry {
|
||||
|
||||
/**
|
||||
* Permissions with an undefined permission default. Use Set to not register the same fallback more than once.
|
||||
* NeoForge requires that all permissions are registered, and cloud-neoforge follows that.
|
||||
* This is unlike most platforms, on which we wouldn't register a permission if no default was provided.
|
||||
*/
|
||||
private final Set<String> undefinedPermissions = new HashSet<>();
|
||||
|
||||
public GeyserNeoForgeCommandRegistry(GeyserImpl geyser, CommandManager<GeyserCommandSource> cloud) {
|
||||
super(geyser, cloud);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void register(GeyserCommand command, Map<String, GeyserCommand> commands) {
|
||||
super.register(command, commands);
|
||||
|
||||
// FIRST STAGE: Collect all permissions that may have undefined defaults.
|
||||
if (!command.permission().isBlank() && command.permissionDefault() == null) {
|
||||
// Permission requirement exists but no default value specified.
|
||||
undefinedPermissions.add(command.permission());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRegisterPermissions(GeyserRegisterPermissionsEvent event) {
|
||||
super.onRegisterPermissions(event);
|
||||
|
||||
// SECOND STAGE
|
||||
// Now that we are aware of all commands, we can eliminate some incorrect assumptions.
|
||||
// Example: two commands may have the same permission, but only of them defines a permission default.
|
||||
undefinedPermissions.removeAll(permissionDefaults.keySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers permissions with possibly undefined defaults.
|
||||
* Should be subscribed late to allow extensions and mods to register a desired permission default first.
|
||||
*/
|
||||
void onPermissionGatherForUndefined(PermissionGatherEvent.Nodes event) {
|
||||
// THIRD STAGE
|
||||
for (String permission : undefinedPermissions) {
|
||||
if (PermissionUtils.register(permission, TriState.NOT_SET, event)) {
|
||||
// The permission was not already registered
|
||||
geyser.getLogger().debug("Registered permission " + permission + " with fallback default value of NOT_SET");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(GeyserCommandSource source, String permission) {
|
||||
// NeoForgeServerCommandManager will throw this exception if the permission is not registered to the server.
|
||||
// We can't realistically ensure that every permission is registered (calls by API users), so we catch this.
|
||||
// This works for our calls, but not for cloud's internal usage. For that case, see above.
|
||||
try {
|
||||
return super.hasPermission(source, permission);
|
||||
} catch (PermissionNotRegisteredException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,10 +54,10 @@ public class GeyserNeoForgeDumpInfo extends BootstrapDumpInfo {
|
||||
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.platformName = server.getServerModName();
|
||||
this.platformVersion = FMLLoader.getCurrent().getVersionInfo().neoForgeVersion();
|
||||
this.minecraftVersion = FMLLoader.getCurrent().getVersionInfo().mcVersion();
|
||||
this.dist = FMLLoader.getCurrent().getDist();
|
||||
this.serverIP = server.getLocalIp() == null ? "unknown" : server.getLocalIp();
|
||||
this.serverPort = server.getPort();
|
||||
this.onlineMode = server.usesAuthentication();
|
||||
|
||||
@@ -1,149 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,20 +26,28 @@
|
||||
package org.geysermc.geyser.platform.neoforge;
|
||||
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.neoforged.fml.ModContainer;
|
||||
import net.neoforged.fml.ModList;
|
||||
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.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class GeyserNeoForgePlatform implements GeyserModPlatform {
|
||||
|
||||
private final ModContainer container;
|
||||
|
||||
public GeyserNeoForgePlatform(ModContainer container) {
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull PlatformType platformType() {
|
||||
return PlatformType.NEOFORGE;
|
||||
@@ -62,11 +70,20 @@ public class GeyserNeoForgePlatform implements GeyserModPlatform {
|
||||
|
||||
@Override
|
||||
public boolean testFloodgatePluginPresent(@NonNull GeyserModBootstrap bootstrap) {
|
||||
return false; // No Floodgate mod for NeoForge yet
|
||||
if (ModList.get().isLoaded("floodgate")) {
|
||||
Path floodgateDataFolder = FMLPaths.CONFIGDIR.get().resolve("floodgate");
|
||||
bootstrap.getGeyserConfig().loadFloodgate(bootstrap, floodgateDataFolder);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable InputStream resolveResource(@NonNull String resource) {
|
||||
return GeyserBootstrap.class.getClassLoader().getResourceAsStream(resource);
|
||||
try {
|
||||
return container.getModInfo().getOwningFile().getFile().getContents().openFile(resource);
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) 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.platform.neoforge;
|
||||
|
||||
import net.neoforged.neoforge.server.permission.events.PermissionGatherEvent;
|
||||
import net.neoforged.neoforge.server.permission.nodes.PermissionNode;
|
||||
import net.neoforged.neoforge.server.permission.nodes.PermissionTypes;
|
||||
import org.geysermc.geyser.api.event.lifecycle.GeyserRegisterPermissionsEvent;
|
||||
import org.geysermc.geyser.api.util.TriState;
|
||||
import org.geysermc.geyser.platform.neoforge.mixin.PermissionNodeMixin;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Common logic for handling the more complicated way we have to register permission on NeoForge
|
||||
*/
|
||||
public class PermissionUtils {
|
||||
|
||||
private PermissionUtils() {
|
||||
//no
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the given permission and its default value to the event. If the permission has the same name as one
|
||||
* that has already been registered to the event, it will not be registered. In other words, it will not override.
|
||||
*
|
||||
* @param permission the permission to register
|
||||
* @param permissionDefault the permission's default value. See {@link GeyserRegisterPermissionsEvent#register(String, TriState)} for TriState meanings.
|
||||
* @param event the registration event
|
||||
* @return true if the permission was registered
|
||||
*/
|
||||
public static boolean register(String permission, TriState permissionDefault, PermissionGatherEvent.Nodes event) {
|
||||
// NeoForge likes to crash if you try and register a duplicate node
|
||||
if (event.getNodes().stream().noneMatch(n -> n.getNodeName().equals(permission))) {
|
||||
PermissionNode<Boolean> node = createNode(permission, permissionDefault);
|
||||
event.addNodes(node);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static PermissionNode<Boolean> createNode(String node, TriState permissionDefault) {
|
||||
return PermissionNodeMixin.geyser$construct(
|
||||
node,
|
||||
PermissionTypes.BOOLEAN,
|
||||
(player, playerUUID, context) -> switch (permissionDefault) {
|
||||
case TRUE -> true;
|
||||
case FALSE -> false;
|
||||
case NOT_SET -> {
|
||||
if (player != null) {
|
||||
yield player.createCommandSourceStack().hasPermission(Objects.requireNonNull(player.level()).getServer().operatorUserPermissionLevel());
|
||||
}
|
||||
yield false; // NeoForge javadocs say player is null in the case of an offline player.
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.platform.neoforge.mixin;
|
||||
|
||||
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 org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
|
||||
@Mixin(value = PermissionNode.class, remap = false) // this is API - do not remap
|
||||
public interface PermissionNodeMixin {
|
||||
|
||||
/**
|
||||
* Invokes the matching private constructor in {@link PermissionNode}.
|
||||
* <p>
|
||||
* The typical constructors in PermissionNode require a mod id, which means our permission nodes
|
||||
* would end up becoming {@code geyser_neoforge.<node>} instead of just {@code <node>}.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes") // the varargs
|
||||
@Invoker("<init>")
|
||||
static <T> PermissionNode<T> geyser$construct(String nodeName, PermissionType<T> type, PermissionNode.PermissionResolver<T> defaultResolver, PermissionDynamicContextKey... dynamics) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
@@ -11,15 +11,17 @@ authors="GeyserMC"
|
||||
description="${description}"
|
||||
[[mixins]]
|
||||
config = "geyser.mixins.json"
|
||||
[[mixins]]
|
||||
config = "geyser_neoforge.mixins.json"
|
||||
[[dependencies.geyser_neoforge]]
|
||||
modId="neoforge"
|
||||
type="required"
|
||||
versionRange="[20.4.48-beta,)"
|
||||
versionRange="[21.9.14-beta,)"
|
||||
ordering="NONE"
|
||||
side="BOTH"
|
||||
[[dependencies.geyser_neoforge]]
|
||||
modId="minecraft"
|
||||
type="required"
|
||||
versionRange="[1.20,1.21)"
|
||||
versionRange="[1.21.9,)"
|
||||
ordering="NONE"
|
||||
side="BOTH"
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"required": true,
|
||||
"minVersion": "0.8",
|
||||
"package": "org.geysermc.geyser.platform.neoforge.mixin",
|
||||
"compatibilityLevel": "JAVA_17",
|
||||
"mixins": [
|
||||
"PermissionNodeMixin"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
}
|
||||
}
|
||||
@@ -25,31 +25,21 @@
|
||||
|
||||
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.command.CommandRegistry;
|
||||
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;
|
||||
@@ -58,8 +48,8 @@ import org.geysermc.geyser.util.FileUtils;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.SocketAddress;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@@ -70,16 +60,18 @@ public abstract class GeyserModBootstrap implements GeyserBootstrap {
|
||||
|
||||
private final GeyserModPlatform platform;
|
||||
|
||||
@Getter
|
||||
private GeyserImpl geyser;
|
||||
private Path dataFolder;
|
||||
|
||||
@Setter
|
||||
@Setter @Getter
|
||||
private MinecraftServer server;
|
||||
|
||||
private GeyserCommandManager geyserCommandManager;
|
||||
@Setter
|
||||
private CommandRegistry commandRegistry;
|
||||
private GeyserModConfiguration geyserConfig;
|
||||
private GeyserModInjector geyserInjector;
|
||||
private GeyserModLogger geyserLogger;
|
||||
private final GeyserModLogger geyserLogger = new GeyserModLogger();
|
||||
private IGeyserPingPassthrough geyserPingPassthrough;
|
||||
private WorldManager geyserWorldManager;
|
||||
|
||||
@@ -91,16 +83,17 @@ public abstract class GeyserModBootstrap implements GeyserBootstrap {
|
||||
if (!loadConfig()) {
|
||||
return;
|
||||
}
|
||||
this.geyserLogger = new GeyserModLogger(geyserConfig.isDebugMode());
|
||||
this.geyserLogger.setDebug(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() {
|
||||
// "Disabling" a mod isn't possible; so if we fail to initialize we need to manually stop here
|
||||
if (geyser == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (GeyserImpl.getInstance().isReloading()) {
|
||||
if (!loadConfig()) {
|
||||
return;
|
||||
@@ -127,50 +120,8 @@ public abstract class GeyserModBootstrap implements GeyserBootstrap {
|
||||
// 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);
|
||||
if (isServer()) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,8 +155,8 @@ public abstract class GeyserModBootstrap implements GeyserBootstrap {
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeyserCommandManager getGeyserCommandManager() {
|
||||
return geyserCommandManager;
|
||||
public CommandRegistry getCommandRegistry() {
|
||||
return commandRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -233,6 +184,7 @@ public abstract class GeyserModBootstrap implements GeyserBootstrap {
|
||||
return this.server.getServerVersion();
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions") // Certain IDEA installations think that ip cannot be null
|
||||
@NonNull
|
||||
@Override
|
||||
public String getServerBindAddress() {
|
||||
@@ -241,10 +193,22 @@ public abstract class GeyserModBootstrap implements GeyserBootstrap {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getServerPort() {
|
||||
return ((GeyserServerPortGetter) server).geyser$getServerPort();
|
||||
public SocketAddress getSocketAddress() {
|
||||
return this.geyserInjector.getServerSocketAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getServerPort() {
|
||||
if (isServer()) {
|
||||
return ((GeyserServerPortGetter) server).geyser$getServerPort();
|
||||
} else {
|
||||
// Set in the IntegratedServerMixin
|
||||
return geyserConfig.getRemote().port();
|
||||
}
|
||||
}
|
||||
|
||||
public abstract boolean isServer();
|
||||
|
||||
@Override
|
||||
public boolean testFloodgatePluginPresent() {
|
||||
return this.platform.testFloodgatePluginPresent(this);
|
||||
@@ -256,10 +220,6 @@ public abstract class GeyserModBootstrap implements GeyserBootstrap {
|
||||
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 {
|
||||
@@ -273,9 +233,14 @@ public abstract class GeyserModBootstrap implements GeyserBootstrap {
|
||||
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserModConfiguration.class);
|
||||
return true;
|
||||
} catch (IOException ex) {
|
||||
LogManager.getLogger("geyser").error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
|
||||
geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
|
||||
ex.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull String getServerPlatform() {
|
||||
return server.getServerModName();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@ 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;
|
||||
import net.minecraft.network.protocol.login.ClientboundLoginFinishedPacket;
|
||||
|
||||
/**
|
||||
* Disables the compression packet (and the compression handlers from being added to the pipeline) for Geyser clients
|
||||
@@ -45,7 +45,7 @@ public class GeyserModCompressionDisabler extends ChannelOutboundHandlerAdapter
|
||||
Class<?> msgClass = msg.getClass();
|
||||
// Don't let any compression packet get through
|
||||
if (!ClientboundLoginCompressionPacket.class.isAssignableFrom(msgClass)) {
|
||||
if (ClientboundGameProfilePacket.class.isAssignableFrom(msgClass)) {
|
||||
if (ClientboundLoginFinishedPacket.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);
|
||||
|
||||
@@ -93,8 +93,11 @@ public class GeyserModInjector extends GeyserInjector {
|
||||
protected void initChannel(@NonNull Channel ch) throws Exception {
|
||||
initChannel.invoke(childHandler, ch);
|
||||
|
||||
int index = ch.pipeline().names().indexOf("encoder");
|
||||
String baseName = index != -1 ? "encoder" : "outbound_config";
|
||||
|
||||
if (bootstrap.getGeyserConfig().isDisableCompression()) {
|
||||
ch.pipeline().addAfter("encoder", "geyser-compression-disabler", new GeyserModCompressionDisabler());
|
||||
ch.pipeline().addAfter(baseName, "geyser-compression-disabler", new GeyserModCompressionDisabler());
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -37,10 +37,6 @@ public class GeyserModLogger implements GeyserLogger {
|
||||
|
||||
private boolean debug;
|
||||
|
||||
public GeyserModLogger(boolean isDebug) {
|
||||
debug = isDebug;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void severe(String message) {
|
||||
logger.fatal(message);
|
||||
@@ -88,6 +84,13 @@ public class GeyserModLogger implements GeyserLogger {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String message, Object... arguments) {
|
||||
if (debug) {
|
||||
logger.info(String.format(message, arguments));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDebug(boolean debug) {
|
||||
this.debug = debug;
|
||||
|
||||
@@ -25,17 +25,22 @@
|
||||
|
||||
package org.geysermc.geyser.platform.mod;
|
||||
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import org.geysermc.geyser.Constants;
|
||||
import org.geysermc.geyser.platform.mod.command.ModCommandSender;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.Permissions;
|
||||
import org.geysermc.geyser.platform.mod.command.ModCommandSource;
|
||||
import org.geysermc.geyser.util.VersionCheckUtils;
|
||||
|
||||
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));
|
||||
public static void onPlayReady(ServerPlayer player) {
|
||||
// We could just not register the listener, but, this allows config reloading
|
||||
if (GeyserImpl.getInstance().getConfig().isNotifyOnNewBedrockUpdate()) {
|
||||
// Should be creating this in the supplier, but we need it for the permission check.
|
||||
// Not a big deal currently because ModCommandSource doesn't load locale, so don't need to try to wait for it.
|
||||
ModCommandSource source = new ModCommandSource(player.createCommandSourceStack());
|
||||
if (source.hasPermission(Permissions.CHECK_UPDATE)) {
|
||||
VersionCheckUtils.checkForGeyserUpdate(() -> source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user