Mirror von
https://github.com/GeyserMC/Geyser.git
synchronisiert 2024-12-28 17:10:19 +01:00
Merge branch 'master' of https://github.com/GeyserMC/Geyser into fixrenderdistance
Dieser Commit ist enthalten in:
Commit
be36e53bf4
14
.editorconfig
Normale Datei
14
.editorconfig
Normale Datei
@ -0,0 +1,14 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
tab_width = 4
|
||||
max_line_length = off
|
||||
|
||||
[*.java]
|
||||
ij_java_class_count_to_use_import_on_demand = 9999
|
||||
ij_java_doc_align_exception_comments = false
|
||||
ij_java_doc_align_param_comments = false
|
1
.github/workflows/build.yml
vendored
1
.github/workflows/build.yml
vendored
@ -16,7 +16,6 @@ on:
|
||||
- 'LICENSE'
|
||||
- 'Jenkinsfile '
|
||||
- 'README.md'
|
||||
- 'licenseheader.txt'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||
Copyright (c) 2019-2024 GeyserMC. http://geysermc.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -14,7 +14,7 @@ The ultimate goal of this project is to allow Minecraft: Bedrock Edition users t
|
||||
|
||||
Special thanks to the DragonProxy project for being a trailblazer in protocol translation and for all the team members who have joined us here!
|
||||
|
||||
### Currently supporting Minecraft Bedrock 1.20.80 - 1.21.1 and Minecraft Java 1.21
|
||||
### Currently supporting Minecraft Bedrock 1.20.80 - 1.21.2 and Minecraft Java 1.21
|
||||
|
||||
## Setting Up
|
||||
Take a look [here](https://wiki.geysermc.org/geyser/setup/) for how to set up Geyser.
|
||||
|
@ -75,7 +75,7 @@ tasks.processResources {
|
||||
expand(
|
||||
"branch" to info.branch,
|
||||
"buildNumber" to info.buildNumber,
|
||||
"projectVersion" to project.version,
|
||||
"projectVersion" to info.version,
|
||||
"commit" to info.commit,
|
||||
"commitAbbrev" to info.commitAbbrev,
|
||||
"commitMessage" to info.commitMessage,
|
||||
@ -89,20 +89,25 @@ sourceSets {
|
||||
blossom {
|
||||
val info = GitInfo()
|
||||
javaSources {
|
||||
property("version", "${project.version} (${info.gitVersion})")
|
||||
property("version", info.version)
|
||||
property("gitVersion", info.gitVersion)
|
||||
property("buildNumber", info.buildNumber.toString())
|
||||
property("branch", info.branch)
|
||||
property("commit", info.commit)
|
||||
property("repository", info.repository)
|
||||
property("devVersion", info.isDev.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.buildNumber(): Int =
|
||||
fun buildNumber(): Int =
|
||||
(System.getenv("BUILD_NUMBER"))?.let { Integer.parseInt(it) } ?: -1
|
||||
|
||||
fun isDevBuild(branch: String, repository: String): Boolean {
|
||||
return branch != "master" || repository.equals("https://github.com/GeyserMC/Geyser", ignoreCase = true).not()
|
||||
}
|
||||
|
||||
inner class GitInfo {
|
||||
val branch: String
|
||||
val commit: String
|
||||
@ -115,22 +120,25 @@ inner class GitInfo {
|
||||
val commitMessage: String
|
||||
val repository: String
|
||||
|
||||
val isDev: Boolean
|
||||
|
||||
init {
|
||||
// On Jenkins, a detached head is checked out, so indra cannot determine the branch.
|
||||
// Fortunately, this environment variable is available.
|
||||
branch = indraGit.branchName() ?: System.getenv("BRANCH_NAME") ?: "DEV"
|
||||
branch = indraGit.branchName() ?: "DEV"
|
||||
|
||||
val commit = indraGit.commit()
|
||||
this.commit = commit?.name ?: "0".repeat(40)
|
||||
commitAbbrev = commit?.name?.substring(0, 7) ?: "0".repeat(7)
|
||||
|
||||
gitVersion = "git-${branch}-${commitAbbrev}"
|
||||
version = "${project.version} ($gitVersion)"
|
||||
buildNumber = buildNumber()
|
||||
|
||||
val git = indraGit.git()
|
||||
commitMessage = git?.commit()?.message ?: ""
|
||||
repository = git?.repository?.config?.getString("remote", "origin", "url") ?: ""
|
||||
|
||||
buildNumber = buildNumber()
|
||||
isDev = isDevBuild(branch, repository)
|
||||
val projectVersion = if (isDev) project.version else project.version.toString().replace("SNAPSHOT", "b${buildNumber}")
|
||||
version = "$projectVersion ($gitVersion)"
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,4 +156,4 @@ tasks.register<DownloadFilesTask>("downloadBedrockData") {
|
||||
suffixedFiles = listOf("block_palette.nbt", "creative_items.json", "runtime_item_states.json")
|
||||
|
||||
destinationDir = "$projectDir/src/main/resources/bedrock"
|
||||
}
|
||||
}
|
||||
|
@ -34,4 +34,9 @@ public class BuildData {
|
||||
public static final String BRANCH = "{{ branch }}";
|
||||
public static final String COMMIT = "{{ commit }}";
|
||||
public static final String REPOSITORY = "{{ repository }}";
|
||||
private static final String DEV = "{{ devVersion }}";
|
||||
|
||||
public static boolean isDevBuild() {
|
||||
return Boolean.parseBoolean(DEV);
|
||||
}
|
||||
}
|
||||
|
@ -122,6 +122,7 @@ public class GeyserImpl implements GeyserApi {
|
||||
public static final String BRANCH = BuildData.BRANCH;
|
||||
public static final String COMMIT = BuildData.COMMIT;
|
||||
public static final String REPOSITORY = BuildData.REPOSITORY;
|
||||
public static final boolean IS_DEV = BuildData.isDevBuild();
|
||||
|
||||
/**
|
||||
* Oauth client ID for Microsoft authentication
|
||||
@ -207,6 +208,12 @@ public class GeyserImpl implements GeyserApi {
|
||||
logger.info("");
|
||||
logger.info(GeyserLocale.getLocaleStringLog("geyser.core.load", NAME, VERSION));
|
||||
logger.info("");
|
||||
if (IS_DEV) {
|
||||
// TODO cloud use language string
|
||||
//logger.info(GeyserLocale.getLocaleStringLog("geyser.core.dev_build", "https://discord.gg/geysermc"));
|
||||
logger.info("You are running a development build of Geyser! Please report any bugs you find on our Discord server: %s".formatted("https://discord.gg/geysermc"));
|
||||
logger.info("");
|
||||
}
|
||||
logger.info("******************************************");
|
||||
|
||||
/* Initialize registries */
|
||||
@ -684,9 +691,10 @@ public class GeyserImpl implements GeyserApi {
|
||||
*
|
||||
* @return true if the version number is not 'DEV'.
|
||||
*/
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
public boolean isProductionEnvironment() {
|
||||
// First is if Blossom runs, second is if Blossom doesn't run
|
||||
//noinspection ConstantConditions,MismatchedStringCase - changes in production
|
||||
//noinspection ConstantConditions - changes in production
|
||||
return !("git-local/dev-0000000".equals(GeyserImpl.GIT_VERSION) || "${gitVersion}".equals(GeyserImpl.GIT_VERSION));
|
||||
}
|
||||
|
||||
|
@ -25,8 +25,8 @@
|
||||
|
||||
package org.geysermc.geyser.command.defaults;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
|
||||
import org.geysermc.geyser.Constants;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.util.PlatformType;
|
||||
import org.geysermc.geyser.command.GeyserCommand;
|
||||
@ -37,8 +37,7 @@ import org.geysermc.geyser.text.ChatColor;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
import org.geysermc.geyser.util.WebUtils;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public class VersionCommand extends GeyserCommand {
|
||||
@ -72,27 +71,36 @@ public class VersionCommand extends GeyserCommand {
|
||||
GeyserImpl.NAME, GeyserImpl.VERSION, javaVersions, bedrockVersions));
|
||||
|
||||
// Disable update checking in dev mode and for players in Geyser Standalone
|
||||
if (GeyserImpl.getInstance().isProductionEnvironment() && !(!sender.isConsole() && geyser.getPlatformType() == PlatformType.STANDALONE)) {
|
||||
sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.checking", sender.locale()));
|
||||
try {
|
||||
String buildXML = WebUtils.getBody("https://ci.opencollab.dev/job/GeyserMC/job/Geyser/job/" +
|
||||
URLEncoder.encode(GeyserImpl.BRANCH, StandardCharsets.UTF_8) + "/lastSuccessfulBuild/api/xml?xpath=//buildNumber");
|
||||
if (buildXML.startsWith("<buildNumber>")) {
|
||||
int latestBuildNum = Integer.parseInt(buildXML.replaceAll("<(\\\\)?(/)?buildNumber>", "").trim());
|
||||
int buildNum = this.geyser.buildNumber();
|
||||
if (latestBuildNum == buildNum) {
|
||||
sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.no_updates", sender.locale()));
|
||||
} else {
|
||||
sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.outdated",
|
||||
sender.locale(), (latestBuildNum - buildNum), Constants.GEYSER_DOWNLOAD_LOCATION));
|
||||
}
|
||||
} else {
|
||||
throw new AssertionError("buildNumber missing");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.commands.version.failed"), e);
|
||||
sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.version.failed", sender.locale()));
|
||||
if (!GeyserImpl.getInstance().isProductionEnvironment() || (!sender.isConsole() && geyser.getPlatformType() == PlatformType.STANDALONE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (GeyserImpl.IS_DEV) {
|
||||
// TODO cloud use language string
|
||||
sender.sendMessage("You are running a development build of Geyser! Please report any bugs you find on our Discord server: %s"
|
||||
.formatted("https://discord.gg/geysermc"));
|
||||
//sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.core.dev_build", sender.locale(), "https://discord.gg/geysermc"));
|
||||
return;
|
||||
}
|
||||
|
||||
sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.checking", sender.locale()));
|
||||
try {
|
||||
int buildNumber = this.geyser.buildNumber();
|
||||
JsonNode response = WebUtils.getJson("https://download.geysermc.org/v2/projects/geyser/versions/latest/builds/latest");
|
||||
int latestBuildNumber = response.get("build").asInt();
|
||||
|
||||
if (latestBuildNumber == buildNumber) {
|
||||
sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.no_updates", sender.locale()));
|
||||
return;
|
||||
}
|
||||
|
||||
sender.sendMessage(GeyserLocale.getPlayerLocaleString(
|
||||
"geyser.commands.version.outdated",
|
||||
sender.locale(), (latestBuildNumber - buildNumber), "https://geysermc.org/download"
|
||||
));
|
||||
} catch (IOException e) {
|
||||
GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.commands.version.failed"), e);
|
||||
sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.version.failed", sender.locale()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,6 +47,7 @@ import java.util.function.BiConsumer;
|
||||
* metadata translators needed to translate the properties sent from the server. The translators are structured in such
|
||||
* a way that inserting a new one (for example in version updates) is convenient.
|
||||
*
|
||||
* @param identifier the Bedrock identifier of this entity
|
||||
* @param <T> the entity type this definition represents
|
||||
*/
|
||||
public record EntityDefinition<T extends Entity>(EntityFactory<T> factory, EntityType entityType, String identifier,
|
||||
|
@ -307,11 +307,11 @@ public final class EntityDefinitions {
|
||||
.addTranslator(MetadataType.INT, TNTEntity::setFuseLength)
|
||||
.build();
|
||||
|
||||
EntityDefinition<Entity> displayBase = EntityDefinition.inherited(entityBase.factory(), entityBase)
|
||||
EntityDefinition<DisplayBaseEntity> displayBase = EntityDefinition.inherited(DisplayBaseEntity::new, entityBase)
|
||||
.addTranslator(null) // Interpolation delay
|
||||
.addTranslator(null) // Transformation interpolation duration
|
||||
.addTranslator(null) // Position/Rotation interpolation duration
|
||||
.addTranslator(null) // Translation
|
||||
.addTranslator(MetadataType.VECTOR3, DisplayBaseEntity::setTranslation) // Translation
|
||||
.addTranslator(null) // Scale
|
||||
.addTranslator(null) // Left rotation
|
||||
.addTranslator(null) // Right rotation
|
||||
|
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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.entity.type;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.util.EntityUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public class DisplayBaseEntity extends Entity {
|
||||
|
||||
private @Nullable Vector3f baseTranslation;
|
||||
|
||||
public DisplayBaseEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
|
||||
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDisplayNameVisible(BooleanEntityMetadata entityMetadata) {
|
||||
// Don't allow the display name to be hidden - messes with our armor stand.
|
||||
// On JE: Hiding the display name still shows the display entity text.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDisplayName(EntityMetadata<Optional<Component>, ?> entityMetadata) {
|
||||
// This would usually set EntityDataTypes.NAME, but we are instead using NAME for the text display.
|
||||
// On JE: custom name does not override text display.
|
||||
}
|
||||
|
||||
public void setTranslation(EntityMetadata<Vector3f, ?> translationMeta){
|
||||
this.baseTranslation = translationMeta.getValue();
|
||||
if (this.baseTranslation == null) {
|
||||
return;
|
||||
}
|
||||
if (this.vehicle == null) {
|
||||
this.setRiderSeatPosition(this.baseTranslation);
|
||||
this.moveRelative(this.baseTranslation.getX(), this.baseTranslation.getY(), this.baseTranslation.getZ(), yaw, pitch, headYaw, false);
|
||||
} else {
|
||||
EntityUtils.updateMountOffset(this, this.vehicle, true, true, false);
|
||||
this.updateBedrockMetadata();
|
||||
}
|
||||
}
|
||||
|
||||
public Vector3f getTranslation() {
|
||||
return baseTranslation;
|
||||
}
|
||||
}
|
@ -86,7 +86,11 @@ public class PaintingEntity extends Entity {
|
||||
|
||||
private Vector3f fixOffset(PaintingType paintingName) {
|
||||
Vector3f position = super.position;
|
||||
position = position.add(0.5, 0.5, 0.5);
|
||||
// ViaVersion already adds the offset for us on older versions,
|
||||
// so no need to do it then otherwise it will be spaced
|
||||
if (session.isEmulatePost1_18Logic()) {
|
||||
position = position.add(0.5, 0.5, 0.5);
|
||||
}
|
||||
double widthOffset = paintingName.getWidth() > 1 && paintingName.getWidth() != 3 ? 0.5 : 0;
|
||||
double heightOffset = paintingName.getHeight() > 1 && paintingName.getHeight() != 3 ? 0.5 : 0;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||
* 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
|
||||
@ -32,13 +32,11 @@ import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.text.MessageTranslator;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
// Note: 1.19.4 requires that the billboard is set to something in order to show, on Java Edition
|
||||
public class TextDisplayEntity extends Entity {
|
||||
public class TextDisplayEntity extends DisplayBaseEntity {
|
||||
public TextDisplayEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
|
||||
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
|
||||
}
|
||||
@ -51,18 +49,6 @@ public class TextDisplayEntity extends Entity {
|
||||
this.dirtyMetadata.put(EntityDataTypes.NAMETAG_ALWAYS_SHOW, (byte) 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDisplayNameVisible(BooleanEntityMetadata entityMetadata) {
|
||||
// Don't allow the display name to be hidden - messes with our armor stand.
|
||||
// On JE: Hiding the display name still shows the display entity text.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDisplayName(EntityMetadata<Optional<Component>, ?> entityMetadata) {
|
||||
// This would usually set EntityDataTypes.NAME, but we are instead using NAME for the text display.
|
||||
// On JE: custom name does not override text display.
|
||||
}
|
||||
|
||||
public void setText(EntityMetadata<Component, ?> entityMetadata) {
|
||||
this.dirtyMetadata.put(EntityDataTypes.NAME, MessageTranslator.convertMessage(entityMetadata.getValue()));
|
||||
}
|
||||
|
@ -27,15 +27,18 @@ package org.geysermc.geyser.entity.type.player;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.AttributeData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
|
||||
import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.network.GameProtocol;
|
||||
import org.geysermc.geyser.level.BedrockDimension;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.util.AttributeUtils;
|
||||
import org.geysermc.geyser.util.DimensionUtils;
|
||||
@ -69,6 +72,15 @@ public class SessionPlayerEntity extends PlayerEntity {
|
||||
|
||||
private int lastAirSupply = getMaxAir();
|
||||
|
||||
/**
|
||||
* Determines if our position is currently out-of-sync with the Java server
|
||||
* due to our workaround for the void floor
|
||||
* <p>
|
||||
* Must be reset when dying, switching worlds, or being teleported out of the void
|
||||
*/
|
||||
@Getter @Setter
|
||||
private boolean voidPositionDesynched;
|
||||
|
||||
public SessionPlayerEntity(GeyserSession session) {
|
||||
super(session, -1, 1, null, Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0, null, null);
|
||||
|
||||
@ -87,10 +99,25 @@ public class SessionPlayerEntity extends PlayerEntity {
|
||||
|
||||
@Override
|
||||
public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) {
|
||||
if (voidPositionDesynched) {
|
||||
if (!isBelowVoidFloor()) {
|
||||
voidPositionDesynched = false; // No need to fix our offset; we've been moved
|
||||
}
|
||||
}
|
||||
super.moveRelative(relX, relY, relZ, yaw, pitch, headYaw, isOnGround);
|
||||
session.getCollisionManager().updatePlayerBoundingBox(this.position.down(definition.offset()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveAbsolute(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) {
|
||||
if (voidPositionDesynched) {
|
||||
if (!isBelowVoidFloor()) {
|
||||
voidPositionDesynched = false; // No need to fix our offset; we've been moved
|
||||
}
|
||||
}
|
||||
super.moveAbsolute(position, yaw, pitch, headYaw, isOnGround, teleported);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPosition(Vector3f position) {
|
||||
if (valid) { // Don't update during session init
|
||||
@ -225,6 +252,9 @@ public class SessionPlayerEntity extends PlayerEntity {
|
||||
} else {
|
||||
dirtyMetadata.put(EntityDataTypes.PLAYER_HAS_DIED, false);
|
||||
}
|
||||
|
||||
// We're either respawning or switching worlds, either way, we are no longer desynched
|
||||
this.setVoidPositionDesynched(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -276,4 +306,48 @@ public class SessionPlayerEntity extends PlayerEntity {
|
||||
public void resetAir() {
|
||||
this.setAirSupply(getMaxAir());
|
||||
}
|
||||
|
||||
private boolean isBelowVoidFloor() {
|
||||
return position.getY() < voidFloorPosition();
|
||||
}
|
||||
|
||||
public int voidFloorPosition() {
|
||||
// The void floor is offset about 40 blocks below the bottom of the world
|
||||
BedrockDimension bedrockDimension = session.getChunkCache().getBedrockDimension();
|
||||
return bedrockDimension.minY() - 40;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method handles teleporting the player below or above the Bedrock void floor.
|
||||
* The Java server should never see this desync as we adjust the position that we send to it
|
||||
*
|
||||
* @param up in which direction to teleport - true to resync our position, or false to be
|
||||
* teleported below the void floor.
|
||||
*/
|
||||
public void teleportVoidFloorFix(boolean up) {
|
||||
// Safety to avoid double teleports
|
||||
if ((voidPositionDesynched && !up) || (!voidPositionDesynched && up)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Work around there being a floor at the bottom of the world and teleport the player below it
|
||||
// Moving from below to above the void floor works fine
|
||||
Vector3f newPosition = this.getPosition();
|
||||
if (up) {
|
||||
newPosition = newPosition.up(4f);
|
||||
voidPositionDesynched = false;
|
||||
} else {
|
||||
newPosition = newPosition.down(4f);
|
||||
voidPositionDesynched = true;
|
||||
}
|
||||
|
||||
this.setPositionManual(newPosition);
|
||||
MovePlayerPacket movePlayerPacket = new MovePlayerPacket();
|
||||
movePlayerPacket.setRuntimeEntityId(geyserId);
|
||||
movePlayerPacket.setPosition(newPosition);
|
||||
movePlayerPacket.setRotation(getBedrockRotation());
|
||||
movePlayerPacket.setMode(MovePlayerPacket.Mode.TELEPORT);
|
||||
movePlayerPacket.setTeleportationCause(MovePlayerPacket.TeleportationCause.BEHAVIOR);
|
||||
session.sendUpstreamPacketImmediately(movePlayerPacket);
|
||||
}
|
||||
}
|
||||
|
@ -32,9 +32,8 @@ import org.cloudburstmc.protocol.bedrock.data.TrimPattern;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.descriptor.ItemDescriptorWithCount;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.descriptor.ItemTagDescriptor;
|
||||
import org.geysermc.geyser.registry.type.ItemMapping;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.registry.RegistryEntryContext;
|
||||
import org.geysermc.geyser.translator.text.MessageTranslator;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.RegistryEntry;
|
||||
|
||||
/**
|
||||
* Stores information on trim materials and patterns, including smithing armor hacks for pre-1.20.
|
||||
@ -46,18 +45,18 @@ public final class TrimRecipe {
|
||||
public static final ItemDescriptorWithCount ADDITION = tagDescriptor("minecraft:trim_materials");
|
||||
public static final ItemDescriptorWithCount TEMPLATE = tagDescriptor("minecraft:trim_templates");
|
||||
|
||||
public static TrimMaterial readTrimMaterial(GeyserSession session, RegistryEntry entry) {
|
||||
String key = entry.getId().asMinimalString();
|
||||
public static TrimMaterial readTrimMaterial(RegistryEntryContext context) {
|
||||
String key = context.id().asMinimalString();
|
||||
|
||||
// Color is used when hovering over the item
|
||||
// Find the nearest legacy color from the RGB Java gives us to work with
|
||||
// Also yes this is a COMPLETE hack but it works ok!!!!!
|
||||
String colorTag = entry.getData().getCompound("description").getString("color");
|
||||
String colorTag = context.data().getCompound("description").getString("color");
|
||||
TextColor color = TextColor.fromHexString(colorTag);
|
||||
String legacy = MessageTranslator.convertMessage(Component.space().color(color));
|
||||
|
||||
String itemIdentifier = entry.getData().getString("ingredient");
|
||||
ItemMapping itemMapping = session.getItemMappings().getMapping(itemIdentifier);
|
||||
String itemIdentifier = context.data().getString("ingredient");
|
||||
ItemMapping itemMapping = context.session().getItemMappings().getMapping(itemIdentifier);
|
||||
if (itemMapping == null) {
|
||||
// This should never happen so not sure what to do here.
|
||||
itemMapping = ItemMapping.AIR;
|
||||
@ -66,11 +65,11 @@ public final class TrimRecipe {
|
||||
return new TrimMaterial(key, legacy.substring(2).trim(), itemMapping.getBedrockIdentifier());
|
||||
}
|
||||
|
||||
public static TrimPattern readTrimPattern(GeyserSession session, RegistryEntry entry) {
|
||||
String key = entry.getId().asMinimalString();
|
||||
public static TrimPattern readTrimPattern(RegistryEntryContext context) {
|
||||
String key = context.id().asMinimalString();
|
||||
|
||||
String itemIdentifier = entry.getData().getString("template_item");
|
||||
ItemMapping itemMapping = session.getItemMappings().getMapping(itemIdentifier);
|
||||
String itemIdentifier = context.data().getString("template_item");
|
||||
ItemMapping itemMapping = context.session().getItemMappings().getMapping(itemIdentifier);
|
||||
if (itemMapping == null) {
|
||||
// This should never happen so not sure what to do here.
|
||||
itemMapping = ItemMapping.AIR;
|
||||
|
@ -27,6 +27,7 @@ package org.geysermc.geyser.inventory.updater;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import java.util.stream.IntStream;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
@ -41,11 +42,14 @@ import org.geysermc.geyser.inventory.item.BedrockEnchantment;
|
||||
import org.geysermc.geyser.item.enchantment.Enchantment;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.tags.EnchantmentTag;
|
||||
import org.geysermc.geyser.session.cache.tags.ItemTag;
|
||||
import org.geysermc.geyser.translator.inventory.InventoryTranslator;
|
||||
import org.geysermc.geyser.translator.text.MessageTranslator;
|
||||
import org.geysermc.geyser.util.ItemUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.HolderSet;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.ItemEnchantments;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundRenameItemPacket;
|
||||
|
||||
@ -310,17 +314,18 @@ public class AnvilInventoryUpdater extends InventoryUpdater {
|
||||
for (Object2IntMap.Entry<Enchantment> entry : getEnchantments(session, material).object2IntEntrySet()) {
|
||||
Enchantment enchantment = entry.getKey();
|
||||
|
||||
boolean canApply = isEnchantedBook(input) || session.getTagCache().is(enchantment.supportedItems(), input);
|
||||
var exclusiveSet = enchantment.exclusiveSet();
|
||||
if (exclusiveSet != null) {
|
||||
int[] incompatibleEnchantments = session.getTagCache().get(exclusiveSet);
|
||||
for (int i : incompatibleEnchantments) {
|
||||
Enchantment incompatible = session.getRegistryCache().enchantments().byId(i);
|
||||
if (combinedEnchantments.containsKey(incompatible)) {
|
||||
canApply = false;
|
||||
if (!bedrock) {
|
||||
cost++;
|
||||
}
|
||||
HolderSet supportedItems = enchantment.supportedItems();
|
||||
int[] supportedItemIds = supportedItems.resolve(tagId -> session.getTagCache().get(ItemTag.ALL_ITEM_TAGS.get(tagId)));
|
||||
boolean canApply = isEnchantedBook(input) || IntStream.of(supportedItemIds).anyMatch(id -> id == input.getJavaId());
|
||||
|
||||
HolderSet exclusiveSet = enchantment.exclusiveSet();
|
||||
int[] incompatibleEnchantments = exclusiveSet.resolve(tagId -> session.getTagCache().get(EnchantmentTag.ALL_ENCHANTMENT_TAGS.get(tagId)));
|
||||
for (int i : incompatibleEnchantments) {
|
||||
Enchantment incompatible = session.getRegistryCache().enchantments().byId(i);
|
||||
if (combinedEnchantments.containsKey(incompatible)) {
|
||||
canApply = false;
|
||||
if (!bedrock) {
|
||||
cost++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
package org.geysermc.geyser.item;
|
||||
|
||||
import org.geysermc.geyser.item.components.Rarity;
|
||||
import org.geysermc.geyser.item.components.ToolTier;
|
||||
import org.geysermc.geyser.item.type.*;
|
||||
import org.geysermc.geyser.level.block.Blocks;
|
||||
@ -122,7 +123,7 @@ public final class Items {
|
||||
public static final Item RAW_IRON_BLOCK = register(new BlockItem(builder(), Blocks.RAW_IRON_BLOCK));
|
||||
public static final Item RAW_COPPER_BLOCK = register(new BlockItem(builder(), Blocks.RAW_COPPER_BLOCK));
|
||||
public static final Item RAW_GOLD_BLOCK = register(new BlockItem(builder(), Blocks.RAW_GOLD_BLOCK));
|
||||
public static final Item HEAVY_CORE = register(new BlockItem(builder(), Blocks.HEAVY_CORE));
|
||||
public static final Item HEAVY_CORE = register(new BlockItem(builder().rarity(Rarity.EPIC), Blocks.HEAVY_CORE));
|
||||
public static final Item AMETHYST_BLOCK = register(new BlockItem(builder(), Blocks.AMETHYST_BLOCK));
|
||||
public static final Item BUDDING_AMETHYST = register(new BlockItem(builder(), Blocks.BUDDING_AMETHYST));
|
||||
public static final Item IRON_BLOCK = register(new BlockItem(builder(), Blocks.IRON_BLOCK));
|
||||
@ -416,7 +417,7 @@ public final class Items {
|
||||
public static final Item END_PORTAL_FRAME = register(new BlockItem(builder(), Blocks.END_PORTAL_FRAME));
|
||||
public static final Item END_STONE = register(new BlockItem(builder(), Blocks.END_STONE));
|
||||
public static final Item END_STONE_BRICKS = register(new BlockItem(builder(), Blocks.END_STONE_BRICKS));
|
||||
public static final Item DRAGON_EGG = register(new BlockItem(builder(), Blocks.DRAGON_EGG));
|
||||
public static final Item DRAGON_EGG = register(new BlockItem(builder().rarity(Rarity.EPIC), Blocks.DRAGON_EGG));
|
||||
public static final Item SANDSTONE_STAIRS = register(new BlockItem(builder(), Blocks.SANDSTONE_STAIRS));
|
||||
public static final Item ENDER_CHEST = register(new BlockItem(builder(), Blocks.ENDER_CHEST));
|
||||
public static final Item EMERALD_BLOCK = register(new BlockItem(builder(), Blocks.EMERALD_BLOCK));
|
||||
@ -432,8 +433,8 @@ public final class Items {
|
||||
public static final Item BAMBOO_MOSAIC_STAIRS = register(new BlockItem(builder(), Blocks.BAMBOO_MOSAIC_STAIRS));
|
||||
public static final Item CRIMSON_STAIRS = register(new BlockItem(builder(), Blocks.CRIMSON_STAIRS));
|
||||
public static final Item WARPED_STAIRS = register(new BlockItem(builder(), Blocks.WARPED_STAIRS));
|
||||
public static final Item COMMAND_BLOCK = register(new BlockItem(builder(), Blocks.COMMAND_BLOCK));
|
||||
public static final Item BEACON = register(new BlockItem(builder(), Blocks.BEACON));
|
||||
public static final Item COMMAND_BLOCK = register(new BlockItem(builder().rarity(Rarity.EPIC), Blocks.COMMAND_BLOCK));
|
||||
public static final Item BEACON = register(new BlockItem(builder().rarity(Rarity.RARE), Blocks.BEACON));
|
||||
public static final Item COBBLESTONE_WALL = register(new BlockItem(builder(), Blocks.COBBLESTONE_WALL));
|
||||
public static final Item MOSSY_COBBLESTONE_WALL = register(new BlockItem(builder(), Blocks.MOSSY_COBBLESTONE_WALL));
|
||||
public static final Item BRICK_WALL = register(new BlockItem(builder(), Blocks.BRICK_WALL));
|
||||
@ -480,8 +481,8 @@ public final class Items {
|
||||
public static final Item GREEN_TERRACOTTA = register(new BlockItem(builder(), Blocks.GREEN_TERRACOTTA));
|
||||
public static final Item RED_TERRACOTTA = register(new BlockItem(builder(), Blocks.RED_TERRACOTTA));
|
||||
public static final Item BLACK_TERRACOTTA = register(new BlockItem(builder(), Blocks.BLACK_TERRACOTTA));
|
||||
public static final Item BARRIER = register(new BlockItem(builder(), Blocks.BARRIER));
|
||||
public static final Item LIGHT = register(new BlockItem(builder(), Blocks.LIGHT));
|
||||
public static final Item BARRIER = register(new BlockItem(builder().rarity(Rarity.EPIC), Blocks.BARRIER));
|
||||
public static final Item LIGHT = register(new BlockItem(builder().rarity(Rarity.EPIC), Blocks.LIGHT));
|
||||
public static final Item HAY_BLOCK = register(new BlockItem(builder(), Blocks.HAY_BLOCK));
|
||||
public static final Item WHITE_CARPET = register(new BlockItem(builder(), Blocks.WHITE_CARPET));
|
||||
public static final Item ORANGE_CARPET = register(new BlockItem(builder(), Blocks.ORANGE_CARPET));
|
||||
@ -551,14 +552,14 @@ public final class Items {
|
||||
public static final Item CHISELED_RED_SANDSTONE = register(new BlockItem(builder(), Blocks.CHISELED_RED_SANDSTONE));
|
||||
public static final Item CUT_RED_SANDSTONE = register(new BlockItem(builder(), Blocks.CUT_RED_SANDSTONE));
|
||||
public static final Item RED_SANDSTONE_STAIRS = register(new BlockItem(builder(), Blocks.RED_SANDSTONE_STAIRS));
|
||||
public static final Item REPEATING_COMMAND_BLOCK = register(new BlockItem(builder(), Blocks.REPEATING_COMMAND_BLOCK));
|
||||
public static final Item CHAIN_COMMAND_BLOCK = register(new BlockItem(builder(), Blocks.CHAIN_COMMAND_BLOCK));
|
||||
public static final Item REPEATING_COMMAND_BLOCK = register(new BlockItem(builder().rarity(Rarity.EPIC), Blocks.REPEATING_COMMAND_BLOCK));
|
||||
public static final Item CHAIN_COMMAND_BLOCK = register(new BlockItem(builder().rarity(Rarity.EPIC), Blocks.CHAIN_COMMAND_BLOCK));
|
||||
public static final Item MAGMA_BLOCK = register(new BlockItem(builder(), Blocks.MAGMA_BLOCK));
|
||||
public static final Item NETHER_WART_BLOCK = register(new BlockItem(builder(), Blocks.NETHER_WART_BLOCK));
|
||||
public static final Item WARPED_WART_BLOCK = register(new BlockItem(builder(), Blocks.WARPED_WART_BLOCK));
|
||||
public static final Item RED_NETHER_BRICKS = register(new BlockItem(builder(), Blocks.RED_NETHER_BRICKS));
|
||||
public static final Item BONE_BLOCK = register(new BlockItem(builder(), Blocks.BONE_BLOCK));
|
||||
public static final Item STRUCTURE_VOID = register(new BlockItem(builder(), Blocks.STRUCTURE_VOID));
|
||||
public static final Item STRUCTURE_VOID = register(new BlockItem(builder().rarity(Rarity.EPIC), Blocks.STRUCTURE_VOID));
|
||||
public static final Item SHULKER_BOX = register(new ShulkerBoxItem(builder().stackSize(1), Blocks.SHULKER_BOX));
|
||||
public static final Item WHITE_SHULKER_BOX = register(new ShulkerBoxItem(builder().stackSize(1), Blocks.WHITE_SHULKER_BOX));
|
||||
public static final Item ORANGE_SHULKER_BOX = register(new ShulkerBoxItem(builder().stackSize(1), Blocks.ORANGE_SHULKER_BOX));
|
||||
@ -657,7 +658,7 @@ public final class Items {
|
||||
public static final Item DEAD_FIRE_CORAL_FAN = register(new BlockItem(builder(), Blocks.DEAD_FIRE_CORAL_FAN, Blocks.DEAD_FIRE_CORAL_WALL_FAN));
|
||||
public static final Item DEAD_HORN_CORAL_FAN = register(new BlockItem(builder(), Blocks.DEAD_HORN_CORAL_FAN, Blocks.DEAD_HORN_CORAL_WALL_FAN));
|
||||
public static final Item BLUE_ICE = register(new BlockItem(builder(), Blocks.BLUE_ICE));
|
||||
public static final Item CONDUIT = register(new BlockItem(builder(), Blocks.CONDUIT));
|
||||
public static final Item CONDUIT = register(new BlockItem(builder().rarity(Rarity.RARE), Blocks.CONDUIT));
|
||||
public static final Item POLISHED_GRANITE_STAIRS = register(new BlockItem(builder(), Blocks.POLISHED_GRANITE_STAIRS));
|
||||
public static final Item SMOOTH_RED_SANDSTONE_STAIRS = register(new BlockItem(builder(), Blocks.SMOOTH_RED_SANDSTONE_STAIRS));
|
||||
public static final Item MOSSY_STONE_BRICK_STAIRS = register(new BlockItem(builder(), Blocks.MOSSY_STONE_BRICK_STAIRS));
|
||||
@ -810,7 +811,7 @@ public final class Items {
|
||||
public static final Item HOPPER_MINECART = register(new Item("hopper_minecart", builder().stackSize(1)));
|
||||
public static final Item CARROT_ON_A_STICK = register(new Item("carrot_on_a_stick", builder().stackSize(1).maxDamage(25)));
|
||||
public static final Item WARPED_FUNGUS_ON_A_STICK = register(new Item("warped_fungus_on_a_stick", builder().stackSize(1).maxDamage(100)));
|
||||
public static final Item ELYTRA = register(new ElytraItem("elytra", builder().stackSize(1).maxDamage(432)));
|
||||
public static final Item ELYTRA = register(new ElytraItem("elytra", builder().stackSize(1).maxDamage(432).rarity(Rarity.UNCOMMON)));
|
||||
public static final Item OAK_BOAT = register(new BoatItem("oak_boat", builder().stackSize(1)));
|
||||
public static final Item OAK_CHEST_BOAT = register(new BoatItem("oak_chest_boat", builder().stackSize(1)));
|
||||
public static final Item SPRUCE_BOAT = register(new BoatItem("spruce_boat", builder().stackSize(1)));
|
||||
@ -829,8 +830,8 @@ public final class Items {
|
||||
public static final Item MANGROVE_CHEST_BOAT = register(new BoatItem("mangrove_chest_boat", builder().stackSize(1)));
|
||||
public static final Item BAMBOO_RAFT = register(new BoatItem("bamboo_raft", builder().stackSize(1)));
|
||||
public static final Item BAMBOO_CHEST_RAFT = register(new BoatItem("bamboo_chest_raft", builder().stackSize(1)));
|
||||
public static final Item STRUCTURE_BLOCK = register(new BlockItem(builder(), Blocks.STRUCTURE_BLOCK));
|
||||
public static final Item JIGSAW = register(new BlockItem(builder(), Blocks.JIGSAW));
|
||||
public static final Item STRUCTURE_BLOCK = register(new BlockItem(builder().rarity(Rarity.EPIC), Blocks.STRUCTURE_BLOCK));
|
||||
public static final Item JIGSAW = register(new BlockItem(builder().rarity(Rarity.EPIC), Blocks.JIGSAW));
|
||||
public static final Item TURTLE_HELMET = register(new ArmorItem("turtle_helmet", ArmorMaterial.TURTLE, builder().stackSize(1).maxDamage(275)));
|
||||
public static final Item TURTLE_SCUTE = register(new Item("turtle_scute", builder()));
|
||||
public static final Item ARMADILLO_SCUTE = register(new Item("armadillo_scute", builder()));
|
||||
@ -921,8 +922,8 @@ public final class Items {
|
||||
public static final Item PORKCHOP = register(new Item("porkchop", builder()));
|
||||
public static final Item COOKED_PORKCHOP = register(new Item("cooked_porkchop", builder()));
|
||||
public static final Item PAINTING = register(new Item("painting", builder()));
|
||||
public static final Item GOLDEN_APPLE = register(new Item("golden_apple", builder()));
|
||||
public static final Item ENCHANTED_GOLDEN_APPLE = register(new Item("enchanted_golden_apple", builder()));
|
||||
public static final Item GOLDEN_APPLE = register(new Item("golden_apple", builder().rarity(Rarity.RARE)));
|
||||
public static final Item ENCHANTED_GOLDEN_APPLE = register(new Item("enchanted_golden_apple", builder().rarity(Rarity.EPIC)));
|
||||
public static final Item OAK_SIGN = register(new BlockItem(builder().stackSize(16), Blocks.OAK_SIGN, Blocks.OAK_WALL_SIGN));
|
||||
public static final Item SPRUCE_SIGN = register(new BlockItem(builder().stackSize(16), Blocks.SPRUCE_SIGN, Blocks.SPRUCE_WALL_SIGN));
|
||||
public static final Item BIRCH_SIGN = register(new BlockItem(builder().stackSize(16), Blocks.BIRCH_SIGN, Blocks.BIRCH_WALL_SIGN));
|
||||
@ -1042,7 +1043,7 @@ public final class Items {
|
||||
public static final Item BLAZE_POWDER = register(new Item("blaze_powder", builder()));
|
||||
public static final Item MAGMA_CREAM = register(new Item("magma_cream", builder()));
|
||||
public static final Item BREWING_STAND = register(new BlockItem(builder(), Blocks.BREWING_STAND));
|
||||
public static final Item CAULDRON = register(new BlockItem(builder(), Blocks.CAULDRON, Blocks.WATER_CAULDRON, Blocks.LAVA_CAULDRON, Blocks.POWDER_SNOW_CAULDRON));
|
||||
public static final Item CAULDRON = register(new BlockItem(builder(), Blocks.CAULDRON, Blocks.WATER_CAULDRON, Blocks.POWDER_SNOW_CAULDRON, Blocks.LAVA_CAULDRON));
|
||||
public static final Item ENDER_EYE = register(new Item("ender_eye", builder()));
|
||||
public static final Item GLISTERING_MELON_SLICE = register(new Item("glistering_melon_slice", builder()));
|
||||
public static final Item ARMADILLO_SPAWN_EGG = register(new SpawnEggItem("armadillo_spawn_egg", builder()));
|
||||
@ -1125,12 +1126,12 @@ public final class Items {
|
||||
public static final Item ZOMBIE_HORSE_SPAWN_EGG = register(new SpawnEggItem("zombie_horse_spawn_egg", builder()));
|
||||
public static final Item ZOMBIE_VILLAGER_SPAWN_EGG = register(new SpawnEggItem("zombie_villager_spawn_egg", builder()));
|
||||
public static final Item ZOMBIFIED_PIGLIN_SPAWN_EGG = register(new SpawnEggItem("zombified_piglin_spawn_egg", builder()));
|
||||
public static final Item EXPERIENCE_BOTTLE = register(new Item("experience_bottle", builder()));
|
||||
public static final Item EXPERIENCE_BOTTLE = register(new Item("experience_bottle", builder().rarity(Rarity.UNCOMMON)));
|
||||
public static final Item FIRE_CHARGE = register(new Item("fire_charge", builder()));
|
||||
public static final Item WIND_CHARGE = register(new Item("wind_charge", builder()));
|
||||
public static final Item WRITABLE_BOOK = register(new WritableBookItem("writable_book", builder().stackSize(1)));
|
||||
public static final Item WRITTEN_BOOK = register(new WrittenBookItem("written_book", builder().stackSize(16)));
|
||||
public static final Item MACE = register(new MaceItem("mace", builder().stackSize(1).maxDamage(500)));
|
||||
public static final Item MACE = register(new MaceItem("mace", builder().stackSize(1).maxDamage(500).rarity(Rarity.EPIC)));
|
||||
public static final Item ITEM_FRAME = register(new Item("item_frame", builder()));
|
||||
public static final Item GLOW_ITEM_FRAME = register(new Item("glow_item_frame", builder()));
|
||||
public static final Item FLOWER_POT = register(new BlockItem(builder(), Blocks.FLOWER_POT));
|
||||
@ -1140,18 +1141,18 @@ public final class Items {
|
||||
public static final Item POISONOUS_POTATO = register(new Item("poisonous_potato", builder()));
|
||||
public static final Item MAP = register(new MapItem("map", builder()));
|
||||
public static final Item GOLDEN_CARROT = register(new Item("golden_carrot", builder()));
|
||||
public static final Item SKELETON_SKULL = register(new BlockItem(builder(), Blocks.SKELETON_SKULL, Blocks.SKELETON_WALL_SKULL));
|
||||
public static final Item WITHER_SKELETON_SKULL = register(new BlockItem(builder(), Blocks.WITHER_SKELETON_SKULL, Blocks.WITHER_SKELETON_WALL_SKULL));
|
||||
public static final Item PLAYER_HEAD = register(new PlayerHeadItem(builder(), Blocks.PLAYER_HEAD, Blocks.PLAYER_WALL_HEAD));
|
||||
public static final Item ZOMBIE_HEAD = register(new BlockItem(builder(), Blocks.ZOMBIE_HEAD, Blocks.ZOMBIE_WALL_HEAD));
|
||||
public static final Item CREEPER_HEAD = register(new BlockItem(builder(), Blocks.CREEPER_HEAD, Blocks.CREEPER_WALL_HEAD));
|
||||
public static final Item DRAGON_HEAD = register(new BlockItem(builder(), Blocks.DRAGON_HEAD, Blocks.DRAGON_WALL_HEAD));
|
||||
public static final Item PIGLIN_HEAD = register(new BlockItem(builder(), Blocks.PIGLIN_HEAD, Blocks.PIGLIN_WALL_HEAD));
|
||||
public static final Item NETHER_STAR = register(new Item("nether_star", builder()));
|
||||
public static final Item SKELETON_SKULL = register(new BlockItem(builder().rarity(Rarity.UNCOMMON), Blocks.SKELETON_SKULL, Blocks.SKELETON_WALL_SKULL));
|
||||
public static final Item WITHER_SKELETON_SKULL = register(new BlockItem(builder().rarity(Rarity.UNCOMMON), Blocks.WITHER_SKELETON_SKULL, Blocks.WITHER_SKELETON_WALL_SKULL));
|
||||
public static final Item PLAYER_HEAD = register(new PlayerHeadItem(builder().rarity(Rarity.UNCOMMON), Blocks.PLAYER_HEAD, Blocks.PLAYER_WALL_HEAD));
|
||||
public static final Item ZOMBIE_HEAD = register(new BlockItem(builder().rarity(Rarity.UNCOMMON), Blocks.ZOMBIE_HEAD, Blocks.ZOMBIE_WALL_HEAD));
|
||||
public static final Item CREEPER_HEAD = register(new BlockItem(builder().rarity(Rarity.UNCOMMON), Blocks.CREEPER_HEAD, Blocks.CREEPER_WALL_HEAD));
|
||||
public static final Item DRAGON_HEAD = register(new BlockItem(builder().rarity(Rarity.UNCOMMON), Blocks.DRAGON_HEAD, Blocks.DRAGON_WALL_HEAD));
|
||||
public static final Item PIGLIN_HEAD = register(new BlockItem(builder().rarity(Rarity.UNCOMMON), Blocks.PIGLIN_HEAD, Blocks.PIGLIN_WALL_HEAD));
|
||||
public static final Item NETHER_STAR = register(new Item("nether_star", builder().rarity(Rarity.UNCOMMON)));
|
||||
public static final Item PUMPKIN_PIE = register(new Item("pumpkin_pie", builder()));
|
||||
public static final Item FIREWORK_ROCKET = register(new FireworkRocketItem("firework_rocket", builder()));
|
||||
public static final Item FIREWORK_STAR = register(new FireworkStarItem("firework_star", builder()));
|
||||
public static final Item ENCHANTED_BOOK = register(new EnchantedBookItem("enchanted_book", builder().stackSize(1)));
|
||||
public static final Item ENCHANTED_BOOK = register(new EnchantedBookItem("enchanted_book", builder().stackSize(1).rarity(Rarity.UNCOMMON)));
|
||||
public static final Item NETHER_BRICK = register(new Item("nether_brick", builder()));
|
||||
public static final Item PRISMARINE_SHARD = register(new Item("prismarine_shard", builder()));
|
||||
public static final Item PRISMARINE_CRYSTALS = register(new Item("prismarine_crystals", builder()));
|
||||
@ -1167,7 +1168,7 @@ public final class Items {
|
||||
public static final Item LEATHER_HORSE_ARMOR = register(new DyeableArmorItem("leather_horse_armor", ArmorMaterial.LEATHER, builder().stackSize(1)));
|
||||
public static final Item LEAD = register(new Item("lead", builder()));
|
||||
public static final Item NAME_TAG = register(new Item("name_tag", builder()));
|
||||
public static final Item COMMAND_BLOCK_MINECART = register(new Item("command_block_minecart", builder().stackSize(1)));
|
||||
public static final Item COMMAND_BLOCK_MINECART = register(new Item("command_block_minecart", builder().stackSize(1).rarity(Rarity.EPIC)));
|
||||
public static final Item MUTTON = register(new Item("mutton", builder()));
|
||||
public static final Item COOKED_MUTTON = register(new Item("cooked_mutton", builder()));
|
||||
public static final Item WHITE_BANNER = register(new BannerItem(builder().stackSize(16), Blocks.WHITE_BANNER, Blocks.WHITE_WALL_BANNER));
|
||||
@ -1186,7 +1187,7 @@ public final class Items {
|
||||
public static final Item GREEN_BANNER = register(new BannerItem(builder().stackSize(16), Blocks.GREEN_BANNER, Blocks.GREEN_WALL_BANNER));
|
||||
public static final Item RED_BANNER = register(new BannerItem(builder().stackSize(16), Blocks.RED_BANNER, Blocks.RED_WALL_BANNER));
|
||||
public static final Item BLACK_BANNER = register(new BannerItem(builder().stackSize(16), Blocks.BLACK_BANNER, Blocks.BLACK_WALL_BANNER));
|
||||
public static final Item END_CRYSTAL = register(new Item("end_crystal", builder()));
|
||||
public static final Item END_CRYSTAL = register(new Item("end_crystal", builder().rarity(Rarity.RARE)));
|
||||
public static final Item CHORUS_FRUIT = register(new Item("chorus_fruit", builder()));
|
||||
public static final Item POPPED_CHORUS_FRUIT = register(new Item("popped_chorus_fruit", builder()));
|
||||
public static final Item TORCHFLOWER_SEEDS = register(new BlockItem("torchflower_seeds", builder(), Blocks.TORCHFLOWER_CROP));
|
||||
@ -1194,52 +1195,52 @@ public final class Items {
|
||||
public static final Item BEETROOT = register(new Item("beetroot", builder()));
|
||||
public static final Item BEETROOT_SEEDS = register(new BlockItem("beetroot_seeds", builder(), Blocks.BEETROOTS));
|
||||
public static final Item BEETROOT_SOUP = register(new Item("beetroot_soup", builder().stackSize(1)));
|
||||
public static final Item DRAGON_BREATH = register(new Item("dragon_breath", builder()));
|
||||
public static final Item DRAGON_BREATH = register(new Item("dragon_breath", builder().rarity(Rarity.UNCOMMON)));
|
||||
public static final Item SPLASH_POTION = register(new PotionItem("splash_potion", builder().stackSize(1)));
|
||||
public static final Item SPECTRAL_ARROW = register(new Item("spectral_arrow", builder()));
|
||||
public static final Item TIPPED_ARROW = register(new TippedArrowItem("tipped_arrow", builder()));
|
||||
public static final Item LINGERING_POTION = register(new PotionItem("lingering_potion", builder().stackSize(1)));
|
||||
public static final Item SHIELD = register(new ShieldItem("shield", builder().stackSize(1).maxDamage(336)));
|
||||
public static final Item TOTEM_OF_UNDYING = register(new Item("totem_of_undying", builder().stackSize(1)));
|
||||
public static final Item TOTEM_OF_UNDYING = register(new Item("totem_of_undying", builder().stackSize(1).rarity(Rarity.UNCOMMON)));
|
||||
public static final Item SHULKER_SHELL = register(new Item("shulker_shell", builder()));
|
||||
public static final Item IRON_NUGGET = register(new Item("iron_nugget", builder()));
|
||||
public static final Item KNOWLEDGE_BOOK = register(new Item("knowledge_book", builder().stackSize(1)));
|
||||
public static final Item DEBUG_STICK = register(new Item("debug_stick", builder().stackSize(1)));
|
||||
public static final Item MUSIC_DISC_13 = register(new Item("music_disc_13", builder().stackSize(1)));
|
||||
public static final Item MUSIC_DISC_CAT = register(new Item("music_disc_cat", builder().stackSize(1)));
|
||||
public static final Item MUSIC_DISC_BLOCKS = register(new Item("music_disc_blocks", builder().stackSize(1)));
|
||||
public static final Item MUSIC_DISC_CHIRP = register(new Item("music_disc_chirp", builder().stackSize(1)));
|
||||
public static final Item MUSIC_DISC_CREATOR = register(new Item("music_disc_creator", builder().stackSize(1)));
|
||||
public static final Item MUSIC_DISC_CREATOR_MUSIC_BOX = register(new Item("music_disc_creator_music_box", builder().stackSize(1)));
|
||||
public static final Item MUSIC_DISC_FAR = register(new Item("music_disc_far", builder().stackSize(1)));
|
||||
public static final Item MUSIC_DISC_MALL = register(new Item("music_disc_mall", builder().stackSize(1)));
|
||||
public static final Item MUSIC_DISC_MELLOHI = register(new Item("music_disc_mellohi", builder().stackSize(1)));
|
||||
public static final Item MUSIC_DISC_STAL = register(new Item("music_disc_stal", builder().stackSize(1)));
|
||||
public static final Item MUSIC_DISC_STRAD = register(new Item("music_disc_strad", builder().stackSize(1)));
|
||||
public static final Item MUSIC_DISC_WARD = register(new Item("music_disc_ward", builder().stackSize(1)));
|
||||
public static final Item MUSIC_DISC_11 = register(new Item("music_disc_11", builder().stackSize(1)));
|
||||
public static final Item MUSIC_DISC_WAIT = register(new Item("music_disc_wait", builder().stackSize(1)));
|
||||
public static final Item MUSIC_DISC_OTHERSIDE = register(new Item("music_disc_otherside", builder().stackSize(1)));
|
||||
public static final Item MUSIC_DISC_RELIC = register(new Item("music_disc_relic", builder().stackSize(1)));
|
||||
public static final Item MUSIC_DISC_5 = register(new Item("music_disc_5", builder().stackSize(1)));
|
||||
public static final Item MUSIC_DISC_PIGSTEP = register(new Item("music_disc_pigstep", builder().stackSize(1)));
|
||||
public static final Item MUSIC_DISC_PRECIPICE = register(new Item("music_disc_precipice", builder().stackSize(1)));
|
||||
public static final Item KNOWLEDGE_BOOK = register(new Item("knowledge_book", builder().stackSize(1).rarity(Rarity.EPIC)));
|
||||
public static final Item DEBUG_STICK = register(new Item("debug_stick", builder().stackSize(1).rarity(Rarity.EPIC).glint(true)));
|
||||
public static final Item MUSIC_DISC_13 = register(new Item("music_disc_13", builder().stackSize(1).rarity(Rarity.RARE)));
|
||||
public static final Item MUSIC_DISC_CAT = register(new Item("music_disc_cat", builder().stackSize(1).rarity(Rarity.RARE)));
|
||||
public static final Item MUSIC_DISC_BLOCKS = register(new Item("music_disc_blocks", builder().stackSize(1).rarity(Rarity.RARE)));
|
||||
public static final Item MUSIC_DISC_CHIRP = register(new Item("music_disc_chirp", builder().stackSize(1).rarity(Rarity.RARE)));
|
||||
public static final Item MUSIC_DISC_CREATOR = register(new Item("music_disc_creator", builder().stackSize(1).rarity(Rarity.RARE)));
|
||||
public static final Item MUSIC_DISC_CREATOR_MUSIC_BOX = register(new Item("music_disc_creator_music_box", builder().stackSize(1).rarity(Rarity.RARE)));
|
||||
public static final Item MUSIC_DISC_FAR = register(new Item("music_disc_far", builder().stackSize(1).rarity(Rarity.RARE)));
|
||||
public static final Item MUSIC_DISC_MALL = register(new Item("music_disc_mall", builder().stackSize(1).rarity(Rarity.RARE)));
|
||||
public static final Item MUSIC_DISC_MELLOHI = register(new Item("music_disc_mellohi", builder().stackSize(1).rarity(Rarity.RARE)));
|
||||
public static final Item MUSIC_DISC_STAL = register(new Item("music_disc_stal", builder().stackSize(1).rarity(Rarity.RARE)));
|
||||
public static final Item MUSIC_DISC_STRAD = register(new Item("music_disc_strad", builder().stackSize(1).rarity(Rarity.RARE)));
|
||||
public static final Item MUSIC_DISC_WARD = register(new Item("music_disc_ward", builder().stackSize(1).rarity(Rarity.RARE)));
|
||||
public static final Item MUSIC_DISC_11 = register(new Item("music_disc_11", builder().stackSize(1).rarity(Rarity.RARE)));
|
||||
public static final Item MUSIC_DISC_WAIT = register(new Item("music_disc_wait", builder().stackSize(1).rarity(Rarity.RARE)));
|
||||
public static final Item MUSIC_DISC_OTHERSIDE = register(new Item("music_disc_otherside", builder().stackSize(1).rarity(Rarity.RARE)));
|
||||
public static final Item MUSIC_DISC_RELIC = register(new Item("music_disc_relic", builder().stackSize(1).rarity(Rarity.RARE)));
|
||||
public static final Item MUSIC_DISC_5 = register(new Item("music_disc_5", builder().stackSize(1).rarity(Rarity.RARE)));
|
||||
public static final Item MUSIC_DISC_PIGSTEP = register(new Item("music_disc_pigstep", builder().stackSize(1).rarity(Rarity.RARE)));
|
||||
public static final Item MUSIC_DISC_PRECIPICE = register(new Item("music_disc_precipice", builder().stackSize(1).rarity(Rarity.RARE)));
|
||||
public static final Item DISC_FRAGMENT_5 = register(new Item("disc_fragment_5", builder()));
|
||||
public static final Item TRIDENT = register(new Item("trident", builder().stackSize(1).maxDamage(250).attackDamage(9.0)));
|
||||
public static final Item TRIDENT = register(new Item("trident", builder().stackSize(1).maxDamage(250).attackDamage(9.0).rarity(Rarity.EPIC)));
|
||||
public static final Item PHANTOM_MEMBRANE = register(new Item("phantom_membrane", builder()));
|
||||
public static final Item NAUTILUS_SHELL = register(new Item("nautilus_shell", builder()));
|
||||
public static final Item HEART_OF_THE_SEA = register(new Item("heart_of_the_sea", builder()));
|
||||
public static final Item HEART_OF_THE_SEA = register(new Item("heart_of_the_sea", builder().rarity(Rarity.UNCOMMON)));
|
||||
public static final Item CROSSBOW = register(new CrossbowItem("crossbow", builder().stackSize(1).maxDamage(465)));
|
||||
public static final Item SUSPICIOUS_STEW = register(new Item("suspicious_stew", builder().stackSize(1)));
|
||||
public static final Item LOOM = register(new BlockItem(builder(), Blocks.LOOM));
|
||||
public static final Item FLOWER_BANNER_PATTERN = register(new Item("flower_banner_pattern", builder().stackSize(1)));
|
||||
public static final Item CREEPER_BANNER_PATTERN = register(new Item("creeper_banner_pattern", builder().stackSize(1)));
|
||||
public static final Item SKULL_BANNER_PATTERN = register(new Item("skull_banner_pattern", builder().stackSize(1)));
|
||||
public static final Item MOJANG_BANNER_PATTERN = register(new Item("mojang_banner_pattern", builder().stackSize(1)));
|
||||
public static final Item CREEPER_BANNER_PATTERN = register(new Item("creeper_banner_pattern", builder().stackSize(1).rarity(Rarity.UNCOMMON)));
|
||||
public static final Item SKULL_BANNER_PATTERN = register(new Item("skull_banner_pattern", builder().stackSize(1).rarity(Rarity.UNCOMMON)));
|
||||
public static final Item MOJANG_BANNER_PATTERN = register(new Item("mojang_banner_pattern", builder().stackSize(1).rarity(Rarity.EPIC)));
|
||||
public static final Item GLOBE_BANNER_PATTERN = register(new Item("globe_banner_pattern", builder().stackSize(1)));
|
||||
public static final Item PIGLIN_BANNER_PATTERN = register(new Item("piglin_banner_pattern", builder().stackSize(1)));
|
||||
public static final Item FLOW_BANNER_PATTERN = register(new Item("flow_banner_pattern", builder().stackSize(1)));
|
||||
public static final Item GUSTER_BANNER_PATTERN = register(new Item("guster_banner_pattern", builder().stackSize(1)));
|
||||
public static final Item PIGLIN_BANNER_PATTERN = register(new Item("piglin_banner_pattern", builder().stackSize(1).rarity(Rarity.UNCOMMON)));
|
||||
public static final Item FLOW_BANNER_PATTERN = register(new Item("flow_banner_pattern", builder().stackSize(1).rarity(Rarity.RARE)));
|
||||
public static final Item GUSTER_BANNER_PATTERN = register(new Item("guster_banner_pattern", builder().stackSize(1).rarity(Rarity.RARE)));
|
||||
public static final Item GOAT_HORN = register(new GoatHornItem("goat_horn", builder().stackSize(1)));
|
||||
public static final Item COMPOSTER = register(new BlockItem(builder(), Blocks.COMPOSTER));
|
||||
public static final Item BARREL = register(new BlockItem(builder(), Blocks.BARREL));
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
* 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
|
||||
@ -21,4 +21,31 @@
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.item.components;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public enum Rarity {
|
||||
COMMON("common", 'f'),
|
||||
UNCOMMON("uncommon", 'e'),
|
||||
RARE("rare", 'b'),
|
||||
EPIC("epic", 'd');
|
||||
|
||||
private final String name;
|
||||
private final char color;
|
||||
|
||||
Rarity(final String name, char chatColor) {
|
||||
this.name = name;
|
||||
this.color = chatColor;
|
||||
}
|
||||
|
||||
private static final Rarity[] VALUES = values();
|
||||
|
||||
public static Rarity fromId(int id) {
|
||||
return VALUES.length > id ? VALUES[id] : VALUES[0];
|
||||
}
|
||||
|
||||
}
|
@ -25,18 +25,21 @@
|
||||
|
||||
package org.geysermc.geyser.item.enchantment;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.geysermc.geyser.inventory.item.BedrockEnchantment;
|
||||
import org.geysermc.geyser.session.cache.tags.EnchantmentTag;
|
||||
import org.geysermc.geyser.session.cache.tags.ItemTag;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.registry.Registries;
|
||||
import org.geysermc.geyser.session.cache.registry.RegistryEntryContext;
|
||||
import org.geysermc.geyser.translator.text.MessageTranslator;
|
||||
import org.geysermc.geyser.util.MinecraftKey;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.RegistryEntry;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.HolderSet;
|
||||
|
||||
/**
|
||||
* @param description only populated if {@link #bedrockEnchantment()} is not null.
|
||||
@ -44,28 +47,32 @@ import java.util.Set;
|
||||
*/
|
||||
public record Enchantment(String identifier,
|
||||
Set<EnchantmentComponent> effects,
|
||||
ItemTag supportedItems,
|
||||
HolderSet supportedItems,
|
||||
int maxLevel,
|
||||
String description,
|
||||
int anvilCost,
|
||||
@Nullable EnchantmentTag exclusiveSet,
|
||||
HolderSet exclusiveSet,
|
||||
@Nullable BedrockEnchantment bedrockEnchantment) {
|
||||
|
||||
// Implementation note: I have a feeling the tags can be a list of items, because in vanilla they're HolderSet classes.
|
||||
// I'm not sure how that's wired over the network, so we'll put it off.
|
||||
public static Enchantment read(RegistryEntry entry) {
|
||||
NbtMap data = entry.getData();
|
||||
public static Enchantment read(RegistryEntryContext context) {
|
||||
NbtMap data = context.data();
|
||||
Set<EnchantmentComponent> effects = readEnchantmentComponents(data.getCompound("effects"));
|
||||
String supportedItems = data.getString("supported_items").substring(1); // Remove '#' at beginning that indicates tag
|
||||
|
||||
HolderSet supportedItems = readHolderSet(data.get("supported_items"), itemId -> Registries.JAVA_ITEM_IDENTIFIERS.getOrDefault(itemId.asString(), Items.AIR).javaId());
|
||||
|
||||
int maxLevel = data.getInt("max_level");
|
||||
int anvilCost = data.getInt("anvil_cost");
|
||||
String exclusiveSet = data.getString("exclusive_set", null);
|
||||
EnchantmentTag exclusiveSetTag = exclusiveSet == null ? null : EnchantmentTag.ALL_ENCHANTMENT_TAGS.get(MinecraftKey.key(exclusiveSet.substring(1)));
|
||||
BedrockEnchantment bedrockEnchantment = BedrockEnchantment.getByJavaIdentifier(entry.getId().asString());
|
||||
|
||||
HolderSet exclusiveSet = readHolderSet(data.getOrDefault("exclusive_set", null), context::getNetworkId);
|
||||
|
||||
BedrockEnchantment bedrockEnchantment = BedrockEnchantment.getByJavaIdentifier(context.id().asString());
|
||||
|
||||
// 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.
|
||||
String description = bedrockEnchantment == null ? MessageTranslator.deserializeDescription(data) : null;
|
||||
|
||||
return new Enchantment(entry.getId().asString(), effects, ItemTag.ALL_ITEM_TAGS.get(MinecraftKey.key(supportedItems)), maxLevel,
|
||||
description, anvilCost, exclusiveSetTag, bedrockEnchantment);
|
||||
return new Enchantment(context.id().asString(), effects, supportedItems, maxLevel,
|
||||
description, anvilCost, exclusiveSet, bedrockEnchantment);
|
||||
}
|
||||
|
||||
private static Set<EnchantmentComponent> readEnchantmentComponents(NbtMap effects) {
|
||||
@ -77,4 +84,24 @@ public record Enchantment(String identifier,
|
||||
}
|
||||
return Set.copyOf(components); // Also ensures any empty sets are consolidated
|
||||
}
|
||||
|
||||
// TODO holder set util?
|
||||
private static HolderSet readHolderSet(@Nullable Object holderSet, Function<Key, Integer> keyIdMapping) {
|
||||
if (holderSet == null) {
|
||||
return new HolderSet(new int[]{});
|
||||
}
|
||||
|
||||
if (holderSet instanceof String stringTag) {
|
||||
// Tag
|
||||
if (stringTag.startsWith("#")) {
|
||||
return new HolderSet(Key.key(stringTag.substring(1))); // Remove '#' at beginning that indicates tag
|
||||
} else {
|
||||
return new HolderSet(new int[]{keyIdMapping.apply(Key.key(stringTag))});
|
||||
}
|
||||
} else if (holderSet instanceof List<?> list) {
|
||||
// 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());
|
||||
}
|
||||
throw new IllegalArgumentException("Holder set must either be a tag, a string ID or a list of string IDs");
|
||||
}
|
||||
}
|
||||
|
@ -51,6 +51,7 @@ public class FilledMapItem extends MapItem {
|
||||
switch (mapColor) {
|
||||
case 3830373 -> builder.damage(3); // Ocean Monument
|
||||
case 5393476 -> builder.damage(4); // Woodland explorer
|
||||
case 12741452 -> builder.damage(14); // Trial Chamber
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
import org.geysermc.geyser.inventory.item.BedrockEnchantment;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.item.components.Rarity;
|
||||
import org.geysermc.geyser.item.enchantment.Enchantment;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.geyser.registry.type.ItemMapping;
|
||||
@ -63,12 +64,16 @@ public class Item {
|
||||
private final int stackSize;
|
||||
private final int attackDamage;
|
||||
private final int maxDamage;
|
||||
private final Rarity rarity;
|
||||
private final boolean glint;
|
||||
|
||||
public Item(String javaIdentifier, Builder builder) {
|
||||
this.javaIdentifier = MinecraftKey.key(javaIdentifier).asString().intern();
|
||||
this.stackSize = builder.stackSize;
|
||||
this.maxDamage = builder.maxDamage;
|
||||
this.attackDamage = builder.attackDamage;
|
||||
this.rarity = builder.rarity;
|
||||
this.glint = builder.glint;
|
||||
}
|
||||
|
||||
public String javaIdentifier() {
|
||||
@ -91,6 +96,14 @@ public class Item {
|
||||
return stackSize;
|
||||
}
|
||||
|
||||
public Rarity rarity() {
|
||||
return rarity;
|
||||
}
|
||||
|
||||
public boolean glint() {
|
||||
return glint;
|
||||
}
|
||||
|
||||
public boolean isValidRepairItem(Item other) {
|
||||
return false;
|
||||
}
|
||||
@ -275,6 +288,8 @@ public class Item {
|
||||
private int stackSize = 64;
|
||||
private int maxDamage;
|
||||
private int attackDamage;
|
||||
private Rarity rarity = Rarity.COMMON;
|
||||
private boolean glint = false;
|
||||
|
||||
public Builder stackSize(int stackSize) {
|
||||
this.stackSize = stackSize;
|
||||
@ -292,6 +307,16 @@ public class Item {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder rarity(Rarity rarity) {
|
||||
this.rarity = rarity;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder glint(boolean glintOverride) {
|
||||
this.glint = glintOverride;
|
||||
return this;
|
||||
}
|
||||
|
||||
private Builder() {
|
||||
}
|
||||
}
|
||||
|
@ -29,15 +29,19 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.cloudburstmc.nbt.NbtType;
|
||||
import org.cloudburstmc.protocol.bedrock.data.definitions.ItemDefinition;
|
||||
import org.geysermc.geyser.inventory.item.Potion;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.geyser.registry.type.ItemMapping;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.item.BedrockItemBuilder;
|
||||
import org.geysermc.geyser.translator.item.CustomItemTranslator;
|
||||
import org.geysermc.geyser.translator.item.ItemTranslator;
|
||||
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.DataComponents;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.PotionContents;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -64,12 +68,35 @@ public class ShulkerBoxItem extends BlockItem {
|
||||
}
|
||||
ItemMapping boxMapping = session.getItemMappings().getMapping(item.getId());
|
||||
|
||||
NbtMapBuilder boxItemNbt = BedrockItemBuilder.createItemNbt(boxMapping, item.getAmount(), boxMapping.getBedrockData()); // Final item tag to add to the list
|
||||
int bedrockData = boxMapping.getBedrockData();
|
||||
String bedrockIdentifier = boxMapping.getBedrockIdentifier();
|
||||
DataComponents boxComponents = item.getDataComponents();
|
||||
|
||||
if (boxComponents != null) {
|
||||
// Check for custom items
|
||||
ItemDefinition customItemDefinition = CustomItemTranslator.getCustomItem(boxComponents, boxMapping);
|
||||
if (customItemDefinition != null) {
|
||||
bedrockIdentifier = customItemDefinition.getIdentifier();
|
||||
bedrockData = 0;
|
||||
} else {
|
||||
// Manual checks for potions/tipped arrows
|
||||
if (boxMapping.getJavaItem() instanceof PotionItem || boxMapping.getJavaItem() instanceof ArrowItem) {
|
||||
PotionContents potionContents = boxComponents.get(DataComponentType.POTION_CONTENTS);
|
||||
if (potionContents != null) {
|
||||
Potion potion = Potion.getByJavaId(potionContents.getPotionId());
|
||||
if (potion != null) {
|
||||
bedrockData = potion.getBedrockId();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NbtMapBuilder boxItemNbt = BedrockItemBuilder.createItemNbt(bedrockIdentifier, item.getAmount(), bedrockData); // Final item tag to add to the list
|
||||
boxItemNbt.putByte("Slot", (byte) slot);
|
||||
boxItemNbt.putByte("WasPickedUp", (byte) 0); // ??? TODO might not be needed
|
||||
|
||||
// Only the display name is what we have interest in, so just translate that if relevant
|
||||
DataComponents boxComponents = item.getDataComponents();
|
||||
if (boxComponents != null) {
|
||||
String customName = ItemTranslator.getCustomName(session, boxComponents, boxMapping, '7');
|
||||
if (customName != null) {
|
||||
|
@ -63,7 +63,8 @@ public enum BedrockMapIcon {
|
||||
ICON_SNOWY_VILLAGE(MapIconType.SNOWY_VILLAGE, 20),
|
||||
ICON_TAIGA_VILLAGE(MapIconType.TAIGA_VILLAGE, 21),
|
||||
ICON_JUNGLE_TEMPLE(MapIconType.JUNGLE_TEMPLE, 22),
|
||||
ICON_SWAMP_HUT(MapIconType.SWAMP_HUT, 23);
|
||||
ICON_SWAMP_HUT(MapIconType.SWAMP_HUT, 23),
|
||||
ICON_TRIAL_CHAMBERS(MapIconType.TRIAL_CHAMBERS, 24);
|
||||
|
||||
private static final BedrockMapIcon[] VALUES = values();
|
||||
|
||||
|
@ -26,7 +26,7 @@
|
||||
package org.geysermc.geyser.level;
|
||||
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.RegistryEntry;
|
||||
import org.geysermc.geyser.session.cache.registry.RegistryEntryContext;
|
||||
|
||||
/**
|
||||
* Represents the information we store from the current Java dimension
|
||||
@ -35,8 +35,8 @@ import org.geysermc.mcprotocollib.protocol.data.game.RegistryEntry;
|
||||
*/
|
||||
public record JavaDimension(int minY, int maxY, boolean piglinSafe, double worldCoordinateScale) {
|
||||
|
||||
public static JavaDimension read(RegistryEntry entry) {
|
||||
NbtMap dimension = entry.getData();
|
||||
public static JavaDimension read(RegistryEntryContext entry) {
|
||||
NbtMap dimension = entry.data();
|
||||
int minY = dimension.getInt("min_y");
|
||||
int maxY = dimension.getInt("height");
|
||||
// Logical height can be ignored probably - seems to be for artificial limits like the Nether.
|
||||
|
@ -27,13 +27,13 @@ package org.geysermc.geyser.level;
|
||||
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.session.cache.registry.RegistryEntryContext;
|
||||
import org.geysermc.geyser.translator.text.MessageTranslator;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.RegistryEntry;
|
||||
|
||||
public record JukeboxSong(String soundEvent, String description) {
|
||||
|
||||
public static JukeboxSong read(RegistryEntry entry) {
|
||||
NbtMap data = entry.getData();
|
||||
public static JukeboxSong read(RegistryEntryContext context) {
|
||||
NbtMap data = context.data();
|
||||
Object soundEventObject = data.get("sound_event");
|
||||
String soundEvent;
|
||||
if (soundEventObject instanceof NbtMap map) {
|
||||
@ -42,7 +42,7 @@ public record JukeboxSong(String soundEvent, String description) {
|
||||
soundEvent = string;
|
||||
} else {
|
||||
soundEvent = "";
|
||||
GeyserImpl.getInstance().getLogger().debug("Sound event for " + entry.getId() + " 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);
|
||||
return new JukeboxSong(soundEvent, description);
|
||||
|
@ -37,9 +37,22 @@ public class DoorBlock extends Block {
|
||||
|
||||
@Override
|
||||
public void updateBlock(GeyserSession session, BlockState state, Vector3i position) {
|
||||
// Needed to check whether we must force the client to update the door state.
|
||||
String doubleBlockHalf = state.getValue(Properties.DOUBLE_BLOCK_HALF);
|
||||
|
||||
if (!session.getGeyser().getWorldManager().hasOwnChunkCache() && doubleBlockHalf.equals("lower")) {
|
||||
BlockState oldBlockState = session.getGeyser().getWorldManager().blockAt(session, position);
|
||||
// If these are the same, it means that we already updated the lower door block (manually in the workaround below),
|
||||
// and we do not need to update the block in the cache/on the client side using the super.updateBlock() method again.
|
||||
// Otherwise, we send the door updates twice which will cause visual glitches on the client side
|
||||
if (oldBlockState == state) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
super.updateBlock(session, state, position);
|
||||
|
||||
if (state.getValue(Properties.DOUBLE_BLOCK_HALF).equals("upper")) {
|
||||
if (doubleBlockHalf.equals("upper")) {
|
||||
// Update the lower door block as Bedrock client doesn't like door to be closed from the top
|
||||
// See https://github.com/GeyserMC/Geyser/issues/4358
|
||||
Vector3i belowDoorPosition = position.sub(0, 1, 0);
|
||||
|
@ -43,13 +43,17 @@ import java.util.StringJoiner;
|
||||
*/
|
||||
public final class GameProtocol {
|
||||
|
||||
// Surprise protocol bump WOW
|
||||
private static final BedrockCodec BEDROCK_V686 = Bedrock_v685.CODEC.toBuilder()
|
||||
.protocolVersion(686)
|
||||
.minecraftVersion("1.21.2")
|
||||
.build();
|
||||
|
||||
/**
|
||||
* Default Bedrock codec that should act as a fallback. Should represent the latest available
|
||||
* release of the game that Geyser supports.
|
||||
*/
|
||||
public static final BedrockCodec DEFAULT_BEDROCK_CODEC = CodecProcessor.processCodec(Bedrock_v685.CODEC.toBuilder()
|
||||
.minecraftVersion("1.21.1")
|
||||
.build());
|
||||
public static final BedrockCodec DEFAULT_BEDROCK_CODEC = CodecProcessor.processCodec(BEDROCK_V686);
|
||||
|
||||
/**
|
||||
* A list of all supported Bedrock versions that can join Geyser
|
||||
@ -66,9 +70,10 @@ public final class GameProtocol {
|
||||
SUPPORTED_BEDROCK_CODECS.add(CodecProcessor.processCodec(Bedrock_v671.CODEC.toBuilder()
|
||||
.minecraftVersion("1.20.80/1.20.81")
|
||||
.build()));
|
||||
SUPPORTED_BEDROCK_CODECS.add(CodecProcessor.processCodec(DEFAULT_BEDROCK_CODEC.toBuilder()
|
||||
.minecraftVersion("1.21.0/1.20.1")
|
||||
SUPPORTED_BEDROCK_CODECS.add(CodecProcessor.processCodec(Bedrock_v685.CODEC.toBuilder()
|
||||
.minecraftVersion("1.21.0/1.21.1")
|
||||
.build()));
|
||||
SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -49,8 +49,6 @@ import java.util.function.Consumer;
|
||||
|
||||
public class CreativeItemRegistryPopulator {
|
||||
private static final List<BiPredicate<String, Integer>> JAVA_ONLY_ITEM_FILTER = List.of(
|
||||
// Just shows an empty texture; either way it doesn't exist in the creative menu on Java
|
||||
(identifier, data) -> identifier.equals("minecraft:debug_stick"),
|
||||
// Bedrock-only as its own item
|
||||
(identifier, data) -> identifier.equals("minecraft:empty_map") && data == 2,
|
||||
// Bedrock-only banner patterns
|
||||
@ -103,16 +101,8 @@ public class CreativeItemRegistryPopulator {
|
||||
}
|
||||
|
||||
GeyserBedrockBlock blockDefinition = null;
|
||||
JsonNode blockRuntimeIdNode = itemNode.get("blockRuntimeId");
|
||||
JsonNode blockStateNode;
|
||||
if (blockRuntimeIdNode != null) {
|
||||
bedrockBlockRuntimeId = blockRuntimeIdNode.asInt();
|
||||
if (bedrockBlockRuntimeId == 0 && !identifier.equals("minecraft:blue_candle")) { // FIXME
|
||||
bedrockBlockRuntimeId = -1;
|
||||
}
|
||||
|
||||
blockDefinition = bedrockBlockRuntimeId == -1 ? null : blockMappings.getDefinition(bedrockBlockRuntimeId);
|
||||
} else if ((blockStateNode = itemNode.get("block_state_b64")) != null) {
|
||||
if ((blockStateNode = itemNode.get("block_state_b64")) != null) {
|
||||
byte[] bytes = Base64.getDecoder().decode(blockStateNode.asText());
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
|
||||
try {
|
||||
|
@ -58,6 +58,8 @@ import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData;
|
||||
import org.geysermc.geyser.inventory.item.StoredItemMappings;
|
||||
import org.geysermc.geyser.item.GeyserCustomMappingData;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.item.components.Rarity;
|
||||
import org.geysermc.geyser.item.type.BlockItem;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.registry.Registries;
|
||||
@ -252,10 +254,7 @@ public class ItemRegistryPopulator {
|
||||
} else {
|
||||
// Try to get an example block runtime ID from the creative contents packet, for Bedrock identifier obtaining
|
||||
int aValidBedrockBlockId = blacklistedIdentifiers.getOrDefault(bedrockIdentifier, customBlockItemOverride != null ? customBlockItemOverride.getRuntimeId() : -1);
|
||||
if (aValidBedrockBlockId == -1 && customBlockItemOverride == null) {
|
||||
// Fallback
|
||||
bedrockBlock = blockMappings.getBedrockBlock(firstBlockRuntimeId);
|
||||
} else {
|
||||
if (aValidBedrockBlockId != -1 || customBlockItemOverride != null) {
|
||||
// As of 1.16.220, every item requires a block runtime ID attached to it.
|
||||
// This is mostly for identifying different blocks with the same item ID - wool, slabs, some walls.
|
||||
// However, in order for some visuals and crafting to work, we need to send the first matching block state
|
||||
@ -266,7 +265,7 @@ public class ItemRegistryPopulator {
|
||||
boolean firstPass = true;
|
||||
// Block states are all grouped together. In the mappings, we store the first block runtime ID in order,
|
||||
// and the last, if relevant. We then iterate over all those values and get their Bedrock equivalents
|
||||
Integer lastBlockRuntimeId = entry.getValue().getLastBlockRuntimeId() == null ? firstBlockRuntimeId : entry.getValue().getLastBlockRuntimeId();
|
||||
int lastBlockRuntimeId = entry.getValue().getLastBlockRuntimeId() == null ? firstBlockRuntimeId : entry.getValue().getLastBlockRuntimeId();
|
||||
for (int i = firstBlockRuntimeId; i <= lastBlockRuntimeId; i++) {
|
||||
GeyserBedrockBlock bedrockBlockRuntimeId = blockMappings.getVanillaBedrockBlock(i);
|
||||
NbtMap blockTag = bedrockBlockRuntimeId.getState();
|
||||
@ -402,9 +401,10 @@ public class ItemRegistryPopulator {
|
||||
}
|
||||
}
|
||||
|
||||
if (javaOnlyItems.contains(javaItem)) {
|
||||
if (javaOnlyItems.contains(javaItem) || javaItem.rarity() != Rarity.COMMON) {
|
||||
// These items don't exist on Bedrock, so set up a variable that indicates they should have custom names
|
||||
mappingBuilder = mappingBuilder.translationString((bedrockBlock != null ? "block." : "item.") + entry.getKey().replace(":", "."));
|
||||
// Or, ensure that we are translating these at all times to account for rarity colouring
|
||||
mappingBuilder = mappingBuilder.translationString((javaItem instanceof BlockItem ? "block." : "item.") + entry.getKey().replace(":", "."));
|
||||
GeyserImpl.getInstance().getLogger().debug("Adding " + entry.getKey() + " as an item that needs to be translated.");
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,7 @@ import java.util.Set;
|
||||
|
||||
@Builder
|
||||
@Value
|
||||
public class BlockMappings implements DefinitionRegistry<GeyserBedrockBlock> {
|
||||
public class BlockMappings implements DefinitionRegistry<BlockDefinition> {
|
||||
GeyserBedrockBlock bedrockAir;
|
||||
BlockDefinition bedrockWater;
|
||||
BlockDefinition bedrockMovingBlock;
|
||||
@ -134,7 +134,7 @@ public class BlockMappings implements DefinitionRegistry<GeyserBedrockBlock> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRegistered(GeyserBedrockBlock bedrockBlock) {
|
||||
public boolean isRegistered(BlockDefinition bedrockBlock) {
|
||||
return getDefinition(bedrockBlock.getRuntimeId()) == bedrockBlock;
|
||||
}
|
||||
}
|
@ -57,7 +57,6 @@ import org.cloudburstmc.protocol.bedrock.data.command.SoftEnumUpdateType;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.*;
|
||||
import org.cloudburstmc.protocol.common.DefinitionRegistry;
|
||||
import org.cloudburstmc.protocol.common.util.OptionalBoolean;
|
||||
import org.geysermc.api.util.BedrockPlatform;
|
||||
import org.geysermc.api.util.InputMode;
|
||||
@ -1490,7 +1489,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
||||
|
||||
private void startGame() {
|
||||
this.upstream.getCodecHelper().setItemDefinitions(this.itemMappings);
|
||||
this.upstream.getCodecHelper().setBlockDefinitions((DefinitionRegistry) this.blockMappings); //FIXME
|
||||
this.upstream.getCodecHelper().setBlockDefinitions(this.blockMappings);
|
||||
this.upstream.getCodecHelper().setCameraPresetDefinitions(CameraDefinitions.CAMERA_DEFINITIONS);
|
||||
|
||||
StartGamePacket startGamePacket = new StartGamePacket();
|
||||
|
@ -27,6 +27,8 @@ package org.geysermc.geyser.session.cache;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.experimental.Accessors;
|
||||
@ -45,6 +47,7 @@ import org.geysermc.geyser.level.JukeboxSong;
|
||||
import org.geysermc.geyser.level.PaintingType;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.registry.JavaRegistry;
|
||||
import org.geysermc.geyser.session.cache.registry.RegistryEntryContext;
|
||||
import org.geysermc.geyser.session.cache.registry.SimpleJavaRegistry;
|
||||
import org.geysermc.geyser.text.TextDecoration;
|
||||
import org.geysermc.geyser.translator.level.BiomeTranslator;
|
||||
@ -59,7 +62,6 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.ToIntFunction;
|
||||
|
||||
@ -76,16 +78,16 @@ public final class RegistryCache {
|
||||
private static final Map<Key, BiConsumer<RegistryCache, List<RegistryEntry>>> REGISTRIES = new HashMap<>();
|
||||
|
||||
static {
|
||||
register("chat_type", cache -> cache.chatTypes, ($, entry) -> TextDecoration.readChatType(entry));
|
||||
register("dimension_type", cache -> cache.dimensions, ($, entry) -> JavaDimension.read(entry));
|
||||
register("enchantment", cache -> cache.enchantments, ($, entry) -> Enchantment.read(entry));
|
||||
register("jukebox_song", cache -> cache.jukeboxSongs, ($, entry) -> JukeboxSong.read(entry));
|
||||
register("painting_variant", cache -> cache.paintings, ($, entry) -> PaintingType.getByName(entry.getId()));
|
||||
register("chat_type", cache -> cache.chatTypes, TextDecoration::readChatType);
|
||||
register("dimension_type", cache -> cache.dimensions, JavaDimension::read);
|
||||
register("enchantment", cache -> cache.enchantments, Enchantment::read);
|
||||
register("jukebox_song", cache -> cache.jukeboxSongs, JukeboxSong::read);
|
||||
register("painting_variant", cache -> cache.paintings, context -> PaintingType.getByName(context.id()));
|
||||
register("trim_material", cache -> cache.trimMaterials, TrimRecipe::readTrimMaterial);
|
||||
register("trim_pattern", cache -> cache.trimPatterns, TrimRecipe::readTrimPattern);
|
||||
register("worldgen/biome", (cache, array) -> cache.biomeTranslations = array, BiomeTranslator::loadServerBiome);
|
||||
register("banner_pattern", cache -> cache.bannerPatterns, ($, entry) -> BannerPattern.getByJavaIdentifier(entry.getId()));
|
||||
register("wolf_variant", cache -> cache.wolfVariants, ($, entry) -> WolfEntity.BuiltInWolfVariant.getByJavaIdentifier(entry.getId().asString()));
|
||||
register("banner_pattern", cache -> cache.bannerPatterns, context -> BannerPattern.getByJavaIdentifier(context.id()));
|
||||
register("wolf_variant", cache -> cache.wolfVariants, context -> WolfEntity.BuiltInWolfVariant.getByJavaIdentifier(context.id().asString()));
|
||||
|
||||
// Load from MCProtocolLib's classloader
|
||||
NbtMap tag = MinecraftProtocol.loadNetworkCodec();
|
||||
@ -149,25 +151,35 @@ public final class RegistryCache {
|
||||
* @param reader converts the RegistryEntry NBT into a class file
|
||||
* @param <T> the class that represents these entries.
|
||||
*/
|
||||
private static <T> void register(String registry, Function<RegistryCache, JavaRegistry<T>> localCacheFunction, BiFunction<GeyserSession, RegistryEntry, T> reader) {
|
||||
Key key = MinecraftKey.key(registry);
|
||||
REGISTRIES.put(key, (registryCache, entries) -> {
|
||||
private static <T> void register(String registry, Function<RegistryCache, JavaRegistry<T>> localCacheFunction, Function<RegistryEntryContext, T> reader) {
|
||||
Key registryKey = MinecraftKey.key(registry);
|
||||
REGISTRIES.put(registryKey, (registryCache, entries) -> {
|
||||
Map<Key, NbtMap> localRegistry = null;
|
||||
JavaRegistry<T> localCache = localCacheFunction.apply(registryCache);
|
||||
// Clear each local cache every time a new registry entry is given to us
|
||||
// (e.g. proxy server switches)
|
||||
|
||||
// Store each of the entries resource location IDs and their respective network ID,
|
||||
// used for the key mapper that's currently only used by the Enchantment class
|
||||
Object2IntMap<Key> entryIdMap = new Object2IntOpenHashMap<>();
|
||||
for (int i = 0; i < entries.size(); i++) {
|
||||
entryIdMap.put(entries.get(i).getId(), i);
|
||||
}
|
||||
|
||||
List<T> builder = new ArrayList<>(entries.size());
|
||||
for (int i = 0; i < entries.size(); i++) {
|
||||
RegistryEntry entry = entries.get(i);
|
||||
// If the data is null, that's the server telling us we need to use our default values.
|
||||
if (entry.getData() == null) {
|
||||
if (localRegistry == null) { // Lazy initialize
|
||||
localRegistry = DEFAULTS.get(key);
|
||||
localRegistry = DEFAULTS.get(registryKey);
|
||||
}
|
||||
entry = new RegistryEntry(entry.getId(), localRegistry.get(entry.getId()));
|
||||
}
|
||||
|
||||
RegistryEntryContext context = new RegistryEntryContext(entry, entryIdMap, registryCache.session);
|
||||
// This is what Geyser wants to keep as a value for this registry.
|
||||
T cacheEntry = reader.apply(registryCache.session, entry);
|
||||
T cacheEntry = reader.apply(context);
|
||||
builder.add(i, cacheEntry);
|
||||
}
|
||||
localCache.reset(builder);
|
||||
|
@ -130,8 +130,12 @@ public final class TagCache {
|
||||
return contains(values, item.javaId());
|
||||
}
|
||||
|
||||
public int[] get(EnchantmentTag tag) {
|
||||
return this.enchantments[tag.ordinal()];
|
||||
public int[] get(ItemTag itemTag) {
|
||||
return this.items[itemTag.ordinal()];
|
||||
}
|
||||
|
||||
public int[] get(EnchantmentTag enchantmentTag) {
|
||||
return this.enchantments[enchantmentTag.ordinal()];
|
||||
}
|
||||
|
||||
private static boolean contains(int[] array, int i) {
|
||||
|
56
core/src/main/java/org/geysermc/geyser/session/cache/registry/RegistryEntryContext.java
vendored
Normale Datei
56
core/src/main/java/org/geysermc/geyser/session/cache/registry/RegistryEntryContext.java
vendored
Normale Datei
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.session.cache.registry;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import java.util.Map;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.RegistryEntry;
|
||||
|
||||
/**
|
||||
* Used to store context around a single registry entry when reading said entry's NBT.
|
||||
*
|
||||
* @param entry the registry entry being read.
|
||||
* @param keyIdMap a map for each of the resource location's in the registry and their respective network IDs.
|
||||
* @param session the Geyser session.
|
||||
*/
|
||||
public record RegistryEntryContext(RegistryEntry entry, Object2IntMap<Key> keyIdMap, GeyserSession session) {
|
||||
|
||||
public int getNetworkId(Key registryKey) {
|
||||
return keyIdMap.getOrDefault(registryKey, 0);
|
||||
}
|
||||
|
||||
public Key id() {
|
||||
return entry.getId();
|
||||
}
|
||||
|
||||
// Not annotated as nullable because data should never be null here
|
||||
public NbtMap data() {
|
||||
return entry.getData();
|
||||
}
|
||||
}
|
@ -29,7 +29,7 @@ import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.Style;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtType;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.RegistryEntry;
|
||||
import org.geysermc.geyser.session.cache.registry.RegistryEntryContext;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.chat.ChatType;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.chat.ChatTypeDecoration;
|
||||
|
||||
@ -43,11 +43,11 @@ public record TextDecoration(String translationKey, List<Parameter> parameters,
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public static ChatType readChatType(RegistryEntry entry) {
|
||||
public static ChatType readChatType(RegistryEntryContext context) {
|
||||
// Note: The ID is NOT ALWAYS THE SAME! ViaVersion as of 1.19 adds two registry entries that do NOT match vanilla.
|
||||
// (This note has been passed around through several classes and iterations. It stays as a warning
|
||||
// to anyone that dares to try and hardcode registry IDs.)
|
||||
NbtMap tag = entry.getData();
|
||||
NbtMap tag = context.data();
|
||||
NbtMap chat = tag.getCompound("chat", null);
|
||||
if (chat != null) {
|
||||
String translationKey = chat.getString("translation_key");
|
||||
|
@ -145,8 +145,15 @@ public final class BedrockItemBuilder {
|
||||
* Creates item NBT to nest within NBT with name, count, and damage set.
|
||||
*/
|
||||
public static NbtMapBuilder createItemNbt(ItemMapping mapping, int count, int damage) {
|
||||
return createItemNbt(mapping.getBedrockIdentifier(), count, damage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates item NBT to nest within NBT with name, count, and damage set.
|
||||
*/
|
||||
public static NbtMapBuilder createItemNbt(String bedrockIdentifier, int count, int damage) {
|
||||
NbtMapBuilder builder = NbtMap.builder();
|
||||
builder.putString("Name", mapping.getBedrockIdentifier());
|
||||
builder.putString("Name", bedrockIdentifier);
|
||||
builder.putByte("Count", (byte) count);
|
||||
builder.putShort("Damage", (short) damage);
|
||||
return builder;
|
||||
|
@ -33,7 +33,9 @@ import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.nbt.NbtList;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
|
||||
import org.cloudburstmc.protocol.bedrock.data.definitions.ItemDefinition;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
|
||||
@ -41,6 +43,7 @@ import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.block.custom.CustomBlockData;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.item.components.Rarity;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
@ -52,6 +55,7 @@ import org.geysermc.geyser.text.ChatColor;
|
||||
import org.geysermc.geyser.text.MinecraftLocale;
|
||||
import org.geysermc.geyser.translator.text.MessageTranslator;
|
||||
import org.geysermc.geyser.util.InventoryUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.attribute.AttributeType;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.attribute.ModifierOperation;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.*;
|
||||
@ -144,7 +148,20 @@ public final class ItemTranslator {
|
||||
if (components.get(DataComponentType.HIDE_TOOLTIP) != null) hideTooltips = true;
|
||||
}
|
||||
|
||||
String customName = getCustomName(session, components, bedrockItem);
|
||||
Rarity rarity = javaItem.rarity();
|
||||
boolean enchantmentGlint = javaItem.glint();
|
||||
if (components != null) {
|
||||
Integer rarityIndex = components.get(DataComponentType.RARITY);
|
||||
if (rarityIndex != null) {
|
||||
rarity = Rarity.fromId(rarityIndex);
|
||||
}
|
||||
Boolean enchantmentGlintOverride = components.get(DataComponentType.ENCHANTMENT_GLINT_OVERRIDE);
|
||||
if (enchantmentGlintOverride != null) {
|
||||
enchantmentGlint = enchantmentGlintOverride;
|
||||
}
|
||||
}
|
||||
|
||||
String customName = getCustomName(session, components, bedrockItem, rarity.getColor());
|
||||
if (customName != null) {
|
||||
nbtBuilder.setCustomName(customName);
|
||||
}
|
||||
@ -161,6 +178,12 @@ public final class ItemTranslator {
|
||||
addAdvancedTooltips(components, nbtBuilder, javaItem, session.locale());
|
||||
}
|
||||
|
||||
// Add enchantment override. We can't remove it - enchantments would stop showing - but we can add it.
|
||||
if (enchantmentGlint) {
|
||||
NbtMapBuilder nbtMapBuilder = nbtBuilder.getOrCreateNbt();
|
||||
nbtMapBuilder.putIfAbsent("ench", NbtList.EMPTY);
|
||||
}
|
||||
|
||||
ItemData.Builder builder = javaItem.translateToBedrock(count, components, bedrockItem, session.getItemMappings());
|
||||
// Finalize the Bedrock NBT
|
||||
builder.tag(nbtBuilder.build());
|
||||
@ -209,7 +232,7 @@ public final class ItemTranslator {
|
||||
Map<ItemAttributeModifiers.EquipmentSlotGroup, List<String>> slotsToModifiers = new HashMap<>();
|
||||
for (ItemAttributeModifiers.Entry entry : modifiers.getModifiers()) {
|
||||
// convert the modifier tag to a lore entry
|
||||
String loreEntry = attributeToLore(entry.getModifier(), language);
|
||||
String loreEntry = attributeToLore(entry.getAttribute(), entry.getModifier(), language);
|
||||
if (loreEntry == null) {
|
||||
continue; // invalid or failed
|
||||
}
|
||||
@ -254,13 +277,13 @@ public final class ItemTranslator {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String attributeToLore(ItemAttributeModifiers.AttributeModifier modifier, String language) {
|
||||
private static String attributeToLore(int attribute, ItemAttributeModifiers.AttributeModifier modifier, String language) {
|
||||
double amount = modifier.getAmount();
|
||||
if (amount == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String name = modifier.getId().asMinimalString();
|
||||
String name = AttributeType.Builtin.from(attribute).getIdentifier().asMinimalString();
|
||||
// the namespace does not need to be present, but if it is, the java client ignores it as of pre-1.20.5
|
||||
|
||||
ModifierOperation operation = modifier.getOperation();
|
||||
@ -400,16 +423,6 @@ public final class ItemTranslator {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates the display name of the item
|
||||
* @param session the Bedrock client's session
|
||||
* @param components the components to translate
|
||||
* @param mapping the item entry, in case it requires translation
|
||||
*/
|
||||
public static String getCustomName(GeyserSession session, DataComponents components, ItemMapping mapping) {
|
||||
return getCustomName(session, components, mapping, 'f');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param translationColor if this item is not available on Java, the color that the new name should be.
|
||||
* Normally, this should just be white, but for shulker boxes this should be gray.
|
||||
@ -418,13 +431,15 @@ public final class ItemTranslator {
|
||||
if (components != null) {
|
||||
// ItemStack#getHoverName as of 1.20.5
|
||||
Component customName = components.get(DataComponentType.CUSTOM_NAME);
|
||||
if (customName == null) {
|
||||
customName = components.get(DataComponentType.ITEM_NAME);
|
||||
}
|
||||
if (customName != null) {
|
||||
// Get the translated name and prefix it with a reset char
|
||||
return MessageTranslator.convertMessage(customName, session.locale());
|
||||
}
|
||||
customName = components.get(DataComponentType.ITEM_NAME);
|
||||
if (customName != null) {
|
||||
// Get the translated name and prefix it with a reset char to prevent italics - matches Java Edition
|
||||
// behavior as of 1.21
|
||||
return ChatColor.RESET + ChatColor.ESCAPE + translationColor + MessageTranslator.convertMessage(customName, session.locale());
|
||||
}
|
||||
}
|
||||
|
||||
if (mapping.hasTranslation()) {
|
||||
|
@ -107,7 +107,7 @@ public class SpawnerBlockEntityTranslator extends BlockEntityTranslator {
|
||||
bedrockNbt.put("isMovable", (byte) 1);
|
||||
}
|
||||
|
||||
static void translateSpawnData(@NonNull NbtMapBuilder builder, @Nullable NbtMap spawnData) {
|
||||
private static void translateSpawnData(@NonNull NbtMapBuilder builder, @Nullable NbtMap spawnData) {
|
||||
if (spawnData == null) {
|
||||
return;
|
||||
}
|
||||
|
@ -27,22 +27,31 @@ package org.geysermc.geyser.translator.level.block.entity;
|
||||
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.registry.Registries;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType;
|
||||
|
||||
@BlockEntity(type = BlockEntityType.TRIAL_SPAWNER)
|
||||
public class TrialSpawnerBlockEntityTranslator extends BlockEntityTranslator {
|
||||
// Note that it would appear block entity updates don't include the NBT, but we do need it on chunk load.
|
||||
@Override
|
||||
public void translateTag(GeyserSession session, NbtMapBuilder bedrockNbt, NbtMap javaNbt, BlockState blockState) {
|
||||
if (javaNbt == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// trial spawners have "spawn_data" instead of "SpawnData"
|
||||
SpawnerBlockEntityTranslator.translateSpawnData(bedrockNbt, javaNbt.getCompound("spawn_data", null));
|
||||
|
||||
// Because trial spawners don't exist on bedrock yet
|
||||
bedrockNbt.put("id", "MobSpawner");
|
||||
NbtMap entityData = javaNbt.getCompound("spawn_data").getCompound("entity");
|
||||
if (entityData.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
NbtMapBuilder spawnData = NbtMap.builder();
|
||||
EntityDefinition<?> definition = Registries.JAVA_ENTITY_IDENTIFIERS.get(entityData.getString("id"));
|
||||
if (definition != null) {
|
||||
spawnData.putString("TypeId", definition.identifier());
|
||||
}
|
||||
spawnData.putInt("Weight", entityData.getInt("Size", 1)); // ??? presumably since these are the only other two extra attributes
|
||||
bedrockNbt.putCompound("spawn_data", spawnData.build());
|
||||
}
|
||||
}
|
||||
|
@ -25,20 +25,19 @@
|
||||
|
||||
package org.geysermc.geyser.translator.protocol.bedrock.entity.player;
|
||||
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosRotPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerRotPacket;
|
||||
import org.geysermc.mcprotocollib.network.packet.Packet;
|
||||
import org.cloudburstmc.math.vector.Vector3d;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket;
|
||||
import org.geysermc.geyser.entity.EntityDefinitions;
|
||||
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
|
||||
import org.geysermc.geyser.level.BedrockDimension;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.text.ChatColor;
|
||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||
import org.geysermc.geyser.translator.protocol.Translator;
|
||||
import org.geysermc.mcprotocollib.network.packet.Packet;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosRotPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerRotPacket;
|
||||
|
||||
@Translator(packet = MovePlayerPacket.class)
|
||||
public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPacket> {
|
||||
@ -93,29 +92,42 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
|
||||
Vector3d position = session.getCollisionManager().adjustBedrockPosition(packet.getPosition(), packet.isOnGround(), packet.getMode() == MovePlayerPacket.Mode.TELEPORT);
|
||||
if (position != null) { // A null return value cancels the packet
|
||||
boolean onGround = packet.isOnGround();
|
||||
boolean isBelowVoid = entity.isVoidPositionDesynched();
|
||||
|
||||
boolean teleportThroughVoidFloor;
|
||||
boolean teleportThroughVoidFloor, mustResyncPosition;
|
||||
// Compare positions here for void floor fix below before the player's position variable is set to the packet position
|
||||
if (entity.getPosition().getY() >= packet.getPosition().getY()) {
|
||||
if (entity.getPosition().getY() >= packet.getPosition().getY() && !isBelowVoid) {
|
||||
int floorY = position.getFloorY();
|
||||
// The void floor is offset about 40 blocks below the bottom of the world
|
||||
BedrockDimension bedrockDimension = session.getChunkCache().getBedrockDimension();
|
||||
int voidFloorLocation = bedrockDimension.minY() - 40;
|
||||
teleportThroughVoidFloor = floorY <= (voidFloorLocation + 2) && floorY >= voidFloorLocation;
|
||||
if (teleportThroughVoidFloor) {
|
||||
// https://github.com/GeyserMC/Geyser/issues/3521 - no void floor in Java so we cannot be on the ground.
|
||||
onGround = false;
|
||||
}
|
||||
int voidFloorLocation = entity.voidFloorPosition();
|
||||
teleportThroughVoidFloor = floorY <= (voidFloorLocation + 1) && floorY >= voidFloorLocation;
|
||||
} else {
|
||||
teleportThroughVoidFloor = false;
|
||||
}
|
||||
|
||||
if (teleportThroughVoidFloor || isBelowVoid) {
|
||||
// https://github.com/GeyserMC/Geyser/issues/3521 - no void floor in Java so we cannot be on the ground.
|
||||
onGround = false;
|
||||
}
|
||||
|
||||
if (isBelowVoid) {
|
||||
int floorY = position.getFloorY();
|
||||
int voidFloorLocation = entity.voidFloorPosition();
|
||||
mustResyncPosition = floorY < voidFloorLocation && floorY >= voidFloorLocation - 1;
|
||||
} else {
|
||||
mustResyncPosition = false;
|
||||
}
|
||||
|
||||
double yPosition = position.getY();
|
||||
if (entity.isVoidPositionDesynched()) { // not using the cached variable on purpose
|
||||
yPosition += 4; // We are de-synched since we had to teleport below the void floor.
|
||||
}
|
||||
|
||||
Packet movePacket;
|
||||
if (rotationChanged) {
|
||||
// Send rotation updates as well
|
||||
movePacket = new ServerboundMovePlayerPosRotPacket(
|
||||
onGround,
|
||||
position.getX(), position.getY(), position.getZ(),
|
||||
position.getX(), yPosition, position.getZ(),
|
||||
yaw, pitch
|
||||
);
|
||||
entity.setYaw(yaw);
|
||||
@ -123,7 +135,7 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
|
||||
entity.setHeadYaw(headYaw);
|
||||
} else {
|
||||
// Rotation did not change; don't send an update with rotation
|
||||
movePacket = new ServerboundMovePlayerPosPacket(onGround, position.getX(), position.getY(), position.getZ());
|
||||
movePacket = new ServerboundMovePlayerPosPacket(onGround, position.getX(), yPosition, position.getZ());
|
||||
}
|
||||
|
||||
entity.setPositionManual(packet.getPosition());
|
||||
@ -133,16 +145,9 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
|
||||
session.sendDownstreamGamePacket(movePacket);
|
||||
|
||||
if (teleportThroughVoidFloor) {
|
||||
// Work around there being a floor at the bottom of the world and teleport the player below it
|
||||
// Moving from below to above the void floor works fine
|
||||
entity.setPosition(entity.getPosition().sub(0, 4f, 0));
|
||||
MovePlayerPacket movePlayerPacket = new MovePlayerPacket();
|
||||
movePlayerPacket.setRuntimeEntityId(entity.getGeyserId());
|
||||
movePlayerPacket.setPosition(entity.getPosition());
|
||||
movePlayerPacket.setRotation(entity.getBedrockRotation());
|
||||
movePlayerPacket.setMode(MovePlayerPacket.Mode.TELEPORT);
|
||||
movePlayerPacket.setTeleportationCause(MovePlayerPacket.TeleportationCause.BEHAVIOR);
|
||||
session.sendUpstreamPacket(movePlayerPacket);
|
||||
entity.teleportVoidFloorFix(false);
|
||||
} else if (mustResyncPosition) {
|
||||
entity.teleportVoidFloorFix(true);
|
||||
}
|
||||
|
||||
session.getSkullCache().updateVisibleSkulls();
|
||||
|
@ -100,8 +100,8 @@ public class JavaBlockUpdateTranslator extends PacketTranslator<ClientboundBlock
|
||||
|| lastInteractPos.getZ() != packet.getEntry().getPosition().getZ())) {
|
||||
return;
|
||||
}
|
||||
String identifier = BlockState.of(packet.getEntry().getBlock()).toString(); // This will be yeeted soon. Thanks Chris.
|
||||
BlockState state = BlockState.of(packet.getEntry().getBlock());
|
||||
session.setInteracting(false);
|
||||
BlockSoundInteractionTranslator.handleBlockInteraction(session, lastInteractPos.toFloat(), identifier);
|
||||
BlockSoundInteractionTranslator.handleBlockInteraction(session, lastInteractPos.toFloat(), state);
|
||||
}
|
||||
}
|
||||
|
@ -28,9 +28,16 @@ package org.geysermc.geyser.translator.protocol.java.level;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.protocol.bedrock.data.LevelEvent;
|
||||
import org.cloudburstmc.protocol.bedrock.data.ParticleType;
|
||||
import org.cloudburstmc.protocol.bedrock.data.SoundEvent;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.*;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.LevelEventGenericPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.LevelEventPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEventPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.PlaySoundPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.SpawnParticleEffectPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.StopSoundPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.TextPacket;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.level.JukeboxSong;
|
||||
import org.geysermc.geyser.registry.Registries;
|
||||
@ -40,13 +47,27 @@ import org.geysermc.geyser.text.MinecraftLocale;
|
||||
import org.geysermc.geyser.translator.level.event.LevelEventTranslator;
|
||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||
import org.geysermc.geyser.translator.protocol.Translator;
|
||||
import org.geysermc.geyser.util.DimensionUtils;
|
||||
import org.geysermc.geyser.util.SoundUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.object.Direction;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.level.event.*;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.level.event.BonemealGrowEventData;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.level.event.BreakBlockEventData;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.level.event.BreakPotionEventData;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.level.event.ComposterEventData;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.level.event.DragonFireballEventData;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.level.event.LevelEventType;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.level.event.RecordEventData;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.level.event.SculkBlockChargeEventData;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.level.event.SmokeEventData;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.level.event.TrialSpawnerDetectEventData;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.level.event.UnknownLevelEventData;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.level.ClientboundLevelEventPacket;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
@Translator(packet = ClientboundLevelEventPacket.class)
|
||||
public class JavaLevelEventTranslator extends PacketTranslator<ClientboundLevelEventPacket> {
|
||||
@ -291,6 +312,20 @@ public class JavaLevelEventTranslator extends PacketTranslator<ClientboundLevelE
|
||||
soundEventPacket.setRelativeVolumeDisabled(false);
|
||||
session.sendUpstreamPacket(soundEventPacket);
|
||||
}
|
||||
case ANIMATION_SPAWN_COBWEB -> effectPacket.setType(org.cloudburstmc.protocol.bedrock.data.LevelEvent.ANIMATION_SPAWN_COBWEB);
|
||||
case ANIMATION_VAULT_ACTIVATE -> effectPacket.setType(org.cloudburstmc.protocol.bedrock.data.LevelEvent.ANIMATION_VAULT_ACTIVATE);
|
||||
case ANIMATION_VAULT_DEACTIVATE -> effectPacket.setType(org.cloudburstmc.protocol.bedrock.data.LevelEvent.ANIMATION_VAULT_DEACTIVATE);
|
||||
case ANIMATION_VAULT_EJECT_ITEM -> effectPacket.setType(org.cloudburstmc.protocol.bedrock.data.LevelEvent.ANIMATION_VAULT_EJECT_ITEM);
|
||||
case ANIMATION_TRIAL_SPAWNER_EJECT_ITEM -> {
|
||||
Random random = ThreadLocalRandom.current();
|
||||
PlaySoundPacket playSoundPacket = new PlaySoundPacket();
|
||||
playSoundPacket.setSound("trial_spawner.eject_item");
|
||||
playSoundPacket.setPosition(pos);
|
||||
playSoundPacket.setVolume(1.0f);
|
||||
playSoundPacket.setPitch(0.8f + random.nextFloat() * 0.3f);
|
||||
session.sendUpstreamPacket(playSoundPacket);
|
||||
return;
|
||||
}
|
||||
case DRIPSTONE_DRIP -> effectPacket.setType(org.cloudburstmc.protocol.bedrock.data.LevelEvent.PARTICLE_DRIPSTONE_DRIP);
|
||||
case PARTICLES_ELECTRIC_SPARK -> effectPacket.setType(org.cloudburstmc.protocol.bedrock.data.LevelEvent.PARTICLE_ELECTRIC_SPARK); // Matches with a Bedrock server but doesn't seem to match up with Java
|
||||
case PARTICLES_AND_SOUND_WAX_ON -> effectPacket.setType(org.cloudburstmc.protocol.bedrock.data.LevelEvent.PARTICLE_WAX_ON);
|
||||
@ -303,22 +338,22 @@ public class JavaLevelEventTranslator extends PacketTranslator<ClientboundLevelE
|
||||
if (eventData.getCharge() > 0) {
|
||||
levelEventPacket.setType(org.cloudburstmc.protocol.bedrock.data.LevelEvent.SCULK_CHARGE);
|
||||
levelEventPacket.setTag(
|
||||
NbtMap.builder()
|
||||
.putInt("x", packet.getPosition().getX())
|
||||
.putInt("y", packet.getPosition().getY())
|
||||
.putInt("z", packet.getPosition().getZ())
|
||||
.putShort("charge", (short) eventData.getCharge())
|
||||
.putShort("facing", encodeFacing(eventData.getBlockFaces())) // TODO check if this is actually correct
|
||||
.build()
|
||||
NbtMap.builder()
|
||||
.putInt("x", packet.getPosition().getX())
|
||||
.putInt("y", packet.getPosition().getY())
|
||||
.putInt("z", packet.getPosition().getZ())
|
||||
.putShort("charge", (short) eventData.getCharge())
|
||||
.putShort("facing", encodeFacing(eventData.getBlockFaces())) // TODO check if this is actually correct
|
||||
.build()
|
||||
);
|
||||
} else {
|
||||
levelEventPacket.setType(org.cloudburstmc.protocol.bedrock.data.LevelEvent.SCULK_CHARGE_POP);
|
||||
levelEventPacket.setTag(
|
||||
NbtMap.builder()
|
||||
.putInt("x", packet.getPosition().getX())
|
||||
.putInt("y", packet.getPosition().getY())
|
||||
.putInt("z", packet.getPosition().getZ())
|
||||
.build()
|
||||
NbtMap.builder()
|
||||
.putInt("x", packet.getPosition().getX())
|
||||
.putInt("y", packet.getPosition().getY())
|
||||
.putInt("z", packet.getPosition().getZ())
|
||||
.build()
|
||||
);
|
||||
}
|
||||
session.sendUpstreamPacket(levelEventPacket);
|
||||
@ -328,11 +363,11 @@ public class JavaLevelEventTranslator extends PacketTranslator<ClientboundLevelE
|
||||
LevelEventGenericPacket levelEventPacket = new LevelEventGenericPacket();
|
||||
levelEventPacket.setType(org.cloudburstmc.protocol.bedrock.data.LevelEvent.PARTICLE_SCULK_SHRIEK);
|
||||
levelEventPacket.setTag(
|
||||
NbtMap.builder()
|
||||
.putInt("originX", packet.getPosition().getX())
|
||||
.putInt("originY", packet.getPosition().getY())
|
||||
.putInt("originZ", packet.getPosition().getZ())
|
||||
.build()
|
||||
NbtMap.builder()
|
||||
.putInt("originX", packet.getPosition().getX())
|
||||
.putInt("originY", packet.getPosition().getY())
|
||||
.putInt("originZ", packet.getPosition().getZ())
|
||||
.build()
|
||||
);
|
||||
session.sendUpstreamPacket(levelEventPacket);
|
||||
|
||||
@ -346,6 +381,54 @@ public class JavaLevelEventTranslator extends PacketTranslator<ClientboundLevelE
|
||||
session.sendUpstreamPacket(soundEventPacket);
|
||||
return;
|
||||
}
|
||||
case PARTICLES_TRIAL_SPAWNER_DETECT_PLAYER -> {
|
||||
// Particles spawn here
|
||||
TrialSpawnerDetectEventData eventData = (TrialSpawnerDetectEventData) packet.getData();
|
||||
effectPacket.setType(org.cloudburstmc.protocol.bedrock.data.LevelEvent.PARTICLE_TRIAL_SPAWNER_DETECTION);
|
||||
// 0.75 is used here for Y instead of 0.5 to match Java Positioning.
|
||||
// 0.5 is what the BDS uses for positioning.
|
||||
effectPacket.setPosition(pos.sub(0.5f, 0.75f, 0.5f));
|
||||
effectPacket.setData(eventData.getDetectedPlayers());
|
||||
}
|
||||
case PARTICLES_TRIAL_SPAWNER_DETECT_PLAYER_OMINOUS -> {
|
||||
effectPacket.setType(org.cloudburstmc.protocol.bedrock.data.LevelEvent.PARTICLE_TRIAL_SPAWNER_DETECTION_CHARGED);
|
||||
effectPacket.setPosition(pos.sub(0.5f, 0.75f, 0.5f));
|
||||
/*
|
||||
Particles don't spawn here for some reason, only sound plays
|
||||
This seems to be a bug in v1.21.0 and v1.21.1: see https://bugs.mojang.com/browse/MCPE-181465
|
||||
If this gets fixed, the spawnOminousTrialSpawnerParticles function can be removed.
|
||||
The positioning should be the same as normal activation.
|
||||
*/
|
||||
spawnOminousTrialSpawnerParticles(session, pos);
|
||||
}
|
||||
case PARTICLES_TRIAL_SPAWNER_BECOME_OMINOUS -> {
|
||||
effectPacket.setType(org.cloudburstmc.protocol.bedrock.data.LevelEvent.PARTICLE_TRIAL_SPAWNER_BECOME_CHARGED);
|
||||
effectPacket.setPosition(pos.sub(0.5f, 0.5f, 0.5f));
|
||||
// Same issue as above here
|
||||
spawnOminousTrialSpawnerParticles(session, pos);
|
||||
}
|
||||
case PARTICLES_TRIAL_SPAWNER_SPAWN_MOB_AT -> {
|
||||
// This should be its own class in MCProtocolLib.
|
||||
// if 0, use Orange Flames,
|
||||
// if 1, use Blue Flames for ominous spawners
|
||||
UnknownLevelEventData eventData = (UnknownLevelEventData) packet.getData();
|
||||
effectPacket.setType(org.cloudburstmc.protocol.bedrock.data.LevelEvent.PARTICLE_TRIAL_SPAWNER_SPAWNING);
|
||||
effectPacket.setData(eventData.getData());
|
||||
}
|
||||
case PARTICLES_TRIAL_SPAWNER_SPAWN -> {
|
||||
UnknownLevelEventData eventData = (UnknownLevelEventData) packet.getData();
|
||||
effectPacket.setType(org.cloudburstmc.protocol.bedrock.data.LevelEvent.PARTICLE_TRIAL_SPAWNER_SPAWNING);
|
||||
effectPacket.setData(eventData.getData());
|
||||
|
||||
Random random = ThreadLocalRandom.current();
|
||||
PlaySoundPacket playSoundPacket = new PlaySoundPacket();
|
||||
playSoundPacket.setSound("trial_spawner.spawn_mob");
|
||||
playSoundPacket.setPosition(pos);
|
||||
playSoundPacket.setVolume(1.0f);
|
||||
playSoundPacket.setPitch(0.8f + random.nextFloat() * 0.3f);
|
||||
session.sendUpstreamPacket(playSoundPacket);
|
||||
}
|
||||
case PARTICLES_TRIAL_SPAWNER_SPAWN_ITEM -> effectPacket.setType(org.cloudburstmc.protocol.bedrock.data.LevelEvent.PARTICLE_TRIAL_SPAWNER_EJECTING);
|
||||
case SOUND_STOP_JUKEBOX_SONG -> {
|
||||
String bedrockSound = session.getWorldCache().removeActiveRecord(origin);
|
||||
if (bedrockSound == null) {
|
||||
@ -396,4 +479,15 @@ public class JavaLevelEventTranslator extends PacketTranslator<ClientboundLevelE
|
||||
}
|
||||
return facing;
|
||||
}
|
||||
}
|
||||
|
||||
private static void spawnOminousTrialSpawnerParticles(GeyserSession session, Vector3f pos) {
|
||||
int dimensionId = DimensionUtils.javaToBedrock(session.getDimension());
|
||||
SpawnParticleEffectPacket stringPacket = new SpawnParticleEffectPacket();
|
||||
stringPacket.setIdentifier("minecraft:trial_spawner_detection_ominous");
|
||||
stringPacket.setDimensionId(dimensionId);
|
||||
stringPacket.setPosition(pos.sub(0.5f, 0.75f, 0.5f));
|
||||
stringPacket.setMolangVariablesJson(Optional.empty());
|
||||
stringPacket.setUniqueEntityId(-1);
|
||||
session.sendUpstreamPacket(stringPacket);
|
||||
}
|
||||
}
|
||||
|
@ -60,6 +60,7 @@ import java.util.function.Function;
|
||||
|
||||
@Translator(packet = ClientboundLevelParticlesPacket.class)
|
||||
public class JavaLevelParticlesTranslator extends PacketTranslator<ClientboundLevelParticlesPacket> {
|
||||
private static final int MAX_PARTICLES = 100;
|
||||
|
||||
@Override
|
||||
public void translate(GeyserSession session, ClientboundLevelParticlesPacket packet) {
|
||||
@ -71,7 +72,8 @@ public class JavaLevelParticlesTranslator extends PacketTranslator<ClientboundLe
|
||||
session.sendUpstreamPacket(particleCreateFunction.apply(position));
|
||||
} else {
|
||||
Random random = ThreadLocalRandom.current();
|
||||
for (int i = 0; i < packet.getAmount(); i++) {
|
||||
int amount = Math.min(MAX_PARTICLES, packet.getAmount());
|
||||
for (int i = 0; i < amount; i++) {
|
||||
double offsetX = random.nextGaussian() * (double) packet.getOffsetX();
|
||||
double offsetY = random.nextGaussian() * (double) packet.getOffsetY();
|
||||
double offsetZ = random.nextGaussian() * (double) packet.getOffsetZ();
|
||||
@ -213,4 +215,4 @@ public class JavaLevelParticlesTranslator extends PacketTranslator<ClientboundLe
|
||||
.putFloat("z", position.getZ())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ package org.geysermc.geyser.translator.sound;
|
||||
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.registry.Registries;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
|
||||
@ -37,17 +38,16 @@ import java.util.Map;
|
||||
/**
|
||||
* Sound interaction handler for when a block is right-clicked.
|
||||
*/
|
||||
public interface BlockSoundInteractionTranslator extends SoundInteractionTranslator<String> {
|
||||
|
||||
public interface BlockSoundInteractionTranslator extends SoundInteractionTranslator<BlockState> {
|
||||
/**
|
||||
* Handles the block interaction when a player
|
||||
* right-clicks a block.
|
||||
*
|
||||
* @param session the session interacting with the block
|
||||
* @param position the position of the block
|
||||
* @param identifier the identifier of the block
|
||||
* @param state the state of the block
|
||||
*/
|
||||
static void handleBlockInteraction(GeyserSession session, Vector3f position, String identifier) {
|
||||
static void handleBlockInteraction(GeyserSession session, Vector3f position, BlockState state) {
|
||||
// If we need to get the hand identifier, only get it once and save it to a variable
|
||||
String handIdentifier = null;
|
||||
|
||||
@ -58,7 +58,7 @@ public interface BlockSoundInteractionTranslator extends SoundInteractionTransla
|
||||
if (interactionEntry.getKey().blocks().length != 0) {
|
||||
boolean contains = false;
|
||||
for (String blockIdentifier : interactionEntry.getKey().blocks()) {
|
||||
if (identifier.contains(blockIdentifier)) {
|
||||
if (state.toString().contains(blockIdentifier)) {
|
||||
contains = true;
|
||||
break;
|
||||
}
|
||||
@ -87,7 +87,7 @@ public interface BlockSoundInteractionTranslator extends SoundInteractionTransla
|
||||
continue;
|
||||
}
|
||||
}
|
||||
((BlockSoundInteractionTranslator) interactionEntry.getValue()).translate(session, position, identifier);
|
||||
((BlockSoundInteractionTranslator) interactionEntry.getValue()).translate(session, position, state);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,7 @@ import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.SoundEvent;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEventPacket;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.sound.BlockSoundInteractionTranslator;
|
||||
import org.geysermc.geyser.translator.sound.SoundTranslator;
|
||||
@ -37,7 +38,8 @@ import org.geysermc.geyser.translator.sound.SoundTranslator;
|
||||
public class BucketSoundInteractionTranslator implements BlockSoundInteractionTranslator {
|
||||
|
||||
@Override
|
||||
public void translate(GeyserSession session, Vector3f position, String identifier) {
|
||||
public void translate(GeyserSession session, Vector3f position, BlockState state) {
|
||||
String identifier = state.toString();
|
||||
if (!session.isPlacedBucket()) {
|
||||
return; // No bucket was really interacted with
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ package org.geysermc.geyser.translator.sound.block;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.LevelEvent;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.LevelEventPacket;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.sound.BlockSoundInteractionTranslator;
|
||||
import org.geysermc.geyser.translator.sound.SoundTranslator;
|
||||
@ -36,7 +37,8 @@ import org.geysermc.geyser.translator.sound.SoundTranslator;
|
||||
public class ComparatorSoundInteractionTranslator implements BlockSoundInteractionTranslator {
|
||||
|
||||
@Override
|
||||
public void translate(GeyserSession session, Vector3f position, String identifier) {
|
||||
public void translate(GeyserSession session, Vector3f position, BlockState state) {
|
||||
String identifier = state.toString();
|
||||
boolean powered = identifier.contains("mode=compare");
|
||||
LevelEventPacket levelEventPacket = new LevelEventPacket();
|
||||
levelEventPacket.setPosition(position);
|
||||
|
@ -28,6 +28,7 @@ package org.geysermc.geyser.translator.sound.block;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.SoundEvent;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEventPacket;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.sound.BlockSoundInteractionTranslator;
|
||||
import org.geysermc.geyser.translator.sound.SoundTranslator;
|
||||
@ -36,7 +37,7 @@ import org.geysermc.geyser.translator.sound.SoundTranslator;
|
||||
public class FlintAndSteelInteractionTranslator implements BlockSoundInteractionTranslator {
|
||||
|
||||
@Override
|
||||
public void translate(GeyserSession session, Vector3f position, String identifier) {
|
||||
public void translate(GeyserSession session, Vector3f position, BlockState state) {
|
||||
LevelSoundEventPacket levelSoundEventPacket = new LevelSoundEventPacket();
|
||||
levelSoundEventPacket.setPosition(position);
|
||||
levelSoundEventPacket.setBabySound(false);
|
||||
|
@ -28,6 +28,7 @@ package org.geysermc.geyser.translator.sound.block;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.SoundEvent;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEventPacket;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.sound.BlockSoundInteractionTranslator;
|
||||
@ -37,7 +38,8 @@ import org.geysermc.geyser.translator.sound.SoundTranslator;
|
||||
public class GrassPathInteractionTranslator implements BlockSoundInteractionTranslator {
|
||||
|
||||
@Override
|
||||
public void translate(GeyserSession session, Vector3f position, String identifier) {
|
||||
public void translate(GeyserSession session, Vector3f position, BlockState state) {
|
||||
String identifier = state.toString();
|
||||
LevelSoundEventPacket levelSoundEventPacket = new LevelSoundEventPacket();
|
||||
levelSoundEventPacket.setPosition(position);
|
||||
levelSoundEventPacket.setBabySound(false);
|
||||
|
@ -28,6 +28,7 @@ package org.geysermc.geyser.translator.sound.block;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.SoundEvent;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEventPacket;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.sound.BlockSoundInteractionTranslator;
|
||||
@ -37,7 +38,8 @@ import org.geysermc.geyser.translator.sound.SoundTranslator;
|
||||
public class HoeInteractionTranslator implements BlockSoundInteractionTranslator {
|
||||
|
||||
@Override
|
||||
public void translate(GeyserSession session, Vector3f position, String identifier) {
|
||||
public void translate(GeyserSession session, Vector3f position, BlockState state) {
|
||||
String identifier = state.toString();
|
||||
LevelSoundEventPacket levelSoundEventPacket = new LevelSoundEventPacket();
|
||||
levelSoundEventPacket.setPosition(position);
|
||||
levelSoundEventPacket.setBabySound(false);
|
||||
|
@ -28,6 +28,7 @@ package org.geysermc.geyser.translator.sound.block;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.LevelEvent;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.LevelEventPacket;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.sound.BlockSoundInteractionTranslator;
|
||||
import org.geysermc.geyser.translator.sound.SoundTranslator;
|
||||
@ -36,7 +37,8 @@ import org.geysermc.geyser.translator.sound.SoundTranslator;
|
||||
public class LeverSoundInteractionTranslator implements BlockSoundInteractionTranslator {
|
||||
|
||||
@Override
|
||||
public void translate(GeyserSession session, Vector3f position, String identifier) {
|
||||
public void translate(GeyserSession session, Vector3f position, BlockState state) {
|
||||
String identifier = state.toString();
|
||||
boolean powered = identifier.contains("powered=true");
|
||||
LevelEventPacket levelEventPacket = new LevelEventPacket();
|
||||
levelEventPacket.setPosition(position);
|
||||
|
@ -27,21 +27,42 @@ package org.geysermc.geyser.translator.sound.block;
|
||||
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.LevelEvent;
|
||||
import org.cloudburstmc.protocol.bedrock.data.SoundEvent;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.LevelEventPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEventPacket;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.sound.BlockSoundInteractionTranslator;
|
||||
import org.geysermc.geyser.translator.sound.SoundTranslator;
|
||||
|
||||
@SoundTranslator(blocks = {"door", "fence_gate"})
|
||||
public class DoorSoundInteractionTranslator implements BlockSoundInteractionTranslator {
|
||||
public class OpenableSoundInteractionTranslator implements BlockSoundInteractionTranslator {
|
||||
|
||||
@Override
|
||||
public void translate(GeyserSession session, Vector3f position, String identifier) {
|
||||
public void translate(GeyserSession session, Vector3f position, BlockState state) {
|
||||
String identifier = state.toString();
|
||||
if (identifier.contains("iron")) return;
|
||||
LevelEventPacket levelEventPacket = new LevelEventPacket();
|
||||
levelEventPacket.setType(LevelEvent.SOUND_DOOR_OPEN);
|
||||
levelEventPacket.setPosition(position);
|
||||
levelEventPacket.setData(0);
|
||||
session.sendUpstreamPacket(levelEventPacket);
|
||||
SoundEvent event = getSound(identifier.contains("open=true"), identifier);
|
||||
LevelSoundEventPacket levelSoundEventPacket = new LevelSoundEventPacket();
|
||||
levelSoundEventPacket.setPosition(position.add(0.5, 0.5, 0.5));
|
||||
levelSoundEventPacket.setBabySound(false);
|
||||
levelSoundEventPacket.setRelativeVolumeDisabled(false);
|
||||
levelSoundEventPacket.setIdentifier(":");
|
||||
levelSoundEventPacket.setSound(event);
|
||||
levelSoundEventPacket.setExtraData(session.getBlockMappings().getBedrockBlock(state).getRuntimeId());
|
||||
session.sendUpstreamPacket(levelSoundEventPacket);
|
||||
}
|
||||
|
||||
private SoundEvent getSound(boolean open, String identifier) {
|
||||
if (identifier.contains("_door")) {
|
||||
return open ? SoundEvent.DOOR_OPEN : SoundEvent.DOOR_CLOSE;
|
||||
}
|
||||
|
||||
if (identifier.contains("_trapdoor")) {
|
||||
return open ? SoundEvent.TRAPDOOR_OPEN : SoundEvent.TRAPDOOR_CLOSE;
|
||||
}
|
||||
|
||||
// Fence Gate
|
||||
return open ? SoundEvent.FENCE_GATE_OPEN : SoundEvent.FENCE_GATE_CLOSE;
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
* 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
|
||||
@ -32,6 +32,7 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.geysermc.geyser.entity.EntityDefinitions;
|
||||
import org.geysermc.geyser.entity.type.BoatEntity;
|
||||
import org.geysermc.geyser.entity.type.Entity;
|
||||
import org.geysermc.geyser.entity.type.TextDisplayEntity;
|
||||
import org.geysermc.geyser.entity.type.living.ArmorStandEntity;
|
||||
import org.geysermc.geyser.entity.type.living.animal.AnimalEntity;
|
||||
import org.geysermc.geyser.entity.type.living.animal.horse.CamelEntity;
|
||||
@ -172,11 +173,7 @@ public final class EntityUtils {
|
||||
case BOAT -> {
|
||||
// Without the X offset, more than one entity on a boat is stacked on top of each other
|
||||
if (moreThanOneEntity) {
|
||||
if (rider) {
|
||||
xOffset = 0.2f;
|
||||
} else {
|
||||
xOffset = -0.6f;
|
||||
}
|
||||
xOffset = rider ? 0.2f : -0.6f;
|
||||
if (passenger instanceof AnimalEntity) {
|
||||
xOffset += 0.2f;
|
||||
}
|
||||
@ -203,6 +200,18 @@ public final class EntityUtils {
|
||||
case CHEST_BOAT -> xOffset = 0.15F;
|
||||
case CHICKEN -> zOffset = -0.1f;
|
||||
case TRADER_LLAMA, LLAMA -> zOffset = -0.3f;
|
||||
case TEXT_DISPLAY -> {
|
||||
if (passenger instanceof TextDisplayEntity textDisplay) {
|
||||
Vector3f displayTranslation = textDisplay.getTranslation();
|
||||
if (displayTranslation == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
xOffset = displayTranslation.getX();
|
||||
yOffset = displayTranslation.getY() + 0.2f;
|
||||
zOffset = displayTranslation.getZ();
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Bedrock Differences
|
||||
@ -228,8 +237,7 @@ public final class EntityUtils {
|
||||
if (mount instanceof ArmorStandEntity armorStand) {
|
||||
yOffset -= armorStand.getYOffset();
|
||||
}
|
||||
Vector3f offset = Vector3f.from(xOffset, yOffset, zOffset);
|
||||
passenger.setRiderSeatPosition(offset);
|
||||
passenger.setRiderSeatPosition(Vector3f.from(xOffset, yOffset, zOffset));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -133,7 +133,7 @@ public final class SoundUtils {
|
||||
}
|
||||
if (sound == null) {
|
||||
session.getGeyser().getLogger().debug("[Builtin] Sound for original '" + soundIdentifier + "' to mappings '" + soundMapping.getBedrock()
|
||||
+ "' was not a playable level sound, or has yet to be mapped to an enum in SoundEvent.");
|
||||
+ "' was not a playable level sound, or has yet to be mapped to an enum in SoundEvent.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -144,7 +144,7 @@ public final class SoundUtils {
|
||||
// Minecraft Wiki: 2^(x/12) = Java pitch where x is -12 to 12
|
||||
// Java sends the note value as above starting with -12 and ending at 12
|
||||
// Bedrock has a number for each type of note, then proceeds up the scale by adding to that number
|
||||
soundPacket.setExtraData(soundMapping.getExtraData() + (int)(Math.round((Math.log10(pitch) / Math.log10(2)) * 12)) + 12);
|
||||
soundPacket.setExtraData(soundMapping.getExtraData() + (int) (Math.round((Math.log10(pitch) / Math.log10(2)) * 12)) + 12);
|
||||
} else if (sound == SoundEvent.PLACE && soundMapping.getExtraData() == -1) {
|
||||
if (!soundMapping.getIdentifier().equals(":")) {
|
||||
int javaId = BlockRegistries.JAVA_IDENTIFIER_TO_ID.get().getOrDefault(soundMapping.getIdentifier(), Block.JAVA_AIR_ID);
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 23cb22f9ceeb7f24b896a69a711944d7f3e756ed
|
||||
Subproject commit aaf53d6953c927e5ac1b87fd6627ffbfd4aa7cf5
|
@ -10,7 +10,7 @@ netty-io-uring = "0.0.25.Final-SNAPSHOT"
|
||||
guava = "29.0-jre"
|
||||
gson = "2.3.1" # Provided by Spigot 1.8.8
|
||||
websocket = "1.5.1"
|
||||
protocol = "3.0.0.Beta2-20240616.144648-10"
|
||||
protocol = "3.0.0.Beta2-20240704.153116-14"
|
||||
raknet = "1.0.0.CR3-20240416.144209-1"
|
||||
mcauthlib = "e5b0bcc"
|
||||
mcprotocollib = "1.21-20240616.154144-5"
|
||||
@ -39,7 +39,7 @@ minecraft = "1.21"
|
||||
indra = "3.1.3"
|
||||
shadow = "8.1.1"
|
||||
architectury-plugin = "3.4-SNAPSHOT"
|
||||
architectury-loom = "1.6-SNAPSHOT"
|
||||
architectury-loom = "1.7-SNAPSHOT"
|
||||
minotaur = "2.8.7"
|
||||
lombok = "8.4"
|
||||
blossom = "2.1.0"
|
||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binäre Datei nicht angezeigt.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren