2 Commits

Author SHA1 Message Date
nick 529e51009c feat: hardcoded breakpoint 2025-11-06 15:57:45 +13:00
nick 2e958fa1ec feat: added debugger - untested 2025-11-06 15:51:47 +13:00
10 changed files with 62 additions and 73 deletions
+2 -1
View File
@@ -1,6 +1,7 @@
{ {
"files.associations": { "files.associations": {
"*.h": "c", "*.h": "c",
"xstring": "cpp" "xstring": "cpp",
"iostream": "cpp"
} }
} }
+14 -2
View File
@@ -25,8 +25,18 @@ target_include_directories(scriptstdstring
PUBLIC external/angelscript/sdk/angelscript/include PUBLIC external/angelscript/sdk/angelscript/include
) )
# ------------------------
# debugger add-on
# ------------------------
add_library(debugger STATIC
external/angelscript/sdk/add_on/debugger/debugger.cpp
)
target_include_directories(debugger PUBLIC
external/angelscript/sdk/angelscript/include
)
# ------------------------- # -------------------------
# 3️⃣.1️⃣ scriptbuilder add-on # scriptbuilder add-on
# ------------------------- # -------------------------
add_library(scriptbuilder STATIC add_library(scriptbuilder STATIC
external/angelscript/sdk/add_on/scriptbuilder/scriptbuilder.cpp external/angelscript/sdk/add_on/scriptbuilder/scriptbuilder.cpp
@@ -37,7 +47,7 @@ target_include_directories(scriptbuilder
) )
# ------------------------- # -------------------------
# 4️⃣ Main executable # Main executable
# ------------------------- # -------------------------
add_executable(simian add_executable(simian
main.cpp main.cpp
@@ -55,6 +65,7 @@ target_include_directories(simian PUBLIC
external/angelscript/sdk/angelscript/include external/angelscript/sdk/angelscript/include
external/angelscript/sdk/add_on/scriptstdstring external/angelscript/sdk/add_on/scriptstdstring
external/angelscript/sdk/add_on/scriptbuilder external/angelscript/sdk/add_on/scriptbuilder
external/angelscript/sdk/add_on/debugger
) )
target_link_libraries(simian target_link_libraries(simian
@@ -62,6 +73,7 @@ target_link_libraries(simian
angelscript angelscript
scriptstdstring scriptstdstring
scriptbuilder scriptbuilder
debugger
) )
# ------------------------- # -------------------------
+4 -10
View File
@@ -5,20 +5,14 @@
class HotReload { class HotReload {
public: public:
// Accept either a single script file or a directory to watch. If a directory is HotReload(const std::string& filename);
// provided, any change to any file in the directory will trigger a reload.
HotReload(const std::string& pathToWatch);
bool CheckForChanges(); bool CheckForChanges();
void UpdateLastWriteTime(); void UpdateLastWriteTime();
private: private:
std::string path; std::string scriptFile;
std::time_t lastWriteTime; std::time_t lastWriteTime;
std::time_t GetFileWriteTime(const std::string& filename); std::time_t GetFileWriteTime(const std::string& filename);
// Return the most recent write time found in the watched path. If `path` is a
// directory, this scans all files (non-recursive) and returns the newest
// modification time. If it's a file, it returns that file's write time.
std::time_t GetLatestWriteTimeInPath();
}; };
+4
View File
@@ -2,6 +2,7 @@
#include "angelscript.h" #include "angelscript.h"
#include "scriptbuilder.h" #include "scriptbuilder.h"
#include <string> #include <string>
#include "debugger.h"
class ScriptEngine { class ScriptEngine {
public: public:
@@ -17,6 +18,8 @@ public:
asIScriptEngine* GetEngine() const { return engine; } asIScriptEngine* GetEngine() const { return engine; }
asIScriptFunction* GetUpdateFunction() const { return updateFunc; } asIScriptFunction* GetUpdateFunction() const { return updateFunc; }
asIScriptFunction* GetDrawFunction() const { return drawFunc; } asIScriptFunction* GetDrawFunction() const { return drawFunc; }
void AddFileBreakPoint(const std::string& file, int line);
void AddFuncBreakPoint(const std::string& func);
private: private:
asIScriptEngine* engine; asIScriptEngine* engine;
@@ -24,6 +27,7 @@ private:
asIScriptFunction* drawFunc; asIScriptFunction* drawFunc;
asIScriptModule* currentModule; asIScriptModule* currentModule;
bool hasValidScript; bool hasValidScript;
CDebugger* debugger = nullptr;
static void MessageCallback(const asSMessageInfo* msg, void* param); static void MessageCallback(const asSMessageInfo* msg, void* param);
static int IncludeCallback(const char* include, const char* from, CScriptBuilder* builder, void* userParam); static int IncludeCallback(const char* include, const char* from, CScriptBuilder* builder, void* userParam);
+2 -2
View File
@@ -2,7 +2,7 @@ float x = 50;
float y = 100; float y = 100;
void Update(float dt) { void Update(float dt) {
x += 500 * dt; x += 50 * dt;
if (x > 800) { if (x > 800) {
x = 0; x = 0;
Print("X position reset!"); Print("X position reset!");
@@ -10,4 +10,4 @@ void Update(float dt) {
Log(LOG_WARNING, "Log WARNING: reset happened"); Log(LOG_WARNING, "Log WARNING: reset happened");
Log(LOG_ERROR, "Log ERROR: reset happened"); Log(LOG_ERROR, "Log ERROR: reset happened");
} }
}// }
+2 -9
View File
@@ -2,7 +2,6 @@
#include "raylib.h" #include "raylib.h"
#include <chrono> #include <chrono>
#include <thread> #include <thread>
#include <filesystem>
#include "log/log.h" #include "log/log.h"
const char* Application::WINDOW_TITLE = "Raylib + AngelScript"; const char* Application::WINDOW_TITLE = "Raylib + AngelScript";
const char* Application::SCRIPT_FILE = "scripts/test.as"; const char* Application::SCRIPT_FILE = "scripts/test.as";
@@ -29,14 +28,8 @@ bool Application::Initialize() {
return false; return false;
} }
// Initialize hot reload to watch the scripts directory so any script change // Initialize hot reload
// (not just the main file) triggers a reload. hotReload = new HotReload(SCRIPT_FILE);
{
std::filesystem::path p(SCRIPT_FILE);
std::string watchPath = p.parent_path().string();
if (watchPath.empty()) watchPath = ".";
hotReload = new HotReload(watchPath);
}
// Compile initial script // Compile initial script
scriptCompilationError = !scriptEngine.CompileScript(SCRIPT_FILE); scriptCompilationError = !scriptEngine.CompileScript(SCRIPT_FILE);
+5 -29
View File
@@ -3,21 +3,21 @@
namespace fs = std::filesystem; namespace fs = std::filesystem;
HotReload::HotReload(const std::string& pathToWatch) : path(pathToWatch), lastWriteTime(0) { HotReload::HotReload(const std::string& filename) : scriptFile(filename), lastWriteTime(0) {
UpdateLastWriteTime(); UpdateLastWriteTime();
} }
bool HotReload::CheckForChanges() { bool HotReload::CheckForChanges() {
std::time_t current = GetLatestWriteTimeInPath(); std::time_t currentWriteTime = GetFileWriteTime(scriptFile);
if (current != lastWriteTime) { if (currentWriteTime != lastWriteTime) {
lastWriteTime = current; lastWriteTime = currentWriteTime;
return true; return true;
} }
return false; return false;
} }
void HotReload::UpdateLastWriteTime() { void HotReload::UpdateLastWriteTime() {
lastWriteTime = GetLatestWriteTimeInPath(); lastWriteTime = GetFileWriteTime(scriptFile);
} }
std::time_t HotReload::GetFileWriteTime(const std::string& filename) { std::time_t HotReload::GetFileWriteTime(const std::string& filename) {
@@ -29,28 +29,4 @@ std::time_t HotReload::GetFileWriteTime(const std::string& filename) {
} catch (const std::exception&) { } catch (const std::exception&) {
return 0; return 0;
} }
}
std::time_t HotReload::GetLatestWriteTimeInPath() {
try {
fs::path p(path);
if (fs::is_regular_file(p)) {
return GetFileWriteTime(path);
}
// If it's a directory, scan files (non-recursively) and return the
// newest write time found.
if (fs::is_directory(p)) {
std::time_t newest = 0;
for (auto &entry : fs::directory_iterator(p)) {
if (!fs::is_regular_file(entry.path())) continue;
std::time_t t = GetFileWriteTime(entry.path().string());
if (t > newest) newest = t;
}
return newest;
}
} catch (const std::exception&) {
// fall through
}
return 0;
} }
+24
View File
@@ -22,6 +22,9 @@ bool ScriptEngine::Initialize() {
return false; return false;
} }
debugger = new CDebugger();
debugger->SetEngine(engine);
// Register std::string // Register std::string
RegisterStdString(engine); RegisterStdString(engine);
@@ -40,6 +43,11 @@ bool ScriptEngine::Initialize() {
void ScriptEngine::Shutdown() { void ScriptEngine::Shutdown() {
ClearCachedFunctions(); ClearCachedFunctions();
if (debugger) {
debugger->SetEngine(nullptr);
delete debugger;
debugger = nullptr;
}
if (engine) { if (engine) {
engine->ShutDownAndRelease(); engine->ShutDownAndRelease();
engine = nullptr; engine = nullptr;
@@ -110,6 +118,11 @@ bool ScriptEngine::CompileScript(const std::string& filename) {
updateFunc = currentModule->GetFunctionByName("Update"); updateFunc = currentModule->GetFunctionByName("Update");
drawFunc = currentModule->GetFunctionByName("Draw"); drawFunc = currentModule->GetFunctionByName("Draw");
hasValidScript = true; hasValidScript = true;
// If debugger is present, add a function breakpoint on Update to stop when Update is entered
// The debugger will transform function breakpoints into file breakpoints when the function is entered
if (debugger && updateFunc) {
debugger->AddFuncBreakPoint("Update");
}
log_info("Script compiled and cached: %s", filename.c_str()); log_info("Script compiled and cached: %s", filename.c_str());
return true; return true;
} }
@@ -120,6 +133,10 @@ void ScriptEngine::CallScriptFunction(asIScriptFunction* func, float dt) {
asIScriptContext* ctx = engine->CreateContext(); asIScriptContext* ctx = engine->CreateContext();
if (!ctx) return; if (!ctx) return;
if (debugger) {
ctx->SetLineCallback(asMETHOD(CDebugger, LineCallback), debugger, asCALL_THISCALL);
}
int r = ctx->Prepare(func); int r = ctx->Prepare(func);
if (r < 0) { if (r < 0) {
ctx->Release(); ctx->Release();
@@ -152,6 +169,13 @@ void ScriptEngine::GarbageCollect() {
} }
} }
void ScriptEngine::AddFileBreakPoint(const std::string &file, int line) {
if (debugger) debugger->AddFileBreakPoint(file, line);
}
void ScriptEngine::AddFuncBreakPoint(const std::string &func) {
if (debugger) debugger->AddFuncBreakPoint(func);
}
void ScriptEngine::MessageCallback(const asSMessageInfo* msg, void*) { void ScriptEngine::MessageCallback(const asSMessageInfo* msg, void*) {
// Map AngelScript message types to log levels // Map AngelScript message types to log levels
if (msg->type == asMSGTYPE_ERROR) { if (msg->type == asMSGTYPE_ERROR) {
+5 -18
View File
@@ -21,7 +21,6 @@
*/ */
#include "log.h" #include "log.h"
#include <string.h> // for memcpy
#define MAX_CALLBACKS 32 #define MAX_CALLBACKS 32
@@ -188,14 +187,6 @@ static void init_event(log_Event *ev, void *udata)
} }
void log_log(int level, const char *file, int line, const char *fmt, ...) void log_log(int level, const char *file, int line, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
log_vlog(level, file, line, fmt, ap);
va_end(ap);
}
void log_vlog(int level, const char *file, int line, const char *fmt, va_list ap)
{ {
log_Event ev = { log_Event ev = {
.fmt = fmt, .fmt = fmt,
@@ -209,11 +200,9 @@ void log_vlog(int level, const char *file, int line, const char *fmt, va_list ap
if (!L.quiet && level >= L.level) if (!L.quiet && level >= L.level)
{ {
init_event(&ev, stderr); init_event(&ev, stderr);
va_list ap_copy; va_start(ev.ap, fmt);
va_copy(ap_copy, ap);
memcpy(&ev.ap, &ap_copy, sizeof(va_list));
stdout_callback(&ev); stdout_callback(&ev);
va_end(ap_copy); va_end(ev.ap);
} }
for (int i = 0; i < MAX_CALLBACKS && L.callbacks[i].fn; i++) for (int i = 0; i < MAX_CALLBACKS && L.callbacks[i].fn; i++)
@@ -222,11 +211,9 @@ void log_vlog(int level, const char *file, int line, const char *fmt, va_list ap
if (level >= cb->level) if (level >= cb->level)
{ {
init_event(&ev, cb->udata); init_event(&ev, cb->udata);
va_list ap_copy; va_start(ev.ap, fmt);
va_copy(ap_copy, ap);
memcpy(&ev.ap, &ap_copy, sizeof(va_list));
cb->fn(&ev); cb->fn(&ev);
va_end(ap_copy); va_end(ev.ap);
} }
} }
@@ -235,5 +222,5 @@ void log_vlog(int level, const char *file, int line, const char *fmt, va_list ap
void raylib_log(int msgType, const char *text, va_list args) void raylib_log(int msgType, const char *text, va_list args)
{ {
log_vlog(msgType, NULL, 0, text, args); log_log(msgType, NULL, 0, text, args);
} }
-2
View File
@@ -48,8 +48,6 @@ int log_add_callback(log_LogFn fn, void *udata, int level);
int log_add_fp(FILE *fp, int level); int log_add_fp(FILE *fp, int level);
void log_log(int level, const char *file, int line, const char *fmt, ...); void log_log(int level, const char *file, int line, const char *fmt, ...);
// Variant that accepts a va_list for backends that already receive va_list
void log_vlog(int level, const char *file, int line, const char *fmt, va_list ap);
void raylib_log(int msgType, const char *text, va_list args); void raylib_log(int msgType, const char *text, va_list args);