host_runtime_sense.cpp 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784
  1. /**
  2. * Msg-6 Sense retention, type-43 tracing and scalar selection.
  3. * Helpers here need the Host runtime lock; the entry points take it themselves.
  4. */
  5. #include <algorithm>
  6. #include <array>
  7. #include <bit>
  8. #include <cstdarg>
  9. #include <cstddef>
  10. #include <cstdio>
  11. #include <limits>
  12. #include <new>
  13. #include <span>
  14. #include "../../core/logging/log.h"
  15. #include "../../middleware/bap/activity_message/ghost_link_sense.h"
  16. #include "../../middleware/bap/activity_message/sense_observation_packet.h"
  17. #include "../../state/activity/runtime.h"
  18. #include "../../state/activity_sdk/format.h"
  19. #include "host_runtime_internal.h"
  20. namespace sunrise::server::activity::host {
  21. namespace {
  22. using namespace detail;
  23. /** @return True when one decoded object has the exact retained observation key. */
  24. [[nodiscard]] bool same_sense_key(
  25. const SenseObservationKey& key,
  26. const middleware::bap::activity_message::sense_update::DecodedObject& object) noexcept {
  27. return key.registryKey == object.registryKey && key.objectTag == object.objectTag
  28. && key.slotType == object.slotType && key.slotIndex == object.slotIndex
  29. && key.senseSchema == object.senseSchema && key.schemaRow == object.schemaRow;
  30. }
  31. /** @return True when the packet carries this key at or after the selected object. */
  32. [[nodiscard]] bool
  33. packet_has_sense_key(const middleware::bap::activity_message::sense_update::DecodedPacket& packet,
  34. const SenseObservationKey& key,
  35. std::size_t first) noexcept {
  36. for (std::size_t index = first; index < packet.objectCount; ++index) {
  37. if (packet.objects[index].status
  38. == middleware::bap::activity_message::sense_update::ObjectStatus::decoded
  39. && packet.objects[index].hasGeneration && same_sense_key(key, packet.objects[index])) {
  40. return true;
  41. }
  42. }
  43. return false;
  44. }
  45. /** @return True when the envelope closed and each decoded object's storage is bounded. */
  46. [[nodiscard]] bool valid_sense_observation_input(const SenseInput& input) noexcept {
  47. namespace sense = middleware::bap::activity_message::sense_update;
  48. const auto& packet = input.decoded;
  49. return input.sourceGeneration != 0 && input.clientMessageSequence != 0
  50. && input.verdict == state::activity::receipts::Verdict::framed
  51. && input.decodeStatus == packet.status && sense::observation_packet(packet)
  52. && input.groupsSeen == packet.groupsSeen && input.groupsDecoded == packet.groupsDecoded
  53. && input.groupsSkipped == packet.groupsSkipped && input.objectsSeen == packet.objectsSeen
  54. && input.objectsDecoded == packet.objectsDecoded;
  55. }
  56. /** Mixes one fixed-width value into the local scene change guard. */
  57. void mix_scene_fingerprint(std::uint64_t& fingerprint, std::uint64_t value) noexcept {
  58. // FNV-1a 64-bit prime.
  59. constexpr std::uint64_t kPrime = 1'099'511'628'211ULL;
  60. for (std::uint8_t shift = 0; shift < 64; shift += 8) {
  61. fingerprint ^= (value >> shift) & 0xFFU;
  62. fingerprint *= kPrime;
  63. }
  64. }
  65. /** @return A run-local change guard over one decoded object and every retained value. */
  66. [[nodiscard]] std::uint64_t
  67. scene_fingerprint(const middleware::bap::activity_message::sense_update::DecodedObject& object,
  68. std::span<const middleware::bap::activity_message::sense_update::DecodedValue>
  69. values) noexcept {
  70. // FNV-1a 64-bit offset basis.
  71. constexpr std::uint64_t kOffset = 14'695'981'039'346'656'037ULL;
  72. std::uint64_t fingerprint = kOffset;
  73. mix_scene_fingerprint(fingerprint, object.objectRow);
  74. mix_scene_fingerprint(fingerprint, object.slotRow);
  75. mix_scene_fingerprint(fingerprint, object.schemaRow);
  76. mix_scene_fingerprint(fingerprint, object.generationPlusOne);
  77. mix_scene_fingerprint(fingerprint, object.deltaBits);
  78. mix_scene_fingerprint(fingerprint, object.hasGeneration ? 1U : 0U);
  79. mix_scene_fingerprint(fingerprint, values.size());
  80. for (const auto& value : values) {
  81. mix_scene_fingerprint(fingerprint, value.unsignedValue);
  82. mix_scene_fingerprint(fingerprint, static_cast<std::uint64_t>(value.signedValue));
  83. mix_scene_fingerprint(fingerprint, std::bit_cast<std::uint32_t>(value.realValue));
  84. mix_scene_fingerprint(fingerprint, value.schemaRow);
  85. mix_scene_fingerprint(fingerprint, value.fieldRow);
  86. mix_scene_fingerprint(fingerprint, value.occurrence);
  87. mix_scene_fingerprint(fingerprint, value.bitOffset);
  88. mix_scene_fingerprint(fingerprint, value.fieldOrdinal);
  89. mix_scene_fingerprint(fingerprint, value.width);
  90. mix_scene_fingerprint(fingerprint, static_cast<std::uint8_t>(value.kind));
  91. mix_scene_fingerprint(fingerprint, value.present ? 1U : 0U);
  92. }
  93. return fingerprint;
  94. }
  95. /** Writes one bounded type-43 diagnostic event. */
  96. void report_scene_sense(const char* format, ...) noexcept {
  97. std::array<char, core::log::kLineCapacity> line{};
  98. va_list arguments;
  99. va_start(arguments, format);
  100. const int written = std::vsnprintf(line.data(), line.size(), format, arguments);
  101. va_end(arguments);
  102. if (written <= 0) {
  103. return;
  104. }
  105. const auto length = static_cast<std::size_t>(written) < line.size()
  106. ? static_cast<std::size_t>(written)
  107. : line.size() - 1;
  108. core::log::write(core::log::Channel::server, core::log::Level::debug, {line.data(), length});
  109. }
  110. /** @return The trace row for one exact ClientRef, or null when it has not been seen. */
  111. [[nodiscard]] SceneSenseTraceRecord* find_scene_trace_record(
  112. SceneSenseTrace& trace,
  113. const middleware::bap::activity_message::sense_update::DecodedObject& object) noexcept {
  114. for (SceneSenseTraceRecord& record : trace.records) {
  115. if (record.occupied && same_sense_key(record.key, object)) {
  116. return &record;
  117. }
  118. }
  119. return nullptr;
  120. }
  121. /** @return One unused trace row, or null when the bounded table is full. */
  122. [[nodiscard]] SceneSenseTraceRecord* reserve_scene_trace_record(SceneSenseTrace& trace) noexcept {
  123. for (SceneSenseTraceRecord& record : trace.records) {
  124. if (!record.occupied) {
  125. return &record;
  126. }
  127. }
  128. return nullptr;
  129. }
  130. /** Reports one typed scalar with its exact reflected rows and wire position. */
  131. void report_scene_value(
  132. const Instance& instance,
  133. const SenseInput& input,
  134. const middleware::bap::activity_message::sense_update::DecodedObject& object,
  135. const middleware::bap::activity_message::sense_update::DecodedValue& value,
  136. std::size_t valueIndex) noexcept {
  137. using ValueKind = middleware::bap::activity_message::sense_update::ValueKind;
  138. // Fixed head of the scene-sense value line; the typed value is appended after it.
  139. constexpr const char* kPrefix =
  140. "ev=scene_sense kind=value activity=%d session=0x%llX binding_rev=%llu "
  141. "source_gen=%llu msg_seq=%llu key=0x%08X tag=0x%08X type=%u index=%u "
  142. "sense_schema=0x%08X gen_plus_one=%u value_index=%zu schema_row=%u "
  143. "field_row=%u ordinal=%u occurrence=%u bit=%u width=%u present=%u";
  144. std::array<char, core::log::kLineCapacity> prefix{};
  145. const int written =
  146. std::snprintf(prefix.data(),
  147. prefix.size(),
  148. kPrefix,
  149. static_cast<int>(instance.view.binding.destination.activityIndex),
  150. static_cast<unsigned long long>(instance.view.binding.sessionId),
  151. static_cast<unsigned long long>(instance.view.binding.createdRevision),
  152. static_cast<unsigned long long>(input.sourceGeneration),
  153. static_cast<unsigned long long>(input.clientMessageSequence),
  154. object.registryKey,
  155. object.objectTag,
  156. static_cast<unsigned>(object.slotType),
  157. static_cast<unsigned>(object.slotIndex),
  158. object.senseSchema,
  159. object.generationPlusOne,
  160. valueIndex,
  161. value.schemaRow,
  162. value.fieldRow,
  163. static_cast<unsigned>(value.fieldOrdinal),
  164. value.occurrence,
  165. value.bitOffset,
  166. static_cast<unsigned>(value.width),
  167. value.present ? 1U : 0U);
  168. if (written <= 0 || static_cast<std::size_t>(written) >= prefix.size()) {
  169. return;
  170. }
  171. if (!value.present) {
  172. report_scene_sense("%s domain=%s value=absent",
  173. prefix.data(),
  174. value.kind == ValueKind::unsignedInteger ? "uint"
  175. : value.kind == ValueKind::signedInteger ? "int"
  176. : value.kind == ValueKind::boolean ? "bool"
  177. : "real32");
  178. return;
  179. }
  180. switch (value.kind) {
  181. case ValueKind::unsignedInteger:
  182. report_scene_sense("%s domain=uint value=0x%llX",
  183. prefix.data(),
  184. static_cast<unsigned long long>(value.unsignedValue));
  185. break;
  186. case ValueKind::signedInteger:
  187. report_scene_sense("%s domain=int raw=0x%llX value=%lld",
  188. prefix.data(),
  189. static_cast<unsigned long long>(value.unsignedValue),
  190. static_cast<long long>(value.signedValue));
  191. break;
  192. case ValueKind::boolean:
  193. report_scene_sense("%s domain=bool raw=0x%llX value=%u",
  194. prefix.data(),
  195. static_cast<unsigned long long>(value.unsignedValue),
  196. value.unsignedValue != 0 ? 1U : 0U);
  197. break;
  198. case ValueKind::real32:
  199. report_scene_sense("%s domain=real32 raw=0x%llX value_bits=0x%08X value=%.9g",
  200. prefix.data(),
  201. static_cast<unsigned long long>(value.unsignedValue),
  202. std::bit_cast<std::uint32_t>(value.realValue),
  203. static_cast<double>(value.realValue));
  204. break;
  205. }
  206. }
  207. /** Reports complete changed type-43 objects without changing retained activity state. */
  208. void trace_scene_sense(Instance& instance, const SenseInput& input) noexcept {
  209. namespace sense = middleware::bap::activity_message::sense_update;
  210. if (!core::log::accepts(core::log::Channel::server, core::log::Level::debug)) {
  211. return;
  212. }
  213. SceneSenseTrace& trace = instance.sceneSenseTrace;
  214. if (trace.sourceGeneration != input.sourceGeneration) {
  215. trace = {};
  216. trace.sourceGeneration = input.sourceGeneration;
  217. }
  218. const sense::DecodedPacket& packet = input.decoded;
  219. bool hasScene = false;
  220. const std::size_t retainedObjectCount = (std::min)(packet.objectCount, packet.objects.size());
  221. for (std::size_t index = 0; index < retainedObjectCount; ++index) {
  222. if (packet.objects[index].slotType
  223. == static_cast<std::uint8_t>(state::activity_sdk::format::kAuthoredSceneSlotType)) {
  224. hasScene = true;
  225. break;
  226. }
  227. }
  228. if (!valid_sense_observation_input(input)) {
  229. if (!trace.incompleteReported && (hasScene || packet.objectsTruncated)) {
  230. trace.incompleteReported = true;
  231. report_scene_sense(
  232. "ev=scene_sense kind=packet result=skip reason=incomplete activity=%d "
  233. "session=0x%llX binding_rev=%llu source_gen=%llu msg_seq=%llu "
  234. "status=%s objects_seen=%u objects_kept=%zu values_kept=%zu "
  235. "objects_truncated=%u values_truncated=%u",
  236. static_cast<int>(instance.view.binding.destination.activityIndex),
  237. static_cast<unsigned long long>(instance.view.binding.sessionId),
  238. static_cast<unsigned long long>(instance.view.binding.createdRevision),
  239. static_cast<unsigned long long>(input.sourceGeneration),
  240. static_cast<unsigned long long>(input.clientMessageSequence),
  241. sense::decode_status_name(packet.status),
  242. packet.objectsSeen,
  243. packet.objectCount,
  244. packet.valueCount,
  245. packet.objectsTruncated ? 1U : 0U,
  246. packet.valuesTruncated ? 1U : 0U);
  247. }
  248. return;
  249. }
  250. for (std::size_t index = 0; index < packet.objectCount; ++index) {
  251. const sense::DecodedObject& object = packet.objects[index];
  252. if (object.status != sense::ObjectStatus::decoded || !object.hasGeneration
  253. || object.slotType
  254. != static_cast<std::uint8_t>(state::activity_sdk::format::kAuthoredSceneSlotType)
  255. || packet_has_sense_key(packet,
  256. {object.registryKey,
  257. object.objectTag,
  258. object.senseSchema,
  259. object.schemaRow,
  260. object.slotIndex,
  261. object.slotType},
  262. index + 1)) {
  263. continue;
  264. }
  265. const std::span values(packet.values.data() + object.firstValue, object.valueCount);
  266. const std::uint64_t fingerprint = scene_fingerprint(object, values);
  267. SceneSenseTraceRecord* record = find_scene_trace_record(trace, object);
  268. const bool known = record != nullptr;
  269. if (known && record->fingerprint == fingerprint
  270. && record->generationPlusOne == object.generationPlusOne
  271. && record->valueCount == object.valueCount
  272. && record->hasGeneration == object.hasGeneration) {
  273. continue;
  274. }
  275. if (record == nullptr) {
  276. record = reserve_scene_trace_record(trace);
  277. }
  278. if (record == nullptr) {
  279. if (!trace.capacityReported) {
  280. trace.capacityReported = true;
  281. report_scene_sense(
  282. "ev=scene_sense kind=packet result=skip reason=trace_capacity activity=%d "
  283. "session=0x%llX binding_rev=%llu source_gen=%llu capacity=%zu",
  284. static_cast<int>(instance.view.binding.destination.activityIndex),
  285. static_cast<unsigned long long>(instance.view.binding.sessionId),
  286. static_cast<unsigned long long>(instance.view.binding.createdRevision),
  287. static_cast<unsigned long long>(input.sourceGeneration),
  288. trace.records.size());
  289. }
  290. continue;
  291. }
  292. record->key = {object.registryKey,
  293. object.objectTag,
  294. object.senseSchema,
  295. object.schemaRow,
  296. object.slotIndex,
  297. object.slotType};
  298. record->fingerprint = fingerprint;
  299. record->generationPlusOne = object.generationPlusOne;
  300. record->valueCount = object.valueCount;
  301. record->hasGeneration = object.hasGeneration;
  302. record->occupied = true;
  303. report_scene_sense("ev=scene_sense kind=object change=%s activity=%d session=0x%llX "
  304. "binding_rev=%llu source_gen=%llu msg_seq=%llu key=0x%08X tag=0x%08X "
  305. "object_row=%u type=%u index=%u slot_row=%u sense_schema=0x%08X "
  306. "schema_row=%u gen_plus_one=%u has_gen=%u delta_bits=%u values=%u",
  307. known ? "update" : "new",
  308. static_cast<int>(instance.view.binding.destination.activityIndex),
  309. static_cast<unsigned long long>(instance.view.binding.sessionId),
  310. static_cast<unsigned long long>(instance.view.binding.createdRevision),
  311. static_cast<unsigned long long>(input.sourceGeneration),
  312. static_cast<unsigned long long>(input.clientMessageSequence),
  313. object.registryKey,
  314. object.objectTag,
  315. object.objectRow,
  316. static_cast<unsigned>(object.slotType),
  317. static_cast<unsigned>(object.slotIndex),
  318. object.slotRow,
  319. object.senseSchema,
  320. object.schemaRow,
  321. object.generationPlusOne,
  322. object.hasGeneration ? 1U : 0U,
  323. object.deltaBits,
  324. object.valueCount);
  325. for (std::size_t valueIndex = 0; valueIndex < values.size(); ++valueIndex) {
  326. report_scene_value(instance, input, object, values[valueIndex], valueIndex);
  327. }
  328. }
  329. }
  330. /** Replaces only fully decoded keys; omitted or unsupported keys stay retained. */
  331. [[nodiscard]] bool
  332. retain_sense_observations(Instance& instance, const SenseInput& input, std::uint64_t now) noexcept {
  333. namespace sense = middleware::bap::activity_message::sense_update;
  334. if (!valid_sense_observation_input(input)) {
  335. return false;
  336. }
  337. const sense::DecodedPacket& packet = input.decoded;
  338. SenseObservationSnapshot next{};
  339. next.revision = next_nonzero(instance.senseObservations.revision);
  340. next.sourceGeneration = input.sourceGeneration;
  341. for (std::size_t index = 0; index < packet.objectCount; ++index) {
  342. const sense::DecodedObject& object = packet.objects[index];
  343. if (object.status != sense::ObjectStatus::decoded || !object.hasGeneration) {
  344. continue;
  345. }
  346. const SenseObservationKey key{object.registryKey,
  347. object.objectTag,
  348. object.senseSchema,
  349. object.schemaRow,
  350. object.slotIndex,
  351. object.slotType};
  352. if (packet_has_sense_key(packet, key, index + 1)) {
  353. continue;
  354. }
  355. SenseObservation observation{};
  356. observation.binding = input.binding;
  357. observation.key = key;
  358. observation.sequence = next.revision;
  359. observation.tick = now;
  360. observation.sourceGeneration = input.sourceGeneration;
  361. observation.clientMessageSequence = input.clientMessageSequence;
  362. observation.generationPlusOne = object.generationPlusOne;
  363. observation.hasGeneration = object.hasGeneration;
  364. const std::span values(packet.values.data() + object.firstValue, object.valueCount);
  365. if (!append_sense_observation(next, observation, values)) {
  366. return false;
  367. }
  368. }
  369. if (instance.senseObservations.sourceGeneration == input.sourceGeneration) {
  370. const SenseObservationSnapshot& current = instance.senseObservations;
  371. for (std::size_t index = 0; index < current.observationCount; ++index) {
  372. const SenseObservation& observation = current.observations[index];
  373. if (packet_has_sense_key(packet, observation.key, 0)
  374. || observation.firstValue > current.valueCount
  375. || observation.valueCount > current.valueCount - observation.firstValue) {
  376. continue;
  377. }
  378. const std::span values(current.values.data() + observation.firstValue,
  379. observation.valueCount);
  380. static_cast<void>(append_sense_observation(next, observation, values));
  381. }
  382. }
  383. instance.senseObservations = next;
  384. instance.view.senseObservationCount = static_cast<std::uint32_t>(next.observationCount);
  385. instance.view.senseObservationValueCount = static_cast<std::uint32_t>(next.valueCount);
  386. instance.view.senseObservationRevision = next.revision;
  387. instance.view.senseObservationSourceGeneration = next.sourceGeneration;
  388. return true;
  389. }
  390. /** Merges complete squad deltas without discarding fields absent from a later report. */
  391. void retain_squad_sense(Instance& instance, const SenseInput& input) noexcept {
  392. namespace sense = middleware::bap::activity_message::sense_update;
  393. namespace squadSense = middleware::bap::activity_message::squad_sense;
  394. const sense::DecodedPacket& packet = input.decoded;
  395. if (input.sourceGeneration == 0 || input.sourceGeneration < instance.squadSenseSourceGeneration
  396. || packet.status == sense::DecodeStatus::malformed || packet.valuesTruncated
  397. || packet.objectsTruncated || packet.objectCount > packet.objects.size()
  398. || packet.valueCount > packet.values.size()) {
  399. return;
  400. }
  401. if (input.sourceGeneration != instance.squadSenseSourceGeneration) {
  402. instance.squadSense.clear();
  403. instance.squadSenseSourceGeneration = input.sourceGeneration;
  404. }
  405. for (const sense::DecodedObject& object : std::span(packet.objects).first(packet.objectCount)) {
  406. if (object.slotType != squadSense::kSlotType || object.senseSchema != squadSense::kSchema
  407. || object.status != sense::ObjectStatus::decoded || !object.hasGeneration) {
  408. continue;
  409. }
  410. auto found = std::ranges::find_if(instance.squadSense, [&](const SquadSenseRecord& record) {
  411. return same_sense_key(record.key, object);
  412. });
  413. squadSense::State merged =
  414. found == instance.squadSense.end() ? squadSense::State{} : found->state;
  415. // An uninitialized replica cannot replace the squad's recovery state.
  416. if (!squadSense::merge(merged, object, std::span(packet.values).first(packet.valueCount))
  417. || !merged.valid) {
  418. continue;
  419. }
  420. if (found != instance.squadSense.end()) {
  421. found->state = merged;
  422. } else if (instance.squadSense.size() < kScriptableGuardCapacity) {
  423. const SenseObservationKey key{object.registryKey,
  424. object.objectTag,
  425. object.senseSchema,
  426. object.schemaRow,
  427. object.slotIndex,
  428. object.slotType};
  429. try {
  430. instance.squadSense.push_back({key, merged});
  431. } catch (const std::bad_alloc&) {
  432. return;
  433. }
  434. }
  435. }
  436. }
  437. /** @return The committed guard for one exact reported slot, or null when none was armed. */
  438. [[nodiscard]] ScriptableGuard* find_ghost_link_guard(
  439. Instance& instance,
  440. const middleware::bap::activity_message::sense_update::DecodedObject& object) noexcept {
  441. for (ScriptableGuard& guard : instance.scriptableGuards) {
  442. if (guard.occupied && guard.target.registryKey == object.registryKey
  443. && guard.target.objectTag == object.objectTag
  444. && guard.target.slotType == object.slotType
  445. && guard.target.slotIndex == object.slotIndex) {
  446. return &guard;
  447. }
  448. }
  449. return nullptr;
  450. }
  451. /**
  452. * Merges the client's type-65 reports and latches the end of the bar.
  453. * The client cannot cross the duration on its own, so reaching the end is a host-owned change: it
  454. * advances the state revision and the next roster carries the finishing Sense root.
  455. */
  456. void retain_ghost_link_sense(Instance& instance, const SenseInput& input) noexcept {
  457. namespace sense = middleware::bap::activity_message::sense_update;
  458. namespace ghostAuth = middleware::bap::activity_message::ghost_link;
  459. namespace ghostSense = middleware::bap::activity_message::ghost_link_sense;
  460. const sense::DecodedPacket& packet = input.decoded;
  461. if (input.sourceGeneration == 0 || packet.objectCount > packet.objects.size()
  462. || packet.valueCount > packet.values.size()) {
  463. return;
  464. }
  465. for (const sense::DecodedObject& object : std::span(packet.objects).first(packet.objectCount)) {
  466. if (object.slotType != ghostAuth::kSlotType || object.senseSchema != ghostAuth::kSenseSchema
  467. || object.status != sense::ObjectStatus::decoded || !object.hasGeneration
  468. || object.firstValue > packet.valueCount
  469. || object.valueCount > packet.valueCount - object.firstValue) {
  470. continue;
  471. }
  472. ScriptableGuard* const guard = find_ghost_link_guard(instance, object);
  473. ghostSense::Level level{};
  474. if (guard == nullptr
  475. || !ghostSense::read(
  476. std::span(packet.values).subspan(object.firstValue, object.valueCount), level)) {
  477. continue;
  478. }
  479. GhostLinkScan& scan = guard->ghostLink;
  480. scan.counter = object.generationPlusOne;
  481. scan.counterKnown = true;
  482. scan.fraction = level.fraction;
  483. scan.active = level.active;
  484. const bool ended = scan.armed && !scan.finished && scan.generation != 0
  485. && level.generation == static_cast<std::int32_t>(scan.generation)
  486. && level.active && level.fraction >= kGhostLinkFinishFraction;
  487. if (ended && instance.view.stateRevision != (std::numeric_limits<std::uint64_t>::max)()) {
  488. scan.finished = true;
  489. ++instance.view.stateRevision;
  490. }
  491. }
  492. }
  493. } // namespace
  494. namespace detail {
  495. /** Appends one observation and its complete owned value range. */
  496. [[nodiscard]] bool append_sense_observation(
  497. SenseObservationSnapshot& output,
  498. SenseObservation observation,
  499. std::span<const middleware::bap::activity_message::sense_update::DecodedValue>
  500. values) noexcept {
  501. if (output.observationCount == output.observations.size()
  502. || values.size() > output.values.size() - output.valueCount) {
  503. return false;
  504. }
  505. observation.firstValue = static_cast<std::uint32_t>(output.valueCount);
  506. observation.valueCount = static_cast<std::uint32_t>(values.size());
  507. output.observations[output.observationCount++] = observation;
  508. std::copy(values.begin(),
  509. values.end(),
  510. output.values.begin() + static_cast<std::ptrdiff_t>(output.valueCount));
  511. output.valueCount += values.size();
  512. return true;
  513. }
  514. /** Applies one copied msg-6 decode summary. */
  515. void apply_sense(const SenseInput& input, std::uint64_t now) noexcept {
  516. Instance* const instance = find_instance(input.binding);
  517. if (instance == nullptr || !instance->view.active) {
  518. ++g_droppedIngress;
  519. return;
  520. }
  521. touch(*instance);
  522. ++instance->view.senseCount;
  523. trace_scene_sense(*instance, input);
  524. retain_squad_sense(*instance, input);
  525. retain_ghost_link_sense(*instance, input);
  526. static_cast<void>(retain_sense_observations(*instance, input, now));
  527. Event event{};
  528. event.attemptGeneration = input.attemptGeneration;
  529. event.binding = input.binding;
  530. event.tick = now;
  531. event.kind = EventKind::senseUpdate;
  532. event.epochFirst = input.epochFirst;
  533. event.epochSecond = input.epochSecond;
  534. event.payloadBytes = input.payloadBytes;
  535. event.peerHeardMask = input.peerHeardMask;
  536. event.tailBits = input.tailBits;
  537. event.consumedBits = input.consumedBits;
  538. event.firstGroupBits = input.firstGroupBits;
  539. event.firstRegistryKey = input.firstRegistryKey;
  540. event.groupsSeen = input.groupsSeen;
  541. event.groupsDecoded = input.groupsDecoded;
  542. event.groupsSkipped = input.groupsSkipped;
  543. event.objectsSeen = input.objectsSeen;
  544. event.objectsDecoded = input.objectsDecoded;
  545. event.firstSlotIndex = input.firstSlotIndex;
  546. event.firstSlotType = input.firstSlotType;
  547. // The event names one slot only: the first decoded ClientRef of the packet.
  548. if (input.decoded.objectCount != 0) {
  549. event.slotObjectTag = input.decoded.objects.front().objectTag;
  550. event.slotSenseSchema = input.decoded.objects.front().senseSchema;
  551. }
  552. event.senseDecodeStatus = input.decodeStatus;
  553. event.senseSnapshotRetained = valid_sense_observation_input(input);
  554. event.hasFirstObject = input.hasFirstObject;
  555. event.stateRevision = instance->view.stateRevision;
  556. event.sourceGeneration = input.sourceGeneration;
  557. event.clientMessageSequence = input.clientMessageSequence;
  558. event.lifetimeState = instance->view.lifetimeState;
  559. event.verdict = input.verdict;
  560. append_event(event);
  561. append_mission_input(event, event.senseSnapshotRetained ? &input.decoded : nullptr);
  562. instance->view.lastEventSequence = g_sequence;
  563. }
  564. } // namespace detail
  565. /** Queues one owned msg-6 prefix for the Activity Host service. */
  566. IngressRefusal submit_sense(const SenseInput& input) noexcept {
  567. if (!state::activity::binding_matches(input.binding)) {
  568. return IngressRefusal::binding;
  569. }
  570. if (input.sourceGeneration == 0) {
  571. return IngressRefusal::generation;
  572. }
  573. AcquireSRWLockExclusive(&g_lock);
  574. PendingInput pending{};
  575. pending.kind = PendingKind::sense;
  576. pending.sense = input;
  577. if (!append_pending(pending)) {
  578. ++g_droppedIngress;
  579. ReleaseSRWLockExclusive(&g_lock);
  580. return IngressRefusal::queue;
  581. }
  582. ++g_queuedIngress;
  583. ReleaseSRWLockExclusive(&g_lock);
  584. return IngressRefusal::none;
  585. }
  586. /** Copies initialized recovery state for one exact ActivityClient generation. */
  587. bool snapshot_squad_sense(const state::activity::SessionBinding& binding,
  588. std::uint64_t sourceGeneration,
  589. const SenseObservationKey& key,
  590. middleware::bap::activity_message::squad_sense::State& output) noexcept {
  591. output = {};
  592. AcquireSRWLockShared(&g_lock);
  593. const Instance* const instance = find_instance(binding);
  594. bool found = false;
  595. if (instance != nullptr && instance->squadSenseSourceGeneration == sourceGeneration) {
  596. for (const SquadSenseRecord& record : instance->squadSense) {
  597. if (record.key.registryKey == key.registryKey && record.key.objectTag == key.objectTag
  598. && record.key.senseSchema == key.senseSchema && record.key.slotType == key.slotType
  599. && record.key.slotIndex == key.slotIndex && record.state.valid) {
  600. output = record.state;
  601. found = true;
  602. break;
  603. }
  604. }
  605. }
  606. ReleaseSRWLockShared(&g_lock);
  607. return found;
  608. }
  609. /** Copies the armed scan model for one exact type-65 slot that has reported at least once. */
  610. bool ghost_link_scan(const state::activity::SessionBinding& binding,
  611. const SenseObservationKey& key,
  612. GhostLinkLevel& output) noexcept {
  613. output = {};
  614. AcquireSRWLockShared(&g_lock);
  615. const Instance* const instance = find_instance(binding);
  616. bool found = false;
  617. if (instance != nullptr) {
  618. for (const ScriptableGuard& guard : instance->scriptableGuards) {
  619. const GhostLinkScan& scan = guard.ghostLink;
  620. if (!guard.occupied || guard.target.registryKey != key.registryKey
  621. || guard.target.objectTag != key.objectTag || guard.target.slotType != key.slotType
  622. || guard.target.slotIndex != key.slotIndex || !scan.armed || scan.generation == 0
  623. || !scan.counterKnown) {
  624. continue;
  625. }
  626. output.generation = static_cast<std::int32_t>(scan.generation);
  627. output.counter = scan.counter;
  628. output.finished = scan.finished;
  629. found = true;
  630. break;
  631. }
  632. }
  633. ReleaseSRWLockShared(&g_lock);
  634. return found;
  635. }
  636. /** Copies the latest complete Sense observations for one exact activity generation. */
  637. bool snapshot_sense_observations(const state::activity::SessionBinding& binding,
  638. SenseObservationSnapshot& output) noexcept {
  639. output = {};
  640. AcquireSRWLockShared(&g_lock);
  641. const Instance* const instance = find_instance(binding);
  642. const bool found = instance != nullptr;
  643. if (found) {
  644. output = instance->senseObservations;
  645. }
  646. ReleaseSRWLockShared(&g_lock);
  647. return found;
  648. }
  649. /** Selects one exact reflected scalar without changing the retained snapshot. */
  650. SenseScalarStatus select_sense_scalar(const SenseObservationSnapshot& snapshot,
  651. const state::activity::SessionBinding& binding,
  652. const SenseScalarIdentity& identity,
  653. SenseScalarSample& output) noexcept {
  654. namespace sense = middleware::bap::activity_message::sense_update;
  655. output = {};
  656. if (binding.sessionId == state::activity::kAbsentSessionId
  657. || binding.createdRevision == state::activity::kInvalidRevision
  658. || identity.object.schemaRow == sense::kAbsentRuntimeRow
  659. || identity.fieldSchemaRow == sense::kAbsentRuntimeRow
  660. || identity.fieldRow == sense::kAbsentRuntimeRow
  661. || identity.fieldSchemaRow != identity.object.schemaRow) {
  662. return SenseScalarStatus::invalidIdentity;
  663. }
  664. if (snapshot.sourceGeneration == 0 || snapshot.observationCount > snapshot.observations.size()
  665. || snapshot.valueCount > snapshot.values.size()) {
  666. return SenseScalarStatus::invalidSnapshot;
  667. }
  668. const auto sameKey = [&identity](const SenseObservationKey& candidate) noexcept {
  669. const SenseObservationKey& expected = identity.object;
  670. return candidate.registryKey == expected.registryKey
  671. && candidate.objectTag == expected.objectTag
  672. && candidate.senseSchema == expected.senseSchema
  673. && candidate.schemaRow == expected.schemaRow
  674. && candidate.slotIndex == expected.slotIndex
  675. && candidate.slotType == expected.slotType;
  676. };
  677. bool found = false;
  678. for (std::size_t observationIndex = 0; observationIndex < snapshot.observationCount;
  679. ++observationIndex) {
  680. const SenseObservation& observation = snapshot.observations[observationIndex];
  681. if (!same_binding(observation.binding, binding) || !sameKey(observation.key)) {
  682. continue;
  683. }
  684. if (observation.sourceGeneration != snapshot.sourceGeneration
  685. || observation.firstValue > snapshot.valueCount
  686. || observation.valueCount > snapshot.valueCount - observation.firstValue) {
  687. output = {};
  688. return SenseScalarStatus::invalidSnapshot;
  689. }
  690. for (std::size_t valueIndex = 0; valueIndex < observation.valueCount; ++valueIndex) {
  691. const sense::DecodedValue& value = snapshot.values[observation.firstValue + valueIndex];
  692. if (value.schemaRow != identity.fieldSchemaRow || value.fieldRow != identity.fieldRow
  693. || value.fieldOrdinal != identity.fieldOrdinal
  694. || value.occurrence != identity.occurrence || value.kind != identity.kind) {
  695. continue;
  696. }
  697. if (found) {
  698. output = {};
  699. return SenseScalarStatus::ambiguous;
  700. }
  701. output.binding = observation.binding;
  702. output.identity = identity;
  703. output.value = value;
  704. output.observationRevision = observation.sequence;
  705. output.tick = observation.tick;
  706. output.sourceGeneration = observation.sourceGeneration;
  707. output.clientMessageSequence = observation.clientMessageSequence;
  708. output.generationPlusOne = observation.generationPlusOne;
  709. output.hasGeneration = observation.hasGeneration;
  710. found = true;
  711. }
  712. }
  713. return found ? SenseScalarStatus::ready : SenseScalarStatus::notFound;
  714. }
  715. /** @return True when two selected scalars have the same presence and raw typed value. */
  716. bool same_sense_scalar_value(const SenseScalarSample& left,
  717. const SenseScalarSample& right) noexcept {
  718. if (left.value.kind != right.value.kind || left.value.present != right.value.present) {
  719. return false;
  720. }
  721. if (!left.value.present) {
  722. return true;
  723. }
  724. return left.value.unsignedValue == right.value.unsignedValue
  725. && left.value.signedValue == right.value.signedValue
  726. && std::bit_cast<std::uint32_t>(left.value.realValue)
  727. == std::bit_cast<std::uint32_t>(right.value.realValue);
  728. }
  729. /** @return True when both samples name the same ActivityClient and reported object generations. */
  730. bool same_sense_scalar_generations(const SenseScalarSample& left,
  731. const SenseScalarSample& right) noexcept {
  732. return left.sourceGeneration != 0 && left.sourceGeneration == right.sourceGeneration
  733. && left.hasGeneration && right.hasGeneration
  734. && left.generationPlusOne == right.generationPlusOne;
  735. }
  736. /** @return Stable text for one exact scalar-selection result. */
  737. const char* sense_scalar_status_name(SenseScalarStatus status) noexcept {
  738. switch (status) {
  739. case SenseScalarStatus::ready:
  740. return "ready";
  741. case SenseScalarStatus::invalidIdentity:
  742. return "invalid_identity";
  743. case SenseScalarStatus::invalidSnapshot:
  744. return "invalid_snapshot";
  745. case SenseScalarStatus::notFound:
  746. return "not_found";
  747. case SenseScalarStatus::ambiguous:
  748. return "ambiguous";
  749. }
  750. return "invalid_status";
  751. }
  752. } // namespace sunrise::server::activity::host