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

Datei anzeigen

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

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

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!
### Currently supporting Minecraft Bedrock 1.20.40 - 1.20.80/81 and Minecraft Java 1.20.5/1.20.6
### Currently supporting Minecraft Bedrock 1.20.80 - 1.21.0 and Minecraft Java 1.20.5/1.20.6
## Setting Up
Take a look [here](https://wiki.geysermc.org/geyser/setup/) for how to set up Geyser.

Datei anzeigen

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

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

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

Datei anzeigen

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

Datei anzeigen

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

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)
api(libs.bundles.protocol)
implementation(libs.blockstateupdater)
api(libs.mcauthlib)
api(libs.mcprotocollib) {

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

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

Datei anzeigen

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

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

Datei anzeigen

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

Datei anzeigen

@ -32,24 +32,50 @@ import org.cloudburstmc.math.vector.Vector2f;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.CameraShakeAction;
import org.cloudburstmc.protocol.bedrock.data.CameraShakeType;
import org.cloudburstmc.protocol.bedrock.data.HudElement;
import org.cloudburstmc.protocol.bedrock.data.HudVisibility;
import org.cloudburstmc.protocol.bedrock.data.camera.CameraEase;
import org.cloudburstmc.protocol.bedrock.data.camera.CameraFadeInstruction;
import org.cloudburstmc.protocol.bedrock.data.camera.CameraSetInstruction;
import org.cloudburstmc.protocol.bedrock.packet.CameraInstructionPacket;
import org.cloudburstmc.protocol.bedrock.packet.CameraShakePacket;
import org.cloudburstmc.protocol.bedrock.packet.PlayerFogPacket;
import org.geysermc.geyser.api.bedrock.camera.*;
import org.cloudburstmc.protocol.bedrock.packet.SetHudPacket;
import org.geysermc.geyser.api.bedrock.camera.CameraData;
import org.geysermc.geyser.api.bedrock.camera.CameraEaseType;
import org.geysermc.geyser.api.bedrock.camera.CameraFade;
import org.geysermc.geyser.api.bedrock.camera.CameraPerspective;
import org.geysermc.geyser.api.bedrock.camera.CameraPosition;
import org.geysermc.geyser.api.bedrock.camera.CameraShake;
import org.geysermc.geyser.api.bedrock.camera.GuiElement;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
import java.util.*;
import java.util.Collections;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
public class GeyserCameraData implements CameraData {
private static final HudElement[] HUD_ELEMENT_VALUES = HudElement.values();
private static final Set<HudElement> ALL_HUD_ELEMENTS = Set.of(HUD_ELEMENT_VALUES);
/**
* An array of elements to hide when the player is in spectator mode.
* Helps with tidying up the GUI; Java-style.
*/
private static final GuiElement[] SPECTATOR_HIDDEN_ELEMENTS = {
GuiElement.AIR_BUBBLES_BAR,
GuiElement.ARMOR,
GuiElement.HEALTH,
GuiElement.FOOD_BAR,
GuiElement.PROGRESS_BAR,
GuiElement.TOOL_TIPS
};
private final GeyserSession session;
@Getter
private CameraPerspective cameraPerspective;
/**
* All fog effects that are currently applied to the client.
*/
@ -57,6 +83,14 @@ public class GeyserCameraData implements CameraData {
private final Set<UUID> cameraLockOwners = new HashSet<>();
/**
* All currently hidden HUD elements
*/
private final Set<GuiElement> hiddenHudElements = new HashSet<>();
@Getter
private CameraPerspective cameraPerspective;
public GeyserCameraData(GeyserSession session) {
this.session = session;
}
@ -223,4 +257,67 @@ public class GeyserCameraData implements CameraData {
public boolean isCameraLocked() {
return !this.cameraLockOwners.isEmpty();
}
@Override
public void hideElement(GuiElement... elements) {
Objects.requireNonNull(elements);
SetHudPacket packet = new SetHudPacket();
packet.setVisibility(HudVisibility.HIDE);
Set<HudElement> elementSet = packet.getElements();
for (GuiElement element : elements) {
this.hiddenHudElements.add(element);
elementSet.add(HUD_ELEMENT_VALUES[element.id()]);
}
session.sendUpstreamPacket(packet);
}
@Override
public void resetElement(GuiElement... elements) {
SetHudPacket packet = new SetHudPacket();
packet.setVisibility(HudVisibility.RESET);
Set<HudElement> elementSet = packet.getElements();
if (elements != null && elements.length != 0) {
for (GuiElement element : elements) {
this.hiddenHudElements.remove(element);
elementSet.add(HUD_ELEMENT_VALUES[element.id()]);
}
} else {
this.hiddenHudElements.clear();
elementSet.addAll(ALL_HUD_ELEMENTS);
}
session.sendUpstreamPacket(packet);
}
@Override
public boolean isHudElementHidden(@NonNull GuiElement element) {
Objects.requireNonNull(element);
return this.hiddenHudElements.contains(element);
}
@Override
public @NonNull Set<GuiElement> hiddenElements() {
return Collections.unmodifiableSet(hiddenHudElements);
}
/**
* Deals with hiding hud elements while in spectator.
*
* @param currentlySpectator whether the player is currently in spectator mode
* @param newGameMode the new GameMode to switch to
*/
public void handleGameModeChange(boolean currentlySpectator, GameMode newGameMode) {
if (newGameMode == GameMode.SPECTATOR) {
if (!currentlySpectator) {
hideElement(SPECTATOR_HIDDEN_ELEMENTS);
}
} else {
if (currentlySpectator) {
resetElement(SPECTATOR_HIDDEN_ELEMENTS);
}
}
}
}

Datei anzeigen

@ -25,6 +25,7 @@
package org.geysermc.geyser.inventory;
import org.geysermc.geyser.level.block.type.Block;
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
import lombok.Getter;
import org.checkerframework.checker.nullness.qual.NonNull;
@ -83,9 +84,9 @@ public class Container extends Inventory {
* Will be overwritten for droppers.
*
* @param usingRealBlock whether this container is using a real container or not
* @param javaBlockId the Java block string of the block, if real
* @param block the Java block, if real
*/
public void setUsingRealBlock(boolean usingRealBlock, String javaBlockId) {
public void setUsingRealBlock(boolean usingRealBlock, Block block) {
isUsingRealBlock = usingRealBlock;
}
}

Datei anzeigen

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

Datei anzeigen

@ -69,7 +69,7 @@ public class GeyserItemStack {
return of(javaId, amount, null);
}
public static @NonNull GeyserItemStack of(int javaId, int amount, DataComponents components) {
public static @NonNull GeyserItemStack of(int javaId, int amount, @Nullable DataComponents components) {
return new GeyserItemStack(javaId, amount, components);
}

Datei anzeigen

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

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

Datei anzeigen

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

Datei anzeigen

@ -25,11 +25,26 @@
package org.geysermc.geyser.item.type;
/**
* TODO needed?
*/
import org.geysermc.geyser.level.block.type.Block;
public class BlockItem extends Item {
public BlockItem(String javaIdentifier, Builder builder) {
public BlockItem(Builder builder, Block block, Block... otherBlocks) {
super(block.javaIdentifier().value(), builder);
// Ensure this item can be looked up by its block(s)
registerBlock(block, this);
for (Block otherBlock : otherBlocks) {
registerBlock(otherBlock, this);
}
}
// Use this constructor if the item name is not the same as its primary block
public BlockItem(String javaIdentifier, Builder builder, Block block, Block... otherBlocks) {
super(javaIdentifier, builder);
registerBlock(block, this);
for (Block otherBlock : otherBlocks) {
registerBlock(otherBlock, this);
}
}
}

Datei anzeigen

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

Datei anzeigen

@ -35,6 +35,7 @@ import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.inventory.item.Enchantment;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.level.block.type.Block;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.registry.type.ItemMappings;
import org.geysermc.geyser.session.GeyserSession;
@ -49,20 +50,12 @@ import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponen
import org.geysermc.mcprotocollib.protocol.data.game.item.component.ItemEnchantments;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Item {
/**
* This is a map from Java-only enchantments to their translation keys so that we can
* map these enchantments to Bedrock clients, since they don't actually exist there.
*/
private static final Map<Enchantment.JavaEnchantment, String> ENCHANTMENT_TRANSLATION_KEYS = Map.of(
Enchantment.JavaEnchantment.SWEEPING_EDGE, "enchantment.minecraft.sweeping",
Enchantment.JavaEnchantment.DENSITY, "enchantment.minecraft.density",
Enchantment.JavaEnchantment.BREACH, "enchantment.minecraft.breach",
Enchantment.JavaEnchantment.WIND_BURST, "enchantment.minecraft.wind_burst");
private static final Map<Block, Item> BLOCK_TO_ITEM = new HashMap<>();
private final String javaIdentifier;
private int javaId = -1;
private final int stackSize;
@ -233,6 +226,16 @@ public class Item {
// }
}
/**
* This is a map from Java-only enchantments to their translation keys so that we can
* map these enchantments to Bedrock clients, since they don't actually exist there.
*/
private static final Map<Enchantment.JavaEnchantment, String> ENCHANTMENT_TRANSLATION_KEYS = Map.of(
Enchantment.JavaEnchantment.SWEEPING_EDGE, "enchantment.minecraft.sweeping",
Enchantment.JavaEnchantment.DENSITY, "enchantment.minecraft.density",
Enchantment.JavaEnchantment.BREACH, "enchantment.minecraft.breach",
Enchantment.JavaEnchantment.WIND_BURST, "enchantment.minecraft.wind_burst");
protected final @Nullable NbtMap remapEnchantment(GeyserSession session, int enchantId, int level, BedrockItemBuilder builder) {
// TODO verify
// TODO streamline Enchantment process
@ -281,6 +284,18 @@ public class Item {
'}';
}
/**
* @return the block associated with this item, or air if nothing
*/
@NonNull
public static Item byBlock(Block block) {
return BLOCK_TO_ITEM.getOrDefault(block, Items.AIR);
}
protected static void registerBlock(Block block, Item item) {
BLOCK_TO_ITEM.put(block, item);
}
public static Builder builder() {
return new Builder();
}

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -25,24 +25,20 @@
package org.geysermc.geyser.level;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.geysermc.erosion.packet.backendbound.*;
import org.geysermc.erosion.packet.backendbound.BackendboundBatchBlockRequestPacket;
import org.geysermc.erosion.packet.backendbound.BackendboundBlockRequestPacket;
import org.geysermc.erosion.packet.backendbound.BackendboundPickBlockPacket;
import org.geysermc.erosion.util.BlockPositionIterator;
import org.geysermc.erosion.util.LecternUtils;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.BlockEntityUtils;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityInfo;
import java.util.List;
import java.util.concurrent.CompletableFuture;
public class GeyserWorldManager extends WorldManager {
@ -91,51 +87,6 @@ public class GeyserWorldManager extends WorldManager {
return false;
}
@Override
public void sendLecternData(GeyserSession session, int x, int z, List<BlockEntityInfo> blockEntityInfos) {
var erosionHandler = session.getErosionHandler().getAsActive();
if (erosionHandler == null) {
// No-op - don't send any additional information other than what the chunk has already sent
return;
}
List<Vector3i> vectors = new ObjectArrayList<>(blockEntityInfos.size());
//noinspection ForLoopReplaceableByForEach - avoid constructing iterator
for (int i = 0; i < blockEntityInfos.size(); i++) {
BlockEntityInfo info = blockEntityInfos.get(i);
vectors.add(Vector3i.from(info.getX(), info.getY(), info.getZ()));
}
erosionHandler.sendPacket(new BackendboundBatchBlockEntityPacket(x, z, vectors));
}
@Override
public void sendLecternData(GeyserSession session, int x, int y, int z) {
var erosionHandler = session.getErosionHandler().getAsActive();
if (erosionHandler != null) {
erosionHandler.sendPacket(new BackendboundBlockEntityPacket(Vector3i.from(x, y, z)));
return;
}
// Without direct server access, we can't get lectern information on-the-fly.
// I should have set this up so it's only called when there is a book in the block state. - Camotoy
NbtMapBuilder lecternTag = LecternUtils.getBaseLecternTag(x, y, z, 1);
lecternTag.putCompound("book", NbtMap.builder()
.putByte("Count", (byte) 1)
.putShort("Damage", (short) 0)
.putString("Name", "minecraft:written_book")
.putCompound("tag", NbtMap.builder()
.putString("photoname", "")
.putString("text", "")
.build())
.build());
lecternTag.putInt("page", -1); // I'm surprisingly glad this exists - it forces Bedrock to stop reading immediately. Usually.
BlockEntityUtils.updateBlockEntity(session, lecternTag.build(), Vector3i.from(x, y, z));
}
@Override
public boolean shouldExpectLecternHandled(GeyserSession session) {
return session.getErosionHandler().isActive();
}
@Override
public void setGameRule(GeyserSession session, String name, Object value) {
super.setGameRule(session, name, value);
@ -179,9 +130,9 @@ public class GeyserWorldManager extends WorldManager {
if (erosionHandler == null) {
return super.getPickItemComponents(session, x, y, z, addNbtData);
}
CompletableFuture<DataComponents> future = new CompletableFuture<>();
CompletableFuture<Int2ObjectMap<byte[]>> future = new CompletableFuture<>();
erosionHandler.setPickBlockLookup(future);
erosionHandler.sendPacket(new BackendboundPickBlockPacket(Vector3i.from(x, y, z)));
return future;
return future.thenApply(RAW_TRANSFORMER);
}
}

Datei anzeigen

@ -25,19 +25,28 @@
package org.geysermc.geyser.level;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.math.vector.Vector3i;
import org.geysermc.erosion.util.BlockPositionIterator;
import org.geysermc.geyser.level.block.type.BlockState;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponent;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityInfo;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.ItemCodecHelper;
import org.geysermc.mcprotocollib.protocol.data.game.setting.Difficulty;
import java.util.List;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
/**
* Class that manages or retrieves various information
@ -48,6 +57,16 @@ import java.util.concurrent.CompletableFuture;
*/
public abstract class WorldManager {
@NonNull
public final BlockState blockAt(GeyserSession session, Vector3i vector) {
return this.blockAt(session, vector.getX(), vector.getY(), vector.getZ());
}
@NonNull
public BlockState blockAt(GeyserSession session, int x, int y, int z) {
return BlockState.of(this.getBlockAt(session, x, y, z));
}
/**
* Gets the Java block state at the specified location
*
@ -97,40 +116,6 @@ public abstract class WorldManager {
*/
public abstract boolean hasOwnChunkCache();
/**
* Sigh. <br>
*
* So, on Java Edition, the lectern is an inventory. Java opens it and gets the contents of the book there.
* On Bedrock, the lectern contents are part of the block entity tag. Therefore, Bedrock expects to have the contents
* of the lectern ready and present in the world. If the contents are not there, it takes at least two clicks for the
* lectern to update the tag and then present itself. <br>
*
* We solve this problem by querying all loaded lecterns, where possible, and sending their information in a block entity
* tag.
* <p>
* Note that the lectern data may be sent asynchronously.
*
* @param session the session of the player
* @param x the x coordinate of the lectern
* @param y the y coordinate of the lectern
* @param z the z coordinate of the lectern
*/
public abstract void sendLecternData(GeyserSession session, int x, int y, int z);
/**
* {@link #sendLecternData(GeyserSession, int, int, int)} but batched for chunks.
*
* @param x chunk x
* @param z chunk z
* @param blockEntityInfos a list of coordinates (chunk local) to grab lecterns from.
*/
public abstract void sendLecternData(GeyserSession session, int x, int z, List<BlockEntityInfo> blockEntityInfos);
/**
* @return whether we should expect lectern data to update, or if we have to fall back on a workaround.
*/
public abstract boolean shouldExpectLecternHandled(GeyserSession session);
/**
* Updates a gamerule value on the Java server
*
@ -223,4 +208,20 @@ public abstract class WorldManager {
public CompletableFuture<@Nullable DataComponents> getPickItemComponents(GeyserSession session, int x, int y, int z, boolean addExtraData) {
return CompletableFuture.completedFuture(null);
}
protected static final Function<Int2ObjectMap<byte[]>, DataComponents> RAW_TRANSFORMER = map -> {
try {
Map<DataComponentType<?>, DataComponent<?, ?>> components = new HashMap<>();
Int2ObjectMaps.fastForEach(map, entry -> {
DataComponentType type = DataComponentType.from(entry.getIntKey());
ByteBuf buf = Unpooled.wrappedBuffer(entry.getValue());
DataComponent value = type.readDataComponent(ItemCodecHelper.INSTANCE, buf);
components.put(type, value);
});
return new DataComponents(components);
} catch (Exception e) {
e.printStackTrace();
return null;
}
};
}

Datei anzeigen

@ -25,363 +25,19 @@
package org.geysermc.geyser.level.block;
import com.fasterxml.jackson.databind.JsonNode;
import it.unimi.dsi.fastutil.ints.*;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import org.geysermc.geyser.level.physics.Direction;
import org.geysermc.geyser.level.block.property.Properties;
import org.geysermc.geyser.level.block.type.Block;
import org.geysermc.geyser.level.block.type.BlockState;
import org.geysermc.geyser.level.block.type.PistonBlock;
import org.geysermc.geyser.level.physics.PistonBehavior;
import org.geysermc.geyser.registry.BlockRegistries;
import org.geysermc.geyser.registry.type.BlockMapping;
import org.geysermc.geyser.translator.level.block.entity.PistonBlockEntityTranslator;
import org.geysermc.geyser.util.collection.FixedInt2ByteMap;
import org.geysermc.geyser.util.collection.FixedInt2IntMap;
import org.geysermc.geyser.util.collection.LecternHasBookMap;
import java.util.Locale;
/**
* Used for block entities if the Java block state contains Bedrock block information.
*/
public final class BlockStateValues {
private static final IntSet ALL_CAULDRONS = new IntOpenHashSet();
private static final Int2IntMap BANNER_COLORS = new FixedInt2IntMap();
private static final Int2ByteMap BED_COLORS = new FixedInt2ByteMap();
private static final Int2IntMap BRUSH_PROGRESS = new Int2IntOpenHashMap();
private static final Int2ByteMap COMMAND_BLOCK_VALUES = new Int2ByteOpenHashMap();
private static final Int2ObjectMap<DoubleChestValue> DOUBLE_CHEST_VALUES = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<String> FLOWER_POT_VALUES = new Int2ObjectOpenHashMap<>();
private static final IntSet HORIZONTAL_FACING_JIGSAWS = new IntOpenHashSet();
private static final LecternHasBookMap LECTERN_BOOK_STATES = new LecternHasBookMap();
private static final IntSet NON_WATER_CAULDRONS = new IntOpenHashSet();
private static final Int2IntMap NOTEBLOCK_PITCHES = new FixedInt2IntMap();
private static final Int2BooleanMap PISTON_VALUES = new Int2BooleanOpenHashMap();
private static final IntSet STICKY_PISTONS = new IntOpenHashSet();
private static final Object2IntMap<Direction> PISTON_HEADS = new Object2IntOpenHashMap<>();
private static final Int2ObjectMap<Direction> PISTON_ORIENTATION = new Int2ObjectOpenHashMap<>();
private static final IntSet ALL_PISTON_HEADS = new IntOpenHashSet();
private static final IntSet MOVING_PISTONS = new IntOpenHashSet();
private static final Int2ByteMap SKULL_VARIANTS = new FixedInt2ByteMap();
private static final IntSet SKULL_POWERED = new IntOpenHashSet();
private static final Int2ByteMap SKULL_ROTATIONS = new Int2ByteOpenHashMap();
private static final Int2IntMap SKULL_WALL_DIRECTIONS = new Int2IntOpenHashMap();
private static final Int2ByteMap SHULKERBOX_DIRECTIONS = new FixedInt2ByteMap();
private static final Int2IntMap WATER_LEVEL = new Int2IntOpenHashMap();
private static final IntSet UPPER_DOORS = new IntOpenHashSet();
public static final int JAVA_AIR_ID = 0;
public static int JAVA_COBWEB_ID;
public static int JAVA_FURNACE_ID;
public static int JAVA_FURNACE_LIT_ID;
public static int JAVA_HONEY_BLOCK_ID;
public static int JAVA_SLIME_BLOCK_ID;
public static int JAVA_SPAWNER_ID;
public static int JAVA_WATER_ID;
public static final int NUM_WATER_LEVELS = 9;
/**
* Determines if the block state contains Bedrock block information
*
* @param javaId The Java Identifier of the block
* @param javaBlockState the Java Block State of the block
* @param blockData JsonNode of info about the block from blocks.json
*/
public static void storeBlockStateValues(String javaId, int javaBlockState, JsonNode blockData) {
JsonNode bannerColor = blockData.get("banner_color");
if (bannerColor != null) {
BANNER_COLORS.put(javaBlockState, (byte) bannerColor.intValue());
return; // There will never be a banner color and a skull variant
}
JsonNode bedColor = blockData.get("bed_color");
if (bedColor != null) {
BED_COLORS.put(javaBlockState, (byte) bedColor.intValue());
return;
}
JsonNode bedrockStates = blockData.get("bedrock_states");
if (bedrockStates != null) {
JsonNode brushedProgress = bedrockStates.get("brushed_progress");
if (brushedProgress != null) {
BRUSH_PROGRESS.put(javaBlockState, brushedProgress.intValue());
return;
}
}
if (javaId.contains("command_block")) {
COMMAND_BLOCK_VALUES.put(javaBlockState, javaId.contains("conditional=true") ? (byte) 1 : (byte) 0);
return;
}
if (blockData.get("double_chest_position") != null) {
boolean isX = (blockData.get("x") != null);
boolean isDirectionPositive = ((blockData.get("x") != null && blockData.get("x").asBoolean()) ||
(blockData.get("z") != null && blockData.get("z").asBoolean()));
boolean isLeft = (blockData.get("double_chest_position").asText().contains("left"));
DOUBLE_CHEST_VALUES.put(javaBlockState, new DoubleChestValue(isX, isDirectionPositive, isLeft));
return;
}
if (javaId.startsWith("minecraft:potted_") || javaId.equals("minecraft:flower_pot")) {
String name = javaId.replace("potted_", "");
if (name.contains("azalea")) {
// Exception to the rule
name = name.replace("_bush", "");
}
FLOWER_POT_VALUES.put(javaBlockState, name);
return;
}
if (javaId.startsWith("minecraft:lectern")) {
LECTERN_BOOK_STATES.put(javaBlockState, javaId.contains("has_book=true"));
return;
}
JsonNode notePitch = blockData.get("note_pitch");
if (notePitch != null) {
NOTEBLOCK_PITCHES.put(javaBlockState, blockData.get("note_pitch").intValue());
return;
}
if (javaId.contains("piston[")) { // minecraft:moving_piston, minecraft:sticky_piston, minecraft:piston
if (javaId.startsWith("minecraft:moving_piston")) {
MOVING_PISTONS.add(javaBlockState);
} else {
PISTON_VALUES.put(javaBlockState, javaId.contains("extended=true"));
}
if (javaId.contains("sticky")) {
STICKY_PISTONS.add(javaBlockState);
}
PISTON_ORIENTATION.put(javaBlockState, getBlockDirection(javaId));
return;
} else if (javaId.startsWith("minecraft:piston_head")) {
ALL_PISTON_HEADS.add(javaBlockState);
if (javaId.contains("short=false")) {
PISTON_HEADS.put(getBlockDirection(javaId), javaBlockState);
}
return;
}
JsonNode skullVariation = blockData.get("variation");
if (skullVariation != null) {
SKULL_VARIANTS.put(javaBlockState, (byte) skullVariation.intValue());
}
JsonNode skullRotation = blockData.get("skull_rotation");
if (skullRotation != null) {
SKULL_ROTATIONS.put(javaBlockState, (byte) skullRotation.intValue());
}
if (javaId.startsWith("minecraft:dragon_head[") || javaId.startsWith("minecraft:piglin_head[")
|| javaId.startsWith("minecraft:dragon_wall_head[") || javaId.startsWith("minecraft:piglin_wall_head[")) {
if (javaId.contains("powered=true")) {
SKULL_POWERED.add(javaBlockState);
}
}
if (javaId.contains("wall_skull") || javaId.contains("wall_head")) {
String direction = javaId.substring(javaId.lastIndexOf("facing=") + 7, javaId.lastIndexOf("powered=") - 1);
int rotation = switch (direction) {
case "north" -> 180;
case "west" -> 90;
case "east" -> 270;
default -> 0; // Also south
};
SKULL_WALL_DIRECTIONS.put(javaBlockState, rotation);
}
JsonNode shulkerDirection = blockData.get("shulker_direction");
if (shulkerDirection != null) {
BlockStateValues.SHULKERBOX_DIRECTIONS.put(javaBlockState, (byte) shulkerDirection.intValue());
return;
}
if (javaId.startsWith("minecraft:water") && !javaId.contains("cauldron")) {
String strLevel = javaId.substring(javaId.lastIndexOf("level=") + 6, javaId.length() - 1);
int level = Integer.parseInt(strLevel);
WATER_LEVEL.put(javaBlockState, level);
return;
}
if (javaId.startsWith("minecraft:jigsaw[orientation=")) {
String blockStateData = javaId.substring(javaId.indexOf("orientation=") + "orientation=".length(), javaId.lastIndexOf('_'));
Direction direction = Direction.valueOf(blockStateData.toUpperCase(Locale.ROOT));
if (direction.isHorizontal()) {
HORIZONTAL_FACING_JIGSAWS.add(javaBlockState);
}
return;
}
if (javaId.contains("cauldron")) {
ALL_CAULDRONS.add(javaBlockState);
}
if (javaId.contains("_cauldron") && !javaId.contains("water_")) {
NON_WATER_CAULDRONS.add(javaBlockState);
}
if (javaId.contains("_door[") && javaId.contains("half=upper")) {
UPPER_DOORS.add(javaBlockState);
}
}
/**
* Banner colors are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock.
* This gives an integer color that Bedrock can use.
*
* @param state BlockState of the block
* @return Banner color integer or -1 if no color
*/
public static int getBannerColor(int state) {
return BANNER_COLORS.getOrDefault(state, -1);
}
/**
* Bed colors are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock.
* This gives a byte color that Bedrock can use - Bedrock needs a byte in the final tag.
*
* @param state BlockState of the block
* @return Bed color byte or -1 if no color
*/
public static byte getBedColor(int state) {
return BED_COLORS.getOrDefault(state, (byte) -1);
}
/**
* The brush progress of suspicious sand/gravel is not sent by the java server when it updates the block entity.
* Although brush progress is part of the bedrock block state, it must be included in the block entity update.
*
* @param state BlockState of the block
* @return brush progress or 0 if the lookup failed
*/
public static int getBrushProgress(int state) {
return BRUSH_PROGRESS.getOrDefault(state, 0);
}
/**
* @return if this Java block state is a non-empty non-water cauldron
*/
public static boolean isNonWaterCauldron(int state) {
return NON_WATER_CAULDRONS.contains(state);
}
/**
* Cauldrons (since Bedrock 1.18.30) must have a block entity packet sent on chunk load to fix rendering issues.
* <p>
* When using a bucket on a cauldron sending a ServerboundUseItemPacket can result in the liquid being placed.
*
* @return if this Java block state is a cauldron
*/
public static boolean isCauldron(int state) {
return ALL_CAULDRONS.contains(state);
}
/**
* The block state in Java and Bedrock both contain the conditional bit, however command block block entity tags
* in Bedrock need the conditional information.
*
* @return the list of all command blocks and if they are conditional (1 or 0)
*/
public static Int2ByteMap getCommandBlockValues() {
return COMMAND_BLOCK_VALUES;
}
/**
* All double chest values are part of the block state in Java and part of the block entity tag in Bedrock.
* This gives the DoubleChestValue that can be calculated into the final tag.
*
* @return The map of all DoubleChestValues.
*/
public static Int2ObjectMap<DoubleChestValue> getDoubleChestValues() {
return DOUBLE_CHEST_VALUES;
}
/**
* Get the Int2ObjectMap of flower pot block states to containing plant
*
* @return Int2ObjectMap of flower pot values
*/
public static Int2ObjectMap<String> getFlowerPotValues() {
return FLOWER_POT_VALUES;
}
/**
* @return a set of all forward-facing jigsaws, to use as a fallback if NBT is missing.
*/
public static IntSet getHorizontalFacingJigsaws() {
return HORIZONTAL_FACING_JIGSAWS;
}
/**
* @return the lectern book state map pointing to book present state
*/
public static LecternHasBookMap getLecternBookStates() {
return LECTERN_BOOK_STATES;
}
/**
* The note that noteblocks output when hit is part of the block state in Java but sent as a BlockEventPacket in Bedrock.
* This gives an integer pitch that Bedrock can use.
*
* @param state BlockState of the block
* @return note block note integer or -1 if not present
*/
public static int getNoteblockPitch(int state) {
return NOTEBLOCK_PITCHES.getOrDefault(state, -1);
}
/**
* Get the Int2BooleanMap showing if a piston block state is extended or not.
*
* @return the Int2BooleanMap of piston extensions.
*/
public static Int2BooleanMap getPistonValues() {
return PISTON_VALUES;
}
public static boolean isStickyPiston(int blockState) {
return STICKY_PISTONS.contains(blockState);
}
public static boolean isPistonHead(int state) {
return ALL_PISTON_HEADS.contains(state);
}
/**
* Get the Java Block State for a piston head for a specific direction
* This is used in PistonBlockEntity to get the BlockCollision for the piston head.
*
* @param direction Direction the piston head points in
* @return Block state for the piston head
*/
public static int getPistonHead(Direction direction) {
return PISTON_HEADS.getOrDefault(direction, BlockStateValues.JAVA_AIR_ID);
}
/**
* Check if a block is a minecraft:moving_piston
* This is used in ChunkUtils to prevent them from being placed as it causes
* pistons to flicker and it is not needed
*
* @param state Block state of the block
* @return True if the block is a moving_piston
*/
public static boolean isMovingPiston(int state) {
return MOVING_PISTONS.contains(state);
}
/**
* This is used in GeyserPistonEvents.java and accepts minecraft:piston,
* minecraft:sticky_piston, and minecraft:moving_piston.
*
* @param state The block state of the piston base
* @return The direction in which the piston faces
*/
public static Direction getPistonOrientation(int state) {
return PISTON_ORIENTATION.get(state);
}
/**
* Checks if a block sticks to other blocks
* (Slime and honey blocks)
@ -389,8 +45,9 @@ public final class BlockStateValues {
* @param state The block state
* @return True if the block sticks to adjacent blocks
*/
public static boolean isBlockSticky(int state) {
return state == JAVA_SLIME_BLOCK_ID || state == JAVA_HONEY_BLOCK_ID;
public static boolean isBlockSticky(BlockState state) {
Block block = state.block();
return block == Blocks.SLIME_BLOCK || block == Blocks.HONEY_BLOCK;
}
/**
@ -400,13 +57,13 @@ public final class BlockStateValues {
* @param stateB The block state of block b
* @return True if the blocks are attached to each other
*/
public static boolean isBlockAttached(int stateA, int stateB) {
public static boolean isBlockAttached(BlockState stateA, BlockState stateB) {
boolean aSticky = isBlockSticky(stateA);
boolean bSticky = isBlockSticky(stateB);
if (aSticky && bSticky) {
// Only matching sticky blocks are attached together
// Honey + Honey & Slime + Slime
return stateA == stateB;
return stateA.block() == stateB.block();
}
return aSticky || bSticky;
}
@ -415,84 +72,33 @@ public final class BlockStateValues {
* @param state The block state of the block
* @return true if a piston can break the block
*/
public static boolean canPistonDestroyBlock(int state) {
return BlockRegistries.JAVA_BLOCKS.getOrDefault(state, BlockMapping.DEFAULT).getPistonBehavior() == PistonBehavior.DESTROY;
public static boolean canPistonDestroyBlock(BlockState state) {
return state.block().pushReaction() == PistonBehavior.DESTROY;
}
public static boolean canPistonMoveBlock(int javaId, boolean isPushing) {
if (javaId == JAVA_AIR_ID) {
public static boolean canPistonMoveBlock(BlockState state, boolean isPushing) {
Block block = state.block();
if (block == Blocks.AIR) {
return true;
}
// Pistons can only be moved if they aren't extended
if (PistonBlockEntityTranslator.isBlock(javaId)) {
return !PISTON_VALUES.get(javaId);
}
BlockMapping block = BlockRegistries.JAVA_BLOCKS.getOrDefault(javaId, BlockMapping.DEFAULT);
// Bedrock, End portal frames, etc. can't be moved
if (block.getHardness() == -1.0d) {
if (block == Blocks.OBSIDIAN || block == Blocks.CRYING_OBSIDIAN || block == Blocks.RESPAWN_ANCHOR || block == Blocks.REINFORCED_DEEPSLATE) { // Hardcoded as of 1.20.5
return false;
}
return switch (block.getPistonBehavior()) {
// Pistons can only be moved if they aren't extended
if (block instanceof PistonBlock) {
return !state.getValue(Properties.EXTENDED);
}
// Bedrock, End portal frames, etc. can't be moved
if (block.destroyTime() == -1.0f) {
return false;
}
return switch (block.pushReaction()) {
case BLOCK, DESTROY -> false;
case PUSH_ONLY -> isPushing; // Glazed terracotta can only be pushed
default -> !block.isBlockEntity(); // Pistons can't move block entities
default -> !block.hasBlockEntity(); // Pistons can't move block entities
};
}
/**
* Skull variations are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock.
* This gives a byte variant ID that Bedrock can use.
*
* @param state BlockState of the block
* @return Skull variant byte or -1 if no variant
*/
public static byte getSkullVariant(int state) {
return SKULL_VARIANTS.getOrDefault(state, (byte) -1);
}
/**
* Skull rotations are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock.
* This gives a byte rotation that Bedrock can use.
*
* @param state BlockState of the block
* @return Skull rotation value or -1 if no value
*/
public static byte getSkullRotation(int state) {
return SKULL_ROTATIONS.getOrDefault(state, (byte) -1);
}
/**
* As of Java 1.20.2:
* Skull powered states are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock.
*
* @param state BlockState of the block
* @return true if this skull is currently being powered.
*/
public static boolean isSkullPowered(int state) {
return SKULL_POWERED.contains(state);
}
/**
* Skull rotations are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock.
* This gives a integer rotation that Bedrock can use.
*
* @return Skull wall rotation value with the blockstate
*/
public static Int2IntMap getSkullWallDirections() {
return SKULL_WALL_DIRECTIONS;
}
/**
* Shulker box directions are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock.
* This gives a byte direction that Bedrock can use.
*
* @param state BlockState of the block
* @return Shulker direction value or -1 if no value
*/
public static byte getShulkerBoxDirection(int state) {
return SHULKERBOX_DIRECTIONS.getOrDefault(state, (byte) -1);
}
/**
* Get the level of water from the block state.
*
@ -500,17 +106,11 @@ public final class BlockStateValues {
* @return The water level or -1 if the block isn't water
*/
public static int getWaterLevel(int state) {
return WATER_LEVEL.getOrDefault(state, -1);
BlockState blockState = BlockState.of(state);
if (!blockState.is(Blocks.WATER)) {
return -1;
}
/**
* Check if a block is the upper half of a door.
*
* @param state BlockState of the block
* @return True if the block is the upper half of a door
*/
public static boolean isUpperDoor(int state) {
return UPPER_DOORS.contains(state);
return blockState.getValue(Properties.LEVEL);
}
/**
@ -544,31 +144,18 @@ public final class BlockStateValues {
* @param state BlockState of the block
* @return The block's slipperiness
*/
public static float getSlipperiness(int state) {
String blockIdentifier = BlockRegistries.JAVA_BLOCKS.getOrDefault(state, BlockMapping.DEFAULT).getJavaIdentifier();
return switch (blockIdentifier) {
case "minecraft:slime_block" -> 0.8f;
case "minecraft:ice", "minecraft:packed_ice" -> 0.98f;
case "minecraft:blue_ice" -> 0.989f;
default -> 0.6f;
};
public static float getSlipperiness(BlockState state) {
Block block = state.block();
if (block == Blocks.SLIME_BLOCK) {
return 0.8f;
}
private static Direction getBlockDirection(String javaId) {
if (javaId.contains("down")) {
return Direction.DOWN;
} else if (javaId.contains("up")) {
return Direction.UP;
} else if (javaId.contains("south")) {
return Direction.SOUTH;
} else if (javaId.contains("west")) {
return Direction.WEST;
} else if (javaId.contains("north")) {
return Direction.NORTH;
} else if (javaId.contains("east")) {
return Direction.EAST;
if (block == Blocks.ICE || block == Blocks.PACKED_ICE) {
return 0.98f;
}
throw new IllegalStateException();
if (block == Blocks.BLUE_ICE) {
return 0.989f;
}
return 0.6f;
}
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
* of this software and associated documentation files (the "Software"), to deal
@ -23,12 +23,24 @@
* @link https://github.com/GeyserMC/Geyser
*/
/**
* Contains useful collections for use in Geyser.
* <p>
* Of note are the fixed int maps. Designed for use with block states that are positive and sequential, they do not allow keys to be
* added that are not greater by one versus the previous key. Because of this, speedy operations of {@link java.util.Map#get(java.lang.Object)}
* and {@link java.util.Map#containsKey(java.lang.Object)} can be performed by simply checking the bounds of the map
* size and its "start" integer.
*/
package org.geysermc.geyser.util.collection;
package org.geysermc.geyser.level.block.property;
public final class BooleanProperty extends Property<Boolean> {
private BooleanProperty(String name) {
super(name);
}
@Override
public int valuesCount() {
return 2;
}
@Override
public int indexOf(Boolean value) {
return value ? 0 : 1;
}
public static BooleanProperty create(String name) {
return new BooleanProperty(name);
}
}

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
* of this software and associated documentation files (the "Software"), to deal
@ -23,18 +23,12 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.level.block;
package org.geysermc.geyser.level.block.property;
/**
* This stores all values of double chests that are part of the Java block state.
*
* @param isFacingEast If true, then chest is facing east/west; if false, south/north
* @param isDirectionPositive If true, direction is positive (east/south); if false, direction is negative (west/north)
* @param isLeft If true, chest is the left of a pair; if false, chest is the right of a pair.
*/
public record DoubleChestValue(
boolean isFacingEast,
boolean isDirectionPositive,
boolean isLeft) {
public enum ChestType {
SINGLE,
LEFT,
RIGHT;
public static final ChestType[] VALUES = values();
}

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
* of this software and associated documentation files (the "Software"), to deal
@ -23,64 +23,36 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.translator.level.block.entity;
package org.geysermc.geyser.level.block.type;
import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.cloudburstmc.protocol.bedrock.packet.UpdateBlockPacket;
import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.level.block.Blocks;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.level.block.entity.BedrockChunkWantsBlockEntityTag;
import org.geysermc.geyser.translator.level.block.entity.BlockEntityTranslator;
import org.geysermc.geyser.util.BlockEntityUtils;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
public class FlowerPotBlockEntityTranslator implements BedrockOnlyBlockEntity {
/**
* @param blockState the Java block state of a potential flower pot block
* @return true if the block is a flower pot
*/
public static boolean isFlowerBlock(int blockState) {
return BlockStateValues.getFlowerPotValues().containsKey(blockState);
}
public class FlowerPotBlock extends Block implements BedrockChunkWantsBlockEntityTag {
private final Block flower;
/**
* Get the Nukkit CompoundTag of the flower pot.
*
* @param blockState Java block state of flower pot.
* @param position Bedrock position of flower pot.
* @return Bedrock tag of flower pot.
*/
public static NbtMap getTag(GeyserSession session, int blockState, Vector3i position) {
NbtMapBuilder tagBuilder = NbtMap.builder()
.putInt("x", position.getX())
.putInt("y", position.getY())
.putInt("z", position.getZ())
.putByte("isMovable", (byte) 1)
.putString("id", "FlowerPot");
// Get the Java name of the plant inside. e.g. minecraft:oak_sapling
String name = BlockStateValues.getFlowerPotValues().get(blockState);
if (name != null) {
// Get the Bedrock CompoundTag of the block.
// This is where we need to store the *Java* name because Bedrock has six minecraft:sapling blocks with different block states.
NbtMap plant = session.getBlockMappings().getFlowerPotBlocks().get(name);
if (plant != null) {
tagBuilder.put("PlantBlock", plant.toBuilder().build());
}
}
return tagBuilder.build();
public FlowerPotBlock(String javaIdentifier, Block flower, Builder builder) {
super(javaIdentifier, builder);
this.flower = flower;
}
@Override
public boolean isBlock(int blockState) {
return isFlowerBlock(blockState);
}
public void updateBlock(GeyserSession session, BlockState state, Vector3i position) {
super.updateBlock(session, state, position);
@Override
public void updateBlock(GeyserSession session, int blockState, Vector3i position) {
NbtMap tag = getTag(session, blockState, position);
NbtMap tag = createTag(session, position, state);
BlockEntityUtils.updateBlockEntity(session, tag, position);
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
updateBlockPacket.setDataLayer(0);
updateBlockPacket.setDefinition(session.getBlockMappings().getBedrockBlock(blockState));
updateBlockPacket.setDefinition(session.getBlockMappings().getBedrockBlock(state));
updateBlockPacket.setBlockPosition(position);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
@ -88,4 +60,33 @@ public class FlowerPotBlockEntityTranslator implements BedrockOnlyBlockEntity {
session.sendUpstreamPacket(updateBlockPacket);
BlockEntityUtils.updateBlockEntity(session, tag, position);
}
@Override
public NbtMap createTag(GeyserSession session, Vector3i position, BlockState blockState) {
NbtMapBuilder tagBuilder = BlockEntityTranslator.getConstantBedrockTag("FlowerPot", position.getX(), position.getY(), position.getZ())
.putByte("isMovable", (byte) 1);
// Get the Java name of the plant inside. e.g. minecraft:oak_sapling
if (this.flower != Blocks.AIR) {
// Get the Bedrock CompoundTag of the block.
// This is where we need to store the *Java* name because Bedrock has six minecraft:sapling blocks with different block states.
// TODO flattening might make this nicer in the future!
NbtMap plant = session.getBlockMappings().getFlowerPotBlocks().get(this.flower);
if (plant != null) {
tagBuilder.putCompound("PlantBlock", plant.toBuilder().build());
}
}
return tagBuilder.build();
}
@Override
public ItemStack pickItem(BlockState state) {
if (this.flower != Blocks.AIR) {
return new ItemStack(this.flower.asItem().javaId());
}
return super.pickItem(state);
}
public Block flower() {
return flower;
}
}

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.player.PlayerEntity;
import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.level.block.Blocks;
import org.geysermc.geyser.level.block.property.Properties;
import org.geysermc.geyser.level.block.type.BlockState;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.PistonCache;
import org.geysermc.geyser.translator.collision.BlockCollision;
@ -405,7 +408,8 @@ public class CollisionManager {
* @return if the player is currently in a water block
*/
public boolean isPlayerInWater() {
return session.getGeyser().getWorldManager().getBlockAt(session, session.getPlayerEntity().getPosition().toInt()) == BlockStateValues.JAVA_WATER_ID;
BlockState state = session.getGeyser().getWorldManager().blockAt(session, session.getPlayerEntity().getPosition().toInt());
return state.is(Blocks.WATER) && state.getValue(Properties.LEVEL) == 0;
}
public boolean isWaterInEyes() {

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -47,7 +47,9 @@ public class InvalidPacketHandler extends ChannelInboundHandlerAdapter {
if (!(rootCause instanceof IllegalArgumentException)) {
super.exceptionCaught(ctx, cause);
// Kick users that cause exceptions
session.getGeyser().getLogger().warning("Exception caught in session of" + session.bedrockUsername() + ": " + rootCause.getMessage());
session.disconnect("An internal error occurred!");
return;
}

Datei anzeigen

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

Datei anzeigen

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

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.CustomBlockState;
import org.geysermc.geyser.api.block.custom.nonvanilla.JavaBlockState;
import org.geysermc.geyser.level.block.Blocks;
import org.geysermc.geyser.level.block.type.Block;
import org.geysermc.geyser.level.block.type.BlockState;
import org.geysermc.geyser.registry.loader.CollisionRegistryLoader;
import org.geysermc.geyser.registry.loader.RegistryLoaders;
import org.geysermc.geyser.registry.populator.BlockRegistryPopulator;
import org.geysermc.geyser.registry.populator.CustomBlockRegistryPopulator;
import org.geysermc.geyser.registry.populator.CustomSkullRegistryPopulator;
import org.geysermc.geyser.registry.type.BlockMapping;
import org.geysermc.geyser.registry.type.BlockMappings;
import org.geysermc.geyser.registry.type.CustomSkull;
import org.geysermc.geyser.translator.collision.BlockCollision;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Set;
/**
@ -59,34 +61,32 @@ public class BlockRegistries {
public static final VersionedRegistry<BlockMappings> BLOCKS = VersionedRegistry.create(RegistryLoaders.empty(Int2ObjectOpenHashMap::new));
/**
* A mapped registry which stores Java to Bedrock block identifiers.
* A registry which stores Java IDs to Java {@link BlockState}s, each with their specific state differences and a link
* to the overarching block.
*/
public static final SimpleMappedRegistry<String, String> JAVA_TO_BEDROCK_IDENTIFIERS = SimpleMappedRegistry.create(RegistryLoaders.empty(Object2ObjectOpenHashMap::new));
/**
* A registry which stores Java IDs to {@link BlockMapping}, containing miscellaneous information about
* blocks and their behavior in many cases.
*/
public static final ArrayRegistry<BlockMapping> JAVA_BLOCKS = ArrayRegistry.create(RegistryLoaders.uninitialized());
public static final ListRegistry<BlockState> BLOCK_STATES = ListRegistry.create(RegistryLoaders.empty(ArrayList::new));
/**
* A mapped registry containing which holds block IDs to its {@link BlockCollision}.
*/
public static final IntMappedRegistry<BlockCollision> COLLISIONS;
public static final ListRegistry<BlockCollision> COLLISIONS;
/**
* A registry which stores Java IDs to {@link Block}, containing miscellaneous information about
* blocks and their behavior in many cases.
*/
public static final ListRegistry<Block> JAVA_BLOCKS = ListRegistry.create(RegistryLoaders.empty(ArrayList::new));
/**
* A mapped registry containing the Java identifiers to IDs.
*/
public static final MappedRegistry<String, Integer, Object2IntMap<String>> JAVA_IDENTIFIER_TO_ID = MappedRegistry.create(RegistryLoaders.empty(Object2IntOpenHashMap::new));
/**
* A registry which stores unique Java IDs to its clean identifier
* This is used in the statistics form.
*/
public static final ArrayRegistry<String> CLEAN_JAVA_IDENTIFIERS = ArrayRegistry.create(RegistryLoaders.uninitialized());
/**
* A registry containing all the waterlogged blockstates.
* Properties.WATERLOGGED should not be relied on for two reasons:
* - Custom blocks
* - Seagrass, kelp, and bubble columns are assumed waterlogged and don't have a waterlogged property
*/
public static final SimpleRegistry<BitSet> WATERLOGGED = SimpleRegistry.create(RegistryLoaders.empty(BitSet::new));
@ -131,12 +131,13 @@ public class BlockRegistries {
public static final SimpleMappedRegistry<String, CustomSkull> CUSTOM_SKULLS = SimpleMappedRegistry.create(RegistryLoaders.empty(Object2ObjectOpenHashMap::new));
static {
Blocks.VAULT.javaId(); // FIXME
CustomSkullRegistryPopulator.populate();
BlockRegistryPopulator.populate(BlockRegistryPopulator.Stage.PRE_INIT);
CustomBlockRegistryPopulator.populate(CustomBlockRegistryPopulator.Stage.DEFINITION);
CustomBlockRegistryPopulator.populate(CustomBlockRegistryPopulator.Stage.NON_VANILLA_REGISTRATION);
BlockRegistryPopulator.populate(BlockRegistryPopulator.Stage.INIT_JAVA);
COLLISIONS = IntMappedRegistry.create(Pair.of("org.geysermc.geyser.translator.collision.CollisionRemapper", "mappings/collision.json"), CollisionRegistryLoader::new);
COLLISIONS = ListRegistry.create(Pair.of("org.geysermc.geyser.translator.collision.CollisionRemapper", "mappings/collisions.nbt"), CollisionRegistryLoader::new);
CustomBlockRegistryPopulator.populate(CustomBlockRegistryPopulator.Stage.VANILLA_REGISTRATION);
CustomBlockRegistryPopulator.populate(CustomBlockRegistryPopulator.Stage.CUSTOM_REGISTRATION);
BlockRegistryPopulator.populate(BlockRegistryPopulator.Stage.INIT_BEDROCK);

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

Datei anzeigen

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

Datei anzeigen

@ -32,36 +32,37 @@ import com.google.common.collect.Interner;
import com.google.common.collect.Interners;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import it.unimi.dsi.fastutil.objects.*;
import org.cloudburstmc.blockstateupdater.BlockStateUpdater;
import org.cloudburstmc.blockstateupdater.util.tagupdater.CompoundTagUpdaterContext;
import org.cloudburstmc.nbt.NBTInputStream;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.cloudburstmc.nbt.NbtType;
import org.cloudburstmc.protocol.bedrock.codec.v622.Bedrock_v622;
import org.cloudburstmc.protocol.bedrock.codec.v630.Bedrock_v630;
import org.cloudburstmc.protocol.bedrock.codec.v649.Bedrock_v649;
import org.cloudburstmc.protocol.bedrock.codec.v662.Bedrock_v662;
import org.cloudburstmc.nbt.*;
import org.cloudburstmc.protocol.bedrock.codec.v671.Bedrock_v671;
import org.cloudburstmc.protocol.bedrock.codec.v685.Bedrock_v685;
import org.cloudburstmc.protocol.bedrock.data.BlockPropertyData;
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.block.custom.CustomBlockData;
import org.geysermc.geyser.api.block.custom.CustomBlockState;
import org.geysermc.geyser.api.block.custom.nonvanilla.JavaBlockState;
import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.level.block.Blocks;
import org.geysermc.geyser.level.block.property.Properties;
import org.geysermc.geyser.level.block.type.Block;
import org.geysermc.geyser.level.block.type.BlockState;
import org.geysermc.geyser.level.block.type.FlowerPotBlock;
import org.geysermc.geyser.level.physics.PistonBehavior;
import org.geysermc.geyser.registry.BlockRegistries;
import org.geysermc.geyser.registry.type.BlockMapping;
import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.registry.type.BlockMappings;
import org.geysermc.geyser.registry.type.GeyserBedrockBlock;
import org.geysermc.geyser.util.BlockUtils;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import java.io.DataInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Stream;
import java.util.zip.GZIPInputStream;
/**
@ -82,24 +83,11 @@ public final class BlockRegistryPopulator {
interface Remapper {
NbtMap remap(NbtMap tag);
static Remapper of(BlockStateUpdater... updaters) {
CompoundTagUpdaterContext context = new CompoundTagUpdaterContext();
for (BlockStateUpdater updater : updaters) {
updater.registerUpdaters(context);
}
return tag -> {
NbtMapBuilder updated = context.update(tag, 0).toBuilder();
updated.remove("version"); // we already removed this, but the context adds it. remove it again.
return updated.build();
};
}
}
public static void populate(Stage stage) {
switch (stage) {
case PRE_INIT, POST_INIT -> nullifyBlocksNode();
case PRE_INIT, POST_INIT -> nullifyBlocksNbt();
case INIT_JAVA -> registerJavaBlocks();
case INIT_BEDROCK -> registerBedrockBlocks();
default -> throw new IllegalArgumentException("Unknown stage: " + stage);
@ -107,24 +95,20 @@ public final class BlockRegistryPopulator {
}
/**
* Stores the raw blocks JSON until it is no longer needed.
* Stores the raw blocks NBT until it is no longer needed.
*/
private static JsonNode BLOCKS_JSON;
private static List<NbtMap> BLOCKS_NBT;
private static int MIN_CUSTOM_RUNTIME_ID = -1;
private static int JAVA_BLOCKS_SIZE = -1;
private static void nullifyBlocksNode() {
BLOCKS_JSON = null;
private static void nullifyBlocksNbt() {
BLOCKS_NBT = null;
}
private static void registerBedrockBlocks() {
var blockMappers = ImmutableMap.<ObjectIntPair<String>, Remapper>builder()
.put(ObjectIntPair.of("1_20_40", Bedrock_v622.CODEC.getProtocolVersion()), Conversion630_622::remapBlock)
.put(ObjectIntPair.of("1_20_50", Bedrock_v630.CODEC.getProtocolVersion()), Conversion649_630::remapBlock)
// Only changes in 1.20.60 are hard_stained_glass (an EDU only block)
.put(ObjectIntPair.of("1_20_60", Bedrock_v649.CODEC.getProtocolVersion()), Conversion662_649::remapBlock)
.put(ObjectIntPair.of("1_20_70", Bedrock_v662.CODEC.getProtocolVersion()), Conversion671_662::remapBlock)
.put(ObjectIntPair.of("1_20_80", Bedrock_v671.CODEC.getProtocolVersion()), tag -> tag)
.put(ObjectIntPair.of("1_20_80", Bedrock_v671.CODEC.getProtocolVersion()), Conversion685_671::remapBlock)
.put(ObjectIntPair.of("1_21_0", Bedrock_v685.CODEC.getProtocolVersion()), tag -> tag)
.build();
// We can keep this strong as nothing should be garbage collected
@ -215,19 +199,34 @@ public final class BlockRegistryPopulator {
int javaRuntimeId = -1;
List<BlockState> javaBlockStates = BlockRegistries.BLOCK_STATES.get();
GeyserBedrockBlock airDefinition = null;
BlockDefinition commandBlockDefinition = null;
BlockDefinition mobSpawnerBlockDefinition = null;
BlockDefinition waterDefinition = null;
BlockDefinition movingBlockDefinition = null;
Iterator<Map.Entry<String, JsonNode>> blocksIterator = BLOCKS_JSON.fields();
Iterator<NbtMap> blocksIterator = BLOCKS_NBT.iterator();
Remapper stateMapper = blockMappers.get(palette);
GeyserBedrockBlock[] javaToBedrockBlocks = new GeyserBedrockBlock[JAVA_BLOCKS_SIZE];
GeyserBedrockBlock[] javaToVanillaBedrockBlocks = new GeyserBedrockBlock[JAVA_BLOCKS_SIZE];
Map<String, NbtMap> flowerPotBlocks = new Object2ObjectOpenHashMap<>();
var javaToBedrockIdentifiers = new Int2ObjectOpenHashMap<String>();
Block lastBlockSeen = null;
// Stream isn't ideal.
List<Block> javaPottable = BlockRegistries.JAVA_BLOCKS.get()
.parallelStream()
.flatMap(block -> {
if (block instanceof FlowerPotBlock flowerPot && flowerPot.flower() != Blocks.AIR) {
return Stream.of(flowerPot.flower());
}
return null;
})
.toList();
Map<Block, NbtMap> flowerPotBlocks = new Object2ObjectOpenHashMap<>();
Map<NbtMap, BlockDefinition> itemFrames = new Object2ObjectOpenHashMap<>();
Set<BlockDefinition> jigsawDefinitions = new ObjectOpenHashSet<>();
@ -236,10 +235,11 @@ public final class BlockRegistryPopulator {
BlockMappings.BlockMappingsBuilder builder = BlockMappings.builder();
while (blocksIterator.hasNext()) {
javaRuntimeId++;
Map.Entry<String, JsonNode> entry = blocksIterator.next();
String javaId = entry.getKey();
NbtMap entry = blocksIterator.next();
BlockState blockState = javaBlockStates.get(javaRuntimeId);
String javaId = blockState.toString();
NbtMap originalBedrockTag = buildBedrockState(entry.getValue());
NbtMap originalBedrockTag = buildBedrockState(blockState, entry);
NbtMap bedrockTag = stateMapper.remap(originalBedrockTag);
GeyserBedrockBlock vanillaBedrockDefinition = blockStateOrderedMap.get(bedrockTag);
@ -271,35 +271,36 @@ public final class BlockRegistryPopulator {
case "minecraft:moving_piston[facing=north,type=normal]" -> movingBlockDefinition = bedrockDefinition;
}
if (javaId.contains("jigsaw")) {
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);
}
if (javaId.contains("structure_block")) {
int modeIndex = javaId.indexOf("mode=");
if (modeIndex != -1) {
int startIndex = modeIndex + 5; // Length of "mode=" is 5
int endIndex = javaId.indexOf("]", startIndex);
if (endIndex != -1) {
String modeValue = javaId.substring(startIndex, endIndex);
structureBlockDefinitions.put(modeValue.toUpperCase(), bedrockDefinition);
}
}
if (block == Blocks.STRUCTURE_BLOCK) {
String mode = blockState.getValue(Properties.STRUCTUREBLOCK_MODE);
structureBlockDefinitions.put(mode.toUpperCase(Locale.ROOT), bedrockDefinition);
}
boolean waterlogged = entry.getKey().contains("waterlogged=true")
|| javaId.contains("minecraft:bubble_column") || javaId.contains("minecraft:kelp") || javaId.contains("seagrass");
boolean waterlogged = blockState.getValue(Properties.WATERLOGGED, false)
|| block == Blocks.BUBBLE_COLUMN || block == Blocks.KELP || block == Blocks.KELP_PLANT
|| block == Blocks.SEAGRASS || block == Blocks.TALL_SEAGRASS;
if (waterlogged) {
int finalJavaRuntimeId = javaRuntimeId;
BlockRegistries.WATERLOGGED.register(set -> set.set(finalJavaRuntimeId));
BlockRegistries.WATERLOGGED.get().set(javaRuntimeId);
}
String cleanJavaIdentifier = BlockUtils.getCleanIdentifier(entry.getKey());
// Get the tag needed for non-empty flower pots
if (entry.getValue().get("pottable") != null) {
flowerPotBlocks.put(cleanJavaIdentifier.intern(), blockStates.get(bedrockDefinition.getRuntimeId()));
if (javaPottable.contains(block)) {
// Specifically NOT putIfAbsent - mangrove propagule breaks otherwise
flowerPotBlocks.put(block, blockStates.get(bedrockDefinition.getRuntimeId()));
}
javaToVanillaBedrockBlocks[javaRuntimeId] = vanillaBedrockDefinition;
@ -355,9 +356,12 @@ public final class BlockRegistryPopulator {
javaToVanillaBedrockBlocks[stateRuntimeId] = bedrockDefinition; // TODO: Check this?
javaToBedrockBlocks[stateRuntimeId] = bedrockDefinition;
javaToBedrockIdentifiers.put(entry.getKey().stateGroupId(), entry.getValue().block().identifier());
}
}
javaToBedrockIdentifiers.trim();
// Loop around again to find all item frame runtime IDs
Object2ObjectMaps.fastForEach(blockStateOrderedMap, entry -> {
String name = entry.getKey().getString("name");
@ -369,6 +373,7 @@ public final class BlockRegistryPopulator {
BlockRegistries.BLOCKS.register(palette.valueInt(), builder.bedrockRuntimeMap(bedrockRuntimeMap)
.javaToBedrockBlocks(javaToBedrockBlocks)
.javaToVanillaBedrockBlocks(javaToVanillaBedrockBlocks)
.javaToBedrockIdentifiers(javaToBedrockIdentifiers)
.stateDefinitionMap(blockStateOrderedMap)
.itemFrames(itemFrames)
.flowerPotBlocks(flowerPotBlocks)
@ -383,208 +388,81 @@ public final class BlockRegistryPopulator {
}
private static void registerJavaBlocks() {
JsonNode blocksJson;
try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow("mappings/blocks.json")) {
blocksJson = GeyserImpl.JSON_MAPPER.readTree(stream);
List<NbtMap> blocksNbt;
try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow("mappings/blocks.nbt")) {
blocksNbt = ((NbtMap) NbtUtils.createGZIPReader(stream).readTag())
.getList("bedrock_mappings", NbtType.COMPOUND);
} catch (Exception e) {
throw new AssertionError("Unable to load Java block mappings", e);
}
JAVA_BLOCKS_SIZE = blocksJson.size();
JAVA_BLOCKS_SIZE = BlockRegistries.BLOCK_STATES.get().size();
if (!BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().isEmpty()) {
MIN_CUSTOM_RUNTIME_ID = BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().keySet().stream().min(Comparator.comparing(JavaBlockState::javaId)).orElseThrow().javaId();
int maxCustomRuntimeID = BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().keySet().stream().max(Comparator.comparing(JavaBlockState::javaId)).orElseThrow().javaId();
if (MIN_CUSTOM_RUNTIME_ID < blocksJson.size()) {
if (MIN_CUSTOM_RUNTIME_ID < blocksNbt.size()) {
throw new RuntimeException("Non vanilla custom block state overrides runtime ID must start after the last vanilla block state (" + JAVA_BLOCKS_SIZE + ")");
}
JAVA_BLOCKS_SIZE = maxCustomRuntimeID + 1; // Runtime ids start at 0, so we need to add 1
}
BlockRegistries.JAVA_BLOCKS.set(new BlockMapping[JAVA_BLOCKS_SIZE]); // Set array size to number of blockstates
Deque<String> cleanIdentifiers = new ArrayDeque<>();
int javaRuntimeId = -1;
int cobwebBlockId = -1;
int furnaceRuntimeId = -1;
int furnaceLitRuntimeId = -1;
int honeyBlockRuntimeId = -1;
int slimeBlockRuntimeId = -1;
int spawnerRuntimeId = -1;
int uniqueJavaId = -1;
int waterRuntimeId = -1;
Iterator<Map.Entry<String, JsonNode>> blocksIterator = blocksJson.fields();
while (blocksIterator.hasNext()) {
for (BlockState javaBlockState : BlockRegistries.BLOCK_STATES.get()) {
javaRuntimeId++;
Map.Entry<String, JsonNode> entry = blocksIterator.next();
String javaId = entry.getKey();
// TODO fix this, (no block should have a null hardness)
BlockMapping.BlockMappingBuilder builder = BlockMapping.builder();
JsonNode hardnessNode = entry.getValue().get("block_hardness");
if (hardnessNode != null) {
builder.hardness(hardnessNode.floatValue());
}
JsonNode canBreakWithHandNode = entry.getValue().get("can_break_with_hand");
if (canBreakWithHandNode != null) {
builder.canBreakWithHand(canBreakWithHandNode.booleanValue());
} else {
builder.canBreakWithHand(false);
}
JsonNode collisionIndexNode = entry.getValue().get("collision_index");
if (hardnessNode != null) {
builder.collisionIndex(collisionIndexNode.intValue());
}
JsonNode pickItemNode = entry.getValue().get("pick_item");
if (pickItemNode != null) {
builder.pickItem(pickItemNode.textValue().intern());
}
if (javaId.equals("minecraft:obsidian") || javaId.equals("minecraft:crying_obsidian") || javaId.startsWith("minecraft:respawn_anchor") || javaId.startsWith("minecraft:reinforced_deepslate")) {
builder.pistonBehavior(PistonBehavior.BLOCK);
} else {
JsonNode pistonBehaviorNode = entry.getValue().get("piston_behavior");
if (pistonBehaviorNode != null) {
builder.pistonBehavior(PistonBehavior.getByName(pistonBehaviorNode.textValue()));
} else {
builder.pistonBehavior(PistonBehavior.NORMAL);
}
}
JsonNode hasBlockEntityNode = entry.getValue().get("has_block_entity");
if (hasBlockEntityNode != null) {
builder.isBlockEntity(hasBlockEntityNode.booleanValue());
} else {
builder.isBlockEntity(false);
}
BlockStateValues.storeBlockStateValues(entry.getKey(), javaRuntimeId, entry.getValue());
String cleanJavaIdentifier = BlockUtils.getCleanIdentifier(entry.getKey());
String bedrockIdentifier = entry.getValue().get("bedrock_identifier").asText();
if (!cleanJavaIdentifier.equals(cleanIdentifiers.peekLast())) {
uniqueJavaId++;
cleanIdentifiers.add(cleanJavaIdentifier.intern());
}
builder.javaIdentifier(javaId);
builder.javaBlockId(uniqueJavaId);
String javaId = javaBlockState.toString().intern();
BlockRegistries.JAVA_IDENTIFIER_TO_ID.register(javaId, javaRuntimeId);
BlockRegistries.JAVA_BLOCKS.register(javaRuntimeId, builder.build());
// Keeping this here since this is currently unchanged between versions
// It's possible to only have this store differences in names, but the key set of all Java names is used in sending command suggestions
BlockRegistries.JAVA_TO_BEDROCK_IDENTIFIERS.register(cleanJavaIdentifier.intern(), bedrockIdentifier.intern());
if (javaId.contains("cobweb")) {
cobwebBlockId = uniqueJavaId;
} else if (javaId.startsWith("minecraft:furnace[facing=north")) {
if (javaId.contains("lit=true")) {
furnaceLitRuntimeId = javaRuntimeId;
} else {
furnaceRuntimeId = javaRuntimeId;
}
} else if (javaId.startsWith("minecraft:spawner")) {
spawnerRuntimeId = javaRuntimeId;
} else if ("minecraft:water[level=0]".equals(javaId)) {
waterRuntimeId = javaRuntimeId;
} else if (javaId.equals("minecraft:honey_block")) {
honeyBlockRuntimeId = javaRuntimeId;
} else if (javaId.equals("minecraft:slime_block")) {
slimeBlockRuntimeId = javaRuntimeId;
}
}
if (cobwebBlockId == -1) {
throw new AssertionError("Unable to find cobwebs in palette");
}
BlockStateValues.JAVA_COBWEB_ID = cobwebBlockId;
if (furnaceRuntimeId == -1) {
throw new AssertionError("Unable to find furnace in palette");
}
BlockStateValues.JAVA_FURNACE_ID = furnaceRuntimeId;
if (furnaceLitRuntimeId == -1) {
throw new AssertionError("Unable to find lit furnace in palette");
}
BlockStateValues.JAVA_FURNACE_LIT_ID = furnaceLitRuntimeId;
if (honeyBlockRuntimeId == -1) {
throw new AssertionError("Unable to find honey block in palette");
}
BlockStateValues.JAVA_HONEY_BLOCK_ID = honeyBlockRuntimeId;
if (slimeBlockRuntimeId == -1) {
throw new AssertionError("Unable to find slime block in palette");
}
BlockStateValues.JAVA_SLIME_BLOCK_ID = slimeBlockRuntimeId;
if (spawnerRuntimeId == -1) {
throw new AssertionError("Unable to find spawner in palette");
}
BlockStateValues.JAVA_SPAWNER_ID = spawnerRuntimeId;
if (waterRuntimeId == -1) {
throw new AssertionError("Unable to find Java water in palette");
}
BlockStateValues.JAVA_WATER_ID = waterRuntimeId;
if (!BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().isEmpty()) {
Set<Integer> usedNonVanillaRuntimeIDs = new HashSet<>();
IntSet usedNonVanillaRuntimeIDs = new IntOpenHashSet();
for (JavaBlockState javaBlockState : BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().keySet()) {
if (!usedNonVanillaRuntimeIDs.add(javaBlockState.javaId())) {
throw new RuntimeException("Duplicate runtime ID " + javaBlockState.javaId() + " for non vanilla Java block state " + javaBlockState.identifier());
}
CustomBlockState customBlockState = BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().get(javaBlockState);
String javaId = javaBlockState.identifier();
int stateRuntimeId = javaBlockState.javaId();
String pistonBehavior = javaBlockState.pistonBehavior();
BlockMapping blockMapping = BlockMapping.builder()
.canBreakWithHand(javaBlockState.canBreakWithHand())
.pickItem(javaBlockState.pickItem())
.isNonVanilla(true)
.javaIdentifier(javaId)
.javaBlockId(javaBlockState.stateGroupId())
.hardness(javaBlockState.blockHardness())
.pistonBehavior(pistonBehavior == null ? PistonBehavior.NORMAL : PistonBehavior.getByName(pistonBehavior))
.isBlockEntity(javaBlockState.hasBlockEntity())
.build();
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 bedrockIdentifier = customBlockState.block().identifier();
if (!cleanJavaIdentifier.equals(cleanIdentifiers.peekLast())) {
uniqueJavaId++;
cleanIdentifiers.add(cleanJavaIdentifier.intern());
String pickItem = javaBlockState.pickItem();
Block block = new Block(cleanJavaIdentifier, builder) {
@Override
public ItemStack pickItem(BlockState state) {
if (this.item == null) {
this.item = Registries.JAVA_ITEM_IDENTIFIERS.get(pickItem);
if (this.item == null) {
GeyserImpl.getInstance().getLogger().warning("We could not find item " + pickItem
+ " for getting the item for block " + javaBlockState.identifier());
this.item = Items.AIR;
}
}
return new ItemStack(this.item.javaId());
}
};
block.setJavaId(javaBlockState.stateGroupId());
BlockRegistries.JAVA_BLOCKS.registerWithAnyIndex(javaBlockState.stateGroupId(), block, Blocks.AIR);
BlockRegistries.JAVA_IDENTIFIER_TO_ID.register(javaId, stateRuntimeId);
BlockRegistries.JAVA_BLOCKS.register(stateRuntimeId, blockMapping);
// Keeping this here since this is currently unchanged between versions
// It's possible to only have this store differences in names, but the key set of all Java names is used in sending command suggestions
BlockRegistries.JAVA_TO_BEDROCK_IDENTIFIERS.register(cleanJavaIdentifier.intern(), bedrockIdentifier.intern());
BlockRegistries.BLOCK_STATES.register(stateRuntimeId, new BlockState(block, stateRuntimeId));
}
}
BlockRegistries.CLEAN_JAVA_IDENTIFIERS.set(cleanIdentifiers.toArray(new String[0]));
BLOCKS_JSON = blocksJson;
BLOCKS_NBT = blocksNbt;
JsonNode blockInteractionsJson;
try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow("mappings/interactions.json")) {
@ -595,6 +473,8 @@ public final class BlockRegistryPopulator {
BlockRegistries.INTERACTIVE.set(toBlockStateSet((ArrayNode) blockInteractionsJson.get("always_consumes")));
BlockRegistries.INTERACTIVE_MAY_BUILD.set(toBlockStateSet((ArrayNode) blockInteractionsJson.get("requires_may_build")));
BlockRegistries.BLOCK_STATES.freeze();
}
private static BitSet toBlockStateSet(ArrayNode node) {
@ -605,29 +485,11 @@ public final class BlockRegistryPopulator {
return blockStateSet;
}
private static NbtMap buildBedrockState(JsonNode node) {
private static NbtMap buildBedrockState(BlockState state, NbtMap nbt) {
NbtMapBuilder tagBuilder = NbtMap.builder();
String bedrockIdentifier = node.get("bedrock_identifier").textValue();
String bedrockIdentifier = "minecraft:" + nbt.getString("bedrock_identifier", state.block().javaIdentifier().value());
tagBuilder.putString("name", bedrockIdentifier);
NbtMapBuilder statesBuilder = NbtMap.builder();
// check for states
JsonNode states = node.get("bedrock_states");
if (states != null) {
Iterator<Map.Entry<String, JsonNode>> statesIterator = states.fields();
while (statesIterator.hasNext()) {
Map.Entry<String, JsonNode> stateEntry = statesIterator.next();
JsonNode stateValue = stateEntry.getValue();
switch (stateValue.getNodeType()) {
case BOOLEAN -> statesBuilder.putBoolean(stateEntry.getKey(), stateValue.booleanValue());
case STRING -> statesBuilder.putString(stateEntry.getKey(), stateValue.textValue());
case NUMBER -> statesBuilder.putInt(stateEntry.getKey(), stateValue.intValue());
}
}
}
tagBuilder.put("states", statesBuilder.build());
tagBuilder.put("states", nbt.getCompound("state"));
return tagBuilder.build();
}

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

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