net/multi/intergration/loveManager.lua
Ryan 7601fc636d Updated to 1.8.0
Added:
SystemThreadedBenchmark
SystemThreadedQueue
Fixed a bunch of bugs in the intergrations and regular multi objects
Fixed Error management in threads
All errors trigger the multi.OnError connection
Module creation support improved
added more examples
added Type to threaded objects
2017-06-28 22:40:04 -04:00

379 lines
10 KiB
Lua

require("multi.compat.love2d")
function multi:canSystemThread()
return true
end
multi.intergration={}
multi.intergration.love2d={}
multi.intergration.love2d.ThreadBase=[[
__THREADNAME__=({...})[1]
require("love.filesystem")
require("love.system")
require("love.timer")
require("multi.all")
GLOBAL={}
setmetatable(GLOBAL,{
__index=function(t,k)
return __proxy__[k]
end,
__newindex=function(t,k,v)
__proxy__[k]=v
if type(v)=="userdata" then
__MainChan__:push(v)
else
__MainChan__:push("SYNC "..type(v).." "..k.." "..resolveData(v))
end
end,
})
function ToStr(val, name, skipnewlines, depth)
skipnewlines = skipnewlines or false
depth = depth or 0
local tmp = string.rep(" ", depth)
if name then
if type(name) == "string" then
tmp = tmp .. "[\""..name.."\"] = "
else
tmp = tmp .. "["..(name or "").."] = "
end
end
if type(val) == "table" then
tmp = tmp .. "{" .. (not skipnewlines and " " or "")
for k, v in pairs(val) do
tmp = tmp .. ToStr(v, k, skipnewlines, depth + 1) .. "," .. (not skipnewlines and " " or "")
end
tmp = tmp .. string.rep(" ", depth) .. "}"
elseif type(val) == "number" then
tmp = tmp .. tostring(val)
elseif type(val) == "string" then
tmp = tmp .. string.format("%q", val)
elseif type(val) == "boolean" then
tmp = tmp .. (val and "true" or "false")
elseif type(val) == "function" then
tmp = tmp .. "loadDump([===["..dump(val).."]===])"
else
tmp = tmp .. "\"[inserializeable datatype:" .. type(val) .. "]\""
end
return tmp
end
function resolveType(tp,d)
if tp=="number" then
return tonumber(d)
elseif tp=="bool" then
return (d=="true")
elseif tp=="function" then
return loadDump(d)
elseif tp=="table" then
return loadstring("return "..d)()
elseif tp=="nil" then
return nil
else
return d
end
end
function resolveData(v)
local data=""
if type(v)=="table" then
data=ToStr(v)
elseif type(v)=="function" then
data=dump(v)
elseif type(v)=="string" or type(v)=="number" or type(v)=="bool" or type(v)=="nil" then
data=tostring(v)
end
return data
end
sThread={}
local function randomString(n)
local c=os.clock()
local a=0
while os.clock()<c+.1 do
a=a+1 -- each cpu has a different load... Doing this allows up to make unique seeds for the random string
end
math.randomseed(a)
local str = ''
local strings = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','1','2','3','4','5','6','7','8','9','0','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'}
for i=1,n do
str = str..''..strings[math.random(1,#strings)]
end
return str
end
__proxy__={} -- this is where the actual globals will live
__MainChan__=love.thread.getChannel("__MainChan__")
__mythreadname__=randomString(16)
__mythread__=love.thread.getChannel(__mythreadname__)
__MainChan__:push("NEWTHREAD "..__mythreadname__.." | |")
function loadDump(d)
local s={}
for p in d:gmatch("(%d-)\\") do
s[#s+1]=string.char(tonumber(p))
end
return loadstring(table.concat(s))
end
function dump(func)
local code,t={},string.dump(func)
for i=1,#t do
code[#code+1]=string.byte(t:sub(i,i)).."\\"
end
return table.concat(code)
end
function sThread.set(name,val)
GLOBAL[name]=val
end
function sThread.get(name)
return GLOBAL[name]
end
function sThread.waitFor(name)
--print("Waiting:",__mythreadname__)
local function wait()
local data=__mythread__:pop()
while data do
if type(data)=="string" then
local cmd,tp,name,d=data:match("(%S-) (%S-) (%S-) (.+)")
if name=="__DIEPLZ"..__THREADNAME__.."__" then
error("Thread: "..__THREADNAME__.." has been stopped!")
end
if cmd=="SYNC" then
__proxy__[name]=resolveType(tp,d)
end
else
__proxy__[name]=data
end
data=__mythread__:pop()
end
end
repeat wait() until GLOBAL[name]
return GLOBAL[name]
end
function sThread.getCores()
return love.system.getProcessorCount()
end
function sThread.sleep(n)
love.timer.sleep(n)
end
function sThread.hold(n)
local function wait()
local data=__mythread__:pop()
while data do
if type(data)=="string" then
local cmd,tp,name,d=data:match("(%S-) (%S-) (%S-) (.+)")
if name=="__DIEPLZ"..__THREADNAME__.."__" then
error("Thread: "..__THREADNAME__.." has been stopped!")
end
if cmd=="SYNC" then
__proxy__[name]=resolveType(tp,d)
end
else
__proxy__[name]=data
end
data=__mythread__:pop()
end
end
repeat wait() until n()
end
updater=multi:newUpdater()
updater:OnUpdate(function(self)
local data=__mythread__:pop()
while data do
if type(data)=="string" then
local cmd,tp,name,d=data:match("(%S-) (%S-) (%S-) (.+)")
if name=="__DIEPLZ"..__THREADNAME__.."__" then
error("Thread: "..__THREADNAME__.." has been stopped!")
end
if cmd=="SYNC" then
__proxy__[name]=resolveType(tp,d)
end
else
__proxy__[name]=data
end
data=__mythread__:pop()
end
end)
func=loadDump([=[INSERT_USER_CODE]=])()
multi:mainloop()
]]
GLOBAL={} -- Allow main thread to interact with these objects as well
__proxy__={}
setmetatable(GLOBAL,{
__index=function(t,k)
return __proxy__[k]
end,
__newindex=function(t,k,v)
__proxy__[k]=v
for i=1,#__channels__ do
if type(v)=="userdata" then
__channels__[i]:push(v)
else
__channels__[i]:push("SYNC "..type(v).." "..k.." "..resolveData(v))
end
end
end,
})
THREAD={} -- Allow main thread to interact with these objects as well
multi.intergration.love2d.mainChannel=love.thread.getChannel("__MainChan__")
function ToStr(val, name, skipnewlines, depth)
skipnewlines = skipnewlines or false
depth = depth or 0
local tmp = string.rep(" ", depth)
if name then
if type(name) == "string" then
tmp = tmp .. "[\""..name.."\"] = "
else
tmp = tmp .. "["..(name or "").."] = "
end
end
if type(val) == "table" then
tmp = tmp .. "{" .. (not skipnewlines and " " or "")
for k, v in pairs(val) do
tmp = tmp .. ToStr(v, k, skipnewlines, depth + 1) .. "," .. (not skipnewlines and " " or "")
end
tmp = tmp .. string.rep(" ", depth) .. "}"
elseif type(val) == "number" then
tmp = tmp .. tostring(val)
elseif type(val) == "string" then
tmp = tmp .. string.format("%q", val)
elseif type(val) == "boolean" then
tmp = tmp .. (val and "true" or "false")
elseif type(val) == "function" then
tmp = tmp .. "loadDump([===["..dump(val).."]===])"
else
tmp = tmp .. "\"[inserializeable datatype:" .. type(val) .. "]\""
end
return tmp
end
function resolveType(tp,d)
if tp=="number" then
return tonumber(d)
elseif tp=="bool" then
return (d=="true")
elseif tp=="function" then
return loadDump(d)
elseif tp=="table" then
return loadstring("return "..d)()
elseif tp=="nil" then
return nil
else
return d
end
end
function resolveData(v)
local data=""
if type(v)=="table" then
data=ToStr(v)
elseif type(v)=="function" then
data=dump(v)
elseif type(v)=="string" or type(v)=="number" or type(v)=="bool" or type(v)=="nil" then
data=tostring(v)
end
return data
end
function loadDump(d)
local s={}
for p in d:gmatch("(%d-)\\") do
s[#s+1]=string.char(tonumber(p))
end
return loadstring(table.concat(s))
end
function dump(func)
local code,t={},string.dump(func)
for i=1,#t do
code[#code+1]=string.byte(t:sub(i,i)).."\\"
end
return table.concat(code)
end
local function randomString(n)
local str = ''
local strings = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','1','2','3','4','5','6','7','8','9','0','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'}
for i=1,n do
str = str..''..strings[math.random(1,#strings)]
end
return str
end
function multi:newSystemThread(name,func) -- the main method
local c={}
c.name=name
c.ID=c.name.."<ID|"..randomString(8)..">"
c.thread=love.thread.newThread(multi.intergration.love2d.ThreadBase:gsub("INSERT_USER_CODE",dump(func)))
c.thread:start(c.ID)
function c:kill()
multi.intergration.GLOBAL["__DIEPLZ"..self.ID.."__"]="__DIEPLZ"..self.ID.."__"
end
return c
end
function love.threaderror( thread, errorstr )
multi.OnError:Fire(thread,errorstr)
print("Error in systemThread: "..tostring(thread)..": "..errorstr)
end
local THREAD={}
function THREAD.set(name,val)
GLOBAL[name]=val
end
function THREAD.get(name)
return GLOBAL[name]
end
function THREAD.waitFor(name)
multi.OBJ_REF:Pause()
repeat multi:lManager() until GLOBAL[name]
multi.OBJ_REF:Resume()
return GLOBAL[name]
end
function THREAD.getCores()
return love.system.getProcessorCount()
end
function THREAD.sleep(n)
love.timer.sleep(n)
end
function THREAD.hold(n)
multi.OBJ_REF:Pause()
repeat multi:lManager() until n()
multi.OBJ_REF:Resume()
end
__channels__={}
multi.intergration.GLOBAL=GLOBAL
multi.intergration.THREAD=THREAD
updater=multi:newUpdater()
updater:OnUpdate(function(self)
local data=multi.intergration.love2d.mainChannel:pop()
while data do
--print("MAIN:",data)
if type(data)=="string" then
local cmd,tp,name,d=data:match("(%S-) (%S-) (%S-) (.+)")
if cmd=="SYNC" then
__proxy__[name]=resolveType(tp,d)
for i=1,#__channels__ do
-- send data to other threads
if type(v)=="userdata" then
__channels__[i]:push(v)
else
__channels__[i]:push("SYNC "..tp.." "..name.." "..d)
end
end
elseif cmd=="NEWTHREAD" then
__channels__[#__channels__+1]=love.thread.getChannel(tp)
for k,v in pairs(__proxy__) do -- sync the global with each new thread
if type(v)=="userdata" then
__channels__[#__channels__]:push(v)
else
__channels__[#__channels__]:push("SYNC "..type(v).." "..k.." "..resolveData(v))
end
end
end
else
__proxy__[name]=data
end
data=multi.intergration.love2d.mainChannel:pop()
end
end)
require("multi.intergration.shared.shared")
print("Intergrated Love2d!")
return {
init=function(t)
if t then
if t.threadNamespace then
multi.intergration.love2d.ThreadBase:gsub("sThread",t.threadNamespace)
end
if t.globalNamespace then
multi.intergration.love2d.ThreadBase:gsub("GLOBAL",t.globalNamespace)
end
end
return GLOBAL,THREAD
end
}