2012-03-31 05:40:54 -06:00
|
|
|
/*
|
|
|
|
https://github.com/peterix/dfhack
|
2012-09-29 20:03:37 -06:00
|
|
|
Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
|
2012-03-31 05:40:54 -06:00
|
|
|
|
|
|
|
This software is provided 'as-is', without any express or implied
|
|
|
|
warranty. In no event will the authors be held liable for any
|
|
|
|
damages arising from the use of this software.
|
|
|
|
|
|
|
|
Permission is granted to anyone to use this software for any
|
|
|
|
purpose, including commercial applications, and to alter it and
|
|
|
|
redistribute it freely, subject to the following restrictions:
|
|
|
|
|
|
|
|
1. The origin of this software must not be misrepresented; you must
|
|
|
|
not claim that you wrote the original software. If you use this
|
|
|
|
software in a product, an acknowledgment in the product documentation
|
|
|
|
would be appreciated but is not required.
|
|
|
|
|
|
|
|
2. Altered source versions must be plainly marked as such, and
|
|
|
|
must not be misrepresented as being the original software.
|
|
|
|
|
|
|
|
3. This notice may not be removed or altered from any source
|
|
|
|
distribution.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
2022-11-02 14:50:52 -06:00
|
|
|
#include <functional>
|
2012-03-31 05:40:54 -06:00
|
|
|
#include <string>
|
|
|
|
#include <sstream>
|
|
|
|
#include <vector>
|
|
|
|
#include <map>
|
2016-07-28 20:04:46 -06:00
|
|
|
#include <type_traits>
|
2023-01-09 18:31:49 -07:00
|
|
|
#include <unordered_map>
|
2023-03-15 01:28:18 -06:00
|
|
|
#include <unordered_set>
|
2012-03-31 05:40:54 -06:00
|
|
|
|
2022-10-31 12:43:37 -06:00
|
|
|
#include "df/interfacest.h"
|
|
|
|
|
2018-07-07 04:27:29 -06:00
|
|
|
#include "ColorText.h"
|
2012-03-31 05:40:54 -06:00
|
|
|
#include "DataDefs.h"
|
|
|
|
|
|
|
|
#include <lua.h>
|
|
|
|
#include <lauxlib.h>
|
|
|
|
|
2013-10-01 08:58:04 -06:00
|
|
|
/// Allocate a new user data object and push it on the stack
|
|
|
|
inline void *operator new (std::size_t size, lua_State *L) {
|
|
|
|
return lua_newuserdata(L, size);
|
|
|
|
}
|
|
|
|
|
2012-04-15 09:09:25 -06:00
|
|
|
namespace DFHack {
|
|
|
|
class function_identity_base;
|
2012-10-23 11:42:03 -06:00
|
|
|
struct MaterialInfo;
|
2012-04-26 08:51:39 -06:00
|
|
|
|
|
|
|
namespace Units {
|
|
|
|
struct NoblePosition;
|
|
|
|
}
|
2012-11-03 10:06:33 -06:00
|
|
|
namespace Screen {
|
|
|
|
struct Pen;
|
|
|
|
};
|
2012-04-15 09:09:25 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
namespace DFHack {namespace Lua {
|
2012-03-31 05:40:54 -06:00
|
|
|
/**
|
|
|
|
* Create or initialize a lua interpreter with access to DFHack tools.
|
|
|
|
*/
|
|
|
|
DFHACK_EXPORT lua_State *Open(color_ostream &out, lua_State *state = NULL);
|
|
|
|
|
2012-05-04 09:47:18 -06:00
|
|
|
DFHACK_EXPORT void PushDFHack(lua_State *state);
|
|
|
|
DFHACK_EXPORT void PushBaseGlobals(lua_State *state);
|
|
|
|
|
2012-03-31 05:40:54 -06:00
|
|
|
/**
|
2012-04-15 09:09:25 -06:00
|
|
|
* Load a module using require(). Leaves the stack as is.
|
2012-03-31 05:40:54 -06:00
|
|
|
*/
|
|
|
|
DFHACK_EXPORT bool Require(color_ostream &out, lua_State *state,
|
|
|
|
const std::string &module, bool setglobal = false);
|
|
|
|
|
2012-04-15 09:09:25 -06:00
|
|
|
/**
|
|
|
|
* 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);
|
|
|
|
|
2012-04-02 09:10:57 -06:00
|
|
|
/**
|
|
|
|
* Check if the object at the given index is NIL or NULL.
|
|
|
|
*/
|
|
|
|
DFHACK_EXPORT bool IsDFNull(lua_State *state, int val_index);
|
|
|
|
|
|
|
|
enum ObjectClass {
|
|
|
|
/** Not a DF wrapper object */
|
|
|
|
OBJ_INVALID = 0,
|
|
|
|
/** NIL or NULL */
|
|
|
|
OBJ_NULL,
|
|
|
|
/** A named type identity object */
|
|
|
|
OBJ_TYPE,
|
|
|
|
/** A void* reference, i.e. non-null lightuserdata */
|
|
|
|
OBJ_VOIDPTR,
|
|
|
|
/** A typed object reference */
|
|
|
|
OBJ_REF
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if the object at the given index is a valid wrapper object.
|
|
|
|
*/
|
|
|
|
DFHACK_EXPORT ObjectClass IsDFObject(lua_State *state, int val_index);
|
|
|
|
|
2012-03-31 05:40:54 -06:00
|
|
|
/**
|
|
|
|
* Push the pointer onto the stack as a wrapped DF object of the given type.
|
|
|
|
*/
|
|
|
|
DFHACK_EXPORT void PushDFObject(lua_State *state, type_identity *type, void *ptr);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check that the value is a wrapped DF object of the given type, and if so return the pointer.
|
|
|
|
*/
|
|
|
|
DFHACK_EXPORT void *GetDFObject(lua_State *state, type_identity *type, int val_index, bool exact_type = false);
|
|
|
|
|
2012-04-15 09:09:25 -06:00
|
|
|
/**
|
|
|
|
* 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);
|
|
|
|
|
2012-04-03 10:02:01 -06:00
|
|
|
/**
|
|
|
|
* Assign the value at val_index to the target of given identity using df.assign().
|
|
|
|
* Return behavior is of SafeCall below.
|
|
|
|
*/
|
|
|
|
DFHACK_EXPORT bool AssignDFObject(color_ostream &out, lua_State *state,
|
2012-05-02 02:50:05 -06:00
|
|
|
type_identity *type, void *target, int val_index,
|
|
|
|
bool exact_type = false, bool perr = true);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Assign the value at val_index to the target of given identity using df.assign().
|
|
|
|
* Otherwise throws an error.
|
|
|
|
*/
|
2012-05-05 10:46:28 -06:00
|
|
|
DFHACK_EXPORT void CheckDFAssign(lua_State *state, type_identity *type,
|
2012-05-02 02:50:05 -06:00
|
|
|
void *target, int val_index, bool exact_type = false);
|
2012-04-03 10:02:01 -06:00
|
|
|
|
2012-03-31 05:40:54 -06:00
|
|
|
/**
|
|
|
|
* Push the pointer onto the stack as a wrapped DF object of a specific type.
|
|
|
|
*/
|
|
|
|
template<class T>
|
|
|
|
void PushDFObject(lua_State *state, T *ptr) {
|
|
|
|
PushDFObject(state, df::identity_traits<T>::get(), ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check that the value is a wrapped DF object of the correct type, and if so return the pointer.
|
|
|
|
*/
|
|
|
|
template<class T>
|
|
|
|
T *GetDFObject(lua_State *state, int val_index, bool exact_type = false) {
|
|
|
|
return (T*)GetDFObject(state, df::identity_traits<T>::get(), val_index, exact_type);
|
|
|
|
}
|
|
|
|
|
2012-04-15 09:09:25 -06:00
|
|
|
/**
|
|
|
|
* 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);
|
|
|
|
}
|
|
|
|
|
2012-04-03 10:02:01 -06:00
|
|
|
/**
|
|
|
|
* Assign the value at val_index to the target using df.assign().
|
|
|
|
*/
|
|
|
|
template<class T>
|
2012-05-02 02:50:05 -06:00
|
|
|
bool AssignDFObject(color_ostream &out, lua_State *state, T *target,
|
|
|
|
int val_index, bool exact_type = false, bool perr = true) {
|
|
|
|
return AssignDFObject(out, state, df::identity_traits<T>::get(),
|
|
|
|
target, val_index, exact_type, perr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Assign the value at val_index to the target using df.assign().
|
|
|
|
* Throws in case of an error.
|
|
|
|
*/
|
|
|
|
template<class T>
|
2012-05-05 10:46:28 -06:00
|
|
|
void CheckDFAssign(lua_State *state, T *target, int val_index, bool exact_type = false) {
|
|
|
|
CheckDFAssign(state, df::identity_traits<T>::get(), target, val_index, exact_type);
|
2012-04-03 10:02:01 -06:00
|
|
|
}
|
|
|
|
|
2012-04-16 08:01:21 -06:00
|
|
|
/**
|
|
|
|
* 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
|
2016-07-26 21:47:53 -06:00
|
|
|
template<int (*cb)(lua_State*,int,lua_KContext)>
|
|
|
|
int TailPCallK_Thunk(lua_State *state, int rv, lua_KContext ctx) {
|
|
|
|
return cb(state, rv, ctx);
|
2012-04-16 08:01:21 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2016-07-26 21:47:53 -06:00
|
|
|
template<int (*cb)(lua_State*,int,lua_KContext)>
|
2012-04-16 08:01:21 -06:00
|
|
|
int TailPCallK(lua_State *state, int narg, int nret, int errfun, int ctx) {
|
2016-07-26 21:47:53 -06:00
|
|
|
int rv = lua_pcallk(state, narg, nret, errfun, ctx, cb);
|
2012-04-16 08:01:21 -06:00
|
|
|
return cb(state, rv, ctx);
|
|
|
|
}
|
|
|
|
|
2012-08-19 04:27:44 -06:00
|
|
|
/**
|
|
|
|
* Call through to the function with try/catch for C++ exceptions.
|
|
|
|
*/
|
|
|
|
DFHACK_EXPORT int CallWithCatch(lua_State *, int (*fn)(lua_State*), const char *context = NULL);
|
|
|
|
|
|
|
|
template<int (*cb)(lua_State*)>
|
|
|
|
int CallWithCatchWrapper(lua_State *state) {
|
|
|
|
return CallWithCatch(state, cb);
|
|
|
|
}
|
|
|
|
|
2012-03-31 05:40:54 -06:00
|
|
|
/**
|
|
|
|
* 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);
|
|
|
|
|
2022-11-02 14:50:52 -06:00
|
|
|
/**
|
|
|
|
* Load named module and function and invoke it via SafeCall. Returns true
|
|
|
|
* on success. If an error is signalled, and perr is true, it is printed and
|
|
|
|
* popped from the stack.
|
|
|
|
*/
|
|
|
|
typedef std::function<void(lua_State *)> LuaLambda;
|
|
|
|
static auto DEFAULT_LUA_LAMBDA = [](lua_State *){};
|
|
|
|
DFHACK_EXPORT bool CallLuaModuleFunction(color_ostream &out,
|
|
|
|
lua_State *state, const char *module_name, const char *fn_name,
|
|
|
|
int nargs = 0, int nres = 0,
|
|
|
|
LuaLambda && args_lambda = DEFAULT_LUA_LAMBDA,
|
|
|
|
LuaLambda && res_lambda = DEFAULT_LUA_LAMBDA,
|
|
|
|
bool perr = true);
|
|
|
|
|
2012-04-16 04:45:04 -06:00
|
|
|
/**
|
|
|
|
* 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);
|
|
|
|
|
2012-04-03 10:02:01 -06:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
DFHACK_EXPORT bool SafeCallString(color_ostream &out, lua_State *state, const std::string &code,
|
|
|
|
int nargs, int nres, bool perr = true,
|
|
|
|
const char *debug_tag = NULL, int env_idx = 0);
|
|
|
|
|
2012-03-31 05:40:54 -06:00
|
|
|
/**
|
|
|
|
* Returns the ostream passed to SafeCall.
|
|
|
|
*/
|
|
|
|
DFHACK_EXPORT color_ostream *GetOutput(lua_State *state);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Run an interactive interpreter loop if possible, or return false.
|
2012-04-15 11:50:22 -06:00
|
|
|
* Uses RunCoreQueryLoop internally.
|
2012-03-31 05:40:54 -06:00
|
|
|
*/
|
|
|
|
DFHACK_EXPORT bool InterpreterLoop(color_ostream &out, lua_State *state,
|
2012-04-15 11:50:22 -06:00
|
|
|
const char *prompt = NULL, const char *hfile = NULL);
|
|
|
|
|
|
|
|
/**
|
2012-04-16 04:45:04 -06:00
|
|
|
* Run an interactive prompt loop. All access to the lua state
|
|
|
|
* is done inside CoreSuspender, while waiting for input happens
|
|
|
|
* without the suspend lock.
|
2012-04-15 11:50:22 -06:00
|
|
|
*/
|
|
|
|
DFHACK_EXPORT bool RunCoreQueryLoop(color_ostream &out, lua_State *state,
|
2012-04-16 04:45:04 -06:00
|
|
|
bool (*init)(color_ostream&, lua_State*, void*),
|
2012-04-15 11:50:22 -06:00
|
|
|
void *arg);
|
2012-04-15 09:09:25 -06:00
|
|
|
|
2015-05-24 17:06:01 -06:00
|
|
|
/**
|
|
|
|
* Attempt to interrupt the currently-executing lua function by raising a lua error
|
|
|
|
* from a lua debug hook, similar to how SIGINT is handled in the lua interpreter (depends/lua/src/lua.c).
|
|
|
|
* The flag set here will only be checked every 256 instructions by default.
|
|
|
|
* Returns false if another debug hook is set and 'force' is false.
|
|
|
|
*
|
|
|
|
* force: Overwrite any existing debug hooks and interrupt the next instruction
|
|
|
|
*/
|
|
|
|
|
|
|
|
DFHACK_EXPORT bool Interrupt (bool force=false);
|
|
|
|
|
2012-04-15 09:09:25 -06:00
|
|
|
/**
|
|
|
|
* Push utility functions
|
|
|
|
*/
|
2012-04-16 00:59:55 -06:00
|
|
|
#if 0
|
2012-04-15 09:09:25 -06:00
|
|
|
#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
|
2012-04-16 00:59:55 -06:00
|
|
|
#else
|
2016-07-28 20:04:46 -06:00
|
|
|
template<class T>
|
2016-07-29 09:06:16 -06:00
|
|
|
inline typename std::enable_if<std::is_integral<T>::value || std::is_enum<T>::value>::type
|
2016-07-28 20:04:46 -06:00
|
|
|
Push(lua_State *state, T value) {
|
|
|
|
lua_pushinteger(state, value);
|
2016-07-28 14:36:02 -06:00
|
|
|
}
|
2016-07-28 20:04:46 -06:00
|
|
|
template<class T>
|
|
|
|
inline typename std::enable_if<std::is_floating_point<T>::value>::type
|
|
|
|
Push(lua_State *state, T value) {
|
2012-04-16 00:59:55 -06:00
|
|
|
lua_pushnumber(state, lua_Number(value));
|
|
|
|
}
|
|
|
|
#endif
|
2012-04-15 09:09:25 -06:00
|
|
|
inline void Push(lua_State *state, bool value) {
|
|
|
|
lua_pushboolean(state, value);
|
|
|
|
}
|
2012-05-13 03:58:41 -06:00
|
|
|
inline void Push(lua_State *state, const char *str) {
|
|
|
|
lua_pushstring(state, str);
|
|
|
|
}
|
2012-04-15 09:09:25 -06:00
|
|
|
inline void Push(lua_State *state, const std::string &str) {
|
|
|
|
lua_pushlstring(state, str.data(), str.size());
|
|
|
|
}
|
2023-02-08 13:15:19 -07:00
|
|
|
DFHACK_EXPORT void Push(lua_State *state, const df::coord &obj);
|
|
|
|
DFHACK_EXPORT void Push(lua_State *state, const df::coord2d &obj);
|
2012-04-26 08:51:39 -06:00
|
|
|
void Push(lua_State *state, const Units::NoblePosition &pos);
|
2023-02-08 13:15:19 -07:00
|
|
|
DFHACK_EXPORT void Push(lua_State *state, const MaterialInfo &info);
|
2012-11-03 10:06:33 -06:00
|
|
|
DFHACK_EXPORT void Push(lua_State *state, const Screen::Pen &info);
|
2012-04-15 09:09:25 -06:00
|
|
|
template<class T> inline void Push(lua_State *state, T *ptr) {
|
|
|
|
PushDFObject(state, ptr);
|
|
|
|
}
|
|
|
|
|
2012-09-11 09:17:24 -06:00
|
|
|
template<class T> inline void SetField(lua_State *L, T val, int idx, const char *name) {
|
|
|
|
if (idx < 0) idx = lua_absindex(L, idx);
|
|
|
|
Push(L, val); lua_setfield(L, idx, name);
|
|
|
|
}
|
|
|
|
|
2022-10-31 12:43:37 -06:00
|
|
|
DFHACK_EXPORT void PushInterfaceKeys(lua_State *L, const std::set<df::interface_key> &keys);
|
|
|
|
|
2023-02-08 13:15:19 -07:00
|
|
|
DFHACK_EXPORT int PushPosXYZ(lua_State *state, const df::coord &pos);
|
|
|
|
DFHACK_EXPORT int PushPosXY(lua_State *state, const df::coord2d &pos);
|
2016-10-15 12:55:48 -06:00
|
|
|
|
2023-03-15 01:28:18 -06:00
|
|
|
template<typename T>
|
|
|
|
void Push(lua_State *L, const std::set<T> &pset) {
|
|
|
|
lua_createtable(L, 0, pset.size());
|
|
|
|
for (auto &entry : pset) {
|
|
|
|
Lua::Push(L, entry);
|
|
|
|
Lua::Push(L, true);
|
|
|
|
lua_settable(L, -3);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T_Key, typename T_Hash>
|
|
|
|
void Push(lua_State *L, const std::unordered_set<T_Key, T_Hash> &pset) {
|
|
|
|
lua_createtable(L, 0, pset.size());
|
|
|
|
for (auto &entry : pset) {
|
|
|
|
Lua::Push(L, entry);
|
|
|
|
Lua::Push(L, true);
|
|
|
|
lua_settable(L, -3);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-04 18:40:50 -06:00
|
|
|
template<typename T_Key, typename T_Value>
|
|
|
|
void Push(lua_State *L, const std::map<T_Key, T_Value> &pmap) {
|
|
|
|
lua_createtable(L, 0, pmap.size());
|
2023-02-08 13:15:19 -07:00
|
|
|
for (auto &entry : pmap) {
|
|
|
|
Lua::Push(L, entry.first);
|
|
|
|
Lua::Push(L, entry.second);
|
|
|
|
lua_settable(L, -3);
|
|
|
|
}
|
2023-01-09 18:31:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T_Key, typename T_Value>
|
|
|
|
void Push(lua_State *L, const std::unordered_map<T_Key, T_Value> &pmap) {
|
|
|
|
lua_createtable(L, 0, pmap.size());
|
2023-02-08 13:15:19 -07:00
|
|
|
for (auto &entry : pmap) {
|
|
|
|
Lua::Push(L, entry.first);
|
|
|
|
Lua::Push(L, entry.second);
|
|
|
|
lua_settable(L, -3);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T_Key, typename T_Value>
|
|
|
|
inline void TableInsert(lua_State *state, const T_Key &key, const T_Value &value) {
|
|
|
|
Lua::Push(state, key);
|
|
|
|
Lua::Push(state, value);
|
|
|
|
lua_settable(state, -3);
|
2022-11-04 18:40:50 -06:00
|
|
|
}
|
|
|
|
|
2023-04-24 15:28:21 -06:00
|
|
|
template<class T>
|
|
|
|
void PushVector(lua_State *state, const T &pvec, bool addn = false)
|
|
|
|
{
|
|
|
|
lua_createtable(state,pvec.size(), addn?1:0);
|
|
|
|
|
|
|
|
if (addn)
|
|
|
|
{
|
|
|
|
lua_pushinteger(state, pvec.size());
|
|
|
|
lua_setfield(state, -2, "n");
|
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t i = 0; i < pvec.size(); i++)
|
|
|
|
{
|
|
|
|
Push(state, pvec[i]);
|
|
|
|
lua_rawseti(state, -2, i+1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DFHACK_EXPORT void GetVector(lua_State *state, std::vector<std::string> &pvec, int idx = 1);
|
|
|
|
|
2012-11-03 10:06:33 -06:00
|
|
|
DFHACK_EXPORT void CheckPen(lua_State *L, Screen::Pen *pen, int index, bool allow_nil = false, bool allow_color = true);
|
|
|
|
|
2012-04-15 09:09:25 -06:00
|
|
|
DFHACK_EXPORT bool IsCoreContext(lua_State *state);
|
|
|
|
|
2012-08-23 09:27:12 -06:00
|
|
|
namespace Event {
|
|
|
|
struct DFHACK_EXPORT Owner {
|
|
|
|
virtual ~Owner() {}
|
|
|
|
virtual void on_count_changed(int new_cnt, int delta) {}
|
|
|
|
virtual void on_invoked(lua_State *state, int nargs, bool from_c) {}
|
|
|
|
};
|
|
|
|
|
|
|
|
DFHACK_EXPORT void New(lua_State *state, Owner *owner = NULL);
|
|
|
|
DFHACK_EXPORT void Make(lua_State *state, void *key, Owner *owner = NULL);
|
|
|
|
DFHACK_EXPORT void SetPrivateCallback(lua_State *state, int ev_idx);
|
|
|
|
DFHACK_EXPORT void Invoke(color_ostream &out, lua_State *state, void *key, int num_args);
|
|
|
|
}
|
2012-04-15 09:09:25 -06:00
|
|
|
|
2012-04-22 09:22:00 -06:00
|
|
|
class StackUnwinder {
|
|
|
|
lua_State *state;
|
|
|
|
int top;
|
|
|
|
public:
|
|
|
|
StackUnwinder(lua_State *state, int bias = 0) : state(state), top(0) {
|
|
|
|
if (state) top = lua_gettop(state) - bias;
|
|
|
|
}
|
|
|
|
~StackUnwinder() {
|
|
|
|
if (state) lua_settop(state, top);
|
|
|
|
}
|
|
|
|
operator int () { return top; }
|
|
|
|
int operator+ (int v) { return top + v; }
|
|
|
|
int operator- (int v) { return top + v; }
|
|
|
|
int operator[] (int v) { return top + v; }
|
|
|
|
StackUnwinder &operator += (int v) { top += v; return *this; }
|
|
|
|
StackUnwinder &operator -= (int v) { top += v; return *this; }
|
|
|
|
StackUnwinder &operator ++ () { top++; return *this; }
|
|
|
|
StackUnwinder &operator -- () { top--; return *this; }
|
|
|
|
};
|
|
|
|
|
2012-04-15 09:09:25 -06:00
|
|
|
/**
|
|
|
|
* 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
|
2015-10-17 19:18:04 -06:00
|
|
|
bool Init(color_ostream &out);
|
2018-06-21 09:58:16 -06:00
|
|
|
DFHACK_EXPORT void Reset(color_ostream &out, const char *where);
|
2012-04-15 09:09:25 -06:00
|
|
|
|
2012-04-17 01:45:09 -06:00
|
|
|
// Events signalled by the core
|
|
|
|
void onStateChange(color_ostream &out, int code);
|
2012-05-04 10:59:06 -06:00
|
|
|
// Signals timers
|
|
|
|
void onUpdate(color_ostream &out);
|
2012-04-17 01:45:09 -06:00
|
|
|
|
2012-04-15 09:09:25 -06:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-23 09:27:12 -06:00
|
|
|
class DFHACK_EXPORT Notification : public Event::Owner {
|
2012-04-15 09:09:25 -06:00
|
|
|
lua_State *state;
|
|
|
|
void *key;
|
|
|
|
function_identity_base *handler;
|
2012-08-23 09:27:12 -06:00
|
|
|
int count;
|
2012-04-15 09:09:25 -06:00
|
|
|
|
|
|
|
public:
|
|
|
|
Notification(function_identity_base *handler = NULL)
|
2012-08-23 09:27:12 -06:00
|
|
|
: state(NULL), key(NULL), handler(handler), count(0) {}
|
2012-04-15 09:09:25 -06:00
|
|
|
|
2012-08-23 09:27:12 -06:00
|
|
|
int get_listener_count() { return count; }
|
2012-04-15 09:09:25 -06:00
|
|
|
lua_State *get_state() { return state; }
|
|
|
|
function_identity_base *get_handler() { return handler; }
|
|
|
|
|
2012-08-23 09:27:12 -06:00
|
|
|
lua_State *state_if_count() { return (count > 0) ? state : NULL; }
|
|
|
|
|
|
|
|
void on_count_changed(int new_cnt, int) { count = new_cnt; }
|
|
|
|
|
2012-04-15 09:09:25 -06:00
|
|
|
void invoke(color_ostream &out, int nargs);
|
|
|
|
|
|
|
|
void bind(lua_State *state, const char *name);
|
|
|
|
void bind(lua_State *state, void *key);
|
|
|
|
};
|
2012-03-31 05:40:54 -06:00
|
|
|
}}
|
|
|
|
|
2012-04-15 09:09:25 -06:00
|
|
|
#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); \
|
2012-08-23 09:27:12 -06:00
|
|
|
if (name##_event.state_if_count()) { \
|
2012-04-15 09:09:25 -06:00
|
|
|
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); \
|
2012-08-23 09:27:12 -06:00
|
|
|
if (auto state = name##_event.state_if_count()) { \
|
2012-04-15 09:09:25 -06:00
|
|
|
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); \
|
2012-08-23 09:27:12 -06:00
|
|
|
if (auto state = name##_event.state_if_count()) { \
|
2012-04-15 09:09:25 -06:00
|
|
|
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); \
|
2012-08-23 09:27:12 -06:00
|
|
|
if (auto state = name##_event.state_if_count()) { \
|
2012-04-15 09:09:25 -06:00
|
|
|
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); \
|
2012-08-23 09:27:12 -06:00
|
|
|
if (auto state = name##_event.state_if_count()) { \
|
2012-04-15 09:09:25 -06:00
|
|
|
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); \
|
|
|
|
} \
|
|
|
|
}
|
2012-05-12 10:54:26 -06:00
|
|
|
|
|
|
|
#define DEFINE_LUA_EVENT_5(name, handler, arg_type1, arg_type2, arg_type3, arg_type4, arg_type5) \
|
|
|
|
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, arg_type5 arg5) { \
|
|
|
|
handler(out, arg1, arg2, arg3, arg4, arg5); \
|
2012-08-23 09:27:12 -06:00
|
|
|
if (auto state = name##_event.state_if_count()) { \
|
2012-05-12 10:54:26 -06:00
|
|
|
DFHack::Lua::Push(state, arg1); \
|
|
|
|
DFHack::Lua::Push(state, arg2); \
|
|
|
|
DFHack::Lua::Push(state, arg3); \
|
|
|
|
DFHack::Lua::Push(state, arg4); \
|
|
|
|
DFHack::Lua::Push(state, arg5); \
|
|
|
|
name##_event.invoke(out, 5); \
|
|
|
|
} \
|
|
|
|
}
|
2012-10-07 11:44:18 -06:00
|
|
|
|
|
|
|
#define DEFINE_LUA_EVENT_6(name, handler, arg_type1, arg_type2, arg_type3, arg_type4, arg_type5,arg_type6) \
|
|
|
|
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,arg_type5 arg5, arg_type6 arg6) { \
|
|
|
|
handler(out, arg1, arg2, arg3, arg4, arg5, arg6); \
|
|
|
|
if (auto state = name##_event.state_if_count()) { \
|
|
|
|
DFHack::Lua::Push(state, arg1); \
|
|
|
|
DFHack::Lua::Push(state, arg2); \
|
|
|
|
DFHack::Lua::Push(state, arg3); \
|
|
|
|
DFHack::Lua::Push(state, arg4); \
|
|
|
|
DFHack::Lua::Push(state, arg5); \
|
|
|
|
DFHack::Lua::Push(state, arg6); \
|
|
|
|
name##_event.invoke(out, 6); \
|
|
|
|
} \
|
2012-11-02 12:29:27 -06:00
|
|
|
}
|
2015-02-02 00:25:42 -07:00
|
|
|
|
|
|
|
#define DEFINE_LUA_EVENT_7(name, handler, arg_type1, arg_type2, arg_type3, arg_type4, arg_type5,arg_type6,arg_type7) \
|
|
|
|
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,arg_type5 arg5, arg_type6 arg6, arg_type7 arg7) { \
|
|
|
|
handler(out, arg1, arg2, arg3, arg4, arg5, arg6, arg7); \
|
|
|
|
if (auto state = name##_event.state_if_count()) { \
|
|
|
|
DFHack::Lua::Push(state, arg1); \
|
|
|
|
DFHack::Lua::Push(state, arg2); \
|
|
|
|
DFHack::Lua::Push(state, arg3); \
|
|
|
|
DFHack::Lua::Push(state, arg4); \
|
|
|
|
DFHack::Lua::Push(state, arg5); \
|
|
|
|
DFHack::Lua::Push(state, arg6); \
|
|
|
|
DFHack::Lua::Push(state, arg7); \
|
|
|
|
name##_event.invoke(out, 7); \
|
|
|
|
} \
|
|
|
|
}
|
2015-12-19 16:13:39 -07:00
|
|
|
|
|
|
|
//No handler versions useful for vmethod events, when we already have a place to put code at triggering
|
|
|
|
#define DEFINE_LUA_EVENT_NH_0(name) \
|
|
|
|
static DFHack::Lua::Notification name##_event; \
|
|
|
|
void name(color_ostream &out) { \
|
|
|
|
if (name##_event.state_if_count()) { \
|
|
|
|
name##_event.invoke(out, 0); \
|
|
|
|
} \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define DEFINE_LUA_EVENT_NH_1(name, arg_type1) \
|
|
|
|
static DFHack::Lua::Notification name##_event; \
|
|
|
|
void name(color_ostream &out, arg_type1 arg1) { \
|
|
|
|
if (auto state = name##_event.state_if_count()) { \
|
|
|
|
DFHack::Lua::Push(state, arg1); \
|
|
|
|
name##_event.invoke(out, 1); \
|
|
|
|
} \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define DEFINE_LUA_EVENT_NH_2(name, arg_type1, arg_type2) \
|
|
|
|
static DFHack::Lua::Notification name##_event; \
|
|
|
|
void name(color_ostream &out, arg_type1 arg1, arg_type2 arg2) { \
|
|
|
|
if (auto state = name##_event.state_if_count()) { \
|
|
|
|
DFHack::Lua::Push(state, arg1); \
|
|
|
|
DFHack::Lua::Push(state, arg2); \
|
|
|
|
name##_event.invoke(out, 2); \
|
|
|
|
} \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define DEFINE_LUA_EVENT_NH_3(name, arg_type1, arg_type2, arg_type3) \
|
|
|
|
static DFHack::Lua::Notification name##_event; \
|
|
|
|
void name(color_ostream &out, arg_type1 arg1, arg_type2 arg2, arg_type3 arg3) { \
|
|
|
|
if (auto state = name##_event.state_if_count()) { \
|
|
|
|
DFHack::Lua::Push(state, arg1); \
|
|
|
|
DFHack::Lua::Push(state, arg2); \
|
|
|
|
DFHack::Lua::Push(state, arg3); \
|
|
|
|
name##_event.invoke(out, 3); \
|
|
|
|
} \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define DEFINE_LUA_EVENT_NH_4(name, arg_type1, arg_type2, arg_type3, arg_type4) \
|
|
|
|
static DFHack::Lua::Notification name##_event; \
|
|
|
|
void name(color_ostream &out, arg_type1 arg1, arg_type2 arg2, arg_type3 arg3, arg_type4 arg4) { \
|
|
|
|
if (auto state = name##_event.state_if_count()) { \
|
|
|
|
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); \
|
|
|
|
} \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define DEFINE_LUA_EVENT_NH_5(name, arg_type1, arg_type2, arg_type3, arg_type4, arg_type5) \
|
|
|
|
static DFHack::Lua::Notification name##_event; \
|
|
|
|
void name(color_ostream &out, arg_type1 arg1, arg_type2 arg2, arg_type3 arg3, arg_type4 arg4, arg_type5 arg5) { \
|
|
|
|
if (auto state = name##_event.state_if_count()) { \
|
|
|
|
DFHack::Lua::Push(state, arg1); \
|
|
|
|
DFHack::Lua::Push(state, arg2); \
|
|
|
|
DFHack::Lua::Push(state, arg3); \
|
|
|
|
DFHack::Lua::Push(state, arg4); \
|
|
|
|
DFHack::Lua::Push(state, arg5); \
|
|
|
|
name##_event.invoke(out, 5); \
|
|
|
|
} \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define DEFINE_LUA_EVENT_NH_6(name, arg_type1, arg_type2, arg_type3, arg_type4, arg_type5,arg_type6) \
|
|
|
|
static DFHack::Lua::Notification name##_event; \
|
|
|
|
void name(color_ostream &out, arg_type1 arg1, arg_type2 arg2, arg_type3 arg3, arg_type4 arg4,arg_type5 arg5, arg_type6 arg6) { \
|
|
|
|
if (auto state = name##_event.state_if_count()) { \
|
|
|
|
DFHack::Lua::Push(state, arg1); \
|
|
|
|
DFHack::Lua::Push(state, arg2); \
|
|
|
|
DFHack::Lua::Push(state, arg3); \
|
|
|
|
DFHack::Lua::Push(state, arg4); \
|
|
|
|
DFHack::Lua::Push(state, arg5); \
|
|
|
|
DFHack::Lua::Push(state, arg6); \
|
|
|
|
name##_event.invoke(out, 6); \
|
|
|
|
} \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define DEFINE_LUA_EVENT_NH_7(name, arg_type1, arg_type2, arg_type3, arg_type4, arg_type5,arg_type6,arg_type7) \
|
|
|
|
static DFHack::Lua::Notification name##_event; \
|
|
|
|
void name(color_ostream &out, arg_type1 arg1, arg_type2 arg2, arg_type3 arg3, arg_type4 arg4,arg_type5 arg5, arg_type6 arg6, arg_type7 arg7) { \
|
|
|
|
if (auto state = name##_event.state_if_count()) { \
|
|
|
|
DFHack::Lua::Push(state, arg1); \
|
|
|
|
DFHack::Lua::Push(state, arg2); \
|
|
|
|
DFHack::Lua::Push(state, arg3); \
|
|
|
|
DFHack::Lua::Push(state, arg4); \
|
|
|
|
DFHack::Lua::Push(state, arg5); \
|
|
|
|
DFHack::Lua::Push(state, arg6); \
|
|
|
|
DFHack::Lua::Push(state, arg7); \
|
|
|
|
name##_event.invoke(out, 7); \
|
|
|
|
} \
|
|
|
|
}
|