Subdomain Posts
None | 3 days ago
None | 11 days ago
None | 26 days ago
None | 26 days ago
None | 27 days ago
None | 29 days ago
None | 29 days ago
None | 35 days ago
C | 44 days ago
None | 45 days ago
Recent Posts
XML | 51 sec ago
CSS | 57 sec ago
None | 1 min ago
None | 1 min ago
None | 1 min ago
REBOL | 1 min ago
None | 2 min ago
PHP | 3 min ago
None | 3 min ago
XML | 4 min ago
Sitereport
Find cool info about any domain on the internet?
visit sitereport
Free Subdomains
Want a pastebin.com sub-domain for your community?
learn more...
What is pastebin?
Pastebin is a website that hosts all your text & code on dedicated servers for easy sharing.
learn more...
Learn a little bit about the new Pastebin.com on our help page. hide message
By Soludra on the 29th of Jan 2010 05:49:16 AM Download | Raw | Embed | Report
  1. -- Semantic versioning: http://semver.org/
  2. local __V_MAJOR, __V_MINOR, __V_PATCH = 1, 2, 4
  3. local __VERSION = string.format("%d.%d.%d", __V_MAJOR, __V_MINOR, __V_PATCH)
  4.  
  5. -- Contains a list of PPI proxies to other plugins.
  6. -- Also contains private data for each PPI.
  7. local PPI_list = {}
  8.  
  9. -- Local data for this plugin's PPI
  10. local myID = GetPluginID()
  11. local myPPI = {}
  12. local func_list = {}
  13.  
  14. -- Variable names
  15. local params_id = "PPIparams"
  16.  
  17. -- Message IDs
  18. local invoke_msg  = "PPI_INVOKE"
  19. local access_msg = "PPI_ACCESS"
  20. local cleanup_msg  = "PPI_CLEANUP"
  21.  
  22. -- Forward decl of new_thunk. Defined later in the file
  23. -- so it can access send_invoke(), but declared here
  24. -- so deserialize() can access it.
  25. local new_thunk = nil
  26.  
  27.  
  28. -- Serializes a Lua table into a list of serialized strings.
  29. -- Each string is a single serialized table. [1] is the original table.
  30. -- [2] and beyond are tables contained by [1].
  31. --
  32. -- The second and third params should not be used externally.
  33. local function serialize(params, params_list, state)
  34.   -- External entry point.
  35.   if not params_list or not state then
  36.     local params_list = {}
  37.     serialize(params, params_list, {})
  38.     return params_list
  39.   end
  40.  
  41.   -- If this table was already serialized, return its ID.
  42.   if state[params] then
  43.     return state[params]
  44.   end
  45.  
  46.   -- Register the table in the state list, and reserve its spot
  47.   -- in the serialized list
  48.   local index = #params_list + 1
  49.   state[params] = index
  50.   params_list[index] = true
  51.  
  52.   -- Serialize every argument in the table, and add it to this array.
  53.   local ary_id = "PPIarray_" .. index
  54.   ArrayCreate(ary_id)
  55.  
  56.   for k,v in pairs(params) do
  57.     -- Only support string or number keys.
  58.     local key = nil
  59.     if type(k) == "string" then
  60.       key = "s:" .. k
  61.     elseif type(k) == "number" then
  62.       key = "n:" .. k
  63.     end
  64.    
  65.     if key then
  66.       local value = "z:~"
  67.      
  68.       if type(v) == "string" then
  69.         value = "s:" .. v
  70.       elseif type(v) == "number" then
  71.         value = "n:" .. tostring(v)
  72.       elseif type(v) == "boolean" then
  73.         value = "b:" .. (v and "1" or "0")
  74.       elseif type(v) == "table" then
  75.         value = "t:" .. serialize(v, params_list, state)
  76.       elseif type(v) == "function" then
  77.         value = func_list[v]
  78.         if not value then
  79.           table.insert(func_list, v)
  80.           func_list[v] = #func_list
  81.           value = #func_list
  82.         end
  83.         value = "f:" .. tostring(value)
  84.       end
  85.      
  86.       ArraySet(ary_id, key, value)
  87.     end
  88.   end
  89.  
  90.   -- Add the serialized string to the list.
  91.   params_list[index] = ArrayExport(ary_id, "|")
  92.  
  93.   -- Delete the array so we clean up our mess
  94.   ArrayDelete(ary_id)
  95.  
  96.   -- Let the calling code know what this table's ID is.
  97.   return index
  98. end
  99.  
  100. -- Deserializes a Lua table from a list of serialized strings.
  101. -- The ID is used to deserialize and store function thunks.
  102. --
  103. -- The third and fourth params should not be used externally.
  104. local function deserialize(id, data_list, index, state)
  105.   -- External entry point.
  106.   if not index or not state then
  107.     return deserialize(id, data_list, 1, {})
  108.   end
  109.  
  110.   -- If this string was already deserialized, return its table.
  111.   if state[index] then
  112.     return state[index]
  113.   end
  114.  
  115.   -- Create a new table to store the deserialized data in.
  116.   -- Set it in the state table in case it's referred to multiple times.
  117.   local tbl = {}
  118.   state[index] = tbl
  119.  
  120.   -- Create an array and load the serialized string into it.
  121.   local ary_id = "PPIarray_" .. index
  122.   ArrayCreate(ary_id)
  123.   ArrayImport(ary_id, data_list[index] or "", "|")
  124.  
  125.   -- Go over each key/value pair in the array, deserialize it,
  126.   -- and add it to the table.
  127.   for k,v in pairs(ArrayList(ary_id)) do
  128.     local key_type = k:sub(1,1)
  129.     local key = k:sub(3)
  130.    
  131.     -- only allow string or number keys
  132.     if key_type == "s" then
  133.       -- key is already deserialized
  134.     elseif key_type == "n" then
  135.       key = tonumber(key)
  136.     else
  137.       key = nil
  138.     end
  139.    
  140.     if key then
  141.       local item_type = v:sub(1,1)
  142.       local item = v:sub(3)
  143.      
  144.       if item_type == "s" then
  145.         -- item is already deserialized
  146.       elseif item_type == "n" then
  147.         item = tonumber(item)
  148.       elseif item_type == "b" then
  149.         item = ((item == "1") and true or false)
  150.       elseif item_type == "t" then
  151.         item = deserialize(id, data_list, tonumber(item), state)
  152.       elseif item_type == "f" then
  153.         local thunks = PPI_list[id].thunks
  154.         local thunk = thunks[tonumber(item)]
  155.        
  156.         if not thunk then
  157.           thunk = new_thunk(id, tonumber(item))
  158.           thunks[tonumber(item)] = thunk
  159.         end
  160.        
  161.         item = thunk
  162.       else
  163.         item = nil
  164.       end
  165.      
  166.       tbl[key] = item
  167.     end
  168.   end
  169.  
  170.   -- Delete the array so we clean up our mess
  171.   ArrayDelete(ary_id)
  172.  
  173.   -- Return the deserialized table
  174.   return tbl
  175. end
  176.  
  177. -- Serializes and pushes the table of parameters
  178. -- to MUSHclient variables.
  179. local function send_params(params)
  180.   for k,v in ipairs(serialize(params)) do
  181.     SetVariable(params_id .. "_" .. k, v)
  182.   end
  183. end
  184.  
  185. -- Retreives and deserializes a table of parameters
  186. -- from another plugin's MUSHclient variables.
  187. local function receive_params(id)
  188.   -- Deserialize parameters
  189.   local params = {}
  190.   local i = 1
  191.   while GetPluginVariable(id, params_id .. "_" .. i) do
  192.     params[i] = GetPluginVariable(id, params_id .. "_" .. i)
  193.     i = i + 1
  194.   end
  195.   return deserialize(id, params)
  196. end
  197.  
  198. -- Called to have the other plugin clean up
  199. local function send_cleanup(id)
  200.   CallPlugin(id, cleanup_msg, myID)
  201. end
  202.  
  203. -- Called to access and return a value from the service.
  204. local function send_access(id, name)
  205.   if PluginSupports(id, access_msg) ~= 0 then
  206.     return nil
  207.   end
  208.  
  209.   -- Prepare the arguments
  210.   send_params({name})
  211.  
  212.   -- Call the plugin
  213.   CallPlugin(id, access_msg, myID)
  214.  
  215.   -- Deserialize parameters
  216.   local returns = receive_params(id)
  217.  
  218.   -- Have the other plugin clean up its return values
  219.   send_cleanup(id)
  220.  
  221.   -- Return the received values
  222.   return unpack(returns)
  223. end
  224.  
  225. -- Called by a thunk to call a remote method.
  226. local function send_invoke(id, func_id, ...)
  227.   if PluginSupports(id, invoke_msg) ~= 0 then
  228.     error("The service does not support PPI INVOKE messages.")
  229.   end
  230.  
  231.   -- Prepare the arguments
  232.   send_params({func_id, ...})
  233.  
  234.   -- Call the plugin
  235.   local curr_caller = PPI.CallerID
  236.   CallPlugin(id, invoke_msg, myID)
  237.   PPI.CallerID = curr_caller
  238.  
  239.   -- Gather the return values
  240.   local returns = receive_params(id)
  241.  
  242.   -- Have the other plugin clean up its return values
  243.   send_cleanup(id)
  244.  
  245.   -- Return the received values.
  246.   return unpack(returns)
  247. end
  248.  
  249. -- Declared earlier in the file as local, see note there.
  250. -- Creates a new resolver thunk.
  251. function new_thunk(id, func_name)
  252.   local current_nonce = PPI_list[id].nonce
  253.   return function(...)
  254.     -- Make sure it's still a valid function by checking the nonce
  255.     if GetPluginInfo(id, 22) ~= current_nonce then
  256.       error("The remote plugin has been reinstalled since the last time this method was accessed.")
  257.     end
  258.    
  259.     return send_invoke(id, func_name, ...)
  260.   end
  261. end
  262.  
  263. local PPI_meta = {
  264.   -- Retrieves the given value by key at the service.
  265.   __index = function(tbl, idx)
  266.     return send_access(PPI_list[tbl], idx)
  267.   end,
  268.  
  269.   __newindex = function(tbl, idx, val)
  270.     error("The client PPI is READ-ONLY! Do not write to this table!")
  271.   end,
  272. }
  273.  
  274. -- The returned module table.
  275. local PPI = {
  276.   -- Version identifiers
  277.   __V = __VERSION,
  278.   __V_MAJOR = __V_MAJOR,
  279.   __V_MINOR = __V_MINOR,
  280.   __V_PATCH = __V_PATCH,
  281.  
  282.   -- Given a value during execution of callbacks
  283.   CallerID = nil,
  284.  
  285.  
  286.   -- Used to retreive a PPI for a specified plugin.
  287.   Load = function(id)
  288.     -- Is the plugin installed?
  289.     if not IsPluginInstalled(id) then
  290.       return nil, "not_installed"
  291.     -- Is the plugin enabled?
  292.     elseif not GetPluginInfo(id, 17) then
  293.       return nil, "not_enabled"
  294.     -- Does the plugin support PPI invocations?
  295.     elseif PluginSupports(id, invoke_msg) ~= 0 then
  296.       return nil, "no_ppi"
  297.     end
  298.    
  299.     -- Get the PPI record
  300.     local tbl = PPI_list[id]
  301.     local reloaded = true
  302.    
  303.     -- Create one if there isn't one yet
  304.     if not tbl then
  305.       tbl = {
  306.         ppi = setmetatable({}, PPI_meta),
  307.         id = id,
  308.         thunks = {},
  309.         nonce = GetPluginInfo(id, 22),
  310.       }
  311.      
  312.       PPI_list[id] = tbl
  313.       PPI_list[tbl.ppi] = id
  314.     -- If there is one, reload it if the plugin's nonce has changed.
  315.     elseif tbl.nonce ~= GetPluginInfo(id, 22) then
  316.       tbl.nonce = GetPluginInfo(id, 22)
  317.       tbl.thunks = {}
  318. --  else
  319. --    reloaded = false
  320.     end
  321.    
  322.     return tbl.ppi, reloaded
  323.   end,
  324.  
  325.   -- Used by a plugin to expose methods to other plugins
  326.   -- through its own PPI.
  327.   Expose = function(name, data)
  328.     -- Add the data to the exposed PPI
  329.     myPPI[name] = data or _G[name]
  330.   end,
  331. }
  332.  
  333. -- PPI invocation resolver
  334. _G[invoke_msg] = function(id)
  335.   -- Ensure that a PPI record exists for the client
  336.   PPI.Load(id)
  337.  
  338.   -- Deserialize parameters
  339.   local params = receive_params(id)
  340.   local func = func_list[table.remove(params, 1)]
  341.  
  342.   -- Tell other plugin to clean up
  343.   send_cleanup(id)
  344.  
  345.   if not func then
  346.     return
  347.   end
  348.  
  349.   -- Call method, get return values
  350.   PPI.CallerID = id
  351.   local returns = {func(unpack(params))}
  352.   PPI.CallerID = nil
  353.  
  354.   -- Send returns
  355.   send_params(returns)
  356. end
  357.  
  358. -- When an exposed value is accessed
  359. _G[access_msg] = function(id)
  360.   -- Ensure that a PPI record exists for the client
  361.   PPI.Load(id)
  362.  
  363.   -- Deserialize parameters
  364.   local params = receive_params(id)
  365.   local item = myPPI[unpack(params)]
  366.  
  367.   -- Tell other plugin to clean up
  368.   send_cleanup(id)
  369.  
  370.   -- Set the return values
  371.   send_params({item})
  372. end
  373.  
  374. -- params/returns cleaner
  375. _G[cleanup_msg] = function(id)
  376.   -- clean up all params
  377.   local i = 1
  378.   while GetVariable(params_id .. "_" .. i) do
  379.     DeleteVariable(params_id .. "_" .. i)
  380.     i = i + 1
  381.   end
  382. end
  383.  
  384.  
  385. -- Return the module table
  386. return PPI
Submit a correction or amendment below. Make A New Post
To highlight particular lines, prefix each line with @h@
Syntax highlighting:
Post expiration:
Post exposure:
Name / Title:
Email: