Mirror von
https://github.com/GeyserMC/Geyser.git
synchronisiert 2024-12-26 16:12:46 +01:00
Rewrite message handling in MessageUtils to use Adventure (#1498)
* Rewrite message handling in MessageUtils to use Adventure * Move to static Adventure commit to fix a bug * Initial test implementation * Add RGB downgrade test * Move MessageUtils and rename * Clean-up and fix tests * Fixed sign and book content handling * Fix blank signs causing NPEs * Fix reset before message being stripped * Add comment about the reset character * Fix legacy style server motds * Fix more messages being handled wrong * Fix title packets being handled wrong * Fix trailing formatting characters on the end of sign lines * Add auto updating of Java locale files * Add en_us locale updating and hash caching * Changes to hash determining Co-authored-by: DoctorMacc <toy.fighter1@gmail.com>
Dieser Commit ist enthalten in:
Ursprung
47f25f1205
Commit
512f8cd6c2
@ -143,17 +143,23 @@
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.kyori</groupId>
|
||||
<groupId>com.github.kyoripowered.adventure</groupId>
|
||||
<artifactId>adventure-text-serializer-gson</artifactId>
|
||||
<version>4.1.1</version>
|
||||
<version>4d8a67d798</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.kyori</groupId>
|
||||
<groupId>com.github.kyoripowered.adventure</groupId>
|
||||
<artifactId>adventure-text-serializer-legacy</artifactId>
|
||||
<version>4.1.1</version>
|
||||
<version>0599048</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.13.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
@ -283,6 +289,15 @@
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.22.0</version>
|
||||
<configuration>
|
||||
<!-- Force the right file encoding during unit testing -->
|
||||
<argLine>-Dfile.encoding=${project.build.sourceEncoding}</argLine>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
@ -26,13 +26,12 @@
|
||||
package org.geysermc.connector.entity;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
import com.github.steveice10.mc.protocol.data.message.Message;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||
import org.geysermc.connector.utils.MessageUtils;
|
||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
||||
|
||||
public class CommandBlockMinecartEntity extends DefaultBlockMinecartEntity {
|
||||
|
||||
@ -51,7 +50,7 @@ public class CommandBlockMinecartEntity extends DefaultBlockMinecartEntity {
|
||||
metadata.put(EntityData.COMMAND_BLOCK_COMMAND, entityMetadata.getValue());
|
||||
}
|
||||
if (entityMetadata.getId() == 14) {
|
||||
metadata.put(EntityData.COMMAND_BLOCK_LAST_OUTPUT, MessageUtils.getBedrockMessage((Message) entityMetadata.getValue()));
|
||||
metadata.put(EntityData.COMMAND_BLOCK_LAST_OUTPUT, MessageTranslator.convertMessage(entityMetadata.getValue().toString()));
|
||||
}
|
||||
super.updateBedrockMetadata(entityMetadata, session);
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.item.ItemRegistry;
|
||||
import org.geysermc.connector.utils.AttributeUtils;
|
||||
import org.geysermc.connector.utils.ChunkUtils;
|
||||
import org.geysermc.connector.utils.MessageUtils;
|
||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
@ -318,7 +318,7 @@ public class Entity {
|
||||
Message message = (Message) entityMetadata.getValue();
|
||||
if (message != null)
|
||||
// Always translate even if it's a TextMessage since there could be translatable parameters
|
||||
metadata.put(EntityData.NAMETAG, MessageUtils.getTranslatedBedrockMessage(message, session.getLocale(), true));
|
||||
metadata.put(EntityData.NAMETAG, MessageTranslator.convertMessage(message.toString(), session.getLocale()));
|
||||
}
|
||||
break;
|
||||
case 3: // is custom name visible
|
||||
|
@ -51,7 +51,7 @@ import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.session.cache.EntityEffectCache;
|
||||
import org.geysermc.connector.scoreboard.Team;
|
||||
import org.geysermc.connector.utils.AttributeUtils;
|
||||
import org.geysermc.connector.utils.MessageUtils;
|
||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -243,13 +243,13 @@ public class PlayerEntity extends LivingEntity {
|
||||
String username = this.username;
|
||||
TextMessage name = (TextMessage) entityMetadata.getValue();
|
||||
if (name != null) {
|
||||
username = MessageUtils.getBedrockMessage(name);
|
||||
username = MessageTranslator.convertMessage(name.toString());
|
||||
}
|
||||
Team team = session.getWorldCache().getScoreboard().getTeamFor(username);
|
||||
if (team != null) {
|
||||
String displayName = "";
|
||||
if (team.isVisibleFor(session.getPlayerEntity().getUsername())) {
|
||||
displayName = MessageUtils.toChatColor(team.getColor()) + username;
|
||||
displayName = MessageTranslator.toChatColor(team.getColor()) + username;
|
||||
displayName = team.getCurrentData().getDisplayName(displayName);
|
||||
}
|
||||
metadata.put(EntityData.NAMETAG, displayName);
|
||||
|
@ -25,7 +25,6 @@
|
||||
|
||||
package org.geysermc.connector.network;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.message.MessageSerializer;
|
||||
import com.nukkitx.protocol.bedrock.BedrockPong;
|
||||
import com.nukkitx.protocol.bedrock.BedrockServerEventHandler;
|
||||
import com.nukkitx.protocol.bedrock.BedrockServerSession;
|
||||
@ -36,7 +35,7 @@ import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.configuration.GeyserConfiguration;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.ping.IGeyserPingPassthrough;
|
||||
import org.geysermc.connector.utils.MessageUtils;
|
||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
||||
import org.geysermc.connector.utils.LanguageUtils;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
@ -76,7 +75,7 @@ public class ConnectorServerEventHandler implements BedrockServerEventHandler {
|
||||
pong.setIpv4Port(config.getBedrock().getPort());
|
||||
|
||||
if (config.isPassthroughMotd() && pingInfo != null && pingInfo.getDescription() != null) {
|
||||
String[] motd = MessageUtils.getBedrockMessage(MessageSerializer.fromString(pingInfo.getDescription())).split("\n");
|
||||
String[] motd = MessageTranslator.convertMessageLenient(pingInfo.getDescription()).split("\n");
|
||||
String mainMotd = motd[0]; // First line of the motd.
|
||||
String subMotd = (motd.length != 1) ? motd[1] : ""; // Second line of the motd if present, otherwise blank.
|
||||
|
||||
|
@ -25,12 +25,11 @@
|
||||
|
||||
package org.geysermc.connector.network;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.message.MessageSerializer;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import org.geysermc.connector.common.ping.GeyserPingInfo;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.utils.MessageUtils;
|
||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
@ -148,7 +147,7 @@ public class QueryPacketHandler {
|
||||
}
|
||||
|
||||
if (connector.getConfig().isPassthroughMotd() && pingInfo != null) {
|
||||
String[] javaMotd = MessageUtils.getBedrockMessage(MessageSerializer.fromString(pingInfo.getDescription())).split("\n");
|
||||
String[] javaMotd = MessageTranslator.convertMessageLenient(pingInfo.getDescription()).split("\n");
|
||||
motd = javaMotd[0].trim(); // First line of the motd.
|
||||
} else {
|
||||
motd = connector.getConfig().getBedrock().getMotd1();
|
||||
|
@ -34,7 +34,6 @@ import com.github.steveice10.mc.protocol.data.SubProtocol;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
|
||||
import com.github.steveice10.mc.protocol.data.game.statistic.Statistic;
|
||||
import com.github.steveice10.mc.protocol.data.game.window.VillagerTrade;
|
||||
import com.github.steveice10.mc.protocol.data.message.MessageSerializer;
|
||||
import com.github.steveice10.mc.protocol.packet.handshake.client.HandshakePacket;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.world.ClientTeleportConfirmPacket;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.ServerRespawnPacket;
|
||||
@ -66,6 +65,7 @@ import org.geysermc.connector.common.AuthType;
|
||||
import org.geysermc.connector.entity.Entity;
|
||||
import org.geysermc.connector.entity.PlayerEntity;
|
||||
import org.geysermc.connector.inventory.PlayerInventory;
|
||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
||||
import org.geysermc.connector.network.remote.RemoteServer;
|
||||
import org.geysermc.connector.network.session.auth.AuthData;
|
||||
import org.geysermc.connector.network.session.auth.BedrockClientData;
|
||||
@ -496,7 +496,7 @@ public class GeyserSession implements CommandSender {
|
||||
event.getCause().printStackTrace();
|
||||
}
|
||||
|
||||
upstream.disconnect(MessageUtils.getBedrockMessage(MessageSerializer.fromString(event.getReason())));
|
||||
upstream.disconnect(MessageTranslator.convertMessageLenient(event.getReason()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -33,7 +33,7 @@ import com.nukkitx.protocol.bedrock.packet.BossEventPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.RemoveEntityPacket;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.utils.MessageUtils;
|
||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class BossBar {
|
||||
@ -58,7 +58,7 @@ public class BossBar {
|
||||
BossEventPacket bossEventPacket = new BossEventPacket();
|
||||
bossEventPacket.setBossUniqueEntityId(entityId);
|
||||
bossEventPacket.setAction(BossEventPacket.Action.CREATE);
|
||||
bossEventPacket.setTitle(MessageUtils.getTranslatedBedrockMessage(title, session.getLocale()));
|
||||
bossEventPacket.setTitle(MessageTranslator.convertMessage(title.toString(), session.getLocale()));
|
||||
bossEventPacket.setHealthPercentage(health);
|
||||
bossEventPacket.setColor(color); //ignored by client
|
||||
bossEventPacket.setOverlay(overlay);
|
||||
@ -72,7 +72,7 @@ public class BossBar {
|
||||
BossEventPacket bossEventPacket = new BossEventPacket();
|
||||
bossEventPacket.setBossUniqueEntityId(entityId);
|
||||
bossEventPacket.setAction(BossEventPacket.Action.UPDATE_NAME);
|
||||
bossEventPacket.setTitle(MessageUtils.getTranslatedBedrockMessage(title, session.getLocale()));
|
||||
bossEventPacket.setTitle(MessageTranslator.convertMessage(title.toString(), session.getLocale()));
|
||||
|
||||
session.sendUpstreamPacket(bossEventPacket);
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ import org.geysermc.connector.network.translators.Translator;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.ClientChatPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.CommandRequestPacket;
|
||||
import org.geysermc.connector.utils.MessageUtils;
|
||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
||||
|
||||
@Translator(packet = CommandRequestPacket.class)
|
||||
public class BedrockCommandRequestTranslator extends PacketTranslator<CommandRequestPacket> {
|
||||
@ -48,7 +48,7 @@ public class BedrockCommandRequestTranslator extends PacketTranslator<CommandReq
|
||||
} else {
|
||||
String message = packet.getCommand().trim();
|
||||
|
||||
if (MessageUtils.isTooLong(message, session)) {
|
||||
if (MessageTranslator.isTooLong(message, session)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,7 @@ import org.geysermc.connector.network.translators.Translator;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.ClientChatPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.TextPacket;
|
||||
import org.geysermc.connector.utils.MessageUtils;
|
||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
||||
|
||||
@Translator(packet = TextPacket.class)
|
||||
public class BedrockTextTranslator extends PacketTranslator<TextPacket> {
|
||||
@ -40,7 +40,7 @@ public class BedrockTextTranslator extends PacketTranslator<TextPacket> {
|
||||
public void translate(TextPacket packet, GeyserSession session) {
|
||||
String message = packet.getMessage().replaceAll("^\\.", "/").trim();
|
||||
|
||||
if (MessageUtils.isTooLong(message, session)) {
|
||||
if (MessageTranslator.isTooLong(message, session)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,278 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.connector.network.translators.chat;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.scoreboard.TeamColor;
|
||||
import com.github.steveice10.mc.protocol.data.message.style.ChatColor;
|
||||
import com.github.steveice10.mc.protocol.data.message.style.ChatFormat;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.renderer.TranslatableComponentRenderer;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import net.kyori.adventure.translation.TranslationRegistry;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.utils.LanguageUtils;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class MessageTranslator {
|
||||
|
||||
// These are used for handling the translations of the messages
|
||||
private static final TranslationRegistry REGISTRY = new MinecraftTranslationRegistry();
|
||||
private static final TranslatableComponentRenderer<Locale> RENDERER = TranslatableComponentRenderer.usingTranslationSource(REGISTRY);
|
||||
|
||||
// Store team colors for player names
|
||||
private static final Map<TeamColor, String> TEAM_COLORS = new HashMap<>();
|
||||
|
||||
static {
|
||||
TEAM_COLORS.put(TeamColor.BLACK, getColor(ChatColor.BLACK));
|
||||
TEAM_COLORS.put(TeamColor.DARK_BLUE, getColor(ChatColor.DARK_BLUE));
|
||||
TEAM_COLORS.put(TeamColor.DARK_GREEN, getColor(ChatColor.DARK_GREEN));
|
||||
TEAM_COLORS.put(TeamColor.DARK_AQUA, getColor(ChatColor.DARK_AQUA));
|
||||
TEAM_COLORS.put(TeamColor.DARK_RED, getColor(ChatColor.DARK_RED));
|
||||
TEAM_COLORS.put(TeamColor.DARK_PURPLE, getColor(ChatColor.DARK_PURPLE));
|
||||
TEAM_COLORS.put(TeamColor.GOLD, getColor(ChatColor.GOLD));
|
||||
TEAM_COLORS.put(TeamColor.GRAY, getColor(ChatColor.GRAY));
|
||||
TEAM_COLORS.put(TeamColor.DARK_GRAY, getColor(ChatColor.DARK_GRAY));
|
||||
TEAM_COLORS.put(TeamColor.BLUE, getColor(ChatColor.BLUE));
|
||||
TEAM_COLORS.put(TeamColor.GREEN, getColor(ChatColor.GREEN));
|
||||
TEAM_COLORS.put(TeamColor.AQUA, getColor(ChatColor.AQUA));
|
||||
TEAM_COLORS.put(TeamColor.RED, getColor(ChatColor.RED));
|
||||
TEAM_COLORS.put(TeamColor.LIGHT_PURPLE, getColor(ChatColor.LIGHT_PURPLE));
|
||||
TEAM_COLORS.put(TeamColor.YELLOW, getColor(ChatColor.YELLOW));
|
||||
TEAM_COLORS.put(TeamColor.WHITE, getColor(ChatColor.WHITE));
|
||||
TEAM_COLORS.put(TeamColor.OBFUSCATED, getFormat(ChatFormat.OBFUSCATED));
|
||||
TEAM_COLORS.put(TeamColor.BOLD, getFormat(ChatFormat.BOLD));
|
||||
TEAM_COLORS.put(TeamColor.STRIKETHROUGH, getFormat(ChatFormat.STRIKETHROUGH));
|
||||
TEAM_COLORS.put(TeamColor.ITALIC, getFormat(ChatFormat.ITALIC));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Java message to the legacy format ready for bedrock
|
||||
*
|
||||
* @param message Java message
|
||||
* @param locale Locale to use for translation strings
|
||||
* @return Parsed and formatted message for bedrock
|
||||
*/
|
||||
public static String convertMessage(String message, String locale) {
|
||||
Component component = GsonComponentSerializer.gson().deserialize(message);
|
||||
|
||||
// Get a Locale from the given locale string
|
||||
Locale localeCode = Locale.forLanguageTag(locale.replace('_', '-'));
|
||||
component = RENDERER.render(component, localeCode);
|
||||
|
||||
return LegacyComponentSerializer.legacySection().serialize(component);
|
||||
}
|
||||
|
||||
public static String convertMessage(String message) {
|
||||
return convertMessage(message, LanguageUtils.getDefaultLocale());
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the message is valid JSON in case it's plaintext. Works around GsonComponentSeraializer not using lenient mode.
|
||||
* See https://wiki.vg/Chat for messages sent in lenient mode, and for a description on leniency.
|
||||
*
|
||||
* @param message Potentially lenient JSON message
|
||||
* @param locale Locale to use for translation strings
|
||||
* @return Bedrock formatted message
|
||||
*/
|
||||
public static String convertMessageLenient(String message, String locale) {
|
||||
if (isMessage(message)) {
|
||||
return convertMessage(message, locale);
|
||||
} else {
|
||||
String convertedMessage = convertMessage(convertToJavaMessage(message), locale);
|
||||
|
||||
// We have to do this since Adventure strips the starting reset character
|
||||
if (message.startsWith(getColor(ChatColor.RESET))) {
|
||||
convertedMessage = getColor(ChatColor.RESET) + convertedMessage;
|
||||
}
|
||||
|
||||
return convertedMessage;
|
||||
}
|
||||
}
|
||||
|
||||
public static String convertMessageLenient(String message) {
|
||||
return convertMessageLenient(message, LanguageUtils.getDefaultLocale());
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Bedrock message string back to a format Java can understand
|
||||
*
|
||||
* @param message Message to convert
|
||||
* @return The formatted JSON string
|
||||
*/
|
||||
public static String convertToJavaMessage(String message) {
|
||||
Component component = LegacyComponentSerializer.legacySection().deserialize(message);
|
||||
return GsonComponentSerializer.gson().serialize(component);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given text string is a JSON message
|
||||
*
|
||||
* @param text String to test
|
||||
* @return True if its a valid message JSON string, false if not
|
||||
*/
|
||||
public static boolean isMessage(String text) {
|
||||
if (text.trim().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
GsonComponentSerializer.gson().deserialize(text);
|
||||
} catch (Exception ex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a {@link ChatColor} into a string for inserting into messages
|
||||
*
|
||||
* @param color {@link ChatColor} to convert
|
||||
* @return The converted color string
|
||||
*/
|
||||
private static String getColor(String color) {
|
||||
String base = "\u00a7";
|
||||
switch (color) {
|
||||
case ChatColor.BLACK:
|
||||
base += "0";
|
||||
break;
|
||||
case ChatColor.DARK_BLUE:
|
||||
base += "1";
|
||||
break;
|
||||
case ChatColor.DARK_GREEN:
|
||||
base += "2";
|
||||
break;
|
||||
case ChatColor.DARK_AQUA:
|
||||
base += "3";
|
||||
break;
|
||||
case ChatColor.DARK_RED:
|
||||
base += "4";
|
||||
break;
|
||||
case ChatColor.DARK_PURPLE:
|
||||
base += "5";
|
||||
break;
|
||||
case ChatColor.GOLD:
|
||||
base += "6";
|
||||
break;
|
||||
case ChatColor.GRAY:
|
||||
base += "7";
|
||||
break;
|
||||
case ChatColor.DARK_GRAY:
|
||||
base += "8";
|
||||
break;
|
||||
case ChatColor.BLUE:
|
||||
base += "9";
|
||||
break;
|
||||
case ChatColor.GREEN:
|
||||
base += "a";
|
||||
break;
|
||||
case ChatColor.AQUA:
|
||||
base += "b";
|
||||
break;
|
||||
case ChatColor.RED:
|
||||
base += "c";
|
||||
break;
|
||||
case ChatColor.LIGHT_PURPLE:
|
||||
base += "d";
|
||||
break;
|
||||
case ChatColor.YELLOW:
|
||||
base += "e";
|
||||
break;
|
||||
case ChatColor.WHITE:
|
||||
base += "f";
|
||||
break;
|
||||
case ChatColor.RESET:
|
||||
base += "r";
|
||||
break;
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a {@link ChatFormat} into a string for inserting into messages
|
||||
*
|
||||
* @param format {@link ChatFormat} to convert
|
||||
* @return The converted chat formatting string
|
||||
*/
|
||||
private static String getFormat(ChatFormat format) {
|
||||
StringBuilder str = new StringBuilder();
|
||||
String base = "\u00a7";
|
||||
switch (format) {
|
||||
case OBFUSCATED:
|
||||
base += "k";
|
||||
break;
|
||||
case BOLD:
|
||||
base += "l";
|
||||
break;
|
||||
case STRIKETHROUGH:
|
||||
base += "m";
|
||||
break;
|
||||
case UNDERLINED:
|
||||
base += "n";
|
||||
break;
|
||||
case ITALIC:
|
||||
base += "o";
|
||||
break;
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
|
||||
str.append(base);
|
||||
|
||||
return str.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a team color to a chat color
|
||||
*
|
||||
* @param teamColor
|
||||
* @return The chat color character
|
||||
*/
|
||||
public static String toChatColor(TeamColor teamColor) {
|
||||
return TEAM_COLORS.getOrDefault(teamColor, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given message is over 256 characters (Java edition server chat limit) and sends a message to the user if it is
|
||||
*
|
||||
* @param message Message to check
|
||||
* @param session {@link GeyserSession} for the user
|
||||
* @return True if the message is too long, false if not
|
||||
*/
|
||||
public static boolean isTooLong(String message, GeyserSession session) {
|
||||
if (message.length() > 256) {
|
||||
session.sendMessage(LanguageUtils.getPlayerLocaleString("geyser.chat.too_long", session.getLocale(), message.length()));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.connector.network.translators.chat;
|
||||
|
||||
import net.kyori.adventure.key.Key;
|
||||
import net.kyori.adventure.translation.TranslationRegistry;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.connector.utils.LocaleUtils;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Locale;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* This class is used for mapping a translation key with the already loaded Java locale data
|
||||
* Used in MessageTranslator.java as part of the KyoriPowered/Adventure library
|
||||
*/
|
||||
public class MinecraftTranslationRegistry implements TranslationRegistry {
|
||||
@Override
|
||||
public @NonNull Key name() {
|
||||
return Key.key("", "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable MessageFormat translate(@NonNull String key, @NonNull Locale locale) {
|
||||
// Get the locale string
|
||||
String localeString = LocaleUtils.getLocaleString(key, locale.toString());
|
||||
|
||||
// Replace the `%s` with numbered inserts `{0}`
|
||||
Pattern p = Pattern.compile("%s");
|
||||
Matcher m = p.matcher(localeString);
|
||||
StringBuffer sb = new StringBuffer();
|
||||
int i = 0;
|
||||
while (m.find()) {
|
||||
m.appendReplacement(sb, "{" + (i++) + "}");
|
||||
}
|
||||
m.appendTail(sb);
|
||||
|
||||
return new MessageFormat(sb.toString(), locale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void defaultLocale(@NonNull Locale locale) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(@NonNull String key, @NonNull Locale locale, @NonNull MessageFormat format) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregister(@NonNull String key) {
|
||||
|
||||
}
|
||||
}
|
@ -26,7 +26,6 @@
|
||||
package org.geysermc.connector.network.translators.item;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
|
||||
import com.github.steveice10.mc.protocol.data.message.MessageSerializer;
|
||||
import com.github.steveice10.opennbt.tag.builtin.*;
|
||||
import com.nukkitx.nbt.NbtList;
|
||||
import com.nukkitx.nbt.NbtMap;
|
||||
@ -44,7 +43,7 @@ import org.geysermc.connector.network.translators.ItemRemapper;
|
||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||
import org.geysermc.connector.utils.FileUtils;
|
||||
import org.geysermc.connector.utils.LanguageUtils;
|
||||
import org.geysermc.connector.utils.MessageUtils;
|
||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
||||
import org.reflections.Reflections;
|
||||
|
||||
import java.util.*;
|
||||
@ -385,19 +384,11 @@ public abstract class ItemTranslator {
|
||||
public static void translateDisplayProperties(GeyserSession session, CompoundTag tag) {
|
||||
if (tag != null) {
|
||||
CompoundTag display = tag.get("display");
|
||||
if (display != null && !display.isEmpty() && display.contains("Name")) {
|
||||
if (display != null && display.contains("Name")) {
|
||||
String name = ((StringTag) display.get("Name")).getValue();
|
||||
|
||||
// If its not a message convert it
|
||||
if (!MessageUtils.isMessage(name)) {
|
||||
Component component = LegacyComponentSerializer.legacySection().deserialize(name);
|
||||
name = GsonComponentSerializer.gson().serialize(component);
|
||||
}
|
||||
|
||||
// Check if its a message to translate
|
||||
if (MessageUtils.isMessage(name)) {
|
||||
// Get the translated name
|
||||
name = MessageUtils.getTranslatedBedrockMessage(MessageSerializer.fromString(name), session.getLocale());
|
||||
// Get the translated name and prefix it with a reset char
|
||||
name = MessageTranslator.convertMessageLenient(name, session.getLocale());
|
||||
|
||||
// Add the new name tag
|
||||
display.put(new StringTag("Name", name));
|
||||
@ -407,7 +398,6 @@ public abstract class ItemTranslator {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an {@link ItemStack} is equal to another item stack
|
||||
|
@ -37,7 +37,6 @@ import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.ItemRemapper;
|
||||
import org.geysermc.connector.network.translators.item.ItemEntry;
|
||||
import org.geysermc.connector.network.translators.item.NbtItemStackTranslator;
|
||||
import org.geysermc.connector.utils.MessageUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -108,7 +107,7 @@ public class BasicItemTranslator extends NbtItemStackTranslator {
|
||||
private String toBedrockMessage(StringTag tag) {
|
||||
String message = tag.getValue();
|
||||
if (message == null) return null;
|
||||
TextComponent component = (TextComponent) MessageUtils.phraseJavaMessage(message);
|
||||
TextComponent component = (TextComponent) GsonComponentSerializer.gson().deserialize(message);
|
||||
String legacy = LegacyComponentSerializer.legacySection().serialize(component);
|
||||
if (hasFormatting(LegacyComponentSerializer.legacySection().deserialize(legacy))) {
|
||||
return "§r" + legacy;
|
||||
|
@ -33,7 +33,7 @@ import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.ItemRemapper;
|
||||
import org.geysermc.connector.network.translators.item.NbtItemStackTranslator;
|
||||
import org.geysermc.connector.network.translators.item.ItemEntry;
|
||||
import org.geysermc.connector.utils.MessageUtils;
|
||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -56,7 +56,7 @@ public class BookPagesTranslator extends NbtItemStackTranslator {
|
||||
|
||||
CompoundTag pageTag = new CompoundTag("");
|
||||
pageTag.put(new StringTag("photoname", ""));
|
||||
pageTag.put(new StringTag("text", MessageUtils.getBedrockMessageLenient(textTag.getValue())));
|
||||
pageTag.put(new StringTag("text", MessageTranslator.convertMessageLenient(textTag.getValue())));
|
||||
pages.add(pageTag);
|
||||
}
|
||||
|
||||
@ -78,7 +78,7 @@ public class BookPagesTranslator extends NbtItemStackTranslator {
|
||||
CompoundTag pageTag = (CompoundTag) tag;
|
||||
|
||||
StringTag textTag = pageTag.get("text");
|
||||
pages.add(new StringTag(MessageUtils.getJavaMessage(textTag.getValue())));
|
||||
pages.add(new StringTag(MessageTranslator.convertToJavaMessage(textTag.getValue())));
|
||||
}
|
||||
|
||||
itemTag.remove("pages");
|
||||
|
@ -25,15 +25,12 @@
|
||||
|
||||
package org.geysermc.connector.network.translators.java;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.message.TranslationMessage;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.ServerChatPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.TextPacket;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.utils.MessageUtils;
|
||||
|
||||
import java.util.List;
|
||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
||||
|
||||
@Translator(packet = ServerChatPacket.class)
|
||||
public class JavaChatTranslator extends PacketTranslator<ServerChatPacket> {
|
||||
@ -59,21 +56,8 @@ public class JavaChatTranslator extends PacketTranslator<ServerChatPacket> {
|
||||
break;
|
||||
}
|
||||
|
||||
String locale = session.getLocale();
|
||||
|
||||
if (packet.getMessage() instanceof TranslationMessage) {
|
||||
textPacket.setType(TextPacket.Type.TRANSLATION);
|
||||
textPacket.setNeedsTranslation(true);
|
||||
|
||||
List<String> paramsTranslated = MessageUtils.getTranslationParams(((TranslationMessage) packet.getMessage()).getWith(), locale, packet.getMessage());
|
||||
textPacket.setParameters(paramsTranslated);
|
||||
|
||||
textPacket.setMessage(MessageUtils.insertParams(MessageUtils.getTranslatedBedrockMessage(packet.getMessage(), locale, true, packet.getMessage()), paramsTranslated));
|
||||
} else {
|
||||
textPacket.setNeedsTranslation(false);
|
||||
|
||||
textPacket.setMessage(MessageUtils.getTranslatedBedrockMessage(packet.getMessage(), locale, false, packet.getMessage()));
|
||||
}
|
||||
textPacket.setMessage(MessageTranslator.convertMessage(packet.getMessage().toString(), session.getLocale()));
|
||||
|
||||
session.sendUpstreamPacket(textPacket);
|
||||
}
|
||||
|
@ -29,13 +29,13 @@ import com.github.steveice10.mc.protocol.packet.ingame.server.ServerDisconnectPa
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.utils.MessageUtils;
|
||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
||||
|
||||
@Translator(packet = ServerDisconnectPacket.class)
|
||||
public class JavaDisconnectPacket extends PacketTranslator<ServerDisconnectPacket> {
|
||||
|
||||
@Override
|
||||
public void translate(ServerDisconnectPacket packet, GeyserSession session) {
|
||||
session.disconnect(MessageUtils.getTranslatedBedrockMessage(packet.getReason(), session.getLocale(), true));
|
||||
session.disconnect(MessageTranslator.convertMessage(packet.getReason().toString(), session.getLocale()));
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ import com.github.steveice10.mc.protocol.packet.login.server.LoginDisconnectPack
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.utils.MessageUtils;
|
||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
||||
|
||||
@Translator(packet = LoginDisconnectPacket.class)
|
||||
public class JavaLoginDisconnectTranslator extends PacketTranslator<LoginDisconnectPacket> {
|
||||
@ -37,6 +37,6 @@ public class JavaLoginDisconnectTranslator extends PacketTranslator<LoginDisconn
|
||||
@Override
|
||||
public void translate(LoginDisconnectPacket packet, GeyserSession session) {
|
||||
// The client doesn't manually get disconnected so we have to do it ourselves
|
||||
session.disconnect(MessageUtils.getTranslatedBedrockMessage(packet.getReason(), session.getLocale()));
|
||||
session.disconnect(MessageTranslator.convertMessage(packet.getReason().toString(), session.getLocale()));
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ package org.geysermc.connector.network.translators.java;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.utils.MessageUtils;
|
||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.ServerTitlePacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.SetTitlePacket;
|
||||
@ -41,14 +41,21 @@ public class JavaTitleTranslator extends PacketTranslator<ServerTitlePacket> {
|
||||
SetTitlePacket titlePacket = new SetTitlePacket();
|
||||
String locale = session.getLocale();
|
||||
|
||||
String text;
|
||||
if (packet.getTitle() == null) {
|
||||
text = " ";
|
||||
} else {
|
||||
text = MessageTranslator.convertMessage(packet.getTitle().toString(), locale);
|
||||
}
|
||||
|
||||
switch (packet.getAction()) {
|
||||
case TITLE:
|
||||
titlePacket.setType(SetTitlePacket.Type.TITLE);
|
||||
titlePacket.setText(MessageUtils.getTranslatedBedrockMessage(packet.getTitle(), locale));
|
||||
titlePacket.setText(text);
|
||||
break;
|
||||
case SUBTITLE:
|
||||
titlePacket.setType(SetTitlePacket.Type.SUBTITLE);
|
||||
titlePacket.setText(MessageUtils.getTranslatedBedrockMessage(packet.getTitle(), locale));
|
||||
titlePacket.setText(text);
|
||||
break;
|
||||
case CLEAR:
|
||||
case RESET:
|
||||
@ -57,9 +64,10 @@ public class JavaTitleTranslator extends PacketTranslator<ServerTitlePacket> {
|
||||
break;
|
||||
case ACTION_BAR:
|
||||
titlePacket.setType(SetTitlePacket.Type.ACTIONBAR);
|
||||
titlePacket.setText(MessageUtils.getTranslatedBedrockMessage(packet.getTitle(), locale));
|
||||
titlePacket.setText(text);
|
||||
break;
|
||||
case TIMES:
|
||||
titlePacket.setType(SetTitlePacket.Type.TIMES);
|
||||
titlePacket.setFadeInTime(packet.getFadeIn());
|
||||
titlePacket.setFadeOutTime(packet.getFadeOut());
|
||||
titlePacket.setStayTime(packet.getStay());
|
||||
|
@ -32,7 +32,7 @@ import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.scoreboard.Objective;
|
||||
import org.geysermc.connector.scoreboard.Scoreboard;
|
||||
import org.geysermc.connector.scoreboard.ScoreboardUpdater;
|
||||
import org.geysermc.connector.utils.MessageUtils;
|
||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.scoreboard.ObjectiveAction;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerScoreboardObjectivePacket;
|
||||
@ -54,7 +54,7 @@ public class JavaScoreboardObjectiveTranslator extends PacketTranslator<ServerSc
|
||||
switch (packet.getAction()) {
|
||||
case ADD:
|
||||
case UPDATE:
|
||||
objective.setDisplayName(MessageUtils.getBedrockMessage(packet.getDisplayName()))
|
||||
objective.setDisplayName(MessageTranslator.convertMessage(packet.getDisplayName().toString()))
|
||||
.setType(packet.getType().ordinal());
|
||||
break;
|
||||
case REMOVE:
|
||||
|
@ -37,7 +37,7 @@ import org.geysermc.connector.scoreboard.ScoreboardUpdater;
|
||||
import org.geysermc.connector.scoreboard.Team;
|
||||
import org.geysermc.connector.scoreboard.UpdateType;
|
||||
import org.geysermc.connector.utils.LanguageUtils;
|
||||
import org.geysermc.connector.utils.MessageUtils;
|
||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
@ -59,11 +59,11 @@ public class JavaTeamTranslator extends PacketTranslator<ServerTeamPacket> {
|
||||
switch (packet.getAction()) {
|
||||
case CREATE:
|
||||
scoreboard.registerNewTeam(packet.getTeamName(), toPlayerSet(packet.getPlayers()))
|
||||
.setName(MessageUtils.getBedrockMessage(packet.getDisplayName()))
|
||||
.setName(MessageTranslator.convertMessage(packet.getDisplayName().toString()))
|
||||
.setColor(packet.getColor())
|
||||
.setNameTagVisibility(packet.getNameTagVisibility())
|
||||
.setPrefix(MessageUtils.getTranslatedBedrockMessage(packet.getPrefix(), session.getLocale()))
|
||||
.setSuffix(MessageUtils.getTranslatedBedrockMessage(packet.getSuffix(), session.getLocale()));
|
||||
.setPrefix(MessageTranslator.convertMessage(packet.getPrefix().toString(), session.getLocale()))
|
||||
.setSuffix(MessageTranslator.convertMessage(packet.getSuffix().toString(), session.getLocale()));
|
||||
break;
|
||||
case UPDATE:
|
||||
if (team == null) {
|
||||
@ -74,11 +74,11 @@ public class JavaTeamTranslator extends PacketTranslator<ServerTeamPacket> {
|
||||
return;
|
||||
}
|
||||
|
||||
team.setName(MessageUtils.getBedrockMessage(packet.getDisplayName()))
|
||||
team.setName(MessageTranslator.convertMessage(packet.getDisplayName().toString()))
|
||||
.setColor(packet.getColor())
|
||||
.setNameTagVisibility(packet.getNameTagVisibility())
|
||||
.setPrefix(MessageUtils.getTranslatedBedrockMessage(packet.getPrefix(), session.getLocale()))
|
||||
.setSuffix(MessageUtils.getTranslatedBedrockMessage(packet.getSuffix(), session.getLocale()))
|
||||
.setPrefix(MessageTranslator.convertMessage(packet.getPrefix().toString(), session.getLocale()))
|
||||
.setSuffix(MessageTranslator.convertMessage(packet.getSuffix().toString(), session.getLocale()))
|
||||
.setUpdateType(UpdateType.UPDATE);
|
||||
break;
|
||||
case ADD_PLAYER:
|
||||
|
@ -25,7 +25,6 @@
|
||||
|
||||
package org.geysermc.connector.network.translators.java.window;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.message.MessageSerializer;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientCloseWindowPacket;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerOpenWindowPacket;
|
||||
import org.geysermc.connector.inventory.Inventory;
|
||||
@ -35,7 +34,7 @@ import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
|
||||
import org.geysermc.connector.utils.InventoryUtils;
|
||||
import org.geysermc.connector.utils.LocaleUtils;
|
||||
import org.geysermc.connector.utils.MessageUtils;
|
||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
||||
|
||||
@Translator(packet = ServerOpenWindowPacket.class)
|
||||
public class JavaOpenWindowTranslator extends PacketTranslator<ServerOpenWindowPacket> {
|
||||
@ -57,8 +56,7 @@ public class JavaOpenWindowTranslator extends PacketTranslator<ServerOpenWindowP
|
||||
return;
|
||||
}
|
||||
|
||||
String name = MessageUtils.getTranslatedBedrockMessage(MessageSerializer.fromString(packet.getName()),
|
||||
session.getLocale());
|
||||
String name = MessageTranslator.convertMessageLenient(packet.getName(), session.getLocale());
|
||||
|
||||
name = LocaleUtils.getLocaleString(name, session.getLocale());
|
||||
|
||||
|
@ -28,7 +28,7 @@ package org.geysermc.connector.network.translators.world.block.entity;
|
||||
import com.github.steveice10.opennbt.tag.builtin.*;
|
||||
import com.nukkitx.nbt.NbtMapBuilder;
|
||||
import org.geysermc.connector.network.translators.world.block.BlockStateValues;
|
||||
import org.geysermc.connector.utils.MessageUtils;
|
||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
||||
|
||||
@BlockEntity(name = "CommandBlock", regex = "command_block")
|
||||
public class CommandBlockBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState {
|
||||
@ -42,7 +42,7 @@ public class CommandBlockBlockEntityTranslator extends BlockEntityTranslator imp
|
||||
// Java and Bedrock values
|
||||
builder.put("conditionMet", ((ByteTag) tag.get("conditionMet")).getValue());
|
||||
builder.put("auto", ((ByteTag) tag.get("auto")).getValue());
|
||||
builder.put("CustomName", MessageUtils.getBedrockMessage(((StringTag) tag.get("CustomName")).getValue()));
|
||||
builder.put("CustomName", MessageTranslator.convertMessage(((StringTag) tag.get("CustomName")).getValue()));
|
||||
builder.put("powered", ((ByteTag) tag.get("powered")).getValue());
|
||||
builder.put("Command", ((StringTag) tag.get("Command")).getValue());
|
||||
builder.put("SuccessCount", ((IntTag) tag.get("SuccessCount")).getValue());
|
||||
|
@ -25,10 +25,9 @@
|
||||
|
||||
package org.geysermc.connector.network.translators.world.block.entity;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.message.MessageSerializer;
|
||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||
import com.nukkitx.nbt.NbtMapBuilder;
|
||||
import org.geysermc.connector.utils.MessageUtils;
|
||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
||||
import org.geysermc.connector.utils.SignUtils;
|
||||
|
||||
@BlockEntity(name = "Sign", regex = "sign")
|
||||
@ -36,12 +35,12 @@ public class SignBlockEntityTranslator extends BlockEntityTranslator {
|
||||
/**
|
||||
* Maps a color stored in a sign's Color tag to a Bedrock Edition formatting code.
|
||||
* <br>
|
||||
* The color names correspond to dye names, because of this we can't use {@link MessageUtils#getColor(String)}.
|
||||
* The color names correspond to dye names, because of this we can't use {@link MessageTranslator#getColor(String)}.
|
||||
*
|
||||
* @param javaColor The dye color stored in the sign's Color tag.
|
||||
* @return A Bedrock Edition formatting code for valid dye colors, otherwise an empty string.
|
||||
*/
|
||||
private static String getBedrockSignColor(String javaColor) {
|
||||
private String getBedrockSignColor(String javaColor) {
|
||||
String base = "\u00a7";
|
||||
switch (javaColor) {
|
||||
case "white":
|
||||
@ -100,7 +99,12 @@ public class SignBlockEntityTranslator extends BlockEntityTranslator {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int currentLine = i + 1;
|
||||
String signLine = getOrDefault(tag.getValue().get("Text" + currentLine), "");
|
||||
signLine = MessageUtils.getBedrockMessage(MessageSerializer.fromString(signLine));
|
||||
signLine = MessageTranslator.convertMessageLenient(signLine);
|
||||
|
||||
// Trim any trailing formatting codes
|
||||
if (signLine.length() > 2 && signLine.toCharArray()[signLine.length() - 2] == '\u00a7') {
|
||||
signLine = signLine.substring(0, signLine.length() - 2);
|
||||
}
|
||||
|
||||
// Check the character width on the sign to ensure there is no overflow that is usually hidden
|
||||
// to Java Edition clients but will appear to Bedrock clients
|
||||
@ -124,6 +128,6 @@ public class SignBlockEntityTranslator extends BlockEntityTranslator {
|
||||
signText.append("\n");
|
||||
}
|
||||
|
||||
builder.put("Text", MessageUtils.getBedrockMessage(MessageSerializer.fromString(signText.toString())));
|
||||
builder.put("Text", signText.toString());
|
||||
}
|
||||
}
|
||||
|
@ -159,7 +159,8 @@ public class FileUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the SHA256 hash of the resource pack file
|
||||
* Calculate the SHA256 hash of a file
|
||||
*
|
||||
* @param file File to calculate the hash for
|
||||
* @return A byte[] representation of the hash
|
||||
*/
|
||||
@ -175,6 +176,24 @@ public class FileUtils {
|
||||
return sha256;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the SHA1 hash of a file
|
||||
*
|
||||
* @param file File to calculate the hash for
|
||||
* @return A byte[] representation of the hash
|
||||
*/
|
||||
public static byte[] calculateSHA1(File file) {
|
||||
byte[] sha1;
|
||||
|
||||
try {
|
||||
sha1 = MessageDigest.getInstance("SHA-1").digest(Files.readAllBytes(file.toPath()));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Could not calculate pack hash", e);
|
||||
}
|
||||
|
||||
return sha1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stored reflection data for a given path
|
||||
*
|
||||
|
@ -47,7 +47,7 @@ public class LocaleUtils {
|
||||
|
||||
private static final Map<String, Asset> ASSET_MAP = new HashMap<>();
|
||||
|
||||
private static String smallestURL = "";
|
||||
private static VersionDownload clientJarInfo;
|
||||
|
||||
static {
|
||||
// Create the locales folder
|
||||
@ -87,9 +87,8 @@ public class LocaleUtils {
|
||||
|
||||
// Get the client jar for use when downloading the en_us locale
|
||||
GeyserConnector.getInstance().getLogger().debug(GeyserConnector.JSON_MAPPER.writeValueAsString(versionInfo.getDownloads()));
|
||||
VersionDownload download = versionInfo.getDownloads().get("client");
|
||||
GeyserConnector.getInstance().getLogger().debug(GeyserConnector.JSON_MAPPER.writeValueAsString(download));
|
||||
smallestURL = download.getUrl();
|
||||
clientJarInfo = versionInfo.getDownloads().get("client");
|
||||
GeyserConnector.getInstance().getLogger().debug(GeyserConnector.JSON_MAPPER.writeValueAsString(clientJarInfo));
|
||||
|
||||
// Get the assets list
|
||||
JsonNode assets = GeyserConnector.JSON_MAPPER.readTree(WebUtils.getBody(versionInfo.getAssetIndex().getUrl())).get("objects");
|
||||
@ -136,9 +135,29 @@ public class LocaleUtils {
|
||||
|
||||
// Check if we have already downloaded the locale file
|
||||
if (localeFile.exists()) {
|
||||
GeyserConnector.getInstance().getLogger().debug("Locale already downloaded: " + locale);
|
||||
String curHash = "";
|
||||
String targetHash = "";
|
||||
|
||||
if (locale.equals("en_us")) {
|
||||
try {
|
||||
Path hashFile = localeFile.getParentFile().toPath().resolve("en_us.hash");
|
||||
if (hashFile.toFile().exists()) {
|
||||
curHash = String.join("", Files.readAllLines(hashFile));
|
||||
}
|
||||
} catch (IOException ignored) { }
|
||||
targetHash = clientJarInfo.getSha1();
|
||||
} else {
|
||||
curHash = byteArrayToHexString(FileUtils.calculateSHA1(localeFile));
|
||||
targetHash = ASSET_MAP.get("minecraft/lang/" + locale + ".json").getHash();
|
||||
}
|
||||
|
||||
if (!curHash.equals(targetHash)) {
|
||||
GeyserConnector.getInstance().getLogger().debug("Locale out of date; re-downloading: " + locale);
|
||||
} else {
|
||||
GeyserConnector.getInstance().getLogger().debug("Locale already downloaded and up-to date: " + locale);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Create the en_us locale
|
||||
if (locale.equals("en_us")) {
|
||||
@ -202,11 +221,11 @@ public class LocaleUtils {
|
||||
try {
|
||||
// Let the user know we are downloading the JAR
|
||||
GeyserConnector.getInstance().getLogger().info(LanguageUtils.getLocaleStringLog("geyser.locale.download.en_us"));
|
||||
GeyserConnector.getInstance().getLogger().debug("Download URL: " + smallestURL);
|
||||
GeyserConnector.getInstance().getLogger().debug("Download URL: " + clientJarInfo.getUrl());
|
||||
|
||||
// Download the smallest JAR (client or server)
|
||||
Path tmpFilePath = GeyserConnector.getInstance().getBootstrap().getConfigFolder().resolve("tmp_locale.jar");
|
||||
WebUtils.downloadFile(smallestURL, tmpFilePath.toString());
|
||||
WebUtils.downloadFile(clientJarInfo.getUrl(), tmpFilePath.toString());
|
||||
|
||||
// Load in the JAR as a zip and extract the file
|
||||
ZipFile localeJar = new ZipFile(tmpFilePath.toString());
|
||||
@ -227,6 +246,9 @@ public class LocaleUtils {
|
||||
fileStream.close();
|
||||
localeJar.close();
|
||||
|
||||
// Store the latest jar hash
|
||||
FileUtils.writeFile(localeFile.getParentFile().toPath().resolve("en_us.hash").toString(), clientJarInfo.getSha1().toCharArray());
|
||||
|
||||
// Delete the nolonger needed client/server jar
|
||||
Files.delete(tmpFilePath);
|
||||
} catch (Exception e) {
|
||||
@ -255,6 +277,20 @@ public class LocaleUtils {
|
||||
return localeStrings.getOrDefault(messageText, messageText);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a byte array into a hex string
|
||||
*
|
||||
* @param b Byte array to convert
|
||||
* @return The hex representation of the given byte array
|
||||
*/
|
||||
private static String byteArrayToHexString(byte[] b) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (byte value : b) {
|
||||
result.append(Integer.toString((value & 0xff) + 0x100, 16).substring(1));
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
public static void init() {
|
||||
// no-op
|
||||
}
|
||||
|
@ -1,494 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.connector.utils;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.scoreboard.TeamColor;
|
||||
import com.github.steveice10.mc.protocol.data.message.Message;
|
||||
import com.github.steveice10.mc.protocol.data.message.MessageSerializer;
|
||||
import com.github.steveice10.mc.protocol.data.message.TextMessage;
|
||||
import com.github.steveice10.mc.protocol.data.message.TranslationMessage;
|
||||
import com.github.steveice10.mc.protocol.data.message.style.ChatColor;
|
||||
import com.github.steveice10.mc.protocol.data.message.style.ChatFormat;
|
||||
import com.github.steveice10.mc.protocol.data.message.style.MessageStyle;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class MessageUtils {
|
||||
|
||||
private static final Map<String, Integer> COLORS = new HashMap<>();
|
||||
private static final Map<TeamColor, String> TEAM_COLORS = new HashMap<>();
|
||||
|
||||
static {
|
||||
COLORS.put(ChatColor.BLACK, 0x000000);
|
||||
COLORS.put(ChatColor.DARK_BLUE, 0x0000aa);
|
||||
COLORS.put(ChatColor.DARK_GREEN, 0x00aa00);
|
||||
COLORS.put(ChatColor.DARK_AQUA, 0x00aaaa);
|
||||
COLORS.put(ChatColor.DARK_RED, 0xaa0000);
|
||||
COLORS.put(ChatColor.DARK_PURPLE, 0xaa00aa);
|
||||
COLORS.put(ChatColor.GOLD, 0xffaa00);
|
||||
COLORS.put(ChatColor.GRAY, 0xaaaaaa);
|
||||
COLORS.put(ChatColor.DARK_GRAY, 0x555555);
|
||||
COLORS.put(ChatColor.BLUE, 0x5555ff);
|
||||
COLORS.put(ChatColor.GREEN, 0x55ff55);
|
||||
COLORS.put(ChatColor.AQUA, 0x55ffff);
|
||||
COLORS.put(ChatColor.RED, 0xff5555);
|
||||
COLORS.put(ChatColor.LIGHT_PURPLE, 0xff55ff);
|
||||
COLORS.put(ChatColor.YELLOW, 0xffff55);
|
||||
COLORS.put(ChatColor.WHITE, 0xffffff);
|
||||
|
||||
TEAM_COLORS.put(TeamColor.BLACK, getColor(ChatColor.BLACK));
|
||||
TEAM_COLORS.put(TeamColor.DARK_BLUE, getColor(ChatColor.DARK_BLUE));
|
||||
TEAM_COLORS.put(TeamColor.DARK_GREEN, getColor(ChatColor.DARK_GREEN));
|
||||
TEAM_COLORS.put(TeamColor.DARK_AQUA, getColor(ChatColor.DARK_AQUA));
|
||||
TEAM_COLORS.put(TeamColor.DARK_RED, getColor(ChatColor.DARK_RED));
|
||||
TEAM_COLORS.put(TeamColor.DARK_PURPLE, getColor(ChatColor.DARK_PURPLE));
|
||||
TEAM_COLORS.put(TeamColor.GOLD, getColor(ChatColor.GOLD));
|
||||
TEAM_COLORS.put(TeamColor.GRAY, getColor(ChatColor.GRAY));
|
||||
TEAM_COLORS.put(TeamColor.DARK_GRAY, getColor(ChatColor.DARK_GRAY));
|
||||
TEAM_COLORS.put(TeamColor.BLUE, getColor(ChatColor.BLUE));
|
||||
TEAM_COLORS.put(TeamColor.GREEN, getColor(ChatColor.GREEN));
|
||||
TEAM_COLORS.put(TeamColor.AQUA, getColor(ChatColor.AQUA));
|
||||
TEAM_COLORS.put(TeamColor.RED, getColor(ChatColor.RED));
|
||||
TEAM_COLORS.put(TeamColor.LIGHT_PURPLE, getColor(ChatColor.LIGHT_PURPLE));
|
||||
TEAM_COLORS.put(TeamColor.YELLOW, getColor(ChatColor.YELLOW));
|
||||
TEAM_COLORS.put(TeamColor.WHITE, getColor(ChatColor.WHITE));
|
||||
TEAM_COLORS.put(TeamColor.OBFUSCATED, getFormat(Collections.singletonList(ChatFormat.OBFUSCATED)));
|
||||
TEAM_COLORS.put(TeamColor.BOLD, getFormat(Collections.singletonList(ChatFormat.BOLD)));
|
||||
TEAM_COLORS.put(TeamColor.STRIKETHROUGH, getFormat(Collections.singletonList(ChatFormat.STRIKETHROUGH)));
|
||||
TEAM_COLORS.put(TeamColor.ITALIC, getFormat(Collections.singletonList(ChatFormat.ITALIC)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively parse each message from a list for usage in a {@link TranslationMessage}
|
||||
*
|
||||
* @param messages A {@link List} of {@link Message} to parse
|
||||
* @param locale A locale loaded to get the message for
|
||||
* @param parent A {@link Message} to use as the parent (can be null)
|
||||
* @return the translation parameters
|
||||
*/
|
||||
public static List<String> getTranslationParams(List<Message> messages, String locale, Message parent) {
|
||||
List<String> strings = new ArrayList<>();
|
||||
for (Message message : messages) {
|
||||
message = fixMessageStyle(message, parent);
|
||||
|
||||
if (message instanceof TranslationMessage) {
|
||||
TranslationMessage translation = (TranslationMessage) message;
|
||||
|
||||
if (locale == null) {
|
||||
String builder = "%" + translation.getKey();
|
||||
strings.add(builder);
|
||||
}
|
||||
|
||||
// Collect all params and add format corrections to the end of them
|
||||
List<String> furtherParams = new ArrayList<>();
|
||||
for (String param : getTranslationParams(translation.getWith(), locale, message)) {
|
||||
String newParam = param;
|
||||
if (parent.getStyle().getFormats().size() != 0) {
|
||||
newParam += getFormat(parent.getStyle().getFormats());
|
||||
}
|
||||
if (parent.getStyle().getColor() != ChatColor.NONE) {
|
||||
newParam += getColor(parent.getStyle().getColor());
|
||||
}
|
||||
|
||||
furtherParams.add(newParam);
|
||||
}
|
||||
|
||||
if (locale != null) {
|
||||
String builder = getFormat(message.getStyle().getFormats()) +
|
||||
getColor(message.getStyle().getColor());
|
||||
builder += insertParams(LocaleUtils.getLocaleString(translation.getKey(), locale), furtherParams);
|
||||
strings.add(builder);
|
||||
} else {
|
||||
String format = getFormat(message.getStyle().getFormats()) +
|
||||
getColor(message.getStyle().getColor());
|
||||
for (String param : furtherParams) {
|
||||
strings.add(format + param);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
String builder = getFormat(message.getStyle().getFormats()) +
|
||||
getColor(message.getStyle().getColor());
|
||||
builder += getTranslatedBedrockMessage(message, locale, false, parent);
|
||||
strings.add(builder);
|
||||
}
|
||||
}
|
||||
|
||||
return strings;
|
||||
}
|
||||
|
||||
public static String getTranslatedBedrockMessage(Message message, String locale) {
|
||||
return getTranslatedBedrockMessage(message, locale, true);
|
||||
}
|
||||
|
||||
public static String getTranslatedBedrockMessage(Message message, String locale, boolean shouldTranslate) {
|
||||
return getTranslatedBedrockMessage(message, locale, shouldTranslate, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate a given {@link TranslationMessage} to the given locale
|
||||
*
|
||||
* @param message The {@link Message} to send
|
||||
* @param locale the locale
|
||||
* @param shouldTranslate if the message should be translated
|
||||
* @param parent the parent message
|
||||
* @return the given translation message translated from the given locale
|
||||
*/
|
||||
public static String getTranslatedBedrockMessage(Message message, String locale, boolean shouldTranslate, Message parent) {
|
||||
JsonParser parser = new JsonParser();
|
||||
if (isMessage(message.toString())) {
|
||||
JsonObject object = parser.parse(message.toString()).getAsJsonObject();
|
||||
message = MessageSerializer.fromJson(object);
|
||||
}
|
||||
|
||||
message = fixMessageStyle(message, parent);
|
||||
|
||||
String messageText = (message instanceof TranslationMessage) ? ((TranslationMessage) message).getKey() : ((TextMessage) message).getText();
|
||||
if (locale != null && shouldTranslate) {
|
||||
messageText = LocaleUtils.getLocaleString(messageText, locale);
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(getFormat(message.getStyle().getFormats()));
|
||||
builder.append(getColor(message.getStyle().getColor()));
|
||||
builder.append(messageText);
|
||||
|
||||
for (Message msg : message.getExtra()) {
|
||||
builder.append(getFormat(msg.getStyle().getFormats()));
|
||||
builder.append(getColor(msg.getStyle().getColor()));
|
||||
if (!(msg.toString() == null)) {
|
||||
boolean isTranslationMessage = (msg instanceof TranslationMessage);
|
||||
String extraText = "";
|
||||
|
||||
if (isTranslationMessage) {
|
||||
List<String> paramsTranslated = getTranslationParams(((TranslationMessage) msg).getWith(), locale, message);
|
||||
extraText = insertParams(getTranslatedBedrockMessage(msg, locale, isTranslationMessage, message), paramsTranslated);
|
||||
} else {
|
||||
extraText = getTranslatedBedrockMessage(msg, locale, isTranslationMessage, message);
|
||||
}
|
||||
|
||||
builder.append(extraText);
|
||||
builder.append("\u00a7r");
|
||||
}
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* If the passed {@link Message} color or format are empty then copy from parent
|
||||
*
|
||||
* @param message {@link Message} to update
|
||||
* @param parent Parent {@link Message} for style
|
||||
* @return The updated {@link Message}
|
||||
*/
|
||||
private static Message fixMessageStyle(Message message, Message parent) {
|
||||
if (parent == null) {
|
||||
return message;
|
||||
}
|
||||
MessageStyle.Builder styleBuilder = message.getStyle().toBuilder();
|
||||
|
||||
// Copy color from parent
|
||||
if (message.getStyle().getColor() == ChatColor.NONE) {
|
||||
styleBuilder.color(parent.getStyle().getColor());
|
||||
}
|
||||
|
||||
// Copy formatting from parent
|
||||
if (message.getStyle().getFormats().size() == 0) {
|
||||
styleBuilder.formats(parent.getStyle().getFormats());
|
||||
}
|
||||
|
||||
return message.toBuilder().style(styleBuilder.build()).build();
|
||||
}
|
||||
|
||||
public static String getBedrockMessage(Message message) {
|
||||
if (isMessage(((TextMessage) message).getText())) {
|
||||
return getBedrockMessage(((TextMessage) message).getText());
|
||||
} else {
|
||||
return getBedrockMessage(MessageSerializer.toJsonString(message));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the message is valid JSON in case it's plaintext. Works around GsonComponentSeraializer not using lenient mode.
|
||||
* See https://wiki.vg/Chat for messages sent in lenient mode, and for a description on leniency.
|
||||
*
|
||||
* @param message Potentially lenient JSON message
|
||||
* @return Bedrock formatted message
|
||||
*/
|
||||
public static String getBedrockMessageLenient(String message) {
|
||||
if (isMessage(message)) {
|
||||
return getBedrockMessage(message);
|
||||
} else {
|
||||
final JsonObject obj = new JsonObject();
|
||||
obj.addProperty("text", message);
|
||||
return getBedrockMessage(obj.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public static String getBedrockMessage(String message) {
|
||||
Component component = phraseJavaMessage(message);
|
||||
return LegacyComponentSerializer.legacySection().serialize(component);
|
||||
}
|
||||
|
||||
public static Component phraseJavaMessage(String message) {
|
||||
return GsonComponentSerializer.gson().deserialize(message);
|
||||
}
|
||||
|
||||
public static String getJavaMessage(String message) {
|
||||
Component component = LegacyComponentSerializer.legacySection().deserialize(message);
|
||||
return GsonComponentSerializer.gson().serialize(component);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the given parameters into the given message both in sequence and as requested
|
||||
*
|
||||
* @param message Message containing possible parameter replacement strings
|
||||
* @param params A list of parameter strings
|
||||
* @return Parsed message with all params inserted as needed
|
||||
*/
|
||||
public static String insertParams(String message, List<String> params) {
|
||||
String newMessage = message;
|
||||
|
||||
Pattern p = Pattern.compile("%([1-9])\\$s");
|
||||
Matcher m = p.matcher(message);
|
||||
while (m.find()) {
|
||||
try {
|
||||
newMessage = newMessage.replaceFirst("%" + m.group(1) + "\\$s", params.get(Integer.parseInt(m.group(1)) - 1));
|
||||
} catch (Exception e) {
|
||||
// Couldn't find the param to replace
|
||||
}
|
||||
}
|
||||
|
||||
for (String text : params) {
|
||||
newMessage = newMessage.replaceFirst("%s", text.replaceAll("%s", "%r"));
|
||||
}
|
||||
|
||||
newMessage = newMessage.replaceAll("%r", "MISSING!");
|
||||
|
||||
return newMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a ChatColor into a string for inserting into messages
|
||||
*
|
||||
* @param color ChatColor to convert
|
||||
* @return The converted color string
|
||||
*/
|
||||
private static String getColor(String color) {
|
||||
String base = "\u00a7";
|
||||
switch (color) {
|
||||
case ChatColor.BLACK:
|
||||
base += "0";
|
||||
break;
|
||||
case ChatColor.DARK_BLUE:
|
||||
base += "1";
|
||||
break;
|
||||
case ChatColor.DARK_GREEN:
|
||||
base += "2";
|
||||
break;
|
||||
case ChatColor.DARK_AQUA:
|
||||
base += "3";
|
||||
break;
|
||||
case ChatColor.DARK_RED:
|
||||
base += "4";
|
||||
break;
|
||||
case ChatColor.DARK_PURPLE:
|
||||
base += "5";
|
||||
break;
|
||||
case ChatColor.GOLD:
|
||||
base += "6";
|
||||
break;
|
||||
case ChatColor.GRAY:
|
||||
base += "7";
|
||||
break;
|
||||
case ChatColor.DARK_GRAY:
|
||||
base += "8";
|
||||
break;
|
||||
case ChatColor.BLUE:
|
||||
base += "9";
|
||||
break;
|
||||
case ChatColor.GREEN:
|
||||
base += "a";
|
||||
break;
|
||||
case ChatColor.AQUA:
|
||||
base += "b";
|
||||
break;
|
||||
case ChatColor.RED:
|
||||
base += "c";
|
||||
break;
|
||||
case ChatColor.LIGHT_PURPLE:
|
||||
base += "d";
|
||||
break;
|
||||
case ChatColor.YELLOW:
|
||||
base += "e";
|
||||
break;
|
||||
case ChatColor.WHITE:
|
||||
base += "f";
|
||||
break;
|
||||
case ChatColor.RESET:
|
||||
//case NONE:
|
||||
base += "r";
|
||||
break;
|
||||
case "": // To stop recursion
|
||||
return "";
|
||||
default:
|
||||
return getClosestColor(color);
|
||||
}
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on https://github.com/ViaVersion/ViaBackwards/blob/master/core/src/main/java/nl/matsv/viabackwards/protocol/protocol1_15_2to1_16/chat/TranslatableRewriter1_16.java
|
||||
*
|
||||
* @param color A color string
|
||||
* @return The closest color to that string
|
||||
*/
|
||||
private static String getClosestColor(String color) {
|
||||
if (!color.startsWith("#")) {
|
||||
return "";
|
||||
}
|
||||
|
||||
int rgb = Integer.parseInt(color.substring(1), 16);
|
||||
int r = (rgb >> 16) & 0xFF;
|
||||
int g = (rgb >> 8) & 0xFF;
|
||||
int b = rgb & 0xFF;
|
||||
|
||||
String closest = null;
|
||||
int smallestDiff = 0;
|
||||
|
||||
for (Map.Entry<String, Integer> testColor : COLORS.entrySet()) {
|
||||
if (testColor.getValue() == rgb) {
|
||||
closest = testColor.getKey();
|
||||
break;
|
||||
}
|
||||
|
||||
int testR = (testColor.getValue() >> 16) & 0xFF;
|
||||
int testG = (testColor.getValue() >> 8) & 0xFF;
|
||||
int testB = testColor.getValue() & 0xFF;
|
||||
|
||||
// Check by the greatest diff of the 3 values
|
||||
int rAverage = (testR + r) / 2;
|
||||
int rDiff = testR - r;
|
||||
int gDiff = testG - g;
|
||||
int bDiff = testB - b;
|
||||
int diff = ((2 + (rAverage >> 8)) * rDiff * rDiff)
|
||||
+ (4 * gDiff * gDiff)
|
||||
+ ((2 + ((255 - rAverage) >> 8)) * bDiff * bDiff);
|
||||
if (closest == null || diff < smallestDiff) {
|
||||
closest = testColor.getKey();
|
||||
smallestDiff = diff;
|
||||
}
|
||||
}
|
||||
|
||||
return getColor(closest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a list of ChatFormats into a string for inserting into messages
|
||||
*
|
||||
* @param formats ChatFormats to convert
|
||||
* @return The converted chat formatting string
|
||||
*/
|
||||
private static String getFormat(List<ChatFormat> formats) {
|
||||
StringBuilder str = new StringBuilder();
|
||||
for (ChatFormat cf : formats) {
|
||||
String base = "\u00a7";
|
||||
switch (cf) {
|
||||
case OBFUSCATED:
|
||||
base += "k";
|
||||
break;
|
||||
case BOLD:
|
||||
base += "l";
|
||||
break;
|
||||
case STRIKETHROUGH:
|
||||
base += "m";
|
||||
break;
|
||||
case UNDERLINED:
|
||||
base += "n";
|
||||
break;
|
||||
case ITALIC:
|
||||
base += "o";
|
||||
break;
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
|
||||
str.append(base);
|
||||
}
|
||||
|
||||
return str.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given text string is a json message
|
||||
*
|
||||
* @param text String to test
|
||||
* @return True if its a valid message json string, false if not
|
||||
*/
|
||||
public static boolean isMessage(String text) {
|
||||
JsonParser parser = new JsonParser();
|
||||
try {
|
||||
JsonObject object = parser.parse(text).getAsJsonObject();
|
||||
try {
|
||||
MessageSerializer.fromJson(object);
|
||||
} catch (Exception ex) {
|
||||
return false;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static String toChatColor(TeamColor teamColor) {
|
||||
return TEAM_COLORS.getOrDefault(teamColor, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given message is over 256 characters (Java edition server chat limit) and sends a message to the user if it is
|
||||
*
|
||||
* @param message Message to check
|
||||
* @param session GeyserSession for the user
|
||||
* @return True if the message is too long, false if not
|
||||
*/
|
||||
public static boolean isTooLong(String message, GeyserSession session) {
|
||||
if (message.length() > 256) {
|
||||
session.sendMessage(LanguageUtils.getPlayerLocaleString("geyser.chat.too_long", session.getLocale(), message.length()));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.connector.network.translators.chat;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class MessageTranslatorTest {
|
||||
|
||||
private Map<String, String> messages = new HashMap<>();
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
messages.put("{\"text\":\"\",\"extra\":[{\"text\":\"DoctorMad9952 joined the game\",\"color\":\"yellow\"}]}",
|
||||
"§eDoctorMad9952 joined the game");
|
||||
|
||||
messages.put("{\"text\":\"\",\"extra\":[\"Plugins (3): \",{\"text\":\"WorldEdit\",\"color\":\"green\"},{\"text\":\", \",\"color\":\"white\"},{\"text\":\"ViaVersion\",\"color\":\"green\"},{\"text\":\", \",\"color\":\"white\"},{\"text\":\"Geyser-Spigot\",\"color\":\"green\"}]}",
|
||||
"Plugins (3): §aWorldEdit§f, §aViaVersion§f, §aGeyser-Spigot");
|
||||
|
||||
// RGB downgrade test
|
||||
messages.put("{\"extra\":[{\"text\":\" \"},{\"color\":\"gold\",\"text\":\"The \"},{\"color\":\"#E14248\",\"obfuscated\":true,\"text\":\"||\"},{\"color\":\"#3AA9FF\",\"bold\":true,\"text\":\"CubeCraft\"},{\"color\":\"#E14248\",\"obfuscated\":true,\"text\":\"||\"},{\"color\":\"gold\",\"text\":\" Network \"},{\"color\":\"green\",\"text\":\"[1.8/1.9+]\\n \"},{\"color\":\"#f5e342\",\"text\":\"✦ \"},{\"color\":\"#b042f5\",\"bold\":true,\"text\":\"N\"},{\"color\":\"#c142f5\",\"bold\":true,\"text\":\"E\"},{\"color\":\"#d342f5\",\"bold\":true,\"text\":\"W\"},{\"color\":\"#e442f5\",\"bold\":true,\"text\":\":\"},{\"color\":\"#f542f5\",\"bold\":true,\"text\":\" \"},{\"color\":\"#bcf542\",\"bold\":true,\"text\":\"A\"},{\"color\":\"#acee3f\",\"bold\":true,\"text\":\"M\"},{\"color\":\"#9ce73c\",\"bold\":true,\"text\":\"O\"},{\"color\":\"#8ce039\",\"bold\":true,\"text\":\"N\"},{\"color\":\"#7cd936\",\"bold\":true,\"text\":\"G\"},{\"color\":\"#6cd233\",\"bold\":true,\"text\":\" \"},{\"color\":\"#5ccb30\",\"bold\":true,\"text\":\"S\"},{\"color\":\"#4cc42d\",\"bold\":true,\"text\":\"L\"},{\"color\":\"#3cbd2a\",\"bold\":true,\"text\":\"I\"},{\"color\":\"#2cb627\",\"bold\":true,\"text\":\"M\"},{\"color\":\"#1caf24\",\"bold\":true,\"text\":\"E\"},{\"color\":\"#0ca821\",\"bold\":true,\"text\":\"S\"},{\"color\":\"#f5e342\",\"text\":\" \"},{\"color\":\"#6d7c87\",\"text\":\"(kinda sus) \"},{\"color\":\"#f5e342\",\"text\":\"✦\"}],\"text\":\"\"}",
|
||||
" §6The §c§k||§r§3§lCubeCraft§r§c§k||§r§6 Network §a[1.8/1.9+]\n" +
|
||||
" §e✦ §d§lN§r§d§lE§r§d§lW§r§d§l:§r§d§l §r§e§lA§r§e§lM§r§a§lO§r§a§lN§r§a§lG§r§a§l §r§a§lS§r§a§lL§r§2§lI§r§2§lM§r§2§lE§r§2§lS§r§e §8(kinda sus) §e✦");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertMessage() {
|
||||
for (Map.Entry<String, String> entry : messages.entrySet()) {
|
||||
String bedrockMessage = MessageTranslator.convertMessage(entry.getKey(), "en_US");
|
||||
Assert.assertEquals("Translation of messages is incorrect", bedrockMessage, entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertMessageLenient() {
|
||||
Assert.assertEquals("All newline message is not handled properly", "\n\n\n\n", MessageTranslator.convertMessageLenient("\n\n\n\n"));
|
||||
Assert.assertEquals("Empty message is not handled properly", "", MessageTranslator.convertMessageLenient(""));
|
||||
Assert.assertEquals("Reset before message is not handled properly", "§r§eGame Selector", MessageTranslator.convertMessageLenient("§r§eGame Selector"));
|
||||
}
|
||||
}
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren