activity_sdk_mission_runtime.cpp 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880
  1. #include <Windows.h>
  2. #include <array>
  3. #include <limits>
  4. #include <optional>
  5. #include <span>
  6. #include "../../middleware/bap/activity_message/sensor_auth_update.h"
  7. #include "../../state/activity/runtime.h"
  8. #include "activity_sdk_behavior_scope.h"
  9. #include "activity_sdk_mission_internal.h"
  10. #include "activity_sdk_scene_spawn.h"
  11. #include "activity_sdk_scriptable_route.h"
  12. #include "host_runtime.h"
  13. namespace sunrise::server::activity::activity_sdk_mission {
  14. namespace {
  15. namespace auth = middleware::bap::activity_message::scriptable_auth;
  16. namespace layouts = state::build_data::scenarios;
  17. namespace message = middleware::bap::activity_message::sensor_auth_update;
  18. namespace sdk = state::activity_sdk;
  19. namespace sdk_route = server::activity::activity_sdk_scriptables;
  20. using detail::binding_status;
  21. using detail::lease_status;
  22. using detail::materialize_plan;
  23. using detail::prepare_dialogue;
  24. using detail::prepare_directive;
  25. using detail::prepare_objective;
  26. using detail::prepare_scene;
  27. using detail::prepare_task;
  28. using detail::prepare_typed_behavior;
  29. using detail::PreparedScene;
  30. using detail::read_lease;
  31. using detail::same_plan;
  32. using detail::scene_binding_status;
  33. } // namespace
  34. /** Resolves the generated plan and current lease without changing transport state. */
  35. Status query(const sdk::BoundView& view, Snapshot& output) noexcept {
  36. output = {};
  37. server::bap::ActivityLinkView link{};
  38. const Status binding = binding_status(view, link);
  39. if (binding != Status::ready) {
  40. return binding;
  41. }
  42. output.activityClientGeneration = link.activityClientGeneration;
  43. output.arrivalSliceSetIndex = link.arrivalSliceSetIndex;
  44. output.liveSliceSetIndex = link.sliceSetIndex;
  45. output.effectiveRegion = link.effectiveRegion;
  46. server::bap::ActivityMissionSeedLeaseView lease{};
  47. const Status leaseResult = read_lease(view, link, lease);
  48. if (leaseResult != Status::ready) {
  49. return leaseResult;
  50. }
  51. output.revision = lease.revision;
  52. output.publishedRevision = lease.publishedRevision;
  53. output.configured = lease.configured;
  54. output.publicationPending = lease.publicationPending;
  55. output.regionArrivalPending = lease.regionArrivalPending;
  56. server::bap::ActivityMissionSeedPlan generated{};
  57. const std::int32_t selectedRegion =
  58. lease.configured
  59. && lease.plan.effectiveRegion
  60. <= static_cast<std::uint32_t>((std::numeric_limits<std::int32_t>::max)())
  61. ? static_cast<std::int32_t>(lease.plan.effectiveRegion)
  62. : link.effectiveRegion;
  63. // The lease's own omissions produced its plan, so the check must re-materialize with them.
  64. const Status generatedStatus = materialize_plan(
  65. view,
  66. selectedRegion,
  67. std::span(lease.plan.omissions).first(lease.configured ? lease.plan.omissionCount : 0),
  68. generated);
  69. output.plan = lease.configured ? lease.plan : generated;
  70. if (generatedStatus != Status::ready) {
  71. return generatedStatus;
  72. }
  73. if (lease.configured && !same_plan(lease.plan, generated)) {
  74. return lease.plan.effectiveRegion == generated.effectiveRegion ? Status::refused
  75. : Status::wrongSliceSet;
  76. }
  77. return Status::ready;
  78. }
  79. /** Selects one exact authored state and exposes its asynchronous publication state. */
  80. Status select_state(const sdk::BoundView& view,
  81. std::int32_t effectiveRegion,
  82. std::span<const sdk::MissionSeedOmission> omissions,
  83. Snapshot& output) noexcept {
  84. output = {};
  85. server::bap::ActivityLinkView link{};
  86. const Status binding = binding_status(view, link);
  87. if (binding != Status::ready) {
  88. return binding;
  89. }
  90. server::bap::ActivityMissionSeedPlan plan{};
  91. const Status materialized = materialize_plan(view, effectiveRegion, omissions, plan);
  92. if (materialized != Status::ready) {
  93. return materialized;
  94. }
  95. const Status selected = lease_status(server::bap::select_activity_mission_seed(
  96. view.binding, plan, link.activityClientGeneration));
  97. if (selected != Status::ready) {
  98. return selected;
  99. }
  100. return query(view, output);
  101. }
  102. /** Checks one exact occurrence and type-43 slot without changing transport state. */
  103. SceneStatus authored_scene_availability(const sdk::BoundView& view,
  104. std::uint32_t occurrenceRow,
  105. std::uint32_t slotRow) noexcept {
  106. PreparedScene prepared{};
  107. return prepare_scene(view, occurrenceRow, slotRow, prepared);
  108. }
  109. /** Queues the next generation for one exact state-local authored scene. */
  110. SceneStatus activate_authored_scene(const sdk::BoundView& view,
  111. std::uint32_t occurrenceRow,
  112. std::uint32_t slotRow) noexcept {
  113. PreparedScene prepared{};
  114. const SceneStatus status = prepare_scene(view, occurrenceRow, slotRow, prepared);
  115. if (status != SceneStatus::ready) {
  116. return status;
  117. }
  118. if (prepared.omittedParticipants != 0) {
  119. log_omitted_participants(*view.catalog, slotRow, prepared.omittedParticipants);
  120. }
  121. if (server::bap::request_activity_state_local_authored_scene_override(
  122. view.binding,
  123. prepared.target,
  124. prepared.rosterGroup,
  125. prepared.effectiveRegion,
  126. prepared.activityClientGeneration,
  127. nullptr,
  128. prepared.sceneDependencies)) {
  129. return SceneStatus::queued;
  130. }
  131. return SceneStatus::refused;
  132. }
  133. /** Checks one exact SDK-bounded authored dialogue cue. */
  134. SceneStatus dialogue_cue_availability(const sdk::BoundView& view,
  135. std::uint32_t occurrenceRow,
  136. std::uint32_t slotRow,
  137. std::uint16_t cueIndex) noexcept {
  138. PreparedScene prepared{};
  139. std::uint16_t authoredCueCount = 0;
  140. return prepare_dialogue(view, occurrenceRow, slotRow, cueIndex, prepared, authoredCueCount);
  141. }
  142. /** Queues one authored dialogue cue through the dedicated type-53 encoder. */
  143. SceneStatus play_dialogue_cue(const sdk::BoundView& view,
  144. std::uint32_t occurrenceRow,
  145. std::uint32_t slotRow,
  146. std::uint16_t cueIndex) noexcept {
  147. PreparedScene prepared{};
  148. std::uint16_t authoredCueCount = 0;
  149. const SceneStatus status =
  150. prepare_dialogue(view, occurrenceRow, slotRow, cueIndex, prepared, authoredCueCount);
  151. if (status != SceneStatus::ready) {
  152. return status;
  153. }
  154. if (server::bap::request_activity_state_local_dialogue_override(
  155. view.binding,
  156. prepared.target,
  157. prepared.rosterGroup,
  158. cueIndex,
  159. authoredCueCount,
  160. prepared.effectiveRegion,
  161. prepared.activityClientGeneration)) {
  162. return SceneStatus::queued;
  163. }
  164. return SceneStatus::refused;
  165. }
  166. /** Checks one exact type-68 slot; the answer holds for every element the slot carries. */
  167. SceneStatus directives_availability(const sdk::BoundView& view,
  168. std::uint32_t occurrenceRow,
  169. std::uint32_t slotRow) noexcept {
  170. PreparedScene prepared{};
  171. return prepare_typed_behavior(view,
  172. occurrenceRow,
  173. slotRow,
  174. sdk::format::kDirectiveSlotType,
  175. sdk::format::kDirectiveComponentClass,
  176. sdk::format::kDirectiveAuthSchema,
  177. false,
  178. prepared);
  179. }
  180. SceneStatus directive_availability(const sdk::BoundView& view,
  181. std::uint32_t occurrenceRow,
  182. std::uint32_t slotRow,
  183. std::uint32_t nameHash,
  184. std::int32_t elementIndex) noexcept {
  185. PreparedScene prepared{};
  186. return prepare_directive(view, occurrenceRow, slotRow, nameHash, elementIndex, prepared);
  187. }
  188. /** Sets one HUD directive element's state and visibility on a prepared objective slot. */
  189. SceneStatus set_directive(const sdk::BoundView& view,
  190. std::uint32_t occurrenceRow,
  191. std::uint32_t slotRow,
  192. std::uint32_t nameHash,
  193. std::int32_t elementIndex,
  194. std::int8_t state,
  195. bool visible) noexcept {
  196. PreparedScene prepared{};
  197. const SceneStatus status =
  198. prepare_directive(view, occurrenceRow, slotRow, nameHash, elementIndex, prepared);
  199. if (status != SceneStatus::ready) {
  200. return status;
  201. }
  202. middleware::bap::activity_message::scriptable_auth::Type68Preset preset{
  203. .nameHash = nameHash, .elementIndex = elementIndex, .state = state, .visible = visible};
  204. std::array<std::byte, middleware::bap::activity_message::scriptable_auth::kType68ByteCount>
  205. body{};
  206. std::size_t written = 0;
  207. if (!middleware::bap::activity_message::scriptable_auth::encode_type68(preset, body, written)
  208. || written != body.size()) {
  209. return SceneStatus::invalidSlot;
  210. }
  211. return server::bap::request_activity_sdk_auth_override(
  212. view.binding,
  213. prepared.target,
  214. &prepared.rosterGroup,
  215. body,
  216. middleware::bap::activity_message::scriptable_auth::kType68BitCount,
  217. prepared.effectiveRegion,
  218. prepared.activityClientGeneration)
  219. ? SceneStatus::queued
  220. : SceneStatus::refused;
  221. }
  222. /**
  223. * Queues one authored dialogue cue against a reservation a script already holds.
  224. * @param reservation Output slot the caller reserved for this revision.
  225. * @param filterSlotRow Type-60 volume the line waits for, or none to play at once.
  226. * @return `queued` once the cue is staged, or the refusal that stopped it.
  227. */
  228. [[nodiscard]] SceneStatus
  229. play_dialogue_cue_reserved(const sdk::BoundView& view,
  230. std::uint32_t occurrenceRow,
  231. std::uint32_t slotRow,
  232. std::uint16_t cueIndex,
  233. const host::ScriptableOutputReservation& reservation,
  234. std::optional<std::uint32_t> filterSlotRow) noexcept {
  235. PreparedScene prepared{};
  236. std::uint16_t authoredCueCount = 0;
  237. const SceneStatus status =
  238. prepare_dialogue(view, occurrenceRow, slotRow, cueIndex, prepared, authoredCueCount);
  239. if (status != SceneStatus::ready) {
  240. return status;
  241. }
  242. auth::Type2LaneClientRef filter{};
  243. if (filterSlotRow.has_value()) {
  244. const auto slots = view.catalog->slots();
  245. const auto objects = view.catalog->objects();
  246. if (*filterSlotRow >= slots.size()) {
  247. return SceneStatus::invalidSlot;
  248. }
  249. const sdk::format::Slot& volume = slots[*filterSlotRow];
  250. if (volume.slotType != static_cast<std::uint32_t>(auth::kType53FilterSlotType)
  251. || volume.objectIndex >= objects.size()
  252. || volume.slotIndex
  253. > static_cast<std::uint32_t>((std::numeric_limits<std::int16_t>::max)())
  254. || objects[volume.objectIndex].objectKey == 0) {
  255. return SceneStatus::invalidSlot;
  256. }
  257. filter = {objects[volume.objectIndex].objectKey,
  258. auth::kType53FilterSlotType,
  259. static_cast<std::int16_t>(volume.slotIndex)};
  260. }
  261. return server::bap::request_activity_state_local_dialogue_override(
  262. view.binding,
  263. prepared.target,
  264. prepared.rosterGroup,
  265. cueIndex,
  266. authoredCueCount,
  267. prepared.effectiveRegion,
  268. prepared.activityClientGeneration,
  269. &reservation,
  270. filter)
  271. ? SceneStatus::queued
  272. : SceneStatus::refused;
  273. }
  274. /** Checks one exact SDK-linked authored task. */
  275. SceneStatus task_availability(const sdk::BoundView& view,
  276. std::uint32_t occurrenceRow,
  277. std::uint32_t slotRow) noexcept {
  278. PreparedScene prepared{};
  279. return prepare_task(view, occurrenceRow, slotRow, prepared);
  280. }
  281. SceneStatus objective_reset_availability(const sdk::BoundView& view,
  282. std::uint32_t occurrenceRow,
  283. std::uint32_t slotRow) noexcept {
  284. PreparedScene prepared{};
  285. return prepare_objective(view, occurrenceRow, slotRow, prepared);
  286. }
  287. /** Clears every objective element of one prepared objective slot. */
  288. SceneStatus reset_objectives(const sdk::BoundView& view,
  289. std::uint32_t occurrenceRow,
  290. std::uint32_t slotRow) noexcept {
  291. PreparedScene prepared{};
  292. const SceneStatus status = prepare_objective(view, occurrenceRow, slotRow, prepared);
  293. if (status != SceneStatus::ready) {
  294. return status;
  295. }
  296. if (server::bap::request_activity_state_local_objective_reset(
  297. view.binding,
  298. prepared.target,
  299. prepared.rosterGroup,
  300. prepared.effectiveRegion,
  301. prepared.activityClientGeneration)) {
  302. return SceneStatus::queued;
  303. }
  304. return SceneStatus::refused;
  305. }
  306. /** Queues one exact objective reset using a caller-owned output reservation. */
  307. [[nodiscard]] SceneStatus
  308. reset_objectives_reserved(const sdk::BoundView& view,
  309. std::uint32_t occurrenceRow,
  310. std::uint32_t slotRow,
  311. const host::ScriptableOutputReservation& reservation) noexcept {
  312. PreparedScene prepared{};
  313. const SceneStatus status = prepare_objective(view, occurrenceRow, slotRow, prepared);
  314. if (status != SceneStatus::ready) {
  315. return status;
  316. }
  317. return server::bap::request_activity_state_local_objective_reset(
  318. view.binding,
  319. prepared.target,
  320. prepared.rosterGroup,
  321. prepared.effectiveRegion,
  322. prepared.activityClientGeneration,
  323. &reservation)
  324. ? SceneStatus::queued
  325. : SceneStatus::refused;
  326. }
  327. /** Queues the next generation through the dedicated type-38 encoder. */
  328. SceneStatus activate_task(const sdk::BoundView& view,
  329. std::uint32_t occurrenceRow,
  330. std::uint32_t slotRow) noexcept {
  331. PreparedScene prepared{};
  332. const SceneStatus status = prepare_task(view, occurrenceRow, slotRow, prepared);
  333. if (status != SceneStatus::ready) {
  334. return status;
  335. }
  336. if (server::bap::request_activity_state_local_task_override(
  337. view.binding,
  338. prepared.target,
  339. prepared.rosterGroup,
  340. prepared.effectiveRegion,
  341. prepared.activityClientGeneration)) {
  342. return SceneStatus::queued;
  343. }
  344. return SceneStatus::refused;
  345. }
  346. /**
  347. * Activates one authored task against a reservation a script already holds.
  348. * @param reservation Output slot the caller reserved for this revision.
  349. * @return `queued` once the task override is staged, or the refusal that stopped it.
  350. */
  351. [[nodiscard]] SceneStatus
  352. activate_task_reserved(const sdk::BoundView& view,
  353. std::uint32_t occurrenceRow,
  354. std::uint32_t slotRow,
  355. const host::ScriptableOutputReservation& reservation) noexcept {
  356. PreparedScene prepared{};
  357. const SceneStatus status = prepare_task(view, occurrenceRow, slotRow, prepared);
  358. if (status != SceneStatus::ready) {
  359. return status;
  360. }
  361. return server::bap::request_activity_state_local_task_override(
  362. view.binding,
  363. prepared.target,
  364. prepared.rosterGroup,
  365. prepared.effectiveRegion,
  366. prepared.activityClientGeneration,
  367. &reservation)
  368. ? SceneStatus::queued
  369. : SceneStatus::refused;
  370. }
  371. /** @return Whether one occurrence's sequence slot can be played right now. */
  372. SceneStatus sequence_availability(const sdk::BoundView& view,
  373. std::uint32_t occurrenceRow,
  374. std::uint32_t slotRow) noexcept {
  375. PreparedScene prepared{};
  376. return prepare_typed_behavior(view,
  377. occurrenceRow,
  378. slotRow,
  379. sdk::format::kSequenceSlotType,
  380. sdk::format::kSequenceComponentClass,
  381. sdk::format::kSequenceAuthSchema,
  382. false,
  383. prepared);
  384. }
  385. /** Plays one sequence slot of a prepared behavior occurrence. */
  386. SceneStatus play_sequence(const sdk::BoundView& view,
  387. std::uint32_t occurrenceRow,
  388. std::uint32_t slotRow) noexcept {
  389. PreparedScene prepared{};
  390. const SceneStatus status = prepare_typed_behavior(view,
  391. occurrenceRow,
  392. slotRow,
  393. sdk::format::kSequenceSlotType,
  394. sdk::format::kSequenceComponentClass,
  395. sdk::format::kSequenceAuthSchema,
  396. false,
  397. prepared);
  398. if (status != SceneStatus::ready) {
  399. return status;
  400. }
  401. if (server::bap::request_activity_state_local_sequence_override(
  402. view.binding,
  403. prepared.target,
  404. prepared.rosterGroup,
  405. prepared.effectiveRegion,
  406. prepared.activityClientGeneration)) {
  407. return SceneStatus::queued;
  408. }
  409. return SceneStatus::refused;
  410. }
  411. /** @return Whether one occurrence's cinematic slot can be driven right now. */
  412. SceneStatus cinematic_availability(const sdk::BoundView& view,
  413. std::uint32_t occurrenceRow,
  414. std::uint32_t slotRow) noexcept {
  415. PreparedScene prepared{};
  416. return prepare_typed_behavior(view,
  417. occurrenceRow,
  418. slotRow,
  419. sdk::format::kCinematicSlotType,
  420. sdk::format::kCinematicComponentClass,
  421. sdk::format::kCinematicAuthSchema,
  422. false,
  423. prepared);
  424. }
  425. /** Sets one cinematic slot active or inactive on a prepared behavior occurrence. */
  426. SceneStatus set_cinematic_active(const sdk::BoundView& view,
  427. std::uint32_t occurrenceRow,
  428. std::uint32_t slotRow,
  429. bool active) noexcept {
  430. PreparedScene prepared{};
  431. const SceneStatus status = prepare_typed_behavior(view,
  432. occurrenceRow,
  433. slotRow,
  434. sdk::format::kCinematicSlotType,
  435. sdk::format::kCinematicComponentClass,
  436. sdk::format::kCinematicAuthSchema,
  437. false,
  438. prepared);
  439. if (status != SceneStatus::ready) {
  440. return status;
  441. }
  442. if (server::bap::request_activity_state_local_cinematic_override(
  443. view.binding,
  444. prepared.target,
  445. prepared.rosterGroup,
  446. active,
  447. prepared.effectiveRegion,
  448. prepared.activityClientGeneration)) {
  449. return SceneStatus::queued;
  450. }
  451. return SceneStatus::refused;
  452. }
  453. /** Plays one sequence slot against an already-reserved Host output revision. */
  454. SceneStatus play_sequence_reserved(const sdk::BoundView& view,
  455. std::uint32_t occurrenceRow,
  456. std::uint32_t slotRow,
  457. const host::ScriptableOutputReservation& reservation) noexcept {
  458. PreparedScene prepared{};
  459. const SceneStatus status = prepare_typed_behavior(view,
  460. occurrenceRow,
  461. slotRow,
  462. sdk::format::kSequenceSlotType,
  463. sdk::format::kSequenceComponentClass,
  464. sdk::format::kSequenceAuthSchema,
  465. false,
  466. prepared);
  467. if (status != SceneStatus::ready) {
  468. return status;
  469. }
  470. return server::bap::request_activity_state_local_sequence_override(
  471. view.binding,
  472. prepared.target,
  473. prepared.rosterGroup,
  474. prepared.effectiveRegion,
  475. prepared.activityClientGeneration,
  476. &reservation)
  477. ? SceneStatus::queued
  478. : SceneStatus::refused;
  479. }
  480. /**
  481. * Drives one cinematic behavior against a reservation a script already holds.
  482. * @param reservation Output slot the caller reserved for this revision.
  483. * @return `queued` once the override is staged, or the refusal that stopped it.
  484. */
  485. SceneStatus
  486. set_cinematic_active_reserved(const sdk::BoundView& view,
  487. std::uint32_t occurrenceRow,
  488. std::uint32_t slotRow,
  489. bool active,
  490. const host::ScriptableOutputReservation& reservation) noexcept {
  491. PreparedScene prepared{};
  492. const SceneStatus status = prepare_typed_behavior(view,
  493. occurrenceRow,
  494. slotRow,
  495. sdk::format::kCinematicSlotType,
  496. sdk::format::kCinematicComponentClass,
  497. sdk::format::kCinematicAuthSchema,
  498. false,
  499. prepared);
  500. if (status != SceneStatus::ready) {
  501. return status;
  502. }
  503. return server::bap::request_activity_state_local_cinematic_override(
  504. view.binding,
  505. prepared.target,
  506. prepared.rosterGroup,
  507. active,
  508. prepared.effectiveRegion,
  509. prepared.activityClientGeneration,
  510. &reservation)
  511. ? SceneStatus::queued
  512. : SceneStatus::refused;
  513. }
  514. /** Finds the occurrence a slot currently belongs to. @return `invalidView` on a stale view. */
  515. [[nodiscard]] static SceneStatus current_behavior_occurrence(
  516. const sdk::BoundView& view, std::uint32_t slotRow, std::uint32_t& occurrenceRow) noexcept {
  517. occurrenceRow = sdk::format::kAbsentIndex;
  518. Snapshot snapshot{};
  519. if (query(view, snapshot) != Status::ready || view.catalog == nullptr) {
  520. return SceneStatus::invalidView;
  521. }
  522. const auto slots = view.catalog->slots();
  523. const auto occurrences = view.catalog->occurrences();
  524. if (slotRow >= slots.size()) {
  525. return SceneStatus::invalidSlot;
  526. }
  527. server::bap::ActivityLinkView link{};
  528. const SceneStatus live = scene_binding_status(view, link);
  529. if (live != SceneStatus::ready) {
  530. return live;
  531. }
  532. const auto selected = behavior_scope::select(occurrences,
  533. view.catalog->states(),
  534. view.catalog->bubbles(),
  535. view.scenarioRow,
  536. slots[slotRow].objectIndex,
  537. snapshot.plan.stateRow,
  538. link.effectiveRegion);
  539. if (selected.ambiguous) {
  540. return SceneStatus::ambiguousTarget;
  541. }
  542. occurrenceRow = selected.row;
  543. return occurrenceRow == sdk::format::kAbsentIndex ? SceneStatus::targetUnavailable
  544. : SceneStatus::ready;
  545. }
  546. SceneStatus sequence_slot_availability(const sdk::BoundView& view, std::uint32_t slotRow) noexcept {
  547. std::uint32_t occurrenceRow = 0;
  548. const SceneStatus found = current_behavior_occurrence(view, slotRow, occurrenceRow);
  549. return found == SceneStatus::ready ? sequence_availability(view, occurrenceRow, slotRow)
  550. : found;
  551. }
  552. SceneStatus objective_reset_slot_availability(const sdk::BoundView& view,
  553. std::uint32_t slotRow) noexcept {
  554. std::uint32_t occurrenceRow = 0;
  555. const SceneStatus found = current_behavior_occurrence(view, slotRow, occurrenceRow);
  556. return found == SceneStatus::ready ? objective_reset_availability(view, occurrenceRow, slotRow)
  557. : found;
  558. }
  559. SceneStatus
  560. reset_objectives_slot_reserved(const sdk::BoundView& view,
  561. std::uint32_t slotRow,
  562. const host::ScriptableOutputReservation& reservation) noexcept {
  563. std::uint32_t occurrenceRow = 0;
  564. const SceneStatus found = current_behavior_occurrence(view, slotRow, occurrenceRow);
  565. return found == SceneStatus::ready
  566. ? reset_objectives_reserved(view, occurrenceRow, slotRow, reservation)
  567. : found;
  568. }
  569. SceneStatus
  570. play_sequence_slot_reserved(const sdk::BoundView& view,
  571. std::uint32_t slotRow,
  572. const host::ScriptableOutputReservation& reservation) noexcept {
  573. std::uint32_t occurrenceRow = 0;
  574. const SceneStatus found = current_behavior_occurrence(view, slotRow, occurrenceRow);
  575. return found == SceneStatus::ready
  576. ? play_sequence_reserved(view, occurrenceRow, slotRow, reservation)
  577. : found;
  578. }
  579. SceneStatus cinematic_slot_availability(const sdk::BoundView& view,
  580. std::uint32_t slotRow) noexcept {
  581. std::uint32_t occurrenceRow = 0;
  582. const SceneStatus found = current_behavior_occurrence(view, slotRow, occurrenceRow);
  583. return found == SceneStatus::ready ? cinematic_availability(view, occurrenceRow, slotRow)
  584. : found;
  585. }
  586. /**
  587. * Resolves the slot's current occurrence, then drives its cinematic behavior.
  588. * @param reservation Output slot the caller reserved for this revision.
  589. * @return `queued` once the override is staged, or the refusal that stopped it.
  590. */
  591. SceneStatus
  592. set_cinematic_slot_active_reserved(const sdk::BoundView& view,
  593. std::uint32_t slotRow,
  594. bool active,
  595. const host::ScriptableOutputReservation& reservation) noexcept {
  596. std::uint32_t occurrenceRow = 0;
  597. const SceneStatus found = current_behavior_occurrence(view, slotRow, occurrenceRow);
  598. return found == SceneStatus::ready
  599. ? set_cinematic_active_reserved(view, occurrenceRow, slotRow, active, reservation)
  600. : found;
  601. }
  602. /** Starts one named state of the actor a type-42 sensor drives, behind its rising generation. */
  603. [[nodiscard]] static SceneStatus
  604. play_performance_reserved(const sdk::BoundView& view,
  605. std::uint32_t occurrenceRow,
  606. std::uint32_t slotRow,
  607. std::uint32_t stateNameHash,
  608. const host::ScriptableOutputReservation& reservation) noexcept {
  609. PreparedScene prepared{};
  610. const SceneStatus status = prepare_typed_behavior(view,
  611. occurrenceRow,
  612. slotRow,
  613. sdk::format::kPerformanceSlotType,
  614. sdk::format::kPerformanceComponentClass,
  615. sdk::format::kPerformanceAuthSchema,
  616. false,
  617. prepared);
  618. if (status != SceneStatus::ready) {
  619. return status;
  620. }
  621. return server::bap::request_activity_state_local_performance_override(
  622. view.binding,
  623. prepared.target,
  624. prepared.rosterGroup,
  625. stateNameHash,
  626. prepared.effectiveRegion,
  627. prepared.activityClientGeneration,
  628. &reservation)
  629. ? SceneStatus::queued
  630. : SceneStatus::refused;
  631. }
  632. /** Starts one state on the squad the sensor drives, through the slot's current occurrence. */
  633. SceneStatus
  634. play_performance_slot_reserved(const sdk::BoundView& view,
  635. std::uint32_t slotRow,
  636. std::uint32_t stateNameHash,
  637. const host::ScriptableOutputReservation& reservation) noexcept {
  638. std::uint32_t occurrenceRow = 0;
  639. const SceneStatus found = current_behavior_occurrence(view, slotRow, occurrenceRow);
  640. return found == SceneStatus::ready
  641. ? play_performance_reserved(view, occurrenceRow, slotRow, stateNameHash, reservation)
  642. : found;
  643. }
  644. /** Starts one authored state on the squad a type-42 sensor drives, for an operator action. */
  645. SceneStatus play_performance_slot(const sdk::BoundView& view,
  646. std::uint32_t slotRow,
  647. std::uint32_t stateNameHash) noexcept {
  648. std::uint32_t occurrenceRow = 0;
  649. const SceneStatus found = current_behavior_occurrence(view, slotRow, occurrenceRow);
  650. if (found != SceneStatus::ready) {
  651. return found;
  652. }
  653. PreparedScene prepared{};
  654. const SceneStatus status = prepare_typed_behavior(view,
  655. occurrenceRow,
  656. slotRow,
  657. sdk::format::kPerformanceSlotType,
  658. sdk::format::kPerformanceComponentClass,
  659. sdk::format::kPerformanceAuthSchema,
  660. false,
  661. prepared);
  662. if (status != SceneStatus::ready) {
  663. return status;
  664. }
  665. return server::bap::request_activity_state_local_performance_override(
  666. view.binding,
  667. prepared.target,
  668. prepared.rosterGroup,
  669. stateNameHash,
  670. prepared.effectiveRegion,
  671. prepared.activityClientGeneration)
  672. ? SceneStatus::queued
  673. : SceneStatus::refused;
  674. }
  675. SceneStatus task_slot_availability(const sdk::BoundView& view, std::uint32_t slotRow) noexcept {
  676. std::uint32_t occurrenceRow = 0;
  677. const SceneStatus found = current_behavior_occurrence(view, slotRow, occurrenceRow);
  678. return found == SceneStatus::ready ? task_availability(view, occurrenceRow, slotRow) : found;
  679. }
  680. SceneStatus
  681. activate_task_slot_reserved(const sdk::BoundView& view,
  682. std::uint32_t slotRow,
  683. const host::ScriptableOutputReservation& reservation) noexcept {
  684. std::uint32_t occurrenceRow = 0;
  685. const SceneStatus found = current_behavior_occurrence(view, slotRow, occurrenceRow);
  686. return found == SceneStatus::ready
  687. ? activate_task_reserved(view, occurrenceRow, slotRow, reservation)
  688. : found;
  689. }
  690. SceneStatus dialogue_cue_slot_availability(const sdk::BoundView& view,
  691. std::uint32_t slotRow,
  692. std::uint16_t cueIndex) noexcept {
  693. std::uint32_t occurrenceRow = 0;
  694. const SceneStatus found = current_behavior_occurrence(view, slotRow, occurrenceRow);
  695. return found == SceneStatus::ready
  696. ? dialogue_cue_availability(view, occurrenceRow, slotRow, cueIndex)
  697. : found;
  698. }
  699. /**
  700. * Resolves the slot's current occurrence, then queues its authored dialogue cue.
  701. * @param reservation Output slot the caller reserved for this revision.
  702. * @param filterSlotRow Type-60 volume the line waits for, or none to play at once.
  703. * @return `queued` once the cue is staged, or the refusal that stopped it.
  704. */
  705. SceneStatus play_dialogue_cue_slot_reserved(const sdk::BoundView& view,
  706. std::uint32_t slotRow,
  707. std::uint16_t cueIndex,
  708. const host::ScriptableOutputReservation& reservation,
  709. std::optional<std::uint32_t> filterSlotRow) noexcept {
  710. std::uint32_t occurrenceRow = 0;
  711. const SceneStatus found = current_behavior_occurrence(view, slotRow, occurrenceRow);
  712. return found == SceneStatus::ready
  713. ? play_dialogue_cue_reserved(
  714. view, occurrenceRow, slotRow, cueIndex, reservation, filterSlotRow)
  715. : found;
  716. }
  717. /** Queues one preflighted authored scene only through an exact unarmed Host revision. */
  718. SceneStatus activate_authored_scene_reserved(const sdk::BoundView& view,
  719. std::uint32_t occurrenceRow,
  720. std::uint32_t slotRow,
  721. const host::ScriptableOutputReservation& reservation,
  722. std::uint32_t eventKey,
  723. bool stop) noexcept {
  724. PreparedScene prepared{};
  725. const SceneStatus status = prepare_scene(view, occurrenceRow, slotRow, prepared);
  726. if (status != SceneStatus::ready) {
  727. return status;
  728. }
  729. if (prepared.omittedParticipants != 0) {
  730. log_omitted_participants(*view.catalog, slotRow, prepared.omittedParticipants);
  731. }
  732. if (server::bap::request_activity_state_local_authored_scene_override(
  733. view.binding,
  734. prepared.target,
  735. prepared.rosterGroup,
  736. prepared.effectiveRegion,
  737. prepared.activityClientGeneration,
  738. &reservation,
  739. prepared.sceneDependencies,
  740. eventKey,
  741. stop)) {
  742. return SceneStatus::queued;
  743. }
  744. return SceneStatus::refused;
  745. }
  746. /** Returns stable concise text for one mission runtime result. */
  747. const char* status_name(Status status) noexcept {
  748. switch (status) {
  749. case Status::ready:
  750. return "ready";
  751. case Status::invalidView:
  752. return "invalid_view";
  753. case Status::staleBinding:
  754. return "stale_binding";
  755. case Status::staleActivityClient:
  756. return "stale_activity_client";
  757. case Status::noActivityLink:
  758. return "no_activity_link";
  759. case Status::missingLiveSliceSet:
  760. return "missing_live_slice_set";
  761. case Status::wrongScenario:
  762. return "wrong_scenario";
  763. case Status::wrongSliceSet:
  764. return "wrong_slice_set";
  765. case Status::missingInitialState:
  766. return "missing_initial_state";
  767. case Status::ambiguousInitialState:
  768. return "ambiguous_initial_state";
  769. case Status::invalidOccurrence:
  770. return "invalid_occurrence";
  771. case Status::schemaJoinNotExact:
  772. return "schema_join_not_exact";
  773. case Status::invalidRosterGroup:
  774. return "invalid_roster_group";
  775. case Status::rosterKeyConflict:
  776. return "roster_key_conflict";
  777. case Status::groupCapacityExceeded:
  778. return "group_capacity_exceeded";
  779. case Status::outputBusy:
  780. return "output_busy";
  781. case Status::refused:
  782. return "refused";
  783. }
  784. return "refused";
  785. }
  786. /** Returns stable concise text for one authored-scene result. */
  787. const char* status_name(SceneStatus status) noexcept {
  788. switch (status) {
  789. case SceneStatus::ready:
  790. return "ready";
  791. case SceneStatus::queued:
  792. return "queued";
  793. case SceneStatus::invalidView:
  794. return "invalid_view";
  795. case SceneStatus::staleBinding:
  796. return "stale_binding";
  797. case SceneStatus::staleActivityClient:
  798. return "stale_activity_client";
  799. case SceneStatus::noActivityLink:
  800. return "no_activity_link";
  801. case SceneStatus::invalidOccurrence:
  802. return "invalid_occurrence";
  803. case SceneStatus::invalidSlot:
  804. return "invalid_slot";
  805. case SceneStatus::wrongScenario:
  806. return "wrong_scenario";
  807. case SceneStatus::wrongState:
  808. return "wrong_state";
  809. case SceneStatus::schemaJoinNotExact:
  810. return "schema_join_not_exact";
  811. case SceneStatus::missingResource:
  812. return "missing_resource";
  813. case SceneStatus::ambiguousResource:
  814. return "ambiguous_resource";
  815. case SceneStatus::targetUnavailable:
  816. return "target_unavailable";
  817. case SceneStatus::ambiguousTarget:
  818. return "ambiguous_target";
  819. case SceneStatus::missionSeedUnavailable:
  820. return "mission_seed_unavailable";
  821. case SceneStatus::missionSeedPending:
  822. return "mission_seed_pending";
  823. case SceneStatus::outputBusy:
  824. return "output_busy";
  825. case SceneStatus::dependencyCapacity:
  826. return "dependency_capacity";
  827. case SceneStatus::refused:
  828. return "refused";
  829. }
  830. return "refused";
  831. }
  832. } // namespace sunrise::server::activity::activity_sdk_mission