Mirror von
https://github.com/GeyserMC/Geyser.git
synchronisiert 2024-10-05 09:20:07 +02:00
Merge remote-tracking branch 'refs/remotes/upstream/master' into customitemapi
Dieser Commit ist enthalten in:
Commit
ac4d543069
89
.github/workflows/build-remote.yml
vendored
89
.github/workflows/build-remote.yml
vendored
@ -22,81 +22,26 @@ jobs:
|
||||
run: |
|
||||
echo "BUILD_NUMBER=${GITHUB_RUN_NUMBER}" >> $GITHUB_ENV
|
||||
|
||||
- name: Set up JDK 21
|
||||
# See https://github.com/actions/setup-java/commits
|
||||
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0
|
||||
- name: Setup Gradle
|
||||
uses: GeyserMC/actions/setup-gradle-composite@master
|
||||
with:
|
||||
java-version: 21
|
||||
distribution: temurin
|
||||
|
||||
- name: Checkout repository and submodules
|
||||
# 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
|
||||
checkout_repository: ${{ inputs.repository }}
|
||||
checkout_ref: ${{ inputs.ref }}
|
||||
setup-java_java-version: 21
|
||||
setup-gradle_cache-read-only: true
|
||||
|
||||
- name: Build Geyser
|
||||
# See https://github.com/gradle/actions/commits
|
||||
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0
|
||||
with:
|
||||
arguments: build
|
||||
build-root-directory: geyser
|
||||
cache-read-only: true
|
||||
run: ./gradlew build
|
||||
|
||||
- name: Archive artifacts (Geyser Fabric)
|
||||
# See https://github.com/actions/upload-artifact/commits
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
- name: Archive Artifacts
|
||||
uses: GeyserMC/actions/upload-multi-artifact@master
|
||||
if: success()
|
||||
with:
|
||||
name: Geyser Fabric
|
||||
path: geyser/bootstrap/mod/fabric/build/libs/Geyser-Fabric.jar
|
||||
if-no-files-found: error
|
||||
- name: Archive artifacts (Geyser NeoForge)
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
|
||||
if: success()
|
||||
with:
|
||||
name: Geyser NeoForge
|
||||
path: geyser/bootstrap/mod/neoforge/build/libs/Geyser-NeoForge.jar
|
||||
if-no-files-found: error
|
||||
- name: Archive artifacts (Geyser Standalone)
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
|
||||
if: success()
|
||||
with:
|
||||
name: Geyser Standalone
|
||||
path: geyser/bootstrap/standalone/build/libs/Geyser-Standalone.jar
|
||||
if-no-files-found: error
|
||||
- name: Archive artifacts (Geyser Spigot)
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
|
||||
if: success()
|
||||
with:
|
||||
name: Geyser Spigot
|
||||
path: geyser/bootstrap/spigot/build/libs/Geyser-Spigot.jar
|
||||
if-no-files-found: error
|
||||
- name: Archive artifacts (Geyser BungeeCord)
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
|
||||
if: success()
|
||||
with:
|
||||
name: Geyser BungeeCord
|
||||
path: geyser/bootstrap/bungeecord/build/libs/Geyser-BungeeCord.jar
|
||||
if-no-files-found: error
|
||||
- name: Archive artifacts (Geyser Velocity)
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
|
||||
if: success()
|
||||
with:
|
||||
name: Geyser Velocity
|
||||
path: geyser/bootstrap/velocity/build/libs/Geyser-Velocity.jar
|
||||
if-no-files-found: error
|
||||
- name: Archive artifacts (Geyser ViaProxy)
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
|
||||
if: success()
|
||||
with:
|
||||
name: Geyser ViaProxy
|
||||
path: geyser/bootstrap/viaproxy/build/libs/Geyser-ViaProxy.jar
|
||||
if-no-files-found: error
|
||||
artifacts: |
|
||||
bootstrap/mod/fabric/build/libs/Geyser-Fabric.jar
|
||||
bootstrap/mod/neoforge/build/libs/Geyser-NeoForge.jar
|
||||
bootstrap/standalone/build/libs/Geyser-Standalone.jar
|
||||
bootstrap/spigot/build/libs/Geyser-Spigot.jar
|
||||
bootstrap/bungeecord/build/libs/Geyser-BungeeCord.jar
|
||||
bootstrap/velocity/build/libs/Geyser-Velocity.jar
|
||||
bootstrap/viaproxy/build/libs/Geyser-ViaProxy.jar
|
191
.github/workflows/build.yml
vendored
191
.github/workflows/build.yml
vendored
@ -21,103 +21,55 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
PROJECT: 'geyser'
|
||||
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:
|
||||
BUILD_JSON: ${{ vars.RELEASEACTION_PREVRELEASE }}
|
||||
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
|
||||
BUILD_NUMBER: ${{ steps.release-info.outputs.curentRelease }}
|
||||
|
||||
- name: Checkout repository and submodules
|
||||
# See https://github.com/actions/checkout/commits
|
||||
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
|
||||
- name: Archive Artifacts
|
||||
uses: GeyserMC/actions/upload-multi-artifact@master
|
||||
if: success()
|
||||
with:
|
||||
name: Geyser Fabric
|
||||
path: bootstrap/mod/fabric/build/libs/Geyser-Fabric.jar
|
||||
if-no-files-found: error
|
||||
- name: Archive artifacts (Geyser NeoForge)
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
|
||||
if: success()
|
||||
with:
|
||||
name: Geyser NeoForge
|
||||
path: bootstrap/mod/neoforge/build/libs/Geyser-NeoForge.jar
|
||||
if-no-files-found: error
|
||||
- name: Archive artifacts (Geyser Standalone)
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
|
||||
if: success()
|
||||
with:
|
||||
name: Geyser Standalone
|
||||
path: bootstrap/standalone/build/libs/Geyser-Standalone.jar
|
||||
if-no-files-found: error
|
||||
- name: Archive artifacts (Geyser Spigot)
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
|
||||
if: success()
|
||||
with:
|
||||
name: Geyser Spigot
|
||||
path: bootstrap/spigot/build/libs/Geyser-Spigot.jar
|
||||
if-no-files-found: error
|
||||
- name: Archive artifacts (Geyser BungeeCord)
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
|
||||
if: success()
|
||||
with:
|
||||
name: Geyser BungeeCord
|
||||
path: bootstrap/bungeecord/build/libs/Geyser-BungeeCord.jar
|
||||
if-no-files-found: error
|
||||
- name: Archive artifacts (Geyser Velocity)
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
|
||||
if: success()
|
||||
with:
|
||||
name: Geyser Velocity
|
||||
path: bootstrap/velocity/build/libs/Geyser-Velocity.jar
|
||||
if-no-files-found: error
|
||||
- name: Archive artifacts (Geyser ViaProxy)
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
|
||||
if: success()
|
||||
with:
|
||||
name: Geyser ViaProxy
|
||||
path: bootstrap/viaproxy/build/libs/Geyser-ViaProxy.jar
|
||||
if-no-files-found: error
|
||||
artifacts: |
|
||||
bootstrap/mod/fabric/build/libs/Geyser-Fabric.jar
|
||||
bootstrap/mod/neoforge/build/libs/Geyser-NeoForge.jar
|
||||
bootstrap/standalone/build/libs/Geyser-Standalone.jar
|
||||
bootstrap/spigot/build/libs/Geyser-Spigot.jar
|
||||
bootstrap/bungeecord/build/libs/Geyser-BungeeCord.jar
|
||||
bootstrap/velocity/build/libs/Geyser-Velocity.jar
|
||||
bootstrap/viaproxy/build/libs/Geyser-ViaProxy.jar
|
||||
|
||||
- name: Publish to Maven Repository
|
||||
if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
|
||||
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
|
||||
run: ./gradlew publish
|
||||
env:
|
||||
BUILD_NUMBER: ${{ steps.release-info.outputs.curentRelease }}
|
||||
ORG_GRADLE_PROJECT_geysermcUsername: ${{ vars.DEPLOY_USER }}
|
||||
ORG_GRADLE_PROJECT_geysermcPassword: ${{ secrets.DEPLOY_PASS }}
|
||||
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
|
||||
if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
|
||||
# See https://github.com/Kas-tle/base-release-action/releases/tag/main-11
|
||||
uses: Kas-tle/base-release-action@b863fa0f89bd15267a96a72efb84aec25f168d4c # main-11
|
||||
if: ${{ (success() || failure()) && github.repository == 'GeyserMC/Geyser' }}
|
||||
uses: GeyserMC/actions/release@master
|
||||
id: metadata
|
||||
with:
|
||||
appID: ${{ secrets.RELEASE_APP_ID }}
|
||||
appPrivateKey: ${{ secrets.RELEASE_APP_PK }}
|
||||
@ -131,61 +83,38 @@ jobs:
|
||||
viaproxy:bootstrap/viaproxy/build/libs/Geyser-ViaProxy.jar
|
||||
releaseEnabled: false
|
||||
saveMetadata: true
|
||||
- name: Update Generated Metadata
|
||||
if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
|
||||
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
|
||||
releaseProject: 'geyser'
|
||||
releaseVersion: ${{ steps.get-version.outputs.VERSION }}
|
||||
|
||||
- name: Publish to Downloads API
|
||||
if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
|
||||
shell: bash
|
||||
env:
|
||||
DOWNLOADS_USERNAME: ${{ vars.DOWNLOADS_USERNAME }}
|
||||
DOWNLOADS_PRIVATE_KEY: ${{ secrets.DOWNLOADS_PRIVATE_KEY }}
|
||||
DOWNLOADS_SERVER_IP: ${{ secrets.DOWNLOADS_SERVER_IP }}
|
||||
run: |
|
||||
# Save the private key to a file
|
||||
echo "$DOWNLOADS_PRIVATE_KEY" > id_ecdsa
|
||||
chmod 600 id_ecdsa
|
||||
# Create the build folder
|
||||
ssh -o StrictHostKeyChecking=no -i id_ecdsa $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP mkdir -p "~/uploads/$PROJECT/$GITHUB_RUN_NUMBER/"
|
||||
# Copy over artifacts
|
||||
rsync -P -e "ssh -o StrictHostKeyChecking=no -i id_ecdsa" bootstrap/**/build/libs/Geyser-*.jar $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP:~/uploads/$PROJECT/$GITHUB_RUN_NUMBER/
|
||||
rsync -P -e "ssh -o StrictHostKeyChecking=no -i id_ecdsa" bootstrap/mod/**/build/libs/Geyser-*.jar $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP:~/uploads/$PROJECT/$GITHUB_RUN_NUMBER/
|
||||
# Run the build script
|
||||
rsync -P -e "ssh -o StrictHostKeyChecking=no -i id_ecdsa" metadata.json $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP:~/uploads/$PROJECT/$GITHUB_RUN_NUMBER/
|
||||
uses: GeyserMC/actions/upload-release@master
|
||||
with:
|
||||
username: ${{ vars.DOWNLOADS_USERNAME }}
|
||||
privateKey: ${{ secrets.DOWNLOADS_PRIVATE_KEY }}
|
||||
host: ${{ secrets.DOWNLOADS_SERVER_IP }}
|
||||
files: |
|
||||
bootstrap/bungeecord/build/libs/Geyser-BungeeCord.jar
|
||||
bootstrap/mod/fabric/build/libs/Geyser-Fabric.jar
|
||||
bootstrap/mod/neoforge/build/libs/Geyser-NeoForge.jar
|
||||
bootstrap/spigot/build/libs/Geyser-Spigot.jar
|
||||
bootstrap/standalone/build/libs/Geyser-Standalone.jar
|
||||
bootstrap/velocity/build/libs/Geyser-Velocity.jar
|
||||
bootstrap/viaproxy/build/libs/Geyser-ViaProxy.jar
|
||||
changelog: ${{ steps.metadata.outputs.body }}
|
||||
|
||||
- name: Publish to Modrinth (Fabric)
|
||||
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
|
||||
- name: Publish to Modrinth
|
||||
if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
|
||||
env:
|
||||
BUILD_NUMBER: ${{ steps.release-info.outputs.curentRelease }}
|
||||
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
|
||||
with:
|
||||
arguments: fabric:modrinth
|
||||
gradle-home-cache-cleanup: true
|
||||
|
||||
- name: Publish to Modrinth (NeoForge)
|
||||
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
|
||||
if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
|
||||
env:
|
||||
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
|
||||
with:
|
||||
arguments: neoforge:modrinth
|
||||
gradle-home-cache-cleanup: true
|
||||
run: ./gradlew modrinth
|
||||
|
||||
- name: Notify Discord
|
||||
if: ${{ (success() || failure()) && github.repository == 'GeyserMC/Geyser' }}
|
||||
# See https://github.com/Tim203/actions-git-discord-webhook/commits
|
||||
uses: Tim203/actions-git-discord-webhook@70f38ded3aca51635ec978ab4e1a58cd4cd0c2ff
|
||||
uses: GeyserMC/actions/notify-discord@master
|
||||
with:
|
||||
webhook_url: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
discordWebhook: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
status: ${{ job.status }}
|
||||
body: ${{ steps.metadata.outputs.body }}
|
||||
includeDownloads: ${{ github.ref_name == 'master' }}
|
||||
|
33
.github/workflows/dispatch-preview.yml
vendored
Normale Datei
33
.github/workflows/dispatch-preview.yml
vendored
Normale Datei
@ -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 }}
|
96
.github/workflows/preview.yml
vendored
96
.github/workflows/preview.yml
vendored
@ -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/
|
14
.github/workflows/pull-request.yml
vendored
14
.github/workflows/pull-request.yml
vendored
@ -8,7 +8,7 @@ jobs:
|
||||
# Forbid access to secrets nor GH Token perms while building the PR
|
||||
permissions: {}
|
||||
secrets: {}
|
||||
uses: ./.github/workflows/build-remote.yml
|
||||
uses: GeyserMC/Geyser/.github/workflows/build-remote.yml@master
|
||||
with:
|
||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
@ -18,7 +18,17 @@ jobs:
|
||||
contains(github.event.pull_request.labels.*.name, 'PR: Needs Testing')
|
||||
# Allow access to secrets if we are uploading a preview
|
||||
secrets: inherit
|
||||
uses: ./.github/workflows/preview.yml
|
||||
uses: GeyserMC/actions/.github/workflows/upload-preview.yml@master
|
||||
with:
|
||||
build: ${{ github.run_number }}
|
||||
version: pr.${{ github.event.pull_request.number }}
|
||||
files: |
|
||||
bungeecord:Geyser-BungeeCord.jar
|
||||
fabric:Geyser-Fabric.jar
|
||||
neoforge:Geyser-NeoForge.jar
|
||||
spigot:Geyser-Spigot.jar
|
||||
standalone:Geyser-Standalone.jar
|
||||
velocity:Geyser-Velocity.jar
|
||||
viaproxy:Geyser-ViaProxy.jar
|
||||
project: geyserpreview
|
||||
runId: ${{ github.run_id }}
|
@ -1,6 +1,7 @@
|
||||
<component name="CopyrightManager">
|
||||
<copyright>
|
||||
<option name="notice" value="Copyright (c) 2019-&#36;today.year GeyserMC. http://geysermc.org Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @author GeyserMC @link https://github.com/GeyserMC/Geyser" />
|
||||
<option name="allowReplaceRegexp" value="Copyright" />
|
||||
<option name="notice" value="Copyright (c) &#36;originalComment.match("Copyright \(c\) (\d+)", 1, "-")&#36;today.year GeyserMC. http://geysermc.org Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @author GeyserMC @link https://github.com/GeyserMC/Geyser" />
|
||||
<option name="myName" value="Geyser" />
|
||||
</copyright>
|
||||
</component>
|
@ -14,7 +14,7 @@ The ultimate goal of this project is to allow Minecraft: Bedrock Edition users t
|
||||
|
||||
Special thanks to the DragonProxy project for being a trailblazer in protocol translation and for all the team members who have joined us here!
|
||||
|
||||
### Currently supporting Minecraft Bedrock 1.20.40 - 1.20.80/81 and Minecraft Java 1.20.5/1.20.6
|
||||
### Currently supporting Minecraft Bedrock 1.20.80 - 1.21.0 and Minecraft Java 1.21
|
||||
|
||||
## Setting Up
|
||||
Take a look [here](https://wiki.geysermc.org/geyser/setup/) for how to set up Geyser.
|
||||
|
@ -145,4 +145,36 @@ public interface CameraData {
|
||||
* @return whether the camera is currently locked
|
||||
*/
|
||||
boolean isCameraLocked();
|
||||
|
||||
/**
|
||||
* Hides a {@link GuiElement} on the client's side.
|
||||
*
|
||||
* @param element the {@link GuiElement} to hide
|
||||
*/
|
||||
void hideElement(@NonNull GuiElement... element);
|
||||
|
||||
/**
|
||||
* Resets a {@link GuiElement} on the client's side.
|
||||
* This makes the client decide on its own - e.g. based on client settings -
|
||||
* whether to show or hide the gui element.
|
||||
* <p>
|
||||
* If no elements are specified, this will reset all currently hidden elements
|
||||
*
|
||||
* @param element the {@link GuiElement} to reset
|
||||
*/
|
||||
void resetElement(@NonNull GuiElement @Nullable... element);
|
||||
|
||||
/**
|
||||
* Determines whether a {@link GuiElement} is currently hidden.
|
||||
*
|
||||
* @param element the {@link GuiElement} to check
|
||||
*/
|
||||
boolean isHudElementHidden(@NonNull GuiElement element);
|
||||
|
||||
/**
|
||||
* Returns the currently hidden {@link GuiElement}s.
|
||||
*
|
||||
* @return an unmodifiable view of all currently hidden {@link GuiElement}s
|
||||
*/
|
||||
@NonNull Set<GuiElement> hiddenElements();
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2024 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.api.bedrock.camera;
|
||||
|
||||
/**
|
||||
* Represent GUI elements on the players HUD display.
|
||||
* These can be hidden using {@link CameraData#hideElement(GuiElement...)},
|
||||
* and one can reset their visibility using {@link CameraData#resetElement(GuiElement...)}.
|
||||
*/
|
||||
public class GuiElement {
|
||||
public static final GuiElement PAPER_DOLL = new GuiElement(0);
|
||||
public static final GuiElement ARMOR = new GuiElement(1);
|
||||
public static final GuiElement TOOL_TIPS = new GuiElement(2);
|
||||
public static final GuiElement TOUCH_CONTROLS = new GuiElement(3);
|
||||
public static final GuiElement CROSSHAIR = new GuiElement(4);
|
||||
public static final GuiElement HOTBAR = new GuiElement(5);
|
||||
public static final GuiElement HEALTH = new GuiElement(6);
|
||||
public static final GuiElement PROGRESS_BAR = new GuiElement(7);
|
||||
public static final GuiElement FOOD_BAR = new GuiElement(8);
|
||||
public static final GuiElement AIR_BUBBLES_BAR = new GuiElement(9);
|
||||
public static final GuiElement VEHICLE_HEALTH = new GuiElement(10);
|
||||
public static final GuiElement EFFECTS_BAR = new GuiElement(11);
|
||||
public static final GuiElement ITEM_TEXT_POPUP = new GuiElement(12);
|
||||
|
||||
private GuiElement(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
private final int id;
|
||||
|
||||
/**
|
||||
* Internal use only; don't depend on these values being consistent.
|
||||
*/
|
||||
public int id() {
|
||||
return this.id;
|
||||
}
|
||||
}
|
@ -36,7 +36,7 @@ import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Called when a session has logged in, and is about to connect to a remote java server.
|
||||
* Called when a session has logged in, and is about to connect to a remote Java server.
|
||||
* This event is cancellable, and can be used to prevent the player from connecting to the remote server.
|
||||
*/
|
||||
public final class SessionLoginEvent extends ConnectionEvent implements Cancellable {
|
||||
@ -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() {
|
||||
return this.remoteServer;
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
@ -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
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@ -23,12 +23,18 @@
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.api.skin;
|
||||
|
||||
/**
|
||||
* Contains useful collections for use in Geyser.
|
||||
* <p>
|
||||
* Of note are the fixed int maps. Designed for use with block states that are positive and sequential, they do not allow keys to be
|
||||
* added that are not greater by one versus the previous key. Because of this, speedy operations of {@link java.util.Map#get(java.lang.Object)}
|
||||
* and {@link java.util.Map#containsKey(java.lang.Object)} can be performed by simply checking the bounds of the map
|
||||
* size and its "start" integer.
|
||||
* Represents a cape.
|
||||
*
|
||||
* @param textureUrl The URL of the cape texture
|
||||
* @param capeId The ID of the cape
|
||||
* @param capeData The raw cape image data in ARGB format
|
||||
* @param failed If the cape failed to load, this is for things like fallback capes
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
@ -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
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@ -23,18 +23,17 @@
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.level.block;
|
||||
package org.geysermc.geyser.api.skin;
|
||||
|
||||
/**
|
||||
* This stores all values of double chests that are part of the Java block state.
|
||||
* Represents a skin.
|
||||
*
|
||||
* @param isFacingEast If true, then chest is facing east/west; if false, south/north
|
||||
* @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.
|
||||
* @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 DoubleChestValue(
|
||||
boolean isFacingEast,
|
||||
boolean isDirectionPositive,
|
||||
boolean isLeft) {
|
||||
|
||||
public record Skin(String textureUrl, byte[] skinData, boolean failed) {
|
||||
public Skin(String textureUrl, byte[] skinData) {
|
||||
this(textureUrl, skinData, false);
|
||||
}
|
||||
}
|
32
api/src/main/java/org/geysermc/geyser/api/skin/SkinData.java
Normale Datei
32
api/src/main/java/org/geysermc/geyser/api/skin/SkinData.java
Normale Datei
@ -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) {
|
||||
}
|
48
api/src/main/java/org/geysermc/geyser/api/skin/SkinGeometry.java
Normale Datei
48
api/src/main/java/org/geysermc/geyser/api/skin/SkinGeometry.java
Normale Datei
@ -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" : "") + "\"}}", "");
|
||||
}
|
||||
}
|
@ -34,3 +34,8 @@ tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
|
||||
exclude(dependency("io.netty:netty-resolver-dns:.*"))
|
||||
}
|
||||
}
|
||||
|
||||
modrinth {
|
||||
uploadFile.set(tasks.getByPath("shadowJar"))
|
||||
loaders.add("bungeecord")
|
||||
}
|
||||
|
@ -26,22 +26,19 @@
|
||||
package org.geysermc.geyser.platform.bungeecord;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.geysermc.geyser.GeyserLogger;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class GeyserBungeeLogger implements GeyserLogger {
|
||||
private final Logger logger;
|
||||
@Getter @Setter
|
||||
private boolean debug;
|
||||
|
||||
public GeyserBungeeLogger(Logger logger, boolean debug) {
|
||||
this.logger = logger;
|
||||
this.debug = debug;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void severe(String message) {
|
||||
logger.severe(message);
|
||||
|
@ -58,14 +58,13 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
||||
|
||||
private GeyserCommandManager geyserCommandManager;
|
||||
private GeyserBungeeConfiguration geyserConfig;
|
||||
private GeyserBungeeInjector geyserInjector;
|
||||
private GeyserBungeeLogger geyserLogger;
|
||||
private final GeyserBungeeLogger geyserLogger = new GeyserBungeeLogger(getLogger());
|
||||
private IGeyserPingPassthrough geyserBungeePingPassthrough;
|
||||
|
||||
private GeyserImpl geyser;
|
||||
@ -82,21 +81,21 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
||||
// Copied from ViaVersion.
|
||||
// https://github.com/ViaVersion/ViaVersion/blob/b8072aad86695cc8ec6f5e4103e43baf3abf6cc5/bungee/src/main/java/us/myles/ViaVersion/BungeePlugin.java#L43
|
||||
try {
|
||||
ProtocolConstants.class.getField("MINECRAFT_1_20_3");
|
||||
ProtocolConstants.class.getField("MINECRAFT_1_21");
|
||||
} catch (NoSuchFieldException e) {
|
||||
getLogger().warning(" / \\");
|
||||
getLogger().warning(" / \\");
|
||||
getLogger().warning(" / | \\");
|
||||
getLogger().warning(" / | \\ " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_proxy", getProxy().getName()));
|
||||
getLogger().warning(" / \\ " + GeyserLocale.getLocaleStringLog("geyser.may_not_work_as_intended_all_caps"));
|
||||
getLogger().warning(" / o \\");
|
||||
getLogger().warning("/_____________\\");
|
||||
geyserLogger.error(" / \\");
|
||||
geyserLogger.error(" / \\");
|
||||
geyserLogger.error(" / | \\");
|
||||
geyserLogger.error(" / | \\ " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_proxy", getProxy().getName()));
|
||||
geyserLogger.error(" / \\ " + GeyserLocale.getLocaleStringLog("geyser.may_not_work_as_intended_all_caps"));
|
||||
geyserLogger.error(" / o \\");
|
||||
geyserLogger.error("/_____________\\");
|
||||
}
|
||||
|
||||
if (!this.loadConfig()) {
|
||||
return;
|
||||
}
|
||||
this.geyserLogger = new GeyserBungeeLogger(getLogger(), geyserConfig.isDebugMode());
|
||||
this.geyserLogger.setDebug(geyserConfig.isDebugMode());
|
||||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
||||
this.geyser = GeyserImpl.load(PlatformType.BUNGEECORD, this);
|
||||
this.geyserInjector = new GeyserBungeeInjector(this);
|
||||
@ -293,7 +292,7 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
||||
"config.yml", (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this);
|
||||
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserBungeeConfiguration.class);
|
||||
} catch (IOException ex) {
|
||||
getLogger().log(Level.SEVERE, GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
|
||||
geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
|
||||
ex.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
|
@ -6,6 +6,13 @@ loom {
|
||||
mixin.defaultRefmapName.set("geyser-refmap.json")
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
// We don't need these
|
||||
tasks.named("remapModrinthJar").configure {
|
||||
enabled = false
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(projects.core)
|
||||
compileOnly(libs.mixin)
|
||||
|
@ -63,6 +63,7 @@ tasks {
|
||||
|
||||
modrinth {
|
||||
loaders.add("fabric")
|
||||
uploadFile.set(tasks.getByPath("remapModrinthJar"))
|
||||
dependencies {
|
||||
required.project("fabric-api")
|
||||
}
|
||||
|
@ -23,8 +23,8 @@
|
||||
"geyser.mixins.json"
|
||||
],
|
||||
"depends": {
|
||||
"fabricloader": ">=0.15.10",
|
||||
"fabricloader": ">=0.15.11",
|
||||
"fabric": "*",
|
||||
"minecraft": ">=1.20.5"
|
||||
"minecraft": ">=1.21"
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,9 @@ dependencies {
|
||||
|
||||
// Let's shade in our own api
|
||||
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
|
||||
includeTransitive(projects.core)
|
||||
@ -53,4 +55,5 @@ tasks {
|
||||
|
||||
modrinth {
|
||||
loaders.add("neoforge")
|
||||
uploadFile.set(tasks.getByPath("remapModrinthJar"))
|
||||
}
|
@ -27,6 +27,7 @@ package org.geysermc.geyser.platform.neoforge;
|
||||
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.neoforged.fml.ModContainer;
|
||||
import net.neoforged.fml.common.Mod;
|
||||
import net.neoforged.fml.loading.FMLLoader;
|
||||
import net.neoforged.neoforge.common.NeoForge;
|
||||
@ -43,8 +44,8 @@ public class GeyserNeoForgeBootstrap extends GeyserModBootstrap {
|
||||
|
||||
private final GeyserNeoForgePermissionHandler permissionHandler = new GeyserNeoForgePermissionHandler();
|
||||
|
||||
public GeyserNeoForgeBootstrap() {
|
||||
super(new GeyserNeoForgePlatform());
|
||||
public GeyserNeoForgeBootstrap(ModContainer container) {
|
||||
super(new GeyserNeoForgePlatform(container));
|
||||
|
||||
if (isServer()) {
|
||||
// Set as an event so we can get the proper IP and port if needed
|
||||
|
@ -26,20 +26,29 @@
|
||||
package org.geysermc.geyser.platform.neoforge;
|
||||
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.neoforged.fml.ModContainer;
|
||||
import net.neoforged.fml.ModList;
|
||||
import net.neoforged.fml.loading.FMLPaths;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.GeyserBootstrap;
|
||||
import org.geysermc.geyser.api.util.PlatformType;
|
||||
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
||||
import org.geysermc.geyser.platform.mod.GeyserModBootstrap;
|
||||
import org.geysermc.geyser.platform.mod.platform.GeyserModPlatform;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class GeyserNeoForgePlatform implements GeyserModPlatform {
|
||||
|
||||
private final ModContainer container;
|
||||
|
||||
public GeyserNeoForgePlatform(ModContainer container) {
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull PlatformType platformType() {
|
||||
return PlatformType.NEOFORGE;
|
||||
@ -62,11 +71,21 @@ public class GeyserNeoForgePlatform implements GeyserModPlatform {
|
||||
|
||||
@Override
|
||||
public boolean testFloodgatePluginPresent(@NonNull GeyserModBootstrap bootstrap) {
|
||||
return false; // No Floodgate mod for NeoForge yet
|
||||
if (ModList.get().isLoaded("floodgate")) {
|
||||
Path floodgateDataFolder = FMLPaths.CONFIGDIR.get().resolve("floodgate");
|
||||
bootstrap.getGeyserConfig().loadFloodgate(bootstrap, floodgateDataFolder);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable InputStream resolveResource(@NonNull String resource) {
|
||||
return GeyserBootstrap.class.getClassLoader().getResourceAsStream(resource);
|
||||
try {
|
||||
Path path = container.getModInfo().getOwningFile().getFile().findResource(resource);
|
||||
return Files.newInputStream(path);
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,12 +14,12 @@ config = "geyser.mixins.json"
|
||||
[[dependencies.geyser_neoforge]]
|
||||
modId="neoforge"
|
||||
type="required"
|
||||
versionRange="[20.5.0-beta,)"
|
||||
versionRange="[21.0.0-beta,)"
|
||||
ordering="NONE"
|
||||
side="BOTH"
|
||||
[[dependencies.geyser_neoforge]]
|
||||
modId="minecraft"
|
||||
type="required"
|
||||
versionRange="[1.20.5,1.21)"
|
||||
versionRange="[1.21,)"
|
||||
ordering="NONE"
|
||||
side="BOTH"
|
@ -34,7 +34,6 @@ import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.Commands;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.GeyserBootstrap;
|
||||
@ -80,7 +79,7 @@ public abstract class GeyserModBootstrap implements GeyserBootstrap {
|
||||
private GeyserCommandManager geyserCommandManager;
|
||||
private GeyserModConfiguration geyserConfig;
|
||||
private GeyserModInjector geyserInjector;
|
||||
private GeyserModLogger geyserLogger;
|
||||
private final GeyserModLogger geyserLogger = new GeyserModLogger();
|
||||
private IGeyserPingPassthrough geyserPingPassthrough;
|
||||
private WorldManager geyserWorldManager;
|
||||
|
||||
@ -92,7 +91,7 @@ public abstract class GeyserModBootstrap implements GeyserBootstrap {
|
||||
if (!loadConfig()) {
|
||||
return;
|
||||
}
|
||||
this.geyserLogger = new GeyserModLogger(geyserConfig.isDebugMode());
|
||||
this.geyserLogger.setDebug(geyserConfig.isDebugMode());
|
||||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
||||
this.geyser = GeyserImpl.load(this.platform.platformType(), this);
|
||||
|
||||
@ -288,7 +287,7 @@ public abstract class GeyserModBootstrap implements GeyserBootstrap {
|
||||
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserModConfiguration.class);
|
||||
return true;
|
||||
} catch (IOException ex) {
|
||||
LogManager.getLogger("geyser").error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
|
||||
geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
|
||||
ex.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
|
@ -37,10 +37,6 @@ public class GeyserModLogger implements GeyserLogger {
|
||||
|
||||
private boolean debug;
|
||||
|
||||
public GeyserModLogger(boolean isDebug) {
|
||||
debug = isDebug;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void severe(String message) {
|
||||
logger.fatal(message);
|
||||
|
@ -25,13 +25,7 @@
|
||||
|
||||
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.legacy.LegacyComponentSerializer;
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
@ -39,33 +33,26 @@ import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.network.Filterable;
|
||||
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.block.Block;
|
||||
import net.minecraft.world.level.block.entity.BannerBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.BannerPatternLayers;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.LecternBlockEntity;
|
||||
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.status.ChunkStatus;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
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.network.GameProtocol;
|
||||
import org.geysermc.geyser.platform.mod.GeyserModBootstrap;
|
||||
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.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
@ -73,7 +60,6 @@ import java.util.concurrent.CompletableFuture;
|
||||
public class GeyserModWorldManager extends GeyserWorldManager {
|
||||
|
||||
private static final GsonComponentSerializer GSON_SERIALIZER = GsonComponentSerializer.gson();
|
||||
private static final LegacyComponentSerializer LEGACY_SERIALIZER = LegacyComponentSerializer.legacySection();
|
||||
private final MinecraftServer server;
|
||||
|
||||
public GeyserModWorldManager(MinecraftServer server) {
|
||||
@ -121,94 +107,6 @@ public class GeyserModWorldManager extends GeyserWorldManager {
|
||||
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
|
||||
public boolean hasPermission(GeyserSession session, String permission) {
|
||||
ServerPlayer player = getPlayer(session);
|
||||
@ -267,39 +165,6 @@ public class GeyserModWorldManager extends GeyserWorldManager {
|
||||
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) {
|
||||
String json = Component.Serializer.toJson(component, RegistryAccess.EMPTY);
|
||||
return GSON_SERIALIZER.deserializeOr(json, net.kyori.adventure.text.Component.empty());
|
||||
@ -309,7 +174,7 @@ public class GeyserModWorldManager extends GeyserWorldManager {
|
||||
return patternLayers.layers().stream()
|
||||
.map(layer -> {
|
||||
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());
|
||||
})
|
||||
|
@ -4,6 +4,12 @@ dependencies {
|
||||
isTransitive = false
|
||||
}
|
||||
|
||||
implementation(libs.erosion.bukkit.nms) {
|
||||
attributes {
|
||||
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 21)
|
||||
}
|
||||
}
|
||||
|
||||
implementation(variantOf(libs.adapters.spigot) {
|
||||
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:.*"))
|
||||
}
|
||||
}
|
||||
|
||||
modrinth {
|
||||
uploadFile.set(tasks.getByPath("shadowJar"))
|
||||
loaders.addAll("spigot", "paper")
|
||||
}
|
||||
|
@ -34,8 +34,8 @@ import java.util.logging.Logger;
|
||||
public final class GeyserPaperLogger extends GeyserSpigotLogger {
|
||||
private final ComponentLogger componentLogger;
|
||||
|
||||
public GeyserPaperLogger(Plugin plugin, Logger logger, boolean debug) {
|
||||
super(logger, debug);
|
||||
public GeyserPaperLogger(Plugin plugin, Logger logger) {
|
||||
super(logger);
|
||||
componentLogger = plugin.getComponentLogger();
|
||||
}
|
||||
|
||||
|
@ -25,15 +25,15 @@
|
||||
|
||||
package org.geysermc.geyser.platform.spigot;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.geysermc.geyser.GeyserLogger;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@AllArgsConstructor
|
||||
@RequiredArgsConstructor
|
||||
public class GeyserSpigotLogger implements GeyserLogger {
|
||||
private final Logger logger;
|
||||
@Getter @Setter
|
||||
|
@ -79,14 +79,14 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||
|
||||
private GeyserSpigotCommandManager geyserCommandManager;
|
||||
private GeyserSpigotConfiguration geyserConfig;
|
||||
private GeyserSpigotInjector geyserInjector;
|
||||
private GeyserSpigotLogger geyserLogger;
|
||||
private final GeyserSpigotLogger geyserLogger = GeyserPaperLogger.supported() ?
|
||||
new GeyserPaperLogger(this, getLogger()) : new GeyserSpigotLogger(getLogger());
|
||||
private IGeyserPingPassthrough geyserSpigotPingPassthrough;
|
||||
private GeyserSpigotWorldManager geyserWorldManager;
|
||||
|
||||
@ -114,12 +114,12 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||
// We depend on this as a fallback in certain scenarios
|
||||
BlockData.class.getMethod("getAsString");
|
||||
} catch (ClassNotFoundException | NoSuchMethodException e) {
|
||||
getLogger().severe("*********************************************");
|
||||
getLogger().severe("");
|
||||
getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server.header"));
|
||||
getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server.message", "1.13.2"));
|
||||
getLogger().severe("");
|
||||
getLogger().severe("*********************************************");
|
||||
geyserLogger.error("*********************************************");
|
||||
geyserLogger.error("");
|
||||
geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server.header"));
|
||||
geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server.message", "1.13.2"));
|
||||
geyserLogger.error("");
|
||||
geyserLogger.error("*********************************************");
|
||||
Bukkit.getPluginManager().disablePlugin(this);
|
||||
return;
|
||||
}
|
||||
@ -128,12 +128,12 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||
Class.forName("net.md_5.bungee.chat.ComponentSerializer");
|
||||
} catch (ClassNotFoundException e) {
|
||||
if (!PaperAdventure.canSendMessageUsingComponent()) { // Prepare for Paper eventually removing Bungee chat
|
||||
getLogger().severe("*********************************************");
|
||||
getLogger().severe("");
|
||||
getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server_type.header", getServer().getName()));
|
||||
getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server_type.message", "Paper"));
|
||||
getLogger().severe("");
|
||||
getLogger().severe("*********************************************");
|
||||
geyserLogger.error("*********************************************");
|
||||
geyserLogger.error("");
|
||||
geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server_type.header", getServer().getName()));
|
||||
geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server_type.message", "Paper"));
|
||||
geyserLogger.error("");
|
||||
geyserLogger.error("*********************************************");
|
||||
Bukkit.getPluginManager().disablePlugin(this);
|
||||
return;
|
||||
}
|
||||
@ -142,11 +142,11 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||
try {
|
||||
Class.forName("io.netty.util.internal.ObjectPool$ObjectCreator");
|
||||
} catch (ClassNotFoundException e) {
|
||||
getLogger().severe("*********************************************");
|
||||
getLogger().severe("");
|
||||
getLogger().severe("This version of Spigot is using an outdated version of netty. Please use Paper instead!");
|
||||
getLogger().severe("");
|
||||
getLogger().severe("*********************************************");
|
||||
geyserLogger.error("*********************************************");
|
||||
geyserLogger.error("");
|
||||
geyserLogger.error("This version of Spigot is using an outdated version of netty. Please use Paper instead!");
|
||||
geyserLogger.error("");
|
||||
geyserLogger.error("*********************************************");
|
||||
Bukkit.getPluginManager().disablePlugin(this);
|
||||
return;
|
||||
}
|
||||
@ -154,8 +154,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||
if (!loadConfig()) {
|
||||
return;
|
||||
}
|
||||
this.geyserLogger = GeyserPaperLogger.supported() ? new GeyserPaperLogger(this, getLogger(), geyserConfig.isDebugMode())
|
||||
: new GeyserSpigotLogger(getLogger(), geyserConfig.isDebugMode());
|
||||
this.geyserLogger.setDebug(geyserConfig.isDebugMode());
|
||||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
||||
|
||||
// Turn "(MC: 1.16.4)" into 1.16.4.
|
||||
@ -252,6 +251,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||
SpigotAdapters.registerWorldAdapter(nmsVersion);
|
||||
geyserLogger.debug("Using spigot NMS adapter for nms version: " + nmsVersion);
|
||||
} catch (Exception e) { // Likely running on Paper 1.20.5+
|
||||
geyserLogger.debug("Unable to find spigot world manager: " + e.getMessage());
|
||||
//noinspection deprecation
|
||||
int protocolVersion = Bukkit.getUnsafe().getProtocolVersion();
|
||||
PaperAdapters.registerClosestWorldAdapter(protocolVersion);
|
||||
@ -266,7 +266,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||
this.geyserWorldManager = new GeyserSpigotNativeWorldManager(this, isPaper);
|
||||
}
|
||||
geyserLogger.debug("Using world manager of type: " + this.geyserWorldManager.getClass().getSimpleName());
|
||||
} catch (Exception e) {
|
||||
} catch (Throwable e) {
|
||||
if (geyserConfig.isDebugMode()) {
|
||||
geyserLogger.debug("Error while attempting to find NMS adapter. Most likely, this can be safely ignored. :)");
|
||||
e.printStackTrace();
|
||||
@ -486,7 +486,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||
(x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this);
|
||||
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserSpigotConfiguration.class);
|
||||
} catch (IOException ex) {
|
||||
getLogger().log(Level.SEVERE, GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
|
||||
geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
|
||||
ex.printStackTrace();
|
||||
Bukkit.getPluginManager().disablePlugin(this);
|
||||
return false;
|
||||
|
@ -25,10 +25,8 @@
|
||||
|
||||
package org.geysermc.geyser.platform.spigot.world;
|
||||
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.level.block.value.PistonValueType;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntArrayMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
@ -40,13 +38,17 @@ import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.BlockPistonEvent;
|
||||
import org.bukkit.event.block.BlockPistonExtendEvent;
|
||||
import org.bukkit.event.block.BlockPistonRetractEvent;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
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.platform.spigot.world.manager.GeyserSpigotWorldManager;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.PistonCache;
|
||||
import org.geysermc.geyser.translator.level.block.entity.PistonBlockEntity;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.level.block.value.PistonValueType;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -85,7 +87,7 @@ public class GeyserPistonListener implements Listener {
|
||||
PistonValueType type = isExtend ? PistonValueType.PUSHING : PistonValueType.PULLING;
|
||||
boolean sticky = event.isSticky();
|
||||
|
||||
Object2IntMap<Vector3i> attachedBlocks = new Object2IntArrayMap<>();
|
||||
Object2ObjectMap<Vector3i, BlockState> attachedBlocks = new Object2ObjectArrayMap<>();
|
||||
boolean blocksFilled = false;
|
||||
|
||||
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();
|
||||
for (Block block : blocks) {
|
||||
Location attachedLocation = block.getLocation();
|
||||
int blockId = worldManager.getBlockNetworkId(block);
|
||||
BlockState state = BlockState.of(worldManager.getBlockNetworkId(block));
|
||||
// Ignore blocks that will be destroyed
|
||||
if (BlockStateValues.canPistonMoveBlock(blockId, isExtend)) {
|
||||
attachedBlocks.put(getVector(attachedLocation), blockId);
|
||||
if (BlockStateValues.canPistonMoveBlock(state, isExtend)) {
|
||||
attachedBlocks.put(getVector(attachedLocation), state);
|
||||
}
|
||||
}
|
||||
blocksFilled = true;
|
||||
@ -119,7 +121,7 @@ public class GeyserPistonListener implements Listener {
|
||||
|
||||
int pistonBlockId = worldManager.getBlockNetworkId(event.getBlock());
|
||||
// event.getDirection() is unreliable
|
||||
Direction orientation = BlockStateValues.getPistonOrientation(pistonBlockId);
|
||||
Direction orientation = BlockState.of(pistonBlockId).getValue(Properties.FACING);
|
||||
|
||||
session.executeInEventLoop(() -> {
|
||||
PistonCache pistonCache = session.getPistonCache();
|
||||
|
@ -33,7 +33,7 @@ import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.BlockPlaceEvent;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.geyser.platform.spigot.world.manager.GeyserSpigotWorldManager;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
@ -59,11 +59,11 @@ public class GeyserSpigotBlockPlaceListener implements Listener {
|
||||
event.getBlockPlaced().getX(), event.getBlockPlaced().getY(), event.getBlockPlaced().getZ())));
|
||||
} else {
|
||||
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(":");
|
||||
session.sendUpstreamPacket(placeBlockSoundPacket);
|
||||
session.setLastBlockPlacePosition(null);
|
||||
session.setLastBlockPlacedId(null);
|
||||
session.setLastBlockPlaced(null);
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ import org.geysermc.geyser.adapters.WorldAdapter;
|
||||
import org.geysermc.geyser.adapters.paper.PaperAdapters;
|
||||
import org.geysermc.geyser.adapters.spigot.SpigotAdapters;
|
||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
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) {
|
||||
Player player = Bukkit.getPlayer(session.getPlayerEntity().getUsername());
|
||||
if (player == null) {
|
||||
return BlockStateValues.JAVA_AIR_ID;
|
||||
return Block.JAVA_AIR_ID;
|
||||
}
|
||||
return adapter.getBlockAt(player.getWorld(), x, y, z);
|
||||
}
|
||||
|
@ -25,30 +25,24 @@
|
||||
|
||||
package org.geysermc.geyser.platform.spigot.world.manager;
|
||||
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityInfo;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.geysermc.erosion.bukkit.BukkitLecterns;
|
||||
import org.geysermc.erosion.bukkit.BukkitUtils;
|
||||
import org.geysermc.erosion.bukkit.PickBlockUtils;
|
||||
import org.geysermc.erosion.bukkit.SchedulerUtils;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.level.GameRule;
|
||||
import org.geysermc.geyser.level.WorldManager;
|
||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
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.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@ -57,23 +51,21 @@ import java.util.concurrent.CompletableFuture;
|
||||
*/
|
||||
public class GeyserSpigotWorldManager extends WorldManager {
|
||||
private final Plugin plugin;
|
||||
private final BukkitLecterns lecterns;
|
||||
|
||||
public GeyserSpigotWorldManager(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
this.lecterns = new BukkitLecterns(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlockAt(GeyserSession session, int x, int y, int z) {
|
||||
Player bukkitPlayer;
|
||||
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();
|
||||
if (!world.isChunkLoaded(x >> 4, z >> 4)) {
|
||||
// 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));
|
||||
@ -84,9 +76,9 @@ public class GeyserSpigotWorldManager extends WorldManager {
|
||||
// Terrible behavior, but this is basically what's always been happening behind the scenes anyway.
|
||||
CompletableFuture<String> blockData = new CompletableFuture<>();
|
||||
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
|
||||
@ -94,69 +86,6 @@ public class GeyserSpigotWorldManager extends WorldManager {
|
||||
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) {
|
||||
org.bukkit.GameRule<?> bukkitGameRule = org.bukkit.GameRule.getByName(gameRule.getJavaID());
|
||||
if (bukkitGameRule == null) {
|
||||
@ -205,17 +134,16 @@ public class GeyserSpigotWorldManager extends WorldManager {
|
||||
|
||||
@Override
|
||||
public @NonNull CompletableFuture<@Nullable DataComponents> getPickItemComponents(GeyserSession session, int x, int y, int z, boolean addNbtData) {
|
||||
CompletableFuture<@Nullable DataComponents> future = new CompletableFuture<>();
|
||||
Player bukkitPlayer;
|
||||
if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUuid())) == null) {
|
||||
future.complete(null);
|
||||
return future;
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
CompletableFuture<Int2ObjectMap<byte[]>> future = new CompletableFuture<>();
|
||||
Block block = bukkitPlayer.getWorld().getBlockAt(x, y, z);
|
||||
// Paper 1.19.3 complains about async access otherwise.
|
||||
// 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
|
||||
return future;
|
||||
SchedulerUtils.runTask(this.plugin, () -> future.complete(PickBlockUtils.pickBlock(block)), block);
|
||||
return future.thenApply(RAW_TRANSFORMER);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -71,7 +71,7 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
||||
|
||||
private GeyserCommandManager geyserCommandManager;
|
||||
private GeyserStandaloneConfiguration geyserConfig;
|
||||
private GeyserStandaloneLogger geyserLogger;
|
||||
private final GeyserStandaloneLogger geyserLogger = new GeyserStandaloneLogger();
|
||||
private IGeyserPingPassthrough geyserPingPassthrough;
|
||||
private GeyserStandaloneGUI gui;
|
||||
@Getter
|
||||
@ -181,8 +181,6 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
||||
}
|
||||
}
|
||||
|
||||
this.geyserLogger = new GeyserStandaloneLogger();
|
||||
|
||||
if (useGui && gui == null) {
|
||||
gui = new GeyserStandaloneGUI(geyserLogger);
|
||||
gui.redirectSystemStreams();
|
||||
|
@ -70,3 +70,8 @@ tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
|
||||
exclude(dependency("net.kyori:adventure-nbt:.*"))
|
||||
}
|
||||
}
|
||||
|
||||
modrinth {
|
||||
uploadFile.set(tasks.getByPath("shadowJar"))
|
||||
loaders.addAll("velocity")
|
||||
}
|
@ -25,13 +25,13 @@
|
||||
|
||||
package org.geysermc.geyser.platform.velocity;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.geysermc.geyser.GeyserLogger;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
@AllArgsConstructor
|
||||
@RequiredArgsConstructor
|
||||
public class GeyserVelocityLogger implements GeyserLogger {
|
||||
private final Logger logger;
|
||||
@Getter @Setter
|
||||
|
@ -64,44 +64,44 @@ import java.util.UUID;
|
||||
|
||||
@Plugin(id = "geyser", name = GeyserImpl.NAME + "-Velocity", version = GeyserImpl.VERSION, url = "https://geysermc.org", authors = "GeyserMC")
|
||||
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 GeyserVelocityConfiguration geyserConfig;
|
||||
private GeyserVelocityInjector geyserInjector;
|
||||
private GeyserVelocityLogger geyserLogger;
|
||||
private IGeyserPingPassthrough geyserPingPassthrough;
|
||||
|
||||
private GeyserImpl geyser;
|
||||
|
||||
@Getter
|
||||
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
|
||||
public void onGeyserInitialize() {
|
||||
GeyserLocale.init(this);
|
||||
|
||||
if (!ProtocolVersion.isSupported(GameProtocol.getJavaProtocolVersion())) {
|
||||
logger.error(" / \\");
|
||||
logger.error(" / \\");
|
||||
logger.error(" / | \\");
|
||||
logger.error(" / | \\ " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_proxy", proxyServer.getVersion().getName()));
|
||||
logger.error(" / \\ " + GeyserLocale.getLocaleStringLog("geyser.may_not_work_as_intended_all_caps"));
|
||||
logger.error(" / o \\");
|
||||
logger.error("/_____________\\");
|
||||
geyserLogger.error(" / \\");
|
||||
geyserLogger.error(" / \\");
|
||||
geyserLogger.error(" / | \\");
|
||||
geyserLogger.error(" / | \\ " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_proxy", proxyServer.getVersion().getName()));
|
||||
geyserLogger.error(" / \\ " + GeyserLocale.getLocaleStringLog("geyser.may_not_work_as_intended_all_caps"));
|
||||
geyserLogger.error(" / o \\");
|
||||
geyserLogger.error("/_____________\\");
|
||||
}
|
||||
|
||||
if (!loadConfig()) {
|
||||
return;
|
||||
}
|
||||
this.geyserLogger = new GeyserVelocityLogger(logger, geyserConfig.isDebugMode());
|
||||
this.geyserLogger.setDebug(geyserConfig.isDebugMode());
|
||||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
||||
|
||||
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);
|
||||
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserVelocityConfiguration.class);
|
||||
} catch (IOException ex) {
|
||||
logger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
|
||||
geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
|
||||
ex.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
|
@ -27,14 +27,23 @@ package org.geysermc.geyser.platform.viaproxy;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import net.raphimc.vialegacy.api.LegacyProtocolVersion;
|
||||
import net.raphimc.viaproxy.ViaProxy;
|
||||
import net.raphimc.viaproxy.protocoltranslator.viaproxy.ViaProxyConfig;
|
||||
import org.geysermc.geyser.configuration.GeyserJacksonConfiguration;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@SuppressWarnings("FieldMayBeFinal") // Jackson requires that the fields are not final
|
||||
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
|
||||
public Path getFloodgateKeyPath() {
|
||||
return new File(GeyserViaProxyPlugin.ROOT_FOLDER, this.getFloodgateKeyFile()).toPath();
|
||||
@ -50,4 +59,9 @@ public class GeyserViaProxyConfiguration extends GeyserJacksonConfiguration {
|
||||
return interval;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemoteConfiguration getRemote() {
|
||||
return this.remote;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ import org.apache.logging.log4j.LogManager;
|
||||
import org.geysermc.geyser.GeyserBootstrap;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.GeyserLogger;
|
||||
import org.geysermc.geyser.api.event.EventRegistrar;
|
||||
import org.geysermc.geyser.api.network.AuthType;
|
||||
import org.geysermc.geyser.api.util.PlatformType;
|
||||
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.ping.GeyserLegacyPingPassthrough;
|
||||
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
|
||||
import org.geysermc.geyser.platform.viaproxy.listener.GeyserServerTransferListener;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
import org.geysermc.geyser.util.FileUtils;
|
||||
@ -57,7 +59,7 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
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");
|
||||
|
||||
@ -120,6 +122,7 @@ public class GeyserViaProxyPlugin extends ViaProxyPlugin implements GeyserBootst
|
||||
}
|
||||
|
||||
this.geyser = GeyserImpl.load(PlatformType.VIAPROXY, this);
|
||||
this.geyser.eventBus().register(this, new GeyserServerTransferListener());
|
||||
LoopbackUtil.checkAndApplyLoopback(this.logger);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -12,6 +12,9 @@ repositories {
|
||||
}
|
||||
|
||||
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.shadow)
|
||||
implementation(libs.architectury.plugin)
|
||||
|
6
build-logic/src/main/kotlin/LibsAccessor.kt
Normale Datei
6
build-logic/src/main/kotlin/LibsAccessor.kt
Normale Datei
@ -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()
|
@ -8,7 +8,6 @@ plugins {
|
||||
id("geyser.publish-conventions")
|
||||
id("architectury-plugin")
|
||||
id("dev.architectury.loom")
|
||||
id("com.modrinth.minotaur")
|
||||
}
|
||||
|
||||
// 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")
|
||||
|
||||
architectury {
|
||||
minecraft = "1.20.5"
|
||||
minecraft = libs.minecraft.get().version as String
|
||||
}
|
||||
|
||||
loom {
|
||||
@ -83,7 +82,7 @@ tasks {
|
||||
register("remapModrinthJar", RemapJarTask::class) {
|
||||
dependsOn(shadowJar)
|
||||
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("")
|
||||
}
|
||||
}
|
||||
@ -93,7 +92,7 @@ afterEvaluate {
|
||||
|
||||
// These are shaded, no need to JiJ them
|
||||
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}")
|
||||
}
|
||||
|
||||
@ -101,39 +100,24 @@ afterEvaluate {
|
||||
configurations["includeTransitive"].resolvedConfiguration.resolvedArtifacts.forEach { dep ->
|
||||
if (!providedDependencies.contains("${dep.moduleVersion.id.group}:${dep.moduleVersion.id.name}")
|
||||
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())
|
||||
} else {
|
||||
println("Not including ${dep.id} for ${project.name}!")
|
||||
//println("Not including ${dep.id} for ${project.name}!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
minecraft("com.mojang:minecraft:1.20.5")
|
||||
minecraft(libs.minecraft)
|
||||
mappings(loom.officialMojangMappings())
|
||||
}
|
||||
|
||||
repositories {
|
||||
// mavenLocal()
|
||||
maven("https://repo.opencollab.dev/maven-releases/")
|
||||
maven("https://repo.opencollab.dev/maven-snapshots/")
|
||||
maven("https://repo.opencollab.dev/main")
|
||||
maven("https://jitpack.io")
|
||||
maven("https://oss.sonatype.org/content/repositories/snapshots/")
|
||||
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
|
||||
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)
|
||||
}
|
@ -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("A changelog can be found at https://github.com/GeyserMC/Geyser/commits")
|
||||
gameVersions.add(libs.minecraft.get().version as String)
|
||||
failSilently.set(true)
|
||||
|
||||
syncBodyFrom.set(rootProject.file("README.md").readText())
|
||||
}
|
@ -26,6 +26,14 @@ val moddedPlatforms = setOf(
|
||||
projects.mod
|
||||
).map { it.dependencyProject }
|
||||
|
||||
val modrinthPlatforms = setOf(
|
||||
projects.bungeecord,
|
||||
projects.fabric,
|
||||
projects.neoforge,
|
||||
projects.spigot,
|
||||
projects.velocity
|
||||
).map { it.dependencyProject }
|
||||
|
||||
subprojects {
|
||||
apply {
|
||||
plugin("java-library")
|
||||
@ -38,4 +46,10 @@ subprojects {
|
||||
in moddedPlatforms -> plugins.apply("geyser.modded-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")
|
||||
}
|
||||
}
|
@ -24,7 +24,6 @@ dependencies {
|
||||
implementation(libs.websocket)
|
||||
|
||||
api(libs.bundles.protocol)
|
||||
implementation(libs.blockstateupdater)
|
||||
|
||||
api(libs.mcauthlib)
|
||||
api(libs.mcprotocollib) {
|
||||
|
@ -45,6 +45,7 @@ import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
|
||||
import org.geysermc.api.Geyser;
|
||||
import org.geysermc.cumulus.form.Form;
|
||||
import org.geysermc.cumulus.form.util.FormBuilder;
|
||||
import org.geysermc.erosion.packet.Packets;
|
||||
import org.geysermc.floodgate.crypto.AesCipher;
|
||||
import org.geysermc.floodgate.crypto.AesKeyProducer;
|
||||
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.PendingMicrosoftAuthentication;
|
||||
import org.geysermc.geyser.session.SessionManager;
|
||||
import org.geysermc.geyser.session.cache.RegistryCache;
|
||||
import org.geysermc.geyser.skin.FloodgateSkinUploader;
|
||||
import org.geysermc.geyser.skin.ProvidedSkins;
|
||||
import org.geysermc.geyser.skin.SkinProvider;
|
||||
@ -211,6 +213,8 @@ public class GeyserImpl implements GeyserApi {
|
||||
Registries.init();
|
||||
BlockRegistries.init();
|
||||
|
||||
RegistryCache.init();
|
||||
|
||||
/* Initialize translators */
|
||||
EntityDefinitions.init();
|
||||
MessageTranslator.init();
|
||||
@ -383,7 +387,7 @@ public class GeyserImpl implements GeyserApi {
|
||||
|
||||
this.newsHandler = new NewsHandler(BRANCH, this.buildNumber());
|
||||
|
||||
//Packets.initGeyser();
|
||||
Packets.initGeyser();
|
||||
|
||||
if (Epoll.isAvailable()) {
|
||||
this.erosionUnixListener = new UnixSocketClientListener();
|
||||
@ -766,6 +770,7 @@ public class GeyserImpl implements GeyserApi {
|
||||
return 0;
|
||||
}
|
||||
|
||||
//noinspection DataFlowIssue
|
||||
return Integer.parseInt(BUILD_NUMBER);
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2024 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@ -72,8 +72,10 @@ public interface GeyserConfiguration {
|
||||
|
||||
boolean isDebugMode();
|
||||
|
||||
@Deprecated
|
||||
boolean isAllowThirdPartyCapes();
|
||||
|
||||
@Deprecated
|
||||
boolean isAllowThirdPartyEars();
|
||||
|
||||
String getShowCooldown();
|
||||
|
@ -94,7 +94,7 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
|
||||
private boolean debugMode = false;
|
||||
|
||||
@JsonProperty("allow-third-party-capes")
|
||||
private boolean allowThirdPartyCapes = true;
|
||||
private boolean allowThirdPartyCapes = false;
|
||||
|
||||
@JsonProperty("show-cooldown")
|
||||
private String showCooldown = "title";
|
||||
|
@ -25,6 +25,8 @@
|
||||
|
||||
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.type.BooleanEntityMetadata;
|
||||
@ -63,6 +65,9 @@ public final class EntityDefinitions {
|
||||
public static final EntityDefinition<BeeEntity> BEE;
|
||||
public static final EntityDefinition<BlazeEntity> BLAZE;
|
||||
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<CatEntity> CAT;
|
||||
public static final EntityDefinition<SpiderEntity> CAVE_SPIDER;
|
||||
@ -165,6 +170,7 @@ public final class EntityDefinitions {
|
||||
public static final EntityDefinition<VindicatorEntity> VINDICATOR;
|
||||
public static final EntityDefinition<AbstractMerchantEntity> WANDERING_TRADER;
|
||||
public static final EntityDefinition<WardenEntity> WARDEN;
|
||||
public static final EntityDefinition<AbstractWindChargeEntity> WIND_CHARGE;
|
||||
public static final EntityDefinition<RaidParticipantEntity> WITCH;
|
||||
public static final EntityDefinition<WitherEntity> WITHER;
|
||||
public static final EntityDefinition<AbstractSkeletonEntity> WITHER_SKELETON;
|
||||
@ -235,7 +241,7 @@ public final class EntityDefinitions {
|
||||
.addTranslator(MetadataType.BOOLEAN,
|
||||
(enderCrystalEntity, entityMetadata) -> enderCrystalEntity.setFlag(EntityFlag.SHOW_BOTTOM, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue())) // There is a base located on the ender crystal
|
||||
.build();
|
||||
EXPERIENCE_ORB = EntityDefinition.<ExpOrbEntity>inherited(null, entityBase)
|
||||
EXPERIENCE_ORB = EntityDefinition.inherited(ExpOrbEntity::new, entityBase)
|
||||
.type(EntityType.EXPERIENCE_ORB)
|
||||
.identifier("minecraft:xp_orb")
|
||||
.build();
|
||||
@ -297,6 +303,7 @@ public final class EntityDefinitions {
|
||||
TNT = EntityDefinition.inherited(TNTEntity::new, entityBase)
|
||||
.type(EntityType.TNT)
|
||||
.heightAndWidth(0.98f)
|
||||
.offset(0.49f)
|
||||
.addTranslator(MetadataType.INT, TNTEntity::setFuseLength)
|
||||
.build();
|
||||
|
||||
@ -374,6 +381,18 @@ public final class EntityDefinitions {
|
||||
.heightAndWidth(0.25f)
|
||||
.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)
|
||||
.addTranslator(MetadataType.BYTE, AbstractArrowEntity::setArrowFlags)
|
||||
.addTranslator(null) // "Piercing level"
|
||||
@ -502,11 +521,20 @@ public final class EntityDefinitions {
|
||||
.height(0.9f).width(0.5f)
|
||||
.addTranslator(MetadataType.BYTE, BatEntity::setBatFlags)
|
||||
.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)
|
||||
.type(EntityType.BLAZE)
|
||||
.height(1.8f).width(0.6f)
|
||||
.addTranslator(MetadataType.BYTE, BlazeEntity::setBlazeFlags)
|
||||
.build();
|
||||
BREEZE = EntityDefinition.inherited(BreezeEntity::new, mobEntityBase)
|
||||
.type(EntityType.BREEZE)
|
||||
.height(1.77f).width(0.6f)
|
||||
.build();
|
||||
CREEPER = EntityDefinition.inherited(CreeperEntity::new, mobEntityBase)
|
||||
.type(EntityType.CREEPER)
|
||||
.height(1.7f).width(0.6f)
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -28,7 +28,7 @@ package org.geysermc.geyser.entity.type;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
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.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata;
|
||||
|
||||
@ -46,12 +46,7 @@ public class ArrowEntity extends AbstractArrowEntity {
|
||||
if (potionColor == -1) {
|
||||
dirtyMetadata.put(EntityDataTypes.CUSTOM_DISPLAY, (byte) 0);
|
||||
} else {
|
||||
TippedArrowPotion potion = TippedArrowPotion.getByJavaColor(potionColor);
|
||||
if (potion != null && potion.getJavaColor() != -1) {
|
||||
dirtyMetadata.put(EntityDataTypes.CUSTOM_DISPLAY, (byte) potion.getBedrockId());
|
||||
} else {
|
||||
dirtyMetadata.put(EntityDataTypes.CUSTOM_DISPLAY, (byte) 0);
|
||||
}
|
||||
dirtyMetadata.put(EntityDataTypes.CUSTOM_DISPLAY, Potion.toTippedArrowId(potionColor));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
|
||||
|
||||
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>
|
||||
@ -65,6 +65,8 @@ public class BoatEntity extends Entity implements Tickable {
|
||||
@Getter
|
||||
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
|
||||
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
|
||||
protected InteractiveTag testInteraction(Hand hand) {
|
||||
InteractiveTag tag = super.testInteraction(hand);
|
||||
if (tag != InteractiveTag.NONE) {
|
||||
return tag;
|
||||
}
|
||||
if (session.isSneaking()) {
|
||||
return InteractiveTag.NONE;
|
||||
} else if (passengers.size() < 2) {
|
||||
@ -160,6 +172,10 @@ public class BoatEntity extends Entity implements Tickable {
|
||||
|
||||
@Override
|
||||
public InteractionResult interact(Hand hand) {
|
||||
InteractionResult result = super.interact(hand);
|
||||
if (result != InteractionResult.PASS) {
|
||||
return result;
|
||||
}
|
||||
if (session.isSneaking()) {
|
||||
return InteractionResult.PASS;
|
||||
} 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) {
|
||||
AnimatePacket packet = new AnimatePacket();
|
||||
packet.setRuntimeEntityId(rower.getGeyserId());
|
||||
|
@ -40,6 +40,7 @@ import org.geysermc.geyser.api.entity.type.GeyserEntity;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.entity.GeyserDirtyMetadata;
|
||||
import org.geysermc.geyser.entity.properties.GeyserEntityPropertyManager;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.text.MessageTranslator;
|
||||
import org.geysermc.geyser.util.EntityUtils;
|
||||
@ -59,6 +60,9 @@ import java.util.*;
|
||||
@Getter
|
||||
@Setter
|
||||
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 int entityId;
|
||||
@ -134,7 +138,7 @@ public class Entity implements GeyserEntity {
|
||||
|
||||
this.valid = false;
|
||||
|
||||
this.propertyManager = new GeyserEntityPropertyManager(definition.registeredProperties());
|
||||
this.propertyManager = definition.registeredProperties() == null ? null : new GeyserEntityPropertyManager(definition.registeredProperties());
|
||||
|
||||
setPosition(position);
|
||||
setAirSupply(getMaxAir());
|
||||
@ -181,7 +185,7 @@ public class Entity implements GeyserEntity {
|
||||
|
||||
flagsDirty = false;
|
||||
|
||||
if (session.getGeyser().getConfig().isDebugMode()) {
|
||||
if (session.getGeyser().getConfig().isDebugMode() && PRINT_ENTITY_SPAWN_DEBUG) {
|
||||
EntityType type = definition.entityType();
|
||||
String name = type != null ? type.name() : getClass().getSimpleName();
|
||||
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;
|
||||
}
|
||||
|
||||
if (propertyManager.hasProperties()) {
|
||||
if (propertyManager != null && propertyManager.hasProperties()) {
|
||||
SetEntityDataPacket entityDataPacket = new SetEntityDataPacket();
|
||||
entityDataPacket.setRuntimeEntityId(geyserId);
|
||||
propertyManager.applyIntProperties(entityDataPacket.getProperties().getIntProperties());
|
||||
@ -554,6 +558,17 @@ public class Entity implements GeyserEntity {
|
||||
* Should usually mirror {@link #interact(Hand)} without any side effects.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
@ -562,6 +577,18 @@ public class Entity implements GeyserEntity {
|
||||
* to ensure packet parity as well as functionality parity (such as sound effect responses).
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -27,11 +27,18 @@ package org.geysermc.geyser.entity.type;
|
||||
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.entity.EntityDefinitions;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
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) {
|
||||
super(session, entityId, geyserId, null, EntityDefinitions.EXPERIENCE_ORB, position, Vector3f.ZERO, 0, 0, 0);
|
||||
|
||||
|
@ -33,6 +33,7 @@ import org.geysermc.erosion.util.BlockPositionIterator;
|
||||
import org.geysermc.geyser.entity.EntityDefinitions;
|
||||
import org.geysermc.geyser.entity.type.player.PlayerEntity;
|
||||
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.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.collision.BlockCollision;
|
||||
@ -162,7 +163,7 @@ public class FishingHookEntity extends ThrowableEntity {
|
||||
*/
|
||||
protected boolean isInAir() {
|
||||
int block = session.getGeyser().getWorldManager().getBlockAt(session, position.toInt());
|
||||
return block == BlockStateValues.JAVA_AIR_ID;
|
||||
return block == Block.JAVA_AIR_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -25,14 +25,16 @@
|
||||
|
||||
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.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
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.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;
|
||||
|
||||
@ -51,7 +53,8 @@ public class FurnaceMinecartEntity extends DefaultBlockMinecartEntity {
|
||||
|
||||
@Override
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -80,7 +80,10 @@ public class InteractionEntity extends Entity {
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -34,6 +34,7 @@ import org.cloudburstmc.protocol.bedrock.packet.AddItemEntityPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.EntityEventPacket;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
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.translator.item.ItemTranslator;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
@ -137,7 +138,7 @@ public class ItemEntity extends ThrowableEntity {
|
||||
protected float getDrag() {
|
||||
if (isOnGround()) {
|
||||
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 0.98f;
|
||||
|
44
core/src/main/java/org/geysermc/geyser/entity/type/Leashable.java
Normale Datei
44
core/src/main/java/org/geysermc/geyser/entity/type/Leashable.java
Normale Datei
@ -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;
|
||||
}
|
||||
}
|
@ -30,6 +30,8 @@ import org.cloudburstmc.protocol.bedrock.packet.AddPaintingPacket;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.level.PaintingType;
|
||||
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.object.Direction;
|
||||
|
||||
@ -49,8 +51,14 @@ public class PaintingEntity extends Entity {
|
||||
// Wait until we get the metadata needed
|
||||
}
|
||||
|
||||
public void setPaintingType(ObjectEntityMetadata<org.geysermc.mcprotocollib.protocol.data.game.entity.type.PaintingType> entityMetadata) {
|
||||
PaintingType type = PaintingType.getByPaintingType(entityMetadata.getValue());
|
||||
public void setPaintingType(ObjectEntityMetadata<Holder<PaintingVariant>> entityMetadata) {
|
||||
if (!entityMetadata.getValue().isId()) {
|
||||
return;
|
||||
}
|
||||
PaintingType type = session.getRegistryCache().paintings().byId(entityMetadata.getValue().id());
|
||||
if (type == null) {
|
||||
return;
|
||||
}
|
||||
AddPaintingPacket addPaintingPacket = new AddPaintingPacket();
|
||||
addPaintingPacket.setUniqueEntityId(geyserId);
|
||||
addPaintingPacket.setRuntimeEntityId(geyserId);
|
||||
@ -79,7 +87,7 @@ public class PaintingEntity extends Entity {
|
||||
private Vector3f fixOffset(PaintingType paintingName) {
|
||||
Vector3f position = super.position;
|
||||
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;
|
||||
|
||||
return switch (direction) {
|
||||
|
@ -28,7 +28,7 @@ package org.geysermc.geyser.entity.type;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
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 java.util.UUID;
|
||||
@ -41,7 +41,7 @@ public class SpawnerMinecartEntity extends DefaultBlockMinecartEntity {
|
||||
|
||||
@Override
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,17 @@ public class TNTEntity extends Entity implements Tickable {
|
||||
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) {
|
||||
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) {
|
||||
|
@ -38,7 +38,7 @@ public class AmbientEntity extends MobEntity {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canBeLeashed() {
|
||||
public boolean canBeLeashed() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ public class DolphinEntity extends WaterEntity {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canBeLeashed() {
|
||||
public boolean canBeLeashed() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -25,12 +25,12 @@
|
||||
|
||||
package org.geysermc.geyser.entity.type.living;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
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.type.Leashable;
|
||||
import org.geysermc.geyser.entity.type.LivingEntity;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
@ -43,11 +43,10 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
|
||||
|
||||
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.
|
||||
*/
|
||||
@Getter
|
||||
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) {
|
||||
@ -65,6 +64,7 @@ public class MobEntity extends LivingEntity {
|
||||
setFlag(EntityFlag.NO_AI, (xd & 0x01) == 0x01);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLeashHolderBedrockId(long bedrockId) {
|
||||
this.leashHolderBedrockId = bedrockId;
|
||||
dirtyMetadata.put(EntityDataTypes.LEASH_HOLDER, bedrockId);
|
||||
@ -79,10 +79,7 @@ public class MobEntity extends LivingEntity {
|
||||
return InteractiveTag.REMOVE_LEASH;
|
||||
} else {
|
||||
GeyserItemStack itemStack = session.getPlayerInventory().getItemInHand(hand);
|
||||
if (itemStack.asItem() == Items.LEAD && canBeLeashed()) {
|
||||
// We shall leash
|
||||
return InteractiveTag.LEASH;
|
||||
} else if (itemStack.asItem() == Items.NAME_TAG) {
|
||||
if (itemStack.asItem() == Items.NAME_TAG) {
|
||||
InteractionResult result = checkInteractWithNameTag(itemStack);
|
||||
if (result.consumesAction()) {
|
||||
return InteractiveTag.NAME;
|
||||
@ -99,9 +96,6 @@ public class MobEntity extends LivingEntity {
|
||||
if (!isAlive()) {
|
||||
// dead lol
|
||||
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 {
|
||||
GeyserItemStack itemInHand = session.getPlayerInventory().getItemInHand(hand);
|
||||
InteractionResult result = checkPriorityInteractions(itemInHand);
|
||||
@ -115,10 +109,7 @@ public class MobEntity extends LivingEntity {
|
||||
}
|
||||
|
||||
private InteractionResult checkPriorityInteractions(GeyserItemStack itemInHand) {
|
||||
if (itemInHand.asItem() == Items.LEAD && canBeLeashed()) {
|
||||
// We shall leash
|
||||
return InteractionResult.SUCCESS;
|
||||
} else if (itemInHand.asItem() == Items.NAME_TAG) {
|
||||
if (itemInHand.asItem() == Items.NAME_TAG) {
|
||||
InteractionResult result = checkInteractWithNameTag(itemInHand);
|
||||
if (result.consumesAction()) {
|
||||
return result;
|
||||
@ -143,12 +134,14 @@ public class MobEntity extends LivingEntity {
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
|
||||
protected boolean canBeLeashed() {
|
||||
@Override
|
||||
public boolean canBeLeashed() {
|
||||
return isNotLeashed() && !isEnemy();
|
||||
}
|
||||
|
||||
protected final boolean isNotLeashed() {
|
||||
return leashHolderBedrockId == -1L;
|
||||
@Override
|
||||
public long leashHolderBedrockId() {
|
||||
return leashHolderBedrockId;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -122,7 +122,7 @@ public class SquidEntity extends WaterEntity implements Tickable {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canBeLeashed() {
|
||||
public boolean canBeLeashed() {
|
||||
return isNotLeashed();
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,7 @@ public class WaterEntity extends CreatureEntity {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canBeLeashed() {
|
||||
public boolean canBeLeashed() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ public class AxolotlEntity extends AnimalEntity {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canBeLeashed() {
|
||||
public boolean canBeLeashed() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -63,7 +63,7 @@ public class HoglinEntity extends AnimalEntity {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canBeLeashed() {
|
||||
public boolean canBeLeashed() {
|
||||
return isNotLeashed();
|
||||
}
|
||||
|
||||
|
@ -123,7 +123,7 @@ public class PandaEntity extends AnimalEntity {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canBeLeashed() {
|
||||
public boolean canBeLeashed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -56,7 +56,7 @@ public class TurtleEntity extends AnimalEntity {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canBeLeashed() {
|
||||
public boolean canBeLeashed() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ public abstract class TameableEntity extends AnimalEntity {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canBeLeashed() {
|
||||
public boolean canBeLeashed() {
|
||||
return isNotLeashed();
|
||||
}
|
||||
}
|
||||
|
@ -33,36 +33,28 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
import org.geysermc.geyser.inventory.item.Enchantment;
|
||||
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.Item;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.tags.ItemTag;
|
||||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
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.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.Hand;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
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 boolean isCurseOfBinding = false;
|
||||
@ -112,12 +104,14 @@ public class WolfEntity extends TameableEntity {
|
||||
}
|
||||
|
||||
// 1.20.5+
|
||||
public void setWolfVariant(IntEntityMetadata entityMetadata) {
|
||||
WolfVariant wolfVariant = session.getRegistryCache().wolfVariants().byId(entityMetadata.getPrimitiveValue());
|
||||
public void setWolfVariant(ObjectEntityMetadata<Holder<WolfVariant>> entityMetadata) {
|
||||
entityMetadata.getValue().ifId(id -> {
|
||||
BuiltInWolfVariant wolfVariant = session.getRegistryCache().wolfVariants().byId(id);
|
||||
if (wolfVariant == null) {
|
||||
wolfVariant = WolfVariant.PALE;
|
||||
wolfVariant = BuiltInWolfVariant.PALE;
|
||||
}
|
||||
dirtyMetadata.put(EntityDataTypes.VARIANT, wolfVariant.ordinal());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -129,11 +123,11 @@ public class WolfEntity extends TameableEntity {
|
||||
@Override
|
||||
public void setChestplate(ItemStack 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
|
||||
protected boolean canBeLeashed() {
|
||||
public boolean canBeLeashed() {
|
||||
return !getFlag(EntityFlag.ANGRY) && super.canBeLeashed();
|
||||
}
|
||||
|
||||
@ -187,7 +181,7 @@ public class WolfEntity extends TameableEntity {
|
||||
}
|
||||
|
||||
// Ordered by bedrock id
|
||||
public enum WolfVariant {
|
||||
public enum BuiltInWolfVariant {
|
||||
PALE,
|
||||
ASHEN,
|
||||
BLACK,
|
||||
@ -198,16 +192,16 @@ public class WolfEntity extends TameableEntity {
|
||||
STRIPED,
|
||||
WOODS;
|
||||
|
||||
private static final WolfVariant[] VALUES = values();
|
||||
private static final BuiltInWolfVariant[] VALUES = values();
|
||||
|
||||
private final String javaIdentifier;
|
||||
|
||||
WolfVariant() {
|
||||
BuiltInWolfVariant() {
|
||||
this.javaIdentifier = "minecraft:" + this.name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
public static @Nullable WolfVariant getByJavaIdentifier(String javaIdentifier) {
|
||||
for (WolfVariant wolfVariant : VALUES) {
|
||||
public static @Nullable BuiltInWolfVariant getByJavaIdentifier(String javaIdentifier) {
|
||||
for (BuiltInWolfVariant wolfVariant : VALUES) {
|
||||
if (wolfVariant.javaIdentifier.equals(javaIdentifier)) {
|
||||
return wolfVariant;
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ public class AbstractMerchantEntity extends AgeableEntity {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canBeLeashed() {
|
||||
public boolean canBeLeashed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -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.packet.MoveEntityAbsolutePacket;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.registry.type.BlockMapping;
|
||||
import org.geysermc.geyser.level.block.property.Properties;
|
||||
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.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.VillagerData;
|
||||
@ -119,29 +120,32 @@ public class VillagerEntity extends AbstractMerchantEntity {
|
||||
}
|
||||
|
||||
// The bed block
|
||||
int blockId = session.getGeyser().getWorldManager().getBlockAt(session, bedPosition);
|
||||
String fullIdentifier = BlockRegistries.JAVA_BLOCKS.getOrDefault(blockId, BlockMapping.DEFAULT).getJavaIdentifier();
|
||||
BlockState state = session.getGeyser().getWorldManager().blockAt(session, bedPosition);
|
||||
|
||||
// Set the correct position offset and rotation when sleeping
|
||||
int bedRotation = 0;
|
||||
float xOffset = 0;
|
||||
float zOffset = 0;
|
||||
if (fullIdentifier.contains("facing=south")) {
|
||||
// bed is facing south
|
||||
if (state.block() instanceof BedBlock) {
|
||||
switch (state.getValue(Properties.HORIZONTAL_FACING)) {
|
||||
case SOUTH -> {
|
||||
bedRotation = 180;
|
||||
zOffset = -.5f;
|
||||
} else if (fullIdentifier.contains("facing=east")) {
|
||||
// bed is facing east
|
||||
}
|
||||
case EAST -> {
|
||||
bedRotation = 90;
|
||||
xOffset = -.5f;
|
||||
} else if (fullIdentifier.contains("facing=west")) {
|
||||
// bed is facing west
|
||||
}
|
||||
case WEST -> {
|
||||
bedRotation = 270;
|
||||
xOffset = .5f;
|
||||
} else if (fullIdentifier.contains("facing=north")) {
|
||||
}
|
||||
case NORTH -> {
|
||||
// rotation does not change because north is 0
|
||||
zOffset = .5f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setYaw(yaw);
|
||||
setPitch(pitch);
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -58,7 +58,7 @@ public class ZoglinEntity extends MonsterEntity {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canBeLeashed() {
|
||||
public boolean canBeLeashed() {
|
||||
return isNotLeashed();
|
||||
}
|
||||
|
||||
|
@ -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.EntityFlag;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityLinkData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.*;
|
||||
import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity;
|
||||
import org.geysermc.geyser.entity.EntityDefinitions;
|
||||
@ -166,6 +167,31 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity {
|
||||
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() {
|
||||
if (session.getEntityCache().getPlayerEntity(uuid) == null)
|
||||
return;
|
||||
|
@ -35,6 +35,7 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
|
||||
import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.network.GameProtocol;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.util.AttributeUtils;
|
||||
import org.geysermc.geyser.util.DimensionUtils;
|
||||
@ -60,16 +61,14 @@ public class SessionPlayerEntity extends PlayerEntity {
|
||||
*/
|
||||
@Getter
|
||||
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.
|
||||
*/
|
||||
@Getter
|
||||
private boolean isRidingInFront;
|
||||
|
||||
private int lastAirSupply = getMaxAir();
|
||||
|
||||
public SessionPlayerEntity(GeyserSession session) {
|
||||
super(session, -1, 1, null, Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0, null, null);
|
||||
|
||||
@ -120,9 +119,7 @@ public class SessionPlayerEntity extends PlayerEntity {
|
||||
// TODO: proper fix, BDS somehow does it? https://paste.gg/p/anonymous/3adfb7612f1540be80fa03a2281f93dc (BDS 1.20.13)
|
||||
if (!this.session.getGameMode().equals(GameMode.SPECTATOR)) {
|
||||
super.setFlags(entityMetadata);
|
||||
session.setSwimmingInWater((entityMetadata.getPrimitiveValue() & 0x10) == 0x10 && getFlag(EntityFlag.SPRINTING));
|
||||
}
|
||||
refreshSpeed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -150,7 +147,6 @@ public class SessionPlayerEntity extends PlayerEntity {
|
||||
public void setPose(Pose pose) {
|
||||
super.setPose(pose);
|
||||
session.setPose(pose);
|
||||
refreshSpeed = true;
|
||||
}
|
||||
|
||||
public float getMaxHealth() {
|
||||
@ -167,7 +163,13 @@ public class SessionPlayerEntity extends PlayerEntity {
|
||||
|
||||
@Override
|
||||
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
|
||||
} else {
|
||||
super.setAirSupply(amount);
|
||||
@ -199,21 +201,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
|
||||
protected void updateAttribute(Attribute javaAttribute, List<AttributeData> newAttributes) {
|
||||
if (javaAttribute.getType() == AttributeType.Builtin.GENERIC_ATTACK_SPEED) {
|
||||
@ -226,17 +213,6 @@ public class SessionPlayerEntity extends PlayerEntity {
|
||||
@Override
|
||||
protected AttributeData calculateAttribute(Attribute javaAttribute, GeyserAttributeType 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);
|
||||
return attributeData;
|
||||
}
|
||||
@ -244,7 +220,7 @@ public class SessionPlayerEntity extends PlayerEntity {
|
||||
public void setLastDeathPosition(@Nullable GlobalPos pos) {
|
||||
if (pos != null) {
|
||||
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);
|
||||
} else {
|
||||
dirtyMetadata.put(EntityDataTypes.PLAYER_HAS_DIED, false);
|
||||
@ -264,19 +240,13 @@ public class SessionPlayerEntity extends PlayerEntity {
|
||||
super.setAbsorptionHearts(entityMetadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetMetadata() {
|
||||
// Reset all metadata to their default values
|
||||
// This is used when a player respawns
|
||||
this.flags.clear();
|
||||
this.initializeMetadata();
|
||||
super.resetMetadata();
|
||||
|
||||
// Reset air
|
||||
this.resetAir();
|
||||
|
||||
// Explicitly reset all metadata not handled by initializeMetadata
|
||||
setParrot(null, true);
|
||||
setParrot(null, false);
|
||||
|
||||
// Absorption is metadata in java edition
|
||||
attributes.remove(GeyserAttributeType.ABSORPTION);
|
||||
UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket();
|
||||
|
@ -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.EntityFlag;
|
||||
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.cache.SkullCache;
|
||||
import org.geysermc.geyser.skin.SkullSkinManager;
|
||||
@ -137,20 +140,19 @@ public class SkullPlayerEntity extends PlayerEntity {
|
||||
float z = skull.getPosition().getZ() + .5f;
|
||||
float rotation;
|
||||
|
||||
int blockState = skull.getBlockState();
|
||||
byte floorRotation = BlockStateValues.getSkullRotation(blockState);
|
||||
if (floorRotation == -1) {
|
||||
// Wall skull
|
||||
BlockState blockState = skull.getBlockState();
|
||||
if (blockState.block() instanceof WallSkullBlock) {
|
||||
y += 0.25f;
|
||||
rotation = BlockStateValues.getSkullWallDirections().get(blockState);
|
||||
switch ((int) rotation) {
|
||||
case 180 -> z += 0.24f; // North
|
||||
case 0 -> z -= 0.24f; // South
|
||||
case 90 -> x += 0.24f; // West
|
||||
case 270 -> x -= 0.24f; // East
|
||||
Direction direction = blockState.getValue(Properties.HORIZONTAL_FACING);
|
||||
rotation = WallSkullBlock.getDegrees(direction);
|
||||
switch (direction) {
|
||||
case NORTH -> z += 0.24f;
|
||||
case SOUTH -> z -= 0.24f;
|
||||
case WEST -> x += 0.24f;
|
||||
case EAST -> x -= 0.24f;
|
||||
}
|
||||
} else {
|
||||
rotation = (180f + (floorRotation * 22.5f)) % 360;
|
||||
rotation = (180f + (blockState.getValue(Properties.ROTATION_16) * 22.5f)) % 360;
|
||||
}
|
||||
|
||||
moveAbsolute(Vector3f.from(x, y, z), rotation, 0, rotation, true, true);
|
||||
|
@ -25,14 +25,13 @@
|
||||
|
||||
package org.geysermc.geyser.erosion;
|
||||
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.level.block.value.PistonValueType;
|
||||
import io.netty.channel.Channel;
|
||||
import it.unimi.dsi.fastutil.Pair;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntArrayMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
@ -43,21 +42,18 @@ import org.geysermc.erosion.packet.ErosionPacketHandler;
|
||||
import org.geysermc.erosion.packet.ErosionPacketSender;
|
||||
import org.geysermc.erosion.packet.backendbound.BackendboundInitializePacket;
|
||||
import org.geysermc.erosion.packet.backendbound.BackendboundPacket;
|
||||
import org.geysermc.erosion.packet.geyserbound.GeyserboundBatchBlockIdPacket;
|
||||
import org.geysermc.erosion.packet.geyserbound.GeyserboundBlockEntityPacket;
|
||||
import org.geysermc.erosion.packet.geyserbound.GeyserboundBlockIdPacket;
|
||||
import org.geysermc.erosion.packet.geyserbound.GeyserboundBlockLookupFailPacket;
|
||||
import org.geysermc.erosion.packet.geyserbound.GeyserboundBlockPlacePacket;
|
||||
import org.geysermc.erosion.packet.geyserbound.GeyserboundHandshakePacket;
|
||||
import org.geysermc.erosion.packet.geyserbound.GeyserboundPickBlockPacket;
|
||||
import org.geysermc.erosion.packet.geyserbound.GeyserboundPistonEventPacket;
|
||||
import org.geysermc.erosion.packet.geyserbound.*;
|
||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||
import org.geysermc.geyser.level.block.property.Properties;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.level.physics.Direction;
|
||||
import org.geysermc.geyser.network.GameProtocol;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.PistonCache;
|
||||
import org.geysermc.geyser.translator.level.block.entity.PistonBlockEntity;
|
||||
import org.geysermc.geyser.util.BlockEntityUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.level.block.value.PistonValueType;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@ -71,7 +67,7 @@ public final class GeyserboundPacketHandlerImpl extends AbstractGeyserboundPacke
|
||||
@Setter
|
||||
private CompletableFuture<int[]> pendingBatchLookup = null;
|
||||
@Setter
|
||||
private CompletableFuture<DataComponents> pickBlockLookup = null;
|
||||
private CompletableFuture<Int2ObjectMap<byte[]>> pickBlockLookup = null;
|
||||
|
||||
private final AtomicInteger nextTransactionId = new AtomicInteger(1);
|
||||
|
||||
@ -127,7 +123,7 @@ public final class GeyserboundPacketHandlerImpl extends AbstractGeyserboundPacke
|
||||
}
|
||||
CompletableFuture<Integer> future = this.asyncPendingLookups.remove(transactionId);
|
||||
if (future != null) {
|
||||
future.complete(BlockStateValues.JAVA_AIR_ID);
|
||||
future.complete(Block.JAVA_AIR_ID);
|
||||
}
|
||||
}
|
||||
|
||||
@ -141,28 +137,29 @@ public final class GeyserboundPacketHandlerImpl extends AbstractGeyserboundPacke
|
||||
placeBlockSoundPacket.setIdentifier(":");
|
||||
session.sendUpstreamPacket(placeBlockSoundPacket);
|
||||
session.setLastBlockPlacePosition(null);
|
||||
session.setLastBlockPlacedId(null);
|
||||
session.setLastBlockPlaced(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlePickBlock(GeyserboundPickBlockPacket packet) {
|
||||
if (this.pickBlockLookup != null) {
|
||||
//this.pickBlockLookup.complete(packet.getTag()); // TODO 1.20.5
|
||||
this.pickBlockLookup.complete(packet.getComponents());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlePistonEvent(GeyserboundPistonEventPacket packet) {
|
||||
Direction orientation = BlockStateValues.getPistonOrientation(packet.getBlockId());
|
||||
Direction orientation = BlockState.of(packet.getBlockId()).getValue(Properties.FACING);
|
||||
Vector3i position = packet.getPos();
|
||||
boolean isExtend = packet.isExtend();
|
||||
|
||||
var stream = packet.getAttachedBlocks()
|
||||
.object2IntEntrySet()
|
||||
.stream()
|
||||
.filter(entry -> BlockStateValues.canPistonMoveBlock(entry.getIntValue(), isExtend));
|
||||
Object2IntMap<Vector3i> attachedBlocks = new Object2IntArrayMap<>();
|
||||
stream.forEach(entry -> attachedBlocks.put(entry.getKey(), entry.getIntValue()));
|
||||
.map(entry -> Pair.of(entry.getKey(), BlockState.of(entry.getIntValue())))
|
||||
.filter(pair -> BlockStateValues.canPistonMoveBlock(pair.value(), isExtend));
|
||||
Object2ObjectMap<Vector3i, BlockState> attachedBlocks = new Object2ObjectArrayMap<>();
|
||||
stream.forEach(pair -> attachedBlocks.put(pair.key(), pair.value()));
|
||||
|
||||
session.executeInEventLoop(() -> {
|
||||
PistonCache pistonCache = session.getPistonCache();
|
||||
|
@ -32,24 +32,50 @@ import org.cloudburstmc.math.vector.Vector2f;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.CameraShakeAction;
|
||||
import org.cloudburstmc.protocol.bedrock.data.CameraShakeType;
|
||||
import org.cloudburstmc.protocol.bedrock.data.HudElement;
|
||||
import org.cloudburstmc.protocol.bedrock.data.HudVisibility;
|
||||
import org.cloudburstmc.protocol.bedrock.data.camera.CameraEase;
|
||||
import org.cloudburstmc.protocol.bedrock.data.camera.CameraFadeInstruction;
|
||||
import org.cloudburstmc.protocol.bedrock.data.camera.CameraSetInstruction;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.CameraInstructionPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.CameraShakePacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.PlayerFogPacket;
|
||||
import org.geysermc.geyser.api.bedrock.camera.*;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.SetHudPacket;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraData;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraEaseType;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraFade;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraPerspective;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraPosition;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraShake;
|
||||
import org.geysermc.geyser.api.bedrock.camera.GuiElement;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public class GeyserCameraData implements CameraData {
|
||||
private static final HudElement[] HUD_ELEMENT_VALUES = HudElement.values();
|
||||
private static final Set<HudElement> ALL_HUD_ELEMENTS = Set.of(HUD_ELEMENT_VALUES);
|
||||
|
||||
/**
|
||||
* An array of elements to hide when the player is in spectator mode.
|
||||
* Helps with tidying up the GUI; Java-style.
|
||||
*/
|
||||
private static final GuiElement[] SPECTATOR_HIDDEN_ELEMENTS = {
|
||||
GuiElement.AIR_BUBBLES_BAR,
|
||||
GuiElement.ARMOR,
|
||||
GuiElement.HEALTH,
|
||||
GuiElement.FOOD_BAR,
|
||||
GuiElement.PROGRESS_BAR,
|
||||
GuiElement.TOOL_TIPS
|
||||
};
|
||||
|
||||
private final GeyserSession session;
|
||||
|
||||
@Getter
|
||||
private CameraPerspective cameraPerspective;
|
||||
|
||||
/**
|
||||
* All fog effects that are currently applied to the client.
|
||||
*/
|
||||
@ -57,6 +83,14 @@ public class GeyserCameraData implements CameraData {
|
||||
|
||||
private final Set<UUID> cameraLockOwners = new HashSet<>();
|
||||
|
||||
/**
|
||||
* All currently hidden HUD elements
|
||||
*/
|
||||
private final Set<GuiElement> hiddenHudElements = new HashSet<>();
|
||||
|
||||
@Getter
|
||||
private CameraPerspective cameraPerspective;
|
||||
|
||||
public GeyserCameraData(GeyserSession session) {
|
||||
this.session = session;
|
||||
}
|
||||
@ -223,4 +257,67 @@ public class GeyserCameraData implements CameraData {
|
||||
public boolean isCameraLocked() {
|
||||
return !this.cameraLockOwners.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideElement(GuiElement... elements) {
|
||||
Objects.requireNonNull(elements);
|
||||
SetHudPacket packet = new SetHudPacket();
|
||||
packet.setVisibility(HudVisibility.HIDE);
|
||||
Set<HudElement> elementSet = packet.getElements();
|
||||
|
||||
for (GuiElement element : elements) {
|
||||
this.hiddenHudElements.add(element);
|
||||
elementSet.add(HUD_ELEMENT_VALUES[element.id()]);
|
||||
}
|
||||
|
||||
session.sendUpstreamPacket(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetElement(GuiElement... elements) {
|
||||
SetHudPacket packet = new SetHudPacket();
|
||||
packet.setVisibility(HudVisibility.RESET);
|
||||
Set<HudElement> elementSet = packet.getElements();
|
||||
|
||||
if (elements != null && elements.length != 0) {
|
||||
for (GuiElement element : elements) {
|
||||
this.hiddenHudElements.remove(element);
|
||||
elementSet.add(HUD_ELEMENT_VALUES[element.id()]);
|
||||
}
|
||||
} else {
|
||||
this.hiddenHudElements.clear();
|
||||
elementSet.addAll(ALL_HUD_ELEMENTS);
|
||||
}
|
||||
|
||||
session.sendUpstreamPacket(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHudElementHidden(@NonNull GuiElement element) {
|
||||
Objects.requireNonNull(element);
|
||||
return this.hiddenHudElements.contains(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Set<GuiElement> hiddenElements() {
|
||||
return Collections.unmodifiableSet(hiddenHudElements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deals with hiding hud elements while in spectator.
|
||||
*
|
||||
* @param currentlySpectator whether the player is currently in spectator mode
|
||||
* @param newGameMode the new GameMode to switch to
|
||||
*/
|
||||
public void handleGameModeChange(boolean currentlySpectator, GameMode newGameMode) {
|
||||
if (newGameMode == GameMode.SPECTATOR) {
|
||||
if (!currentlySpectator) {
|
||||
hideElement(SPECTATOR_HIDDEN_ELEMENTS);
|
||||
}
|
||||
} else {
|
||||
if (currentlySpectator) {
|
||||
resetElement(SPECTATOR_HIDDEN_ELEMENTS);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -25,6 +25,7 @@
|
||||
|
||||
package org.geysermc.geyser.inventory;
|
||||
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
|
||||
import lombok.Getter;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
@ -37,7 +38,7 @@ import org.jetbrains.annotations.Range;
|
||||
*/
|
||||
@Getter
|
||||
public class Container extends Inventory {
|
||||
private final PlayerInventory playerInventory;
|
||||
protected final PlayerInventory playerInventory;
|
||||
private final int containerSize;
|
||||
|
||||
/**
|
||||
@ -83,9 +84,9 @@ public class Container extends Inventory {
|
||||
* Will be overwritten for droppers.
|
||||
*
|
||||
* @param usingRealBlock whether this container is using a real container or not
|
||||
* @param javaBlockId the Java block string of the block, if real
|
||||
* @param block the Java block, if real
|
||||
*/
|
||||
public void setUsingRealBlock(boolean usingRealBlock, String javaBlockId) {
|
||||
public void setUsingRealBlock(boolean usingRealBlock, Block block) {
|
||||
isUsingRealBlock = usingRealBlock;
|
||||
}
|
||||
}
|
||||
|
@ -25,13 +25,19 @@
|
||||
|
||||
package org.geysermc.geyser.inventory;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.inventory.CrafterInventoryTranslator;
|
||||
import org.geysermc.geyser.translator.inventory.InventoryTranslator;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.jetbrains.annotations.Range;
|
||||
|
||||
@Getter
|
||||
public class CrafterContainer extends Container {
|
||||
private GeyserItemStack resultItem = GeyserItemStack.EMPTY;
|
||||
|
||||
@Setter
|
||||
private boolean triggered = false;
|
||||
@ -46,8 +52,36 @@ public class CrafterContainer extends Container {
|
||||
super(title, id, size, containerType, playerInventory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeyserItemStack getItem(int slot) {
|
||||
if (slot == CrafterInventoryTranslator.JAVA_RESULT_SLOT) {
|
||||
return this.resultItem;
|
||||
} else if (isCraftingGrid(slot)) {
|
||||
return super.getItem(slot);
|
||||
} else {
|
||||
return playerInventory.getItem(slot - CrafterInventoryTranslator.GRID_SIZE + InventoryTranslator.PLAYER_INVENTORY_OFFSET);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOffsetForHotbar(@Range(from = 0, to = 8) int slot) {
|
||||
return playerInventory.getOffsetForHotbar(slot) - InventoryTranslator.PLAYER_INVENTORY_OFFSET + CrafterInventoryTranslator.GRID_SIZE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(int slot, @NonNull GeyserItemStack newItem, GeyserSession session) {
|
||||
if (slot == CrafterInventoryTranslator.JAVA_RESULT_SLOT) {
|
||||
// Result item probably won't be an item that needs to worry about net ID or lodestone compasses
|
||||
this.resultItem = newItem;
|
||||
} else if (isCraftingGrid(slot)) {
|
||||
super.setItem(slot, newItem, session);
|
||||
} else {
|
||||
playerInventory.setItem(slot - CrafterInventoryTranslator.GRID_SIZE + InventoryTranslator.PLAYER_INVENTORY_OFFSET, newItem, session);
|
||||
}
|
||||
}
|
||||
|
||||
public void setSlot(int slot, boolean enabled) {
|
||||
if (slot < 0 || slot > 8) {
|
||||
if (!isCraftingGrid(slot)) {
|
||||
GeyserImpl.getInstance().getLogger().warning("Crafter slot out of bounds: " + slot);
|
||||
return;
|
||||
}
|
||||
@ -58,4 +92,8 @@ public class CrafterContainer extends Container {
|
||||
disabledSlotsMask = (short) (disabledSlotsMask | (1 << slot));
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isCraftingGrid(int slot) {
|
||||
return slot >= 0 && slot <= 8;
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,8 @@
|
||||
package org.geysermc.geyser.inventory;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.geysermc.geyser.level.block.Blocks;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.inventory.Generic3X3InventoryTranslator;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
|
||||
@ -44,10 +46,10 @@ public class Generic3X3Container extends Container {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUsingRealBlock(boolean usingRealBlock, String javaBlockId) {
|
||||
super.setUsingRealBlock(usingRealBlock, javaBlockId);
|
||||
public void setUsingRealBlock(boolean usingRealBlock, Block block) {
|
||||
super.setUsingRealBlock(usingRealBlock, block);
|
||||
if (usingRealBlock) {
|
||||
isDropper = javaBlockId.startsWith("minecraft:dropper");
|
||||
isDropper = block == Blocks.DROPPER;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,12 +25,11 @@
|
||||
|
||||
package org.geysermc.geyser.inventory;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.EnchantData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.EnchantOptionData;
|
||||
import lombok.Getter;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@ -44,13 +43,13 @@ public class GeyserEnchantOption {
|
||||
* is controlled by the server.
|
||||
* So, of course, we have to throw in some easter eggs. ;)
|
||||
*/
|
||||
private static final List<String> ENCHANT_NAMES = Arrays.asList("tougher armor", "lukeeey", "fall better",
|
||||
"explode less", "camo toy", "breathe better", "rtm five one six", "armor stab", "water walk", "you are elsa",
|
||||
"tim two zero three", "fast walk nether", "davchoo", "oof ouch owie", "enemy on fire", "spider sad", "aj ferguson", "redned",
|
||||
"more items thx", "long sword reach", "fast tool", "give me block", "less breaky break", "cube craft",
|
||||
"strong arrow", "fist arrow", "spicy arrow", "many many arrows", "geyser", "come here fish", "i like this",
|
||||
"stabby stab", "supreme mortal", "avatar i guess", "more arrows", "fly finder seventeen", "in and out",
|
||||
"xp heals tools", "dragon proxy waz here");
|
||||
private static final List<String> ENCHANT_NAMES = List.of("tougher armor", "lukeeey", "fall better",
|
||||
"explode less", "camo toy", "armor stab", "breathe better", "water walk", "rtm five one six", "oof ouch owie",
|
||||
"enemy on fire", "spider sad", "aj ferguson", "redned", "more items thx", "fast tool", "give me block",
|
||||
"less breaky break", "cube craft", "strong arrow", "fist arrow", "spicy arrow", "many many arrows", "geyser",
|
||||
"come here fish", "you are elsa", "xp heals tools", "tim two zero three", "dragon proxy waz here",
|
||||
"stabby stab", "supreme mortal", "i like this", "avatar i guess", "more arrows", "in and out",
|
||||
"fly finder seventeen", "fast walk nether", "davchoo", "onechris", "death bringer thirteen", "kastle");
|
||||
|
||||
@Getter
|
||||
private final int javaIndex;
|
||||
@ -62,7 +61,6 @@ public class GeyserEnchantOption {
|
||||
private boolean hasChanged;
|
||||
|
||||
private int xpCost = 0;
|
||||
private int javaEnchantIndex = -1;
|
||||
private int bedrockEnchantIndex = -1;
|
||||
private int enchantLevel = -1;
|
||||
|
||||
@ -74,7 +72,7 @@ public class GeyserEnchantOption {
|
||||
this.hasChanged = false;
|
||||
return new EnchantOptionData(xpCost, javaIndex + 16, EMPTY,
|
||||
enchantLevel == -1 ? EMPTY : Collections.singletonList(new EnchantData(bedrockEnchantIndex, enchantLevel)), EMPTY,
|
||||
javaEnchantIndex == -1 ? "unknown" : ENCHANT_NAMES.get(javaEnchantIndex), enchantLevel == -1 ? 0 : session.getNextItemNetId());
|
||||
bedrockEnchantIndex == -1 ? "unknown" : ENCHANT_NAMES.get(bedrockEnchantIndex), enchantLevel == -1 ? 0 : session.getNextItemNetId());
|
||||
}
|
||||
|
||||
public boolean hasChanged() {
|
||||
@ -88,10 +86,9 @@ public class GeyserEnchantOption {
|
||||
}
|
||||
}
|
||||
|
||||
public void setEnchantIndex(int javaEnchantIndex, int bedrockEnchantIndex) {
|
||||
if (this.javaEnchantIndex != javaEnchantIndex) {
|
||||
public void setEnchantIndex(int bedrockEnchantIndex) {
|
||||
if (this.bedrockEnchantIndex != bedrockEnchantIndex) {
|
||||
hasChanged = true;
|
||||
this.javaEnchantIndex = javaEnchantIndex;
|
||||
this.bedrockEnchantIndex = bedrockEnchantIndex;
|
||||
}
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ public class GeyserItemStack {
|
||||
return of(javaId, amount, null);
|
||||
}
|
||||
|
||||
public static @NonNull GeyserItemStack of(int javaId, int amount, DataComponents components) {
|
||||
public static @NonNull GeyserItemStack of(int javaId, int amount, @Nullable DataComponents components) {
|
||||
return new GeyserItemStack(javaId, amount, components);
|
||||
}
|
||||
|
||||
|
@ -36,11 +36,11 @@ import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.inventory.Container;
|
||||
import org.geysermc.geyser.inventory.Inventory;
|
||||
import org.geysermc.geyser.inventory.LecternContainer;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.registry.type.BlockMapping;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.inventory.InventoryTranslator;
|
||||
import org.geysermc.geyser.util.BlockUtils;
|
||||
import org.geysermc.geyser.util.InventoryUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
@ -55,20 +55,24 @@ public class BlockInventoryHolder extends InventoryHolder {
|
||||
/**
|
||||
* The default Java block ID to translate as a fake block
|
||||
*/
|
||||
private final int defaultJavaBlockState;
|
||||
private final BlockState defaultJavaBlockState;
|
||||
private final ContainerType containerType;
|
||||
private final Set<String> validBlocks;
|
||||
private final Set<Block> validBlocks;
|
||||
|
||||
public BlockInventoryHolder(String javaBlockIdentifier, ContainerType containerType, String... validBlocks) {
|
||||
this.defaultJavaBlockState = BlockRegistries.JAVA_IDENTIFIER_TO_ID.get().getInt(javaBlockIdentifier);
|
||||
public BlockInventoryHolder(Block defaultJavaBlock, ContainerType containerType, Block... validBlocks) {
|
||||
this(defaultJavaBlock.defaultBlockState(), containerType, validBlocks);
|
||||
}
|
||||
|
||||
public BlockInventoryHolder(BlockState defaultJavaBlockState, ContainerType containerType, Block... validBlocks) {
|
||||
this.defaultJavaBlockState = defaultJavaBlockState;
|
||||
this.containerType = containerType;
|
||||
if (validBlocks != null) {
|
||||
Set<String> validBlocksTemp = new HashSet<>(validBlocks.length + 1);
|
||||
Set<Block> validBlocksTemp = new HashSet<>(validBlocks.length + 1);
|
||||
Collections.addAll(validBlocksTemp, validBlocks);
|
||||
validBlocksTemp.add(BlockUtils.getCleanIdentifier(javaBlockIdentifier));
|
||||
validBlocksTemp.add(defaultJavaBlockState.block());
|
||||
this.validBlocks = Set.copyOf(validBlocksTemp);
|
||||
} else {
|
||||
this.validBlocks = Collections.singleton(BlockUtils.getCleanIdentifier(javaBlockIdentifier));
|
||||
this.validBlocks = Collections.singleton(defaultJavaBlockState.block());
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,14 +84,13 @@ public class BlockInventoryHolder extends InventoryHolder {
|
||||
if (checkInteractionPosition(session)) {
|
||||
// Then, check to see if the interacted block is valid for this inventory by ensuring the block state identifier is valid
|
||||
// and the bedrock block is vanilla
|
||||
int javaBlockId = session.getGeyser().getWorldManager().getBlockAt(session, session.getLastInteractionBlockPosition());
|
||||
if (!BlockRegistries.CUSTOM_BLOCK_STATE_OVERRIDES.get().containsKey(javaBlockId)) {
|
||||
String[] javaBlockString = BlockRegistries.JAVA_BLOCKS.getOrDefault(javaBlockId, BlockMapping.DEFAULT).getJavaIdentifier().split("\\[");
|
||||
if (isValidBlock(javaBlockString)) {
|
||||
BlockState state = session.getGeyser().getWorldManager().blockAt(session, session.getLastInteractionBlockPosition());
|
||||
if (!BlockRegistries.CUSTOM_BLOCK_STATE_OVERRIDES.get().containsKey(state.javaId())) {
|
||||
if (isValidBlock(state)) {
|
||||
// We can safely use this block
|
||||
inventory.setHolderPosition(session.getLastInteractionBlockPosition());
|
||||
((Container) inventory).setUsingRealBlock(true, javaBlockString[0]);
|
||||
setCustomName(session, session.getLastInteractionBlockPosition(), inventory, javaBlockId);
|
||||
((Container) inventory).setUsingRealBlock(true, state.block());
|
||||
setCustomName(session, session.getLastInteractionBlockPosition(), inventory, state);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -125,11 +128,11 @@ public class BlockInventoryHolder extends InventoryHolder {
|
||||
/**
|
||||
* @return true if this Java block ID can be used for player inventory.
|
||||
*/
|
||||
protected boolean isValidBlock(String[] javaBlockString) {
|
||||
return this.validBlocks.contains(javaBlockString[0]);
|
||||
protected boolean isValidBlock(BlockState blockState) {
|
||||
return this.validBlocks.contains(blockState.block());
|
||||
}
|
||||
|
||||
protected void setCustomName(GeyserSession session, Vector3i position, Inventory inventory, int javaBlockState) {
|
||||
protected void setCustomName(GeyserSession session, Vector3i position, Inventory inventory, BlockState javaBlockState) {
|
||||
NbtMap tag = NbtMap.builder()
|
||||
.putInt("x", position.getX())
|
||||
.putInt("y", position.getY())
|
||||
@ -160,6 +163,7 @@ public class BlockInventoryHolder extends InventoryHolder {
|
||||
ContainerClosePacket packet = new ContainerClosePacket();
|
||||
packet.setId((byte) inventory.getBedrockId());
|
||||
packet.setServerInitiated(true);
|
||||
packet.setType(ContainerType.CONTAINER);
|
||||
session.sendUpstreamPacket(packet);
|
||||
return;
|
||||
}
|
||||
|
@ -26,7 +26,9 @@
|
||||
package org.geysermc.geyser.inventory.item;
|
||||
|
||||
import lombok.Getter;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.util.MinecraftKey;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
@ -72,21 +74,23 @@ public enum BannerPattern {
|
||||
SKULL("sku"),
|
||||
FLOWER("flo"),
|
||||
MOJANG("moj"),
|
||||
PIGLIN("pig");
|
||||
PIGLIN("pig"),
|
||||
FLOW("flw"),
|
||||
GUSTER("gus");
|
||||
|
||||
private static final BannerPattern[] VALUES = values();
|
||||
|
||||
private final String javaIdentifier;
|
||||
private final Key javaIdentifier;
|
||||
private final String bedrockIdentifier;
|
||||
|
||||
BannerPattern(String bedrockIdentifier) {
|
||||
this.javaIdentifier = "minecraft:" + this.name().toLowerCase(Locale.ROOT);
|
||||
this.javaIdentifier = MinecraftKey.key(this.name().toLowerCase(Locale.ROOT));
|
||||
this.bedrockIdentifier = bedrockIdentifier;
|
||||
}
|
||||
|
||||
public static @Nullable BannerPattern getByJavaIdentifier(String javaIdentifier) {
|
||||
public static @Nullable BannerPattern getByJavaIdentifier(Key key) {
|
||||
for (BannerPattern bannerPattern : VALUES) {
|
||||
if (bannerPattern.javaIdentifier.equals(javaIdentifier)) {
|
||||
if (bannerPattern.javaIdentifier.equals(key)) {
|
||||
return bannerPattern;
|
||||
}
|
||||
}
|
||||
|
@ -25,13 +25,11 @@
|
||||
|
||||
package org.geysermc.geyser.inventory.item;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
@Getter
|
||||
public enum Enchantment {
|
||||
public enum BedrockEnchantment {
|
||||
PROTECTION,
|
||||
FIRE_PROTECTION,
|
||||
FEATHER_FALLING,
|
||||
@ -69,18 +67,21 @@ public enum Enchantment {
|
||||
PIERCING,
|
||||
QUICK_CHARGE,
|
||||
SOUL_SPEED,
|
||||
SWIFT_SNEAK;
|
||||
SWIFT_SNEAK,
|
||||
WIND_BURST,
|
||||
DENSITY,
|
||||
BREACH;
|
||||
|
||||
private static final Enchantment[] VALUES = values();
|
||||
private static final BedrockEnchantment[] VALUES = values();
|
||||
|
||||
private final String javaIdentifier;
|
||||
|
||||
Enchantment() {
|
||||
BedrockEnchantment() {
|
||||
this.javaIdentifier = "minecraft:" + this.name().toLowerCase(Locale.ENGLISH);
|
||||
}
|
||||
|
||||
public static @Nullable Enchantment getByJavaIdentifier(String javaIdentifier) {
|
||||
for (Enchantment enchantment : VALUES) {
|
||||
public static @Nullable BedrockEnchantment getByJavaIdentifier(String javaIdentifier) {
|
||||
for (BedrockEnchantment enchantment : VALUES) {
|
||||
if (enchantment.javaIdentifier.equals(javaIdentifier) || enchantment.name().toLowerCase(Locale.ENGLISH).equalsIgnoreCase(javaIdentifier)) {
|
||||
return enchantment;
|
||||
}
|
||||
@ -88,88 +89,10 @@ public enum Enchantment {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static @Nullable Enchantment getByBedrockId(int bedrockId) {
|
||||
public static @Nullable BedrockEnchantment getByBedrockId(int bedrockId) {
|
||||
if (bedrockId >= 0 && bedrockId < VALUES.length) {
|
||||
return VALUES[bedrockId];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enchantments classified by their Java index
|
||||
*/
|
||||
public enum JavaEnchantment {
|
||||
PROTECTION,
|
||||
FIRE_PROTECTION,
|
||||
FEATHER_FALLING,
|
||||
BLAST_PROTECTION,
|
||||
PROJECTILE_PROTECTION,
|
||||
RESPIRATION,
|
||||
AQUA_AFFINITY,
|
||||
THORNS,
|
||||
DEPTH_STRIDER,
|
||||
FROST_WALKER,
|
||||
BINDING_CURSE,
|
||||
SOUL_SPEED,
|
||||
SWIFT_SNEAK,
|
||||
SHARPNESS,
|
||||
SMITE,
|
||||
BANE_OF_ARTHROPODS,
|
||||
KNOCKBACK,
|
||||
FIRE_ASPECT,
|
||||
LOOTING,
|
||||
SWEEPING_EDGE,
|
||||
EFFICIENCY,
|
||||
SILK_TOUCH,
|
||||
UNBREAKING,
|
||||
FORTUNE,
|
||||
POWER,
|
||||
PUNCH,
|
||||
FLAME,
|
||||
INFINITY,
|
||||
LUCK_OF_THE_SEA,
|
||||
LURE,
|
||||
LOYALTY,
|
||||
IMPALING,
|
||||
RIPTIDE,
|
||||
CHANNELING,
|
||||
MULTISHOT,
|
||||
QUICK_CHARGE,
|
||||
PIERCING,
|
||||
DENSITY,
|
||||
BREACH,
|
||||
WIND_BURST,
|
||||
MENDING,
|
||||
VANISHING_CURSE;
|
||||
|
||||
private static final JavaEnchantment[] VALUES = JavaEnchantment.values();
|
||||
|
||||
public static JavaEnchantment of(int index) {
|
||||
return VALUES[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of all enchantment Java identifiers for use with command suggestions.
|
||||
*/
|
||||
public static final String[] ALL_JAVA_IDENTIFIERS;
|
||||
|
||||
public static @Nullable JavaEnchantment getByJavaIdentifier(String javaIdentifier) {
|
||||
if (!javaIdentifier.startsWith("minecraft:")) {
|
||||
javaIdentifier = "minecraft:" + javaIdentifier;
|
||||
}
|
||||
for (int i = 0; i < ALL_JAVA_IDENTIFIERS.length; i++) {
|
||||
if (ALL_JAVA_IDENTIFIERS[i].equalsIgnoreCase(javaIdentifier)) {
|
||||
return VALUES[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static {
|
||||
ALL_JAVA_IDENTIFIERS = new String[VALUES.length];
|
||||
for (int i = 0; i < ALL_JAVA_IDENTIFIERS.length; i++) {
|
||||
ALL_JAVA_IDENTIFIERS[i] = "minecraft:" + VALUES[i].name().toLowerCase(Locale.ENGLISH);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -34,57 +34,68 @@ import java.util.Locale;
|
||||
|
||||
@Getter
|
||||
public enum Potion {
|
||||
WATER(0),
|
||||
MUNDANE(1),
|
||||
THICK(3),
|
||||
AWKWARD(4),
|
||||
NIGHT_VISION(5),
|
||||
LONG_NIGHT_VISION(6),
|
||||
INVISIBILITY(7),
|
||||
LONG_INVISIBILITY(8),
|
||||
LEAPING(9),
|
||||
LONG_LEAPING(10),
|
||||
STRONG_LEAPING(11),
|
||||
FIRE_RESISTANCE(12),
|
||||
LONG_FIRE_RESISTANCE(13),
|
||||
SWIFTNESS(14),
|
||||
LONG_SWIFTNESS(15),
|
||||
STRONG_SWIFTNESS(16),
|
||||
SLOWNESS(17),
|
||||
LONG_SLOWNESS(18),
|
||||
STRONG_SLOWNESS(42),
|
||||
TURTLE_MASTER(37),
|
||||
LONG_TURTLE_MASTER(38),
|
||||
STRONG_TURTLE_MASTER(39),
|
||||
WATER_BREATHING(19),
|
||||
LONG_WATER_BREATHING(20),
|
||||
HEALING(21),
|
||||
STRONG_HEALING(22),
|
||||
HARMING(23),
|
||||
STRONG_HARMING(24),
|
||||
POISON(25),
|
||||
LONG_POISON(26),
|
||||
STRONG_POISON(27),
|
||||
REGENERATION(28),
|
||||
LONG_REGENERATION(29),
|
||||
STRONG_REGENERATION(30),
|
||||
STRENGTH(31),
|
||||
LONG_STRENGTH(32),
|
||||
STRONG_STRENGTH(33),
|
||||
WEAKNESS(34),
|
||||
LONG_WEAKNESS(35),
|
||||
LUCK(2), //does not exist
|
||||
SLOW_FALLING(40),
|
||||
LONG_SLOW_FALLING(41);
|
||||
WATER(0, ArrowParticleColors.NONE),
|
||||
MUNDANE(1, ArrowParticleColors.NONE), // 2 is extended?
|
||||
THICK(3, ArrowParticleColors.NONE),
|
||||
AWKWARD(4, ArrowParticleColors.NONE),
|
||||
NIGHT_VISION(5, ArrowParticleColors.NIGHT_VISION),
|
||||
LONG_NIGHT_VISION(6, ArrowParticleColors.NIGHT_VISION),
|
||||
INVISIBILITY(7, ArrowParticleColors.INVISIBILITY),
|
||||
LONG_INVISIBILITY(8, ArrowParticleColors.INVISIBILITY),
|
||||
LEAPING(9, ArrowParticleColors.LEAPING),
|
||||
LONG_LEAPING(10, ArrowParticleColors.LEAPING),
|
||||
STRONG_LEAPING(11, ArrowParticleColors.LEAPING),
|
||||
FIRE_RESISTANCE(12, ArrowParticleColors.FIRE_RESISTANCE),
|
||||
LONG_FIRE_RESISTANCE(13, ArrowParticleColors.FIRE_RESISTANCE),
|
||||
SWIFTNESS(14, ArrowParticleColors.SWIFTNESS),
|
||||
LONG_SWIFTNESS(15, ArrowParticleColors.SWIFTNESS),
|
||||
STRONG_SWIFTNESS(16, ArrowParticleColors.SWIFTNESS),
|
||||
SLOWNESS(17, ArrowParticleColors.SLOWNESS),
|
||||
LONG_SLOWNESS(18, ArrowParticleColors.SLOWNESS),
|
||||
STRONG_SLOWNESS(42, ArrowParticleColors.SLOWNESS),
|
||||
TURTLE_MASTER(37, ArrowParticleColors.TURTLE_MASTER),
|
||||
LONG_TURTLE_MASTER(38, ArrowParticleColors.TURTLE_MASTER),
|
||||
STRONG_TURTLE_MASTER(39, ArrowParticleColors.TURTLE_MASTER),
|
||||
WATER_BREATHING(19, ArrowParticleColors.WATER_BREATHING),
|
||||
LONG_WATER_BREATHING(20, ArrowParticleColors.WATER_BREATHING),
|
||||
HEALING(21, ArrowParticleColors.HEALING),
|
||||
STRONG_HEALING(22, ArrowParticleColors.HEALING),
|
||||
HARMING(23, ArrowParticleColors.HARMING),
|
||||
STRONG_HARMING(24, ArrowParticleColors.HARMING),
|
||||
POISON(25, ArrowParticleColors.POISON),
|
||||
LONG_POISON(26, ArrowParticleColors.POISON),
|
||||
STRONG_POISON(27, ArrowParticleColors.POISON),
|
||||
REGENERATION(28, ArrowParticleColors.REGENERATION),
|
||||
LONG_REGENERATION(29, ArrowParticleColors.REGENERATION),
|
||||
STRONG_REGENERATION(30, ArrowParticleColors.REGENERATION),
|
||||
STRENGTH(31, ArrowParticleColors.STRENGTH),
|
||||
LONG_STRENGTH(32, ArrowParticleColors.STRENGTH),
|
||||
STRONG_STRENGTH(33, ArrowParticleColors.STRENGTH),
|
||||
WEAKNESS(34, ArrowParticleColors.WEAKNESS),
|
||||
LONG_WEAKNESS(35, ArrowParticleColors.WEAKNESS),
|
||||
LUCK(2, ArrowParticleColors.NONE), // does not exist in Bedrock
|
||||
SLOW_FALLING(40, ArrowParticleColors.SLOW_FALLING),
|
||||
LONG_SLOW_FALLING(41, ArrowParticleColors.SLOW_FALLING),
|
||||
WIND_CHARGING(43, ArrowParticleColors.WIND_CHARGING),
|
||||
WEAVING(44, ArrowParticleColors.WEAVING),
|
||||
OOZING(45, ArrowParticleColors.OOZING),
|
||||
INFESTATION(46, ArrowParticleColors.INFESTATION);
|
||||
|
||||
public static final Potion[] VALUES = values();
|
||||
|
||||
private final String javaIdentifier;
|
||||
private final short bedrockId;
|
||||
private final int javaColor;
|
||||
|
||||
Potion(int bedrockId) {
|
||||
Potion(int bedrockId, int javaColor) {
|
||||
this.javaIdentifier = "minecraft:" + this.name().toLowerCase(Locale.ENGLISH);
|
||||
this.bedrockId = (short) bedrockId;
|
||||
this.javaColor = javaColor;
|
||||
}
|
||||
|
||||
public int tippedArrowId() {
|
||||
// +1 likely to offset 0 as nothing?
|
||||
return this.bedrockId + 1;
|
||||
}
|
||||
|
||||
public PotionContents toComponent() {
|
||||
@ -106,4 +117,44 @@ public enum Potion {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static @Nullable Potion getByTippedArrowDamage(int bedrockId) {
|
||||
return getByBedrockId(bedrockId - 1);
|
||||
}
|
||||
|
||||
public static byte toTippedArrowId(int javaParticleColor) {
|
||||
for (Potion potion : VALUES) {
|
||||
if (potion.javaColor == javaParticleColor) {
|
||||
return (byte) (potion.bedrockId + 1);
|
||||
}
|
||||
}
|
||||
return (byte) 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* For tipped arrow usage
|
||||
*/
|
||||
private static final class ArrowParticleColors {
|
||||
static final int NONE = 1;
|
||||
static final int NIGHT_VISION = 2039713;
|
||||
static final int INVISIBILITY = 8356754;
|
||||
static final int LEAPING = 2293580;
|
||||
static final int FIRE_RESISTANCE = 14981690;
|
||||
static final int SWIFTNESS = 8171462;
|
||||
static final int SLOWNESS = 5926017;
|
||||
static final int TURTLE_MASTER = 7691106;
|
||||
static final int WATER_BREATHING = 3035801;
|
||||
static final int HEALING = 16262179;
|
||||
static final int HARMING = 4393481;
|
||||
static final int POISON = 5149489;
|
||||
static final int REGENERATION = 13458603;
|
||||
static final int STRENGTH = 9643043;
|
||||
static final int WEAKNESS = 4738376;
|
||||
static final int LUCK = 3381504;
|
||||
static final int SLOW_FALLING = 16773073;
|
||||
static final int WIND_CHARGING = 12438015;
|
||||
static final int WEAVING = 7891290;
|
||||
static final int OOZING = 10092451;
|
||||
static final int INFESTATION = 9214860;
|
||||
}
|
||||
}
|
||||
|
@ -1,153 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.inventory.item;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Potion identifiers and their respective Bedrock IDs used with arrows.
|
||||
* <a href="https://minecraft.wiki/w/Arrow#Data_values">See here</a>
|
||||
*/
|
||||
@Getter
|
||||
public enum TippedArrowPotion {
|
||||
WATER(-1, ArrowParticleColors.NONE), // Guessing this based off of the Potion enum. TODO merge?
|
||||
MUNDANE(2, ArrowParticleColors.NONE), // 3 is extended?
|
||||
THICK(4, ArrowParticleColors.NONE),
|
||||
AWKWARD(5, ArrowParticleColors.NONE),
|
||||
NIGHT_VISION(6, ArrowParticleColors.NIGHT_VISION),
|
||||
LONG_NIGHT_VISION(7, ArrowParticleColors.NIGHT_VISION),
|
||||
INVISIBILITY(8, ArrowParticleColors.INVISIBILITY),
|
||||
LONG_INVISIBILITY(9, ArrowParticleColors.INVISIBILITY),
|
||||
LEAPING(10, ArrowParticleColors.LEAPING),
|
||||
LONG_LEAPING(11, ArrowParticleColors.LEAPING),
|
||||
STRONG_LEAPING(12, ArrowParticleColors.LEAPING),
|
||||
FIRE_RESISTANCE(13, ArrowParticleColors.FIRE_RESISTANCE),
|
||||
LONG_FIRE_RESISTANCE(14, ArrowParticleColors.FIRE_RESISTANCE),
|
||||
SWIFTNESS(15, ArrowParticleColors.SWIFTNESS),
|
||||
LONG_SWIFTNESS(16, ArrowParticleColors.SWIFTNESS),
|
||||
STRONG_SWIFTNESS(17, ArrowParticleColors.SWIFTNESS),
|
||||
SLOWNESS(18, ArrowParticleColors.SLOWNESS),
|
||||
LONG_SLOWNESS(19, ArrowParticleColors.SLOWNESS),
|
||||
STRONG_SLOWNESS(43, ArrowParticleColors.SLOWNESS),
|
||||
WATER_BREATHING(20, ArrowParticleColors.WATER_BREATHING),
|
||||
LONG_WATER_BREATHING(21, ArrowParticleColors.WATER_BREATHING),
|
||||
HEALING(22, ArrowParticleColors.HEALING),
|
||||
STRONG_HEALING(23, ArrowParticleColors.HEALING),
|
||||
HARMING(24, ArrowParticleColors.HARMING),
|
||||
STRONG_HARMING(25, ArrowParticleColors.HARMING),
|
||||
POISON(26, ArrowParticleColors.POISON),
|
||||
LONG_POISON(27, ArrowParticleColors.POISON),
|
||||
STRONG_POISON(28, ArrowParticleColors.POISON),
|
||||
REGENERATION(29, ArrowParticleColors.REGENERATION),
|
||||
LONG_REGENERATION(30, ArrowParticleColors.REGENERATION),
|
||||
STRONG_REGENERATION(31, ArrowParticleColors.REGENERATION),
|
||||
STRENGTH(32, ArrowParticleColors.STRENGTH),
|
||||
LONG_STRENGTH(33, ArrowParticleColors.STRENGTH),
|
||||
STRONG_STRENGTH(34, ArrowParticleColors.STRENGTH),
|
||||
WEAKNESS(35, ArrowParticleColors.WEAKNESS),
|
||||
LONG_WEAKNESS(36, ArrowParticleColors.WEAKNESS),
|
||||
LUCK(2, ArrowParticleColors.NONE), // does not exist in Bedrock
|
||||
TURTLE_MASTER(38, ArrowParticleColors.TURTLE_MASTER),
|
||||
LONG_TURTLE_MASTER(39, ArrowParticleColors.TURTLE_MASTER),
|
||||
STRONG_TURTLE_MASTER(40, ArrowParticleColors.TURTLE_MASTER),
|
||||
SLOW_FALLING(41, ArrowParticleColors.SLOW_FALLING),
|
||||
LONG_SLOW_FALLING(42, ArrowParticleColors.SLOW_FALLING);
|
||||
|
||||
private static final TippedArrowPotion[] VALUES = values();
|
||||
|
||||
private final String javaIdentifier;
|
||||
private final short bedrockId;
|
||||
/**
|
||||
* The Java color associated with this ID.
|
||||
* Used for looking up Java arrow color entity metadata as Bedrock potion IDs, which is what is used for entities in Bedrock
|
||||
*/
|
||||
private final int javaColor;
|
||||
|
||||
TippedArrowPotion(int bedrockId, ArrowParticleColors arrowParticleColor) {
|
||||
this.javaIdentifier = "minecraft:" + this.name().toLowerCase(Locale.ENGLISH);
|
||||
this.bedrockId = (short) bedrockId;
|
||||
this.javaColor = arrowParticleColor.getColor();
|
||||
}
|
||||
|
||||
public static @Nullable TippedArrowPotion of(int id) {
|
||||
if (id >= 0 && id < VALUES.length) {
|
||||
return VALUES[id];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static @Nullable TippedArrowPotion getByBedrockId(int bedrockId) {
|
||||
for (TippedArrowPotion potion : VALUES) {
|
||||
if (potion.bedrockId == bedrockId) {
|
||||
return potion;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param color the potion color to look up
|
||||
* @return the tipped arrow potion that most closely resembles that color.
|
||||
*/
|
||||
public static @Nullable TippedArrowPotion getByJavaColor(int color) {
|
||||
for (TippedArrowPotion potion : VALUES) {
|
||||
if (potion.javaColor == color) {
|
||||
return potion;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private enum ArrowParticleColors {
|
||||
NONE(-1),
|
||||
NIGHT_VISION(2039713),
|
||||
INVISIBILITY(8356754),
|
||||
LEAPING(2293580),
|
||||
FIRE_RESISTANCE(14981690),
|
||||
SWIFTNESS(8171462),
|
||||
SLOWNESS(5926017),
|
||||
TURTLE_MASTER(7691106),
|
||||
WATER_BREATHING(3035801),
|
||||
HEALING(16262179),
|
||||
HARMING(4393481),
|
||||
POISON(5149489),
|
||||
REGENERATION(13458603),
|
||||
STRENGTH(9643043),
|
||||
WEAKNESS(4738376),
|
||||
LUCK(3381504),
|
||||
SLOW_FALLING(16773073);
|
||||
|
||||
@Getter
|
||||
private final int color;
|
||||
|
||||
ArrowParticleColors(int color) {
|
||||
this.color = color;
|
||||
}
|
||||
}
|
||||
}
|
@ -25,6 +25,9 @@
|
||||
|
||||
package org.geysermc.geyser.inventory.recipe;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
|
||||
|
||||
/**
|
||||
* A more compact version of {@link org.geysermc.mcprotocollib.protocol.data.game.recipe.Recipe}.
|
||||
*/
|
||||
@ -33,4 +36,7 @@ public interface GeyserRecipe {
|
||||
* Whether the recipe is flexible or not in which items can be placed where.
|
||||
*/
|
||||
boolean isShaped();
|
||||
|
||||
@Nullable
|
||||
ItemStack result();
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ public final class TrimRecipe {
|
||||
public static final ItemDescriptorWithCount TEMPLATE = tagDescriptor("minecraft:trim_templates");
|
||||
|
||||
public static TrimMaterial readTrimMaterial(GeyserSession session, RegistryEntry entry) {
|
||||
String key = stripMinecraftNamespace(entry.getId());
|
||||
String key = entry.getId().asMinimalString();
|
||||
|
||||
// Color is used when hovering over the item
|
||||
// Find the nearest legacy color from the RGB Java gives us to work with
|
||||
@ -67,7 +67,7 @@ public final class TrimRecipe {
|
||||
}
|
||||
|
||||
public static TrimPattern readTrimPattern(GeyserSession session, RegistryEntry entry) {
|
||||
String key = stripMinecraftNamespace(entry.getId());
|
||||
String key = entry.getId().asMinimalString();
|
||||
|
||||
String itemIdentifier = entry.getData().getString("template_item");
|
||||
ItemMapping itemMapping = session.getItemMappings().getMapping(itemIdentifier);
|
||||
@ -78,19 +78,6 @@ public final class TrimRecipe {
|
||||
return new TrimPattern(itemMapping.getBedrockIdentifier(), key);
|
||||
}
|
||||
|
||||
// TODO find a good place for a stripNamespace util method
|
||||
private static String stripMinecraftNamespace(String identifier) {
|
||||
int i = identifier.indexOf(':');
|
||||
if (i >= 0) {
|
||||
String namespace = identifier.substring(0, i);
|
||||
// Only strip minecraft namespace
|
||||
if (namespace.equals("minecraft")) {
|
||||
return identifier.substring(i + 1);
|
||||
}
|
||||
}
|
||||
return identifier;
|
||||
}
|
||||
|
||||
private TrimRecipe() {
|
||||
//no-op
|
||||
}
|
||||
|
@ -26,7 +26,6 @@
|
||||
package org.geysermc.geyser.inventory.updater;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMaps;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
@ -38,10 +37,9 @@ import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.inventory.AnvilContainer;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
import org.geysermc.geyser.inventory.Inventory;
|
||||
import org.geysermc.geyser.inventory.item.Enchantment.JavaEnchantment;
|
||||
import org.geysermc.geyser.inventory.item.BedrockEnchantment;
|
||||
import org.geysermc.geyser.item.enchantment.Enchantment;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.registry.Registries;
|
||||
import org.geysermc.geyser.registry.type.EnchantmentData;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.inventory.InventoryTranslator;
|
||||
import org.geysermc.geyser.translator.text.MessageTranslator;
|
||||
@ -307,18 +305,17 @@ public class AnvilInventoryUpdater extends InventoryUpdater {
|
||||
*/
|
||||
private int calcMergeEnchantmentCost(GeyserSession session, GeyserItemStack input, GeyserItemStack material, boolean bedrock) {
|
||||
boolean hasCompatible = false;
|
||||
Object2IntMap<JavaEnchantment> combinedEnchantments = getEnchantments(input);
|
||||
Object2IntMap<Enchantment> combinedEnchantments = getEnchantments(session, input);
|
||||
int cost = 0;
|
||||
for (Object2IntMap.Entry<JavaEnchantment> entry : getEnchantments(material).object2IntEntrySet()) {
|
||||
JavaEnchantment enchantment = entry.getKey();
|
||||
EnchantmentData data = Registries.ENCHANTMENTS.get(enchantment);
|
||||
if (data == null) {
|
||||
GeyserImpl.getInstance().getLogger().debug("Java enchantment not in registry: " + enchantment);
|
||||
continue;
|
||||
}
|
||||
for (Object2IntMap.Entry<Enchantment> entry : getEnchantments(session, material).object2IntEntrySet()) {
|
||||
Enchantment enchantment = entry.getKey();
|
||||
|
||||
boolean canApply = isEnchantedBook(input) || data.validItems().contains(input.getJavaId());
|
||||
for (JavaEnchantment incompatible : data.incompatibleEnchantments()) {
|
||||
boolean canApply = isEnchantedBook(input) || session.getTagCache().is(enchantment.supportedItems(), input);
|
||||
var exclusiveSet = enchantment.exclusiveSet();
|
||||
if (exclusiveSet != null) {
|
||||
int[] incompatibleEnchantments = session.getTagCache().get(exclusiveSet);
|
||||
for (int i : incompatibleEnchantments) {
|
||||
Enchantment incompatible = session.getRegistryCache().enchantments().byId(i);
|
||||
if (combinedEnchantments.containsKey(incompatible)) {
|
||||
canApply = false;
|
||||
if (!bedrock) {
|
||||
@ -326,6 +323,7 @@ public class AnvilInventoryUpdater extends InventoryUpdater {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (canApply || (!bedrock && session.getGameMode() == GameMode.CREATIVE)) {
|
||||
int currentLevel = combinedEnchantments.getOrDefault(enchantment, 0);
|
||||
@ -334,12 +332,12 @@ public class AnvilInventoryUpdater extends InventoryUpdater {
|
||||
newLevel++;
|
||||
}
|
||||
newLevel = Math.max(currentLevel, newLevel);
|
||||
if (newLevel > data.maxLevel()) {
|
||||
newLevel = data.maxLevel();
|
||||
if (newLevel > enchantment.maxLevel()) {
|
||||
newLevel = enchantment.maxLevel();
|
||||
}
|
||||
combinedEnchantments.put(enchantment, newLevel);
|
||||
|
||||
int rarityMultiplier = data.rarityMultiplier();
|
||||
int rarityMultiplier = enchantment.anvilCost();
|
||||
if (isEnchantedBook(material) && rarityMultiplier > 1) {
|
||||
rarityMultiplier /= 2;
|
||||
}
|
||||
@ -347,11 +345,11 @@ public class AnvilInventoryUpdater extends InventoryUpdater {
|
||||
if (newLevel > currentLevel) {
|
||||
hasCompatible = true;
|
||||
}
|
||||
if (enchantment == JavaEnchantment.IMPALING) {
|
||||
if (enchantment.bedrockEnchantment() == BedrockEnchantment.IMPALING) {
|
||||
// Multiplier is halved on Bedrock for some reason
|
||||
rarityMultiplier /= 2;
|
||||
} else if (enchantment == JavaEnchantment.SWEEPING_EDGE) {
|
||||
// Doesn't exist on Bedrock
|
||||
} else if (enchantment.bedrockEnchantment() == null) {
|
||||
// Whatever this is, doesn't exist on Bedrock
|
||||
rarityMultiplier = 0;
|
||||
}
|
||||
cost += rarityMultiplier * (newLevel - currentLevel);
|
||||
@ -368,7 +366,7 @@ public class AnvilInventoryUpdater extends InventoryUpdater {
|
||||
return cost;
|
||||
}
|
||||
|
||||
private Object2IntMap<JavaEnchantment> getEnchantments(GeyserItemStack itemStack) {
|
||||
private Object2IntMap<Enchantment> getEnchantments(GeyserSession session, GeyserItemStack itemStack) {
|
||||
ItemEnchantments enchantmentComponent;
|
||||
if (isEnchantedBook(itemStack)) {
|
||||
enchantmentComponent = itemStack.getComponent(DataComponentType.STORED_ENCHANTMENTS);
|
||||
@ -376,9 +374,9 @@ public class AnvilInventoryUpdater extends InventoryUpdater {
|
||||
enchantmentComponent = itemStack.getComponent(DataComponentType.ENCHANTMENTS);
|
||||
}
|
||||
if (enchantmentComponent != null) {
|
||||
Object2IntMap<JavaEnchantment> enchantments = new Object2IntOpenHashMap<>();
|
||||
Object2IntMap<Enchantment> enchantments = new Object2IntOpenHashMap<>();
|
||||
for (Map.Entry<Integer, Integer> entry : enchantmentComponent.getEnchantments().entrySet()) {
|
||||
JavaEnchantment enchantment = JavaEnchantment.of(entry.getKey());
|
||||
Enchantment enchantment = session.getRegistryCache().enchantments().byId(entry.getKey());
|
||||
if (enchantment == null) {
|
||||
GeyserImpl.getInstance().getLogger().debug("Unknown Java enchantment in anvil: " + entry.getKey());
|
||||
continue;
|
||||
|
Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden Mehr anzeigen
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren