feat: add multiplayer capture vertical slice

This commit is contained in:
2026-08-15 09:09:17 +02:00
parent 9229040382
commit c418e2f36c
24 changed files with 3989 additions and 47 deletions
+29
View File
@@ -0,0 +1,29 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
# shellcheck source=lib.sh
source "${SCRIPT_DIR}/lib.sh"
require_command git
require_command pnpm
stage_parent="$(make_stage_parent)"
trap 'safe_remove_stage "${stage_parent}"' EXIT
source_dir="${stage_parent}/source"
prepare_source "${source_dir}"
run_bun install --cwd "${source_dir}" --frozen-lockfile
run_bun test --cwd "${source_dir}"
run_bun run --cwd "${source_dir}" build
output_dir="${TOOL_ROOT}/dist"
mkdir -p -- "${output_dir}"
cp -- "${source_dir}/dist/mcp.js" "${output_dir}/mcp-projectcapture-1.6.1.js"
cp -- "${source_dir}/dist/icon.svg" "${output_dir}/mcp-icon.svg"
cp -- "${source_dir}/dist/about.md" "${output_dir}/mcp-about.md"
bundle_hash="$(sha256_file "${output_dir}/mcp-projectcapture-1.6.1.js")"
write_dist_checksums "${output_dir}"
printf 'Gehärtetes Build erstellt: %s\nSHA-256: %s\n' \
"${output_dir}/mcp-projectcapture-1.6.1.js" "${bundle_hash}"
+51
View File
@@ -0,0 +1,51 @@
#!/usr/bin/env bash
set -euo pipefail
umask 077
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
# shellcheck source=lib.sh
source "${SCRIPT_DIR}/lib.sh"
require_command git
if [[ "$#" -ne 2 ]]; then
printf 'Verwendung: %s AUSGABEDATEI ABSOLUTER_ASSET_ROOT\n' "$0" >&2
exit 2
fi
output="$1"
asset_root="$2"
output_parent="$(CDPATH= cd -- "$(dirname -- "${output}")" && pwd -P)" || {
printf 'Das Ausgabeverzeichnis muss bereits existieren.\n' >&2
exit 1
}
output="${output_parent}/$(basename -- "${output}")"
project_root="$(git -C "${TOOL_ROOT}" rev-parse --show-toplevel)"
project_root="$(CDPATH= cd -- "${project_root}" && pwd -P)"
case "${output}" in
"${project_root}"|"${project_root}"/*)
printf 'Die Secret-Datei darf nicht im Repository liegen.\n' >&2
exit 1
;;
esac
[[ "${asset_root}" == /* ]] || {
printf 'Asset-Root muss absolut sein.\n' >&2
exit 1
}
[[ ! -e "${output}" ]] || {
printf 'Ausgabedatei existiert bereits; sie wird nicht überschrieben.\n' >&2
exit 1
}
if command -v openssl >/dev/null 2>&1; then
token="$(openssl rand -base64 48 | tr -d '\n')"
else
printf 'openssl wird zur sicheren Token-Erzeugung benötigt.\n' >&2
exit 1
fi
printf 'PROJECTCAPTURE_BLOCKBENCH_TOKEN=%q\nPROJECTCAPTURE_ASSET_ROOT=%q\n' \
"${token}" "${asset_root}" > "${output}"
chmod 0600 "${output}"
printf 'Lokale Environment-Datei erstellt (Token wird nicht ausgegeben): %s\n' "${output}"
+41
View File
@@ -0,0 +1,41 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
# shellcheck source=lib.sh
source "${SCRIPT_DIR}/lib.sh"
if [[ "$#" -ne 2 || "$1" != "--i-reviewed-gpl" ]]; then
printf 'Verwendung: %s --i-reviewed-gpl EXPLIZITES_PLUGIN_ZIELVERZEICHNIS\n' "$0" >&2
exit 2
fi
destination="$2"
[[ -d "${destination}" && ! -L "${destination}" ]] || {
printf 'Ziel muss ein vorhandenes echtes Verzeichnis sein: %s\n' "${destination}" >&2
exit 1
}
mcp_source="${TOOL_ROOT}/dist/mcp-projectcapture-1.6.1.js"
hytale_source="${TOOL_ROOT}/dist/hytale_plugin-0.9.1.js"
[[ -f "${mcp_source}" && ! -L "${mcp_source}" ]] || {
printf 'MCP-Build fehlt; zuerst scripts/build.sh ausführen.\n' >&2
exit 1
}
[[ -f "${hytale_source}" && ! -L "${hytale_source}" ]] || {
printf 'Hytale-Plugin fehlt; zuerst scripts/stage-hytale-plugin.sh ausführen.\n' >&2
exit 1
}
# Bestehende Dateien werden absichtlich nicht überschrieben. Review/Backup ist
# Sache des aufrufenden Installationsschritts.
for target in "${destination}/mcp.js" "${destination}/hytale_plugin.js"; do
[[ ! -e "${target}" ]] || {
printf 'Zieldatei existiert bereits; Installation abgebrochen: %s\n' "${target}" >&2
exit 1
}
done
cp -- "${mcp_source}" "${destination}/mcp.js"
cp -- "${hytale_source}" "${destination}/hytale_plugin.js"
printf 'Geprüfte Plugins installiert. Codex-Konfiguration wurde nicht verändert.\n'
+96
View File
@@ -0,0 +1,96 @@
#!/usr/bin/env bash
set -euo pipefail
TOOL_ROOT="$(CDPATH= cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd -P)"
MCP_REPOSITORY="https://github.com/jasonjgardner/blockbench-mcp-plugin.git"
MCP_COMMIT="6b069e308fdfc9b0a1c15bc924ca78150815f143"
MCP_PATCH="${TOOL_ROOT}/patches/0001-projectcapture-hardening.patch"
MCP_PATCH_SHA256="9de0b3767e96db7a68ef4c7082d7c76bf140b4932fc9550ad6e99d9a24a048c3"
BUN_VERSION="1.3.14"
require_command() {
command -v "$1" >/dev/null 2>&1 || {
printf 'Fehlendes Werkzeug: %s\n' "$1" >&2
exit 1
}
}
sha256_file() {
if command -v shasum >/dev/null 2>&1; then
shasum -a 256 "$1" | awk '{print $1}'
elif command -v sha256sum >/dev/null 2>&1; then
sha256sum "$1" | awk '{print $1}'
else
printf 'Weder shasum noch sha256sum ist verfügbar.\n' >&2
exit 1
fi
}
verify_patch() {
local actual
actual="$(sha256_file "${MCP_PATCH}")"
if [[ "${actual}" != "${MCP_PATCH_SHA256}" ]]; then
printf 'Patch-Hash stimmt nicht: erwartet %s, erhalten %s\n' \
"${MCP_PATCH_SHA256}" "${actual}" >&2
exit 1
fi
}
prepare_source() {
local destination="$1"
if [[ -z "${destination}" || -e "${destination}" ]]; then
printf 'Temporäres Ziel muss neu und nicht vorhanden sein: %s\n' "${destination}" >&2
exit 1
fi
verify_patch
git clone --quiet --filter=blob:none --no-checkout "${MCP_REPOSITORY}" "${destination}"
git -C "${destination}" checkout --quiet --detach "${MCP_COMMIT}"
local resolved
resolved="$(git -C "${destination}" rev-parse HEAD)"
if [[ "${resolved}" != "${MCP_COMMIT}" ]]; then
printf 'Upstream-Commit stimmt nicht: %s\n' "${resolved}" >&2
exit 1
fi
git -C "${destination}" apply --check "${MCP_PATCH}"
git -C "${destination}" apply "${MCP_PATCH}"
git -C "${destination}" diff --check
}
make_stage_parent() {
mktemp -d "${TMPDIR:-/tmp}/projectcapture-blockbench-mcp.XXXXXX"
}
safe_remove_stage() {
local stage="$1"
local leaf
leaf="$(basename -- "${stage}")"
if [[ -d "${stage}" && ! -L "${stage}" && "${leaf}" == projectcapture-blockbench-mcp.* ]]; then
rm -rf -- "${stage}"
else
printf 'Unsicheres temporäres Löschziel wurde nicht entfernt: %s\n' "${stage}" >&2
fi
}
run_bun() {
pnpm dlx "bun@${BUN_VERSION}" "$@"
}
write_dist_checksums() {
local output_dir="$1"
local checksum_file="${output_dir}/SHA256SUMS"
local temporary_file="${checksum_file}.tmp.$$"
local artifact
: > "${temporary_file}"
for artifact in \
hytale_plugin-0.9.1.js \
mcp-about.md \
mcp-icon.svg \
mcp-projectcapture-1.6.1.js; do
if [[ -f "${output_dir}/${artifact}" && ! -L "${output_dir}/${artifact}" ]]; then
printf '%s %s\n' "$(sha256_file "${output_dir}/${artifact}")" "${artifact}" \
>> "${temporary_file}"
fi
done
mv -- "${temporary_file}" "${checksum_file}"
}
+33
View File
@@ -0,0 +1,33 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
# shellcheck source=lib.sh
source "${SCRIPT_DIR}/lib.sh"
require_command git
repository="https://github.com/JannisX11/hytale-blockbench-plugin.git"
commit="82dc58d19949c802f60f31c24fcb063a6b12fb07"
expected_sha="ce1b0a6a3ad2f36b073b96a71a416261e43f089f4c02799b180b71c9e3f849f3"
stage_parent="$(make_stage_parent)"
trap 'safe_remove_stage "${stage_parent}"' EXIT
source_dir="${stage_parent}/hytale-plugin"
git clone --quiet --filter=blob:none --no-checkout "${repository}" "${source_dir}"
git -C "${source_dir}" checkout --quiet --detach "${commit}"
[[ "$(git -C "${source_dir}" rev-parse HEAD)" == "${commit}" ]] || exit 1
artifact="${source_dir}/dist/hytale_plugin.js"
actual_sha="$(sha256_file "${artifact}")"
if [[ "${actual_sha}" != "${expected_sha}" ]]; then
printf 'Hytale-Plugin-Hash stimmt nicht.\n' >&2
exit 1
fi
output_dir="${TOOL_ROOT}/dist"
mkdir -p -- "${output_dir}"
cp -- "${artifact}" "${output_dir}/hytale_plugin-0.9.1.js"
write_dist_checksums "${output_dir}"
printf 'Offizielles Hytale-Plugin geprüft und bereitgestellt: %s\n' \
"${output_dir}/hytale_plugin-0.9.1.js"
+33
View File
@@ -0,0 +1,33 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
# shellcheck source=lib.sh
source "${SCRIPT_DIR}/lib.sh"
require_command git
require_command pnpm
stage_parent="$(make_stage_parent)"
trap 'safe_remove_stage "${stage_parent}"' EXIT
source_dir="${stage_parent}/source"
prepare_source "${source_dir}"
run_bun install --cwd "${source_dir}" --frozen-lockfile
run_bun test --cwd "${source_dir}"
run_bun run --cwd "${source_dir}" build
bundle="${source_dir}/dist/mcp.js"
[[ -f "${bundle}" ]] || {
printf 'Build-Artefakt fehlt: %s\n' "${bundle}" >&2
exit 1
}
for forbidden_network_marker in 'https://cdn.jsdelivr.net' 'fetch('; do
if grep -Fq "${forbidden_network_marker}" "${bundle}"; then
printf 'Unerwarteter Netzwerkmarker im Build: %s\n' "${forbidden_network_marker}" >&2
exit 1
fi
done
printf 'Blockbench-MCP: Tests und Build erfolgreich.\n'