Posted by
| Nick Gammon
Australia (23,120 posts) Bio
Forum Administrator |
Message
| Version 4.55 of MUSHclient offers an extended version of the CallPlugin syntax, for Lua-to-Lua calls only.
First, some background ...
Recently MUSHclient plugins have become more sophisticated, as authors have been implementing miniwindows, telnet negotiation, mappers, and caching. From time to time, plugin authors want to share information from one plugin to another.
Up until now, there have been two main methods of doing this:
- BroadcastPlugin
- CallPlugin
Both of these let plugins pass information from one to another. In the case of BroadcastPlugin a "broadcast" is made, which all plugins can pick up. In the case of CallPlugin, a specific plugin is targeted.
However neither call allows information to be returned to the caller, which makes them less useful for situations like "give me the current mob's name", or "look up a cached item on the database and give me its value".
Up until now, this has been achieved reasonably well by letting the called plugin set a variable, which the calling plugin can query, however this is a bit cumbersome. Twisol has developed a PPI module (Plugin-to-Plugin Interface) but even that suffers a bit from being a bit unwieldy, in that it has to still set plugin variables, albeit in the background.
The old CallPlugin syntax looked like this:
result_code = CallPlugin (plugin_id, function_name, argument)
(Where result_code was 0 for success, and the argument was a single string value).
For example:
result_code = CallPlugin ("80cc18937a2aca27079567f0", "LogIt", "Data to be logged")
The new syntax (which is backwards compatible with the old) looks like this:
result_code [ , val1, val2, val3 ... ] = CallPlugin (plugin_id, function_name, arg1 [, arg2, arg3, arg4 ... ] )
For example:
rc, a, b, c, d = CallPlugin ("80cc18937a2aca27079567f0", "show_message", "red", "green", "message", 42)
An example of the implementation of show_message in the target plugin would be:
function show_message (fore_colour, back_colour, message, count)
for i = 1, count do
ColourNote (fore_colour, back_colour, message)
end -- for
return 1, 2, 3, 4
end -- function show_message
The calling plugin would call it like this:
rc, a, b, c, d = CallPlugin ("80cc18937a2aca27079567f0", "show_message", "red", "green", "message", 42)
print ("rc =", rc) --> rc = 0
print (a, b, c, d) --> 1 2 3 4
The backwards compatibility exists because the first result from CallPlugin is still the return code, which is still 0 in the case of no error.
If the return code is in fact 0 (otherwise defined as error_code.eOK) then any values returned by the called function are then returned as additional return values (which Lua lets you do, unlike most languages). Also the case of sending a single string argument (which the old CallPlugin did) is now a special case of sending multiple arguments.
If the return code is not zero, then it will be one of the return codes defined for CallPlugin. In addition to that, a second value will be returned which is a string explanation of the error.
For example:
rc, a, b, c, d = CallPlugin ("80cc18937a2aca27079567f0", "foo", "bar")
print ("rc =", rc) --> rc = 30036
print (a, b, c, d) --> No function 'foo' in plugin 'Test_Plugin' (80cc18937a2aca27079567f0) nil nil nil
This lets you find out in more detail why a CallPlugin call failed.
In order for all this to work:
- Both the caller and called plugin must be written in Lua (the caller does not have to be a plugin)
- You can pass between zero and any number of arguments to the requested function
- The passed arguments can be one of the following types only:
- nil
- boolean
- number
- string
- In particular, the following types are not supported and will result in an error code being returned:
- function
- userdata
- thread
- table
Functions are specific to a Lua script space (for example, they may be implemented in a DLL) and are not portable across script spaces.
Userdata is likely to represent non-portable data, such as open files.
Threads (coroutines) will not be portable.
Tables are not only likely to be deep (eg, the entire _G table), plus are likely to contain functions, userdata and threads.
If you wish to pass a table (eg. a player's inventory) then this can be accomplished by using the serialize library to convert a table into a string.
- It is permissible to pass no arguments at all. eg.
CallPlugin ("80cc18937a2aca27079567f0", "my_func")
- It is possible for the caller and callee to be in the same plugin (however there is not much point to doing that). If you do that, the restrictions on the types of data that may be passed and returned do not apply.
The return code(s) from CallPlugin are:
On failure:
- A status code, as before, which will be non-zero
- A string indicating the reason in human-readable form
- In the case of a runtime error in the called function, a second string indicating the nature of the error
On success:
- A status code, as before, which will be the number zero
- Zero or more values as returned by the called function.
- The returned values can be one of the following types only:
- nil
- boolean
- number
- string
Thus it would be an error for the called function to return (for example) string.match as that is a function.
The reason for this is the same reason as the restrictions on the parameter passing - the parameters and returned values have to be copied from one Lua script space to another, and only certain data types lend themselves to such a copy operation.
Because of the backwards compatibility, no changes should be required to existing plugins or code. Also if you do not use Lua, then you can continue to use existing methods, such as setting plugin variables, or using world.Execute to cause aliases to fire in other plugins.
|
- Nick Gammon
www.gammon.com.au, www.mushclient.com | Top |
|