mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2025-11-07 17:36:07 +00:00
Merge pull request #160 from Pokechu22/1.9-terrain-and-movement
Implementation of terrain and movement for 1.9 by Pokechu22
This commit is contained in:
commit
734abe2296
1 changed files with 148 additions and 32 deletions
|
|
@ -20,6 +20,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
{
|
||||
private const int MC18Version = 47;
|
||||
private const int MC19Version = 107;
|
||||
private const int MC191Version = 108;
|
||||
private const int MC110Version = 210;
|
||||
|
||||
private int compression_treshold = 0;
|
||||
|
|
@ -38,6 +39,8 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
IAesStream s;
|
||||
TcpClient c;
|
||||
|
||||
int currentDimension;
|
||||
|
||||
public Protocol18Handler(TcpClient Client, int ProtocolVersion, IMinecraftComHandler Handler, ForgeInfo ForgeInfo)
|
||||
{
|
||||
ConsoleIO.SetAutoCompleteEngine(this);
|
||||
|
|
@ -46,12 +49,6 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
this.protocolversion = ProtocolVersion;
|
||||
this.handler = Handler;
|
||||
this.forgeInfo = ForgeInfo;
|
||||
|
||||
if (Settings.TerrainAndMovements && protocolversion > MC18Version)
|
||||
{
|
||||
ConsoleIO.WriteLineFormatted("§8Terrain & Movements currently not handled for that MC version.");
|
||||
Settings.TerrainAndMovements = false;
|
||||
}
|
||||
}
|
||||
|
||||
private Protocol18Handler(TcpClient Client)
|
||||
|
|
@ -142,6 +139,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
KeepAlive,
|
||||
JoinGame,
|
||||
ChatMessage,
|
||||
Respawn,
|
||||
PlayerPositionAndLook,
|
||||
ChunkData,
|
||||
MultiBlockChange,
|
||||
|
|
@ -173,6 +171,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
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;
|
||||
|
|
@ -195,6 +194,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
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;
|
||||
|
|
@ -234,7 +234,6 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
}
|
||||
}
|
||||
// Regular in-game packets
|
||||
|
||||
switch (getPacketIncomingType(packetID, protocolversion))
|
||||
{
|
||||
case PacketIncomingType.KeepAlive:
|
||||
|
|
@ -242,6 +241,16 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
break;
|
||||
case PacketIncomingType.JoinGame:
|
||||
handler.OnGameJoined();
|
||||
readNextInt(packetData);
|
||||
readNextByte(packetData);
|
||||
if (protocolversion >= MC191Version)
|
||||
this.currentDimension = readNextInt(packetData);
|
||||
else
|
||||
this.currentDimension = (sbyte)readNextByte(packetData);
|
||||
readNextByte(packetData);
|
||||
readNextByte(packetData);
|
||||
readNextString(packetData);
|
||||
readNextBool(packetData);
|
||||
break;
|
||||
case PacketIncomingType.ChatMessage:
|
||||
string message = readNextString(packetData);
|
||||
|
|
@ -256,6 +265,12 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
catch (ArgumentOutOfRangeException) { /* No message type */ }
|
||||
handler.OnTextReceived(ChatParser.ParseText(message));
|
||||
break;
|
||||
case PacketIncomingType.Respawn:
|
||||
this.currentDimension = readNextInt(packetData);
|
||||
readNextByte(packetData);
|
||||
readNextByte(packetData);
|
||||
readNextString(packetData);
|
||||
break;
|
||||
case PacketIncomingType.PlayerPositionAndLook:
|
||||
if (Settings.TerrainAndMovements)
|
||||
{
|
||||
|
|
@ -273,7 +288,11 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
handler.UpdateLocation(location);
|
||||
|
||||
if (protocolversion >= MC19Version)
|
||||
readNextVarInt(packetData);
|
||||
{
|
||||
int teleportID = readNextVarInt(packetData);
|
||||
// Teleport confirm packet
|
||||
SendPacket(0x00, getVarInt(teleportID));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PacketIncomingType.ChunkData:
|
||||
|
|
@ -446,11 +465,13 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
SendPluginChannelPacket("REGISTER", Encoding.UTF8.GetBytes(string.Join("\0", channels)));
|
||||
|
||||
byte fmlProtocolVersion = readNextByte(packetData);
|
||||
// There's another value afterwards for the dimension, but we don't need it.
|
||||
|
||||
if (Settings.DebugMessages)
|
||||
ConsoleIO.WriteLineFormatted("§8Forge protocol version : " + fmlProtocolVersion);
|
||||
|
||||
if (fmlProtocolVersion >= 1)
|
||||
this.currentDimension = readNextInt(packetData);
|
||||
|
||||
// Tell the server we're running the same version.
|
||||
SendForgeHandshakePacket(FMLHandshakeDiscriminator.ClientHello, new byte[] { fmlProtocolVersion });
|
||||
|
||||
|
|
@ -581,51 +602,132 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
|
||||
private void ProcessChunkColumnData(int chunkX, int chunkZ, ushort chunkMask, bool hasSkyLight, bool chunksContinuous, List<byte> cache)
|
||||
{
|
||||
if (protocolversion < MC19Version && chunksContinuous && chunkMask == 0)
|
||||
if (protocolversion >= MC19Version)
|
||||
{
|
||||
//Unload the entire chunk column
|
||||
handler.GetWorld()[chunkX, chunkZ] = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Load chunk data from the server
|
||||
// 1.9 and above chunk format
|
||||
// Unloading chunks is handled by a separate packet
|
||||
for (int chunkY = 0; chunkY < ChunkColumn.ColumnSize; chunkY++)
|
||||
{
|
||||
if ((chunkMask & (1 << chunkY)) != 0)
|
||||
{
|
||||
byte bitsPerBlock = readNextByte(cache);
|
||||
bool usePalette = (bitsPerBlock <= 8);
|
||||
|
||||
int paletteLength = readNextVarInt(cache);
|
||||
int[] palette = new int[paletteLength];
|
||||
for (int i = 0; i < paletteLength; i++)
|
||||
{
|
||||
palette[i] = readNextVarInt(cache);
|
||||
}
|
||||
|
||||
// Bit mask covering bitsPerBlock bits
|
||||
// EG, if bitsPerBlock = 5, valueMask = 00011111 in binary
|
||||
uint valueMask = (uint)((1 << bitsPerBlock) - 1);
|
||||
|
||||
ulong[] dataArray = readNextULongArray(cache);
|
||||
|
||||
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());
|
||||
{
|
||||
int blockNumber = (blockY * Chunk.SizeZ + blockZ) * Chunk.SizeX + blockX;
|
||||
|
||||
int startLong = (blockNumber * bitsPerBlock) / 64;
|
||||
int startOffset = (blockNumber * bitsPerBlock) % 64;
|
||||
int endLong = ((blockNumber + 1) * bitsPerBlock - 1) / 64;
|
||||
|
||||
// TODO: In the future a single ushort may not store the entire block id;
|
||||
// the Block code may need to change.
|
||||
ushort blockId;
|
||||
if (startLong == endLong)
|
||||
{
|
||||
blockId = (ushort)((dataArray[startLong] >> startOffset) & valueMask);
|
||||
}
|
||||
else
|
||||
{
|
||||
int endOffset = 64 - startOffset;
|
||||
blockId = (ushort)((dataArray[startLong] >> startOffset | dataArray[endLong] << endOffset) & valueMask);
|
||||
}
|
||||
|
||||
if (usePalette)
|
||||
{
|
||||
// Get the real block ID out of the palette
|
||||
blockId = (ushort)palette[blockId];
|
||||
}
|
||||
|
||||
chunk[blockX, blockY, blockZ] = new Block(blockId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//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)
|
||||
if (this.currentDimension != -1)
|
||||
// Sky light is not sent in the nether
|
||||
readData((Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ) / 2, cache);
|
||||
}
|
||||
}
|
||||
|
||||
//Skip biome metadata
|
||||
if (chunksContinuous)
|
||||
readData(Chunk.SizeX * Chunk.SizeZ, cache);
|
||||
// Don't worry about skipping remaining data since there is no useful data afterwards in 1.9
|
||||
// (plus, it would require parsing the tile entity lists' NBT)
|
||||
}
|
||||
else
|
||||
{
|
||||
// Pre 1.9 chunk format
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -755,9 +857,9 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an unsigned short integer from a cache of bytes and remove it from the cache
|
||||
/// Read an unsigned long integer from a cache of bytes and remove it from the cache
|
||||
/// </summary>
|
||||
/// <returns>The unsigned short integer value</returns>
|
||||
/// <returns>The unsigned long integer value</returns>
|
||||
|
||||
private static ulong readNextULong(List<byte> cache)
|
||||
{
|
||||
|
|
@ -805,6 +907,20 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
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>
|
||||
|
||||
private static 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>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue