diff --git a/mc_aigod_paper.json b/mc_aigod_paper.json index bb9b770..656f053 100644 --- a/mc_aigod_paper.json +++ b/mc_aigod_paper.json @@ -1,5 +1,6 @@ { "server_name": "paper-ai-25567", + "server_type": "paper", "log_path": "/opt/paper-ai-25567/logs/latest.log", "rcon_host": "127.0.0.1", "rcon_port": 25577, diff --git a/mc_aigod_paper.py b/mc_aigod_paper.py index 75fd4f3..5d3bde4 100644 --- a/mc_aigod_paper.py +++ b/mc_aigod_paper.py @@ -767,6 +767,9 @@ def process_sudo_template_command(player: str, prompt: str, config) -> bool: return True if p.startswith("template build"): + if not get_server_capabilities(config).get("template_build", False): + _send_private(player, "[SUDO-TEMPLATE] template build requires a Paper server with WorldEdit/FAWE.", config, "yellow") + return True rest = raw[len("template build"):].strip() src = _resolve_template_file(rest, player, config) if not src: @@ -1214,6 +1217,38 @@ def get_player_context(player: str, config) -> str: # LLM # --------------------------------------------------------------------------- +# --------------------------------------------------------------------------- +# Server type capability sets +# --------------------------------------------------------------------------- + +SERVER_CAPABILITIES = { + "vanilla": { + "safe_prefixes": [ + 'give ', 'effect ', 'xp ', 'tp ', 'time ', 'weather ', + 'execute ', 'kill ', 'summon ', 'tellraw ', 'worldborder ', + ], + "sudo_whitelist_note": "give, effect, xp, tp, time, weather, execute, kill, summon, tellraw, worldborder", + "template_build": False, + }, + "paper": { + "safe_prefixes": [ + 'give ', 'effect ', 'xp ', 'tp ', 'time ', 'weather ', + 'execute ', 'kill ', 'summon ', 'tellraw ', 'worldborder ', + 'fill ', 'setblock ', 'clone ', + ], + "sudo_whitelist_note": "give, effect, xp, tp, time, weather, execute, kill, summon, tellraw, worldborder, fill, setblock, clone", + "template_build": True, + }, +} + +DEFAULT_SERVER_TYPE = "paper" + + +def get_server_capabilities(config) -> dict: + server_type = str(config.get("server_type", DEFAULT_SERVER_TYPE)).lower().strip() if config else DEFAULT_SERVER_TYPE + return SERVER_CAPABILITIES.get(server_type, SERVER_CAPABILITIES[DEFAULT_SERVER_TYPE]) + + COMMAND_PALETTE = """ GIVE (any item, based on player need — see Item Naming Rules below): SYNTAX: give minecraft: @@ -1445,13 +1480,16 @@ COMMANDS_SYSTEM_PROMPT = ( + ENCHANTMENT_CONTEXT ) -SUDO_COMMANDS_SYSTEM_PROMPT = ( +def build_sudo_commands_system_prompt(config=None) -> str: + caps = get_server_capabilities(config) if config else SERVER_CAPABILITIES[DEFAULT_SERVER_TYPE] + whitelist = caps["sudo_whitelist_note"] + return ( "You are a Minecraft command translator. Convert a player's natural-language request into " "Minecraft server commands. You do NOT roleplay.\n\n" "Respond ONLY with valid JSON:\n" "{\"commands\": [\"cmd1\", \"cmd2\"]}\n\n" "Rules:\n" - "- Use commands from this whitelist only: give, effect, xp, tp, time, weather, execute, kill, summon, tellraw, worldborder, fill, setblock, clone.\n" + f"- Use commands from this whitelist only: {whitelist}.\n" "- If the request cannot be mapped safely, return commands: [].\n" "- If player says 'me' or 'my', target the requesting player.\n" "- You will receive LAST 10 SUDO ACTIONS. Use them for continuity and corrections when the player says previous output was wrong.\n" @@ -1533,6 +1571,9 @@ FIRST_LOGIN_BENEVOLENCE_PROMPT = ( "- Use only whitelisted command families: give, effect, xp, tp, time, weather, execute, kill, summon, tellraw, worldborder.\n" ) +# Keep backward-compatible alias using default (paper) config +SUDO_COMMANDS_SYSTEM_PROMPT = build_sudo_commands_system_prompt() + def build_message_system_prompt(config) -> str: base = ( "You are God in a Minecraft server. You are benevolent but just. " @@ -2000,8 +2041,10 @@ def validate_command(cmd, online_players, fallback_player, config=None): resolved = resolved[1:] resolved = fix_give_command(resolved) resolved = fix_effect_command(resolved) - if not any(resolved.startswith(p) for p in SAFE_PREFIXES): - log.warning(f"Command blocked (unknown prefix): {resolved}") + caps = get_server_capabilities(config) if config else SERVER_CAPABILITIES[DEFAULT_SERVER_TYPE] + prefixes = caps["safe_prefixes"] + if not any(resolved.startswith(p) for p in prefixes): + log.warning(f"Command blocked (unknown prefix for server_type={config.get('server_type', DEFAULT_SERVER_TYPE) if config else DEFAULT_SERVER_TYPE}): {resolved}") return resolved, False if config and _extract_tp_abs_xyz(resolved) and not _tp_inside_worldborder(resolved, config): log.warning(f"Command blocked (tp outside worldborder): {resolved}") @@ -2336,7 +2379,7 @@ def process_sudo(player, prompt, config): def _local_sudo_translate() -> list: content = _llm_call( model=command_model, - system=SUDO_COMMANDS_SYSTEM_PROMPT, + system=build_sudo_commands_system_prompt(config), user=context_hint, config=config, fmt="json",