diff --git a/.skills/mcc-version-adaptation/SKILL.md b/.skills/mcc-version-adaptation/SKILL.md index 44023b06..ce21cf04 100644 --- a/.skills/mcc-version-adaptation/SKILL.md +++ b/.skills/mcc-version-adaptation/SKILL.md @@ -10,13 +10,12 @@ Systematic workflow for updating Minecraft Console Client to support a new Minec ## Prerequisites - Decompiled server source for both the old and new MC versions in `$MCC_REPO/MinecraftOfficial/-decompiled/` -- If missing, decompile first: +- If missing, decompile and download server.jar: ```bash - cd $MCC_REPO/MinecraftOfficial - java -jar MinecraftDecompiler.jar --version --side SERVER \ - --decompile --output -remapped.jar --decompiled-output -decompiled + $MCC_REPO/tools/decompile.sh --version ``` -- A test server of the target version in `$MCC_SERVERS/` (see `mcc-dev-workflow` skill) + This auto-downloads `MinecraftDecompiler.jar` if needed, produces the decompiled source, and downloads `server.jar` into `$MCC_SERVERS//`. +- 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) @@ -24,7 +23,7 @@ Systematic workflow for updating Minecraft Console Client to support a new Minec ```bash cd /tmp && java -DbundlerMainClass=net.minecraft.data.Main \ - -jar $MCC_SERVERS/-Vanilla/server.jar \ + -jar $MCC_SERVERS//server.jar \ --reports --output /tmp/mc_reports ``` @@ -186,7 +185,37 @@ Compare key packet codec classes between versions. Known changes: When in doubt, compare the relevant packet class (e.g. `ClientboundAddEntityPacket.java`) between versions. -## Step 8: Compile and Verify +## Step 8: Update Block Collision Shapes (Physics Engine) + +MCC's physics engine uses block collision shape data from PrismarineJS `minecraft-data` to perform accurate AABB collision detection (stored in `MinecraftClient/Physics/BlockShapeData.json`, embedded as a resource). + +When a new MC version introduces new blocks or changes block shapes, update this data: + +```bash +# Download and compact collision shapes for the target version +python3 $MCC_REPO/tools/gen_block_shapes.py +# e.g. python3 tools/gen_block_shapes.py 1.21.11 +``` + +If network is slow or unreliable, download the file manually and convert: +```bash +# Manual download +curl -L -o /tmp/bcs.json \ + "https://raw.githubusercontent.com/PrismarineJS/minecraft-data/master/data/pc//blockCollisionShapes.json" + +# Then compact from local file +python3 $MCC_REPO/tools/gen_block_shapes.py --from-file /tmp/bcs.json +``` + +Output: `MinecraftClient/Physics/BlockShapeData.json` (embedded via `MinecraftClient.csproj`) + +The JSON maps block names (snake_case) → collision shape IDs → AABB coordinates. At runtime, `BlockShapes.cs` maps MCC's block state IDs to these AABBs using the block palette. + +**When to update**: Whenever new blocks are added that have non-trivial collision shapes (e.g., new slab variants, stairs, fences). If only items or entities changed, this step can be skipped. + +**Data source**: PrismarineJS `minecraft-data` repo, path: `data/pc//blockCollisionShapes.json`. Version availability can be checked via `data/dataPaths.json`. + +## Step 9: Compile and Verify ```bash dotnet build $MCC_REPO/MinecraftClient.sln -c Release @@ -244,3 +273,4 @@ All scripts are in `$MCC_REPO/tools/`. See `tools/README.md` for detailed usage. | `gen_block_palette.py` | Generate BlockPalette C# | blocks.json | | `gen_entity_palette.py` | Generate EntityPalette C# | registries.json | | `gen_entity_metadata_palette.py` | Generate EntityMetadataPalette C# | Decompiled source | +| `gen_block_shapes.py` | Download & compact block collision shapes | PrismarineJS minecraft-data | diff --git a/tools/README.md b/tools/README.md index f8ba7226..17c2d5cb 100644 --- a/tools/README.md +++ b/tools/README.md @@ -18,17 +18,21 @@ Two types of data can be used as input: ### Decompiling a new MC version ```bash -cd MinecraftOfficial -java -jar MinecraftDecompiler.jar --version 1.21.9 --side SERVER \ - --decompile --output 1.21.9-remapped.jar --decompiled-output 1.21.9-decompiled +# Server side (default) — also downloads server.jar into MinecraftOfficial/downloads// +tools/decompile.sh --version 1.21.9 + +# Client side +tools/decompile.sh --version 1.21.9 --side CLIENT ``` +The script auto-downloads `MinecraftDecompiler.jar` from GitHub releases if it doesn't exist. + ### Generating server data reports ```bash cd /tmp java -DbundlerMainClass=net.minecraft.data.Main \ - -jar /path/to/server.jar \ + -jar $MCC_SERVERS//server.jar \ --reports --output /tmp/mc_reports ``` diff --git a/tools/decompile.sh b/tools/decompile.sh new file mode 100644 index 00000000..cd17ab7a --- /dev/null +++ b/tools/decompile.sh @@ -0,0 +1,138 @@ +#!/bin/bash +# Download (if needed) and run MinecraftDecompiler to produce decompiled source +# and server.jar for a given Minecraft version. +# +# Usage: +# ./tools/decompile.sh --version [--side SERVER|CLIENT] +# +# Examples: +# ./tools/decompile.sh --version 1.21.11 +# ./tools/decompile.sh --version 1.21.11 --side CLIENT + +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" +MC_OFFICIAL="$REPO_ROOT/MinecraftOfficial" +DECOMPILER_JAR="$MC_OFFICIAL/MinecraftDecompiler.jar" +DECOMPILER_REPO="MaxPixelStudios/MinecraftDecompiler" + +VERSION="" +SIDE="SERVER" + +while [[ $# -gt 0 ]]; do + case "$1" in + --version) VERSION="$2"; shift 2 ;; + --side) SIDE="$(echo "$2" | tr '[:lower:]' '[:upper:]')"; shift 2 ;; + -h|--help) + echo "Usage: $0 --version [--side SERVER|CLIENT]" + echo "" + echo "Options:" + echo " --version Minecraft version (e.g. 1.21.11)" + echo " --side SERVER (default) or CLIENT" + exit 0 + ;; + *) echo "Unknown option: $1"; exit 1 ;; + esac +done + +if [[ -z "$VERSION" ]]; then + echo "Error: --version is required" + echo "Usage: $0 --version [--side SERVER|CLIENT]" + exit 1 +fi + +if [[ "$SIDE" != "SERVER" && "$SIDE" != "CLIENT" ]]; then + echo "Error: --side must be SERVER or CLIENT (got: $SIDE)" + exit 1 +fi + +# --- Ensure MinecraftDecompiler.jar exists --- +if [[ ! -f "$DECOMPILER_JAR" ]]; then + echo "MinecraftDecompiler.jar not found, downloading latest release..." + DOWNLOAD_URL=$(curl -sL "https://api.github.com/repos/$DECOMPILER_REPO/releases/latest" \ + | python3 -c " +import json, sys +data = json.load(sys.stdin) +for a in data['assets']: + if a['name'] == 'MinecraftDecompiler.jar': + print(a['browser_download_url']) + break +") + if [[ -z "$DOWNLOAD_URL" ]]; then + echo "Error: could not find MinecraftDecompiler.jar in latest release" + exit 1 + fi + echo "Downloading from $DOWNLOAD_URL ..." + curl -L -o "$DECOMPILER_JAR" "$DOWNLOAD_URL" + echo "Downloaded MinecraftDecompiler.jar" +fi + +# --- Build output paths --- +SIDE_LOWER="$(echo "$SIDE" | tr '[:upper:]' '[:lower:]')" + +if [[ "$SIDE" == "SERVER" ]]; then + REMAPPED_JAR="$MC_OFFICIAL/remapped_jar/${VERSION}-remapped.jar" + DECOMPILED_DIR="$MC_OFFICIAL/${VERSION}-decompiled" +else + REMAPPED_JAR="$MC_OFFICIAL/remapped_jar/${VERSION}-${SIDE_LOWER}-remapped.jar" + DECOMPILED_DIR="$MC_OFFICIAL/${VERSION}-${SIDE_LOWER}-decompiled" +fi + +if [[ -d "$DECOMPILED_DIR" ]]; then + echo "Decompiled source already exists: $DECOMPILED_DIR" + echo "Delete it first if you want to re-decompile." + exit 0 +fi + +mkdir -p "$MC_OFFICIAL/remapped_jar" + +echo "=== Decompiling Minecraft $VERSION ($SIDE) ===" +echo " Remapped JAR: $REMAPPED_JAR" +echo " Decompiled: $DECOMPILED_DIR" +echo "" + +cd "$MC_OFFICIAL" +java -jar "$DECOMPILER_JAR" \ + --version "$VERSION" \ + --side "$SIDE" \ + --decompile \ + --output "$REMAPPED_JAR" \ + --decompiled-output "$DECOMPILED_DIR" + +echo "" +echo "=== Done ===" +echo "Decompiled source: $DECOMPILED_DIR" + +# --- For SERVER side, also ensure downloads//server.jar exists --- +if [[ "$SIDE" == "SERVER" ]]; then + DOWNLOADS_DIR="$MC_OFFICIAL/downloads/$VERSION" + if [[ ! -f "$DOWNLOADS_DIR/server.jar" ]]; then + mkdir -p "$DOWNLOADS_DIR" + # MinecraftDecompiler downloads the original jar into its cache; + # extract it from the bundled remapped jar or re-download via manifest. + echo "" + echo "Downloading server.jar for $VERSION into $DOWNLOADS_DIR ..." + MANIFEST_URL="https://launchermeta.mojang.com/mc/game/version_manifest_v2.json" + VERSION_URL=$(curl -sL "$MANIFEST_URL" | python3 -c " +import json, sys +data = json.load(sys.stdin) +for v in data['versions']: + if v['id'] == '$VERSION': + print(v['url']) + break +") + if [[ -n "$VERSION_URL" ]]; then + SERVER_JAR_URL=$(curl -sL "$VERSION_URL" | python3 -c " +import json, sys +data = json.load(sys.stdin) +print(data['downloads']['server']['url']) +") + curl -L -o "$DOWNLOADS_DIR/server.jar" "$SERVER_JAR_URL" + echo "Downloaded server.jar" + else + echo "Warning: could not find version $VERSION in Mojang manifest; server.jar not downloaded." + fi + else + echo "server.jar already exists: $DOWNLOADS_DIR/server.jar" + fi +fi diff --git a/tools/mc-rcon.sh b/tools/mc-rcon.sh new file mode 100644 index 00000000..5017358a --- /dev/null +++ b/tools/mc-rcon.sh @@ -0,0 +1,46 @@ +#!/bin/bash +# Send an RCON command to a Minecraft server +# Usage: mc-rcon.sh [port] [password] +set -euo pipefail + +CMD="${1:?Usage: mc-rcon.sh [port] [password]}" +PORT="${2:-25575}" +PW="${3:-test123}" + +python3 -c " +import socket, struct, sys + +s = socket.socket() +s.settimeout(5) +try: + s.connect(('localhost', $PORT)) +except Exception as e: + print(f'Connection failed: {e}', file=sys.stderr) + sys.exit(1) + +def send(req_id, pkt_type, body): + body = body.encode() + s.send(struct.pack(' underscores) +_mc-session() { echo "mc-${1//\./_}"; } + +# --- Minecraft Server Management --- +mc-start() { "$MCC_REPO/tools/start-server.sh" "${1:-1.20.6}"; } +mc-stop() { local v="${1:-1.20.6}"; echo "stop" > "$MCC_SERVERS/$v/stdin.pipe"; } +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"; } + +# --- RCON --- +mc-rcon() { "$MCC_REPO/tools/mc-rcon.sh" "$@"; } + +# --- MCC Build/Run --- +mcc-build() { dotnet build "$MCC_REPO/MinecraftClient.sln" -c Release; } +mcc-run() { cd "$MCC_REPO" && MCC_FILE_INPUT=1 dotnet run --project MinecraftClient -c Release -- CursorBot - "localhost:${1:-25565}" 2>&1; } +mcc-cmd() { echo "$1" >> "$MCC_REPO/mcc_input.txt"; } +mcc-kill() { pkill -f "MinecraftClient" 2>/dev/null && echo "MCC killed" || echo "No MCC process found"; } +mcc-reload() { + mcc-kill + sleep 1 + mcc-build && mcc-run +} diff --git a/tools/start-server.sh b/tools/start-server.sh new file mode 100644 index 00000000..f4a8ccd7 --- /dev/null +++ b/tools/start-server.sh @@ -0,0 +1,39 @@ +#!/bin/bash +# Start a Minecraft server in a tmux session with named pipe for stdin +# Servers live in MinecraftOfficial/downloads// alongside the downloaded server.jar +VERSION="${1}" +REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" +DOWNLOADS="$REPO_ROOT/MinecraftOfficial/downloads" +DIR="$DOWNLOADS/$VERSION" +PIPE="$DIR/stdin.pipe" +SESSION="mc-${VERSION//\./_}" + +if [ -z "$VERSION" ] || [ ! -d "$DIR" ]; then + echo "Error: Server directory not found${VERSION:+: $DIR}" + echo "Available versions:" + ls "$DOWNLOADS" | grep -E '^[0-9]' | sort -V + exit 1 +fi + +if [ ! -f "$DIR/server.jar" ]; then + echo "Error: No server.jar in $DIR" + 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" + echo "Send command: echo 'say hello' > $PIPE" + exit 0 +fi + +rm -f "$DIR/world/session.lock" + +[ -p "$PIPE" ] || mkfifo "$PIPE" + +tmux new-session -d -s "$SESSION" -c "$DIR" \ + "tail -f $PIPE | java -Xmx2G -Xms2G -jar server.jar nogui 2>&1" + +echo "Server $VERSION started in tmux session '$SESSION'" +echo "Send commands: echo 'say hello' > $PIPE" +echo "View output: tmux capture-pane -t '$SESSION' -p -S -50"