Modernize Crypt of Sokoban demo game and fix timer segfault

Game script updates (src/scripts/):
- Migrate Sound/Music API: createSoundBuffer() -> Sound() objects
- Migrate Scene API: sceneUI("name") -> scene.children
- Migrate Timer API: setTimer/delTimer -> Timer objects with stop()
- Fix callback signatures: (x,y,btn,event) -> (pos,btn,action) with Vector
- Fix grid_size unpacking: now returns Vector, use .x/.y with int()

Segfault fix (src/PyTimer.cpp):
- Remove direct map erase in PyTimer::stop() that caused iterator
  invalidation when timer.stop() was called from within a callback
- Now just marks timer as stopped; testTimers() handles safe removal

The game now starts and runs without crashes.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
John McCardle 2026-01-21 23:47:46 -05:00
commit c23da11d7d
4 changed files with 61 additions and 48 deletions

View file

@ -156,14 +156,10 @@ PyObject* PyTimer::stop(PyTimerObject* self, PyObject* Py_UNUSED(ignored)) {
return nullptr;
}
// Remove from game engine map (but preserve the Timer data!)
if (Resources::game && !self->name.empty()) {
auto it = Resources::game->timers.find(self->name);
if (it != Resources::game->timers.end() && it->second == self->data) {
Resources::game->timers.erase(it);
}
}
// Just mark as stopped - do NOT erase from map here!
// Removing from the map during iteration (e.g., from a timer callback)
// would invalidate iterators in testTimers(). The stopped flag tells
// testTimers() to safely remove this timer on its next pass.
self->data->stop();
// NOTE: We do NOT reset self->data here - the timer can be restarted
Py_RETURN_NONE;