mission_script_vm.h 28 KB

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