McRogueFace/exec_flag_implementation.cpp

189 lines
6.0 KiB
C++

// 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<std::filesystem::path> 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