mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2026-08-15 13:04:36 +00:00
chores: Minor Optimizations
This commit is contained in:
commit
3557239c43
7 changed files with 113 additions and 25 deletions
|
|
@ -1,3 +1,4 @@
|
|||
using System;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
|
|
@ -23,10 +24,46 @@ public static class Json
|
|||
public static JsonNode? ParseJson(string? json)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(json)) return null;
|
||||
ReadOnlySpan<char> text = json.AsSpan().TrimStart();
|
||||
if (!LooksLikeJson(text))
|
||||
return JsonValue.Create(json);
|
||||
|
||||
try { return JsonNode.Parse(json); }
|
||||
catch (JsonException) { return JsonValue.Create(json); }
|
||||
}
|
||||
|
||||
private static bool LooksLikeJson(ReadOnlySpan<char> text)
|
||||
{
|
||||
if (text.IsEmpty)
|
||||
return false;
|
||||
|
||||
return text[0] switch
|
||||
{
|
||||
'{' or '"' => true,
|
||||
'[' => LooksLikeJsonArray(text[1..]),
|
||||
'-' => text.Length > 1 && char.IsAsciiDigit(text[1]),
|
||||
>= '0' and <= '9' => true,
|
||||
't' or 'f' or 'n' => true,
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
|
||||
private static bool LooksLikeJsonArray(ReadOnlySpan<char> text)
|
||||
{
|
||||
text = text.TrimStart();
|
||||
if (text.IsEmpty)
|
||||
return false;
|
||||
|
||||
return text[0] switch
|
||||
{
|
||||
']' or '{' or '[' or '"' => true,
|
||||
'-' => text.Length > 1 && char.IsAsciiDigit(text[1]),
|
||||
>= '0' and <= '9' => true,
|
||||
't' or 'f' or 'n' => true,
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Escape a string for embedding inside a JSON string literal.
|
||||
/// Uses System.Text.Json serialization and strips the surrounding quotes.
|
||||
|
|
@ -52,4 +89,4 @@ public static class JsonNodeExtensions
|
|||
JsonValue val when val.TryGetValue<string>(out var s) => s,
|
||||
_ => node.ToJsonString()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -232,8 +232,8 @@ namespace MinecraftClient.Mapping
|
|||
int tentativeGScore = current.GScore + (int)current.Location.DistanceSquared(neighbor);
|
||||
|
||||
// If the neighbor is not in the GScoreDict OR its current tentativeGScore is lower than the previously saved one:
|
||||
if (!gScoreDict.ContainsKey(neighbor) ||
|
||||
(gScoreDict.ContainsKey(neighbor) && tentativeGScore < gScoreDict[neighbor]))
|
||||
if (!gScoreDict.TryGetValue(neighbor, out int existingGScore) ||
|
||||
tentativeGScore < existingGScore)
|
||||
{
|
||||
// Save the new relation between the neighbored block and the current one
|
||||
cameFrom[neighbor] = current.Location;
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@ namespace MinecraftClient.Mapping
|
|||
{
|
||||
/// <summary>
|
||||
/// The chunks contained into the Minecraft world
|
||||
/// Tuple<int, int>: Tuple<chunkX, chunkZ>
|
||||
/// (int ChunkX, int ChunkZ): chunkX, chunkZ
|
||||
/// </summary>
|
||||
private ConcurrentDictionary<Tuple<int, int>, ChunkColumn> chunks = new();
|
||||
private ConcurrentDictionary<(int ChunkX, int ChunkZ), ChunkColumn> chunks = new();
|
||||
|
||||
/// <summary>
|
||||
/// The dimension info of the world
|
||||
|
|
@ -49,12 +49,12 @@ namespace MinecraftClient.Mapping
|
|||
{
|
||||
get
|
||||
{
|
||||
chunks.TryGetValue(new(chunkX, chunkZ), out ChunkColumn? chunkColumn);
|
||||
chunks.TryGetValue((chunkX, chunkZ), out ChunkColumn? chunkColumn);
|
||||
return chunkColumn;
|
||||
}
|
||||
set
|
||||
{
|
||||
Tuple<int, int> chunkCoord = new(chunkX, chunkZ);
|
||||
var chunkCoord = (chunkX, chunkZ);
|
||||
if (value is null)
|
||||
chunks.TryRemove(chunkCoord, out _);
|
||||
else
|
||||
|
|
@ -361,7 +361,7 @@ namespace MinecraftClient.Mapping
|
|||
/// <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 = chunks.GetOrAdd(new(chunkX, chunkZ), (_) => new(chunkColumnSize));
|
||||
ChunkColumn chunkColumn = chunks.GetOrAdd((chunkX, chunkZ), (_) => new(chunkColumnSize));
|
||||
chunkColumn[chunkY] = chunk;
|
||||
if (loadCompleted)
|
||||
chunkColumn.FullyLoaded = true;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Frozen;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
|
@ -19,7 +20,7 @@ namespace MinecraftClient.Physics
|
|||
private static readonly Aabb[] FullBlockArray = { FullBlock };
|
||||
private static readonly Aabb[] EmptyArray = Array.Empty<Aabb>();
|
||||
|
||||
private static Dictionary<int, Aabb[]>? stateToShape;
|
||||
private static FrozenDictionary<int, Aabb[]>? stateToShape;
|
||||
private static Dictionary<string, object>? prismarineBlocks;
|
||||
private static Dictionary<int, Aabb[]>? prismarineShapes;
|
||||
|
||||
|
|
@ -136,14 +137,21 @@ namespace MinecraftClient.Physics
|
|||
|
||||
private static void BuildStateMap()
|
||||
{
|
||||
stateToShape = new Dictionary<int, Aabb[]>();
|
||||
var builder = new Dictionary<int, Aabb[]>();
|
||||
|
||||
if (prismarineBlocks is null || prismarineShapes is null)
|
||||
{
|
||||
stateToShape = builder.ToFrozenDictionary();
|
||||
return;
|
||||
}
|
||||
|
||||
var palette = Block.Palette;
|
||||
var dict = GetPaletteDict(palette);
|
||||
if (dict is null) return;
|
||||
if (dict is null)
|
||||
{
|
||||
stateToShape = builder.ToFrozenDictionary();
|
||||
return;
|
||||
}
|
||||
|
||||
// Group consecutive state IDs by Material to find state ranges per block
|
||||
var materialRanges = new Dictionary<Material, List<(int start, int end)>>();
|
||||
|
|
@ -182,20 +190,20 @@ namespace MinecraftClient.Physics
|
|||
{
|
||||
var shapes = prismarineShapes.GetValueOrDefault(singleShapeId, EmptyArray);
|
||||
for (int sid = start; sid <= end; sid++)
|
||||
stateToShape[sid] = shapes;
|
||||
builder[sid] = shapes;
|
||||
}
|
||||
else if (blockShapeData is List<int> shapeIdList)
|
||||
{
|
||||
for (int i = 0; i < stateCount && (globalStateOffset + i) < shapeIdList.Count; i++)
|
||||
{
|
||||
int shapeId = shapeIdList[globalStateOffset + i];
|
||||
stateToShape[start + i] = prismarineShapes.GetValueOrDefault(shapeId, EmptyArray);
|
||||
builder[start + i] = prismarineShapes.GetValueOrDefault(shapeId, EmptyArray);
|
||||
}
|
||||
}
|
||||
globalStateOffset += stateCount;
|
||||
}
|
||||
}
|
||||
|
||||
stateToShape = builder.ToFrozenDictionary();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -3918,26 +3918,42 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
/// <param name="packetData">packet Data</param>
|
||||
private void SendPacket(int packetId, IEnumerable<byte> packetData)
|
||||
{
|
||||
byte[] payload = packetData as byte[] ?? packetData.ToArray();
|
||||
|
||||
if (handler.GetNetworkPacketCaptureEnabled())
|
||||
{
|
||||
var clone = packetData.ToList();
|
||||
handler.OnNetworkPacket(packetId, clone, currentState == CurrentState.Login, false);
|
||||
handler.OnNetworkPacket(packetId, payload.ToList(), currentState == CurrentState.Login, false);
|
||||
}
|
||||
|
||||
//log.Info($"[C -> S] Sending packet {packetId:X} > {dataTypes.ByteArrayToString(packetData.ToArray())}");
|
||||
|
||||
//The inner packet
|
||||
var thePacket = dataTypes.ConcatBytes(DataTypes.GetVarInt(packetId), packetData.ToArray());
|
||||
byte[] packetIdBytes = DataTypes.GetVarInt(packetId);
|
||||
byte[] thePacket = new byte[packetIdBytes.Length + payload.Length];
|
||||
Buffer.BlockCopy(packetIdBytes, 0, thePacket, 0, packetIdBytes.Length);
|
||||
Buffer.BlockCopy(payload, 0, thePacket, packetIdBytes.Length, payload.Length);
|
||||
|
||||
if (compression_treshold >= 0) //Compression enabled?
|
||||
{
|
||||
thePacket = thePacket.Length >= compression_treshold
|
||||
? dataTypes.ConcatBytes(DataTypes.GetVarInt(thePacket.Length), ZlibUtils.Compress(thePacket))
|
||||
: dataTypes.ConcatBytes(DataTypes.GetVarInt(0), thePacket);
|
||||
byte[] compressedHeader = thePacket.Length >= compression_treshold
|
||||
? DataTypes.GetVarInt(thePacket.Length)
|
||||
: DataTypes.GetVarInt(0);
|
||||
byte[] compressedPayload = thePacket.Length >= compression_treshold
|
||||
? ZlibUtils.Compress(thePacket)
|
||||
: thePacket;
|
||||
|
||||
byte[] compressedPacket = new byte[compressedHeader.Length + compressedPayload.Length];
|
||||
Buffer.BlockCopy(compressedHeader, 0, compressedPacket, 0, compressedHeader.Length);
|
||||
Buffer.BlockCopy(compressedPayload, 0, compressedPacket, compressedHeader.Length, compressedPayload.Length);
|
||||
thePacket = compressedPacket;
|
||||
}
|
||||
|
||||
//log.Debug("[C -> S] Sending packet " + packetId + " > " + dataTypes.ByteArrayToString(dataTypes.ConcatBytes(dataTypes.GetVarInt(thePacket.Length), thePacket)));
|
||||
socketWrapper.SendDataRAW(dataTypes.ConcatBytes(DataTypes.GetVarInt(thePacket.Length), thePacket));
|
||||
byte[] packetLengthBytes = DataTypes.GetVarInt(thePacket.Length);
|
||||
byte[] fullPacket = new byte[packetLengthBytes.Length + thePacket.Length];
|
||||
Buffer.BlockCopy(packetLengthBytes, 0, fullPacket, 0, packetLengthBytes.Length);
|
||||
Buffer.BlockCopy(thePacket, 0, fullPacket, packetLengthBytes.Length, thePacket.Length);
|
||||
socketWrapper.SendDataRAW(fullPacket);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -4,8 +4,6 @@ MCC.LoadBot(new PacketCadenceCaptureBot());
|
|||
|
||||
//MCCScript Extensions
|
||||
|
||||
using System.Threading;
|
||||
|
||||
public class PacketCadenceCaptureBot : ChatBot
|
||||
{
|
||||
private const int CaptureDurationSeconds = 5;
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ EOF
|
|||
SERVER_DIR="${1:-}"
|
||||
MC_VERSION="${2:-}"
|
||||
PROFILE="${3:-}"
|
||||
SERVER_PORT=""
|
||||
|
||||
if [[ -z "$SERVER_DIR" || -z "$MC_VERSION" || -z "$PROFILE" ]]; then
|
||||
usage >&2
|
||||
|
|
@ -62,6 +63,21 @@ wait_for_file_pattern() {
|
|||
return 1
|
||||
}
|
||||
|
||||
wait_for_rcon_port_free() {
|
||||
local timeout="${1:-30}"
|
||||
local elapsed=0
|
||||
|
||||
while (( elapsed < timeout )); do
|
||||
if ! ss -ltn '( sport = :25575 )' 2>/dev/null | grep -Fq ':25575'; then
|
||||
return 0
|
||||
fi
|
||||
sleep 1
|
||||
((elapsed += 1))
|
||||
done
|
||||
|
||||
echo "Timed out waiting for RCON port 25575 to become free" >&2
|
||||
return 1
|
||||
}
|
||||
cleanup() {
|
||||
if [[ -n "${MCC_PID:-}" ]] && kill -0 "$MCC_PID" 2>/dev/null; then
|
||||
echo "quit" >> "$INPUT_FILE" 2>/dev/null || true
|
||||
|
|
@ -76,12 +92,24 @@ cleanup() {
|
|||
fi
|
||||
|
||||
tmux kill-session -t "$SESSION_NAME" 2>/dev/null || true
|
||||
wait_for_rcon_port_free 30 || true
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
prepare_config() {
|
||||
bash "$REPO_ROOT/.skills/mcc-integration-testing/scripts/prepare_offline_mcc_config.sh" "$CFG" "$MC_VERSION" CursorBot >/dev/null
|
||||
MCC_TEST_ACCOUNT_TYPE=mojang MCC_TEST_PASSWORD=- \
|
||||
bash "$REPO_ROOT/.skills/mcc-integration-testing/scripts/prepare_offline_mcc_config.sh" \
|
||||
"$REPO_ROOT/MinecraftClient.ini" "$CFG" "$MC_VERSION" CursorBot >/dev/null
|
||||
|
||||
sed_in_place \
|
||||
-e "s#^Server = .*#Server = { Host = \"localhost\", Port = $SERVER_PORT }#" \
|
||||
-e 's/TerrainAndMovements = false/TerrainAndMovements = true/' \
|
||||
-e 's/InventoryHandling = false/InventoryHandling = true/' \
|
||||
-e 's/EntityHandling = false/EntityHandling = true/' \
|
||||
-e 's/AutoRespawn = false/AutoRespawn = true/' \
|
||||
"$CFG"
|
||||
disable_noisy_bots_in_ini "$CFG"
|
||||
}
|
||||
|
||||
send_mcc_command() {
|
||||
|
|
@ -156,7 +184,7 @@ modern_mob_and_effects() {
|
|||
|
||||
bash "$REPO_ROOT/.skills/mcc-integration-testing/scripts/preflight_test_env.sh" "$SERVER_DIR" >/dev/null
|
||||
bash "$REPO_ROOT/.skills/mcc-integration-testing/scripts/reset_shared_test_state.sh" --all >/dev/null
|
||||
prepare_config
|
||||
wait_for_rcon_port_free 30 || true
|
||||
rm -f "$MCC_LOG" "$INPUT_FILE"
|
||||
|
||||
bash "$REPO_ROOT/.skills/mcc-integration-testing/scripts/ensure_offline_server.sh" "$SERVER_DIR" >/dev/null
|
||||
|
|
@ -167,6 +195,7 @@ fi
|
|||
|
||||
mc-start "$SERVER_DIR" >/dev/null
|
||||
wait_for_server_ready "$SERVER_DIR" || exit 1
|
||||
prepare_config
|
||||
|
||||
: > "$INPUT_FILE"
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue