activity_sdk_mission_runtime.cpp 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863
  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.sceneDependencies.count == 0 && prepared.castCount != 0) {
  119. log_omitted_scene_dependencies(*view.catalog, slotRow, prepared.castCount);
  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. SceneStatus directive_availability(const sdk::BoundView& view,
  167. std::uint32_t occurrenceRow,
  168. std::uint32_t slotRow,
  169. std::uint32_t nameHash,
  170. std::int32_t elementIndex) noexcept {
  171. PreparedScene prepared{};
  172. return prepare_directive(view, occurrenceRow, slotRow, nameHash, elementIndex, prepared);
  173. }
  174. /** Sets one HUD directive element's state and visibility on a prepared objective slot. */
  175. SceneStatus set_directive(const sdk::BoundView& view,
  176. std::uint32_t occurrenceRow,
  177. std::uint32_t slotRow,
  178. std::uint32_t nameHash,
  179. std::int32_t elementIndex,
  180. std::int8_t state,
  181. bool visible) noexcept {
  182. PreparedScene prepared{};
  183. const SceneStatus status =
  184. prepare_directive(view, occurrenceRow, slotRow, nameHash, elementIndex, prepared);
  185. if (status != SceneStatus::ready) {
  186. return status;
  187. }
  188. middleware::bap::activity_message::scriptable_auth::Type68Preset preset{
  189. .nameHash = nameHash, .elementIndex = elementIndex, .state = state, .visible = visible};
  190. std::array<std::byte, middleware::bap::activity_message::scriptable_auth::kType68ByteCount>
  191. body{};
  192. std::size_t written = 0;
  193. if (!middleware::bap::activity_message::scriptable_auth::encode_type68(preset, body, written)
  194. || written != body.size()) {
  195. return SceneStatus::invalidSlot;
  196. }
  197. return server::bap::request_activity_sdk_auth_override(
  198. view.binding,
  199. prepared.target,
  200. &prepared.rosterGroup,
  201. body,
  202. middleware::bap::activity_message::scriptable_auth::kType68BitCount,
  203. prepared.effectiveRegion,
  204. prepared.activityClientGeneration)
  205. ? SceneStatus::queued
  206. : SceneStatus::refused;
  207. }
  208. /**
  209. * Queues one authored dialogue cue against a reservation a script already holds.
  210. * @param reservation Output slot the caller reserved for this revision.
  211. * @param filterSlotRow Type-60 volume the line waits for, or none to play at once.
  212. * @return `queued` once the cue is staged, or the refusal that stopped it.
  213. */
  214. [[nodiscard]] SceneStatus
  215. play_dialogue_cue_reserved(const sdk::BoundView& view,
  216. std::uint32_t occurrenceRow,
  217. std::uint32_t slotRow,
  218. std::uint16_t cueIndex,
  219. const host::ScriptableOutputReservation& reservation,
  220. std::optional<std::uint32_t> filterSlotRow) noexcept {
  221. PreparedScene prepared{};
  222. std::uint16_t authoredCueCount = 0;
  223. const SceneStatus status =
  224. prepare_dialogue(view, occurrenceRow, slotRow, cueIndex, prepared, authoredCueCount);
  225. if (status != SceneStatus::ready) {
  226. return status;
  227. }
  228. auth::Type2LaneClientRef filter{};
  229. if (filterSlotRow.has_value()) {
  230. const auto slots = view.catalog->slots();
  231. const auto objects = view.catalog->objects();
  232. if (*filterSlotRow >= slots.size()) {
  233. return SceneStatus::invalidSlot;
  234. }
  235. const sdk::format::Slot& volume = slots[*filterSlotRow];
  236. if (volume.slotType != static_cast<std::uint32_t>(auth::kType53FilterSlotType)
  237. || volume.objectIndex >= objects.size()
  238. || volume.slotIndex
  239. > static_cast<std::uint32_t>((std::numeric_limits<std::int16_t>::max)())
  240. || objects[volume.objectIndex].objectKey == 0) {
  241. return SceneStatus::invalidSlot;
  242. }
  243. filter = {objects[volume.objectIndex].objectKey,
  244. auth::kType53FilterSlotType,
  245. static_cast<std::int16_t>(volume.slotIndex)};
  246. }
  247. return server::bap::request_activity_state_local_dialogue_override(
  248. view.binding,
  249. prepared.target,
  250. prepared.rosterGroup,
  251. cueIndex,
  252. authoredCueCount,
  253. prepared.effectiveRegion,
  254. prepared.activityClientGeneration,
  255. &reservation,
  256. filter)
  257. ? SceneStatus::queued
  258. : SceneStatus::refused;
  259. }
  260. /** Checks one exact SDK-linked authored task. */
  261. SceneStatus task_availability(const sdk::BoundView& view,
  262. std::uint32_t occurrenceRow,
  263. std::uint32_t slotRow) noexcept {
  264. PreparedScene prepared{};
  265. return prepare_task(view, occurrenceRow, slotRow, prepared);
  266. }
  267. SceneStatus objective_reset_availability(const sdk::BoundView& view,
  268. std::uint32_t occurrenceRow,
  269. std::uint32_t slotRow) noexcept {
  270. PreparedScene prepared{};
  271. return prepare_objective(view, occurrenceRow, slotRow, prepared);
  272. }
  273. /** Clears every objective element of one prepared objective slot. */
  274. SceneStatus reset_objectives(const sdk::BoundView& view,
  275. std::uint32_t occurrenceRow,
  276. std::uint32_t slotRow) noexcept {
  277. PreparedScene prepared{};
  278. const SceneStatus status = prepare_objective(view, occurrenceRow, slotRow, prepared);
  279. if (status != SceneStatus::ready) {
  280. return status;
  281. }
  282. if (server::bap::request_activity_state_local_objective_reset(
  283. view.binding,
  284. prepared.target,
  285. prepared.rosterGroup,
  286. prepared.effectiveRegion,
  287. prepared.activityClientGeneration)) {
  288. return SceneStatus::queued;
  289. }
  290. return SceneStatus::refused;
  291. }
  292. /** Queues one exact objective reset using a caller-owned output reservation. */
  293. [[nodiscard]] SceneStatus
  294. reset_objectives_reserved(const sdk::BoundView& view,
  295. std::uint32_t occurrenceRow,
  296. std::uint32_t slotRow,
  297. const host::ScriptableOutputReservation& reservation) noexcept {
  298. PreparedScene prepared{};
  299. const SceneStatus status = prepare_objective(view, occurrenceRow, slotRow, prepared);
  300. if (status != SceneStatus::ready) {
  301. return status;
  302. }
  303. return server::bap::request_activity_state_local_objective_reset(
  304. view.binding,
  305. prepared.target,
  306. prepared.rosterGroup,
  307. prepared.effectiveRegion,
  308. prepared.activityClientGeneration,
  309. &reservation)
  310. ? SceneStatus::queued
  311. : SceneStatus::refused;
  312. }
  313. /** Queues the next generation through the dedicated type-38 encoder. */
  314. SceneStatus activate_task(const sdk::BoundView& view,
  315. std::uint32_t occurrenceRow,
  316. std::uint32_t slotRow) noexcept {
  317. PreparedScene prepared{};
  318. const SceneStatus status = prepare_task(view, occurrenceRow, slotRow, prepared);
  319. if (status != SceneStatus::ready) {
  320. return status;
  321. }
  322. if (server::bap::request_activity_state_local_task_override(
  323. view.binding,
  324. prepared.target,
  325. prepared.rosterGroup,
  326. prepared.effectiveRegion,
  327. prepared.activityClientGeneration)) {
  328. return SceneStatus::queued;
  329. }
  330. return SceneStatus::refused;
  331. }
  332. /**
  333. * Activates one authored task against a reservation a script already holds.
  334. * @param reservation Output slot the caller reserved for this revision.
  335. * @return `queued` once the task override is staged, or the refusal that stopped it.
  336. */
  337. [[nodiscard]] SceneStatus
  338. activate_task_reserved(const sdk::BoundView& view,
  339. std::uint32_t occurrenceRow,
  340. std::uint32_t slotRow,
  341. const host::ScriptableOutputReservation& reservation) noexcept {
  342. PreparedScene prepared{};
  343. const SceneStatus status = prepare_task(view, occurrenceRow, slotRow, prepared);
  344. if (status != SceneStatus::ready) {
  345. return status;
  346. }
  347. return server::bap::request_activity_state_local_task_override(
  348. view.binding,
  349. prepared.target,
  350. prepared.rosterGroup,
  351. prepared.effectiveRegion,
  352. prepared.activityClientGeneration,
  353. &reservation)
  354. ? SceneStatus::queued
  355. : SceneStatus::refused;
  356. }
  357. /** @return Whether one occurrence's sequence slot can be played right now. */
  358. SceneStatus sequence_availability(const sdk::BoundView& view,
  359. std::uint32_t occurrenceRow,
  360. std::uint32_t slotRow) noexcept {
  361. PreparedScene prepared{};
  362. return prepare_typed_behavior(view,
  363. occurrenceRow,
  364. slotRow,
  365. sdk::format::kSequenceSlotType,
  366. sdk::format::kSequenceComponentClass,
  367. sdk::format::kSequenceAuthSchema,
  368. false,
  369. prepared);
  370. }
  371. /** Plays one sequence slot of a prepared behavior occurrence. */
  372. SceneStatus play_sequence(const sdk::BoundView& view,
  373. std::uint32_t occurrenceRow,
  374. std::uint32_t slotRow) noexcept {
  375. PreparedScene prepared{};
  376. const SceneStatus status = 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. if (status != SceneStatus::ready) {
  385. return status;
  386. }
  387. if (server::bap::request_activity_state_local_sequence_override(
  388. view.binding,
  389. prepared.target,
  390. prepared.rosterGroup,
  391. prepared.effectiveRegion,
  392. prepared.activityClientGeneration)) {
  393. return SceneStatus::queued;
  394. }
  395. return SceneStatus::refused;
  396. }
  397. /** @return Whether one occurrence's cinematic slot can be driven right now. */
  398. SceneStatus cinematic_availability(const sdk::BoundView& view,
  399. std::uint32_t occurrenceRow,
  400. std::uint32_t slotRow) noexcept {
  401. PreparedScene prepared{};
  402. return prepare_typed_behavior(view,
  403. occurrenceRow,
  404. slotRow,
  405. sdk::format::kCinematicSlotType,
  406. sdk::format::kCinematicComponentClass,
  407. sdk::format::kCinematicAuthSchema,
  408. false,
  409. prepared);
  410. }
  411. /** Sets one cinematic slot active or inactive on a prepared behavior occurrence. */
  412. SceneStatus set_cinematic_active(const sdk::BoundView& view,
  413. std::uint32_t occurrenceRow,
  414. std::uint32_t slotRow,
  415. bool active) noexcept {
  416. PreparedScene prepared{};
  417. const SceneStatus status = prepare_typed_behavior(view,
  418. occurrenceRow,
  419. slotRow,
  420. sdk::format::kCinematicSlotType,
  421. sdk::format::kCinematicComponentClass,
  422. sdk::format::kCinematicAuthSchema,
  423. false,
  424. prepared);
  425. if (status != SceneStatus::ready) {
  426. return status;
  427. }
  428. if (server::bap::request_activity_state_local_cinematic_override(
  429. view.binding,
  430. prepared.target,
  431. prepared.rosterGroup,
  432. active,
  433. prepared.effectiveRegion,
  434. prepared.activityClientGeneration)) {
  435. return SceneStatus::queued;
  436. }
  437. return SceneStatus::refused;
  438. }
  439. /** Plays one sequence slot against an already-reserved Host output revision. */
  440. SceneStatus play_sequence_reserved(const sdk::BoundView& view,
  441. std::uint32_t occurrenceRow,
  442. std::uint32_t slotRow,
  443. const host::ScriptableOutputReservation& reservation) noexcept {
  444. PreparedScene prepared{};
  445. const SceneStatus status = prepare_typed_behavior(view,
  446. occurrenceRow,
  447. slotRow,
  448. sdk::format::kSequenceSlotType,
  449. sdk::format::kSequenceComponentClass,
  450. sdk::format::kSequenceAuthSchema,
  451. false,
  452. prepared);
  453. if (status != SceneStatus::ready) {
  454. return status;
  455. }
  456. return server::bap::request_activity_state_local_sequence_override(
  457. view.binding,
  458. prepared.target,
  459. prepared.rosterGroup,
  460. prepared.effectiveRegion,
  461. prepared.activityClientGeneration,
  462. &reservation)
  463. ? SceneStatus::queued
  464. : SceneStatus::refused;
  465. }
  466. /**
  467. * Drives one cinematic behavior against a reservation a script already holds.
  468. * @param reservation Output slot the caller reserved for this revision.
  469. * @return `queued` once the override is staged, or the refusal that stopped it.
  470. */
  471. SceneStatus
  472. set_cinematic_active_reserved(const sdk::BoundView& view,
  473. std::uint32_t occurrenceRow,
  474. std::uint32_t slotRow,
  475. bool active,
  476. const host::ScriptableOutputReservation& reservation) noexcept {
  477. PreparedScene prepared{};
  478. const SceneStatus status = prepare_typed_behavior(view,
  479. occurrenceRow,
  480. slotRow,
  481. sdk::format::kCinematicSlotType,
  482. sdk::format::kCinematicComponentClass,
  483. sdk::format::kCinematicAuthSchema,
  484. false,
  485. prepared);
  486. if (status != SceneStatus::ready) {
  487. return status;
  488. }
  489. return server::bap::request_activity_state_local_cinematic_override(
  490. view.binding,
  491. prepared.target,
  492. prepared.rosterGroup,
  493. active,
  494. prepared.effectiveRegion,
  495. prepared.activityClientGeneration,
  496. &reservation)
  497. ? SceneStatus::queued
  498. : SceneStatus::refused;
  499. }
  500. /** Finds the occurrence a slot currently belongs to. @return `invalidView` on a stale view. */
  501. [[nodiscard]] static SceneStatus current_behavior_occurrence(
  502. const sdk::BoundView& view, std::uint32_t slotRow, std::uint32_t& occurrenceRow) noexcept {
  503. occurrenceRow = sdk::format::kAbsentIndex;
  504. Snapshot snapshot{};
  505. if (query(view, snapshot) != Status::ready || view.catalog == nullptr) {
  506. return SceneStatus::invalidView;
  507. }
  508. const auto slots = view.catalog->slots();
  509. const auto occurrences = view.catalog->occurrences();
  510. if (slotRow >= slots.size()) {
  511. return SceneStatus::invalidSlot;
  512. }
  513. server::bap::ActivityLinkView link{};
  514. const SceneStatus live = scene_binding_status(view, link);
  515. if (live != SceneStatus::ready) {
  516. return live;
  517. }
  518. const auto selected = behavior_scope::select(occurrences,
  519. view.catalog->states(),
  520. view.catalog->bubbles(),
  521. view.scenarioRow,
  522. slots[slotRow].objectIndex,
  523. snapshot.plan.stateRow,
  524. link.effectiveRegion);
  525. if (selected.ambiguous) {
  526. return SceneStatus::ambiguousTarget;
  527. }
  528. occurrenceRow = selected.row;
  529. return occurrenceRow == sdk::format::kAbsentIndex ? SceneStatus::targetUnavailable
  530. : SceneStatus::ready;
  531. }
  532. SceneStatus sequence_slot_availability(const sdk::BoundView& view, std::uint32_t slotRow) noexcept {
  533. std::uint32_t occurrenceRow = 0;
  534. const SceneStatus found = current_behavior_occurrence(view, slotRow, occurrenceRow);
  535. return found == SceneStatus::ready ? sequence_availability(view, occurrenceRow, slotRow)
  536. : found;
  537. }
  538. SceneStatus objective_reset_slot_availability(const sdk::BoundView& view,
  539. std::uint32_t slotRow) noexcept {
  540. std::uint32_t occurrenceRow = 0;
  541. const SceneStatus found = current_behavior_occurrence(view, slotRow, occurrenceRow);
  542. return found == SceneStatus::ready ? objective_reset_availability(view, occurrenceRow, slotRow)
  543. : found;
  544. }
  545. SceneStatus
  546. reset_objectives_slot_reserved(const sdk::BoundView& view,
  547. std::uint32_t slotRow,
  548. const host::ScriptableOutputReservation& reservation) noexcept {
  549. std::uint32_t occurrenceRow = 0;
  550. const SceneStatus found = current_behavior_occurrence(view, slotRow, occurrenceRow);
  551. return found == SceneStatus::ready
  552. ? reset_objectives_reserved(view, occurrenceRow, slotRow, reservation)
  553. : found;
  554. }
  555. SceneStatus
  556. play_sequence_slot_reserved(const sdk::BoundView& view,
  557. std::uint32_t slotRow,
  558. const host::ScriptableOutputReservation& reservation) noexcept {
  559. std::uint32_t occurrenceRow = 0;
  560. const SceneStatus found = current_behavior_occurrence(view, slotRow, occurrenceRow);
  561. return found == SceneStatus::ready
  562. ? play_sequence_reserved(view, occurrenceRow, slotRow, reservation)
  563. : found;
  564. }
  565. SceneStatus cinematic_slot_availability(const sdk::BoundView& view,
  566. std::uint32_t slotRow) noexcept {
  567. std::uint32_t occurrenceRow = 0;
  568. const SceneStatus found = current_behavior_occurrence(view, slotRow, occurrenceRow);
  569. return found == SceneStatus::ready ? cinematic_availability(view, occurrenceRow, slotRow)
  570. : found;
  571. }
  572. /**
  573. * Resolves the slot's current occurrence, then drives its cinematic behavior.
  574. * @param reservation Output slot the caller reserved for this revision.
  575. * @return `queued` once the override is staged, or the refusal that stopped it.
  576. */
  577. SceneStatus
  578. set_cinematic_slot_active_reserved(const sdk::BoundView& view,
  579. std::uint32_t slotRow,
  580. bool active,
  581. const host::ScriptableOutputReservation& reservation) noexcept {
  582. std::uint32_t occurrenceRow = 0;
  583. const SceneStatus found = current_behavior_occurrence(view, slotRow, occurrenceRow);
  584. return found == SceneStatus::ready
  585. ? set_cinematic_active_reserved(view, occurrenceRow, slotRow, active, reservation)
  586. : found;
  587. }
  588. /** Starts one named state of the actor a type-42 sensor drives, behind its rising generation. */
  589. [[nodiscard]] static SceneStatus
  590. play_performance_reserved(const sdk::BoundView& view,
  591. std::uint32_t occurrenceRow,
  592. std::uint32_t slotRow,
  593. std::uint32_t stateNameHash,
  594. const host::ScriptableOutputReservation& reservation) noexcept {
  595. PreparedScene prepared{};
  596. const SceneStatus status = prepare_typed_behavior(view,
  597. occurrenceRow,
  598. slotRow,
  599. sdk::format::kPerformanceSlotType,
  600. sdk::format::kPerformanceComponentClass,
  601. sdk::format::kPerformanceAuthSchema,
  602. false,
  603. prepared);
  604. if (status != SceneStatus::ready) {
  605. return status;
  606. }
  607. return server::bap::request_activity_state_local_performance_override(
  608. view.binding,
  609. prepared.target,
  610. prepared.rosterGroup,
  611. stateNameHash,
  612. prepared.effectiveRegion,
  613. prepared.activityClientGeneration,
  614. &reservation)
  615. ? SceneStatus::queued
  616. : SceneStatus::refused;
  617. }
  618. /** Starts one state on the squad the sensor drives, through the slot's current occurrence. */
  619. SceneStatus
  620. play_performance_slot_reserved(const sdk::BoundView& view,
  621. std::uint32_t slotRow,
  622. std::uint32_t stateNameHash,
  623. const host::ScriptableOutputReservation& reservation) noexcept {
  624. std::uint32_t occurrenceRow = 0;
  625. const SceneStatus found = current_behavior_occurrence(view, slotRow, occurrenceRow);
  626. return found == SceneStatus::ready
  627. ? play_performance_reserved(view, occurrenceRow, slotRow, stateNameHash, reservation)
  628. : found;
  629. }
  630. /** Starts one authored state on the squad a type-42 sensor drives, for an operator action. */
  631. SceneStatus play_performance_slot(const sdk::BoundView& view,
  632. std::uint32_t slotRow,
  633. std::uint32_t stateNameHash) noexcept {
  634. std::uint32_t occurrenceRow = 0;
  635. const SceneStatus found = current_behavior_occurrence(view, slotRow, occurrenceRow);
  636. if (found != SceneStatus::ready) {
  637. return found;
  638. }
  639. PreparedScene prepared{};
  640. const SceneStatus status = prepare_typed_behavior(view,
  641. occurrenceRow,
  642. slotRow,
  643. sdk::format::kPerformanceSlotType,
  644. sdk::format::kPerformanceComponentClass,
  645. sdk::format::kPerformanceAuthSchema,
  646. false,
  647. prepared);
  648. if (status != SceneStatus::ready) {
  649. return status;
  650. }
  651. return server::bap::request_activity_state_local_performance_override(
  652. view.binding,
  653. prepared.target,
  654. prepared.rosterGroup,
  655. stateNameHash,
  656. prepared.effectiveRegion,
  657. prepared.activityClientGeneration)
  658. ? SceneStatus::queued
  659. : SceneStatus::refused;
  660. }
  661. SceneStatus task_slot_availability(const sdk::BoundView& view, std::uint32_t slotRow) noexcept {
  662. std::uint32_t occurrenceRow = 0;
  663. const SceneStatus found = current_behavior_occurrence(view, slotRow, occurrenceRow);
  664. return found == SceneStatus::ready ? task_availability(view, occurrenceRow, slotRow) : found;
  665. }
  666. SceneStatus
  667. activate_task_slot_reserved(const sdk::BoundView& view,
  668. std::uint32_t slotRow,
  669. const host::ScriptableOutputReservation& reservation) noexcept {
  670. std::uint32_t occurrenceRow = 0;
  671. const SceneStatus found = current_behavior_occurrence(view, slotRow, occurrenceRow);
  672. return found == SceneStatus::ready
  673. ? activate_task_reserved(view, occurrenceRow, slotRow, reservation)
  674. : found;
  675. }
  676. SceneStatus dialogue_cue_slot_availability(const sdk::BoundView& view,
  677. std::uint32_t slotRow,
  678. std::uint16_t cueIndex) noexcept {
  679. std::uint32_t occurrenceRow = 0;
  680. const SceneStatus found = current_behavior_occurrence(view, slotRow, occurrenceRow);
  681. return found == SceneStatus::ready
  682. ? dialogue_cue_availability(view, occurrenceRow, slotRow, cueIndex)
  683. : found;
  684. }
  685. /**
  686. * Resolves the slot's current occurrence, then queues its authored dialogue cue.
  687. * @param reservation Output slot the caller reserved for this revision.
  688. * @param filterSlotRow Type-60 volume the line waits for, or none to play at once.
  689. * @return `queued` once the cue is staged, or the refusal that stopped it.
  690. */
  691. SceneStatus play_dialogue_cue_slot_reserved(const sdk::BoundView& view,
  692. std::uint32_t slotRow,
  693. std::uint16_t cueIndex,
  694. const host::ScriptableOutputReservation& reservation,
  695. std::optional<std::uint32_t> filterSlotRow) noexcept {
  696. std::uint32_t occurrenceRow = 0;
  697. const SceneStatus found = current_behavior_occurrence(view, slotRow, occurrenceRow);
  698. return found == SceneStatus::ready
  699. ? play_dialogue_cue_reserved(
  700. view, occurrenceRow, slotRow, cueIndex, reservation, filterSlotRow)
  701. : found;
  702. }
  703. /** Queues one preflighted authored scene only through an exact unarmed Host revision. */
  704. SceneStatus activate_authored_scene_reserved(const sdk::BoundView& view,
  705. std::uint32_t occurrenceRow,
  706. std::uint32_t slotRow,
  707. const host::ScriptableOutputReservation& reservation,
  708. std::uint32_t eventKey,
  709. bool stop) noexcept {
  710. PreparedScene prepared{};
  711. const SceneStatus status = prepare_scene(view, occurrenceRow, slotRow, prepared);
  712. if (status != SceneStatus::ready) {
  713. return status;
  714. }
  715. if (prepared.sceneDependencies.count == 0 && prepared.castCount != 0) {
  716. log_omitted_scene_dependencies(*view.catalog, slotRow, prepared.castCount);
  717. }
  718. if (server::bap::request_activity_state_local_authored_scene_override(
  719. view.binding,
  720. prepared.target,
  721. prepared.rosterGroup,
  722. prepared.effectiveRegion,
  723. prepared.activityClientGeneration,
  724. &reservation,
  725. prepared.sceneDependencies,
  726. eventKey,
  727. stop)) {
  728. return SceneStatus::queued;
  729. }
  730. return SceneStatus::refused;
  731. }
  732. /** Returns stable concise text for one mission runtime result. */
  733. const char* status_name(Status status) noexcept {
  734. switch (status) {
  735. case Status::ready:
  736. return "ready";
  737. case Status::invalidView:
  738. return "invalid_view";
  739. case Status::staleBinding:
  740. return "stale_binding";
  741. case Status::staleActivityClient:
  742. return "stale_activity_client";
  743. case Status::noActivityLink:
  744. return "no_activity_link";
  745. case Status::missingLiveSliceSet:
  746. return "missing_live_slice_set";
  747. case Status::wrongScenario:
  748. return "wrong_scenario";
  749. case Status::wrongSliceSet:
  750. return "wrong_slice_set";
  751. case Status::missingInitialState:
  752. return "missing_initial_state";
  753. case Status::ambiguousInitialState:
  754. return "ambiguous_initial_state";
  755. case Status::invalidOccurrence:
  756. return "invalid_occurrence";
  757. case Status::schemaJoinNotExact:
  758. return "schema_join_not_exact";
  759. case Status::invalidRosterGroup:
  760. return "invalid_roster_group";
  761. case Status::rosterKeyConflict:
  762. return "roster_key_conflict";
  763. case Status::groupCapacityExceeded:
  764. return "group_capacity_exceeded";
  765. case Status::outputBusy:
  766. return "output_busy";
  767. case Status::refused:
  768. return "refused";
  769. }
  770. return "refused";
  771. }
  772. /** Returns stable concise text for one authored-scene result. */
  773. const char* status_name(SceneStatus status) noexcept {
  774. switch (status) {
  775. case SceneStatus::ready:
  776. return "ready";
  777. case SceneStatus::queued:
  778. return "queued";
  779. case SceneStatus::invalidView:
  780. return "invalid_view";
  781. case SceneStatus::staleBinding:
  782. return "stale_binding";
  783. case SceneStatus::staleActivityClient:
  784. return "stale_activity_client";
  785. case SceneStatus::noActivityLink:
  786. return "no_activity_link";
  787. case SceneStatus::invalidOccurrence:
  788. return "invalid_occurrence";
  789. case SceneStatus::invalidSlot:
  790. return "invalid_slot";
  791. case SceneStatus::wrongScenario:
  792. return "wrong_scenario";
  793. case SceneStatus::wrongState:
  794. return "wrong_state";
  795. case SceneStatus::schemaJoinNotExact:
  796. return "schema_join_not_exact";
  797. case SceneStatus::missingResource:
  798. return "missing_resource";
  799. case SceneStatus::ambiguousResource:
  800. return "ambiguous_resource";
  801. case SceneStatus::targetUnavailable:
  802. return "target_unavailable";
  803. case SceneStatus::ambiguousTarget:
  804. return "ambiguous_target";
  805. case SceneStatus::missionSeedUnavailable:
  806. return "mission_seed_unavailable";
  807. case SceneStatus::missionSeedPending:
  808. return "mission_seed_pending";
  809. case SceneStatus::outputBusy:
  810. return "output_busy";
  811. case SceneStatus::refused:
  812. return "refused";
  813. }
  814. return "refused";
  815. }
  816. } // namespace sunrise::server::activity::activity_sdk_mission