mirror of
https://github.com/rayaman/multi.git
synced 2026-09-05 07:27:35 -04:00
Added intergration loveManager
Adds multi.intergrations.loveManager,lua Created an example file for you to look at
This commit is contained in:
@@ -1,15 +1,339 @@
|
||||
--[[
|
||||
This is going to be fun. With love2d channels are easy to get but the way they work are very different from lanes'
|
||||
Channels work like a queue so in order to recreate the global table we would have to do some very weird things...
|
||||
|
||||
First off every thread can share a channel thats the same, but once one thread eats the value it isn't availible for the rest... You can peek but then you would have to convey that to the other threads
|
||||
The way the structure of threading works in love2d makes you not want to have a thread that is constantly running because data passing in a nice way is tricky.
|
||||
|
||||
The idea that love took prevents data errors, but a global table is something that needs to be implemted so basic libraries
|
||||
that use the multi library threading ablities are cross platform.
|
||||
|
||||
Idea 1:
|
||||
One method could be to have a read channel that all data is sent through.
|
||||
The first thread that gets it using Channel:performAtomic() takes that data keeps a copy for its Global then sends a copy to the other threads individual global channel
|
||||
basically an updater that updates a local table.
|
||||
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__={}
|
||||
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)
|
||||
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user