1
0
Fork 0

Implemented cPluginManager:CallPlugin() API.

This function supersedes cPlugin:Call(), is safer to use in regards to multithreading and once again removes the need for the cPlugin class being exported at all.
This commit is contained in:
madmaxoft 2014-01-21 22:59:08 +01:00
parent 9c93ab15ab
commit 2a018cfa49
9 changed files with 508 additions and 217 deletions

View File

@ -1667,7 +1667,7 @@ a_Player:OpenWindow(Window);
]],
Functions =
{
Call = { Params = "Function name, [All the parameters divided with commas]", Notes = "This function allows you to call a function from another plugin. It can only use pass: integers, booleans, strings and usertypes (cPlayer, cEntity, cCuboid, etc.)." },
Call = { Params = "Function name, [All the parameters divided with commas]", Notes = "(<b>OBSOLETE</b>) This function allows you to call a function from another plugin. It can only use pass: integers, booleans, strings and usertypes (cPlayer, cEntity, cCuboid, etc.).<br /><br /><b>This function is obsolete and unsafe, use {{cPluginManager}}:CallPlugin() instead!</b>" },
GetDirectory = { Return = "string", Notes = "Returns the name of the folder where the plugin's files are. (APIDump)" },
GetLocalDirectory = { Notes = "OBSOLETE use GetLocalFolder instead." },
GetLocalFolder = { Return = "string", Notes = "Returns the path where the plugin's files are. (Plugins/APIDump)" },
@ -1719,6 +1719,7 @@ cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChatMessage);
{ Params = "Command, Callback, HelpString", Return = "", Notes = "(STATIC) Binds a console command with the specified callback function and help string. By common convention, providing an empty string for HelpString will hide the command from the \"help\" console command." },
{ Params = "Command, Callback, HelpString", Return = "", Notes = "Binds a console command with the specified callback function and help string. By common convention, providing an empty string for HelpString will hide the command from the \"help\" console command." },
},
CallPlugin = { Params = "PluginName, FunctionName, [FunctionArgs...]", Return = "[FunctionRets]", Notes = "(STATIC) Calls the specified function in the specified plugin, passing all the given arguments to it. If it succeeds, it returns all the values returned by that function. If it fails, returns no value at all. Note that only strings, numbers, bools, nils and classes can be used for parameters and return values; tables and functions cannot be copied across plugins." },
DisablePlugin = { Params = "PluginName", Return = "bool", Notes = "Disables a plugin specified by its name. Returns true if the plugin was disabled, false if it wasn't found or wasn't active." },
ExecuteCommand = { Params = "{{cPlayer|Player}}, CommandStr", Return = "bool", Notes = "Executes the command as if given by the specified Player. Checks permissions. Returns true if executed." },
FindPlugins = { Params = "", Return = "", Notes = "Refreshes the list of plugins to include all folders inside the Plugins folder (potentially new disabled plugins)" },

View File

@ -64,7 +64,8 @@ function Initialize(Plugin)
-- TestBlockAreas();
-- TestSQLiteBindings();
-- TestExpatBindings();
TestPluginCalls();
return true
end;
@ -72,6 +73,38 @@ end;
function TestPluginCalls()
-- In order to test the inter-plugin communication, we're going to call Core's ReturnColorFromChar() function
-- It is a rather simple function that doesn't need any tables as its params and returns a value, too
-- Note the signature: function ReturnColorFromChar( Split, char ) ... return cChatColog.Gray ... end
-- The Split parameter should be a table, but it is not used in that function anyway,
-- so we can get away with passing nil to it.
-- Use the old, deprecated and unsafe method:
local Core = cPluginManager:Get():GetPlugin("Core")
if (Core ~= nil) then
LOGINFO("Calling Core::ReturnColorFromChar() the old-fashioned way...")
local Gray = Core:Call("ReturnColorFromChar", nil, "8")
if (Gray ~= cChatColor.Gray) then
LOGWARNING("Call failed, exp " .. cChatColor.Gray .. ", got " .. (Gray or "<nil>"))
else
LOGINFO("Call succeeded")
end
end
-- Use the new method:
LOGINFO("Calling Core::ReturnColorFromChar() the recommended way...")
local Gray = cPluginManager:CallPlugin("Core", "ReturnColorFromChar", nil, "8")
if (Gray ~= cChatColor.Gray) then
LOGWARNING("Call failed, exp " .. cChatColor.Gray .. ", got " .. (Gray or "<nil>"))
else
LOGINFO("Call succeeded")
end
end
function TestBlockAreas()
LOG("Testing block areas...");

View File

@ -235,7 +235,7 @@ bool cLuaState::PushFunction(const char * a_FunctionName)
if (!lua_isfunction(m_LuaState, -1))
{
LOGWARNING("Error in %s: Could not find function %s()", m_SubsystemName.c_str(), a_FunctionName);
lua_pop(m_LuaState, 1);
lua_pop(m_LuaState, 2);
return false;
}
m_CurrentFunctionName.assign(a_FunctionName);
@ -258,7 +258,7 @@ bool cLuaState::PushFunction(int a_FnRef)
lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, a_FnRef); // same as lua_getref()
if (!lua_isfunction(m_LuaState, -1))
{
lua_pop(m_LuaState, 1);
lua_pop(m_LuaState, 2);
return false;
}
m_CurrentFunctionName = "<callback>";
@ -282,7 +282,7 @@ bool cLuaState::PushFunction(const cTableRef & a_TableRef)
if (!lua_istable(m_LuaState, -1))
{
// Not a table, bail out
lua_pop(m_LuaState, 1);
lua_pop(m_LuaState, 2);
return false;
}
lua_getfield(m_LuaState, -1, a_TableRef.GetFnName());
@ -742,6 +742,10 @@ bool cLuaState::CallFunction(int a_NumResults)
}
m_NumCurrentFunctionArgs = -1;
m_CurrentFunctionName.clear();
// Remove the error handler from the stack:
lua_remove(m_LuaState, -a_NumResults - 1);
return true;
}
@ -1025,21 +1029,184 @@ void cLuaState::LogStackTrace(lua_State * a_LuaState)
AString cLuaState::GetTypeText(int a_StackPos)
{
int Type = lua_type(m_LuaState, a_StackPos);
switch (Type)
return lua_typename(m_LuaState, lua_type(m_LuaState, a_StackPos));
}
int cLuaState::CallFunctionWithForeignParams(
const AString & a_FunctionName,
cLuaState & a_SrcLuaState,
int a_SrcParamStart,
int a_SrcParamEnd
)
{
ASSERT(IsValid());
ASSERT(a_SrcLuaState.IsValid());
// Store the stack position before any changes
int OldTop = lua_gettop(m_LuaState);
// Push the function to call, including the error handler:
if (!PushFunction(a_FunctionName.c_str()))
{
case LUA_TNONE: return "TNONE";
case LUA_TNIL: return "TNIL";
case LUA_TBOOLEAN: return "TBOOLEAN";
case LUA_TLIGHTUSERDATA: return "TLIGHTUSERDATA";
case LUA_TNUMBER: return "TNUMBER";
case LUA_TSTRING: return "TSTRING";
case LUA_TTABLE: return "TTABLE";
case LUA_TFUNCTION: return "TFUNCTION";
case LUA_TUSERDATA: return "TUSERDATA";
case LUA_TTHREAD: return "TTHREAD";
LOGWARNING("Function '%s' not found", a_FunctionName.c_str());
lua_pop(m_LuaState, 2);
return -1;
}
return Printf("Unknown (%d)", Type);
// Copy the function parameters to the target state
if (CopyStackFrom(a_SrcLuaState, a_SrcParamStart, a_SrcParamEnd) < 0)
{
// Something went wrong, fix the stack and exit
lua_pop(m_LuaState, 2);
return -1;
}
// Call the function, with an error handler:
int s = lua_pcall(m_LuaState, a_SrcParamEnd - a_SrcParamStart + 1, LUA_MULTRET, OldTop);
if (ReportErrors(s))
{
LOGWARN("Error while calling function '%s' in '%s'", a_FunctionName.c_str(), m_SubsystemName.c_str());
// Fix the stack.
// We don't know how many values have been pushed, so just get rid of any that weren't there initially
int CurTop = lua_gettop(m_LuaState);
if (CurTop > OldTop)
{
lua_pop(m_LuaState, CurTop - OldTop);
}
return -1;
}
// Reset the internal checking mechanisms:
m_NumCurrentFunctionArgs = -1;
// Remove the error handler from the stack:
lua_remove(m_LuaState, OldTop + 1);
// Return the number of return values:
return lua_gettop(m_LuaState) - OldTop;
}
int cLuaState::CopyStackFrom(cLuaState & a_SrcLuaState, int a_SrcStart, int a_SrcEnd)
{
/*
// DEBUG:
LOGD("Copying stack values from %d to %d", a_SrcStart, a_SrcEnd);
a_SrcLuaState.LogStack("Src stack before copying:");
LogStack("Dst stack before copying:");
*/
for (int i = a_SrcStart; i <= a_SrcEnd; ++i)
{
int t = lua_type(a_SrcLuaState, i);
switch (t)
{
case LUA_TNIL:
{
lua_pushnil(m_LuaState);
break;
}
case LUA_TSTRING:
{
AString s;
a_SrcLuaState.ToString(i, s);
Push(s);
break;
}
case LUA_TBOOLEAN:
{
bool b = (tolua_toboolean(a_SrcLuaState, i, false) != 0);
Push(b);
break;
}
case LUA_TNUMBER:
{
lua_Number d = tolua_tonumber(a_SrcLuaState, i, 0);
Push(d);
break;
}
case LUA_TUSERDATA:
{
// Get the class name:
const char * type = NULL;
if (lua_getmetatable(a_SrcLuaState, i) == 0)
{
LOGWARNING("%s: Unknown class in pos %d, cannot copy.", __FUNCTION__, i);
lua_pop(m_LuaState, i - a_SrcStart);
return -1;
}
lua_rawget(a_SrcLuaState, LUA_REGISTRYINDEX); // Stack +1
type = lua_tostring(a_SrcLuaState, -1);
lua_pop(a_SrcLuaState, 1); // Stack -1
// Copy the value:
void * ud = tolua_touserdata(a_SrcLuaState, i, NULL);
tolua_pushusertype(m_LuaState, ud, type);
}
default:
{
LOGWARNING("%s: Unsupported value: '%s' at stack position %d. Can only copy numbers, strings, bools and classes!",
__FUNCTION__, lua_typename(a_SrcLuaState, t), i
);
a_SrcLuaState.LogStack("Stack where copying failed:");
lua_pop(m_LuaState, i - a_SrcStart);
return -1;
}
}
}
return a_SrcEnd - a_SrcStart + 1;
}
void cLuaState::ToString(int a_StackPos, AString & a_String)
{
size_t len;
const char * s = lua_tolstring(m_LuaState, a_StackPos, &len);
if (s != NULL)
{
a_String.assign(s, len);
}
}
void cLuaState::LogStack(const char * a_Header)
{
LogStack(m_LuaState, a_Header);
}
void cLuaState::LogStack(lua_State * a_LuaState, const char * a_Header)
{
LOGD((a_Header != NULL) ? a_Header : "Lua C API Stack contents:");
for (int i = lua_gettop(a_LuaState); i >= 0; i--)
{
AString Value;
int Type = lua_type(a_LuaState, i);
switch (Type)
{
case LUA_TBOOLEAN: Value.assign((lua_toboolean(a_LuaState, i) != 0) ? "true" : "false"); break;
case LUA_TLIGHTUSERDATA: Printf(Value, "%p", lua_touserdata(a_LuaState, i)); break;
case LUA_TNUMBER: Printf(Value, "%f", (double)lua_tonumber(a_LuaState, i)); break;
case LUA_TSTRING: Printf(Value, "%s", lua_tostring(a_LuaState, i)); break;
default: break;
}
LOGD(" Idx %d: type %d (%s) %s", i, Type, lua_typename(a_LuaState, Type), Value.c_str());
} // for i - stack idx
}

View File

@ -60,23 +60,23 @@ class cBlockEntity;
/// Encapsulates a Lua state and provides some syntactic sugar for common operations
/** Encapsulates a Lua state and provides some syntactic sugar for common operations */
class cLuaState
{
public:
/// Used for storing references to object in the global registry
/** Used for storing references to object in the global registry */
class cRef
{
public:
/// Creates a reference in the specified LuaState for object at the specified StackPos
/** Creates a reference in the specified LuaState for object at the specified StackPos */
cRef(cLuaState & a_LuaState, int a_StackPos);
~cRef();
/// Returns true if the reference is valid
/** Returns true if the reference is valid */
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; }
protected:
@ -102,7 +102,7 @@ public:
} ;
/// A dummy class that's used only to delimit function args from return values for cLuaState::Call()
/** A dummy class that's used only to delimit function args from return values for cLuaState::Call() */
class cRet
{
} ;
@ -123,22 +123,22 @@ public:
~cLuaState();
/// Allows this object to be used in the same way as a lua_State *, for example in the LuaLib functions
/** Allows this object to be used in the same way as a lua_State *, for example in the LuaLib functions */
operator lua_State * (void) { return m_LuaState; }
/// Creates the m_LuaState, if not closed already. This state will be automatically closed in the destructor
/** Creates the m_LuaState, if not closed already. This state will be automatically closed in the destructor */
void Create(void);
/// Closes the m_LuaState, if not closed already
/** Closes the m_LuaState, if not closed already */
void Close(void);
/// Attaches the specified state. Operations will be carried out on this state, but it will not be closed in the destructor
/** Attaches the specified state. Operations will be carried out on this state, but it will not be closed in the destructor */
void Attach(lua_State * a_State);
/// Detaches a previously attached state.
/** Detaches a previously attached state. */
void Detach(void);
/// Returns true if the m_LuaState is valid
/** Returns true if the m_LuaState is valid */
bool IsValid(void) const { return (m_LuaState != NULL); }
/** Loads the specified file
@ -147,7 +147,7 @@ public:
*/
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);
// Push a value onto the stack
@ -182,7 +182,7 @@ public:
void Push(cHopperEntity * a_Hopper);
void Push(cBlockEntity * a_BlockEntity);
/// Call any 0-param 0-return Lua function in a single line:
/** Call any 0-param 0-return Lua function in a single line: */
template <typename FnT>
bool Call(FnT a_FnName)
{
@ -193,7 +193,7 @@ public:
return CallFunction(0);
}
/// Call any 1-param 0-return Lua function in a single line:
/** Call any 1-param 0-return Lua function in a single line: */
template<
typename FnT,
typename ArgT1
@ -208,7 +208,7 @@ public:
return CallFunction(0);
}
/// Call any 2-param 0-return Lua function in a single line:
/** Call any 2-param 0-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename ArgT2
>
@ -223,7 +223,7 @@ public:
return CallFunction(0);
}
/// Call any 3-param 0-return Lua function in a single line:
/** Call any 3-param 0-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename ArgT2, typename ArgT3
>
@ -239,7 +239,7 @@ public:
return CallFunction(0);
}
/// Call any 0-param 1-return Lua function in a single line:
/** Call any 0-param 1-return Lua function in a single line: */
template<
typename FnT, typename RetT1
>
@ -259,12 +259,13 @@ public:
return true;
}
/// Call any 1-param 1-return Lua function in a single line:
/** Call any 1-param 1-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename RetT1
>
bool Call(FnT a_FnName, ArgT1 a_Arg1, const cRet & a_Mark, RetT1 & a_Ret1)
{
int InitialTop = lua_gettop(m_LuaState);
UNUSED(a_Mark);
if (!PushFunction(a_FnName))
{
@ -277,10 +278,11 @@ public:
}
GetReturn(-1, a_Ret1);
lua_pop(m_LuaState, 1);
ASSERT(InitialTop == lua_gettop(m_LuaState));
return true;
}
/// Call any 2-param 1-return Lua function in a single line:
/** Call any 2-param 1-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename ArgT2, typename RetT1
>
@ -302,7 +304,7 @@ public:
return true;
}
/// Call any 3-param 1-return Lua function in a single line:
/** Call any 3-param 1-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename RetT1
>
@ -325,7 +327,7 @@ public:
return true;
}
/// Call any 4-param 1-return Lua function in a single line:
/** Call any 4-param 1-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename RetT1
>
@ -349,7 +351,7 @@ public:
return true;
}
/// Call any 5-param 1-return Lua function in a single line:
/** Call any 5-param 1-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename RetT1
>
@ -374,7 +376,7 @@ public:
return true;
}
/// Call any 6-param 1-return Lua function in a single line:
/** Call any 6-param 1-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6,
typename RetT1
@ -401,7 +403,7 @@ public:
return true;
}
/// Call any 7-param 1-return Lua function in a single line:
/** Call any 7-param 1-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6,
typename ArgT7, typename RetT1
@ -429,7 +431,7 @@ public:
return true;
}
/// Call any 8-param 1-return Lua function in a single line:
/** Call any 8-param 1-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6,
typename ArgT7, typename ArgT8, typename RetT1
@ -458,7 +460,7 @@ public:
return true;
}
/// Call any 9-param 1-return Lua function in a single line:
/** Call any 9-param 1-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6,
typename ArgT7, typename ArgT8, typename ArgT9, typename RetT1
@ -488,7 +490,7 @@ public:
return true;
}
/// Call any 10-param 1-return Lua function in a single line:
/** Call any 10-param 1-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6,
typename ArgT7, typename ArgT8, typename ArgT9, typename ArgT10, typename RetT1
@ -519,7 +521,7 @@ public:
return true;
}
/// Call any 1-param 2-return Lua function in a single line:
/** Call any 1-param 2-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename RetT1, typename RetT2
>
@ -541,7 +543,7 @@ public:
return true;
}
/// Call any 2-param 2-return Lua function in a single line:
/** Call any 2-param 2-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename ArgT2, typename RetT1, typename RetT2
>
@ -564,7 +566,7 @@ public:
return true;
}
/// Call any 3-param 2-return Lua function in a single line:
/** Call any 3-param 2-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename ArgT2, typename ArgT3,
typename RetT1, typename RetT2
@ -589,7 +591,7 @@ public:
return true;
}
/// Call any 4-param 2-return Lua function in a single line:
/** Call any 4-param 2-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4,
typename RetT1, typename RetT2
@ -615,7 +617,7 @@ public:
return true;
}
/// Call any 5-param 2-return Lua function in a single line:
/** Call any 5-param 2-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5,
typename RetT1, typename RetT2
@ -642,7 +644,7 @@ public:
return true;
}
/// Call any 6-param 2-return Lua function in a single line:
/** Call any 6-param 2-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5,
typename ArgT6,
@ -671,7 +673,7 @@ public:
return true;
}
/// Call any 7-param 2-return Lua function in a single line:
/** Call any 7-param 2-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5,
typename ArgT6, typename ArgT7,
@ -701,7 +703,7 @@ public:
return true;
}
/// Call any 7-param 3-return Lua function in a single line:
/** Call any 7-param 3-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5,
typename ArgT6, typename ArgT7,
@ -732,7 +734,7 @@ public:
return true;
}
/// Call any 8-param 3-return Lua function in a single line:
/** Call any 8-param 3-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5,
typename ArgT6, typename ArgT7, typename ArgT8,
@ -764,7 +766,7 @@ public:
return true;
}
/// Call any 9-param 5-return Lua function in a single line:
/** Call any 9-param 5-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5,
typename ArgT6, typename ArgT7, typename ArgT8, typename ArgT9,
@ -800,46 +802,71 @@ public:
}
/// Returns true if the specified parameters on the stack are of the specified usertable type; also logs warning if not. Used for static functions
/** Returns true if the specified parameters on the stack are of the specified usertable type; also logs warning if not. Used for static functions */
bool CheckParamUserTable(int a_StartParam, const char * a_UserTable, int a_EndParam = -1);
/// Returns true if the specified parameters on the stack are of the specified usertype; also logs warning if not. Used for regular functions
/** Returns true if the specified parameters on the stack are of the specified usertype; also logs warning if not. Used for regular functions */
bool CheckParamUserType(int a_StartParam, const char * a_UserType, int a_EndParam = -1);
/// Returns true if the specified parameters on the stack are tables; also logs warning if not
/** Returns true if the specified parameters on the stack are tables; also logs warning if not */
bool CheckParamTable(int a_StartParam, int a_EndParam = -1);
/// Returns true if the specified parameters on the stack are numbers; also logs warning if not
/** Returns true if the specified parameters on the stack are numbers; also logs warning if not */
bool CheckParamNumber(int a_StartParam, int a_EndParam = -1);
/// Returns true if the specified parameters on the stack are strings; also logs warning if not
/** Returns true if the specified parameters on the stack are strings; also logs warning if not */
bool CheckParamString(int a_StartParam, int a_EndParam = -1);
/// Returns true if the specified parameters on the stack are functions; also logs warning if not
/** Returns true if the specified parameters on the stack are functions; also logs warning if not */
bool CheckParamFunction(int a_StartParam, int a_EndParam = -1);
/// Returns true if the specified parameter on the stack is nil (indicating an end-of-parameters)
/** Returns true if the specified parameter on the stack is nil (indicating an end-of-parameters) */
bool CheckParamEnd(int a_Param);
/// If the status is nonzero, prints the text on the top of Lua stack and returns true
/** If the status is nonzero, prints the text on the top of Lua stack and returns true */
bool ReportErrors(int status);
/// If the status is nonzero, prints the text on the top of Lua stack and returns true
/** If the status is nonzero, prints the text on the top of Lua stack and returns true */
static bool ReportErrors(lua_State * a_LuaState, int status);
/// Logs all items in the current stack trace to the server console
/** Logs all items in the current stack trace to the server console */
void LogStackTrace(void);
/// Logs all items in the current stack trace to the server console
/** Logs all items in the current stack trace to the server console */
static void LogStackTrace(lua_State * a_LuaState);
/// Returns the type of the item on the specified position in the stack
/** Returns the type of the item on the specified position in the stack */
AString GetTypeText(int a_StackPos);
/** Calls the function specified by its name, with arguments copied off the foreign state.
If successful, keeps the return values on the stack and returns their number.
If unsuccessful, returns a negative number and keeps the stack position unchanged. */
int CallFunctionWithForeignParams(
const AString & a_FunctionName,
cLuaState & a_SrcLuaState,
int a_SrcParamStart,
int a_SrcParamEnd
);
/** Copies objects on the stack from the specified state.
Only numbers, bools, strings and userdatas are copied.
If successful, returns the number of objects copied.
If failed, returns a negative number and rewinds the stack position. */
int CopyStackFrom(cLuaState & a_SrcLuaState, int a_SrcStart, int a_SrcEnd);
/** Reads the value at the specified stack position as a string and sets it to a_String. */
void ToString(int a_StackPos, AString & a_String);
/** Logs all the elements' types on the API stack, with an optional header for the listing. */
void LogStack(const char * a_Header);
/** Logs all the elements' types on the API stack, with an optional header for the listing. */
static void LogStack(lua_State * a_LuaState, const char * a_Header = NULL);
protected:
lua_State * m_LuaState;
/// If true, the state is owned by this object and will be auto-Closed. False => attached state
/** If true, the state is owned by this object and will be auto-Closed. False => attached state */
bool m_IsOwned;
/** The subsystem name is used for reporting errors to the console, it is either "plugin %s" or "LuaScript"
@ -847,10 +874,10 @@ protected:
*/
AString m_SubsystemName;
/// Name of the currently pushed function (for the Push / Call chain)
/** Name of the currently pushed function (for the Push / Call chain) */
AString m_CurrentFunctionName;
/// Number of arguments currently pushed (for the Push / Call chain)
/** Number of arguments currently pushed (for the Push / Call chain) */
int m_NumCurrentFunctionArgs;
@ -869,19 +896,19 @@ protected:
*/
bool PushFunction(const cTableRef & a_TableRef);
/// Pushes a usertype of the specified class type onto the stack
/** Pushes a usertype of the specified class type onto the stack */
void PushUserType(void * a_Object, const char * a_Type);
/// Retrieve value returned at a_StackPos, if it is a valid bool. If not, a_ReturnedVal is unchanged
/** Retrieve value returned at a_StackPos, if it is a valid bool. If not, a_ReturnedVal is unchanged */
void GetReturn(int a_StackPos, bool & a_ReturnedVal);
/// Retrieve value returned at a_StackPos, if it is a valid string. If not, a_ReturnedVal is unchanged
/** Retrieve value returned at a_StackPos, if it is a valid string. If not, a_ReturnedVal is unchanged */
void GetReturn(int a_StackPos, AString & a_ReturnedVal);
/// Retrieve value returned at a_StackPos, if it is a valid number. If not, a_ReturnedVal is unchanged
/** Retrieve value returned at a_StackPos, if it is a valid number. If not, a_ReturnedVal is unchanged */
void GetReturn(int a_StackPos, int & a_ReturnedVal);
/// Retrieve value returned at a_StackPos, if it is a valid number. If not, a_ReturnedVal is unchanged
/** Retrieve value returned at a_StackPos, if it is a valid number. If not, a_ReturnedVal is unchanged */
void GetReturn(int a_StackPos, double & a_ReturnedVal);
/**

View File

@ -1540,6 +1540,85 @@ static int tolua_cPluginManager_BindConsoleCommand(lua_State * L)
static int tolua_cPluginManager_CallPlugin(lua_State * tolua_S)
{
/*
Function signature:
cPluginManager:CallPlugin("PluginName", "FunctionName", args...)
*/
// Check the parameters:
cLuaState L(tolua_S);
if (
!L.CheckParamUserTable(1, "cPluginManager") ||
!L.CheckParamString(2, 3))
{
return 0;
}
// Retrieve the plugin name and function name
AString PluginName, FunctionName;
L.ToString(2, PluginName);
L.ToString(3, FunctionName);
if (PluginName.empty() || FunctionName.empty())
{
LOGWARNING("cPluginManager:CallPlugin(): Invalid plugin name or function name");
L.LogStackTrace();
return 0;
}
// If requesting calling the current plugin, refuse:
cPluginLua * ThisPlugin = GetLuaPlugin(L);
if (ThisPlugin == NULL)
{
return 0;
}
if (ThisPlugin->GetName() == PluginName)
{
LOGWARNING("cPluginManager::CallPlugin(): Calling self is not implemented (why would it?)");
L.LogStackTrace();
return 0;
}
// Call the destination plugin using a plugin callback:
class cCallback :
public cPluginManager::cPluginCallback
{
public:
int m_NumReturns;
cCallback(const AString & a_FunctionName, cLuaState & a_SrcLuaState) :
m_FunctionName(a_FunctionName),
m_SrcLuaState(a_SrcLuaState),
m_NumReturns(0)
{
}
protected:
const AString & m_FunctionName;
cLuaState & m_SrcLuaState;
virtual bool Item(cPlugin * a_Plugin) override
{
m_NumReturns = ((cPluginLua *)a_Plugin)->CallFunctionFromForeignState(
m_FunctionName, m_SrcLuaState, 4, lua_gettop(m_SrcLuaState)
);
return true;
}
} Callback(FunctionName, L);
if (!cPluginManager::Get()->DoWithPlugin(PluginName, Callback))
{
// TODO 2014_01_20 _X: This might be too much logging, plugins cannot know if other plugins are loaded (async)
LOGWARNING("cPluginManager::CallPlugin: No such plugin name (\"%s\")", PluginName.c_str());
L.LogStackTrace();
return 0;
}
return Callback.m_NumReturns;
}
static int tolua_cPlayer_GetGroups(lua_State* tolua_S)
{
cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
@ -1734,112 +1813,28 @@ static int tolua_cPluginLua_AddTab(lua_State* tolua_S)
// Perhaps use this as well for copying tables https://github.com/keplerproject/rings/pull/1
static int copy_lua_values(lua_State * a_Source, lua_State * a_Destination, int i, int top)
static int tolua_cPlugin_Call(lua_State * tolua_S)
{
for(; i <= top; ++i )
{
int t = lua_type(a_Source, i);
switch (t) {
case LUA_TSTRING: /* strings */
{
const char * s = lua_tostring(a_Source, i);
LOGD("%i push string: %s", i, s);
tolua_pushstring(a_Destination, s);
}
break;
case LUA_TBOOLEAN: /* booleans */
{
int b = tolua_toboolean(a_Source, i, false);
LOGD("%i push bool: %i", i, b);
tolua_pushboolean(a_Destination, b );
}
break;
case LUA_TNUMBER: /* numbers */
{
lua_Number d = tolua_tonumber(a_Source, i, 0);
LOGD("%i push number: %0.2f", i, d);
tolua_pushnumber(a_Destination, d );
}
break;
case LUA_TUSERDATA:
{
const char * type = 0;
if (lua_getmetatable(a_Source,i))
{
lua_rawget(a_Source, LUA_REGISTRYINDEX);
type = lua_tostring(a_Source, -1);
lua_pop(a_Source, 1); // Pop.. something?! I don't knooow~~ T_T
}
// don't need tolua_tousertype we already have the type
void * ud = tolua_touserdata(a_Source, i, 0);
LOGD("%i push usertype: %p of type '%s'", i, ud, type);
if( type == 0 )
{
LOGERROR("Call(): Something went wrong when trying to get usertype name!");
return 0;
}
tolua_pushusertype(a_Destination, ud, type);
}
break;
default: /* other values */
LOGERROR("Call(): Unsupported value: '%s'. Can only use numbers and strings!", lua_typename(a_Source, t));
return 0;
}
}
return 1;
}
static int tolua_cPlugin_Call(lua_State* tolua_S)
{
cPluginLua * self = (cPluginLua *) tolua_tousertype(tolua_S, 1, 0);
lua_State* targetState = self->GetLuaState();
int targetTop = lua_gettop(targetState);
int top = lua_gettop(tolua_S);
LOGD("total in stack: %i", top );
std::string funcName = tolua_tostring(tolua_S, 2, "");
LOGD("Func name: %s", funcName.c_str() );
lua_getglobal(targetState, funcName.c_str());
if(!lua_isfunction(targetState,-1))
{
LOGWARN("Error could not find function '%s' in plugin '%s'", funcName.c_str(), self->GetName().c_str() );
lua_pop(targetState,1);
return 0;
}
if( copy_lua_values(tolua_S, targetState, 3, top) == 0 ) // Start at 3 because 1 and 2 are the plugin and function name respectively
{
// something went wrong, exit
return 0;
}
cLuaState L(tolua_S);
int s = lua_pcall(targetState, top - 2, LUA_MULTRET, 0);
if (cLuaState::ReportErrors(targetState, s))
// Log the obsoletion warning:
LOGWARNING("cPlugin:Call() is obsolete and unsafe, use cPluginManager:CallPlugin() instead.");
L.LogStackTrace();
// Retrieve the params: plugin and the function name to call
cPluginLua * TargetPlugin = (cPluginLua *) tolua_tousertype(tolua_S, 1, 0);
AString FunctionName = tolua_tostring(tolua_S, 2, "");
// Call the function:
int NumReturns = TargetPlugin->CallFunctionFromForeignState(FunctionName, L, 3, lua_gettop(L));
if (NumReturns < 0)
{
LOGWARN("Error while calling function '%s' in plugin '%s'", funcName.c_str(), self->GetName().c_str() );
LOGWARNING("cPlugin::Call() failed to call destination function");
L.LogStackTrace();
return 0;
}
int nresults = lua_gettop(targetState) - targetTop;
LOGD("num results: %i", nresults);
int ttop = lua_gettop(targetState);
if( copy_lua_values(targetState, tolua_S, targetTop+1, ttop) == 0 ) // Start at targetTop+1 and I have no idea why xD
{
// something went wrong, exit
return 0;
}
lua_pop(targetState, nresults); // I have no idea what I'm doing, but it works
return nresults;
return NumReturns;
}
@ -2305,6 +2300,7 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_function(tolua_S, "AddHook", tolua_cPluginManager_AddHook);
tolua_function(tolua_S, "BindCommand", tolua_cPluginManager_BindCommand);
tolua_function(tolua_S, "BindConsoleCommand", tolua_cPluginManager_BindConsoleCommand);
tolua_function(tolua_S, "CallPlugin", tolua_cPluginManager_CallPlugin);
tolua_function(tolua_S, "ForEachCommand", tolua_cPluginManager_ForEachCommand);
tolua_function(tolua_S, "ForEachConsoleCommand", tolua_cPluginManager_ForEachConsoleCommand);
tolua_function(tolua_S, "GetAllPlugins", tolua_cPluginManager_GetAllPlugins);

View File

@ -1469,6 +1469,40 @@ bool cPluginLua::AddHookRef(int a_HookType, int a_FnRefIdx)
int cPluginLua::CallFunctionFromForeignState(
const AString & a_FunctionName,
cLuaState & a_ForeignState,
int a_ParamStart,
int a_ParamEnd
)
{
cCSLock Lock(m_CriticalSection);
// Call the function:
int NumReturns = m_LuaState.CallFunctionWithForeignParams(a_FunctionName, a_ForeignState, a_ParamStart, a_ParamEnd);
if (NumReturns < 0)
{
// The call has failed, an error has already been output to the log, so just silently bail out with the same error
return NumReturns;
}
// Copy all the return values:
int Top = lua_gettop(m_LuaState);
int res = a_ForeignState.CopyStackFrom(m_LuaState, Top - NumReturns + 1, Top);
// Remove the return values off this stack:
if (NumReturns > 0)
{
lua_pop(m_LuaState, NumReturns);
}
return res;
}
AString cPluginLua::HandleWebRequest(const HTTPRequest * a_Request )
{
cCSLock Lock(m_CriticalSection);

View File

@ -105,7 +105,7 @@ public:
virtual void ClearConsoleCommands(void) override;
/// Returns true if the plugin contains the function for the specified hook type, using the old-style registration (#121)
/** Returns true if the plugin contains the function for the specified hook type, using the old-style registration (#121) */
bool CanAddOldStyleHook(int a_HookType);
// cWebPlugin override
@ -115,26 +115,26 @@ public:
virtual AString HandleWebRequest(const HTTPRequest * a_Request ) override;
bool AddWebTab(const AString & a_Title, lua_State * a_LuaState, int a_FunctionReference); // >> EXPORTED IN MANUALBINDINGS <<
/// Binds the command to call the function specified by a Lua function reference. Simply adds to CommandMap.
/** Binds the command to call the function specified by a Lua function reference. Simply adds to CommandMap. */
void BindCommand(const AString & a_Command, int a_FnRef);
/// Binds the console command to call the function specified by a Lua function reference. Simply adds to CommandMap.
/** Binds the console command to call the function specified by a Lua function reference. Simply adds to CommandMap. */
void BindConsoleCommand(const AString & a_Command, int a_FnRef);
cLuaState & GetLuaState(void) { return m_LuaState; }
cCriticalSection & GetCriticalSection(void) { return m_CriticalSection; }
/// Removes a previously referenced object (luaL_unref())
/** Removes a previously referenced object (luaL_unref()) */
void Unreference(int a_LuaRef);
/// Calls the plugin-specified "cLuaWindow closing" callback. Returns true only if the callback returned true
/** Calls the plugin-specified "cLuaWindow closing" callback. Returns true only if the callback returned true */
bool CallbackWindowClosing(int a_FnRef, cWindow & a_Window, cPlayer & a_Player, bool a_CanRefuse);
/// Calls the plugin-specified "cLuaWindow slot changed" callback.
/** Calls the plugin-specified "cLuaWindow slot changed" callback. */
void CallbackWindowSlotChanged(int a_FnRef, cWindow & a_Window, int a_SlotNum);
/// Returns the name of Lua function that should handle the specified hook type in the older (#121) API
/** Returns the name of Lua function that should handle the specified hook type in the older (#121) API */
static const char * GetHookFnName(int a_HookType);
/** Adds a Lua function to be called for the specified hook.
@ -143,37 +143,47 @@ public:
*/
bool AddHookRef(int a_HookType, int a_FnRefIdx);
/** Calls a function in this plugin's LuaState with parameters copied over from a_ForeignState.
The values that the function returns are placed onto a_ForeignState.
Returns the number of values returned, if successful, or negative number on failure. */
int CallFunctionFromForeignState(
const AString & a_FunctionName,
cLuaState & a_ForeignState,
int a_ParamStart,
int a_ParamEnd
);
// The following templates allow calls to arbitrary Lua functions residing in the plugin:
/// Call a Lua function with 0 args
/** Call a Lua function with 0 args */
template <typename FnT> bool Call(FnT a_Fn)
{
cCSLock Lock(m_CriticalSection);
return m_LuaState.Call(a_Fn);
}
/// Call a Lua function with 1 arg
/** Call a Lua function with 1 arg */
template <typename FnT, typename ArgT0> bool Call(FnT a_Fn, ArgT0 a_Arg0)
{
cCSLock Lock(m_CriticalSection);
return m_LuaState.Call(a_Fn, a_Arg0);
}
/// Call a Lua function with 2 args
/** Call a Lua function with 2 args */
template <typename FnT, typename ArgT0, typename ArgT1> bool Call(FnT a_Fn, ArgT0 a_Arg0, ArgT1 a_Arg1)
{
cCSLock Lock(m_CriticalSection);
return m_LuaState.Call(a_Fn, a_Arg0, a_Arg1);
}
/// Call a Lua function with 3 args
/** Call a Lua function with 3 args */
template <typename FnT, typename ArgT0, typename ArgT1, typename ArgT2> bool Call(FnT a_Fn, ArgT0 a_Arg0, ArgT1 a_Arg1, ArgT2 a_Arg2)
{
cCSLock Lock(m_CriticalSection);
return m_LuaState.Call(a_Fn, a_Arg0, a_Arg1, a_Arg2);
}
/// Call a Lua function with 4 args
/** Call a Lua function with 4 args */
template <typename FnT, typename ArgT0, typename ArgT1, typename ArgT2, typename ArgT3> bool Call(FnT a_Fn, ArgT0 a_Arg0, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3)
{
cCSLock Lock(m_CriticalSection);
@ -181,13 +191,13 @@ public:
}
protected:
/// Maps command name into Lua function reference
/** Maps command name into Lua function reference */
typedef std::map<AString, int> CommandMap;
/// Provides an array of Lua function references
/** Provides an array of Lua function references */
typedef std::vector<cLuaState::cRef *> cLuaRefs;
/// Maps hook types into arrays of Lua function references to call for each hook type
/** Maps hook types into arrays of Lua function references to call for each hook type */
typedef std::map<int, cLuaRefs> cHookMap;
cCriticalSection m_CriticalSection;
@ -198,7 +208,7 @@ protected:
cHookMap m_HookMap;
/// Releases all Lua references and closes the LuaState
/** Releases all Lua references and closes the LuaState */
void Close(void);
} ; // tolua_export

View File

@ -1736,6 +1736,21 @@ bool cPluginManager::IsValidHookType(int a_HookType)
bool cPluginManager::DoWithPlugin(const AString & a_PluginName, cPluginCallback & a_Callback)
{
// TODO: Implement locking for plugins
PluginMap::iterator itr = m_Plugins.find(a_PluginName);
if (itr == m_Plugins.end())
{
return false;
}
return a_Callback.Item(itr->second);
}
bool cPluginManager::AddPlugin(cPlugin * a_Plugin)
{
m_Plugins[a_Plugin->GetDirectory()] = a_Plugin;

View File

@ -122,7 +122,7 @@ public: // tolua_export
} ;
// tolua_end
/// Used as a callback for enumerating bound commands
/** Used as a callback for enumerating bound commands */
class cCommandEnumCallback
{
public:
@ -132,7 +132,11 @@ public: // tolua_export
virtual bool Command(const AString & a_Command, const cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) = 0;
} ;
/// Returns the instance of the Plugin Manager (there is only ever one)
/** The interface used for enumerating and extern-calling plugins */
typedef cItemCallback<cPlugin> cPluginCallback;
/** Returns the instance of the Plugin Manager (there is only ever one) */
static cPluginManager * Get(void); // tolua_export
typedef std::map< AString, cPlugin * > PluginMap;
@ -143,7 +147,7 @@ public: // tolua_export
void FindPlugins(); // tolua_export
void ReloadPlugins(); // tolua_export
/// Adds the plugin to the list of plugins called for the specified hook type. Handles multiple adds as a single add
/** Adds the plugin to the list of plugins called for the specified hook type. Handles multiple adds as a single add */
void AddHook(cPlugin * a_Plugin, int a_HookType);
unsigned int GetNumPlugins() const; // tolua_export
@ -206,46 +210,46 @@ public: // tolua_export
bool DisablePlugin(const AString & a_PluginName); // tolua_export
bool LoadPlugin (const AString & a_PluginName); // tolua_export
/// Removes all hooks the specified plugin has registered
/** Removes all hooks the specified plugin has registered */
void RemoveHooks(cPlugin * a_Plugin);
/// Removes the plugin from the internal structures and deletes its object.
/** Removes the plugin from the internal structures and deletes its object. */
void RemovePlugin(cPlugin * a_Plugin);
/// Removes all command bindings that the specified plugin has made
/** Removes all command bindings that the specified plugin has made */
void RemovePluginCommands(cPlugin * a_Plugin);
/// Binds a command to the specified plugin. Returns true if successful, false if command already bound.
/** Binds a command to the specified plugin. Returns true if successful, false if command already bound. */
bool BindCommand(const AString & a_Command, cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString); // Exported in ManualBindings.cpp, without the a_Plugin param
/// Calls a_Callback for each bound command, returns true if all commands were enumerated
/** Calls a_Callback for each bound command, returns true if all commands were enumerated */
bool ForEachCommand(cCommandEnumCallback & a_Callback); // Exported in ManualBindings.cpp
/// Returns true if the command is in the command map
/** Returns true if the command is in the command map */
bool IsCommandBound(const AString & a_Command); // tolua_export
/// Returns the permission needed for the specified command; empty string if command not found
/** Returns the permission needed for the specified command; empty string if command not found */
AString GetCommandPermission(const AString & a_Command); // tolua_export
/// Executes the command, as if it was requested by a_Player. Checks permissions first. Returns true if executed.
/** Executes the command, as if it was requested by a_Player. Checks permissions first. Returns true if executed. */
bool ExecuteCommand(cPlayer * a_Player, const AString & a_Command); // tolua_export
/// Executes the command, as if it was requested by a_Player. Permisssions are not checked. Returns true if executed (false if not found)
/** Executes the command, as if it was requested by a_Player. Permisssions are not checked. Returns true if executed (false if not found) */
bool ForceExecuteCommand(cPlayer * a_Player, const AString & a_Command); // tolua_export
/// Removes all console command bindings that the specified plugin has made
/** Removes all console command bindings that the specified plugin has made */
void RemovePluginConsoleCommands(cPlugin * a_Plugin);
/// Binds a console command to the specified plugin. Returns true if successful, false if command already bound.
/** Binds a console command to the specified plugin. Returns true if successful, false if command already bound. */
bool BindConsoleCommand(const AString & a_Command, cPlugin * a_Plugin, const AString & a_HelpString); // Exported in ManualBindings.cpp, without the a_Plugin param
/// Calls a_Callback for each bound console command, returns true if all commands were enumerated
/** Calls a_Callback for each bound console command, returns true if all commands were enumerated */
bool ForEachConsoleCommand(cCommandEnumCallback & a_Callback); // Exported in ManualBindings.cpp
/// Returns true if the console command is in the command map
/** Returns true if the console command is in the command map */
bool IsConsoleCommandBound(const AString & a_Command); // tolua_export
/// Executes the command split into a_Split, as if it was given on the console. Returns true if executed. Output is sent to the a_Output callback
/** Executes the command split into a_Split, as if it was given on the console. Returns true if executed. Output is sent to the a_Output callback */
bool ExecuteConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output);
/** Appends all commands beginning with a_Text (case-insensitive) into a_Results.
@ -253,9 +257,13 @@ public: // tolua_export
*/
void TabCompleteCommand(const AString & a_Text, AStringVector & a_Results, cPlayer * a_Player);
/// Returns true if the specified hook type is within the allowed range
/** Returns true if the specified hook type is within the allowed range */
static bool IsValidHookType(int a_HookType);
/** Calls the specified callback with the plugin object of the specified plugin.
Returns false if plugin not found, and the value that the callback has returned otherwise. */
bool DoWithPlugin(const AString & a_PluginName, cPluginCallback & a_Callback);
private:
friend class cRoot;
@ -281,22 +289,22 @@ private:
cPluginManager();
virtual ~cPluginManager();
/// Reloads all plugins, defaulting to settings.ini for settings location
/** Reloads all plugins, defaulting to settings.ini for settings location */
void ReloadPluginsNow(void);
/// Reloads all plugins with a cIniFile object expected to be initialised to settings.ini
/** Reloads all plugins with a cIniFile object expected to be initialised to settings.ini */
void ReloadPluginsNow(cIniFile & a_SettingsIni);
/// Unloads all plugins
/** Unloads all plugins */
void UnloadPluginsNow(void);
/// Handles writing default plugins if 'Plugins' key not found using a cIniFile object expected to be intialised to settings.ini
/** Handles writing default plugins if 'Plugins' key not found using a cIniFile object expected to be intialised to settings.ini */
void InsertDefaultPlugins(cIniFile & a_SettingsIni);
/// Adds the plugin into the internal list of plugins and initializes it. If initialization fails, the plugin is removed again.
/** Adds the plugin into the internal list of plugins and initializes it. If initialization fails, the plugin is removed again. */
bool AddPlugin(cPlugin * a_Plugin);
/// Tries to match a_Command to the internal table of commands, if a match is found, the corresponding plugin is called. Returns true if the command is handled.
/** Tries to match a_Command to the internal table of commands, if a match is found, the corresponding plugin is called. Returns true if the command is handled. */
bool HandleCommand(cPlayer * a_Player, const AString & a_Command, bool a_ShouldCheckPermissions, bool & a_WasCommandForbidden);
bool HandleCommand(cPlayer * a_Player, const AString & a_Command, bool a_ShouldCheckPermissions)
{