[skipci] Merge pull request #3078 from BruceChenQAQ/bruce/shared-server-mcc-sessions

Add MCC wrappers and guards for build and publish
This commit is contained in:
BruceChen 2026-04-13 02:39:32 +08:00 committed by GitHub
commit 4013d39068
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 199 additions and 12 deletions

16
.codex/hooks.json Normal file
View file

@ -0,0 +1,16 @@
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "/usr/bin/python3 \"$(git rev-parse --show-toplevel)/.codex/hooks/pre_tool_use_mcc_build_guard.py\"",
"statusMessage": "Checking MCC build command policy"
}
]
}
]
}
}

View file

@ -0,0 +1,67 @@
#!/usr/bin/env python3
import json
import re
import sys
RAW_DOTNET_BUILD_RE = re.compile(r"(^|[\s;&|()])dotnet\s+build(\s|$)")
ABSOLUTE_DOTNET_BUILD_RE = re.compile(r"(^|[\s;&|()])/\S*dotnet\s+build(\s|$)")
RAW_DOTNET_PUBLISH_RE = re.compile(r"(^|[\s;&|()])dotnet\s+publish(\s|$)")
ABSOLUTE_DOTNET_PUBLISH_RE = re.compile(r"(^|[\s;&|()])/\S*dotnet\s+publish(\s|$)")
def main() -> int:
try:
payload = json.load(sys.stdin)
except json.JSONDecodeError:
return 0
command = payload.get("tool_input", {}).get("command", "")
if not isinstance(command, str) or not command:
return 0
if ABSOLUTE_DOTNET_BUILD_RE.search(command) or ABSOLUTE_DOTNET_PUBLISH_RE.search(command):
return 0
if RAW_DOTNET_BUILD_RE.search(command):
response = {
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": (
"Raw 'dotnet build' is blocked in this repository. "
"Use 'source tools/mcc-env.sh && mcc-build' instead so MCC temp-build routing stays active. "
"If you intentionally need the raw .NET CLI, call it by absolute path such as '/usr/bin/dotnet build ...' to bypass this guard."
),
},
"systemMessage": (
"Blocked raw 'dotnet build'. Use 'source tools/mcc-env.sh && mcc-build'. "
"If you intentionally need raw .NET CLI behavior, call '/usr/bin/dotnet build ...' explicitly."
),
}
json.dump(response, sys.stdout)
sys.stdout.write("\n")
elif RAW_DOTNET_PUBLISH_RE.search(command):
response = {
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": (
"Raw 'dotnet publish' is blocked in this repository. "
"Use 'source tools/mcc-env.sh && mcc-publish --rid <RID>' instead so MCC publish defaults stay aligned with the repo workflow. "
"If you intentionally need the raw .NET CLI, call it by absolute path such as '/usr/bin/dotnet publish ...' to bypass this guard."
),
},
"systemMessage": (
"Blocked raw 'dotnet publish'. Use 'source tools/mcc-env.sh && mcc-publish --rid <RID>'. "
"If you intentionally need raw .NET CLI behavior, call '/usr/bin/dotnet publish ...' explicitly."
),
}
json.dump(response, sys.stdout)
sys.stdout.write("\n")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View file

@ -93,9 +93,12 @@ mc-reset-test-env 1.21.11
## Build ## Build
```bash ```bash
dotnet build MinecraftClient.sln -c Release source tools/mcc-env.sh
mcc-build
``` ```
Use `mcc-build` for normal local development so any `MCC_BUILD_MODE=tmpfs` routing stays active. Only use raw `dotnet build` when you are intentionally debugging the build system itself.
## Server management ## Server management
Interactive shell: Interactive shell:
@ -276,6 +279,7 @@ After `source tools/mcc-env.sh`:
| `mc-wait-stop VER [SEC]` | Wait for server shutdown, with force-kill fallback | | `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 | | `mc-reset-test-env [--all|VER...]` | Reset shared tmux server state and stale pipes |
| `mcc-build` | Build MCC | | `mcc-build` | Build MCC |
| `mcc-publish --rid <RID>` | Publish MCC with the repo's CI-like defaults |
| `mcc-build-clean` | Clear the current worktree's build output | | `mcc-build-clean` | Clear the current worktree's build output |
| `mcc-run [--session NAME] [--username NAME] [--port PORT]` | Convenience wrapper for `mcc-debug --file-input --no-build` | | `mcc-run [--session NAME] [--username NAME] [--port PORT]` | Convenience wrapper for `mcc-debug --file-input --no-build` |
| `mcc-tui [--session NAME] [--username NAME] [--port PORT]` | Convenience wrapper for `mcc-debug -m tui --no-build` | | `mcc-tui [--session NAME] [--username NAME] [--port PORT]` | Convenience wrapper for `mcc-debug -m tui --no-build` |

View file

@ -8,16 +8,17 @@
## Build / Run ## Build / Run
- Init submodules first: `git submodule update --init --recursive` - Init submodules first: `git submodule update --init --recursive`
- Build: `dotnet build MinecraftClient.sln -c Release` - Build for local development: `source tools/mcc-env.sh && mcc-build`
- Publish (matches CI shape): `dotnet publish MinecraftClient.sln -f net10.0 -r <RID> --self-contained=true -c Release -p:UseAppHost=true -p:IncludeNativeLibrariesForSelfExtract=true -p:EnableCompressionInSingleFile=true -p:DebugType=Embedded` - Publish (matches CI shape): `source tools/mcc-env.sh && mcc-publish --rid <RID>`
- Run from source: `dotnet run --project MinecraftClient -- --help` - Run/debug from source: `source tools/mcc-env.sh && mcc-debug -v 1.21.11 --file-input`
- Docs: `cd docs && npm install && npm run docs:dev` or `npm run docs:build` - Docs: `cd docs && npm install && npm run docs:dev` or `npm run docs:build`
- Docker: `cd Docker && docker build -t minecraft-console-client:latest .` - Docker: `cd Docker && docker build -t minecraft-console-client:latest .`
- Tests: no dedicated test project is present in the main solution. - Tests: no dedicated test project is present in the main solution.
- Current state: the solution builds after submodule init, but `dotnet build` emits many analyzer and NuGet vulnerability warnings; treat them as real. - Current state: the solution builds after submodule init, but the underlying .NET build emits many analyzer and NuGet vulnerability warnings; treat them as real.
- Server roots: `tools/` helpers look for server jars under `MinecraftOfficial/downloads/<version>/` by default, but also support an external root via the `MCC_SERVERS` environment variable. - Server roots: `tools/` helpers look for server jars under `MinecraftOfficial/downloads/<version>/` by default, but also support an external root via the `MCC_SERVERS` environment variable.
- Multi-version testing: tmux-based local server sessions are shared state. Run cross-version test matrices sequentially unless you have explicit per-version isolation. A server logging `Done` does not guarantee immediate RCON availability; retry RCON setup commands. - Multi-version testing: tmux-based local server sessions are shared state. Run cross-version test matrices sequentially unless you have explicit per-version isolation. A server logging `Done` does not guarantee immediate RCON availability; retry RCON setup commands.
- Automated test configs: for repeated or matrix test runs, prefer generating a temporary MCC config per run instead of reusing the repo-root `MinecraftClient.ini`, to avoid leaking state between runs. - Automated test configs: for repeated or matrix test runs, prefer generating a temporary MCC config per run instead of reusing the repo-root `MinecraftClient.ini`, to avoid leaking state between runs.
- For agent-driven local development, prefer `mcc-build`, `mcc-publish`, `mcc-build-clean`, `mcc-debug`, `mcc-run`, and `mcc-tui` over raw `dotnet build`, `dotnet publish`, or `dotnet run`, so worktree-local temp build routing stays active.
## Architecture ## Architecture
- `Program` bootstraps console I/O, TOML config, auth/session state, MC version selection, Forge detection, then creates `McClient`. - `Program` bootstraps console I/O, TOML config, auth/session state, MC version selection, Forge detection, then creates `McClient`.

View file

@ -54,6 +54,8 @@ It is built around two layers:
- repo tools in `tools/`, which do the actual work - repo tools in `tools/`, which do the actual work
- AI skills in `.skills/`, which tell the agent when and how to use those tools - AI skills in `.skills/`, which tell the agent when and how to use those tools
For agent-driven local development, prefer the `mcc-*` wrappers after `source tools/mcc-env.sh`. They preserve session isolation, temp configs, and optional tmpfs build routing. Do not default to raw `dotnet build` or `dotnet run` for the normal MCC debug loop.
## Setup ## Setup
You only do most of this once. You only do most of this once.
@ -716,6 +718,7 @@ Typical flow:
1. Decide whether this should be a standalone `/script` bot or a built-in bot. 1. Decide whether this should be a standalone `/script` bot or a built-in bot.
2. Use the authoring skill's references and templates. 2. Use the authoring skill's references and templates.
3. Build MCC. 3. Build MCC.
Use `mcc-build` instead of raw `dotnet build` so worktree-local temp build output still applies.
4. Start a local server and join it. 4. Start a local server and join it.
5. Test the bot behavior through live commands, chat, or event-driven actions. 5. Test the bot behavior through live commands, chat, or event-driven actions.
6. Make sure cleanup paths such as `OnUnload()` are correct. 6. Make sure cleanup paths such as `OnUnload()` are correct.

View file

@ -131,6 +131,12 @@ If the build succeeds, the published binary `MinecraftClient.exe` will be in `Mi
#### Building using .NET manually without Visual Studio #### Building using .NET manually without Visual Studio
<div class="custom-container tip"><p class="custom-container-title">Tip</p>
If you are following the AI-assisted repo workflow, use WSL or another Unix-style shell and prefer `source tools/mcc-env.sh` followed by `mcc-build`. That path keeps MCC's session and temp-build helpers enabled. The `dotnet` commands below are the low-level manual fallback.
</div>
1. Open the `Minecraft-Console-Client` folder you've cloned or downloaded 1. Open the `Minecraft-Console-Client` folder you've cloned or downloaded
2. Open the PowerShell (`Right-Click` on the whitespace and click `Open PowerShell`, or in Windows Explorer: `File -> Open PowerShell`) 2. Open the PowerShell (`Right-Click` on the whitespace and click `Open PowerShell`, or in Windows Explorer: `File -> Open PowerShell`)
3. Install the .NET 10 SDK if you do not already have it. The easiest current option on Windows is: 3. Install the .NET 10 SDK if you do not already have it. The easiest current option on Windows is:
@ -148,7 +154,8 @@ dotnet build MinecraftClient.sln -c Release
5. If you want a release-like published binary that matches the repo's CI workflow, run: 5. If you want a release-like published binary that matches the repo's CI workflow, run:
```bash ```bash
dotnet publish MinecraftClient.sln -f net10.0 -r win-x64 --self-contained=true -c Release -p:UseAppHost=true -p:IncludeNativeLibrariesForSelfExtract=true -p:EnableCompressionInSingleFile=true -p:DebugType=Embedded source tools/mcc-env.sh
mcc-publish --rid win-x64
``` ```
6. Verify the SDK installation if needed: 6. Verify the SDK installation if needed:
@ -210,18 +217,26 @@ git clone https://github.com/MCCTeam/Minecraft-Console-Client.git --recursive
5. If you want to download translation resources, please check out [Download translation resources](#download-translation-resources-optional) 5. If you want to download translation resources, please check out [Download translation resources](#download-translation-resources-optional)
6. Run the following command for a normal local build: 6. For the repo's normal local development workflow, source the helper environment and build through `mcc-build`:
```bash
source tools/mcc-env.sh
mcc-build
```
7. If you specifically want the low-level manual .NET command instead of the MCC wrapper, run:
```bash ```bash
dotnet build MinecraftClient.sln -c Release dotnet build MinecraftClient.sln -c Release
``` ```
7. Run the following command if you want a release-like published binary that matches the repo's CI workflow: 8. Run the following command if you want a release-like published binary that matches the repo's CI workflow:
- On Linux: - On Linux:
```bash ```bash
dotnet publish MinecraftClient.sln -f net10.0 -r linux-x64 --self-contained=true -c Release -p:UseAppHost=true -p:IncludeNativeLibrariesForSelfExtract=true -p:EnableCompressionInSingleFile=true -p:DebugType=Embedded source tools/mcc-env.sh
mcc-publish --rid linux-x64
``` ```
<div class="custom-container note"><p class="custom-container-title">Note</p> <div class="custom-container note"><p class="custom-container-title">Note</p>
@ -233,7 +248,8 @@ git clone https://github.com/MCCTeam/Minecraft-Console-Client.git --recursive
- On macOS: - On macOS:
```bash ```bash
dotnet publish MinecraftClient.sln -f net10.0 -r osx-x64 --self-contained=true -c Release -p:UseAppHost=true -p:IncludeNativeLibrariesForSelfExtract=true -p:EnableCompressionInSingleFile=true -p:DebugType=Embedded source tools/mcc-env.sh
mcc-publish --rid osx-x64
``` ```
<div class="custom-container note"><p class="custom-container-title">Note</p> <div class="custom-container note"><p class="custom-container-title">Note</p>

View file

@ -13,6 +13,7 @@ source tools/mcc-env.sh
mc-start 1.21.11 mc-start 1.21.11
mcc-debug -v 1.21.11 --file-input mcc-debug -v 1.21.11 --file-input
mcc-cmd "debug state" mcc-cmd "debug state"
mcc-publish --rid linux-x64
``` ```
### Shared server, isolated MCC sessions ### Shared server, isolated MCC sessions

View file

@ -126,11 +126,44 @@ _mcc_dotnet_env() {
local build_root local build_root
build_root="$(_mcc_build_root)" build_root="$(_mcc_build_root)"
mkdir -p "$build_root" mkdir -p "$build_root"
env MCC_BUILD_ROOT="$build_root" "$@" env MCC_BUILD_ROOT="$build_root" MCC_ALLOW_RAW_DOTNET=1 "$@"
return $? return $?
fi fi
"$@" MCC_ALLOW_RAW_DOTNET=1 "$@"
}
_mcc_is_repo_dotnet_build_blocked() {
local repo_root cwd
repo_root="$(_mcc_repo_root)"
cwd="${PWD:-}"
[[ -n "$repo_root" && -n "$cwd" && "$cwd" == "$repo_root"* ]]
}
dotnet() {
if [[ "${MCC_ALLOW_RAW_DOTNET:-0}" != "1" ]] && _mcc_is_repo_dotnet_build_blocked; then
case "${1:-}" in
build)
cat >&2 <<'EOF'
[MCC] Raw 'dotnet build' is blocked in this repository.
[MCC] Use: source tools/mcc-env.sh && mcc-build
[MCC] If you intentionally need the raw .NET CLI, call it by absolute path to bypass this guard.
EOF
return 64
;;
publish)
cat >&2 <<'EOF'
[MCC] Raw 'dotnet publish' is blocked in this repository.
[MCC] Use: source tools/mcc-env.sh && mcc-publish --rid <RID>
[MCC] If you intentionally need the raw .NET CLI, call it by absolute path to bypass this guard.
EOF
return 64
;;
esac
fi
command dotnet "$@"
} }
# Helper: convert version to tmux session name (dots -> underscores) # Helper: convert version to tmux session name (dots -> underscores)
@ -248,6 +281,52 @@ mcc-build() {
repo_root="$(_mcc_repo_root)" repo_root="$(_mcc_repo_root)"
_mcc_dotnet_env dotnet build "$repo_root/MinecraftClient.sln" -c Release _mcc_dotnet_env dotnet build "$repo_root/MinecraftClient.sln" -c Release
} }
mcc-publish() {
local repo_root rid=""
local -a extra_args=()
repo_root="$(_mcc_repo_root)"
while [[ $# -gt 0 ]]; do
case "$1" in
--rid|-r)
shift
if [[ $# -eq 0 ]]; then
echo "mcc-publish: --rid requires a value" >&2
return 1
fi
rid="$1"
shift
;;
--)
shift
extra_args+=("$@")
break
;;
*)
extra_args+=("$1")
shift
;;
esac
done
if [[ -z "$rid" ]]; then
echo "mcc-publish: missing required --rid <RID>" >&2
echo "mcc-publish: example: mcc-publish --rid linux-x64" >&2
return 1
fi
_mcc_dotnet_env dotnet publish "$repo_root/MinecraftClient.sln" \
-f net10.0 \
-r "$rid" \
--self-contained=true \
-c Release \
-p:UseAppHost=true \
-p:IncludeNativeLibrariesForSelfExtract=true \
-p:EnableCompressionInSingleFile=true \
-p:DebugType=Embedded \
"${extra_args[@]}"
}
mcc-build-clean() { mcc-build-clean() {
if [[ "${MCC_BUILD_MODE:-local}" == "tmpfs" ]]; then if [[ "${MCC_BUILD_MODE:-local}" == "tmpfs" ]]; then
local build_root local build_root