Added 1.19.1/2 + Terrain Handling

Added 1.19.1/2 + Terrain Handling
This commit is contained in:
Anon 2022-09-03 19:07:51 +00:00 committed by GitHub
commit a383ead27f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 1433 additions and 576 deletions

View file

@ -22,6 +22,13 @@ namespace MinecraftClient.Mapping
/// </summary> /// </summary>
public readonly bool piglinSafe = false; public readonly bool piglinSafe = false;
/// <summary>
/// Possibly the light level(s) at which monsters can spawn.
/// </summary>
public readonly int monsterSpawnMinLightLevel = 0;
public readonly int monsterSpawnMaxLightLevel = 7;
public readonly int monsterSpawnBlockLightLimit = 0;
/// <summary> /// <summary>
/// When false, compasses spin randomly. When true, nether portals can spawn zombified piglins. /// When false, compasses spin randomly. When true, nether portals can spawn zombified piglins.
/// </summary> /// </summary>
@ -132,6 +139,25 @@ namespace MinecraftClient.Mapping
if (nbt.ContainsKey("piglin_safe")) if (nbt.ContainsKey("piglin_safe"))
this.piglinSafe = 1 == (byte)nbt["piglin_safe"]; this.piglinSafe = 1 == (byte)nbt["piglin_safe"];
if (nbt.ContainsKey("monster_spawn_light_level"))
{
try
{
var monsterSpawnLightLevelObj = nbt["monster_spawn_light_level"];
if (monsterSpawnLightLevelObj.GetType() == typeof(int))
this.monsterSpawnMinLightLevel = this.monsterSpawnMaxLightLevel = (int)monsterSpawnLightLevelObj;
else
{
var inclusive = (Dictionary<string, object>)(((Dictionary<string, object>)monsterSpawnLightLevelObj)["value"]);
this.monsterSpawnMinLightLevel = (int)inclusive["min_inclusive"];
this.monsterSpawnMaxLightLevel = (int)inclusive["max_inclusive"];
}
}
catch (KeyNotFoundException) { }
}
if (nbt.ContainsKey("monster_spawn_block_light_limit"))
this.monsterSpawnBlockLightLimit = (int)nbt["monster_spawn_block_light_limit"];
if (nbt.ContainsKey("natural")) if (nbt.ContainsKey("natural"))
this.natural = 1 == (byte)nbt["natural"]; this.natural = 1 == (byte)nbt["natural"];
if (nbt.ContainsKey("ambient_light")) if (nbt.ContainsKey("ambient_light"))

View file

@ -65,6 +65,9 @@ namespace MinecraftClient.Mapping
case Material.BlackShulkerBox: case Material.BlackShulkerBox:
case Material.BlackStainedGlass: case Material.BlackStainedGlass:
case Material.BlackStainedGlassPane: case Material.BlackStainedGlassPane:
case Material.Blackstone:
case Material.BlackstoneSlab:
case Material.BlackstoneStairs:
case Material.BlackTerracotta: case Material.BlackTerracotta:
case Material.BlackWool: case Material.BlackWool:
case Material.BlastFurnace: case Material.BlastFurnace:

View file

@ -19,7 +19,9 @@ namespace MinecraftClient.Mapping
/// <summary> /// <summary>
/// The dimension info of the world /// The dimension info of the world
/// </summary> /// </summary>
private static Dimension dimension = new Dimension(); private static Dimension curDimension = new Dimension();
private static Dictionary<string, Dimension>? dimensionList = null;
/// <summary> /// <summary>
/// Lock for thread safety /// Lock for thread safety
@ -89,24 +91,42 @@ namespace MinecraftClient.Mapping
} }
} }
/// <summary> /// <summary>
/// Set dimension type /// Storage of all dimensional data - 1.19.1 and above
/// </summary>
/// <param name="registryCodec">Registry Codec nbt data</param>
public static void StoreDimensionList(Dictionary<string, object> registryCodec)
{
dimensionList = new();
var dimensionListNbt = (object[])(((Dictionary<string, object>)registryCodec["minecraft:dimension_type"])["value"]);
foreach (Dictionary<string, object> dimensionNbt in dimensionListNbt)
{
string dimensionName = (string)dimensionNbt["name"];
Dictionary<string, object> element = (Dictionary<string, object>)dimensionNbt["element"];
dimensionList.Add(dimensionName, new Dimension(dimensionName, element));
}
}
/// <summary>
/// Set current dimension - 1.16 and above
/// </summary> /// </summary>
/// <param name="name"> The name of the dimension type</param> /// <param name="name"> The name of the dimension type</param>
/// <param name="nbt">The dimension type (NBT Tag Compound)</param> /// <param name="nbt">The dimension type (NBT Tag Compound)</param>
public static void SetDimension(string name, Dictionary<string, object> nbt) public static void SetDimension(string name)
{ {
// will change in 1.19 and above curDimension = dimensionList![name]; // Should not fail
dimension = new Dimension(name, nbt);
} }
/// <summary> /// <summary>
/// Get current dimension /// Get current dimension
/// </summary> /// </summary>
/// <returns>Current dimension</returns> /// <returns>Current dimension</returns>
public static Dimension GetDimension() public static Dimension GetDimension()
{ {
return dimension; return curDimension;
} }
/// <summary> /// <summary>

View file

@ -14,6 +14,7 @@ using MinecraftClient.Mapping;
using MinecraftClient.Inventory; using MinecraftClient.Inventory;
using MinecraftClient.Logger; using MinecraftClient.Logger;
using MinecraftClient.Protocol.Keys; using MinecraftClient.Protocol.Keys;
using MinecraftClient.Protocol.Message;
namespace MinecraftClient namespace MinecraftClient
{ {
@ -68,9 +69,10 @@ namespace MinecraftClient
private int port; private int port;
private int protocolversion; private int protocolversion;
private string username; private string username;
private string uuid; private Guid uuid;
private string uuidStr;
private string sessionid; private string sessionid;
private PlayerKeyPair playerKeyPair; private PlayerKeyPair? playerKeyPair;
private DateTime lastKeepAlive; private DateTime lastKeepAlive;
private object lastKeepAliveLock = new(); private object lastKeepAliveLock = new();
private int respawnTicks = 0; private int respawnTicks = 0;
@ -104,7 +106,8 @@ namespace MinecraftClient
public int GetServerPort() { return port; } public int GetServerPort() { return port; }
public string GetServerHost() { return host; } public string GetServerHost() { return host; }
public string GetUsername() { return username; } public string GetUsername() { return username; }
public string GetUserUUID() { return uuid; } public Guid GetUserUuid() { return uuid; }
public string GetUserUuidStr() { return uuidStr; }
public string GetSessionID() { return sessionid; } public string GetSessionID() { return sessionid; }
public Location GetCurrentLocation() { return location; } public Location GetCurrentLocation() { return location; }
public float GetYaw() { return playerYaw; } public float GetYaw() { return playerYaw; }
@ -141,7 +144,7 @@ namespace MinecraftClient
/// <param name="server_ip">The server IP</param> /// <param name="server_ip">The server IP</param>
/// <param name="port">The server port to use</param> /// <param name="port">The server port to use</param>
/// <param name="protocolversion">Minecraft protocol version to use</param> /// <param name="protocolversion">Minecraft protocol version to use</param>
public McClient(string username, string uuid, string sessionID, PlayerKeyPair playerKeyPair, int protocolversion, ForgeInfo forgeInfo, string server_ip, ushort port) public McClient(string username, string uuid, string sessionID, PlayerKeyPair? playerKeyPair, int protocolversion, ForgeInfo forgeInfo, string server_ip, ushort port)
{ {
StartClient(username, uuid, sessionID, playerKeyPair, server_ip, port, protocolversion, forgeInfo, false, ""); StartClient(username, uuid, sessionID, playerKeyPair, server_ip, port, protocolversion, forgeInfo, false, "");
} }
@ -156,7 +159,7 @@ namespace MinecraftClient
/// <param name="port">The server port to use</param> /// <param name="port">The server port to use</param>
/// <param name="protocolversion">Minecraft protocol version to use</param> /// <param name="protocolversion">Minecraft protocol version to use</param>
/// <param name="command">The text or command to send.</param> /// <param name="command">The text or command to send.</param>
public McClient(string username, string uuid, string sessionID, PlayerKeyPair playerKeyPair, string server_ip, ushort port, int protocolversion, ForgeInfo forgeInfo, string command) public McClient(string username, string uuid, string sessionID, PlayerKeyPair? playerKeyPair, string server_ip, ushort port, int protocolversion, ForgeInfo forgeInfo, string command)
{ {
StartClient(username, uuid, sessionID, playerKeyPair, server_ip, port, protocolversion, forgeInfo, true, command); StartClient(username, uuid, sessionID, playerKeyPair, server_ip, port, protocolversion, forgeInfo, true, command);
} }
@ -172,7 +175,7 @@ namespace MinecraftClient
/// <param name="uuid">The player's UUID for online-mode authentication</param> /// <param name="uuid">The player's UUID for online-mode authentication</param>
/// <param name="singlecommand">If set to true, the client will send a single command and then disconnect from the server</param> /// <param name="singlecommand">If set to true, the client will send a single command and then disconnect from the server</param>
/// <param name="command">The text or command to send. Will only be sent if singlecommand is set to true.</param> /// <param name="command">The text or command to send. Will only be sent if singlecommand is set to true.</param>
private void StartClient(string user, string uuid, string sessionID, PlayerKeyPair playerKeyPair, string server_ip, ushort port, int protocolversion, ForgeInfo forgeInfo, bool singlecommand, string command) private void StartClient(string user, string uuid, string sessionID, PlayerKeyPair? playerKeyPair, string server_ip, ushort port, int protocolversion, ForgeInfo forgeInfo, bool singlecommand, string command)
{ {
terrainAndMovementsEnabled = Settings.TerrainAndMovements; terrainAndMovementsEnabled = Settings.TerrainAndMovements;
inventoryHandlingEnabled = Settings.InventoryHandling; inventoryHandlingEnabled = Settings.InventoryHandling;
@ -180,7 +183,9 @@ namespace MinecraftClient
bool retry = false; bool retry = false;
this.sessionid = sessionID; this.sessionid = sessionID;
this.uuid = uuid; if (!Guid.TryParse(uuid, out this.uuid))
this.uuid = Guid.Empty;
this.uuidStr = uuid;
this.username = user; this.username = user;
this.host = server_ip; this.host = server_ip;
this.port = port; this.port = port;
@ -2093,20 +2098,18 @@ namespace MinecraftClient
List<string> links = new(); List<string> links = new();
string messageText; string messageText;
if (message.isJson) if (message.isSignedChat)
{ {
if (message.isSignedChat) if (!Settings.ShowIllegalSignedChat && !message.isSystemChat && !(bool)message.isSignatureLegal!)
{ return;
if (!Settings.ShowIllegalSignedChat && !message.isSystemChat && !(bool)message.isSignatureLegal!) messageText = ChatParser.ParseSignedChat(message, links);
return;
messageText = ChatParser.ParseSignedChat(message, links);
}
else
messageText = ChatParser.ParseText(message.content, links);
} }
else else
{ {
messageText = message.content; if (message.isJson)
messageText = ChatParser.ParseText(message.content, links);
else
messageText = message.content;
} }
Log.Chat(messageText); Log.Chat(messageText);
@ -2243,15 +2246,16 @@ namespace MinecraftClient
if (player.Name == username) if (player.Name == username)
{ {
// 1.19+ offline server is possible to return different uuid // 1.19+ offline server is possible to return different uuid
this.uuid = player.UUID.ToString().Replace("-", string.Empty); this.uuid = player.Uuid;
this.uuidStr = player.Uuid.ToString().Replace("-", string.Empty);
} }
lock (onlinePlayers) lock (onlinePlayers)
{ {
onlinePlayers[player.UUID] = player; onlinePlayers[player.Uuid] = player;
} }
DispatchBotEvent(bot => bot.OnPlayerJoin(player.UUID, player.Name)); DispatchBotEvent(bot => bot.OnPlayerJoin(player.Uuid, player.Name));
} }
/// <summary> /// <summary>

View file

@ -40,7 +40,7 @@ namespace MinecraftClient
public const string Version = MCHighestVersion; public const string Version = MCHighestVersion;
public const string MCLowestVersion = "1.4.6"; public const string MCLowestVersion = "1.4.6";
public const string MCHighestVersion = "1.19"; public const string MCHighestVersion = "1.19.2";
public static readonly string BuildInfo = null; public static readonly string BuildInfo = null;
private static Tuple<Thread, CancellationTokenSource>? offlinePrompt = null; private static Tuple<Thread, CancellationTokenSource>? offlinePrompt = null;

View file

@ -1105,9 +1105,58 @@ namespace MinecraftClient.Protocol.Handlers
/// </summary> /// </summary>
/// <param name="bytes">Byte array</param> /// <param name="bytes">Byte array</param>
/// <returns>String representation</returns> /// <returns>String representation</returns>
public string ByteArrayToString(byte[] bytes) public string ByteArrayToString(byte[]? bytes)
{ {
return BitConverter.ToString(bytes).Replace("-", " "); if (bytes == null)
return "null";
else
return BitConverter.ToString(bytes).Replace("-", " ");
}
/// <summary>
/// Write LastSeenMessageList
/// </summary>
/// <param name="msgList">Message.LastSeenMessageList</param>
/// <param name="isOnlineMode">Whether the server is in online mode</param>
/// <returns>Message.LastSeenMessageList Packet Data</returns>
public byte[] GetLastSeenMessageList(Message.LastSeenMessageList msgList, bool isOnlineMode)
{
if (!isOnlineMode)
return GetVarInt(0); // Message list size
else
{
List<byte> fields = new();
fields.AddRange(GetVarInt(msgList.entries.Length)); // Message list size
foreach (Message.LastSeenMessageList.Entry entry in msgList.entries)
{
fields.AddRange(entry.profileId.ToBigEndianBytes()); // UUID
fields.AddRange(GetVarInt(entry.lastSignature.Length)); // Signature length
fields.AddRange(entry.lastSignature); // Signature data
}
return fields.ToArray();
}
}
/// <summary>
/// Write LastSeenMessageList.Acknowledgment
/// </summary>
/// <param name="ack">Acknowledgment</param>
/// <param name="isOnlineMode">Whether the server is in online mode</param>
/// <returns>Acknowledgment Packet Data</returns>
public byte[] GetAcknowledgment(Message.LastSeenMessageList.Acknowledgment ack, bool isOnlineMode)
{
List<byte> fields = new();
fields.AddRange(GetLastSeenMessageList(ack.lastSeen, isOnlineMode));
if (!isOnlineMode || ack.lastReceived == null)
fields.AddRange(GetBool(false)); // Has last received message
else
{
fields.AddRange(GetBool(true));
fields.AddRange(ack.lastReceived.profileId.ToBigEndianBytes()); // Has last received message
fields.AddRange(GetVarInt(ack.lastReceived.lastSignature.Length)); // Last received message signature length
fields.AddRange(ack.lastReceived.lastSignature); // Last received message signature data
}
return fields.ToArray();
} }
} }
} }

View file

@ -6,165 +6,165 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes
{ {
private Dictionary<int, PacketTypesIn> typeIn = new Dictionary<int, PacketTypesIn>() private Dictionary<int, PacketTypesIn> typeIn = new Dictionary<int, PacketTypesIn>()
{ {
{ 0x00, PacketTypesIn.SpawnEntity }, // Changed in 1.19 (Wiki name: Spawn Entity) - DONE { 0x00, PacketTypesIn.SpawnEntity }, // Changed in 1.19 (Wiki name: Spawn Entity) - DONE
{ 0x01, PacketTypesIn.SpawnExperienceOrb }, //(Wiki name: Spawn Exeprience Orb) { 0x01, PacketTypesIn.SpawnExperienceOrb }, // (Wiki name: Spawn Exeprience Orb)
{ 0x02, PacketTypesIn.SpawnPlayer }, { 0x02, PacketTypesIn.SpawnPlayer }, //
{ 0x03, PacketTypesIn.EntityAnimation }, //(Wiki name: Entity Animation (clientbound)) { 0x03, PacketTypesIn.EntityAnimation }, // (Wiki name: Entity Animation (clientbound))
{ 0x04, PacketTypesIn.Statistics }, //(Wiki name: Award Statistics) { 0x04, PacketTypesIn.Statistics }, // (Wiki name: Award Statistics)
{ 0x05, PacketTypesIn.BlockChangedAck }, //Added 1.19 (Wiki name: Acknowledge Block Change) - DONE { 0x05, PacketTypesIn.BlockChangedAck }, // Added 1.19 (Wiki name: Acknowledge Block Change) - DONE
{ 0x06, PacketTypesIn.BlockBreakAnimation }, //(Wiki name: Set Block Destroy Stage) { 0x06, PacketTypesIn.BlockBreakAnimation }, // (Wiki name: Set Block Destroy Stage)
{ 0x07, PacketTypesIn.BlockEntityData }, { 0x07, PacketTypesIn.BlockEntityData }, //
{ 0x08, PacketTypesIn.BlockAction }, { 0x08, PacketTypesIn.BlockAction }, //
{ 0x09, PacketTypesIn.BlockChange }, //(Wiki name: Block Update) { 0x09, PacketTypesIn.BlockChange }, // (Wiki name: Block Update)
{ 0x0A, PacketTypesIn.BossBar }, { 0x0A, PacketTypesIn.BossBar }, //
{ 0x0B, PacketTypesIn.ServerDifficulty }, // (Wiki name: Change Difficulty) { 0x0B, PacketTypesIn.ServerDifficulty }, // (Wiki name: Change Difficulty)
{ 0x0C, PacketTypesIn.ChatPreview }, // Added 1.19 { 0x0C, PacketTypesIn.ChatPreview }, // Added 1.19
{ 0x0D, PacketTypesIn.ClearTiles }, { 0x0D, PacketTypesIn.ClearTiles }, //
{ 0x0E, PacketTypesIn.TabComplete }, // (Wiki name: Command Suggestions Response) { 0x0E, PacketTypesIn.TabComplete }, // (Wiki name: Command Suggestions Response)
{ 0x0F, PacketTypesIn.DeclareCommands }, // (Wiki name: Commands) { 0x0F, PacketTypesIn.DeclareCommands }, // (Wiki name: Commands)
{ 0x10, PacketTypesIn.CloseWindow }, // (Wiki name: Close Container (clientbound)) { 0x10, PacketTypesIn.CloseWindow }, // (Wiki name: Close Container (clientbound))
{ 0x11, PacketTypesIn.WindowItems }, // (Wiki name: Set Container Content) { 0x11, PacketTypesIn.WindowItems }, // (Wiki name: Set Container Content)
{ 0x12, PacketTypesIn.WindowProperty }, // (Wiki name: Set Container Property) { 0x12, PacketTypesIn.WindowProperty }, // (Wiki name: Set Container Property)
{ 0x13, PacketTypesIn.SetSlot }, // (Wiki name: Set Container Slot) { 0x13, PacketTypesIn.SetSlot }, // (Wiki name: Set Container Slot)
{ 0x14, PacketTypesIn.SetCooldown }, { 0x14, PacketTypesIn.SetCooldown }, //
{ 0x15, PacketTypesIn.PluginMessage }, // (Wiki name: Plugin Message (clientbound)) { 0x15, PacketTypesIn.PluginMessage }, // (Wiki name: Plugin Message (clientbound))
{ 0x16, PacketTypesIn.NamedSoundEffect }, // Changed in 1.19 (Added "Speed" field) (Wiki name: Custom Sound Effect) - DONE (No need to be implemented) { 0x16, PacketTypesIn.NamedSoundEffect }, // Changed in 1.19 (Added "Speed" field) (Wiki name: Custom Sound Effect) - DONE (No need to be implemented)
{ 0x17, PacketTypesIn.Disconnect }, { 0x17, PacketTypesIn.Disconnect }, //
{ 0x18, PacketTypesIn.EntityStatus }, // (Wiki name: Entity Event) { 0x18, PacketTypesIn.EntityStatus }, // (Wiki name: Entity Event)
{ 0x19, PacketTypesIn.Explosion }, // Changed in 1.19 (Location fields are now Double instead of Float) (Wiki name: Explosion) - DONE { 0x19, PacketTypesIn.Explosion }, // Changed in 1.19 (Location fields are now Double instead of Float) (Wiki name: Explosion) - DONE
{ 0x1A, PacketTypesIn.UnloadChunk }, // (Wiki name: Forget Chunk) { 0x1A, PacketTypesIn.UnloadChunk }, // (Wiki name: Forget Chunk)
{ 0x1B, PacketTypesIn.ChangeGameState }, // (Wiki name: Game Event) { 0x1B, PacketTypesIn.ChangeGameState }, // (Wiki name: Game Event)
{ 0x1C, PacketTypesIn.OpenHorseWindow }, // (Wiki name: Horse Screen Open) { 0x1C, PacketTypesIn.OpenHorseWindow }, // (Wiki name: Horse Screen Open)
{ 0x1D, PacketTypesIn.InitializeWorldBorder }, { 0x1D, PacketTypesIn.InitializeWorldBorder }, //
{ 0x1E, PacketTypesIn.KeepAlive }, { 0x1E, PacketTypesIn.KeepAlive }, //
{ 0x1F, PacketTypesIn.ChunkData }, { 0x1F, PacketTypesIn.ChunkData }, //
{ 0x20, PacketTypesIn.Effect }, // (Wiki name: Level Event) { 0x20, PacketTypesIn.Effect }, // (Wiki name: Level Event)
{ 0x21, PacketTypesIn.Particle }, // Changed in 1.19 ("Particle Data" field is now "Max Speed", it's the same Float data type) (Wiki name: Level Particle) - DONE (No need to be implemented) { 0x21, PacketTypesIn.Particle }, // Changed in 1.19 ("Particle Data" field is now "Max Speed", it's the same Float data type) (Wiki name: Level Particle) - DONE (No need to be implemented)
{ 0x22, PacketTypesIn.UpdateLight }, // (Wiki name: Light Update) { 0x22, PacketTypesIn.UpdateLight }, // (Wiki name: Light Update)
{ 0x23, PacketTypesIn.JoinGame }, // Changed in 1.19 (lot's of changes) (Wiki name: Login (play)) - DONE { 0x23, PacketTypesIn.JoinGame }, // Changed in 1.19 (lot's of changes) (Wiki name: Login (play)) - DONE
{ 0x24, PacketTypesIn.MapData }, // (Wiki name: Map Item Data) { 0x24, PacketTypesIn.MapData }, // (Wiki name: Map Item Data)
{ 0x25, PacketTypesIn.TradeList }, // (Wiki name: Merchant Offers) { 0x25, PacketTypesIn.TradeList }, // (Wiki name: Merchant Offers)
{ 0x26, PacketTypesIn.EntityPosition }, // (Wiki name: Move Entity Position) { 0x26, PacketTypesIn.EntityPosition }, // (Wiki name: Move Entity Position)
{ 0x27, PacketTypesIn.EntityPositionAndRotation }, // (Wiki name: Move Entity Position and Rotation) { 0x27, PacketTypesIn.EntityPositionAndRotation }, // (Wiki name: Move Entity Position and Rotation)
{ 0x28, PacketTypesIn.EntityRotation }, // (Wiki name: Move Entity Rotation) { 0x28, PacketTypesIn.EntityRotation }, // (Wiki name: Move Entity Rotation)
{ 0x29, PacketTypesIn.VehicleMove }, // (Wiki name: Move Vehicle) { 0x29, PacketTypesIn.VehicleMove }, // (Wiki name: Move Vehicle)
{ 0x2A, PacketTypesIn.OpenBook }, { 0x2A, PacketTypesIn.OpenBook }, //
{ 0x2B, PacketTypesIn.OpenWindow }, // (Wiki name: Open Screen) { 0x2B, PacketTypesIn.OpenWindow }, // (Wiki name: Open Screen)
{ 0x2C, PacketTypesIn.OpenSignEditor }, { 0x2C, PacketTypesIn.OpenSignEditor }, //
{ 0x2D, PacketTypesIn.Ping }, // (Wiki name: Ping (play)) { 0x2D, PacketTypesIn.Ping }, // (Wiki name: Ping (play))
{ 0x2E, PacketTypesIn.CraftRecipeResponse }, // (Wiki name: Place Ghost Recipe) { 0x2E, PacketTypesIn.CraftRecipeResponse }, // (Wiki name: Place Ghost Recipe)
{ 0x2F, PacketTypesIn.PlayerAbilities }, { 0x2F, PacketTypesIn.PlayerAbilities }, //
{ 0x30, PacketTypesIn.ChatMessage }, // Changed in 1.19 (Completely changed) (Wiki name: Player Chat Message) { 0x30, PacketTypesIn.ChatMessage }, // Changed in 1.19 (Completely changed) (Wiki name: Player Chat Message)
{ 0x31, PacketTypesIn.EndCombatEvent }, // (Wiki name: Player Combat End) { 0x31, PacketTypesIn.EndCombatEvent }, // (Wiki name: Player Combat End)
{ 0x32, PacketTypesIn.EnterCombatEvent }, // (Wiki name: Player Combat Enter) { 0x32, PacketTypesIn.EnterCombatEvent }, // (Wiki name: Player Combat Enter)
{ 0x33, PacketTypesIn.DeathCombatEvent }, // (Wiki name: Player Combat Kill) { 0x33, PacketTypesIn.DeathCombatEvent }, // (Wiki name: Player Combat Kill)
{ 0x34, PacketTypesIn.PlayerInfo }, // Changed in 1.19 (Heavy changes) - DONE { 0x34, PacketTypesIn.PlayerInfo }, // Changed in 1.19 (Heavy changes) - DONE
{ 0x35, PacketTypesIn.FacePlayer }, // (Wiki name: Player Look At) { 0x35, PacketTypesIn.FacePlayer }, // (Wiki name: Player Look At)
{ 0x36, PacketTypesIn.PlayerPositionAndLook }, // (Wiki name: Player Position) { 0x36, PacketTypesIn.PlayerPositionAndLook }, // (Wiki name: Player Position)
{ 0x37, PacketTypesIn.UnlockRecipes }, // (Wiki name: Recipe) { 0x37, PacketTypesIn.UnlockRecipes }, // (Wiki name: Recipe)
{ 0x38, PacketTypesIn.DestroyEntities }, // (Wiki name: Remove Entites) { 0x38, PacketTypesIn.DestroyEntities }, // (Wiki name: Remove Entites)
{ 0x39, PacketTypesIn.RemoveEntityEffect }, { 0x39, PacketTypesIn.RemoveEntityEffect }, //
{ 0x3A, PacketTypesIn.ResourcePackSend }, // (Wiki name: Resource Pack) { 0x3A, PacketTypesIn.ResourcePackSend }, // (Wiki name: Resource Pack)
{ 0x3B, PacketTypesIn.Respawn }, // Changed in 1.19 (Heavy changes) - DONE { 0x3B, PacketTypesIn.Respawn }, // Changed in 1.19 (Heavy changes) - DONE
{ 0x3C, PacketTypesIn.EntityHeadLook }, // (Wiki name: Rotate Head) { 0x3C, PacketTypesIn.EntityHeadLook }, // (Wiki name: Rotate Head)
{ 0x3D, PacketTypesIn.MultiBlockChange }, // (Wiki name: Sections Block Update) { 0x3D, PacketTypesIn.MultiBlockChange }, // (Wiki name: Sections Block Update)
{ 0x3E, PacketTypesIn.SelectAdvancementTab }, { 0x3E, PacketTypesIn.SelectAdvancementTab }, //
{ 0x3F, PacketTypesIn.ServerData }, // Added in 1.19 { 0x3F, PacketTypesIn.ServerData }, // Added in 1.19
{ 0x40, PacketTypesIn.ActionBar }, // (Wiki name: Set Action Bar Text) { 0x40, PacketTypesIn.ActionBar }, // (Wiki name: Set Action Bar Text)
{ 0x41, PacketTypesIn.WorldBorderCenter }, // (Wiki name: Set Border Center) { 0x41, PacketTypesIn.WorldBorderCenter }, // (Wiki name: Set Border Center)
{ 0x42, PacketTypesIn.WorldBorderLerpSize }, { 0x42, PacketTypesIn.WorldBorderLerpSize }, //
{ 0x43, PacketTypesIn.WorldBorderSize }, // (Wiki name: Set World Border Size) { 0x43, PacketTypesIn.WorldBorderSize }, // (Wiki name: Set World Border Size)
{ 0x44, PacketTypesIn.WorldBorderWarningDelay }, // (Wiki name: Set World Border Warning Delay) { 0x44, PacketTypesIn.WorldBorderWarningDelay }, // (Wiki name: Set World Border Warning Delay)
{ 0x45, PacketTypesIn.WorldBorderWarningReach }, // (Wiki name: Set Border Warning Distance) { 0x45, PacketTypesIn.WorldBorderWarningReach }, // (Wiki name: Set Border Warning Distance)
{ 0x46, PacketTypesIn.Camera }, // (Wiki name: Set Camera) { 0x46, PacketTypesIn.Camera }, // (Wiki name: Set Camera)
{ 0x47, PacketTypesIn.HeldItemChange }, // (Wiki name: Set Carried Item (clientbound)) { 0x47, PacketTypesIn.HeldItemChange }, // (Wiki name: Set Carried Item (clientbound))
{ 0x48, PacketTypesIn.UpdateViewPosition }, // (Wiki name: Set Chunk Cache Center) { 0x48, PacketTypesIn.UpdateViewPosition }, // (Wiki name: Set Chunk Cache Center)
{ 0x49, PacketTypesIn.UpdateViewDistance }, // (Wiki name: Set Chunk Cache Radius) { 0x49, PacketTypesIn.UpdateViewDistance }, // (Wiki name: Set Chunk Cache Radius)
{ 0x4A, PacketTypesIn.SpawnPosition }, // (Wiki name: Set Default Spawn Position) { 0x4A, PacketTypesIn.SpawnPosition }, // (Wiki name: Set Default Spawn Position)
{ 0x4B, PacketTypesIn.SetDisplayChatPreview }, // Added in 1.19 (Wiki name: Set Display Chat Preview) { 0x4B, PacketTypesIn.SetDisplayChatPreview }, // Added in 1.19 (Wiki name: Set Display Chat Preview)
{ 0x4C, PacketTypesIn.DisplayScoreboard }, // (Wiki name: Set Display Objective) { 0x4C, PacketTypesIn.DisplayScoreboard }, // (Wiki name: Set Display Objective)
{ 0x4D, PacketTypesIn.EntityMetadata }, // (Wiki name: Set Entity Metadata) { 0x4D, PacketTypesIn.EntityMetadata }, // (Wiki name: Set Entity Metadata)
{ 0x4E, PacketTypesIn.AttachEntity }, // (Wiki name: Set Entity Link) { 0x4E, PacketTypesIn.AttachEntity }, // (Wiki name: Set Entity Link)
{ 0x4F, PacketTypesIn.EntityVelocity }, // (Wiki name: Set Entity Motion) { 0x4F, PacketTypesIn.EntityVelocity }, // (Wiki name: Set Entity Motion)
{ 0x50, PacketTypesIn.EntityEquipment }, // (Wiki name: Set Equipment) { 0x50, PacketTypesIn.EntityEquipment }, // (Wiki name: Set Equipment)
{ 0x51, PacketTypesIn.SetExperience }, { 0x51, PacketTypesIn.SetExperience }, //
{ 0x52, PacketTypesIn.UpdateHealth }, // (Wiki name: Set Health) { 0x52, PacketTypesIn.UpdateHealth }, // (Wiki name: Set Health)
{ 0x53, PacketTypesIn.ScoreboardObjective }, // (Wiki name: Set Objective) { 0x53, PacketTypesIn.ScoreboardObjective }, // (Wiki name: Set Objective)
{ 0x54, PacketTypesIn.SetPassengers }, { 0x54, PacketTypesIn.SetPassengers }, //
{ 0x55, PacketTypesIn.Teams }, // (Wiki name: Set Player Team) { 0x55, PacketTypesIn.Teams }, // (Wiki name: Set Player Team)
{ 0x56, PacketTypesIn.UpdateScore }, // (Wiki name: Set Score) { 0x56, PacketTypesIn.UpdateScore }, // (Wiki name: Set Score)
{ 0x57, PacketTypesIn.UpdateSimulationDistance }, // (Wiki name: Set Simulation Distance) { 0x57, PacketTypesIn.UpdateSimulationDistance }, // (Wiki name: Set Simulation Distance)
{ 0x58, PacketTypesIn.SetTitleSubTitle }, // (Wiki name: Set Subtitle Test) { 0x58, PacketTypesIn.SetTitleSubTitle }, // (Wiki name: Set Subtitle Test)
{ 0x59, PacketTypesIn.TimeUpdate }, // (Wiki name: Set Time) { 0x59, PacketTypesIn.TimeUpdate }, // (Wiki name: Set Time)
{ 0x5A, PacketTypesIn.SetTitleText }, // (Wiki name: Set Title) { 0x5A, PacketTypesIn.SetTitleText }, // (Wiki name: Set Title)
{ 0x5B, PacketTypesIn.SetTitleTime }, // (Wiki name: Set Titles Animation) { 0x5B, PacketTypesIn.SetTitleTime }, // (Wiki name: Set Titles Animation)
{ 0x5C, PacketTypesIn.EntitySoundEffect }, // (Wiki name: Sound Entity) { 0x5C, PacketTypesIn.EntitySoundEffect }, // (Wiki name: Sound Entity)
{ 0x5D, PacketTypesIn.SoundEffect }, // Changed in 1.19 (Added "Seed" field) (Wiki name: Sound Effect) - DONE (No need to be implemented) { 0x5D, PacketTypesIn.SoundEffect }, // Changed in 1.19 (Added "Seed" field) (Wiki name: Sound Effect) - DONE (No need to be implemented)
{ 0x5E, PacketTypesIn.StopSound }, { 0x5E, PacketTypesIn.StopSound }, //
{ 0x5F, PacketTypesIn.SystemChat }, // Added in 1.19 (Wiki name: System Chat Message) { 0x5F, PacketTypesIn.SystemChat }, // Added in 1.19 (Wiki name: System Chat Message)
{ 0x60, PacketTypesIn.PlayerListHeaderAndFooter }, // (Wiki name: Tab List) { 0x60, PacketTypesIn.PlayerListHeaderAndFooter }, // (Wiki name: Tab List)
{ 0x61, PacketTypesIn.NBTQueryResponse }, // (Wiki name: Tab Query) { 0x61, PacketTypesIn.NBTQueryResponse }, // (Wiki name: Tab Query)
{ 0x62, PacketTypesIn.CollectItem }, // (Wiki name: Take Item Entity) { 0x62, PacketTypesIn.CollectItem }, // (Wiki name: Take Item Entity)
{ 0x63, PacketTypesIn.EntityTeleport }, // (Wiki name: Teleport Entity) { 0x63, PacketTypesIn.EntityTeleport }, // (Wiki name: Teleport Entity)
{ 0x64, PacketTypesIn.Advancements }, // (Wiki name: Update Advancements) { 0x64, PacketTypesIn.Advancements }, // (Wiki name: Update Advancements)
{ 0x65, PacketTypesIn.EntityProperties }, // (Wiki name: Update Attributes) { 0x65, PacketTypesIn.EntityProperties }, // (Wiki name: Update Attributes)
{ 0x66, PacketTypesIn.EntityEffect }, // Changed in 1.19 (Added "Has Factor Data" and "Factor Codec" fields) (Wiki name: Entity Effect) - DONE { 0x66, PacketTypesIn.EntityEffect }, // Changed in 1.19 (Added "Has Factor Data" and "Factor Codec" fields) (Wiki name: Entity Effect) - DONE
{ 0x67, PacketTypesIn.DeclareRecipes }, // (Wiki name: Update Recipes) { 0x67, PacketTypesIn.DeclareRecipes }, // (Wiki name: Update Recipes)
{ 0x68, PacketTypesIn.Tags }, // (Wiki name: Update Tags) { 0x68, PacketTypesIn.Tags }, // (Wiki name: Update Tags)
}; };
private Dictionary<int, PacketTypesOut> typeOut = new Dictionary<int, PacketTypesOut>() private Dictionary<int, PacketTypesOut> typeOut = new Dictionary<int, PacketTypesOut>()
{ {
{ 0x00, PacketTypesOut.TeleportConfirm }, // (Wiki name: Confirm Teleportation) { 0x00, PacketTypesOut.TeleportConfirm }, // (Wiki name: Confirm Teleportation)
{ 0x01, PacketTypesOut.QueryBlockNBT }, // (Wiki name: Query Block Entity Tag) { 0x01, PacketTypesOut.QueryBlockNBT }, // (Wiki name: Query Block Entity Tag)
{ 0x02, PacketTypesOut.SetDifficulty }, // (Wiki name: Change Difficutly) { 0x02, PacketTypesOut.SetDifficulty }, // (Wiki name: Change Difficutly)
{ 0x03, PacketTypesOut.ChatCommand }, // Added in 1.19 { 0x03, PacketTypesOut.ChatCommand }, // Added in 1.19
{ 0x04, PacketTypesOut.ChatMessage }, // Changed in 1.19 (Completely changed) (Wiki name: Chat) { 0x04, PacketTypesOut.ChatMessage }, // Changed in 1.19 (Completely changed) (Wiki name: Chat)
{ 0x05, PacketTypesOut.ChatPreview }, // Added in 1.19 (Wiki name: Chat Preview (serverbound)) { 0x05, PacketTypesOut.ChatPreview }, // Added in 1.19 (Wiki name: Chat Preview (serverbound))
{ 0x06, PacketTypesOut.ClientStatus }, // (Wiki name: Client Command) { 0x06, PacketTypesOut.ClientStatus }, // (Wiki name: Client Command)
{ 0x07, PacketTypesOut.ClientSettings }, // (Wiki name: Client Information) { 0x07, PacketTypesOut.ClientSettings }, // (Wiki name: Client Information)
{ 0x08, PacketTypesOut.TabComplete }, // (Wiki name: Command Suggestions Request) { 0x08, PacketTypesOut.TabComplete }, // (Wiki name: Command Suggestions Request)
{ 0x09, PacketTypesOut.ClickWindowButton }, // (Wiki name: Click Container Button) { 0x09, PacketTypesOut.ClickWindowButton }, // (Wiki name: Click Container Button)
{ 0x0A, PacketTypesOut.ClickWindow }, // (Wiki name: Click Container) { 0x0A, PacketTypesOut.ClickWindow }, // (Wiki name: Click Container)
{ 0x0B, PacketTypesOut.CloseWindow }, // (Wiki name: Close Container (serverbound)) { 0x0B, PacketTypesOut.CloseWindow }, // (Wiki name: Close Container (serverbound))
{ 0x0C, PacketTypesOut.PluginMessage }, // (Wiki name: Plugin Message (serverbound)) { 0x0C, PacketTypesOut.PluginMessage }, // (Wiki name: Plugin Message (serverbound))
{ 0x0D, PacketTypesOut.EditBook }, { 0x0D, PacketTypesOut.EditBook }, //
{ 0x0E, PacketTypesOut.EntityNBTRequest }, // (Wiki name: Query Entity Tag) { 0x0E, PacketTypesOut.EntityNBTRequest }, // (Wiki name: Query Entity Tag)
{ 0x0F, PacketTypesOut.InteractEntity }, // (Wiki name: Interact) { 0x0F, PacketTypesOut.InteractEntity }, // (Wiki name: Interact)
{ 0x10, PacketTypesOut.GenerateStructure }, // (Wiki name: Jigsaw Generate) { 0x10, PacketTypesOut.GenerateStructure }, // (Wiki name: Jigsaw Generate)
{ 0x11, PacketTypesOut.KeepAlive }, { 0x11, PacketTypesOut.KeepAlive }, //
{ 0x12, PacketTypesOut.LockDifficulty }, { 0x12, PacketTypesOut.LockDifficulty }, //
{ 0x13, PacketTypesOut.PlayerPosition }, // (Wiki name: Move Player Position) { 0x13, PacketTypesOut.PlayerPosition }, // (Wiki name: Move Player Position)
{ 0x14, PacketTypesOut.PlayerPositionAndRotation }, // (Wiki name: Set Player Position and Rotation) { 0x14, PacketTypesOut.PlayerPositionAndRotation }, // (Wiki name: Set Player Position and Rotation)
{ 0x15, PacketTypesOut.PlayerRotation }, // (Wiki name: Set Player Rotation) { 0x15, PacketTypesOut.PlayerRotation }, // (Wiki name: Set Player Rotation)
{ 0x16, PacketTypesOut.PlayerMovement }, // (Wiki name: Set Player On Ground) { 0x16, PacketTypesOut.PlayerMovement }, // (Wiki name: Set Player On Ground)
{ 0x17, PacketTypesOut.VehicleMove }, // (Wiki name: Move Vehicle (serverbound)) { 0x17, PacketTypesOut.VehicleMove }, // (Wiki name: Move Vehicle (serverbound))
{ 0x18, PacketTypesOut.SteerBoat }, // (Wiki name: Paddle Boat) { 0x18, PacketTypesOut.SteerBoat }, // (Wiki name: Paddle Boat)
{ 0x19, PacketTypesOut.PickItem }, { 0x19, PacketTypesOut.PickItem }, //
{ 0x1A, PacketTypesOut.CraftRecipeRequest }, // (Wiki name: Place recipe) { 0x1A, PacketTypesOut.CraftRecipeRequest }, // (Wiki name: Place recipe)
{ 0x1B, PacketTypesOut.PlayerAbilities }, { 0x1B, PacketTypesOut.PlayerAbilities }, //
{ 0x1C, PacketTypesOut.PlayerDigging }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Player Action) - DONE { 0x1C, PacketTypesOut.PlayerDigging }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Player Action) - DONE
{ 0x1D, PacketTypesOut.EntityAction }, // (Wiki name: Player Command) { 0x1D, PacketTypesOut.EntityAction }, // (Wiki name: Player Command)
{ 0x1E, PacketTypesOut.SteerVehicle }, // (Wiki name: Player Input) { 0x1E, PacketTypesOut.SteerVehicle }, // (Wiki name: Player Input)
{ 0x1F, PacketTypesOut.Pong }, // (Wiki name: Pong (play)) { 0x1F, PacketTypesOut.Pong }, // (Wiki name: Pong (play))
{ 0x20, PacketTypesOut.SetDisplayedRecipe }, // (Wiki name: Recipe Book Change Settings) { 0x20, PacketTypesOut.SetDisplayedRecipe }, // (Wiki name: Recipe Book Change Settings)
{ 0x21, PacketTypesOut.SetRecipeBookState }, // (Wiki name: Recipe Book Seen Recipe) { 0x21, PacketTypesOut.SetRecipeBookState }, // (Wiki name: Recipe Book Seen Recipe)
{ 0x22, PacketTypesOut.NameItem }, // (Wiki name: Rename Item) { 0x22, PacketTypesOut.NameItem }, // (Wiki name: Rename Item)
{ 0x23, PacketTypesOut.ResourcePackStatus }, // (Wiki name: Resource Pack (serverbound)) { 0x23, PacketTypesOut.ResourcePackStatus }, // (Wiki name: Resource Pack (serverbound))
{ 0x24, PacketTypesOut.AdvancementTab }, // (Wiki name: Seen Advancements) { 0x24, PacketTypesOut.AdvancementTab }, // (Wiki name: Seen Advancements)
{ 0x25, PacketTypesOut.SelectTrade }, { 0x25, PacketTypesOut.SelectTrade }, //
{ 0x26, PacketTypesOut.SetBeaconEffect }, // Changed in 1.19 (Added a "Secondary Effect Present" and "Secondary Effect" fields) (Wiki name: Set Beacon) - DONE - (No need to be implemented) { 0x26, PacketTypesOut.SetBeaconEffect }, // Changed in 1.19 (Added a "Secondary Effect Present" and "Secondary Effect" fields) (Wiki name: Set Beacon) - DONE - (No need to be implemented)
{ 0x27, PacketTypesOut.HeldItemChange }, // (Wiki name: Set Carried Item (serverbound)) { 0x27, PacketTypesOut.HeldItemChange }, // (Wiki name: Set Carried Item (serverbound))
{ 0x28, PacketTypesOut.UpdateCommandBlock }, // (Wiki name: Set Command Block) { 0x28, PacketTypesOut.UpdateCommandBlock }, // (Wiki name: Set Command Block)
{ 0x29, PacketTypesOut.UpdateCommandBlockMinecart }, { 0x29, PacketTypesOut.UpdateCommandBlockMinecart }, //
{ 0x2A, PacketTypesOut.CreativeInventoryAction }, // (Wiki name: Set Creative Mode Slot) { 0x2A, PacketTypesOut.CreativeInventoryAction }, // (Wiki name: Set Creative Mode Slot)
{ 0x2B, PacketTypesOut.UpdateJigsawBlock }, // (Wiki name: Set Jigsaw Block) { 0x2B, PacketTypesOut.UpdateJigsawBlock }, // (Wiki name: Set Jigsaw Block)
{ 0x2C, PacketTypesOut.UpdateStructureBlock }, // (Wiki name: Set Structure Block) { 0x2C, PacketTypesOut.UpdateStructureBlock }, // (Wiki name: Set Structure Block)
{ 0x2D, PacketTypesOut.UpdateSign }, // (Wiki name: Sign Update) { 0x2D, PacketTypesOut.UpdateSign }, // (Wiki name: Sign Update)
{ 0x2E, PacketTypesOut.Animation }, // (Wiki name: Swing) { 0x2E, PacketTypesOut.Animation }, // (Wiki name: Swing)
{ 0x2F, PacketTypesOut.Spectate }, // (Wiki name: Teleport To Entity) { 0x2F, PacketTypesOut.Spectate }, // (Wiki name: Teleport To Entity)
{ 0x30, PacketTypesOut.PlayerBlockPlacement }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Use Item On) - DONE { 0x30, PacketTypesOut.PlayerBlockPlacement }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Use Item On) - DONE
{ 0x31, PacketTypesOut.UseItem }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Use Item) - DONE { 0x31, PacketTypesOut.UseItem }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Use Item) - DONE
}; };
protected override Dictionary<int, PacketTypesIn> GetListIn() protected override Dictionary<int, PacketTypesIn> GetListIn()

View file

@ -0,0 +1,184 @@
using System.Collections.Generic;
namespace MinecraftClient.Protocol.Handlers.PacketPalettes
{
public class PacketPalette1192 : PacketTypePalette
{
private Dictionary<int, PacketTypesIn> typeIn = new Dictionary<int, PacketTypesIn>()
{
{ 0x00, PacketTypesIn.SpawnEntity }, // Changed in 1.19 (Wiki name: Spawn Entity) - DONE
{ 0x01, PacketTypesIn.SpawnExperienceOrb }, // (Wiki name: Spawn Exeprience Orb)
{ 0x02, PacketTypesIn.SpawnPlayer }, //
{ 0x03, PacketTypesIn.EntityAnimation }, // (Wiki name: Entity Animation (clientbound))
{ 0x04, PacketTypesIn.Statistics }, // (Wiki name: Award Statistics)
{ 0x05, PacketTypesIn.BlockChangedAck }, // Added 1.19 (Wiki name: Acknowledge Block Change) - DONE
{ 0x06, PacketTypesIn.BlockBreakAnimation }, // (Wiki name: Set Block Destroy Stage)
{ 0x07, PacketTypesIn.BlockEntityData }, //
{ 0x08, PacketTypesIn.BlockAction }, //
{ 0x09, PacketTypesIn.BlockChange }, // (Wiki name: Block Update)
{ 0x0A, PacketTypesIn.BossBar }, //
{ 0x0B, PacketTypesIn.ServerDifficulty }, // (Wiki name: Change Difficulty)
{ 0x0C, PacketTypesIn.ChatPreview }, // Added 1.19
{ 0x0D, PacketTypesIn.ClearTiles }, //
{ 0x0E, PacketTypesIn.TabComplete }, // (Wiki name: Command Suggestions Response)
{ 0x0F, PacketTypesIn.DeclareCommands }, // (Wiki name: Commands)
{ 0x10, PacketTypesIn.CloseWindow }, // (Wiki name: Close Container (clientbound))
{ 0x11, PacketTypesIn.WindowItems }, // (Wiki name: Set Container Content)
{ 0x12, PacketTypesIn.WindowProperty }, // (Wiki name: Set Container Property)
{ 0x13, PacketTypesIn.SetSlot }, // (Wiki name: Set Container Slot)
{ 0x14, PacketTypesIn.SetCooldown }, //
{ 0x15, PacketTypesIn.ChatSuggestions }, // Added 1.19.1
{ 0x16, PacketTypesIn.PluginMessage }, // (Wiki name: Plugin Message (clientbound))
{ 0x17, PacketTypesIn.NamedSoundEffect }, // Changed in 1.19 (Added "Speed" field) (Wiki name: Custom Sound Effect) - DONE (No need to be implemented)
{ 0x18, PacketTypesIn.HideMessage }, // Added 1.19.1
{ 0x19, PacketTypesIn.Disconnect }, //
{ 0x1A, PacketTypesIn.EntityStatus }, // (Wiki name: Entity Event)
{ 0x1B, PacketTypesIn.Explosion }, // Changed in 1.19 (Location fields are now Double instead of Float) (Wiki name: Explosion) - DONE
{ 0x1C, PacketTypesIn.UnloadChunk }, // (Wiki name: Forget Chunk)
{ 0x1D, PacketTypesIn.ChangeGameState }, // (Wiki name: Game Event)
{ 0x1E, PacketTypesIn.OpenHorseWindow }, // (Wiki name: Horse Screen Open)
{ 0x1F, PacketTypesIn.InitializeWorldBorder }, //
{ 0x20, PacketTypesIn.KeepAlive }, //
{ 0x21, PacketTypesIn.ChunkData }, //
{ 0x22, PacketTypesIn.Effect }, // (Wiki name: Level Event)
{ 0x23, PacketTypesIn.Particle }, // Changed in 1.19 ("Particle Data" field is now "Max Speed", it's the same Float data type) (Wiki name: Level Particle) - DONE (No need to be implemented)
{ 0x24, PacketTypesIn.UpdateLight }, // (Wiki name: Light Update)
{ 0x25, PacketTypesIn.JoinGame }, // Changed in 1.19 (lot's of changes) (Wiki name: Login (play)) - DONE
{ 0x26, PacketTypesIn.MapData }, // (Wiki name: Map Item Data)
{ 0x27, PacketTypesIn.TradeList }, // (Wiki name: Merchant Offers)
{ 0x28, PacketTypesIn.EntityPosition }, // (Wiki name: Move Entity Position)
{ 0x29, PacketTypesIn.EntityPositionAndRotation }, // (Wiki name: Move Entity Position and Rotation)
{ 0x2A, PacketTypesIn.EntityRotation }, // (Wiki name: Move Entity Rotation)
{ 0x2B, PacketTypesIn.VehicleMove }, // (Wiki name: Move Vehicle)
{ 0x2C, PacketTypesIn.OpenBook }, //
{ 0x2D, PacketTypesIn.OpenWindow }, // (Wiki name: Open Screen)
{ 0x2E, PacketTypesIn.OpenSignEditor }, //
{ 0x2F, PacketTypesIn.Ping }, // (Wiki name: Ping (play))
{ 0x30, PacketTypesIn.CraftRecipeResponse }, // (Wiki name: Place Ghost Recipe)
{ 0x31, PacketTypesIn.PlayerAbilities }, //
{ 0x32, PacketTypesIn.MessageHeader }, // Added 1.19.1
{ 0x33, PacketTypesIn.ChatMessage }, // Changed in 1.19 (Completely changed) (Wiki name: Player Chat Message)
{ 0x34, PacketTypesIn.EndCombatEvent }, // (Wiki name: Player Combat End)
{ 0x35, PacketTypesIn.EnterCombatEvent }, // (Wiki name: Player Combat Enter)
{ 0x36, PacketTypesIn.DeathCombatEvent }, // (Wiki name: Player Combat Kill)
{ 0x37, PacketTypesIn.PlayerInfo }, // Changed in 1.19 (Heavy changes) - DONE
{ 0x38, PacketTypesIn.FacePlayer }, // (Wiki name: Player Look At)
{ 0x39, PacketTypesIn.PlayerPositionAndLook }, // (Wiki name: Player Position)
{ 0x3A, PacketTypesIn.UnlockRecipes }, // (Wiki name: Recipe)
{ 0x3B, PacketTypesIn.DestroyEntities }, // (Wiki name: Remove Entites)
{ 0x3C, PacketTypesIn.RemoveEntityEffect }, //
{ 0x3D, PacketTypesIn.ResourcePackSend }, // (Wiki name: Resource Pack)
{ 0x3E, PacketTypesIn.Respawn }, // Changed in 1.19 (Heavy changes) - DONE
{ 0x3F, PacketTypesIn.EntityHeadLook }, // (Wiki name: Rotate Head)
{ 0x40, PacketTypesIn.MultiBlockChange }, // (Wiki name: Sections Block Update)
{ 0x41, PacketTypesIn.SelectAdvancementTab }, //
{ 0x42, PacketTypesIn.ServerData }, // Added in 1.19
{ 0x43, PacketTypesIn.ActionBar }, // (Wiki name: Set Action Bar Text)
{ 0x44, PacketTypesIn.WorldBorderCenter }, // (Wiki name: Set Border Center)
{ 0x45, PacketTypesIn.WorldBorderLerpSize }, //
{ 0x46, PacketTypesIn.WorldBorderSize }, // (Wiki name: Set World Border Size)
{ 0x47, PacketTypesIn.WorldBorderWarningDelay }, // (Wiki name: Set World Border Warning Delay)
{ 0x48, PacketTypesIn.WorldBorderWarningReach }, // (Wiki name: Set Border Warning Distance)
{ 0x49, PacketTypesIn.Camera }, // (Wiki name: Set Camera)
{ 0x4A, PacketTypesIn.HeldItemChange }, // (Wiki name: Set Carried Item (clientbound))
{ 0x4B, PacketTypesIn.UpdateViewPosition }, // (Wiki name: Set Chunk Cache Center)
{ 0x4C, PacketTypesIn.UpdateViewDistance }, // (Wiki name: Set Chunk Cache Radius)
{ 0x4D, PacketTypesIn.SpawnPosition }, // (Wiki name: Set Default Spawn Position)
{ 0x4E, PacketTypesIn.SetDisplayChatPreview }, // Added in 1.19 (Wiki name: Set Display Chat Preview)
{ 0x4F, PacketTypesIn.DisplayScoreboard }, // (Wiki name: Set Display Objective)
{ 0x50, PacketTypesIn.EntityMetadata }, // (Wiki name: Set Entity Metadata)
{ 0x51, PacketTypesIn.AttachEntity }, // (Wiki name: Set Entity Link)
{ 0x52, PacketTypesIn.EntityVelocity }, // (Wiki name: Set Entity Motion)
{ 0x53, PacketTypesIn.EntityEquipment }, // (Wiki name: Set Equipment)
{ 0x54, PacketTypesIn.SetExperience }, //
{ 0x55, PacketTypesIn.UpdateHealth }, // (Wiki name: Set Health)
{ 0x56, PacketTypesIn.ScoreboardObjective }, // (Wiki name: Set Objective)
{ 0x57, PacketTypesIn.SetPassengers }, //
{ 0x58, PacketTypesIn.Teams }, // (Wiki name: Set Player Team)
{ 0x59, PacketTypesIn.UpdateScore }, // (Wiki name: Set Score)
{ 0x5A, PacketTypesIn.UpdateSimulationDistance }, // (Wiki name: Set Simulation Distance)
{ 0x5B, PacketTypesIn.SetTitleSubTitle }, // (Wiki name: Set Subtitle Test)
{ 0x5C, PacketTypesIn.TimeUpdate }, // (Wiki name: Set Time)
{ 0x5D, PacketTypesIn.SetTitleText }, // (Wiki name: Set Title)
{ 0x5E, PacketTypesIn.SetTitleTime }, // (Wiki name: Set Titles Animation)
{ 0x5F, PacketTypesIn.EntitySoundEffect }, // (Wiki name: Sound Entity)
{ 0x60, PacketTypesIn.SoundEffect }, // Changed in 1.19 (Added "Seed" field) (Wiki name: Sound Effect) - DONE (No need to be implemented)
{ 0x61, PacketTypesIn.StopSound }, //
{ 0x62, PacketTypesIn.SystemChat }, // Added in 1.19 (Wiki name: System Chat Message)
{ 0x63, PacketTypesIn.PlayerListHeaderAndFooter }, // (Wiki name: Tab List)
{ 0x64, PacketTypesIn.NBTQueryResponse }, // (Wiki name: Tab Query)
{ 0x65, PacketTypesIn.CollectItem }, // (Wiki name: Take Item Entity)
{ 0x66, PacketTypesIn.EntityTeleport }, // (Wiki name: Teleport Entity)
{ 0x67, PacketTypesIn.Advancements }, // (Wiki name: Update Advancements)
{ 0x68, PacketTypesIn.EntityProperties }, // (Wiki name: Update Attributes)
{ 0x69, PacketTypesIn.EntityEffect }, // Changed in 1.19 (Added "Has Factor Data" and "Factor Codec" fields) (Wiki name: Entity Effect) - DONE
{ 0x6A, PacketTypesIn.DeclareRecipes }, // (Wiki name: Update Recipes)
{ 0x6B, PacketTypesIn.Tags }, // (Wiki name: Update Tags)
};
private Dictionary<int, PacketTypesOut> typeOut = new Dictionary<int, PacketTypesOut>()
{
{ 0x00, PacketTypesOut.TeleportConfirm }, // (Wiki name: Confirm Teleportation)
{ 0x01, PacketTypesOut.QueryBlockNBT }, // (Wiki name: Query Block Entity Tag)
{ 0x02, PacketTypesOut.SetDifficulty }, // (Wiki name: Change Difficutly)
{ 0x03, PacketTypesOut.MessageAcknowledgment }, // Added in 1.19.1
{ 0x04, PacketTypesOut.ChatCommand }, // Added in 1.19
{ 0x05, PacketTypesOut.ChatMessage }, // Changed in 1.19 (Completely changed) (Wiki name: Chat)
{ 0x06, PacketTypesOut.ChatPreview }, // Added in 1.19 (Wiki name: Chat Preview (serverbound))
{ 0x07, PacketTypesOut.ClientStatus }, // (Wiki name: Client Command)
{ 0x08, PacketTypesOut.ClientSettings }, // (Wiki name: Client Information)
{ 0x09, PacketTypesOut.TabComplete }, // (Wiki name: Command Suggestions Request)
{ 0x0A, PacketTypesOut.ClickWindowButton }, // (Wiki name: Click Container Button)
{ 0x0B, PacketTypesOut.ClickWindow }, // (Wiki name: Click Container)
{ 0x0C, PacketTypesOut.CloseWindow }, // (Wiki name: Close Container (serverbound))
{ 0x0D, PacketTypesOut.PluginMessage }, // (Wiki name: Plugin Message (serverbound))
{ 0x0E, PacketTypesOut.EditBook }, //
{ 0x0F, PacketTypesOut.EntityNBTRequest }, // (Wiki name: Query Entity Tag)
{ 0x10, PacketTypesOut.InteractEntity }, // (Wiki name: Interact)
{ 0x11, PacketTypesOut.GenerateStructure }, // (Wiki name: Jigsaw Generate)
{ 0x12, PacketTypesOut.KeepAlive }, //
{ 0x13, PacketTypesOut.LockDifficulty }, //
{ 0x14, PacketTypesOut.PlayerPosition }, // (Wiki name: Move Player Position)
{ 0x15, PacketTypesOut.PlayerPositionAndRotation }, // (Wiki name: Set Player Position and Rotation)
{ 0x16, PacketTypesOut.PlayerRotation }, // (Wiki name: Set Player Rotation)
{ 0x17, PacketTypesOut.PlayerMovement }, // (Wiki name: Set Player On Ground)
{ 0x18, PacketTypesOut.VehicleMove }, // (Wiki name: Move Vehicle (serverbound))
{ 0x19, PacketTypesOut.SteerBoat }, // (Wiki name: Paddle Boat)
{ 0x1A, PacketTypesOut.PickItem }, //
{ 0x1B, PacketTypesOut.CraftRecipeRequest }, // (Wiki name: Place recipe)
{ 0x1C, PacketTypesOut.PlayerAbilities }, //
{ 0x1D, PacketTypesOut.PlayerDigging }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Player Action) - DONE
{ 0x1E, PacketTypesOut.EntityAction }, // (Wiki name: Player Command)
{ 0x1F, PacketTypesOut.SteerVehicle }, // (Wiki name: Player Input)
{ 0x20, PacketTypesOut.Pong }, // (Wiki name: Pong (play))
{ 0x21, PacketTypesOut.SetDisplayedRecipe }, // (Wiki name: Recipe Book Change Settings)
{ 0x22, PacketTypesOut.SetRecipeBookState }, // (Wiki name: Recipe Book Seen Recipe)
{ 0x23, PacketTypesOut.NameItem }, // (Wiki name: Rename Item)
{ 0x24, PacketTypesOut.ResourcePackStatus }, // (Wiki name: Resource Pack (serverbound))
{ 0x25, PacketTypesOut.AdvancementTab }, // (Wiki name: Seen Advancements)
{ 0x26, PacketTypesOut.SelectTrade }, //
{ 0x27, PacketTypesOut.SetBeaconEffect }, // Changed in 1.19 (Added a "Secondary Effect Present" and "Secondary Effect" fields) (Wiki name: Set Beacon) - DONE - (No need to be implemented)
{ 0x28, PacketTypesOut.HeldItemChange }, // (Wiki name: Set Carried Item (serverbound))
{ 0x29, PacketTypesOut.UpdateCommandBlock }, // (Wiki name: Set Command Block)
{ 0x2A, PacketTypesOut.UpdateCommandBlockMinecart }, //
{ 0x2B, PacketTypesOut.CreativeInventoryAction }, // (Wiki name: Set Creative Mode Slot)
{ 0x2C, PacketTypesOut.UpdateJigsawBlock }, // (Wiki name: Set Jigsaw Block)
{ 0x2D, PacketTypesOut.UpdateStructureBlock }, // (Wiki name: Set Structure Block)
{ 0x2E, PacketTypesOut.UpdateSign }, // (Wiki name: Sign Update)
{ 0x2F, PacketTypesOut.Animation }, // (Wiki name: Swing)
{ 0x30, PacketTypesOut.Spectate }, // (Wiki name: Teleport To Entity)
{ 0x31, PacketTypesOut.PlayerBlockPlacement }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Use Item On) - DONE
{ 0x32, PacketTypesOut.UseItem }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Use Item) - DONE
};
protected override Dictionary<int, PacketTypesIn> GetListIn()
{
return typeIn;
}
protected override Dictionary<int, PacketTypesOut> GetListOut()
{
return typeOut;
}
}
}

View file

@ -51,8 +51,9 @@ namespace MinecraftClient.Protocol.Handlers
public PacketTypePalette GetTypeHandler(int protocol) public PacketTypePalette GetTypeHandler(int protocol)
{ {
PacketTypePalette p; PacketTypePalette p;
if (protocol > Protocol18Handler.MC_1_19_Version) if (protocol > Protocol18Handler.MC_1_19_2_Version)
throw new NotImplementedException(Translations.Get("exception.palette.packet")); throw new NotImplementedException(Translations.Get("exception.palette.packet"));
if (protocol <= Protocol18Handler.MC_1_8_Version) if (protocol <= Protocol18Handler.MC_1_8_Version)
p = new PacketPalette17(); p = new PacketPalette17();
else if (protocol <= Protocol18Handler.MC_1_11_2_Version) else if (protocol <= Protocol18Handler.MC_1_11_2_Version)
@ -75,8 +76,10 @@ namespace MinecraftClient.Protocol.Handlers
p = new PacketPalette117(); p = new PacketPalette117();
else if (protocol <= Protocol18Handler.MC_1_18_2_Version) else if (protocol <= Protocol18Handler.MC_1_18_2_Version)
p = new PacketPalette118(); p = new PacketPalette118();
else else if (protocol <= Protocol18Handler.MC_1_19_Version)
p = new PacketPalette119(); p = new PacketPalette119();
else
p = new PacketPalette1192();
p.SetForgeEnabled(this.forgeEnabled); p.SetForgeEnabled(this.forgeEnabled);
return p; return p;

View file

@ -10,128 +10,129 @@ namespace MinecraftClient.Protocol.Handlers
/// </summary> /// </summary>
public enum PacketTypesIn public enum PacketTypesIn
{ {
SpawnEntity, AcknowledgePlayerDigging, //
SpawnExperienceOrb, ActionBar, //
SpawnWeatherEntity, Advancements, //
SpawnLivingEntity, AttachEntity, //
SpawnPainting, BlockAction, //
SpawnPlayer, BlockBreakAnimation, //
EntityAnimation, BlockChange, //
Statistics, BlockChangedAck, // Added in 1.19
AcknowledgePlayerDigging, BlockEntityData, //
BlockBreakAnimation, BossBar, //
BlockEntityData, Camera, //
BlockAction, ChangeGameState, //
BlockChange, ChatMessage, //
BossBar, ChatPreview, // Added in 1.19
ServerDifficulty, ChatSuggestions, // Added in 1.19.1 (1.19.2)
ChatMessage, ChunkData, //
MultiBlockChange, ClearTiles, //
TabComplete, CloseWindow, //
DeclareCommands, CollectItem, //
WindowConfirmation, CombatEvent, //
CloseWindow, CraftRecipeResponse, //
WindowItems, DeathCombatEvent, //
WindowProperty, DeclareCommands, //
SetSlot, DeclareRecipes, //
SetCooldown, DestroyEntities, //
PluginMessage, Disconnect, //
NamedSoundEffect, DisplayScoreboard, //
Disconnect, Effect, //
EntityStatus, EndCombatEvent, //
Explosion, EnterCombatEvent, //
UnloadChunk, EntityAnimation, //
ChangeGameState, EntityEffect, //
OpenHorseWindow, EntityEquipment, //
KeepAlive, EntityHeadLook, //
ChunkData, EntityMetadata, //
Effect, EntityMovement, //
Particle, EntityPosition, //
UpdateLight, EntityPositionAndRotation, //
JoinGame, EntityProperties, //
MapData, EntityRotation, //
TradeList, EntitySoundEffect, //
EntityPosition, EntityStatus, //
EntityPositionAndRotation, EntityTeleport, //
EntityRotation, EntityVelocity, //
EntityMovement, Explosion, //
VehicleMove, FacePlayer, //
OpenBook, HeldItemChange, //
OpenWindow, HideMessage, // Added in 1.19.1 (1.19.2)
OpenSignEditor, InitializeWorldBorder, //
CraftRecipeResponse, JoinGame, //
PlayerAbilities, KeepAlive, //
CombatEvent, MapChunkBulk, // For 1.8 or below
PlayerInfo, MapData, //
FacePlayer, MessageHeader, // Added in 1.19.1 (1.19.2)
PlayerPositionAndLook, MultiBlockChange, //
UnlockRecipes, NamedSoundEffect, //
DestroyEntities, NBTQueryResponse, //
RemoveEntityEffect, OpenBook, //
ResourcePackSend, OpenHorseWindow, //
Respawn, OpenSignEditor, //
EntityHeadLook, OpenWindow, //
SelectAdvancementTab, Particle, //
WorldBorder, Ping, //
Camera, PlayerAbilities, //
HeldItemChange, PlayerInfo, //
UpdateViewPosition, PlayerListHeaderAndFooter, //
UpdateViewDistance, PlayerPositionAndLook, //
DisplayScoreboard, PluginMessage, //
EntityMetadata, RemoveEntityEffect, //
AttachEntity, ResourcePackSend, //
EntityVelocity, Respawn, //
EntityEquipment, ScoreboardObjective, //
SetExperience, SelectAdvancementTab, //
UpdateHealth, ServerData, // Added in 1.19
ScoreboardObjective, ServerDifficulty, //
SetPassengers, SetCompression, // For 1.8 or below
Teams, SetCooldown, //
UpdateScore, SetDisplayChatPreview, // Added in 1.19
SpawnPosition, SetExperience, //
TimeUpdate, SetPassengers, //
Title, SetSlot, //
EntitySoundEffect, SetTitleSubTitle, //
SoundEffect, SetTitleText, //
StopSound, SetTitleTime, //
PlayerListHeaderAndFooter, SkulkVibrationSignal, //
NBTQueryResponse, SoundEffect, //
CollectItem, SpawnEntity, //
EntityTeleport, SpawnExperienceOrb, //
Advancements, SpawnLivingEntity, //
EntityProperties, SpawnPainting, //
EntityEffect, SpawnPlayer, //
DeclareRecipes, SpawnPosition, //
SetTitleTime, SpawnWeatherEntity, //
SetTitleText, Statistics, //
SetTitleSubTitle, StopSound, //
WorldBorderWarningReach, SystemChat, // Added in 1.19
WorldBorderWarningDelay, TabComplete, //
WorldBorderSize, Tags, //
WorldBorderLerpSize, Teams, //
WorldBorderCenter, TimeUpdate, //
ActionBar, Title, //
Tags, TradeList, //
DeathCombatEvent, Unknown, // For old version packet that have been removed and not used by mcc
EnterCombatEvent, UnloadChunk, //
EndCombatEvent, UnlockRecipes, //
Ping, UpdateEntityNBT, // For 1.8 or below
InitializeWorldBorder, UpdateHealth, //
SkulkVibrationSignal, UpdateLight, //
ClearTiles, UpdateScore, //
UseBed, // For 1.13.2 or below UpdateSign, // For 1.8 or below
MapChunkBulk, // For 1.8 or below UpdateSimulationDistance, //
SetCompression, // For 1.8 or below UpdateViewDistance, //
UpdateSign, // For 1.8 or below UpdateViewPosition, //
UpdateEntityNBT, // For 1.8 or below UseBed, // For 1.13.2 or below
Unknown, // For old version packet that have been removed and not used by mcc VehicleMove, //
UpdateSimulationDistance, WindowConfirmation, //
WindowItems, //
// 1.19 Additions WindowProperty, //
BlockChangedAck, WorldBorder, //
ChatPreview, WorldBorderCenter, //
ServerData, WorldBorderLerpSize, //
SetDisplayChatPreview, WorldBorderSize, //
SystemChat WorldBorderWarningDelay, //
WorldBorderWarningReach, //
} }
} }

View file

@ -10,62 +10,61 @@ namespace MinecraftClient.Protocol.Handlers
/// </summary> /// </summary>
public enum PacketTypesOut public enum PacketTypesOut
{ {
TeleportConfirm, AdvancementTab, //
QueryBlockNBT, Animation, //
SetDifficulty, ChatCommand, // Added in 1.19
ChatMessage, ChatMessage, //
ClientStatus, ChatPreview, // Added in 1.19
ClientSettings, ClickWindow, //
TabComplete, ClickWindowButton, //
WindowConfirmation, ClientSettings, //
ClickWindowButton, ClientStatus, //
ClickWindow, CloseWindow, //
CloseWindow, CraftRecipeRequest, //
PluginMessage, CreativeInventoryAction, //
EditBook, EditBook, //
EntityNBTRequest, EnchantItem, // For 1.13.2 or below
InteractEntity, EntityAction, //
KeepAlive, EntityNBTRequest, //
LockDifficulty, GenerateStructure, // Added in 1.16
PlayerPosition, HeldItemChange, //
PlayerPositionAndRotation, InteractEntity, //
PlayerRotation, KeepAlive, //
PlayerMovement, LockDifficulty, //
VehicleMove, MessageAcknowledgment, // Added in 1.19.1 (1.19.2)
SteerBoat, NameItem, //
PickItem, PickItem, //
CraftRecipeRequest, PlayerAbilities, //
PlayerAbilities, PlayerBlockPlacement, //
PlayerDigging, PlayerDigging, //
EntityAction, PlayerMovement, //
SteerVehicle, PlayerPosition, //
RecipeBookData, PlayerPositionAndRotation, //
NameItem, PlayerRotation, //
ResourcePackStatus, PluginMessage, //
AdvancementTab, Pong, //
SelectTrade, PrepareCraftingGrid, // For 1.12 - 1.12.1 only
SetBeaconEffect, QueryBlockNBT, //
HeldItemChange, RecipeBookData, //
UpdateCommandBlock, ResourcePackStatus, //
UpdateCommandBlockMinecart, SelectTrade, //
CreativeInventoryAction, SetBeaconEffect, //
UpdateJigsawBlock, SetDifficulty, //
UpdateStructureBlock, SetDisplayedRecipe, // Added in 1.16.2
UpdateSign, SetRecipeBookState, // Added in 1.16.2
Animation, Spectate, //
Spectate, SteerBoat, //
PlayerBlockPlacement, SteerVehicle, //
UseItem, TabComplete, //
Pong, TeleportConfirm, //
PrepareCraftingGrid, // For 1.12 - 1.12.1 only Unknown, // For old version packet that have been removed and not used by mcc
EnchantItem, // For 1.13.2 or below UpdateCommandBlock, //
GenerateStructure, // Added in 1.16 UpdateCommandBlockMinecart, //
SetDisplayedRecipe, // Added in 1.16.2 UpdateJigsawBlock, //
SetRecipeBookState, // Added in 1.16.2 UpdateSign, //
Unknown, // For old version packet that have been removed and not used by mcc UpdateStructureBlock, //
UseItem, //
// Added in 1.19 VehicleMove, //
ChatCommand, WindowConfirmation, //
ChatPreview,
} }
} }

View file

@ -10,6 +10,7 @@ using System.Security.Cryptography;
using MinecraftClient.Mapping; using MinecraftClient.Mapping;
using MinecraftClient.Inventory; using MinecraftClient.Inventory;
using MinecraftClient.Protocol.Keys; using MinecraftClient.Protocol.Keys;
using MinecraftClient.Protocol.Message;
namespace MinecraftClient.Protocol.Handlers namespace MinecraftClient.Protocol.Handlers
{ {
@ -559,7 +560,7 @@ namespace MinecraftClient.Protocol.Handlers
public bool Login(PlayerKeyPair playerKeyPair) public bool Login(PlayerKeyPair playerKeyPair)
{ {
if (Handshake(handler.GetUserUUID(), handler.GetUsername(), handler.GetSessionID(), handler.GetServerHost(), handler.GetServerPort())) if (Handshake(handler.GetUserUuidStr(), handler.GetUsername(), handler.GetSessionID(), handler.GetServerHost(), handler.GetServerPort()))
{ {
Send(new byte[] { 0xCD, 0 }); Send(new byte[] { 0xCD, 0 });
try try

View file

@ -19,6 +19,7 @@ using MinecraftClient.Logger;
using System.Threading.Tasks; using System.Threading.Tasks;
using MinecraftClient.Protocol.Keys; using MinecraftClient.Protocol.Keys;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using MinecraftClient.Protocol.Message;
namespace MinecraftClient.Protocol.Handlers namespace MinecraftClient.Protocol.Handlers
{ {
@ -55,6 +56,7 @@ namespace MinecraftClient.Protocol.Handlers
internal const int MC_1_18_1_Version = 757; internal const int MC_1_18_1_Version = 757;
internal const int MC_1_18_2_Version = 758; internal const int MC_1_18_2_Version = 758;
internal const int MC_1_19_Version = 759; internal const int MC_1_19_Version = 759;
internal const int MC_1_19_2_Version = 760;
private int compression_treshold = 0; private int compression_treshold = 0;
private bool autocomplete_received = false; private bool autocomplete_received = false;
@ -64,6 +66,11 @@ namespace MinecraftClient.Protocol.Handlers
private bool login_phase = true; private bool login_phase = true;
private int protocolVersion; private int protocolVersion;
private int currentDimension; private int currentDimension;
private bool isOnlineMode = false;
private int pendingAcknowledgments = 0;
private LastSeenMessagesCollector lastSeenMessagesCollector = new(5);
private LastSeenMessageList.Entry? lastReceivedMessage = null;
Protocol18Forge pForge; Protocol18Forge pForge;
Protocol18Terrain pTerrain; Protocol18Terrain pTerrain;
@ -91,85 +98,105 @@ namespace MinecraftClient.Protocol.Handlers
this.log = handler.GetLogger(); this.log = handler.GetLogger();
this.randomGen = RandomNumberGenerator.Create(); this.randomGen = RandomNumberGenerator.Create();
if (handler.GetTerrainEnabled() && protocolVersion > MC_1_18_2_Version) if (handler.GetTerrainEnabled() && protocolVersion > MC_1_19_2_Version)
{ {
log.Error(Translations.Get("extra.terrainandmovement_disabled")); log.Error(Translations.Get("extra.terrainandmovement_disabled"));
handler.SetTerrainEnabled(false); handler.SetTerrainEnabled(false);
} }
if (handler.GetInventoryEnabled() && (protocolVersion < MC_1_10_Version || protocolVersion > MC_1_19_Version)) if (handler.GetInventoryEnabled() && (protocolVersion < MC_1_10_Version || protocolVersion > MC_1_19_2_Version))
{ {
log.Error(Translations.Get("extra.inventory_disabled")); log.Error(Translations.Get("extra.inventory_disabled"));
handler.SetInventoryEnabled(false); handler.SetInventoryEnabled(false);
} }
if (handler.GetEntityHandlingEnabled() && (protocolVersion < MC_1_10_Version || protocolVersion > MC_1_19_2_Version))
if (handler.GetEntityHandlingEnabled() && (protocolVersion < MC_1_10_Version || protocolVersion > MC_1_19_Version))
{ {
log.Error(Translations.Get("extra.entity_disabled")); log.Error(Translations.Get("extra.entity_disabled"));
handler.SetEntityHandlingEnabled(false); handler.SetEntityHandlingEnabled(false);
} }
// Block palette // Block palette
if (protocolVersion >= MC_1_13_Version) if (protocolVersion > MC_1_19_2_Version && handler.GetTerrainEnabled())
{ throw new NotImplementedException(Translations.Get("exception.palette.block"));
if (protocolVersion > MC_1_19_Version && handler.GetTerrainEnabled())
throw new NotImplementedException(Translations.Get("exception.palette.block"));
if (protocolVersion == MC_1_19_Version) if (protocolVersion >= MC_1_19_Version)
Block.Palette = new Palette119(); Block.Palette = new Palette119();
else if (protocolVersion >= MC_1_17_Version) else if (protocolVersion >= MC_1_17_Version)
Block.Palette = new Palette117(); Block.Palette = new Palette117();
else if (protocolVersion >= MC_1_16_Version) else if (protocolVersion >= MC_1_16_Version)
Block.Palette = new Palette116(); Block.Palette = new Palette116();
else if (protocolVersion >= MC_1_15_Version) else if (protocolVersion >= MC_1_15_Version)
Block.Palette = new Palette115(); Block.Palette = new Palette115();
else if (protocolVersion >= MC_1_14_Version) else if (protocolVersion >= MC_1_14_Version)
Block.Palette = new Palette114(); Block.Palette = new Palette114();
else Block.Palette = new Palette113(); else if (protocolVersion >= MC_1_13_Version)
Block.Palette = new Palette113();
} else
else Block.Palette = new Palette112(); Block.Palette = new Palette112();
// Entity palette // Entity palette
if (protocolVersion >= MC_1_13_Version) if (protocolVersion > MC_1_19_2_Version && handler.GetEntityHandlingEnabled())
{ throw new NotImplementedException(Translations.Get("exception.palette.entity"));
if (protocolVersion > MC_1_19_Version && handler.GetEntityHandlingEnabled())
throw new NotImplementedException(Translations.Get("exception.palette.entity"));
if (protocolVersion == MC_1_19_Version) if (protocolVersion >= MC_1_19_Version)
entityPalette = new EntityPalette119(); entityPalette = new EntityPalette119();
else if (protocolVersion >= MC_1_17_Version) else if (protocolVersion >= MC_1_17_Version)
entityPalette = new EntityPalette117(); entityPalette = new EntityPalette117();
else if (protocolVersion >= MC_1_16_2_Version) else if (protocolVersion >= MC_1_16_2_Version)
entityPalette = new EntityPalette1162(); entityPalette = new EntityPalette1162();
else if (protocolVersion >= MC_1_16_Version) else if (protocolVersion >= MC_1_16_Version)
entityPalette = new EntityPalette1161(); entityPalette = new EntityPalette1161();
else if (protocolVersion >= MC_1_15_Version) else if (protocolVersion >= MC_1_15_Version)
entityPalette = new EntityPalette115(); entityPalette = new EntityPalette115();
else if (protocolVersion >= MC_1_14_Version) else if (protocolVersion >= MC_1_14_Version)
entityPalette = new EntityPalette114(); entityPalette = new EntityPalette114();
else entityPalette = new EntityPalette113(); else if (protocolVersion >= MC_1_13_Version)
} entityPalette = new EntityPalette113();
else entityPalette = new EntityPalette112(); else
entityPalette = new EntityPalette112();
// Item palette // Item palette
if (protocolVersion >= MC_1_16_2_Version) if (protocolVersion > MC_1_19_2_Version && handler.GetInventoryEnabled())
{ throw new NotImplementedException(Translations.Get("exception.palette.item"));
if (protocolVersion > MC_1_19_Version && handler.GetInventoryEnabled())
throw new NotImplementedException(Translations.Get("exception.palette.item"));
if (protocolVersion == MC_1_19_Version) if (protocolVersion >= MC_1_19_Version)
itemPalette = new ItemPalette119(); itemPalette = new ItemPalette119();
else if (protocolVersion >= MC_1_18_1_Version) else if (protocolVersion >= MC_1_18_1_Version)
itemPalette = new ItemPalette118(); itemPalette = new ItemPalette118();
else if (protocolVersion >= MC_1_17_Version) else if (protocolVersion >= MC_1_17_Version)
itemPalette = new ItemPalette117(); itemPalette = new ItemPalette117();
else if (protocolVersion >= MC_1_16_2_Version) else if (protocolVersion >= MC_1_16_2_Version)
if (protocolVersion >= MC_1_16_2_Version) itemPalette = new ItemPalette1162();
itemPalette = new ItemPalette1162(); else if (protocolVersion >= MC_1_16_1_Version)
else itemPalette = new ItemPalette1161(); itemPalette = new ItemPalette1161();
} else
else itemPalette = new ItemPalette115(); itemPalette = new ItemPalette115();
// MessageType
// You can find it in https://wiki.vg/Protocol#Player_Chat_Message or /net/minecraft/network/message/MessageType.java
if (this.protocolVersion >= MC_1_19_2_Version)
ChatParser.ChatId2Type = new()
{
{ 0, ChatParser.MessageType.CHAT },
{ 1, ChatParser.MessageType.SAY_COMMAND },
{ 2, ChatParser.MessageType.MSG_COMMAND_INCOMING },
{ 3, ChatParser.MessageType.MSG_COMMAND_OUTGOING },
{ 4, ChatParser.MessageType.TEAM_MSG_COMMAND_INCOMING },
{ 5, ChatParser.MessageType.TEAM_MSG_COMMAND_OUTGOING },
{ 6, ChatParser.MessageType.EMOTE_COMMAND },
};
else if (this.protocolVersion == MC_1_19_Version)
ChatParser.ChatId2Type = new()
{
{ 0, ChatParser.MessageType.CHAT },
{ 1, ChatParser.MessageType.RAW_MSG },
{ 2, ChatParser.MessageType.RAW_MSG },
{ 3, ChatParser.MessageType.SAY_COMMAND },
{ 4, ChatParser.MessageType.MSG_COMMAND_INCOMING },
{ 5, ChatParser.MessageType.TEAM_MSG_COMMAND_INCOMING },
{ 6, ChatParser.MessageType.EMOTE_COMMAND },
{ 7, ChatParser.MessageType.RAW_MSG },
};
} }
/// <summary> /// <summary>
@ -328,26 +355,22 @@ namespace MinecraftClient.Protocol.Handlers
int worldCount = dataTypes.ReadNextVarInt(packetData); // Dimension Count (World Count) - 1.16 and above int worldCount = dataTypes.ReadNextVarInt(packetData); // Dimension Count (World Count) - 1.16 and above
for (int i = 0; i < worldCount; i++) for (int i = 0; i < worldCount; i++)
dataTypes.ReadNextString(packetData); // Dimension Names (World Names) - 1.16 and above dataTypes.ReadNextString(packetData); // Dimension Names (World Names) - 1.16 and above
dataTypes.ReadNextNbt(packetData); // Registry Codec (Dimension Codec) - 1.16 and above var registryCodec = dataTypes.ReadNextNbt(packetData); // Registry Codec (Dimension Codec) - 1.16 and above
World.StoreDimensionList(registryCodec);
} }
string? currentDimensionName = null;
Dictionary<string, object>? currentDimensionType = null;
// Current dimension // Current dimension
// NBT Tag Compound: 1.16.2 and above // String: 1.19 and above
// NBT Tag Compound: [1.16.2 to 1.18.2]
// String identifier: 1.16 and 1.16.1 // String identifier: 1.16 and 1.16.1
// varInt: [1.9.1 to 1.15.2] // varInt: [1.9.1 to 1.15.2]
// byte: below 1.9.1 // byte: below 1.9.1
if (protocolVersion >= MC_1_16_Version) if (protocolVersion >= MC_1_16_Version)
{ {
if (protocolVersion >= MC_1_19_Version) if (protocolVersion >= MC_1_19_Version)
{ dataTypes.ReadNextString(packetData); // Dimension Type: Identifier
dataTypes.ReadNextString(packetData); // Dimension Type: Identifier
currentDimensionType = new Dictionary<string, object>();
}
else if (protocolVersion >= MC_1_16_2_Version) else if (protocolVersion >= MC_1_16_2_Version)
currentDimensionType = dataTypes.ReadNextNbt(packetData); // Dimension Type: NBT Tag Compound dataTypes.ReadNextNbt(packetData); // Dimension Type: NBT Tag Compound
else else
dataTypes.ReadNextString(packetData); dataTypes.ReadNextString(packetData);
this.currentDimension = 0; this.currentDimension = 0;
@ -361,10 +384,10 @@ namespace MinecraftClient.Protocol.Handlers
dataTypes.ReadNextByte(packetData); // Difficulty - 1.13 and below dataTypes.ReadNextByte(packetData); // Difficulty - 1.13 and below
if (protocolVersion >= MC_1_16_Version) if (protocolVersion >= MC_1_16_Version)
currentDimensionName = dataTypes.ReadNextString(packetData); // Dimension Name (World Name) - 1.16 and above {
string dimensionName = dataTypes.ReadNextString(packetData); // Dimension Name (World Name) - 1.16 and above
if (protocolVersion >= MC_1_16_2_Version) World.SetDimension(dimensionName);
World.SetDimension(currentDimensionName, currentDimensionType); }
if (protocolVersion >= MC_1_15_Version) if (protocolVersion >= MC_1_15_Version)
dataTypes.ReadNextLong(packetData); // Hashed world seed - 1.15 and above dataTypes.ReadNextLong(packetData); // Hashed world seed - 1.15 and above
@ -392,8 +415,8 @@ namespace MinecraftClient.Protocol.Handlers
bool hasDeathLocation = dataTypes.ReadNextBool(packetData); // Has death location bool hasDeathLocation = dataTypes.ReadNextBool(packetData); // Has death location
if (hasDeathLocation) if (hasDeathLocation)
{ {
dataTypes.ReadNextString(packetData); // Death dimension name: Identifier dataTypes.ReadNextString(packetData); // Death dimension name: Identifier
dataTypes.ReadNextLocation(packetData); // Death location dataTypes.ReadNextLocation(packetData); // Death location
} }
} }
break; break;
@ -422,7 +445,7 @@ namespace MinecraftClient.Protocol.Handlers
handler.OnTextReceived(new(message, true, messageType, senderUUID)); handler.OnTextReceived(new(message, true, messageType, senderUUID));
} }
else // 1.19+ else if (protocolVersion == MC_1_19_Version) // 1.19
{ {
string signedChat = dataTypes.ReadNextString(packetData); string signedChat = dataTypes.ReadNextString(packetData);
@ -446,38 +469,134 @@ namespace MinecraftClient.Protocol.Handlers
byte[] messageSignature = dataTypes.ReadNextByteArray(packetData); byte[] messageSignature = dataTypes.ReadNextByteArray(packetData);
PlayerInfo? player = handler.GetPlayerInfo(senderUUID); bool verifyResult;
bool verifyResult = player == null ? false : player.VerifyMessage(signedChat, senderUUID, timestamp, salt, ref messageSignature); if (!isOnlineMode)
verifyResult = false;
else if (senderUUID == handler.GetUserUuid())
verifyResult = true;
else
{
PlayerInfo? player = handler.GetPlayerInfo(senderUUID);
verifyResult = player == null ? false : player.VerifyMessage(signedChat, timestamp, salt, ref messageSignature);
}
handler.OnTextReceived(new(signedChat, true, messageType, senderUUID, unsignedChatContent, senderDisplayName, senderTeamName, timestamp, verifyResult)); ChatMessage chat = new(signedChat, true, messageType, senderUUID, unsignedChatContent, senderDisplayName, senderTeamName, timestamp, messageSignature, verifyResult);
handler.OnTextReceived(chat);
}
else // 1.19.1 +
{
byte[]? precedingSignature = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextByteArray(packetData) : null;
Guid senderUUID = dataTypes.ReadNextUUID(packetData);
byte[] headerSignature = dataTypes.ReadNextByteArray(packetData);
string signedChat = dataTypes.ReadNextString(packetData);
string? decorated = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null;
long timestamp = dataTypes.ReadNextLong(packetData);
long salt = dataTypes.ReadNextLong(packetData);
int lastSeenMessageListLen = dataTypes.ReadNextVarInt(packetData);
LastSeenMessageList.Entry[] lastSeenMessageList = new LastSeenMessageList.Entry[lastSeenMessageListLen];
for (int i = 0; i < lastSeenMessageListLen; ++i)
{
Guid user = dataTypes.ReadNextUUID(packetData);
byte[] lastSignature = dataTypes.ReadNextByteArray(packetData);
lastSeenMessageList[i] = new(user, lastSignature);
}
LastSeenMessageList lastSeenMessages = new(lastSeenMessageList);
string? unsignedChatContent = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null;
int filterEnum = dataTypes.ReadNextVarInt(packetData);
if (filterEnum == 2) // PARTIALLY_FILTERED
dataTypes.ReadNextULongArray(packetData);
int chatTypeId = dataTypes.ReadNextVarInt(packetData);
string chatName = dataTypes.ReadNextString(packetData);
string? targetName = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null;
Dictionary<string, Json.JSONData> chatInfo = Json.ParseJson(chatName).Properties;
string senderDisplayName = (chatInfo.ContainsKey("insertion") ? chatInfo["insertion"] : chatInfo["text"]).StringValue;
string? senderTeamName = null;
ChatParser.MessageType messageTypeEnum = ChatParser.ChatId2Type!.GetValueOrDefault(chatTypeId, ChatParser.MessageType.CHAT);
if (targetName != null &&
(messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_INCOMING || messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_OUTGOING))
senderTeamName = Json.ParseJson(targetName).Properties["with"].DataArray[0].Properties["text"].StringValue;
bool verifyResult;
if (!isOnlineMode)
verifyResult = false;
else if (senderUUID == handler.GetUserUuid())
verifyResult = true;
else
{
PlayerInfo? player = handler.GetPlayerInfo(senderUUID);
if (player == null || !player.IsMessageChainLegal())
verifyResult = false;
else
{
bool lastVerifyResult = player.IsMessageChainLegal();
verifyResult = player.VerifyMessage(signedChat, timestamp, salt, ref headerSignature, ref precedingSignature, lastSeenMessages);
if (lastVerifyResult && !verifyResult)
log.Warn(Translations.Get("chat.message_chain_broken", senderDisplayName));
}
}
ChatMessage chat = new(signedChat, false, chatTypeId, senderUUID, unsignedChatContent, senderDisplayName, senderTeamName, timestamp, headerSignature, verifyResult);
if (isOnlineMode && !chat.lacksSender())
this.acknowledge(chat);
handler.OnTextReceived(chat);
}
break;
case PacketTypesIn.MessageHeader:
if (protocolVersion >= MC_1_19_2_Version)
{
byte[]? precedingSignature = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextByteArray(packetData) : null;
Guid senderUUID = dataTypes.ReadNextUUID(packetData);
byte[] headerSignature = dataTypes.ReadNextByteArray(packetData);
byte[] bodyDigest = dataTypes.ReadNextByteArray(packetData);
bool verifyResult;
if (!isOnlineMode)
verifyResult = false;
else if (senderUUID == handler.GetUserUuid())
verifyResult = true;
else
{
PlayerInfo? player = handler.GetPlayerInfo(senderUUID);
if (player == null || !player.IsMessageChainLegal())
verifyResult = false;
else
{
bool lastVerifyResult = player.IsMessageChainLegal();
verifyResult = player.VerifyMessageHead(ref precedingSignature, ref headerSignature, ref bodyDigest);
if (lastVerifyResult && !verifyResult)
log.Warn("Player " + player.DisplayName + "'s message chain is broken!");
}
}
} }
break; break;
case PacketTypesIn.Respawn: case PacketTypesIn.Respawn:
string? dimensionNameInRespawn = null;
Dictionary<string, object> dimensionTypeInRespawn = null;
if (protocolVersion >= MC_1_16_Version) if (protocolVersion >= MC_1_16_Version)
{ {
if (protocolVersion >= MC_1_19_Version) if (protocolVersion >= MC_1_19_Version)
{ dataTypes.ReadNextString(packetData); // Dimension Type: Identifier
dataTypes.ReadNextString(packetData); // Dimension Type: Identifier
dimensionTypeInRespawn = new Dictionary<string, object>();
}
else if (protocolVersion >= MC_1_16_2_Version) else if (protocolVersion >= MC_1_16_2_Version)
dimensionTypeInRespawn = dataTypes.ReadNextNbt(packetData); // Dimension Type: NBT Tag Compound dataTypes.ReadNextNbt(packetData); // Dimension Type: NBT Tag Compound
else else
dataTypes.ReadNextString(packetData); dataTypes.ReadNextString(packetData);
this.currentDimension = 0; this.currentDimension = 0;
} }
else else
{ { // 1.15 and below
// 1.15 and below
this.currentDimension = dataTypes.ReadNextInt(packetData); this.currentDimension = dataTypes.ReadNextInt(packetData);
} }
if (protocolVersion >= MC_1_16_Version)
dimensionNameInRespawn = dataTypes.ReadNextString(packetData); // Dimension Name (World Name) - 1.16 and above
if (protocolVersion >= MC_1_16_2_Version) if (protocolVersion >= MC_1_16_Version)
World.SetDimension(dimensionNameInRespawn, dimensionTypeInRespawn); {
string dimensionName = dataTypes.ReadNextString(packetData); // Dimension Name (World Name) - 1.16 and above
World.SetDimension(dimensionName);
}
if (protocolVersion < MC_1_14_Version) if (protocolVersion < MC_1_14_Version)
dataTypes.ReadNextByte(packetData); // Difficulty - 1.13 and below dataTypes.ReadNextByte(packetData); // Difficulty - 1.13 and below
@ -499,8 +618,8 @@ namespace MinecraftClient.Protocol.Handlers
bool hasDeathLocation = dataTypes.ReadNextBool(packetData); // Has death location bool hasDeathLocation = dataTypes.ReadNextBool(packetData); // Has death location
if (hasDeathLocation) if (hasDeathLocation)
{ {
dataTypes.ReadNextString(packetData); // Death dimension name: Identifier dataTypes.ReadNextString(packetData); // Death dimension name: Identifier
dataTypes.ReadNextLocation(packetData); // Death location dataTypes.ReadNextLocation(packetData); // Death location
} }
} }
handler.OnRespawn(); handler.OnRespawn();
@ -829,6 +948,8 @@ namespace MinecraftClient.Protocol.Handlers
//handler.OnTextReceived(message, true); //handler.OnTextReceived(message, true);
} }
break;
case PacketTypesIn.ChatSuggestions:
break; break;
case PacketTypesIn.MapChunkBulk: case PacketTypesIn.MapChunkBulk:
if (protocolVersion < MC_1_9_Version && handler.GetTerrainEnabled()) if (protocolVersion < MC_1_9_Version && handler.GetTerrainEnabled())
@ -904,14 +1025,20 @@ namespace MinecraftClient.Protocol.Handlers
string name = dataTypes.ReadNextString(packetData); // Player name string name = dataTypes.ReadNextString(packetData); // Player name
int propNum = dataTypes.ReadNextVarInt(packetData); // Number of properties in the following array int propNum = dataTypes.ReadNextVarInt(packetData); // Number of properties in the following array
Tuple<string, string, string>[]? property = null; // Property: Tuple<Name, Value, Signature(empty if there is no signature) // Property: Tuple<Name, Value, Signature(empty if there is no signature)
// The Property field looks as in the response of https://wiki.vg/Mojang_API#UUID_to_Profile_and_Skin.2FCape
const bool useProperty = false;
Tuple<string, string, string?>[]? properties = useProperty ?
new Tuple<string, string, string?>[propNum] : null;
for (int p = 0; p < propNum; p++) for (int p = 0; p < propNum; p++)
{ {
string key = dataTypes.ReadNextString(packetData); // Name string propertyName = dataTypes.ReadNextString(packetData); // Name: String (32767)
string val = dataTypes.ReadNextString(packetData); // Value string val = dataTypes.ReadNextString(packetData); // Value: String (32767)
string? propertySignature = null;
if (dataTypes.ReadNextBool(packetData)) // Is Signed if (dataTypes.ReadNextBool(packetData)) // Is Signed
dataTypes.ReadNextString(packetData); // Signature propertySignature = dataTypes.ReadNextString(packetData); // Signature: String (32767)
if (useProperty)
properties![p] = new(propertyName, val, propertySignature);
} }
int gameMode = dataTypes.ReadNextVarInt(packetData); // Gamemode int gameMode = dataTypes.ReadNextVarInt(packetData); // Gamemode
@ -942,7 +1069,7 @@ namespace MinecraftClient.Protocol.Handlers
} }
} }
handler.OnPlayerJoin(new PlayerInfo(uuid, name, property, gameMode, ping, displayName, keyExpiration, publicKey, signature)); handler.OnPlayerJoin(new PlayerInfo(uuid, name, properties, gameMode, ping, displayName, keyExpiration, publicKey, signature));
break; break;
case 0x01: //Update gamemode case 0x01: //Update gamemode
handler.OnGamemodeUpdate(uuid, dataTypes.ReadNextVarInt(packetData)); handler.OnGamemodeUpdate(uuid, dataTypes.ReadNextVarInt(packetData));
@ -1291,13 +1418,16 @@ namespace MinecraftClient.Protocol.Handlers
int EntityID = dataTypes.ReadNextVarInt(packetData); int EntityID = dataTypes.ReadNextVarInt(packetData);
Dictionary<int, object> metadata = dataTypes.ReadNextMetadata(packetData, itemPalette); Dictionary<int, object> metadata = dataTypes.ReadNextMetadata(packetData, itemPalette);
// See https://wiki.vg/Entity_metadata#Living_Entity int healthField; // See https://wiki.vg/Entity_metadata#Living_Entity
int healthField = 7; // From 1.10 to 1.13.2 if (protocolVersion > MC_1_19_2_Version)
if (protocolVersion >= MC_1_14_Version) throw new NotImplementedException(Translations.Get("exception.palette.healthfield"));
healthField = 8; // 1.14 and above else if (protocolVersion >= MC_1_17_Version) // 1.17 and above
if (protocolVersion >= MC_1_17_Version) healthField = 9;
healthField = 9; // 1.17 and above else if (protocolVersion >= MC_1_14_Version) // 1.14 and above
if (protocolVersion > MC_1_19_Version) healthField = 8;
else if (protocolVersion >= MC_1_10_Version) // 1.10 and above
healthField = 7;
else
throw new NotImplementedException(Translations.Get("exception.palette.healthfield")); throw new NotImplementedException(Translations.Get("exception.palette.healthfield"));
if (metadata.ContainsKey(healthField) && metadata[healthField] != null && metadata[healthField].GetType() == typeof(float)) if (metadata.ContainsKey(healthField) && metadata[healthField] != null && metadata[healthField].GetType() == typeof(float))
@ -1541,13 +1671,27 @@ namespace MinecraftClient.Protocol.Handlers
fullLoginPacket.AddRange(dataTypes.GetBool(true)); // Has Sig Data fullLoginPacket.AddRange(dataTypes.GetBool(true)); // Has Sig Data
fullLoginPacket.AddRange(dataTypes.GetLong(playerKeyPair.GetExpirationMilliseconds())); // Expiration time fullLoginPacket.AddRange(dataTypes.GetLong(playerKeyPair.GetExpirationMilliseconds())); // Expiration time
fullLoginPacket.AddRange(dataTypes.GetArray(playerKeyPair.PublicKey.Key)); // Public key received from Microsoft API fullLoginPacket.AddRange(dataTypes.GetArray(playerKeyPair.PublicKey.Key)); // Public key received from Microsoft API
fullLoginPacket.AddRange(dataTypes.GetArray(playerKeyPair.PublicKey.Signature)); // Public key signature received from Microsoft API if (protocolVersion >= MC_1_19_2_Version)
fullLoginPacket.AddRange(dataTypes.GetArray(playerKeyPair.PublicKey.SignatureV2!)); // Public key signature received from Microsoft API
else
fullLoginPacket.AddRange(dataTypes.GetArray(playerKeyPair.PublicKey.Signature!)); // Public key signature received from Microsoft API
}
}
if (protocolVersion >= MC_1_19_2_Version)
{
Guid uuid = handler.GetUserUuid();
if (uuid == Guid.Empty)
fullLoginPacket.AddRange(dataTypes.GetBool(false)); // Has UUID
else
{
fullLoginPacket.AddRange(dataTypes.GetBool(true)); // Has UUID
fullLoginPacket.AddRange(dataTypes.GetUUID(uuid)); // UUID
} }
} }
SendPacket(0x00, fullLoginPacket); SendPacket(0x00, fullLoginPacket);
int packetID = -1; int packetID = -1;
Queue<byte> packetData = new Queue<byte>(); Queue<byte> packetData = new();
while (true) while (true)
{ {
ReadNextPacket(ref packetID, packetData); ReadNextPacket(ref packetID, packetData);
@ -1558,10 +1702,11 @@ namespace MinecraftClient.Protocol.Handlers
} }
else if (packetID == 0x01) //Encryption request else if (packetID == 0x01) //Encryption request
{ {
this.isOnlineMode = true;
string serverID = dataTypes.ReadNextString(packetData); string serverID = dataTypes.ReadNextString(packetData);
byte[] serverPublicKey = dataTypes.ReadNextByteArray(packetData); byte[] serverPublicKey = dataTypes.ReadNextByteArray(packetData);
byte[] token = dataTypes.ReadNextByteArray(packetData); byte[] token = dataTypes.ReadNextByteArray(packetData);
return StartEncryption(handler.GetUserUUID(), handler.GetSessionID(), token, serverID, serverPublicKey, playerKeyPair); return StartEncryption(handler.GetUserUuidStr(), handler.GetSessionID(), token, serverID, serverPublicKey, playerKeyPair);
} }
else if (packetID == 0x02) //Login successful else if (packetID == 0x02) //Login successful
{ {
@ -1753,7 +1898,7 @@ namespace MinecraftClient.Protocol.Handlers
/// Ping a Minecraft server to get information about the server /// Ping a Minecraft server to get information about the server
/// </summary> /// </summary>
/// <returns>True if ping was successful</returns> /// <returns>True if ping was successful</returns>
public static bool doPing(string host, int port, ref int protocolVersion, ref ForgeInfo? forgeInfo) public static bool doPing(string host, int port, ref int protocolversion, ref ForgeInfo? forgeInfo)
{ {
string version = ""; string version = "";
TcpClient tcp = ProxyHandler.newTcpClient(host, port); TcpClient tcp = ProxyHandler.newTcpClient(host, port);
@ -1805,12 +1950,12 @@ namespace MinecraftClient.Protocol.Handlers
//Retrieve protocol version number for handling this server //Retrieve protocol version number for handling this server
if (versionData.Properties.ContainsKey("protocol")) if (versionData.Properties.ContainsKey("protocol"))
protocolVersion = int.Parse(versionData.Properties["protocol"].StringValue); protocolversion = int.Parse(versionData.Properties["protocol"].StringValue);
// Check for forge on the server. // Check for forge on the server.
Protocol18Forge.ServerInfoCheckForge(jsonData, ref forgeInfo); Protocol18Forge.ServerInfoCheckForge(jsonData, ref forgeInfo);
ConsoleIO.WriteLineFormatted(Translations.Get("mcc.server_protocol", version, protocolVersion + (forgeInfo != null ? Translations.Get("mcc.with_forge") : ""))); ConsoleIO.WriteLineFormatted(Translations.Get("mcc.server_protocol", version, protocolversion + (forgeInfo != null ? Translations.Get("mcc.with_forge") : "")));
return true; return true;
} }
@ -1843,41 +1988,87 @@ namespace MinecraftClient.Protocol.Handlers
return protocolVersion; return protocolVersion;
} }
/// <summary>
/// Send MessageAcknowledgment packet
/// </summary>
/// <param name="acknowledgment">Message acknowledgment</param>
/// <returns>True if properly sent</returns>
public bool SendMessageAcknowledgment(LastSeenMessageList.Acknowledgment acknowledgment)
{
try
{
byte[] fields = dataTypes.GetAcknowledgment(acknowledgment, isOnlineMode);
SendPacket(PacketTypesOut.MessageAcknowledgment, fields);
return true;
}
catch (SocketException) { return false; }
catch (System.IO.IOException) { return false; }
catch (ObjectDisposedException) { return false; }
}
public LastSeenMessageList.Acknowledgment consumeAcknowledgment()
{
this.pendingAcknowledgments = 0;
return new LastSeenMessageList.Acknowledgment(this.lastSeenMessagesCollector.GetLastSeenMessages(), this.lastReceivedMessage);
}
public void acknowledge(ChatMessage message)
{
LastSeenMessageList.Entry? entry = message.toLastSeenMessageEntry();
if (entry != null)
{
lastSeenMessagesCollector.Add(entry);
lastReceivedMessage = null;
if (pendingAcknowledgments++ > 64)
SendMessageAcknowledgment(this.consumeAcknowledgment());
}
}
/// <summary> /// <summary>
/// The signable argument names and their values from command /// The signable argument names and their values from command
/// Signature will used in Vanilla's say, me, msg, teammsg, ban, banip, and kick commands. /// Signature will used in Vanilla's say, me, msg, teammsg, ban, banip, and kick commands.
/// https://gist.github.com/kennytv/ed783dd244ca0321bbd882c347892874#signed-command-arguments /// https://gist.github.com/kennytv/ed783dd244ca0321bbd882c347892874#signed-command-arguments
/// You can find all the commands that need to be signed by searching for "MessageArgumentType.getSignedMessage" in the source code.
/// Don't forget to handle the redirected commands, e.g. /tm, /w
///
/// </summary> /// </summary>
/// <param name="command">Command</param> /// <param name="command">Command</param>
/// <returns> List< Argument Name, Argument Value > </returns> /// <returns> List< Argument Name, Argument Value > </returns>
private List<Tuple<string, string>> collectCommandArguments(string command) private List<Tuple<string, string>>? CollectCommandArguments(string command)
{ {
List<Tuple<string, string>> needSigned = new(); if (!isOnlineMode || !Settings.SignMessageInCommand)
return null;
if (!Settings.SignMessageInCommand) List<Tuple<string, string>> needSigned = new();
return needSigned;
string[] argStage1 = command.Split(' ', 2, StringSplitOptions.None); string[] argStage1 = command.Split(' ', 2, StringSplitOptions.None);
if (argStage1.Length == 2) if (argStage1.Length == 2)
{ {
/* /me <action> /* /me <action>
/say <message> /say <message>
/teammsg <message> */ /teammsg <message>
/tm <message> */
if (argStage1[0] == "me") if (argStage1[0] == "me")
needSigned.Add(new("action", argStage1[1])); needSigned.Add(new("action", argStage1[1]));
else if (argStage1[0] == "say" || argStage1[0] == "teammsg") else if (argStage1[0] == "say" || argStage1[0] == "teammsg" || argStage1[0] == "tm")
needSigned.Add(new("message", argStage1[1])); needSigned.Add(new("message", argStage1[1]));
else if (argStage1[0] == "msg" || argStage1[0] == "ban" || argStage1[0] == "ban-ip" || argStage1[0] == "kick") else if (argStage1[0] == "msg" || argStage1[0] == "tell" || argStage1[0] == "w" ||
argStage1[0] == "ban" || argStage1[0] == "ban-ip" || argStage1[0] == "kick")
{ {
/* /msg <targets> <message> /* /msg <targets> <message>
/tell <targets> <message>
/w <targets> <message>
/ban <target> [<reason>] /ban <target> [<reason>]
/ban-ip <target> [<reason>] /ban-ip <target> [<reason>]
/kick <target> [<reason>] */ /kick <target> [<reason>] */
string[] argStage2 = argStage1[1].Split(' ', 2, StringSplitOptions.None); string[] argStage2 = argStage1[1].Split(' ', 2, StringSplitOptions.None);
if (argStage2.Length == 2) if (argStage2.Length == 2)
{ {
if (argStage1[0] == "msg") if (argStage1[0] == "msg" || argStage1[0] == "tell" || argStage1[0] == "w")
needSigned.Add(new("message", argStage2[1])); needSigned.Add(new("message", argStage2[1]));
else if (argStage1[0] == "ban" || argStage1[0] == "ban-ip" || argStage1[0] == "kick") else if (argStage1[0] == "ban" || argStage1[0] == "ban-ip" || argStage1[0] == "kick")
needSigned.Add(new("reason", argStage2[1])); needSigned.Add(new("reason", argStage2[1]));
@ -1889,7 +2080,7 @@ namespace MinecraftClient.Protocol.Handlers
} }
/// <summary> /// <summary>
/// Send a chat command to the server /// Send a chat command to the server - 1.19 and above
/// </summary> /// </summary>
/// <param name="command">Command</param> /// <param name="command">Command</param>
/// <param name="playerKeyPair">PlayerKeyPair</param> /// <param name="playerKeyPair">PlayerKeyPair</param>
@ -1906,6 +2097,9 @@ namespace MinecraftClient.Protocol.Handlers
try try
{ {
LastSeenMessageList.Acknowledgment? acknowledgment =
(protocolVersion >= MC_1_19_2_Version) ? this.consumeAcknowledgment() : null;
List<byte> fields = new(); List<byte> fields = new();
// Command: String // Command: String
@ -1915,24 +2109,25 @@ namespace MinecraftClient.Protocol.Handlers
DateTimeOffset timeNow = DateTimeOffset.UtcNow; DateTimeOffset timeNow = DateTimeOffset.UtcNow;
fields.AddRange(dataTypes.GetLong(timeNow.ToUnixTimeMilliseconds())); fields.AddRange(dataTypes.GetLong(timeNow.ToUnixTimeMilliseconds()));
List<Tuple<string, string>> needSigned = collectCommandArguments(command); // List< Argument Name, Argument Value > List<Tuple<string, string>>? needSigned =
// foreach (var msg in needSigned) playerKeyPair != null ? CollectCommandArguments(command) : null; // List< Argument Name, Argument Value >
// log.Info("<" + msg.Item1 + ">: " + msg.Item2); if (needSigned == null || needSigned!.Count == 0)
if (needSigned.Count == 0 || playerKeyPair == null || !Settings.SignMessageInCommand)
{ {
fields.AddRange(dataTypes.GetLong(0)); // Salt: Long fields.AddRange(dataTypes.GetLong(0)); // Salt: Long
fields.AddRange(dataTypes.GetVarInt(0)); // Signature Length: VarInt fields.AddRange(dataTypes.GetVarInt(0)); // Signature Length: VarInt
} }
else else
{ {
string uuid = handler.GetUserUUID()!; Guid uuid = handler.GetUserUuid();
byte[] salt = GenerateSalt(); byte[] salt = GenerateSalt();
fields.AddRange(salt); // Salt: Long fields.AddRange(salt); // Salt: Long
fields.AddRange(dataTypes.GetVarInt(needSigned.Count)); // Signature Length: VarInt fields.AddRange(dataTypes.GetVarInt(needSigned.Count)); // Signature Length: VarInt
foreach (var argument in needSigned) foreach (var argument in needSigned)
{ {
fields.AddRange(dataTypes.GetString(argument.Item1)); // Argument name: String fields.AddRange(dataTypes.GetString(argument.Item1)); // Argument name: String
byte[] sign = playerKeyPair.PrivateKey.SignMessage(argument.Item2, uuid, timeNow, ref salt); byte[] sign = (protocolVersion >= MC_1_19_2_Version) ?
playerKeyPair!.PrivateKey.SignMessage(argument.Item2, uuid, timeNow, ref salt, acknowledgment!.lastSeen) :
playerKeyPair!.PrivateKey.SignMessage(argument.Item2, uuid, timeNow, ref salt);
fields.AddRange(dataTypes.GetVarInt(sign.Length)); // Signature length: VarInt fields.AddRange(dataTypes.GetVarInt(sign.Length)); // Signature length: VarInt
fields.AddRange(sign); // Signature: Byte Array fields.AddRange(sign); // Signature: Byte Array
} }
@ -1941,6 +2136,12 @@ namespace MinecraftClient.Protocol.Handlers
// Signed Preview: Boolean // Signed Preview: Boolean
fields.AddRange(dataTypes.GetBool(false)); fields.AddRange(dataTypes.GetBool(false));
if (protocolVersion >= MC_1_19_2_Version)
{
// Message Acknowledgment
fields.AddRange(dataTypes.GetAcknowledgment(acknowledgment!, isOnlineMode));
}
SendPacket(PacketTypesOut.ChatCommand, fields); SendPacket(PacketTypesOut.ChatCommand, fields);
return true; return true;
} }
@ -1973,11 +2174,14 @@ namespace MinecraftClient.Protocol.Handlers
if (protocolVersion >= MC_1_19_Version) if (protocolVersion >= MC_1_19_Version)
{ {
LastSeenMessageList.Acknowledgment? acknowledgment =
(protocolVersion >= MC_1_19_2_Version) ? this.consumeAcknowledgment() : null;
// Timestamp: Instant(Long) // Timestamp: Instant(Long)
DateTimeOffset timeNow = DateTimeOffset.UtcNow; DateTimeOffset timeNow = DateTimeOffset.UtcNow;
fields.AddRange(dataTypes.GetLong(timeNow.ToUnixTimeMilliseconds())); fields.AddRange(dataTypes.GetLong(timeNow.ToUnixTimeMilliseconds()));
if (playerKeyPair == null || !Settings.SignChat) if (!isOnlineMode || playerKeyPair == null || !Settings.SignChat)
{ {
fields.AddRange(dataTypes.GetLong(0)); // Salt: Long fields.AddRange(dataTypes.GetLong(0)); // Salt: Long
fields.AddRange(dataTypes.GetVarInt(0)); // Signature Length: VarInt fields.AddRange(dataTypes.GetVarInt(0)); // Signature Length: VarInt
@ -1989,14 +2193,22 @@ namespace MinecraftClient.Protocol.Handlers
fields.AddRange(salt); fields.AddRange(salt);
// Signature Length & Signature: (VarInt) and Byte Array // Signature Length & Signature: (VarInt) and Byte Array
string uuid = handler.GetUserUUID()!; Guid uuid = handler.GetUserUuid();
byte[] sign = playerKeyPair.PrivateKey.SignMessage(message, uuid, timeNow, ref salt); byte[] sign = (protocolVersion >= MC_1_19_2_Version) ?
playerKeyPair.PrivateKey.SignMessage(message, uuid, timeNow, ref salt, acknowledgment!.lastSeen) :
playerKeyPair.PrivateKey.SignMessage(message, uuid, timeNow, ref salt);
fields.AddRange(dataTypes.GetVarInt(sign.Length)); fields.AddRange(dataTypes.GetVarInt(sign.Length));
fields.AddRange(sign); fields.AddRange(sign);
} }
// Signed Preview: Boolean // Signed Preview: Boolean
fields.AddRange(dataTypes.GetBool(false)); fields.AddRange(dataTypes.GetBool(false));
if (protocolVersion >= MC_1_19_2_Version)
{
// Message Acknowledgment
fields.AddRange(dataTypes.GetAcknowledgment(acknowledgment!, isOnlineMode));
}
} }
SendPacket(PacketTypesOut.ChatMessage, fields); SendPacket(PacketTypesOut.ChatMessage, fields);
return true; return true;
@ -2077,9 +2289,12 @@ namespace MinecraftClient.Protocol.Handlers
List<byte> fields = new List<byte>(); List<byte> fields = new List<byte>();
fields.AddRange(dataTypes.GetString(language)); fields.AddRange(dataTypes.GetString(language));
fields.Add(viewDistance); fields.Add(viewDistance);
fields.AddRange(protocolVersion >= MC_1_9_Version
? dataTypes.GetVarInt(chatMode) if (protocolVersion >= MC_1_9_Version)
: new byte[] { chatMode }); fields.AddRange(dataTypes.GetVarInt(chatMode));
else
fields.AddRange(new byte[] { chatMode });
fields.Add(chatColors ? (byte)1 : (byte)0); fields.Add(chatColors ? (byte)1 : (byte)0);
if (protocolVersion < MC_1_8_Version) if (protocolVersion < MC_1_8_Version)
{ {

View file

@ -22,7 +22,7 @@ namespace MinecraftClient.Protocol
/// Start the login procedure once connected to the server /// Start the login procedure once connected to the server
/// </summary> /// </summary>
/// <returns>True if login was successful</returns> /// <returns>True if login was successful</returns>
bool Login(PlayerKeyPair playerKeyPair); bool Login(PlayerKeyPair? playerKeyPair);
/// <summary> /// <summary>
/// Disconnect from the server /// Disconnect from the server

View file

@ -5,6 +5,7 @@ using System.Text;
using MinecraftClient.Mapping; using MinecraftClient.Mapping;
using MinecraftClient.Inventory; using MinecraftClient.Inventory;
using MinecraftClient.Logger; using MinecraftClient.Logger;
using MinecraftClient.Protocol.Message;
namespace MinecraftClient.Protocol namespace MinecraftClient.Protocol
{ {
@ -22,7 +23,8 @@ namespace MinecraftClient.Protocol
int GetServerPort(); int GetServerPort();
string GetServerHost(); string GetServerHost();
string GetUsername(); string GetUsername();
string GetUserUUID(); Guid GetUserUuid();
string GetUserUuidStr();
string GetSessionID(); string GetSessionID();
string[] GetOnlinePlayers(); string[] GetOnlinePlayers();
Dictionary<string, string> GetOnlinePlayersWithUUID(); Dictionary<string, string> GetOnlinePlayersWithUUID();

View file

@ -4,7 +4,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace MinecraftClient.Protocol namespace MinecraftClient.Protocol.Message
{ {
public class ChatMessage public class ChatMessage
{ {
@ -17,7 +17,7 @@ namespace MinecraftClient.Protocol
// 0: chat (chat box), 1: system message (chat box), 2: game info (above hotbar), 3: say command, // 0: chat (chat box), 1: system message (chat box), 2: game info (above hotbar), 3: say command,
// 4: msg command, 5: team msg command, 6: emote command, 7: tellraw command // 4: msg command, 5: team msg command, 6: emote command, 7: tellraw command
public readonly int chatType; public readonly int chatTypeId;
public readonly Guid senderUUID; public readonly Guid senderUUID;
@ -31,31 +31,44 @@ namespace MinecraftClient.Protocol
public readonly DateTime? timestamp; public readonly DateTime? timestamp;
public readonly byte[]? signature;
public readonly bool? isSignatureLegal; public readonly bool? isSignatureLegal;
public ChatMessage(string content, bool isJson, int chatType, Guid senderUUID, string? unsignedContent, string displayName, string? teamName, long timestamp, bool isSignatureLegal) public ChatMessage(string content, bool isJson, int chatType, Guid senderUUID, string? unsignedContent, string displayName, string? teamName, long timestamp, byte[] signature, bool isSignatureLegal)
{ {
this.isSignedChat = true; isSignedChat = true;
this.isSystemChat = false; isSystemChat = false;
this.content = content; this.content = content;
this.isJson = isJson; this.isJson = isJson;
this.chatType = chatType; this.chatTypeId = chatType;
this.senderUUID = senderUUID; this.senderUUID = senderUUID;
this.unsignedContent = unsignedContent; this.unsignedContent = unsignedContent;
this.displayName = displayName; this.displayName = displayName;
this.teamName = teamName; this.teamName = teamName;
this.timestamp = DateTimeOffset.FromUnixTimeMilliseconds(timestamp).DateTime; this.timestamp = DateTimeOffset.FromUnixTimeMilliseconds(timestamp).DateTime;
this.signature = signature;
this.isSignatureLegal = isSignatureLegal; this.isSignatureLegal = isSignatureLegal;
} }
public ChatMessage(string content, bool isJson, int chatType, Guid senderUUID, bool isSystemChat = false) public ChatMessage(string content, bool isJson, int chatType, Guid senderUUID, bool isSystemChat = false)
{ {
this.isSignedChat = isSystemChat; isSignedChat = isSystemChat;
this.isSystemChat = isSystemChat; this.isSystemChat = isSystemChat;
this.content = content; this.content = content;
this.isJson = isJson; this.isJson = isJson;
this.chatType = chatType; this.chatTypeId = chatType;
this.senderUUID = senderUUID; this.senderUUID = senderUUID;
} }
public LastSeenMessageList.Entry? toLastSeenMessageEntry()
{
return signature != null ? new LastSeenMessageList.Entry(senderUUID, signature) : null;
}
public bool lacksSender()
{
return this.senderUUID == Guid.Empty;
}
} }
} }

View file

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using MinecraftClient.Protocol.Message;
namespace MinecraftClient.Protocol namespace MinecraftClient.Protocol
{ {
@ -12,6 +13,20 @@ namespace MinecraftClient.Protocol
static class ChatParser static class ChatParser
{ {
public enum MessageType
{
CHAT,
SAY_COMMAND,
MSG_COMMAND_INCOMING,
MSG_COMMAND_OUTGOING,
TEAM_MSG_COMMAND_INCOMING,
TEAM_MSG_COMMAND_OUTGOING,
EMOTE_COMMAND,
RAW_MSG
};
public static Dictionary<int, MessageType>? ChatId2Type;
/// <summary> /// <summary>
/// The main function to convert text from MC 1.6+ JSON to MC 1.5.2 formatted text /// The main function to convert text from MC 1.6+ JSON to MC 1.5.2 formatted text
/// </summary> /// </summary>
@ -31,60 +46,68 @@ namespace MinecraftClient.Protocol
/// <returns>Returns the translated text</returns> /// <returns>Returns the translated text</returns>
public static string ParseSignedChat(ChatMessage message, List<string>? links = null) public static string ParseSignedChat(ChatMessage message, List<string>? links = null)
{ {
string content; string chatContent = Settings.ShowModifiedChat && message.unsignedContent != null ? message.unsignedContent : message.content;
if (Settings.ShowModifiedChat && message.unsignedContent != null) string content = message.isJson ? ParseText(chatContent, links) : chatContent;
content = ChatParser.ParseText(message.unsignedContent, links);
else
content = ChatParser.ParseText(message.content, links);
string sender = message.displayName!; string sender = message.displayName!;
string text; string text;
List<string> usingData = new(); List<string> usingData = new();
switch (message.chatType)
MessageType chatType;
if (message.isSystemChat)
chatType = MessageType.RAW_MSG;
else if (!ChatId2Type!.TryGetValue(message.chatTypeId, out chatType))
chatType = MessageType.CHAT;
switch (chatType)
{ {
case 0: // chat (chat box) case MessageType.CHAT:
usingData.Add(sender); usingData.Add(sender);
usingData.Add(content); usingData.Add(content);
text = TranslateString("chat.type.text", usingData); text = TranslateString("chat.type.text", usingData);
break; break;
case 1: // system message (chat box) case MessageType.SAY_COMMAND:
text = content;
break;
case 2: // game info (above hotbar)
text = content;
break;
case 3: // say command
usingData.Add(sender); usingData.Add(sender);
usingData.Add(content); usingData.Add(content);
text = TranslateString("chat.type.announcement", usingData); text = TranslateString("chat.type.announcement", usingData);
break; break;
case 4: // msg command case MessageType.MSG_COMMAND_INCOMING:
usingData.Add(sender); usingData.Add(sender);
usingData.Add(content); usingData.Add(content);
text = TranslateString("commands.message.display.incoming", usingData); text = TranslateString("commands.message.display.incoming", usingData);
break; break;
case 5: // team msg command (/teammsg) case MessageType.MSG_COMMAND_OUTGOING:
usingData.Add(sender);
usingData.Add(content);
text = TranslateString("commands.message.display.outgoing", usingData);
break;
case MessageType.TEAM_MSG_COMMAND_INCOMING:
usingData.Add(message.teamName!); usingData.Add(message.teamName!);
usingData.Add(sender); usingData.Add(sender);
usingData.Add(content); usingData.Add(content);
text = TranslateString("chat.type.team.text", usingData); text = TranslateString("chat.type.team.text", usingData);
break; break;
case 6: // emote command (/me) case MessageType.TEAM_MSG_COMMAND_OUTGOING:
usingData.Add(message.teamName!);
usingData.Add(sender);
usingData.Add(content);
text = TranslateString("chat.type.team.sent", usingData);
break;
case MessageType.EMOTE_COMMAND:
usingData.Add(sender); usingData.Add(sender);
usingData.Add(content); usingData.Add(content);
text = TranslateString("chat.type.emote", usingData); text = TranslateString("chat.type.emote", usingData);
break; break;
case 7: // tellraw command case MessageType.RAW_MSG:
text = content; text = content;
break; break;
default: default:
text = $"{sender}: {content}"; goto case MessageType.CHAT;
break;
} }
string color = String.Empty; string color = string.Empty;
if (message.isSystemChat) if (message.isSystemChat)
{ {
if (Settings.MarkSystemMessage) if (Settings.MarkSystemMessage)
color = "§z §r "; // Custom: Background Gray color = "§z §r "; // Custom color code §z : Background Gray
} }
else else
{ {
@ -93,18 +116,18 @@ namespace MinecraftClient.Protocol
if (Settings.ShowModifiedChat && message.unsignedContent != null) if (Settings.ShowModifiedChat && message.unsignedContent != null)
{ {
if (Settings.MarkModifiedMsg) if (Settings.MarkModifiedMsg)
color = "§x §r "; // Custom: Background Yellow color = "§x §r "; // Custom color code §x : Background Yellow
} }
else else
{ {
if (Settings.MarkLegallySignedMsg) if (Settings.MarkLegallySignedMsg)
color = "§y §r "; // Custom: Background Green color = "§y §r "; // Custom color code §y : Background Green
} }
} }
else else
{ {
if (Settings.MarkIllegallySignedMsg) if (Settings.MarkIllegallySignedMsg)
color = "§w §r "; // Custom: Background Red color = "§w §r "; // Custom color code §w : Background Red
} }
} }
return color + text; return color + text;
@ -172,13 +195,13 @@ namespace MinecraftClient.Protocol
TranslationRules["commands.message.display.outgoing"] = "§7You whisper to %s: %s"; TranslationRules["commands.message.display.outgoing"] = "§7You whisper to %s: %s";
//Language file in a subfolder, depending on the language setting //Language file in a subfolder, depending on the language setting
if (!System.IO.Directory.Exists("lang")) if (!Directory.Exists("lang"))
System.IO.Directory.CreateDirectory("lang"); Directory.CreateDirectory("lang");
string Language_File = "lang" + Path.DirectorySeparatorChar + Settings.Language + ".lang"; string Language_File = "lang" + Path.DirectorySeparatorChar + Settings.Language + ".lang";
//File not found? Try downloading language file from Mojang's servers? //File not found? Try downloading language file from Mojang's servers?
if (!System.IO.File.Exists(Language_File)) if (!File.Exists(Language_File))
{ {
ConsoleIO.WriteLineFormatted(Translations.Get("chat.download", Settings.Language)); ConsoleIO.WriteLineFormatted(Translations.Get("chat.download", Settings.Language));
try try
@ -197,7 +220,7 @@ namespace MinecraftClient.Protocol
stringBuilder.Append(entry.Key + "=" + entry.Value.StringValue + Environment.NewLine); stringBuilder.Append(entry.Key + "=" + entry.Value.StringValue + Environment.NewLine);
} }
System.IO.File.WriteAllText(Language_File, stringBuilder.ToString()); File.WriteAllText(Language_File, stringBuilder.ToString());
ConsoleIO.WriteLineFormatted(Translations.Get("chat.done", Language_File)); ConsoleIO.WriteLineFormatted(Translations.Get("chat.done", Language_File));
} }
catch catch
@ -207,17 +230,17 @@ namespace MinecraftClient.Protocol
} }
//Download Failed? Defaulting to en_GB.lang if the game is installed //Download Failed? Defaulting to en_GB.lang if the game is installed
if (!System.IO.File.Exists(Language_File) //Try en_GB.lang if (!File.Exists(Language_File) //Try en_GB.lang
&& System.IO.File.Exists(Settings.TranslationsFile_FromMCDir)) && File.Exists(Settings.TranslationsFile_FromMCDir))
{ {
Language_File = Settings.TranslationsFile_FromMCDir; Language_File = Settings.TranslationsFile_FromMCDir;
Translations.WriteLineFormatted("chat.from_dir"); Translations.WriteLineFormatted("chat.from_dir");
} }
//Load the external dictionnary of translation rules or display an error message //Load the external dictionnary of translation rules or display an error message
if (System.IO.File.Exists(Language_File)) if (File.Exists(Language_File))
{ {
string[] translations = System.IO.File.ReadAllLines(Language_File); string[] translations = File.ReadAllLines(Language_File);
foreach (string line in translations) foreach (string line in translations)
{ {
if (line.Length > 0) if (line.Length > 0)
@ -289,7 +312,7 @@ namespace MinecraftClient.Protocol
} }
return result.ToString(); return result.ToString();
} }
else return "[" + rulename + "] " + String.Join(" ", using_data); else return "[" + rulename + "] " + string.Join(" ", using_data);
} }
/// <summary> /// <summary>
@ -315,11 +338,11 @@ namespace MinecraftClient.Protocol
if (clickEvent.Properties.ContainsKey("action") if (clickEvent.Properties.ContainsKey("action")
&& clickEvent.Properties.ContainsKey("value") && clickEvent.Properties.ContainsKey("value")
&& clickEvent.Properties["action"].StringValue == "open_url" && clickEvent.Properties["action"].StringValue == "open_url"
&& !String.IsNullOrEmpty(clickEvent.Properties["value"].StringValue)) && !string.IsNullOrEmpty(clickEvent.Properties["value"].StringValue))
{ {
links.Add(clickEvent.Properties["value"].StringValue); links.Add(clickEvent.Properties["value"].StringValue);
} }
} }
if (data.Properties.ContainsKey("extra")) if (data.Properties.ContainsKey("extra"))
{ {
Json.JSONData[] extras = data.Properties["extra"].DataArray.ToArray(); Json.JSONData[] extras = data.Properties["extra"].DataArray.ToArray();
@ -372,7 +395,7 @@ namespace MinecraftClient.Protocol
System.Net.HttpWebRequest myRequest = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(url); System.Net.HttpWebRequest myRequest = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(url);
myRequest.Method = "GET"; myRequest.Method = "GET";
System.Net.WebResponse myResponse = myRequest.GetResponse(); System.Net.WebResponse myResponse = myRequest.GetResponse();
System.IO.StreamReader sr = new System.IO.StreamReader(myResponse.GetResponseStream(), System.Text.Encoding.UTF8); StreamReader sr = new StreamReader(myResponse.GetResponseStream(), Encoding.UTF8);
string result = sr.ReadToEnd(); string result = sr.ReadToEnd();
sr.Close(); sr.Close();
myResponse.Close(); myResponse.Close();

View file

@ -0,0 +1,117 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MinecraftClient.Protocol.Message
{
/// <summary>
/// A list of messages a client has seen.
/// </summary>
public class LastSeenMessageList
{
public static readonly LastSeenMessageList EMPTY = new(new Entry[0]);
public static readonly int MAX_ENTRIES = 5;
public Entry[] entries;
public LastSeenMessageList(Entry[] list)
{
entries = list;
}
public void WriteForSign(List<byte> data)
{
foreach (Entry entry in entries)
{
data.Add(70);
data.AddRange(entry.profileId.ToBigEndianBytes());
data.AddRange(entry.lastSignature);
}
}
/// <summary>
/// A pair of a player's UUID and the signature of the last message they saw, used as an entry of LastSeenMessageList.
/// </summary>
public class Entry
{
public Guid profileId;
public byte[] lastSignature;
public Entry(Guid profileId, byte[] lastSignature)
{
this.profileId = profileId;
this.lastSignature = lastSignature;
}
}
/// <summary>
/// A record of messages acknowledged by a client.
/// This holds the messages the client has recently seen, as well as the last message they received, if any.
/// </summary>
public class Acknowledgment
{
public LastSeenMessageList lastSeen;
public Entry? lastReceived;
public Acknowledgment(LastSeenMessageList lastSeenMessageList, Entry? lastReceivedMessage)
{
lastSeen = lastSeenMessageList;
lastReceived = lastReceivedMessage;
}
}
}
/// <summary>
/// Collects the message that are last seen by a client.
/// The message, along with the "last received" message, forms an "acknowledgment" of received messages.
/// They are sent to the server when the client has enough messages received or when they send a message.
/// The maximum amount of message entries are specified in the constructor.The vanilla clients collect 5 entries.
/// Calling add adds the message to the beginning of the entries list, and evicts the oldest message.
/// If there are entries with the same sender profile ID, the older entry will be replaced with null instead of filling the hole.
/// </summary>
public class LastSeenMessagesCollector
{
private readonly LastSeenMessageList.Entry[] entries;
private int size = 0;
private LastSeenMessageList lastSeenMessages;
public LastSeenMessagesCollector(int size)
{
lastSeenMessages = LastSeenMessageList.EMPTY;
entries = new LastSeenMessageList.Entry[size];
}
public void Add(LastSeenMessageList.Entry entry)
{
LastSeenMessageList.Entry? lastEntry = entry;
for (int i = 0; i < size; ++i)
{
LastSeenMessageList.Entry curEntry = entries[i];
entries[i] = lastEntry;
lastEntry = curEntry;
if (curEntry.profileId == entry.profileId)
{
lastEntry = null;
break;
}
}
if (lastEntry != null && size < entries.Length)
entries[size++] = lastEntry;
LastSeenMessageList.Entry[] msgList = new LastSeenMessageList.Entry[size];
for (int i = 0; i < size; ++i)
msgList[i] = entries[i];
lastSeenMessages = new LastSeenMessageList(msgList);
}
public LastSeenMessageList GetLastSeenMessages()
{
return lastSeenMessages;
}
}
}

View file

@ -4,17 +4,18 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using MinecraftClient.Protocol.Keys; using MinecraftClient.Protocol.Keys;
using MinecraftClient.Protocol.Message;
namespace MinecraftClient.Protocol namespace MinecraftClient.Protocol
{ {
public class PlayerInfo public class PlayerInfo
{ {
public readonly Guid UUID; public readonly Guid Uuid;
public readonly string Name; public readonly string Name;
// Tuple<Name, Value, Signature(empty if there is no signature) // Tuple<Name, Value, Signature(empty if there is no signature)
public readonly Tuple<string, string, string>[]? Property; public readonly Tuple<string, string, string?>[]? Property;
public int Gamemode; public int Gamemode;
@ -26,9 +27,13 @@ namespace MinecraftClient.Protocol
private readonly DateTime? KeyExpiresAt; private readonly DateTime? KeyExpiresAt;
public PlayerInfo(Guid uuid, string name, Tuple<string, string, string>[]? property, int gamemode, int ping, string? displayName, long? timeStamp, byte[]? publicKey, byte[]? signature) private bool lastMessageVerified;
private byte[]? precedingSignature;
public PlayerInfo(Guid uuid, string name, Tuple<string, string, string?>[]? property, int gamemode, int ping, string? displayName, long? timeStamp, byte[]? publicKey, byte[]? signature)
{ {
UUID = uuid; Uuid = uuid;
Name = name; Name = name;
if (property != null) if (property != null)
Property = property; Property = property;
@ -48,36 +53,107 @@ namespace MinecraftClient.Protocol
PublicKey = null; PublicKey = null;
} }
} }
lastMessageVerified = true;
precedingSignature = null;
} }
public PlayerInfo(string name, Guid uuid) public PlayerInfo(string name, Guid uuid)
{ {
Name = name; Name = name;
UUID = uuid; Uuid = uuid;
Gamemode = -1; Gamemode = -1;
Ping = 0; Ping = 0;
lastMessageVerified = true;
precedingSignature = null;
} }
public bool IsKeyVaild() public bool IsMessageChainLegal()
{ {
return PublicKey != null && DateTime.Now.ToUniversalTime() > this.KeyExpiresAt; return this.lastMessageVerified;
} }
public bool VerifyMessage(string message, Guid uuid, long timestamp, long salt, ref byte[] signature) public bool IsKeyExpired()
{ {
if (PublicKey == null) return DateTime.Now.ToUniversalTime() > this.KeyExpiresAt;
}
/// <summary>
/// Verify message - 1.19
/// </summary>
/// <param name="message">Message content</param>
/// <param name="timestamp">Timestamp</param>
/// <param name="salt">Salt</param>
/// <param name="signature">Message signature</param>
/// <returns>Is this message vaild</returns>
public bool VerifyMessage(string message, long timestamp, long salt, ref byte[] signature)
{
if (PublicKey == null || IsKeyExpired())
return false; return false;
else else
{ {
string uuidString = uuid.ToString().Replace("-", string.Empty);
DateTimeOffset timeOffset = DateTimeOffset.FromUnixTimeMilliseconds(timestamp); DateTimeOffset timeOffset = DateTimeOffset.FromUnixTimeMilliseconds(timestamp);
byte[] saltByte = BitConverter.GetBytes(salt); byte[] saltByte = BitConverter.GetBytes(salt);
Array.Reverse(saltByte); Array.Reverse(saltByte);
return PublicKey.VerifyMessage(message, uuidString, timeOffset, ref saltByte, ref signature); return PublicKey.VerifyMessage(message, Uuid, timeOffset, ref saltByte, ref signature);
} }
} }
/// <summary>
/// Verify message - 1.19.1 and above
/// </summary>
/// <param name="message">Message content</param>
/// <param name="timestamp">Timestamp</param>
/// <param name="salt">Salt</param>
/// <param name="signature">Message signature</param>
/// <param name="precedingSignature">Preceding message signature</param>
/// <param name="lastSeenMessages">LastSeenMessages</param>
/// <returns>Is this message chain vaild</returns>
public bool VerifyMessage(string message, long timestamp, long salt, ref byte[] signature, ref byte[]? precedingSignature, LastSeenMessageList lastSeenMessages)
{
if (this.lastMessageVerified == false)
return false;
if (PublicKey == null || IsKeyExpired() || (this.precedingSignature != null && precedingSignature == null))
return false;
if (this.precedingSignature != null && !this.precedingSignature.SequenceEqual(precedingSignature!))
return false;
DateTimeOffset timeOffset = DateTimeOffset.FromUnixTimeMilliseconds(timestamp);
byte[] saltByte = BitConverter.GetBytes(salt);
Array.Reverse(saltByte);
bool res = PublicKey.VerifyMessage(message, Uuid, timeOffset, ref saltByte, ref signature, ref precedingSignature, lastSeenMessages);
this.lastMessageVerified = res;
this.precedingSignature = signature;
return res;
}
/// <summary>
/// Verify message head - 1.19.1 and above
/// </summary>
/// <param name="precedingSignature">Preceding message signature</param>
/// <param name="headerSignature">Message signature</param>
/// <param name="bodyDigest">Message body hash</param>
/// <returns>Is this message chain vaild</returns>
public bool VerifyMessageHead(ref byte[]? precedingSignature, ref byte[] headerSignature, ref byte[] bodyDigest)
{
if (this.lastMessageVerified == false)
return false;
if (PublicKey == null || IsKeyExpired() || (this.precedingSignature != null && precedingSignature == null))
return false;
if (this.precedingSignature != null && !this.precedingSignature.SequenceEqual(precedingSignature!))
return false;
bool res = PublicKey.VerifyHeader(ref bodyDigest, ref headerSignature);
this.lastMessageVerified = res;
this.precedingSignature = headerSignature;
return res;
}
} }
} }

View file

@ -1,14 +1,19 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Security.Cryptography;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using MinecraftClient.Protocol.Handlers;
using MinecraftClient.Protocol.Message;
namespace MinecraftClient.Protocol.Keys namespace MinecraftClient.Protocol.Keys
{ {
static class KeyUtils static class KeyUtils
{ {
private static string certificates = "https://api.minecraftservices.com/player/certificates"; private static readonly SHA256 sha256Hash = SHA256.Create();
private static readonly string certificates = "https://api.minecraftservices.com/player/certificates";
public static PlayerKeyPair? GetKeys(string accessToken) public static PlayerKeyPair? GetKeys(string accessToken)
{ {
@ -67,19 +72,18 @@ namespace MinecraftClient.Protocol.Keys
return Convert.FromBase64String(key); return Convert.FromBase64String(key);
} }
public static byte[] GetSignatureData(string message, string uuid, DateTimeOffset timestamp, ref byte[] salt) public static byte[] ComputeHash(byte[] data)
{
return sha256Hash.ComputeHash(data);
}
public static byte[] GetSignatureData(string message, Guid uuid, DateTimeOffset timestamp, ref byte[] salt)
{ {
List<byte> data = new(); List<byte> data = new();
data.AddRange(salt); data.AddRange(salt);
byte[] UUIDLeastSignificantBits = BitConverter.GetBytes(Convert.ToInt64(uuid[..16], 16)); data.AddRange(uuid.ToBigEndianBytes());
Array.Reverse(UUIDLeastSignificantBits);
data.AddRange(UUIDLeastSignificantBits);
byte[] UUIDMostSignificantBits = BitConverter.GetBytes(Convert.ToInt64(uuid.Substring(16, 16), 16));
Array.Reverse(UUIDMostSignificantBits);
data.AddRange(UUIDMostSignificantBits);
byte[] timestampByte = BitConverter.GetBytes(timestamp.ToUnixTimeSeconds()); byte[] timestampByte = BitConverter.GetBytes(timestamp.ToUnixTimeSeconds());
Array.Reverse(timestampByte); Array.Reverse(timestampByte);
@ -90,6 +94,39 @@ namespace MinecraftClient.Protocol.Keys
return data.ToArray(); return data.ToArray();
} }
public static byte[] GetSignatureData(string message, DateTimeOffset timestamp, ref byte[] salt, LastSeenMessageList lastSeenMessages)
{
List<byte> data = new();
data.AddRange(salt);
byte[] timestampByte = BitConverter.GetBytes(timestamp.ToUnixTimeSeconds());
Array.Reverse(timestampByte);
data.AddRange(timestampByte);
data.AddRange(Encoding.UTF8.GetBytes(message));
data.Add(70);
lastSeenMessages.WriteForSign(data);
return data.ToArray();
}
public static byte[] GetSignatureData(byte[]? precedingSignature, Guid sender, byte[] bodySign)
{
List<byte> data = new();
if (precedingSignature != null)
data.AddRange(precedingSignature);
data.AddRange(sender.ToBigEndianBytes());
data.AddRange(bodySign);
return data.ToArray();
}
// https://github.com/mono/mono/blob/master/mcs/class/System.Json/System.Json/JsonValue.cs // https://github.com/mono/mono/blob/master/mcs/class/System.Json/System.Json/JsonValue.cs
public static string EscapeString(string src) public static string EscapeString(string src)
{ {

View file

@ -65,7 +65,10 @@ namespace MinecraftClient.Protocol.Keys
{ {
List<string> datas = new List<string>(); List<string> datas = new List<string>();
datas.Add(Convert.ToBase64String(PublicKey.Key)); datas.Add(Convert.ToBase64String(PublicKey.Key));
datas.Add(Convert.ToBase64String(PublicKey.Signature)); if (PublicKey.Signature == null)
datas.Add(String.Empty);
else
datas.Add(Convert.ToBase64String(PublicKey.Signature));
if (PublicKey.SignatureV2 == null) if (PublicKey.SignatureV2 == null)
datas.Add(String.Empty); datas.Add(String.Empty);
else else

View file

@ -4,6 +4,7 @@ using System.Linq;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using MinecraftClient.Protocol.Message;
namespace MinecraftClient.Protocol.Keys namespace MinecraftClient.Protocol.Keys
{ {
@ -13,6 +14,8 @@ namespace MinecraftClient.Protocol.Keys
private readonly RSA rsa; private readonly RSA rsa;
private byte[]? precedingSignature = null;
public PrivateKey(string pemKey) public PrivateKey(string pemKey)
{ {
this.Key = KeyUtils.DecodePemKey(pemKey, "-----BEGIN RSA PRIVATE KEY-----", "-----END RSA PRIVATE KEY-----"); this.Key = KeyUtils.DecodePemKey(pemKey, "-----BEGIN RSA PRIVATE KEY-----", "-----END RSA PRIVATE KEY-----");
@ -26,7 +29,15 @@ namespace MinecraftClient.Protocol.Keys
return rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); return rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
} }
public byte[] SignMessage(string message, string uuid, DateTimeOffset timestamp, ref byte[] salt) /// <summary>
/// Sign message - 1.19
/// </summary>
/// <param name="message">Message content</param>
/// <param name="uuid">Sender uuid</param>
/// <param name="timestamp">Timestamp</param>
/// <param name="salt">Salt</param>
/// <returns>Signature data</returns>
public byte[] SignMessage(string message, Guid uuid, DateTimeOffset timestamp, ref byte[] salt)
{ {
string messageJson = "{\"text\":\"" + KeyUtils.EscapeString(message) + "\"}"; string messageJson = "{\"text\":\"" + KeyUtils.EscapeString(message) + "\"}";
@ -35,5 +46,27 @@ namespace MinecraftClient.Protocol.Keys
return SignData(data); return SignData(data);
} }
/// <summary>
/// Sign message - 1.19.1 and above
/// </summary>
/// <param name="message">Message content</param>
/// <param name="uuid">Sender uuid</param>
/// <param name="timestamp">Timestamp</param>
/// <param name="salt">Salt</param>
/// <param name="lastSeenMessages">LastSeenMessageList</param>
/// <returns>Signature data</returns>
public byte[] SignMessage(string message, Guid uuid, DateTimeOffset timestamp, ref byte[] salt, LastSeenMessageList lastSeenMessages)
{
byte[] bodySignData = KeyUtils.GetSignatureData(message, timestamp, ref salt, lastSeenMessages);
byte[] bodyDigest = KeyUtils.ComputeHash(bodySignData);
byte[] msgSignData = KeyUtils.GetSignatureData(precedingSignature, uuid, bodyDigest);
byte[] msgSign = SignData(msgSignData);
this.precedingSignature = msgSign;
return msgSign;
}
} }
} }

View file

@ -4,25 +4,27 @@ using System.Linq;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using MinecraftClient.Protocol.Message;
namespace MinecraftClient.Protocol.Keys namespace MinecraftClient.Protocol.Keys
{ {
public class PublicKey public class PublicKey
{ {
public byte[] Key { get; set; } public byte[] Key { get; set; }
public byte[] Signature { get; set; } public byte[]? Signature { get; set; }
public byte[]? SignatureV2 { get; set; } public byte[]? SignatureV2 { get; set; }
private readonly RSA rsa; private readonly RSA rsa;
public PublicKey(string pemKey, string sig, string? sigV2 = null) public PublicKey(string pemKey, string? sig = null, string? sigV2 = null)
{ {
this.Key = KeyUtils.DecodePemKey(pemKey, "-----BEGIN RSA PUBLIC KEY-----", "-----END RSA PUBLIC KEY-----"); this.Key = KeyUtils.DecodePemKey(pemKey, "-----BEGIN RSA PUBLIC KEY-----", "-----END RSA PUBLIC KEY-----");
this.rsa = RSA.Create(); this.rsa = RSA.Create();
rsa.ImportSubjectPublicKeyInfo(this.Key, out _); rsa.ImportSubjectPublicKeyInfo(this.Key, out _);
this.Signature = Convert.FromBase64String(sig); if (!string.IsNullOrEmpty(sig))
this.Signature = Convert.FromBase64String(sig);
if (!string.IsNullOrEmpty(sigV2)) if (!string.IsNullOrEmpty(sigV2))
this.SignatureV2 = Convert.FromBase64String(sigV2!); this.SignatureV2 = Convert.FromBase64String(sigV2!);
@ -43,12 +45,53 @@ namespace MinecraftClient.Protocol.Keys
return rsa.VerifyData(data, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); return rsa.VerifyData(data, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
} }
public bool VerifyMessage(string message, string uuid, DateTimeOffset timestamp, ref byte[] salt, ref byte[] signature) /// <summary>
/// Verify message - 1.19
/// </summary>
/// <param name="message">Message content</param>
/// <param name="uuid">Sender uuid</param>
/// <param name="timestamp">Timestamp</param>
/// <param name="salt">Salt</param>
/// <param name="signature">Message signature</param>
/// <returns>Is this message vaild</returns>
public bool VerifyMessage(string message, Guid uuid, DateTimeOffset timestamp, ref byte[] salt, ref byte[] signature)
{ {
byte[] data = KeyUtils.GetSignatureData(message, uuid, timestamp, ref salt); byte[] data = KeyUtils.GetSignatureData(message, uuid, timestamp, ref salt);
return VerifyData(data, signature); return VerifyData(data, signature);
} }
/// <summary>
/// Verify message - 1.19.1 and above
/// </summary>
/// <param name="message">Message content</param>
/// <param name="uuid">Sender uuid</param>
/// <param name="timestamp">Timestamp</param>
/// <param name="salt">Salt</param>
/// <param name="signature">Message signature</param>
/// <param name="precedingSignature">Preceding message signature</param>
/// <param name="lastSeenMessages">LastSeenMessages</param>
/// <returns>Is this message vaild</returns>
public bool VerifyMessage(string message, Guid uuid, DateTimeOffset timestamp, ref byte[] salt, ref byte[] signature, ref byte[]? precedingSignature, LastSeenMessageList lastSeenMessages)
{
byte[] bodySignData = KeyUtils.GetSignatureData(message, timestamp, ref salt, lastSeenMessages);
byte[] bodyDigest = KeyUtils.ComputeHash(bodySignData);
byte[] msgSignData = KeyUtils.GetSignatureData(precedingSignature, uuid, bodyDigest);
return VerifyData(msgSignData, signature);
}
/// <summary>
/// Verify message head - 1.19.1 and above
/// </summary>
/// <param name="bodyDigest">Message body hash</param>
/// <param name="signature">Message signature</param>
/// <returns>Is this message header vaild</returns>
public bool VerifyHeader(ref byte[] bodyDigest, ref byte[] signature)
{
return VerifyData(bodyDigest, signature);
}
} }
} }

View file

@ -131,7 +131,7 @@ namespace MinecraftClient.Protocol
if (Array.IndexOf(supportedVersions_Protocol16, ProtocolVersion) > -1) if (Array.IndexOf(supportedVersions_Protocol16, ProtocolVersion) > -1)
return new Protocol16Handler(Client, ProtocolVersion, Handler); return new Protocol16Handler(Client, ProtocolVersion, Handler);
int[] supportedVersions_Protocol18 = { 4, 5, 47, 107, 108, 109, 110, 210, 315, 316, 335, 338, 340, 393, 401, 404, 477, 480, 485, 490, 498, 573, 575, 578, 735, 736, 751, 753, 754, 755, 756, 757, 758, 759 }; int[] supportedVersions_Protocol18 = { 4, 5, 47, 107, 108, 109, 110, 210, 315, 316, 335, 338, 340, 393, 401, 404, 477, 480, 485, 490, 498, 573, 575, 578, 735, 736, 751, 753, 754, 755, 756, 757, 758, 759, 760 };
if (Array.IndexOf(supportedVersions_Protocol18, ProtocolVersion) > -1) if (Array.IndexOf(supportedVersions_Protocol18, ProtocolVersion) > -1)
return new Protocol18Handler(Client, ProtocolVersion, Handler, forgeInfo); return new Protocol18Handler(Client, ProtocolVersion, Handler, forgeInfo);
@ -263,6 +263,9 @@ namespace MinecraftClient.Protocol
return 758; return 758;
case "1.19": case "1.19":
return 759; return 759;
case "1.19.1":
case "1.19.2":
return 760;
default: default:
return 0; return 0;
} }
@ -329,6 +332,7 @@ namespace MinecraftClient.Protocol
case 757: return "1.18.1"; case 757: return "1.18.1";
case 758: return "1.18.2"; case 758: return "1.18.2";
case 759: return "1.19"; case 759: return "1.19";
case 760: return "1.19.2";
default: return "0.0"; default: return "0.0";
} }
} }

View file

@ -205,6 +205,7 @@ chat.fail=§8Failed to download the file.
chat.from_dir=§8Defaulting to en_GB.lang from your Minecraft directory. chat.from_dir=§8Defaulting to en_GB.lang from your Minecraft directory.
chat.loaded=§8Translations file loaded. chat.loaded=§8Translations file loaded.
chat.not_found=§8Translations file not found: '{0}'\nSome messages won't be properly printed without this file. chat.not_found=§8Translations file not found: '{0}'\nSome messages won't be properly printed without this file.
chat.message_chain_broken=Player {0}'s message chain is broken!
[general] [general]
# General message/information (i.e. Done) # General message/information (i.e. Done)

View file

@ -1110,7 +1110,7 @@ namespace MinecraftClient
/// <returns>UserUUID of the current account</returns> /// <returns>UserUUID of the current account</returns>
protected string GetUserUUID() protected string GetUserUUID()
{ {
return Handler.GetUserUUID(); return Handler.GetUserUuidStr();
} }
/// <summary> /// <summary>