fix: resolve crashes on invalid script hot reload

This commit is contained in:
2025-11-05 23:36:44 +13:00
parent 29d52c4578
commit 28407e9ebd
5 changed files with 86 additions and 23 deletions
+67 -16
View File
@@ -6,7 +6,7 @@
#include <sstream>
#include <assert.h>
ScriptEngine::ScriptEngine() : engine(nullptr), updateFunc(nullptr), drawFunc(nullptr) {
ScriptEngine::ScriptEngine() : engine(nullptr), updateFunc(nullptr), drawFunc(nullptr), currentModule(nullptr), hasValidScript(false) {
}
ScriptEngine::~ScriptEngine() {
@@ -37,54 +37,105 @@ bool ScriptEngine::Initialize() {
}
void ScriptEngine::Shutdown() {
ClearCachedFunctions();
if (engine) {
engine->ShutDownAndRelease();
engine = nullptr;
}
updateFunc = nullptr;
drawFunc = nullptr;
currentModule = nullptr;
hasValidScript = false;
}
bool ScriptEngine::CompileScript(const std::string& filename) {
if (!engine) return false;
engine->GarbageCollect();
asIScriptModule* mod = engine->GetModule("main", asGM_ALWAYS_CREATE);
std::string code = ReadFile(filename);
if (code.empty()) {
std::cerr << "Failed to read script file: " << filename << "\n";
return false;
}
int r = mod->AddScriptSection(filename.c_str(), code.c_str());
if (r < 0) return false;
// Try to compile in a temporary module first
asIScriptModule* tempMod = engine->GetModule("temp", asGM_ALWAYS_CREATE);
int r = tempMod->AddScriptSection(filename.c_str(), code.c_str());
if (r < 0) {
std::cerr << "Failed to add script section for: " << filename << "\n";
engine->DiscardModule("temp");
return false;
}
r = mod->Build();
if (r < 0) return false;
r = tempMod->Build();
if (r < 0) {
std::cerr << "Failed to build script: " << filename << "\n";
engine->DiscardModule("temp");
return false;
}
// Cache Update(float dt) and Draw() functions if they exist
updateFunc = mod->GetFunctionByName("Update");
drawFunc = mod->GetFunctionByName("Draw");
// If we get here, compilation succeeded
// Now safely replace the main module
if (currentModule) {
engine->DiscardModule("main");
}
// Create new main module with the working code
currentModule = engine->GetModule("main", asGM_ALWAYS_CREATE);
r = currentModule->AddScriptSection(filename.c_str(), code.c_str());
if (r >= 0) {
r = currentModule->Build();
}
// Clean up temp module
engine->DiscardModule("temp");
if (r < 0) {
// This shouldn't happen since we already tested compilation
std::cerr << "Unexpected error when creating main module\n";
ClearCachedFunctions();
return false;
}
// Cache new functions
updateFunc = currentModule->GetFunctionByName("Update");
drawFunc = currentModule->GetFunctionByName("Draw");
hasValidScript = true;
std::cout << "Script compiled and cached: " << filename << "\n";
return true;
}
void ScriptEngine::CallScriptFunction(asIScriptFunction* func, float dt) {
if (!func || !engine) return;
if (!func || !engine || !hasValidScript) return;
asIScriptContext* ctx = engine->CreateContext();
ctx->Prepare(func);
if (!ctx) return;
int r = ctx->Prepare(func);
if (r < 0) {
ctx->Release();
return;
}
if (func->GetParamCount() == 1) {
ctx->SetArgFloat(0, dt);
}
ctx->Execute();
r = ctx->Execute();
if (r != asEXECUTION_FINISHED) {
if (r == asEXECUTION_EXCEPTION) {
std::cerr << "Script exception: " << ctx->GetExceptionString() << "\n";
}
}
ctx->Release();
}
void ScriptEngine::ClearCachedFunctions() {
updateFunc = nullptr;
drawFunc = nullptr;
hasValidScript = false;
}
void ScriptEngine::GarbageCollect() {
if (engine) {
engine->GarbageCollect();