mirror of
https://github.com/GeyserMC/Geyser.git
synced 2025-03-14 11:43:48 +01:00
Merge remote-tracking branch 'origin/master' into feature/protocol-3.0
This commit is contained in:
commit
d9811d08e3
110 changed files with 12141 additions and 1039 deletions
130
.github/workflows/build.yml
vendored
Normal file
130
.github/workflows/build.yml
vendored
Normal file
|
@ -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 }}
|
82
.github/workflows/pullrequest.yml
vendored
82
.github/workflows/pullrequest.yml
vendored
|
@ -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
|
||||
|
|
|
@ -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
71
Jenkinsfile
vendored
|
@ -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') {
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
<img src="https://geysermc.org/img/geyser-1760-860.png" alt="Geyser" width="600"/>
|
||||
|
||||
[](https://java.com/)
|
||||
|
||||
[](LICENSE)
|
||||
[](https://ci.opencollab.dev/job/GeyserMC/job/Geyser/job/master/)
|
||||
[](https://discord.gg/geysermc)
|
||||
[](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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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:.*"))
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -24,6 +24,11 @@ tasks {
|
|||
exclude(dependency(string))
|
||||
}
|
||||
}
|
||||
|
||||
sJar.dependencies {
|
||||
exclude(dependency("org.checkerframework:checker-qual:.*"))
|
||||
exclude(dependency("org.jetbrains:annotations:.*"))
|
||||
}
|
||||
}
|
||||
}
|
||||
named("build") {
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -83,8 +83,6 @@ public interface GeyserConfiguration {
|
|||
|
||||
boolean isDisableBedrockScaffolding();
|
||||
|
||||
boolean isAlwaysQuickChangeArmor();
|
||||
|
||||
EmoteOffhandWorkaroundOption getEmoteOffhandWorkaround();
|
||||
|
||||
String getDefaultLocale();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 ?
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,4 +47,9 @@ public class JavaKeepAliveTranslator extends PacketTranslator<ClientboundKeepAli
|
|||
latencyPacket.setTimestamp(packet.getPingId() * 1000);
|
||||
session.sendUpstreamPacket(latencyPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldExecuteInEventLoop() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()));
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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() {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue