|
|
|
@ -34,18 +34,33 @@ distribution.
|
|
|
|
|
#include <lua.h>
|
|
|
|
|
#include <lauxlib.h>
|
|
|
|
|
|
|
|
|
|
namespace DFHack { namespace Lua {
|
|
|
|
|
namespace DFHack {
|
|
|
|
|
class function_identity_base;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace DFHack {namespace Lua {
|
|
|
|
|
/**
|
|
|
|
|
* Create or initialize a lua interpreter with access to DFHack tools.
|
|
|
|
|
*/
|
|
|
|
|
DFHACK_EXPORT lua_State *Open(color_ostream &out, lua_State *state = NULL);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Load a module using require().
|
|
|
|
|
* Load a module using require(). Leaves the stack as is.
|
|
|
|
|
*/
|
|
|
|
|
DFHACK_EXPORT bool Require(color_ostream &out, lua_State *state,
|
|
|
|
|
const std::string &module, bool setglobal = false);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Push the module table, loading it using require() if necessary.
|
|
|
|
|
*/
|
|
|
|
|
DFHACK_EXPORT bool PushModule(color_ostream &out, lua_State *state, const char *module);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Push the public object name exported by the module. Uses PushModule.
|
|
|
|
|
*/
|
|
|
|
|
DFHACK_EXPORT bool PushModulePublic(color_ostream &out, lua_State *state,
|
|
|
|
|
const char *module, const char *name);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Check if the object at the given index is NIL or NULL.
|
|
|
|
|
*/
|
|
|
|
@ -79,6 +94,12 @@ namespace DFHack { namespace Lua {
|
|
|
|
|
*/
|
|
|
|
|
DFHACK_EXPORT void *GetDFObject(lua_State *state, type_identity *type, int val_index, bool exact_type = false);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Check that the value is a wrapped DF object of the given type, and if so return the pointer.
|
|
|
|
|
* Otherwise throw an argument type error.
|
|
|
|
|
*/
|
|
|
|
|
DFHACK_EXPORT void *CheckDFObject(lua_State *state, type_identity *type, int val_index, bool exact_type = false);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Assign the value at val_index to the target of given identity using df.assign().
|
|
|
|
|
* Return behavior is of SafeCall below.
|
|
|
|
@ -102,6 +123,14 @@ namespace DFHack { namespace Lua {
|
|
|
|
|
return (T*)GetDFObject(state, df::identity_traits<T>::get(), val_index, exact_type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Check that the value is a wrapped DF object of the correct type, and if so return the pointer. Otherwise throw an argument type error.
|
|
|
|
|
*/
|
|
|
|
|
template<class T>
|
|
|
|
|
T *CheckDFObject(lua_State *state, int val_index, bool exact_type = false) {
|
|
|
|
|
return (T*)CheckDFObject(state, df::identity_traits<T>::get(), val_index, exact_type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Assign the value at val_index to the target using df.assign().
|
|
|
|
|
*/
|
|
|
|
@ -110,12 +139,56 @@ namespace DFHack { namespace Lua {
|
|
|
|
|
return AssignDFObject(out, state, df::identity_traits<T>::get(), target, val_index, perr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Check if the status is a success, i.e. LUA_OK or LUA_YIELD.
|
|
|
|
|
*/
|
|
|
|
|
inline bool IsSuccess(int status) {
|
|
|
|
|
return (status == LUA_OK || status == LUA_YIELD);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Internal helper
|
|
|
|
|
template<int (*cb)(lua_State*,int,int)>
|
|
|
|
|
int TailPCallK_Thunk(lua_State *state) {
|
|
|
|
|
int tmp;
|
|
|
|
|
int rv = lua_getctx(state, &tmp);
|
|
|
|
|
return cb(state, rv, tmp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* A utility for using the restartable pcall feature more conveniently;
|
|
|
|
|
* specifically, the callback is called with the same kind of arguments
|
|
|
|
|
* in both yield and non-yield case.
|
|
|
|
|
*/
|
|
|
|
|
template<int (*cb)(lua_State*,int,int)>
|
|
|
|
|
int TailPCallK(lua_State *state, int narg, int nret, int errfun, int ctx) {
|
|
|
|
|
int rv = lua_pcallk(state, narg, nret, errfun, ctx, &TailPCallK_Thunk<cb>);
|
|
|
|
|
return cb(state, rv, ctx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Invoke lua function via pcall. Returns true if success.
|
|
|
|
|
* If an error is signalled, and perr is true, it is printed and popped from the stack.
|
|
|
|
|
*/
|
|
|
|
|
DFHACK_EXPORT bool SafeCall(color_ostream &out, lua_State *state, int nargs, int nres, bool perr = true);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Pops a function from the top of the stack, and pushes a new coroutine.
|
|
|
|
|
*/
|
|
|
|
|
DFHACK_EXPORT lua_State *NewCoroutine(lua_State *state);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Resume the coroutine using nargs values from state from. Results or the error are moved back.
|
|
|
|
|
* If an error is signalled, and perr is true, it is printed and popped from the stack.
|
|
|
|
|
* Returns the lua_resume return value.
|
|
|
|
|
*/
|
|
|
|
|
DFHACK_EXPORT int SafeResume(color_ostream &out, lua_State *from, lua_State *thread, int nargs, int nres, bool perr = true);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Works just like SafeCall, only expects a coroutine on the stack
|
|
|
|
|
* instead of a function. Returns the lua_resume return value.
|
|
|
|
|
*/
|
|
|
|
|
DFHACK_EXPORT int SafeResume(color_ostream &out, lua_State *from, int nargs, int nres, bool perr = true);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Parse code from string with debug_tag and env_idx, then call it using SafeCall.
|
|
|
|
|
* In case of error, it is either left on the stack, or printed like SafeCall does.
|
|
|
|
@ -131,8 +204,168 @@ namespace DFHack { namespace Lua {
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Run an interactive interpreter loop if possible, or return false.
|
|
|
|
|
* Uses RunCoreQueryLoop internally.
|
|
|
|
|
*/
|
|
|
|
|
DFHACK_EXPORT bool InterpreterLoop(color_ostream &out, lua_State *state,
|
|
|
|
|
const char *prompt = NULL, int env = 0, const char *hfile = NULL);
|
|
|
|
|
const char *prompt = NULL, const char *hfile = NULL);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Run an interactive prompt loop. All access to the lua state
|
|
|
|
|
* is done inside CoreSuspender, while waiting for input happens
|
|
|
|
|
* without the suspend lock.
|
|
|
|
|
*/
|
|
|
|
|
DFHACK_EXPORT bool RunCoreQueryLoop(color_ostream &out, lua_State *state,
|
|
|
|
|
bool (*init)(color_ostream&, lua_State*, void*),
|
|
|
|
|
void *arg);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Push utility functions
|
|
|
|
|
*/
|
|
|
|
|
#if 0
|
|
|
|
|
#define NUMBER_PUSH(type) inline void Push(lua_State *state, type value) { lua_pushnumber(state, value); }
|
|
|
|
|
NUMBER_PUSH(char)
|
|
|
|
|
NUMBER_PUSH(int8_t) NUMBER_PUSH(uint8_t)
|
|
|
|
|
NUMBER_PUSH(int16_t) NUMBER_PUSH(uint16_t)
|
|
|
|
|
NUMBER_PUSH(int32_t) NUMBER_PUSH(uint32_t)
|
|
|
|
|
NUMBER_PUSH(int64_t) NUMBER_PUSH(uint64_t)
|
|
|
|
|
NUMBER_PUSH(float) NUMBER_PUSH(double)
|
|
|
|
|
#undef NUMBER_PUSH
|
|
|
|
|
#else
|
|
|
|
|
template<class T> inline void Push(lua_State *state, T value) {
|
|
|
|
|
lua_pushnumber(state, lua_Number(value));
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
inline void Push(lua_State *state, bool value) {
|
|
|
|
|
lua_pushboolean(state, value);
|
|
|
|
|
}
|
|
|
|
|
inline void Push(lua_State *state, const std::string &str) {
|
|
|
|
|
lua_pushlstring(state, str.data(), str.size());
|
|
|
|
|
}
|
|
|
|
|
inline void Push(lua_State *state, df::coord &obj) { PushDFObject(state, &obj); }
|
|
|
|
|
inline void Push(lua_State *state, df::coord2d &obj) { PushDFObject(state, &obj); }
|
|
|
|
|
template<class T> inline void Push(lua_State *state, T *ptr) {
|
|
|
|
|
PushDFObject(state, ptr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<class T>
|
|
|
|
|
void PushVector(lua_State *state, const T &pvec)
|
|
|
|
|
{
|
|
|
|
|
lua_createtable(state,pvec.size(),0);
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < pvec.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
Push(state, pvec[i]);
|
|
|
|
|
lua_rawseti(state, -2, i+1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DFHACK_EXPORT int PushPosXYZ(lua_State *state, df::coord pos);
|
|
|
|
|
|
|
|
|
|
DFHACK_EXPORT bool IsCoreContext(lua_State *state);
|
|
|
|
|
|
|
|
|
|
DFHACK_EXPORT int NewEvent(lua_State *state);
|
|
|
|
|
DFHACK_EXPORT void MakeEvent(lua_State *state, void *key);
|
|
|
|
|
DFHACK_EXPORT void InvokeEvent(color_ostream &out, lua_State *state, void *key, int num_args);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Namespace for the common lua interpreter state.
|
|
|
|
|
* All accesses must be done under CoreSuspender.
|
|
|
|
|
*/
|
|
|
|
|
namespace Core {
|
|
|
|
|
DFHACK_EXPORT extern lua_State *State;
|
|
|
|
|
|
|
|
|
|
// Not exported; for use by the Core class
|
|
|
|
|
void Init(color_ostream &out);
|
|
|
|
|
void Reset(color_ostream &out, const char *where);
|
|
|
|
|
|
|
|
|
|
// Events signalled by the core
|
|
|
|
|
void onStateChange(color_ostream &out, int code);
|
|
|
|
|
|
|
|
|
|
template<class T> inline void Push(T &arg) { Lua::Push(State, arg); }
|
|
|
|
|
template<class T> inline void Push(const T &arg) { Lua::Push(State, arg); }
|
|
|
|
|
template<class T> inline void PushVector(const T &arg) { Lua::PushVector(State, arg); }
|
|
|
|
|
|
|
|
|
|
inline bool SafeCall(color_ostream &out, int nargs, int nres, bool perr = true) {
|
|
|
|
|
return Lua::SafeCall(out, State, nargs, nres, perr);
|
|
|
|
|
}
|
|
|
|
|
inline bool PushModule(color_ostream &out, const char *module) {
|
|
|
|
|
return Lua::PushModule(out, State, module);
|
|
|
|
|
}
|
|
|
|
|
inline bool PushModulePublic(color_ostream &out, const char *module, const char *name) {
|
|
|
|
|
return Lua::PushModulePublic(out, State, module, name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class DFHACK_EXPORT Notification {
|
|
|
|
|
lua_State *state;
|
|
|
|
|
void *key;
|
|
|
|
|
function_identity_base *handler;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
Notification(function_identity_base *handler = NULL)
|
|
|
|
|
: state(NULL), key(NULL), handler(handler) {}
|
|
|
|
|
|
|
|
|
|
lua_State *get_state() { return state; }
|
|
|
|
|
function_identity_base *get_handler() { return handler; }
|
|
|
|
|
|
|
|
|
|
void invoke(color_ostream &out, int nargs);
|
|
|
|
|
|
|
|
|
|
void bind(lua_State *state, const char *name);
|
|
|
|
|
void bind(lua_State *state, void *key);
|
|
|
|
|
};
|
|
|
|
|
}}
|
|
|
|
|
|
|
|
|
|
#define DEFINE_LUA_EVENT_0(name, handler) \
|
|
|
|
|
static DFHack::Lua::Notification name##_event(df::wrap_function(handler, true)); \
|
|
|
|
|
void name(color_ostream &out) { \
|
|
|
|
|
handler(out); \
|
|
|
|
|
if (name##_event.get_state()) { \
|
|
|
|
|
name##_event.invoke(out, 0); \
|
|
|
|
|
} \
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define DEFINE_LUA_EVENT_1(name, handler, arg_type1) \
|
|
|
|
|
static DFHack::Lua::Notification name##_event(df::wrap_function(handler, true)); \
|
|
|
|
|
void name(color_ostream &out, arg_type1 arg1) { \
|
|
|
|
|
handler(out, arg1); \
|
|
|
|
|
if (auto state = name##_event.get_state()) { \
|
|
|
|
|
DFHack::Lua::Push(state, arg1); \
|
|
|
|
|
name##_event.invoke(out, 1); \
|
|
|
|
|
} \
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define DEFINE_LUA_EVENT_2(name, handler, arg_type1, arg_type2) \
|
|
|
|
|
static DFHack::Lua::Notification name##_event(df::wrap_function(handler, true)); \
|
|
|
|
|
void name(color_ostream &out, arg_type1 arg1, arg_type2 arg2) { \
|
|
|
|
|
handler(out, arg1, arg2); \
|
|
|
|
|
if (auto state = name##_event.get_state()) { \
|
|
|
|
|
DFHack::Lua::Push(state, arg1); \
|
|
|
|
|
DFHack::Lua::Push(state, arg2); \
|
|
|
|
|
name##_event.invoke(out, 2); \
|
|
|
|
|
} \
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define DEFINE_LUA_EVENT_3(name, handler, arg_type1, arg_type2, arg_type3) \
|
|
|
|
|
static DFHack::Lua::Notification name##_event(df::wrap_function(handler, true)); \
|
|
|
|
|
void name(color_ostream &out, arg_type1 arg1, arg_type2 arg2, arg_type3 arg3) { \
|
|
|
|
|
handler(out, arg1, arg2, arg3); \
|
|
|
|
|
if (auto state = name##_event.get_state()) { \
|
|
|
|
|
DFHack::Lua::Push(state, arg1); \
|
|
|
|
|
DFHack::Lua::Push(state, arg2); \
|
|
|
|
|
DFHack::Lua::Push(state, arg3); \
|
|
|
|
|
name##_event.invoke(out, 3); \
|
|
|
|
|
} \
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define DEFINE_LUA_EVENT_4(name, handler, arg_type1, arg_type2, arg_type3, arg_type4) \
|
|
|
|
|
static DFHack::Lua::Notification name##_event(df::wrap_function(handler, true)); \
|
|
|
|
|
void name(color_ostream &out, arg_type1 arg1, arg_type2 arg2, arg_type3 arg3, arg_type4 arg4) { \
|
|
|
|
|
handler(out, arg1, arg2, arg3, arg4); \
|
|
|
|
|
if (auto state = name##_event.get_state()) { \
|
|
|
|
|
DFHack::Lua::Push(state, arg1); \
|
|
|
|
|
DFHack::Lua::Push(state, arg2); \
|
|
|
|
|
DFHack::Lua::Push(state, arg3); \
|
|
|
|
|
DFHack::Lua::Push(state, arg4); \
|
|
|
|
|
name##_event.invoke(out, 4); \
|
|
|
|
|
} \
|
|
|
|
|
}
|
|
|
|
|