| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752 |
- #include <algorithm>
- #include <array>
- #include <cstddef>
- #include <cstdint>
- #include <cstdio>
- #include <limits>
- #include <span>
- #include <string_view>
- #include "../../../middleware/content/packages/tables/scenario_reader.h"
- #include "../../../state/activity/membership/activity_membership_query.h"
- #include "../../../state/build_data/runtime.h"
- #include "../../../state/build_data/spawn_sets/spawn_set_catalog.h"
- #include "../activity_sdk_device_runtime.h"
- #include "../activity_sdk_lifetime_runtime.h"
- #include "../activity_sdk_mission_runtime.h"
- #include "../activity_sdk_squad_runtime.h"
- #include "mission_script_runtime.h"
- #include "mission_script_runtime_internal.h"
- // The intent fan-out reserves one Host output revision, then asks one typed adapter to encode it.
- namespace sunrise::server::activity::mission {
- namespace {
- namespace devices = activity_sdk_devices;
- namespace lifetimes = activity_sdk_lifetime;
- namespace scenes = activity_sdk_mission;
- namespace squads = activity_sdk_squads;
- namespace scriptable_auth = middleware::bap::activity_message::scriptable_auth;
- namespace squad_auth = middleware::bap::activity_message::squad_auth;
- const void* g_actorCommandPolicyContext{};
- ActorCommandPolicy g_actorCommandPolicy{};
- /** Resolves one durable selector against the current pinned SDK. */
- [[nodiscard]] ActorCommandPolicyStatus
- dispatch_actor_command(const RuntimeInstance& instance, const lua_vm::Intent& intent) noexcept {
- if (instance.view.catalog == nullptr) {
- return ActorCommandPolicyStatus::refused;
- }
- const sdk::Snapshot published = sdk::snapshot();
- if (published == nullptr) {
- return ActorCommandPolicyStatus::refused;
- }
- const std::span<const std::byte> sdkBuild = published->sdk_build_sha256();
- if (sdkBuild.size() != intent.sdkBuildSha256.size()
- || !std::equal(sdkBuild.begin(), sdkBuild.end(), intent.sdkBuildSha256.begin())
- || instance.view.catalog->sdk_build_sha256().size() != sdkBuild.size()
- || !std::equal(
- sdkBuild.begin(), sdkBuild.end(), instance.view.catalog->sdk_build_sha256().begin())) {
- return ActorCommandPolicyStatus::refused;
- }
- const auto squads = published->squads();
- if (intent.firstRow >= squads.size()
- || (squads[intent.firstRow].flags & format::kSquadRunnableMask)
- != format::kSquadRunnableMask) {
- return ActorCommandPolicyStatus::refused;
- }
- const auto commands = published->actor_command_definitions();
- for (std::size_t index = 0; index < commands.size(); ++index) {
- const format::ActorCommandDefinition& command = commands[index];
- if (command.selector != intent.actorCommandSelector) {
- continue;
- }
- const bool valueValid = command.effect == format::ActorCommandEffect::setFaction
- && (intent.actorCommandValue == command.factionNone
- || intent.actorCommandValue == command.factionRemoved
- || intent.actorCommandValue == command.factionHostileToAll);
- if (command.payloadHandle == 0
- || command.provenance != format::ActorSemanticProvenance::executableStatic
- || command.flags != format::kActorCommandDefinitionExact || !valueValid) {
- return ActorCommandPolicyStatus::refused;
- }
- if (g_actorCommandPolicy == nullptr) {
- return ActorCommandPolicyStatus::unavailable;
- }
- const ActorCommandPolicyRequest request{
- .binding = instance.view.binding,
- .sdkBuildSha256 = intent.sdkBuildSha256,
- .squadRow = intent.firstRow,
- .commandRow = static_cast<std::uint32_t>(index),
- .commandSelector = command.selector,
- .value = intent.actorCommandValue,
- };
- return g_actorCommandPolicy(g_actorCommandPolicyContext, request);
- }
- return ActorCommandPolicyStatus::refused;
- }
- /** Records the one deterministic execution attempt for diagnostics. */
- void begin_intent_attempt(RuntimeInstance& instance, std::uint64_t now) noexcept {
- if (instance.intentAttempts == 0) {
- instance.firstIntentAttempt = now;
- }
- ++instance.intentAttempts;
- }
- /** Assigns one exact Host output revision without clearing the durable intent. */
- [[nodiscard]] bool assign_delivery_state(RuntimeInstance& instance,
- std::uint64_t hostOutputRevision) noexcept {
- mission_state::Snapshot snapshot{};
- const mission_state::Status status =
- mission_state::assign_intent_output(instance.view.binding,
- instance.programKey,
- instance.missionStateRevision,
- instance.durableIntentSequence,
- hostOutputRevision,
- snapshot);
- if (status != mission_state::Status::ready) {
- lua_vm::fault(instance.vm, "durable mission intent assignment compare was refused");
- instance.programStatus = ProgramStatus::programError;
- log_line(
- core::log::Level::warn, &instance, "intent_assign", mission_state::status_name(status));
- return false;
- }
- accept_mission_state(instance, snapshot);
- return instance.durableHostOutputRevision == hostOutputRevision;
- }
- /** Reserves one exact Host revision, then makes the durable head own it before enqueue. */
- [[nodiscard]] bool reserve_delivery(RuntimeInstance& instance,
- host::ScriptableOutputReservation& reservation) noexcept {
- if (!host::reserve_scriptable_output(
- instance.view.binding, reservation, instance.durableIntentSequence)) {
- return false;
- }
- if (!assign_delivery_state(instance, reservation.revision)) {
- if (!host::release_scriptable_output(reservation)) {
- log_line(core::log::Level::warn, &instance, "intent_reserve", "release_refused");
- }
- persist_mission_fault(instance);
- clear_pending_events(instance.view.binding);
- clear_delivery(instance);
- return false;
- }
- instance.expectedScriptableRevision = reservation.revision;
- return true;
- }
- /** Releases State before opening the reserved Host lane after an adapter refusal. */
- [[nodiscard]] bool
- abandon_reserved_delivery(RuntimeInstance& instance,
- const host::ScriptableOutputReservation& reservation) noexcept {
- if (!release_delivery_state(instance)) {
- // Keep the Host reservation fail-closed when State cannot remove its ownership.
- persist_mission_fault(instance);
- clear_pending_events(instance.view.binding);
- clear_delivery(instance);
- return false;
- }
- if (!host::release_scriptable_output(reservation)) {
- fault_instance(instance, "Host mission output reservation release was refused");
- instance.programStatus = ProgramStatus::programError;
- log_line(core::log::Level::warn, &instance, "intent_reserve", "release_refused");
- clear_pending_events(instance.view.binding);
- clear_delivery(instance);
- return false;
- }
- return true;
- }
- /**
- * Enters the first delivery stage once an adapter has queued the request.
- * @param now Service tick the commit deadline is measured from.
- * @param result Diagnostic name of the queued request.
- */
- void await_host_commit(RuntimeInstance& instance,
- std::uint64_t now,
- std::string_view result) noexcept {
- if (instance.expectedScriptableRevision == 0
- || instance.durableHostOutputRevision != instance.expectedScriptableRevision) {
- fault_delivery(instance,
- "revision_unavailable",
- "queued intent did not match durable Host revision ownership");
- return;
- }
- instance.lastIntentStatus = (std::numeric_limits<std::uint16_t>::max)();
- instance.deliveryStage = DeliveryStage::awaitingHostCommit;
- instance.deliveryDeadline = deadline_after(now, kHostCommitTimeoutMs);
- log_line(core::log::Level::info, &instance, "intent", result);
- }
- } // namespace
- /** Installs the gameplay policy seam. */
- void install_actor_command_policy(const void* context, ActorCommandPolicy policy) noexcept {
- g_actorCommandPolicyContext = context;
- g_actorCommandPolicy = policy;
- }
- /**
- * Moves the client to the region a freshly selected mission state belongs to.
- * The client picks its object registry from the loaded slice-set entry, so a state in another
- * region has no findable objects until it transitions. Message 12 is the only mid-activity move.
- * @param instance Runtime instance whose state selection just published.
- * @param plan Published plan naming the target region and its bubble.
- */
- void arm_state_region_teleport(RuntimeInstance& instance,
- const server::bap::ActivityMissionSeedPlan& plan) noexcept {
- namespace membership = ::sunrise::state::activity::membership;
- const auto& destination = instance.view.binding.destination;
- if (destination.packageNameLength == 0
- || destination.packageNameLength > destination.packageName.size()
- || plan.effectiveRegion > static_cast<std::uint32_t>(membership::kMaximumSliceSetIndex)) {
- return;
- }
- const std::int32_t reported = membership::player_region(instance.view.binding.sessionId);
- // The client answers a slice-set transition by de-instantiating its slice set and building the
- // target, so a move inside one bubble leaves both copies alive and doubles its content. The arm
- // must stay anyway: it is what orders the spawn, and without it the client never spawns in.
- if (reported == static_cast<std::int32_t>(plan.effectiveRegion)) {
- // Already there. Clear any earlier arm so the mirror owns the block again.
- static_cast<void>(membership::arm_host_teleport(
- instance.view.binding.sessionId, membership::kAbsentSliceSetIndex, 0));
- return;
- }
- // Native 4C8E40 registers alternate entries as distinct packed regions. A sibling
- // bookend still needs travel: its cinematic controller is absent in gameplay's world.
- const std::string_view name(reinterpret_cast<const char*>(destination.packageName.data()),
- destination.packageNameLength);
- ::sunrise::state::build_data::scenarios::Definition layout{};
- if (!::sunrise::state::build_data::find_scenario_layout(name, layout)
- || plan.bubbleOrdinal >= layout.bubbleHashes.size()) {
- return;
- }
- const std::uint32_t hash = layout.bubbleHashes[plan.bubbleOrdinal];
- if (hash == 0) {
- // Without the slice-set name hash the client cannot resolve the target, and a zero would
- // arm a move it can never finish.
- return;
- }
- const bool armed = membership::arm_host_teleport(
- instance.view.binding.sessionId, static_cast<std::int32_t>(plan.effectiveRegion), hash);
- log_line(core::log::Level::info,
- &instance,
- "state_region",
- armed ? "teleport_armed" : "teleport_unchanged");
- }
- /**
- * @return True while a scene-family request is valid but its mission-seed lease has not published.
- * The request stays queued: refusing it drops an answer the client is still owed.
- */
- [[nodiscard]] bool scene_lease_still_publishing(scenes::SceneStatus status) noexcept {
- return status == scenes::SceneStatus::missionSeedPending
- || status == scenes::SceneStatus::outputBusy;
- }
- /** Raises one queued intent, or advances the delivery already in flight. */
- void dispatch_intent(RuntimeInstance& instance, std::uint64_t now) noexcept {
- if (instance.programStatus != ProgramStatus::loaded) {
- return;
- }
- lua_vm::Intent intent{};
- if (!lua_vm::pending_intent(instance.vm, intent)) {
- if (instance.deliveryStage != DeliveryStage::idle) {
- fault_delivery(instance, "missing_intent", "delivery lost its pending Lua intent");
- } else {
- clear_delivery(instance);
- }
- return;
- }
- if (intent_lifetime_expired(instance, now)) {
- if (reconcile_expired_delivery(instance)) {
- return;
- }
- refuse_delivery(instance,
- "intent_timeout",
- "intent exceeded its delivery lifetime",
- host::EffectOutcome::expired);
- return;
- }
- if (instance.deliveryStage != DeliveryStage::idle) {
- if (reconcile_transport_stage(instance)) {
- return;
- }
- static_cast<void>(service_delivery_timeout(instance, now));
- return;
- }
- begin_intent_attempt(instance, now);
- switch (intent.kind) {
- case lua_vm::IntentKind::selectMissionState: {
- scenes::Snapshot selected{};
- const scenes::Status status =
- scenes::select_state(instance.view,
- intent.effectiveRegion,
- std::span(intent.seedOmissions).first(intent.seedOmissionCount),
- selected);
- if (status == scenes::Status::outputBusy) {
- report_intent_status(
- instance, kIntentStatusSceneOutputBusy, scenes::status_name(status));
- return;
- }
- if (status != scenes::Status::ready || !selected.configured
- || selected.plan.effectiveRegion
- != static_cast<std::uint32_t>(intent.effectiveRegion)) {
- refuse_delivery(instance,
- "state_refused",
- scenes::status_name(status),
- host::EffectOutcome::refused);
- return;
- }
- // The selected effective region is an authored-state key, not a region the client reports.
- // Publishing this lease revision is the completion edge, except when publication waits for
- // arrival: there the teleport is armed first, because arrival closes that window.
- if (!selected.regionArrivalPending
- && (selected.publicationPending || selected.revision == 0
- || selected.publishedRevision != selected.revision)) {
- report_intent_status(
- instance, kIntentStatusStateTransitionPending, "state_transition_pending");
- return;
- }
- // A selected state names its own slice-set region. Until the client transitions there its
- // object registry comes from the loaded slice-set entry, so the new state's objects stay
- // unfindable. Arming the host teleport is the only mid-activity move.
- arm_state_region_teleport(instance, selected.plan);
- static_cast<void>(complete_local_effect(instance, "state_selected"));
- return;
- }
- case lua_vm::IntentKind::restartCheckpoint: {
- if (intent.checkpointReleaseRequest) {
- if (::sunrise::state::activity::membership::release_hard_wipe(instance.view.binding,
- intent.checkpointReleaseRequest))
- static_cast<void>(complete_local_effect(instance,"checkpoint_reset_ready"));
- else refuse_delivery(instance,"checkpoint_refused","wipe_not_active",host::EffectOutcome::refused);
- return;
- }
- namespace data=::sunrise::state::build_data;
- const auto& destination=instance.view.binding.destination;
- const std::string_view package(reinterpret_cast<const char*>(destination.packageName.data()),
- destination.packageNameLength);
- data::scenarios::Definition layout{};
- data::spawn_sets::NameHash spawn{};
- if (instance.publicTarget || !instance.lastFireteamLife.all_dead()
- || instance.activeRegion!=intent.effectiveRegion
- || !data::find_scenario_layout(package,layout)
- || !data::spawn_sets::find_hash({layout.spawnStem.data(),layout.spawnStemLength},
- intent.checkpointSpawnHash,spawn) || !spawn.pointCount) {
- refuse_delivery(instance,"checkpoint_refused","party_or_spawn_unavailable",host::EffectOutcome::refused);
- return;
- }
- if (::sunrise::state::activity::membership::arm_hard_wipe(instance.view.binding,
- intent.requestKey,intent.effectiveRegion,intent.checkpointSpawnHash))
- static_cast<void>(complete_local_effect(instance,"checkpoint_wipe_armed"));
- else refuse_delivery(instance,"checkpoint_refused","membership_busy",host::EffectOutcome::refused);
- return;
- }
- case lua_vm::IntentKind::placeSquad: {
- const std::span<const std::int32_t> counts(intent.squadCounts.data(), intent.squadCount);
- const auto mode = static_cast<squad_auth::Mode>(intent.squadMode);
- host::ScriptableOutputReservation reservation{};
- if (!reserve_delivery(instance, reservation)) {
- if (instance.programStatus == ProgramStatus::loaded) {
- refuse_delivery(instance,
- "squad_refused",
- "host_reservation_unavailable",
- host::EffectOutcome::refused);
- }
- return;
- }
- const squads::Status status = squads::place_reserved(instance.view,
- intent.firstRow,
- counts,
- mode,
- reservation,
- std::nullopt,
- intent.squadRetireOnReturn);
- if (status == squads::Status::queued) {
- await_host_commit(instance, now, "squad_enqueued");
- } else if (!abandon_reserved_delivery(instance, reservation)) {
- return;
- } else {
- refuse_delivery(instance,
- "squad_refused",
- squads::status_name(status),
- host::EffectOutcome::refused);
- }
- return;
- }
- case lua_vm::IntentKind::actorCommand: {
- const ActorCommandPolicyStatus status = dispatch_actor_command(instance, intent);
- if (status == ActorCommandPolicyStatus::queued) {
- static_cast<void>(complete_local_effect(instance, "actor_command_queued"));
- } else {
- refuse_delivery(instance,
- "actor_command_refused",
- status == ActorCommandPolicyStatus::unavailable
- ? "gameplay_policy_unavailable"
- : "sdk_command_refused",
- host::EffectOutcome::refused);
- }
- return;
- }
- case lua_vm::IntentKind::bindCombatantToSquad: {
- host::ScriptableOutputReservation reservation{};
- if (!reserve_delivery(instance, reservation)) {
- if (instance.programStatus == ProgramStatus::loaded) {
- refuse_delivery(instance,
- "combatant_binding_refused",
- "host_reservation_unavailable",
- host::EffectOutcome::refused);
- }
- return;
- }
- const devices::Status status =
- devices::bind_combatant_to_squad_reserved(instance.view, intent.firstRow, reservation);
- if (status == devices::Status::queued) {
- await_host_commit(instance, now, "combatant_binding_enqueued");
- } else if (!abandon_reserved_delivery(instance, reservation)) {
- return;
- } else {
- refuse_delivery(instance,
- "combatant_binding_refused",
- devices::status_name(status),
- host::EffectOutcome::refused);
- }
- return;
- }
- case lua_vm::IntentKind::setObjectActive: {
- host::ScriptableOutputReservation reservation{};
- if (!reserve_delivery(instance, reservation)) {
- if (instance.programStatus == ProgramStatus::loaded) {
- refuse_delivery(instance,
- "object_refused",
- "host_reservation_unavailable",
- host::EffectOutcome::refused);
- }
- return;
- }
- const devices::Status status = devices::set_objects_active_reserved(
- instance.view,
- intent.firstRow,
- std::span(intent.burstRows).first(intent.burstRowCount),
- intent.entryIndex,
- intent.active,
- reservation);
- if (status == devices::Status::queued) {
- await_host_commit(instance, now, "object_enqueued");
- } else if (!abandon_reserved_delivery(instance, reservation)) {
- return;
- } else {
- refuse_delivery(instance,
- "object_refused",
- devices::status_name(status),
- host::EffectOutcome::refused);
- }
- return;
- }
- case lua_vm::IntentKind::setDeviceChannel: {
- const auto channel = static_cast<scriptable_auth::Type23Channel>(intent.deviceChannel);
- host::ScriptableOutputReservation reservation{};
- if (!reserve_delivery(instance, reservation)) {
- if (instance.programStatus == ProgramStatus::loaded) {
- refuse_delivery(instance,
- "device_refused",
- "host_reservation_unavailable",
- host::EffectOutcome::refused);
- }
- return;
- }
- const devices::Status status = devices::set_channel_reserved(instance.view,
- intent.firstRow,
- channel,
- intent.deviceValue,
- intent.deviceSnap,
- reservation);
- if (status == devices::Status::queued) {
- await_host_commit(instance, now, "device_enqueued");
- } else if (!abandon_reserved_delivery(instance, reservation)) {
- return;
- } else {
- refuse_delivery(instance,
- "device_refused",
- devices::status_name(status),
- host::EffectOutcome::refused);
- }
- return;
- }
- case lua_vm::IntentKind::fireTrigger: {
- host::ScriptableOutputReservation reservation{};
- if (!reserve_delivery(instance, reservation)) {
- if (instance.programStatus == ProgramStatus::loaded) {
- refuse_delivery(instance,
- "trigger_refused",
- "host_reservation_unavailable",
- host::EffectOutcome::refused);
- }
- return;
- }
- const devices::Status status =
- devices::fire_trigger_reserved(instance.view, intent.firstRow, reservation);
- if (status == devices::Status::queued) {
- await_host_commit(instance, now, "trigger_enqueued");
- } else if (!abandon_reserved_delivery(instance, reservation)) {
- return;
- } else {
- refuse_delivery(instance,
- "trigger_refused",
- devices::status_name(status),
- host::EffectOutcome::refused);
- }
- return;
- }
- case lua_vm::IntentKind::playSequence:
- case lua_vm::IntentKind::setCinematicActive: {
- const bool sequence = intent.kind == lua_vm::IntentKind::playSequence;
- host::ScriptableOutputReservation reservation{};
- if (!reserve_delivery(instance, reservation)) {
- if (instance.programStatus == ProgramStatus::loaded) {
- refuse_delivery(instance,
- sequence ? "sequence_refused" : "cinematic_refused",
- "host_reservation_unavailable",
- host::EffectOutcome::refused);
- }
- return;
- }
- const scenes::SceneStatus status =
- sequence
- ? scenes::play_sequence_slot_reserved(instance.view, intent.firstRow, reservation)
- : scenes::set_cinematic_slot_active_reserved(
- instance.view, intent.firstRow, intent.active, reservation);
- if (status == scenes::SceneStatus::queued) {
- await_host_commit(instance, now, sequence ? "sequence_enqueued" : "cinematic_enqueued");
- } else if (!abandon_reserved_delivery(instance, reservation)) {
- return;
- } else if (scene_lease_still_publishing(status)) {
- // A lease that never publishes stalls the intent until its lifetime expires, and the
- // status name alone does not say which side moved. The dispatcher gate that lets a
- // state selection complete tests exactly `revision == publishedRevision`, so a lease
- // pending immediately afterwards means one of them changed between the two checks.
- // report_intent_status dedups on the status, so this records the numbers once.
- std::array<char, 176> detail{};
- scenes::Snapshot lease{};
- const scenes::Status leaseStatus = scenes::query(instance.view, lease);
- const int written =
- std::snprintf(detail.data(),
- detail.size(),
- "%s lease=%s configured=%d revision=%llu published=%llu "
- "pending=%d arrival=%d region=%d plan_region=%u state_row=%u",
- scenes::status_name(status),
- scenes::status_name(leaseStatus),
- lease.configured ? 1 : 0,
- static_cast<unsigned long long>(lease.revision),
- static_cast<unsigned long long>(lease.publishedRevision),
- lease.publicationPending ? 1 : 0,
- lease.regionArrivalPending ? 1 : 0,
- lease.effectiveRegion,
- static_cast<unsigned>(lease.plan.effectiveRegion),
- static_cast<unsigned>(lease.plan.stateRow));
- report_intent_status(
- instance,
- kIntentStatusSceneLeasePending,
- written > 0
- ? std::string_view{detail.data(), static_cast<std::size_t>(written)}
- : scenes::status_name(status));
- } else {
- refuse_delivery(instance,
- sequence ? "sequence_refused" : "cinematic_refused",
- scenes::status_name(status),
- host::EffectOutcome::refused);
- }
- return;
- }
- case lua_vm::IntentKind::playPerformance: {
- host::ScriptableOutputReservation reservation{};
- if (!reserve_delivery(instance, reservation)) {
- if (instance.programStatus == ProgramStatus::loaded) {
- refuse_delivery(instance,
- "performance_refused",
- "host_reservation_unavailable",
- host::EffectOutcome::refused);
- }
- return;
- }
- const scenes::SceneStatus status = scenes::play_performance_slot_reserved(
- instance.view, intent.firstRow, intent.secondRow, reservation);
- if (status == scenes::SceneStatus::queued) {
- await_host_commit(instance, now, "performance_enqueued");
- } else if (!abandon_reserved_delivery(instance, reservation)) {
- return;
- } else if (scene_lease_still_publishing(status)) {
- report_intent_status(
- instance, kIntentStatusSceneLeasePending, scenes::status_name(status));
- } else {
- refuse_delivery(instance,
- "performance_refused",
- scenes::status_name(status),
- host::EffectOutcome::refused);
- }
- return;
- }
- case lua_vm::IntentKind::resetObjectives: {
- host::ScriptableOutputReservation reservation{};
- if (!reserve_delivery(instance, reservation)) {
- if (instance.programStatus == ProgramStatus::loaded) {
- refuse_delivery(instance,
- "objective_reset_refused",
- "host_reservation_unavailable",
- host::EffectOutcome::refused);
- }
- return;
- }
- const scenes::SceneStatus status =
- scenes::reset_objectives_slot_reserved(instance.view, intent.firstRow, reservation);
- if (status == scenes::SceneStatus::queued) {
- await_host_commit(instance, now, "objective_reset_enqueued");
- } else if (!abandon_reserved_delivery(instance, reservation)) {
- return;
- } else if (scene_lease_still_publishing(status)) {
- report_intent_status(
- instance, kIntentStatusSceneLeasePending, scenes::status_name(status));
- } else {
- refuse_delivery(instance,
- "objective_reset_refused",
- scenes::status_name(status),
- host::EffectOutcome::refused);
- }
- return;
- }
- case lua_vm::IntentKind::advanceTask:
- case lua_vm::IntentKind::playDialogueCue: {
- const bool dialogue = intent.kind == lua_vm::IntentKind::playDialogueCue;
- const std::uint16_t cueIndex = static_cast<std::uint16_t>(intent.secondRow);
- host::ScriptableOutputReservation reservation{};
- if (!reserve_delivery(instance, reservation)) {
- if (instance.programStatus == ProgramStatus::loaded) {
- refuse_delivery(instance,
- dialogue ? "dialogue_refused" : "task_refused",
- "host_reservation_unavailable",
- host::EffectOutcome::refused);
- }
- return;
- }
- const scenes::SceneStatus status =
- dialogue
- ? scenes::play_dialogue_cue_slot_reserved(
- instance.view, intent.firstRow, cueIndex, reservation)
- : scenes::activate_task_slot_reserved(instance.view, intent.firstRow, reservation);
- if (status == scenes::SceneStatus::queued) {
- await_host_commit(instance, now, dialogue ? "dialogue_enqueued" : "task_enqueued");
- } else if (!abandon_reserved_delivery(instance, reservation)) {
- return;
- } else if (scene_lease_still_publishing(status)) {
- report_intent_status(
- instance, kIntentStatusSceneLeasePending, scenes::status_name(status));
- } else {
- refuse_delivery(instance,
- dialogue ? "dialogue_refused" : "task_refused",
- scenes::status_name(status),
- host::EffectOutcome::refused);
- }
- return;
- }
- case lua_vm::IntentKind::applySlotAuth: {
- if (intent.authByteCount == 0 || intent.authByteCount != intent.authBody.size()) {
- refuse_delivery(
- instance, "slot_auth_refused", "invalid_body", host::EffectOutcome::refused);
- return;
- }
- const std::span<const std::byte> body(intent.authBody.data(), intent.authByteCount);
- const std::span<const std::byte> sdkBuild(intent.sdkBuildSha256);
- host::ScriptableOutputReservation reservation{};
- if (!reserve_delivery(instance, reservation)) {
- if (instance.programStatus == ProgramStatus::loaded) {
- refuse_delivery(instance,
- "slot_auth_refused",
- "host_reservation_unavailable",
- host::EffectOutcome::refused);
- }
- return;
- }
- const devices::Status status = devices::apply_auth_reserved(instance.view,
- intent.firstRow,
- intent.objectTag,
- intent.registryKey,
- intent.authSchema,
- intent.slotIndex,
- intent.slotType,
- body,
- intent.authBitCount,
- sdkBuild,
- reservation);
- if (status == devices::Status::queued) {
- await_host_commit(instance, now, "slot_auth_enqueued");
- } else if (!abandon_reserved_delivery(instance, reservation)) {
- return;
- } else {
- refuse_delivery(instance,
- "slot_auth_refused",
- devices::status_name(status),
- host::EffectOutcome::refused);
- }
- return;
- }
- case lua_vm::IntentKind::setLifetime: {
- host::ScriptableOutputReservation reservation{};
- if (!reserve_delivery(instance, reservation)) {
- if (instance.programStatus == ProgramStatus::loaded) {
- refuse_delivery(instance,
- "lifetime_refused",
- "host_reservation_unavailable",
- host::EffectOutcome::refused);
- }
- return;
- }
- const devices::Status status =
- lifetimes::set_reserved(instance.view, intent.lifetimeState, reservation);
- if (status == devices::Status::queued) {
- await_host_commit(instance, now, "lifetime_enqueued");
- } else if (!abandon_reserved_delivery(instance, reservation)) {
- return;
- } else {
- refuse_delivery(instance,
- "lifetime_refused",
- devices::status_name(status),
- host::EffectOutcome::refused);
- }
- return;
- }
- case lua_vm::IntentKind::activateAuthoredScene: {
- host::ScriptableOutputReservation reservation{};
- if (!reserve_delivery(instance, reservation)) {
- if (instance.programStatus == ProgramStatus::loaded) {
- refuse_delivery(instance,
- "scene_refused",
- "host_reservation_unavailable",
- host::EffectOutcome::refused);
- }
- return;
- }
- const scenes::SceneStatus status = scenes::activate_authored_scene_reserved(
- instance.view, intent.firstRow, intent.secondRow, reservation);
- if (status == scenes::SceneStatus::queued) {
- await_host_commit(instance, now, "scene_enqueued");
- } else if (!abandon_reserved_delivery(instance, reservation)) {
- return;
- } else if (scene_lease_still_publishing(status)) {
- report_intent_status(
- instance, kIntentStatusSceneLeasePending, scenes::status_name(status));
- } else {
- refuse_delivery(instance,
- "scene_refused",
- scenes::status_name(status),
- host::EffectOutcome::refused);
- }
- return;
- }
- }
- }
- } // namespace sunrise::server::activity::mission
|