mission_script_lua_slot_api.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. #include <array>
  2. #include <cstddef>
  3. #include <cstdint>
  4. #include <limits>
  5. #include <span>
  6. #include <string_view>
  7. #include "../../../middleware/bap/activity_message/auth_fields.h"
  8. #include "../../../middleware/bap/activity_message/auth_schema_catalog.h"
  9. #include "../../../middleware/bap/activity_message/volume_toggle_auth.h"
  10. #include "mission_script_lua_dispatch.h"
  11. #include "mission_script_lua_internal.h"
  12. namespace sunrise::server::activity::mission::lua_vm::detail {
  13. namespace format = state::activity_sdk::format;
  14. namespace scriptable_auth = middleware::bap::activity_message::scriptable_auth;
  15. namespace auth_fields = middleware::bap::activity_message::auth_fields;
  16. namespace auth_catalog = middleware::bap::activity_message::auth_schema_catalog;
  17. /** Positive 31-bit generations and revisions are the counters every typed Auth body accepts. */
  18. [[nodiscard]] bool valid_counter(lua_Integer value) noexcept {
  19. return value > 0 && value <= auth_fields::kMaximumCounter;
  20. }
  21. /**
  22. * Reads one optional slot-handle argument into a ClientRef.
  23. * @param slotType Slot type the referenced slot must have.
  24. * @param output Left unset when the argument is nil.
  25. * @return False when the argument is present but stale or of another type.
  26. */
  27. [[nodiscard]] bool optional_slot_reference(lua_State* state,
  28. const char* name,
  29. std::uint32_t slotType,
  30. scriptable_auth::Type2LaneClientRef& output) {
  31. lua_getfield(state, 2, name);
  32. bool valid = true;
  33. if (!lua_isnil(state, -1)) {
  34. const auto* const handle =
  35. static_cast<const SlotHandle*>(luaL_checkudata(state, -1, kSlotMetatable));
  36. SlotDefinition definition{};
  37. valid = current_slot(state, *handle, definition) && definition.slotType == slotType
  38. && definition.slotIndex <= auth_fields::kMaximumClientRefIndex;
  39. if (valid) {
  40. output = {definition.registryKey,
  41. static_cast<std::int8_t>(slotType),
  42. static_cast<std::int16_t>(definition.slotIndex)};
  43. }
  44. }
  45. lua_pop(state, 1);
  46. return valid;
  47. }
  48. /** Stages one authored native Auth body on the guarded message-5 route. */
  49. [[nodiscard]] int queue_slot_auth(lua_State* state,
  50. const SlotDefinition& slot,
  51. std::uint32_t schema,
  52. std::size_t bitCount,
  53. std::span<const std::byte> body,
  54. IntentKind kind,
  55. bool active) {
  56. Impl* const impl = impl_from_state(state);
  57. std::array<std::byte, 32> sdkBuildSha256{};
  58. if (!decode_sdk_build_sha256(std::string_view(impl->identity.sdkBuildId.data()),
  59. sdkBuildSha256)) {
  60. return luaL_error(state, "loaded SDK generation identity is invalid");
  61. }
  62. CallFrame& frame = active_frame(state);
  63. Intent intent{};
  64. intent.kind = kind;
  65. intent.active = active;
  66. intent.sdkBuildSha256 = sdkBuildSha256;
  67. intent.firstRow = slot.nativeRow;
  68. if (kind == IntentKind::runActorProgram && active) {
  69. if (impl->definitions.resolveActorProgramSource == nullptr
  70. || !impl->definitions.resolveActorProgramSource(
  71. impl->definitions.context, slot.nativeRow, intent.secondRow)) {
  72. return luaL_error(state, "actor creation has no exact authored source");
  73. }
  74. }
  75. intent.objectTag = slot.objectTag;
  76. intent.registryKey = slot.registryKey;
  77. intent.authSchema = schema;
  78. intent.authBitCount = static_cast<std::uint16_t>(bitCount);
  79. intent.slotIndex = static_cast<std::uint16_t>(slot.slotIndex);
  80. intent.slotType = static_cast<std::uint8_t>(slot.slotType);
  81. intent.authByteCount = static_cast<std::uint16_t>(body.size());
  82. try {
  83. intent.authBody.assign(body.begin(), body.end());
  84. } catch (const std::bad_alloc&) {
  85. frame.intentAllocationFailed = true;
  86. return luaL_error(state, "mission Auth body allocation failed");
  87. }
  88. return queue_intent(state, frame, intent);
  89. }
  90. /** Reads one integer field from a generated directive declaration. */
  91. [[nodiscard]] lua_Integer directive_integer(lua_State* state, int table, const char* field) {
  92. lua_getfield(state, table, field);
  93. const lua_Integer value = luaL_checkinteger(state, -1);
  94. lua_pop(state, 1);
  95. return value;
  96. }
  97. namespace {
  98. constexpr const api::MemberSpec& kVolumeActive = api::slot_member("set_volume_active");
  99. /** Reads one required boolean argument; any other type is an error. */
  100. [[nodiscard]] bool required_boolean(lua_State* state, const char* name) {
  101. lua_getfield(state, 2, name);
  102. if (!lua_isboolean(state, -1)) {
  103. luaL_error(state, "%s must be a boolean", name);
  104. }
  105. const bool value = lua_toboolean(state, -1) != 0;
  106. lua_pop(state, 1);
  107. return value;
  108. }
  109. /** Turns one authored type-60 volume of the same object on or off through its type-32 toggle. */
  110. [[nodiscard]] int slot_set_volume_active(lua_State* state) {
  111. const auto* const handle =
  112. static_cast<const SlotHandle*>(luaL_checkudata(state, 1, kSlotMetatable));
  113. refuse_unknown_arguments(state, kVolumeActive);
  114. const SlotHandle target = checked_argument<SlotHandle>(
  115. state, required_key(kVolumeActive, "volume", api::ValueKind::handle), kSlotMetatable);
  116. const bool active =
  117. required_boolean(state, required_key(kVolumeActive, "active", api::ValueKind::boolean));
  118. SlotDefinition source{}, volume{};
  119. if (!current_slot(state, *handle, source) || !current_slot(state, target, volume)) {
  120. return luaL_error(state, "toggle or volume is stale or invalid");
  121. }
  122. if (source.slotType != scriptable_auth::kType32SlotType
  123. || source.componentClass != scriptable_auth::kType32ComponentClass
  124. || source.authSchema != scriptable_auth::kType32Schema
  125. || (source.flags & format::kSlotSchemaJoinExact) == 0) {
  126. return luaL_error(state, "source must be an exact type-32 toggle sensor");
  127. }
  128. if (volume.slotType != scriptable_auth::kVolumeSlotType || volume.objectTag != source.objectTag
  129. || volume.registryKey != source.registryKey
  130. || volume.slotIndex
  131. > static_cast<std::uint32_t>((std::numeric_limits<std::int16_t>::max)())) {
  132. return luaL_error(state, "volume must be a same-owner type-60 slot");
  133. }
  134. std::array<std::byte, scriptable_auth::kType32ByteCount> body{};
  135. std::size_t written = 0;
  136. if (!scriptable_auth::encode_type32_volume(
  137. {volume.registryKey, static_cast<std::int16_t>(volume.slotIndex), active},
  138. body,
  139. written)) {
  140. return luaL_error(state, "native toggle encoder rejected the volume");
  141. }
  142. return queue_slot_auth(
  143. state, source, scriptable_auth::kType32Schema, scriptable_auth::kType32BitCount, body);
  144. }
  145. /** Pushes one property of the slot's Auth catalog entry, or nil when it has none. */
  146. template <typename Push> void push_auth(lua_State* state, const SlotDefinition& slot, Push push) {
  147. const auth_catalog::Type* const auth =
  148. auth_catalog::find(static_cast<std::uint8_t>(slot.slotType), slot.authSchema);
  149. if (auth == nullptr) {
  150. lua_pushnil(state);
  151. } else {
  152. push(*auth);
  153. }
  154. }
  155. /** Pushes one fixed-size text field of a slot row. */
  156. template <typename Text> void push_text(lua_State* state, const Text& text) {
  157. lua_pushlstring(state, text.data(), text.size());
  158. }
  159. using SlotBinding = Binding<SlotDefinition>;
  160. /** Slot members in table order. play_sequence takes the actor form on an exact combatant. */
  161. constexpr std::array<SlotBinding, std::size(api::kSlotMembers)> kSlotBindings{{
  162. {"row",
  163. nullptr,
  164. [](lua_State* state, const SlotDefinition& slot) { lua_pushinteger(state, slot.localRow); }},
  165. {"applied", &slot_applied},
  166. {"id",
  167. nullptr,
  168. [](lua_State* state, const SlotDefinition& slot) { push_text(state, slot.id); }},
  169. {"name",
  170. nullptr,
  171. [](lua_State* state, const SlotDefinition& slot) { push_text(state, slot.name); }},
  172. {"object_id",
  173. nullptr,
  174. [](lua_State* state, const SlotDefinition& slot) { push_text(state, slot.objectId); }},
  175. {"object_tag",
  176. nullptr,
  177. [](lua_State* state, const SlotDefinition& slot) { lua_pushinteger(state, slot.objectTag); }},
  178. {"registry_key",
  179. nullptr,
  180. [](lua_State* state, const SlotDefinition& slot) {
  181. lua_pushinteger(state, slot.registryKey);
  182. }},
  183. {"slot_index",
  184. nullptr,
  185. [](lua_State* state, const SlotDefinition& slot) { lua_pushinteger(state, slot.slotIndex); }},
  186. {"slot_type",
  187. nullptr,
  188. [](lua_State* state, const SlotDefinition& slot) { lua_pushinteger(state, slot.slotType); }},
  189. {"component_class",
  190. nullptr,
  191. [](lua_State* state, const SlotDefinition& slot) {
  192. lua_pushinteger(state, slot.componentClass);
  193. }},
  194. {"sense_schema",
  195. nullptr,
  196. [](lua_State* state, const SlotDefinition& slot) {
  197. lua_pushinteger(state, slot.senseSchema);
  198. }},
  199. {"auth_schema",
  200. nullptr,
  201. [](lua_State* state, const SlotDefinition& slot) { lua_pushinteger(state, slot.authSchema); }},
  202. {"sense_schema_id",
  203. nullptr,
  204. [](lua_State* state, const SlotDefinition& slot) { push_text(state, slot.senseSchemaId); }},
  205. {"auth_schema_id",
  206. nullptr,
  207. [](lua_State* state, const SlotDefinition& slot) { push_text(state, slot.authSchemaId); }},
  208. {"auth_type",
  209. nullptr,
  210. [](lua_State* state, const SlotDefinition& slot) {
  211. push_auth(state, slot, [state](const auth_catalog::Type& auth) {
  212. lua_pushlstring(state, auth.name.data(), auth.name.size());
  213. });
  214. }},
  215. {"auth_min_bits",
  216. nullptr,
  217. [](lua_State* state, const SlotDefinition& slot) {
  218. push_auth(state, slot, [state](const auth_catalog::Type& auth) {
  219. lua_pushinteger(state, auth.minimumBits);
  220. });
  221. }},
  222. {"auth_max_bits",
  223. nullptr,
  224. [](lua_State* state, const SlotDefinition& slot) {
  225. push_auth(state, slot, [state](const auth_catalog::Type& auth) {
  226. lua_pushinteger(state, auth.maximumBits);
  227. });
  228. }},
  229. {"auth_component_offset",
  230. nullptr,
  231. [](lua_State* state, const SlotDefinition& slot) {
  232. push_auth(state, slot, [state](const auth_catalog::Type& auth) {
  233. if (auth.hasContiguousMirror) {
  234. lua_pushinteger(state, auth.componentOffset);
  235. } else {
  236. lua_pushnil(state);
  237. }
  238. });
  239. }},
  240. {"auth_dynamic",
  241. nullptr,
  242. [](lua_State* state, const SlotDefinition& slot) {
  243. push_auth(state, slot, [state](const auth_catalog::Type& auth) {
  244. lua_pushboolean(state, auth.hasDynamicBody);
  245. });
  246. }},
  247. {"auth_writable",
  248. nullptr,
  249. [](lua_State* state, const SlotDefinition& slot) {
  250. push_auth(state, slot, [state](const auth_catalog::Type& auth) {
  251. lua_pushboolean(state, auth.writable);
  252. });
  253. }},
  254. {"flags",
  255. nullptr,
  256. [](lua_State* state, const SlotDefinition& slot) { lua_pushinteger(state, slot.flags); }},
  257. {"set_object_filter", &slot_set_object_filter},
  258. {"set_volume_active", &slot_set_volume_active},
  259. {"watch_damage", &slot_watch_damage},
  260. {"set_object_active", &slot_set_object_active},
  261. {"set_channel", &slot_set_channel},
  262. {"transition", &slot_transition},
  263. {"set_occupancy_condition", &slot_set_occupancy_condition},
  264. {"generate_map", &slot_generate_map},
  265. {"set_directive", &slot_set_directive},
  266. {"clear_directives", &slot_clear_directives},
  267. {"set_darkness_zone", &slot_set_darkness_zone},
  268. {"set_music_section", &slot_set_music_section},
  269. {"set_interactable_object", &slot_set_interactable_object},
  270. {"set_ghost_link", &slot_set_ghost_link},
  271. {"ghost_link", &slot_ghost_link},
  272. {"set_public_event_state", &slot_set_public_event_state},
  273. {"assign_combat_objective", &slot_assign_combat_objective},
  274. {"play_actor_path", &slot_play_actor_path},
  275. {"play_actor_action", &slot_play_actor_action},
  276. {"retire_actor", &slot_retire_actor},
  277. {"bind_combatant_to_squad", &slot_bind_combatant_to_squad},
  278. {"run_atoms", &slot_run_atoms},
  279. {"fire_trigger", &slot_fire_trigger},
  280. {"disarm_trigger", &slot_disarm_trigger},
  281. {"play_sequence", &slot_play_actor_sequence, nullptr, &exact_combatant_slot},
  282. {"play_sequence", &slot_play_sequence},
  283. {"sequences", &slot_actor_sequences},
  284. {"set_cinematic_active", &slot_set_cinematic_active},
  285. {"reset_objectives", &slot_reset_objectives},
  286. {"advance_task", &slot_advance_task},
  287. {"play_dialogue_cue", &slot_play_dialogue_cue},
  288. {"play_performance", &slot_play_performance},
  289. }};
  290. static_assert(bound_to<SlotDefinition>(api::kSlotMembers, kSlotBindings));
  291. } // namespace
  292. /** Reads one Slot row member, its syntax methods, and its authorized actions. */
  293. [[nodiscard]] int slot_index(lua_State* state) {
  294. const auto* const handle =
  295. static_cast<const SlotHandle*>(luaL_checkudata(state, 1, kSlotMetatable));
  296. SlotDefinition definition{};
  297. if (!current_slot(state, *handle, definition)) {
  298. return luaL_error(state, "activity slot is stale");
  299. }
  300. return dispatch<SlotDefinition>(state, kSlotBindings, lua_string_view(state, 2), definition);
  301. }
  302. void register_slot_metatables(lua_State* state) {
  303. register_metatable(state, kSlotMetatable, &slot_index);
  304. }
  305. } // namespace sunrise::server::activity::mission::lua_vm::detail