载入中...
搜索中...
未找到
ScriptGeneratorHost.cpp
浏览该文件的文档.
2
6#include "procgen/Procgen.h"
8
9#include <simplesquirrel/simplesquirrel.hpp>
10
11#include <cstdint>
12#include <exception>
13#include <memory>
14#include <optional>
15#include <string>
16#include <unordered_set>
17
18namespace eve::procgen {
19namespace {
20
21template <class T>
22eve::Result<T> hostFailure(eve::DiagnosticCode code, std::string message, std::string path) {
24 eve::Diagnostic::error(code, message, path, {}, "procgen.script-host"));
25}
26
27ssq::Table projectHostResult(HSQUIRRELVM vm, eve::Result<eve::Value>&& result) {
28 const eve::Status status = result.status();
29 if (!result.ok()) return eve::script::projectStatusResult(vm, status);
30 eve::Value value = std::move(result).takeValue();
32}
33
34class ActiveSystemScope {
35public:
36 ActiveSystemScope(std::unordered_set<std::string>& active, std::string system)
37 : active_(active), system_(std::move(system)) {
38 inserted_ = active_.insert(system_).second;
39 }
40 ~ActiveSystemScope() {
41 if (inserted_) active_.erase(system_);
42 }
43 [[nodiscard]] bool inserted() const noexcept { return inserted_; }
44
45private:
46 std::unordered_set<std::string>& active_;
47 std::string system_;
48 bool inserted_ = false;
49};
50
51eve::Result<eve::Value> runGenerator(HSQUIRRELVM vm, Procgen& procgen, const ssq::Table& generator,
52 const ssq::Object& params, const std::string& systemName,
53 std::uint32_t seed) {
54 if (systemName.empty())
55 return hostFailure<eve::Value>(eve::DiagnosticCode::InvalidArgument,
56 "script generator system name must not be empty", "system");
57 if (generator.isEmpty())
58 return hostFailure<eve::Value>(eve::DiagnosticCode::InvalidArgument,
59 "script generator module must be a table", "generator");
60 if (params.isEmpty())
61 return hostFailure<eve::Value>(eve::DiagnosticCode::InvalidArgument,
62 "script generator Params must not be empty", "params");
63
64 thread_local std::unordered_set<std::string> activeSystems;
65 ActiveSystemScope active(activeSystems, systemName);
66 if (!active.inserted())
67 return hostFailure<eve::Value>(eve::DiagnosticCode::Conflict,
68 "script generator cannot recursively rebuild the same system", "system");
69
70 std::optional<ssq::Function> generate;
71 try {
72 generate.emplace(generator.findFunc("generate"));
73 } catch (const std::exception& error) {
74 return hostFailure<eve::Value>(eve::DiagnosticCode::InvalidArgument,
75 std::string("script generator requires generate(params, ctx): ") + error.what(),
76 "generator.generate");
77 }
78
79 auto contextResult = Procgen::beginSystemHandle(systemName, seed);
80 if (!contextResult.ok()) return eve::Result<eve::Value>::failure(contextResult.status());
81 const ProcgenContextHandleRef contextRef = std::move(contextResult).takeValue();
82 auto contextView = Procgen::resolve(contextRef);
83 if (!contextView.isBound()) {
84 Procgen::release(contextRef).ignore("release unresolved script generator context");
85 return hostFailure<eve::Value>(eve::DiagnosticCode::StaleHandle,
86 "script generator context became stale before execution", "context");
87 }
88
89 auto contextObject = eve::script::makeOwnedSquirrelInstance<ScriptProcgenContext>(
90 vm, std::make_unique<ScriptProcgenContext>(contextRef));
91 if (!contextObject.ok()) {
92 const eve::Status status = contextObject.status();
93 contextObject.ignore("script generator context proxy creation failed");
94 procgen.abortSystem(contextRef).ignore("abort script generator after proxy creation failure");
95 Procgen::release(contextRef).ignore("release script generator context after proxy creation failure");
97 }
98 ssq::Object context = std::move(contextObject).takeValue();
99
100 const SQInteger top = sq_gettop(vm);
102 sq_pushobject(vm, generate->getRaw());
103 sq_pushobject(vm, generator.getRaw());
104 sq_pushobject(vm, params.getRaw());
105 sq_pushobject(vm, context.getRaw());
106 const bool called = SQ_SUCCEEDED(sq_call(vm, 3, SQFalse, SQTrue));
107 sq_settop(vm, top);
108
109 if (!called) {
111 if (error.empty()) error = "script generator callback failed";
112 procgen.abortSystem(contextRef).ignore("abort failed script generator callback");
113 Procgen::release(contextRef).ignore("release failed script generator context");
114 return hostFailure<eve::Value>(eve::DiagnosticCode::Failed, std::move(error), "generator.generate");
115 }
116
117 auto committed = procgen.commitSystem(contextRef);
118 const eve::Status commitStatus = committed.status();
119 if (!committed.ok()) {
120 Procgen::release(contextRef).ignore("release rejected script generator context");
121 return eve::Result<eve::Value>::failure(commitStatus);
122 }
123 committed.ignore("script generator commit observed by host");
124
125 eve::Value::Array outputs;
126 const int outputCount = procgen.getSystemOutputCount(systemName);
127 outputs.reserve(static_cast<std::size_t>(outputCount));
128 for (int index = 0; index < outputCount; ++index)
129 outputs.emplace_back(procgen.getSystemOutputName(systemName, index));
130
132 {"system", eve::Value(systemName)},
133 {"seed", eve::Value(static_cast<std::int64_t>(procgen.getSystemSeed(systemName)))},
134 {"revision", eve::Value(static_cast<std::int64_t>(procgen.getSystemRevision(systemName)))},
135 {"outputs", eve::Value(std::move(outputs))},
136 });
137 Procgen::release(contextRef).ignore("release committed script generator context");
138 return eve::Result<eve::Value>::success(std::move(receipt), commitStatus);
139}
140
141} // namespace
142
144 const HSQUIRRELVM vm = cls.getHandle();
145 cls.addFunc("runScriptGenerator",
146 [vm](Procgen* self, ssq::Table generator, ssq::Object params,
147 const std::string& systemName, std::uint32_t seed) {
148 if (!self)
149 return projectHostResult(
150 vm, hostFailure<eve::Value>(eve::DiagnosticCode::InvalidArgument,
151 "runScriptGenerator requires a Procgen module", "procgen"));
152 return projectHostResult(vm, runGenerator(vm, *self, generator, params, systemName, seed));
153 });
154}
155
156} // namespace eve::procgen
double value
SQInteger top
bool & active
struct SQVM * HSQUIRRELVM
HSQUIRRELVM vm
Definition ECS.cpp:20
HSQOBJECT cls
Definition ECS.cpp:21
std::string message
DiagnosticCode code
wgpu::PopErrorScopeStatus status
std::string error
Definition Package.cpp:60
std::string path
Definition PlayHost.cpp:110
std::uint32_t seed
Definition PointSet.cpp:807
The single Squirrel projection for common Result, Status and Value.
Explicit Value/Owned/Borrowed semantics at the Squirrel boundary.
uint32_t index
const VegetationPresetContext & context
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
void ignore(std::string_view reason={}) const noexcept
Explicitly discard this result after documenting the reason.
Definition Result.h:537
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
Structured status and zero or more diagnostics for an operation.
Definition Status.h:68
The canonical owning dynamic value used by data-facing protocols.
Definition Value.h:31
std::map< std::string, Value > Object
Definition Value.h:34
std::vector< Value > Array
Definition Value.h:33
Procedural generation module. Phase A: runtime maps (semantic Grid2D → TileLayer)....
Definition Procgen.h:146
static eve::script::Borrowed< Params > resolve(ProcgenParamsHandleRef reference) noexcept
Resolves parameters as a non-owning observation.
Definition Procgen.cpp:403
static eve::Result< ProcgenContextHandleRef > beginSystemHandle(const std::string &name, uint32_t seed)
Starts a module-owned rebuild context and returns its handle.
Definition Procgen.cpp:1482
static eve::Result< void > release(ProcgenParamsHandleRef reference)
Releases module-owned parameters.
Definition Procgen.cpp:409
std::vector< ParamSpec > params
Result< Grid2D > generate(std::string_view operation, const GenerateSettings &settings)
Generate.
void exposeScriptGeneratorHost(ssq::Class &cls)
Add the synchronous script-generator host API to the Procgen facade.
eve::script::RuntimeHandleRef< ProcgenContextHandleTag > ProcgenContextHandleRef
Definition Procgen.h:114
void clearLastScriptError(HSQUIRRELVM vm)
Drops any recorded error for a VM.
std::string formatLastScriptError(HSQUIRRELVM vm)
Formats the last captured error for a VM without consuming it.
ssq::Table projectStatusResult(HSQUIRRELVM vm, const Status &status)
Project a checked native status that carries no payload.
DiagnosticCode
Stable machine-readable diagnostic codes.
Definition Diagnostic.h:47