mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2025-11-07 17:36:07 +00:00
Refactoring to asynchronous. (partially completed)
This commit is contained in:
parent
7ee08092d4
commit
096ea0c70c
72 changed files with 6033 additions and 5080 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace MinecraftClient.Protocol.Handlers.Forge
|
||||
{
|
||||
|
|
@ -11,16 +12,22 @@ namespace MinecraftClient.Protocol.Handlers.Forge
|
|||
/// <summary>
|
||||
/// Represents an individual forge mod.
|
||||
/// </summary>
|
||||
public class ForgeMod
|
||||
public record ForgeMod
|
||||
{
|
||||
public ForgeMod(String ModID, String Version)
|
||||
public ForgeMod(string? modID, string? version)
|
||||
{
|
||||
this.ModID = ModID;
|
||||
this.Version = Version;
|
||||
ModID = modID;
|
||||
Version = ModMarker = version;
|
||||
}
|
||||
|
||||
public readonly String ModID;
|
||||
public readonly String Version;
|
||||
[JsonPropertyName("modId")]
|
||||
public string? ModID { init; get; }
|
||||
|
||||
[JsonPropertyName("version")]
|
||||
public string? Version { init; get; }
|
||||
|
||||
[JsonPropertyName("modmarker")]
|
||||
public string? ModMarker { init; get; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
|
|
@ -138,5 +145,16 @@ namespace MinecraftClient.Protocol.Handlers.Forge
|
|||
throw new NotImplementedException("FMLVersion '" + fmlVersion + "' not implemented!");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new ForgeInfo from the given data.
|
||||
/// </summary>
|
||||
/// <param name="data">The modinfo JSON tag.</param>
|
||||
/// <param name="fmlVersion">Forge protocol version</param>
|
||||
internal ForgeInfo(ForgeMod[] mods, FMLVersion fmlVersion)
|
||||
{
|
||||
Mods = new(mods);
|
||||
Version = fmlVersion;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using MinecraftClient.Protocol.PacketPipeline;
|
||||
|
||||
namespace MinecraftClient.Protocol.Handlers.packet.s2c
|
||||
{
|
||||
|
|
@ -8,7 +10,7 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c
|
|||
private static int RootIdx;
|
||||
private static CommandNode[] Nodes = Array.Empty<CommandNode>();
|
||||
|
||||
public static void Read(DataTypes dataTypes, Queue<byte> packetData)
|
||||
public static async Task Read(DataTypes dataTypes, PacketStream packetData)
|
||||
{
|
||||
int count = dataTypes.ReadNextVarInt(packetData);
|
||||
Nodes = new CommandNode[count];
|
||||
|
|
@ -23,7 +25,7 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c
|
|||
|
||||
int redirectNode = ((flags & 0x08) > 0) ? dataTypes.ReadNextVarInt(packetData) : -1;
|
||||
|
||||
string? name = ((flags & 0x03) == 1 || (flags & 0x03) == 2) ? dataTypes.ReadNextString(packetData) : null;
|
||||
string? name = ((flags & 0x03) == 1 || (flags & 0x03) == 2) ? (await dataTypes.ReadNextStringAsync(packetData)) : null;
|
||||
|
||||
int paserId = ((flags & 0x03) == 2) ? dataTypes.ReadNextVarInt(packetData) : -1;
|
||||
Paser? paser = null;
|
||||
|
|
@ -50,7 +52,7 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c
|
|||
};
|
||||
}
|
||||
|
||||
string? suggestionsType = ((flags & 0x10) > 0) ? dataTypes.ReadNextString(packetData) : null;
|
||||
string? suggestionsType = ((flags & 0x10) > 0) ? (await dataTypes.ReadNextStringAsync(packetData)) : null;
|
||||
|
||||
Nodes[i] = new(flags, childs, redirectNode, name, paser, suggestionsType);
|
||||
}
|
||||
|
|
@ -158,7 +160,7 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c
|
|||
internal class PaserEmpty : Paser
|
||||
{
|
||||
|
||||
public PaserEmpty(DataTypes dataTypes, Queue<byte> packetData) { }
|
||||
public PaserEmpty(DataTypes dataTypes, PacketStream packetData) { }
|
||||
|
||||
public override bool Check(string text)
|
||||
{
|
||||
|
|
@ -181,7 +183,7 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c
|
|||
private byte Flags;
|
||||
private float Min = float.MinValue, Max = float.MaxValue;
|
||||
|
||||
public PaserFloat(DataTypes dataTypes, Queue<byte> packetData)
|
||||
public PaserFloat(DataTypes dataTypes, PacketStream packetData)
|
||||
{
|
||||
Flags = dataTypes.ReadNextByte(packetData);
|
||||
if ((Flags & 0x01) > 0)
|
||||
|
|
@ -211,7 +213,7 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c
|
|||
private byte Flags;
|
||||
private double Min = double.MinValue, Max = double.MaxValue;
|
||||
|
||||
public PaserDouble(DataTypes dataTypes, Queue<byte> packetData)
|
||||
public PaserDouble(DataTypes dataTypes, PacketStream packetData)
|
||||
{
|
||||
Flags = dataTypes.ReadNextByte(packetData);
|
||||
if ((Flags & 0x01) > 0)
|
||||
|
|
@ -241,7 +243,7 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c
|
|||
private byte Flags;
|
||||
private int Min = int.MinValue, Max = int.MaxValue;
|
||||
|
||||
public PaserInteger(DataTypes dataTypes, Queue<byte> packetData)
|
||||
public PaserInteger(DataTypes dataTypes, PacketStream packetData)
|
||||
{
|
||||
Flags = dataTypes.ReadNextByte(packetData);
|
||||
if ((Flags & 0x01) > 0)
|
||||
|
|
@ -271,7 +273,7 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c
|
|||
private byte Flags;
|
||||
private long Min = long.MinValue, Max = long.MaxValue;
|
||||
|
||||
public PaserLong(DataTypes dataTypes, Queue<byte> packetData)
|
||||
public PaserLong(DataTypes dataTypes, PacketStream packetData)
|
||||
{
|
||||
Flags = dataTypes.ReadNextByte(packetData);
|
||||
if ((Flags & 0x01) > 0)
|
||||
|
|
@ -302,7 +304,7 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c
|
|||
|
||||
private enum StringType { SINGLE_WORD, QUOTABLE_PHRASE, GREEDY_PHRASE };
|
||||
|
||||
public PaserString(DataTypes dataTypes, Queue<byte> packetData)
|
||||
public PaserString(DataTypes dataTypes, PacketStream packetData)
|
||||
{
|
||||
Type = (StringType)dataTypes.ReadNextVarInt(packetData);
|
||||
}
|
||||
|
|
@ -327,7 +329,7 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c
|
|||
{
|
||||
private byte Flags;
|
||||
|
||||
public PaserEntity(DataTypes dataTypes, Queue<byte> packetData)
|
||||
public PaserEntity(DataTypes dataTypes, PacketStream packetData)
|
||||
{
|
||||
Flags = dataTypes.ReadNextByte(packetData);
|
||||
}
|
||||
|
|
@ -351,7 +353,7 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c
|
|||
internal class PaserBlockPos : Paser
|
||||
{
|
||||
|
||||
public PaserBlockPos(DataTypes dataTypes, Queue<byte> packetData) { }
|
||||
public PaserBlockPos(DataTypes dataTypes, PacketStream packetData) { }
|
||||
|
||||
public override bool Check(string text)
|
||||
{
|
||||
|
|
@ -372,7 +374,7 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c
|
|||
internal class PaserColumnPos : Paser
|
||||
{
|
||||
|
||||
public PaserColumnPos(DataTypes dataTypes, Queue<byte> packetData) { }
|
||||
public PaserColumnPos(DataTypes dataTypes, PacketStream packetData) { }
|
||||
|
||||
public override bool Check(string text)
|
||||
{
|
||||
|
|
@ -393,7 +395,7 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c
|
|||
internal class PaserVec3 : Paser
|
||||
{
|
||||
|
||||
public PaserVec3(DataTypes dataTypes, Queue<byte> packetData) { }
|
||||
public PaserVec3(DataTypes dataTypes, PacketStream packetData) { }
|
||||
|
||||
public override bool Check(string text)
|
||||
{
|
||||
|
|
@ -414,7 +416,7 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c
|
|||
internal class PaserVec2 : Paser
|
||||
{
|
||||
|
||||
public PaserVec2(DataTypes dataTypes, Queue<byte> packetData) { }
|
||||
public PaserVec2(DataTypes dataTypes, PacketStream packetData) { }
|
||||
|
||||
public override bool Check(string text)
|
||||
{
|
||||
|
|
@ -435,7 +437,7 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c
|
|||
internal class PaserRotation : Paser
|
||||
{
|
||||
|
||||
public PaserRotation(DataTypes dataTypes, Queue<byte> packetData) { }
|
||||
public PaserRotation(DataTypes dataTypes, PacketStream packetData) { }
|
||||
|
||||
public override bool Check(string text)
|
||||
{
|
||||
|
|
@ -455,7 +457,7 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c
|
|||
|
||||
internal class PaserMessage : Paser
|
||||
{
|
||||
public PaserMessage(DataTypes dataTypes, Queue<byte> packetData) { }
|
||||
public PaserMessage(DataTypes dataTypes, PacketStream packetData) { }
|
||||
|
||||
public override bool Check(string text)
|
||||
{
|
||||
|
|
@ -477,7 +479,7 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c
|
|||
{
|
||||
private byte Flags;
|
||||
|
||||
public PaserScoreHolder(DataTypes dataTypes, Queue<byte> packetData)
|
||||
public PaserScoreHolder(DataTypes dataTypes, PacketStream packetData)
|
||||
{
|
||||
Flags = dataTypes.ReadNextByte(packetData);
|
||||
}
|
||||
|
|
@ -502,7 +504,7 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c
|
|||
{
|
||||
private bool Decimals;
|
||||
|
||||
public PaserRange(DataTypes dataTypes, Queue<byte> packetData)
|
||||
public PaserRange(DataTypes dataTypes, PacketStream packetData)
|
||||
{
|
||||
Decimals = dataTypes.ReadNextBool(packetData);
|
||||
}
|
||||
|
|
@ -527,9 +529,11 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c
|
|||
{
|
||||
private string Registry;
|
||||
|
||||
public PaserResourceOrTag(DataTypes dataTypes, Queue<byte> packetData)
|
||||
public PaserResourceOrTag(DataTypes dataTypes, PacketStream packetData)
|
||||
{
|
||||
Registry = dataTypes.ReadNextString(packetData);
|
||||
var task = dataTypes.ReadNextStringAsync(packetData);
|
||||
task.Wait();
|
||||
Registry = task.Result;
|
||||
}
|
||||
|
||||
public override bool Check(string text)
|
||||
|
|
@ -552,9 +556,11 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c
|
|||
{
|
||||
private string Registry;
|
||||
|
||||
public PaserResource(DataTypes dataTypes, Queue<byte> packetData)
|
||||
public PaserResource(DataTypes dataTypes, PacketStream packetData)
|
||||
{
|
||||
Registry = dataTypes.ReadNextString(packetData);
|
||||
var task = dataTypes.ReadNextStringAsync(packetData);
|
||||
task.Wait();
|
||||
Registry = task.Result;
|
||||
}
|
||||
|
||||
public override bool Check(string text)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -3,9 +3,12 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MinecraftClient.Protocol.Handlers.Forge;
|
||||
using MinecraftClient.Protocol.Message;
|
||||
using MinecraftClient.Protocol.PacketPipeline;
|
||||
using MinecraftClient.Scripting;
|
||||
using static MinecraftClient.Protocol.Handlers.Protocol18Handler;
|
||||
|
||||
namespace MinecraftClient.Protocol.Handlers
|
||||
{
|
||||
|
|
@ -54,23 +57,23 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
/// Completes the Minecraft Forge handshake (Forge Protocol version 1: FML)
|
||||
/// </summary>
|
||||
/// <returns>Whether the handshake was successful.</returns>
|
||||
public bool CompleteForgeHandshake()
|
||||
public async Task<bool> CompleteForgeHandshake(SocketWrapper socketWrapper)
|
||||
{
|
||||
if (ForgeEnabled() && forgeInfo!.Version == FMLVersion.FML)
|
||||
{
|
||||
while (fmlHandshakeState != FMLHandshakeClientState.DONE)
|
||||
{
|
||||
(int packetID, Queue<byte> packetData) = protocol18.ReadNextPacket();
|
||||
(int packetID, PacketStream packetStream) = await socketWrapper.GetNextPacket(handleCompress: true);
|
||||
|
||||
if (packetID == 0x40) // Disconnect
|
||||
{
|
||||
mcHandler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, ChatParser.ParseText(dataTypes.ReadNextString(packetData)));
|
||||
mcHandler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, ChatParser.ParseText(await dataTypes.ReadNextStringAsync(packetStream)));
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Send back regular packet to the vanilla protocol handler
|
||||
protocol18.HandlePacket(packetID, packetData);
|
||||
await protocol18.HandlePacket(packetID, packetStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -82,7 +85,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
/// </summary>
|
||||
/// <param name="packetData">Packet data to read from</param>
|
||||
/// <returns>Length from packet data</returns>
|
||||
public int ReadNextVarShort(Queue<byte> packetData)
|
||||
public int ReadNextVarShort(PacketStream packetData)
|
||||
{
|
||||
if (ForgeEnabled())
|
||||
{
|
||||
|
|
@ -103,10 +106,11 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
/// <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, Queue<byte> packetData, ref int currentDimension)
|
||||
public async Task<Tuple<bool, int>> HandlePluginMessage(string channel, byte[] packetDataArr, int currentDimension)
|
||||
{
|
||||
if (ForgeEnabled() && forgeInfo!.Version == FMLVersion.FML && fmlHandshakeState != FMLHandshakeClientState.DONE)
|
||||
{
|
||||
Queue<byte> packetData = new(packetDataArr);
|
||||
if (channel == "FML|HS")
|
||||
{
|
||||
FMLHandshakeDiscriminator discriminator = (FMLHandshakeDiscriminator)dataTypes.ReadNextByte(packetData);
|
||||
|
|
@ -114,21 +118,21 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
if (discriminator == FMLHandshakeDiscriminator.HandshakeReset)
|
||||
{
|
||||
fmlHandshakeState = FMLHandshakeClientState.START;
|
||||
return true;
|
||||
return new(true, currentDimension);
|
||||
}
|
||||
|
||||
switch (fmlHandshakeState)
|
||||
{
|
||||
case FMLHandshakeClientState.START:
|
||||
if (discriminator != FMLHandshakeDiscriminator.ServerHello)
|
||||
return false;
|
||||
return new(false, currentDimension);
|
||||
|
||||
// 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)));
|
||||
await protocol18.SendPluginChannelPacket("REGISTER", Encoding.UTF8.GetBytes(string.Join("\0", channels)));
|
||||
|
||||
byte fmlProtocolVersion = dataTypes.ReadNextByte(packetData);
|
||||
|
||||
|
|
@ -139,7 +143,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
currentDimension = dataTypes.ReadNextInt(packetData);
|
||||
|
||||
// Tell the server we're running the same version.
|
||||
SendForgeHandshakePacket(FMLHandshakeDiscriminator.ClientHello, new byte[] { fmlProtocolVersion });
|
||||
await SendForgeHandshakePacket(FMLHandshakeDiscriminator.ClientHello, new byte[] { fmlProtocolVersion });
|
||||
|
||||
// Then tell the server that we're running the same mods.
|
||||
if (Settings.Config.Logging.DebugMessages)
|
||||
|
|
@ -148,17 +152,17 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
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));
|
||||
mods[i] = dataTypes.ConcatBytes(dataTypes.GetString(mod.ModID!), dataTypes.GetString(mod.Version ?? mod.ModMarker ?? string.Empty));
|
||||
}
|
||||
SendForgeHandshakePacket(FMLHandshakeDiscriminator.ModList,
|
||||
await SendForgeHandshakePacket(FMLHandshakeDiscriminator.ModList,
|
||||
dataTypes.ConcatBytes(dataTypes.GetVarInt(forgeInfo.Mods.Count), dataTypes.ConcatBytes(mods)));
|
||||
|
||||
fmlHandshakeState = FMLHandshakeClientState.WAITINGSERVERDATA;
|
||||
|
||||
return true;
|
||||
return new(true, currentDimension);
|
||||
case FMLHandshakeClientState.WAITINGSERVERDATA:
|
||||
if (discriminator != FMLHandshakeDiscriminator.ModList)
|
||||
return false;
|
||||
return new(false, currentDimension);
|
||||
|
||||
Thread.Sleep(2000);
|
||||
|
||||
|
|
@ -167,16 +171,16 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
// 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,
|
||||
await SendForgeHandshakePacket(FMLHandshakeDiscriminator.HandshakeAck,
|
||||
new byte[] { (byte)FMLHandshakeClientState.WAITINGSERVERDATA });
|
||||
|
||||
fmlHandshakeState = FMLHandshakeClientState.WAITINGSERVERCOMPLETE;
|
||||
return false;
|
||||
return new(false, currentDimension);
|
||||
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;
|
||||
return new(false, currentDimension);
|
||||
|
||||
if (protocolversion < Protocol18Handler.MC_1_8_Version)
|
||||
{
|
||||
|
|
@ -202,34 +206,34 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
fmlHandshakeState = FMLHandshakeClientState.PENDINGCOMPLETE;
|
||||
}
|
||||
|
||||
return false;
|
||||
return new(false, currentDimension);
|
||||
case FMLHandshakeClientState.PENDINGCOMPLETE:
|
||||
// The server will ask us to accept the registries.
|
||||
// Just say yes.
|
||||
if (discriminator != FMLHandshakeDiscriminator.HandshakeAck)
|
||||
return false;
|
||||
return new(false, currentDimension);
|
||||
if (Settings.Config.Logging.DebugMessages)
|
||||
ConsoleIO.WriteLineFormatted("§8" + Translations.forge_accept_registry, acceptnewlines: true);
|
||||
SendForgeHandshakePacket(FMLHandshakeDiscriminator.HandshakeAck,
|
||||
await SendForgeHandshakePacket(FMLHandshakeDiscriminator.HandshakeAck,
|
||||
new byte[] { (byte)FMLHandshakeClientState.PENDINGCOMPLETE });
|
||||
fmlHandshakeState = FMLHandshakeClientState.COMPLETE;
|
||||
|
||||
return true;
|
||||
return new(true, currentDimension);
|
||||
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,
|
||||
await SendForgeHandshakePacket(FMLHandshakeDiscriminator.HandshakeAck,
|
||||
new byte[] { (byte)FMLHandshakeClientState.COMPLETE });
|
||||
if (Settings.Config.Logging.DebugMessages)
|
||||
ConsoleIO.WriteLine(Translations.forge_complete);
|
||||
fmlHandshakeState = FMLHandshakeClientState.DONE;
|
||||
return true;
|
||||
return new(true, currentDimension);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return new(false, currentDimension);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -239,8 +243,9 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
/// <param name="packetData">Plugin message data</param>
|
||||
/// <param name="responseData">Response data to return to server</param>
|
||||
/// <returns>TRUE/FALSE depending on whether the packet was understood or not</returns>
|
||||
public bool HandleLoginPluginRequest(string channel, Queue<byte> packetData, ref List<byte> responseData)
|
||||
public async Task<Tuple<bool, List<byte>>> HandleLoginPluginRequest(string channel, PacketStream packetData)
|
||||
{
|
||||
List<byte> responseData = new();
|
||||
if (ForgeEnabled() && forgeInfo!.Version == FMLVersion.FML2 && channel == "fml:loginwrapper")
|
||||
{
|
||||
// Forge Handshake handler source code used to implement the FML2 packets:
|
||||
|
|
@ -278,8 +283,8 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
// The content of each message is mapped into a class inside FMLHandshakeMessages.java
|
||||
// FMLHandshakeHandler will then process the packet, e.g. handleServerModListOnClient() for Server Mod List.
|
||||
|
||||
string fmlChannel = dataTypes.ReadNextString(packetData);
|
||||
dataTypes.ReadNextVarInt(packetData); // Packet length
|
||||
string fmlChannel = await dataTypes.ReadNextStringAsync(packetData);
|
||||
dataTypes.SkipNextVarInt(packetData); // Packet length
|
||||
int packetID = dataTypes.ReadNextVarInt(packetData);
|
||||
|
||||
if (fmlChannel == "fml:handshake")
|
||||
|
|
@ -308,17 +313,17 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
List<string> mods = new();
|
||||
int modCount = dataTypes.ReadNextVarInt(packetData);
|
||||
for (int i = 0; i < modCount; i++)
|
||||
mods.Add(dataTypes.ReadNextString(packetData));
|
||||
mods.Add(await dataTypes.ReadNextStringAsync(packetData));
|
||||
|
||||
Dictionary<string, string> channels = new();
|
||||
int channelCount = dataTypes.ReadNextVarInt(packetData);
|
||||
for (int i = 0; i < channelCount; i++)
|
||||
channels.Add(dataTypes.ReadNextString(packetData), dataTypes.ReadNextString(packetData));
|
||||
channels.Add(await dataTypes.ReadNextStringAsync(packetData), await dataTypes.ReadNextStringAsync(packetData));
|
||||
|
||||
List<string> registries = new();
|
||||
int registryCount = dataTypes.ReadNextVarInt(packetData);
|
||||
for (int i = 0; i < registryCount; i++)
|
||||
registries.Add(dataTypes.ReadNextString(packetData));
|
||||
registries.Add(await dataTypes.ReadNextStringAsync(packetData));
|
||||
|
||||
// Server Mod List Reply: FMLHandshakeMessages.java > C2SModListReply > encode()
|
||||
//
|
||||
|
|
@ -372,7 +377,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
|
||||
if (Settings.Config.Logging.DebugMessages)
|
||||
{
|
||||
string registryName = dataTypes.ReadNextString(packetData);
|
||||
string registryName = await dataTypes.ReadNextStringAsync(packetData);
|
||||
ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.forge_fml2_registry, registryName));
|
||||
}
|
||||
|
||||
|
|
@ -391,7 +396,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
|
||||
if (Settings.Config.Logging.DebugMessages)
|
||||
{
|
||||
string configName = dataTypes.ReadNextString(packetData);
|
||||
string configName = await dataTypes.ReadNextStringAsync(packetData);
|
||||
ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.forge_fml2_config, configName));
|
||||
}
|
||||
|
||||
|
|
@ -408,11 +413,10 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
if (fmlResponseReady)
|
||||
{
|
||||
// Wrap our FML packet into a LoginPluginResponse payload
|
||||
responseData.Clear();
|
||||
responseData.AddRange(dataTypes.GetString(fmlChannel));
|
||||
responseData.AddRange(dataTypes.GetVarInt(fmlResponsePacket.Count));
|
||||
responseData.AddRange(fmlResponsePacket);
|
||||
return true;
|
||||
return new(true, responseData);
|
||||
}
|
||||
}
|
||||
else if (Settings.Config.Logging.DebugMessages)
|
||||
|
|
@ -420,7 +424,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.forge_fml2_unknown_channel, fmlChannel));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return new(false, responseData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -428,9 +432,9 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
/// </summary>
|
||||
/// <param name="discriminator">Discriminator to use.</param>
|
||||
/// <param name="data">packet Data</param>
|
||||
private void SendForgeHandshakePacket(FMLHandshakeDiscriminator discriminator, byte[] data)
|
||||
private async Task SendForgeHandshakePacket(FMLHandshakeDiscriminator discriminator, byte[] data)
|
||||
{
|
||||
protocol18.SendPluginChannelPacket("FML|HS", dataTypes.ConcatBytes(new byte[] { (byte)discriminator }, data));
|
||||
await protocol18.SendPluginChannelPacket("FML|HS", dataTypes.ConcatBytes(new byte[] { (byte)discriminator }, data));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -439,10 +443,10 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
/// <param name="jsonData">JSON data returned by the server</param>
|
||||
/// <param name="forgeInfo">ForgeInfo to populate</param>
|
||||
/// <returns>True if the server is running Forge</returns>
|
||||
public static bool ServerInfoCheckForge(Json.JSONData jsonData, ref ForgeInfo? forgeInfo)
|
||||
public static bool ServerInfoCheckForge(PingResult jsonData, ref ForgeInfo? forgeInfo)
|
||||
{
|
||||
return ServerInfoCheckForgeSub(jsonData, ref forgeInfo, FMLVersion.FML) // MC 1.12 and lower
|
||||
|| ServerInfoCheckForgeSub(jsonData, ref forgeInfo, FMLVersion.FML2); // MC 1.13 and greater
|
||||
return ServerInfoCheckForgeSubFML1(jsonData, ref forgeInfo) // MC 1.12 and lower
|
||||
|| ServerInfoCheckForgeSubFML2(jsonData, ref forgeInfo); // MC 1.13 and greater
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -474,38 +478,21 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
/// </summary>
|
||||
/// <param name="jsonData">JSON data returned by the server</param>
|
||||
/// <param name="forgeInfo">ForgeInfo to populate</param>
|
||||
/// <param name="fmlVersion">Forge protocol version</param>
|
||||
/// <returns>True if the server is running Forge</returns>
|
||||
private static bool ServerInfoCheckForgeSub(Json.JSONData jsonData, ref ForgeInfo? forgeInfo, FMLVersion fmlVersion)
|
||||
private static bool ServerInfoCheckForgeSubFML1(PingResult jsonData, ref ForgeInfo? forgeInfo)
|
||||
{
|
||||
string forgeDataTag;
|
||||
string versionField;
|
||||
string versionString;
|
||||
|
||||
switch (fmlVersion)
|
||||
if (jsonData.modinfo != null)
|
||||
{
|
||||
case FMLVersion.FML:
|
||||
forgeDataTag = "modinfo";
|
||||
versionField = "type";
|
||||
versionString = "FML";
|
||||
break;
|
||||
case FMLVersion.FML2:
|
||||
forgeDataTag = "forgeData";
|
||||
versionField = "fmlNetworkVersion";
|
||||
versionString = "2";
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException("FMLVersion '" + fmlVersion + "' not implemented!");
|
||||
}
|
||||
|
||||
if (jsonData.Properties.ContainsKey(forgeDataTag) && jsonData.Properties[forgeDataTag].Type == Json.JSONData.DataType.Object)
|
||||
{
|
||||
Json.JSONData modData = jsonData.Properties[forgeDataTag];
|
||||
if (modData.Properties.ContainsKey(versionField) && modData.Properties[versionField].StringValue == versionString)
|
||||
if (jsonData.modinfo.type == "FML")
|
||||
{
|
||||
forgeInfo = new ForgeInfo(modData, fmlVersion);
|
||||
if (forgeInfo.Mods.Any())
|
||||
if (jsonData.modinfo.modList == null || jsonData.modinfo.modList.Length == 0)
|
||||
{
|
||||
forgeInfo = null;
|
||||
ConsoleIO.WriteLineFormatted("§8" + Translations.forge_no_mod, acceptnewlines: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
forgeInfo = new ForgeInfo(jsonData.modinfo.modList, FMLVersion.FML);
|
||||
ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.forge_with_mod, forgeInfo.Mods.Count));
|
||||
if (Settings.Config.Logging.DebugMessages)
|
||||
{
|
||||
|
|
@ -515,10 +502,39 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Server Info: Check for For Forge on a Minecraft server Ping result (Handles FML and FML2
|
||||
/// </summary>
|
||||
/// <param name="jsonData">JSON data returned by the server</param>
|
||||
/// <param name="forgeInfo">ForgeInfo to populate</param>
|
||||
/// <returns>True if the server is running Forge</returns>
|
||||
private static bool ServerInfoCheckForgeSubFML2(PingResult jsonData, ref ForgeInfo? forgeInfo)
|
||||
{
|
||||
if (jsonData.forgeData != null)
|
||||
{
|
||||
if (jsonData.forgeData.fmlNetworkVersion == "2")
|
||||
{
|
||||
if (jsonData.forgeData.mods == null || jsonData.forgeData.mods.Length == 0)
|
||||
{
|
||||
forgeInfo = null;
|
||||
ConsoleIO.WriteLineFormatted("§8" + Translations.forge_no_mod, acceptnewlines: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
ConsoleIO.WriteLineFormatted("§8" + Translations.forge_no_mod, acceptnewlines: true);
|
||||
forgeInfo = null;
|
||||
forgeInfo = new ForgeInfo(jsonData.forgeData.mods, FMLVersion.FML2);
|
||||
ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.forge_with_mod, forgeInfo.Mods.Count));
|
||||
if (Settings.Config.Logging.DebugMessages)
|
||||
{
|
||||
ConsoleIO.WriteLineFormatted("§8" + Translations.forge_mod_list, acceptnewlines: true);
|
||||
foreach (ForgeInfo.ForgeMod mod in forgeInfo.Mods)
|
||||
ConsoleIO.WriteLineFormatted("§8 " + mod.ToString());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,9 +3,11 @@ using System.Collections.Generic;
|
|||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
//using System.Linq;
|
||||
//using System.Text;
|
||||
using MinecraftClient.Mapping;
|
||||
using MinecraftClient.Protocol.PacketPipeline;
|
||||
|
||||
namespace MinecraftClient.Protocol.Handlers
|
||||
{
|
||||
|
|
@ -33,21 +35,21 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
/// <summary>
|
||||
/// Reading the "Block states" field: consists of 4096 entries, representing all the blocks in the chunk section.
|
||||
/// </summary>
|
||||
/// <param name="cache">Cache for reading data</param>
|
||||
/// <param name="stream">Cache for reading data</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||
private Chunk? ReadBlockStatesField(Queue<byte> cache)
|
||||
private async Task<Chunk?> ReadBlockStatesFieldAsync(PacketStream stream)
|
||||
{
|
||||
// read Block states (Type: Paletted Container)
|
||||
byte bitsPerEntry = dataTypes.ReadNextByte(cache);
|
||||
byte bitsPerEntry = await dataTypes.ReadNextByteAsync(stream);
|
||||
|
||||
// 1.18(1.18.1) add a pattle named "Single valued" to replace the vertical strip bitmask in the old
|
||||
if (bitsPerEntry == 0 && protocolversion >= Protocol18Handler.MC_1_18_1_Version)
|
||||
{
|
||||
// Palettes: Single valued - 1.18(1.18.1) and above
|
||||
ushort blockId = (ushort)dataTypes.ReadNextVarInt(cache);
|
||||
ushort blockId = (ushort)(await dataTypes.ReadNextVarIntAsync(stream));
|
||||
Block block = new(blockId);
|
||||
|
||||
dataTypes.SkipNextVarInt(cache); // Data Array Length will be zero
|
||||
dataTypes.SkipNextVarInt(stream); // Data Array Length will be zero
|
||||
|
||||
// Empty chunks will not be stored
|
||||
if (block.Type == Material.Air)
|
||||
|
|
@ -73,16 +75,16 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
// EG, if bitsPerEntry = 5, valueMask = 00011111 in binary
|
||||
uint valueMask = (uint)((1 << bitsPerEntry) - 1);
|
||||
|
||||
int paletteLength = usePalette ? dataTypes.ReadNextVarInt(cache) : 0; // Assume zero when length is absent
|
||||
int paletteLength = usePalette ? await dataTypes.ReadNextVarIntAsync(stream) : 0; // Assume zero when length is absent
|
||||
|
||||
Span<uint> palette = paletteLength < 256 ? stackalloc uint[paletteLength] : new uint[paletteLength];
|
||||
uint[] palette = new uint[paletteLength];
|
||||
for (int i = 0; i < paletteLength; i++)
|
||||
palette[i] = (uint)dataTypes.ReadNextVarInt(cache);
|
||||
palette[i] = (uint)(await dataTypes.ReadNextVarIntAsync(stream));
|
||||
|
||||
//// Block IDs are packed in the array of 64-bits integers
|
||||
dataTypes.SkipNextVarInt(cache); // Entry length
|
||||
Span<byte> entryDataByte = stackalloc byte[8];
|
||||
Span<long> entryDataLong = MemoryMarshal.Cast<byte, long>(entryDataByte); // Faster than MemoryMarshal.Read<long>
|
||||
dataTypes.SkipNextVarInt(stream); // Entry length
|
||||
|
||||
long entryData = 0;
|
||||
|
||||
Chunk chunk = new();
|
||||
int startOffset = 64; // Read the first data immediately
|
||||
|
|
@ -101,10 +103,10 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
|
||||
// When overlapping, move forward to the beginning of the next Long
|
||||
startOffset = 0;
|
||||
dataTypes.ReadDataReverse(cache, entryDataByte); // read long
|
||||
entryData = await dataTypes.ReadNextLongAsync(stream);
|
||||
}
|
||||
|
||||
uint blockId = (uint)(entryDataLong[0] >> startOffset) & valueMask;
|
||||
uint blockId = (uint)(entryData >> startOffset) & valueMask;
|
||||
|
||||
// Map small IDs to actual larger block IDs
|
||||
if (usePalette)
|
||||
|
|
@ -141,10 +143,10 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
/// <param name="chunkX">Chunk X location</param>
|
||||
/// <param name="chunkZ">Chunk Z location</param>
|
||||
/// <param name="verticalStripBitmask">Chunk mask for reading data, store in bitset, used in 1.17 and 1.17.1</param>
|
||||
/// <param name="cache">Cache for reading chunk data</param>
|
||||
/// <param name="stream">Cache for reading chunk data</param>
|
||||
/// <param name="cancellationToken">token to cancel the task</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||
public void ProcessChunkColumnData(int chunkX, int chunkZ, ulong[]? verticalStripBitmask, Queue<byte> cache)
|
||||
public async Task ProcessChunkColumnData(int chunkX, int chunkZ, ulong[]? verticalStripBitmask, PacketStream stream)
|
||||
{
|
||||
World world = handler.GetWorld();
|
||||
|
||||
|
|
@ -181,10 +183,10 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
((verticalStripBitmask![chunkY / 64] & (1UL << (chunkY % 64))) != 0))
|
||||
{
|
||||
// Non-air block count inside chunk section, for lighting purposes
|
||||
int blockCnt = dataTypes.ReadNextShort(cache);
|
||||
int blockCnt = await dataTypes.ReadNextShortAsync(stream);
|
||||
|
||||
// Read Block states (Type: Paletted Container)
|
||||
Chunk? chunk = ReadBlockStatesField(cache);
|
||||
Chunk? chunk = await ReadBlockStatesFieldAsync(stream);
|
||||
|
||||
//We have our chunk, save the chunk into the world
|
||||
world.StoreChunk(chunkX, chunkY, chunkZ, chunkColumnSize, chunk, chunkY == lastChunkY);
|
||||
|
|
@ -192,23 +194,23 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
// Skip Read Biomes (Type: Paletted Container) - 1.18(1.18.1) and above
|
||||
if (protocolversion >= Protocol18Handler.MC_1_18_1_Version)
|
||||
{
|
||||
byte bitsPerEntryBiome = dataTypes.ReadNextByte(cache); // Bits Per Entry
|
||||
byte bitsPerEntryBiome = await dataTypes.ReadNextByteAsync(stream); // Bits Per Entry
|
||||
if (bitsPerEntryBiome == 0)
|
||||
{
|
||||
dataTypes.SkipNextVarInt(cache); // Value
|
||||
dataTypes.SkipNextVarInt(cache); // Data Array Length
|
||||
dataTypes.SkipNextVarInt(stream); // Value
|
||||
dataTypes.SkipNextVarInt(stream); // Data Array Length
|
||||
// Data Array must be empty
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bitsPerEntryBiome <= 3)
|
||||
{
|
||||
int paletteLength = dataTypes.ReadNextVarInt(cache); // Palette Length
|
||||
int paletteLength = await dataTypes.ReadNextVarIntAsync(stream); // Palette Length
|
||||
for (int i = 0; i < paletteLength; i++)
|
||||
dataTypes.SkipNextVarInt(cache); // Palette
|
||||
dataTypes.SkipNextVarInt(stream); // Palette
|
||||
}
|
||||
int dataArrayLength = dataTypes.ReadNextVarInt(cache); // Data Array Length
|
||||
dataTypes.DropData(dataArrayLength * 8, cache); // Data Array
|
||||
int dataArrayLength = await dataTypes.ReadNextVarIntAsync(stream); // Data Array Length
|
||||
await dataTypes.DropDataAsync(dataArrayLength * 8, stream); // Data Array
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -228,10 +230,10 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
/// <param name="hasSkyLight">Contains skylight info</param>
|
||||
/// <param name="chunksContinuous">Are the chunk continuous</param>
|
||||
/// <param name="currentDimension">Current dimension type (0 = overworld)</param>
|
||||
/// <param name="cache">Cache for reading chunk data</param>
|
||||
/// <param name="stream">Cache for reading chunk data</param>
|
||||
/// <param name="cancellationToken">token to cancel the task</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||
public void ProcessChunkColumnData(int chunkX, int chunkZ, ushort chunkMask, ushort chunkMask2, bool hasSkyLight, bool chunksContinuous, int currentDimension, Queue<byte> cache)
|
||||
public async Task ProcessChunkColumnData(int chunkX, int chunkZ, ushort chunkMask, ushort chunkMask2, bool hasSkyLight, bool chunksContinuous, int currentDimension, PacketStream stream)
|
||||
{
|
||||
World world = handler.GetWorld();
|
||||
|
||||
|
|
@ -247,9 +249,9 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
{
|
||||
// 1.14 and above Non-air block count inside chunk section, for lighting purposes
|
||||
if (protocolversion >= Protocol18Handler.MC_1_14_Version)
|
||||
dataTypes.ReadNextShort(cache);
|
||||
await dataTypes.SkipNextShortAsync(stream);
|
||||
|
||||
byte bitsPerBlock = dataTypes.ReadNextByte(cache);
|
||||
byte bitsPerBlock = await dataTypes.ReadNextByteAsync(stream);
|
||||
bool usePalette = (bitsPerBlock <= 8);
|
||||
|
||||
// Vanilla Minecraft will use at least 4 bits per block
|
||||
|
|
@ -260,12 +262,12 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
// is not used, MC 1.13+ does not send the field at all in this case
|
||||
int paletteLength = 0; // Assume zero when length is absent
|
||||
if (usePalette || protocolversion < Protocol18Handler.MC_1_13_Version)
|
||||
paletteLength = dataTypes.ReadNextVarInt(cache);
|
||||
paletteLength = await dataTypes.ReadNextVarIntAsync(stream);
|
||||
|
||||
int[] palette = new int[paletteLength];
|
||||
for (int i = 0; i < paletteLength; i++)
|
||||
{
|
||||
palette[i] = dataTypes.ReadNextVarInt(cache);
|
||||
palette[i] = await dataTypes.ReadNextVarIntAsync(stream);
|
||||
}
|
||||
|
||||
// Bit mask covering bitsPerBlock bits
|
||||
|
|
@ -273,7 +275,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
uint valueMask = (uint)((1 << bitsPerBlock) - 1);
|
||||
|
||||
// Block IDs are packed in the array of 64-bits integers
|
||||
ulong[] dataArray = dataTypes.ReadNextULongArray(cache);
|
||||
ulong[] dataArray = await dataTypes.ReadNextULongArrayAsync(stream);
|
||||
|
||||
Chunk chunk = new();
|
||||
|
||||
|
|
@ -358,19 +360,19 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
}
|
||||
}
|
||||
|
||||
//We have our chunk, save the chunk into the world
|
||||
// We have our chunk, save the chunk into the world
|
||||
world.StoreChunk(chunkX, chunkY, chunkZ, chunkColumnSize, chunk, chunkY == maxChunkY);
|
||||
|
||||
//Pre-1.14 Lighting data
|
||||
// Pre-1.14 Lighting data
|
||||
if (protocolversion < Protocol18Handler.MC_1_14_Version)
|
||||
{
|
||||
//Skip block light
|
||||
dataTypes.DropData((Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ) / 2, cache);
|
||||
// Skip block light
|
||||
await dataTypes.DropDataAsync((Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ) / 2, stream);
|
||||
|
||||
//Skip sky light
|
||||
// Skip sky light
|
||||
if (currentDimension == 0)
|
||||
// Sky light is not sent in the nether or the end
|
||||
dataTypes.DropData((Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ) / 2, cache);
|
||||
await dataTypes.DropDataAsync((Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ) / 2, stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -383,15 +385,12 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
// 1.8 chunk format
|
||||
if (chunksContinuous && chunkMask == 0)
|
||||
{
|
||||
//Unload the entire chunk column
|
||||
handler.InvokeOnMainThread(() =>
|
||||
{
|
||||
world[chunkX, chunkZ] = null;
|
||||
});
|
||||
// Unload the entire chunk column
|
||||
world[chunkX, chunkZ] = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Load chunk data from the server
|
||||
// Load chunk data from the server
|
||||
int maxChunkY = sizeof(int) * 8 - 1 - BitOperations.LeadingZeroCount(chunkMask);
|
||||
for (int chunkY = 0; chunkY <= maxChunkY; chunkY++)
|
||||
{
|
||||
|
|
@ -399,35 +398,34 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
{
|
||||
Chunk chunk = new();
|
||||
|
||||
//Read chunk data, all at once for performance reasons, and build the chunk object
|
||||
Queue<ushort> queue = new(dataTypes.ReadNextUShortsLittleEndian(Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ, cache));
|
||||
// Read chunk data, all at once for performance reasons, and build the chunk object
|
||||
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.SetWithoutCheck(blockX, blockY, blockZ, new Block(queue.Dequeue()));
|
||||
chunk.SetWithoutCheck(blockX, blockY, blockZ, new Block(await dataTypes.ReadNextUShortAsync(stream)));
|
||||
|
||||
//We have our chunk, save the chunk into the world
|
||||
// We have our chunk, save the chunk into the world
|
||||
world.StoreChunk(chunkX, chunkY, chunkZ, chunkColumnSize, chunk, chunkY == maxChunkY);
|
||||
}
|
||||
}
|
||||
|
||||
//Skip light information
|
||||
// Skip light information
|
||||
for (int chunkY = 0; chunkY < chunkColumnSize; chunkY++)
|
||||
{
|
||||
if ((chunkMask & (1 << chunkY)) != 0)
|
||||
{
|
||||
//Skip block light
|
||||
dataTypes.DropData((Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ) / 2, cache);
|
||||
// Skip block light
|
||||
await dataTypes.DropDataAsync((Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ) / 2, stream);
|
||||
|
||||
//Skip sky light
|
||||
// Skip sky light
|
||||
if (hasSkyLight)
|
||||
dataTypes.DropData((Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ) / 2, cache);
|
||||
await dataTypes.DropDataAsync((Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ) / 2, stream);
|
||||
}
|
||||
}
|
||||
|
||||
//Skip biome metadata
|
||||
// Skip biome metadata
|
||||
if (chunksContinuous)
|
||||
dataTypes.DropData(Chunk.SizeX * Chunk.SizeZ, cache);
|
||||
await dataTypes.DropDataAsync(Chunk.SizeX * Chunk.SizeZ, stream);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -435,15 +433,12 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
// 1.7 chunk format
|
||||
if (chunksContinuous && chunkMask == 0)
|
||||
{
|
||||
//Unload the entire chunk column
|
||||
handler.InvokeOnMainThread(() =>
|
||||
{
|
||||
world[chunkX, chunkZ] = null;
|
||||
});
|
||||
// Unload the entire chunk column
|
||||
world[chunkX, chunkZ] = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Count chunk sections
|
||||
// Count chunk sections
|
||||
int sectionCount = 0;
|
||||
int addDataSectionCount = 0;
|
||||
for (int chunkY = 0; chunkY < chunkColumnSize; chunkY++)
|
||||
|
|
@ -454,10 +449,10 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
addDataSectionCount++;
|
||||
}
|
||||
|
||||
//Read chunk data, unpacking 4-bit values into 8-bit values for block metadata
|
||||
Queue<byte> blockTypes = new(dataTypes.ReadData(Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ * sectionCount, cache));
|
||||
// Read chunk data, unpacking 4-bit values into 8-bit values for block metadata
|
||||
Queue<byte> blockTypes = new(await dataTypes.ReadDataAsync(Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ * sectionCount, stream));
|
||||
Queue<byte> blockMeta = new();
|
||||
foreach (byte packed in dataTypes.ReadData((Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ * sectionCount) / 2, cache))
|
||||
foreach (byte packed in await dataTypes.ReadDataAsync((Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ * sectionCount) / 2, stream))
|
||||
{
|
||||
byte hig = (byte)(packed >> 4);
|
||||
byte low = (byte)(packed & (byte)0x0F);
|
||||
|
|
@ -465,15 +460,15 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
blockMeta.Enqueue(low);
|
||||
}
|
||||
|
||||
//Skip data we don't need
|
||||
dataTypes.DropData((Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ * sectionCount) / 2, cache); //Block light
|
||||
// Skip data we don't need
|
||||
await dataTypes.DropDataAsync((Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ * sectionCount) / 2, stream); //Block light
|
||||
if (hasSkyLight)
|
||||
dataTypes.DropData((Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ * sectionCount) / 2, cache); //Sky light
|
||||
dataTypes.DropData((Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ * addDataSectionCount) / 2, cache); //BlockAdd
|
||||
await dataTypes.DropDataAsync((Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ * sectionCount) / 2, stream); //Sky light
|
||||
await dataTypes.DropDataAsync((Chunk.SizeX * Chunk.SizeY * Chunk.SizeZ * addDataSectionCount) / 2, stream); //BlockAdd
|
||||
if (chunksContinuous)
|
||||
dataTypes.DropData(Chunk.SizeX * Chunk.SizeZ, cache); //Biomes
|
||||
await dataTypes.DropDataAsync(Chunk.SizeX * Chunk.SizeZ, stream); //Biomes
|
||||
|
||||
//Load chunk data
|
||||
// Load chunk data
|
||||
int maxChunkY = sizeof(int) * 8 - 1 - BitOperations.LeadingZeroCount(chunkMask);
|
||||
for (int chunkY = 0; chunkY <= maxChunkY; chunkY++)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,114 +0,0 @@
|
|||
using System;
|
||||
using System.Net.Sockets;
|
||||
using MinecraftClient.Crypto;
|
||||
|
||||
namespace MinecraftClient.Protocol.Handlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Wrapper for handling unencrypted & encrypted socket
|
||||
/// </summary>
|
||||
class SocketWrapper
|
||||
{
|
||||
readonly TcpClient c;
|
||||
AesCfb8Stream? s;
|
||||
bool encrypted = false;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize a new SocketWrapper
|
||||
/// </summary>
|
||||
/// <param name="client">TcpClient connected to the server</param>
|
||||
public SocketWrapper(TcpClient client)
|
||||
{
|
||||
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!?");
|
||||
s = new AesCfb8Stream(c.GetStream(), secretKey);
|
||||
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 Array.Empty<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) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
using Ionic.Zlib;
|
||||
|
||||
namespace MinecraftClient.Protocol.Handlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Quick Zlib compression handling for network packet compression.
|
||||
/// Note: Underlying compression handling is taken from the DotNetZip Library.
|
||||
/// This library is open source and provided under the Microsoft Public License.
|
||||
/// More info about DotNetZip at dotnetzip.codeplex.com.
|
||||
/// </summary>
|
||||
public static class ZlibUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Compress a byte array into another bytes array using Zlib compression
|
||||
/// </summary>
|
||||
/// <param name="to_compress">Data to compress</param>
|
||||
/// <returns>Compressed data as a byte array</returns>
|
||||
public static byte[] Compress(byte[] to_compress)
|
||||
{
|
||||
byte[] data;
|
||||
using (System.IO.MemoryStream memstream = new())
|
||||
{
|
||||
using (ZlibStream stream = new(memstream, CompressionMode.Compress))
|
||||
{
|
||||
stream.Write(to_compress, 0, to_compress.Length);
|
||||
}
|
||||
data = memstream.ToArray();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a byte array into another byte array of the specified size
|
||||
/// </summary>
|
||||
/// <param name="to_decompress">Data to decompress</param>
|
||||
/// <param name="size_uncompressed">Size of the data once decompressed</param>
|
||||
/// <returns>Decompressed data as a byte array</returns>
|
||||
public static byte[] Decompress(byte[] to_decompress, int size_uncompressed)
|
||||
{
|
||||
ZlibStream stream = new(new System.IO.MemoryStream(to_decompress, false), CompressionMode.Decompress);
|
||||
byte[] packetData_decompressed = new byte[size_uncompressed];
|
||||
stream.Read(packetData_decompressed, 0, size_uncompressed);
|
||||
stream.Close();
|
||||
return packetData_decompressed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a byte array into another byte array of a potentially unlimited size (!)
|
||||
/// </summary>
|
||||
/// <param name="to_decompress">Data to decompress</param>
|
||||
/// <returns>Decompressed data as byte array</returns>
|
||||
public static byte[] Decompress(byte[] to_decompress)
|
||||
{
|
||||
ZlibStream stream = new(new System.IO.MemoryStream(to_decompress, false), CompressionMode.Decompress);
|
||||
byte[] buffer = new byte[16 * 1024];
|
||||
using System.IO.MemoryStream decompressedBuffer = new();
|
||||
int read;
|
||||
while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
|
||||
decompressedBuffer.Write(buffer, 0, read);
|
||||
return decompressedBuffer.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue