mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2025-10-14 21:22:49 +00:00
Add world handling (and fall to ground)
- World is now properly parsed and stored from chunk data - Block changes are also handled and world updated accordingly - Added ground checking, the player will move down to reach the ground - Performance tweaking in Protocol18, using lists instead of arrays - Fix player look not properly skipped causing invalid location after teleport
This commit is contained in:
parent
2e4544fc5a
commit
cb00c28b6e
10 changed files with 661 additions and 91 deletions
99
MinecraftClient/Mapping/Block.cs
Normal file
99
MinecraftClient/Mapping/Block.cs
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace MinecraftClient.Mapping
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a Minecraft Block
|
||||||
|
/// </summary>
|
||||||
|
public struct Block
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Storage for block ID and metadata
|
||||||
|
/// </summary>
|
||||||
|
private ushort blockIdAndMeta;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Id of the block
|
||||||
|
/// </summary>
|
||||||
|
public short BlockId
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return (short)(blockIdAndMeta >> 4);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
blockIdAndMeta = (ushort)(value << 4 | BlockMeta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Metadata of the block
|
||||||
|
/// </summary>
|
||||||
|
public byte BlockMeta
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return (byte)(blockIdAndMeta & 0x0F);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
blockIdAndMeta = (ushort)((blockIdAndMeta & ~0x0F) | (value & 0x0F));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if the block can be passed through or not
|
||||||
|
/// </summary>
|
||||||
|
public bool Solid
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return BlockId != 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a block of the specified type and metadata
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">Block type</param>
|
||||||
|
/// <param name="metadata">Block metadata</param>
|
||||||
|
public Block(short type, byte metadata = 0)
|
||||||
|
{
|
||||||
|
this.blockIdAndMeta = 0;
|
||||||
|
this.BlockId = type;
|
||||||
|
this.BlockMeta = metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a block of the specified type and metadata
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="typeAndMeta"></param>
|
||||||
|
public Block(ushort typeAndMeta)
|
||||||
|
{
|
||||||
|
this.blockIdAndMeta = typeAndMeta;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an empty block
|
||||||
|
/// </summary>
|
||||||
|
public static Block Air
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return new Block(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// String representation of the block
|
||||||
|
/// </summary>
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return BlockId.ToString() + (BlockMeta != 0 ? ":" + BlockMeta.ToString() : "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
63
MinecraftClient/Mapping/Chunk.cs
Normal file
63
MinecraftClient/Mapping/Chunk.cs
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace MinecraftClient.Mapping
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represent a chunk of terrain in a Minecraft world
|
||||||
|
/// </summary>
|
||||||
|
public class Chunk
|
||||||
|
{
|
||||||
|
public const int SizeX = 16;
|
||||||
|
public const int SizeY = 16;
|
||||||
|
public const int SizeZ = 16;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Blocks contained into the chunk
|
||||||
|
/// </summary>
|
||||||
|
private readonly Block[,,] blocks = new Block[SizeX, SizeY, SizeZ];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read, or set the specified block
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="blockX">Block X</param>
|
||||||
|
/// <param name="blockY">Block Y</param>
|
||||||
|
/// <param name="blockZ">Block Z</param>
|
||||||
|
/// <returns>chunk at the given location</returns>
|
||||||
|
public Block this[int blockX, int blockY, int blockZ]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (blockX < 0 || blockX >= SizeX)
|
||||||
|
throw new ArgumentOutOfRangeException("blockX", "Must be between 0 and " + (SizeX - 1) + " (inclusive)");
|
||||||
|
if (blockY < 0 || blockY >= SizeY)
|
||||||
|
throw new ArgumentOutOfRangeException("blockY", "Must be between 0 and " + (SizeY - 1) + " (inclusive)");
|
||||||
|
if (blockZ < 0 || blockZ >= SizeZ)
|
||||||
|
throw new ArgumentOutOfRangeException("blockZ", "Must be between 0 and " + (SizeZ - 1) + " (inclusive)");
|
||||||
|
return blocks[blockX, blockY, blockZ];
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (blockX < 0 || blockX >= SizeX)
|
||||||
|
throw new ArgumentOutOfRangeException("blockX", "Must be between 0 and " + (SizeX - 1) + " (inclusive)");
|
||||||
|
if (blockY < 0 || blockY >= SizeY)
|
||||||
|
throw new ArgumentOutOfRangeException("blockY", "Must be between 0 and " + (SizeY - 1) + " (inclusive)");
|
||||||
|
if (blockZ < 0 || blockZ >= SizeZ)
|
||||||
|
throw new ArgumentOutOfRangeException("blockZ", "Must be between 0 and " + (SizeZ - 1) + " (inclusive)");
|
||||||
|
blocks[blockX, blockY, blockZ] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get block at the specified location
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="location">Location, a modulo will be applied</param>
|
||||||
|
/// <returns>The block</returns>
|
||||||
|
public Block GetBlock(Location location)
|
||||||
|
{
|
||||||
|
return this[((int)location.X) % Chunk.SizeX, ((int)location.Y) % Chunk.SizeY, ((int)location.Z) % Chunk.SizeZ];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
48
MinecraftClient/Mapping/ChunkColumn.cs
Normal file
48
MinecraftClient/Mapping/ChunkColumn.cs
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace MinecraftClient.Mapping
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represent a column of chunks of terrain in a Minecraft world
|
||||||
|
/// </summary>
|
||||||
|
public class ChunkColumn
|
||||||
|
{
|
||||||
|
public const int ColumnSize = 16;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Blocks contained into the chunk
|
||||||
|
/// </summary>
|
||||||
|
private readonly Chunk[] chunks = new Chunk[ColumnSize];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get or set the specified chunk column
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="chunkX">ChunkColumn X</param>
|
||||||
|
/// <param name="chunkY">ChunkColumn Y</param>
|
||||||
|
/// <returns>chunk at the given location</returns>
|
||||||
|
public Chunk this[int chunkY]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return chunks[chunkY];
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
chunks[chunkY] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get chunk at the specified location
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="location">Location, a modulo will be applied</param>
|
||||||
|
/// <returns>The chunk, or null if not loaded</returns>
|
||||||
|
public Chunk GetChunk(Location location)
|
||||||
|
{
|
||||||
|
return this[location.ChunkY];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -46,6 +46,72 @@ namespace MinecraftClient.Mapping
|
||||||
Z = z;
|
Z = z;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The X index of the corresponding chunk in the world
|
||||||
|
/// </summary>
|
||||||
|
public int ChunkX
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return ((int)X) / Chunk.SizeX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Y index of the corresponding chunk in the world
|
||||||
|
/// </summary>
|
||||||
|
public int ChunkY
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return ((int)Y) / Chunk.SizeY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Z index of the corresponding chunk in the world
|
||||||
|
/// </summary>
|
||||||
|
public int ChunkZ
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return ((int)Z) / Chunk.SizeY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The X index of the corresponding block in the corresponding chunk of the world
|
||||||
|
/// </summary>
|
||||||
|
public int ChunkBlockX
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return ((int)X) % Chunk.SizeX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Y index of the corresponding block in the corresponding chunk of the world
|
||||||
|
/// </summary>
|
||||||
|
public int ChunkBlockY
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return ((int)Y) % Chunk.SizeY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Z index of the corresponding block in the corresponding chunk of the world
|
||||||
|
/// </summary>
|
||||||
|
public int ChunkBlockZ
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return ((int)Z) % Chunk.SizeZ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Compare two locations. Locations are equals if the integer part of their coordinates are equals.
|
/// Compare two locations. Locations are equals if the integer part of their coordinates are equals.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -155,5 +221,14 @@ namespace MinecraftClient.Mapping
|
||||||
| (((int)Y) & ~((~0) << 13)) << 13
|
| (((int)Y) & ~((~0) << 13)) << 13
|
||||||
| (((int)Z) & ~((~0) << 06)) << 00;
|
| (((int)Z) & ~((~0) << 06)) << 00;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convert the location into a string representation
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>String representation of the location</returns>
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return String.Format("X:{0} Y:{1} Z:{2}", X, Y, Z);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
103
MinecraftClient/Mapping/World.cs
Normal file
103
MinecraftClient/Mapping/World.cs
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace MinecraftClient.Mapping
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a Minecraft World
|
||||||
|
/// </summary>
|
||||||
|
public class World
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The chunks contained into the Minecraft world
|
||||||
|
/// </summary>
|
||||||
|
private Dictionary<int, Dictionary<int, ChunkColumn>> chunks = new Dictionary<int, Dictionary<int, ChunkColumn>>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read, set or unload the specified chunk column
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="chunkX">ChunkColumn X</param>
|
||||||
|
/// <param name="chunkY">ChunkColumn Y</param>
|
||||||
|
/// <returns>chunk at the given location</returns>
|
||||||
|
public ChunkColumn this[int chunkX, int chunkZ]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
//Read a chunk
|
||||||
|
if (chunks.ContainsKey(chunkX))
|
||||||
|
if (chunks[chunkX].ContainsKey(chunkZ))
|
||||||
|
return chunks[chunkX][chunkZ];
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value != null)
|
||||||
|
{
|
||||||
|
//Update a chunk column
|
||||||
|
if (!chunks.ContainsKey(chunkX))
|
||||||
|
chunks[chunkX] = new Dictionary<int, ChunkColumn>();
|
||||||
|
chunks[chunkX][chunkZ] = value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//Unload a chunk column
|
||||||
|
if (chunks.ContainsKey(chunkX))
|
||||||
|
{
|
||||||
|
if (chunks[chunkX].ContainsKey(chunkZ))
|
||||||
|
{
|
||||||
|
chunks[chunkX].Remove(chunkZ);
|
||||||
|
if (chunks[chunkX].Count == 0)
|
||||||
|
chunks.Remove(chunkX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get chunk column at the specified location
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="location">Location to retrieve chunk column</param>
|
||||||
|
/// <returns>The chunk column</returns>
|
||||||
|
public ChunkColumn GetChunkColumn(Location location)
|
||||||
|
{
|
||||||
|
return this[location.ChunkX, location.ChunkZ];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get block at the specified location
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="location">Location to retrieve block from</param>
|
||||||
|
/// <returns>Block at specified location or Air if the location is not loaded</returns>
|
||||||
|
public Block GetBlock(Location location)
|
||||||
|
{
|
||||||
|
ChunkColumn column = GetChunkColumn(location);
|
||||||
|
if (column != null)
|
||||||
|
{
|
||||||
|
Chunk chunk = column.GetChunk(location);
|
||||||
|
if (chunk != null)
|
||||||
|
return chunk.GetBlock(location);
|
||||||
|
}
|
||||||
|
return Block.Air;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set block at the specified location
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="location">Location to set block to</param>
|
||||||
|
/// <param name="block">Block to set</param>
|
||||||
|
public void SetBlock(Location location, Block block)
|
||||||
|
{
|
||||||
|
ChunkColumn column = this[location.ChunkX, location.ChunkZ];
|
||||||
|
if (column != null)
|
||||||
|
{
|
||||||
|
Chunk chunk = column[location.ChunkY];
|
||||||
|
if (chunk == null)
|
||||||
|
column[location.ChunkY] = chunk = new Chunk();
|
||||||
|
chunk[location.ChunkBlockX, location.ChunkBlockY, location.ChunkBlockZ] = block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -31,6 +31,8 @@ namespace MinecraftClient
|
||||||
public void BotUnLoad(ChatBot b) { bots.RemoveAll(item => object.ReferenceEquals(item, b)); }
|
public void BotUnLoad(ChatBot b) { bots.RemoveAll(item => object.ReferenceEquals(item, b)); }
|
||||||
public void BotClear() { bots.Clear(); }
|
public void BotClear() { bots.Clear(); }
|
||||||
|
|
||||||
|
private object locationLock = new object();
|
||||||
|
private World world = new World();
|
||||||
private Location location;
|
private Location location;
|
||||||
private int updateTicks = 0;
|
private int updateTicks = 0;
|
||||||
|
|
||||||
|
|
@ -46,6 +48,7 @@ namespace MinecraftClient
|
||||||
public string GetUserUUID() { return uuid; }
|
public string GetUserUUID() { return uuid; }
|
||||||
public string GetSessionID() { return sessionid; }
|
public string GetSessionID() { return sessionid; }
|
||||||
public Location GetCurrentLocation() { return location; }
|
public Location GetCurrentLocation() { return location; }
|
||||||
|
public World GetWorld() { return world; }
|
||||||
|
|
||||||
TcpClient client;
|
TcpClient client;
|
||||||
IMinecraftCom handler;
|
IMinecraftCom handler;
|
||||||
|
|
@ -345,11 +348,14 @@ namespace MinecraftClient
|
||||||
|
|
||||||
public void UpdateLocation(Location location, bool relative)
|
public void UpdateLocation(Location location, bool relative)
|
||||||
{
|
{
|
||||||
if (relative)
|
lock (locationLock)
|
||||||
{
|
{
|
||||||
this.location += location;
|
if (relative)
|
||||||
|
{
|
||||||
|
this.location += location;
|
||||||
|
}
|
||||||
|
else this.location = location;
|
||||||
}
|
}
|
||||||
else this.location = location;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -448,7 +454,14 @@ namespace MinecraftClient
|
||||||
{
|
{
|
||||||
if (updateTicks >= 10)
|
if (updateTicks >= 10)
|
||||||
{
|
{
|
||||||
handler.SendLocationUpdate(location, true); //TODO handle onGround once terrain data is available
|
lock (locationLock)
|
||||||
|
{
|
||||||
|
Location belowMe = location + new Location(0, -1, 0);
|
||||||
|
Block blockBelowMe = world.GetBlock(belowMe);
|
||||||
|
handler.SendLocationUpdate(location, blockBelowMe.Solid);
|
||||||
|
if (!blockBelowMe.Solid)
|
||||||
|
location = belowMe;
|
||||||
|
}
|
||||||
updateTicks = 0;
|
updateTicks = 0;
|
||||||
}
|
}
|
||||||
updateTicks++;
|
updateTicks++;
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,10 @@
|
||||||
<Compile Include="Crypto\Streams\RegularAesStream.cs" />
|
<Compile Include="Crypto\Streams\RegularAesStream.cs" />
|
||||||
<Compile Include="Crypto\CryptoHandler.cs" />
|
<Compile Include="Crypto\CryptoHandler.cs" />
|
||||||
<Compile Include="CSharpRunner.cs" />
|
<Compile Include="CSharpRunner.cs" />
|
||||||
|
<Compile Include="Mapping\Block.cs" />
|
||||||
|
<Compile Include="Mapping\Chunk.cs" />
|
||||||
|
<Compile Include="Mapping\ChunkColumn.cs" />
|
||||||
|
<Compile Include="Mapping\World.cs" />
|
||||||
<Compile Include="Protocol\Handlers\Forge\FMLHandshakeClientState.cs" />
|
<Compile Include="Protocol\Handlers\Forge\FMLHandshakeClientState.cs" />
|
||||||
<Compile Include="Protocol\Handlers\Forge\FMLHandshakeDiscriminator.cs" />
|
<Compile Include="Protocol\Handlers\Forge\FMLHandshakeDiscriminator.cs" />
|
||||||
<Compile Include="Protocol\Handlers\Forge\ForgeInfo.cs" />
|
<Compile Include="Protocol\Handlers\Forge\ForgeInfo.cs" />
|
||||||
|
|
|
||||||
|
|
@ -86,9 +86,9 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
while (c.Client.Available > 0)
|
while (c.Client.Available > 0)
|
||||||
{
|
{
|
||||||
int packetID = 0;
|
int packetID = 0;
|
||||||
byte[] packetData = new byte[] { };
|
List<byte> packetData = new List<byte>();
|
||||||
readNextPacket(ref packetID, ref packetData);
|
readNextPacket(ref packetID, packetData);
|
||||||
handlePacket(packetID, packetData);
|
handlePacket(packetID, new List<byte>(packetData));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (SocketException) { return false; }
|
catch (SocketException) { return false; }
|
||||||
|
|
@ -102,21 +102,26 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
/// <param name="packetID">will contain packet ID</param>
|
/// <param name="packetID">will contain packet ID</param>
|
||||||
/// <param name="packetData">will contain raw packet Data</param>
|
/// <param name="packetData">will contain raw packet Data</param>
|
||||||
|
|
||||||
private void readNextPacket(ref int packetID, ref byte[] packetData)
|
private void readNextPacket(ref int packetID, List<byte> packetData)
|
||||||
{
|
{
|
||||||
int size = readNextVarIntRAW(); //Packet size
|
int size = readNextVarIntRAW(); //Packet size
|
||||||
packetData = readDataRAW(size); //Packet contents
|
packetData.AddRange(readDataRAW(size)); //Packet contents
|
||||||
|
|
||||||
//Handle packet decompression
|
//Handle packet decompression
|
||||||
if (protocolversion >= MC18Version
|
if (protocolversion >= MC18Version
|
||||||
&& compression_treshold > 0)
|
&& compression_treshold > 0)
|
||||||
{
|
{
|
||||||
int size_uncompressed = readNextVarInt(ref packetData);
|
int sizeUncompressed = readNextVarInt(packetData);
|
||||||
if (size_uncompressed != 0) // != 0 means compressed, let's decompress
|
if (sizeUncompressed != 0) // != 0 means compressed, let's decompress
|
||||||
packetData = ZlibUtils.Decompress(packetData, size_uncompressed);
|
{
|
||||||
|
byte[] toDecompress = packetData.ToArray();
|
||||||
|
byte[] uncompressed = ZlibUtils.Decompress(toDecompress, sizeUncompressed);
|
||||||
|
packetData.Clear();
|
||||||
|
packetData.AddRange(uncompressed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
packetID = readNextVarInt(ref packetData); //Packet ID
|
packetID = readNextVarInt(packetData); //Packet ID
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -126,7 +131,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
/// <param name="packetData">Packet contents</param>
|
/// <param name="packetData">Packet contents</param>
|
||||||
/// <returns>TRUE if the packet was processed, FALSE if ignored or unknown</returns>
|
/// <returns>TRUE if the packet was processed, FALSE if ignored or unknown</returns>
|
||||||
|
|
||||||
private bool handlePacket(int packetID, byte[] packetData)
|
private bool handlePacket(int packetID, List<byte> packetData)
|
||||||
{
|
{
|
||||||
if (login_phase)
|
if (login_phase)
|
||||||
{
|
{
|
||||||
|
|
@ -134,7 +139,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
{
|
{
|
||||||
case 0x03:
|
case 0x03:
|
||||||
if (protocolversion >= MC18Version)
|
if (protocolversion >= MC18Version)
|
||||||
compression_treshold = readNextVarInt(ref packetData);
|
compression_treshold = readNextVarInt(packetData);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return false; //Ignored packet
|
return false; //Ignored packet
|
||||||
|
|
@ -151,11 +156,11 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
handler.OnGameJoined();
|
handler.OnGameJoined();
|
||||||
break;
|
break;
|
||||||
case 0x02: //Chat message
|
case 0x02: //Chat message
|
||||||
string message = readNextString(ref packetData);
|
string message = readNextString(packetData);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
//Hide system messages or xp bar messages?
|
//Hide system messages or xp bar messages?
|
||||||
byte messageType = readData(1, ref packetData)[0];
|
byte messageType = readNextByte(packetData);
|
||||||
if ((messageType == 1 && !Settings.DisplaySystemMessages)
|
if ((messageType == 1 && !Settings.DisplaySystemMessages)
|
||||||
|| (messageType == 2 && !Settings.DisplayXPBarMessages))
|
|| (messageType == 2 && !Settings.DisplayXPBarMessages))
|
||||||
break;
|
break;
|
||||||
|
|
@ -163,14 +168,14 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
catch (IndexOutOfRangeException) { /* No message type */ }
|
catch (IndexOutOfRangeException) { /* No message type */ }
|
||||||
handler.OnTextReceived(ChatParser.ParseText(message));
|
handler.OnTextReceived(ChatParser.ParseText(message));
|
||||||
break;
|
break;
|
||||||
case 0x08:
|
case 0x08: //Player Position and Look
|
||||||
if (Settings.TerrainAndMovements)
|
if (Settings.TerrainAndMovements)
|
||||||
{
|
{
|
||||||
double x = readNextDouble(ref packetData);
|
double x = readNextDouble(packetData);
|
||||||
double y = readNextDouble(ref packetData);
|
double y = readNextDouble(packetData);
|
||||||
double z = readNextDouble(ref packetData);
|
double z = readNextDouble(packetData);
|
||||||
|
readData(8, packetData); //Ignore look
|
||||||
byte locMask = readNextByte(ref packetData);
|
byte locMask = readNextByte(packetData);
|
||||||
Location location = handler.GetCurrentLocation();
|
Location location = handler.GetCurrentLocation();
|
||||||
|
|
||||||
location.X = (locMask & 1 << 0) != 0 ? location.X + x : x;
|
location.X = (locMask & 1 << 0) != 0 ? location.X + x : x;
|
||||||
|
|
@ -180,18 +185,72 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
handler.UpdateLocation(location);
|
handler.UpdateLocation(location);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 0x21: //Chunk Data
|
||||||
|
if (Settings.TerrainAndMovements)
|
||||||
|
{
|
||||||
|
int chunkX = readNextInt(packetData);
|
||||||
|
int chunkZ = readNextInt(packetData);
|
||||||
|
bool chunksContinuous = readNextBool(packetData);
|
||||||
|
ushort chunkMask = readNextUShort(packetData);
|
||||||
|
int dataSize = readNextVarInt(packetData);
|
||||||
|
ProcessChunkColumnData(chunkX, chunkZ, chunkMask, false, chunksContinuous, packetData);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x22: //Multi Block Change
|
||||||
|
if (Settings.TerrainAndMovements)
|
||||||
|
{
|
||||||
|
int chunkX = readNextInt(packetData);
|
||||||
|
int chunkZ = readNextInt(packetData);
|
||||||
|
int recordCount = readNextVarInt(packetData);
|
||||||
|
for (int i = 0; i < recordCount; i++)
|
||||||
|
{
|
||||||
|
byte locationXZ = readNextByte(packetData);
|
||||||
|
int blockX = locationXZ >> 4;
|
||||||
|
int blockZ = locationXZ & 0x0F;
|
||||||
|
int blockY = (ushort)readNextByte(packetData);
|
||||||
|
Block block = new Block((ushort)readNextVarInt(packetData));
|
||||||
|
handler.GetWorld().SetBlock(new Location(blockX + chunkX * Chunk.SizeX, blockY, blockZ + chunkZ * Chunk.SizeZ), block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x23: //Block Change
|
||||||
|
if (Settings.TerrainAndMovements)
|
||||||
|
handler.GetWorld().SetBlock(Location.FromLongRepresentation(readNextULong(packetData)), new Block((ushort)readNextVarInt(packetData)));
|
||||||
|
break;
|
||||||
|
case 0x26: //Map Chunk Bulk
|
||||||
|
if (Settings.TerrainAndMovements)
|
||||||
|
{
|
||||||
|
bool hasSkyLight = readNextBool(packetData);
|
||||||
|
int chunkCount = readNextVarInt(packetData);
|
||||||
|
|
||||||
|
//Read chunk records
|
||||||
|
int[] chunkXs = new int[chunkCount];
|
||||||
|
int[] chunkZs = new int[chunkCount];
|
||||||
|
ushort[] chunkMasks = new ushort[chunkCount];
|
||||||
|
for (int chunkColumnNo = 0; chunkColumnNo < chunkCount; chunkColumnNo++)
|
||||||
|
{
|
||||||
|
chunkXs[chunkColumnNo] = readNextInt(packetData);
|
||||||
|
chunkZs[chunkColumnNo] = readNextInt(packetData);
|
||||||
|
chunkMasks[chunkColumnNo] = readNextUShort(packetData);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Process chunk records
|
||||||
|
for (int chunkColumnNo = 0; chunkColumnNo < chunkCount; chunkColumnNo++)
|
||||||
|
ProcessChunkColumnData(chunkXs[chunkColumnNo], chunkZs[chunkColumnNo], chunkMasks[chunkColumnNo], hasSkyLight, true, packetData);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case 0x38: //Player List update
|
case 0x38: //Player List update
|
||||||
if (protocolversion >= MC18Version)
|
if (protocolversion >= MC18Version)
|
||||||
{
|
{
|
||||||
int action = readNextVarInt(ref packetData);
|
int action = readNextVarInt(packetData);
|
||||||
int numActions = readNextVarInt(ref packetData);
|
int numActions = readNextVarInt(packetData);
|
||||||
for (int i = 0; i < numActions; i++)
|
for (int i = 0; i < numActions; i++)
|
||||||
{
|
{
|
||||||
Guid uuid = readNextUUID(ref packetData);
|
Guid uuid = readNextUUID(packetData);
|
||||||
switch (action)
|
switch (action)
|
||||||
{
|
{
|
||||||
case 0x00: //Player Join
|
case 0x00: //Player Join
|
||||||
string name = readNextString(ref packetData);
|
string name = readNextString(packetData);
|
||||||
handler.OnPlayerJoin(uuid, name);
|
handler.OnPlayerJoin(uuid, name);
|
||||||
break;
|
break;
|
||||||
case 0x04: //Player Leave
|
case 0x04: //Player Leave
|
||||||
|
|
@ -205,9 +264,9 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
}
|
}
|
||||||
else //MC 1.7.X does not provide UUID in tab-list updates
|
else //MC 1.7.X does not provide UUID in tab-list updates
|
||||||
{
|
{
|
||||||
string name = readNextString(ref packetData);
|
string name = readNextString(packetData);
|
||||||
bool online = readNextBool(ref packetData);
|
bool online = readNextBool(packetData);
|
||||||
short ping = readNextShort(ref packetData);
|
short ping = readNextShort(packetData);
|
||||||
Guid FakeUUID = new Guid(MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(name)).Take(16).ToArray());
|
Guid FakeUUID = new Guid(MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(name)).Take(16).ToArray());
|
||||||
if (online)
|
if (online)
|
||||||
handler.OnPlayerJoin(FakeUUID, name);
|
handler.OnPlayerJoin(FakeUUID, name);
|
||||||
|
|
@ -215,11 +274,11 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x3A: //Tab-Complete Result
|
case 0x3A: //Tab-Complete Result
|
||||||
int autocomplete_count = readNextVarInt(ref packetData);
|
int autocomplete_count = readNextVarInt(packetData);
|
||||||
string tab_list = "";
|
string tab_list = "";
|
||||||
for (int i = 0; i < autocomplete_count; i++)
|
for (int i = 0; i < autocomplete_count; i++)
|
||||||
{
|
{
|
||||||
autocomplete_result = readNextString(ref packetData);
|
autocomplete_result = readNextString(packetData);
|
||||||
if (autocomplete_result != "")
|
if (autocomplete_result != "")
|
||||||
tab_list = tab_list + autocomplete_result + " ";
|
tab_list = tab_list + autocomplete_result + " ";
|
||||||
}
|
}
|
||||||
|
|
@ -229,26 +288,26 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
ConsoleIO.WriteLineFormatted("§8" + tab_list, false);
|
ConsoleIO.WriteLineFormatted("§8" + tab_list, false);
|
||||||
break;
|
break;
|
||||||
case 0x3F: //Plugin message.
|
case 0x3F: //Plugin message.
|
||||||
String channel = readNextString(ref packetData);
|
String channel = readNextString(packetData);
|
||||||
if (protocolversion < MC18Version)
|
if (protocolversion < MC18Version)
|
||||||
{
|
{
|
||||||
if (forgeInfo == null)
|
if (forgeInfo == null)
|
||||||
{
|
{
|
||||||
// 1.7 and lower prefix plugin channel packets with the length.
|
// 1.7 and lower prefix plugin channel packets with the length.
|
||||||
// We can skip it, though.
|
// We can skip it, though.
|
||||||
readNextShort(ref packetData);
|
readNextShort(packetData);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Forge does something even weirder with the length.
|
// Forge does something even weirder with the length.
|
||||||
readNextVarShort(ref packetData);
|
readNextVarShort(packetData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (forgeInfo != null)
|
if (forgeInfo != null)
|
||||||
{
|
{
|
||||||
if (channel == "FML|HS")
|
if (channel == "FML|HS")
|
||||||
{
|
{
|
||||||
FMLHandshakeDiscriminator discriminator = (FMLHandshakeDiscriminator)readNextByte(ref packetData);
|
FMLHandshakeDiscriminator discriminator = (FMLHandshakeDiscriminator)readNextByte(packetData);
|
||||||
|
|
||||||
if (discriminator == FMLHandshakeDiscriminator.HandshakeReset)
|
if (discriminator == FMLHandshakeDiscriminator.HandshakeReset)
|
||||||
{
|
{
|
||||||
|
|
@ -269,7 +328,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
string[] channels = { "FML|HS", "FML", "FML|MP", "FML", "FORGE" };
|
string[] channels = { "FML|HS", "FML", "FML|MP", "FML", "FORGE" };
|
||||||
SendPluginChannelPacket("REGISTER", Encoding.UTF8.GetBytes(string.Join("\0", channels)));
|
SendPluginChannelPacket("REGISTER", Encoding.UTF8.GetBytes(string.Join("\0", channels)));
|
||||||
|
|
||||||
byte fmlProtocolVersion = readNextByte(ref packetData);
|
byte fmlProtocolVersion = readNextByte(packetData);
|
||||||
// There's another value afterwards for the dimension, but we don't need it.
|
// There's another value afterwards for the dimension, but we don't need it.
|
||||||
|
|
||||||
ConsoleIO.WriteLineFormatted("§8Forge protocol version : " + fmlProtocolVersion);
|
ConsoleIO.WriteLineFormatted("§8Forge protocol version : " + fmlProtocolVersion);
|
||||||
|
|
@ -316,7 +375,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
{
|
{
|
||||||
// 1.7.10 and below have one registry
|
// 1.7.10 and below have one registry
|
||||||
// with blocks and items.
|
// with blocks and items.
|
||||||
int registrySize = readNextVarInt(ref packetData);
|
int registrySize = readNextVarInt(packetData);
|
||||||
|
|
||||||
ConsoleIO.WriteLineFormatted("§8Received registry " +
|
ConsoleIO.WriteLineFormatted("§8Received registry " +
|
||||||
"with " + registrySize + " entries");
|
"with " + registrySize + " entries");
|
||||||
|
|
@ -327,9 +386,9 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
{
|
{
|
||||||
// 1.8+ has more than one registry.
|
// 1.8+ has more than one registry.
|
||||||
|
|
||||||
bool hasNextRegistry = readNextBool(ref packetData);
|
bool hasNextRegistry = readNextBool(packetData);
|
||||||
string registryName = readNextString(ref packetData);
|
string registryName = readNextString(packetData);
|
||||||
int registrySize = readNextVarInt(ref packetData);
|
int registrySize = readNextVarInt(packetData);
|
||||||
|
|
||||||
ConsoleIO.WriteLineFormatted("§8Received registry " + registryName +
|
ConsoleIO.WriteLineFormatted("§8Received registry " + registryName +
|
||||||
" with " + registrySize + " entries");
|
" with " + registrySize + " entries");
|
||||||
|
|
@ -370,15 +429,15 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
case 0x40: //Kick Packet
|
case 0x40: //Kick Packet
|
||||||
handler.OnConnectionLost(ChatBot.DisconnectReason.InGameKick, ChatParser.ParseText(readNextString(ref packetData)));
|
handler.OnConnectionLost(ChatBot.DisconnectReason.InGameKick, ChatParser.ParseText(readNextString(packetData)));
|
||||||
return false;
|
return false;
|
||||||
case 0x46: //Network Compression Treshold Info
|
case 0x46: //Network Compression Treshold Info
|
||||||
if (protocolversion >= MC18Version)
|
if (protocolversion >= MC18Version)
|
||||||
compression_treshold = readNextVarInt(ref packetData);
|
compression_treshold = readNextVarInt(packetData);
|
||||||
break;
|
break;
|
||||||
case 0x48: //Resource Pack Send
|
case 0x48: //Resource Pack Send
|
||||||
string url = readNextString(ref packetData);
|
string url = readNextString(packetData);
|
||||||
string hash = readNextString(ref packetData);
|
string hash = readNextString(packetData);
|
||||||
//Send back "accepted" and "successfully loaded" responses for plugins making use of resource pack mandatory
|
//Send back "accepted" and "successfully loaded" responses for plugins making use of resource pack mandatory
|
||||||
SendPacket(0x19, concatBytes(getVarInt(hash.Length), Encoding.UTF8.GetBytes(hash), getVarInt(3)));
|
SendPacket(0x19, concatBytes(getVarInt(hash.Length), Encoding.UTF8.GetBytes(hash), getVarInt(3)));
|
||||||
SendPacket(0x19, concatBytes(getVarInt(hash.Length), Encoding.UTF8.GetBytes(hash), getVarInt(0)));
|
SendPacket(0x19, concatBytes(getVarInt(hash.Length), Encoding.UTF8.GetBytes(hash), getVarInt(0)));
|
||||||
|
|
@ -389,6 +448,66 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return true; //Packet processed
|
return true; //Packet processed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Process chunk column data from the server and (un)load the chunk from the Minecraft world
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="chunkX">Chunk X location</param>
|
||||||
|
/// <param name="chunkZ">Chunk Z location</param>
|
||||||
|
/// <param name="chunkMask">Chunk mask for reading data</param>
|
||||||
|
/// <param name="hasSkyLight">Contains skylight info</param>
|
||||||
|
/// <param name="chunksContinuous">Are the chunk continuous</param>
|
||||||
|
/// <param name="cache">Cache for reading chunk data</param>
|
||||||
|
|
||||||
|
private void ProcessChunkColumnData(int chunkX, int chunkZ, ushort chunkMask, bool hasSkyLight, bool chunksContinuous, List<byte> cache)
|
||||||
|
{
|
||||||
|
if (chunksContinuous && chunkMask == 0)
|
||||||
|
{
|
||||||
|
//Unload the entire chunk column
|
||||||
|
handler.GetWorld()[chunkX, chunkZ] = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//Load chunk data from the server
|
||||||
|
for (int chunkY = 0; chunkY < ChunkColumn.ColumnSize; chunkY++)
|
||||||
|
{
|
||||||
|
if ((chunkMask & (1 << chunkY)) != 0)
|
||||||
|
{
|
||||||
|
Chunk chunk = new Chunk();
|
||||||
|
|
||||||
|
//Read chunk data, all at once for performance reasons, and build the chunk object
|
||||||
|
Queue<ushort> queue = new Queue<ushort>(readNextUShortsLittleEndian(Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ, cache));
|
||||||
|
for (int blockY = 0; blockY < Chunk.SizeY; blockY++)
|
||||||
|
for (int blockZ = 0; blockZ < Chunk.SizeZ; blockZ++)
|
||||||
|
for (int blockX = 0; blockX < Chunk.SizeX; blockX++)
|
||||||
|
chunk[blockX, blockY, blockZ] = new Block(queue.Dequeue());
|
||||||
|
|
||||||
|
//We have our chunk, save the chunk into the world
|
||||||
|
if (handler.GetWorld()[chunkX, chunkZ] == null)
|
||||||
|
handler.GetWorld()[chunkX, chunkZ] = new ChunkColumn();
|
||||||
|
handler.GetWorld()[chunkX, chunkZ][chunkY] = chunk;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Skip light information
|
||||||
|
for (int chunkY = 0; chunkY < ChunkColumn.ColumnSize; chunkY++)
|
||||||
|
{
|
||||||
|
if ((chunkMask & (1 << chunkY)) != 0)
|
||||||
|
{
|
||||||
|
//Skip block light
|
||||||
|
readData((Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ) / 2, cache);
|
||||||
|
|
||||||
|
//Skip sky light
|
||||||
|
if (hasSkyLight)
|
||||||
|
readData((Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ) / 2, cache);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Skip biome metadata
|
||||||
|
if (chunksContinuous)
|
||||||
|
readData(Chunk.SizeX * Chunk.SizeZ, cache);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Start the updating thread. Should be called after login success.
|
/// Start the updating thread. Should be called after login success.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -445,10 +564,10 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
/// <param name="cache">Cache of bytes to read from</param>
|
/// <param name="cache">Cache of bytes to read from</param>
|
||||||
/// <returns>The data read from the cache as an array</returns>
|
/// <returns>The data read from the cache as an array</returns>
|
||||||
|
|
||||||
private static byte[] readData(int offset, ref byte[] cache)
|
private static byte[] readData(int offset, List<byte> cache)
|
||||||
{
|
{
|
||||||
byte[] result = cache.Take(offset).ToArray();
|
byte[] result = cache.Take(offset).ToArray();
|
||||||
cache = cache.Skip(offset).ToArray();
|
cache.RemoveRange(0, offset);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -458,12 +577,12 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
/// <param name="cache">Cache of bytes to read from</param>
|
/// <param name="cache">Cache of bytes to read from</param>
|
||||||
/// <returns>The string</returns>
|
/// <returns>The string</returns>
|
||||||
|
|
||||||
private static string readNextString(ref byte[] cache)
|
private static string readNextString(List<byte> cache)
|
||||||
{
|
{
|
||||||
int length = readNextVarInt(ref cache);
|
int length = readNextVarInt(cache);
|
||||||
if (length > 0)
|
if (length > 0)
|
||||||
{
|
{
|
||||||
return Encoding.UTF8.GetString(readData(length, ref cache));
|
return Encoding.UTF8.GetString(readData(length, cache));
|
||||||
}
|
}
|
||||||
else return "";
|
else return "";
|
||||||
}
|
}
|
||||||
|
|
@ -473,9 +592,9 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The boolean value</returns>
|
/// <returns>The boolean value</returns>
|
||||||
|
|
||||||
private static bool readNextBool(ref byte[] cache)
|
private static bool readNextBool(List<byte> cache)
|
||||||
{
|
{
|
||||||
return readData(1, ref cache)[0] != 0x00;
|
return readNextByte(cache) != 0x00;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -483,34 +602,79 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The short integer value</returns>
|
/// <returns>The short integer value</returns>
|
||||||
|
|
||||||
private static short readNextShort(ref byte[] cache)
|
private static short readNextShort(List<byte> cache)
|
||||||
{
|
{
|
||||||
byte[] rawValue = readData(2, ref cache);
|
byte[] rawValue = readData(2, cache);
|
||||||
Array.Reverse(rawValue); //Endianness
|
Array.Reverse(rawValue); //Endianness
|
||||||
return BitConverter.ToInt16(rawValue, 0);
|
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>
|
||||||
|
|
||||||
|
private static int readNextInt(List<byte> cache)
|
||||||
|
{
|
||||||
|
byte[] rawValue = readData(4, cache);
|
||||||
|
Array.Reverse(rawValue); //Endianness
|
||||||
|
return BitConverter.ToInt32(rawValue, 0);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read an unsigned short integer from a cache of bytes and remove it from the cache
|
/// Read an unsigned short integer from a cache of bytes and remove it from the cache
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The unsigned short integer value</returns>
|
/// <returns>The unsigned short integer value</returns>
|
||||||
|
|
||||||
private static ushort readNextUShort(ref byte[] cache)
|
private static ushort readNextUShort(List<byte> cache)
|
||||||
{
|
{
|
||||||
byte[] rawValue = readData(2, ref cache);
|
byte[] rawValue = readData(2, cache);
|
||||||
Array.Reverse(rawValue); //Endianness
|
Array.Reverse(rawValue); //Endianness
|
||||||
return BitConverter.ToUInt16(rawValue, 0);
|
return BitConverter.ToUInt16(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>
|
||||||
|
|
||||||
|
private static ulong readNextULong(List<byte> cache)
|
||||||
|
{
|
||||||
|
byte[] rawValue = readData(8, cache);
|
||||||
|
Array.Reverse(rawValue); //Endianness
|
||||||
|
return BitConverter.ToUInt64(rawValue, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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>
|
||||||
|
|
||||||
|
private static ushort[] readNextUShortsLittleEndian(int amount, List<byte> cache)
|
||||||
|
{
|
||||||
|
byte[] rawValues = readData(2 * amount, cache);
|
||||||
|
byte[] rawValue = new byte[2];
|
||||||
|
ushort[] result = new ushort[amount];
|
||||||
|
|
||||||
|
for (int i = 0; i < amount; i++)
|
||||||
|
{
|
||||||
|
rawValue[0] = rawValues[i * 2];
|
||||||
|
rawValue[1] = rawValues[i * 2 + 1];
|
||||||
|
result[i] = BitConverter.ToUInt16(rawValue, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read a uuid from a cache of bytes and remove it from the cache
|
/// Read a uuid from a cache of bytes and remove it from the cache
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="cache">Cache of bytes to read from</param>
|
/// <param name="cache">Cache of bytes to read from</param>
|
||||||
/// <returns>The uuid</returns>
|
/// <returns>The uuid</returns>
|
||||||
|
|
||||||
private static Guid readNextUUID(ref byte[] cache)
|
private static Guid readNextUUID(List<byte> cache)
|
||||||
{
|
{
|
||||||
return new Guid(readData(16, ref cache));
|
return new Guid(readData(16, cache));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -519,12 +683,12 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
/// <param name="cache">Cache of bytes to read from</param>
|
/// <param name="cache">Cache of bytes to read from</param>
|
||||||
/// <returns>The byte array</returns>
|
/// <returns>The byte array</returns>
|
||||||
|
|
||||||
private byte[] readNextByteArray(ref byte[] cache)
|
private byte[] readNextByteArray(List<byte> cache)
|
||||||
{
|
{
|
||||||
int len = protocolversion >= MC18Version
|
int len = protocolversion >= MC18Version
|
||||||
? readNextVarInt(ref cache)
|
? readNextVarInt(cache)
|
||||||
: readNextShort(ref cache);
|
: readNextShort(cache);
|
||||||
return readData(len, ref cache);
|
return readData(len, cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -532,9 +696,9 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The double value</returns>
|
/// <returns>The double value</returns>
|
||||||
|
|
||||||
private static double readNextDouble(ref byte[] cache)
|
private static double readNextDouble(List<byte> cache)
|
||||||
{
|
{
|
||||||
byte[] rawValue = readData(8, ref cache);
|
byte[] rawValue = readData(8, cache);
|
||||||
Array.Reverse(rawValue); //Endianness
|
Array.Reverse(rawValue); //Endianness
|
||||||
return BitConverter.ToDouble(rawValue, 0);
|
return BitConverter.ToDouble(rawValue, 0);
|
||||||
}
|
}
|
||||||
|
|
@ -567,16 +731,14 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
/// <param name="cache">Cache of bytes to read from</param>
|
/// <param name="cache">Cache of bytes to read from</param>
|
||||||
/// <returns>The integer</returns>
|
/// <returns>The integer</returns>
|
||||||
|
|
||||||
private static int readNextVarInt(ref byte[] cache)
|
private static int readNextVarInt(List<byte> cache)
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int j = 0;
|
int j = 0;
|
||||||
int k = 0;
|
int k = 0;
|
||||||
byte[] tmp = new byte[1];
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
tmp = readData(1, ref cache);
|
k = readNextByte(cache);
|
||||||
k = tmp[0];
|
|
||||||
i |= (k & 0x7F) << j++ * 7;
|
i |= (k & 0x7F) << j++ * 7;
|
||||||
if (j > 5) throw new OverflowException("VarInt too big");
|
if (j > 5) throw new OverflowException("VarInt too big");
|
||||||
if ((k & 0x80) != 128) break;
|
if ((k & 0x80) != 128) break;
|
||||||
|
|
@ -592,14 +754,14 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
/// <param name="cache">Cache of bytes to read from</param>
|
/// <param name="cache">Cache of bytes to read from</param>
|
||||||
/// <returns>The int</returns>
|
/// <returns>The int</returns>
|
||||||
|
|
||||||
private static int readNextVarShort(ref byte[] cache)
|
private static int readNextVarShort(List<byte> cache)
|
||||||
{
|
{
|
||||||
ushort low = readNextUShort(ref cache);
|
ushort low = readNextUShort(cache);
|
||||||
byte high = 0;
|
byte high = 0;
|
||||||
if ((low & 0x8000) != 0)
|
if ((low & 0x8000) != 0)
|
||||||
{
|
{
|
||||||
low &= 0x7FFF;
|
low &= 0x7FFF;
|
||||||
high = readNextByte(ref cache);
|
high = readNextByte(cache);
|
||||||
}
|
}
|
||||||
return ((high & 0xFF) << 15) | low;
|
return ((high & 0xFF) << 15) | low;
|
||||||
}
|
}
|
||||||
|
|
@ -609,9 +771,11 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The byte that was read</returns>
|
/// <returns>The byte that was read</returns>
|
||||||
|
|
||||||
private static byte readNextByte(ref byte[] cache)
|
private static byte readNextByte(List<byte> cache)
|
||||||
{
|
{
|
||||||
return readData(1, ref cache)[0];
|
byte result = cache[0];
|
||||||
|
cache.RemoveAt(0);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -734,10 +898,10 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
/// <param name="packetID">packet ID</param>
|
/// <param name="packetID">packet ID</param>
|
||||||
/// <param name="packetData">packet Data</param>
|
/// <param name="packetData">packet Data</param>
|
||||||
|
|
||||||
private void SendPacket(int packetID, byte[] packetData)
|
private void SendPacket(int packetID, IEnumerable<byte> packetData)
|
||||||
{
|
{
|
||||||
//The inner packet
|
//The inner packet
|
||||||
byte[] the_packet = concatBytes(getVarInt(packetID), packetData);
|
byte[] the_packet = concatBytes(getVarInt(packetID), packetData.ToArray());
|
||||||
|
|
||||||
if (compression_treshold > 0) //Compression enabled?
|
if (compression_treshold > 0) //Compression enabled?
|
||||||
{
|
{
|
||||||
|
|
@ -795,20 +959,20 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
SendPacket(0x00, login_packet);
|
SendPacket(0x00, login_packet);
|
||||||
|
|
||||||
int packetID = -1;
|
int packetID = -1;
|
||||||
byte[] packetData = new byte[] { };
|
List<byte> packetData = new List<byte>();
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
readNextPacket(ref packetID, ref packetData);
|
readNextPacket(ref packetID, packetData);
|
||||||
if (packetID == 0x00) //Login rejected
|
if (packetID == 0x00) //Login rejected
|
||||||
{
|
{
|
||||||
handler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, ChatParser.ParseText(readNextString(ref packetData)));
|
handler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, ChatParser.ParseText(readNextString(packetData)));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if (packetID == 0x01) //Encryption request
|
else if (packetID == 0x01) //Encryption request
|
||||||
{
|
{
|
||||||
string serverID = readNextString(ref packetData);
|
string serverID = readNextString(packetData);
|
||||||
byte[] Serverkey = readNextByteArray(ref packetData);
|
byte[] Serverkey = readNextByteArray(packetData);
|
||||||
byte[] token = readNextByteArray(ref packetData);
|
byte[] token = readNextByteArray(packetData);
|
||||||
return StartEncryption(handler.GetUserUUID(), handler.GetSessionID(), token, serverID, Serverkey);
|
return StartEncryption(handler.GetUserUUID(), handler.GetSessionID(), token, serverID, Serverkey);
|
||||||
}
|
}
|
||||||
else if (packetID == 0x02) //Login successful
|
else if (packetID == 0x02) //Login successful
|
||||||
|
|
@ -838,15 +1002,15 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
private bool CompleteForgeHandshake()
|
private bool CompleteForgeHandshake()
|
||||||
{
|
{
|
||||||
int packetID = -1;
|
int packetID = -1;
|
||||||
byte[] packetData = new byte[0];
|
List<byte> packetData = new List<byte>();
|
||||||
|
|
||||||
while (fmlHandshakeState != FMLHandshakeClientState.DONE)
|
while (fmlHandshakeState != FMLHandshakeClientState.DONE)
|
||||||
{
|
{
|
||||||
readNextPacket(ref packetID, ref packetData);
|
readNextPacket(ref packetID, packetData);
|
||||||
|
|
||||||
if (packetID == 0x40) // Disconect
|
if (packetID == 0x40) // Disconect
|
||||||
{
|
{
|
||||||
handler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, ChatParser.ParseText(readNextString(ref packetData)));
|
handler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, ChatParser.ParseText(readNextString(packetData)));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -893,13 +1057,13 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
|
|
||||||
//Process the next packet
|
//Process the next packet
|
||||||
int packetID = -1;
|
int packetID = -1;
|
||||||
byte[] packetData = new byte[] { };
|
List<byte> packetData = new List<byte>();
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
readNextPacket(ref packetID, ref packetData);
|
readNextPacket(ref packetID, packetData);
|
||||||
if (packetID == 0x00) //Login rejected
|
if (packetID == 0x00) //Login rejected
|
||||||
{
|
{
|
||||||
handler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, ChatParser.ParseText(readNextString(ref packetData)));
|
handler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, ChatParser.ParseText(readNextString(packetData)));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if (packetID == 0x02) //Login successful
|
else if (packetID == 0x02) //Login successful
|
||||||
|
|
@ -1101,10 +1265,10 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
int packetLength = ComTmp.readNextVarIntRAW();
|
int packetLength = ComTmp.readNextVarIntRAW();
|
||||||
if (packetLength > 0) //Read Response length
|
if (packetLength > 0) //Read Response length
|
||||||
{
|
{
|
||||||
byte[] packetData = ComTmp.readDataRAW(packetLength);
|
List<byte> packetData = new List<byte>(ComTmp.readDataRAW(packetLength));
|
||||||
if (readNextVarInt(ref packetData) == 0x00) //Read Packet ID
|
if (readNextVarInt(packetData) == 0x00) //Read Packet ID
|
||||||
{
|
{
|
||||||
string result = readNextString(ref packetData); //Get the Json data
|
string result = readNextString(packetData); //Get the Json data
|
||||||
|
|
||||||
if (!String.IsNullOrEmpty(result) && result.StartsWith("{") && result.EndsWith("}"))
|
if (!String.IsNullOrEmpty(result) && result.StartsWith("{") && result.EndsWith("}"))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ namespace MinecraftClient.Protocol
|
||||||
string GetSessionID();
|
string GetSessionID();
|
||||||
string[] GetOnlinePlayers();
|
string[] GetOnlinePlayers();
|
||||||
Location GetCurrentLocation();
|
Location GetCurrentLocation();
|
||||||
|
World GetWorld();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when a server was successfully joined
|
/// Called when a server was successfully joined
|
||||||
|
|
|
||||||
|
|
@ -409,7 +409,7 @@ namespace MinecraftClient
|
||||||
+ "chatbotlogfile= #leave empty for no logfile\r\n"
|
+ "chatbotlogfile= #leave empty for no logfile\r\n"
|
||||||
+ "showsystemmessages=true #system messages for server ops\r\n"
|
+ "showsystemmessages=true #system messages for server ops\r\n"
|
||||||
+ "showxpbarmessages=true #messages displayed above xp bar\r\n"
|
+ "showxpbarmessages=true #messages displayed above xp bar\r\n"
|
||||||
+ "handleterrainandmovements=false #requires more ram and cpu\r\n"
|
+ "handleterrainandmovements=false #requires quite more ram\r\n"
|
||||||
+ "accountlist=accounts.txt\r\n"
|
+ "accountlist=accounts.txt\r\n"
|
||||||
+ "serverlist=servers.txt\r\n"
|
+ "serverlist=servers.txt\r\n"
|
||||||
+ "playerheadicon=true\r\n"
|
+ "playerheadicon=true\r\n"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue