Minecraft-Console-Client/MinecraftClient/Protocol/Handlers/Protocol18.cs
Рома Данилов c2e2e85063
Add Entity.Item, Entity.CustomName, OnEntityMetadata event (#1222)
* Add New Event
* new Event
* Add OnEntityMetadaTa
* Update ChatBot.cs
* Update Protocol18.cs
* Update Entity.cs
* EntityCMD Update
* Update IMinecraftComHandler.cs
* Update Protocol18.cs
* Update IMinecraftComHandler.cs
* Update McClient.cs
* Update IMinecraftComHandler.cs
* Update McClient.cs
* Update McClient.cs
* Update McClient.cs
* Update McClient.cs
* Update ChatBot.cs
* Update McClient.cs
* Update Entity.cs
* Create EntityPose.cs
* Update MinecraftClient.csproj
* Update McClient.cs
* Update EntityPose.cs
* Update Entity.cs
* Update McClient.cs
* Remove debug line
* Update Entitycmd.cs
* Update Entity.cs
* Update McClient.cs
* Update Entity.cs
* Update McClient.cs
* Update McClient.cs
* Update Entity.cs
* Update McClient.cs
* Update Entitycmd.cs
* Update Entitycmd.cs
* Update McClient.cs
* Update Entitycmd.cs
* Update Entitycmd.cs
* Update Entity.cs
* Update McClient.cs
* Update Entitycmd.cs
* Update Entitycmd.cs
* Update Entitycmd.cs
* Update Entitycmd.cs
* Update Entitycmd.cs
* Update Entitycmd.cs
* Crash Fix on Item
* Crashes Fix
* Update McClient.cs
* Crashes fix
* Update McClient.cs
* Update Entity.cs
* Update Entity.cs
* Update McClient.cs
* Update McClient.cs
* Update McClient.cs
* Update McClient.cs
* Update McClient.cs
* Update McClient.cs
* Update McClient.cs
* Update ChatBot.cs
* Update IMinecraftComHandler.cs
* Update McClient.cs
* Update Protocol18.cs
* Update ChatBot.cs
* Update IMinecraftComHandler.cs
* Update Protocol18.cs
* Update McClient.cs
* Fix unaddressed issues
Co-authored-by: ORelio <oreliogitantispam.l0gin@spamgourmet.com>
2020-08-20 18:36:50 +02:00

1854 lines
95 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using MinecraftClient.Crypto;
using MinecraftClient.Proxy;
using System.Security.Cryptography;
using MinecraftClient.Mapping;
using MinecraftClient.Mapping.BlockPalettes;
using MinecraftClient.Mapping.EntityPalettes;
using MinecraftClient.Protocol.Handlers.Forge;
using MinecraftClient.Inventory;
using System.Windows.Forms;
using System.Data.SqlClient;
using System.Diagnostics;
using MinecraftClient.Inventory.ItemPalettes;
namespace MinecraftClient.Protocol.Handlers
{
/// <summary>
/// Implementation for Minecraft 1.7.X+ Protocols
/// </summary>
/// <remarks>
/// Typical update steps for implementing protocol changes for a new Minecraft version:
/// - Perform a diff between latest supported version in MCC and new stable version to support on https://wiki.vg/Protocol
/// - If there are any changes in packets implemented by MCC, add MCXXXVersion field below and implement new packet layouts
/// - If packet IDs were changed, also update getPacketIncomingType() and getPacketOutgoingID() inside Protocol18PacketTypes.cs
/// - Also see Material.cs and ItemType.cs for updating block and item data inside MCC
/// </remarks>
class Protocol18Handler : IMinecraftCom
{
internal const int MC18Version = 47;
internal const int MC19Version = 107;
internal const int MC191Version = 108;
internal const int MC110Version = 210;
internal const int MC1112Version = 316;
internal const int MC112Version = 335;
internal const int MC1122Version = 340;
internal const int MC113Version = 393;
internal const int MC114Version = 477;
internal const int MC115Version = 573;
internal const int MC1152Version = 578;
internal const int MC116Version = 735;
internal const int MC1161Version = 736;
internal const int MC1162Version = 751;
private int compression_treshold = 0;
private bool autocomplete_received = false;
private int autocomplete_transaction_id = 0;
private readonly List<string> autocomplete_result = new List<string>();
private readonly Dictionary<int, short> window_actions = new Dictionary<int, short>();
private bool login_phase = true;
private int protocolversion;
private int currentDimension;
Protocol18Forge pForge;
Protocol18Terrain pTerrain;
IMinecraftComHandler handler;
EntityPalette entityPalette;
ItemPalette itemPalette;
SocketWrapper socketWrapper;
DataTypes dataTypes;
Thread netRead;
public Protocol18Handler(TcpClient Client, int protocolVersion, IMinecraftComHandler handler, ForgeInfo forgeInfo)
{
ConsoleIO.SetAutoCompleteEngine(this);
ChatParser.InitTranslations();
this.socketWrapper = new SocketWrapper(Client);
this.dataTypes = new DataTypes(protocolVersion);
this.protocolversion = protocolVersion;
this.handler = handler;
this.pForge = new Protocol18Forge(forgeInfo, protocolVersion, dataTypes, this, handler);
this.pTerrain = new Protocol18Terrain(protocolVersion, dataTypes, handler);
if (handler.GetTerrainEnabled() && protocolversion > MC1152Version)
{
ConsoleIO.WriteLineFormatted("§8Terrain & Movements currently not handled for that MC version.");
handler.SetTerrainEnabled(false);
}
if (handler.GetInventoryEnabled() && (protocolversion < MC110Version || protocolversion > MC1162Version))
{
ConsoleIO.WriteLineFormatted("§8Inventories are currently not handled for that MC version.");
handler.SetInventoryEnabled(false);
}
if (handler.GetEntityHandlingEnabled() && (protocolversion < MC110Version || protocolversion > MC1162Version))
{
ConsoleIO.WriteLineFormatted("§8Entities are currently not handled for that MC version.");
handler.SetEntityHandlingEnabled(false);
}
// Block palette
if (protocolversion >= MC113Version)
{
if (protocolVersion > MC1152Version && handler.GetTerrainEnabled())
throw new NotImplementedException("Please update block types handling for this Minecraft version. See Material.cs");
if (protocolVersion >= MC115Version)
Block.Palette = new Palette115();
else if (protocolVersion >= MC114Version)
Block.Palette = new Palette114();
else Block.Palette = new Palette113();
}
else Block.Palette = new Palette112();
// Entity palette
if (protocolversion >= MC113Version)
{
if (protocolversion > MC1162Version && handler.GetEntityHandlingEnabled())
throw new NotImplementedException("Please update entity types handling for this Minecraft version. See EntityType.cs");
if (protocolversion >= MC1162Version)
entityPalette = new EntityPalette1162();
else if (protocolversion >= MC116Version)
entityPalette = new EntityPalette1161();
else if (protocolversion >= MC115Version)
entityPalette = new EntityPalette115();
else if (protocolVersion >= MC114Version)
entityPalette = new EntityPalette114();
else entityPalette = new EntityPalette113();
}
else entityPalette = new EntityPalette112();
// Item palette
if (protocolversion >= MC116Version)
{
if (protocolversion > MC1162Version && handler.GetInventoryEnabled())
throw new NotImplementedException("Please update item types handling for this Minecraft version. See ItemType.cs");
if (protocolversion >= MC1162Version)
itemPalette = new ItemPalette1162();
else itemPalette = new ItemPalette1161();
}
else itemPalette = new ItemPalette115();
}
/// <summary>
/// Separate thread. Network reading loop.
/// </summary>
private void Updater()
{
try
{
do
{
Thread.Sleep(100);
}
while (Update());
}
catch (System.IO.IOException) { }
catch (SocketException) { }
catch (ObjectDisposedException) { }
handler.OnConnectionLost(ChatBot.DisconnectReason.ConnectionLost, "");
}
/// <summary>
/// Read data from the network. Should be called on a separate thread.
/// </summary>
/// <returns>FALSE if an error occured, TRUE otherwise.</returns>
private bool Update()
{
handler.OnUpdate();
if (!socketWrapper.IsConnected())
return false;
try
{
while (socketWrapper.HasDataAvailable())
{
int packetID = 0;
Queue<byte> packetData = new Queue<byte>();
ReadNextPacket(ref packetID, packetData);
HandlePacket(packetID, new Queue<byte>(packetData));
}
}
catch (System.IO.IOException) { return false; }
catch (SocketException) { return false; }
catch (NullReferenceException) { return false; }
return true;
}
/// <summary>
/// Read the next packet from the network
/// </summary>
/// <param name="packetID">will contain packet ID</param>
/// <param name="packetData">will contain raw packet Data</param>
internal void ReadNextPacket(ref int packetID, Queue<byte> packetData)
{
packetData.Clear();
int size = dataTypes.ReadNextVarIntRAW(socketWrapper); //Packet size
byte[] rawpacket = socketWrapper.ReadDataRAW(size); //Packet contents
for (int i = 0; i < rawpacket.Length; i++)
packetData.Enqueue(rawpacket[i]);
//Handle packet decompression
if (protocolversion >= MC18Version
&& compression_treshold > 0)
{
int sizeUncompressed = dataTypes.ReadNextVarInt(packetData);
if (sizeUncompressed != 0) // != 0 means compressed, let's decompress
{
byte[] toDecompress = packetData.ToArray();
byte[] uncompressed = ZlibUtils.Decompress(toDecompress, sizeUncompressed);
packetData.Clear();
for (int i = 0; i < uncompressed.Length; i++)
packetData.Enqueue(uncompressed[i]);
}
}
packetID = dataTypes.ReadNextVarInt(packetData); //Packet ID
}
/// <summary>
/// Handle the given packet
/// </summary>
/// <param name="packetID">Packet ID</param>
/// <param name="packetData">Packet contents</param>
/// <returns>TRUE if the packet was processed, FALSE if ignored or unknown</returns>
internal bool HandlePacket(int packetID, Queue<byte> packetData)
{
try
{
if (login_phase)
{
switch (packetID) //Packet IDs are different while logging in
{
case 0x03:
if (protocolversion >= MC18Version)
compression_treshold = dataTypes.ReadNextVarInt(packetData);
break;
case 0x04:
int messageId = dataTypes.ReadNextVarInt(packetData);
string channel = dataTypes.ReadNextString(packetData);
List<byte> responseData = new List<byte>();
bool understood = pForge.HandleLoginPluginRequest(channel, packetData, ref responseData);
SendLoginPluginResponse(messageId, understood, responseData.ToArray());
return understood;
default:
return false; //Ignored packet
}
}
// Regular in-game packets
else switch (Protocol18PacketTypes.GetPacketIncomingType(packetID, protocolversion))
{
case PacketIncomingType.KeepAlive:
SendPacket(PacketOutgoingType.KeepAlive, packetData);
handler.OnServerKeepAlive();
break;
case PacketIncomingType.JoinGame:
handler.OnGameJoined();
int playerEntityID = dataTypes.ReadNextInt(packetData);
handler.OnReceivePlayerEntityID(playerEntityID);
if (protocolversion >= MC1162Version)
dataTypes.ReadNextBool(packetData); // Is hardcore - 1.16.2 and above
handler.OnGamemodeUpdate(Guid.Empty, dataTypes.ReadNextByte(packetData));
if (protocolversion >= MC116Version)
{
dataTypes.ReadNextByte(packetData); // Previous Gamemode - 1.16 and above
int worldCount = dataTypes.ReadNextVarInt(packetData); // World Count - 1.16 and above
for (int i = 0; i < worldCount; i++)
dataTypes.ReadNextString(packetData); // World Names - 1.16 and above
dataTypes.ReadNextNbt(packetData); // Dimension Codec - 1.16 and above
}
//Current dimension - String identifier in 1.16, varInt below 1.16, byte below 1.9.1
if (protocolversion >= MC116Version)
{
if (protocolversion >= MC1162Version)
dataTypes.ReadNextNbt(packetData);
else
dataTypes.ReadNextString(packetData);
// TODO handle dimensions for 1.16+, needed for terrain handling
this.currentDimension = 0;
}
else if (protocolversion >= MC191Version)
this.currentDimension = dataTypes.ReadNextInt(packetData);
else
this.currentDimension = (sbyte)dataTypes.ReadNextByte(packetData);
if (protocolversion < MC114Version)
dataTypes.ReadNextByte(packetData); // Difficulty - 1.13 and below
if (protocolversion >= MC116Version)
dataTypes.ReadNextString(packetData); // World Name - 1.16 and above
if (protocolversion >= MC115Version)
dataTypes.ReadNextLong(packetData); // Hashed world seed - 1.15 and above
if (protocolversion >= MC1162Version)
dataTypes.ReadNextVarInt(packetData); // Max Players - 1.16.2 and above
else
dataTypes.ReadNextByte(packetData); // Max Players - 1.16.1 and below
if (protocolversion < MC116Version)
dataTypes.ReadNextString(packetData); // Level Type - 1.15 and below
if (protocolversion >= MC114Version)
dataTypes.ReadNextVarInt(packetData); // View distance - 1.14 and above
if (protocolversion >= MC18Version)
dataTypes.ReadNextBool(packetData); // Reduced debug info - 1.8 and above
if (protocolversion >= MC115Version)
dataTypes.ReadNextBool(packetData); // Enable respawn screen - 1.15 and above
if (protocolversion >= MC116Version)
{
dataTypes.ReadNextBool(packetData); // Is Debug - 1.16 and above
dataTypes.ReadNextBool(packetData); // Is Flat - 1.16 and above
}
break;
case PacketIncomingType.ChatMessage:
string message = dataTypes.ReadNextString(packetData);
if (protocolversion >= MC18Version)
{
//Hide system messages or xp bar messages?
byte messageType = dataTypes.ReadNextByte(packetData);
if ((messageType == 1 && !Settings.DisplaySystemMessages)
|| (messageType == 2 && !Settings.DisplayXPBarMessages))
break;
}
handler.OnTextReceived(message, true);
break;
case PacketIncomingType.Respawn:
if (protocolversion >= MC116Version)
{
// TODO handle dimensions for 1.16+, needed for terrain handling
dataTypes.ReadNextString(packetData);
this.currentDimension = 0;
}
else
{
// 1.15 and below
this.currentDimension = dataTypes.ReadNextInt(packetData);
}
if (protocolversion >= MC116Version)
dataTypes.ReadNextString(packetData); // World Name - 1.16 and above
if (protocolversion < MC114Version)
dataTypes.ReadNextByte(packetData); // Difficulty - 1.13 and below
if (protocolversion >= MC115Version)
dataTypes.ReadNextLong(packetData); // Hashed world seed - 1.15 and above
dataTypes.ReadNextByte(packetData); // Gamemode
if (protocolversion >= MC116Version)
dataTypes.ReadNextByte(packetData); // Previous Game mode - 1.16 and above
if (protocolversion < MC116Version)
dataTypes.ReadNextString(packetData); // Level Type - 1.15 and below
if (protocolversion >= MC116Version)
{
dataTypes.ReadNextBool(packetData); // Is Debug - 1.16 and above
dataTypes.ReadNextBool(packetData); // Is Flat - 1.16 and above
dataTypes.ReadNextBool(packetData); // Copy metadata - 1.16 and above
}
handler.OnRespawn();
break;
case PacketIncomingType.PlayerPositionAndLook:
// These always need to be read, since we need the field after them for teleport confirm
double x = dataTypes.ReadNextDouble(packetData);
double y = dataTypes.ReadNextDouble(packetData);
double z = dataTypes.ReadNextDouble(packetData);
float yaw = dataTypes.ReadNextFloat(packetData);
float pitch = dataTypes.ReadNextFloat(packetData);
byte locMask = dataTypes.ReadNextByte(packetData);
// entity handling require player pos for distance calculating
if (handler.GetTerrainEnabled() || handler.GetEntityHandlingEnabled())
{
if (protocolversion >= MC18Version)
{
Location location = handler.GetCurrentLocation();
location.X = (locMask & 1 << 0) != 0 ? location.X + x : x;
location.Y = (locMask & 1 << 1) != 0 ? location.Y + y : y;
location.Z = (locMask & 1 << 2) != 0 ? location.Z + z : z;
handler.UpdateLocation(location, yaw, pitch);
}
else handler.UpdateLocation(new Location(x, y, z), yaw, pitch);
}
if (protocolversion >= MC19Version)
{
int teleportID = dataTypes.ReadNextVarInt(packetData);
// Teleport confirm packet
SendPacket(PacketOutgoingType.TeleportConfirm, dataTypes.GetVarInt(teleportID));
}
break;
case PacketIncomingType.ChunkData:
if (handler.GetTerrainEnabled())
{
int chunkX = dataTypes.ReadNextInt(packetData);
int chunkZ = dataTypes.ReadNextInt(packetData);
bool chunksContinuous = dataTypes.ReadNextBool(packetData);
ushort chunkMask = protocolversion >= MC19Version
? (ushort)dataTypes.ReadNextVarInt(packetData)
: dataTypes.ReadNextUShort(packetData);
if (protocolversion < MC18Version)
{
ushort addBitmap = dataTypes.ReadNextUShort(packetData);
int compressedDataSize = dataTypes.ReadNextInt(packetData);
byte[] compressed = dataTypes.ReadData(compressedDataSize, packetData);
byte[] decompressed = ZlibUtils.Decompress(compressed);
pTerrain.ProcessChunkColumnData(chunkX, chunkZ, chunkMask, addBitmap, currentDimension == 0, chunksContinuous, currentDimension, new Queue<byte>(decompressed));
}
else
{
if (protocolversion >= MC114Version)
dataTypes.ReadNextNbt(packetData); // Heightmaps - 1.14 and above
if (protocolversion >= MC115Version && chunksContinuous)
dataTypes.ReadData(1024 * 4, packetData); // Biomes - 1.15 and above
int dataSize = dataTypes.ReadNextVarInt(packetData);
pTerrain.ProcessChunkColumnData(chunkX, chunkZ, chunkMask, 0, false, chunksContinuous, currentDimension, packetData);
}
}
break;
case PacketIncomingType.MapData:
int mapid = dataTypes.ReadNextVarInt(packetData);
byte scale = dataTypes.ReadNextByte(packetData);
bool trackingposition = dataTypes.ReadNextBool(packetData);
bool locked = false;
if (protocolversion >= MC114Version)
{
locked = dataTypes.ReadNextBool(packetData);
}
int iconcount = dataTypes.ReadNextVarInt(packetData);
handler.OnMapData(mapid, scale, trackingposition, locked, iconcount);
break;
case PacketIncomingType.Title:
if (protocolversion >= MC18Version)
{
int action2 = dataTypes.ReadNextVarInt(packetData);
string titletext = String.Empty;
string subtitletext = String.Empty;
string actionbartext = String.Empty;
string json = String.Empty;
int fadein = -1;
int stay = -1;
int fadeout = -1;
if (protocolversion >= MC110Version)
{
if (action2 == 0)
{
json = titletext;
titletext = ChatParser.ParseText(dataTypes.ReadNextString(packetData));
}
else if (action2 == 1)
{
json = subtitletext;
subtitletext = ChatParser.ParseText(dataTypes.ReadNextString(packetData));
}
else if (action2 == 2)
{
json = actionbartext;
actionbartext = ChatParser.ParseText(dataTypes.ReadNextString(packetData));
}
else if (action2 == 3)
{
fadein = dataTypes.ReadNextInt(packetData);
stay = dataTypes.ReadNextInt(packetData);
fadeout = dataTypes.ReadNextInt(packetData);
}
}
else
{
if (action2 == 0)
{
json = titletext;
titletext = ChatParser.ParseText(dataTypes.ReadNextString(packetData));
}
else if (action2 == 1)
{
json = subtitletext;
subtitletext = ChatParser.ParseText(dataTypes.ReadNextString(packetData));
}
else if (action2 == 2)
{
fadein = dataTypes.ReadNextInt(packetData);
stay = dataTypes.ReadNextInt(packetData);
fadeout = dataTypes.ReadNextInt(packetData);
}
}
handler.OnTitle(action2, titletext, subtitletext, actionbartext, fadein, stay, fadeout, json);
}
break;
case PacketIncomingType.MultiBlockChange:
if (handler.GetTerrainEnabled())
{
int chunkX = dataTypes.ReadNextInt(packetData);
int chunkZ = dataTypes.ReadNextInt(packetData);
int recordCount = protocolversion < MC18Version
? (int)dataTypes.ReadNextShort(packetData)
: dataTypes.ReadNextVarInt(packetData);
for (int i = 0; i < recordCount; i++)
{
byte locationXZ;
ushort blockIdMeta;
int blockY;
if (protocolversion < MC18Version)
{
blockIdMeta = dataTypes.ReadNextUShort(packetData);
blockY = (ushort)dataTypes.ReadNextByte(packetData);
locationXZ = dataTypes.ReadNextByte(packetData);
}
else
{
locationXZ = dataTypes.ReadNextByte(packetData);
blockY = (ushort)dataTypes.ReadNextByte(packetData);
blockIdMeta = (ushort)dataTypes.ReadNextVarInt(packetData);
}
int blockX = locationXZ >> 4;
int blockZ = locationXZ & 0x0F;
Block block = new Block(blockIdMeta);
handler.GetWorld().SetBlock(new Location(chunkX, chunkZ, blockX, blockY, blockZ), block);
}
}
break;
case PacketIncomingType.BlockChange:
if (handler.GetTerrainEnabled())
{
if (protocolversion < MC18Version)
{
int blockX = dataTypes.ReadNextInt(packetData);
int blockY = dataTypes.ReadNextByte(packetData);
int blockZ = dataTypes.ReadNextInt(packetData);
short blockId = (short)dataTypes.ReadNextVarInt(packetData);
byte blockMeta = dataTypes.ReadNextByte(packetData);
handler.GetWorld().SetBlock(new Location(blockX, blockY, blockZ), new Block(blockId, blockMeta));
}
else handler.GetWorld().SetBlock(dataTypes.ReadNextLocation(packetData), new Block((ushort)dataTypes.ReadNextVarInt(packetData)));
}
break;
case PacketIncomingType.MapChunkBulk:
if (protocolversion < MC19Version && handler.GetTerrainEnabled())
{
int chunkCount;
bool hasSkyLight;
Queue<byte> chunkData = packetData;
//Read global fields
if (protocolversion < MC18Version)
{
chunkCount = dataTypes.ReadNextShort(packetData);
int compressedDataSize = dataTypes.ReadNextInt(packetData);
hasSkyLight = dataTypes.ReadNextBool(packetData);
byte[] compressed = dataTypes.ReadData(compressedDataSize, packetData);
byte[] decompressed = ZlibUtils.Decompress(compressed);
chunkData = new Queue<byte>(decompressed);
}
else
{
hasSkyLight = dataTypes.ReadNextBool(packetData);
chunkCount = dataTypes.ReadNextVarInt(packetData);
}
//Read chunk records
int[] chunkXs = new int[chunkCount];
int[] chunkZs = new int[chunkCount];
ushort[] chunkMasks = new ushort[chunkCount];
ushort[] addBitmaps = new ushort[chunkCount];
for (int chunkColumnNo = 0; chunkColumnNo < chunkCount; chunkColumnNo++)
{
chunkXs[chunkColumnNo] = dataTypes.ReadNextInt(packetData);
chunkZs[chunkColumnNo] = dataTypes.ReadNextInt(packetData);
chunkMasks[chunkColumnNo] = dataTypes.ReadNextUShort(packetData);
addBitmaps[chunkColumnNo] = protocolversion < MC18Version
? dataTypes.ReadNextUShort(packetData)
: (ushort)0;
}
//Process chunk records
for (int chunkColumnNo = 0; chunkColumnNo < chunkCount; chunkColumnNo++)
pTerrain.ProcessChunkColumnData(chunkXs[chunkColumnNo], chunkZs[chunkColumnNo], chunkMasks[chunkColumnNo], addBitmaps[chunkColumnNo], hasSkyLight, true, currentDimension, chunkData);
}
break;
case PacketIncomingType.UnloadChunk:
if (protocolversion >= MC19Version && handler.GetTerrainEnabled())
{
int chunkX = dataTypes.ReadNextInt(packetData);
int chunkZ = dataTypes.ReadNextInt(packetData);
handler.GetWorld()[chunkX, chunkZ] = null;
}
break;
case PacketIncomingType.PlayerListUpdate:
if (protocolversion >= MC18Version)
{
int action = dataTypes.ReadNextVarInt(packetData);
int numActions = dataTypes.ReadNextVarInt(packetData);
for (int i = 0; i < numActions; i++)
{
Guid uuid = dataTypes.ReadNextUUID(packetData);
switch (action)
{
case 0x00: //Player Join
string name = dataTypes.ReadNextString(packetData);
int propNum = dataTypes.ReadNextVarInt(packetData);
for (int p = 0; p < propNum; p++)
{
string key = dataTypes.ReadNextString(packetData);
string val = dataTypes.ReadNextString(packetData);
if (dataTypes.ReadNextBool(packetData))
dataTypes.ReadNextString(packetData);
}
handler.OnGamemodeUpdate(uuid, dataTypes.ReadNextVarInt(packetData));
dataTypes.ReadNextVarInt(packetData);
if (dataTypes.ReadNextBool(packetData))
dataTypes.ReadNextString(packetData);
handler.OnPlayerJoin(uuid, name);
break;
case 0x01: //Update gamemode
handler.OnGamemodeUpdate(uuid, dataTypes.ReadNextVarInt(packetData));
break;
case 0x02: //Update latency
int latency = dataTypes.ReadNextVarInt(packetData);
handler.OnLatencyUpdate(uuid, latency); //Update latency;
break;
case 0x03: //Update display name
if (dataTypes.ReadNextBool(packetData))
dataTypes.ReadNextString(packetData);
break;
case 0x04: //Player Leave
handler.OnPlayerLeave(uuid);
break;
default:
//Unknown player list item type
break;
}
}
}
else //MC 1.7.X does not provide UUID in tab-list updates
{
string name = dataTypes.ReadNextString(packetData);
bool online = dataTypes.ReadNextBool(packetData);
short ping = dataTypes.ReadNextShort(packetData);
Guid FakeUUID = new Guid(MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(name)).Take(16).ToArray());
if (online)
handler.OnPlayerJoin(FakeUUID, name);
else handler.OnPlayerLeave(FakeUUID);
}
break;
case PacketIncomingType.TabCompleteResult:
if (protocolversion >= MC113Version)
{
autocomplete_transaction_id = dataTypes.ReadNextVarInt(packetData);
dataTypes.ReadNextVarInt(packetData); // Start of text to replace
dataTypes.ReadNextVarInt(packetData); // Length of text to replace
}
int autocomplete_count = dataTypes.ReadNextVarInt(packetData);
autocomplete_result.Clear();
for (int i = 0; i < autocomplete_count; i++)
{
autocomplete_result.Add(dataTypes.ReadNextString(packetData));
if (protocolversion >= MC113Version)
{
// Skip optional tooltip for each tab-complete result
if (dataTypes.ReadNextBool(packetData))
dataTypes.ReadNextString(packetData);
}
}
autocomplete_received = true;
break;
case PacketIncomingType.PluginMessage:
String channel = dataTypes.ReadNextString(packetData);
// Length is unneeded as the whole remaining packetData is the entire payload of the packet.
if (protocolversion < MC18Version)
pForge.ReadNextVarShort(packetData);
handler.OnPluginChannelMessage(channel, packetData.ToArray());
return pForge.HandlePluginMessage(channel, packetData, ref currentDimension);
case PacketIncomingType.KickPacket:
handler.OnConnectionLost(ChatBot.DisconnectReason.InGameKick, ChatParser.ParseText(dataTypes.ReadNextString(packetData)));
return false;
case PacketIncomingType.NetworkCompressionTreshold:
if (protocolversion >= MC18Version && protocolversion < MC19Version)
compression_treshold = dataTypes.ReadNextVarInt(packetData);
break;
case PacketIncomingType.OpenWindow:
if (handler.GetInventoryEnabled())
{
if (protocolversion < MC114Version)
{
// MC 1.13 or lower
byte windowID = dataTypes.ReadNextByte(packetData);
string type = dataTypes.ReadNextString(packetData).Replace("minecraft:", "").ToUpper();
ContainerTypeOld inventoryType = (ContainerTypeOld)Enum.Parse(typeof(ContainerTypeOld), type);
string title = dataTypes.ReadNextString(packetData);
byte slots = dataTypes.ReadNextByte(packetData);
Container inventory = new Container(windowID, inventoryType, ChatParser.ParseText(title));
handler.OnInventoryOpen(windowID, inventory);
}
else
{
// MC 1.14 or greater
int windowID = dataTypes.ReadNextVarInt(packetData);
int windowType = dataTypes.ReadNextVarInt(packetData);
string title = dataTypes.ReadNextString(packetData);
Container inventory = new Container(windowID, windowType, ChatParser.ParseText(title));
handler.OnInventoryOpen(windowID, inventory);
}
}
break;
case PacketIncomingType.CloseWindow:
if (handler.GetInventoryEnabled())
{
byte windowID = dataTypes.ReadNextByte(packetData);
lock (window_actions) { window_actions[windowID] = 0; }
handler.OnInventoryClose(windowID);
}
break;
case PacketIncomingType.WindowItems:
if (handler.GetInventoryEnabled())
{
byte windowId = dataTypes.ReadNextByte(packetData);
short elements = dataTypes.ReadNextShort(packetData);
Dictionary<int, Item> inventorySlots = new Dictionary<int, Item>();
for (short slotId = 0; slotId < elements; slotId++)
{
Item item = dataTypes.ReadNextItemSlot(packetData, itemPalette);
if (item != null)
inventorySlots[slotId] = item;
}
handler.OnWindowItems(windowId, inventorySlots);
}
break;
case PacketIncomingType.SetSlot:
if (handler.GetInventoryEnabled())
{
byte windowID = dataTypes.ReadNextByte(packetData);
short slotID = dataTypes.ReadNextShort(packetData);
Item item = dataTypes.ReadNextItemSlot(packetData, itemPalette);
handler.OnSetSlot(windowID, slotID, item);
}
break;
case PacketIncomingType.WindowConfirmation:
if (handler.GetInventoryEnabled())
{
byte windowID = dataTypes.ReadNextByte(packetData);
short actionID = dataTypes.ReadNextShort(packetData);
bool accepted = dataTypes.ReadNextBool(packetData);
if (!accepted)
{
SendWindowConfirmation(windowID, actionID, accepted);
}
}
break;
case PacketIncomingType.ResourcePackSend:
string url = dataTypes.ReadNextString(packetData);
string hash = dataTypes.ReadNextString(packetData);
// Some server plugins may send invalid resource packs to probe the client and we need to ignore them (issue #1056)
if (hash.Length != 40)
break;
//Send back "accepted" and "successfully loaded" responses for plugins making use of resource pack mandatory
byte[] responseHeader = new byte[0];
if (protocolversion < MC110Version) //MC 1.10 does not include resource pack hash in responses
responseHeader = dataTypes.ConcatBytes(dataTypes.GetVarInt(hash.Length), Encoding.UTF8.GetBytes(hash));
SendPacket(PacketOutgoingType.ResourcePackStatus, dataTypes.ConcatBytes(responseHeader, dataTypes.GetVarInt(3))); //Accepted pack
SendPacket(PacketOutgoingType.ResourcePackStatus, dataTypes.ConcatBytes(responseHeader, dataTypes.GetVarInt(0))); //Successfully loaded
break;
case PacketIncomingType.SpawnEntity:
if (handler.GetEntityHandlingEnabled())
{
Entity entity = dataTypes.ReadNextEntity(packetData, entityPalette, false);
handler.OnSpawnEntity(entity);
}
break;
case PacketIncomingType.EntityEquipment:
if (handler.GetEntityHandlingEnabled())
{
int entityid = dataTypes.ReadNextVarInt(packetData);
if (protocolversion >= MC116Version)
{
bool hasNext;
do
{
byte bitsData = dataTypes.ReadNextByte(packetData);
// Top bit set if another entry follows, and otherwise unset if this is the last item in the array
hasNext = (bitsData >> 7) == 1 ? true : false;
int slot2 = bitsData >> 1;
Item item = dataTypes.ReadNextItemSlot(packetData, itemPalette);
handler.OnEntityEquipment(entityid, slot2, item);
} while (hasNext);
}
else
{
int slot2 = dataTypes.ReadNextVarInt(packetData);
Item item = dataTypes.ReadNextItemSlot(packetData, itemPalette);
handler.OnEntityEquipment(entityid, slot2, item);
}
}
break;
case PacketIncomingType.SpawnLivingEntity:
if (handler.GetEntityHandlingEnabled())
{
Entity entity = dataTypes.ReadNextEntity(packetData, entityPalette, true);
// packet before 1.15 has metadata at the end
// this is not handled in dataTypes.ReadNextEntity()
// we are simply ignoring leftover data in packet
handler.OnSpawnEntity(entity);
}
break;
case PacketIncomingType.SpawnPlayer:
if (handler.GetEntityHandlingEnabled())
{
int EntityID = dataTypes.ReadNextVarInt(packetData);
Guid UUID = dataTypes.ReadNextUUID(packetData);
double X = dataTypes.ReadNextDouble(packetData);
double Y = dataTypes.ReadNextDouble(packetData);
double Z = dataTypes.ReadNextDouble(packetData);
byte Yaw = dataTypes.ReadNextByte(packetData);
byte Pitch = dataTypes.ReadNextByte(packetData);
Location EntityLocation = new Location(X, Y, Z);
handler.OnSpawnPlayer(EntityID, UUID, EntityLocation, Yaw, Pitch);
}
break;
case PacketIncomingType.EntityEffect:
if (handler.GetEntityHandlingEnabled())
{
int entityid = dataTypes.ReadNextVarInt(packetData);
Inventory.Effects effect = Effects.Speed;
if (Enum.TryParse(dataTypes.ReadNextByte(packetData).ToString(), out effect))
{
int amplifier = dataTypes.ReadNextByte(packetData);
int duration = dataTypes.ReadNextVarInt(packetData);
byte flags = dataTypes.ReadNextByte(packetData);
handler.OnEntityEffect(entityid, effect, amplifier, duration, flags);
}
}
break;
case PacketIncomingType.DestroyEntities:
if (handler.GetEntityHandlingEnabled())
{
int EntityCount = dataTypes.ReadNextVarInt(packetData);
int[] EntitiesList = new int[EntityCount];
for (int i = 0; i < EntityCount; i++)
{
EntitiesList[i] = dataTypes.ReadNextVarInt(packetData);
}
handler.OnDestroyEntities(EntitiesList);
}
break;
case PacketIncomingType.EntityPosition:
if (handler.GetEntityHandlingEnabled())
{
int EntityID = dataTypes.ReadNextVarInt(packetData);
Double DeltaX = Convert.ToDouble(dataTypes.ReadNextShort(packetData));
Double DeltaY = Convert.ToDouble(dataTypes.ReadNextShort(packetData));
Double DeltaZ = Convert.ToDouble(dataTypes.ReadNextShort(packetData));
bool OnGround = dataTypes.ReadNextBool(packetData);
DeltaX = DeltaX / (128 * 32);
DeltaY = DeltaY / (128 * 32);
DeltaZ = DeltaZ / (128 * 32);
handler.OnEntityPosition(EntityID, DeltaX, DeltaY, DeltaZ, OnGround);
}
break;
case PacketIncomingType.EntityPositionAndRotation:
if (handler.GetEntityHandlingEnabled())
{
int EntityID = dataTypes.ReadNextVarInt(packetData);
Double DeltaX = Convert.ToDouble(dataTypes.ReadNextShort(packetData));
Double DeltaY = Convert.ToDouble(dataTypes.ReadNextShort(packetData));
Double DeltaZ = Convert.ToDouble(dataTypes.ReadNextShort(packetData));
byte _yaw = dataTypes.ReadNextByte(packetData);
byte _pitch = dataTypes.ReadNextByte(packetData);
bool OnGround = dataTypes.ReadNextBool(packetData);
DeltaX = DeltaX / (128 * 32);
DeltaY = DeltaY / (128 * 32);
DeltaZ = DeltaZ / (128 * 32);
handler.OnEntityPosition(EntityID, DeltaX, DeltaY, DeltaZ, OnGround);
}
break;
case PacketIncomingType.EntityProperties:
if (handler.GetEntityHandlingEnabled())
{
int EntityID = dataTypes.ReadNextVarInt(packetData);
int NumberOfProperties = dataTypes.ReadNextInt(packetData);
Dictionary<string, Double> keys = new Dictionary<string, Double>();
for (int i = 0; i < NumberOfProperties; i++)
{
string _key = dataTypes.ReadNextString(packetData);
Double _value = dataTypes.ReadNextDouble(packetData);
List<double> op0 = new List<double>();
List<double> op1 = new List<double>();
List<double> op2 = new List<double>();
int NumberOfModifiers = dataTypes.ReadNextVarInt(packetData);
for (int j = 0; j < NumberOfModifiers; j++)
{
dataTypes.ReadNextUUID(packetData);
Double amount = dataTypes.ReadNextDouble(packetData);
byte operation = dataTypes.ReadNextByte(packetData);
switch (operation)
{
case 0: op0.Add(amount); break;
case 1: op1.Add(amount); break;
case 2: op2.Add(amount + 1); break;
}
}
if (op0.Count > 0) _value += op0.Sum();
if (op1.Count > 0) _value *= 1 + op1.Sum();
if (op2.Count > 0) _value *= op2.Aggregate((a, _x) => a * _x);
keys.Add(_key, _value);
}
handler.OnEntityProperties(EntityID, keys);
}
break;
case PacketIncomingType.EntityMetadata:
if (handler.GetEntityHandlingEnabled())
{
int EntityID = dataTypes.ReadNextVarInt(packetData);
Dictionary<int, object> metadata = dataTypes.ReadNextMetadata(packetData, itemPalette);
int healthField = protocolversion >= MC114Version ? 8 : 7; // Health is field no. 7 in 1.10+ and 8 in 1.14+
if (metadata.ContainsKey(healthField) && metadata[healthField].GetType() == typeof(float))
handler.OnEntityHealth(EntityID, (float)metadata[healthField]);
handler.OnEntityMetadata(EntityID, metadata);
}
break;
case PacketIncomingType.TimeUpdate:
long WorldAge = dataTypes.ReadNextLong(packetData);
long TimeOfday = dataTypes.ReadNextLong(packetData);
handler.OnTimeUpdate(WorldAge, TimeOfday);
break;
case PacketIncomingType.EntityTeleport:
if (handler.GetEntityHandlingEnabled())
{
int EntityID = dataTypes.ReadNextVarInt(packetData);
Double X = dataTypes.ReadNextDouble(packetData);
Double Y = dataTypes.ReadNextDouble(packetData);
Double Z = dataTypes.ReadNextDouble(packetData);
byte EntityYaw = dataTypes.ReadNextByte(packetData);
byte EntityPitch = dataTypes.ReadNextByte(packetData);
bool OnGround = dataTypes.ReadNextBool(packetData);
handler.OnEntityTeleport(EntityID, X, Y, Z, OnGround);
}
break;
case PacketIncomingType.UpdateHealth:
float health = dataTypes.ReadNextFloat(packetData);
int food;
if (protocolversion >= MC18Version)
food = dataTypes.ReadNextVarInt(packetData);
else
food = dataTypes.ReadNextShort(packetData);
dataTypes.ReadNextFloat(packetData); // Food Saturation
handler.OnUpdateHealth(health, food);
break;
case PacketIncomingType.SetExperience:
float experiencebar = dataTypes.ReadNextFloat(packetData);
int level = dataTypes.ReadNextVarInt(packetData);
int totalexperience = dataTypes.ReadNextVarInt(packetData);
handler.OnSetExperience(experiencebar, level, totalexperience);
break;
case PacketIncomingType.Explosion:
Location explosionLocation = new Location(dataTypes.ReadNextFloat(packetData), dataTypes.ReadNextFloat(packetData), dataTypes.ReadNextFloat(packetData));
float explosionStrength = dataTypes.ReadNextFloat(packetData);
int explosionBlockCount = dataTypes.ReadNextInt(packetData);
// Ignoring additional fields (records, pushback)
handler.OnExplosion(explosionLocation, explosionStrength, explosionBlockCount);
break;
case PacketIncomingType.HeldItemChange:
byte slot = dataTypes.ReadNextByte(packetData);
handler.OnHeldItemChange(slot);
break;
case PacketIncomingType.ScoreboardObjective:
string objectivename = dataTypes.ReadNextString(packetData);
byte mode = dataTypes.ReadNextByte(packetData);
string objectivevalue = String.Empty;
int type2 = -1;
if (mode == 0 || mode == 2)
{
objectivevalue = dataTypes.ReadNextString(packetData);
type2 = dataTypes.ReadNextVarInt(packetData);
}
handler.OnScoreboardObjective(objectivename, mode, objectivevalue, type2);
break;
case PacketIncomingType.UpdateScore:
string entityname = dataTypes.ReadNextString(packetData);
byte action3 = dataTypes.ReadNextByte(packetData);
string objectivename2 = null;
int value = -1;
if (action3 != 1 || protocolversion >= MC18Version)
objectivename2 = dataTypes.ReadNextString(packetData);
if (action3 != 1)
value = dataTypes.ReadNextVarInt(packetData);
handler.OnUpdateScore(entityname, action3, objectivename2, value);
break;
default:
return false; //Ignored packet
}
return true; //Packet processed
}
catch (Exception innerException)
{
if (innerException is ThreadAbortException || innerException is SocketException || innerException.InnerException is SocketException)
throw; //Thread abort or Connection lost rather than invalid data
throw new System.IO.InvalidDataException(
String.Format("Failed to process incoming packet of type {0}. (PacketID: {1}, Protocol: {2}, LoginPhase: {3}, InnerException: {4}).",
Protocol18PacketTypes.GetPacketIncomingType(packetID, protocolversion),
packetID,
protocolversion,
login_phase,
innerException.GetType()),
innerException);
}
}
/// <summary>
/// Start the updating thread. Should be called after login success.
/// </summary>
private void StartUpdating()
{
netRead = new Thread(new ThreadStart(Updater));
netRead.Name = "ProtocolPacketHandler";
netRead.Start();
}
/// <summary>
/// Disconnect from the server, cancel network reading.
/// </summary>
public void Dispose()
{
try
{
if (netRead != null)
{
netRead.Abort();
socketWrapper.Disconnect();
}
}
catch { }
}
/// <summary>
/// Send a packet to the server. Packet ID, compression, and encryption will be handled automatically.
/// </summary>
/// <param name="packet">packet type</param>
/// <param name="packetData">packet Data</param>
private void SendPacket(PacketOutgoingType packet, IEnumerable<byte> packetData)
{
SendPacket(Protocol18PacketTypes.GetPacketOutgoingID(packet, protocolversion), packetData);
}
/// <summary>
/// Send a packet to the server. Compression and encryption will be handled automatically.
/// </summary>
/// <param name="packetID">packet ID</param>
/// <param name="packetData">packet Data</param>
private void SendPacket(int packetID, IEnumerable<byte> packetData)
{
//The inner packet
byte[] the_packet = dataTypes.ConcatBytes(dataTypes.GetVarInt(packetID), packetData.ToArray());
if (compression_treshold > 0) //Compression enabled?
{
if (the_packet.Length >= compression_treshold) //Packet long enough for compressing?
{
byte[] compressed_packet = ZlibUtils.Compress(the_packet);
the_packet = dataTypes.ConcatBytes(dataTypes.GetVarInt(the_packet.Length), compressed_packet);
}
else
{
byte[] uncompressed_length = dataTypes.GetVarInt(0); //Not compressed (short packet)
the_packet = dataTypes.ConcatBytes(uncompressed_length, the_packet);
}
}
socketWrapper.SendDataRAW(dataTypes.ConcatBytes(dataTypes.GetVarInt(the_packet.Length), the_packet));
}
/// <summary>
/// Do the Minecraft login.
/// </summary>
/// <returns>True if login successful</returns>
public bool Login()
{
byte[] protocol_version = dataTypes.GetVarInt(protocolversion);
string server_address = pForge.GetServerAddress(handler.GetServerHost());
byte[] server_port = dataTypes.GetUShort((ushort)handler.GetServerPort());
byte[] next_state = dataTypes.GetVarInt(2);
byte[] handshake_packet = dataTypes.ConcatBytes(protocol_version, dataTypes.GetString(server_address), server_port, next_state);
SendPacket(0x00, handshake_packet);
byte[] login_packet = dataTypes.GetString(handler.GetUsername());
SendPacket(0x00, login_packet);
int packetID = -1;
Queue<byte> packetData = new Queue<byte>();
while (true)
{
ReadNextPacket(ref packetID, packetData);
if (packetID == 0x00) //Login rejected
{
handler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, ChatParser.ParseText(dataTypes.ReadNextString(packetData)));
return false;
}
else if (packetID == 0x01) //Encryption request
{
string serverID = dataTypes.ReadNextString(packetData);
byte[] Serverkey = dataTypes.ReadNextByteArray(packetData);
byte[] token = dataTypes.ReadNextByteArray(packetData);
return StartEncryption(handler.GetUserUUID(), handler.GetSessionID(), token, serverID, Serverkey);
}
else if (packetID == 0x02) //Login successful
{
ConsoleIO.WriteLineFormatted("§8Server is in offline mode.");
login_phase = false;
if (!pForge.CompleteForgeHandshake())
{
ConsoleIO.WriteLineFormatted("§8Forge Login Handshake did not complete successfully");
return false;
}
StartUpdating();
return true; //No need to check session or start encryption
}
else HandlePacket(packetID, packetData);
}
}
/// <summary>
/// Start network encryption. Automatically called by Login() if the server requests encryption.
/// </summary>
/// <returns>True if encryption was successful</returns>
private bool StartEncryption(string uuid, string sessionID, byte[] token, string serverIDhash, byte[] serverKey)
{
System.Security.Cryptography.RSACryptoServiceProvider RSAService = CryptoHandler.DecodeRSAPublicKey(serverKey);
byte[] secretKey = CryptoHandler.GenerateAESPrivateKey();
if (Settings.DebugMessages)
ConsoleIO.WriteLineFormatted("§8Crypto keys & hash generated.");
if (serverIDhash != "-")
{
Console.WriteLine("Checking Session...");
if (!ProtocolHandler.SessionCheck(uuid, sessionID, CryptoHandler.getServerHash(serverIDhash, serverKey, secretKey)))
{
handler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, "Failed to check session.");
return false;
}
}
//Encrypt the data
byte[] key_enc = dataTypes.GetArray(RSAService.Encrypt(secretKey, false));
byte[] token_enc = dataTypes.GetArray(RSAService.Encrypt(token, false));
//Encryption Response packet
SendPacket(0x01, dataTypes.ConcatBytes(key_enc, token_enc));
//Start client-side encryption
socketWrapper.SwitchToEncrypted(secretKey);
//Process the next packet
int packetID = -1;
Queue<byte> packetData = new Queue<byte>();
while (true)
{
ReadNextPacket(ref packetID, packetData);
if (packetID == 0x00) //Login rejected
{
handler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, ChatParser.ParseText(dataTypes.ReadNextString(packetData)));
return false;
}
else if (packetID == 0x02) //Login successful
{
login_phase = false;
if (!pForge.CompleteForgeHandshake())
{
ConsoleIO.WriteLineFormatted("§8Forge StartEncryption Handshake did not complete successfully");
return false;
}
StartUpdating();
return true;
}
else HandlePacket(packetID, packetData);
}
}
/// <summary>
/// Disconnect from the server
/// </summary>
public void Disconnect()
{
socketWrapper.Disconnect();
}
/// <summary>
/// Autocomplete text while typing username or command
/// </summary>
/// <param name="BehindCursor">Text behind cursor</param>
/// <returns>Completed text</returns>
IEnumerable<string> IAutoComplete.AutoComplete(string BehindCursor)
{
if (String.IsNullOrEmpty(BehindCursor))
return new string[] { };
byte[] transaction_id = dataTypes.GetVarInt(autocomplete_transaction_id);
byte[] assume_command = new byte[] { 0x00 };
byte[] has_position = new byte[] { 0x00 };
byte[] tabcomplete_packet = new byte[] { };
if (protocolversion >= MC18Version)
{
if (protocolversion >= MC113Version)
{
tabcomplete_packet = dataTypes.ConcatBytes(tabcomplete_packet, transaction_id);
tabcomplete_packet = dataTypes.ConcatBytes(tabcomplete_packet, dataTypes.GetString(BehindCursor));
}
else
{
tabcomplete_packet = dataTypes.ConcatBytes(tabcomplete_packet, dataTypes.GetString(BehindCursor));
if (protocolversion >= MC19Version)
{
tabcomplete_packet = dataTypes.ConcatBytes(tabcomplete_packet, assume_command);
}
tabcomplete_packet = dataTypes.ConcatBytes(tabcomplete_packet, has_position);
}
}
else
{
tabcomplete_packet = dataTypes.ConcatBytes(dataTypes.GetString(BehindCursor));
}
autocomplete_received = false;
autocomplete_result.Clear();
autocomplete_result.Add(BehindCursor);
SendPacket(PacketOutgoingType.TabComplete, tabcomplete_packet);
int wait_left = 50; //do not wait more than 5 seconds (50 * 100 ms)
Thread t1 = new Thread(new ThreadStart(delegate
{
while (wait_left > 0 && !autocomplete_received) { System.Threading.Thread.Sleep(100); wait_left--; }
if (autocomplete_result.Count > 0)
ConsoleIO.WriteLineFormatted("§8" + String.Join(" ", autocomplete_result), false);
}));
t1.Start();
return autocomplete_result;
}
/// <summary>
/// Ping a Minecraft server to get information about the server
/// </summary>
/// <returns>True if ping was successful</returns>
public static bool doPing(string host, int port, ref int protocolversion, ref ForgeInfo forgeInfo)
{
string version = "";
TcpClient tcp = ProxyHandler.newTcpClient(host, port);
tcp.ReceiveBufferSize = 1024 * 1024;
SocketWrapper socketWrapper = new SocketWrapper(tcp);
DataTypes dataTypes = new DataTypes(MC18Version);
byte[] packet_id = dataTypes.GetVarInt(0);
byte[] protocol_version = dataTypes.GetVarInt(-1);
byte[] server_port = BitConverter.GetBytes((ushort)port); Array.Reverse(server_port);
byte[] next_state = dataTypes.GetVarInt(1);
byte[] packet = dataTypes.ConcatBytes(packet_id, protocol_version, dataTypes.GetString(host), server_port, next_state);
byte[] tosend = dataTypes.ConcatBytes(dataTypes.GetVarInt(packet.Length), packet);
socketWrapper.SendDataRAW(tosend);
byte[] status_request = dataTypes.GetVarInt(0);
byte[] request_packet = dataTypes.ConcatBytes(dataTypes.GetVarInt(status_request.Length), status_request);
socketWrapper.SendDataRAW(request_packet);
int packetLength = dataTypes.ReadNextVarIntRAW(socketWrapper);
if (packetLength > 0) //Read Response length
{
Queue<byte> packetData = new Queue<byte>(socketWrapper.ReadDataRAW(packetLength));
if (dataTypes.ReadNextVarInt(packetData) == 0x00) //Read Packet ID
{
string result = dataTypes.ReadNextString(packetData); //Get the Json data
if (Settings.DebugMessages)
{
// May contain formatting codes, cannot use WriteLineFormatted
Console.ForegroundColor = ConsoleColor.DarkGray;
ConsoleIO.WriteLine(result);
Console.ForegroundColor = ConsoleColor.Gray;
}
if (!String.IsNullOrEmpty(result) && result.StartsWith("{") && result.EndsWith("}"))
{
Json.JSONData jsonData = Json.ParseJson(result);
if (jsonData.Type == Json.JSONData.DataType.Object && jsonData.Properties.ContainsKey("version"))
{
Json.JSONData versionData = jsonData.Properties["version"];
//Retrieve display name of the Minecraft version
if (versionData.Properties.ContainsKey("name"))
version = versionData.Properties["name"].StringValue;
//Retrieve protocol version number for handling this server
if (versionData.Properties.ContainsKey("protocol"))
protocolversion = dataTypes.Atoi(versionData.Properties["protocol"].StringValue);
// Check for forge on the server.
Protocol18Forge.ServerInfoCheckForge(jsonData, ref forgeInfo);
ConsoleIO.WriteLineFormatted("§8Server version : " + version + " (protocol v" + protocolversion + (forgeInfo != null ? ", with Forge)." : ")."));
return true;
}
}
}
}
return false;
}
/// <summary>
/// Get max length for chat messages
/// </summary>
/// <returns>Max length, in characters</returns>
public int GetMaxChatMessageLength()
{
return protocolversion > MC110Version
? 256
: 100;
}
/// <summary>
/// Send a chat message to the server
/// </summary>
/// <param name="message">Message</param>
/// <returns>True if properly sent</returns>
public bool SendChatMessage(string message)
{
if (String.IsNullOrEmpty(message))
return true;
try
{
byte[] message_packet = dataTypes.GetString(message);
SendPacket(PacketOutgoingType.ChatMessage, message_packet);
return true;
}
catch (SocketException) { return false; }
catch (System.IO.IOException) { return false; }
catch (ObjectDisposedException) { return false; }
}
public bool SendEntityAction(int PlayerEntityID, int ActionID)
{
try
{
List<byte> fields = new List<byte>();
fields.AddRange(dataTypes.GetVarInt(PlayerEntityID));
fields.AddRange(dataTypes.GetVarInt(ActionID));
fields.AddRange(dataTypes.GetVarInt(0));
SendPacket(PacketOutgoingType.EntityAction, fields);
return true;
}
catch (SocketException) { return false; }
catch (System.IO.IOException) { return false; }
catch (ObjectDisposedException) { return false; }
}
/// <summary>
/// Send a respawn packet to the server
/// </summary>
/// <returns>True if properly sent</returns>
public bool SendRespawnPacket()
{
try
{
SendPacket(PacketOutgoingType.ClientStatus, new byte[] { 0 });
return true;
}
catch (SocketException) { return false; }
catch (System.IO.IOException) { return false; }
catch (ObjectDisposedException) { return false; }
}
/// <summary>
/// Tell the server what client is being used to connect to the server
/// </summary>
/// <param name="brandInfo">Client string describing the client</param>
/// <returns>True if brand info was successfully sent</returns>
public bool SendBrandInfo(string brandInfo)
{
if (String.IsNullOrEmpty(brandInfo))
return false;
// Plugin channels were significantly changed between Minecraft 1.12 and 1.13
// https://wiki.vg/index.php?title=Pre-release_protocol&oldid=14132#Plugin_Channels
if (protocolversion >= MC113Version)
{
return SendPluginChannelPacket("minecraft:brand", dataTypes.GetString(brandInfo));
}
else
{
return SendPluginChannelPacket("MC|Brand", dataTypes.GetString(brandInfo));
}
}
/// <summary>
/// Inform the server of the client's Minecraft settings
/// </summary>
/// <param name="language">Client language eg en_US</param>
/// <param name="viewDistance">View distance, in chunks</param>
/// <param name="difficulty">Game difficulty (client-side...)</param>
/// <param name="chatMode">Chat mode (allows muting yourself)</param>
/// <param name="chatColors">Show chat colors</param>
/// <param name="skinParts">Show skin layers</param>
/// <param name="mainHand">1.9+ main hand</param>
/// <returns>True if client settings were successfully sent</returns>
public bool SendClientSettings(string language, byte viewDistance, byte difficulty, byte chatMode, bool chatColors, byte skinParts, byte mainHand)
{
try
{
List<byte> fields = new List<byte>();
fields.AddRange(dataTypes.GetString(language));
fields.Add(viewDistance);
fields.AddRange(protocolversion >= MC19Version
? dataTypes.GetVarInt(chatMode)
: new byte[] { chatMode });
fields.Add(chatColors ? (byte)1 : (byte)0);
if (protocolversion < MC18Version)
{
fields.Add(difficulty);
fields.Add((byte)(skinParts & 0x1)); //show cape
}
else fields.Add(skinParts);
if (protocolversion >= MC19Version)
fields.AddRange(dataTypes.GetVarInt(mainHand));
SendPacket(PacketOutgoingType.ClientSettings, fields);
}
catch (SocketException) { }
catch (System.IO.IOException) { return false; }
catch (ObjectDisposedException) { return false; }
return false;
}
/// <summary>
/// Send a location update to the server
/// </summary>
/// <param name="location">The new location of the player</param>
/// <param name="onGround">True if the player is on the ground</param>
/// <param name="yaw">Optional new yaw for updating player look</param>
/// <param name="pitch">Optional new pitch for updating player look</param>
/// <returns>True if the location update was successfully sent</returns>
public bool SendLocationUpdate(Location location, bool onGround, float? yaw = null, float? pitch = null)
{
if (handler.GetTerrainEnabled())
{
byte[] yawpitch = new byte[0];
PacketOutgoingType packetType = PacketOutgoingType.PlayerPosition;
if (yaw.HasValue && pitch.HasValue)
{
yawpitch = dataTypes.ConcatBytes(dataTypes.GetFloat(yaw.Value), dataTypes.GetFloat(pitch.Value));
packetType = PacketOutgoingType.PlayerPositionAndLook;
}
try
{
SendPacket(packetType, dataTypes.ConcatBytes(
dataTypes.GetDouble(location.X),
dataTypes.GetDouble(location.Y),
protocolversion < MC18Version
? dataTypes.GetDouble(location.Y + 1.62)
: new byte[0],
dataTypes.GetDouble(location.Z),
yawpitch,
new byte[] { onGround ? (byte)1 : (byte)0 }));
return true;
}
catch (SocketException) { return false; }
catch (System.IO.IOException) { return false; }
catch (ObjectDisposedException) { return false; }
}
else return false;
}
/// <summary>
/// Send a plugin channel packet (0x17) to the server, compression and encryption will be handled automatically
/// </summary>
/// <param name="channel">Channel to send packet on</param>
/// <param name="data">packet Data</param>
public bool SendPluginChannelPacket(string channel, byte[] data)
{
try
{
// In 1.7, length needs to be included.
// In 1.8, it must not be.
if (protocolversion < MC18Version)
{
byte[] length = BitConverter.GetBytes((short)data.Length);
Array.Reverse(length);
SendPacket(PacketOutgoingType.PluginMessage, dataTypes.ConcatBytes(dataTypes.GetString(channel), length, data));
}
else
{
SendPacket(PacketOutgoingType.PluginMessage, dataTypes.ConcatBytes(dataTypes.GetString(channel), data));
}
return true;
}
catch (SocketException) { return false; }
catch (System.IO.IOException) { return false; }
catch (ObjectDisposedException) { return false; }
}
/// <summary>
/// Send a Login Plugin Response packet (0x02)
/// </summary>
/// <param name="messageId">Login Plugin Request message Id </param>
/// <param name="understood">TRUE if the request was understood</param>
/// <param name="data">Response to the request</param>
/// <returns>TRUE if successfully sent</returns>
public bool SendLoginPluginResponse(int messageId, bool understood, byte[] data)
{
try
{
SendPacket(0x02, dataTypes.ConcatBytes(dataTypes.GetVarInt(messageId), dataTypes.GetBool(understood), data));
return true;
}
catch (SocketException) { return false; }
catch (System.IO.IOException) { return false; }
catch (ObjectDisposedException) { return false; }
}
/// <summary>
/// Send an Interact Entity Packet to server
/// </summary>
/// <param name="EntityID"></param>
/// <param name="type"></param>
/// <returns></returns>
public bool SendInteractEntity(int EntityID, int type)
{
try
{
List<byte> fields = new List<byte>();
fields.AddRange(dataTypes.GetVarInt(EntityID));
fields.AddRange(dataTypes.GetVarInt(type));
// Is player Sneaking (Only 1.16 and above)
// Currently hardcoded to false
// TODO: Update to reflect the real player state
if (protocolversion >= MC116Version)
fields.AddRange(dataTypes.GetBool(false));
SendPacket(PacketOutgoingType.InteractEntity, fields);
return true;
}
catch (SocketException) { return false; }
catch (System.IO.IOException) { return false; }
catch (ObjectDisposedException) { return false; }
}
// TODO: Interact at block location (e.g. chest minecart)
public bool SendInteractEntity(int EntityID, int type, float X, float Y, float Z, int hand)
{
try
{
List<byte> fields = new List<byte>();
fields.AddRange(dataTypes.GetVarInt(EntityID));
fields.AddRange(dataTypes.GetVarInt(type));
fields.AddRange(dataTypes.GetFloat(X));
fields.AddRange(dataTypes.GetFloat(Y));
fields.AddRange(dataTypes.GetFloat(Z));
fields.AddRange(dataTypes.GetVarInt(hand));
SendPacket(PacketOutgoingType.InteractEntity, fields);
return true;
}
catch (SocketException) { return false; }
catch (System.IO.IOException) { return false; }
catch (ObjectDisposedException) { return false; }
}
public bool SendInteractEntity(int EntityID, int type, int hand)
{
try
{
List<byte> fields = new List<byte>();
fields.AddRange(dataTypes.GetVarInt(EntityID));
fields.AddRange(dataTypes.GetVarInt(type));
fields.AddRange(dataTypes.GetVarInt(hand));
SendPacket(PacketOutgoingType.InteractEntity, fields);
return true;
}
catch (SocketException) { return false; }
catch (System.IO.IOException) { return false; }
catch (ObjectDisposedException) { return false; }
}
public bool SendInteractEntity(int EntityID, int type, float X, float Y, float Z)
{
return false;
}
public bool SendUseItem(int hand)
{
if (protocolversion < MC19Version)
return false; // Packet does not exist prior to MC 1.9
// According to https://wiki.vg/index.php?title=Protocol&oldid=5486#Player_Block_Placement
// MC 1.7 does this using Player Block Placement with special values
// TODO once Player Block Placement is implemented for older versions
try
{
List<byte> packet = new List<byte>();
packet.AddRange(dataTypes.GetVarInt(hand));
SendPacket(PacketOutgoingType.UseItem, packet);
return true;
}
catch (SocketException) { return false; }
catch (System.IO.IOException) { return false; }
catch (ObjectDisposedException) { return false; }
}
public bool SendPlayerDigging(int status, Location location, Direction face)
{
try
{
List<byte> packet = new List<byte>();
packet.AddRange(dataTypes.GetVarInt(status));
packet.AddRange(dataTypes.GetLocation(location));
packet.AddRange(dataTypes.GetVarInt(dataTypes.GetBlockFace(face)));
SendPacket(PacketOutgoingType.PlayerDigging, packet);
return true;
}
catch (SocketException) { return false; }
catch (System.IO.IOException) { return false; }
catch (ObjectDisposedException) { return false; }
}
public bool SendPlayerBlockPlacement(int hand, Location location, Direction face)
{
if (protocolversion < MC114Version)
return false; // NOT IMPLEMENTED for older MC versions
try
{
List<byte> packet = new List<byte>();
packet.AddRange(dataTypes.GetVarInt(hand));
packet.AddRange(dataTypes.GetLocation(location));
packet.AddRange(dataTypes.GetVarInt(dataTypes.GetBlockFace(face)));
packet.AddRange(dataTypes.GetFloat(0.5f)); // cursorX
packet.AddRange(dataTypes.GetFloat(0.5f)); // cursorY
packet.AddRange(dataTypes.GetFloat(0.5f)); // cursorZ
packet.Add(0); // insideBlock = false;
SendPacket(PacketOutgoingType.PlayerBlockPlacement, packet);
return true;
}
catch (SocketException) { return false; }
catch (System.IO.IOException) { return false; }
catch (ObjectDisposedException) { return false; }
}
public bool SendHeldItemChange(short slot)
{
try
{
List<byte> packet = new List<byte>();
packet.AddRange(dataTypes.GetShort(slot));
SendPacket(PacketOutgoingType.HeldItemChange, packet);
return true;
}
catch (SocketException) { return false; }
catch (System.IO.IOException) { return false; }
catch (ObjectDisposedException) { return false; }
}
public bool SendWindowAction(int windowId, int slotId, WindowActionType action, Item item)
{
try
{
short actionNumber;
lock (window_actions)
{
if (!window_actions.ContainsKey(windowId))
window_actions[windowId] = 0;
actionNumber = (short)(window_actions[windowId] + 1);
window_actions[windowId] = actionNumber;
}
byte button = 0;
byte mode = 0;
switch (action)
{
case WindowActionType.LeftClick: button = 0; break;
case WindowActionType.RightClick: button = 1; break;
case WindowActionType.MiddleClick: button = 2; mode = 3; break;
case WindowActionType.ShiftClick: button = 0; mode = 1; item = new Item(ItemType.Null, 0, null); break;
case WindowActionType.DropItem: button = 0; mode = 4; item = new Item(ItemType.Null, 0, null); break;
case WindowActionType.DropItemStack: button = 1; mode = 4; item = new Item(ItemType.Null, 0, null); break;
case WindowActionType.StartDragLeft: button = 0; mode = 5; item = new Item(ItemType.Null, 0, null); slotId = -999; break;
case WindowActionType.StartDragRight: button = 4; mode = 5; item = new Item(ItemType.Null, 0, null); slotId = -999; break;
case WindowActionType.StartDragMiddle: button = 8; mode = 5; item = new Item(ItemType.Null, 0, null); slotId = -999; break;
case WindowActionType.EndDragLeft: button = 2; mode = 5; item = new Item(ItemType.Null, 0, null); slotId = -999; break;
case WindowActionType.EndDragRight: button = 6; mode = 5; item = new Item(ItemType.Null, 0, null); slotId = -999; break;
case WindowActionType.EndDragMiddle: button = 10; mode = 5; item = new Item(ItemType.Null, 0, null); slotId = -999; break;
case WindowActionType.AddDragLeft: button = 1; mode = 5; item = new Item(ItemType.Null, 0, null); break;
case WindowActionType.AddDragRight: button = 5; mode = 5; item = new Item(ItemType.Null, 0, null); break;
case WindowActionType.AddDragMiddle: button = 9; mode = 5; item = new Item(ItemType.Null, 0, null); break;
}
List<byte> packet = new List<byte>();
packet.Add((byte)windowId);
packet.AddRange(dataTypes.GetShort((short)slotId));
packet.Add(button);
packet.AddRange(dataTypes.GetShort(actionNumber));
if (protocolversion >= MC19Version)
packet.AddRange(dataTypes.GetVarInt(mode));
else packet.Add(mode);
packet.AddRange(dataTypes.GetItemSlot(item, itemPalette));
SendPacket(PacketOutgoingType.ClickWindow, packet);
return true;
}
catch (SocketException) { return false; }
catch (System.IO.IOException) { return false; }
catch (ObjectDisposedException) { return false; }
}
public bool SendCreativeInventoryAction(int slot, ItemType itemType, int count, Dictionary<string, object> nbt)
{
try
{
List<byte> packet = new List<byte>();
packet.AddRange(dataTypes.GetShort((short)slot));
packet.AddRange(dataTypes.GetItemSlot(new Item(itemType, count, nbt), itemPalette));
SendPacket(PacketOutgoingType.CreativeInventoryAction, packet);
return true;
}
catch (SocketException) { return false; }
catch (System.IO.IOException) { return false; }
catch (ObjectDisposedException) { return false; }
}
public bool SendAnimation(int animation, int playerid)
{
try
{
if (animation == 0 || animation == 1)
{
List<byte> packet = new List<byte>();
if (protocolversion < MC18Version)
{
packet.AddRange(dataTypes.GetInt(playerid));
packet.Add((byte)1); // Swing arm
}
else if (protocolversion < MC19Version)
{
// No fields in 1.8.X
}
else // MC 1.9+
{
packet.AddRange(dataTypes.GetVarInt(animation));
}
SendPacket(PacketOutgoingType.Animation, packet);
return true;
}
else
{
return false;
}
}
catch (SocketException) { return false; }
catch (System.IO.IOException) { return false; }
catch (ObjectDisposedException) { return false; }
}
public bool SendCloseWindow(int windowId)
{
try
{
lock (window_actions)
{
if (window_actions.ContainsKey(windowId))
window_actions[windowId] = 0;
}
SendPacket(PacketOutgoingType.CloseWindow, new[] { (byte)windowId });
return true;
}
catch (SocketException) { return false; }
catch (System.IO.IOException) { return false; }
catch (ObjectDisposedException) { return false; }
}
public bool SendUpdateSign(Location sign, string line1, string line2, string line3, string line4)
{
try
{
if (line1.Length > 23)
line1 = line1.Substring(0, 23);
if (line2.Length > 23)
line2 = line1.Substring(0, 23);
if (line3.Length > 23)
line3 = line1.Substring(0, 23);
if (line4.Length > 23)
line4 = line1.Substring(0, 23);
List<byte> packet = new List<byte>();
packet.AddRange(dataTypes.GetLocation(sign));
packet.AddRange(dataTypes.GetString(line1));
packet.AddRange(dataTypes.GetString(line2));
packet.AddRange(dataTypes.GetString(line3));
packet.AddRange(dataTypes.GetString(line4));
SendPacket(PacketOutgoingType.UpdateSign, packet);
return true;
}
catch (SocketException) { return false; }
catch (System.IO.IOException) { return false; }
catch (ObjectDisposedException) { return false; }
}
public bool UpdateCommandBlock(Location location, string command, CommandBlockMode mode, CommandBlockFlags flags)
{
if (protocolversion <= MC113Version)
{
try
{
List<byte> packet = new List<byte>();
packet.AddRange(dataTypes.GetLocation(location));
packet.AddRange(dataTypes.GetString(command));
packet.AddRange(dataTypes.GetVarInt((int)mode));
packet.Add((byte)flags);
SendPacket(PacketOutgoingType.UpdateSign, packet);
return true;
}
catch (SocketException) { return false; }
catch (System.IO.IOException) { return false; }
catch (ObjectDisposedException) { return false; }
}
else { return false; }
}
public bool SendWindowConfirmation(byte windowID, short actionID, bool accepted)
{
try
{
List<byte> packet = new List<byte>();
packet.Add(windowID);
packet.AddRange(dataTypes.GetShort(actionID));
packet.Add(accepted ? (byte)1 : (byte)0);
SendPacket(PacketOutgoingType.WindowConfirmation, packet);
return true;
}
catch (SocketException) { return false; }
catch (System.IO.IOException) { return false; }
catch (ObjectDisposedException) { return false; }
}
}
}