Merge remote-tracking branch 'refs/remotes/upstream/master' into end-void

# Conflicts:
#	core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java
This commit is contained in:
onebeastchris 2024-06-19 02:31:27 +02:00
commit c496680289
309 changed files with 12400 additions and 43130 deletions

View file

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

View file

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

33
.github/workflows/dispatch-preview.yml vendored Normal file
View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -55,4 +55,5 @@ tasks {
modrinth {
loaders.add("neoforge")
uploadFile.set(tasks.getByPath("remapModrinthJar"))
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -25,6 +25,8 @@
package org.geysermc.geyser.entity;
import org.geysermc.geyser.entity.type.AbstractWindChargeEntity;
import org.geysermc.geyser.entity.factory.EntityFactory;
import org.geysermc.geyser.entity.type.living.monster.raid.RavagerEntity;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.MetadataType;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
@ -63,6 +65,9 @@ public final class EntityDefinitions {
public static final EntityDefinition<BeeEntity> BEE;
public static final EntityDefinition<BlazeEntity> BLAZE;
public static final EntityDefinition<BoatEntity> BOAT;
public static final EntityDefinition<BoggedEntity> BOGGED;
public static final EntityDefinition<BreezeEntity> BREEZE;
public static final EntityDefinition<AbstractWindChargeEntity> BREEZE_WIND_CHARGE;
public static final EntityDefinition<CamelEntity> CAMEL;
public static final EntityDefinition<CatEntity> CAT;
public static final EntityDefinition<SpiderEntity> CAVE_SPIDER;
@ -165,6 +170,7 @@ public final class EntityDefinitions {
public static final EntityDefinition<VindicatorEntity> VINDICATOR;
public static final EntityDefinition<AbstractMerchantEntity> WANDERING_TRADER;
public static final EntityDefinition<WardenEntity> WARDEN;
public static final EntityDefinition<AbstractWindChargeEntity> WIND_CHARGE;
public static final EntityDefinition<RaidParticipantEntity> WITCH;
public static final EntityDefinition<WitherEntity> WITHER;
public static final EntityDefinition<AbstractSkeletonEntity> WITHER_SKELETON;
@ -375,6 +381,18 @@ public final class EntityDefinitions {
.heightAndWidth(0.25f)
.build();
EntityFactory<AbstractWindChargeEntity> windChargeSupplier = AbstractWindChargeEntity::new;
BREEZE_WIND_CHARGE = EntityDefinition.inherited(windChargeSupplier, entityBase)
.type(EntityType.BREEZE_WIND_CHARGE)
.identifier("minecraft:breeze_wind_charge_projectile")
.heightAndWidth(0.3125f)
.build();
WIND_CHARGE = EntityDefinition.inherited(windChargeSupplier, entityBase)
.type(EntityType.WIND_CHARGE)
.identifier("minecraft:wind_charge_projectile")
.heightAndWidth(0.3125f)
.build();
EntityDefinition<AbstractArrowEntity> abstractArrowBase = EntityDefinition.inherited(AbstractArrowEntity::new, entityBase)
.addTranslator(MetadataType.BYTE, AbstractArrowEntity::setArrowFlags)
.addTranslator(null) // "Piercing level"
@ -503,11 +521,20 @@ public final class EntityDefinitions {
.height(0.9f).width(0.5f)
.addTranslator(MetadataType.BYTE, BatEntity::setBatFlags)
.build();
BOGGED = EntityDefinition.inherited(BoggedEntity::new, mobEntityBase)
.type(EntityType.BOGGED)
.height(1.99f).width(0.6f)
.addTranslator(MetadataType.BOOLEAN, BoggedEntity::setSheared)
.build();
BLAZE = EntityDefinition.inherited(BlazeEntity::new, mobEntityBase)
.type(EntityType.BLAZE)
.height(1.8f).width(0.6f)
.addTranslator(MetadataType.BYTE, BlazeEntity::setBlazeFlags)
.build();
BREEZE = EntityDefinition.inherited(BreezeEntity::new, mobEntityBase)
.type(EntityType.BREEZE)
.height(1.77f).width(0.6f)
.build();
CREEPER = EntityDefinition.inherited(CreeperEntity::new, mobEntityBase)
.type(EntityType.CREEPER)
.height(1.7f).width(0.6f)

View file

@ -0,0 +1,53 @@
/*
* Copyright (c) 2024 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.entity.type;
import org.cloudburstmc.math.vector.Vector3f;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.session.GeyserSession;
import java.util.UUID;
/**
* Note that, as of 1.21, a wind charge entity does not actually implement the thrown item. We're just reusing
* the "hide until far away" aspect.
*/
public class AbstractWindChargeEntity extends ThrowableItemEntity {
public AbstractWindChargeEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
@Override
public void tick() {
super.tick();
}
@Override
protected float getDrag() {
// Always, even in water. As of 1.21.
return 1f;
}
}

View file

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

View file

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

View file

@ -40,6 +40,7 @@ import org.geysermc.geyser.api.entity.type.GeyserEntity;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.entity.GeyserDirtyMetadata;
import org.geysermc.geyser.entity.properties.GeyserEntityPropertyManager;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.geyser.util.EntityUtils;
@ -137,7 +138,7 @@ public class Entity implements GeyserEntity {
this.valid = false;
this.propertyManager = new GeyserEntityPropertyManager(definition.registeredProperties());
this.propertyManager = definition.registeredProperties() == null ? null : new GeyserEntityPropertyManager(definition.registeredProperties());
setPosition(position);
setAirSupply(getMaxAir());
@ -364,7 +365,7 @@ public class Entity implements GeyserEntity {
return;
}
if (propertyManager.hasProperties()) {
if (propertyManager != null && propertyManager.hasProperties()) {
SetEntityDataPacket entityDataPacket = new SetEntityDataPacket();
entityDataPacket.setRuntimeEntityId(geyserId);
propertyManager.applyIntProperties(entityDataPacket.getProperties().getIntProperties());
@ -557,6 +558,17 @@ public class Entity implements GeyserEntity {
* Should usually mirror {@link #interact(Hand)} without any side effects.
*/
protected InteractiveTag testInteraction(Hand hand) {
if (isAlive() && this instanceof Leashable leashable) {
if (leashable.leashHolderBedrockId() == session.getPlayerEntity().getGeyserId()) {
// Note this might be client side. Has yet to be an issue though, as of Java 1.21.
return InteractiveTag.REMOVE_LEASH;
}
if (session.getPlayerInventory().getItemInHand(hand).asItem() == Items.LEAD && leashable.canBeLeashed()) {
// We shall leash
return InteractiveTag.LEASH;
}
}
return InteractiveTag.NONE;
}
@ -565,6 +577,18 @@ public class Entity implements GeyserEntity {
* to ensure packet parity as well as functionality parity (such as sound effect responses).
*/
public InteractionResult interact(Hand hand) {
if (isAlive() && this instanceof Leashable leashable) {
if (leashable.leashHolderBedrockId() == session.getPlayerEntity().getGeyserId()) {
// Note this might also update client side (a theoretical Geyser/client desync and Java parity issue).
// Has yet to be an issue though, as of Java 1.21.
return InteractionResult.SUCCESS;
}
if (session.getPlayerInventory().getItemInHand(hand).asItem() == Items.LEAD && leashable.canBeLeashed()) {
// We shall leash
return InteractionResult.SUCCESS;
}
}
return InteractionResult.PASS;
}

View file

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

View file

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

View file

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

View file

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

View file

@ -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,22 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.entity.type;
/**
* 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.
* I can haz lead
* (The item, not the mineral)
*/
package org.geysermc.geyser.util.collection;
public interface Leashable {
void setLeashHolderBedrockId(long bedrockId);
long leashHolderBedrockId();
default boolean canBeLeashed() {
return isNotLeashed();
}
default boolean isNotLeashed() {
return leashHolderBedrockId() == -1L;
}
}

View file

@ -30,6 +30,8 @@ import org.cloudburstmc.protocol.bedrock.packet.AddPaintingPacket;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.level.PaintingType;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.Holder;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.PaintingVariant;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ObjectEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.object.Direction;
@ -49,8 +51,14 @@ public class PaintingEntity extends Entity {
// Wait until we get the metadata needed
}
public void setPaintingType(ObjectEntityMetadata<org.geysermc.mcprotocollib.protocol.data.game.entity.type.PaintingType> entityMetadata) {
PaintingType type = PaintingType.getByPaintingType(entityMetadata.getValue());
public void setPaintingType(ObjectEntityMetadata<Holder<PaintingVariant>> entityMetadata) {
if (!entityMetadata.getValue().isId()) {
return;
}
PaintingType type = session.getRegistryCache().paintings().byId(entityMetadata.getValue().id());
if (type == null) {
return;
}
AddPaintingPacket addPaintingPacket = new AddPaintingPacket();
addPaintingPacket.setUniqueEntityId(geyserId);
addPaintingPacket.setRuntimeEntityId(geyserId);
@ -79,7 +87,7 @@ public class PaintingEntity extends Entity {
private Vector3f fixOffset(PaintingType paintingName) {
Vector3f position = super.position;
position = position.add(0.5, 0.5, 0.5);
double widthOffset = paintingName.getWidth() > 1 ? 0.5 : 0;
double widthOffset = paintingName.getWidth() > 1 && paintingName.getWidth() != 3 ? 0.5 : 0;
double heightOffset = paintingName.getHeight() > 1 && paintingName.getHeight() != 3 ? 0.5 : 0;
return switch (direction) {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,73 @@
/*
* Copyright (c) 2024 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.entity.type.living.monster;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InteractiveTag;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import java.util.UUID;
public class BoggedEntity extends AbstractSkeletonEntity {
private boolean sheared = false;
public BoggedEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
public void setSheared(BooleanEntityMetadata entityMetadata) {
this.sheared = entityMetadata.getPrimitiveValue();
setFlag(EntityFlag.SHEARED, this.sheared);
}
@Override
protected @NonNull InteractiveTag testMobInteraction(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
if (itemInHand.asItem() == Items.SHEARS && readyForShearing()) {
return InteractiveTag.SHEAR;
}
return super.testMobInteraction(hand, itemInHand);
}
@Override
protected @NonNull InteractionResult mobInteract(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
if (itemInHand.asItem() == Items.SHEARS && readyForShearing()) {
return InteractionResult.SUCCESS;
}
return super.mobInteract(hand, itemInHand);
}
private boolean readyForShearing() {
return !this.sheared && this.isAlive();
}
}

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2024 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.entity.type.living.monster;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.Pose;
import java.util.UUID;
public class BreezeEntity extends MonsterEntity {
public BreezeEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
@Override
public void setPose(Pose pose) {
// TODO Test
setFlag(EntityFlag.FACING_TARGET_TO_RANGE_ATTACK, pose == Pose.SHOOTING);
setFlag(EntityFlag.JUMP_GOAL_JUMP, pose == Pose.INHALING);
super.setPose(pose);
}
}

View file

@ -58,7 +58,7 @@ public class ZoglinEntity extends MonsterEntity {
}
@Override
protected boolean canBeLeashed() {
public boolean canBeLeashed() {
return isNotLeashed();
}

View file

@ -37,6 +37,7 @@ import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket;
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.level.BedrockDimension;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.AttributeUtils;
@ -63,16 +64,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();
/**
* Determines if our position is currently out-of-sync with the Java server
* due to our workaround for the void floor
@ -147,9 +146,7 @@ public class SessionPlayerEntity extends PlayerEntity {
// TODO: proper fix, BDS somehow does it? https://paste.gg/p/anonymous/3adfb7612f1540be80fa03a2281f93dc (BDS 1.20.13)
if (!this.session.getGameMode().equals(GameMode.SPECTATOR)) {
super.setFlags(entityMetadata);
session.setSwimmingInWater((entityMetadata.getPrimitiveValue() & 0x10) == 0x10 && getFlag(EntityFlag.SPRINTING));
}
refreshSpeed = true;
}
/**
@ -177,7 +174,6 @@ public class SessionPlayerEntity extends PlayerEntity {
public void setPose(Pose pose) {
super.setPose(pose);
session.setPose(pose);
refreshSpeed = true;
}
public float getMaxHealth() {
@ -194,7 +190,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);
@ -226,21 +228,6 @@ public class SessionPlayerEntity extends PlayerEntity {
}
}
@Override
public void updateBedrockMetadata() {
super.updateBedrockMetadata();
if (refreshSpeed) {
AttributeData speedAttribute = session.adjustSpeed();
if (speedAttribute != null) {
UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket();
attributesPacket.setRuntimeEntityId(geyserId);
attributesPacket.setAttributes(Collections.singletonList(speedAttribute));
session.sendUpstreamPacket(attributesPacket);
}
refreshSpeed = false;
}
}
@Override
protected void updateAttribute(Attribute javaAttribute, List<AttributeData> newAttributes) {
if (javaAttribute.getType() == AttributeType.Builtin.GENERIC_ATTACK_SPEED) {
@ -253,17 +240,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;
}
@ -271,7 +247,7 @@ public class SessionPlayerEntity extends PlayerEntity {
public void setLastDeathPosition(@Nullable GlobalPos pos) {
if (pos != null) {
dirtyMetadata.put(EntityDataTypes.PLAYER_LAST_DEATH_POS, pos.getPosition());
dirtyMetadata.put(EntityDataTypes.PLAYER_LAST_DEATH_DIMENSION, DimensionUtils.javaToBedrock(pos.getDimension()));
dirtyMetadata.put(EntityDataTypes.PLAYER_LAST_DEATH_DIMENSION, DimensionUtils.javaToBedrock(pos.getDimension().asString()));
dirtyMetadata.put(EntityDataTypes.PLAYER_HAS_DIED, true);
} else {
dirtyMetadata.put(EntityDataTypes.PLAYER_HAS_DIED, false);

View file

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

View file

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

View file

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

View file

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

View file

@ -25,13 +25,19 @@
package org.geysermc.geyser.inventory;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.inventory.CrafterInventoryTranslator;
import org.geysermc.geyser.translator.inventory.InventoryTranslator;
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
import lombok.Getter;
import lombok.Setter;
import org.geysermc.geyser.GeyserImpl;
import org.jetbrains.annotations.Range;
@Getter
public class CrafterContainer extends Container {
private GeyserItemStack resultItem = GeyserItemStack.EMPTY;
@Setter
private boolean triggered = false;
@ -46,8 +52,36 @@ public class CrafterContainer extends Container {
super(title, id, size, containerType, playerInventory);
}
@Override
public GeyserItemStack getItem(int slot) {
if (slot == CrafterInventoryTranslator.JAVA_RESULT_SLOT) {
return this.resultItem;
} else if (isCraftingGrid(slot)) {
return super.getItem(slot);
} else {
return playerInventory.getItem(slot - CrafterInventoryTranslator.GRID_SIZE + InventoryTranslator.PLAYER_INVENTORY_OFFSET);
}
}
@Override
public int getOffsetForHotbar(@Range(from = 0, to = 8) int slot) {
return playerInventory.getOffsetForHotbar(slot) - InventoryTranslator.PLAYER_INVENTORY_OFFSET + CrafterInventoryTranslator.GRID_SIZE;
}
@Override
public void setItem(int slot, @NonNull GeyserItemStack newItem, GeyserSession session) {
if (slot == CrafterInventoryTranslator.JAVA_RESULT_SLOT) {
// Result item probably won't be an item that needs to worry about net ID or lodestone compasses
this.resultItem = newItem;
} else if (isCraftingGrid(slot)) {
super.setItem(slot, newItem, session);
} else {
playerInventory.setItem(slot - CrafterInventoryTranslator.GRID_SIZE + InventoryTranslator.PLAYER_INVENTORY_OFFSET, newItem, session);
}
}
public void setSlot(int slot, boolean enabled) {
if (slot < 0 || slot > 8) {
if (!isCraftingGrid(slot)) {
GeyserImpl.getInstance().getLogger().warning("Crafter slot out of bounds: " + slot);
return;
}
@ -58,4 +92,8 @@ public class CrafterContainer extends Container {
disabledSlotsMask = (short) (disabledSlotsMask | (1 << slot));
}
}
private static boolean isCraftingGrid(int slot) {
return slot >= 0 && slot <= 8;
}
}

View file

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

View file

@ -25,12 +25,11 @@
package org.geysermc.geyser.inventory;
import lombok.Getter;
import org.cloudburstmc.protocol.bedrock.data.inventory.EnchantData;
import org.cloudburstmc.protocol.bedrock.data.inventory.EnchantOptionData;
import lombok.Getter;
import org.geysermc.geyser.session.GeyserSession;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -44,13 +43,13 @@ public class GeyserEnchantOption {
* is controlled by the server.
* So, of course, we have to throw in some easter eggs. ;)
*/
private static final List<String> ENCHANT_NAMES = Arrays.asList("tougher armor", "lukeeey", "fall better",
"explode less", "camo toy", "breathe better", "rtm five one six", "armor stab", "water walk", "you are elsa",
"tim two zero three", "fast walk nether", "davchoo", "oof ouch owie", "enemy on fire", "spider sad", "aj ferguson", "redned",
"more items thx", "long sword reach", "fast tool", "give me block", "less breaky break", "cube craft",
"strong arrow", "fist arrow", "spicy arrow", "many many arrows", "geyser", "come here fish", "i like this",
"stabby stab", "supreme mortal", "avatar i guess", "more arrows", "fly finder seventeen", "in and out",
"xp heals tools", "dragon proxy waz here");
private static final List<String> ENCHANT_NAMES = List.of("tougher armor", "lukeeey", "fall better",
"explode less", "camo toy", "armor stab", "breathe better", "water walk", "rtm five one six", "oof ouch owie",
"enemy on fire", "spider sad", "aj ferguson", "redned", "more items thx", "fast tool", "give me block",
"less breaky break", "cube craft", "strong arrow", "fist arrow", "spicy arrow", "many many arrows", "geyser",
"come here fish", "you are elsa", "xp heals tools", "tim two zero three", "dragon proxy waz here",
"stabby stab", "supreme mortal", "i like this", "avatar i guess", "more arrows", "in and out",
"fly finder seventeen", "fast walk nether", "davchoo", "onechris", "death bringer thirteen", "kastle");
@Getter
private final int javaIndex;
@ -62,7 +61,6 @@ public class GeyserEnchantOption {
private boolean hasChanged;
private int xpCost = 0;
private int javaEnchantIndex = -1;
private int bedrockEnchantIndex = -1;
private int enchantLevel = -1;
@ -74,7 +72,7 @@ public class GeyserEnchantOption {
this.hasChanged = false;
return new EnchantOptionData(xpCost, javaIndex + 16, EMPTY,
enchantLevel == -1 ? EMPTY : Collections.singletonList(new EnchantData(bedrockEnchantIndex, enchantLevel)), EMPTY,
javaEnchantIndex == -1 ? "unknown" : ENCHANT_NAMES.get(javaEnchantIndex), enchantLevel == -1 ? 0 : session.getNextItemNetId());
bedrockEnchantIndex == -1 ? "unknown" : ENCHANT_NAMES.get(bedrockEnchantIndex), enchantLevel == -1 ? 0 : session.getNextItemNetId());
}
public boolean hasChanged() {
@ -88,10 +86,9 @@ public class GeyserEnchantOption {
}
}
public void setEnchantIndex(int javaEnchantIndex, int bedrockEnchantIndex) {
if (this.javaEnchantIndex != javaEnchantIndex) {
public void setEnchantIndex(int bedrockEnchantIndex) {
if (this.bedrockEnchantIndex != bedrockEnchantIndex) {
hasChanged = true;
this.javaEnchantIndex = javaEnchantIndex;
this.bedrockEnchantIndex = bedrockEnchantIndex;
}
}

View file

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

View file

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

View file

@ -26,7 +26,9 @@
package org.geysermc.geyser.inventory.item;
import lombok.Getter;
import net.kyori.adventure.key.Key;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.util.MinecraftKey;
import java.util.Locale;
@ -72,21 +74,23 @@ public enum BannerPattern {
SKULL("sku"),
FLOWER("flo"),
MOJANG("moj"),
PIGLIN("pig");
PIGLIN("pig"),
FLOW("flw"),
GUSTER("gus");
private static final BannerPattern[] VALUES = values();
private final String javaIdentifier;
private final Key javaIdentifier;
private final String bedrockIdentifier;
BannerPattern(String bedrockIdentifier) {
this.javaIdentifier = "minecraft:" + this.name().toLowerCase(Locale.ROOT);
this.javaIdentifier = MinecraftKey.key(this.name().toLowerCase(Locale.ROOT));
this.bedrockIdentifier = bedrockIdentifier;
}
public static @Nullable BannerPattern getByJavaIdentifier(String javaIdentifier) {
public static @Nullable BannerPattern getByJavaIdentifier(Key key) {
for (BannerPattern bannerPattern : VALUES) {
if (bannerPattern.javaIdentifier.equals(javaIdentifier)) {
if (bannerPattern.javaIdentifier.equals(key)) {
return bannerPattern;
}
}

View file

@ -25,13 +25,11 @@
package org.geysermc.geyser.inventory.item;
import lombok.Getter;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Locale;
@Getter
public enum Enchantment {
public enum BedrockEnchantment {
PROTECTION,
FIRE_PROTECTION,
FEATHER_FALLING,
@ -69,18 +67,21 @@ public enum Enchantment {
PIERCING,
QUICK_CHARGE,
SOUL_SPEED,
SWIFT_SNEAK;
SWIFT_SNEAK,
WIND_BURST,
DENSITY,
BREACH;
private static final Enchantment[] VALUES = values();
private static final BedrockEnchantment[] VALUES = values();
private final String javaIdentifier;
Enchantment() {
BedrockEnchantment() {
this.javaIdentifier = "minecraft:" + this.name().toLowerCase(Locale.ENGLISH);
}
public static @Nullable Enchantment getByJavaIdentifier(String javaIdentifier) {
for (Enchantment enchantment : VALUES) {
public static @Nullable BedrockEnchantment getByJavaIdentifier(String javaIdentifier) {
for (BedrockEnchantment enchantment : VALUES) {
if (enchantment.javaIdentifier.equals(javaIdentifier) || enchantment.name().toLowerCase(Locale.ENGLISH).equalsIgnoreCase(javaIdentifier)) {
return enchantment;
}
@ -88,88 +89,10 @@ public enum Enchantment {
return null;
}
public static @Nullable Enchantment getByBedrockId(int bedrockId) {
public static @Nullable BedrockEnchantment getByBedrockId(int bedrockId) {
if (bedrockId >= 0 && bedrockId < VALUES.length) {
return VALUES[bedrockId];
}
return null;
}
/**
* Enchantments classified by their Java index
*/
public enum JavaEnchantment {
PROTECTION,
FIRE_PROTECTION,
FEATHER_FALLING,
BLAST_PROTECTION,
PROJECTILE_PROTECTION,
RESPIRATION,
AQUA_AFFINITY,
THORNS,
DEPTH_STRIDER,
FROST_WALKER,
BINDING_CURSE,
SOUL_SPEED,
SWIFT_SNEAK,
SHARPNESS,
SMITE,
BANE_OF_ARTHROPODS,
KNOCKBACK,
FIRE_ASPECT,
LOOTING,
SWEEPING_EDGE,
EFFICIENCY,
SILK_TOUCH,
UNBREAKING,
FORTUNE,
POWER,
PUNCH,
FLAME,
INFINITY,
LUCK_OF_THE_SEA,
LURE,
LOYALTY,
IMPALING,
RIPTIDE,
CHANNELING,
MULTISHOT,
QUICK_CHARGE,
PIERCING,
DENSITY,
BREACH,
WIND_BURST,
MENDING,
VANISHING_CURSE;
private static final JavaEnchantment[] VALUES = JavaEnchantment.values();
public static JavaEnchantment of(int index) {
return VALUES[index];
}
/**
* A list of all enchantment Java identifiers for use with command suggestions.
*/
public static final String[] ALL_JAVA_IDENTIFIERS;
public static @Nullable JavaEnchantment getByJavaIdentifier(String javaIdentifier) {
if (!javaIdentifier.startsWith("minecraft:")) {
javaIdentifier = "minecraft:" + javaIdentifier;
}
for (int i = 0; i < ALL_JAVA_IDENTIFIERS.length; i++) {
if (ALL_JAVA_IDENTIFIERS[i].equalsIgnoreCase(javaIdentifier)) {
return VALUES[i];
}
}
return null;
}
static {
ALL_JAVA_IDENTIFIERS = new String[VALUES.length];
for (int i = 0; i < ALL_JAVA_IDENTIFIERS.length; i++) {
ALL_JAVA_IDENTIFIERS[i] = "minecraft:" + VALUES[i].name().toLowerCase(Locale.ENGLISH);
}
}
}
}

View file

@ -34,63 +34,83 @@ import java.util.Locale;
@Getter
public enum Potion {
WATER(0),
MUNDANE(1),
THICK(3),
AWKWARD(4),
NIGHT_VISION(5),
LONG_NIGHT_VISION(6),
INVISIBILITY(7),
LONG_INVISIBILITY(8),
LEAPING(9),
LONG_LEAPING(10),
STRONG_LEAPING(11),
FIRE_RESISTANCE(12),
LONG_FIRE_RESISTANCE(13),
SWIFTNESS(14),
LONG_SWIFTNESS(15),
STRONG_SWIFTNESS(16),
SLOWNESS(17),
LONG_SLOWNESS(18),
STRONG_SLOWNESS(42),
TURTLE_MASTER(37),
LONG_TURTLE_MASTER(38),
STRONG_TURTLE_MASTER(39),
WATER_BREATHING(19),
LONG_WATER_BREATHING(20),
HEALING(21),
STRONG_HEALING(22),
HARMING(23),
STRONG_HARMING(24),
POISON(25),
LONG_POISON(26),
STRONG_POISON(27),
REGENERATION(28),
LONG_REGENERATION(29),
STRONG_REGENERATION(30),
STRENGTH(31),
LONG_STRENGTH(32),
STRONG_STRENGTH(33),
WEAKNESS(34),
LONG_WEAKNESS(35),
LUCK(2), //does not exist
SLOW_FALLING(40),
LONG_SLOW_FALLING(41);
WATER(0, ArrowParticleColors.NONE),
MUNDANE(1, ArrowParticleColors.NONE), // 2 is extended?
THICK(3, ArrowParticleColors.NONE),
AWKWARD(4, ArrowParticleColors.NONE),
NIGHT_VISION(5, ArrowParticleColors.NIGHT_VISION),
LONG_NIGHT_VISION(6, ArrowParticleColors.NIGHT_VISION),
INVISIBILITY(7, ArrowParticleColors.INVISIBILITY),
LONG_INVISIBILITY(8, ArrowParticleColors.INVISIBILITY),
LEAPING(9, ArrowParticleColors.LEAPING),
LONG_LEAPING(10, ArrowParticleColors.LEAPING),
STRONG_LEAPING(11, ArrowParticleColors.LEAPING),
FIRE_RESISTANCE(12, ArrowParticleColors.FIRE_RESISTANCE),
LONG_FIRE_RESISTANCE(13, ArrowParticleColors.FIRE_RESISTANCE),
SWIFTNESS(14, ArrowParticleColors.SWIFTNESS),
LONG_SWIFTNESS(15, ArrowParticleColors.SWIFTNESS),
STRONG_SWIFTNESS(16, ArrowParticleColors.SWIFTNESS),
SLOWNESS(17, ArrowParticleColors.SLOWNESS),
LONG_SLOWNESS(18, ArrowParticleColors.SLOWNESS),
STRONG_SLOWNESS(42, ArrowParticleColors.SLOWNESS),
TURTLE_MASTER(37, ArrowParticleColors.TURTLE_MASTER),
LONG_TURTLE_MASTER(38, ArrowParticleColors.TURTLE_MASTER),
STRONG_TURTLE_MASTER(39, ArrowParticleColors.TURTLE_MASTER),
WATER_BREATHING(19, ArrowParticleColors.WATER_BREATHING),
LONG_WATER_BREATHING(20, ArrowParticleColors.WATER_BREATHING),
HEALING(21, ArrowParticleColors.HEALING),
STRONG_HEALING(22, ArrowParticleColors.HEALING),
HARMING(23, ArrowParticleColors.HARMING),
STRONG_HARMING(24, ArrowParticleColors.HARMING),
POISON(25, ArrowParticleColors.POISON),
LONG_POISON(26, ArrowParticleColors.POISON),
STRONG_POISON(27, ArrowParticleColors.POISON),
REGENERATION(28, ArrowParticleColors.REGENERATION),
LONG_REGENERATION(29, ArrowParticleColors.REGENERATION),
STRONG_REGENERATION(30, ArrowParticleColors.REGENERATION),
STRENGTH(31, ArrowParticleColors.STRENGTH),
LONG_STRENGTH(32, ArrowParticleColors.STRENGTH),
STRONG_STRENGTH(33, ArrowParticleColors.STRENGTH),
WEAKNESS(34, ArrowParticleColors.WEAKNESS),
LONG_WEAKNESS(35, ArrowParticleColors.WEAKNESS),
LUCK(2, ArrowParticleColors.NONE), // does not exist in Bedrock
SLOW_FALLING(40, ArrowParticleColors.SLOW_FALLING),
LONG_SLOW_FALLING(41, ArrowParticleColors.SLOW_FALLING),
WIND_CHARGING(43, ArrowParticleColors.WIND_CHARGING),
WEAVING(44, ArrowParticleColors.WEAVING),
OOZING(45, ArrowParticleColors.OOZING),
INFESTATION(46, ArrowParticleColors.INFESTATION);
public static final Potion[] VALUES = values();
private final String javaIdentifier;
private final short bedrockId;
private final int javaColor;
Potion(int bedrockId) {
Potion(int bedrockId, int javaColor) {
this.javaIdentifier = "minecraft:" + this.name().toLowerCase(Locale.ENGLISH);
this.bedrockId = (short) bedrockId;
this.javaColor = javaColor;
}
public int tippedArrowId() {
// +1 likely to offset 0 as nothing?
return this.bedrockId + 1;
}
public PotionContents toComponent() {
return new PotionContents(this.ordinal(), -1, Collections.emptyList());
}
public static Potion getByJavaIdentifier(String javaIdentifier) {
for (Potion potion : VALUES) {
if (potion.javaIdentifier.equals(javaIdentifier)) {
return potion;
}
}
return null;
}
public static @Nullable Potion getByJavaId(int javaId) {
if (javaId >= 0 && javaId < VALUES.length) {
return VALUES[javaId];
@ -106,4 +126,44 @@ public enum Potion {
}
return null;
}
public static @Nullable Potion getByTippedArrowDamage(int bedrockId) {
return getByBedrockId(bedrockId - 1);
}
public static byte toTippedArrowId(int javaParticleColor) {
for (Potion potion : VALUES) {
if (potion.javaColor == javaParticleColor) {
return (byte) (potion.bedrockId + 1);
}
}
return (byte) 0;
}
/**
* For tipped arrow usage
*/
private static final class ArrowParticleColors {
static final int NONE = 1;
static final int NIGHT_VISION = 2039713;
static final int INVISIBILITY = 8356754;
static final int LEAPING = 2293580;
static final int FIRE_RESISTANCE = 14981690;
static final int SWIFTNESS = 8171462;
static final int SLOWNESS = 5926017;
static final int TURTLE_MASTER = 7691106;
static final int WATER_BREATHING = 3035801;
static final int HEALING = 16262179;
static final int HARMING = 4393481;
static final int POISON = 5149489;
static final int REGENERATION = 13458603;
static final int STRENGTH = 9643043;
static final int WEAKNESS = 4738376;
static final int LUCK = 3381504;
static final int SLOW_FALLING = 16773073;
static final int WIND_CHARGING = 12438015;
static final int WEAVING = 7891290;
static final int OOZING = 10092451;
static final int INFESTATION = 9214860;
}
}

View file

@ -1,153 +0,0 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.inventory.item;
import lombok.Getter;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Locale;
/**
* Potion identifiers and their respective Bedrock IDs used with arrows.
* <a href="https://minecraft.wiki/w/Arrow#Data_values">See here</a>
*/
@Getter
public enum TippedArrowPotion {
WATER(-1, ArrowParticleColors.NONE), // Guessing this based off of the Potion enum. TODO merge?
MUNDANE(2, ArrowParticleColors.NONE), // 3 is extended?
THICK(4, ArrowParticleColors.NONE),
AWKWARD(5, ArrowParticleColors.NONE),
NIGHT_VISION(6, ArrowParticleColors.NIGHT_VISION),
LONG_NIGHT_VISION(7, ArrowParticleColors.NIGHT_VISION),
INVISIBILITY(8, ArrowParticleColors.INVISIBILITY),
LONG_INVISIBILITY(9, ArrowParticleColors.INVISIBILITY),
LEAPING(10, ArrowParticleColors.LEAPING),
LONG_LEAPING(11, ArrowParticleColors.LEAPING),
STRONG_LEAPING(12, ArrowParticleColors.LEAPING),
FIRE_RESISTANCE(13, ArrowParticleColors.FIRE_RESISTANCE),
LONG_FIRE_RESISTANCE(14, ArrowParticleColors.FIRE_RESISTANCE),
SWIFTNESS(15, ArrowParticleColors.SWIFTNESS),
LONG_SWIFTNESS(16, ArrowParticleColors.SWIFTNESS),
STRONG_SWIFTNESS(17, ArrowParticleColors.SWIFTNESS),
SLOWNESS(18, ArrowParticleColors.SLOWNESS),
LONG_SLOWNESS(19, ArrowParticleColors.SLOWNESS),
STRONG_SLOWNESS(43, ArrowParticleColors.SLOWNESS),
WATER_BREATHING(20, ArrowParticleColors.WATER_BREATHING),
LONG_WATER_BREATHING(21, ArrowParticleColors.WATER_BREATHING),
HEALING(22, ArrowParticleColors.HEALING),
STRONG_HEALING(23, ArrowParticleColors.HEALING),
HARMING(24, ArrowParticleColors.HARMING),
STRONG_HARMING(25, ArrowParticleColors.HARMING),
POISON(26, ArrowParticleColors.POISON),
LONG_POISON(27, ArrowParticleColors.POISON),
STRONG_POISON(28, ArrowParticleColors.POISON),
REGENERATION(29, ArrowParticleColors.REGENERATION),
LONG_REGENERATION(30, ArrowParticleColors.REGENERATION),
STRONG_REGENERATION(31, ArrowParticleColors.REGENERATION),
STRENGTH(32, ArrowParticleColors.STRENGTH),
LONG_STRENGTH(33, ArrowParticleColors.STRENGTH),
STRONG_STRENGTH(34, ArrowParticleColors.STRENGTH),
WEAKNESS(35, ArrowParticleColors.WEAKNESS),
LONG_WEAKNESS(36, ArrowParticleColors.WEAKNESS),
LUCK(2, ArrowParticleColors.NONE), // does not exist in Bedrock
TURTLE_MASTER(38, ArrowParticleColors.TURTLE_MASTER),
LONG_TURTLE_MASTER(39, ArrowParticleColors.TURTLE_MASTER),
STRONG_TURTLE_MASTER(40, ArrowParticleColors.TURTLE_MASTER),
SLOW_FALLING(41, ArrowParticleColors.SLOW_FALLING),
LONG_SLOW_FALLING(42, ArrowParticleColors.SLOW_FALLING);
private static final TippedArrowPotion[] VALUES = values();
private final String javaIdentifier;
private final short bedrockId;
/**
* The Java color associated with this ID.
* Used for looking up Java arrow color entity metadata as Bedrock potion IDs, which is what is used for entities in Bedrock
*/
private final int javaColor;
TippedArrowPotion(int bedrockId, ArrowParticleColors arrowParticleColor) {
this.javaIdentifier = "minecraft:" + this.name().toLowerCase(Locale.ENGLISH);
this.bedrockId = (short) bedrockId;
this.javaColor = arrowParticleColor.getColor();
}
public static @Nullable TippedArrowPotion of(int id) {
if (id >= 0 && id < VALUES.length) {
return VALUES[id];
}
return null;
}
public static @Nullable TippedArrowPotion getByBedrockId(int bedrockId) {
for (TippedArrowPotion potion : VALUES) {
if (potion.bedrockId == bedrockId) {
return potion;
}
}
return null;
}
/**
* @param color the potion color to look up
* @return the tipped arrow potion that most closely resembles that color.
*/
public static @Nullable TippedArrowPotion getByJavaColor(int color) {
for (TippedArrowPotion potion : VALUES) {
if (potion.javaColor == color) {
return potion;
}
}
return null;
}
private enum ArrowParticleColors {
NONE(-1),
NIGHT_VISION(2039713),
INVISIBILITY(8356754),
LEAPING(2293580),
FIRE_RESISTANCE(14981690),
SWIFTNESS(8171462),
SLOWNESS(5926017),
TURTLE_MASTER(7691106),
WATER_BREATHING(3035801),
HEALING(16262179),
HARMING(4393481),
POISON(5149489),
REGENERATION(13458603),
STRENGTH(9643043),
WEAKNESS(4738376),
LUCK(3381504),
SLOW_FALLING(16773073);
@Getter
private final int color;
ArrowParticleColors(int color) {
this.color = color;
}
}
}

View file

@ -25,6 +25,9 @@
package org.geysermc.geyser.inventory.recipe;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
/**
* A more compact version of {@link org.geysermc.mcprotocollib.protocol.data.game.recipe.Recipe}.
*/
@ -33,4 +36,7 @@ public interface GeyserRecipe {
* Whether the recipe is flexible or not in which items can be placed where.
*/
boolean isShaped();
@Nullable
ItemStack result();
}

View file

@ -47,7 +47,7 @@ public final class TrimRecipe {
public static final ItemDescriptorWithCount TEMPLATE = tagDescriptor("minecraft:trim_templates");
public static TrimMaterial readTrimMaterial(GeyserSession session, RegistryEntry entry) {
String key = stripMinecraftNamespace(entry.getId());
String key = entry.getId().asMinimalString();
// Color is used when hovering over the item
// Find the nearest legacy color from the RGB Java gives us to work with
@ -67,7 +67,7 @@ public final class TrimRecipe {
}
public static TrimPattern readTrimPattern(GeyserSession session, RegistryEntry entry) {
String key = stripMinecraftNamespace(entry.getId());
String key = entry.getId().asMinimalString();
String itemIdentifier = entry.getData().getString("template_item");
ItemMapping itemMapping = session.getItemMappings().getMapping(itemIdentifier);
@ -78,19 +78,6 @@ public final class TrimRecipe {
return new TrimPattern(itemMapping.getBedrockIdentifier(), key);
}
// TODO find a good place for a stripNamespace util method
private static String stripMinecraftNamespace(String identifier) {
int i = identifier.indexOf(':');
if (i >= 0) {
String namespace = identifier.substring(0, i);
// Only strip minecraft namespace
if (namespace.equals("minecraft")) {
return identifier.substring(i + 1);
}
}
return identifier;
}
private TrimRecipe() {
//no-op
}

View file

@ -26,7 +26,6 @@
package org.geysermc.geyser.inventory.updater;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntMaps;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import net.kyori.adventure.text.Component;
import org.cloudburstmc.nbt.NbtMap;
@ -38,10 +37,9 @@ import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.inventory.AnvilContainer;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.inventory.Inventory;
import org.geysermc.geyser.inventory.item.Enchantment.JavaEnchantment;
import org.geysermc.geyser.inventory.item.BedrockEnchantment;
import org.geysermc.geyser.item.enchantment.Enchantment;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.registry.type.EnchantmentData;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.inventory.InventoryTranslator;
import org.geysermc.geyser.translator.text.MessageTranslator;
@ -307,22 +305,22 @@ public class AnvilInventoryUpdater extends InventoryUpdater {
*/
private int calcMergeEnchantmentCost(GeyserSession session, GeyserItemStack input, GeyserItemStack material, boolean bedrock) {
boolean hasCompatible = false;
Object2IntMap<JavaEnchantment> combinedEnchantments = getEnchantments(input);
Object2IntMap<Enchantment> combinedEnchantments = getEnchantments(session, input);
int cost = 0;
for (Object2IntMap.Entry<JavaEnchantment> entry : getEnchantments(material).object2IntEntrySet()) {
JavaEnchantment enchantment = entry.getKey();
EnchantmentData data = Registries.ENCHANTMENTS.get(enchantment);
if (data == null) {
GeyserImpl.getInstance().getLogger().debug("Java enchantment not in registry: " + enchantment);
continue;
}
for (Object2IntMap.Entry<Enchantment> entry : getEnchantments(session, material).object2IntEntrySet()) {
Enchantment enchantment = entry.getKey();
boolean canApply = isEnchantedBook(input) || data.validItems().contains(input.getJavaId());
for (JavaEnchantment incompatible : data.incompatibleEnchantments()) {
if (combinedEnchantments.containsKey(incompatible)) {
canApply = false;
if (!bedrock) {
cost++;
boolean canApply = isEnchantedBook(input) || session.getTagCache().is(enchantment.supportedItems(), input);
var exclusiveSet = enchantment.exclusiveSet();
if (exclusiveSet != null) {
int[] incompatibleEnchantments = session.getTagCache().get(exclusiveSet);
for (int i : incompatibleEnchantments) {
Enchantment incompatible = session.getRegistryCache().enchantments().byId(i);
if (combinedEnchantments.containsKey(incompatible)) {
canApply = false;
if (!bedrock) {
cost++;
}
}
}
}
@ -334,12 +332,12 @@ public class AnvilInventoryUpdater extends InventoryUpdater {
newLevel++;
}
newLevel = Math.max(currentLevel, newLevel);
if (newLevel > data.maxLevel()) {
newLevel = data.maxLevel();
if (newLevel > enchantment.maxLevel()) {
newLevel = enchantment.maxLevel();
}
combinedEnchantments.put(enchantment, newLevel);
int rarityMultiplier = data.rarityMultiplier();
int rarityMultiplier = enchantment.anvilCost();
if (isEnchantedBook(material) && rarityMultiplier > 1) {
rarityMultiplier /= 2;
}
@ -347,11 +345,11 @@ public class AnvilInventoryUpdater extends InventoryUpdater {
if (newLevel > currentLevel) {
hasCompatible = true;
}
if (enchantment == JavaEnchantment.IMPALING) {
if (enchantment.bedrockEnchantment() == BedrockEnchantment.IMPALING) {
// Multiplier is halved on Bedrock for some reason
rarityMultiplier /= 2;
} else if (enchantment == JavaEnchantment.SWEEPING_EDGE) {
// Doesn't exist on Bedrock
} else if (enchantment.bedrockEnchantment() == null) {
// Whatever this is, doesn't exist on Bedrock
rarityMultiplier = 0;
}
cost += rarityMultiplier * (newLevel - currentLevel);
@ -368,7 +366,7 @@ public class AnvilInventoryUpdater extends InventoryUpdater {
return cost;
}
private Object2IntMap<JavaEnchantment> getEnchantments(GeyserItemStack itemStack) {
private Object2IntMap<Enchantment> getEnchantments(GeyserSession session, GeyserItemStack itemStack) {
ItemEnchantments enchantmentComponent;
if (isEnchantedBook(itemStack)) {
enchantmentComponent = itemStack.getComponent(DataComponentType.STORED_ENCHANTMENTS);
@ -376,9 +374,9 @@ public class AnvilInventoryUpdater extends InventoryUpdater {
enchantmentComponent = itemStack.getComponent(DataComponentType.ENCHANTMENTS);
}
if (enchantmentComponent != null) {
Object2IntMap<JavaEnchantment> enchantments = new Object2IntOpenHashMap<>();
Object2IntMap<Enchantment> enchantments = new Object2IntOpenHashMap<>();
for (Map.Entry<Integer, Integer> entry : enchantmentComponent.getEnchantments().entrySet()) {
JavaEnchantment enchantment = JavaEnchantment.of(entry.getKey());
Enchantment enchantment = session.getRegistryCache().enchantments().byId(entry.getKey());
if (enchantment == null) {
GeyserImpl.getInstance().getLogger().debug("Unknown Java enchantment in anvil: " + entry.getKey());
continue;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,80 @@
/*
* 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.item.enchantment;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.nbt.NbtMap;
import org.geysermc.geyser.inventory.item.BedrockEnchantment;
import org.geysermc.geyser.session.cache.tags.EnchantmentTag;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.geyser.util.MinecraftKey;
import org.geysermc.mcprotocollib.protocol.data.game.RegistryEntry;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* @param description only populated if {@link #bedrockEnchantment()} is not null.
* @param anvilCost also as a rarity multiplier
*/
public record Enchantment(String identifier,
Set<EnchantmentComponent> effects,
ItemTag supportedItems,
int maxLevel,
String description,
int anvilCost,
@Nullable EnchantmentTag exclusiveSet,
@Nullable BedrockEnchantment bedrockEnchantment) {
// Implementation note: I have a feeling the tags can be a list of items, because in vanilla they're HolderSet classes.
// I'm not sure how that's wired over the network, so we'll put it off.
public static Enchantment read(RegistryEntry entry) {
NbtMap data = entry.getData();
Set<EnchantmentComponent> effects = readEnchantmentComponents(data.getCompound("effects"));
String supportedItems = data.getString("supported_items").substring(1); // Remove '#' at beginning that indicates tag
int maxLevel = data.getInt("max_level");
int anvilCost = data.getInt("anvil_cost");
String exclusiveSet = data.getString("exclusive_set", null);
EnchantmentTag exclusiveSetTag = exclusiveSet == null ? null : EnchantmentTag.ALL_ENCHANTMENT_TAGS.get(MinecraftKey.key(exclusiveSet.substring(1)));
BedrockEnchantment bedrockEnchantment = BedrockEnchantment.getByJavaIdentifier(entry.getId().asString());
String description = bedrockEnchantment == null ? MessageTranslator.deserializeDescription(data) : null;
return new Enchantment(entry.getId().asString(), effects, ItemTag.ALL_ITEM_TAGS.get(MinecraftKey.key(supportedItems)), maxLevel,
description, anvilCost, exclusiveSetTag, bedrockEnchantment);
}
private static Set<EnchantmentComponent> readEnchantmentComponents(NbtMap effects) {
Set<EnchantmentComponent> components = new HashSet<>();
for (Map.Entry<String, Object> entry : effects.entrySet()) {
switch (entry.getKey()) {
case "minecraft:prevent_armor_change" -> components.add(EnchantmentComponent.PREVENT_ARMOR_CHANGE);
}
}
return Set.copyOf(components); // Also ensures any empty sets are consolidated
}
}

View file

@ -0,0 +1,33 @@
/*
* 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.item.enchantment;
public class EnchantmentComponent {
/**
* Singleton with no additional data
*/
public static final EnchantmentComponent PREVENT_ARMOR_CHANGE = new EnchantmentComponent();
}

View file

@ -28,15 +28,13 @@ package org.geysermc.geyser.item.type;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.inventory.item.TippedArrowPotion;
import org.geysermc.geyser.inventory.item.Potion;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.registry.type.ItemMappings;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.PotionContents;
import java.util.Collections;
public class ArrowItem extends Item {
public ArrowItem(String javaIdentifier, Builder builder) {
super(javaIdentifier, builder);
@ -44,13 +42,18 @@ public class ArrowItem extends Item {
@Override
public @NonNull GeyserItemStack translateToJava(@NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) {
TippedArrowPotion tippedArrowPotion = TippedArrowPotion.getByBedrockId(itemData.getDamage());
Potion potion = Potion.getByTippedArrowDamage(itemData.getDamage());
GeyserItemStack itemStack = super.translateToJava(itemData, mapping, mappings);
if (tippedArrowPotion != null) {
if (potion != null) {
itemStack = Items.TIPPED_ARROW.newItemStack(itemStack.getAmount(), itemStack.getComponents());
PotionContents contents = new PotionContents(tippedArrowPotion.ordinal(), -1, Collections.emptyList());
PotionContents contents = potion.toComponent();
itemStack.getOrCreateComponents().put(DataComponentType.POTION_CONTENTS, contents);
}
return itemStack;
}
@Override
public boolean ignoreDamage() {
return true;
}
}

View file

@ -26,6 +26,7 @@
package org.geysermc.geyser.item.type;
import it.unimi.dsi.fastutil.Pair;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextColor;
@ -35,12 +36,13 @@ 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;
import org.geysermc.geyser.translator.item.BedrockItemBuilder;
import org.geysermc.geyser.util.MinecraftKey;
import org.geysermc.mcprotocollib.protocol.data.game.Holder;
import org.geysermc.mcprotocollib.protocol.data.game.Identifier;
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.item.component.DataComponents;
@ -59,9 +61,6 @@ public class BannerItem extends BlockItem {
*/
private static final List<Pair<BannerPattern, DyeColor>> OMINOUS_BANNER_PATTERN;
// TODO fix - we somehow need to be able to get the sessions banner pattern registry, which we don't have where we need this :/
private static final int[] ominousBannerPattern = new int[] { 21, 29, 30, 1, 34, 15, 3, 1 };
static {
// Construct what an ominous banner is supposed to look like
OMINOUS_BANNER_PATTERN = List.of(
@ -108,7 +107,7 @@ public class BannerItem extends BlockItem {
if (color != pair.right()) {
return false;
}
String id = Identifier.formalize(patternLayer.getString("pattern")); // Ouch
Key id = MinecraftKey.key(patternLayer.getString("pattern")); // Ouch
BannerPattern bannerPattern = BannerPattern.getByJavaIdentifier(id);
if (bannerPattern != pair.left()) {
return false;
@ -145,8 +144,8 @@ public class BannerItem extends BlockItem {
} else {
List<NbtMap> patternList = new ArrayList<>(patterns.size());
for (BannerPatternLayer patternLayer : patterns) {
patternLayer.getPattern().ifId(holder -> {
BannerPattern bannerPattern = session.getRegistryCache().bannerPatterns().byId(holder.id());
patternLayer.getPattern().ifId(id -> {
BannerPattern bannerPattern = session.getRegistryCache().bannerPatterns().byId(id);
if (bannerPattern != null) {
NbtMap tag = NbtMap.builder()
.putString("Pattern", bannerPattern.getBedrockIdentifier())
@ -168,7 +167,7 @@ public class BannerItem extends BlockItem {
*/
private static NbtMap getBedrockBannerPattern(NbtMap pattern) {
// ViaVersion 1.20.4 -> 1.20.5 can send without the namespace
BannerPattern bannerPattern = BannerPattern.getByJavaIdentifier(Identifier.formalize(pattern.getString("pattern")));
BannerPattern bannerPattern = BannerPattern.getByJavaIdentifier(MinecraftKey.key(pattern.getString("pattern")));
DyeColor dyeColor = DyeColor.getByJavaIdentifier(pattern.getString("color"));
if (bannerPattern == null || dyeColor == null) {
return null;
@ -199,8 +198,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
@ -214,20 +213,22 @@ public class BannerItem extends BlockItem {
}
@Override
public void translateNbtToJava(@NonNull NbtMap bedrockTag, @NonNull DataComponents components, @NonNull ItemMapping mapping) {
super.translateNbtToJava(bedrockTag, components, mapping);
public void translateNbtToJava(@NonNull GeyserSession session, @NonNull NbtMap bedrockTag, @NonNull DataComponents components, @NonNull ItemMapping mapping) {
super.translateNbtToJava(session, bedrockTag, components, mapping);
if (bedrockTag.getInt("Type") == 1) {
// Ominous banner pattern
List<BannerPatternLayer> patternLayers = new ArrayList<>();
for (int i = 0; i < ominousBannerPattern.length; i++) {
patternLayers.add(new BannerPatternLayer(Holder.ofId(ominousBannerPattern[i]), OMINOUS_BANNER_PATTERN.get(i).right().ordinal()));
for (int i = 0; i < OMINOUS_BANNER_PATTERN.size(); i++) {
var pair = OMINOUS_BANNER_PATTERN.get(i);
patternLayers.add(new BannerPatternLayer(Holder.ofId(session.getRegistryCache().bannerPatterns().byValue(pair.left())),
pair.right().ordinal()));
}
components.put(DataComponentType.BANNER_PATTERNS, patternLayers);
components.put(DataComponentType.HIDE_ADDITIONAL_TOOLTIP, Unit.INSTANCE);
components.put(DataComponentType.ITEM_NAME, Component
.translatable("block.minecraft.ominous_banner") // thank god this works
.translatable("block.minecraft.ominous_banner")
.style(Style.style(TextColor.color(16755200)))
);
}

View file

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

View file

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

View file

@ -29,9 +29,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.item.ArmorMaterial;
import org.geysermc.geyser.session.GeyserSession;
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;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DyedItemColor;
public class DyeableArmorItem extends ArmorItem {
public DyeableArmorItem(String javaIdentifier, ArmorMaterial material, Builder builder) {
@ -44,9 +42,6 @@ public class DyeableArmorItem extends ArmorItem {
// Note that this is handled as of 1.20.5 in the ItemColors class.
// But horse leather armor and body leather armor are now both armor items. So it works!
DyedItemColor dyedItemColor = components.get(DataComponentType.DYED_COLOR);
if (dyedItemColor != null) {
builder.putInt("customColor", dyedItemColor.getRgb());
}
translateDyedColor(components, builder);
}
}

View file

@ -31,7 +31,8 @@ import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtType;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.inventory.item.Enchantment;
import org.geysermc.geyser.inventory.item.BedrockEnchantment;
import org.geysermc.geyser.item.enchantment.Enchantment;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.item.BedrockItemBuilder;
@ -69,8 +70,8 @@ public class EnchantedBookItem extends Item {
}
@Override
public void translateNbtToJava(@NonNull NbtMap bedrockTag, @NonNull DataComponents components, @NonNull ItemMapping mapping) {
super.translateNbtToJava(bedrockTag, components, mapping);
public void translateNbtToJava(@NonNull GeyserSession session, @NonNull NbtMap bedrockTag, @NonNull DataComponents components, @NonNull ItemMapping mapping) {
super.translateNbtToJava(session, bedrockTag, components, mapping);
List<NbtMap> enchantmentTag = bedrockTag.getList("ench", NbtType.COMPOUND);
if (enchantmentTag != null) {
@ -78,11 +79,16 @@ public class EnchantedBookItem extends Item {
for (NbtMap bedrockEnchantment : enchantmentTag) {
short bedrockId = bedrockEnchantment.getShort("id");
Enchantment enchantment = Enchantment.getByBedrockId(bedrockId);
BedrockEnchantment enchantment = BedrockEnchantment.getByBedrockId(bedrockId);
if (enchantment != null) {
int level = bedrockEnchantment.getShort("lvl", (short) 1);
// TODO
javaEnchantments.put(Enchantment.JavaEnchantment.valueOf(enchantment.name()).ordinal(), level);
List<Enchantment> enchantments = session.getRegistryCache().enchantments().values();
for (int i = 0; i < enchantments.size(); i++) {
if (enchantments.get(i).bedrockEnchantment() == enchantment) {
int level = bedrockEnchantment.getShort("lvl", (short) 1);
javaEnchantments.put(i, level);
break;
}
}
} else {
GeyserImpl.getInstance().getLogger().debug("Unknown bedrock enchantment: " + bedrockId);
}

View file

@ -70,8 +70,8 @@ public class FireworkRocketItem extends Item {
}
@Override
public void translateNbtToJava(@NonNull NbtMap bedrockTag, @NonNull DataComponents components, @NonNull ItemMapping mapping) {
super.translateNbtToJava(bedrockTag, components, mapping);
public void translateNbtToJava(@NonNull GeyserSession session, @NonNull NbtMap bedrockTag, @NonNull DataComponents components, @NonNull ItemMapping mapping) {
super.translateNbtToJava(session, bedrockTag, components, mapping);
NbtMap fireworksTag = bedrockTag.getCompound("Fireworks");
if (!fireworksTag.isEmpty()) {

View file

@ -78,8 +78,8 @@ public class FireworkStarItem extends Item {
}
@Override
public void translateNbtToJava(@NonNull NbtMap bedrockTag, @NonNull DataComponents components, @NonNull ItemMapping mapping) {
super.translateNbtToJava(bedrockTag, components, mapping);
public void translateNbtToJava(@NonNull GeyserSession session, @NonNull NbtMap bedrockTag, @NonNull DataComponents components, @NonNull ItemMapping mapping) {
super.translateNbtToJava(session, bedrockTag, components, mapping);
NbtMap explosion = bedrockTag.getCompound("FireworksItem");
if (!explosion.isEmpty()) {
@ -90,4 +90,9 @@ public class FireworkStarItem extends Item {
components.put(DataComponentType.FIREWORK_EXPLOSION, newExplosion);
}
}
@Override
public boolean ignoreDamage() {
return true;
}
}

View file

@ -62,4 +62,9 @@ public class GoatHornItem extends Item {
return itemStack;
}
@Override
public boolean ignoreDamage() {
return true;
}
}

Some files were not shown because too many files have changed in this diff Show more