载入中...
搜索中...
未找到
EditorTargetCoordinator.cpp
浏览该文件的文档.
2
6
7#include <map>
8#include <set>
9#include <utility>
10#include <vector>
11
12namespace eve::editor {
13namespace {
14
16 EditorValue::Array capabilities;
17 for (const CapabilityId& capability : descriptor.capabilities)
18 capabilities.emplace_back(capability.value());
19 return EditorValue::Object{{"id", descriptor.id.value()},
20 {"type", descriptor.type},
21 {"revision", static_cast<std::int64_t>(descriptor.revision)},
22 {"readOnly", descriptor.readOnly},
23 {"capabilities", std::move(capabilities)}};
24}
25
26} // namespace
27
29 struct TargetEntry {
30 explicit TargetEntry(IEditableTarget& value, IDomainOperationTarget& operations, std::uint64_t valueGeneration)
31 : target(&value), generation(valueGeneration), authority(&operations), transactions(&authority) {}
32
34 std::shared_ptr<void> lifetime = std::make_shared<int>(0);
35 std::uint64_t generation = 0;
38 };
39
41
43 const auto found = targets.find(id);
44 return found == targets.end() ? nullptr : found->second.get();
45 }
46 const TargetEntry* entry(const TargetId& id) const {
47 const auto found = targets.find(id);
48 return found == targets.end() ? nullptr : found->second.get();
49 }
50
52 TargetEntry* selected = entry(plan.target);
53 if (!selected || !selected->target)
54 return eve::editing::failed<TransactionReceipt>(EditorStatus::NotFound, RuleId("editor.target.not-found"),
55 "Editing target is not registered: " + plan.target.value());
56 if (selected->target->revision() != plan.baseRevision)
57 return eve::editing::failed<TransactionReceipt>(EditorStatus::Conflict,
58 RuleId("editor.target.revision-conflict"),
59 "Editing target changed after planning");
60 if (selected->generation != plan.targetGeneration)
61 return eve::editing::failed<TransactionReceipt>(EditorStatus::Conflict,
62 RuleId("editor.target.generation-conflict"),
63 "Editing target lifetime changed after planning");
64 TransactionSpec specification;
65 specification.id = TransactionId(plan.id.value() + ".transaction");
66 specification.label = plan.command.value();
67 specification.origin = ActionOrigin::Automation;
68 specification.target = plan.target;
69 specification.baseRevision = plan.baseRevision;
70 auto begun = selected->transactions.begin(std::move(specification));
71 if (!begun.ok())
72 return eve::editing::failed<TransactionReceipt>(begun.code(), RuleId("editor.target.begin-failed"),
73 "Could not begin the editing transaction");
74 for (const DomainOperation& operation : plan.operations) {
75 auto appended = selected->transactions.append(operation);
76 if (appended.ok()) continue;
77 auto discarded = selected->transactions.discard();
78 if (!discarded.ok())
79 return eve::editing::failed<TransactionReceipt>(EditorStatus::Failed,
80 RuleId("editor.target.discard-failed"),
81 "Could not discard a rejected transaction");
82 return eve::editing::failed<TransactionReceipt>(appended.code(), RuleId("editor.target.append-failed"),
83 "Could not stage the editing operation");
84 }
85 auto committed = selected->transactions.commit();
86 if (!committed.ok()) {
87 // This coordinator owns the one-shot batch; callers cannot retry or
88 // discard it themselves. Leave the existing undo history usable.
89 auto discarded = selected->transactions.discard();
90 if (!discarded.ok())
91 return eve::editing::failed<TransactionReceipt>(EditorStatus::Failed,
92 RuleId("editor.target.discard-failed"),
93 "Could not discard a rejected commit");
94 }
95 return committed;
96 }
97
99 std::map<TargetId, std::unique_ptr<TargetEntry>> targets;
102 std::uint64_t generation = 0;
103 };
104 std::map<EditorSession*, SessionBinding> sessions;
105 std::vector<std::pair<CommandId, std::string>> registeredCommands;
106 std::uint64_t nextTargetGeneration = 1;
107};
108
112 while (!impl_->sessions.empty()) {
113 EditorSession* session = impl_->sessions.begin()->first;
114 if (session) session->coordinatorDestroyed(*this);
115 impl_->sessions.erase(impl_->sessions.begin());
116 }
117 for (const auto& [id, owner] : impl_->registeredCommands)
118 (void)impl_->commands->unregisterCommand(id, owner);
119}
120
123 const CommandId commandId = descriptor.id;
124 const std::string ownerModule = descriptor.ownerModule;
125 CommandDescriptor hostDescriptor;
126 hostDescriptor.id = descriptor.id;
127 hostDescriptor.ownerModule = std::move(descriptor.ownerModule);
128 hostDescriptor.displayName = std::move(descriptor.displayName);
129 hostDescriptor.category = std::move(descriptor.category);
130 hostDescriptor.automationAllowed = descriptor.automationAllowed;
131 auto registered = impl_->commands->registerPlannedCommand(
132 std::move(hostDescriptor),
133 [state = impl_.get(), planner = std::move(planner)](const CommandRequest& request) {
134 Impl::TargetEntry* selected = state->entry(request.context.target);
135 if (!selected || !selected->target)
136 return eve::editing::failed<CommandPlan>(EditorStatus::NotFound, RuleId("editor.target.not-found"),
137 "Editing target is not registered");
138 Result<CommandPlan> result = planner(*selected->target, request);
139 if (result.ok()) result.value().targetGeneration = selected->generation;
140 return result;
141 },
142 [state = impl_.get()](const CommandRequest&, const CommandPlan& plan) { return state->execute(plan); });
143 if (!registered.ok()) return Result<void>::failure(registered.status());
144 impl_->registeredCommands.emplace_back(commandId, ownerModule);
145 return eve::editing::applied<void>(registered.diagnostics());
146}
147
149 const std::string& ownerModule) {
150 const std::size_t removed = impl_->commands->unregisterOwner(ownerModule);
151 std::erase_if(impl_->registeredCommands,
152 [&](const auto& registration) { return registration.second == ownerModule; });
153 return eve::editing::applied<std::size_t>(removed);
154}
155
157 auto* operations = dynamic_cast<IDomainOperationTarget*>(&target);
158 if (!operations)
159 return eve::editing::failed<void>(EditorStatus::Unsupported, RuleId("editor.target.operations-unsupported"),
160 "Editing target does not support domain operations");
161 const TargetId id(target.targetId());
162 if (id.empty())
163 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId("editor.target.empty-id"),
164 "Editing target id must not be empty");
165 if (const auto found = impl_->targets.find(id); found != impl_->targets.end()) {
166 if (found->second->target == &target) {
167 return eve::editing::noOp();
168 }
169 return eve::editing::failed<void>(EditorStatus::Conflict, RuleId("editor.target.duplicate-id"),
170 "Another editing target already uses id: " + id.value());
171 }
172 impl_->targets.emplace(id,
173 std::make_unique<Impl::TargetEntry>(target, *operations, impl_->nextTargetGeneration++));
174 return eve::editing::applied<void>();
175}
176
178 Impl::TargetEntry* selected = impl_->entry(target);
179 if (selected) {
180 std::vector<EditorSession*> sessions;
181 for (const auto& [session, binding] : impl_->sessions)
182 if (binding.target == target && binding.generation == selected->generation) sessions.push_back(session);
184 if (session) session->invalidateTrackedTarget(*this, target, selected->generation);
185 impl_->targets.erase(target);
186 return eve::editing::applied<void>();
187 }
188 return eve::editing::noOp();
189}
190
192 Impl::TargetEntry* selected = impl_->entry(target);
193 if (!selected || !selected->target)
194 return eve::editing::failed<void>(EditorStatus::NotFound, RuleId("editor.target.not-found"),
195 "Editing target is not registered: " + target.value());
196 session.bindTrackedTarget(*selected->target, selected->lifetime, *this, selected->generation);
197 impl_->sessions[&session] = {target, selected->generation};
198 return eve::editing::applied<void>();
199}
200
201void EditorTargetCoordinator::detach(EditorSession& session) noexcept { impl_->sessions.erase(&session); }
202
204 const Impl::TargetEntry* selected = impl_->entry(target);
205 if (!selected || !selected->target)
206 return eve::editing::failed<EditorValue>(EditorStatus::NotFound, RuleId("editor.target.not-found"),
207 "Editing target is not registered: " + target.value());
208 EditorValue::Object result;
209 result["descriptor"] = descriptorValue(selected->target->describe());
210 auto snapshot = selected->target->capability<eve::editing::IEditingSnapshotProvider>();
211 result["snapshot"] = snapshot ? snapshot->get().snapshotValue() : EditorValue{};
212 return eve::editing::applied<EditorValue>(EditorValue(std::move(result)));
213}
214
216 Impl::TargetEntry* selected = impl_->entry(target);
217 if (!selected)
218 return eve::editing::failed<TransactionReceipt>(EditorStatus::NotFound, RuleId("editor.target.not-found"),
219 "Editing target is not registered: " + target.value());
220 return selected->transactions.undo();
221}
222
224 Impl::TargetEntry* selected = impl_->entry(target);
225 if (!selected)
226 return eve::editing::failed<TransactionReceipt>(EditorStatus::NotFound, RuleId("editor.target.not-found"),
227 "Editing target is not registered: " + target.value());
228 return selected->transactions.redo();
229}
230
231std::vector<EditorTargetCoordinator::TargetSummary> EditorTargetCoordinator::targets() const {
232 std::vector<TargetSummary> summaries;
233 summaries.reserve(impl_->targets.size());
234 // std::map keeps the ids ordered, so discovery is deterministic for a caller
235 // that diffs two lists.
236 for (const auto& [id, entry] : impl_->targets) {
237 TargetSummary summary;
238 summary.id = id.value();
239 if (entry) {
240 summary.generation = entry->generation;
241 if (entry->target) {
242 const TargetDescriptor descriptor = entry->target->describe();
243 summary.type = descriptor.type;
244 summary.revision = static_cast<std::uint64_t>(descriptor.revision);
245 }
246 }
247 summaries.push_back(std::move(summary));
248 }
249 return summaries;
250}
251
252} // namespace eve::editor
LogicalId target
ActionParameterOperation operation
double value
std::string descriptor
const GltfImportRequest & request
int sessions
eve::action::ActionVfxBinding binding
std::string id
Definition PlayHost.cpp:108
bool found
int removed
std::optional< InteractionSession > session
Definition Tactics.cpp:39
std::vector< VegetationPresetCommand > commands
Move-only operation result carrying either a value or Status.
Definition Result.h:155
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
Editable-target capability that applies owning domain operations.
IEditableTarget public API.
eve::OptionalRef< C > capability()
Query a capability through its interface-owned stable identity.
virtual std::uint64_t revision() const =0
Revision.
virtual TargetDescriptor describe() const
Describe the target for tools and automation.
Optional capability exposing a deterministic, persistence-safe editing snapshot.
virtual Value snapshotValue() const =0
Capture the target without exposing mutable runtime storage.
In-process authority for a single editable runtime or document target.
Deterministic owning value tree shared by authoring hosts.
std::vector< Value > Array
std::map< std::string, Value > Object
Type type() const
Return the active value type.
Registry and execution gate for UI-, script- and automation-neutral commands.
Hosts interchangeable editor tools and routes their lifecycle/input.
Result< TransactionReceipt > redo(const TargetId &target)
Redo.
~EditorTargetCoordinator()
Editor target coordinator.
eve::editing::Result< void > registerPlannedCommand(eve::editing::EditingCommandDescriptor descriptor, eve::editing::EditingCommandPlanner planner) override
Registers planned command.
Result< void > unregisterTarget(const TargetId &target)
Unregisters target.
Result< EditorValue > inspect(const TargetId &target) const
Inspect.
eve::editing::Result< std::size_t > unregisterOwner(const std::string &ownerModule) override
Unregisters owner.
Result< void > bind(EditorSession &session, const TargetId &target)
Binds .
EditorTargetCoordinator(EditorCommandService &commands)
Editor target coordinator.
Result< TransactionReceipt > undo(const TargetId &target)
Undo.
std::vector< TargetSummary > targets() const
Enumerate every registered target, ordered by id.
Result< void > registerTarget(IEditableTarget &target)
Registers target.
Local transaction coordinator backed by an injected edit authority.
Result< TransactionReceipt > commit() override
Preflight and commit all active operations through the authority.
Result< TransactionId > begin(TransactionSpec specification) override
Begin a transaction with a stable id and base revision.
Result< TransactionReceipt > undo() override
Compensate the most recent committed transaction.
Result< void > discard() override
Discard the active, not-yet-committed operation list.
Result< TransactionReceipt > redo() override
Reapply the most recently compensated transaction.
Result< void > append(DomainOperation operation) override
Append an operation to the active transaction without applying it.
std::function< Result< CommandPlan >(IEditableTarget &, const CommandRequest &)> EditingCommandPlanner
Side-effect-free planner invoked with the resolved target.
Result< void > noOp(std::vector< Diagnostic > diagnostics={})
Construct a successful void result for an operation that made no change.
eve::editing::Value EditorValue
Compatibility alias for the UI-independent authoring value tree.
Definition EditorValue.h:8
eve::editing::RuleId RuleId
Definition EditorIds.h:32
eve::editing::CapabilityId CapabilityId
Definition EditorIds.h:33
eve::editing::TransactionId TransactionId
Definition EditorIds.h:34
CommandPlan public API.
std::vector< DomainOperation > operations
std::uint64_t targetGeneration
Target lifetime generation captured while planning.
CommandRequest public API.
DomainOperation public API.
Domain-owned metadata for one planned editing command.
Stable discovery metadata exposed by every editable target.
TransactionSpec public API.
Presentation and execution metadata for a registered command.
TargetEntry(IEditableTarget &value, IDomainOperationTarget &operations, std::uint64_t valueGeneration)
const TargetEntry * entry(const TargetId &id) const
std::map< EditorSession *, SessionBinding > sessions
Result< TransactionReceipt > execute(const CommandPlan &plan)
std::map< TargetId, std::unique_ptr< TargetEntry > > targets
std::vector< std::pair< CommandId, std::string > > registeredCommands
Discovery metadata for one registered editable target.