mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2026-08-15 13:04:36 +00:00
Merge pull request #2944 from milutinke/net-10
This commit is contained in:
commit
1ac9df86a4
23 changed files with 660 additions and 95 deletions
7
.github/workflows/build-and-release.yml
vendored
7
.github/workflows/build-and-release.yml
vendored
|
|
@ -8,7 +8,7 @@ on:
|
|||
|
||||
env:
|
||||
PROJECT: "MinecraftClient"
|
||||
target-version: "net8.0"
|
||||
target-version: "net10.0"
|
||||
compile-flags: "--self-contained=true -c Release -p:UseAppHost=true -p:IncludeNativeLibrariesForSelfExtract=true -p:EnableCompressionInSingleFile=true -p:DebugType=Embedded"
|
||||
|
||||
jobs:
|
||||
|
|
@ -45,6 +45,11 @@ jobs:
|
|||
run: |
|
||||
echo project-path=${{ github.workspace }}/${{ env.PROJECT }} >> $GITHUB_ENV
|
||||
echo file-ext=${{ (startsWith(matrix.target, 'win') && '.exe') || ' ' }} >> $GITHUB_ENV
|
||||
|
||||
- name: Setup .NET SDK
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 10.0.x
|
||||
|
||||
- name: Setup Environment Variables
|
||||
run: |
|
||||
|
|
|
|||
56
.skills/mcc-integration-testing/SKILL.md
Normal file
56
.skills/mcc-integration-testing/SKILL.md
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
---
|
||||
name: mcc-integration-testing
|
||||
description: Repeatable local offline integration testing for Minecraft Console Client against a local Minecraft Java server. Use this whenever the user wants to validate MCC end-to-end against a local server, switch the server to persistent offline mode, run chat or server commands through FileInputBot, exercise inventory/entity handling, or perform deeper smoke testing with mobs, particles, sounds, TNT, and operator actions.
|
||||
---
|
||||
|
||||
# MCC Integration Testing
|
||||
|
||||
Use this skill for local MCC validation against the user's `mc-*` and `mcc-*` shell helpers.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Source `~/.zshrc` in command invocations.
|
||||
2. Do not read `~/.zshrc` directly.
|
||||
3. Ensure the target server is configured for persistent offline testing:
|
||||
- `online-mode=false`
|
||||
- `enforce-secure-profile=false`
|
||||
- `enable-rcon=true`
|
||||
- `rcon.password=test123`
|
||||
4. Build with `mcc-build`.
|
||||
5. Run the scripted scenario with `scripts/run_full_spectrum_test.sh`.
|
||||
6. Summarize the evidence with `scripts/summarize_test_run.sh`.
|
||||
|
||||
## Required Preconditions
|
||||
|
||||
- Server jar exists under `~/Minecraft/Servers/<version>/server.jar`
|
||||
- `eula.txt` contains `eula=true`
|
||||
- Repo root `MinecraftClient.ini` is the offline MCC test profile
|
||||
- The MCC config round-trip issue must be fixed before relying on repeated launches
|
||||
|
||||
## Scripts
|
||||
|
||||
- `scripts/ensure_offline_server.sh`
|
||||
- Generates `server.properties` if missing
|
||||
- Applies persistent offline and RCON settings
|
||||
- `scripts/run_full_spectrum_test.sh`
|
||||
- Builds MCC
|
||||
- Starts server and MCC
|
||||
- Runs the full-spectrum scenario
|
||||
- Verifies key MCC and server log assertions
|
||||
- `scripts/summarize_test_run.sh`
|
||||
- Prints the most relevant evidence from the latest run directory
|
||||
|
||||
## Scenario Coverage
|
||||
|
||||
The scripted run should cover:
|
||||
|
||||
- offline join
|
||||
- MCC-originated chat
|
||||
- MCC-originated slash command
|
||||
- internal MCC commands such as `health`, `list`, `inventory`, and `entity`
|
||||
- OP + creative mode
|
||||
- passive and hostile mob spawns
|
||||
- representative sound and particle events
|
||||
- TNT / explosion handling
|
||||
|
||||
If a command syntax needs to be checked, use `references/command-matrix.md`.
|
||||
39
.skills/mcc-integration-testing/evals/evals.json
Normal file
39
.skills/mcc-integration-testing/evals/evals.json
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"skill_name": "mcc-integration-testing",
|
||||
"evals": [
|
||||
{
|
||||
"id": 1,
|
||||
"prompt": "Configure the local 1.21.11 MCC test server for persistent offline mode, then run a deep MCC integration test covering chat, operator actions, creative inventory, entity tracking, sounds, particles, and TNT.",
|
||||
"expected_output": "The server is left in offline mode with RCON enabled, MCC joins successfully, and the run reports clear pass or fail evidence from both MCC and server logs.",
|
||||
"files": [],
|
||||
"expectations": [
|
||||
"The workflow sources ~/.zshrc without reading it directly.",
|
||||
"The server is configured with online-mode=false and enable-rcon=true.",
|
||||
"The run uses both mc-rcon and mcc-cmd.",
|
||||
"The result includes evidence from MCC output and server logs."
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"prompt": "Validate that MCC still works after a runtime or framework change by performing a repeatable offline smoke test against the local vanilla server and exercising entity and inventory handling.",
|
||||
"expected_output": "The response runs the repeatable local workflow, checks for a successful join, verifies inventory and entity commands, and calls out any configuration or protocol regression.",
|
||||
"files": [],
|
||||
"expectations": [
|
||||
"The workflow builds MCC before running the server scenario.",
|
||||
"The workflow checks inventory and entity handling explicitly.",
|
||||
"The response flags config reload failures as regressions."
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"prompt": "Use the local MCC testing workflow to run a full-spectrum client/server exercise and summarize the important evidence only.",
|
||||
"expected_output": "The response runs the scripted scenario and returns a concise summary with pass/fail status, affected commands, and log evidence.",
|
||||
"files": [],
|
||||
"expectations": [
|
||||
"The workflow uses the scripted test runner.",
|
||||
"The summary includes join status, MCC command coverage, and server-side effects.",
|
||||
"The summary points to the saved log locations."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
53
.skills/mcc-integration-testing/references/command-matrix.md
Normal file
53
.skills/mcc-integration-testing/references/command-matrix.md
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
# Command Matrix
|
||||
|
||||
This skill uses a fixed set of stable commands for local offline integration testing.
|
||||
|
||||
## MCC-side commands via `mcc-cmd`
|
||||
|
||||
- `health`
|
||||
- `list`
|
||||
- `inventory player list`
|
||||
- `/gamemode creative`
|
||||
- `inventory creativegive 36 Diamond 16`
|
||||
- `entity`
|
||||
- `/time query daytime`
|
||||
- `smoke_test_from_mcc_full_spectrum`
|
||||
|
||||
Notes:
|
||||
- Lines starting with `/` are sent to the server as chat/commands.
|
||||
- Non-slash lines are treated as MCC internal commands first, then fall back to chat.
|
||||
|
||||
## Server-side commands via `mc-rcon`
|
||||
|
||||
- `op CursorBot`
|
||||
- `gamerule sendCommandFeedback true`
|
||||
- `gamerule logAdminCommands true`
|
||||
- `time set day`
|
||||
- `weather clear`
|
||||
|
||||
## Representative entity coverage
|
||||
|
||||
- `execute as CursorBot at @s run summon minecraft:cow ~2 ~ ~`
|
||||
- `execute as CursorBot at @s run summon minecraft:zombie ~4 ~ ~`
|
||||
- `execute as CursorBot at @s run summon minecraft:creeper ~6 ~ ~`
|
||||
- `execute as CursorBot at @s run summon minecraft:skeleton ~8 ~ ~`
|
||||
- `execute as CursorBot at @s run summon minecraft:villager ~-2 ~ ~`
|
||||
- `execute as CursorBot at @s run summon minecraft:allay ~-4 ~ ~`
|
||||
- `execute as CursorBot at @s run summon minecraft:armor_stand ~ ~ ~2`
|
||||
|
||||
## Representative particle coverage
|
||||
|
||||
- `execute as CursorBot at @s run particle minecraft:happy_villager ~ ~1 ~ 0.5 0.5 0.5 0 12 force`
|
||||
- `execute as CursorBot at @s run particle minecraft:end_rod ~ ~1 ~ 0.5 0.5 0.5 0.01 20 force`
|
||||
- `execute as CursorBot at @s run particle minecraft:explosion ~ ~1 ~ 0 0 0 0 1 force`
|
||||
- `execute as CursorBot at @s run particle minecraft:totem_of_undying ~ ~1 ~ 0.5 0.5 0.5 0.1 20 force`
|
||||
|
||||
## Representative sound coverage
|
||||
|
||||
- `execute as CursorBot at @s run playsound minecraft:entity.lightning_bolt.thunder master CursorBot ~ ~ ~ 1 1 0`
|
||||
- `execute as CursorBot at @s run playsound minecraft:block.note_block.bell master CursorBot ~ ~ ~ 1 1 0`
|
||||
|
||||
## Explosion coverage
|
||||
|
||||
- `execute as CursorBot at @s run summon minecraft:tnt ~3 ~ ~`
|
||||
- `execute as CursorBot at @s run summon minecraft:tnt ~6 ~ ~`
|
||||
84
.skills/mcc-integration-testing/scripts/ensure_offline_server.sh
Executable file
84
.skills/mcc-integration-testing/scripts/ensure_offline_server.sh
Executable file
|
|
@ -0,0 +1,84 @@
|
|||
#!/usr/bin/env zsh
|
||||
set -euo pipefail
|
||||
|
||||
set +eu
|
||||
source ~/.zshrc
|
||||
set -eu
|
||||
|
||||
VERSION="${1:-1.21.11-Vanilla}"
|
||||
SERVER_DIR="${MCC_SERVERS:?}/$VERSION"
|
||||
PROPS_FILE="$SERVER_DIR/server.properties"
|
||||
SESSION_NAME="mc-${VERSION//./_}"
|
||||
|
||||
if [[ ! -d "$SERVER_DIR" ]]; then
|
||||
echo "Server directory not found: $SERVER_DIR" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "$SERVER_DIR/eula.txt" ]] || ! grep -Eq '^eula=true$' "$SERVER_DIR/eula.txt"; then
|
||||
echo "Missing accepted EULA in $SERVER_DIR/eula.txt" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
server_running() {
|
||||
mc-list | grep -Fq "$SESSION_NAME"
|
||||
}
|
||||
|
||||
wait_for_server_ready() {
|
||||
local timeout="${1:-60}"
|
||||
local elapsed=0
|
||||
while (( elapsed < timeout )); do
|
||||
if mc-log "$VERSION" 200 2>/dev/null | grep -Fq "Done ("; then
|
||||
return 0
|
||||
fi
|
||||
sleep 1
|
||||
((elapsed += 1))
|
||||
done
|
||||
echo "Timed out waiting for $VERSION to become ready" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
wait_for_server_stop() {
|
||||
local timeout="${1:-60}"
|
||||
local elapsed=0
|
||||
while (( elapsed < timeout )); do
|
||||
if ! server_running; then
|
||||
return 0
|
||||
fi
|
||||
sleep 1
|
||||
((elapsed += 1))
|
||||
done
|
||||
echo "Timed out waiting for $VERSION to stop" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
upsert_property() {
|
||||
local key="$1"
|
||||
local value="$2"
|
||||
|
||||
if grep -Eq "^${key}=" "$PROPS_FILE"; then
|
||||
sed -i "s#^${key}=.*#${key}=${value}#" "$PROPS_FILE"
|
||||
else
|
||||
printf '%s=%s\n' "$key" "$value" >> "$PROPS_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ ! -f "$PROPS_FILE" ]]; then
|
||||
mc-start "$VERSION"
|
||||
wait_for_server_ready
|
||||
mc-stop "$VERSION"
|
||||
wait_for_server_stop
|
||||
fi
|
||||
|
||||
if server_running; then
|
||||
mc-stop "$VERSION"
|
||||
wait_for_server_stop
|
||||
fi
|
||||
|
||||
upsert_property "online-mode" "false"
|
||||
upsert_property "enforce-secure-profile" "false"
|
||||
upsert_property "enable-rcon" "true"
|
||||
upsert_property "rcon.port" "25575"
|
||||
upsert_property "rcon.password" "test123"
|
||||
|
||||
echo "Configured $VERSION for persistent offline testing"
|
||||
219
.skills/mcc-integration-testing/scripts/run_full_spectrum_test.sh
Executable file
219
.skills/mcc-integration-testing/scripts/run_full_spectrum_test.sh
Executable file
|
|
@ -0,0 +1,219 @@
|
|||
#!/usr/bin/env zsh
|
||||
set -euo pipefail
|
||||
|
||||
set +eu
|
||||
source ~/.zshrc
|
||||
set -eu
|
||||
|
||||
SCRIPT_DIR="${0:A:h}"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
|
||||
VERSION="${1:-1.21.11-Vanilla}"
|
||||
RUN_ROOT="${TMPDIR:-/tmp}/mcc-integration-testing"
|
||||
RUN_ID="$(date +%Y%m%d-%H%M%S)"
|
||||
RUN_DIR="$RUN_ROOT/$RUN_ID"
|
||||
SERVER_LOG_FILE="$MCC_SERVERS/$VERSION/logs/latest.log"
|
||||
MCC_LOG="$RUN_DIR/mcc.log"
|
||||
BUILD_LOG="$RUN_DIR/build.log"
|
||||
SERVER_TMUX_LOG="$RUN_DIR/server-tmux.log"
|
||||
SERVER_FILE_LOG="$RUN_DIR/server-latest.log"
|
||||
INPUT_FILE="$REPO_ROOT/mcc_input.txt"
|
||||
MCC_PID=""
|
||||
|
||||
mkdir -p "$RUN_DIR"
|
||||
|
||||
cleanup() {
|
||||
if [[ -n "${MCC_PID:-}" ]] && kill -0 "$MCC_PID" 2>/dev/null; then
|
||||
mcc-cmd "quit" >/dev/null 2>&1 || true
|
||||
sleep 2
|
||||
kill "$MCC_PID" 2>/dev/null || true
|
||||
wait "$MCC_PID" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
mc-stop "$VERSION" >/dev/null 2>&1 || true
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
wait_for_file_pattern() {
|
||||
local file="$1"
|
||||
local pattern="$2"
|
||||
local description="$3"
|
||||
local timeout="${4:-60}"
|
||||
local elapsed=0
|
||||
|
||||
while (( elapsed < timeout )); do
|
||||
if [[ -f "$file" ]] && grep -Fq "$pattern" "$file"; then
|
||||
return 0
|
||||
fi
|
||||
sleep 1
|
||||
((elapsed += 1))
|
||||
done
|
||||
|
||||
echo "Timed out waiting for: $description" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
wait_for_server_ready() {
|
||||
local timeout="${1:-60}"
|
||||
local elapsed=0
|
||||
|
||||
while (( elapsed < timeout )); do
|
||||
if mc-log "$VERSION" 250 2>/dev/null | grep -Fq "Done ("; then
|
||||
return 0
|
||||
fi
|
||||
sleep 1
|
||||
((elapsed += 1))
|
||||
done
|
||||
|
||||
echo "Timed out waiting for server readiness" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
wait_for_server_log_pattern() {
|
||||
local pattern="$1"
|
||||
local description="$2"
|
||||
local timeout="${3:-60}"
|
||||
local elapsed=0
|
||||
|
||||
while (( elapsed < timeout )); do
|
||||
if [[ -f "$SERVER_LOG_FILE" ]] && grep -Fq "$pattern" "$SERVER_LOG_FILE"; then
|
||||
return 0
|
||||
fi
|
||||
sleep 1
|
||||
((elapsed += 1))
|
||||
done
|
||||
|
||||
echo "Timed out waiting for server log: $description" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
capture_server_logs() {
|
||||
mc-log "$VERSION" 400 > "$SERVER_TMUX_LOG" 2>/dev/null || true
|
||||
if [[ -f "$SERVER_LOG_FILE" ]]; then
|
||||
cp "$SERVER_LOG_FILE" "$SERVER_FILE_LOG"
|
||||
fi
|
||||
}
|
||||
|
||||
fail() {
|
||||
capture_server_logs
|
||||
echo "FAIL: $1" >&2
|
||||
echo "Run directory: $RUN_DIR" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
assert_contains() {
|
||||
local file="$1"
|
||||
local pattern="$2"
|
||||
local description="$3"
|
||||
|
||||
grep -Fq "$pattern" "$file" || fail "$description"
|
||||
}
|
||||
|
||||
assert_not_contains() {
|
||||
local file="$1"
|
||||
local pattern="$2"
|
||||
local description="$3"
|
||||
|
||||
if grep -Fq "$pattern" "$file"; then
|
||||
fail "$description"
|
||||
fi
|
||||
}
|
||||
|
||||
run_server_command() {
|
||||
local cmd="$1"
|
||||
echo "SERVER> $cmd"
|
||||
mc-rcon "$cmd" >/dev/null || fail "Server command failed: $cmd"
|
||||
}
|
||||
|
||||
run_mcc_command() {
|
||||
local cmd="$1"
|
||||
echo "MCC> $cmd"
|
||||
mcc-cmd "$cmd"
|
||||
sleep 2
|
||||
}
|
||||
|
||||
"$SCRIPT_DIR/ensure_offline_server.sh" "$VERSION"
|
||||
|
||||
: > "$INPUT_FILE"
|
||||
|
||||
echo "Building MCC..."
|
||||
mcc-build > "$BUILD_LOG" 2>&1 || fail "mcc-build failed"
|
||||
|
||||
echo "Starting server..."
|
||||
mc-start "$VERSION" >/dev/null
|
||||
wait_for_server_ready || fail "Server did not become ready"
|
||||
|
||||
echo "Starting MCC..."
|
||||
mcc-run 25565 > "$MCC_LOG" 2>&1 &
|
||||
MCC_PID=$!
|
||||
|
||||
wait_for_file_pattern "$MCC_LOG" "Server was successfully joined." "MCC join success" 90 || fail "MCC failed to join"
|
||||
wait_for_server_log_pattern "CursorBot joined the game" "server join entry" 30 || fail "Server never logged the join"
|
||||
|
||||
run_server_command "op CursorBot"
|
||||
run_server_command "gamerule sendCommandFeedback true"
|
||||
run_server_command "gamerule logAdminCommands true"
|
||||
run_server_command "time set day"
|
||||
run_server_command "weather clear"
|
||||
sleep 2
|
||||
|
||||
run_mcc_command "health"
|
||||
run_mcc_command "list"
|
||||
run_mcc_command "inventory player list"
|
||||
run_mcc_command "/gamemode creative"
|
||||
run_mcc_command "inventory creativegive 36 Diamond 16"
|
||||
run_mcc_command "inventory player list"
|
||||
run_mcc_command "entity"
|
||||
run_mcc_command "/time query daytime"
|
||||
run_mcc_command "smoke_test_from_mcc_full_spectrum"
|
||||
|
||||
run_server_command "execute as CursorBot at @s run summon minecraft:cow ~2 ~ ~"
|
||||
run_server_command "execute as CursorBot at @s run summon minecraft:zombie ~4 ~ ~"
|
||||
run_server_command "execute as CursorBot at @s run summon minecraft:creeper ~6 ~ ~"
|
||||
run_server_command "execute as CursorBot at @s run summon minecraft:skeleton ~8 ~ ~"
|
||||
run_server_command "execute as CursorBot at @s run summon minecraft:villager ~-2 ~ ~"
|
||||
run_server_command "execute as CursorBot at @s run summon minecraft:allay ~-4 ~ ~"
|
||||
run_server_command "execute as CursorBot at @s run summon minecraft:armor_stand ~ ~ ~2"
|
||||
|
||||
sleep 2
|
||||
run_mcc_command "entity"
|
||||
|
||||
run_server_command "execute as CursorBot at @s run particle minecraft:happy_villager ~ ~1 ~ 0.5 0.5 0.5 0 12 force"
|
||||
run_server_command "execute as CursorBot at @s run particle minecraft:end_rod ~ ~1 ~ 0.5 0.5 0.5 0.01 20 force"
|
||||
run_server_command "execute as CursorBot at @s run particle minecraft:explosion ~ ~1 ~ 0 0 0 0 1 force"
|
||||
run_server_command "execute as CursorBot at @s run particle minecraft:totem_of_undying ~ ~1 ~ 0.5 0.5 0.5 0.1 20 force"
|
||||
|
||||
run_server_command "execute as CursorBot at @s run playsound minecraft:entity.lightning_bolt.thunder master CursorBot ~ ~ ~ 1 1 0"
|
||||
run_server_command "execute as CursorBot at @s run playsound minecraft:block.note_block.bell master CursorBot ~ ~ ~ 1 1 0"
|
||||
|
||||
run_server_command "execute as CursorBot at @s run summon minecraft:tnt ~3 ~ ~"
|
||||
sleep 2
|
||||
run_server_command "execute as CursorBot at @s run summon minecraft:tnt ~6 ~ ~"
|
||||
|
||||
sleep 6
|
||||
capture_server_logs
|
||||
|
||||
assert_contains "$MCC_LOG" "Server was successfully joined." "MCC never joined the server"
|
||||
assert_contains "$MCC_LOG" "[FileInput] > inventory player list" "Inventory command was not executed"
|
||||
assert_contains "$MCC_LOG" "[FileInput] > entity" "Entity command was not executed"
|
||||
assert_contains "$MCC_LOG" "[FileInput] > /gamemode creative" "Creative mode command was not executed from MCC"
|
||||
assert_contains "$MCC_LOG" "Requested Diamond x16 in slot #36" "Creative inventory give did not succeed"
|
||||
assert_contains "$MCC_LOG" "smoke_test_from_mcc_full_spectrum" "Client-originated chat was not observed"
|
||||
assert_not_contains "$MCC_LOG" "Please enable InventoryHandling" "Inventory handling is still disabled"
|
||||
assert_not_contains "$MCC_LOG" "Please enable EntityHandling" "Entity handling is still disabled"
|
||||
assert_not_contains "$MCC_LOG" "You must be in Creative gamemode" "Creative mode was not active when creativegive ran"
|
||||
assert_not_contains "$MCC_LOG" "Failed to load settings" "MCC failed to reload its config"
|
||||
|
||||
assert_contains "$SERVER_FILE_LOG" "CursorBot joined the game" "Server never saw CursorBot join"
|
||||
assert_contains "$SERVER_FILE_LOG" "smoke_test_from_mcc_full_spectrum" "Server never received the client chat message"
|
||||
assert_contains "$SERVER_FILE_LOG" "Displaying particle minecraft:happy_villager" "Particle events were not recorded on the server"
|
||||
assert_contains "$SERVER_FILE_LOG" "Played sound minecraft:block.note_block.bell to CursorBot" "Sound events were not recorded on the server"
|
||||
assert_contains "$SERVER_FILE_LOG" "Summoned new Primed TNT" "TNT summon did not occur on the server"
|
||||
assert_not_contains "$SERVER_FILE_LOG" "Sending unknown packet 'clientbound/minecraft:disconnect'" "Server hit the disconnect packet regression during the test"
|
||||
|
||||
cat <<EOF
|
||||
PASS
|
||||
Run directory: $RUN_DIR
|
||||
MCC log: $MCC_LOG
|
||||
Server log: $SERVER_FILE_LOG
|
||||
Build log: $BUILD_LOG
|
||||
EOF
|
||||
25
.skills/mcc-integration-testing/scripts/summarize_test_run.sh
Executable file
25
.skills/mcc-integration-testing/scripts/summarize_test_run.sh
Executable file
|
|
@ -0,0 +1,25 @@
|
|||
#!/usr/bin/env zsh
|
||||
set -euo pipefail
|
||||
|
||||
RUN_ROOT="${TMPDIR:-/tmp}/mcc-integration-testing"
|
||||
RUN_DIR="${1:-$(find "$RUN_ROOT" -mindepth 1 -maxdepth 1 -type d | sort | tail -n 1)}"
|
||||
|
||||
if [[ -z "${RUN_DIR:-}" ]] || [[ ! -d "$RUN_DIR" ]]; then
|
||||
echo "Run directory not found" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
MCC_LOG="$RUN_DIR/mcc.log"
|
||||
SERVER_LOG="$RUN_DIR/server-latest.log"
|
||||
BUILD_LOG="$RUN_DIR/build.log"
|
||||
|
||||
echo "Run directory: $RUN_DIR"
|
||||
echo
|
||||
echo "Build result:"
|
||||
grep -E "Warning\(s\)|Error\(s\)|Time Elapsed" "$BUILD_LOG" || true
|
||||
echo
|
||||
echo "MCC highlights:"
|
||||
grep -E "Server was successfully joined|FileInput|smoke_test_from_mcc_full_spectrum|There are [0-9]+ of a max|health|Creative" "$MCC_LOG" || true
|
||||
echo
|
||||
echo "Server highlights:"
|
||||
grep -E "joined the game|Made CursorBot a server operator|game mode|smoke_test_from_mcc_full_spectrum|summon|particle|playsound|tnt" "$SERVER_LOG" || true
|
||||
|
|
@ -64,6 +64,12 @@ namespace MinecraftClient.ChatBots
|
|||
{
|
||||
public double min, max;
|
||||
|
||||
public Range()
|
||||
{
|
||||
min = 0;
|
||||
max = 0;
|
||||
}
|
||||
|
||||
public Range(int value)
|
||||
{
|
||||
min = max = value;
|
||||
|
|
@ -180,4 +186,4 @@ namespace MinecraftClient.ChatBots
|
|||
currentLocation.Z + random.Next(range * -1, range));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -106,6 +106,13 @@ namespace MinecraftClient.ChatBots
|
|||
{
|
||||
public double X, Y, Z;
|
||||
|
||||
public LocationConfig()
|
||||
{
|
||||
X = 0;
|
||||
Y = 0;
|
||||
Z = 0;
|
||||
}
|
||||
|
||||
public LocationConfig(double X, double Y, double Z)
|
||||
{
|
||||
this.X = X;
|
||||
|
|
|
|||
|
|
@ -85,6 +85,13 @@ namespace MinecraftClient.ChatBots
|
|||
{
|
||||
public double x, y, z;
|
||||
|
||||
public Coordination()
|
||||
{
|
||||
x = 0;
|
||||
y = 0;
|
||||
z = 0;
|
||||
}
|
||||
|
||||
public Coordination(double x, double y, double z)
|
||||
{
|
||||
this.x = x; this.y = y; this.z = z;
|
||||
|
|
|
|||
|
|
@ -103,6 +103,12 @@ namespace MinecraftClient.ChatBots
|
|||
public Coordination? XYZ;
|
||||
public Facing? facing;
|
||||
|
||||
public LocationConfig()
|
||||
{
|
||||
XYZ = null;
|
||||
facing = null;
|
||||
}
|
||||
|
||||
public LocationConfig(double yaw, double pitch)
|
||||
{
|
||||
this.XYZ = null;
|
||||
|
|
@ -125,6 +131,13 @@ namespace MinecraftClient.ChatBots
|
|||
{
|
||||
public double x, y, z;
|
||||
|
||||
public Coordination()
|
||||
{
|
||||
x = 0;
|
||||
y = 0;
|
||||
z = 0;
|
||||
}
|
||||
|
||||
public Coordination(double x, double y, double z)
|
||||
{
|
||||
this.x = x; this.y = y; this.z = z;
|
||||
|
|
@ -135,6 +148,12 @@ namespace MinecraftClient.ChatBots
|
|||
{
|
||||
public double yaw, pitch;
|
||||
|
||||
public Facing()
|
||||
{
|
||||
yaw = 0;
|
||||
pitch = 0;
|
||||
}
|
||||
|
||||
public Facing(double yaw, double pitch)
|
||||
{
|
||||
this.yaw = yaw; this.pitch = pitch;
|
||||
|
|
|
|||
|
|
@ -57,6 +57,12 @@ namespace MinecraftClient.ChatBots
|
|||
{
|
||||
public double min, max;
|
||||
|
||||
public Range()
|
||||
{
|
||||
min = 0;
|
||||
max = 0;
|
||||
}
|
||||
|
||||
public Range(int value)
|
||||
{
|
||||
min = max = value;
|
||||
|
|
|
|||
|
|
@ -284,7 +284,7 @@ namespace MinecraftClient.ChatBots
|
|||
if (text != null)
|
||||
messageBuilder.WithContent(text);
|
||||
|
||||
messageBuilder.WithFiles(new Dictionary<string, Stream>() { { $"attachment://{filePath}", fs } });
|
||||
messageBuilder.AddFiles(new Dictionary<string, Stream>() { { filePath, fs } });
|
||||
|
||||
discordBotClient!.SendMessageAsync(discordChannel, messageBuilder).Wait(Config.Message_Send_Timeout * 1000);
|
||||
}
|
||||
|
|
@ -301,7 +301,7 @@ namespace MinecraftClient.ChatBots
|
|||
if (!CanSendMessages())
|
||||
return;
|
||||
|
||||
SendMessage(new DiscordMessageBuilder().WithFile(fileStream));
|
||||
SendMessage(new DiscordMessageBuilder().AddFile(fileStream));
|
||||
}
|
||||
|
||||
private bool CanSendMessages()
|
||||
|
|
|
|||
|
|
@ -259,7 +259,8 @@ namespace MinecraftClient.ChatBots
|
|||
{
|
||||
using (var image = new MagickImage(fileName))
|
||||
{
|
||||
var size = new MagickGeometry(Config.Resize_To, Config.Resize_To);
|
||||
uint resizeTo = (uint)Math.Max(Config.Resize_To, 1);
|
||||
var size = new MagickGeometry(resizeTo, resizeTo);
|
||||
size.IgnoreAspectRatio = true;
|
||||
|
||||
image.Resize(size);
|
||||
|
|
|
|||
|
|
@ -116,6 +116,12 @@ namespace MinecraftClient.ChatBots
|
|||
public bool Enable = false;
|
||||
public TimeSpan[] Times;
|
||||
|
||||
public TriggerOnTimeConfig()
|
||||
{
|
||||
Enable = false;
|
||||
Times = Array.Empty<TimeSpan>();
|
||||
}
|
||||
|
||||
public TriggerOnTimeConfig(bool Enable, TimeSpan[] Time)
|
||||
{
|
||||
this.Enable = Enable;
|
||||
|
|
@ -134,6 +140,13 @@ namespace MinecraftClient.ChatBots
|
|||
public bool Enable = false;
|
||||
public double MinTime, MaxTime;
|
||||
|
||||
public TriggerOnIntervalConfig()
|
||||
{
|
||||
Enable = false;
|
||||
MinTime = 0;
|
||||
MaxTime = 0;
|
||||
}
|
||||
|
||||
public TriggerOnIntervalConfig(double value)
|
||||
{
|
||||
this.Enable = true;
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ using Telegram.Bot.Exceptions;
|
|||
using Telegram.Bot.Polling;
|
||||
using Telegram.Bot.Types;
|
||||
using Telegram.Bot.Types.Enums;
|
||||
using Telegram.Bot.Types.InputFiles;
|
||||
using Tomlet.Attributes;
|
||||
using File = System.IO.File;
|
||||
|
||||
|
|
@ -205,7 +204,7 @@ namespace MinecraftClient.ChatBots
|
|||
|
||||
try
|
||||
{
|
||||
botClient!.SendTextMessageAsync(Config.ChannelId.Trim(), message, ParseMode.Markdown).Wait(Config.Message_Send_Timeout);
|
||||
botClient!.SendMessage(Config.ChannelId.Trim(), message, parseMode: ParseMode.Markdown).Wait(Config.Message_Send_Timeout);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
|
@ -224,9 +223,9 @@ namespace MinecraftClient.ChatBots
|
|||
string fileName = filePath[(filePath.IndexOf(Path.DirectorySeparatorChar) + 1)..];
|
||||
|
||||
Stream stream = File.OpenRead(filePath);
|
||||
botClient!.SendDocumentAsync(
|
||||
botClient!.SendDocument(
|
||||
Config.ChannelId.Trim(),
|
||||
document: new InputOnlineFile(content: stream, fileName),
|
||||
document: InputFile.FromStream(stream, fileName),
|
||||
caption: text,
|
||||
parseMode: ParseMode.Markdown).Wait(Config.Message_Send_Timeout * 1000);
|
||||
}
|
||||
|
|
@ -260,14 +259,14 @@ namespace MinecraftClient.ChatBots
|
|||
cancellationToken = new CancellationTokenSource();
|
||||
|
||||
botClient.StartReceiving(
|
||||
updateHandler: HandleUpdateAsync,
|
||||
pollingErrorHandler: HandlePollingErrorAsync,
|
||||
receiverOptions: new ReceiverOptions
|
||||
HandleUpdateAsync,
|
||||
HandlePollingErrorAsync,
|
||||
new ReceiverOptions
|
||||
{
|
||||
// receive all update types
|
||||
AllowedUpdates = Array.Empty<UpdateType>()
|
||||
},
|
||||
cancellationToken: cancellationToken.Token
|
||||
cancellationToken.Token
|
||||
);
|
||||
|
||||
IsConnected = true;
|
||||
|
|
@ -313,9 +312,9 @@ namespace MinecraftClient.ChatBots
|
|||
|
||||
if (text.ToLower().Contains(".chatid"))
|
||||
{
|
||||
await botClient.SendTextMessageAsync(chatId: chatId,
|
||||
replyToMessageId: message.MessageId,
|
||||
await botClient.SendMessage(chatId: chatId,
|
||||
text: $"Chat ID: {chatId}",
|
||||
replyParameters: message.MessageId,
|
||||
cancellationToken: _cancellationToken,
|
||||
parseMode: ParseMode.Markdown);
|
||||
return;
|
||||
|
|
@ -324,10 +323,10 @@ namespace MinecraftClient.ChatBots
|
|||
if (Config.Authorized_Chat_Ids.Length > 0 && !Config.Authorized_Chat_Ids.Contains(chatId))
|
||||
{
|
||||
LogDebugToConsole($"Unauthorized message '{messageText}' received in a chat with with an ID: {chatId} !");
|
||||
await botClient.SendTextMessageAsync(
|
||||
await botClient.SendMessage(
|
||||
chatId: chatId,
|
||||
replyToMessageId: message.MessageId,
|
||||
text: Translations.bot_TelegramBridge_unauthorized,
|
||||
replyParameters: message.MessageId,
|
||||
cancellationToken: _cancellationToken,
|
||||
parseMode: ParseMode.Markdown);
|
||||
return;
|
||||
|
|
@ -347,10 +346,10 @@ namespace MinecraftClient.ChatBots
|
|||
|
||||
if (command.ToLower().Contains("quit") || command.ToLower().Contains("exit"))
|
||||
{
|
||||
await botClient.SendTextMessageAsync(
|
||||
await botClient.SendMessage(
|
||||
chatId: chatId,
|
||||
replyToMessageId: message.MessageId,
|
||||
text: $"{Translations.bot_TelegramBridge_quit_disabled}",
|
||||
replyParameters: message.MessageId,
|
||||
cancellationToken: _cancellationToken,
|
||||
parseMode: ParseMode.Markdown);
|
||||
return;;
|
||||
|
|
@ -359,11 +358,10 @@ namespace MinecraftClient.ChatBots
|
|||
CmdResult result = new();
|
||||
PerformInternalCommand(command, ref result);
|
||||
|
||||
await botClient.SendTextMessageAsync(
|
||||
await botClient.SendMessage(
|
||||
chatId: chatId,
|
||||
replyToMessageId:
|
||||
message.MessageId,
|
||||
text: $"{Translations.bot_TelegramBridge_command_executed}:\n\n{result}",
|
||||
replyParameters: message.MessageId,
|
||||
cancellationToken: _cancellationToken,
|
||||
parseMode: ParseMode.Markdown);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<OutputType>Exe</OutputType>
|
||||
<PublishUrl>publish\</PublishUrl>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
|
|
@ -28,25 +28,22 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Brigadier.NET" Version="1.2.13" />
|
||||
<PackageReference Include="DnsClient" Version="1.7.0" />
|
||||
<PackageReference Include="DotNetZip" Version="1.16.0" />
|
||||
<PackageReference Include="DSharpPlus" Version="4.2.0" />
|
||||
<PackageReference Include="DynamicExpresso.Core" Version="2.13.0" />
|
||||
<PackageReference Include="DnsClient" Version="1.8.0" />
|
||||
<PackageReference Include="DSharpPlus" Version="4.5.1" />
|
||||
<PackageReference Include="DynamicExpresso.Core" Version="2.19.3" />
|
||||
<PackageReference Include="FuzzySharp" Version="2.0.2" />
|
||||
<PackageReference Include="Magick.NET-Q16-AnyCPU" Version="13.3.0" />
|
||||
<PackageReference Include="MessagePack" Version="3.1.0" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.4.0" />
|
||||
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="5.2.9" />
|
||||
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||
<PackageReference Include="Microsoft.Windows.Compatibility" Version="7.0.3" />
|
||||
<PackageReference Include="Samboy063.Tomlet" Version="5.0.1" />
|
||||
<PackageReference Include="Sentry" Version="4.1.0" />
|
||||
<PackageReference Include="SingleFileExtractor.Core" Version="1.0.1" />
|
||||
<PackageReference Include="Magick.NET-Q16-AnyCPU" Version="14.11.0" />
|
||||
<PackageReference Include="MessagePack" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="5.3.0" />
|
||||
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Windows.Compatibility" Version="10.0.5" />
|
||||
<PackageReference Include="Samboy063.Tomlet" Version="6.2.0" />
|
||||
<PackageReference Include="Sentry" Version="6.2.0" />
|
||||
<PackageReference Include="SingleFileExtractor.Core" Version="2.3.0" />
|
||||
<PackageReference Include="starksoft.aspen" Version="1.1.8">
|
||||
<NoWarn>NU1701</NoWarn>
|
||||
</PackageReference>
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="7.0.0" />
|
||||
<PackageReference Include="Telegram.Bot" Version="18.0.0" />
|
||||
<PackageReference Include="Telegram.Bot" Version="22.9.5.3" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Remove="config\ChatBots\AutoLeaveOnLowHp.cs" />
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ namespace MinecraftClient
|
|||
options.Dsn = SentryDSN;
|
||||
options.AutoSessionTracking = true;
|
||||
options.IsGlobalModeEnabled = true;
|
||||
options.EnableTracing = true;
|
||||
options.TracesSampleRate = 1.0;
|
||||
options.SendDefaultPii = false;
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -350,7 +350,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
{
|
||||
break;
|
||||
}
|
||||
catch (Ionic.Zlib.ZlibException)
|
||||
catch (System.IO.InvalidDataException)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
using Ionic.Zlib;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
|
||||
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
|
||||
{
|
||||
|
|
@ -17,16 +15,13 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
/// <returns>Compressed data as a byte array</returns>
|
||||
public static byte[] Compress(byte[] to_compress)
|
||||
{
|
||||
byte[] data;
|
||||
using (System.IO.MemoryStream memstream = new())
|
||||
using MemoryStream memstream = new();
|
||||
using (ZLibStream stream = new(memstream, CompressionMode.Compress, leaveOpen: true))
|
||||
{
|
||||
using (ZlibStream stream = new(memstream, CompressionMode.Compress))
|
||||
{
|
||||
stream.Write(to_compress, 0, to_compress.Length);
|
||||
}
|
||||
data = memstream.ToArray();
|
||||
stream.Write(to_compress, 0, to_compress.Length);
|
||||
}
|
||||
return data;
|
||||
|
||||
return memstream.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -37,10 +32,20 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
/// <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);
|
||||
using MemoryStream compressedStream = new(to_decompress, writable: false);
|
||||
using ZLibStream stream = new(compressedStream, CompressionMode.Decompress);
|
||||
|
||||
byte[] packetData_decompressed = new byte[size_uncompressed];
|
||||
stream.Read(packetData_decompressed, 0, size_uncompressed);
|
||||
stream.Close();
|
||||
int totalRead = 0;
|
||||
while (totalRead < size_uncompressed)
|
||||
{
|
||||
int read = stream.Read(packetData_decompressed, totalRead, size_uncompressed - totalRead);
|
||||
if (read <= 0)
|
||||
break;
|
||||
|
||||
totalRead += read;
|
||||
}
|
||||
|
||||
return packetData_decompressed;
|
||||
}
|
||||
|
||||
|
|
@ -51,12 +56,14 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
/// <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);
|
||||
using MemoryStream compressedStream = new(to_decompress, writable: false);
|
||||
using ZLibStream stream = new(compressedStream, CompressionMode.Decompress);
|
||||
byte[] buffer = new byte[16 * 1024];
|
||||
using System.IO.MemoryStream decompressedBuffer = new();
|
||||
using MemoryStream decompressedBuffer = new();
|
||||
int read;
|
||||
while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
|
||||
decompressedBuffer.Write(buffer, 0, read);
|
||||
|
||||
return decompressedBuffer.ToArray();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using Ionic.Zip;
|
||||
using MinecraftClient.Mapping;
|
||||
using MinecraftClient.Protocol.Handlers;
|
||||
using MinecraftClient.Protocol.Handlers.PacketPalettes;
|
||||
|
|
@ -138,12 +138,18 @@ namespace MinecraftClient.Protocol
|
|||
using (Stream recordingFile = new FileStream(Path.Combine(temporaryCache, recordingTmpFileName), FileMode.Open))
|
||||
{
|
||||
using Stream metaDataFile = new FileStream(Path.Combine(temporaryCache, MetaData.MetaDataFileName), FileMode.Open);
|
||||
using ZipOutputStream zs = new(Path.Combine(ReplayFileDirectory, replayFileName));
|
||||
zs.PutNextEntry(recordingTmpFileName);
|
||||
recordingFile.CopyTo(zs);
|
||||
zs.PutNextEntry(MetaData.MetaDataFileName);
|
||||
metaDataFile.CopyTo(zs);
|
||||
zs.Close();
|
||||
using FileStream replayArchiveFile = new(Path.Combine(ReplayFileDirectory, replayFileName), FileMode.Create, FileAccess.Write);
|
||||
using ZipArchive replayArchive = new(replayArchiveFile, ZipArchiveMode.Create);
|
||||
|
||||
ZipArchiveEntry recordingEntry = replayArchive.CreateEntry(recordingTmpFileName);
|
||||
using (Stream recordingEntryStream = recordingEntry.Open())
|
||||
{
|
||||
recordingFile.CopyTo(recordingEntryStream);
|
||||
}
|
||||
|
||||
ZipArchiveEntry metadataEntry = replayArchive.CreateEntry(MetaData.MetaDataFileName);
|
||||
using Stream metadataEntryStream = metadataEntry.Open();
|
||||
metaDataFile.CopyTo(metadataEntryStream);
|
||||
}
|
||||
|
||||
File.Delete(Path.Combine(temporaryCache, recordingTmpFileName));
|
||||
|
|
@ -167,18 +173,29 @@ namespace MinecraftClient.Protocol
|
|||
|
||||
using (Stream metaDataFile = new FileStream(Path.Combine(temporaryCache, MetaData.MetaDataFileName), FileMode.Open))
|
||||
{
|
||||
using ZipOutputStream zs = new(replayFileName);
|
||||
zs.PutNextEntry(recordingTmpFileName);
|
||||
// .CopyTo() method start from stream current position
|
||||
// We need to reset position in order to get full content
|
||||
var lastPosition = recordStream!.BaseStream.Position;
|
||||
recordStream.BaseStream.Position = 0;
|
||||
recordStream.BaseStream.CopyTo(zs);
|
||||
recordStream.BaseStream.Position = lastPosition;
|
||||
using FileStream replayArchiveFile = new(replayFileName, FileMode.Create, FileAccess.Write);
|
||||
using ZipArchive replayArchive = new(replayArchiveFile, ZipArchiveMode.Create);
|
||||
|
||||
zs.PutNextEntry(MetaData.MetaDataFileName);
|
||||
metaDataFile.CopyTo(zs);
|
||||
zs.Close();
|
||||
ZipArchiveEntry recordingEntry = replayArchive.CreateEntry(recordingTmpFileName);
|
||||
using (Stream recordingEntryStream = recordingEntry.Open())
|
||||
{
|
||||
// .CopyTo() method start from stream current position
|
||||
// We need to reset position in order to get full content
|
||||
long lastPosition = recordStream!.BaseStream.Position;
|
||||
try
|
||||
{
|
||||
recordStream.BaseStream.Position = 0;
|
||||
recordStream.BaseStream.CopyTo(recordingEntryStream);
|
||||
}
|
||||
finally
|
||||
{
|
||||
recordStream.BaseStream.Position = lastPosition;
|
||||
}
|
||||
}
|
||||
|
||||
ZipArchiveEntry metadataEntry = replayArchive.CreateEntry(MetaData.MetaDataFileName);
|
||||
using Stream metadataEntryStream = metadataEntry.Open();
|
||||
metaDataFile.CopyTo(metadataEntryStream);
|
||||
}
|
||||
|
||||
WriteDebugLog("Backup replay file created.");
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ https://github.com/laurentkempe/DynamicRun/blob/master/LICENSE
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.MemoryMappedFiles;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
|
@ -117,10 +116,11 @@ namespace MinecraftClient.Scripting.DynamicRun.Builder
|
|||
File.Copy(executablePath, tempFile);
|
||||
|
||||
// Access the contents of the executable.
|
||||
ExecutableReader e = new();
|
||||
var viewAccessor = MemoryMappedFile.CreateFromFile(tempFile, FileMode.Open).CreateViewAccessor();
|
||||
var manifest = e.ReadManifest(viewAccessor);
|
||||
var files = manifest.Files;
|
||||
using ExecutableReader executableReader = new(tempFile);
|
||||
if (!executableReader.IsSingleFile)
|
||||
throw new InvalidOperationException("[Script Error] The executable is not a single-file bundle.");
|
||||
|
||||
var files = executableReader.Bundle.Files;
|
||||
|
||||
Stream? assemblyStream;
|
||||
|
||||
|
|
@ -133,8 +133,8 @@ namespace MinecraftClient.Scripting.DynamicRun.Builder
|
|||
if (string.IsNullOrEmpty(loadedAssembly.Location)) {
|
||||
// Check if we can access the file from the executable.
|
||||
var reference = files.FirstOrDefault(x =>
|
||||
x.RelativePath.Remove(x.RelativePath.Length - 4) == refs.Name);
|
||||
var refCount = files.Count(x => x.RelativePath.Remove(x.RelativePath.Length - 4) == refs.Name);
|
||||
Path.GetFileNameWithoutExtension(x.RelativePath) == refs.Name);
|
||||
var refCount = files.Count(x => Path.GetFileNameWithoutExtension(x.RelativePath) == refs.Name);
|
||||
if (refCount > 1) {
|
||||
// Safety net for the case where the assembly is referenced multiple times.
|
||||
// Should not happen normally, but we can make exceptions when it does happen.
|
||||
|
|
@ -147,17 +147,13 @@ namespace MinecraftClient.Scripting.DynamicRun.Builder
|
|||
"[Script Error] The executable does not contain a referenced assembly. Assembly name: " + refs.Name);
|
||||
}
|
||||
|
||||
assemblyStream = GetStreamForFileEntry(viewAccessor, reference);
|
||||
assemblyStream = reference.AsStream();
|
||||
references.Add(MetadataReference.CreateFromStream(assemblyStream!));
|
||||
continue;
|
||||
}
|
||||
|
||||
references.Add(MetadataReference.CreateFromFile(loadedAssembly.Location));
|
||||
}
|
||||
|
||||
// Cleanup.
|
||||
viewAccessor.Flush();
|
||||
viewAccessor.Dispose();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -176,14 +172,6 @@ namespace MinecraftClient.Scripting.DynamicRun.Builder
|
|||
assemblyIdentityComparer: DesktopAssemblyIdentityComparer.Default));
|
||||
}
|
||||
|
||||
private static Stream? GetStreamForFileEntry(MemoryMappedViewAccessor viewAccessor, FileEntry file)
|
||||
{
|
||||
if (typeof(BundleExtractor).GetMethod("GetStreamForFileEntry", BindingFlags.NonPublic | BindingFlags.Static)!.Invoke(null, new object[] { viewAccessor, file }) is not Stream stream)
|
||||
throw new InvalidOperationException("[Script Error] The executable does not contain the assembly. Assembly name: " + file.RelativePath);
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
internal struct CompileResult
|
||||
{
|
||||
internal byte[]? Assembly;
|
||||
|
|
|
|||
|
|
@ -658,6 +658,12 @@ namespace MinecraftClient
|
|||
{
|
||||
public string Login = string.Empty, Password = string.Empty;
|
||||
|
||||
public AccountInfoConfig()
|
||||
{
|
||||
Login = string.Empty;
|
||||
Password = string.Empty;
|
||||
}
|
||||
|
||||
public AccountInfoConfig(string Login)
|
||||
{
|
||||
this.Login = Login;
|
||||
|
|
@ -676,6 +682,12 @@ namespace MinecraftClient
|
|||
public string Host = string.Empty;
|
||||
public ushort? Port = null;
|
||||
|
||||
public ServerInfoConfig()
|
||||
{
|
||||
Host = string.Empty;
|
||||
Port = null;
|
||||
}
|
||||
|
||||
public ServerInfoConfig(string Host)
|
||||
{
|
||||
string[] sip = Host.Split(new[] { ":", ":" }, StringSplitOptions.None);
|
||||
|
|
@ -699,6 +711,12 @@ namespace MinecraftClient
|
|||
public string Host = string.Empty;
|
||||
public int Port = 443;
|
||||
|
||||
public AuthlibServer()
|
||||
{
|
||||
Host = string.Empty;
|
||||
Port = 443;
|
||||
}
|
||||
|
||||
public AuthlibServer(string Host)
|
||||
{
|
||||
string[] sip = Host.Split(new[] { ":", ":" }, StringSplitOptions.None);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue