1.20.3: Update chat parser to parse new NBT format

This commit is contained in:
ReinforceZwei 2024-02-04 18:14:09 +08:00
parent 975aab88e3
commit a9f1ad4433
4 changed files with 157 additions and 57 deletions

View file

@ -6,6 +6,7 @@ using MinecraftClient.Inventory;
using MinecraftClient.Inventory.ItemPalettes; using MinecraftClient.Inventory.ItemPalettes;
using MinecraftClient.Mapping; using MinecraftClient.Mapping;
using MinecraftClient.Mapping.EntityPalettes; using MinecraftClient.Mapping.EntityPalettes;
using MinecraftClient.Protocol.Message;
namespace MinecraftClient.Protocol.Handlers namespace MinecraftClient.Protocol.Handlers
{ {
@ -561,7 +562,7 @@ namespace MinecraftClient.Protocol.Handlers
return nbtData; return nbtData;
} }
var nextId = cache.Peek(); var nextId = cache.Dequeue();
if (protocolversion < Protocol18Handler.MC_1_20_2_Version) if (protocolversion < Protocol18Handler.MC_1_20_2_Version)
{ {
if (nextId is 10) // TAG_Compound if (nextId is 10) // TAG_Compound
@ -596,7 +597,7 @@ namespace MinecraftClient.Protocol.Handlers
} }
// Read TAG_Compound // Read TAG_Compound
ReadNextByte(cache); //ReadNextByte(cache);
} }
} }
@ -1059,6 +1060,23 @@ namespace MinecraftClient.Protocol.Handlers
maximumNumberOfTradeUses, xp, specialPrice, priceMultiplier, demand); maximumNumberOfTradeUses, xp, specialPrice, priceMultiplier, demand);
} }
public string ReadNextChat(Queue<byte> cache)
{
if (protocolversion >= Protocol18Handler.MC_1_20_4_Version)
{
// Read as NBT
var r = ReadNextNbt(cache);
var msg = ChatParser.ParseText(r);
return msg;
}
else
{
// Read as String
var json = ReadNextString(cache);
return ChatParser.ParseText(json);
}
}
/// <summary> /// <summary>
/// Build an uncompressed Named Binary Tag blob for sending over the network /// Build an uncompressed Named Binary Tag blob for sending over the network
/// </summary> /// </summary>

View file

@ -71,7 +71,7 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c
44 => new ParserResource(dataTypes, packetData), 44 => new ParserResource(dataTypes, packetData),
_ => new ParserEmpty(dataTypes, packetData), _ => new ParserEmpty(dataTypes, packetData),
}; };
else // 1.19.4+ else if (protocolVersion <= Protocol18Handler.MC_1_20_2_Version)// 1.19.4 - 1.20.2
parser = parserId switch parser = parserId switch
{ {
1 => new ParserFloat(dataTypes, packetData), 1 => new ParserFloat(dataTypes, packetData),
@ -94,11 +94,34 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c
44 => new ParserResource(dataTypes, packetData), 44 => new ParserResource(dataTypes, packetData),
_ => new ParserEmpty(dataTypes, packetData), _ => new ParserEmpty(dataTypes, packetData),
}; };
else // 1.20.3+
parser = parserId switch
{
1 => new ParserFloat(dataTypes, packetData),
2 => new ParserDouble(dataTypes, packetData),
3 => new ParserInteger(dataTypes, packetData),
4 => new ParserLong(dataTypes, packetData),
5 => new ParserString(dataTypes, packetData),
6 => new ParserEntity(dataTypes, packetData),
8 => new ParserBlockPos(dataTypes, packetData),
9 => new ParserColumnPos(dataTypes, packetData),
10 => new ParserVec3(dataTypes, packetData),
11 => new ParserVec2(dataTypes, packetData),
18 => new ParserMessage(dataTypes, packetData),
27 => new ParserRotation(dataTypes, packetData),
30 => new ParserScoreHolder(dataTypes, packetData),
41 => new ParserTime(dataTypes, packetData),
42 => new ParserResourceOrTag(dataTypes, packetData),
43 => new ParserResourceOrTag(dataTypes, packetData),
44 => new ParserResource(dataTypes, packetData),
45 => new ParserResource(dataTypes, packetData),
_ => new ParserEmpty(dataTypes, packetData),
};
} }
string? suggestionsType = ((flags & 0x10) == 0x10) ? dataTypes.ReadNextString(packetData) : null; string? suggestionsType = ((flags & 0x10) == 0x10) ? dataTypes.ReadNextString(packetData) : null;
Nodes[i] = new(flags, childs, redirectNode, name, parser, suggestionsType); Nodes[i] = new(flags, childs, redirectNode, name, parser, suggestionsType, parserId);
} }
RootIdx = dataTypes.ReadNextVarInt(packetData); RootIdx = dataTypes.ReadNextVarInt(packetData);
@ -174,6 +197,7 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c
public string? Name; public string? Name;
public Parser? Paser; public Parser? Paser;
public string? SuggestionsType; public string? SuggestionsType;
public int ParserId; // Added for easy debug
public CommandNode(byte Flags, public CommandNode(byte Flags,
@ -181,7 +205,8 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c
int RedirectNode = -1, int RedirectNode = -1,
string? Name = null, string? Name = null,
Parser? Paser = null, Parser? Paser = null,
string? SuggestionsType = null) string? SuggestionsType = null,
int parserId = -1)
{ {
this.Flags = Flags; this.Flags = Flags;
this.Clildren = Clildren; this.Clildren = Clildren;
@ -189,6 +214,7 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c
this.Name = Name; this.Name = Name;
this.Paser = Paser; this.Paser = Paser;
this.SuggestionsType = SuggestionsType; this.SuggestionsType = SuggestionsType;
ParserId = parserId;
} }
} }

View file

@ -362,8 +362,8 @@ namespace MinecraftClient.Protocol.Handlers
/// <returns>TRUE if the packet was processed, FALSE if ignored or unknown</returns> /// <returns>TRUE if the packet was processed, FALSE if ignored or unknown</returns>
internal bool HandlePacket(int packetId, Queue<byte> packetData) internal bool HandlePacket(int packetId, Queue<byte> packetData)
{ {
try //try
{ //{
switch (currentState) switch (currentState)
{ {
// https://wiki.vg/Protocol#Login // https://wiki.vg/Protocol#Login
@ -398,7 +398,7 @@ namespace MinecraftClient.Protocol.Handlers
{ {
case ConfigurationPacketTypesIn.Disconnect: case ConfigurationPacketTypesIn.Disconnect:
handler.OnConnectionLost(ChatBot.DisconnectReason.InGameKick, handler.OnConnectionLost(ChatBot.DisconnectReason.InGameKick,
ChatParser.ParseText(dataTypes.ReadNextString(packetData))); dataTypes.ReadNextChat(packetData));
return false; return false;
case ConfigurationPacketTypesIn.FinishConfiguration: case ConfigurationPacketTypesIn.FinishConfiguration:
@ -446,22 +446,23 @@ namespace MinecraftClient.Protocol.Handlers
default: default:
return true; return true;
} }
} //}
catch (Exception innerException) //catch (Exception innerException)
{ //{
if (innerException is ThreadAbortException || innerException is SocketException || // //throw;
innerException.InnerException is SocketException) // if (innerException is ThreadAbortException || innerException is SocketException ||
throw; //Thread abort or Connection lost rather than invalid data // innerException.InnerException is SocketException)
// throw; //Thread abort or Connection lost rather than invalid data
throw new System.IO.InvalidDataException( // throw new System.IO.InvalidDataException(
string.Format(Translations.exception_packet_process, // string.Format(Translations.exception_packet_process,
packetPalette.GetIncomingTypeById(packetId), // packetPalette.GetIncomingTypeById(packetId),
packetId, // packetId,
protocolVersion, // protocolVersion,
currentState == CurrentState.Login, // currentState == CurrentState.Login,
innerException.GetType()), // innerException.GetType()),
innerException); // innerException);
} //}
return true; return true;
} }
@ -480,7 +481,7 @@ namespace MinecraftClient.Protocol.Handlers
{ {
dataTypes.ReadNextBool(packetData); // Forced dataTypes.ReadNextBool(packetData); // Forced
if (dataTypes.ReadNextBool(packetData)) // Has Prompt Message if (dataTypes.ReadNextBool(packetData)) // Has Prompt Message
dataTypes.SkipNextString(packetData); // Prompt Message dataTypes.ReadNextChat(packetData); // Prompt Message
} }
// Some server plugins may send invalid resource packs to probe the client and we need to ignore them (issue #1056) // Some server plugins may send invalid resource packs to probe the client and we need to ignore them (issue #1056)
@ -918,7 +919,7 @@ namespace MinecraftClient.Protocol.Handlers
// Other // Other
var unsignedChatContent = dataTypes.ReadNextBool(packetData) var unsignedChatContent = dataTypes.ReadNextBool(packetData)
? dataTypes.ReadNextString(packetData) ? dataTypes.ReadNextChat(packetData)
: null; : null;
var filterType = (MessageFilterType)dataTypes.ReadNextVarInt(packetData); var filterType = (MessageFilterType)dataTypes.ReadNextVarInt(packetData);
@ -929,26 +930,17 @@ namespace MinecraftClient.Protocol.Handlers
// Network Target // Network Target
// net.minecraft.network.message.MessageType.Serialized#write // net.minecraft.network.message.MessageType.Serialized#write
var chatTypeId = dataTypes.ReadNextVarInt(packetData); var chatTypeId = dataTypes.ReadNextVarInt(packetData);
var chatName = dataTypes.ReadNextString(packetData); var chatName = dataTypes.ReadNextChat(packetData);
var targetName = dataTypes.ReadNextBool(packetData) var targetName = dataTypes.ReadNextBool(packetData)
? dataTypes.ReadNextString(packetData) ? dataTypes.ReadNextChat(packetData)
: null; : null;
var messageTypeEnum = var messageTypeEnum =
ChatParser.ChatId2Type!.GetValueOrDefault(chatTypeId, ChatParser.MessageType.CHAT); ChatParser.ChatId2Type!.GetValueOrDefault(chatTypeId, ChatParser.MessageType.CHAT);
var chatInfo = //var chatInfo = Json.ParseJson(targetName ?? chatName).Properties;
Json.ParseJson(targetName ?? chatName).Properties; var senderDisplayName = chatName;
var senderDisplayName = chatInfo != null && chatInfo.Count > 0 string? senderTeamName = targetName;
? (chatInfo.ContainsKey("insertion") ? chatInfo["insertion"] : chatInfo["text"])
.StringValue
: "";
string? senderTeamName = null;
if (targetName != null &&
messageTypeEnum is ChatParser.MessageType.TEAM_MSG_COMMAND_INCOMING
or ChatParser.MessageType.TEAM_MSG_COMMAND_OUTGOING)
senderTeamName = Json.ParseJson(targetName).Properties["with"].DataArray[0]
.Properties["text"].StringValue;
if (string.IsNullOrWhiteSpace(senderDisplayName)) if (string.IsNullOrWhiteSpace(senderDisplayName))
{ {
@ -956,7 +948,7 @@ namespace MinecraftClient.Protocol.Handlers
if (player != null && (player.DisplayName != null || player.Name != null) && if (player != null && (player.DisplayName != null || player.Name != null) &&
string.IsNullOrWhiteSpace(senderDisplayName)) string.IsNullOrWhiteSpace(senderDisplayName))
{ {
senderDisplayName = ChatParser.ParseText(player.DisplayName ?? player.Name); senderDisplayName = player.DisplayName ?? player.Name;
if (string.IsNullOrWhiteSpace(senderDisplayName)) if (string.IsNullOrWhiteSpace(senderDisplayName))
senderDisplayName = player.DisplayName ?? player.Name; senderDisplayName = player.DisplayName ?? player.Name;
else else
@ -1020,7 +1012,7 @@ namespace MinecraftClient.Protocol.Handlers
$"HideMessage was not processed! (SigLen={hideMessageSignature.Length})"); $"HideMessage was not processed! (SigLen={hideMessageSignature.Length})");
break; break;
case PacketTypesIn.SystemChat: case PacketTypesIn.SystemChat:
var systemMessage = dataTypes.ReadNextString(packetData); var systemMessage = dataTypes.ReadNextChat(packetData);
if (protocolVersion >= MC_1_19_3_Version) if (protocolVersion >= MC_1_19_3_Version)
{ {
@ -1036,7 +1028,7 @@ namespace MinecraftClient.Protocol.Handlers
break; break;
} }
handler.OnTextReceived(new(systemMessage, null, true, -1, Guid.Empty, true)); handler.OnTextReceived(new(systemMessage, null, false, -1, Guid.Empty, true));
} }
else else
{ {
@ -1048,15 +1040,15 @@ namespace MinecraftClient.Protocol.Handlers
break; break;
case PacketTypesIn.ProfilelessChatMessage: case PacketTypesIn.ProfilelessChatMessage:
var message_ = dataTypes.ReadNextString(packetData); var message_ = dataTypes.ReadNextChat(packetData);
var messageType_ = dataTypes.ReadNextVarInt(packetData); var messageType_ = dataTypes.ReadNextVarInt(packetData);
var messageName = dataTypes.ReadNextString(packetData); var messageName = dataTypes.ReadNextChat(packetData);
var targetName_ = dataTypes.ReadNextBool(packetData) var targetName_ = dataTypes.ReadNextBool(packetData)
? dataTypes.ReadNextString(packetData) ? dataTypes.ReadNextChat(packetData)
: null; : null;
ChatMessage profilelessChat = new(message_, targetName_ ?? messageName, true, messageType_, ChatMessage profilelessChat = new(message_, targetName_ ?? messageName, false, messageType_,
Guid.Empty, true); Guid.Empty, true);
profilelessChat.isSenderJson = true; profilelessChat.isSenderJson = false;
handler.OnTextReceived(profilelessChat); handler.OnTextReceived(profilelessChat);
break; break;
case PacketTypesIn.CombatEvent: case PacketTypesIn.CombatEvent:
@ -1082,7 +1074,7 @@ namespace MinecraftClient.Protocol.Handlers
handler.OnPlayerKilled( handler.OnPlayerKilled(
protocolVersion >= MC_1_20_Version ? -1 : dataTypes.ReadNextInt(packetData), protocolVersion >= MC_1_20_Version ? -1 : dataTypes.ReadNextInt(packetData),
ChatParser.ParseText(dataTypes.ReadNextString(packetData)) ChatParser.ParseText(dataTypes.ReadNextChat(packetData))
); );
break; break;
@ -1481,7 +1473,7 @@ namespace MinecraftClient.Protocol.Handlers
if (dataTypes.ReadNextBool(packetData)) // Has Display Name? if (dataTypes.ReadNextBool(packetData)) // Has Display Name?
mapIcon.DisplayName = mapIcon.DisplayName =
ChatParser.ParseText(dataTypes.ReadNextString(packetData)); ChatParser.ParseText(dataTypes.ReadNextChat(packetData));
} }
icons.Add(mapIcon); icons.Add(mapIcon);
@ -1669,12 +1661,12 @@ namespace MinecraftClient.Protocol.Handlers
hasMotd = dataTypes.ReadNextBool(packetData); hasMotd = dataTypes.ReadNextBool(packetData);
if (hasMotd) if (hasMotd)
motd = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); motd = ChatParser.ParseText(dataTypes.ReadNextChat(packetData));
} }
else else
{ {
hasMotd = true; hasMotd = true;
motd = (string)dataTypes.ReadNextNbt(packetData)[""]; motd = ChatParser.ParseText(dataTypes.ReadNextChat(packetData));
} }
var iconBase64 = "-"; var iconBase64 = "-";
@ -1888,7 +1880,7 @@ namespace MinecraftClient.Protocol.Handlers
// Actions bit 5: update display name // Actions bit 5: update display name
if ((actionBitset & 1 << 5) <= 0) continue; if ((actionBitset & 1 << 5) <= 0) continue;
player.DisplayName = dataTypes.ReadNextBool(packetData) player.DisplayName = dataTypes.ReadNextBool(packetData)
? dataTypes.ReadNextString(packetData) ? dataTypes.ReadNextChat(packetData)
: null; : null;
} }
} }
@ -2034,7 +2026,7 @@ namespace MinecraftClient.Protocol.Handlers
// Skip optional tooltip for each tab-complete resul`t // Skip optional tooltip for each tab-complete resul`t
if (dataTypes.ReadNextBool(packetData)) if (dataTypes.ReadNextBool(packetData))
dataTypes.SkipNextString(packetData); dataTypes.ReadNextChat(packetData);
} }
handler.OnAutoCompleteDone(oldTransactionId, autocompleteResult); handler.OnAutoCompleteDone(oldTransactionId, autocompleteResult);
@ -2048,7 +2040,7 @@ namespace MinecraftClient.Protocol.Handlers
return pForge.HandlePluginMessage(channel, packetData, ref currentDimension); return pForge.HandlePluginMessage(channel, packetData, ref currentDimension);
case PacketTypesIn.Disconnect: case PacketTypesIn.Disconnect:
handler.OnConnectionLost(ChatBot.DisconnectReason.InGameKick, handler.OnConnectionLost(ChatBot.DisconnectReason.InGameKick,
ChatParser.ParseText(dataTypes.ReadNextString(packetData))); ChatParser.ParseText(dataTypes.ReadNextChat(packetData)));
return false; return false;
case PacketTypesIn.SetCompression: case PacketTypesIn.SetCompression:
if (protocolVersion is >= MC_1_8_Version and < MC_1_9_Version) if (protocolVersion is >= MC_1_8_Version and < MC_1_9_Version)
@ -2065,7 +2057,7 @@ namespace MinecraftClient.Protocol.Handlers
.ToUpper(); .ToUpper();
var inventoryType = var inventoryType =
(ContainerTypeOld)Enum.Parse(typeof(ContainerTypeOld), type); (ContainerTypeOld)Enum.Parse(typeof(ContainerTypeOld), type);
var title = dataTypes.ReadNextString(packetData); var title = dataTypes.ReadNextChat(packetData);
var slots = dataTypes.ReadNextByte(packetData); var slots = dataTypes.ReadNextByte(packetData);
Container inventory = new(windowId, inventoryType, ChatParser.ParseText(title)); Container inventory = new(windowId, inventoryType, ChatParser.ParseText(title));
handler.OnInventoryOpen(windowId, inventory); handler.OnInventoryOpen(windowId, inventory);
@ -2075,7 +2067,7 @@ namespace MinecraftClient.Protocol.Handlers
// MC 1.14 or greater // MC 1.14 or greater
var windowId = dataTypes.ReadNextVarInt(packetData); var windowId = dataTypes.ReadNextVarInt(packetData);
var windowType = dataTypes.ReadNextVarInt(packetData); var windowType = dataTypes.ReadNextVarInt(packetData);
var title = dataTypes.ReadNextString(packetData); var title = dataTypes.ReadNextChat(packetData);
Container inventory = new(windowId, windowType, ChatParser.ParseText(title)); Container inventory = new(windowId, windowType, ChatParser.ParseText(title));
handler.OnInventoryOpen(windowId, inventory); handler.OnInventoryOpen(windowId, inventory);
} }
@ -2544,7 +2536,7 @@ namespace MinecraftClient.Protocol.Handlers
if (mode is 0 or 2) if (mode is 0 or 2)
{ {
objectiveValue = dataTypes.ReadNextString(packetData); objectiveValue = dataTypes.ReadNextChat(packetData);
objectiveType = dataTypes.ReadNextVarInt(packetData); objectiveType = dataTypes.ReadNextVarInt(packetData);
if (protocolVersion >= MC_1_20_4_Version) if (protocolVersion >= MC_1_20_4_Version)

View file

@ -66,6 +66,11 @@ namespace MinecraftClient.Protocol.Message
return JSONData2String(Json.ParseJson(json), "", links); return JSONData2String(Json.ParseJson(json), "", links);
} }
public static string ParseText(Dictionary<string, object> nbt)
{
return NbtToString(nbt);
}
/// <summary> /// <summary>
/// The main function to convert text from MC 1.9+ JSON to MC 1.5.2 formatted text /// The main function to convert text from MC 1.9+ JSON to MC 1.5.2 formatted text
/// </summary> /// </summary>
@ -418,5 +423,64 @@ namespace MinecraftClient.Protocol.Message
return ""; return "";
} }
private static string NbtToString(Dictionary<string, object> nbt)
{
if (nbt.Count == 1 && nbt.TryGetValue("", out object rootMessage))
{
// Nameless root tag
return (string)rootMessage;
}
string message = string.Empty;
StringBuilder extraBuilder = new StringBuilder();
foreach (var kvp in nbt)
{
string key = kvp.Key;
object value = kvp.Value;
switch (key)
{
case "text":
{
message = (string)value;
}
break;
case "extra":
{
object[] extras = (object[])value;
for (int i = 0; i < extras.Length; i++)
{
var extraDict = (Dictionary<string, object>)extras[i];
if (extraDict.TryGetValue("text", out object extraText))
{
extraBuilder.Append(extraText);
}
}
}
break;
case "with":
{
if (nbt.TryGetValue("translate", out object translate))
{
var translateKey = (string)translate;
List<string> translateString = new();
var withs = (object[])value;
for (int i = 0; i < withs.Length; i++)
{
var withDict = (Dictionary<string, object>)withs[i];
if (withDict.TryGetValue("text", out object withText))
{
translateString.Add((string)withText);
}
}
message = TranslateString(translateKey, translateString);
}
}
break;
}
}
return message + extraBuilder.ToString();
}
} }
} }