3
0
Mirror von https://github.com/GeyserMC/Geyser.git synchronisiert 2024-11-20 06:50:09 +01:00

Merge remote-tracking branch 'refs/remotes/upstream/master' into subpacks-rewrite-merge-urlpacks

# Conflicts:
#	core/src/main/java/org/geysermc/geyser/item/enchantment/Enchantment.java
Dieser Commit ist enthalten in:
onebeastchris 2024-09-19 18:46:53 +08:00
Commit 6d4ae9680f
81 geänderte Dateien mit 14230 neuen und 395 gelöschten Zeilen

Datei anzeigen

@ -0,0 +1,3 @@
plugins {
id("geyser.base-conventions")
}

Datei anzeigen

@ -1,3 +1,8 @@
plugins {
id("geyser.platform-conventions")
id("geyser.modrinth-uploading-conventions")
}
dependencies { dependencies {
api(projects.core) api(projects.core)

Datei anzeigen

@ -111,6 +111,21 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
if (geyser == null) { if (geyser == null) {
return; // Config did not load properly! return; // Config did not load properly!
} }
// After Geyser initialize for parity with other platforms.
var sourceConverter = new CommandSourceConverter<>(
CommandSender.class,
id -> getProxy().getPlayer(id),
() -> getProxy().getConsole(),
BungeeCommandSource::new
);
CommandManager<GeyserCommandSource> cloud = new BungeeCommandManager<>(
this,
ExecutionCoordinator.simpleCoordinator(),
sourceConverter
);
this.commandRegistry = new CommandRegistry(geyser, cloud, false); // applying root permission would be a breaking change because we can't register permission defaults
// Big hack - Bungee does not provide us an event to listen to, so schedule a repeating // Big hack - Bungee does not provide us an event to listen to, so schedule a repeating
// task that waits for a field to be filled which is set after the plugin enable // task that waits for a field to be filled which is set after the plugin enable
// process is complete // process is complete
@ -150,19 +165,6 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
} }
this.geyserLogger.setDebug(geyserConfig.isDebugMode()); this.geyserLogger.setDebug(geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
} else {
var sourceConverter = new CommandSourceConverter<>(
CommandSender.class,
id -> getProxy().getPlayer(id),
() -> getProxy().getConsole(),
BungeeCommandSource::new
);
CommandManager<GeyserCommandSource> cloud = new BungeeCommandManager<>(
this,
ExecutionCoordinator.simpleCoordinator(),
sourceConverter
);
this.commandRegistry = new CommandRegistry(geyser, cloud, false); // applying root permission would be a breaking change because we can't register permission defaults
} }
// Force-disable query if enabled, or else Geyser won't enable // Force-disable query if enabled, or else Geyser won't enable

Datei anzeigen

@ -1,3 +1,7 @@
plugins {
id("geyser.modded-conventions")
}
architectury { architectury {
common("neoforge", "fabric") common("neoforge", "fabric")
} }

Datei anzeigen

@ -1,3 +1,8 @@
plugins {
id("geyser.modded-conventions")
id("geyser.modrinth-uploading-conventions")
}
architectury { architectury {
platformSetupLoomIde() platformSetupLoomIde()
fabric() fabric()

Datei anzeigen

@ -1,13 +1,18 @@
// This is provided by "org.cloudburstmc.math.mutable" too, so yeet. plugins {
// NeoForge's class loader is *really* annoying. id("geyser.modded-conventions")
provided("org.cloudburstmc.math", "api") id("geyser.modrinth-uploading-conventions")
provided("com.google.errorprone", "error_prone_annotations") }
architectury { architectury {
platformSetupLoomIde() platformSetupLoomIde()
neoForge() neoForge()
} }
// This is provided by "org.cloudburstmc.math.mutable" too, so yeet.
// NeoForge's class loader is *really* annoying.
provided("org.cloudburstmc.math", "api")
provided("com.google.errorprone", "error_prone_annotations")
val includeTransitive: Configuration = configurations.getByName("includeTransitive") val includeTransitive: Configuration = configurations.getByName("includeTransitive")
dependencies { dependencies {

Datei anzeigen

@ -82,6 +82,7 @@ public class GeyserNeoForgeBootstrap extends GeyserModBootstrap {
); );
GeyserNeoForgeCommandRegistry registry = new GeyserNeoForgeCommandRegistry(getGeyser(), cloud); GeyserNeoForgeCommandRegistry registry = new GeyserNeoForgeCommandRegistry(getGeyser(), cloud);
this.setCommandRegistry(registry); this.setCommandRegistry(registry);
// An auxiliary listener for registering undefined permissions belonging to commands. See javadocs for more info.
NeoForge.EVENT_BUS.addListener(EventPriority.LOWEST, registry::onPermissionGatherForUndefined); NeoForge.EVENT_BUS.addListener(EventPriority.LOWEST, registry::onPermissionGatherForUndefined);
} }

Datei anzeigen

@ -1,3 +1,8 @@
plugins {
id("geyser.platform-conventions")
id("geyser.modrinth-uploading-conventions")
}
dependencies { dependencies {
api(projects.core) api(projects.core)
api(libs.erosion.bukkit.common) { api(libs.erosion.bukkit.common) {

Datei anzeigen

@ -181,7 +181,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
return; return;
} }
// Create command manager early so we can add Geyser extension commands // Register commands after Geyser initialization, but before the server starts.
var sourceConverter = new CommandSourceConverter<>( var sourceConverter = new CommandSourceConverter<>(
CommandSender.class, CommandSender.class,
Bukkit::getPlayer, Bukkit::getPlayer,

Datei anzeigen

@ -2,6 +2,7 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCach
plugins { plugins {
application application
id("geyser.platform-conventions")
} }
val terminalConsoleVersion = "1.2.0" val terminalConsoleVersion = "1.2.0"

Datei anzeigen

@ -1,3 +1,8 @@
plugins {
id("geyser.platform-conventions")
id("geyser.modrinth-uploading-conventions")
}
dependencies { dependencies {
annotationProcessor(libs.velocity.api) annotationProcessor(libs.velocity.api)
api(projects.core) api(projects.core)

Datei anzeigen

@ -109,6 +109,22 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
this.geyser = GeyserImpl.load(PlatformType.VELOCITY, this); this.geyser = GeyserImpl.load(PlatformType.VELOCITY, this);
this.geyserInjector = new GeyserVelocityInjector(proxyServer); this.geyserInjector = new GeyserVelocityInjector(proxyServer);
// We need to register commands here, rather than in onGeyserEnable which is invoked during the appropriate ListenerBoundEvent.
// Reason: players can connect after a listener is bound, and a player join locks registration to the cloud CommandManager.
var sourceConverter = new CommandSourceConverter<>(
CommandSource.class,
id -> proxyServer.getPlayer(id).orElse(null),
proxyServer::getConsoleCommandSource,
VelocityCommandSource::new
);
CommandManager<GeyserCommandSource> cloud = new VelocityCommandManager<>(
container,
proxyServer,
ExecutionCoordinator.simpleCoordinator(),
sourceConverter
);
this.commandRegistry = new CommandRegistry(geyser, cloud, false); // applying root permission would be a breaking change because we can't register permission defaults
} }
@Override @Override
@ -123,20 +139,6 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
} }
this.geyserLogger.setDebug(geyserConfig.isDebugMode()); this.geyserLogger.setDebug(geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
} else {
var sourceConverter = new CommandSourceConverter<>(
CommandSource.class,
id -> proxyServer.getPlayer(id).orElse(null),
proxyServer::getConsoleCommandSource,
VelocityCommandSource::new
);
CommandManager<GeyserCommandSource> cloud = new VelocityCommandManager<>(
container,
proxyServer,
ExecutionCoordinator.simpleCoordinator(),
sourceConverter
);
this.commandRegistry = new CommandRegistry(geyser, cloud, false); // applying root permission would be a breaking change because we can't register permission defaults
} }
GeyserImpl.start(); GeyserImpl.start();

Datei anzeigen

@ -1,3 +1,7 @@
plugins {
id("geyser.platform-conventions")
}
dependencies { dependencies {
api(projects.core) api(projects.core)

Datei anzeigen

@ -12,9 +12,14 @@ repositories {
} }
dependencies { dependencies {
// This is for the LibsAccessor.kt hack
// this is OK as long as the same version catalog is used in the main build and build-logic // this is OK as long as the same version catalog is used in the main build and build-logic
// see https://github.com/gradle/gradle/issues/15383#issuecomment-779893192 // see https://github.com/gradle/gradle/issues/15383#issuecomment-779893192
implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location)) implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location))
// This is for applying plugins, and using the version from the libs.versions.toml
// Unfortunately they still need to be applied by their string name in the convention scripts.
implementation(libs.lombok)
implementation(libs.indra) implementation(libs.indra)
implementation(libs.shadow) implementation(libs.shadow)
implementation(libs.architectury.plugin) implementation(libs.architectury.plugin)

Datei anzeigen

@ -8,4 +8,4 @@ dependencyResolutionManagement {
} }
} }
rootProject.name = "build-logic" rootProject.name = "build-logic"

Datei anzeigen

@ -118,3 +118,12 @@ open class DownloadFilesTask : DefaultTask() {
private fun calcExclusion(section: String, bit: Int, excludedOn: Int): String = private fun calcExclusion(section: String, bit: Int, excludedOn: Int): String =
if (excludedOn and bit > 0) section else "" if (excludedOn and bit > 0) section else ""
fun projectVersion(project: Project): String =
project.version.toString().replace("SNAPSHOT", "b" + buildNumber())
fun versionName(project: Project): String =
"Geyser-" + project.name.replaceFirstChar { it.uppercase() } + "-" + projectVersion(project)
fun buildNumber(): Int =
(System.getenv("BUILD_NUMBER"))?.let { Integer.parseInt(it) } ?: -1

Datei anzeigen

@ -3,9 +3,10 @@ plugins {
id("net.kyori.indra") id("net.kyori.indra")
} }
dependencies { val rootProperties: Map<String, *> = project.rootProject.properties
compileOnly("org.checkerframework", "checker-qual", "3.19.0") group = rootProperties["group"] as String + "." + rootProperties["id"] as String
} version = rootProperties["version"] as String
description = rootProperties["description"] as String
indra { indra {
github("GeyserMC", "Geyser") { github("GeyserMC", "Geyser") {
@ -20,18 +21,52 @@ indra {
} }
} }
tasks { dependencies {
processResources { compileOnly("org.checkerframework", "checker-qual", libs.checker.qual.get().version)
// Spigot, BungeeCord, Velocity, Fabric, ViaProxy, NeoForge }
filesMatching(listOf("plugin.yml", "bungee.yml", "velocity-plugin.json", "fabric.mod.json", "viaproxy.yml", "META-INF/neoforge.mods.toml")) {
expand( repositories {
"id" to "geyser", // mavenLocal()
"name" to "Geyser",
"version" to project.version, mavenCentral()
"description" to project.description,
"url" to "https://geysermc.org", // Floodgate, Cumulus etc.
"author" to "GeyserMC" maven("https://repo.opencollab.dev/main")
)
} // Paper, Velocity
maven("https://repo.papermc.io/repository/maven-public")
// Spigot
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots") {
mavenContent { snapshotsOnly() }
} }
}
// BungeeCord
maven("https://oss.sonatype.org/content/repositories/snapshots") {
mavenContent { snapshotsOnly() }
}
// NeoForge
maven("https://maven.neoforged.net/releases") {
mavenContent { releasesOnly() }
}
// Minecraft
maven("https://libraries.minecraft.net") {
name = "minecraft"
mavenContent { releasesOnly() }
}
// ViaVersion
maven("https://repo.viaversion.com") {
name = "viaversion"
}
// Jitpack for e.g. MCPL
maven("https://jitpack.io") {
content { includeGroupByRegex("com\\.github\\..*") }
}
// For Adventure snapshots
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
}

Datei anzeigen

@ -1,45 +0,0 @@
repositories {
// mavenLocal()
mavenCentral()
// Floodgate, Cumulus etc.
maven("https://repo.opencollab.dev/main")
// Paper, Velocity
maven("https://repo.papermc.io/repository/maven-public")
// Spigot
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots") {
mavenContent { snapshotsOnly() }
}
// BungeeCord
maven("https://oss.sonatype.org/content/repositories/snapshots") {
mavenContent { snapshotsOnly() }
}
// NeoForge
maven("https://maven.neoforged.net/releases") {
mavenContent { releasesOnly() }
}
// Minecraft
maven("https://libraries.minecraft.net") {
name = "minecraft"
mavenContent { releasesOnly() }
}
// ViaVersion
maven("https://repo.viaversion.com") {
name = "viaversion"
}
// Jitpack for e.g. MCPL
maven("https://jitpack.io") {
content { includeGroupByRegex("com\\.github\\..*") }
}
// For Adventure snapshots
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
}

Datei anzeigen

@ -2,11 +2,9 @@
import net.fabricmc.loom.task.RemapJarTask import net.fabricmc.loom.task.RemapJarTask
import org.gradle.kotlin.dsl.dependencies import org.gradle.kotlin.dsl.dependencies
import org.gradle.kotlin.dsl.maven
plugins { plugins {
id("geyser.build-logic") id("geyser.platform-conventions")
id("geyser.publish-conventions")
id("architectury-plugin") id("architectury-plugin")
id("dev.architectury.loom") id("dev.architectury.loom")
} }
@ -87,7 +85,7 @@ tasks {
register("remapModrinthJar", RemapJarTask::class) { register("remapModrinthJar", RemapJarTask::class) {
dependsOn(shadowJar) dependsOn(shadowJar)
inputFile.set(shadowJar.get().archiveFile) inputFile.set(shadowJar.get().archiveFile)
archiveVersion.set(project.version.toString() + "+build." + System.getenv("BUILD_NUMBER")) archiveVersion.set(versionName(project))
archiveClassifier.set("") archiveClassifier.set("")
} }
} }

Datei anzeigen

@ -8,7 +8,8 @@ tasks.modrinth.get().dependsOn(tasks.modrinthSyncBody)
modrinth { modrinth {
token.set(System.getenv("MODRINTH_TOKEN") ?: "") // Even though this is the default value, apparently this prevents GitHub Actions caching the token? token.set(System.getenv("MODRINTH_TOKEN") ?: "") // Even though this is the default value, apparently this prevents GitHub Actions caching the token?
projectId.set("geyser") projectId.set("geyser")
versionNumber.set(project.version as String + "-" + System.getenv("BUILD_NUMBER")) versionName.set(versionName(project))
versionNumber.set(projectVersion(project))
versionType.set("beta") versionType.set("beta")
changelog.set(System.getenv("CHANGELOG") ?: "") changelog.set(System.getenv("CHANGELOG") ?: "")
gameVersions.addAll("1.21", libs.minecraft.get().version as String) gameVersions.addAll("1.21", libs.minecraft.get().version as String)

Datei anzeigen

@ -1,3 +1,20 @@
plugins { plugins {
id("geyser.publish-conventions") id("geyser.publish-conventions")
} id("io.freefair.lombok")
}
tasks {
processResources {
// Spigot, BungeeCord, Velocity, Fabric, ViaProxy, NeoForge
filesMatching(listOf("plugin.yml", "bungee.yml", "velocity-plugin.json", "fabric.mod.json", "viaproxy.yml", "META-INF/neoforge.mods.toml")) {
expand(
"id" to "geyser",
"name" to "Geyser",
"version" to project.version,
"description" to project.description,
"url" to "https://geysermc.org",
"author" to "GeyserMC"
)
}
}
}

Datei anzeigen

@ -1,55 +1,5 @@
plugins { plugins {
`java-library`
// Ensure AP works in eclipse (no effect on other IDEs) // Ensure AP works in eclipse (no effect on other IDEs)
eclipse eclipse
id("geyser.build-logic") id("geyser.base-conventions")
alias(libs.plugins.lombok) apply false
} }
allprojects {
group = properties["group"] as String + "." + properties["id"] as String
version = properties["version"] as String
description = properties["description"] as String
}
val basePlatforms = setOf(
projects.bungeecord,
projects.spigot,
projects.standalone,
projects.velocity,
projects.viaproxy
).map { it.dependencyProject }
val moddedPlatforms = setOf(
projects.fabric,
projects.neoforge,
projects.mod
).map { it.dependencyProject }
val modrinthPlatforms = setOf(
projects.bungeecord,
projects.fabric,
projects.neoforge,
projects.spigot,
projects.velocity
).map { it.dependencyProject }
subprojects {
apply {
plugin("java-library")
plugin("io.freefair.lombok")
plugin("geyser.build-logic")
}
when (this) {
in basePlatforms -> plugins.apply("geyser.platform-conventions")
in moddedPlatforms -> plugins.apply("geyser.modded-conventions")
else -> plugins.apply("geyser.base-conventions")
}
// Not combined with platform-conventions as that also contains
// platforms which we cant publish to modrinth
if (modrinthPlatforms.contains(this)) {
plugins.apply("geyser.modrinth-uploading-conventions")
}
}

Datei anzeigen

@ -1,5 +1,6 @@
plugins { plugins {
id("geyser.publish-conventions") id("geyser.publish-conventions")
id("io.freefair.lombok")
} }
dependencies { dependencies {

Datei anzeigen

@ -3,6 +3,7 @@ plugins {
idea idea
alias(libs.plugins.blossom) alias(libs.plugins.blossom)
id("geyser.publish-conventions") id("geyser.publish-conventions")
id("io.freefair.lombok")
} }
dependencies { dependencies {
@ -103,9 +104,6 @@ sourceSets {
} }
} }
fun buildNumber(): Int =
(System.getenv("BUILD_NUMBER"))?.let { Integer.parseInt(it) } ?: -1
fun isDevBuild(branch: String, repository: String): Boolean { fun isDevBuild(branch: String, repository: String): Boolean {
return branch != "master" || repository.equals("https://github.com/GeyserMC/Geyser", ignoreCase = true).not() return branch != "master" || repository.equals("https://github.com/GeyserMC/Geyser", ignoreCase = true).not()
} }
@ -139,7 +137,7 @@ inner class GitInfo {
buildNumber = buildNumber() buildNumber = buildNumber()
isDev = isDevBuild(branch, repository) isDev = isDevBuild(branch, repository)
val projectVersion = if (isDev) project.version else project.version.toString().replace("SNAPSHOT", "b${buildNumber}") val projectVersion = if (isDev) project.version else projectVersion(project)
version = "$projectVersion ($gitVersion)" version = "$projectVersion ($gitVersion)"
} }
} }

Datei anzeigen

@ -364,22 +364,6 @@ public class GeyserImpl implements GeyserApi, EventRegistrar {
} }
} }
String broadcastPort = System.getProperty("geyserBroadcastPort", "");
if (!broadcastPort.isEmpty()) {
int parsedPort;
try {
parsedPort = Integer.parseInt(broadcastPort);
if (parsedPort < 1 || parsedPort > 65535) {
throw new NumberFormatException("The broadcast port must be between 1 and 65535 inclusive!");
}
} catch (NumberFormatException e) {
logger.error(String.format("Invalid broadcast port: %s! Defaulting to configured port.", broadcastPort + " (" + e.getMessage() + ")"));
parsedPort = config.getBedrock().port();
}
config.getBedrock().setBroadcastPort(parsedPort);
logger.info("Broadcast port set from system property: " + parsedPort);
}
if (platformType != PlatformType.VIAPROXY) { if (platformType != PlatformType.VIAPROXY) {
boolean floodgatePresent = bootstrap.testFloodgatePluginPresent(); boolean floodgatePresent = bootstrap.testFloodgatePluginPresent();
if (config.getRemote().authType() == AuthType.FLOODGATE && !floodgatePresent) { if (config.getRemote().authType() == AuthType.FLOODGATE && !floodgatePresent) {
@ -394,6 +378,26 @@ public class GeyserImpl implements GeyserApi, EventRegistrar {
} }
} }
// Now that the Bedrock port may have been changed, also check the broadcast port (configurable on all platforms)
String broadcastPort = System.getProperty("geyserBroadcastPort", "");
if (!broadcastPort.isEmpty()) {
try {
int parsedPort = Integer.parseInt(broadcastPort);
if (parsedPort < 1 || parsedPort > 65535) {
throw new NumberFormatException("The broadcast port must be between 1 and 65535 inclusive!");
}
config.getBedrock().setBroadcastPort(parsedPort);
logger.info("Broadcast port set from system property: " + parsedPort);
} catch (NumberFormatException e) {
logger.error(String.format("Invalid broadcast port from system property: %s! Defaulting to configured port.", broadcastPort + " (" + e.getMessage() + ")"));
}
}
// It's set to 0 only if no system property or manual config value was set
if (config.getBedrock().broadcastPort() == 0) {
config.getBedrock().setBroadcastPort(config.getBedrock().port());
}
String remoteAddress = config.getRemote().address(); String remoteAddress = config.getRemote().address();
// Filters whether it is not an IP address or localhost, because otherwise it is not possible to find out an SRV entry. // Filters whether it is not an IP address or localhost, because otherwise it is not possible to find out an SRV entry.
if (!remoteAddress.matches(IP_REGEX) && !remoteAddress.equalsIgnoreCase("localhost")) { if (!remoteAddress.matches(IP_REGEX) && !remoteAddress.equalsIgnoreCase("localhost")) {

Datei anzeigen

@ -54,7 +54,7 @@ public enum GeyserAttributeType {
// Bedrock Attributes // Bedrock Attributes
ABSORPTION(null, "minecraft:absorption", 0f, 1024f, 0f), ABSORPTION(null, "minecraft:absorption", 0f, 1024f, 0f),
EXHAUSTION(null, "minecraft:player.exhaustion", 0f, 5f, 0f), EXHAUSTION(null, "minecraft:player.exhaustion", 0f, 20f, 0f),
EXPERIENCE(null, "minecraft:player.experience", 0f, 1f, 0f), EXPERIENCE(null, "minecraft:player.experience", 0f, 1f, 0f),
EXPERIENCE_LEVEL(null, "minecraft:player.level", 0f, 24791.00f, 0f), EXPERIENCE_LEVEL(null, "minecraft:player.level", 0f, 24791.00f, 0f),
HEALTH(null, "minecraft:health", 0f, 1024f, 20f), HEALTH(null, "minecraft:health", 0f, 1024f, 20f),

Datei anzeigen

@ -42,7 +42,11 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityLinkData; import org.cloudburstmc.protocol.bedrock.data.entity.EntityLinkData;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.cloudburstmc.protocol.bedrock.packet.*; import org.cloudburstmc.protocol.bedrock.packet.AddPlayerPacket;
import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket;
import org.cloudburstmc.protocol.bedrock.packet.SetEntityDataPacket;
import org.cloudburstmc.protocol.bedrock.packet.SetEntityLinkPacket;
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity; import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity;
import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.EntityDefinitions;
import org.geysermc.geyser.entity.attribute.GeyserAttributeType; import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
@ -278,7 +282,13 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity {
@Override @Override
public void setPosition(Vector3f position) { public void setPosition(Vector3f position) {
super.setPosition(position.add(0, definition.offset(), 0)); if (this.bedPosition != null) {
// As of Bedrock 1.21.22 and Fabric 1.21.1
// Messes with Bedrock if we send this to the client itself, though.
super.setPosition(position.up(0.2f));
} else {
super.setPosition(position.add(0, definition.offset(), 0));
}
} }
@Override @Override

Datei anzeigen

@ -140,7 +140,7 @@ public class SessionPlayerEntity extends PlayerEntity {
if (valid) { // Don't update during session init if (valid) { // Don't update during session init
session.getCollisionManager().updatePlayerBoundingBox(position); session.getCollisionManager().updatePlayerBoundingBox(position);
} }
super.setPosition(position); this.position = position.add(0, definition.offset(), 0);
} }
/** /**

Datei anzeigen

@ -0,0 +1,34 @@
/*
* Copyright (c) 2024 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.erosion;
import java.io.Serial;
import java.util.concurrent.CancellationException;
public class ErosionCancellationException extends CancellationException {
@Serial
private static final long serialVersionUID = 1L;
}

Datei anzeigen

@ -42,7 +42,6 @@ public final class GeyserboundHandshakePacketHandler extends AbstractGeyserbound
public void handleHandshake(GeyserboundHandshakePacket packet) { public void handleHandshake(GeyserboundHandshakePacket packet) {
boolean useTcp = packet.getTransportType().getSocketAddress() == null; boolean useTcp = packet.getTransportType().getSocketAddress() == null;
GeyserboundPacketHandlerImpl handler = new GeyserboundPacketHandlerImpl(session, useTcp ? new GeyserErosionPacketSender(session) : new NettyPacketSender<>()); GeyserboundPacketHandlerImpl handler = new GeyserboundPacketHandlerImpl(session, useTcp ? new GeyserErosionPacketSender(session) : new NettyPacketSender<>());
session.setErosionHandler(handler);
if (!useTcp) { if (!useTcp) {
if (session.getGeyser().getErosionUnixListener() == null) { if (session.getGeyser().getErosionUnixListener() == null) {
session.disconnect("Erosion configurations using Unix socket handling are not supported on this hardware!"); session.disconnect("Erosion configurations using Unix socket handling are not supported on this hardware!");
@ -52,6 +51,7 @@ public final class GeyserboundHandshakePacketHandler extends AbstractGeyserbound
} else { } else {
handler.onConnect(); handler.onConnect();
} }
session.setErosionHandler(handler);
session.ensureInEventLoop(() -> session.getChunkCache().clear()); session.ensureInEventLoop(() -> session.getChunkCache().clear());
} }

Datei anzeigen

@ -171,10 +171,10 @@ public final class GeyserboundPacketHandlerImpl extends AbstractGeyserboundPacke
@Override @Override
public void handleHandshake(GeyserboundHandshakePacket packet) { public void handleHandshake(GeyserboundHandshakePacket packet) {
this.close();
var handler = new GeyserboundHandshakePacketHandler(this.session); var handler = new GeyserboundHandshakePacketHandler(this.session);
session.setErosionHandler(handler); session.setErosionHandler(handler);
handler.handleHandshake(packet); handler.handleHandshake(packet);
this.close();
} }
@Override @Override
@ -198,6 +198,17 @@ public final class GeyserboundPacketHandlerImpl extends AbstractGeyserboundPacke
public void close() { public void close() {
this.packetSender.close(); this.packetSender.close();
if (pendingLookup != null) {
pendingLookup.completeExceptionally(new ErosionCancellationException());
}
if (pendingBatchLookup != null) {
pendingBatchLookup.completeExceptionally(new ErosionCancellationException());
}
if (pickBlockLookup != null) {
pickBlockLookup.completeExceptionally(new ErosionCancellationException());
}
asyncPendingLookups.forEach(($, future) -> future.completeExceptionally(new ErosionCancellationException()));
} }
public int getNextTransactionId() { public int getNextTransactionId() {

Datei anzeigen

@ -43,13 +43,13 @@ public class CameraDefinitions {
static { static {
CAMERA_PRESETS = List.of( CAMERA_PRESETS = List.of(
new CameraPreset(CameraPerspective.FIRST_PERSON.id(), "", null, null, null, null, null, null, OptionalBoolean.empty()), new CameraPreset(CameraPerspective.FIRST_PERSON.id(), "", null, null, null, null, null, null, OptionalBoolean.empty(), null, OptionalBoolean.empty(), null),
new CameraPreset(CameraPerspective.FREE.id(), "", null, null, null, null, null, null, OptionalBoolean.empty()), new CameraPreset(CameraPerspective.FREE.id(), "", null, null, null, null, null, null, OptionalBoolean.empty(), null, OptionalBoolean.empty(), null),
new CameraPreset(CameraPerspective.THIRD_PERSON.id(), "", null, null, null, null, null, null, OptionalBoolean.empty()), new CameraPreset(CameraPerspective.THIRD_PERSON.id(), "", null, null, null, null, null, null, OptionalBoolean.empty(), null, OptionalBoolean.empty(), null),
new CameraPreset(CameraPerspective.THIRD_PERSON_FRONT.id(), "", null, null, null, null, null, null, OptionalBoolean.empty()), new CameraPreset(CameraPerspective.THIRD_PERSON_FRONT.id(), "", null, null, null, null, null, null, OptionalBoolean.empty(), null, OptionalBoolean.empty(), null),
new CameraPreset("geyser:free_audio", "minecraft:free", null, null, null, null, null, CameraAudioListener.PLAYER, OptionalBoolean.of(false)), new CameraPreset("geyser:free_audio", "minecraft:free", null, null, null, null, null, CameraAudioListener.PLAYER, OptionalBoolean.empty(), null, OptionalBoolean.of(false), null),
new CameraPreset("geyser:free_effects", "minecraft:free", null, null, null, null, null, CameraAudioListener.CAMERA, OptionalBoolean.of(true)), new CameraPreset("geyser:free_effects", "minecraft:free", null, null, null, null, null, CameraAudioListener.CAMERA, OptionalBoolean.empty(), null, OptionalBoolean.of(true), null),
new CameraPreset("geyser:free_audio_effects", "minecraft:free", null, null, null, null, null, CameraAudioListener.PLAYER, OptionalBoolean.of(true))); new CameraPreset("geyser:free_audio_effects", "minecraft:free", null, null, null, null, null, CameraAudioListener.PLAYER, OptionalBoolean.empty(), null, OptionalBoolean.of(true), null));
SimpleDefinitionRegistry.Builder<NamedDefinition> builder = SimpleDefinitionRegistry.builder(); SimpleDefinitionRegistry.Builder<NamedDefinition> builder = SimpleDefinitionRegistry.builder();
for (int i = 0; i < CAMERA_PRESETS.size(); i++) { for (int i = 0; i < CAMERA_PRESETS.size(); i++) {

Datei anzeigen

@ -162,6 +162,27 @@ public final class ClickPlan {
finished = true; finished = true;
} }
public Inventory getInventory() {
return inventory;
}
/**
* Test if the item stacks with another item in the specified slot.
* This will check the simulated inventory without copying.
*/
public boolean canStack(int slot, GeyserItemStack item) {
GeyserItemStack slotItem = simulatedItems.getOrDefault(slot, inventory.getItem(slot));
return InventoryUtils.canStack(slotItem, item);
}
/**
* Test if the specified slot is empty.
* This will check the simulated inventory without copying.
*/
public boolean isEmpty(int slot) {
return simulatedItems.getOrDefault(slot, inventory.getItem(slot)).isEmpty();
}
public GeyserItemStack getItem(int slot) { public GeyserItemStack getItem(int slot) {
return simulatedItems.computeIfAbsent(slot, k -> inventory.getItem(slot).copy()); return simulatedItems.computeIfAbsent(slot, k -> inventory.getItem(slot).copy());
} }

Datei anzeigen

@ -32,6 +32,8 @@ import net.kyori.adventure.text.Component;
import org.cloudburstmc.nbt.NbtMap; import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder; import org.cloudburstmc.nbt.NbtMapBuilder;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerId; import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerId;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType;
import org.cloudburstmc.protocol.bedrock.data.inventory.FullContainerName;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.cloudburstmc.protocol.bedrock.packet.InventorySlotPacket; import org.cloudburstmc.protocol.bedrock.packet.InventorySlotPacket;
import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserImpl;
@ -78,6 +80,7 @@ public class AnvilInventoryUpdater extends InventoryUpdater {
slotPacket.setContainerId(ContainerId.UI); slotPacket.setContainerId(ContainerId.UI);
slotPacket.setSlot(bedrockSlot); slotPacket.setSlot(bedrockSlot);
slotPacket.setItem(inventory.getItem(i).getItemData(session)); slotPacket.setItem(inventory.getItem(i).getItemData(session));
slotPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null));
session.sendUpstreamPacket(slotPacket); session.sendUpstreamPacket(slotPacket);
} }
} }
@ -98,6 +101,7 @@ public class AnvilInventoryUpdater extends InventoryUpdater {
slotPacket.setContainerId(ContainerId.UI); slotPacket.setContainerId(ContainerId.UI);
slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot)); slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
slotPacket.setItem(inventory.getItem(javaSlot).getItemData(session)); slotPacket.setItem(inventory.getItem(javaSlot).getItemData(session));
slotPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null));
session.sendUpstreamPacket(slotPacket); session.sendUpstreamPacket(slotPacket);
} else if (lastTargetSlot != javaSlot) { } else if (lastTargetSlot != javaSlot) {
// Update the previous target slot to remove repair cost changes // Update the previous target slot to remove repair cost changes
@ -105,6 +109,7 @@ public class AnvilInventoryUpdater extends InventoryUpdater {
slotPacket.setContainerId(ContainerId.UI); slotPacket.setContainerId(ContainerId.UI);
slotPacket.setSlot(translator.javaSlotToBedrock(lastTargetSlot)); slotPacket.setSlot(translator.javaSlotToBedrock(lastTargetSlot));
slotPacket.setItem(inventory.getItem(lastTargetSlot).getItemData(session)); slotPacket.setItem(inventory.getItem(lastTargetSlot).getItemData(session));
slotPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null));
session.sendUpstreamPacket(slotPacket); session.sendUpstreamPacket(slotPacket);
} }
@ -168,6 +173,7 @@ public class AnvilInventoryUpdater extends InventoryUpdater {
slotPacket.setContainerId(ContainerId.UI); slotPacket.setContainerId(ContainerId.UI);
slotPacket.setSlot(translator.javaSlotToBedrock(slot)); slotPacket.setSlot(translator.javaSlotToBedrock(slot));
slotPacket.setItem(itemData); slotPacket.setItem(itemData);
slotPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null));
session.sendUpstreamPacket(slotPacket); session.sendUpstreamPacket(slotPacket);
} }

Datei anzeigen

@ -25,6 +25,8 @@
package org.geysermc.geyser.inventory.updater; package org.geysermc.geyser.inventory.updater;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType;
import org.cloudburstmc.protocol.bedrock.data.inventory.FullContainerName;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.cloudburstmc.protocol.bedrock.packet.InventoryContentPacket; import org.cloudburstmc.protocol.bedrock.packet.InventoryContentPacket;
import org.cloudburstmc.protocol.bedrock.packet.InventorySlotPacket; import org.cloudburstmc.protocol.bedrock.packet.InventorySlotPacket;
@ -61,6 +63,7 @@ public class ChestInventoryUpdater extends InventoryUpdater {
InventoryContentPacket contentPacket = new InventoryContentPacket(); InventoryContentPacket contentPacket = new InventoryContentPacket();
contentPacket.setContainerId(inventory.getBedrockId()); contentPacket.setContainerId(inventory.getBedrockId());
contentPacket.setContents(bedrockItems); contentPacket.setContents(bedrockItems);
contentPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null));
session.sendUpstreamPacket(contentPacket); session.sendUpstreamPacket(contentPacket);
} }
@ -73,6 +76,7 @@ public class ChestInventoryUpdater extends InventoryUpdater {
slotPacket.setContainerId(inventory.getBedrockId()); slotPacket.setContainerId(inventory.getBedrockId());
slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot)); slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
slotPacket.setItem(inventory.getItem(javaSlot).getItemData(session)); slotPacket.setItem(inventory.getItem(javaSlot).getItemData(session));
slotPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null));
session.sendUpstreamPacket(slotPacket); session.sendUpstreamPacket(slotPacket);
return true; return true;
} }

Datei anzeigen

@ -25,6 +25,8 @@
package org.geysermc.geyser.inventory.updater; package org.geysermc.geyser.inventory.updater;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType;
import org.cloudburstmc.protocol.bedrock.data.inventory.FullContainerName;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.cloudburstmc.protocol.bedrock.packet.InventoryContentPacket; import org.cloudburstmc.protocol.bedrock.packet.InventoryContentPacket;
import org.cloudburstmc.protocol.bedrock.packet.InventorySlotPacket; import org.cloudburstmc.protocol.bedrock.packet.InventorySlotPacket;
@ -49,6 +51,7 @@ public class ContainerInventoryUpdater extends InventoryUpdater {
InventoryContentPacket contentPacket = new InventoryContentPacket(); InventoryContentPacket contentPacket = new InventoryContentPacket();
contentPacket.setContainerId(inventory.getBedrockId()); contentPacket.setContainerId(inventory.getBedrockId());
contentPacket.setContents(Arrays.asList(bedrockItems)); contentPacket.setContents(Arrays.asList(bedrockItems));
contentPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null));
session.sendUpstreamPacket(contentPacket); session.sendUpstreamPacket(contentPacket);
} }
@ -61,6 +64,7 @@ public class ContainerInventoryUpdater extends InventoryUpdater {
slotPacket.setContainerId(inventory.getBedrockId()); slotPacket.setContainerId(inventory.getBedrockId());
slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot)); slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
slotPacket.setItem(inventory.getItem(javaSlot).getItemData(session)); slotPacket.setItem(inventory.getItem(javaSlot).getItemData(session));
slotPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null));
session.sendUpstreamPacket(slotPacket); session.sendUpstreamPacket(slotPacket);
return true; return true;
} }

Datei anzeigen

@ -26,6 +26,8 @@
package org.geysermc.geyser.inventory.updater; package org.geysermc.geyser.inventory.updater;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerId; import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerId;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType;
import org.cloudburstmc.protocol.bedrock.data.inventory.FullContainerName;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.cloudburstmc.protocol.bedrock.packet.InventoryContentPacket; import org.cloudburstmc.protocol.bedrock.packet.InventoryContentPacket;
import org.cloudburstmc.protocol.bedrock.packet.InventorySlotPacket; import org.cloudburstmc.protocol.bedrock.packet.InventorySlotPacket;
@ -56,6 +58,7 @@ public class CrafterInventoryUpdater extends InventoryUpdater {
contentPacket = new InventoryContentPacket(); contentPacket = new InventoryContentPacket();
contentPacket.setContainerId(inventory.getBedrockId()); contentPacket.setContainerId(inventory.getBedrockId());
contentPacket.setContents(Arrays.asList(bedrockItems)); contentPacket.setContents(Arrays.asList(bedrockItems));
contentPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null));
session.sendUpstreamPacket(contentPacket); session.sendUpstreamPacket(contentPacket);
// inventory and hotbar // inventory and hotbar
@ -67,6 +70,7 @@ public class CrafterInventoryUpdater extends InventoryUpdater {
contentPacket = new InventoryContentPacket(); contentPacket = new InventoryContentPacket();
contentPacket.setContainerId(ContainerId.INVENTORY); contentPacket.setContainerId(ContainerId.INVENTORY);
contentPacket.setContents(Arrays.asList(bedrockItems)); contentPacket.setContents(Arrays.asList(bedrockItems));
contentPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null));
session.sendUpstreamPacket(contentPacket); session.sendUpstreamPacket(contentPacket);
// Crafter result - it doesn't come after the grid, as explained elsewhere. // Crafter result - it doesn't come after the grid, as explained elsewhere.
@ -88,6 +92,7 @@ public class CrafterInventoryUpdater extends InventoryUpdater {
packet.setContainerId(containerId); packet.setContainerId(containerId);
packet.setSlot(translator.javaSlotToBedrock(javaSlot)); packet.setSlot(translator.javaSlotToBedrock(javaSlot));
packet.setItem(inventory.getItem(javaSlot).getItemData(session)); packet.setItem(inventory.getItem(javaSlot).getItemData(session));
packet.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null));
session.sendUpstreamPacket(packet); session.sendUpstreamPacket(packet);
return true; return true;
} }

Datei anzeigen

@ -25,6 +25,8 @@
package org.geysermc.geyser.inventory.updater; package org.geysermc.geyser.inventory.updater;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType;
import org.cloudburstmc.protocol.bedrock.data.inventory.FullContainerName;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.cloudburstmc.protocol.bedrock.packet.InventoryContentPacket; import org.cloudburstmc.protocol.bedrock.packet.InventoryContentPacket;
import org.cloudburstmc.protocol.bedrock.packet.InventorySlotPacket; import org.cloudburstmc.protocol.bedrock.packet.InventorySlotPacket;
@ -49,6 +51,7 @@ public class HorseInventoryUpdater extends InventoryUpdater {
InventoryContentPacket contentPacket = new InventoryContentPacket(); InventoryContentPacket contentPacket = new InventoryContentPacket();
contentPacket.setContainerId(inventory.getBedrockId()); contentPacket.setContainerId(inventory.getBedrockId());
contentPacket.setContents(Arrays.asList(bedrockItems)); contentPacket.setContents(Arrays.asList(bedrockItems));
contentPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null));
session.sendUpstreamPacket(contentPacket); session.sendUpstreamPacket(contentPacket);
} }
@ -61,6 +64,7 @@ public class HorseInventoryUpdater extends InventoryUpdater {
slotPacket.setContainerId(4); // Horse GUI? slotPacket.setContainerId(4); // Horse GUI?
slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot)); slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
slotPacket.setItem(inventory.getItem(javaSlot).getItemData(session)); slotPacket.setItem(inventory.getItem(javaSlot).getItemData(session));
slotPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null));
session.sendUpstreamPacket(slotPacket); session.sendUpstreamPacket(slotPacket);
return true; return true;
} }

Datei anzeigen

@ -26,6 +26,8 @@
package org.geysermc.geyser.inventory.updater; package org.geysermc.geyser.inventory.updater;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerId; import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerId;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType;
import org.cloudburstmc.protocol.bedrock.data.inventory.FullContainerName;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.cloudburstmc.protocol.bedrock.packet.InventoryContentPacket; import org.cloudburstmc.protocol.bedrock.packet.InventoryContentPacket;
import org.cloudburstmc.protocol.bedrock.packet.InventorySlotPacket; import org.cloudburstmc.protocol.bedrock.packet.InventorySlotPacket;
@ -45,6 +47,7 @@ public class InventoryUpdater {
InventoryContentPacket contentPacket = new InventoryContentPacket(); InventoryContentPacket contentPacket = new InventoryContentPacket();
contentPacket.setContainerId(ContainerId.INVENTORY); contentPacket.setContainerId(ContainerId.INVENTORY);
contentPacket.setContents(Arrays.asList(bedrockItems)); contentPacket.setContents(Arrays.asList(bedrockItems));
contentPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null));
session.sendUpstreamPacket(contentPacket); session.sendUpstreamPacket(contentPacket);
} }
@ -54,6 +57,7 @@ public class InventoryUpdater {
slotPacket.setContainerId(ContainerId.INVENTORY); slotPacket.setContainerId(ContainerId.INVENTORY);
slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot)); slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
slotPacket.setItem(inventory.getItem(javaSlot).getItemData(session)); slotPacket.setItem(inventory.getItem(javaSlot).getItemData(session));
slotPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null));
session.sendUpstreamPacket(slotPacket); session.sendUpstreamPacket(slotPacket);
return true; return true;
} }

Datei anzeigen

@ -26,6 +26,8 @@
package org.geysermc.geyser.inventory.updater; package org.geysermc.geyser.inventory.updater;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerId; import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerId;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType;
import org.cloudburstmc.protocol.bedrock.data.inventory.FullContainerName;
import org.cloudburstmc.protocol.bedrock.packet.InventorySlotPacket; import org.cloudburstmc.protocol.bedrock.packet.InventorySlotPacket;
import org.geysermc.geyser.inventory.Inventory; import org.geysermc.geyser.inventory.Inventory;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
@ -46,6 +48,7 @@ public class UIInventoryUpdater extends InventoryUpdater {
slotPacket.setContainerId(ContainerId.UI); slotPacket.setContainerId(ContainerId.UI);
slotPacket.setSlot(bedrockSlot); slotPacket.setSlot(bedrockSlot);
slotPacket.setItem(inventory.getItem(i).getItemData(session)); slotPacket.setItem(inventory.getItem(i).getItemData(session));
slotPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null));
session.sendUpstreamPacket(slotPacket); session.sendUpstreamPacket(slotPacket);
} }
} }
@ -59,6 +62,7 @@ public class UIInventoryUpdater extends InventoryUpdater {
slotPacket.setContainerId(ContainerId.UI); slotPacket.setContainerId(ContainerId.UI);
slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot)); slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
slotPacket.setItem(inventory.getItem(javaSlot).getItemData(session)); slotPacket.setItem(inventory.getItem(javaSlot).getItemData(session));
slotPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null));
session.sendUpstreamPacket(slotPacket); session.sendUpstreamPacket(slotPacket);
return true; return true;
} }

Datei anzeigen

@ -25,6 +25,7 @@
package org.geysermc.geyser.item.enchantment; package org.geysermc.geyser.item.enchantment;
import it.unimi.dsi.fastutil.ints.IntArrays;
import net.kyori.adventure.key.Key; import net.kyori.adventure.key.Key;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.nbt.NbtMap; import org.cloudburstmc.nbt.NbtMap;
@ -33,13 +34,14 @@ import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.session.cache.registry.RegistryEntryContext; import org.geysermc.geyser.session.cache.registry.RegistryEntryContext;
import org.geysermc.geyser.translator.text.MessageTranslator; import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.geyser.util.MinecraftKey;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.HolderSet; import org.geysermc.mcprotocollib.protocol.data.game.item.component.HolderSet;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.function.Function; import java.util.function.ToIntFunction;
/** /**
* @param description only populated if {@link #bedrockEnchantment()} is not null. * @param description only populated if {@link #bedrockEnchantment()} is not null.
@ -69,7 +71,7 @@ public record Enchantment(String identifier,
// TODO - description is a component. So if a hardcoded literal string is given, this will display normally on Java, // TODO - description is a component. So if a hardcoded literal string is given, this will display normally on Java,
// but Geyser will attempt to lookup the literal string as translation - and will fail, displaying an empty string as enchantment name. // but Geyser will attempt to lookup the literal string as translation - and will fail, displaying an empty string as enchantment name.
String description = bedrockEnchantment == null ? MessageTranslator.deserializeDescription(data) : null; String description = bedrockEnchantment == null ? MessageTranslator.deserializeDescription(context.session(), data) : null;
return new Enchantment(context.id().asString(), effects, supportedItems, maxLevel, return new Enchantment(context.id().asString(), effects, supportedItems, maxLevel,
description, anvilCost, exclusiveSet, bedrockEnchantment); description, anvilCost, exclusiveSet, bedrockEnchantment);
@ -86,21 +88,21 @@ public record Enchantment(String identifier,
} }
// TODO holder set util? // TODO holder set util?
private static HolderSet readHolderSet(@Nullable Object holderSet, Function<Key, Integer> keyIdMapping) { private static HolderSet readHolderSet(@Nullable Object holderSet, ToIntFunction<Key> keyIdMapping) {
if (holderSet == null) { if (holderSet == null) {
return new HolderSet(new int[]{}); return new HolderSet(IntArrays.EMPTY_ARRAY);
} }
if (holderSet instanceof String stringTag) { if (holderSet instanceof String stringTag) {
// Tag // Tag
if (stringTag.startsWith("#")) { if (stringTag.startsWith("#")) {
return new HolderSet(Key.key(stringTag.substring(1))); // Remove '#' at beginning that indicates tag return new HolderSet(MinecraftKey.key(stringTag.substring(1))); // Remove '#' at beginning that indicates tag
} else { } else {
return new HolderSet(new int[]{keyIdMapping.apply(Key.key(stringTag))}); return new HolderSet(new int[]{keyIdMapping.applyAsInt(MinecraftKey.key(stringTag))});
} }
} else if (holderSet instanceof List<?> list) { } else if (holderSet instanceof List<?> list) {
// Assume the list is a list of strings // Assume the list is a list of strings
return new HolderSet(list.stream().map(o -> (String) o).map(Key::key).map(keyIdMapping).mapToInt(Integer::intValue).toArray()); return new HolderSet(list.stream().map(o -> (String) o).map(Key::key).mapToInt(keyIdMapping).toArray());
} }
throw new IllegalArgumentException("Holder set must either be a tag, a string ID or a list of string IDs"); throw new IllegalArgumentException("Holder set must either be a tag, a string ID or a list of string IDs");
} }

Datei anzeigen

@ -35,6 +35,7 @@ import org.geysermc.erosion.packet.backendbound.BackendboundBatchBlockRequestPac
import org.geysermc.erosion.packet.backendbound.BackendboundBlockRequestPacket; import org.geysermc.erosion.packet.backendbound.BackendboundBlockRequestPacket;
import org.geysermc.erosion.packet.backendbound.BackendboundPickBlockPacket; import org.geysermc.erosion.packet.backendbound.BackendboundPickBlockPacket;
import org.geysermc.erosion.util.BlockPositionIterator; import org.geysermc.erosion.util.BlockPositionIterator;
import org.geysermc.geyser.erosion.ErosionCancellationException;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
@ -49,6 +50,8 @@ public class GeyserWorldManager extends WorldManager {
var erosionHandler = session.getErosionHandler().getAsActive(); var erosionHandler = session.getErosionHandler().getAsActive();
if (erosionHandler == null) { if (erosionHandler == null) {
return session.getChunkCache().getBlockAt(x, y, z); return session.getChunkCache().getBlockAt(x, y, z);
} else if (session.isClosed()) {
throw new ErosionCancellationException();
} }
CompletableFuture<Integer> future = new CompletableFuture<>(); // Boxes CompletableFuture<Integer> future = new CompletableFuture<>(); // Boxes
erosionHandler.setPendingLookup(future); erosionHandler.setPendingLookup(future);
@ -61,6 +64,8 @@ public class GeyserWorldManager extends WorldManager {
var erosionHandler = session.getErosionHandler().getAsActive(); var erosionHandler = session.getErosionHandler().getAsActive();
if (erosionHandler == null) { if (erosionHandler == null) {
return super.getBlockAtAsync(session, x, y, z); return super.getBlockAtAsync(session, x, y, z);
} else if (session.isClosed()) {
return CompletableFuture.failedFuture(new ErosionCancellationException());
} }
CompletableFuture<Integer> future = new CompletableFuture<>(); // Boxes CompletableFuture<Integer> future = new CompletableFuture<>(); // Boxes
int transactionId = erosionHandler.getNextTransactionId(); int transactionId = erosionHandler.getNextTransactionId();
@ -74,6 +79,8 @@ public class GeyserWorldManager extends WorldManager {
var erosionHandler = session.getErosionHandler().getAsActive(); var erosionHandler = session.getErosionHandler().getAsActive();
if (erosionHandler == null) { if (erosionHandler == null) {
return super.getBlocksAt(session, iter); return super.getBlocksAt(session, iter);
} else if (session.isClosed()) {
throw new ErosionCancellationException();
} }
CompletableFuture<int[]> future = new CompletableFuture<>(); CompletableFuture<int[]> future = new CompletableFuture<>();
erosionHandler.setPendingBatchLookup(future); erosionHandler.setPendingBatchLookup(future);
@ -124,6 +131,8 @@ public class GeyserWorldManager extends WorldManager {
var erosionHandler = session.getErosionHandler().getAsActive(); var erosionHandler = session.getErosionHandler().getAsActive();
if (erosionHandler == null) { if (erosionHandler == null) {
return super.getPickItemComponents(session, x, y, z, addNbtData); return super.getPickItemComponents(session, x, y, z, addNbtData);
} else if (session.isClosed()) {
return CompletableFuture.failedFuture(new ErosionCancellationException());
} }
CompletableFuture<Int2ObjectMap<byte[]>> future = new CompletableFuture<>(); CompletableFuture<Int2ObjectMap<byte[]>> future = new CompletableFuture<>();
erosionHandler.setPickBlockLookup(future); erosionHandler.setPickBlockLookup(future);

Datei anzeigen

@ -44,7 +44,7 @@ public record JukeboxSong(String soundEvent, String description) {
soundEvent = ""; soundEvent = "";
GeyserImpl.getInstance().getLogger().debug("Sound event for " + context.id() + " was of an unexpected type! Expected string or NBT map, got " + soundEventObject); GeyserImpl.getInstance().getLogger().debug("Sound event for " + context.id() + " was of an unexpected type! Expected string or NBT map, got " + soundEventObject);
} }
String description = MessageTranslator.deserializeDescription(data); String description = MessageTranslator.deserializeDescription(context.session(), data);
return new JukeboxSong(soundEvent, description); return new JukeboxSong(soundEvent, description);
} }
} }

Datei anzeigen

@ -33,7 +33,6 @@ import org.cloudburstmc.protocol.bedrock.codec.v291.serializer.MobArmorEquipment
import org.cloudburstmc.protocol.bedrock.codec.v291.serializer.MobEquipmentSerializer_v291; import org.cloudburstmc.protocol.bedrock.codec.v291.serializer.MobEquipmentSerializer_v291;
import org.cloudburstmc.protocol.bedrock.codec.v291.serializer.PlayerHotbarSerializer_v291; import org.cloudburstmc.protocol.bedrock.codec.v291.serializer.PlayerHotbarSerializer_v291;
import org.cloudburstmc.protocol.bedrock.codec.v291.serializer.SetEntityLinkSerializer_v291; import org.cloudburstmc.protocol.bedrock.codec.v291.serializer.SetEntityLinkSerializer_v291;
import org.cloudburstmc.protocol.bedrock.codec.v291.serializer.SetEntityMotionSerializer_v291;
import org.cloudburstmc.protocol.bedrock.codec.v390.serializer.PlayerSkinSerializer_v390; import org.cloudburstmc.protocol.bedrock.codec.v390.serializer.PlayerSkinSerializer_v390;
import org.cloudburstmc.protocol.bedrock.codec.v407.serializer.InventoryContentSerializer_v407; import org.cloudburstmc.protocol.bedrock.codec.v407.serializer.InventoryContentSerializer_v407;
import org.cloudburstmc.protocol.bedrock.codec.v407.serializer.InventorySlotSerializer_v407; import org.cloudburstmc.protocol.bedrock.codec.v407.serializer.InventorySlotSerializer_v407;
@ -43,6 +42,8 @@ import org.cloudburstmc.protocol.bedrock.codec.v662.serializer.SetEntityMotionSe
import org.cloudburstmc.protocol.bedrock.codec.v712.serializer.InventoryContentSerializer_v712; import org.cloudburstmc.protocol.bedrock.codec.v712.serializer.InventoryContentSerializer_v712;
import org.cloudburstmc.protocol.bedrock.codec.v712.serializer.InventorySlotSerializer_v712; import org.cloudburstmc.protocol.bedrock.codec.v712.serializer.InventorySlotSerializer_v712;
import org.cloudburstmc.protocol.bedrock.codec.v712.serializer.MobArmorEquipmentSerializer_v712; import org.cloudburstmc.protocol.bedrock.codec.v712.serializer.MobArmorEquipmentSerializer_v712;
import org.cloudburstmc.protocol.bedrock.codec.v729.serializer.InventoryContentSerializer_v729;
import org.cloudburstmc.protocol.bedrock.codec.v729.serializer.InventorySlotSerializer_v729;
import org.cloudburstmc.protocol.bedrock.packet.AnvilDamagePacket; import org.cloudburstmc.protocol.bedrock.packet.AnvilDamagePacket;
import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket; import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket;
import org.cloudburstmc.protocol.bedrock.packet.BossEventPacket; import org.cloudburstmc.protocol.bedrock.packet.BossEventPacket;
@ -139,6 +140,13 @@ class CodecProcessor {
} }
}; };
private static final BedrockPacketSerializer<InventoryContentPacket> INVENTORY_CONTENT_SERIALIZER_V729 = new InventoryContentSerializer_v729() {
@Override
public void deserialize(ByteBuf buffer, BedrockCodecHelper helper, InventoryContentPacket packet) {
throw new IllegalArgumentException("Client cannot send InventoryContentPacket in server-auth inventory environment!");
}
};
/** /**
* Serializer that throws an exception when trying to deserialize InventorySlotPacket since server-auth inventory is used. * Serializer that throws an exception when trying to deserialize InventorySlotPacket since server-auth inventory is used.
*/ */
@ -159,6 +167,13 @@ class CodecProcessor {
} }
}; };
private static final BedrockPacketSerializer<InventorySlotPacket> INVENTORY_SLOT_SERIALIZER_V729 = new InventorySlotSerializer_v729() {
@Override
public void deserialize(ByteBuf buffer, BedrockCodecHelper helper, InventorySlotPacket packet) {
throw new IllegalArgumentException("Client cannot send InventorySlotPacket in server-auth inventory environment!");
}
};
/** /**
* Serializer that does nothing when trying to deserialize BossEventPacket since it is not used from the client. * Serializer that does nothing when trying to deserialize BossEventPacket since it is not used from the client.
*/ */
@ -214,16 +229,7 @@ class CodecProcessor {
}; };
/** /**
* Serializer that does nothing when trying to deserialize SetEntityMotionPacket since it is not used from the client for codec v291. * Serializer that does nothing when trying to deserialize SetEntityMotionPacket since it is not used from the client.
*/
private static final BedrockPacketSerializer<SetEntityMotionPacket> SET_ENTITY_MOTION_SERIALIZER_V291 = new SetEntityMotionSerializer_v291() {
@Override
public void deserialize(ByteBuf buffer, BedrockCodecHelper helper, SetEntityMotionPacket packet) {
}
};
/**
* Serializer that does nothing when trying to deserialize SetEntityMotionPacket since it is not used from the client for codec v662.
*/ */
private static final BedrockPacketSerializer<SetEntityMotionPacket> SET_ENTITY_MOTION_SERIALIZER = new SetEntityMotionSerializer_v662() { private static final BedrockPacketSerializer<SetEntityMotionPacket> SET_ENTITY_MOTION_SERIALIZER = new SetEntityMotionSerializer_v662() {
@Override @Override
@ -256,8 +262,27 @@ class CodecProcessor {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
static BedrockCodec processCodec(BedrockCodec codec) { static BedrockCodec processCodec(BedrockCodec codec) {
boolean isPre712 = codec.getProtocolVersion() < 712; boolean is729OrAbove = codec.getProtocolVersion() >= 729;
boolean is712OrAbove = codec.getProtocolVersion() >= 712;
BedrockPacketSerializer<InventoryContentPacket> inventoryContentSerializer;
if (is729OrAbove) {
inventoryContentSerializer = INVENTORY_CONTENT_SERIALIZER_V729;
} else if (is712OrAbove) {
inventoryContentSerializer = INVENTORY_CONTENT_SERIALIZER_V712;
} else {
inventoryContentSerializer = INVENTORY_CONTENT_SERIALIZER_V407;
}
BedrockPacketSerializer<InventorySlotPacket> inventorySlotSerializer;
if (is729OrAbove) {
inventorySlotSerializer = INVENTORY_SLOT_SERIALIZER_V729;
} else if (is712OrAbove) {
inventorySlotSerializer = INVENTORY_SLOT_SERIALIZER_V712;
} else {
inventorySlotSerializer = INVENTORY_SLOT_SERIALIZER_V407;
}
BedrockCodec.Builder codecBuilder = codec.toBuilder() BedrockCodec.Builder codecBuilder = codec.toBuilder()
// Illegal unused serverbound EDU packets // Illegal unused serverbound EDU packets
.updateSerializer(PhotoTransferPacket.class, ILLEGAL_SERIALIZER) .updateSerializer(PhotoTransferPacket.class, ILLEGAL_SERIALIZER)
@ -286,11 +311,11 @@ class CodecProcessor {
.updateSerializer(AnvilDamagePacket.class, IGNORED_SERIALIZER) .updateSerializer(AnvilDamagePacket.class, IGNORED_SERIALIZER)
.updateSerializer(RefreshEntitlementsPacket.class, IGNORED_SERIALIZER) .updateSerializer(RefreshEntitlementsPacket.class, IGNORED_SERIALIZER)
// Illegal when serverbound due to Geyser specific setup // Illegal when serverbound due to Geyser specific setup
.updateSerializer(InventoryContentPacket.class, isPre712 ? INVENTORY_CONTENT_SERIALIZER_V407 : INVENTORY_CONTENT_SERIALIZER_V712) .updateSerializer(InventoryContentPacket.class, inventoryContentSerializer)
.updateSerializer(InventorySlotPacket.class, isPre712 ? INVENTORY_SLOT_SERIALIZER_V407 : INVENTORY_SLOT_SERIALIZER_V712) .updateSerializer(InventorySlotPacket.class, inventorySlotSerializer)
// Ignored only when serverbound // Ignored only when serverbound
.updateSerializer(BossEventPacket.class, BOSS_EVENT_SERIALIZER) .updateSerializer(BossEventPacket.class, BOSS_EVENT_SERIALIZER)
.updateSerializer(MobArmorEquipmentPacket.class, isPre712 ? MOB_ARMOR_EQUIPMENT_SERIALIZER_V291 : MOB_ARMOR_EQUIPMENT_SERIALIZER_V712) .updateSerializer(MobArmorEquipmentPacket.class, is712OrAbove ? MOB_ARMOR_EQUIPMENT_SERIALIZER_V712 : MOB_ARMOR_EQUIPMENT_SERIALIZER_V291)
.updateSerializer(PlayerHotbarPacket.class, PLAYER_HOTBAR_SERIALIZER) .updateSerializer(PlayerHotbarPacket.class, PLAYER_HOTBAR_SERIALIZER)
.updateSerializer(PlayerSkinPacket.class, PLAYER_SKIN_SERIALIZER) .updateSerializer(PlayerSkinPacket.class, PLAYER_SKIN_SERIALIZER)
.updateSerializer(SetEntityDataPacket.class, SET_ENTITY_DATA_SERIALIZER) .updateSerializer(SetEntityDataPacket.class, SET_ENTITY_DATA_SERIALIZER)

Datei anzeigen

@ -31,6 +31,7 @@ import org.cloudburstmc.protocol.bedrock.codec.v671.Bedrock_v671;
import org.cloudburstmc.protocol.bedrock.codec.v685.Bedrock_v685; import org.cloudburstmc.protocol.bedrock.codec.v685.Bedrock_v685;
import org.cloudburstmc.protocol.bedrock.codec.v686.Bedrock_v686; import org.cloudburstmc.protocol.bedrock.codec.v686.Bedrock_v686;
import org.cloudburstmc.protocol.bedrock.codec.v712.Bedrock_v712; import org.cloudburstmc.protocol.bedrock.codec.v712.Bedrock_v712;
import org.cloudburstmc.protocol.bedrock.codec.v729.Bedrock_v729;
import org.cloudburstmc.protocol.bedrock.netty.codec.packet.BedrockPacketCodec; import org.cloudburstmc.protocol.bedrock.netty.codec.packet.BedrockPacketCodec;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.codec.MinecraftCodec; import org.geysermc.mcprotocollib.protocol.codec.MinecraftCodec;
@ -49,8 +50,8 @@ public final class GameProtocol {
* Default Bedrock codec that should act as a fallback. Should represent the latest available * Default Bedrock codec that should act as a fallback. Should represent the latest available
* release of the game that Geyser supports. * release of the game that Geyser supports.
*/ */
public static final BedrockCodec DEFAULT_BEDROCK_CODEC = CodecProcessor.processCodec(Bedrock_v712.CODEC.toBuilder() public static final BedrockCodec DEFAULT_BEDROCK_CODEC = CodecProcessor.processCodec(Bedrock_v729.CODEC.toBuilder()
.minecraftVersion("1.21.20/1.21.21") .minecraftVersion("1.21.30")
.build()); .build());
/** /**
@ -74,6 +75,9 @@ public final class GameProtocol {
SUPPORTED_BEDROCK_CODECS.add(CodecProcessor.processCodec(Bedrock_v686.CODEC.toBuilder() SUPPORTED_BEDROCK_CODECS.add(CodecProcessor.processCodec(Bedrock_v686.CODEC.toBuilder()
.minecraftVersion("1.21.2/1.21.3") .minecraftVersion("1.21.2/1.21.3")
.build())); .build()));
SUPPORTED_BEDROCK_CODECS.add(CodecProcessor.processCodec(Bedrock_v712.CODEC.toBuilder()
.minecraftVersion("1.21.20 - 1.21.23")
.build()));
SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC); SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC);
} }

Datei anzeigen

@ -29,7 +29,6 @@ import io.netty.buffer.Unpooled;
import org.cloudburstmc.protocol.bedrock.BedrockDisconnectReasons; import org.cloudburstmc.protocol.bedrock.BedrockDisconnectReasons;
import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec; import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
import org.cloudburstmc.protocol.bedrock.codec.compat.BedrockCompat; import org.cloudburstmc.protocol.bedrock.codec.compat.BedrockCompat;
import org.cloudburstmc.protocol.bedrock.codec.v622.Bedrock_v622;
import org.cloudburstmc.protocol.bedrock.data.ExperimentData; import org.cloudburstmc.protocol.bedrock.data.ExperimentData;
import org.cloudburstmc.protocol.bedrock.data.PacketCompressionAlgorithm; import org.cloudburstmc.protocol.bedrock.data.PacketCompressionAlgorithm;
import org.cloudburstmc.protocol.bedrock.data.ResourcePackType; import org.cloudburstmc.protocol.bedrock.data.ResourcePackType;
@ -124,10 +123,11 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
session.disconnect(disconnectMessage); session.disconnect(disconnectMessage);
return false; return false;
} else if (protocolVersion < GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) { } else if (protocolVersion < GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) {
if (protocolVersion < Bedrock_v622.CODEC.getProtocolVersion()) { // A note on the following line: various older client versions have different forms of DisconnectPacket.
// https://github.com/GeyserMC/Geyser/issues/4378 // Using only the latest BedrockCompat for such clients leads to inaccurate disconnect messages: https://github.com/GeyserMC/Geyser/issues/4378
session.getUpstream().getSession().setCodec(BedrockCompat.CODEC_LEGACY); // This updates the BedrockCompat protocol if necessary:
} session.getUpstream().getSession().setCodec(BedrockCompat.disconnectCompat(protocolVersion));
session.disconnect(GeyserLocale.getLocaleStringLog("geyser.network.outdated.client", supportedVersions)); session.disconnect(GeyserLocale.getLocaleStringLog("geyser.network.outdated.client", supportedVersions));
return false; return false;
} else { } else {

Datei anzeigen

@ -144,11 +144,6 @@ public final class GeyserServer {
this.proxiedAddresses = null; this.proxiedAddresses = null;
} }
// It's set to 0 only if no system property or manual config value was set
if (geyser.getConfig().getBedrock().broadcastPort() == 0) {
geyser.getConfig().getBedrock().setBroadcastPort(geyser.getConfig().getBedrock().port());
}
this.broadcastPort = geyser.getConfig().getBedrock().broadcastPort(); this.broadcastPort = geyser.getConfig().getBedrock().broadcastPort();
} }

Datei anzeigen

@ -31,6 +31,7 @@ import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.level.Clien
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.level.ClientboundLightUpdatePacket; import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.level.ClientboundLightUpdatePacket;
import io.netty.channel.EventLoop; import io.netty.channel.EventLoop;
import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.erosion.ErosionCancellationException;
import org.geysermc.geyser.registry.loader.RegistryLoaders; import org.geysermc.geyser.registry.loader.RegistryLoaders;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.text.GeyserLocale;
@ -87,6 +88,8 @@ public class PacketTranslatorRegistry<T> extends AbstractMappedRegistry<Class<?
try { try {
translator.translate(session, packet); translator.translate(session, packet);
} catch (ErosionCancellationException ex) {
GeyserImpl.getInstance().getLogger().debug("Caught ErosionCancellationException");
} catch (Throwable ex) { } catch (Throwable ex) {
GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.network.translator.packet.failed", packet.getClass().getSimpleName()), ex); GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.network.translator.packet.failed", packet.getClass().getSimpleName()), ex);
ex.printStackTrace(); ex.printStackTrace();

Datei anzeigen

@ -34,11 +34,20 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet; import it.unimi.dsi.fastutil.ints.IntSet;
import it.unimi.dsi.fastutil.objects.*; import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import org.cloudburstmc.nbt.*; import it.unimi.dsi.fastutil.objects.Object2ObjectMaps;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectIntPair;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import org.cloudburstmc.nbt.NBTInputStream;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.cloudburstmc.nbt.NbtType;
import org.cloudburstmc.nbt.NbtUtils;
import org.cloudburstmc.protocol.bedrock.codec.v671.Bedrock_v671; import org.cloudburstmc.protocol.bedrock.codec.v671.Bedrock_v671;
import org.cloudburstmc.protocol.bedrock.codec.v685.Bedrock_v685; import org.cloudburstmc.protocol.bedrock.codec.v685.Bedrock_v685;
import org.cloudburstmc.protocol.bedrock.codec.v712.Bedrock_v712; import org.cloudburstmc.protocol.bedrock.codec.v712.Bedrock_v712;
import org.cloudburstmc.protocol.bedrock.codec.v729.Bedrock_v729;
import org.cloudburstmc.protocol.bedrock.data.BlockPropertyData; import org.cloudburstmc.protocol.bedrock.data.BlockPropertyData;
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition; import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserImpl;
@ -62,7 +71,15 @@ import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.InputStream; import java.io.InputStream;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.*; import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream; import java.util.stream.Stream;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
@ -110,7 +127,8 @@ public final class BlockRegistryPopulator {
var blockMappers = ImmutableMap.<ObjectIntPair<String>, Remapper>builder() var blockMappers = ImmutableMap.<ObjectIntPair<String>, Remapper>builder()
.put(ObjectIntPair.of("1_20_80", Bedrock_v671.CODEC.getProtocolVersion()), Conversion685_671::remapBlock) .put(ObjectIntPair.of("1_20_80", Bedrock_v671.CODEC.getProtocolVersion()), Conversion685_671::remapBlock)
.put(ObjectIntPair.of("1_21_0", Bedrock_v685.CODEC.getProtocolVersion()), Conversion712_685::remapBlock) .put(ObjectIntPair.of("1_21_0", Bedrock_v685.CODEC.getProtocolVersion()), Conversion712_685::remapBlock)
.put(ObjectIntPair.of("1_21_20", Bedrock_v712.CODEC.getProtocolVersion()), tag -> tag) .put(ObjectIntPair.of("1_21_20", Bedrock_v712.CODEC.getProtocolVersion()), Conversion729_712::remapBlock)
.put(ObjectIntPair.of("1_21_30", Bedrock_v729.CODEC.getProtocolVersion()), tag -> tag)
.build(); .build();
// We can keep this strong as nothing should be garbage collected // We can keep this strong as nothing should be garbage collected

Datei anzeigen

@ -32,6 +32,8 @@ public class Conversion712_685 {
private static final List<String> NEW_BLOCKS = Stream.of(NEW_STONE_BLOCK_SLABS_2, NEW_STONE_BLOCK_SLABS_3, NEW_STONE_BLOCK_SLABS_4, NEW_DOUBLE_STONE_BLOCK_SLABS, NEW_DOUBLE_STONE_BLOCK_SLABS_2, NEW_DOUBLE_STONE_BLOCK_SLABS_3, NEW_DOUBLE_STONE_BLOCK_SLABS_4, NEW_PRISMARINE_BLOCKS, NEW_CORAL_FAN_HANGS, NEW_CORAL_FAN_HANGS_2, NEW_CORAL_FAN_HANGS_3, NEW_MONSTER_EGGS, NEW_STONEBRICK_BLOCKS, NEW_LIGHT_BLOCKS, NEW_SANDSTONE_BLOCKS, NEW_QUARTZ_BLOCKS, NEW_RED_SANDSTONE_BLOCKS, NEW_SAND_BLOCKS, NEW_DIRT_BLOCKS, NEW_ANVILS, NEW_YELLOW_FLOWERS).flatMap(List::stream).toList(); private static final List<String> NEW_BLOCKS = Stream.of(NEW_STONE_BLOCK_SLABS_2, NEW_STONE_BLOCK_SLABS_3, NEW_STONE_BLOCK_SLABS_4, NEW_DOUBLE_STONE_BLOCK_SLABS, NEW_DOUBLE_STONE_BLOCK_SLABS_2, NEW_DOUBLE_STONE_BLOCK_SLABS_3, NEW_DOUBLE_STONE_BLOCK_SLABS_4, NEW_PRISMARINE_BLOCKS, NEW_CORAL_FAN_HANGS, NEW_CORAL_FAN_HANGS_2, NEW_CORAL_FAN_HANGS_3, NEW_MONSTER_EGGS, NEW_STONEBRICK_BLOCKS, NEW_LIGHT_BLOCKS, NEW_SANDSTONE_BLOCKS, NEW_QUARTZ_BLOCKS, NEW_RED_SANDSTONE_BLOCKS, NEW_SAND_BLOCKS, NEW_DIRT_BLOCKS, NEW_ANVILS, NEW_YELLOW_FLOWERS).flatMap(List::stream).toList();
static GeyserMappingItem remapItem(Item item, GeyserMappingItem mapping) { static GeyserMappingItem remapItem(Item item, GeyserMappingItem mapping) {
mapping = Conversion729_712.remapItem(item, mapping);
String identifer = mapping.getBedrockIdentifier(); String identifer = mapping.getBedrockIdentifier();
if (!NEW_BLOCKS.contains(identifer)) { if (!NEW_BLOCKS.contains(identifer)) {
@ -153,6 +155,8 @@ public class Conversion712_685 {
} }
static NbtMap remapBlock(NbtMap tag) { static NbtMap remapBlock(NbtMap tag) {
tag = Conversion729_712.remapBlock(tag);
final String name = tag.getString("name"); final String name = tag.getString("name");
if (!NEW_BLOCKS.contains(name)) { if (!NEW_BLOCKS.contains(name)) {

Datei anzeigen

@ -0,0 +1,151 @@
package org.geysermc.geyser.registry.populator;
import org.cloudburstmc.nbt.NbtMap;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.registry.type.GeyserMappingItem;
import java.util.List;
import java.util.stream.Stream;
public class Conversion729_712 {
private static final List<String> NEW_PURPUR_BLOCKS = List.of("minecraft:purpur_block", "minecraft:purpur_pillar");
private static final List<String> NEW_WALL_BLOCKS = List.of("minecraft:cobblestone_wall", "minecraft:mossy_cobblestone_wall", "minecraft:granite_wall", "minecraft:diorite_wall", "minecraft:andesite_wall", "minecraft:sandstone_wall", "minecraft:brick_wall", "minecraft:stone_brick_wall", "minecraft:mossy_stone_brick_wall", "minecraft:nether_brick_wall", "minecraft:end_stone_brick_wall", "minecraft:prismarine_wall", "minecraft:red_sandstone_wall", "minecraft:red_nether_brick_wall");
private static final List<String> NEW_SPONGE_BLOCKS = List.of("minecraft:sponge", "minecraft:wet_sponge");
private static final List<String> NEW_TNT_BLOCKS = List.of("minecraft:tnt", "minecraft:underwater_tnt");
private static final List<String> STRUCTURE_VOID = List.of("minecraft:structure_void");
private static final List<String> NEW_BLOCKS = Stream.of(NEW_PURPUR_BLOCKS, NEW_WALL_BLOCKS, NEW_SPONGE_BLOCKS, NEW_TNT_BLOCKS, STRUCTURE_VOID).flatMap(List::stream).toList();
static GeyserMappingItem remapItem(Item item, GeyserMappingItem mapping) {
String identifier = mapping.getBedrockIdentifier();
if (!NEW_BLOCKS.contains(identifier)) {
return mapping;
}
if (identifier.equals("minecraft:underwater_tnt")) {
return mapping.withBedrockIdentifier("minecraft:tnt").withBedrockData(1);
}
if (NEW_PURPUR_BLOCKS.contains(identifier)) {
switch (identifier) {
case "minecraft:purpur_block" -> { return mapping.withBedrockIdentifier("minecraft:purpur_block").withBedrockData(0); }
case "minecraft:purpur_pillar" -> { return mapping.withBedrockIdentifier("minecraft:purpur_block").withBedrockData(1); }
}
}
if (NEW_WALL_BLOCKS.contains(identifier)) {
switch (identifier) {
case "minecraft:cobblestone_wall" -> { return mapping.withBedrockIdentifier("minecraft:cobblestone_wall").withBedrockData(0); }
case "minecraft:mossy_cobblestone_wall" -> { return mapping.withBedrockIdentifier("minecraft:cobblestone_wall").withBedrockData(1); }
case "minecraft:granite_wall" -> { return mapping.withBedrockIdentifier("minecraft:cobblestone_wall").withBedrockData(2); }
case "minecraft:diorite_wall" -> { return mapping.withBedrockIdentifier("minecraft:cobblestone_wall").withBedrockData(3); }
case "minecraft:andesite_wall" -> { return mapping.withBedrockIdentifier("minecraft:cobblestone_wall").withBedrockData(4); }
case "minecraft:sandstone_wall" -> { return mapping.withBedrockIdentifier("minecraft:cobblestone_wall").withBedrockData(5); }
case "minecraft:brick_wall" -> { return mapping.withBedrockIdentifier("minecraft:cobblestone_wall").withBedrockData(6); }
case "minecraft:stone_brick_wall" -> { return mapping.withBedrockIdentifier("minecraft:cobblestone_wall").withBedrockData(7); }
case "minecraft:mossy_stone_brick_wall" -> { return mapping.withBedrockIdentifier("minecraft:cobblestone_wall").withBedrockData(8); }
case "minecraft:nether_brick_wall" -> { return mapping.withBedrockIdentifier("minecraft:cobblestone_wall").withBedrockData(9); }
case "minecraft:end_stone_brick_wall" -> { return mapping.withBedrockIdentifier("minecraft:cobblestone_wall").withBedrockData(10); }
case "minecraft:prismarine_wall" -> { return mapping.withBedrockIdentifier("minecraft:cobblestone_wall").withBedrockData(11); }
case "minecraft:red_sandstone_wall" -> { return mapping.withBedrockIdentifier("minecraft:cobblestone_wall").withBedrockData(12); }
case "minecraft:red_nether_brick_wall" -> { return mapping.withBedrockIdentifier("minecraft:cobblestone_wall").withBedrockData(13); }
}
}
if (NEW_SPONGE_BLOCKS.contains(identifier)) {
switch (identifier) {
case "minecraft:sponge" -> { return mapping.withBedrockIdentifier("minecraft:sponge").withBedrockData(0); }
case "minecraft:wet_sponge" -> { return mapping.withBedrockIdentifier("minecraft:sponge").withBedrockData(1); }
}
}
return mapping;
}
static NbtMap remapBlock(NbtMap tag) {
final String name = tag.getString("name");
if (!NEW_BLOCKS.contains(name)) {
return tag;
}
String replacement;
if (NEW_PURPUR_BLOCKS.contains(name)) {
replacement = "minecraft:purpur_block";
String purpurType = name.equals("minecraft:purpur_pillar") ? "lines" : "default";
NbtMap states = tag.getCompound("states")
.toBuilder()
.putString("chisel_type", purpurType)
.build();
return tag.toBuilder().putString("name", replacement).putCompound("states", states).build();
}
if (NEW_WALL_BLOCKS.contains(name)) {
replacement = "minecraft:cobblestone_wall";
String wallType;
switch (name) {
case "minecraft:cobblestone_wall" -> wallType = "cobblestone";
case "minecraft:mossy_cobblestone_wall" -> wallType = "mossy_cobblestone";
case "minecraft:granite_wall" -> wallType = "granite";
case "minecraft:diorite_wall" -> wallType = "diorite";
case "minecraft:andesite_wall" -> wallType = "andesite";
case "minecraft:sandstone_wall" -> wallType = "sandstone";
case "minecraft:brick_wall" -> wallType = "brick";
case "minecraft:stone_brick_wall" -> wallType = "stone_brick";
case "minecraft:mossy_stone_brick_wall" -> wallType = "mossy_stone_brick";
case "minecraft:nether_brick_wall" -> wallType = "nether_brick";
case "minecraft:end_stone_brick_wall" -> wallType = "end_brick";
case "minecraft:prismarine_wall" -> wallType = "prismarine";
case "minecraft:red_sandstone_wall" -> wallType = "red_sandstone";
case "minecraft:red_nether_brick_wall" -> wallType = "red_nether_brick";
default -> throw new IllegalStateException("Unexpected value: " + name);
}
NbtMap states = tag.getCompound("states")
.toBuilder()
.putString("wall_block_type", wallType)
.build();
return tag.toBuilder().putString("name", replacement).putCompound("states", states).build();
}
if (NEW_SPONGE_BLOCKS.contains(name)) {
replacement = "minecraft:sponge";
String spongeType = name.equals("minecraft:wet_sponge") ? "wet" : "dry";
NbtMap states = tag.getCompound("states")
.toBuilder()
.putString("sponge_type", spongeType)
.build();
return tag.toBuilder().putString("name", replacement).putCompound("states", states).build();
}
if (NEW_TNT_BLOCKS.contains(name)) {
replacement = "minecraft:tnt";
byte tntType = (byte) (name.equals("minecraft:underwater_tnt") ? 1 : 0);
NbtMap states = tag.getCompound("states")
.toBuilder()
.putByte("allow_underwater_bit", tntType)
.build();
return tag.toBuilder().putString("name", replacement).putCompound("states", states).build();
}
if (STRUCTURE_VOID.contains(name)) {
NbtMap states = tag.getCompound("states")
.toBuilder()
.putString("structure_void_type", "air")
.build();
return tag.toBuilder().putCompound("states", states).build();
}
return tag;
}
}

Datei anzeigen

@ -42,6 +42,7 @@ import org.cloudburstmc.nbt.NbtUtils;
import org.cloudburstmc.protocol.bedrock.codec.v671.Bedrock_v671; import org.cloudburstmc.protocol.bedrock.codec.v671.Bedrock_v671;
import org.cloudburstmc.protocol.bedrock.codec.v685.Bedrock_v685; import org.cloudburstmc.protocol.bedrock.codec.v685.Bedrock_v685;
import org.cloudburstmc.protocol.bedrock.codec.v712.Bedrock_v712; import org.cloudburstmc.protocol.bedrock.codec.v712.Bedrock_v712;
import org.cloudburstmc.protocol.bedrock.codec.v729.Bedrock_v729;
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition; import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
import org.cloudburstmc.protocol.bedrock.data.definitions.ItemDefinition; import org.cloudburstmc.protocol.bedrock.data.definitions.ItemDefinition;
import org.cloudburstmc.protocol.bedrock.data.definitions.SimpleItemDefinition; import org.cloudburstmc.protocol.bedrock.data.definitions.SimpleItemDefinition;
@ -92,7 +93,8 @@ public class ItemRegistryPopulator {
List<PaletteVersion> paletteVersions = new ArrayList<>(3); List<PaletteVersion> paletteVersions = new ArrayList<>(3);
paletteVersions.add(new PaletteVersion("1_20_80", Bedrock_v671.CODEC.getProtocolVersion(), Collections.emptyMap(), Conversion685_671::remapItem)); paletteVersions.add(new PaletteVersion("1_20_80", Bedrock_v671.CODEC.getProtocolVersion(), Collections.emptyMap(), Conversion685_671::remapItem));
paletteVersions.add(new PaletteVersion("1_21_0", Bedrock_v685.CODEC.getProtocolVersion(), Collections.emptyMap(), Conversion712_685::remapItem)); paletteVersions.add(new PaletteVersion("1_21_0", Bedrock_v685.CODEC.getProtocolVersion(), Collections.emptyMap(), Conversion712_685::remapItem));
paletteVersions.add(new PaletteVersion("1_21_20", Bedrock_v712.CODEC.getProtocolVersion())); paletteVersions.add(new PaletteVersion("1_21_20", Bedrock_v712.CODEC.getProtocolVersion(), Collections.emptyMap(), Conversion729_712::remapItem));
paletteVersions.add(new PaletteVersion("1_21_30", Bedrock_v729.CODEC.getProtocolVersion()));
GeyserBootstrap bootstrap = GeyserImpl.getInstance().getBootstrap(); GeyserBootstrap bootstrap = GeyserImpl.getInstance().getBootstrap();
@ -171,6 +173,11 @@ public class ItemRegistryPopulator {
List<ItemData> creativeItems = new ArrayList<>(); List<ItemData> creativeItems = new ArrayList<>();
Set<String> noBlockDefinitions = new ObjectOpenHashSet<>(); Set<String> noBlockDefinitions = new ObjectOpenHashSet<>();
// Fix: Usage of structure blocks/voids in recipes
// https://github.com/GeyserMC/Geyser/issues/2890
noBlockDefinitions.add("minecraft:structure_block");
noBlockDefinitions.add("minecraft:structure_void");
AtomicInteger creativeNetId = new AtomicInteger(); AtomicInteger creativeNetId = new AtomicInteger();
CreativeItemRegistryPopulator.populate(palette, definitions, itemBuilder -> { CreativeItemRegistryPopulator.populate(palette, definitions, itemBuilder -> {
ItemData item = itemBuilder.netId(creativeNetId.incrementAndGet()).build(); ItemData item = itemBuilder.netId(creativeNetId.incrementAndGet()).build();

Datei anzeigen

@ -40,6 +40,7 @@ import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import net.kyori.adventure.key.Key; import net.kyori.adventure.key.Key;
import net.raphimc.minecraftauth.responsehandler.exception.MinecraftRequestException;
import net.raphimc.minecraftauth.step.java.StepMCProfile; import net.raphimc.minecraftauth.step.java.StepMCProfile;
import net.raphimc.minecraftauth.step.java.StepMCToken; import net.raphimc.minecraftauth.step.java.StepMCToken;
import net.raphimc.minecraftauth.step.java.session.StepFullJavaSession; import net.raphimc.minecraftauth.step.java.session.StepFullJavaSession;
@ -66,6 +67,7 @@ import org.cloudburstmc.protocol.bedrock.data.ExperimentData;
import org.cloudburstmc.protocol.bedrock.data.GamePublishSetting; import org.cloudburstmc.protocol.bedrock.data.GamePublishSetting;
import org.cloudburstmc.protocol.bedrock.data.GameRuleData; import org.cloudburstmc.protocol.bedrock.data.GameRuleData;
import org.cloudburstmc.protocol.bedrock.data.GameType; import org.cloudburstmc.protocol.bedrock.data.GameType;
import org.cloudburstmc.protocol.bedrock.data.LevelEvent;
import org.cloudburstmc.protocol.bedrock.data.PlayerPermission; import org.cloudburstmc.protocol.bedrock.data.PlayerPermission;
import org.cloudburstmc.protocol.bedrock.data.SoundEvent; import org.cloudburstmc.protocol.bedrock.data.SoundEvent;
import org.cloudburstmc.protocol.bedrock.data.SpawnBiomeType; import org.cloudburstmc.protocol.bedrock.data.SpawnBiomeType;
@ -85,6 +87,7 @@ import org.cloudburstmc.protocol.bedrock.packet.CreativeContentPacket;
import org.cloudburstmc.protocol.bedrock.packet.EmoteListPacket; import org.cloudburstmc.protocol.bedrock.packet.EmoteListPacket;
import org.cloudburstmc.protocol.bedrock.packet.GameRulesChangedPacket; import org.cloudburstmc.protocol.bedrock.packet.GameRulesChangedPacket;
import org.cloudburstmc.protocol.bedrock.packet.ItemComponentPacket; import org.cloudburstmc.protocol.bedrock.packet.ItemComponentPacket;
import org.cloudburstmc.protocol.bedrock.packet.LevelEventPacket;
import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEvent2Packet; import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEvent2Packet;
import org.cloudburstmc.protocol.bedrock.packet.PlayStatusPacket; import org.cloudburstmc.protocol.bedrock.packet.PlayStatusPacket;
import org.cloudburstmc.protocol.bedrock.packet.SetTimePacket; import org.cloudburstmc.protocol.bedrock.packet.SetTimePacket;
@ -130,6 +133,7 @@ import org.geysermc.geyser.entity.type.Tickable;
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
import org.geysermc.geyser.entity.vehicle.ClientVehicle; import org.geysermc.geyser.entity.vehicle.ClientVehicle;
import org.geysermc.geyser.erosion.AbstractGeyserboundPacketHandler; import org.geysermc.geyser.erosion.AbstractGeyserboundPacketHandler;
import org.geysermc.geyser.erosion.ErosionCancellationException;
import org.geysermc.geyser.erosion.GeyserboundHandshakePacketHandler; import org.geysermc.geyser.erosion.GeyserboundHandshakePacketHandler;
import org.geysermc.geyser.impl.camera.CameraDefinitions; import org.geysermc.geyser.impl.camera.CameraDefinitions;
import org.geysermc.geyser.impl.camera.GeyserCameraData; import org.geysermc.geyser.impl.camera.GeyserCameraData;
@ -228,6 +232,7 @@ import java.util.Queue;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -258,7 +263,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
@NonNull @NonNull
@Setter @Setter
private AbstractGeyserboundPacketHandler erosionHandler; private volatile AbstractGeyserboundPacketHandler erosionHandler;
@Accessors(fluent = true) @Accessors(fluent = true)
@Setter @Setter
@ -567,16 +572,16 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
private float walkSpeed; private float walkSpeed;
/** /**
* Caches current rain status. * Caches current rain strength.
* Value between 0 and 1.
*/ */
@Setter private float rainStrength = 0.0f;
private boolean raining = false;
/** /**
* Caches current thunder status. * Caches current thunder strength.
* Value between 0 and 1.
*/ */
@Setter private float thunderStrength = 0.0f;
private boolean thunder = false;
/** /**
* Stores a map of all statistics sent from the server. * Stores a map of all statistics sent from the server.
@ -876,7 +881,14 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
return task.getAuthentication().handle((result, ex) -> { return task.getAuthentication().handle((result, ex) -> {
if (ex != null) { if (ex != null) {
geyser.getLogger().error("Failed to log in with Microsoft code!", ex); geyser.getLogger().error("Failed to log in with Microsoft code!", ex);
disconnect(ex.toString()); if (ex instanceof CompletionException ce
&& ce.getCause() instanceof MinecraftRequestException mre
&& mre.getResponse().getStatusCode() == 404) {
// Player is trying to join with a Microsoft account that doesn't have Java Edition purchased
disconnect(GeyserLocale.getPlayerLocaleString("geyser.network.remote.invalid_account", locale()));
} else {
disconnect(ex.toString());
}
return false; return false;
} }
@ -1190,9 +1202,9 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
tickThread.cancel(false); tickThread.cancel(false);
} }
erosionHandler.close(); // Mark session as closed before cancelling erosion futures
closed = true; closed = true;
erosionHandler.close();
} }
/** /**
@ -1213,6 +1225,8 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
eventLoop.execute(() -> { eventLoop.execute(() -> {
try { try {
runnable.run(); runnable.run();
} catch (ErosionCancellationException e) {
geyser.getLogger().debug("Caught ErosionCancellationException");
} catch (Throwable e) { } catch (Throwable e) {
geyser.getLogger().error("Error thrown in " + this.bedrockUsername() + "'s event loop!", e); geyser.getLogger().error("Error thrown in " + this.bedrockUsername() + "'s event loop!", e);
} }
@ -1230,6 +1244,8 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
if (!closed) { if (!closed) {
runnable.run(); runnable.run();
} }
} catch (ErosionCancellationException e) {
geyser.getLogger().debug("Caught ErosionCancellationException");
} catch (Throwable e) { } catch (Throwable e) {
geyser.getLogger().error("Error thrown in " + this.bedrockUsername() + "'s event loop!", e); geyser.getLogger().error("Error thrown in " + this.bedrockUsername() + "'s event loop!", e);
} }
@ -2002,6 +2018,71 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
}; };
} }
/**
* Sends a packet to update rain strength.
* Stops rain if strength is 0.
*
* @param strength value between 0 and 1
*/
public void updateRain(float strength) {
boolean wasRaining = isRaining();
this.rainStrength = strength;
LevelEventPacket rainPacket = new LevelEventPacket();
rainPacket.setType(isRaining() ? LevelEvent.START_RAINING : LevelEvent.STOP_RAINING);
rainPacket.setData((int) (strength * 65535));
rainPacket.setPosition(Vector3f.ZERO);
sendUpstreamPacket(rainPacket);
// Keep thunder in sync with rain when starting/stopping a storm
if ((wasRaining != isRaining()) && isThunder()) {
if (isRaining()) {
LevelEventPacket thunderPacket = new LevelEventPacket();
thunderPacket.setType(LevelEvent.START_THUNDERSTORM);
thunderPacket.setData((int) (this.thunderStrength * 65535));
thunderPacket.setPosition(Vector3f.ZERO);
sendUpstreamPacket(thunderPacket);
} else {
LevelEventPacket thunderPacket = new LevelEventPacket();
thunderPacket.setType(LevelEvent.STOP_THUNDERSTORM);
thunderPacket.setData(0);
thunderPacket.setPosition(Vector3f.ZERO);
sendUpstreamPacket(thunderPacket);
}
}
}
/**
* Sends a packet to update thunderstorm strength.
* Stops thunderstorm if strength is 0.
*
* @param strength value between 0 and 1
*/
public void updateThunder(float strength) {
this.thunderStrength = strength;
// Do not send thunder packet if not raining
// The bedrock client will start raining automatically when updating thunder strength
// https://github.com/GeyserMC/Geyser/issues/3679
if (!isRaining()) {
return;
}
LevelEventPacket thunderPacket = new LevelEventPacket();
thunderPacket.setType(isThunder() ? LevelEvent.START_THUNDERSTORM : LevelEvent.STOP_THUNDERSTORM);
thunderPacket.setData((int) (strength * 65535));
thunderPacket.setPosition(Vector3f.ZERO);
sendUpstreamPacket(thunderPacket);
}
public boolean isRaining() {
return this.rainStrength > 0;
}
public boolean isThunder() {
return this.thunderStrength > 0;
}
@Override @Override
public @NonNull String bedrockUsername() { public @NonNull String bedrockUsername() {
return authData.name(); return authData.name();

Datei anzeigen

@ -49,7 +49,7 @@ import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.registry.JavaRegistry; import org.geysermc.geyser.session.cache.registry.JavaRegistry;
import org.geysermc.geyser.session.cache.registry.RegistryEntryContext; import org.geysermc.geyser.session.cache.registry.RegistryEntryContext;
import org.geysermc.geyser.session.cache.registry.SimpleJavaRegistry; import org.geysermc.geyser.session.cache.registry.SimpleJavaRegistry;
import org.geysermc.geyser.text.TextDecoration; import org.geysermc.geyser.text.ChatDecoration;
import org.geysermc.geyser.translator.level.BiomeTranslator; import org.geysermc.geyser.translator.level.BiomeTranslator;
import org.geysermc.geyser.util.MinecraftKey; import org.geysermc.geyser.util.MinecraftKey;
import org.geysermc.mcprotocollib.protocol.MinecraftProtocol; import org.geysermc.mcprotocollib.protocol.MinecraftProtocol;
@ -78,7 +78,7 @@ public final class RegistryCache {
private static final Map<Key, BiConsumer<RegistryCache, List<RegistryEntry>>> REGISTRIES = new HashMap<>(); private static final Map<Key, BiConsumer<RegistryCache, List<RegistryEntry>>> REGISTRIES = new HashMap<>();
static { static {
register("chat_type", cache -> cache.chatTypes, TextDecoration::readChatType); register("chat_type", cache -> cache.chatTypes, ChatDecoration::readChatType);
register("dimension_type", cache -> cache.dimensions, JavaDimension::read); register("dimension_type", cache -> cache.dimensions, JavaDimension::read);
register("enchantment", cache -> cache.enchantments, Enchantment::read); register("enchantment", cache -> cache.enchantments, Enchantment::read);
register("jukebox_song", cache -> cache.jukeboxSongs, JukeboxSong::read); register("jukebox_song", cache -> cache.jukeboxSongs, JukeboxSong::read);

Datei anzeigen

@ -34,6 +34,7 @@ import lombok.Setter;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.math.vector.Vector3i; import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.protocol.bedrock.packet.SetTitlePacket; import org.cloudburstmc.protocol.bedrock.packet.SetTitlePacket;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.scoreboard.Scoreboard; import org.geysermc.geyser.scoreboard.Scoreboard;
import org.geysermc.geyser.scoreboard.ScoreboardUpdater.ScoreboardSession; import org.geysermc.geyser.scoreboard.ScoreboardUpdater.ScoreboardSession;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
@ -70,6 +71,8 @@ public final class WorldCache {
@Setter @Setter
private boolean editingSignOnFront; private boolean editingSignOnFront;
private final Object2IntMap<Item> activeCooldowns = new Object2IntOpenHashMap<>(2);
public WorldCache(GeyserSession session) { public WorldCache(GeyserSession session) {
this.session = session; this.session = session;
this.scoreboard = new Scoreboard(session); this.scoreboard = new Scoreboard(session);
@ -201,4 +204,32 @@ public final class WorldCache {
public String removeActiveRecord(Vector3i pos) { public String removeActiveRecord(Vector3i pos) {
return this.activeRecords.remove(pos); return this.activeRecords.remove(pos);
} }
public void setCooldown(Item item, int ticks) {
if (ticks == 0) {
// As of Java 1.21
this.activeCooldowns.removeInt(item);
return;
}
this.activeCooldowns.put(item, session.getTicks() + ticks);
}
public boolean hasCooldown(Item item) {
return this.activeCooldowns.containsKey(item);
}
public void tick() {
// Implementation note: technically we could empty the field during hasCooldown checks,
// but we don't want the cooldown field to balloon in size from overuse.
if (!this.activeCooldowns.isEmpty()) {
int ticks = session.getTicks();
Iterator<Object2IntMap.Entry<Item>> it = Object2IntMaps.fastIterator(this.activeCooldowns);
while (it.hasNext()) {
Object2IntMap.Entry<Item> entry = it.next();
if (entry.getIntValue() <= ticks) {
it.remove();
}
}
}
}
} }

Datei anzeigen

@ -25,17 +25,19 @@
package org.geysermc.geyser.text; package org.geysermc.geyser.text;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.Style; import net.kyori.adventure.text.format.Style;
import org.cloudburstmc.nbt.NbtMap; import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtType; import org.cloudburstmc.nbt.NbtType;
import org.geysermc.geyser.session.cache.registry.RegistryEntryContext; import org.geysermc.geyser.session.cache.registry.RegistryEntryContext;
import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.mcprotocollib.protocol.data.game.chat.ChatType; import org.geysermc.mcprotocollib.protocol.data.game.chat.ChatType;
import org.geysermc.mcprotocollib.protocol.data.game.chat.ChatTypeDecoration; import org.geysermc.mcprotocollib.protocol.data.game.chat.ChatTypeDecoration;
import java.util.*; import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
public record TextDecoration(String translationKey, List<Parameter> parameters, Style deserializedStyle) implements ChatTypeDecoration { public record ChatDecoration(String translationKey, List<Parameter> parameters, Style deserializedStyle) implements ChatTypeDecoration {
@Override @Override
public NbtMap style() { public NbtMap style() {
@ -53,38 +55,22 @@ public record TextDecoration(String translationKey, List<Parameter> parameters,
String translationKey = chat.getString("translation_key"); String translationKey = chat.getString("translation_key");
NbtMap styleTag = chat.getCompound("style"); NbtMap styleTag = chat.getCompound("style");
Style style = deserializeStyle(styleTag); Style style = MessageTranslator.getStyleFromNbtMap(styleTag);
List<ChatTypeDecoration.Parameter> parameters = new ArrayList<>(); List<ChatTypeDecoration.Parameter> parameters = new ArrayList<>();
List<String> parametersNbt = chat.getList("parameters", NbtType.STRING); List<String> parametersNbt = chat.getList("parameters", NbtType.STRING);
for (String parameter : parametersNbt) { for (String parameter : parametersNbt) {
parameters.add(ChatTypeDecoration.Parameter.valueOf(parameter.toUpperCase(Locale.ROOT))); parameters.add(ChatTypeDecoration.Parameter.valueOf(parameter.toUpperCase(Locale.ROOT)));
} }
return new ChatType(new TextDecoration(translationKey, parameters, style), null); return new ChatType(new ChatDecoration(translationKey, parameters, style), null);
} }
return new ChatType(null, null); return new ChatType(null, null);
} }
public static Style getStyle(ChatTypeDecoration decoration) { public static Style getStyle(ChatTypeDecoration decoration) {
if (decoration instanceof TextDecoration textDecoration) { if (decoration instanceof ChatDecoration chatDecoration) {
return textDecoration.deserializedStyle(); return chatDecoration.deserializedStyle();
} }
return deserializeStyle(decoration.style()); return MessageTranslator.getStyleFromNbtMap(decoration.style());
}
private static Style deserializeStyle(NbtMap styleTag) {
Style.Builder builder = Style.style();
if (!styleTag.isEmpty()) {
String color = styleTag.getString("color", null);
if (color != null) {
builder.color(NamedTextColor.NAMES.value(color));
}
//TODO implement the rest
boolean italic = styleTag.getBoolean("italic");
if (italic) {
builder.decorate(net.kyori.adventure.text.format.TextDecoration.ITALIC);
}
}
return builder.build();
} }
} }

Datei anzeigen

@ -201,6 +201,9 @@ public abstract class InventoryTranslator {
public ItemStackResponse translateRequest(GeyserSession session, Inventory inventory, ItemStackRequest request) { public ItemStackResponse translateRequest(GeyserSession session, Inventory inventory, ItemStackRequest request) {
ClickPlan plan = new ClickPlan(session, this, inventory); ClickPlan plan = new ClickPlan(session, this, inventory);
IntSet affectedSlots = new IntOpenHashSet(); IntSet affectedSlots = new IntOpenHashSet();
int pendingOutput = 0;
int savedTempSlot = -1;
for (ItemStackRequestAction action : request.getActions()) { for (ItemStackRequestAction action : request.getActions()) {
GeyserItemStack cursor = session.getPlayerInventory().getCursor(); GeyserItemStack cursor = session.getPlayerInventory().getCursor();
switch (action.getType()) { switch (action.getType()) {
@ -241,6 +244,65 @@ public abstract class InventoryTranslator {
return rejectRequest(request, false); return rejectRequest(request, false);
} }
// Handle partial transfer of output slot
if (pendingOutput == 0 && !isSourceCursor && getSlotType(sourceSlot) == SlotType.OUTPUT
&& transferAction.getCount() < plan.getItem(sourceSlot).getAmount()) {
// Cursor as dest should always be full transfer.
if (isDestCursor) {
return rejectRequest(request);
}
if (!plan.getCursor().isEmpty()) {
savedTempSlot = findTempSlot(plan, plan.getCursor(), true);
if (savedTempSlot == -1) {
return rejectRequest(request);
}
plan.add(Click.LEFT, savedTempSlot);
}
// Pickup entire stack from output
pendingOutput = plan.getItem(sourceSlot).getAmount();
plan.add(Click.LEFT, sourceSlot);
}
// Continue transferring items from output that is currently stored in the cursor
if (pendingOutput > 0) {
if (isSourceCursor || getSlotType(sourceSlot) != SlotType.OUTPUT
|| transferAction.getCount() > pendingOutput
|| destSlot == savedTempSlot
|| isDestCursor) {
return rejectRequest(request);
}
// Make sure item can be placed here
GeyserItemStack destItem = plan.getItem(destSlot);
if (!destItem.isEmpty() && !InventoryUtils.canStack(destItem, plan.getCursor())) {
return rejectRequest(request);
}
// TODO: Optimize using max stack size
if (pendingOutput == transferAction.getCount()) {
plan.add(Click.LEFT, destSlot);
} else {
for (int i = 0; i < transferAction.getCount(); i++) {
plan.add(Click.RIGHT, destSlot);
}
}
pendingOutput -= transferAction.getCount();
if (pendingOutput != plan.getCursor().getAmount()) {
return rejectRequest(request);
}
if (pendingOutput == 0 && savedTempSlot != -1) {
plan.add(Click.LEFT, savedTempSlot);
savedTempSlot = -1;
}
// Skip to next action
continue;
}
if (isSourceCursor && isDestCursor) { //??? if (isSourceCursor && isDestCursor) { //???
return rejectRequest(request); return rejectRequest(request);
} else if (isSourceCursor) { //releasing cursor } else if (isSourceCursor) { //releasing cursor
@ -271,7 +333,7 @@ public abstract class InventoryTranslator {
return rejectRequest(request); return rejectRequest(request);
} }
if (transferAction.getCount() != sourceAmount) { if (transferAction.getCount() != sourceAmount) {
int tempSlot = findTempSlot(inventory, cursor, false, sourceSlot); int tempSlot = findTempSlot(plan, cursor, false, sourceSlot);
if (tempSlot == -1) { if (tempSlot == -1) {
return rejectRequest(request); return rejectRequest(request);
} }
@ -292,7 +354,7 @@ public abstract class InventoryTranslator {
} else { //transfer from one slot to another } else { //transfer from one slot to another
int tempSlot = -1; int tempSlot = -1;
if (!plan.getCursor().isEmpty()) { if (!plan.getCursor().isEmpty()) {
tempSlot = findTempSlot(inventory, cursor, false, sourceSlot, destSlot); tempSlot = findTempSlot(plan, cursor, getSlotType(sourceSlot) != SlotType.NORMAL, sourceSlot, destSlot);
if (tempSlot == -1) { if (tempSlot == -1) {
return rejectRequest(request); return rejectRequest(request);
} }
@ -440,6 +502,11 @@ public abstract class InventoryTranslator {
return rejectRequest(request); return rejectRequest(request);
} }
} }
if (pendingOutput != 0) {
return rejectRequest(request);
}
plan.execute(false); plan.execute(false);
affectedSlots.addAll(plan.getAffectedSlots()); affectedSlots.addAll(plan.getAffectedSlots());
return acceptRequest(request, makeContainerEntries(session, inventory, affectedSlots)); return acceptRequest(request, makeContainerEntries(session, inventory, affectedSlots));
@ -536,7 +603,7 @@ public abstract class InventoryTranslator {
} }
} else { } else {
GeyserItemStack cursor = session.getPlayerInventory().getCursor(); GeyserItemStack cursor = session.getPlayerInventory().getCursor();
int tempSlot = findTempSlot(inventory, cursor, true, sourceSlot, destSlot); int tempSlot = findTempSlot(plan, cursor, true, sourceSlot, destSlot);
if (tempSlot == -1) { if (tempSlot == -1) {
return rejectRequest(request); return rejectRequest(request);
} }
@ -699,7 +766,7 @@ public abstract class InventoryTranslator {
int javaSlot = bedrockSlotToJava(transferAction.getDestination()); int javaSlot = bedrockSlotToJava(transferAction.getDestination());
if (isCursor(transferAction.getDestination())) { //TODO if (isCursor(transferAction.getDestination())) { //TODO
if (timesCrafted > 1) { if (timesCrafted > 1) {
tempSlot = findTempSlot(inventory, GeyserItemStack.from(output), true); tempSlot = findTempSlot(plan, GeyserItemStack.from(output), true);
if (tempSlot == -1) { if (tempSlot == -1) {
return rejectRequest(request); return rejectRequest(request);
} }
@ -836,49 +903,68 @@ public abstract class InventoryTranslator {
} }
/** /**
* Try to find a slot that can temporarily store the given item. * Try to find a slot that is preferably empty, or does not stack with a given item.
* Only looks in the main inventory and hotbar (excluding offhand). * Only looks in the main inventory and hotbar (excluding offhand).
* Only slots that are empty or contain a different type of item are valid. * <p>
* Slots are searched in the reverse order that the bedrock client uses for quick moving.
* *
* @return java id for the temporary slot, or -1 if no viable slot was found * @param plan used to check the simulated inventory
* @param item the item to temporarily store
* @param emptyOnly if only empty slots should be considered
* @param slotBlacklist list of slots to exclude; the items contained in these slots will also be checked for stacking
* @return the temp slot, or -1 if no suitable slot was found
*/ */
//TODO: compatibility for simulated inventory (ClickPlan) private static int findTempSlot(ClickPlan plan, GeyserItemStack item, boolean emptyOnly, int... slotBlacklist) {
private static int findTempSlot(Inventory inventory, GeyserItemStack item, boolean emptyOnly, int... slotBlacklist) { IntSortedSet potentialSlots = new IntLinkedOpenHashSet(PLAYER_INVENTORY_SIZE);
int offset = inventory.getJavaId() == 0 ? 1 : 0; //offhand is not a viable temp slot int hotbarOffset = plan.getInventory().getOffsetForHotbar(0);
HashSet<GeyserItemStack> itemBlacklist = new HashSet<>(slotBlacklist.length + 1);
itemBlacklist.add(item);
IntSet potentialSlots = new IntOpenHashSet(36); // Add main inventory slots in reverse
for (int i = inventory.getSize() - (36 + offset); i < inventory.getSize() - offset; i++) { for (int i = hotbarOffset - 1; i >= hotbarOffset - 27; i--) {
potentialSlots.add(i); potentialSlots.add(i);
} }
// Add hotbar slots in reverse
for (int i = hotbarOffset + 8; i >= hotbarOffset; i--) {
potentialSlots.add(i);
}
for (int i : slotBlacklist) { for (int i : slotBlacklist) {
potentialSlots.remove(i); potentialSlots.remove(i);
GeyserItemStack blacklistedItem = inventory.getItem(i); }
if (!blacklistedItem.isEmpty()) {
itemBlacklist.add(blacklistedItem); // Prefer empty slots
IntIterator it = potentialSlots.iterator();
while (it.hasNext()) {
int slot = it.nextInt();
if (plan.isEmpty(slot)) {
return slot;
} }
} }
for (int i : potentialSlots) { if (emptyOnly) {
GeyserItemStack testItem = inventory.getItem(i); return -1;
if ((emptyOnly && !testItem.isEmpty())) { }
// No empty slots. Look for a slot that does not stack
it = potentialSlots.iterator();
outer:
while (it.hasNext()) {
int slot = it.nextInt();
if (plan.canStack(slot, item)) {
continue; continue;
} }
boolean viable = true; for (int blacklistedSlot : slotBlacklist) {
for (GeyserItemStack blacklistedItem : itemBlacklist) { GeyserItemStack blacklistedItem = plan.getItem(blacklistedSlot);
if (InventoryUtils.canStack(testItem, blacklistedItem)) { if (plan.canStack(slot, blacklistedItem)) {
viable = false; continue outer;
break;
} }
} }
if (!viable) {
continue; return slot;
}
return i;
} }
//could not find a viable temp slot
return -1; return -1;
} }
@ -895,11 +981,11 @@ public abstract class InventoryTranslator {
List<ItemStackResponseContainer> containerEntries = new ArrayList<>(); List<ItemStackResponseContainer> containerEntries = new ArrayList<>();
for (Map.Entry<ContainerSlotType, List<ItemStackResponseSlot>> entry : containerMap.entrySet()) { for (Map.Entry<ContainerSlotType, List<ItemStackResponseSlot>> entry : containerMap.entrySet()) {
containerEntries.add(new ItemStackResponseContainer(entry.getKey(), entry.getValue(), new FullContainerName(entry.getKey(), 0))); containerEntries.add(new ItemStackResponseContainer(entry.getKey(), entry.getValue(), new FullContainerName(entry.getKey(), null)));
} }
ItemStackResponseSlot cursorEntry = makeItemEntry(0, session.getPlayerInventory().getCursor()); ItemStackResponseSlot cursorEntry = makeItemEntry(0, session.getPlayerInventory().getCursor());
containerEntries.add(new ItemStackResponseContainer(ContainerSlotType.CURSOR, Collections.singletonList(cursorEntry), new FullContainerName(ContainerSlotType.CURSOR, 0))); containerEntries.add(new ItemStackResponseContainer(ContainerSlotType.CURSOR, Collections.singletonList(cursorEntry), new FullContainerName(ContainerSlotType.CURSOR, null)));
return containerEntries; return containerEntries;
} }

Datei anzeigen

@ -28,6 +28,7 @@ package org.geysermc.geyser.translator.inventory;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerId; import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerId;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType; import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType; import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType;
import org.cloudburstmc.protocol.bedrock.data.inventory.FullContainerName;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.ItemStackRequest; import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.ItemStackRequest;
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.ItemStackRequestSlotData; import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.ItemStackRequestSlotData;
@ -139,6 +140,7 @@ public class OldSmithingTableTranslator extends AbstractBlockInventoryTranslator
slotPacket.setContainerId(ContainerId.UI); slotPacket.setContainerId(ContainerId.UI);
slotPacket.setSlot(53); slotPacket.setSlot(53);
slotPacket.setItem(UPGRADE_TEMPLATE.apply(session.getUpstream().getProtocolVersion())); slotPacket.setItem(UPGRADE_TEMPLATE.apply(session.getUpstream().getProtocolVersion()));
slotPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null));
session.sendUpstreamPacket(slotPacket); session.sendUpstreamPacket(slotPacket);
} }
} }

Datei anzeigen

@ -30,6 +30,7 @@ import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet; import it.unimi.dsi.fastutil.ints.IntSet;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerId; import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerId;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType; import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType;
import org.cloudburstmc.protocol.bedrock.data.inventory.FullContainerName;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.ItemStackRequest; import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.ItemStackRequest;
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.ItemStackRequestSlotData; import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.ItemStackRequestSlotData;
@ -83,6 +84,7 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
contents[i - 36] = inventory.getItem(i).getItemData(session); contents[i - 36] = inventory.getItem(i).getItemData(session);
} }
inventoryContentPacket.setContents(Arrays.asList(contents)); inventoryContentPacket.setContents(Arrays.asList(contents));
inventoryContentPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null));
session.sendUpstreamPacket(inventoryContentPacket); session.sendUpstreamPacket(inventoryContentPacket);
// Armor // Armor
@ -99,12 +101,14 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
} }
} }
armorContentPacket.setContents(Arrays.asList(contents)); armorContentPacket.setContents(Arrays.asList(contents));
armorContentPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null));
session.sendUpstreamPacket(armorContentPacket); session.sendUpstreamPacket(armorContentPacket);
// Offhand // Offhand
InventoryContentPacket offhandPacket = new InventoryContentPacket(); InventoryContentPacket offhandPacket = new InventoryContentPacket();
offhandPacket.setContainerId(ContainerId.OFFHAND); offhandPacket.setContainerId(ContainerId.OFFHAND);
offhandPacket.setContents(Collections.singletonList(inventory.getItem(45).getItemData(session))); offhandPacket.setContents(Collections.singletonList(inventory.getItem(45).getItemData(session)));
offhandPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null));
session.sendUpstreamPacket(offhandPacket); session.sendUpstreamPacket(offhandPacket);
} }
@ -123,9 +127,10 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
if (session.getGameMode() == GameMode.CREATIVE) { if (session.getGameMode() == GameMode.CREATIVE) {
slotPacket.setItem(UNUSUABLE_CRAFTING_SPACE_BLOCK.apply(session.getUpstream().getProtocolVersion())); slotPacket.setItem(UNUSUABLE_CRAFTING_SPACE_BLOCK.apply(session.getUpstream().getProtocolVersion()));
} else { } else {
slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(i).getItemStack())); slotPacket.setItem(inventory.getItem(i).getItemData(session));
} }
slotPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null));
session.sendUpstreamPacket(slotPacket); session.sendUpstreamPacket(slotPacket);
} }
} }
@ -162,11 +167,13 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
slotPacket.setSlot(slot + 27); slotPacket.setSlot(slot + 27);
} }
slotPacket.setItem(bedrockItem); slotPacket.setItem(bedrockItem);
slotPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null));
session.sendUpstreamPacket(slotPacket); session.sendUpstreamPacket(slotPacket);
} else if (slot == 45) { } else if (slot == 45) {
InventoryContentPacket offhandPacket = new InventoryContentPacket(); InventoryContentPacket offhandPacket = new InventoryContentPacket();
offhandPacket.setContainerId(ContainerId.OFFHAND); offhandPacket.setContainerId(ContainerId.OFFHAND);
offhandPacket.setContents(Collections.singletonList(bedrockItem)); offhandPacket.setContents(Collections.singletonList(bedrockItem));
offhandPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null));
session.sendUpstreamPacket(offhandPacket); session.sendUpstreamPacket(offhandPacket);
} }
} }

Datei anzeigen

@ -27,6 +27,7 @@ package org.geysermc.geyser.translator.inventory.horse;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerId; import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerId;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType; import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType;
import org.cloudburstmc.protocol.bedrock.data.inventory.FullContainerName;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.ItemStackRequestSlotData; import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.ItemStackRequestSlotData;
import org.cloudburstmc.protocol.bedrock.packet.InventoryContentPacket; import org.cloudburstmc.protocol.bedrock.packet.InventoryContentPacket;
@ -94,6 +95,7 @@ public abstract class ChestedHorseInventoryTranslator extends AbstractHorseInven
InventoryContentPacket contentPacket = new InventoryContentPacket(); InventoryContentPacket contentPacket = new InventoryContentPacket();
contentPacket.setContainerId(ContainerId.INVENTORY); contentPacket.setContainerId(ContainerId.INVENTORY);
contentPacket.setContents(Arrays.asList(bedrockItems)); contentPacket.setContents(Arrays.asList(bedrockItems));
contentPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null));
session.sendUpstreamPacket(contentPacket); session.sendUpstreamPacket(contentPacket);
ItemData[] horseItems = new ItemData[chestSize + 1]; ItemData[] horseItems = new ItemData[chestSize + 1];
@ -107,6 +109,7 @@ public abstract class ChestedHorseInventoryTranslator extends AbstractHorseInven
InventoryContentPacket horseContentsPacket = new InventoryContentPacket(); InventoryContentPacket horseContentsPacket = new InventoryContentPacket();
horseContentsPacket.setContainerId(inventory.getBedrockId()); horseContentsPacket.setContainerId(inventory.getBedrockId());
horseContentsPacket.setContents(Arrays.asList(horseItems)); horseContentsPacket.setContents(Arrays.asList(horseItems));
horseContentsPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null));
session.sendUpstreamPacket(horseContentsPacket); session.sendUpstreamPacket(horseContentsPacket);
} }
} }

Datei anzeigen

@ -31,6 +31,7 @@ import org.cloudburstmc.math.vector.Vector3d;
import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.math.vector.Vector3i; import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.protocol.bedrock.data.LevelEvent; import org.cloudburstmc.protocol.bedrock.data.LevelEvent;
import org.cloudburstmc.protocol.bedrock.data.SoundEvent;
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition; import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
import org.cloudburstmc.protocol.bedrock.data.definitions.ItemDefinition; import org.cloudburstmc.protocol.bedrock.data.definitions.ItemDefinition;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType; import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType;
@ -42,6 +43,7 @@ import org.cloudburstmc.protocol.bedrock.data.inventory.transaction.LegacySetIte
import org.cloudburstmc.protocol.bedrock.packet.ContainerOpenPacket; import org.cloudburstmc.protocol.bedrock.packet.ContainerOpenPacket;
import org.cloudburstmc.protocol.bedrock.packet.InventoryTransactionPacket; import org.cloudburstmc.protocol.bedrock.packet.InventoryTransactionPacket;
import org.cloudburstmc.protocol.bedrock.packet.LevelEventPacket; import org.cloudburstmc.protocol.bedrock.packet.LevelEventPacket;
import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEventPacket;
import org.cloudburstmc.protocol.bedrock.packet.UpdateBlockPacket; import org.cloudburstmc.protocol.bedrock.packet.UpdateBlockPacket;
import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.EntityDefinitions;
import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.type.Entity;
@ -75,12 +77,15 @@ import org.geysermc.geyser.util.CooldownUtils;
import org.geysermc.geyser.util.EntityUtils; import org.geysermc.geyser.util.EntityUtils;
import org.geysermc.geyser.util.InteractionResult; import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InventoryUtils; import org.geysermc.geyser.util.InventoryUtils;
import org.geysermc.mcprotocollib.protocol.data.game.Holder;
import org.geysermc.mcprotocollib.protocol.data.game.entity.object.Direction; import org.geysermc.mcprotocollib.protocol.data.game.entity.object.Direction;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.InteractAction; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.InteractAction;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.PlayerAction; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.PlayerAction;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack; import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.Instrument;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundContainerClickPacket; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundContainerClickPacket;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundInteractPacket; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundInteractPacket;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosRotPacket; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosRotPacket;
@ -373,6 +378,22 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
break; break;
} else if (packet.getItemInHand().getDefinition() == session.getItemMappings().getStoredItems().writtenBook().getBedrockDefinition()) { } else if (packet.getItemInHand().getDefinition() == session.getItemMappings().getStoredItems().writtenBook().getBedrockDefinition()) {
session.setCurrentBook(packet.getItemInHand()); session.setCurrentBook(packet.getItemInHand());
} else if (session.getPlayerInventory().getItemInHand().asItem() == Items.GOAT_HORN) {
// Temporary workaround while we don't have full item/block use tracking.
if (!session.getWorldCache().hasCooldown(Items.GOAT_HORN)) {
Holder<Instrument> instrument = session.getPlayerInventory()
.getItemInHand()
.getComponent(DataComponentType.INSTRUMENT);
if (instrument != null && instrument.isId()) {
// BDS uses a LevelSoundEvent2Packet, but that doesn't work here... (as of 1.21.20)
LevelSoundEventPacket soundPacket = new LevelSoundEventPacket();
soundPacket.setSound(SoundEvent.valueOf("GOAT_CALL_" + instrument.id()));
soundPacket.setPosition(session.getPlayerEntity().getPosition());
soundPacket.setIdentifier("minecraft:player");
soundPacket.setExtraData(-1);
session.sendUpstreamPacket(soundPacket);
}
}
} }
} }

Datei anzeigen

@ -58,6 +58,14 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
session.confirmTeleport(packet.getPosition().toDouble().sub(0, EntityDefinitions.PLAYER.offset(), 0)); session.confirmTeleport(packet.getPosition().toDouble().sub(0, EntityDefinitions.PLAYER.offset(), 0));
return; return;
} }
if (entity.getBedPosition() != null) {
// https://github.com/GeyserMC/Geyser/issues/5001
// Bedrock 1.21.22 started sending a MovePlayerPacket as soon as it got into a bed.
// This trips up Fabric.
return;
}
float yaw = packet.getRotation().getY(); float yaw = packet.getRotation().getY();
float pitch = packet.getRotation().getX(); float pitch = packet.getRotation().getX();
float headYaw = packet.getRotation().getY(); float headYaw = packet.getRotation().getY();

Datei anzeigen

@ -88,6 +88,7 @@ public class JavaCommandsTranslator extends PacketTranslator<ClientboundCommands
if (a == null || b == null) return false; if (a == null || b == null) return false;
if ("help".equals(a.name()) && !"help".equals(b.name())) { if ("help".equals(a.name()) && !"help".equals(b.name())) {
// Merging this causes Bedrock to fallback to its own help command // Merging this causes Bedrock to fallback to its own help command
// Tested on Paper 1.20.4 with Essentials and Bedrock 1.21
// https://github.com/GeyserMC/Geyser/issues/2573 // https://github.com/GeyserMC/Geyser/issues/2573
return false; return false;
} }
@ -124,8 +125,9 @@ public class JavaCommandsTranslator extends PacketTranslator<ClientboundCommands
if (!session.getGeyser().getConfig().isCommandSuggestions()) { if (!session.getGeyser().getConfig().isCommandSuggestions()) {
session.getGeyser().getLogger().debug("Not sending translated command suggestions as they are disabled."); session.getGeyser().getLogger().debug("Not sending translated command suggestions as they are disabled.");
// Send an empty packet so Bedrock doesn't override /help with its own, built-in help command. // Send a mostly empty packet so Bedrock doesn't override /help with its own, built-in help command.
AvailableCommandsPacket emptyPacket = new AvailableCommandsPacket(); AvailableCommandsPacket emptyPacket = new AvailableCommandsPacket();
emptyPacket.getCommands().add(createFakeHelpCommand());
session.sendUpstreamPacket(emptyPacket); session.sendUpstreamPacket(emptyPacket);
return; return;
} }
@ -182,6 +184,8 @@ public class JavaCommandsTranslator extends PacketTranslator<ClientboundCommands
// The command flags, set to NOT_CHEAT so known commands can be used while achievements are enabled. // The command flags, set to NOT_CHEAT so known commands can be used while achievements are enabled.
Set<CommandData.Flag> flags = Set.of(CommandData.Flag.NOT_CHEAT); Set<CommandData.Flag> flags = Set.of(CommandData.Flag.NOT_CHEAT);
boolean helpAdded = false;
// Loop through all the found commands // Loop through all the found commands
for (Map.Entry<BedrockCommandInfo, Set<String>> entry : commands.entrySet()) { for (Map.Entry<BedrockCommandInfo, Set<String>> entry : commands.entrySet()) {
String commandName = entry.getValue().iterator().next(); // We know this has a value String commandName = entry.getValue().iterator().next(); // We know this has a value
@ -198,6 +202,15 @@ public class JavaCommandsTranslator extends PacketTranslator<ClientboundCommands
// Build the completed command and add it to the final list // Build the completed command and add it to the final list
CommandData data = new CommandData(commandName, entry.getKey().description(), flags, CommandPermission.ANY, aliases, Collections.emptyList(), entry.getKey().paramData()); CommandData data = new CommandData(commandName, entry.getKey().description(), flags, CommandPermission.ANY, aliases, Collections.emptyList(), entry.getKey().paramData());
commandData.add(data); commandData.add(data);
if (commandName.equals("help")) {
helpAdded = true;
}
}
if (!helpAdded) {
// https://github.com/GeyserMC/Geyser/issues/2573 if Brigadier does not send the help command.
commandData.add(createFakeHelpCommand());
} }
// Add our commands to the AvailableCommandsPacket for the bedrock client // Add our commands to the AvailableCommandsPacket for the bedrock client
@ -286,6 +299,11 @@ public class JavaCommandsTranslator extends PacketTranslator<ClientboundCommands
}; };
} }
private CommandData createFakeHelpCommand() {
CommandEnumData aliases = new CommandEnumData("helpAliases", Map.of("help", EnumSet.of(CommandEnumConstraint.ALLOW_ALIASES)), false);
return new CommandData("help", "", Set.of(CommandData.Flag.NOT_CHEAT), CommandPermission.ANY, aliases, Collections.emptyList(), new CommandOverloadData[0]);
}
/** /**
* Stores the command description and parameter data for best optimizing the Bedrock commands packet. * Stores the command description and parameter data for best optimizing the Bedrock commands packet.
*/ */

Datei anzeigen

@ -33,7 +33,6 @@ import org.geysermc.erosion.Constants;
import org.geysermc.floodgate.pluginmessage.PluginMessageChannels; import org.geysermc.floodgate.pluginmessage.PluginMessageChannels;
import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.api.network.AuthType;
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
import org.geysermc.geyser.erosion.GeyserboundHandshakePacketHandler;
import org.geysermc.geyser.level.JavaDimension; import org.geysermc.geyser.level.JavaDimension;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.PacketTranslator;
@ -57,11 +56,6 @@ public class JavaLoginTranslator extends PacketTranslator<ClientboundLoginPacket
SessionPlayerEntity entity = session.getPlayerEntity(); SessionPlayerEntity entity = session.getPlayerEntity();
entity.setEntityId(packet.getEntityId()); entity.setEntityId(packet.getEntityId());
if (session.getErosionHandler().isActive()) {
session.getErosionHandler().close();
session.setErosionHandler(new GeyserboundHandshakePacketHandler(session));
}
PlayerSpawnInfo spawnInfo = packet.getCommonPlayerSpawnInfo(); PlayerSpawnInfo spawnInfo = packet.getCommonPlayerSpawnInfo();
JavaDimension newDimension = session.getRegistryCache().dimensions().byId(spawnInfo.getDimension()); JavaDimension newDimension = session.getRegistryCache().dimensions().byId(spawnInfo.getDimension());

Datei anzeigen

@ -25,9 +25,6 @@
package org.geysermc.geyser.translator.protocol.java; package org.geysermc.geyser.translator.protocol.java;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.LevelEvent;
import org.cloudburstmc.protocol.bedrock.packet.LevelEventPacket;
import org.cloudburstmc.protocol.bedrock.packet.SetPlayerGameTypePacket; import org.cloudburstmc.protocol.bedrock.packet.SetPlayerGameTypePacket;
import org.geysermc.geyser.entity.attribute.GeyserAttributeType; import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
@ -76,21 +73,11 @@ public class JavaRespawnTranslator extends PacketTranslator<ClientboundRespawnPa
session.setGameMode(spawnInfo.getGameMode()); session.setGameMode(spawnInfo.getGameMode());
if (session.isRaining()) { if (session.isRaining()) {
LevelEventPacket stopRainPacket = new LevelEventPacket(); session.updateRain(0);
stopRainPacket.setType(LevelEvent.STOP_RAINING);
stopRainPacket.setData(0);
stopRainPacket.setPosition(Vector3f.ZERO);
session.sendUpstreamPacket(stopRainPacket);
session.setRaining(false);
} }
if (session.isThunder()) { if (session.isThunder()) {
LevelEventPacket stopThunderPacket = new LevelEventPacket(); session.updateThunder(0);
stopThunderPacket.setType(LevelEvent.STOP_THUNDERSTORM);
stopThunderPacket.setData(0);
stopThunderPacket.setPosition(Vector3f.ZERO);
session.sendUpstreamPacket(stopThunderPacket);
session.setThunder(false);
} }
JavaDimension newDimension = session.getRegistryCache().dimensions().byId(spawnInfo.getDimension()); JavaDimension newDimension = session.getRegistryCache().dimensions().byId(spawnInfo.getDimension());

Datei anzeigen

@ -0,0 +1,52 @@
/*
* Copyright (c) 2024 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.translator.protocol.java;
import org.geysermc.geyser.erosion.GeyserboundHandshakePacketHandler;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.ClientboundStartConfigurationPacket;
@Translator(packet = ClientboundStartConfigurationPacket.class)
public class JavaStartConfigurationTranslator extends PacketTranslator<ClientboundStartConfigurationPacket> {
@Override
public void translate(GeyserSession session, ClientboundStartConfigurationPacket packet) {
var erosionHandler = session.getErosionHandler();
if (erosionHandler.isActive()) {
// Set new handler before closing
session.setErosionHandler(new GeyserboundHandshakePacketHandler(session));
erosionHandler.close();
}
}
@Override
public boolean shouldExecuteInEventLoop() {
// Execute outside of event loop to cancel any pending erosion futures
return false;
}
}

Datei anzeigen

@ -25,8 +25,13 @@
package org.geysermc.geyser.translator.protocol.java; package org.geysermc.geyser.translator.protocol.java;
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.ClientboundSystemChatPacket; import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.TranslatableComponent; import net.kyori.adventure.text.TranslatableComponent;
import net.kyori.adventure.text.TranslationArgument;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.protocol.bedrock.data.LevelEvent;
import org.cloudburstmc.protocol.bedrock.packet.LevelEventGenericPacket;
import org.cloudburstmc.protocol.bedrock.packet.TextPacket; import org.cloudburstmc.protocol.bedrock.packet.TextPacket;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.text.ChatColor; import org.geysermc.geyser.text.ChatColor;
@ -34,18 +39,47 @@ import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator; import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.geyser.translator.text.MessageTranslator; import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.ClientboundSystemChatPacket;
@Translator(packet = ClientboundSystemChatPacket.class) @Translator(packet = ClientboundSystemChatPacket.class)
public class JavaSystemChatTranslator extends PacketTranslator<ClientboundSystemChatPacket> { public class JavaSystemChatTranslator extends PacketTranslator<ClientboundSystemChatPacket> {
@Override @Override
public void translate(GeyserSession session, ClientboundSystemChatPacket packet) { public void translate(GeyserSession session, ClientboundSystemChatPacket packet) {
if (packet.getContent() instanceof TranslatableComponent component && component.key().equals("chat.disabled.missingProfileKey")) { if (packet.getContent() instanceof TranslatableComponent component) {
// We likely got this message as a response to a player trying to chat if (component.key().equals("chat.disabled.missingProfileKey")) {
// As there SHOULD be no false flags for this, print every time it shows up in chat. // We likely got this message as a response to a player trying to chat
if (Boolean.parseBoolean(System.getProperty("Geyser.PrintSecureChatInformation", "true"))) { // As there SHOULD be no false flags for this, print every time it shows up in chat.
session.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.chat.secure_info_1", session.locale())); if (Boolean.parseBoolean(System.getProperty("Geyser.PrintSecureChatInformation", "true"))) {
session.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.chat.secure_info_2", session.locale(), "https://geysermc.link/secure-chat")); session.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.chat.secure_info_1", session.locale()));
session.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.chat.secure_info_2", session.locale(), "https://geysermc.link/secure-chat"));
}
} else if (component.key().equals("sleep.players_sleeping")) {
if (component.arguments().size() == 2) {
// Hack FYI, but it allows Bedrock players to easily understand this information
// without it being covered up or saying the night is being slept through.
Integer numPlayersSleeping = convertToInt(component.arguments().get(0));
Integer totalPlayersNeeded = convertToInt(component.arguments().get(1));
if (numPlayersSleeping != null && totalPlayersNeeded != null) {
LevelEventGenericPacket sleepInfoPacket = new LevelEventGenericPacket();
sleepInfoPacket.setType(LevelEvent.SLEEPING_PLAYERS);
sleepInfoPacket.setTag(NbtMap.builder()
.putInt("ableToSleep", totalPlayersNeeded)
.putInt("overworldPlayerCount", totalPlayersNeeded)
.putInt("sleepingPlayerCount", numPlayersSleeping)
.build());
session.sendUpstreamPacket(sleepInfoPacket);
}
}
} else if (component.key().equals("sleep.skipping_night")) {
LevelEventGenericPacket sleepInfoPacket = new LevelEventGenericPacket();
sleepInfoPacket.setType(LevelEvent.SLEEPING_PLAYERS);
sleepInfoPacket.setTag(NbtMap.builder()
.putInt("ableToSleep", 1)
.putInt("overworldPlayerCount", 1)
.putInt("sleepingPlayerCount", 1)
.build());
session.sendUpstreamPacket(sleepInfoPacket);
} }
} }
@ -68,4 +102,19 @@ public class JavaSystemChatTranslator extends PacketTranslator<ClientboundSystem
session.getUpstream().queuePostStartGamePacket(textPacket); session.getUpstream().queuePostStartGamePacket(textPacket);
} }
} }
private static @Nullable Integer convertToInt(TranslationArgument translationArgument) {
Object value = translationArgument.value();
if (value instanceof Number number) {
return number.intValue();
}
if (value instanceof TextComponent textComponent) {
try {
return Integer.parseInt(textComponent.content());
} catch (NumberFormatException e) {
// Ignore
}
}
return null;
}
} }

Datei anzeigen

@ -30,6 +30,8 @@ import org.cloudburstmc.protocol.bedrock.data.SoundEvent;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityEventType; import org.cloudburstmc.protocol.bedrock.data.entity.EntityEventType;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerId; import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerId;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType;
import org.cloudburstmc.protocol.bedrock.data.inventory.FullContainerName;
import org.cloudburstmc.protocol.bedrock.packet.EntityEventPacket; import org.cloudburstmc.protocol.bedrock.packet.EntityEventPacket;
import org.cloudburstmc.protocol.bedrock.packet.InventoryContentPacket; import org.cloudburstmc.protocol.bedrock.packet.InventoryContentPacket;
import org.cloudburstmc.protocol.bedrock.packet.LevelEventPacket; import org.cloudburstmc.protocol.bedrock.packet.LevelEventPacket;
@ -167,6 +169,7 @@ public class JavaEntityEventTranslator extends PacketTranslator<ClientboundEntit
InventoryContentPacket offhandPacket = new InventoryContentPacket(); InventoryContentPacket offhandPacket = new InventoryContentPacket();
offhandPacket.setContainerId(ContainerId.OFFHAND); offhandPacket.setContainerId(ContainerId.OFFHAND);
offhandPacket.setContents(Collections.singletonList(InventoryUtils.getTotemOfUndying().apply(session.getUpstream().getProtocolVersion()))); offhandPacket.setContents(Collections.singletonList(InventoryUtils.getTotemOfUndying().apply(session.getUpstream().getProtocolVersion())));
offhandPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null));
session.sendUpstreamPacket(offhandPacket); session.sendUpstreamPacket(offhandPacket);
} }

Datei anzeigen

@ -25,6 +25,8 @@
package org.geysermc.geyser.translator.protocol.java.inventory; package org.geysermc.geyser.translator.protocol.java.inventory;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType;
import org.cloudburstmc.protocol.bedrock.data.inventory.FullContainerName;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack; import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import org.geysermc.mcprotocollib.protocol.data.game.recipe.Ingredient; import org.geysermc.mcprotocollib.protocol.data.game.recipe.Ingredient;
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.inventory.ClientboundContainerSetSlotPacket; import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.inventory.ClientboundContainerSetSlotPacket;
@ -180,6 +182,7 @@ public class JavaContainerSetSlotTranslator extends PacketTranslator<Clientbound
slotPacket.setContainerId(ContainerId.UI); slotPacket.setContainerId(ContainerId.UI);
slotPacket.setSlot(col + (row * gridDimensions) + offset); slotPacket.setSlot(col + (row * gridDimensions) + offset);
slotPacket.setItem(ItemData.AIR); slotPacket.setItem(ItemData.AIR);
slotPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null));
session.sendUpstreamPacket(slotPacket); session.sendUpstreamPacket(slotPacket);
index++; index++;
} }
@ -212,6 +215,7 @@ public class JavaContainerSetSlotTranslator extends PacketTranslator<Clientbound
slotPacket.setContainerId(ContainerId.UI); slotPacket.setContainerId(ContainerId.UI);
slotPacket.setSlot(col + (row * gridDimensions) + offset); slotPacket.setSlot(col + (row * gridDimensions) + offset);
slotPacket.setItem(ingredients[index]); slotPacket.setItem(ingredients[index]);
slotPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null));
session.sendUpstreamPacket(slotPacket); session.sendUpstreamPacket(slotPacket);
index++; index++;
} }

Datei anzeigen

@ -57,5 +57,7 @@ public class JavaCooldownTranslator extends PacketTranslator<ClientboundCooldown
bedrockPacket.setCooldownDuration(packet.getCooldownTicks()); bedrockPacket.setCooldownDuration(packet.getCooldownTicks());
session.sendUpstreamPacket(bedrockPacket); session.sendUpstreamPacket(bedrockPacket);
} }
session.getWorldCache().setCooldown(item, packet.getCooldownTicks());
} }
} }

Datei anzeigen

@ -33,9 +33,7 @@ import org.geysermc.mcprotocollib.protocol.data.game.level.notify.RespawnScreenV
import org.geysermc.mcprotocollib.protocol.data.game.level.notify.ThunderStrengthValue; import org.geysermc.mcprotocollib.protocol.data.game.level.notify.ThunderStrengthValue;
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.level.ClientboundGameEventPacket; import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.level.ClientboundGameEventPacket;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.ServerboundClientCommandPacket; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.ServerboundClientCommandPacket;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.GameRuleData; import org.cloudburstmc.protocol.bedrock.data.GameRuleData;
import org.cloudburstmc.protocol.bedrock.data.LevelEvent;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityEventType; import org.cloudburstmc.protocol.bedrock.data.entity.EntityEventType;
import org.cloudburstmc.protocol.bedrock.packet.*; import org.cloudburstmc.protocol.bedrock.packet.*;
import org.geysermc.geyser.entity.type.player.PlayerEntity; import org.geysermc.geyser.entity.type.player.PlayerEntity;
@ -48,9 +46,6 @@ import org.geysermc.geyser.util.EntityUtils;
@Translator(packet = ClientboundGameEventPacket.class) @Translator(packet = ClientboundGameEventPacket.class)
public class JavaGameEventTranslator extends PacketTranslator<ClientboundGameEventPacket> { 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 @Override
public void translate(GeyserSession session, ClientboundGameEventPacket packet) { public void translate(GeyserSession session, ClientboundGameEventPacket packet) {
PlayerEntity entity = session.getPlayerEntity(); PlayerEntity entity = session.getPlayerEntity();
@ -65,42 +60,20 @@ public class JavaGameEventTranslator extends PacketTranslator<ClientboundGameEve
// As a result many developers use these packets for the opposite of what their names implies // 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 // Behavior last verified with Java 1.19.4 and Bedrock 1.19.71
case START_RAIN: case START_RAIN:
LevelEventPacket stopRainPacket = new LevelEventPacket(); session.updateRain(0);
stopRainPacket.setType(LevelEvent.STOP_RAINING);
stopRainPacket.setData(0);
stopRainPacket.setPosition(Vector3f.ZERO);
session.sendUpstreamPacket(stopRainPacket);
session.setRaining(false);
break; break;
case STOP_RAIN: case STOP_RAIN:
LevelEventPacket startRainPacket = new LevelEventPacket(); session.updateRain(1);
startRainPacket.setType(LevelEvent.START_RAINING);
startRainPacket.setData(MAX_STORM_STRENGTH);
startRainPacket.setPosition(Vector3f.ZERO);
session.sendUpstreamPacket(startRainPacket);
session.setRaining(true);
break; break;
case RAIN_STRENGTH: case RAIN_STRENGTH:
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 // 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)); float rainStrength = ((RainStrengthValue) packet.getValue()).getStrength();
changeRainPacket.setPosition(Vector3f.ZERO); session.updateRain(rainStrength);
session.sendUpstreamPacket(changeRainPacket);
session.setRaining(isCurrentlyRaining);
break; break;
case THUNDER_STRENGTH: case THUNDER_STRENGTH:
// See above, same process // See above, same process
float thunderStrength = ((ThunderStrengthValue) packet.getValue()).getStrength(); float thunderStrength = ((ThunderStrengthValue) packet.getValue()).getStrength();
boolean isCurrentlyThundering = thunderStrength > 0f; session.updateThunder(thunderStrength);
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; break;
case CHANGE_GAMEMODE: case CHANGE_GAMEMODE:
GameMode gameMode = (GameMode) packet.getValue(); GameMode gameMode = (GameMode) packet.getValue();

Datei anzeigen

@ -291,10 +291,7 @@ public class JavaLevelEventTranslator extends PacketTranslator<ClientboundLevelE
session.sendUpstreamPacket(soundEventPacket); session.sendUpstreamPacket(soundEventPacket);
} }
} }
case PARTICLES_DRAGON_BLOCK_BREAK -> { case PARTICLES_DRAGON_BLOCK_BREAK -> effectPacket.setType(ParticleType.DRAGON_DESTROY_BLOCK);
effectPacket.setType(org.cloudburstmc.protocol.bedrock.data.LevelEvent.PARTICLE_GENERIC_SPAWN);
effectPacket.setData(61);
}
case PARTICLES_WATER_EVAPORATING -> { case PARTICLES_WATER_EVAPORATING -> {
effectPacket.setType(org.cloudburstmc.protocol.bedrock.data.LevelEvent.PARTICLE_EVAPORATE_WATER); effectPacket.setType(org.cloudburstmc.protocol.bedrock.data.LevelEvent.PARTICLE_EVAPORATE_WATER);
effectPacket.setPosition(pos.add(-0.5f, 0.5f, -0.5f)); effectPacket.setPosition(pos.add(-0.5f, 0.5f, -0.5f));

Datei anzeigen

@ -26,10 +26,14 @@
package org.geysermc.geyser.translator.text; package org.geysermc.geyser.translator.text;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.JoinConfiguration;
import net.kyori.adventure.text.ScoreComponent; import net.kyori.adventure.text.ScoreComponent;
import net.kyori.adventure.text.TranslatableComponent; import net.kyori.adventure.text.TranslatableComponent;
import net.kyori.adventure.text.flattener.ComponentFlattener; import net.kyori.adventure.text.flattener.ComponentFlattener;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextColor; import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.format.TextDecoration;
import net.kyori.adventure.text.renderer.TranslatableComponentRenderer; import net.kyori.adventure.text.renderer.TranslatableComponentRenderer;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.kyori.adventure.text.serializer.legacy.CharacterAndFormat; import net.kyori.adventure.text.serializer.legacy.CharacterAndFormat;
@ -39,14 +43,22 @@ import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.protocol.bedrock.packet.TextPacket; import org.cloudburstmc.protocol.bedrock.packet.TextPacket;
import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.text.*; import org.geysermc.geyser.text.ChatColor;
import org.geysermc.geyser.text.ChatDecoration;
import org.geysermc.geyser.text.DummyLegacyHoverEventSerializer;
import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.text.GsonComponentSerializerWrapper;
import org.geysermc.geyser.text.MinecraftTranslationRegistry;
import org.geysermc.mcprotocollib.protocol.data.DefaultComponentSerializer; import org.geysermc.mcprotocollib.protocol.data.DefaultComponentSerializer;
import org.geysermc.mcprotocollib.protocol.data.game.Holder; import org.geysermc.mcprotocollib.protocol.data.game.Holder;
import org.geysermc.mcprotocollib.protocol.data.game.chat.ChatType; import org.geysermc.mcprotocollib.protocol.data.game.chat.ChatType;
import org.geysermc.mcprotocollib.protocol.data.game.chat.ChatTypeDecoration; import org.geysermc.mcprotocollib.protocol.data.game.chat.ChatTypeDecoration;
import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.TeamColor; import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.TeamColor;
import java.util.*; import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
public class MessageTranslator { public class MessageTranslator {
// These are used for handling the translations of the messages // These are used for handling the translations of the messages
@ -341,16 +353,16 @@ public class MessageTranslator {
// Though, Bedrock cannot care about the signed stuff. // Though, Bedrock cannot care about the signed stuff.
TranslatableComponent.Builder withDecoration = Component.translatable() TranslatableComponent.Builder withDecoration = Component.translatable()
.key(chat.translationKey()) .key(chat.translationKey())
.style(TextDecoration.getStyle(chat)); .style(ChatDecoration.getStyle(chat));
List<ChatTypeDecoration.Parameter> parameters = chat.parameters(); List<ChatTypeDecoration.Parameter> parameters = chat.parameters();
List<Component> args = new ArrayList<>(3); List<Component> args = new ArrayList<>(3);
if (parameters.contains(TextDecoration.Parameter.TARGET)) { if (parameters.contains(ChatDecoration.Parameter.TARGET)) {
args.add(targetName); args.add(targetName);
} }
if (parameters.contains(TextDecoration.Parameter.SENDER)) { if (parameters.contains(ChatDecoration.Parameter.SENDER)) {
args.add(sender); args.add(sender);
} }
if (parameters.contains(TextDecoration.Parameter.CONTENT)) { if (parameters.contains(ChatDecoration.Parameter.CONTENT)) {
args.add(message); args.add(message);
} }
withDecoration.arguments(args); withDecoration.arguments(args);
@ -426,17 +438,92 @@ public class MessageTranslator {
} }
/** /**
* Deserialize an NbtMap provided from a registry into a string. * Deserialize an NbtMap with a description text component (usually provided from a registry) into a Bedrock-formatted string.
*/ */
// This may be a Component in the future. public static String deserializeDescription(GeyserSession session, NbtMap tag) {
public static String deserializeDescription(NbtMap tag) { Object description = tag.get("description");
NbtMap description = tag.getCompound("description"); Component parsed = componentFromNbtTag(description);
String translate = description.getString("translate", null); return convertMessage(session, parsed);
if (translate == null) { }
GeyserImpl.getInstance().getLogger().debug("Don't know how to read description! " + tag);
return ""; public static Component componentFromNbtTag(Object nbtTag) {
return componentFromNbtTag(nbtTag, Style.empty());
}
private static Component componentFromNbtTag(Object nbtTag, Style style) {
if (nbtTag instanceof String literal) {
return Component.text(literal).style(style);
} else if (nbtTag instanceof List<?> list) {
return Component.join(JoinConfiguration.noSeparators(), componentsFromNbtList(list, style));
} else if (nbtTag instanceof NbtMap map) {
Component component = null;
String text = map.getString("text", null);
if (text != null) {
component = Component.text(text);
} else {
String translateKey = map.getString("translate", null);
if (translateKey != null) {
String fallback = map.getString("fallback", "");
List<Component> args = new ArrayList<>();
Object with = map.get("with");
if (with instanceof List<?> list) {
args = componentsFromNbtList(list, style);
} else if (with != null) {
args.add(componentFromNbtTag(with, style));
}
component = Component.translatable(translateKey, fallback, args);
}
}
if (component != null) {
Style newStyle = getStyleFromNbtMap(map, style);
component = component.style(newStyle);
Object extra = map.get("extra");
if (extra != null) {
component = component.append(componentFromNbtTag(extra, newStyle));
}
return component;
}
} }
return translate;
GeyserImpl.getInstance().getLogger().error("Expected tag to be a literal string, a list of components, or a component object with a text/translate key: " + nbtTag);
return Component.empty();
}
private static List<Component> componentsFromNbtList(List<?> list, Style style) {
List<Component> components = new ArrayList<>();
for (Object entry : list) {
components.add(componentFromNbtTag(entry, style));
}
return components;
}
public static Style getStyleFromNbtMap(NbtMap map) {
Style.Builder style = Style.style();
String colorString = map.getString("color", null);
if (colorString != null) {
if (colorString.startsWith(TextColor.HEX_PREFIX)) {
style.color(TextColor.fromHexString(colorString));
} else {
style.color(NamedTextColor.NAMES.value(colorString));
}
}
map.listenForBoolean("bold", value -> style.decoration(TextDecoration.BOLD, value));
map.listenForBoolean("italic", value -> style.decoration(TextDecoration.ITALIC, value));
map.listenForBoolean("underlined", value -> style.decoration(TextDecoration.UNDERLINED, value));
map.listenForBoolean("strikethrough", value -> style.decoration(TextDecoration.STRIKETHROUGH, value));
map.listenForBoolean("obfuscated", value -> style.decoration(TextDecoration.OBFUSCATED, value));
return style.build();
}
public static Style getStyleFromNbtMap(NbtMap map, Style base) {
return base.merge(getStyleFromNbtMap(map));
} }
public static void init() { public static void init() {

Datei anzeigen

@ -28,11 +28,9 @@ package org.geysermc.geyser.util;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.math.vector.Vector3i; import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.protocol.bedrock.data.LevelEvent;
import org.cloudburstmc.protocol.bedrock.data.PlayerActionType; import org.cloudburstmc.protocol.bedrock.data.PlayerActionType;
import org.cloudburstmc.protocol.bedrock.packet.ChangeDimensionPacket; import org.cloudburstmc.protocol.bedrock.packet.ChangeDimensionPacket;
import org.cloudburstmc.protocol.bedrock.packet.ChunkRadiusUpdatedPacket; import org.cloudburstmc.protocol.bedrock.packet.ChunkRadiusUpdatedPacket;
import org.cloudburstmc.protocol.bedrock.packet.LevelEventPacket;
import org.cloudburstmc.protocol.bedrock.packet.MobEffectPacket; import org.cloudburstmc.protocol.bedrock.packet.MobEffectPacket;
import org.cloudburstmc.protocol.bedrock.packet.PlayerActionPacket; import org.cloudburstmc.protocol.bedrock.packet.PlayerActionPacket;
import org.cloudburstmc.protocol.bedrock.packet.StopSoundPacket; import org.cloudburstmc.protocol.bedrock.packet.StopSoundPacket;
@ -89,18 +87,8 @@ public class DimensionUtils {
entityEffects.clear(); entityEffects.clear();
// Always reset weather, as it sometimes suddenly starts raining. See https://github.com/GeyserMC/Geyser/issues/3679 // Always reset weather, as it sometimes suddenly starts raining. See https://github.com/GeyserMC/Geyser/issues/3679
LevelEventPacket stopRainPacket = new LevelEventPacket(); session.updateRain(0);
stopRainPacket.setType(LevelEvent.STOP_RAINING); session.updateThunder(0);
stopRainPacket.setData(0);
stopRainPacket.setPosition(Vector3f.ZERO);
session.sendUpstreamPacket(stopRainPacket);
session.setRaining(false);
LevelEventPacket stopThunderPacket = new LevelEventPacket();
stopThunderPacket.setType(LevelEvent.STOP_THUNDERSTORM);
stopThunderPacket.setData(0);
stopThunderPacket.setPosition(Vector3f.ZERO);
session.sendUpstreamPacket(stopThunderPacket);
session.setThunder(false);
finalizeDimensionSwitch(session, player); finalizeDimensionSwitch(session, player);

Datei anzeigen

@ -32,6 +32,8 @@ import org.cloudburstmc.nbt.NbtMapBuilder;
import org.cloudburstmc.nbt.NbtType; import org.cloudburstmc.nbt.NbtType;
import org.cloudburstmc.protocol.bedrock.data.definitions.ItemDefinition; import org.cloudburstmc.protocol.bedrock.data.definitions.ItemDefinition;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerId; import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerId;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType;
import org.cloudburstmc.protocol.bedrock.data.inventory.FullContainerName;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.cloudburstmc.protocol.bedrock.packet.InventorySlotPacket; import org.cloudburstmc.protocol.bedrock.packet.InventorySlotPacket;
import org.cloudburstmc.protocol.bedrock.packet.PlayerHotbarPacket; import org.cloudburstmc.protocol.bedrock.packet.PlayerHotbarPacket;
@ -180,6 +182,7 @@ public class InventoryUtils {
cursorPacket.setContainerId(ContainerId.UI); cursorPacket.setContainerId(ContainerId.UI);
cursorPacket.setSlot(0); cursorPacket.setSlot(0);
cursorPacket.setItem(session.getPlayerInventory().getCursor().getItemData(session)); cursorPacket.setItem(session.getPlayerInventory().getCursor().getItemData(session));
cursorPacket.setContainerNameData(new FullContainerName(ContainerSlotType.ANVIL_INPUT, null));
session.sendUpstreamPacket(cursorPacket); session.sendUpstreamPacket(cursorPacket);
} }

Binäre Datei nicht angezeigt.

Datei-Diff unterdrückt, da er zu groß ist Diff laden

Datei-Diff unterdrückt, da er zu groß ist Diff laden

@ -1 +1 @@
Subproject commit 698fd2b108a9e53f1e47b8cfdc122651b70d6059 Subproject commit 830085e37e0053c9d6116bcad37cafcba83aded5

Datei anzeigen

@ -1,11 +1,12 @@
# Gradle settings # Gradle settings
org.gradle.jvmargs=-Xmx4G org.gradle.jvmargs=-Xmx4G
org.gradle.daemon=false org.gradle.daemon=false
org.gradle.configureondemand=true
org.gradle.parallel=true org.gradle.parallel=true
org.gradle.caching=true org.gradle.caching=true
org.gradle.vfs.watch=false org.gradle.vfs.watch=false
group=org.geysermc group=org.geysermc
id=geyser id=geyser
version=2.4.2-SNAPSHOT version=2.4.3-SNAPSHOT
description=Allows for players from Minecraft: Bedrock Edition to join Minecraft: Java Edition servers. description=Allows for players from Minecraft: Bedrock Edition to join Minecraft: Java Edition servers.

Datei anzeigen

@ -10,9 +10,9 @@ netty-io-uring = "0.0.25.Final-SNAPSHOT"
guava = "29.0-jre" guava = "29.0-jre"
gson = "2.3.1" # Provided by Spigot 1.8.8 gson = "2.3.1" # Provided by Spigot 1.8.8
websocket = "1.5.1" websocket = "1.5.1"
protocol-connection = "3.0.0.Beta3-20240819.124045-12" protocol-connection = "3.0.0.Beta5-20240916.181041-6"
protocol-common = "3.0.0.Beta3-20240819.124045-10" protocol-common = "3.0.0.Beta5-20240916.181041-6"
protocol-codec = "3.0.0.Beta3-20240819.124045-13" protocol-codec = "3.0.0.Beta5-20240916.181041-6"
raknet = "1.0.0.CR3-20240416.144209-1" raknet = "1.0.0.CR3-20240416.144209-1"
minecraftauth = "4.1.1-20240806.235051-7" minecraftauth = "4.1.1-20240806.235051-7"
mcprotocollib = "1.21-20240725.013034-16" mcprotocollib = "1.21-20240725.013034-16"
@ -134,6 +134,7 @@ protocol-connection = { group = "org.cloudburstmc.protocol", name = "bedrock-con
math = { group = "org.cloudburstmc.math", name = "immutable", version = "2.0" } math = { group = "org.cloudburstmc.math", name = "immutable", version = "2.0" }
# plugins # plugins
lombok = { group = "io.freefair.gradle", name = "lombok-plugin", version.ref = "lombok" }
indra = { group = "net.kyori", name = "indra-common", version.ref = "indra" } indra = { group = "net.kyori", name = "indra-common", version.ref = "indra" }
shadow = { group = "com.github.johnrengelman", name = "shadow", version.ref = "shadow" } shadow = { group = "com.github.johnrengelman", name = "shadow", version.ref = "shadow" }
architectury-plugin = { group = "architectury-plugin", name = "architectury-plugin.gradle.plugin", version.ref = "architectury-plugin" } architectury-plugin = { group = "architectury-plugin", name = "architectury-plugin.gradle.plugin", version.ref = "architectury-plugin" }
@ -141,7 +142,6 @@ architectury-loom = { group = "dev.architectury.loom", name = "dev.architectury.
minotaur = { group = "com.modrinth.minotaur", name = "Minotaur", version.ref = "minotaur" } minotaur = { group = "com.modrinth.minotaur", name = "Minotaur", version.ref = "minotaur" }
[plugins] [plugins]
lombok = { id = "io.freefair.lombok", version.ref = "lombok" }
indra = { id = "net.kyori.indra", version.ref = "indra" } indra = { id = "net.kyori.indra", version.ref = "indra" }
blossom = { id = "net.kyori.blossom", version.ref = "blossom" } blossom = { id = "net.kyori.blossom", version.ref = "blossom" }