host_runtime_scriptable.cpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700
  1. #include <algorithm>
  2. #include <cmath>
  3. #include <limits>
  4. #include <new>
  5. #include "../../middleware/bap/activity_message/mission_auth_patch.h"
  6. #include "../../middleware/bap/activity_message/sensor_auth_update.h"
  7. #include "../../state/activity/mission/runtime.h"
  8. #include "../../state/activity/runtime.h"
  9. #include "../gameplay/squad_entity_retirement.h"
  10. #include "host_runtime_internal.h"
  11. namespace sunrise::server::activity::host {
  12. namespace {
  13. namespace auth = middleware::bap::activity_message::scriptable_auth;
  14. namespace scene = middleware::bap::activity_message::sensor_auth_update;
  15. namespace squad = middleware::bap::activity_message::squad_auth;
  16. using namespace detail;
  17. /** Authored-scene activation generations are positive signed 32-bit values. */
  18. constexpr std::uint32_t kMaximumAuthoredSceneGeneration = 0x7FFFFFFFU;
  19. /** Encodes the fixed type-43 body with no dependencies, scalar input, or events. */
  20. [[nodiscard]] bool encode_authored_scene(std::uint32_t generation,
  21. std::span<std::byte> output,
  22. std::size_t& written) noexcept {
  23. written = 0;
  24. if (generation == 0 || generation > kMaximumAuthoredSceneGeneration
  25. || output.size() < scene::kAuthoredSceneAuthByteCount) {
  26. return false;
  27. }
  28. const std::uint32_t wireGeneration = generation + 0x80000000U;
  29. output[0] = static_cast<std::byte>(wireGeneration >> 24U);
  30. output[1] = static_cast<std::byte>(wireGeneration >> 16U);
  31. output[2] = static_cast<std::byte>(wireGeneration >> 8U);
  32. output[3] = static_cast<std::byte>(wireGeneration);
  33. std::fill(output.begin() + 4, output.begin() + scene::kAuthoredSceneAuthByteCount, std::byte{});
  34. written = scene::kAuthoredSceneAuthByteCount;
  35. return true;
  36. }
  37. /** @return True when both values name the same full ClientRef slot. */
  38. [[nodiscard]] bool same_target(const ScriptableTarget& left,
  39. const ScriptableTarget& right) noexcept {
  40. return left.objectTag == right.objectTag && left.registryKey == right.registryKey
  41. && left.authSchema == right.authSchema && left.rosterGroupIndex == right.rosterGroupIndex
  42. && left.rosterSlotOffset == right.rosterSlotOffset && left.slotIndex == right.slotIndex
  43. && left.sdkObjectIndex == right.sdkObjectIndex
  44. && left.stateLocalRegion == right.stateLocalRegion && left.slotType == right.slotType
  45. && left.stateLocalRoster == right.stateLocalRoster;
  46. }
  47. /** @return True when both routes resolve to the same wire ClientRef. */
  48. [[nodiscard]] bool same_client_ref(const ScriptableTarget& left,
  49. const ScriptableTarget& right) noexcept {
  50. return left.objectTag == right.objectTag && left.registryKey == right.registryKey
  51. && left.slotIndex == right.slotIndex && left.slotType == right.slotType;
  52. }
  53. /** @return True when both retained generated groups are byte-for-byte identical in used fields. */
  54. [[nodiscard]] bool same_group(const state::build_data::scenarios::RosterGroup& left,
  55. const state::build_data::scenarios::RosterGroup& right) noexcept {
  56. if (!state::build_data::scenarios::valid_roster_group(left)
  57. || !state::build_data::scenarios::valid_roster_group(right)
  58. || left.registryKey != right.registryKey || left.objectTag != right.objectTag
  59. || left.slotCount != right.slotCount) {
  60. return false;
  61. }
  62. for (std::size_t index = 0; index < left.slotCount; ++index) {
  63. if (left.slotTypes[index] != right.slotTypes[index]
  64. || left.slotFlags[index] != right.slotFlags[index]
  65. || left.slotIndices[index] != right.slotIndices[index]) {
  66. return false;
  67. }
  68. }
  69. return true;
  70. }
  71. /** @return True when one caller still owns the exact unarmed lane held by this instance. */
  72. [[nodiscard]] bool same_reservation(const ScriptableOutputReservation& left,
  73. const ScriptableOutputReservation& right) noexcept {
  74. return same_binding(left.binding, right.binding)
  75. && left.resetGeneration == right.resetGeneration && left.token == right.token
  76. && left.revision == right.revision && left.intentSequence == right.intentSequence
  77. && left.resetGeneration != 0 && left.token != 0 && left.revision != 0;
  78. }
  79. /** Clears one unarmed reservation without touching the committed output counter. */
  80. void clear_reservation(Instance& instance) noexcept {
  81. instance.scriptableReservation = {};
  82. instance.view.scriptableReservedRevision = 0;
  83. instance.view.scriptableReservationPending = false;
  84. }
  85. /** @return True when the target carries one exact supported type/schema pair. */
  86. [[nodiscard]] bool supported_target(const ScriptableTarget& target) noexcept {
  87. const bool generatedStateLocal = target.stateLocalRoster && target.stateLocalRegion >= 0
  88. && target.rosterGroupIndex == kGeneratedRosterGroupIndex
  89. && target.sdkObjectIndex != kNoSdkObjectIndex;
  90. const bool canonical = !target.stateLocalRoster && target.stateLocalRegion < 0
  91. && target.rosterGroupIndex != kGeneratedRosterGroupIndex
  92. && target.sdkObjectIndex == kNoSdkObjectIndex;
  93. return target.slotType <= scene::kMaximumSlotType
  94. && target.slotIndex <= scene::kMaximumSlotIndex && target.authSchema != 0
  95. && (generatedStateLocal || canonical);
  96. }
  97. /** Finds unused storage for a full-slot guard without mutating it. */
  98. [[nodiscard]] ScriptableGuard* free_guard(Instance& instance) noexcept {
  99. for (ScriptableGuard& guard : instance.scriptableGuards) {
  100. if (!guard.occupied) {
  101. return &guard;
  102. }
  103. }
  104. return nullptr;
  105. }
  106. /** @return True when this kind carries no push-side behaviour beyond its retained body. */
  107. [[nodiscard]] bool tail_eligible(ScriptableOverrideKind kind) noexcept {
  108. return kind != ScriptableOverrideKind::lifetime && kind != ScriptableOverrideKind::squad;
  109. }
  110. /** @return True when this instance already holds a committed body for the same ClientRef. */
  111. [[nodiscard]] bool pending_holds_target(const Instance& instance,
  112. const ScriptableTarget& target) noexcept {
  113. if (instance.view.outputPending && same_client_ref(instance.pendingScriptable.target, target)) {
  114. return true;
  115. }
  116. for (std::size_t index = 0; index < instance.pendingScriptableTailCount; ++index) {
  117. if (same_client_ref(instance.pendingScriptableTail[index].target, target)) {
  118. return true;
  119. }
  120. }
  121. return false;
  122. }
  123. /** @return True when another body may commit before the pending push carries the head out. */
  124. [[nodiscard]] bool tail_has_room(const Instance& instance,
  125. const ScriptableRequest& request) noexcept {
  126. return instance.view.outputPending && tail_eligible(request.kind)
  127. && instance.view.outputKind == OutputKind::scriptableOverride
  128. && tail_eligible(instance.pendingScriptable.kind)
  129. && instance.pendingScriptableTailCount < instance.pendingScriptableTail.size()
  130. && !pending_holds_target(instance, request.target);
  131. }
  132. } // namespace
  133. namespace detail {
  134. /** @return The next positive authored-scene generation without changing the guard. */
  135. [[nodiscard]] bool next_authored_scene_generation(std::uint32_t last,
  136. std::uint32_t& output) noexcept {
  137. output = 0;
  138. if (last >= kMaximumAuthoredSceneGeneration) {
  139. return false;
  140. }
  141. output = last + 1;
  142. return true;
  143. }
  144. /** @return True when one carried group contains the target's exact selected auth slot. */
  145. [[nodiscard]] bool
  146. valid_state_local_group(const ScriptableTarget& target,
  147. const state::build_data::scenarios::RosterGroup& group) noexcept {
  148. const std::size_t slot = target.rosterSlotOffset;
  149. return target.stateLocalRoster && target.stateLocalRegion >= 0
  150. && target.rosterGroupIndex == kGeneratedRosterGroupIndex
  151. && target.sdkObjectIndex != kNoSdkObjectIndex
  152. && state::build_data::scenarios::valid_roster_group(group) && group.objectTag != 0
  153. && group.objectTag == target.objectTag && group.registryKey == target.registryKey
  154. && slot < group.slotCount && group.slotTypes[slot] == target.slotType
  155. && group.slotIndices[slot] == target.slotIndex
  156. && (group.slotFlags[slot] & state::build_data::scenarios::kSlotAuthFlag) != 0;
  157. }
  158. /** @return True when the bit count and the body agree to within one trailing byte. */
  159. [[nodiscard]] bool valid_auth_storage(std::span<const std::byte> body,
  160. std::size_t bitCount) noexcept {
  161. if (body.empty() || bitCount > body.size() * 8U || bitCount + 7U < body.size() * 8U) {
  162. return false;
  163. }
  164. const std::size_t trailingBits = bitCount % 8U;
  165. if (trailingBits == 0) {
  166. return true;
  167. }
  168. const std::uint8_t paddingMask =
  169. static_cast<std::uint8_t>((std::uint16_t{1} << (8U - trailingBits)) - 1U);
  170. return (std::to_integer<std::uint8_t>(body.back()) & paddingMask) == 0;
  171. }
  172. /** Finds one committed full-slot guard while the runtime lock is held. */
  173. [[nodiscard]] ScriptableGuard* find_guard(Instance& instance,
  174. const ScriptableTarget& target) noexcept {
  175. for (ScriptableGuard& guard : instance.scriptableGuards) {
  176. if (guard.occupied && same_target(guard.target, target)) {
  177. return &guard;
  178. }
  179. }
  180. return nullptr;
  181. }
  182. /** @return True when a transport acknowledgement names the retained body byte-for-byte. */
  183. [[nodiscard]] bool same_pending(const PendingScriptableOverride& left,
  184. const PendingScriptableOverride& right) noexcept {
  185. return left.squadRetirement == right.squadRetirement && left.revision == right.revision
  186. && left.kind == right.kind && same_target(left.target, right.target)
  187. && left.generation == right.generation
  188. && left.expectedActivityClientGeneration == right.expectedActivityClientGeneration
  189. && left.sequence == right.sequence && left.dialogueSequence == right.dialogueSequence
  190. && left.dialogueCue == right.dialogueCue && left.bitCount == right.bitCount
  191. && left.byteCount == right.byteCount && left.channel == right.channel
  192. && left.lifetimeState == right.lifetimeState && left.body == right.body
  193. && left.sdkCompiled == right.sdkCompiled
  194. && (!left.target.stateLocalRoster
  195. || same_group(left.stateLocalRosterGroup, right.stateLocalRosterGroup));
  196. }
  197. /** Replaces one delivered full-ClientRef body, or appends its first value. */
  198. [[nodiscard]] bool retain_scriptable_auth(Instance& instance,
  199. const PendingScriptableOverride& pending,
  200. std::uint64_t sourceGeneration) noexcept {
  201. if (pending.kind == ScriptableOverrideKind::lifetime) {
  202. return true;
  203. }
  204. if (pending.byteCount == 0 || pending.byteCount > pending.body.size()) {
  205. return false;
  206. }
  207. PendingScriptableOverride owned = pending;
  208. if (owned.expectedActivityClientGeneration == 0) {
  209. owned.expectedActivityClientGeneration = sourceGeneration;
  210. }
  211. for (PendingScriptableOverride& retained : instance.scriptableAuthEstate) {
  212. if (same_client_ref(retained.target, pending.target)) {
  213. retained = owned;
  214. return true;
  215. }
  216. }
  217. try {
  218. instance.scriptableAuthEstate.push_back(owned);
  219. } catch (const std::bad_alloc&) {
  220. return false;
  221. }
  222. return true;
  223. }
  224. /** Queues one validated scriptable request in the shared ordered control lane. */
  225. [[nodiscard]] bool enqueue_request(ScriptableRequest request,
  226. const ScriptableOutputReservation* reservation) noexcept {
  227. // A lifetime request changes activity state, so it carries no ClientRef slot to validate.
  228. const bool untargeted = request.kind == ScriptableOverrideKind::lifetime;
  229. if ((!untargeted && !supported_target(request.target))
  230. || !state::activity::binding_matches(request.binding)) {
  231. return false;
  232. }
  233. if (reservation != nullptr && !same_binding(reservation->binding, request.binding)) {
  234. return false;
  235. }
  236. AcquireSRWLockExclusive(&g_lock);
  237. Instance* const instance = find_instance(request.binding);
  238. const bool ownsReservation =
  239. reservation != nullptr && instance != nullptr && instance->view.active
  240. && instance->view.scriptableReservationPending
  241. && same_reservation(instance->scriptableReservation, *reservation)
  242. && instance->view.scriptableRevision != (std::numeric_limits<std::uint64_t>::max)()
  243. && instance->view.scriptableRevision + 1 == reservation->revision;
  244. const bool burst = request.burstMember && request.expectedRevision != 0
  245. && tail_eligible(request.kind) && instance != nullptr;
  246. if ((!burst && has_queued_control(request.binding))
  247. || (!burst && instance != nullptr && instance->view.outputPending)
  248. || (!burst && reservation != nullptr && !ownsReservation)
  249. || (!burst && reservation == nullptr && instance != nullptr
  250. && instance->view.scriptableReservationPending)) {
  251. ++g_refusedControls;
  252. ReleaseSRWLockExclusive(&g_lock);
  253. return false;
  254. }
  255. if (reservation != nullptr) {
  256. request.expectedRevision = reservation->revision;
  257. request.expectedIntentSequence = reservation->intentSequence;
  258. }
  259. PendingInput pending{};
  260. pending.kind = PendingKind::scriptableControl;
  261. pending.scriptableControl = request;
  262. if (!append_pending(pending)) {
  263. ++g_refusedControls;
  264. ReleaseSRWLockExclusive(&g_lock);
  265. return false;
  266. }
  267. ++g_queuedControls;
  268. if (reservation != nullptr) {
  269. clear_reservation(*instance);
  270. }
  271. ReleaseSRWLockExclusive(&g_lock);
  272. return true;
  273. }
  274. /** Clears one exact pending body while the runtime lock is held. */
  275. void cancel_pending(Instance& instance,
  276. const state::activity::SessionBinding& binding,
  277. std::uint64_t expectedRevision) noexcept {
  278. const std::uint64_t now = GetTickCount64();
  279. Event event{};
  280. event.binding = binding;
  281. event.tick = now;
  282. event.kind = EventKind::scriptableOverrideCanceled;
  283. for (std::size_t index = 0; index < instance.pendingScriptableTailCount; ++index) {
  284. event.scriptableRevision = instance.pendingScriptableTail[index].revision;
  285. append_event(event);
  286. }
  287. instance.pendingScriptableTail.fill({});
  288. instance.pendingScriptableTailCount = 0;
  289. instance.pendingScriptable = {};
  290. instance.view.outputPending = false;
  291. instance.view.outputKind = OutputKind::none;
  292. instance.view.outputStatus = OutputStatus::canceled;
  293. event.scriptableRevision = expectedRevision;
  294. append_event(event);
  295. instance.view.lastEventSequence = g_sequence;
  296. }
  297. /** Assigns and encodes one typed counter without committing it before transport staging. */
  298. void apply_scriptable_control(const ScriptableRequest& request, std::uint64_t now) noexcept {
  299. Event event{};
  300. event.binding = request.binding;
  301. event.tick = now;
  302. event.kind = EventKind::operatorRefused;
  303. Instance* const instance = find_instance(request.binding);
  304. const bool durableAssignment =
  305. request.expectedIntentSequence == 0
  306. || state::activity::mission::intent_output_assigned(
  307. request.binding, request.expectedIntentSequence, request.expectedRevision);
  308. const bool joinsTail = instance != nullptr && request.burstMember
  309. && tail_has_room(*instance, request)
  310. && request.expectedRevision == instance->view.scriptableRevision;
  311. if (instance == nullptr || !instance->view.active
  312. || (instance->view.outputPending && !joinsTail) || !durableAssignment
  313. || instance->view.scriptableRevision == (std::numeric_limits<std::uint64_t>::max)()
  314. || (request.expectedRevision != 0 && !joinsTail
  315. && instance->view.scriptableRevision + 1 != request.expectedRevision)) {
  316. ++g_refusedControls;
  317. append_event(event);
  318. if (instance != nullptr) {
  319. instance->view.lastEventSequence = g_sequence;
  320. }
  321. return;
  322. }
  323. // A lifetime request owns no ClientRef slot, so it takes no full-slot counter guard.
  324. const bool untargeted = request.kind == ScriptableOverrideKind::lifetime;
  325. ScriptableGuard* guard = untargeted ? nullptr : find_guard(*instance, request.target);
  326. ScriptableGuard candidate{};
  327. if (!untargeted) {
  328. if (guard == nullptr) {
  329. guard = free_guard(*instance);
  330. candidate.target = request.target;
  331. candidate.occupied = true;
  332. } else {
  333. candidate = *guard;
  334. }
  335. }
  336. PendingScriptableOverride pending{};
  337. pending.target = request.target;
  338. pending.stateLocalRosterGroup = request.stateLocalRosterGroup;
  339. pending.revision = request.expectedRevision == 0 ? instance->view.scriptableRevision + 1
  340. : request.expectedRevision;
  341. pending.kind = request.kind;
  342. pending.squadRetirement = request.squadRetirement;
  343. pending.expectedActivityClientGeneration = request.expectedActivityClientGeneration;
  344. std::size_t written = 0;
  345. std::size_t writtenBits = 0;
  346. bool encoded = untargeted || guard != nullptr;
  347. if (encoded && request.kind == ScriptableOverrideKind::lifetime) {
  348. pending.lifetimeState = request.lifetimeState;
  349. } else if (encoded && request.kind == ScriptableOverrideKind::squad) {
  350. std::uint32_t generation = 0;
  351. encoded = request.requestedCountLength <= request.requestedCounts.size();
  352. if (encoded) {
  353. const std::span<const std::int32_t> counts(request.requestedCounts.data(),
  354. request.requestedCountLength);
  355. encoded = squad::next_generation(candidate.squad, generation)
  356. && squad::encode({counts,
  357. generation,
  358. request.squadMode,
  359. request.nameHash,
  360. request.squadAuthoredProfile},
  361. candidate.squad,
  362. pending.body,
  363. written,
  364. writtenBits);
  365. }
  366. pending.generation = generation;
  367. if (writtenBits <= (std::numeric_limits<std::uint16_t>::max)()) {
  368. pending.bitCount = static_cast<std::uint16_t>(writtenBits);
  369. } else {
  370. encoded = false;
  371. }
  372. } else if (encoded && request.kind == ScriptableOverrideKind::combatantChannel) {
  373. std::uint32_t revision = 0;
  374. encoded = auth::next_type2_revision(candidate.type2, revision);
  375. candidate.type2.revision = revision;
  376. encoded =
  377. encoded && auth::set_type2_channel(candidate.type2, request.channelHash, request.value)
  378. && auth::encode_type2_channels(candidate.type2, pending.body, written, writtenBits);
  379. if (writtenBits <= (std::numeric_limits<std::uint16_t>::max)()) {
  380. pending.bitCount = static_cast<std::uint16_t>(writtenBits);
  381. } else {
  382. encoded = false;
  383. }
  384. pending.channelHash = request.channelHash;
  385. pending.channelValue = request.value;
  386. pending.generation = revision;
  387. } else if (encoded && request.kind == ScriptableOverrideKind::combatantBinding) {
  388. std::uint32_t revision = 0;
  389. encoded = auth::next_type2_revision(candidate.type2, revision);
  390. candidate.type2.revision = revision;
  391. candidate.type2.actorBinding = auth::Type2ActorBinding::squadMember;
  392. encoded =
  393. encoded
  394. && auth::encode_type2_channels(candidate.type2, pending.body, written, writtenBits);
  395. if (writtenBits <= (std::numeric_limits<std::uint16_t>::max)()) {
  396. pending.bitCount = static_cast<std::uint16_t>(writtenBits);
  397. } else {
  398. encoded = false;
  399. }
  400. pending.generation = revision;
  401. } else if (encoded && request.kind == ScriptableOverrideKind::object) {
  402. pending.bitCount = static_cast<std::uint16_t>(auth::kType4BitCount);
  403. std::int32_t generation = 0;
  404. encoded = auth::next_type4_generation(candidate.type4, generation)
  405. && auth::encode_type4({generation, request.entryIndex, request.active},
  406. candidate.type4,
  407. pending.body,
  408. written);
  409. pending.generation = static_cast<std::uint64_t>(generation);
  410. } else if (encoded && request.kind == ScriptableOverrideKind::sequence) {
  411. pending.bitCount = static_cast<std::uint16_t>(auth::kType5BitCount);
  412. std::uint8_t revision = 0;
  413. encoded = auth::next_type5_revision(candidate.type5, revision)
  414. && auth::encode_type5({revision}, candidate.type5, pending.body, written);
  415. pending.generation = revision;
  416. } else if (encoded && request.kind == ScriptableOverrideKind::cinematic) {
  417. pending.bitCount = static_cast<std::uint16_t>(auth::kType6BitCount);
  418. std::uint32_t generation = 0;
  419. encoded = auth::next_type6_generation(candidate.type6, generation)
  420. && auth::encode_type6(
  421. {generation, request.active}, candidate.type6, pending.body, written);
  422. pending.generation = generation;
  423. } else if (encoded && request.kind == ScriptableOverrideKind::performance) {
  424. pending.bitCount = static_cast<std::uint16_t>(auth::kType42BitCount);
  425. std::int32_t generation = 0;
  426. auth::Type42Preset preset{};
  427. preset.nameHash = request.nameHash.value_or(0);
  428. encoded = auth::next_type42_generation(candidate.type42, generation);
  429. preset.generation = generation;
  430. encoded = encoded && auth::encode_type42(preset, candidate.type42, pending.body, written);
  431. pending.generation = static_cast<std::uint64_t>(generation);
  432. } else if (encoded && request.kind == ScriptableOverrideKind::type23) {
  433. pending.channel = request.channel;
  434. pending.bitCount = static_cast<std::uint16_t>(auth::kType23BitCount);
  435. auth::Type23Body body{};
  436. auth::Type23SequenceGuard composedGuard = candidate.type23;
  437. for (const PendingScriptableOverride& retained : instance->scriptableAuthEstate) {
  438. if (!same_client_ref(retained.target, request.target)
  439. || retained.bitCount != auth::kType23BitCount
  440. || retained.byteCount != auth::kType23ByteCount
  441. || !auth::decode_type23_body(std::span(retained.body).first(retained.byteCount),
  442. body)) {
  443. continue;
  444. }
  445. for (std::size_t index = 0; index < body.channels.size(); ++index) {
  446. composedGuard.last[index] =
  447. (std::max)(composedGuard.last[index], body.channels[index].sequence);
  448. }
  449. break;
  450. }
  451. std::int16_t sequence = 0;
  452. encoded = auth::next_type23_sequence(composedGuard, request.channel, sequence);
  453. if (encoded) {
  454. const std::size_t channel = static_cast<std::size_t>(request.channel);
  455. body.channels[channel] = {request.value, sequence, request.snap};
  456. encoded = auth::encode_type23_body(body, pending.body, written);
  457. }
  458. pending.sequence = sequence;
  459. } else if (encoded && request.kind == ScriptableOverrideKind::type31) {
  460. pending.bitCount = static_cast<std::uint16_t>(auth::kType31BitCount);
  461. std::uint64_t generation = 0;
  462. encoded = auth::next_type31_generation(candidate.type31, generation)
  463. && auth::encode_type31({generation}, candidate.type31, pending.body, written);
  464. pending.generation = generation;
  465. } else if (encoded && request.kind == ScriptableOverrideKind::objectiveReset) {
  466. pending.bitCount = static_cast<std::uint16_t>(auth::kType3BitCount);
  467. std::int32_t generation = 0;
  468. auth::Type3Body body{};
  469. encoded = auth::next_type3_generation(candidate.type3, generation);
  470. body.generation = generation;
  471. encoded = encoded && auth::encode_type3(body, candidate.type3, pending.body, written);
  472. pending.generation = static_cast<std::uint64_t>(generation);
  473. } else if (encoded && request.kind == ScriptableOverrideKind::task) {
  474. pending.bitCount = static_cast<std::uint16_t>(auth::kType38BitCount);
  475. std::int32_t generation = 0;
  476. encoded = auth::next_type38_generation(candidate.type38, generation)
  477. && auth::encode_type38({generation}, candidate.type38, pending.body, written);
  478. pending.generation = static_cast<std::uint64_t>(generation);
  479. } else if (encoded && request.kind == ScriptableOverrideKind::authoredScene) {
  480. pending.bitCount = scene::kAuthoredSceneAuthBitCount;
  481. std::uint32_t generation = 0;
  482. encoded = next_authored_scene_generation(candidate.authoredSceneGeneration, generation)
  483. && encode_authored_scene(generation, pending.body, written);
  484. pending.generation = generation;
  485. } else if (encoded && request.kind == ScriptableOverrideKind::dialogue) {
  486. pending.bitCount = static_cast<std::uint16_t>(auth::kType53BitCount);
  487. pending.dialogueCue = request.dialogueCue;
  488. std::int32_t sequence = 0;
  489. encoded = auth::next_type53_sequence(candidate.type53, request.dialogueCue, sequence)
  490. && auth::encode_type53(
  491. {request.dialogueCue, sequence}, candidate.type53, pending.body, written);
  492. pending.dialogueSequence = sequence;
  493. } else if (encoded && request.kind == ScriptableOverrideKind::sdkAuth) {
  494. written = request.authByteCount;
  495. pending.bitCount = request.authBitCount;
  496. pending.sdkCompiled = true;
  497. std::copy_n(request.authBody.begin(), written, pending.body.begin());
  498. } else {
  499. encoded = false;
  500. }
  501. // A mission API body carries only the root fields it sets, and the native override replaces
  502. // the whole object. Compose it over the last transported body for the same ClientRef first.
  503. namespace patching = middleware::bap::activity_message::mission_auth_patch;
  504. const std::span<const std::byte> patch = std::span(pending.body).first(written);
  505. patching::Layout layout{};
  506. const bool rootPatch =
  507. encoded
  508. && (request.kind == ScriptableOverrideKind::squad
  509. || request.kind == ScriptableOverrideKind::sdkAuth)
  510. && patching::parse(request.target.authSchema, patch, pending.bitCount, layout);
  511. if (rootPatch) {
  512. std::span<const std::byte> previous{};
  513. std::size_t previousBits = 0;
  514. // A tail never holds the same ClientRef as another pending body, so the predecessor is
  515. // always in the transported estate.
  516. for (const auto& retained : instance->scriptableAuthEstate) {
  517. if (same_client_ref(retained.target, request.target)) {
  518. previous = std::span(retained.body).first(retained.byteCount);
  519. previousBits = retained.bitCount;
  520. break;
  521. }
  522. }
  523. std::size_t composedBits = 0;
  524. encoded = patching::compose(request.target.authSchema,
  525. previous,
  526. previousBits,
  527. patch,
  528. pending.bitCount,
  529. pending.body,
  530. written,
  531. composedBits);
  532. if (encoded) {
  533. pending.bitCount = static_cast<std::uint16_t>(composedBits);
  534. }
  535. }
  536. if (!encoded || written > (std::numeric_limits<std::uint16_t>::max)()) {
  537. ++g_refusedControls;
  538. } else {
  539. if (guard != nullptr && !guard->occupied) {
  540. // Reserve only the target identity. Mutable lane state commits after transport stages
  541. // the exact body; type 2 in particular retains a complete per-actor channel set.
  542. guard->target = request.target;
  543. guard->occupied = true;
  544. }
  545. pending.byteCount = static_cast<std::uint16_t>(written);
  546. touch(*instance);
  547. if (joinsTail) {
  548. instance->pendingScriptableTail[instance->pendingScriptableTailCount] = pending;
  549. ++instance->pendingScriptableTailCount;
  550. } else {
  551. instance->pendingScriptable = pending;
  552. instance->view.outputPending = true;
  553. instance->view.outputKind = OutputKind::scriptableOverride;
  554. instance->view.outputStatus = OutputStatus::pending;
  555. instance->view.lastOutputAttemptTick = 0;
  556. instance->view.lastOutputSourceGeneration = 0;
  557. instance->view.outputAttempts = 0;
  558. }
  559. if (!joinsTail) {
  560. instance->view.scriptableRevision = pending.revision;
  561. }
  562. event.kind = EventKind::scriptableOverrideCommitted;
  563. event.scriptableRevision = pending.revision;
  564. }
  565. append_event(event);
  566. instance->view.lastEventSequence = g_sequence;
  567. }
  568. } // namespace detail
  569. /** Holds one unarmed exact revision while its durable Mission State assignment publishes. */
  570. bool reserve_scriptable_output(const state::activity::SessionBinding& binding,
  571. ScriptableOutputReservation& output,
  572. std::uint64_t intentSequence) noexcept {
  573. output = {};
  574. if (!state::activity::binding_matches(binding)) {
  575. return false;
  576. }
  577. AcquireSRWLockExclusive(&g_lock);
  578. Instance* const instance = find_instance(binding);
  579. const bool available =
  580. instance != nullptr && instance->view.active && !instance->view.outputPending
  581. && !instance->view.scriptableReservationPending && !has_queued_control(binding)
  582. && instance->view.scriptableRevision != (std::numeric_limits<std::uint64_t>::max)()
  583. && g_scriptableReservationSequence != (std::numeric_limits<std::uint64_t>::max)();
  584. if (!available) {
  585. ++g_refusedControls;
  586. ReleaseSRWLockExclusive(&g_lock);
  587. return false;
  588. }
  589. ++g_scriptableReservationSequence;
  590. output.binding = binding;
  591. output.resetGeneration = g_scriptableReservationGeneration;
  592. output.token = g_scriptableReservationSequence;
  593. output.revision = instance->view.scriptableRevision + 1;
  594. output.intentSequence = intentSequence;
  595. instance->scriptableReservation = output;
  596. instance->view.scriptableReservedRevision = output.revision;
  597. instance->view.scriptableReservationPending = true;
  598. ReleaseSRWLockExclusive(&g_lock);
  599. return true;
  600. }
  601. /** Releases one exact unarmed reservation without changing the Host output revision. */
  602. bool release_scriptable_output(const ScriptableOutputReservation& reservation) noexcept {
  603. if (reservation.resetGeneration == 0 || reservation.token == 0 || reservation.revision == 0) {
  604. return false;
  605. }
  606. AcquireSRWLockExclusive(&g_lock);
  607. Instance* const instance = find_instance(reservation.binding);
  608. const bool owned = instance != nullptr && instance->view.scriptableReservationPending
  609. && same_reservation(instance->scriptableReservation, reservation);
  610. if (owned) {
  611. clear_reservation(*instance);
  612. }
  613. ReleaseSRWLockExclusive(&g_lock);
  614. return owned;
  615. }
  616. /** Atomically withdraws one queued durable reducer row or reports its committed disposition. */
  617. ScriptableWithdrawStatus withdraw_scriptable_output(const state::activity::SessionBinding& binding,
  618. std::uint64_t intentSequence,
  619. std::uint64_t expectedRevision) noexcept {
  620. if (intentSequence == 0 || expectedRevision == 0) {
  621. return ScriptableWithdrawStatus::mismatch;
  622. }
  623. AcquireSRWLockExclusive(&g_lock);
  624. Instance* const instance = find_instance(binding);
  625. if (instance == nullptr) {
  626. ReleaseSRWLockExclusive(&g_lock);
  627. return ScriptableWithdrawStatus::mismatch;
  628. }
  629. if (instance->view.scriptableTransportRevision == expectedRevision) {
  630. ReleaseSRWLockExclusive(&g_lock);
  631. return ScriptableWithdrawStatus::transportStaged;
  632. }
  633. if (instance->view.scriptableRevision > expectedRevision
  634. || instance->view.scriptableTransportRevision > expectedRevision) {
  635. ReleaseSRWLockExclusive(&g_lock);
  636. return ScriptableWithdrawStatus::advanced;
  637. }
  638. if (instance->view.scriptableRevision == expectedRevision) {
  639. const ScriptableWithdrawStatus status =
  640. instance->view.outputPending
  641. && instance->view.outputKind == OutputKind::scriptableOverride
  642. ? ScriptableWithdrawStatus::committed
  643. : ScriptableWithdrawStatus::canceled;
  644. ReleaseSRWLockExclusive(&g_lock);
  645. return status;
  646. }
  647. if (instance->view.outputPending || instance->view.scriptableReservationPending) {
  648. ReleaseSRWLockExclusive(&g_lock);
  649. return ScriptableWithdrawStatus::mismatch;
  650. }
  651. for (std::size_t index = g_pendingRead; index < g_pending.size(); ++index) {
  652. PendingInput& pending = g_pending[index];
  653. if (pending.kind != PendingKind::scriptableControl
  654. || !same_binding(pending.scriptableControl.binding, binding)
  655. || pending.scriptableControl.expectedIntentSequence != intentSequence
  656. || pending.scriptableControl.expectedRevision != expectedRevision) {
  657. continue;
  658. }
  659. pending.scriptableControl = {};
  660. pending.kind = PendingKind::discardedControl;
  661. --g_queuedControls;
  662. ReleaseSRWLockExclusive(&g_lock);
  663. return ScriptableWithdrawStatus::withdrawn;
  664. }
  665. ReleaseSRWLockExclusive(&g_lock);
  666. return ScriptableWithdrawStatus::absent;
  667. }
  668. } // namespace sunrise::server::activity::host