mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2025-10-14 21:22:49 +00:00
Split Protocol18 protocol into several classes
- Protocol18: Handles Vanilla packets - Protocol18Forge: Handles Forge packets - DataTypes: Handles packet field encoding/decoding - SocketWrapper: Handles encryption and basic R/W operations This commit should not change anything for end users, only devs. See also #704 for discussions about this.
This commit is contained in:
parent
295d6546b8
commit
d2cbc9f1c3
8 changed files with 1317 additions and 1162 deletions
|
|
@ -102,6 +102,12 @@
|
|||
<Compile Include="Mapping\BlockPalettes\PaletteGenerator.cs" />
|
||||
<Compile Include="Mapping\BlockPalettes\PaletteMapping.cs" />
|
||||
<Compile Include="Mapping\MaterialExtensions.cs" />
|
||||
<Compile Include="Protocol\Handlers\DataTypes.cs" />
|
||||
<Compile Include="Protocol\Handlers\PacketIncomingType.cs" />
|
||||
<Compile Include="Protocol\Handlers\PacketOutgoingType.cs" />
|
||||
<Compile Include="Protocol\Handlers\Protocol18Forge.cs" />
|
||||
<Compile Include="Protocol\Handlers\Protocol18PacketTypes.cs" />
|
||||
<Compile Include="Protocol\Handlers\SocketWrapper.cs" />
|
||||
<Compile Include="Protocol\Session\SessionFileMonitor.cs" />
|
||||
<Compile Include="WinAPI\ConsoleIcon.cs" />
|
||||
<Compile Include="ConsoleIO.cs" />
|
||||
|
|
|
|||
414
MinecraftClient/Protocol/Handlers/DataTypes.cs
Normal file
414
MinecraftClient/Protocol/Handlers/DataTypes.cs
Normal file
|
|
@ -0,0 +1,414 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Net.Sockets;
|
||||
using MinecraftClient.Mapping;
|
||||
using MinecraftClient.Crypto;
|
||||
|
||||
namespace MinecraftClient.Protocol.Handlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Handle data types encoding / decoding
|
||||
/// </summary>
|
||||
class DataTypes
|
||||
{
|
||||
/// <summary>
|
||||
/// Protocol version for adjusting data types
|
||||
/// </summary>
|
||||
private int protocolversion;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize a new DataTypes instance
|
||||
/// </summary>
|
||||
/// <param name="protocol">Protocol version</param>
|
||||
public DataTypes(int protocol)
|
||||
{
|
||||
this.protocolversion = protocol;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read some data from a cache of bytes and remove it from the cache
|
||||
/// </summary>
|
||||
/// <param name="offset">Amount of bytes to read</param>
|
||||
/// <param name="cache">Cache of bytes to read from</param>
|
||||
/// <returns>The data read from the cache as an array</returns>
|
||||
public byte[] ReadData(int offset, List<byte> cache)
|
||||
{
|
||||
byte[] result = cache.Take(offset).ToArray();
|
||||
cache.RemoveRange(0, offset);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a string from a cache of bytes and remove it from the cache
|
||||
/// </summary>
|
||||
/// <param name="cache">Cache of bytes to read from</param>
|
||||
/// <returns>The string</returns>
|
||||
public string ReadNextString(List<byte> cache)
|
||||
{
|
||||
int length = ReadNextVarInt(cache);
|
||||
if (length > 0)
|
||||
{
|
||||
return Encoding.UTF8.GetString(ReadData(length, cache));
|
||||
}
|
||||
else return "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a boolean from a cache of bytes and remove it from the cache
|
||||
/// </summary>
|
||||
/// <returns>The boolean value</returns>
|
||||
public bool ReadNextBool(List<byte> cache)
|
||||
{
|
||||
return ReadNextByte(cache) != 0x00;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a short integer from a cache of bytes and remove it from the cache
|
||||
/// </summary>
|
||||
/// <returns>The short integer value</returns>
|
||||
public short ReadNextShort(List<byte> cache)
|
||||
{
|
||||
byte[] rawValue = ReadData(2, cache);
|
||||
Array.Reverse(rawValue); //Endianness
|
||||
return BitConverter.ToInt16(rawValue, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an integer from a cache of bytes and remove it from the cache
|
||||
/// </summary>
|
||||
/// <returns>The integer value</returns>
|
||||
public int ReadNextInt(List<byte> cache)
|
||||
{
|
||||
byte[] rawValue = ReadData(4, cache);
|
||||
Array.Reverse(rawValue); //Endianness
|
||||
return BitConverter.ToInt32(rawValue, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an unsigned short integer from a cache of bytes and remove it from the cache
|
||||
/// </summary>
|
||||
/// <returns>The unsigned short integer value</returns>
|
||||
public ushort ReadNextUShort(List<byte> cache)
|
||||
{
|
||||
byte[] rawValue = ReadData(2, cache);
|
||||
Array.Reverse(rawValue); //Endianness
|
||||
return BitConverter.ToUInt16(rawValue, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an unsigned long integer from a cache of bytes and remove it from the cache
|
||||
/// </summary>
|
||||
/// <returns>The unsigned long integer value</returns>
|
||||
public ulong ReadNextULong(List<byte> cache)
|
||||
{
|
||||
byte[] rawValue = ReadData(8, cache);
|
||||
Array.Reverse(rawValue); //Endianness
|
||||
return BitConverter.ToUInt64(rawValue, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a Location encoded as an ulong field and remove it from the cache
|
||||
/// </summary>
|
||||
/// <returns>The Location value</returns>
|
||||
public Location ReadNextLocation(List<byte> cache)
|
||||
{
|
||||
ulong locEncoded = ReadNextULong(cache);
|
||||
int x, y, z;
|
||||
if (protocolversion >= Protocol18Handler.MC114Version)
|
||||
{
|
||||
x = (int)(locEncoded >> 38);
|
||||
y = (int)(locEncoded & 0xFFF);
|
||||
z = (int)(locEncoded << 26 >> 38);
|
||||
}
|
||||
else
|
||||
{
|
||||
x = (int)(locEncoded >> 38);
|
||||
y = (int)((locEncoded >> 26) & 0xFFF);
|
||||
z = (int)(locEncoded << 38 >> 38);
|
||||
}
|
||||
if (x >= 33554432)
|
||||
x -= 67108864;
|
||||
if (y >= 2048)
|
||||
y -= 4096;
|
||||
if (z >= 33554432)
|
||||
z -= 67108864;
|
||||
return new Location(x, y, z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read several little endian unsigned short integers at once from a cache of bytes and remove them from the cache
|
||||
/// </summary>
|
||||
/// <returns>The unsigned short integer value</returns>
|
||||
public ushort[] ReadNextUShortsLittleEndian(int amount, List<byte> cache)
|
||||
{
|
||||
byte[] rawValues = ReadData(2 * amount, cache);
|
||||
ushort[] result = new ushort[amount];
|
||||
for (int i = 0; i < amount; i++)
|
||||
result[i] = BitConverter.ToUInt16(rawValues, i * 2);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a uuid from a cache of bytes and remove it from the cache
|
||||
/// </summary>
|
||||
/// <param name="cache">Cache of bytes to read from</param>
|
||||
/// <returns>The uuid</returns>
|
||||
public Guid ReadNextUUID(List<byte> cache)
|
||||
{
|
||||
byte[] javaUUID = ReadData(16, cache);
|
||||
Guid guid;
|
||||
if (BitConverter.IsLittleEndian)
|
||||
{
|
||||
// Convert big-endian Java UUID to little-endian .NET GUID
|
||||
byte[] netGUID = new byte[16];
|
||||
for (int i = 8; i < 16; i++)
|
||||
netGUID[i] = javaUUID[i];
|
||||
netGUID[3] = javaUUID[0];
|
||||
netGUID[2] = javaUUID[1];
|
||||
netGUID[1] = javaUUID[2];
|
||||
netGUID[0] = javaUUID[3];
|
||||
netGUID[5] = javaUUID[4];
|
||||
netGUID[4] = javaUUID[5];
|
||||
netGUID[6] = javaUUID[7];
|
||||
netGUID[7] = javaUUID[6];
|
||||
guid = new Guid(netGUID);
|
||||
}
|
||||
else
|
||||
{
|
||||
guid = new Guid(javaUUID);
|
||||
}
|
||||
return guid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a byte array from a cache of bytes and remove it from the cache
|
||||
/// </summary>
|
||||
/// <param name="cache">Cache of bytes to read from</param>
|
||||
/// <returns>The byte array</returns>
|
||||
public byte[] ReadNextByteArray(List<byte> cache)
|
||||
{
|
||||
int len = protocolversion >= Protocol18Handler.MC18Version
|
||||
? ReadNextVarInt(cache)
|
||||
: ReadNextShort(cache);
|
||||
return ReadData(len, cache);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a length-prefixed array of unsigned long integers and removes it from the cache
|
||||
/// </summary>
|
||||
/// <returns>The unsigned long integer values</returns>
|
||||
public ulong[] ReadNextULongArray(List<byte> cache)
|
||||
{
|
||||
int len = ReadNextVarInt(cache);
|
||||
ulong[] result = new ulong[len];
|
||||
for (int i = 0; i < len; i++)
|
||||
result[i] = ReadNextULong(cache);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a double from a cache of bytes and remove it from the cache
|
||||
/// </summary>
|
||||
/// <returns>The double value</returns>
|
||||
public double ReadNextDouble(List<byte> cache)
|
||||
{
|
||||
byte[] rawValue = ReadData(8, cache);
|
||||
Array.Reverse(rawValue); //Endianness
|
||||
return BitConverter.ToDouble(rawValue, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a float from a cache of bytes and remove it from the cache
|
||||
/// </summary>
|
||||
/// <returns>The float value</returns>
|
||||
public float ReadNextFloat(List<byte> cache)
|
||||
{
|
||||
byte[] rawValue = ReadData(4, cache);
|
||||
Array.Reverse(rawValue); //Endianness
|
||||
return BitConverter.ToSingle(rawValue, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an integer from the network
|
||||
/// </summary>
|
||||
/// <returns>The integer</returns>
|
||||
public int ReadNextVarIntRAW(SocketWrapper socket)
|
||||
{
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
int k = 0;
|
||||
while (true)
|
||||
{
|
||||
k = socket.ReadDataRAW(1)[0];
|
||||
i |= (k & 0x7F) << j++ * 7;
|
||||
if (j > 5) throw new OverflowException("VarInt too big");
|
||||
if ((k & 0x80) != 128) break;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an integer from a cache of bytes and remove it from the cache
|
||||
/// </summary>
|
||||
/// <param name="cache">Cache of bytes to read from</param>
|
||||
/// <returns>The integer</returns>
|
||||
public int ReadNextVarInt(List<byte> cache)
|
||||
{
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
int k = 0;
|
||||
while (true)
|
||||
{
|
||||
k = ReadNextByte(cache);
|
||||
i |= (k & 0x7F) << j++ * 7;
|
||||
if (j > 5) throw new OverflowException("VarInt too big");
|
||||
if ((k & 0x80) != 128) break;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an "extended short", which is actually an int of some kind, from the cache of bytes.
|
||||
/// This is only done with forge. It looks like it's a normal short, except that if the high
|
||||
/// bit is set, it has an extra byte.
|
||||
/// </summary>
|
||||
/// <param name="cache">Cache of bytes to read from</param>
|
||||
/// <returns>The int</returns>
|
||||
public int ReadNextVarShort(List<byte> cache)
|
||||
{
|
||||
ushort low = ReadNextUShort(cache);
|
||||
byte high = 0;
|
||||
if ((low & 0x8000) != 0)
|
||||
{
|
||||
low &= 0x7FFF;
|
||||
high = ReadNextByte(cache);
|
||||
}
|
||||
return ((high & 0xFF) << 15) | low;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a single byte from a cache of bytes and remove it from the cache
|
||||
/// </summary>
|
||||
/// <returns>The byte that was read</returns>
|
||||
public byte ReadNextByte(List<byte> cache)
|
||||
{
|
||||
byte result = cache[0];
|
||||
cache.RemoveAt(0);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build an integer for sending over the network
|
||||
/// </summary>
|
||||
/// <param name="paramInt">Integer to encode</param>
|
||||
/// <returns>Byte array for this integer</returns>
|
||||
public byte[] GetVarInt(int paramInt)
|
||||
{
|
||||
List<byte> bytes = new List<byte>();
|
||||
while ((paramInt & -128) != 0)
|
||||
{
|
||||
bytes.Add((byte)(paramInt & 127 | 128));
|
||||
paramInt = (int)(((uint)paramInt) >> 7);
|
||||
}
|
||||
bytes.Add((byte)paramInt);
|
||||
return bytes.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get byte array representing a double
|
||||
/// </summary>
|
||||
/// <param name="number">Double to process</param>
|
||||
/// <returns>Array ready to send</returns>
|
||||
public byte[] GetDouble(double number)
|
||||
{
|
||||
byte[] theDouble = BitConverter.GetBytes(number);
|
||||
Array.Reverse(theDouble); //Endianness
|
||||
return theDouble;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get byte array representing a float
|
||||
/// </summary>
|
||||
/// <param name="number">Floalt to process</param>
|
||||
/// <returns>Array ready to send</returns>
|
||||
public byte[] GetFloat(float number)
|
||||
{
|
||||
byte[] theFloat = BitConverter.GetBytes(number);
|
||||
Array.Reverse(theFloat); //Endianness
|
||||
return theFloat;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get byte array with length information prepended to it
|
||||
/// </summary>
|
||||
/// <param name="array">Array to process</param>
|
||||
/// <returns>Array ready to send</returns>
|
||||
public byte[] GetArray(byte[] array)
|
||||
{
|
||||
if (protocolversion < Protocol18Handler.MC18Version)
|
||||
{
|
||||
byte[] length = BitConverter.GetBytes((short)array.Length);
|
||||
Array.Reverse(length);
|
||||
return ConcatBytes(length, array);
|
||||
}
|
||||
else return ConcatBytes(GetVarInt(array.Length), array);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a byte array from the given string for sending over the network, with length information prepended.
|
||||
/// </summary>
|
||||
/// <param name="text">String to process</param>
|
||||
/// <returns>Array ready to send</returns>
|
||||
public byte[] GetString(string text)
|
||||
{
|
||||
byte[] bytes = Encoding.UTF8.GetBytes(text);
|
||||
|
||||
return ConcatBytes(GetVarInt(bytes.Length), bytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a byte array representing the given location encoded as an unsigned short
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A modulo will be applied if the location is outside the following ranges:
|
||||
/// X: -33,554,432 to +33,554,431
|
||||
/// Y: -2,048 to +2,047
|
||||
/// Z: -33,554,432 to +33,554,431
|
||||
/// </remarks>
|
||||
/// <returns>Location representation as ulong</returns>
|
||||
public byte[] GetLocation(Location location)
|
||||
{
|
||||
if (protocolversion >= Protocol18Handler.MC114Version)
|
||||
{
|
||||
return BitConverter.GetBytes(((((ulong)location.X) & 0x3FFFFFF) << 38) | ((((ulong)location.Z) & 0x3FFFFFF) << 12) | (((ulong)location.Y) & 0xFFF));
|
||||
}
|
||||
else return BitConverter.GetBytes(((((ulong)location.X) & 0x3FFFFFF) << 38) | ((((ulong)location.Y) & 0xFFF) << 26) | (((ulong)location.Z) & 0x3FFFFFF));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Easily append several byte arrays
|
||||
/// </summary>
|
||||
/// <param name="bytes">Bytes to append</param>
|
||||
/// <returns>Array containing all the data</returns>
|
||||
public byte[] ConcatBytes(params byte[][] bytes)
|
||||
{
|
||||
List<byte> result = new List<byte>();
|
||||
foreach (byte[] array in bytes)
|
||||
result.AddRange(array);
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// C-like atoi function for parsing an int from string
|
||||
/// </summary>
|
||||
/// <param name="str">String to parse</param>
|
||||
/// <returns>Int parsed</returns>
|
||||
public int Atoi(string str)
|
||||
{
|
||||
return int.Parse(new string(str.Trim().TakeWhile(char.IsDigit).ToArray()));
|
||||
}
|
||||
}
|
||||
}
|
||||
31
MinecraftClient/Protocol/Handlers/PacketIncomingType.cs
Normal file
31
MinecraftClient/Protocol/Handlers/PacketIncomingType.cs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MinecraftClient.Protocol.Handlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Abstract incoming packet numbering
|
||||
/// </summary>
|
||||
enum PacketIncomingType
|
||||
{
|
||||
KeepAlive,
|
||||
JoinGame,
|
||||
ChatMessage,
|
||||
Respawn,
|
||||
PlayerPositionAndLook,
|
||||
ChunkData,
|
||||
MultiBlockChange,
|
||||
BlockChange,
|
||||
MapChunkBulk,
|
||||
UnloadChunk,
|
||||
PlayerListUpdate,
|
||||
TabCompleteResult,
|
||||
PluginMessage,
|
||||
KickPacket,
|
||||
NetworkCompressionTreshold,
|
||||
ResourcePackSend,
|
||||
UnknownPacket
|
||||
}
|
||||
}
|
||||
24
MinecraftClient/Protocol/Handlers/PacketOutgoingType.cs
Normal file
24
MinecraftClient/Protocol/Handlers/PacketOutgoingType.cs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MinecraftClient.Protocol.Handlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Abstract outgoing packet numbering
|
||||
/// </summary>
|
||||
enum PacketOutgoingType
|
||||
{
|
||||
KeepAlive,
|
||||
ResourcePackStatus,
|
||||
ChatMessage,
|
||||
ClientStatus,
|
||||
ClientSettings,
|
||||
PluginMessage,
|
||||
TabComplete,
|
||||
PlayerPosition,
|
||||
PlayerPositionAndLook,
|
||||
TeleportConfirm
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
246
MinecraftClient/Protocol/Handlers/Protocol18Forge.cs
Normal file
246
MinecraftClient/Protocol/Handlers/Protocol18Forge.cs
Normal file
|
|
@ -0,0 +1,246 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using MinecraftClient.Protocol.Handlers.Forge;
|
||||
using System.Threading;
|
||||
|
||||
namespace MinecraftClient.Protocol.Handlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Handler for the Minecraft Forge protocol
|
||||
/// </summary>
|
||||
class Protocol18Forge
|
||||
{
|
||||
private int protocolversion;
|
||||
private DataTypes dataTypes;
|
||||
private Protocol18Handler protocol18;
|
||||
private IMinecraftComHandler mcHandler;
|
||||
|
||||
private ForgeInfo forgeInfo;
|
||||
private FMLHandshakeClientState fmlHandshakeState = FMLHandshakeClientState.START;
|
||||
private bool ForgeEnabled() { return forgeInfo != null; }
|
||||
|
||||
/// <summary>
|
||||
/// Initialize a new Forge protocol handler
|
||||
/// </summary>
|
||||
/// <param name="forgeInfo">Forge Server Information</param>
|
||||
/// <param name="protocolversion">Minecraft protocol version</param>
|
||||
/// <param name="datatypes">Minecraft data types handler</param>
|
||||
public Protocol18Forge(ForgeInfo forgeInfo, int protocolVersion, DataTypes dataTypes, Protocol18Handler protocol18, IMinecraftComHandler mcHandler)
|
||||
{
|
||||
this.forgeInfo = forgeInfo;
|
||||
this.protocolversion = protocolVersion;
|
||||
this.dataTypes = dataTypes;
|
||||
this.protocol18 = protocol18;
|
||||
this.mcHandler = mcHandler;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get Forge-Tagged server address
|
||||
/// </summary>
|
||||
/// <param name="serverAddress">Server Address</param>
|
||||
/// <returns>Forge-Tagged server address</returns>
|
||||
public string GetServerAddress(string serverAddress)
|
||||
{
|
||||
if (ForgeEnabled())
|
||||
return serverAddress + "\0FML\0";
|
||||
return serverAddress;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Completes the Minecraft Forge handshake.
|
||||
/// </summary>
|
||||
/// <returns>Whether the handshake was successful.</returns>
|
||||
public bool CompleteForgeHandshake()
|
||||
{
|
||||
if (ForgeEnabled())
|
||||
{
|
||||
int packetID = -1;
|
||||
List<byte> packetData = new List<byte>();
|
||||
|
||||
while (fmlHandshakeState != FMLHandshakeClientState.DONE)
|
||||
{
|
||||
protocol18.ReadNextPacket(ref packetID, packetData);
|
||||
|
||||
if (packetID == 0x40) // Disconnect
|
||||
{
|
||||
mcHandler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, ChatParser.ParseText(dataTypes.ReadNextString(packetData)));
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Send back regular packet to the vanilla protocol handler
|
||||
protocol18.HandlePacket(packetID, packetData);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read Forge VarShort field
|
||||
/// </summary>
|
||||
/// <param name="packetData">Packet data to read from</param>
|
||||
/// <returns>Length from packet data</returns>
|
||||
public int ReadNextVarShort(List<byte> packetData)
|
||||
{
|
||||
if (ForgeEnabled())
|
||||
{
|
||||
// Forge special VarShort field.
|
||||
return (int)dataTypes.ReadNextVarShort(packetData);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Vanilla regular Short field
|
||||
return (int)dataTypes.ReadNextShort(packetData);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle Forge plugin messages
|
||||
/// </summary>
|
||||
/// <param name="channel">Plugin message channel</param>
|
||||
/// <param name="packetData">Plugin message data</param>
|
||||
/// <param name="currentdimension">Current world dimension</param>
|
||||
/// <returns>TRUE if the plugin message was recognized and handled</returns>
|
||||
public bool HandlePluginMessage(string channel, List<byte> packetData, ref int currentDimension)
|
||||
{
|
||||
if (ForgeEnabled() && fmlHandshakeState != FMLHandshakeClientState.DONE)
|
||||
{
|
||||
if (channel == "FML|HS")
|
||||
{
|
||||
FMLHandshakeDiscriminator discriminator = (FMLHandshakeDiscriminator)dataTypes.ReadNextByte(packetData);
|
||||
|
||||
if (discriminator == FMLHandshakeDiscriminator.HandshakeReset)
|
||||
{
|
||||
fmlHandshakeState = FMLHandshakeClientState.START;
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (fmlHandshakeState)
|
||||
{
|
||||
case FMLHandshakeClientState.START:
|
||||
if (discriminator != FMLHandshakeDiscriminator.ServerHello)
|
||||
return false;
|
||||
|
||||
// Send the plugin channel registration.
|
||||
// REGISTER is somewhat special in that it doesn't actually include length information,
|
||||
// and is also \0-separated.
|
||||
// Also, yes, "FML" is there twice. Don't ask me why, but that's the way forge does it.
|
||||
string[] channels = { "FML|HS", "FML", "FML|MP", "FML", "FORGE" };
|
||||
protocol18.SendPluginChannelPacket("REGISTER", Encoding.UTF8.GetBytes(string.Join("\0", channels)));
|
||||
|
||||
byte fmlProtocolVersion = dataTypes.ReadNextByte(packetData);
|
||||
|
||||
if (Settings.DebugMessages)
|
||||
ConsoleIO.WriteLineFormatted("§8Forge protocol version : " + fmlProtocolVersion);
|
||||
|
||||
if (fmlProtocolVersion >= 1)
|
||||
currentDimension = dataTypes.ReadNextInt(packetData);
|
||||
|
||||
// Tell the server we're running the same version.
|
||||
SendForgeHandshakePacket(FMLHandshakeDiscriminator.ClientHello, new byte[] { fmlProtocolVersion });
|
||||
|
||||
// Then tell the server that we're running the same mods.
|
||||
if (Settings.DebugMessages)
|
||||
ConsoleIO.WriteLineFormatted("§8Sending falsified mod list to server...");
|
||||
byte[][] mods = new byte[forgeInfo.Mods.Count][];
|
||||
for (int i = 0; i < forgeInfo.Mods.Count; i++)
|
||||
{
|
||||
ForgeInfo.ForgeMod mod = forgeInfo.Mods[i];
|
||||
mods[i] = dataTypes.ConcatBytes(dataTypes.GetString(mod.ModID), dataTypes.GetString(mod.Version));
|
||||
}
|
||||
SendForgeHandshakePacket(FMLHandshakeDiscriminator.ModList,
|
||||
dataTypes.ConcatBytes(dataTypes.GetVarInt(forgeInfo.Mods.Count), dataTypes.ConcatBytes(mods)));
|
||||
|
||||
fmlHandshakeState = FMLHandshakeClientState.WAITINGSERVERDATA;
|
||||
|
||||
return true;
|
||||
case FMLHandshakeClientState.WAITINGSERVERDATA:
|
||||
if (discriminator != FMLHandshakeDiscriminator.ModList)
|
||||
return false;
|
||||
|
||||
Thread.Sleep(2000);
|
||||
|
||||
if (Settings.DebugMessages)
|
||||
ConsoleIO.WriteLineFormatted("§8Accepting server mod list...");
|
||||
// Tell the server that yes, we are OK with the mods it has
|
||||
// even though we don't actually care what mods it has.
|
||||
|
||||
SendForgeHandshakePacket(FMLHandshakeDiscriminator.HandshakeAck,
|
||||
new byte[] { (byte)FMLHandshakeClientState.WAITINGSERVERDATA });
|
||||
|
||||
fmlHandshakeState = FMLHandshakeClientState.WAITINGSERVERCOMPLETE;
|
||||
return false;
|
||||
case FMLHandshakeClientState.WAITINGSERVERCOMPLETE:
|
||||
// The server now will tell us a bunch of registry information.
|
||||
// We need to read it all, though, until it says that there is no more.
|
||||
if (discriminator != FMLHandshakeDiscriminator.RegistryData)
|
||||
return false;
|
||||
|
||||
if (protocolversion < Protocol18Handler.MC18Version)
|
||||
{
|
||||
// 1.7.10 and below have one registry
|
||||
// with blocks and items.
|
||||
int registrySize = dataTypes.ReadNextVarInt(packetData);
|
||||
|
||||
if (Settings.DebugMessages)
|
||||
ConsoleIO.WriteLineFormatted("§8Received registry with " + registrySize + " entries");
|
||||
|
||||
fmlHandshakeState = FMLHandshakeClientState.PENDINGCOMPLETE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 1.8+ has more than one registry.
|
||||
|
||||
bool hasNextRegistry = dataTypes.ReadNextBool(packetData);
|
||||
string registryName = dataTypes.ReadNextString(packetData);
|
||||
int registrySize = dataTypes.ReadNextVarInt(packetData);
|
||||
if (Settings.DebugMessages)
|
||||
ConsoleIO.WriteLineFormatted("§8Received registry " + registryName + " with " + registrySize + " entries");
|
||||
if (!hasNextRegistry)
|
||||
fmlHandshakeState = FMLHandshakeClientState.PENDINGCOMPLETE;
|
||||
}
|
||||
|
||||
return false;
|
||||
case FMLHandshakeClientState.PENDINGCOMPLETE:
|
||||
// The server will ask us to accept the registries.
|
||||
// Just say yes.
|
||||
if (discriminator != FMLHandshakeDiscriminator.HandshakeAck)
|
||||
return false;
|
||||
if (Settings.DebugMessages)
|
||||
ConsoleIO.WriteLineFormatted("§8Accepting server registries...");
|
||||
SendForgeHandshakePacket(FMLHandshakeDiscriminator.HandshakeAck,
|
||||
new byte[] { (byte)FMLHandshakeClientState.PENDINGCOMPLETE });
|
||||
fmlHandshakeState = FMLHandshakeClientState.COMPLETE;
|
||||
|
||||
return true;
|
||||
case FMLHandshakeClientState.COMPLETE:
|
||||
// One final "OK". On the actual forge source, a packet is sent from
|
||||
// the client to the client saying that the connection was complete, but
|
||||
// we don't need to do that.
|
||||
|
||||
SendForgeHandshakePacket(FMLHandshakeDiscriminator.HandshakeAck,
|
||||
new byte[] { (byte)FMLHandshakeClientState.COMPLETE });
|
||||
if (Settings.DebugMessages)
|
||||
ConsoleIO.WriteLine("Forge server connection complete!");
|
||||
fmlHandshakeState = FMLHandshakeClientState.DONE;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a forge plugin channel packet ("FML|HS"). Compression and encryption will be handled automatically
|
||||
/// </summary>
|
||||
/// <param name="discriminator">Discriminator to use.</param>
|
||||
/// <param name="data">packet Data</param>
|
||||
private void SendForgeHandshakePacket(FMLHandshakeDiscriminator discriminator, byte[] data)
|
||||
{
|
||||
protocol18.SendPluginChannelPacket("FML|HS", dataTypes.ConcatBytes(new byte[] { (byte)discriminator }, data));
|
||||
}
|
||||
}
|
||||
}
|
||||
261
MinecraftClient/Protocol/Handlers/Protocol18PacketTypes.cs
Normal file
261
MinecraftClient/Protocol/Handlers/Protocol18PacketTypes.cs
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MinecraftClient.Protocol.Handlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains packet ID mappings for Protocol18
|
||||
/// </summary>
|
||||
class Protocol18PacketTypes
|
||||
{
|
||||
/// <summary>
|
||||
/// Get abstract numbering of the specified packet ID
|
||||
/// </summary>
|
||||
/// <param name="packetID">Packet ID</param>
|
||||
/// <param name="protocol">Protocol version</param>
|
||||
/// <returns>Abstract numbering</returns>
|
||||
public static PacketIncomingType GetPacketIncomingType(int packetID, int protocol)
|
||||
{
|
||||
if (protocol <= Protocol18Handler.MC18Version) // MC 1.7 and 1.8
|
||||
{
|
||||
switch (packetID)
|
||||
{
|
||||
case 0x00: return PacketIncomingType.KeepAlive;
|
||||
case 0x01: return PacketIncomingType.JoinGame;
|
||||
case 0x02: return PacketIncomingType.ChatMessage;
|
||||
case 0x07: return PacketIncomingType.Respawn;
|
||||
case 0x08: return PacketIncomingType.PlayerPositionAndLook;
|
||||
case 0x21: return PacketIncomingType.ChunkData;
|
||||
case 0x22: return PacketIncomingType.MultiBlockChange;
|
||||
case 0x23: return PacketIncomingType.BlockChange;
|
||||
case 0x26: return PacketIncomingType.MapChunkBulk;
|
||||
//UnloadChunk does not exists prior to 1.9
|
||||
case 0x38: return PacketIncomingType.PlayerListUpdate;
|
||||
case 0x3A: return PacketIncomingType.TabCompleteResult;
|
||||
case 0x3F: return PacketIncomingType.PluginMessage;
|
||||
case 0x40: return PacketIncomingType.KickPacket;
|
||||
case 0x46: return PacketIncomingType.NetworkCompressionTreshold;
|
||||
case 0x48: return PacketIncomingType.ResourcePackSend;
|
||||
default: return PacketIncomingType.UnknownPacket;
|
||||
}
|
||||
}
|
||||
else if (protocol <= Protocol18Handler.MC1112Version) // MC 1.9, 1.10 and 1.11
|
||||
{
|
||||
switch (packetID)
|
||||
{
|
||||
case 0x1F: return PacketIncomingType.KeepAlive;
|
||||
case 0x23: return PacketIncomingType.JoinGame;
|
||||
case 0x0F: return PacketIncomingType.ChatMessage;
|
||||
case 0x33: return PacketIncomingType.Respawn;
|
||||
case 0x2E: return PacketIncomingType.PlayerPositionAndLook;
|
||||
case 0x20: return PacketIncomingType.ChunkData;
|
||||
case 0x10: return PacketIncomingType.MultiBlockChange;
|
||||
case 0x0B: return PacketIncomingType.BlockChange;
|
||||
//MapChunkBulk removed in 1.9
|
||||
case 0x1D: return PacketIncomingType.UnloadChunk;
|
||||
case 0x2D: return PacketIncomingType.PlayerListUpdate;
|
||||
case 0x0E: return PacketIncomingType.TabCompleteResult;
|
||||
case 0x18: return PacketIncomingType.PluginMessage;
|
||||
case 0x1A: return PacketIncomingType.KickPacket;
|
||||
//NetworkCompressionTreshold removed in 1.9
|
||||
case 0x32: return PacketIncomingType.ResourcePackSend;
|
||||
default: return PacketIncomingType.UnknownPacket;
|
||||
}
|
||||
}
|
||||
else if (protocol <= Protocol18Handler.MC112Version) // MC 1.12.0
|
||||
{
|
||||
switch (packetID)
|
||||
{
|
||||
case 0x1F: return PacketIncomingType.KeepAlive;
|
||||
case 0x23: return PacketIncomingType.JoinGame;
|
||||
case 0x0F: return PacketIncomingType.ChatMessage;
|
||||
case 0x34: return PacketIncomingType.Respawn;
|
||||
case 0x2E: return PacketIncomingType.PlayerPositionAndLook;
|
||||
case 0x20: return PacketIncomingType.ChunkData;
|
||||
case 0x10: return PacketIncomingType.MultiBlockChange;
|
||||
case 0x0B: return PacketIncomingType.BlockChange;
|
||||
case 0x1D: return PacketIncomingType.UnloadChunk;
|
||||
case 0x2D: return PacketIncomingType.PlayerListUpdate;
|
||||
case 0x0E: return PacketIncomingType.TabCompleteResult;
|
||||
case 0x18: return PacketIncomingType.PluginMessage;
|
||||
case 0x1A: return PacketIncomingType.KickPacket;
|
||||
case 0x33: return PacketIncomingType.ResourcePackSend;
|
||||
default: return PacketIncomingType.UnknownPacket;
|
||||
}
|
||||
}
|
||||
else if (protocol <= Protocol18Handler.MC1122Version) // MC 1.12.2
|
||||
{
|
||||
switch (packetID)
|
||||
{
|
||||
case 0x1F: return PacketIncomingType.KeepAlive;
|
||||
case 0x23: return PacketIncomingType.JoinGame;
|
||||
case 0x0F: return PacketIncomingType.ChatMessage;
|
||||
case 0x35: return PacketIncomingType.Respawn;
|
||||
case 0x2F: return PacketIncomingType.PlayerPositionAndLook;
|
||||
case 0x20: return PacketIncomingType.ChunkData;
|
||||
case 0x10: return PacketIncomingType.MultiBlockChange;
|
||||
case 0x0B: return PacketIncomingType.BlockChange;
|
||||
case 0x1D: return PacketIncomingType.UnloadChunk;
|
||||
case 0x2E: return PacketIncomingType.PlayerListUpdate;
|
||||
case 0x0E: return PacketIncomingType.TabCompleteResult;
|
||||
case 0x18: return PacketIncomingType.PluginMessage;
|
||||
case 0x1A: return PacketIncomingType.KickPacket;
|
||||
case 0x34: return PacketIncomingType.ResourcePackSend;
|
||||
default: return PacketIncomingType.UnknownPacket;
|
||||
}
|
||||
}
|
||||
else if (protocol < Protocol18Handler.MC114Version) // MC 1.13 to 1.13.2
|
||||
{
|
||||
switch (packetID)
|
||||
{
|
||||
case 0x21: return PacketIncomingType.KeepAlive;
|
||||
case 0x25: return PacketIncomingType.JoinGame;
|
||||
case 0x0E: return PacketIncomingType.ChatMessage;
|
||||
case 0x38: return PacketIncomingType.Respawn;
|
||||
case 0x32: return PacketIncomingType.PlayerPositionAndLook;
|
||||
case 0x22: return PacketIncomingType.ChunkData;
|
||||
case 0x0F: return PacketIncomingType.MultiBlockChange;
|
||||
case 0x0B: return PacketIncomingType.BlockChange;
|
||||
case 0x1F: return PacketIncomingType.UnloadChunk;
|
||||
case 0x30: return PacketIncomingType.PlayerListUpdate;
|
||||
case 0x10: return PacketIncomingType.TabCompleteResult;
|
||||
case 0x19: return PacketIncomingType.PluginMessage;
|
||||
case 0x1B: return PacketIncomingType.KickPacket;
|
||||
case 0x37: return PacketIncomingType.ResourcePackSend;
|
||||
default: return PacketIncomingType.UnknownPacket;
|
||||
}
|
||||
}
|
||||
else // MC 1.14
|
||||
{
|
||||
switch (packetID)
|
||||
{
|
||||
case 0x20: return PacketIncomingType.KeepAlive;
|
||||
case 0x25: return PacketIncomingType.JoinGame;
|
||||
case 0x0E: return PacketIncomingType.ChatMessage;
|
||||
case 0x3A: return PacketIncomingType.Respawn;
|
||||
case 0x35: return PacketIncomingType.PlayerPositionAndLook;
|
||||
case 0x21: return PacketIncomingType.ChunkData;
|
||||
case 0x0F: return PacketIncomingType.MultiBlockChange;
|
||||
case 0x0B: return PacketIncomingType.BlockChange;
|
||||
case 0x1D: return PacketIncomingType.UnloadChunk;
|
||||
case 0x33: return PacketIncomingType.PlayerListUpdate;
|
||||
case 0x10: return PacketIncomingType.TabCompleteResult;
|
||||
case 0x18: return PacketIncomingType.PluginMessage;
|
||||
case 0x1A: return PacketIncomingType.KickPacket;
|
||||
case 0x39: return PacketIncomingType.ResourcePackSend;
|
||||
default: return PacketIncomingType.UnknownPacket;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get packet ID of the specified outgoing packet
|
||||
/// </summary>
|
||||
/// <param name="packet">Abstract packet numbering</param>
|
||||
/// <param name="protocol">Protocol version</param>
|
||||
/// <returns>Packet ID</returns>
|
||||
public static int GetPacketOutgoingID(PacketOutgoingType packet, int protocol)
|
||||
{
|
||||
if (protocol <= Protocol18Handler.MC18Version) // MC 1.7 and 1.8
|
||||
{
|
||||
switch (packet)
|
||||
{
|
||||
case PacketOutgoingType.KeepAlive: return 0x00;
|
||||
case PacketOutgoingType.ResourcePackStatus: return 0x19;
|
||||
case PacketOutgoingType.ChatMessage: return 0x01;
|
||||
case PacketOutgoingType.ClientStatus: return 0x16;
|
||||
case PacketOutgoingType.ClientSettings: return 0x15;
|
||||
case PacketOutgoingType.PluginMessage: return 0x17;
|
||||
case PacketOutgoingType.TabComplete: return 0x14;
|
||||
case PacketOutgoingType.PlayerPosition: return 0x04;
|
||||
case PacketOutgoingType.PlayerPositionAndLook: return 0x06;
|
||||
case PacketOutgoingType.TeleportConfirm: throw new InvalidOperationException("Teleport confirm is not supported in protocol " + protocol);
|
||||
}
|
||||
}
|
||||
else if (protocol <= Protocol18Handler.MC1112Version) // MC 1.9, 1,10 and 1.11
|
||||
{
|
||||
switch (packet)
|
||||
{
|
||||
case PacketOutgoingType.KeepAlive: return 0x0B;
|
||||
case PacketOutgoingType.ResourcePackStatus: return 0x16;
|
||||
case PacketOutgoingType.ChatMessage: return 0x02;
|
||||
case PacketOutgoingType.ClientStatus: return 0x03;
|
||||
case PacketOutgoingType.ClientSettings: return 0x04;
|
||||
case PacketOutgoingType.PluginMessage: return 0x09;
|
||||
case PacketOutgoingType.TabComplete: return 0x01;
|
||||
case PacketOutgoingType.PlayerPosition: return 0x0C;
|
||||
case PacketOutgoingType.PlayerPositionAndLook: return 0x0D;
|
||||
case PacketOutgoingType.TeleportConfirm: return 0x00;
|
||||
}
|
||||
}
|
||||
else if (protocol <= Protocol18Handler.MC112Version) // MC 1.12
|
||||
{
|
||||
switch (packet)
|
||||
{
|
||||
case PacketOutgoingType.KeepAlive: return 0x0C;
|
||||
case PacketOutgoingType.ResourcePackStatus: return 0x18;
|
||||
case PacketOutgoingType.ChatMessage: return 0x03;
|
||||
case PacketOutgoingType.ClientStatus: return 0x04;
|
||||
case PacketOutgoingType.ClientSettings: return 0x05;
|
||||
case PacketOutgoingType.PluginMessage: return 0x0A;
|
||||
case PacketOutgoingType.TabComplete: return 0x02;
|
||||
case PacketOutgoingType.PlayerPosition: return 0x0E;
|
||||
case PacketOutgoingType.PlayerPositionAndLook: return 0x0F;
|
||||
case PacketOutgoingType.TeleportConfirm: return 0x00;
|
||||
}
|
||||
}
|
||||
else if (protocol <= Protocol18Handler.MC1122Version) // 1.12.2
|
||||
{
|
||||
switch (packet)
|
||||
{
|
||||
case PacketOutgoingType.KeepAlive: return 0x0B;
|
||||
case PacketOutgoingType.ResourcePackStatus: return 0x18;
|
||||
case PacketOutgoingType.ChatMessage: return 0x02;
|
||||
case PacketOutgoingType.ClientStatus: return 0x03;
|
||||
case PacketOutgoingType.ClientSettings: return 0x04;
|
||||
case PacketOutgoingType.PluginMessage: return 0x09;
|
||||
case PacketOutgoingType.TabComplete: return 0x01;
|
||||
case PacketOutgoingType.PlayerPosition: return 0x0D;
|
||||
case PacketOutgoingType.PlayerPositionAndLook: return 0x0E;
|
||||
case PacketOutgoingType.TeleportConfirm: return 0x00;
|
||||
}
|
||||
}
|
||||
else if (protocol < Protocol18Handler.MC114Version) // MC 1.13 to 1.13.2
|
||||
{
|
||||
switch (packet)
|
||||
{
|
||||
case PacketOutgoingType.KeepAlive: return 0x0E;
|
||||
case PacketOutgoingType.ResourcePackStatus: return 0x1D;
|
||||
case PacketOutgoingType.ChatMessage: return 0x02;
|
||||
case PacketOutgoingType.ClientStatus: return 0x03;
|
||||
case PacketOutgoingType.ClientSettings: return 0x04;
|
||||
case PacketOutgoingType.PluginMessage: return 0x0A;
|
||||
case PacketOutgoingType.TabComplete: return 0x05;
|
||||
case PacketOutgoingType.PlayerPosition: return 0x10;
|
||||
case PacketOutgoingType.PlayerPositionAndLook: return 0x11;
|
||||
case PacketOutgoingType.TeleportConfirm: return 0x00;
|
||||
}
|
||||
}
|
||||
else // MC 1.14
|
||||
{
|
||||
switch (packet)
|
||||
{
|
||||
case PacketOutgoingType.KeepAlive: return 0x0F;
|
||||
case PacketOutgoingType.ResourcePackStatus: return 0x1F;
|
||||
case PacketOutgoingType.ChatMessage: return 0x03;
|
||||
case PacketOutgoingType.ClientStatus: return 0x04;
|
||||
case PacketOutgoingType.ClientSettings: return 0x05;
|
||||
case PacketOutgoingType.PluginMessage: return 0x0B;
|
||||
case PacketOutgoingType.TabComplete: return 0x06;
|
||||
case PacketOutgoingType.PlayerPosition: return 0x11;
|
||||
case PacketOutgoingType.PlayerPositionAndLook: return 0x12;
|
||||
case PacketOutgoingType.TeleportConfirm: return 0x00;
|
||||
}
|
||||
}
|
||||
|
||||
throw new System.ComponentModel.InvalidEnumArgumentException("Unknown PacketOutgoingType (protocol=" + protocol + ")", (int)packet, typeof(PacketOutgoingType));
|
||||
}
|
||||
}
|
||||
}
|
||||
116
MinecraftClient/Protocol/Handlers/SocketWrapper.cs
Normal file
116
MinecraftClient/Protocol/Handlers/SocketWrapper.cs
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
using System;
|
||||
using System.Net.Sockets;
|
||||
using MinecraftClient.Crypto;
|
||||
|
||||
namespace MinecraftClient.Protocol.Handlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Wrapper for handling unencrypted & encrypted socket
|
||||
/// </summary>
|
||||
class SocketWrapper
|
||||
{
|
||||
TcpClient c;
|
||||
IAesStream s;
|
||||
bool encrypted = false;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize a new SocketWrapper
|
||||
/// </summary>
|
||||
/// <param name="client">TcpClient connected to the server</param>
|
||||
public SocketWrapper(TcpClient client)
|
||||
{
|
||||
this.c = client;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the socket is still connected
|
||||
/// </summary>
|
||||
/// <returns>TRUE if still connected</returns>
|
||||
/// <remarks>Silently dropped connection can only be detected by attempting to read/write data</remarks>
|
||||
public bool IsConnected()
|
||||
{
|
||||
return c.Client != null && c.Connected;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the socket has data available to read
|
||||
/// </summary>
|
||||
/// <returns>TRUE if data is available to read</returns>
|
||||
public bool HasDataAvailable()
|
||||
{
|
||||
return c.Client.Available > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Switch network reading/writing to an encrypted stream
|
||||
/// </summary>
|
||||
/// <param name="secretKey">AES secret key</param>
|
||||
public void SwitchToEncrypted(byte[] secretKey)
|
||||
{
|
||||
if (encrypted)
|
||||
throw new InvalidOperationException("Stream is already encrypted!?");
|
||||
this.s = CryptoHandler.getAesStream(c.GetStream(), secretKey);
|
||||
this.encrypted = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Network reading method. Read bytes from the socket or encrypted socket.
|
||||
/// </summary>
|
||||
private void Receive(byte[] buffer, int start, int offset, SocketFlags f)
|
||||
{
|
||||
int read = 0;
|
||||
while (read < offset)
|
||||
{
|
||||
if (encrypted)
|
||||
{
|
||||
read += s.Read(buffer, start + read, offset - read);
|
||||
}
|
||||
else read += c.Client.Receive(buffer, start + read, offset - read, f);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read some data from the server.
|
||||
/// </summary>
|
||||
/// <param name="length">Amount of bytes to read</param>
|
||||
/// <returns>The data read from the network as an array</returns>
|
||||
public byte[] ReadDataRAW(int length)
|
||||
{
|
||||
if (length > 0)
|
||||
{
|
||||
byte[] cache = new byte[length];
|
||||
Receive(cache, 0, length, SocketFlags.None);
|
||||
return cache;
|
||||
}
|
||||
return new byte[] { };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send raw data to the server.
|
||||
/// </summary>
|
||||
/// <param name="buffer">data to send</param>
|
||||
public void SendDataRAW(byte[] buffer)
|
||||
{
|
||||
if (encrypted)
|
||||
{
|
||||
s.Write(buffer, 0, buffer.Length);
|
||||
}
|
||||
else c.Client.Send(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disconnect from the server
|
||||
/// </summary>
|
||||
public void Disconnect()
|
||||
{
|
||||
try
|
||||
{
|
||||
c.Close();
|
||||
}
|
||||
catch (SocketException) { }
|
||||
catch (System.IO.IOException) { }
|
||||
catch (NullReferenceException) { }
|
||||
catch (ObjectDisposedException) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue