1
0
Fork 0

LuaState: Inter-plugin calls now support simple tables. (#3220)

This commit is contained in:
Mattes D 2016-05-31 01:01:55 +02:00 committed by worktycho
parent bbcf2d6bd6
commit 5618e453e6
5 changed files with 207 additions and 58 deletions

View File

@ -65,7 +65,7 @@ cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChatMessage);
{ Params = "Command, Callback, HelpString", Return = "[bool]", 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. Returns true if successful, logs to console and returns no value on error. The callback uses the following signature: <pre class=\"prettyprint lang-lua\">function(Split)</pre> The Split parameter contains an array-table of the words that the admin has typed. If the callback returns true, the command is assumed to have executed successfully; in all other cases the server issues a warning to the console that the command is unknown (this is so that subcommands can be implemented)." },
{ Params = "Command, Callback, HelpString", Return = "[bool]", 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. Returns true if successful, logs to console and returns no value on error. The callback uses the following signature: <pre class=\"prettyprint lang-lua\">function(Split)</pre> The Split parameter contains an array-table of the words that the admin has typed. If the callback returns true, the command is assumed to have executed successfully; in all other cases the server issues a warning to the console that the command is unknown (this is so that subcommands can be implemented)." },
},
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." },
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, API classes and simple tables can be used for parameters and return values; functions cannot be copied across plugins." },
DoWithPlugin = { Params = "PluginName, CallbackFn", Return = "bool", Notes = "(STATIC) Calls the CallbackFn for the specified plugin, if found. A plugin can be found even if it is currently unloaded, disabled or errored, the callback should check the plugin status. If the plugin is not found, this function returns false, otherwise it returns the bool value that the callback has returned. The CallbackFn has the following signature: <pre class=\"prettyprint lang-lua\">function ({{cPlugin|Plugin}})</pre>" },
ExecuteCommand = { Params = "{{cPlayer|Player}}, CommandStr", Return = "{{cPluginManager#CommandResult|CommandResult}}", Notes = "Executes the command as if given by the specified Player. Checks permissions." },
ExecuteConsoleCommand = { Params = "CommandStr", Return = "bool, string", Notes = "Executes the console command as if given by the admin on the console. If the command is successfully executed, returns true and the text that would be output to the console normally. On error it returns false and an error message." },

View File

@ -1921,6 +1921,63 @@ end
function HandleConsoleTestCall(a_Split, a_EntireCmd)
LOG("Testing inter-plugin calls")
LOG("Note: These will fail if the Core plugin is not enabled")
-- Test calling the HandleConsoleWeather handler:
local pm = cPluginManager
LOG("Calling Core's HandleConsoleWeather")
local isSuccess = pm:CallPlugin("Core", "HandleConsoleWeather",
{
"/weather",
"rain",
}
)
if (type(isSuccess) == "boolean") then
LOG("Success")
else
LOG("FAILED")
end
-- Test injecting some code:
LOG("Injecting code into the Core plugin")
isSuccess = pm:CallPlugin("Core", "dofile", pm:GetCurrentPlugin():GetLocalFolder() .. "/Inject.lua")
if (type(isSuccess) == "boolean") then
LOG("Success")
else
LOG("FAILED")
end
-- Test the full capabilities of the table-passing API, using the injected function:
LOG("Calling injected code")
isSuccess = pm:CallPlugin("Core", "injectedPrintParams",
{
"test",
nil,
{
"test",
"test"
},
[10] = "test",
["test"] = "test",
[{"test"}] = "test",
[true] = "test",
}
)
if (type(isSuccess) == "boolean") then
LOG("Success")
else
LOG("FAILED")
end
return true
end
function HandleConsoleTestJson(a_Split, a_EntireCmd)
LOG("Testing Json parsing...")
local t1 = cJson:Parse([[{"a": 1, "b": "2", "c": [3, "4", 5] }]])

View File

@ -254,6 +254,12 @@ g_PluginInfo =
HelpString = "Tests the world scheduling",
},
["testcall"] =
{
Handler = HandleConsoleTestCall,
HelpString = "Tests inter-plugin calls with various values"
},
["testjson"] =
{
Handler = HandleConsoleTestJson,

View File

@ -1470,7 +1470,7 @@ int cLuaState::CallFunctionWithForeignParams(
int cLuaState::CopyStackFrom(cLuaState & a_SrcLuaState, int a_SrcStart, int a_SrcEnd)
int cLuaState::CopyStackFrom(cLuaState & a_SrcLuaState, int a_SrcStart, int a_SrcEnd, int a_NumAllowedNestingLevels)
{
/*
// DEBUG:
@ -1480,61 +1480,10 @@ int cLuaState::CopyStackFrom(cLuaState & a_SrcLuaState, int a_SrcStart, int a_Sr
*/
for (int i = a_SrcStart; i <= a_SrcEnd; ++i)
{
int t = lua_type(a_SrcLuaState, i);
switch (t)
if (!CopySingleValueFrom(a_SrcLuaState, i, a_NumAllowedNestingLevels))
{
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 = nullptr;
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, nullptr);
tolua_pushusertype(m_LuaState, ud, type);
break;
}
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;
}
lua_pop(m_LuaState, i - a_SrcStart);
return -1;
}
}
return a_SrcEnd - a_SrcStart + 1;
@ -1544,6 +1493,131 @@ int cLuaState::CopyStackFrom(cLuaState & a_SrcLuaState, int a_SrcStart, int a_Sr
bool cLuaState::CopyTableFrom(cLuaState & a_SrcLuaState, int a_SrcStackIdx, int a_NumAllowedNestingLevels)
{
// Create the dest table:
#ifdef _DEBUG
auto srcTop = lua_gettop(a_SrcLuaState);
auto dstTop = lua_gettop(m_LuaState);
#endif
lua_createtable(m_LuaState, 0, 0); // DST: <table>
lua_pushvalue(a_SrcLuaState, a_SrcStackIdx); // SRC: <table>
lua_pushnil(a_SrcLuaState); // SRC: <table> <key>
while (lua_next(a_SrcLuaState, -2) != 0) // SRC: <table> <key> <value>
{
assert(lua_gettop(a_SrcLuaState) == srcTop + 3);
assert(lua_gettop(m_LuaState) == dstTop + 1);
// Copy the key:
if (!CopySingleValueFrom(a_SrcLuaState, -2, a_NumAllowedNestingLevels)) // DST: <table> <key>
{
lua_pop(m_LuaState, 1);
lua_pop(a_SrcLuaState, 3);
assert(lua_gettop(a_SrcLuaState) == srcTop);
assert(lua_gettop(m_LuaState) == dstTop);
return false;
}
assert(lua_gettop(a_SrcLuaState) == srcTop + 3);
assert(lua_gettop(m_LuaState) == dstTop + 2);
// Copy the value:
if (!CopySingleValueFrom(a_SrcLuaState, -1, a_NumAllowedNestingLevels - 1)) // DST: <table> <key> <value>
{
lua_pop(m_LuaState, 2); // DST: empty
lua_pop(a_SrcLuaState, 3); // SRC: empty
assert(lua_gettop(a_SrcLuaState) == srcTop);
assert(lua_gettop(m_LuaState) == dstTop);
return false;
}
assert(lua_gettop(a_SrcLuaState) == srcTop + 3);
assert(lua_gettop(m_LuaState) == dstTop + 3);
// Set the value and fix up stacks:
lua_rawset(m_LuaState, -3); // DST: <table>
lua_pop(a_SrcLuaState, 1); // SRC: <table> <key>
assert(lua_gettop(a_SrcLuaState) == srcTop + 2);
assert(lua_gettop(m_LuaState) == dstTop + 1);
}
lua_pop(a_SrcLuaState, 1); // SRC: empty
assert(lua_gettop(a_SrcLuaState) == srcTop);
assert(lua_gettop(m_LuaState) == dstTop + 1);
return true;
}
bool cLuaState::CopySingleValueFrom(cLuaState & a_SrcLuaState, int a_StackIdx, int a_NumAllowedNestingLevels)
{
int t = lua_type(a_SrcLuaState, a_StackIdx);
switch (t)
{
case LUA_TNIL:
{
lua_pushnil(m_LuaState);
return true;
}
case LUA_TSTRING:
{
AString s;
a_SrcLuaState.ToString(a_StackIdx, s);
Push(s);
return true;
}
case LUA_TBOOLEAN:
{
bool b = (tolua_toboolean(a_SrcLuaState, a_StackIdx, false) != 0);
Push(b);
return true;
}
case LUA_TNUMBER:
{
lua_Number d = tolua_tonumber(a_SrcLuaState, a_StackIdx, 0);
Push(d);
return true;
}
case LUA_TUSERDATA:
{
// Get the class name:
const char * type = nullptr;
if (lua_getmetatable(a_SrcLuaState, a_StackIdx) == 0)
{
LOGWARNING("%s: Unknown class in pos %d, cannot copy.", __FUNCTION__, a_StackIdx);
return false;
}
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, a_StackIdx, nullptr);
tolua_pushusertype(m_LuaState, ud, type);
return true;
}
case LUA_TTABLE:
{
if (!CopyTableFrom(a_SrcLuaState, a_StackIdx, a_NumAllowedNestingLevels - 1))
{
LOGWARNING("%s: Failed to copy table in pos %d.", __FUNCTION__, a_StackIdx);
return false;
}
return true;
}
default:
{
LOGWARNING("%s: Unsupported value: '%s' at stack position %d. Can only copy numbers, strings, bools, classes and simple tables!",
__FUNCTION__, lua_typename(a_SrcLuaState, t), a_StackIdx
);
return false;
}
}
}
void cLuaState::ToString(int a_StackPos, AString & a_String)
{
size_t len;

View File

@ -419,10 +419,22 @@ public:
);
/** Copies objects on the stack from the specified state.
Only numbers, bools, strings and userdatas are copied.
Only numbers, bools, strings, API classes and simple tables containing these (recursively) are copied.
a_NumAllowedNestingLevels specifies how many table nesting levels are allowed, copying fails if there's a deeper table.
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);
int CopyStackFrom(cLuaState & a_SrcLuaState, int a_SrcStart, int a_SrcEnd, int a_NumAllowedNestingLevels = 16);
/** Copies a table at the specified stack index of the source Lua state to the top of this Lua state's stack.
a_NumAllowedNestingLevels specifies how many table nesting levels are allowed, copying fails if there's a deeper table.
Returns true if successful, false on failure.
Can copy only simple values - numbers, bools, strings and recursively simple tables. */
bool CopyTableFrom(cLuaState & a_SrcLuaState, int a_TableIdx, int a_NumAllowedNestingLevels);
/** Copies a single value from the specified stack index of the source Lua state to the top of this Lua state's stack.
a_NumAllowedNestingLevels specifies how many table nesting levels are allowed, copying fails if there's a deeper table.
Returns true if the value was copied, false on failure. */
bool CopySingleValueFrom(cLuaState & a_SrcLuaState, int a_StackIdx, int a_NumAllowedNestingLevels);
/** 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);