载入中...
搜索中...
未找到
EditorProjectSettings.cpp
浏览该文件的文档.
2
3#include <algorithm>
4#include <utility>
5
6namespace eve::editor {
7namespace {
8
9const EditorValue* field(const EditorValue& value, const char* key) {
10 const auto* object = value.getIf<EditorValue::Object>();
11 if (!object) return nullptr;
12 const auto found = object->find(key);
13 return found == object->end() ? nullptr : &found->second;
14}
15
16DomainOperation operation(const std::string& target, const PropertyPath& path, EditorValue value, EditorValue inverse) {
17 DomainOperation result;
18 result.type = "project.setting.set.v1";
19 result.inverseType = result.type;
20 result.target = TargetId(target);
21 result.payload = EditorValue::Object{{"path", path.value()}, {"value", std::move(value)}};
22 result.inverse = EditorValue::Object{{"path", path.value()}, {"value", std::move(inverse)}};
23 result.hasInverse = true;
24 result.affectedProperties.push_back(path.value());
25 return result;
26}
27
29 PropertyFlag flags = PropertyFlag::None) {
30 PropertyDescriptor result;
31 result.path = PropertyPath(path);
32 result.displayNameKey = "editor.settings." + path;
33 result.category = "project";
34 result.type = type;
35 result.flags = flags;
36 result.defaultValue = std::move(value);
37 return result;
38}
39
40} // namespace
41
43 : id_(std::move(id)), schema_(std::move(schema)) {
44 for (const auto& setting : schema_.settings)
45 values_.emplace(setting.property.path.value(), setting.property.defaultValue);
46}
47
49 return {TargetId(id_),
50 "project-settings",
52 false,
53 {CapabilityId("eve.editor.target.project-settings")}};
54}
55
57 return capability == CapabilityId("eve.editor.target.project-settings") ? static_cast<IPropertyProvider*>(this)
58 : nullptr;
59}
60
61const ProjectSettingDescriptor* ProjectSettingsTarget::descriptor(const PropertyPath& path) const {
62 const auto found =
63 std::find_if(schema_.settings.begin(), schema_.settings.end(),
64 [&](const ProjectSettingDescriptor& setting) { return setting.property.path == path; });
65 return found == schema_.settings.end() ? nullptr : &*found;
66}
67
68bool ProjectSettingsTarget::selectionMatches(const SelectionSnapshot& selection) const {
69 return selection.items.size() == 1 && selection.items.front().target == TargetId(id_);
70}
71
73 if (domainOperation.target != TargetId(id_) || domainOperation.type != "project.setting.set.v1")
74 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId("editor.settings.operation"),
75 "Unsupported or mismatched settings operation");
76 const auto* pathEntry = field(domainOperation.payload, "path");
77 const auto* value = field(domainOperation.payload, "value");
78 const auto* path = pathEntry ? pathEntry->getIf<std::string>() : nullptr;
79 const auto* setting = path ? descriptor(PropertyPath(*path)) : nullptr;
80 if (!setting || !value)
81 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId("editor.settings.payload"),
82 "Settings operation payload is incomplete");
83 auto valid = validatePropertyValue(setting->property, *value);
84 if (!valid.ok()) return valid;
85 if (setting->sensitive) {
86 const auto* reference = value->getIf<std::string>();
87 if (!reference || !reference->starts_with("secret://"))
88 return eve::editing::failed<void>(
89 EditorStatus::Rejected, RuleId("editor.settings.raw-secret"),
90 "Sensitive settings accept secret:// references only; raw credentials are never stored");
91 }
92 if (values_.at(*path) == *value) return eve::editing::noOp();
93 values_[*path] = *value;
94 if (setting->requiresRestart) restartDirty_[*path] = true;
96 widenDirty(0, 0);
97 return eve::editing::applied<void>();
98}
99
106
108 PropertySchema result;
109 result.typeId = schema_.typeId;
110 result.version = schema_.version;
111 for (const auto& setting : schema_.settings) result.properties.push_back(setting.property);
112 return result;
113}
114
116 if (!selectionMatches(selection))
117 return {PropertyReadState::Error,
118 {},
120 DiagnosticSeverity::Error, "Selection does not match settings target")}};
121 const auto found = values_.find(path.value());
122 if (found == values_.end()) return {};
123 const auto* setting = descriptor(path);
124 if (setting && setting->sensitive) {
125 const auto* reference = found->second.getIf<std::string>();
126 if (!reference)
127 return {PropertyReadState::Error,
128 {},
130 eve::DiagnosticCode::PreconditionViolation, RuleId("editor.settings.invalid-sensitive-schema"),
131 DiagnosticSeverity::Error, "Sensitive setting schema must use a string value")}};
132 if (!reference->empty()) return {PropertyReadState::Value, EditorValue("secret://redacted"), {}};
133 }
134 return {PropertyReadState::Value, found->second, {}};
135}
136
138 const PropertyPath& path, const EditorValue& value,
139 PropertySetMode mode) const {
140 if (!selectionMatches(selection))
141 return eve::editing::failed<DomainOperation>(EditorStatus::Rejected, RuleId("editor.settings.selection"),
142 "Selection does not match settings target");
143 const auto* setting = descriptor(path);
144 if (!setting)
145 return eve::editing::failed<DomainOperation>(EditorStatus::Unsupported, RuleId("editor.settings.property"),
146 "Unknown project setting: " + path.value());
147 const EditorValue candidate = mode == PropertySetMode::Reset ? setting->property.defaultValue : value;
148 auto valid = validatePropertyValue(setting->property, candidate);
149 if (!valid.ok())
150 return eve::editing::failed<DomainOperation>(valid.code(), RuleId("editor.settings.value"),
151 "Project setting value is invalid");
152 if (setting->sensitive) {
153 const auto* reference = candidate.getIf<std::string>();
154 if (!reference || (!reference->empty() && !reference->starts_with("secret://")))
155 return eve::editing::failed<DomainOperation>(
156 EditorStatus::Rejected, RuleId("editor.settings.raw-secret"),
157 "Sensitive settings accept an empty value or secret:// reference only");
158 }
159 return eve::editing::applied<DomainOperation>(operation(id_, path, candidate, values_.at(path.value())));
160}
161
163 const PropertyPath& path) const {
164 return makeSet(selection, path, {}, PropertySetMode::Reset);
165}
166
167std::vector<PropertyPath> ProjectSettingsTarget::pendingRestart() const {
168 std::vector<PropertyPath> result;
169 for (const auto& [path, dirty] : restartDirty_)
170 if (dirty) result.emplace_back(path);
171 return result;
172}
173
176 for (const auto& setting : schema_.settings) {
177 const auto& value = values_.at(setting.property.path.value());
178 const auto* reference = setting.sensitive ? value.getIf<std::string>() : nullptr;
179 if (!setting.sensitive || (reference && (reference->empty() || reference->starts_with("secret://"))))
180 values.emplace(setting.property.path.value(), value);
181 }
182 return EditorValue::Object{
183 {"schema", schema_.typeId}, {"schemaVersion", int64_t{schema_.version}}, {"values", std::move(values)}};
184}
185
187 const auto* schemaEntry = field(snapshot, "schema");
188 const auto* versionEntry = field(snapshot, "schemaVersion");
189 const auto* valuesEntry = field(snapshot, "values");
190 const auto* schema = schemaEntry ? schemaEntry->getIf<std::string>() : nullptr;
191 const auto* version = versionEntry ? versionEntry->getIf<int64_t>() : nullptr;
192 const auto* values = valuesEntry ? valuesEntry->getIf<EditorValue::Object>() : nullptr;
193 if (!schema || *schema != schema_.typeId || !version || *version != schema_.version || !values)
194 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId("editor.settings.snapshot"),
195 "Settings snapshot schema does not match this target");
196 ProjectSettingsTarget candidate(id_, schema_);
197 for (const auto& [path, value] : *values) {
198 const auto* setting = candidate.descriptor(PropertyPath(path));
199 if (!setting)
200 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId("editor.settings.unknown-snapshot-key"),
201 "Settings snapshot contains an unknown key");
202 auto valid = validatePropertyValue(setting->property, value);
203 if (!valid.ok())
204 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId("editor.settings.invalid-snapshot-value"),
205 "Settings snapshot contains an invalid value");
206 if (setting->sensitive) {
207 const auto* reference = value.getIf<std::string>();
208 if (!reference || (!reference->empty() && !reference->starts_with("secret://")))
209 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId("editor.settings.raw-secret"),
210 "Settings snapshot contains a raw sensitive value");
211 }
212 candidate.values_[path] = value;
213 }
214 values_ = std::move(candidate.values_);
215 restartDirty_.clear();
216 bumpRevision();
217 widenDirty(0, 0);
218 return eve::editing::applied<void>();
219}
220
223 result.typeId = "engine.project-settings";
224 result.settings = {
225 {property("content.asset-root", PropertyType::String, "assets"), "filesystem", true, false},
226 {property("content.import-cache", PropertyType::String, ".cache/import"), "filesystem", true, false},
227 {property("plugins.allow-unsigned", PropertyType::Bool, false, PropertyFlag::Dangerous), "plugins", true,
228 false},
229 {property("network.endpoint", PropertyType::String, ""), "network", true, false},
230 {property("network.auth-token", PropertyType::String, ""), "network", true, true},
231 {property("database.connector", PropertyType::Enum, "SQLite"), "database", true, false},
232 {property("database.connection", PropertyType::String, ""), "database", true, true}};
233 result.settings[5].property.enumItems = {"SQLite", "MySQL", "PostgreSQL"};
234 return result;
235}
236
237ProjectSettingsSchema importerSettingsSchema(std::string importerId, std::vector<ProjectSettingDescriptor> settings,
238 std::uint32_t version) {
240 result.typeId = "engine.importer-settings." + importerId;
241 result.version = version;
242 result.settings = std::move(settings);
243 return result;
244}
245
246} // namespace eve::editor
LogicalId target
ActionParameterOperation operation
double value
std::string descriptor
AuthorityStoreHandleRef reference
Definition Authority.cpp:24
std::map< std::string, Var > values
std::uint32_t key
bool valid
bool dirty
std::string path
Definition PlayHost.cpp:110
std::string id
Definition PlayHost.cpp:108
bool found
TerrainThermalSettings settings
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
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
UUID-backed identifier adapter for legacy textual boundaries.
Definition Identity.h:314
const std::string & value() const noexcept
Returns the exact legacy or canonical compatibility spelling.
Definition Identity.h:362
void bumpRevision()
Advance the revision without touching the dirty region.
Revision revisionValue() const noexcept
Return the revision as the shared protocol revision type.
void widenDirty()
Mark an unknown area dirty without advancing the revision.
eve::OptionalRef< C > capability()
Query a capability through its interface-owned stable identity.
UI-independent property access for an immutable selection snapshot.
Dotted property path (layout.size) compared and persisted as text.
Definition EditingIds.h:78
Deterministic owning value tree shared by authoring hosts.
std::map< std::string, Value > Object
const T * getIf() const
Return an immediately borrowed typed value.
Schema-driven, reversible settings target with secret-reference enforcement.
Result< void > loadSnapshot(const EditorValue &snapshot)
Atomically load matching-schema values.
TargetDescriptor describe() const override
Describe.
Result< DomainOperation > makeReset(const SelectionSnapshot &selection, const PropertyPath &path) const override
Make reset.
void * queryCapability(const CapabilityId &capability) override
Query an optional target capability.
eve::Result< eve::Revision > currentRevision(const SelectionSnapshot &selection) const override
Current revision.
EditorValue snapshotValue() const
Capture values without ever serializing raw sensitive values.
std::vector< PropertyPath > pendingRestart() const
Return settings whose changed values require subsystem restart.
PropertySchema schema(const SelectionSnapshot &selection) const override
Schema.
ProjectSettingsTarget(std::string id, ProjectSettingsSchema schema)
Project settings target.
Result< void > applyDomainOperation(const DomainOperation &operation) override
Applies domain operation.
PropertyReadResult read(const SelectionSnapshot &selection, const PropertyPath &path) const override
Reads .
Result< DomainOperation > makeSet(const SelectionSnapshot &selection, const PropertyPath &path, const EditorValue &value, PropertySetMode mode) const override
Make set.
PropertySetMode
PropertySetMode public API.
Result< void > noOp(std::vector< Diagnostic > diagnostics={})
Construct a successful void result for an operation that made no change.
Diagnostic ruleDiagnostic(eve::DiagnosticCode code, RuleId rule, DiagnosticSeverity severity, std::string message)
Build a common diagnostic carrying an open editing rule identity.
eve::editing::Value EditorValue
Compatibility alias for the UI-independent authoring value tree.
Definition EditorValue.h:8
eve::editing::PropertyType PropertyType
eve::editing::PropertyPath PropertyPath
Definition EditorIds.h:37
eve::editing::PropertyFlag PropertyFlag
eve::editing::PropertyDescriptor PropertyDescriptor
eve::editing::TargetId TargetId
Definition EditorIds.h:29
eve::editing::RuleId RuleId
Definition EditorIds.h:32
eve::editing::SelectionSnapshot SelectionSnapshot
Immutable multi-selection captured for property and command work.
eve::editing::CapabilityId CapabilityId
Definition EditorIds.h:33
ProjectSettingsSchema importerSettingsSchema(std::string importerId, std::vector< ProjectSettingDescriptor > settings, std::uint32_t version)
Create per-importer settings without hard-coding a presenter.
eve::editing::DomainOperation DomainOperation
ProjectSettingsSchema defaultProjectSettingsSchema()
Common engine project settings covering content, network and database boundaries.
const EditorValue * field(const EditorValue &value, const char *name)
DomainOperation public API.
PropertyReadResult public API.
EVENGINE_API_PLATFORM public API.
std::vector< PropertyDescriptor > properties
SelectionSnapshot public API.
Stable discovery metadata exposed by every editable target.
Project-setting descriptor with deployment and secrecy policy.
Versioned settings schema shared by project and importer documents.
std::vector< ProjectSettingDescriptor > settings