3
0
Mirror von https://github.com/GeyserMC/Geyser.git synchronisiert 2024-10-01 23:50:11 +02:00

Merge branch 'master' into main

Dieser Commit ist enthalten in:
strom 2024-06-26 23:43:48 +02:00 committet von GitHub
Commit c9b27c3987
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: B5690EEEBB952194
355 geänderte Dateien mit 13644 neuen und 44191 gelöschten Zeilen

14
.editorconfig Normale Datei
Datei anzeigen

@ -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

Datei anzeigen

@ -22,81 +22,26 @@ jobs:
run: | run: |
echo "BUILD_NUMBER=${GITHUB_RUN_NUMBER}" >> $GITHUB_ENV echo "BUILD_NUMBER=${GITHUB_RUN_NUMBER}" >> $GITHUB_ENV
- name: Set up JDK 21 - name: Setup Gradle
# See https://github.com/actions/setup-java/commits uses: GeyserMC/actions/setup-gradle-composite@master
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0
with: with:
java-version: 21 checkout_repository: ${{ inputs.repository }}
distribution: temurin checkout_ref: ${{ inputs.ref }}
setup-java_java-version: 21
- name: Checkout repository and submodules setup-gradle_cache-read-only: true
# See https://github.com/actions/checkout/commits
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
repository: ${{ inputs.repository }}
ref: ${{ inputs.ref }}
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 - name: Build Geyser
# See https://github.com/gradle/actions/commits run: ./gradlew build
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0
with:
arguments: build
build-root-directory: geyser
cache-read-only: true
- name: Archive artifacts (Geyser Fabric) - name: Archive Artifacts
# See https://github.com/actions/upload-artifact/commits uses: GeyserMC/actions/upload-multi-artifact@master
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
if: success() if: success()
with: with:
name: Geyser Fabric artifacts: |
path: geyser/bootstrap/mod/fabric/build/libs/Geyser-Fabric.jar bootstrap/mod/fabric/build/libs/Geyser-Fabric.jar
if-no-files-found: error bootstrap/mod/neoforge/build/libs/Geyser-NeoForge.jar
- name: Archive artifacts (Geyser NeoForge) bootstrap/standalone/build/libs/Geyser-Standalone.jar
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 bootstrap/spigot/build/libs/Geyser-Spigot.jar
if: success() bootstrap/bungeecord/build/libs/Geyser-BungeeCord.jar
with: bootstrap/velocity/build/libs/Geyser-Velocity.jar
name: Geyser NeoForge bootstrap/viaproxy/build/libs/Geyser-ViaProxy.jar
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

Datei anzeigen

@ -21,103 +21,55 @@ on:
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
env:
PROJECT: 'geyser'
steps: steps:
- name: Set Build Number - name: Get Release Info
id: release-info
uses: GeyserMC/actions/previous-release@master
with:
data: ${{ vars.RELEASEACTION_PREVRELEASE }}
- name: Setup Gradle
uses: GeyserMC/actions/setup-gradle-composite@master
with:
setup-java_java-version: 21
- name: Build Geyser
run: ./gradlew build
env: env:
BUILD_JSON: ${{ vars.RELEASEACTION_PREVRELEASE }} BUILD_NUMBER: ${{ steps.release-info.outputs.curentRelease }}
run: |
BUILD_NUMBER=$(echo $BUILD_JSON | jq --arg branch "${GITHUB_REF_NAME}" 'if .[$branch] == null then 1 else .[$branch] | .t | tonumber + 1 end // 1')
echo "BUILD_NUMBER=${BUILD_NUMBER:=$GITHUB_RUN_NUMBER}" >> $GITHUB_ENV
- name: Checkout repository and submodules - name: Archive Artifacts
# See https://github.com/actions/checkout/commits uses: GeyserMC/actions/upload-multi-artifact@master
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
submodules: recursive
- name: Validate Gradle Wrapper
# See https://github.com/gradle/wrapper-validation-action/commits
uses: gradle/wrapper-validation-action@699bb18358f12c5b78b37bb0111d3a0e2276e0e2 # v2.1.1
# See https://github.com/actions/setup-java/commits
- uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0
with:
java-version: 21
distribution: temurin
- name: Build
# See https://github.com/gradle/actions/commits
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0
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
if: success() if: success()
with: with:
name: Geyser Fabric artifacts: |
path: bootstrap/mod/fabric/build/libs/Geyser-Fabric.jar bootstrap/mod/fabric/build/libs/Geyser-Fabric.jar
if-no-files-found: error bootstrap/mod/neoforge/build/libs/Geyser-NeoForge.jar
- name: Archive artifacts (Geyser NeoForge) bootstrap/standalone/build/libs/Geyser-Standalone.jar
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 bootstrap/spigot/build/libs/Geyser-Spigot.jar
if: success() bootstrap/bungeecord/build/libs/Geyser-BungeeCord.jar
with: bootstrap/velocity/build/libs/Geyser-Velocity.jar
name: Geyser NeoForge bootstrap/viaproxy/build/libs/Geyser-ViaProxy.jar
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
- name: Publish to Maven Repository - name: Publish to Maven Repository
if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }} if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 run: ./gradlew publish
env: env:
BUILD_NUMBER: ${{ steps.release-info.outputs.curentRelease }}
ORG_GRADLE_PROJECT_geysermcUsername: ${{ vars.DEPLOY_USER }} ORG_GRADLE_PROJECT_geysermcUsername: ${{ vars.DEPLOY_USER }}
ORG_GRADLE_PROJECT_geysermcPassword: ${{ secrets.DEPLOY_PASS }} ORG_GRADLE_PROJECT_geysermcPassword: ${{ secrets.DEPLOY_PASS }}
with:
arguments: publish - 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 - name: Get Release Metadata
if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }} if: ${{ (success() || failure()) && github.repository == 'GeyserMC/Geyser' }}
# See https://github.com/Kas-tle/base-release-action/releases/tag/main-11 uses: GeyserMC/actions/release@master
uses: Kas-tle/base-release-action@b863fa0f89bd15267a96a72efb84aec25f168d4c # main-11 id: metadata
with: with:
appID: ${{ secrets.RELEASE_APP_ID }} appID: ${{ secrets.RELEASE_APP_ID }}
appPrivateKey: ${{ secrets.RELEASE_APP_PK }} appPrivateKey: ${{ secrets.RELEASE_APP_PK }}
@ -131,61 +83,39 @@ jobs:
viaproxy:bootstrap/viaproxy/build/libs/Geyser-ViaProxy.jar viaproxy:bootstrap/viaproxy/build/libs/Geyser-ViaProxy.jar
releaseEnabled: false releaseEnabled: false
saveMetadata: true saveMetadata: true
- name: Update Generated Metadata releaseProject: 'geyser'
if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }} releaseVersion: ${{ steps.get-version.outputs.VERSION }}
run: |
cat metadata.json
echo
mv metadata.json metadata.json.tmp
version=$(cat gradle.properties | grep -o "version=[0-9\\.]*" | cut -d"=" -f2)
jq --arg project "${PROJECT}" --arg version "${version}" '
.
| .changes |= map({"commit", "summary", "message"})
| .downloads |= map_values({"name", "sha256"})
| {$project, "repo", $version, "number": .build, "changes", "downloads"}
' metadata.json.tmp > metadata.json
cat metadata.json
- name: Publish to Downloads API - name: Publish to Downloads API
if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }} if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
shell: bash uses: GeyserMC/actions/upload-release@master
env: with:
DOWNLOADS_USERNAME: ${{ vars.DOWNLOADS_USERNAME }} username: ${{ vars.DOWNLOADS_USERNAME }}
DOWNLOADS_PRIVATE_KEY: ${{ secrets.DOWNLOADS_PRIVATE_KEY }} privateKey: ${{ secrets.DOWNLOADS_PRIVATE_KEY }}
DOWNLOADS_SERVER_IP: ${{ secrets.DOWNLOADS_SERVER_IP }} host: ${{ secrets.DOWNLOADS_SERVER_IP }}
run: | files: |
# Save the private key to a file bootstrap/bungeecord/build/libs/Geyser-BungeeCord.jar
echo "$DOWNLOADS_PRIVATE_KEY" > id_ecdsa bootstrap/mod/fabric/build/libs/Geyser-Fabric.jar
chmod 600 id_ecdsa bootstrap/mod/neoforge/build/libs/Geyser-NeoForge.jar
# Create the build folder bootstrap/spigot/build/libs/Geyser-Spigot.jar
ssh -o StrictHostKeyChecking=no -i id_ecdsa $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP mkdir -p "~/uploads/$PROJECT/$GITHUB_RUN_NUMBER/" bootstrap/standalone/build/libs/Geyser-Standalone.jar
# Copy over artifacts bootstrap/velocity/build/libs/Geyser-Velocity.jar
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/ bootstrap/viaproxy/build/libs/Geyser-ViaProxy.jar
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/ changelog: ${{ steps.metadata.outputs.body }}
# Run the build script
rsync -P -e "ssh -o StrictHostKeyChecking=no -i id_ecdsa" metadata.json $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP:~/uploads/$PROJECT/$GITHUB_RUN_NUMBER/
- name: Publish to Modrinth (Fabric) - name: Publish to Modrinth
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }} if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
env: env:
CHANGELOG: ${{ steps.metadata.outputs.body }}
BUILD_NUMBER: ${{ steps.release-info.outputs.curentRelease }}
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }} MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
with: run: ./gradlew modrinth
arguments: fabric:modrinth
gradle-home-cache-cleanup: true
- name: Publish to Modrinth (NeoForge)
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
env:
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
with:
arguments: neoforge:modrinth
gradle-home-cache-cleanup: true
- name: Notify Discord - name: Notify Discord
if: ${{ (success() || failure()) && github.repository == 'GeyserMC/Geyser' }} if: ${{ (success() || failure()) && github.repository == 'GeyserMC/Geyser' }}
# See https://github.com/Tim203/actions-git-discord-webhook/commits uses: GeyserMC/actions/notify-discord@master
uses: Tim203/actions-git-discord-webhook@70f38ded3aca51635ec978ab4e1a58cd4cd0c2ff
with: with:
webhook_url: ${{ secrets.DISCORD_WEBHOOK }} discordWebhook: ${{ secrets.DISCORD_WEBHOOK }}
status: ${{ job.status }} status: ${{ job.status }}
body: ${{ steps.metadata.outputs.body }}
includeDownloads: ${{ github.ref_name == 'master' }}

33
.github/workflows/dispatch-preview.yml vendored Normale Datei
Datei anzeigen

@ -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 }}

Datei anzeigen

@ -1,96 +0,0 @@
name: Upload 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'
workflow_call:
inputs:
build:
required: true
description: 'Build number for the release'
type: string
version:
required: true
description: 'Version under which to upload to the Downloads API'
type: string
jobs:
upload:
runs-on: ubuntu-latest
env:
PROJECT: 'geyserpreview'
steps:
- name: Set Variables
id: setvars
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
echo "BUILD=${{ github.event.inputs.build }}" >> $GITHUB_ENV
echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
echo "RUN=${{ github.event.inputs.runId }}" >> $GITHUB_OUTPUT
else
echo "BUILD=${{ inputs.build }}" >> $GITHUB_ENV
echo "VERSION=${{ inputs.version }}" >> $GITHUB_ENV
echo "RUN=${{ github.run_id }}" >> $GITHUB_OUTPUT
fi
- uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427
with:
run-id: ${{ steps.setvars.outputs.RUN }}
github-token: ${{ secrets.GITHUB_TOKEN }}
merge-multiple: true
- name: Get Preview Metadata
if: success()
# See https://github.com/Kas-tle/base-release-action/releases/tag/main-11
uses: Kas-tle/base-release-action@664c39985eb9d0d393ce98e7eb8414d3d98e762a # main-11
with:
appID: ${{ secrets.RELEASE_APP_ID }}
appPrivateKey: ${{ secrets.RELEASE_APP_PK }}
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
releaseEnabled: false
saveMetadata: true
updateReleaseData: false
- name: Update Generated Metadata
if: success()
run: |
cat metadata.json
echo
mv metadata.json metadata.json.tmp
jq --arg project "${PROJECT}" --arg version "${VERSION}" --arg number "${BUILD}" '
.
| .downloads |= map_values({"name", "sha256"})
| {$project, "repo", $version, "number": $number | tonumber, "changes": [], "downloads"}
' metadata.json.tmp > metadata.json
cat metadata.json
- name: Publish to Downloads API
if: success()
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
# Create the build folder
ssh -o StrictHostKeyChecking=no -i id_ecdsa $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP mkdir -p "~/uploads/$PROJECT/$BUILD/"
# Copy over artifacts
rsync -P -e "ssh -o StrictHostKeyChecking=no -i id_ecdsa" Geyser-*.jar $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP:~/uploads/$PROJECT/$BUILD/
# Run the build script
# Push the metadata
rsync -P -e "ssh -o StrictHostKeyChecking=no -i id_ecdsa" metadata.json $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP:~/uploads/$PROJECT/$BUILD/

Datei anzeigen

@ -8,7 +8,7 @@ jobs:
# Forbid access to secrets nor GH Token perms while building the PR # Forbid access to secrets nor GH Token perms while building the PR
permissions: {} permissions: {}
secrets: {} secrets: {}
uses: ./.github/workflows/build-remote.yml uses: GeyserMC/Geyser/.github/workflows/build-remote.yml@master
with: with:
repository: ${{ github.event.pull_request.head.repo.full_name }} repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.sha }} ref: ${{ github.event.pull_request.head.sha }}
@ -18,7 +18,17 @@ jobs:
contains(github.event.pull_request.labels.*.name, 'PR: Needs Testing') contains(github.event.pull_request.labels.*.name, 'PR: Needs Testing')
# Allow access to secrets if we are uploading a preview # Allow access to secrets if we are uploading a preview
secrets: inherit secrets: inherit
uses: ./.github/workflows/preview.yml uses: GeyserMC/actions/.github/workflows/upload-preview.yml@master
with: with:
build: ${{ github.run_number }} build: ${{ github.run_number }}
version: pr.${{ github.event.pull_request.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 }}

Datei anzeigen

@ -1,6 +1,7 @@
<component name="CopyrightManager"> <component name="CopyrightManager">
<copyright> <copyright>
<option name="notice" value="Copyright (c) 2019-&amp;#36;today.year GeyserMC. http://geysermc.org&#10;&#10;Permission is hereby granted, free of charge, to any person obtaining a copy&#10;of this software and associated documentation files (the &quot;Software&quot;), to deal&#10;in the Software without restriction, including without limitation the rights&#10;to use, copy, modify, merge, publish, distribute, sublicense, and/or sell&#10;copies of the Software, and to permit persons to whom the Software is&#10;furnished to do so, subject to the following conditions:&#10;&#10;The above copyright notice and this permission notice shall be included in&#10;all copies or substantial portions of the Software.&#10;&#10;THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR&#10;IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,&#10;FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE&#10;AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER&#10;LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,&#10;OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN&#10;THE SOFTWARE.&#10;&#10;@author GeyserMC&#10;@link https://github.com/GeyserMC/Geyser" /> <option name="allowReplaceRegexp" value="Copyright" />
<option name="notice" value="Copyright (c) &amp;#36;originalComment.match(&quot;Copyright \(c\) (\d+)&quot;, 1, &quot;-&quot;)&amp;#36;today.year GeyserMC. http://geysermc.org&#10;&#10;Permission is hereby granted, free of charge, to any person obtaining a copy&#10;of this software and associated documentation files (the &quot;Software&quot;), to deal&#10;in the Software without restriction, including without limitation the rights&#10;to use, copy, modify, merge, publish, distribute, sublicense, and/or sell&#10;copies of the Software, and to permit persons to whom the Software is&#10;furnished to do so, subject to the following conditions:&#10;&#10;The above copyright notice and this permission notice shall be included in&#10;all copies or substantial portions of the Software.&#10;&#10;THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR&#10;IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,&#10;FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE&#10;AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER&#10;LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,&#10;OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN&#10;THE SOFTWARE.&#10;&#10;@author GeyserMC&#10;@link https://github.com/GeyserMC/Geyser" />
<option name="myName" value="Geyser" /> <option name="myName" value="Geyser" />
</copyright> </copyright>
</component> </component>

Datei anzeigen

@ -14,7 +14,7 @@ The ultimate goal of this project is to allow Minecraft: Bedrock Edition users t
Special thanks to the DragonProxy project for being a trailblazer in protocol translation and for all the team members who have joined us here! 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.80/81 and Minecraft Java 1.20.5/1.20.6 ### Currently supporting Minecraft Bedrock 1.20.80 - 1.21.1 and Minecraft Java 1.21
## Setting Up ## Setting Up
Take a look [here](https://wiki.geysermc.org/geyser/setup/) for how to set up Geyser. Take a look [here](https://wiki.geysermc.org/geyser/setup/) for how to set up Geyser.

Datei anzeigen

@ -145,4 +145,36 @@ public interface CameraData {
* @return whether the camera is currently locked * @return whether the camera is currently locked
*/ */
boolean isCameraLocked(); 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();
} }

Datei anzeigen

@ -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;
}
}

Datei anzeigen

@ -73,7 +73,10 @@ public interface JavaBlockState {
* Gets whether the block state has block entity * Gets whether the block state has block entity
* *
* @return whether the block state has 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(); boolean hasBlockEntity();
/** /**
@ -104,6 +107,11 @@ public interface JavaBlockState {
Builder pistonBehavior(@Nullable String pistonBehavior); 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); Builder hasBlockEntity(boolean hasBlockEntity);
JavaBlockState build(); JavaBlockState build();

Datei anzeigen

@ -36,7 +36,7 @@ import java.util.Map;
import java.util.Objects; 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. * 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 { public final class SessionLoginEvent extends ConnectionEvent implements Cancellable {
@ -99,9 +99,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() { public @NonNull RemoteServer remoteServer() {
return this.remoteServer; return this.remoteServer;

Datei anzeigen

@ -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));
}
}

Datei anzeigen

@ -46,13 +46,35 @@ public final class ConnectionRequestEvent implements Event, Cancellable {
this.proxyIp = proxyIp; 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 * The IP address of the client attempting to connect
* *
* @return the IP address of the client attempting to connect * @return the IP address of the client attempting to connect
*/ */
@NonNull @NonNull
public InetSocketAddress getInetSocketAddress() { public InetSocketAddress inetSocketAddress() {
return ip; return ip;
} }
@ -62,7 +84,7 @@ public final class ConnectionRequestEvent implements Event, Cancellable {
* @return the IP address of the proxy handling the connection * @return the IP address of the proxy handling the connection
*/ */
@Nullable @Nullable
public InetSocketAddress getProxyIp() { public InetSocketAddress proxyIp() {
return proxyIp; return proxyIp;
} }

Datei anzeigen

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org * Copyright (c) 2024 GeyserMC. http://geysermc.org
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@ -23,12 +23,18 @@
* @link https://github.com/GeyserMC/Geyser * @link https://github.com/GeyserMC/Geyser
*/ */
package org.geysermc.geyser.api.skin;
/** /**
* Contains useful collections for use in Geyser. * Represents a cape.
* <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 * @param textureUrl The URL of the cape texture
* 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)} * @param capeId The ID of the cape
* and {@link java.util.Map#containsKey(java.lang.Object)} can be performed by simply checking the bounds of the map * @param capeData The raw cape image data in ARGB format
* size and its "start" integer. * @param failed If the cape failed to load, this is for things like fallback capes
*/ */
package org.geysermc.geyser.util.collection; public record Cape(String textureUrl, String capeId, byte[] capeData, boolean failed) {
public Cape(String textureUrl, String capeId, byte[] capeData) {
this(textureUrl, capeId, capeData, false);
}
}

Datei anzeigen

@ -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);
}
}

Datei anzeigen

@ -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) {
}

Datei anzeigen

@ -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" : "") + "\"}}", "");
}
}

Datei anzeigen

@ -34,3 +34,8 @@ tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
exclude(dependency("io.netty:netty-resolver-dns:.*")) exclude(dependency("io.netty:netty-resolver-dns:.*"))
} }
} }
modrinth {
uploadFile.set(tasks.getByPath("shadowJar"))
loaders.add("bungeecord")
}

Datei anzeigen

@ -26,22 +26,19 @@
package org.geysermc.geyser.platform.bungeecord; package org.geysermc.geyser.platform.bungeecord;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter; import lombok.Setter;
import org.geysermc.geyser.GeyserLogger; import org.geysermc.geyser.GeyserLogger;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@RequiredArgsConstructor
public class GeyserBungeeLogger implements GeyserLogger { public class GeyserBungeeLogger implements GeyserLogger {
private final Logger logger; private final Logger logger;
@Getter @Setter @Getter @Setter
private boolean debug; private boolean debug;
public GeyserBungeeLogger(Logger logger, boolean debug) {
this.logger = logger;
this.debug = debug;
}
@Override @Override
public void severe(String message) { public void severe(String message) {
logger.severe(message); logger.severe(message);

Datei anzeigen

@ -58,14 +58,13 @@ import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
private GeyserCommandManager geyserCommandManager; private GeyserCommandManager geyserCommandManager;
private GeyserBungeeConfiguration geyserConfig; private GeyserBungeeConfiguration geyserConfig;
private GeyserBungeeInjector geyserInjector; private GeyserBungeeInjector geyserInjector;
private GeyserBungeeLogger geyserLogger; private final GeyserBungeeLogger geyserLogger = new GeyserBungeeLogger(getLogger());
private IGeyserPingPassthrough geyserBungeePingPassthrough; private IGeyserPingPassthrough geyserBungeePingPassthrough;
private GeyserImpl geyser; private GeyserImpl geyser;
@ -82,21 +81,21 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
// Copied from ViaVersion. // Copied from ViaVersion.
// https://github.com/ViaVersion/ViaVersion/blob/b8072aad86695cc8ec6f5e4103e43baf3abf6cc5/bungee/src/main/java/us/myles/ViaVersion/BungeePlugin.java#L43 // https://github.com/ViaVersion/ViaVersion/blob/b8072aad86695cc8ec6f5e4103e43baf3abf6cc5/bungee/src/main/java/us/myles/ViaVersion/BungeePlugin.java#L43
try { try {
ProtocolConstants.class.getField("MINECRAFT_1_20_3"); ProtocolConstants.class.getField("MINECRAFT_1_21");
} catch (NoSuchFieldException e) { } catch (NoSuchFieldException e) {
getLogger().warning(" / \\"); geyserLogger.error(" / \\");
getLogger().warning(" / \\"); geyserLogger.error(" / \\");
getLogger().warning(" / | \\"); geyserLogger.error(" / | \\");
getLogger().warning(" / | \\ " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_proxy", getProxy().getName())); geyserLogger.error(" / | \\ " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_proxy", getProxy().getName()));
getLogger().warning(" / \\ " + GeyserLocale.getLocaleStringLog("geyser.may_not_work_as_intended_all_caps")); geyserLogger.error(" / \\ " + GeyserLocale.getLocaleStringLog("geyser.may_not_work_as_intended_all_caps"));
getLogger().warning(" / o \\"); geyserLogger.error(" / o \\");
getLogger().warning("/_____________\\"); geyserLogger.error("/_____________\\");
} }
if (!this.loadConfig()) { if (!this.loadConfig()) {
return; return;
} }
this.geyserLogger = new GeyserBungeeLogger(getLogger(), geyserConfig.isDebugMode()); this.geyserLogger.setDebug(geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
this.geyser = GeyserImpl.load(PlatformType.BUNGEECORD, this); this.geyser = GeyserImpl.load(PlatformType.BUNGEECORD, this);
this.geyserInjector = new GeyserBungeeInjector(this); this.geyserInjector = new GeyserBungeeInjector(this);
@ -293,7 +292,7 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
"config.yml", (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this); "config.yml", (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this);
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserBungeeConfiguration.class); this.geyserConfig = FileUtils.loadConfig(configFile, GeyserBungeeConfiguration.class);
} catch (IOException ex) { } catch (IOException ex) {
getLogger().log(Level.SEVERE, GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex); geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
ex.printStackTrace(); ex.printStackTrace();
return false; return false;
} }

Datei anzeigen

@ -6,6 +6,13 @@ loom {
mixin.defaultRefmapName.set("geyser-refmap.json") mixin.defaultRefmapName.set("geyser-refmap.json")
} }
afterEvaluate {
// We don't need these
tasks.named("remapModrinthJar").configure {
enabled = false
}
}
dependencies { dependencies {
api(projects.core) api(projects.core)
compileOnly(libs.mixin) compileOnly(libs.mixin)

Datei anzeigen

@ -63,6 +63,7 @@ tasks {
modrinth { modrinth {
loaders.add("fabric") loaders.add("fabric")
uploadFile.set(tasks.getByPath("remapModrinthJar"))
dependencies { dependencies {
required.project("fabric-api") required.project("fabric-api")
} }

Datei anzeigen

@ -23,8 +23,8 @@
"geyser.mixins.json" "geyser.mixins.json"
], ],
"depends": { "depends": {
"fabricloader": ">=0.15.10", "fabricloader": ">=0.15.11",
"fabric": "*", "fabric": "*",
"minecraft": ">=1.20.5" "minecraft": ">=1.21"
} }
} }

Datei anzeigen

@ -31,7 +31,9 @@ dependencies {
// Let's shade in our own api // Let's shade in our own api
shadow(projects.api) { isTransitive = false } shadow(projects.api) { isTransitive = false }
shadow(projects.common) { isTransitive = false }
// 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 // Include all transitive deps of core via JiJ
includeTransitive(projects.core) includeTransitive(projects.core)
@ -53,4 +55,5 @@ tasks {
modrinth { modrinth {
loaders.add("neoforge") loaders.add("neoforge")
uploadFile.set(tasks.getByPath("remapModrinthJar"))
} }

Datei anzeigen

@ -27,6 +27,7 @@ package org.geysermc.geyser.platform.neoforge;
import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.CommandSourceStack;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.neoforged.fml.ModContainer;
import net.neoforged.fml.common.Mod; import net.neoforged.fml.common.Mod;
import net.neoforged.fml.loading.FMLLoader; import net.neoforged.fml.loading.FMLLoader;
import net.neoforged.neoforge.common.NeoForge; import net.neoforged.neoforge.common.NeoForge;
@ -43,8 +44,8 @@ public class GeyserNeoForgeBootstrap extends GeyserModBootstrap {
private final GeyserNeoForgePermissionHandler permissionHandler = new GeyserNeoForgePermissionHandler(); private final GeyserNeoForgePermissionHandler permissionHandler = new GeyserNeoForgePermissionHandler();
public GeyserNeoForgeBootstrap() { public GeyserNeoForgeBootstrap(ModContainer container) {
super(new GeyserNeoForgePlatform()); super(new GeyserNeoForgePlatform(container));
if (isServer()) { if (isServer()) {
// Set as an event so we can get the proper IP and port if needed // Set as an event so we can get the proper IP and port if needed

Datei anzeigen

@ -26,20 +26,29 @@
package org.geysermc.geyser.platform.neoforge; package org.geysermc.geyser.platform.neoforge;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.neoforged.fml.ModContainer;
import net.neoforged.fml.ModList;
import net.neoforged.fml.loading.FMLPaths; import net.neoforged.fml.loading.FMLPaths;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.GeyserBootstrap;
import org.geysermc.geyser.api.util.PlatformType; import org.geysermc.geyser.api.util.PlatformType;
import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.dump.BootstrapDumpInfo;
import org.geysermc.geyser.platform.mod.GeyserModBootstrap; import org.geysermc.geyser.platform.mod.GeyserModBootstrap;
import org.geysermc.geyser.platform.mod.platform.GeyserModPlatform; import org.geysermc.geyser.platform.mod.platform.GeyserModPlatform;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
public class GeyserNeoForgePlatform implements GeyserModPlatform { public class GeyserNeoForgePlatform implements GeyserModPlatform {
private final ModContainer container;
public GeyserNeoForgePlatform(ModContainer container) {
this.container = container;
}
@Override @Override
public @NonNull PlatformType platformType() { public @NonNull PlatformType platformType() {
return PlatformType.NEOFORGE; return PlatformType.NEOFORGE;
@ -62,11 +71,21 @@ public class GeyserNeoForgePlatform implements GeyserModPlatform {
@Override @Override
public boolean testFloodgatePluginPresent(@NonNull GeyserModBootstrap bootstrap) { 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 @Override
public @Nullable InputStream resolveResource(@NonNull String resource) { public @Nullable InputStream resolveResource(@NonNull String resource) {
return GeyserBootstrap.class.getClassLoader().getResourceAsStream(resource); try {
Path path = container.getModInfo().getOwningFile().getFile().findResource(resource);
return Files.newInputStream(path);
} catch (IOException e) {
return null;
}
} }
} }

Datei anzeigen

@ -14,12 +14,12 @@ config = "geyser.mixins.json"
[[dependencies.geyser_neoforge]] [[dependencies.geyser_neoforge]]
modId="neoforge" modId="neoforge"
type="required" type="required"
versionRange="[20.5.0-beta,)" versionRange="[21.0.0-beta,)"
ordering="NONE" ordering="NONE"
side="BOTH" side="BOTH"
[[dependencies.geyser_neoforge]] [[dependencies.geyser_neoforge]]
modId="minecraft" modId="minecraft"
type="required" type="required"
versionRange="[1.20.5,1.21)" versionRange="[1.21,)"
ordering="NONE" ordering="NONE"
side="BOTH" side="BOTH"

Datei anzeigen

@ -34,7 +34,6 @@ import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands; import net.minecraft.commands.Commands;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.world.entity.player.Player; 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.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.GeyserBootstrap; import org.geysermc.geyser.GeyserBootstrap;
@ -80,7 +79,7 @@ public abstract class GeyserModBootstrap implements GeyserBootstrap {
private GeyserCommandManager geyserCommandManager; private GeyserCommandManager geyserCommandManager;
private GeyserModConfiguration geyserConfig; private GeyserModConfiguration geyserConfig;
private GeyserModInjector geyserInjector; private GeyserModInjector geyserInjector;
private GeyserModLogger geyserLogger; private final GeyserModLogger geyserLogger = new GeyserModLogger();
private IGeyserPingPassthrough geyserPingPassthrough; private IGeyserPingPassthrough geyserPingPassthrough;
private WorldManager geyserWorldManager; private WorldManager geyserWorldManager;
@ -92,7 +91,7 @@ public abstract class GeyserModBootstrap implements GeyserBootstrap {
if (!loadConfig()) { if (!loadConfig()) {
return; return;
} }
this.geyserLogger = new GeyserModLogger(geyserConfig.isDebugMode()); this.geyserLogger.setDebug(geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
this.geyser = GeyserImpl.load(this.platform.platformType(), this); this.geyser = GeyserImpl.load(this.platform.platformType(), this);
@ -288,7 +287,7 @@ public abstract class GeyserModBootstrap implements GeyserBootstrap {
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserModConfiguration.class); this.geyserConfig = FileUtils.loadConfig(configFile, GeyserModConfiguration.class);
return true; return true;
} catch (IOException ex) { } catch (IOException ex) {
LogManager.getLogger("geyser").error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex); geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
ex.printStackTrace(); ex.printStackTrace();
return false; return false;
} }

Datei anzeigen

@ -37,10 +37,6 @@ public class GeyserModLogger implements GeyserLogger {
private boolean debug; private boolean debug;
public GeyserModLogger(boolean isDebug) {
debug = isDebug;
}
@Override @Override
public void severe(String message) { public void severe(String message) {
logger.fatal(message); logger.fatal(message);

Datei anzeigen

@ -25,55 +25,45 @@
package org.geysermc.geyser.platform.mod.world; package org.geysermc.geyser.platform.mod.world;
import org.geysermc.mcprotocollib.protocol.data.game.Holder;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.BannerPatternLayer;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityInfo;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import net.minecraft.SharedConstants; import net.minecraft.SharedConstants;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.RegistryAccess; import net.minecraft.core.RegistryAccess;
import net.minecraft.core.component.DataComponents; import net.minecraft.core.component.DataComponents;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.network.Filterable;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.component.WritableBookContent;
import net.minecraft.world.item.component.WrittenBookContent;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BannerBlockEntity; import net.minecraft.world.level.block.entity.BannerBlockEntity;
import net.minecraft.world.level.block.entity.BannerPatternLayers; import net.minecraft.world.level.block.entity.BannerPatternLayers;
import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.LecternBlockEntity; import net.minecraft.world.level.block.entity.DecoratedPotBlockEntity;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.LevelChunkSection; import net.minecraft.world.level.chunk.LevelChunkSection;
import net.minecraft.world.level.chunk.status.ChunkStatus; import net.minecraft.world.level.chunk.status.ChunkStatus;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.math.vector.Vector3i; import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.cloudburstmc.nbt.NbtType;
import org.geysermc.erosion.util.LecternUtils;
import org.geysermc.geyser.level.GeyserWorldManager; import org.geysermc.geyser.level.GeyserWorldManager;
import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.network.GameProtocol;
import org.geysermc.geyser.platform.mod.GeyserModBootstrap; import org.geysermc.geyser.platform.mod.GeyserModBootstrap;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.BlockEntityUtils; import org.geysermc.geyser.util.MinecraftKey;
import org.geysermc.mcprotocollib.protocol.data.game.Holder;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.BannerPatternLayer;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
public class GeyserModWorldManager extends GeyserWorldManager { public class GeyserModWorldManager extends GeyserWorldManager {
private static final GsonComponentSerializer GSON_SERIALIZER = GsonComponentSerializer.gson(); private static final GsonComponentSerializer GSON_SERIALIZER = GsonComponentSerializer.gson();
private static final LegacyComponentSerializer LEGACY_SERIALIZER = LegacyComponentSerializer.legacySection();
private final MinecraftServer server; private final MinecraftServer server;
public GeyserModWorldManager(MinecraftServer server) { public GeyserModWorldManager(MinecraftServer server) {
@ -121,94 +111,6 @@ public class GeyserModWorldManager extends GeyserWorldManager {
return SharedConstants.getCurrentVersion().getProtocolVersion() == GameProtocol.getJavaProtocolVersion(); return SharedConstants.getCurrentVersion().getProtocolVersion() == GameProtocol.getJavaProtocolVersion();
} }
@Override
public boolean shouldExpectLecternHandled(GeyserSession session) {
return true;
}
@Override
public void sendLecternData(GeyserSession session, int x, int z, List<BlockEntityInfo> blockEntityInfos) {
server.execute(() -> {
ServerPlayer player = getPlayer(session);
if (player == null) {
return;
}
//noinspection resource - level() is just a getter
LevelChunk chunk = player.level().getChunk(x, z);
final int chunkBlockX = x << 4;
final int chunkBlockZ = z << 4;
//noinspection ForLoopReplaceableByForEach - avoid constructing iterator
for (int i = 0; i < blockEntityInfos.size(); i++) {
BlockEntityInfo blockEntityInfo = blockEntityInfos.get(i);
BlockEntity blockEntity = chunk.getBlockEntity(new BlockPos(chunkBlockX + blockEntityInfo.getX(),
blockEntityInfo.getY(), chunkBlockZ + blockEntityInfo.getZ()));
sendLecternData(session, blockEntity, true);
}
});
}
@Override
public void sendLecternData(GeyserSession session, int x, int y, int z) {
server.execute(() -> {
ServerPlayer player = getPlayer(session);
if (player == null) {
return;
}
//noinspection resource - level() is just a getter
BlockEntity blockEntity = player.level().getBlockEntity(new BlockPos(x, y, z));
sendLecternData(session, blockEntity, false);
});
}
private void sendLecternData(GeyserSession session, BlockEntity blockEntity, boolean isChunkLoad) {
if (!(blockEntity instanceof LecternBlockEntity lectern)) {
return;
}
int x = blockEntity.getBlockPos().getX();
int y = blockEntity.getBlockPos().getY();
int z = blockEntity.getBlockPos().getZ();
if (!lectern.hasBook()) {
if (!isChunkLoad) {
BlockEntityUtils.updateBlockEntity(session, LecternUtils.getBaseLecternTag(x, y, z, 0).build(), Vector3i.from(x, y, z));
}
return;
}
ItemStack book = lectern.getBook();
int pageCount = getPageCount(book);
boolean hasBookPages = pageCount > 0;
NbtMapBuilder lecternTag = LecternUtils.getBaseLecternTag(x, y, z, hasBookPages ? pageCount : 1);
lecternTag.putInt("page", lectern.getPage() / 2);
NbtMapBuilder bookTag = NbtMap.builder()
.putByte("Count", (byte) book.getCount())
.putShort("Damage", (short) 0)
.putString("Name", "minecraft:writable_book");
List<NbtMap> pages = new ArrayList<>(hasBookPages ? pageCount : 1);
if (hasBookPages) {
List<String> bookPages = getPages(book);
for (String page : bookPages) {
NbtMapBuilder pageBuilder = NbtMap.builder()
.putString("photoname", "")
.putString("text", page);
pages.add(pageBuilder.build());
}
} else {
// Empty page
NbtMapBuilder pageBuilder = NbtMap.builder()
.putString("photoname", "")
.putString("text", "");
pages.add(pageBuilder.build());
}
bookTag.putCompound("tag", NbtMap.builder().putList("pages", NbtType.COMPOUND, pages).build());
lecternTag.putCompound("book", bookTag.build());
NbtMap blockEntityTag = lecternTag.build();
BlockEntityUtils.updateBlockEntity(session, blockEntityTag, Vector3i.from(x, y, z));
}
@Override @Override
public boolean hasPermission(GeyserSession session, String permission) { public boolean hasPermission(GeyserSession session, String permission) {
ServerPlayer player = getPlayer(session); ServerPlayer player = getPlayer(session);
@ -263,43 +165,31 @@ public class GeyserModWorldManager extends GeyserWorldManager {
return future; return future;
} }
@Override
public void getDecoratedPotData(GeyserSession session, Vector3i pos, Consumer<List<String>> apply) {
server.execute(() -> {
ServerPlayer player = getPlayer(session);
if (player == null) {
return;
}
BlockPos blockPos = new BlockPos(pos.getX(), pos.getY(), pos.getZ());
// Don't create a new block entity if invalid
//noinspection resource - level() is just a getter
BlockEntity blockEntity = player.level().getChunkAt(blockPos).getBlockEntity(blockPos);
if (blockEntity instanceof DecoratedPotBlockEntity pot) {
List<String> sherds = pot.getDecorations().ordered()
.stream().map(item -> BuiltInRegistries.ITEM.getKey(item).toString())
.toList();
apply.accept(sherds);
}
});
}
private ServerPlayer getPlayer(GeyserSession session) { private ServerPlayer getPlayer(GeyserSession session) {
return server.getPlayerList().getPlayer(session.getPlayerEntity().getUuid()); return server.getPlayerList().getPlayer(session.getPlayerEntity().getUuid());
} }
private static int getPageCount(ItemStack itemStack) {
WrittenBookContent writtenBookContent = itemStack.get(DataComponents.WRITTEN_BOOK_CONTENT);
if (writtenBookContent != null) {
return writtenBookContent.pages().size();
} else {
WritableBookContent writableBookContent = itemStack.get(DataComponents.WRITABLE_BOOK_CONTENT);
return writableBookContent != null ? writableBookContent.pages().size() : 0;
}
}
private static List<String> getPages(ItemStack itemStack) {
WrittenBookContent writtenBookContent = itemStack.get(DataComponents.WRITTEN_BOOK_CONTENT);
if (writtenBookContent != null) {
return writtenBookContent.pages().stream()
.map(Filterable::raw)
.map(GeyserModWorldManager::fromComponent)
.toList();
} else {
WritableBookContent writableBookContent = itemStack.get(DataComponents.WRITABLE_BOOK_CONTENT);
if (writableBookContent == null) {
return List.of();
}
return writableBookContent.pages().stream()
.map(Filterable::raw)
.toList();
}
}
private static String fromComponent(Component component) {
String json = Component.Serializer.toJson(component, RegistryAccess.EMPTY);
return LEGACY_SERIALIZER.serialize(GSON_SERIALIZER.deserializeOr(json, net.kyori.adventure.text.Component.empty()));
}
private static net.kyori.adventure.text.Component toKyoriComponent(Component component) { private static net.kyori.adventure.text.Component toKyoriComponent(Component component) {
String json = Component.Serializer.toJson(component, RegistryAccess.EMPTY); String json = Component.Serializer.toJson(component, RegistryAccess.EMPTY);
return GSON_SERIALIZER.deserializeOr(json, net.kyori.adventure.text.Component.empty()); return GSON_SERIALIZER.deserializeOr(json, net.kyori.adventure.text.Component.empty());
@ -309,7 +199,7 @@ public class GeyserModWorldManager extends GeyserWorldManager {
return patternLayers.layers().stream() return patternLayers.layers().stream()
.map(layer -> { .map(layer -> {
BannerPatternLayer.BannerPattern pattern = new BannerPatternLayer.BannerPattern( BannerPatternLayer.BannerPattern pattern = new BannerPatternLayer.BannerPattern(
layer.pattern().value().assetId().toString(), layer.pattern().value().translationKey() MinecraftKey.key(layer.pattern().value().assetId().toString()), layer.pattern().value().translationKey()
); );
return new BannerPatternLayer(Holder.ofCustom(pattern), layer.color().getId()); return new BannerPatternLayer(Holder.ofCustom(pattern), layer.color().getId());
}) })

Datei anzeigen

@ -4,6 +4,12 @@ dependencies {
isTransitive = false isTransitive = false
} }
implementation(libs.erosion.bukkit.nms) {
attributes {
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 21)
}
}
implementation(variantOf(libs.adapters.spigot) { implementation(variantOf(libs.adapters.spigot) {
classifier("all") // otherwise the unshaded jar is used without the shaded NMS implementations classifier("all") // otherwise the unshaded jar is used without the shaded NMS implementations
}) })
@ -70,3 +76,8 @@ tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
exclude(dependency("com.mojang:.*")) exclude(dependency("com.mojang:.*"))
} }
} }
modrinth {
uploadFile.set(tasks.getByPath("shadowJar"))
loaders.addAll("spigot", "paper")
}

Datei anzeigen

@ -34,8 +34,8 @@ import java.util.logging.Logger;
public final class GeyserPaperLogger extends GeyserSpigotLogger { public final class GeyserPaperLogger extends GeyserSpigotLogger {
private final ComponentLogger componentLogger; private final ComponentLogger componentLogger;
public GeyserPaperLogger(Plugin plugin, Logger logger, boolean debug) { public GeyserPaperLogger(Plugin plugin, Logger logger) {
super(logger, debug); super(logger);
componentLogger = plugin.getComponentLogger(); componentLogger = plugin.getComponentLogger();
} }

Datei anzeigen

@ -25,15 +25,15 @@
package org.geysermc.geyser.platform.spigot; package org.geysermc.geyser.platform.spigot;
import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter; import lombok.Setter;
import org.geysermc.geyser.GeyserLogger; import org.geysermc.geyser.GeyserLogger;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@AllArgsConstructor @RequiredArgsConstructor
public class GeyserSpigotLogger implements GeyserLogger { public class GeyserSpigotLogger implements GeyserLogger {
private final Logger logger; private final Logger logger;
@Getter @Setter @Getter @Setter

Datei anzeigen

@ -79,14 +79,14 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.UUID; import java.util.UUID;
import java.util.logging.Level;
public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
private GeyserSpigotCommandManager geyserCommandManager; private GeyserSpigotCommandManager geyserCommandManager;
private GeyserSpigotConfiguration geyserConfig; private GeyserSpigotConfiguration geyserConfig;
private GeyserSpigotInjector geyserInjector; private GeyserSpigotInjector geyserInjector;
private GeyserSpigotLogger geyserLogger; private final GeyserSpigotLogger geyserLogger = GeyserPaperLogger.supported() ?
new GeyserPaperLogger(this, getLogger()) : new GeyserSpigotLogger(getLogger());
private IGeyserPingPassthrough geyserSpigotPingPassthrough; private IGeyserPingPassthrough geyserSpigotPingPassthrough;
private GeyserSpigotWorldManager geyserWorldManager; private GeyserSpigotWorldManager geyserWorldManager;
@ -114,12 +114,12 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
// We depend on this as a fallback in certain scenarios // We depend on this as a fallback in certain scenarios
BlockData.class.getMethod("getAsString"); BlockData.class.getMethod("getAsString");
} catch (ClassNotFoundException | NoSuchMethodException e) { } catch (ClassNotFoundException | NoSuchMethodException e) {
getLogger().severe("*********************************************"); geyserLogger.error("*********************************************");
getLogger().severe(""); geyserLogger.error("");
getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server.header")); geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server.header"));
getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server.message", "1.13.2")); geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server.message", "1.13.2"));
getLogger().severe(""); geyserLogger.error("");
getLogger().severe("*********************************************"); geyserLogger.error("*********************************************");
Bukkit.getPluginManager().disablePlugin(this); Bukkit.getPluginManager().disablePlugin(this);
return; return;
} }
@ -128,12 +128,12 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
Class.forName("net.md_5.bungee.chat.ComponentSerializer"); Class.forName("net.md_5.bungee.chat.ComponentSerializer");
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
if (!PaperAdventure.canSendMessageUsingComponent()) { // Prepare for Paper eventually removing Bungee chat if (!PaperAdventure.canSendMessageUsingComponent()) { // Prepare for Paper eventually removing Bungee chat
getLogger().severe("*********************************************"); geyserLogger.error("*********************************************");
getLogger().severe(""); geyserLogger.error("");
getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server_type.header", getServer().getName())); geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server_type.header", getServer().getName()));
getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server_type.message", "Paper")); geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server_type.message", "Paper"));
getLogger().severe(""); geyserLogger.error("");
getLogger().severe("*********************************************"); geyserLogger.error("*********************************************");
Bukkit.getPluginManager().disablePlugin(this); Bukkit.getPluginManager().disablePlugin(this);
return; return;
} }
@ -142,11 +142,11 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
try { try {
Class.forName("io.netty.util.internal.ObjectPool$ObjectCreator"); Class.forName("io.netty.util.internal.ObjectPool$ObjectCreator");
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
getLogger().severe("*********************************************"); geyserLogger.error("*********************************************");
getLogger().severe(""); geyserLogger.error("");
getLogger().severe("This version of Spigot is using an outdated version of netty. Please use Paper instead!"); geyserLogger.error("This version of Spigot is using an outdated version of netty. Please use Paper instead!");
getLogger().severe(""); geyserLogger.error("");
getLogger().severe("*********************************************"); geyserLogger.error("*********************************************");
Bukkit.getPluginManager().disablePlugin(this); Bukkit.getPluginManager().disablePlugin(this);
return; return;
} }
@ -154,8 +154,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
if (!loadConfig()) { if (!loadConfig()) {
return; return;
} }
this.geyserLogger = GeyserPaperLogger.supported() ? new GeyserPaperLogger(this, getLogger(), geyserConfig.isDebugMode()) this.geyserLogger.setDebug(geyserConfig.isDebugMode());
: new GeyserSpigotLogger(getLogger(), geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
// Turn "(MC: 1.16.4)" into 1.16.4. // Turn "(MC: 1.16.4)" into 1.16.4.
@ -252,6 +251,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
SpigotAdapters.registerWorldAdapter(nmsVersion); SpigotAdapters.registerWorldAdapter(nmsVersion);
geyserLogger.debug("Using spigot NMS adapter for nms version: " + nmsVersion); geyserLogger.debug("Using spigot NMS adapter for nms version: " + nmsVersion);
} catch (Exception e) { // Likely running on Paper 1.20.5+ } catch (Exception e) { // Likely running on Paper 1.20.5+
geyserLogger.debug("Unable to find spigot world manager: " + e.getMessage());
//noinspection deprecation //noinspection deprecation
int protocolVersion = Bukkit.getUnsafe().getProtocolVersion(); int protocolVersion = Bukkit.getUnsafe().getProtocolVersion();
PaperAdapters.registerClosestWorldAdapter(protocolVersion); PaperAdapters.registerClosestWorldAdapter(protocolVersion);
@ -266,7 +266,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
this.geyserWorldManager = new GeyserSpigotNativeWorldManager(this, isPaper); this.geyserWorldManager = new GeyserSpigotNativeWorldManager(this, isPaper);
} }
geyserLogger.debug("Using world manager of type: " + this.geyserWorldManager.getClass().getSimpleName()); geyserLogger.debug("Using world manager of type: " + this.geyserWorldManager.getClass().getSimpleName());
} catch (Exception e) { } catch (Throwable e) {
if (geyserConfig.isDebugMode()) { if (geyserConfig.isDebugMode()) {
geyserLogger.debug("Error while attempting to find NMS adapter. Most likely, this can be safely ignored. :)"); geyserLogger.debug("Error while attempting to find NMS adapter. Most likely, this can be safely ignored. :)");
e.printStackTrace(); e.printStackTrace();
@ -486,7 +486,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
(x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this); (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this);
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserSpigotConfiguration.class); this.geyserConfig = FileUtils.loadConfig(configFile, GeyserSpigotConfiguration.class);
} catch (IOException ex) { } catch (IOException ex) {
getLogger().log(Level.SEVERE, GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex); geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
ex.printStackTrace(); ex.printStackTrace();
Bukkit.getPluginManager().disablePlugin(this); Bukkit.getPluginManager().disablePlugin(this);
return false; return false;

Datei anzeigen

@ -25,10 +25,8 @@
package org.geysermc.geyser.platform.spigot.world; package org.geysermc.geyser.platform.spigot.world;
import org.geysermc.mcprotocollib.protocol.data.game.level.block.value.PistonValueType; import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
import org.cloudburstmc.math.vector.Vector3i; import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import it.unimi.dsi.fastutil.objects.Object2IntArrayMap;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.World; import org.bukkit.World;
@ -40,13 +38,17 @@ import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockPistonEvent; import org.bukkit.event.block.BlockPistonEvent;
import org.bukkit.event.block.BlockPistonExtendEvent; import org.bukkit.event.block.BlockPistonExtendEvent;
import org.bukkit.event.block.BlockPistonRetractEvent; import org.bukkit.event.block.BlockPistonRetractEvent;
import org.cloudburstmc.math.vector.Vector3i;
import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.level.block.property.Properties;
import org.geysermc.geyser.level.block.type.BlockState;
import org.geysermc.geyser.level.physics.Direction; import org.geysermc.geyser.level.physics.Direction;
import org.geysermc.geyser.platform.spigot.world.manager.GeyserSpigotWorldManager; import org.geysermc.geyser.platform.spigot.world.manager.GeyserSpigotWorldManager;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.PistonCache; import org.geysermc.geyser.session.cache.PistonCache;
import org.geysermc.geyser.translator.level.block.entity.PistonBlockEntity; import org.geysermc.geyser.translator.level.block.entity.PistonBlockEntity;
import org.geysermc.mcprotocollib.protocol.data.game.level.block.value.PistonValueType;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -85,7 +87,7 @@ public class GeyserPistonListener implements Listener {
PistonValueType type = isExtend ? PistonValueType.PUSHING : PistonValueType.PULLING; PistonValueType type = isExtend ? PistonValueType.PUSHING : PistonValueType.PULLING;
boolean sticky = event.isSticky(); boolean sticky = event.isSticky();
Object2IntMap<Vector3i> attachedBlocks = new Object2IntArrayMap<>(); Object2ObjectMap<Vector3i, BlockState> attachedBlocks = new Object2ObjectArrayMap<>();
boolean blocksFilled = false; boolean blocksFilled = false;
for (Map.Entry<UUID, GeyserSession> entry : geyser.getSessionManager().getSessions().entrySet()) { for (Map.Entry<UUID, GeyserSession> entry : geyser.getSessionManager().getSessions().entrySet()) {
@ -108,10 +110,10 @@ public class GeyserPistonListener implements Listener {
List<Block> blocks = isExtend ? ((BlockPistonExtendEvent) event).getBlocks() : ((BlockPistonRetractEvent) event).getBlocks(); List<Block> blocks = isExtend ? ((BlockPistonExtendEvent) event).getBlocks() : ((BlockPistonRetractEvent) event).getBlocks();
for (Block block : blocks) { for (Block block : blocks) {
Location attachedLocation = block.getLocation(); Location attachedLocation = block.getLocation();
int blockId = worldManager.getBlockNetworkId(block); BlockState state = BlockState.of(worldManager.getBlockNetworkId(block));
// Ignore blocks that will be destroyed // Ignore blocks that will be destroyed
if (BlockStateValues.canPistonMoveBlock(blockId, isExtend)) { if (BlockStateValues.canPistonMoveBlock(state, isExtend)) {
attachedBlocks.put(getVector(attachedLocation), blockId); attachedBlocks.put(getVector(attachedLocation), state);
} }
} }
blocksFilled = true; blocksFilled = true;
@ -119,7 +121,7 @@ public class GeyserPistonListener implements Listener {
int pistonBlockId = worldManager.getBlockNetworkId(event.getBlock()); int pistonBlockId = worldManager.getBlockNetworkId(event.getBlock());
// event.getDirection() is unreliable // event.getDirection() is unreliable
Direction orientation = BlockStateValues.getPistonOrientation(pistonBlockId); Direction orientation = BlockState.of(pistonBlockId).getValue(Properties.FACING);
session.executeInEventLoop(() -> { session.executeInEventLoop(() -> {
PistonCache pistonCache = session.getPistonCache(); PistonCache pistonCache = session.getPistonCache();

Datei anzeigen

@ -33,7 +33,7 @@ import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.block.BlockPlaceEvent;
import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.level.block.type.Block;
import org.geysermc.geyser.platform.spigot.world.manager.GeyserSpigotWorldManager; import org.geysermc.geyser.platform.spigot.world.manager.GeyserSpigotWorldManager;
import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.BlockRegistries;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
@ -59,11 +59,11 @@ public class GeyserSpigotBlockPlaceListener implements Listener {
event.getBlockPlaced().getX(), event.getBlockPlaced().getY(), event.getBlockPlaced().getZ()))); event.getBlockPlaced().getX(), event.getBlockPlaced().getY(), event.getBlockPlaced().getZ())));
} else { } else {
String javaBlockId = event.getBlockPlaced().getBlockData().getAsString(); String javaBlockId = event.getBlockPlaced().getBlockData().getAsString();
placeBlockSoundPacket.setExtraData(session.getBlockMappings().getBedrockBlockId(BlockRegistries.JAVA_IDENTIFIER_TO_ID.get().getOrDefault(javaBlockId, BlockStateValues.JAVA_AIR_ID))); placeBlockSoundPacket.setExtraData(session.getBlockMappings().getBedrockBlockId(BlockRegistries.JAVA_IDENTIFIER_TO_ID.get().getOrDefault(javaBlockId, Block.JAVA_AIR_ID)));
} }
placeBlockSoundPacket.setIdentifier(":"); placeBlockSoundPacket.setIdentifier(":");
session.sendUpstreamPacket(placeBlockSoundPacket); session.sendUpstreamPacket(placeBlockSoundPacket);
session.setLastBlockPlacePosition(null); session.setLastBlockPlacePosition(null);
session.setLastBlockPlacedId(null); session.setLastBlockPlaced(null);
} }
} }

Datei anzeigen

@ -34,6 +34,7 @@ import org.geysermc.geyser.adapters.WorldAdapter;
import org.geysermc.geyser.adapters.paper.PaperAdapters; import org.geysermc.geyser.adapters.paper.PaperAdapters;
import org.geysermc.geyser.adapters.spigot.SpigotAdapters; import org.geysermc.geyser.adapters.spigot.SpigotAdapters;
import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.level.block.type.Block;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
public class GeyserSpigotNativeWorldManager extends GeyserSpigotWorldManager { public class GeyserSpigotNativeWorldManager extends GeyserSpigotWorldManager {
@ -52,7 +53,7 @@ public class GeyserSpigotNativeWorldManager extends GeyserSpigotWorldManager {
public int getBlockAt(GeyserSession session, int x, int y, int z) { public int getBlockAt(GeyserSession session, int x, int y, int z) {
Player player = Bukkit.getPlayer(session.getPlayerEntity().getUsername()); Player player = Bukkit.getPlayer(session.getPlayerEntity().getUsername());
if (player == null) { if (player == null) {
return BlockStateValues.JAVA_AIR_ID; return Block.JAVA_AIR_ID;
} }
return adapter.getBlockAt(player.getWorld(), x, y, z); return adapter.getBlockAt(player.getWorld(), x, y, z);
} }

Datei anzeigen

@ -25,55 +25,52 @@
package org.geysermc.geyser.platform.spigot.world.manager; package org.geysermc.geyser.platform.spigot.world.manager;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityInfo;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.DecoratedPot;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.nbt.NbtMap; import org.cloudburstmc.math.vector.Vector3i;
import org.geysermc.erosion.bukkit.BukkitLecterns;
import org.geysermc.erosion.bukkit.BukkitUtils; import org.geysermc.erosion.bukkit.BukkitUtils;
import org.geysermc.erosion.bukkit.PickBlockUtils;
import org.geysermc.erosion.bukkit.SchedulerUtils; import org.geysermc.erosion.bukkit.SchedulerUtils;
import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.level.GameRule; import org.geysermc.geyser.level.GameRule;
import org.geysermc.geyser.level.WorldManager; import org.geysermc.geyser.level.WorldManager;
import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.BlockRegistries;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.BlockEntityUtils; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
/** /**
* The base world manager to use when there is no supported NMS revision * The base world manager to use when there is no supported NMS revision
*/ */
public class GeyserSpigotWorldManager extends WorldManager { public class GeyserSpigotWorldManager extends WorldManager {
private final Plugin plugin; private final Plugin plugin;
private final BukkitLecterns lecterns;
public GeyserSpigotWorldManager(Plugin plugin) { public GeyserSpigotWorldManager(Plugin plugin) {
this.plugin = plugin; this.plugin = plugin;
this.lecterns = new BukkitLecterns(plugin);
} }
@Override @Override
public int getBlockAt(GeyserSession session, int x, int y, int z) { public int getBlockAt(GeyserSession session, int x, int y, int z) {
Player bukkitPlayer; Player bukkitPlayer;
if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) { if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) {
return BlockStateValues.JAVA_AIR_ID; return org.geysermc.geyser.level.block.type.Block.JAVA_AIR_ID;
} }
World world = bukkitPlayer.getWorld(); World world = bukkitPlayer.getWorld();
if (!world.isChunkLoaded(x >> 4, z >> 4)) { if (!world.isChunkLoaded(x >> 4, z >> 4)) {
// If the chunk isn't loaded, how could we even be here? // If the chunk isn't loaded, how could we even be here?
return BlockStateValues.JAVA_AIR_ID; return org.geysermc.geyser.level.block.type.Block.JAVA_AIR_ID;
} }
return getBlockNetworkId(world.getBlockAt(x, y, z)); return getBlockNetworkId(world.getBlockAt(x, y, z));
@ -84,9 +81,9 @@ public class GeyserSpigotWorldManager extends WorldManager {
// Terrible behavior, but this is basically what's always been happening behind the scenes anyway. // Terrible behavior, but this is basically what's always been happening behind the scenes anyway.
CompletableFuture<String> blockData = new CompletableFuture<>(); CompletableFuture<String> blockData = new CompletableFuture<>();
Bukkit.getRegionScheduler().execute(this.plugin, block.getLocation(), () -> blockData.complete(block.getBlockData().getAsString())); Bukkit.getRegionScheduler().execute(this.plugin, block.getLocation(), () -> blockData.complete(block.getBlockData().getAsString()));
return BlockRegistries.JAVA_IDENTIFIER_TO_ID.getOrDefault(blockData.join(), BlockStateValues.JAVA_AIR_ID); return BlockRegistries.JAVA_IDENTIFIER_TO_ID.getOrDefault(blockData.join(), org.geysermc.geyser.level.block.type.Block.JAVA_AIR_ID);
} }
return BlockRegistries.JAVA_IDENTIFIER_TO_ID.getOrDefault(block.getBlockData().getAsString(), BlockStateValues.JAVA_AIR_ID); return BlockRegistries.JAVA_IDENTIFIER_TO_ID.getOrDefault(block.getBlockData().getAsString(), org.geysermc.geyser.level.block.type.Block.JAVA_AIR_ID); // TODO could just make this a BlockState lookup?
} }
@Override @Override
@ -94,69 +91,6 @@ public class GeyserSpigotWorldManager extends WorldManager {
return true; return true;
} }
@Override
public void sendLecternData(GeyserSession session, int x, int y, int z) {
Player bukkitPlayer;
if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) {
return;
}
Block block = bukkitPlayer.getWorld().getBlockAt(x, y, z);
// Run as a task to prevent async issues
SchedulerUtils.runTask(this.plugin, () -> sendLecternData(session, block, false), block);
}
public void sendLecternData(GeyserSession session, int x, int z, List<BlockEntityInfo> blockEntityInfos) {
Player bukkitPlayer;
if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) {
return;
}
if (SchedulerUtils.FOLIA) {
Chunk chunk = getChunk(bukkitPlayer.getWorld(), x, z);
if (chunk == null) {
return;
}
Bukkit.getRegionScheduler().execute(this.plugin, bukkitPlayer.getWorld(), x, z, () ->
sendLecternData(session, chunk, blockEntityInfos));
} else {
Bukkit.getScheduler().runTask(this.plugin, () -> {
Chunk chunk = getChunk(bukkitPlayer.getWorld(), x, z);
if (chunk == null) {
return;
}
sendLecternData(session, chunk, blockEntityInfos);
});
}
}
private @Nullable Chunk getChunk(World world, int x, int z) {
if (!world.isChunkLoaded(x, z)) {
return null;
}
return world.getChunkAt(x, z);
}
private void sendLecternData(GeyserSession session, Chunk chunk, List<BlockEntityInfo> blockEntityInfos) {
//noinspection ForLoopReplaceableByForEach - avoid constructing Iterator
for (int i = 0; i < blockEntityInfos.size(); i++) {
BlockEntityInfo info = blockEntityInfos.get(i);
Block block = chunk.getBlock(info.getX(), info.getY(), info.getZ());
sendLecternData(session, block, true);
}
}
private void sendLecternData(GeyserSession session, Block block, boolean isChunkLoad) {
NbtMap blockEntityTag = this.lecterns.getLecternData(block, isChunkLoad);
if (blockEntityTag != null) {
BlockEntityUtils.updateBlockEntity(session, blockEntityTag, BukkitUtils.getVector(block.getLocation()));
}
}
@Override
public boolean shouldExpectLecternHandled(GeyserSession session) {
return true;
}
public boolean getGameRuleBool(GeyserSession session, GameRule gameRule) { public boolean getGameRuleBool(GeyserSession session, GameRule gameRule) {
org.bukkit.GameRule<?> bukkitGameRule = org.bukkit.GameRule.getByName(gameRule.getJavaID()); org.bukkit.GameRule<?> bukkitGameRule = org.bukkit.GameRule.getByName(gameRule.getJavaID());
if (bukkitGameRule == null) { if (bukkitGameRule == null) {
@ -205,17 +139,31 @@ public class GeyserSpigotWorldManager extends WorldManager {
@Override @Override
public @NonNull CompletableFuture<@Nullable DataComponents> getPickItemComponents(GeyserSession session, int x, int y, int z, boolean addNbtData) { public @NonNull CompletableFuture<@Nullable DataComponents> getPickItemComponents(GeyserSession session, int x, int y, int z, boolean addNbtData) {
CompletableFuture<@Nullable DataComponents> future = new CompletableFuture<>();
Player bukkitPlayer; Player bukkitPlayer;
if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUuid())) == null) { if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUuid())) == null) {
future.complete(null); return CompletableFuture.completedFuture(null);
return future;
} }
CompletableFuture<Int2ObjectMap<byte[]>> future = new CompletableFuture<>();
Block block = bukkitPlayer.getWorld().getBlockAt(x, y, z); Block block = bukkitPlayer.getWorld().getBlockAt(x, y, z);
// Paper 1.19.3 complains about async access otherwise. // Paper 1.19.3 complains about async access otherwise.
// java.lang.IllegalStateException: Tile is null, asynchronous access? // java.lang.IllegalStateException: Tile is null, asynchronous access?
SchedulerUtils.runTask(this.plugin, () -> future.complete(/*PickBlockUtils.pickBlock(block)*/ null), block); // TODO fix erosion once clear how to handle this SchedulerUtils.runTask(this.plugin, () -> future.complete(PickBlockUtils.pickBlock(block)), block);
return future; return future.thenApply(RAW_TRANSFORMER);
}
public void getDecoratedPotData(GeyserSession session, Vector3i pos, Consumer<List<String>> apply) {
Player bukkitPlayer;
if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUuid())) == null) {
return;
}
Block block = bukkitPlayer.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ());
SchedulerUtils.runTask(this.plugin, () -> {
var state = BukkitUtils.getBlockState(block);
if (!(state instanceof DecoratedPot pot)) {
return;
}
apply.accept(pot.getShards().stream().map(material -> material.getKey().toString()).toList());
}, block);
} }
/** /**

Datei anzeigen

@ -71,7 +71,7 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
private GeyserCommandManager geyserCommandManager; private GeyserCommandManager geyserCommandManager;
private GeyserStandaloneConfiguration geyserConfig; private GeyserStandaloneConfiguration geyserConfig;
private GeyserStandaloneLogger geyserLogger; private final GeyserStandaloneLogger geyserLogger = new GeyserStandaloneLogger();
private IGeyserPingPassthrough geyserPingPassthrough; private IGeyserPingPassthrough geyserPingPassthrough;
private GeyserStandaloneGUI gui; private GeyserStandaloneGUI gui;
@Getter @Getter
@ -181,8 +181,6 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
} }
} }
this.geyserLogger = new GeyserStandaloneLogger();
if (useGui && gui == null) { if (useGui && gui == null) {
gui = new GeyserStandaloneGUI(geyserLogger); gui = new GeyserStandaloneGUI(geyserLogger);
gui.redirectSystemStreams(); gui.redirectSystemStreams();

Datei anzeigen

@ -70,3 +70,8 @@ tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
exclude(dependency("net.kyori:adventure-nbt:.*")) exclude(dependency("net.kyori:adventure-nbt:.*"))
} }
} }
modrinth {
uploadFile.set(tasks.getByPath("shadowJar"))
loaders.addAll("velocity")
}

Datei anzeigen

@ -25,13 +25,13 @@
package org.geysermc.geyser.platform.velocity; package org.geysermc.geyser.platform.velocity;
import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter; import lombok.Setter;
import org.geysermc.geyser.GeyserLogger; import org.geysermc.geyser.GeyserLogger;
import org.slf4j.Logger; import org.slf4j.Logger;
@AllArgsConstructor @RequiredArgsConstructor
public class GeyserVelocityLogger implements GeyserLogger { public class GeyserVelocityLogger implements GeyserLogger {
private final Logger logger; private final Logger logger;
@Getter @Setter @Getter @Setter

Datei anzeigen

@ -64,44 +64,44 @@ import java.util.UUID;
@Plugin(id = "geyser", name = GeyserImpl.NAME + "-Velocity", version = GeyserImpl.VERSION, url = "https://geysermc.org", authors = "GeyserMC") @Plugin(id = "geyser", name = GeyserImpl.NAME + "-Velocity", version = GeyserImpl.VERSION, url = "https://geysermc.org", authors = "GeyserMC")
public class GeyserVelocityPlugin implements GeyserBootstrap { public class GeyserVelocityPlugin implements GeyserBootstrap {
@Inject
private Logger logger;
@Inject
private ProxyServer proxyServer;
@Inject
private CommandManager commandManager;
private final ProxyServer proxyServer;
private final CommandManager commandManager;
private final GeyserVelocityLogger geyserLogger;
private GeyserCommandManager geyserCommandManager; private GeyserCommandManager geyserCommandManager;
private GeyserVelocityConfiguration geyserConfig; private GeyserVelocityConfiguration geyserConfig;
private GeyserVelocityInjector geyserInjector; private GeyserVelocityInjector geyserInjector;
private GeyserVelocityLogger geyserLogger;
private IGeyserPingPassthrough geyserPingPassthrough; private IGeyserPingPassthrough geyserPingPassthrough;
private GeyserImpl geyser; private GeyserImpl geyser;
@Getter @Getter
private final Path configFolder = Paths.get("plugins/" + GeyserImpl.NAME + "-Velocity/"); private final Path configFolder = Paths.get("plugins/" + GeyserImpl.NAME + "-Velocity/");
@Inject
public GeyserVelocityPlugin(ProxyServer server, Logger logger, CommandManager manager) {
this.geyserLogger = new GeyserVelocityLogger(logger);
this.proxyServer = server;
this.commandManager = manager;
}
@Override @Override
public void onGeyserInitialize() { public void onGeyserInitialize() {
GeyserLocale.init(this); GeyserLocale.init(this);
if (!ProtocolVersion.isSupported(GameProtocol.getJavaProtocolVersion())) { if (!ProtocolVersion.isSupported(GameProtocol.getJavaProtocolVersion())) {
logger.error(" / \\"); geyserLogger.error(" / \\");
logger.error(" / \\"); geyserLogger.error(" / \\");
logger.error(" / | \\"); geyserLogger.error(" / | \\");
logger.error(" / | \\ " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_proxy", proxyServer.getVersion().getName())); geyserLogger.error(" / | \\ " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_proxy", proxyServer.getVersion().getName()));
logger.error(" / \\ " + GeyserLocale.getLocaleStringLog("geyser.may_not_work_as_intended_all_caps")); geyserLogger.error(" / \\ " + GeyserLocale.getLocaleStringLog("geyser.may_not_work_as_intended_all_caps"));
logger.error(" / o \\"); geyserLogger.error(" / o \\");
logger.error("/_____________\\"); geyserLogger.error("/_____________\\");
} }
if (!loadConfig()) { if (!loadConfig()) {
return; return;
} }
this.geyserLogger = new GeyserVelocityLogger(logger, geyserConfig.isDebugMode()); this.geyserLogger.setDebug(geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
this.geyser = GeyserImpl.load(PlatformType.VELOCITY, this); this.geyser = GeyserImpl.load(PlatformType.VELOCITY, this);
@ -249,7 +249,7 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
"config.yml", (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this); "config.yml", (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this);
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserVelocityConfiguration.class); this.geyserConfig = FileUtils.loadConfig(configFile, GeyserVelocityConfiguration.class);
} catch (IOException ex) { } catch (IOException ex) {
logger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex); geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
ex.printStackTrace(); ex.printStackTrace();
return false; return false;
} }

Datei anzeigen

@ -27,14 +27,23 @@ package org.geysermc.geyser.platform.viaproxy;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import net.raphimc.vialegacy.api.LegacyProtocolVersion; import net.raphimc.vialegacy.api.LegacyProtocolVersion;
import net.raphimc.viaproxy.ViaProxy; import net.raphimc.viaproxy.ViaProxy;
import net.raphimc.viaproxy.protocoltranslator.viaproxy.ViaProxyConfig;
import org.geysermc.geyser.configuration.GeyserJacksonConfiguration; import org.geysermc.geyser.configuration.GeyserJacksonConfiguration;
import java.io.File; import java.io.File;
import java.nio.file.Path; import java.nio.file.Path;
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
@SuppressWarnings("FieldMayBeFinal") // Jackson requires that the fields are not final
public class GeyserViaProxyConfiguration extends GeyserJacksonConfiguration { public class GeyserViaProxyConfiguration extends GeyserJacksonConfiguration {
private RemoteConfiguration remote = new RemoteConfiguration() {
@Override
public boolean isForwardHost() {
return super.isForwardHost() || !ViaProxy.getConfig().getWildcardDomainHandling().equals(ViaProxyConfig.WildcardDomainHandling.NONE);
}
};
@Override @Override
public Path getFloodgateKeyPath() { public Path getFloodgateKeyPath() {
return new File(GeyserViaProxyPlugin.ROOT_FOLDER, this.getFloodgateKeyFile()).toPath(); return new File(GeyserViaProxyPlugin.ROOT_FOLDER, this.getFloodgateKeyFile()).toPath();
@ -50,4 +59,9 @@ public class GeyserViaProxyConfiguration extends GeyserJacksonConfiguration {
return interval; return interval;
} }
@Override
public RemoteConfiguration getRemote() {
return this.remote;
}
} }

Datei anzeigen

@ -37,6 +37,7 @@ import org.apache.logging.log4j.LogManager;
import org.geysermc.geyser.GeyserBootstrap; import org.geysermc.geyser.GeyserBootstrap;
import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.GeyserLogger; import org.geysermc.geyser.GeyserLogger;
import org.geysermc.geyser.api.event.EventRegistrar;
import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.api.network.AuthType;
import org.geysermc.geyser.api.util.PlatformType; import org.geysermc.geyser.api.util.PlatformType;
import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.command.GeyserCommandManager;
@ -44,6 +45,7 @@ import org.geysermc.geyser.configuration.GeyserConfiguration;
import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.dump.BootstrapDumpInfo;
import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough; import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough;
import org.geysermc.geyser.ping.IGeyserPingPassthrough; import org.geysermc.geyser.ping.IGeyserPingPassthrough;
import org.geysermc.geyser.platform.viaproxy.listener.GeyserServerTransferListener;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.util.FileUtils; import org.geysermc.geyser.util.FileUtils;
@ -57,7 +59,7 @@ import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.UUID; import java.util.UUID;
public class GeyserViaProxyPlugin extends ViaProxyPlugin implements GeyserBootstrap { public class GeyserViaProxyPlugin extends ViaProxyPlugin implements GeyserBootstrap, EventRegistrar {
public static final File ROOT_FOLDER = new File(PluginManager.PLUGINS_DIR, "Geyser"); public static final File ROOT_FOLDER = new File(PluginManager.PLUGINS_DIR, "Geyser");
@ -120,6 +122,7 @@ public class GeyserViaProxyPlugin extends ViaProxyPlugin implements GeyserBootst
} }
this.geyser = GeyserImpl.load(PlatformType.VIAPROXY, this); this.geyser = GeyserImpl.load(PlatformType.VIAPROXY, this);
this.geyser.eventBus().register(this, new GeyserServerTransferListener());
LoopbackUtil.checkAndApplyLoopback(this.logger); LoopbackUtil.checkAndApplyLoopback(this.logger);
} }

Datei anzeigen

@ -0,0 +1,62 @@
/*
* 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.viaproxy.listener;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.net.HostAndPort;
import org.geysermc.event.PostOrder;
import org.geysermc.event.subscribe.Subscribe;
import org.geysermc.geyser.api.event.bedrock.SessionLoginEvent;
import org.geysermc.geyser.api.event.java.ServerTransferEvent;
import org.geysermc.geyser.session.GeyserSession;
import java.util.Map;
import java.util.concurrent.TimeUnit;
public class GeyserServerTransferListener {
private final Cache<String, Map<String, byte[]>> cookieStorages = CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES).build();
@Subscribe(postOrder = PostOrder.FIRST)
private void onServerTransfer(final ServerTransferEvent event) {
this.cookieStorages.put(event.connection().xuid(), event.cookies());
final GeyserSession geyserSession = (GeyserSession) event.connection();
final HostAndPort hostAndPort = HostAndPort.fromString(geyserSession.getClientData().getServerAddress()).withDefaultPort(19132);
event.bedrockHost(hostAndPort.getHost());
event.bedrockPort(hostAndPort.getPort());
}
@Subscribe(postOrder = PostOrder.FIRST)
private void onSessionLogin(final SessionLoginEvent event) {
final Map<String, byte[]> cookies = this.cookieStorages.asMap().remove(event.connection().xuid());
if (cookies != null) {
event.cookies(cookies);
event.transferring(true);
}
}
}

Datei anzeigen

@ -12,6 +12,9 @@ repositories {
} }
dependencies { dependencies {
// this is OK as long as the same version catalog is used in the main build and build-logic
// see https://github.com/gradle/gradle/issues/15383#issuecomment-779893192
implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location))
implementation(libs.indra) implementation(libs.indra)
implementation(libs.shadow) implementation(libs.shadow)
implementation(libs.architectury.plugin) implementation(libs.architectury.plugin)

Datei anzeigen

@ -0,0 +1,6 @@
import org.gradle.accessors.dm.LibrariesForLibs
import org.gradle.api.Project
import org.gradle.kotlin.dsl.getByType
val Project.libs: LibrariesForLibs
get() = rootProject.extensions.getByType()

Datei anzeigen

@ -8,7 +8,6 @@ plugins {
id("geyser.publish-conventions") id("geyser.publish-conventions")
id("architectury-plugin") id("architectury-plugin")
id("dev.architectury.loom") id("dev.architectury.loom")
id("com.modrinth.minotaur")
} }
// These are provided by Minecraft/modded platforms already, no need to include them // These are provided by Minecraft/modded platforms already, no need to include them
@ -39,7 +38,7 @@ provided("io.netty", "netty-resolver-dns-native-macos")
provided("org.ow2.asm", "asm") provided("org.ow2.asm", "asm")
architectury { architectury {
minecraft = "1.20.5" minecraft = libs.minecraft.get().version as String
} }
loom { loom {
@ -83,7 +82,7 @@ tasks {
register("remapModrinthJar", RemapJarTask::class) { register("remapModrinthJar", RemapJarTask::class) {
dependsOn(shadowJar) dependsOn(shadowJar)
inputFile.set(shadowJar.get().archiveFile) inputFile.set(shadowJar.get().archiveFile)
archiveVersion.set(project.version.toString() + "+build." + System.getenv("GITHUB_RUN_NUMBER")) archiveVersion.set(project.version.toString() + "+build." + System.getenv("BUILD_NUMBER"))
archiveClassifier.set("") archiveClassifier.set("")
} }
} }
@ -93,7 +92,7 @@ afterEvaluate {
// These are shaded, no need to JiJ them // These are shaded, no need to JiJ them
configurations["shadow"].dependencies.forEach {shadowed -> configurations["shadow"].dependencies.forEach {shadowed ->
println("Not including shadowed dependency: ${shadowed.group}:${shadowed.name}") //println("Not including shadowed dependency: ${shadowed.group}:${shadowed.name}")
providedDependencies.add("${shadowed.group}:${shadowed.name}") providedDependencies.add("${shadowed.group}:${shadowed.name}")
} }
@ -101,39 +100,24 @@ afterEvaluate {
configurations["includeTransitive"].resolvedConfiguration.resolvedArtifacts.forEach { dep -> configurations["includeTransitive"].resolvedConfiguration.resolvedArtifacts.forEach { dep ->
if (!providedDependencies.contains("${dep.moduleVersion.id.group}:${dep.moduleVersion.id.name}") if (!providedDependencies.contains("${dep.moduleVersion.id.group}:${dep.moduleVersion.id.name}")
and !providedDependencies.contains("${dep.moduleVersion.id.group}:.*")) { and !providedDependencies.contains("${dep.moduleVersion.id.group}:.*")) {
println("Including dependency via JiJ: ${dep.id}") //println("Including dependency via JiJ: ${dep.id}")
dependencies.add("include", dep.moduleVersion.id.toString()) dependencies.add("include", dep.moduleVersion.id.toString())
} else { } else {
println("Not including ${dep.id} for ${project.name}!") //println("Not including ${dep.id} for ${project.name}!")
} }
} }
} }
dependencies { dependencies {
minecraft("com.mojang:minecraft:1.20.5") minecraft(libs.minecraft)
mappings(loom.officialMojangMappings()) mappings(loom.officialMojangMappings())
} }
repositories { repositories {
// mavenLocal() // mavenLocal()
maven("https://repo.opencollab.dev/maven-releases/") maven("https://repo.opencollab.dev/main")
maven("https://repo.opencollab.dev/maven-snapshots/")
maven("https://jitpack.io") maven("https://jitpack.io")
maven("https://oss.sonatype.org/content/repositories/snapshots/") maven("https://oss.sonatype.org/content/repositories/snapshots/")
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/") maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
maven("https://maven.neoforged.net/releases") maven("https://maven.neoforged.net/releases")
} }
modrinth {
token.set(System.getenv("MODRINTH_TOKEN")) // Even though this is the default value, apparently this prevents GitHub Actions caching the token?
projectId.set("wKkoqHrH")
versionNumber.set(project.version as String + "-" + System.getenv("GITHUB_RUN_NUMBER"))
versionType.set("beta")
changelog.set("A changelog can be found at https://github.com/GeyserMC/Geyser/commits")
syncBodyFrom.set(rootProject.file("README.md").readText())
uploadFile.set(tasks.getByPath("remapModrinthJar"))
gameVersions.addAll("1.20.5", "1.20.6")
failSilently.set(true)
}

Datei anzeigen

@ -0,0 +1,18 @@
plugins {
id("com.modrinth.minotaur")
}
// Ensure that the readme is synched
tasks.modrinth.get().dependsOn(tasks.modrinthSyncBody)
modrinth {
token.set(System.getenv("MODRINTH_TOKEN") ?: "") // Even though this is the default value, apparently this prevents GitHub Actions caching the token?
projectId.set("geyser")
versionNumber.set(project.version as String + "-" + System.getenv("BUILD_NUMBER"))
versionType.set("beta")
changelog.set(System.getenv("CHANGELOG") ?: "")
gameVersions.add(libs.minecraft.get().version as String)
failSilently.set(true)
syncBodyFrom.set(rootProject.file("README.md").readText())
}

Datei anzeigen

@ -26,6 +26,14 @@ val moddedPlatforms = setOf(
projects.mod projects.mod
).map { it.dependencyProject } ).map { it.dependencyProject }
val modrinthPlatforms = setOf(
projects.bungeecord,
projects.fabric,
projects.neoforge,
projects.spigot,
projects.velocity
).map { it.dependencyProject }
subprojects { subprojects {
apply { apply {
plugin("java-library") plugin("java-library")
@ -38,4 +46,10 @@ subprojects {
in moddedPlatforms -> plugins.apply("geyser.modded-conventions") in moddedPlatforms -> plugins.apply("geyser.modded-conventions")
else -> plugins.apply("geyser.base-conventions") else -> plugins.apply("geyser.base-conventions")
} }
// Not combined with platform-conventions as that also contains
// platforms which we cant publish to modrinth
if (modrinthPlatforms.contains(this)) {
plugins.apply("geyser.modrinth-uploading-conventions")
}
} }

Datei anzeigen

@ -1,6 +1,6 @@
import net.kyori.blossom.BlossomExtension
plugins { plugins {
// Allow blossom to mark sources root of templates
idea
alias(libs.plugins.blossom) alias(libs.plugins.blossom)
id("geyser.publish-conventions") id("geyser.publish-conventions")
} }
@ -24,7 +24,6 @@ dependencies {
implementation(libs.websocket) implementation(libs.websocket)
api(libs.bundles.protocol) api(libs.bundles.protocol)
implementation(libs.blockstateupdater)
api(libs.mcauthlib) api(libs.mcauthlib)
api(libs.mcprotocollib) { api(libs.mcprotocollib) {
@ -76,7 +75,7 @@ tasks.processResources {
expand( expand(
"branch" to info.branch, "branch" to info.branch,
"buildNumber" to info.buildNumber, "buildNumber" to info.buildNumber,
"projectVersion" to project.version, "projectVersion" to info.version,
"commit" to info.commit, "commit" to info.commit,
"commitAbbrev" to info.commitAbbrev, "commitAbbrev" to info.commitAbbrev,
"commitMessage" to info.commitMessage, "commitMessage" to info.commitMessage,
@ -85,21 +84,30 @@ tasks.processResources {
} }
} }
configure<BlossomExtension> { sourceSets {
val mainFile = "src/main/java/org/geysermc/geyser/GeyserImpl.java" main {
blossom {
val info = GitInfo() val info = GitInfo()
javaSources {
replaceToken("\${version}", "${project.version} (${info.gitVersion})", mainFile) property("version", "${info.version} (${info.gitVersion})")
replaceToken("\${gitVersion}", info.gitVersion, mainFile) property("gitVersion", info.gitVersion)
replaceToken("\${buildNumber}", info.buildNumber, mainFile) property("buildNumber", info.buildNumber.toString())
replaceToken("\${branch}", info.branch, mainFile) property("branch", info.branch)
replaceToken("\${commit}", info.commit, mainFile) property("commit", info.commit)
replaceToken("\${repository}", info.repository, mainFile) property("repository", info.repository)
property("devVersion", info.isDev.toString())
}
}
}
} }
fun Project.buildNumber(): Int = fun buildNumber(): Int =
(System.getenv("BUILD_NUMBER"))?.let { Integer.parseInt(it) } ?: -1 (System.getenv("BUILD_NUMBER"))?.let { Integer.parseInt(it) } ?: -1
fun isDevBuild(branch: String, repository: String): Boolean {
return branch != "master" || repository.equals("https://github.com/GeyserMC/Geyser", ignoreCase = true).not()
}
inner class GitInfo { inner class GitInfo {
val branch: String val branch: String
val commit: String val commit: String
@ -112,22 +120,25 @@ inner class GitInfo {
val commitMessage: String val commitMessage: String
val repository: String val repository: String
val isDev: Boolean
init { init {
// On Jenkins, a detached head is checked out, so indra cannot determine the branch. branch = indraGit.branchName() ?: "DEV"
// Fortunately, this environment variable is available.
branch = indraGit.branchName() ?: System.getenv("BRANCH_NAME") ?: "DEV"
val commit = indraGit.commit() val commit = indraGit.commit()
this.commit = commit?.name ?: "0".repeat(40) this.commit = commit?.name ?: "0".repeat(40)
commitAbbrev = commit?.name?.substring(0, 7) ?: "0".repeat(7) commitAbbrev = commit?.name?.substring(0, 7) ?: "0".repeat(7)
gitVersion = "git-${branch}-${commitAbbrev}" gitVersion = "git-${branch}-${commitAbbrev}"
version = "${project.version} ($gitVersion)"
buildNumber = buildNumber()
val git = indraGit.git() val git = indraGit.git()
commitMessage = git?.commit()?.message ?: "" commitMessage = git?.commit()?.message ?: ""
repository = git?.repository?.config?.getString("remote", "origin", "url") ?: "" repository = git?.repository?.config?.getString("remote", "origin", "url") ?: ""
buildNumber = buildNumber()
isDev = isDevBuild(branch, repository)
val projectVersion = if (isDev) project.version else project.version.toString().replace("SNAPSHOT", "b${buildNumber}")
version = "$projectVersion ($gitVersion)"
} }
} }

Datei anzeigen

@ -23,18 +23,20 @@
* @link https://github.com/GeyserMC/Geyser * @link https://github.com/GeyserMC/Geyser
*/ */
package org.geysermc.geyser.level.block; package org.geysermc.geyser;
/** // The constants are replaced before compilation
* This stores all values of double chests that are part of the Java block state. public class BuildData {
* public static final String GIT_VERSION = "{{ gitVersion }}";
* @param isFacingEast If true, then chest is facing east/west; if false, south/north public static final String VERSION = "{{ version }}";
* @param isDirectionPositive If true, direction is positive (east/south); if false, direction is negative (west/north)
* @param isLeft If true, chest is the left of a pair; if false, chest is the right of a pair.
*/
public record DoubleChestValue(
boolean isFacingEast,
boolean isDirectionPositive,
boolean isLeft) {
public static final String BUILD_NUMBER = "{{ buildNumber }}";
public static final String BRANCH = "{{ branch }}";
public static final String COMMIT = "{{ commit }}";
public static final String REPOSITORY = "{{ repository }}";
private static final String DEV = "{{ devVersion }}";
public static boolean isDevBuild() {
return Boolean.parseBoolean(DEV);
}
} }

Datei anzeigen

@ -34,7 +34,7 @@ public final class Constants {
public static final String NEWS_OVERVIEW_URL = "https://api.geysermc.org/v2/news/"; public static final String NEWS_OVERVIEW_URL = "https://api.geysermc.org/v2/news/";
public static final String NEWS_PROJECT_NAME = "geyser"; public static final String NEWS_PROJECT_NAME = "geyser";
public static final String FLOODGATE_DOWNLOAD_LOCATION = "https://ci.opencollab.dev/job/GeyserMC/job/Floodgate/job/master/"; public static final String FLOODGATE_DOWNLOAD_LOCATION = "https://geysermc.org/download#floodgate";
public static final String GEYSER_DOWNLOAD_LOCATION = "https://geysermc.org/download"; public static final String GEYSER_DOWNLOAD_LOCATION = "https://geysermc.org/download";
public static final String UPDATE_PERMISSION = "geyser.update"; public static final String UPDATE_PERMISSION = "geyser.update";

Datei anzeigen

@ -45,6 +45,7 @@ import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
import org.geysermc.api.Geyser; import org.geysermc.api.Geyser;
import org.geysermc.cumulus.form.Form; import org.geysermc.cumulus.form.Form;
import org.geysermc.cumulus.form.util.FormBuilder; import org.geysermc.cumulus.form.util.FormBuilder;
import org.geysermc.erosion.packet.Packets;
import org.geysermc.floodgate.crypto.AesCipher; import org.geysermc.floodgate.crypto.AesCipher;
import org.geysermc.floodgate.crypto.AesKeyProducer; import org.geysermc.floodgate.crypto.AesKeyProducer;
import org.geysermc.floodgate.crypto.Base64Topping; import org.geysermc.floodgate.crypto.Base64Topping;
@ -77,6 +78,7 @@ import org.geysermc.geyser.scoreboard.ScoreboardUpdater;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.PendingMicrosoftAuthentication; import org.geysermc.geyser.session.PendingMicrosoftAuthentication;
import org.geysermc.geyser.session.SessionManager; import org.geysermc.geyser.session.SessionManager;
import org.geysermc.geyser.session.cache.RegistryCache;
import org.geysermc.geyser.skin.FloodgateSkinUploader; import org.geysermc.geyser.skin.FloodgateSkinUploader;
import org.geysermc.geyser.skin.ProvidedSkins; import org.geysermc.geyser.skin.ProvidedSkins;
import org.geysermc.geyser.skin.SkinProvider; import org.geysermc.geyser.skin.SkinProvider;
@ -113,13 +115,14 @@ public class GeyserImpl implements GeyserApi {
.enable(JsonParser.Feature.ALLOW_SINGLE_QUOTES); .enable(JsonParser.Feature.ALLOW_SINGLE_QUOTES);
public static final String NAME = "Geyser"; public static final String NAME = "Geyser";
public static final String GIT_VERSION = "${gitVersion}"; public static final String GIT_VERSION = BuildData.GIT_VERSION;
public static final String VERSION = "${version}"; public static final String VERSION = BuildData.VERSION;
public static final String BUILD_NUMBER = "${buildNumber}"; public static final String BUILD_NUMBER = BuildData.BUILD_NUMBER;
public static final String BRANCH = "${branch}"; public static final String BRANCH = BuildData.BRANCH;
public static final String COMMIT = "${commit}"; public static final String COMMIT = BuildData.COMMIT;
public static final String REPOSITORY = "${repository}"; public static final String REPOSITORY = BuildData.REPOSITORY;
public static final boolean IS_DEV = BuildData.isDevBuild();
/** /**
* Oauth client ID for Microsoft authentication * Oauth client ID for Microsoft authentication
@ -205,12 +208,20 @@ public class GeyserImpl implements GeyserApi {
logger.info(""); logger.info("");
logger.info(GeyserLocale.getLocaleStringLog("geyser.core.load", NAME, VERSION)); logger.info(GeyserLocale.getLocaleStringLog("geyser.core.load", NAME, VERSION));
logger.info(""); logger.info("");
if (IS_DEV) {
// TODO cloud use language string
//logger.info(GeyserLocale.getLocaleStringLog("geyser.core.dev_build", "https://discord.gg/geysermc"));
logger.info("You are running a development build of Geyser! Please report any bugs you find on our Discord server: %s".formatted("https://discord.gg/geysermc"));
logger.info("");
}
logger.info("******************************************"); logger.info("******************************************");
/* Initialize registries */ /* Initialize registries */
Registries.init(); Registries.init();
BlockRegistries.init(); BlockRegistries.init();
RegistryCache.init();
/* Initialize translators */ /* Initialize translators */
EntityDefinitions.init(); EntityDefinitions.init();
MessageTranslator.init(); MessageTranslator.init();
@ -383,7 +394,7 @@ public class GeyserImpl implements GeyserApi {
this.newsHandler = new NewsHandler(BRANCH, this.buildNumber()); this.newsHandler = new NewsHandler(BRANCH, this.buildNumber());
//Packets.initGeyser(); Packets.initGeyser();
if (Epoll.isAvailable()) { if (Epoll.isAvailable()) {
this.erosionUnixListener = new UnixSocketClientListener(); this.erosionUnixListener = new UnixSocketClientListener();
@ -680,6 +691,7 @@ public class GeyserImpl implements GeyserApi {
* *
* @return true if the version number is not 'DEV'. * @return true if the version number is not 'DEV'.
*/ */
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
public boolean isProductionEnvironment() { public boolean isProductionEnvironment() {
// First is if Blossom runs, second is if Blossom doesn't run // First is if Blossom runs, second is if Blossom doesn't run
//noinspection ConstantConditions,MismatchedStringCase - changes in production //noinspection ConstantConditions,MismatchedStringCase - changes in production
@ -766,6 +778,7 @@ public class GeyserImpl implements GeyserApi {
return 0; return 0;
} }
//noinspection DataFlowIssue
return Integer.parseInt(BUILD_NUMBER); return Integer.parseInt(BUILD_NUMBER);
} }

Datei anzeigen

@ -25,8 +25,8 @@
package org.geysermc.geyser.command.defaults; package org.geysermc.geyser.command.defaults;
import com.fasterxml.jackson.databind.JsonNode;
import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec; import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
import org.geysermc.geyser.Constants;
import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.util.PlatformType; import org.geysermc.geyser.api.util.PlatformType;
import org.geysermc.geyser.command.GeyserCommand; import org.geysermc.geyser.command.GeyserCommand;
@ -37,8 +37,7 @@ import org.geysermc.geyser.text.ChatColor;
import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.util.WebUtils; import org.geysermc.geyser.util.WebUtils;
import java.net.URLEncoder; import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List; import java.util.List;
public class VersionCommand extends GeyserCommand { public class VersionCommand extends GeyserCommand {
@ -72,29 +71,38 @@ public class VersionCommand extends GeyserCommand {
GeyserImpl.NAME, GeyserImpl.VERSION, javaVersions, bedrockVersions)); GeyserImpl.NAME, GeyserImpl.VERSION, javaVersions, bedrockVersions));
// Disable update checking in dev mode and for players in Geyser Standalone // Disable update checking in dev mode and for players in Geyser Standalone
if (GeyserImpl.getInstance().isProductionEnvironment() && !(!sender.isConsole() && geyser.getPlatformType() == PlatformType.STANDALONE)) { if (!GeyserImpl.getInstance().isProductionEnvironment() || (!sender.isConsole() && geyser.getPlatformType() == PlatformType.STANDALONE)) {
return;
}
if (GeyserImpl.IS_DEV) {
// TODO cloud use language string
sender.sendMessage("You are running a development build of Geyser! Please report any bugs you find on our Discord server: %s"
.formatted("https://discord.gg/geysermc"));
//sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.core.dev_build", sender.locale(), "https://discord.gg/geysermc"));
return;
}
sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.checking", sender.locale())); sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.checking", sender.locale()));
try { try {
String buildXML = WebUtils.getBody("https://ci.opencollab.dev/job/GeyserMC/job/Geyser/job/" + int buildNumber = this.geyser.buildNumber();
URLEncoder.encode(GeyserImpl.BRANCH, StandardCharsets.UTF_8) + "/lastSuccessfulBuild/api/xml?xpath=//buildNumber"); JsonNode response = WebUtils.getJson("https://download.geysermc.org/v2/projects/geyser/versions/latest/builds/latest");
if (buildXML.startsWith("<buildNumber>")) { int latestBuildNumber = response.get("build").asInt();
int latestBuildNum = Integer.parseInt(buildXML.replaceAll("<(\\\\)?(/)?buildNumber>", "").trim());
int buildNum = this.geyser.buildNumber(); if (latestBuildNumber == buildNumber) {
if (latestBuildNum == buildNum) {
sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.no_updates", sender.locale())); sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.no_updates", sender.locale()));
} else { return;
sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.outdated",
sender.locale(), (latestBuildNum - buildNum), Constants.GEYSER_DOWNLOAD_LOCATION));
} }
} else {
throw new AssertionError("buildNumber missing"); sender.sendMessage(GeyserLocale.getPlayerLocaleString(
} "geyser.commands.version.outdated",
} catch (Exception e) { sender.locale(), (latestBuildNumber - buildNumber), "https://geysermc.org/download"
));
} catch (IOException e) {
GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.commands.version.failed"), e); GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.commands.version.failed"), e);
sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.version.failed", sender.locale())); sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.version.failed", sender.locale()));
} }
} }
}
@Override @Override
public boolean isSuggestedOpOnly() { public boolean isSuggestedOpOnly() {

Datei anzeigen

@ -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 * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@ -72,8 +72,10 @@ public interface GeyserConfiguration {
boolean isDebugMode(); boolean isDebugMode();
@Deprecated
boolean isAllowThirdPartyCapes(); boolean isAllowThirdPartyCapes();
@Deprecated
boolean isAllowThirdPartyEars(); boolean isAllowThirdPartyEars();
String getShowCooldown(); String getShowCooldown();

Datei anzeigen

@ -94,7 +94,7 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
private boolean debugMode = false; private boolean debugMode = false;
@JsonProperty("allow-third-party-capes") @JsonProperty("allow-third-party-capes")
private boolean allowThirdPartyCapes = true; private boolean allowThirdPartyCapes = false;
@JsonProperty("show-cooldown") @JsonProperty("show-cooldown")
private String showCooldown = "title"; private String showCooldown = "title";

Datei anzeigen

@ -47,6 +47,7 @@ import java.util.function.BiConsumer;
* metadata translators needed to translate the properties sent from the server. The translators are structured in such * metadata translators needed to translate the properties sent from the server. The translators are structured in such
* a way that inserting a new one (for example in version updates) is convenient. * a way that inserting a new one (for example in version updates) is convenient.
* *
* @param identifier the Bedrock identifier of this entity
* @param <T> the entity type this definition represents * @param <T> the entity type this definition represents
*/ */
public record EntityDefinition<T extends Entity>(EntityFactory<T> factory, EntityType entityType, String identifier, public record EntityDefinition<T extends Entity>(EntityFactory<T> factory, EntityType entityType, String identifier,

Datei anzeigen

@ -25,6 +25,9 @@
package org.geysermc.geyser.entity; package org.geysermc.geyser.entity;
import org.geysermc.geyser.entity.type.AbstractWindChargeEntity;
import org.geysermc.geyser.entity.factory.EntityFactory;
import org.geysermc.geyser.entity.type.living.monster.raid.RavagerEntity;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.MetadataType; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.MetadataType;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.FloatEntityMetadata; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.FloatEntityMetadata;
@ -62,6 +65,9 @@ public final class EntityDefinitions {
public static final EntityDefinition<BeeEntity> BEE; public static final EntityDefinition<BeeEntity> BEE;
public static final EntityDefinition<BlazeEntity> BLAZE; public static final EntityDefinition<BlazeEntity> BLAZE;
public static final EntityDefinition<BoatEntity> BOAT; public static final EntityDefinition<BoatEntity> BOAT;
public static final EntityDefinition<BoggedEntity> BOGGED;
public static final EntityDefinition<BreezeEntity> BREEZE;
public static final EntityDefinition<AbstractWindChargeEntity> BREEZE_WIND_CHARGE;
public static final EntityDefinition<CamelEntity> CAMEL; public static final EntityDefinition<CamelEntity> CAMEL;
public static final EntityDefinition<CatEntity> CAT; public static final EntityDefinition<CatEntity> CAT;
public static final EntityDefinition<SpiderEntity> CAVE_SPIDER; public static final EntityDefinition<SpiderEntity> CAVE_SPIDER;
@ -132,7 +138,7 @@ public final class EntityDefinitions {
public static final EntityDefinition<ThrownPotionEntity> POTION; public static final EntityDefinition<ThrownPotionEntity> POTION;
public static final EntityDefinition<PufferFishEntity> PUFFERFISH; public static final EntityDefinition<PufferFishEntity> PUFFERFISH;
public static final EntityDefinition<RabbitEntity> RABBIT; public static final EntityDefinition<RabbitEntity> RABBIT;
public static final EntityDefinition<RaidParticipantEntity> RAVAGER; public static final EntityDefinition<RavagerEntity> RAVAGER;
public static final EntityDefinition<AbstractFishEntity> SALMON; public static final EntityDefinition<AbstractFishEntity> SALMON;
public static final EntityDefinition<SheepEntity> SHEEP; public static final EntityDefinition<SheepEntity> SHEEP;
public static final EntityDefinition<ShulkerEntity> SHULKER; public static final EntityDefinition<ShulkerEntity> SHULKER;
@ -164,6 +170,7 @@ public final class EntityDefinitions {
public static final EntityDefinition<VindicatorEntity> VINDICATOR; public static final EntityDefinition<VindicatorEntity> VINDICATOR;
public static final EntityDefinition<AbstractMerchantEntity> WANDERING_TRADER; public static final EntityDefinition<AbstractMerchantEntity> WANDERING_TRADER;
public static final EntityDefinition<WardenEntity> WARDEN; public static final EntityDefinition<WardenEntity> WARDEN;
public static final EntityDefinition<AbstractWindChargeEntity> WIND_CHARGE;
public static final EntityDefinition<RaidParticipantEntity> WITCH; public static final EntityDefinition<RaidParticipantEntity> WITCH;
public static final EntityDefinition<WitherEntity> WITHER; public static final EntityDefinition<WitherEntity> WITHER;
public static final EntityDefinition<AbstractSkeletonEntity> WITHER_SKELETON; public static final EntityDefinition<AbstractSkeletonEntity> WITHER_SKELETON;
@ -234,7 +241,7 @@ public final class EntityDefinitions {
.addTranslator(MetadataType.BOOLEAN, .addTranslator(MetadataType.BOOLEAN,
(enderCrystalEntity, entityMetadata) -> enderCrystalEntity.setFlag(EntityFlag.SHOW_BOTTOM, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue())) // There is a base located on the ender crystal (enderCrystalEntity, entityMetadata) -> enderCrystalEntity.setFlag(EntityFlag.SHOW_BOTTOM, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue())) // There is a base located on the ender crystal
.build(); .build();
EXPERIENCE_ORB = EntityDefinition.<ExpOrbEntity>inherited(null, entityBase) EXPERIENCE_ORB = EntityDefinition.inherited(ExpOrbEntity::new, entityBase)
.type(EntityType.EXPERIENCE_ORB) .type(EntityType.EXPERIENCE_ORB)
.identifier("minecraft:xp_orb") .identifier("minecraft:xp_orb")
.build(); .build();
@ -296,6 +303,7 @@ public final class EntityDefinitions {
TNT = EntityDefinition.inherited(TNTEntity::new, entityBase) TNT = EntityDefinition.inherited(TNTEntity::new, entityBase)
.type(EntityType.TNT) .type(EntityType.TNT)
.heightAndWidth(0.98f) .heightAndWidth(0.98f)
.offset(0.49f)
.addTranslator(MetadataType.INT, TNTEntity::setFuseLength) .addTranslator(MetadataType.INT, TNTEntity::setFuseLength)
.build(); .build();
@ -373,6 +381,18 @@ public final class EntityDefinitions {
.heightAndWidth(0.25f) .heightAndWidth(0.25f)
.build(); .build();
EntityFactory<AbstractWindChargeEntity> windChargeSupplier = AbstractWindChargeEntity::new;
BREEZE_WIND_CHARGE = EntityDefinition.inherited(windChargeSupplier, entityBase)
.type(EntityType.BREEZE_WIND_CHARGE)
.identifier("minecraft:breeze_wind_charge_projectile")
.heightAndWidth(0.3125f)
.build();
WIND_CHARGE = EntityDefinition.inherited(windChargeSupplier, entityBase)
.type(EntityType.WIND_CHARGE)
.identifier("minecraft:wind_charge_projectile")
.heightAndWidth(0.3125f)
.build();
EntityDefinition<AbstractArrowEntity> abstractArrowBase = EntityDefinition.inherited(AbstractArrowEntity::new, entityBase) EntityDefinition<AbstractArrowEntity> abstractArrowBase = EntityDefinition.inherited(AbstractArrowEntity::new, entityBase)
.addTranslator(MetadataType.BYTE, AbstractArrowEntity::setArrowFlags) .addTranslator(MetadataType.BYTE, AbstractArrowEntity::setArrowFlags)
.addTranslator(null) // "Piercing level" .addTranslator(null) // "Piercing level"
@ -501,11 +521,20 @@ public final class EntityDefinitions {
.height(0.9f).width(0.5f) .height(0.9f).width(0.5f)
.addTranslator(MetadataType.BYTE, BatEntity::setBatFlags) .addTranslator(MetadataType.BYTE, BatEntity::setBatFlags)
.build(); .build();
BOGGED = EntityDefinition.inherited(BoggedEntity::new, mobEntityBase)
.type(EntityType.BOGGED)
.height(1.99f).width(0.6f)
.addTranslator(MetadataType.BOOLEAN, BoggedEntity::setSheared)
.build();
BLAZE = EntityDefinition.inherited(BlazeEntity::new, mobEntityBase) BLAZE = EntityDefinition.inherited(BlazeEntity::new, mobEntityBase)
.type(EntityType.BLAZE) .type(EntityType.BLAZE)
.height(1.8f).width(0.6f) .height(1.8f).width(0.6f)
.addTranslator(MetadataType.BYTE, BlazeEntity::setBlazeFlags) .addTranslator(MetadataType.BYTE, BlazeEntity::setBlazeFlags)
.build(); .build();
BREEZE = EntityDefinition.inherited(BreezeEntity::new, mobEntityBase)
.type(EntityType.BREEZE)
.height(1.77f).width(0.6f)
.build();
CREEPER = EntityDefinition.inherited(CreeperEntity::new, mobEntityBase) CREEPER = EntityDefinition.inherited(CreeperEntity::new, mobEntityBase)
.type(EntityType.CREEPER) .type(EntityType.CREEPER)
.height(1.7f).width(0.6f) .height(1.7f).width(0.6f)
@ -745,9 +774,9 @@ public final class EntityDefinitions {
.type(EntityType.PILLAGER) .type(EntityType.PILLAGER)
.height(1.8f).width(0.6f) .height(1.8f).width(0.6f)
.offset(1.62f) .offset(1.62f)
.addTranslator(null) // Charging; doesn't have an equivalent on Bedrock //TODO check .addTranslator(MetadataType.BOOLEAN, PillagerEntity::setChargingCrossbow)
.build(); .build();
RAVAGER = EntityDefinition.inherited(raidParticipantEntityBase.factory(), raidParticipantEntityBase) RAVAGER = EntityDefinition.inherited(RavagerEntity::new, raidParticipantEntityBase)
.type(EntityType.RAVAGER) .type(EntityType.RAVAGER)
.height(1.9f).width(1.2f) .height(1.9f).width(1.2f)
.build(); .build();

Datei anzeigen

@ -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.entity.type;
import org.cloudburstmc.math.vector.Vector3f;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.session.GeyserSession;
import java.util.UUID;
/**
* Note that, as of 1.21, a wind charge entity does not actually implement the thrown item. We're just reusing
* the "hide until far away" aspect.
*/
public class AbstractWindChargeEntity extends ThrowableItemEntity {
public AbstractWindChargeEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
@Override
public void tick() {
super.tick();
}
@Override
protected float getDrag() {
// Always, even in water. As of 1.21.
return 1f;
}
}

Datei anzeigen

@ -28,7 +28,7 @@ package org.geysermc.geyser.entity.type;
import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.inventory.item.TippedArrowPotion; import org.geysermc.geyser.inventory.item.Potion;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata;
@ -46,12 +46,7 @@ public class ArrowEntity extends AbstractArrowEntity {
if (potionColor == -1) { if (potionColor == -1) {
dirtyMetadata.put(EntityDataTypes.CUSTOM_DISPLAY, (byte) 0); dirtyMetadata.put(EntityDataTypes.CUSTOM_DISPLAY, (byte) 0);
} else { } else {
TippedArrowPotion potion = TippedArrowPotion.getByJavaColor(potionColor); dirtyMetadata.put(EntityDataTypes.CUSTOM_DISPLAY, Potion.toTippedArrowId(potionColor));
if (potion != null && potion.getJavaColor() != -1) {
dirtyMetadata.put(EntityDataTypes.CUSTOM_DISPLAY, (byte) potion.getBedrockId());
} else {
dirtyMetadata.put(EntityDataTypes.CUSTOM_DISPLAY, (byte) 0);
}
} }
} }
} }

Datei anzeigen

@ -41,7 +41,7 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import java.util.UUID; import java.util.UUID;
public class BoatEntity extends Entity implements Tickable { public class BoatEntity extends Entity implements Leashable, Tickable {
/** /**
* Required when IS_BUOYANT is sent in order for boats to work in the water. <br> * Required when IS_BUOYANT is sent in order for boats to work in the water. <br>
@ -65,6 +65,8 @@ public class BoatEntity extends Entity implements Tickable {
@Getter @Getter
private int variant; private int variant;
private long leashHolderBedrockId = -1;
// Looks too fast and too choppy with 0.1f, which is how I believe the Microsoftian client handles it // Looks too fast and too choppy with 0.1f, which is how I believe the Microsoftian client handles it
private final float ROWING_SPEED = 0.1f; private final float ROWING_SPEED = 0.1f;
@ -147,8 +149,18 @@ public class BoatEntity extends Entity implements Tickable {
} }
} }
@Override
public void setLeashHolderBedrockId(long bedrockId) {
this.leashHolderBedrockId = bedrockId;
dirtyMetadata.put(EntityDataTypes.LEASH_HOLDER, bedrockId);
}
@Override @Override
protected InteractiveTag testInteraction(Hand hand) { protected InteractiveTag testInteraction(Hand hand) {
InteractiveTag tag = super.testInteraction(hand);
if (tag != InteractiveTag.NONE) {
return tag;
}
if (session.isSneaking()) { if (session.isSneaking()) {
return InteractiveTag.NONE; return InteractiveTag.NONE;
} else if (passengers.size() < 2) { } else if (passengers.size() < 2) {
@ -160,6 +172,10 @@ public class BoatEntity extends Entity implements Tickable {
@Override @Override
public InteractionResult interact(Hand hand) { public InteractionResult interact(Hand hand) {
InteractionResult result = super.interact(hand);
if (result != InteractionResult.PASS) {
return result;
}
if (session.isSneaking()) { if (session.isSneaking()) {
return InteractionResult.PASS; return InteractionResult.PASS;
} else { } else {
@ -191,6 +207,11 @@ public class BoatEntity extends Entity implements Tickable {
} }
} }
@Override
public long leashHolderBedrockId() {
return leashHolderBedrockId;
}
private void sendAnimationPacket(GeyserSession session, Entity rower, AnimatePacket.Action action, float rowTime) { private void sendAnimationPacket(GeyserSession session, Entity rower, AnimatePacket.Action action, float rowTime) {
AnimatePacket packet = new AnimatePacket(); AnimatePacket packet = new AnimatePacket();
packet.setRuntimeEntityId(rower.getGeyserId()); packet.setRuntimeEntityId(rower.getGeyserId());

Datei anzeigen

@ -40,6 +40,7 @@ import org.geysermc.geyser.api.entity.type.GeyserEntity;
import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.entity.GeyserDirtyMetadata; import org.geysermc.geyser.entity.GeyserDirtyMetadata;
import org.geysermc.geyser.entity.properties.GeyserEntityPropertyManager; import org.geysermc.geyser.entity.properties.GeyserEntityPropertyManager;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.text.MessageTranslator; import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.geyser.util.EntityUtils; import org.geysermc.geyser.util.EntityUtils;
@ -59,6 +60,9 @@ import java.util.*;
@Getter @Getter
@Setter @Setter
public class Entity implements GeyserEntity { public class Entity implements GeyserEntity {
private static final boolean PRINT_ENTITY_SPAWN_DEBUG = Boolean.parseBoolean(System.getProperty("Geyser.PrintEntitySpawnDebug", "false"));
protected final GeyserSession session; protected final GeyserSession session;
protected int entityId; protected int entityId;
@ -134,7 +138,7 @@ public class Entity implements GeyserEntity {
this.valid = false; this.valid = false;
this.propertyManager = new GeyserEntityPropertyManager(definition.registeredProperties()); this.propertyManager = definition.registeredProperties() == null ? null : new GeyserEntityPropertyManager(definition.registeredProperties());
setPosition(position); setPosition(position);
setAirSupply(getMaxAir()); setAirSupply(getMaxAir());
@ -181,7 +185,7 @@ public class Entity implements GeyserEntity {
flagsDirty = false; flagsDirty = false;
if (session.getGeyser().getConfig().isDebugMode()) { if (session.getGeyser().getConfig().isDebugMode() && PRINT_ENTITY_SPAWN_DEBUG) {
EntityType type = definition.entityType(); EntityType type = definition.entityType();
String name = type != null ? type.name() : getClass().getSimpleName(); String name = type != null ? type.name() : getClass().getSimpleName();
session.getGeyser().getLogger().debug("Spawned entity " + name + " at location " + position + " with id " + geyserId + " (java id " + entityId + ")"); session.getGeyser().getLogger().debug("Spawned entity " + name + " at location " + position + " with id " + geyserId + " (java id " + entityId + ")");
@ -361,7 +365,7 @@ public class Entity implements GeyserEntity {
return; return;
} }
if (propertyManager.hasProperties()) { if (propertyManager != null && propertyManager.hasProperties()) {
SetEntityDataPacket entityDataPacket = new SetEntityDataPacket(); SetEntityDataPacket entityDataPacket = new SetEntityDataPacket();
entityDataPacket.setRuntimeEntityId(geyserId); entityDataPacket.setRuntimeEntityId(geyserId);
propertyManager.applyIntProperties(entityDataPacket.getProperties().getIntProperties()); propertyManager.applyIntProperties(entityDataPacket.getProperties().getIntProperties());
@ -554,6 +558,17 @@ public class Entity implements GeyserEntity {
* Should usually mirror {@link #interact(Hand)} without any side effects. * Should usually mirror {@link #interact(Hand)} without any side effects.
*/ */
protected InteractiveTag testInteraction(Hand hand) { protected InteractiveTag testInteraction(Hand hand) {
if (isAlive() && this instanceof Leashable leashable) {
if (leashable.leashHolderBedrockId() == session.getPlayerEntity().getGeyserId()) {
// Note this might be client side. Has yet to be an issue though, as of Java 1.21.
return InteractiveTag.REMOVE_LEASH;
}
if (session.getPlayerInventory().getItemInHand(hand).asItem() == Items.LEAD && leashable.canBeLeashed()) {
// We shall leash
return InteractiveTag.LEASH;
}
}
return InteractiveTag.NONE; return InteractiveTag.NONE;
} }
@ -562,6 +577,18 @@ public class Entity implements GeyserEntity {
* to ensure packet parity as well as functionality parity (such as sound effect responses). * to ensure packet parity as well as functionality parity (such as sound effect responses).
*/ */
public InteractionResult interact(Hand hand) { public InteractionResult interact(Hand hand) {
if (isAlive() && this instanceof Leashable leashable) {
if (leashable.leashHolderBedrockId() == session.getPlayerEntity().getGeyserId()) {
// Note this might also update client side (a theoretical Geyser/client desync and Java parity issue).
// Has yet to be an issue though, as of Java 1.21.
return InteractionResult.SUCCESS;
}
if (session.getPlayerInventory().getItemInHand(hand).asItem() == Items.LEAD && leashable.canBeLeashed()) {
// We shall leash
return InteractionResult.SUCCESS;
}
}
return InteractionResult.PASS; return InteractionResult.PASS;
} }

Datei anzeigen

@ -27,11 +27,18 @@ package org.geysermc.geyser.entity.type;
import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.EntityDefinitions;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import java.util.UUID;
public class ExpOrbEntity extends Entity { public class ExpOrbEntity extends Entity {
public ExpOrbEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> entityDefinition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
this(session, 1, entityId, geyserId, position);
}
public ExpOrbEntity(GeyserSession session, int amount, int entityId, long geyserId, Vector3f position) { public ExpOrbEntity(GeyserSession session, int amount, int entityId, long geyserId, Vector3f position) {
super(session, entityId, geyserId, null, EntityDefinitions.EXPERIENCE_ORB, position, Vector3f.ZERO, 0, 0, 0); super(session, entityId, geyserId, null, EntityDefinitions.EXPERIENCE_ORB, position, Vector3f.ZERO, 0, 0, 0);

Datei anzeigen

@ -33,6 +33,7 @@ import org.geysermc.erosion.util.BlockPositionIterator;
import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.EntityDefinitions;
import org.geysermc.geyser.entity.type.player.PlayerEntity; import org.geysermc.geyser.entity.type.player.PlayerEntity;
import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.level.block.type.Block;
import org.geysermc.geyser.level.physics.BoundingBox; import org.geysermc.geyser.level.physics.BoundingBox;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.collision.BlockCollision; import org.geysermc.geyser.translator.collision.BlockCollision;
@ -162,7 +163,7 @@ public class FishingHookEntity extends ThrowableEntity {
*/ */
protected boolean isInAir() { protected boolean isInAir() {
int block = session.getGeyser().getWorldManager().getBlockAt(session, position.toInt()); int block = session.getGeyser().getWorldManager().getBlockAt(session, position.toInt());
return block == BlockStateValues.JAVA_AIR_ID; return block == Block.JAVA_AIR_ID;
} }
@Override @Override

Datei anzeigen

@ -25,14 +25,16 @@
package org.geysermc.geyser.entity.type; package org.geysermc.geyser.entity.type;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.level.block.Blocks;
import org.geysermc.geyser.level.block.property.Properties;
import org.geysermc.geyser.level.block.type.BlockState;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.InteractionResult; import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import java.util.UUID; import java.util.UUID;
@ -51,7 +53,8 @@ public class FurnaceMinecartEntity extends DefaultBlockMinecartEntity {
@Override @Override
public void updateDefaultBlockMetadata() { public void updateDefaultBlockMetadata() {
dirtyMetadata.put(EntityDataTypes.DISPLAY_BLOCK_STATE, session.getBlockMappings().getBedrockBlock(hasFuel ? BlockStateValues.JAVA_FURNACE_LIT_ID : BlockStateValues.JAVA_FURNACE_ID)); BlockState furnace = Blocks.FURNACE.defaultBlockState().withValue(Properties.LIT, hasFuel);
dirtyMetadata.put(EntityDataTypes.DISPLAY_BLOCK_STATE, session.getBlockMappings().getBedrockBlock(furnace));
dirtyMetadata.put(EntityDataTypes.DISPLAY_OFFSET, 6); dirtyMetadata.put(EntityDataTypes.DISPLAY_OFFSET, 6);
} }

Datei anzeigen

@ -80,7 +80,10 @@ public class InteractionEntity extends Entity {
} }
public void setHeight(FloatEntityMetadata height) { public void setHeight(FloatEntityMetadata height) {
setBoundingBoxHeight(height.getPrimitiveValue()); // Bedrock does *not* like high values being placed here
// https://gist.github.com/Owen1212055/f5d59169d3a6a5c32f0c173d57eb199d recommend(s/ed) using the tactic
// https://github.com/GeyserMC/Geyser/issues/4688
setBoundingBoxHeight(Math.min(height.getPrimitiveValue(), 64f));
} }
public void setResponse(BooleanEntityMetadata response) { public void setResponse(BooleanEntityMetadata response) {

Datei anzeigen

@ -34,6 +34,7 @@ import org.cloudburstmc.protocol.bedrock.packet.AddItemEntityPacket;
import org.cloudburstmc.protocol.bedrock.packet.EntityEventPacket; import org.cloudburstmc.protocol.bedrock.packet.EntityEventPacket;
import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.level.block.type.BlockState;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.item.ItemTranslator; import org.geysermc.geyser.translator.item.ItemTranslator;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata;
@ -137,7 +138,7 @@ public class ItemEntity extends ThrowableEntity {
protected float getDrag() { protected float getDrag() {
if (isOnGround()) { if (isOnGround()) {
Vector3i groundBlockPos = position.toInt().down(1); Vector3i groundBlockPos = position.toInt().down(1);
int blockState = session.getGeyser().getWorldManager().getBlockAt(session, groundBlockPos); BlockState blockState = session.getGeyser().getWorldManager().blockAt(session, groundBlockPos);
return BlockStateValues.getSlipperiness(blockState) * 0.98f; return BlockStateValues.getSlipperiness(blockState) * 0.98f;
} }
return 0.98f; return 0.98f;

Datei anzeigen

@ -0,0 +1,44 @@
/*
* 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.entity.type;
/**
* I can haz lead
* (The item, not the mineral)
*/
public interface Leashable {
void setLeashHolderBedrockId(long bedrockId);
long leashHolderBedrockId();
default boolean canBeLeashed() {
return isNotLeashed();
}
default boolean isNotLeashed() {
return leashHolderBedrockId() == -1L;
}
}

Datei anzeigen

@ -352,6 +352,15 @@ public class LivingEntity extends Entity {
session.sendUpstreamPacket(offHandPacket); session.sendUpstreamPacket(offHandPacket);
} }
/**
* Called when a SWING_ARM animation packet is received
*
* @return true if an ATTACK_START event should be used instead
*/
public boolean useArmSwingAttack() {
return false;
}
/** /**
* Attributes are properties of an entity that are generally more runtime-based instead of permanent properties. * Attributes are properties of an entity that are generally more runtime-based instead of permanent properties.
* Movement speed, current attack damage with a weapon, current knockback resistance. * Movement speed, current attack damage with a weapon, current knockback resistance.

Datei anzeigen

@ -30,6 +30,8 @@ import org.cloudburstmc.protocol.bedrock.packet.AddPaintingPacket;
import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.level.PaintingType; import org.geysermc.geyser.level.PaintingType;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.Holder;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.PaintingVariant;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ObjectEntityMetadata; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ObjectEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.object.Direction; import org.geysermc.mcprotocollib.protocol.data.game.entity.object.Direction;
@ -49,8 +51,14 @@ public class PaintingEntity extends Entity {
// Wait until we get the metadata needed // Wait until we get the metadata needed
} }
public void setPaintingType(ObjectEntityMetadata<org.geysermc.mcprotocollib.protocol.data.game.entity.type.PaintingType> entityMetadata) { public void setPaintingType(ObjectEntityMetadata<Holder<PaintingVariant>> entityMetadata) {
PaintingType type = PaintingType.getByPaintingType(entityMetadata.getValue()); if (!entityMetadata.getValue().isId()) {
return;
}
PaintingType type = session.getRegistryCache().paintings().byId(entityMetadata.getValue().id());
if (type == null) {
return;
}
AddPaintingPacket addPaintingPacket = new AddPaintingPacket(); AddPaintingPacket addPaintingPacket = new AddPaintingPacket();
addPaintingPacket.setUniqueEntityId(geyserId); addPaintingPacket.setUniqueEntityId(geyserId);
addPaintingPacket.setRuntimeEntityId(geyserId); addPaintingPacket.setRuntimeEntityId(geyserId);
@ -78,8 +86,12 @@ public class PaintingEntity extends Entity {
private Vector3f fixOffset(PaintingType paintingName) { private Vector3f fixOffset(PaintingType paintingName) {
Vector3f position = super.position; Vector3f position = super.position;
// ViaVersion already adds the offset for us on older versions,
// so no need to do it then otherwise it will be spaced
if (session.isEmulatePost1_18Logic()) {
position = position.add(0.5, 0.5, 0.5); position = position.add(0.5, 0.5, 0.5);
double widthOffset = paintingName.getWidth() > 1 ? 0.5 : 0; }
double widthOffset = paintingName.getWidth() > 1 && paintingName.getWidth() != 3 ? 0.5 : 0;
double heightOffset = paintingName.getHeight() > 1 && paintingName.getHeight() != 3 ? 0.5 : 0; double heightOffset = paintingName.getHeight() > 1 && paintingName.getHeight() != 3 ? 0.5 : 0;
return switch (direction) { return switch (direction) {

Datei anzeigen

@ -28,7 +28,7 @@ package org.geysermc.geyser.entity.type;
import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.level.block.Blocks;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import java.util.UUID; import java.util.UUID;
@ -41,7 +41,7 @@ public class SpawnerMinecartEntity extends DefaultBlockMinecartEntity {
@Override @Override
public void updateDefaultBlockMetadata() { public void updateDefaultBlockMetadata() {
dirtyMetadata.put(EntityDataTypes.DISPLAY_BLOCK_STATE, session.getBlockMappings().getBedrockBlock(BlockStateValues.JAVA_SPAWNER_ID)); dirtyMetadata.put(EntityDataTypes.DISPLAY_BLOCK_STATE, session.getBlockMappings().getBedrockBlock(Blocks.SPAWNER.defaultBlockState()));
dirtyMetadata.put(EntityDataTypes.DISPLAY_OFFSET, 6); dirtyMetadata.put(EntityDataTypes.DISPLAY_OFFSET, 6);
} }
} }

Datei anzeigen

@ -39,7 +39,17 @@ public class TNTEntity extends Entity implements Tickable {
private int currentTick; private int currentTick;
public TNTEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) { public TNTEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw); super(session, entityId, geyserId, uuid, definition, position.add(0, definition.offset(), 0), motion, yaw, pitch, headYaw);
}
@Override
public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, boolean isOnGround) {
super.moveRelative(relX, relY + definition.offset(), relZ, yaw, pitch, isOnGround);
}
@Override
public void moveAbsolute(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) {
super.moveAbsolute(position.add(Vector3f.from(0, definition.offset(), 0)), yaw, pitch, headYaw, isOnGround, teleported);
} }
public void setFuseLength(IntEntityMetadata entityMetadata) { public void setFuseLength(IntEntityMetadata entityMetadata) {

Datei anzeigen

@ -38,7 +38,7 @@ public class AmbientEntity extends MobEntity {
} }
@Override @Override
protected boolean canBeLeashed() { public boolean canBeLeashed() {
return false; return false;
} }
} }

Datei anzeigen

@ -43,7 +43,7 @@ public class DolphinEntity extends WaterEntity {
} }
@Override @Override
protected boolean canBeLeashed() { public boolean canBeLeashed() {
return true; return true;
} }

Datei anzeigen

@ -25,12 +25,12 @@
package org.geysermc.geyser.entity.type.living; package org.geysermc.geyser.entity.type.living;
import lombok.Getter;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.entity.type.Leashable;
import org.geysermc.geyser.entity.type.LivingEntity; import org.geysermc.geyser.entity.type.LivingEntity;
import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.item.Items; import org.geysermc.geyser.item.Items;
@ -43,11 +43,10 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import java.util.UUID; import java.util.UUID;
public class MobEntity extends LivingEntity { public class MobEntity extends LivingEntity implements Leashable {
/** /**
* If another mob is holding this mob by a leash, this variable tracks their Bedrock entity ID. * If another mob is holding this mob by a leash, this variable tracks their Bedrock entity ID.
*/ */
@Getter
private long leashHolderBedrockId; private long leashHolderBedrockId;
public MobEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) { public MobEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
@ -65,6 +64,7 @@ public class MobEntity extends LivingEntity {
setFlag(EntityFlag.NO_AI, (xd & 0x01) == 0x01); setFlag(EntityFlag.NO_AI, (xd & 0x01) == 0x01);
} }
@Override
public void setLeashHolderBedrockId(long bedrockId) { public void setLeashHolderBedrockId(long bedrockId) {
this.leashHolderBedrockId = bedrockId; this.leashHolderBedrockId = bedrockId;
dirtyMetadata.put(EntityDataTypes.LEASH_HOLDER, bedrockId); dirtyMetadata.put(EntityDataTypes.LEASH_HOLDER, bedrockId);
@ -79,10 +79,7 @@ public class MobEntity extends LivingEntity {
return InteractiveTag.REMOVE_LEASH; return InteractiveTag.REMOVE_LEASH;
} else { } else {
GeyserItemStack itemStack = session.getPlayerInventory().getItemInHand(hand); GeyserItemStack itemStack = session.getPlayerInventory().getItemInHand(hand);
if (itemStack.asItem() == Items.LEAD && canBeLeashed()) { if (itemStack.asItem() == Items.NAME_TAG) {
// We shall leash
return InteractiveTag.LEASH;
} else if (itemStack.asItem() == Items.NAME_TAG) {
InteractionResult result = checkInteractWithNameTag(itemStack); InteractionResult result = checkInteractWithNameTag(itemStack);
if (result.consumesAction()) { if (result.consumesAction()) {
return InteractiveTag.NAME; return InteractiveTag.NAME;
@ -99,9 +96,6 @@ public class MobEntity extends LivingEntity {
if (!isAlive()) { if (!isAlive()) {
// dead lol // dead lol
return InteractionResult.PASS; return InteractionResult.PASS;
} else if (leashHolderBedrockId == session.getPlayerEntity().getGeyserId()) {
// TODO looks like the client assumes it will go through and removes the attachment itself?
return InteractionResult.SUCCESS;
} else { } else {
GeyserItemStack itemInHand = session.getPlayerInventory().getItemInHand(hand); GeyserItemStack itemInHand = session.getPlayerInventory().getItemInHand(hand);
InteractionResult result = checkPriorityInteractions(itemInHand); InteractionResult result = checkPriorityInteractions(itemInHand);
@ -115,10 +109,7 @@ public class MobEntity extends LivingEntity {
} }
private InteractionResult checkPriorityInteractions(GeyserItemStack itemInHand) { private InteractionResult checkPriorityInteractions(GeyserItemStack itemInHand) {
if (itemInHand.asItem() == Items.LEAD && canBeLeashed()) { if (itemInHand.asItem() == Items.NAME_TAG) {
// We shall leash
return InteractionResult.SUCCESS;
} else if (itemInHand.asItem() == Items.NAME_TAG) {
InteractionResult result = checkInteractWithNameTag(itemInHand); InteractionResult result = checkInteractWithNameTag(itemInHand);
if (result.consumesAction()) { if (result.consumesAction()) {
return result; return result;
@ -143,12 +134,14 @@ public class MobEntity extends LivingEntity {
return InteractionResult.PASS; return InteractionResult.PASS;
} }
protected boolean canBeLeashed() { @Override
public boolean canBeLeashed() {
return isNotLeashed() && !isEnemy(); return isNotLeashed() && !isEnemy();
} }
protected final boolean isNotLeashed() { @Override
return leashHolderBedrockId == -1L; public long leashHolderBedrockId() {
return leashHolderBedrockId;
} }
/** /**

Datei anzeigen

@ -122,7 +122,7 @@ public class SquidEntity extends WaterEntity implements Tickable {
} }
@Override @Override
protected boolean canBeLeashed() { public boolean canBeLeashed() {
return isNotLeashed(); return isNotLeashed();
} }

Datei anzeigen

@ -38,7 +38,7 @@ public class WaterEntity extends CreatureEntity {
} }
@Override @Override
protected boolean canBeLeashed() { public boolean canBeLeashed() {
return false; return false;
} }
} }

Datei anzeigen

@ -72,7 +72,7 @@ public class AxolotlEntity extends AnimalEntity {
} }
@Override @Override
protected boolean canBeLeashed() { public boolean canBeLeashed() {
return true; return true;
} }

Datei anzeigen

@ -27,6 +27,7 @@ package org.geysermc.geyser.entity.type.living.animal;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
@ -40,6 +41,8 @@ public class HoglinEntity extends AnimalEntity {
public HoglinEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) { public HoglinEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw); super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
dirtyMetadata.put(EntityDataTypes.TARGET_EID, session.getPlayerEntity().getGeyserId());
setFlag(EntityFlag.SHAKING, isShaking());
} }
public void setImmuneToZombification(BooleanEntityMetadata entityMetadata) { public void setImmuneToZombification(BooleanEntityMetadata entityMetadata) {
@ -60,7 +63,7 @@ public class HoglinEntity extends AnimalEntity {
} }
@Override @Override
protected boolean canBeLeashed() { public boolean canBeLeashed() {
return isNotLeashed(); return isNotLeashed();
} }
@ -68,4 +71,9 @@ public class HoglinEntity extends AnimalEntity {
protected boolean isEnemy() { protected boolean isEnemy() {
return true; return true;
} }
@Override
public boolean useArmSwingAttack() {
return true;
}
} }

Datei anzeigen

@ -123,7 +123,7 @@ public class PandaEntity extends AnimalEntity {
} }
@Override @Override
protected boolean canBeLeashed() { public boolean canBeLeashed() {
return false; return false;
} }

Datei anzeigen

@ -56,7 +56,7 @@ public class TurtleEntity extends AnimalEntity {
} }
@Override @Override
protected boolean canBeLeashed() { public boolean canBeLeashed() {
return false; return false;
} }
} }

Datei anzeigen

@ -84,7 +84,7 @@ public abstract class TameableEntity extends AnimalEntity {
} }
@Override @Override
protected boolean canBeLeashed() { public boolean canBeLeashed() {
return isNotLeashed(); return isNotLeashed();
} }
} }

Datei anzeigen

@ -33,36 +33,28 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket; import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.inventory.item.Enchantment;
import org.geysermc.geyser.item.Items; import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.item.enchantment.EnchantmentComponent;
import org.geysermc.geyser.item.type.DyeItem; import org.geysermc.geyser.item.type.DyeItem;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag; import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.util.InteractionResult; import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InteractiveTag; import org.geysermc.geyser.util.InteractiveTag;
import org.geysermc.geyser.util.ItemUtils; import org.geysermc.geyser.util.ItemUtils;
import org.geysermc.mcprotocollib.protocol.data.game.Holder;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.WolfVariant;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ObjectEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack; import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import java.util.Collections; import java.util.Collections;
import java.util.Locale; import java.util.Locale;
import java.util.Set;
import java.util.UUID; import java.util.UUID;
public class WolfEntity extends TameableEntity { public class WolfEntity extends TameableEntity {
/**
* A list of all foods a wolf can eat on Java Edition.
* Used to display interactive tag or particles if needed.
* TODO generate
*/
private static final Set<Item> WOLF_FOODS = Set.of(Items.PUFFERFISH, Items.TROPICAL_FISH, Items.CHICKEN, Items.COOKED_CHICKEN,
Items.PORKCHOP, Items.BEEF, Items.RABBIT, Items.COOKED_PORKCHOP, Items.COOKED_BEEF, Items.ROTTEN_FLESH, Items.MUTTON, Items.COOKED_MUTTON,
Items.COOKED_RABBIT);
private byte collarColor = 14; // Red - default private byte collarColor = 14; // Red - default
private boolean isCurseOfBinding = false; private boolean isCurseOfBinding = false;
@ -112,12 +104,14 @@ public class WolfEntity extends TameableEntity {
} }
// 1.20.5+ // 1.20.5+
public void setWolfVariant(IntEntityMetadata entityMetadata) { public void setWolfVariant(ObjectEntityMetadata<Holder<WolfVariant>> entityMetadata) {
WolfVariant wolfVariant = session.getRegistryCache().wolfVariants().byId(entityMetadata.getPrimitiveValue()); entityMetadata.getValue().ifId(id -> {
BuiltInWolfVariant wolfVariant = session.getRegistryCache().wolfVariants().byId(id);
if (wolfVariant == null) { if (wolfVariant == null) {
wolfVariant = WolfVariant.PALE; wolfVariant = BuiltInWolfVariant.PALE;
} }
dirtyMetadata.put(EntityDataTypes.VARIANT, wolfVariant.ordinal()); dirtyMetadata.put(EntityDataTypes.VARIANT, wolfVariant.ordinal());
});
} }
@Override @Override
@ -129,11 +123,11 @@ public class WolfEntity extends TameableEntity {
@Override @Override
public void setChestplate(ItemStack stack) { public void setChestplate(ItemStack stack) {
super.setChestplate(stack); super.setChestplate(stack);
isCurseOfBinding = ItemUtils.getEnchantmentLevel(stack.getDataComponents(), Enchantment.JavaEnchantment.BINDING_CURSE) > 0; isCurseOfBinding = ItemUtils.hasEffect(session, stack, EnchantmentComponent.PREVENT_ARMOR_CHANGE); // TODO test
} }
@Override @Override
protected boolean canBeLeashed() { public boolean canBeLeashed() {
return !getFlag(EntityFlag.ANGRY) && super.canBeLeashed(); return !getFlag(EntityFlag.ANGRY) && super.canBeLeashed();
} }
@ -187,7 +181,7 @@ public class WolfEntity extends TameableEntity {
} }
// Ordered by bedrock id // Ordered by bedrock id
public enum WolfVariant { public enum BuiltInWolfVariant {
PALE, PALE,
ASHEN, ASHEN,
BLACK, BLACK,
@ -198,16 +192,16 @@ public class WolfEntity extends TameableEntity {
STRIPED, STRIPED,
WOODS; WOODS;
private static final WolfVariant[] VALUES = values(); private static final BuiltInWolfVariant[] VALUES = values();
private final String javaIdentifier; private final String javaIdentifier;
WolfVariant() { BuiltInWolfVariant() {
this.javaIdentifier = "minecraft:" + this.name().toLowerCase(Locale.ROOT); this.javaIdentifier = "minecraft:" + this.name().toLowerCase(Locale.ROOT);
} }
public static @Nullable WolfVariant getByJavaIdentifier(String javaIdentifier) { public static @Nullable BuiltInWolfVariant getByJavaIdentifier(String javaIdentifier) {
for (WolfVariant wolfVariant : VALUES) { for (BuiltInWolfVariant wolfVariant : VALUES) {
if (wolfVariant.javaIdentifier.equals(javaIdentifier)) { if (wolfVariant.javaIdentifier.equals(javaIdentifier)) {
return wolfVariant; return wolfVariant;
} }

Datei anzeigen

@ -47,7 +47,7 @@ public class AbstractMerchantEntity extends AgeableEntity {
} }
@Override @Override
protected boolean canBeLeashed() { public boolean canBeLeashed() {
return false; return false;
} }

Datei anzeigen

@ -33,8 +33,9 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.cloudburstmc.protocol.bedrock.packet.MoveEntityAbsolutePacket; import org.cloudburstmc.protocol.bedrock.packet.MoveEntityAbsolutePacket;
import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.level.block.property.Properties;
import org.geysermc.geyser.registry.type.BlockMapping; import org.geysermc.geyser.level.block.type.BedBlock;
import org.geysermc.geyser.level.block.type.BlockState;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.VillagerData; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.VillagerData;
@ -119,29 +120,32 @@ public class VillagerEntity extends AbstractMerchantEntity {
} }
// The bed block // The bed block
int blockId = session.getGeyser().getWorldManager().getBlockAt(session, bedPosition); BlockState state = session.getGeyser().getWorldManager().blockAt(session, bedPosition);
String fullIdentifier = BlockRegistries.JAVA_BLOCKS.getOrDefault(blockId, BlockMapping.DEFAULT).getJavaIdentifier();
// Set the correct position offset and rotation when sleeping // Set the correct position offset and rotation when sleeping
int bedRotation = 0; int bedRotation = 0;
float xOffset = 0; float xOffset = 0;
float zOffset = 0; float zOffset = 0;
if (fullIdentifier.contains("facing=south")) { if (state.block() instanceof BedBlock) {
// bed is facing south switch (state.getValue(Properties.HORIZONTAL_FACING)) {
case SOUTH -> {
bedRotation = 180; bedRotation = 180;
zOffset = -.5f; zOffset = -.5f;
} else if (fullIdentifier.contains("facing=east")) { }
// bed is facing east case EAST -> {
bedRotation = 90; bedRotation = 90;
xOffset = -.5f; xOffset = -.5f;
} else if (fullIdentifier.contains("facing=west")) { }
// bed is facing west case WEST -> {
bedRotation = 270; bedRotation = 270;
xOffset = .5f; xOffset = .5f;
} else if (fullIdentifier.contains("facing=north")) { }
case NORTH -> {
// rotation does not change because north is 0 // rotation does not change because north is 0
zOffset = .5f; zOffset = .5f;
} }
}
}
setYaw(yaw); setYaw(yaw);
setPitch(pitch); setPitch(pitch);

Datei anzeigen

@ -26,7 +26,9 @@
package org.geysermc.geyser.entity.type.living.monster; package org.geysermc.geyser.entity.type.living.monster;
import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.definitions.ItemDefinition;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
@ -45,5 +47,17 @@ public class AbstractSkeletonEntity extends MonsterEntity {
byte xd = entityMetadata.getPrimitiveValue(); byte xd = entityMetadata.getPrimitiveValue();
// A bit of a loophole so the hands get raised - set the target ID to its own ID // A bit of a loophole so the hands get raised - set the target ID to its own ID
dirtyMetadata.put(EntityDataTypes.TARGET_EID, ((xd & 4) == 4) ? geyserId : 0); dirtyMetadata.put(EntityDataTypes.TARGET_EID, ((xd & 4) == 4) ? geyserId : 0);
if ((xd & 4) == 4) {
ItemDefinition bow = session.getItemMappings().getStoredItems().bow().getBedrockDefinition();
setFlag(EntityFlag.FACING_TARGET_TO_RANGE_ATTACK, this.hand.getDefinition() == bow || this.offhand.getDefinition() == bow);
} else {
setFlag(EntityFlag.FACING_TARGET_TO_RANGE_ATTACK, false);
}
}
@Override
public boolean useArmSwingAttack() {
return true;
} }
} }

Datei anzeigen

@ -26,10 +26,13 @@
package org.geysermc.geyser.entity.type.living.monster; package org.geysermc.geyser.entity.type.living.monster;
import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType;
import java.util.UUID; import java.util.UUID;
@ -38,6 +41,16 @@ public class BasePiglinEntity extends MonsterEntity {
public BasePiglinEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) { public BasePiglinEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw); super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
// Both TARGET_EID and BLOCK are needed for melee attack animation
dirtyMetadata.put(EntityDataTypes.BLOCK, session.getBlockMappings().getDefinition(1));
setFlag(EntityFlag.SHAKING, isShaking());
}
@Override
public void setMobFlags(ByteEntityMetadata entityMetadata) {
super.setMobFlags(entityMetadata);
byte xd = entityMetadata.getPrimitiveValue();
dirtyMetadata.put(EntityDataTypes.TARGET_EID, (xd & 4) == 4 ? session.getPlayerEntity().getGeyserId() : 0);
} }
public void setImmuneToZombification(BooleanEntityMetadata entityMetadata) { public void setImmuneToZombification(BooleanEntityMetadata entityMetadata) {
@ -50,4 +63,9 @@ public class BasePiglinEntity extends MonsterEntity {
protected boolean isShaking() { protected boolean isShaking() {
return (!isImmuneToZombification && !session.getDimensionType().piglinSafe()) || super.isShaking(); return (!isImmuneToZombification && !session.getDimensionType().piglinSafe()) || super.isShaking();
} }
@Override
public boolean useArmSwingAttack() {
return true;
}
} }

Datei anzeigen

@ -0,0 +1,73 @@
/*
* 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.entity.type.living.monster;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InteractiveTag;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import java.util.UUID;
public class BoggedEntity extends AbstractSkeletonEntity {
private boolean sheared = false;
public BoggedEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
public void setSheared(BooleanEntityMetadata entityMetadata) {
this.sheared = entityMetadata.getPrimitiveValue();
setFlag(EntityFlag.SHEARED, this.sheared);
}
@Override
protected @NonNull InteractiveTag testMobInteraction(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
if (itemInHand.asItem() == Items.SHEARS && readyForShearing()) {
return InteractiveTag.SHEAR;
}
return super.testMobInteraction(hand, itemInHand);
}
@Override
protected @NonNull InteractionResult mobInteract(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
if (itemInHand.asItem() == Items.SHEARS && readyForShearing()) {
return InteractionResult.SUCCESS;
}
return super.mobInteract(hand, itemInHand);
}
private boolean readyForShearing() {
return !this.sheared && this.isAlive();
}
}

Datei anzeigen

@ -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.entity.type.living.monster;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.Pose;
import java.util.UUID;
public class BreezeEntity extends MonsterEntity {
public BreezeEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
@Override
public void setPose(Pose pose) {
// TODO Test
setFlag(EntityFlag.FACING_TARGET_TO_RANGE_ATTACK, pose == Pose.SHOOTING);
setFlag(EntityFlag.JUMP_GOAL_JUMP, pose == Pose.INHALING);
super.setPose(pose);
}
}

Datei anzeigen

@ -27,16 +27,22 @@ package org.geysermc.geyser.entity.type.living.monster;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerId;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.cloudburstmc.protocol.bedrock.packet.MobEquipmentPacket;
import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.item.Items; import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag; import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.util.InteractionResult; import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InteractiveTag; import org.geysermc.geyser.util.InteractiveTag;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import java.util.UUID; import java.util.UUID;
@ -55,13 +61,61 @@ public class PiglinEntity extends BasePiglinEntity {
} }
public void setChargingCrossbow(BooleanEntityMetadata entityMetadata) { public void setChargingCrossbow(BooleanEntityMetadata entityMetadata) {
setFlag(EntityFlag.CHARGING, entityMetadata.getPrimitiveValue()); boolean charging = entityMetadata.getPrimitiveValue();
setFlag(EntityFlag.CHARGING, charging);
dirtyMetadata.put(EntityDataTypes.CHARGE_AMOUNT, charging ? (byte) 64 : (byte) 0); // TODO: gradually increase
} }
public void setDancing(BooleanEntityMetadata entityMetadata) { public void setDancing(BooleanEntityMetadata entityMetadata) {
setFlag(EntityFlag.DANCING, entityMetadata.getPrimitiveValue()); setFlag(EntityFlag.DANCING, entityMetadata.getPrimitiveValue());
} }
@Override
public void setHand(ItemStack stack) {
ItemMapping crossbow = session.getItemMappings().getStoredItems().crossbow();
boolean toCrossbow = stack != null && stack.getId() == crossbow.getJavaItem().javaId();
if (toCrossbow ^ this.hand.getDefinition() == crossbow.getBedrockDefinition()) { // If switching to/from crossbow
dirtyMetadata.put(EntityDataTypes.BLOCK, session.getBlockMappings().getDefinition(toCrossbow ? 0 : 1));
dirtyMetadata.put(EntityDataTypes.CHARGE_AMOUNT, (byte) 0);
setFlag(EntityFlag.CHARGED, false);
setFlag(EntityFlag.USING_ITEM, false);
updateBedrockMetadata();
if (this.hand.isValid()) {
MobEquipmentPacket mobEquipmentPacket = new MobEquipmentPacket();
mobEquipmentPacket.setRuntimeEntityId(geyserId);
mobEquipmentPacket.setContainerId(ContainerId.INVENTORY);
mobEquipmentPacket.setInventorySlot(0);
mobEquipmentPacket.setHotbarSlot(-1);
mobEquipmentPacket.setItem(ItemData.AIR);
session.sendUpstreamPacket(mobEquipmentPacket);
}
}
super.setHand(stack);
}
@Override
public void updateMainHand(GeyserSession session) {
super.updateMainHand(session);
if (this.hand.getDefinition() == session.getItemMappings().getStoredItems().crossbow().getBedrockDefinition()) {
if (this.hand.getTag() != null && this.hand.getTag().containsKey("chargedItem")) {
dirtyMetadata.put(EntityDataTypes.CHARGE_AMOUNT, Byte.MAX_VALUE);
setFlag(EntityFlag.CHARGING, false);
setFlag(EntityFlag.CHARGED, true);
setFlag(EntityFlag.USING_ITEM, true);
} else if (getFlag(EntityFlag.CHARGED)) {
dirtyMetadata.put(EntityDataTypes.CHARGE_AMOUNT, (byte) 0);
setFlag(EntityFlag.CHARGED, false);
setFlag(EntityFlag.USING_ITEM, false);
}
}
updateBedrockMetadata();
}
@Override @Override
public void updateOffHand(GeyserSession session) { public void updateOffHand(GeyserSession session) {
// Check if the Piglin is holding Gold and set the ADMIRING flag accordingly so its pose updates // Check if the Piglin is holding Gold and set the ADMIRING flag accordingly so its pose updates

Datei anzeigen

@ -26,6 +26,7 @@
package org.geysermc.geyser.entity.type.living.monster; package org.geysermc.geyser.entity.type.living.monster;
import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
@ -37,6 +38,7 @@ public class ZoglinEntity extends MonsterEntity {
public ZoglinEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) { public ZoglinEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw); super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
dirtyMetadata.put(EntityDataTypes.TARGET_EID, session.getPlayerEntity().getGeyserId());
} }
public void setBaby(BooleanEntityMetadata entityMetadata) { public void setBaby(BooleanEntityMetadata entityMetadata) {
@ -56,7 +58,7 @@ public class ZoglinEntity extends MonsterEntity {
} }
@Override @Override
protected boolean canBeLeashed() { public boolean canBeLeashed() {
return isNotLeashed(); return isNotLeashed();
} }
@ -64,4 +66,9 @@ public class ZoglinEntity extends MonsterEntity {
protected boolean isEnemy() { protected boolean isEnemy() {
return true; return true;
} }
@Override
public boolean useArmSwingAttack() {
return true;
}
} }

Datei anzeigen

@ -57,4 +57,9 @@ public class ZombieEntity extends MonsterEntity {
protected boolean isShaking() { protected boolean isShaking() {
return convertingToDrowned || super.isShaking(); return convertingToDrowned || super.isShaking();
} }
@Override
public boolean useArmSwingAttack() {
return true;
}
} }

Datei anzeigen

@ -26,10 +26,13 @@
package org.geysermc.geyser.entity.type.living.monster.raid; package org.geysermc.geyser.entity.type.living.monster.raid;
import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import java.util.UUID; import java.util.UUID;
@ -39,16 +42,22 @@ public class PillagerEntity extends AbstractIllagerEntity {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw); super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
} }
public void setChargingCrossbow(BooleanEntityMetadata entityMetadata) {
boolean charging = entityMetadata.getPrimitiveValue();
setFlag(EntityFlag.CHARGING, charging);
dirtyMetadata.put(EntityDataTypes.CHARGE_AMOUNT, charging ? (byte) 64 : (byte) 0); // TODO: gradually increase
}
@Override @Override
public void updateMainHand(GeyserSession session) { //TODO public void updateMainHand(GeyserSession session) {
checkForCrossbow(); updateCrossbow();
super.updateMainHand(session); super.updateMainHand(session);
} }
@Override @Override
public void updateOffHand(GeyserSession session) { public void updateOffHand(GeyserSession session) {
checkForCrossbow(); updateCrossbow();
super.updateOffHand(session); super.updateOffHand(session);
} }
@ -56,12 +65,27 @@ public class PillagerEntity extends AbstractIllagerEntity {
/** /**
* Check for a crossbow in either the mainhand or offhand. If one exists, indicate that the pillager should be posing * Check for a crossbow in either the mainhand or offhand. If one exists, indicate that the pillager should be posing
*/ */
protected void checkForCrossbow() { protected void updateCrossbow() {
ItemMapping crossbow = session.getItemMappings().getStoredItems().crossbow(); ItemMapping crossbow = session.getItemMappings().getStoredItems().crossbow();
boolean hasCrossbow = this.hand.getDefinition() == crossbow.getBedrockDefinition() ItemData activeCrossbow = null;
|| this.offhand.getDefinition() == crossbow.getBedrockDefinition(); if (this.hand.getDefinition() == crossbow.getBedrockDefinition()) {
setFlag(EntityFlag.USING_ITEM, hasCrossbow); activeCrossbow = this.hand;
setFlag(EntityFlag.CHARGED, hasCrossbow); } else if (this.offhand.getDefinition() == crossbow.getBedrockDefinition()) {
activeCrossbow = this.offhand;
}
if (activeCrossbow != null) {
if (activeCrossbow.getTag() != null && activeCrossbow.getTag().containsKey("chargedItem")) {
dirtyMetadata.put(EntityDataTypes.CHARGE_AMOUNT, Byte.MAX_VALUE);
setFlag(EntityFlag.CHARGING, false);
setFlag(EntityFlag.CHARGED, true);
setFlag(EntityFlag.USING_ITEM, true);
} else if (getFlag(EntityFlag.CHARGED)) {
dirtyMetadata.put(EntityDataTypes.CHARGE_AMOUNT, (byte) 0);
setFlag(EntityFlag.CHARGED, false);
setFlag(EntityFlag.USING_ITEM, false);
}
}
updateBedrockMetadata(); updateBedrockMetadata();
} }

Datei anzeigen

@ -0,0 +1,54 @@
/*
* 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.entity.type.living.monster.raid;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.session.GeyserSession;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public class RavagerEntity extends RaidParticipantEntity {
public RavagerEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
@Override
public boolean useArmSwingAttack() {
setFlag(EntityFlag.DELAYED_ATTACK, false);
updateBedrockMetadata();
session.scheduleInEventLoop(() -> {
setFlag(EntityFlag.DELAYED_ATTACK, true);
updateBedrockMetadata();
}, 75, TimeUnit.MILLISECONDS);
return true;
}
}

Datei anzeigen

@ -26,6 +26,7 @@
package org.geysermc.geyser.entity.type.living.monster.raid; package org.geysermc.geyser.entity.type.living.monster.raid;
import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
@ -37,6 +38,7 @@ public class VindicatorEntity extends AbstractIllagerEntity {
public VindicatorEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) { public VindicatorEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw); super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
dirtyMetadata.put(EntityDataTypes.TARGET_EID, session.getPlayerEntity().getGeyserId());
} }
@Override @Override
@ -46,4 +48,9 @@ public class VindicatorEntity extends AbstractIllagerEntity {
byte xd = entityMetadata.getPrimitiveValue(); byte xd = entityMetadata.getPrimitiveValue();
setFlag(EntityFlag.ANGRY, (xd & 4) == 4); setFlag(EntityFlag.ANGRY, (xd & 4) == 4);
} }
@Override
public boolean useArmSwingAttack() {
return true;
}
} }

Datei anzeigen

@ -41,6 +41,7 @@ import org.cloudburstmc.protocol.bedrock.data.command.CommandPermission;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityLinkData; import org.cloudburstmc.protocol.bedrock.data.entity.EntityLinkData;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.cloudburstmc.protocol.bedrock.packet.*; import org.cloudburstmc.protocol.bedrock.packet.*;
import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity; import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity;
import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.EntityDefinitions;
@ -166,6 +167,31 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity {
session.sendUpstreamPacket(addPlayerPacket); session.sendUpstreamPacket(addPlayerPacket);
} }
@Override
public void despawnEntity() {
super.despawnEntity();
// Since we re-use player entities: Clear flags, held item, etc
this.resetMetadata();
this.hand = ItemData.AIR;
this.offhand = ItemData.AIR;
this.boots = ItemData.AIR;
this.leggings = ItemData.AIR;
this.chestplate = ItemData.AIR;
this.helmet = ItemData.AIR;
}
public void resetMetadata() {
// Reset all metadata to their default values
// This is used when a player respawns
this.flags.clear();
this.initializeMetadata();
// Explicitly reset all metadata not handled by initializeMetadata
setParrot(null, true);
setParrot(null, false);
}
public void sendPlayer() { public void sendPlayer() {
if (session.getEntityCache().getPlayerEntity(uuid) == null) if (session.getEntityCache().getPlayerEntity(uuid) == null)
return; return;

Datei anzeigen

@ -27,14 +27,18 @@ package org.geysermc.geyser.entity.type.player;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import lombok.Getter; import lombok.Getter;
import lombok.Setter;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.AttributeData; import org.cloudburstmc.protocol.bedrock.data.AttributeData;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket;
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket; import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
import org.geysermc.geyser.entity.attribute.GeyserAttributeType; import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
import org.geysermc.geyser.item.Items; import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.network.GameProtocol;
import org.geysermc.geyser.level.BedrockDimension;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.AttributeUtils; import org.geysermc.geyser.util.AttributeUtils;
import org.geysermc.geyser.util.DimensionUtils; import org.geysermc.geyser.util.DimensionUtils;
@ -60,16 +64,23 @@ public class SessionPlayerEntity extends PlayerEntity {
*/ */
@Getter @Getter
protected final Map<GeyserAttributeType, AttributeData> attributes = new Object2ObjectOpenHashMap<>(); protected final Map<GeyserAttributeType, AttributeData> attributes = new Object2ObjectOpenHashMap<>();
/**
* Whether to check for updated speed after all entity metadata has been processed
*/
private boolean refreshSpeed = false;
/** /**
* Used in PlayerInputTranslator for movement checks. * Used in PlayerInputTranslator for movement checks.
*/ */
@Getter @Getter
private boolean isRidingInFront; private boolean isRidingInFront;
private int lastAirSupply = getMaxAir();
/**
* Determines if our position is currently out-of-sync with the Java server
* due to our workaround for the void floor
* <p>
* Must be reset when dying, switching worlds, or being teleported out of the void
*/
@Getter @Setter
private boolean voidPositionDesynched;
public SessionPlayerEntity(GeyserSession session) { public SessionPlayerEntity(GeyserSession session) {
super(session, -1, 1, null, Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0, null, null); super(session, -1, 1, null, Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0, null, null);
@ -88,10 +99,25 @@ public class SessionPlayerEntity extends PlayerEntity {
@Override @Override
public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) { public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) {
if (voidPositionDesynched) {
if (!isBelowVoidFloor()) {
voidPositionDesynched = false; // No need to fix our offset; we've been moved
}
}
super.moveRelative(relX, relY, relZ, yaw, pitch, headYaw, isOnGround); super.moveRelative(relX, relY, relZ, yaw, pitch, headYaw, isOnGround);
session.getCollisionManager().updatePlayerBoundingBox(this.position.down(definition.offset())); session.getCollisionManager().updatePlayerBoundingBox(this.position.down(definition.offset()));
} }
@Override
public void moveAbsolute(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) {
if (voidPositionDesynched) {
if (!isBelowVoidFloor()) {
voidPositionDesynched = false; // No need to fix our offset; we've been moved
}
}
super.moveAbsolute(position, yaw, pitch, headYaw, isOnGround, teleported);
}
@Override @Override
public void setPosition(Vector3f position) { public void setPosition(Vector3f position) {
if (valid) { // Don't update during session init if (valid) { // Don't update during session init
@ -120,9 +146,7 @@ public class SessionPlayerEntity extends PlayerEntity {
// TODO: proper fix, BDS somehow does it? https://paste.gg/p/anonymous/3adfb7612f1540be80fa03a2281f93dc (BDS 1.20.13) // TODO: proper fix, BDS somehow does it? https://paste.gg/p/anonymous/3adfb7612f1540be80fa03a2281f93dc (BDS 1.20.13)
if (!this.session.getGameMode().equals(GameMode.SPECTATOR)) { if (!this.session.getGameMode().equals(GameMode.SPECTATOR)) {
super.setFlags(entityMetadata); super.setFlags(entityMetadata);
session.setSwimmingInWater((entityMetadata.getPrimitiveValue() & 0x10) == 0x10 && getFlag(EntityFlag.SPRINTING));
} }
refreshSpeed = true;
} }
/** /**
@ -150,7 +174,6 @@ public class SessionPlayerEntity extends PlayerEntity {
public void setPose(Pose pose) { public void setPose(Pose pose) {
super.setPose(pose); super.setPose(pose);
session.setPose(pose); session.setPose(pose);
refreshSpeed = true;
} }
public float getMaxHealth() { public float getMaxHealth() {
@ -167,7 +190,13 @@ public class SessionPlayerEntity extends PlayerEntity {
@Override @Override
protected void setAirSupply(int amount) { protected void setAirSupply(int amount) {
if (amount == getMaxAir()) { // Seemingly required to be sent as of Bedrock 1.21. Otherwise, bubbles will appear as empty
// Also, this changes how the air bubble graphics/sounds are presented. Breathing on means sound effects and
// the bubbles visually pop
setFlag(EntityFlag.BREATHING, amount >= this.lastAirSupply);
this.lastAirSupply = amount;
if (amount == getMaxAir() && GameProtocol.isPre1_21_0(session)) {
super.setAirSupply(0); // Hide the bubble counter from the UI for the player super.setAirSupply(0); // Hide the bubble counter from the UI for the player
} else { } else {
super.setAirSupply(amount); super.setAirSupply(amount);
@ -199,21 +228,6 @@ public class SessionPlayerEntity extends PlayerEntity {
} }
} }
@Override
public void updateBedrockMetadata() {
super.updateBedrockMetadata();
if (refreshSpeed) {
AttributeData speedAttribute = session.adjustSpeed();
if (speedAttribute != null) {
UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket();
attributesPacket.setRuntimeEntityId(geyserId);
attributesPacket.setAttributes(Collections.singletonList(speedAttribute));
session.sendUpstreamPacket(attributesPacket);
}
refreshSpeed = false;
}
}
@Override @Override
protected void updateAttribute(Attribute javaAttribute, List<AttributeData> newAttributes) { protected void updateAttribute(Attribute javaAttribute, List<AttributeData> newAttributes) {
if (javaAttribute.getType() == AttributeType.Builtin.GENERIC_ATTACK_SPEED) { if (javaAttribute.getType() == AttributeType.Builtin.GENERIC_ATTACK_SPEED) {
@ -226,17 +240,6 @@ public class SessionPlayerEntity extends PlayerEntity {
@Override @Override
protected AttributeData calculateAttribute(Attribute javaAttribute, GeyserAttributeType type) { protected AttributeData calculateAttribute(Attribute javaAttribute, GeyserAttributeType type) {
AttributeData attributeData = super.calculateAttribute(javaAttribute, type); AttributeData attributeData = super.calculateAttribute(javaAttribute, type);
if (javaAttribute.getType() == AttributeType.Builtin.GENERIC_MOVEMENT_SPEED) {
session.setOriginalSpeedAttribute(attributeData.getValue());
AttributeData speedAttribute = session.adjustSpeed();
if (speedAttribute != null) {
// Overwrite the attribute with our own
this.attributes.put(type, speedAttribute);
return speedAttribute;
}
}
this.attributes.put(type, attributeData); this.attributes.put(type, attributeData);
return attributeData; return attributeData;
} }
@ -244,11 +247,14 @@ public class SessionPlayerEntity extends PlayerEntity {
public void setLastDeathPosition(@Nullable GlobalPos pos) { public void setLastDeathPosition(@Nullable GlobalPos pos) {
if (pos != null) { if (pos != null) {
dirtyMetadata.put(EntityDataTypes.PLAYER_LAST_DEATH_POS, pos.getPosition()); dirtyMetadata.put(EntityDataTypes.PLAYER_LAST_DEATH_POS, pos.getPosition());
dirtyMetadata.put(EntityDataTypes.PLAYER_LAST_DEATH_DIMENSION, DimensionUtils.javaToBedrock(pos.getDimension())); dirtyMetadata.put(EntityDataTypes.PLAYER_LAST_DEATH_DIMENSION, DimensionUtils.javaToBedrock(pos.getDimension().asString()));
dirtyMetadata.put(EntityDataTypes.PLAYER_HAS_DIED, true); dirtyMetadata.put(EntityDataTypes.PLAYER_HAS_DIED, true);
} else { } else {
dirtyMetadata.put(EntityDataTypes.PLAYER_HAS_DIED, false); dirtyMetadata.put(EntityDataTypes.PLAYER_HAS_DIED, false);
} }
// We're either respawning or switching worlds, either way, we are no longer desynched
this.setVoidPositionDesynched(false);
} }
@Override @Override
@ -264,19 +270,13 @@ public class SessionPlayerEntity extends PlayerEntity {
super.setAbsorptionHearts(entityMetadata); super.setAbsorptionHearts(entityMetadata);
} }
@Override
public void resetMetadata() { public void resetMetadata() {
// Reset all metadata to their default values super.resetMetadata();
// This is used when a player respawns
this.flags.clear();
this.initializeMetadata();
// Reset air // Reset air
this.resetAir(); this.resetAir();
// Explicitly reset all metadata not handled by initializeMetadata
setParrot(null, true);
setParrot(null, false);
// Absorption is metadata in java edition // Absorption is metadata in java edition
attributes.remove(GeyserAttributeType.ABSORPTION); attributes.remove(GeyserAttributeType.ABSORPTION);
UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket(); UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket();
@ -306,4 +306,48 @@ public class SessionPlayerEntity extends PlayerEntity {
public void resetAir() { public void resetAir() {
this.setAirSupply(getMaxAir()); this.setAirSupply(getMaxAir());
} }
private boolean isBelowVoidFloor() {
return position.getY() < voidFloorPosition();
}
public int voidFloorPosition() {
// The void floor is offset about 40 blocks below the bottom of the world
BedrockDimension bedrockDimension = session.getChunkCache().getBedrockDimension();
return bedrockDimension.minY() - 40;
}
/**
* This method handles teleporting the player below or above the Bedrock void floor.
* The Java server should never see this desync as we adjust the position that we send to it
*
* @param up in which direction to teleport - true to resync our position, or false to be
* teleported below the void floor.
*/
public void teleportVoidFloorFix(boolean up) {
// Safety to avoid double teleports
if ((voidPositionDesynched && !up) || (!voidPositionDesynched && up)) {
return;
}
// Work around there being a floor at the bottom of the world and teleport the player below it
// Moving from below to above the void floor works fine
Vector3f newPosition = this.getPosition();
if (up) {
newPosition = newPosition.up(4f);
voidPositionDesynched = false;
} else {
newPosition = newPosition.down(4f);
voidPositionDesynched = true;
}
this.setPositionManual(newPosition);
MovePlayerPacket movePlayerPacket = new MovePlayerPacket();
movePlayerPacket.setRuntimeEntityId(geyserId);
movePlayerPacket.setPosition(newPosition);
movePlayerPacket.setRotation(getBedrockRotation());
movePlayerPacket.setMode(MovePlayerPacket.Mode.TELEPORT);
movePlayerPacket.setTeleportationCause(MovePlayerPacket.TeleportationCause.BEHAVIOR);
session.sendUpstreamPacketImmediately(movePlayerPacket);
}
} }

Datei anzeigen

@ -34,7 +34,10 @@ import org.cloudburstmc.protocol.bedrock.data.command.CommandPermission;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.cloudburstmc.protocol.bedrock.packet.AddPlayerPacket; import org.cloudburstmc.protocol.bedrock.packet.AddPlayerPacket;
import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.level.block.property.Properties;
import org.geysermc.geyser.level.block.type.BlockState;
import org.geysermc.geyser.level.block.type.WallSkullBlock;
import org.geysermc.geyser.level.physics.Direction;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.SkullCache; import org.geysermc.geyser.session.cache.SkullCache;
import org.geysermc.geyser.skin.SkullSkinManager; import org.geysermc.geyser.skin.SkullSkinManager;
@ -137,20 +140,19 @@ public class SkullPlayerEntity extends PlayerEntity {
float z = skull.getPosition().getZ() + .5f; float z = skull.getPosition().getZ() + .5f;
float rotation; float rotation;
int blockState = skull.getBlockState(); BlockState blockState = skull.getBlockState();
byte floorRotation = BlockStateValues.getSkullRotation(blockState); if (blockState.block() instanceof WallSkullBlock) {
if (floorRotation == -1) {
// Wall skull
y += 0.25f; y += 0.25f;
rotation = BlockStateValues.getSkullWallDirections().get(blockState); Direction direction = blockState.getValue(Properties.HORIZONTAL_FACING);
switch ((int) rotation) { rotation = WallSkullBlock.getDegrees(direction);
case 180 -> z += 0.24f; // North switch (direction) {
case 0 -> z -= 0.24f; // South case NORTH -> z += 0.24f;
case 90 -> x += 0.24f; // West case SOUTH -> z -= 0.24f;
case 270 -> x -= 0.24f; // East case WEST -> x += 0.24f;
case EAST -> x -= 0.24f;
} }
} else { } else {
rotation = (180f + (floorRotation * 22.5f)) % 360; rotation = (180f + blockState.getValue(Properties.ROTATION_16, 0) * 22.5f) % 360;
} }
moveAbsolute(Vector3f.from(x, y, z), rotation, 0, rotation, true, true); moveAbsolute(Vector3f.from(x, y, z), rotation, 0, rotation, true, true);

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden Mehr anzeigen