3
0
Mirror von https://github.com/GeyserMC/Geyser.git synchronisiert 2024-07-03 20:08:08 +02:00

Merge remote-tracking branch 'origin/master' into feature/protocol-3.0

Dieser Commit ist enthalten in:
Camotoy 2023-04-06 13:26:28 -04:00
Commit d9811d08e3
110 geänderte Dateien mit 12141 neuen und 1039 gelöschten Zeilen

130
.github/workflows/build.yml vendored Normale Datei
Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

71
Jenkinsfile vendored
Datei anzeigen

@ -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') {

Datei anzeigen

@ -1,9 +1,6 @@
<img src="https://geysermc.org/img/geyser-1760-860.png" alt="Geyser" width="600"/>
[![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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -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<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
// 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:.*"))

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -60,16 +60,16 @@ public final class ReflectedNames {
}
static Constructor<PaperServerListPingEvent> 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);
}

Datei anzeigen

@ -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<Vector3i> attachedBlocks = new Object2IntOpenHashMap<>();
Object2IntMap<Vector3i> attachedBlocks = new Object2IntArrayMap<>();
boolean blocksFilled = false;
for (Map.Entry<UUID, GeyserSession> entry : geyser.getSessionManager().getSessions().entrySet()) {

Datei anzeigen

@ -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<String> 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<NbtMap> 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<BlockEntityInfo> blockEntityInfos) {
Player bukkitPlayer;
if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) {
return;
}
if (SchedulerUtils.FOLIA) {
Chunk chunk = getChunk(bukkitPlayer.getWorld(), x, z);
if (chunk == null) {
return;
}
Bukkit.getRegionScheduler().execute(this.plugin, bukkitPlayer.getWorld(), x, z, () ->
sendLecternData(session, chunk, blockEntityInfos));
} else {
Bukkit.getScheduler().runTask(this.plugin, () -> {
Chunk chunk = getChunk(bukkitPlayer.getWorld(), x, z);
if (chunk == null) {
return;
}
sendLecternData(session, chunk, blockEntityInfos);
});
}
}
private Chunk getChunk(World world, int x, int z) {
if (!world.isChunkLoaded(x, z)) {
return null;
}
return world.getChunkAt(x, z);
}
private void sendLecternData(GeyserSession session, Chunk chunk, List<BlockEntityInfo> blockEntityInfos) {
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.

Datei anzeigen

@ -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 <subcommand>
usage: /geyser <subcommand>
permission: geyser.command
permissions:
geyser.command:
default: true

Datei anzeigen

@ -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<KotlinCompile> {
kotlinOptions {
jvmTarget = "16"
}
}

Datei anzeigen

@ -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>("shadowJar") {
relocate(pattern, "org.geysermc.geyser.shaded.$pattern")

Datei anzeigen

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

Datei anzeigen

@ -1,33 +1,9 @@
plugins {
id("geyser.shadow-conventions")
id("com.jfrog.artifactory")
id("maven-publish")
id("net.kyori.indra.publishing")
}
publishing {
publications {
create<MavenPublication>("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")
}

Datei anzeigen

@ -24,6 +24,11 @@ tasks {
exclude(dependency(string))
}
}
sJar.dependencies {
exclude(dependency("org.checkerframework:checker-qual:.*"))
exclude(dependency("org.jetbrains:annotations:.*"))
}
}
}
named("build") {

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -83,8 +83,6 @@ public interface GeyserConfiguration {
boolean isDisableBedrockScaffolding();
boolean isAlwaysQuickChangeArmor();
EmoteOffhandWorkaroundOption getEmoteOffhandWorkaround();
String getDefaultLocale();

Datei anzeigen

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

Datei anzeigen

@ -148,6 +148,7 @@ public final class EntityDefinitions {
public static final EntityDefinition<AbstractSkeletonEntity> STRAY;
public static final EntityDefinition<StriderEntity> STRIDER;
public static final EntityDefinition<TadpoleEntity> TADPOLE;
public static final EntityDefinition<TextDisplayEntity> TEXT_DISPLAY;
public static final EntityDefinition<TNTEntity> TNT;
public static final EntityDefinition<MinecartEntity> TNT_MINECART;
public static final EntityDefinition<TraderLlamaEntity> TRADER_LLAMA;
@ -295,6 +296,28 @@ public final class EntityDefinitions {
.addTranslator(MetadataType.INT, TNTEntity::setFuseLength)
.build();
EntityDefinition<Entity> 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<FireballEntity> 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<AbstractHorseEntity> 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)

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -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<Component, ?> entityMetadata) {
this.dirtyMetadata.put(EntityDataTypes.NAME, MessageTranslator.convertMessage(entityMetadata.getValue()));
}
}

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -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<Direction, ?> entityMetadata) {

Datei anzeigen

@ -247,7 +247,10 @@ public class PlayerEntity extends LivingEntity {
@Override
public Vector3i setBedPosition(EntityMetadata<Optional<Vector3i>, ?> 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) {

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -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<BackendboundPacket> packetSender;
@Setter
private CompletableFuture<Integer> pendingLookup = null;
@Getter
private final Int2ObjectMap<CompletableFuture<Integer>> asyncPendingLookups = Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap<>(4));
@Setter
private CompletableFuture<int[]> pendingBatchLookup = null;
@Setter
private CompletableFuture<CompoundTag> pickBlockLookup = null;
private final AtomicInteger nextTransactionId = new AtomicInteger(1);
public GeyserboundPacketHandlerImpl(GeyserSession session, ErosionPacketSender<BackendboundPacket> 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<Integer> 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<Integer> 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<Vector3i> 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;
}
}

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -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 extends Item> T register(T item) {
return register(item, Registries.JAVA_ITEMS.get().size());

Datei anzeigen

@ -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<Set<Item>> repairIngredients;

Datei anzeigen

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

Datei anzeigen

@ -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<String, String> 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<Integer> future = new CompletableFuture<>(); // Boxes
erosionHandler.setPendingLookup(future);
erosionHandler.sendPacket(new BackendboundBlockRequestPacket(0, Vector3i.from(x, y, z)));
return future.join();
}
@Override
public CompletableFuture<Integer> 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<Integer> 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<int[]> 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<BlockEntityInfo> blockEntityInfos) {
var erosionHandler = session.getErosionHandler().getAsActive();
if (erosionHandler == null) {
// No-op - don't send any additional information other than what the chunk has already sent
return;
}
List<Vector3i> vectors = new ObjectArrayList<>(blockEntityInfos.size());
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<CompoundTag> future = new CompletableFuture<>();
erosionHandler.setPickBlockLookup(future);
erosionHandler.sendPacket(new BackendboundPickBlockPacket(Vector3i.from(x, y, z)));
return future;
}
}

Datei anzeigen

@ -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<Integer> getBlockAtAsync(GeyserSession session, Vector3i vector) {
return this.getBlockAtAsync(session, vector.getX(), vector.getY(), vector.getZ());
}
public CompletableFuture<Integer> 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.
* <p>
@ -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<BlockEntityInfo> blockEntityInfos);
/**
* @return whether we should expect lectern data to update, or if we have to fall back on a workaround.
*/
public abstract boolean shouldExpectLecternHandled();
public abstract boolean shouldExpectLecternHandled(GeyserSession session);
/**
* Updates a gamerule value on the Java server

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -58,7 +58,7 @@ public abstract class AbstractMappedRegistry<K, V, M extends Map<K, V>> 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

Datei anzeigen

@ -140,7 +140,7 @@ public final class Registries {
/**
* A registry holding all the potion mixes.
*/
public static final SimpleRegistry<Set<PotionMixData>> POTION_MIXES;
public static final VersionedRegistry<Set<PotionMixData>> 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

Datei anzeigen

@ -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<Object, Set<PotionMixData>> {
public class PotionMixRegistryLoader implements RegistryLoader<Object, Int2ObjectMap<Set<PotionMixData>>> {
@Override
public Set<PotionMixData> load(Object input) {
List<ItemMapping> ingredients = new ArrayList<>();
ingredients.add(getNonNull(Items.NETHER_WART));
ingredients.add(getNonNull(Items.REDSTONE));
ingredients.add(getNonNull(Items.GLOWSTONE_DUST));
ingredients.add(getNonNull(Items.FERMENTED_SPIDER_EYE));
ingredients.add(getNonNull(Items.GUNPOWDER));
ingredients.add(getNonNull(Items.DRAGON_BREATH));
ingredients.add(getNonNull(Items.SUGAR));
ingredients.add(getNonNull(Items.RABBIT_FOOT));
ingredients.add(getNonNull(Items.GLISTERING_MELON_SLICE));
ingredients.add(getNonNull(Items.SPIDER_EYE));
ingredients.add(getNonNull(Items.PUFFERFISH));
ingredients.add(getNonNull(Items.MAGMA_CREAM));
ingredients.add(getNonNull(Items.GOLDEN_CARROT));
ingredients.add(getNonNull(Items.BLAZE_POWDER));
ingredients.add(getNonNull(Items.GHAST_TEAR));
ingredients.add(getNonNull(Items.TURTLE_HELMET));
ingredients.add(getNonNull(Items.PHANTOM_MEMBRANE));
public Int2ObjectMap<Set<PotionMixData>> load(Object input) {
var allPotionMixes = new Int2ObjectOpenHashMap<Set<PotionMixData>>(Registries.ITEMS.get().size());
for (var entry : Registries.ITEMS.get().int2ObjectEntrySet()) {
ItemMappings mappings = entry.getValue();
List<ItemMapping> ingredients = new ArrayList<>();
ingredients.add(getNonNull(mappings, Items.NETHER_WART));
ingredients.add(getNonNull(mappings, Items.REDSTONE));
ingredients.add(getNonNull(mappings, Items.GLOWSTONE_DUST));
ingredients.add(getNonNull(mappings, Items.FERMENTED_SPIDER_EYE));
ingredients.add(getNonNull(mappings, Items.GUNPOWDER));
ingredients.add(getNonNull(mappings, Items.DRAGON_BREATH));
ingredients.add(getNonNull(mappings, Items.SUGAR));
ingredients.add(getNonNull(mappings, Items.RABBIT_FOOT));
ingredients.add(getNonNull(mappings, Items.GLISTERING_MELON_SLICE));
ingredients.add(getNonNull(mappings, Items.SPIDER_EYE));
ingredients.add(getNonNull(mappings, Items.PUFFERFISH));
ingredients.add(getNonNull(mappings, Items.MAGMA_CREAM));
ingredients.add(getNonNull(mappings, Items.GOLDEN_CARROT));
ingredients.add(getNonNull(mappings, Items.BLAZE_POWDER));
ingredients.add(getNonNull(mappings, Items.GHAST_TEAR));
ingredients.add(getNonNull(mappings, Items.TURTLE_HELMET));
ingredients.add(getNonNull(mappings, Items.PHANTOM_MEMBRANE));
List<ItemMapping> inputs = new ArrayList<>();
inputs.add(getNonNull(Items.POTION));
inputs.add(getNonNull(Items.SPLASH_POTION));
inputs.add(getNonNull(Items.LINGERING_POTION));
List<ItemMapping> inputs = List.of(
getNonNull(mappings, Items.POTION),
getNonNull(mappings, Items.SPLASH_POTION),
getNonNull(mappings, Items.LINGERING_POTION)
);
ItemMapping glassBottle = getNonNull(Items.GLASS_BOTTLE);
ItemMapping glassBottle = getNonNull(mappings, Items.GLASS_BOTTLE);
Set<PotionMixData> potionMixes = new HashSet<>();
Set<PotionMixData> potionMixes = new HashSet<>();
// Add all types of potions as inputs
ItemMapping fillerIngredient = ingredients.get(0);
for (ItemMapping entryInput : inputs) {
for (Potion potion : Potion.VALUES) {
// Add all types of potions as inputs
ItemMapping fillerIngredient = ingredients.get(0);
for (ItemMapping entryInput : inputs) {
for (Potion potion : Potion.VALUES) {
potionMixes.add(new PotionMixData(
entryInput.getBedrockDefinition().getRuntimeId(), potion.getBedrockId(),
fillerIngredient.getBedrockDefinition().getRuntimeId(), fillerIngredient.getBedrockData(),
glassBottle.getBedrockDefinition().getRuntimeId(), glassBottle.getBedrockData())
);
}
}
// Add all brewing ingredients
// Also adds glass bottle as input
for (ItemMapping ingredient : ingredients) {
potionMixes.add(new PotionMixData(
entryInput.getBedrockDefinition().getRuntimeId(), potion.getBedrockId(),
fillerIngredient.getBedrockDefinition().getRuntimeId(), fillerIngredient.getBedrockData(),
glassBottle.getBedrockDefinition().getRuntimeId(), glassBottle.getBedrockData(),
ingredient.getBedrockDefinition().getRuntimeId(), ingredient.getBedrockData(),
glassBottle.getBedrockDefinition().getRuntimeId(), glassBottle.getBedrockData())
);
}
}
// Add all brewing ingredients
// Also adds glass bottle as input
for (ItemMapping ingredient : ingredients) {
potionMixes.add(new PotionMixData(
glassBottle.getBedrockDefinition().getRuntimeId(), glassBottle.getBedrockData(),
ingredient.getBedrockDefinition().getRuntimeId(), ingredient.getBedrockData(),
glassBottle.getBedrockDefinition().getRuntimeId(), glassBottle.getBedrockData())
);
allPotionMixes.put(entry.getIntKey(), potionMixes);
}
return potionMixes;
allPotionMixes.trim();
return allPotionMixes;
}
private static ItemMapping getNonNull(Item javaItem) {
private static ItemMapping getNonNull(ItemMappings mappings, Item javaItem) {
ItemMapping itemMapping = Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()).getMapping(javaItem);
if (itemMapping == null)
throw new NullPointerException("No item entry exists for java identifier: " + javaItem.javaIdentifier());

Datei anzeigen

@ -35,6 +35,7 @@ import org.cloudburstmc.nbt.*;
import org.cloudburstmc.protocol.bedrock.codec.v544.Bedrock_v544;
import org.cloudburstmc.protocol.bedrock.codec.v560.Bedrock_v560;
import org.cloudburstmc.protocol.bedrock.codec.v567.Bedrock_v567;
import org.cloudburstmc.protocol.bedrock.codec.v575.Bedrock_v575;
import org.cloudburstmc.protocol.bedrock.data.defintions.BlockDefinition;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.level.block.BlockStateValues;
@ -73,6 +74,16 @@ public final class BlockRegistryPopulator {
.put(ObjectIntPair.of("1_19_20", Bedrock_v544.CODEC.getProtocolVersion()), emptyMapper)
.put(ObjectIntPair.of("1_19_50", Bedrock_v560.CODEC.getProtocolVersion()), emptyMapper)
.put(ObjectIntPair.of("1_19_60", Bedrock_v567.CODEC.getProtocolVersion()), emptyMapper)
.put(ObjectIntPair.of("1_19_70", Bedrock_v575.CODEC.getProtocolVersion()), (bedrockIdentifier, statesBuilder) -> {
if (bedrockIdentifier.equals("minecraft:wool")) {
String color = (String) statesBuilder.remove("color");
if ("silver".equals(color)) {
color = "light_gray";
}
return "minecraft:" + color + "_wool";
}
return null;
})
.build();
for (Map.Entry<ObjectIntPair<String>, BiFunction<String, NbtMapBuilder, String>> palette : blockMappers.entrySet()) {
@ -216,7 +227,6 @@ public final class BlockRegistryPopulator {
Deque<String> cleanIdentifiers = new ArrayDeque<>();
int javaRuntimeId = -1;
int bellBlockId = -1;
int cobwebBlockId = -1;
int furnaceRuntimeId = -1;
int furnaceLitRuntimeId = -1;
@ -293,10 +303,7 @@ public final class BlockRegistryPopulator {
// It's possible to only have this store differences in names, but the key set of all Java names is used in sending command suggestions
BlockRegistries.JAVA_TO_BEDROCK_IDENTIFIERS.register(cleanJavaIdentifier.intern(), bedrockIdentifier.intern());
if (javaId.startsWith("minecraft:bell[")) {
bellBlockId = uniqueJavaId;
} else if (javaId.contains("cobweb")) {
if (javaId.contains("cobweb")) {
cobwebBlockId = uniqueJavaId;
} else if (javaId.startsWith("minecraft:furnace[facing=north")) {
@ -317,10 +324,6 @@ public final class BlockRegistryPopulator {
slimeBlockRuntimeId = javaRuntimeId;
}
}
if (bellBlockId == -1) {
throw new AssertionError("Unable to find bell in palette");
}
BlockStateValues.JAVA_BELL_ID = bellBlockId;
if (cobwebBlockId == -1) {
throw new AssertionError("Unable to find cobwebs in palette");

Datei anzeigen

@ -40,6 +40,7 @@ import org.cloudburstmc.nbt.NbtType;
import org.cloudburstmc.protocol.bedrock.codec.v544.Bedrock_v544;
import org.cloudburstmc.protocol.bedrock.codec.v560.Bedrock_v560;
import org.cloudburstmc.protocol.bedrock.codec.v567.Bedrock_v567;
import org.cloudburstmc.protocol.bedrock.codec.v575.Bedrock_v575;
import org.cloudburstmc.protocol.bedrock.data.defintions.BlockDefinition;
import org.cloudburstmc.protocol.bedrock.data.defintions.ItemDefinition;
import org.cloudburstmc.protocol.bedrock.data.defintions.SimpleItemDefinition;
@ -76,6 +77,7 @@ public class ItemRegistryPopulator {
paletteVersions.put("1_19_20", new PaletteVersion(Bedrock_v544.CODEC.getProtocolVersion(), Collections.emptyMap()));
paletteVersions.put("1_19_50", new PaletteVersion(Bedrock_v560.CODEC.getProtocolVersion(), Collections.emptyMap()));
paletteVersions.put("1_19_60", new PaletteVersion(Bedrock_v567.CODEC.getProtocolVersion(), Collections.emptyMap()));
paletteVersions.put("1_19_70", new PaletteVersion(Bedrock_v575.CODEC.getProtocolVersion(), Collections.emptyMap()));
GeyserBootstrap bootstrap = GeyserImpl.getInstance().getBootstrap();
@ -173,6 +175,7 @@ public class ItemRegistryPopulator {
Set<Item> javaOnlyItems = new ObjectOpenHashSet<>();
Collections.addAll(javaOnlyItems, Items.SPECTRAL_ARROW, Items.DEBUG_STICK,
Items.KNOWLEDGE_BOOK, Items.TIPPED_ARROW, Items.BUNDLE);
javaOnlyItems.add(Items.DECORATED_POT);
if (!customItemsAllowed) {
javaOnlyItems.add(Items.FURNACE_MINECART);
}
@ -196,6 +199,11 @@ public class ItemRegistryPopulator {
mappingItem = entry.getValue();
}
// 1.19.70+
if (palette.getValue().protocolVersion() >= Bedrock_v575.CODEC.getProtocolVersion() && mappingItem.getBedrockIdentifier().equals("minecraft:wool")) {
mappingItem.setBedrockIdentifier(javaItem.javaIdentifier());
}
if (customItemsAllowed && javaItem == Items.FURNACE_MINECART) {
// Will be added later
mappings.add(null);

Datei anzeigen

@ -35,6 +35,7 @@ import org.geysermc.geyser.util.FileUtils;
public class PacketRegistryPopulator {
@SuppressWarnings("unchecked")
public static void populate() {
for (Class<?> clazz : FileUtils.getGeneratedClassesForAnnotation(Translator.class)) {
Class<?> packet = clazz.getAnnotation(Translator.class).packet();
@ -44,18 +45,18 @@ public class PacketRegistryPopulator {
try {
if (Packet.class.isAssignableFrom(packet)) {
Class<? extends Packet> targetPacket = (Class<? extends Packet>) packet;
PacketTranslator<? extends Packet> translator = (PacketTranslator<? extends Packet>) clazz.newInstance();
PacketTranslator<? extends Packet> translator = (PacketTranslator<? extends Packet>) clazz.getConstructor().newInstance();
Registries.JAVA_PACKET_TRANSLATORS.register(targetPacket, translator);
} else if (BedrockPacket.class.isAssignableFrom(packet)) {
Class<? extends BedrockPacket> targetPacket = (Class<? extends BedrockPacket>) packet;
PacketTranslator<? extends BedrockPacket> translator = (PacketTranslator<? extends BedrockPacket>) clazz.newInstance();
PacketTranslator<? extends BedrockPacket> translator = (PacketTranslator<? extends BedrockPacket>) clazz.getConstructor().newInstance();
Registries.BEDROCK_PACKET_TRANSLATORS.register(targetPacket, translator);
} else {
GeyserImpl.getInstance().getLogger().error("Class " + clazz.getCanonicalName() + " is annotated as a translator but has an invalid target packet.");
}
} catch (InstantiationException | IllegalAccessException e) {
} catch (Exception e) {
GeyserImpl.getInstance().getLogger().error("Could not instantiate annotated translator " + clazz.getCanonicalName());
}
}

Datei anzeigen

@ -26,7 +26,6 @@
package org.geysermc.geyser.registry.type;
import org.cloudburstmc.protocol.bedrock.data.LevelEventType;
import org.cloudburstmc.protocol.bedrock.data.ParticleType;
import javax.annotation.ParametersAreNullableByDefault;

Datei anzeigen

@ -27,6 +27,7 @@ package org.geysermc.geyser.scoreboard;
import com.github.steveice10.mc.protocol.data.game.scoreboard.ScoreboardPosition;
import org.cloudburstmc.protocol.bedrock.data.ScoreInfo;
import org.cloudburstmc.protocol.bedrock.data.command.CommandEnumConstraint;
import org.cloudburstmc.protocol.bedrock.packet.RemoveObjectivePacket;
import org.cloudburstmc.protocol.bedrock.packet.SetDisplayObjectivePacket;
import org.cloudburstmc.protocol.bedrock.packet.SetScorePacket;
@ -44,6 +45,8 @@ import javax.annotation.Nullable;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
import java.util.stream.Collectors;
import static org.geysermc.geyser.scoreboard.UpdateType.*;
@ -365,8 +368,10 @@ public final class Scoreboard {
}
@Contract("-> new")
public String[] getTeamNames() {
return teams.keySet().toArray(new String[0]);
public LinkedHashMap<String, Set<CommandEnumConstraint>> getTeamNames() {
return teams.keySet().stream()
.collect(Collectors.toMap(Function.identity(), o -> EnumSet.noneOf(CommandEnumConstraint.class),
(o1, o2) -> o1, LinkedHashMap::new));
}
/**

Datei anzeigen

@ -33,6 +33,8 @@ import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.HashSet;
import java.util.Set;
@ -44,7 +46,7 @@ public final class Team {
@Getter(AccessLevel.PACKAGE)
private final Set<String> entities;
@Setter private NameTagVisibility nameTagVisibility;
@Nonnull private NameTagVisibility nameTagVisibility = NameTagVisibility.ALWAYS;
@Setter private TeamColor color;
private final TeamData currentData;
@ -199,6 +201,14 @@ public final class Team {
};
}
public Team setNameTagVisibility(@Nullable NameTagVisibility nameTagVisibility) {
if (nameTagVisibility != null) {
// Null check like this (and this.nameTagVisibility defaults to ALWAYS) as of Java 1.19.4
this.nameTagVisibility = nameTagVisibility;
}
return this;
}
@Override
public int hashCode() {
return id.hashCode();

Datei anzeigen

@ -81,11 +81,11 @@ import lombok.Setter;
import lombok.experimental.Accessors;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.common.value.qual.IntRange;
import org.cloudburstmc.math.GenericMath;
import org.cloudburstmc.math.vector.*;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.protocol.bedrock.BedrockServerSession;
import org.cloudburstmc.protocol.bedrock.data.*;
import org.cloudburstmc.protocol.bedrock.data.command.CommandEnumConstraint;
import org.cloudburstmc.protocol.bedrock.data.command.CommandEnumData;
import org.cloudburstmc.protocol.bedrock.data.command.CommandPermission;
import org.cloudburstmc.protocol.bedrock.data.command.SoftEnumUpdateType;
@ -114,6 +114,8 @@ import org.geysermc.geyser.entity.type.Entity;
import org.geysermc.geyser.entity.type.ItemFrameEntity;
import org.geysermc.geyser.entity.type.Tickable;
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
import org.geysermc.geyser.erosion.AbstractGeyserboundPacketHandler;
import org.geysermc.geyser.erosion.GeyserboundHandshakePacketHandler;
import org.geysermc.geyser.inventory.Inventory;
import org.geysermc.geyser.inventory.PlayerInventory;
import org.geysermc.geyser.inventory.recipe.GeyserRecipe;
@ -138,6 +140,7 @@ import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.geyser.util.ChunkUtils;
import org.geysermc.geyser.util.DimensionUtils;
import org.geysermc.geyser.util.LoginEncryptionUtils;
import org.jetbrains.annotations.NotNull;
import java.net.ConnectException;
import java.net.InetSocketAddress;
@ -170,6 +173,10 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
@Setter
private List<SignedJWT> certChainData;
@NotNull
@Setter
private AbstractGeyserboundPacketHandler erosionHandler;
@Accessors(fluent = true)
@Setter
private RemoteServer remoteServer;
@ -257,7 +264,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
/**
* Stores a list of all lectern locations and their block entity tags.
* See {@link WorldManager#getLecternDataAt(GeyserSession, int, int, int, boolean)}
* See {@link WorldManager#sendLecternData(GeyserSession, int, int, int)}
* for more information.
*/
private final Set<Vector3i> lecternCache;
@ -553,6 +560,8 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
this.upstream = new UpstreamSession(bedrockServerSession);
this.eventLoop = eventLoop;
this.erosionHandler = new GeyserboundHandshakePacketHandler(this);
this.advancementsCache = new AdvancementsCache(this);
this.bookEditCache = new BookEditCache(this);
this.chunkCache = new ChunkCache(this);
@ -581,7 +590,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
this.spawned = false;
this.loggedIn = false;
if (geyser.getWorldManager().shouldExpectLecternHandled()) {
if (geyser.getWorldManager().shouldExpectLecternHandled(this)) {
// Unneeded on these platforms
this.lecternCache = null;
} else {
@ -631,7 +640,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
// Potion mixes are registered by default, as they are needed to be able to put ingredients into the brewing stand.
CraftingDataPacket craftingDataPacket = new CraftingDataPacket();
craftingDataPacket.setCleanRecipes(true);
craftingDataPacket.getPotionMixData().addAll(Registries.POTION_MIXES.get());
craftingDataPacket.getPotionMixData().addAll(Registries.POTION_MIXES.forVersion(this.upstream.getProtocolVersion()));
upstream.sendPacket(craftingDataPacket);
PlayStatusPacket playStatusPacket = new PlayStatusPacket();
@ -723,7 +732,11 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
return;
}
connectDownstream();
try {
connectDownstream();
} catch (Throwable t) {
t.printStackTrace();
}
});
}
@ -767,7 +780,11 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
return;
}
connectDownstream();
try {
connectDownstream();
} catch (Throwable t) {
t.printStackTrace();
}
});
}
@ -841,7 +858,12 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
selectedProfile,
service.getAccessToken()
);
connectDownstream();
try {
connectDownstream();
} catch (Throwable t) {
t.printStackTrace();
return false;
}
// Save our refresh token for later use
geyser.saveRefreshToken(bedrockUsername(), service.getRefreshToken());
@ -1006,7 +1028,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
// Server is offline, probably
disconnectMessage = GeyserLocale.getPlayerLocaleString("geyser.network.remote.server_offline", locale());
} else {
disconnectMessage = MessageTranslator.convertMessageLenient(event.getReason());
disconnectMessage = MessageTranslator.convertMessage(event.getReason());
}
if (downstream instanceof LocalSession) {
@ -1069,6 +1091,8 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
tickThread.cancel(false);
}
erosionHandler.close();
closed = true;
}
@ -1346,6 +1370,12 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
return false;
}
public void requestOffhandSwap() {
ServerboundPlayerActionPacket swapHandsPacket = new ServerboundPlayerActionPacket(PlayerAction.SWAP_HANDS, Vector3i.ZERO,
Direction.DOWN, 0);
sendDownstreamPacket(swapHandsPacket);
}
/**
* Will be overwritten for GeyserConnect.
*/
@ -1396,10 +1426,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
}
public void setServerRenderDistance(int renderDistance) {
// +1 is for Fabric and Spigot
// Without the client misses loading some chunks per https://github.com/GeyserMC/Geyser/issues/3490
// Fog still appears essentially normally
renderDistance = renderDistance + 1;
this.serverRenderDistance = renderDistance;
ChunkRadiusUpdatedPacket chunkRadiusUpdatedPacket = new ChunkRadiusUpdatedPacket();
@ -1411,11 +1437,13 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
return this.upstream.getAddress();
}
@Override
public boolean sendForm(@NonNull Form form) {
formCache.showForm(form);
return true;
}
@Override
public boolean sendForm(@NonNull FormBuilder<?, ?, ?> formBuilder) {
formCache.showForm(formBuilder.build());
return true;
@ -1682,6 +1710,8 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
abilities.add(Ability.MINE);
// Needed so you can drop items
abilities.add(Ability.DOORS_AND_SWITCHES);
// Required for lecterns to work (likely started around 1.19.10; confirmed on 1.19.70)
abilities.add(Ability.OPEN_CONTAINERS);
if (gameMode == GameMode.CREATIVE) {
// Needed so the client doesn't attempt to take away items
abilities.add(Ability.INSTABUILD);
@ -1896,18 +1926,19 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
return true;
}
public void addCommandEnum(String name, String... enums) {
public void addCommandEnum(String name, String enums) {
softEnumPacket(name, SoftEnumUpdateType.ADD, enums);
}
public void removeCommandEnum(String name, String... enums) {
public void removeCommandEnum(String name, String enums) {
softEnumPacket(name, SoftEnumUpdateType.REMOVE, enums);
}
private void softEnumPacket(String name, SoftEnumUpdateType type, String... enums) {
private void softEnumPacket(String name, SoftEnumUpdateType type, String enums) {
UpdateSoftEnumPacket packet = new UpdateSoftEnumPacket();
packet.setType(type);
packet.setSoftEnum(new CommandEnumData(name, enums, true));
// TODO
packet.setSoftEnum(new CommandEnumData(name, new LinkedHashMap<>(Collections.singletonMap(enums, EnumSet.noneOf(CommandEnumConstraint.class))), true));
sendUpstreamPacket(packet);
}
}

Datei anzeigen

@ -73,8 +73,7 @@ public final class LodestoneCache {
}
}
for (Int2ObjectMap.Entry<LodestonePos> entry : this.lodestones.int2ObjectEntrySet()) {
LodestonePos pos = entry.getValue();
for (LodestonePos pos : this.lodestones.values()) {
if (pos.equals(x, y, z, dim)) {
// Use this existing position instead
this.activeLodestones.put(itemStack, pos);

Datei anzeigen

@ -43,6 +43,7 @@ import org.geysermc.geyser.inventory.updater.UIInventoryUpdater;
import org.geysermc.geyser.session.GeyserSession;
import java.util.Arrays;
import java.util.Locale;
public class EnchantingInventoryTranslator extends AbstractBlockInventoryTranslator {
public EnchantingInventoryTranslator() {
@ -71,7 +72,7 @@ public class EnchantingInventoryTranslator extends AbstractBlockInventoryTransla
// The Bedrock index might need changed, so let's look it up and see.
int bedrockIndex = value;
if (bedrockIndex != -1) {
Enchantment enchantment = Enchantment.getByJavaIdentifier("minecraft:" + Enchantment.JavaEnchantment.of(bedrockIndex).name().toLowerCase());
Enchantment enchantment = Enchantment.getByJavaIdentifier("minecraft:" + Enchantment.JavaEnchantment.of(bedrockIndex).name().toLowerCase(Locale.ROOT));
if (enchantment != null) {
// Convert the Java enchantment index to Bedrock's
bedrockIndex = enchantment.ordinal();

Datei anzeigen

@ -92,7 +92,7 @@ public abstract class InventoryTranslator {
put(ContainerType.LOOM, new LoomInventoryTranslator());
put(ContainerType.MERCHANT, new MerchantInventoryTranslator());
put(ContainerType.SHULKER_BOX, new ShulkerInventoryTranslator());
put(ContainerType.SMITHING, new SmithingInventoryTranslator());
put(ContainerType.LEGACY_SMITHING, new SmithingInventoryTranslator());
put(ContainerType.STONECUTTER, new StonecutterInventoryTranslator());
/* Lectern */

Datei anzeigen

@ -35,6 +35,7 @@ import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.cloudburstmc.nbt.NbtType;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.geysermc.erosion.util.LecternUtils;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.inventory.Inventory;
import org.geysermc.geyser.inventory.LecternContainer;
@ -110,13 +111,13 @@ public class LecternInventoryTranslator extends BaseInventoryTranslator {
Vector3i position = session.getLastInteractionBlockPosition();
// If shouldExpectLecternHandled returns true, this is already handled for us
// shouldRefresh means that we should boot out the client on our side because their lectern GUI isn't updated yet
boolean shouldRefresh = !session.getGeyser().getWorldManager().shouldExpectLecternHandled() && !session.getLecternCache().contains(position);
boolean shouldRefresh = !session.getGeyser().getWorldManager().shouldExpectLecternHandled(session) && !session.getLecternCache().contains(position);
NbtMap blockEntityTag;
if (tag != null) {
int pagesSize = ((ListTag) tag.get("pages")).size();
ItemData itemData = book.getItemData(session);
NbtMapBuilder lecternTag = getBaseLecternTag(position.getX(), position.getY(), position.getZ(), pagesSize);
NbtMapBuilder lecternTag = LecternUtils.getBaseLecternTag(position.getX(), position.getY(), position.getZ(), pagesSize);
lecternTag.putCompound("book", NbtMap.builder()
.putByte("Count", (byte) itemData.getCount())
.putShort("Damage", (short) 0)
@ -127,7 +128,7 @@ public class LecternInventoryTranslator extends BaseInventoryTranslator {
blockEntityTag = lecternTag.build();
} else {
// There is *a* book here, but... no NBT.
NbtMapBuilder lecternTag = getBaseLecternTag(position.getX(), position.getY(), position.getZ(), 1);
NbtMapBuilder lecternTag = LecternUtils.getBaseLecternTag(position.getX(), position.getY(), position.getZ(), 1);
NbtMapBuilder bookTag = NbtMap.builder()
.putByte("Count", (byte) 1)
.putShort("Damage", (short) 0)
@ -162,20 +163,4 @@ public class LecternInventoryTranslator extends BaseInventoryTranslator {
public Inventory createInventory(String name, int windowId, ContainerType containerType, PlayerInventory playerInventory) {
return new LecternContainer(name, windowId, this.size, containerType, playerInventory);
}
public static NbtMapBuilder getBaseLecternTag(int x, int y, int z, int totalPages) {
NbtMapBuilder builder = NbtMap.builder()
.putInt("x", x)
.putInt("y", y)
.putInt("z", z)
.putString("id", "Lectern");
if (totalPages != 0) {
builder.putByte("hasBook", (byte) 1);
builder.putInt("totalPages", totalPages);
} else {
// Not usually needed, but helps with kicking out Bedrock players from reading the UI
builder.putByte("hasBook", (byte) 0);
}
return builder;
}
}

Datei anzeigen

@ -41,10 +41,10 @@ import java.util.OptionalInt;
/**
* This is only a separate class for testing purposes so we don't have to load in GeyserImpl in ItemTranslator.
*/
final class CustomItemTranslator {
public final class CustomItemTranslator {
@Nullable
static ItemDefinition getCustomItem(CompoundTag nbt, ItemMapping mapping) {
public static ItemDefinition getCustomItem(CompoundTag nbt, ItemMapping mapping) {
if (nbt == null) {
return null;
}

Datei anzeigen

@ -43,6 +43,8 @@ import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static org.geysermc.erosion.util.BannerUtils.getJavaPatternTag;
@ItemRemapper
public class BannerTranslator extends NbtItemStackTranslator {
/**
@ -69,15 +71,6 @@ public class BannerTranslator extends NbtItemStackTranslator {
OMINOUS_BANNER_PATTERN.add(getJavaPatternTag("bo", 15));
}
public static CompoundTag getJavaPatternTag(String pattern, int color) {
StringTag patternType = new StringTag("Pattern", pattern);
IntTag colorTag = new IntTag("Color", color);
CompoundTag tag = new CompoundTag("");
tag.put(patternType);
tag.put(colorTag);
return tag;
}
public BannerTranslator() {
appliedItems = Registries.JAVA_ITEMS.get().stream()
.filter(entry -> entry.javaIdentifier().endsWith("banner"))
@ -120,7 +113,7 @@ public class BannerTranslator extends NbtItemStackTranslator {
* @return The Java edition format pattern nbt
*/
public static CompoundTag getJavaBannerPattern(NbtMap pattern) {
return BannerTranslator.getJavaPatternTag(pattern.getString("Pattern"), 15 - pattern.getInt("Color"));
return getJavaPatternTag(pattern.getString("Pattern"), 15 - pattern.getInt("Color"));
}
/**

Datei anzeigen

@ -25,11 +25,14 @@
package org.geysermc.geyser.translator.inventory.item.nbt;
import com.github.steveice10.mc.protocol.data.game.Identifier;
import com.github.steveice10.opennbt.tag.builtin.*;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.inventory.item.Enchantment;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.text.ChatColor;
import org.geysermc.geyser.text.MinecraftLocale;
import org.geysermc.geyser.translator.inventory.item.ItemRemapper;
import org.geysermc.geyser.translator.inventory.item.NbtItemStackTranslator;
@ -43,28 +46,27 @@ public class EnchantmentTranslator extends NbtItemStackTranslator {
@Override
public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemMapping mapping) {
List<Tag> newTags = new ArrayList<>();
Tag enchantmentTag = itemTag.get("Enchantments");
Tag enchantmentTag = itemTag.remove("Enchantments");
if (enchantmentTag instanceof ListTag listTag) {
for (Tag tag : listTag.getValue()) {
if (!(tag instanceof CompoundTag)) continue;
CompoundTag bedrockTag = remapEnchantment((CompoundTag) tag);
newTags.add(bedrockTag);
}
itemTag.remove("Enchantments");
}
enchantmentTag = itemTag.get("StoredEnchantments");
if (enchantmentTag instanceof ListTag listTag) {
for (Tag tag : listTag.getValue()) {
if (!(tag instanceof CompoundTag)) continue;
CompoundTag bedrockTag = remapEnchantment((CompoundTag) tag);
CompoundTag bedrockTag = remapEnchantment(session, (CompoundTag) tag, itemTag);
if (bedrockTag != null) {
newTags.add(bedrockTag);
}
}
}
// TODO consolidate this into EnchantedBookTranslator
enchantmentTag = itemTag.remove("StoredEnchantments");
if (enchantmentTag instanceof ListTag listTag) {
for (Tag tag : listTag.getValue()) {
if (!(tag instanceof CompoundTag)) continue;
CompoundTag bedrockTag = remapEnchantment(session, (CompoundTag) tag, itemTag);
if (bedrockTag != null) {
bedrockTag.put(new ShortTag("GeyserStoredEnchantment", (short) 0));
newTags.add(bedrockTag);
}
}
itemTag.remove("StoredEnchantments");
}
if (!newTags.isEmpty()) {
@ -99,7 +101,6 @@ public class EnchantmentTranslator extends NbtItemStackTranslator {
javaValue.put("lvl", new IntTag("lvl", levelTag != null ? levelTag.getValue() : 1));
javaTag.setValue(javaValue);
if (geyserStoredEnchantmentTag != null) {
tagValue.remove("GeyserStoredEnchantment");
storedEnchantments.add(javaTag);
@ -120,13 +121,20 @@ public class EnchantmentTranslator extends NbtItemStackTranslator {
}
private CompoundTag remapEnchantment(CompoundTag tag) {
private CompoundTag remapEnchantment(GeyserSession session, CompoundTag tag, CompoundTag rootTag) {
Tag javaEnchId = tag.get("id");
if (!(javaEnchId instanceof StringTag))
return null;
Enchantment enchantment = Enchantment.getByJavaIdentifier(((StringTag) javaEnchId).getValue());
if (enchantment == null) {
if (Identifier.formalize((String) javaEnchId.getValue()).equals("minecraft:sweeping")) {
Tag javaEnchLvl = tag.get("lvl");
int sweepingLvl = javaEnchLvl != null && javaEnchLvl.getValue() instanceof Number lvl ? lvl.intValue() : 0;
addSweeping(session, rootTag, sweepingLvl);
return null;
}
GeyserImpl.getInstance().getLogger().debug("Unknown Java enchantment while NBT item translating: " + javaEnchId.getValue());
return null;
}
@ -140,4 +148,21 @@ public class EnchantmentTranslator extends NbtItemStackTranslator {
return bedrockTag;
}
}
private void addSweeping(GeyserSession session, CompoundTag itemTag, int level) {
CompoundTag displayTag = itemTag.get("display");
if (displayTag == null) {
displayTag = new CompoundTag("display");
itemTag.put(displayTag);
}
ListTag loreTag = displayTag.get("Lore");
if (loreTag == null) {
loreTag = new ListTag("Lore");
displayTag.put(loreTag);
}
String sweepingTranslation = MinecraftLocale.getLocaleString("enchantment.minecraft.sweeping", session.locale());
String lvlTranslation = MinecraftLocale.getLocaleString("enchantment.level." + level, session.locale());
loreTag.add(new StringTag("", ChatColor.RESET + ChatColor.GRAY + sweepingTranslation + " " + lvlTranslation));
}
}

Datei anzeigen

@ -224,7 +224,7 @@ public class PistonBlockEntity {
int blockId = session.getGeyser().getWorldManager().getBlockAt(session, blockInFront);
if (BlockStateValues.isPistonHead(blockId)) {
ChunkUtils.updateBlock(session, BlockStateValues.JAVA_AIR_ID, blockInFront);
} else if (session.getGeyser().getPlatformType() == PlatformType.SPIGOT && blockId == BlockStateValues.JAVA_AIR_ID) {
} else if ((session.getGeyser().getPlatformType() == PlatformType.SPIGOT || session.getErosionHandler().isActive()) && blockId == BlockStateValues.JAVA_AIR_ID) {
// Spigot removes the piston head from the cache, but we need to send the block update ourselves
ChunkUtils.updateBlock(session, BlockStateValues.JAVA_AIR_ID, blockInFront);
}

Datei anzeigen

@ -71,30 +71,28 @@ public class BedrockBlockPickRequestTranslator extends PacketTranslator<BlockPic
boolean addNbtData = packet.isAddUserData() && blockMapping.isBlockEntity(); // Holding down CTRL
if (BlockStateValues.getBannerColor(blockToPick) != -1 || addNbtData) {
session.getGeyser().getWorldManager().getPickItemNbt(session, vector.getX(), vector.getY(), vector.getZ(), addNbtData)
.whenComplete((tag, ex) -> {
.whenComplete((tag, ex) -> session.ensureInEventLoop(() -> {
if (tag == null) {
pickItem(session, blockMapping);
return;
}
session.ensureInEventLoop(() -> {
if (addNbtData) {
ListTag lore = new ListTag("Lore");
lore.add(new StringTag("", "\"(+NBT)\""));
CompoundTag display = tag.get("display");
if (display == null) {
display = new CompoundTag("display");
tag.put(display);
}
display.put(lore);
if (addNbtData) {
ListTag lore = new ListTag("Lore");
lore.add(new StringTag("", "\"(+NBT)\""));
CompoundTag display = tag.get("display");
if (display == null) {
display = new CompoundTag("display");
tag.put(display);
}
// I don't really like this... I'd rather get an ID from the block mapping I think
ItemMapping mapping = session.getItemMappings().getMapping(blockMapping.getPickItem());
display.put(lore);
}
// I don't really like this... I'd rather get an ID from the block mapping I think
ItemMapping mapping = session.getItemMappings().getMapping(blockMapping.getPickItem());
ItemStack itemStack = new ItemStack(mapping.getJavaItem().javaId(), 1, tag);
InventoryUtils.findOrCreateItem(session, itemStack);
});
});
ItemStack itemStack = new ItemStack(mapping.getJavaItem().javaId(), 1, tag);
InventoryUtils.findOrCreateItem(session, itemStack);
}));
return;
}

Datei anzeigen

@ -62,8 +62,10 @@ public class BedrockEntityPickRequestTranslator extends PacketTranslator<EntityP
case 2 -> "birch";
case 3 -> "jungle";
case 4 -> "acacia";
case 5 -> "dark_oak";
case 6 -> "mangrove";
//case 5 -> "cherry"; TODO
case 6 -> "dark_oak";
case 7 -> "mangrove";
//case 8 -> "bamboo";
default -> "oak";
};
itemName = typeOfBoat + "_" + entity.getDefinition().entityType().name().toLowerCase(Locale.ROOT);

Datei anzeigen

@ -74,9 +74,11 @@ import org.geysermc.geyser.translator.inventory.InventoryTranslator;
import org.geysermc.geyser.translator.inventory.item.ItemTranslator;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.geyser.util.*;
import org.geysermc.geyser.util.BlockUtils;
import org.geysermc.geyser.util.CooldownUtils;
import org.geysermc.geyser.util.EntityUtils;
import org.geysermc.geyser.util.InteractionResult;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
@ -355,43 +357,6 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
ServerboundUseItemPacket useItemPacket = new ServerboundUseItemPacket(Hand.MAIN_HAND, session.getWorldCache().nextPredictionSequence());
session.sendDownstreamPacket(useItemPacket);
List<LegacySetItemSlotData> legacySlots = packet.getLegacySlots();
if (packet.getActions().size() == 1 && legacySlots.size() > 0) {
InventoryActionData actionData = packet.getActions().get(0);
LegacySetItemSlotData slotData = legacySlots.get(0);
if (slotData.getContainerId() == 6 && actionData.getToItem().getDefinition() != ItemDefinition.AIR) {
// The player is trying to swap out an armor piece that already has an item in it
if (session.getGeyser().getConfig().isAlwaysQuickChangeArmor()) {
// Java doesn't know when a player is in its own inventory and not, so we
// can abuse this feature to send a swap inventory packet
int bedrockHotbarSlot = packet.getHotbarSlot();
Click click = InventoryUtils.getClickForHotbarSwap(bedrockHotbarSlot);
if (click != null && slotData.getSlots().length != 0) {
Inventory playerInventory = session.getPlayerInventory();
// Bedrock sends us the index of the slot in the armor container; armor in Java
// Edition is offset by 5 in the player inventory
int armorSlot = slotData.getSlots()[0] + 5;
GeyserItemStack armorSlotItem = playerInventory.getItem(armorSlot);
GeyserItemStack hotbarItem = playerInventory.getItem(playerInventory.getOffsetForHotbar(bedrockHotbarSlot));
playerInventory.setItem(armorSlot, hotbarItem, session);
playerInventory.setItem(bedrockHotbarSlot, armorSlotItem, session);
Int2ObjectMap<ItemStack> changedSlots = new Int2ObjectOpenHashMap<>(2);
changedSlots.put(armorSlot, hotbarItem.getItemStack());
changedSlots.put(bedrockHotbarSlot, armorSlotItem.getItemStack());
ServerboundContainerClickPacket clickPacket = new ServerboundContainerClickPacket(
playerInventory.getJavaId(), playerInventory.getStateId(), armorSlot,
click.actionType, click.action, null, changedSlots);
session.sendDownstreamPacket(clickPacket);
}
} else {
// Disallowed; let's revert
session.getInventoryTranslator().updateInventory(session, session.getPlayerInventory());
}
}
}
}
case 2 -> {
int blockState = session.getGameMode() == GameMode.CREATIVE ?

Datei anzeigen

@ -30,7 +30,6 @@ import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.Server
import org.cloudburstmc.protocol.bedrock.data.Ability;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.cloudburstmc.protocol.bedrock.packet.RequestAbilityPacket;
import org.geysermc.geyser.network.GameProtocol;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
@ -43,31 +42,22 @@ public class BedrockRequestAbilityTranslator extends PacketTranslator<RequestAbi
@Override
public void translate(GeyserSession session, RequestAbilityPacket packet) {
// Gatekeep to 1.19.30 so older versions don't fire twice
if (!GameProtocol.supports1_19_30(session)) {
return;
}
if (packet.getAbility() == Ability.FLYING) {
handle(session, packet.isBoolValue());
}
}
boolean isFlying = packet.isBoolValue();
if (!isFlying && session.getGameMode() == GameMode.SPECTATOR) {
// We should always be flying in spectator mode
session.sendAdventureSettings();
return;
} else if (isFlying && session.getPlayerEntity().getFlag(EntityFlag.SWIMMING) && session.getCollisionManager().isPlayerInWater()) {
// As of 1.18.1, Java Edition cannot fly while in water, but it can fly while crawling
// If this isn't present, swimming on a 1.13.2 server and then attempting to fly will put you into a flying/swimming state that is invalid on JE
session.sendAdventureSettings();
return;
}
//FIXME remove after pre-1.19.30 support is dropped and merge into main method
static void handle(GeyserSession session, boolean isFlying) {
if (!isFlying && session.getGameMode() == GameMode.SPECTATOR) {
// We should always be flying in spectator mode
session.sendAdventureSettings();
return;
} else if (isFlying && session.getPlayerEntity().getFlag(EntityFlag.SWIMMING) && session.getCollisionManager().isPlayerInWater()) {
// As of 1.18.1, Java Edition cannot fly while in water, but it can fly while crawling
// If this isn't present, swimming on a 1.13.2 server and then attempting to fly will put you into a flying/swimming state that is invalid on JE
session.sendAdventureSettings();
return;
session.setFlying(isFlying);
ServerboundPlayerAbilitiesPacket abilitiesPacket = new ServerboundPlayerAbilitiesPacket(isFlying);
session.sendDownstreamPacket(abilitiesPacket);
}
session.setFlying(isFlying);
ServerboundPlayerAbilitiesPacket abilitiesPacket = new ServerboundPlayerAbilitiesPacket(isFlying);
session.sendDownstreamPacket(abilitiesPacket);
}
}

Datei anzeigen

@ -25,11 +25,8 @@
package org.geysermc.geyser.translator.protocol.bedrock.entity.player;
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.cloudburstmc.protocol.bedrock.packet.EmotePacket;
import org.geysermc.geyser.api.event.bedrock.ClientEmoteEvent;
import org.geysermc.geyser.configuration.EmoteOffhandWorkaroundOption;
import org.geysermc.geyser.entity.type.Entity;
import org.geysermc.geyser.session.GeyserSession;
@ -43,15 +40,20 @@ public class BedrockEmoteTranslator extends PacketTranslator<EmotePacket> {
public void translate(GeyserSession session, EmotePacket packet) {
if (session.getGeyser().getConfig().getEmoteOffhandWorkaround() != EmoteOffhandWorkaroundOption.DISABLED) {
// Activate the workaround - we should trigger the offhand now
ServerboundPlayerActionPacket swapHandsPacket = new ServerboundPlayerActionPacket(PlayerAction.SWAP_HANDS, Vector3i.ZERO,
Direction.DOWN, 0);
session.sendDownstreamPacket(swapHandsPacket);
session.requestOffhandSwap();
if (session.getGeyser().getConfig().getEmoteOffhandWorkaround() == EmoteOffhandWorkaroundOption.NO_EMOTES) {
return;
}
}
// For the future: could have a method that exposes which players will see the emote
ClientEmoteEvent event = new ClientEmoteEvent(session, packet.getEmoteId());
session.getGeyser().eventBus().fire(event);
if (event.isCancelled()) {
return;
}
int javaId = session.getPlayerEntity().getEntityId();
for (GeyserSession otherSession : session.getGeyser().getSessionManager().getSessions().values()) {
if (otherSession != session) {

Datei anzeigen

@ -80,21 +80,23 @@ public class BedrockInteractTranslator extends PacketTranslator<InteractPacket>
session.sendDownstreamPacket(sneakPacket);
Entity currentVehicle = session.getPlayerEntity().getVehicle();
session.setMountVehicleScheduledFuture(session.scheduleInEventLoop(() -> {
if (session.getPlayerEntity().getVehicle() == null) {
return;
}
if (currentVehicle != null) {
session.setMountVehicleScheduledFuture(session.scheduleInEventLoop(() -> {
if (session.getPlayerEntity().getVehicle() == null) {
return;
}
long vehicleBedrockId = currentVehicle.getGeyserId();
if (session.getPlayerEntity().getVehicle().getGeyserId() == vehicleBedrockId) {
// The Bedrock client, as of 1.19.51, dismounts on its end. The server may not agree with this.
// If the server doesn't agree with our dismount (sends a packet saying we dismounted),
// then remount the player.
SetEntityLinkPacket linkPacket = new SetEntityLinkPacket();
linkPacket.setEntityLink(new EntityLinkData(vehicleBedrockId, session.getPlayerEntity().getGeyserId(), EntityLinkData.Type.PASSENGER, true, false));
session.sendUpstreamPacket(linkPacket);
}
}, 1, TimeUnit.SECONDS));
long vehicleBedrockId = currentVehicle.getGeyserId();
if (session.getPlayerEntity().getVehicle().getGeyserId() == vehicleBedrockId) {
// The Bedrock client, as of 1.19.51, dismounts on its end. The server may not agree with this.
// If the server doesn't agree with our dismount (sends a packet saying we dismounted),
// then remount the player.
SetEntityLinkPacket linkPacket = new SetEntityLinkPacket();
linkPacket.setEntityLink(new EntityLinkData(vehicleBedrockId, session.getPlayerEntity().getGeyserId(), EntityLinkData.Type.PASSENGER, true, false));
session.sendUpstreamPacket(linkPacket);
}
}, 1, TimeUnit.SECONDS));
}
break;
case MOUSEOVER:
// Handle the buttons for mobile - "Mount", etc; and the suggestions for console - "ZL: Mount", etc

Datei anzeigen

@ -30,12 +30,6 @@ import com.github.steveice10.mc.protocol.data.game.command.CommandParser;
import com.github.steveice10.mc.protocol.data.game.command.properties.ResourceProperties;
import com.github.steveice10.mc.protocol.data.game.entity.attribute.AttributeType;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundCommandsPacket;
import org.cloudburstmc.protocol.bedrock.data.command.CommandData;
import org.cloudburstmc.protocol.bedrock.data.command.CommandEnumConstraint;
import org.cloudburstmc.protocol.bedrock.data.command.CommandEnumData;
import org.cloudburstmc.protocol.bedrock.data.command.CommandParam;
import org.cloudburstmc.protocol.bedrock.data.command.CommandParamData;
import org.cloudburstmc.protocol.bedrock.packet.AvailableCommandsPacket;
import it.unimi.dsi.fastutil.Hash;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
@ -46,6 +40,8 @@ import lombok.Getter;
import lombok.ToString;
import net.kyori.adventure.text.format.NamedTextColor;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.cloudburstmc.protocol.bedrock.data.command.*;
import org.cloudburstmc.protocol.bedrock.packet.AvailableCommandsPacket;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.event.java.ServerDefineCommandsEvent;
import org.geysermc.geyser.command.GeyserCommandManager;
@ -58,8 +54,8 @@ import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.geyser.util.EntityUtils;
import java.util.*;
import java.util.stream.Collectors;
@SuppressWarnings("removal") // We know. This is our doing.
@Translator(packet = ClientboundCommandsPacket.class)
public class JavaCommandsTranslator extends PacketTranslator<ClientboundCommandsPacket> {
@ -207,9 +203,10 @@ public class JavaCommandsTranslator extends PacketTranslator<ClientboundCommands
*/
private static CommandParamData[][] getParams(GeyserSession session, CommandNode commandNode, CommandNode[] allNodes) {
// Check if the command is an alias and redirect it
if (commandNode.getRedirectIndex() != -1) {
GeyserImpl.getInstance().getLogger().debug("Redirecting command " + commandNode.getName() + " to " + allNodes[commandNode.getRedirectIndex()].getName());
commandNode = allNodes[commandNode.getRedirectIndex()];
if (commandNode.getRedirectIndex().isPresent()) {
int redirectIndex = commandNode.getRedirectIndex().getAsInt();
GeyserImpl.getInstance().getLogger().debug("Redirecting command " + commandNode.getName() + " to " + allNodes[redirectIndex].getName());
commandNode = allNodes[redirectIndex];
}
if (commandNode.getChildIndices().length >= 1) {
@ -342,9 +339,7 @@ public class JavaCommandsTranslator extends PacketTranslator<ClientboundCommands
return teams;
}
return (teams = new CommandEnumData("Geyser_Teams",
Arrays.stream(session.getWorldCache().getScoreboard().getTeamNames())
.collect(Collectors.toMap(o -> o, o -> EnumSet.noneOf(CommandEnumConstraint.class), (o1, o2) -> o1, LinkedHashMap::new)),
true
session.getWorldCache().getScoreboard().getTeamNames(), true
));
}
}

Datei anzeigen

@ -35,10 +35,13 @@ import io.netty.buffer.Unpooled;
import org.geysermc.cumulus.Forms;
import org.geysermc.cumulus.form.Form;
import org.geysermc.cumulus.form.util.FormType;
import org.geysermc.erosion.Constants;
import org.geysermc.erosion.packet.ErosionPacket;
import org.geysermc.erosion.packet.Packets;
import org.geysermc.erosion.packet.geyserbound.GeyserboundPacket;
import org.geysermc.floodgate.pluginmessage.PluginMessageChannels;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.GeyserLogger;
import org.geysermc.geyser.api.network.AuthType;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
@ -51,82 +54,96 @@ public class JavaCustomPayloadTranslator extends PacketTranslator<ClientboundCus
@Override
public void translate(GeyserSession session, ClientboundCustomPayloadPacket packet) {
// The only plugin messages it has to listen for are Floodgate plugin messages
if (session.remoteServer().authType() != AuthType.FLOODGATE) {
String channel = packet.getChannel();
if (channel.equals(Constants.PLUGIN_MESSAGE)) {
ByteBuf buf = Unpooled.wrappedBuffer(packet.getData());
ErosionPacket<?> erosionPacket = Packets.decode(buf);
((GeyserboundPacket) erosionPacket).handle(session.getErosionHandler());
return;
}
String channel = packet.getChannel();
if (channel.equals(PluginMessageChannels.FORM)) {
byte[] data = packet.getData();
session.ensureInEventLoop(() -> {
byte[] data = packet.getData();
// receive: first byte is form type, second and third are the id, remaining is the form data
// respond: first and second byte id, remaining is form response data
// receive: first byte is form type, second and third are the id, remaining is the form data
// respond: first and second byte id, remaining is form response data
FormType type = FormType.fromOrdinal(data[0]);
if (type == null) {
throw new NullPointerException("Got type " + data[0] + " which isn't a valid form type!");
}
String dataString = new String(data, 3, data.length - 3, Charsets.UTF_8);
Form form = Forms.fromJson(dataString, type, (ignored, response) -> {
byte[] finalData;
if (response == null) {
// Response data can be null as of 1.19.20 (same behaviour as empty response data)
// Only need to send the form id
finalData = new byte[]{data[1], data[2]};
} else {
byte[] raw = response.getBytes(StandardCharsets.UTF_8);
finalData = new byte[raw.length + 2];
finalData[0] = data[1];
finalData[1] = data[2];
System.arraycopy(raw, 0, finalData, 2, raw.length);
FormType type = FormType.fromOrdinal(data[0]);
if (type == null) {
throw new NullPointerException("Got type " + data[0] + " which isn't a valid form type!");
}
session.sendDownstreamPacket(new ServerboundCustomPayloadPacket(channel, finalData));
String dataString = new String(data, 3, data.length - 3, Charsets.UTF_8);
Form form = Forms.fromJson(dataString, type, (ignored, response) -> {
byte[] finalData;
if (response == null) {
// Response data can be null as of 1.19.20 (same behaviour as empty response data)
// Only need to send the form id
finalData = new byte[]{data[1], data[2]};
} else {
byte[] raw = response.getBytes(StandardCharsets.UTF_8);
finalData = new byte[raw.length + 2];
finalData[0] = data[1];
finalData[1] = data[2];
System.arraycopy(raw, 0, finalData, 2, raw.length);
}
session.sendDownstreamPacket(new ServerboundCustomPayloadPacket(channel, finalData));
});
session.sendForm(form);
});
session.sendForm(form);
} else if (channel.equals(PluginMessageChannels.TRANSFER)) {
byte[] data = packet.getData();
session.ensureInEventLoop(() -> {
byte[] data = packet.getData();
// port (4 bytes), address (remaining data)
if (data.length < 5) {
throw new NullPointerException("Transfer data should be at least 5 bytes long");
}
// port (4 bytes), address (remaining data)
if (data.length < 5) {
throw new NullPointerException("Transfer data should be at least 5 bytes long");
}
int port = data[0] << 24 | (data[1] & 0xFF) << 16 | (data[2] & 0xFF) << 8 | data[3] & 0xFF;
String address = new String(data, 4, data.length - 4);
int port = data[0] << 24 | (data[1] & 0xFF) << 16 | (data[2] & 0xFF) << 8 | data[3] & 0xFF;
String address = new String(data, 4, data.length - 4);
if (logger.isDebug()) {
logger.info("Transferring client to: " + address + ":" + port);
}
if (logger.isDebug()) {
logger.info("Transferring client to: " + address + ":" + port);
}
TransferPacket transferPacket = new TransferPacket();
transferPacket.setAddress(address);
transferPacket.setPort(port);
session.sendUpstreamPacket(transferPacket);
TransferPacket transferPacket = new TransferPacket();
transferPacket.setAddress(address);
transferPacket.setPort(port);
session.sendUpstreamPacket(transferPacket);
});
} else if (channel.equals(PluginMessageChannels.PACKET)) {
logger.debug("A packet has been sent using the Floodgate api");
byte[] data = packet.getData();
session.ensureInEventLoop(() -> {
logger.debug("A packet has been sent using the Floodgate api");
byte[] data = packet.getData();
// packet id, packet data
if (data.length < 2) {
throw new IllegalStateException("Packet data should be at least 2 bytes long");
}
// packet id, packet data
if (data.length < 2) {
throw new IllegalStateException("Packet data should be at least 2 bytes long");
}
int packetId = data[0] & 0xFF;
ByteBuf packetData = Unpooled.wrappedBuffer(data, 1, data.length - 1);
int packetId = data[0] & 0xFF;
ByteBuf packetData = Unpooled.wrappedBuffer(data, 1, data.length - 1);
var toSend = new UnknownPacket();
toSend.setPacketId(packetId);
toSend.setPayload(packetData);
var toSend = new UnknownPacket();
toSend.setPacketId(packetId);
toSend.setPayload(packetData);
session.sendUpstreamPacket(toSend);
session.sendUpstreamPacket(toSend);
});
}
}
@Override
public boolean shouldExecuteInEventLoop() {
// For Erosion packets
return false;
}
}

Datei anzeigen

@ -47,4 +47,9 @@ public class JavaKeepAliveTranslator extends PacketTranslator<ClientboundKeepAli
latencyPacket.setTimestamp(packet.getPingId() * 1000);
session.sendUpstreamPacket(latencyPacket);
}
@Override
public boolean shouldExecuteInEventLoop() {
return false;
}
}

Datei anzeigen

@ -30,14 +30,13 @@ import com.github.steveice10.mc.protocol.packet.ingame.serverbound.ServerboundCu
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.IntTag;
import org.cloudburstmc.protocol.bedrock.data.GameRuleData;
import org.cloudburstmc.protocol.bedrock.data.PlayerPermission;
import org.cloudburstmc.protocol.bedrock.packet.AdventureSettingsPacket;
import org.cloudburstmc.protocol.bedrock.packet.GameRulesChangedPacket;
import org.cloudburstmc.protocol.bedrock.packet.SetPlayerGameTypePacket;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import org.geysermc.floodgate.pluginmessage.PluginMessageChannels;
import org.geysermc.geyser.api.network.AuthType;
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
import org.geysermc.geyser.erosion.GeyserboundHandshakePacketHandler;
import org.geysermc.geyser.level.JavaDimension;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.text.TextDecoration;
@ -59,6 +58,11 @@ public class JavaLoginTranslator extends PacketTranslator<ClientboundLoginPacket
SessionPlayerEntity entity = session.getPlayerEntity();
entity.setEntityId(packet.getEntityId());
if (session.getErosionHandler().isActive()) {
session.getErosionHandler().close();
session.setErosionHandler(new GeyserboundHandshakePacketHandler(session));
}
Map<String, JavaDimension> dimensions = session.getDimensions();
dimensions.clear();
@ -107,11 +111,6 @@ public class JavaLoginTranslator extends PacketTranslator<ClientboundLoginPacket
session.getUpstream().sendPostStartGamePackets();
}
AdventureSettingsPacket bedrockPacket = new AdventureSettingsPacket();
bedrockPacket.setUniqueEntityId(session.getPlayerEntity().getGeyserId());
bedrockPacket.setPlayerPermission(PlayerPermission.MEMBER);
session.sendUpstreamPacket(bedrockPacket);
if (!needsSpawnPacket) {
SetPlayerGameTypePacket playerGameTypePacket = new SetPlayerGameTypePacket();
playerGameTypePacket.setGamemode(packet.getGameMode().ordinal());
@ -136,6 +135,10 @@ public class JavaLoginTranslator extends PacketTranslator<ClientboundLoginPacket
session.sendDownstreamPacket(new ServerboundCustomPayloadPacket("minecraft:brand", PluginMessageUtils.getGeyserBrandData()));
// TODO don't send two packets
// if (true) {
// session.sendDownstreamPacket(new ServerboundCustomPayloadPacket("minecraft:register", Constants.PLUGIN_MESSAGE.getBytes(StandardCharsets.UTF_8)));
// }
// register the plugin messaging channels used in Floodgate
if (session.remoteServer().authType() == AuthType.FLOODGATE) {
session.sendDownstreamPacket(new ServerboundCustomPayloadPacket("minecraft:register", PluginMessageChannels.getFloodgateRegisterData()));

Datei anzeigen

@ -29,9 +29,9 @@ import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.mc.protocol.data.game.recipe.Ingredient;
import com.github.steveice10.mc.protocol.data.game.recipe.Recipe;
import com.github.steveice10.mc.protocol.data.game.recipe.RecipeType;
import com.github.steveice10.mc.protocol.data.game.recipe.data.LegacyUpgradeRecipeData;
import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapedRecipeData;
import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapelessRecipeData;
import com.github.steveice10.mc.protocol.data.game.recipe.data.SmithingRecipeData;
import com.github.steveice10.mc.protocol.data.game.recipe.data.StoneCuttingRecipeData;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundUpdateRecipesPacket;
import it.unimi.dsi.fastutil.ints.*;
@ -73,7 +73,7 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator<ClientboundUpd
/**
* Required to use the specified cartography table recipes
*/
private static final List<RecipeData> CARTOGRAPHY_RECIPES = Arrays.asList(
private static final List<RecipeData> CARTOGRAPHY_RECIPES = List.of(
MultiRecipeData.of(UUID.fromString("8b36268c-1829-483c-a0f1-993b7156a8f2"), ++LAST_RECIPE_NET_ID), // Map extending
MultiRecipeData.of(UUID.fromString("442d85ed-8272-4543-a6f1-418f90ded05d"), ++LAST_RECIPE_NET_ID), // Map cloning
MultiRecipeData.of(UUID.fromString("98c84b38-1085-46bd-b1ce-dd38c159e6cc"), ++LAST_RECIPE_NET_ID), // Map upgrading
@ -102,6 +102,10 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator<ClientboundUpd
// Strip NBT - tools won't appear in the recipe book otherwise
output = output.toBuilder().tag(null).build();
ItemDescriptorWithCount[][] inputCombinations = combinations(session, shapelessRecipeData.getIngredients());
if (inputCombinations == null) {
continue;
}
for (ItemDescriptorWithCount[] inputs : inputCombinations) {
UUID uuid = UUID.randomUUID();
craftingDataPacket.getCraftingData().add(org.cloudburstmc.protocol.bedrock.data.inventory.crafting.recipe.ShapelessRecipeData.shapeless(uuid.toString(),
@ -119,6 +123,9 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator<ClientboundUpd
// See above
output = output.toBuilder().tag(null).build();
ItemDescriptorWithCount[][] inputCombinations = combinations(session, shapedRecipeData.getIngredients());
if (inputCombinations == null) {
continue;
}
for (ItemDescriptorWithCount[] inputs : inputCombinations) {
UUID uuid = UUID.randomUUID();
craftingDataPacket.getCraftingData().add(org.cloudburstmc.protocol.bedrock.data.inventory.crafting.recipe.ShapedRecipeData.shaped(uuid.toString(),
@ -140,7 +147,7 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator<ClientboundUpd
}
case SMITHING -> {
// Required to translate these as of 1.18.10, or else they cannot be crafted
SmithingRecipeData recipeData = (SmithingRecipeData) recipe.getData();
LegacyUpgradeRecipeData recipeData = (LegacyUpgradeRecipeData) recipe.getData();
ItemData output = ItemTranslator.translateToBedrock(session, recipeData.getResult());
for (ItemStack base : recipeData.getBase().getOptions()) {
ItemDescriptorWithCount bedrockBase = ItemDescriptorWithCount.fromItem(ItemTranslator.translateToBedrock(session, base));
@ -170,7 +177,7 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator<ClientboundUpd
}
}
craftingDataPacket.getCraftingData().addAll(CARTOGRAPHY_RECIPES);
craftingDataPacket.getPotionMixData().addAll(Registries.POTION_MIXES.get());
craftingDataPacket.getPotionMixData().addAll(Registries.POTION_MIXES.forVersion(session.getUpstream().getProtocolVersion()));
Int2ObjectMap<GeyserStonecutterData> stonecutterRecipeMap = new Int2ObjectOpenHashMap<>();
for (Int2ObjectMap.Entry<List<StoneCuttingRecipeData>> data : unsortedStonecutterData.int2ObjectEntrySet()) {
@ -219,12 +226,14 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator<ClientboundUpd
* @return the Java ingredient list as an array that Bedrock can understand
*/
private ItemDescriptorWithCount[][] combinations(GeyserSession session, Ingredient[] ingredients) {
boolean empty = true;
Map<Set<ItemDescriptorWithCount>, IntSet> squashedOptions = new HashMap<>();
for (int i = 0; i < ingredients.length; i++) {
if (ingredients[i].getOptions().length == 0) {
squashedOptions.computeIfAbsent(Collections.singleton(ItemDescriptorWithCount.EMPTY), k -> new IntOpenHashSet()).add(i);
continue;
}
empty = false;
Ingredient ingredient = ingredients[i];
Map<GroupedItem, List<ItemDescriptorWithCount>> groupedByIds = Arrays.stream(ingredient.getOptions())
.map(item -> ItemDescriptorWithCount.fromItem(ItemTranslator.translateToBedrock(session, item)))
@ -252,6 +261,11 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator<ClientboundUpd
}
squashedOptions.computeIfAbsent(optionSet, k -> new IntOpenHashSet()).add(i);
}
if (empty) {
// Crashes Bedrock 1.19.70 otherwise
// Fixes https://github.com/GeyserMC/Geyser/issues/3549
return null;
}
int totalCombinations = 1;
for (Set<ItemDescriptorWithCount> optionSet : squashedOptions.keySet()) {
totalCombinations *= optionSet.size();

Datei anzeigen

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
* 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
@ -23,20 +23,30 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.translator.protocol.bedrock;
package org.geysermc.geyser.translator.protocol.java.entity;
import org.cloudburstmc.protocol.bedrock.data.AdventureSetting;
import org.cloudburstmc.protocol.bedrock.packet.AdventureSettingsPacket;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.entity.ClientboundDamageEventPacket;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityEventType;
import org.cloudburstmc.protocol.bedrock.packet.EntityEventPacket;
import org.geysermc.geyser.entity.type.Entity;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
@Translator(packet = AdventureSettingsPacket.class)
public class BedrockAdventureSettingsTranslator extends PacketTranslator<AdventureSettingsPacket> {
@Translator(packet = ClientboundDamageEventPacket.class)
public class JavaDamageEventTranslator extends PacketTranslator<ClientboundDamageEventPacket> {
@Override
public void translate(GeyserSession session, AdventureSettingsPacket packet) {
boolean isFlying = packet.getSettings().contains(AdventureSetting.FLYING);
BedrockRequestAbilityTranslator.handle(session, isFlying);
public void translate(GeyserSession session, ClientboundDamageEventPacket packet) {
Entity entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
if (entity == null) {
return;
}
// We can probably actually map damage types.
EntityEventPacket entityEventPacket = new EntityEventPacket();
entityEventPacket.setRuntimeEntityId(entity.getGeyserId());
entityEventPacket.setType(EntityEventType.HURT);
session.sendUpstreamPacket(entityEventPacket);
}
}

Datei anzeigen

@ -88,15 +88,6 @@ public class JavaEntityEventTranslator extends PacketTranslator<ClientboundEntit
session.sendAdventureSettings();
return;
// EntityEventType.HURT sends extra data depending on the type of damage. However this appears to have no visual changes
case LIVING_BURN:
case LIVING_DROWN:
case LIVING_HURT:
case LIVING_HURT_SWEET_BERRY_BUSH:
case LIVING_HURT_THORNS:
case LIVING_FREEZE:
entityEventPacket.setType(EntityEventType.HURT);
break;
case LIVING_DEATH:
entityEventPacket.setType(EntityEventType.DEATH);
if (entity.getDefinition() == EntityDefinitions.EGG) {

Datei anzeigen

@ -91,6 +91,16 @@ public class JavaSetPassengersTranslator extends PacketTranslator<ClientboundSet
EntityUtils.updateMountOffset(passenger, entity, false, false, (packet.getPassengerIds().length > 1));
// Force an update to the passenger metadata
passenger.updateBedrockMetadata();
if (passenger == session.getPlayerEntity()) {
//TODO test
if (session.getMountVehicleScheduledFuture() != null) {
// Cancel this task as it is now unnecessary.
// Note that this isn't present in JavaSetPassengersTranslator as that code is not called for players
// as of Java 1.19.3, but the scheduled future checks for the vehicle being null anyway.
session.getMountVehicleScheduledFuture().cancel(false);
}
}
}
}

Datei anzeigen

@ -30,20 +30,16 @@ import com.github.steveice10.mc.protocol.packet.ingame.clientbound.entity.player
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.level.ServerboundAcceptTeleportationPacket;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosRotPacket;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityLinkData;
import org.cloudburstmc.protocol.bedrock.packet.ChunkRadiusUpdatedPacket;
import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket;
import org.cloudburstmc.protocol.bedrock.packet.RespawnPacket;
import org.cloudburstmc.protocol.bedrock.packet.SetEntityLinkPacket;
import org.geysermc.geyser.entity.EntityDefinitions;
import org.geysermc.geyser.entity.type.Entity;
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.TeleportCache;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.geyser.util.ChunkUtils;
import org.geysermc.geyser.util.EntityUtils;
@Translator(packet = ClientboundPlayerPositionPacket.class)
public class JavaPlayerPositionTranslator extends PacketTranslator<ClientboundPlayerPositionPacket> {
@ -101,27 +97,6 @@ public class JavaPlayerPositionTranslator extends PacketTranslator<ClientboundPl
return;
}
Entity vehicle;
if (packet.isDismountVehicle() && (vehicle = session.getPlayerEntity().getVehicle()) != null) {
SetEntityLinkPacket linkPacket = new SetEntityLinkPacket();
linkPacket.setEntityLink(new EntityLinkData(vehicle.getGeyserId(), entity.getGeyserId(), EntityLinkData.Type.REMOVE, false, false));
session.sendUpstreamPacket(linkPacket);
vehicle.getPassengers().remove(entity);
session.getPlayerEntity().setVehicle(null);
EntityUtils.updateRiderRotationLock(entity, null, false);
EntityUtils.updateMountOffset(entity, null, false, false, entity.getPassengers().size() > 1);
entity.updateBedrockMetadata();
if (session.getMountVehicleScheduledFuture() != null) {
// Cancel this task as it is now unnecessary.
// Note that this isn't present in JavaSetPassengersTranslator as that code is not called for players
// as of Java 1.19.3, but the scheduled future checks for the vehicle being null anyway.
session.getMountVehicleScheduledFuture().cancel(false);
}
}
// If coordinates are relative, then add to the existing coordinate
double newX = packet.getX() +
(packet.getRelative().contains(PositionElement.X) ? entity.getPosition().getX() : 0);

Datei anzeigen

@ -58,15 +58,16 @@ public class JavaBlockEventTranslator extends PacketTranslator<ClientboundBlockE
blockEventPacket.setEventType(1);
session.sendUpstreamPacket(blockEventPacket);
} else if (packet.getValue() instanceof NoteBlockValue) {
int blockState = session.getGeyser().getWorldManager().getBlockAt(session, position);
blockEventPacket.setEventData(BlockStateValues.getNoteblockPitch(blockState));
session.sendUpstreamPacket(blockEventPacket);
session.getGeyser().getWorldManager().getBlockAtAsync(session, position).thenAccept(blockState -> {
blockEventPacket.setEventData(BlockStateValues.getNoteblockPitch(blockState));
session.sendUpstreamPacket(blockEventPacket);
});
} else if (packet.getValue() instanceof PistonValue pistonValue) {
PistonValueType action = (PistonValueType) packet.getType();
Direction direction = Direction.fromPistonValue(pistonValue);
Direction direction = Direction.fromPistonValue(pistonValue.getDirection());
PistonCache pistonCache = session.getPistonCache();
if (session.getGeyser().getPlatformType() == PlatformType.SPIGOT) {
if (session.getGeyser().getPlatformType() == PlatformType.SPIGOT || session.getErosionHandler().isActive()) {
// Mostly handled in the GeyserPistonEvents class
// Retracting sticky pistons is an exception, since the event is not called on Spigot from 1.13.2 - 1.17.1
// See https://github.com/PaperMC/Paper/blob/6fa1983e9ce177a4a412d5b950fd978620174777/patches/server/0304-Fire-BlockPistonRetractEvent-for-all-empty-pistons.patch
@ -103,7 +104,7 @@ public class JavaBlockEventTranslator extends PacketTranslator<ClientboundBlockE
} else if (packet.getValue() instanceof EndGatewayValue) {
blockEventPacket.setEventType(1);
session.sendUpstreamPacket(blockEventPacket);
} else if (packet.getValue() instanceof GenericBlockValue bellValue && packet.getBlockId() == BlockStateValues.JAVA_BELL_ID) {
} else if (packet.getValue() instanceof BellValue bellValue) {
// Bells - needed to show ring from other players
BlockEntityDataPacket blockEntityPacket = new BlockEntityDataPacket();
blockEntityPacket.setBlockPosition(position);
@ -113,11 +114,12 @@ public class JavaBlockEventTranslator extends PacketTranslator<ClientboundBlockE
builder.putInt("y", position.getY());
builder.putInt("z", position.getZ());
builder.putString("id", "Bell");
int bedrockRingDirection = switch (bellValue.getValue()) {
case 3 -> 0; // north
case 4 -> 1; // east
case 5 -> 3;// west
default -> bellValue.getValue(); // south (2) is identical
int bedrockRingDirection = switch (bellValue.getDirection()) {
case SOUTH -> 0;
case WEST -> 1;
case NORTH -> 2;
case EAST -> 3;
default -> throw new IllegalStateException("Unexpected BellValue Direction: " + bellValue.getDirection());
};
builder.putInt("Direction", bedrockRingDirection);
builder.putByte("Ringing", (byte) 1);

Datei anzeigen

@ -43,7 +43,7 @@ public class JavaBlockUpdateTranslator extends PacketTranslator<ClientboundBlock
public void translate(GeyserSession session, ClientboundBlockUpdatePacket packet) {
Vector3i pos = packet.getEntry().getPosition();
boolean updatePlacement = session.getGeyser().getPlatformType() != PlatformType.SPIGOT && // Spigot simply listens for the block place event
session.getGeyser().getWorldManager().getBlockAt(session, pos) != packet.getEntry().getBlock();
!session.getErosionHandler().isActive() && session.getGeyser().getWorldManager().getBlockAt(session, pos) != packet.getEntry().getBlock();
session.getWorldCache().updateServerCorrectBlockState(pos, packet.getEntry().getBlock());
if (updatePlacement) {
this.checkPlace(session, packet);

Datei anzeigen

@ -52,7 +52,7 @@ public class JavaForgetLevelChunkTranslator extends PacketTranslator<Clientbound
}
removedSkulls.forEach(session.getSkullCache()::removeSkull);
if (!session.getGeyser().getWorldManager().shouldExpectLecternHandled()) {
if (!session.getGeyser().getWorldManager().shouldExpectLecternHandled(session)) {
// Do the same thing with lecterns
Iterator<Vector3i> iterator = session.getLecternCache().iterator();
while (iterator.hasNext()) {

Datei anzeigen

@ -48,21 +48,23 @@ import org.geysermc.geyser.translator.protocol.Translator;
@Translator(packet = ClientboundGameEventPacket.class)
public class JavaGameEventTranslator extends PacketTranslator<ClientboundGameEventPacket> {
// Strength of rainstorms and thunderstorms is a 0-1 float on Java, while on Bedrock it is a 0-65535 int
private static final int MAX_STORM_STRENGTH = 65535;
@Override
public void translate(GeyserSession session, ClientboundGameEventPacket packet) {
PlayerEntity entity = session.getPlayerEntity();
switch (packet.getNotification()) {
// Yes, START_RAIN and STOP_RAIN are swapped in terms of what they cause the client to do.
// This is how the Mojang mappings name them, so we go with it
// It seems Mojang's intent was that START_RAIN would set the rain strength to 0 so that it can then be incremeneted on a gradient by the server
// The inverse is true for STOP_RAIN
// This is indeed the behavior of the vanilla server
// However, it seems most server software (at least Spigot and Paper) did not go along with this
// As a result many developers use these packets for the opposite of what their names implies
// Behavior last verified with Java 1.19.4 and Bedrock 1.19.71
case START_RAIN:
LevelEventPacket startRainPacket = new LevelEventPacket();
startRainPacket.setType(LevelEvent.START_RAINING);
startRainPacket.setData(Integer.MAX_VALUE);
startRainPacket.setPosition(Vector3f.ZERO);
session.sendUpstreamPacket(startRainPacket);
session.setRaining(true);
break;
case STOP_RAIN:
LevelEventPacket stopRainPacket = new LevelEventPacket();
stopRainPacket.setType(LevelEvent.STOP_RAINING);
stopRainPacket.setData(0);
@ -70,34 +72,35 @@ public class JavaGameEventTranslator extends PacketTranslator<ClientboundGameEve
session.sendUpstreamPacket(stopRainPacket);
session.setRaining(false);
break;
case STOP_RAIN:
LevelEventPacket startRainPacket = new LevelEventPacket();
startRainPacket.setType(LevelEvent.START_RAINING);
startRainPacket.setData(MAX_STORM_STRENGTH);
startRainPacket.setPosition(Vector3f.ZERO);
session.sendUpstreamPacket(startRainPacket);
session.setRaining(true);
break;
case RAIN_STRENGTH:
// While the above values are used, they CANNOT BE TRUSTED on a vanilla server as they are swapped around
// Spigot and forks implement it correctly
// Rain strength is your best way for determining if there is any rain
RainStrengthValue value = (RainStrengthValue) packet.getValue();
boolean isCurrentlyRaining = value.getStrength() > 0f;
// Java sends the rain level. Bedrock doesn't care, so we don't care if it's already raining.
if (isCurrentlyRaining != session.isRaining()) {
LevelEventPacket changeRainPacket = new LevelEventPacket();
changeRainPacket.setType(isCurrentlyRaining ? LevelEvent.START_RAINING : LevelEvent.STOP_RAINING);
changeRainPacket.setData(Integer.MAX_VALUE); // Dunno what this does; used to be implemented with ThreadLocalRandom
changeRainPacket.setPosition(Vector3f.ZERO);
session.sendUpstreamPacket(changeRainPacket);
session.setRaining(isCurrentlyRaining);
}
float rainStrength = ((RainStrengthValue) packet.getValue()).getStrength();
boolean isCurrentlyRaining = rainStrength > 0f;
LevelEventPacket changeRainPacket = new LevelEventPacket();
changeRainPacket.setType(isCurrentlyRaining ? LevelEvent.START_RAINING : LevelEvent.STOP_RAINING);
// This is the rain strength on LevelEventType.START_RAINING, but can be any value on LevelEventType.STOP_RAINING
changeRainPacket.setData((int) (rainStrength * MAX_STORM_STRENGTH));
changeRainPacket.setPosition(Vector3f.ZERO);
session.sendUpstreamPacket(changeRainPacket);
session.setRaining(isCurrentlyRaining);
break;
case THUNDER_STRENGTH:
// See above, same process
ThunderStrengthValue thunderValue = (ThunderStrengthValue) packet.getValue();
boolean isCurrentlyThundering = thunderValue.getStrength() > 0f;
if (isCurrentlyThundering != session.isThunder()) {
LevelEventPacket changeThunderPacket = new LevelEventPacket();
changeThunderPacket.setType(isCurrentlyThundering ? LevelEvent.START_THUNDERSTORM : LevelEvent.STOP_THUNDERSTORM);
changeThunderPacket.setData(Integer.MAX_VALUE);
changeThunderPacket.setPosition(Vector3f.ZERO);
session.sendUpstreamPacket(changeThunderPacket);
session.setThunder(isCurrentlyThundering);
}
float thunderStrength = ((ThunderStrengthValue) packet.getValue()).getStrength();
boolean isCurrentlyThundering = thunderStrength > 0f;
LevelEventPacket changeThunderPacket = new LevelEventPacket();
changeThunderPacket.setType(isCurrentlyThundering ? LevelEvent.START_THUNDERSTORM : LevelEvent.STOP_THUNDERSTORM);
changeThunderPacket.setData((int) (thunderStrength * MAX_STORM_STRENGTH));
changeThunderPacket.setPosition(Vector3f.ZERO);
session.sendUpstreamPacket(changeThunderPacket);
session.setThunder(isCurrentlyThundering);
break;
case CHANGE_GAMEMODE:
GameMode gameMode = (GameMode) packet.getValue();

Datei anzeigen

@ -35,11 +35,6 @@ import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityInfo;
import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityType;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.level.ClientboundLevelChunkWithLightPacket;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.nbt.NBTOutputStream;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtUtils;
import org.cloudburstmc.protocol.bedrock.packet.LevelChunkPacket;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.ByteBufOutputStream;
@ -48,6 +43,13 @@ import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.ints.IntLists;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.nbt.NBTOutputStream;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.cloudburstmc.nbt.NbtUtils;
import org.cloudburstmc.protocol.bedrock.packet.LevelChunkPacket;
import org.geysermc.erosion.util.LecternUtils;
import org.geysermc.geyser.entity.type.ItemFrameEntity;
import org.geysermc.geyser.level.BedrockDimension;
import org.geysermc.geyser.level.block.BlockStateValues;
@ -94,6 +96,7 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator<Clientbo
final BlockEntityInfo[] blockEntities = packet.getBlockEntities();
final List<NbtMap> bedrockBlockEntities = new ObjectArrayList<>(blockEntities.length);
final List<BlockEntityInfo> lecterns = new ObjectArrayList<>();
BitSet waterloggedPaletteIds = new BitSet();
BitSet bedrockOnlyBlockEntityIds = new BitSet();
@ -239,7 +242,9 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator<Clientbo
sections[bedrockSectionY] = new GeyserChunkSection(layers);
}
session.getChunkCache().addToCache(packet.getX(), packet.getZ(), javaChunks);
if (!session.getErosionHandler().isActive()) {
session.getChunkCache().addToCache(packet.getX(), packet.getZ(), javaChunks);
}
final int chunkBlockX = packet.getX() << 4;
final int chunkBlockZ = packet.getZ() << 4;
@ -261,8 +266,15 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator<Clientbo
if (type == BlockEntityType.LECTERN && BlockStateValues.getLecternBookStates().get(blockState)) {
// If getLecternBookStates is false, let's just treat it like a normal block entity
bedrockBlockEntities.add(session.getGeyser().getWorldManager().getLecternDataAt(
session, x + chunkBlockX, y, z + chunkBlockZ, true));
// Fill in tag with a default value
NbtMapBuilder lecternTag = LecternUtils.getBaseLecternTag(x + chunkBlockX, y, z + chunkBlockZ, 1);
lecternTag.putCompound("book", NbtMap.builder()
.putByte("Count", (byte) 1)
.putShort("Damage", (short) 0)
.putString("Name", "minecraft:written_book").build());
lecternTag.putInt("page", -1);
bedrockBlockEntities.add(lecternTag.build());
lecterns.add(blockEntity);
continue;
}
@ -353,6 +365,10 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator<Clientbo
levelChunkPacket.setData(byteBuf.retain());
session.sendUpstreamPacket(levelChunkPacket);
if (!lecterns.isEmpty()) {
session.getGeyser().getWorldManager().sendLecternData(session, packet.getX(), packet.getZ(), lecterns);
}
for (Map.Entry<Vector3i, ItemFrameEntity> entry : session.getItemFrameCache().entrySet()) {
Vector3i position = entry.getKey();
if ((position.getX() >> 4) == packet.getX() && (position.getZ() >> 4) == packet.getZ()) {

Datei anzeigen

@ -159,7 +159,7 @@ public class JavaLevelEventTranslator extends PacketTranslator<ClientboundLevelE
SmokeEventData smokeEventData = (SmokeEventData) packet.getData();
int data = 0;
switch (smokeEventData) {
switch (smokeEventData.getDirection()) {
case DOWN -> {
data = 4;
pos = pos.add(0, -0.9f, 0);

Datei anzeigen

@ -30,6 +30,7 @@ import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.Unpooled;
import it.unimi.dsi.fastutil.ints.IntLists;
import lombok.experimental.UtilityClass;
import org.cloudburstmc.math.GenericMath;
import org.cloudburstmc.math.vector.Vector2i;
import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.protocol.bedrock.data.defintions.BlockDefinition;
@ -92,7 +93,9 @@ public class ChunkUtils {
if (chunkPos == null || !chunkPos.equals(newChunkPos)) {
NetworkChunkPublisherUpdatePacket chunkPublisherUpdatePacket = new NetworkChunkPublisherUpdatePacket();
chunkPublisherUpdatePacket.setPosition(position);
chunkPublisherUpdatePacket.setRadius(session.getServerRenderDistance() << 4);
// Mitigates chunks not loading on 1.17.1 Paper and 1.19.3 Fabric. As of Bedrock 1.19.60.
// https://github.com/GeyserMC/Geyser/issues/3490
chunkPublisherUpdatePacket.setRadius(GenericMath.ceil((session.getServerRenderDistance() + 1) * MathUtils.SQRT_OF_TWO) << 4);
session.sendUpstreamPacket(chunkPublisherUpdatePacket);
session.setLastChunkPosition(newChunkPos);

Datei anzeigen

@ -97,6 +97,7 @@ public class InventoryUtils {
if (openInv != null && openInv.getJavaId() == inventory.getJavaId()) {
translator.openInventory(session, inventory);
translator.updateInventory(session, inventory);
openInv.setDisplayed(true);
} else if (openInv != null && openInv.isPending()) {
// Presumably, this inventory is no longer relevant, and the client doesn't care about it
displayInventory(session, openInv);
@ -105,6 +106,7 @@ public class InventoryUtils {
} else {
translator.openInventory(session, inventory);
translator.updateInventory(session, inventory);
inventory.setDisplayed(true);
}
} else {
session.setOpenInventory(null);
@ -119,7 +121,7 @@ public class InventoryUtils {
if (inventory != null) {
InventoryTranslator translator = session.getInventoryTranslator();
translator.closeInventory(session, inventory);
if (confirm && !inventory.isPending() && !(translator instanceof LecternInventoryTranslator)) {
if (confirm && inventory.isDisplayed() && !inventory.isPending() && !(translator instanceof LecternInventoryTranslator)) {
session.setClosingInventory(true);
}
}

Datei anzeigen

@ -165,18 +165,14 @@ public class LoginEncryptionUtils {
data.setOriginalString(clientData);
session.setClientData(data);
if (EncryptionUtils.canUseEncryption()) {
try {
LoginEncryptionUtils.startEncryptionHandshake(session, identityPublicKey);
} catch (Throwable e) {
// An error can be thrown on older Java 8 versions about an invalid key
if (geyser.getConfig().isDebugMode()) {
e.printStackTrace();
}
sendEncryptionFailedMessage(geyser);
try {
LoginEncryptionUtils.startEncryptionHandshake(session, identityPublicKey);
} catch (Throwable e) {
// An error can be thrown on older Java 8 versions about an invalid key
if (geyser.getConfig().isDebugMode()) {
e.printStackTrace();
}
} else {
sendEncryptionFailedMessage(geyser);
}
} catch (Exception ex) {

Datei anzeigen

@ -38,10 +38,13 @@ import org.geysermc.geyser.command.GeyserCommandSource;
import org.geysermc.geyser.network.GameProtocol;
import org.geysermc.geyser.text.GeyserLocale;
import javax.annotation.Nonnull;
import java.util.OptionalInt;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
public final class VersionCheckUtils {
private static @Nonnull OptionalInt LATEST_BEDROCK_RELEASE = OptionalInt.empty();
public static void checkForOutdatedFloodgate(GeyserLogger logger) {
try {
@ -61,10 +64,12 @@ public final class VersionCheckUtils {
JsonNode bedrock = json.get("bedrock").get("protocol");
int protocolVersion = bedrock.get("id").asInt();
if (GameProtocol.getBedrockCodec(protocolVersion) != null) {
LATEST_BEDROCK_RELEASE = OptionalInt.empty();
// We support the latest version! No need to print a message.
return;
}
LATEST_BEDROCK_RELEASE = OptionalInt.of(protocolVersion);
final String newBedrockVersion = bedrock.get("name").asText();
// Delayed for two reasons: save unnecessary processing, and wait to load locale if this is on join.
@ -89,6 +94,10 @@ public final class VersionCheckUtils {
});
}
public static @Nonnull OptionalInt getLatestBedrockRelease() {
return LATEST_BEDROCK_RELEASE;
}
private VersionCheckUtils() {
}
}

Datei anzeigen

@ -91,7 +91,7 @@ public class WebUtils {
InputStream in = con.getInputStream();
Files.copy(in, Paths.get(fileLocation), StandardCopyOption.REPLACE_EXISTING);
} catch (Exception e) {
throw new AssertionError("Unable to download and save file: " + fileLocation + " (" + reqURL + ")", e);
throw new RuntimeException("Unable to download and save file: " + fileLocation + " (" + reqURL + ")", e);
}
}

Datei anzeigen

@ -27,9 +27,9 @@ package org.geysermc.geyser.util.collection;
import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.nbt.NbtMap;
import org.geysermc.erosion.util.LecternUtils;
import org.geysermc.geyser.level.WorldManager;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.inventory.LecternInventoryTranslator;
import org.geysermc.geyser.util.BlockEntityUtils;
/**
@ -47,27 +47,24 @@ public class LecternHasBookMap extends FixedInt2BooleanMap {
int offset = blockState - this.start;
if (offset < 0 || offset >= this.value.length) {
// Block state is out of bounds of this map - lectern has been destroyed, if it existed
if (!worldManager.shouldExpectLecternHandled()) {
if (!worldManager.shouldExpectLecternHandled(session)) {
session.getLecternCache().remove(position);
}
return;
}
boolean newLecternHasBook;
if (worldManager.shouldExpectLecternHandled()) {
// As of right now, no tag can be added asynchronously
worldManager.getLecternDataAt(session, position.getX(), position.getY(), position.getZ(), false);
if (worldManager.shouldExpectLecternHandled(session)) {
worldManager.sendLecternData(session, position.getX(), position.getY(), position.getZ());
} else if ((newLecternHasBook = this.value[offset]) != this.get(worldManager.getBlockAt(session, position))) {
// If the lectern block was updated, or it previously had a book
NbtMap newLecternTag;
// newLecternHasBook = the new lectern block state's "has book" toggle.
if (newLecternHasBook) {
newLecternTag = worldManager.getLecternDataAt(session, position.getX(), position.getY(), position.getZ(), false);
worldManager.sendLecternData(session, position.getX(), position.getY(), position.getZ());
} else {
session.getLecternCache().remove(position);
newLecternTag = LecternInventoryTranslator.getBaseLecternTag(position.getX(), position.getY(), position.getZ(), 0).build();
NbtMap newLecternTag = LecternUtils.getBaseLecternTag(position.getX(), position.getY(), position.getZ(), 0).build();
BlockEntityUtils.updateBlockEntity(session, newLecternTag, position);
}
BlockEntityUtils.updateBlockEntity(session, newLecternTag, position);
}
}
}

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