From 972768eb265d2a95796d63400c10d9e8364076cb Mon Sep 17 00:00:00 2001 From: John McCardle Date: Tue, 12 Mar 2024 21:02:48 -0400 Subject: [PATCH 1/5] inital PyCallable work; isolate very well behaved usage of PyObject references behind RAII --- src/GameEngine.cpp | 6 ++--- src/GameEngine.h | 4 +++- src/McRFPy_API.h | 6 ----- src/PyCallable.cpp | 57 ++++++++++++++++++++++++++++++++++++++++++++++ src/PyCallable.h | 26 +++++++++++++++++++++ 5 files changed, 89 insertions(+), 10 deletions(-) create mode 100644 src/PyCallable.cpp create mode 100644 src/PyCallable.h diff --git a/src/GameEngine.cpp b/src/GameEngine.cpp index 554eabf..beb2b31 100644 --- a/src/GameEngine.cpp +++ b/src/GameEngine.cpp @@ -75,7 +75,7 @@ void GameEngine::manageTimer(std::string name, PyObject* target, int interval) { if (target == NULL || target == Py_None) // delete { - Py_DECREF(timers[name].target); + //Py_DECREF(timers[name].target); timers.erase(it); return; } @@ -85,7 +85,7 @@ void GameEngine::manageTimer(std::string name, PyObject* target, int interval) std::cout << "Refusing to initialize timer to None. It's not an error, it's just pointless." << std::endl; return; } - timers[name] = Timer(target, interval, runtime.getElapsedTime().asMilliseconds()); + timers[name] = std::make_shared(target, interval, runtime.getElapsedTime().asMilliseconds()); Py_INCREF(target); } @@ -94,7 +94,7 @@ void GameEngine::testTimers() int now = runtime.getElapsedTime().asMilliseconds(); for (auto& [name, timer]: timers) { - timer.test(now); + timer->test(now); } } diff --git a/src/GameEngine.h b/src/GameEngine.h index 138e74d..8d688b3 100644 --- a/src/GameEngine.h +++ b/src/GameEngine.h @@ -5,6 +5,7 @@ #include "McRFPy_API.h" #include "IndexTexture.h" #include "Timer.h" +#include "PyCallable.h" class GameEngine { @@ -20,7 +21,8 @@ class GameEngine std::string window_title; sf::Clock runtime; - std::map timers; + //std::map timers; + std::map> timers; void testTimers(); public: diff --git a/src/McRFPy_API.h b/src/McRFPy_API.h index 588f897..025706b 100644 --- a/src/McRFPy_API.h +++ b/src/McRFPy_API.h @@ -12,12 +12,6 @@ private: texture_width = 12, texture_height = 11, // w & h sprite/frame count texture_sprite_count = 11 * 12; // t_width * t_height, minus blanks? - // TODO: this is wrong, load resources @ GameEngineSprite sprite; - // sf::Texture texture; - - //std::vector mcrfpyMethodsVector; - //static PyObject* PyInit_mcrfpy(); - McRFPy_API(); public: diff --git a/src/PyCallable.cpp b/src/PyCallable.cpp new file mode 100644 index 0000000..5e16884 --- /dev/null +++ b/src/PyCallable.cpp @@ -0,0 +1,57 @@ +#include "PyCallable.h" + +PyCallable::PyCallable(PyObject* _target) +{ + target = Py_XNewRef(_target); +} + +PyCallable::~PyCallable() +{ + if (target) + Py_DECREF(target); +} + +PyObject* PyCallable::call(PyObject* args, PyObject* kwargs) +{ + return PyObject_Call(target, args, kwargs); +} + +PyTimerCallable::PyTimerCallable(PyObject* _target, int _interval, int now) +: PyCallable(_target), interval(_interval), last_ran(now) +{} + +PyTimerCallable::PyTimerCallable() +: PyCallable(Py_None), interval(0), last_ran(0) +{} + +bool PyTimerCallable::hasElapsed(int now) +{ + return now >= last_ran + interval; +} + +void PyTimerCallable::call(int now) +{ + PyObject* args = Py_BuildValue("(i)", now); + PyObject* retval = PyCallable::call(args, NULL); + if (!retval) + { + std::cout << "timer has raised an exception. It's going to STDERR and being dropped:" << std::endl; + PyErr_Print(); + PyErr_Clear(); + } else if (retval != Py_None) + { + std::cout << "timer returned a non-None value. It's not an error, it's just not being saved or used." << std::endl; + std::cout << PyUnicode_AsUTF8(PyObject_Repr(retval)) << std::endl; + } +} + +bool PyTimerCallable::test(int now) +{ + if(hasElapsed(now)) + { + call(now); + last_ran = now; + return true; + } + return false; +} diff --git a/src/PyCallable.h b/src/PyCallable.h new file mode 100644 index 0000000..fd9257d --- /dev/null +++ b/src/PyCallable.h @@ -0,0 +1,26 @@ +#pragma once +#include "Common.h" +#include "Python.h" + +class PyCallable +{ +private: + PyObject* target; +protected: + PyCallable(PyObject*); + ~PyCallable(); + PyObject* call(PyObject*, PyObject*); +}; + +class PyTimerCallable: public PyCallable +{ +private: + int interval; + int last_ran; + void call(int); +public: + bool hasElapsed(int); + bool test(int); + PyTimerCallable(PyObject*, int, int); + PyTimerCallable(); +}; From 05d9f6a88233931313b9ed09c86227608a676749 Mon Sep 17 00:00:00 2001 From: John McCardle Date: Tue, 12 Mar 2024 22:27:12 -0400 Subject: [PATCH 2/5] wow, good test of Key and Click Callable classes. Cleanup, squash, and merge after I give it a lookover in daylight, though. --- src/GameEngine.cpp | 5 ++++- src/McRFPy_API.cpp | 3 +++ src/PyCallable.cpp | 52 +++++++++++++++++++++++++++++++++++++++++++++ src/PyCallable.h | 21 ++++++++++++++++-- src/PyScene.cpp | 4 ++++ src/Scene.cpp | 8 ++++++- src/Scene.h | 4 +++- src/UI.cpp | 8 ++++++- src/UI.h | 12 ++++++----- src/UITestScene.cpp | 5 +++-- 10 files changed, 109 insertions(+), 13 deletions(-) diff --git a/src/GameEngine.cpp b/src/GameEngine.cpp index beb2b31..2b62886 100644 --- a/src/GameEngine.cpp +++ b/src/GameEngine.cpp @@ -157,8 +157,10 @@ void GameEngine::sUserInput() std::string name = currentScene()->action(actionCode); currentScene()->doAction(name, actionType); } - else if (currentScene()->key_callable != NULL && currentScene()->key_callable != Py_None) + else if (currentScene()->key_callable) { + currentScene()->key_callable->call(ActionCode::key_str(event.key.code), actionType); + /* PyObject* args = Py_BuildValue("(ss)", ActionCode::key_str(event.key.code).c_str(), actionType.c_str()); PyObject* retval = PyObject_Call(currentScene()->key_callable, args, NULL); if (!retval) @@ -170,6 +172,7 @@ void GameEngine::sUserInput() { std::cout << "key_callable returned a non-None value. It's not an error, it's just not being saved or used." << std::endl; } + */ } else { diff --git a/src/McRFPy_API.cpp b/src/McRFPy_API.cpp index 5aa61e1..113c13a 100644 --- a/src/McRFPy_API.cpp +++ b/src/McRFPy_API.cpp @@ -453,6 +453,7 @@ PyObject* McRFPy_API::_createScene(PyObject* self, PyObject* args) { PyObject* McRFPy_API::_keypressScene(PyObject* self, PyObject* args) { PyObject* callable; if (!PyArg_ParseTuple(args, "O", &callable)) return NULL; + /* if (game->currentScene()->key_callable != NULL and game->currentScene()->key_callable != Py_None) { Py_DECREF(game->currentScene()->key_callable); @@ -460,6 +461,8 @@ PyObject* McRFPy_API::_keypressScene(PyObject* self, PyObject* args) { Py_INCREF(callable); game->currentScene()->key_callable = callable; Py_INCREF(Py_None); + */ + game->currentScene()->key_callable = std::make_unique(callable); return Py_None; } diff --git a/src/PyCallable.cpp b/src/PyCallable.cpp index 5e16884..2226128 100644 --- a/src/PyCallable.cpp +++ b/src/PyCallable.cpp @@ -55,3 +55,55 @@ bool PyTimerCallable::test(int now) } return false; } + +PyClickCallable::PyClickCallable(PyObject* _target) +: PyCallable(_target) +{} + +PyClickCallable::PyClickCallable() +: PyCallable(Py_None) +{} + +void PyClickCallable::call(sf::Vector2f mousepos, std::string button, std::string action) +{ + PyObject* args = Py_BuildValue("(iiss)", (int)mousepos.x, (int)mousepos.y, button.c_str(), action.c_str()); + PyObject* retval = PyCallable::call(args, NULL); + if (!retval) + { + std::cout << "ClickCallable has raised an exception. It's going to STDERR and being dropped:" << std::endl; + PyErr_Print(); + PyErr_Clear(); + } else if (retval != Py_None) + { + std::cout << "ClickCallable returned a non-None value. It's not an error, it's just not being saved or used." << std::endl; + std::cout << PyUnicode_AsUTF8(PyObject_Repr(retval)) << std::endl; + } +} + +PyObject* PyClickCallable::borrow() +{ + return target; +} + +PyKeyCallable::PyKeyCallable(PyObject* _target) +: PyCallable(_target) +{} + +PyKeyCallable::PyKeyCallable() +: PyCallable(Py_None) +{} + +void PyKeyCallable::call(std::string key, std::string action) +{ + PyObject* args = Py_BuildValue("(ss)", key.c_str(), action.c_str()); + PyObject* retval = PyCallable::call(args, NULL); + if (!retval) + { + std::cout << "KeyCallable has raised an exception. It's going to STDERR and being dropped:" << std::endl; + PyErr_Print(); + PyErr_Clear(); + } else if (retval != Py_None) + { + std::cout << "KeyCallable returned a non-None value. It's not an error, it's just not being saved or used." << std::endl; + } +} diff --git a/src/PyCallable.h b/src/PyCallable.h index fd9257d..b59e774 100644 --- a/src/PyCallable.h +++ b/src/PyCallable.h @@ -4,9 +4,8 @@ class PyCallable { -private: - PyObject* target; protected: + PyObject* target; PyCallable(PyObject*); ~PyCallable(); PyObject* call(PyObject*, PyObject*); @@ -24,3 +23,21 @@ public: PyTimerCallable(PyObject*, int, int); PyTimerCallable(); }; + +class PyClickCallable: public PyCallable +{ +public: + void call(sf::Vector2f, std::string, std::string); + PyObject* borrow(); + PyClickCallable(PyObject*); + PyClickCallable(); +}; + +class PyKeyCallable: public PyCallable +{ +public: + void call(std::string, std::string); + //PyObject* borrow(); // not yet implemented + PyKeyCallable(PyObject*); + PyKeyCallable(); +}; diff --git a/src/PyScene.cpp b/src/PyScene.cpp index ab62567..5ed8b74 100644 --- a/src/PyScene.cpp +++ b/src/PyScene.cpp @@ -1,6 +1,7 @@ #include "PyScene.h" #include "ActionCode.h" #include "Resources.h" +#include "PyCallable.h" PyScene::PyScene(GameEngine* g) : Scene(g) { @@ -27,6 +28,7 @@ void PyScene::do_mouse_input(std::string button, std::string type) target = d->click_at(sf::Vector2f(mousepos)); if (target) { + /* PyObject* args = Py_BuildValue("(iiss)", (int)mousepos.x, (int)mousepos.y, button.c_str(), type.c_str()); PyObject* retval = PyObject_Call(target->click_callable, args, NULL); if (!retval) @@ -38,6 +40,8 @@ void PyScene::do_mouse_input(std::string button, std::string type) { std::cout << "click_callable returned a non-None value. It's not an error, it's just not being saved or used." << std::endl; } + */ + target->click_callable->call(mousepos, button, type); } } } diff --git a/src/Scene.cpp b/src/Scene.cpp index 8567e61..d9438e3 100644 --- a/src/Scene.cpp +++ b/src/Scene.cpp @@ -4,7 +4,7 @@ //Scene::Scene() { game = 0; std::cout << "WARN: default Scene constructor called. (game = " << game << ")" << std::endl;}; Scene::Scene(GameEngine* g) { - key_callable = Py_None; + key_callable = std::make_unique(); game = g; ui_elements = std::make_shared>>(); } @@ -43,6 +43,7 @@ bool Scene::unregisterActionInjected(int code, std::string name) void Scene::key_register(PyObject* callable) { + /* if (key_callable) { // decrement reference before overwriting @@ -50,11 +51,16 @@ void Scene::key_register(PyObject* callable) } key_callable = callable; Py_INCREF(key_callable); + */ + key_callable = std::make_unique(callable); } void Scene::key_unregister() { + /* if (key_callable == NULL) return; Py_DECREF(key_callable); key_callable = NULL; + */ + key_callable.reset(); } diff --git a/src/Scene.h b/src/Scene.h index 90b82ef..02931b1 100644 --- a/src/Scene.h +++ b/src/Scene.h @@ -9,6 +9,7 @@ #include "Common.h" #include #include "UI.h" +#include "PyCallable.h" //#include "GameEngine.h" class GameEngine; // forward declare @@ -41,7 +42,8 @@ public: std::shared_ptr>> ui_elements; - PyObject* key_callable; + //PyObject* key_callable; + std::unique_ptr key_callable; void key_register(PyObject*); void key_unregister(); }; diff --git a/src/UI.cpp b/src/UI.cpp index eeb2354..b2447cd 100644 --- a/src/UI.cpp +++ b/src/UI.cpp @@ -56,6 +56,7 @@ UIDrawable* UIGrid::click_at(sf::Vector2f point) void UIDrawable::click_register(PyObject* callable) { + /* if (click_callable) { // decrement reference before overwriting @@ -63,13 +64,18 @@ void UIDrawable::click_register(PyObject* callable) } click_callable = callable; Py_INCREF(click_callable); + */ + click_callable = std::make_unique(callable); } void UIDrawable::click_unregister() { + /* if (click_callable == NULL) return; Py_DECREF(click_callable); click_callable = NULL; + */ + click_callable.reset(); } void UIDrawable::render() @@ -378,7 +384,7 @@ void UIGrid::render(sf::Vector2f) for (auto e : *entities) { // TODO skip out-of-bounds entities (grid square not visible at all, check for partially on visible grid squares / floating point grid position) //auto drawent = e->cGrid->indexsprite.drawable(); - auto drawent = e->sprite; + auto& drawent = e->sprite; //drawent.setScale(zoom, zoom); drawent.setScale(zoom); auto pixel_pos = sf::Vector2f( diff --git a/src/UI.h b/src/UI.h index d6d6ac4..665aa95 100644 --- a/src/UI.h +++ b/src/UI.h @@ -5,6 +5,7 @@ #include "IndexTexture.h" #include "Resources.h" #include +#include "PyCallable.h" enum PyObjectsEnum : int { @@ -27,7 +28,8 @@ public: virtual PyObjectsEnum derived_type() = 0; // Mouse input handling - callable object, methods to find event's destination - PyObject* click_callable; + //PyObject* click_callable; + std::unique_ptr click_callable; virtual UIDrawable* click_at(sf::Vector2f point) = 0; void click_register(PyObject*); void click_unregister(); @@ -322,16 +324,16 @@ static PyObject* PyUIDrawable_get_click(PyUIGridObject* self, void* closure) { switch (objtype) { case PyObjectsEnum::UIFRAME: - ptr = ((PyUIFrameObject*)self)->data->click_callable; + ptr = ((PyUIFrameObject*)self)->data->click_callable->borrow(); break; case PyObjectsEnum::UICAPTION: - ptr = ((PyUICaptionObject*)self)->data->click_callable; + ptr = ((PyUICaptionObject*)self)->data->click_callable->borrow(); break; case PyObjectsEnum::UISPRITE: - ptr = ((PyUISpriteObject*)self)->data->click_callable; + ptr = ((PyUISpriteObject*)self)->data->click_callable->borrow(); break; case PyObjectsEnum::UIGRID: - ptr = ((PyUIGridObject*)self)->data->click_callable; + ptr = ((PyUIGridObject*)self)->data->click_callable->borrow(); break; default: PyErr_SetString(PyExc_TypeError, "no idea how you did that; invalid UIDrawable derived instance for _get_click"); diff --git a/src/UITestScene.cpp b/src/UITestScene.cpp index 656ef84..ff70c53 100644 --- a/src/UITestScene.cpp +++ b/src/UITestScene.cpp @@ -123,8 +123,9 @@ UITestScene::UITestScene(GameEngine* g) : Scene(g) // TODO - reimplement UISprite style rendering within UIEntity class. Entities don't have a screen pixel position, they have a grid position, and grid sets zoom when rendering them. auto e5a = std::make_shared(*e5); // this basic constructor sucks: sprite position + zoom are irrelevant for UIEntity. e5a->grid = e5; - auto e5as = UISprite(indextex, 85, sf::Vector2f(0, 0), 1.0); - e5a->sprite = e5as; // will copy constructor even exist for UISprite...? + //auto e5as = UISprite(indextex, 85, sf::Vector2f(0, 0), 1.0); + //e5a->sprite = e5as; // will copy constructor even exist for UISprite...? + e5a->sprite = UISprite(indextex, 85, sf::Vector2f(0, 0), 1.0); e5a->position = sf::Vector2f(1, 0); e5->entities->push_back(e5a); From 0a8f67e3911b17b3e9dfd52893d120bcf591516b Mon Sep 17 00:00:00 2001 From: John McCardle Date: Thu, 14 Mar 2024 23:13:13 -0400 Subject: [PATCH 3/5] Stress test is failing: By removing a timer to a function (from inside that function?) I can immediately cause a segfault. --- src/GameEngine.cpp | 4 ++- src/PyCallable.cpp | 9 +++++ src/scripts/game.py | 84 +++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 94 insertions(+), 3 deletions(-) diff --git a/src/GameEngine.cpp b/src/GameEngine.cpp index 2b62886..74ed443 100644 --- a/src/GameEngine.cpp +++ b/src/GameEngine.cpp @@ -76,7 +76,9 @@ void GameEngine::manageTimer(std::string name, PyObject* target, int interval) if (target == NULL || target == Py_None) // delete { //Py_DECREF(timers[name].target); + std::cout << "Erasing a timer" << std::endl; timers.erase(it); + std::cout << "It was erased" << std::endl; return; } } @@ -86,7 +88,7 @@ void GameEngine::manageTimer(std::string name, PyObject* target, int interval) return; } timers[name] = std::make_shared(target, interval, runtime.getElapsedTime().asMilliseconds()); - Py_INCREF(target); + //Py_INCREF(target); } void GameEngine::testTimers() diff --git a/src/PyCallable.cpp b/src/PyCallable.cpp index 2226128..dee7884 100644 --- a/src/PyCallable.cpp +++ b/src/PyCallable.cpp @@ -3,6 +3,10 @@ PyCallable::PyCallable(PyObject* _target) { target = Py_XNewRef(_target); + if (target) + { + //std::cout << PyUnicode_AsUTF8(PyObject_Repr(target)) << std::endl; + } } PyCallable::~PyCallable() @@ -13,6 +17,9 @@ PyCallable::~PyCallable() PyObject* PyCallable::call(PyObject* args, PyObject* kwargs) { + std::cout << "Calling object with args: "; + std::cout << PyUnicode_AsUTF8(PyObject_Repr(target)) << " <- "; + std::cout << PyUnicode_AsUTF8(PyObject_Repr(args)) << std::endl; return PyObject_Call(target, args, kwargs); } @@ -31,6 +38,7 @@ bool PyTimerCallable::hasElapsed(int now) void PyTimerCallable::call(int now) { + std::cout << "PyTimerCallable called. (" << (target == NULL) << ")" << std::endl; PyObject* args = Py_BuildValue("(i)", now); PyObject* retval = PyCallable::call(args, NULL); if (!retval) @@ -47,6 +55,7 @@ void PyTimerCallable::call(int now) bool PyTimerCallable::test(int now) { + std::cout << "PyTimerCallable tested. (" << (target == NULL) << ")" << interval << " " << last_ran << std::endl; if(hasElapsed(now)) { call(now); diff --git a/src/scripts/game.py b/src/scripts/game.py index 15cc524..5684bc2 100644 --- a/src/scripts/game.py +++ b/src/scripts/game.py @@ -9,7 +9,7 @@ texture_hot = mcrfpy.Texture("assets/kenney_lava.png", 16, 12, 11) # Test stuff mcrfpy.createScene("boom") -#mcrfpy.setScene("boom") +mcrfpy.setScene("boom") ui = mcrfpy.sceneUI("boom") box = mcrfpy.Frame(40, 60, 200, 300, fill_color=(255,128,0), outline=4.0, outline_color=(64,64,255,96)) ui.append(box) @@ -36,10 +36,90 @@ box.children.append(sprite) box.children.append(spritecap) box.click = click_sprite +f_a = mcrfpy.Frame(250, 60, 80, 80, fill_color=(255, 92, 92)) +f_a_txt = mcrfpy.Caption(5, 5, "0", font) + +f_b = mcrfpy.Frame(340, 60, 80, 80, fill_color=(92, 255, 92)) +f_b_txt = mcrfpy.Caption(5, 5, "0", font) + +f_c = mcrfpy.Frame(430, 60, 80, 80, fill_color=(92, 92, 255)) +f_c_txt = mcrfpy.Caption(5, 5, "0", font) + + +ui.append(f_a) +f_a.children.append(f_a_txt) +ui.append(f_b) +f_b.children.append(f_b_txt) +ui.append(f_c) +f_c.children.append(f_c_txt) + +import sys +def ding(*args): + f_a_txt.text = str(sys.getrefcount(ding)) + " refs" + f_b_txt.text = sys.getrefcount(dong) + f_c_txt.text = sys.getrefcount(stress_test) + +def dong(*args): + f_a_txt.text = str(sys.getrefcount(ding)) + " refs" + f_b_txt.text = sys.getrefcount(dong) + f_c_txt.text = sys.getrefcount(stress_test) + +running = False +timers = [] + +def add_ding(): + global timers + n = len(timers) + mcrfpy.setTimer(f"timer{n}", ding, 100) + print("+1 ding:", timers) + +def add_dong(): + global timers + n = len(timers) + mcrfpy.setTimer(f"timer{n}", dong, 100) + print("+1 dong:", timers) + +def remove_random(): + global timers + target = random.choice(timers) + print("-1 timer:", target) + print("remove from list") + timers.remove(target) + print("delTimer") + mcrfpy.delTimer(target) + print("done") + +import random +def stress_test(*args): + global running + global timers + if not running: + print("stress test initial") + running = True + timers.append("recurse") + add_ding() + add_dong() + mcrfpy.setTimer("recurse", stress_test, 1000) + mcrfpy.setTimer("terminate", lambda *args: mcrfpy.delTimer("recurse"), 30000) + ding(); dong() + else: + #print("stress test random activity") + #random.choice([ + # add_ding, + # add_dong, + # remove_random + # ])() + #print(timers) + print("Segfaultin' time") + mcrfpy.delTimer("recurse") + +stress_test() + + # Loading Screen mcrfpy.createScene("loading") ui = mcrfpy.sceneUI("loading") -mcrfpy.setScene("loading") +#mcrfpy.setScene("loading") logo_texture = mcrfpy.Texture("assets/temp_logo.png", 1024, 1, 1) logo_sprite = mcrfpy.Sprite(50, 50, logo_texture, 0, 0.5) ui.append(logo_sprite) From c9d5251c71bb994de365801beb453fb61922da31 Mon Sep 17 00:00:00 2001 From: John McCardle Date: Fri, 15 Mar 2024 20:00:57 -0400 Subject: [PATCH 4/5] In-place map modification worked --- src/GameEngine.cpp | 16 +++++++++++++--- src/PyCallable.cpp | 5 +++++ src/PyCallable.h | 2 ++ src/scripts/game.py | 5 +++++ 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/GameEngine.cpp b/src/GameEngine.cpp index 74ed443..6c3d95d 100644 --- a/src/GameEngine.cpp +++ b/src/GameEngine.cpp @@ -77,7 +77,8 @@ void GameEngine::manageTimer(std::string name, PyObject* target, int interval) { //Py_DECREF(timers[name].target); std::cout << "Erasing a timer" << std::endl; - timers.erase(it); + //timers.erase(it); + timers[name] = std::make_shared(Py_None, 1000, runtime.getElapsedTime().asMilliseconds()); std::cout << "It was erased" << std::endl; return; } @@ -94,9 +95,18 @@ void GameEngine::manageTimer(std::string name, PyObject* target, int interval) void GameEngine::testTimers() { int now = runtime.getElapsedTime().asMilliseconds(); - for (auto& [name, timer]: timers) + //for (auto& [name, timer]: timers) + auto it = timers.begin(); + while (it != timers.end()) { - timer->test(now); + it->second->test(now); + + if (it->second->isNone()) + { + it = timers.erase(it); + } + else + it++; } } diff --git a/src/PyCallable.cpp b/src/PyCallable.cpp index dee7884..2aca5c2 100644 --- a/src/PyCallable.cpp +++ b/src/PyCallable.cpp @@ -23,6 +23,11 @@ PyObject* PyCallable::call(PyObject* args, PyObject* kwargs) return PyObject_Call(target, args, kwargs); } +bool PyCallable::isNone() +{ + return (target == Py_None || target == NULL); +} + PyTimerCallable::PyTimerCallable(PyObject* _target, int _interval, int now) : PyCallable(_target), interval(_interval), last_ran(now) {} diff --git a/src/PyCallable.h b/src/PyCallable.h index b59e774..ae828c7 100644 --- a/src/PyCallable.h +++ b/src/PyCallable.h @@ -9,6 +9,8 @@ protected: PyCallable(PyObject*); ~PyCallable(); PyObject* call(PyObject*, PyObject*); +public: + bool isNone(); }; class PyTimerCallable: public PyCallable diff --git a/src/scripts/game.py b/src/scripts/game.py index 5684bc2..18e46d0 100644 --- a/src/scripts/game.py +++ b/src/scripts/game.py @@ -90,6 +90,7 @@ def remove_random(): print("done") import random +import time def stress_test(*args): global running global timers @@ -112,6 +113,10 @@ def stress_test(*args): #print(timers) print("Segfaultin' time") mcrfpy.delTimer("recurse") + print("Does this still work?") + time.sleep(0.5) + print("How about now?") + stress_test() From 8f060dc87beadfa814b554ab23a5d187d6695c90 Mon Sep 17 00:00:00 2001 From: John McCardle Date: Fri, 15 Mar 2024 22:20:03 -0400 Subject: [PATCH 5/5] Removing std::cout debugging statements --- src/GameEngine.cpp | 11 +++-------- src/PyCallable.cpp | 11 +---------- 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/src/GameEngine.cpp b/src/GameEngine.cpp index 6c3d95d..d1d035f 100644 --- a/src/GameEngine.cpp +++ b/src/GameEngine.cpp @@ -69,17 +69,14 @@ void GameEngine::run() void GameEngine::manageTimer(std::string name, PyObject* target, int interval) { - //std::cout << "Manage timer called. " << name << " " << interval << std::endl; auto it = timers.find(name); if (it != timers.end()) // overwrite existing { - if (target == NULL || target == Py_None) // delete + if (target == NULL || target == Py_None) { - //Py_DECREF(timers[name].target); - std::cout << "Erasing a timer" << std::endl; - //timers.erase(it); + // Delete: Overwrite existing timer with one that calls None. This will be deleted in the next timer check + // see gitea issue #4: this allows for a timer to be deleted during its own call to itself timers[name] = std::make_shared(Py_None, 1000, runtime.getElapsedTime().asMilliseconds()); - std::cout << "It was erased" << std::endl; return; } } @@ -89,13 +86,11 @@ void GameEngine::manageTimer(std::string name, PyObject* target, int interval) return; } timers[name] = std::make_shared(target, interval, runtime.getElapsedTime().asMilliseconds()); - //Py_INCREF(target); } void GameEngine::testTimers() { int now = runtime.getElapsedTime().asMilliseconds(); - //for (auto& [name, timer]: timers) auto it = timers.begin(); while (it != timers.end()) { diff --git a/src/PyCallable.cpp b/src/PyCallable.cpp index 2aca5c2..6d44501 100644 --- a/src/PyCallable.cpp +++ b/src/PyCallable.cpp @@ -3,10 +3,6 @@ PyCallable::PyCallable(PyObject* _target) { target = Py_XNewRef(_target); - if (target) - { - //std::cout << PyUnicode_AsUTF8(PyObject_Repr(target)) << std::endl; - } } PyCallable::~PyCallable() @@ -17,9 +13,6 @@ PyCallable::~PyCallable() PyObject* PyCallable::call(PyObject* args, PyObject* kwargs) { - std::cout << "Calling object with args: "; - std::cout << PyUnicode_AsUTF8(PyObject_Repr(target)) << " <- "; - std::cout << PyUnicode_AsUTF8(PyObject_Repr(args)) << std::endl; return PyObject_Call(target, args, kwargs); } @@ -43,12 +36,10 @@ bool PyTimerCallable::hasElapsed(int now) void PyTimerCallable::call(int now) { - std::cout << "PyTimerCallable called. (" << (target == NULL) << ")" << std::endl; PyObject* args = Py_BuildValue("(i)", now); PyObject* retval = PyCallable::call(args, NULL); if (!retval) { - std::cout << "timer has raised an exception. It's going to STDERR and being dropped:" << std::endl; PyErr_Print(); PyErr_Clear(); } else if (retval != Py_None) @@ -60,7 +51,6 @@ void PyTimerCallable::call(int now) bool PyTimerCallable::test(int now) { - std::cout << "PyTimerCallable tested. (" << (target == NULL) << ")" << interval << " " << last_ran << std::endl; if(hasElapsed(now)) { call(now); @@ -109,6 +99,7 @@ PyKeyCallable::PyKeyCallable() void PyKeyCallable::call(std::string key, std::string action) { + if (target == Py_None || target == NULL) return; PyObject* args = Py_BuildValue("(ss)", key.c_str(), action.c_str()); PyObject* retval = PyCallable::call(args, NULL); if (!retval)