[Home] [Downloads] [Search] [Help/forum]


Register forum user name Search FAQ

Gammon Forum

[Folder]  Entire forum
-> [Folder]  MUSHclient
. -> [Folder]  Tips and tricks
. . -> [Subject]  PPI - A plugin communications script

PPI - A plugin communications script

It is now over 60 days since the last post. This thread is closed.     [Refresh] Refresh page


Pages: 1 2  3  4  5  6  7  8  9  10  

Posted by Twisol   USA  (2,257 posts)  [Biography] bio
Date Sun 03 Jan 2010 06:54 AM (UTC)

Amended on Sun 03 Jan 2010 07:01 AM (UTC) by Twisol

Message
With the addition of better plugin dependency support, I decided to write a new version of LoadPPI from scratch. This version no longer has the load-on-demand functionality (because it's no longer needed and was always rather hacky), and uses a combination of CallPlugin() and MUSHclient variables to execute a method in another plugin. It also makes it so that the public interface of a plugin - the methods that the plugin wants to expose through the PPI library - doesn't have to be duplicated for every supported language, because this version doesn't load the code and execute it directly. (The PPI library itself would need to be ported for each language, but that's all.)

Hopefully some of you find this script useful. Here's an example, and at the end is the source for ppi.lua itself.

Plugin 1 (client)
local PPI = require("ppi")
OnPluginListChanged = function()
  local ppi = PPI.Load("7c08e2961c5e20e5bdbf7fc5")
  if not ppi then
    error("Dependency plugin not installed!")
  end
  
  ppi.SomeMethodHere(parameters, go, here)
end


Plugin 2 (service)
local PPI = require("ppi")

local function SomeMethodHere(parameters, go, here)
  print(parameters, go, here)
end

PPI.Expose("SomeMethodHere", SomeMethodHere)


The PPI library itself manages the intermediate steps, though at the moment it only supports strings parameters directly. It would be nice to modify it to support numbers, booleans, and tables in some manner, but it's just strings for the moment.

As promised, here is the source for ppi.lua. I wrote it in a couple hours, so it's probably not as well written as I'd like, but there it is.
local PPI_list = setmetatable({}, {__mode = "k"})

-- Takes a table and returns all values at numeric indices.
-- Example:
--  Input: {1, 2, 3, "5", "7", "10"}
--  Returns: 1, 2, 3, "5", "7", "10"
local function ExplodeParams(tbl)
  local param = table.remove(tbl, 1)
  if #tbl > 0 then
    return param, ExplodeParams(tbl)
  else
    return param
  end
end

-- A 'thunk' is a delayed resolver function.
local function new_thunk(id, func_name)
  return function(...)
    local params = {...}
    
    -- Prepare the arguments
    for i=1,#params do
      SetVariable("param" .. i .. "_" .. id, tostring(params[i]))
    end
    
    -- Call the method
    SetVariable("method_" .. id, func_name)
    CallPlugin(id, "PPI_" .. id .. "_PPI", GetPluginID())
    
    -- Clean up the arguments
    for i=1,#params do
      DeleteVariable("param" .. i .. "_" .. id)
    end
    
    -- Gather the return values
    local returns = {}
    local i = 1
    while GetPluginVariable(id, "return" .. i .. "_" .. GetPluginID()) ~= nil do
      table.insert(returns, GetPluginVariable(id, "return" .. i .. "_" .. GetPluginID()))
      i = i + 1
    end
    
    -- Have the other plugin clean up the return values
    CallPlugin(id, "PPI_" .. id .. "_PPI_CLEAN", GetPluginID())
    
    return ExplodeParams(returns)
  end
end

-- If the requested function hasn't yet had a thunk created,
-- create a new thunk and return it.
local PPI_meta = {
  __index = function(tbl, idx)
    local thunk = new_thunk(PPI_list[tbl].id, idx)
    tbl[idx] = thunk
    return thunk
  end,
}

local PPI = {
  -- Used to retreive a PPI for a specified plugin.
  Load = function(plugin_id)
    if not IsPluginInstalled(plugin_id) then
      return false
    end
    
    local tbl = PPI_list[plugin_id]
    if not tbl then
      tbl = setmetatable({}, PPI_meta)
      PPI_list[tbl] = {id = plugin_id}
      PPI_list[plugin_id] = tbl
    end
    return tbl
  end,
  
  -- Used by a plugin to expose methods to other plugins
  -- through its own PPI.
  Expose = function(name, func)
    local myPPI = PPI_list[GetPluginID()]
    myPPI[name] = func
  end,
}

-- create a PPI for this plugin
myPPI = {}
PPI_list[myPPI] = {id = GetPluginID()}
PPI_list[GetPluginID()] = myPPI

-- PPI request resolver
_G["PPI_" .. GetPluginID() .. "_PPI"] = function(id)
  local myPPI = PPI_list[GetPluginID()]
  local myID = PPI_list[myPPI].id
  if not myPPI then
    return
  end
  
  local params = {}
  local i = 1
  while GetPluginVariable(id, "param" .. i .. "_" .. myID) ~= nil do
    table.insert(params, GetPluginVariable(id, "param" .. i .. "_" .. myID))
    i = i + 1
  end
  
  local func_name = GetPluginVariable(id, "method_" .. myID)
  local func = myPPI[func_name]
  if not func then
    return
  end
  
  local returns = {func(ExplodeParams(params))}
  for i=1, #returns do
    SetVariable("return" .. i .. "_" .. id, tostring(returns[i]))
  end
end

-- Return value cleaner
_G["PPI_" .. GetPluginID() .. "_PPI_CLEAN"] = function(id)
  local i = 1
  while GetVariable("return" .. i .. "_" .. id) ~= nil do
    DeleteVariable("return" .. i .. "_" .. id)
    i = i + 1
  end
end

return PPI

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
[Go to top] top

Posted by Nick Gammon   Australia  (22,973 posts)  [Biography] bio   Forum Administrator
Date Reply #1 on Thu 07 Jan 2010 10:28 PM (UTC)
Message
I'm starting to play with this. I think you can delete these lines:


-- Takes a table and returns all values at numeric indices.
-- Example:
--  Input: {1, 2, 3, "5", "7", "10"}
--  Returns: 1, 2, 3, "5", "7", "10"
local function ExplodeParams(tbl)
  local param = table.remove(tbl, 1)
  if #tbl > 0 then
    return param, ExplodeParams(tbl)
  else
    return param
  end
end


Replace calls to ExplodeParams by a call to unpack. That seems to work.

http://www.gammon.com.au/scripts/doc.php?lua=unpack

You can also simplify the Expose function by looking up the function yourself, like this:


 -- Used by a plugin to expose methods to other plugins
  -- through its own PPI.
  Expose = function (name)
    assert (type (_G [name]) == "function", "Function " .. name .. " does not exist.")
    PPI_list[GetPluginID()] [name] = _G [name]
  end,


That way instead of:


PPI.Expose("SomeMethodHere", SomeMethodHere)


you can use:


PPI.Expose "SomeMethodHere"


However "SomeMethodHere" now has to be a global function and not a local one. However that saves duplication of typing, and possibly mistyping the function name.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Nick Gammon   Australia  (22,973 posts)  [Biography] bio   Forum Administrator
Date Reply #2 on Thu 07 Jan 2010 11:22 PM (UTC)
Message
I offer this up as an enhanced version of your PPI system. Basically it improves upon it by keeping the types of the supplied parameters, and the returned values.


module (..., package.seeall)

require "serialize"

local PPI_list = {}

-- A 'thunk' is a delayed resolver function.
local function new_thunk(id, func_name)
  return function(...)
    
    -- Prepare the arguments
    SetVariable("params_" .. id, serialize.save_simple {...})
    
    -- Call the method
    SetVariable("method_" .. id, func_name)
    CallPlugin(id, "PPI_" .. id .. "_PPI", GetPluginID())
    
    -- Clean up the arguments
    DeleteVariable("params_" .. id)
    
    local returns = {}  -- table of returned values
    local r = GetPluginVariable(id, "returns_" .. GetPluginID())
    if r then 
      local f = assert (loadstring ("r = " .. r))  -- convert serialized data back
      setfenv (f, returns)  -- make sure we use the returns table as our destination
      f ()   -- put data back in table "r"
    end -- if
    
    -- Have the other plugin clean up the return values
    CallPlugin(id, "PPI_" .. id .. "_PPI_CLEAN", GetPluginID())
    
    return unpack (returns.r)
  end
end

-- If the requested function hasn't yet had a thunk created,
-- create a new thunk and return it.
local PPI_meta = {
  __index = function(tbl, idx)
    local thunk = new_thunk(PPI_list[tbl].id, idx)
    tbl[idx] = thunk
    return thunk
  end,
}

local PPI = {
  -- Used to retreive a PPI for a specified plugin.
  Load = function(plugin_id)
    if not IsPluginInstalled(plugin_id) then
      return false
    end
    
    local tbl = PPI_list[plugin_id]
    if not tbl then
      tbl = setmetatable({}, PPI_meta)
      PPI_list[tbl] = {id = plugin_id}
      PPI_list[plugin_id] = tbl
    end
    return tbl
  end,
  
  -- Used by a plugin to expose methods to other plugins
  -- through its own PPI.
  Expose = function (name)
    assert (type (_G [name]) == "function", "Function " .. name .. " does not exist.")
    PPI_list[GetPluginID()] [name] = _G [name]
  end,
}

-- create a PPI for this plugin
myPPI = {}
PPI_list[myPPI] = {id = GetPluginID()}
PPI_list[GetPluginID()] = myPPI

-- PPI request resolver
_G["PPI_" .. GetPluginID() .. "_PPI"] = function(id)
  local myPPI = PPI_list[GetPluginID()]
  local myID = PPI_list[myPPI].id
  if not myPPI then
    return
  end
  
  local params = {}  -- table of parameters
  local p = GetPluginVariable(id, "params_" .. myID)
  if p then 
    local f = assert (loadstring ("t = " .. p))  -- convert serialized data back
    setfenv (f, params)  -- make sure we use the params table as our destination
    f ()   -- put data back in table "t"
  end -- if
    
  local func_name = GetPluginVariable(id, "method_" .. myID)
  local func = myPPI[func_name]
  if not func then
    return
  end
  
  -- call target function, get return values, serialize back into variable
  SetVariable("returns_" .. id, serialize.save_simple {func(unpack (params.t))})

end -- PPI request resolver

-- Return value cleaner
_G["PPI_" .. GetPluginID() .. "_PPI_CLEAN"] = function(id)
  DeleteVariable("returns_" .. id)
end

return PPI


For example, my service is:


local PPI = require("ppi")

require "tprint"

function SomeMethodHere (...)
  tprint {...}  
  
  return 1, "x", false, { y = 88 }
end

PPI.Expose "SomeMethodHere"


And the client is:


local PPI = require("ppi")
OnPluginListChanged = function()
  ppi = PPI.Load("15783160bde378741f9652d1")
  if not ppi then
    error("Dependency plugin not installed!")
  end
  tprint {ppi.SomeMethodHere (42, "Nick", true, {a = "56"} )}
end


Notice that the client sends various types to the service (number, string, boolean, table) and the service replies with number, string, boolean, table.



Client received:

1=42
2="Nick"
3=true
4:
  "a"="56"

Service replied:

1=1
2="x"
3=false
4:
  "y"=88


Note how the service received the correct types, and the client received back the correct types.

This version is slightly simpler, if anything, because of instead of lots of MUSHclient variables being set up, a single variable is created with serialize.save_simple.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Nick Gammon   Australia  (22,973 posts)  [Biography] bio   Forum Administrator
Date Reply #3 on Thu 07 Jan 2010 11:26 PM (UTC)
Message
Since you can send tables, you could call a function in another plugin and send stuff like style runs, for example, or an inventory list.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Nick Gammon   Australia  (22,973 posts)  [Biography] bio   Forum Administrator
Date Reply #4 on Thu 07 Jan 2010 11:34 PM (UTC)
Message
I don't know about using the weak keys, either. You may find that your table gets garbage-collected when you least expect it.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Twisol   USA  (2,257 posts)  [Biography] bio
Date Reply #5 on Thu 07 Jan 2010 11:44 PM (UTC)

Amended on Thu 07 Jan 2010 11:45 PM (UTC) by Twisol

Message
Briefly, because I'm still reading through it, I want to mention that one of the key reasons I moved to CallPlugin() was because then only the PPI library would need to be ported between languages. Using the 'serialize' library would bind it to Lua, or require compatible serialization libraries in other languages.

I was planning on writing a PPI-specific notation that would be attached to each parameter, something like "n|100", "s|a string", "b|0" (boolean), and for tables I wasn't quite sure, but I was probably going to use the Array* methods and convert the table into a string, like "t|the array stuff here".


About the weak keys, you're probably right.


Also, I really appreciate that you took the time to play around with it. :)

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
[Go to top] top

Posted by Twisol   USA  (2,257 posts)  [Biography] bio
Date Reply #6 on Thu 07 Jan 2010 11:54 PM (UTC)
Message
As a side note, I somewhat preferred the two-argument version of Expose, as you could map a function to a string with a different name, or inline the function right there:

PPI.Expose("MyMethod", function()
    print("Hallos.")
  end
)


Basically it's just extra flexibility. It probably shouldn't be too hard to write a version that accepts either one or two arguments, though.

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
[Go to top] top

Posted by Nick Gammon   Australia  (22,973 posts)  [Biography] bio   Forum Administrator
Date Reply #7 on Fri 08 Jan 2010 01:10 AM (UTC)
Message
Twisol said:

Briefly, because I'm still reading through it, I want to mention that one of the key reasons I moved to CallPlugin() was because then only the PPI library would need to be ported between languages. Using the 'serialize' library would bind it to Lua, or require compatible serialization libraries in other languages.


OK, well as it stands, the module requires to be used from Lua, so using the Lua serialization makes it more powerful for that purpose.

If someone did (say) a VBscript version then they could handle the different data types differently, but for a pure Lua application, I think it is easier and more natural than having to pass down data types in the way you describe.

I would suggest that modern developers are probably going to use Lua, and if so they might have a helper plugin (also in Lua) and communicate with it using your module. However if they insist on using something else, then they can take your broad ideas and make something similar for their own language.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Twisol   USA  (2,257 posts)  [Biography] bio
Date Reply #8 on Fri 08 Jan 2010 01:17 AM (UTC)

Amended on Fri 08 Jan 2010 01:21 AM (UTC) by Twisol

Message
It's not really so difficult. I'm working on an improved version currently (I did incorporate many/all of your suggestions), and it's coming along fairly well. At this stage, it's using the Array*() functions to load all of the parameters into, then exporting it into a single MUSH variable. I have a promising plan to extend this to table parameters as well (including nested tables), by giving each table its own MUSH variable, and using "t|2" for the table-type in the array, where 2 is (part of) the ID of the MUSH variable containing that table's definition.

It's not much trouble, and I enjoy this kind of critical thinking anyways. I hope to post a mid-way version soon.

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
[Go to top] top

Posted by Twisol   USA  (2,257 posts)  [Biography] bio
Date Reply #9 on Fri 08 Jan 2010 01:45 AM (UTC)
Message
Alright, here's a midway version, switched up to use arrays for serialization. It still restricts parameters to strings, but that should change soon.

It also supports both one-arg and two-args versions of PPI.Expose. If the second argument is missing, it simply uses _G[first argument].

local PPI_list = {}

local myID = GetPluginID()
local myPPI = {}

local params_id  = function(id) return "PPIparams_" .. id  end
local method_id  = function(id) return "PPImethod_" .. id  end
local returns_id = function(id) return "PPIreturns_" .. id end

local request_id = function(id) return "PPI_" .. id .. "_REQUEST" end
local cleanup_id = function(id) return "PPI_" .. id .. "_CLEANUP" end


local function request(id, func_name, ...)
  -- Prepare the arguments
  local params = {...}
  
  ArrayCreate(params_id(id))
  
  for i=1,#params do
    ArraySet(params_id(id), tostring(i), tostring(params[i]))
  end

  SetVariable(params_id(id), ArrayExport(params_id(id), "|"))
  ArrayDelete(params_id(id))
  
  -- Call the method
  SetVariable(method_id(id), func_name)
  CallPlugin(id, request_id(id), myID)
  
  -- Clean up the arguments
  DeleteVariable(params_id(id))
  DeleteVariable(method_id(id))
  
  -- Gather the return values
  local returns = {}
  
  ArrayCreate(returns_id(id))
  ArrayImport(returns_id(id), GetPluginVariable(id, returns_id(myID)), "|")
  
  for k,v in pairs(ArrayList(returns_id(id))) do
    returns[tonumber(k)] = v
  end
  ArrayDelete(returns_id(id))
  
  -- Have the other plugin clean up its return values
  CallPlugin(id, cleanup_id(id), myID)
  
  return unpack(returns)
end

-- A 'thunk' is a delayed resolver function.
local function new_thunk(id, func_name)
  return function(...)
    return request(id, func_name, ...)
  end
end

-- If the requested function hasn't yet had a thunk created,
-- create a new thunk and return it.
local PPI_meta = {
  __index = function(tbl, idx)
    local thunk = new_thunk(PPI_list[tbl].id, idx)
    tbl[idx] = thunk
    return thunk
  end,
}

local PPI = {
  -- Used to retreive a PPI for a specified plugin.
  Load = function(plugin_id)
    if not IsPluginInstalled(plugin_id) then
      return false
    end
    
    local tbl = PPI_list[plugin_id]
    if not tbl then
      tbl = setmetatable({}, PPI_meta)
      PPI_list[tbl] = {id = plugin_id}
      PPI_list[plugin_id] = tbl
    end
    return tbl
  end,
  
  -- Used by a plugin to expose methods to other plugins
  -- through its own PPI.
  Expose = function(name, func)
    myPPI[name] = (func and func or _G[name])
  end,
}

-- PPI request resolver
_G[request_id(myID)] = function(id)
  ArrayCreate(params_id(id))
  ArrayImport(params_id(id), GetPluginVariable(id, params_id(myID)), "|")
  
  local params = {}
  for k,v in pairs(ArrayList(params_id(id))) do
    params[tonumber(k)] = v
  end
  ArrayDelete(params_id(id))
  
  local func_name = GetPluginVariable(id, method_id(myID))
  local func = myPPI[func_name]
  if not func then
    return
  end
  
  local returns = {func(unpack(params))}
  ArrayCreate(returns_id(id))
  
  for i=1,#returns do
    ArraySet(returns_id(id), tostring(i), tostring(returns[i]))
  end

  SetVariable(returns_id(id), ArrayExport(returns_id(id), "|"))
  ArrayDelete(returns_id(id))
end

-- Return value cleaner
_G[cleanup_id(myID)] = function(id)
  DeleteVariable(returns_id(id))
end

return PPI

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
[Go to top] top

Posted by Twisol   USA  (2,257 posts)  [Biography] bio
Date Reply #10 on Fri 08 Jan 2010 01:57 AM (UTC)
Message
Once 4.46 is released, I will probably build support in for checking a plugin's nonce value [1], so that the whole process is fairly automatic.

[1] http://www.gammon.com.au/forum/?id=9969&page=999

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
[Go to top] top

Posted by Nick Gammon   Australia  (22,973 posts)  [Biography] bio   Forum Administrator
Date Reply #11 on Fri 08 Jan 2010 02:59 AM (UTC)
Message
While you were doing that I did another version myself. ;)

This one removes your PPI_list table, which I couldn't really see the purpose of. It is somewhat shorter as it effectively just uses an upvalue to store the only thing you really need to remember, which is the function associated with the name.


--[[

Author:  	Twisol 
Date:  3rd January 2010

Amendments: Nick Gammon
Date: 8th January 2010

Example of use:

SERVICE

-- require PPI module
require "ppi"

-- exposed function
function SomeMethodHere (a, b, c, d)
  -- do something with a, b, c, d
  return 1, 2, 3, 4
end

-- notify PPI of this function
ppi.Expose "SomeMethodHere"


CLIENT

-- require PPI module
require "ppi"

-- resolve dependencies
function OnPluginListChanged ()
  
  -- get PPI entries for all exposed function in this plugin
  my_service = ppi.Load ("15783160bde378741f9652d1")  -- plugin ID of service plugin

  if not my_service then
    Note ("Dependency plugin not installed!")
  end
  
end -- OnPluginListChanged

-- later on in plugin ...

-- call SomeMethodHere in other plugin, passing various data types, getting results

if my_service then
  w, x, y, z = my_service.SomeMethodHere (42, "Nick", true, { a = 63, b = 22 } )
end -- if service installed

--]]

module (..., package.seeall)

require "serialize"

-- For any function in our PPI table, try to call that in the target plugin
local PPI_meta = {
  __index = function(tbl, idx)
    return function(...)
       
        -- Call the method in the target plugin
        local status = CallPlugin (tbl._id, "PPI_" .. idx .. "_PPI_", serialize.save_simple {...})
        
        -- explain a bit if we failed
        if status ~= error_code.eOK then
          ColourNote ("white", "red", "Error calling " .. idx .. " using PPI from " .. GetPluginName ())
          check (status)
        end -- if
        
        local returns = {}  -- table of returned values
        local r = GetPluginVariable(tbl._id, "PPI_returns_PPI_") or "{}"
        local f = assert (loadstring ("r = " .. r))  -- convert serialized data back
        setfenv (f, returns) () -- load the returned values into 'returns'
        
        return unpack (returns.r)
      end  -- generated function
    end,
}  -- end PPI_meta table

-- PPI request resolver
local function PPI_resolver (func) 
  return function (p)
    local params = {}  -- table of parameters
    local f = assert (loadstring ("t = " .. p))  -- convert serialized data back
    setfenv (f, params) ()  -- load the parameters into 'params'
    
    -- call target function, get return values, serialize back into variable
    SetVariable("PPI_returns_PPI_", serialize.save_simple {func(unpack (params.t))})
  end -- generated function
  
end -- PPI_resolver

-- EXPOSED FUNCTIONS

-- We "load" a plugin by checking it exists, and creating a table saving the
-- target plugin ID, and have a metatable which will handle function calls
function Load (plugin_id)
  if IsPluginInstalled (plugin_id) then
    return setmetatable ({ _id = plugin_id }, PPI_meta)
  end -- if plugin exists
end -- function Load 

-- Used by a plugin to expose methods to other plugins
-- Each exposed function will be added to global namespace as PPI_<name>_PPI_
function Expose (name, func)
  assert (type (func or _G [name]) == "function", "Function " .. name .. " does not exist.")
  _G ["PPI_" .. name .. "_PPI_"] = PPI_resolver (func or _G [name])
end -- function Expose


- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Nick Gammon   Australia  (22,973 posts)  [Biography] bio   Forum Administrator
Date Reply #12 on Fri 08 Jan 2010 03:41 AM (UTC)
Message
I should point out that testing shows that this error becomes unrecoverable, without reinstalling *this* plugin:


OnPluginListChanged = function()
  local ppi = PPI.Load("7c08e2961c5e20e5bdbf7fc5")
  if not ppi then
    error("Dependency plugin not installed!")
  end
  
  ppi.SomeMethodHere(parameters, go, here)
end


The error raised in OnPluginListChanged makes MUSHclient flag OnPluginListChanged as "a module in error" and no longer calls it, even if the plugin list changes again.

Thus, reinstalling (or installing for the first time) the missing plugin, is not sufficient to recover.

You may want to downgrade that to a warning (eg. Note instead of error), so that if the other plugin is subsequently installed, OnPluginListChanged will be called again to do the PPI.Load.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Twisol   USA  (2,257 posts)  [Biography] bio
Date Reply #13 on Fri 08 Jan 2010 03:47 AM (UTC)

Amended on Fri 08 Jan 2010 03:48 AM (UTC) by Twisol

Message
(Currently working on table serialization; the other three datatypes work beautifully)

On ppi_list: Well, it does provide a good place to store "private" values like the plugin's ID, as well as the current 'nonce' for the PPI in order to implement the nonce-checking I mentioned before.

On Note() versus error(): It depends on the plugin, really. If it will work acceptably even if the dependency isn't there, sure, a Note() is probably better. But if you absolutely don't want the plugin doing anything at all if the dependency isn't loaded, if things such as collecting information or other stuff are pointless and wasteful if the dependency isn't there, then I would say an error() is the way to go. You can always have the error say to reinstall the plugin, after all.

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
[Go to top] top

Posted by Twisol   USA  (2,257 posts)  [Biography] bio
Date Reply #14 on Fri 08 Jan 2010 09:53 AM (UTC)

Amended on Fri 08 Jan 2010 10:07 AM (UTC) by Twisol

Message
Alrighty, here's the more-or-less final version of PPI. It now supports string, number, boolean, and table values, and string and number keys. Some scripting languages can only support one or the other as keys; in these cases, ports of PPI may default to strings, or come up with a language-specific mechanism. So long as the shared interface between PPI libraries is unchanged, the implementation details aren't terribly important.

Nick's previous service/client examples should also be valid under this version of PPI. I've added version numbers to the PPI table to be safe; this is v1.0.0.

(Due to post size limits, I had to offload the library to a pastebin: [1])

[1] http://mushclient.pastebin.com/f7b5b37a9

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
[Go to top] top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


273,228 views.

This is page 1, subject is 10 pages long: 1 2  3  4  5  6  7  8  9  10  [Next page]

It is now over 60 days since the last post. This thread is closed.     [Refresh] Refresh page

Go to topic:           Search the forum


[Go to top] top

Quick links: MUSHclient. MUSHclient help. Forum shortcuts. Posting templates. Lua modules. Lua documentation.

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.

[Home]


Written by Nick Gammon - 5K   profile for Nick Gammon on Stack Exchange, a network of free, community-driven Q&A sites   Marriage equality

Comments to: Gammon Software support
[RH click to get RSS URL] Forum RSS feed ( https://gammon.com.au/rss/forum.xml )

[Best viewed with any browser - 2K]    [Hosted at HostDash]