Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
01ef9a8
Bug fix: Cancel chunk load task when switching worlds
BruceChenQAQ Aug 24, 2022
58eafdf
Optimize cold start speed and block loading speed
BruceChenQAQ Aug 24, 2022
b125b0f
Remove debug message
BruceChenQAQ Aug 24, 2022
64915c8
Bug fix: Incorrect handling of Chunk.FullyLoaded in 1.17[.1]
BruceChenQAQ Aug 24, 2022
5f520e2
Improve ReadBlockStatesField
BruceChenQAQ Aug 25, 2022
ed8e97f
Bug fix: /move command went to the wrong location
BruceChenQAQ Aug 25, 2022
a3971f9
Update to BouncyCastle 1.9.0
BruceChenQAQ Aug 25, 2022
13d1a98
Rewrote AES stream & Perform "SessionCheck" in advance
BruceChenQAQ Aug 27, 2022
842c968
Use AES-NI instruction set if possible
BruceChenQAQ Aug 28, 2022
4757c4b
Trim
BruceChenQAQ Aug 28, 2022
d10ad13
Trim
BruceChenQAQ Aug 28, 2022
dff3f23
Bug fix: ResourcePackSend.hasPromptMessage
BruceChenQAQ Aug 28, 2022
75e7b0e
Fix issue #2119
BruceChenQAQ Aug 28, 2022
003c4c3
Fix issue #2094
BruceChenQAQ Aug 28, 2022
68b9c81
Improve ReadBlockStatesField
BruceChenQAQ Aug 29, 2022
cd45c64
Change chunk storage structure
BruceChenQAQ Aug 30, 2022
e09016c
Improve ReadBlockStatesField
BruceChenQAQ Aug 30, 2022
c941d08
Improve ReadBlockStatesField
BruceChenQAQ Aug 30, 2022
1a90b6d
Use separate threads for decryption and decompression
BruceChenQAQ Aug 30, 2022
a6b98de
Fix not calling "handler.OnUpdate()" on time
BruceChenQAQ Aug 30, 2022
e4c77b0
Bug fix: Call handler.OnNetworkPacket() repeatedly
BruceChenQAQ Aug 30, 2022
aceccaf
No longer need to cancel chunk loading
BruceChenQAQ Aug 30, 2022
3d13eb5
Trim
BruceChenQAQ Aug 30, 2022
c90ea0e
Startup Optimization
BruceChenQAQ Aug 30, 2022
9089bb4
Change how world is stored & Bug fix
BruceChenQAQ Aug 31, 2022
0a689e4
Trim
BruceChenQAQ Aug 31, 2022
c0be6a6
Trim
BruceChenQAQ Aug 31, 2022
db64515
Fix issue #2144
BruceChenQAQ Aug 31, 2022
98dd645
Bug fix: Can't reconnect after connection lost
BruceChenQAQ Aug 31, 2022
a13af47
Reduce the latency of sending messages
BruceChenQAQ Aug 31, 2022
b26949e
Fix issue #2111
BruceChenQAQ Sep 1, 2022
290ec9c
Bug fix
BruceChenQAQ Sep 1, 2022
4538095
Reduce merge conflicts
BruceChenQAQ Sep 2, 2022
8ed2bc9
Merge branch 'master' into bugfix
BruceChenQAQ Sep 2, 2022
0382e07
Bug fix: Chunk deleted by mistake
BruceChenQAQ Sep 2, 2022
3b95cbc
Trim
BruceChenQAQ Sep 2, 2022
afdf2f9
Merge from master
BruceChenQAQ Sep 4, 2022
bcded40
Bug fix
BruceChenQAQ Sep 4, 2022
db17bab
Bug fix
BruceChenQAQ Sep 4, 2022
dfc310b
Fix for EntityEffect packet
BruceChenQAQ Sep 4, 2022
015d28e
Fix for LoginSuccess packet
BruceChenQAQ Sep 4, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion MinecraftClient/ChatBots/ChatLog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public ChatLog(string file, MessageFilter filter, bool AddDateAndTime)

public static MessageFilter str2filter(string filtername)
{
switch (filtername.ToLower())
switch (Settings.ToLowerIfNeed(filtername))
{
case "all": return MessageFilter.AllText;
case "messages": return MessageFilter.AllMessages;
Expand Down
15 changes: 11 additions & 4 deletions MinecraftClient/Commands/Move.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,16 @@ public override string Run(McClient handler, string command, Dictionary<string,
case "get": return handler.GetCurrentLocation().ToString();
default: return Translations.Get("cmd.look.unknown", args[0]);
}

Location goal = Movement.Move(handler.GetCurrentLocation(), direction);

ChunkColumn? chunkColumn = handler.GetWorld().GetChunkColumn(goal);
if (chunkColumn == null || chunkColumn.FullyLoaded == false)
return Translations.Get("cmd.move.chunk_not_loaded");

if (Movement.CanMove(handler.GetWorld(), handler.GetCurrentLocation(), direction))
{
if (handler.MoveTo(Movement.Move(handler.GetCurrentLocation(), direction), allowUnsafe: takeRisk))
if (handler.MoveTo(goal, allowUnsafe: takeRisk))
return Translations.Get("cmd.move.moving", args[0]);
else return takeRisk ? Translations.Get("cmd.move.dir_fail") : Translations.Get("cmd.move.suggestforce");
}
Expand All @@ -88,12 +95,12 @@ public override string Run(McClient handler, string command, Dictionary<string,
int z = int.Parse(args[2]);
Location goal = new Location(x, y, z);

if (handler.GetWorld().GetChunkColumn(goal) == null || handler.GetWorld().GetChunkColumn(goal).FullyLoaded == false)
ChunkColumn? chunkColumn = handler.GetWorld().GetChunkColumn(goal);
if (chunkColumn == null || chunkColumn.FullyLoaded == false)
return Translations.Get("cmd.move.chunk_not_loaded");

Location current = handler.GetCurrentLocation();
Location currentCenter = new Location(Math.Floor(current.X) + 0.5, current.Y, Math.Floor(current.Z) + 0.5);
handler.MoveTo(currentCenter, allowDirectTeleport: true);
handler.MoveTo(current.ToCenter(), allowDirectTeleport: true);

if (handler.MoveTo(goal, allowUnsafe: takeRisk))
return Translations.Get("cmd.move.walk", goal, current);
Expand Down
2 changes: 1 addition & 1 deletion MinecraftClient/ConsoleIO.cs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ public static void WriteLogLine(string text, bool acceptnewlines = true)
{
if (!acceptnewlines)
text = text.Replace('\n', ' ');
WriteLineFormatted(LogPrefix + text);
WriteLineFormatted(LogPrefix + text, acceptnewlines);
}

#region Subfunctions
Expand Down
210 changes: 210 additions & 0 deletions MinecraftClient/Crypto/AesCfb8Stream.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Security.Cryptography;
using System.IO;
using System.Collections.Concurrent;
using System.Runtime.CompilerServices;

namespace MinecraftClient.Crypto
{
public class AesCfb8Stream : Stream
{
public static readonly int blockSize = 16;

private readonly Aes? Aes = null;
private readonly FastAes? FastAes = null;

private bool inStreamEnded = false;

private byte[] ReadStreamIV = new byte[16];
private byte[] WriteStreamIV = new byte[16];

public Stream BaseStream { get; set; }

public AesCfb8Stream(Stream stream, byte[] key)
{
BaseStream = stream;

if (FastAes.IsSupported())
FastAes = new FastAes(key);
else
{
Aes = Aes.Create();
Aes.BlockSize = 128;
Aes.KeySize = 128;
Aes.Key = key;
Aes.Mode = CipherMode.ECB;
Aes.Padding = PaddingMode.None;
}

Array.Copy(key, ReadStreamIV, 16);
Array.Copy(key, WriteStreamIV, 16);
}

public override bool CanRead
{
get { return true; }
}

public override bool CanSeek
{
get { return false; }
}

public override bool CanWrite
{
get { return true; }
}

public override void Flush()
{
BaseStream.Flush();
}

public override long Length
{
get { throw new NotSupportedException(); }
}

public override long Position
{
get
{
throw new NotSupportedException();
}
set
{
throw new NotSupportedException();
}
}

public override int ReadByte()
{
if (inStreamEnded)
return -1;

int inputBuf = BaseStream.ReadByte();
if (inputBuf == -1)
{
inStreamEnded = true;
return -1;
}

Span<byte> blockOutput = stackalloc byte[blockSize];
if (FastAes != null)
FastAes.EncryptEcb(ReadStreamIV, blockOutput);
else
Aes!.EncryptEcb(ReadStreamIV, blockOutput, PaddingMode.None);

// Shift left
Array.Copy(ReadStreamIV, 1, ReadStreamIV, 0, blockSize - 1);
ReadStreamIV[blockSize - 1] = (byte)inputBuf;

return (byte)(blockOutput[0] ^ inputBuf);
}

[MethodImpl(MethodImplOptions.AggressiveOptimization)]
public override int Read(byte[] buffer, int outOffset, int required)
{
if (inStreamEnded)
return 0;

Span<byte> blockOutput = FastAes != null ? stackalloc byte[blockSize] : null;

byte[] inputBuf = new byte[blockSize + required];
Array.Copy(ReadStreamIV, inputBuf, blockSize);

for (int readed = 0, curRead; readed < required; readed += curRead)
{
curRead = BaseStream.Read(inputBuf, blockSize + readed, required - readed);
if (curRead == 0)
{
inStreamEnded = true;
return readed;
}

int processEnd = readed + curRead;
if (FastAes != null)
{
for (int idx = readed; idx < processEnd; idx++)
{
ReadOnlySpan<byte> blockInput = new(inputBuf, idx, blockSize);
FastAes.EncryptEcb(blockInput, blockOutput);
buffer[outOffset + idx] = (byte)(blockOutput[0] ^ inputBuf[idx + blockSize]);
}
}
else
{
OrderablePartitioner<Tuple<int, int>> rangePartitioner = curRead <= 256 ?
Partitioner.Create(readed, processEnd, 32) : Partitioner.Create(readed, processEnd);
Parallel.ForEach(rangePartitioner, (range, loopState) =>
{
Span<byte> blockOutput = stackalloc byte[blockSize];
for (int idx = range.Item1; idx < range.Item2; idx++)
{
ReadOnlySpan<byte> blockInput = new(inputBuf, idx, blockSize);
Aes!.EncryptEcb(blockInput, blockOutput, PaddingMode.None);
buffer[outOffset + idx] = (byte)(blockOutput[0] ^ inputBuf[idx + blockSize]);
}
});
}
}

Array.Copy(inputBuf, required, ReadStreamIV, 0, blockSize);

return required;
}

public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}

public override void SetLength(long value)
{
throw new NotSupportedException();
}

public override void WriteByte(byte b)
{
Span<byte> blockOutput = stackalloc byte[blockSize];

if (FastAes != null)
FastAes.EncryptEcb(WriteStreamIV, blockOutput);
else
Aes!.EncryptEcb(WriteStreamIV, blockOutput, PaddingMode.None);

byte outputBuf = (byte)(blockOutput[0] ^ b);

BaseStream.WriteByte(outputBuf);

// Shift left
Array.Copy(WriteStreamIV, 1, WriteStreamIV, 0, blockSize - 1);
WriteStreamIV[blockSize - 1] = outputBuf;
}

[MethodImpl(MethodImplOptions.AggressiveOptimization)]
public override void Write(byte[] input, int offset, int required)
{
byte[] outputBuf = new byte[blockSize + required];
Array.Copy(WriteStreamIV, outputBuf, blockSize);

Span<byte> blockOutput = stackalloc byte[blockSize];
for (int wirtten = 0; wirtten < required; ++wirtten)
{
ReadOnlySpan<byte> blockInput = new(outputBuf, wirtten, blockSize);
if (FastAes != null)
FastAes.EncryptEcb(blockInput, blockOutput);
else
Aes!.EncryptEcb(blockInput, blockOutput, PaddingMode.None);
outputBuf[blockSize + wirtten] = (byte)(blockOutput[0] ^ input[offset + wirtten]);
}
BaseStream.WriteAsync(outputBuf, blockSize, required);

Array.Copy(outputBuf, required, WriteStreamIV, 0, blockSize);
}
}
}
17 changes: 3 additions & 14 deletions MinecraftClient/Crypto/CryptoHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ namespace MinecraftClient.Crypto
/// Methods for handling all the crypto stuff: RSA (Encryption Key Request), AES (Encrypted Stream), SHA-1 (Server Hash).
/// </summary>

public class CryptoHandler
public static class CryptoHandler
{
public static byte[]? ClientAESPrivateKey = null;

/// <summary>
/// Get a cryptographic service for encrypting data using the server's RSA public key
/// </summary>
Expand Down Expand Up @@ -191,18 +193,5 @@ private static byte[] TwosComplementLittleEndian(byte[] p)
}
return p;
}

/// <summary>
/// Get a new AES-encrypted stream for wrapping a non-encrypted stream.
/// </summary>
/// <param name="underlyingStream">Stream to encrypt</param>
/// <param name="AesKey">Key to use</param>
/// <returns>Return an appropriate stream depending on the framework being used</returns>

public static IAesStream getAesStream(Stream underlyingStream, byte[] AesKey)
{
// return new Streams.RegularAesStream(underlyingStream, AesKey);
return new Streams.MonoAesStream(underlyingStream, AesKey);
}
}
}
99 changes: 99 additions & 0 deletions MinecraftClient/Crypto/FastAes.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;

namespace MinecraftClient.Crypto
{
// Using the AES-NI instruction set
// https://gist.github.com/Thealexbarney/9f75883786a9f3100408ff795fb95d85
public class FastAes
{
private Vector128<byte>[] RoundKeys { get; }

public FastAes(Span<byte> key)
{
RoundKeys = KeyExpansion(key);
}

/// <summary>
/// Detects if the required instruction set is supported
/// </summary>
/// <returns>Is it supported</returns>
public static bool IsSupported()
{
return Sse2.IsSupported && Aes.IsSupported;
}

[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public void EncryptEcb(ReadOnlySpan<byte> plaintext, Span<byte> destination)
{
Vector128<byte>[] keys = RoundKeys;

ReadOnlySpan<Vector128<byte>> blocks = MemoryMarshal.Cast<byte, Vector128<byte>>(plaintext);
Span<Vector128<byte>> dest = MemoryMarshal.Cast<byte, Vector128<byte>>(destination);

// Makes the JIT remove all the other range checks on keys
_ = keys[10];

for (int i = 0; i < blocks.Length; i++)
{
Vector128<byte> b = blocks[i];

b = Sse2.Xor(b, keys[0]);
b = Aes.Encrypt(b, keys[1]);
b = Aes.Encrypt(b, keys[2]);
b = Aes.Encrypt(b, keys[3]);
b = Aes.Encrypt(b, keys[4]);
b = Aes.Encrypt(b, keys[5]);
b = Aes.Encrypt(b, keys[6]);
b = Aes.Encrypt(b, keys[7]);
b = Aes.Encrypt(b, keys[8]);
b = Aes.Encrypt(b, keys[9]);
b = Aes.EncryptLast(b, keys[10]);

dest[i] = b;
}
}

private static Vector128<byte>[] KeyExpansion(Span<byte> key)
{
var keys = new Vector128<byte>[20];

keys[0] = Unsafe.ReadUnaligned<Vector128<byte>>(ref key[0]);

MakeRoundKey(keys, 1, 0x01);
MakeRoundKey(keys, 2, 0x02);
MakeRoundKey(keys, 3, 0x04);
MakeRoundKey(keys, 4, 0x08);
MakeRoundKey(keys, 5, 0x10);
MakeRoundKey(keys, 6, 0x20);
MakeRoundKey(keys, 7, 0x40);
MakeRoundKey(keys, 8, 0x80);
MakeRoundKey(keys, 9, 0x1b);
MakeRoundKey(keys, 10, 0x36);

for (int i = 1; i < 10; i++)
{
keys[10 + i] = Aes.InverseMixColumns(keys[i]);
}

return keys;
}

private static void MakeRoundKey(Vector128<byte>[] keys, int i, byte rcon)
{
Vector128<byte> s = keys[i - 1];
Vector128<byte> t = keys[i - 1];

t = Aes.KeygenAssist(t, rcon);
t = Sse2.Shuffle(t.AsUInt32(), 0xFF).AsByte();

s = Sse2.Xor(s, Sse2.ShiftLeftLogical128BitLane(s, 4));
s = Sse2.Xor(s, Sse2.ShiftLeftLogical128BitLane(s, 8));

keys[i] = Sse2.Xor(s, t);
}
}
}
Loading