mission_script_lua_context_api.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. #include <charconv>
  2. #include <cstddef>
  3. #include <cstdint>
  4. #include <limits>
  5. #include <string_view>
  6. #include "../../../state/activity/membership/definition.h"
  7. #include "mission_script_lua_internal.h"
  8. #include "mission_script_lua_names.h"
  9. #include "mission_script_lua_peer_internal.h"
  10. #include "mission_script_lua_resolve.h"
  11. #include "mission_script_lua_types.h"
  12. #include "mission_script_vm_internal.h"
  13. namespace sunrise::server::activity::mission::lua_vm::detail {
  14. namespace {
  15. [[nodiscard]] int context_squad(lua_State* state) {
  16. static_cast<void>(luaL_checkudata(state, 1, kContextMetatable));
  17. SquadDefinition definition{};
  18. if (!resolve_squad(state, 2, definition)) {
  19. return luaL_error(state, "unknown or ambiguous activity squad");
  20. }
  21. push_handle(state, kSquadMetatable, SquadHandle{definition.localRow});
  22. return 1;
  23. }
  24. /**
  25. * Arms a native hard wipe at an authored spawn set, or releases one with its request key.
  26. * The Lua caller passes `release_request` as the decimal string of the original key.
  27. */
  28. [[nodiscard]] int context_restart_checkpoint(lua_State* state) {
  29. static_cast<void>(luaL_checkudata(state, 1, kContextMetatable));
  30. static constexpr std::array<std::string_view, 3> kDeclared{
  31. "region", "spawn_set_hash", "release_request"};
  32. refuse_unknown_arguments(state, kDeclared);
  33. const lua_Integer region = optional_integer_argument(state, "region", -1);
  34. const lua_Integer hash = optional_integer_argument(state, "spawn_set_hash", 0);
  35. if (region < 0 || region > ::sunrise::state::activity::membership::kMaximumSliceSetIndex
  36. || hash <= 0
  37. || hash >= (std::numeric_limits<std::uint32_t>::max)()) {
  38. return luaL_error(state, "checkpoint requires an authored region and spawn-set hash");
  39. }
  40. std::uint64_t release = 0;
  41. lua_getfield(state, 2, "release_request");
  42. if (!lua_isnil(state, -1)) {
  43. std::size_t length = 0;
  44. const char* const value = luaL_checklstring(state, -1, &length);
  45. const auto parsed = std::from_chars(value, value + length, release);
  46. if (parsed.ec != std::errc{} || parsed.ptr != value + length || release == 0) {
  47. return luaL_error(state, "checkpoint release requires the original RequestKey.value");
  48. }
  49. }
  50. lua_pop(state, 1);
  51. Intent intent{};
  52. intent.kind = IntentKind::restartCheckpoint;
  53. intent.checkpointReleaseRequest = release;
  54. intent.effectiveRegion = static_cast<std::int32_t>(region);
  55. intent.checkpointSpawnHash = static_cast<std::uint32_t>(hash);
  56. return queue_intent(state, active_frame(state), intent);
  57. }
  58. [[nodiscard]] int context_scene(lua_State* state) {
  59. static_cast<void>(luaL_checkudata(state, 1, kContextMetatable));
  60. SceneDefinition definition{};
  61. if (!resolve_scene(state, 2, definition)) {
  62. return luaL_error(state, "unknown or ambiguous authored scene");
  63. }
  64. push_handle(state, kSceneMetatable, SceneHandle{definition.localRow});
  65. return 1;
  66. }
  67. [[nodiscard]] int context_slot(lua_State* state) {
  68. static_cast<void>(luaL_checkudata(state, 1, kContextMetatable));
  69. SlotDefinition definition{};
  70. if (!resolve_slot(state, 2, definition)) {
  71. return luaL_error(state, "unknown or ambiguous activity slot");
  72. }
  73. push_handle(state, kSlotMetatable, SlotHandle{definition.localRow});
  74. return 1;
  75. }
  76. } // namespace
  77. /** Resolves the squad one Lua argument names, by handle or by index. */
  78. [[nodiscard]] bool resolve_squad(lua_State* state, int selector, SquadDefinition& output) {
  79. Impl* const impl = impl_from_state(state);
  80. if (impl == nullptr) {
  81. return false;
  82. }
  83. if (lua_isinteger(state, selector)) {
  84. const lua_Integer row = lua_tointeger(state, selector);
  85. return row > 0
  86. && static_cast<std::uint64_t>(row) <= (std::numeric_limits<std::uint32_t>::max)()
  87. && impl->definitions.resolveSquadRow != nullptr
  88. && impl->definitions.resolveSquadRow(
  89. impl->definitions.context, static_cast<std::uint32_t>(row), output);
  90. }
  91. return impl->definitions.resolveSquadId != nullptr
  92. && impl->definitions.resolveSquadId(
  93. impl->definitions.context, lua_string_view(state, selector), output);
  94. }
  95. /** Resolves the scene one Lua argument names, by handle or by index. */
  96. [[nodiscard]] bool resolve_scene(lua_State* state, int selector, SceneDefinition& output) {
  97. Impl* const impl = impl_from_state(state);
  98. if (impl == nullptr) {
  99. return false;
  100. }
  101. if (lua_isinteger(state, selector)) {
  102. const lua_Integer row = lua_tointeger(state, selector);
  103. return row > 0
  104. && static_cast<std::uint64_t>(row) <= (std::numeric_limits<std::uint32_t>::max)()
  105. && impl->definitions.resolveSceneRow != nullptr
  106. && impl->definitions.resolveSceneRow(
  107. impl->definitions.context, static_cast<std::uint32_t>(row), output);
  108. }
  109. return impl->definitions.resolveSceneId != nullptr
  110. && impl->definitions.resolveSceneId(
  111. impl->definitions.context, lua_string_view(state, selector), output);
  112. }
  113. /** Resolves the slot one Lua argument names, by handle or by index. */
  114. [[nodiscard]] bool resolve_slot(lua_State* state, int selector, SlotDefinition& output) {
  115. Impl* const impl = impl_from_state(state);
  116. if (impl == nullptr) {
  117. return false;
  118. }
  119. if (lua_isinteger(state, selector)) {
  120. const lua_Integer row = lua_tointeger(state, selector);
  121. return row > 0
  122. && static_cast<std::uint64_t>(row) <= (std::numeric_limits<std::uint32_t>::max)()
  123. && impl->definitions.resolveSlotRow != nullptr
  124. && impl->definitions.resolveSlotRow(
  125. impl->definitions.context, static_cast<std::uint32_t>(row), output);
  126. }
  127. return impl->definitions.resolveSlotId != nullptr
  128. && impl->definitions.resolveSlotId(
  129. impl->definitions.context, lua_string_view(state, selector), output);
  130. }
  131. [[nodiscard]] bool
  132. resolve_message_name(lua_State* state, std::string_view name, ActivityMessageDefinition& output) {
  133. Impl* const impl = impl_from_state(state);
  134. return impl != nullptr && impl->definitions.resolveActivityMessageName != nullptr
  135. && impl->definitions.resolveActivityMessageName(impl->definitions.context, name, output);
  136. }
  137. /**
  138. * Reads the optional omit list: generated slots whose owning object stays out of the seed.
  139. * @return False with the Lua error already raised.
  140. */
  141. [[nodiscard]] bool parse_seed_omissions(lua_State* state, int index, Intent& intent) {
  142. if (lua_isnoneornil(state, index)) {
  143. return true;
  144. }
  145. luaL_checktype(state, index, LUA_TTABLE);
  146. const lua_Integer count = static_cast<lua_Integer>(lua_rawlen(state, index));
  147. if (count < 0
  148. || static_cast<std::size_t>(count)
  149. > ::sunrise::state::activity::mission::kMissionSeedOmitCapacity) {
  150. static_cast<void>(luaL_argerror(state, index, "mission seed omit list is too long"));
  151. return false;
  152. }
  153. for (lua_Integer entry = 1; entry <= count; ++entry) {
  154. lua_rawgeti(state, index, entry);
  155. SlotDefinition definition{};
  156. const bool resolved = resolve_slot(state, lua_gettop(state), definition);
  157. lua_pop(state, 1);
  158. if (!resolved) {
  159. static_cast<void>(luaL_argerror(state, index, "unknown or ambiguous activity slot"));
  160. return false;
  161. }
  162. intent.seedOmissions[static_cast<std::size_t>(entry - 1)] = {definition.objectTag,
  163. definition.registryKey};
  164. }
  165. intent.seedOmissionCount = static_cast<std::uint8_t>(count);
  166. return true;
  167. }
  168. /** Queues one generated mission state by its authored effective region. */
  169. [[nodiscard]] int context_select_state(lua_State* state) {
  170. static_cast<void>(luaL_checkudata(state, 1, kContextMetatable));
  171. luaL_checktype(state, 2, LUA_TTABLE);
  172. lua_getfield(state, 2, "region_index");
  173. if (!lua_isinteger(state, -1)) {
  174. return luaL_argerror(state, 2, "generated mission state has no integer region_index");
  175. }
  176. const lua_Integer region = lua_tointeger(state, -1);
  177. lua_pop(state, 1);
  178. if (region < 0
  179. || static_cast<std::uint64_t>(region)
  180. > static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)())) {
  181. return luaL_argerror(state, 2, "generated mission state region_index is outside i32");
  182. }
  183. CallFrame& frame = active_frame(state);
  184. Intent intent{};
  185. intent.kind = IntentKind::selectMissionState;
  186. intent.effectiveRegion = static_cast<std::int32_t>(region);
  187. if (!parse_seed_omissions(state, 3, intent)) {
  188. return 0;
  189. }
  190. return queue_intent(state, frame, intent);
  191. }
  192. /** Lua index for the mission context: its collections, phase, variables and timers. */
  193. [[nodiscard]] int context_index(lua_State* state) {
  194. static_cast<void>(luaL_checkudata(state, 1, kContextMetatable));
  195. Impl* const impl = impl_from_state(state);
  196. const std::string_view key = lua_string_view(state, 2);
  197. if (key == "sdk_build_id") {
  198. lua_pushstring(state, impl->identity.sdkBuildId.data());
  199. } else if (key == "activity_id") {
  200. lua_pushstring(state, impl->identity.activityId.data());
  201. } else if (key == "activity_row") {
  202. lua_pushinteger(state, impl->identity.activityRow);
  203. } else if (key == "definition_hash") {
  204. lua_pushinteger(state, impl->identity.definitionHash);
  205. } else if (key == "activity_role") {
  206. lua_pushstring(state, impl->identity.publicTarget ? "public" : "private");
  207. } else if (key == "player_key") {
  208. push_u64_string(state, impl->identity.playerKey);
  209. } else if (key == "sdk") {
  210. push_activity(state);
  211. } else if (key == "lifetime") {
  212. push_lifetime(state);
  213. } else if (key == "peers") {
  214. push_peers(state);
  215. } else if (key == "squad") {
  216. lua_pushcfunction(state, &context_squad);
  217. } else if (key == "scene") {
  218. lua_pushcfunction(state, &context_scene);
  219. } else if (key == "slot") {
  220. lua_pushcfunction(state, &context_slot);
  221. } else if (key == "select_state") {
  222. lua_pushcfunction(state, &context_select_state);
  223. } else if (key == "restart_checkpoint") {
  224. lua_pushcfunction(state, &context_restart_checkpoint);
  225. } else if (key == "set_phase") {
  226. lua_pushcfunction(state, &context_set_phase);
  227. } else if (key == "set_variable") {
  228. lua_pushcfunction(state, &context_set_variable);
  229. } else if (key == "clear_variable") {
  230. lua_pushcfunction(state, &context_clear_variable);
  231. } else if (key == "start_timer") {
  232. lua_pushcfunction(state, &context_start_timer);
  233. } else if (key == "cancel_timer") {
  234. lua_pushcfunction(state, &context_cancel_timer);
  235. } else if (!push_key_context_member(state, key)) {
  236. lua_pushnil(state);
  237. }
  238. return 1;
  239. }
  240. void register_context_metatables(lua_State* state) {
  241. register_metatable(state, kContextMetatable, &context_index);
  242. }
  243. } // namespace sunrise::server::activity::mission::lua_vm::detail