mission_script_vm.h 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534
  1. #pragma once
  2. #include <array>
  3. #include <cstddef>
  4. #include <cstdint>
  5. #include <span>
  6. #include <string_view>
  7. #include "../../../state/activity_sdk/format.h"
  8. #include "../host_runtime.h"
  9. #include "mission_script_catalog_sdk.h"
  10. #include "mission_script_manifest_sdk.h"
  11. #include "mission_script_world_sdk.h"
  12. namespace sunrise::server::activity::mission::lua_vm {
  13. namespace detail {
  14. struct VmAccess;
  15. }
  16. /** Lua ceiling per open program. The largest generated module peaks near 23 MiB. */
  17. inline constexpr std::size_t kArenaByteCapacity = 64U * 1024U * 1024U;
  18. inline constexpr std::size_t kVariableCapacity = state::activity::mission::kVariableCapacity;
  19. inline constexpr std::size_t kTimerCapacity = state::activity::mission::kTimerCapacity;
  20. inline constexpr std::size_t kStateKeyByteCapacity =
  21. state::activity::mission::kStateKeyByteCapacity;
  22. /** One variable string may not outgrow the durable slot the state holds it in. */
  23. inline constexpr std::size_t kVariableStringByteCapacity =
  24. state::activity::mission::kVariableStringByteCapacity;
  25. /** Squad Auth accepts at most fifteen authored member counts. */
  26. inline constexpr std::size_t kSquadMemberCapacity = 15;
  27. /** Script source is bounded before it reaches the parser. */
  28. inline constexpr std::size_t kSourceByteCapacity = 128U * 1024U;
  29. /** One callback is stopped after this many approximate VM instructions. */
  30. inline constexpr std::uint32_t kInstructionBudget = 100'000;
  31. /** Generated mission modules are large declarations and execute only while a VM opens. */
  32. inline constexpr std::uint32_t kInitializationInstructionBudget = 2'000'000;
  33. /** Stable storage holds everything but the arena block, which the open program owns. */
  34. inline constexpr std::size_t kVmStorageByteCapacity = 512U * 1024U;
  35. /** Current generated identity selected by the runtime before a program attaches. */
  36. struct ProgramIdentity final {
  37. std::array<char, 72> sdkBuildId{};
  38. std::array<char, 64> activityId{};
  39. /** The only filesystem search path available to generated-module require calls. */
  40. std::array<char, 1024> sdkLuaSearchPath{};
  41. std::uint32_t activityRow{};
  42. std::uint32_t definitionHash{};
  43. /** The player key the bound link's message 5 binds; the client matches it on its own datum. */
  44. std::uint64_t playerKey{};
  45. /** Exact ActivityClient directory selected for this VM. */
  46. bool publicTarget{};
  47. };
  48. /** One activity-local squad row resolved against the pinned SDK view. */
  49. struct SquadDefinition final {
  50. std::array<std::int32_t, kSquadMemberCapacity> defaultCounts{};
  51. std::string_view id{};
  52. std::string_view name{};
  53. std::uint32_t nativeRow{};
  54. std::uint32_t localRow{};
  55. std::size_t memberCount{};
  56. };
  57. /** One activity-local authored-scene row resolved against the pinned SDK view. */
  58. struct SceneDefinition final {
  59. std::array<char, 512> id{};
  60. std::uint32_t occurrenceRow{};
  61. std::uint32_t slotRow{};
  62. std::uint32_t localRow{};
  63. std::size_t idLength{};
  64. };
  65. /** One scenario-local reusable object slot resolved against the pinned SDK view. */
  66. struct SlotDefinition final {
  67. std::string_view id{};
  68. std::string_view name{};
  69. std::string_view objectId{};
  70. std::string_view senseSchemaId{};
  71. std::string_view authSchemaId{};
  72. std::uint32_t nativeRow{};
  73. std::uint32_t localRow{};
  74. std::uint32_t objectTag{};
  75. std::uint32_t registryKey{};
  76. std::uint32_t slotIndex{};
  77. std::uint32_t slotType{};
  78. std::uint32_t componentClass{};
  79. std::uint32_t senseSchema{};
  80. std::uint32_t authSchema{};
  81. std::uint32_t flags{};
  82. };
  83. /**
  84. * One type-38 task-sensor target whose authored reference was proved to resolve.
  85. * The client calls through that reference without a null check, so an unresolved target is fatal.
  86. */
  87. struct TaskSensorDefinition final {
  88. std::string_view id{};
  89. std::uint32_t localRow{};
  90. std::uint32_t slotRow{};
  91. /** Authored object key the slot record's reference resolves to. */
  92. std::uint32_t targetObjectKey{};
  93. /** Authored bit index the applier clears. It is authored, never a caller parameter. */
  94. std::uint32_t bitIndex{};
  95. };
  96. /**
  97. * One type-68 directive element proved against the authored directive list.
  98. * The client indexes by the element index with no bound, and dereferences the hash lookup with
  99. * no null check. So both must already have resolved before a caller can name this element.
  100. */
  101. struct DirectiveElementDefinition final {
  102. std::string_view id{};
  103. std::uint32_t localRow{};
  104. std::uint32_t slotRow{};
  105. /** Name hash an authored directive owns. A hash owning nothing never becomes a handle. */
  106. std::uint32_t nameHash{};
  107. /** Index inside the owning authored sub-array, already inside its real length. */
  108. std::int32_t elementIndex{};
  109. /** Rows that sub-array holds, so the bound is the array's, never the wire lane's width. */
  110. std::uint32_t elementCount{};
  111. };
  112. /** One state name a type-42 performance sensor may start on the actor it drives. */
  113. struct PerformanceStateDefinition final {
  114. std::uint32_t slotRow{};
  115. /** FNV-1 of the state name in the target actor's state-machine definition. */
  116. std::uint32_t nameHash{};
  117. /** Distinct state names every member of the target squad declares. */
  118. std::uint32_t stateCount{};
  119. };
  120. /** One peer session bound to this destination, as the roster currently holds it. */
  121. struct PeerSession final {
  122. std::uint64_t sessionId{};
  123. /** Record generation, so a replacement is told apart from the record it replaced. */
  124. std::uint64_t sessionGeneration{};
  125. /** Peer client key bound by its join, or zero before one. */
  126. std::uint64_t memberKey{};
  127. /** The identity message 12 publishes for the peer, which is also its message-5 player key. */
  128. std::uint64_t joinIdentity{};
  129. };
  130. /** Peers the VM can report. It is the activity session capacity, not a bound of its own. */
  131. inline constexpr std::size_t kPeerCapacity = state::activity::kSessionCapacity;
  132. /** One generated typed Lua surface owned by an activity-message route. */
  133. struct ActivityMessageSurfaceDefinition final {
  134. std::string_view id{};
  135. std::string_view luaName{};
  136. };
  137. /** Capacity follows the generated mission-surface section, not current msg-5 callability. */
  138. inline constexpr std::size_t kActivityMessageSurfaceCapacity =
  139. state::activity_sdk::format::kMissionSurfaceCount;
  140. /** One checked activity-message row resolved from the pinned SDK view. */
  141. struct ActivityMessageDefinition final {
  142. std::array<ActivityMessageSurfaceDefinition, kActivityMessageSurfaceCapacity> ingressSurfaces{};
  143. std::array<ActivityMessageSurfaceDefinition, kActivityMessageSurfaceCapacity> egressSurfaces{};
  144. std::string_view name{};
  145. std::string_view ingressAdapter{};
  146. std::string_view ingressAdapterPath{};
  147. std::string_view ingressClass{};
  148. std::string_view egressAdapter{};
  149. std::string_view egressAdapterPath{};
  150. std::string_view egressClass{};
  151. std::string_view outputCodec{};
  152. std::string_view outputCodecPath{};
  153. std::string_view stateOwner{};
  154. std::string_view stateOwnerPath{};
  155. std::string_view luaExposure{};
  156. std::string_view ingressDelivery{};
  157. std::string_view egressDelivery{};
  158. std::string_view lateJoinHandoff{};
  159. std::string_view ingressStatus{};
  160. std::string_view egressStatus{};
  161. std::uint32_t localRow{};
  162. std::uint32_t messageId{};
  163. std::uint32_t direction{};
  164. std::uint32_t coverage{};
  165. std::uint32_t definitionHandle{};
  166. std::uint32_t callForm{};
  167. std::uint32_t definitionState{};
  168. std::uint32_t definitionStructSize{};
  169. std::uint32_t wireMinBits{};
  170. std::uint32_t wireMaxBits{};
  171. std::uint32_t fieldCount{};
  172. std::uint32_t namedFieldCount{};
  173. std::uint32_t graphFieldCount{};
  174. std::uint32_t authoredFieldCount{};
  175. std::uint32_t typedLuaSurfaceCount{};
  176. std::uint32_t communicationFlags{};
  177. std::uint32_t flags{};
  178. std::uint8_t ingressSurfaceCount{};
  179. std::uint8_t egressSurfaceCount{};
  180. bool executableRoute{};
  181. };
  182. /** One checked field row owned by an activity message in the pinned SDK view. */
  183. struct ActivityMessageFieldDefinition final {
  184. std::string_view path{};
  185. std::string_view name{};
  186. std::string_view type{};
  187. std::uint32_t localRow{};
  188. std::uint32_t globalRow{};
  189. std::uint32_t messageRow{};
  190. std::uint32_t ordinal{};
  191. std::uint32_t source{};
  192. std::uint32_t structOffset{};
  193. std::uint32_t structOffsetAbs{};
  194. std::uint32_t typeCode{};
  195. std::int64_t bias{};
  196. std::uint32_t bits{};
  197. std::uint32_t bitsMin{};
  198. std::uint32_t bitsMax{};
  199. std::uint32_t widthOrCountOffset{};
  200. std::uint32_t repeat{};
  201. std::uint32_t nestedHandle{};
  202. std::uint32_t ownerHandle{};
  203. std::uint32_t depth{};
  204. std::uint32_t flags{};
  205. std::uint32_t confidence{};
  206. };
  207. /** Complete authenticated binding metadata for the activity owning this script VM. */
  208. struct ActivityBindingDefinition final {
  209. std::string_view internalName{};
  210. std::string_view displayName{};
  211. std::uint32_t selectedActivityRootTag{};
  212. std::uint32_t selectedScenarioTag{};
  213. std::uint32_t matchmakingConfigTag{};
  214. std::uint32_t joinStatus{};
  215. std::uint32_t bindingDisposition{};
  216. std::uint32_t bindingReason{};
  217. std::uint32_t bindingEvidenceBasis{};
  218. std::uint32_t runnableStatus{};
  219. bool fullSdkAcceptable{};
  220. bool hasInternalName{};
  221. bool hasMatchmakingConfig{};
  222. };
  223. /** Which closed tag range one immutable ActivityView collection exposes. */
  224. enum class ActivityBindingTagKind : std::uint8_t {
  225. activityRootCandidates,
  226. scenarioNameCandidates,
  227. evidenceRoots,
  228. };
  229. /** One exact package locator supporting the current activity classification. */
  230. struct ActivityBindingLocatorDefinition final {
  231. std::uint32_t tag{};
  232. std::uint64_t offset{};
  233. std::uint32_t localRow{};
  234. };
  235. using ActionKind = state::activity::mission::IntentKind;
  236. using StateKey = state::activity::mission::StateKey;
  237. using VariableValueKind = state::activity::mission::VariableValueKind;
  238. using VariableValue = state::activity::mission::VariableValue;
  239. using ScriptVariable = state::activity::mission::ScriptVariable;
  240. using MissionTimer = state::activity::mission::MissionTimer;
  241. using IntentKind = ActionKind;
  242. using ResolveSquadRow = bool (*)(const void* context,
  243. std::uint32_t localRow,
  244. SquadDefinition& output) noexcept;
  245. using ResolveSquadId = bool (*)(const void* context,
  246. std::string_view id,
  247. SquadDefinition& output) noexcept;
  248. using ResolveSceneRow = bool (*)(const void* context,
  249. std::uint32_t localRow,
  250. SceneDefinition& output) noexcept;
  251. using ResolveSceneId = bool (*)(const void* context,
  252. std::string_view id,
  253. SceneDefinition& output) noexcept;
  254. using ResolveSlotRow = bool (*)(const void* context,
  255. std::uint32_t localRow,
  256. SlotDefinition& output) noexcept;
  257. using ResolveSlotId = bool (*)(const void* context,
  258. std::string_view id,
  259. SlotDefinition& output) noexcept;
  260. using ResolveSenseSlot = bool (*)(const void* context,
  261. const host::SenseObservationKey& key,
  262. SlotDefinition& output) noexcept;
  263. using ResolveTaskSensorRow = bool (*)(const void* context,
  264. std::uint32_t localRow,
  265. TaskSensorDefinition& output) noexcept;
  266. using ResolveTaskSensorId = bool (*)(const void* context,
  267. std::string_view id,
  268. TaskSensorDefinition& output) noexcept;
  269. using ResolveDirectiveElementRow = bool (*)(const void* context,
  270. std::uint32_t localRow,
  271. DirectiveElementDefinition& output) noexcept;
  272. using ResolveDirectiveElement = bool (*)(const void* context,
  273. std::uint32_t slotRow,
  274. std::uint32_t nameHash,
  275. std::int32_t elementIndex,
  276. DirectiveElementDefinition& output) noexcept;
  277. /** A zero name hash selects the one state the slot's target declares, and refuses any other. */
  278. using ResolvePerformanceState = bool (*)(const void* context,
  279. std::uint32_t slotRow,
  280. std::uint32_t nameHash,
  281. PerformanceStateDefinition& output) noexcept;
  282. using ResolveActivityMessageRow = bool (*)(const void* context,
  283. std::uint32_t localRow,
  284. ActivityMessageDefinition& output) noexcept;
  285. using ResolveActivityMessageId = bool (*)(const void* context,
  286. std::uint32_t messageId,
  287. ActivityMessageDefinition& output) noexcept;
  288. using ResolveActivityMessageName = bool (*)(const void* context,
  289. std::string_view name,
  290. ActivityMessageDefinition& output) noexcept;
  291. using ResolveActivityMessageFieldRow = bool (*)(const void* context,
  292. std::uint32_t messageRow,
  293. std::uint32_t localRow,
  294. ActivityMessageFieldDefinition& output) noexcept;
  295. using ResolveActivityMessageFieldIndex = bool (*)(const void* context,
  296. std::uint32_t messageId,
  297. std::uint32_t globalFieldIndex,
  298. ActivityMessageFieldDefinition& output) noexcept;
  299. using ResolveActivityBinding = bool (*)(const void* context,
  300. ActivityBindingDefinition& output) noexcept;
  301. using ActivityBindingTagCount = std::size_t (*)(const void* context,
  302. ActivityBindingTagKind kind) noexcept;
  303. using ResolveActivityBindingTag = bool (*)(const void* context,
  304. ActivityBindingTagKind kind,
  305. std::uint32_t localRow,
  306. std::uint32_t& output) noexcept;
  307. using ActivityBindingLocatorCount = std::size_t (*)(const void* context) noexcept;
  308. using ResolveActivityBindingLocator = bool (*)(const void* context,
  309. std::uint32_t localRow,
  310. ActivityBindingLocatorDefinition& output) noexcept;
  311. using DefinitionCount = std::size_t (*)(const void* context) noexcept;
  312. /** Native SDK/live projection used by the sandbox; no borrowed pointer is script-visible. */
  313. struct DefinitionApi final {
  314. const void* context{};
  315. ResolveSquadRow resolveSquadRow{};
  316. ResolveSquadId resolveSquadId{};
  317. ResolveSceneRow resolveSceneRow{};
  318. ResolveSceneId resolveSceneId{};
  319. ResolveSlotRow resolveSlotRow{};
  320. ResolveSlotId resolveSlotId{};
  321. ResolveSenseSlot resolveSenseSlot{};
  322. // These resolvers expose generated task and directive rows without script-owned identifiers.
  323. ResolveTaskSensorRow resolveTaskSensorRow{};
  324. ResolveTaskSensorId resolveTaskSensorId{};
  325. ResolveDirectiveElementRow resolveDirectiveElementRow{};
  326. ResolveDirectiveElement resolveDirectiveElement{};
  327. ResolvePerformanceState resolvePerformanceState{};
  328. ResolveActivityMessageRow resolveActivityMessageRow{};
  329. ResolveActivityMessageId resolveActivityMessageId{};
  330. ResolveActivityMessageName resolveActivityMessageName{};
  331. ResolveActivityMessageFieldRow resolveActivityMessageFieldRow{};
  332. ResolveActivityMessageFieldIndex resolveActivityMessageFieldIndex{};
  333. ResolveActivityBinding resolveActivityBinding{};
  334. ActivityBindingTagCount activityBindingTagCount{};
  335. ResolveActivityBindingTag resolveActivityBindingTag{};
  336. ActivityBindingLocatorCount activityBindingLocatorCount{};
  337. ResolveActivityBindingLocator resolveActivityBindingLocator{};
  338. DefinitionCount squadCount{};
  339. DefinitionCount sceneCount{};
  340. DefinitionCount slotCount{};
  341. DefinitionCount taskSensorCount{};
  342. DefinitionCount directiveElementCount{};
  343. DefinitionCount activityMessageCount{};
  344. CatalogDefinitionApi catalog{};
  345. ManifestDefinitionApi manifest{};
  346. WorldDefinitionApi world{};
  347. };
  348. using Intent = state::activity::mission::TypedIntent;
  349. /** Stable program-open outcome. */
  350. enum class OpenStatus : std::uint8_t {
  351. ready,
  352. sourceTooLarge,
  353. outOfMemory,
  354. compileError,
  355. runtimeError,
  356. invalidProgram,
  357. };
  358. /** Stable protected-callback outcome. */
  359. enum class CallStatus : std::uint8_t {
  360. committed,
  361. noHandler,
  362. inactive,
  363. scriptError,
  364. instructionBudget,
  365. outOfMemory,
  366. };
  367. /** Read-only diagnostics copied without entering Lua. */
  368. struct Snapshot final {
  369. std::array<char, 256> lastError{};
  370. std::uint64_t stateRevision{};
  371. std::uint64_t callbacks{};
  372. std::uint64_t committedCallbacks{};
  373. std::uint64_t refusedCallbacks{};
  374. std::uint64_t collections{};
  375. std::uint32_t phase{};
  376. std::size_t pendingIntents{};
  377. std::size_t variableCount{};
  378. std::size_t timerCount{};
  379. std::uint64_t nextTimerSequence{state::activity::mission::kFirstTimerSequence};
  380. std::uint64_t nextIntentKey{state::activity::mission::kFirstIntentKey};
  381. std::int32_t initialStateRegion{-1};
  382. std::size_t arenaBytes{};
  383. std::size_t arenaHighWater{};
  384. std::size_t arenaBytesAfterClose{};
  385. bool active{};
  386. bool faulted{};
  387. bool hasInitialState{};
  388. };
  389. /** Noncopyable Lua state with a fixed arena and RAII-owned work queues. */
  390. class Vm final {
  391. public:
  392. Vm() noexcept;
  393. ~Vm() noexcept;
  394. Vm(const Vm&) = delete;
  395. Vm& operator=(const Vm&) = delete;
  396. private:
  397. friend struct detail::VmAccess;
  398. friend OpenStatus
  399. open(Vm&, const ProgramIdentity&, const DefinitionApi&, std::span<const char>) noexcept;
  400. friend bool rebind(Vm&, const ProgramIdentity&, const DefinitionApi&) noexcept;
  401. friend bool restore_state(Vm&,
  402. std::uint32_t,
  403. std::uint64_t,
  404. std::span<const ScriptVariable>,
  405. std::span<const MissionTimer>,
  406. std::uint64_t,
  407. std::span<const Intent>) noexcept;
  408. friend CallStatus start(Vm&, std::uint64_t) noexcept;
  409. friend CallStatus load(Vm&, std::uint64_t) noexcept;
  410. friend CallStatus
  411. dispatch(Vm&, const host::Event&, const host::ClientMessageSnapshot*, std::uint64_t) noexcept;
  412. friend bool handles_event(const Vm&, host::EventKind) noexcept;
  413. friend bool initial_state_region(const Vm&, std::int32_t&) noexcept;
  414. friend bool pending_intent(const Vm&, Intent&) noexcept;
  415. friend bool snapshot_intents(const Vm&, std::vector<Intent>&) noexcept;
  416. friend bool snapshot_durable_state(const Vm&,
  417. std::span<ScriptVariable>,
  418. std::size_t&,
  419. std::span<MissionTimer>,
  420. std::size_t&,
  421. std::uint64_t&) noexcept;
  422. friend void consume_intent(Vm&) noexcept;
  423. friend void discard_intents(Vm&) noexcept;
  424. friend void fault(Vm&, std::string_view) noexcept;
  425. friend void snapshot(const Vm&, Snapshot&) noexcept;
  426. friend void close(Vm&) noexcept;
  427. alignas(std::max_align_t) std::array<std::byte, kVmStorageByteCapacity> storage_{};
  428. };
  429. /** Opens one text-only, build-matched program in a fresh sandbox. */
  430. [[nodiscard]] OpenStatus open(Vm& vm,
  431. const ProgramIdentity& identity,
  432. const DefinitionApi& definitions,
  433. std::span<const char> source) noexcept;
  434. /**
  435. * Re-points an open program at a new bound view of the same source.
  436. * @return False when no program is open.
  437. */
  438. [[nodiscard]] bool
  439. rebind(Vm& vm, const ProgramIdentity& identity, const DefinitionApi& definitions) noexcept;
  440. /** Restores authoritative state and typed actions before the first callback. */
  441. [[nodiscard]] bool restore_state(Vm& vm,
  442. std::uint32_t phase,
  443. std::uint64_t revision,
  444. std::span<const ScriptVariable> variables,
  445. std::span<const MissionTimer> timers,
  446. std::uint64_t nextTimerSequence,
  447. std::uint64_t nextIntentKey,
  448. std::span<const Intent> pendingIntents) noexcept;
  449. /** Restores authoritative state when no typed actions are pending. */
  450. [[nodiscard]] bool restore_state(Vm& vm, std::uint32_t phase, std::uint64_t revision) noexcept;
  451. /**
  452. * Publishes the current peer set so any callback can read it, on_load included.
  453. * It is instance state, so it persists until the next publish. Peers past kPeerCapacity are
  454. * dropped, which is the same bound the roster itself carries.
  455. */
  456. void publish_peers(Vm& vm, std::span<const PeerSession> peers) noexcept;
  457. /** Runs the optional on_start(context, state) transaction. */
  458. [[nodiscard]] CallStatus start(Vm& vm, std::uint64_t now = 0) noexcept;
  459. /**
  460. * Runs optional on_load(context, state) after same-session VM reattachment.
  461. * It rebuilds only Lua-heap caches and reserves no revision.
  462. */
  463. [[nodiscard]] CallStatus load(Vm& vm, std::uint64_t now = 0) noexcept;
  464. /** Runs the optional callback for the event's exact native kind. */
  465. [[nodiscard]] CallStatus dispatch(Vm& vm,
  466. const host::Event& event,
  467. const host::ClientMessageSnapshot* clientMessage = nullptr,
  468. std::uint64_t now = 0) noexcept;
  469. /** @return True when the program declares the exact event callback. */
  470. [[nodiscard]] bool handles_event(const Vm& vm, host::EventKind kind) noexcept;
  471. /** Reads the optional effective region captured from program.initial_state.region_index. */
  472. [[nodiscard]] bool initial_state_region(const Vm& vm, std::int32_t& output) noexcept;
  473. /** Copies the oldest committed action without consuming it. */
  474. [[nodiscard]] bool pending_intent(const Vm& vm, Intent& output) noexcept;
  475. /** Copies the complete ordered outbox without consuming an action. */
  476. [[nodiscard]] bool snapshot_intents(const Vm& vm, std::vector<Intent>& output) noexcept;
  477. /** Copies complete durable variable and timer candidates without invoking Lua. */
  478. [[nodiscard]] bool snapshot_durable_state(const Vm& vm,
  479. std::span<ScriptVariable> variables,
  480. std::size_t& variableCount,
  481. std::span<MissionTimer> timers,
  482. std::size_t& timerCount,
  483. std::uint64_t& nextTimerSequence,
  484. std::uint64_t& nextIntentKey) noexcept;
  485. /** Removes the oldest action after a native adapter accepted it. */
  486. void consume_intent(Vm& vm) noexcept;
  487. /** Discards every committed action after a terminal native-delivery failure. */
  488. void discard_intents(Vm& vm) noexcept;
  489. /** Stops callbacks after an unrecoverable event or action failure. */
  490. void fault(Vm& vm, std::string_view reason) noexcept;
  491. /** Copies bounded diagnostics without invoking script code. */
  492. void snapshot(const Vm& vm, Snapshot& output) noexcept;
  493. /** Closes one state and releases every allocation back to its fixed arena. */
  494. void close(Vm& vm) noexcept;
  495. [[nodiscard]] const char* status_name(OpenStatus status) noexcept;
  496. [[nodiscard]] const char* status_name(CallStatus status) noexcept;
  497. } // namespace sunrise::server::activity::mission::lua_vm