载入中...
搜索中...
未找到
ConsolePanel.cpp
浏览该文件的文档.
3
5
6#include <simplesquirrel/simplesquirrel.hpp>
7#include <squirrel.h>
8
9#include <cstdarg>
10#include <cstdio>
11#include <chrono>
12#include <ctime>
13#include <sstream>
14#include <utility>
15
16namespace eve::dev {
17namespace {
18
19ConsolePanel::ImGuiDrawer g_imguiDrawer = nullptr;
20
21// Original Squirrel print/error callbacks (installed by the VM / std lib).
22SQPRINTFUNCTION g_prevPrint = nullptr;
23SQPRINTFUNCTION g_prevError = nullptr;
24
25void forwardPrint(HSQUIRRELVM v, const SQChar* text) {
26 if (g_prevPrint) {
27 g_prevPrint(v, "%s", text ? text : "");
28 } else {
29 std::fputs(text ? text : "", stdout);
30 }
31}
32
33void forwardError(HSQUIRRELVM v, const SQChar* text) {
34 if (g_prevError) {
35 g_prevError(v, "%s", text ? text : "");
36 } else {
37 std::fputs(text ? text : "", stderr);
38 }
39}
40
41// Squirrel print callback (varargs, printf-style). Captured into the console
42// log, then forwarded to the previous handler so stdout/stderr behavior stays.
43void capturePrint(HSQUIRRELVM v, const SQChar* s, ...) {
44 va_list args;
45 va_start(args, s);
46 char buf[1024];
47 vsnprintf(buf, sizeof(buf), s ? s : "", args);
48 va_end(args);
49 ConsolePanel::instance().addLog("print", buf);
50 forwardPrint(v, buf);
51}
52
53void captureError(HSQUIRRELVM v, const SQChar* s, ...) {
54 va_list args;
55 va_start(args, s);
56 char buf[1024];
57 vsnprintf(buf, sizeof(buf), s ? s : "", args);
58 va_end(args);
59 ConsolePanel::instance().addLog("error", buf);
60 forwardError(v, buf);
61}
62
63std::string typeName(HSQUIRRELVM vm, SQInteger idx) {
64 switch (sq_gettype(vm, idx)) {
65 case OT_NULL: return "null";
66 case OT_INTEGER: return "integer";
67 case OT_FLOAT: return "float";
68 case OT_BOOL: return "bool";
69 case OT_STRING: return "string";
70 case OT_TABLE: return "table";
71 case OT_ARRAY: return "array";
72 case OT_CLOSURE: return "closure";
73 case OT_NATIVECLOSURE: return "native";
74 case OT_USERDATA: return "userdata";
75 case OT_CLASS: return "class";
76 case OT_INSTANCE: return "instance";
77 case OT_THREAD: return "thread";
78 case OT_GENERATOR: return "generator";
79 case OT_WEAKREF: return "weakref";
80 default: return "other";
81 }
82}
83
84std::string formatValue(HSQUIRRELVM vm, SQInteger idx) {
85 switch (sq_gettype(vm, idx)) {
86 case OT_NULL: return "null";
87 case OT_BOOL: {
88 SQBool b = SQFalse;
89 sq_getbool(vm, idx, &b);
90 return b ? "true" : "false";
91 }
92 case OT_INTEGER: {
93 SQInteger v = 0;
94 sq_getinteger(vm, idx, &v);
95 return std::to_string(static_cast<long long>(v));
96 }
97 case OT_FLOAT: {
98 SQFloat v = 0;
99 sq_getfloat(vm, idx, &v);
100 std::ostringstream oss;
101 oss << static_cast<double>(v);
102 return oss.str();
103 }
104 case OT_STRING: {
105 const SQChar* s = nullptr;
106 sq_getstring(vm, idx, &s);
107 return std::string("\"") + (s ? s : "") + "\"";
108 }
109 case OT_TABLE: return "<table>";
110 case OT_ARRAY: return "<array>";
111 case OT_CLOSURE: return "<closure>";
112 case OT_NATIVECLOSURE: return "<native>";
113 case OT_USERDATA: return "<userdata>";
114 case OT_INSTANCE: return "<instance>";
115 case OT_CLASS: return "<class>";
116 case OT_THREAD: return "<thread>";
117 default: return "<" + typeName(vm, idx) + ">";
118 }
119}
120
121} // namespace
122
124 // Process-immortal singleton; see devtools/Immortal.hpp.
126}
127
128std::string ConsolePanel::nowStamp() {
129 using clock = std::chrono::system_clock;
130 const auto t = clock::to_time_t(clock::now());
131 std::tm tm{};
132#if defined(_WIN32)
133 localtime_s(&tm, &t);
134#else
135 localtime_r(&t, &tm);
136#endif
137 char buf[32];
138 std::strftime(buf, sizeof(buf), "%H:%M:%S", &tm);
139 return buf;
140}
141
143 std::lock_guard<std::mutex> lock(mu_);
144 visible_ = on;
145}
146
148 std::lock_guard<std::mutex> lock(mu_);
149 return visible_;
150}
151
153
154void ConsolePanel::addLog(std::string level, std::string text) {
155 std::lock_guard<std::mutex> lock(mu_);
157 line.timestamp = nowStamp();
158 line.level = std::move(level);
159 line.text = std::move(text);
160 log_.push_back(std::move(line));
161 while (log_.size() > maxEntries_) log_.pop_front();
162}
163
164void ConsolePanel::addInfo(std::string text) { addLog("info", std::move(text)); }
165
166void ConsolePanel::addWarn(std::string text) { addLog("warn", std::move(text)); }
167
168void ConsolePanel::addError(std::string text) { addLog("error", std::move(text)); }
169
171 std::lock_guard<std::mutex> lock(mu_);
172 log_.clear();
173}
174
176 std::lock_guard<std::mutex> lock(mu_);
177 maxEntries_ = n == 0 ? 1 : n;
178 while (log_.size() > maxEntries_) log_.pop_front();
179}
180
181std::vector<ConsoleLine> ConsolePanel::recent(size_t max) const {
182 std::lock_guard<std::mutex> lock(mu_);
183 std::vector<ConsoleLine> out;
184 if (log_.empty() || max == 0) return out;
185 const size_t start = log_.size() > max ? log_.size() - max : 0;
186 out.assign(log_.begin() + static_cast<std::ptrdiff_t>(start), log_.end());
187 return out;
188}
189
190std::string ConsolePanel::format(size_t max) const {
191 auto lines = recent(max);
192 std::ostringstream oss;
193 for (const auto& l : lines) {
194 oss << '[' << l.timestamp << "] " << l.level << " | " << l.text << '\n';
195 }
196 return oss.str();
197}
198
200 if (!vm) return;
201 detach();
202 vm_ = vm;
203 // Snapshot existing handlers once (they may be the stdlib defaults).
204 if (!g_prevPrint) g_prevPrint = sq_getprintfunc(vm);
205 if (!g_prevError) g_prevError = sq_geterrorfunc(vm);
206 sq_setprintfunc(vm, capturePrint, captureError);
207 addLog("info", "console attached to VM");
208}
209
211 if (!vm_) return;
212 sq_setprintfunc(vm_, g_prevPrint, g_prevError);
213 vm_ = nullptr;
214 addLog("info", "console detached from VM");
215}
216
217std::string ConsolePanel::eval(const std::string& expression) {
218 if (expression.empty()) return "error: empty expression";
219 HSQUIRRELVM vm = vm_;
220 if (!vm) return "error: no VM attached";
221 addLog("cmd", expression);
222
223 const SQInteger top = sq_gettop(vm);
224 // Compile `return (expr);` so the evaluation result lands on the stack.
225 const std::string source = "return (" + expression + ");";
226 if (SQ_FAILED(sq_compilebuffer(vm, source.c_str(), static_cast<SQInteger>(source.size()),
227 _SC("console_repl.nut"), SQTrue))) {
228 sq_settop(vm, top);
230 const std::string err = "error: " +
231 (ctx.empty() ? std::string("compile failed")
233 addLog("error", err);
234 return err;
235 }
236 sq_pushroottable(vm);
237 if (SQ_FAILED(sq_call(vm, 1, SQTrue, SQTrue))) {
238 sq_settop(vm, top);
240 const std::string err = "error: " +
241 (ctx.empty() ? std::string("runtime failed")
243 addLog("error", err);
244 return err;
245 }
246 std::string result = formatValue(vm, -1);
247 sq_settop(vm, top);
248 addLog("result", result);
249 return result;
250}
251
253
255 // ImGui UI lives in eve_imgui (ImGuiBackend) so EVDevTools does not
256 // instantiate imgui.h inlines — that blew the MSVC 65535 export limit.
257 if (g_imguiDrawer) g_imguiDrawer(*this);
258}
259
260} // namespace eve::dev
struct SQVM * HSQUIRRELVM
int line
HSQUIRRELVM vm
Definition ECS.cpp:20
glm::vec3 n
Definition Grass.cpp:64
uint32_t b
int idx
SettlementPipeline::Stage fn
int v
uint32_t s
Definition Weather.cpp:28
In-engine runtime console / log ring buffer for DevTools.
std::vector< ConsoleLine > recent(size_t max=128) const
static void setImGuiDrawer(ImGuiDrawer fn)
void addWarn(std::string text)
void addLog(std::string level, std::string text)
Append a leveled log line (thread-safe).
void(*)(ConsolePanel &panel) ImGuiDrawer
static ConsolePanel & instance()
std::string eval(const std::string &expression)
Evaluate a Squirrel expression against the root table and return a formatted result (or error message...
void addError(std::string text)
void setMaxEntries(size_t n)
void addInfo(std::string text)
void attach(HSQUIRRELVM vm)
Attach to a Squirrel VM: capture print/script errors into the log.
std::string format(size_t max=128) const
void drawImGui()
Optional ImGui draw hook. Default no-op: console UI is registered by the host (see setImGuiDrawer) so...
ScriptErrorContext captureCompileError(HSQUIRRELVM vm)
Captures the last compilation error recorded by the VM.
std::string formatScriptError(const ScriptErrorContext &ctx)
Formats a context into a human-readable multi-line report.
ScriptErrorContext takeLastScriptError(HSQUIRRELVM vm)
Consumes and clears the last recorded error for a VM.
static T & get()
Returns the process-lifetime instance.
Definition Immortal.hpp:21
Structured snapshot of a script error: message, throw site and stack.
Definition ScriptError.h:25
bool empty() const noexcept
True when no error payload was captured.
Definition ScriptError.h:36