From 8fcb05bfd07c909f2970b7e28ee99170a5cb3e4f Mon Sep 17 00:00:00 2001 From: asarium Date: Mon, 10 Aug 2020 12:15:27 +0200 Subject: [PATCH] Add scripting API for custom execution context This allows a script to create its own execution context so that it can control when a coroutine is valid to play based on custom data. Also adds a function which causes a coroutine to end in an error. --- code/executor/CombinedExecutionContext.cpp | 34 + code/executor/CombinedExecutionContext.h | 21 + code/scripting/api/LuaExecutionContext.cpp | 50 ++ code/scripting/api/LuaExecutionContext.h | 23 + code/scripting/api/libs/async.cpp | 122 +++- code/scripting/api/objs/enums.cpp | 597 +++--------------- code/scripting/api/objs/enums.h | 7 +- code/scripting/api/objs/execution_context.cpp | 67 ++ code/scripting/api/objs/execution_context.h | 25 + code/source_groups.cmake | 6 + 10 files changed, 447 insertions(+), 505 deletions(-) create mode 100644 code/executor/CombinedExecutionContext.cpp create mode 100644 code/executor/CombinedExecutionContext.h create mode 100644 code/scripting/api/LuaExecutionContext.cpp create mode 100644 code/scripting/api/LuaExecutionContext.h create mode 100644 code/scripting/api/objs/execution_context.cpp create mode 100644 code/scripting/api/objs/execution_context.h diff --git a/code/executor/CombinedExecutionContext.cpp b/code/executor/CombinedExecutionContext.cpp new file mode 100644 index 0000000000..debb533822 --- /dev/null +++ b/code/executor/CombinedExecutionContext.cpp @@ -0,0 +1,34 @@ +#include "CombinedExecutionContext.h" + +namespace executor { +CombinedExecutionContext::CombinedExecutionContext(SCP_vector> contexts) + : m_contexts(std::move(contexts)) +{ +} +IExecutionContext::State CombinedExecutionContext::determineContextState() const +{ + bool suspended_context = false; + bool invalid_context = false; + for (const auto& context : m_contexts) { + const auto state = context->determineContextState(); + + if (state == IExecutionContext::State::Suspended) { + suspended_context = true; + } else if (state == IExecutionContext::State::Invalid) { + invalid_context = true; + } + } + + // If there is an invalid context then the combined state is invalid + // If there is not but there is a suspended one, then the state is suspended + if (invalid_context) { + return IExecutionContext::State::Invalid; + } else if (suspended_context) { + return IExecutionContext::State::Suspended; + } else { + return IExecutionContext::State::Valid; + } +} +CombinedExecutionContext::~CombinedExecutionContext() = default; + +} // namespace executor diff --git a/code/executor/CombinedExecutionContext.h b/code/executor/CombinedExecutionContext.h new file mode 100644 index 0000000000..7dee66040a --- /dev/null +++ b/code/executor/CombinedExecutionContext.h @@ -0,0 +1,21 @@ +#pragma once + +#include "IExecutionContext.h" + +#include "gamesequence/gamesequence.h" +#include "utils/event.h" + +namespace executor { + +class CombinedExecutionContext : public IExecutionContext { + public: + CombinedExecutionContext(SCP_vector> contexts); + ~CombinedExecutionContext() override; + + State determineContextState() const override; + + private: + SCP_vector> m_contexts; +}; + +} // namespace executor diff --git a/code/scripting/api/LuaExecutionContext.cpp b/code/scripting/api/LuaExecutionContext.cpp new file mode 100644 index 0000000000..f46ef2ba18 --- /dev/null +++ b/code/scripting/api/LuaExecutionContext.cpp @@ -0,0 +1,50 @@ +#include "LuaExecutionContext.h" + +#include "scripting/ade_args.h" +#include "scripting/api/objs/enums.h" + +namespace scripting { +namespace api { + +LuaExecutionContext::LuaExecutionContext(luacpp::LuaFunction contextFunction) + : m_contextFunction(std::move(contextFunction)) +{ +} +LuaExecutionContext::~LuaExecutionContext() = default; + +executor::IExecutionContext::State LuaExecutionContext::determineContextState() const +{ + auto L = m_contextFunction.getLuaState(); + const auto returnVal = m_contextFunction.call(L); + if (returnVal.size() != 1) { + LuaError(L, "Need exactly one return value for Lua execution context function!"); + return executor::IExecutionContext::State::Invalid; + } + + const auto currentTop = lua_gettop(L); + returnVal[0].pushValue(L); + + enum_h enumValue; + internal::Ade_get_args_lfunction = true; + internal::Ade_get_args_skip = currentTop; + if (!ade_get_args(L, "o", l_Enum.Get(&enumValue))) { + lua_settop(L, currentTop); + return executor::IExecutionContext::State::Invalid; + } + lua_settop(L, currentTop); + + switch (enumValue.index) { + case LE_CONTEXT_INVALID: + return executor::IExecutionContext::State::Invalid; + case LE_CONTEXT_SUSPENDED: + return executor::IExecutionContext::State::Suspended; + case LE_CONTEXT_VALID: + return executor::IExecutionContext::State::Valid; + default: + LuaError(L, "Got invalid enum value %s from Lua context function.", enumValue.getName().c_str()); + return executor::IExecutionContext::State::Invalid; + } +} + +} // namespace api +} // namespace scripting diff --git a/code/scripting/api/LuaExecutionContext.h b/code/scripting/api/LuaExecutionContext.h new file mode 100644 index 0000000000..47e144a756 --- /dev/null +++ b/code/scripting/api/LuaExecutionContext.h @@ -0,0 +1,23 @@ +#pragma once + +#include "executor/IExecutionContext.h" +#include "gamesequence/gamesequence.h" +#include "scripting/lua/LuaFunction.h" +#include "utils/event.h" + +namespace scripting { +namespace api { + +class LuaExecutionContext : public executor::IExecutionContext { + public: + LuaExecutionContext(luacpp::LuaFunction contextFunction); + ~LuaExecutionContext() override; + + State determineContextState() const override; + + private: + luacpp::LuaFunction m_contextFunction; +}; + +} // namespace api +} // namespace scripting diff --git a/code/scripting/api/libs/async.cpp b/code/scripting/api/libs/async.cpp index abe71b6b62..ddb6fae37a 100644 --- a/code/scripting/api/libs/async.cpp +++ b/code/scripting/api/libs/async.cpp @@ -1,9 +1,12 @@ #include "async.h" +#include "executor/CombinedExecutionContext.h" #include "executor/GameStateExecutionContext.h" #include "executor/global_executors.h" #include "scripting/api/LuaCoroutineRunner.h" +#include "scripting/api/LuaExecutionContext.h" +#include "scripting/api/objs/execution_context.h" #include "scripting/api/objs/executor.h" #include "scripting/api/objs/promise.h" #include "scripting/lua/LuaThread.h" @@ -169,7 +172,8 @@ ADE_FUNC(errored, ADE_FUNC(run, l_Async, - "function() => any body", + "function() => any body, [executor executeOn = nil, boolean|execution_context captureContext = true /* Captures " + "game state context by default */]", "Runs an asynchronous function. Inside this function you can use async.await to suspend the function until a " "promise resolves.", "promise", @@ -177,9 +181,24 @@ ADE_FUNC(run, { luacpp::LuaFunction body; executor_h executor; - bool captureContext = true; - if (!ade_get_args(L, "u|ob", &body, l_Executor.Get(&executor), &captureContext)) { - return ADE_RETURN_NIL; + std::shared_ptr context; + + if (lua_type(L, 3) == LUA_TUSERDATA) { + execution_context_h* ctx = nullptr; + if (!ade_get_args(L, "u|oo", &body, l_Executor.Get(&executor), l_ExecutionContext.GetPtr(&ctx))) { + return ADE_RETURN_NIL; + } + if (ctx != nullptr) { + context = ctx->getExecutionContext(); + } + } else { + bool captureContext = true; + if (!ade_get_args(L, "u|ob", &body, l_Executor.Get(&executor), &captureContext)) { + return ADE_RETURN_NIL; + } + if (executor.isValid() && captureContext) { + context = executor::GameStateExecutionContext::captureContext(); + } } auto coroutine = luacpp::LuaThread::create(L, body); @@ -188,14 +207,9 @@ ADE_FUNC(run, return true; }); - std::shared_ptr exeContext; - if (executor.isValid() && captureContext) { - exeContext = executor::GameStateExecutionContext::captureContext(); - } - return ade_set_args(L, "o", - l_Promise.Set(runAsyncCoroutine(std::move(coroutine), executor.getExecutor(), std::move(exeContext)))); + l_Promise.Set(runAsyncCoroutine(std::move(coroutine), executor.getExecutor(), std::move(context)))); } ADE_FUNC(await, @@ -273,5 +287,93 @@ ADE_FUNC(yield, l_Promise.Set(LuaPromise(std::make_shared(executor::currentExecutor())))); } +ADE_FUNC(error, + l_Async, + "...", + "Causes the currently running coroutine to fail with an error with the specified values.", + nullptr, + "Does not return") +{ + // error cannot be used on the main thread since there is nothing that will wait for the promise + if (lua_pushthread(L)) { + // We are the main thread + lua_pop(L, 1); + + LuaError(L, "Tried to cause a coroutine error on the main thread! That is not supported."); + return ADE_RETURN_NIL; + } + lua_pop(L, 1); + + auto nargs = lua_gettop(L); + luacpp::LuaValueList values; + values.reserve(nargs); + + for (int i = 1; i <= nargs; ++i) { + luacpp::LuaValue val; + val.setReference(luacpp::UniqueLuaReference::create(L, i)); + values.push_back(val); + } + + // Push an errored promise on the stack and then yield the coroutine. The awaiter will get this promise and stop + // execution. + ade_set_args(L, "o", l_Promise.Set(LuaPromise::errored(values))); + return lua_yield(L, 1); +} + +//**********SUBLIBRARY: async/context +ADE_LIB_DERIV(l_Async_Context, "context", nullptr, "Support library for creating execution contexts.", l_Async); + +ADE_FUNC(captureGameState, + l_Async_Context, + nullptr, + "Captures the current game state as an execution context", + "execution_context", + "The execution context or invalid handle on error") +{ + return ade_set_args(L, + "o", + l_ExecutionContext.Set(execution_context_h(executor::GameStateExecutionContext::captureContext()))); +} + +ADE_FUNC(createLuaState, + l_Async_Context, + "function() => enumeration", + "Creates an execution state by storing the passed function and calling that when the state is required.", + "execution_context", + "The execution context or invalid handle on error") +{ + luacpp::LuaFunction body; + if (!ade_get_args(L, "u", &body)) { + return ade_set_args(L, "o", l_ExecutionContext.Set(execution_context_h(nullptr))); + } + + return ade_set_args(L, + "o", + l_ExecutionContext.Set(execution_context_h(std::make_shared(std::move(body))))); +} + +ADE_FUNC(combineContexts, + l_Async_Context, + "execution_context... contexts", + "Combines several execution contexts into a single one by only return a valid state if all contexts are valid.", + "execution_context", + "The execution context or invalid handle on error") +{ + SCP_vector> contexts; + for (int i = 0; i < lua_gettop(L); ++i) { + execution_context_h* ctx; + internal::Ade_get_args_skip = i; + if (!ade_get_args(L, "o", l_ExecutionContext.GetPtr(&ctx))) { + return ade_set_args(L, "o", l_ExecutionContext.Set(execution_context_h(nullptr))); + } + contexts.emplace_back(ctx->getExecutionContext()); + } + + return ade_set_args(L, + "o", + l_ExecutionContext.Set( + execution_context_h(std::make_shared(std::move(contexts))))); +} + } // namespace api } // namespace scripting diff --git a/code/scripting/api/objs/enums.cpp b/code/scripting/api/objs/enums.cpp index c09becab5e..d80e32d8ea 100644 --- a/code/scripting/api/objs/enums.cpp +++ b/code/scripting/api/objs/enums.cpp @@ -13,494 +13,95 @@ namespace api { //or after. Don't forget to increment NEXT INDEX after you're done. //===================================== //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +// clang-format off flag_def_list Enumerations[] = { - { - "ALPHABLEND_FILTER", - LE_ALPHABLEND_FILTER, - 0 - }, - - { - "ALPHABLEND_NONE", - LE_ALPHABLEND_NONE, - 0 - }, - - { - "CFILE_TYPE_NORMAL", - LE_CFILE_TYPE_NORMAL, - 0 - }, - - { - "CFILE_TYPE_MEMORY_MAPPED", - LE_CFILE_TYPE_MEMORY_MAPPED, - 0 - }, - - { - "MOUSE_LEFT_BUTTON", - LE_MOUSE_LEFT_BUTTON, - 0 - }, - - { - "MOUSE_RIGHT_BUTTON", - LE_MOUSE_RIGHT_BUTTON, - 0 - }, - - { - "MOUSE_MIDDLE_BUTTON", - LE_MOUSE_MIDDLE_BUTTON, - 0 - }, - - { - "ORDER_ATTACK", - LE_ORDER_ATTACK, - 0 - }, - - { - "ORDER_ATTACK_ANY", - LE_ORDER_ATTACK_ANY, - 0 - }, - - { - "ORDER_DEPART", - LE_ORDER_DEPART, - 0 - }, - - { - "ORDER_DISABLE", - LE_ORDER_DISABLE, - 0 - }, - - { - "ORDER_DISARM", - LE_ORDER_DISARM, - 0 - }, - - { - "ORDER_DOCK", - LE_ORDER_DOCK, - 0 - }, - - { - "ORDER_EVADE", - LE_ORDER_EVADE, - 0 - }, - - { - "ORDER_FLY_TO", - LE_ORDER_FLY_TO, - 0 - }, - - { - "ORDER_FORM_ON_WING", - LE_ORDER_FORM_ON_WING, - 0 - }, - - { - "ORDER_GUARD", - LE_ORDER_GUARD, - 0 - }, - - { - "ORDER_IGNORE_SHIP", - LE_ORDER_IGNORE, - 0 - }, - - { - "ORDER_KEEP_SAFE_DISTANCE", - LE_ORDER_KEEP_SAFE_DISTANCE, - 0 - }, - - { - "ORDER_PLAY_DEAD", - LE_ORDER_PLAY_DEAD, - 0 - }, - - { - "ORDER_PLAY_DEAD_PERSISTENT", - LE_ORDER_PLAY_DEAD_PERSISTENT, - 0 - }, - - { - "ORDER_REARM", - LE_ORDER_REARM, - 0 - }, - - { - "ORDER_STAY_NEAR", - LE_ORDER_STAY_NEAR, - 0 - }, - - { - "ORDER_STAY_STILL", - LE_ORDER_STAY_STILL, - 0 - }, - - { - "ORDER_UNDOCK", - LE_ORDER_UNDOCK, - 0 - }, - - { - "ORDER_WAYPOINTS", - LE_ORDER_WAYPOINTS, - 0 - }, - - { - "ORDER_WAYPOINTS_ONCE", - LE_ORDER_WAYPOINTS_ONCE, - 0 - }, - - { - "ORDER_ATTACK_WING", - LE_ORDER_ATTACK_WING, - 0 - }, - - { - "ORDER_GUARD_WING", - LE_ORDER_GUARD_WING, - 0 - }, - - { - "ORDER_ATTACK_SHIP_CLASS", - LE_ORDER_ATTACK_SHIP_CLASS, - 0 - }, - - { - "PARTICLE_DEBUG", - LE_PARTICLE_DEBUG, - 0 - }, - - { - "PARTICLE_BITMAP", - LE_PARTICLE_BITMAP, - 0 - }, - - { - "PARTICLE_FIRE", - LE_PARTICLE_FIRE, - 0 - }, - - { - "PARTICLE_SMOKE", - LE_PARTICLE_SMOKE, - 0 - }, - - { - "PARTICLE_SMOKE2", - LE_PARTICLE_SMOKE2, - 0 - }, - - { - "PARTICLE_PERSISTENT_BITMAP", - LE_PARTICLE_PERSISTENT_BITMAP, - 0 - }, - - { - "SEXPVAR_CAMPAIGN_PERSISTENT", - LE_SEXPVAR_CAMPAIGN_PERSISTENT, - 0 - }, - - { - "SEXPVAR_NOT_PERSISTENT", - LE_SEXPVAR_NOT_PERSISTENT, - 0 - }, - - { - "SEXPVAR_PLAYER_PERSISTENT", - LE_SEXPVAR_PLAYER_PERSISTENT, - 0 - }, - - { - "SEXPVAR_TYPE_NUMBER", - LE_SEXPVAR_TYPE_NUMBER, - 0 - }, - - { - "SEXPVAR_TYPE_STRING", - LE_SEXPVAR_TYPE_STRING, - 0 - }, - - { - "TEXTURE_STATIC", - LE_TEXTURE_STATIC, - 0 - }, - - { - "TEXTURE_DYNAMIC", - LE_TEXTURE_DYNAMIC, - 0 - }, - - { - "LOCK", - LE_LOCK, - 0 - }, - - { - "UNLOCK", - LE_UNLOCK, - 0 - }, - - { - "NONE", - LE_NONE, - 0 - }, - - { - "SHIELD_FRONT", - LE_SHIELD_FRONT, - 0 - }, - - { - "SHIELD_LEFT", - LE_SHIELD_LEFT, - 0 - }, - - { - "SHIELD_RIGHT", - LE_SHIELD_RIGHT, - 0 - }, - - { - "SHIELD_BACK", - LE_SHIELD_BACK, - 0 - }, - - { - "MISSION_REPEAT", - LE_MISSION_REPEAT, - 0 - }, - - { - "NORMAL_CONTROLS", - LE_NORMAL_CONTROLS, - 0 - }, - - { - "LUA_STEERING_CONTROLS", - LE_LUA_STEERING_CONTROLS, - 0 - }, - - { - "LUA_FULL_CONTROLS", - LE_LUA_FULL_CONTROLS, - 0 - }, - - { - "NORMAL_BUTTON_CONTROLS", - LE_NORMAL_BUTTON_CONTROLS, - 0 - }, - - { - "LUA_ADDITIVE_BUTTON_CONTROL", - LE_LUA_ADDITIVE_BUTTON_CONTROL, - 0 - }, - - { - "LUA_OVERRIDE_BUTTON_CONTROL", - LE_LUA_OVERRIDE_BUTTON_CONTROL, - 0 - }, - - { - "VM_INTERNAL", - LE_VM_INTERNAL, - 0 - }, - - { - "VM_EXTERNAL", - LE_VM_EXTERNAL, - 0 - }, - - { - "VM_TRACK", - LE_VM_TRACK, - 0 - }, - - { - "VM_DEAD_VIEW", - LE_VM_DEAD_VIEW, - 0 - }, - - { - "VM_CHASE", - LE_VM_CHASE, - 0 - }, - - { - "VM_OTHER_SHIP", - LE_VM_OTHER_SHIP, - 0 - }, - - { - "VM_EXTERNAL_CAMERA_LOCKED", - LE_VM_EXTERNAL_CAMERA_LOCKED, - 0 - }, - - { - "VM_CAMERA_LOCKED", - LE_VM_CAMERA_LOCKED, - 0 - }, - - { - "VM_WARP_CHASE", - LE_VM_WARP_CHASE, - 0 - }, - - { - "VM_PADLOCK_UP", - LE_VM_PADLOCK_UP, - 0 - }, - - { - "VM_PADLOCK_REAR", - LE_VM_PADLOCK_REAR, - 0 - }, - - { - "VM_PADLOCK_LEFT", - LE_VM_PADLOCK_LEFT, - 0 - }, - - { - "VM_PADLOCK_RIGHT", - LE_VM_PADLOCK_RIGHT, - 0 - }, - - { - "VM_WARPIN_ANCHOR", - LE_VM_WARPIN_ANCHOR, - 0 - }, - - { - "VM_TOPDOWN", - LE_VM_TOPDOWN, - 0 - }, - - { - "VM_FREECAMERA", - LE_VM_FREECAMERA, - 0 - }, - - { - "VM_CENTERING", - LE_VM_CENTERING, - 0 - }, - - { - "MESSAGE_PRIORITY_LOW", - LE_MESSAGE_PRIORITY_LOW, - 0 - }, - - { - "MESSAGE_PRIORITY_NORMAL", - LE_MESSAGE_PRIORITY_NORMAL, - 0 - }, - - { - "MESSAGE_PRIORITY_HIGH", - LE_MESSAGE_PRIORITY_HIGH, - 0 - }, - { - "OPTION_TYPE_SELECTION", - LE_OPTION_TYPE_SELECTION, - 0 - }, - { - "OPTION_TYPE_RANGE", - LE_OPTION_TYPE_RANGE, - 0 - }, - { - "AUDIOSTREAM_EVENTMUSIC", - LE_ASF_EVENTMUSIC, - 0 - }, - { - "AUDIOSTREAM_MENUMUSIC", - LE_ASF_MENUMUSIC, - 0 - }, - { - "AUDIOSTREAM_VOICE", - LE_ASF_VOICE, - 0 - }, + {"ALPHABLEND_FILTER", LE_ALPHABLEND_FILTER, 0}, + {"ALPHABLEND_NONE", LE_ALPHABLEND_NONE, 0}, + {"CFILE_TYPE_NORMAL", LE_CFILE_TYPE_NORMAL, 0}, + {"CFILE_TYPE_MEMORY_MAPPED", LE_CFILE_TYPE_MEMORY_MAPPED, 0}, + {"MOUSE_LEFT_BUTTON", LE_MOUSE_LEFT_BUTTON, 0}, + {"MOUSE_RIGHT_BUTTON", LE_MOUSE_RIGHT_BUTTON, 0}, + {"MOUSE_MIDDLE_BUTTON", LE_MOUSE_MIDDLE_BUTTON, 0}, + {"ORDER_ATTACK", LE_ORDER_ATTACK, 0}, + {"ORDER_ATTACK_ANY", LE_ORDER_ATTACK_ANY, 0}, + {"ORDER_DEPART", LE_ORDER_DEPART, 0}, + {"ORDER_DISABLE", LE_ORDER_DISABLE, 0}, + {"ORDER_DISARM", LE_ORDER_DISARM, 0}, + {"ORDER_DOCK", LE_ORDER_DOCK, 0}, + {"ORDER_EVADE", LE_ORDER_EVADE, 0}, + {"ORDER_FLY_TO", LE_ORDER_FLY_TO, 0}, + {"ORDER_FORM_ON_WING", LE_ORDER_FORM_ON_WING, 0}, + {"ORDER_GUARD", LE_ORDER_GUARD, 0}, + {"ORDER_IGNORE_SHIP", LE_ORDER_IGNORE, 0}, + {"ORDER_KEEP_SAFE_DISTANCE", LE_ORDER_KEEP_SAFE_DISTANCE, 0}, + {"ORDER_PLAY_DEAD", LE_ORDER_PLAY_DEAD, 0}, + {"ORDER_PLAY_DEAD_PERSISTENT", LE_ORDER_PLAY_DEAD_PERSISTENT, 0}, + {"ORDER_REARM", LE_ORDER_REARM, 0}, + {"ORDER_STAY_NEAR", LE_ORDER_STAY_NEAR, 0}, + {"ORDER_STAY_STILL", LE_ORDER_STAY_STILL, 0}, + {"ORDER_UNDOCK", LE_ORDER_UNDOCK, 0}, + {"ORDER_WAYPOINTS", LE_ORDER_WAYPOINTS, 0}, + {"ORDER_WAYPOINTS_ONCE", LE_ORDER_WAYPOINTS_ONCE, 0}, + {"ORDER_ATTACK_WING", LE_ORDER_ATTACK_WING, 0}, + {"ORDER_GUARD_WING", LE_ORDER_GUARD_WING, 0}, + {"ORDER_ATTACK_SHIP_CLASS", LE_ORDER_ATTACK_SHIP_CLASS, 0}, + {"PARTICLE_DEBUG", LE_PARTICLE_DEBUG, 0}, + {"PARTICLE_BITMAP", LE_PARTICLE_BITMAP, 0}, + {"PARTICLE_FIRE", LE_PARTICLE_FIRE, 0}, + {"PARTICLE_SMOKE", LE_PARTICLE_SMOKE, 0}, + {"PARTICLE_SMOKE2", LE_PARTICLE_SMOKE2, 0}, + {"PARTICLE_PERSISTENT_BITMAP", LE_PARTICLE_PERSISTENT_BITMAP, 0}, + {"SEXPVAR_CAMPAIGN_PERSISTENT", LE_SEXPVAR_CAMPAIGN_PERSISTENT, 0}, + {"SEXPVAR_NOT_PERSISTENT", LE_SEXPVAR_NOT_PERSISTENT, 0}, + {"SEXPVAR_PLAYER_PERSISTENT", LE_SEXPVAR_PLAYER_PERSISTENT, 0}, + {"SEXPVAR_TYPE_NUMBER", LE_SEXPVAR_TYPE_NUMBER, 0}, + {"SEXPVAR_TYPE_STRING", LE_SEXPVAR_TYPE_STRING, 0}, + {"TEXTURE_STATIC", LE_TEXTURE_STATIC, 0}, + {"TEXTURE_DYNAMIC", LE_TEXTURE_DYNAMIC, 0}, + {"LOCK", LE_LOCK, 0}, + {"UNLOCK", LE_UNLOCK, 0}, + {"NONE", LE_NONE, 0}, + {"SHIELD_FRONT", LE_SHIELD_FRONT, 0}, + {"SHIELD_LEFT", LE_SHIELD_LEFT, 0}, + {"SHIELD_RIGHT", LE_SHIELD_RIGHT, 0}, + {"SHIELD_BACK", LE_SHIELD_BACK, 0}, + {"MISSION_REPEAT", LE_MISSION_REPEAT, 0}, + {"NORMAL_CONTROLS", LE_NORMAL_CONTROLS, 0}, + {"LUA_STEERING_CONTROLS", LE_LUA_STEERING_CONTROLS, 0}, + {"LUA_FULL_CONTROLS", LE_LUA_FULL_CONTROLS, 0}, + {"NORMAL_BUTTON_CONTROLS", LE_NORMAL_BUTTON_CONTROLS, 0}, + {"LUA_ADDITIVE_BUTTON_CONTROL", LE_LUA_ADDITIVE_BUTTON_CONTROL, 0}, + {"LUA_OVERRIDE_BUTTON_CONTROL", LE_LUA_OVERRIDE_BUTTON_CONTROL, 0}, + {"VM_INTERNAL", LE_VM_INTERNAL, 0}, + {"VM_EXTERNAL", LE_VM_EXTERNAL, 0}, + {"VM_TRACK", LE_VM_TRACK, 0}, + {"VM_DEAD_VIEW", LE_VM_DEAD_VIEW, 0}, + {"VM_CHASE", LE_VM_CHASE, 0}, + {"VM_OTHER_SHIP", LE_VM_OTHER_SHIP, 0}, + {"VM_EXTERNAL_CAMERA_LOCKED", LE_VM_EXTERNAL_CAMERA_LOCKED, 0}, + {"VM_CAMERA_LOCKED", LE_VM_CAMERA_LOCKED, 0}, + {"VM_WARP_CHASE", LE_VM_WARP_CHASE, 0}, + {"VM_PADLOCK_UP", LE_VM_PADLOCK_UP, 0}, + {"VM_PADLOCK_REAR", LE_VM_PADLOCK_REAR, 0}, + {"VM_PADLOCK_LEFT", LE_VM_PADLOCK_LEFT, 0}, + {"VM_PADLOCK_RIGHT", LE_VM_PADLOCK_RIGHT, 0}, + {"VM_WARPIN_ANCHOR", LE_VM_WARPIN_ANCHOR, 0}, + {"VM_TOPDOWN", LE_VM_TOPDOWN, 0}, + {"VM_FREECAMERA", LE_VM_FREECAMERA, 0}, + {"VM_CENTERING", LE_VM_CENTERING, 0}, + {"MESSAGE_PRIORITY_LOW", LE_MESSAGE_PRIORITY_LOW, 0}, + {"MESSAGE_PRIORITY_NORMAL", LE_MESSAGE_PRIORITY_NORMAL, 0}, + {"MESSAGE_PRIORITY_HIGH", LE_MESSAGE_PRIORITY_HIGH, 0}, + {"OPTION_TYPE_SELECTION", LE_OPTION_TYPE_SELECTION, 0}, + {"OPTION_TYPE_RANGE", LE_OPTION_TYPE_RANGE, 0}, + {"AUDIOSTREAM_EVENTMUSIC", LE_ASF_EVENTMUSIC, 0}, + {"AUDIOSTREAM_MENUMUSIC", LE_ASF_MENUMUSIC, 0}, + {"AUDIOSTREAM_VOICE", LE_ASF_VOICE, 0}, + {"CONTEXT_VALID", LE_CONTEXT_VALID, 0}, + {"CONTEXT_SUSPENDED", LE_CONTEXT_SUSPENDED, 0}, + {"CONTEXT_INVALID", LE_CONTEXT_INVALID, 0}, }; +// clang-format on //DO NOT FORGET to increment NEXT INDEX: !!!!!!!!!!!!! @@ -511,10 +112,21 @@ enum_h::enum_h() { index = -1; is_constant = false; } -enum_h::enum_h(int n_index) { +enum_h::enum_h(int n_index) +{ index = n_index; is_constant = false; } +SCP_string enum_h::getName() const +{ + for (size_t i = 0; i < Num_enumerations; i++) { + if (Enumerations[i].def == index) { + return Enumerations[i].name; + } + } + + return SCP_string(); +} bool enum_h::IsValid() const { return (index > -1 && index < ENUM_NEXT_INDEX); } ADE_OBJ(l_Enum, enum_h, "enumeration", "Enumeration object"); @@ -552,14 +164,11 @@ ADE_FUNC(__tostring, return ade_set_args(L, "s", ""); } - uint i; - for (i = 0; i < Num_enumerations; i++) { - if (Enumerations[i].def == e->index) { - return ade_set_args(L, "s", Enumerations[i].name); - } + const auto name = e->getName(); + if (name.empty()) { + return ade_set_args(L, "s", ""); } - - return ade_set_args(L, "s", ""); + return ade_set_args(L, "s", name); } ADE_FUNC(__eq, diff --git a/code/scripting/api/objs/enums.h b/code/scripting/api/objs/enums.h index e8f351ca27..b76ea8336d 100644 --- a/code/scripting/api/objs/enums.h +++ b/code/scripting/api/objs/enums.h @@ -92,8 +92,11 @@ const int32_t LE_OPTION_TYPE_RANGE = 79; const int32_t LE_ASF_EVENTMUSIC = 80; const int32_t LE_ASF_MENUMUSIC = 81; const int32_t LE_ASF_VOICE = 82; +const int32_t LE_CONTEXT_VALID = 83; +const int32_t LE_CONTEXT_SUSPENDED = 84; +const int32_t LE_CONTEXT_INVALID = 85; -const int ENUM_NEXT_INDEX = 83; // <<<<<<<<<<<<<<<<<<<<<< +const int ENUM_NEXT_INDEX = 86; // <<<<<<<<<<<<<<<<<<<<<< extern flag_def_list Enumerations[]; extern size_t Num_enumerations; @@ -105,6 +108,8 @@ struct enum_h { explicit enum_h(int n_index); + SCP_string getName() const; + bool IsValid() const; }; diff --git a/code/scripting/api/objs/execution_context.cpp b/code/scripting/api/objs/execution_context.cpp new file mode 100644 index 0000000000..25ca75c33c --- /dev/null +++ b/code/scripting/api/objs/execution_context.cpp @@ -0,0 +1,67 @@ +#include "execution_context.h" + +#include "enums.h" + +namespace scripting { +namespace api { + +execution_context_h::execution_context_h(std::shared_ptr executionContext) + : m_executionContext(std::move(executionContext)) +{ +} +const std::shared_ptr& execution_context_h::getExecutionContext() const +{ + return m_executionContext; +} +bool execution_context_h::isValid() const { return m_executionContext != nullptr; } + +ADE_OBJ(l_ExecutionContext, + execution_context_h, + "execution_context", + "An execution context for asynchonous operations"); + +ADE_FUNC(determineState, + l_ExecutionContext, + nullptr, + "Determines the current state of the context.", + "enum", + "One of the CONTEXT_ enumerations") +{ + execution_context_h* context = nullptr; + if (!ade_get_args(L, "o", l_ExecutionContext.GetPtr(&context))) { + return ADE_RETURN_FALSE; + } + + if (!context->isValid()) { + return ade_set_args(L, "o", l_Enum.Set(enum_h(LE_CONTEXT_INVALID))); + } + + const auto state = context->getExecutionContext()->determineContextState(); + switch (state) { + case executor::IExecutionContext::State::Valid: + return ade_set_args(L, "o", l_Enum.Set(enum_h(LE_CONTEXT_VALID))); + case executor::IExecutionContext::State::Suspended: + return ade_set_args(L, "o", l_Enum.Set(enum_h(LE_CONTEXT_SUSPENDED))); + case executor::IExecutionContext::State::Invalid: + default: + return ade_set_args(L, "o", l_Enum.Set(enum_h(LE_CONTEXT_INVALID))); + } +} + +ADE_FUNC(isValid, + l_ExecutionContext, + nullptr, + "Determines if the handle is valid", + "boolean", + "true if valid, false otherwise") +{ + execution_context_h* context = nullptr; + if (!ade_get_args(L, "o", l_ExecutionContext.GetPtr(&context))) { + return ADE_RETURN_FALSE; + } + + return ade_set_args(L, "b", context != nullptr && context->isValid()); +} + +} // namespace api +} // namespace scripting diff --git a/code/scripting/api/objs/execution_context.h b/code/scripting/api/objs/execution_context.h new file mode 100644 index 0000000000..a8171beba6 --- /dev/null +++ b/code/scripting/api/objs/execution_context.h @@ -0,0 +1,25 @@ + +#pragma once + +#include "executor/IExecutionContext.h" +#include "scripting/ade_api.h" + +namespace scripting { +namespace api { + +class execution_context_h { + public: + execution_context_h(std::shared_ptr executionContext); + + const std::shared_ptr& getExecutionContext() const; + + bool isValid() const; + + private: + std::shared_ptr m_executionContext; +}; + +DECLARE_ADE_OBJ(l_ExecutionContext, execution_context_h); + +} // namespace api +} // namespace scripting diff --git a/code/source_groups.cmake b/code/source_groups.cmake index 7ec63029b3..b14d9cac58 100644 --- a/code/source_groups.cmake +++ b/code/source_groups.cmake @@ -262,6 +262,8 @@ add_file_folder("ExceptionHandler" # Executor files add_file_folder("Executor" + executor/CombinedExecutionContext.cpp + executor/CombinedExecutionContext.h executor/Executor.cpp executor/Executor.h executor/GameStateExecutionContext.cpp @@ -1091,6 +1093,8 @@ add_file_folder("Scripting\\\\Api" scripting/api/LuaCoroutineRunner.h scripting/api/LuaEventCallback.cpp scripting/api/LuaEventCallback.h + scripting/api/LuaExecutionContext.cpp + scripting/api/LuaExecutionContext.h scripting/api/LuaPromise.cpp scripting/api/LuaPromise.h ) @@ -1157,6 +1161,8 @@ add_file_folder("Scripting\\\\Api\\\\Objs" scripting/api/objs/enums.h scripting/api/objs/event.cpp scripting/api/objs/event.h + scripting/api/objs/execution_context.cpp + scripting/api/objs/execution_context.h scripting/api/objs/executor.cpp scripting/api/objs/executor.h scripting/api/objs/eye.cpp