1
0

LuaState: Added support for config-style usage.

Globals and table values can be queried from the Lua state easily.
Use perfect forwarding.
This commit is contained in:
Mattes D 2015-06-17 15:21:20 +02:00
parent 813ca04676
commit c6012a95bd
3 changed files with 207 additions and 43 deletions

View File

@ -211,23 +211,31 @@ void cLuaState::AddPackagePath(const AString & a_PathVariable, const AString & a
bool cLuaState::LoadFile(const AString & a_FileName) bool cLuaState::LoadFile(const AString & a_FileName, bool a_LogWarnings)
{ {
ASSERT(IsValid()); ASSERT(IsValid());
// Load the file: // Load the file:
int s = luaL_loadfile(m_LuaState, a_FileName.c_str()); int s = luaL_loadfile(m_LuaState, a_FileName.c_str());
if (ReportErrors(s)) if (s != 0)
{ {
LOGWARNING("Can't load %s because of an error in file %s", m_SubsystemName.c_str(), a_FileName.c_str()); if (a_LogWarnings)
{
LOGWARNING("Can't load %s because of a load error in file %s: %d (%s)", m_SubsystemName.c_str(), a_FileName.c_str(), s, lua_tostring(m_LuaState, -1));
}
lua_pop(m_LuaState, 1);
return false; return false;
} }
// Execute the globals: // Execute the globals:
s = lua_pcall(m_LuaState, 0, LUA_MULTRET, 0); s = lua_pcall(m_LuaState, 0, LUA_MULTRET, 0);
if (ReportErrors(s)) if (s != 0)
{ {
LOGWARNING("Error in %s in file %s", m_SubsystemName.c_str(), a_FileName.c_str()); if (a_LogWarnings)
{
LOGWARNING("Can't load %s because of an initialization error in file %s: %d (%s)", m_SubsystemName.c_str(), a_FileName.c_str(), s, lua_tostring(m_LuaState, -1));
}
lua_pop(m_LuaState, 1);
return false; return false;
} }
@ -446,6 +454,18 @@ void cLuaState::Push(const cPlayer * a_Player)
void cLuaState::Push(const cLuaState::cRef & a_Ref)
{
ASSERT(IsValid());
lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, static_cast<int>(a_Ref));
m_NumCurrentFunctionArgs += 1;
}
void cLuaState::Push(const HTTPRequest * a_Request) void cLuaState::Push(const HTTPRequest * a_Request)
{ {
ASSERT(IsValid()); ASSERT(IsValid());
@ -765,15 +785,18 @@ bool cLuaState::GetStackValue(int a_StackPos, double & a_ReturnedVal)
bool cLuaState::GetStackValue(int a_StackPos, float & a_ReturnedVal) bool cLuaState::GetStackValue(int a_StackPos, eBlockFace & a_ReturnedVal)
{ {
if (lua_isnumber(m_LuaState, a_StackPos)) if (!lua_isnumber(m_LuaState, a_StackPos))
{ {
a_ReturnedVal = static_cast<float>(tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal));
return true;
}
return false; return false;
} }
a_ReturnedVal = static_cast<eBlockFace>(Clamp(
static_cast<int>(tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal)),
static_cast<int>(BLOCK_FACE_MIN), static_cast<int>(BLOCK_FACE_MAX))
);
return true;
}
@ -796,6 +819,46 @@ bool cLuaState::GetStackValue(int a_StackPos, eWeather & a_ReturnedVal)
bool cLuaState::GetStackValue(int a_StackPos, float & a_ReturnedVal)
{
if (lua_isnumber(m_LuaState, a_StackPos))
{
a_ReturnedVal = static_cast<float>(tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal));
return true;
}
return false;
}
cLuaState::cStackValue cLuaState::WalkToValue(const AString & a_Name)
{
auto path = StringSplit(a_Name, ".");
lua_pushvalue(m_LuaState, -1); // Copy the stack value into the "working area"
for (const auto & elem: path)
{
// If the value is not a table, bail out (error):
if (!lua_istable(m_LuaState, -1))
{
lua_pop(m_LuaState, 1);
return cStackValue();
}
// Get the next part of the path:
lua_getfield(m_LuaState, -1, elem.c_str());
// Remove the previous value from the stack (keep only the new one):
lua_remove(m_LuaState, -2);
} // for elem - path[]
return std::move(cStackValue(*this));
}
bool cLuaState::CallFunction(int a_NumResults) bool cLuaState::CallFunction(int a_NumResults)
{ {
ASSERT (m_NumCurrentFunctionArgs >= 0); // A function must be pushed to stack first ASSERT (m_NumCurrentFunctionArgs >= 0); // A function must be pushed to stack first

View File

@ -78,11 +78,14 @@ public:
bool IsValid(void) const {return (m_Ref != LUA_REFNIL); } bool IsValid(void) const {return (m_Ref != LUA_REFNIL); }
/** Allows to use this class wherever an int (i. e. ref) is to be used */ /** Allows to use this class wherever an int (i. e. ref) is to be used */
operator int(void) const { return m_Ref; } explicit operator int(void) const { return m_Ref; }
protected: protected:
cLuaState * m_LuaState; cLuaState * m_LuaState;
int m_Ref; int m_Ref;
// Remove the copy-constructor:
cRef(const cRef &) = delete;
} ; } ;
@ -98,6 +101,12 @@ public:
{ {
} }
cTableRef(const cRef & a_TableRef, const char * a_FnName) :
m_TableRef(static_cast<int>(a_TableRef)),
m_FnName(a_FnName)
{
}
int GetTableRef(void) const { return m_TableRef; } int GetTableRef(void) const { return m_TableRef; }
const char * GetFnName(void) const { return m_FnName; } const char * GetFnName(void) const { return m_FnName; }
} ; } ;
@ -111,6 +120,61 @@ public:
static const cRet Return; // Use this constant to delimit function args from return values for cLuaState::Call() static const cRet Return; // Use this constant to delimit function args from return values for cLuaState::Call()
/** A RAII class for values pushed onto the Lua stack.
Will pop the value off the stack in the destructor. */
class cStackValue
{
public:
cStackValue(void):
m_LuaState(nullptr)
{
}
cStackValue(cLuaState & a_LuaState):
m_LuaState(a_LuaState)
{
m_StackLen = lua_gettop(a_LuaState);
}
cStackValue(cStackValue && a_Src):
m_LuaState(nullptr),
m_StackLen(-1)
{
std::swap(m_LuaState, a_Src.m_LuaState);
std::swap(m_StackLen, a_Src.m_StackLen);
}
~cStackValue()
{
if (m_LuaState != nullptr)
{
auto top = lua_gettop(m_LuaState);
ASSERT(m_StackLen == top);
lua_pop(m_LuaState, 1);
}
}
void Set(cLuaState & a_LuaState)
{
m_LuaState = a_LuaState;
m_StackLen = lua_gettop(a_LuaState);
}
bool IsValid(void) const
{
return (m_LuaState != nullptr);
}
protected:
lua_State * m_LuaState;
int m_StackLen;
// Remove the copy-constructor:
cStackValue(const cStackValue &) = delete;
};
/** Creates a new instance. The LuaState is not initialized. /** Creates a new instance. The LuaState is not initialized.
a_SubsystemName is used for reporting problems in the console, it is "plugin %s" for plugins, a_SubsystemName is used for reporting problems in the console, it is "plugin %s" for plugins,
or "LuaScript" for the cLuaScript template or "LuaScript" for the cLuaScript template
@ -151,10 +215,9 @@ public:
void AddPackagePath(const AString & a_PathVariable, const AString & a_Path); void AddPackagePath(const AString & a_PathVariable, const AString & a_Path);
/** Loads the specified file /** Loads the specified file
Returns false and logs a warning to the console if not successful (but the LuaState is kept open). Returns false and optionally logs a warning to the console if not successful (but the LuaState is kept open).
m_SubsystemName is displayed in the warning log message. m_SubsystemName is displayed in the warning log message. */
*/ bool LoadFile(const AString & a_FileName, bool a_LogWarnings = true);
bool LoadFile(const AString & a_FileName);
/** Returns true if a_FunctionName is a valid Lua function that can be called */ /** Returns true if a_FunctionName is a valid Lua function that can be called */
bool HasFunction(const char * a_FunctionName); bool HasFunction(const char * a_FunctionName);
@ -169,6 +232,7 @@ public:
void Push(const char * a_Value); void Push(const char * a_Value);
void Push(const cItems & a_Items); void Push(const cItems & a_Items);
void Push(const cPlayer * a_Player); void Push(const cPlayer * a_Player);
void Push(const cRef & a_Ref);
void Push(const HTTPRequest * a_Request); void Push(const HTTPRequest * a_Request);
void Push(const HTTPTemplateRequest * a_Request); void Push(const HTTPTemplateRequest * a_Request);
void Push(const Vector3d & a_Vector); void Push(const Vector3d & a_Vector);
@ -188,12 +252,13 @@ public:
// GetStackValue() retrieves the value at a_StackPos, if it is a valid type. If not, a_Value is unchanged. // GetStackValue() retrieves the value at a_StackPos, if it is a valid type. If not, a_Value is unchanged.
// Returns whether value was changed // Returns whether value was changed
// Enum values are clamped to their allowed range. // Enum values are checked for their allowed values and fail if the value is not assigned.
bool GetStackValue(int a_StackPos, AString & a_Value); bool GetStackValue(int a_StackPos, AString & a_Value);
bool GetStackValue(int a_StackPos, bool & a_Value); bool GetStackValue(int a_StackPos, bool & a_Value);
bool GetStackValue(int a_StackPos, cPluginManager::CommandResult & a_Result); bool GetStackValue(int a_StackPos, cPluginManager::CommandResult & a_Result);
bool GetStackValue(int a_StackPos, cRef & a_Ref); bool GetStackValue(int a_StackPos, cRef & a_Ref);
bool GetStackValue(int a_StackPos, double & a_Value); bool GetStackValue(int a_StackPos, double & a_Value);
bool GetStackValue(int a_StackPos, eBlockFace & a_Value);
bool GetStackValue(int a_StackPos, eWeather & a_Value); bool GetStackValue(int a_StackPos, eWeather & a_Value);
bool GetStackValue(int a_StackPos, float & a_ReturnedVal); bool GetStackValue(int a_StackPos, float & a_ReturnedVal);
@ -202,9 +267,11 @@ public:
bool GetStackValue(int a_StackPos, T & a_ReturnedVal, typename std::enable_if<std::is_integral<T>::value>::type * unused = nullptr) bool GetStackValue(int a_StackPos, T & a_ReturnedVal, typename std::enable_if<std::is_integral<T>::value>::type * unused = nullptr)
{ {
UNUSED(unused); UNUSED(unused);
if (lua_isnumber(m_LuaState, a_StackPos)) if (!lua_isnumber(m_LuaState, a_StackPos)) // Also accepts strings representing a number: http://pgl.yoyo.org/luai/i/lua_isnumber
{ {
lua_Number Val = tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal); return false;
}
lua_Number Val = lua_tonumber(m_LuaState, a_StackPos);
if (Val > std::numeric_limits<T>::max()) if (Val > std::numeric_limits<T>::max())
{ {
return false; return false;
@ -216,8 +283,38 @@ public:
a_ReturnedVal = static_cast<T>(Val); a_ReturnedVal = static_cast<T>(Val);
return true; return true;
} }
/** Pushes the named value in the table at the top of the stack.
a_Name may be a path containing multiple table levels, such as "_G.cChatColor.Blue".
If the value is found, it is pushed on top of the stack and the returned cStackValue is valid.
If the value is not found, the stack is unchanged and the returned cStackValue is invalid. */
cStackValue WalkToValue(const AString & a_Name);
/** Retrieves the named value in the table at the top of the Lua stack.
a_Name may be a path containing multiple table levels, such as "_G.cChatColor.Blue".
Returns true if the value was successfully retrieved, false on error. */
template <typename T> bool GetNamedValue(const AString & a_Name, T & a_Value)
{
auto stk = WalkToValue(a_Name);
if (!stk.IsValid())
{
// Name not found
return false; return false;
} }
return GetStackValue(-1, a_Value);
}
/** Retrieves the named global value. a_Name may be a path containing multiple table levels, such as "_G.cChatColor.Blue".
Returns true if the value was successfully retrieved, false on error. */
template <typename T> bool GetNamedGlobal(const AString & a_Name, T & a_Value)
{
// Push the globals table onto the stack and make it RAII-removed:
lua_getglobal(m_LuaState, "_G");
cStackValue stk(*this);
// Get the named global:
return GetNamedValue(a_Name, a_Value);
}
// Include the auto-generated Push and GetStackValue() functions: // Include the auto-generated Push and GetStackValue() functions:
#include "LuaState_Declaration.inc" #include "LuaState_Declaration.inc"
@ -229,12 +326,12 @@ public:
template <typename FnT, typename... Args> template <typename FnT, typename... Args>
bool Call(const FnT & a_Function, Args &&... args) bool Call(const FnT & a_Function, Args &&... args)
{ {
if (!PushFunction(a_Function)) if (!PushFunction(std::forward<const FnT &>(a_Function)))
{ {
// Pushing the function failed // Pushing the function failed
return false; return false;
} }
return PushCallPop(args...); return PushCallPop(std::forward<Args>(args)...);
} }
/** Retrieves a list of values from the Lua stack, starting at the specified index. */ /** Retrieves a list of values from the Lua stack, starting at the specified index. */
@ -343,10 +440,10 @@ protected:
/** Variadic template recursor: More params to push. Push them and recurse. */ /** Variadic template recursor: More params to push. Push them and recurse. */
template <typename T, typename... Args> template <typename T, typename... Args>
inline bool PushCallPop(T a_Param, Args &&... args) inline bool PushCallPop(T && a_Param, Args &&... args)
{ {
Push(a_Param); Push(std::forward<T>(a_Param));
return PushCallPop(args...); return PushCallPop(std::forward<Args>(args)...);
} }
/** Variadic template terminator: If there's nothing more to push, but return values to collect, call the function and collect the returns. */ /** Variadic template terminator: If there's nothing more to push, but return values to collect, call the function and collect the returns. */
@ -363,7 +460,7 @@ protected:
} }
// Collect the return values: // Collect the return values:
GetStackValues(-NumReturns, args...); GetStackValues(-NumReturns, std::forward<Args>(args)...);
lua_pop(m_LuaState, NumReturns); lua_pop(m_LuaState, NumReturns);
// All successful: // All successful:

View File

@ -29,7 +29,7 @@ enum
/// Block face constants, used in PlayerDigging and PlayerBlockPlacement packets and bbox collision calc /** Block face constants, used in PlayerDigging and PlayerBlockPlacement packets and bbox collision calc */
enum eBlockFace enum eBlockFace
{ {
BLOCK_FACE_NONE = -1, // Interacting with no block face - swinging the item in the air BLOCK_FACE_NONE = -1, // Interacting with no block face - swinging the item in the air
@ -47,6 +47,10 @@ enum eBlockFace
BLOCK_FACE_SOUTH = BLOCK_FACE_ZP, // Interacting with the southern face of the block BLOCK_FACE_SOUTH = BLOCK_FACE_ZP, // Interacting with the southern face of the block
BLOCK_FACE_WEST = BLOCK_FACE_XM, // Interacting with the western face of the block BLOCK_FACE_WEST = BLOCK_FACE_XM, // Interacting with the western face of the block
BLOCK_FACE_EAST = BLOCK_FACE_XP, // Interacting with the eastern face of the block BLOCK_FACE_EAST = BLOCK_FACE_XP, // Interacting with the eastern face of the block
// Bounds, used for range-checking:
BLOCK_FACE_MIN = -1,
BLOCK_FACE_MAX = 5,
} ; } ;