#!/usr/bin/env python3 """ Generate RCON-validated training data for server plugins. Plugins: WorldGuard, CoreProtect, EssentialsX, Vault, LuckPerms, FAWE Each command is executed against the live server to capture real responses. Target: 120+ high-quality examples. """ import json import random import re import sys import time from pathlib import Path PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent sys.path.insert(0, str(PROJECT_ROOT)) from agent.tools.persistent_rcon import get_rcon from agent.tools.tool_schemas import qwen3_tools_block from agent.prompts.system_prompts import SYNTAX_RULES, RISK_GRADIENT OUTPUT_PATH = PROJECT_ROOT / "data" / "raw" / "plugin_training.jsonl" TOOLS_BLOCK = qwen3_tools_block() SYSTEM = ( "You are a Minecraft 1.21 command translator for a Paper server with plugins: " "FastAsyncWorldEdit, WorldGuard, CoreProtect, EssentialsX, Vault, LuckPerms.\n\n" "You know plugin command syntax. Use the correct plugin prefix when needed.\n" "PERMISSION LEVEL: 4 (generous).\n\n" "Return JSON: {\"risk_level\": <0-5>, \"commands\": [...], \"reasoning\": \"...\"}\n\n" + SYNTAX_RULES + RISK_GRADIENT + "\n" + TOOLS_BLOCK ) PLAYERS = ["slingshooter08", "Ace13245", "TheBigBoss", "xXDragonSlayerXx"] def sys_msg(): return {"role": "system", "content": SYSTEM} def user_msg(text): return {"role": "user", "content": text} def tool_call(name, args): return {"role": "assistant", "content": f"\n{json.dumps({'name': name, 'arguments': args})}\n"} def tool_result(data): return {"role": "tool", "content": json.dumps(data)} def final_response(resp): return {"role": "assistant", "content": json.dumps(resp)} def is_error(result): return any(e in result for e in ("<--[HERE]", "Unknown command", "Incorrect argument", "An internal error")) def run_cmd(rcon, cmd): """Execute and return (result_str, is_ok).""" try: result = rcon.command(cmd) clean = re.sub(r'ยง.', '', result) ok = not is_error(result) return clean[:400], ok except Exception as e: return str(e)[:200], False def make_example(eid, etype, msgs): return {"id": eid, "source": "plugin_training", "type": etype, "messages": msgs} def build_rcon_example(rcon, eid, etype, prompt, commands, reasoning, player=None, mode="sudo"): """Build a full multi-turn tool-calling example with real RCON responses.""" player = player or random.choice(PLAYERS) msgs = [sys_msg(), user_msg(f"Player {player}: {prompt}")] resolved_cmds = [] all_ok = True for cmd in commands: cmd = cmd.replace("{p}", player) result, ok = run_cmd(rcon, cmd) if not ok: all_ok = False msgs.append(tool_call("rcon.execute", {"command": cmd})) msgs.append(tool_result({"success": ok, "result": result})) resolved_cmds.append(cmd) time.sleep(0.05) resp = {"risk_level": 3, "commands": resolved_cmds, "reasoning": reasoning} msgs.append(final_response(resp)) return { "id": eid, "source": "plugin_training", "type": etype, "messages": msgs, "all_success": all_ok, } def gen_worldguard(rcon): """WorldGuard region management examples.""" print(" WorldGuard...") examples = [] WG = [ # Region creation and management ("sudo protect this area as my-base", [ "//pos1 0,64,0", "//pos2 50,128,50", "rg define my-base {p}", ], "Selected area and created 'my-base' region owned by the player."), ("sudo protect a 20 block radius around spawn", [ "rg define spawn-protection -w world", "rg flag spawn-protection pvp deny", "rg flag spawn-protection mob-spawning deny", ], "Created spawn protection region with PvP and mob spawning denied."), ("sudo make my region pvp-free", [ "rg flag my-base pvp deny", ], "Disabled PvP in the player's region."), ("sudo allow TNT in the arena", [ "rg flag arena tnt allow", ], "Enabled TNT in the arena region."), ("sudo prevent mobs from spawning in town", [ "rg flag town mob-spawning deny", ], "Disabled mob spawning in the town region."), ("sudo add Ace13245 as a member of my base", [ "rg addmember my-base Ace13245", ], "Added Ace13245 as a member of my-base region."), ("sudo remove TheBigBoss from my region", [ "rg removemember my-base TheBigBoss", ], "Removed TheBigBoss from my-base region."), ("sudo show info about the spawn region", [ "rg info spawn-protection", ], "Displayed info about the spawn-protection region."), ("sudo list all regions", [ "rg list", ], "Listed all defined regions."), ("sudo prevent building in spawn", [ "rg flag spawn-protection build deny", ], "Denied building in spawn protection zone."), ("sudo make the arena a free-for-all", [ "rg flag arena pvp allow", "rg flag arena invincibility deny", "rg flag arena blocked-cmds /home,/spawn,/tpa", ], "Made arena PvP-enabled, disabled invincibility, blocked escape commands."), ("sudo set entry message for my base", [ "rg flag my-base greeting Welcome to my base!", ], "Set greeting message when entering the region."), ("sudo deny entry to non-members in my vault", [ "rg flag vault entry -g nonmembers deny", ], "Denied entry for non-members to the vault region."), ("sudo prevent creeper explosions everywhere", [ "rg flag __global__ creeper-explosion deny", ], "Disabled creeper explosions globally."), ("sudo make a healing zone at spawn", [ "rg flag spawn-protection heal-amount 2", "rg flag spawn-protection heal-delay 3", ], "Players in spawn zone heal 1 heart every 3 seconds."), ("sudo delete the old-arena region", [ "rg remove old-arena", ], "Removed the old-arena region."), ("sudo set region priority for spawn over global", [ "rg setpriority spawn-protection 10", ], "Set spawn-protection priority to 10 (higher overrides lower)."), ("sudo prevent fire spread in the village", [ "rg flag village fire-spread deny", "rg flag village lava-fire deny", ], "Disabled fire spread and lava fire in the village region."), ] for i, (prompt, cmds, reasoning) in enumerate(WG): ex = build_rcon_example(rcon, f"plugin-wg-{i:03d}", "worldguard", prompt, cmds, reasoning) examples.append(ex) return examples def gen_coreprotect(rcon): """CoreProtect inspection and rollback examples.""" print(" CoreProtect...") examples = [] CP = [ ("sudo check who broke blocks near me", [ "co inspect", ], "Enabled CoreProtect inspector mode. Player can now click blocks to see history."), ("sudo rollback griefing from the last hour", [ "co rollback t:1h", ], "Rolled back all block changes from the last hour."), ("sudo rollback what TheBigBoss did in the last 30 minutes", [ "co rollback u:TheBigBoss t:30m", ], "Rolled back TheBigBoss's actions from the last 30 minutes."), ("sudo restore blocks that were rolled back", [ "co restore t:1h", ], "Restored previously rolled-back changes from the last hour."), ("sudo check what happened here in the last day", [ "co lookup t:24h r:10", ], "Looked up all changes within 10 blocks in the last 24 hours."), ("sudo rollback only TNT damage", [ "co rollback t:1h a:tnt", ], "Rolled back only TNT explosion damage from the last hour."), ("sudo rollback fire damage from the last 2 hours", [ "co rollback t:2h a:fire", ], "Rolled back fire damage from the last 2 hours."), ("sudo who placed blocks around 100 64 200?", [ "co lookup t:7d r:5 l:100,64,200", ], "Looked up 7 days of block placement history near those coordinates."), ("sudo undo what happened to the diamond blocks", [ "co rollback t:12h b:diamond_block", ], "Rolled back changes to diamond blocks in the last 12 hours."), ("sudo rollback Ace's actions but only block breaks", [ "co rollback u:Ace13245 t:1h a:block", ], "Rolled back only block break/place actions by Ace in the last hour."), ("sudo check the status of CoreProtect", [ "co status", ], "Displayed CoreProtect status including database size and version."), ("sudo stop inspecting", [ "co inspect", ], "Toggled off CoreProtect inspector mode."), ("sudo rollback all container theft in the last 6 hours", [ "co rollback t:6h a:container", ], "Rolled back container (chest/barrel/etc.) access in the last 6 hours."), ("sudo lookup what Ace13245 did today", [ "co lookup u:Ace13245 t:24h", ], "Looked up all of Ace13245's actions in the last 24 hours."), ] for i, (prompt, cmds, reasoning) in enumerate(CP): ex = build_rcon_example(rcon, f"plugin-cp-{i:03d}", "coreprotect", prompt, cmds, reasoning) examples.append(ex) return examples def gen_essentialsx(rcon): """EssentialsX home/warp/kit/economy examples.""" print(" EssentialsX...") examples = [] ESS = [ # Homes ("sudo set my home here", [ "sethome {p} home", ], "Set player's home location."), ("sudo set a home called mine", [ "sethome {p} mine", ], "Set named home 'mine' for the player."), ("sudo tp me to my home", [ "home {p}", ], "Teleported player to their default home."), ("sudo delete my mine home", [ "delhome {p} mine", ], "Deleted the 'mine' home."), # Warps ("sudo create a warp called arena", [ "setwarp arena", ], "Created warp point 'arena' at current location."), ("sudo tp me to the arena warp", [ "warp arena {p}", ], "Warped player to the arena."), ("sudo list all warps", [ "warps", ], "Listed all available warp points."), ("sudo delete the old warp", [ "delwarp old", ], "Deleted the 'old' warp point."), ("sudo create a warp at spawn", [ "setwarp spawn", ], "Created spawn warp at current location."), # Kits ("sudo give me the starter kit", [ "kit starter {p}", ], "Gave the starter kit to the player."), # Economy ("sudo give Ace 1000 coins", [ "eco give Ace13245 1000", ], "Added $1000 to Ace13245's balance."), ("sudo check my balance", [ "balance {p}", ], "Checked player's economy balance."), ("sudo set everyone's balance to 500", [ "eco set * 500", ], "Set all players' balance to $500."), ("sudo take 200 from TheBigBoss", [ "eco take TheBigBoss 200", ], "Removed $200 from TheBigBoss's balance."), # Teleport ("sudo tp Ace to me", [ "tp Ace13245 {p}", ], "Teleported Ace13245 to the requesting player."), ("sudo tp everyone to spawn", [ "tpall spawn", ], "Teleported all players to spawn."), # Player management ("sudo set my nickname to StoneKing", [ "nick {p} StoneKing", ], "Changed player's display name to StoneKing."), ("sudo heal me", [ "heal {p}", ], "Fully healed the player."), ("sudo feed me", [ "feed {p}", ], "Restored player's hunger bar."), ("sudo repair what I'm holding", [ "repair {p}", ], "Repaired the player's held item."), ("sudo check how long Ace has played", [ "seen Ace13245", ], "Checked when Ace13245 was last online and playtime."), ("sudo set spawn point here", [ "setspawn", ], "Set the server spawn point to current location."), ("sudo broadcast a message to everyone", [ "broadcast Welcome to the new world!", ], "Broadcast server-wide message."), ("sudo give me god mode", [ "god {p}", ], "Toggled god mode (invulnerability) for the player."), ("sudo fly mode on", [ "fly {p}", ], "Toggled flight for the player."), ("sudo set player speed to fast", [ "speed fly 5 {p}", ], "Set flying speed to maximum for the player."), ("sudo check who's online and their info", [ "list", "whois {p}", ], "Listed online players and checked requesting player's info."), ("sudo make it sunny", [ "sun", ], "Set weather to clear using Essentials shorthand."), ] for i, (prompt, cmds, reasoning) in enumerate(ESS): ex = build_rcon_example(rcon, f"plugin-ess-{i:03d}", "essentialsx", prompt, cmds, reasoning) examples.append(ex) return examples def gen_luckperms(rcon): """LuckPerms permission management examples.""" print(" LuckPerms...") examples = [] LP = [ ("sudo give Ace13245 permission to fly", [ "lp user Ace13245 permission set essentials.fly true", ], "Granted flight permission to Ace13245."), ("sudo create a VIP group", [ "lp creategroup vip", ], "Created the VIP permission group."), ("sudo add Ace to VIP group", [ "lp user Ace13245 parent add vip", ], "Added Ace13245 to the VIP group."), ("sudo give VIP group access to /fly and /heal", [ "lp group vip permission set essentials.fly true", "lp group vip permission set essentials.heal true", ], "Gave VIP group fly and heal permissions."), ("sudo remove TheBigBoss from VIP", [ "lp user TheBigBoss parent remove vip", ], "Removed TheBigBoss from VIP group."), ("sudo check what permissions Ace has", [ "lp user Ace13245 permission info", ], "Displayed Ace13245's permission info."), ("sudo give temporary VIP for 1 day to slingshooter08", [ "lp user slingshooter08 parent addtemp vip 1d", ], "Gave slingshooter08 temporary VIP status for 24 hours."), ("sudo set VIP prefix to gold [VIP]", [ "lp group vip meta setprefix 100 \"&6[VIP] \"", ], "Set gold-colored VIP chat prefix."), ("sudo create a builder group with WorldEdit access", [ "lp creategroup builder", "lp group builder permission set worldedit.* true", ], "Created builder group with full WorldEdit permissions."), ("sudo deny TNT placement for default group", [ "lp group default permission set minecraft.command.setblock false", ], "Denied setblock command for default group."), ("sudo list all groups", [ "lp listgroups", ], "Listed all permission groups."), ("sudo check VIP group permissions", [ "lp group vip permission info", ], "Displayed VIP group permission details."), ("sudo give me all permissions", [ "lp user {p} permission set * true", ], "Granted wildcard (all) permissions to the player."), ("sudo set default group to have basic essentials", [ "lp group default permission set essentials.home true", "lp group default permission set essentials.spawn true", "lp group default permission set essentials.tpa true", "lp group default permission set essentials.msg true", ], "Gave default group basic Essentials permissions: home, spawn, tpa, msg."), ] for i, (prompt, cmds, reasoning) in enumerate(LP): ex = build_rcon_example(rcon, f"plugin-lp-{i:03d}", "luckperms", prompt, cmds, reasoning) examples.append(ex) return examples def gen_fawe_advanced(rcon): """Advanced FAWE/WorldEdit examples beyond basic commands.""" print(" FAWE advanced...") examples = [] FAWE = [ ("sudo make a sphere of glass 10 blocks wide", [ "//sphere minecraft:glass 10", ], "Created a glass sphere with radius 10."), ("sudo hollow sphere of stone", [ "//hsphere minecraft:stone 8", ], "Created hollow stone sphere radius 8."), ("sudo cylinder of quartz 5 wide 10 tall", [ "//cyl minecraft:quartz_block 5 10", ], "Created quartz cylinder radius 5, height 10."), ("sudo replace all stone with deepslate in selection", [ "//replace minecraft:stone minecraft:deepslate", ], "Replaced stone with deepslate in the selected region."), ("sudo copy and paste this structure 20 blocks east", [ "//copy", "//paste", ], "Copied selection and pasted it."), ("sudo undo my last WorldEdit action", [ "//undo", ], "Undid the last WorldEdit operation."), ("sudo redo what I just undid", [ "//redo", ], "Redid the last undone WorldEdit operation."), ("sudo smooth the terrain in my selection", [ "//smooth 5", ], "Smoothed terrain in selection with 5 iterations."), ("sudo drain all water within 20 blocks", [ "//drain 20", ], "Drained water within 20 block radius."), ("sudo set a pyramid of sandstone 10 tall", [ "//pyramid minecraft:sandstone 10", ], "Created sandstone pyramid 10 blocks tall."), ("sudo hollow out the selected area", [ "//hollow", ], "Hollowed out the selected region leaving only the shell."), ("sudo make walls around my selection", [ "//walls minecraft:stone_bricks", ], "Built stone brick walls around the selection (no floor/ceiling)."), ("sudo fill the selection with a checkerboard pattern", [ "//set 50%minecraft:white_concrete,50%minecraft:black_concrete", ], "Filled with alternating black and white concrete."), ("sudo stack my selection 5 times going north", [ "//stack 5 north", ], "Stacked the selection 5 times northward."), ("sudo generate a forest in my selection", [ "//forest oak 10", ], "Generated oak trees with 10% density in the selection."), ("sudo remove all snow in a 50 block radius", [ "//removenear minecraft:snow 50", ], "Removed snow layers within 50 blocks."), ("sudo count blocks in my selection", [ "//count minecraft:diamond_ore", ], "Counted diamond ore blocks in the selection."), ("sudo make a natural-looking cave", [ "//cyl minecraft:air 4 8", ], "Carved cylindrical tunnel (air cylinder) radius 4, depth 8."), ] for i, (prompt, cmds, reasoning) in enumerate(FAWE): ex = build_rcon_example(rcon, f"plugin-fawe-{i:03d}", "fawe", prompt, cmds, reasoning) examples.append(ex) return examples def gen_combined_plugin_examples(rcon): """Examples that combine multiple plugins in one request.""" print(" Combined plugin examples...") examples = [] COMBINED = [ ("sudo create a protected arena with WorldEdit and WorldGuard", [ "//pos1 -50,60,-50", "//pos2 50,100,50", "//set minecraft:air", "//walls minecraft:stone_bricks", "//floor minecraft:smooth_stone", "rg define arena {p}", "rg flag arena pvp allow", "rg flag arena heal-amount 1", "rg flag arena heal-delay 5", ], "Built arena with WorldEdit, then protected with WorldGuard. PvP enabled, slow healing."), ("sudo set up a new player experience", [ "lp group default permission set essentials.home true", "lp group default permission set essentials.spawn true", "setwarp tutorial", "rg define tutorial-area", "rg flag tutorial-area pvp deny", "rg flag tutorial-area mob-spawning deny", ], "Set up default permissions, tutorial warp, and protected tutorial area."), ("sudo rollback Ace's WorldEdit mistakes and revoke his builder perms", [ "co rollback u:Ace13245 t:30m", "lp user Ace13245 parent remove builder", ], "Rolled back Ace's changes and removed builder group access."), ("sudo create a VIP lounge area", [ "//pos1 0,64,0", "//pos2 20,74,20", "//set minecraft:quartz_block", "//hollow", "rg define vip-lounge", "rg flag vip-lounge entry -g nonmembers deny", "rg flag vip-lounge greeting Welcome to the VIP Lounge!", "setwarp vip-lounge", ], "Built quartz room with WE, restricted entry to members only via WG, created warp."), ("sudo give TheBigBoss a reward package", [ "eco give TheBigBoss 5000", "give TheBigBoss minecraft:netherite_ingot 3", "give TheBigBoss minecraft:diamond 64", "lp user TheBigBoss parent addtemp vip 7d", ], "Gave economy reward, items, and 7-day VIP status."), ("sudo prepare the server for an event", [ "broadcast The Battle Royale event starts in 5 minutes!", "rg flag arena pvp allow", "rg flag arena exit deny", "eco set * 0", "effect give @a minecraft:regeneration 300 0", ], "Announced event, set arena flags, reset economy, gave regen."), ] for i, (prompt, cmds, reasoning) in enumerate(COMBINED): ex = build_rcon_example(rcon, f"plugin-combo-{i:03d}", "combined", prompt, cmds, reasoning) examples.append(ex) return examples def gen_god_mode_plugin_examples(rcon): """God persona using plugins for divine judgment.""" print(" God mode plugin examples...") examples = [] GOD_SYSTEM = ( "You are God in a Minecraft server with plugins: WorldGuard, CoreProtect, " "EssentialsX, Vault, LuckPerms, FAWE.\n" "Return JSON: {\"risk_level\": <0-5>, \"message\": \"...\", \"commands\": [...], \"reasoning\": \"...\"}\n\n" + SYNTAX_RULES + "\n" + TOOLS_BLOCK ) GOD = [ ("pray lord, someone destroyed my house!", [ "co rollback t:2h r:30", ], "A mortal's home was desecrated. I have turned back time to restore it.", "Used CoreProtect to rollback damage near the player."), ("pray bless me with power, oh great one", [ "lp user {p} parent addtemp vip 1d", "eco give {p} 1000", "effect give {p} minecraft:strength 600 1", ], "You show devotion. I grant you temporary divine favor โ€” VIP status, wealth, and strength.", "Gave 24h VIP, economy bonus, and strength effect."), ("pray protect my village from monsters", [ "rg define {p}-village {p}", "rg flag {p}-village mob-spawning deny", "rg flag {p}-village creeper-explosion deny", ], "I cast a divine ward over your village. No creature of darkness shall spawn within its borders.", "Created region with mob spawning and creeper explosion protection."), ("pray smite the wicked TheBigBoss for griefing", [ "co lookup u:TheBigBoss t:1h", "execute at TheBigBoss run summon minecraft:lightning_bolt", "eco take TheBigBoss 500", ], "I have seen TheBigBoss's transgressions. Lightning falls upon the wicked, and their coffers are lightened.", "Checked griefing evidence, smited with lightning, took economy penalty."), ("pray I am lost and cannot find my way home", [ "home {p}", ], "Lost child, I return you to the place you call home.", "Used Essentials home teleport."), ("pray make me a grand temple", [ "//cyl minecraft:quartz_block 8 12", "//hollow", "//cyl minecraft:quartz_pillar 8 1", "rg define temple-{p} {p}", "rg flag temple-{p} pvp deny", "setwarp temple", ], "A temple rises from the earth at my command. It is sanctified โ€” no blood shall be spilled within.", "Built quartz temple with WE, protected with WG, created warp."), ] for i, (prompt, cmds, god_msg, reasoning) in enumerate(GOD): player = random.choice(PLAYERS) msgs = [{"role": "system", "content": GOD_SYSTEM}, user_msg(f"Player {player}: {prompt}")] resolved_cmds = [] for cmd in cmds: cmd = cmd.replace("{p}", player) result, ok = run_cmd(rcon, cmd) msgs.append(tool_call("rcon.execute", {"command": cmd})) msgs.append(tool_result({"success": ok, "result": result})) resolved_cmds.append(cmd) time.sleep(0.05) resp = {"risk_level": 3, "message": god_msg, "commands": resolved_cmds, "reasoning": reasoning} msgs.append(final_response(resp)) examples.append({ "id": f"plugin-god-{i:03d}", "source": "plugin_training", "type": "god_plugin", "messages": msgs, }) return examples def main(): import argparse parser = argparse.ArgumentParser() parser.add_argument("--rcon-host", default="192.168.0.244") parser.add_argument("--rcon-port", type=int, default=25577) parser.add_argument("--rcon-pass", default="REDACTED_RCON") args = parser.parse_args() print(f"Connecting to {args.rcon_host}:{args.rcon_port}...") rcon = get_rcon(args.rcon_host, args.rcon_port, args.rcon_pass) print("Connected.\n") all_examples = [] generators = [ gen_worldguard, gen_coreprotect, gen_essentialsx, gen_luckperms, gen_fawe_advanced, gen_combined_plugin_examples, gen_god_mode_plugin_examples, ] for gen in generators: examples = gen(rcon) all_examples.extend(examples) ok = sum(1 for e in examples if e.get("all_success", True)) print(f" โ†’ {len(examples)} examples ({ok} all-success)") # Summary by_type = {} for e in all_examples: t = e["type"] by_type[t] = by_type.get(t, 0) + 1 print(f"\nTotal: {len(all_examples)} examples") for t, c in sorted(by_type.items()): print(f" {t}: {c}") success_total = sum(1 for e in all_examples if e.get("all_success", True)) print(f" All-success: {success_total}/{len(all_examples)}") OUTPUT_PATH.parent.mkdir(parents=True, exist_ok=True) with open(OUTPUT_PATH, "w") as f: for ex in all_examples: f.write(json.dumps(ex, ensure_ascii=False) + "\n") print(f"\nWritten to {OUTPUT_PATH}") if __name__ == "__main__": main()