rockbandcheeseman

Thread Library

Apr 12th, 2014
134
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 10.15 KB | None | 0 0
  1. -- Threads
  2.  
  3. --[[
  4.     Documenation:
  5.  
  6.     This script will allow you to easily execute any non-Phasor function in Lua lanes.
  7.     Lanes allow you to execute multiple chunks of code at once, which will ultimately solve all lag issues with functions that require a lot of power.
  8.     In order for this to work, you MUST specify all functions and global variables lanes will be using in the 'global' table (see below).
  9.     There are only a few functions and they are all specifically made to be straightforward and easier to use than lanes by itself.
  10.    
  11.     The examples given in each of the function descriptions will build a script that will request a player's stats from a website and write to them in the script.
  12.    
  13.     thread.execute(func, ...)
  14.    
  15.         -- func: <string> The name of the function you want to be executed in a separate thread.
  16.         -- ...: <any amount of data> The parameters that should be passed into the function you're calling in a separate thread.
  17.        
  18.         Returns: Key which will be used for retrieving returned data.
  19.        
  20.         Example:
  21.        
  22.             global = {"getstats"}
  23.             URL = "http://www.phasorstats.com/examplestats"  -- not a real URL; just used for the sake of example
  24.            
  25.             function OnPlayerJoin(player)
  26.                
  27.                 local hash = gethash(player)
  28.                 local key = thread.execute("getstats", hash)
  29.             end
  30.            
  31.             function getstats(hash)
  32.            
  33.                 local http = require "socket.http"
  34.                 local info = http.request(URL .. "/" .. hash)  -- pretending that the stats are stored at "http://www.phasorstats.com/examplestats/" .. hash
  35.                 return assert(load(info))()  -- assuming the data is sent from the website in the same form as table.save saves tables
  36.             end
  37.            
  38.         Notes:
  39.        
  40.             -- Make sure all functions you use in thread.execute are in the global table, including functions called within the functions you use in thread.execute.
  41.             -- Once again, Phasor functions DO NOT WORK in separate threads, so make sure the function you are passing has no Phasor functions.
  42.            
  43.     thread.receive(key)
  44.    
  45.         -- key: <string> Key returned by thread.execute to access the return value of the function call.
  46.        
  47.         Returns: What is returned by the function passed into thread.execute at the specified key.
  48.        
  49.         Example:
  50.        
  51.             global = {"http", "getstats"}
  52.             URL = "http://www.phasorstats.com/examplestats"  -- not a real URL; just used for the sake of example
  53.            
  54.             function OnPlayerJoin(player)
  55.                
  56.                 local hash = gethash(player)
  57.                 local key = thread.execute("getstats", hash)
  58.                 registertimer(100, "ReceiveStats", {player, key})  -- we need to register a timer that will check to see when the thread finishes executing and has a response.
  59.             end
  60.            
  61.             function ReceiveStats(id, count, info)
  62.            
  63.                 local player, key = table.unpack(info)
  64.                 if thread.done(key) then  -- thread.done example (see thread.done syntax for info)
  65.                     local hash = gethash(player)
  66.                     stats[hash] = thread.receive(key)  -- set the player's stats to what getstats returns
  67.                     return false  -- no need to keep this timer running
  68.                 end
  69.                
  70.                 return true  -- keep calling the timer until getstats is finished executing
  71.             end
  72.            
  73.             function getstats(hash)
  74.            
  75.                 local http = require "socket.http"
  76.                 local info = http.request(URL .. "/" .. hash)  -- pretending that the stats are stored at "http://www.phasorstats.com/examplestats/" .. hash
  77.                 return assert(load(info))()  -- assuming the data is sent from the website in the same form as table.save saves tables
  78.             end
  79.            
  80.     thread.done(key)
  81.    
  82.         -- key: <string> Key returned by thread.execute.
  83.        
  84.         Returns: Boolean specifying if the thread has finished (true or false).
  85.        
  86.         Example:
  87.        
  88.             (See example for thread.receive)
  89.            
  90.     thread.cancel(key)
  91.    
  92.         -- key: <string> Key returned by thread.execute.
  93.        
  94.         Example:
  95.        
  96.             statsthread = {}
  97.             global = {"http", "getstats"}
  98.             URL = "http://www.phasorstats.com/examplestats"  -- not a real URL; just used for the sake of example
  99.            
  100.             function OnPlayerJoin(player)
  101.                
  102.                 local hash = gethash(player)
  103.                 local key = thread.execute("getstats", hash)
  104.                 statsthread[player] = key
  105.                 registertimer(100, "ReceiveStats", {player, key})  -- we need to register a timer that will check to see when the thread finishes executing and has a response.
  106.             end
  107.            
  108.             function OnPlayerLeave(player)
  109.            
  110.                 thread.cancel(statsthread[player])
  111.             end
  112.            
  113.             function ReceiveStats(id, count, info)
  114.            
  115.                 local player, key = table.unpack(info)
  116.                 if thread.done(key) then  -- thread.done example (see thread.done syntax for info)
  117.                     local hash = gethash(player)
  118.                     stats[hash] = thread.receive(key)  -- set the player's stats to what getstats returns
  119.                     return false  -- no need to keep this timer running
  120.                 end
  121.                
  122.                 return true  -- keep calling the timer until getstats is finished executing
  123.             end
  124.            
  125.             function getstats(hash)
  126.            
  127.                 local http = require "socket.http"
  128.                 local info = http.request(URL .. "/" .. hash)  -- pretending that the stats are stored at "http://www.phasorstats.com/examplestats/" .. hash
  129.                 return assert(load(info))()  -- assuming the data is sent from the website in the same form as table.save saves tables
  130.             end
  131.            
  132.         Notes:
  133.        
  134.             -- This is only necessary if you want to stop a thread from attempting to execute a function.
  135.             -- In the case of our stats example, we want to cancel the thread from executing if the player leaves before we can access his/her stats.
  136.             -- Using thread.cancel on a key that has already finished executing has no effect.
  137.            
  138.     Miscellaneous Notes:
  139.    
  140.         -- Make sure functions you execute in separate threads don't have random number calculations in them; they will probably always end up being the same.
  141.         -- If you are using other libraries (like socket), make sure you require them in the function they're being used in (see thread.execute example).
  142.    
  143.     If you have any questions about this script, PM me (Nuggets) at phasor.proboards.com.
  144. --]]
  145.  
  146. -- The Global Table --
  147. -- Insert the names of user-created (i.e. not string.gsub, table.len, etc.) variables as strings which will be used in lanes, including functions used in functions you specify.
  148. -- For example, if your function called "somefunction" uses a function within it called "anotherfunction", you must also include "anotherfunction" for "somefunction" to work.
  149. -- You may not use Phasor functions (you will get a Lua Lanes error).
  150.  
  151. global = {}
  152.  
  153. -- Don't edit below this line --
  154.  
  155. local lanes = require "lanes"
  156. lanes.configure()
  157.  
  158. local linda = lanes.linda()
  159.  
  160. lua_lanes_error = false
  161.  
  162. thread = {}
  163.  
  164. function GetRequiredVersion()
  165.  
  166.     return 200
  167. end
  168.  
  169. function OnScriptLoad(processId, game, persistent)
  170.    
  171.     laneglobals = GetGlobalTable()
  172.     workerhandle = lanes.gen("*", {globals = laneglobals}, Worker)
  173.     registertimer(100, "ErrorCheck")   
  174.     registertimer(10, "Sender")
  175.     registertimer(10, "Receiver")
  176. end
  177.  
  178. function GetGlobalTable()
  179.  
  180.     funcs = {}
  181.  
  182.     for _,func in ipairs(global) do
  183.         local split = string.split(func, ".")
  184.         local f = "funcs"
  185.         local g = "_G"
  186.         for k,v in ipairs(split) do
  187.             if type(v) == "string" then
  188.                 v = "\"" .. v .. "\""
  189.             end
  190.  
  191.             f = f .. "[" .. v .. "]"
  192.             g = g .. "[" .. v .. "]"
  193.             assert(load(f .. " = " .. f .. " or " .. g .. " or {}"))()
  194.         end
  195.  
  196.         assert(load(f .. " = " .. g))()
  197.     end
  198.  
  199.     return funcs
  200. end
  201.  
  202. function Worker(key)
  203.  
  204.     while true do
  205.         local _,cmd = linda:receive("command: " .. key)
  206.         local _,parameters = linda:receive("parameters: " .. key)
  207.         if cmd == "thread.cancel" then
  208.             linda:send("returnthread.cancel" .. key)
  209.             break
  210.         else
  211.             if _G[cmd] then
  212.                 local v = {_G[cmd](table.unpack(parameters))}
  213.                 linda:send("return " .. key, v)
  214.             else
  215.                 print("Warning: " .. cmd .. " not defined in global table!")
  216.                 print("Execution of " .. cmd .. " has been cancelled.")
  217.             end
  218.            
  219.             break
  220.         end
  221.     end
  222. end
  223.  
  224. function Sender(id, count)
  225.  
  226.     for k,v in pairs(thread) do
  227.         if type(v) == "table" then
  228.             if not thread[k].sent then
  229.                 thread[k].sent = true
  230.                 thread[k].handle = workerhandle(k)
  231.             end
  232.         end
  233.     end
  234.    
  235.     return not lua_lanes_error
  236. end
  237.  
  238. function Receiver(id, count)
  239.  
  240.     for k,v in pairs(thread) do
  241.         if type(v) == "table" then
  242.             if not thread[k].done then
  243.                 _,thread[k].value = linda:receive(0, "return " .. k)
  244.                 if thread[k].value then
  245.                     thread[k].done = true
  246.                     thread[k].handle = nil
  247.                 end
  248.             end
  249.         end
  250.     end
  251.    
  252.     return not lua_lanes_error
  253. end
  254.  
  255. function ErrorCheck(id, count)
  256.  
  257.     for k,v in pairs(thread) do
  258.         if type(v) == "table" then
  259.             local worker = thread[k].handle
  260.             if worker then
  261.                 if worker.status == "error" then
  262.                     hprintf("Lua Lanes Error:")
  263.                     _, err = worker:join()
  264.                     hprintf(err)
  265.                     lua_lanes_error = true
  266.                     return false
  267.                 end
  268.             end
  269.         end
  270.     end
  271.    
  272.     return true
  273. end
  274.  
  275. function thread.execute(func, ...)
  276.  
  277.     local args = {...}
  278.     for k,v in ipairs(args) do
  279.         args[k] = tostring(v)
  280.     end
  281.    
  282.     local key = func .. table.concat(args, "")
  283.    
  284.     if not thread[key] then
  285.         linda:send("command: " .. key, func)
  286.         linda:send("parameters: " .. key, {...})
  287.         thread[key] = {}
  288.     end
  289.    
  290.     return key
  291. end
  292.  
  293. function thread.receive(key)
  294.  
  295.     return table.unpack(thread[key].value)
  296. end
  297.  
  298. function thread.done(key)
  299.  
  300.     return thread[key].done or false
  301. end
  302.  
  303. function thread.cancel(key)
  304.  
  305.     thread.execute("thread.cancel", key)
  306.     local worker = thread[key].handle
  307.     if worker then
  308.         while worker.status == "running" or worker.status == "waiting" do end
  309.     end
  310. end
  311.  
  312. function string.split(str, ...)
  313.  
  314.     local args = {...}
  315.    
  316.     for k,v in ipairs(args) do
  317.         if v == "" then
  318.             local subs = {}
  319.             for i = 1,string.len(str) do
  320.                 table.insert(subs, string.sub(str, i, i))
  321.             end
  322.  
  323.             return subs
  324.         end
  325.     end
  326.  
  327.     local subs = {}
  328.     local sub = ""
  329.     for i = 1,string.len(str) do
  330.         local bool
  331.         local char = string.sub(str, i, i)
  332.         for k,v in ipairs(args) do
  333.             local delim = string.sub(str, i - (string.len(v) - 1), i)
  334.             if v == delim then
  335.                 bool = true
  336.                 sub = string.sub(sub, 1, string.len(sub) - (string.len(v) - 1))
  337.                 if sub ~= "" then
  338.                     table.insert(subs, sub)
  339.                 end
  340.                 sub = ""
  341.                 break
  342.             end
  343.         end
  344.  
  345.         if not bool then
  346.             sub = sub .. char
  347.         end
  348.        
  349.         if i == string.len(str) then
  350.             table.insert(subs, sub)
  351.         end
  352.     end
  353.  
  354.     return subs
  355. end
Advertisement
Add Comment
Please, Sign In to add comment