diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 000000000..ca8b63cc4
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,130 @@
+name: Build
+
+on:
+ workflow_dispatch:
+ push:
+ paths-ignore:
+ - '.github/ISSUE_TEMPLATE/*.yml'
+ - '.github/actions/pullrequest.yml'
+ - '.idea/copyright/*.xml'
+ - '.gitignore'
+ - 'CONTRIBUTING.md'
+ - 'LICENSE'
+ - 'Jenkinsfile '
+ - 'README.md'
+ - 'licenseheader.txt'
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository and submodules
+ uses: actions/checkout@v3
+ with:
+ submodules: recursive
+
+ - name: Validate Gradle Wrapper
+ uses: gradle/wrapper-validation-action@v1
+
+ - uses: actions/setup-java@v3
+ with:
+ java-version: 17
+ distribution: temurin
+
+ - name: Build
+ uses: gradle/gradle-build-action@v2
+ with:
+ arguments: build
+ gradle-home-cache-cleanup: true
+
+ - name: Archive artifacts (Geyser Fabric)
+ uses: actions/upload-artifact@v3
+ if: success()
+ with:
+ name: Geyser Fabric
+ path: bootstrap/fabric/build/libs/Geyser-Fabric.jar
+ if-no-files-found: error
+ - name: Archive artifacts (Geyser Standalone)
+ uses: actions/upload-artifact@v3
+ if: success()
+ with:
+ name: Geyser Standalone
+ path: bootstrap/standalone/build/libs/Geyser-Standalone.jar
+ if-no-files-found: error
+ - name: Archive artifacts (Geyser Spigot)
+ uses: actions/upload-artifact@v3
+ if: success()
+ with:
+ name: Geyser Spigot
+ path: bootstrap/spigot/build/libs/Geyser-Spigot.jar
+ if-no-files-found: error
+ - name: Archive artifacts (Geyser BungeeCord)
+ uses: actions/upload-artifact@v3
+ if: success()
+ with:
+ name: Geyser BungeeCord
+ path: bootstrap/bungeecord/build/libs/Geyser-BungeeCord.jar
+ if-no-files-found: error
+ - name: Archive artifacts (Geyser Sponge)
+ uses: actions/upload-artifact@v3
+ if: success()
+ with:
+ name: Geyser Sponge
+ path: bootstrap/sponge/build/libs/Geyser-Sponge.jar
+ if-no-files-found: error
+ - name: Archive artifacts (Geyser Velocity)
+ uses: actions/upload-artifact@v3
+ if: success()
+ with:
+ name: Geyser Velocity
+ path: bootstrap/velocity/build/libs/Geyser-Velocity.jar
+ if-no-files-found: error
+
+ - name: Publish to Maven Repository
+ if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
+ uses: gradle/gradle-build-action@v2
+ env:
+ ORG_GRADLE_PROJECT_geysermcUsername: ${{ vars.DEPLOY_USER }}
+ ORG_GRADLE_PROJECT_geysermcPassword: ${{ secrets.DEPLOY_PASS }}
+ with:
+ arguments: publish
+
+ - name: Publish to Downloads API
+ if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
+ shell: bash
+ env:
+ DOWNLOADS_USERNAME: ${{ vars.DOWNLOADS_USERNAME }}
+ DOWNLOADS_PRIVATE_KEY: ${{ secrets.DOWNLOADS_PRIVATE_KEY }}
+ DOWNLOADS_SERVER_IP: ${{ secrets.DOWNLOADS_SERVER_IP }}
+ run: |
+ # Save the private key to a file
+ echo "$DOWNLOADS_PRIVATE_KEY" > id_ecdsa
+ chmod 600 id_ecdsa
+ # Set the project
+ project=geyser
+ # Get the version from gradle.properties
+ version=$(cat gradle.properties | grep -o "version=[0-9\\.]*" | cut -d"=" -f2)
+ # Create the build folder
+ ssh -o StrictHostKeyChecking=no -i id_ecdsa $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP mkdir -p "~/uploads/$project/$GITHUB_RUN_NUMBER/"
+ # Copy over artifacts
+ rsync -P -e "ssh -o StrictHostKeyChecking=no -i id_ecdsa" bootstrap/**/build/libs/Geyser-*.jar $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP:~/uploads/$project/$GITHUB_RUN_NUMBER/
+ # Run the build script
+ # Push the metadata
+ echo "{\"project\": \"$project\", \"version\": \"$version\", \"id\": $GITHUB_RUN_NUMBER, \"commit\": \"$GITHUB_SHA\"}" > metadata.json
+ 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
+ uses: gradle/gradle-build-action@v2
+ if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
+ env:
+ MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
+ with:
+ arguments: fabric:modrinth
+ gradle-home-cache-cleanup: true
+
+ - name: Notify Discord
+ if: ${{ (success() || failure()) && github.repository == 'GeyserMC/Geyser' }}
+ uses: Tim203/actions-git-discord-webhook@main
+ with:
+ webhook_url: ${{ secrets.DISCORD_WEBHOOK }}
+ status: ${{ job.status }}
diff --git a/.github/workflows/pullrequest.yml b/.github/workflows/pullrequest.yml
index 39e9fe188..9567fb414 100644
--- a/.github/workflows/pullrequest.yml
+++ b/.github/workflows/pullrequest.yml
@@ -1,58 +1,96 @@
name: Build Pull Request
-on: [pull_request]
+on:
+ pull_request:
+ paths-ignore:
+ - '.github/ISSUE_TEMPLATE/*.yml'
+ - '.idea/copyright/*.xml'
+ - '.gitignore'
+ - 'CONTRIBUTING.md'
+ - 'LICENSE'
+ - 'Jenkinsfile '
+ - 'README.md'
+ - 'licenseheader.txt'
jobs:
build:
-
runs-on: ubuntu-latest
-
steps:
- - uses: actions/checkout@v2
- name: Set up JDK 17
uses: actions/setup-java@v1
with:
- distribution: 'temurin'
java-version: 17
- cache: 'gradle'
- - name: submodules-init
- uses: snickerbockers/submodules-init@v4
- - name: Build with Gradle
- run: ./gradlew build
+ distribution: temurin
+
+ - name: Check if the author has forked the API repo
+ uses: Kas-tle/ForkFinder@v1.0.1
+ id: find_forks
+ with:
+ owner: GeyserMC
+ repo: api
+ token: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Use author's API repo if it exists
+ if: steps.find_forks.outputs.target_branch_found == 'true'
+ env:
+ API_FORK_URL: ${{ steps.find_forks.outputs.user_fork_url }}
+ API_FORK_BRANCH: ${{ github.event.pull_request.head.ref }}
+ run: |
+ git clone "${API_FORK_URL}" --single-branch --branch "${API_FORK_BRANCH}" api
+ cd api
+ ./gradlew publishToMavenLocal
+
+ - name: Checkout repository and submodules
+ uses: actions/checkout@v3
+ with:
+ submodules: recursive
+ path: geyser
+
+ - name: Build Geyser
+ uses: gradle/gradle-build-action@v2
+ with:
+ arguments: build
+ build-root-directory: geyser
- name: Archive artifacts (Geyser Fabric)
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v3
if: success()
with:
name: Geyser Fabric
- path: bootstrap/fabric/build/libs/Geyser-Fabric.jar
+ path: geyser/bootstrap/fabric/build/libs/Geyser-Fabric.jar
+ if-no-files-found: error
- name: Archive artifacts (Geyser Standalone)
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v3
if: success()
with:
name: Geyser Standalone
- path: bootstrap/standalone/build/libs/Geyser-Standalone.jar
+ path: geyser/bootstrap/standalone/build/libs/Geyser-Standalone.jar
+ if-no-files-found: error
- name: Archive artifacts (Geyser Spigot)
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v3
if: success()
with:
name: Geyser Spigot
- path: bootstrap/spigot/build/libs/Geyser-Spigot.jar
+ path: geyser/bootstrap/spigot/build/libs/Geyser-Spigot.jar
+ if-no-files-found: error
- name: Archive artifacts (Geyser BungeeCord)
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v3
if: success()
with:
name: Geyser BungeeCord
- path: bootstrap/bungeecord/build/libs/Geyser-BungeeCord.jar
+ path: geyser/bootstrap/bungeecord/build/libs/Geyser-BungeeCord.jar
+ if-no-files-found: error
- name: Archive artifacts (Geyser Sponge)
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v3
if: success()
with:
name: Geyser Sponge
- path: bootstrap/sponge/build/libs/Geyser-Sponge.jar
+ path: geyser/bootstrap/sponge/build/libs/Geyser-Sponge.jar
+ if-no-files-found: error
- name: Archive artifacts (Geyser Velocity)
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v3
if: success()
with:
name: Geyser Velocity
- path: bootstrap/velocity/build/libs/Geyser-Velocity.jar
+ path: geyser/bootstrap/velocity/build/libs/Geyser-Velocity.jar
+ if-no-files-found: error
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index ce6894845..dac8a9a07 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -48,4 +48,6 @@ Make sure to comment your code where possible.
The nature of our software requires a lot of arrays and maps to be stored - where possible, use Fastutil's specialized maps. For example, if you're storing block state translations, use an `Int2IntMap`.
-We have a rundown of all the tools you need to develop over on our [wiki](https://github.com/GeyserMC/Geyser/wiki/Developer-Guide). If you have any questions, please feel free to reach out to our [Discord](https://discord.gg/geysermc)!
+We have a rundown of all the tools you need to develop over on our [wiki](https://wiki.geysermc.org/other/developer-guide/). If you have any questions, please feel free to reach out to our [Discord](https://discord.gg/geysermc)!
+
+If you're making a pull request that also depends on changes to [the base API](https://github.com/GeyserMC/api), simply fork the API repo and create a branch with the same name as your Geyser PR. The pull request [action](https://github.com/GeyserMC/Geyser/blob/master/.github/workflows/pullrequest.yml) will automatically use your API changes while building your changes to Geyser.
\ No newline at end of file
diff --git a/Jenkinsfile b/Jenkinsfile
index 072f99154..5c2eada3d 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -24,80 +24,9 @@ pipeline {
}
}
}
-
- stage ('Deploy') {
- when {
- anyOf {
- branch "master"
- }
- }
-
- steps {
- rtGradleDeployer(
- id: "GRADLE_DEPLOYER",
- serverId: "opencollab-artifactory",
- releaseRepo: "maven-releases",
- snapshotRepo: "maven-snapshots"
- )
- rtGradleResolver(
- id: "GRADLE_RESOLVER",
- serverId: "opencollab-artifactory"
- )
- rtGradleRun(
- usesPlugin: true,
- tool: 'Gradle 7',
- rootDir: "",
- useWrapper: true,
- buildFile: 'build.gradle.kts',
- tasks: 'artifactoryPublish',
- deployerId: "GRADLE_DEPLOYER",
- resolverId: "GRADLE_RESOLVER"
- )
- rtPublishBuildInfo(
- serverId: "opencollab-artifactory"
- )
- }
- }
}
post {
- always {
- script {
- def changeLogSets = currentBuild.changeSets
- def message = "**Changes:**"
-
- if (changeLogSets.size() == 0) {
- message += "\n*No changes.*"
- } else {
- def repositoryUrl = scm.userRemoteConfigs[0].url.replace(".git", "")
- def count = 0;
- def extra = 0;
- for (int i = 0; i < changeLogSets.size(); i++) {
- def entries = changeLogSets[i].items
- for (int j = 0; j < entries.length; j++) {
- if (count <= 10) {
- def entry = entries[j]
- def commitId = entry.commitId.substring(0, 6)
- message += "\n - [`${commitId}`](${repositoryUrl}/commit/${entry.commitId}) ${entry.msg}"
- count++
- } else {
- extra++;
- }
- }
- }
-
- if (extra != 0) {
- message += "\n - ${extra} more commits"
- }
- }
-
- env.changes = message
- }
- deleteDir()
- withCredentials([string(credentialsId: 'geyser-discord-webhook', variable: 'DISCORD_WEBHOOK')]) {
- discordSend description: "**Build:** [${currentBuild.id}](${env.BUILD_URL})\n**Status:** [${currentBuild.currentResult}](${env.BUILD_URL})\n${changes}\n\n[**Artifacts on Jenkins**](https://ci.opencollab.dev/job/GeyserMC/job/Geyser)", footer: 'Open Collaboration Jenkins', link: env.BUILD_URL, successful: currentBuild.resultIsBetterOrEqualTo('SUCCESS'), title: "${env.JOB_NAME} #${currentBuild.id}", webhookURL: DISCORD_WEBHOOK
- }
- }
success {
script {
if (env.BRANCH_NAME == 'master') {
diff --git a/README.md b/README.md
index b36235e36..77e2671f9 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,6 @@
-[![forthebadge made-with-java](https://forthebadge.com/images/badges/made-with-java.svg)](https://java.com/)
-
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
-[![Build Status](https://ci.opencollab.dev/job/Geyser/job/master/badge/icon)](https://ci.opencollab.dev/job/GeyserMC/job/Geyser/job/master/)
[![Discord](https://img.shields.io/discord/613163671870242838.svg?color=%237289da&label=discord)](https://discord.gg/geysermc)
[![Crowdin](https://badges.crowdin.net/geyser/localized.svg)](https://translate.geysermc.org/)
@@ -17,7 +14,7 @@ The ultimate goal of this project is to allow Minecraft: Bedrock Edition users t
Special thanks to the DragonProxy project for being a trailblazer in protocol translation and for all the team members who have joined us here!
-### Currently supporting Minecraft Bedrock 1.19.20 - 1.19.60 and Minecraft Java 1.19.3.
+### Currently supporting Minecraft Bedrock 1.19.30 - 1.19.71 and Minecraft Java 1.19.4.
## Setting Up
Take a look [here](https://wiki.geysermc.org/geyser/setup/) for how to set up Geyser.
@@ -27,7 +24,7 @@ Take a look [here](https://wiki.geysermc.org/geyser/setup/) for how to set up Ge
## Links:
- Website: https://geysermc.org
- Docs: https://wiki.geysermc.org/geyser/
-- Download: https://ci.geysermc.org
+- Download: https://geysermc.org/download
- Discord: https://discord.gg/geysermc
- Donate: https://opencollective.com/geysermc
- Test Server: `test.geysermc.org` port `25565` for Java and `19132` for Bedrock
diff --git a/api/src/main/java/org/geysermc/geyser/api/event/bedrock/BedrockEmoteEvent.java b/api/src/main/java/org/geysermc/geyser/api/event/bedrock/ClientEmoteEvent.java
similarity index 92%
rename from api/src/main/java/org/geysermc/geyser/api/event/bedrock/BedrockEmoteEvent.java
rename to api/src/main/java/org/geysermc/geyser/api/event/bedrock/ClientEmoteEvent.java
index efe3f12d6..35b6a9e73 100644
--- a/api/src/main/java/org/geysermc/geyser/api/event/bedrock/BedrockEmoteEvent.java
+++ b/api/src/main/java/org/geysermc/geyser/api/event/bedrock/ClientEmoteEvent.java
@@ -33,11 +33,11 @@ import org.geysermc.geyser.api.event.connection.ConnectionEvent;
/**
* Called whenever a Bedrock player performs an emote on their end, before it is broadcasted to the rest of the server.
*/
-public final class BedrockEmoteEvent extends ConnectionEvent implements Cancellable {
+public final class ClientEmoteEvent extends ConnectionEvent implements Cancellable {
private final String emoteId;
private boolean cancelled;
- public BedrockEmoteEvent(@NonNull GeyserConnection connection, @NonNull String emoteId) {
+ public ClientEmoteEvent(@NonNull GeyserConnection connection, @NonNull String emoteId) {
super(connection);
this.emoteId = emoteId;
}
diff --git a/bootstrap/fabric/build.gradle.kts b/bootstrap/fabric/build.gradle.kts
index 743b75a26..35270df80 100644
--- a/bootstrap/fabric/build.gradle.kts
+++ b/bootstrap/fabric/build.gradle.kts
@@ -1,5 +1,6 @@
plugins {
id("fabric-loom") version "1.0-SNAPSHOT"
+ id("com.modrinth.minotaur") version "2.+"
}
java {
@@ -65,6 +66,22 @@ tasks {
relocate("org.yaml", "org.geysermc.relocate.yaml") // https://github.com/CardboardPowered/cardboard/issues/139
relocate("com.fasterxml.jackson", "org.geysermc.relocate.jackson")
relocate("net.kyori", "org.geysermc.relocate.kyori")
+
+ dependencies {
+ // Exclude everything EXCEPT KQueue and some DNS stuff required for HAProxyc
+ exclude(dependency("io.netty:netty-transport-classes-epoll:.*"))
+ exclude(dependency("io.netty:netty-transport-native-epoll:.*"))
+ exclude(dependency("io.netty:netty-transport-native-unix-common:.*"))
+ exclude(dependency("io.netty:netty-transport-native-kqueue:.*"))
+ exclude(dependency("io.netty:netty-handler:.*"))
+ exclude(dependency("io.netty:netty-common:.*"))
+ exclude(dependency("io.netty:netty-buffer:.*"))
+ exclude(dependency("io.netty:netty-resolver:.*"))
+ exclude(dependency("io.netty:netty-transport:.*"))
+ exclude(dependency("io.netty:netty-codec:.*"))
+ exclude(dependency("io.netty:netty-resolver-dns:.*"))
+ exclude(dependency("io.netty:netty-resolver-dns-native-macos:.*"))
+ }
}
remapJar {
@@ -74,4 +91,23 @@ tasks {
archiveClassifier.set("")
archiveVersion.set("")
}
+}
+
+modrinth {
+ token.set(System.getenv("MODRINTH_TOKEN")) // Even though this is the default value, apparently this prevents GitHub Actions caching the token?
+ projectId.set("wKkoqHrH")
+ versionNumber.set(project.version as String + "-" + System.getenv("GITHUB_RUN_NUMBER"))
+ versionType.set("beta")
+ changelog.set("A changelog can be found at https://github.com/GeyserMC/Geyser/commits")
+
+ syncBodyFrom.set(rootProject.file("README.md").readText())
+
+ uploadFile.set(tasks.getByPath("remapJar"))
+ gameVersions.addAll("1.19", "1.19.1", "1.19.2", "1.19.3", "1.19.4")
+
+ loaders.add("fabric")
+
+ dependencies {
+ required.project("fabric-api")
+ }
}
\ No newline at end of file
diff --git a/bootstrap/fabric/src/main/java/org/geysermc/geyser/platform/fabric/world/GeyserFabricWorldManager.java b/bootstrap/fabric/src/main/java/org/geysermc/geyser/platform/fabric/world/GeyserFabricWorldManager.java
index 108d88e8b..454a9167e 100644
--- a/bootstrap/fabric/src/main/java/org/geysermc/geyser/platform/fabric/world/GeyserFabricWorldManager.java
+++ b/bootstrap/fabric/src/main/java/org/geysermc/geyser/platform/fabric/world/GeyserFabricWorldManager.java
@@ -25,13 +25,7 @@
package org.geysermc.geyser.platform.fabric.world;
-import org.cloudburstmc.math.vector.Vector3i;
-<<<<<<< HEAD
-import org.cloudburstmc.nbt.NbtMap;
-import org.cloudburstmc.nbt.NbtMapBuilder;
-import org.cloudburstmc.nbt.NbtType;
-=======
->>>>>>> d1febe0b3904d52cdc6301711950f22d1caf09b5
+import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityInfo;
import me.lucko.fabric.api.permissions.v0.Permissions;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.*;
@@ -43,19 +37,20 @@ import net.minecraft.world.item.WrittenBookItem;
import net.minecraft.world.level.block.entity.BannerBlockEntity;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.LecternBlockEntity;
+import net.minecraft.world.level.chunk.LevelChunk;
+import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.cloudburstmc.nbt.NbtType;
+import org.geysermc.erosion.util.LecternUtils;
import org.geysermc.geyser.level.GeyserWorldManager;
import org.geysermc.geyser.session.GeyserSession;
-import org.geysermc.geyser.translator.inventory.LecternInventoryTranslator;
import org.geysermc.geyser.util.BlockEntityUtils;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.TimeUnit;
public class GeyserFabricWorldManager extends GeyserWorldManager {
private final MinecraftServer server;
@@ -65,69 +60,91 @@ public class GeyserFabricWorldManager extends GeyserWorldManager {
}
@Override
- public boolean shouldExpectLecternHandled() {
+ public boolean shouldExpectLecternHandled(GeyserSession session) {
return true;
}
@Override
- public NbtMap getLecternDataAt(GeyserSession session, int x, int y, int z, boolean isChunkLoad) {
- Runnable lecternGet = () -> {
- // Mostly a reimplementation of Spigot lectern support
+ public void sendLecternData(GeyserSession session, int x, int z, List blockEntityInfos) {
+ server.execute(() -> {
ServerPlayer player = getPlayer(session);
- if (player != null) {
- BlockEntity blockEntity = player.level.getBlockEntity(new BlockPos(x, y, z));
- if (!(blockEntity instanceof LecternBlockEntity lectern)) {
- return;
- }
-
- if (!lectern.hasBook()) {
- if (!isChunkLoad) {
- BlockEntityUtils.updateBlockEntity(session, LecternInventoryTranslator.getBaseLecternTag(x, y, z, 0).build(), Vector3i.from(x, y, z));
- }
- return;
- }
-
- ItemStack book = lectern.getBook();
- int pageCount = WrittenBookItem.getPageCount(book);
- boolean hasBookPages = pageCount > 0;
- NbtMapBuilder lecternTag = LecternInventoryTranslator.getBaseLecternTag(x, y, z, hasBookPages ? pageCount : 1);
- lecternTag.putInt("page", lectern.getPage() / 2);
- NbtMapBuilder bookTag = NbtMap.builder()
- .putByte("Count", (byte) book.getCount())
- .putShort("Damage", (short) 0)
- .putString("Name", "minecraft:writable_book");
- List pages = new ArrayList<>(hasBookPages ? pageCount : 1);
- if (hasBookPages && WritableBookItem.makeSureTagIsValid(book.getTag())) {
- ListTag listTag = book.getTag().getList("pages", 8);
-
- for (int i = 0; i < listTag.size(); i++) {
- String page = listTag.getString(i);
- NbtMapBuilder pageBuilder = NbtMap.builder()
- .putString("photoname", "")
- .putString("text", page);
- pages.add(pageBuilder.build());
- }
- } else {
- // Empty page
- NbtMapBuilder pageBuilder = NbtMap.builder()
- .putString("photoname", "")
- .putString("text", "");
- pages.add(pageBuilder.build());
- }
-
- bookTag.putCompound("tag", NbtMap.builder().putList("pages", NbtType.COMPOUND, pages).build());
- lecternTag.putCompound("book", bookTag.build());
- NbtMap blockEntityTag = lecternTag.build();
- BlockEntityUtils.updateBlockEntity(session, blockEntityTag, Vector3i.from(x, y, z));
+ if (player == null) {
+ return;
}
- };
- if (isChunkLoad) {
- // Hacky hacks to allow lectern loading to be delayed
- session.scheduleInEventLoop(() -> server.execute(lecternGet), 1, TimeUnit.SECONDS);
- } else {
- server.execute(lecternGet);
+
+ LevelChunk chunk = player.getLevel().getChunk(x, z);
+ final int chunkBlockX = x << 4;
+ final int chunkBlockZ = z << 4;
+ for (int i = 0; i < blockEntityInfos.size(); i++) {
+ BlockEntityInfo blockEntityInfo = blockEntityInfos.get(i);
+ BlockEntity blockEntity = chunk.getBlockEntity(new BlockPos(chunkBlockX + blockEntityInfo.getX(),
+ blockEntityInfo.getY(), chunkBlockZ + blockEntityInfo.getZ()));
+ sendLecternData(session, blockEntity, true);
+ }
+ });
+ }
+
+ @Override
+ public void sendLecternData(GeyserSession session, int x, int y, int z) {
+ server.execute(() -> {
+ ServerPlayer player = getPlayer(session);
+ if (player == null) {
+ return;
+ }
+
+ BlockEntity blockEntity = player.level.getBlockEntity(new BlockPos(x, y, z));
+ sendLecternData(session, blockEntity, false);
+ });
+ }
+
+ private void sendLecternData(GeyserSession session, BlockEntity blockEntity, boolean isChunkLoad) {
+ if (!(blockEntity instanceof LecternBlockEntity lectern)) {
+ return;
}
- return LecternInventoryTranslator.getBaseLecternTag(x, y, z, 0).build();
+
+ int x = blockEntity.getBlockPos().getX();
+ int y = blockEntity.getBlockPos().getY();
+ int z = blockEntity.getBlockPos().getZ();
+
+ if (!lectern.hasBook()) {
+ if (!isChunkLoad) {
+ BlockEntityUtils.updateBlockEntity(session, LecternUtils.getBaseLecternTag(x, y, z, 0).build(), Vector3i.from(x, y, z));
+ }
+ return;
+ }
+
+ ItemStack book = lectern.getBook();
+ int pageCount = WrittenBookItem.getPageCount(book);
+ boolean hasBookPages = pageCount > 0;
+ NbtMapBuilder lecternTag = LecternUtils.getBaseLecternTag(x, y, z, hasBookPages ? pageCount : 1);
+ lecternTag.putInt("page", lectern.getPage() / 2);
+ NbtMapBuilder bookTag = NbtMap.builder()
+ .putByte("Count", (byte) book.getCount())
+ .putShort("Damage", (short) 0)
+ .putString("Name", "minecraft:writable_book");
+ List pages = new ArrayList<>(hasBookPages ? pageCount : 1);
+ if (hasBookPages && WritableBookItem.makeSureTagIsValid(book.getTag())) {
+ ListTag listTag = book.getTag().getList("pages", 8);
+
+ for (int i = 0; i < listTag.size(); i++) {
+ String page = listTag.getString(i);
+ NbtMapBuilder pageBuilder = NbtMap.builder()
+ .putString("photoname", "")
+ .putString("text", page);
+ pages.add(pageBuilder.build());
+ }
+ } else {
+ // Empty page
+ NbtMapBuilder pageBuilder = NbtMap.builder()
+ .putString("photoname", "")
+ .putString("text", "");
+ pages.add(pageBuilder.build());
+ }
+
+ bookTag.putCompound("tag", NbtMap.builder().putList("pages", NbtType.COMPOUND, pages).build());
+ lecternTag.putCompound("book", bookTag.build());
+ NbtMap blockEntityTag = lecternTag.build();
+ BlockEntityUtils.updateBlockEntity(session, blockEntityTag, Vector3i.from(x, y, z));
}
@Override
diff --git a/bootstrap/spigot/build.gradle.kts b/bootstrap/spigot/build.gradle.kts
index b5ef4e69e..aa7958732 100644
--- a/bootstrap/spigot/build.gradle.kts
+++ b/bootstrap/spigot/build.gradle.kts
@@ -1,5 +1,8 @@
dependencies {
api(projects.core)
+ api(libs.erosion.bukkit.common) {
+ isTransitive = false
+ }
implementation(libs.adapters.spigot)
@@ -7,8 +10,8 @@ dependencies {
implementation(libs.adventure.text.serializer.bungeecord)
- // Both paper-api and paper-mojangapi only provide Java 17 versions for 1.19
- compileOnly(libs.paper.api) {
+ // Both folia-api and paper-mojangapi only provide Java 17 versions for 1.19
+ compileOnly(libs.folia.api) {
attributes {
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17)
}
@@ -44,6 +47,7 @@ tasks.withType {
// We cannot shade Netty, or else native libraries will not load
// Needed because older Spigot builds do not provide the haproxy module
+ exclude(dependency("io.netty:netty-transport-classes-epoll:.*"))
exclude(dependency("io.netty:netty-transport-native-epoll:.*"))
exclude(dependency("io.netty:netty-transport-native-unix-common:.*"))
exclude(dependency("io.netty:netty-transport-native-kqueue:.*"))
diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserPaperPingPassthrough.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserPaperPingPassthrough.java
index 36dd81d44..bb0f30e70 100644
--- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserPaperPingPassthrough.java
+++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserPaperPingPassthrough.java
@@ -59,13 +59,13 @@ public final class GeyserPaperPingPassthrough implements IGeyserPingPassthrough
// runtime because we still have to shade in our own Adventure class. For now.
PaperServerListPingEvent event;
if (OLD_CONSTRUCTOR != null) {
- // Approximately pre-1.19
+ // 1.19, removed in 1.19.4
event = OLD_CONSTRUCTOR.newInstance(new GeyserStatusClient(inetSocketAddress),
Bukkit.getMotd(), Bukkit.getOnlinePlayers().size(),
Bukkit.getMaxPlayers(), Bukkit.getVersion(), GameProtocol.getJavaProtocolVersion(), null);
} else {
event = new PaperServerListPingEvent(new GeyserStatusClient(inetSocketAddress),
- Bukkit.getMotd(), Bukkit.shouldSendChatPreviews(), Bukkit.getOnlinePlayers().size(),
+ Bukkit.getMotd(), Bukkit.getOnlinePlayers().size(),
Bukkit.getMaxPlayers(), Bukkit.getVersion(), GameProtocol.getJavaProtocolVersion(), null);
}
Bukkit.getPluginManager().callEvent(event);
diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPingPassthrough.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPingPassthrough.java
index 634d1f8a8..1e6a0ad6c 100644
--- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPingPassthrough.java
+++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPingPassthrough.java
@@ -66,7 +66,7 @@ public class GeyserSpigotPingPassthrough implements IGeyserPingPassthrough {
private static class GeyserPingEvent extends ServerListPingEvent {
public GeyserPingEvent(InetAddress address, String motd, int numPlayers, int maxPlayers) {
- super(address, motd, Bukkit.shouldSendChatPreviews(), numPlayers, maxPlayers);
+ super("", address, motd, numPlayers, maxPlayers);
}
@Override
diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/ReflectedNames.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/ReflectedNames.java
index 3185f2d30..67e31fea2 100644
--- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/ReflectedNames.java
+++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/ReflectedNames.java
@@ -60,16 +60,16 @@ public final class ReflectedNames {
}
static Constructor getOldPaperPingConstructor() {
- if (getConstructor(PaperServerListPingEvent.class, StatusClient.class, String.class, boolean.class, int.class,
+ if (getConstructor(PaperServerListPingEvent.class, StatusClient.class, String.class, int.class,
int.class, String.class, int.class, CachedServerIcon.class) != null) {
- // @NotNull StatusClient client, @NotNull String motd, boolean shouldSendChatPreviews, int numPlayers, int maxPlayers,
+ // @NotNull StatusClient client, @NotNull String motd, int numPlayers, int maxPlayers,
// @NotNull String version, int protocolVersion, @Nullable CachedServerIcon favicon
// New constructor is present
return null;
}
- // @NotNull StatusClient client, @NotNull String motd, int numPlayers, int maxPlayers,
+ // @NotNull StatusClient client, @NotNull String motd, boolean shouldSendChatPreviews, int numPlayers, int maxPlayers,
// @NotNull String version, int protocolVersion, @Nullable CachedServerIcon favicon
- return getConstructor(PaperServerListPingEvent.class, StatusClient.class, String.class, int.class, int.class,
+ return getConstructor(PaperServerListPingEvent.class, StatusClient.class, String.class, boolean.class, int.class, int.class,
String.class, int.class, CachedServerIcon.class);
}
diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/GeyserPistonListener.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/GeyserPistonListener.java
index 50caf8db2..01b0be9f2 100644
--- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/GeyserPistonListener.java
+++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/GeyserPistonListener.java
@@ -27,8 +27,8 @@ package org.geysermc.geyser.platform.spigot.world;
import com.github.steveice10.mc.protocol.data.game.level.block.value.PistonValueType;
import org.cloudburstmc.math.vector.Vector3i;
+import it.unimi.dsi.fastutil.objects.Object2IntArrayMap;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
-import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
@@ -85,7 +85,7 @@ public class GeyserPistonListener implements Listener {
PistonValueType type = isExtend ? PistonValueType.PUSHING : PistonValueType.PULLING;
boolean sticky = event.isSticky();
- Object2IntMap attachedBlocks = new Object2IntOpenHashMap<>();
+ Object2IntMap attachedBlocks = new Object2IntArrayMap<>();
boolean blocksFilled = false;
for (Map.Entry entry : geyser.getSessionManager().getSessions().entrySet()) {
diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotWorldManager.java
index 5ea67a42f..d54926a5b 100644
--- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotWorldManager.java
+++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotWorldManager.java
@@ -25,33 +25,28 @@
package org.geysermc.geyser.platform.spigot.world.manager;
+import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityInfo;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
-import com.github.steveice10.opennbt.tag.builtin.ListTag;
-import com.github.steveice10.opennbt.tag.builtin.Tag;
import org.bukkit.Bukkit;
+import org.bukkit.Chunk;
import org.bukkit.World;
-import org.bukkit.block.*;
-import org.bukkit.block.banner.Pattern;
+import org.bukkit.block.Block;
import org.bukkit.entity.Player;
-import org.bukkit.inventory.ItemStack;
-import org.bukkit.inventory.meta.BookMeta;
import org.bukkit.plugin.Plugin;
-import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.nbt.NbtMap;
-import org.cloudburstmc.nbt.NbtMapBuilder;
-import org.cloudburstmc.nbt.NbtType;
+import org.geysermc.erosion.bukkit.BukkitLecterns;
+import org.geysermc.erosion.bukkit.BukkitUtils;
+import org.geysermc.erosion.bukkit.PickBlockUtils;
+import org.geysermc.erosion.bukkit.SchedulerUtils;
import org.geysermc.geyser.level.GameRule;
import org.geysermc.geyser.level.WorldManager;
import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.registry.BlockRegistries;
import org.geysermc.geyser.session.GeyserSession;
-import org.geysermc.geyser.translator.inventory.LecternInventoryTranslator;
-import org.geysermc.geyser.translator.inventory.item.nbt.BannerTranslator;
import org.geysermc.geyser.util.BlockEntityUtils;
import org.jetbrains.annotations.Nullable;
import javax.annotation.Nonnull;
-import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@@ -60,9 +55,11 @@ import java.util.concurrent.CompletableFuture;
*/
public class GeyserSpigotWorldManager extends WorldManager {
private final Plugin plugin;
+ private final BukkitLecterns lecterns;
public GeyserSpigotWorldManager(Plugin plugin) {
this.plugin = plugin;
+ this.lecterns = new BukkitLecterns(plugin);
}
@Override
@@ -81,6 +78,12 @@ public class GeyserSpigotWorldManager extends WorldManager {
}
public int getBlockNetworkId(Block block) {
+ if (SchedulerUtils.FOLIA && !Bukkit.isOwnedByCurrentRegion(block)) {
+ // Terrible behavior, but this is basically what's always been happening behind the scenes anyway.
+ CompletableFuture blockData = new CompletableFuture<>();
+ Bukkit.getRegionScheduler().execute(this.plugin, block.getLocation(), () -> blockData.complete(block.getBlockData().getAsString()));
+ return BlockRegistries.JAVA_IDENTIFIERS.getOrDefault(blockData.join(), BlockStateValues.JAVA_AIR_ID);
+ }
return BlockRegistries.JAVA_IDENTIFIERS.getOrDefault(block.getBlockData().getAsString(), BlockStateValues.JAVA_AIR_ID);
}
@@ -90,71 +93,64 @@ public class GeyserSpigotWorldManager extends WorldManager {
}
@Override
- public NbtMap getLecternDataAt(GeyserSession session, int x, int y, int z, boolean isChunkLoad) {
- // Run as a task to prevent async issues
- Runnable lecternInfoGet = () -> {
- Player bukkitPlayer;
- if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) {
- return;
- }
-
- Block block = bukkitPlayer.getWorld().getBlockAt(x, y, z);
- if (!(block.getState() instanceof Lectern lectern)) {
- session.getGeyser().getLogger().error("Lectern expected at: " + Vector3i.from(x, y, z).toString() + " but was not! " + block.toString());
- return;
- }
-
- ItemStack itemStack = lectern.getInventory().getItem(0);
- if (itemStack == null || !(itemStack.getItemMeta() instanceof BookMeta bookMeta)) {
- if (!isChunkLoad) {
- // We need to update the lectern since it's not going to be updated otherwise
- BlockEntityUtils.updateBlockEntity(session, LecternInventoryTranslator.getBaseLecternTag(x, y, z, 0).build(), Vector3i.from(x, y, z));
- }
- // We don't care; return
- return;
- }
-
- // On the count: allow the book to show/open even there are no pages. We know there is a book here, after all, and this matches Java behavior
- boolean hasBookPages = bookMeta.getPageCount() > 0;
- NbtMapBuilder lecternTag = LecternInventoryTranslator.getBaseLecternTag(x, y, z, hasBookPages ? bookMeta.getPageCount() : 1);
- lecternTag.putInt("page", lectern.getPage() / 2);
- NbtMapBuilder bookTag = NbtMap.builder()
- .putByte("Count", (byte) itemStack.getAmount())
- .putShort("Damage", (short) 0)
- .putString("Name", "minecraft:writable_book");
- List pages = new ArrayList<>(bookMeta.getPageCount());
- if (hasBookPages) {
- for (String page : bookMeta.getPages()) {
- NbtMapBuilder pageBuilder = NbtMap.builder()
- .putString("photoname", "")
- .putString("text", page);
- pages.add(pageBuilder.build());
- }
- } else {
- // Empty page
- NbtMapBuilder pageBuilder = NbtMap.builder()
- .putString("photoname", "")
- .putString("text", "");
- pages.add(pageBuilder.build());
- }
-
- bookTag.putCompound("tag", NbtMap.builder().putList("pages", NbtType.COMPOUND, pages).build());
- lecternTag.putCompound("book", bookTag.build());
- NbtMap blockEntityTag = lecternTag.build();
- BlockEntityUtils.updateBlockEntity(session, blockEntityTag, Vector3i.from(x, y, z));
- };
-
- if (isChunkLoad) {
- // Delay to ensure the chunk is sent first, and then the lectern data
- Bukkit.getScheduler().runTaskLater(this.plugin, lecternInfoGet, 5);
- } else {
- Bukkit.getScheduler().runTask(this.plugin, lecternInfoGet);
+ public void sendLecternData(GeyserSession session, int x, int y, int z) {
+ Player bukkitPlayer;
+ if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) {
+ return;
+ }
+
+ Block block = bukkitPlayer.getWorld().getBlockAt(x, y, z);
+ // Run as a task to prevent async issues
+ SchedulerUtils.runTask(this.plugin, () -> sendLecternData(session, block, false), block);
+ }
+
+ public void sendLecternData(GeyserSession session, int x, int z, List blockEntityInfos) {
+ Player bukkitPlayer;
+ if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) {
+ return;
+ }
+ if (SchedulerUtils.FOLIA) {
+ Chunk chunk = getChunk(bukkitPlayer.getWorld(), x, z);
+ if (chunk == null) {
+ return;
+ }
+ Bukkit.getRegionScheduler().execute(this.plugin, bukkitPlayer.getWorld(), x, z, () ->
+ sendLecternData(session, chunk, blockEntityInfos));
+ } else {
+ Bukkit.getScheduler().runTask(this.plugin, () -> {
+ Chunk chunk = getChunk(bukkitPlayer.getWorld(), x, z);
+ if (chunk == null) {
+ return;
+ }
+ sendLecternData(session, chunk, blockEntityInfos);
+ });
+ }
+ }
+
+ private Chunk getChunk(World world, int x, int z) {
+ if (!world.isChunkLoaded(x, z)) {
+ return null;
+ }
+ return world.getChunkAt(x, z);
+ }
+
+ private void sendLecternData(GeyserSession session, Chunk chunk, List blockEntityInfos) {
+ for (int i = 0; i < blockEntityInfos.size(); i++) {
+ BlockEntityInfo info = blockEntityInfos.get(i);
+ Block block = chunk.getBlock(info.getX(), info.getY(), info.getZ());
+ sendLecternData(session, block, true);
+ }
+ }
+
+ private void sendLecternData(GeyserSession session, Block block, boolean isChunkLoad) {
+ NbtMap blockEntityTag = this.lecterns.getLecternData(block, isChunkLoad);
+ if (blockEntityTag != null) {
+ BlockEntityUtils.updateBlockEntity(session, blockEntityTag, BukkitUtils.getVector(block.getLocation()));
}
- return LecternInventoryTranslator.getBaseLecternTag(x, y, z, 0).build(); // Will be updated later
}
@Override
- public boolean shouldExpectLecternHandled() {
+ public boolean shouldExpectLecternHandled(GeyserSession session) {
return true;
}
@@ -184,42 +180,18 @@ public class GeyserSpigotWorldManager extends WorldManager {
@Override
public CompletableFuture<@Nullable CompoundTag> getPickItemNbt(GeyserSession session, int x, int y, int z, boolean addNbtData) {
CompletableFuture<@Nullable CompoundTag> future = new CompletableFuture<>();
+ Player bukkitPlayer;
+ if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUuid())) == null) {
+ future.complete(null);
+ return future;
+ }
+ Block block = bukkitPlayer.getWorld().getBlockAt(x, y, z);
// Paper 1.19.3 complains about async access otherwise.
// java.lang.IllegalStateException: Tile is null, asynchronous access?
- Bukkit.getScheduler().runTask(this.plugin, () -> {
- Player bukkitPlayer;
- if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUuid())) == null) {
- future.complete(null);
- return;
- }
-
- Block block = bukkitPlayer.getWorld().getBlockAt(x, y, z);
- BlockState state = block.getState();
- if (state instanceof Banner banner) {
- ListTag list = new ListTag("Patterns");
- for (int i = 0; i < banner.numberOfPatterns(); i++) {
- Pattern pattern = banner.getPattern(i);
- list.add(BannerTranslator.getJavaPatternTag(pattern.getPattern().getIdentifier(), pattern.getColor().ordinal()));
- }
-
- CompoundTag root = addToBlockEntityTag(list);
-
- future.complete(root);
- return;
- }
- future.complete(null);
- });
+ SchedulerUtils.runTask(this.plugin, () -> future.complete(PickBlockUtils.pickBlock(block)), block);
return future;
}
- private CompoundTag addToBlockEntityTag(Tag tag) {
- CompoundTag compoundTag = new CompoundTag("");
- CompoundTag blockEntityTag = new CompoundTag("BlockEntityTag");
- blockEntityTag.put(tag);
- compoundTag.put(blockEntityTag);
- return compoundTag;
- }
-
/**
* This should be set to true if we are post-1.13 but before the latest version, and we should convert the old block state id
* to the current one.
diff --git a/bootstrap/spigot/src/main/resources/plugin.yml b/bootstrap/spigot/src/main/resources/plugin.yml
index e28b8981d..6e81ccdb6 100644
--- a/bootstrap/spigot/src/main/resources/plugin.yml
+++ b/bootstrap/spigot/src/main/resources/plugin.yml
@@ -5,7 +5,12 @@ website: ${url}
version: ${version}
softdepend: ["ViaVersion", "floodgate"]
api-version: 1.13
+folia-supported: true
commands:
geyser:
description: The main command for Geyser.
- usage: /geyser
\ No newline at end of file
+ usage: /geyser
+ permission: geyser.command
+permissions:
+ geyser.command:
+ default: true
diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts
index e21806660..3d1fb47f7 100644
--- a/build-logic/build.gradle.kts
+++ b/build-logic/build.gradle.kts
@@ -1,5 +1,3 @@
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
-
plugins {
`kotlin-dsl`
}
@@ -10,17 +8,10 @@ repositories {
}
dependencies {
- implementation("net.kyori", "indra-common", "2.0.6")
- implementation("org.jfrog.buildinfo", "build-info-extractor-gradle", "4.26.1")
+ implementation("net.kyori", "indra-common", "3.0.1")
implementation("com.github.johnrengelman", "shadow", "7.1.3-SNAPSHOT")
// Within the gradle plugin classpath, there is a version conflict between loom and some other
// plugin for databind. This fixes it: minimum 2.13.2 is required by loom.
implementation("com.fasterxml.jackson.core:jackson-databind:2.14.0")
}
-
-tasks.withType {
- kotlinOptions {
- jvmTarget = "16"
- }
-}
diff --git a/build-logic/src/main/kotlin/extensions.kt b/build-logic/src/main/kotlin/extensions.kt
index 0c01913d2..1e1732852 100644
--- a/build-logic/src/main/kotlin/extensions.kt
+++ b/build-logic/src/main/kotlin/extensions.kt
@@ -30,9 +30,6 @@ import org.gradle.api.artifacts.ProjectDependency
import org.gradle.api.provider.Provider
import org.gradle.kotlin.dsl.named
-fun Project.isSnapshot(): Boolean =
- version.toString().endsWith("-SNAPSHOT")
-
fun Project.relocate(pattern: String) {
tasks.named("shadowJar") {
relocate(pattern, "org.geysermc.geyser.shaded.$pattern")
diff --git a/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts b/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts
index 44a74db3d..709867300 100644
--- a/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts
+++ b/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts
@@ -1,12 +1,25 @@
plugins {
`java-library`
- `maven-publish`
+ id("net.kyori.indra")
}
dependencies {
compileOnly("org.checkerframework", "checker-qual", "3.19.0")
}
+indra {
+ github("GeyserMC", "Geyser") {
+ ci(true)
+ issues(true)
+ scm(true)
+ }
+ mitLicense()
+
+ javaVersions {
+ target(16)
+ }
+}
+
tasks {
processResources {
// Spigot, BungeeCord, Velocity, Sponge, Fabric
@@ -21,14 +34,4 @@ tasks {
)
}
}
- compileJava {
- options.encoding = Charsets.UTF_8.name()
- }
-}
-
-java {
- sourceCompatibility = JavaVersion.VERSION_16
- targetCompatibility = JavaVersion.VERSION_16
-
- withSourcesJar()
}
diff --git a/build-logic/src/main/kotlin/geyser.publish-conventions.gradle.kts b/build-logic/src/main/kotlin/geyser.publish-conventions.gradle.kts
index 7525f97fa..036ee803c 100644
--- a/build-logic/src/main/kotlin/geyser.publish-conventions.gradle.kts
+++ b/build-logic/src/main/kotlin/geyser.publish-conventions.gradle.kts
@@ -1,33 +1,9 @@
plugins {
id("geyser.shadow-conventions")
- id("com.jfrog.artifactory")
- id("maven-publish")
+ id("net.kyori.indra.publishing")
}
-publishing {
- publications {
- create("mavenJava") {
- groupId = project.group as String
- artifactId = project.name
- version = project.version as String
-
- from(components["java"])
- }
- }
-}
-
-artifactory {
- setContextUrl("https://repo.opencollab.dev/artifactory")
- publish {
- repository {
- setRepoKey(if (isSnapshot()) "maven-snapshots" else "maven-releases")
- setMavenCompatible(true)
- }
- defaults {
- publications("mavenJava")
- setPublishArtifacts(true)
- setPublishPom(true)
- setPublishIvy(false)
- }
- }
+indra {
+ publishSnapshotsTo("geysermc", "https://repo.opencollab.dev/maven-snapshots")
+ publishReleasesTo("geysermc", "https://repo.opencollab.dev/maven-releases")
}
diff --git a/build-logic/src/main/kotlin/geyser.shadow-conventions.gradle.kts b/build-logic/src/main/kotlin/geyser.shadow-conventions.gradle.kts
index 395beb104..dde85c33a 100644
--- a/build-logic/src/main/kotlin/geyser.shadow-conventions.gradle.kts
+++ b/build-logic/src/main/kotlin/geyser.shadow-conventions.gradle.kts
@@ -24,6 +24,11 @@ tasks {
exclude(dependency(string))
}
}
+
+ sJar.dependencies {
+ exclude(dependency("org.checkerframework:checker-qual:.*"))
+ exclude(dependency("org.jetbrains:annotations:.*"))
+ }
}
}
named("build") {
diff --git a/core/build.gradle.kts b/core/build.gradle.kts
index 2c1e1fc1e..444a393fa 100644
--- a/core/build.gradle.kts
+++ b/core/build.gradle.kts
@@ -29,10 +29,6 @@ dependencies {
exclude("com.github.GeyserMC", "mcauthlib")
}
- api(libs.packetlib) {
- exclude("io.netty", "netty-all")
- }
-
implementation(libs.raknet) {
exclude("io.netty", "*");
}
@@ -51,6 +47,10 @@ dependencies {
// Adventure text serialization
api(libs.bundles.adventure)
+ api(libs.erosion.common) {
+ isTransitive = false
+ }
+
// Test
testImplementation(libs.junit)
diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java
index 73ea88f37..e9c567473 100644
--- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java
+++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java
@@ -47,6 +47,7 @@ import org.geysermc.api.Geyser;
import org.geysermc.common.PlatformType;
import org.geysermc.cumulus.form.Form;
import org.geysermc.cumulus.form.util.FormBuilder;
+import org.geysermc.erosion.packet.Packets;
import org.geysermc.floodgate.crypto.AesCipher;
import org.geysermc.floodgate.crypto.AesKeyProducer;
import org.geysermc.floodgate.crypto.Base64Topping;
@@ -64,6 +65,7 @@ import org.geysermc.geyser.api.network.RemoteServer;
import org.geysermc.geyser.command.GeyserCommandManager;
import org.geysermc.geyser.configuration.GeyserConfiguration;
import org.geysermc.geyser.entity.EntityDefinitions;
+import org.geysermc.geyser.erosion.UnixSocketClientListener;
import org.geysermc.geyser.event.GeyserEventBus;
import org.geysermc.geyser.extension.GeyserExtensionManager;
import org.geysermc.geyser.level.WorldManager;
@@ -137,6 +139,8 @@ public class GeyserImpl implements GeyserApi {
private FloodgateSkinUploader skinUploader;
private NewsHandler newsHandler;
+ private UnixSocketClientListener erosionUnixListener;
+
private volatile boolean shuttingDown = false;
private ScheduledExecutorService scheduledThread;
@@ -290,6 +294,14 @@ public class GeyserImpl implements GeyserApi {
this.newsHandler = new NewsHandler(BRANCH, this.buildNumber());
+ Packets.initGeyser();
+
+ if (Epoll.isAvailable()) {
+ this.erosionUnixListener = new UnixSocketClientListener();
+ } else {
+ logger.debug("Epoll is not available; Erosion's Unix socket handling will not work.");
+ }
+
CooldownUtils.setDefaultShowCooldown(config.getShowCooldown());
DimensionUtils.changeBedrockNetherId(config.isAboveBedrockNetherBuilding()); // Apply End dimension ID workaround to Nether
@@ -542,6 +554,10 @@ public class GeyserImpl implements GeyserApi {
newsHandler.shutdown();
this.commandManager().getCommands().clear();
+ if (this.erosionUnixListener != null) {
+ this.erosionUnixListener.close();
+ }
+
ResourcePack.PACKS.clear();
this.eventBus.fire(new GeyserShutdownEvent(this.extensionManager, this.eventBus));
diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/OffhandCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/OffhandCommand.java
index 3258e8d22..6188e6924 100644
--- a/core/src/main/java/org/geysermc/geyser/command/defaults/OffhandCommand.java
+++ b/core/src/main/java/org/geysermc/geyser/command/defaults/OffhandCommand.java
@@ -25,10 +25,6 @@
package org.geysermc.geyser.command.defaults;
-import com.github.steveice10.mc.protocol.data.game.entity.object.Direction;
-import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction;
-import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundPlayerActionPacket;
-import org.cloudburstmc.math.vector.Vector3i;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.command.GeyserCommand;
import org.geysermc.geyser.command.GeyserCommandSource;
@@ -46,9 +42,7 @@ public class OffhandCommand extends GeyserCommand {
return;
}
- ServerboundPlayerActionPacket releaseItemPacket = new ServerboundPlayerActionPacket(PlayerAction.SWAP_HANDS, Vector3i.ZERO,
- Direction.DOWN, 0);
- session.sendDownstreamPacket(releaseItemPacket);
+ session.requestOffhandSwap();
}
@Override
diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java
index 4843df72b..ea4c31876 100644
--- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java
+++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java
@@ -83,8 +83,6 @@ public interface GeyserConfiguration {
boolean isDisableBedrockScaffolding();
- boolean isAlwaysQuickChangeArmor();
-
EmoteOffhandWorkaroundOption getEmoteOffhandWorkaround();
String getDefaultLocale();
diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java
index dc675319b..bbfa37ec2 100644
--- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java
+++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java
@@ -111,9 +111,6 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
@JsonProperty("disable-bedrock-scaffolding")
private boolean disableBedrockScaffolding = false;
- @JsonProperty("always-quick-change-armor")
- private boolean alwaysQuickChangeArmor = false;
-
@JsonDeserialize(using = EmoteOffhandWorkaroundOption.Deserializer.class)
@JsonProperty("emote-offhand-workaround")
private EmoteOffhandWorkaroundOption emoteOffhandWorkaround = EmoteOffhandWorkaroundOption.DISABLED;
diff --git a/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java b/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java
index c3b1d49c4..9c7e19853 100644
--- a/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java
+++ b/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java
@@ -148,6 +148,7 @@ public final class EntityDefinitions {
public static final EntityDefinition STRAY;
public static final EntityDefinition STRIDER;
public static final EntityDefinition TADPOLE;
+ public static final EntityDefinition TEXT_DISPLAY;
public static final EntityDefinition TNT;
public static final EntityDefinition TNT_MINECART;
public static final EntityDefinition TRADER_LLAMA;
@@ -295,6 +296,28 @@ public final class EntityDefinitions {
.addTranslator(MetadataType.INT, TNTEntity::setFuseLength)
.build();
+ EntityDefinition displayBase = EntityDefinition.inherited(entityBase.factory(), entityBase)
+ .addTranslator(null) // Interpolation start ticks
+ .addTranslator(null) // Interpolation duration ID
+ .addTranslator(null) // Translation
+ .addTranslator(null) // Scale
+ .addTranslator(null) // Left rotation
+ .addTranslator(null) // Right rotation
+ .addTranslator(null) // Billboard render constraints
+ .addTranslator(null) // Brightness override
+ .addTranslator(null) // View range
+ .addTranslator(null) // Shadow radius
+ .addTranslator(null) // Shadow strength
+ .addTranslator(null) // Width
+ .addTranslator(null) // Height
+ .addTranslator(null) // Glow color override
+ .build();
+ TEXT_DISPLAY = EntityDefinition.inherited(TextDisplayEntity::new, displayBase)
+ .type(EntityType.TEXT_DISPLAY)
+ .identifier("minecraft:armor_stand")
+ .addTranslator(MetadataType.CHAT, TextDisplayEntity::setText)
+ .build();
+
EntityDefinition fireballBase = EntityDefinition.inherited(FireballEntity::new, entityBase)
.addTranslator(null) // Item
.build();
@@ -486,7 +509,7 @@ public final class EntityDefinitions {
ENDERMAN = EntityDefinition.inherited(EndermanEntity::new, mobEntityBase)
.type(EntityType.ENDERMAN)
.height(2.9f).width(0.6f)
- .addTranslator(MetadataType.BLOCK_STATE, EndermanEntity::setCarriedBlock)
+ .addTranslator(MetadataType.OPTIONAL_BLOCK_STATE, EndermanEntity::setCarriedBlock)
.addTranslator(MetadataType.BOOLEAN, EndermanEntity::setScreaming)
.addTranslator(MetadataType.BOOLEAN, EndermanEntity::setAngry)
.build();
@@ -858,7 +881,6 @@ public final class EntityDefinitions {
{
EntityDefinition abstractHorseEntityBase = EntityDefinition.inherited(AbstractHorseEntity::new, ageableEntityBase)
.addTranslator(MetadataType.BYTE, AbstractHorseEntity::setHorseFlags)
- .addTranslator(null) // UUID of owner
.build();
CAMEL = EntityDefinition.inherited(CamelEntity::new, abstractHorseEntityBase)
.type(EntityType.CAMEL)
diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/BoatEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/BoatEntity.java
index f9da5c69f..1bf6e581e 100644
--- a/core/src/main/java/org/geysermc/geyser/entity/type/BoatEntity.java
+++ b/core/src/main/java/org/geysermc/geyser/entity/type/BoatEntity.java
@@ -124,7 +124,11 @@ public class BoatEntity extends Entity {
public void setVariant(IntEntityMetadata entityMetadata) {
variant = entityMetadata.getPrimitiveValue();
- dirtyMetadata.put(EntityDataTypes.VARIANT, variant);
+ dirtyMetadata.put(EntityDataTypes.VARIANT, switch (variant) {
+ case 6, 7 -> variant - 1; // Dark oak and mangrove
+ case 5, 8 -> 0; // TODO temp until 1.20. Cherry and bamboo
+ default -> variant;
+ });
}
public void setPaddlingLeft(BooleanEntityMetadata entityMetadata) {
diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/FishingHookEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/FishingHookEntity.java
index 2885cfd23..bcbff16ce 100644
--- a/core/src/main/java/org/geysermc/geyser/entity/type/FishingHookEntity.java
+++ b/core/src/main/java/org/geysermc/geyser/entity/type/FishingHookEntity.java
@@ -30,12 +30,11 @@ import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.packet.PlaySoundPacket;
import lombok.Getter;
+import org.geysermc.erosion.util.BlockPositionIterator;
import org.geysermc.geyser.entity.EntityDefinitions;
import org.geysermc.geyser.entity.type.player.PlayerEntity;
-import org.geysermc.geyser.level.block.BlockPositionIterator;
import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.level.physics.BoundingBox;
-import org.geysermc.geyser.registry.BlockRegistries;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.collision.BlockCollision;
import org.geysermc.geyser.util.BlockUtils;
diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/ItemEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/ItemEntity.java
index 19125a0a8..bb67a60f6 100644
--- a/core/src/main/java/org/geysermc/geyser/entity/type/ItemEntity.java
+++ b/core/src/main/java/org/geysermc/geyser/entity/type/ItemEntity.java
@@ -40,11 +40,12 @@ import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.inventory.item.ItemTranslator;
import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
public class ItemEntity extends ThrowableEntity {
protected ItemData item;
- private int waterLevel = -1;
+ private CompletableFuture waterLevel = CompletableFuture.completedFuture(-1);
public ItemEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
@@ -111,15 +112,15 @@ public class ItemEntity extends ThrowableEntity {
@Override
protected void moveAbsoluteImmediate(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) {
float offset = definition.offset();
- if (waterLevel == 0) { // Item is in a full block of water
+ if (waterLevel.join() == 0) { // Item is in a full block of water
// Move the item entity down so it doesn't float above the water
offset = -definition.offset();
}
super.moveAbsoluteImmediate(position.add(0, offset, 0), 0, 0, 0, isOnGround, teleported);
this.position = position;
- int block = session.getGeyser().getWorldManager().getBlockAt(session, position.toInt());
- waterLevel = BlockStateValues.getWaterLevel(block);
+ waterLevel = session.getGeyser().getWorldManager().getBlockAtAsync(session, position.getFloorX(), position.getFloorY(), position.getFloorZ())
+ .thenApply(BlockStateValues::getWaterLevel);
}
@Override
@@ -144,6 +145,6 @@ public class ItemEntity extends ThrowableEntity {
@Override
protected boolean isInWater() {
- return waterLevel != -1;
+ return waterLevel.join() != -1;
}
}
diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/TNTEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/TNTEntity.java
index d4f1ae762..98c2edd00 100644
--- a/core/src/main/java/org/geysermc/geyser/entity/type/TNTEntity.java
+++ b/core/src/main/java/org/geysermc/geyser/entity/type/TNTEntity.java
@@ -43,7 +43,7 @@ public class TNTEntity extends Entity implements Tickable {
}
public void setFuseLength(IntEntityMetadata entityMetadata) {
- currentTick = ((IntEntityMetadata) entityMetadata).getPrimitiveValue();
+ currentTick = entityMetadata.getPrimitiveValue();
setFlag(EntityFlag.IGNITED, true);
dirtyMetadata.put(EntityDataTypes.FUSE_TIME, currentTick);
}
diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/TextDisplayEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/TextDisplayEntity.java
new file mode 100644
index 000000000..f2671f7a9
--- /dev/null
+++ b/core/src/main/java/org/geysermc/geyser/entity/type/TextDisplayEntity.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.geyser.entity.type;
+
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
+import net.kyori.adventure.text.Component;
+import org.cloudburstmc.math.vector.Vector3f;
+import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
+import org.geysermc.geyser.entity.EntityDefinition;
+import org.geysermc.geyser.session.GeyserSession;
+import org.geysermc.geyser.translator.text.MessageTranslator;
+
+import java.util.UUID;
+
+// Note: 1.19.4 requires that the billboard is set to something in order to show, on Java Edition
+public class TextDisplayEntity extends Entity {
+ public TextDisplayEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
+ super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
+ }
+
+ @Override
+ protected void initializeMetadata() {
+ super.initializeMetadata();
+ // Remove armor stand body
+ this.dirtyMetadata.put(EntityDataTypes.SCALE, 0f);
+ this.dirtyMetadata.put(EntityDataTypes.NAMETAG_ALWAYS_SHOW, (byte) 1);
+ }
+
+ public void setText(EntityMetadata entityMetadata) {
+ this.dirtyMetadata.put(EntityDataTypes.NAME, MessageTranslator.convertMessage(entityMetadata.getValue()));
+ }
+}
diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/SquidEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/SquidEntity.java
index 7e1a39dd5..80a5af442 100644
--- a/core/src/main/java/org/geysermc/geyser/entity/type/living/SquidEntity.java
+++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/SquidEntity.java
@@ -34,12 +34,13 @@ import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.session.GeyserSession;
import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
public class SquidEntity extends WaterEntity implements Tickable {
private float targetPitch;
private float targetYaw;
- private boolean inWater;
+ private CompletableFuture inWater = CompletableFuture.completedFuture(Boolean.FALSE);
public SquidEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
@@ -50,7 +51,7 @@ public class SquidEntity extends WaterEntity implements Tickable {
boolean pitchChanged;
boolean yawChanged;
float oldPitch = pitch;
- if (inWater) {
+ if (inWater.join()) {
float oldYaw = yaw;
pitch += (targetPitch - pitch) * 0.1f;
yaw += (targetYaw - yaw) * 0.1f;
@@ -93,7 +94,7 @@ public class SquidEntity extends WaterEntity implements Tickable {
@Override
public void setYaw(float yaw) {
// Let the Java server control yaw when the squid is out of water
- if (!inWater) {
+ if (!inWater.join()) {
this.yaw = yaw;
}
}
@@ -127,10 +128,10 @@ public class SquidEntity extends WaterEntity implements Tickable {
private void checkInWater() {
if (getFlag(EntityFlag.RIDING)) {
- inWater = false;
+ inWater = CompletableFuture.completedFuture(false);
} else {
- int block = session.getGeyser().getWorldManager().getBlockAt(session, position.toInt());
- inWater = BlockStateValues.getWaterLevel(block) != -1;
+ inWater = session.getGeyser().getWorldManager().getBlockAtAsync(session, position.toInt())
+ .thenApply(block -> BlockStateValues.getWaterLevel(block) != -1);
}
}
}
diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/EndermanEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/EndermanEntity.java
index 0634f4727..0d52b7a35 100644
--- a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/EndermanEntity.java
+++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/EndermanEntity.java
@@ -25,9 +25,8 @@
package org.geysermc.geyser.entity.type.living.monster;
-import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
-import com.github.steveice10.mc.protocol.data.game.entity.metadata.OptionalIntMetadataType;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.SoundEvent;
import org.cloudburstmc.protocol.bedrock.data.defintions.BlockDefinition;
@@ -37,7 +36,6 @@ import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEvent2Packet;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.session.GeyserSession;
-import java.util.OptionalInt;
import java.util.UUID;
public class EndermanEntity extends MonsterEntity {
@@ -46,13 +44,8 @@ public class EndermanEntity extends MonsterEntity {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
- public void setCarriedBlock(EntityMetadata entityMetadata) {
- BlockDefinition bedrockBlockId;
- if (entityMetadata.getValue().isPresent()) {
- bedrockBlockId = session.getBlockMappings().getBedrockBlock(entityMetadata.getValue().getAsInt());
- } else {
- bedrockBlockId = session.getBlockMappings().getBedrockAir();
- }
+ public void setCarriedBlock(IntEntityMetadata entityMetadata) {
+ BlockDefinition bedrockBlockId = session.getBlockMappings().getBedrockBlock(entityMetadata.getPrimitiveValue());
dirtyMetadata.put(EntityDataTypes.CARRY_BLOCK_STATE, bedrockBlockId);
}
diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/ShulkerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/ShulkerEntity.java
index ee622dc42..27dd45f40 100644
--- a/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/ShulkerEntity.java
+++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/monster/ShulkerEntity.java
@@ -43,6 +43,15 @@ public class ShulkerEntity extends GolemEntity {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
// Indicate that invisibility should be fixed through the resource pack
setFlag(EntityFlag.BRIBED, true);
+
+ }
+
+ @Override
+ protected void initializeMetadata() {
+ super.initializeMetadata();
+ // As of 1.19.4, it seems Java no longer sends the shulker color if it's the default color on initial spawn
+ // We still need the special case for 16 color in setShulkerColor though as it will send it for an entity metadata update
+ dirtyMetadata.put(EntityDataTypes.VARIANT, 16);
}
public void setAttachedFace(EntityMetadata entityMetadata) {
diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java
index 547c9c559..b060758ae 100644
--- a/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java
+++ b/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java
@@ -247,7 +247,10 @@ public class PlayerEntity extends LivingEntity {
@Override
public Vector3i setBedPosition(EntityMetadata, ?> entityMetadata) {
- return bedPosition = super.setBedPosition(entityMetadata);
+ bedPosition = super.setBedPosition(entityMetadata);
+ // Fixes https://github.com/GeyserMC/Geyser/issues/3595 on vanilla 1.19.3 servers - did not happen on Paper
+ entityMetadata.getValue().ifPresent(pos -> this.setPosition(pos.toFloat()));
+ return bedPosition;
}
public void setAbsorptionHearts(FloatEntityMetadata entityMetadata) {
diff --git a/core/src/main/java/org/geysermc/geyser/erosion/AbstractGeyserboundPacketHandler.java b/core/src/main/java/org/geysermc/geyser/erosion/AbstractGeyserboundPacketHandler.java
new file mode 100644
index 000000000..eabed8f7b
--- /dev/null
+++ b/core/src/main/java/org/geysermc/geyser/erosion/AbstractGeyserboundPacketHandler.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.geyser.erosion;
+
+import org.geysermc.erosion.packet.geyserbound.*;
+import org.geysermc.geyser.session.GeyserSession;
+import org.jetbrains.annotations.Nullable;
+
+public abstract class AbstractGeyserboundPacketHandler implements GeyserboundPacketHandler {
+ protected final GeyserSession session;
+
+ public AbstractGeyserboundPacketHandler(GeyserSession session) {
+ this.session = session;
+ }
+
+ @Override
+ public void handleBatchBlockId(GeyserboundBatchBlockIdPacket packet) {
+ illegalPacket(packet);
+ }
+
+ @Override
+ public void handleBlockEntity(GeyserboundBlockEntityPacket packet) {
+ illegalPacket(packet);
+ }
+
+ @Override
+ public void handleBlockId(GeyserboundBlockIdPacket packet) {
+ illegalPacket(packet);
+ }
+
+ @Override
+ public void handleBlockLookupFail(GeyserboundBlockLookupFailPacket packet) {
+ illegalPacket(packet);
+ }
+
+ @Override
+ public void handleBlockPlace(GeyserboundBlockPlacePacket packet) {
+ illegalPacket(packet);
+ }
+
+ @Override
+ public void handlePistonEvent(GeyserboundPistonEventPacket packet) {
+ illegalPacket(packet);
+ }
+
+ @Override
+ public void handlePickBlock(GeyserboundPickBlockPacket packet) {
+ illegalPacket(packet);
+ }
+
+ /**
+ * Is this handler actually listening to any packets?
+ */
+ public abstract boolean isActive();
+
+ @Nullable
+ public abstract GeyserboundPacketHandlerImpl getAsActive();
+
+ public void close() {
+ }
+
+ protected final void illegalPacket(GeyserboundPacket packet) {
+ session.getGeyser().getLogger().warning("Illegal packet sent from backend server! " + packet);
+ }
+}
diff --git a/core/src/main/java/org/geysermc/geyser/erosion/GeyserErosionPacketSender.java b/core/src/main/java/org/geysermc/geyser/erosion/GeyserErosionPacketSender.java
new file mode 100644
index 000000000..610917adc
--- /dev/null
+++ b/core/src/main/java/org/geysermc/geyser/erosion/GeyserErosionPacketSender.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.geyser.erosion;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import org.geysermc.erosion.Constants;
+import org.geysermc.erosion.packet.ErosionPacketSender;
+import org.geysermc.erosion.packet.Packets;
+import org.geysermc.erosion.packet.backendbound.BackendboundPacket;
+import org.geysermc.geyser.session.GeyserSession;
+import org.geysermc.geyser.util.PluginMessageUtils;
+
+import java.io.IOException;
+
+public record GeyserErosionPacketSender(GeyserSession session) implements ErosionPacketSender {
+
+ @Override
+ public void sendPacket(BackendboundPacket packet) {
+ ByteBuf buf = Unpooled.buffer();
+ try {
+ Packets.encode(buf, packet);
+ byte[] bytes = new byte[buf.readableBytes()];
+ buf.readBytes(bytes);
+ PluginMessageUtils.sendMessage(session, Constants.PLUGIN_MESSAGE, bytes);
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ buf.release();
+ }
+ }
+
+ @Override
+ public void setChannel(Channel channel) {
+ }
+}
diff --git a/core/src/main/java/org/geysermc/geyser/erosion/GeyserboundHandshakePacketHandler.java b/core/src/main/java/org/geysermc/geyser/erosion/GeyserboundHandshakePacketHandler.java
new file mode 100644
index 000000000..196595383
--- /dev/null
+++ b/core/src/main/java/org/geysermc/geyser/erosion/GeyserboundHandshakePacketHandler.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.geyser.erosion;
+
+import io.netty.channel.Channel;
+import org.geysermc.erosion.netty.NettyPacketSender;
+import org.geysermc.erosion.packet.ErosionPacketHandler;
+import org.geysermc.erosion.packet.geyserbound.GeyserboundHandshakePacket;
+import org.geysermc.geyser.session.GeyserSession;
+import org.jetbrains.annotations.Nullable;
+
+public final class GeyserboundHandshakePacketHandler extends AbstractGeyserboundPacketHandler {
+
+ public GeyserboundHandshakePacketHandler(GeyserSession session) {
+ super(session);
+ }
+
+ @Override
+ public void handleHandshake(GeyserboundHandshakePacket packet) {
+ boolean useTcp = packet.getTransportType().getSocketAddress() == null;
+ GeyserboundPacketHandlerImpl handler = new GeyserboundPacketHandlerImpl(session, useTcp ? new GeyserErosionPacketSender(session) : new NettyPacketSender<>());
+ session.setErosionHandler(handler);
+ if (!useTcp) {
+ if (session.getGeyser().getErosionUnixListener() == null) {
+ session.disconnect("Erosion configurations using Unix socket handling are not supported on this hardware!");
+ return;
+ }
+ session.getGeyser().getErosionUnixListener().createClient(handler, packet.getTransportType().getSocketAddress());
+ } else {
+ handler.onConnect();
+ }
+ session.ensureInEventLoop(() -> session.getChunkCache().clear());
+ }
+
+ @Override
+ public boolean isActive() {
+ return false;
+ }
+
+ @Override
+ public @Nullable GeyserboundPacketHandlerImpl getAsActive() {
+ return null;
+ }
+
+ @Override
+ public ErosionPacketHandler setChannel(Channel channel) {
+ return null;
+ }
+}
diff --git a/core/src/main/java/org/geysermc/geyser/erosion/GeyserboundPacketHandlerImpl.java b/core/src/main/java/org/geysermc/geyser/erosion/GeyserboundPacketHandlerImpl.java
new file mode 100644
index 000000000..3ae458f63
--- /dev/null
+++ b/core/src/main/java/org/geysermc/geyser/erosion/GeyserboundPacketHandlerImpl.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.geyser.erosion;
+
+import com.github.steveice10.mc.protocol.data.game.level.block.value.PistonValueType;
+import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
+import io.netty.channel.Channel;
+import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
+import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
+import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
+import it.unimi.dsi.fastutil.objects.Object2IntArrayMap;
+import it.unimi.dsi.fastutil.objects.Object2IntMap;
+import lombok.Getter;
+import lombok.Setter;
+import org.cloudburstmc.math.vector.Vector3i;
+import org.cloudburstmc.nbt.NbtMap;
+import org.cloudburstmc.protocol.bedrock.data.SoundEvent;
+import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEventPacket;
+import org.geysermc.erosion.packet.ErosionPacketHandler;
+import org.geysermc.erosion.packet.ErosionPacketSender;
+import org.geysermc.erosion.packet.backendbound.BackendboundInitializePacket;
+import org.geysermc.erosion.packet.backendbound.BackendboundPacket;
+import org.geysermc.erosion.packet.geyserbound.*;
+import org.geysermc.geyser.level.block.BlockStateValues;
+import org.geysermc.geyser.level.physics.Direction;
+import org.geysermc.geyser.network.GameProtocol;
+import org.geysermc.geyser.session.GeyserSession;
+import org.geysermc.geyser.session.cache.PistonCache;
+import org.geysermc.geyser.translator.level.block.entity.PistonBlockEntity;
+import org.geysermc.geyser.util.BlockEntityUtils;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public final class GeyserboundPacketHandlerImpl extends AbstractGeyserboundPacketHandler {
+ private final ErosionPacketSender packetSender;
+ @Setter
+ private CompletableFuture pendingLookup = null;
+ @Getter
+ private final Int2ObjectMap> asyncPendingLookups = Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap<>(4));
+ @Setter
+ private CompletableFuture pendingBatchLookup = null;
+ @Setter
+ private CompletableFuture pickBlockLookup = null;
+
+ private final AtomicInteger nextTransactionId = new AtomicInteger(1);
+
+ public GeyserboundPacketHandlerImpl(GeyserSession session, ErosionPacketSender packetSender) {
+ super(session);
+ this.packetSender = packetSender;
+ }
+
+ @Override
+ public void handleBatchBlockId(GeyserboundBatchBlockIdPacket packet) {
+ if (this.pendingBatchLookup != null) {
+ this.pendingBatchLookup.complete(packet.getBlocks());
+ } else {
+ session.getGeyser().getLogger().warning("Batch block ID packet received with no future to complete.");
+ }
+ }
+
+ @Override
+ public void handleBlockEntity(GeyserboundBlockEntityPacket packet) {
+ NbtMap nbt = packet.getNbt();
+ BlockEntityUtils.updateBlockEntity(session, nbt, Vector3i.from(nbt.getInt("x"), nbt.getInt("y"), nbt.getInt("z")));
+ }
+
+ @Override
+ public void handleBlockId(GeyserboundBlockIdPacket packet) {
+ if (packet.getTransactionId() == 0) {
+ if (this.pendingLookup != null) {
+ this.pendingLookup.complete(packet.getBlockId());
+ return;
+ }
+ }
+ CompletableFuture future = this.asyncPendingLookups.remove(packet.getTransactionId());
+ if (future != null) {
+ future.complete(packet.getBlockId());
+ return;
+ }
+ session.getGeyser().getLogger().warning("Block ID packet received with no future to complete.");
+ }
+
+ @Override
+ public void handleBlockLookupFail(GeyserboundBlockLookupFailPacket packet) {
+ if (packet.getTransactionId() == 0) {
+ if (this.pendingBatchLookup != null) {
+ this.pendingBatchLookup.complete(null);
+ return;
+ }
+ }
+ int transactionId = packet.getTransactionId() - 1;
+ if (transactionId == 0) {
+ if (this.pendingLookup != null) {
+ this.pendingLookup.complete(0);
+ }
+ }
+ CompletableFuture future = this.asyncPendingLookups.remove(transactionId);
+ if (future != null) {
+ future.complete(BlockStateValues.JAVA_AIR_ID);
+ }
+ }
+
+ @Override
+ public void handleBlockPlace(GeyserboundBlockPlacePacket packet) {
+ LevelSoundEventPacket placeBlockSoundPacket = new LevelSoundEventPacket();
+ placeBlockSoundPacket.setSound(SoundEvent.PLACE);
+ placeBlockSoundPacket.setPosition(packet.getPos().toFloat());
+ placeBlockSoundPacket.setBabySound(false);
+ placeBlockSoundPacket.setExtraData(session.getBlockMappings().getBedrockBlockId(packet.getBlockId()));
+ placeBlockSoundPacket.setIdentifier(":");
+ session.sendUpstreamPacket(placeBlockSoundPacket);
+ session.setLastBlockPlacePosition(null);
+ session.setLastBlockPlacedId(null);
+ }
+
+ @Override
+ public void handlePickBlock(GeyserboundPickBlockPacket packet) {
+ if (this.pickBlockLookup != null) {
+ this.pickBlockLookup.complete(packet.getTag());
+ }
+ }
+
+ @Override
+ public void handlePistonEvent(GeyserboundPistonEventPacket packet) {
+ Direction orientation = BlockStateValues.getPistonOrientation(packet.getBlockId());
+ Vector3i position = packet.getPos();
+ boolean isExtend = packet.isExtend();
+
+ var stream = packet.getAttachedBlocks()
+ .object2IntEntrySet()
+ .stream()
+ .filter(entry -> BlockStateValues.canPistonMoveBlock(entry.getIntValue(), isExtend));
+ Object2IntMap attachedBlocks = new Object2IntArrayMap<>();
+ stream.forEach(entry -> attachedBlocks.put(entry.getKey(), entry.getIntValue()));
+
+ session.executeInEventLoop(() -> {
+ PistonCache pistonCache = session.getPistonCache();
+ PistonBlockEntity blockEntity = pistonCache.getPistons().computeIfAbsent(position, pos ->
+ new PistonBlockEntity(session, position, orientation, packet.isSticky(), !isExtend));
+ blockEntity.setAction(isExtend ? PistonValueType.PUSHING : PistonValueType.PULLING, attachedBlocks);
+ });
+ }
+
+ @Override
+ public void handleHandshake(GeyserboundHandshakePacket packet) {
+ this.close();
+ var handler = new GeyserboundHandshakePacketHandler(this.session);
+ session.setErosionHandler(handler);
+ handler.handleHandshake(packet);
+ }
+
+ @Override
+ public boolean isActive() {
+ return true;
+ }
+
+ @Override
+ public GeyserboundPacketHandlerImpl getAsActive() {
+ return this;
+ }
+
+ @Override
+ public void onConnect() {
+ sendPacket(new BackendboundInitializePacket(session.getPlayerEntity().getUuid(), GameProtocol.getJavaProtocolVersion()));
+ }
+
+ public void sendPacket(BackendboundPacket packet) {
+ this.packetSender.sendPacket(packet);
+ }
+
+ public void close() {
+ this.packetSender.close();
+ }
+
+ public int getNextTransactionId() {
+ return nextTransactionId.getAndIncrement();
+ }
+
+ @Override
+ public ErosionPacketHandler setChannel(Channel channel) {
+ this.packetSender.setChannel(channel);
+ return this;
+ }
+}
diff --git a/core/src/main/java/org/geysermc/geyser/erosion/UnixSocketClientListener.java b/core/src/main/java/org/geysermc/geyser/erosion/UnixSocketClientListener.java
new file mode 100644
index 000000000..a236099df
--- /dev/null
+++ b/core/src/main/java/org/geysermc/geyser/erosion/UnixSocketClientListener.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.geyser.erosion;
+
+import io.netty.bootstrap.Bootstrap;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.epoll.EpollDomainSocketChannel;
+import io.netty.channel.epoll.EpollEventLoopGroup;
+import org.geysermc.erosion.netty.impl.AbstractUnixSocketListener;
+import org.geysermc.erosion.packet.geyserbound.GeyserboundPacketHandler;
+
+import java.net.SocketAddress;
+
+public final class UnixSocketClientListener extends AbstractUnixSocketListener {
+ private EventLoopGroup eventLoopGroup;
+
+ public void initializeEventLoopGroup() {
+ if (this.eventLoopGroup == null) {
+ this.eventLoopGroup = new EpollEventLoopGroup();
+ }
+ }
+
+ public void createClient(GeyserboundPacketHandler handler, SocketAddress address) {
+ initializeEventLoopGroup();
+ (new Bootstrap()
+ .channel(EpollDomainSocketChannel.class)
+ .handler(new ChannelInitializer() {
+ @Override
+ protected void initChannel(Channel ch) {
+ initPipeline(ch, handler);
+ }
+ })
+ .group(this.eventLoopGroup.next())
+ .connect(address))
+ .syncUninterruptibly()
+ .channel();
+ }
+
+ @Override
+ public void close() {
+ if (this.eventLoopGroup != null) {
+ this.eventLoopGroup.shutdownGracefully();
+ }
+ }
+}
diff --git a/core/src/main/java/org/geysermc/geyser/inventory/Inventory.java b/core/src/main/java/org/geysermc/geyser/inventory/Inventory.java
index 9f8c603e1..1dbde84f4 100644
--- a/core/src/main/java/org/geysermc/geyser/inventory/Inventory.java
+++ b/core/src/main/java/org/geysermc/geyser/inventory/Inventory.java
@@ -91,6 +91,10 @@ public abstract class Inventory {
@Setter
private boolean pending = false;
+ @Getter
+ @Setter
+ private boolean displayed = false;
+
protected Inventory(int id, int size, ContainerType containerType) {
this("Inventory", id, size, containerType);
}
diff --git a/core/src/main/java/org/geysermc/geyser/item/Items.java b/core/src/main/java/org/geysermc/geyser/item/Items.java
index 8c7a43223..ad13faf19 100644
--- a/core/src/main/java/org/geysermc/geyser/item/Items.java
+++ b/core/src/main/java/org/geysermc/geyser/item/Items.java
@@ -63,6 +63,7 @@ public final class Items {
public static final Item BIRCH_PLANKS = register(new Item("birch_planks", builder()));
public static final Item JUNGLE_PLANKS = register(new Item("jungle_planks", builder()));
public static final Item ACACIA_PLANKS = register(new Item("acacia_planks", builder()));
+ public static final Item CHERRY_PLANKS = register(new Item("cherry_planks", builder()));
public static final Item DARK_OAK_PLANKS = register(new Item("dark_oak_planks", builder()));
public static final Item MANGROVE_PLANKS = register(new Item("mangrove_planks", builder()));
public static final Item BAMBOO_PLANKS = register(new Item("bamboo_planks", builder()));
@@ -74,10 +75,12 @@ public final class Items {
public static final Item BIRCH_SAPLING = register(new Item("birch_sapling", builder()));
public static final Item JUNGLE_SAPLING = register(new Item("jungle_sapling", builder()));
public static final Item ACACIA_SAPLING = register(new Item("acacia_sapling", builder()));
+ public static final Item CHERRY_SAPLING = register(new Item("cherry_sapling", builder()));
public static final Item DARK_OAK_SAPLING = register(new Item("dark_oak_sapling", builder()));
public static final Item MANGROVE_PROPAGULE = register(new Item("mangrove_propagule", builder()));
public static final Item BEDROCK = register(new Item("bedrock", builder()));
public static final Item SAND = register(new Item("sand", builder()));
+ public static final Item SUSPICIOUS_SAND = register(new Item("suspicious_sand", builder()));
public static final Item RED_SAND = register(new Item("red_sand", builder()));
public static final Item GRAVEL = register(new Item("gravel", builder()));
public static final Item COAL_ORE = register(new Item("coal_ore", builder()));
@@ -146,6 +149,7 @@ public final class Items {
public static final Item BIRCH_LOG = register(new Item("birch_log", builder()));
public static final Item JUNGLE_LOG = register(new Item("jungle_log", builder()));
public static final Item ACACIA_LOG = register(new Item("acacia_log", builder()));
+ public static final Item CHERRY_LOG = register(new Item("cherry_log", builder()));
public static final Item DARK_OAK_LOG = register(new Item("dark_oak_log", builder()));
public static final Item MANGROVE_LOG = register(new Item("mangrove_log", builder()));
public static final Item MANGROVE_ROOTS = register(new Item("mangrove_roots", builder()));
@@ -158,6 +162,7 @@ public final class Items {
public static final Item STRIPPED_BIRCH_LOG = register(new Item("stripped_birch_log", builder()));
public static final Item STRIPPED_JUNGLE_LOG = register(new Item("stripped_jungle_log", builder()));
public static final Item STRIPPED_ACACIA_LOG = register(new Item("stripped_acacia_log", builder()));
+ public static final Item STRIPPED_CHERRY_LOG = register(new Item("stripped_cherry_log", builder()));
public static final Item STRIPPED_DARK_OAK_LOG = register(new Item("stripped_dark_oak_log", builder()));
public static final Item STRIPPED_MANGROVE_LOG = register(new Item("stripped_mangrove_log", builder()));
public static final Item STRIPPED_CRIMSON_STEM = register(new Item("stripped_crimson_stem", builder()));
@@ -167,6 +172,7 @@ public final class Items {
public static final Item STRIPPED_BIRCH_WOOD = register(new Item("stripped_birch_wood", builder()));
public static final Item STRIPPED_JUNGLE_WOOD = register(new Item("stripped_jungle_wood", builder()));
public static final Item STRIPPED_ACACIA_WOOD = register(new Item("stripped_acacia_wood", builder()));
+ public static final Item STRIPPED_CHERRY_WOOD = register(new Item("stripped_cherry_wood", builder()));
public static final Item STRIPPED_DARK_OAK_WOOD = register(new Item("stripped_dark_oak_wood", builder()));
public static final Item STRIPPED_MANGROVE_WOOD = register(new Item("stripped_mangrove_wood", builder()));
public static final Item STRIPPED_CRIMSON_HYPHAE = register(new Item("stripped_crimson_hyphae", builder()));
@@ -177,6 +183,7 @@ public final class Items {
public static final Item BIRCH_WOOD = register(new Item("birch_wood", builder()));
public static final Item JUNGLE_WOOD = register(new Item("jungle_wood", builder()));
public static final Item ACACIA_WOOD = register(new Item("acacia_wood", builder()));
+ public static final Item CHERRY_WOOD = register(new Item("cherry_wood", builder()));
public static final Item DARK_OAK_WOOD = register(new Item("dark_oak_wood", builder()));
public static final Item MANGROVE_WOOD = register(new Item("mangrove_wood", builder()));
public static final Item CRIMSON_HYPHAE = register(new Item("crimson_hyphae", builder()));
@@ -186,6 +193,7 @@ public final class Items {
public static final Item BIRCH_LEAVES = register(new Item("birch_leaves", builder()));
public static final Item JUNGLE_LEAVES = register(new Item("jungle_leaves", builder()));
public static final Item ACACIA_LEAVES = register(new Item("acacia_leaves", builder()));
+ public static final Item CHERRY_LEAVES = register(new Item("cherry_leaves", builder()));
public static final Item DARK_OAK_LEAVES = register(new Item("dark_oak_leaves", builder()));
public static final Item MANGROVE_LEAVES = register(new Item("mangrove_leaves", builder()));
public static final Item AZALEA_LEAVES = register(new Item("azalea_leaves", builder()));
@@ -235,6 +243,7 @@ public final class Items {
public static final Item CORNFLOWER = register(new Item("cornflower", builder()));
public static final Item LILY_OF_THE_VALLEY = register(new Item("lily_of_the_valley", builder()));
public static final Item WITHER_ROSE = register(new Item("wither_rose", builder()));
+ public static final Item TORCHFLOWER = register(new Item("torchflower", builder()));
public static final Item SPORE_BLOSSOM = register(new Item("spore_blossom", builder()));
public static final Item BROWN_MUSHROOM = register(new Item("brown_mushroom", builder()));
public static final Item RED_MUSHROOM = register(new Item("red_mushroom", builder()));
@@ -248,6 +257,7 @@ public final class Items {
public static final Item SUGAR_CANE = register(new Item("sugar_cane", builder()));
public static final Item KELP = register(new Item("kelp", builder()));
public static final Item MOSS_CARPET = register(new Item("moss_carpet", builder()));
+ public static final Item PINK_PETALS = register(new Item("pink_petals", builder()));
public static final Item MOSS_BLOCK = register(new Item("moss_block", builder()));
public static final Item HANGING_ROOTS = register(new Item("hanging_roots", builder()));
public static final Item BIG_DRIPLEAF = register(new Item("big_dripleaf", builder()));
@@ -258,6 +268,7 @@ public final class Items {
public static final Item BIRCH_SLAB = register(new Item("birch_slab", builder()));
public static final Item JUNGLE_SLAB = register(new Item("jungle_slab", builder()));
public static final Item ACACIA_SLAB = register(new Item("acacia_slab", builder()));
+ public static final Item CHERRY_SLAB = register(new Item("cherry_slab", builder()));
public static final Item DARK_OAK_SLAB = register(new Item("dark_oak_slab", builder()));
public static final Item MANGROVE_SLAB = register(new Item("mangrove_slab", builder()));
public static final Item BAMBOO_SLAB = register(new Item("bamboo_slab", builder()));
@@ -288,6 +299,7 @@ public final class Items {
public static final Item BRICKS = register(new Item("bricks", builder()));
public static final Item BOOKSHELF = register(new Item("bookshelf", builder()));
public static final Item CHISELED_BOOKSHELF = register(new Item("chiseled_bookshelf", builder()));
+ public static final Item DECORATED_POT = register(new Item("decorated_pot", builder().stackSize(1)));
public static final Item MOSSY_COBBLESTONE = register(new Item("mossy_cobblestone", builder()));
public static final Item OBSIDIAN = register(new Item("obsidian", builder()));
public static final Item TORCH = register(new Item("torch", builder()));
@@ -315,6 +327,7 @@ public final class Items {
public static final Item BIRCH_FENCE = register(new Item("birch_fence", builder()));
public static final Item JUNGLE_FENCE = register(new Item("jungle_fence", builder()));
public static final Item ACACIA_FENCE = register(new Item("acacia_fence", builder()));
+ public static final Item CHERRY_FENCE = register(new Item("cherry_fence", builder()));
public static final Item DARK_OAK_FENCE = register(new Item("dark_oak_fence", builder()));
public static final Item MANGROVE_FENCE = register(new Item("mangrove_fence", builder()));
public static final Item BAMBOO_FENCE = register(new Item("bamboo_fence", builder()));
@@ -386,6 +399,7 @@ public final class Items {
public static final Item BIRCH_STAIRS = register(new Item("birch_stairs", builder()));
public static final Item JUNGLE_STAIRS = register(new Item("jungle_stairs", builder()));
public static final Item ACACIA_STAIRS = register(new Item("acacia_stairs", builder()));
+ public static final Item CHERRY_STAIRS = register(new Item("cherry_stairs", builder()));
public static final Item DARK_OAK_STAIRS = register(new Item("dark_oak_stairs", builder()));
public static final Item MANGROVE_STAIRS = register(new Item("mangrove_stairs", builder()));
public static final Item BAMBOO_STAIRS = register(new Item("bamboo_stairs", builder()));
@@ -684,6 +698,7 @@ public final class Items {
public static final Item BIRCH_BUTTON = register(new Item("birch_button", builder()));
public static final Item JUNGLE_BUTTON = register(new Item("jungle_button", builder()));
public static final Item ACACIA_BUTTON = register(new Item("acacia_button", builder()));
+ public static final Item CHERRY_BUTTON = register(new Item("cherry_button", builder()));
public static final Item DARK_OAK_BUTTON = register(new Item("dark_oak_button", builder()));
public static final Item MANGROVE_BUTTON = register(new Item("mangrove_button", builder()));
public static final Item BAMBOO_BUTTON = register(new Item("bamboo_button", builder()));
@@ -698,6 +713,7 @@ public final class Items {
public static final Item BIRCH_PRESSURE_PLATE = register(new Item("birch_pressure_plate", builder()));
public static final Item JUNGLE_PRESSURE_PLATE = register(new Item("jungle_pressure_plate", builder()));
public static final Item ACACIA_PRESSURE_PLATE = register(new Item("acacia_pressure_plate", builder()));
+ public static final Item CHERRY_PRESSURE_PLATE = register(new Item("cherry_pressure_plate", builder()));
public static final Item DARK_OAK_PRESSURE_PLATE = register(new Item("dark_oak_pressure_plate", builder()));
public static final Item MANGROVE_PRESSURE_PLATE = register(new Item("mangrove_pressure_plate", builder()));
public static final Item BAMBOO_PRESSURE_PLATE = register(new Item("bamboo_pressure_plate", builder()));
@@ -709,6 +725,7 @@ public final class Items {
public static final Item BIRCH_DOOR = register(new Item("birch_door", builder()));
public static final Item JUNGLE_DOOR = register(new Item("jungle_door", builder()));
public static final Item ACACIA_DOOR = register(new Item("acacia_door", builder()));
+ public static final Item CHERRY_DOOR = register(new Item("cherry_door", builder()));
public static final Item DARK_OAK_DOOR = register(new Item("dark_oak_door", builder()));
public static final Item MANGROVE_DOOR = register(new Item("mangrove_door", builder()));
public static final Item BAMBOO_DOOR = register(new Item("bamboo_door", builder()));
@@ -720,6 +737,7 @@ public final class Items {
public static final Item BIRCH_TRAPDOOR = register(new Item("birch_trapdoor", builder()));
public static final Item JUNGLE_TRAPDOOR = register(new Item("jungle_trapdoor", builder()));
public static final Item ACACIA_TRAPDOOR = register(new Item("acacia_trapdoor", builder()));
+ public static final Item CHERRY_TRAPDOOR = register(new Item("cherry_trapdoor", builder()));
public static final Item DARK_OAK_TRAPDOOR = register(new Item("dark_oak_trapdoor", builder()));
public static final Item MANGROVE_TRAPDOOR = register(new Item("mangrove_trapdoor", builder()));
public static final Item BAMBOO_TRAPDOOR = register(new Item("bamboo_trapdoor", builder()));
@@ -730,6 +748,7 @@ public final class Items {
public static final Item BIRCH_FENCE_GATE = register(new Item("birch_fence_gate", builder()));
public static final Item JUNGLE_FENCE_GATE = register(new Item("jungle_fence_gate", builder()));
public static final Item ACACIA_FENCE_GATE = register(new Item("acacia_fence_gate", builder()));
+ public static final Item CHERRY_FENCE_GATE = register(new Item("cherry_fence_gate", builder()));
public static final Item DARK_OAK_FENCE_GATE = register(new Item("dark_oak_fence_gate", builder()));
public static final Item MANGROVE_FENCE_GATE = register(new Item("mangrove_fence_gate", builder()));
public static final Item BAMBOO_FENCE_GATE = register(new Item("bamboo_fence_gate", builder()));
@@ -758,6 +777,8 @@ public final class Items {
public static final Item JUNGLE_CHEST_BOAT = register(new Item("jungle_chest_boat", builder().stackSize(1)));
public static final Item ACACIA_BOAT = register(new Item("acacia_boat", builder().stackSize(1)));
public static final Item ACACIA_CHEST_BOAT = register(new Item("acacia_chest_boat", builder().stackSize(1)));
+ public static final Item CHERRY_BOAT = register(new Item("cherry_boat", builder().stackSize(1)));
+ public static final Item CHERRY_CHEST_BOAT = register(new Item("cherry_chest_boat", builder().stackSize(1)));
public static final Item DARK_OAK_BOAT = register(new Item("dark_oak_boat", builder().stackSize(1)));
public static final Item DARK_OAK_CHEST_BOAT = register(new Item("dark_oak_chest_boat", builder().stackSize(1)));
public static final Item MANGROVE_BOAT = register(new Item("mangrove_boat", builder().stackSize(1)));
@@ -861,6 +882,7 @@ public final class Items {
public static final Item BIRCH_SIGN = register(new Item("birch_sign", builder().stackSize(16)));
public static final Item JUNGLE_SIGN = register(new Item("jungle_sign", builder().stackSize(16)));
public static final Item ACACIA_SIGN = register(new Item("acacia_sign", builder().stackSize(16)));
+ public static final Item CHERRY_SIGN = register(new Item("cherry_sign", builder().stackSize(16)));
public static final Item DARK_OAK_SIGN = register(new Item("dark_oak_sign", builder().stackSize(16)));
public static final Item MANGROVE_SIGN = register(new Item("mangrove_sign", builder().stackSize(16)));
public static final Item BAMBOO_SIGN = register(new Item("bamboo_sign", builder().stackSize(16)));
@@ -871,6 +893,7 @@ public final class Items {
public static final Item BIRCH_HANGING_SIGN = register(new Item("birch_hanging_sign", builder().stackSize(16)));
public static final Item JUNGLE_HANGING_SIGN = register(new Item("jungle_hanging_sign", builder().stackSize(16)));
public static final Item ACACIA_HANGING_SIGN = register(new Item("acacia_hanging_sign", builder().stackSize(16)));
+ public static final Item CHERRY_HANGING_SIGN = register(new Item("cherry_hanging_sign", builder().stackSize(16)));
public static final Item DARK_OAK_HANGING_SIGN = register(new Item("dark_oak_hanging_sign", builder().stackSize(16)));
public static final Item MANGROVE_HANGING_SIGN = register(new Item("mangrove_hanging_sign", builder().stackSize(16)));
public static final Item BAMBOO_HANGING_SIGN = register(new Item("bamboo_hanging_sign", builder().stackSize(16)));
@@ -1028,6 +1051,7 @@ public final class Items {
public static final SpawnEggItem SKELETON_SPAWN_EGG = register(new SpawnEggItem("skeleton_spawn_egg", builder()));
public static final SpawnEggItem SKELETON_HORSE_SPAWN_EGG = register(new SpawnEggItem("skeleton_horse_spawn_egg", builder()));
public static final SpawnEggItem SLIME_SPAWN_EGG = register(new SpawnEggItem("slime_spawn_egg", builder()));
+ public static final SpawnEggItem SNIFFER_SPAWN_EGG = register(new SpawnEggItem("sniffer_spawn_egg", builder()));
public static final SpawnEggItem SNOW_GOLEM_SPAWN_EGG = register(new SpawnEggItem("snow_golem_spawn_egg", builder()));
public static final SpawnEggItem SPIDER_SPAWN_EGG = register(new SpawnEggItem("spider_spawn_egg", builder()));
public static final SpawnEggItem SQUID_SPAWN_EGG = register(new SpawnEggItem("squid_spawn_egg", builder()));
@@ -1113,6 +1137,7 @@ public final class Items {
public static final Item END_CRYSTAL = register(new Item("end_crystal", builder()));
public static final Item CHORUS_FRUIT = register(new Item("chorus_fruit", builder()));
public static final Item POPPED_CHORUS_FRUIT = register(new Item("popped_chorus_fruit", builder()));
+ public static final Item TORCHFLOWER_SEEDS = register(new Item("torchflower_seeds", builder()));
public static final Item BEETROOT = register(new Item("beetroot", builder()));
public static final Item BEETROOT_SEEDS = register(new Item("beetroot_seeds", builder()));
public static final Item BEETROOT_SOUP = register(new Item("beetroot_soup", builder().stackSize(1)));
@@ -1221,6 +1246,23 @@ public final class Items {
public static final Item PEARLESCENT_FROGLIGHT = register(new Item("pearlescent_froglight", builder()));
public static final Item FROGSPAWN = register(new Item("frogspawn", builder()));
public static final Item ECHO_SHARD = register(new Item("echo_shard", builder()));
+ public static final Item BRUSH = register(new Item("brush", builder().stackSize(1).maxDamage(64)));
+ public static final Item NETHERITE_UPGRADE_SMITHING_TEMPLATE = register(new Item("netherite_upgrade_smithing_template", builder()));
+ public static final Item SENTRY_ARMOR_TRIM_SMITHING_TEMPLATE = register(new Item("sentry_armor_trim_smithing_template", builder()));
+ public static final Item DUNE_ARMOR_TRIM_SMITHING_TEMPLATE = register(new Item("dune_armor_trim_smithing_template", builder()));
+ public static final Item COAST_ARMOR_TRIM_SMITHING_TEMPLATE = register(new Item("coast_armor_trim_smithing_template", builder()));
+ public static final Item WILD_ARMOR_TRIM_SMITHING_TEMPLATE = register(new Item("wild_armor_trim_smithing_template", builder()));
+ public static final Item WARD_ARMOR_TRIM_SMITHING_TEMPLATE = register(new Item("ward_armor_trim_smithing_template", builder()));
+ public static final Item EYE_ARMOR_TRIM_SMITHING_TEMPLATE = register(new Item("eye_armor_trim_smithing_template", builder()));
+ public static final Item VEX_ARMOR_TRIM_SMITHING_TEMPLATE = register(new Item("vex_armor_trim_smithing_template", builder()));
+ public static final Item TIDE_ARMOR_TRIM_SMITHING_TEMPLATE = register(new Item("tide_armor_trim_smithing_template", builder()));
+ public static final Item SNOUT_ARMOR_TRIM_SMITHING_TEMPLATE = register(new Item("snout_armor_trim_smithing_template", builder()));
+ public static final Item RIB_ARMOR_TRIM_SMITHING_TEMPLATE = register(new Item("rib_armor_trim_smithing_template", builder()));
+ public static final Item SPIRE_ARMOR_TRIM_SMITHING_TEMPLATE = register(new Item("spire_armor_trim_smithing_template", builder()));
+ public static final Item POTTERY_SHARD_ARCHER = register(new Item("pottery_shard_archer", builder()));
+ public static final Item POTTERY_SHARD_PRIZE = register(new Item("pottery_shard_prize", builder()));
+ public static final Item POTTERY_SHARD_ARMS_UP = register(new Item("pottery_shard_arms_up", builder()));
+ public static final Item POTTERY_SHARD_SKULL = register(new Item("pottery_shard_skull", builder()));
private static T register(T item) {
return register(item, Registries.JAVA_ITEMS.get().size());
diff --git a/core/src/main/java/org/geysermc/geyser/item/components/ToolTier.java b/core/src/main/java/org/geysermc/geyser/item/components/ToolTier.java
index 674e8c3f4..ee144acc6 100644
--- a/core/src/main/java/org/geysermc/geyser/item/components/ToolTier.java
+++ b/core/src/main/java/org/geysermc/geyser/item/components/ToolTier.java
@@ -43,7 +43,7 @@ public enum ToolTier {
DIAMOND(8, () -> Collections.singleton(Items.DIAMOND)),
NETHERITE(9, () -> Collections.singleton(Items.NETHERITE_INGOT));
- public static final ToolTier[] VALUES = values();
+ private static final ToolTier[] VALUES = values();
private final int speed;
private final Supplier> repairIngredients;
diff --git a/core/src/main/java/org/geysermc/geyser/item/type/PotionItem.java b/core/src/main/java/org/geysermc/geyser/item/type/PotionItem.java
index b2251ff0f..338473fc5 100644
--- a/core/src/main/java/org/geysermc/geyser/item/type/PotionItem.java
+++ b/core/src/main/java/org/geysermc/geyser/item/type/PotionItem.java
@@ -28,11 +28,13 @@ package org.geysermc.geyser.item.type;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
+import org.cloudburstmc.protocol.bedrock.data.defintions.ItemDefinition;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.inventory.item.Potion;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.registry.type.ItemMappings;
+import org.geysermc.geyser.translator.inventory.item.CustomItemTranslator;
import org.geysermc.geyser.translator.inventory.item.ItemTranslator;
public class PotionItem extends Item {
@@ -45,15 +47,23 @@ public class PotionItem extends Item {
if (itemStack.getNbt() == null) return super.translateToBedrock(itemStack, mapping, mappings);
Tag potionTag = itemStack.getNbt().get("Potion");
if (potionTag instanceof StringTag) {
- Potion potion = Potion.getByJavaIdentifier(((StringTag) potionTag).getValue());
- if (potion != null) {
+ ItemDefinition customItemDefinition = CustomItemTranslator.getCustomItem(itemStack.getNbt(), mapping);
+ if (customItemDefinition == null) {
+ Potion potion = Potion.getByJavaIdentifier(((StringTag) potionTag).getValue());
+ if (potion != null) {
+ return ItemData.builder()
+ .definition(mapping.getBedrockDefinition())
+ .damage(potion.getBedrockId())
+ .count(itemStack.getAmount())
+ .tag(ItemTranslator.translateNbtToBedrock(itemStack.getNbt()));
+ }
+ GeyserImpl.getInstance().getLogger().debug("Unknown Java potion: " + potionTag.getValue());
+ } else {
return ItemData.builder()
- .definition(mapping.getBedrockDefinition())
- .damage(potion.getBedrockId())
+ .definition(customItemDefinition)
.count(itemStack.getAmount())
.tag(ItemTranslator.translateNbtToBedrock(itemStack.getNbt()));
}
- GeyserImpl.getInstance().getLogger().debug("Unknown Java potion: " + potionTag.getValue());
}
return super.translateToBedrock(itemStack, mapping, mappings);
}
diff --git a/core/src/main/java/org/geysermc/geyser/level/GeyserWorldManager.java b/core/src/main/java/org/geysermc/geyser/level/GeyserWorldManager.java
index 210269876..8d4b3f2e6 100644
--- a/core/src/main/java/org/geysermc/geyser/level/GeyserWorldManager.java
+++ b/core/src/main/java/org/geysermc/geyser/level/GeyserWorldManager.java
@@ -25,25 +25,63 @@
package org.geysermc.geyser.level;
+import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityInfo;
+import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
+import it.unimi.dsi.fastutil.objects.ObjectArrayList;
+import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
-import org.geysermc.geyser.level.block.BlockStateValues;
+import org.geysermc.erosion.packet.backendbound.*;
+import org.geysermc.erosion.util.BlockPositionIterator;
+import org.geysermc.erosion.util.LecternUtils;
import org.geysermc.geyser.session.GeyserSession;
-import org.geysermc.geyser.session.cache.ChunkCache;
-import org.geysermc.geyser.translator.inventory.LecternInventoryTranslator;
+import org.geysermc.geyser.util.BlockEntityUtils;
+import org.jetbrains.annotations.Nullable;
+
+import javax.annotation.Nonnull;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
public class GeyserWorldManager extends WorldManager {
private final Object2ObjectMap gameruleCache = new Object2ObjectOpenHashMap<>();
@Override
public int getBlockAt(GeyserSession session, int x, int y, int z) {
- ChunkCache chunkCache = session.getChunkCache();
- if (chunkCache != null) { // Chunk cache can be null if the session is closed asynchronously
- return chunkCache.getBlockAt(x, y, z);
+ var erosionHandler = session.getErosionHandler().getAsActive();
+ if (erosionHandler == null) {
+ return session.getChunkCache().getBlockAt(x, y, z);
}
- return BlockStateValues.JAVA_AIR_ID;
+ CompletableFuture future = new CompletableFuture<>(); // Boxes
+ erosionHandler.setPendingLookup(future);
+ erosionHandler.sendPacket(new BackendboundBlockRequestPacket(0, Vector3i.from(x, y, z)));
+ return future.join();
+ }
+
+ @Override
+ public CompletableFuture getBlockAtAsync(GeyserSession session, int x, int y, int z) {
+ var erosionHandler = session.getErosionHandler().getAsActive();
+ if (erosionHandler == null) {
+ return super.getBlockAtAsync(session, x, y, z);
+ }
+ CompletableFuture future = new CompletableFuture<>(); // Boxes
+ int transactionId = erosionHandler.getNextTransactionId();
+ erosionHandler.getAsyncPendingLookups().put(transactionId, future);
+ erosionHandler.sendPacket(new BackendboundBlockRequestPacket(transactionId, Vector3i.from(x, y, z)));
+ return future;
+ }
+
+ @Override
+ public int[] getBlocksAt(GeyserSession session, BlockPositionIterator iter) {
+ var erosionHandler = session.getErosionHandler().getAsActive();
+ if (erosionHandler == null) {
+ return super.getBlocksAt(session, iter);
+ }
+ CompletableFuture future = new CompletableFuture<>();
+ erosionHandler.setPendingBatchLookup(future);
+ erosionHandler.sendPacket(new BackendboundBatchBlockRequestPacket(iter));
+ return future.join();
}
@Override
@@ -53,10 +91,31 @@ public class GeyserWorldManager extends WorldManager {
}
@Override
- public NbtMap getLecternDataAt(GeyserSession session, int x, int y, int z, boolean isChunkLoad) {
+ public void sendLecternData(GeyserSession session, int x, int z, List blockEntityInfos) {
+ var erosionHandler = session.getErosionHandler().getAsActive();
+ if (erosionHandler == null) {
+ // No-op - don't send any additional information other than what the chunk has already sent
+ return;
+ }
+ List vectors = new ObjectArrayList<>(blockEntityInfos.size());
+ for (int i = 0; i < blockEntityInfos.size(); i++) {
+ BlockEntityInfo info = blockEntityInfos.get(i);
+ vectors.add(Vector3i.from(info.getX(), info.getY(), info.getZ()));
+ }
+ erosionHandler.sendPacket(new BackendboundBatchBlockEntityPacket(x, z, vectors));
+ }
+
+ @Override
+ public void sendLecternData(GeyserSession session, int x, int y, int z) {
+ var erosionHandler = session.getErosionHandler().getAsActive();
+ if (erosionHandler != null) {
+ erosionHandler.sendPacket(new BackendboundBlockEntityPacket(Vector3i.from(x, y, z)));
+ return;
+ }
+
// Without direct server access, we can't get lectern information on-the-fly.
// I should have set this up so it's only called when there is a book in the block state. - Camotoy
- NbtMapBuilder lecternTag = LecternInventoryTranslator.getBaseLecternTag(x, y, z, 1);
+ NbtMapBuilder lecternTag = LecternUtils.getBaseLecternTag(x, y, z, 1);
lecternTag.putCompound("book", NbtMap.builder()
.putByte("Count", (byte) 1)
.putShort("Damage", (short) 0)
@@ -67,12 +126,12 @@ public class GeyserWorldManager extends WorldManager {
.build())
.build());
lecternTag.putInt("page", -1); // I'm surprisingly glad this exists - it forces Bedrock to stop reading immediately. Usually.
- return lecternTag.build();
+ BlockEntityUtils.updateBlockEntity(session, lecternTag.build(), Vector3i.from(x, y, z));
}
@Override
- public boolean shouldExpectLecternHandled() {
- return false;
+ public boolean shouldExpectLecternHandled(GeyserSession session) {
+ return session.getErosionHandler().isActive();
}
@Override
@@ -105,4 +164,17 @@ public class GeyserWorldManager extends WorldManager {
public boolean hasPermission(GeyserSession session, String permission) {
return false;
}
+
+ @Nonnull
+ @Override
+ public CompletableFuture<@Nullable CompoundTag> getPickItemNbt(GeyserSession session, int x, int y, int z, boolean addNbtData) {
+ var erosionHandler = session.getErosionHandler().getAsActive();
+ if (erosionHandler == null) {
+ return super.getPickItemNbt(session, x, y, z, addNbtData);
+ }
+ CompletableFuture future = new CompletableFuture<>();
+ erosionHandler.setPickBlockLookup(future);
+ erosionHandler.sendPacket(new BackendboundPickBlockPacket(Vector3i.from(x, y, z)));
+ return future;
+ }
}
diff --git a/core/src/main/java/org/geysermc/geyser/level/WorldManager.java b/core/src/main/java/org/geysermc/geyser/level/WorldManager.java
index c408d1018..006caff55 100644
--- a/core/src/main/java/org/geysermc/geyser/level/WorldManager.java
+++ b/core/src/main/java/org/geysermc/geyser/level/WorldManager.java
@@ -26,14 +26,16 @@
package org.geysermc.geyser.level;
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
+import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityInfo;
import com.github.steveice10.mc.protocol.data.game.setting.Difficulty;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import org.cloudburstmc.math.vector.Vector3i;
-import org.cloudburstmc.nbt.NbtMap;
+import org.geysermc.erosion.util.BlockPositionIterator;
import org.geysermc.geyser.session.GeyserSession;
import org.jetbrains.annotations.Nullable;
import javax.annotation.Nonnull;
+import java.util.List;
import java.util.Locale;
import java.util.concurrent.CompletableFuture;
@@ -68,6 +70,23 @@ public abstract class WorldManager {
*/
public abstract int getBlockAt(GeyserSession session, int x, int y, int z);
+ public final CompletableFuture getBlockAtAsync(GeyserSession session, Vector3i vector) {
+ return this.getBlockAtAsync(session, vector.getX(), vector.getY(), vector.getZ());
+ }
+
+ public CompletableFuture getBlockAtAsync(GeyserSession session, int x, int y, int z) {
+ return CompletableFuture.completedFuture(this.getBlockAt(session, x, y, z));
+ }
+
+ public int[] getBlocksAt(GeyserSession session, BlockPositionIterator iter) {
+ int[] blocks = new int[iter.getMaxIterations()];
+ for (; iter.hasNext(); iter.next()) {
+ int networkId = this.getBlockAt(session, iter.getX(), iter.getY(), iter.getZ());
+ blocks[iter.getIteration()] = networkId;
+ }
+ return blocks;
+ }
+
/**
* Checks whether or not this world manager requires a separate chunk cache/has access to more block data than the chunk cache.
*
@@ -89,20 +108,28 @@ public abstract class WorldManager {
* We solve this problem by querying all loaded lecterns, where possible, and sending their information in a block entity
* tag.
*
+ * Note that the lectern data may be sent asynchronously.
+ *
* @param session the session of the player
* @param x the x coordinate of the lectern
* @param y the y coordinate of the lectern
* @param z the z coordinate of the lectern
- * @param isChunkLoad if this is called during a chunk load or not. Changes behavior in certain instances.
- * @return the Bedrock lectern block entity tag. This may not be the exact block entity tag - for example, Spigot's
- * block handled must be done on the server thread, so we send the tag manually there.
*/
- public abstract NbtMap getLecternDataAt(GeyserSession session, int x, int y, int z, boolean isChunkLoad);
+ public abstract void sendLecternData(GeyserSession session, int x, int y, int z);
+
+ /**
+ * {@link #sendLecternData(GeyserSession, int, int, int)} but batched for chunks.
+ *
+ * @param x chunk x
+ * @param z chunk z
+ * @param blockEntityInfos a list of coordinates (chunk local) to grab lecterns from.
+ */
+ public abstract void sendLecternData(GeyserSession session, int x, int z, List blockEntityInfos);
/**
* @return whether we should expect lectern data to update, or if we have to fall back on a workaround.
*/
- public abstract boolean shouldExpectLecternHandled();
+ public abstract boolean shouldExpectLecternHandled(GeyserSession session);
/**
* Updates a gamerule value on the Java server
diff --git a/core/src/main/java/org/geysermc/geyser/level/block/BlockPositionIterator.java b/core/src/main/java/org/geysermc/geyser/level/block/BlockPositionIterator.java
deleted file mode 100644
index 7b94f751b..000000000
--- a/core/src/main/java/org/geysermc/geyser/level/block/BlockPositionIterator.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- * @author GeyserMC
- * @link https://github.com/GeyserMC/Geyser
- */
-
-package org.geysermc.geyser.level.block;
-
-import org.cloudburstmc.protocol.common.util.Preconditions;
-
-public class BlockPositionIterator {
- private final int minX;
- private final int minY;
- private final int minZ;
-
- private final int sizeX;
- private final int sizeZ;
-
- private int i = 0;
- private final int maxI;
-
- public BlockPositionIterator(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) {
- Preconditions.checkArgument(maxX >= minX, "maxX is not greater than or equal to minX");
- Preconditions.checkArgument(maxY >= minY, "maxY is not greater than or equal to minY");
- Preconditions.checkArgument(maxZ >= minZ, "maxZ is not greater than or equal to minZ");
-
- this.minX = minX;
- this.minY = minY;
- this.minZ = minZ;
-
- this.sizeX = maxX - minX + 1;
- int sizeY = maxY - minY + 1;
- this.sizeZ = maxZ - minZ + 1;
- this.maxI = sizeX * sizeY * sizeZ;
- }
-
- public boolean hasNext() {
- return i < maxI;
- }
-
- public void next() {
- // Iterate in zxy order
- i++;
- }
-
- public void reset() {
- i = 0;
- }
-
- public int getX() {
- return ((i / sizeZ) % sizeX) + minX;
- }
-
- public int getY() {
- return (i / sizeZ / sizeX) + minY;
- }
-
- public int getZ() {
- return (i % sizeZ) + minZ;
- }
-}
\ No newline at end of file
diff --git a/core/src/main/java/org/geysermc/geyser/level/block/BlockStateValues.java b/core/src/main/java/org/geysermc/geyser/level/block/BlockStateValues.java
index 58cbce77f..c6fc60303 100644
--- a/core/src/main/java/org/geysermc/geyser/level/block/BlockStateValues.java
+++ b/core/src/main/java/org/geysermc/geyser/level/block/BlockStateValues.java
@@ -68,7 +68,6 @@ public final class BlockStateValues {
public static final int JAVA_AIR_ID = 0;
- public static int JAVA_BELL_ID;
public static int JAVA_COBWEB_ID;
public static int JAVA_FURNACE_ID;
public static int JAVA_FURNACE_LIT_ID;
diff --git a/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java b/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java
index 6410bb6c9..b983da8b4 100644
--- a/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java
+++ b/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java
@@ -33,10 +33,10 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket;
import lombok.Getter;
import lombok.Setter;
+import org.geysermc.erosion.util.BlockPositionIterator;
import org.geysermc.geyser.entity.EntityDefinitions;
import org.geysermc.geyser.entity.type.Entity;
import org.geysermc.geyser.entity.type.player.PlayerEntity;
-import org.geysermc.geyser.level.block.BlockPositionIterator;
import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.PistonCache;
@@ -215,7 +215,7 @@ public class CollisionManager {
int minCollisionZ = (int) Math.floor(position.getZ() - ((box.getSizeZ() / 2) + COLLISION_TOLERANCE + pistonExpand));
int maxCollisionZ = (int) Math.floor(position.getZ() + (box.getSizeZ() / 2) + COLLISION_TOLERANCE + pistonExpand);
- return new BlockPositionIterator(minCollisionX, minCollisionY, minCollisionZ, maxCollisionX, maxCollisionY, maxCollisionZ);
+ return BlockPositionIterator.fromMinMax(minCollisionX, minCollisionY, minCollisionZ, maxCollisionX, maxCollisionY, maxCollisionZ);
}
public BlockPositionIterator playerCollidableBlocksIterator() {
@@ -235,8 +235,9 @@ public class CollisionManager {
// Used when correction code needs to be run before the main correction
BlockPositionIterator iter = session.getCollisionManager().playerCollidableBlocksIterator();
- for (; iter.hasNext(); iter.next()) {
- BlockCollision blockCollision = BlockUtils.getCollisionAt(session, iter.getX(), iter.getY(), iter.getZ());
+ int[] blocks = session.getGeyser().getWorldManager().getBlocksAt(session, iter);
+ for (iter.reset(); iter.hasNext(); iter.next()) {
+ BlockCollision blockCollision = BlockUtils.getCollision(blocks[iter.getIteration()]);
if (blockCollision != null) {
blockCollision.beforeCorrectPosition(iter.getX(), iter.getY(), iter.getZ(), playerBoundingBox);
}
@@ -244,7 +245,7 @@ public class CollisionManager {
// Main correction code
for (iter.reset(); iter.hasNext(); iter.next()) {
- BlockCollision blockCollision = BlockUtils.getCollisionAt(session, iter.getX(), iter.getY(), iter.getZ());
+ BlockCollision blockCollision = BlockUtils.getCollision(blocks[iter.getIteration()]);
if (blockCollision != null) {
if (!blockCollision.correctPosition(session, iter.getX(), iter.getY(), iter.getZ(), playerBoundingBox)) {
return false;
diff --git a/core/src/main/java/org/geysermc/geyser/level/physics/Direction.java b/core/src/main/java/org/geysermc/geyser/level/physics/Direction.java
index a05a2f452..fa5201db9 100644
--- a/core/src/main/java/org/geysermc/geyser/level/physics/Direction.java
+++ b/core/src/main/java/org/geysermc/geyser/level/physics/Direction.java
@@ -25,19 +25,18 @@
package org.geysermc.geyser.level.physics;
-import com.github.steveice10.mc.protocol.data.game.level.block.value.PistonValue;
import org.cloudburstmc.math.vector.Vector3i;
import lombok.Getter;
import javax.annotation.Nonnull;
public enum Direction {
- DOWN(1, Vector3i.from(0, -1, 0), Axis.Y, PistonValue.DOWN),
- UP(0, Vector3i.UNIT_Y, Axis.Y, PistonValue.UP),
- NORTH(3, Vector3i.from(0, 0, -1), Axis.Z, PistonValue.NORTH),
- SOUTH(2, Vector3i.UNIT_Z, Axis.Z, PistonValue.SOUTH),
- WEST(5, Vector3i.from(-1, 0, 0), Axis.X, PistonValue.WEST),
- EAST(4, Vector3i.UNIT_X, Axis.X, PistonValue.EAST);
+ DOWN(1, Vector3i.from(0, -1, 0), Axis.Y, com.github.steveice10.mc.protocol.data.game.entity.object.Direction.DOWN),
+ UP(0, Vector3i.UNIT_Y, Axis.Y, com.github.steveice10.mc.protocol.data.game.entity.object.Direction.UP),
+ NORTH(3, Vector3i.from(0, 0, -1), Axis.Z, com.github.steveice10.mc.protocol.data.game.entity.object.Direction.NORTH),
+ SOUTH(2, Vector3i.UNIT_Z, Axis.Z, com.github.steveice10.mc.protocol.data.game.entity.object.Direction.SOUTH),
+ WEST(5, Vector3i.from(-1, 0, 0), Axis.X, com.github.steveice10.mc.protocol.data.game.entity.object.Direction.WEST),
+ EAST(4, Vector3i.UNIT_X, Axis.X, com.github.steveice10.mc.protocol.data.game.entity.object.Direction.EAST);
public static final Direction[] VALUES = values();
@@ -46,10 +45,9 @@ public enum Direction {
private final Vector3i unitVector;
@Getter
private final Axis axis;
- @Getter
- private final PistonValue pistonValue;
+ private final com.github.steveice10.mc.protocol.data.game.entity.object.Direction pistonValue;
- Direction(int reversedId, Vector3i unitVector, Axis axis, PistonValue pistonValue) {
+ Direction(int reversedId, Vector3i unitVector, Axis axis, com.github.steveice10.mc.protocol.data.game.entity.object.Direction pistonValue) {
this.reversedId = reversedId;
this.unitVector = unitVector;
this.axis = axis;
@@ -69,7 +67,7 @@ public enum Direction {
}
@Nonnull
- public static Direction fromPistonValue(PistonValue pistonValue) {
+ public static Direction fromPistonValue(com.github.steveice10.mc.protocol.data.game.entity.object.Direction pistonValue) {
for (Direction direction : VALUES) {
if (direction.pistonValue == pistonValue) {
return direction;
diff --git a/core/src/main/java/org/geysermc/geyser/level/physics/PistonBehavior.java b/core/src/main/java/org/geysermc/geyser/level/physics/PistonBehavior.java
index bf943134b..a6b25d01e 100644
--- a/core/src/main/java/org/geysermc/geyser/level/physics/PistonBehavior.java
+++ b/core/src/main/java/org/geysermc/geyser/level/physics/PistonBehavior.java
@@ -33,7 +33,7 @@ public enum PistonBehavior {
DESTROY,
PUSH_ONLY;
- public static final PistonBehavior[] VALUES = values();
+ private static final PistonBehavior[] VALUES = values();
public static PistonBehavior getByName(String name) {
String upperCase = name.toUpperCase(Locale.ROOT);
diff --git a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java
index 87890d3e8..870328f27 100644
--- a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java
+++ b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java
@@ -28,12 +28,12 @@ package org.geysermc.geyser.network;
import com.github.steveice10.mc.protocol.codec.MinecraftCodec;
import com.github.steveice10.mc.protocol.codec.PacketCodec;
import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
-import org.cloudburstmc.protocol.bedrock.codec.v544.Bedrock_v544;
-import org.cloudburstmc.protocol.bedrock.codec.v545.Bedrock_v545;
import org.cloudburstmc.protocol.bedrock.codec.v554.Bedrock_v554;
import org.cloudburstmc.protocol.bedrock.codec.v557.Bedrock_v557;
import org.cloudburstmc.protocol.bedrock.codec.v560.Bedrock_v560;
import org.cloudburstmc.protocol.bedrock.codec.v567.Bedrock_v567;
+import org.cloudburstmc.protocol.bedrock.codec.v568.Bedrock_v568;
+import org.cloudburstmc.protocol.bedrock.codec.v575.Bedrock_v575;
import org.cloudburstmc.protocol.bedrock.netty.codec.packet.BedrockPacketCodec;
import org.geysermc.geyser.session.GeyserSession;
@@ -49,7 +49,9 @@ public final class GameProtocol {
* Default Bedrock codec that should act as a fallback. Should represent the latest available
* release of the game that Geyser supports.
*/
- public static final BedrockCodec DEFAULT_BEDROCK_CODEC = Bedrock_v560.CODEC;
+ public static final BedrockCodec DEFAULT_BEDROCK_CODEC = Bedrock_v575.CODEC.toBuilder()
+ .minecraftVersion("1.19.73")
+ .build();
/**
* A list of all supported Bedrock versions that can join Geyser
*/
@@ -62,20 +64,20 @@ public final class GameProtocol {
private static final PacketCodec DEFAULT_JAVA_CODEC = MinecraftCodec.CODEC;
static {
- SUPPORTED_BEDROCK_CODECS.add(Bedrock_v544.CODEC);
- SUPPORTED_BEDROCK_CODECS.add(Bedrock_v545.CODEC.toBuilder()
- .minecraftVersion("1.19.21/1.19.22")
- .build());
SUPPORTED_BEDROCK_CODECS.add(Bedrock_v554.CODEC.toBuilder()
.minecraftVersion("1.19.30/1.19.31")
.build());
SUPPORTED_BEDROCK_CODECS.add(Bedrock_v557.CODEC.toBuilder()
.minecraftVersion("1.19.40/1.19.41")
.build());
- SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC.toBuilder()
+ SUPPORTED_BEDROCK_CODECS.add(Bedrock_v560.CODEC.toBuilder()
.minecraftVersion("1.19.50/1.19.51")
.build());
SUPPORTED_BEDROCK_CODECS.add(Bedrock_v567.CODEC);
+ SUPPORTED_BEDROCK_CODECS.add(Bedrock_v568.CODEC);
+ SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC.toBuilder()
+ .minecraftVersion("1.19.70/1.19.71/1.19.73")
+ .build());
}
/**
@@ -94,10 +96,6 @@ public final class GameProtocol {
/* Bedrock convenience methods to gatekeep features and easily remove the check on version removal */
- public static boolean supports1_19_30(GeyserSession session) {
- return session.getUpstream().getProtocolVersion() >= Bedrock_v554.CODEC.getProtocolVersion();
- }
-
public static boolean supports1_19_50(GeyserSession session) {
return session.getUpstream().getProtocolVersion() >= Bedrock_v560.CODEC.getProtocolVersion();
}
diff --git a/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java b/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java
index 0a81f430d..f373cd25d 100644
--- a/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java
+++ b/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java
@@ -28,6 +28,8 @@ package org.geysermc.geyser.network;
import io.netty.buffer.Unpooled;
import org.cloudburstmc.protocol.bedrock.BedrockDisconnectReasons;
import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
+import org.cloudburstmc.protocol.bedrock.codec.v567.Bedrock_v567;
+import org.cloudburstmc.protocol.bedrock.codec.v568.Bedrock_v568;
import org.cloudburstmc.protocol.bedrock.data.ExperimentData;
import org.cloudburstmc.protocol.bedrock.data.PacketCompressionAlgorithm;
import org.cloudburstmc.protocol.bedrock.data.ResourcePackType;
@@ -46,6 +48,7 @@ import org.cloudburstmc.protocol.bedrock.packet.ResourcePackStackPacket;
import org.cloudburstmc.protocol.bedrock.packet.ResourcePacksInfoPacket;
import org.cloudburstmc.protocol.bedrock.packet.SetTitlePacket;
import org.cloudburstmc.protocol.common.PacketSignal;
+import org.geysermc.geyser.Constants;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.network.AuthType;
import org.geysermc.geyser.configuration.GeyserConfiguration;
@@ -58,11 +61,13 @@ import org.geysermc.geyser.session.PendingMicrosoftAuthentication;
import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.util.LoginEncryptionUtils;
import org.geysermc.geyser.util.MathUtils;
+import org.geysermc.geyser.util.VersionCheckUtils;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayDeque;
import java.util.Deque;
+import java.util.OptionalInt;
public class UpstreamPacketHandler extends LoggingPacketHandler {
@@ -90,7 +95,14 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
String supportedVersions = GameProtocol.getAllSupportedBedrockVersions();
if (protocolVersion > GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) {
// Too early to determine session locale
- session.disconnect(GeyserLocale.getLocaleStringLog("geyser.network.outdated.server", supportedVersions));
+ String disconnectMessage = GeyserLocale.getLocaleStringLog("geyser.network.outdated.server", supportedVersions);
+ // If the latest release matches this version, then let the user know.
+ OptionalInt latestRelease = VersionCheckUtils.getLatestBedrockRelease();
+ if (latestRelease.isPresent() && latestRelease.getAsInt() == protocolVersion) {
+ // Random note: don't make the disconnect message too long or Bedrock will cut it off on smaller screens
+ disconnectMessage += "\n" + GeyserLocale.getLocaleStringLog("geyser.version.new.on_disconnect", Constants.GEYSER_DOWNLOAD_LOCATION);
+ }
+ session.disconnect(disconnectMessage);
return false;
} else if (protocolVersion < GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) {
session.disconnect(GeyserLocale.getLocaleStringLog("geyser.network.outdated.client", supportedVersions));
@@ -161,6 +173,11 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
return PacketSignal.HANDLED;
}
+ // Hack for... whatever this is
+ if (loginPacket.getProtocolVersion() == Bedrock_v567.CODEC.getProtocolVersion() && !session.getClientData().getGameVersion().equals("1.19.60")) {
+ session.getUpstream().getSession().setCodec(Bedrock_v568.CODEC);
+ }
+
PlayStatusPacket playStatus = new PlayStatusPacket();
playStatus.setStatus(PlayStatusPacket.Status.LOGIN_SUCCESS);
session.sendUpstreamPacket(playStatus);
diff --git a/core/src/main/java/org/geysermc/geyser/pack/ResourcePack.java b/core/src/main/java/org/geysermc/geyser/pack/ResourcePack.java
index 6df1a0c0e..07d56194a 100644
--- a/core/src/main/java/org/geysermc/geyser/pack/ResourcePack.java
+++ b/core/src/main/java/org/geysermc/geyser/pack/ResourcePack.java
@@ -102,13 +102,17 @@ public class ResourcePack {
pack.sha256 = FileUtils.calculateSHA256(file);
- Stream extends ZipEntry> stream = null;
- try {
- ZipFile zip = new ZipFile(file);
-
- stream = zip.stream();
+ try (ZipFile zip = new ZipFile(file);
+ Stream extends ZipEntry> stream = zip.stream()) {
stream.forEach((x) -> {
- if (x.getName().contains("manifest.json")) {
+ String name = x.getName();
+ if (name.length() >= 80) {
+ GeyserImpl.getInstance().getLogger().warning("The resource pack " + file.getName()
+ + " has a file in it that meets or exceeds 80 characters in its path (" + name
+ + ", " + name.length() + " characters long). This will cause problems on some Bedrock platforms." +
+ " Please rename it to be shorter, or reduce the amount of folders needed to get to the file.");
+ }
+ if (name.contains("manifest.json")) {
try {
ResourcePackManifest manifest = FileUtils.loadJson(zip.getInputStream(x), ResourcePackManifest.class);
// Sometimes a pack_manifest file is present and not in a valid format,
@@ -133,10 +137,6 @@ public class ResourcePack {
} catch (Exception e) {
GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.resource_pack.broken", file.getName()));
e.printStackTrace();
- } finally {
- if (stream != null) {
- stream.close();
- }
}
}
}
diff --git a/core/src/main/java/org/geysermc/geyser/ping/GeyserLegacyPingPassthrough.java b/core/src/main/java/org/geysermc/geyser/ping/GeyserLegacyPingPassthrough.java
index af030eb00..e3c72c174 100644
--- a/core/src/main/java/org/geysermc/geyser/ping/GeyserLegacyPingPassthrough.java
+++ b/core/src/main/java/org/geysermc/geyser/ping/GeyserLegacyPingPassthrough.java
@@ -79,7 +79,8 @@ public class GeyserLegacyPingPassthrough implements IGeyserPingPassthrough, Runn
try (Socket socket = new Socket()) {
String address = geyser.getConfig().getRemote().address();
int port = geyser.getConfig().getRemote().port();
- socket.connect(new InetSocketAddress(address, port), 5000);
+ InetSocketAddress endpoint = new InetSocketAddress(address, port);
+ socket.connect(endpoint, 5000);
ByteArrayOutputStream byteArrayStream = new ByteArrayOutputStream();
try (DataOutputStream handshake = new DataOutputStream(byteArrayStream)) {
@@ -103,7 +104,8 @@ public class GeyserLegacyPingPassthrough implements IGeyserPingPassthrough, Runn
HAProxyProxiedProtocol.TCP4.byteValue() : HAProxyProxiedProtocol.TCP6.byteValue());
byte[] srcAddrBytes = NetUtil.createByteArrayFromIpAddressString(
((InetSocketAddress) socket.getLocalSocketAddress()).getAddress().getHostAddress());
- byte[] dstAddrBytes = NetUtil.createByteArrayFromIpAddressString(address);
+ byte[] dstAddrBytes = NetUtil.createByteArrayFromIpAddressString(
+ endpoint.getAddress().getHostAddress());
dataOutputStream.writeShort(srcAddrBytes.length + dstAddrBytes.length + 4);
dataOutputStream.write(srcAddrBytes);
dataOutputStream.write(dstAddrBytes);
diff --git a/core/src/main/java/org/geysermc/geyser/registry/AbstractMappedRegistry.java b/core/src/main/java/org/geysermc/geyser/registry/AbstractMappedRegistry.java
index c1268f504..fc4e3d022 100644
--- a/core/src/main/java/org/geysermc/geyser/registry/AbstractMappedRegistry.java
+++ b/core/src/main/java/org/geysermc/geyser/registry/AbstractMappedRegistry.java
@@ -58,7 +58,7 @@ public abstract class AbstractMappedRegistry> extends
}
/**
- * Returns & maps the value by the given key if present.
+ * Returns and maps the value by the given key if present.
*
* @param key the key
* @param mapper the mapper
diff --git a/core/src/main/java/org/geysermc/geyser/registry/Registries.java b/core/src/main/java/org/geysermc/geyser/registry/Registries.java
index 144d58232..0bf6ae078 100644
--- a/core/src/main/java/org/geysermc/geyser/registry/Registries.java
+++ b/core/src/main/java/org/geysermc/geyser/registry/Registries.java
@@ -140,7 +140,7 @@ public final class Registries {
/**
* A registry holding all the potion mixes.
*/
- public static final SimpleRegistry> POTION_MIXES;
+ public static final VersionedRegistry> POTION_MIXES;
/**
* A registry holding all the
@@ -183,7 +183,7 @@ public final class Registries {
RecipeRegistryPopulator.populate();
// Create registries that require other registries to load first
- POTION_MIXES = SimpleRegistry.create(PotionMixRegistryLoader::new);
+ POTION_MIXES = VersionedRegistry.create(PotionMixRegistryLoader::new);
ENCHANTMENTS = SimpleMappedRegistry.create("mappings/enchantments.json", EnchantmentRegistryLoader::new);
// Remove unneeded client generation data from NbtMapBuilder
diff --git a/core/src/main/java/org/geysermc/geyser/registry/loader/PotionMixRegistryLoader.java b/core/src/main/java/org/geysermc/geyser/registry/loader/PotionMixRegistryLoader.java
index 16fc1f817..88d2ef29d 100644
--- a/core/src/main/java/org/geysermc/geyser/registry/loader/PotionMixRegistryLoader.java
+++ b/core/src/main/java/org/geysermc/geyser/registry/loader/PotionMixRegistryLoader.java
@@ -25,6 +25,8 @@
package org.geysermc.geyser.registry.loader;
+import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
+import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.cloudburstmc.protocol.bedrock.data.inventory.crafting.PotionMixData;
import org.geysermc.geyser.inventory.item.Potion;
import org.geysermc.geyser.item.Items;
@@ -32,13 +34,13 @@ import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.network.GameProtocol;
import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.registry.type.ItemMapping;
+import org.geysermc.geyser.registry.type.ItemMappings;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
-//TODO this needs to be versioned, but the runtime item states between 1.17 and 1.17.10 are identical except for new blocks so this works for both
/**
* Generates a collection of {@link PotionMixData} that enables the
* Bedrock client to place brewing items into the brewing stand.
@@ -48,63 +50,71 @@ import java.util.Set;
* (Ex: Bedrock cannot normally place glass bottles or fully upgraded
* potions into the brewing stand, but Java can.)
*/
-public class PotionMixRegistryLoader implements RegistryLoader