More examples small tweaks added/modified examples typos as always minor bug fixes some new features, read readme for them
344 lines
8.7 KiB
Lua
344 lines
8.7 KiB
Lua
require("multi.compat.love2d")
|
|
multi.intergration={}
|
|
multi.intergration.love2d={}
|
|
multi.intergration.love2d.ThreadBase=[[
|
|
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")
|
|
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)()
|
|
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" 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
|
|
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 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 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 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")
|
|
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)()
|
|
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" 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
|
|
function multi:newSystemThread(name,func) -- the main method
|
|
local c={}
|
|
c.name=name
|
|
c.thread=love.thread.newThread(multi.intergration.love2d.ThreadBase:gsub("INSERT_USER_CODE",dump(func)))
|
|
c.thread:start()
|
|
return c
|
|
end
|
|
function love.threaderror( thread, errorstr )
|
|
print("Error in "..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)
|
|
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
|
|
}
|