|
|
@@ -2,10 +2,12 @@
|
|
|
#include <pybind11/embed.h>
|
|
|
#include <filesystem>
|
|
|
#include <iostream>
|
|
|
-#include "../Engine.h"
|
|
|
#include <SDL2/SDL_log.h>
|
|
|
+#include <thread>
|
|
|
+#include <chrono>
|
|
|
|
|
|
namespace py = pybind11;
|
|
|
+namespace fs = std::filesystem;
|
|
|
|
|
|
PythonEngine& PythonEngine::Get()
|
|
|
{
|
|
|
@@ -25,12 +27,15 @@ void PythonEngine::Init()
|
|
|
|
|
|
try
|
|
|
{
|
|
|
- py::module_::import("sys").attr("path").attr("append")("assets/scripts");
|
|
|
- SDL_Log("[Python] scripts/ path added");
|
|
|
+ auto sys = py::module_::import("sys");
|
|
|
+ sys.attr("path").attr("append")(".");
|
|
|
+ sys.attr("path").attr("append")("scripts");
|
|
|
+ sys.attr("path").attr("append")("assets/scripts");
|
|
|
+ SDL_Log("[Python] scripts paths added");
|
|
|
}
|
|
|
- catch (const py::error_already_set& e)
|
|
|
+ catch (...)
|
|
|
{
|
|
|
- SDL_Log("[Python] Error setting sys.path: %s", e.what());
|
|
|
+ SDL_Log("[Python] Warning: Failed to update sys.path ");
|
|
|
}
|
|
|
|
|
|
// Release GIL immidiately so game loop can run freely
|
|
|
@@ -44,6 +49,8 @@ void PythonEngine::Shutdown()
|
|
|
{
|
|
|
if (initialized_) { return; }
|
|
|
|
|
|
+ StopAutoReload();
|
|
|
+
|
|
|
PyEval_RestoreThread(PyGILState_GetThisThreadState());
|
|
|
py::finalize_interpreter();
|
|
|
initialized_ = false;
|
|
|
@@ -65,4 +72,75 @@ void PythonEngine::Exec(const std::string& code)
|
|
|
{
|
|
|
SDL_Log("[Python] Error: %s", e.what());
|
|
|
}
|
|
|
+}
|
|
|
+
|
|
|
+// Reload single module
|
|
|
+bool PythonEngine::ReloadModule(const std::string& module_name)
|
|
|
+{
|
|
|
+ py::gil_scoped_acquire gil;
|
|
|
+ try
|
|
|
+ {
|
|
|
+ auto importlib = py::module_::import("importlib");
|
|
|
+ auto sys = py::module_::import("sys");
|
|
|
+
|
|
|
+ if (py::hasattr(sys, "modules") && py::hasattr(sys.attr("modules"), module_name.c_str()))
|
|
|
+ {
|
|
|
+ auto module = sys.attr("modules")[module_name.c_str()];
|
|
|
+ importlib.attr("reload")(module);
|
|
|
+ SDL_Log("[Python] Reloaded module: %s", module_name.c_str());
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ else // First-time import
|
|
|
+ {
|
|
|
+ py::module_::import(module_name.c_str());
|
|
|
+ SDL_Log("[Python] Imported module: %s", module_name.c_str());
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (const py::error_already_set& e)
|
|
|
+ {
|
|
|
+ SDL_Log("[Python] Reload module %s error: %s", module_name.c_str(), e.what());
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void PythonEngine::ReloadAllScripts()
|
|
|
+{
|
|
|
+ SDL_Log("[Python] Reloading all scripts");
|
|
|
+
|
|
|
+ // ReloadModule("main_menu");
|
|
|
+ // ReloadModule("player_controller");
|
|
|
+ // ...
|
|
|
+}
|
|
|
+
|
|
|
+void PythonEngine::StartAutoReload(float intervalSeconds)
|
|
|
+{
|
|
|
+ if (autoReloadRunning_) { return; }
|
|
|
+
|
|
|
+ autoReloadRunning_ = true;
|
|
|
+ autoReloadThread_ = std::thread([this, interval = std::chrono::milliseconds(static_cast<int>(intervalSeconds * 1000))]() {
|
|
|
+ while (autoReloadRunning_)
|
|
|
+ {
|
|
|
+ std::this_thread::sleep_for(interval);
|
|
|
+
|
|
|
+ py::gil_scoped_acquire gil;
|
|
|
+ try
|
|
|
+ {
|
|
|
+ ReloadAllScripts();
|
|
|
+ }
|
|
|
+ catch (...)
|
|
|
+ {
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+void PythonEngine::StopAutoReload()
|
|
|
+{
|
|
|
+ if (!autoReloadRunning_) { return; }
|
|
|
+ autoReloadRunning_ = false;
|
|
|
+ if (autoReloadThread_.joinable())
|
|
|
+ {
|
|
|
+ autoReloadThread_.join();
|
|
|
+ }
|
|
|
}
|