diff --git a/core/src/main/java/org/geysermc/geyser/level/block/BlockStateValues.java b/core/src/main/java/org/geysermc/geyser/level/block/BlockStateValues.java index 1d56946a8..27fb539be 100644 --- a/core/src/main/java/org/geysermc/geyser/level/block/BlockStateValues.java +++ b/core/src/main/java/org/geysermc/geyser/level/block/BlockStateValues.java @@ -45,6 +45,7 @@ import java.util.Locale; */ public final class BlockStateValues { private static final IntSet ALL_CAULDRONS = new IntOpenHashSet(); + private static final IntSet HANGING_SIGNS = new IntOpenHashSet(); private static final Int2IntMap BANNER_COLORS = new FixedInt2IntMap(); private static final Int2ByteMap BED_COLORS = new FixedInt2ByteMap(); private static final Int2ByteMap COMMAND_BLOCK_VALUES = new Int2ByteOpenHashMap(); @@ -86,6 +87,12 @@ public final class BlockStateValues { * @param blockData JsonNode of info about the block from blocks.json */ public static void storeBlockStateValues(String javaId, int javaBlockState, JsonNode blockData) { + if (javaId.contains("_hanging_sign")) { + // covers hanging_sign and wall_hanging_sign + HANGING_SIGNS.add(javaBlockState); + return; + } + JsonNode bannerColor = blockData.get("banner_color"); if (bannerColor != null) { BANNER_COLORS.put(javaBlockState, (byte) bannerColor.intValue()); @@ -203,6 +210,17 @@ public final class BlockStateValues { } } + /** + * Hanging signs have a different maximum text width than "normal" signs. As a result, when the client + * updates the text of a sign without indication of the sign type, we must determine it. + * + * @param state BlockState of the block + * @return true if the sign is any hanging variant + */ + public static boolean isHangingSign(int state) { + return HANGING_SIGNS.contains(state); + } + /** * Banner colors are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock. * This gives an integer color that Bedrock can use. diff --git a/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/HangingSignBlockEntityTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/HangingSignBlockEntityTranslator.java new file mode 100644 index 000000000..ea11dcf48 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/HangingSignBlockEntityTranslator.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019-2023 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.translator.level.block.entity; + +import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityType; +import org.geysermc.geyser.util.SignUtils; + +@BlockEntity(type = BlockEntityType.HANGING_SIGN) +public class HangingSignBlockEntityTranslator extends SignBlockEntityTranslator { + + @Override + public int signWidthMax() { + return SignUtils.HANGING_SIGN_WIDTH_MAX; // Smaller than that for BlockEntityType.SIGN + } +} diff --git a/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/SignBlockEntityTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/SignBlockEntityTranslator.java index 76d4bfdf4..f5dae3818 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/SignBlockEntityTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/SignBlockEntityTranslator.java @@ -35,7 +35,7 @@ import org.geysermc.geyser.text.ChatColor; import org.geysermc.geyser.translator.text.MessageTranslator; import org.geysermc.geyser.util.SignUtils; -@BlockEntity(type = {BlockEntityType.SIGN, BlockEntityType.HANGING_SIGN}) +@BlockEntity(type = BlockEntityType.SIGN) public class SignBlockEntityTranslator extends BlockEntityTranslator { /** * Maps a color stored in a sign's Color tag to its ARGB value. @@ -67,6 +67,10 @@ public class SignBlockEntityTranslator extends BlockEntityTranslator { return dyeColor | (255 << 24); } + public int signWidthMax() { + return SignUtils.SIGN_WIDTH_MAX; + } + @Override public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { builder.putCompound("FrontText", translateSide(tag.get("front_text"))); @@ -105,8 +109,7 @@ public class SignBlockEntityTranslator extends BlockEntityTranslator { signWidth += SignUtils.getCharacterWidth(c); } - // todo 1.20: update for hanging signs (smaller width). Currently OK because bedrock sees hanging signs as normal signs - if (signWidth <= SignUtils.BEDROCK_CHARACTER_WIDTH_MAX) { + if (signWidth <= signWidthMax()) { finalSignLine.append(c); } else { // Adding the character would make Bedrock move to the next line - Java doesn't do that, so we do not want to diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockBlockEntityDataTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockBlockEntityDataTranslator.java index 3a04513e5..7e972c3da 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockBlockEntityDataTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockBlockEntityDataTranslator.java @@ -44,6 +44,9 @@ public class BedrockBlockEntityDataTranslator extends PacketTranslator SignUtils.JAVA_CHARACTER_WIDTH_MAX) { + if (character == '\n' || widthCount > widthMax) { // We need to apply some more logic if we went over the character width max - boolean wentOverMax = widthCount > SignUtils.JAVA_CHARACTER_WIDTH_MAX && character != '\n'; + boolean wentOverMax = widthCount > widthMax && character != '\n'; widthCount = 0; // Saves if we're moving a word to the next line String word = null; diff --git a/core/src/main/java/org/geysermc/geyser/util/SignUtils.java b/core/src/main/java/org/geysermc/geyser/util/SignUtils.java index 82030b8f5..f2558297d 100644 --- a/core/src/main/java/org/geysermc/geyser/util/SignUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/SignUtils.java @@ -25,6 +25,8 @@ package org.geysermc.geyser.util; +import org.geysermc.geyser.level.block.BlockStateValues; + /** * Provides utilities for interacting with signs. Mainly, it deals with the widths of each character. * Since Bedrock auto-wraps signs and Java does not, we have to take this into account when translating signs. @@ -33,14 +35,15 @@ public class SignUtils { // TODO: If we send the Java font via resource pack, does width change? /** - * The maximum character width that a sign can hold in Bedrock + * The maximum character width that a non-hanging sign can hold in both Java and Bedrock */ - public static final int BEDROCK_CHARACTER_WIDTH_MAX = 88; + public static final int SIGN_WIDTH_MAX = 90; /** - * The maximum character width that a sign can hold in Java + * The maximum character width that a hanging sign can hold in both Java and Bedrock. Hanging signs are narrower. */ - public static final int JAVA_CHARACTER_WIDTH_MAX = 90; + public static final int HANGING_SIGN_WIDTH_MAX = 60; + /** * Gets the Minecraft width of a character @@ -58,4 +61,10 @@ public class SignUtils { }; } + public static int getSignWidthMax(int javaBlockState) { + if (BlockStateValues.isHangingSign(javaBlockState)) { + return HANGING_SIGN_WIDTH_MAX; + } + return SIGN_WIDTH_MAX; + } }