mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2025-10-14 21:22:49 +00:00
Optimize cold start speed and block loading speed
This commit is contained in:
parent
01ef9a89ca
commit
58eafdfd5c
8 changed files with 147 additions and 127 deletions
|
|
@ -20,11 +20,6 @@ namespace MinecraftClient.Mapping
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly Block[,,] blocks = new Block[SizeX, SizeY, SizeZ];
|
private readonly Block[,,] blocks = new Block[SizeX, SizeY, SizeZ];
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Lock for thread safety
|
|
||||||
/// </summary>
|
|
||||||
private readonly ReaderWriterLockSlim blockLock = new ReaderWriterLockSlim();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read, or set the specified block
|
/// Read, or set the specified block
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -43,15 +38,7 @@ namespace MinecraftClient.Mapping
|
||||||
if (blockZ < 0 || blockZ >= SizeZ)
|
if (blockZ < 0 || blockZ >= SizeZ)
|
||||||
throw new ArgumentOutOfRangeException("blockZ", "Must be between 0 and " + (SizeZ - 1) + " (inclusive)");
|
throw new ArgumentOutOfRangeException("blockZ", "Must be between 0 and " + (SizeZ - 1) + " (inclusive)");
|
||||||
|
|
||||||
blockLock.EnterReadLock();
|
return blocks[blockX, blockY, blockZ];
|
||||||
try
|
|
||||||
{
|
|
||||||
return blocks[blockX, blockY, blockZ];
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
blockLock.ExitReadLock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
|
|
@ -62,18 +49,22 @@ namespace MinecraftClient.Mapping
|
||||||
if (blockZ < 0 || blockZ >= SizeZ)
|
if (blockZ < 0 || blockZ >= SizeZ)
|
||||||
throw new ArgumentOutOfRangeException("blockZ", "Must be between 0 and " + (SizeZ - 1) + " (inclusive)");
|
throw new ArgumentOutOfRangeException("blockZ", "Must be between 0 and " + (SizeZ - 1) + " (inclusive)");
|
||||||
|
|
||||||
blockLock.EnterWriteLock();
|
blocks[blockX, blockY, blockZ] = value;
|
||||||
try
|
|
||||||
{
|
|
||||||
blocks[blockX, blockY, blockZ] = value;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
blockLock.ExitWriteLock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used when parsing chunks
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="blockX">Block X</param>
|
||||||
|
/// <param name="blockY">Block Y</param>
|
||||||
|
/// <param name="blockZ">Block Z</param>
|
||||||
|
/// <param name="block">Block</param>
|
||||||
|
public void SetWithoutCheck(int blockX, int blockY, int blockZ, Block block)
|
||||||
|
{
|
||||||
|
blocks[blockX, blockY, blockZ] = block;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get block at the specified location
|
/// Get block at the specified location
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -20,11 +20,6 @@ namespace MinecraftClient.Mapping
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly Chunk[] chunks;
|
private readonly Chunk[] chunks;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Lock for thread safety
|
|
||||||
/// </summary>
|
|
||||||
private readonly ReaderWriterLockSlim chunkLock = new ReaderWriterLockSlim();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new ChunkColumn
|
/// Create a new ChunkColumn
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -44,27 +39,11 @@ namespace MinecraftClient.Mapping
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
chunkLock.EnterReadLock();
|
return chunks[chunkY];
|
||||||
try
|
|
||||||
{
|
|
||||||
return chunks[chunkY];
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
chunkLock.ExitReadLock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
chunkLock.EnterWriteLock();
|
chunks[chunkY] = value;
|
||||||
try
|
|
||||||
{
|
|
||||||
chunks[chunkY] = value;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
chunkLock.ExitWriteLock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,17 +14,17 @@ namespace MinecraftClient.Mapping
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The chunks contained into the Minecraft world
|
/// The chunks contained into the Minecraft world
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private Dictionary<int, Dictionary<int, ChunkColumn>> chunks = new Dictionary<int, Dictionary<int, ChunkColumn>>();
|
private Dictionary<int, Dictionary<int, ChunkColumn>> chunks = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The dimension info of the world
|
/// The dimension info of the world
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static Dimension dimension = new Dimension();
|
private static Dimension dimension = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Lock for thread safety
|
/// Lock for thread safety
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly ReaderWriterLockSlim chunksLock = new ReaderWriterLockSlim();
|
private readonly ReaderWriterLockSlim chunksLock = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Chunk data parsing progress
|
/// Chunk data parsing progress
|
||||||
|
|
@ -109,6 +109,54 @@ namespace MinecraftClient.Mapping
|
||||||
return dimension;
|
return dimension;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set chunk column at the specified location
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="chunkX">ChunkColumn X</param>
|
||||||
|
/// <param name="chunkY">ChunkColumn Y</param>
|
||||||
|
/// <param name="chunkZ">ChunkColumn Z</param>
|
||||||
|
/// <param name="chunkColumnSize">ChunkColumn size</param>
|
||||||
|
/// <param name="chunk">Chunk data</param>
|
||||||
|
/// <param name="loadCompleted">Whether the ChunkColumn has been fully loaded</param>
|
||||||
|
public void StoreChunk(int chunkX, int chunkY, int chunkZ, int chunkColumnSize, Chunk chunk, bool loadCompleted)
|
||||||
|
{
|
||||||
|
ChunkColumn? chunkColumn = null;
|
||||||
|
|
||||||
|
chunksLock.EnterUpgradeableReadLock();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
//Read a chunk
|
||||||
|
if (chunks.ContainsKey(chunkX))
|
||||||
|
if (chunks[chunkX].ContainsKey(chunkZ))
|
||||||
|
chunkColumn = chunks[chunkX][chunkZ];
|
||||||
|
|
||||||
|
if (chunkColumn == null)
|
||||||
|
{
|
||||||
|
chunkColumn = new ChunkColumn(chunkColumnSize);
|
||||||
|
chunksLock.EnterWriteLock();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
//Update a chunk column
|
||||||
|
if (!chunks.ContainsKey(chunkX))
|
||||||
|
chunks[chunkX] = new Dictionary<int, ChunkColumn>();
|
||||||
|
chunks[chunkX][chunkZ] = chunkColumn;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
chunksLock.ExitWriteLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
chunksLock.ExitUpgradeableReadLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
chunkColumn[chunkY] = chunk;
|
||||||
|
if (loadCompleted)
|
||||||
|
chunkColumn.FullyLoaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get chunk column at the specified location
|
/// Get chunk column at the specified location
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ using MinecraftClient.WinAPI;
|
||||||
using MinecraftClient.Protocol.Keys;
|
using MinecraftClient.Protocol.Keys;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace MinecraftClient
|
namespace MinecraftClient
|
||||||
{
|
{
|
||||||
|
|
@ -84,9 +85,12 @@ namespace MinecraftClient
|
||||||
//Take advantage of Windows 10 / Mac / Linux UTF-8 console
|
//Take advantage of Windows 10 / Mac / Linux UTF-8 console
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
{
|
{
|
||||||
// If we're on windows, check if our version is Win10 or greater.
|
Parallel.Invoke(() =>
|
||||||
if (WindowsVersion.WinMajorVersion >= 10)
|
{
|
||||||
Console.OutputEncoding = Console.InputEncoding = Encoding.UTF8;
|
// If we're on windows, check if our version is Win10 or greater.
|
||||||
|
if (WindowsVersion.WinMajorVersion >= 10)
|
||||||
|
Console.OutputEncoding = Console.InputEncoding = Encoding.UTF8;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -253,17 +253,15 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
/// <returns>The integer</returns>
|
/// <returns>The integer</returns>
|
||||||
public int ReadNextVarInt(Queue<byte> cache)
|
public int ReadNextVarInt(Queue<byte> cache)
|
||||||
{
|
{
|
||||||
string rawData = BitConverter.ToString(cache.ToArray());
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int j = 0;
|
int j = 0;
|
||||||
int k = 0;
|
byte b;
|
||||||
while (true)
|
do
|
||||||
{
|
{
|
||||||
k = ReadNextByte(cache);
|
b = cache.Dequeue();
|
||||||
i |= (k & 0x7F) << j++ * 7;
|
i |= (b & 127) << j++ * 7;
|
||||||
if (j > 5) throw new OverflowException("VarInt too big " + rawData);
|
if (j > 5) throw new OverflowException("VarInt too big");
|
||||||
if ((k & 0x80) != 128) break;
|
} while ((b & 128) == 128);
|
||||||
}
|
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -558,12 +558,19 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
|
|
||||||
int dataSize = dataTypes.ReadNextVarInt(packetData); // Size
|
int dataSize = dataTypes.ReadNextVarInt(packetData); // Size
|
||||||
|
|
||||||
new Task(() =>
|
Parallel.Invoke(() =>
|
||||||
{
|
{
|
||||||
bool loaded = pTerrain.ProcessChunkColumnData(chunkX, chunkZ, verticalStripBitmask, packetData, cancellationToken);
|
bool loaded = pTerrain.ProcessChunkColumnData(chunkX, chunkZ, verticalStripBitmask, packetData, cancellationToken);
|
||||||
if (loaded)
|
if (loaded)
|
||||||
|
{
|
||||||
Interlocked.Decrement(ref handler.GetWorld().chunkLoadNotCompleted);
|
Interlocked.Decrement(ref handler.GetWorld().chunkLoadNotCompleted);
|
||||||
}).Start();
|
if (handler.GetWorld().chunkCnt == 464)
|
||||||
|
{
|
||||||
|
log.Info("Loaded 464 chunks, exit");
|
||||||
|
System.Environment.Exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Block Entity data: ignored
|
// Block Entity data: ignored
|
||||||
// Light data: ignored
|
// Light data: ignored
|
||||||
|
|
@ -582,12 +589,12 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
int compressedDataSize = dataTypes.ReadNextInt(packetData);
|
int compressedDataSize = dataTypes.ReadNextInt(packetData);
|
||||||
byte[] compressed = dataTypes.ReadData(compressedDataSize, packetData);
|
byte[] compressed = dataTypes.ReadData(compressedDataSize, packetData);
|
||||||
byte[] decompressed = ZlibUtils.Decompress(compressed);
|
byte[] decompressed = ZlibUtils.Decompress(compressed);
|
||||||
new Task(() =>
|
Parallel.Invoke(() =>
|
||||||
{
|
{
|
||||||
bool loaded = pTerrain.ProcessChunkColumnData(chunkX, chunkZ, chunkMask, addBitmap, currentDimension == 0, chunksContinuous, currentDimension, new Queue<byte>(decompressed), cancellationToken);
|
bool loaded = pTerrain.ProcessChunkColumnData(chunkX, chunkZ, chunkMask, addBitmap, currentDimension == 0, chunksContinuous, currentDimension, new Queue<byte>(decompressed), cancellationToken);
|
||||||
if (loaded)
|
if (loaded)
|
||||||
Interlocked.Decrement(ref handler.GetWorld().chunkLoadNotCompleted);
|
Interlocked.Decrement(ref handler.GetWorld().chunkLoadNotCompleted);
|
||||||
}).Start();
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -611,12 +618,12 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
else dataTypes.ReadData(1024 * 4, packetData); // Biomes - 1.15 and above
|
else dataTypes.ReadData(1024 * 4, packetData); // Biomes - 1.15 and above
|
||||||
}
|
}
|
||||||
int dataSize = dataTypes.ReadNextVarInt(packetData);
|
int dataSize = dataTypes.ReadNextVarInt(packetData);
|
||||||
new Task(() =>
|
Parallel.Invoke(() =>
|
||||||
{
|
{
|
||||||
bool loaded = pTerrain.ProcessChunkColumnData(chunkX, chunkZ, chunkMask, 0, false, chunksContinuous, currentDimension, packetData, cancellationToken);
|
bool loaded = pTerrain.ProcessChunkColumnData(chunkX, chunkZ, chunkMask, 0, false, chunksContinuous, currentDimension, packetData, cancellationToken);
|
||||||
if (loaded)
|
if (loaded)
|
||||||
Interlocked.Decrement(ref handler.GetWorld().chunkLoadNotCompleted);
|
Interlocked.Decrement(ref handler.GetWorld().chunkLoadNotCompleted);
|
||||||
}).Start();
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -872,7 +879,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
}
|
}
|
||||||
|
|
||||||
//Process chunk records
|
//Process chunk records
|
||||||
new Task(() =>
|
Parallel.Invoke(() =>
|
||||||
{
|
{
|
||||||
for (int chunkColumnNo = 0; chunkColumnNo < chunkCount; chunkColumnNo++)
|
for (int chunkColumnNo = 0; chunkColumnNo < chunkCount; chunkColumnNo++)
|
||||||
{
|
{
|
||||||
|
|
@ -882,7 +889,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
if (loaded)
|
if (loaded)
|
||||||
Interlocked.Decrement(ref handler.GetWorld().chunkLoadNotCompleted);
|
Interlocked.Decrement(ref handler.GetWorld().chunkLoadNotCompleted);
|
||||||
}
|
}
|
||||||
}).Start();
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Numerics;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
//using System.Linq;
|
//using System.Linq;
|
||||||
//using System.Text;
|
//using System.Text;
|
||||||
|
|
@ -42,24 +43,19 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
if (bitsPerEntry == 0 && protocolversion >= Protocol18Handler.MC_1_18_1_Version)
|
if (bitsPerEntry == 0 && protocolversion >= Protocol18Handler.MC_1_18_1_Version)
|
||||||
{
|
{
|
||||||
// Palettes: Single valued - 1.18(1.18.1) and above
|
// Palettes: Single valued - 1.18(1.18.1) and above
|
||||||
ushort value = (ushort)dataTypes.ReadNextVarInt(cache);
|
ushort blockId = (ushort)dataTypes.ReadNextVarInt(cache);
|
||||||
|
|
||||||
dataTypes.SkipNextVarInt(cache); // Data Array Length will be zero
|
dataTypes.SkipNextVarInt(cache); // Data Array Length will be zero
|
||||||
|
|
||||||
// Empty chunks will not be stored
|
// Empty chunks will not be stored
|
||||||
if (new Block(value).Type == Material.Air)
|
if (new Block(blockId).Type == Material.Air)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
for (int blockY = 0; blockY < Chunk.SizeY; blockY++)
|
for (int blockY = 0; blockY < Chunk.SizeY; blockY++)
|
||||||
{
|
|
||||||
for (int blockZ = 0; blockZ < Chunk.SizeZ; blockZ++)
|
for (int blockZ = 0; blockZ < Chunk.SizeZ; blockZ++)
|
||||||
{
|
|
||||||
for (int blockX = 0; blockX < Chunk.SizeX; blockX++)
|
for (int blockX = 0; blockX < Chunk.SizeX; blockX++)
|
||||||
{
|
chunk.SetWithoutCheck(blockX, blockY, blockZ, new Block(blockId));
|
||||||
chunk[blockX, blockY, blockZ] = new Block(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -129,7 +125,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
}
|
}
|
||||||
|
|
||||||
// We have our block, save the block into the chunk
|
// We have our block, save the block into the chunk
|
||||||
chunk[blockX, blockY, blockZ] = new Block(blockId);
|
chunk.SetWithoutCheck(blockX, blockY, blockZ, new Block(blockId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -152,7 +148,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
if (cancellationToken.IsCancellationRequested)
|
if (cancellationToken.IsCancellationRequested)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var world = handler.GetWorld();
|
World world = handler.GetWorld();
|
||||||
|
|
||||||
int chunkColumnSize = (World.GetDimension().height + 15) / 16; // Round up
|
int chunkColumnSize = (World.GetDimension().height + 15) / 16; // Round up
|
||||||
|
|
||||||
|
|
@ -167,10 +163,8 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
|
|
||||||
// 1.18 and above always contains all chunk section in data
|
// 1.18 and above always contains all chunk section in data
|
||||||
// 1.17 and 1.17.1 need vertical strip bitmask to know if the chunk section is included
|
// 1.17 and 1.17.1 need vertical strip bitmask to know if the chunk section is included
|
||||||
if ((protocolversion >= Protocol18Handler.MC_1_18_1_Version) ||
|
if ((protocolversion >= Protocol18Handler.MC_1_18_1_Version) ||
|
||||||
(((protocolversion == Protocol18Handler.MC_1_17_Version) ||
|
((verticalStripBitmask![chunkY / 64] & (1UL << (chunkY % 64))) != 0))
|
||||||
(protocolversion == Protocol18Handler.MC_1_17_1_Version)) &&
|
|
||||||
((verticalStripBitmask![chunkY / 64] & (1UL << (chunkY % 64))) != 0)))
|
|
||||||
{
|
{
|
||||||
// Non-air block count inside chunk section, for lighting purposes
|
// Non-air block count inside chunk section, for lighting purposes
|
||||||
int blockCnt = dataTypes.ReadNextShort(cache);
|
int blockCnt = dataTypes.ReadNextShort(cache);
|
||||||
|
|
@ -186,9 +180,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
//We have our chunk, save the chunk into the world
|
//We have our chunk, save the chunk into the world
|
||||||
handler.InvokeOnMainThread(() =>
|
handler.InvokeOnMainThread(() =>
|
||||||
{
|
{
|
||||||
if (handler.GetWorld()[chunkX, chunkZ] == null)
|
world.StoreChunk(chunkX, chunkY, chunkZ, chunkColumnSize, chunk, chunkY == (chunkColumnSize - 1));
|
||||||
handler.GetWorld()[chunkX, chunkZ] = new ChunkColumn(chunkColumnSize);
|
|
||||||
handler.GetWorld()[chunkX, chunkZ]![chunkY] = chunk;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Skip Read Biomes (Type: Paletted Container) - 1.18(1.18.1) and above
|
// Skip Read Biomes (Type: Paletted Container) - 1.18(1.18.1) and above
|
||||||
|
|
@ -218,11 +210,6 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
// Don't worry about skipping remaining data since there is no useful data afterwards in 1.9
|
// 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)
|
// (plus, it would require parsing the tile entity lists' NBT)
|
||||||
}
|
}
|
||||||
handler.InvokeOnMainThread(() =>
|
|
||||||
{
|
|
||||||
if (handler.GetWorld()[chunkX, chunkZ] != null)
|
|
||||||
handler.GetWorld()[chunkX, chunkZ]!.FullyLoaded = true;
|
|
||||||
});
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -244,12 +231,15 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
if (cancellationToken.IsCancellationRequested)
|
if (cancellationToken.IsCancellationRequested)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
World world = handler.GetWorld();
|
||||||
|
|
||||||
const int chunkColumnSize = 16;
|
const int chunkColumnSize = 16;
|
||||||
if (protocolversion >= Protocol18Handler.MC_1_9_Version)
|
if (protocolversion >= Protocol18Handler.MC_1_9_Version)
|
||||||
{
|
{
|
||||||
// 1.9 and above chunk format
|
// 1.9 and above chunk format
|
||||||
// Unloading chunks is handled by a separate packet
|
// Unloading chunks is handled by a separate packet
|
||||||
for (int chunkY = 0; chunkY < chunkColumnSize; chunkY++)
|
int maxChunkY = sizeof(int) * 8 - 1 - BitOperations.LeadingZeroCount(chunkMask);
|
||||||
|
for (int chunkY = 0; chunkY <= maxChunkY; chunkY++)
|
||||||
{
|
{
|
||||||
if (cancellationToken.IsCancellationRequested)
|
if (cancellationToken.IsCancellationRequested)
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -363,7 +353,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
}
|
}
|
||||||
|
|
||||||
// We have our block, save the block into the chunk
|
// We have our block, save the block into the chunk
|
||||||
chunk[blockX, blockY, blockZ] = new Block(blockId);
|
chunk.SetWithoutCheck(blockX, blockY, blockZ, new Block(blockId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -376,9 +366,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
//We have our chunk, save the chunk into the world
|
//We have our chunk, save the chunk into the world
|
||||||
handler.InvokeOnMainThread(() =>
|
handler.InvokeOnMainThread(() =>
|
||||||
{
|
{
|
||||||
if (handler.GetWorld()[chunkX, chunkZ] == null)
|
world.StoreChunk(chunkX, chunkY, chunkZ, chunkColumnSize, chunk, chunkY == maxChunkY);
|
||||||
handler.GetWorld()[chunkX, chunkZ] = new ChunkColumn();
|
|
||||||
handler.GetWorld()[chunkX, chunkZ]![chunkY] = chunk;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//Pre-1.14 Lighting data
|
//Pre-1.14 Lighting data
|
||||||
|
|
@ -406,13 +394,14 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
//Unload the entire chunk column
|
//Unload the entire chunk column
|
||||||
handler.InvokeOnMainThread(() =>
|
handler.InvokeOnMainThread(() =>
|
||||||
{
|
{
|
||||||
handler.GetWorld()[chunkX, chunkZ] = null;
|
world[chunkX, chunkZ] = null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//Load chunk data from the server
|
//Load chunk data from the server
|
||||||
for (int chunkY = 0; chunkY < chunkColumnSize; chunkY++)
|
int maxChunkY = sizeof(int) * 8 - 1 - BitOperations.LeadingZeroCount(chunkMask);
|
||||||
|
for (int chunkY = 0; chunkY <= maxChunkY; chunkY++)
|
||||||
{
|
{
|
||||||
if (cancellationToken.IsCancellationRequested)
|
if (cancellationToken.IsCancellationRequested)
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -426,7 +415,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
for (int blockY = 0; blockY < Chunk.SizeY; blockY++)
|
for (int blockY = 0; blockY < Chunk.SizeY; blockY++)
|
||||||
for (int blockZ = 0; blockZ < Chunk.SizeZ; blockZ++)
|
for (int blockZ = 0; blockZ < Chunk.SizeZ; blockZ++)
|
||||||
for (int blockX = 0; blockX < Chunk.SizeX; blockX++)
|
for (int blockX = 0; blockX < Chunk.SizeX; blockX++)
|
||||||
chunk[blockX, blockY, blockZ] = new Block(queue.Dequeue());
|
chunk.SetWithoutCheck(blockX, blockY, blockZ, new Block(queue.Dequeue()));
|
||||||
|
|
||||||
// check before store chunk
|
// check before store chunk
|
||||||
if (cancellationToken.IsCancellationRequested)
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
|
@ -435,9 +424,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
//We have our chunk, save the chunk into the world
|
//We have our chunk, save the chunk into the world
|
||||||
handler.InvokeOnMainThread(() =>
|
handler.InvokeOnMainThread(() =>
|
||||||
{
|
{
|
||||||
if (handler.GetWorld()[chunkX, chunkZ] == null)
|
world.StoreChunk(chunkX, chunkY, chunkZ, chunkColumnSize, chunk, chunkY == maxChunkY);
|
||||||
handler.GetWorld()[chunkX, chunkZ] = new ChunkColumn();
|
|
||||||
handler.GetWorld()[chunkX, chunkZ]![chunkY] = chunk;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -469,7 +456,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
//Unload the entire chunk column
|
//Unload the entire chunk column
|
||||||
handler.InvokeOnMainThread(() =>
|
handler.InvokeOnMainThread(() =>
|
||||||
{
|
{
|
||||||
handler.GetWorld()[chunkX, chunkZ] = null;
|
world[chunkX, chunkZ] = null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -505,7 +492,8 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
dataTypes.ReadData(Chunk.SizeX * Chunk.SizeZ, cache); //Biomes
|
dataTypes.ReadData(Chunk.SizeX * Chunk.SizeZ, cache); //Biomes
|
||||||
|
|
||||||
//Load chunk data
|
//Load chunk data
|
||||||
for (int chunkY = 0; chunkY < chunkColumnSize; chunkY++)
|
int maxChunkY = sizeof(int) * 8 - 1 - BitOperations.LeadingZeroCount(chunkMask);
|
||||||
|
for (int chunkY = 0; chunkY <= maxChunkY; chunkY++)
|
||||||
{
|
{
|
||||||
if ((chunkMask & (1 << chunkY)) != 0)
|
if ((chunkMask & (1 << chunkY)) != 0)
|
||||||
{
|
{
|
||||||
|
|
@ -514,7 +502,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
for (int blockY = 0; blockY < Chunk.SizeY; blockY++)
|
for (int blockY = 0; blockY < Chunk.SizeY; blockY++)
|
||||||
for (int blockZ = 0; blockZ < Chunk.SizeZ; blockZ++)
|
for (int blockZ = 0; blockZ < Chunk.SizeZ; blockZ++)
|
||||||
for (int blockX = 0; blockX < Chunk.SizeX; blockX++)
|
for (int blockX = 0; blockX < Chunk.SizeX; blockX++)
|
||||||
chunk[blockX, blockY, blockZ] = new Block(blockTypes.Dequeue(), blockMeta.Dequeue());
|
chunk.SetWithoutCheck(blockX, blockY, blockZ, new Block(blockTypes.Dequeue(), blockMeta.Dequeue()));
|
||||||
|
|
||||||
// check before store chunk
|
// check before store chunk
|
||||||
if (cancellationToken.IsCancellationRequested)
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
|
@ -522,19 +510,12 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
|
|
||||||
handler.InvokeOnMainThread(() =>
|
handler.InvokeOnMainThread(() =>
|
||||||
{
|
{
|
||||||
if (handler.GetWorld()[chunkX, chunkZ] == null)
|
world.StoreChunk(chunkX, chunkY, chunkZ, chunkColumnSize, chunk, chunkY == maxChunkY);
|
||||||
handler.GetWorld()[chunkX, chunkZ] = new ChunkColumn();
|
|
||||||
handler.GetWorld()[chunkX, chunkZ]![chunkY] = chunk;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
handler.InvokeOnMainThread(() =>
|
|
||||||
{
|
|
||||||
if (handler.GetWorld()[chunkX, chunkZ] != null)
|
|
||||||
handler.GetWorld()[chunkX, chunkZ]!.FullyLoaded = true;
|
|
||||||
});
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ using System.Text.RegularExpressions;
|
||||||
using MinecraftClient.Protocol.Session;
|
using MinecraftClient.Protocol.Session;
|
||||||
using MinecraftClient.Protocol;
|
using MinecraftClient.Protocol;
|
||||||
using MinecraftClient.Mapping;
|
using MinecraftClient.Mapping;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
namespace MinecraftClient
|
namespace MinecraftClient
|
||||||
{
|
{
|
||||||
|
|
@ -261,29 +263,39 @@ namespace MinecraftClient
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string[] Lines = File.ReadAllLines(file);
|
string[] Lines = File.ReadAllLines(file);
|
||||||
Section section = Section.Default;
|
|
||||||
foreach (string lineRAW in Lines)
|
|
||||||
{
|
|
||||||
string line = section == Section.Main && lineRAW.ToLower().Trim().StartsWith("password")
|
|
||||||
? lineRAW.Trim() //Do not strip # in passwords
|
|
||||||
: lineRAW.Split('#')[0].Trim();
|
|
||||||
|
|
||||||
if (line.Length > 0)
|
ConcurrentDictionary<int, Section> sectionInfo = new();
|
||||||
|
Parallel.For(0, Lines.Length, idx =>
|
||||||
|
{
|
||||||
|
string line = Lines[idx].Split('#')[0].Trim();
|
||||||
|
if (line.Length > 2 && line[0] == '[' && line[^1] == ']')
|
||||||
|
sectionInfo[idx] = GetSection(line[1..^1]);
|
||||||
|
});
|
||||||
|
|
||||||
|
Section section = Section.Default;
|
||||||
|
for (int idx = 0; idx < Lines.Length; ++idx)
|
||||||
|
{
|
||||||
|
if (sectionInfo.ContainsKey(idx))
|
||||||
{
|
{
|
||||||
if (line[0] == '[' && line[line.Length - 1] == ']')
|
section = sectionInfo[idx];
|
||||||
{
|
continue;
|
||||||
section = GetSection(line.Substring(1, line.Length - 2).ToLower());
|
}
|
||||||
}
|
|
||||||
else
|
Parallel.Invoke(() =>
|
||||||
|
{
|
||||||
|
string line = Lines[idx].Split('#')[0].Trim();
|
||||||
|
if (line.Length > 1)
|
||||||
{
|
{
|
||||||
string argName = line.Split('=')[0];
|
string argName = line.Split('=')[0];
|
||||||
|
if (section == Section.Main && argName == "password")
|
||||||
|
line = Lines[idx].Trim(); //Do not strip # in passwords
|
||||||
if (line.Length > (argName.Length + 1))
|
if (line.Length > (argName.Length + 1))
|
||||||
{
|
{
|
||||||
string argValue = line.Substring(argName.Length + 1);
|
string argValue = line[(argName.Length + 1)..];
|
||||||
LoadSingleSetting(section, argName, argValue);
|
LoadSingleSetting(section, argName, argValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (IOException) { }
|
catch (IOException) { }
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue