3
0
Mirror von https://github.com/GeyserMC/Geyser.git synchronisiert 2024-12-28 00:50:20 +01:00

Merge branch 'master' of https://github.com/GeyserMC/Geyser into feature/configurate

Dieser Commit ist enthalten in:
Camotoy 2024-06-15 12:48:59 -04:00
Commit 10bf4eeb4e
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: 7EEFB66FE798081F
220 geänderte Dateien mit 9912 neuen und 41664 gelöschten Zeilen

Datei anzeigen

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

Datei anzeigen

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

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

@ -0,0 +1,33 @@
name: Dispatch Preview
on:
workflow_dispatch:
inputs:
runId:
required: true
description: 'ID of the action to pull artifacts from'
build:
required: true
description: 'Build number for the release'
version:
required: true
description: 'Version under which to upload to the Downloads API'
jobs:
dispatch-preview:
# Allow access to secrets if we are uploading a preview
secrets: inherit
uses: GeyserMC/actions/.github/workflows/upload-preview.yml@master
with:
build: ${{ inputs.build }}
version: ${{ inputs.version }}
files: |
bungeecord:Geyser-BungeeCord.jar
fabric:Geyser-Fabric.jar
neoforge:Geyser-NeoForge.jar
spigot:Geyser-Spigot.jar
standalone:Geyser-Standalone.jar
velocity:Geyser-Velocity.jar
viaproxy:Geyser-ViaProxy.jar
project: geyserpreview
runId: ${{ inputs.runId }}

Datei anzeigen

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

Datei anzeigen

@ -8,7 +8,7 @@ jobs:
# Forbid access to secrets nor GH Token perms while building the PR # Forbid access to secrets nor GH Token perms while building the PR
permissions: {} permissions: {}
secrets: {} secrets: {}
uses: ./.github/workflows/build-remote.yml uses: GeyserMC/Geyser/.github/workflows/build-remote.yml@master
with: with:
repository: ${{ github.event.pull_request.head.repo.full_name }} repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.sha }} ref: ${{ github.event.pull_request.head.sha }}
@ -18,7 +18,17 @@ jobs:
contains(github.event.pull_request.labels.*.name, 'PR: Needs Testing') contains(github.event.pull_request.labels.*.name, 'PR: Needs Testing')
# Allow access to secrets if we are uploading a preview # Allow access to secrets if we are uploading a preview
secrets: inherit secrets: inherit
uses: ./.github/workflows/preview.yml uses: GeyserMC/actions/.github/workflows/upload-preview.yml@master
with: with:
build: ${{ github.run_number }} build: ${{ github.run_number }}
version: pr.${{ github.event.pull_request.number }} version: pr.${{ github.event.pull_request.number }}
files: |
bungeecord:Geyser-BungeeCord.jar
fabric:Geyser-Fabric.jar
neoforge:Geyser-NeoForge.jar
spigot:Geyser-Spigot.jar
standalone:Geyser-Standalone.jar
velocity:Geyser-Velocity.jar
viaproxy:Geyser-ViaProxy.jar
project: geyserpreview
runId: ${{ github.run_id }}

Datei anzeigen

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

Datei anzeigen

@ -145,4 +145,36 @@ public interface CameraData {
* @return whether the camera is currently locked * @return whether the camera is currently locked
*/ */
boolean isCameraLocked(); boolean isCameraLocked();
/**
* Hides a {@link GuiElement} on the client's side.
*
* @param element the {@link GuiElement} to hide
*/
void hideElement(@NonNull GuiElement... element);
/**
* Resets a {@link GuiElement} on the client's side.
* This makes the client decide on its own - e.g. based on client settings -
* whether to show or hide the gui element.
* <p>
* If no elements are specified, this will reset all currently hidden elements
*
* @param element the {@link GuiElement} to reset
*/
void resetElement(@NonNull GuiElement @Nullable... element);
/**
* Determines whether a {@link GuiElement} is currently hidden.
*
* @param element the {@link GuiElement} to check
*/
boolean isHudElementHidden(@NonNull GuiElement element);
/**
* Returns the currently hidden {@link GuiElement}s.
*
* @return an unmodifiable view of all currently hidden {@link GuiElement}s
*/
@NonNull Set<GuiElement> hiddenElements();
} }

Datei anzeigen

@ -0,0 +1,60 @@
/*
* Copyright (c) 2024 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.api.bedrock.camera;
/**
* Represent GUI elements on the players HUD display.
* These can be hidden using {@link CameraData#hideElement(GuiElement...)},
* and one can reset their visibility using {@link CameraData#resetElement(GuiElement...)}.
*/
public class GuiElement {
public static final GuiElement PAPER_DOLL = new GuiElement(0);
public static final GuiElement ARMOR = new GuiElement(1);
public static final GuiElement TOOL_TIPS = new GuiElement(2);
public static final GuiElement TOUCH_CONTROLS = new GuiElement(3);
public static final GuiElement CROSSHAIR = new GuiElement(4);
public static final GuiElement HOTBAR = new GuiElement(5);
public static final GuiElement HEALTH = new GuiElement(6);
public static final GuiElement PROGRESS_BAR = new GuiElement(7);
public static final GuiElement FOOD_BAR = new GuiElement(8);
public static final GuiElement AIR_BUBBLES_BAR = new GuiElement(9);
public static final GuiElement VEHICLE_HEALTH = new GuiElement(10);
public static final GuiElement EFFECTS_BAR = new GuiElement(11);
public static final GuiElement ITEM_TEXT_POPUP = new GuiElement(12);
private GuiElement(int id) {
this.id = id;
}
private final int id;
/**
* Internal use only; don't depend on these values being consistent.
*/
public int id() {
return this.id;
}
}

Datei anzeigen

@ -36,7 +36,7 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
/** /**
* Called when a session has logged in, and is about to connect to a remote java server. * Called when a session has logged in, and is about to connect to a remote Java server.
* This event is cancellable, and can be used to prevent the player from connecting to the remote server. * This event is cancellable, and can be used to prevent the player from connecting to the remote server.
*/ */
public final class SessionLoginEvent extends ConnectionEvent implements Cancellable { public final class SessionLoginEvent extends ConnectionEvent implements Cancellable {
@ -99,9 +99,9 @@ public final class SessionLoginEvent extends ConnectionEvent implements Cancella
} }
/** /**
* Gets the {@link RemoteServer} the section will attempt to connect to. * Gets the {@link RemoteServer} the session will attempt to connect to.
* *
* @return the {@link RemoteServer} the section will attempt to connect to. * @return the {@link RemoteServer} the session will attempt to connect to.
*/ */
public @NonNull RemoteServer remoteServer() { public @NonNull RemoteServer remoteServer() {
return this.remoteServer; return this.remoteServer;

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -4,6 +4,12 @@ dependencies {
isTransitive = false isTransitive = false
} }
implementation(libs.erosion.bukkit.nms) {
attributes {
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 21)
}
}
implementation(variantOf(libs.adapters.spigot) { implementation(variantOf(libs.adapters.spigot) {
classifier("all") // otherwise the unshaded jar is used without the shaded NMS implementations classifier("all") // otherwise the unshaded jar is used without the shaded NMS implementations
}) })

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -81,14 +81,14 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.UUID; import java.util.UUID;
import java.util.logging.Level;
public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
private GeyserSpigotCommandManager geyserCommandManager; private GeyserSpigotCommandManager geyserCommandManager;
private GeyserSpigotConfiguration geyserConfig; private GeyserSpigotConfiguration geyserConfig;
private GeyserSpigotInjector geyserInjector; private GeyserSpigotInjector geyserInjector;
private GeyserSpigotLogger geyserLogger; private final GeyserSpigotLogger geyserLogger = GeyserPaperLogger.supported() ?
new GeyserPaperLogger(this, getLogger()) : new GeyserSpigotLogger(getLogger());
private IGeyserPingPassthrough geyserSpigotPingPassthrough; private IGeyserPingPassthrough geyserSpigotPingPassthrough;
private GeyserSpigotWorldManager geyserWorldManager; private GeyserSpigotWorldManager geyserWorldManager;
@ -116,12 +116,12 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
// We depend on this as a fallback in certain scenarios // We depend on this as a fallback in certain scenarios
BlockData.class.getMethod("getAsString"); BlockData.class.getMethod("getAsString");
} catch (ClassNotFoundException | NoSuchMethodException e) { } catch (ClassNotFoundException | NoSuchMethodException e) {
getLogger().severe("*********************************************"); geyserLogger.error("*********************************************");
getLogger().severe(""); geyserLogger.error("");
getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server.header")); geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server.header"));
getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server.message", "1.13.2")); geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server.message", "1.13.2"));
getLogger().severe(""); geyserLogger.error("");
getLogger().severe("*********************************************"); geyserLogger.error("*********************************************");
Bukkit.getPluginManager().disablePlugin(this); Bukkit.getPluginManager().disablePlugin(this);
return; return;
} }
@ -130,12 +130,12 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
Class.forName("net.md_5.bungee.chat.ComponentSerializer"); Class.forName("net.md_5.bungee.chat.ComponentSerializer");
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
if (!PaperAdventure.canSendMessageUsingComponent()) { // Prepare for Paper eventually removing Bungee chat if (!PaperAdventure.canSendMessageUsingComponent()) { // Prepare for Paper eventually removing Bungee chat
getLogger().severe("*********************************************"); geyserLogger.error("*********************************************");
getLogger().severe(""); geyserLogger.error("");
getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server_type.header", getServer().getName())); geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server_type.header", getServer().getName()));
getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server_type.message", "Paper")); geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server_type.message", "Paper"));
getLogger().severe(""); geyserLogger.error("");
getLogger().severe("*********************************************"); geyserLogger.error("*********************************************");
Bukkit.getPluginManager().disablePlugin(this); Bukkit.getPluginManager().disablePlugin(this);
return; return;
} }
@ -144,11 +144,11 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
try { try {
Class.forName("io.netty.util.internal.ObjectPool$ObjectCreator"); Class.forName("io.netty.util.internal.ObjectPool$ObjectCreator");
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
getLogger().severe("*********************************************"); geyserLogger.error("*********************************************");
getLogger().severe(""); geyserLogger.error("");
getLogger().severe("This version of Spigot is using an outdated version of netty. Please use Paper instead!"); geyserLogger.error("This version of Spigot is using an outdated version of netty. Please use Paper instead!");
getLogger().severe(""); geyserLogger.error("");
getLogger().severe("*********************************************"); geyserLogger.error("*********************************************");
Bukkit.getPluginManager().disablePlugin(this); Bukkit.getPluginManager().disablePlugin(this);
return; return;
} }
@ -156,8 +156,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
if (!loadConfig()) { if (!loadConfig()) {
return; return;
} }
this.geyserLogger = GeyserPaperLogger.supported() ? new GeyserPaperLogger(this, getLogger(), geyserConfig.isDebugMode()) this.geyserLogger.setDebug(geyserConfig.isDebugMode());
: new GeyserSpigotLogger(getLogger(), geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
// Turn "(MC: 1.16.4)" into 1.16.4. // Turn "(MC: 1.16.4)" into 1.16.4.
@ -268,7 +267,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
this.geyserWorldManager = new GeyserSpigotNativeWorldManager(this, isPaper); this.geyserWorldManager = new GeyserSpigotNativeWorldManager(this, isPaper);
} }
geyserLogger.debug("Using world manager of type: " + this.geyserWorldManager.getClass().getSimpleName()); geyserLogger.debug("Using world manager of type: " + this.geyserWorldManager.getClass().getSimpleName());
} catch (Exception e) { } catch (Throwable e) {
if (geyserConfig.isDebugMode()) { if (geyserConfig.isDebugMode()) {
geyserLogger.debug("Error while attempting to find NMS adapter. Most likely, this can be safely ignored. :)"); geyserLogger.debug("Error while attempting to find NMS adapter. Most likely, this can be safely ignored. :)");
e.printStackTrace(); e.printStackTrace();
@ -489,7 +488,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserSpigotConfiguration.class); this.geyserConfig = FileUtils.loadConfig(configFile, GeyserSpigotConfiguration.class);
ConfigLoaderTemp.load(GeyserPluginConfig.class); ConfigLoaderTemp.load(GeyserPluginConfig.class);
} catch (IOException ex) { } catch (IOException ex) {
getLogger().log(Level.SEVERE, GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex); geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
ex.printStackTrace(); ex.printStackTrace();
Bukkit.getPluginManager().disablePlugin(this); Bukkit.getPluginManager().disablePlugin(this);
return false; return false;

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -0,0 +1,62 @@
/*
* Copyright (c) 2024 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.viaproxy.listener;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.net.HostAndPort;
import org.geysermc.event.PostOrder;
import org.geysermc.event.subscribe.Subscribe;
import org.geysermc.geyser.api.event.bedrock.SessionLoginEvent;
import org.geysermc.geyser.api.event.java.ServerTransferEvent;
import org.geysermc.geyser.session.GeyserSession;
import java.util.Map;
import java.util.concurrent.TimeUnit;
public class GeyserServerTransferListener {
private final Cache<String, Map<String, byte[]>> cookieStorages = CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES).build();
@Subscribe(postOrder = PostOrder.FIRST)
private void onServerTransfer(final ServerTransferEvent event) {
this.cookieStorages.put(event.connection().xuid(), event.cookies());
final GeyserSession geyserSession = (GeyserSession) event.connection();
final HostAndPort hostAndPort = HostAndPort.fromString(geyserSession.getClientData().getServerAddress()).withDefaultPort(19132);
event.bedrockHost(hostAndPort.getHost());
event.bedrockPort(hostAndPort.getPort());
}
@Subscribe(postOrder = PostOrder.FIRST)
private void onSessionLogin(final SessionLoginEvent event) {
final Map<String, byte[]> cookies = this.cookieStorages.asMap().remove(event.connection().xuid());
if (cookies != null) {
event.cookies(cookies);
event.transferring(true);
}
}
}

Datei anzeigen

@ -27,7 +27,6 @@ dependencies {
implementation(libs.websocket) implementation(libs.websocket)
api(libs.bundles.protocol) api(libs.bundles.protocol)
implementation(libs.blockstateupdater)
api(libs.mcauthlib) api(libs.mcauthlib)
api(libs.mcprotocollib) { api(libs.mcprotocollib) {

Datei anzeigen

@ -47,6 +47,7 @@ import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
import org.geysermc.api.Geyser; import org.geysermc.api.Geyser;
import org.geysermc.cumulus.form.Form; import org.geysermc.cumulus.form.Form;
import org.geysermc.cumulus.form.util.FormBuilder; import org.geysermc.cumulus.form.util.FormBuilder;
import org.geysermc.erosion.packet.Packets;
import org.geysermc.floodgate.crypto.AesCipher; import org.geysermc.floodgate.crypto.AesCipher;
import org.geysermc.floodgate.crypto.AesKeyProducer; import org.geysermc.floodgate.crypto.AesKeyProducer;
import org.geysermc.floodgate.crypto.Base64Topping; import org.geysermc.floodgate.crypto.Base64Topping;
@ -79,6 +80,7 @@ import org.geysermc.geyser.scoreboard.ScoreboardUpdater;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.PendingMicrosoftAuthentication; import org.geysermc.geyser.session.PendingMicrosoftAuthentication;
import org.geysermc.geyser.session.SessionManager; import org.geysermc.geyser.session.SessionManager;
import org.geysermc.geyser.session.cache.RegistryCache;
import org.geysermc.geyser.skin.FloodgateSkinUploader; import org.geysermc.geyser.skin.FloodgateSkinUploader;
import org.geysermc.geyser.skin.ProvidedSkins; import org.geysermc.geyser.skin.ProvidedSkins;
import org.geysermc.geyser.skin.SkinProvider; import org.geysermc.geyser.skin.SkinProvider;
@ -217,6 +219,8 @@ public class GeyserImpl implements GeyserApi {
Registries.init(); Registries.init();
BlockRegistries.init(); BlockRegistries.init();
RegistryCache.init();
/* Initialize translators */ /* Initialize translators */
EntityDefinitions.init(); EntityDefinitions.init();
MessageTranslator.init(); MessageTranslator.init();
@ -389,7 +393,7 @@ public class GeyserImpl implements GeyserApi {
this.newsHandler = new NewsHandler(BRANCH, this.buildNumber()); this.newsHandler = new NewsHandler(BRANCH, this.buildNumber());
//Packets.initGeyser(); Packets.initGeyser();
if (Epoll.isAvailable()) { if (Epoll.isAvailable()) {
this.erosionUnixListener = new UnixSocketClientListener(); this.erosionUnixListener = new UnixSocketClientListener();
@ -772,6 +776,7 @@ public class GeyserImpl implements GeyserApi {
return 0; return 0;
} }
//noinspection DataFlowIssue
return Integer.parseInt(BUILD_NUMBER); return Integer.parseInt(BUILD_NUMBER);
} }

Datei anzeigen

@ -137,7 +137,7 @@ public class Entity implements GeyserEntity {
this.valid = false; this.valid = false;
this.propertyManager = new GeyserEntityPropertyManager(definition.registeredProperties()); this.propertyManager = definition.registeredProperties() == null ? null : new GeyserEntityPropertyManager(definition.registeredProperties());
setPosition(position); setPosition(position);
setAirSupply(getMaxAir()); setAirSupply(getMaxAir());
@ -364,7 +364,7 @@ public class Entity implements GeyserEntity {
return; return;
} }
if (propertyManager.hasProperties()) { if (propertyManager != null && propertyManager.hasProperties()) {
SetEntityDataPacket entityDataPacket = new SetEntityDataPacket(); SetEntityDataPacket entityDataPacket = new SetEntityDataPacket();
entityDataPacket.setRuntimeEntityId(geyserId); entityDataPacket.setRuntimeEntityId(geyserId);
propertyManager.applyIntProperties(entityDataPacket.getProperties().getIntProperties()); propertyManager.applyIntProperties(entityDataPacket.getProperties().getIntProperties());

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -35,6 +35,7 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket; import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
import org.geysermc.geyser.entity.attribute.GeyserAttributeType; import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
import org.geysermc.geyser.item.Items; import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.network.GameProtocol;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.AttributeUtils; import org.geysermc.geyser.util.AttributeUtils;
import org.geysermc.geyser.util.DimensionUtils; import org.geysermc.geyser.util.DimensionUtils;
@ -60,16 +61,14 @@ public class SessionPlayerEntity extends PlayerEntity {
*/ */
@Getter @Getter
protected final Map<GeyserAttributeType, AttributeData> attributes = new Object2ObjectOpenHashMap<>(); protected final Map<GeyserAttributeType, AttributeData> attributes = new Object2ObjectOpenHashMap<>();
/**
* Whether to check for updated speed after all entity metadata has been processed
*/
private boolean refreshSpeed = false;
/** /**
* Used in PlayerInputTranslator for movement checks. * Used in PlayerInputTranslator for movement checks.
*/ */
@Getter @Getter
private boolean isRidingInFront; private boolean isRidingInFront;
private int lastAirSupply = getMaxAir();
public SessionPlayerEntity(GeyserSession session) { public SessionPlayerEntity(GeyserSession session) {
super(session, -1, 1, null, Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0, null, null); super(session, -1, 1, null, Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0, null, null);
@ -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) // TODO: proper fix, BDS somehow does it? https://paste.gg/p/anonymous/3adfb7612f1540be80fa03a2281f93dc (BDS 1.20.13)
if (!this.session.getGameMode().equals(GameMode.SPECTATOR)) { if (!this.session.getGameMode().equals(GameMode.SPECTATOR)) {
super.setFlags(entityMetadata); super.setFlags(entityMetadata);
session.setSwimmingInWater((entityMetadata.getPrimitiveValue() & 0x10) == 0x10 && getFlag(EntityFlag.SPRINTING));
} }
refreshSpeed = true;
} }
/** /**
@ -150,7 +147,6 @@ public class SessionPlayerEntity extends PlayerEntity {
public void setPose(Pose pose) { public void setPose(Pose pose) {
super.setPose(pose); super.setPose(pose);
session.setPose(pose); session.setPose(pose);
refreshSpeed = true;
} }
public float getMaxHealth() { public float getMaxHealth() {
@ -167,7 +163,13 @@ public class SessionPlayerEntity extends PlayerEntity {
@Override @Override
protected void setAirSupply(int amount) { protected void setAirSupply(int amount) {
if (amount == getMaxAir()) { // Seemingly required to be sent as of Bedrock 1.21. Otherwise, bubbles will appear as empty
// Also, this changes how the air bubble graphics/sounds are presented. Breathing on means sound effects and
// the bubbles visually pop
setFlag(EntityFlag.BREATHING, amount >= this.lastAirSupply);
this.lastAirSupply = amount;
if (amount == getMaxAir() && GameProtocol.isPre1_21_0(session)) {
super.setAirSupply(0); // Hide the bubble counter from the UI for the player super.setAirSupply(0); // Hide the bubble counter from the UI for the player
} else { } else {
super.setAirSupply(amount); super.setAirSupply(amount);
@ -199,21 +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 @Override
protected void updateAttribute(Attribute javaAttribute, List<AttributeData> newAttributes) { protected void updateAttribute(Attribute javaAttribute, List<AttributeData> newAttributes) {
if (javaAttribute.getType() == AttributeType.Builtin.GENERIC_ATTACK_SPEED) { if (javaAttribute.getType() == AttributeType.Builtin.GENERIC_ATTACK_SPEED) {
@ -226,17 +213,6 @@ public class SessionPlayerEntity extends PlayerEntity {
@Override @Override
protected AttributeData calculateAttribute(Attribute javaAttribute, GeyserAttributeType type) { protected AttributeData calculateAttribute(Attribute javaAttribute, GeyserAttributeType type) {
AttributeData attributeData = super.calculateAttribute(javaAttribute, type); AttributeData attributeData = super.calculateAttribute(javaAttribute, type);
if (javaAttribute.getType() == AttributeType.Builtin.GENERIC_MOVEMENT_SPEED) {
session.setOriginalSpeedAttribute(attributeData.getValue());
AttributeData speedAttribute = session.adjustSpeed();
if (speedAttribute != null) {
// Overwrite the attribute with our own
this.attributes.put(type, speedAttribute);
return speedAttribute;
}
}
this.attributes.put(type, attributeData); this.attributes.put(type, attributeData);
return attributeData; return attributeData;
} }

Datei anzeigen

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

Datei anzeigen

@ -25,14 +25,13 @@
package org.geysermc.geyser.erosion; 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 io.netty.channel.Channel;
import it.unimi.dsi.fastutil.Pair;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2IntArrayMap; import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import org.cloudburstmc.math.vector.Vector3i; 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.ErosionPacketSender;
import org.geysermc.erosion.packet.backendbound.BackendboundInitializePacket; import org.geysermc.erosion.packet.backendbound.BackendboundInitializePacket;
import org.geysermc.erosion.packet.backendbound.BackendboundPacket; import org.geysermc.erosion.packet.backendbound.BackendboundPacket;
import org.geysermc.erosion.packet.geyserbound.GeyserboundBatchBlockIdPacket; import org.geysermc.erosion.packet.geyserbound.*;
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.geyser.level.block.BlockStateValues; import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.level.block.property.Properties;
import org.geysermc.geyser.level.block.type.Block;
import org.geysermc.geyser.level.block.type.BlockState;
import org.geysermc.geyser.level.physics.Direction; import org.geysermc.geyser.level.physics.Direction;
import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.network.GameProtocol;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.PistonCache; import org.geysermc.geyser.session.cache.PistonCache;
import org.geysermc.geyser.translator.level.block.entity.PistonBlockEntity; import org.geysermc.geyser.translator.level.block.entity.PistonBlockEntity;
import org.geysermc.geyser.util.BlockEntityUtils; 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.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@ -71,7 +67,7 @@ public final class GeyserboundPacketHandlerImpl extends AbstractGeyserboundPacke
@Setter @Setter
private CompletableFuture<int[]> pendingBatchLookup = null; private CompletableFuture<int[]> pendingBatchLookup = null;
@Setter @Setter
private CompletableFuture<DataComponents> pickBlockLookup = null; private CompletableFuture<Int2ObjectMap<byte[]>> pickBlockLookup = null;
private final AtomicInteger nextTransactionId = new AtomicInteger(1); private final AtomicInteger nextTransactionId = new AtomicInteger(1);
@ -127,7 +123,7 @@ public final class GeyserboundPacketHandlerImpl extends AbstractGeyserboundPacke
} }
CompletableFuture<Integer> future = this.asyncPendingLookups.remove(transactionId); CompletableFuture<Integer> future = this.asyncPendingLookups.remove(transactionId);
if (future != null) { 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(":"); placeBlockSoundPacket.setIdentifier(":");
session.sendUpstreamPacket(placeBlockSoundPacket); session.sendUpstreamPacket(placeBlockSoundPacket);
session.setLastBlockPlacePosition(null); session.setLastBlockPlacePosition(null);
session.setLastBlockPlacedId(null); session.setLastBlockPlaced(null);
} }
@Override @Override
public void handlePickBlock(GeyserboundPickBlockPacket packet) { public void handlePickBlock(GeyserboundPickBlockPacket packet) {
if (this.pickBlockLookup != null) { if (this.pickBlockLookup != null) {
//this.pickBlockLookup.complete(packet.getTag()); // TODO 1.20.5 this.pickBlockLookup.complete(packet.getComponents());
} }
} }
@Override @Override
public void handlePistonEvent(GeyserboundPistonEventPacket packet) { public void handlePistonEvent(GeyserboundPistonEventPacket packet) {
Direction orientation = BlockStateValues.getPistonOrientation(packet.getBlockId()); Direction orientation = BlockState.of(packet.getBlockId()).getValue(Properties.FACING);
Vector3i position = packet.getPos(); Vector3i position = packet.getPos();
boolean isExtend = packet.isExtend(); boolean isExtend = packet.isExtend();
var stream = packet.getAttachedBlocks() var stream = packet.getAttachedBlocks()
.object2IntEntrySet() .object2IntEntrySet()
.stream() .stream()
.filter(entry -> BlockStateValues.canPistonMoveBlock(entry.getIntValue(), isExtend)); .map(entry -> Pair.of(entry.getKey(), BlockState.of(entry.getIntValue())))
Object2IntMap<Vector3i> attachedBlocks = new Object2IntArrayMap<>(); .filter(pair -> BlockStateValues.canPistonMoveBlock(pair.value(), isExtend));
stream.forEach(entry -> attachedBlocks.put(entry.getKey(), entry.getIntValue())); Object2ObjectMap<Vector3i, BlockState> attachedBlocks = new Object2ObjectArrayMap<>();
stream.forEach(pair -> attachedBlocks.put(pair.key(), pair.value()));
session.executeInEventLoop(() -> { session.executeInEventLoop(() -> {
PistonCache pistonCache = session.getPistonCache(); PistonCache pistonCache = session.getPistonCache();

Datei anzeigen

@ -32,24 +32,50 @@ import org.cloudburstmc.math.vector.Vector2f;
import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.CameraShakeAction; import org.cloudburstmc.protocol.bedrock.data.CameraShakeAction;
import org.cloudburstmc.protocol.bedrock.data.CameraShakeType; 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.CameraEase;
import org.cloudburstmc.protocol.bedrock.data.camera.CameraFadeInstruction; import org.cloudburstmc.protocol.bedrock.data.camera.CameraFadeInstruction;
import org.cloudburstmc.protocol.bedrock.data.camera.CameraSetInstruction; import org.cloudburstmc.protocol.bedrock.data.camera.CameraSetInstruction;
import org.cloudburstmc.protocol.bedrock.packet.CameraInstructionPacket; import org.cloudburstmc.protocol.bedrock.packet.CameraInstructionPacket;
import org.cloudburstmc.protocol.bedrock.packet.CameraShakePacket; import org.cloudburstmc.protocol.bedrock.packet.CameraShakePacket;
import org.cloudburstmc.protocol.bedrock.packet.PlayerFogPacket; 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.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 { 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; private final GeyserSession session;
@Getter
private CameraPerspective cameraPerspective;
/** /**
* All fog effects that are currently applied to the client. * 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<>(); 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) { public GeyserCameraData(GeyserSession session) {
this.session = session; this.session = session;
} }
@ -223,4 +257,67 @@ public class GeyserCameraData implements CameraData {
public boolean isCameraLocked() { public boolean isCameraLocked() {
return !this.cameraLockOwners.isEmpty(); 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);
}
}
}
} }

Datei anzeigen

@ -25,6 +25,7 @@
package org.geysermc.geyser.inventory; package org.geysermc.geyser.inventory;
import org.geysermc.geyser.level.block.type.Block;
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType; import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
import lombok.Getter; import lombok.Getter;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
@ -83,9 +84,9 @@ public class Container extends Inventory {
* Will be overwritten for droppers. * Will be overwritten for droppers.
* *
* @param usingRealBlock whether this container is using a real container or not * @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; isUsingRealBlock = usingRealBlock;
} }
} }

Datei anzeigen

@ -26,6 +26,8 @@
package org.geysermc.geyser.inventory; package org.geysermc.geyser.inventory;
import lombok.Getter; 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.session.GeyserSession;
import org.geysermc.geyser.translator.inventory.Generic3X3InventoryTranslator; import org.geysermc.geyser.translator.inventory.Generic3X3InventoryTranslator;
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType; import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
@ -44,10 +46,10 @@ public class Generic3X3Container extends Container {
} }
@Override @Override
public void setUsingRealBlock(boolean usingRealBlock, String javaBlockId) { public void setUsingRealBlock(boolean usingRealBlock, Block block) {
super.setUsingRealBlock(usingRealBlock, javaBlockId); super.setUsingRealBlock(usingRealBlock, block);
if (usingRealBlock) { if (usingRealBlock) {
isDropper = javaBlockId.startsWith("minecraft:dropper"); isDropper = block == Blocks.DROPPER;
} }
} }
} }

Datei anzeigen

@ -69,7 +69,7 @@ public class GeyserItemStack {
return of(javaId, amount, null); 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); return new GeyserItemStack(javaId, amount, components);
} }

Datei anzeigen

@ -36,11 +36,11 @@ import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.inventory.Container; import org.geysermc.geyser.inventory.Container;
import org.geysermc.geyser.inventory.Inventory; import org.geysermc.geyser.inventory.Inventory;
import org.geysermc.geyser.inventory.LecternContainer; 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.BlockRegistries;
import org.geysermc.geyser.registry.type.BlockMapping;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.inventory.InventoryTranslator; import org.geysermc.geyser.translator.inventory.InventoryTranslator;
import org.geysermc.geyser.util.BlockUtils;
import org.geysermc.geyser.util.InventoryUtils; import org.geysermc.geyser.util.InventoryUtils;
import java.util.Collections; import java.util.Collections;
@ -55,20 +55,24 @@ public class BlockInventoryHolder extends InventoryHolder {
/** /**
* The default Java block ID to translate as a fake block * 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 ContainerType containerType;
private final Set<String> validBlocks; private final Set<Block> validBlocks;
public BlockInventoryHolder(String javaBlockIdentifier, ContainerType containerType, String... validBlocks) { public BlockInventoryHolder(Block defaultJavaBlock, ContainerType containerType, Block... validBlocks) {
this.defaultJavaBlockState = BlockRegistries.JAVA_IDENTIFIER_TO_ID.get().getInt(javaBlockIdentifier); this(defaultJavaBlock.defaultBlockState(), containerType, validBlocks);
}
public BlockInventoryHolder(BlockState defaultJavaBlockState, ContainerType containerType, Block... validBlocks) {
this.defaultJavaBlockState = defaultJavaBlockState;
this.containerType = containerType; this.containerType = containerType;
if (validBlocks != null) { if (validBlocks != null) {
Set<String> validBlocksTemp = new HashSet<>(validBlocks.length + 1); Set<Block> validBlocksTemp = new HashSet<>(validBlocks.length + 1);
Collections.addAll(validBlocksTemp, validBlocks); Collections.addAll(validBlocksTemp, validBlocks);
validBlocksTemp.add(BlockUtils.getCleanIdentifier(javaBlockIdentifier)); validBlocksTemp.add(defaultJavaBlockState.block());
this.validBlocks = Set.copyOf(validBlocksTemp); this.validBlocks = Set.copyOf(validBlocksTemp);
} else { } 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)) { if (checkInteractionPosition(session)) {
// Then, check to see if the interacted block is valid for this inventory by ensuring the block state identifier is valid // 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 // and the bedrock block is vanilla
int javaBlockId = session.getGeyser().getWorldManager().getBlockAt(session, session.getLastInteractionBlockPosition()); BlockState state = session.getGeyser().getWorldManager().blockAt(session, session.getLastInteractionBlockPosition());
if (!BlockRegistries.CUSTOM_BLOCK_STATE_OVERRIDES.get().containsKey(javaBlockId)) { if (!BlockRegistries.CUSTOM_BLOCK_STATE_OVERRIDES.get().containsKey(state.javaId())) {
String[] javaBlockString = BlockRegistries.JAVA_BLOCKS.getOrDefault(javaBlockId, BlockMapping.DEFAULT).getJavaIdentifier().split("\\["); if (isValidBlock(state)) {
if (isValidBlock(javaBlockString)) {
// We can safely use this block // We can safely use this block
inventory.setHolderPosition(session.getLastInteractionBlockPosition()); inventory.setHolderPosition(session.getLastInteractionBlockPosition());
((Container) inventory).setUsingRealBlock(true, javaBlockString[0]); ((Container) inventory).setUsingRealBlock(true, state.block());
setCustomName(session, session.getLastInteractionBlockPosition(), inventory, javaBlockId); setCustomName(session, session.getLastInteractionBlockPosition(), inventory, state);
return true; return true;
} }
@ -125,11 +128,11 @@ public class BlockInventoryHolder extends InventoryHolder {
/** /**
* @return true if this Java block ID can be used for player inventory. * @return true if this Java block ID can be used for player inventory.
*/ */
protected boolean isValidBlock(String[] javaBlockString) { protected boolean isValidBlock(BlockState blockState) {
return this.validBlocks.contains(javaBlockString[0]); 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() NbtMap tag = NbtMap.builder()
.putInt("x", position.getX()) .putInt("x", position.getX())
.putInt("y", position.getY()) .putInt("y", position.getY())
@ -160,6 +163,7 @@ public class BlockInventoryHolder extends InventoryHolder {
ContainerClosePacket packet = new ContainerClosePacket(); ContainerClosePacket packet = new ContainerClosePacket();
packet.setId((byte) inventory.getBedrockId()); packet.setId((byte) inventory.getBedrockId());
packet.setServerInitiated(true); packet.setServerInitiated(true);
packet.setType(ContainerType.CONTAINER);
session.sendUpstreamPacket(packet); session.sendUpstreamPacket(packet);
return; return;
} }

Datei-Diff unterdrückt, da er zu groß ist Diff laden

Datei anzeigen

@ -35,6 +35,7 @@ import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtType; import org.cloudburstmc.nbt.NbtType;
import org.geysermc.geyser.inventory.item.BannerPattern; import org.geysermc.geyser.inventory.item.BannerPattern;
import org.geysermc.geyser.inventory.item.DyeColor; 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.registry.type.ItemMapping;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.registry.JavaRegistry; import org.geysermc.geyser.session.cache.registry.JavaRegistry;
@ -199,8 +200,8 @@ public class BannerItem extends BlockItem {
return null; return null;
} }
public BannerItem(String javaIdentifier, Builder builder) { public BannerItem(Builder builder, Block block, Block... otherBlocks) {
super(javaIdentifier, builder); super(builder, block, otherBlocks);
} }
@Override @Override

Datei anzeigen

@ -25,11 +25,26 @@
package org.geysermc.geyser.item.type; package org.geysermc.geyser.item.type;
/** import org.geysermc.geyser.level.block.type.Block;
* TODO needed?
*/
public class BlockItem extends Item { 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); super(javaIdentifier, builder);
registerBlock(block, this);
for (Block otherBlock : otherBlocks) {
registerBlock(otherBlock, this);
}
} }
} }

Datei anzeigen

@ -27,6 +27,7 @@ package org.geysermc.geyser.item.type;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.nbt.NbtType; import org.cloudburstmc.nbt.NbtType;
import org.geysermc.geyser.level.block.type.Block;
import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.item.BedrockItemBuilder; import org.geysermc.geyser.translator.item.BedrockItemBuilder;
@ -38,8 +39,8 @@ import java.util.List;
public class DecoratedPotItem extends BlockItem { public class DecoratedPotItem extends BlockItem {
public DecoratedPotItem(String javaIdentifier, Builder builder) { public DecoratedPotItem(Builder builder, Block block, Block... otherBlocks) {
super(javaIdentifier, builder); super(builder, block, otherBlocks);
} }
@Override @Override

Datei anzeigen

@ -35,6 +35,7 @@ import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.inventory.item.Enchantment; import org.geysermc.geyser.inventory.item.Enchantment;
import org.geysermc.geyser.item.Items; 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.ItemMapping;
import org.geysermc.geyser.registry.type.ItemMappings; import org.geysermc.geyser.registry.type.ItemMappings;
import org.geysermc.geyser.session.GeyserSession; 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 org.geysermc.mcprotocollib.protocol.data.game.item.component.ItemEnchantments;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
public class Item { public class Item {
/** private static final Map<Block, Item> BLOCK_TO_ITEM = new HashMap<>();
* 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 final String javaIdentifier; private final String javaIdentifier;
private int javaId = -1; private int javaId = -1;
private final int stackSize; 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) { protected final @Nullable NbtMap remapEnchantment(GeyserSession session, int enchantId, int level, BedrockItemBuilder builder) {
// TODO verify // TODO verify
// TODO streamline Enchantment process // 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() { public static Builder builder() {
return new Builder(); return new Builder();
} }

Datei anzeigen

@ -27,6 +27,7 @@ package org.geysermc.geyser.item.type;
import com.github.steveice10.mc.auth.data.GameProfile; import com.github.steveice10.mc.auth.data.GameProfile;
import org.checkerframework.checker.nullness.qual.NonNull; 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.session.GeyserSession;
import org.geysermc.geyser.text.ChatColor; import org.geysermc.geyser.text.ChatColor;
import org.geysermc.geyser.text.MinecraftLocale; 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.DataComponentType;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
public class PlayerHeadItem extends Item { public class PlayerHeadItem extends BlockItem {
public PlayerHeadItem(String javaIdentifier, Builder builder) { public PlayerHeadItem(Builder builder, Block block, Block... otherBlocks) {
super(javaIdentifier, builder); super(builder, block, otherBlocks);
} }
@Override @Override

Datei anzeigen

@ -30,6 +30,7 @@ import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder; import org.cloudburstmc.nbt.NbtMapBuilder;
import org.cloudburstmc.nbt.NbtType; import org.cloudburstmc.nbt.NbtType;
import org.geysermc.geyser.item.Items; 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.ItemMapping;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.item.BedrockItemBuilder; import org.geysermc.geyser.translator.item.BedrockItemBuilder;
@ -42,8 +43,8 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
public class ShulkerBoxItem extends BlockItem { public class ShulkerBoxItem extends BlockItem {
public ShulkerBoxItem(String javaIdentifier, Builder builder) { public ShulkerBoxItem(Builder builder, Block block, Block... otherBlocks) {
super(javaIdentifier, builder); super(builder, block, otherBlocks);
} }
@Override @Override

Datei anzeigen

@ -41,6 +41,7 @@ public class TippedArrowItem extends ArrowItem {
@Override @Override
public ItemData.Builder translateToBedrock(int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) { public ItemData.Builder translateToBedrock(int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) {
if (components != null) {
PotionContents potionContents = components.get(DataComponentType.POTION_CONTENTS); PotionContents potionContents = components.get(DataComponentType.POTION_CONTENTS);
if (potionContents != null) { if (potionContents != null) {
TippedArrowPotion tippedArrowPotion = TippedArrowPotion.of(potionContents.getPotionId()); TippedArrowPotion tippedArrowPotion = TippedArrowPotion.of(potionContents.getPotionId());
@ -52,6 +53,7 @@ public class TippedArrowItem extends ArrowItem {
} }
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); return super.translateToBedrock(count, components, mapping, mappings);
} }
} }

Datei anzeigen

@ -25,24 +25,20 @@
package org.geysermc.geyser.level; 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.Object2ObjectMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; 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.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.math.vector.Vector3i; import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.nbt.NbtMap; import org.geysermc.erosion.packet.backendbound.BackendboundBatchBlockRequestPacket;
import org.cloudburstmc.nbt.NbtMapBuilder; import org.geysermc.erosion.packet.backendbound.BackendboundBlockRequestPacket;
import org.geysermc.erosion.packet.backendbound.*; import org.geysermc.erosion.packet.backendbound.BackendboundPickBlockPacket;
import org.geysermc.erosion.util.BlockPositionIterator; import org.geysermc.erosion.util.BlockPositionIterator;
import org.geysermc.erosion.util.LecternUtils;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.BlockEntityUtils;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents; 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; import java.util.concurrent.CompletableFuture;
public class GeyserWorldManager extends WorldManager { public class GeyserWorldManager extends WorldManager {
@ -91,51 +87,6 @@ public class GeyserWorldManager extends WorldManager {
return false; 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 @Override
public void setGameRule(GeyserSession session, String name, Object value) { public void setGameRule(GeyserSession session, String name, Object value) {
super.setGameRule(session, name, value); super.setGameRule(session, name, value);
@ -179,9 +130,9 @@ public class GeyserWorldManager extends WorldManager {
if (erosionHandler == null) { if (erosionHandler == null) {
return super.getPickItemComponents(session, x, y, z, addNbtData); return super.getPickItemComponents(session, x, y, z, addNbtData);
} }
CompletableFuture<DataComponents> future = new CompletableFuture<>(); CompletableFuture<Int2ObjectMap<byte[]>> future = new CompletableFuture<>();
erosionHandler.setPickBlockLookup(future); erosionHandler.setPickBlockLookup(future);
erosionHandler.sendPacket(new BackendboundPickBlockPacket(Vector3i.from(x, y, z))); erosionHandler.sendPacket(new BackendboundPickBlockPacket(Vector3i.from(x, y, z)));
return future; return future.thenApply(RAW_TRANSFORMER);
} }
} }

Datei anzeigen

@ -25,19 +25,28 @@
package org.geysermc.geyser.level; 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.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.math.vector.Vector3i; import org.cloudburstmc.math.vector.Vector3i;
import org.geysermc.erosion.util.BlockPositionIterator; import org.geysermc.erosion.util.BlockPositionIterator;
import org.geysermc.geyser.level.block.type.BlockState;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode; 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.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 org.geysermc.mcprotocollib.protocol.data.game.setting.Difficulty;
import java.util.List; import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
/** /**
* Class that manages or retrieves various information * Class that manages or retrieves various information
@ -48,6 +57,16 @@ import java.util.concurrent.CompletableFuture;
*/ */
public abstract class WorldManager { 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 * Gets the Java block state at the specified location
* *
@ -97,40 +116,6 @@ public abstract class WorldManager {
*/ */
public abstract boolean hasOwnChunkCache(); 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 * 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) { public CompletableFuture<@Nullable DataComponents> getPickItemComponents(GeyserSession session, int x, int y, int z, boolean addExtraData) {
return CompletableFuture.completedFuture(null); 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;
}
};
} }

Datei anzeigen

@ -25,363 +25,19 @@
package org.geysermc.geyser.level.block; package org.geysermc.geyser.level.block;
import com.fasterxml.jackson.databind.JsonNode; import org.geysermc.geyser.level.block.property.Properties;
import it.unimi.dsi.fastutil.ints.*; import org.geysermc.geyser.level.block.type.Block;
import it.unimi.dsi.fastutil.objects.Object2IntMap; import org.geysermc.geyser.level.block.type.BlockState;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import org.geysermc.geyser.level.block.type.PistonBlock;
import org.geysermc.geyser.level.physics.Direction;
import org.geysermc.geyser.level.physics.PistonBehavior; import org.geysermc.geyser.level.physics.PistonBehavior;
import org.geysermc.geyser.registry.BlockRegistries; 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. * Used for block entities if the Java block state contains Bedrock block information.
*/ */
public final class BlockStateValues { 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; 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 * Checks if a block sticks to other blocks
* (Slime and honey blocks) * (Slime and honey blocks)
@ -389,8 +45,9 @@ public final class BlockStateValues {
* @param state The block state * @param state The block state
* @return True if the block sticks to adjacent blocks * @return True if the block sticks to adjacent blocks
*/ */
public static boolean isBlockSticky(int state) { public static boolean isBlockSticky(BlockState state) {
return state == JAVA_SLIME_BLOCK_ID || state == JAVA_HONEY_BLOCK_ID; 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 * @param stateB The block state of block b
* @return True if the blocks are attached to each other * @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 aSticky = isBlockSticky(stateA);
boolean bSticky = isBlockSticky(stateB); boolean bSticky = isBlockSticky(stateB);
if (aSticky && bSticky) { if (aSticky && bSticky) {
// Only matching sticky blocks are attached together // Only matching sticky blocks are attached together
// Honey + Honey & Slime + Slime // Honey + Honey & Slime + Slime
return stateA == stateB; return stateA.block() == stateB.block();
} }
return aSticky || bSticky; return aSticky || bSticky;
} }
@ -415,84 +72,33 @@ public final class BlockStateValues {
* @param state The block state of the block * @param state The block state of the block
* @return true if a piston can break the block * @return true if a piston can break the block
*/ */
public static boolean canPistonDestroyBlock(int state) { public static boolean canPistonDestroyBlock(BlockState state) {
return BlockRegistries.JAVA_BLOCKS.getOrDefault(state, BlockMapping.DEFAULT).getPistonBehavior() == PistonBehavior.DESTROY; return state.block().pushReaction() == PistonBehavior.DESTROY;
} }
public static boolean canPistonMoveBlock(int javaId, boolean isPushing) { public static boolean canPistonMoveBlock(BlockState state, boolean isPushing) {
if (javaId == JAVA_AIR_ID) { Block block = state.block();
if (block == Blocks.AIR) {
return true; return true;
} }
// Pistons can only be moved if they aren't extended if (block == Blocks.OBSIDIAN || block == Blocks.CRYING_OBSIDIAN || block == Blocks.RESPAWN_ANCHOR || block == Blocks.REINFORCED_DEEPSLATE) { // Hardcoded as of 1.20.5
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) {
return false; 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 BLOCK, DESTROY -> false;
case PUSH_ONLY -> isPushing; // Glazed terracotta can only be pushed 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. * 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 * @return The water level or -1 if the block isn't water
*/ */
public static int getWaterLevel(int state) { public static int getWaterLevel(int state) {
return WATER_LEVEL.getOrDefault(state, -1); BlockState blockState = BlockState.of(state);
if (!blockState.is(Blocks.WATER)) {
return -1;
} }
return blockState.getValue(Properties.LEVEL);
/**
* 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);
} }
/** /**
@ -544,31 +144,18 @@ public final class BlockStateValues {
* @param state BlockState of the block * @param state BlockState of the block
* @return The block's slipperiness * @return The block's slipperiness
*/ */
public static float getSlipperiness(int state) { public static float getSlipperiness(BlockState state) {
String blockIdentifier = BlockRegistries.JAVA_BLOCKS.getOrDefault(state, BlockMapping.DEFAULT).getJavaIdentifier(); Block block = state.block();
return switch (blockIdentifier) { if (block == Blocks.SLIME_BLOCK) {
case "minecraft:slime_block" -> 0.8f; return 0.8f;
case "minecraft:ice", "minecraft:packed_ice" -> 0.98f;
case "minecraft:blue_ice" -> 0.989f;
default -> 0.6f;
};
} }
if (block == Blocks.ICE || block == Blocks.PACKED_ICE) {
private static Direction getBlockDirection(String javaId) { return 0.98f;
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;
} }
throw new IllegalStateException(); if (block == Blocks.BLUE_ICE) {
return 0.989f;
}
return 0.6f;
} }
private BlockStateValues() { private BlockStateValues() {

Datei-Diff unterdrückt, da er zu groß ist Diff laden

Datei anzeigen

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

Datei anzeigen

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org * Copyright (c) 2024 GeyserMC. http://geysermc.org
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@ -23,12 +23,24 @@
* @link https://github.com/GeyserMC/Geyser * @link https://github.com/GeyserMC/Geyser
*/ */
/** package org.geysermc.geyser.level.block.property;
* Contains useful collections for use in Geyser.
* <p> public final class BooleanProperty extends Property<Boolean> {
* 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 private BooleanProperty(String name) {
* 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)} super(name);
* 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.
*/ @Override
package org.geysermc.geyser.util.collection; 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);
}
}

Datei anzeigen

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org * Copyright (c) 2024 GeyserMC. http://geysermc.org
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@ -23,18 +23,12 @@
* @link https://github.com/GeyserMC/Geyser * @link https://github.com/GeyserMC/Geyser
*/ */
package org.geysermc.geyser.level.block; package org.geysermc.geyser.level.block.property;
/** public enum ChestType {
* This stores all values of double chests that are part of the Java block state. SINGLE,
* LEFT,
* @param isFacingEast If true, then chest is facing east/west; if false, south/north RIGHT;
* @param isDirectionPositive If true, direction is positive (east/south); if false, direction is negative (west/north)
* @param isLeft If true, chest is the left of a pair; if false, chest is the right of a pair.
*/
public record DoubleChestValue(
boolean isFacingEast,
boolean isDirectionPositive,
boolean isLeft) {
public static final ChestType[] VALUES = values();
} }

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -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 + "]";
}
}

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org * Copyright (c) 2024 GeyserMC. http://geysermc.org
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@ -23,64 +23,36 @@
* @link https://github.com/GeyserMC/Geyser * @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.math.vector.Vector3i;
import org.cloudburstmc.nbt.NbtMap; import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder; import org.cloudburstmc.nbt.NbtMapBuilder;
import org.cloudburstmc.protocol.bedrock.packet.UpdateBlockPacket; 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.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.geyser.util.BlockEntityUtils;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
public class FlowerPotBlockEntityTranslator implements BedrockOnlyBlockEntity { public class FlowerPotBlock extends Block implements BedrockChunkWantsBlockEntityTag {
/** private final Block flower;
* @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 FlowerPotBlock(String javaIdentifier, Block flower, Builder builder) {
* Get the Nukkit CompoundTag of the flower pot. super(javaIdentifier, builder);
* this.flower = flower;
* @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();
} }
@Override @Override
public boolean isBlock(int blockState) { public void updateBlock(GeyserSession session, BlockState state, Vector3i position) {
return isFlowerBlock(blockState); super.updateBlock(session, state, position);
}
@Override NbtMap tag = createTag(session, position, state);
public void updateBlock(GeyserSession session, int blockState, Vector3i position) {
NbtMap tag = getTag(session, blockState, position);
BlockEntityUtils.updateBlockEntity(session, tag, position); BlockEntityUtils.updateBlockEntity(session, tag, position);
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket(); UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
updateBlockPacket.setDataLayer(0); updateBlockPacket.setDataLayer(0);
updateBlockPacket.setDefinition(session.getBlockMappings().getBedrockBlock(blockState)); updateBlockPacket.setDefinition(session.getBlockMappings().getBedrockBlock(state));
updateBlockPacket.setBlockPosition(position); updateBlockPacket.setBlockPosition(position);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS); updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK); updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
@ -88,4 +60,33 @@ public class FlowerPotBlockEntityTranslator implements BedrockOnlyBlockEntity {
session.sendUpstreamPacket(updateBlockPacket); session.sendUpstreamPacket(updateBlockPacket);
BlockEntityUtils.updateBlockEntity(session, tag, position); 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;
}
} }

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -39,6 +39,9 @@ import org.geysermc.geyser.entity.EntityDefinitions;
import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.type.Entity;
import org.geysermc.geyser.entity.type.player.PlayerEntity; import org.geysermc.geyser.entity.type.player.PlayerEntity;
import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.level.block.Blocks;
import org.geysermc.geyser.level.block.property.Properties;
import org.geysermc.geyser.level.block.type.BlockState;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.PistonCache; import org.geysermc.geyser.session.cache.PistonCache;
import org.geysermc.geyser.translator.collision.BlockCollision; import org.geysermc.geyser.translator.collision.BlockCollision;
@ -405,7 +408,8 @@ public class CollisionManager {
* @return if the player is currently in a water block * @return if the player is currently in a water block
*/ */
public boolean isPlayerInWater() { 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() { public boolean isWaterInEyes() {

Datei anzeigen

@ -224,7 +224,7 @@ class CodecProcessor {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
static BedrockCodec processCodec(BedrockCodec codec) { static BedrockCodec processCodec(BedrockCodec codec) {
return codec.toBuilder() BedrockCodec.Builder codecBuilder = codec.toBuilder()
// Illegal unused serverbound EDU packets // Illegal unused serverbound EDU packets
.updateSerializer(PhotoTransferPacket.class, ILLEGAL_SERIALIZER) .updateSerializer(PhotoTransferPacket.class, ILLEGAL_SERIALIZER)
.updateSerializer(LabTablePacket.class, ILLEGAL_SERIALIZER) .updateSerializer(LabTablePacket.class, ILLEGAL_SERIALIZER)
@ -232,10 +232,11 @@ class CodecProcessor {
.updateSerializer(CreatePhotoPacket.class, ILLEGAL_SERIALIZER) .updateSerializer(CreatePhotoPacket.class, ILLEGAL_SERIALIZER)
.updateSerializer(NpcRequestPacket.class, ILLEGAL_SERIALIZER) .updateSerializer(NpcRequestPacket.class, ILLEGAL_SERIALIZER)
.updateSerializer(PhotoInfoRequestPacket.class, ILLEGAL_SERIALIZER) .updateSerializer(PhotoInfoRequestPacket.class, ILLEGAL_SERIALIZER)
// Illegal unused serverbound packets for featured servers // Unused serverbound packets for featured servers, which is for some reason still occasionally sent
.updateSerializer(PurchaseReceiptPacket.class, ILLEGAL_SERIALIZER) .updateSerializer(PurchaseReceiptPacket.class, IGNORED_SERIALIZER)
// Illegal unused serverbound packets that are deprecated // Illegal unused serverbound packets that are deprecated
.updateSerializer(ClientCheatAbilityPacket.class, ILLEGAL_SERIALIZER) .updateSerializer(ClientCheatAbilityPacket.class, ILLEGAL_SERIALIZER)
.updateSerializer(CraftingEventPacket.class, ILLEGAL_SERIALIZER)
// Illegal unusued serverbound packets that relate to unused features // Illegal unusued serverbound packets that relate to unused features
.updateSerializer(PlayerAuthInputPacket.class, ILLEGAL_SERIALIZER) .updateSerializer(PlayerAuthInputPacket.class, ILLEGAL_SERIALIZER)
.updateSerializer(ClientCacheBlobStatusPacket.class, ILLEGAL_SERIALIZER) .updateSerializer(ClientCacheBlobStatusPacket.class, ILLEGAL_SERIALIZER)
@ -243,7 +244,6 @@ class CodecProcessor {
.updateSerializer(SubChunkRequestPacket.class, ILLEGAL_SERIALIZER) .updateSerializer(SubChunkRequestPacket.class, ILLEGAL_SERIALIZER)
.updateSerializer(GameTestRequestPacket.class, ILLEGAL_SERIALIZER) .updateSerializer(GameTestRequestPacket.class, ILLEGAL_SERIALIZER)
// Ignored serverbound packets // Ignored serverbound packets
.updateSerializer(CraftingEventPacket.class, IGNORED_SERIALIZER) // Make illegal when 1.20.40 is removed
.updateSerializer(ClientToServerHandshakePacket.class, IGNORED_SERIALIZER) .updateSerializer(ClientToServerHandshakePacket.class, IGNORED_SERIALIZER)
.updateSerializer(EntityFallPacket.class, IGNORED_SERIALIZER) .updateSerializer(EntityFallPacket.class, IGNORED_SERIALIZER)
.updateSerializer(MapCreateLockedCopyPacket.class, IGNORED_SERIALIZER) .updateSerializer(MapCreateLockedCopyPacket.class, IGNORED_SERIALIZER)
@ -260,22 +260,25 @@ class CodecProcessor {
.updateSerializer(PlayerHotbarPacket.class, PLAYER_HOTBAR_SERIALIZER) .updateSerializer(PlayerHotbarPacket.class, PLAYER_HOTBAR_SERIALIZER)
.updateSerializer(PlayerSkinPacket.class, PLAYER_SKIN_SERIALIZER) .updateSerializer(PlayerSkinPacket.class, PLAYER_SKIN_SERIALIZER)
.updateSerializer(SetEntityDataPacket.class, SET_ENTITY_DATA_SERIALIZER) .updateSerializer(SetEntityDataPacket.class, SET_ENTITY_DATA_SERIALIZER)
.updateSerializer(SetEntityMotionPacket.class, codec.getProtocolVersion() < 662 ? .updateSerializer(SetEntityMotionPacket.class, SET_ENTITY_MOTION_SERIALIZER_V662)
SET_ENTITY_MOTION_SERIALIZER_V291 :
SET_ENTITY_MOTION_SERIALIZER_V662)
.updateSerializer(SetEntityLinkPacket.class, SET_ENTITY_LINK_SERIALIZER) .updateSerializer(SetEntityLinkPacket.class, SET_ENTITY_LINK_SERIALIZER)
// Valid serverbound packets where reading of some fields can be skipped // Valid serverbound packets where reading of some fields can be skipped
.updateSerializer(MobEquipmentPacket.class, MOB_EQUIPMENT_SERIALIZER) .updateSerializer(MobEquipmentPacket.class, MOB_EQUIPMENT_SERIALIZER)
// // Illegal bidirectional packets // Illegal bidirectional packets
.updateSerializer(DebugInfoPacket.class, ILLEGAL_SERIALIZER) .updateSerializer(DebugInfoPacket.class, ILLEGAL_SERIALIZER)
.updateSerializer(EditorNetworkPacket.class, ILLEGAL_SERIALIZER) .updateSerializer(EditorNetworkPacket.class, ILLEGAL_SERIALIZER)
.updateSerializer(ScriptMessagePacket.class, ILLEGAL_SERIALIZER) .updateSerializer(ScriptMessagePacket.class, ILLEGAL_SERIALIZER)
// // Ignored bidirectional packets // Ignored bidirectional packets
.updateSerializer(ClientCacheStatusPacket.class, IGNORED_SERIALIZER) .updateSerializer(ClientCacheStatusPacket.class, IGNORED_SERIALIZER)
.updateSerializer(SimpleEventPacket.class, IGNORED_SERIALIZER) .updateSerializer(SimpleEventPacket.class, IGNORED_SERIALIZER)
.updateSerializer(TickSyncPacket.class, IGNORED_SERIALIZER) .updateSerializer(MultiplayerSettingsPacket.class, IGNORED_SERIALIZER);
.updateSerializer(MultiplayerSettingsPacket.class, IGNORED_SERIALIZER)
.build(); if (codec.getProtocolVersion() < 685) {
// Ignored bidirectional packets
codecBuilder.updateSerializer(TickSyncPacket.class, IGNORED_SERIALIZER);
}
return codecBuilder.build();
} }
/** /**

Datei anzeigen

@ -27,11 +27,8 @@ package org.geysermc.geyser.network;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec; 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.v671.Bedrock_v671;
import org.cloudburstmc.protocol.bedrock.codec.v685.Bedrock_v685;
import org.cloudburstmc.protocol.bedrock.netty.codec.packet.BedrockPacketCodec; import org.cloudburstmc.protocol.bedrock.netty.codec.packet.BedrockPacketCodec;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.codec.MinecraftCodec; 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 * Default Bedrock codec that should act as a fallback. Should represent the latest available
* release of the game that Geyser supports. * release of the game that Geyser supports.
*/ */
public static final BedrockCodec DEFAULT_BEDROCK_CODEC = CodecProcessor.processCodec(Bedrock_v671.CODEC.toBuilder() public static final BedrockCodec DEFAULT_BEDROCK_CODEC = CodecProcessor.processCodec(Bedrock_v685.CODEC.toBuilder()
.minecraftVersion("1.20.81") .minecraftVersion("1.21.0")
.build()); .build());
/** /**
@ -66,20 +63,11 @@ public final class GameProtocol {
private static final PacketCodec DEFAULT_JAVA_CODEC = MinecraftCodec.CODEC; private static final PacketCodec DEFAULT_JAVA_CODEC = MinecraftCodec.CODEC;
static { static {
SUPPORTED_BEDROCK_CODECS.add(CodecProcessor.processCodec(Bedrock_v622.CODEC.toBuilder() SUPPORTED_BEDROCK_CODECS.add(CodecProcessor.processCodec(Bedrock_v671.CODEC.toBuilder()
.minecraftVersion("1.20.40/1.20.41") .minecraftVersion("1.20.80/1.20.81")
.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")
.build())); .build()));
SUPPORTED_BEDROCK_CODECS.add(CodecProcessor.processCodec(DEFAULT_BEDROCK_CODEC.toBuilder() SUPPORTED_BEDROCK_CODECS.add(CodecProcessor.processCodec(DEFAULT_BEDROCK_CODEC.toBuilder()
.minecraftVersion("1.20.80/1.20.81") .minecraftVersion("1.21.0")
.build())); .build()));
} }
@ -99,16 +87,8 @@ public final class GameProtocol {
/* Bedrock convenience methods to gatekeep features and easily remove the check on version removal */ /* Bedrock convenience methods to gatekeep features and easily remove the check on version removal */
public static boolean isPre1_20_50(GeyserSession session) { public static boolean isPre1_21_0(GeyserSession session) {
return session.getUpstream().getProtocolVersion() < Bedrock_v630.CODEC.getProtocolVersion(); return session.getUpstream().getProtocolVersion() < Bedrock_v685.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();
} }
/** /**

Datei anzeigen

@ -47,7 +47,9 @@ public class InvalidPacketHandler extends ChannelInboundHandlerAdapter {
if (!(rootCause instanceof IllegalArgumentException)) { 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; return;
} }

Datei anzeigen

@ -132,6 +132,8 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
} }
session.getUpstream().getSession().setCodec(packetCodec); session.getUpstream().getSession().setCodec(packetCodec);
// FIXME temporary until 1.20.80 is dropped
session.getPlayerEntity().resetAir();
return true; return true;
} }

Datei anzeigen

@ -35,10 +35,7 @@ import org.cloudburstmc.nbt.util.VarInts;
import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.network.GameProtocol;
import java.io.ByteArrayOutputStream; import java.io.*;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.*; import java.net.*;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -139,6 +136,9 @@ public class GeyserLegacyPingPassthrough implements IGeyserPingPassthrough, Runn
this.geyser.getLogger().debug("Connection timeout for ping passthrough."); this.geyser.getLogger().debug("Connection timeout for ping passthrough.");
} catch (JsonParseException | JsonMappingException ex) { } catch (JsonParseException | JsonMappingException ex) {
this.geyser.getLogger().error("Failed to parse json when pinging server!", 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) { } catch (UnknownHostException ex) {
// Don't reset pingInfo, as we want to keep the last known value // 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?"); this.geyser.getLogger().warning("Unable to resolve remote host! Is the remote server down or invalid?");

Datei anzeigen

@ -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.CustomBlockData;
import org.geysermc.geyser.api.block.custom.CustomBlockState; import org.geysermc.geyser.api.block.custom.CustomBlockState;
import org.geysermc.geyser.api.block.custom.nonvanilla.JavaBlockState; 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.CollisionRegistryLoader;
import org.geysermc.geyser.registry.loader.RegistryLoaders; import org.geysermc.geyser.registry.loader.RegistryLoaders;
import org.geysermc.geyser.registry.populator.BlockRegistryPopulator; import org.geysermc.geyser.registry.populator.BlockRegistryPopulator;
import org.geysermc.geyser.registry.populator.CustomBlockRegistryPopulator; import org.geysermc.geyser.registry.populator.CustomBlockRegistryPopulator;
import org.geysermc.geyser.registry.populator.CustomSkullRegistryPopulator; 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.BlockMappings;
import org.geysermc.geyser.registry.type.CustomSkull; import org.geysermc.geyser.registry.type.CustomSkull;
import org.geysermc.geyser.translator.collision.BlockCollision; import org.geysermc.geyser.translator.collision.BlockCollision;
import java.util.ArrayList;
import java.util.BitSet; import java.util.BitSet;
import java.util.Set; import java.util.Set;
/** /**
@ -59,34 +61,32 @@ public class BlockRegistries {
public static final VersionedRegistry<BlockMappings> BLOCKS = VersionedRegistry.create(RegistryLoaders.empty(Int2ObjectOpenHashMap::new)); 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)); public static final ListRegistry<BlockState> BLOCK_STATES = ListRegistry.create(RegistryLoaders.empty(ArrayList::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());
/** /**
* A mapped registry containing which holds block IDs to its {@link BlockCollision}. * 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. * 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)); 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. * 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)); 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)); public static final SimpleMappedRegistry<String, CustomSkull> CUSTOM_SKULLS = SimpleMappedRegistry.create(RegistryLoaders.empty(Object2ObjectOpenHashMap::new));
static { static {
Blocks.VAULT.javaId(); // FIXME
CustomSkullRegistryPopulator.populate(); CustomSkullRegistryPopulator.populate();
BlockRegistryPopulator.populate(BlockRegistryPopulator.Stage.PRE_INIT); BlockRegistryPopulator.populate(BlockRegistryPopulator.Stage.PRE_INIT);
CustomBlockRegistryPopulator.populate(CustomBlockRegistryPopulator.Stage.DEFINITION); CustomBlockRegistryPopulator.populate(CustomBlockRegistryPopulator.Stage.DEFINITION);
CustomBlockRegistryPopulator.populate(CustomBlockRegistryPopulator.Stage.NON_VANILLA_REGISTRATION); CustomBlockRegistryPopulator.populate(CustomBlockRegistryPopulator.Stage.NON_VANILLA_REGISTRATION);
BlockRegistryPopulator.populate(BlockRegistryPopulator.Stage.INIT_JAVA); 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.VANILLA_REGISTRATION);
CustomBlockRegistryPopulator.populate(CustomBlockRegistryPopulator.Stage.CUSTOM_REGISTRATION); CustomBlockRegistryPopulator.populate(CustomBlockRegistryPopulator.Stage.CUSTOM_REGISTRATION);
BlockRegistryPopulator.populate(BlockRegistryPopulator.Stage.INIT_BEDROCK); BlockRegistryPopulator.populate(BlockRegistryPopulator.Stage.INIT_BEDROCK);

Datei anzeigen

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

Datei anzeigen

@ -127,7 +127,10 @@ public final class Registries {
*/ */
public static final PacketTranslatorRegistry<Packet> JAVA_PACKET_TRANSLATORS = PacketTranslatorRegistry.create(); 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)); public static final SimpleMappedRegistry<String, Item> JAVA_ITEM_IDENTIFIERS = SimpleMappedRegistry.create(RegistryLoaders.empty(Object2ObjectOpenHashMap::new));

Datei anzeigen

@ -25,18 +25,19 @@
package org.geysermc.geyser.registry.loader; package org.geysermc.geyser.registry.loader;
import com.fasterxml.jackson.databind.node.ArrayNode;
import it.unimi.dsi.fastutil.Pair; 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.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectArrayList; import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import org.checkerframework.checker.nullness.qual.Nullable; 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.GeyserImpl;
import org.geysermc.geyser.level.block.type.BlockState;
import org.geysermc.geyser.level.physics.BoundingBox; import org.geysermc.geyser.level.physics.BoundingBox;
import org.geysermc.geyser.registry.BlockRegistries; 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.BlockCollision;
import org.geysermc.geyser.translator.collision.CollisionRemapper; import org.geysermc.geyser.translator.collision.CollisionRemapper;
import org.geysermc.geyser.translator.collision.OtherCollision; import org.geysermc.geyser.translator.collision.OtherCollision;
@ -51,41 +52,43 @@ import java.util.regex.Pattern;
/** /**
* Loads collision data from the given resource path. * 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 @Override
public Int2ObjectMap<BlockCollision> load(Pair<String, String> input) { public List<BlockCollision> load(Pair<String, String> input) {
Int2ObjectMap<BlockCollision> collisions = new Int2ObjectOpenHashMap<>();
Map<Class<?>, CollisionInfo> annotationMap = new IdentityHashMap<>(); Map<Class<?>, CollisionInfo> annotationMap = new IdentityHashMap<>();
for (Class<?> clazz : FileUtils.getGeneratedClassesForAnnotation(CollisionRemapper.class.getName())) { for (Class<?> clazz : FileUtils.getGeneratedClassesForAnnotation(CollisionRemapper.class.getName())) {
GeyserImpl.getInstance().getLogger().debug("Found annotated collision translator: " + clazz.getCanonicalName()); GeyserImpl.getInstance().getLogger().debug("Found annotated collision translator: " + clazz.getCanonicalName());
CollisionRemapper collisionRemapper = clazz.getAnnotation(CollisionRemapper.class); 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 // Load collision mappings file
int[] indices;
List<BoundingBox[]> collisionList; List<BoundingBox[]> collisionList;
try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow(input.value())) { try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow(input.value())) {
ArrayNode collisionNode = (ArrayNode) GeyserImpl.JSON_MAPPER.readTree(stream); NbtMap collisionData = (NbtMap) NbtUtils.createGZIPReader(stream).readTag();
collisionList = loadBoundingBoxes(collisionNode); indices = collisionData.getIntArray("indices");
//SuppressWarnings unchecked
collisionList = loadBoundingBoxes(collisionData.getList("collisions", NbtType.LIST));
} catch (Exception e) { } catch (Exception e) {
throw new AssertionError("Unable to load collision data", 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 of unique collisions to its instance
Map<BlockCollision, BlockCollision> collisionInstances = new Object2ObjectOpenHashMap<>(); Map<BlockCollision, BlockCollision> collisionInstances = new Object2ObjectOpenHashMap<>();
for (int i = 0; i < blockMappings.length; i++) { for (int i = 0; i < blockStates.size(); i++) {
BlockMapping blockMapping = blockMappings[i]; BlockState state = blockStates.get(i);
if (blockMapping == null) { if (state == null) {
GeyserImpl.getInstance().getLogger().warning("Missing block mapping for Java block " + i); GeyserImpl.getInstance().getLogger().warning("Missing block state for Java block " + i);
continue; continue;
} }
BlockCollision newCollision = instantiateCollision(blockMapping, annotationMap, collisionList); BlockCollision newCollision = instantiateCollision(state, annotationMap, indices[i], collisionList);
if (newCollision != null) { if (newCollision != null) {
// If there's an existing instance equal to this one, use that instead // 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; return collisions;
} }
private @Nullable BlockCollision instantiateCollision(BlockMapping mapping, Map<Class<?>, CollisionInfo> annotationMap, List<BoundingBox[]> collisionList) { private @Nullable BlockCollision instantiateCollision(BlockState state, Map<Class<?>, CollisionInfo> annotationMap, int collisionIndex, List<BoundingBox[]> collisionList) {
String[] blockIdParts = mapping.getJavaIdentifier().split("\\["); String blockName = state.block().javaIdentifier().value();
String blockName = blockIdParts[0].replace("minecraft:", "");
String params = "";
if (blockIdParts.length == 2) {
params = "[" + blockIdParts[1];
}
int collisionIndex = mapping.getCollisionIndex();
for (Map.Entry<Class<?>, CollisionInfo> collisionRemappers : annotationMap.entrySet()) { for (Map.Entry<Class<?>, CollisionInfo> collisionRemappers : annotationMap.entrySet()) {
Class<?> type = collisionRemappers.getKey(); Class<?> type = collisionRemappers.getKey();
CollisionInfo collisionInfo = collisionRemappers.getValue(); CollisionInfo collisionInfo = collisionRemappers.getValue();
CollisionRemapper annotation = collisionInfo.collisionRemapper; CollisionRemapper annotation = collisionInfo.collisionRemapper;
if (collisionInfo.pattern.matcher(blockName).find() && collisionInfo.paramsPattern.matcher(params).find()) { if (collisionInfo.pattern.matcher(blockName).find()) {
try { try {
if (annotation.passDefaultBoxes()) { if (annotation.passDefaultBoxes()) {
// Create an OtherCollision instance and get the bounding boxes // Create an OtherCollision instance and get the bounding boxes
BoundingBox[] defaultBoxes = collisionList.get(collisionIndex); 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 { } else {
return (BlockCollision) type.getDeclaredConstructor(String.class).newInstance(params); return (BlockCollision) type.getDeclaredConstructor(BlockState.class).newInstance(state);
} }
} catch (IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) { } catch (IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) {
throw new RuntimeException(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 // Unless some of the low IDs are changed, which is unlikely, the second item should always be full collision
if (collisionIndex == 1) { if (collisionIndex == 1) {
return new SolidCollision(params); return new SolidCollision(state);
} }
return new OtherCollision(collisionList.get(collisionIndex)); return new OtherCollision(collisionList.get(collisionIndex));
} }
private List<BoundingBox[]> loadBoundingBoxes(ArrayNode collisionNode) { private List<BoundingBox[]> loadBoundingBoxes(List<NbtList> collisionNode) {
List<BoundingBox[]> collisions = new ObjectArrayList<>(); List<BoundingBox[]> collisions = new ObjectArrayList<>();
for (int collisionIndex = 0; collisionIndex < collisionNode.size(); collisionIndex++) { 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()]; BoundingBox[] boundingBoxes = new BoundingBox[boundingBoxArray.size()];
for (int i = 0; i < boundingBoxArray.size(); i++) { for (int i = 0; i < boundingBoxArray.size(); i++) {
ArrayNode boxProperties = (ArrayNode) boundingBoxArray.get(i); NbtList<Double> boxProperties = boundingBoxArray.get(i);
boundingBoxes[i] = new BoundingBox(boxProperties.get(0).asDouble(), boundingBoxes[i] = new BoundingBox(boxProperties.get(0),
boxProperties.get(1).asDouble(), boxProperties.get(1),
boxProperties.get(2).asDouble(), boxProperties.get(2),
boxProperties.get(3).asDouble(), boxProperties.get(3),
boxProperties.get(4).asDouble(), boxProperties.get(4),
boxProperties.get(5).asDouble()); boxProperties.get(5));
} }
// Sorting by lowest Y first fixes some bugs // Sorting by lowest Y first fixes some bugs
@ -173,6 +171,5 @@ public class CollisionRegistryLoader extends MultiResourceRegistryLoader<String,
public static class CollisionInfo { public static class CollisionInfo {
private final CollisionRemapper collisionRemapper; private final CollisionRemapper collisionRemapper;
private final Pattern pattern; private final Pattern pattern;
private final Pattern paramsPattern;
} }
} }

Datei anzeigen

@ -32,36 +32,37 @@ import com.google.common.collect.Interner;
import com.google.common.collect.Interners; import com.google.common.collect.Interners;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; 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 it.unimi.dsi.fastutil.objects.*;
import org.cloudburstmc.blockstateupdater.BlockStateUpdater; import org.cloudburstmc.nbt.*;
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.protocol.bedrock.codec.v671.Bedrock_v671; 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.BlockPropertyData;
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition; import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.block.custom.CustomBlockData; import org.geysermc.geyser.api.block.custom.CustomBlockData;
import org.geysermc.geyser.api.block.custom.CustomBlockState; import org.geysermc.geyser.api.block.custom.CustomBlockState;
import org.geysermc.geyser.api.block.custom.nonvanilla.JavaBlockState; 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.level.physics.PistonBehavior;
import org.geysermc.geyser.registry.BlockRegistries; 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.BlockMappings;
import org.geysermc.geyser.registry.type.GeyserBedrockBlock; import org.geysermc.geyser.registry.type.GeyserBedrockBlock;
import org.geysermc.geyser.util.BlockUtils; import org.geysermc.geyser.util.BlockUtils;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.InputStream; import java.io.InputStream;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.*; import java.util.*;
import java.util.stream.Stream;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
/** /**
@ -82,24 +83,11 @@ public final class BlockRegistryPopulator {
interface Remapper { interface Remapper {
NbtMap remap(NbtMap tag); 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) { public static void populate(Stage stage) {
switch (stage) { switch (stage) {
case PRE_INIT, POST_INIT -> nullifyBlocksNode(); case PRE_INIT, POST_INIT -> nullifyBlocksNbt();
case INIT_JAVA -> registerJavaBlocks(); case INIT_JAVA -> registerJavaBlocks();
case INIT_BEDROCK -> registerBedrockBlocks(); case INIT_BEDROCK -> registerBedrockBlocks();
default -> throw new IllegalArgumentException("Unknown stage: " + stage); 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 MIN_CUSTOM_RUNTIME_ID = -1;
private static int JAVA_BLOCKS_SIZE = -1; private static int JAVA_BLOCKS_SIZE = -1;
private static void nullifyBlocksNode() { private static void nullifyBlocksNbt() {
BLOCKS_JSON = null; BLOCKS_NBT = null;
} }
private static void registerBedrockBlocks() { private static void registerBedrockBlocks() {
var blockMappers = ImmutableMap.<ObjectIntPair<String>, Remapper>builder() 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_80", Bedrock_v671.CODEC.getProtocolVersion()), Conversion685_671::remapBlock)
.put(ObjectIntPair.of("1_20_50", Bedrock_v630.CODEC.getProtocolVersion()), Conversion649_630::remapBlock) .put(ObjectIntPair.of("1_21_0", Bedrock_v685.CODEC.getProtocolVersion()), tag -> tag)
// 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)
.build(); .build();
// We can keep this strong as nothing should be garbage collected // We can keep this strong as nothing should be garbage collected
@ -215,19 +199,34 @@ public final class BlockRegistryPopulator {
int javaRuntimeId = -1; int javaRuntimeId = -1;
List<BlockState> javaBlockStates = BlockRegistries.BLOCK_STATES.get();
GeyserBedrockBlock airDefinition = null; GeyserBedrockBlock airDefinition = null;
BlockDefinition commandBlockDefinition = null; BlockDefinition commandBlockDefinition = null;
BlockDefinition mobSpawnerBlockDefinition = null; BlockDefinition mobSpawnerBlockDefinition = null;
BlockDefinition waterDefinition = null; BlockDefinition waterDefinition = null;
BlockDefinition movingBlockDefinition = null; BlockDefinition movingBlockDefinition = null;
Iterator<Map.Entry<String, JsonNode>> blocksIterator = BLOCKS_JSON.fields(); Iterator<NbtMap> blocksIterator = BLOCKS_NBT.iterator();
Remapper stateMapper = blockMappers.get(palette); Remapper stateMapper = blockMappers.get(palette);
GeyserBedrockBlock[] javaToBedrockBlocks = new GeyserBedrockBlock[JAVA_BLOCKS_SIZE]; GeyserBedrockBlock[] javaToBedrockBlocks = new GeyserBedrockBlock[JAVA_BLOCKS_SIZE];
GeyserBedrockBlock[] javaToVanillaBedrockBlocks = 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<>(); Map<NbtMap, BlockDefinition> itemFrames = new Object2ObjectOpenHashMap<>();
Set<BlockDefinition> jigsawDefinitions = new ObjectOpenHashSet<>(); Set<BlockDefinition> jigsawDefinitions = new ObjectOpenHashSet<>();
@ -236,10 +235,11 @@ public final class BlockRegistryPopulator {
BlockMappings.BlockMappingsBuilder builder = BlockMappings.builder(); BlockMappings.BlockMappingsBuilder builder = BlockMappings.builder();
while (blocksIterator.hasNext()) { while (blocksIterator.hasNext()) {
javaRuntimeId++; javaRuntimeId++;
Map.Entry<String, JsonNode> entry = blocksIterator.next(); NbtMap entry = blocksIterator.next();
String javaId = entry.getKey(); BlockState blockState = javaBlockStates.get(javaRuntimeId);
String javaId = blockState.toString();
NbtMap originalBedrockTag = buildBedrockState(entry.getValue()); NbtMap originalBedrockTag = buildBedrockState(blockState, entry);
NbtMap bedrockTag = stateMapper.remap(originalBedrockTag); NbtMap bedrockTag = stateMapper.remap(originalBedrockTag);
GeyserBedrockBlock vanillaBedrockDefinition = blockStateOrderedMap.get(bedrockTag); GeyserBedrockBlock vanillaBedrockDefinition = blockStateOrderedMap.get(bedrockTag);
@ -271,35 +271,36 @@ public final class BlockRegistryPopulator {
case "minecraft:moving_piston[facing=north,type=normal]" -> movingBlockDefinition = bedrockDefinition; case "minecraft:moving_piston[facing=north,type=normal]" -> movingBlockDefinition = bedrockDefinition;
} }
if (javaId.contains("jigsaw")) { 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());
}
}
if (block == Blocks.JIGSAW) {
jigsawDefinitions.add(bedrockDefinition); jigsawDefinitions.add(bedrockDefinition);
} }
if (javaId.contains("structure_block")) { if (block == Blocks.STRUCTURE_BLOCK) {
int modeIndex = javaId.indexOf("mode="); String mode = blockState.getValue(Properties.STRUCTUREBLOCK_MODE);
if (modeIndex != -1) { structureBlockDefinitions.put(mode.toUpperCase(Locale.ROOT), bedrockDefinition);
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);
}
}
} }
boolean waterlogged = entry.getKey().contains("waterlogged=true") boolean waterlogged = blockState.getValue(Properties.WATERLOGGED, false)
|| javaId.contains("minecraft:bubble_column") || javaId.contains("minecraft:kelp") || javaId.contains("seagrass"); || block == Blocks.BUBBLE_COLUMN || block == Blocks.KELP || block == Blocks.KELP_PLANT
|| block == Blocks.SEAGRASS || block == Blocks.TALL_SEAGRASS;
if (waterlogged) { if (waterlogged) {
int finalJavaRuntimeId = javaRuntimeId; BlockRegistries.WATERLOGGED.get().set(javaRuntimeId);
BlockRegistries.WATERLOGGED.register(set -> set.set(finalJavaRuntimeId));
} }
String cleanJavaIdentifier = BlockUtils.getCleanIdentifier(entry.getKey());
// Get the tag needed for non-empty flower pots // Get the tag needed for non-empty flower pots
if (entry.getValue().get("pottable") != null) { if (javaPottable.contains(block)) {
flowerPotBlocks.put(cleanJavaIdentifier.intern(), blockStates.get(bedrockDefinition.getRuntimeId())); // Specifically NOT putIfAbsent - mangrove propagule breaks otherwise
flowerPotBlocks.put(block, blockStates.get(bedrockDefinition.getRuntimeId()));
} }
javaToVanillaBedrockBlocks[javaRuntimeId] = vanillaBedrockDefinition; javaToVanillaBedrockBlocks[javaRuntimeId] = vanillaBedrockDefinition;
@ -355,9 +356,12 @@ public final class BlockRegistryPopulator {
javaToVanillaBedrockBlocks[stateRuntimeId] = bedrockDefinition; // TODO: Check this? javaToVanillaBedrockBlocks[stateRuntimeId] = bedrockDefinition; // TODO: Check this?
javaToBedrockBlocks[stateRuntimeId] = bedrockDefinition; javaToBedrockBlocks[stateRuntimeId] = bedrockDefinition;
javaToBedrockIdentifiers.put(entry.getKey().stateGroupId(), entry.getValue().block().identifier());
} }
} }
javaToBedrockIdentifiers.trim();
// Loop around again to find all item frame runtime IDs // Loop around again to find all item frame runtime IDs
Object2ObjectMaps.fastForEach(blockStateOrderedMap, entry -> { Object2ObjectMaps.fastForEach(blockStateOrderedMap, entry -> {
String name = entry.getKey().getString("name"); String name = entry.getKey().getString("name");
@ -369,6 +373,7 @@ public final class BlockRegistryPopulator {
BlockRegistries.BLOCKS.register(palette.valueInt(), builder.bedrockRuntimeMap(bedrockRuntimeMap) BlockRegistries.BLOCKS.register(palette.valueInt(), builder.bedrockRuntimeMap(bedrockRuntimeMap)
.javaToBedrockBlocks(javaToBedrockBlocks) .javaToBedrockBlocks(javaToBedrockBlocks)
.javaToVanillaBedrockBlocks(javaToVanillaBedrockBlocks) .javaToVanillaBedrockBlocks(javaToVanillaBedrockBlocks)
.javaToBedrockIdentifiers(javaToBedrockIdentifiers)
.stateDefinitionMap(blockStateOrderedMap) .stateDefinitionMap(blockStateOrderedMap)
.itemFrames(itemFrames) .itemFrames(itemFrames)
.flowerPotBlocks(flowerPotBlocks) .flowerPotBlocks(flowerPotBlocks)
@ -383,208 +388,81 @@ public final class BlockRegistryPopulator {
} }
private static void registerJavaBlocks() { private static void registerJavaBlocks() {
JsonNode blocksJson; List<NbtMap> blocksNbt;
try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow("mappings/blocks.json")) { try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow("mappings/blocks.nbt")) {
blocksJson = GeyserImpl.JSON_MAPPER.readTree(stream); blocksNbt = ((NbtMap) NbtUtils.createGZIPReader(stream).readTag())
.getList("bedrock_mappings", NbtType.COMPOUND);
} catch (Exception e) { } catch (Exception e) {
throw new AssertionError("Unable to load Java block mappings", 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()) { 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(); 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(); 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 + ")"); 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 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 javaRuntimeId = -1;
int cobwebBlockId = -1; for (BlockState javaBlockState : BlockRegistries.BLOCK_STATES.get()) {
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()) {
javaRuntimeId++; javaRuntimeId++;
Map.Entry<String, JsonNode> entry = blocksIterator.next(); String javaId = javaBlockState.toString().intern();
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);
BlockRegistries.JAVA_IDENTIFIER_TO_ID.register(javaId, javaRuntimeId); 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()) { 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()) { for (JavaBlockState javaBlockState : BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().keySet()) {
if (!usedNonVanillaRuntimeIDs.add(javaBlockState.javaId())) { if (!usedNonVanillaRuntimeIDs.add(javaBlockState.javaId())) {
throw new RuntimeException("Duplicate runtime ID " + javaBlockState.javaId() + " for non vanilla Java block state " + javaBlockState.identifier()); 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(); String javaId = javaBlockState.identifier();
int stateRuntimeId = javaBlockState.javaId(); int stateRuntimeId = javaBlockState.javaId();
String pistonBehavior = javaBlockState.pistonBehavior(); 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();
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 cleanJavaIdentifier = BlockUtils.getCleanIdentifier(javaBlockState.identifier());
String bedrockIdentifier = customBlockState.block().identifier(); String pickItem = javaBlockState.pickItem();
Block block = new Block(cleanJavaIdentifier, builder) {
if (!cleanJavaIdentifier.equals(cleanIdentifiers.peekLast())) { @Override
uniqueJavaId++; public ItemStack pickItem(BlockState state) {
cleanIdentifiers.add(cleanJavaIdentifier.intern()); 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_IDENTIFIER_TO_ID.register(javaId, stateRuntimeId);
BlockRegistries.JAVA_BLOCKS.register(stateRuntimeId, blockMapping); BlockRegistries.BLOCK_STATES.register(stateRuntimeId, new BlockState(block, stateRuntimeId));
// 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.CLEAN_JAVA_IDENTIFIERS.set(cleanIdentifiers.toArray(new String[0])); BLOCKS_NBT = blocksNbt;
BLOCKS_JSON = blocksJson;
JsonNode blockInteractionsJson; JsonNode blockInteractionsJson;
try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow("mappings/interactions.json")) { 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.set(toBlockStateSet((ArrayNode) blockInteractionsJson.get("always_consumes")));
BlockRegistries.INTERACTIVE_MAY_BUILD.set(toBlockStateSet((ArrayNode) blockInteractionsJson.get("requires_may_build"))); BlockRegistries.INTERACTIVE_MAY_BUILD.set(toBlockStateSet((ArrayNode) blockInteractionsJson.get("requires_may_build")));
BlockRegistries.BLOCK_STATES.freeze();
} }
private static BitSet toBlockStateSet(ArrayNode node) { private static BitSet toBlockStateSet(ArrayNode node) {
@ -605,29 +485,11 @@ public final class BlockRegistryPopulator {
return blockStateSet; return blockStateSet;
} }
private static NbtMap buildBedrockState(JsonNode node) { private static NbtMap buildBedrockState(BlockState state, NbtMap nbt) {
NbtMapBuilder tagBuilder = NbtMap.builder(); 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); tagBuilder.putString("name", bedrockIdentifier);
tagBuilder.put("states", nbt.getCompound("state"));
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());
return tagBuilder.build(); return tagBuilder.build();
} }

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -53,7 +53,6 @@ import org.geysermc.geyser.level.block.GeyserCustomBlockData;
import org.geysermc.geyser.level.block.GeyserCustomBlockState; import org.geysermc.geyser.level.block.GeyserCustomBlockState;
import org.geysermc.geyser.level.block.GeyserGeometryComponent; import org.geysermc.geyser.level.block.GeyserGeometryComponent;
import org.geysermc.geyser.level.block.GeyserMaterialInstance; import org.geysermc.geyser.level.block.GeyserMaterialInstance;
import org.geysermc.geyser.network.GameProtocol;
import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.BlockRegistries;
import org.geysermc.geyser.registry.mappings.MappingsConfigReader; import org.geysermc.geyser.registry.mappings.MappingsConfigReader;
import org.geysermc.geyser.registry.type.CustomSkull; 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 // meaning of this version is unknown, but it's required for tags to work and should probably be checked periodically
.putInt("molangVersion", 1) .putInt("molangVersion", 1)
.putList("permutations", NbtType.COMPOUND, permutations) .putList("permutations", NbtType.COMPOUND, permutations)
.putList("properties", NbtType.COMPOUND, properties); .putList("properties", NbtType.COMPOUND, properties)
.putCompound("vanilla_block_data", NbtMap.builder()
if (GameProtocol.is1_20_60orHigher(protocolVersion)) {
propertyTag.putCompound("vanilla_block_data", NbtMap.builder()
.putInt("block_id", BLOCK_ID.getAndIncrement()) .putInt("block_id", BLOCK_ID.getAndIncrement())
.build()); .build());
}
return new BlockPropertyData(customBlock.identifier(), propertyTag.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