Merge branch 'origin/master' - combine double-execution fixes
Both branches fixed the --exec double-execution bug with complementary approaches: - origin/master: Added executeStartupScripts() method for cleaner separation - HEAD: Avoided engine recreation to preserve state This merge keeps the best of both: executeStartupScripts() called on the existing engine without recreation. Also accepts deletion of flaky test_viewport_visual.py from origin/master. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
commit
a703bce196
|
|
@ -63,23 +63,31 @@ GameEngine::GameEngine(const McRogueFaceConfig& cfg)
|
||||||
McRFPy_API::executePyString("import mcrfpy");
|
McRFPy_API::executePyString("import mcrfpy");
|
||||||
McRFPy_API::executeScript("scripts/game.py");
|
McRFPy_API::executeScript("scripts/game.py");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note: --exec scripts are NOT executed here.
|
||||||
|
// They are executed via executeStartupScripts() after the final engine is set up.
|
||||||
|
// This prevents double-execution when main.cpp creates multiple GameEngine instances.
|
||||||
|
|
||||||
|
clock.restart();
|
||||||
|
runtime.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameEngine::executeStartupScripts()
|
||||||
|
{
|
||||||
// Execute any --exec scripts in order
|
// Execute any --exec scripts in order
|
||||||
|
// This is called ONCE from main.cpp after the final engine is set up
|
||||||
if (!config.exec_scripts.empty()) {
|
if (!config.exec_scripts.empty()) {
|
||||||
if (!Py_IsInitialized()) {
|
if (!Py_IsInitialized()) {
|
||||||
McRFPy_API::api_init();
|
McRFPy_API::api_init();
|
||||||
}
|
}
|
||||||
McRFPy_API::executePyString("import mcrfpy");
|
McRFPy_API::executePyString("import mcrfpy");
|
||||||
|
|
||||||
for (const auto& exec_script : config.exec_scripts) {
|
for (const auto& exec_script : config.exec_scripts) {
|
||||||
std::cout << "Executing script: " << exec_script << std::endl;
|
std::cout << "Executing script: " << exec_script << std::endl;
|
||||||
McRFPy_API::executeScript(exec_script.string());
|
McRFPy_API::executeScript(exec_script.string());
|
||||||
}
|
}
|
||||||
std::cout << "All --exec scripts completed" << std::endl;
|
std::cout << "All --exec scripts completed" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
clock.restart();
|
|
||||||
runtime.restart();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GameEngine::~GameEngine()
|
GameEngine::~GameEngine()
|
||||||
|
|
|
||||||
|
|
@ -146,6 +146,7 @@ public:
|
||||||
void run();
|
void run();
|
||||||
void sUserInput();
|
void sUserInput();
|
||||||
void cleanup(); // Clean up Python references before destruction
|
void cleanup(); // Clean up Python references before destruction
|
||||||
|
void executeStartupScripts(); // Execute --exec scripts (called once after final engine setup)
|
||||||
int getFrame() { return currentFrame; }
|
int getFrame() { return currentFrame; }
|
||||||
float getFrameTime() { return frameTime; }
|
float getFrameTime() { return frameTime; }
|
||||||
sf::View getView() { return visible; }
|
sf::View getView() { return visible; }
|
||||||
|
|
|
||||||
|
|
@ -183,8 +183,8 @@ int run_python_interpreter(const McRogueFaceConfig& config)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
else if (!config.exec_scripts.empty()) {
|
else if (!config.exec_scripts.empty()) {
|
||||||
// With --exec, scripts were already executed by the first GameEngine constructor.
|
// Execute startup scripts on the existing engine (not in constructor to prevent double-execution)
|
||||||
// Just configure auto-exit and run the existing engine to preserve timers/state.
|
engine->executeStartupScripts();
|
||||||
if (config.headless) {
|
if (config.headless) {
|
||||||
engine->setAutoExitAfterExec(true);
|
engine->setAutoExitAfterExec(true);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,6 @@ from mcrfpy import automation
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
|
||||||
# Note: Engine runs --exec scripts twice - we use this to our advantage
|
|
||||||
# First run sets up scenes, second run's timer fires after game loop starts
|
|
||||||
|
|
||||||
# Add parent to path for imports
|
# Add parent to path for imports
|
||||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,141 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
"""Visual viewport test with screenshots"""
|
|
||||||
|
|
||||||
import mcrfpy
|
|
||||||
from mcrfpy import Window, Frame, Caption, Color
|
|
||||||
import sys
|
|
||||||
|
|
||||||
def test_viewport_visual(runtime):
|
|
||||||
"""Visual test of viewport modes"""
|
|
||||||
mcrfpy.delTimer("test")
|
|
||||||
|
|
||||||
print("Creating visual viewport test...")
|
|
||||||
|
|
||||||
# Get window singleton
|
|
||||||
window = Window.get()
|
|
||||||
|
|
||||||
# Create test scene
|
|
||||||
scene = mcrfpy.sceneUI("test")
|
|
||||||
|
|
||||||
# Create visual elements at game resolution boundaries
|
|
||||||
game_res = window.game_resolution
|
|
||||||
|
|
||||||
# Full boundary frame
|
|
||||||
boundary = Frame(pos=(0, 0), size=(game_res[0], game_res[1]),
|
|
||||||
fill_color=Color(40, 40, 80),
|
|
||||||
outline_color=Color(255, 255, 0),
|
|
||||||
outline=3)
|
|
||||||
scene.append(boundary)
|
|
||||||
|
|
||||||
# Corner markers
|
|
||||||
corner_size = 100
|
|
||||||
colors = [
|
|
||||||
Color(255, 100, 100), # Red TL
|
|
||||||
Color(100, 255, 100), # Green TR
|
|
||||||
Color(100, 100, 255), # Blue BL
|
|
||||||
Color(255, 255, 100), # Yellow BR
|
|
||||||
]
|
|
||||||
positions = [
|
|
||||||
(0, 0), # Top-left
|
|
||||||
(game_res[0] - corner_size, 0), # Top-right
|
|
||||||
(0, game_res[1] - corner_size), # Bottom-left
|
|
||||||
(game_res[0] - corner_size, game_res[1] - corner_size) # Bottom-right
|
|
||||||
]
|
|
||||||
labels = ["TL", "TR", "BL", "BR"]
|
|
||||||
|
|
||||||
for (x, y), color, label in zip(positions, colors, labels):
|
|
||||||
corner = Frame(pos=(x, y), size=(corner_size, corner_size),
|
|
||||||
fill_color=color,
|
|
||||||
outline_color=Color(255, 255, 255),
|
|
||||||
outline=2)
|
|
||||||
scene.append(corner)
|
|
||||||
|
|
||||||
text = Caption(text=label, pos=(x + 10, y + 10))
|
|
||||||
text.font_size = 32
|
|
||||||
text.fill_color = Color(0, 0, 0)
|
|
||||||
scene.append(text)
|
|
||||||
|
|
||||||
# Center crosshair
|
|
||||||
center_x = game_res[0] // 2
|
|
||||||
center_y = game_res[1] // 2
|
|
||||||
h_line = Frame(pos=(0, center_y - 1), size=(game_res[0], 2),
|
|
||||||
fill_color=Color(255, 255, 255, 128))
|
|
||||||
v_line = Frame(pos=(center_x - 1, 0), size=(2, game_res[1]),
|
|
||||||
fill_color=Color(255, 255, 255, 128))
|
|
||||||
scene.append(h_line)
|
|
||||||
scene.append(v_line)
|
|
||||||
|
|
||||||
# Mode text
|
|
||||||
mode_text = Caption(text=f"Mode: {window.scaling_mode}",
|
|
||||||
pos=(center_x - 100, center_y - 50))
|
|
||||||
mode_text.font_size = 36
|
|
||||||
mode_text.fill_color = Color(255, 255, 255)
|
|
||||||
scene.append(mode_text)
|
|
||||||
|
|
||||||
# Resolution text
|
|
||||||
res_text = Caption(text=f"Game: {game_res[0]}x{game_res[1]}",
|
|
||||||
pos=(center_x - 150, center_y + 10))
|
|
||||||
res_text.font_size = 24
|
|
||||||
res_text.fill_color = Color(200, 200, 200)
|
|
||||||
scene.append(res_text)
|
|
||||||
|
|
||||||
from mcrfpy import automation
|
|
||||||
|
|
||||||
# Test different modes and window sizes
|
|
||||||
def test_sequence(runtime):
|
|
||||||
mcrfpy.delTimer("seq")
|
|
||||||
|
|
||||||
# Test 1: Fit mode with original size
|
|
||||||
print("Test 1: Fit mode, original window size")
|
|
||||||
automation.screenshot("viewport_01_fit_original.png")
|
|
||||||
|
|
||||||
# Test 2: Wider window
|
|
||||||
window.resolution = (1400, 768)
|
|
||||||
print(f"Test 2: Fit mode, wider window {window.resolution}")
|
|
||||||
automation.screenshot("viewport_02_fit_wide.png")
|
|
||||||
|
|
||||||
# Test 3: Taller window
|
|
||||||
window.resolution = (1024, 900)
|
|
||||||
print(f"Test 3: Fit mode, taller window {window.resolution}")
|
|
||||||
automation.screenshot("viewport_03_fit_tall.png")
|
|
||||||
|
|
||||||
# Test 4: Center mode
|
|
||||||
window.scaling_mode = "center"
|
|
||||||
mode_text.text = "Mode: center"
|
|
||||||
print(f"Test 4: Center mode {window.resolution}")
|
|
||||||
automation.screenshot("viewport_04_center.png")
|
|
||||||
|
|
||||||
# Test 5: Stretch mode
|
|
||||||
window.scaling_mode = "stretch"
|
|
||||||
mode_text.text = "Mode: stretch"
|
|
||||||
window.resolution = (1280, 720)
|
|
||||||
print(f"Test 5: Stretch mode {window.resolution}")
|
|
||||||
automation.screenshot("viewport_05_stretch.png")
|
|
||||||
|
|
||||||
# Test 6: Small window with fit
|
|
||||||
window.scaling_mode = "fit"
|
|
||||||
mode_text.text = "Mode: fit"
|
|
||||||
window.resolution = (640, 480)
|
|
||||||
print(f"Test 6: Fit mode, small window {window.resolution}")
|
|
||||||
automation.screenshot("viewport_06_fit_small.png")
|
|
||||||
|
|
||||||
print("\nViewport visual test completed!")
|
|
||||||
print("Screenshots saved:")
|
|
||||||
print(" - viewport_01_fit_original.png")
|
|
||||||
print(" - viewport_02_fit_wide.png")
|
|
||||||
print(" - viewport_03_fit_tall.png")
|
|
||||||
print(" - viewport_04_center.png")
|
|
||||||
print(" - viewport_05_stretch.png")
|
|
||||||
print(" - viewport_06_fit_small.png")
|
|
||||||
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
# Start test sequence after a short delay
|
|
||||||
mcrfpy.setTimer("seq", test_sequence, 500)
|
|
||||||
|
|
||||||
# Main execution
|
|
||||||
print("Starting visual viewport test...")
|
|
||||||
mcrfpy.createScene("test")
|
|
||||||
mcrfpy.setScene("test")
|
|
||||||
mcrfpy.setTimer("test", test_viewport_visual, 100)
|
|
||||||
print("Test scheduled...")
|
|
||||||
Loading…
Reference in New Issue