145 lines
4.4 KiB
Bash
145 lines
4.4 KiB
Bash
|
|
#!/usr/bin/env bash
|
||
|
|
# scripts/test-default-perms.sh
|
||
|
|
#
|
||
|
|
# Snapshot the things that influence what a default-rank player on
|
||
|
|
# racked.ru can do. Designed for diffing across config changes.
|
||
|
|
#
|
||
|
|
# Output: a deterministic dump (sorted where possible) of:
|
||
|
|
# - LuckPerms `default` group nodes
|
||
|
|
# - ProAntiTab `default` group whitelist
|
||
|
|
# - PAT global command whitelist
|
||
|
|
# - Plugin enable order from the most recent boot
|
||
|
|
# - Aliases defined in commands.yml
|
||
|
|
#
|
||
|
|
# Runs from the host (laptop or nullstone). Reads files out of the
|
||
|
|
# minecraft-mc container; parses YAML / JSON on the host (PyYAML and
|
||
|
|
# python3 are not present in the Paper container image).
|
||
|
|
#
|
||
|
|
# Usage (from any host with SSH access to nullstone):
|
||
|
|
# bash scripts/test-default-perms.sh > snapshot.txt
|
||
|
|
# bash scripts/test-default-perms.sh --remote > snapshot.txt # explicit ssh
|
||
|
|
#
|
||
|
|
# Then `diff` two snapshots taken before/after a change.
|
||
|
|
#
|
||
|
|
# Pairs with docs/PLAYER-SMOKE-TEST.md.
|
||
|
|
|
||
|
|
set -euo pipefail
|
||
|
|
|
||
|
|
CONTAINER="${MC_CONTAINER:-minecraft-mc}"
|
||
|
|
DATA="${MC_DATA:-/data}"
|
||
|
|
SSH_TARGET="${MC_SSH:-user@192.168.0.100}"
|
||
|
|
|
||
|
|
# If we're running on the box itself (docker is local), skip SSH.
|
||
|
|
mode="local"
|
||
|
|
if ! command -v docker >/dev/null 2>&1 || ! docker ps --format '{{.Names}}' 2>/dev/null | grep -qx "$CONTAINER"; then
|
||
|
|
mode="ssh"
|
||
|
|
fi
|
||
|
|
if [[ "${1:-}" == "--remote" ]]; then
|
||
|
|
mode="ssh"
|
||
|
|
fi
|
||
|
|
|
||
|
|
# read_file <path-inside-container> -> stdout = file contents
|
||
|
|
read_file() {
|
||
|
|
local path="$1"
|
||
|
|
if [[ "$mode" == "ssh" ]]; then
|
||
|
|
ssh -o BatchMode=yes "$SSH_TARGET" "docker exec $CONTAINER cat $path"
|
||
|
|
else
|
||
|
|
docker exec "$CONTAINER" cat "$path"
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
# rcon <command> -> stdout = response (LP exports go to file, not stdout)
|
||
|
|
rcon() {
|
||
|
|
local cmd="$1"
|
||
|
|
if [[ "$mode" == "ssh" ]]; then
|
||
|
|
ssh -o BatchMode=yes "$SSH_TARGET" "echo '$cmd' | docker exec -i $CONTAINER rcon-cli"
|
||
|
|
else
|
||
|
|
echo "$cmd" | docker exec -i "$CONTAINER" rcon-cli
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
# docker_logs -> stdout = full container log
|
||
|
|
docker_logs() {
|
||
|
|
if [[ "$mode" == "ssh" ]]; then
|
||
|
|
ssh -o BatchMode=yes "$SSH_TARGET" "docker logs $CONTAINER 2>&1"
|
||
|
|
else
|
||
|
|
docker logs "$CONTAINER" 2>&1
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
# read_lp_export -> stdout = JSON (decompressed) of latest snapshot
|
||
|
|
read_lp_export() {
|
||
|
|
rcon 'lp export default-perms-snapshot' >/dev/null
|
||
|
|
sleep 1
|
||
|
|
local path="${DATA}/plugins/LuckPerms/default-perms-snapshot.json.gz"
|
||
|
|
if [[ "$mode" == "ssh" ]]; then
|
||
|
|
ssh -o BatchMode=yes "$SSH_TARGET" "docker exec $CONTAINER bash -c 'gunzip -c $path'"
|
||
|
|
else
|
||
|
|
docker exec "$CONTAINER" bash -c "gunzip -c $path"
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
heading() {
|
||
|
|
printf '\n========== %s ==========\n' "$1"
|
||
|
|
}
|
||
|
|
|
||
|
|
heading "Mode"
|
||
|
|
echo "$mode (target: ${SSH_TARGET}, container: ${CONTAINER})"
|
||
|
|
|
||
|
|
# ---------- LuckPerms default-group export ----------
|
||
|
|
heading "LuckPerms default-group nodes"
|
||
|
|
read_lp_export | python3 -c '
|
||
|
|
import json, sys
|
||
|
|
try:
|
||
|
|
data = json.load(sys.stdin)
|
||
|
|
except Exception as e:
|
||
|
|
print(f"(failed to parse LP export: {e})")
|
||
|
|
sys.exit(0)
|
||
|
|
default = data.get("groups", {}).get("default", {}).get("nodes", [])
|
||
|
|
for node in sorted(default, key=lambda n: (n.get("type",""), n.get("key",""))):
|
||
|
|
t = node.get("type","?")
|
||
|
|
k = node.get("key","?")
|
||
|
|
v = node.get("value","?")
|
||
|
|
print(f"{t:12s} {k} = {v}")
|
||
|
|
'
|
||
|
|
|
||
|
|
# ---------- ProAntiTab whitelist ----------
|
||
|
|
heading "ProAntiTab — global commands"
|
||
|
|
read_file "${DATA}/plugins/ProAntiTab/storage.yml" | python3 -c '
|
||
|
|
import sys, yaml
|
||
|
|
data = yaml.safe_load(sys.stdin) or {}
|
||
|
|
for cmd in sorted(data.get("global", {}).get("commands", []) or []):
|
||
|
|
print(cmd)
|
||
|
|
'
|
||
|
|
|
||
|
|
heading "ProAntiTab — default-group commands"
|
||
|
|
read_file "${DATA}/plugins/ProAntiTab/storage.yml" | python3 -c '
|
||
|
|
import sys, yaml
|
||
|
|
data = yaml.safe_load(sys.stdin) or {}
|
||
|
|
default = (data.get("groups", {}) or {}).get("default", {}) or {}
|
||
|
|
priority = default.get("priority", "?")
|
||
|
|
print("# priority: " + str(priority))
|
||
|
|
for cmd in sorted(default.get("commands", []) or []):
|
||
|
|
print(cmd)
|
||
|
|
'
|
||
|
|
|
||
|
|
# ---------- commands.yml aliases ----------
|
||
|
|
heading "commands.yml aliases"
|
||
|
|
read_file "${DATA}/commands.yml" | python3 -c '
|
||
|
|
import sys, yaml
|
||
|
|
data = yaml.safe_load(sys.stdin) or {}
|
||
|
|
aliases = data.get("aliases", {}) or {}
|
||
|
|
for name in sorted(aliases.keys()):
|
||
|
|
target = aliases[name]
|
||
|
|
print(f"{name}: {target}")
|
||
|
|
'
|
||
|
|
|
||
|
|
# ---------- Plugin enable order (latest boot) ----------
|
||
|
|
heading "Plugin enable order — latest boot"
|
||
|
|
docker_logs \
|
||
|
|
| grep -E "Enabling [A-Z]" \
|
||
|
|
| tail -25 \
|
||
|
|
| sed -E 's/^\[[0-9:]+\] \[Server thread\/INFO\]: //'
|
||
|
|
|
||
|
|
heading "Snapshot complete: $(date -Is)"
|