activity_sdk_lua_contract.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. #include <algorithm>
  2. #include <array>
  3. #include <cstdio>
  4. #include <vector>
  5. #include "../../../middleware/bap/activity_message/auth_schema_catalog.h"
  6. #include "activity_sdk_lua_artifacts_internal.h"
  7. namespace sunrise::client::content::activity::sdk_generation::lua_artifacts::internal {
  8. /** Emits the manifest, the mission API schema, the LuaLS declarations and the behavior module. */
  9. bool render_contract_files(const Source& source, Bundle& output) noexcept {
  10. try {
  11. const Value manifest = object({
  12. {"schema", string("sunrise-activity-sdk-v11")},
  13. {"format_version", number(format::kVersion)},
  14. {"sdk_build_id", string(digest_hex(source.sdkBuildSha256, true))},
  15. {"sdk_payload_sha256", string(digest_hex(source.sdkPayloadSha256, false))},
  16. {"content_key", string(digest_hex(source.contentKeySha256, true))},
  17. {"logical_ir_sha256", string(digest_hex(source.logicalIrSha256, false))},
  18. {"counts",
  19. object({
  20. {"activities", number(source.activities.size())},
  21. {"slots", number(source.slots.size())},
  22. {"states", number(source.states.size())},
  23. {"squads", number(source.squads.size())},
  24. {"authored_scene_resources", number(source.authoredSceneResources.size())},
  25. {"authored_scene_squad_edges", number(source.authoredSceneSquadEdges.size())},
  26. {"task_targets", number(source.taskTargets.size())},
  27. {"dialogue_cue_texts", number(source.dialogueCueTexts.size())},
  28. {"directive_elements", number(source.directiveElements.size())},
  29. {"behavior_programs", number(source.behaviorPrograms.size())},
  30. {"behavior_inputs", number(source.behaviorInputs.size())},
  31. {"behavior_channel_writes", number(source.behaviorChannelWrites.size())},
  32. {"behavior_owners", number(source.behaviorOwners.size())},
  33. {"behavior_activity_bindings", number(source.behaviorActivityBindings.size())},
  34. {"actor_message_schemas", number(source.actorMessageSchemas.size())},
  35. {"actor_command_definitions", number(source.actorCommandDefinitions.size())},
  36. {"actor_behavior_profiles", number(source.actorBehaviorProfiles.size())},
  37. {"simulation_event_definitions", number(source.simulationEventDefinitions.size())},
  38. {"runtime_schemas", number(source.runtimeSchemas.size())},
  39. {"runtime_fields", number(source.runtimeFields.size())},
  40. {"runtime_type_definitions", number(source.runtimeTypeDefinitions.size())},
  41. {"sobject_rsats", number(source.sobjectRsats.size())},
  42. {"sobject_rsat_descriptors", number(source.sobjectRsatDescriptors.size())},
  43. {"entity_type_definitions", number(source.entityTypeDefinitions.size())},
  44. {"sobject_rsat_field_bindings", number(source.sobjectRsatFieldBindings.size())},
  45. {"actor_state_names", number(source.actorStateNames.size())},
  46. {"actor_sequence_tables", number(source.actorSequenceTables.size())},
  47. {"actor_sequence_entries", number(source.actorSequenceEntries.size())},
  48. {"actor_sequence_bindings", number(source.actorSequenceBindings.size())},
  49. {"authored_scene_event_keys", number(source.authoredSceneEventKeys.size())},
  50. {"dialogue_cues", number(source.dialogueCues.size())},
  51. })},
  52. });
  53. if (!render_json(manifest, 0, output.manifestJson)) {
  54. return false;
  55. }
  56. output.manifestJson.push_back('\n');
  57. Value missionApi{};
  58. if (!build_mission_api_value(missionApi)
  59. || !render_json(missionApi, 0, output.missionApiJson)) {
  60. return false;
  61. }
  62. output.missionApiJson.push_back('\n');
  63. output.activitySdkModule =
  64. "-- Generated LuaLS declarations and stable SDK enums. Do not edit.\n";
  65. append_mission_api_luals(output.activitySdkModule);
  66. output.activitySdkModule +=
  67. R"lua(
  68. ---@class SunriseVector3
  69. ---@field x number
  70. ---@field y number
  71. ---@field z number
  72. ---@class SunriseSlot
  73. ---@field id string
  74. ---@field name string
  75. ---@field index integer
  76. ---@field type integer
  77. ---@field component_class integer
  78. ---@field sense_schema integer
  79. ---@field auth_schema integer
  80. ---@field auth_type string|nil
  81. ---@field auth_min_bits integer|nil
  82. ---@field auth_max_bits integer|nil
  83. ---@field auth_component_offset integer|nil
  84. ---@field auth_dynamic boolean|nil
  85. ---@field auth_writable boolean|nil
  86. ---@field unresourced boolean|nil
  87. ---@class SunriseDirective
  88. ---@field id string
  89. ---@field slot_row integer
  90. ---@field sensor string The directive sensor's mission.Slot value.
  91. ---@field name_hash integer
  92. ---@field element integer
  93. ---@field title string
  94. ---@field description string Empty when the element was authored without one.
  95. ---@field progress string|nil Label shown before the counter, when authored.
  96. ---@field counter boolean|nil True when the HUD shows the lane's progress values.
  97. ---@class SunriseState
  98. ---@field id string
  99. ---@field ordinal integer
  100. ---@field slice_set_index integer Authored package slice-set base.
  101. ---@field region_index integer Effective membership region for this state ordinal.
  102. ---@field hash integer
  103. ---@field value integer
  104. ---@class SunriseSquadAnchor: SunriseVector3
  105. ---@field point integer
  106. ---@class SunriseSquadMember
  107. ---@field actor_class integer|nil
  108. ---@field behavior_config integer|nil
  109. ---@field default_faction integer|nil
  110. ---@class SunriseSquad
  111. ---@field id string
  112. ---@field slot integer
  113. ---@field spawner_config integer
  114. ---@field spawn_rule_config integer
  115. ---@field members SunriseSquadMember[]
  116. ---@field anchors SunriseSquadAnchor[]
  117. ---@class SunriseScene
  118. ---@field id string
  119. ---@field slot integer
  120. ---@field config_tag integer
  121. ---@field resource_tag integer
  122. ---@field event_keys integer[]|nil
  123. ---@class SunriseTaskTarget
  124. ---@field id string
  125. ---@field task_slot integer
  126. ---@field objective_slot integer
  127. ---@field objective_bit integer
  128. ---@field config_tag integer
  129. ---@class SunriseTriggerVolume
  130. ---@field names string[]
  131. ---@field registry_key integer
  132. ---@field slot_type integer
  133. ---@field slot_index integer
  134. ---@field position SunriseVector3
  135. ---@field minimum SunriseVector3
  136. ---@field maximum SunriseVector3
  137. ---@field shape_tag integer
  138. ---@field shape_index integer
  139. ---@field active integer
  140. ---@class SunriseActorAbility
  141. ---@field slot_row integer
  142. ---@field group_hash integer
  143. ---@field request_hash integer
  144. ---@class SunriseCombatTaskGroup
  145. ---@field slot_row integer
  146. ---@field group_index integer
  147. ---@class SunriseMission
  148. ---@field name string
  149. ---@field id string
  150. ---@field tag integer
  151. ---@field states table<string, SunriseState>
  152. ---@field slots table<string, SunriseSlot>
  153. ---@field squads table<string, SunriseSquad>
  154. ---@field scenes table<string, SunriseScene>
  155. ---@field tasks table<string, SunriseTaskTarget>
  156. ---@field trigger_volumes SunriseTriggerVolume[]
  157. ---@field State table<string, integer>
  158. ---@field Slot table<string, string>
  159. ---@field Auth table<string, table<string, string>> Auth slots grouped by their native Auth type.
  160. ---@field Squad table<string, string>
  161. ---@field Scene table<string, string> Exact occurrence-bound authored scene identities.
  162. ---@field SceneInput table<string, table<string, integer>> Keys a scene waits on, by trigger name.
  163. ---@field SceneOutput table<string, table<string, integer>> Keys a scene reports, by trigger name.
  164. ---@field Task table<string, string>
  165. ---@field TaskGroup table<string, table<string, SunriseCombatTaskGroup>>
  166. ---@field ActorAbility table<string, table<string, table<string, SunriseActorAbility>>>
  167. ---@field TriggerVolume table<string, SunriseTriggerVolume>
  168. ---@field Directive table<string, SunriseDirective>
  169. ---@field PerformanceState table<string, table<string, {slot_row: integer, )lua"
  170. R"lua(name_hash: integer, ordinal: integer}>>
  171. ---@field DialogueCue table<string, table<string, integer>>
  172. ---@class SunriseActivity
  173. ---@field client_teleport_reset integer
  174. ---@field name string
  175. ---@field display_name string
  176. ---@field id string
  177. ---@field index integer
  178. ---@field definition_hash integer
  179. ---@field activity_root_tag integer
  180. ---@field scenario_tag integer
  181. ---@field matchmaking_config_tag integer
  182. ---@field mission SunriseMission|nil
  183. local sdk = {}
  184. sdk.EventKind = EventKind
  185. sdk.SlotType = {
  186. SQUAD = 1,
  187. SEQUENCE = 5,
  188. CINEMATIC = 6,
  189. DEVICE = 23,
  190. TRIGGER = 31,
  191. TASK = 38,
  192. SCENE = 43,
  193. TRIGGER_VOLUME = 60,
  194. }
  195. return sdk
  196. )lua";
  197. const std::size_t returnOffset = output.activitySdkModule.rfind("return sdk\n");
  198. if (returnOffset == std::string::npos) {
  199. return false;
  200. }
  201. std::string authTypes = "---@class SunriseAuthType\n"
  202. "---@field name string\n"
  203. "---@field slot_type integer\n"
  204. "---@field schema integer\n"
  205. "---@field struct_bytes integer\n"
  206. "---@field min_bits integer\n"
  207. "---@field max_bits integer\n"
  208. "---@field component_offset integer|nil\n"
  209. "---@field dynamic boolean\n"
  210. "---@field writable boolean\n\n"
  211. "---@type table<string, SunriseAuthType>\n"
  212. "sdk.AuthType = {\n";
  213. std::array<char, 320> authLine{};
  214. for (const auto& auth : middleware::bap::activity_message::auth_schema_catalog::kTypes) {
  215. const int written = auth.hasContiguousMirror
  216. ? std::snprintf(authLine.data(),
  217. authLine.size(),
  218. " [\"%.*s\"] = { name = \"%.*s\", "
  219. "slot_type = %u, schema = 0x%08X, "
  220. "struct_bytes = %u, min_bits = %u, "
  221. "max_bits = %u, component_offset = %u, "
  222. "dynamic = %s, writable = %s },\n",
  223. static_cast<int>(auth.name.size()),
  224. auth.name.data(),
  225. static_cast<int>(auth.name.size()),
  226. auth.name.data(),
  227. auth.slotType,
  228. auth.schema,
  229. auth.structBytes,
  230. auth.minimumBits,
  231. auth.maximumBits,
  232. auth.componentOffset,
  233. auth.hasDynamicBody ? "true" : "false",
  234. auth.writable ? "true" : "false")
  235. : std::snprintf(authLine.data(),
  236. authLine.size(),
  237. " [\"%.*s\"] = { name = \"%.*s\", "
  238. "slot_type = %u, schema = 0x%08X, "
  239. "struct_bytes = %u, min_bits = %u, "
  240. "max_bits = %u, component_offset = nil, "
  241. "dynamic = %s, writable = %s },\n",
  242. static_cast<int>(auth.name.size()),
  243. auth.name.data(),
  244. static_cast<int>(auth.name.size()),
  245. auth.name.data(),
  246. auth.slotType,
  247. auth.schema,
  248. auth.structBytes,
  249. auth.minimumBits,
  250. auth.maximumBits,
  251. auth.hasDynamicBody ? "true" : "false",
  252. auth.writable ? "true" : "false");
  253. if (written <= 0 || static_cast<std::size_t>(written) >= authLine.size()) {
  254. return false;
  255. }
  256. authTypes.append(authLine.data(), static_cast<std::size_t>(written));
  257. }
  258. authTypes.append(R"lua(}
  259. ---@class SunriseType2LaneClientRef
  260. ---@field registry_key integer
  261. ---@field slot_type integer
  262. ---@field slot_index integer
  263. sdk.CombatantLanePrimarySchema = {
  264. REF_BYTE = 0x80807F7F,
  265. U32 = 0x80807F7E,
  266. REAL32 = 0x80807F7D,
  267. REF_BYTE_BOOL = 0x80807F7A,
  268. EMPTY = 0x80807F79,
  269. U6 = 0x80807F80,
  270. U32_BOOL = 0x80807F78,
  271. U32_REAL32 = 0x80807F81,
  272. ALTERNATE_REF_BYTE = 0x80807F77,
  273. TRIPLE_U32_REF_MODES = 0x80807F76,
  274. }
  275. sdk.CombatantLaneSecondarySchema = {
  276. EMPTY_A = 0x80807F8B,
  277. EMPTY_B = 0x80807F8A,
  278. QUANTIZED_11 = 0x80807F89,
  279. }
  280. -- Authored behavior-condition classes embedded in package behavior graphs.
  281. sdk.AuthoredBehaviorFilterClass = {
  282. ACTIVITY_FLAG = 0x80804D83,
  283. ACTOR = 0x80804D82,
  284. AIRBORNE = 0x80804D81,
  285. CHANNEL = 0x80804D7D,
  286. HAS_PARENT = 0x80804D80,
  287. DAMAGE_OWNER = 0x80804D7C,
  288. DEAD = 0x80804D7B,
  289. DISTANCE = 0x80804D7A,
  290. FACTION = 0x80804D78,
  291. FACTION_RELATIONSHIP = 0x80804D76,
  292. HOP_ON = 0x80804D75,
  293. LINE_OF_SIGHT = 0x80804D74,
  294. OBJECT_LABEL = 0x80804D73,
  295. OBJECT_TYPE = 0x80804D72,
  296. PLAYER = 0x80804D71,
  297. RESOURCE = 0x80804D5F,
  298. SAME_OBJECT = 0x80802F95,
  299. SCOREBOARD_STAT = 0x80804D5E,
  300. TEAM_SIDE = 0x80804D5D,
  301. COMPOSITE = 0x80809310,
  302. EXTERNAL = 0x8080930F,
  303. }
  304. -- Compact code-34 Auth predicate schemas. These are a separate runtime registry.
  305. sdk.ObjectFilterPredicateSchema = {
  306. MODE_U32_A = 0x80809571,
  307. MODE_U32_B = 0x80809572,
  308. MODE_ONLY_A = 0x80809573,
  309. UNREGISTERED_MODE_SLOT_REF = 0x80809574,
  310. MODE_U32_C = 0x80809575,
  311. MODE_FLAG_SLOT_REF = 0x80809576,
  312. MODE_SLOT_REF_A = 0x80809577,
  313. MODE_SLOT_REF_B = 0x80809578,
  314. MODE_SLOT_REF_C = 0x80809579,
  315. MODE_I32 = 0x8080957A,
  316. MODE_SLOT_REF_D = 0x8080957B,
  317. MODE_U32_D = 0x8080957C,
  318. MODE_ONLY_B = 0x8080957D,
  319. }
  320. ---@class SunriseObjectFilterModeOnly
  321. ---@field mode integer
  322. ---@class SunriseObjectFilterModeU32: SunriseObjectFilterModeOnly
  323. ---@field value integer
  324. ---@class SunriseObjectFilterModeSlotRef: SunriseObjectFilterModeOnly
  325. ---@field reference SunriseType2LaneClientRef
  326. ---@class SunriseObjectFilterModeFlagSlotRef: SunriseObjectFilterModeSlotRef
  327. ---@field flag boolean
  328. )lua");
  329. append_actor_sequence_contract(source, authTypes);
  330. output.activitySdkModule.insert(returnOffset, authTypes);
  331. output.behaviorModule =
  332. "-- Generated compiled behavior declarations. Do not edit.\n"
  333. "local behavior = {}\n\n"
  334. "behavior.InputSelector = { TARGET_OBJECT = 0, CONTEXT_PRIMARY_OBJECT = 1, "
  335. "CONTEXT_SECONDARY_OBJECT = 2 }\n"
  336. "behavior.InputRole = { ACTION = 1, CONDITION_LEFT = 2, CONDITION_RIGHT = 3 }\n\n"
  337. "behavior.Program = {\n";
  338. std::array<char, 320> line{};
  339. for (const format::BehaviorProgram& row : source.behaviorPrograms) {
  340. const int written = std::snprintf(
  341. line.data(),
  342. line.size(),
  343. " ROOT_%08X = { tag = 0x%08X, first_input = %u, input_count = %u, "
  344. "first_write = %u, write_count = %u, node_count = %u, expression_count = %u },\n",
  345. row.rootTag,
  346. row.rootTag,
  347. row.inputs.first,
  348. row.inputs.count,
  349. row.channelWrites.first,
  350. row.channelWrites.count,
  351. row.nodeCount,
  352. row.expressionCount);
  353. if (written <= 0 || static_cast<std::size_t>(written) >= line.size()) {
  354. return false;
  355. }
  356. output.behaviorModule.append(line.data(), static_cast<std::size_t>(written));
  357. }
  358. output.behaviorModule.append("}\n\nbehavior.Channel = {\n");
  359. std::vector<std::uint32_t> behaviorChannels{};
  360. behaviorChannels.reserve(source.behaviorInputs.size()
  361. + source.behaviorChannelWrites.size());
  362. for (const format::BehaviorInput& row : source.behaviorInputs) {
  363. behaviorChannels.push_back(row.channelHash);
  364. }
  365. for (const format::BehaviorChannelWrite& row : source.behaviorChannelWrites) {
  366. behaviorChannels.push_back(row.channelHash);
  367. }
  368. std::sort(behaviorChannels.begin(), behaviorChannels.end());
  369. behaviorChannels.erase(std::unique(behaviorChannels.begin(), behaviorChannels.end()),
  370. behaviorChannels.end());
  371. for (const std::uint32_t channel : behaviorChannels) {
  372. const int written = std::snprintf(
  373. line.data(), line.size(), " CHANNEL_%08X = 0x%08X,\n", channel, channel);
  374. if (written <= 0 || static_cast<std::size_t>(written) >= line.size()) {
  375. return false;
  376. }
  377. output.behaviorModule.append(line.data(), static_cast<std::size_t>(written));
  378. }
  379. output.behaviorModule.append("}\n\nbehavior.Input = {\n");
  380. for (const format::BehaviorInput& row : source.behaviorInputs) {
  381. const int written =
  382. std::snprintf(line.data(),
  383. line.size(),
  384. " { program = %u, node_offset = 0x%X, expression_offset = 0x%X, "
  385. "channel = behavior.Channel.CHANNEL_%08X, input_or_mode = \"%llu\", "
  386. "native_override = %d, "
  387. "active_field = %u, selector = %u, role = %u },\n",
  388. row.programIndex,
  389. row.nodeOffset,
  390. row.expressionOffset,
  391. row.channelHash,
  392. static_cast<unsigned long long>(row.inputOrMode),
  393. row.nativeOverride,
  394. row.activeField,
  395. static_cast<unsigned int>(row.selector),
  396. static_cast<unsigned int>(row.role));
  397. if (written <= 0 || static_cast<std::size_t>(written) >= line.size()) {
  398. return false;
  399. }
  400. output.behaviorModule.append(line.data(), static_cast<std::size_t>(written));
  401. }
  402. output.behaviorModule.append("}\n\nbehavior.ChannelWrite = {\n");
  403. for (const format::BehaviorChannelWrite& row : source.behaviorChannelWrites) {
  404. const int written = std::snprintf(line.data(),
  405. line.size(),
  406. " { program = %u, node_offset = 0x%X, "
  407. "channel = behavior.Channel.CHANNEL_%08X },\n",
  408. row.programIndex,
  409. row.nodeOffset,
  410. row.channelHash);
  411. if (written <= 0 || static_cast<std::size_t>(written) >= line.size()) {
  412. return false;
  413. }
  414. output.behaviorModule.append(line.data(), static_cast<std::size_t>(written));
  415. }
  416. output.behaviorModule.append("}\n\nbehavior.SubmissionKind = { ACTIVE_NATIVE = 0, PASSIVE "
  417. "= 1, UNRESOLVED = 2 }\n\nbehavior.Owner = {\n");
  418. for (const format::BehaviorOwner& row : source.behaviorOwners) {
  419. const int written =
  420. std::snprintf(line.data(),
  421. line.size(),
  422. " { program = %u, actor_class = %u, config = 0x%08X, "
  423. "config_offset = 0x%X, build = %u, descriptor = %u, subtype = "
  424. "0x%08X, kind = %u },\n",
  425. row.programIndex,
  426. row.actorClassIndex,
  427. row.configTag,
  428. row.configFieldOffset,
  429. row.buildOrdinal,
  430. row.descriptorOrdinal,
  431. row.submitterSubtype,
  432. static_cast<unsigned int>(row.submissionKind));
  433. if (written <= 0 || static_cast<std::size_t>(written) >= line.size()) {
  434. return false;
  435. }
  436. output.behaviorModule.append(line.data(), static_cast<std::size_t>(written));
  437. }
  438. output.behaviorModule.append("}\n\nbehavior.ActivityBinding = {\n");
  439. for (const format::BehaviorActivityBinding& row : source.behaviorActivityBindings) {
  440. const int written = std::snprintf(line.data(),
  441. line.size(),
  442. " { owner = %u, squad = %u, member = %u, scenario "
  443. "= %u, occurrence = %u, state = %u, object = %u },\n",
  444. row.ownerIndex,
  445. row.squadIndex,
  446. row.squadMemberIndex,
  447. row.scenarioIndex,
  448. row.occurrenceIndex,
  449. row.stateIndex,
  450. row.objectIndex);
  451. if (written <= 0 || static_cast<std::size_t>(written) >= line.size()) {
  452. return false;
  453. }
  454. output.behaviorModule.append(line.data(), static_cast<std::size_t>(written));
  455. }
  456. output.behaviorModule.append("}\n\nreturn behavior\n");
  457. return true;
  458. } catch (...) {
  459. output.manifestJson.clear();
  460. output.missionApiJson.clear();
  461. output.activitySdkModule.clear();
  462. output.behaviorModule.clear();
  463. return false;
  464. }
  465. }
  466. } // namespace sunrise::client::content::activity::sdk_generation::lua_artifacts::internal