feat: Add mcrfpy.step() and synchronous screenshot for headless mode (closes #153)
Implements Python-controlled simulation advancement for headless mode: - Add mcrfpy.step(dt) to advance simulation by dt seconds - step(None) advances to next scheduled event (timer/animation) - Timers use simulation_time in headless mode for deterministic behavior - automation.screenshot() now renders synchronously in headless mode (captures current state, not previous frame) This enables LLM agent orchestration (#156) by allowing: - Set perspective, take screenshot, query LLM - all synchronous - Deterministic simulation control without frame timing issues - Event-driven advancement with step(None) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
f33e79a123
commit
60ffa68d04
7 changed files with 409 additions and 10 deletions
|
|
@ -185,47 +185,52 @@ void McRFPy_Automation::injectTextEvent(sf::Uint32 unicode) {
|
|||
}
|
||||
|
||||
// Screenshot implementation
|
||||
// #153 - In headless mode, this is now SYNCHRONOUS: renders scene then captures
|
||||
PyObject* McRFPy_Automation::_screenshot(PyObject* self, PyObject* args) {
|
||||
const char* filename;
|
||||
if (!PyArg_ParseTuple(args, "s", &filename)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
auto engine = getGameEngine();
|
||||
if (!engine) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Game engine not initialized");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// Get the render target
|
||||
sf::RenderTarget* target = engine->getRenderTargetPtr();
|
||||
if (!target) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "No render target available");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// For RenderWindow, we can get a screenshot directly
|
||||
|
||||
// For RenderWindow (windowed mode), capture the current buffer
|
||||
if (auto* window = dynamic_cast<sf::RenderWindow*>(target)) {
|
||||
sf::Vector2u windowSize = window->getSize();
|
||||
sf::Texture texture;
|
||||
texture.create(windowSize.x, windowSize.y);
|
||||
texture.update(*window);
|
||||
|
||||
|
||||
if (texture.copyToImage().saveToFile(filename)) {
|
||||
Py_RETURN_TRUE;
|
||||
} else {
|
||||
Py_RETURN_FALSE;
|
||||
}
|
||||
}
|
||||
// For RenderTexture (headless mode)
|
||||
// For RenderTexture (headless mode) - SYNCHRONOUS render then capture
|
||||
else if (auto* renderTexture = dynamic_cast<sf::RenderTexture*>(target)) {
|
||||
// #153 - Force a synchronous render before capturing
|
||||
// This ensures we capture the CURRENT state, not the previous frame
|
||||
engine->renderScene();
|
||||
|
||||
if (renderTexture->getTexture().copyToImage().saveToFile(filename)) {
|
||||
Py_RETURN_TRUE;
|
||||
} else {
|
||||
Py_RETURN_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PyErr_SetString(PyExc_RuntimeError, "Unknown render target type");
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue