// Example implementation of --exec flag for McRogueFace // This shows the minimal changes needed to support multiple script execution // === In McRogueFaceConfig.h === struct McRogueFaceConfig { // ... existing fields ... // Scripts to execute after main script (McRogueFace style) std::vector exec_scripts; }; // === In CommandLineParser.cpp === CommandLineParser::ParseResult CommandLineParser::parse(McRogueFaceConfig& config) { // ... existing parsing code ... for (int i = 1; i < argc; i++) { std::string arg = argv[i]; // ... existing flag handling ... else if (arg == "--exec") { // Add script to exec list if (i + 1 < argc) { config.exec_scripts.push_back(argv[++i]); } else { std::cerr << "Error: --exec requires a script path\n"; return {true, 1}; } } } } // === In GameEngine.cpp === GameEngine::GameEngine(const McRogueFaceConfig& cfg) : config(cfg) { // ... existing initialization ... // Only load game.py if no custom script/command/module is specified bool should_load_game = config.script_path.empty() && config.python_command.empty() && config.python_module.empty() && !config.interactive_mode && !config.python_mode && config.exec_scripts.empty(); // Add this check if (should_load_game) { if (!Py_IsInitialized()) { McRFPy_API::api_init(); } McRFPy_API::executePyString("import mcrfpy"); McRFPy_API::executeScript("scripts/game.py"); } // Execute any --exec scripts for (const auto& exec_script : config.exec_scripts) { std::cout << "Executing script: " << exec_script << std::endl; McRFPy_API::executeScript(exec_script.string()); } } // === Usage Examples === // Example 1: Run game with automation // ./mcrogueface game.py --exec automation.py // Example 2: Run game with multiple automation scripts // ./mcrogueface game.py --exec test_suite.py --exec monitor.py --exec logger.py // Example 3: Run only automation (no game) // ./mcrogueface --exec standalone_test.py // Example 4: Headless automation // ./mcrogueface --headless game.py --exec automation.py // === Python Script Example (automation.py) === /* import mcrfpy from mcrfpy import automation def periodic_test(): """Run automated tests every 5 seconds""" # Take screenshot automation.screenshot(f"test_{mcrfpy.getFrame()}.png") # Check game state scene = mcrfpy.currentScene() if scene == "main_menu": # Click start button automation.click(400, 300) elif scene == "game": # Perform game tests automation.hotkey("i") # Open inventory print(f"Test completed at frame {mcrfpy.getFrame()}") # Register timer for periodic testing mcrfpy.setTimer("automation_test", periodic_test, 5000) print("Automation script loaded - tests will run every 5 seconds") # Script returns here - giving control back to C++ */ // === Advanced Example: Event-Driven Automation === /* # automation_advanced.py import mcrfpy from mcrfpy import automation import json class AutomationFramework: def __init__(self): self.test_queue = [] self.results = [] self.load_test_suite() def load_test_suite(self): """Load test definitions from JSON""" with open("test_suite.json") as f: self.test_queue = json.load(f)["tests"] def run_next_test(self): """Execute next test in queue""" if not self.test_queue: self.finish_testing() return test = self.test_queue.pop(0) try: if test["type"] == "click": automation.click(test["x"], test["y"]) elif test["type"] == "key": automation.keyDown(test["key"]) automation.keyUp(test["key"]) elif test["type"] == "screenshot": automation.screenshot(test["filename"]) elif test["type"] == "wait": # Re-queue this test for later self.test_queue.insert(0, test) return self.results.append({"test": test, "status": "pass"}) except Exception as e: self.results.append({"test": test, "status": "fail", "error": str(e)}) def finish_testing(self): """Save test results and cleanup""" with open("test_results.json", "w") as f: json.dump(self.results, f, indent=2) print(f"Testing complete: {len(self.results)} tests executed") mcrfpy.delTimer("automation_framework") # Create and start automation framework = AutomationFramework() mcrfpy.setTimer("automation_framework", framework.run_next_test, 100) */ // === Thread Safety Considerations === // The --exec approach requires NO thread safety changes because: // 1. All scripts run in the same Python interpreter // 2. Scripts execute sequentially during initialization // 3. After initialization, only callbacks run (timer/input based) // 4. C++ maintains control of the render loop // This is the "honor system" - scripts must: // - Set up their callbacks/timers // - Return control to C++ // - Not block or run infinite loops // - Use timers for periodic tasks // === Future Extensions === // 1. Script communication via shared Python modules // game.py: // import mcrfpy // mcrfpy.game_state = {"level": 1, "score": 0} // // automation.py: // import mcrfpy // if mcrfpy.game_state["level"] == 1: // # Test level 1 specific features // 2. Priority-based script execution // ./mcrogueface game.py --exec-priority high:critical.py --exec-priority low:logging.py // 3. Conditional execution // ./mcrogueface game.py --exec-if-scene menu:menu_test.py --exec-if-scene game:game_test.py