载入中...
搜索中...
未找到
ScenarioRecorder.cpp
浏览该文件的文档.
3
4#include "common/Module.h"
6
7#include <Poco/JSON/Array.h>
8#include <Poco/JSON/Object.h>
9#include <Poco/JSON/Parser.h>
10
11#include <fstream>
12#include <sstream>
13
14namespace eve::dev {
15
17 static ScenarioRecorder inst;
18 return inst;
19}
20
21void ScenarioRecorder::setObserver() {
23 eventModule_ = eve::ModuleManager::requireInstance<eve::platform_event::PlatformEvent>("PlatformEvent");
24 if (!eventModule_) return;
25 savedObserver_ = eventModule_->pollObserver();
26 eventModule_->setPollObserver([this](const eve::platform_event::Message& m) { onEventConsumed(m); });
27}
28
30 if (!eventModule_) return;
31 eventModule_->setPollObserver(savedObserver_);
32 eventModule_ = nullptr;
33 savedObserver_ = {};
34}
35
36bool ScenarioRecorder::begin(HSQUIRRELVM vm, std::string* err) {
37 if (!vm) {
38 if (err) *err = "null VM";
39 return false;
40 }
41 cancel();
42 const std::string base = Snapshot::instance().capture(vm, err);
43 if (base.empty()) return false;
44 vm_ = vm;
45 baseline_ = base;
46 frames_.clear();
47 errorReport_.clear();
48 errorSite_.clear();
49 currentFrame_ = 0;
50 recording_ = true;
51 setObserver();
52 return true;
53}
54
56 if (!recording_) return;
57 ++currentFrame_;
58 if (frames_.empty() || frames_.back().frame != currentFrame_)
59 frames_.push_back(ScenarioFrame{currentFrame_});
60}
61
62bool ScenarioRecorder::end(const std::string& path, std::string* err) {
63 if (!recording_) {
64 if (err) *err = "not recording";
65 return false;
66 }
67 recording_ = false;
69 const bool ok = save(baseline_, frames_, errorReport_, errorSite_, path, err);
70 vm_ = nullptr;
71 return ok;
72}
73
75 recording_ = false;
76 replay_ = false;
78 vm_ = nullptr;
79 baseline_.clear();
80 frames_.clear();
81 errorReport_.clear();
82 errorSite_.clear();
83 currentFrame_ = 0;
84 replayIndex_ = 0;
85}
86
87void ScenarioRecorder::setErrorInfo(const std::string& report, const std::string& site) {
88 if (!recording_) return;
89 if (!report.empty()) errorReport_ = report;
90 if (!site.empty()) errorSite_ = site;
91}
92
94 if (!recording_ || !vm_) return;
96 ev.name = msg.name;
97 for (const auto& a : msg.args) {
98 switch (a.type) {
99 case eve::platform_event::Variant::Type::String: ev.args.push_back(a.s); break;
101 ev.args.push_back(std::to_string(static_cast<long long>(a.i)));
102 break;
103 case eve::platform_event::Variant::Type::Nil: ev.args.push_back({}); break;
104 default:
105 // Ptr payloads (borrowed/owned native pointers) are not serializable; drop.
106 break;
107 }
108 }
109 if (frames_.empty() || frames_.back().frame != currentFrame_)
110 frames_.push_back(ScenarioFrame{currentFrame_});
111 frames_.back().events.push_back(std::move(ev));
112}
113
114bool ScenarioRecorder::beginReplay(HSQUIRRELVM vm, const std::string& path, std::string* err) {
115 if (!vm) {
116 if (err) *err = "null VM";
117 return false;
118 }
119 std::string base, er, es;
120 std::vector<ScenarioFrame> frames;
121 if (!load(path, &base, &frames, &er, &es, err)) return false;
122 if (!Snapshot::instance().restore(vm, base, err)) return false;
123 cancel();
124 vm_ = vm;
125 baseline_ = base;
126 frames_ = std::move(frames);
127 errorReport_ = er;
128 errorSite_ = es;
129 replay_ = true;
130 replayIndex_ = 0;
131 // Acquire the event module for input injection (observer stays detached).
132 eventModule_ = eve::ModuleManager::requireInstance<eve::platform_event::PlatformEvent>("PlatformEvent");
133 return true;
134}
135
136bool ScenarioRecorder::stageFrame() {
137 if (!replay_ || !eventModule_) return false;
138 if (replayIndex_ >= static_cast<int>(frames_.size())) {
139 replay_ = false;
140 return false;
141 }
142 const ScenarioFrame& frame = frames_[replayIndex_++];
143 for (const auto& ev : frame.events) {
144 std::vector<eve::platform_event::Variant> args;
145 for (const auto& a : ev.args) args.push_back(eve::platform_event::Variant::makeString(a));
146 eventModule_->push(new eve::platform_event::Message(ev.name, args));
147 }
148 return true;
149}
150
151bool ScenarioRecorder::save(const std::string& baseline,
152 const std::vector<ScenarioFrame>& frames,
153 const std::string& errorReport, const std::string& errorSite,
154 const std::string& path, std::string* err) {
155 try {
156 Poco::JSON::Object root;
157 root.set("version", "1");
158 root.set("baseline", baseline);
159 Poco::JSON::Array framesArr;
160 for (const auto& f : frames) {
161 Poco::JSON::Object fo;
162 fo.set("frame", f.frame);
163 Poco::JSON::Array evs;
164 for (const auto& e : f.events) {
165 Poco::JSON::Object eo;
166 eo.set("name", e.name);
167 Poco::JSON::Array args;
168 for (const auto& a : e.args)
169 args.add(a);
170 eo.set("args", args);
171 evs.add(eo);
172 }
173 fo.set("events", evs);
174 framesArr.add(fo);
175 }
176 root.set("frames", framesArr);
177 root.set("errorReport", errorReport);
178 root.set("errorSite", errorSite);
179 std::ostringstream oss;
180 root.stringify(oss);
181 std::ofstream ofs(path, std::ios::binary);
182 if (!ofs) {
183 if (err) *err = "cannot write " + path;
184 return false;
185 }
186 ofs << oss.str();
187 return static_cast<bool>(ofs);
188 } catch (const std::exception& e) {
189 if (err) *err = e.what();
190 return false;
191 }
192}
193
194bool ScenarioRecorder::load(const std::string& path, std::string* baseline,
195 std::vector<ScenarioFrame>* frames, std::string* errorReport,
196 std::string* errorSite, std::string* err) {
197 try {
198 std::ifstream ifs(path, std::ios::binary);
199 if (!ifs) {
200 if (err) *err = "cannot read " + path;
201 return false;
202 }
203 std::ostringstream oss;
204 oss << ifs.rdbuf();
205 Poco::JSON::Parser parser;
206 Poco::Dynamic::Var result = parser.parse(oss.str());
207 Poco::JSON::Object::Ptr root = result.extract<Poco::JSON::Object::Ptr>();
208 if (baseline) *baseline = root->getValue<std::string>("baseline");
209 if (errorReport) *errorReport = root->optValue<std::string>("errorReport", "");
210 if (errorSite) *errorSite = root->optValue<std::string>("errorSite", "");
211 if (frames) {
212 frames->clear();
213 Poco::JSON::Array::Ptr fa = root->getArray("frames");
214 if (fa) {
215 for (size_t i = 0; i < fa->size(); ++i) {
216 Poco::JSON::Object::Ptr fo = fa->getObject(static_cast<unsigned int>(i));
218 f.frame = fo->optValue<int>("frame", static_cast<int>(i));
219 Poco::JSON::Array::Ptr evs = fo->getArray("events");
220 if (evs) {
221 for (size_t j = 0; j < evs->size(); ++j) {
222 Poco::JSON::Object::Ptr eo =
223 evs->getObject(static_cast<unsigned int>(j));
225 e.name = eo->getValue<std::string>("name");
226 Poco::JSON::Array::Ptr args = eo->getArray("args");
227 if (args) {
228 for (size_t k = 0; k < args->size(); ++k)
229 e.args.push_back(args->getElement<std::string>(
230 static_cast<unsigned int>(k)));
231 }
232 f.events.push_back(std::move(e));
233 }
234 }
235 frames->push_back(std::move(f));
236 }
237 }
238 }
239 return true;
240 } catch (const std::exception& e) {
241 if (err) *err = e.what();
242 return false;
243 }
244}
245
246} // namespace eve::dev
int root
Definition AnimSmr.cpp:119
struct SQVM * HSQUIRRELVM
HSQUIRRELVM vm
Definition ECS.cpp:20
MeleePoint3 a
Definition MeleeHit.cpp:40
float f
std::string path
Definition PlayHost.cpp:110
float m[16]
A state-driven bug scenario: baseline snapshot + ordered per-frame inputs.
void setErrorInfo(const std::string &report, const std::string &site)
Called by DevTool when a script error is dumped during recording.
bool end(const std::string &path, std::string *err=nullptr)
Stop recording and persist the scenario to a JSON file.
void markFrame()
Begin a new frame bucket; no-op unless recording.
void cancel()
Abort recording and restore the previous event observer.
bool begin(HSQUIRRELVM vm, std::string *err=nullptr)
Start recording: capture the baseline snapshot and observe the event queue.
void detachObserver()
Ensure the event observer is detached (restore previous); safe to call repeatedly.
void onEventConsumed(const eve::platform_event::Message &msg)
Invoked by the event poll observer for every consumed message.
static ScenarioRecorder & instance()
Instance.
static bool save(const std::string &baseline, const std::vector< ScenarioFrame > &frames, const std::string &errorReport, const std::string &errorSite, const std::string &path, std::string *err=nullptr)
Persist a scenario to a JSON file (used for both record and replay paths).
std::string capture(HSQUIRRELVM vm, std::string *error=nullptr) const
Capture marked roots, or heuristic roots when none marked.
Definition Snapshot.cpp:438
static Snapshot & instance()
Definition Snapshot.cpp:290
A named event carrying an ordered list of Variant payloads. Pushed messages are heap-allocated; the q...
const std::vector< Variant > args
const std::function< void(const Message &)> & pollObserver() const
Current poll observer, or empty if none.
void setPollObserver(std::function< void(const Message &)> observer)
Sets an observer invoked for every message consumed via poll*().
One serializable event payload recorded from the event queue.
std::vector< std::string > args
String payloads (non-string payloads dropped).
std::string name
Event/message name ("keypressed", ...).
All events consumed during one game frame.
int frame
Frame index within the scenario.
std::vector< ScenarioEvent > events
Events consumed that frame, in order.
static Variant makeString(std::string v)
Constructs a string variant (takes ownership of the value).