载入中...
搜索中...
未找到
ClimbingControl.cpp
浏览该文件的文档.
2
3#include "common/Capability.h"
4
5#include <algorithm>
6
7namespace eve::climbing {
8namespace {
9
10LogicalId id(std::string_view text) { return LogicalId::parse(text).value(); }
11
12std::string phaseName(ClimbingPhase phase) {
13 switch (phase) {
14 case ClimbingPhase::Idle: return "idle";
15 case ClimbingPhase::Requested: return "requested";
16 case ClimbingPhase::Aligning: return "aligning";
17 case ClimbingPhase::Launching: return "launching";
18 case ClimbingPhase::Climbing: return "climbing";
19 case ClimbingPhase::Landing: return "landing";
20 case ClimbingPhase::Recovering: return "recovering";
21 case ClimbingPhase::Hanging: return "hanging";
22 case ClimbingPhase::Dropping: return "dropping";
23 case ClimbingPhase::Completed: return "completed";
24 case ClimbingPhase::Cancelled: return "cancelled";
25 case ClimbingPhase::Failed: return "failed";
26 case ClimbingPhase::Balanced: return "balanced";
27 case ClimbingPhase::Swinging: return "swinging";
28 }
29 return "unknown";
30}
31
32std::string eventName(ClimbingEventKind kind) {
33 switch (kind) {
34 case ClimbingEventKind::Started: return "started";
35 case ClimbingEventKind::AnchorTransitionStarted: return "anchor-transition-started";
36 case ClimbingEventKind::AnchorReached: return "anchor-reached";
37 case ClimbingEventKind::ContactLeftHand: return "contact-left-hand";
38 case ClimbingEventKind::ContactRightHand: return "contact-right-hand";
39 case ClimbingEventKind::Landed: return "landed";
40 case ClimbingEventKind::Hanging: return "hanging";
41 case ClimbingEventKind::Dropped: return "dropped";
42 case ClimbingEventKind::Completed: return "completed";
43 case ClimbingEventKind::Cancelled: return "cancelled";
44 case ClimbingEventKind::Failed: return "failed";
45 }
46 return "unknown";
47}
48
49bool terminal(ClimbingPhase phase) {
52}
53
54} // namespace
55
58 const ClimbingPose& pose)
59 : instance_(instance), character_(character), runtime_(&runtime), world_(&world), pose_(&pose) {
60 cap::addListener<IGameplayControlProvider>(this);
61 cap::addListener<IGameplayInstanceCatalog>(this);
62}
63
65 cap::removeListener<IGameplayInstanceCatalog>(this);
66 cap::removeListener<IGameplayControlProvider>(this);
67}
68
69std::string_view ClimbingControl::gameplayDomain() const noexcept { return "climbing"; }
70
71std::vector<SubjectRef> ClimbingControl::gameplayInstances() const {
72 // This adapter serves exactly one instance: the identity it was constructed
73 // with is the only one it can answer for.
74 if (!instance_.isValid()) return {};
75 return {instance_};
76}
77
78bool ClimbingControl::controls(const GameplaySession& session) const {
80 std::find(session.controlledSubjects.begin(), session.controlledSubjects.end(), character_) !=
81 session.controlledSubjects.end();
82}
83
84Result<std::uint64_t> ClimbingControl::refreshRevision() const {
85 if (!runtime_ || !world_ || !pose_)
87 Diagnostic::error(DiagnosticCode::StaleHandle, "climbing control collaborators are invalid", "instance"));
88 auto snapshot = runtime_->snapshotJson();
89 if (!snapshot) return Result<std::uint64_t>::failure(snapshot.status());
90 if (snapshot.value() != stateFingerprint_) {
91 stateFingerprint_ = snapshot.value();
92 ++revision_;
93 }
94 return Result<std::uint64_t>::success(revision_);
95}
96
98 SubjectRef instance) const {
99 if (instance != instance_ || !instance_.isValid())
101 Diagnostic::error(DiagnosticCode::NotFound, "climbing gameplay instance was not found", "instance"));
102 if (!controls(session))
104 DiagnosticCode::PreconditionViolation, "session does not control this character", "instance"));
105 auto revision = refreshRevision();
107 auto snapshot = runtime_->snapshot();
108 if (!snapshot) return Result<GameplayObservation>::failure(snapshot.status());
109 GameplayObservation observation;
110 observation.domain = id("gameplay:climbing");
111 observation.instance = instance_;
112 observation.tick = tick_;
113 observation.revision = revision.value();
114 observation.state = Value(Value::Object{
115 {"phase", Value(phaseName(runtime_->phase()))},
116 {"runtime", std::move(snapshot).takeValue()},
117 });
118 return Result<GameplayObservation>::success(std::move(observation));
119}
120
122 const GameplaySession& session, SubjectRef instance, SubjectRef subject) const {
123 auto observed = observeGameplay(session, instance);
124 if (!observed) return Result<std::vector<GameplayActionDescriptor>>::failure(observed.status());
125 std::move(observed).takeValue();
126 if (subject != character_)
128 DiagnosticCode::PreconditionViolation, "climbing action subject must be the character", "subject"));
129 const Value emptySchema(Value::Object{});
130 std::vector<GameplayActionDescriptor> actions;
131 const auto phase = runtime_->phase();
132 if (terminal(phase)) actions.push_back({id("climbing:begin-best"), emptySchema});
134 actions.push_back({id("climbing:climb-up"), emptySchema});
135 actions.push_back({id("climbing:drop"), emptySchema});
136 }
137 if (!terminal(phase)) actions.push_back({id("climbing:cancel"), emptySchema});
138 return Result<std::vector<GameplayActionDescriptor>>::success(std::move(actions));
139}
140
141Result<void> ClimbingControl::captureEvents(std::string_view commandId) {
142 auto drained = runtime_->drainEvents();
143 if (!drained) return Result<void>::failure(drained.status());
144 for (auto& source : std::move(drained).takeValue()) {
145 GameplayEvent event;
146 event.sequence = nextEventSequence_++;
147 event.tick = source.tick;
148 event.type = "climbing." + eventName(source.kind);
149 event.subject = character_;
150 event.causationCommandId = std::string(commandId);
151 event.correlationId = std::string(commandId);
152 Value::Array metadata;
153 for (auto& item : source.metadata) metadata.emplace_back(std::move(item));
154 event.payload = Value(Value::Object{
155 {"actionId", Value(std::move(source.actionId))},
156 {"executionId", Value(static_cast<std::int64_t>(source.executionId.value()))},
157 {"metadata", Value(std::move(metadata))},
158 });
159 events_.push_back(std::move(event));
160 }
161 return Result<void>::success();
162}
163
164Result<GameplayCommandReceipt> ClimbingControl::submitGameplay(const GameplaySession& session,
165 SubjectRef instance,
166 const GameplayCommand& command) {
167 if (instance != instance_)
169 Diagnostic::error(DiagnosticCode::NotFound, "climbing gameplay instance was not found", "instance"));
170 if (!controls(session) || command.subject != character_)
172 DiagnosticCode::PreconditionViolation, "session does not control this character", "command.subject"));
173 if (command.id.empty())
175 Diagnostic::error(DiagnosticCode::InvalidArgument, "command id must not be empty", "command.id"));
176 auto revision = refreshRevision();
178 if (command.observedTick != tick_ || command.expectedRevision != revision.value())
180 DiagnosticCode::Conflict, "climbing command was based on a stale observation", "command.expectedRevision"));
181
182 std::string operation;
183 if (command.action == id("climbing:begin-best")) {
184 auto begun = runtime_->tryBegin(*world_, *pose_, tick_);
185 if (!begun) return Result<GameplayCommandReceipt>::failure(begun.status());
186 std::move(begun).takeValue();
187 operation = "begin-best";
188 } else if (command.action == id("climbing:climb-up")) {
189 auto climbed = runtime_->climbUp(tick_);
190 if (!climbed) return Result<GameplayCommandReceipt>::failure(climbed.status());
191 operation = "climb-up";
192 } else if (command.action == id("climbing:drop")) {
193 auto dropped = runtime_->drop(tick_);
195 operation = "drop";
196 } else if (command.action == id("climbing:cancel")) {
197 auto cancelled = runtime_->cancel(ClimbingCancelReason::PlayerRequest, tick_);
198 if (!cancelled) return Result<GameplayCommandReceipt>::failure(cancelled.status());
199 operation = "cancel";
200 } else {
202 Diagnostic::error(DiagnosticCode::Unsupported, "unsupported climbing action", "command.action"));
203 }
204 auto captured = captureEvents(command.id);
205 if (!captured) return Result<GameplayCommandReceipt>::failure(captured.status());
206 stateFingerprint_.clear();
207 auto resultingRevision = refreshRevision();
208 if (!resultingRevision) return Result<GameplayCommandReceipt>::failure(resultingRevision.status());
210 receipt.commandId = command.id;
211 receipt.executionId = "climbing-" + std::to_string(runtime_->executionId().value());
212 receipt.acceptedTick = tick_;
213 receipt.resultingRevision = resultingRevision.value();
214 receipt.details = Value(Value::Object{{"operation", Value(operation)},
215 {"phase", Value(phaseName(runtime_->phase()))}});
216 return Result<GameplayCommandReceipt>::success(std::move(receipt),
218}
219
220Result<GameplayObservation> ClimbingControl::advanceGameplay(const GameplaySession& session,
221 SubjectRef instance,
222 const SimulationStep& step) {
223 if (step.tick <= tick_)
225 Diagnostic::error(DiagnosticCode::Conflict, "climbing simulation tick must increase", "step.tick"));
226 auto observed = observeGameplay(session, instance);
227 if (!observed) return Result<GameplayObservation>::failure(observed.status());
228 std::move(observed).takeValue();
229 if (!terminal(runtime_->phase())) {
230 auto advanced = runtime_->advance(*world_, step);
231 if (!advanced) return Result<GameplayObservation>::failure(advanced.status());
232 std::move(advanced).takeValue();
233 auto captured = captureEvents("advance");
234 if (!captured) return Result<GameplayObservation>::failure(captured.status());
235 }
236 tick_ = step.tick;
237 stateFingerprint_.clear();
238 return observeGameplay(session, instance);
239}
240
241Result<std::vector<GameplayEvent>> ClimbingControl::gameplayEvents(const GameplaySession& session,
242 SubjectRef instance,
243 std::uint64_t afterSequence) const {
244 auto observed = observeGameplay(session, instance);
245 if (!observed) return Result<std::vector<GameplayEvent>>::failure(observed.status());
246 std::move(observed).takeValue();
247 std::vector<GameplayEvent> result;
248 for (const auto& event : events_)
249 if (event.sequence > afterSequence) result.push_back(event);
250 const bool empty = result.empty();
251 return Result<std::vector<GameplayEvent>>::success(
252 std::move(result), Status::success(empty ? StatusCode::NoOp : StatusCode::Applied));
253}
254
255} // namespace eve::climbing
ActionParameterOperation operation
int subject
Definition AnimSmr.cpp:163
eve::EntitySpatialPose pose
float phase
Definition CaveMesh.cpp:58
Player-equivalent control adapter for one authoritative climbing runtime.
std::uint32_t dropped
std::string text
TokenKind kind
World3D * world
std::uint64_t revision
std::vector< ActionSpec > actions
Definition PlayHost.cpp:126
std::string id
Definition PlayHost.cpp:108
std::optional< InteractionSession > session
Definition Tactics.cpp:39
float step
Definition TreeMesh.cpp:314
const UnitySourceAsset & source
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
static std::optional< LogicalId > parse(std::string_view text)
Parses a scoped logical name.
Definition Identity.cpp:36
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
static Status success(StatusCode code=StatusCode::Ok)
Construct a successful status with an explicit non-error outcome.
Definition Status.h:81
Strong, domain-neutral reference to a runtime subject.
Definition SubjectRef.h:26
bool isValid() const noexcept
Return whether this reference is valid and non-nil.
Definition SubjectRef.h:38
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
Result< std::vector< GameplayActionDescriptor > > availableGameplayActions(const GameplaySession &session, SubjectRef instance, SubjectRef subject) const override
Discover currently legal player-semantic actions for one subject.
ClimbingControl(SubjectRef instance, SubjectRef character, ClimbingRuntime &runtime, physics::World3D &world, const ClimbingPose &pose)
Construct an adapter over one runtime, world and authoritative character pose.
Result< GameplayObservation > observeGameplay(const GameplaySession &session, SubjectRef instance) const override
Observe one provider-owned gameplay instance without mutation.
std::string_view gameplayDomain() const noexcept override
Stable provider domain used for discovery and routing.
std::vector< SubjectRef > gameplayInstances() const override
Instances currently served, in publication order.
~ClimbingControl() override
Unpublish the capability without destroying borrowed authorities.
Owner-thread climbing planner and executor for one character.
Definition Climbing.h:653
eve::Result< std::string > snapshotJson() const
Serializes the canonical runtime snapshot as JSON.
eve::Result< eve::Value > snapshot() const
Captures deterministic runtime state; debug candidates and final bone poses are excluded.
ClimbingPhase phase() const noexcept
Current lifecycle phase.
Definition Climbing.h:803
Box3D rigid-body world. Script coordinates are meters (Box3D native), unlike 2D World which uses pixe...
Definition World3D.h:63
ClimbingPhase
Execution lifecycle visible to gameplay and animation adapters.
Definition Climbing.h:470
ClimbingEventKind
Stable semantic event emitted by the authoritative climbing lifecycle.
Definition Climbing.h:488
std::variant< std::monostate, std::int64_t, double, std::string, bool > Value
Definition Database.h:26
std::string_view phaseName(BattlePhase phase) noexcept
Return the stable protocol spelling of a battle phase.
Owning evidence that a gameplay authority accepted one command.
One player-semantic command submitted to a domain authority.
std::uint64_t expectedRevision
SimulationTick observedTick
Owning event projection safe across frames and process boundaries.
std::uint64_t sequence
Immutable owning observation of one authoritative gameplay instance.
Owning authorization context shared by player, script and automation adapters.
One deterministic fixed-step emitted by SimulationClock.
Definition Time.h:158
Input pose used for one deterministic candidate probe.
Definition Climbing.h:275