Take width of hanging signs into account

This commit is contained in:
Konicai 2023-05-18 00:58:27 -04:00
parent b9c568733a
commit df5092d32f
5 changed files with 80 additions and 12 deletions

View file

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

View file

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

View file

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

View file

@ -44,6 +44,9 @@ public class BedrockBlockEntityDataTranslator extends PacketTranslator<BlockEnti
NbtMap tag = packet.getData();
String id = tag.getString("id");
if (id.equals("Sign")) {
// Hanging signs are narrower
int widthMax = SignUtils.getSignWidthMax(session.getGeyser().getWorldManager().getBlockAt(session, packet.getBlockPosition()));
String text = MessageTranslator.convertToPlainText(
tag.getCompound(session.getWorldCache().isEditingSignOnFront() ? "FrontText" : "BackText").getString("Text"));
// Note: as of 1.18.30, only one packet is sent from Bedrock when the sign is finished.
@ -60,13 +63,10 @@ public class BedrockBlockEntityDataTranslator extends PacketTranslator<BlockEnti
for (char character : text.toCharArray()) {
widthCount += SignUtils.getCharacterWidth(character);
// todo 1.20: update for hanging signs (smaller width). Currently bedrock thinks hanging signs are normal,
// so it thinks hanging signs have more width than they actually do. Seems like JE just truncates it.
// If we get a return in Bedrock, or go over the character width max, that signals to use the next line.
if (character == '\n' || widthCount > 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;

View file

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