mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2025-10-14 21:22:49 +00:00
Merge from master
This commit is contained in:
commit
afdf2f9e2c
28 changed files with 1453 additions and 1114 deletions
|
|
@ -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"))
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,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();
|
private static Dimension curDimension = new Dimension();
|
||||||
|
|
||||||
|
private static Dictionary<string, Dimension>? dimensionList = null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Chunk data parsing progress
|
/// Chunk data parsing progress
|
||||||
|
|
@ -52,24 +54,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>
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ using MinecraftClient.Inventory;
|
||||||
using MinecraftClient.Logger;
|
using MinecraftClient.Logger;
|
||||||
using MinecraftClient.Protocol.Keys;
|
using MinecraftClient.Protocol.Keys;
|
||||||
using MinecraftClient.Protocol.Session;
|
using MinecraftClient.Protocol.Session;
|
||||||
|
using MinecraftClient.Protocol.Message;
|
||||||
|
|
||||||
namespace MinecraftClient
|
namespace MinecraftClient
|
||||||
{
|
{
|
||||||
|
|
@ -69,7 +70,8 @@ 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;
|
||||||
|
|
@ -105,7 +107,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; }
|
||||||
|
|
@ -151,7 +154,9 @@ namespace MinecraftClient
|
||||||
|
|
||||||
bool retry = false;
|
bool retry = false;
|
||||||
this.sessionid = session.ID;
|
this.sessionid = session.ID;
|
||||||
this.uuid = session.PlayerID;
|
if (!Guid.TryParse(session.PlayerID, out this.uuid))
|
||||||
|
this.uuid = Guid.Empty;
|
||||||
|
this.uuidStr = session.PlayerID;
|
||||||
this.username = session.PlayerName;
|
this.username = session.PlayerName;
|
||||||
this.host = server_ip;
|
this.host = server_ip;
|
||||||
this.port = port;
|
this.port = port;
|
||||||
|
|
@ -2092,19 +2097,17 @@ 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!)
|
if (!Settings.ShowIllegalSignedChat && !message.isSystemChat && !(bool)message.isSignatureLegal!)
|
||||||
return;
|
return;
|
||||||
messageText = ChatParser.ParseSignedChat(message, links);
|
messageText = ChatParser.ParseSignedChat(message, links);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
messageText = ChatParser.ParseText(message.content, links);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (message.isJson)
|
||||||
|
messageText = ChatParser.ParseText(message.content, links);
|
||||||
|
else
|
||||||
messageText = message.content;
|
messageText = message.content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2242,15 +2245,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>
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,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;
|
||||||
|
|
|
||||||
|
|
@ -1161,9 +1161,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)
|
||||||
{
|
{
|
||||||
|
if (bytes == null)
|
||||||
|
return "null";
|
||||||
|
else
|
||||||
return BitConverter.ToString(bytes).Replace("-", " ");
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,36 +8,36 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes
|
||||||
{
|
{
|
||||||
{ 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)
|
||||||
|
|
@ -48,12 +48,12 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes
|
||||||
{ 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)
|
||||||
|
|
@ -63,16 +63,16 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes
|
||||||
{ 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)
|
||||||
|
|
@ -87,10 +87,10 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes
|
||||||
{ 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)
|
||||||
|
|
@ -100,7 +100,7 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes
|
||||||
{ 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)
|
||||||
|
|
@ -128,21 +128,21 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes
|
||||||
{ 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)
|
||||||
|
|
@ -152,11 +152,11 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes
|
||||||
{ 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)
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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,
|
|
||||||
PlayerInfo,
|
|
||||||
FacePlayer,
|
|
||||||
PlayerPositionAndLook,
|
|
||||||
UnlockRecipes,
|
|
||||||
DestroyEntities,
|
|
||||||
RemoveEntityEffect,
|
|
||||||
ResourcePackSend,
|
|
||||||
Respawn,
|
|
||||||
EntityHeadLook,
|
|
||||||
SelectAdvancementTab,
|
|
||||||
WorldBorder,
|
|
||||||
Camera,
|
|
||||||
HeldItemChange,
|
|
||||||
UpdateViewPosition,
|
|
||||||
UpdateViewDistance,
|
|
||||||
DisplayScoreboard,
|
|
||||||
EntityMetadata,
|
|
||||||
AttachEntity,
|
|
||||||
EntityVelocity,
|
|
||||||
EntityEquipment,
|
|
||||||
SetExperience,
|
|
||||||
UpdateHealth,
|
|
||||||
ScoreboardObjective,
|
|
||||||
SetPassengers,
|
|
||||||
Teams,
|
|
||||||
UpdateScore,
|
|
||||||
SpawnPosition,
|
|
||||||
TimeUpdate,
|
|
||||||
Title,
|
|
||||||
EntitySoundEffect,
|
|
||||||
SoundEffect,
|
|
||||||
StopSound,
|
|
||||||
PlayerListHeaderAndFooter,
|
|
||||||
NBTQueryResponse,
|
|
||||||
CollectItem,
|
|
||||||
EntityTeleport,
|
|
||||||
Advancements,
|
|
||||||
EntityProperties,
|
|
||||||
EntityEffect,
|
|
||||||
DeclareRecipes,
|
|
||||||
SetTitleTime,
|
|
||||||
SetTitleText,
|
|
||||||
SetTitleSubTitle,
|
|
||||||
WorldBorderWarningReach,
|
|
||||||
WorldBorderWarningDelay,
|
|
||||||
WorldBorderSize,
|
|
||||||
WorldBorderLerpSize,
|
|
||||||
WorldBorderCenter,
|
|
||||||
ActionBar,
|
|
||||||
Tags,
|
|
||||||
DeathCombatEvent,
|
|
||||||
EnterCombatEvent,
|
|
||||||
EndCombatEvent,
|
|
||||||
Ping,
|
|
||||||
InitializeWorldBorder,
|
|
||||||
SkulkVibrationSignal,
|
|
||||||
ClearTiles,
|
|
||||||
UseBed, // For 1.13.2 or below
|
|
||||||
MapChunkBulk, // For 1.8 or below
|
MapChunkBulk, // For 1.8 or below
|
||||||
|
MapData, //
|
||||||
|
MessageHeader, // Added in 1.19.1 (1.19.2)
|
||||||
|
MultiBlockChange, //
|
||||||
|
NamedSoundEffect, //
|
||||||
|
NBTQueryResponse, //
|
||||||
|
OpenBook, //
|
||||||
|
OpenHorseWindow, //
|
||||||
|
OpenSignEditor, //
|
||||||
|
OpenWindow, //
|
||||||
|
Particle, //
|
||||||
|
Ping, //
|
||||||
|
PlayerAbilities, //
|
||||||
|
PlayerInfo, //
|
||||||
|
PlayerListHeaderAndFooter, //
|
||||||
|
PlayerPositionAndLook, //
|
||||||
|
PluginMessage, //
|
||||||
|
RemoveEntityEffect, //
|
||||||
|
ResourcePackSend, //
|
||||||
|
Respawn, //
|
||||||
|
ScoreboardObjective, //
|
||||||
|
SelectAdvancementTab, //
|
||||||
|
ServerData, // Added in 1.19
|
||||||
|
ServerDifficulty, //
|
||||||
SetCompression, // For 1.8 or below
|
SetCompression, // For 1.8 or below
|
||||||
UpdateSign, // For 1.8 or below
|
SetCooldown, //
|
||||||
UpdateEntityNBT, // For 1.8 or below
|
SetDisplayChatPreview, // Added in 1.19
|
||||||
|
SetExperience, //
|
||||||
|
SetPassengers, //
|
||||||
|
SetSlot, //
|
||||||
|
SetTitleSubTitle, //
|
||||||
|
SetTitleText, //
|
||||||
|
SetTitleTime, //
|
||||||
|
SkulkVibrationSignal, //
|
||||||
|
SoundEffect, //
|
||||||
|
SpawnEntity, //
|
||||||
|
SpawnExperienceOrb, //
|
||||||
|
SpawnLivingEntity, //
|
||||||
|
SpawnPainting, //
|
||||||
|
SpawnPlayer, //
|
||||||
|
SpawnPosition, //
|
||||||
|
SpawnWeatherEntity, //
|
||||||
|
Statistics, //
|
||||||
|
StopSound, //
|
||||||
|
SystemChat, // Added in 1.19
|
||||||
|
TabComplete, //
|
||||||
|
Tags, //
|
||||||
|
Teams, //
|
||||||
|
TimeUpdate, //
|
||||||
|
Title, //
|
||||||
|
TradeList, //
|
||||||
Unknown, // For old version packet that have been removed and not used by mcc
|
Unknown, // For old version packet that have been removed and not used by mcc
|
||||||
UpdateSimulationDistance,
|
UnloadChunk, //
|
||||||
|
UnlockRecipes, //
|
||||||
// 1.19 Additions
|
UpdateEntityNBT, // For 1.8 or below
|
||||||
BlockChangedAck,
|
UpdateHealth, //
|
||||||
ChatPreview,
|
UpdateLight, //
|
||||||
ServerData,
|
UpdateScore, //
|
||||||
SetDisplayChatPreview,
|
UpdateSign, // For 1.8 or below
|
||||||
SystemChat
|
UpdateSimulationDistance, //
|
||||||
|
UpdateViewDistance, //
|
||||||
|
UpdateViewPosition, //
|
||||||
|
UseBed, // For 1.13.2 or below
|
||||||
|
VehicleMove, //
|
||||||
|
WindowConfirmation, //
|
||||||
|
WindowItems, //
|
||||||
|
WindowProperty, //
|
||||||
|
WorldBorder, //
|
||||||
|
WorldBorderCenter, //
|
||||||
|
WorldBorderLerpSize, //
|
||||||
|
WorldBorderSize, //
|
||||||
|
WorldBorderWarningDelay, //
|
||||||
|
WorldBorderWarningReach, //
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
|
||||||
InteractEntity,
|
|
||||||
KeepAlive,
|
|
||||||
LockDifficulty,
|
|
||||||
PlayerPosition,
|
|
||||||
PlayerPositionAndRotation,
|
|
||||||
PlayerRotation,
|
|
||||||
PlayerMovement,
|
|
||||||
VehicleMove,
|
|
||||||
SteerBoat,
|
|
||||||
PickItem,
|
|
||||||
CraftRecipeRequest,
|
|
||||||
PlayerAbilities,
|
|
||||||
PlayerDigging,
|
|
||||||
EntityAction,
|
|
||||||
SteerVehicle,
|
|
||||||
RecipeBookData,
|
|
||||||
NameItem,
|
|
||||||
ResourcePackStatus,
|
|
||||||
AdvancementTab,
|
|
||||||
SelectTrade,
|
|
||||||
SetBeaconEffect,
|
|
||||||
HeldItemChange,
|
|
||||||
UpdateCommandBlock,
|
|
||||||
UpdateCommandBlockMinecart,
|
|
||||||
CreativeInventoryAction,
|
|
||||||
UpdateJigsawBlock,
|
|
||||||
UpdateStructureBlock,
|
|
||||||
UpdateSign,
|
|
||||||
Animation,
|
|
||||||
Spectate,
|
|
||||||
PlayerBlockPlacement,
|
|
||||||
UseItem,
|
|
||||||
Pong,
|
|
||||||
PrepareCraftingGrid, // For 1.12 - 1.12.1 only
|
|
||||||
EnchantItem, // For 1.13.2 or below
|
EnchantItem, // For 1.13.2 or below
|
||||||
|
EntityAction, //
|
||||||
|
EntityNBTRequest, //
|
||||||
GenerateStructure, // Added in 1.16
|
GenerateStructure, // Added in 1.16
|
||||||
|
HeldItemChange, //
|
||||||
|
InteractEntity, //
|
||||||
|
KeepAlive, //
|
||||||
|
LockDifficulty, //
|
||||||
|
MessageAcknowledgment, // Added in 1.19.1 (1.19.2)
|
||||||
|
NameItem, //
|
||||||
|
PickItem, //
|
||||||
|
PlayerAbilities, //
|
||||||
|
PlayerBlockPlacement, //
|
||||||
|
PlayerDigging, //
|
||||||
|
PlayerMovement, //
|
||||||
|
PlayerPosition, //
|
||||||
|
PlayerPositionAndRotation, //
|
||||||
|
PlayerRotation, //
|
||||||
|
PluginMessage, //
|
||||||
|
Pong, //
|
||||||
|
PrepareCraftingGrid, // For 1.12 - 1.12.1 only
|
||||||
|
QueryBlockNBT, //
|
||||||
|
RecipeBookData, //
|
||||||
|
ResourcePackStatus, //
|
||||||
|
SelectTrade, //
|
||||||
|
SetBeaconEffect, //
|
||||||
|
SetDifficulty, //
|
||||||
SetDisplayedRecipe, // Added in 1.16.2
|
SetDisplayedRecipe, // Added in 1.16.2
|
||||||
SetRecipeBookState, // Added in 1.16.2
|
SetRecipeBookState, // Added in 1.16.2
|
||||||
|
Spectate, //
|
||||||
|
SteerBoat, //
|
||||||
|
SteerVehicle, //
|
||||||
|
TabComplete, //
|
||||||
|
TeleportConfirm, //
|
||||||
Unknown, // For old version packet that have been removed and not used by mcc
|
Unknown, // For old version packet that have been removed and not used by mcc
|
||||||
|
UpdateCommandBlock, //
|
||||||
// Added in 1.19
|
UpdateCommandBlockMinecart, //
|
||||||
ChatCommand,
|
UpdateJigsawBlock, //
|
||||||
ChatPreview,
|
UpdateSign, //
|
||||||
|
UpdateStructureBlock, //
|
||||||
|
UseItem, //
|
||||||
|
VehicleMove, //
|
||||||
|
WindowConfirmation, //
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ using MinecraftClient.Mapping;
|
||||||
using MinecraftClient.Inventory;
|
using MinecraftClient.Inventory;
|
||||||
using MinecraftClient.Protocol.Keys;
|
using MinecraftClient.Protocol.Keys;
|
||||||
using MinecraftClient.Protocol.Session;
|
using MinecraftClient.Protocol.Session;
|
||||||
|
using MinecraftClient.Protocol.Message;
|
||||||
|
|
||||||
namespace MinecraftClient.Protocol.Handlers
|
namespace MinecraftClient.Protocol.Handlers
|
||||||
{
|
{
|
||||||
|
|
@ -581,7 +582,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
|
|
||||||
public bool Login(PlayerKeyPair? playerKeyPair, SessionToken session)
|
public bool Login(PlayerKeyPair? playerKeyPair, SessionToken session)
|
||||||
{
|
{
|
||||||
if (Handshake(handler.GetUserUUID(), handler.GetUsername(), handler.GetSessionID(), handler.GetServerHost(), handler.GetServerPort(), session))
|
if (Handshake(handler.GetUserUuidStr(), handler.GetUsername(), handler.GetSessionID(), handler.GetServerHost(), handler.GetServerPort()))
|
||||||
{
|
{
|
||||||
Send(new byte[] { 0xCD, 0 });
|
Send(new byte[] { 0xCD, 0 });
|
||||||
try
|
try
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ using MinecraftClient.Protocol.Keys;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using MinecraftClient.Protocol.Session;
|
using MinecraftClient.Protocol.Session;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using MinecraftClient.Protocol.Message;
|
||||||
|
|
||||||
namespace MinecraftClient.Protocol.Handlers
|
namespace MinecraftClient.Protocol.Handlers
|
||||||
{
|
{
|
||||||
|
|
@ -57,6 +58,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;
|
||||||
|
|
@ -66,8 +68,13 @@ 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 readonly BlockingCollection<Tuple<int, Queue<byte>>> packetQueue = new();
|
private readonly BlockingCollection<Tuple<int, Queue<byte>>> packetQueue = new();
|
||||||
|
|
||||||
|
private int pendingAcknowledgments = 0;
|
||||||
|
private LastSeenMessagesCollector lastSeenMessagesCollector = new(5);
|
||||||
|
private LastSeenMessageList.Entry? lastReceivedMessage = null;
|
||||||
|
|
||||||
Protocol18Forge pForge;
|
Protocol18Forge pForge;
|
||||||
Protocol18Terrain pTerrain;
|
Protocol18Terrain pTerrain;
|
||||||
IMinecraftComHandler handler;
|
IMinecraftComHandler handler;
|
||||||
|
|
@ -95,31 +102,28 @@ 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_18_2_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_18_2_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())
|
||||||
{
|
|
||||||
if (protocolVersion > MC_1_19_Version && handler.GetTerrainEnabled())
|
|
||||||
throw new NotImplementedException(Translations.Get("exception.palette.block"));
|
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();
|
||||||
|
|
@ -129,18 +133,16 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
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())
|
||||||
{
|
|
||||||
if (protocolVersion > MC_1_19_Version && handler.GetEntityHandlingEnabled())
|
|
||||||
throw new NotImplementedException(Translations.Get("exception.palette.entity"));
|
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();
|
||||||
|
|
@ -152,29 +154,53 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
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
|
else if (protocolVersion >= MC_1_13_Version)
|
||||||
entityPalette = new EntityPalette113();
|
entityPalette = new EntityPalette113();
|
||||||
}
|
else
|
||||||
else entityPalette = new EntityPalette112();
|
entityPalette = new EntityPalette112();
|
||||||
|
|
||||||
// Item palette
|
// Item palette
|
||||||
if (protocolVersion >= MC_1_16_2_Version)
|
if (protocolVersion > MC_1_19_2_Version && handler.GetInventoryEnabled())
|
||||||
{
|
|
||||||
if (protocolVersion > MC_1_19_Version && handler.GetInventoryEnabled())
|
|
||||||
throw new NotImplementedException(Translations.Get("exception.palette.item"));
|
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 itemPalette = new ItemPalette1161();
|
else if (protocolVersion >= MC_1_16_1_Version)
|
||||||
}
|
itemPalette = new ItemPalette1161();
|
||||||
else itemPalette = new ItemPalette115();
|
else
|
||||||
|
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>
|
||||||
|
|
@ -344,26 +370,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;
|
||||||
|
|
@ -377,10 +399,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
|
||||||
|
|
@ -438,7 +460,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);
|
||||||
|
|
||||||
|
|
@ -462,38 +484,134 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
|
|
||||||
byte[] messageSignature = dataTypes.ReadNextByteArray(packetData);
|
byte[] messageSignature = dataTypes.ReadNextByteArray(packetData);
|
||||||
|
|
||||||
|
bool verifyResult;
|
||||||
|
if (!isOnlineMode)
|
||||||
|
verifyResult = false;
|
||||||
|
else if (senderUUID == handler.GetUserUuid())
|
||||||
|
verifyResult = true;
|
||||||
|
else
|
||||||
|
{
|
||||||
PlayerInfo? player = handler.GetPlayerInfo(senderUUID);
|
PlayerInfo? player = handler.GetPlayerInfo(senderUUID);
|
||||||
bool verifyResult = player == null ? false : player.VerifyMessage(signedChat, senderUUID, timestamp, salt, ref messageSignature);
|
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
|
||||||
|
|
@ -842,6 +960,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())
|
||||||
|
|
@ -921,14 +1041,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
|
||||||
|
|
@ -959,7 +1085,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));
|
||||||
|
|
@ -1315,13 +1441,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))
|
||||||
|
|
@ -1573,7 +1702,21 @@ 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);
|
||||||
|
|
@ -1588,10 +1731,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, session);
|
return StartEncryption(handler.GetUserUuidStr(), handler.GetSessionID(), token, serverID, serverPublicKey, playerKeyPair, session);
|
||||||
}
|
}
|
||||||
else if (packetID == 0x02) //Login successful
|
else if (packetID == 0x02) //Login successful
|
||||||
{
|
{
|
||||||
|
|
@ -1890,41 +2034,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]));
|
||||||
|
|
@ -1936,7 +2126,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>
|
||||||
|
|
@ -1953,6 +2143,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
|
||||||
|
|
@ -1962,24 +2155,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
|
||||||
}
|
}
|
||||||
|
|
@ -1988,6 +2182,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;
|
||||||
}
|
}
|
||||||
|
|
@ -2020,11 +2220,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
|
||||||
|
|
@ -2036,14 +2239,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;
|
||||||
|
|
@ -2124,9 +2335,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)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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,15 +230,15 @@ 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))
|
||||||
{
|
{
|
||||||
foreach (var line in File.ReadLines(Language_File))
|
foreach (var line in File.ReadLines(Language_File))
|
||||||
{
|
{
|
||||||
|
|
@ -288,7 +311,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>
|
||||||
|
|
@ -314,7 +337,7 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
@ -371,7 +394,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();
|
||||||
117
MinecraftClient/Protocol/Message/LastSeenMessageList.cs
Normal file
117
MinecraftClient/Protocol/Message/LastSeenMessageList.cs
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
byte[] saltByte = BitConverter.GetBytes(salt);
|
||||||
|
Array.Reverse(saltByte);
|
||||||
|
|
||||||
|
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);
|
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);
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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? GetNewProfileKeys(string accessToken)
|
public static PlayerKeyPair? GetNewProfileKeys(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)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,9 @@ 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));
|
||||||
|
if (PublicKey.Signature == null)
|
||||||
|
datas.Add(String.Empty);
|
||||||
|
else
|
||||||
datas.Add(Convert.ToBase64String(PublicKey.Signature));
|
datas.Add(Convert.ToBase64String(PublicKey.Signature));
|
||||||
if (PublicKey.SignatureV2 == null)
|
if (PublicKey.SignatureV2 == null)
|
||||||
datas.Add(String.Empty);
|
datas.Add(String.Empty);
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,24 +4,26 @@ 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 _);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(sig))
|
||||||
this.Signature = Convert.FromBase64String(sig);
|
this.Signature = Convert.FromBase64String(sig);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(sigV2))
|
if (!string.IsNullOrEmpty(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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -279,6 +279,12 @@ class HTTP
|
||||||
{
|
{
|
||||||
public static byte[] Post(string url, NameValueCollection pairs)
|
public static byte[] Post(string url, NameValueCollection pairs)
|
||||||
{
|
{
|
||||||
|
ServicePointManager.Expect100Continue = true;
|
||||||
|
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls
|
||||||
|
| SecurityProtocolType.Tls11
|
||||||
|
| SecurityProtocolType.Tls12
|
||||||
|
| SecurityProtocolType.Ssl3;
|
||||||
|
|
||||||
using (WebClient webClient = new WebClient())
|
using (WebClient webClient = new WebClient())
|
||||||
{
|
{
|
||||||
return webClient.UploadValues(url, pairs);
|
return webClient.UploadValues(url, pairs);
|
||||||
|
|
|
||||||
|
|
@ -1,517 +1,4 @@
|
||||||
Minecraft Console Client User Manual
|
Minecraft Console Client User Manual
|
||||||
======
|
======
|
||||||
|
|
||||||
**Thanks for dowloading Minecraft Console Client!**
|
This page has been moved to appropriate sections on our new [Documentation website](https://mccteam.github.io/docs/).
|
||||||
|
|
||||||
Minecraft Console Client is a lightweight app able to connect to any minecraft server,
|
|
||||||
both offline and online mode. It enables you to send commands and receive text messages
|
|
||||||
in a fast and easy way without having to open the main Minecraft game.
|
|
||||||
|
|
||||||
How to use
|
|
||||||
------
|
|
||||||
|
|
||||||
First, extract the archive if not already extracted.
|
|
||||||
On Windows, simply open MinecraftClient.exe by double-clicking on it.
|
|
||||||
On Mac or Linux you need to install the Mono Runtime:
|
|
||||||
- On Mac: http://www.mono-project.com/download/#download-mac
|
|
||||||
- On Linux: sudo apt-get install mono-runtime libmono-reflection-cil
|
|
||||||
Then, open a terminal in this folder and run "mono MinecraftClient.exe".
|
|
||||||
If you cannot authenticate on Mono because you have TLS/HTTPS/Certificate errors, you'll need to run `mozroots --import --ask-remove` once or install `ca-certificates-mono` (See [#1708](https://github.com/MCCTeam/Minecraft-Console-Client/issues/1708#issuecomment-893768862)).
|
|
||||||
If Mono crashes, retry with `mono-complete` instead of `mono-runtime`. Use at least Mono v4.0.
|
|
||||||
|
|
||||||
Docker
|
|
||||||
------
|
|
||||||
|
|
||||||
Using Docker do the following:
|
|
||||||
|
|
||||||
**Building the Image:**
|
|
||||||
```bash
|
|
||||||
# Using HTTPS
|
|
||||||
git clone https://github.com/MCCTeam/Minecraft-Console-Client.git
|
|
||||||
|
|
||||||
# Using SSH
|
|
||||||
git clone git@github.com:MCCTeam/Minecraft-Console-Client.git
|
|
||||||
|
|
||||||
cd Minecraft-Console-Client/Docker
|
|
||||||
|
|
||||||
docker build -t minecraft-console-client:latest .
|
|
||||||
```
|
|
||||||
|
|
||||||
**Start the container using Docker:**
|
|
||||||
```bash
|
|
||||||
# You could also ignore the -v parameter if you dont want to mount the volume that is up to you. If you don't it's harder to edit the .ini file if thats something you want to do
|
|
||||||
docker run -it -v <PATH_ON_YOUR_MACHINE_TO_MOUNT>:/opt/data minecraft-console-client:latest
|
|
||||||
```
|
|
||||||
Now you could login and the Client is running. To detach from the Client but still keep it running in the Background press: `CTRL + P`, `CTRL + Q`.
|
|
||||||
To reattach use the `docker attach` command.
|
|
||||||
|
|
||||||
**Start the container using docker-compose:**
|
|
||||||
|
|
||||||
By default, the volume of the container gets mapped into a new folder named `data` in the same folder the `docker-compose.yml` is stored.
|
|
||||||
|
|
||||||
If you don't want to map a volume, you have to comment out or delete the entire volumes section:
|
|
||||||
```yml
|
|
||||||
#volumes:
|
|
||||||
#- './data:/opt/data'
|
|
||||||
```
|
|
||||||
Make sure you are in the directory the `docker-compose.yml` is stored before you attempt to start. If you do so, you can start the container:
|
|
||||||
```bash
|
|
||||||
docker-compose run MCC
|
|
||||||
```
|
|
||||||
Remember to remove the container after usage:
|
|
||||||
```bash
|
|
||||||
docker-compose down
|
|
||||||
```
|
|
||||||
|
|
||||||
If you use the INI file and entered your data (username, password, server) there, you can start your container using
|
|
||||||
```bash
|
|
||||||
docker-compose up
|
|
||||||
docker-compose up -d #for deamonized running in the background
|
|
||||||
```
|
|
||||||
Note that you won't be able to interact with the client using `docker-compose up`. If you want that functionality, please use the first method: `docker-compose run MCC`.
|
|
||||||
As above, you can stop and remove the container using
|
|
||||||
```bash
|
|
||||||
docker-compose down
|
|
||||||
```
|
|
||||||
|
|
||||||
Using Configuration files & Enabling bots
|
|
||||||
------
|
|
||||||
|
|
||||||
Simply open the INI configuration file with a text editor and change the values.
|
|
||||||
To enable a bot change the `enabled` value in the INI file from `false` to `true`.
|
|
||||||
You will still be able to send and receive chat messages when a bot is loaded.
|
|
||||||
You can remove or comment some lines from the INI file to use the default values instead.
|
|
||||||
You can have several INI files and drag & drop one of them over MinecraftClient.exe
|
|
||||||
|
|
||||||
Command-line usage
|
|
||||||
------
|
|
||||||
|
|
||||||
Quick usage:
|
|
||||||
|
|
||||||
```
|
|
||||||
MinecraftClient.exe --help
|
|
||||||
MinecraftClient.exe <username> <password> <server>
|
|
||||||
MinecraftClient.exe <username> <password> <server> "/mycommand"
|
|
||||||
MinecraftClient.exe --setting=value [--other settings]
|
|
||||||
MinecraftClient.exe --section.setting=value [--other settings]
|
|
||||||
MinecraftClient.exe <settings-file.ini> [--other settings]
|
|
||||||
```
|
|
||||||
You can mix and match arguments by following theses rules:
|
|
||||||
* First positional argument may be either the login or settings file
|
|
||||||
* Other positional arguments are read in order: login, password, server, command
|
|
||||||
* Arguments starting with `--` can be in any order and position
|
|
||||||
|
|
||||||
Examples and further explanations:
|
|
||||||
|
|
||||||
```
|
|
||||||
MinecraftClient.exe <login> <password> <server>
|
|
||||||
```
|
|
||||||
|
|
||||||
* This will automatically connect you to the chosen server.
|
|
||||||
* You may omit password and/or server to specify e.g. only the login
|
|
||||||
* To specify a server but ask password interactively, use `""` as password.
|
|
||||||
* To specify offline mode with no password, use `-` as password.
|
|
||||||
|
|
||||||
```
|
|
||||||
MinecraftClient.exe <login> <password> <server> "/mycommand"
|
|
||||||
```
|
|
||||||
|
|
||||||
* This will automatically send `/mycommand` to the server and close.
|
|
||||||
* To send several commands and/or stay connected, use the ScriptScheduler bot instead.
|
|
||||||
|
|
||||||
```
|
|
||||||
MinecraftClient.exe <myconfig.ini>
|
|
||||||
```
|
|
||||||
|
|
||||||
* This will load the specified configuration file
|
|
||||||
* If the file contains login / password / server ip, it will automatically connect.
|
|
||||||
|
|
||||||
```
|
|
||||||
MinecraftClient.exe --setting=value [--other settings]
|
|
||||||
```
|
|
||||||
|
|
||||||
* Specify settings on the command-line, see possible value in the configuration file
|
|
||||||
* Use `--section.setting=value` for settings outside the `[Main]` section
|
|
||||||
* Example: `--antiafk.enabled=true` for enabling the AntiAFK bot
|
|
||||||
|
|
||||||
```
|
|
||||||
MinecraftClient.exe <myconfig.ini> <login> <password> <server> [--other settings]
|
|
||||||
```
|
|
||||||
|
|
||||||
* Load the specified configuration file and override some settings from the file
|
|
||||||
|
|
||||||
Internal commands
|
|
||||||
------
|
|
||||||
|
|
||||||
These commands can be performed from the chat prompt, scripts or remote control.
|
|
||||||
From chat prompt, commands must by default be prepended with a slash, eg. `/quit`.
|
|
||||||
In scripts and remote control, no slash is needed to perform the command, eg. `quit`.
|
|
||||||
|
|
||||||
- `quit` or `exit`: disconnect from the server and close the application
|
|
||||||
- `reco [account]`: disconnect and reconnect to the server
|
|
||||||
- `connect <server> [account]`: go to the given server and resume the script
|
|
||||||
- `script <script name>`: run a script containing a list of commands
|
|
||||||
- `send <text>`: send a message or a command to the server
|
|
||||||
- `respawn`: Use this to respawn if you are dead (like clicking "respawn" ingame)
|
|
||||||
- `log <text>`: display some text in the console (useful for scripts)
|
|
||||||
- `list`: list players logged in to the server (uses tab list info sent by server)
|
|
||||||
- `set varname=value`: set a value which can be used as `%varname%` in further commands
|
|
||||||
- `setrnd variable string1 "\"string2\" string3"`: set a `%variable%` to one of the provided values
|
|
||||||
- `setrnd variable -7to10`: set a `%variable%` to a number from -7 to 9
|
|
||||||
- `wait <time>`: wait X ticks (10 ticks = ~1 second. Only for scripts)
|
|
||||||
- `move`: used for moving when terrain and movements feature is enabled
|
|
||||||
- `look`: used for looking at direction when terrain and movements is enabled
|
|
||||||
- `debug`: toggle debug messages, useful for chatbot developers
|
|
||||||
- `help`: show command help. Tip: Use "/send /help" for server help
|
|
||||||
- Some commands may not be documented yet, use `help` to list them all.
|
|
||||||
|
|
||||||
`[account]` is an account alias defined in accounts file, read more below.
|
|
||||||
|
|
||||||
`<server>` is either a server IP or a server alias defined in servers file
|
|
||||||
|
|
||||||
Servers and Accounts file
|
|
||||||
------
|
|
||||||
|
|
||||||
These two files can be used to store info about accounts and server, and give them aliases.
|
|
||||||
The purpose of this is to give them an easy-to-remember alias and to avoid typing account passwords.
|
|
||||||
|
|
||||||
As what you are typing can be read by the server admin if using the remote control feature,
|
|
||||||
using aliases is really important for privacy and for safely switching between accounts.
|
|
||||||
|
|
||||||
To use these files, simply take a look at [`sample-accounts.txt`](https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/config/sample-accounts.txt) and [`sample-servers.txt`](https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/config/sample-servers.txt).
|
|
||||||
Once you have created your files, fill the `accountlist` and `serverlist` fields in INI file.
|
|
||||||
|
|
||||||
Interacting with the Minecraft world
|
|
||||||
------
|
|
||||||
|
|
||||||
By default, Minecraft Console Client cannot interact with the world around you.
|
|
||||||
However, for some versions of the game you can enable the `terrainandmovements` setting.
|
|
||||||
|
|
||||||
This feature will allow you to properly fall on ground, pickup items and move around.
|
|
||||||
There is a C# API for reading terrain data around the player and moving from C# scripts.
|
|
||||||
|
|
||||||
Please note that this requires much more RAM to store all the terrain data, a bit more CPU
|
|
||||||
to process all of this, and slightly more bandwidth as locations updates are
|
|
||||||
sent back to the server in a spammy way (that's how Minecraft works).
|
|
||||||
|
|
||||||
How to write a script file
|
|
||||||
------
|
|
||||||
|
|
||||||
A script file can be launched by using `/script <filename>` in the client's command prompt.
|
|
||||||
The client will automatically look for your script in the current directory or `scripts` subfolder.
|
|
||||||
If the file extension is `.txt` or `.cs`, you may omit it and the client will autodectect the extension.
|
|
||||||
|
|
||||||
Regarding the script file, it is a text file with one instruction per line.
|
|
||||||
Any line beginning with `#` is ignored and treated as a comment.
|
|
||||||
Allowed instructions are given in [Internal commands](#internal-commands) section.
|
|
||||||
|
|
||||||
Application variables defined using the `set` command or `[AppVars]` INI section can be used.
|
|
||||||
The following read-only variables can also be used: %username%, %login%, %serverip%, %serverport%
|
|
||||||
|
|
||||||
How to write a C# script
|
|
||||||
------
|
|
||||||
|
|
||||||
If you are experienced with C#, you may also write a C# script.
|
|
||||||
That's a bit more involved, but way more powerful than regular scripts.
|
|
||||||
You can look at the provided sample C# scripts for getting started.
|
|
||||||
|
|
||||||
C# scripts can be used for creating your own ChatBot without recompiling the whole project.
|
|
||||||
These bots are embedded in a script file, which is compiled and loaded on the fly.
|
|
||||||
ChatBots can access plugin channels for communicating with some server plugins.
|
|
||||||
|
|
||||||
For knowing everything the API has to offer, you can look at [`CSharpRunner.cs`](https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/CSharpRunner.cs) and [`ChatBot.cs`](https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/ChatBot.cs).
|
|
||||||
|
|
||||||
The structure of the C# file must be like this:
|
|
||||||
```csharp
|
|
||||||
//MCCScript 1.0
|
|
||||||
|
|
||||||
MCC.LoadBot(<instance of your class which extends the ChatBot class>);
|
|
||||||
|
|
||||||
//MCCScript Extensions
|
|
||||||
|
|
||||||
<your class code here>
|
|
||||||
```
|
|
||||||
The first line always needs to be `//MCCScript 1.0` comment, as the program requires it to determine the version of the script.
|
|
||||||
|
|
||||||
Everything between `//MCCScript 1.0` and `//MCCScript Extensions` comments will be treated as code, that part of the code will be inserted into a class method at compile time. The main part of the script has access to the [`CSharpRunner.cs`](https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/CSharpRunner.cs) API while the ChatBot defined in the Extensions section will use the [`ChatBot.cs`](https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/ChatBot.cs) API.
|
|
||||||
|
|
||||||
You can include standard .NET libraries/namespaces using the following syntax: `//using <name>;`. Example: `//using System.Net;`
|
|
||||||
|
|
||||||
Some sample scripts and optional Chatbots are made available in the [`config`](https://github.com/MCCTeam/Minecraft-Console-Client/tree/master/MinecraftClient/config) folder.
|
|
||||||
|
|
||||||
Using HTTP/Socks proxies
|
|
||||||
------
|
|
||||||
|
|
||||||
If you are on a restricted network you might want to use some HTTP or SOCKS proxies.
|
|
||||||
To do so, find a proxy, enable proxying in INI file and fill in the relevant settings.
|
|
||||||
Proxies with username/password authentication are supported but have not been tested.
|
|
||||||
Not every proxy will work for playing Minecraft, because of port 80/443 web browsing restrictions.
|
|
||||||
However, you can choose to use a proxy for login only, most proxies should work in this mode.
|
|
||||||
|
|
||||||
Connecting to servers when ping is disabled
|
|
||||||
------
|
|
||||||
|
|
||||||
On some servers, the server list ping feature has been disabled, which prevents Minecraft Console Client
|
|
||||||
from pinging the server to determine the Minecraft version to use. To connect to this kind of servers,
|
|
||||||
find out which Minecraft version is running on the server, and fill in the `mcversion` field in INI file.
|
|
||||||
This will disable the ping step while connecting, but requires you to manually provide the version to use.
|
|
||||||
Recent versions of Minecraft Console Client may also prompt you for MC version in case of ping failure.
|
|
||||||
|
|
||||||
About translation files
|
|
||||||
------
|
|
||||||
|
|
||||||
When connecting to 1.6+ servers, you will need a translation file to display properly some chat messages.
|
|
||||||
These files describe how some messages should be printed depending on your preferred language.
|
|
||||||
The client will automatically load en_GB.lang from your Minecraft folder if Minecraft is installed on your
|
|
||||||
computer, or download it from Mojang's servers. You may choose another language in the configuration file.
|
|
||||||
|
|
||||||
Detecting chat messages
|
|
||||||
------
|
|
||||||
|
|
||||||
Minecraft Console Client can parse messages from the server in order to detect private and public messages.
|
|
||||||
This is useful for reacting to messages eg when using the AutoRespond, Hangman game, or RemoteControl bots.
|
|
||||||
However, for unusual chat formats, you may need to tinker with the ChatFormat section `MinecraftClient.ini`. This section defines the chat format by the means of regular expressions. Building regular expressions can be a bit tricky, so you might want to try them out eg on https://regex101.com - See also issue [#1640](https://github.com/MCCTeam/Minecraft-Console-Client/issues/1640) for more explanations on regular expressions. You can test that your MCC instance properly detects chat messages using [`sample-script-with-chatbot.cs`](https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/config/sample-script-with-chatbot.cs).
|
|
||||||
|
|
||||||
About Replay Mod feature
|
|
||||||
------
|
|
||||||
Replay Mod is _A Minecraft Mod to record, relive and share your experience._ You can see more at https://www.replaymod.com/
|
|
||||||
|
|
||||||
MCC supports recording and saving your game to a file which can be used by Replay Mod. You can simply enable ReplayMod in the `.ini` setting to use this feature. The only limitation is the client player (you) will not be shown in the replay. Please note that when recording is in progress, you SHOULD exit MCC with the `/quit` command or use `/replay stop` command before closing MCC as your replay may become corrupt if you force-close MCC with CTRL+C or the [X] button.
|
|
||||||
|
|
||||||
Using the Alerts bot
|
|
||||||
------
|
|
||||||
|
|
||||||
Write in [`alerts.txt`](https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/config/alerts.txt) the words you want the console to beep/alert you on.
|
|
||||||
Write in [`alerts-exclude.txt`](https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/config/alerts-exclude.txt) the words you want NOT to be alerted on.
|
|
||||||
For example write `Yourname` in `alerts.txt` and `<Yourname>` in `alerts-exclude.txt` to avoid alerts when you are talking.
|
|
||||||
|
|
||||||
Using the AutoRelog bot
|
|
||||||
------
|
|
||||||
|
|
||||||
Write in [`kickmessages.txt`](https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/config/kickmessages.txt) some words, such as `Restarting` for example.
|
|
||||||
If the kick message contains one of them, you will automatically be re-connected.
|
|
||||||
|
|
||||||
- A kick message `Connection has been lost.` is generated by the console itself when connection is lost.
|
|
||||||
- A kick message `Login failed.` is generated the same way when it failed to login to the server.
|
|
||||||
- A kick message `Failed to ping this IP.` is generated when it failed to ping the server.
|
|
||||||
|
|
||||||
You can use them for reconnecting when connection is lost or the login failed.
|
|
||||||
|
|
||||||
If you want to always reconnect, set `ignorekickmessage=true` in `MinecraftClient.ini`. Use at own risk! Server staff might not appreciate auto-relog on manual kicks, so always keep server rules in mind when configuring your client.
|
|
||||||
|
|
||||||
Using the Script Scheduler / Task Scheduler
|
|
||||||
------
|
|
||||||
|
|
||||||
The script scheduler allows you to perform scripts or internal commands on various events such as log on server, time interval, or fixed date and time. Simply enable the ScriptScheduler bot and specify a tasks file in your INI file. Please read [`sample-tasks.ini`](https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/config/sample-tasks.ini) for learning how to make your own task file.
|
|
||||||
|
|
||||||
Using the Hangman game
|
|
||||||
------
|
|
||||||
|
|
||||||
Hangman game is one of the first bots ever written for MCC, to demonstrate ChatBot capabilities.
|
|
||||||
Create a file with words to guess (examples: [`words-en.txt`](https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/config/hangman-en.txt), [`words-fr.txt`](https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/config/hangman-fr.txt)) and set it in config inside the `[Hangman]` section. Also set `enabled` to `true`. Then, add your username in the `botowners` INI setting, and finally, connect to the server and use `/tell <bot username> start` to start the game. If the bot does not respond to bot owners, see the [Detecting chat messages](#detecting-chat-messages) section.
|
|
||||||
|
|
||||||
Using the Remote Control
|
|
||||||
------
|
|
||||||
|
|
||||||
When the remote control bot is enabled, you can send commands to your bot using whispers.
|
|
||||||
Don't forget to add your username in the `botowners` INI setting if you want it to obey.
|
|
||||||
If it does not respond to bot owners, read the [Detecting chat messages](#detecting-chat-messages) section.
|
|
||||||
**Please note that server admins can read what you type and output from the bot. They can also impersonate bot owners with `/nick`, so do not use Remote Control if you do not trust server admins. See [#1142](https://github.com/MCCTeam/Minecraft-Console-Client/issues/1142) for more info.**
|
|
||||||
|
|
||||||
To perform a command simply do the following: `/tell <yourbot> <thecommand>`
|
|
||||||
Where `<thecommand>` is an internal command as described in [Internal commands](#internal-commands) section.
|
|
||||||
You can remotely send chat messages or commands using `/tell <yourbot> send <thetext>`
|
|
||||||
|
|
||||||
Remote control system can by default auto-accept `/tpa` and `/tpahere` requests from the bot owners.
|
|
||||||
Auto-accept can be disabled or extended to requests from anyone in remote control configuration.
|
|
||||||
|
|
||||||
Using the AutoRespond feature
|
|
||||||
------
|
|
||||||
|
|
||||||
The AutoRespond bot allows you to automatically react on specific chat messages or server announcements.
|
|
||||||
You can use either a string to detect in chat messages, or an advanced regular expression.
|
|
||||||
For more information about how to define match rules, please refer to [`sample-matches.ini`](https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/config/sample-matches.ini).
|
|
||||||
See [Detecting chat messages](#detecting-chat-messages) if your messages are not detected or to learn more about regular expressions.
|
|
||||||
|
|
||||||
Using the Auto Attack
|
|
||||||
------
|
|
||||||
|
|
||||||
The AutoAttack bot allows you to automatically attack mobs around you within radius of 4 block.
|
|
||||||
To use this bot, you will need to enable **Entity Handling** in the configuration file first.
|
|
||||||
|
|
||||||
Using the Auto Fishing
|
|
||||||
------
|
|
||||||
|
|
||||||
The AutoFish bot can automatically fish for you.
|
|
||||||
To use this bot, you will need to enable **Entity Handling** in the configuration file first.
|
|
||||||
If you want to get an alert message when the fishing rod was broken, enable **Inventory Handling** in the configuration file.
|
|
||||||
A fishing rod with **Mending enchantment** is strongly recommended.
|
|
||||||
|
|
||||||
Steps for using this bot:
|
|
||||||
1. Hold a fishing rod and aim towards the sea before login with MCC
|
|
||||||
2. Make sure AutoFish is enabled in config file
|
|
||||||
3. Login with MCC
|
|
||||||
4. Do `/useitem` and you should see "threw a fishing rod"
|
|
||||||
5. To stop fishing, do `/useitem` again
|
|
||||||
|
|
||||||
Using the Mailer bot
|
|
||||||
------
|
|
||||||
|
|
||||||
The Mailer bot can store and relay mails much like Essential's /mail command.
|
|
||||||
|
|
||||||
* `/tell <Bot> mail [RECIPIENT] [MESSAGE]`: Save your message for future delivery
|
|
||||||
* `/tell <Bot> tellonym [RECIPIENT] [MESSAGE]`: Same, but the recipient will receive an anonymous mail
|
|
||||||
|
|
||||||
The bot will automatically deliver the mail when the recipient is online.
|
|
||||||
The bot also offers a /mailer command from the MCC command prompt:
|
|
||||||
|
|
||||||
* `/mailer getmails`: Show all mails in the console
|
|
||||||
* `/mailer addignored [NAME]`: Prevent a specific player from sending mails
|
|
||||||
* `/mailer removeignored [NAME]`: Lift the mailer restriction for this player
|
|
||||||
* `/mailer getignored`: Show all ignored players
|
|
||||||
|
|
||||||
**CAUTION: The bot identifies players by their name (Not by UUID!).**
|
|
||||||
A nickname plugin or a minecraft rename may cause mails going to the wrong player!
|
|
||||||
Never write something to the bot you wouldn't say in the normal chat (You have been warned!)
|
|
||||||
|
|
||||||
**Mailer Network:** The Mailer bot can relay messages between servers.
|
|
||||||
To set up a network of two or more bots, launch several instances with the bot activated and the same database.
|
|
||||||
If you launch two instances from one .exe they should syncronize automatically to the same file.
|
|
||||||
|
|
||||||
Using the AutoCraft bot
|
|
||||||
------
|
|
||||||
|
|
||||||
The AutoCraft bot can automatically craft items for you as long as you have defined the item recipe.
|
|
||||||
The bot will automatically generate a default configuration file on first launch, when `enabled` is set to `true` in the `[AutoCraft]` section in config. To use this bot, you will also need to enable **Inventory Handling** in the configuration file.
|
|
||||||
|
|
||||||
Useful commands description:
|
|
||||||
* `/autocraft reload`: Reload the config from disk. You can load your edited AutoCraft config without restarting the client.
|
|
||||||
* `/autocraft resetcfg`: Reset your AutoCraft config back to default. Use with care!
|
|
||||||
* `/autocraft list`: List all loaded recipes.
|
|
||||||
* `/autocraft start <name>`: Start the crafting process with the given recipe name you had defined.
|
|
||||||
* `/autocraft stop`: Stop the crafting process.
|
|
||||||
* `/autocraft help`: In-game help command.
|
|
||||||
|
|
||||||
How to define a recipe?
|
|
||||||
|
|
||||||
_Example_
|
|
||||||
```md
|
|
||||||
[Recipe]
|
|
||||||
name=whatever # name could be whatever you like. This field must be defined first
|
|
||||||
type=player # crafting table type: player or table
|
|
||||||
result=StoneButton # the resulting item
|
|
||||||
|
|
||||||
# define slots with their deserved item
|
|
||||||
slot1=stone # slot start with 1, count from left to right, top to bottom
|
|
||||||
# For the naming of the items, please see
|
|
||||||
# https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Inventory/ItemType.cs
|
|
||||||
```
|
|
||||||
|
|
||||||
1. You need to give your recipe a **name** so that you could start them later by name.
|
|
||||||
2. The size of crafting area needed for your recipe, 2x2 (player inventory) or 3x3 (crafting table). If you need to use the crafting table, make sure to set the **table coordinate** in the `[AutoCraft]` section.
|
|
||||||
3. The expected crafting result.
|
|
||||||
|
|
||||||
Then you need to define the position of each crafting materials.
|
|
||||||
Slots are indexed as follow:
|
|
||||||
|
|
||||||
2x2
|
|
||||||
```
|
|
||||||
╔═══╦═══╗
|
|
||||||
║ 1 ║ 2 ║
|
|
||||||
╠═══╬═══╣
|
|
||||||
║ 3 ║ 4 ║
|
|
||||||
╚═══╩═══╝
|
|
||||||
```
|
|
||||||
3x3
|
|
||||||
```
|
|
||||||
╔═══╦═══╦═══╗
|
|
||||||
║ 1 ║ 2 ║ 3 ║
|
|
||||||
╠═══╬═══╬═══╣
|
|
||||||
║ 4 ║ 5 ║ 6 ║
|
|
||||||
╠═══╬═══╬═══╣
|
|
||||||
║ 7 ║ 8 ║ 9 ║
|
|
||||||
╚═══╩═══╩═══╝
|
|
||||||
```
|
|
||||||
Simply use `slotIndex=MaterialName` to define material.
|
|
||||||
e.g. `slot1=coal` and `slot3=stick` will craft a torch.
|
|
||||||
|
|
||||||
For the naming of items, please see [ItemType.cs](https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Inventory/ItemType.cs) for all the material names.
|
|
||||||
|
|
||||||
After you finished writing your config, you can use `/autocraft start <recipe name>` to start crafting. Make sure to provide materials for your bot by placing them in inventory first.
|
|
||||||
|
|
||||||
Disclaimer
|
|
||||||
------
|
|
||||||
|
|
||||||
Even if everything should work, we are not responsible for any damage this app could cause to your computer or your server.
|
|
||||||
This app does not steal your password. If you don't trust it, don't use it or check & compile from the source code.
|
|
||||||
|
|
||||||
Also, remember that when you connect to a server with this program, you will appear where you left the last time.
|
|
||||||
This means that **you can die if you log in in an unsafe place on a survival server!**
|
|
||||||
Use the script scheduler bot to send a teleport command after logging in.
|
|
||||||
|
|
||||||
We remind you that **you may get banned** by your server for using this program. Use accordingly with server rules.
|
|
||||||
|
|
||||||
License
|
|
||||||
------
|
|
||||||
|
|
||||||
Minecraft Console Client is a totally free of charge, open source project.
|
|
||||||
Source code is available at https://github.com/MCCTeam/Minecraft-Console-Client
|
|
||||||
|
|
||||||
Unless specifically stated, source code is from the MCC Team or Contributors, and available under CDDL-1.0.
|
|
||||||
More info about CDDL-1.0: http://qstuff.blogspot.fr/2007/04/why-cddl.html
|
|
||||||
Full license at http://opensource.org/licenses/CDDL-1.0
|
|
||||||
|
|
||||||
Credits
|
|
||||||
------
|
|
||||||
|
|
||||||
_Project initiated by [ORelio](https://github.com/ORelio) in 2012 on the [Minecraft Forum](http://www.minecraftforum.net/topic/1314800-/)._
|
|
||||||
|
|
||||||
Many features would not have been possible without the help of our talented community:
|
|
||||||
|
|
||||||
**Maintainers**
|
|
||||||
|
|
||||||
ORelio
|
|
||||||
ReinforceZwei
|
|
||||||
|
|
||||||
**Ideas**
|
|
||||||
|
|
||||||
ambysdotnet, Awpocalypse, azoundria, bearbear12345, bSun0000, Cat7373, dagonzaros, Dids,
|
|
||||||
Elvang, fuckofftwice, GeorgH93, initsuj, JamieSinn, joshbean39, LehmusFIN, maski, medxo,
|
|
||||||
mobdon, MousePak, TNT-UP, TorchRJ, yayes2, Yoann166, ZizzyDizzyMC
|
|
||||||
|
|
||||||
**Bug Hunters**
|
|
||||||
|
|
||||||
1092CQ, ambysdotnet, bearbear12345, c0dei, Cat7373, Chtholly, Darkaegis, dbear20,
|
|
||||||
DigitalSniperz, doranchak, drXor, FantomHD, gerik43, ibspa, iTzMrpitBull, JamieSinn,
|
|
||||||
k3ldon, KenXeiko, link3321, lyze237, mattman00000, Nicconyancat, Pokechu22, ridgewell,
|
|
||||||
Ryan6578, Solethia, TNT-UP, TorchRJ, TRTrident, WeedIsGood, xp9kus, Yoann166
|
|
||||||
|
|
||||||
**Contributors**
|
|
||||||
|
|
||||||
Allyoutoo, Aragas, Bancey, bearbear12345, corbanmailloux, Daenges, dbear20, dogwatch,
|
|
||||||
initsuj, JamieSinn, justcool393, lokulin, maxpowa, medxo, milutinke, Pokechu22,
|
|
||||||
ReinforceZwei, repository, TheMeq, TheSnoozer, vkorn, v1RuX, yunusemregul, ZizzyDizzyMC
|
|
||||||
_... And all the [GitHub contributors](https://github.com/MCCTeam/Minecraft-Console-Client/graphs/contributors)!_
|
|
||||||
|
|
||||||
**Libraries:**
|
|
||||||
|
|
||||||
Minecraft Console Client also borrows code from the following libraries:
|
|
||||||
|
|
||||||
| Name | Purpose | Author | License |
|
|
||||||
|--------------|-------------------|------------------|---------|
|
|
||||||
| Biko | Proxy handling | Benton Stark | MIT |
|
|
||||||
| BouncyCastle | CFB-8 AES on Mono | The Legion | MIT |
|
|
||||||
| Heijden.Dns | DNS SRV Lookup | Geoffrey Huntley | MIT |
|
|
||||||
| DotNetZip | Zlib compression | Dino Chiesa | MS-PL |
|
|
||||||
|
|
||||||
**Support:**
|
|
||||||
|
|
||||||
If you still have any question after reading this file, you can get support here:
|
|
||||||
|
|
||||||
- GitHub Issues: https://github.com/MCCTeam/Minecraft-Console-Client/issues (You can contact the MCC Team here)
|
|
||||||
- Minecraft Forums: http://www.minecraftforum.net/topic/1314800-/ (Thead not maintained by the MCC Team anymore)
|
|
||||||
|
|
||||||
Code contributions, bug reports and any kind of comments are also highly appreciated :)
|
|
||||||
|
|
||||||
_© 2012-2020 ORelio_
|
|
||||||
_© 2020-2021 The MCC Team_
|
|
||||||
54
README.md
54
README.md
|
|
@ -1,19 +1,31 @@
|
||||||
Minecraft Console Client
|
<div align="center">
|
||||||
========================
|
|
||||||
|
<img src="https://i.pics.rs/LLDhE.png" alt="Logo"/>
|
||||||
|
|
||||||
|
# Minecraft Console Client (MCC)
|
||||||
|
|
||||||
|
[Documentation](https://mccteam.github.io/docs/) | [Download](#download) | [Installation](https://mccteam.github.io/docs/guide/installation.html) | [Configuration](https://mccteam.github.io/docs/guide/configuration.html) | [Usage](https://mccteam.github.io/docs/guide/usage.html)
|
||||||
|
|
||||||
[](https://github.com/MCCTeam/Minecraft-Console-Client/releases/latest)
|
[](https://github.com/MCCTeam/Minecraft-Console-Client/releases/latest)
|
||||||
|
|
||||||
Minecraft Console Client (MCC) is a lightweight app allowing you to connect to any Minecraft server, send commands and receive text messages in a fast and easy way without having to open the main Minecraft game. It also provides various automations that you can enable for administration and other purposes.
|
</div>
|
||||||
|
|
||||||
## Download 🔽
|
## **About ℹ️**
|
||||||
|
|
||||||
|
**Minecraft Console Client (MCC)** is a lightweight cross-platform open-source Minecraft TUI client for **Java** edition that allows you to connect to any Minecraft Java server, send commands and receive text messages in a fast and easy way without having to open the main Minecraft game.
|
||||||
|
|
||||||
|
## Download
|
||||||
|
|
||||||
Get development builds from the [Releases section](https://github.com/MCCTeam/Minecraft-Console-Client/releases/latest)
|
Get development builds from the [Releases section](https://github.com/MCCTeam/Minecraft-Console-Client/releases/latest)
|
||||||
|
|
||||||
## How to use 📚
|
## How to use 📚
|
||||||
|
|
||||||
* 🌐 https://mccteam.github.io/
|
- 🌐 [Full Documentation](https://mccteam.github.io/)
|
||||||
* 📝 [Sample configuration files](MinecraftClient/config/)
|
- 📦 [Installation](https://mccteam.github.io/docs/guide/installation.html)
|
||||||
* 📖 [Configuration Readme](https://github.com/MCCTeam/Minecraft-Console-Client/tree/master/MinecraftClient/config#minecraft-console-client-user-manual)
|
- 📖 [Usage](https://mccteam.github.io/docs/guide/usage.html)
|
||||||
|
- ⚙️ [Configuration](https://mccteam.github.io/docs/guide/configuration.html)
|
||||||
|
- 🤖 [Chat Bots](https://mccteam.github.io/docs/guide/chat-bots.html)
|
||||||
|
- 📝 [Sample configuration files](MinecraftClient/config/)
|
||||||
|
|
||||||
## Getting Help 🙋
|
## Getting Help 🙋
|
||||||
|
|
||||||
|
|
@ -25,7 +37,7 @@ We are a small community so we need help to implement upgrades for new Minecraft
|
||||||
|
|
||||||
## How to contribute 📝
|
## How to contribute 📝
|
||||||
|
|
||||||
If you'd like to contribute to Minecraft Console Client, great, just fork the repository and submit a pull request on the *Master* branch. To contribute to the website / online documentation see also the [Website repository](https://github.com/MCCTeam/MCCTeam.github.io).
|
If you'd like to contribute to Minecraft Console Client, great, just fork the repository and submit a pull request on the _Master_ branch. To contribute to the website / online documentation see also the [Website repository](https://github.com/MCCTeam/MCCTeam.github.io).
|
||||||
|
|
||||||
## Translating Minecraft Console Client 🌍
|
## Translating Minecraft Console Client 🌍
|
||||||
|
|
||||||
|
|
@ -35,31 +47,9 @@ To use the translated language file, place it under `lang/mcc/` folder and set y
|
||||||
|
|
||||||
For the names of the translation file, please see [this comment](https://github.com/MCCTeam/Minecraft-Console-Client/pull/1282#issuecomment-711150715).
|
For the names of the translation file, please see [this comment](https://github.com/MCCTeam/Minecraft-Console-Client/pull/1282#issuecomment-711150715).
|
||||||
|
|
||||||
## Building from source 🏗️
|
## Building from the source 🏗️
|
||||||
|
|
||||||
_The recommended development environment is [Visual Studio](https://visualstudio.microsoft.com/). If you want to build the project without installing a development environment, you may also follow these instructions:_
|
This section has been moved to our new [Documentation website](https://mccteam.github.io/docs/guide/installation.html#building-from-the-source-code).
|
||||||
|
|
||||||
First of all, download the .NET 6.0 SDK [here](https://dotnet.microsoft.com/en-us/download) and follow the install instructions.
|
|
||||||
|
|
||||||
Get a [zip of source code](https://github.com/MCCTeam/Minecraft-Console-Client/archive/master.zip), extract it and navigate to the `MinecraftClient` folder.
|
|
||||||
|
|
||||||
### On Windows 🪟
|
|
||||||
|
|
||||||
1. Open a Terminal / Command Prompt.
|
|
||||||
2. Type in `dotnet publish --no-self-contained -r win-x64 -c Release`.
|
|
||||||
3. If the build succeeds, you can find `MinecraftClient.exe` under `MinecraftClient\bin\Release\net6.0\win-x64\publish\`
|
|
||||||
|
|
||||||
### On Linux 🐧
|
|
||||||
|
|
||||||
1. Open a Terminal / Command Prompt.
|
|
||||||
2. Type in `dotnet publish --no-self-contained -r linux-x64 -c Release`.
|
|
||||||
3. If the build succeeds, you can find `MinecraftClient` under `MinecraftClient\bin\Release\net6.0\linux-x64\publish\`
|
|
||||||
|
|
||||||
### On Mac 🍎
|
|
||||||
|
|
||||||
1. Open a Terminal / Command Prompt.
|
|
||||||
2. Type in `dotnet publish --no-self-contained -r osx-x64 -c Release`.
|
|
||||||
3. If the build succeeds, you can find `MinecraftClient` under `MinecraftClient\bin\Release\net6.0\osx-x64\publish\`
|
|
||||||
|
|
||||||
## License ⚖️
|
## License ⚖️
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue