diff --git a/.skills/mcc-dev-workflow/SKILL.md b/.skills/mcc-dev-workflow/SKILL.md index f1a3c8fe..b1a9ef01 100644 --- a/.skills/mcc-dev-workflow/SKILL.md +++ b/.skills/mcc-dev-workflow/SKILL.md @@ -1,6 +1,6 @@ --- name: mcc-dev-workflow -description: Build, run, and debug Minecraft Console Client (MCC) against a real local Minecraft Java server in WSL. Use this whenever the user wants to compile MCC, start or inspect a local test server, connect MCC to a server, debug protocol or login issues, validate a code change end-to-end, or run MCC commands on a real server instead of guessing from static code. +description: Build, run, and debug Minecraft Console Client (MCC) against a real local Minecraft Java server on Linux, macOS, or WSL. Use this whenever the user wants to compile MCC, start or inspect a local test server, connect MCC to a server, debug protocol or login issues, validate a code change end-to-end, or run MCC commands on a real server instead of guessing from static code. --- # MCC Development Workflow @@ -11,7 +11,7 @@ Use this skill when the task needs a real local server loop, not just code readi - Solution: `MinecraftClient.sln` - Runtime target: `.NET 10` / `net10.0` -- Environment: WSL Ubuntu, Java 21, tmux, python3 +- Environment: Linux, macOS, or WSL with Java, tmux, python3, and dotnet available - Default server root: `${MCC_SERVERS:-$MCC_REPO/MinecraftOfficial/downloads}` - Default validation target when the user does not specify a version: `1.21.11` @@ -30,10 +30,22 @@ Both modes support the same commands and input/output through `ConsoleIO.Backend - Prefer a real local server over static reasoning for protocol, login, movement, inventory, entity, or command-path work. - Treat tmux `mc-*` sessions as shared state. Do not run multi-version server workflows in parallel unless the harness explicitly isolates them. -- For scripted or repeatable runs, prefer a temporary config copied from `MinecraftClient.ini`. Use the repo-root config only for ad hoc manual work. +- For scripted or repeatable runs, use a generated temporary config. Do not edit the repo-root `MinecraftClient.ini` as part of the test loop. - A server log line containing `Done (` means startup finished. It does not guarantee that RCON is ready on the first attempt. Retry early `mc-rcon` commands. - When instructions, docs, and code disagree, trust current code and current tool behavior first. +## Preflight and reset + +Before scripted runs, especially on macOS or in a reused tmux environment: + +```bash +source tools/mcc-env.sh +mcc-preflight 1.21.11 +mc-reset-test-env 1.21.11 +``` + +`mcc-preflight` checks Java, tmux, dotnet, python3, and server directories. It also resolves common Homebrew Java paths on macOS. `mc-reset-test-env` clears stale tmux sessions and stale `stdin.pipe` files before they turn into misleading startup failures. + ## Build ```bash @@ -92,7 +104,7 @@ mcc-debug -v 1.21.11 --file-input --no-build ### What mcc-debug.sh does 1. Builds MCC (unless `--no-build`) -2. Creates a temp config at `/tmp/mcc-debug/MinecraftClient.debug.ini` with CursorBot account, Terrain/Inventory/Entity enabled +2. Creates a clean temp config at `/tmp/mcc-debug/MinecraftClient.debug.ini` with CursorBot account, Terrain/Inventory/Entity enabled and noisy bots disabled 3. Ensures server is running (starts if not, waits for `Done (`) 4. Launches MCC in the specified mode @@ -210,6 +222,9 @@ After `source tools/mcc-env.sh`: | `mc-rcon "CMD"` | Send RCON command | | `mc-kill VER` | Force-kill server tmux session | | `mc-list` | List running MC server sessions | +| `mc-wait-ready VER [SEC]` | Wait for server `Done (` | +| `mc-wait-stop VER [SEC]` | Wait for server shutdown, with force-kill fallback | +| `mc-reset-test-env [--all|VER...]` | Reset shared tmux server state and stale pipes | | `mcc-build` | Build MCC | | `mcc-run [PORT]` | Run MCC classic+FileInput on port | | `mcc-tui [PORT]` | Run MCC TUI mode in tmux | @@ -218,6 +233,7 @@ After `source tools/mcc-env.sh`: | `mcc-debug [OPTS]` | One-step debug session (see above) | | `mcc-log-mcc` | Tail MCC debug log | | `mcc-state` | Send `debug state` and print last 30 log lines | +| `mcc-preflight [VER...]` | Verify Java, tmux, dotnet, python3, and server dirs | ## Temporary config recipe @@ -226,14 +242,10 @@ source tools/mcc-env.sh TEST_ROOT="${TMPDIR:-/tmp}/mcc-dev" CFG="$TEST_ROOT/MinecraftClient.1.21.11.ini" mkdir -p "$TEST_ROOT" -cp "$MCC_REPO/MinecraftClient.ini" "$CFG" -sed -i \ - -e 's/Account = { Login = "test", Password = "-" }/Account = { Login = "CursorBot", Password = "-" }/' \ - -e 's/MinecraftVersion = "auto"/MinecraftVersion = "1.21.11"/' \ - -e 's/TerrainAndMovements = false/TerrainAndMovements = true/' \ - -e 's/InventoryHandling = false/InventoryHandling = true/' \ - -e 's/EntityHandling = false/EntityHandling = true/' \ - "$CFG" +bash "$MCC_REPO/.skills/mcc-integration-testing/scripts/prepare_offline_mcc_config.sh" \ + "$CFG" \ + "1.21.11" \ + "CursorBot" ``` For TUI mode, also add: @@ -257,6 +269,8 @@ Basic command check: mcc-cmd "inventory player list" ``` +If a scripted run fails before MCC joins, check for a harness problem before assuming a product regression. Missing `mcc.log`, a pre-join `Connection refused`, or a server that never reached `Done (` usually means shared-state cleanup or startup failed. + ## Typical debug loop 1. `source tools/mcc-env.sh` diff --git a/.skills/mcc-integration-testing/SKILL.md b/.skills/mcc-integration-testing/SKILL.md index 50673abb..168b545f 100644 --- a/.skills/mcc-integration-testing/SKILL.md +++ b/.skills/mcc-integration-testing/SKILL.md @@ -56,7 +56,7 @@ If the environment cannot run a real server, say so and report the result as une - Use a real local server. - Launch MCC against an explicit `localhost:` target for repeatable local tests. - Keep version matrices sequential in shared local environments. The tmux server harness is shared state by default. -- Prefer temporary MCC configs for scripted runs so one test does not contaminate the next. +- Prefer generated temporary MCC configs for scripted runs so one test does not contaminate the next. - Default to offline auth in generated temp configs. Do not trust the repo-root `MinecraftClient.ini` account defaults. - If the user explicitly asks for Microsoft online login, honor that request and generate the temp config for Microsoft auth instead of offline mode. - For Microsoft auth, prefer an interactive TTY launch with `BasicIO-NoColor` so the device code is easy to read and relay to the user. @@ -65,8 +65,10 @@ If the environment cannot run a real server, say so and report the result as une - Legacy and modern command syntax differ. Do not assume one server-command profile fits every version. - Use actual MCC output and actual server logs for assertions. Do not invent success strings. - Treat server `Done` as startup progress, not RCON readiness. Retry the first RCON command before assuming the setup is broken. +- Run preflight before scripted test loops. On macOS, Java may be installed but not exported on PATH in the shell the harness uses. - If a change touches shared routing or a version range, test at least one adjacent version that shares that path, or explicitly mark adjacent versions as unexecuted and inferred. - For palette or version-content changes, probe at least one neighboring or existing item, entity, or block. Do not only check the headline addition. +- Separate product failures from harness failures. Missing logs, stale tmux state, stale `stdin.pipe`, or pre-join `Connection refused` errors are usually environment problems until proven otherwise. ## Choose the test mode @@ -117,11 +119,19 @@ Run them against a real server with a temp config and summarize counts from the Before running any scenario: +0. run preflight and clear stale shared state when the environment is reused 1. configure the target server for offline testing 2. ensure `eula=true` 3. ensure RCON is enabled 4. build MCC unless the task explicitly reuses a fresh build +Preflight and reset helpers: + +```bash +.skills/mcc-integration-testing/scripts/preflight_test_env.sh 1.21.11-Vanilla +.skills/mcc-integration-testing/scripts/reset_shared_test_state.sh 1.21.11-Vanilla +``` + Offline configuration helper: ```bash @@ -141,8 +151,12 @@ Optionally override the login name with the fourth argument to the config helper - `.skills/mcc-integration-testing/scripts/ensure_offline_server.sh` - configures persistent offline mode and RCON +- `.skills/mcc-integration-testing/scripts/preflight_test_env.sh` + - verifies Java, tmux, dotnet, python3, server directories, and resolves common Java PATH issues +- `.skills/mcc-integration-testing/scripts/reset_shared_test_state.sh` + - clears stale tmux sessions and stale `stdin.pipe` files before a rerun - `.skills/mcc-integration-testing/scripts/prepare_offline_mcc_config.sh` - - copies `MinecraftClient.ini`, prepares offline login by default, and can switch to Microsoft auth when explicitly requested + - generates a clean temporary MCC config, prepares offline login by default, disables noisy bots, and can switch to Microsoft auth when explicitly requested - `.skills/mcc-integration-testing/scripts/get_server_port.sh` - resolves the actual local server port from `server.properties` or the latest server log - `.skills/mcc-integration-testing/scripts/run_full_spectrum_test.sh` @@ -159,6 +173,7 @@ In every report, separate: - `Executed`: exact scripts, commands, versions, auth mode, and whether the run was sequential or single-version - `Observed`: exact MCC output, exact server-log evidence, and the saved log directory - `Inferred`: conclusions not directly shown by that run's runtime evidence +- `Harness issues`: setup or runner problems such as missing Java on PATH, stale tmux sessions, stale `stdin.pipe`, missing log artifacts, or failed config generation Never upgrade inferred claims to observed facts. Absence of errors is supporting evidence only; pair it with a positive assertion for the feature under test. @@ -196,6 +211,7 @@ Always summarize: ## Troubleshooting - If the first RCON command fails, retry it before assuming the setup is broken. +- If Java is installed but the harness still says it is missing, run `preflight_test_env.sh`. This resolves common Homebrew Java paths on macOS. - If MCC reaches Microsoft device-code login during an offline test, stop and inspect the generated temp config before retrying. - If the user explicitly requests Microsoft online login, set `MCC_TEST_ACCOUNT_TYPE=microsoft` before launching the harness. - If the user explicitly requests Microsoft online login, use `BasicIO-NoColor` in a real TTY, relay the device code from the TUI, and avoid pressing empty Enter at any auth prompt. @@ -203,7 +219,8 @@ Always summarize: - If `dotnet run` cannot see an existing Microsoft session, check whether `SessionCache.db` and `ProfileKeyCache.ini` need to be synced from `MinecraftClient/bin/Release/net10.0/` to the repo root. - If Microsoft auth keeps prompting even with a valid session cache, verify `Account.Login` matches the cached username exactly. - If MCC reports `Connection refused`, verify the launched target matches the server's actual `server-port`. +- If MCC reports `Connection refused` immediately after a server start, also check for stale shared state: old tmux sessions, a stale `stdin.pipe`, or a server that never actually reached `Done (`. - If multiple versions are being tested, do not start them in parallel unless the harness isolates tmux sessions and input files. - If a test assertion fails, inspect the real MCC output before changing the code or weakening the assertion. - If an older server behaves oddly on Linux, check `use-native-transport=false` in `server.properties`. -- If a test should be repeatable, avoid mutating the repo-root `MinecraftClient.ini`. +- If a matrix row fails before producing `mcc.log` or a command transcript, treat it as a harness failure, fix the environment, and rerun that row before drawing product conclusions. diff --git a/.skills/mcc-integration-testing/scripts/common.sh b/.skills/mcc-integration-testing/scripts/common.sh new file mode 100755 index 00000000..973b5da3 --- /dev/null +++ b/.skills/mcc-integration-testing/scripts/common.sh @@ -0,0 +1,110 @@ +#!/usr/bin/env bash + +sed_in_place() { + if [[ "$(uname)" == "Darwin" ]]; then + sed -i '' "$@" + else + sed -i "$@" + fi +} + +ensure_java_in_path() { + if command -v java >/dev/null 2>&1 && java -version >/dev/null 2>&1; then + return 0 + fi + + local candidate + for candidate in \ + "${JAVA_BIN:-}" \ + "/opt/homebrew/opt/openjdk/bin/java" \ + "/usr/local/opt/openjdk/bin/java" \ + "/usr/lib/jvm/default-java/bin/java" + do + [[ -z "$candidate" ]] && continue + if [[ -x "$candidate" ]]; then + export PATH="$(dirname "$candidate"):$PATH" + export JAVA_BIN="$candidate" + if java -version >/dev/null 2>&1; then + return 0 + fi + fi + done + + echo "java was not found on PATH. Install Java or set JAVA_BIN." >&2 + return 1 +} + +server_session_name() { + printf 'mc-%s\n' "${1//./_}" +} + +server_running() { + local version="$1" + mc-list | grep -Fq "$(server_session_name "$version")" +} + +wait_for_server_ready() { + local version="$1" + local timeout="${2:-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 $version to become ready" >&2 + return 1 +} + +wait_for_server_stop() { + local version="$1" + local timeout="${2:-60}" + local elapsed=0 + + while (( elapsed < timeout )); do + if ! server_running "$version"; then + return 0 + fi + sleep 1 + ((elapsed += 1)) + done + + mc-kill "$version" >/dev/null 2>&1 || true + + if ! server_running "$version"; then + return 0 + fi + + echo "Timed out waiting for $version to stop" >&2 + return 1 +} + +disable_noisy_bots_in_ini() { + local ini_file="$1" + local section + + for section in \ + ScriptScheduler \ + DiscordRpc \ + AntiAFK \ + AutoDig \ + AutoAttack \ + PlayerListLogger \ + ReplayCapture + do + sed_in_place "/^\\[ChatBot\\.${section}\\]/,/^\\[/ { s/^Enabled = true/Enabled = false/; }" "$ini_file" + done +} + +remove_stale_stdin_pipe() { + local version="$1" + local pipe_path="$MCC_SERVERS/$version/stdin.pipe" + + if [[ -e "$pipe_path" && ! -p "$pipe_path" ]]; then + rm -f "$pipe_path" + fi +} diff --git a/.skills/mcc-integration-testing/scripts/ensure_offline_server.sh b/.skills/mcc-integration-testing/scripts/ensure_offline_server.sh index 1e348445..38e73978 100755 --- a/.skills/mcc-integration-testing/scripts/ensure_offline_server.sh +++ b/.skills/mcc-integration-testing/scripts/ensure_offline_server.sh @@ -5,14 +5,8 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" # shellcheck source=tools/mcc-env.sh source "$REPO_ROOT/tools/mcc-env.sh" - -sed_in_place() { - if [[ "$(uname)" == "Darwin" ]]; then - sed -i '' "$@" - else - sed -i "$@" - fi -} +# shellcheck source=.skills/mcc-integration-testing/scripts/common.sh +source "$SCRIPT_DIR/common.sh" VERSION="${1:-1.21.11-Vanilla}" SERVER_DIR="${MCC_SERVERS:?}/$VERSION" @@ -33,43 +27,6 @@ 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 - - # Legacy servers can leave the tmux session around after stdin stop. - # Fall back to force-killing the session so the harness can continue. - mc-kill "$VERSION" >/dev/null 2>&1 || true - - if ! server_running; then - return 0 - fi - - echo "Timed out waiting for $VERSION to stop" >&2 - return 1 -} - upsert_property() { local key="$1" local value="$2" @@ -83,14 +40,14 @@ upsert_property() { if [[ ! -f "$PROPS_FILE" ]]; then mc-start "$VERSION" - wait_for_server_ready + wait_for_server_ready "$VERSION" mc-stop "$VERSION" - wait_for_server_stop + wait_for_server_stop "$VERSION" fi if server_running; then mc-stop "$VERSION" - wait_for_server_stop + wait_for_server_stop "$VERSION" fi upsert_property "online-mode" "false" diff --git a/.skills/mcc-integration-testing/scripts/preflight_test_env.sh b/.skills/mcc-integration-testing/scripts/preflight_test_env.sh new file mode 100755 index 00000000..22376026 --- /dev/null +++ b/.skills/mcc-integration-testing/scripts/preflight_test_env.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +# shellcheck source=tools/mcc-env.sh +source "$REPO_ROOT/tools/mcc-env.sh" +# shellcheck source=.skills/mcc-integration-testing/scripts/common.sh +source "$SCRIPT_DIR/common.sh" + +usage() { + cat <<'EOF' +Usage: preflight_test_env.sh [server-dir...] + +Checks the local MCC test environment and resolves common Java path issues. +EOF +} + +if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then + usage + exit 0 +fi + +ensure_java_in_path +command -v tmux >/dev/null 2>&1 || { echo "tmux was not found on PATH." >&2; exit 1; } +command -v dotnet >/dev/null 2>&1 || { echo "dotnet was not found on PATH." >&2; exit 1; } +command -v python3 >/dev/null 2>&1 || { echo "python3 was not found on PATH." >&2; exit 1; } + +if [[ ! -d "$MCC_SERVERS" ]]; then + echo "Server root not found: $MCC_SERVERS" >&2 + exit 1 +fi + +for server_dir in "$@"; do + [[ -z "$server_dir" ]] && continue + if [[ ! -d "$MCC_SERVERS/$server_dir" ]]; then + echo "Server directory not found: $MCC_SERVERS/$server_dir" >&2 + exit 1 + fi + + remove_stale_stdin_pipe "$server_dir" +done + +printf 'MCC_REPO=%s\n' "$MCC_REPO" +printf 'MCC_SERVERS=%s\n' "$MCC_SERVERS" +printf 'JAVA=%s\n' "$(command -v java)" +printf 'TMUX=%s\n' "$(command -v tmux)" +printf 'DOTNET=%s\n' "$(command -v dotnet)" diff --git a/.skills/mcc-integration-testing/scripts/prepare_offline_mcc_config.sh b/.skills/mcc-integration-testing/scripts/prepare_offline_mcc_config.sh index 9eae53b3..64727a58 100644 --- a/.skills/mcc-integration-testing/scripts/prepare_offline_mcc_config.sh +++ b/.skills/mcc-integration-testing/scripts/prepare_offline_mcc_config.sh @@ -1,23 +1,40 @@ #!/usr/bin/env bash set -euo pipefail -sed_in_place() { - if [[ "$(uname)" == "Darwin" ]]; then - sed -i '' "$@" - else - sed -i "$@" - fi +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +# shellcheck source=.skills/mcc-integration-testing/scripts/common.sh +source "$SCRIPT_DIR/common.sh" + +usage() { + cat <<'EOF' >&2 +Usage: + prepare_offline_mcc_config.sh [login] + prepare_offline_mcc_config.sh [login] +EOF } -if [[ $# -lt 3 || $# -gt 4 ]]; then - echo "Usage: $0 [login]" >&2 +if [[ $# -lt 2 || $# -gt 4 ]]; then + usage exit 1 fi -TEMPLATE_INI="$1" -OUTPUT_INI="$2" -MC_VERSION="$3" -LOGIN_NAME="${4:-CursorBot}" +TEMPLATE_INI="" +OUTPUT_INI="" +MC_VERSION="" +LOGIN_NAME="" + +if [[ $# -ge 3 && -f "$1" ]]; then + TEMPLATE_INI="$1" + OUTPUT_INI="$2" + MC_VERSION="$3" + LOGIN_NAME="${4:-CursorBot}" +else + OUTPUT_INI="$1" + MC_VERSION="$2" + LOGIN_NAME="${3:-CursorBot}" +fi + ACCOUNT_TYPE="${MCC_TEST_ACCOUNT_TYPE:-mojang}" PASSWORD_VALUE="${MCC_TEST_PASSWORD-}" @@ -34,6 +51,32 @@ if [[ -z "${MCC_TEST_PASSWORD+x}" ]]; then fi fi +generate_template_ini() { + local template_root + template_root="$(mktemp -d "${TMPDIR:-/tmp}/mcc-config-template.XXXXXX")" + + if [[ ! -f "$REPO_ROOT/MinecraftClient/bin/Release/net10.0/MinecraftClient.dll" ]]; then + dotnet build "$REPO_ROOT/MinecraftClient.sln" -c Release -v quiet --nologo >/dev/null + fi + + ( + cd "$template_root" + dotnet run --project "$REPO_ROOT/MinecraftClient" -c Release --no-build -- --help >/dev/null 2>&1 + ) + + if [[ ! -f "$template_root/MinecraftClient.ini" ]]; then + echo "Failed to generate a temporary MCC config template." >&2 + exit 1 + fi + + TEMPLATE_INI="$template_root/MinecraftClient.ini" +} + +if [[ -z "$TEMPLATE_INI" ]]; then + generate_template_ini +fi + +mkdir -p "$(dirname "$OUTPUT_INI")" cp "$TEMPLATE_INI" "$OUTPUT_INI" sed_in_place \ @@ -46,6 +89,8 @@ sed_in_place \ -e 's#^AutoRespawn = false#AutoRespawn = true#' \ "$OUTPUT_INI" +disable_noisy_bots_in_ini "$OUTPUT_INI" + grep -Fq "AccountType = \"$ACCOUNT_TYPE\"" "$OUTPUT_INI" || { echo "Failed to enforce account type $ACCOUNT_TYPE in $OUTPUT_INI" >&2 exit 1 diff --git a/.skills/mcc-integration-testing/scripts/reset_shared_test_state.sh b/.skills/mcc-integration-testing/scripts/reset_shared_test_state.sh new file mode 100755 index 00000000..2d84ac1b --- /dev/null +++ b/.skills/mcc-integration-testing/scripts/reset_shared_test_state.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +# shellcheck source=tools/mcc-env.sh +source "$REPO_ROOT/tools/mcc-env.sh" +# shellcheck source=.skills/mcc-integration-testing/scripts/common.sh +source "$SCRIPT_DIR/common.sh" + +usage() { + cat <<'EOF' +Usage: reset_shared_test_state.sh [--all | ...] + +Kills shared tmux test sessions and removes stale stdin pipes. +EOF +} + +if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then + usage + exit 0 +fi + +kill_named_session() { + local session_name="$1" + tmux kill-session -t "$session_name" 2>/dev/null || true +} + +kill_named_session "mcc-debug" + +if [[ $# -eq 0 || "${1:-}" == "--all" ]]; then + while IFS= read -r session_name; do + [[ -z "$session_name" ]] && continue + kill_named_session "$session_name" + done < <(tmux list-sessions 2>/dev/null | awk -F: '/^mc-/{print $1}' || true) + + while IFS= read -r pipe_path; do + [[ -z "$pipe_path" ]] && continue + if [[ ! -p "$pipe_path" ]]; then + rm -f "$pipe_path" + fi + done < <(find "$MCC_SERVERS" -maxdepth 2 -name 'stdin.pipe' 2>/dev/null || true) +else + for version in "$@"; do + kill_named_session "$(server_session_name "$version")" + remove_stale_stdin_pipe "$version" + done +fi + +rm -f "$MCC_REPO/mcc_input.txt" diff --git a/.skills/mcc-integration-testing/scripts/run_achievements_matrix.sh b/.skills/mcc-integration-testing/scripts/run_achievements_matrix.sh index 45629525..65ff7d3f 100755 --- a/.skills/mcc-integration-testing/scripts/run_achievements_matrix.sh +++ b/.skills/mcc-integration-testing/scripts/run_achievements_matrix.sh @@ -64,6 +64,16 @@ run_version() { # shellcheck disable=SC1090 source "$summary_env" + if [[ -n "${MCC_LOG:-}" && ! -f "$MCC_LOG" ]]; then + NOTE="Harness failure: MCC log was not produced." + VERDICT="❌ Fail" + fi + + if [[ -n "${COMMAND_LOG:-}" && ! -f "$COMMAND_LOG" ]]; then + NOTE="Harness failure: command transcript was not produced." + VERDICT="❌ Fail" + fi + write_row "$VERSION" "$SERVER_DIR" "$PORT" "$FAMILY" "$INITIAL_STATUS" "$GRANT_STATUS" "$REVOKE_STATUS" \ "$API_STATUS" "$VERDICT" "$NOTE" "$RUN_DIR" "$MCC_LOG" "$COPIED_SERVER_LOG" "$COMMAND_LOG" } @@ -94,6 +104,7 @@ if ! command -v tmux >/dev/null 2>&1; then fi if [[ "$DOTNET_OK" == "yes" ]]; then + bash "$SCRIPT_DIR/preflight_test_env.sh" >/dev/null 2>&1 || true if ! dotnet build "$REPO_ROOT/MinecraftClient.sln" -c Release > "$BUILD_LOG" 2>&1; then BUILD_OK="no" fi diff --git a/.skills/mcc-integration-testing/scripts/run_achievements_test.sh b/.skills/mcc-integration-testing/scripts/run_achievements_test.sh index 88c2b027..df0236e0 100755 --- a/.skills/mcc-integration-testing/scripts/run_achievements_test.sh +++ b/.skills/mcc-integration-testing/scripts/run_achievements_test.sh @@ -5,14 +5,8 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" # shellcheck source=tools/mcc-env.sh source "$REPO_ROOT/tools/mcc-env.sh" - -sed_in_place() { - if [[ "$(uname)" == "Darwin" ]]; then - sed -i '' "$@" - else - sed -i "$@" - fi -} +# shellcheck source=.skills/mcc-integration-testing/scripts/common.sh +source "$SCRIPT_DIR/common.sh" usage() { cat <<'EOF' @@ -131,6 +125,7 @@ cleanup() { fi mc-stop "$SERVER_DIR" >/dev/null 2>&1 || true + wait_for_server_stop "$SERVER_DIR" 20 >/dev/null 2>&1 || true ln -sfn "$RUN_DIR" "$LATEST_LINK" write_summary } @@ -165,43 +160,6 @@ wait_for_file_pattern() { return 1 } -wait_for_server_ready() { - local timeout="${1:-60}" - local elapsed=0 - - while (( elapsed < timeout )); do - if mc-log "$SERVER_DIR" 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 -} - -disable_noisy_bots() { - sed_in_place '/^\[ChatBot.ScriptScheduler\]/,/^\[/ { s/^Enabled = true/Enabled = false/; }' "$REPO_ROOT/MinecraftClient.ini" - sed_in_place '/^\[ChatBot.DiscordRpc\]/,/^\[/ { s/^Enabled = true/Enabled = false/; }' "$REPO_ROOT/MinecraftClient.ini" - sed_in_place '/^\[ChatBot.AntiAFK\]/,/^\[/ { s/^Enabled = true/Enabled = false/; }' "$REPO_ROOT/MinecraftClient.ini" - sed_in_place '/^\[ChatBot.AutoDig\]/,/^\[/ { s/^Enabled = true/Enabled = false/; }' "$REPO_ROOT/MinecraftClient.ini" - sed_in_place '/^\[ChatBot.AutoAttack\]/,/^\[/ { s/^Enabled = true/Enabled = false/; }' "$REPO_ROOT/MinecraftClient.ini" - sed_in_place '/^\[ChatBot.PlayerListLogger\]/,/^\[/ { s/^Enabled = true/Enabled = false/; }' "$REPO_ROOT/MinecraftClient.ini" - sed_in_place '/^\[ChatBot.ReplayCapture\]/,/^\[/ { s/^Enabled = true/Enabled = false/; }' "$REPO_ROOT/MinecraftClient.ini" -} - -ensure_root_config() { - if [[ -f "$REPO_ROOT/MinecraftClient.ini" ]]; then - return - fi - - ( - cd "$REPO_ROOT" - dotnet run --project MinecraftClient -c Release --no-build -- --help >/dev/null 2>&1 - ) -} - write_probe_script() { cat > "$PROBE_SCRIPT" </dev/null 2>&1 || ! java -version >/dev/null 2>&1; then - fail "java was not found on PATH." -fi - -if ! command -v tmux >/dev/null 2>&1; then - fail "tmux was not found on PATH." -fi - -if [[ ! -d "$MCC_SERVERS/$SERVER_DIR" ]]; then - fail "Server directory not found: $MCC_SERVERS/$SERVER_DIR" -fi - -PORT="$(bash "$SCRIPT_DIR/get_server_port.sh" "$SERVER_DIR")" - -ensure_root_config -"$SCRIPT_DIR/ensure_offline_server.sh" "$SERVER_DIR" -disable_noisy_bots -write_probe_script - -if [[ "$PROFILE" == "legacy" && -f "$MCC_SERVERS/$SERVER_DIR/server.properties" ]]; then - sed_in_place 's/^use-native-transport=.*/use-native-transport=false/' "$MCC_SERVERS/$SERVER_DIR/server.properties" -fi - if $DO_BUILD; then log_step "BUILD> dotnet build MinecraftClient.sln -c Release" mcc-build > "$BUILD_LOG" 2>&1 || fail "dotnet build failed." @@ -344,17 +279,35 @@ else : > "$BUILD_LOG" fi +bash "$SCRIPT_DIR/preflight_test_env.sh" "$SERVER_DIR" >/dev/null || fail "Test environment preflight failed." +bash "$SCRIPT_DIR/reset_shared_test_state.sh" "$SERVER_DIR" >/dev/null || fail "Failed to reset shared test state." + +if [[ ! -d "$MCC_SERVERS/$SERVER_DIR" ]]; then + fail "Server directory not found: $MCC_SERVERS/$SERVER_DIR" +fi + +bash "$SCRIPT_DIR/prepare_offline_mcc_config.sh" "$CFG" "$MC_VERSION" CursorBot >/dev/null || fail "Failed to prepare temporary MCC config." +PORT="$(bash "$SCRIPT_DIR/get_server_port.sh" "$SERVER_DIR")" + +"$SCRIPT_DIR/ensure_offline_server.sh" "$SERVER_DIR" +write_probe_script + +if [[ "$PROFILE" == "legacy" && -f "$MCC_SERVERS/$SERVER_DIR/server.properties" ]]; then + sed_in_place 's/^use-native-transport=.*/use-native-transport=false/' "$MCC_SERVERS/$SERVER_DIR/server.properties" +fi + : > "$INPUT_FILE" rm -f "$MCC_LOG" log_step "Starting server $SERVER_DIR on port $PORT" mc-start "$SERVER_DIR" >/dev/null -wait_for_server_ready || fail "Server did not become ready." +wait_for_server_ready "$SERVER_DIR" || fail "Server did not become ready." log_step "Starting MCC for $MC_VERSION" ( cd "$REPO_ROOT" MCC_FILE_INPUT=1 dotnet run --project MinecraftClient -c Release --no-build -- \ + "$CFG" \ CursorBot \ - \ "localhost:$PORT" \ diff --git a/.skills/mcc-integration-testing/scripts/run_full_spectrum_test.sh b/.skills/mcc-integration-testing/scripts/run_full_spectrum_test.sh index 2db7d2a8..51021974 100755 --- a/.skills/mcc-integration-testing/scripts/run_full_spectrum_test.sh +++ b/.skills/mcc-integration-testing/scripts/run_full_spectrum_test.sh @@ -5,6 +5,8 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" # shellcheck source=tools/mcc-env.sh source "$REPO_ROOT/tools/mcc-env.sh" +# shellcheck source=.skills/mcc-integration-testing/scripts/common.sh +source "$SCRIPT_DIR/common.sh" VERSION="${1:-1.21.11-Vanilla}" MC_VERSION="${VERSION%-Vanilla}" @@ -34,46 +36,12 @@ cleanup() { fi mc-stop "$VERSION" >/dev/null 2>&1 || true + wait_for_server_stop "$VERSION" 20 >/dev/null 2>&1 || true } trap cleanup EXIT prepare_config() { - bash "$SCRIPT_DIR/prepare_offline_mcc_config.sh" "$REPO_ROOT/MinecraftClient.ini" "$CFG" "$MC_VERSION" >/dev/null -} - -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 + bash "$SCRIPT_DIR/prepare_offline_mcc_config.sh" "$CFG" "$MC_VERSION" CursorBot >/dev/null } wait_for_server_log_pattern() { @@ -101,6 +69,25 @@ capture_server_logs() { fi } +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 +} + fail() { capture_server_logs echo "FAIL: $1" >&2 @@ -146,18 +133,19 @@ run_mcc_command() { sleep 2 } +bash "$SCRIPT_DIR/preflight_test_env.sh" "$VERSION" >/dev/null +bash "$SCRIPT_DIR/reset_shared_test_state.sh" "$VERSION" >/dev/null "$SCRIPT_DIR/ensure_offline_server.sh" "$VERSION" +echo "Building MCC..." +mcc-build > "$BUILD_LOG" 2>&1 || fail "mcc-build failed" prepare_config SERVER_PORT="$(bash "$SCRIPT_DIR/get_server_port.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" +wait_for_server_ready "$VERSION" || fail "Server did not become ready" echo "Starting MCC..." ( diff --git a/.skills/mcc-integration-testing/scripts/summarize_achievements_matrix.sh b/.skills/mcc-integration-testing/scripts/summarize_achievements_matrix.sh index 9dbeb78c..6ef72397 100755 --- a/.skills/mcc-integration-testing/scripts/summarize_achievements_matrix.sh +++ b/.skills/mcc-integration-testing/scripts/summarize_achievements_matrix.sh @@ -54,4 +54,4 @@ echo "## Inferred" echo echo "- Only rows with real MCC and server-log artifacts count as executed proof." echo "- Rows blocked by missing Java, tmux, or server directories are environment-limited, not product pass results." -echo "- Legacy rows remain the highest-risk bucket because static inspection suggests pre-1.12 \`Statistics\` packets may not currently reach the achievements handler." +echo "- Rows with missing MCC or command-log artifacts should be treated as harness failures until rerun confirms a product issue." diff --git a/.skills/mcc-version-adaptation/SKILL.md b/.skills/mcc-version-adaptation/SKILL.md index 195b5592..706399cb 100644 --- a/.skills/mcc-version-adaptation/SKILL.md +++ b/.skills/mcc-version-adaptation/SKILL.md @@ -15,6 +15,7 @@ Systematic workflow for updating Minecraft Console Client to support a new Minec $MCC_REPO/tools/decompile.sh --version ``` This auto-downloads `MinecraftDecompiler.jar` if needed, produces the decompiled source, and downloads `server.jar` into `$MCC_SERVERS//`. +- `tools/decompile.sh` depends on official mappings. For older versions where it refuses to decompile, fall back to a raw Java decompiler such as `cfr-decompiler` against `$MCC_SERVERS//server.jar`. That fallback is good enough for packet inspection and registration order checks even when the output is obfuscated. - A test server of the target version in `$MCC_SERVERS//` (see `mcc-dev-workflow` skill) ## Step 0: Generate Server Reports (CRITICAL since 1.21.9) diff --git a/tools/mcc-debug.sh b/tools/mcc-debug.sh index b29f4b20..c5c6205e 100644 --- a/tools/mcc-debug.sh +++ b/tools/mcc-debug.sh @@ -32,6 +32,7 @@ EOF VERSION="1.21.11-Vanilla" MODE="classic" PORT="25565" +PORT_SET_BY_USER=false DO_BUILD=true DEBUG_ON=false FILE_INPUT=false @@ -40,7 +41,7 @@ while [[ $# -gt 0 ]]; do case "$1" in -v|--version) VERSION="$2"; shift 2 ;; -m|--mode) MODE="$2"; shift 2 ;; - -p|--port) PORT="$2"; shift 2 ;; + -p|--port) PORT="$2"; PORT_SET_BY_USER=true; shift 2 ;; --no-build) DO_BUILD=false; shift ;; --debug-on) DEBUG_ON=true; shift ;; --file-input) FILE_INPUT=true; shift ;; @@ -54,6 +55,10 @@ CFG="$TEST_ROOT/MinecraftClient.debug.ini" MCC_LOG="$TEST_ROOT/mcc-debug.log" INPUT_FILE="$REPO_ROOT/mcc_input.txt" SESSION_NAME="mc-${VERSION//\./_}" +PREPARE_CFG_SCRIPT="$REPO_ROOT/.skills/mcc-integration-testing/scripts/prepare_offline_mcc_config.sh" +ENSURE_SERVER_SCRIPT="$REPO_ROOT/.skills/mcc-integration-testing/scripts/ensure_offline_server.sh" +PREFLIGHT_SCRIPT="$REPO_ROOT/.skills/mcc-integration-testing/scripts/preflight_test_env.sh" +GET_PORT_SCRIPT="$REPO_ROOT/.skills/mcc-integration-testing/scripts/get_server_port.sh" mkdir -p "$TEST_ROOT" @@ -64,6 +69,8 @@ echo " Config: $CFG" echo " Log: $MCC_LOG" echo "" +bash "$PREFLIGHT_SCRIPT" "$VERSION" >/dev/null + # --- Build --- if $DO_BUILD; then echo "[1/4] Building MCC..." @@ -75,21 +82,22 @@ fi # --- Prepare config --- echo "[2/4] Preparing config..." -cp "$REPO_ROOT/MinecraftClient.ini" "$CFG" - -sed -i \ - -e 's/Account = { Login = "[^"]*", Password = "[^"]*" }/Account = { Login = "CursorBot", Password = "-" }/' \ - -e 's/TerrainAndMovements = false/TerrainAndMovements = true/' \ - -e 's/InventoryHandling = false/InventoryHandling = true/' \ - -e 's/EntityHandling = false/EntityHandling = true/' \ - "$CFG" +bash "$PREPARE_CFG_SCRIPT" "$CFG" "${VERSION%-Vanilla}" CursorBot >/dev/null if [[ "$MODE" == "tui" ]]; then - sed -i 's/ConsoleMode = "classic"/ConsoleMode = "tui"/' "$CFG" + if [[ "$(uname)" == "Darwin" ]]; then + sed -i '' 's/ConsoleMode = "classic"/ConsoleMode = "tui"/' "$CFG" + else + sed -i 's/ConsoleMode = "classic"/ConsoleMode = "tui"/' "$CFG" + fi fi if $DEBUG_ON; then - sed -i 's/DebugMessages = false/DebugMessages = true/' "$CFG" + if [[ "$(uname)" == "Darwin" ]]; then + sed -i '' 's/DebugMessages = false/DebugMessages = true/' "$CFG" + else + sed -i 's/DebugMessages = false/DebugMessages = true/' "$CFG" + fi fi echo " Config ready" @@ -99,14 +107,7 @@ echo "[3/4] Starting server $VERSION..." if tmux has-session -t "$SESSION_NAME" 2>/dev/null; then echo " Server already running" else - # Ensure offline mode - SERVER_DIR="$MCC_SERVERS/$VERSION" - if [[ -f "$SERVER_DIR/server.properties" ]]; then - sed -i 's/^online-mode=.*/online-mode=false/' "$SERVER_DIR/server.properties" - grep -q "^enable-rcon=" "$SERVER_DIR/server.properties" || echo "enable-rcon=true" >> "$SERVER_DIR/server.properties" - grep -q "^rcon.password=" "$SERVER_DIR/server.properties" || echo "rcon.password=test123" >> "$SERVER_DIR/server.properties" - grep -q "^rcon.port=" "$SERVER_DIR/server.properties" || echo "rcon.port=25575" >> "$SERVER_DIR/server.properties" - fi + bash "$ENSURE_SERVER_SCRIPT" "$VERSION" >/dev/null mc-start "$VERSION" >/dev/null echo -n " Waiting for server..." @@ -125,6 +126,10 @@ else done fi +if ! $PORT_SET_BY_USER; then + PORT="$(bash "$GET_PORT_SCRIPT" "$VERSION")" +fi + # --- Launch MCC --- echo "[4/4] Launching MCC in $MODE mode..." : > "$INPUT_FILE" diff --git a/tools/mcc-env.sh b/tools/mcc-env.sh index 6ddca998..904a8a00 100644 --- a/tools/mcc-env.sh +++ b/tools/mcc-env.sh @@ -26,6 +26,9 @@ mc-cmd() { local v="${2:-1.20.6}"; echo "$1" > "$MCC_SERVERS/$v/stdin.pipe"; } mc-log() { local s; s=$(_mc-session "${1:-1.20.6}"); tmux capture-pane -t "$s" -p -S "-${2:-50}"; } mc-kill() { local v="${1:-1.20.6}" s; s=$(_mc-session "$v"); tmux kill-session -t "$s" 2>/dev/null; rm -f "$MCC_SERVERS/$v/stdin.pipe"; echo "Killed $s"; } mc-list() { tmux list-sessions 2>/dev/null | grep "^mc-" || echo "No running MC servers"; } +mc-wait-ready() { bash "$MCC_REPO/.skills/mcc-integration-testing/scripts/preflight_test_env.sh" "${1:-1.20.6}" >/dev/null && source "$MCC_REPO/.skills/mcc-integration-testing/scripts/common.sh" && wait_for_server_ready "${1:-1.20.6}" "${2:-60}"; } +mc-wait-stop() { source "$MCC_REPO/.skills/mcc-integration-testing/scripts/common.sh" && wait_for_server_stop "${1:-1.20.6}" "${2:-60}"; } +mc-reset-test-env() { bash "$MCC_REPO/.skills/mcc-integration-testing/scripts/reset_shared_test_state.sh" "$@"; } # --- RCON --- mc-rcon() { bash "$MCC_REPO/tools/mc-rcon.sh" "$@"; } @@ -59,3 +62,4 @@ mcc-tui() { mcc-debug() { bash "$MCC_REPO/tools/mcc-debug.sh" "$@"; } mcc-log-mcc() { tail -f "${TMPDIR:-/tmp}/mcc-debug/mcc-debug.log" 2>/dev/null || echo "No MCC log found"; } mcc-state() { echo "debug state" >> "$MCC_REPO/mcc_input.txt"; sleep 1; tail -30 "${TMPDIR:-/tmp}/mcc-debug/mcc-debug.log" 2>/dev/null; } +mcc-preflight() { bash "$MCC_REPO/.skills/mcc-integration-testing/scripts/preflight_test_env.sh" "$@"; } diff --git a/tools/run-creative-e2e.sh b/tools/run-creative-e2e.sh index 35555ad9..83fdb42a 100644 --- a/tools/run-creative-e2e.sh +++ b/tools/run-creative-e2e.sh @@ -5,6 +5,8 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" # shellcheck source=tools/mcc-env.sh source "$REPO_ROOT/tools/mcc-env.sh" +# shellcheck source=.skills/mcc-integration-testing/scripts/common.sh +source "$REPO_ROOT/.skills/mcc-integration-testing/scripts/common.sh" usage() { cat <<'EOF' @@ -37,6 +39,7 @@ MCC_LOG="$TEST_ROOT/mcc.log" SERVER_LOG_FILE="$MCC_SERVERS/$SERVER_DIR/logs/latest.log" INPUT_FILE="$REPO_ROOT/mcc_input.txt" MCC_PID="" +SERVER_PORT="25565" mkdir -p "$TEST_ROOT" @@ -59,33 +62,6 @@ wait_for_file_pattern() { return 1 } -wait_for_server_ready() { - local timeout="${1:-60}" - local elapsed=0 - - while (( elapsed < timeout )); do - if mc-log "$SERVER_DIR" 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 -} - -kill_other_servers() { - local sessions - sessions="$(tmux list-sessions 2>/dev/null | awk -F: '/^mc-/{print $1}' || true)" - if [[ -n "$sessions" ]]; then - while IFS= read -r session; do - [[ -z "$session" ]] && continue - tmux kill-session -t "$session" 2>/dev/null || true - done <<< "$sessions" - fi -} - cleanup() { if [[ -n "${MCC_PID:-}" ]] && kill -0 "$MCC_PID" 2>/dev/null; then echo "quit" >> "$INPUT_FILE" 2>/dev/null || true @@ -96,7 +72,7 @@ cleanup() { if [[ -p "$MCC_SERVERS/$SERVER_DIR/stdin.pipe" ]]; then echo "stop" > "$MCC_SERVERS/$SERVER_DIR/stdin.pipe" 2>/dev/null || true - sleep 2 + wait_for_server_stop "$SERVER_DIR" 20 >/dev/null 2>&1 || true fi tmux kill-session -t "$SESSION_NAME" 2>/dev/null || true @@ -105,24 +81,7 @@ cleanup() { trap cleanup EXIT prepare_config() { - cp "$REPO_ROOT/MinecraftClient.ini" "$CFG" - - sed -i \ - -e 's/Account = { Login = "test", Password = "-" }/Account = { Login = "CursorBot", Password = "-" }/' \ - -e "s/MinecraftVersion = \"auto\"/MinecraftVersion = \"$MC_VERSION\"/" \ - -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" - - sed -i '/^\[ChatBot.ScriptScheduler\]/,/^\[/ { s/^Enabled = true/Enabled = false/; }' "$CFG" - sed -i '/^\[ChatBot.DiscordRpc\]/,/^\[/ { s/^Enabled = true/Enabled = false/; }' "$CFG" - sed -i '/^\[ChatBot.AntiAFK\]/,/^\[/ { s/^Enabled = true/Enabled = false/; }' "$CFG" - sed -i '/^\[ChatBot.AutoDig\]/,/^\[/ { s/^Enabled = true/Enabled = false/; }' "$CFG" - sed -i '/^\[ChatBot.AutoAttack\]/,/^\[/ { s/^Enabled = true/Enabled = false/; }' "$CFG" - sed -i '/^\[ChatBot.PlayerListLogger\]/,/^\[/ { s/^Enabled = true/Enabled = false/; }' "$CFG" - sed -i '/^\[ChatBot.ReplayCapture\]/,/^\[/ { s/^Enabled = true/Enabled = false/; }' "$CFG" + bash "$REPO_ROOT/.skills/mcc-integration-testing/scripts/prepare_offline_mcc_config.sh" "$CFG" "$MC_VERSION" CursorBot >/dev/null } send_mcc_command() { @@ -195,23 +154,30 @@ modern_mob_and_effects() { run_server_command "effect give CursorBot minecraft:regeneration 10 1 true" } +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 -kill_other_servers rm -f "$MCC_LOG" "$INPUT_FILE" bash "$REPO_ROOT/.skills/mcc-integration-testing/scripts/ensure_offline_server.sh" "$SERVER_DIR" >/dev/null +SERVER_PORT="$(bash "$REPO_ROOT/.skills/mcc-integration-testing/scripts/get_server_port.sh" "$SERVER_DIR")" if [[ -f "$MCC_SERVERS/$SERVER_DIR/server.properties" ]]; then - sed -i 's/^use-native-transport=.*/use-native-transport=false/' "$MCC_SERVERS/$SERVER_DIR/server.properties" + sed_in_place 's/^use-native-transport=.*/use-native-transport=false/' "$MCC_SERVERS/$SERVER_DIR/server.properties" fi mc-start "$SERVER_DIR" >/dev/null -wait_for_server_ready || exit 1 +wait_for_server_ready "$SERVER_DIR" || exit 1 : > "$INPUT_FILE" ( cd "$REPO_ROOT" - MCC_FILE_INPUT=1 dotnet run --project MinecraftClient -c Release --no-build -- "$CFG" > "$MCC_LOG" 2>&1 + MCC_FILE_INPUT=1 dotnet run --project MinecraftClient -c Release --no-build -- \ + "$CFG" \ + CursorBot \ + - \ + "localhost:$SERVER_PORT" \ + > "$MCC_LOG" 2>&1 ) & MCC_PID=$! diff --git a/tools/start-server.sh b/tools/start-server.sh index 9debbae9..63e5eed3 100644 --- a/tools/start-server.sh +++ b/tools/start-server.sh @@ -1,12 +1,38 @@ #!/bin/bash # Start a Minecraft server in a tmux session with named pipe for stdin # Servers live under $MCC_SERVERS or default to MinecraftOfficial/downloads//. +resolve_java_bin() { + if command -v java >/dev/null 2>&1 && java -version >/dev/null 2>&1; then + command -v java + return 0 + fi + + local candidate + for candidate in \ + "${JAVA_BIN:-}" \ + "/opt/homebrew/opt/openjdk/bin/java" \ + "/usr/local/opt/openjdk/bin/java" \ + "/usr/lib/jvm/default-java/bin/java" + do + [[ -z "$candidate" ]] && continue + if [[ -x "$candidate" ]]; then + if "$candidate" -version >/dev/null 2>&1; then + printf '%s\n' "$candidate" + return 0 + fi + fi + done + + return 1 +} + VERSION="${1}" REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" DOWNLOADS="${MCC_SERVERS:-$REPO_ROOT/MinecraftOfficial/downloads}" DIR="$DOWNLOADS/$VERSION" PIPE="$DIR/stdin.pipe" SESSION="mc-${VERSION//\./_}" +JAVA_BIN="$(resolve_java_bin || true)" if [ -z "$VERSION" ] || [ ! -d "$DIR" ]; then echo "Error: Server directory not found${VERSION:+: $DIR}" @@ -20,6 +46,16 @@ if [ ! -f "$DIR/server.jar" ]; then exit 1 fi +if ! command -v tmux >/dev/null 2>&1; then + echo "Error: tmux is required to start local test servers" + exit 1 +fi + +if [[ -z "$JAVA_BIN" ]]; then + echo "Error: Java was not found on PATH. Install Java or set JAVA_BIN." >&2 + exit 1 +fi + if tmux has-session -t "$SESSION" 2>/dev/null; then echo "Server $VERSION already running in tmux session '$SESSION'" echo "View output: tmux capture-pane -t '$SESSION' -p -S -50" @@ -29,10 +65,14 @@ fi rm -f "$DIR/world/session.lock" +if [[ -e "$PIPE" && ! -p "$PIPE" ]]; then + rm -f "$PIPE" +fi + [ -p "$PIPE" ] || mkfifo "$PIPE" tmux new-session -d -s "$SESSION" -c "$DIR" \ - "tail -f $PIPE | java -Xmx2G -Xms2G -jar server.jar nogui 2>&1" + "tail -f $PIPE | '$JAVA_BIN' -Xmx2G -Xms2G -jar server.jar nogui 2>&1" echo "Server $VERSION started in tmux session '$SESSION'" echo "Send commands: echo 'say hello' > $PIPE"