载入中...
搜索中...
未找到
SoftRestart.cpp
浏览该文件的文档.
2
3#include "common/Capability.h"
5#include "common/Resource.h"
9
10#include <Poco/Dynamic/Var.h>
11#include <Poco/JSON/Array.h>
12#include <Poco/JSON/Object.h>
13
14#include <squirrel.h>
15
16#include <string>
17
18namespace eve::dev {
19namespace {
20
21constexpr int kMaxArgumentDepth = 16;
22
23void pushJsonValue(HSQUIRRELVM vm, const Poco::Dynamic::Var& value, int depth) {
24 if (depth > kMaxArgumentDepth || value.isEmpty()) {
25 sq_pushnull(vm);
26 return;
27 }
28 if (value.isBoolean()) {
29 sq_pushbool(vm, value.convert<bool>() ? SQTrue : SQFalse);
30 return;
31 }
32 if (value.isInteger()) {
33 sq_pushinteger(vm, static_cast<SQInteger>(value.convert<Poco::Int64>()));
34 return;
35 }
36 if (value.isNumeric()) {
37 sq_pushfloat(vm, static_cast<SQFloat>(value.convert<double>()));
38 return;
39 }
40 if (value.isString()) {
41 const std::string text = value.convert<std::string>();
42 sq_pushstring(vm, text.c_str(), static_cast<SQInteger>(text.size()));
43 return;
44 }
45 if (value.type() == typeid(Poco::JSON::Array::Ptr)) {
46 auto array = value.extract<Poco::JSON::Array::Ptr>();
47 sq_newarray(vm, 0);
48 const SQInteger target = sq_gettop(vm);
49 for (unsigned index = 0; index < array->size(); ++index) {
50 pushJsonValue(vm, array->get(index), depth + 1);
51 sq_arrayappend(vm, target);
52 }
53 return;
54 }
55 if (value.type() == typeid(Poco::JSON::Object::Ptr)) {
56 auto object = value.extract<Poco::JSON::Object::Ptr>();
57 sq_newtable(vm);
58 const SQInteger target = sq_gettop(vm);
59 for (const auto& entry : *object) {
60 sq_pushstring(vm, entry.first.c_str(), static_cast<SQInteger>(entry.first.size()));
61 pushJsonValue(vm, entry.second, depth + 1);
62 sq_newslot(vm, target, SQFalse);
63 }
64 return;
65 }
66 sq_pushnull(vm);
67}
68
69std::string lastVmError(HSQUIRRELVM vm) {
71 if (!ctx.empty()) return eve::script::formatScriptError(ctx);
72 return "soft restart failed";
73}
74
76Result<void> installRestartArgs(HSQUIRRELVM vm, Poco::JSON::Object::Ptr args) {
77 const SQInteger top = sq_gettop(vm);
78 sq_pushroottable(vm);
79 sq_pushstring(vm, _SC("eve"), -1);
80 if (SQ_FAILED(sq_get(vm, -2)) || sq_gettype(vm, -1) != OT_TABLE) {
81 sq_settop(vm, top);
83 Diagnostic::error(DiagnosticCode::NotFound, "eve table missing; is load.nut running?", "eve"));
84 }
85 sq_pushstring(vm, _SC("restartArgs"), -1);
86 if (args)
87 pushJsonValue(vm, Poco::Dynamic::Var(args), 0);
88 else
89 sq_newtable(vm);
90 if (SQ_FAILED(sq_newslot(vm, -3, SQFalse))) {
91 sq_settop(vm, top);
93 Diagnostic::error(DiagnosticCode::Failed, "failed to set eve.restartArgs", "eve.restartArgs"));
94 }
95 sq_settop(vm, top);
96 return Result<void>::success();
97}
98
100Result<bool> callSoftRestartGame(HSQUIRRELVM vm, bool reloadScripts) {
101 const SQInteger top = sq_gettop(vm);
102 sq_pushroottable(vm);
103 sq_pushstring(vm, _SC("soft_restart_game"), -1);
104 if (SQ_FAILED(sq_get(vm, -2)) || sq_gettype(vm, -1) != OT_CLOSURE) {
105 sq_settop(vm, top);
108 "soft_restart_game is missing; start the game with load.nut (eve run --mcp-port ...)",
109 "soft_restart_game"));
110 }
111 sq_pushroottable(vm); // this
112 sq_pushbool(vm, reloadScripts ? SQTrue : SQFalse);
113 if (SQ_FAILED(sq_call(vm, 2, SQTrue, SQTrue))) {
114 const std::string err = lastVmError(vm);
115 sq_settop(vm, top);
117 Diagnostic::error(DiagnosticCode::Failed, err, "soft_restart_game"));
118 }
119 SQBool ok = SQFalse;
120 if (sq_gettype(vm, -1) == OT_BOOL) sq_getbool(vm, -1, &ok);
121 sq_settop(vm, top);
122 if (!ok) {
124 DiagnosticCode::Failed, "soft_restart_game returned false (see console / eve_console_read)",
125 "soft_restart_game"));
126 }
127 return Result<bool>::success(true);
128}
129
130} // namespace
131
133 std::uint32_t ok = 0;
134 eve::cap::forEach<eve::caps::IStateProvider>([&](eve::caps::IStateProvider* provider) {
135 if (provider && provider->resetToDefaults()) ++ok;
136 });
137 return ok;
138}
139
141
143 if (!vm) {
145 Diagnostic::error(DiagnosticCode::NotFound, "no VM attached", "vm"));
146 }
149 DiagnosticCode::Conflict, "a state hot-reload session is already active", "ReloadSession"));
150 }
151
152 SoftRestartReport report;
154 report.args = request.args ? request.args : Poco::JSON::Object::Ptr(new Poco::JSON::Object());
155 report.scriptsReloaded = request.reloadScripts;
156
157 auto installed = installRestartArgs(vm, report.args);
158 if (!installed) return Result<SoftRestartReport>::failure(installed.status());
159
160 auto restarted = callSoftRestartGame(vm, request.reloadScripts);
161 if (!restarted) return Result<SoftRestartReport>::failure(restarted.status());
162
163 report.initCalled = true;
165 // Soft restart must not drop the unified resource cache. A drop here means
166 // someone called clear()/unload during the restart path — treat as failure
167 // so agents do not silently pay for a cold asset load on the next frame.
168 if (report.resourceCountAfter < report.resourceCountBefore) {
171 "resource cache shrank during soft restart (expected resources to be kept)",
172 "ResourceManager"));
173 }
174 return Result<SoftRestartReport>::success(std::move(report));
175}
176
177} // namespace eve::dev
LogicalId target
double value
SQInteger top
bool & active
struct SQVM * HSQUIRRELVM
HSQUIRRELVM vm
Definition ECS.cpp:20
const GltfImportRequest & request
std::string text
Soft-restart a live game without unloading the resource cache.
Json object
uint32_t index
std::uint32_t depth
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
size_t count() const
Number of cached entries.
Definition Resource.cpp:58
static ResourceManager & getInstance()
Returns the instance.
Definition Resource.cpp:15
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
Runtime-state serialization for state hot reload.
virtual bool resetToDefaults()=0
Reset this provider to its default state (restore fallback).
static ReloadSession & instance()
Instance.
std::size_t resourceCacheCount()
Current ResourceManager cache entry count (for restart telemetry).
std::uint32_t resetNativeStateProviders()
Reset every registered IStateProvider to its defaults.
Result< SoftRestartReport > executeSoftRestart(HSQUIRRELVM vm, SoftRestartRequest request)
Soft-restart the game attached to 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.
Outcome of a successful soft restart. @ownership Owning value; safe to keep across frames.
bool scriptsReloaded
Whether tracked scripts were re-dofile'd.
Poco::JSON::Object::Ptr args
Echo of the args table that was installed (may be empty).
std::size_t resourceCountBefore
Cached resource entries before the restart (must equal after).
bool initCalled
Whether eve_init / eve_restart ran.
std::size_t resourceCountAfter
Cached resource entries after the restart.
Inputs for one soft restart. @thread Game / MCP poll thread only.
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