2465 lines
56 KiB
Lua
2465 lines
56 KiB
Lua
--[[
|
|
MIT License
|
|
|
|
Copyright (c) 2018 Ryan Ward
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE.
|
|
]]
|
|
local bin = pcall(require,"bin")
|
|
local multi = {}
|
|
local clock = os.clock
|
|
multi.Version = "13.0.0"
|
|
multi._VERSION = "13.0.0"
|
|
multi.stage = "stable"
|
|
multi.__index = multi
|
|
multi.Name = "multi.Root"
|
|
multi.Mainloop = {}
|
|
multi.Garbage = {}
|
|
multi.ender = {}
|
|
multi.Children = {}
|
|
multi.Active = true
|
|
multi.fps = 60
|
|
multi.Type = "mainprocess"
|
|
multi.Rest = 0
|
|
multi._type = type
|
|
multi.Jobs = {}
|
|
multi.queue = {}
|
|
multi.jobUS = 2
|
|
multi.clock = os.clock
|
|
multi.time = os.time
|
|
multi.LinkedPath = multi
|
|
multi.lastTime = clock()
|
|
local mainloopActive = false
|
|
local isRunning = false
|
|
local next
|
|
local ncount = 0
|
|
multi.defaultSettings = {
|
|
priority = 0,
|
|
protect = false,
|
|
}
|
|
--Do not change these ever...Any other number will not work (Unless you are using enablePriority2())
|
|
multi.Priority_Core = 1
|
|
multi.Priority_High = 4
|
|
multi.Priority_Above_Normal = 16
|
|
multi.Priority_Normal = 64
|
|
multi.Priority_Below_Normal = 256
|
|
multi.Priority_Low = 1024
|
|
multi.Priority_Idle = 4096
|
|
multi.PriorityResolve = {
|
|
[1]="Core",
|
|
[4]="High",
|
|
[16]="Above Normal",
|
|
[64]="Normal",
|
|
[256]="Below Normal",
|
|
[1024]="Low",
|
|
[4096]="Idle",
|
|
}
|
|
multi.PStep = 1
|
|
multi.PList = {multi.Priority_Core,multi.Priority_High,multi.Priority_Above_Normal,multi.Priority_Normal,multi.Priority_Below_Normal,multi.Priority_Low,multi.Priority_Idle}
|
|
--^^^^
|
|
multi.PriorityTick=1 -- Between 1, 2 and 4
|
|
multi.Priority=multi.Priority_High
|
|
multi.threshold=256
|
|
multi.threstimed=.001
|
|
function multi.queuefinal(self)
|
|
self:Destroy()
|
|
if self.Parent.Mainloop[#self.Parent.Mainloop] then
|
|
if self.Parent.Mainloop[#self.Parent.Mainloop].Type=="alarm" then
|
|
self.Parent.Mainloop[#self.Parent.Mainloop]:Reset()
|
|
self.Parent.Mainloop[#self.Parent.Mainloop].Active=true
|
|
else
|
|
self.Parent.Mainloop[#self.Parent.Mainloop]:Resume()
|
|
end
|
|
else
|
|
for i=1,#self.Parent.funcE do
|
|
self.Parent.funcE[i](self)
|
|
end
|
|
self.Parent:Remove()
|
|
end
|
|
end
|
|
if table.unpack and not unpack then
|
|
unpack=table.unpack
|
|
end
|
|
function table.merge(t1, t2)
|
|
for k,v in pairs(t2) do
|
|
if type(v) == 'table' then
|
|
if type(t1[k] or false) == 'table' then
|
|
table.merge(t1[k] or {}, t2[k] or {})
|
|
else
|
|
t1[k] = v
|
|
end
|
|
else
|
|
t1[k] = v
|
|
end
|
|
end
|
|
return t1
|
|
end
|
|
_print=print
|
|
function print(...)
|
|
if not __SUPPRESSPRINTS then
|
|
_print(...)
|
|
end
|
|
end
|
|
_write=io.write
|
|
function io.write(...)
|
|
if not __SUPPRESSWRITES then
|
|
_write(...)
|
|
end
|
|
end
|
|
function multi:setThrestimed(n)
|
|
self.deltaTarget=n or .1
|
|
end
|
|
function multi:enableLoadDetection()
|
|
if multi.maxSpd then return end
|
|
-- here we are going to run a quick benchMark solo
|
|
local temp = multi:newProcessor()
|
|
temp:Start()
|
|
local t = os.clock()
|
|
local stop = false
|
|
temp:benchMark(.01):OnBench(function(time,steps)
|
|
stop = steps*1.1
|
|
end)
|
|
while not stop do
|
|
temp:uManager()
|
|
end
|
|
temp:Destroy()
|
|
multi.maxSpd = stop
|
|
end
|
|
local MaxLoad = nil
|
|
function multi:setLoad(n)
|
|
MaxLoad = n
|
|
end
|
|
local busy = false
|
|
local lastVal = 0
|
|
function multi:getLoad()
|
|
if not multi.maxSpd then multi:enableLoadDetection() end
|
|
if busy then return lastVal end
|
|
local val = nil
|
|
if thread.isThread() then
|
|
local bench
|
|
multi:benchMark(.01):OnBench(function(time,steps)
|
|
bench = steps
|
|
end)
|
|
thread.hold(function()
|
|
return bench
|
|
end)
|
|
bench = bench^1.5
|
|
val = math.ceil((1-(bench/(multi.maxSpd/2.2)))*100)
|
|
else
|
|
busy = true
|
|
local bench
|
|
multi:benchMark(.01):OnBench(function(time,steps)
|
|
bench = steps
|
|
end)
|
|
while not bench do
|
|
multi:uManager()
|
|
end
|
|
bench = bench^1.5
|
|
val = math.ceil((1-(bench/(multi.maxSpd/2.2)))*100)
|
|
busy = false
|
|
end
|
|
if val<0 then val = 0 end
|
|
if val > 100 then val = 100 end
|
|
lastVal = val
|
|
return val
|
|
end
|
|
function multi:setDomainName(name)
|
|
self[name]={}
|
|
end
|
|
function multi:linkDomain(name)
|
|
return self[name]
|
|
end
|
|
function multi:_Pause()
|
|
self.Active=false
|
|
end
|
|
function multi:setPriority(s)
|
|
if type(s)==number then
|
|
self.Priority=s
|
|
elseif type(s)=='string' then
|
|
if s:lower()=='core' or s:lower()=='c' then
|
|
self.Priority=self.Priority_Core
|
|
elseif s:lower()=='high' or s:lower()=='h' then
|
|
self.Priority=self.Priority_High
|
|
elseif s:lower()=='above' or s:lower()=='a' then
|
|
self.Priority=self.Priority_Above_Normal
|
|
elseif s:lower()=='normal' or s:lower()=='n' then
|
|
self.Priority=self.Priority_Normal
|
|
elseif s:lower()=='below' or s:lower()=='b' then
|
|
self.Priority=self.Priority_Below_Normal
|
|
elseif s:lower()=='low' or s:lower()=='l' then
|
|
self.Priority=self.Priority_Low
|
|
elseif s:lower()=='idle' or s:lower()=='i' then
|
|
self.Priority=self.Priority_Idle
|
|
end
|
|
self.solid = true
|
|
end
|
|
end
|
|
-- System
|
|
function os.getOS()
|
|
if package.config:sub(1,1)=='\\' then
|
|
return 'windows'
|
|
else
|
|
return 'unix'
|
|
end
|
|
end
|
|
if os.getOS()=='windows' then
|
|
function os.sleep(n)
|
|
if n > 0 then os.execute('ping -n ' .. tonumber(n+1) .. ' localhost > NUL') end
|
|
end
|
|
else
|
|
function os.sleep(n)
|
|
os.execute('sleep ' .. tonumber(n))
|
|
end
|
|
end
|
|
function multi.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:getParentProcess()
|
|
return self.Mainloop[self.CID]
|
|
end
|
|
multi.GetParentProcess=multi.getParentProcess
|
|
function multi.Stop()
|
|
mainloopActive=false
|
|
end
|
|
function multi:isHeld()
|
|
return self.held
|
|
end
|
|
multi.important={}
|
|
multi.IsHeld=multi.isHeld
|
|
function multi.executeFunction(name,...)
|
|
if type(_G[name])=='function' then
|
|
_G[name](...)
|
|
else
|
|
print('Error: Not a function')
|
|
end
|
|
end
|
|
function multi:getChildren()
|
|
return self.Mainloop
|
|
end
|
|
function multi:getVersion()
|
|
return multi.Version
|
|
end
|
|
function multi:getPlatform()
|
|
if love then
|
|
if love.thread then
|
|
return "love2d"
|
|
end
|
|
else
|
|
return "lanes"
|
|
end
|
|
end
|
|
function multi:canSystemThread()
|
|
return false
|
|
end
|
|
--Processor
|
|
function multi:getError()
|
|
if self.error then
|
|
return self.error
|
|
end
|
|
end
|
|
function multi:benchMark(sec,p,pt)
|
|
local c = 0
|
|
local temp=self:newLoop(function(self,t)
|
|
if t>sec then
|
|
if pt then
|
|
print(pt.." "..c.." Steps in "..sec.." second(s)!")
|
|
end
|
|
self.tt(sec,c)
|
|
self:Destroy()
|
|
else
|
|
c=c+1
|
|
end
|
|
end)
|
|
temp:setPriority(p or 1)
|
|
function temp:OnBench(func)
|
|
self.tt=func
|
|
end
|
|
self.tt=function() end
|
|
return temp
|
|
end
|
|
function multi.Round(num, numDecimalPlaces)
|
|
local mult = 10^(numDecimalPlaces or 0)
|
|
return math.floor(num * mult + 0.5) / mult
|
|
end
|
|
function multi.AlignTable(tab)
|
|
local longest = {}
|
|
local columns = #tab[1]
|
|
local rows = #tab
|
|
for i=1, columns do
|
|
longest[i] = -math.huge
|
|
end
|
|
for i = 1,rows do
|
|
for j = 1,columns do
|
|
tab[i][j] = tostring(tab[i][j])
|
|
if #tab[i][j]>longest[j] then
|
|
longest[j] = #tab[i][j]
|
|
end
|
|
end
|
|
end
|
|
for i = 1,rows do
|
|
for j = 1,columns do
|
|
if tab[i][j]~=nil and #tab[i][j]<longest[j] then
|
|
tab[i][j]=tab[i][j]..string.rep(" ",longest[j]-#tab[i][j])
|
|
end
|
|
end
|
|
end
|
|
local str = {}
|
|
for i = 1,rows do
|
|
str[#str+1] = table.concat(tab[i]," ")
|
|
end
|
|
return table.concat(str,"\n")
|
|
end
|
|
local priorityTable = {[0]="Round-Robin",[1]="Just-Right",[2]="Top-heavy",[3]="Timed-Based-Balancer"}
|
|
local ProcessName = {[true]="SubProcessor",[false]="MainProcessor"}
|
|
function multi:getTasksDetails(t)
|
|
if t == "string" or not t then
|
|
str = {
|
|
{"Type <Identifier","Uptime","Priority","TID"}
|
|
}
|
|
local count = 0
|
|
for i,v in pairs(self.Mainloop) do
|
|
local name = v.Name or ""
|
|
if name~="" then
|
|
name = " <"..name..">"
|
|
end
|
|
count = count + 1
|
|
table.insert(str,{v.Type:sub(1,1):upper()..v.Type:sub(2,-1)..name,multi.Round(os.clock()-v.creationTime,3),self.PriorityResolve[v.Priority],i})
|
|
end
|
|
if count == 0 then
|
|
table.insert(str,{"Currently no processes running!","","",""})
|
|
end
|
|
local s = multi.AlignTable(str)
|
|
dat = ""
|
|
dat2 = ""
|
|
if multi.SystemThreads then
|
|
for i = 1,#multi.SystemThreads do
|
|
dat2 = dat2.."<SystemThread: "..multi.SystemThreads[i].Name.." | "..os.clock()-multi.SystemThreads[i].creationTime..">\n"
|
|
end
|
|
end
|
|
if multi.scheduler then
|
|
for i=1,#multi.scheduler.Threads do
|
|
dat = dat .. "<THREAD: "..multi.scheduler.Threads[i].Name.." | "..os.clock()-multi.scheduler.Threads[i].creationTime..">\n"
|
|
end
|
|
return "Load on "..ProcessName[self.Type=="process"].."<"..(self.Name or "Unnamed")..">"..": "..multi.Round(multi:getLoad(),2).."%\nMemory Usage: "..math.ceil(collectgarbage("count")).." KB\nThreads Running: "..#multi.scheduler.Threads.."\nSystemThreads Running: "..#(multi.SystemThreads or {}).."\nPriority Scheme: "..priorityTable[multi.defaultSettings.priority or 0].."\n\n"..s.."\n\n"..dat..dat2
|
|
else
|
|
return "Load on "..ProcessName[self.Type=="process"].."<"..(self.Name or "Unnamed")..">"..": "..multi.Round(multi:getLoad(),2).."%\nMemory Usage: "..math.ceil(collectgarbage("count")).." KB\nThreads Running: 0\nPriority Scheme: "..priorityTable[multi.defaultSettings.priority or 0].."\n\n"..s..dat2
|
|
end
|
|
elseif t == "t" or t == "table" then
|
|
str = {
|
|
ThreadCount = #multi.scheduler.Threads,
|
|
MemoryUsage = math.ceil(collectgarbage("count")).." KB",
|
|
PriorityScheme = priorityTable[multi.defaultSettings.priority or 0],
|
|
SystemLoad = multi.Round(multi:getLoad(),2)
|
|
}
|
|
str.threads = {}
|
|
str.systemthreads = {}
|
|
for i,v in pairs(self.Mainloop) do
|
|
str[#str+1]={Type=v.Type,Name=v.Name,Uptime=os.clock()-v.creationTime,Priority=self.PriorityResolve[v.Priority],TID = i}
|
|
end
|
|
for i=1,#multi.scheduler.Threads do
|
|
str.threads[multi.scheduler.Threads[i].Name]={Uptime = os.clock()-multi.scheduler.Threads[i].creationTime}
|
|
end
|
|
if multi.canSystemThread then
|
|
for i=1,#multi.SystemThreads do
|
|
str.systemthreads[multi.SystemThreads[i].Name]={Uptime = os.clock()-multi.SystemThreads[i].creationTime}
|
|
end
|
|
end
|
|
return str
|
|
end
|
|
end
|
|
function multi:endTask(TID)
|
|
self.Mainloop[TID]:Destroy()
|
|
end
|
|
function multi.startFPSMonitior()
|
|
if not multi.runFPS then
|
|
multi.doFPS(s)
|
|
multi.runFPS=true
|
|
end
|
|
end
|
|
function multi.doFPS(s)
|
|
multi:benchMark(1):OnBench(doFPS)
|
|
if s then
|
|
multi.fps=s
|
|
end
|
|
end
|
|
--Helpers
|
|
function multi.timer(func,...)
|
|
local timer=multi:newTimer()
|
|
timer:Start()
|
|
args={func(...)}
|
|
local t = timer:Get()
|
|
timer = nil
|
|
return t,unpack(args)
|
|
end
|
|
function multi:IsAnActor()
|
|
return self.Act~=nil
|
|
end
|
|
function multi:OnMainConnect(func)
|
|
table.insert(self.func,func)
|
|
return self
|
|
end
|
|
function multi:reallocate(o,n)
|
|
n=n or #o.Mainloop+1
|
|
local int=self.Parent
|
|
self:Destroy()
|
|
self.Parent=o
|
|
table.insert(o.Mainloop,n,self)
|
|
self.Active=true
|
|
end
|
|
multi.Reallocate=multi.Reallocate
|
|
function multi:setJobSpeed(n)
|
|
self.jobUS=n
|
|
end
|
|
function multi:hasJobs()
|
|
return #self.Jobs>0,#self.Jobs
|
|
end
|
|
function multi:getJobs()
|
|
return #self.Jobs
|
|
end
|
|
function multi:removeJob(name)
|
|
local count = 0
|
|
for i=#self.Jobs,1,-1 do
|
|
if self.Jobs[i][2]==name then
|
|
table.remove(self.Jobs,i)
|
|
count = count + 1
|
|
end
|
|
end
|
|
return count
|
|
end
|
|
function multi:FreeMainEvent()
|
|
self.func={}
|
|
end
|
|
function multi:connectFinal(func)
|
|
if self.Type=='event' then
|
|
self:OnEvent(func)
|
|
elseif self.Type=='alarm' then
|
|
self:OnRing(func)
|
|
elseif self.Type=='step' or self.Type=='tstep' then
|
|
self:OnEnd(func)
|
|
else
|
|
print("Warning!!! "..self.Type.." doesn't contain a Final Connection State! Use "..self.Type..":Break(func) to trigger it's final event!")
|
|
self:OnBreak(func)
|
|
end
|
|
end
|
|
multi.ConnectFinal=multi.connectFinal
|
|
function multi:Break()
|
|
self:Pause()
|
|
self.Active=nil
|
|
for i=1,#self.ender do
|
|
if self.ender[i] then
|
|
self.ender[i](self)
|
|
end
|
|
end
|
|
end
|
|
function multi:OnBreak(func)
|
|
table.insert(self.ender,func)
|
|
end
|
|
function multi:isPaused()
|
|
return not(self.Active)
|
|
end
|
|
multi.IsPaused=multi.isPaused
|
|
function multi:isActive()
|
|
return self.Active
|
|
end
|
|
multi.IsActive=multi.isActive
|
|
function multi:getType()
|
|
return self.Type
|
|
end
|
|
multi.GetType=multi.getType
|
|
-- Advance Timer stuff
|
|
function multi:SetTime(n)
|
|
if not n then n=3 end
|
|
local c=multi:newBase()
|
|
c.Type='timemaster'
|
|
c.timer=multi:newTimer()
|
|
c.timer:Start()
|
|
c.set=n
|
|
c.link=self
|
|
self._timer=c.timer
|
|
function c:Act()
|
|
if self.timer:Get()>=self.set then
|
|
self.link:Pause()
|
|
for i=1,#self.link.funcTM do
|
|
self.link.funcTM[i](self.link)
|
|
end
|
|
self:Destroy()
|
|
end
|
|
end
|
|
return self
|
|
end
|
|
multi.ResetTime=multi.SetTime
|
|
function multi:ResolveTimer(...)
|
|
self._timer:Pause()
|
|
for i=1,#self.funcTMR do
|
|
self.funcTMR[i](self,...)
|
|
end
|
|
self:Pause()
|
|
return self
|
|
end
|
|
function multi:OnTimedOut(func)
|
|
self.funcTM[#self.funcTM+1]=func
|
|
return self
|
|
end
|
|
function multi:OnTimerResolved(func)
|
|
self.funcTMR[#self.funcTMR+1]=func
|
|
return self
|
|
end
|
|
-- Timer stuff done
|
|
function multi:Pause()
|
|
if self.Type=='mainprocess' then
|
|
print("You cannot pause the main process. Doing so will stop all methods and freeze your program! However if you still want to use multi:_Pause()")
|
|
else
|
|
self.Active=false
|
|
local loop = self.Parent.Mainloop
|
|
for i=1,#loop do
|
|
if loop[i] == self then
|
|
table.remove(loop,i)
|
|
break
|
|
end
|
|
end
|
|
end
|
|
return self
|
|
end
|
|
function multi:Resume()
|
|
if self.Type=='process' or self.Type=='mainprocess' then
|
|
self.Active=true
|
|
local c=self:getChildren()
|
|
for i=1,#c do
|
|
c[i]:Resume()
|
|
end
|
|
else
|
|
if self.Active==false then
|
|
table.insert(self.Parent.Mainloop,self)
|
|
self.Active=true
|
|
end
|
|
end
|
|
return self
|
|
end
|
|
function multi:Destroy()
|
|
if self.Type=='process' or self.Type=='mainprocess' then
|
|
local c=self:getChildren()
|
|
for i=1,#c do
|
|
self.OnObjectDestroyed:Fire(c[i])
|
|
c[i]:Destroy()
|
|
end
|
|
else
|
|
for i=1,#self.Parent.Mainloop do
|
|
if self.Parent.Mainloop[i]==self then
|
|
self.Parent.OnObjectDestroyed:Fire(self)
|
|
table.remove(self.Parent.Mainloop,i)
|
|
break
|
|
end
|
|
end
|
|
self.Active=false
|
|
end
|
|
return self
|
|
end
|
|
function multi:Reset(n)
|
|
self:Resume()
|
|
return self
|
|
end
|
|
function multi:isDone()
|
|
return self.Active~=true
|
|
end
|
|
multi.IsDone=multi.isDone
|
|
function multi:create(ref)
|
|
multi.OnObjectCreated:Fire(ref,self)
|
|
end
|
|
function multi:setName(name)
|
|
self.Name = name
|
|
return self
|
|
end
|
|
multi.SetName = multi.setName
|
|
--Constructors [CORE]
|
|
function multi:newBase(ins)
|
|
if not(self.Type=='mainprocess' or self.Type=='process' or self.Type=='queue') then error('Can only create an object on multi or an interface obj') return false end
|
|
local c = {}
|
|
if self.Type=='process' or self.Type=='queue' then
|
|
setmetatable(c, self.Parent)
|
|
else
|
|
setmetatable(c, self)
|
|
end
|
|
c.Active=true
|
|
c.func={}
|
|
c.funcTM={}
|
|
c.funcTMR={}
|
|
c.ender={}
|
|
c.important={}
|
|
c.Act=function() end
|
|
c.Parent=self
|
|
c.held=false
|
|
c.creationTime = os.clock()
|
|
if ins then
|
|
table.insert(self.Mainloop,ins,c)
|
|
else
|
|
table.insert(self.Mainloop,c)
|
|
end
|
|
return c
|
|
end
|
|
function multi:newProcessor(file)
|
|
if not(self.Type=='mainprocess') then error('Can only create an interface on the multi obj') return false end
|
|
local c = {}
|
|
setmetatable(c, self)
|
|
c.Parent=self
|
|
c.Active=true
|
|
c.func={}
|
|
c.Type='process'
|
|
c.Mainloop={}
|
|
c.Garbage={}
|
|
c.Children={}
|
|
c.Active=false
|
|
c.Rest=0
|
|
c.Jobs={}
|
|
c.queue={}
|
|
c.jobUS=2
|
|
c.l=self:newLoop(function(self,dt)
|
|
if self.link.Active then
|
|
c:uManager()
|
|
end
|
|
end)
|
|
c.l.link = c
|
|
c.l.Type = "processor"
|
|
function c:getController()
|
|
return c.l
|
|
end
|
|
function c:Start()
|
|
self.Active = true
|
|
return self
|
|
end
|
|
function c:Resume()
|
|
self.Active = false
|
|
return self
|
|
end
|
|
function c:setName(name)
|
|
c.l.Name = name
|
|
return self
|
|
end
|
|
function c:Pause()
|
|
if self.l then
|
|
self.l:Pause()
|
|
end
|
|
return self
|
|
end
|
|
function c:Remove()
|
|
if self.Type == "process" then
|
|
self:__Destroy()
|
|
self.l:Destroy()
|
|
else
|
|
self:__Destroy()
|
|
end
|
|
end
|
|
function c:Destroy()
|
|
if self == c then
|
|
self.l:Destroy()
|
|
else
|
|
for i = #c.Mainloop,1,-1 do
|
|
if c.Mainloop[i] == self then
|
|
table.remove(c.Mainloop,i)
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
if file then
|
|
self.Cself=c
|
|
loadstring('local process=multi.Cself '..io.open(file,'rb'):read('*all'))()
|
|
end
|
|
-- c.__Destroy = self.Destroy
|
|
-- c.Destroy = c.Remove
|
|
self:create(c)
|
|
--~ c:IngoreObject()
|
|
return c
|
|
end
|
|
function multi:newTimer()
|
|
local c={}
|
|
c.Type='timer'
|
|
local time=0
|
|
local count=0
|
|
local paused=false
|
|
function c:Start()
|
|
time=os.clock()
|
|
return self
|
|
end
|
|
function c:Get()
|
|
if self:isPaused() then return time end
|
|
return (clock()-time)+count
|
|
end
|
|
function c:isPaused()
|
|
return paused
|
|
end
|
|
c.Reset=c.Start
|
|
function c:Pause()
|
|
time=self:Get()
|
|
paused=true
|
|
return self
|
|
end
|
|
function c:Resume()
|
|
paused=false
|
|
time=os.clock()-time
|
|
return self
|
|
end
|
|
function c:tofile(path)
|
|
local m=bin.new()
|
|
count=count+self:Get()
|
|
m:addBlock(self.Type)
|
|
m:addBlock(count)
|
|
m:tofile(path)
|
|
return self
|
|
end
|
|
self:create(c)
|
|
return c
|
|
end
|
|
function multi:newConnector()
|
|
local c = {Type = "connector"}
|
|
return c
|
|
end
|
|
function multi:newConnection(protect,func)
|
|
local c={}
|
|
c.callback = func
|
|
c.Parent=self
|
|
setmetatable(c,{__call=function(self,...)
|
|
local t = ...
|
|
if type(t)=="table" and t.Type ~= nil then
|
|
return self:Fire(args,select(2,...))
|
|
else
|
|
return self:connect(...)
|
|
end
|
|
end})
|
|
c.Type='connector'
|
|
c.func={}
|
|
c.ID=0
|
|
c.protect=protect or true
|
|
c.connections={}
|
|
c.fconnections={}
|
|
c.FC=0
|
|
function c:holdUT(n)
|
|
local n=n or 0
|
|
self.waiting=true
|
|
local count=0
|
|
local id=self:connect(function()
|
|
count = count + 1
|
|
if n<=count then
|
|
self.waiting=false
|
|
end
|
|
end)
|
|
repeat
|
|
self.Parent:uManager(multi.defaultSettings)
|
|
until self.waiting==false
|
|
id:Destroy()
|
|
return self
|
|
end
|
|
c.HoldUT=c.holdUT
|
|
function c:fConnect(func)
|
|
local temp=self:connect(func)
|
|
table.insert(self.fconnections,temp)
|
|
self.FC=self.FC+1
|
|
return self
|
|
end
|
|
c.FConnect=c.fConnect
|
|
function c:getConnection(name,ingore)
|
|
if ingore then
|
|
return self.connections[name] or {
|
|
Fire=function() return end -- if the connection doesn't exist lets call all of them or silently ignore
|
|
}
|
|
else
|
|
return self.connections[name] or self
|
|
end
|
|
end
|
|
function c:Fire(...)
|
|
local ret={}
|
|
for i=#self.func,1,-1 do
|
|
if self.protect then
|
|
local temp={pcall(self.func[i][1],...)}
|
|
if temp[1] then
|
|
table.remove(temp,1)
|
|
table.insert(ret,temp)
|
|
else
|
|
print(temp[2])
|
|
end
|
|
else
|
|
table.insert(ret,{self.func[i][1](...)})
|
|
end
|
|
end
|
|
return ret
|
|
end
|
|
function c:Bind(t)
|
|
self.func=t
|
|
return self
|
|
end
|
|
function c:Remove()
|
|
self.func={}
|
|
return self
|
|
end
|
|
function c:connect(func,name,num)
|
|
self.ID=self.ID+1
|
|
table.insert(self.func,num or #self.func+1,{func,self.ID})
|
|
local temp = {
|
|
Link=self.func,
|
|
func=func,
|
|
ID=self.ID,
|
|
Parent=self,
|
|
Fire=function(self,...)
|
|
--~ if self.Parent.FC>0 then
|
|
--~ for i=1,#self.Parent.FC do
|
|
--~ self.Parent.FC[i]:Fire(...)
|
|
--~ end
|
|
--~ end
|
|
if self.Parent.protect then
|
|
local t=pcall(self.func,...)
|
|
if t then
|
|
return t
|
|
end
|
|
else
|
|
return self.func(...)
|
|
end
|
|
end,
|
|
Remove=function(self)
|
|
for i=1,#self.Link do
|
|
if self.Link[i][2]~=nil then
|
|
if self.Link[i][2]==self.ID then
|
|
table.remove(self.Link,i)
|
|
self.remove=function() end
|
|
self.Link=nil
|
|
self.ID=nil
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
end,
|
|
}
|
|
temp.Destroy=temp.Remove
|
|
if name then
|
|
self.connections[name]=temp
|
|
end
|
|
if self.callback then
|
|
self.callback(temp)
|
|
end
|
|
return temp
|
|
end
|
|
c.Connect=c.connect
|
|
c.GetConnection=c.getConnection
|
|
function c:tofile(path)
|
|
local m=bin.new()
|
|
m:addBlock(self.Type)
|
|
m:addBlock(self.func)
|
|
m:tofile(path)
|
|
return self
|
|
end
|
|
return c
|
|
end
|
|
multi.OnObjectCreated=multi:newConnection()
|
|
multi.OnObjectDestroyed=multi:newConnection()
|
|
function multi:newJob(func,name)
|
|
if not(self.Type=='mainprocess' or self.Type=='process') then error('Can only create an object on multi or an interface obj') return false end
|
|
local c = {}
|
|
if self.Type=='process' then
|
|
setmetatable(c, self.Parent)
|
|
else
|
|
setmetatable(c, self)
|
|
end
|
|
c.Active=true
|
|
c.func={}
|
|
c.Parent=self
|
|
c.Type='job'
|
|
c.trigfunc=func or function() end
|
|
function c:Act()
|
|
self:trigfunc(self)
|
|
end
|
|
table.insert(self.Jobs,{c,name})
|
|
if self.JobRunner==nil then
|
|
self.JobRunner=self:newAlarm(self.jobUS):setName("multi.jobHandler")
|
|
self.JobRunner:OnRing(function(self)
|
|
if #self.Parent.Jobs>0 then
|
|
if self.Parent.Jobs[1] then
|
|
self.Parent.Jobs[1][1]:Act()
|
|
table.remove(self.Parent.Jobs,1)
|
|
end
|
|
end
|
|
self:Reset(self.Parent.jobUS)
|
|
end)
|
|
end
|
|
end
|
|
function multi.nextStep(func)
|
|
ncount = ncount+1
|
|
if not next then
|
|
next = {func}
|
|
else
|
|
next[#next+1] = func
|
|
end
|
|
end
|
|
multi.OnPreLoad=multi:newConnection()
|
|
--Core Actors
|
|
function multi:newCustomObject(objRef,isActor)
|
|
local c={}
|
|
if isActor then
|
|
c=self:newBase()
|
|
if type(objRef)=='table' then
|
|
table.merge(c,objRef)
|
|
end
|
|
if not c.Act then
|
|
function c:Act()
|
|
-- Empty function
|
|
end
|
|
end
|
|
else
|
|
c=objRef or {}
|
|
end
|
|
if not c.Type then
|
|
c.Type='coustomObject'
|
|
end
|
|
self:create(c)
|
|
return c
|
|
end
|
|
function multi:newEvent(task)
|
|
local c=self:newBase()
|
|
c.Type='event'
|
|
c.Task=task or function() end
|
|
function c:Act()
|
|
local t = {self.Task(self)}
|
|
if t[1] then
|
|
self:Pause()
|
|
self.returns = t
|
|
for _E=1,#self.func do
|
|
self.func[_E](self)
|
|
end
|
|
end
|
|
end
|
|
function c:SetTask(func)
|
|
self.Task=func
|
|
return self
|
|
end
|
|
function c:OnEvent(func)
|
|
table.insert(self.func,func)
|
|
return self
|
|
end
|
|
self:setPriority("core")
|
|
self:create(c)
|
|
return c
|
|
end
|
|
function multi:newUpdater(skip)
|
|
local c=self:newBase()
|
|
c.Type='updater'
|
|
c.pos=1
|
|
c.skip=skip or 1
|
|
function c:Act()
|
|
if self.pos>=self.skip then
|
|
self.pos=0
|
|
for i=1,#self.func do
|
|
self.func[i](self)
|
|
end
|
|
end
|
|
self.pos=self.pos+1
|
|
end
|
|
function c:SetSkip(n)
|
|
self.skip=n
|
|
return self
|
|
end
|
|
c.OnUpdate=self.OnMainConnect
|
|
self:create(c)
|
|
return c
|
|
end
|
|
function multi:newAlarm(set)
|
|
local c=self:newBase()
|
|
c.Type='alarm'
|
|
c.Priority=self.Priority_Low
|
|
c.set=set or 0
|
|
local count = 0
|
|
local t = clock()
|
|
function c:Act()
|
|
if clock()-t>=self.set then
|
|
self:Pause()
|
|
self.Active=false
|
|
for i=1,#self.func do
|
|
self.func[i](self)
|
|
end
|
|
t = clock()
|
|
end
|
|
end
|
|
function c:Resume()
|
|
self.Parent.Resume(self)
|
|
t = count + t
|
|
return self
|
|
end
|
|
function c:Reset(n)
|
|
if n then self.set=n end
|
|
self:Resume()
|
|
t = clock()
|
|
return self
|
|
end
|
|
function c:OnRing(func)
|
|
table.insert(self.func,func)
|
|
return self
|
|
end
|
|
function c:Pause()
|
|
count = clock()
|
|
self.Parent.Pause(self)
|
|
return self
|
|
end
|
|
self:create(c)
|
|
return c
|
|
end
|
|
function multi:newLoop(func)
|
|
local c=self:newBase()
|
|
c.Type='loop'
|
|
local start=self.clock()
|
|
local funcs = {}
|
|
if func then
|
|
funcs={func}
|
|
end
|
|
function c:Act()
|
|
for i=1,#funcs do
|
|
funcs[i](self,clock()-start)
|
|
end
|
|
end
|
|
function c:OnLoop(func)
|
|
table.insert(funcs,func)
|
|
return self
|
|
end
|
|
self:create(c)
|
|
return c
|
|
end
|
|
function multi:newFunction(func)
|
|
local c={}
|
|
c.func=func
|
|
mt={
|
|
__index=multi,
|
|
__call=function(self,...)
|
|
if self.Active then
|
|
return self:func(...)
|
|
end
|
|
return nil,true
|
|
end
|
|
}
|
|
c.Parent=self
|
|
function c:Pause()
|
|
self.Active=false
|
|
return self
|
|
end
|
|
function c:Resume()
|
|
self.Active=true
|
|
return self
|
|
end
|
|
setmetatable(c,mt)
|
|
self:create(c)
|
|
return c
|
|
end
|
|
function multi:newStep(start,reset,count,skip)
|
|
local c=self:newBase()
|
|
think=1
|
|
c.Type='step'
|
|
c.pos=start or 1
|
|
c.endAt=reset or math.huge
|
|
c.skip=skip or 0
|
|
c.spos=0
|
|
c.count=count or 1*think
|
|
c.funcE={}
|
|
c.funcS={}
|
|
c.start=start or 1
|
|
if start~=nil and reset~=nil then
|
|
if start>reset then
|
|
think=-1
|
|
end
|
|
end
|
|
function c:Act()
|
|
if self~=nil then
|
|
if self.spos==0 then
|
|
if self.pos==self.start then
|
|
for fe=1,#self.funcS do
|
|
self.funcS[fe](self)
|
|
end
|
|
end
|
|
for i=1,#self.func do
|
|
self.func[i](self,self.pos)
|
|
end
|
|
self.pos=self.pos+self.count
|
|
if self.pos-self.count==self.endAt then
|
|
self:Pause()
|
|
for fe=1,#self.funcE do
|
|
self.funcE[fe](self)
|
|
end
|
|
self.pos=self.start
|
|
end
|
|
end
|
|
end
|
|
self.spos=self.spos+1
|
|
if self.spos>=self.skip then
|
|
self.spos=0
|
|
end
|
|
end
|
|
c.Reset=c.Resume
|
|
function c:OnStart(func)
|
|
table.insert(self.funcS,func)
|
|
return self
|
|
end
|
|
function c:OnStep(func)
|
|
table.insert(self.func,1,func)
|
|
return self
|
|
end
|
|
function c:OnEnd(func)
|
|
table.insert(self.funcE,func)
|
|
return self
|
|
end
|
|
function c:Break()
|
|
self.Active=nil
|
|
return self
|
|
end
|
|
function c:Update(start,reset,count,skip)
|
|
self.start=start or self.start
|
|
self.endAt=reset or self.endAt
|
|
self.skip=skip or self.skip
|
|
self.count=count or self.count
|
|
self:Resume()
|
|
return self
|
|
end
|
|
self:create(c)
|
|
return c
|
|
end
|
|
function multi:newTLoop(func,set)
|
|
local c=self:newBase()
|
|
c.Type='tloop'
|
|
c.set=set or 0
|
|
c.timer=self:newTimer()
|
|
c.life=0
|
|
c:setPriority("Low")
|
|
if func then
|
|
c.func={func}
|
|
end
|
|
function c:Act()
|
|
if self.timer:Get()>=self.set then
|
|
self.life=self.life+1
|
|
for i=1,#self.func do
|
|
self.func[i](self,self.life)
|
|
end
|
|
self.timer:Reset()
|
|
end
|
|
end
|
|
function c:Resume()
|
|
self.Parent.Resume(self)
|
|
self.timer:Resume()
|
|
return self
|
|
end
|
|
function c:Pause()
|
|
self.timer:Pause()
|
|
self.Parent.Pause(self)
|
|
return self
|
|
end
|
|
function c:OnLoop(func)
|
|
table.insert(self.func,func)
|
|
return self
|
|
end
|
|
self:create(c)
|
|
return c
|
|
end
|
|
function multi:newTrigger(func)
|
|
local c={}
|
|
c.Type='trigger'
|
|
c.trigfunc=func or function() end
|
|
function c:Fire(...)
|
|
self:trigfunc(...)
|
|
return self
|
|
end
|
|
self:create(c)
|
|
return c
|
|
end
|
|
function multi:newTStep(start,reset,count,set)
|
|
local c=self:newBase()
|
|
think=1
|
|
c.Type='tstep'
|
|
c.Priority=self.Priority_Low
|
|
c.start=start or 1
|
|
local reset = reset or math.huge
|
|
c.endAt=reset
|
|
c.pos=start or 1
|
|
c.skip=skip or 0
|
|
c.count=count or 1*think
|
|
c.funcE={}
|
|
c.timer=self.clock()
|
|
c.set=set or 1
|
|
c.funcS={}
|
|
function c:Update(start,reset,count,set)
|
|
self.start=start or self.start
|
|
self.pos=self.start
|
|
self.endAt=reset or self.endAt
|
|
self.set=set or self.set
|
|
self.count=count or self.count or 1
|
|
self.timer=self.clock()
|
|
self:Resume()
|
|
return self
|
|
end
|
|
function c:Act()
|
|
if self.clock()-self.timer>=self.set then
|
|
self:Reset()
|
|
if self.pos==self.start then
|
|
for fe=1,#self.funcS do
|
|
self.funcS[fe](self)
|
|
end
|
|
end
|
|
for i=1,#self.func do
|
|
self.func[i](self,self.pos)
|
|
end
|
|
self.pos=self.pos+self.count
|
|
if self.pos-self.count==self.endAt then
|
|
self:Pause()
|
|
for fe=1,#self.funcE do
|
|
self.funcE[fe](self)
|
|
end
|
|
self.pos=self.start
|
|
end
|
|
end
|
|
end
|
|
function c:OnStart(func)
|
|
table.insert(self.funcS,func)
|
|
return self
|
|
end
|
|
function c:OnStep(func)
|
|
table.insert(self.func,func)
|
|
return self
|
|
end
|
|
function c:OnEnd(func)
|
|
table.insert(self.funcE,func)
|
|
return self
|
|
end
|
|
function c:Break()
|
|
self.Active=nil
|
|
return self
|
|
end
|
|
function c:Reset(n)
|
|
if n then self.set=n end
|
|
self.timer=self.clock()
|
|
self:Resume()
|
|
return self
|
|
end
|
|
self:create(c)
|
|
return c
|
|
end
|
|
function multi:newTimeStamper()
|
|
local c=self:newUpdater(self.Priority_Idle)
|
|
c:OnUpdate(function()
|
|
c:Run()
|
|
end)
|
|
local feb = 28
|
|
local leap = tonumber(os.date("%Y"))%4==0 and (tonumber(os.date("%Y"))%100~=0 or tonumber(os.date("%Y"))%400==0)
|
|
if leap then
|
|
feb = 29
|
|
end
|
|
local dInM = {
|
|
["01"] = 31,
|
|
["02"] = feb,
|
|
["03"] = 31,
|
|
["04"] = 30,
|
|
["05"] = 31,
|
|
["06"] = 30,
|
|
["07"] = 31, -- This is dumb, why do we still follow this double 31 days!?
|
|
["08"] = 31,
|
|
["09"] = 30,
|
|
["10"] = 31,
|
|
["11"] = 30,
|
|
["12"] = 31,
|
|
}
|
|
c.Type='timestamper'
|
|
c.Priority=self.Priority_Idle
|
|
c.hour = {}
|
|
c.minute = {}
|
|
c.second = {}
|
|
c.time = {}
|
|
c.day = {}
|
|
c.month = {}
|
|
c.year = {}
|
|
function c:Run()
|
|
for i=1,#self.hour do
|
|
if self.hour[i][1]==os.date("%H") and self.hour[i][3] then
|
|
self.hour[i][2](self)
|
|
self.hour[i][3]=false
|
|
elseif self.hour[i][1]~=os.date("%H") and not self.hour[i][3] then
|
|
self.hour[i][3]=true
|
|
end
|
|
end
|
|
for i=1,#self.minute do
|
|
if self.minute[i][1]==os.date("%M") and self.minute[i][3] then
|
|
self.minute[i][2](self)
|
|
self.minute[i][3]=false
|
|
elseif self.minute[i][1]~=os.date("%M") and not self.minute[i][3] then
|
|
self.minute[i][3]=true
|
|
end
|
|
end
|
|
for i=1,#self.second do
|
|
if self.second[i][1]==os.date("%S") and self.second[i][3] then
|
|
self.second[i][2](self)
|
|
self.second[i][3]=false
|
|
elseif self.second[i][1]~=os.date("%S") and not self.second[i][3] then
|
|
self.second[i][3]=true
|
|
end
|
|
end
|
|
for i=1,#self.day do
|
|
if type(self.day[i][1])=="string" then
|
|
if self.day[i][1]==os.date("%a") and self.day[i][3] then
|
|
self.day[i][2](self)
|
|
self.day[i][3]=false
|
|
elseif self.day[i][1]~=os.date("%a") and not self.day[i][3] then
|
|
self.day[i][3]=true
|
|
end
|
|
else
|
|
local dday = self.day[i][1]
|
|
if dday < 0 then
|
|
dday = dInM[os.date("%m")]+(dday+1)
|
|
end
|
|
if string.format("%02d",dday)==os.date("%d") and self.day[i][3] then
|
|
self.day[i][2](self)
|
|
self.day[i][3]=false
|
|
elseif string.format("%02d",dday)~=os.date("%d") and not self.day[i][3] then
|
|
self.day[i][3]=true
|
|
end
|
|
end
|
|
end
|
|
for i=1,#self.month do
|
|
if self.month[i][1]==os.date("%m") and self.month[i][3] then
|
|
self.month[i][2](self)
|
|
self.month[i][3]=false
|
|
elseif self.month[i][1]~=os.date("%m") and not self.month[i][3] then
|
|
self.month[i][3]=true
|
|
end
|
|
end
|
|
for i=1,#self.time do
|
|
if self.time[i][1]==os.date("%X") and self.time[i][3] then
|
|
self.time[i][2](self)
|
|
self.time[i][3]=false
|
|
elseif self.time[i][1]~=os.date("%X") and not self.time[i][3] then
|
|
self.time[i][3]=true
|
|
end
|
|
end
|
|
for i=1,#self.year do
|
|
if self.year[i][1]==os.date("%y") and self.year[i][3] then
|
|
self.year[i][2](self)
|
|
self.year[i][3]=false
|
|
elseif self.year[i][1]~=os.date("%y") and not self.year[i][3] then
|
|
self.year[i][3]=true
|
|
end
|
|
end
|
|
end
|
|
function c:OnTime(hour,minute,second,func)
|
|
if type(hour)=="number" then
|
|
self.time[#self.time+1]={string.format("%02d:%02d:%02d",hour,minute,second),func,true}
|
|
else
|
|
self.time[#self.time+1]={hour,minute,true}
|
|
end
|
|
return self
|
|
end
|
|
function c:OnHour(hour,func)
|
|
self.hour[#self.hour+1]={string.format("%02d",hour),func,true}
|
|
return self
|
|
end
|
|
function c:OnMinute(minute,func)
|
|
self.minute[#self.minute+1]={string.format("%02d",minute),func,true}
|
|
return self
|
|
end
|
|
function c:OnSecond(second,func)
|
|
self.second[#self.second+1]={string.format("%02d",second),func,true}
|
|
return self
|
|
end
|
|
function c:OnDay(day,func)
|
|
self.day[#self.day+1]={day,func,true}
|
|
return self
|
|
end
|
|
function c:OnMonth(month,func)
|
|
self.month[#self.month+1]={string.format("%02d",month),func,true}
|
|
return self
|
|
end
|
|
function c:OnYear(year,func)
|
|
self.year[#self.year+1]={string.format("%02d",year),func,true}
|
|
return self
|
|
end
|
|
self:create(c)
|
|
return c
|
|
end
|
|
function multi:newWatcher(namespace,name)
|
|
local function WatcherObj(ns,n)
|
|
if self.Type=='queue' then
|
|
print("Cannot create a watcher on a queue! Creating on 'multi' instead!")
|
|
self=multi
|
|
end
|
|
local c=self:newBase()
|
|
c.Type='watcher'
|
|
c.ns=ns
|
|
c.n=n
|
|
c.cv=ns[n]
|
|
function c:OnValueChanged(func)
|
|
table.insert(self.func,func)
|
|
return self
|
|
end
|
|
function c:Act()
|
|
if self.cv~=self.ns[self.n] then
|
|
for i=1,#self.func do
|
|
self.func[i](self,self.cv,self.ns[self.n])
|
|
end
|
|
self.cv=self.ns[self.n]
|
|
end
|
|
end
|
|
self:create(c)
|
|
return c
|
|
end
|
|
if type(namespace)~='table' and type(namespace)=='string' then
|
|
return WatcherObj(_G,namespace)
|
|
elseif type(namespace)=='table' and (type(name)=='string' or 'number') then
|
|
return WatcherObj(namespace,name)
|
|
else
|
|
print('Warning, invalid arguments! Nothing returned!')
|
|
end
|
|
end
|
|
-- Threading stuff
|
|
thread={}
|
|
multi.GlobalVariables={}
|
|
if os.getOS()=="windows" then
|
|
thread.__CORES=tonumber(os.getenv("NUMBER_OF_PROCESSORS"))
|
|
else
|
|
thread.__CORES=tonumber(io.popen("nproc --all"):read("*n"))
|
|
end
|
|
function thread.sleep(n)
|
|
coroutine.yield({"_sleep_",n or 0})
|
|
end
|
|
function thread.hold(n)
|
|
return coroutine.yield({"_hold_",n or function() return true end})
|
|
end
|
|
function thread.skip(n)
|
|
coroutine.yield({"_skip_",n or 0})
|
|
end
|
|
function thread.kill()
|
|
coroutine.yield({"_kill_",":)"})
|
|
end
|
|
function thread.yeild()
|
|
coroutine.yield({"_sleep_",0})
|
|
end
|
|
function thread.isThread()
|
|
return coroutine.running()~=nil
|
|
end
|
|
function thread.getCores()
|
|
return thread.__CORES
|
|
end
|
|
function thread.set(name,val)
|
|
multi.GlobalVariables[name]=val
|
|
return true
|
|
end
|
|
function thread.get(name)
|
|
return multi.GlobalVariables[name]
|
|
end
|
|
function thread.waitFor(name)
|
|
thread.hold(function() return thread.get(name)~=nil end)
|
|
return thread.get(name)
|
|
end
|
|
function thread.testFor(name,_val,sym)
|
|
thread.hold(function()
|
|
local val = thread.get(name)~=nil
|
|
if val then
|
|
if sym == "==" or sym == "=" then
|
|
return _val==val
|
|
elseif sym == ">" then
|
|
return _val>val
|
|
elseif sym == "<" then
|
|
return _val<val
|
|
elseif sym == "<=" then
|
|
return _val<=val
|
|
elseif sym == ">=" then
|
|
return _val>=val
|
|
end
|
|
end
|
|
end)
|
|
return thread.get(name)
|
|
end
|
|
multi:setDomainName("Threads")
|
|
multi:setDomainName("Globals")
|
|
local initT = false
|
|
function multi:newThread(name,func)
|
|
local c={}
|
|
c.ref={}
|
|
c.Name=name
|
|
c.thread=coroutine.create(func)
|
|
c.sleep=1
|
|
c.Type="thread"
|
|
c.firstRunDone=false
|
|
c.timer=multi:newTimer()
|
|
c.ref.Globals=self:linkDomain("Globals")
|
|
function c.ref:send(name,val)
|
|
ret=coroutine.yield({Name=name,Value=val})
|
|
self:syncGlobals(ret)
|
|
end
|
|
function c.ref:get(name)
|
|
return self.Globals[name]
|
|
end
|
|
function c.ref:kill()
|
|
err=coroutine.yield({"_kill_"})
|
|
if err then
|
|
error("Failed to kill a thread! Exiting...")
|
|
end
|
|
end
|
|
function c.ref:sleep(n)
|
|
if type(n)=="function" then
|
|
ret=coroutine.yield({"_hold_",n})
|
|
self:syncGlobals(ret)
|
|
elseif type(n)=="number" then
|
|
n = tonumber(n) or 0
|
|
ret=coroutine.yield({"_sleep_",n})
|
|
self:syncGlobals(ret)
|
|
else
|
|
error("Invalid Type for sleep!")
|
|
end
|
|
end
|
|
function c.ref:syncGlobals(v)
|
|
self.Globals=v
|
|
end
|
|
table.insert(self:linkDomain("Threads"),c)
|
|
if initT==false then
|
|
multi.initThreads()
|
|
end
|
|
c.creationTime = os.clock()
|
|
end
|
|
function multi.initThreads()
|
|
initT = true
|
|
multi.scheduler=multi:newLoop():setName("multi.thread")
|
|
multi.scheduler.Type="scheduler"
|
|
function multi.scheduler:setStep(n)
|
|
self.skip=tonumber(n) or 24
|
|
end
|
|
multi.scheduler.skip=0
|
|
multi.scheduler.counter=0
|
|
multi.scheduler.Threads=multi:linkDomain("Threads")
|
|
multi.scheduler.Globals=multi:linkDomain("Globals")
|
|
multi.scheduler:OnLoop(function(self)
|
|
self.counter=self.counter+1
|
|
for i=#self.Threads,1,-1 do
|
|
ret={}
|
|
if coroutine.status(self.Threads[i].thread)=="dead" then
|
|
table.remove(self.Threads,i)
|
|
else
|
|
if self.Threads[i].timer:Get()>=self.Threads[i].sleep then
|
|
if self.Threads[i].firstRunDone==false then
|
|
self.Threads[i].firstRunDone=true
|
|
self.Threads[i].timer:Start()
|
|
if unpack(self.Threads[i].returns or {}) then
|
|
_,ret=coroutine.resume(self.Threads[i].thread,unpack(self.Threads[i].returns))
|
|
else
|
|
_,ret=coroutine.resume(self.Threads[i].thread,self.Threads[i].ref)
|
|
end
|
|
else
|
|
if unpack(self.Threads[i].returns or {}) then
|
|
_,ret=coroutine.resume(self.Threads[i].thread,unpack(self.Threads[i].returns))
|
|
else
|
|
_,ret=coroutine.resume(self.Threads[i].thread,self.Globals)
|
|
end
|
|
end
|
|
if _==false then
|
|
self.Parent.OnError:Fire(self.Threads[i],"Error in thread: <"..self.Threads[i].Name.."> "..ret)
|
|
end
|
|
if ret==true or ret==false then
|
|
print("Thread Ended!!!")
|
|
ret={}
|
|
end
|
|
end
|
|
if ret then
|
|
if ret[1]=="_kill_" then
|
|
table.remove(self.Threads,i)
|
|
elseif ret[1]=="_sleep_" then
|
|
self.Threads[i].timer:Reset()
|
|
self.Threads[i].sleep=ret[2]
|
|
elseif ret[1]=="_skip_" then
|
|
self.Threads[i].timer:Reset()
|
|
self.Threads[i].sleep=math.huge
|
|
local event=multi:newEvent(function(evnt) return multi.scheduler.counter>=evnt.counter end)
|
|
event.link=self.Threads[i]
|
|
event.counter=self.counter+ret[2]
|
|
event:OnEvent(function(evnt)
|
|
evnt.link.sleep=0
|
|
evnt:Destroy()
|
|
end):setName("multi.thread.skip")
|
|
elseif ret[1]=="_hold_" then
|
|
self.Threads[i].timer:Reset()
|
|
self.Threads[i].sleep=math.huge
|
|
local event=multi:newEvent(ret[2])
|
|
event.returns = nil
|
|
event.link=self.Threads[i]
|
|
event:OnEvent(function(evnt)
|
|
evnt.link.sleep=0
|
|
evnt.link.returns = evnt.returns
|
|
multi.nextStep(function()
|
|
evnt:Destroy()
|
|
end)
|
|
end):setName("multi.thread.hold")
|
|
elseif ret.Name then
|
|
self.Globals[ret.Name]=ret.Value
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
end
|
|
multi.OnError=multi:newConnection()
|
|
function multi:newThreadedProcess(name)
|
|
local c = {}
|
|
local holding = false
|
|
local kill = false
|
|
setmetatable(c, multi)
|
|
function c:newBase(ins)
|
|
local ct = {}
|
|
ct.Active=true
|
|
ct.func={}
|
|
ct.ender={}
|
|
ct.Act=function() end
|
|
ct.Parent=self
|
|
ct.held=false
|
|
ct.ref=self.ref
|
|
table.insert(self.Mainloop,ct)
|
|
return ct
|
|
end
|
|
c.Parent=self
|
|
c.Active=true
|
|
c.func={}
|
|
c.Type='threadedprocess'
|
|
c.Mainloop={}
|
|
c.Garbage={}
|
|
c.Children={}
|
|
c.Active=true
|
|
c.Rest=0
|
|
c.updaterate=.01
|
|
c.restRate=.1
|
|
c.Jobs={}
|
|
c.queue={}
|
|
c.jobUS=2
|
|
c.rest=false
|
|
function c:getController()
|
|
return nil
|
|
end
|
|
function c:Start()
|
|
self.rest=false
|
|
return self
|
|
end
|
|
function c:Resume()
|
|
self.rest=false
|
|
return self
|
|
end
|
|
function c:Pause()
|
|
self.rest=true
|
|
return self
|
|
end
|
|
function c:Remove()
|
|
self.ref:kill()
|
|
return self
|
|
end
|
|
function c:Kill()
|
|
kill = true
|
|
return self
|
|
end
|
|
function c:Sleep(n)
|
|
holding = true
|
|
if type(n)=="number" then
|
|
multi:newAlarm(n):OnRing(function(a)
|
|
holding = false
|
|
a:Destroy()
|
|
end):setName("multi.TPSleep")
|
|
elseif type(n)=="function" then
|
|
multi:newEvent(n):OnEvent(function(e)
|
|
holding = false
|
|
e:Destroy()
|
|
end):setName("multi.TPHold")
|
|
end
|
|
return self
|
|
end
|
|
c.Hold=c.Sleep
|
|
multi:newThread(name,function(ref)
|
|
while true do
|
|
thread.hold(function()
|
|
return not(holding)
|
|
end)
|
|
c:uManager()
|
|
end
|
|
end)
|
|
return c
|
|
end
|
|
function multi:newHyperThreadedProcess(name)
|
|
if not name then error("All threads must have a name!") end
|
|
local c = {}
|
|
setmetatable(c, multi)
|
|
local ind = 0
|
|
local holding = true
|
|
local kill = false
|
|
function c:newBase(ins)
|
|
local ct = {}
|
|
ct.Active=true
|
|
ct.func={}
|
|
ct.ender={}
|
|
ct.Act=function() end
|
|
ct.Parent=self
|
|
ct.held=false
|
|
ct.ref=self.ref
|
|
ind = ind + 1
|
|
multi:newThread("Proc <"..name.."> #"..ind,function()
|
|
while true do
|
|
thread.hold(function()
|
|
return not(holding)
|
|
end)
|
|
if kill then
|
|
err=coroutine.yield({"_kill_"})
|
|
if err then
|
|
error("Failed to kill a thread! Exiting...")
|
|
end
|
|
end
|
|
ct:Act()
|
|
end
|
|
end)
|
|
return ct
|
|
end
|
|
c.Parent=self
|
|
c.Active=true
|
|
c.func={}
|
|
c.Type='hyperthreadedprocess'
|
|
c.Mainloop={}
|
|
c.Garbage={}
|
|
c.Children={}
|
|
c.Active=true
|
|
c.Rest=0
|
|
c.updaterate=.01
|
|
c.restRate=.1
|
|
c.Jobs={}
|
|
c.queue={}
|
|
c.jobUS=2
|
|
c.rest=false
|
|
function c:getController()
|
|
return nil
|
|
end
|
|
function c:Start()
|
|
holding = false
|
|
return self
|
|
end
|
|
function c:Resume()
|
|
holding = false
|
|
return self
|
|
end
|
|
function c:Pause()
|
|
holding = true
|
|
return self
|
|
end
|
|
function c:Remove()
|
|
self.ref:kill()
|
|
return self
|
|
end
|
|
function c:Kill()
|
|
kill = true
|
|
return self
|
|
end
|
|
function c:Sleep(b)
|
|
holding = true
|
|
if type(b)=="number" then
|
|
local t = os.clock()
|
|
multi:newAlarm(b):OnRing(function(a)
|
|
holding = false
|
|
a:Destroy()
|
|
end):setName("multi.HTPSleep")
|
|
elseif type(b)=="function" then
|
|
multi:newEvent(b):OnEvent(function(e)
|
|
holding = false
|
|
e:Destroy()
|
|
end):setName("multi.HTPHold")
|
|
end
|
|
return self
|
|
end
|
|
c.Hold=c.Sleep
|
|
return c
|
|
end
|
|
-- Multi runners
|
|
function multi:threadloop(settings)
|
|
multi.scheduler:Destroy() -- destroy is an interesting thing... if you dont set references to nil, then you only remove it from the mainloop
|
|
local Threads=multi:linkDomain("Threads")
|
|
local Globals=multi:linkDomain("Globals")
|
|
local counter=0
|
|
local tick = 0
|
|
while true do
|
|
tick = tick + 1
|
|
if tick == 1024 then
|
|
tick = 0
|
|
multi:uManager(settings)
|
|
end
|
|
counter=counter+1
|
|
for i=#Threads,1,-1 do
|
|
ret={}
|
|
if coroutine.status(Threads[i].thread)=="dead" then
|
|
table.remove(Threads,i)
|
|
else
|
|
if Threads[i].timer:Get()>=Threads[i].sleep then
|
|
if Threads[i].firstRunDone==false then
|
|
Threads[i].firstRunDone=true
|
|
Threads[i].timer:Start()
|
|
_,ret=coroutine.resume(Threads[i].thread,Threads[i].ref)
|
|
else
|
|
_,ret=coroutine.resume(Threads[i].thread,Globals)
|
|
end
|
|
if _==false then
|
|
multi.OnError:Fire(Threads[i],"Error in thread: <"..Threads[i].Name.."> "..ret)
|
|
end
|
|
if ret==true or ret==false then
|
|
ret={}
|
|
end
|
|
end
|
|
if ret then
|
|
if ret[1]=="_kill_" then
|
|
table.remove(Threads,i)
|
|
elseif ret[1]=="_sleep_" then
|
|
Threads[i].timer:Reset()
|
|
Threads[i].sleep=ret[2]
|
|
elseif ret[1]=="_skip_" then
|
|
Threads[i].timer:Reset()
|
|
Threads[i].sleep=math.huge
|
|
local event=multi:newEvent(function(evnt) return counter>=evnt.counter end)
|
|
event.link=Threads[i]
|
|
event.counter=counter+ret[2]
|
|
event:OnEvent(function(evnt)
|
|
evnt.link.sleep=0
|
|
evnt:Destroy()
|
|
end)
|
|
elseif ret[1]=="_hold_" then
|
|
Threads[i].timer:Reset()
|
|
Threads[i].sleep=math.huge
|
|
local event=multi:newEvent(ret[2])
|
|
event.link=Threads[i]
|
|
event:OnEvent(function(evnt)
|
|
evnt.link.sleep=0
|
|
evnt:Destroy()
|
|
end)
|
|
elseif ret.Name then
|
|
Globals[ret.Name]=ret.Value
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
function multi:mainloop(settings)
|
|
multi.defaultSettings = settings or multi.defaultSettings
|
|
self.uManager=self.uManagerRef
|
|
multi.OnPreLoad:Fire()
|
|
local p_c,p_h,p_an,p_n,p_bn,p_l,p_i = self.Priority_Core,self.Priority_High,self.Priority_Above_Normal,self.Priority_Normal,self.Priority_Below_Normal,self.Priority_Low,self.Priority_Idle
|
|
local P_LB = p_i
|
|
if not isRunning then
|
|
local protect = false
|
|
local priority = false
|
|
local stopOnError = true
|
|
local delay = 3
|
|
if settings then
|
|
priority = settings.priority
|
|
if settings.auto_priority then
|
|
priority = -1
|
|
end
|
|
if settings.preLoop then
|
|
settings.preLoop(self)
|
|
end
|
|
if settings.stopOnError then
|
|
stopOnError = settings.stopOnError
|
|
end
|
|
if settings.auto_stretch then
|
|
p_i = p_i * settings.auto_stretch
|
|
end
|
|
if settings.auto_delay then
|
|
delay = settings.auto_delay
|
|
end
|
|
if settings.auto_lowerbound then
|
|
P_LB = settings.auto_lowerbound
|
|
end
|
|
protect = settings.protect
|
|
end
|
|
local t,tt = clock(),0
|
|
isRunning=true
|
|
local lastTime = clock()
|
|
rawset(self,'Start',clock())
|
|
mainloopActive = true
|
|
local Loop=self.Mainloop
|
|
local PS=self
|
|
local PStep = 1
|
|
local autoP = 0
|
|
local solid
|
|
local sRef
|
|
while mainloopActive do
|
|
if next then
|
|
local DD = table.remove(next,1)
|
|
while DD do
|
|
DD()
|
|
DD = table.remove(next,1)
|
|
end
|
|
end
|
|
if priority == 1 then
|
|
for _D=#Loop,1,-1 do
|
|
for P=1,7 do
|
|
if Loop[_D] then
|
|
if (PS.PList[P])%Loop[_D].Priority==0 then
|
|
if Loop[_D].Active then
|
|
self.CID=_D
|
|
if not protect then
|
|
Loop[_D]:Act()
|
|
else
|
|
local status, err=pcall(Loop[_D].Act,Loop[_D])
|
|
if err then
|
|
Loop[_D].error=err
|
|
self.OnError:Fire(Loop[_D],err)
|
|
if stopOnError then
|
|
Loop[_D]:Destroy()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
elseif priority == 2 then
|
|
for _D=#Loop,1,-1 do
|
|
if Loop[_D] then
|
|
if (PStep)%Loop[_D].Priority==0 then
|
|
if Loop[_D].Active then
|
|
self.CID=_D
|
|
if not protect then
|
|
Loop[_D]:Act()
|
|
else
|
|
local status, err=pcall(Loop[_D].Act,Loop[_D])
|
|
if err then
|
|
Loop[_D].error=err
|
|
self.OnError:Fire(Loop[_D],err)
|
|
if stopOnError then
|
|
Loop[_D]:Destroy()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
PStep=PStep+1
|
|
if PStep==p_i then
|
|
PStep=0
|
|
end
|
|
elseif priority == 3 then
|
|
tt = clock()-t
|
|
t = clock()
|
|
for _D=#Loop,1,-1 do
|
|
if Loop[_D] then
|
|
if Loop[_D].Priority == p_c or (Loop[_D].Priority == p_h and tt<.5) or (Loop[_D].Priority == p_an and tt<.125) or (Loop[_D].Priority == p_n and tt<.063) or (Loop[_D].Priority == p_bn and tt<.016) or (Loop[_D].Priority == p_l and tt<.003) or (Loop[_D].Priority == p_i and tt<.001) then
|
|
if Loop[_D].Active then
|
|
self.CID=_D
|
|
if not protect then
|
|
Loop[_D]:Act()
|
|
else
|
|
local status, err=pcall(Loop[_D].Act,Loop[_D])
|
|
if err then
|
|
Loop[_D].error=err
|
|
self.OnError:Fire(Loop[_D],err)
|
|
if stopOnError then
|
|
Loop[_D]:Destroy()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
elseif priority == -1 then
|
|
for _D=#Loop,1,-1 do
|
|
sRef = Loop[_D]
|
|
if Loop[_D] then
|
|
if (sRef.Priority == p_c) or PStep==0 then
|
|
if sRef.Active then
|
|
self.CID=_D
|
|
if not protect then
|
|
if sRef.solid then
|
|
sRef:Act()
|
|
solid = true
|
|
else
|
|
time = multi.timer(sRef.Act,sRef)
|
|
sRef.solid = true
|
|
solid = false
|
|
end
|
|
if Loop[_D] and not solid then
|
|
if time == 0 then
|
|
Loop[_D].Priority = p_c
|
|
else
|
|
Loop[_D].Priority = P_LB
|
|
end
|
|
end
|
|
else
|
|
if Loop[_D].solid then
|
|
Loop[_D]:Act()
|
|
solid = true
|
|
else
|
|
time, status, err=multi.timer(pcall,Loop[_D].Act,Loop[_D])
|
|
Loop[_D].solid = true
|
|
solid = false
|
|
end
|
|
if Loop[_D] and not solid then
|
|
if time == 0 then
|
|
Loop[_D].Priority = p_c
|
|
else
|
|
Loop[_D].Priority = P_LB
|
|
end
|
|
end
|
|
if err then
|
|
Loop[_D].error=err
|
|
self.OnError:Fire(Loop[_D],err)
|
|
if stopOnError then
|
|
Loop[_D]:Destroy()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
PStep=PStep+1
|
|
if PStep>p_i then
|
|
PStep=0
|
|
if clock()-lastTime>delay then
|
|
lastTime = clock()
|
|
for i = 1,#Loop do
|
|
Loop[i]:ResetPriority()
|
|
end
|
|
end
|
|
end
|
|
else
|
|
for _D=#Loop,1,-1 do
|
|
if Loop[_D] then
|
|
if Loop[_D].Active then
|
|
self.CID=_D
|
|
if not protect then
|
|
Loop[_D]:Act()
|
|
else
|
|
local status, err=pcall(Loop[_D].Act,Loop[_D])
|
|
if err then
|
|
Loop[_D].error=err
|
|
self.OnError:Fire(Loop[_D],err)
|
|
if stopOnError then
|
|
Loop[_D]:Destroy()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
else
|
|
return "Already Running!"
|
|
end
|
|
end
|
|
function multi:uManager(settings)
|
|
multi.OnPreLoad:Fire()
|
|
multi.defaultSettings = settings or multi.defaultSettings
|
|
self.t,self.tt = clock(),0
|
|
if settings then
|
|
priority = settings.priority
|
|
if settings.auto_priority then
|
|
priority = -1
|
|
end
|
|
if settings.preLoop then
|
|
settings.preLoop(self)
|
|
end
|
|
if settings.stopOnError then
|
|
stopOnError = settings.stopOnError
|
|
end
|
|
multi.defaultSettings.p_i = self.Priority_Idle
|
|
if settings.auto_stretch then
|
|
multi.defaultSettings.p_i = settings.auto_stretch*self.Priority_Idle
|
|
end
|
|
multi.defaultSettings.delay = settings.auto_delay or 3
|
|
multi.defaultSettings.auto_lowerbound = settings.auto_lowerbound or self.Priority_Idle
|
|
protect = settings.protect
|
|
end
|
|
self.uManager=self.uManagerRef
|
|
end
|
|
function multi:uManagerRef(settings)
|
|
if self.Active then
|
|
if next then
|
|
local DD = table.remove(next,1)
|
|
while DD do
|
|
DD()
|
|
DD = table.remove(next,1)
|
|
end
|
|
end
|
|
local Loop=self.Mainloop
|
|
local PS=self
|
|
if multi.defaultSettings.priority==1 then
|
|
for _D=#Loop,1,-1 do
|
|
for P=1,7 do
|
|
if Loop[_D] then
|
|
if (PS.PList[P])%Loop[_D].Priority==0 then
|
|
if Loop[_D].Active then
|
|
self.CID=_D
|
|
if not multi.defaultSettings.protect then
|
|
Loop[_D]:Act()
|
|
else
|
|
local status, err=pcall(Loop[_D].Act,Loop[_D])
|
|
if err then
|
|
Loop[_D].error=err
|
|
self.OnError:Fire(Loop[_D],err)
|
|
if multi.defaultSettings.stopOnError then
|
|
Loop[_D]:Destroy()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
elseif multi.defaultSettings.priority==2 then
|
|
for _D=#Loop,1,-1 do
|
|
if Loop[_D] then
|
|
if (PS.PStep)%Loop[_D].Priority==0 then
|
|
if Loop[_D].Active then
|
|
self.CID=_D
|
|
if not multi.defaultSettings.protect then
|
|
Loop[_D]:Act()
|
|
else
|
|
local status, err=pcall(Loop[_D].Act,Loop[_D])
|
|
if err then
|
|
Loop[_D].error=err
|
|
self.OnError:Fire(Loop[_D],err)
|
|
if multi.defaultSettings.stopOnError then
|
|
Loop[_D]:Destroy()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
PS.PStep=PS.PStep+1
|
|
if PS.PStep>self.Priority_Idle then
|
|
PS.PStep=0
|
|
end
|
|
elseif priority == 3 then
|
|
self.tt = clock()-self.t
|
|
self.t = clock()
|
|
for _D=#Loop,1,-1 do
|
|
if Loop[_D] then
|
|
if Loop[_D].Priority == self.Priority_Core or (Loop[_D].Priority == self.Priority_High and tt<.5) or (Loop[_D].Priority == self.Priority_Above_Normal and tt<.125) or (Loop[_D].Priority == self.Priority_Normal and tt<.063) or (Loop[_D].Priority == self.Priority_Below_Normal and tt<.016) or (Loop[_D].Priority == self.Priority_Low and tt<.003) or (Loop[_D].Priority == self.Priority_Idle and tt<.001) then
|
|
if Loop[_D].Active then
|
|
self.CID=_D
|
|
if not protect then
|
|
Loop[_D]:Act()
|
|
else
|
|
local status, err=pcall(Loop[_D].Act,Loop[_D])
|
|
if err then
|
|
Loop[_D].error=err
|
|
self.OnError:Fire(Loop[_D],err)
|
|
if multi.defaultSettings.stopOnError then
|
|
Loop[_D]:Destroy()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
elseif priority == -1 then
|
|
for _D=#Loop,1,-1 do
|
|
local sRef = Loop[_D]
|
|
if Loop[_D] then
|
|
if (sRef.Priority == self.Priority_Core) or PStep==0 then
|
|
if sRef.Active then
|
|
self.CID=_D
|
|
if not protect then
|
|
if sRef.solid then
|
|
sRef:Act()
|
|
solid = true
|
|
else
|
|
time = multi.timer(sRef.Act,sRef)
|
|
sRef.solid = true
|
|
solid = false
|
|
end
|
|
if Loop[_D] and not solid then
|
|
if time == 0 then
|
|
Loop[_D].Priority = self.Priority_Core
|
|
else
|
|
Loop[_D].Priority = multi.defaultSettings.auto_lowerbound
|
|
end
|
|
end
|
|
else
|
|
if Loop[_D].solid then
|
|
Loop[_D]:Act()
|
|
solid = true
|
|
else
|
|
time, status, err=multi.timer(pcall,Loop[_D].Act,Loop[_D])
|
|
Loop[_D].solid = true
|
|
solid = false
|
|
end
|
|
if Loop[_D] and not solid then
|
|
if time == 0 then
|
|
Loop[_D].Priority = self.Priority_Core
|
|
else
|
|
Loop[_D].Priority = multi.defaultSettings.auto_lowerbound
|
|
end
|
|
end
|
|
if err then
|
|
Loop[_D].error=err
|
|
self.OnError:Fire(Loop[_D],err)
|
|
if multi.defaultSettings.stopOnError then
|
|
Loop[_D]:Destroy()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
self.PStep=self.PStep+1
|
|
if self.PStep>multi.defaultSettings.p_i then
|
|
self.PStep=0
|
|
if clock()-self.lastTime>multi.defaultSettings.delay then
|
|
self.lastTime = clock()
|
|
for i = 1,#Loop do
|
|
Loop[i]:ResetPriority()
|
|
end
|
|
end
|
|
end
|
|
else
|
|
for _D=#Loop,1,-1 do
|
|
if Loop[_D] then
|
|
if Loop[_D].Active then
|
|
self.CID=_D
|
|
if not multi.defaultSettings.protect then
|
|
Loop[_D]:Act()
|
|
else
|
|
local status, err=pcall(Loop[_D].Act,Loop[_D])
|
|
if err then
|
|
Loop[_D].error=err
|
|
self.OnError:Fire(Loop[_D],err)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
-- State Saving Stuff
|
|
function multi:IngoreObject()
|
|
self.Ingore=true
|
|
return self
|
|
end
|
|
function multi:ToString()
|
|
if self.Ingore then return end
|
|
local t=self.Type
|
|
local data;
|
|
print(t)
|
|
if t:sub(-6)=="Thread" then
|
|
data={
|
|
Type=t,
|
|
rest=self.rest,
|
|
updaterate=self.updaterest,
|
|
restrate=self.restrate,
|
|
name=self.name,
|
|
func=self.func,
|
|
important=self.important,
|
|
Active=self.Active,
|
|
ender=self.ender,
|
|
held=self.held,
|
|
}
|
|
else
|
|
data={
|
|
Type=t,
|
|
func=self.func,
|
|
funcTM=self.funcTM,
|
|
funcTMR=self.funcTMR,
|
|
important=self.important,
|
|
ender=self.ender,
|
|
held=self.held,
|
|
}
|
|
end
|
|
if t=="eventThread" or t=="event" then
|
|
table.merge(data,{
|
|
Task=self.Task,
|
|
})
|
|
elseif t=="loopThread" or t=="loop" then
|
|
table.merge(data,{
|
|
Start=self.Start,
|
|
})
|
|
elseif t=="stepThread" or t=="step" then
|
|
table.merge(data,{
|
|
funcE=self.funcE,
|
|
funcS=self.funcS,
|
|
pos=self.pos,
|
|
endAt=self.endAt,
|
|
start=self.start,
|
|
spos=self.spos,
|
|
skip=self.skip,
|
|
count=self.count,
|
|
})
|
|
elseif t=="tloopThread" then
|
|
table.merge(data,{
|
|
restN=self.restN,
|
|
})
|
|
elseif t=="tloop" then
|
|
table.merge(data,{
|
|
set=self.set,
|
|
life=self.life,
|
|
})
|
|
elseif t=="tstepThread" or t=="tstep" then
|
|
table.merge(data,{
|
|
funcE=self.funcE,
|
|
funcS=self.funcS,
|
|
pos=self.pos,
|
|
endAt=self.endAt,
|
|
start=self.start,
|
|
spos=self.spos,
|
|
skip=self.skip,
|
|
count=self.count,
|
|
timer=self.timer,
|
|
set=self.set,
|
|
reset=self.reset,
|
|
})
|
|
elseif t=="updaterThread" or t=="updater" then
|
|
table.merge(data,{
|
|
pos=self.pos,
|
|
skip=self.skip,
|
|
})
|
|
elseif t=="alarmThread" or t=="alarm" then
|
|
table.merge(data,{
|
|
set=self.set,
|
|
})
|
|
elseif t=="watcher" then
|
|
print("Currently cannot sterilize a watcher object!")
|
|
-- needs testing
|
|
-- table.merge(data,{
|
|
-- ns=self.ns,
|
|
-- n=self.n,
|
|
-- cv=self.cv,
|
|
-- })
|
|
elseif t=="timemaster" then
|
|
-- Weird stuff is going on here!
|
|
-- Need to do some testing
|
|
table.merge(data,{
|
|
timer=self.timer,
|
|
_timer=self._timer,
|
|
set=self.set,
|
|
link=self.link,
|
|
})
|
|
elseif t=="process" or t=="mainprocess" then
|
|
local loop=self.Mainloop
|
|
local dat={}
|
|
for i=1,#loop do
|
|
local ins=loop[i]:ToString()
|
|
if ins~=nil then
|
|
table.insert(dat,ins)
|
|
end
|
|
end
|
|
local str=bin.new()
|
|
str:addBlock({Type=t})
|
|
str:addBlock(#dat,4,"n")
|
|
for i=1,#dat do
|
|
str:addBlock(#dat[i],4,"n")
|
|
str:addBlock(dat[i])
|
|
end
|
|
return str.data
|
|
end
|
|
for i,v in pairs(self.important) do
|
|
data[v]=self[v]
|
|
end
|
|
local str=bin.new()
|
|
str:addBlock(data)
|
|
return str.data
|
|
end
|
|
function multi:newFromString(str)
|
|
if type(str)=="table" then
|
|
if str.Type=="bin" then
|
|
str=str.data
|
|
end
|
|
end
|
|
local handle=bin.new(str)
|
|
local data=handle:getBlock("t")
|
|
local t=data.Type
|
|
if t=="mainprocess" then
|
|
local objs=handle:getBlock("n",4)
|
|
for i=1,objs do
|
|
self:newFromString(handle:getBlock("s",(handle:getBlock("n",4))))
|
|
end
|
|
return self
|
|
elseif t=="process" then
|
|
local temp=multi:newProcessor()
|
|
local objs=handle:getBlock("n",4)
|
|
for i=1,objs do
|
|
temp:newFromString(handle:getBlock("s",(handle:getBlock("n",4))))
|
|
end
|
|
return temp
|
|
elseif t=="step" then -- GOOD
|
|
local item=self:newStep()
|
|
table.merge(item,data)
|
|
return item
|
|
elseif t=="tstep" then -- GOOD
|
|
local item=self:newTStep()
|
|
table.merge(item,data)
|
|
return item
|
|
elseif t=="tloop" then -- GOOD
|
|
local item=self:newTLoop()
|
|
table.merge(item,data)
|
|
return item
|
|
elseif t=="event" then -- GOOD
|
|
local item=self:newEvent(data.task)
|
|
table.merge(item,data)
|
|
return item
|
|
elseif t=="alarm" then -- GOOD
|
|
local item=self:newAlarm()
|
|
table.merge(item,data)
|
|
return item
|
|
elseif t=="watcher" then -- NEEDS TESTING
|
|
local item=self:newWatcher()
|
|
table.merge(item,data)
|
|
return item
|
|
elseif t=="updater" then -- GOOD
|
|
local item=self:newUpdater()
|
|
table.merge(item,data)
|
|
return item
|
|
elseif t=="loop" then -- GOOD
|
|
local item=self:newLoop()
|
|
table.merge(item,data)
|
|
return item
|
|
end
|
|
end
|
|
function multi:Important(varname)
|
|
table.insert(important,varname)
|
|
end
|
|
function multi:ToFile(path)
|
|
bin.new(self:ToString()):tofile(path)
|
|
end
|
|
function multi:fromFile(path)
|
|
self:newFromString(bin.load(path))
|
|
end
|
|
function multi:SetStateFlag(opt)
|
|
--
|
|
end
|
|
function multi:quickStateSave(b)
|
|
--
|
|
end
|
|
function multi:saveState(path,opt)
|
|
--
|
|
end
|
|
function multi:loadState(path)
|
|
--
|
|
end
|
|
function multi:setDefualtStateFlag(opt)
|
|
--
|
|
end
|
|
return multi
|