Mirror von
https://github.com/GeyserMC/Geyser.git
synchronisiert 2024-12-26 16:12:46 +01:00
Merge branch 'master' of https://github.com/GeyserMC/Geyser into feature/configurate
Dieser Commit ist enthalten in:
Commit
10bf4eeb4e
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
|
186
.github/workflows/build.yml
vendored
186
.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
|
||||
|
||||
- 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
|
||||
BUILD_NUMBER: ${{ steps.release-info.outputs.curentRelease }}
|
||||
|
||||
- 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,45 @@ 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
|
||||
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
|
||||
run: ./gradlew fabric:modrinth
|
||||
|
||||
- name: Publish to Modrinth (NeoForge)
|
||||
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
|
||||
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: neoforge:modrinth
|
||||
gradle-home-cache-cleanup: true
|
||||
run: ./gradlew neoforge: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/
|
16
.github/workflows/pull-request.yml
vendored
16
.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 }}
|
||||
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 }}
|
@ -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.20.5/1.20.6
|
||||
|
||||
## 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;
|
||||
|
@ -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_20_5");
|
||||
} 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;
|
||||
}
|
||||
|
@ -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,25 @@ 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.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 +59,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 +106,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 +164,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());
|
||||
|
@ -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
|
||||
})
|
||||
|
@ -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
|
||||
|
@ -81,14 +81,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;
|
||||
|
||||
@ -116,12 +116,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;
|
||||
}
|
||||
@ -130,12 +130,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;
|
||||
}
|
||||
@ -144,11 +144,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;
|
||||
}
|
||||
@ -156,8 +156,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.
|
||||
@ -268,7 +267,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();
|
||||
@ -489,7 +488,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserSpigotConfiguration.class);
|
||||
ConfigLoaderTemp.load(GeyserPluginConfig.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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -68,7 +68,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
|
||||
@ -178,8 +178,6 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
||||
}
|
||||
}
|
||||
|
||||
this.geyserLogger = new GeyserStandaloneLogger();
|
||||
|
||||
if (useGui && gui == null) {
|
||||
gui = new GeyserStandaloneGUI(geyserLogger);
|
||||
gui.redirectSystemStreams();
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -27,7 +27,6 @@ dependencies {
|
||||
implementation(libs.websocket)
|
||||
|
||||
api(libs.bundles.protocol)
|
||||
implementation(libs.blockstateupdater)
|
||||
|
||||
api(libs.mcauthlib)
|
||||
api(libs.mcprotocollib) {
|
||||
|
@ -47,6 +47,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;
|
||||
@ -79,6 +80,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;
|
||||
@ -217,6 +219,8 @@ public class GeyserImpl implements GeyserApi {
|
||||
Registries.init();
|
||||
BlockRegistries.init();
|
||||
|
||||
RegistryCache.init();
|
||||
|
||||
/* Initialize translators */
|
||||
EntityDefinitions.init();
|
||||
MessageTranslator.init();
|
||||
@ -389,7 +393,7 @@ public class GeyserImpl implements GeyserApi {
|
||||
|
||||
this.newsHandler = new NewsHandler(BRANCH, this.buildNumber());
|
||||
|
||||
//Packets.initGeyser();
|
||||
Packets.initGeyser();
|
||||
|
||||
if (Epoll.isAvailable()) {
|
||||
this.erosionUnixListener = new UnixSocketClientListener();
|
||||
@ -772,6 +776,7 @@ public class GeyserImpl implements GeyserApi {
|
||||
return 0;
|
||||
}
|
||||
|
||||
//noinspection DataFlowIssue
|
||||
return Integer.parseInt(BUILD_NUMBER);
|
||||
}
|
||||
|
||||
|
@ -137,7 +137,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());
|
||||
@ -364,7 +364,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());
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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,28 +120,31 @@ 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
|
||||
bedRotation = 180;
|
||||
zOffset = -.5f;
|
||||
} else if (fullIdentifier.contains("facing=east")) {
|
||||
// bed is facing east
|
||||
bedRotation = 90;
|
||||
xOffset = -.5f;
|
||||
} else if (fullIdentifier.contains("facing=west")) {
|
||||
// bed is facing west
|
||||
bedRotation = 270;
|
||||
xOffset = .5f;
|
||||
} else if (fullIdentifier.contains("facing=north")) {
|
||||
// rotation does not change because north is 0
|
||||
zOffset = .5f;
|
||||
if (state.block() instanceof BedBlock) {
|
||||
switch (state.getValue(Properties.HORIZONTAL_FACING)) {
|
||||
case SOUTH -> {
|
||||
bedRotation = 180;
|
||||
zOffset = -.5f;
|
||||
}
|
||||
case EAST -> {
|
||||
bedRotation = 90;
|
||||
xOffset = -.5f;
|
||||
}
|
||||
case WEST -> {
|
||||
bedRotation = 270;
|
||||
xOffset = .5f;
|
||||
}
|
||||
case NORTH -> {
|
||||
// rotation does not change because north is 0
|
||||
zOffset = .5f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setYaw(yaw);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
@ -35,6 +35,7 @@ import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtType;
|
||||
import org.geysermc.geyser.inventory.item.BannerPattern;
|
||||
import org.geysermc.geyser.inventory.item.DyeColor;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.geyser.registry.type.ItemMapping;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.registry.JavaRegistry;
|
||||
@ -199,8 +200,8 @@ public class BannerItem extends BlockItem {
|
||||
return null;
|
||||
}
|
||||
|
||||
public BannerItem(String javaIdentifier, Builder builder) {
|
||||
super(javaIdentifier, builder);
|
||||
public BannerItem(Builder builder, Block block, Block... otherBlocks) {
|
||||
super(builder, block, otherBlocks);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -25,11 +25,26 @@
|
||||
|
||||
package org.geysermc.geyser.item.type;
|
||||
|
||||
/**
|
||||
* TODO needed?
|
||||
*/
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
|
||||
public class BlockItem extends Item {
|
||||
public BlockItem(String javaIdentifier, Builder builder) {
|
||||
public BlockItem(Builder builder, Block block, Block... otherBlocks) {
|
||||
super(block.javaIdentifier().value(), builder);
|
||||
|
||||
// Ensure this item can be looked up by its block(s)
|
||||
registerBlock(block, this);
|
||||
for (Block otherBlock : otherBlocks) {
|
||||
registerBlock(otherBlock, this);
|
||||
}
|
||||
}
|
||||
|
||||
// Use this constructor if the item name is not the same as its primary block
|
||||
public BlockItem(String javaIdentifier, Builder builder, Block block, Block... otherBlocks) {
|
||||
super(javaIdentifier, builder);
|
||||
|
||||
registerBlock(block, this);
|
||||
for (Block otherBlock : otherBlocks) {
|
||||
registerBlock(otherBlock, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ package org.geysermc.geyser.item.type;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.nbt.NbtType;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.geyser.registry.type.ItemMapping;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.item.BedrockItemBuilder;
|
||||
@ -38,8 +39,8 @@ import java.util.List;
|
||||
|
||||
public class DecoratedPotItem extends BlockItem {
|
||||
|
||||
public DecoratedPotItem(String javaIdentifier, Builder builder) {
|
||||
super(javaIdentifier, builder);
|
||||
public DecoratedPotItem(Builder builder, Block block, Block... otherBlocks) {
|
||||
super(builder, block, otherBlocks);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -35,6 +35,7 @@ import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
import org.geysermc.geyser.inventory.item.Enchantment;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.geyser.registry.type.ItemMapping;
|
||||
import org.geysermc.geyser.registry.type.ItemMappings;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
@ -49,20 +50,12 @@ import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponen
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.ItemEnchantments;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class Item {
|
||||
/**
|
||||
* This is a map from Java-only enchantments to their translation keys so that we can
|
||||
* map these enchantments to Bedrock clients, since they don't actually exist there.
|
||||
*/
|
||||
private static final Map<Enchantment.JavaEnchantment, String> ENCHANTMENT_TRANSLATION_KEYS = Map.of(
|
||||
Enchantment.JavaEnchantment.SWEEPING_EDGE, "enchantment.minecraft.sweeping",
|
||||
Enchantment.JavaEnchantment.DENSITY, "enchantment.minecraft.density",
|
||||
Enchantment.JavaEnchantment.BREACH, "enchantment.minecraft.breach",
|
||||
Enchantment.JavaEnchantment.WIND_BURST, "enchantment.minecraft.wind_burst");
|
||||
|
||||
private static final Map<Block, Item> BLOCK_TO_ITEM = new HashMap<>();
|
||||
private final String javaIdentifier;
|
||||
private int javaId = -1;
|
||||
private final int stackSize;
|
||||
@ -233,6 +226,16 @@ public class Item {
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a map from Java-only enchantments to their translation keys so that we can
|
||||
* map these enchantments to Bedrock clients, since they don't actually exist there.
|
||||
*/
|
||||
private static final Map<Enchantment.JavaEnchantment, String> ENCHANTMENT_TRANSLATION_KEYS = Map.of(
|
||||
Enchantment.JavaEnchantment.SWEEPING_EDGE, "enchantment.minecraft.sweeping",
|
||||
Enchantment.JavaEnchantment.DENSITY, "enchantment.minecraft.density",
|
||||
Enchantment.JavaEnchantment.BREACH, "enchantment.minecraft.breach",
|
||||
Enchantment.JavaEnchantment.WIND_BURST, "enchantment.minecraft.wind_burst");
|
||||
|
||||
protected final @Nullable NbtMap remapEnchantment(GeyserSession session, int enchantId, int level, BedrockItemBuilder builder) {
|
||||
// TODO verify
|
||||
// TODO streamline Enchantment process
|
||||
@ -281,6 +284,18 @@ public class Item {
|
||||
'}';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the block associated with this item, or air if nothing
|
||||
*/
|
||||
@NonNull
|
||||
public static Item byBlock(Block block) {
|
||||
return BLOCK_TO_ITEM.getOrDefault(block, Items.AIR);
|
||||
}
|
||||
|
||||
protected static void registerBlock(Block block, Item item) {
|
||||
BLOCK_TO_ITEM.put(block, item);
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ package org.geysermc.geyser.item.type;
|
||||
|
||||
import com.github.steveice10.mc.auth.data.GameProfile;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.text.ChatColor;
|
||||
import org.geysermc.geyser.text.MinecraftLocale;
|
||||
@ -34,9 +35,9 @@ import org.geysermc.geyser.translator.item.BedrockItemBuilder;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
|
||||
|
||||
public class PlayerHeadItem extends Item {
|
||||
public PlayerHeadItem(String javaIdentifier, Builder builder) {
|
||||
super(javaIdentifier, builder);
|
||||
public class PlayerHeadItem extends BlockItem {
|
||||
public PlayerHeadItem(Builder builder, Block block, Block... otherBlocks) {
|
||||
super(builder, block, otherBlocks);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -30,6 +30,7 @@ import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.cloudburstmc.nbt.NbtType;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.geyser.registry.type.ItemMapping;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.item.BedrockItemBuilder;
|
||||
@ -42,8 +43,8 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ShulkerBoxItem extends BlockItem {
|
||||
public ShulkerBoxItem(String javaIdentifier, Builder builder) {
|
||||
super(javaIdentifier, builder);
|
||||
public ShulkerBoxItem(Builder builder, Block block, Block... otherBlocks) {
|
||||
super(builder, block, otherBlocks);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -41,16 +41,18 @@ public class TippedArrowItem extends ArrowItem {
|
||||
|
||||
@Override
|
||||
public ItemData.Builder translateToBedrock(int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) {
|
||||
PotionContents potionContents = components.get(DataComponentType.POTION_CONTENTS);
|
||||
if (potionContents != null) {
|
||||
TippedArrowPotion tippedArrowPotion = TippedArrowPotion.of(potionContents.getPotionId());
|
||||
if (tippedArrowPotion != null) {
|
||||
return ItemData.builder()
|
||||
.definition(mapping.getBedrockDefinition())
|
||||
.damage(tippedArrowPotion.getBedrockId())
|
||||
.count(count);
|
||||
if (components != null) {
|
||||
PotionContents potionContents = components.get(DataComponentType.POTION_CONTENTS);
|
||||
if (potionContents != null) {
|
||||
TippedArrowPotion tippedArrowPotion = TippedArrowPotion.of(potionContents.getPotionId());
|
||||
if (tippedArrowPotion != null) {
|
||||
return ItemData.builder()
|
||||
.definition(mapping.getBedrockDefinition())
|
||||
.damage(tippedArrowPotion.getBedrockId())
|
||||
.count(count);
|
||||
}
|
||||
GeyserImpl.getInstance().getLogger().debug("Unknown Java potion (tipped arrow): " + potionContents.getPotionId());
|
||||
}
|
||||
GeyserImpl.getInstance().getLogger().debug("Unknown Java potion (tipped arrow): " + potionContents.getPotionId());
|
||||
}
|
||||
return super.translateToBedrock(count, components, mapping, mappings);
|
||||
}
|
||||
|
@ -25,24 +25,20 @@
|
||||
|
||||
package org.geysermc.geyser.level;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.geysermc.erosion.packet.backendbound.*;
|
||||
import org.geysermc.erosion.packet.backendbound.BackendboundBatchBlockRequestPacket;
|
||||
import org.geysermc.erosion.packet.backendbound.BackendboundBlockRequestPacket;
|
||||
import org.geysermc.erosion.packet.backendbound.BackendboundPickBlockPacket;
|
||||
import org.geysermc.erosion.util.BlockPositionIterator;
|
||||
import org.geysermc.erosion.util.LecternUtils;
|
||||
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 org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityInfo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class GeyserWorldManager extends WorldManager {
|
||||
@ -91,51 +87,6 @@ public class GeyserWorldManager extends WorldManager {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendLecternData(GeyserSession session, int x, int z, List<BlockEntityInfo> blockEntityInfos) {
|
||||
var erosionHandler = session.getErosionHandler().getAsActive();
|
||||
if (erosionHandler == null) {
|
||||
// No-op - don't send any additional information other than what the chunk has already sent
|
||||
return;
|
||||
}
|
||||
List<Vector3i> vectors = new ObjectArrayList<>(blockEntityInfos.size());
|
||||
//noinspection ForLoopReplaceableByForEach - avoid constructing iterator
|
||||
for (int i = 0; i < blockEntityInfos.size(); i++) {
|
||||
BlockEntityInfo info = blockEntityInfos.get(i);
|
||||
vectors.add(Vector3i.from(info.getX(), info.getY(), info.getZ()));
|
||||
}
|
||||
erosionHandler.sendPacket(new BackendboundBatchBlockEntityPacket(x, z, vectors));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendLecternData(GeyserSession session, int x, int y, int z) {
|
||||
var erosionHandler = session.getErosionHandler().getAsActive();
|
||||
if (erosionHandler != null) {
|
||||
erosionHandler.sendPacket(new BackendboundBlockEntityPacket(Vector3i.from(x, y, z)));
|
||||
return;
|
||||
}
|
||||
|
||||
// Without direct server access, we can't get lectern information on-the-fly.
|
||||
// I should have set this up so it's only called when there is a book in the block state. - Camotoy
|
||||
NbtMapBuilder lecternTag = LecternUtils.getBaseLecternTag(x, y, z, 1);
|
||||
lecternTag.putCompound("book", NbtMap.builder()
|
||||
.putByte("Count", (byte) 1)
|
||||
.putShort("Damage", (short) 0)
|
||||
.putString("Name", "minecraft:written_book")
|
||||
.putCompound("tag", NbtMap.builder()
|
||||
.putString("photoname", "")
|
||||
.putString("text", "")
|
||||
.build())
|
||||
.build());
|
||||
lecternTag.putInt("page", -1); // I'm surprisingly glad this exists - it forces Bedrock to stop reading immediately. Usually.
|
||||
BlockEntityUtils.updateBlockEntity(session, lecternTag.build(), Vector3i.from(x, y, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldExpectLecternHandled(GeyserSession session) {
|
||||
return session.getErosionHandler().isActive();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGameRule(GeyserSession session, String name, Object value) {
|
||||
super.setGameRule(session, name, value);
|
||||
@ -179,9 +130,9 @@ public class GeyserWorldManager extends WorldManager {
|
||||
if (erosionHandler == null) {
|
||||
return super.getPickItemComponents(session, x, y, z, addNbtData);
|
||||
}
|
||||
CompletableFuture<DataComponents> future = new CompletableFuture<>();
|
||||
CompletableFuture<Int2ObjectMap<byte[]>> future = new CompletableFuture<>();
|
||||
erosionHandler.setPickBlockLookup(future);
|
||||
erosionHandler.sendPacket(new BackendboundPickBlockPacket(Vector3i.from(x, y, z)));
|
||||
return future;
|
||||
return future.thenApply(RAW_TRANSFORMER);
|
||||
}
|
||||
}
|
||||
|
@ -25,19 +25,28 @@
|
||||
|
||||
package org.geysermc.geyser.level;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.geysermc.erosion.util.BlockPositionIterator;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponent;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityInfo;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.ItemCodecHelper;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.setting.Difficulty;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Class that manages or retrieves various information
|
||||
@ -48,6 +57,16 @@ import java.util.concurrent.CompletableFuture;
|
||||
*/
|
||||
public abstract class WorldManager {
|
||||
|
||||
@NonNull
|
||||
public final BlockState blockAt(GeyserSession session, Vector3i vector) {
|
||||
return this.blockAt(session, vector.getX(), vector.getY(), vector.getZ());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public BlockState blockAt(GeyserSession session, int x, int y, int z) {
|
||||
return BlockState.of(this.getBlockAt(session, x, y, z));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Java block state at the specified location
|
||||
*
|
||||
@ -97,40 +116,6 @@ public abstract class WorldManager {
|
||||
*/
|
||||
public abstract boolean hasOwnChunkCache();
|
||||
|
||||
/**
|
||||
* Sigh. <br>
|
||||
*
|
||||
* So, on Java Edition, the lectern is an inventory. Java opens it and gets the contents of the book there.
|
||||
* On Bedrock, the lectern contents are part of the block entity tag. Therefore, Bedrock expects to have the contents
|
||||
* of the lectern ready and present in the world. If the contents are not there, it takes at least two clicks for the
|
||||
* lectern to update the tag and then present itself. <br>
|
||||
*
|
||||
* We solve this problem by querying all loaded lecterns, where possible, and sending their information in a block entity
|
||||
* tag.
|
||||
* <p>
|
||||
* Note that the lectern data may be sent asynchronously.
|
||||
*
|
||||
* @param session the session of the player
|
||||
* @param x the x coordinate of the lectern
|
||||
* @param y the y coordinate of the lectern
|
||||
* @param z the z coordinate of the lectern
|
||||
*/
|
||||
public abstract void sendLecternData(GeyserSession session, int x, int y, int z);
|
||||
|
||||
/**
|
||||
* {@link #sendLecternData(GeyserSession, int, int, int)} but batched for chunks.
|
||||
*
|
||||
* @param x chunk x
|
||||
* @param z chunk z
|
||||
* @param blockEntityInfos a list of coordinates (chunk local) to grab lecterns from.
|
||||
*/
|
||||
public abstract void sendLecternData(GeyserSession session, int x, int z, List<BlockEntityInfo> blockEntityInfos);
|
||||
|
||||
/**
|
||||
* @return whether we should expect lectern data to update, or if we have to fall back on a workaround.
|
||||
*/
|
||||
public abstract boolean shouldExpectLecternHandled(GeyserSession session);
|
||||
|
||||
/**
|
||||
* Updates a gamerule value on the Java server
|
||||
*
|
||||
@ -223,4 +208,20 @@ public abstract class WorldManager {
|
||||
public CompletableFuture<@Nullable DataComponents> getPickItemComponents(GeyserSession session, int x, int y, int z, boolean addExtraData) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
protected static final Function<Int2ObjectMap<byte[]>, DataComponents> RAW_TRANSFORMER = map -> {
|
||||
try {
|
||||
Map<DataComponentType<?>, DataComponent<?, ?>> components = new HashMap<>();
|
||||
Int2ObjectMaps.fastForEach(map, entry -> {
|
||||
DataComponentType type = DataComponentType.from(entry.getIntKey());
|
||||
ByteBuf buf = Unpooled.wrappedBuffer(entry.getValue());
|
||||
DataComponent value = type.readDataComponent(ItemCodecHelper.INSTANCE, buf);
|
||||
components.put(type, value);
|
||||
});
|
||||
return new DataComponents(components);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -25,363 +25,19 @@
|
||||
|
||||
package org.geysermc.geyser.level.block;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import it.unimi.dsi.fastutil.ints.*;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import org.geysermc.geyser.level.physics.Direction;
|
||||
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.block.type.PistonBlock;
|
||||
import org.geysermc.geyser.level.physics.PistonBehavior;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.registry.type.BlockMapping;
|
||||
import org.geysermc.geyser.translator.level.block.entity.PistonBlockEntityTranslator;
|
||||
import org.geysermc.geyser.util.collection.FixedInt2ByteMap;
|
||||
import org.geysermc.geyser.util.collection.FixedInt2IntMap;
|
||||
import org.geysermc.geyser.util.collection.LecternHasBookMap;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Used for block entities if the Java block state contains Bedrock block information.
|
||||
*/
|
||||
public final class BlockStateValues {
|
||||
private static final IntSet ALL_CAULDRONS = new IntOpenHashSet();
|
||||
private static final Int2IntMap BANNER_COLORS = new FixedInt2IntMap();
|
||||
private static final Int2ByteMap BED_COLORS = new FixedInt2ByteMap();
|
||||
private static final Int2IntMap BRUSH_PROGRESS = new Int2IntOpenHashMap();
|
||||
private static final Int2ByteMap COMMAND_BLOCK_VALUES = new Int2ByteOpenHashMap();
|
||||
private static final Int2ObjectMap<DoubleChestValue> DOUBLE_CHEST_VALUES = new Int2ObjectOpenHashMap<>();
|
||||
private static final Int2ObjectMap<String> FLOWER_POT_VALUES = new Int2ObjectOpenHashMap<>();
|
||||
private static final IntSet HORIZONTAL_FACING_JIGSAWS = new IntOpenHashSet();
|
||||
private static final LecternHasBookMap LECTERN_BOOK_STATES = new LecternHasBookMap();
|
||||
private static final IntSet NON_WATER_CAULDRONS = new IntOpenHashSet();
|
||||
private static final Int2IntMap NOTEBLOCK_PITCHES = new FixedInt2IntMap();
|
||||
private static final Int2BooleanMap PISTON_VALUES = new Int2BooleanOpenHashMap();
|
||||
private static final IntSet STICKY_PISTONS = new IntOpenHashSet();
|
||||
private static final Object2IntMap<Direction> PISTON_HEADS = new Object2IntOpenHashMap<>();
|
||||
private static final Int2ObjectMap<Direction> PISTON_ORIENTATION = new Int2ObjectOpenHashMap<>();
|
||||
private static final IntSet ALL_PISTON_HEADS = new IntOpenHashSet();
|
||||
private static final IntSet MOVING_PISTONS = new IntOpenHashSet();
|
||||
private static final Int2ByteMap SKULL_VARIANTS = new FixedInt2ByteMap();
|
||||
private static final IntSet SKULL_POWERED = new IntOpenHashSet();
|
||||
private static final Int2ByteMap SKULL_ROTATIONS = new Int2ByteOpenHashMap();
|
||||
private static final Int2IntMap SKULL_WALL_DIRECTIONS = new Int2IntOpenHashMap();
|
||||
private static final Int2ByteMap SHULKERBOX_DIRECTIONS = new FixedInt2ByteMap();
|
||||
private static final Int2IntMap WATER_LEVEL = new Int2IntOpenHashMap();
|
||||
private static final IntSet UPPER_DOORS = new IntOpenHashSet();
|
||||
|
||||
public static final int JAVA_AIR_ID = 0;
|
||||
|
||||
public static int JAVA_COBWEB_ID;
|
||||
public static int JAVA_FURNACE_ID;
|
||||
public static int JAVA_FURNACE_LIT_ID;
|
||||
public static int JAVA_HONEY_BLOCK_ID;
|
||||
public static int JAVA_SLIME_BLOCK_ID;
|
||||
public static int JAVA_SPAWNER_ID;
|
||||
public static int JAVA_WATER_ID;
|
||||
|
||||
public static final int NUM_WATER_LEVELS = 9;
|
||||
|
||||
/**
|
||||
* Determines if the block state contains Bedrock block information
|
||||
*
|
||||
* @param javaId The Java Identifier of the block
|
||||
* @param javaBlockState the Java Block State of the block
|
||||
* @param blockData JsonNode of info about the block from blocks.json
|
||||
*/
|
||||
public static void storeBlockStateValues(String javaId, int javaBlockState, JsonNode blockData) {
|
||||
JsonNode bannerColor = blockData.get("banner_color");
|
||||
if (bannerColor != null) {
|
||||
BANNER_COLORS.put(javaBlockState, (byte) bannerColor.intValue());
|
||||
return; // There will never be a banner color and a skull variant
|
||||
}
|
||||
|
||||
JsonNode bedColor = blockData.get("bed_color");
|
||||
if (bedColor != null) {
|
||||
BED_COLORS.put(javaBlockState, (byte) bedColor.intValue());
|
||||
return;
|
||||
}
|
||||
|
||||
JsonNode bedrockStates = blockData.get("bedrock_states");
|
||||
if (bedrockStates != null) {
|
||||
JsonNode brushedProgress = bedrockStates.get("brushed_progress");
|
||||
if (brushedProgress != null) {
|
||||
BRUSH_PROGRESS.put(javaBlockState, brushedProgress.intValue());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (javaId.contains("command_block")) {
|
||||
COMMAND_BLOCK_VALUES.put(javaBlockState, javaId.contains("conditional=true") ? (byte) 1 : (byte) 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (blockData.get("double_chest_position") != null) {
|
||||
boolean isX = (blockData.get("x") != null);
|
||||
boolean isDirectionPositive = ((blockData.get("x") != null && blockData.get("x").asBoolean()) ||
|
||||
(blockData.get("z") != null && blockData.get("z").asBoolean()));
|
||||
boolean isLeft = (blockData.get("double_chest_position").asText().contains("left"));
|
||||
DOUBLE_CHEST_VALUES.put(javaBlockState, new DoubleChestValue(isX, isDirectionPositive, isLeft));
|
||||
return;
|
||||
}
|
||||
|
||||
if (javaId.startsWith("minecraft:potted_") || javaId.equals("minecraft:flower_pot")) {
|
||||
String name = javaId.replace("potted_", "");
|
||||
if (name.contains("azalea")) {
|
||||
// Exception to the rule
|
||||
name = name.replace("_bush", "");
|
||||
}
|
||||
FLOWER_POT_VALUES.put(javaBlockState, name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (javaId.startsWith("minecraft:lectern")) {
|
||||
LECTERN_BOOK_STATES.put(javaBlockState, javaId.contains("has_book=true"));
|
||||
return;
|
||||
}
|
||||
|
||||
JsonNode notePitch = blockData.get("note_pitch");
|
||||
if (notePitch != null) {
|
||||
NOTEBLOCK_PITCHES.put(javaBlockState, blockData.get("note_pitch").intValue());
|
||||
return;
|
||||
}
|
||||
|
||||
if (javaId.contains("piston[")) { // minecraft:moving_piston, minecraft:sticky_piston, minecraft:piston
|
||||
if (javaId.startsWith("minecraft:moving_piston")) {
|
||||
MOVING_PISTONS.add(javaBlockState);
|
||||
} else {
|
||||
PISTON_VALUES.put(javaBlockState, javaId.contains("extended=true"));
|
||||
}
|
||||
if (javaId.contains("sticky")) {
|
||||
STICKY_PISTONS.add(javaBlockState);
|
||||
}
|
||||
PISTON_ORIENTATION.put(javaBlockState, getBlockDirection(javaId));
|
||||
return;
|
||||
} else if (javaId.startsWith("minecraft:piston_head")) {
|
||||
ALL_PISTON_HEADS.add(javaBlockState);
|
||||
if (javaId.contains("short=false")) {
|
||||
PISTON_HEADS.put(getBlockDirection(javaId), javaBlockState);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
JsonNode skullVariation = blockData.get("variation");
|
||||
if (skullVariation != null) {
|
||||
SKULL_VARIANTS.put(javaBlockState, (byte) skullVariation.intValue());
|
||||
}
|
||||
|
||||
JsonNode skullRotation = blockData.get("skull_rotation");
|
||||
if (skullRotation != null) {
|
||||
SKULL_ROTATIONS.put(javaBlockState, (byte) skullRotation.intValue());
|
||||
}
|
||||
|
||||
if (javaId.startsWith("minecraft:dragon_head[") || javaId.startsWith("minecraft:piglin_head[")
|
||||
|| javaId.startsWith("minecraft:dragon_wall_head[") || javaId.startsWith("minecraft:piglin_wall_head[")) {
|
||||
if (javaId.contains("powered=true")) {
|
||||
SKULL_POWERED.add(javaBlockState);
|
||||
}
|
||||
}
|
||||
|
||||
if (javaId.contains("wall_skull") || javaId.contains("wall_head")) {
|
||||
String direction = javaId.substring(javaId.lastIndexOf("facing=") + 7, javaId.lastIndexOf("powered=") - 1);
|
||||
int rotation = switch (direction) {
|
||||
case "north" -> 180;
|
||||
case "west" -> 90;
|
||||
case "east" -> 270;
|
||||
default -> 0; // Also south
|
||||
};
|
||||
SKULL_WALL_DIRECTIONS.put(javaBlockState, rotation);
|
||||
}
|
||||
|
||||
JsonNode shulkerDirection = blockData.get("shulker_direction");
|
||||
if (shulkerDirection != null) {
|
||||
BlockStateValues.SHULKERBOX_DIRECTIONS.put(javaBlockState, (byte) shulkerDirection.intValue());
|
||||
return;
|
||||
}
|
||||
|
||||
if (javaId.startsWith("minecraft:water") && !javaId.contains("cauldron")) {
|
||||
String strLevel = javaId.substring(javaId.lastIndexOf("level=") + 6, javaId.length() - 1);
|
||||
int level = Integer.parseInt(strLevel);
|
||||
WATER_LEVEL.put(javaBlockState, level);
|
||||
return;
|
||||
}
|
||||
|
||||
if (javaId.startsWith("minecraft:jigsaw[orientation=")) {
|
||||
String blockStateData = javaId.substring(javaId.indexOf("orientation=") + "orientation=".length(), javaId.lastIndexOf('_'));
|
||||
Direction direction = Direction.valueOf(blockStateData.toUpperCase(Locale.ROOT));
|
||||
if (direction.isHorizontal()) {
|
||||
HORIZONTAL_FACING_JIGSAWS.add(javaBlockState);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (javaId.contains("cauldron")) {
|
||||
ALL_CAULDRONS.add(javaBlockState);
|
||||
}
|
||||
if (javaId.contains("_cauldron") && !javaId.contains("water_")) {
|
||||
NON_WATER_CAULDRONS.add(javaBlockState);
|
||||
}
|
||||
|
||||
if (javaId.contains("_door[") && javaId.contains("half=upper")) {
|
||||
UPPER_DOORS.add(javaBlockState);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Banner colors are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock.
|
||||
* This gives an integer color that Bedrock can use.
|
||||
*
|
||||
* @param state BlockState of the block
|
||||
* @return Banner color integer or -1 if no color
|
||||
*/
|
||||
public static int getBannerColor(int state) {
|
||||
return BANNER_COLORS.getOrDefault(state, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bed colors are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock.
|
||||
* This gives a byte color that Bedrock can use - Bedrock needs a byte in the final tag.
|
||||
*
|
||||
* @param state BlockState of the block
|
||||
* @return Bed color byte or -1 if no color
|
||||
*/
|
||||
public static byte getBedColor(int state) {
|
||||
return BED_COLORS.getOrDefault(state, (byte) -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* The brush progress of suspicious sand/gravel is not sent by the java server when it updates the block entity.
|
||||
* Although brush progress is part of the bedrock block state, it must be included in the block entity update.
|
||||
*
|
||||
* @param state BlockState of the block
|
||||
* @return brush progress or 0 if the lookup failed
|
||||
*/
|
||||
public static int getBrushProgress(int state) {
|
||||
return BRUSH_PROGRESS.getOrDefault(state, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return if this Java block state is a non-empty non-water cauldron
|
||||
*/
|
||||
public static boolean isNonWaterCauldron(int state) {
|
||||
return NON_WATER_CAULDRONS.contains(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cauldrons (since Bedrock 1.18.30) must have a block entity packet sent on chunk load to fix rendering issues.
|
||||
* <p>
|
||||
* When using a bucket on a cauldron sending a ServerboundUseItemPacket can result in the liquid being placed.
|
||||
*
|
||||
* @return if this Java block state is a cauldron
|
||||
*/
|
||||
public static boolean isCauldron(int state) {
|
||||
return ALL_CAULDRONS.contains(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* The block state in Java and Bedrock both contain the conditional bit, however command block block entity tags
|
||||
* in Bedrock need the conditional information.
|
||||
*
|
||||
* @return the list of all command blocks and if they are conditional (1 or 0)
|
||||
*/
|
||||
public static Int2ByteMap getCommandBlockValues() {
|
||||
return COMMAND_BLOCK_VALUES;
|
||||
}
|
||||
|
||||
/**
|
||||
* All double chest values are part of the block state in Java and part of the block entity tag in Bedrock.
|
||||
* This gives the DoubleChestValue that can be calculated into the final tag.
|
||||
*
|
||||
* @return The map of all DoubleChestValues.
|
||||
*/
|
||||
public static Int2ObjectMap<DoubleChestValue> getDoubleChestValues() {
|
||||
return DOUBLE_CHEST_VALUES;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Int2ObjectMap of flower pot block states to containing plant
|
||||
*
|
||||
* @return Int2ObjectMap of flower pot values
|
||||
*/
|
||||
public static Int2ObjectMap<String> getFlowerPotValues() {
|
||||
return FLOWER_POT_VALUES;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a set of all forward-facing jigsaws, to use as a fallback if NBT is missing.
|
||||
*/
|
||||
public static IntSet getHorizontalFacingJigsaws() {
|
||||
return HORIZONTAL_FACING_JIGSAWS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the lectern book state map pointing to book present state
|
||||
*/
|
||||
public static LecternHasBookMap getLecternBookStates() {
|
||||
return LECTERN_BOOK_STATES;
|
||||
}
|
||||
|
||||
/**
|
||||
* The note that noteblocks output when hit is part of the block state in Java but sent as a BlockEventPacket in Bedrock.
|
||||
* This gives an integer pitch that Bedrock can use.
|
||||
*
|
||||
* @param state BlockState of the block
|
||||
* @return note block note integer or -1 if not present
|
||||
*/
|
||||
public static int getNoteblockPitch(int state) {
|
||||
return NOTEBLOCK_PITCHES.getOrDefault(state, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Int2BooleanMap showing if a piston block state is extended or not.
|
||||
*
|
||||
* @return the Int2BooleanMap of piston extensions.
|
||||
*/
|
||||
public static Int2BooleanMap getPistonValues() {
|
||||
return PISTON_VALUES;
|
||||
}
|
||||
|
||||
public static boolean isStickyPiston(int blockState) {
|
||||
return STICKY_PISTONS.contains(blockState);
|
||||
}
|
||||
|
||||
public static boolean isPistonHead(int state) {
|
||||
return ALL_PISTON_HEADS.contains(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Java Block State for a piston head for a specific direction
|
||||
* This is used in PistonBlockEntity to get the BlockCollision for the piston head.
|
||||
*
|
||||
* @param direction Direction the piston head points in
|
||||
* @return Block state for the piston head
|
||||
*/
|
||||
public static int getPistonHead(Direction direction) {
|
||||
return PISTON_HEADS.getOrDefault(direction, BlockStateValues.JAVA_AIR_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a block is a minecraft:moving_piston
|
||||
* This is used in ChunkUtils to prevent them from being placed as it causes
|
||||
* pistons to flicker and it is not needed
|
||||
*
|
||||
* @param state Block state of the block
|
||||
* @return True if the block is a moving_piston
|
||||
*/
|
||||
public static boolean isMovingPiston(int state) {
|
||||
return MOVING_PISTONS.contains(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is used in GeyserPistonEvents.java and accepts minecraft:piston,
|
||||
* minecraft:sticky_piston, and minecraft:moving_piston.
|
||||
*
|
||||
* @param state The block state of the piston base
|
||||
* @return The direction in which the piston faces
|
||||
*/
|
||||
public static Direction getPistonOrientation(int state) {
|
||||
return PISTON_ORIENTATION.get(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a block sticks to other blocks
|
||||
* (Slime and honey blocks)
|
||||
@ -389,8 +45,9 @@ public final class BlockStateValues {
|
||||
* @param state The block state
|
||||
* @return True if the block sticks to adjacent blocks
|
||||
*/
|
||||
public static boolean isBlockSticky(int state) {
|
||||
return state == JAVA_SLIME_BLOCK_ID || state == JAVA_HONEY_BLOCK_ID;
|
||||
public static boolean isBlockSticky(BlockState state) {
|
||||
Block block = state.block();
|
||||
return block == Blocks.SLIME_BLOCK || block == Blocks.HONEY_BLOCK;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -400,13 +57,13 @@ public final class BlockStateValues {
|
||||
* @param stateB The block state of block b
|
||||
* @return True if the blocks are attached to each other
|
||||
*/
|
||||
public static boolean isBlockAttached(int stateA, int stateB) {
|
||||
public static boolean isBlockAttached(BlockState stateA, BlockState stateB) {
|
||||
boolean aSticky = isBlockSticky(stateA);
|
||||
boolean bSticky = isBlockSticky(stateB);
|
||||
if (aSticky && bSticky) {
|
||||
// Only matching sticky blocks are attached together
|
||||
// Honey + Honey & Slime + Slime
|
||||
return stateA == stateB;
|
||||
return stateA.block() == stateB.block();
|
||||
}
|
||||
return aSticky || bSticky;
|
||||
}
|
||||
@ -415,84 +72,33 @@ public final class BlockStateValues {
|
||||
* @param state The block state of the block
|
||||
* @return true if a piston can break the block
|
||||
*/
|
||||
public static boolean canPistonDestroyBlock(int state) {
|
||||
return BlockRegistries.JAVA_BLOCKS.getOrDefault(state, BlockMapping.DEFAULT).getPistonBehavior() == PistonBehavior.DESTROY;
|
||||
public static boolean canPistonDestroyBlock(BlockState state) {
|
||||
return state.block().pushReaction() == PistonBehavior.DESTROY;
|
||||
}
|
||||
|
||||
public static boolean canPistonMoveBlock(int javaId, boolean isPushing) {
|
||||
if (javaId == JAVA_AIR_ID) {
|
||||
public static boolean canPistonMoveBlock(BlockState state, boolean isPushing) {
|
||||
Block block = state.block();
|
||||
if (block == Blocks.AIR) {
|
||||
return true;
|
||||
}
|
||||
// Pistons can only be moved if they aren't extended
|
||||
if (PistonBlockEntityTranslator.isBlock(javaId)) {
|
||||
return !PISTON_VALUES.get(javaId);
|
||||
}
|
||||
BlockMapping block = BlockRegistries.JAVA_BLOCKS.getOrDefault(javaId, BlockMapping.DEFAULT);
|
||||
// Bedrock, End portal frames, etc. can't be moved
|
||||
if (block.getHardness() == -1.0d) {
|
||||
if (block == Blocks.OBSIDIAN || block == Blocks.CRYING_OBSIDIAN || block == Blocks.RESPAWN_ANCHOR || block == Blocks.REINFORCED_DEEPSLATE) { // Hardcoded as of 1.20.5
|
||||
return false;
|
||||
}
|
||||
return switch (block.getPistonBehavior()) {
|
||||
// Pistons can only be moved if they aren't extended
|
||||
if (block instanceof PistonBlock) {
|
||||
return !state.getValue(Properties.EXTENDED);
|
||||
}
|
||||
// Bedrock, End portal frames, etc. can't be moved
|
||||
if (block.destroyTime() == -1.0f) {
|
||||
return false;
|
||||
}
|
||||
return switch (block.pushReaction()) {
|
||||
case BLOCK, DESTROY -> false;
|
||||
case PUSH_ONLY -> isPushing; // Glazed terracotta can only be pushed
|
||||
default -> !block.isBlockEntity(); // Pistons can't move block entities
|
||||
default -> !block.hasBlockEntity(); // Pistons can't move block entities
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Skull variations are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock.
|
||||
* This gives a byte variant ID that Bedrock can use.
|
||||
*
|
||||
* @param state BlockState of the block
|
||||
* @return Skull variant byte or -1 if no variant
|
||||
*/
|
||||
public static byte getSkullVariant(int state) {
|
||||
return SKULL_VARIANTS.getOrDefault(state, (byte) -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Skull rotations are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock.
|
||||
* This gives a byte rotation that Bedrock can use.
|
||||
*
|
||||
* @param state BlockState of the block
|
||||
* @return Skull rotation value or -1 if no value
|
||||
*/
|
||||
public static byte getSkullRotation(int state) {
|
||||
return SKULL_ROTATIONS.getOrDefault(state, (byte) -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* As of Java 1.20.2:
|
||||
* Skull powered states are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock.
|
||||
*
|
||||
* @param state BlockState of the block
|
||||
* @return true if this skull is currently being powered.
|
||||
*/
|
||||
public static boolean isSkullPowered(int state) {
|
||||
return SKULL_POWERED.contains(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Skull rotations are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock.
|
||||
* This gives a integer rotation that Bedrock can use.
|
||||
*
|
||||
* @return Skull wall rotation value with the blockstate
|
||||
*/
|
||||
public static Int2IntMap getSkullWallDirections() {
|
||||
return SKULL_WALL_DIRECTIONS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shulker box directions are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock.
|
||||
* This gives a byte direction that Bedrock can use.
|
||||
*
|
||||
* @param state BlockState of the block
|
||||
* @return Shulker direction value or -1 if no value
|
||||
*/
|
||||
public static byte getShulkerBoxDirection(int state) {
|
||||
return SHULKERBOX_DIRECTIONS.getOrDefault(state, (byte) -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the level of water from the block state.
|
||||
*
|
||||
@ -500,17 +106,11 @@ public final class BlockStateValues {
|
||||
* @return The water level or -1 if the block isn't water
|
||||
*/
|
||||
public static int getWaterLevel(int state) {
|
||||
return WATER_LEVEL.getOrDefault(state, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a block is the upper half of a door.
|
||||
*
|
||||
* @param state BlockState of the block
|
||||
* @return True if the block is the upper half of a door
|
||||
*/
|
||||
public static boolean isUpperDoor(int state) {
|
||||
return UPPER_DOORS.contains(state);
|
||||
BlockState blockState = BlockState.of(state);
|
||||
if (!blockState.is(Blocks.WATER)) {
|
||||
return -1;
|
||||
}
|
||||
return blockState.getValue(Properties.LEVEL);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -544,31 +144,18 @@ public final class BlockStateValues {
|
||||
* @param state BlockState of the block
|
||||
* @return The block's slipperiness
|
||||
*/
|
||||
public static float getSlipperiness(int state) {
|
||||
String blockIdentifier = BlockRegistries.JAVA_BLOCKS.getOrDefault(state, BlockMapping.DEFAULT).getJavaIdentifier();
|
||||
return switch (blockIdentifier) {
|
||||
case "minecraft:slime_block" -> 0.8f;
|
||||
case "minecraft:ice", "minecraft:packed_ice" -> 0.98f;
|
||||
case "minecraft:blue_ice" -> 0.989f;
|
||||
default -> 0.6f;
|
||||
};
|
||||
}
|
||||
|
||||
private static Direction getBlockDirection(String javaId) {
|
||||
if (javaId.contains("down")) {
|
||||
return Direction.DOWN;
|
||||
} else if (javaId.contains("up")) {
|
||||
return Direction.UP;
|
||||
} else if (javaId.contains("south")) {
|
||||
return Direction.SOUTH;
|
||||
} else if (javaId.contains("west")) {
|
||||
return Direction.WEST;
|
||||
} else if (javaId.contains("north")) {
|
||||
return Direction.NORTH;
|
||||
} else if (javaId.contains("east")) {
|
||||
return Direction.EAST;
|
||||
public static float getSlipperiness(BlockState state) {
|
||||
Block block = state.block();
|
||||
if (block == Blocks.SLIME_BLOCK) {
|
||||
return 0.8f;
|
||||
}
|
||||
throw new IllegalStateException();
|
||||
if (block == Blocks.ICE || block == Blocks.PACKED_ICE) {
|
||||
return 0.98f;
|
||||
}
|
||||
if (block == Blocks.BLUE_ICE) {
|
||||
return 0.989f;
|
||||
}
|
||||
return 0.6f;
|
||||
}
|
||||
|
||||
private BlockStateValues() {
|
||||
|
2823
core/src/main/java/org/geysermc/geyser/level/block/Blocks.java
Normale Datei
2823
core/src/main/java/org/geysermc/geyser/level/block/Blocks.java
Normale Datei
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.level.block.property;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents enums we don't need classes for in Geyser.
|
||||
*/
|
||||
public final class BasicEnumProperty extends Property<String> {
|
||||
private final List<String> values;
|
||||
|
||||
private BasicEnumProperty(String name, List<String> values) {
|
||||
super(name);
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int valuesCount() {
|
||||
return this.values.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int indexOf(String value) {
|
||||
int index = this.values.indexOf(value);
|
||||
if (index == -1) {
|
||||
throw new IllegalArgumentException("Property " + this + " does not have value " + value);
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T values() {
|
||||
return (T) this.values;
|
||||
}
|
||||
|
||||
public static BasicEnumProperty create(String name, String... values) {
|
||||
return new BasicEnumProperty(name, List.of(values));
|
||||
}
|
||||
}
|
@ -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,24 @@
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
package org.geysermc.geyser.util.collection;
|
||||
package org.geysermc.geyser.level.block.property;
|
||||
|
||||
public final class BooleanProperty extends Property<Boolean> {
|
||||
private BooleanProperty(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int valuesCount() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int indexOf(Boolean value) {
|
||||
return value ? 0 : 1;
|
||||
}
|
||||
|
||||
public static BooleanProperty create(String name) {
|
||||
return new BooleanProperty(name);
|
||||
}
|
||||
}
|
@ -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,12 @@
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.level.block;
|
||||
package org.geysermc.geyser.level.block.property;
|
||||
|
||||
/**
|
||||
* This stores all values of double chests that are part of the Java block state.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
public record DoubleChestValue(
|
||||
boolean isFacingEast,
|
||||
boolean isDirectionPositive,
|
||||
boolean isLeft) {
|
||||
public enum ChestType {
|
||||
SINGLE,
|
||||
LEFT,
|
||||
RIGHT;
|
||||
|
||||
public static final ChestType[] VALUES = values();
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2024 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.level.block.property;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
|
||||
public final class EnumProperty<T extends Enum<T>> extends Property<T> {
|
||||
private final IntList ordinalValues;
|
||||
|
||||
/**
|
||||
* @param values all possible values of this enum.
|
||||
*/
|
||||
private EnumProperty(String name, T[] values) {
|
||||
super(name);
|
||||
this.ordinalValues = new IntArrayList(values.length);
|
||||
for (T anEnum : values) {
|
||||
this.ordinalValues.add(anEnum.ordinal());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int valuesCount() {
|
||||
return this.ordinalValues.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int indexOf(T value) {
|
||||
return this.ordinalValues.indexOf(value.ordinal());
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public static <T extends Enum<T>> EnumProperty<T> create(String name, T... values) {
|
||||
return new EnumProperty<>(name, values);
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.level.block.property;
|
||||
|
||||
import org.geysermc.geyser.level.physics.Direction;
|
||||
|
||||
public enum FrontAndTop {
|
||||
DOWN_EAST(Direction.DOWN),
|
||||
DOWN_NORTH(Direction.DOWN),
|
||||
DOWN_SOUTH(Direction.DOWN),
|
||||
DOWN_WEST(Direction.DOWN),
|
||||
UP_EAST(Direction.UP),
|
||||
UP_NORTH(Direction.UP),
|
||||
UP_SOUTH(Direction.UP),
|
||||
UP_WEST(Direction.UP),
|
||||
WEST_UP(Direction.WEST),
|
||||
EAST_UP(Direction.EAST),
|
||||
NORTH_UP(Direction.NORTH),
|
||||
SOUTH_UP(Direction.SOUTH);
|
||||
|
||||
private final boolean horizontal;
|
||||
|
||||
FrontAndTop(Direction front) {
|
||||
this.horizontal = front.isHorizontal();
|
||||
}
|
||||
|
||||
public boolean isHorizontal() {
|
||||
return horizontal;
|
||||
}
|
||||
|
||||
public static final FrontAndTop[] VALUES = values();
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2024 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.level.block.property;
|
||||
|
||||
public final class IntegerProperty extends Property<Integer> {
|
||||
private final int offset;
|
||||
private final int valuesCount;
|
||||
|
||||
private IntegerProperty(String name, int low, int high) {
|
||||
super(name);
|
||||
this.offset = low;
|
||||
this.valuesCount = high - low;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int valuesCount() {
|
||||
return this.valuesCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int indexOf(Integer value) {
|
||||
return value - this.offset;
|
||||
}
|
||||
|
||||
public int low() {
|
||||
return this.offset;
|
||||
}
|
||||
|
||||
public int high() {
|
||||
return this.offset + this.valuesCount;
|
||||
}
|
||||
|
||||
public static IntegerProperty create(String name, int low, int high) {
|
||||
return new IntegerProperty(name, low, high);
|
||||
}
|
||||
}
|
@ -0,0 +1,146 @@
|
||||
/*
|
||||
* 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.level.block.property;
|
||||
|
||||
import org.geysermc.geyser.level.physics.Axis;
|
||||
import org.geysermc.geyser.level.physics.Direction;
|
||||
|
||||
public final class Properties {
|
||||
public static final BooleanProperty ATTACHED = BooleanProperty.create("attached");
|
||||
public static final BooleanProperty BOTTOM = BooleanProperty.create("bottom");
|
||||
public static final BooleanProperty CONDITIONAL = BooleanProperty.create("conditional");
|
||||
public static final BooleanProperty DISARMED = BooleanProperty.create("disarmed");
|
||||
public static final BooleanProperty DRAG = BooleanProperty.create("drag");
|
||||
public static final BooleanProperty ENABLED = BooleanProperty.create("enabled");
|
||||
public static final BooleanProperty EXTENDED = BooleanProperty.create("extended");
|
||||
public static final BooleanProperty EYE = BooleanProperty.create("eye");
|
||||
public static final BooleanProperty FALLING = BooleanProperty.create("falling");
|
||||
public static final BooleanProperty HANGING = BooleanProperty.create("hanging");
|
||||
public static final BooleanProperty HAS_BOTTLE_0 = BooleanProperty.create("has_bottle_0");
|
||||
public static final BooleanProperty HAS_BOTTLE_1 = BooleanProperty.create("has_bottle_1");
|
||||
public static final BooleanProperty HAS_BOTTLE_2 = BooleanProperty.create("has_bottle_2");
|
||||
public static final BooleanProperty HAS_RECORD = BooleanProperty.create("has_record");
|
||||
public static final BooleanProperty HAS_BOOK = BooleanProperty.create("has_book");
|
||||
public static final BooleanProperty INVERTED = BooleanProperty.create("inverted");
|
||||
public static final BooleanProperty IN_WALL = BooleanProperty.create("in_wall");
|
||||
public static final BooleanProperty LIT = BooleanProperty.create("lit");
|
||||
public static final BooleanProperty LOCKED = BooleanProperty.create("locked");
|
||||
public static final BooleanProperty OCCUPIED = BooleanProperty.create("occupied");
|
||||
public static final BooleanProperty OPEN = BooleanProperty.create("open");
|
||||
public static final BooleanProperty PERSISTENT = BooleanProperty.create("persistent");
|
||||
public static final BooleanProperty POWERED = BooleanProperty.create("powered");
|
||||
public static final BooleanProperty SHORT = BooleanProperty.create("short");
|
||||
public static final BooleanProperty SIGNAL_FIRE = BooleanProperty.create("signal_fire");
|
||||
public static final BooleanProperty SNOWY = BooleanProperty.create("snowy");
|
||||
public static final BooleanProperty TRIGGERED = BooleanProperty.create("triggered");
|
||||
public static final BooleanProperty UNSTABLE = BooleanProperty.create("unstable");
|
||||
public static final BooleanProperty WATERLOGGED = BooleanProperty.create("waterlogged");
|
||||
public static final BooleanProperty BERRIES = BooleanProperty.create("berries");
|
||||
public static final BooleanProperty BLOOM = BooleanProperty.create("bloom");
|
||||
public static final BooleanProperty SHRIEKING = BooleanProperty.create("shrieking");
|
||||
public static final BooleanProperty CAN_SUMMON = BooleanProperty.create("can_summon");
|
||||
public static final EnumProperty<Axis> HORIZONTAL_AXIS = EnumProperty.create("axis", Axis.X, Axis.Z);
|
||||
public static final EnumProperty<Axis> AXIS = EnumProperty.create("axis", Axis.VALUES);
|
||||
public static final BooleanProperty UP = BooleanProperty.create("up");
|
||||
public static final BooleanProperty DOWN = BooleanProperty.create("down");
|
||||
public static final BooleanProperty NORTH = BooleanProperty.create("north");
|
||||
public static final BooleanProperty EAST = BooleanProperty.create("east");
|
||||
public static final BooleanProperty SOUTH = BooleanProperty.create("south");
|
||||
public static final BooleanProperty WEST = BooleanProperty.create("west");
|
||||
public static final EnumProperty<Direction> FACING = EnumProperty.create("facing", Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN);
|
||||
public static final EnumProperty<Direction> FACING_HOPPER = EnumProperty.create("facing", Direction.DOWN, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST);
|
||||
public static final EnumProperty<Direction> HORIZONTAL_FACING = EnumProperty.create("facing", Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST);
|
||||
public static final IntegerProperty FLOWER_AMOUNT = IntegerProperty.create("flower_amount", 1, 4);
|
||||
public static final EnumProperty<FrontAndTop> ORIENTATION = EnumProperty.create("orientation", FrontAndTop.VALUES);
|
||||
public static final BasicEnumProperty ATTACH_FACE = BasicEnumProperty.create("face", "floor", "wall", "ceiling");
|
||||
public static final BasicEnumProperty BELL_ATTACHMENT = BasicEnumProperty.create("attachment", "floor", "ceiling", "single_wall", "double_wall");
|
||||
public static final BasicEnumProperty EAST_WALL = BasicEnumProperty.create("east", "none", "low", "tall");
|
||||
public static final BasicEnumProperty NORTH_WALL = BasicEnumProperty.create("north", "none", "low", "tall");
|
||||
public static final BasicEnumProperty SOUTH_WALL = BasicEnumProperty.create("south", "none", "low", "tall");
|
||||
public static final BasicEnumProperty WEST_WALL = BasicEnumProperty.create("west", "none", "low", "tall");
|
||||
public static final BasicEnumProperty EAST_REDSTONE = BasicEnumProperty.create("east", "up", "side", "none");
|
||||
public static final BasicEnumProperty NORTH_REDSTONE = BasicEnumProperty.create("north", "up", "side", "none");
|
||||
public static final BasicEnumProperty SOUTH_REDSTONE = BasicEnumProperty.create("south", "up", "side", "none");
|
||||
public static final BasicEnumProperty WEST_REDSTONE = BasicEnumProperty.create("west", "up", "side", "none");
|
||||
public static final BasicEnumProperty DOUBLE_BLOCK_HALF = BasicEnumProperty.create("half", "upper", "lower");
|
||||
public static final BasicEnumProperty HALF = BasicEnumProperty.create("half", "top", "bottom");
|
||||
public static final BasicEnumProperty RAIL_SHAPE = BasicEnumProperty.create("shape", "north_south", "east_west", "ascending_east", "ascending_west", "ascending_north", "ascending_south", "south_east", "south_west", "north_west", "north_east");
|
||||
public static final BasicEnumProperty RAIL_SHAPE_STRAIGHT = BasicEnumProperty.create("shape", "north_south", "east_west", "ascending_east", "ascending_west", "ascending_north", "ascending_south");
|
||||
public static final IntegerProperty AGE_1 = IntegerProperty.create("age", 0, 1);
|
||||
public static final IntegerProperty AGE_2 = IntegerProperty.create("age", 0, 2);
|
||||
public static final IntegerProperty AGE_3 = IntegerProperty.create("age", 0, 3);
|
||||
public static final IntegerProperty AGE_4 = IntegerProperty.create("age", 0, 4);
|
||||
public static final IntegerProperty AGE_5 = IntegerProperty.create("age", 0, 5);
|
||||
public static final IntegerProperty AGE_7 = IntegerProperty.create("age", 0, 7);
|
||||
public static final IntegerProperty AGE_15 = IntegerProperty.create("age", 0, 15);
|
||||
public static final IntegerProperty AGE_25 = IntegerProperty.create("age", 0, 25);
|
||||
public static final IntegerProperty BITES = IntegerProperty.create("bites", 0, 6);
|
||||
public static final IntegerProperty CANDLES = IntegerProperty.create("candles", 1, 4);
|
||||
public static final IntegerProperty DELAY = IntegerProperty.create("delay", 1, 4);
|
||||
public static final IntegerProperty DISTANCE = IntegerProperty.create("distance", 1, 7);
|
||||
public static final IntegerProperty EGGS = IntegerProperty.create("eggs", 1, 4);
|
||||
public static final IntegerProperty HATCH = IntegerProperty.create("hatch", 0, 2);
|
||||
public static final IntegerProperty LAYERS = IntegerProperty.create("layers", 1, 8);
|
||||
public static final IntegerProperty LEVEL_CAULDRON = IntegerProperty.create("level", 1, 3);
|
||||
public static final IntegerProperty LEVEL_COMPOSTER = IntegerProperty.create("level", 0, 8);
|
||||
public static final IntegerProperty LEVEL_FLOWING = IntegerProperty.create("level", 1, 8);
|
||||
public static final IntegerProperty LEVEL_HONEY = IntegerProperty.create("honey_level", 0, 5);
|
||||
public static final IntegerProperty LEVEL = IntegerProperty.create("level", 0, 15);
|
||||
public static final IntegerProperty MOISTURE = IntegerProperty.create("moisture", 0, 7);
|
||||
public static final IntegerProperty NOTE = IntegerProperty.create("note", 0, 24);
|
||||
public static final IntegerProperty PICKLES = IntegerProperty.create("pickles", 1, 4);
|
||||
public static final IntegerProperty POWER = IntegerProperty.create("power", 0, 15);
|
||||
public static final IntegerProperty STAGE = IntegerProperty.create("stage", 0, 1);
|
||||
public static final IntegerProperty STABILITY_DISTANCE = IntegerProperty.create("distance", 0, 7);
|
||||
public static final IntegerProperty RESPAWN_ANCHOR_CHARGES = IntegerProperty.create("charges", 0, 4);
|
||||
public static final IntegerProperty ROTATION_16 = IntegerProperty.create("rotation", 0, 15);
|
||||
public static final BasicEnumProperty BED_PART = BasicEnumProperty.create("part", "head", "foot");
|
||||
public static final EnumProperty<ChestType> CHEST_TYPE = EnumProperty.create("type", ChestType.VALUES);
|
||||
public static final BasicEnumProperty MODE_COMPARATOR = BasicEnumProperty.create("mode", "compare", "subtract");
|
||||
public static final BasicEnumProperty DOOR_HINGE = BasicEnumProperty.create("hinge", "left", "right");
|
||||
public static final BasicEnumProperty NOTEBLOCK_INSTRUMENT = BasicEnumProperty.create("instrument", "harp", "basedrum", "snare", "hat", "bass", "flute", "bell", "guitar", "chime", "xylophone", "iron_xylophone", "cow_bell", "didgeridoo", "bit", "banjo", "pling", "zombie", "skeleton", "creeper", "dragon", "wither_skeleton", "piglin", "custom_head");
|
||||
public static final BasicEnumProperty PISTON_TYPE = BasicEnumProperty.create("type", "normal", "sticky");
|
||||
public static final BasicEnumProperty SLAB_TYPE = BasicEnumProperty.create("type", "top", "bottom", "double");
|
||||
public static final BasicEnumProperty STAIRS_SHAPE = BasicEnumProperty.create("shape", "straight", "inner_left", "inner_right", "outer_left", "outer_right");
|
||||
public static final BasicEnumProperty STRUCTUREBLOCK_MODE = BasicEnumProperty.create("mode", "save", "load", "corner", "data");
|
||||
public static final BasicEnumProperty BAMBOO_LEAVES = BasicEnumProperty.create("leaves", "none", "small", "large");
|
||||
public static final BasicEnumProperty TILT = BasicEnumProperty.create("tilt", "none", "unstable", "partial", "full");
|
||||
public static final EnumProperty<Direction> VERTICAL_DIRECTION = EnumProperty.create("vertical_direction", Direction.UP, Direction.DOWN);
|
||||
public static final BasicEnumProperty DRIPSTONE_THICKNESS = BasicEnumProperty.create("thickness", "tip_merge", "tip", "frustum", "middle", "base");
|
||||
public static final BasicEnumProperty SCULK_SENSOR_PHASE = BasicEnumProperty.create("sculk_sensor_phase", "inactive", "active", "cooldown");
|
||||
public static final BooleanProperty CHISELED_BOOKSHELF_SLOT_0_OCCUPIED = BooleanProperty.create("slot_0_occupied");
|
||||
public static final BooleanProperty CHISELED_BOOKSHELF_SLOT_1_OCCUPIED = BooleanProperty.create("slot_1_occupied");
|
||||
public static final BooleanProperty CHISELED_BOOKSHELF_SLOT_2_OCCUPIED = BooleanProperty.create("slot_2_occupied");
|
||||
public static final BooleanProperty CHISELED_BOOKSHELF_SLOT_3_OCCUPIED = BooleanProperty.create("slot_3_occupied");
|
||||
public static final BooleanProperty CHISELED_BOOKSHELF_SLOT_4_OCCUPIED = BooleanProperty.create("slot_4_occupied");
|
||||
public static final BooleanProperty CHISELED_BOOKSHELF_SLOT_5_OCCUPIED = BooleanProperty.create("slot_5_occupied");
|
||||
public static final IntegerProperty DUSTED = IntegerProperty.create("dusted", 0, 3);
|
||||
public static final BooleanProperty CRACKED = BooleanProperty.create("cracked");
|
||||
public static final BooleanProperty CRAFTING = BooleanProperty.create("crafting");
|
||||
public static final BasicEnumProperty TRIAL_SPAWNER_STATE = BasicEnumProperty.create("trial_spawner_state", "inactive", "waiting_for_players", "active", "waiting_for_reward_ejection", "ejecting_reward", "cooldown");
|
||||
public static final BasicEnumProperty VAULT_STATE = BasicEnumProperty.create("vault_state", "inactive", "active", "unlocking", "ejecting");
|
||||
public static final BooleanProperty OMINOUS = BooleanProperty.create("ominous");
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.level.block.property;
|
||||
|
||||
public abstract class Property<T extends Comparable<T>> {
|
||||
private final String name;
|
||||
|
||||
protected Property(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public abstract int valuesCount();
|
||||
|
||||
public abstract int indexOf(T value);
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + "[" + name + "]";
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2024 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.level.block.type;
|
||||
|
||||
public class BannerBlock extends Block {
|
||||
private final int dyeColor;
|
||||
|
||||
public BannerBlock(String javaIdentifier, int dyeColor, Builder builder) {
|
||||
super(javaIdentifier, builder);
|
||||
this.dyeColor = dyeColor;
|
||||
}
|
||||
|
||||
public int dyeColor() {
|
||||
return dyeColor;
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2024 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.level.block.type;
|
||||
|
||||
public class BedBlock extends Block {
|
||||
private final int dyeColor;
|
||||
|
||||
public BedBlock(String javaIdentifier, int dyeColor, Builder builder) {
|
||||
super(javaIdentifier, builder);
|
||||
this.dyeColor = dyeColor;
|
||||
}
|
||||
|
||||
public int dyeColor() {
|
||||
return dyeColor;
|
||||
}
|
||||
}
|
355
core/src/main/java/org/geysermc/geyser/level/block/type/Block.java
Normale Datei
355
core/src/main/java/org/geysermc/geyser/level/block/type/Block.java
Normale Datei
@ -0,0 +1,355 @@
|
||||
/*
|
||||
* 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.level.block.type;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.UpdateBlockPacket;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.level.block.Blocks;
|
||||
import org.geysermc.geyser.level.block.property.BasicEnumProperty;
|
||||
import org.geysermc.geyser.level.block.property.IntegerProperty;
|
||||
import org.geysermc.geyser.level.block.property.Property;
|
||||
import org.geysermc.geyser.level.physics.PistonBehavior;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
|
||||
import org.intellij.lang.annotations.Subst;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Block {
|
||||
public static final int JAVA_AIR_ID = 0;
|
||||
|
||||
private final Key javaIdentifier;
|
||||
/**
|
||||
* Can you harvest this with your hand.
|
||||
*/
|
||||
private final boolean requiresCorrectToolForDrops;
|
||||
private final boolean hasBlockEntity;
|
||||
private final float destroyTime;
|
||||
private final @NonNull PistonBehavior pushReaction;
|
||||
/**
|
||||
* Used for classes we don't have implemented yet that override Mojmap getCloneItemStack with their own item.
|
||||
* A supplier prevents any issues arising where the Items class finishes before the Blocks class.
|
||||
*/
|
||||
private final Supplier<Item> pickItem;
|
||||
protected Item item = null;
|
||||
private int javaId = -1;
|
||||
|
||||
/**
|
||||
* Used for switching a given block state to different states.
|
||||
*/
|
||||
private final Property<?>[] propertyKeys;
|
||||
private final BlockState defaultState;
|
||||
|
||||
public Block(@Subst("empty") String javaIdentifier, Builder builder) {
|
||||
this.javaIdentifier = Key.key(javaIdentifier);
|
||||
this.requiresCorrectToolForDrops = builder.requiresCorrectToolForDrops;
|
||||
this.hasBlockEntity = builder.hasBlockEntity;
|
||||
this.destroyTime = builder.destroyTime;
|
||||
this.pushReaction = builder.pushReaction;
|
||||
this.pickItem = builder.pickItem;
|
||||
|
||||
BlockState firstState = builder.build(this).get(0);
|
||||
this.propertyKeys = builder.propertyKeys; // Ensure this is not null before iterating over states
|
||||
this.defaultState = setDefaultState(firstState);
|
||||
}
|
||||
|
||||
public void updateBlock(GeyserSession session, BlockState state, Vector3i position) {
|
||||
checkForEmptySkull(session, state, position);
|
||||
|
||||
BlockDefinition definition = session.getBlockMappings().getBedrockBlock(state);
|
||||
sendBlockUpdatePacket(session, state, definition, position);
|
||||
|
||||
// Extended collision boxes for custom blocks
|
||||
if (!session.getBlockMappings().getExtendedCollisionBoxes().isEmpty()) {
|
||||
int aboveBlock = session.getGeyser().getWorldManager().getBlockAt(session, position.getX(), position.getY() + 1, position.getZ());
|
||||
BlockDefinition aboveBedrockExtendedCollisionDefinition = session.getBlockMappings().getExtendedCollisionBoxes().get(state.javaId());
|
||||
int belowBlock = session.getGeyser().getWorldManager().getBlockAt(session, position.getX(), position.getY() - 1, position.getZ());
|
||||
BlockDefinition belowBedrockExtendedCollisionDefinition = session.getBlockMappings().getExtendedCollisionBoxes().get(belowBlock);
|
||||
if (belowBedrockExtendedCollisionDefinition != null && state.is(Blocks.AIR)) {
|
||||
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
|
||||
updateBlockPacket.setDataLayer(0);
|
||||
updateBlockPacket.setBlockPosition(position);
|
||||
updateBlockPacket.setDefinition(belowBedrockExtendedCollisionDefinition);
|
||||
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
|
||||
session.sendUpstreamPacket(updateBlockPacket);
|
||||
} else if (aboveBedrockExtendedCollisionDefinition != null && aboveBlock == Block.JAVA_AIR_ID) {
|
||||
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
|
||||
updateBlockPacket.setDataLayer(0);
|
||||
updateBlockPacket.setBlockPosition(position.add(0, 1, 0));
|
||||
updateBlockPacket.setDefinition(aboveBedrockExtendedCollisionDefinition);
|
||||
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
|
||||
session.sendUpstreamPacket(updateBlockPacket);
|
||||
} else if (aboveBlock == Block.JAVA_AIR_ID) {
|
||||
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
|
||||
updateBlockPacket.setDataLayer(0);
|
||||
updateBlockPacket.setBlockPosition(position.add(0, 1, 0));
|
||||
updateBlockPacket.setDefinition(session.getBlockMappings().getBedrockAir());
|
||||
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
|
||||
session.sendUpstreamPacket(updateBlockPacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void sendBlockUpdatePacket(GeyserSession session, BlockState state, BlockDefinition definition, Vector3i position) {
|
||||
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
|
||||
updateBlockPacket.setDataLayer(0);
|
||||
updateBlockPacket.setBlockPosition(position);
|
||||
updateBlockPacket.setDefinition(definition);
|
||||
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS);
|
||||
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
|
||||
session.sendUpstreamPacket(updateBlockPacket);
|
||||
|
||||
UpdateBlockPacket waterPacket = new UpdateBlockPacket();
|
||||
waterPacket.setDataLayer(1);
|
||||
waterPacket.setBlockPosition(position);
|
||||
if (BlockRegistries.WATERLOGGED.get().get(state.javaId())) {
|
||||
waterPacket.setDefinition(session.getBlockMappings().getBedrockWater());
|
||||
} else {
|
||||
waterPacket.setDefinition(session.getBlockMappings().getBedrockAir());
|
||||
}
|
||||
session.sendUpstreamPacket(waterPacket);
|
||||
}
|
||||
|
||||
protected void checkForEmptySkull(GeyserSession session, BlockState state, Vector3i position) {
|
||||
if (!(state.block() instanceof SkullBlock)) {
|
||||
// Skull is gone
|
||||
session.getSkullCache().removeSkull(position);
|
||||
}
|
||||
}
|
||||
|
||||
public Item asItem() {
|
||||
if (this.item == null) {
|
||||
return this.item = Item.byBlock(this);
|
||||
}
|
||||
return this.item;
|
||||
}
|
||||
|
||||
public ItemStack pickItem(BlockState state) {
|
||||
if (this.pickItem != null) {
|
||||
return new ItemStack(this.pickItem.get().javaId());
|
||||
}
|
||||
return new ItemStack(this.asItem().javaId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Should only be ran on block creation. Can be overridden.
|
||||
* @param firstState the first state created from this block
|
||||
*/
|
||||
protected BlockState setDefaultState(BlockState firstState) {
|
||||
return firstState;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Key javaIdentifier() {
|
||||
return javaIdentifier;
|
||||
}
|
||||
|
||||
public boolean requiresCorrectToolForDrops() {
|
||||
return requiresCorrectToolForDrops;
|
||||
}
|
||||
|
||||
public boolean hasBlockEntity() {
|
||||
return hasBlockEntity;
|
||||
}
|
||||
|
||||
public float destroyTime() {
|
||||
return destroyTime;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public PistonBehavior pushReaction() {
|
||||
return this.pushReaction;
|
||||
}
|
||||
|
||||
public BlockState defaultBlockState() {
|
||||
return this.defaultState;
|
||||
}
|
||||
|
||||
public int javaId() {
|
||||
return javaId;
|
||||
}
|
||||
|
||||
public void setJavaId(int javaId) {
|
||||
if (this.javaId != -1) {
|
||||
throw new RuntimeException("Block ID has already been set!");
|
||||
}
|
||||
this.javaId = javaId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Block{" +
|
||||
"javaIdentifier='" + javaIdentifier + '\'' +
|
||||
", javaId=" + javaId +
|
||||
'}';
|
||||
}
|
||||
|
||||
Property<?>[] propertyKeys() {
|
||||
return propertyKeys;
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static final class Builder {
|
||||
private final Map<Property<?>, List<Comparable<?>>> states = new LinkedHashMap<>();
|
||||
private boolean requiresCorrectToolForDrops = false;
|
||||
private boolean hasBlockEntity = false;
|
||||
private PistonBehavior pushReaction = PistonBehavior.NORMAL;
|
||||
private float destroyTime;
|
||||
private Supplier<Item> pickItem;
|
||||
|
||||
// We'll use this field after building
|
||||
private Property<?>[] propertyKeys;
|
||||
|
||||
/**
|
||||
* For states that we're just tracking for mirroring Java states.
|
||||
*/
|
||||
public Builder enumState(BasicEnumProperty property) {
|
||||
states.put(property, property.values());
|
||||
return this;
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public final <T extends Enum<T>> Builder enumState(Property<T> property, T... enums) {
|
||||
states.put(property, List.of(enums));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder booleanState(Property<Boolean> property) {
|
||||
states.put(property, List.of(Boolean.TRUE, Boolean.FALSE)); // Make this list a static constant if it'll survive past initialization
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder intState(IntegerProperty property) {
|
||||
int low = property.low();
|
||||
int high = property.high();
|
||||
IntList list = new IntArrayList();
|
||||
// There is a state for every number between the low and high.
|
||||
for (int i = low; i <= high; i++) {
|
||||
list.add(i);
|
||||
}
|
||||
states.put(property, List.copyOf(list)); // Boxing reasons for that copy I guess.
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder requiresCorrectToolForDrops() {
|
||||
this.requiresCorrectToolForDrops = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setBlockEntity() {
|
||||
this.hasBlockEntity = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder destroyTime(float destroyTime) {
|
||||
this.destroyTime = destroyTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder pushReaction(PistonBehavior pushReaction) {
|
||||
this.pushReaction = pushReaction;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder pickItem(Supplier<Item> pickItem) {
|
||||
this.pickItem = pickItem;
|
||||
return this;
|
||||
}
|
||||
|
||||
private List<BlockState> build(Block block) {
|
||||
if (states.isEmpty()) {
|
||||
BlockState state = new BlockState(block, BlockRegistries.BLOCK_STATES.get().size());
|
||||
BlockRegistries.BLOCK_STATES.get().add(state);
|
||||
propertyKeys = null;
|
||||
return List.of(state);
|
||||
} else if (states.size() == 1) {
|
||||
// We can optimize because we don't need to worry about combinations
|
||||
Map.Entry<Property<?>, List<Comparable<?>>> property = this.states.entrySet().stream().findFirst().orElseThrow();
|
||||
List<BlockState> states = new ArrayList<>(property.getValue().size());
|
||||
property.getValue().forEach(value -> {
|
||||
BlockState state = new BlockState(block, BlockRegistries.BLOCK_STATES.get().size(), new Comparable[] {value});
|
||||
BlockRegistries.BLOCK_STATES.get().add(state);
|
||||
states.add(state);
|
||||
});
|
||||
this.propertyKeys = new Property[]{property.getKey()};
|
||||
return states;
|
||||
} else {
|
||||
// Think of this stream as another list containing, at the start, one empty list.
|
||||
// It's two collections. Not a stream from the empty list.
|
||||
Stream<List<Comparable<?>>> stream = Stream.of(Collections.emptyList());
|
||||
for (var values : this.states.values()) {
|
||||
// OK, so here's how I understand this works. Because this was staring at vanilla Java code trying
|
||||
// to figure out exactly how it works so we don't have any discrepencies.
|
||||
// For each existing pair in the list, a new list is created, adding one of the new values.
|
||||
// Property up [true/false] would exist as true and false
|
||||
// Both entries will get duplicated, adding down, true and false.
|
||||
stream = stream.flatMap(aPreviousPropertiesList ->
|
||||
// So the above is a list. It may be empty if this is the first property,
|
||||
// or it may be populated if this is not the first property.
|
||||
// We're about to create a new stream, each with a new list,
|
||||
// for every previous property
|
||||
values.stream().map(value -> {
|
||||
var newProperties = new ArrayList<>(aPreviousPropertiesList);
|
||||
newProperties.add(value);
|
||||
return newProperties;
|
||||
}));
|
||||
}
|
||||
|
||||
List<BlockState> states = new ArrayList<>();
|
||||
// Now we have a list of Pair<Property, Value>s. Each list is a block state!
|
||||
// If we have two boolean properties: up [true/false] and down [true/false],
|
||||
// We'll see [up=true,down=true], [up=false,down=true], [up=true,down=false], [up=false,down=false]
|
||||
List<List<Comparable<?>>> result = stream.toList();
|
||||
// Ensure each block state shares the same key array. Creating a keySet here shouldn't be an issue since
|
||||
// this states map should be removed after build.
|
||||
Property<?>[] keys = this.states.keySet().toArray(new Property<?>[0]);
|
||||
result.forEach(properties -> {
|
||||
Comparable<?>[] values = properties.toArray(new Comparable<?>[0]);
|
||||
BlockState state = new BlockState(block, BlockRegistries.BLOCK_STATES.get().size(), values);
|
||||
BlockRegistries.BLOCK_STATES.get().add(state);
|
||||
states.add(state);
|
||||
});
|
||||
this.propertyKeys = keys;
|
||||
return states;
|
||||
}
|
||||
}
|
||||
|
||||
private Builder() {
|
||||
}
|
||||
}
|
||||
}
|
189
core/src/main/java/org/geysermc/geyser/level/block/type/BlockState.java
Normale Datei
189
core/src/main/java/org/geysermc/geyser/level/block/type/BlockState.java
Normale Datei
@ -0,0 +1,189 @@
|
||||
/*
|
||||
* 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.level.block.type;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.level.block.property.Property;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public final class BlockState {
|
||||
private final Block block;
|
||||
private final int javaId;
|
||||
/**
|
||||
* The values of each property of this block state. These should be treated as keys to {@link Block#propertyKeys()}.
|
||||
* Of note - the comparable part probably doesn't do anything because we occasionally use strings in place of enums.
|
||||
* Will be null if there's only one block state for a block.
|
||||
*/
|
||||
private final Comparable<?>[] states;
|
||||
|
||||
public BlockState(Block block, int javaId) {
|
||||
this(block, javaId, null);
|
||||
}
|
||||
|
||||
BlockState(Block block, int javaId, Comparable<?>[] states) {
|
||||
this.block = block;
|
||||
this.javaId = javaId;
|
||||
this.states = states;
|
||||
}
|
||||
|
||||
public <T extends Comparable<T>> T getValue(Property<T> property) {
|
||||
//noinspection unchecked
|
||||
return (T) get(property);
|
||||
}
|
||||
|
||||
public <T extends Comparable<T>> T getValueNullable(Property<T> property) {
|
||||
var value = get(property);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
//noinspection unchecked
|
||||
return (T) get(property);
|
||||
}
|
||||
|
||||
public boolean getValue(Property<Boolean> property, boolean def) {
|
||||
var value = get(property);
|
||||
if (value == null) {
|
||||
return def;
|
||||
}
|
||||
return (Boolean) value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Comparable<?> get(Property<?> property) {
|
||||
Property<?>[] keys = this.block.propertyKeys();
|
||||
if (keys == null) {
|
||||
return null;
|
||||
}
|
||||
// We're copying the behavior Reference2ObjectArrayMap uses
|
||||
for (int i = keys.length; i-- != 0;) {
|
||||
if (keys[i] == property) {
|
||||
return this.states[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link BlockState} instance with the given value.
|
||||
*/
|
||||
public <T extends Comparable<T>> BlockState withValue(Property<T> property, T value) {
|
||||
Property<?>[] keys = this.block.propertyKeys();
|
||||
if (keys == null) {
|
||||
throw new IllegalStateException(this + " does not have any different states!");
|
||||
}
|
||||
|
||||
T currentValue = getValue(property);
|
||||
if (currentValue == null) {
|
||||
throw new IllegalArgumentException("This BlockState does not have the property " + property);
|
||||
}
|
||||
if (currentValue.equals(value)) {
|
||||
// No action required. This block state is the state we're looking for.
|
||||
return this;
|
||||
}
|
||||
|
||||
// Diff is how much we will have to traverse as a sort of offset
|
||||
|
||||
// Block states are calculated in a predictable structure:
|
||||
// minecraft:cobblestone_wall[east=none,north=none,south=none,up=true,waterlogged=true,west=none]
|
||||
// minecraft:cobblestone_wall[east=none,north=none,south=none,up=true,waterlogged=true,west=low]
|
||||
// minecraft:cobblestone_wall[east=none,north=none,south=none,up=true,waterlogged=true,west=tall]
|
||||
// minecraft:cobblestone_wall[east=none,north=none,south=none,up=true,waterlogged=false,west=none]
|
||||
// minecraft:cobblestone_wall[east=none,north=none,south=none,up=true,waterlogged=false,west=low]
|
||||
// minecraft:cobblestone_wall[east=none,north=none,south=none,up=true,waterlogged=false,west=tall]
|
||||
// minecraft:cobblestone_wall[east=none,north=none,south=none,up=false,waterlogged=true,west=none]
|
||||
|
||||
// The last value goes through all its iterations, then the next state goes through all its iterations.
|
||||
// West goes none -> low -> tall, then waterlogged is toggled as west cycles again.
|
||||
// Then when waterlogged goes through all its properties, up is toggled, and west goes through again
|
||||
// If we want to find the "up" property in order, then we need to find how many iterations each property
|
||||
// after it goes in. West goes for 3, waterlogged goes for 2. Adding those together, we find that we need to
|
||||
// add five to get to the next toggle of the up property
|
||||
int diff = 0;
|
||||
for (int i = keys.length - 1; i >= 0; i--) {
|
||||
if (keys[i] != property) {
|
||||
diff += keys[i].valuesCount();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// How many times do we have to jump by diff? This depends on how far away each value is from each other.
|
||||
// piston_head[facing=north] might be right next to piston_head[facing=south], which just one diff'd hop.
|
||||
// But piston_head[facing=west] is further away, requiring more hops.
|
||||
int thatOffset = property.indexOf(value);
|
||||
int thisOffset = property.indexOf(currentValue);
|
||||
if (diff == 0) {
|
||||
// This can happen if the property is at the tail end of the block and there are no other properties to look through
|
||||
// If we have minecraft:cobblestone_wall[east=none,north=none,south=none,up=true,waterlogged=true,west=none]
|
||||
// And want minecraft:cobblestone_wall[east=none,north=none,south=none,up=true,waterlogged=true,west=low]
|
||||
// The above for loop will always stop at the first break because the last property has already been found
|
||||
diff = 1;
|
||||
}
|
||||
return of(this.javaId + ((thatOffset - thisOffset) * diff));
|
||||
}
|
||||
|
||||
public Block block() {
|
||||
return this.block;
|
||||
}
|
||||
|
||||
public int javaId() {
|
||||
return this.javaId;
|
||||
}
|
||||
|
||||
public boolean is(Block block) {
|
||||
return this.block == block;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (this.states == null) {
|
||||
return this.block.javaIdentifier().toString();
|
||||
}
|
||||
return this.block.javaIdentifier().toString() + "[" + paramsToString() + "]";
|
||||
}
|
||||
|
||||
private String paramsToString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
Property<?>[] propertyKeys = this.block.propertyKeys();
|
||||
if (propertyKeys != null) {
|
||||
for (int i = 0; i < propertyKeys.length; i++) {
|
||||
builder.append(propertyKeys[i].name())
|
||||
.append("=")
|
||||
.append(this.states[i].toString().toLowerCase(Locale.ROOT)); // lowercase covers enums
|
||||
if (i < propertyKeys.length - 1) {
|
||||
builder.append(",");
|
||||
}
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public static BlockState of(int javaId) {
|
||||
return BlockRegistries.BLOCK_STATES.get(javaId);
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.level.block.type;
|
||||
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.nbt.NbtList;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtType;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.level.block.entity.BedrockChunkWantsBlockEntityTag;
|
||||
import org.geysermc.geyser.translator.level.block.entity.BlockEntityTranslator;
|
||||
|
||||
public class CauldronBlock extends Block implements BedrockChunkWantsBlockEntityTag {
|
||||
public CauldronBlock(String javaIdentifier, Builder builder) {
|
||||
super(javaIdentifier, builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NbtMap createTag(GeyserSession session, Vector3i position, BlockState blockState) {
|
||||
// As of 1.18.30: this is required to make rendering not look weird on chunk load (lava and snow cauldrons look dim)
|
||||
return BlockEntityTranslator.getConstantBedrockTag("Cauldron", position.getX(), position.getY(), position.getZ())
|
||||
.putByte("isMovable", (byte) 0)
|
||||
.putShort("PotionId", (short) -1)
|
||||
.putShort("PotionType", (short) -1)
|
||||
.putList("Items", NbtType.END, NbtList.EMPTY)
|
||||
.build();
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.level.block.type;
|
||||
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.geysermc.geyser.level.block.property.ChestType;
|
||||
import org.geysermc.geyser.level.block.property.Properties;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.level.block.entity.BlockEntityTranslator;
|
||||
import org.geysermc.geyser.util.BlockEntityUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType;
|
||||
|
||||
public class ChestBlock extends Block {
|
||||
public ChestBlock(String javaIdentifier, Builder builder) {
|
||||
super(javaIdentifier, builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBlock(GeyserSession session, BlockState state, Vector3i position) {
|
||||
super.updateBlock(session, state, position);
|
||||
|
||||
if (state.getValue(Properties.CHEST_TYPE) != ChestType.SINGLE) {
|
||||
NbtMapBuilder tagBuilder = BlockEntityTranslator.getConstantBedrockTag(BlockEntityType.CHEST, position.getX(), position.getY(), position.getZ());
|
||||
BlockEntityUtils.getBlockEntityTranslator(BlockEntityType.CHEST).translateTag(session, tagBuilder, null, state); //TODO
|
||||
BlockEntityUtils.updateBlockEntity(session, tagBuilder.build(), position);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.level.block.type;
|
||||
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.geysermc.geyser.level.block.property.Properties;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.util.ChunkUtils;
|
||||
|
||||
public class DoorBlock extends Block {
|
||||
public DoorBlock(String javaIdentifier, Builder builder) {
|
||||
super(javaIdentifier, builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBlock(GeyserSession session, BlockState state, Vector3i position) {
|
||||
super.updateBlock(session, state, position);
|
||||
|
||||
if (state.getValue(Properties.DOUBLE_BLOCK_HALF).equals("upper")) {
|
||||
// Update the lower door block as Bedrock client doesn't like door to be closed from the top
|
||||
// See https://github.com/GeyserMC/Geyser/issues/4358
|
||||
Vector3i belowDoorPosition = position.sub(0, 1, 0);
|
||||
BlockState belowDoorBlockState = session.getGeyser().getWorldManager().blockAt(session, belowDoorPosition.getX(), belowDoorPosition.getY(), belowDoorPosition.getZ());
|
||||
ChunkUtils.updateBlock(session, belowDoorBlockState, belowDoorPosition);
|
||||
}
|
||||
}
|
||||
}
|
@ -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,64 +23,36 @@
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.translator.level.block.entity;
|
||||
package org.geysermc.geyser.level.block.type;
|
||||
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.UpdateBlockPacket;
|
||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||
import org.geysermc.geyser.level.block.Blocks;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.level.block.entity.BedrockChunkWantsBlockEntityTag;
|
||||
import org.geysermc.geyser.translator.level.block.entity.BlockEntityTranslator;
|
||||
import org.geysermc.geyser.util.BlockEntityUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
|
||||
|
||||
public class FlowerPotBlockEntityTranslator implements BedrockOnlyBlockEntity {
|
||||
/**
|
||||
* @param blockState the Java block state of a potential flower pot block
|
||||
* @return true if the block is a flower pot
|
||||
*/
|
||||
public static boolean isFlowerBlock(int blockState) {
|
||||
return BlockStateValues.getFlowerPotValues().containsKey(blockState);
|
||||
}
|
||||
public class FlowerPotBlock extends Block implements BedrockChunkWantsBlockEntityTag {
|
||||
private final Block flower;
|
||||
|
||||
/**
|
||||
* Get the Nukkit CompoundTag of the flower pot.
|
||||
*
|
||||
* @param blockState Java block state of flower pot.
|
||||
* @param position Bedrock position of flower pot.
|
||||
* @return Bedrock tag of flower pot.
|
||||
*/
|
||||
public static NbtMap getTag(GeyserSession session, int blockState, Vector3i position) {
|
||||
NbtMapBuilder tagBuilder = NbtMap.builder()
|
||||
.putInt("x", position.getX())
|
||||
.putInt("y", position.getY())
|
||||
.putInt("z", position.getZ())
|
||||
.putByte("isMovable", (byte) 1)
|
||||
.putString("id", "FlowerPot");
|
||||
// Get the Java name of the plant inside. e.g. minecraft:oak_sapling
|
||||
String name = BlockStateValues.getFlowerPotValues().get(blockState);
|
||||
if (name != null) {
|
||||
// Get the Bedrock CompoundTag of the block.
|
||||
// This is where we need to store the *Java* name because Bedrock has six minecraft:sapling blocks with different block states.
|
||||
NbtMap plant = session.getBlockMappings().getFlowerPotBlocks().get(name);
|
||||
if (plant != null) {
|
||||
tagBuilder.put("PlantBlock", plant.toBuilder().build());
|
||||
}
|
||||
}
|
||||
return tagBuilder.build();
|
||||
public FlowerPotBlock(String javaIdentifier, Block flower, Builder builder) {
|
||||
super(javaIdentifier, builder);
|
||||
this.flower = flower;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBlock(int blockState) {
|
||||
return isFlowerBlock(blockState);
|
||||
}
|
||||
public void updateBlock(GeyserSession session, BlockState state, Vector3i position) {
|
||||
super.updateBlock(session, state, position);
|
||||
|
||||
@Override
|
||||
public void updateBlock(GeyserSession session, int blockState, Vector3i position) {
|
||||
NbtMap tag = getTag(session, blockState, position);
|
||||
NbtMap tag = createTag(session, position, state);
|
||||
BlockEntityUtils.updateBlockEntity(session, tag, position);
|
||||
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
|
||||
updateBlockPacket.setDataLayer(0);
|
||||
updateBlockPacket.setDefinition(session.getBlockMappings().getBedrockBlock(blockState));
|
||||
updateBlockPacket.setDefinition(session.getBlockMappings().getBedrockBlock(state));
|
||||
updateBlockPacket.setBlockPosition(position);
|
||||
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS);
|
||||
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
|
||||
@ -88,4 +60,33 @@ public class FlowerPotBlockEntityTranslator implements BedrockOnlyBlockEntity {
|
||||
session.sendUpstreamPacket(updateBlockPacket);
|
||||
BlockEntityUtils.updateBlockEntity(session, tag, position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NbtMap createTag(GeyserSession session, Vector3i position, BlockState blockState) {
|
||||
NbtMapBuilder tagBuilder = BlockEntityTranslator.getConstantBedrockTag("FlowerPot", position.getX(), position.getY(), position.getZ())
|
||||
.putByte("isMovable", (byte) 1);
|
||||
// Get the Java name of the plant inside. e.g. minecraft:oak_sapling
|
||||
if (this.flower != Blocks.AIR) {
|
||||
// Get the Bedrock CompoundTag of the block.
|
||||
// This is where we need to store the *Java* name because Bedrock has six minecraft:sapling blocks with different block states.
|
||||
// TODO flattening might make this nicer in the future!
|
||||
NbtMap plant = session.getBlockMappings().getFlowerPotBlocks().get(this.flower);
|
||||
if (plant != null) {
|
||||
tagBuilder.putCompound("PlantBlock", plant.toBuilder().build());
|
||||
}
|
||||
}
|
||||
return tagBuilder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack pickItem(BlockState state) {
|
||||
if (this.flower != Blocks.AIR) {
|
||||
return new ItemStack(this.flower.asItem().javaId());
|
||||
}
|
||||
return super.pickItem(state);
|
||||
}
|
||||
|
||||
public Block flower() {
|
||||
return flower;
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.level.block.type;
|
||||
|
||||
import org.geysermc.geyser.level.block.property.Properties;
|
||||
import org.geysermc.geyser.level.physics.Direction;
|
||||
|
||||
public class FurnaceBlock extends Block {
|
||||
public FurnaceBlock(String javaIdentifier, Builder builder) {
|
||||
super(javaIdentifier, builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BlockState setDefaultState(BlockState firstState) {
|
||||
// Both furnace minecart states look north.
|
||||
return firstState.withValue(Properties.HORIZONTAL_FACING, Direction.NORTH);
|
||||
}
|
||||
}
|
@ -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.level.block.type;
|
||||
|
||||
public class HoneyBlock extends Block {
|
||||
public HoneyBlock(String javaIdentifier, Builder builder) {
|
||||
super(javaIdentifier, builder);
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 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.level.block.type;
|
||||
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.cloudburstmc.nbt.NbtType;
|
||||
import org.geysermc.geyser.level.WorldManager;
|
||||
import org.geysermc.geyser.level.block.property.Properties;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.level.block.entity.BedrockChunkWantsBlockEntityTag;
|
||||
import org.geysermc.geyser.translator.level.block.entity.BlockEntityTranslator;
|
||||
import org.geysermc.geyser.util.BlockEntityUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
public class LecternBlock extends Block implements BedrockChunkWantsBlockEntityTag {
|
||||
public LecternBlock(String javaIdentifier, Builder builder) {
|
||||
super(javaIdentifier, builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NbtMap createTag(GeyserSession session, Vector3i position, BlockState blockState) {
|
||||
return getBaseLecternTag(position, blockState.getValue(Properties.HAS_BOOK));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBlock(GeyserSession session, BlockState state, Vector3i position) {
|
||||
WorldManager worldManager = session.getGeyser().getWorldManager();
|
||||
boolean currentHasBook = state.getValue(Properties.HAS_BOOK);
|
||||
Boolean previousHasBook = worldManager.blockAt(session, position).getValueNullable(Properties.HAS_BOOK); // Can be null if not a lectern, watch out
|
||||
if (previousHasBook == null || currentHasBook != previousHasBook) {
|
||||
BlockEntityUtils.updateBlockEntity(session, getBaseLecternTag(position, currentHasBook), position);
|
||||
}
|
||||
super.updateBlock(session, state, position);
|
||||
}
|
||||
|
||||
public static NbtMap getBaseLecternTag(Vector3i position, boolean hasBook) {
|
||||
if (hasBook) {
|
||||
return getBaseLecternTag(position, 1)
|
||||
.putCompound("book", NbtMap.builder()
|
||||
.putByte("Count", (byte) 1)
|
||||
.putShort("Damage", (short) 0)
|
||||
.putString("Name", "minecraft:writable_book")
|
||||
.putCompound("tag", NbtMap.builder().putList("pages", NbtType.COMPOUND, Collections.singletonList(
|
||||
NbtMap.builder()
|
||||
.putString("photoname", "")
|
||||
.putString("text", "")
|
||||
.build()
|
||||
)).build())
|
||||
.build())
|
||||
.build();
|
||||
} else {
|
||||
return getBaseLecternTag(position, 0).build();
|
||||
}
|
||||
}
|
||||
|
||||
public static NbtMapBuilder getBaseLecternTag(Vector3i position, int pages) {
|
||||
NbtMapBuilder builder = BlockEntityTranslator.getConstantBedrockTag("Lectern", position);
|
||||
builder.putBoolean("isMovable", true);
|
||||
|
||||
if (pages != 0) {
|
||||
builder.putByte("hasBook", (byte) 1);
|
||||
builder.putInt("totalPages", 1); // we'll override it anyway
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.level.block.type;
|
||||
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
public class MovingPistonBlock extends Block {
|
||||
public MovingPistonBlock(String javaIdentifier, Builder builder) {
|
||||
super(javaIdentifier, builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendBlockUpdatePacket(GeyserSession session, BlockState state, BlockDefinition definition, Vector3i position) {
|
||||
// Prevent moving_piston from being placed
|
||||
// It's used for extending piston heads, but it isn't needed on Bedrock and causes pistons to flicker
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.level.block.type;
|
||||
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.geysermc.geyser.level.block.Blocks;
|
||||
import org.geysermc.geyser.level.block.property.Properties;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.level.block.entity.BedrockChunkWantsBlockEntityTag;
|
||||
import org.geysermc.geyser.translator.level.block.entity.PistonBlockEntity;
|
||||
|
||||
public class PistonBlock extends Block implements BedrockChunkWantsBlockEntityTag {
|
||||
public PistonBlock(String javaIdentifier, Builder builder) {
|
||||
super(javaIdentifier, builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NbtMap createTag(GeyserSession session, Vector3i position, BlockState blockState) {
|
||||
boolean extended = blockState.getValue(Properties.EXTENDED);
|
||||
boolean sticky = blockState.is(Blocks.STICKY_PISTON);
|
||||
return PistonBlockEntity.buildStaticPistonTag(position, extended, sticky);
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.level.block.type;
|
||||
|
||||
import org.geysermc.geyser.level.block.Blocks;
|
||||
import org.geysermc.geyser.level.block.property.Properties;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
|
||||
|
||||
public class PistonHeadBlock extends Block {
|
||||
public PistonHeadBlock(String javaIdentifier, Builder builder) {
|
||||
super(javaIdentifier, builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack pickItem(BlockState state) {
|
||||
Block block = state.getValue(Properties.PISTON_TYPE).equals("sticky") ? Blocks.STICKY_PISTON : Blocks.PISTON;
|
||||
return new ItemStack(block.asItem().javaId());
|
||||
}
|
||||
}
|
120
core/src/main/java/org/geysermc/geyser/level/block/type/SkullBlock.java
Normale Datei
120
core/src/main/java/org/geysermc/geyser/level/block/type/SkullBlock.java
Normale Datei
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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.level.block.type;
|
||||
|
||||
import com.github.steveice10.mc.auth.data.GameProfile;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.SkullCache;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.UUID;
|
||||
|
||||
public class SkullBlock extends Block {
|
||||
private final Type type;
|
||||
|
||||
public SkullBlock(String javaIdentifier, Type type, Builder builder) {
|
||||
super(javaIdentifier, builder);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendBlockUpdatePacket(GeyserSession session, BlockState state, BlockDefinition definition, Vector3i position) {
|
||||
if (this.type == Type.PLAYER) {
|
||||
// The changed block was a player skull so check if a custom block was defined for this skull
|
||||
SkullCache.Skull skull = session.getSkullCache().updateSkull(position, state);
|
||||
if (skull != null && skull.getBlockDefinition() != null) {
|
||||
definition = skull.getBlockDefinition();
|
||||
}
|
||||
}
|
||||
super.sendBlockUpdatePacket(session, state, definition, position);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void checkForEmptySkull(GeyserSession session, BlockState state, Vector3i position) {
|
||||
// It's not an empty skull.
|
||||
}
|
||||
|
||||
public ItemStack pickItem(GeyserSession session, BlockState state, Vector3i position) {
|
||||
SkullCache.Skull skull = session.getSkullCache().getSkulls().get(position);
|
||||
if (skull == null) {
|
||||
return new ItemStack(pickItem(state).getId());
|
||||
}
|
||||
|
||||
GeyserItemStack itemStack = GeyserItemStack.of(pickItem(state).getId(), 1);
|
||||
// This is a universal block entity behavior, but hardcode how it works for now.
|
||||
NbtMapBuilder builder = NbtMap.builder()
|
||||
.putString("id", "minecraft:skull")
|
||||
.putInt("x", position.getX())
|
||||
.putInt("y", position.getY())
|
||||
.putInt("z", position.getZ());
|
||||
DataComponents components = itemStack.getOrCreateComponents();
|
||||
components.put(DataComponentType.BLOCK_ENTITY_DATA, builder.build());
|
||||
|
||||
UUID uuid = skull.getUuid();
|
||||
String texturesProperty = skull.getTexturesProperty();
|
||||
GameProfile profile = new GameProfile(uuid, null);
|
||||
if (texturesProperty != null) {
|
||||
profile.setProperties(Collections.singletonList(new GameProfile.Property("textures", texturesProperty)));
|
||||
}
|
||||
components.put(DataComponentType.PROFILE, profile);
|
||||
return itemStack.getItemStack();
|
||||
}
|
||||
|
||||
public Type skullType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enum order matches Java.
|
||||
*/
|
||||
public enum Type {
|
||||
SKELETON(0),
|
||||
WITHER_SKELETON(1),
|
||||
PLAYER(3),
|
||||
ZOMBIE(2),
|
||||
CREEPER(4),
|
||||
PIGLIN(6),
|
||||
DRAGON(5);
|
||||
|
||||
private final int bedrockId;
|
||||
|
||||
Type(int bedrockId) {
|
||||
this.bedrockId = bedrockId;
|
||||
}
|
||||
|
||||
public int bedrockId() {
|
||||
return bedrockId;
|
||||
}
|
||||
}
|
||||
}
|
@ -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.level.block.type;
|
||||
|
||||
public class SpawnerBlock extends Block {
|
||||
public SpawnerBlock(String javaIdentifier, Builder builder) {
|
||||
super(javaIdentifier, builder);
|
||||
}
|
||||
}
|
@ -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.level.block.type;
|
||||
|
||||
public class TrapDoorBlock extends Block {
|
||||
public TrapDoorBlock(String javaIdentifier, Builder builder) {
|
||||
super(javaIdentifier, builder);
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.level.block.type;
|
||||
|
||||
import org.geysermc.geyser.level.block.property.Properties;
|
||||
import org.geysermc.geyser.level.physics.Direction;
|
||||
|
||||
public class WallSkullBlock extends SkullBlock {
|
||||
public WallSkullBlock(String javaIdentifier, Type type, Builder builder) {
|
||||
super(javaIdentifier, type, builder);
|
||||
}
|
||||
|
||||
public static int getDegrees(BlockState state) {
|
||||
return getDegrees(state.getValue(Properties.HORIZONTAL_FACING));
|
||||
}
|
||||
|
||||
public static int getDegrees(Direction direction) {
|
||||
return switch (direction) {
|
||||
case NORTH -> 180;
|
||||
case WEST -> 90;
|
||||
case EAST -> 270;
|
||||
case SOUTH -> 0;
|
||||
default -> throw new IllegalStateException();
|
||||
};
|
||||
}
|
||||
}
|
@ -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.level.block.type;
|
||||
|
||||
public class WaterBlock extends Block {
|
||||
public WaterBlock(String javaIdentifier, Builder builder) {
|
||||
super(javaIdentifier, builder);
|
||||
}
|
||||
}
|
@ -39,6 +39,9 @@ import org.geysermc.geyser.entity.EntityDefinitions;
|
||||
import org.geysermc.geyser.entity.type.Entity;
|
||||
import org.geysermc.geyser.entity.type.player.PlayerEntity;
|
||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||
import org.geysermc.geyser.level.block.Blocks;
|
||||
import org.geysermc.geyser.level.block.property.Properties;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.PistonCache;
|
||||
import org.geysermc.geyser.translator.collision.BlockCollision;
|
||||
@ -405,7 +408,8 @@ public class CollisionManager {
|
||||
* @return if the player is currently in a water block
|
||||
*/
|
||||
public boolean isPlayerInWater() {
|
||||
return session.getGeyser().getWorldManager().getBlockAt(session, session.getPlayerEntity().getPosition().toInt()) == BlockStateValues.JAVA_WATER_ID;
|
||||
BlockState state = session.getGeyser().getWorldManager().blockAt(session, session.getPlayerEntity().getPosition().toInt());
|
||||
return state.is(Blocks.WATER) && state.getValue(Properties.LEVEL) == 0;
|
||||
}
|
||||
|
||||
public boolean isWaterInEyes() {
|
||||
|
@ -224,7 +224,7 @@ class CodecProcessor {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static BedrockCodec processCodec(BedrockCodec codec) {
|
||||
return codec.toBuilder()
|
||||
BedrockCodec.Builder codecBuilder = codec.toBuilder()
|
||||
// Illegal unused serverbound EDU packets
|
||||
.updateSerializer(PhotoTransferPacket.class, ILLEGAL_SERIALIZER)
|
||||
.updateSerializer(LabTablePacket.class, ILLEGAL_SERIALIZER)
|
||||
@ -232,10 +232,11 @@ class CodecProcessor {
|
||||
.updateSerializer(CreatePhotoPacket.class, ILLEGAL_SERIALIZER)
|
||||
.updateSerializer(NpcRequestPacket.class, ILLEGAL_SERIALIZER)
|
||||
.updateSerializer(PhotoInfoRequestPacket.class, ILLEGAL_SERIALIZER)
|
||||
// Illegal unused serverbound packets for featured servers
|
||||
.updateSerializer(PurchaseReceiptPacket.class, ILLEGAL_SERIALIZER)
|
||||
// Unused serverbound packets for featured servers, which is for some reason still occasionally sent
|
||||
.updateSerializer(PurchaseReceiptPacket.class, IGNORED_SERIALIZER)
|
||||
// Illegal unused serverbound packets that are deprecated
|
||||
.updateSerializer(ClientCheatAbilityPacket.class, ILLEGAL_SERIALIZER)
|
||||
.updateSerializer(CraftingEventPacket.class, ILLEGAL_SERIALIZER)
|
||||
// Illegal unusued serverbound packets that relate to unused features
|
||||
.updateSerializer(PlayerAuthInputPacket.class, ILLEGAL_SERIALIZER)
|
||||
.updateSerializer(ClientCacheBlobStatusPacket.class, ILLEGAL_SERIALIZER)
|
||||
@ -243,7 +244,6 @@ class CodecProcessor {
|
||||
.updateSerializer(SubChunkRequestPacket.class, ILLEGAL_SERIALIZER)
|
||||
.updateSerializer(GameTestRequestPacket.class, ILLEGAL_SERIALIZER)
|
||||
// Ignored serverbound packets
|
||||
.updateSerializer(CraftingEventPacket.class, IGNORED_SERIALIZER) // Make illegal when 1.20.40 is removed
|
||||
.updateSerializer(ClientToServerHandshakePacket.class, IGNORED_SERIALIZER)
|
||||
.updateSerializer(EntityFallPacket.class, IGNORED_SERIALIZER)
|
||||
.updateSerializer(MapCreateLockedCopyPacket.class, IGNORED_SERIALIZER)
|
||||
@ -260,22 +260,25 @@ class CodecProcessor {
|
||||
.updateSerializer(PlayerHotbarPacket.class, PLAYER_HOTBAR_SERIALIZER)
|
||||
.updateSerializer(PlayerSkinPacket.class, PLAYER_SKIN_SERIALIZER)
|
||||
.updateSerializer(SetEntityDataPacket.class, SET_ENTITY_DATA_SERIALIZER)
|
||||
.updateSerializer(SetEntityMotionPacket.class, codec.getProtocolVersion() < 662 ?
|
||||
SET_ENTITY_MOTION_SERIALIZER_V291 :
|
||||
SET_ENTITY_MOTION_SERIALIZER_V662)
|
||||
.updateSerializer(SetEntityMotionPacket.class, SET_ENTITY_MOTION_SERIALIZER_V662)
|
||||
.updateSerializer(SetEntityLinkPacket.class, SET_ENTITY_LINK_SERIALIZER)
|
||||
// Valid serverbound packets where reading of some fields can be skipped
|
||||
.updateSerializer(MobEquipmentPacket.class, MOB_EQUIPMENT_SERIALIZER)
|
||||
// // Illegal bidirectional packets
|
||||
// Illegal bidirectional packets
|
||||
.updateSerializer(DebugInfoPacket.class, ILLEGAL_SERIALIZER)
|
||||
.updateSerializer(EditorNetworkPacket.class, ILLEGAL_SERIALIZER)
|
||||
.updateSerializer(ScriptMessagePacket.class, ILLEGAL_SERIALIZER)
|
||||
// // Ignored bidirectional packets
|
||||
// Ignored bidirectional packets
|
||||
.updateSerializer(ClientCacheStatusPacket.class, IGNORED_SERIALIZER)
|
||||
.updateSerializer(SimpleEventPacket.class, IGNORED_SERIALIZER)
|
||||
.updateSerializer(TickSyncPacket.class, IGNORED_SERIALIZER)
|
||||
.updateSerializer(MultiplayerSettingsPacket.class, IGNORED_SERIALIZER)
|
||||
.build();
|
||||
.updateSerializer(MultiplayerSettingsPacket.class, IGNORED_SERIALIZER);
|
||||
|
||||
if (codec.getProtocolVersion() < 685) {
|
||||
// Ignored bidirectional packets
|
||||
codecBuilder.updateSerializer(TickSyncPacket.class, IGNORED_SERIALIZER);
|
||||
}
|
||||
|
||||
return codecBuilder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -27,11 +27,8 @@ package org.geysermc.geyser.network;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v622.Bedrock_v622;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v630.Bedrock_v630;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v649.Bedrock_v649;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v662.Bedrock_v662;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v671.Bedrock_v671;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v685.Bedrock_v685;
|
||||
import org.cloudburstmc.protocol.bedrock.netty.codec.packet.BedrockPacketCodec;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.mcprotocollib.protocol.codec.MinecraftCodec;
|
||||
@ -50,8 +47,8 @@ public final class GameProtocol {
|
||||
* Default Bedrock codec that should act as a fallback. Should represent the latest available
|
||||
* release of the game that Geyser supports.
|
||||
*/
|
||||
public static final BedrockCodec DEFAULT_BEDROCK_CODEC = CodecProcessor.processCodec(Bedrock_v671.CODEC.toBuilder()
|
||||
.minecraftVersion("1.20.81")
|
||||
public static final BedrockCodec DEFAULT_BEDROCK_CODEC = CodecProcessor.processCodec(Bedrock_v685.CODEC.toBuilder()
|
||||
.minecraftVersion("1.21.0")
|
||||
.build());
|
||||
|
||||
/**
|
||||
@ -66,20 +63,11 @@ public final class GameProtocol {
|
||||
private static final PacketCodec DEFAULT_JAVA_CODEC = MinecraftCodec.CODEC;
|
||||
|
||||
static {
|
||||
SUPPORTED_BEDROCK_CODECS.add(CodecProcessor.processCodec(Bedrock_v622.CODEC.toBuilder()
|
||||
.minecraftVersion("1.20.40/1.20.41")
|
||||
.build()));
|
||||
SUPPORTED_BEDROCK_CODECS.add(CodecProcessor.processCodec(Bedrock_v630.CODEC.toBuilder()
|
||||
.minecraftVersion("1.20.50/1.20.51")
|
||||
.build()));
|
||||
SUPPORTED_BEDROCK_CODECS.add(CodecProcessor.processCodec(Bedrock_v649.CODEC.toBuilder()
|
||||
.minecraftVersion("1.20.60/1.20.62")
|
||||
.build()));
|
||||
SUPPORTED_BEDROCK_CODECS.add(CodecProcessor.processCodec(Bedrock_v662.CODEC.toBuilder()
|
||||
.minecraftVersion("1.20.70/1.20.73")
|
||||
SUPPORTED_BEDROCK_CODECS.add(CodecProcessor.processCodec(Bedrock_v671.CODEC.toBuilder()
|
||||
.minecraftVersion("1.20.80/1.20.81")
|
||||
.build()));
|
||||
SUPPORTED_BEDROCK_CODECS.add(CodecProcessor.processCodec(DEFAULT_BEDROCK_CODEC.toBuilder()
|
||||
.minecraftVersion("1.20.80/1.20.81")
|
||||
.minecraftVersion("1.21.0")
|
||||
.build()));
|
||||
}
|
||||
|
||||
@ -99,16 +87,8 @@ public final class GameProtocol {
|
||||
|
||||
/* Bedrock convenience methods to gatekeep features and easily remove the check on version removal */
|
||||
|
||||
public static boolean isPre1_20_50(GeyserSession session) {
|
||||
return session.getUpstream().getProtocolVersion() < Bedrock_v630.CODEC.getProtocolVersion();
|
||||
}
|
||||
|
||||
public static boolean isPre1_20_70(GeyserSession session) {
|
||||
return session.getUpstream().getProtocolVersion() < Bedrock_v662.CODEC.getProtocolVersion();
|
||||
}
|
||||
|
||||
public static boolean is1_20_60orHigher(int protocolVersion) {
|
||||
return protocolVersion >= Bedrock_v649.CODEC.getProtocolVersion();
|
||||
public static boolean isPre1_21_0(GeyserSession session) {
|
||||
return session.getUpstream().getProtocolVersion() < Bedrock_v685.CODEC.getProtocolVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -47,7 +47,9 @@ public class InvalidPacketHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
|
||||
if (!(rootCause instanceof IllegalArgumentException)) {
|
||||
super.exceptionCaught(ctx, cause);
|
||||
// Kick users that cause exceptions
|
||||
session.getGeyser().getLogger().warning("Exception caught in session of" + session.bedrockUsername() + ": " + rootCause.getMessage());
|
||||
session.disconnect("An internal error occurred!");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -132,6 +132,8 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
||||
}
|
||||
|
||||
session.getUpstream().getSession().setCodec(packetCodec);
|
||||
// FIXME temporary until 1.20.80 is dropped
|
||||
session.getPlayerEntity().resetAir();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -35,10 +35,7 @@ import org.cloudburstmc.nbt.util.VarInts;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.network.GameProtocol;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@ -139,6 +136,9 @@ public class GeyserLegacyPingPassthrough implements IGeyserPingPassthrough, Runn
|
||||
this.geyser.getLogger().debug("Connection timeout for ping passthrough.");
|
||||
} catch (JsonParseException | JsonMappingException ex) {
|
||||
this.geyser.getLogger().error("Failed to parse json when pinging server!", ex);
|
||||
} catch (EOFException e) {
|
||||
this.pingInfo = null;
|
||||
this.geyser.getLogger().warning("Failed to ping the remote Java server! Is it online and configured in Geyser's config?");
|
||||
} catch (UnknownHostException ex) {
|
||||
// Don't reset pingInfo, as we want to keep the last known value
|
||||
this.geyser.getLogger().warning("Unable to resolve remote host! Is the remote server down or invalid?");
|
||||
|
@ -34,18 +34,20 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import org.geysermc.geyser.api.block.custom.CustomBlockData;
|
||||
import org.geysermc.geyser.api.block.custom.CustomBlockState;
|
||||
import org.geysermc.geyser.api.block.custom.nonvanilla.JavaBlockState;
|
||||
import org.geysermc.geyser.level.block.Blocks;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.registry.loader.CollisionRegistryLoader;
|
||||
import org.geysermc.geyser.registry.loader.RegistryLoaders;
|
||||
import org.geysermc.geyser.registry.populator.BlockRegistryPopulator;
|
||||
import org.geysermc.geyser.registry.populator.CustomBlockRegistryPopulator;
|
||||
import org.geysermc.geyser.registry.populator.CustomSkullRegistryPopulator;
|
||||
import org.geysermc.geyser.registry.type.BlockMapping;
|
||||
import org.geysermc.geyser.registry.type.BlockMappings;
|
||||
import org.geysermc.geyser.registry.type.CustomSkull;
|
||||
import org.geysermc.geyser.translator.collision.BlockCollision;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@ -59,34 +61,32 @@ public class BlockRegistries {
|
||||
public static final VersionedRegistry<BlockMappings> BLOCKS = VersionedRegistry.create(RegistryLoaders.empty(Int2ObjectOpenHashMap::new));
|
||||
|
||||
/**
|
||||
* A mapped registry which stores Java to Bedrock block identifiers.
|
||||
* A registry which stores Java IDs to Java {@link BlockState}s, each with their specific state differences and a link
|
||||
* to the overarching block.
|
||||
*/
|
||||
public static final SimpleMappedRegistry<String, String> JAVA_TO_BEDROCK_IDENTIFIERS = SimpleMappedRegistry.create(RegistryLoaders.empty(Object2ObjectOpenHashMap::new));
|
||||
|
||||
/**
|
||||
* A registry which stores Java IDs to {@link BlockMapping}, containing miscellaneous information about
|
||||
* blocks and their behavior in many cases.
|
||||
*/
|
||||
public static final ArrayRegistry<BlockMapping> JAVA_BLOCKS = ArrayRegistry.create(RegistryLoaders.uninitialized());
|
||||
public static final ListRegistry<BlockState> BLOCK_STATES = ListRegistry.create(RegistryLoaders.empty(ArrayList::new));
|
||||
|
||||
/**
|
||||
* A mapped registry containing which holds block IDs to its {@link BlockCollision}.
|
||||
*/
|
||||
public static final IntMappedRegistry<BlockCollision> COLLISIONS;
|
||||
public static final ListRegistry<BlockCollision> COLLISIONS;
|
||||
|
||||
/**
|
||||
* A registry which stores Java IDs to {@link Block}, containing miscellaneous information about
|
||||
* blocks and their behavior in many cases.
|
||||
*/
|
||||
public static final ListRegistry<Block> JAVA_BLOCKS = ListRegistry.create(RegistryLoaders.empty(ArrayList::new));
|
||||
|
||||
/**
|
||||
* A mapped registry containing the Java identifiers to IDs.
|
||||
*/
|
||||
public static final MappedRegistry<String, Integer, Object2IntMap<String>> JAVA_IDENTIFIER_TO_ID = MappedRegistry.create(RegistryLoaders.empty(Object2IntOpenHashMap::new));
|
||||
|
||||
/**
|
||||
* A registry which stores unique Java IDs to its clean identifier
|
||||
* This is used in the statistics form.
|
||||
*/
|
||||
public static final ArrayRegistry<String> CLEAN_JAVA_IDENTIFIERS = ArrayRegistry.create(RegistryLoaders.uninitialized());
|
||||
|
||||
/**
|
||||
* A registry containing all the waterlogged blockstates.
|
||||
* Properties.WATERLOGGED should not be relied on for two reasons:
|
||||
* - Custom blocks
|
||||
* - Seagrass, kelp, and bubble columns are assumed waterlogged and don't have a waterlogged property
|
||||
*/
|
||||
public static final SimpleRegistry<BitSet> WATERLOGGED = SimpleRegistry.create(RegistryLoaders.empty(BitSet::new));
|
||||
|
||||
@ -131,12 +131,13 @@ public class BlockRegistries {
|
||||
public static final SimpleMappedRegistry<String, CustomSkull> CUSTOM_SKULLS = SimpleMappedRegistry.create(RegistryLoaders.empty(Object2ObjectOpenHashMap::new));
|
||||
|
||||
static {
|
||||
Blocks.VAULT.javaId(); // FIXME
|
||||
CustomSkullRegistryPopulator.populate();
|
||||
BlockRegistryPopulator.populate(BlockRegistryPopulator.Stage.PRE_INIT);
|
||||
CustomBlockRegistryPopulator.populate(CustomBlockRegistryPopulator.Stage.DEFINITION);
|
||||
CustomBlockRegistryPopulator.populate(CustomBlockRegistryPopulator.Stage.NON_VANILLA_REGISTRATION);
|
||||
BlockRegistryPopulator.populate(BlockRegistryPopulator.Stage.INIT_JAVA);
|
||||
COLLISIONS = IntMappedRegistry.create(Pair.of("org.geysermc.geyser.translator.collision.CollisionRemapper", "mappings/collision.json"), CollisionRegistryLoader::new);
|
||||
COLLISIONS = ListRegistry.create(Pair.of("org.geysermc.geyser.translator.collision.CollisionRemapper", "mappings/collisions.nbt"), CollisionRegistryLoader::new);
|
||||
CustomBlockRegistryPopulator.populate(CustomBlockRegistryPopulator.Stage.VANILLA_REGISTRATION);
|
||||
CustomBlockRegistryPopulator.populate(CustomBlockRegistryPopulator.Stage.CUSTOM_REGISTRATION);
|
||||
BlockRegistryPopulator.populate(BlockRegistryPopulator.Stage.INIT_BEDROCK);
|
||||
|
154
core/src/main/java/org/geysermc/geyser/registry/ListRegistry.java
Normale Datei
154
core/src/main/java/org/geysermc/geyser/registry/ListRegistry.java
Normale Datei
@ -0,0 +1,154 @@
|
||||
/*
|
||||
* 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.registry;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.registry.loader.RegistryLoader;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ListRegistry<M> extends Registry<List<M>> {
|
||||
private boolean frozen = false;
|
||||
|
||||
/**
|
||||
* Creates a new instance of this class with the given input and
|
||||
* {@link RegistryLoader}. The input specified is what the registry
|
||||
* loader needs to take in.
|
||||
*
|
||||
* @param input the input
|
||||
* @param registryLoader the registry loader
|
||||
*/
|
||||
protected <I> ListRegistry(I input, RegistryLoader<I, List<M>> registryLoader) {
|
||||
super(input, registryLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value registered by the given index.
|
||||
*
|
||||
* @param index the index
|
||||
* @return the value registered by the given index.
|
||||
*/
|
||||
@Nullable
|
||||
public M get(int index) {
|
||||
if (index >= this.mappings.size()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.mappings.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value registered by the given index or the default value
|
||||
* specified if null.
|
||||
*
|
||||
* @param index the index
|
||||
* @param defaultValue the default value
|
||||
* @return the value registered by the given key or the default value
|
||||
* specified if null.
|
||||
*/
|
||||
public M getOrDefault(int index, M defaultValue) {
|
||||
M value = this.get(index);
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new value into this registry with the given index.
|
||||
*
|
||||
* @param index the index
|
||||
* @param value the value
|
||||
* @return a new value into this registry with the given index.
|
||||
*/
|
||||
public M register(int index, M value) {
|
||||
if (this.frozen) {
|
||||
throw new IllegalStateException("Registry should not be modified after frozen!");
|
||||
}
|
||||
return this.mappings.set(index, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new value into this registry with the given index, even if this value would normally be outside
|
||||
* the range of a list.
|
||||
*
|
||||
* @param index the index
|
||||
* @param value the value
|
||||
* @param defaultValue the default value to fill empty spaces in the registry with.
|
||||
* @return a new value into this registry with the given index.
|
||||
*/
|
||||
public M registerWithAnyIndex(int index, M value, M defaultValue) {
|
||||
if (this.frozen) {
|
||||
throw new IllegalStateException("Registry should not be modified after frozen!");
|
||||
}
|
||||
if (this.mappings.size() <= index) {
|
||||
this.mappings.addAll(Collections.nCopies(index - this.mappings.size() + 1, defaultValue));
|
||||
}
|
||||
return this.mappings.set(index, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark this registry as unsuitable for new additions. The backing list will then be optimized for storage.
|
||||
*/
|
||||
public void freeze() {
|
||||
if (!this.frozen) {
|
||||
this.frozen = true;
|
||||
if (this.mappings instanceof ArrayList<M> arrayList) {
|
||||
arrayList.trimToSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new array registry with the given {@link RegistryLoader}. The
|
||||
* input type is not specified here, meaning the loader return type is either
|
||||
* predefined, or the registry is populated at a later point.
|
||||
*
|
||||
* @param registryLoader the registry loader
|
||||
* @param <I> the input type
|
||||
* @param <M> the returned mappings type
|
||||
* @return a new registry with the given RegistryLoader supplier
|
||||
*/
|
||||
public static <I, M> ListRegistry<M> create(RegistryLoader<I, List<M>> registryLoader) {
|
||||
return new ListRegistry<>(null, registryLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new integer mapped registry with the given {@link RegistryLoader} and input.
|
||||
*
|
||||
* @param registryLoader the registry loader
|
||||
* @param <I> the input
|
||||
* @param <M> the type value
|
||||
* @return a new registry with the given RegistryLoader supplier
|
||||
*/
|
||||
public static <I, M> ListRegistry<M> create(I input, Supplier<RegistryLoader<I, List<M>>> registryLoader) {
|
||||
return new ListRegistry<>(input, registryLoader.get());
|
||||
}
|
||||
}
|
@ -127,7 +127,10 @@ public final class Registries {
|
||||
*/
|
||||
public static final PacketTranslatorRegistry<Packet> JAVA_PACKET_TRANSLATORS = PacketTranslatorRegistry.create();
|
||||
|
||||
public static final SimpleRegistry<List<Item>> JAVA_ITEMS = SimpleRegistry.create(RegistryLoaders.empty(ArrayList::new));
|
||||
/**
|
||||
* A registry containing all Java items ordered by their network ID.
|
||||
*/
|
||||
public static final ListRegistry<Item> JAVA_ITEMS = ListRegistry.create(RegistryLoaders.empty(ArrayList::new));
|
||||
|
||||
public static final SimpleMappedRegistry<String, Item> JAVA_ITEM_IDENTIFIERS = SimpleMappedRegistry.create(RegistryLoaders.empty(Object2ObjectOpenHashMap::new));
|
||||
|
||||
|
@ -25,18 +25,19 @@
|
||||
|
||||
package org.geysermc.geyser.registry.loader;
|
||||
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import it.unimi.dsi.fastutil.Pair;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.nbt.NbtList;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtType;
|
||||
import org.cloudburstmc.nbt.NbtUtils;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.level.physics.BoundingBox;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.registry.type.BlockMapping;
|
||||
import org.geysermc.geyser.translator.collision.BlockCollision;
|
||||
import org.geysermc.geyser.translator.collision.CollisionRemapper;
|
||||
import org.geysermc.geyser.translator.collision.OtherCollision;
|
||||
@ -51,41 +52,43 @@ import java.util.regex.Pattern;
|
||||
/**
|
||||
* Loads collision data from the given resource path.
|
||||
*/
|
||||
public class CollisionRegistryLoader extends MultiResourceRegistryLoader<String, Int2ObjectMap<BlockCollision>> {
|
||||
public class CollisionRegistryLoader extends MultiResourceRegistryLoader<String, List<BlockCollision>> {
|
||||
|
||||
@Override
|
||||
public Int2ObjectMap<BlockCollision> load(Pair<String, String> input) {
|
||||
Int2ObjectMap<BlockCollision> collisions = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
public List<BlockCollision> load(Pair<String, String> input) {
|
||||
Map<Class<?>, CollisionInfo> annotationMap = new IdentityHashMap<>();
|
||||
for (Class<?> clazz : FileUtils.getGeneratedClassesForAnnotation(CollisionRemapper.class.getName())) {
|
||||
GeyserImpl.getInstance().getLogger().debug("Found annotated collision translator: " + clazz.getCanonicalName());
|
||||
|
||||
CollisionRemapper collisionRemapper = clazz.getAnnotation(CollisionRemapper.class);
|
||||
annotationMap.put(clazz, new CollisionInfo(collisionRemapper, Pattern.compile(collisionRemapper.regex()), Pattern.compile(collisionRemapper.paramRegex())));
|
||||
annotationMap.put(clazz, new CollisionInfo(collisionRemapper, Pattern.compile(collisionRemapper.regex())));
|
||||
}
|
||||
|
||||
// Load collision mappings file
|
||||
int[] indices;
|
||||
List<BoundingBox[]> collisionList;
|
||||
try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow(input.value())) {
|
||||
ArrayNode collisionNode = (ArrayNode) GeyserImpl.JSON_MAPPER.readTree(stream);
|
||||
collisionList = loadBoundingBoxes(collisionNode);
|
||||
NbtMap collisionData = (NbtMap) NbtUtils.createGZIPReader(stream).readTag();
|
||||
indices = collisionData.getIntArray("indices");
|
||||
//SuppressWarnings unchecked
|
||||
collisionList = loadBoundingBoxes(collisionData.getList("collisions", NbtType.LIST));
|
||||
} catch (Exception e) {
|
||||
throw new AssertionError("Unable to load collision data", e);
|
||||
}
|
||||
|
||||
BlockMapping[] blockMappings = BlockRegistries.JAVA_BLOCKS.get();
|
||||
List<BlockState> blockStates = BlockRegistries.BLOCK_STATES.get();
|
||||
var collisions = new ObjectArrayList<BlockCollision>(blockStates.size());
|
||||
|
||||
// Map of unique collisions to its instance
|
||||
Map<BlockCollision, BlockCollision> collisionInstances = new Object2ObjectOpenHashMap<>();
|
||||
for (int i = 0; i < blockMappings.length; i++) {
|
||||
BlockMapping blockMapping = blockMappings[i];
|
||||
if (blockMapping == null) {
|
||||
GeyserImpl.getInstance().getLogger().warning("Missing block mapping for Java block " + i);
|
||||
for (int i = 0; i < blockStates.size(); i++) {
|
||||
BlockState state = blockStates.get(i);
|
||||
if (state == null) {
|
||||
GeyserImpl.getInstance().getLogger().warning("Missing block state for Java block " + i);
|
||||
continue;
|
||||
}
|
||||
|
||||
BlockCollision newCollision = instantiateCollision(blockMapping, annotationMap, collisionList);
|
||||
BlockCollision newCollision = instantiateCollision(state, annotationMap, indices[i], collisionList);
|
||||
|
||||
if (newCollision != null) {
|
||||
// If there's an existing instance equal to this one, use that instead
|
||||
@ -97,33 +100,28 @@ public class CollisionRegistryLoader extends MultiResourceRegistryLoader<String,
|
||||
}
|
||||
}
|
||||
|
||||
collisions.put(i, newCollision);
|
||||
collisions.add(newCollision);
|
||||
}
|
||||
collisions.trim();
|
||||
return collisions;
|
||||
}
|
||||
|
||||
private @Nullable BlockCollision instantiateCollision(BlockMapping mapping, Map<Class<?>, CollisionInfo> annotationMap, List<BoundingBox[]> collisionList) {
|
||||
String[] blockIdParts = mapping.getJavaIdentifier().split("\\[");
|
||||
String blockName = blockIdParts[0].replace("minecraft:", "");
|
||||
String params = "";
|
||||
if (blockIdParts.length == 2) {
|
||||
params = "[" + blockIdParts[1];
|
||||
}
|
||||
int collisionIndex = mapping.getCollisionIndex();
|
||||
private @Nullable BlockCollision instantiateCollision(BlockState state, Map<Class<?>, CollisionInfo> annotationMap, int collisionIndex, List<BoundingBox[]> collisionList) {
|
||||
String blockName = state.block().javaIdentifier().value();
|
||||
|
||||
for (Map.Entry<Class<?>, CollisionInfo> collisionRemappers : annotationMap.entrySet()) {
|
||||
Class<?> type = collisionRemappers.getKey();
|
||||
CollisionInfo collisionInfo = collisionRemappers.getValue();
|
||||
CollisionRemapper annotation = collisionInfo.collisionRemapper;
|
||||
|
||||
if (collisionInfo.pattern.matcher(blockName).find() && collisionInfo.paramsPattern.matcher(params).find()) {
|
||||
if (collisionInfo.pattern.matcher(blockName).find()) {
|
||||
try {
|
||||
if (annotation.passDefaultBoxes()) {
|
||||
// Create an OtherCollision instance and get the bounding boxes
|
||||
BoundingBox[] defaultBoxes = collisionList.get(collisionIndex);
|
||||
return (BlockCollision) type.getDeclaredConstructor(String.class, BoundingBox[].class).newInstance(params, defaultBoxes);
|
||||
return (BlockCollision) type.getDeclaredConstructor(BlockState.class, BoundingBox[].class).newInstance(state, defaultBoxes);
|
||||
} else {
|
||||
return (BlockCollision) type.getDeclaredConstructor(String.class).newInstance(params);
|
||||
return (BlockCollision) type.getDeclaredConstructor(BlockState.class).newInstance(state);
|
||||
}
|
||||
} catch (IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
@ -138,25 +136,25 @@ public class CollisionRegistryLoader extends MultiResourceRegistryLoader<String,
|
||||
|
||||
// Unless some of the low IDs are changed, which is unlikely, the second item should always be full collision
|
||||
if (collisionIndex == 1) {
|
||||
return new SolidCollision(params);
|
||||
return new SolidCollision(state);
|
||||
}
|
||||
return new OtherCollision(collisionList.get(collisionIndex));
|
||||
}
|
||||
|
||||
private List<BoundingBox[]> loadBoundingBoxes(ArrayNode collisionNode) {
|
||||
private List<BoundingBox[]> loadBoundingBoxes(List<NbtList> collisionNode) {
|
||||
List<BoundingBox[]> collisions = new ObjectArrayList<>();
|
||||
for (int collisionIndex = 0; collisionIndex < collisionNode.size(); collisionIndex++) {
|
||||
ArrayNode boundingBoxArray = (ArrayNode) collisionNode.get(collisionIndex);
|
||||
@SuppressWarnings("unchecked") NbtList<NbtList<Double>> boundingBoxArray = (NbtList<NbtList<Double>>) collisionNode.get(collisionIndex);
|
||||
|
||||
BoundingBox[] boundingBoxes = new BoundingBox[boundingBoxArray.size()];
|
||||
for (int i = 0; i < boundingBoxArray.size(); i++) {
|
||||
ArrayNode boxProperties = (ArrayNode) boundingBoxArray.get(i);
|
||||
boundingBoxes[i] = new BoundingBox(boxProperties.get(0).asDouble(),
|
||||
boxProperties.get(1).asDouble(),
|
||||
boxProperties.get(2).asDouble(),
|
||||
boxProperties.get(3).asDouble(),
|
||||
boxProperties.get(4).asDouble(),
|
||||
boxProperties.get(5).asDouble());
|
||||
NbtList<Double> boxProperties = boundingBoxArray.get(i);
|
||||
boundingBoxes[i] = new BoundingBox(boxProperties.get(0),
|
||||
boxProperties.get(1),
|
||||
boxProperties.get(2),
|
||||
boxProperties.get(3),
|
||||
boxProperties.get(4),
|
||||
boxProperties.get(5));
|
||||
}
|
||||
|
||||
// Sorting by lowest Y first fixes some bugs
|
||||
@ -173,6 +171,5 @@ public class CollisionRegistryLoader extends MultiResourceRegistryLoader<String,
|
||||
public static class CollisionInfo {
|
||||
private final CollisionRemapper collisionRemapper;
|
||||
private final Pattern pattern;
|
||||
private final Pattern paramsPattern;
|
||||
}
|
||||
}
|
@ -32,36 +32,37 @@ import com.google.common.collect.Interner;
|
||||
import com.google.common.collect.Interners;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
import it.unimi.dsi.fastutil.objects.*;
|
||||
import org.cloudburstmc.blockstateupdater.BlockStateUpdater;
|
||||
import org.cloudburstmc.blockstateupdater.util.tagupdater.CompoundTagUpdaterContext;
|
||||
import org.cloudburstmc.nbt.NBTInputStream;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.cloudburstmc.nbt.NbtType;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v622.Bedrock_v622;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v630.Bedrock_v630;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v649.Bedrock_v649;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v662.Bedrock_v662;
|
||||
import org.cloudburstmc.nbt.*;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v671.Bedrock_v671;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v685.Bedrock_v685;
|
||||
import org.cloudburstmc.protocol.bedrock.data.BlockPropertyData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.block.custom.CustomBlockData;
|
||||
import org.geysermc.geyser.api.block.custom.CustomBlockState;
|
||||
import org.geysermc.geyser.api.block.custom.nonvanilla.JavaBlockState;
|
||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.level.block.Blocks;
|
||||
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.block.type.FlowerPotBlock;
|
||||
import org.geysermc.geyser.level.physics.PistonBehavior;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.registry.type.BlockMapping;
|
||||
import org.geysermc.geyser.registry.Registries;
|
||||
import org.geysermc.geyser.registry.type.BlockMappings;
|
||||
import org.geysermc.geyser.registry.type.GeyserBedrockBlock;
|
||||
import org.geysermc.geyser.util.BlockUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
/**
|
||||
@ -82,24 +83,11 @@ public final class BlockRegistryPopulator {
|
||||
interface Remapper {
|
||||
|
||||
NbtMap remap(NbtMap tag);
|
||||
|
||||
static Remapper of(BlockStateUpdater... updaters) {
|
||||
CompoundTagUpdaterContext context = new CompoundTagUpdaterContext();
|
||||
for (BlockStateUpdater updater : updaters) {
|
||||
updater.registerUpdaters(context);
|
||||
}
|
||||
|
||||
return tag -> {
|
||||
NbtMapBuilder updated = context.update(tag, 0).toBuilder();
|
||||
updated.remove("version"); // we already removed this, but the context adds it. remove it again.
|
||||
return updated.build();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static void populate(Stage stage) {
|
||||
switch (stage) {
|
||||
case PRE_INIT, POST_INIT -> nullifyBlocksNode();
|
||||
case PRE_INIT, POST_INIT -> nullifyBlocksNbt();
|
||||
case INIT_JAVA -> registerJavaBlocks();
|
||||
case INIT_BEDROCK -> registerBedrockBlocks();
|
||||
default -> throw new IllegalArgumentException("Unknown stage: " + stage);
|
||||
@ -107,24 +95,20 @@ public final class BlockRegistryPopulator {
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the raw blocks JSON until it is no longer needed.
|
||||
* Stores the raw blocks NBT until it is no longer needed.
|
||||
*/
|
||||
private static JsonNode BLOCKS_JSON;
|
||||
private static List<NbtMap> BLOCKS_NBT;
|
||||
private static int MIN_CUSTOM_RUNTIME_ID = -1;
|
||||
private static int JAVA_BLOCKS_SIZE = -1;
|
||||
|
||||
private static void nullifyBlocksNode() {
|
||||
BLOCKS_JSON = null;
|
||||
private static void nullifyBlocksNbt() {
|
||||
BLOCKS_NBT = null;
|
||||
}
|
||||
|
||||
private static void registerBedrockBlocks() {
|
||||
var blockMappers = ImmutableMap.<ObjectIntPair<String>, Remapper>builder()
|
||||
.put(ObjectIntPair.of("1_20_40", Bedrock_v622.CODEC.getProtocolVersion()), Conversion630_622::remapBlock)
|
||||
.put(ObjectIntPair.of("1_20_50", Bedrock_v630.CODEC.getProtocolVersion()), Conversion649_630::remapBlock)
|
||||
// Only changes in 1.20.60 are hard_stained_glass (an EDU only block)
|
||||
.put(ObjectIntPair.of("1_20_60", Bedrock_v649.CODEC.getProtocolVersion()), Conversion662_649::remapBlock)
|
||||
.put(ObjectIntPair.of("1_20_70", Bedrock_v662.CODEC.getProtocolVersion()), Conversion671_662::remapBlock)
|
||||
.put(ObjectIntPair.of("1_20_80", Bedrock_v671.CODEC.getProtocolVersion()), tag -> tag)
|
||||
.put(ObjectIntPair.of("1_20_80", Bedrock_v671.CODEC.getProtocolVersion()), Conversion685_671::remapBlock)
|
||||
.put(ObjectIntPair.of("1_21_0", Bedrock_v685.CODEC.getProtocolVersion()), tag -> tag)
|
||||
.build();
|
||||
|
||||
// We can keep this strong as nothing should be garbage collected
|
||||
@ -215,19 +199,34 @@ public final class BlockRegistryPopulator {
|
||||
|
||||
int javaRuntimeId = -1;
|
||||
|
||||
List<BlockState> javaBlockStates = BlockRegistries.BLOCK_STATES.get();
|
||||
|
||||
GeyserBedrockBlock airDefinition = null;
|
||||
BlockDefinition commandBlockDefinition = null;
|
||||
BlockDefinition mobSpawnerBlockDefinition = null;
|
||||
BlockDefinition waterDefinition = null;
|
||||
BlockDefinition movingBlockDefinition = null;
|
||||
Iterator<Map.Entry<String, JsonNode>> blocksIterator = BLOCKS_JSON.fields();
|
||||
Iterator<NbtMap> blocksIterator = BLOCKS_NBT.iterator();
|
||||
|
||||
Remapper stateMapper = blockMappers.get(palette);
|
||||
|
||||
GeyserBedrockBlock[] javaToBedrockBlocks = new GeyserBedrockBlock[JAVA_BLOCKS_SIZE];
|
||||
GeyserBedrockBlock[] javaToVanillaBedrockBlocks = new GeyserBedrockBlock[JAVA_BLOCKS_SIZE];
|
||||
|
||||
Map<String, NbtMap> flowerPotBlocks = new Object2ObjectOpenHashMap<>();
|
||||
var javaToBedrockIdentifiers = new Int2ObjectOpenHashMap<String>();
|
||||
Block lastBlockSeen = null;
|
||||
|
||||
// Stream isn't ideal.
|
||||
List<Block> javaPottable = BlockRegistries.JAVA_BLOCKS.get()
|
||||
.parallelStream()
|
||||
.flatMap(block -> {
|
||||
if (block instanceof FlowerPotBlock flowerPot && flowerPot.flower() != Blocks.AIR) {
|
||||
return Stream.of(flowerPot.flower());
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.toList();
|
||||
Map<Block, NbtMap> flowerPotBlocks = new Object2ObjectOpenHashMap<>();
|
||||
Map<NbtMap, BlockDefinition> itemFrames = new Object2ObjectOpenHashMap<>();
|
||||
|
||||
Set<BlockDefinition> jigsawDefinitions = new ObjectOpenHashSet<>();
|
||||
@ -236,10 +235,11 @@ public final class BlockRegistryPopulator {
|
||||
BlockMappings.BlockMappingsBuilder builder = BlockMappings.builder();
|
||||
while (blocksIterator.hasNext()) {
|
||||
javaRuntimeId++;
|
||||
Map.Entry<String, JsonNode> entry = blocksIterator.next();
|
||||
String javaId = entry.getKey();
|
||||
NbtMap entry = blocksIterator.next();
|
||||
BlockState blockState = javaBlockStates.get(javaRuntimeId);
|
||||
String javaId = blockState.toString();
|
||||
|
||||
NbtMap originalBedrockTag = buildBedrockState(entry.getValue());
|
||||
NbtMap originalBedrockTag = buildBedrockState(blockState, entry);
|
||||
NbtMap bedrockTag = stateMapper.remap(originalBedrockTag);
|
||||
|
||||
GeyserBedrockBlock vanillaBedrockDefinition = blockStateOrderedMap.get(bedrockTag);
|
||||
@ -271,35 +271,36 @@ public final class BlockRegistryPopulator {
|
||||
case "minecraft:moving_piston[facing=north,type=normal]" -> movingBlockDefinition = bedrockDefinition;
|
||||
}
|
||||
|
||||
if (javaId.contains("jigsaw")) {
|
||||
jigsawDefinitions.add(bedrockDefinition);
|
||||
}
|
||||
|
||||
if (javaId.contains("structure_block")) {
|
||||
int modeIndex = javaId.indexOf("mode=");
|
||||
if (modeIndex != -1) {
|
||||
int startIndex = modeIndex + 5; // Length of "mode=" is 5
|
||||
int endIndex = javaId.indexOf("]", startIndex);
|
||||
if (endIndex != -1) {
|
||||
String modeValue = javaId.substring(startIndex, endIndex);
|
||||
structureBlockDefinitions.put(modeValue.toUpperCase(), bedrockDefinition);
|
||||
}
|
||||
Block block = blockState.block();
|
||||
if (block != lastBlockSeen) {
|
||||
lastBlockSeen = block;
|
||||
String bedrockName = bedrockDefinition.getState().getString("name");
|
||||
if (!block.javaIdentifier().toString().equals(bedrockName)) {
|
||||
javaToBedrockIdentifiers.put(block.javaId(), bedrockName.substring("minecraft:".length()).intern());
|
||||
}
|
||||
}
|
||||
|
||||
boolean waterlogged = entry.getKey().contains("waterlogged=true")
|
||||
|| javaId.contains("minecraft:bubble_column") || javaId.contains("minecraft:kelp") || javaId.contains("seagrass");
|
||||
|
||||
if (waterlogged) {
|
||||
int finalJavaRuntimeId = javaRuntimeId;
|
||||
BlockRegistries.WATERLOGGED.register(set -> set.set(finalJavaRuntimeId));
|
||||
if (block == Blocks.JIGSAW) {
|
||||
jigsawDefinitions.add(bedrockDefinition);
|
||||
}
|
||||
|
||||
String cleanJavaIdentifier = BlockUtils.getCleanIdentifier(entry.getKey());
|
||||
if (block == Blocks.STRUCTURE_BLOCK) {
|
||||
String mode = blockState.getValue(Properties.STRUCTUREBLOCK_MODE);
|
||||
structureBlockDefinitions.put(mode.toUpperCase(Locale.ROOT), bedrockDefinition);
|
||||
}
|
||||
|
||||
boolean waterlogged = blockState.getValue(Properties.WATERLOGGED, false)
|
||||
|| block == Blocks.BUBBLE_COLUMN || block == Blocks.KELP || block == Blocks.KELP_PLANT
|
||||
|| block == Blocks.SEAGRASS || block == Blocks.TALL_SEAGRASS;
|
||||
|
||||
if (waterlogged) {
|
||||
BlockRegistries.WATERLOGGED.get().set(javaRuntimeId);
|
||||
}
|
||||
|
||||
// Get the tag needed for non-empty flower pots
|
||||
if (entry.getValue().get("pottable") != null) {
|
||||
flowerPotBlocks.put(cleanJavaIdentifier.intern(), blockStates.get(bedrockDefinition.getRuntimeId()));
|
||||
if (javaPottable.contains(block)) {
|
||||
// Specifically NOT putIfAbsent - mangrove propagule breaks otherwise
|
||||
flowerPotBlocks.put(block, blockStates.get(bedrockDefinition.getRuntimeId()));
|
||||
}
|
||||
|
||||
javaToVanillaBedrockBlocks[javaRuntimeId] = vanillaBedrockDefinition;
|
||||
@ -355,9 +356,12 @@ public final class BlockRegistryPopulator {
|
||||
|
||||
javaToVanillaBedrockBlocks[stateRuntimeId] = bedrockDefinition; // TODO: Check this?
|
||||
javaToBedrockBlocks[stateRuntimeId] = bedrockDefinition;
|
||||
javaToBedrockIdentifiers.put(entry.getKey().stateGroupId(), entry.getValue().block().identifier());
|
||||
}
|
||||
}
|
||||
|
||||
javaToBedrockIdentifiers.trim();
|
||||
|
||||
// Loop around again to find all item frame runtime IDs
|
||||
Object2ObjectMaps.fastForEach(blockStateOrderedMap, entry -> {
|
||||
String name = entry.getKey().getString("name");
|
||||
@ -369,6 +373,7 @@ public final class BlockRegistryPopulator {
|
||||
BlockRegistries.BLOCKS.register(palette.valueInt(), builder.bedrockRuntimeMap(bedrockRuntimeMap)
|
||||
.javaToBedrockBlocks(javaToBedrockBlocks)
|
||||
.javaToVanillaBedrockBlocks(javaToVanillaBedrockBlocks)
|
||||
.javaToBedrockIdentifiers(javaToBedrockIdentifiers)
|
||||
.stateDefinitionMap(blockStateOrderedMap)
|
||||
.itemFrames(itemFrames)
|
||||
.flowerPotBlocks(flowerPotBlocks)
|
||||
@ -383,208 +388,81 @@ public final class BlockRegistryPopulator {
|
||||
}
|
||||
|
||||
private static void registerJavaBlocks() {
|
||||
JsonNode blocksJson;
|
||||
try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow("mappings/blocks.json")) {
|
||||
blocksJson = GeyserImpl.JSON_MAPPER.readTree(stream);
|
||||
List<NbtMap> blocksNbt;
|
||||
try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow("mappings/blocks.nbt")) {
|
||||
blocksNbt = ((NbtMap) NbtUtils.createGZIPReader(stream).readTag())
|
||||
.getList("bedrock_mappings", NbtType.COMPOUND);
|
||||
} catch (Exception e) {
|
||||
throw new AssertionError("Unable to load Java block mappings", e);
|
||||
}
|
||||
|
||||
JAVA_BLOCKS_SIZE = blocksJson.size();
|
||||
JAVA_BLOCKS_SIZE = BlockRegistries.BLOCK_STATES.get().size();
|
||||
|
||||
if (!BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().isEmpty()) {
|
||||
MIN_CUSTOM_RUNTIME_ID = BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().keySet().stream().min(Comparator.comparing(JavaBlockState::javaId)).orElseThrow().javaId();
|
||||
int maxCustomRuntimeID = BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().keySet().stream().max(Comparator.comparing(JavaBlockState::javaId)).orElseThrow().javaId();
|
||||
|
||||
if (MIN_CUSTOM_RUNTIME_ID < blocksJson.size()) {
|
||||
if (MIN_CUSTOM_RUNTIME_ID < blocksNbt.size()) {
|
||||
throw new RuntimeException("Non vanilla custom block state overrides runtime ID must start after the last vanilla block state (" + JAVA_BLOCKS_SIZE + ")");
|
||||
}
|
||||
|
||||
JAVA_BLOCKS_SIZE = maxCustomRuntimeID + 1; // Runtime ids start at 0, so we need to add 1
|
||||
}
|
||||
|
||||
BlockRegistries.JAVA_BLOCKS.set(new BlockMapping[JAVA_BLOCKS_SIZE]); // Set array size to number of blockstates
|
||||
|
||||
Deque<String> cleanIdentifiers = new ArrayDeque<>();
|
||||
|
||||
int javaRuntimeId = -1;
|
||||
int cobwebBlockId = -1;
|
||||
int furnaceRuntimeId = -1;
|
||||
int furnaceLitRuntimeId = -1;
|
||||
int honeyBlockRuntimeId = -1;
|
||||
int slimeBlockRuntimeId = -1;
|
||||
int spawnerRuntimeId = -1;
|
||||
int uniqueJavaId = -1;
|
||||
int waterRuntimeId = -1;
|
||||
Iterator<Map.Entry<String, JsonNode>> blocksIterator = blocksJson.fields();
|
||||
while (blocksIterator.hasNext()) {
|
||||
for (BlockState javaBlockState : BlockRegistries.BLOCK_STATES.get()) {
|
||||
javaRuntimeId++;
|
||||
Map.Entry<String, JsonNode> entry = blocksIterator.next();
|
||||
String javaId = entry.getKey();
|
||||
|
||||
// TODO fix this, (no block should have a null hardness)
|
||||
BlockMapping.BlockMappingBuilder builder = BlockMapping.builder();
|
||||
JsonNode hardnessNode = entry.getValue().get("block_hardness");
|
||||
if (hardnessNode != null) {
|
||||
builder.hardness(hardnessNode.floatValue());
|
||||
}
|
||||
|
||||
JsonNode canBreakWithHandNode = entry.getValue().get("can_break_with_hand");
|
||||
if (canBreakWithHandNode != null) {
|
||||
builder.canBreakWithHand(canBreakWithHandNode.booleanValue());
|
||||
} else {
|
||||
builder.canBreakWithHand(false);
|
||||
}
|
||||
|
||||
JsonNode collisionIndexNode = entry.getValue().get("collision_index");
|
||||
if (hardnessNode != null) {
|
||||
builder.collisionIndex(collisionIndexNode.intValue());
|
||||
}
|
||||
|
||||
JsonNode pickItemNode = entry.getValue().get("pick_item");
|
||||
if (pickItemNode != null) {
|
||||
builder.pickItem(pickItemNode.textValue().intern());
|
||||
}
|
||||
|
||||
if (javaId.equals("minecraft:obsidian") || javaId.equals("minecraft:crying_obsidian") || javaId.startsWith("minecraft:respawn_anchor") || javaId.startsWith("minecraft:reinforced_deepslate")) {
|
||||
builder.pistonBehavior(PistonBehavior.BLOCK);
|
||||
} else {
|
||||
JsonNode pistonBehaviorNode = entry.getValue().get("piston_behavior");
|
||||
if (pistonBehaviorNode != null) {
|
||||
builder.pistonBehavior(PistonBehavior.getByName(pistonBehaviorNode.textValue()));
|
||||
} else {
|
||||
builder.pistonBehavior(PistonBehavior.NORMAL);
|
||||
}
|
||||
}
|
||||
|
||||
JsonNode hasBlockEntityNode = entry.getValue().get("has_block_entity");
|
||||
if (hasBlockEntityNode != null) {
|
||||
builder.isBlockEntity(hasBlockEntityNode.booleanValue());
|
||||
} else {
|
||||
builder.isBlockEntity(false);
|
||||
}
|
||||
|
||||
BlockStateValues.storeBlockStateValues(entry.getKey(), javaRuntimeId, entry.getValue());
|
||||
|
||||
String cleanJavaIdentifier = BlockUtils.getCleanIdentifier(entry.getKey());
|
||||
String bedrockIdentifier = entry.getValue().get("bedrock_identifier").asText();
|
||||
|
||||
if (!cleanJavaIdentifier.equals(cleanIdentifiers.peekLast())) {
|
||||
uniqueJavaId++;
|
||||
cleanIdentifiers.add(cleanJavaIdentifier.intern());
|
||||
}
|
||||
|
||||
builder.javaIdentifier(javaId);
|
||||
builder.javaBlockId(uniqueJavaId);
|
||||
String javaId = javaBlockState.toString().intern();
|
||||
|
||||
BlockRegistries.JAVA_IDENTIFIER_TO_ID.register(javaId, javaRuntimeId);
|
||||
BlockRegistries.JAVA_BLOCKS.register(javaRuntimeId, builder.build());
|
||||
|
||||
// Keeping this here since this is currently unchanged between versions
|
||||
// It's possible to only have this store differences in names, but the key set of all Java names is used in sending command suggestions
|
||||
BlockRegistries.JAVA_TO_BEDROCK_IDENTIFIERS.register(cleanJavaIdentifier.intern(), bedrockIdentifier.intern());
|
||||
|
||||
if (javaId.contains("cobweb")) {
|
||||
cobwebBlockId = uniqueJavaId;
|
||||
|
||||
} else if (javaId.startsWith("minecraft:furnace[facing=north")) {
|
||||
if (javaId.contains("lit=true")) {
|
||||
furnaceLitRuntimeId = javaRuntimeId;
|
||||
} else {
|
||||
furnaceRuntimeId = javaRuntimeId;
|
||||
}
|
||||
|
||||
} else if (javaId.startsWith("minecraft:spawner")) {
|
||||
spawnerRuntimeId = javaRuntimeId;
|
||||
|
||||
} else if ("minecraft:water[level=0]".equals(javaId)) {
|
||||
waterRuntimeId = javaRuntimeId;
|
||||
} else if (javaId.equals("minecraft:honey_block")) {
|
||||
honeyBlockRuntimeId = javaRuntimeId;
|
||||
} else if (javaId.equals("minecraft:slime_block")) {
|
||||
slimeBlockRuntimeId = javaRuntimeId;
|
||||
}
|
||||
}
|
||||
|
||||
if (cobwebBlockId == -1) {
|
||||
throw new AssertionError("Unable to find cobwebs in palette");
|
||||
}
|
||||
BlockStateValues.JAVA_COBWEB_ID = cobwebBlockId;
|
||||
|
||||
if (furnaceRuntimeId == -1) {
|
||||
throw new AssertionError("Unable to find furnace in palette");
|
||||
}
|
||||
BlockStateValues.JAVA_FURNACE_ID = furnaceRuntimeId;
|
||||
|
||||
if (furnaceLitRuntimeId == -1) {
|
||||
throw new AssertionError("Unable to find lit furnace in palette");
|
||||
}
|
||||
BlockStateValues.JAVA_FURNACE_LIT_ID = furnaceLitRuntimeId;
|
||||
|
||||
if (honeyBlockRuntimeId == -1) {
|
||||
throw new AssertionError("Unable to find honey block in palette");
|
||||
}
|
||||
BlockStateValues.JAVA_HONEY_BLOCK_ID = honeyBlockRuntimeId;
|
||||
|
||||
if (slimeBlockRuntimeId == -1) {
|
||||
throw new AssertionError("Unable to find slime block in palette");
|
||||
}
|
||||
BlockStateValues.JAVA_SLIME_BLOCK_ID = slimeBlockRuntimeId;
|
||||
|
||||
if (spawnerRuntimeId == -1) {
|
||||
throw new AssertionError("Unable to find spawner in palette");
|
||||
}
|
||||
BlockStateValues.JAVA_SPAWNER_ID = spawnerRuntimeId;
|
||||
|
||||
if (waterRuntimeId == -1) {
|
||||
throw new AssertionError("Unable to find Java water in palette");
|
||||
}
|
||||
BlockStateValues.JAVA_WATER_ID = waterRuntimeId;
|
||||
|
||||
if (!BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().isEmpty()) {
|
||||
Set<Integer> usedNonVanillaRuntimeIDs = new HashSet<>();
|
||||
IntSet usedNonVanillaRuntimeIDs = new IntOpenHashSet();
|
||||
|
||||
for (JavaBlockState javaBlockState : BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().keySet()) {
|
||||
if (!usedNonVanillaRuntimeIDs.add(javaBlockState.javaId())) {
|
||||
throw new RuntimeException("Duplicate runtime ID " + javaBlockState.javaId() + " for non vanilla Java block state " + javaBlockState.identifier());
|
||||
}
|
||||
|
||||
CustomBlockState customBlockState = BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().get(javaBlockState);
|
||||
|
||||
String javaId = javaBlockState.identifier();
|
||||
int stateRuntimeId = javaBlockState.javaId();
|
||||
String pistonBehavior = javaBlockState.pistonBehavior();
|
||||
BlockMapping blockMapping = BlockMapping.builder()
|
||||
.canBreakWithHand(javaBlockState.canBreakWithHand())
|
||||
.pickItem(javaBlockState.pickItem())
|
||||
.isNonVanilla(true)
|
||||
.javaIdentifier(javaId)
|
||||
.javaBlockId(javaBlockState.stateGroupId())
|
||||
.hardness(javaBlockState.blockHardness())
|
||||
.pistonBehavior(pistonBehavior == null ? PistonBehavior.NORMAL : PistonBehavior.getByName(pistonBehavior))
|
||||
.isBlockEntity(javaBlockState.hasBlockEntity())
|
||||
.build();
|
||||
|
||||
String cleanJavaIdentifier = BlockUtils.getCleanIdentifier(javaBlockState.identifier());
|
||||
String bedrockIdentifier = customBlockState.block().identifier();
|
||||
|
||||
if (!cleanJavaIdentifier.equals(cleanIdentifiers.peekLast())) {
|
||||
uniqueJavaId++;
|
||||
cleanIdentifiers.add(cleanJavaIdentifier.intern());
|
||||
Block.Builder builder = Block.builder()
|
||||
.destroyTime(javaBlockState.blockHardness())
|
||||
.pushReaction(pistonBehavior == null ? PistonBehavior.NORMAL : PistonBehavior.getByName(pistonBehavior));
|
||||
if (!javaBlockState.canBreakWithHand()) {
|
||||
builder.requiresCorrectToolForDrops();
|
||||
}
|
||||
if (javaBlockState.hasBlockEntity()) {
|
||||
builder.setBlockEntity();
|
||||
}
|
||||
String cleanJavaIdentifier = BlockUtils.getCleanIdentifier(javaBlockState.identifier());
|
||||
String pickItem = javaBlockState.pickItem();
|
||||
Block block = new Block(cleanJavaIdentifier, builder) {
|
||||
@Override
|
||||
public ItemStack pickItem(BlockState state) {
|
||||
if (this.item == null) {
|
||||
this.item = Registries.JAVA_ITEM_IDENTIFIERS.get(pickItem);
|
||||
if (this.item == null) {
|
||||
GeyserImpl.getInstance().getLogger().warning("We could not find item " + pickItem
|
||||
+ " for getting the item for block " + javaBlockState.identifier());
|
||||
this.item = Items.AIR;
|
||||
}
|
||||
}
|
||||
return new ItemStack(this.item.javaId());
|
||||
}
|
||||
};
|
||||
block.setJavaId(javaBlockState.stateGroupId());
|
||||
|
||||
BlockRegistries.JAVA_BLOCKS.registerWithAnyIndex(javaBlockState.stateGroupId(), block, Blocks.AIR);
|
||||
BlockRegistries.JAVA_IDENTIFIER_TO_ID.register(javaId, stateRuntimeId);
|
||||
BlockRegistries.JAVA_BLOCKS.register(stateRuntimeId, blockMapping);
|
||||
|
||||
// Keeping this here since this is currently unchanged between versions
|
||||
// It's possible to only have this store differences in names, but the key set of all Java names is used in sending command suggestions
|
||||
BlockRegistries.JAVA_TO_BEDROCK_IDENTIFIERS.register(cleanJavaIdentifier.intern(), bedrockIdentifier.intern());
|
||||
BlockRegistries.BLOCK_STATES.register(stateRuntimeId, new BlockState(block, stateRuntimeId));
|
||||
}
|
||||
}
|
||||
|
||||
BlockRegistries.CLEAN_JAVA_IDENTIFIERS.set(cleanIdentifiers.toArray(new String[0]));
|
||||
|
||||
BLOCKS_JSON = blocksJson;
|
||||
BLOCKS_NBT = blocksNbt;
|
||||
|
||||
JsonNode blockInteractionsJson;
|
||||
try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow("mappings/interactions.json")) {
|
||||
@ -595,6 +473,8 @@ public final class BlockRegistryPopulator {
|
||||
|
||||
BlockRegistries.INTERACTIVE.set(toBlockStateSet((ArrayNode) blockInteractionsJson.get("always_consumes")));
|
||||
BlockRegistries.INTERACTIVE_MAY_BUILD.set(toBlockStateSet((ArrayNode) blockInteractionsJson.get("requires_may_build")));
|
||||
|
||||
BlockRegistries.BLOCK_STATES.freeze();
|
||||
}
|
||||
|
||||
private static BitSet toBlockStateSet(ArrayNode node) {
|
||||
@ -605,29 +485,11 @@ public final class BlockRegistryPopulator {
|
||||
return blockStateSet;
|
||||
}
|
||||
|
||||
private static NbtMap buildBedrockState(JsonNode node) {
|
||||
private static NbtMap buildBedrockState(BlockState state, NbtMap nbt) {
|
||||
NbtMapBuilder tagBuilder = NbtMap.builder();
|
||||
String bedrockIdentifier = node.get("bedrock_identifier").textValue();
|
||||
String bedrockIdentifier = "minecraft:" + nbt.getString("bedrock_identifier", state.block().javaIdentifier().value());
|
||||
tagBuilder.putString("name", bedrockIdentifier);
|
||||
|
||||
NbtMapBuilder statesBuilder = NbtMap.builder();
|
||||
|
||||
// check for states
|
||||
JsonNode states = node.get("bedrock_states");
|
||||
if (states != null) {
|
||||
Iterator<Map.Entry<String, JsonNode>> statesIterator = states.fields();
|
||||
|
||||
while (statesIterator.hasNext()) {
|
||||
Map.Entry<String, JsonNode> stateEntry = statesIterator.next();
|
||||
JsonNode stateValue = stateEntry.getValue();
|
||||
switch (stateValue.getNodeType()) {
|
||||
case BOOLEAN -> statesBuilder.putBoolean(stateEntry.getKey(), stateValue.booleanValue());
|
||||
case STRING -> statesBuilder.putString(stateEntry.getKey(), stateValue.textValue());
|
||||
case NUMBER -> statesBuilder.putInt(stateEntry.getKey(), stateValue.intValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
tagBuilder.put("states", statesBuilder.build());
|
||||
tagBuilder.put("states", nbt.getCompound("state"));
|
||||
return tagBuilder.build();
|
||||
}
|
||||
|
||||
|
@ -1,217 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.registry.populator;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.registry.type.GeyserMappingItem;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Backwards-maps the blocks and items of 1.20.50 (630) to 1.20.40 (622)
|
||||
*/
|
||||
class Conversion630_622 {
|
||||
|
||||
private static final List<String> NEW_STONES = List.of("minecraft:stone", "minecraft:granite", "minecraft:polished_granite", "minecraft:diorite", "minecraft:polished_diorite", "minecraft:andesite", "minecraft:polished_andesite");
|
||||
private static final List<String> NEW_WOODS = List.of("minecraft:oak_planks", "minecraft:spruce_planks", "minecraft:birch_planks", "minecraft:jungle_planks", "minecraft:acacia_planks", "minecraft:dark_oak_planks");
|
||||
|
||||
private static final Map<String, String> ITEMS = new Object2ObjectOpenHashMap<>();
|
||||
|
||||
static {
|
||||
ITEMS.put("minecraft:acacia_planks", "minecraft:planks");
|
||||
ITEMS.put("minecraft:birch_planks", "minecraft:planks");
|
||||
ITEMS.put("minecraft:dark_oak_planks", "minecraft:planks");
|
||||
ITEMS.put("minecraft:jungle_planks", "minecraft:planks");
|
||||
ITEMS.put("minecraft:oak_planks", "minecraft:planks");
|
||||
ITEMS.put("minecraft:spruce_planks", "minecraft:planks");
|
||||
|
||||
ITEMS.put("minecraft:diorite", "minecraft:stone");
|
||||
ITEMS.put("minecraft:andesite", "minecraft:stone");
|
||||
ITEMS.put("minecraft:granite", "minecraft:stone");
|
||||
ITEMS.put("minecraft:polished_andesite", "minecraft:stone");
|
||||
ITEMS.put("minecraft:polished_diorite", "minecraft:stone");
|
||||
ITEMS.put("minecraft:polished_granite", "minecraft:stone");
|
||||
|
||||
ITEMS.put("minecraft:chiseled_tuff", "minecraft:chiseled_deepslate");
|
||||
ITEMS.put("minecraft:chiseled_tuff_bricks", "minecraft:chiseled_deepslate");
|
||||
ITEMS.put("minecraft:polished_tuff", "minecraft:polished_deepslate");
|
||||
ITEMS.put("minecraft:polished_tuff_double_slab", "minecraft:polished_deepslate_double_slab");
|
||||
ITEMS.put("minecraft:polished_tuff_slab", "minecraft:polished_deepslate_slab");
|
||||
ITEMS.put("minecraft:polished_tuff_stairs", "minecraft:polished_deepslate_stairs");
|
||||
ITEMS.put("minecraft:polished_tuff_wall", "minecraft:polished_deepslate_wall");
|
||||
ITEMS.put("minecraft:tuff_brick_double_slab", "minecraft:deepslate_brick_double_slab");
|
||||
ITEMS.put("minecraft:tuff_brick_slab", "minecraft:deepslate_brick_slab");
|
||||
ITEMS.put("minecraft:tuff_brick_stairs", "minecraft:deepslate_brick_stairs");
|
||||
ITEMS.put("minecraft:tuff_brick_wall", "minecraft:deepslate_brick_wall");
|
||||
ITEMS.put("minecraft:tuff_bricks", "minecraft:deepslate_bricks");
|
||||
ITEMS.put("minecraft:tuff_double_slab", "minecraft:cobbled_deepslate_double_slab");
|
||||
ITEMS.put("minecraft:tuff_slab", "minecraft:cobbled_deepslate_slab");
|
||||
ITEMS.put("minecraft:tuff_stairs", "minecraft:cobbled_deepslate_stairs");
|
||||
ITEMS.put("minecraft:tuff_wall", "minecraft:cobbled_deepslate_wall");
|
||||
|
||||
ITEMS.put("minecraft:chiseled_copper", "minecraft:copper_block");
|
||||
ITEMS.put("minecraft:copper_bulb", "minecraft:copper_block");
|
||||
ITEMS.put("minecraft:copper_door", "minecraft:iron_door");
|
||||
ITEMS.put("minecraft:copper_grate", "minecraft:raw_iron_block");
|
||||
ITEMS.put("minecraft:copper_trapdoor", "minecraft:iron_trapdoor");
|
||||
ITEMS.put("minecraft:exposed_chiseled_copper", "minecraft:exposed_copper");
|
||||
ITEMS.put("minecraft:exposed_copper_bulb", "minecraft:exposed_copper");
|
||||
ITEMS.put("minecraft:exposed_copper_door", "minecraft:iron_door");
|
||||
ITEMS.put("minecraft:exposed_copper_grate", "minecraft:raw_iron_block");
|
||||
ITEMS.put("minecraft:exposed_copper_trapdoor", "minecraft:iron_trapdoor");
|
||||
ITEMS.put("minecraft:oxidized_chiseled_copper", "minecraft:oxidized_copper");
|
||||
ITEMS.put("minecraft:oxidized_copper_bulb", "minecraft:oxidized_copper");
|
||||
ITEMS.put("minecraft:oxidized_copper_door", "minecraft:iron_door");
|
||||
ITEMS.put("minecraft:oxidized_copper_grate", "minecraft:raw_iron_block");
|
||||
ITEMS.put("minecraft:oxidized_copper_trapdoor", "minecraft:iron_trapdoor");
|
||||
ITEMS.put("minecraft:waxed_chiseled_copper", "minecraft:waxed_copper");
|
||||
ITEMS.put("minecraft:waxed_copper_bulb", "minecraft:waxed_copper");
|
||||
ITEMS.put("minecraft:waxed_copper_door", "minecraft:iron_door");
|
||||
ITEMS.put("minecraft:waxed_copper_grate", "minecraft:raw_iron_block");
|
||||
ITEMS.put("minecraft:waxed_copper_trapdoor", "minecraft:iron_trapdoor");
|
||||
ITEMS.put("minecraft:waxed_exposed_chiseled_copper", "minecraft:waxed_exposed_copper");
|
||||
ITEMS.put("minecraft:waxed_exposed_copper_bulb", "minecraft:waxed_exposed_copper");
|
||||
ITEMS.put("minecraft:waxed_exposed_copper_door", "minecraft:iron_door");
|
||||
ITEMS.put("minecraft:waxed_exposed_copper_grate", "minecraft:raw_iron_block");
|
||||
ITEMS.put("minecraft:waxed_exposed_copper_trapdoor", "minecraft:iron_trapdoor");
|
||||
ITEMS.put("minecraft:waxed_oxidized_chiseled_copper", "minecraft:waxed_oxidized_copper");
|
||||
ITEMS.put("minecraft:waxed_oxidized_copper_bulb", "minecraft:waxed_oxidized_copper");
|
||||
ITEMS.put("minecraft:waxed_oxidized_copper_door", "minecraft:iron_door");
|
||||
ITEMS.put("minecraft:waxed_oxidized_copper_grate", "minecraft:raw_iron_block");
|
||||
ITEMS.put("minecraft:waxed_oxidized_copper_trapdoor", "minecraft:iron_trapdoor");
|
||||
ITEMS.put("minecraft:waxed_weathered_chiseled_copper", "minecraft:waxed_weathered_copper");
|
||||
ITEMS.put("minecraft:waxed_weathered_copper_bulb", "minecraft:waxed_weathered_copper");
|
||||
ITEMS.put("minecraft:waxed_weathered_copper_door", "minecraft:iron_door");
|
||||
ITEMS.put("minecraft:waxed_weathered_copper_grate", "minecraft:raw_iron_block");
|
||||
ITEMS.put("minecraft:waxed_weathered_copper_trapdoor", "minecraft:iron_trapdoor");
|
||||
ITEMS.put("minecraft:weathered_chiseled_copper", "minecraft:weathered_copper");
|
||||
ITEMS.put("minecraft:weathered_copper_bulb", "minecraft:weathered_copper");
|
||||
ITEMS.put("minecraft:weathered_copper_door", "minecraft:iron_door");
|
||||
ITEMS.put("minecraft:weathered_copper_grate", "minecraft:raw_iron_block");
|
||||
ITEMS.put("minecraft:weathered_copper_trapdoor", "minecraft:iron_trapdoor");
|
||||
|
||||
ITEMS.put("minecraft:crafter", "minecraft:crafting_table");
|
||||
}
|
||||
|
||||
static GeyserMappingItem remapItem(@SuppressWarnings("unused") Item item, GeyserMappingItem mapping) {
|
||||
mapping = Conversion649_630.remapItem(item, mapping);
|
||||
|
||||
String replacement = ITEMS.get(mapping.getBedrockIdentifier());
|
||||
if (replacement == null) {
|
||||
return mapping;
|
||||
} else {
|
||||
return mapping.withBedrockIdentifier(replacement);
|
||||
}
|
||||
}
|
||||
|
||||
static NbtMap remapBlock(NbtMap tag) {
|
||||
tag = Conversion649_630.remapBlock(tag);
|
||||
|
||||
final String name = tag.getString("name");
|
||||
|
||||
String replacement;
|
||||
if (NEW_STONES.contains(name) || NEW_WOODS.contains(name)) {
|
||||
|
||||
String typeKey;
|
||||
String type = name.substring(10);
|
||||
if (NEW_STONES.contains(name)) {
|
||||
replacement = "minecraft:stone";
|
||||
typeKey = "stone_type";
|
||||
if (type.startsWith("polished_")) {
|
||||
type = type.substring(9) + "_smooth";
|
||||
}
|
||||
} else {
|
||||
replacement = "minecraft:planks";
|
||||
typeKey = "wood_type";
|
||||
type = type.substring(0, type.indexOf("_planks"));
|
||||
}
|
||||
|
||||
return tag.toBuilder()
|
||||
.putString("name", replacement)
|
||||
.putCompound("states", NbtMap.builder().putString(typeKey, type).build())
|
||||
.build();
|
||||
} else if (name.contains("tuff") && !name.equals("minecraft:tuff")) {
|
||||
|
||||
if (name.contains("brick") || name.contains("polished") || name.contains("chiseled")) {
|
||||
replacement = name.replace("tuff", "deepslate");
|
||||
|
||||
if (name.contains("chiseled")) {
|
||||
// chiseled deepslate bricks don't exist. just use chiseled deepslate instead
|
||||
replacement = replacement.replace("_bricks", "");
|
||||
}
|
||||
} else {
|
||||
replacement = name.replace("tuff", "cobbled_deepslate");
|
||||
}
|
||||
|
||||
return tag.toBuilder()
|
||||
.putString("name", replacement)
|
||||
.build();
|
||||
} else if (name.contains("copper")) {
|
||||
|
||||
boolean removeStates = false;
|
||||
if (name.contains("chiseled")) {
|
||||
replacement = name.replace("_chiseled", ""); // special chiseled
|
||||
replacement = replacement.replace("chiseled_", ""); // plain chiseled
|
||||
} else if (name.endsWith("bulb")) {
|
||||
replacement = name.replace("_bulb", "");
|
||||
removeStates = true;
|
||||
} else if (name.endsWith("grate")) {
|
||||
replacement = "minecraft:raw_iron_block";
|
||||
} else if (name.endsWith("door")) {
|
||||
if (name.contains("trap")) {
|
||||
replacement = "minecraft:iron_trapdoor";
|
||||
} else {
|
||||
replacement = "minecraft:iron_door";
|
||||
}
|
||||
} else {
|
||||
return tag;
|
||||
}
|
||||
|
||||
if (replacement.endsWith(":copper")) {
|
||||
// case for plain chiseled copper and plain bulb
|
||||
replacement = replacement + "_block";
|
||||
}
|
||||
|
||||
NbtMapBuilder builder = tag.toBuilder();
|
||||
builder.putString("name", replacement);
|
||||
if (removeStates) {
|
||||
builder.putCompound("states", NbtMap.EMPTY);
|
||||
}
|
||||
return builder.build();
|
||||
} else if (name.equals("minecraft:crafter")) {
|
||||
NbtMapBuilder builder = tag.toBuilder();
|
||||
builder.put("name", "minecraft:crafting_table");
|
||||
builder.put("states", NbtMap.EMPTY);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
return tag;
|
||||
}
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
/*
|
||||
* 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.registry.populator;
|
||||
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.registry.type.GeyserMappingItem;
|
||||
|
||||
public class Conversion649_630 {
|
||||
|
||||
static GeyserMappingItem remapItem(@SuppressWarnings("unused") Item item, GeyserMappingItem mapping) {
|
||||
mapping = Conversion662_649.remapItem(item, mapping);
|
||||
|
||||
String identifer = mapping.getBedrockIdentifier();
|
||||
|
||||
switch (identifer) {
|
||||
case "minecraft:armadillo_scute", "minecraft:turtle_scute" -> { return mapping.withBedrockIdentifier("minecraft:scute"); }
|
||||
case "minecraft:armadillo_spawn_egg" -> { return mapping.withBedrockIdentifier("minecraft:rabbit_spawn_egg"); }
|
||||
case "minecraft:trial_spawner" -> { return mapping.withBedrockIdentifier("minecraft:mob_spawner"); }
|
||||
case "minecraft:trial_key" -> { return mapping.withBedrockIdentifier("minecraft:echo_shard"); }
|
||||
case "minecraft:wolf_armor" -> { return mapping.withBedrockIdentifier("minecraft:leather_horse_armor"); }
|
||||
default -> { return mapping; }
|
||||
}
|
||||
}
|
||||
|
||||
static NbtMap remapBlock(NbtMap tag) {
|
||||
tag = Conversion662_649.remapBlock(tag);
|
||||
|
||||
final String name = tag.getString("name");
|
||||
|
||||
if (name.equals("minecraft:trial_spawner")) {
|
||||
NbtMapBuilder builder = tag.toBuilder()
|
||||
.putString("name", "minecraft:mob_spawner")
|
||||
.putCompound("states", NbtMap.EMPTY);
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
return tag;
|
||||
}
|
||||
}
|
@ -1,187 +0,0 @@
|
||||
/*
|
||||
* 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.registry.populator;
|
||||
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.registry.type.GeyserMappingItem;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Conversion662_649 {
|
||||
|
||||
private static final List<String> NEW_MISC = List.of("minecraft:grass_block", "minecraft:vault");
|
||||
private static final List<String> NEW_WOODS = List.of("minecraft:oak_wood", "minecraft:spruce_wood", "minecraft:birch_wood", "minecraft:jungle_wood", "minecraft:acacia_wood", "minecraft:dark_oak_wood", "minecraft:stripped_oak_wood", "minecraft:stripped_spruce_wood", "minecraft:stripped_birch_wood", "minecraft:stripped_jungle_wood", "minecraft:stripped_acacia_wood", "minecraft:stripped_dark_oak_wood");
|
||||
private static final List<String> NEW_LEAVES = List.of("minecraft:oak_leaves", "minecraft:spruce_leaves", "minecraft:birch_leaves", "minecraft:jungle_leaves");
|
||||
private static final List<String> NEW_LEAVES2 = List.of("minecraft:acacia_leaves", "minecraft:dark_oak_leaves");
|
||||
private static final List<String> NEW_SLABS = List.of("minecraft:oak_slab", "minecraft:spruce_slab", "minecraft:birch_slab", "minecraft:jungle_slab", "minecraft:acacia_slab", "minecraft:dark_oak_slab", "minecraft:oak_double_slab", "minecraft:spruce_double_slab", "minecraft:birch_double_slab", "minecraft:jungle_double_slab", "minecraft:acacia_double_slab", "minecraft:dark_oak_double_slab");
|
||||
private static final List<String> NEW_BLOCKS = Stream.of(NEW_WOODS, NEW_LEAVES, NEW_LEAVES2, NEW_SLABS, NEW_MISC).flatMap(List::stream).toList();
|
||||
|
||||
|
||||
static GeyserMappingItem remapItem(@SuppressWarnings("unused") Item item, GeyserMappingItem mapping) {
|
||||
mapping = Conversion671_662.remapItem(item, mapping);
|
||||
|
||||
String identifer = mapping.getBedrockIdentifier();
|
||||
|
||||
switch (identifer) {
|
||||
case "minecraft:bogged_spawn_egg" -> { return mapping.withBedrockIdentifier("minecraft:creeper_spawn_egg"); }
|
||||
case "minecraft:grass_block" -> { return mapping.withBedrockIdentifier("minecraft:grass"); }
|
||||
case "minecraft:vault" -> { return mapping.withBedrockIdentifier("minecraft:trial_spawner"); }
|
||||
case "minecraft:wind_charge" -> { return mapping.withBedrockIdentifier("minecraft:snowball"); }
|
||||
};
|
||||
|
||||
if (NEW_WOODS.contains(identifer)) {
|
||||
switch (identifer) {
|
||||
case "minecraft:oak_wood" -> { return mapping.withBedrockIdentifier("minecraft:wood").withBedrockData(0); }
|
||||
case "minecraft:spruce_wood" -> { return mapping.withBedrockIdentifier("minecraft:wood").withBedrockData(1); }
|
||||
case "minecraft:birch_wood" -> { return mapping.withBedrockIdentifier("minecraft:wood").withBedrockData(2); }
|
||||
case "minecraft:jungle_wood" -> { return mapping.withBedrockIdentifier("minecraft:wood").withBedrockData(3); }
|
||||
case "minecraft:acacia_wood" -> { return mapping.withBedrockIdentifier("minecraft:wood").withBedrockData(4); }
|
||||
case "minecraft:dark_oak_wood" -> { return mapping.withBedrockIdentifier("minecraft:wood").withBedrockData(5); }
|
||||
case "minecraft:stripped_oak_wood" -> { return mapping.withBedrockIdentifier("minecraft:wood").withBedrockData(8); }
|
||||
case "minecraft:stripped_spruce_wood" -> { return mapping.withBedrockIdentifier("minecraft:wood").withBedrockData(9); }
|
||||
case "minecraft:stripped_birch_wood" -> { return mapping.withBedrockIdentifier("minecraft:wood").withBedrockData(10); }
|
||||
case "minecraft:stripped_jungle_wood" -> { return mapping.withBedrockIdentifier("minecraft:wood").withBedrockData(11); }
|
||||
case "minecraft:stripped_acacia_wood" -> { return mapping.withBedrockIdentifier("minecraft:wood").withBedrockData(12); }
|
||||
case "minecraft:stripped_dark_oak_wood" -> { return mapping.withBedrockIdentifier("minecraft:wood").withBedrockData(13); }
|
||||
}
|
||||
}
|
||||
|
||||
if (NEW_SLABS.contains(identifer)) {
|
||||
switch (identifer) {
|
||||
case "minecraft:oak_slab" -> { return mapping.withBedrockIdentifier("minecraft:wooden_slab").withBedrockData(0); }
|
||||
case "minecraft:spruce_slab" -> { return mapping.withBedrockIdentifier("minecraft:wooden_slab").withBedrockData(1); }
|
||||
case "minecraft:birch_slab" -> { return mapping.withBedrockIdentifier("minecraft:wooden_slab").withBedrockData(2); }
|
||||
case "minecraft:jungle_slab" -> { return mapping.withBedrockIdentifier("minecraft:wooden_slab").withBedrockData(3); }
|
||||
case "minecraft:acacia_slab" -> { return mapping.withBedrockIdentifier("minecraft:wooden_slab").withBedrockData(4); }
|
||||
case "minecraft:dark_oak_slab" -> { return mapping.withBedrockIdentifier("minecraft:wooden_slab").withBedrockData(5); }
|
||||
}
|
||||
}
|
||||
|
||||
if (NEW_LEAVES.contains(identifer) || NEW_LEAVES2.contains(identifer)) {
|
||||
switch (identifer) {
|
||||
case "minecraft:oak_leaves" -> { return mapping.withBedrockIdentifier("minecraft:leaves").withBedrockData(0); }
|
||||
case "minecraft:spruce_leaves" -> { return mapping.withBedrockIdentifier("minecraft:leaves").withBedrockData(1); }
|
||||
case "minecraft:birch_leaves" -> { return mapping.withBedrockIdentifier("minecraft:leaves").withBedrockData(2); }
|
||||
case "minecraft:jungle_leaves" -> { return mapping.withBedrockIdentifier("minecraft:leaves").withBedrockData(3); }
|
||||
case "minecraft:acacia_leaves" -> { return mapping.withBedrockIdentifier("minecraft:leaves2").withBedrockData(0); }
|
||||
case "minecraft:dark_oak_leaves" -> { return mapping.withBedrockIdentifier("minecraft:leaves2").withBedrockData(1); }
|
||||
}
|
||||
}
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
static NbtMap remapBlock(NbtMap tag) {
|
||||
tag = Conversion671_662.remapBlock(tag);
|
||||
|
||||
final String name = tag.getString("name");
|
||||
|
||||
if (!NEW_BLOCKS.contains(name)) {
|
||||
return tag;
|
||||
}
|
||||
|
||||
String replacement;
|
||||
|
||||
if (name.equals("minecraft:grass_block")) {
|
||||
replacement = "minecraft:grass";
|
||||
|
||||
NbtMapBuilder builder = tag.toBuilder();
|
||||
builder.putString("name", replacement);
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
if (name.equals("minecraft:vault")) {
|
||||
replacement = "minecraft:trial_spawner";
|
||||
|
||||
NbtMapBuilder statesBuilder = NbtMap.builder()
|
||||
.putInt("trial_spawner_state", 0);
|
||||
|
||||
NbtMapBuilder builder = tag.toBuilder();
|
||||
builder.putString("name", replacement);
|
||||
builder.putCompound("states", statesBuilder.build());
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
if (NEW_WOODS.contains(name)) {
|
||||
replacement = "minecraft:wood";
|
||||
|
||||
NbtMap states = tag.getCompound("states");
|
||||
boolean stripped = name.startsWith("minecraft:stripped_");
|
||||
String woodType = name.replaceAll("minecraft:|_wood|stripped_", "");
|
||||
|
||||
NbtMapBuilder statesBuilder = states.toBuilder()
|
||||
.putString("wood_type", woodType)
|
||||
.putBoolean("stripped_bit", stripped);
|
||||
|
||||
NbtMapBuilder builder = tag.toBuilder()
|
||||
.putString("name", replacement)
|
||||
.putCompound("states", statesBuilder.build());
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
if (NEW_LEAVES.contains(name) || NEW_LEAVES2.contains(name)) {
|
||||
boolean leaves2 = NEW_LEAVES2.contains(name);
|
||||
replacement = leaves2 ? "minecraft:leaves2" : "minecraft:leaves";
|
||||
|
||||
NbtMap states = tag.getCompound("states");
|
||||
String leafType = name.replaceAll("minecraft:|_leaves", "");
|
||||
|
||||
NbtMapBuilder statesBuilder = states.toBuilder()
|
||||
.putString(leaves2 ? "new_leaf_type" : "old_leaf_type", leafType);
|
||||
|
||||
NbtMapBuilder builder = tag.toBuilder()
|
||||
.putString("name", replacement)
|
||||
.putCompound("states", statesBuilder.build());
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
|
||||
if (NEW_SLABS.contains(name)) {
|
||||
replacement = name.contains("double") ? "minecraft:double_wooden_slab" : "minecraft:wooden_slab";
|
||||
|
||||
NbtMap states = tag.getCompound("states");
|
||||
String woodType = name.replaceAll("minecraft:|_double|_slab", "");
|
||||
|
||||
NbtMapBuilder statesBuilder = states.toBuilder()
|
||||
.putString("wood_type", woodType);
|
||||
|
||||
NbtMapBuilder builder = tag.toBuilder()
|
||||
.putString("name", replacement)
|
||||
.putCompound("states", statesBuilder.build());
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
return tag;
|
||||
}
|
||||
}
|
@ -1,205 +0,0 @@
|
||||
/*
|
||||
* 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.registry.populator;
|
||||
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.registry.type.GeyserMappingItem;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Conversion671_662 {
|
||||
private static final List<String> NEW_MISC = List.of("minecraft:heavy_core", "minecraft:mace", "minecraft:flow_banner_pattern", "minecraft:guster_banner_pattern", "minecraft:flow_armor_trim_smithing_template", "minecraft:bolt_armor_trim_smithing_template", "minecraft:flow_pottery_sherd", "minecraft:guster_pottery_sherd", "minecraft:scrape_pottery_sherd", "minecraft:breeze_rod");
|
||||
private static final List<String> NEW_CORAL_FANS = List.of("minecraft:tube_coral_fan", "minecraft:brain_coral_fan", "minecraft:bubble_coral_fan", "minecraft:fire_coral_fan", "minecraft:horn_coral_fan");
|
||||
private static final List<String> NEW_DEAD_CORAL_FANS = List.of("minecraft:dead_tube_coral_fan", "minecraft:dead_brain_coral_fan", "minecraft:dead_bubble_coral_fan", "minecraft:dead_fire_coral_fan", "minecraft:dead_horn_coral_fan");
|
||||
private static final List<String> NEW_FLOWERS = List.of("minecraft:poppy", "minecraft:blue_orchid", "minecraft:allium", "minecraft:azure_bluet", "minecraft:red_tulip", "minecraft:orange_tulip", "minecraft:white_tulip", "minecraft:pink_tulip", "minecraft:oxeye_daisy", "minecraft:cornflower", "minecraft:lily_of_the_valley");
|
||||
private static final List<String> NEW_SAPLINGS = List.of("minecraft:oak_sapling", "minecraft:spruce_sapling", "minecraft:birch_sapling", "minecraft:jungle_sapling", "minecraft:acacia_sapling", "minecraft:dark_oak_sapling", "minecraft:bamboo_sapling");
|
||||
private static final List<String> NEW_BLOCKS = Stream.of(NEW_MISC, NEW_CORAL_FANS, NEW_DEAD_CORAL_FANS, NEW_FLOWERS, NEW_SAPLINGS).flatMap(List::stream).toList();
|
||||
|
||||
static GeyserMappingItem remapItem(@SuppressWarnings("unused") Item item, GeyserMappingItem mapping) {
|
||||
String identifer = mapping.getBedrockIdentifier();
|
||||
|
||||
if (!NEW_BLOCKS.contains(identifer)) {
|
||||
return mapping;
|
||||
}
|
||||
|
||||
switch (identifer) {
|
||||
case "minecraft:bolt_armor_trim_smithing_template" -> { return mapping.withBedrockIdentifier("minecraft:wayfinder_armor_trim_smithing_template"); }
|
||||
case "minecraft:breeze_rod" -> { return mapping.withBedrockIdentifier("minecraft:blaze_rod"); }
|
||||
case "minecraft:flow_armor_trim_smithing_template" -> { return mapping.withBedrockIdentifier("minecraft:spire_armor_trim_smithing_template"); }
|
||||
case "minecraft:flow_banner_pattern", "minecraft:guster_banner_pattern" -> { return mapping.withBedrockIdentifier("minecraft:globe_banner_pattern"); }
|
||||
case "minecraft:flow_pottery_sherd" -> { return mapping.withBedrockIdentifier("minecraft:skull_pottery_sherd"); }
|
||||
case "minecraft:guster_pottery_sherd" -> { return mapping.withBedrockIdentifier("minecraft:shelter_pottery_sherd"); }
|
||||
case "minecraft:scrape_pottery_sherd" -> { return mapping.withBedrockIdentifier("minecraft:heartbreak_pottery_sherd"); }
|
||||
case "minecraft:heavy_core" -> { return mapping.withBedrockIdentifier("minecraft:conduit"); }
|
||||
case "minecraft:mace" -> { return mapping.withBedrockIdentifier("minecraft:netherite_axe"); }
|
||||
}
|
||||
|
||||
if (NEW_FLOWERS.contains(identifer)) {
|
||||
switch (identifer) {
|
||||
case "minecraft:poppy" -> { return mapping.withBedrockIdentifier("minecraft:red_flower").withBedrockData(0); }
|
||||
case "minecraft:blue_orchid" -> { return mapping.withBedrockIdentifier("minecraft:red_flower").withBedrockData(1); }
|
||||
case "minecraft:allium" -> { return mapping.withBedrockIdentifier("minecraft:red_flower").withBedrockData(2); }
|
||||
case "minecraft:azure_bluet" -> { return mapping.withBedrockIdentifier("minecraft:red_flower").withBedrockData(3); }
|
||||
case "minecraft:red_tulip" -> { return mapping.withBedrockIdentifier("minecraft:red_flower").withBedrockData(4); }
|
||||
case "minecraft:orange_tulip" -> { return mapping.withBedrockIdentifier("minecraft:red_flower").withBedrockData(5); }
|
||||
case "minecraft:white_tulip" -> { return mapping.withBedrockIdentifier("minecraft:red_flower").withBedrockData(6); }
|
||||
case "minecraft:pink_tulip" -> { return mapping.withBedrockIdentifier("minecraft:red_flower").withBedrockData(7); }
|
||||
case "minecraft:oxeye_daisy" -> { return mapping.withBedrockIdentifier("minecraft:red_flower").withBedrockData(8); }
|
||||
case "minecraft:cornflower" -> { return mapping.withBedrockIdentifier("minecraft:red_flower").withBedrockData(9); }
|
||||
case "minecraft:lily_of_the_valley" -> { return mapping.withBedrockIdentifier("minecraft:red_flower").withBedrockData(10); }
|
||||
}
|
||||
}
|
||||
|
||||
if (NEW_SAPLINGS.contains(identifer)) {
|
||||
switch (identifer) {
|
||||
case "minecraft:oak_sapling" -> { return mapping.withBedrockIdentifier("minecraft:sapling").withBedrockData(0); }
|
||||
case "minecraft:spruce_sapling" -> { return mapping.withBedrockIdentifier("minecraft:sapling").withBedrockData(1); }
|
||||
case "minecraft:birch_sapling" -> { return mapping.withBedrockIdentifier("minecraft:sapling").withBedrockData(2); }
|
||||
case "minecraft:jungle_sapling" -> { return mapping.withBedrockIdentifier("minecraft:sapling").withBedrockData(3); }
|
||||
case "minecraft:acacia_sapling" -> { return mapping.withBedrockIdentifier("minecraft:sapling").withBedrockData(4); }
|
||||
case "minecraft:dark_oak_sapling" -> { return mapping.withBedrockIdentifier("minecraft:sapling").withBedrockData(5); }
|
||||
}
|
||||
}
|
||||
|
||||
if (NEW_CORAL_FANS.contains(identifer)) {
|
||||
switch (identifer) {
|
||||
case "minecraft:tube_coral_fan" -> { return mapping.withBedrockIdentifier("minecraft:coral_fan").withBedrockData(0); }
|
||||
case "minecraft:brain_coral_fan" -> { return mapping.withBedrockIdentifier("minecraft:coral_fan").withBedrockData(1); }
|
||||
case "minecraft:bubble_coral_fan" -> { return mapping.withBedrockIdentifier("minecraft:coral_fan").withBedrockData(2); }
|
||||
case "minecraft:fire_coral_fan" -> { return mapping.withBedrockIdentifier("minecraft:coral_fan").withBedrockData(3); }
|
||||
case "minecraft:horn_coral_fan" -> { return mapping.withBedrockIdentifier("minecraft:coral_fan").withBedrockData(4); }
|
||||
}
|
||||
}
|
||||
|
||||
if (NEW_DEAD_CORAL_FANS.contains(identifer)) {
|
||||
switch (identifer) {
|
||||
case "minecraft:dead_tube_coral_fan" -> { return mapping.withBedrockIdentifier("minecraft:coral_fan_dead").withBedrockData(0); }
|
||||
case "minecraft:dead_brain_coral_fan" -> { return mapping.withBedrockIdentifier("minecraft:coral_fan_dead").withBedrockData(1); }
|
||||
case "minecraft:dead_bubble_coral_fan" -> { return mapping.withBedrockIdentifier("minecraft:coral_fan_dead").withBedrockData(2); }
|
||||
case "minecraft:dead_fire_coral_fan" -> { return mapping.withBedrockIdentifier("minecraft:coral_fan_dead").withBedrockData(3); }
|
||||
case "minecraft:dead_horn_coral_fan" -> { return mapping.withBedrockIdentifier("minecraft:coral_fan_dead").withBedrockData(4); }
|
||||
}
|
||||
}
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
static NbtMap remapBlock(NbtMap tag) {
|
||||
final String name = tag.getString("name");
|
||||
|
||||
if (!NEW_BLOCKS.contains(name)) {
|
||||
return tag;
|
||||
}
|
||||
|
||||
if (name.equals("minecraft:bamboo_sapling")) {
|
||||
NbtMap states = tag.getCompound("states")
|
||||
.toBuilder()
|
||||
.putString("sapling_type", "oak")
|
||||
.build();
|
||||
|
||||
return tag.toBuilder().putCompound("states", states).build();
|
||||
}
|
||||
|
||||
String replacement;
|
||||
|
||||
if (name.equals("minecraft:heavy_core")) {
|
||||
replacement = "minecraft:conduit";
|
||||
|
||||
NbtMapBuilder builder = tag.toBuilder();
|
||||
builder.putString("name", replacement);
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
if (NEW_SAPLINGS.contains(name)) {
|
||||
replacement = "minecraft:sapling";
|
||||
String saplingType = name.replaceAll("minecraft:|_sapling", "");;
|
||||
|
||||
NbtMap states = tag.getCompound("states")
|
||||
.toBuilder()
|
||||
.putString("sapling_type", saplingType)
|
||||
.build();
|
||||
|
||||
return tag.toBuilder().putString("name", replacement).putCompound("states", states).build();
|
||||
}
|
||||
|
||||
if (NEW_FLOWERS.contains(name)) {
|
||||
replacement = "minecraft:red_flower";
|
||||
String flowerType;
|
||||
|
||||
switch (name) {
|
||||
case "minecraft:poppy" -> flowerType = "poppy";
|
||||
case "minecraft:blue_orchid" -> flowerType = "orchid";
|
||||
case "minecraft:allium" -> flowerType = "allium";
|
||||
case "minecraft:azure_bluet" -> flowerType = "houstonia";
|
||||
case "minecraft:red_tulip" -> flowerType = "tulip_red";
|
||||
case "minecraft:orange_tulip" -> flowerType = "tulip_orange";
|
||||
case "minecraft:white_tulip" -> flowerType = "tulip_white";
|
||||
case "minecraft:pink_tulip" -> flowerType = "tulip_pink";
|
||||
case "minecraft:oxeye_daisy" -> flowerType = "oxeye";
|
||||
case "minecraft:cornflower" -> flowerType = "cornflower";
|
||||
case "minecraft:lily_of_the_valley" -> flowerType = "lily_of_the_valley";
|
||||
default -> throw new IllegalStateException("Unexpected value: " + name);
|
||||
}
|
||||
|
||||
NbtMap states = tag.getCompound("states")
|
||||
.toBuilder()
|
||||
.putString("flower_type", flowerType)
|
||||
.build();
|
||||
|
||||
return tag.toBuilder().putString("name", replacement).putCompound("states", states).build();
|
||||
}
|
||||
|
||||
boolean isLiveCoralFan = NEW_CORAL_FANS.contains(name);
|
||||
boolean isDeadCoralFan = NEW_DEAD_CORAL_FANS.contains(name);
|
||||
|
||||
if (isLiveCoralFan || isDeadCoralFan) {
|
||||
replacement = isLiveCoralFan ? "minecraft:coral_fan" : "minecraft:coral_fan_dead";
|
||||
String coralColor;
|
||||
|
||||
switch (name) {
|
||||
case "minecraft:tube_coral_fan", "minecraft:dead_tube_coral_fan" -> coralColor = "blue";
|
||||
case "minecraft:brain_coral_fan", "minecraft:dead_brain_coral_fan" -> coralColor = "pink";
|
||||
case "minecraft:bubble_coral_fan", "minecraft:dead_bubble_coral_fan" -> coralColor = "purple";
|
||||
case "minecraft:fire_coral_fan", "minecraft:dead_fire_coral_fan" -> coralColor = "yellow";
|
||||
case "minecraft:horn_coral_fan", "minecraft:dead_horn_coral_fan" -> coralColor = "red";
|
||||
default -> throw new IllegalStateException("Unexpected value: " + name);
|
||||
}
|
||||
|
||||
NbtMap states = tag.getCompound("states")
|
||||
.toBuilder()
|
||||
.putString("coral_color", coralColor)
|
||||
.build();
|
||||
|
||||
return tag.toBuilder().putString("name", replacement).putCompound("states", states).build();
|
||||
}
|
||||
|
||||
return tag;
|
||||
}
|
||||
}
|
@ -0,0 +1,205 @@
|
||||
/*
|
||||
* 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.registry.populator;
|
||||
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.registry.type.GeyserMappingItem;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Conversion685_671 {
|
||||
private static final List<String> NEW_CORAL_BLOCKS = List.of("minecraft:tube_coral_block", "minecraft:brain_coral_block", "minecraft:bubble_coral_block", "minecraft:fire_coral_block", "minecraft:horn_coral_block", "minecraft:dead_tube_coral_block", "minecraft:dead_brain_coral_block", "minecraft:dead_bubble_coral_block", "minecraft:dead_fire_coral_block", "minecraft:dead_horn_coral_block");
|
||||
private static final List<String> NEW_DOUBLE_PLANTS = List.of("minecraft:sunflower", "minecraft:lilac", "minecraft:tall_grass", "minecraft:large_fern", "minecraft:rose_bush", "minecraft:peony");
|
||||
private static final List<String> NEW_STONE_BLOCK_SLABS = List.of("minecraft:smooth_stone_slab", "minecraft:sandstone_slab", "minecraft:petrified_oak_slab", "minecraft:cobblestone_slab", "minecraft:brick_slab", "minecraft:stone_brick_slab", "minecraft:quartz_slab", "minecraft:nether_brick_slab");
|
||||
private static final List<String> NEW_TALLGRASSES = List.of("minecraft:fern", "minecraft:short_grass");
|
||||
private static final List<String> OMINOUS_BLOCKS = List.of("minecraft:trial_spawner", "minecraft:vault");
|
||||
private static final List<String> NEW_BLOCKS = Stream.of(NEW_CORAL_BLOCKS, NEW_DOUBLE_PLANTS, NEW_STONE_BLOCK_SLABS, NEW_TALLGRASSES).flatMap(List::stream).toList();
|
||||
private static final List<String> MODIFIED_BLOCKS = Stream.of(NEW_BLOCKS, OMINOUS_BLOCKS).flatMap(List::stream).toList();
|
||||
|
||||
static GeyserMappingItem remapItem(@SuppressWarnings("unused") Item item, GeyserMappingItem mapping) {
|
||||
String identifer = mapping.getBedrockIdentifier();
|
||||
|
||||
if (!NEW_BLOCKS.contains(identifer)) {
|
||||
return mapping;
|
||||
}
|
||||
|
||||
if (NEW_CORAL_BLOCKS.contains(identifer)) {
|
||||
switch (identifer) {
|
||||
case "minecraft:tube_coral_block" -> { return mapping.withBedrockIdentifier("minecraft:coral_block").withBedrockData(0); }
|
||||
case "minecraft:brain_coral_block" -> { return mapping.withBedrockIdentifier("minecraft:coral_block").withBedrockData(1); }
|
||||
case "minecraft:bubble_coral_block" -> { return mapping.withBedrockIdentifier("minecraft:coral_block").withBedrockData(2); }
|
||||
case "minecraft:fire_coral_block" -> { return mapping.withBedrockIdentifier("minecraft:coral_block").withBedrockData(3); }
|
||||
case "minecraft:horn_coral_block" -> { return mapping.withBedrockIdentifier("minecraft:coral_block").withBedrockData(4); }
|
||||
case "minecraft:dead_tube_coral_block" -> { return mapping.withBedrockIdentifier("minecraft:coral_block").withBedrockData(8); }
|
||||
case "minecraft:dead_brain_coral_block" -> { return mapping.withBedrockIdentifier("minecraft:coral_block").withBedrockData(9); }
|
||||
case "minecraft:dead_bubble_coral_block" -> { return mapping.withBedrockIdentifier("minecraft:coral_block").withBedrockData(10); }
|
||||
case "minecraft:dead_fire_coral_block" -> { return mapping.withBedrockIdentifier("minecraft:coral_block").withBedrockData(11); }
|
||||
case "minecraft:dead_horn_coral_block" -> { return mapping.withBedrockIdentifier("minecraft:coral_block").withBedrockData(12); }
|
||||
}
|
||||
}
|
||||
|
||||
if (NEW_DOUBLE_PLANTS.contains(identifer)) {
|
||||
switch (identifer) {
|
||||
case "minecraft:sunflower" -> { return mapping.withBedrockIdentifier("minecraft:double_plant").withBedrockData(0); }
|
||||
case "minecraft:lilac" -> { return mapping.withBedrockIdentifier("minecraft:double_plant").withBedrockData(1); }
|
||||
case "minecraft:tall_grass" -> { return mapping.withBedrockIdentifier("minecraft:double_plant").withBedrockData(2); }
|
||||
case "minecraft:large_fern" -> { return mapping.withBedrockIdentifier("minecraft:double_plant").withBedrockData(3); }
|
||||
case "minecraft:rose_bush" -> { return mapping.withBedrockIdentifier("minecraft:double_plant").withBedrockData(4); }
|
||||
case "minecraft:peony" -> { return mapping.withBedrockIdentifier("minecraft:double_plant").withBedrockData(5); }
|
||||
}
|
||||
}
|
||||
|
||||
if (NEW_STONE_BLOCK_SLABS.contains(identifer)) {
|
||||
switch (identifer) {
|
||||
case "minecraft:smooth_stone_slab" -> { return mapping.withBedrockIdentifier("minecraft:stone_block_slab").withBedrockData(0); }
|
||||
case "minecraft:sandstone_slab" -> { return mapping.withBedrockIdentifier("minecraft:stone_block_slab").withBedrockData(1); }
|
||||
case "minecraft:petrified_oak_slab" -> { return mapping.withBedrockIdentifier("minecraft:stone_block_slab").withBedrockData(2); }
|
||||
case "minecraft:cobblestone_slab" -> { return mapping.withBedrockIdentifier("minecraft:stone_block_slab").withBedrockData(3); }
|
||||
case "minecraft:brick_slab" -> { return mapping.withBedrockIdentifier("minecraft:stone_block_slab").withBedrockData(4); }
|
||||
case "minecraft:stone_brick_slab" -> { return mapping.withBedrockIdentifier("minecraft:stone_block_slab").withBedrockData(5); }
|
||||
case "minecraft:quartz_slab" -> { return mapping.withBedrockIdentifier("minecraft:stone_block_slab").withBedrockData(6); }
|
||||
case "minecraft:nether_brick_slab" -> { return mapping.withBedrockIdentifier("minecraft:stone_block_slab").withBedrockData(7); }
|
||||
}
|
||||
}
|
||||
|
||||
if (NEW_TALLGRASSES.contains(identifer)) {
|
||||
switch (identifer) {
|
||||
case "minecraft:short_grass" -> { return mapping.withBedrockIdentifier("minecraft:tallgrass").withBedrockData(1); }
|
||||
case "minecraft:fern" -> { return mapping.withBedrockIdentifier("minecraft:tallgrass").withBedrockData(2); }
|
||||
}
|
||||
}
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
static NbtMap remapBlock(NbtMap tag) {
|
||||
final String name = tag.getString("name");
|
||||
|
||||
if (!MODIFIED_BLOCKS.contains(name)) {
|
||||
return tag;
|
||||
}
|
||||
|
||||
if (OMINOUS_BLOCKS.contains(name)) {
|
||||
NbtMapBuilder builder = tag.getCompound("states").toBuilder();
|
||||
builder.remove("ominous");
|
||||
return tag.toBuilder().putCompound("states", builder.build()).build();
|
||||
}
|
||||
|
||||
String replacement;
|
||||
|
||||
if (NEW_CORAL_BLOCKS.contains(name)) {
|
||||
replacement = "minecraft:coral_block";
|
||||
String coralColor;
|
||||
boolean deadBit = name.startsWith("minecraft:dead_");
|
||||
|
||||
switch(name) {
|
||||
case "minecraft:tube_coral_block", "minecraft:dead_tube_coral_block" -> coralColor = "blue";
|
||||
case "minecraft:brain_coral_block", "minecraft:dead_brain_coral_block" -> coralColor = "pink";
|
||||
case "minecraft:bubble_coral_block", "minecraft:dead_bubble_coral_block" -> coralColor = "purple";
|
||||
case "minecraft:fire_coral_block", "minecraft:dead_fire_coral_block" -> coralColor = "yellow";
|
||||
case "minecraft:horn_coral_block", "minecraft:dead_horn_coral_block" -> coralColor = "red";
|
||||
default -> throw new IllegalStateException("Unexpected value: " + name);
|
||||
}
|
||||
|
||||
NbtMap states = tag.getCompound("states")
|
||||
.toBuilder()
|
||||
.putString("coral_color", coralColor)
|
||||
.putBoolean("dead_bit", deadBit)
|
||||
.build();
|
||||
|
||||
return tag.toBuilder().putString("name", replacement).putCompound("states", states).build();
|
||||
}
|
||||
|
||||
if (NEW_DOUBLE_PLANTS.contains(name)) {
|
||||
replacement = "minecraft:double_plant";
|
||||
String doublePlantType;
|
||||
|
||||
switch(name) {
|
||||
case "minecraft:sunflower" -> doublePlantType = "sunflower";
|
||||
case "minecraft:lilac" -> doublePlantType = "syringa";
|
||||
case "minecraft:tall_grass" -> doublePlantType = "grass";
|
||||
case "minecraft:large_fern" -> doublePlantType = "fern";
|
||||
case "minecraft:rose_bush" -> doublePlantType = "rose";
|
||||
case "minecraft:peony" -> doublePlantType = "paeonia";
|
||||
default -> throw new IllegalStateException("Unexpected value: " + name);
|
||||
}
|
||||
|
||||
NbtMap states = tag.getCompound("states")
|
||||
.toBuilder()
|
||||
.putString("double_plant_type", doublePlantType)
|
||||
.build();
|
||||
|
||||
return tag.toBuilder().putString("name", replacement).putCompound("states", states).build();
|
||||
}
|
||||
|
||||
if (NEW_STONE_BLOCK_SLABS.contains(name)) {
|
||||
replacement = "minecraft:stone_block_slab";
|
||||
String stoneSlabType;
|
||||
|
||||
switch(name) {
|
||||
case "minecraft:smooth_stone_slab" -> stoneSlabType = "smooth_stone";
|
||||
case "minecraft:sandstone_slab" -> stoneSlabType = "sandstone";
|
||||
case "minecraft:petrified_oak_slab" -> stoneSlabType = "wood";
|
||||
case "minecraft:cobblestone_slab" -> stoneSlabType = "cobblestone";
|
||||
case "minecraft:brick_slab" -> stoneSlabType = "brick";
|
||||
case "minecraft:stone_brick_slab" -> stoneSlabType = "stone_brick";
|
||||
case "minecraft:quartz_slab" -> stoneSlabType = "quartz";
|
||||
case "minecraft:nether_brick_slab" -> stoneSlabType = "nether_brick";
|
||||
default -> throw new IllegalStateException("Unexpected value: " + name);
|
||||
}
|
||||
|
||||
NbtMap states = tag.getCompound("states")
|
||||
.toBuilder()
|
||||
.putString("stone_slab_type", stoneSlabType)
|
||||
.build();
|
||||
|
||||
return tag.toBuilder().putString("name", replacement).putCompound("states", states).build();
|
||||
}
|
||||
|
||||
if (NEW_TALLGRASSES.contains(name)) {
|
||||
replacement = "minecraft:tallgrass";
|
||||
String tallGrassType;
|
||||
|
||||
switch(name) {
|
||||
case "minecraft:short_grass" -> tallGrassType = "tall";
|
||||
case "minecraft:fern" -> tallGrassType = "fern";
|
||||
default -> throw new IllegalStateException("Unexpected value: " + name);
|
||||
}
|
||||
|
||||
NbtMap states = tag.getCompound("states")
|
||||
.toBuilder()
|
||||
.putString("tall_grass_type", tallGrassType)
|
||||
.build();
|
||||
|
||||
return tag.toBuilder().putString("name", replacement).putCompound("states", states).build();
|
||||
}
|
||||
|
||||
return tag;
|
||||
}
|
||||
}
|
@ -53,7 +53,6 @@ import org.geysermc.geyser.level.block.GeyserCustomBlockData;
|
||||
import org.geysermc.geyser.level.block.GeyserCustomBlockState;
|
||||
import org.geysermc.geyser.level.block.GeyserGeometryComponent;
|
||||
import org.geysermc.geyser.level.block.GeyserMaterialInstance;
|
||||
import org.geysermc.geyser.network.GameProtocol;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.registry.mappings.MappingsConfigReader;
|
||||
import org.geysermc.geyser.registry.type.CustomSkull;
|
||||
@ -325,13 +324,11 @@ public class CustomBlockRegistryPopulator {
|
||||
// meaning of this version is unknown, but it's required for tags to work and should probably be checked periodically
|
||||
.putInt("molangVersion", 1)
|
||||
.putList("permutations", NbtType.COMPOUND, permutations)
|
||||
.putList("properties", NbtType.COMPOUND, properties);
|
||||
|
||||
if (GameProtocol.is1_20_60orHigher(protocolVersion)) {
|
||||
propertyTag.putCompound("vanilla_block_data", NbtMap.builder()
|
||||
.putList("properties", NbtType.COMPOUND, properties)
|
||||
.putCompound("vanilla_block_data", NbtMap.builder()
|
||||
.putInt("block_id", BLOCK_ID.getAndIncrement())
|
||||
.build());
|
||||
}
|
||||
|
||||
return new BlockPropertyData(customBlock.identifier(), propertyTag.build());
|
||||
}
|
||||
|
||||
|
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