mirror of https://github.com/GeyserMC/Geyser.git
Merge remote-tracking branch 'origin/master' into feature/floodgate-merge
# Conflicts: # core/src/main/java/org/geysermc/geyser/session/GeyserSession.java
This commit is contained in:
commit
120b367a41
|
@ -3,10 +3,12 @@ name: Build
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
|
branches-ignore:
|
||||||
|
- 'gh-readonly-queue/**'
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- '.github/ISSUE_TEMPLATE/*.yml'
|
- '.github/ISSUE_TEMPLATE/*.yml'
|
||||||
- '.github/actions/pullrequest.yml'
|
- '.github/actions/pullrequest.yml'
|
||||||
- '.idea/copyright/*.xml'
|
- '.idea/copyright/*.xml'
|
||||||
- '.gitignore'
|
- '.gitignore'
|
||||||
- 'CONTRIBUTING.md'
|
- 'CONTRIBUTING.md'
|
||||||
- 'LICENSE'
|
- 'LICENSE'
|
||||||
|
@ -20,72 +22,72 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository and submodules
|
- name: Checkout repository and submodules
|
||||||
# See https://github.com/actions/checkout/commits
|
# See https://github.com/actions/checkout/commits
|
||||||
uses: actions/checkout@72f2cec99f417b1a1c5e2e88945068983b7965f9
|
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
|
|
||||||
- name: Validate Gradle Wrapper
|
- name: Validate Gradle Wrapper
|
||||||
# See https://github.com/gradle/wrapper-validation-action/commits
|
# See https://github.com/gradle/wrapper-validation-action/commits
|
||||||
uses: gradle/wrapper-validation-action@56b90f209b02bf6d1deae490e9ef18b21a389cd4
|
uses: gradle/wrapper-validation-action@699bb18358f12c5b78b37bb0111d3a0e2276e0e2 # v2.1.1
|
||||||
|
|
||||||
# See https://github.com/actions/setup-java/commits
|
# See https://github.com/actions/setup-java/commits
|
||||||
- uses: actions/setup-java@4075bfc1b51bf22876335ae1cd589602d60d8758
|
- uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0
|
||||||
with:
|
with:
|
||||||
java-version: 17
|
java-version: 17
|
||||||
distribution: temurin
|
distribution: temurin
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
# See https://github.com/gradle/gradle-build-action/commits
|
# See https://github.com/gradle/gradle-build-action/commits
|
||||||
uses: gradle/gradle-build-action@3bfe3a46584a206fb8361cdedd0647b0c4204232
|
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 from https://github.com/gradle/actions/commits
|
||||||
with:
|
with:
|
||||||
arguments: build
|
arguments: build
|
||||||
gradle-home-cache-cleanup: true
|
gradle-home-cache-cleanup: true
|
||||||
|
|
||||||
- name: Archive artifacts (Geyser Fabric)
|
- name: Archive artifacts (Geyser Fabric)
|
||||||
# See https://github.com/actions/upload-artifact/commits
|
# See https://github.com/actions/upload-artifact/commits
|
||||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32
|
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||||
if: success()
|
if: success()
|
||||||
with:
|
with:
|
||||||
name: Geyser Fabric
|
name: Geyser Fabric
|
||||||
path: bootstrap/mod/fabric/build/libs/Geyser-Fabric.jar
|
path: bootstrap/mod/fabric/build/libs/Geyser-Fabric.jar
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
- name: Archive artifacts (Geyser NeoForge)
|
- name: Archive artifacts (Geyser NeoForge)
|
||||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32
|
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
|
||||||
if: success()
|
if: success()
|
||||||
with:
|
with:
|
||||||
name: Geyser NeoForge
|
name: Geyser NeoForge
|
||||||
path: bootstrap/mod/neoforge/build/libs/Geyser-NeoForge.jar
|
path: bootstrap/mod/neoforge/build/libs/Geyser-NeoForge.jar
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
- name: Archive artifacts (Geyser Standalone)
|
- name: Archive artifacts (Geyser Standalone)
|
||||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32
|
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
|
||||||
if: success()
|
if: success()
|
||||||
with:
|
with:
|
||||||
name: Geyser Standalone
|
name: Geyser Standalone
|
||||||
path: bootstrap/standalone/build/libs/Geyser-Standalone.jar
|
path: bootstrap/standalone/build/libs/Geyser-Standalone.jar
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
- name: Archive artifacts (Geyser Spigot)
|
- name: Archive artifacts (Geyser Spigot)
|
||||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32
|
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
|
||||||
if: success()
|
if: success()
|
||||||
with:
|
with:
|
||||||
name: Geyser Spigot
|
name: Geyser Spigot
|
||||||
path: bootstrap/spigot/build/libs/Geyser-Spigot.jar
|
path: bootstrap/spigot/build/libs/Geyser-Spigot.jar
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
- name: Archive artifacts (Geyser BungeeCord)
|
- name: Archive artifacts (Geyser BungeeCord)
|
||||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32
|
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
|
||||||
if: success()
|
if: success()
|
||||||
with:
|
with:
|
||||||
name: Geyser BungeeCord
|
name: Geyser BungeeCord
|
||||||
path: bootstrap/bungeecord/build/libs/Geyser-BungeeCord.jar
|
path: bootstrap/bungeecord/build/libs/Geyser-BungeeCord.jar
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
- name: Archive artifacts (Geyser Velocity)
|
- name: Archive artifacts (Geyser Velocity)
|
||||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32
|
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
|
||||||
if: success()
|
if: success()
|
||||||
with:
|
with:
|
||||||
name: Geyser Velocity
|
name: Geyser Velocity
|
||||||
path: bootstrap/velocity/build/libs/Geyser-Velocity.jar
|
path: bootstrap/velocity/build/libs/Geyser-Velocity.jar
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
- name: Archive artifacts (Geyser ViaProxy)
|
- name: Archive artifacts (Geyser ViaProxy)
|
||||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32
|
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
|
||||||
if: success()
|
if: success()
|
||||||
with:
|
with:
|
||||||
name: Geyser ViaProxy
|
name: Geyser ViaProxy
|
||||||
|
@ -94,7 +96,7 @@ jobs:
|
||||||
|
|
||||||
- name: Publish to Maven Repository
|
- name: Publish to Maven Repository
|
||||||
if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
|
if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
|
||||||
uses: gradle/gradle-build-action@3bfe3a46584a206fb8361cdedd0647b0c4204232
|
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
|
||||||
env:
|
env:
|
||||||
ORG_GRADLE_PROJECT_geysermcUsername: ${{ vars.DEPLOY_USER }}
|
ORG_GRADLE_PROJECT_geysermcUsername: ${{ vars.DEPLOY_USER }}
|
||||||
ORG_GRADLE_PROJECT_geysermcPassword: ${{ secrets.DEPLOY_PASS }}
|
ORG_GRADLE_PROJECT_geysermcPassword: ${{ secrets.DEPLOY_PASS }}
|
||||||
|
@ -127,7 +129,7 @@ jobs:
|
||||||
rsync -P -e "ssh -o StrictHostKeyChecking=no -i id_ecdsa" metadata.json $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP:~/uploads/$project/$GITHUB_RUN_NUMBER/
|
rsync -P -e "ssh -o StrictHostKeyChecking=no -i id_ecdsa" metadata.json $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP:~/uploads/$project/$GITHUB_RUN_NUMBER/
|
||||||
|
|
||||||
- name: Publish to Modrinth (Fabric)
|
- name: Publish to Modrinth (Fabric)
|
||||||
uses: gradle/gradle-build-action@3bfe3a46584a206fb8361cdedd0647b0c4204232
|
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
|
||||||
if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
|
if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
|
||||||
env:
|
env:
|
||||||
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
|
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
|
||||||
|
@ -136,7 +138,7 @@ jobs:
|
||||||
gradle-home-cache-cleanup: true
|
gradle-home-cache-cleanup: true
|
||||||
|
|
||||||
- name: Publish to Modrinth (NeoForge)
|
- name: Publish to Modrinth (NeoForge)
|
||||||
uses: gradle/gradle-build-action@3bfe3a46584a206fb8361cdedd0647b0c4204232
|
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
|
||||||
if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
|
if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
|
||||||
env:
|
env:
|
||||||
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
|
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
|
||||||
|
|
|
@ -2,6 +2,7 @@ name: Build Pull Request
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
|
merge_group:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
@ -9,7 +10,7 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 17
|
||||||
# See https://github.com/actions/setup-java/commits
|
# See https://github.com/actions/setup-java/commits
|
||||||
uses: actions/setup-java@4075bfc1b51bf22876335ae1cd589602d60d8758
|
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0
|
||||||
with:
|
with:
|
||||||
java-version: 17
|
java-version: 17
|
||||||
distribution: temurin
|
distribution: temurin
|
||||||
|
@ -35,67 +36,67 @@ jobs:
|
||||||
|
|
||||||
- name: Checkout repository and submodules
|
- name: Checkout repository and submodules
|
||||||
# See https://github.com/actions/checkout/commits
|
# See https://github.com/actions/checkout/commits
|
||||||
uses: actions/checkout@72f2cec99f417b1a1c5e2e88945068983b7965f9
|
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
path: geyser
|
path: geyser
|
||||||
|
|
||||||
- name: Validate Gradle Wrapper
|
- name: Validate Gradle Wrapper
|
||||||
# See https://github.com/gradle/wrapper-validation-action/commits
|
# See https://github.com/gradle/wrapper-validation-action/commits
|
||||||
uses: gradle/wrapper-validation-action@56b90f209b02bf6d1deae490e9ef18b21a389cd4
|
uses: gradle/wrapper-validation-action@699bb18358f12c5b78b37bb0111d3a0e2276e0e2 # v2.1.1
|
||||||
|
|
||||||
- name: Build Geyser
|
- name: Build Geyser
|
||||||
# See https://github.com/gradle/gradle-build-action/commits
|
# See https://github.com/gradle/gradle-build-action/commits
|
||||||
uses: gradle/gradle-build-action@3bfe3a46584a206fb8361cdedd0647b0c4204232
|
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 from https://github.com/gradle/actions/commits
|
||||||
with:
|
with:
|
||||||
arguments: build
|
arguments: build
|
||||||
build-root-directory: geyser
|
build-root-directory: geyser
|
||||||
|
|
||||||
- name: Archive artifacts (Geyser Fabric)
|
- name: Archive artifacts (Geyser Fabric)
|
||||||
# See https://github.com/actions/upload-artifact/commits
|
# See https://github.com/actions/upload-artifact/commits
|
||||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32
|
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||||
if: success()
|
if: success()
|
||||||
with:
|
with:
|
||||||
name: Geyser Fabric
|
name: Geyser Fabric
|
||||||
path: geyser/bootstrap/mod/fabric/build/libs/Geyser-Fabric.jar
|
path: geyser/bootstrap/mod/fabric/build/libs/Geyser-Fabric.jar
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
- name: Archive artifacts (Geyser NeoForge)
|
- name: Archive artifacts (Geyser NeoForge)
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
|
||||||
if: success()
|
if: success()
|
||||||
with:
|
with:
|
||||||
name: Geyser NeoForge
|
name: Geyser NeoForge
|
||||||
path: geyser/bootstrap/mod/neoforge/build/libs/Geyser-NeoForge.jar
|
path: geyser/bootstrap/mod/neoforge/build/libs/Geyser-NeoForge.jar
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
- name: Archive artifacts (Geyser Standalone)
|
- name: Archive artifacts (Geyser Standalone)
|
||||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32
|
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
|
||||||
if: success()
|
if: success()
|
||||||
with:
|
with:
|
||||||
name: Geyser Standalone
|
name: Geyser Standalone
|
||||||
path: geyser/bootstrap/standalone/build/libs/Geyser-Standalone.jar
|
path: geyser/bootstrap/standalone/build/libs/Geyser-Standalone.jar
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
- name: Archive artifacts (Geyser Spigot)
|
- name: Archive artifacts (Geyser Spigot)
|
||||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32
|
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
|
||||||
if: success()
|
if: success()
|
||||||
with:
|
with:
|
||||||
name: Geyser Spigot
|
name: Geyser Spigot
|
||||||
path: geyser/bootstrap/spigot/build/libs/Geyser-Spigot.jar
|
path: geyser/bootstrap/spigot/build/libs/Geyser-Spigot.jar
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
- name: Archive artifacts (Geyser BungeeCord)
|
- name: Archive artifacts (Geyser BungeeCord)
|
||||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32
|
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
|
||||||
if: success()
|
if: success()
|
||||||
with:
|
with:
|
||||||
name: Geyser BungeeCord
|
name: Geyser BungeeCord
|
||||||
path: geyser/bootstrap/bungeecord/build/libs/Geyser-BungeeCord.jar
|
path: geyser/bootstrap/bungeecord/build/libs/Geyser-BungeeCord.jar
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
- name: Archive artifacts (Geyser Velocity)
|
- name: Archive artifacts (Geyser Velocity)
|
||||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32
|
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
|
||||||
if: success()
|
if: success()
|
||||||
with:
|
with:
|
||||||
name: Geyser Velocity
|
name: Geyser Velocity
|
||||||
path: geyser/bootstrap/velocity/build/libs/Geyser-Velocity.jar
|
path: geyser/bootstrap/velocity/build/libs/Geyser-Velocity.jar
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
- name: Archive artifacts (Geyser ViaProxy)
|
- name: Archive artifacts (Geyser ViaProxy)
|
||||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32
|
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
|
||||||
if: success()
|
if: success()
|
||||||
with:
|
with:
|
||||||
name: Geyser ViaProxy
|
name: Geyser ViaProxy
|
||||||
|
|
|
@ -14,7 +14,7 @@ The ultimate goal of this project is to allow Minecraft: Bedrock Edition users t
|
||||||
|
|
||||||
Special thanks to the DragonProxy project for being a trailblazer in protocol translation and for all the team members who have joined us here!
|
Special thanks to the DragonProxy project for being a trailblazer in protocol translation and for all the team members who have joined us here!
|
||||||
|
|
||||||
### Currently supporting Minecraft Bedrock 1.20.40 - 1.20.61 and Minecraft Java 1.20.4
|
### Currently supporting Minecraft Bedrock 1.20.40 - 1.20.71 and Minecraft Java 1.20.4
|
||||||
|
|
||||||
## Setting Up
|
## Setting Up
|
||||||
Take a look [here](https://wiki.geysermc.org/geyser/setup/) for how to set up Geyser.
|
Take a look [here](https://wiki.geysermc.org/geyser/setup/) for how to set up Geyser.
|
||||||
|
|
|
@ -61,16 +61,16 @@ public interface CustomBlockData {
|
||||||
boolean includedInCreativeInventory();
|
boolean includedInCreativeInventory();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the item's creative category, or tab id.
|
* Gets the block's creative category, or tab id.
|
||||||
*
|
*
|
||||||
* @return the item's creative category
|
* @return the block's creative category
|
||||||
*/
|
*/
|
||||||
@Nullable CreativeCategory creativeCategory();
|
@Nullable CreativeCategory creativeCategory();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the item's creative group.
|
* Gets the block's creative group.
|
||||||
*
|
*
|
||||||
* @return the item's creative group
|
* @return the block's creative group
|
||||||
*/
|
*/
|
||||||
@Nullable String creativeGroup();
|
@Nullable String creativeGroup();
|
||||||
|
|
||||||
|
|
|
@ -129,8 +129,11 @@ public interface CustomBlockComponents {
|
||||||
* Gets the unit cube component
|
* Gets the unit cube component
|
||||||
* Equivalent to "minecraft:unit_cube"
|
* Equivalent to "minecraft:unit_cube"
|
||||||
*
|
*
|
||||||
|
* @deprecated Use {@link #geometry()} and compare with `minecraft:geometry.full_block` instead.
|
||||||
|
*
|
||||||
* @return The rotation.
|
* @return The rotation.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
boolean unitCube();
|
boolean unitCube();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -181,6 +184,10 @@ public interface CustomBlockComponents {
|
||||||
|
|
||||||
Builder transformation(TransformationComponent transformation);
|
Builder transformation(TransformationComponent transformation);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use {@link #geometry(GeometryComponent)} with `minecraft:geometry.full_block` instead.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
Builder unitCube(boolean unitCube);
|
Builder unitCube(boolean unitCube);
|
||||||
|
|
||||||
Builder placeAir(boolean placeAir);
|
Builder placeAir(boolean placeAir);
|
||||||
|
|
|
@ -29,6 +29,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import org.geysermc.geyser.api.GeyserApi;
|
import org.geysermc.geyser.api.GeyserApi;
|
||||||
|
|
||||||
|
import java.util.OptionalInt;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -77,6 +78,20 @@ public interface CustomItemData {
|
||||||
*/
|
*/
|
||||||
boolean displayHandheld();
|
boolean displayHandheld();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the item's creative category, or tab id.
|
||||||
|
*
|
||||||
|
* @return the item's creative category
|
||||||
|
*/
|
||||||
|
@NonNull OptionalInt creativeCategory();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the item's creative group.
|
||||||
|
*
|
||||||
|
* @return the item's creative group
|
||||||
|
*/
|
||||||
|
@Nullable String creativeGroup();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the item's texture size. This is to resize the item if the texture is not 16x16.
|
* Gets the item's texture size. This is to resize the item if the texture is not 16x16.
|
||||||
*
|
*
|
||||||
|
@ -119,6 +134,10 @@ public interface CustomItemData {
|
||||||
|
|
||||||
Builder displayHandheld(boolean displayHandheld);
|
Builder displayHandheld(boolean displayHandheld);
|
||||||
|
|
||||||
|
Builder creativeCategory(int creativeCategory);
|
||||||
|
|
||||||
|
Builder creativeGroup(@Nullable String creativeGroup);
|
||||||
|
|
||||||
Builder textureSize(int textureSize);
|
Builder textureSize(int textureSize);
|
||||||
|
|
||||||
Builder renderOffsets(@Nullable CustomRenderOffsets renderOffsets);
|
Builder renderOffsets(@Nullable CustomRenderOffsets renderOffsets);
|
||||||
|
|
|
@ -30,7 +30,6 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import org.geysermc.geyser.api.GeyserApi;
|
import org.geysermc.geyser.api.GeyserApi;
|
||||||
|
|
||||||
import java.util.OptionalInt;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -107,20 +106,6 @@ public interface NonVanillaCustomItemData extends CustomItemData {
|
||||||
*/
|
*/
|
||||||
@Nullable Set<String> repairMaterials();
|
@Nullable Set<String> repairMaterials();
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the item's creative category, or tab id.
|
|
||||||
*
|
|
||||||
* @return the item's creative category
|
|
||||||
*/
|
|
||||||
@NonNull OptionalInt creativeCategory();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the item's creative group.
|
|
||||||
*
|
|
||||||
* @return the item's creative group
|
|
||||||
*/
|
|
||||||
@Nullable String creativeGroup();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets if the item is a hat. This is used to determine if the item should be rendered on the player's head, and
|
* Gets if the item is a hat. This is used to determine if the item should be rendered on the player's head, and
|
||||||
* normally allow the player to equip it. This is not meant for armor.
|
* normally allow the player to equip it. This is not meant for armor.
|
||||||
|
@ -196,10 +181,6 @@ public interface NonVanillaCustomItemData extends CustomItemData {
|
||||||
|
|
||||||
Builder repairMaterials(@Nullable Set<String> repairMaterials);
|
Builder repairMaterials(@Nullable Set<String> repairMaterials);
|
||||||
|
|
||||||
Builder creativeCategory(int creativeCategory);
|
|
||||||
|
|
||||||
Builder creativeGroup(@Nullable String creativeGroup);
|
|
||||||
|
|
||||||
Builder hat(boolean isHat);
|
Builder hat(boolean isHat);
|
||||||
|
|
||||||
Builder foil(boolean isFoil);
|
Builder foil(boolean isFoil);
|
||||||
|
@ -218,6 +199,12 @@ public interface NonVanillaCustomItemData extends CustomItemData {
|
||||||
return displayHandheld(isTool);
|
return displayHandheld(isTool);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Builder creativeCategory(int creativeCategory);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Builder creativeGroup(@Nullable String creativeGroup);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
Builder customItemOptions(@NonNull CustomItemOptions customItemOptions);
|
Builder customItemOptions(@NonNull CustomItemOptions customItemOptions);
|
||||||
|
|
||||||
|
|
|
@ -444,7 +444,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (int i = protocolList.size() - 1; i >= 0; i--) {
|
for (int i = protocolList.size() - 1; i >= 0; i--) {
|
||||||
MappingData mappingData = protocolList.get(i).getProtocol().getMappingData();
|
MappingData mappingData = protocolList.get(i).protocol().getMappingData();
|
||||||
if (mappingData != null) {
|
if (mappingData != null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,11 +24,17 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||||
|
import org.gradle.api.DefaultTask
|
||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
import org.gradle.api.artifacts.MinimalExternalModuleDependency
|
import org.gradle.api.artifacts.MinimalExternalModuleDependency
|
||||||
import org.gradle.api.artifacts.ProjectDependency
|
import org.gradle.api.artifacts.ProjectDependency
|
||||||
import org.gradle.api.provider.Provider
|
import org.gradle.api.provider.Provider
|
||||||
|
import org.gradle.api.tasks.Input
|
||||||
|
import org.gradle.api.tasks.options.Option
|
||||||
|
import org.gradle.api.tasks.TaskAction
|
||||||
import org.gradle.kotlin.dsl.named
|
import org.gradle.kotlin.dsl.named
|
||||||
|
import java.io.File
|
||||||
|
import java.net.URL
|
||||||
|
|
||||||
fun Project.relocate(pattern: String) {
|
fun Project.relocate(pattern: String) {
|
||||||
tasks.named<ShadowJar>("shadowJar") {
|
tasks.named<ShadowJar>("shadowJar") {
|
||||||
|
@ -69,5 +75,45 @@ fun Project.provided(dependency: MinimalExternalModuleDependency) =
|
||||||
fun Project.provided(provider: Provider<MinimalExternalModuleDependency>) =
|
fun Project.provided(provider: Provider<MinimalExternalModuleDependency>) =
|
||||||
provided(provider.get())
|
provided(provider.get())
|
||||||
|
|
||||||
|
open class DownloadFilesTask : DefaultTask() {
|
||||||
|
@Input
|
||||||
|
var urls: List<String> = listOf()
|
||||||
|
|
||||||
|
@Input
|
||||||
|
var destinationDir: String = ""
|
||||||
|
|
||||||
|
@Option(option="suffix", description="suffix")
|
||||||
|
@Input
|
||||||
|
var suffix: String = ""
|
||||||
|
|
||||||
|
@Input
|
||||||
|
var suffixedFiles: List<String> = listOf()
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
fun downloadAndAddSuffix() {
|
||||||
|
urls.forEach { fileUrl ->
|
||||||
|
val fileName = fileUrl.substringAfterLast("/")
|
||||||
|
val baseName = fileName.substringBeforeLast(".")
|
||||||
|
val extension = fileName.substringAfterLast(".", "")
|
||||||
|
val shouldSuffix = fileName in suffixedFiles
|
||||||
|
val suffixedFileName = if (shouldSuffix && extension.isNotEmpty()) "$baseName.$suffix.$extension" else fileName
|
||||||
|
val outputFile = File(destinationDir, suffixedFileName)
|
||||||
|
|
||||||
|
if (!outputFile.parentFile.exists()) {
|
||||||
|
outputFile.parentFile.mkdirs()
|
||||||
|
}
|
||||||
|
|
||||||
|
URL(fileUrl).openStream().use { input ->
|
||||||
|
outputFile.outputStream().use { output ->
|
||||||
|
input.copyTo(output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println("Downloaded: $suffixedFileName")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun calcExclusion(section: String, bit: Int, excludedOn: Int): String =
|
private fun calcExclusion(section: String, bit: Int, excludedOn: Int): String =
|
||||||
if (excludedOn and bit > 0) section else ""
|
if (excludedOn and bit > 0) section else ""
|
||||||
|
|
||||||
|
|
|
@ -134,3 +134,19 @@ inner class GitInfo {
|
||||||
|
|
||||||
// todo remove this when we're not using Jenkins anymore
|
// todo remove this when we're not using Jenkins anymore
|
||||||
fun jenkinsBuildNumber(): String? = System.getenv("BUILD_NUMBER")
|
fun jenkinsBuildNumber(): String? = System.getenv("BUILD_NUMBER")
|
||||||
|
|
||||||
|
// Manual task to download the bedrock data files from the CloudburstMC/Data repository
|
||||||
|
// Invoke with ./gradlew :core:downloadBedrockData --suffix=1_20_70
|
||||||
|
// Set suffix to the current Bedrock version
|
||||||
|
tasks.register<DownloadFilesTask>("downloadBedrockData") {
|
||||||
|
urls = listOf(
|
||||||
|
"https://raw.githubusercontent.com/CloudburstMC/Data/master/entity_identifiers.dat",
|
||||||
|
"https://raw.githubusercontent.com/CloudburstMC/Data/master/biome_definitions.dat",
|
||||||
|
"https://raw.githubusercontent.com/CloudburstMC/Data/master/block_palette.nbt",
|
||||||
|
"https://raw.githubusercontent.com/CloudburstMC/Data/master/creative_items.json",
|
||||||
|
"https://raw.githubusercontent.com/CloudburstMC/Data/master/runtime_item_states.json"
|
||||||
|
)
|
||||||
|
suffixedFiles = listOf("block_palette.nbt", "creative_items.json", "runtime_item_states.json")
|
||||||
|
|
||||||
|
destinationDir = "$projectDir/src/main/resources/bedrock"
|
||||||
|
}
|
|
@ -26,10 +26,13 @@
|
||||||
package org.geysermc.geyser.inventory;
|
package org.geysermc.geyser.inventory;
|
||||||
|
|
||||||
import com.github.steveice10.mc.protocol.data.game.inventory.ContainerType;
|
import com.github.steveice10.mc.protocol.data.game.inventory.ContainerType;
|
||||||
import org.cloudburstmc.math.vector.Vector3i;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.cloudburstmc.math.vector.Vector3i;
|
||||||
import org.cloudburstmc.nbt.NbtMap;
|
import org.cloudburstmc.nbt.NbtMap;
|
||||||
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
import org.geysermc.geyser.translator.protocol.java.inventory.JavaOpenBookTranslator;
|
||||||
|
|
||||||
public class LecternContainer extends Container {
|
public class LecternContainer extends Container {
|
||||||
@Getter @Setter
|
@Getter @Setter
|
||||||
|
@ -39,7 +42,34 @@ public class LecternContainer extends Container {
|
||||||
@Getter @Setter
|
@Getter @Setter
|
||||||
private Vector3i position;
|
private Vector3i position;
|
||||||
|
|
||||||
|
// Sigh. When the lectern container is created, we don't know (yet) if it's fake or not.
|
||||||
|
// So... time for a manual check :/
|
||||||
|
@Getter
|
||||||
|
private boolean isFakeLectern = false;
|
||||||
|
|
||||||
public LecternContainer(String title, int id, int size, ContainerType containerType, PlayerInventory playerInventory) {
|
public LecternContainer(String title, int id, int size, ContainerType containerType, PlayerInventory playerInventory) {
|
||||||
super(title, id, size, containerType, playerInventory);
|
super(title, id, size, containerType, playerInventory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When we are using a fake lectern, the Java server expects us to still be in a player inventory.
|
||||||
|
* We can't use {@link #isUsingRealBlock()} as that may not be determined yet.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setItem(int slot, @NonNull GeyserItemStack newItem, GeyserSession session) {
|
||||||
|
if (isFakeLectern) {
|
||||||
|
session.getPlayerInventory().setItem(slot, newItem, session);
|
||||||
|
} else {
|
||||||
|
super.setItem(slot, newItem, session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is used ONLY once to set the book of a fake lectern in {@link JavaOpenBookTranslator}.
|
||||||
|
* See {@link LecternContainer#setItem(int, GeyserItemStack, GeyserSession)} as for why this is separate.
|
||||||
|
*/
|
||||||
|
public void setFakeLecternBook(GeyserItemStack book, GeyserSession session) {
|
||||||
|
this.isFakeLectern = true;
|
||||||
|
super.setItem(0, book, session);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,8 +32,10 @@ import org.cloudburstmc.protocol.bedrock.packet.BlockEntityDataPacket;
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.ContainerClosePacket;
|
import org.cloudburstmc.protocol.bedrock.packet.ContainerClosePacket;
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.ContainerOpenPacket;
|
import org.cloudburstmc.protocol.bedrock.packet.ContainerOpenPacket;
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.UpdateBlockPacket;
|
import org.cloudburstmc.protocol.bedrock.packet.UpdateBlockPacket;
|
||||||
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.inventory.Container;
|
import org.geysermc.geyser.inventory.Container;
|
||||||
import org.geysermc.geyser.inventory.Inventory;
|
import org.geysermc.geyser.inventory.Inventory;
|
||||||
|
import org.geysermc.geyser.inventory.LecternContainer;
|
||||||
import org.geysermc.geyser.registry.BlockRegistries;
|
import org.geysermc.geyser.registry.BlockRegistries;
|
||||||
import org.geysermc.geyser.registry.type.BlockMapping;
|
import org.geysermc.geyser.registry.type.BlockMapping;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
@ -151,13 +153,27 @@ public class BlockInventoryHolder extends InventoryHolder {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void closeInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
|
public void closeInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
|
||||||
if (((Container) inventory).isUsingRealBlock()) {
|
if (inventory instanceof Container container) {
|
||||||
// No need to reset a block since we didn't change any blocks
|
if (container.isUsingRealBlock() && !(inventory instanceof LecternContainer)) {
|
||||||
// But send a container close packet because we aren't destroying the original.
|
// No need to reset a block since we didn't change any blocks
|
||||||
ContainerClosePacket packet = new ContainerClosePacket();
|
// But send a container close packet because we aren't destroying the original.
|
||||||
packet.setId((byte) inventory.getBedrockId());
|
ContainerClosePacket packet = new ContainerClosePacket();
|
||||||
packet.setServerInitiated(true);
|
packet.setId((byte) inventory.getBedrockId());
|
||||||
session.sendUpstreamPacket(packet);
|
packet.setServerInitiated(true);
|
||||||
|
session.sendUpstreamPacket(packet);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
GeyserImpl.getInstance().getLogger().warning("Tried to close a non-container inventory in a block inventory holder! ");
|
||||||
|
if (GeyserImpl.getInstance().getLogger().isDebug()) {
|
||||||
|
GeyserImpl.getInstance().getLogger().debug("Current inventory: " + inventory);
|
||||||
|
GeyserImpl.getInstance().getLogger().debug("Open inventory: " + session.getOpenInventory());
|
||||||
|
}
|
||||||
|
// Try to save ourselves? maybe?
|
||||||
|
// https://github.com/GeyserMC/Geyser/issues/4141
|
||||||
|
// TODO: improve once this issue is pinned down properly
|
||||||
|
session.setOpenInventory(null);
|
||||||
|
session.setInventoryTranslator(InventoryTranslator.PLAYER_INVENTORY_TRANSLATOR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,7 @@ public class StoredItemMappings {
|
||||||
private final ItemMapping upgradeTemplate;
|
private final ItemMapping upgradeTemplate;
|
||||||
private final ItemMapping wheat;
|
private final ItemMapping wheat;
|
||||||
private final ItemMapping writableBook;
|
private final ItemMapping writableBook;
|
||||||
|
private final ItemMapping writtenBook;
|
||||||
|
|
||||||
public StoredItemMappings(Map<Item, ItemMapping> itemMappings) {
|
public StoredItemMappings(Map<Item, ItemMapping> itemMappings) {
|
||||||
this.bamboo = load(itemMappings, Items.BAMBOO);
|
this.bamboo = load(itemMappings, Items.BAMBOO);
|
||||||
|
@ -68,6 +69,7 @@ public class StoredItemMappings {
|
||||||
this.upgradeTemplate = load(itemMappings, Items.NETHERITE_UPGRADE_SMITHING_TEMPLATE);
|
this.upgradeTemplate = load(itemMappings, Items.NETHERITE_UPGRADE_SMITHING_TEMPLATE);
|
||||||
this.wheat = load(itemMappings, Items.WHEAT);
|
this.wheat = load(itemMappings, Items.WHEAT);
|
||||||
this.writableBook = load(itemMappings, Items.WRITABLE_BOOK);
|
this.writableBook = load(itemMappings, Items.WRITABLE_BOOK);
|
||||||
|
this.writtenBook = load(itemMappings, Items.WRITTEN_BOOK);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
|
|
|
@ -35,6 +35,7 @@ import org.geysermc.geyser.api.item.custom.CustomRenderOffsets;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.OptionalInt;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@EqualsAndHashCode
|
@EqualsAndHashCode
|
||||||
|
@ -46,6 +47,8 @@ public class GeyserCustomItemData implements CustomItemData {
|
||||||
private final String icon;
|
private final String icon;
|
||||||
private final boolean allowOffhand;
|
private final boolean allowOffhand;
|
||||||
private final boolean displayHandheld;
|
private final boolean displayHandheld;
|
||||||
|
private final OptionalInt creativeCategory;
|
||||||
|
private final String creativeGroup;
|
||||||
private final int textureSize;
|
private final int textureSize;
|
||||||
private final CustomRenderOffsets renderOffsets;
|
private final CustomRenderOffsets renderOffsets;
|
||||||
private final Set<String> tags;
|
private final Set<String> tags;
|
||||||
|
@ -56,6 +59,8 @@ public class GeyserCustomItemData implements CustomItemData {
|
||||||
String icon,
|
String icon,
|
||||||
boolean allowOffhand,
|
boolean allowOffhand,
|
||||||
boolean displayHandheld,
|
boolean displayHandheld,
|
||||||
|
OptionalInt creativeCategory,
|
||||||
|
String creativeGroup,
|
||||||
int textureSize,
|
int textureSize,
|
||||||
CustomRenderOffsets renderOffsets,
|
CustomRenderOffsets renderOffsets,
|
||||||
Set<String> tags) {
|
Set<String> tags) {
|
||||||
|
@ -65,6 +70,8 @@ public class GeyserCustomItemData implements CustomItemData {
|
||||||
this.icon = icon;
|
this.icon = icon;
|
||||||
this.allowOffhand = allowOffhand;
|
this.allowOffhand = allowOffhand;
|
||||||
this.displayHandheld = displayHandheld;
|
this.displayHandheld = displayHandheld;
|
||||||
|
this.creativeCategory = creativeCategory;
|
||||||
|
this.creativeGroup = creativeGroup;
|
||||||
this.textureSize = textureSize;
|
this.textureSize = textureSize;
|
||||||
this.renderOffsets = renderOffsets;
|
this.renderOffsets = renderOffsets;
|
||||||
this.tags = tags;
|
this.tags = tags;
|
||||||
|
@ -100,6 +107,16 @@ public class GeyserCustomItemData implements CustomItemData {
|
||||||
return this.displayHandheld;
|
return this.displayHandheld;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull OptionalInt creativeCategory() {
|
||||||
|
return this.creativeCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable String creativeGroup() {
|
||||||
|
return this.creativeGroup;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int textureSize() {
|
public int textureSize() {
|
||||||
return textureSize;
|
return textureSize;
|
||||||
|
@ -118,11 +135,12 @@ public class GeyserCustomItemData implements CustomItemData {
|
||||||
public static class Builder implements CustomItemData.Builder {
|
public static class Builder implements CustomItemData.Builder {
|
||||||
protected String name = null;
|
protected String name = null;
|
||||||
protected CustomItemOptions customItemOptions = null;
|
protected CustomItemOptions customItemOptions = null;
|
||||||
|
|
||||||
protected String displayName = null;
|
protected String displayName = null;
|
||||||
protected String icon = null;
|
protected String icon = null;
|
||||||
protected boolean allowOffhand = true; // Bedrock doesn't give items offhand allowance unless they serve gameplay purpose, but we want to be friendly with Java
|
protected boolean allowOffhand = true; // Bedrock doesn't give items offhand allowance unless they serve gameplay purpose, but we want to be friendly with Java
|
||||||
protected boolean displayHandheld = false;
|
protected boolean displayHandheld = false;
|
||||||
|
protected OptionalInt creativeCategory = OptionalInt.empty();
|
||||||
|
protected String creativeGroup = null;
|
||||||
protected int textureSize = 16;
|
protected int textureSize = 16;
|
||||||
protected CustomRenderOffsets renderOffsets = null;
|
protected CustomRenderOffsets renderOffsets = null;
|
||||||
protected Set<String> tags = new HashSet<>();
|
protected Set<String> tags = new HashSet<>();
|
||||||
|
@ -163,6 +181,18 @@ public class GeyserCustomItemData implements CustomItemData {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder creativeCategory(int creativeCategory) {
|
||||||
|
this.creativeCategory = OptionalInt.of(creativeCategory);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder creativeGroup(@Nullable String creativeGroup) {
|
||||||
|
this.creativeGroup = creativeGroup;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Builder textureSize(int textureSize) {
|
public Builder textureSize(int textureSize) {
|
||||||
this.textureSize = textureSize;
|
this.textureSize = textureSize;
|
||||||
|
@ -193,7 +223,8 @@ public class GeyserCustomItemData implements CustomItemData {
|
||||||
if (this.icon == null) {
|
if (this.icon == null) {
|
||||||
this.icon = this.name;
|
this.icon = this.name;
|
||||||
}
|
}
|
||||||
return new GeyserCustomItemData(this.name, this.customItemOptions, this.displayName, this.icon, this.allowOffhand, this.displayHandheld, this.textureSize, this.renderOffsets, this.tags);
|
return new GeyserCustomItemData(this.name, this.customItemOptions, this.displayName, this.icon, this.allowOffhand,
|
||||||
|
this.displayHandheld, this.creativeCategory, this.creativeGroup, this.textureSize, this.renderOffsets, this.tags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,10 +33,8 @@ import org.geysermc.geyser.api.item.custom.CustomItemOptions;
|
||||||
import org.geysermc.geyser.api.item.custom.CustomRenderOffsets;
|
import org.geysermc.geyser.api.item.custom.CustomRenderOffsets;
|
||||||
import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData;
|
import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData;
|
||||||
|
|
||||||
import java.util.OptionalInt;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
|
||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
@ToString
|
@ToString
|
||||||
public final class GeyserNonVanillaCustomItemData extends GeyserCustomItemData implements NonVanillaCustomItemData {
|
public final class GeyserNonVanillaCustomItemData extends GeyserCustomItemData implements NonVanillaCustomItemData {
|
||||||
|
@ -50,8 +48,6 @@ public final class GeyserNonVanillaCustomItemData extends GeyserCustomItemData i
|
||||||
private final int protectionValue;
|
private final int protectionValue;
|
||||||
private final String translationString;
|
private final String translationString;
|
||||||
private final Set<String> repairMaterials;
|
private final Set<String> repairMaterials;
|
||||||
private final OptionalInt creativeCategory;
|
|
||||||
private final String creativeGroup;
|
|
||||||
private final boolean isHat;
|
private final boolean isHat;
|
||||||
private final boolean isFoil;
|
private final boolean isFoil;
|
||||||
private final boolean isTool;
|
private final boolean isTool;
|
||||||
|
@ -61,7 +57,8 @@ public final class GeyserNonVanillaCustomItemData extends GeyserCustomItemData i
|
||||||
|
|
||||||
public GeyserNonVanillaCustomItemData(Builder builder) {
|
public GeyserNonVanillaCustomItemData(Builder builder) {
|
||||||
super(builder.name, builder.customItemOptions, builder.displayName, builder.icon, builder.allowOffhand,
|
super(builder.name, builder.customItemOptions, builder.displayName, builder.icon, builder.allowOffhand,
|
||||||
builder.displayHandheld, builder.textureSize, builder.renderOffsets, builder.tags);
|
builder.displayHandheld, builder.creativeCategory, builder.creativeGroup,
|
||||||
|
builder.textureSize, builder.renderOffsets, builder.tags);
|
||||||
|
|
||||||
this.identifier = builder.identifier;
|
this.identifier = builder.identifier;
|
||||||
this.javaId = builder.javaId;
|
this.javaId = builder.javaId;
|
||||||
|
@ -73,8 +70,6 @@ public final class GeyserNonVanillaCustomItemData extends GeyserCustomItemData i
|
||||||
this.protectionValue = builder.protectionValue;
|
this.protectionValue = builder.protectionValue;
|
||||||
this.translationString = builder.translationString;
|
this.translationString = builder.translationString;
|
||||||
this.repairMaterials = builder.repairMaterials;
|
this.repairMaterials = builder.repairMaterials;
|
||||||
this.creativeCategory = builder.creativeCategory;
|
|
||||||
this.creativeGroup = builder.creativeGroup;
|
|
||||||
this.isHat = builder.hat;
|
this.isHat = builder.hat;
|
||||||
this.isFoil = builder.foil;
|
this.isFoil = builder.foil;
|
||||||
this.isTool = builder.tool;
|
this.isTool = builder.tool;
|
||||||
|
@ -133,16 +128,6 @@ public final class GeyserNonVanillaCustomItemData extends GeyserCustomItemData i
|
||||||
return repairMaterials;
|
return repairMaterials;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull OptionalInt creativeCategory() {
|
|
||||||
return creativeCategory;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String creativeGroup() {
|
|
||||||
return creativeGroup;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isHat() {
|
public boolean isHat() {
|
||||||
return isHat;
|
return isHat;
|
||||||
|
@ -186,9 +171,6 @@ public final class GeyserNonVanillaCustomItemData extends GeyserCustomItemData i
|
||||||
|
|
||||||
private Set<String> repairMaterials;
|
private Set<String> repairMaterials;
|
||||||
|
|
||||||
private OptionalInt creativeCategory = OptionalInt.empty();
|
|
||||||
private String creativeGroup = null;
|
|
||||||
|
|
||||||
private boolean hat = false;
|
private boolean hat = false;
|
||||||
private boolean foil = false;
|
private boolean foil = false;
|
||||||
private boolean tool = false;
|
private boolean tool = false;
|
||||||
|
@ -243,103 +225,101 @@ public final class GeyserNonVanillaCustomItemData extends GeyserCustomItemData i
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NonVanillaCustomItemData.Builder identifier(@NonNull String identifier) {
|
public Builder identifier(@NonNull String identifier) {
|
||||||
this.identifier = identifier;
|
this.identifier = identifier;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NonVanillaCustomItemData.Builder javaId(int javaId) {
|
public Builder javaId(int javaId) {
|
||||||
this.javaId = javaId;
|
this.javaId = javaId;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NonVanillaCustomItemData.Builder stackSize(int stackSize) {
|
public Builder stackSize(int stackSize) {
|
||||||
this.stackSize = stackSize;
|
this.stackSize = stackSize;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NonVanillaCustomItemData.Builder maxDamage(int maxDamage) {
|
public Builder maxDamage(int maxDamage) {
|
||||||
this.maxDamage = maxDamage;
|
this.maxDamage = maxDamage;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NonVanillaCustomItemData.Builder toolType(@Nullable String toolType) {
|
public Builder toolType(@Nullable String toolType) {
|
||||||
this.toolType = toolType;
|
this.toolType = toolType;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NonVanillaCustomItemData.Builder toolTier(@Nullable String toolTier) {
|
public Builder toolTier(@Nullable String toolTier) {
|
||||||
this.toolTier = toolTier;
|
this.toolTier = toolTier;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NonVanillaCustomItemData.Builder armorType(@Nullable String armorType) {
|
public Builder armorType(@Nullable String armorType) {
|
||||||
this.armorType = armorType;
|
this.armorType = armorType;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NonVanillaCustomItemData.Builder protectionValue(int protectionValue) {
|
public Builder protectionValue(int protectionValue) {
|
||||||
this.protectionValue = protectionValue;
|
this.protectionValue = protectionValue;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NonVanillaCustomItemData.Builder translationString(@Nullable String translationString) {
|
public Builder translationString(@Nullable String translationString) {
|
||||||
this.translationString = translationString;
|
this.translationString = translationString;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NonVanillaCustomItemData.Builder repairMaterials(@Nullable Set<String> repairMaterials) {
|
public Builder repairMaterials(@Nullable Set<String> repairMaterials) {
|
||||||
this.repairMaterials = repairMaterials;
|
this.repairMaterials = repairMaterials;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NonVanillaCustomItemData.Builder creativeCategory(int creativeCategory) {
|
public Builder creativeCategory(int creativeCategory) {
|
||||||
this.creativeCategory = OptionalInt.of(creativeCategory);
|
return (Builder) super.creativeCategory(creativeCategory);
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NonVanillaCustomItemData.Builder creativeGroup(@Nullable String creativeGroup) {
|
public Builder creativeGroup(@Nullable String creativeGroup) {
|
||||||
this.creativeGroup = creativeGroup;
|
return (Builder) super.creativeGroup(creativeGroup);
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NonVanillaCustomItemData.Builder hat(boolean isHat) {
|
public Builder hat(boolean isHat) {
|
||||||
this.hat = isHat;
|
this.hat = isHat;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NonVanillaCustomItemData.Builder foil(boolean isFoil) {
|
public Builder foil(boolean isFoil) {
|
||||||
this.foil = isFoil;
|
this.foil = isFoil;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NonVanillaCustomItemData.Builder edible(boolean isEdible) {
|
public Builder edible(boolean isEdible) {
|
||||||
this.edible = isEdible;
|
this.edible = isEdible;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NonVanillaCustomItemData.Builder canAlwaysEat(boolean canAlwaysEat) {
|
public Builder canAlwaysEat(boolean canAlwaysEat) {
|
||||||
this.canAlwaysEat = canAlwaysEat;
|
this.canAlwaysEat = canAlwaysEat;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NonVanillaCustomItemData.Builder chargeable(boolean isChargeable) {
|
public Builder chargeable(boolean isChargeable) {
|
||||||
this.chargeable = isChargeable;
|
this.chargeable = isChargeable;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,14 @@ public enum BedrockMapIcon {
|
||||||
ICON_GREEN_BANNER(MapIconType.GREEN_BANNER, 13, 94, 124, 22),
|
ICON_GREEN_BANNER(MapIconType.GREEN_BANNER, 13, 94, 124, 22),
|
||||||
ICON_RED_BANNER(MapIconType.RED_BANNER, 13, 176, 46, 38),
|
ICON_RED_BANNER(MapIconType.RED_BANNER, 13, 176, 46, 38),
|
||||||
ICON_BLACK_BANNER(MapIconType.BLACK_BANNER, 13, 29, 29, 33),
|
ICON_BLACK_BANNER(MapIconType.BLACK_BANNER, 13, 29, 29, 33),
|
||||||
ICON_TREASURE_MARKER(MapIconType.TREASURE_MARKER, 4);
|
ICON_TREASURE_MARKER(MapIconType.TREASURE_MARKER, 4),
|
||||||
|
ICON_DESERT_VILLAGE(MapIconType.DESERT_VILLAGE, 17),
|
||||||
|
ICON_PLAINS_VILLAGE(MapIconType.PLAINS_VILLAGE, 18),
|
||||||
|
ICON_SAVANNA_VILLAGE(MapIconType.SAVANNA_VILLAGE, 19),
|
||||||
|
ICON_SNOWY_VILLAGE(MapIconType.SNOWY_VILLAGE, 20),
|
||||||
|
ICON_TAIGA_VILLAGE(MapIconType.TAIGA_VILLAGE, 21),
|
||||||
|
ICON_JUNGLE_TEMPLE(MapIconType.JUNGLE_TEMPLE, 22),
|
||||||
|
ICON_SWAMP_HUT(MapIconType.SWAMP_HUT, 23);
|
||||||
|
|
||||||
private static final BedrockMapIcon[] VALUES = values();
|
private static final BedrockMapIcon[] VALUES = values();
|
||||||
|
|
||||||
|
|
|
@ -58,7 +58,6 @@ public class GeyserCustomBlockComponents implements CustomBlockComponents {
|
||||||
Integer lightEmission;
|
Integer lightEmission;
|
||||||
Integer lightDampening;
|
Integer lightDampening;
|
||||||
TransformationComponent transformation;
|
TransformationComponent transformation;
|
||||||
boolean unitCube;
|
|
||||||
boolean placeAir;
|
boolean placeAir;
|
||||||
Set<String> tags;
|
Set<String> tags;
|
||||||
|
|
||||||
|
@ -66,7 +65,13 @@ public class GeyserCustomBlockComponents implements CustomBlockComponents {
|
||||||
this.selectionBox = builder.selectionBox;
|
this.selectionBox = builder.selectionBox;
|
||||||
this.collisionBox = builder.collisionBox;
|
this.collisionBox = builder.collisionBox;
|
||||||
this.displayName = builder.displayName;
|
this.displayName = builder.displayName;
|
||||||
this.geometry = builder.geometry;
|
GeometryComponent geo = builder.geometry;
|
||||||
|
if (builder.unitCube && geo == null) {
|
||||||
|
geo = GeometryComponent.builder()
|
||||||
|
.identifier("minecraft:geometry.full_block")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
this.geometry = geo;
|
||||||
if (builder.materialInstances.isEmpty()) {
|
if (builder.materialInstances.isEmpty()) {
|
||||||
this.materialInstances = Object2ObjectMaps.emptyMap();
|
this.materialInstances = Object2ObjectMaps.emptyMap();
|
||||||
} else {
|
} else {
|
||||||
|
@ -78,7 +83,6 @@ public class GeyserCustomBlockComponents implements CustomBlockComponents {
|
||||||
this.lightEmission = builder.lightEmission;
|
this.lightEmission = builder.lightEmission;
|
||||||
this.lightDampening = builder.lightDampening;
|
this.lightDampening = builder.lightDampening;
|
||||||
this.transformation = builder.transformation;
|
this.transformation = builder.transformation;
|
||||||
this.unitCube = builder.unitCube;
|
|
||||||
this.placeAir = builder.placeAir;
|
this.placeAir = builder.placeAir;
|
||||||
if (builder.tags.isEmpty()) {
|
if (builder.tags.isEmpty()) {
|
||||||
this.tags = Set.of();
|
this.tags = Set.of();
|
||||||
|
@ -144,7 +148,7 @@ public class GeyserCustomBlockComponents implements CustomBlockComponents {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean unitCube() {
|
public boolean unitCube() {
|
||||||
return unitCube;
|
return geometry.identifier().equals("minecraft:geometry.full_block");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -32,6 +32,7 @@ import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
|
||||||
import org.cloudburstmc.protocol.bedrock.codec.v622.Bedrock_v622;
|
import org.cloudburstmc.protocol.bedrock.codec.v622.Bedrock_v622;
|
||||||
import org.cloudburstmc.protocol.bedrock.codec.v630.Bedrock_v630;
|
import org.cloudburstmc.protocol.bedrock.codec.v630.Bedrock_v630;
|
||||||
import org.cloudburstmc.protocol.bedrock.codec.v649.Bedrock_v649;
|
import org.cloudburstmc.protocol.bedrock.codec.v649.Bedrock_v649;
|
||||||
|
import org.cloudburstmc.protocol.bedrock.codec.v662.Bedrock_v662;
|
||||||
import org.cloudburstmc.protocol.bedrock.netty.codec.packet.BedrockPacketCodec;
|
import org.cloudburstmc.protocol.bedrock.netty.codec.packet.BedrockPacketCodec;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
|
||||||
|
@ -47,7 +48,7 @@ public final class GameProtocol {
|
||||||
* Default Bedrock codec that should act as a fallback. Should represent the latest available
|
* Default Bedrock codec that should act as a fallback. Should represent the latest available
|
||||||
* release of the game that Geyser supports.
|
* release of the game that Geyser supports.
|
||||||
*/
|
*/
|
||||||
public static final BedrockCodec DEFAULT_BEDROCK_CODEC = Bedrock_v649.CODEC;
|
public static final BedrockCodec DEFAULT_BEDROCK_CODEC = Bedrock_v662.CODEC;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A list of all supported Bedrock versions that can join Geyser
|
* A list of all supported Bedrock versions that can join Geyser
|
||||||
|
@ -67,8 +68,11 @@ public final class GameProtocol {
|
||||||
SUPPORTED_BEDROCK_CODECS.add(Bedrock_v630.CODEC.toBuilder()
|
SUPPORTED_BEDROCK_CODECS.add(Bedrock_v630.CODEC.toBuilder()
|
||||||
.minecraftVersion("1.20.50/1.20.51")
|
.minecraftVersion("1.20.50/1.20.51")
|
||||||
.build());
|
.build());
|
||||||
|
SUPPORTED_BEDROCK_CODECS.add(Bedrock_v649.CODEC.toBuilder()
|
||||||
|
.minecraftVersion("1.20.60/1.20.62")
|
||||||
|
.build());
|
||||||
SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC.toBuilder()
|
SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC.toBuilder()
|
||||||
.minecraftVersion("1.20.60/1.20.61")
|
.minecraftVersion("1.20.70/1.20.71")
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,6 +96,10 @@ public final class GameProtocol {
|
||||||
return session.getUpstream().getProtocolVersion() < Bedrock_v630.CODEC.getProtocolVersion();
|
return session.getUpstream().getProtocolVersion() < Bedrock_v630.CODEC.getProtocolVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isPre1_20_70(GeyserSession session) {
|
||||||
|
return session.getUpstream().getProtocolVersion() < Bedrock_v662.CODEC.getProtocolVersion();
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean is1_20_60orHigher(int protocolVersion) {
|
public static boolean is1_20_60orHigher(int protocolVersion) {
|
||||||
return protocolVersion >= Bedrock_v649.CODEC.getProtocolVersion();
|
return protocolVersion >= Bedrock_v649.CODEC.getProtocolVersion();
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,18 +31,18 @@ import io.netty.channel.ChannelFuture;
|
||||||
import io.netty.channel.epoll.Native;
|
import io.netty.channel.epoll.Native;
|
||||||
import io.netty.channel.unix.UnixChannelOption;
|
import io.netty.channel.unix.UnixChannelOption;
|
||||||
import lombok.experimental.UtilityClass;
|
import lombok.experimental.UtilityClass;
|
||||||
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
|
||||||
@UtilityClass
|
@UtilityClass
|
||||||
public final class Bootstraps {
|
public final class Bootstraps {
|
||||||
private static final Optional<int[]> KERNEL_VERSION;
|
|
||||||
|
|
||||||
// The REUSEPORT_AVAILABLE socket option is available starting from kernel version 3.9.
|
// The REUSEPORT_AVAILABLE socket option is available starting from kernel version 3.9.
|
||||||
// This option allows multiple sockets to listen on the same IP address and port without conflict.
|
// This option allows multiple sockets to listen on the same IP address and port without conflict.
|
||||||
private static final int[] REUSEPORT_VERSION = new int[]{3, 9, 0};
|
private static final int[] REUSEPORT_VERSION = new int[]{3, 9};
|
||||||
private static final boolean REUSEPORT_AVAILABLE;
|
private static final boolean REUSEPORT_AVAILABLE;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
@ -50,24 +50,16 @@ public final class Bootstraps {
|
||||||
try {
|
try {
|
||||||
kernelVersion = Native.KERNEL_VERSION;
|
kernelVersion = Native.KERNEL_VERSION;
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
|
GeyserImpl.getInstance().getLogger().debug("Could not determine kernel version! " + e.getMessage());
|
||||||
kernelVersion = null;
|
kernelVersion = null;
|
||||||
}
|
}
|
||||||
if (kernelVersion != null && kernelVersion.contains("-")) {
|
|
||||||
int index = kernelVersion.indexOf('-');
|
|
||||||
if (index > -1) {
|
|
||||||
kernelVersion = kernelVersion.substring(0, index);
|
|
||||||
}
|
|
||||||
int[] kernelVer = fromString(kernelVersion);
|
|
||||||
KERNEL_VERSION = Optional.of(kernelVer);
|
|
||||||
REUSEPORT_AVAILABLE = checkVersion(kernelVer, 0);
|
|
||||||
} else {
|
|
||||||
KERNEL_VERSION = Optional.empty();
|
|
||||||
REUSEPORT_AVAILABLE = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Optional<int[]> getKernelVersion() {
|
if (kernelVersion == null) {
|
||||||
return KERNEL_VERSION;
|
REUSEPORT_AVAILABLE = false;
|
||||||
|
} else {
|
||||||
|
int[] kernelVer = fromString(kernelVersion);
|
||||||
|
REUSEPORT_AVAILABLE = checkVersion(kernelVer, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isReusePortAvailable() {
|
public static boolean isReusePortAvailable() {
|
||||||
|
@ -81,17 +73,19 @@ public final class Bootstraps {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int[] fromString(String ver) {
|
private static int[] fromString(String input) {
|
||||||
String[] parts = ver.split("\\.");
|
// Match only beginning of string for at least two digits separated by dot
|
||||||
if (parts.length < 2) {
|
Pattern pattern = Pattern.compile("^(\\d+)\\.(\\d+)");
|
||||||
throw new IllegalArgumentException("At least 2 version numbers required");
|
Matcher matcher = pattern.matcher(input);
|
||||||
|
|
||||||
|
int[] version = {0, 0};
|
||||||
|
|
||||||
|
if (matcher.find()) {
|
||||||
|
version[0] = Integer.parseInt(matcher.group(1));
|
||||||
|
version[1] = Integer.parseInt(matcher.group(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new int[]{
|
return version;
|
||||||
Integer.parseInt(parts[0]),
|
|
||||||
Integer.parseInt(parts[1]),
|
|
||||||
parts.length == 2 ? 0 : Integer.parseInt(parts[2])
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean checkVersion(int[] ver, int i) {
|
private static boolean checkVersion(int[] ver, int i) {
|
||||||
|
|
|
@ -193,6 +193,14 @@ public class MappingsReader_v1 extends MappingsReader {
|
||||||
customItemData.icon(node.get("icon").asText());
|
customItemData.icon(node.get("icon").asText());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (node.has("creative_category")) {
|
||||||
|
customItemData.creativeCategory(node.get("creative_category").asInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.has("creative_group")) {
|
||||||
|
customItemData.creativeGroup(node.get("creative_group").asText());
|
||||||
|
}
|
||||||
|
|
||||||
if (node.has("allow_offhand")) {
|
if (node.has("allow_offhand")) {
|
||||||
customItemData.allowOffhand(node.get("allow_offhand").asBoolean());
|
customItemData.allowOffhand(node.get("allow_offhand").asBoolean());
|
||||||
}
|
}
|
||||||
|
@ -488,7 +496,9 @@ public class MappingsReader_v1 extends MappingsReader {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.has("unit_cube")) {
|
if (node.has("unit_cube")) {
|
||||||
builder.unitCube(node.get("unit_cube").asBoolean());
|
builder.geometry(GeometryComponent.builder()
|
||||||
|
.identifier("minecraft:geometry.full_block")
|
||||||
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.has("material_instances")) {
|
if (node.has("material_instances")) {
|
||||||
|
|
|
@ -42,6 +42,7 @@ import org.cloudburstmc.nbt.NbtType;
|
||||||
import org.cloudburstmc.protocol.bedrock.codec.v622.Bedrock_v622;
|
import org.cloudburstmc.protocol.bedrock.codec.v622.Bedrock_v622;
|
||||||
import org.cloudburstmc.protocol.bedrock.codec.v630.Bedrock_v630;
|
import org.cloudburstmc.protocol.bedrock.codec.v630.Bedrock_v630;
|
||||||
import org.cloudburstmc.protocol.bedrock.codec.v649.Bedrock_v649;
|
import org.cloudburstmc.protocol.bedrock.codec.v649.Bedrock_v649;
|
||||||
|
import org.cloudburstmc.protocol.bedrock.codec.v662.Bedrock_v662;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.BlockPropertyData;
|
import org.cloudburstmc.protocol.bedrock.data.BlockPropertyData;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
|
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
|
@ -118,9 +119,10 @@ public final class BlockRegistryPopulator {
|
||||||
private static void registerBedrockBlocks() {
|
private static void registerBedrockBlocks() {
|
||||||
var blockMappers = ImmutableMap.<ObjectIntPair<String>, Remapper>builder()
|
var blockMappers = ImmutableMap.<ObjectIntPair<String>, Remapper>builder()
|
||||||
.put(ObjectIntPair.of("1_20_40", Bedrock_v622.CODEC.getProtocolVersion()), Conversion630_622::remapBlock)
|
.put(ObjectIntPair.of("1_20_40", Bedrock_v622.CODEC.getProtocolVersion()), Conversion630_622::remapBlock)
|
||||||
.put(ObjectIntPair.of("1_20_50", Bedrock_v630.CODEC.getProtocolVersion()), tag -> tag)
|
.put(ObjectIntPair.of("1_20_50", Bedrock_v630.CODEC.getProtocolVersion()), Conversion649_630::remapBlock)
|
||||||
// Only changes in 1.20.60 are hard_stained_glass (an EDU only block)
|
// Only changes in 1.20.60 are hard_stained_glass (an EDU only block)
|
||||||
.put(ObjectIntPair.of("1_20_60", Bedrock_v649.CODEC.getProtocolVersion()), tag -> tag)
|
.put(ObjectIntPair.of("1_20_60", Bedrock_v649.CODEC.getProtocolVersion()), Conversion662_649::remapBlock)
|
||||||
|
.put(ObjectIntPair.of("1_20_70", Bedrock_v662.CODEC.getProtocolVersion()), tag -> tag)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// We can keep this strong as nothing should be garbage collected
|
// We can keep this strong as nothing should be garbage collected
|
||||||
|
@ -133,7 +135,7 @@ public final class BlockRegistryPopulator {
|
||||||
List<NbtMap> vanillaBlockStates;
|
List<NbtMap> vanillaBlockStates;
|
||||||
List<NbtMap> blockStates;
|
List<NbtMap> blockStates;
|
||||||
try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow(String.format("bedrock/block_palette.%s.nbt", palette.key()));
|
try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow(String.format("bedrock/block_palette.%s.nbt", palette.key()));
|
||||||
NBTInputStream nbtInputStream = new NBTInputStream(new DataInputStream(new GZIPInputStream(stream)), true, true)) {
|
NBTInputStream nbtInputStream = new NBTInputStream(new DataInputStream(new GZIPInputStream(stream)), true, true)) {
|
||||||
NbtMap blockPalette = (NbtMap) nbtInputStream.readTag();
|
NbtMap blockPalette = (NbtMap) nbtInputStream.readTag();
|
||||||
|
|
||||||
vanillaBlockStates = new ArrayList<>(blockPalette.getList("blocks", NbtType.COMPOUND));
|
vanillaBlockStates = new ArrayList<>(blockPalette.getList("blocks", NbtType.COMPOUND));
|
||||||
|
|
|
@ -121,6 +121,8 @@ class Conversion630_622 {
|
||||||
}
|
}
|
||||||
|
|
||||||
static GeyserMappingItem remapItem(@SuppressWarnings("unused") Item item, GeyserMappingItem mapping) {
|
static GeyserMappingItem remapItem(@SuppressWarnings("unused") Item item, GeyserMappingItem mapping) {
|
||||||
|
mapping = Conversion649_630.remapItem(item, mapping);
|
||||||
|
|
||||||
String replacement = ITEMS.get(mapping.getBedrockIdentifier());
|
String replacement = ITEMS.get(mapping.getBedrockIdentifier());
|
||||||
if (replacement == null) {
|
if (replacement == null) {
|
||||||
return mapping;
|
return mapping;
|
||||||
|
@ -130,6 +132,8 @@ class Conversion630_622 {
|
||||||
}
|
}
|
||||||
|
|
||||||
static NbtMap remapBlock(NbtMap tag) {
|
static NbtMap remapBlock(NbtMap tag) {
|
||||||
|
tag = Conversion649_630.remapBlock(tag);
|
||||||
|
|
||||||
final String name = tag.getString("name");
|
final String name = tag.getString("name");
|
||||||
|
|
||||||
String replacement;
|
String replacement;
|
||||||
|
|
|
@ -22,19 +22,41 @@
|
||||||
* @author GeyserMC
|
* @author GeyserMC
|
||||||
* @link https://github.com/GeyserMC/Geyser
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.geysermc.geyser.registry.populator;
|
package org.geysermc.geyser.registry.populator;
|
||||||
|
|
||||||
|
import org.cloudburstmc.nbt.NbtMap;
|
||||||
|
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||||
import org.geysermc.geyser.item.type.Item;
|
import org.geysermc.geyser.item.type.Item;
|
||||||
import org.geysermc.geyser.registry.type.GeyserMappingItem;
|
import org.geysermc.geyser.registry.type.GeyserMappingItem;
|
||||||
|
|
||||||
|
public class Conversion649_630 {
|
||||||
|
|
||||||
|
static GeyserMappingItem remapItem(@SuppressWarnings("unused") Item item, GeyserMappingItem mapping) {
|
||||||
|
mapping = Conversion662_649.remapItem(item, mapping);
|
||||||
|
|
||||||
public class Conversion630_649 {
|
String identifer = mapping.getBedrockIdentifier();
|
||||||
|
|
||||||
static GeyserMappingItem remapItem(@SuppressWarnings("unused") Item item, GeyserMappingItem mapping) {
|
switch (identifer) {
|
||||||
if (mapping.getBedrockIdentifier().equalsIgnoreCase("minecraft:scute")) {
|
case "minecraft:turtle_scute" -> { return mapping.withBedrockIdentifier("minecraft:scute"); }
|
||||||
return mapping.withBedrockIdentifier("minecraft:turtle_scute");
|
case "minecraft:trial_spawner" -> { return mapping.withBedrockIdentifier("minecraft:mob_spawner"); }
|
||||||
|
case "minecraft:trial_key" -> { return mapping.withBedrockIdentifier("minecraft:echo_shard"); }
|
||||||
|
default -> { return mapping; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static NbtMap remapBlock(NbtMap tag) {
|
||||||
|
tag = Conversion662_649.remapBlock(tag);
|
||||||
|
|
||||||
|
final String name = tag.getString("name");
|
||||||
|
|
||||||
|
if (name.equals("minecraft:trial_spawner")) {
|
||||||
|
NbtMapBuilder builder = tag.toBuilder()
|
||||||
|
.putString("name", "minecraft:mob_spawner")
|
||||||
|
.putCompound("states", NbtMap.EMPTY);
|
||||||
|
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
return tag;
|
||||||
}
|
}
|
||||||
return mapping;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -0,0 +1,167 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2024 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.registry.populator;
|
||||||
|
|
||||||
|
import org.cloudburstmc.nbt.NbtMap;
|
||||||
|
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||||
|
import org.geysermc.geyser.item.type.Item;
|
||||||
|
import org.geysermc.geyser.registry.type.GeyserMappingItem;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public class Conversion662_649 {
|
||||||
|
|
||||||
|
private static final List<String> NEW_MISC = List.of("minecraft:grass_block", "minecraft:trial_spawner");
|
||||||
|
private static final List<String> NEW_WOODS = List.of("minecraft:oak_wood", "minecraft:spruce_wood", "minecraft:birch_wood", "minecraft:jungle_wood", "minecraft:acacia_wood", "minecraft:dark_oak_wood", "minecraft:stripped_oak_wood", "minecraft:stripped_spruce_wood", "minecraft:stripped_birch_wood", "minecraft:stripped_jungle_wood", "minecraft:stripped_acacia_wood", "minecraft:stripped_dark_oak_wood");
|
||||||
|
private static final List<String> NEW_LEAVES = List.of("minecraft:oak_leaves", "minecraft:spruce_leaves", "minecraft:birch_leaves", "minecraft:jungle_leaves");
|
||||||
|
private static final List<String> NEW_LEAVES2 = List.of("minecraft:acacia_leaves", "minecraft:dark_oak_leaves");
|
||||||
|
private static final List<String> NEW_SLABS = List.of("minecraft:oak_slab", "minecraft:spruce_slab", "minecraft:birch_slab", "minecraft:jungle_slab", "minecraft:acacia_slab", "minecraft:dark_oak_slab", "minecraft:oak_double_slab", "minecraft:spruce_double_slab", "minecraft:birch_double_slab", "minecraft:jungle_double_slab", "minecraft:acacia_double_slab", "minecraft:dark_oak_double_slab");
|
||||||
|
private static final List<String> NEW_BLOCKS = Stream.of(NEW_WOODS, NEW_LEAVES, NEW_LEAVES2, NEW_SLABS, NEW_MISC).flatMap(List::stream).toList();
|
||||||
|
|
||||||
|
|
||||||
|
static GeyserMappingItem remapItem(@SuppressWarnings("unused") Item item, GeyserMappingItem mapping) {
|
||||||
|
String identifer = mapping.getBedrockIdentifier();
|
||||||
|
|
||||||
|
if (identifer.equals("minecraft:grass_block")) {
|
||||||
|
return mapping.withBedrockIdentifier("minecraft:grass");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NEW_WOODS.contains(identifer)) {
|
||||||
|
switch (identifer) {
|
||||||
|
case "minecraft:oak_wood" -> { return mapping.withBedrockIdentifier("minecraft:wood").withBedrockData(0); }
|
||||||
|
case "minecraft:spruce_wood" -> { return mapping.withBedrockIdentifier("minecraft:wood").withBedrockData(1); }
|
||||||
|
case "minecraft:birch_wood" -> { return mapping.withBedrockIdentifier("minecraft:wood").withBedrockData(2); }
|
||||||
|
case "minecraft:jungle_wood" -> { return mapping.withBedrockIdentifier("minecraft:wood").withBedrockData(3); }
|
||||||
|
case "minecraft:acacia_wood" -> { return mapping.withBedrockIdentifier("minecraft:wood").withBedrockData(4); }
|
||||||
|
case "minecraft:dark_oak_wood" -> { return mapping.withBedrockIdentifier("minecraft:wood").withBedrockData(5); }
|
||||||
|
case "minecraft:stripped_oak_wood" -> { return mapping.withBedrockIdentifier("minecraft:wood").withBedrockData(8); }
|
||||||
|
case "minecraft:stripped_spruce_wood" -> { return mapping.withBedrockIdentifier("minecraft:wood").withBedrockData(9); }
|
||||||
|
case "minecraft:stripped_birch_wood" -> { return mapping.withBedrockIdentifier("minecraft:wood").withBedrockData(10); }
|
||||||
|
case "minecraft:stripped_jungle_wood" -> { return mapping.withBedrockIdentifier("minecraft:wood").withBedrockData(11); }
|
||||||
|
case "minecraft:stripped_acacia_wood" -> { return mapping.withBedrockIdentifier("minecraft:wood").withBedrockData(12); }
|
||||||
|
case "minecraft:stripped_dark_oak_wood" -> { return mapping.withBedrockIdentifier("minecraft:wood").withBedrockData(13); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NEW_SLABS.contains(identifer)) {
|
||||||
|
switch (identifer) {
|
||||||
|
case "minecraft:oak_slab" -> { return mapping.withBedrockIdentifier("minecraft:wooden_slab").withBedrockData(0); }
|
||||||
|
case "minecraft:spruce_slab" -> { return mapping.withBedrockIdentifier("minecraft:wooden_slab").withBedrockData(1); }
|
||||||
|
case "minecraft:birch_slab" -> { return mapping.withBedrockIdentifier("minecraft:wooden_slab").withBedrockData(2); }
|
||||||
|
case "minecraft:jungle_slab" -> { return mapping.withBedrockIdentifier("minecraft:wooden_slab").withBedrockData(3); }
|
||||||
|
case "minecraft:acacia_slab" -> { return mapping.withBedrockIdentifier("minecraft:wooden_slab").withBedrockData(4); }
|
||||||
|
case "minecraft:dark_oak_slab" -> { return mapping.withBedrockIdentifier("minecraft:wooden_slab").withBedrockData(5); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NEW_LEAVES.contains(identifer) || NEW_LEAVES2.contains(identifer)) {
|
||||||
|
switch (identifer) {
|
||||||
|
case "minecraft:oak_leaves" -> { return mapping.withBedrockIdentifier("minecraft:leaves").withBedrockData(0); }
|
||||||
|
case "minecraft:spruce_leaves" -> { return mapping.withBedrockIdentifier("minecraft:leaves").withBedrockData(1); }
|
||||||
|
case "minecraft:birch_leaves" -> { return mapping.withBedrockIdentifier("minecraft:leaves").withBedrockData(2); }
|
||||||
|
case "minecraft:jungle_leaves" -> { return mapping.withBedrockIdentifier("minecraft:leaves").withBedrockData(3); }
|
||||||
|
case "minecraft:acacia_leaves" -> { return mapping.withBedrockIdentifier("minecraft:leaves2").withBedrockData(0); }
|
||||||
|
case "minecraft:dark_oak_leaves" -> { return mapping.withBedrockIdentifier("minecraft:leaves2").withBedrockData(1); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NbtMap remapBlock(NbtMap tag) {
|
||||||
|
final String name = tag.getString("name");
|
||||||
|
|
||||||
|
if (!NEW_BLOCKS.contains(name)) {
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
String replacement;
|
||||||
|
|
||||||
|
if (name.equals("minecraft:grass_block")) {
|
||||||
|
replacement = "minecraft:grass";
|
||||||
|
|
||||||
|
NbtMapBuilder builder = tag.toBuilder();
|
||||||
|
builder.putString("name", replacement);
|
||||||
|
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NEW_WOODS.contains(name)) {
|
||||||
|
replacement = "minecraft:wood";
|
||||||
|
|
||||||
|
NbtMap states = tag.getCompound("states");
|
||||||
|
boolean stripped = name.startsWith("minecraft:stripped_");
|
||||||
|
String woodType = name.replaceAll("minecraft:|_wood|stripped_", "");
|
||||||
|
|
||||||
|
NbtMapBuilder statesBuilder = states.toBuilder()
|
||||||
|
.putString("wood_type", woodType)
|
||||||
|
.putBoolean("stripped_bit", stripped);
|
||||||
|
|
||||||
|
NbtMapBuilder builder = tag.toBuilder()
|
||||||
|
.putString("name", replacement)
|
||||||
|
.putCompound("states", statesBuilder.build());
|
||||||
|
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NEW_LEAVES.contains(name) || NEW_LEAVES2.contains(name)) {
|
||||||
|
boolean leaves2 = NEW_LEAVES2.contains(name);
|
||||||
|
replacement = leaves2 ? "minecraft:leaves2" : "minecraft:leaves";
|
||||||
|
|
||||||
|
NbtMap states = tag.getCompound("states");
|
||||||
|
String leafType = name.replaceAll("minecraft:|_leaves", "");
|
||||||
|
|
||||||
|
NbtMapBuilder statesBuilder = states.toBuilder()
|
||||||
|
.putString(leaves2 ? "new_leaf_type" : "old_leaf_type", leafType);
|
||||||
|
|
||||||
|
NbtMapBuilder builder = tag.toBuilder()
|
||||||
|
.putString("name", replacement)
|
||||||
|
.putCompound("states", statesBuilder.build());
|
||||||
|
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (NEW_SLABS.contains(name)) {
|
||||||
|
replacement = name.contains("double") ? "minecraft:double_wooden_slab" : "minecraft:wooden_slab";
|
||||||
|
|
||||||
|
NbtMap states = tag.getCompound("states");
|
||||||
|
String woodType = name.replaceAll("minecraft:|_double|_slab", "");
|
||||||
|
|
||||||
|
NbtMapBuilder statesBuilder = states.toBuilder()
|
||||||
|
.putString("wood_type", woodType);
|
||||||
|
|
||||||
|
NbtMapBuilder builder = tag.toBuilder()
|
||||||
|
.putString("name", replacement)
|
||||||
|
.putCompound("states", statesBuilder.build());
|
||||||
|
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
}
|
|
@ -422,10 +422,6 @@ public class CustomBlockRegistryPopulator {
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (components.unitCube()) {
|
|
||||||
builder.putCompound("minecraft:unit_cube", NbtMap.EMPTY);
|
|
||||||
}
|
|
||||||
|
|
||||||
// place_air is not an actual component
|
// place_air is not an actual component
|
||||||
// We just apply a dummy event to prevent the client from trying to place a block
|
// We just apply a dummy event to prevent the client from trying to place a block
|
||||||
// This mitigates the issue with the client sometimes double placing blocks
|
// This mitigates the issue with the client sometimes double placing blocks
|
||||||
|
|
|
@ -246,13 +246,6 @@ public class CustomItemRegistryPopulator {
|
||||||
|
|
||||||
computeRenderOffsets(isHat, customItemData, componentBuilder);
|
computeRenderOffsets(isHat, customItemData, componentBuilder);
|
||||||
|
|
||||||
if (creativeGroup != null) {
|
|
||||||
itemProperties.putString("creative_group", creativeGroup);
|
|
||||||
}
|
|
||||||
if (creativeCategory.isPresent()) {
|
|
||||||
itemProperties.putInt("creative_category", creativeCategory.getAsInt());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (customItemData.isFoil()) {
|
if (customItemData.isFoil()) {
|
||||||
itemProperties.putBoolean("foil", true);
|
itemProperties.putBoolean("foil", true);
|
||||||
}
|
}
|
||||||
|
@ -278,6 +271,14 @@ public class CustomItemRegistryPopulator {
|
||||||
}
|
}
|
||||||
itemProperties.putCompound("minecraft:icon", iconMap);
|
itemProperties.putCompound("minecraft:icon", iconMap);
|
||||||
|
|
||||||
|
if (customItemData.creativeCategory().isPresent()) {
|
||||||
|
itemProperties.putInt("creative_category", customItemData.creativeCategory().getAsInt());
|
||||||
|
|
||||||
|
if (customItemData.creativeGroup() != null) {
|
||||||
|
itemProperties.putString("creative_group", customItemData.creativeGroup());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
componentBuilder.putCompound("minecraft:display_name", NbtMap.builder().putString("value", customItemData.displayName()).build());
|
componentBuilder.putCompound("minecraft:display_name", NbtMap.builder().putString("value", customItemData.displayName()).build());
|
||||||
|
|
||||||
// Add a Geyser tag to the item, allowing Molang queries
|
// Add a Geyser tag to the item, allowing Molang queries
|
||||||
|
@ -370,21 +371,33 @@ public class CustomItemRegistryPopulator {
|
||||||
componentBuilder.putString("minecraft:render_offsets", "boots");
|
componentBuilder.putString("minecraft:render_offsets", "boots");
|
||||||
componentBuilder.putCompound("minecraft:wearable", WearableSlot.FEET.getSlotNbt());
|
componentBuilder.putCompound("minecraft:wearable", WearableSlot.FEET.getSlotNbt());
|
||||||
componentBuilder.putCompound("minecraft:armor", NbtMap.builder().putInt("protection", protectionValue).build());
|
componentBuilder.putCompound("minecraft:armor", NbtMap.builder().putInt("protection", protectionValue).build());
|
||||||
|
|
||||||
|
itemProperties.putString("enchantable_slot", "armor_feet");
|
||||||
|
itemProperties.putInt("enchantable_value", 15);
|
||||||
}
|
}
|
||||||
case "chestplate" -> {
|
case "chestplate" -> {
|
||||||
componentBuilder.putString("minecraft:render_offsets", "chestplates");
|
componentBuilder.putString("minecraft:render_offsets", "chestplates");
|
||||||
componentBuilder.putCompound("minecraft:wearable", WearableSlot.CHEST.getSlotNbt());
|
componentBuilder.putCompound("minecraft:wearable", WearableSlot.CHEST.getSlotNbt());
|
||||||
componentBuilder.putCompound("minecraft:armor", NbtMap.builder().putInt("protection", protectionValue).build());
|
componentBuilder.putCompound("minecraft:armor", NbtMap.builder().putInt("protection", protectionValue).build());
|
||||||
|
|
||||||
|
itemProperties.putString("enchantable_slot", "armor_torso");
|
||||||
|
itemProperties.putInt("enchantable_value", 15);
|
||||||
}
|
}
|
||||||
case "leggings" -> {
|
case "leggings" -> {
|
||||||
componentBuilder.putString("minecraft:render_offsets", "leggings");
|
componentBuilder.putString("minecraft:render_offsets", "leggings");
|
||||||
componentBuilder.putCompound("minecraft:wearable", WearableSlot.LEGS.getSlotNbt());
|
componentBuilder.putCompound("minecraft:wearable", WearableSlot.LEGS.getSlotNbt());
|
||||||
componentBuilder.putCompound("minecraft:armor", NbtMap.builder().putInt("protection", protectionValue).build());
|
componentBuilder.putCompound("minecraft:armor", NbtMap.builder().putInt("protection", protectionValue).build());
|
||||||
|
|
||||||
|
itemProperties.putString("enchantable_slot", "armor_legs");
|
||||||
|
itemProperties.putInt("enchantable_value", 15);
|
||||||
}
|
}
|
||||||
case "helmet" -> {
|
case "helmet" -> {
|
||||||
componentBuilder.putString("minecraft:render_offsets", "helmets");
|
componentBuilder.putString("minecraft:render_offsets", "helmets");
|
||||||
componentBuilder.putCompound("minecraft:wearable", WearableSlot.HEAD.getSlotNbt());
|
componentBuilder.putCompound("minecraft:wearable", WearableSlot.HEAD.getSlotNbt());
|
||||||
componentBuilder.putCompound("minecraft:armor", NbtMap.builder().putInt("protection", protectionValue).build());
|
componentBuilder.putCompound("minecraft:armor", NbtMap.builder().putInt("protection", protectionValue).build());
|
||||||
|
|
||||||
|
itemProperties.putString("enchantable_slot", "armor_head");
|
||||||
|
itemProperties.putInt("enchantable_value", 15);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,7 @@ import org.cloudburstmc.nbt.NbtType;
|
||||||
import org.cloudburstmc.protocol.bedrock.codec.v622.Bedrock_v622;
|
import org.cloudburstmc.protocol.bedrock.codec.v622.Bedrock_v622;
|
||||||
import org.cloudburstmc.protocol.bedrock.codec.v630.Bedrock_v630;
|
import org.cloudburstmc.protocol.bedrock.codec.v630.Bedrock_v630;
|
||||||
import org.cloudburstmc.protocol.bedrock.codec.v649.Bedrock_v649;
|
import org.cloudburstmc.protocol.bedrock.codec.v649.Bedrock_v649;
|
||||||
|
import org.cloudburstmc.protocol.bedrock.codec.v662.Bedrock_v662;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.SoundEvent;
|
import org.cloudburstmc.protocol.bedrock.data.SoundEvent;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
|
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.definitions.ItemDefinition;
|
import org.cloudburstmc.protocol.bedrock.data.definitions.ItemDefinition;
|
||||||
|
@ -90,8 +91,9 @@ public class ItemRegistryPopulator {
|
||||||
public static void populate() {
|
public static void populate() {
|
||||||
List<PaletteVersion> paletteVersions = new ArrayList<>(3);
|
List<PaletteVersion> paletteVersions = new ArrayList<>(3);
|
||||||
paletteVersions.add(new PaletteVersion("1_20_40", Bedrock_v622.CODEC.getProtocolVersion(), Collections.emptyMap(), Conversion630_622::remapItem));
|
paletteVersions.add(new PaletteVersion("1_20_40", Bedrock_v622.CODEC.getProtocolVersion(), Collections.emptyMap(), Conversion630_622::remapItem));
|
||||||
paletteVersions.add(new PaletteVersion("1_20_50", Bedrock_v630.CODEC.getProtocolVersion()));
|
paletteVersions.add(new PaletteVersion("1_20_50", Bedrock_v630.CODEC.getProtocolVersion(), Collections.emptyMap(), Conversion649_630::remapItem));
|
||||||
paletteVersions.add(new PaletteVersion("1_20_60", Bedrock_v649.CODEC.getProtocolVersion(), Collections.emptyMap(), Conversion630_649::remapItem));
|
paletteVersions.add(new PaletteVersion("1_20_60", Bedrock_v649.CODEC.getProtocolVersion(), Collections.emptyMap(), Conversion662_649::remapItem));
|
||||||
|
paletteVersions.add(new PaletteVersion("1_20_70", Bedrock_v662.CODEC.getProtocolVersion()));
|
||||||
|
|
||||||
GeyserBootstrap bootstrap = GeyserImpl.getInstance().getBootstrap();
|
GeyserBootstrap bootstrap = GeyserImpl.getInstance().getBootstrap();
|
||||||
|
|
||||||
|
@ -425,6 +427,16 @@ public class ItemRegistryPopulator {
|
||||||
GeyserCustomMappingData customMapping = CustomItemRegistryPopulator.registerCustomItem(
|
GeyserCustomMappingData customMapping = CustomItemRegistryPopulator.registerCustomItem(
|
||||||
customItemName, javaItem, mappingItem, customItem, customProtocolId, palette.protocolVersion
|
customItemName, javaItem, mappingItem, customItem, customProtocolId, palette.protocolVersion
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (customItem.creativeCategory().isPresent()) {
|
||||||
|
creativeItems.add(ItemData.builder()
|
||||||
|
.netId(creativeNetId.incrementAndGet())
|
||||||
|
.definition(customMapping.itemDefinition())
|
||||||
|
.blockDefinition(null)
|
||||||
|
.count(1)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
// ComponentItemData - used to register some custom properties
|
// ComponentItemData - used to register some custom properties
|
||||||
componentItemData.add(customMapping.componentItemData());
|
componentItemData.add(customMapping.componentItemData());
|
||||||
customItemOptions.add(Pair.of(customItem.customItemOptions(), customMapping.itemDefinition()));
|
customItemOptions.add(Pair.of(customItem.customItemOptions(), customMapping.itemDefinition()));
|
||||||
|
@ -523,7 +535,7 @@ public class ItemRegistryPopulator {
|
||||||
mappings.set(javaItem.javaId(), mapping);
|
mappings.set(javaItem.javaId(), mapping);
|
||||||
registry.put(customItemId, mapping.getBedrockDefinition());
|
registry.put(customItemId, mapping.getBedrockDefinition());
|
||||||
|
|
||||||
if (customItem.creativeGroup() != null || customItem.creativeCategory().isPresent()) {
|
if (customItem.creativeCategory().isPresent()) {
|
||||||
creativeItems.add(ItemData.builder()
|
creativeItems.add(ItemData.builder()
|
||||||
.definition(registration.mapping().getBedrockDefinition())
|
.definition(registration.mapping().getBedrockDefinition())
|
||||||
.netId(creativeNetId.incrementAndGet())
|
.netId(creativeNetId.incrementAndGet())
|
||||||
|
|
|
@ -124,6 +124,7 @@ import org.cloudburstmc.protocol.bedrock.data.command.CommandEnumData;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.command.CommandPermission;
|
import org.cloudburstmc.protocol.bedrock.data.command.CommandPermission;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.command.SoftEnumUpdateType;
|
import org.cloudburstmc.protocol.bedrock.data.command.SoftEnumUpdateType;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||||
|
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.AvailableEntityIdentifiersPacket;
|
import org.cloudburstmc.protocol.bedrock.packet.AvailableEntityIdentifiersPacket;
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket;
|
import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket;
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.BiomeDefinitionListPacket;
|
import org.cloudburstmc.protocol.bedrock.packet.BiomeDefinitionListPacket;
|
||||||
|
@ -335,7 +336,7 @@ public class GeyserSession extends FloodgateConnection implements GeyserConnecti
|
||||||
* See {@link WorldManager#sendLecternData(GeyserSession, int, int, int)}
|
* See {@link WorldManager#sendLecternData(GeyserSession, int, int, int)}
|
||||||
* for more information.
|
* for more information.
|
||||||
*/
|
*/
|
||||||
private final Set<Vector3i> lecternCache;
|
private final @Nullable Set<Vector3i> lecternCache;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A list of all players that have a player head on with a custom texture.
|
* A list of all players that have a player head on with a custom texture.
|
||||||
|
@ -646,6 +647,12 @@ public class GeyserSession extends FloodgateConnection implements GeyserConnecti
|
||||||
*/
|
*/
|
||||||
private final Queue<Long> keepAliveCache = new ConcurrentLinkedQueue<>();
|
private final Queue<Long> keepAliveCache = new ConcurrentLinkedQueue<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the book that is currently being read. Used in {@link org.geysermc.geyser.translator.protocol.java.inventory.JavaOpenBookTranslator}
|
||||||
|
*/
|
||||||
|
@Setter
|
||||||
|
private @Nullable ItemData currentBook = null;
|
||||||
|
|
||||||
private final GeyserCameraData cameraData;
|
private final GeyserCameraData cameraData;
|
||||||
|
|
||||||
private final GeyserEntityData entityData;
|
private final GeyserEntityData entityData;
|
||||||
|
@ -1487,6 +1494,8 @@ public class GeyserSession extends FloodgateConnection implements GeyserConnecti
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setServerRenderDistance(int renderDistance) {
|
public void setServerRenderDistance(int renderDistance) {
|
||||||
|
// Ensure render distance is not above 96 as sending a larger value at any point crashes mobile clients and 96 is the max of any bedrock platform
|
||||||
|
renderDistance = Math.min(renderDistance, 96);
|
||||||
this.serverRenderDistance = renderDistance;
|
this.serverRenderDistance = renderDistance;
|
||||||
|
|
||||||
ChunkRadiusUpdatedPacket chunkRadiusUpdatedPacket = new ChunkRadiusUpdatedPacket();
|
ChunkRadiusUpdatedPacket chunkRadiusUpdatedPacket = new ChunkRadiusUpdatedPacket();
|
||||||
|
|
|
@ -36,36 +36,71 @@ import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||||
import org.cloudburstmc.nbt.NbtType;
|
import org.cloudburstmc.nbt.NbtType;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
|
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
|
||||||
import org.geysermc.erosion.util.LecternUtils;
|
import org.geysermc.erosion.util.LecternUtils;
|
||||||
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
|
import org.geysermc.geyser.inventory.Container;
|
||||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||||
import org.geysermc.geyser.inventory.Inventory;
|
import org.geysermc.geyser.inventory.Inventory;
|
||||||
import org.geysermc.geyser.inventory.LecternContainer;
|
import org.geysermc.geyser.inventory.LecternContainer;
|
||||||
import org.geysermc.geyser.inventory.PlayerInventory;
|
import org.geysermc.geyser.inventory.PlayerInventory;
|
||||||
import org.geysermc.geyser.inventory.updater.InventoryUpdater;
|
import org.geysermc.geyser.inventory.updater.ContainerInventoryUpdater;
|
||||||
|
import org.geysermc.geyser.network.GameProtocol;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.util.BlockEntityUtils;
|
import org.geysermc.geyser.util.BlockEntityUtils;
|
||||||
import org.geysermc.geyser.util.InventoryUtils;
|
import org.geysermc.geyser.util.InventoryUtils;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
public class LecternInventoryTranslator extends BaseInventoryTranslator {
|
public class LecternInventoryTranslator extends AbstractBlockInventoryTranslator {
|
||||||
private final InventoryUpdater updater;
|
|
||||||
|
/**
|
||||||
|
* Hack: Java opens a lectern first, and then follows it up with a ClientboundContainerSetContentPacket
|
||||||
|
* to actually send the book's contents. We delay opening the inventory until the book was sent.
|
||||||
|
*/
|
||||||
|
private boolean initialized = false;
|
||||||
|
|
||||||
public LecternInventoryTranslator() {
|
public LecternInventoryTranslator() {
|
||||||
super(1);
|
super(1, "minecraft:lectern[facing=north,has_book=true,powered=true]", org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType.LECTERN , ContainerInventoryUpdater.INSTANCE);
|
||||||
this.updater = new InventoryUpdater();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean prepareInventory(GeyserSession session, Inventory inventory) {
|
public boolean prepareInventory(GeyserSession session, Inventory inventory) {
|
||||||
|
super.prepareInventory(session, inventory);
|
||||||
|
if (((Container) inventory).isUsingRealBlock()) {
|
||||||
|
initialized = false; // We have to wait until we get the book to show to the client
|
||||||
|
} else {
|
||||||
|
updateBook(session, inventory, inventory.getItem(0)); // See JavaOpenBookTranslator; placed here manually
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void openInventory(GeyserSession session, Inventory inventory) {
|
public void openInventory(GeyserSession session, Inventory inventory) {
|
||||||
|
// Hacky, but we're dealing with LECTERNS! It cannot not be hacky.
|
||||||
|
// "initialized" indicates whether we've received the book from the Java server yet.
|
||||||
|
// dropping lectern book is the fun workaround when we have to enter the gui to drop the book.
|
||||||
|
// Since we leave it immediately... don't open it!
|
||||||
|
if (initialized && !session.isDroppingLecternBook()) {
|
||||||
|
super.openInventory(session, inventory);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void closeInventory(GeyserSession session, Inventory inventory) {
|
public void closeInventory(GeyserSession session, Inventory inventory) {
|
||||||
|
// Of course, sending a simple ContainerClosePacket, or even breaking the block doesn't work to close a lectern.
|
||||||
|
// Heck, the latter crashes the client xd
|
||||||
|
// BDS just sends an empty base lectern tag... that kicks out the client. Fine. Let's do that!
|
||||||
|
LecternContainer lecternContainer = (LecternContainer) inventory;
|
||||||
|
Vector3i position = lecternContainer.isUsingRealBlock() ? session.getLastInteractionBlockPosition() : inventory.getHolderPosition();
|
||||||
|
var baseLecternTag = LecternUtils.getBaseLecternTag(position.getX(), position.getY(), position.getZ(), 0);
|
||||||
|
BlockEntityUtils.updateBlockEntity(session, baseLecternTag.build(), position);
|
||||||
|
|
||||||
|
super.closeInventory(session, inventory); // Removes the fake blocks if need be
|
||||||
|
|
||||||
|
// Now: Restore the lectern, if it actually exists
|
||||||
|
if (lecternContainer.isUsingRealBlock()) {
|
||||||
|
GeyserImpl.getInstance().getWorldManager().sendLecternData(session, position.getX(), position.getY(), position.getZ());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -82,13 +117,24 @@ public class LecternInventoryTranslator extends BaseInventoryTranslator {
|
||||||
public void updateInventory(GeyserSession session, Inventory inventory) {
|
public void updateInventory(GeyserSession session, Inventory inventory) {
|
||||||
GeyserItemStack itemStack = inventory.getItem(0);
|
GeyserItemStack itemStack = inventory.getItem(0);
|
||||||
if (!itemStack.isEmpty()) {
|
if (!itemStack.isEmpty()) {
|
||||||
|
boolean isDropping = session.isDroppingLecternBook();
|
||||||
updateBook(session, inventory, itemStack);
|
updateBook(session, inventory, itemStack);
|
||||||
|
|
||||||
|
if (!initialized && !isDropping) {
|
||||||
|
initialized = true;
|
||||||
|
openInventory(session, inventory);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateSlot(GeyserSession session, Inventory inventory, int slot) {
|
public void updateSlot(GeyserSession session, Inventory inventory, int slot) {
|
||||||
this.updater.updateSlot(this, session, inventory, slot);
|
// If we're not in a real lectern, the Java server thinks we are still in the player inventory.
|
||||||
|
if (((LecternContainer) inventory).isFakeLectern()) {
|
||||||
|
InventoryTranslator.PLAYER_INVENTORY_TRANSLATOR.updateSlot(session, session.getPlayerInventory(), slot);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
super.updateSlot(session, inventory, slot);
|
||||||
if (slot == 0) {
|
if (slot == 0) {
|
||||||
updateBook(session, inventory, inventory.getItem(0));
|
updateBook(session, inventory, inventory.getItem(0));
|
||||||
}
|
}
|
||||||
|
@ -107,11 +153,14 @@ public class LecternInventoryTranslator extends BaseInventoryTranslator {
|
||||||
InventoryUtils.closeInventory(session, inventory.getJavaId(), false);
|
InventoryUtils.closeInventory(session, inventory.getJavaId(), false);
|
||||||
} else if (lecternContainer.getBlockEntityTag() == null) {
|
} else if (lecternContainer.getBlockEntityTag() == null) {
|
||||||
CompoundTag tag = book.getNbt();
|
CompoundTag tag = book.getNbt();
|
||||||
// Position has to be the last interacted position... right?
|
Vector3i position = lecternContainer.isUsingRealBlock() ? session.getLastInteractionBlockPosition() : inventory.getHolderPosition();
|
||||||
Vector3i position = session.getLastInteractionBlockPosition();
|
|
||||||
// If shouldExpectLecternHandled returns true, this is already handled for us
|
// If shouldExpectLecternHandled returns true, this is already handled for us
|
||||||
// shouldRefresh means that we should boot out the client on our side because their lectern GUI isn't updated yet
|
// shouldRefresh means that we should boot out the client on our side because their lectern GUI isn't updated yet
|
||||||
boolean shouldRefresh = !session.getGeyser().getWorldManager().shouldExpectLecternHandled(session) && !session.getLecternCache().contains(position);
|
// TODO: yeet after 1.20.60 is minimum supported version
|
||||||
|
boolean shouldRefresh = !session.getGeyser().getWorldManager().shouldExpectLecternHandled(session)
|
||||||
|
&& !session.getLecternCache().contains(position)
|
||||||
|
&& !GameProtocol.is1_20_60orHigher(session.getUpstream().getProtocolVersion());
|
||||||
|
|
||||||
NbtMap blockEntityTag;
|
NbtMap blockEntityTag;
|
||||||
if (tag != null) {
|
if (tag != null) {
|
||||||
|
@ -147,10 +196,13 @@ public class LecternInventoryTranslator extends BaseInventoryTranslator {
|
||||||
// the block entity tag
|
// the block entity tag
|
||||||
lecternContainer.setBlockEntityTag(blockEntityTag);
|
lecternContainer.setBlockEntityTag(blockEntityTag);
|
||||||
lecternContainer.setPosition(position);
|
lecternContainer.setPosition(position);
|
||||||
|
|
||||||
|
BlockEntityUtils.updateBlockEntity(session, blockEntityTag, position);
|
||||||
|
|
||||||
if (shouldRefresh) {
|
if (shouldRefresh) {
|
||||||
// Update the lectern because it's not updated client-side
|
// the lectern cache doesn't always exist; only when we must refresh
|
||||||
BlockEntityUtils.updateBlockEntity(session, blockEntityTag, position);
|
|
||||||
session.getLecternCache().add(position);
|
session.getLecternCache().add(position);
|
||||||
|
|
||||||
// Close the window - we will reopen it once the client has this data synced
|
// Close the window - we will reopen it once the client has this data synced
|
||||||
ServerboundContainerClosePacket closeWindowPacket = new ServerboundContainerClosePacket(lecternContainer.getJavaId());
|
ServerboundContainerClosePacket closeWindowPacket = new ServerboundContainerClosePacket(lecternContainer.getJavaId());
|
||||||
session.sendDownstreamGamePacket(closeWindowPacket);
|
session.sendDownstreamGamePacket(closeWindowPacket);
|
||||||
|
@ -161,6 +213,6 @@ public class LecternInventoryTranslator extends BaseInventoryTranslator {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Inventory createInventory(String name, int windowId, ContainerType containerType, PlayerInventory playerInventory) {
|
public Inventory createInventory(String name, int windowId, ContainerType containerType, PlayerInventory playerInventory) {
|
||||||
return new LecternContainer(name, windowId, this.size, containerType, playerInventory);
|
return new LecternContainer(name, windowId, this.size + playerInventory.getSize(), containerType, playerInventory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,8 @@ import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action
|
||||||
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.SwapAction;
|
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.SwapAction;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.TransferItemStackRequestAction;
|
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.TransferItemStackRequestAction;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.response.ItemStackResponse;
|
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.response.ItemStackResponse;
|
||||||
|
import org.cloudburstmc.protocol.bedrock.packet.ContainerClosePacket;
|
||||||
|
import org.cloudburstmc.protocol.bedrock.packet.ContainerOpenPacket;
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.InventoryContentPacket;
|
import org.cloudburstmc.protocol.bedrock.packet.InventoryContentPacket;
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.InventorySlotPacket;
|
import org.cloudburstmc.protocol.bedrock.packet.InventorySlotPacket;
|
||||||
import org.geysermc.geyser.inventory.*;
|
import org.geysermc.geyser.inventory.*;
|
||||||
|
@ -534,10 +536,20 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void openInventory(GeyserSession session, Inventory inventory) {
|
public void openInventory(GeyserSession session, Inventory inventory) {
|
||||||
|
ContainerOpenPacket containerOpenPacket = new ContainerOpenPacket();
|
||||||
|
containerOpenPacket.setId((byte) 0);
|
||||||
|
containerOpenPacket.setType(org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType.INVENTORY);
|
||||||
|
containerOpenPacket.setUniqueEntityId(-1);
|
||||||
|
containerOpenPacket.setBlockPosition(session.getPlayerEntity().getPosition().toInt());
|
||||||
|
session.sendUpstreamPacket(containerOpenPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void closeInventory(GeyserSession session, Inventory inventory) {
|
public void closeInventory(GeyserSession session, Inventory inventory) {
|
||||||
|
ContainerClosePacket packet = new ContainerClosePacket();
|
||||||
|
packet.setServerInitiated(true);
|
||||||
|
packet.setId((byte) ContainerId.INVENTORY);
|
||||||
|
session.sendUpstreamPacket(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -380,6 +380,8 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
||||||
} else if (packet.getItemInHand().getDefinition() == session.getItemMappings().getStoredItems().glassBottle().getBedrockDefinition()) {
|
} else if (packet.getItemInHand().getDefinition() == session.getItemMappings().getStoredItems().glassBottle().getBedrockDefinition()) {
|
||||||
// Handled in case 0
|
// Handled in case 0
|
||||||
break;
|
break;
|
||||||
|
} else if (packet.getItemInHand().getDefinition() == session.getItemMappings().getStoredItems().writtenBook().getBedrockDefinition()) {
|
||||||
|
session.setCurrentBook(packet.getItemInHand());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -387,7 +389,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
||||||
session.sendDownstreamGamePacket(useItemPacket);
|
session.sendDownstreamGamePacket(useItemPacket);
|
||||||
|
|
||||||
List<LegacySetItemSlotData> legacySlots = packet.getLegacySlots();
|
List<LegacySetItemSlotData> legacySlots = packet.getLegacySlots();
|
||||||
if (packet.getActions().size() == 1 && legacySlots.size() > 0) {
|
if (packet.getActions().size() == 1 && !legacySlots.isEmpty()) {
|
||||||
InventoryActionData actionData = packet.getActions().get(0);
|
InventoryActionData actionData = packet.getActions().get(0);
|
||||||
LegacySetItemSlotData slotData = legacySlots.get(0);
|
LegacySetItemSlotData slotData = legacySlots.get(0);
|
||||||
if (slotData.getContainerId() == 6 && !actionData.getFromItem().isNull()) {
|
if (slotData.getContainerId() == 6 && !actionData.getFromItem().isNull()) {
|
||||||
|
|
|
@ -39,10 +39,12 @@ import org.geysermc.geyser.translator.protocol.Translator;
|
||||||
* Pre-1.16.210: used for both survival and creative item frame item removal
|
* Pre-1.16.210: used for both survival and creative item frame item removal
|
||||||
* <p>
|
* <p>
|
||||||
* 1.16.210: only used in creative.
|
* 1.16.210: only used in creative.
|
||||||
|
* 1.20.70: no longer used.
|
||||||
*/
|
*/
|
||||||
@Translator(packet = ItemFrameDropItemPacket.class)
|
@Translator(packet = ItemFrameDropItemPacket.class)
|
||||||
public class BedrockItemFrameDropItemTranslator extends PacketTranslator<ItemFrameDropItemPacket> {
|
public class BedrockItemFrameDropItemTranslator extends PacketTranslator<ItemFrameDropItemPacket> {
|
||||||
|
|
||||||
|
// TODO: Remove when 1.20.60 is no longer supported
|
||||||
@Override
|
@Override
|
||||||
public void translate(GeyserSession session, ItemFrameDropItemPacket packet) {
|
public void translate(GeyserSession session, ItemFrameDropItemPacket packet) {
|
||||||
Entity entity = ItemFrameEntity.getItemFrameEntity(session, packet.getBlockPosition());
|
Entity entity = ItemFrameEntity.getItemFrameEntity(session, packet.getBlockPosition());
|
||||||
|
|
|
@ -31,8 +31,10 @@ import com.github.steveice10.mc.protocol.packet.ingame.serverbound.inventory.Ser
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.inventory.ServerboundContainerClosePacket;
|
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.inventory.ServerboundContainerClosePacket;
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundUseItemOnPacket;
|
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundUseItemOnPacket;
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.LecternUpdatePacket;
|
import org.cloudburstmc.protocol.bedrock.packet.LecternUpdatePacket;
|
||||||
|
import org.geysermc.geyser.inventory.Inventory;
|
||||||
import org.geysermc.geyser.inventory.LecternContainer;
|
import org.geysermc.geyser.inventory.LecternContainer;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
import org.geysermc.geyser.translator.inventory.LecternInventoryTranslator;
|
||||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||||
import org.geysermc.geyser.translator.protocol.Translator;
|
import org.geysermc.geyser.translator.protocol.Translator;
|
||||||
import org.geysermc.geyser.util.InventoryUtils;
|
import org.geysermc.geyser.util.InventoryUtils;
|
||||||
|
@ -45,6 +47,7 @@ public class BedrockLecternUpdateTranslator extends PacketTranslator<LecternUpda
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translate(GeyserSession session, LecternUpdatePacket packet) {
|
public void translate(GeyserSession session, LecternUpdatePacket packet) {
|
||||||
|
// TODO: Remove dropping book check here when 1.20.60 is no longer supported
|
||||||
if (packet.isDroppingBook()) {
|
if (packet.isDroppingBook()) {
|
||||||
// Bedrock drops the book outside of the GUI. Java drops it in the GUI
|
// Bedrock drops the book outside of the GUI. Java drops it in the GUI
|
||||||
// So, we enter the GUI and then drop it! :)
|
// So, we enter the GUI and then drop it! :)
|
||||||
|
@ -77,6 +80,15 @@ public class BedrockLecternUpdateTranslator extends PacketTranslator<LecternUpda
|
||||||
int newJavaPage = (packet.getPage() * 2);
|
int newJavaPage = (packet.getPage() * 2);
|
||||||
int currentJavaPage = (lecternContainer.getCurrentBedrockPage() * 2);
|
int currentJavaPage = (lecternContainer.getCurrentBedrockPage() * 2);
|
||||||
|
|
||||||
|
// So, fun fact: We need to separately handle fake lecterns!
|
||||||
|
// Since those are not actually a real lectern... the Java server won't respond to our requests.
|
||||||
|
if (!lecternContainer.isUsingRealBlock()) {
|
||||||
|
LecternInventoryTranslator translator = (LecternInventoryTranslator) session.getInventoryTranslator();
|
||||||
|
Inventory inventory = session.getOpenInventory();
|
||||||
|
translator.updateProperty(session, inventory, 0, newJavaPage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Send as many click button packets as we need to
|
// Send as many click button packets as we need to
|
||||||
// Java has the option to specify exact page numbers by adding 100 to the number, but buttonId variable
|
// Java has the option to specify exact page numbers by adding 100 to the number, but buttonId variable
|
||||||
// is a byte when transmitted over the network and therefore this stops us at 128
|
// is a byte when transmitted over the network and therefore this stops us at 128
|
||||||
|
|
|
@ -46,6 +46,7 @@ import org.geysermc.geyser.entity.type.ItemFrameEntity;
|
||||||
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
|
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
|
||||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||||
|
import org.geysermc.geyser.network.GameProtocol;
|
||||||
import org.geysermc.geyser.registry.BlockRegistries;
|
import org.geysermc.geyser.registry.BlockRegistries;
|
||||||
import org.geysermc.geyser.registry.type.BlockMapping;
|
import org.geysermc.geyser.registry.type.BlockMapping;
|
||||||
import org.geysermc.geyser.registry.type.ItemMapping;
|
import org.geysermc.geyser.registry.type.ItemMapping;
|
||||||
|
@ -299,39 +300,70 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case START_FLYING: // Since 1.20.30
|
case START_FLYING: // Since 1.20.30
|
||||||
if (session.isCanFly()) {
|
if (session.isCanFly()) {
|
||||||
if (session.getGameMode() == GameMode.SPECTATOR) {
|
if (session.getGameMode() == GameMode.SPECTATOR) {
|
||||||
// should already be flying
|
// should already be flying
|
||||||
session.sendAdventureSettings();
|
session.sendAdventureSettings();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (session.getPlayerEntity().getFlag(EntityFlag.SWIMMING) && session.getCollisionManager().isPlayerInWater()) {
|
if (session.getPlayerEntity().getFlag(EntityFlag.SWIMMING) && session.getCollisionManager().isPlayerInWater()) {
|
||||||
// As of 1.18.1, Java Edition cannot fly while in water, but it can fly while crawling
|
// As of 1.18.1, Java Edition cannot fly while in water, but it can fly while crawling
|
||||||
// If this isn't present, swimming on a 1.13.2 server and then attempting to fly will put you into a flying/swimming state that is invalid on JE
|
// If this isn't present, swimming on a 1.13.2 server and then attempting to fly will put you into a flying/swimming state that is invalid on JE
|
||||||
session.sendAdventureSettings();
|
session.sendAdventureSettings();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
session.setFlying(true);
|
session.setFlying(true);
|
||||||
session.sendDownstreamGamePacket(new ServerboundPlayerAbilitiesPacket(true));
|
session.sendDownstreamGamePacket(new ServerboundPlayerAbilitiesPacket(true));
|
||||||
} else {
|
} else {
|
||||||
// update whether we can fly
|
// update whether we can fly
|
||||||
session.sendAdventureSettings();
|
session.sendAdventureSettings();
|
||||||
// stop flying
|
// stop flying
|
||||||
PlayerActionPacket stopFlyingPacket = new PlayerActionPacket();
|
PlayerActionPacket stopFlyingPacket = new PlayerActionPacket();
|
||||||
stopFlyingPacket.setRuntimeEntityId(session.getPlayerEntity().getGeyserId());
|
stopFlyingPacket.setRuntimeEntityId(session.getPlayerEntity().getGeyserId());
|
||||||
stopFlyingPacket.setAction(PlayerActionType.STOP_FLYING);
|
stopFlyingPacket.setAction(PlayerActionType.STOP_FLYING);
|
||||||
stopFlyingPacket.setBlockPosition(Vector3i.ZERO);
|
stopFlyingPacket.setBlockPosition(Vector3i.ZERO);
|
||||||
stopFlyingPacket.setResultPosition(Vector3i.ZERO);
|
stopFlyingPacket.setResultPosition(Vector3i.ZERO);
|
||||||
stopFlyingPacket.setFace(0);
|
stopFlyingPacket.setFace(0);
|
||||||
session.sendUpstreamPacket(stopFlyingPacket);
|
session.sendUpstreamPacket(stopFlyingPacket);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case STOP_FLYING:
|
case STOP_FLYING:
|
||||||
session.setFlying(false);
|
session.setFlying(false);
|
||||||
session.sendDownstreamGamePacket(new ServerboundPlayerAbilitiesPacket(false));
|
session.sendDownstreamGamePacket(new ServerboundPlayerAbilitiesPacket(false));
|
||||||
break;
|
break;
|
||||||
|
case DIMENSION_CHANGE_REQUEST_OR_CREATIVE_DESTROY_BLOCK: // Used by client to get book from lecterns and items from item frame in creative mode since 1.20.70
|
||||||
|
if (GameProtocol.isPre1_20_70(session)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int interactedBlock = session.getGeyser().getWorldManager().getBlockAt(session, vector);
|
||||||
|
|
||||||
|
if (BlockStateValues.getLecternBookStates().getOrDefault(interactedBlock, false)) {
|
||||||
|
session.setDroppingLecternBook(true);
|
||||||
|
|
||||||
|
ServerboundUseItemOnPacket blockPacket = new ServerboundUseItemOnPacket(
|
||||||
|
vector,
|
||||||
|
Direction.DOWN,
|
||||||
|
Hand.MAIN_HAND,
|
||||||
|
0, 0, 0,
|
||||||
|
false,
|
||||||
|
session.getWorldCache().nextPredictionSequence());
|
||||||
|
session.sendDownstreamGamePacket(blockPacket);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (session.getItemFrameCache().containsKey(vector)) {
|
||||||
|
Entity itemFrame = ItemFrameEntity.getItemFrameEntity(session, packet.getBlockPosition());
|
||||||
|
|
||||||
|
if (itemFrame != null) {
|
||||||
|
ServerboundInteractPacket interactPacket = new ServerboundInteractPacket(itemFrame.getEntityId(),
|
||||||
|
InteractAction.ATTACK, Hand.MAIN_HAND, session.isSneaking());
|
||||||
|
session.sendDownstreamGamePacket(interactPacket);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,8 +33,6 @@ import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.Server
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundPlayerCommandPacket;
|
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundPlayerCommandPacket;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityLinkData;
|
import org.cloudburstmc.protocol.bedrock.data.entity.EntityLinkData;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType;
|
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.ContainerOpenPacket;
|
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.InteractPacket;
|
import org.cloudburstmc.protocol.bedrock.packet.InteractPacket;
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.SetEntityLinkPacket;
|
import org.cloudburstmc.protocol.bedrock.packet.SetEntityLinkPacket;
|
||||||
import org.geysermc.geyser.entity.type.Entity;
|
import org.geysermc.geyser.entity.type.Entity;
|
||||||
|
@ -43,6 +41,7 @@ import org.geysermc.geyser.item.Items;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||||
import org.geysermc.geyser.translator.protocol.Translator;
|
import org.geysermc.geyser.translator.protocol.Translator;
|
||||||
|
import org.geysermc.geyser.util.InventoryUtils;
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@ -125,15 +124,12 @@ public class BedrockInteractTranslator extends PacketTranslator<InteractPacket>
|
||||||
ServerboundPlayerCommandPacket openVehicleWindowPacket = new ServerboundPlayerCommandPacket(session.getPlayerEntity().getEntityId(), PlayerState.OPEN_VEHICLE_INVENTORY);
|
ServerboundPlayerCommandPacket openVehicleWindowPacket = new ServerboundPlayerCommandPacket(session.getPlayerEntity().getEntityId(), PlayerState.OPEN_VEHICLE_INVENTORY);
|
||||||
session.sendDownstreamGamePacket(openVehicleWindowPacket);
|
session.sendDownstreamGamePacket(openVehicleWindowPacket);
|
||||||
} else {
|
} else {
|
||||||
session.setOpenInventory(session.getPlayerInventory());
|
InventoryUtils.openInventory(session, session.getPlayerInventory());
|
||||||
|
|
||||||
ContainerOpenPacket containerOpenPacket = new ContainerOpenPacket();
|
|
||||||
containerOpenPacket.setId((byte) 0);
|
|
||||||
containerOpenPacket.setType(ContainerType.INVENTORY);
|
|
||||||
containerOpenPacket.setUniqueEntityId(-1);
|
|
||||||
containerOpenPacket.setBlockPosition(entity.getPosition().toInt());
|
|
||||||
session.sendUpstreamPacket(containerOpenPacket);
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Case: Player tries to open a player inventory, while we think it should be in a different inventory
|
||||||
|
// Now: Open the inventory that we're supposed to be in.
|
||||||
|
InventoryUtils.openInventory(session, session.getOpenInventory());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,11 +25,16 @@
|
||||||
|
|
||||||
package org.geysermc.geyser.translator.protocol.bedrock.world;
|
package org.geysermc.geyser.translator.protocol.bedrock.world;
|
||||||
|
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.entity.object.Direction;
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundSwingPacket;
|
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundSwingPacket;
|
||||||
|
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundUseItemOnPacket;
|
||||||
|
import org.cloudburstmc.math.vector.Vector3f;
|
||||||
|
import org.cloudburstmc.math.vector.Vector3i;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.SoundEvent;
|
import org.cloudburstmc.protocol.bedrock.data.SoundEvent;
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.AnimatePacket;
|
import org.cloudburstmc.protocol.bedrock.packet.AnimatePacket;
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEventPacket;
|
import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEventPacket;
|
||||||
|
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||||
import org.geysermc.geyser.translator.protocol.Translator;
|
import org.geysermc.geyser.translator.protocol.Translator;
|
||||||
|
@ -66,5 +71,26 @@ public class BedrockLevelSoundEventTranslator extends PacketTranslator<LevelSoun
|
||||||
animatePacket.setAction(AnimatePacket.Action.SWING_ARM);
|
animatePacket.setAction(AnimatePacket.Action.SWING_ARM);
|
||||||
session.sendUpstreamPacket(animatePacket);
|
session.sendUpstreamPacket(animatePacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Used by client to get book from lecterns in survial mode since 1.20.70
|
||||||
|
if (packet.getSound() == SoundEvent.HIT) {
|
||||||
|
Vector3f position = packet.getPosition();
|
||||||
|
Vector3i blockPosition = Vector3i.from(position.getX(), position.getY(), position.getZ());
|
||||||
|
|
||||||
|
int potentialLectern = session.getGeyser().getWorldManager().getBlockAt(session, blockPosition);
|
||||||
|
|
||||||
|
if (BlockStateValues.getLecternBookStates().getOrDefault(potentialLectern, false)) {
|
||||||
|
session.setDroppingLecternBook(true);
|
||||||
|
|
||||||
|
ServerboundUseItemOnPacket blockPacket = new ServerboundUseItemOnPacket(
|
||||||
|
blockPosition,
|
||||||
|
Direction.DOWN,
|
||||||
|
Hand.MAIN_HAND,
|
||||||
|
0, 0, 0,
|
||||||
|
false,
|
||||||
|
session.getWorldCache().nextPredictionSequence());
|
||||||
|
session.sendDownstreamGamePacket(blockPacket);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,6 @@ package org.geysermc.geyser.translator.protocol.java;
|
||||||
|
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundRecipePacket;
|
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundRecipePacket;
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.UnlockedRecipesPacket;
|
import org.cloudburstmc.protocol.bedrock.packet.UnlockedRecipesPacket;
|
||||||
import org.geysermc.geyser.network.GameProtocol;
|
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||||
import org.geysermc.geyser.translator.protocol.Translator;
|
import org.geysermc.geyser.translator.protocol.Translator;
|
||||||
|
@ -47,12 +46,22 @@ public class JavaClientboundRecipesTranslator extends PacketTranslator<Clientbou
|
||||||
recipesPacket.getUnlockedRecipes().addAll(getBedrockRecipes(session, packet.getAlreadyKnownRecipes()));
|
recipesPacket.getUnlockedRecipes().addAll(getBedrockRecipes(session, packet.getAlreadyKnownRecipes()));
|
||||||
}
|
}
|
||||||
case ADD -> {
|
case ADD -> {
|
||||||
|
List<String> recipes = getBedrockRecipes(session, packet.getRecipes());
|
||||||
|
if (recipes.isEmpty()) {
|
||||||
|
// Sending an empty list here packet will crash the client as of 1.20.60
|
||||||
|
return;
|
||||||
|
}
|
||||||
recipesPacket.setAction(UnlockedRecipesPacket.ActionType.NEWLY_UNLOCKED);
|
recipesPacket.setAction(UnlockedRecipesPacket.ActionType.NEWLY_UNLOCKED);
|
||||||
recipesPacket.getUnlockedRecipes().addAll(getBedrockRecipes(session, packet.getRecipes()));
|
recipesPacket.getUnlockedRecipes().addAll(recipes);
|
||||||
}
|
}
|
||||||
case REMOVE -> {
|
case REMOVE -> {
|
||||||
|
List<String> recipes = getBedrockRecipes(session, packet.getRecipes());
|
||||||
|
if (recipes.isEmpty()) {
|
||||||
|
// Sending an empty list here will crash the client as of 1.20.60
|
||||||
|
return;
|
||||||
|
}
|
||||||
recipesPacket.setAction(UnlockedRecipesPacket.ActionType.REMOVE_UNLOCKED);
|
recipesPacket.setAction(UnlockedRecipesPacket.ActionType.REMOVE_UNLOCKED);
|
||||||
recipesPacket.getUnlockedRecipes().addAll(getBedrockRecipes(session, packet.getRecipes()));
|
recipesPacket.getUnlockedRecipes().addAll(recipes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
session.sendUpstreamPacket(recipesPacket);
|
session.sendUpstreamPacket(recipesPacket);
|
||||||
|
@ -70,5 +79,4 @@ public class JavaClientboundRecipesTranslator extends PacketTranslator<Clientbou
|
||||||
}
|
}
|
||||||
return recipes;
|
return recipes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ package org.geysermc.geyser.translator.protocol.java.inventory;
|
||||||
|
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.inventory.ClientboundContainerSetContentPacket;
|
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.inventory.ClientboundContainerSetContentPacket;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
|
import org.geysermc.geyser.GeyserLogger;
|
||||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||||
import org.geysermc.geyser.inventory.Inventory;
|
import org.geysermc.geyser.inventory.Inventory;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
@ -48,12 +49,12 @@ public class JavaContainerSetContentTranslator extends PacketTranslator<Clientbo
|
||||||
int inventorySize = inventory.getSize();
|
int inventorySize = inventory.getSize();
|
||||||
for (int i = 0; i < packet.getItems().length; i++) {
|
for (int i = 0; i < packet.getItems().length; i++) {
|
||||||
if (i >= inventorySize) {
|
if (i >= inventorySize) {
|
||||||
GeyserImpl geyser = session.getGeyser();
|
GeyserLogger logger = session.getGeyser().getLogger();
|
||||||
geyser.getLogger().warning("ClientboundContainerSetContentPacket sent to " + session.bedrockUsername()
|
logger.warning("ClientboundContainerSetContentPacket sent to " + session.bedrockUsername()
|
||||||
+ " that exceeds inventory size!");
|
+ " that exceeds inventory size!");
|
||||||
if (geyser.getConfig().isDebugMode()) {
|
if (logger.isDebug()) {
|
||||||
geyser.getLogger().debug(packet);
|
logger.debug(packet);
|
||||||
geyser.getLogger().debug(inventory);
|
logger.debug(inventory);
|
||||||
}
|
}
|
||||||
updateInventory(session, inventory, packet.getContainerId());
|
updateInventory(session, inventory, packet.getContainerId());
|
||||||
// 1.18.1 behavior: the previous items will be correctly set, but the state ID and carried item will not
|
// 1.18.1 behavior: the previous items will be correctly set, but the state ID and carried item will not
|
||||||
|
|
|
@ -34,7 +34,7 @@ import org.cloudburstmc.protocol.bedrock.data.inventory.crafting.recipe.ShapedRe
|
||||||
import org.cloudburstmc.protocol.bedrock.data.inventory.descriptor.ItemDescriptorWithCount;
|
import org.cloudburstmc.protocol.bedrock.data.inventory.descriptor.ItemDescriptorWithCount;
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.CraftingDataPacket;
|
import org.cloudburstmc.protocol.bedrock.packet.CraftingDataPacket;
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.InventorySlotPacket;
|
import org.cloudburstmc.protocol.bedrock.packet.InventorySlotPacket;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserLogger;
|
||||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||||
import org.geysermc.geyser.inventory.Inventory;
|
import org.geysermc.geyser.inventory.Inventory;
|
||||||
import org.geysermc.geyser.inventory.recipe.GeyserShapedRecipe;
|
import org.geysermc.geyser.inventory.recipe.GeyserShapedRecipe;
|
||||||
|
@ -65,8 +65,9 @@ public class JavaContainerSetSlotTranslator extends PacketTranslator<Clientbound
|
||||||
|
|
||||||
//TODO: support window id -2, should update player inventory
|
//TODO: support window id -2, should update player inventory
|
||||||
Inventory inventory = InventoryUtils.getInventory(session, packet.getContainerId());
|
Inventory inventory = InventoryUtils.getInventory(session, packet.getContainerId());
|
||||||
if (inventory == null)
|
if (inventory == null) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
InventoryTranslator translator = session.getInventoryTranslator();
|
InventoryTranslator translator = session.getInventoryTranslator();
|
||||||
if (translator != null) {
|
if (translator != null) {
|
||||||
|
@ -76,12 +77,12 @@ public class JavaContainerSetSlotTranslator extends PacketTranslator<Clientbound
|
||||||
|
|
||||||
int slot = packet.getSlot();
|
int slot = packet.getSlot();
|
||||||
if (slot >= inventory.getSize()) {
|
if (slot >= inventory.getSize()) {
|
||||||
GeyserImpl geyser = session.getGeyser();
|
GeyserLogger logger = session.getGeyser().getLogger();
|
||||||
geyser.getLogger().warning("ClientboundContainerSetSlotPacket sent to " + session.bedrockUsername()
|
logger.warning("ClientboundContainerSetSlotPacket sent to " + session.bedrockUsername()
|
||||||
+ " that exceeds inventory size!");
|
+ " that exceeds inventory size!");
|
||||||
if (geyser.getConfig().isDebugMode()) {
|
if (logger.isDebug()) {
|
||||||
geyser.getLogger().debug(packet);
|
logger.debug(packet.toString());
|
||||||
geyser.getLogger().debug(inventory);
|
logger.debug(inventory.toString());
|
||||||
}
|
}
|
||||||
// 1.19.0 behavior: the state ID will not be set due to exception
|
// 1.19.0 behavior: the state ID will not be set due to exception
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2024 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.translator.protocol.java.inventory;
|
||||||
|
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.inventory.ContainerType;
|
||||||
|
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.inventory.ClientboundOpenBookPacket;
|
||||||
|
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.inventory.ServerboundContainerClosePacket;
|
||||||
|
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||||
|
import org.geysermc.geyser.inventory.Inventory;
|
||||||
|
import org.geysermc.geyser.inventory.LecternContainer;
|
||||||
|
import org.geysermc.geyser.item.Items;
|
||||||
|
import org.geysermc.geyser.network.GameProtocol;
|
||||||
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
import org.geysermc.geyser.translator.inventory.InventoryTranslator;
|
||||||
|
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||||
|
import org.geysermc.geyser.translator.protocol.Translator;
|
||||||
|
import org.geysermc.geyser.util.InventoryUtils;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@Translator(packet = ClientboundOpenBookPacket.class)
|
||||||
|
public class JavaOpenBookTranslator extends PacketTranslator<ClientboundOpenBookPacket> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlike other fake inventories that rely on placing blocks in the world;
|
||||||
|
* the virtual lectern workaround for books isn't triggered the same way.
|
||||||
|
* Specifically, we don't get a window id - hence, we just use our own!
|
||||||
|
*/
|
||||||
|
private final static int FAKE_LECTERN_WINDOW_ID = -69;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void translate(GeyserSession session, ClientboundOpenBookPacket packet) {
|
||||||
|
GeyserItemStack stack = session.getPlayerInventory().getItemInHand();
|
||||||
|
|
||||||
|
// Don't spawn a fake lectern for books already opened "normally" by the client.
|
||||||
|
if (stack.getItemData(session).equals(session.getCurrentBook())) {
|
||||||
|
session.setCurrentBook(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only post 1.20.60 is it possible to tell the client to open a lectern.
|
||||||
|
if (!GameProtocol.is1_20_60orHigher(session.getUpstream().getProtocolVersion())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stack.asItem().equals(Items.WRITTEN_BOOK)) {
|
||||||
|
Inventory openInventory = session.getOpenInventory();
|
||||||
|
if (openInventory != null) {
|
||||||
|
InventoryUtils.closeInventory(session, openInventory.getJavaId(), true);
|
||||||
|
|
||||||
|
ServerboundContainerClosePacket closeWindowPacket = new ServerboundContainerClosePacket(openInventory.getJavaId());
|
||||||
|
session.sendDownstreamGamePacket(closeWindowPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
InventoryTranslator translator = InventoryTranslator.inventoryTranslator(ContainerType.LECTERN);
|
||||||
|
session.setInventoryTranslator(translator);
|
||||||
|
|
||||||
|
// Should never be null
|
||||||
|
Objects.requireNonNull(translator, "lectern translator must exist");
|
||||||
|
Inventory inventory = translator.createInventory("", FAKE_LECTERN_WINDOW_ID, ContainerType.LECTERN, session.getPlayerInventory());
|
||||||
|
((LecternContainer) inventory).setFakeLecternBook(stack, session);
|
||||||
|
InventoryUtils.openInventory(session, inventory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -45,6 +45,7 @@ import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.inventory.Container;
|
import org.geysermc.geyser.inventory.Container;
|
||||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||||
import org.geysermc.geyser.inventory.Inventory;
|
import org.geysermc.geyser.inventory.Inventory;
|
||||||
|
import org.geysermc.geyser.inventory.LecternContainer;
|
||||||
import org.geysermc.geyser.inventory.PlayerInventory;
|
import org.geysermc.geyser.inventory.PlayerInventory;
|
||||||
import org.geysermc.geyser.inventory.click.Click;
|
import org.geysermc.geyser.inventory.click.Click;
|
||||||
import org.geysermc.geyser.inventory.recipe.GeyserRecipe;
|
import org.geysermc.geyser.inventory.recipe.GeyserRecipe;
|
||||||
|
@ -123,7 +124,9 @@ public class InventoryUtils {
|
||||||
if (inventory != null) {
|
if (inventory != null) {
|
||||||
InventoryTranslator translator = session.getInventoryTranslator();
|
InventoryTranslator translator = session.getInventoryTranslator();
|
||||||
translator.closeInventory(session, inventory);
|
translator.closeInventory(session, inventory);
|
||||||
if (confirm && inventory.isDisplayed() && !inventory.isPending() && !(translator instanceof LecternInventoryTranslator)) {
|
if (confirm && inventory.isDisplayed() && !inventory.isPending()
|
||||||
|
&& !(translator instanceof LecternInventoryTranslator) // TODO: double-check
|
||||||
|
) {
|
||||||
session.setClosingInventory(true);
|
session.setClosingInventory(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -133,6 +136,10 @@ public class InventoryUtils {
|
||||||
|
|
||||||
public static @Nullable Inventory getInventory(GeyserSession session, int javaId) {
|
public static @Nullable Inventory getInventory(GeyserSession session, int javaId) {
|
||||||
if (javaId == 0) {
|
if (javaId == 0) {
|
||||||
|
// ugly hack: lecterns aren't their own inventory on Java, and can hence be closed with e.g. an id of 0
|
||||||
|
if (session.getOpenInventory() instanceof LecternContainer) {
|
||||||
|
return session.getOpenInventory();
|
||||||
|
}
|
||||||
return session.getPlayerInventory();
|
return session.getPlayerInventory();
|
||||||
} else {
|
} else {
|
||||||
Inventory openInventory = session.getOpenInventory();
|
Inventory openInventory = session.getOpenInventory();
|
||||||
|
|
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
@ -1 +1 @@
|
||||||
Subproject commit 522967d6ee76972994ad05a992dc9d7bb4e889ba
|
Subproject commit b1883ca53afc082de98aeb062ba2fad00e069617
|
|
@ -6,14 +6,14 @@ erosion = "1.0-20230406.174837-8"
|
||||||
events = "1.1-SNAPSHOT"
|
events = "1.1-SNAPSHOT"
|
||||||
jackson = "2.14.2"
|
jackson = "2.14.2"
|
||||||
fastutil = "8.5.2"
|
fastutil = "8.5.2"
|
||||||
netty = "4.1.103.Final"
|
netty = "4.1.107.Final"
|
||||||
guava = "29.0-jre"
|
guava = "29.0-jre"
|
||||||
gson = "2.3.1" # Provided by Spigot 1.8.8
|
gson = "2.3.1" # Provided by Spigot 1.8.8
|
||||||
websocket = "1.5.1"
|
websocket = "1.5.1"
|
||||||
protocol = "3.0.0.Beta1-20240204.134050-120"
|
protocol = "3.0.0.Beta1-20240313.120922-126"
|
||||||
protocol-connection = "3.0.0.Beta1-20240204.134050-119"
|
protocol-connection = "3.0.0.Beta1-20240313.120922-125"
|
||||||
raknet = "1.0.0.CR1-20231206.145325-12"
|
raknet = "1.0.0.CR1-20231206.145325-12"
|
||||||
blockstateupdater="1.20.60-20240129.140535-1"
|
blockstateupdater="1.20.70-20240303.125052-2"
|
||||||
mcauthlib = "d9d773e"
|
mcauthlib = "d9d773e"
|
||||||
mcprotocollib = "1.20.4-2-20240116.220521-7"
|
mcprotocollib = "1.20.4-2-20240116.220521-7"
|
||||||
adventure = "4.14.0"
|
adventure = "4.14.0"
|
||||||
|
|
Loading…
Reference in New Issue