Added files

This commit is contained in:
Ryan 2017-07-11 23:14:50 -04:00
parent 48eb46ec50
commit ca2259a7df
58 changed files with 20224 additions and 0 deletions

39
LoveKaraoke/conf.lua Normal file
View File

@ -0,0 +1,39 @@
function love.conf(t)
t.identity = nil -- The name of the save directory (string)
t.version = "0.10.2" -- The LOVE version this game was made for (string)
t.console = true -- Attach a console (boolean, Windows only)
t.window.title = "Recording Test" -- The window title (string)
t.window.icon = nil -- Filepath to an image to use as the window's icon (string)
t.window.width = 300 -- The window width (number)
t.window.height = 100 -- The window height (number)
t.window.borderless = false -- Remove all border visuals from the window (boolean)
t.window.resizable = false -- Let the window be user-resizable (boolean)
t.window.minwidth = 1 -- Minimum window width if the window is resizable (number)
t.window.minheight = 1 -- Minimum window height if the window is resizable (number)
t.window.fullscreen = false -- Enable fullscreen (boolean)
t.window.fullscreentype = "desktop" -- Standard fullscreen or desktop fullscreen mode (string)
t.window.vsync = false -- Enable vertical sync (boolean)
t.window.fsaa = 0 -- The number of samples to use with multi-sampled antialiasing (number)
t.window.display = 1 -- Index of the monitor to show the window in (number)
t.window.highdpi = false -- Enable high-dpi mode for the window on a Retina display (boolean)
t.window.srgb = false -- Enable sRGB gamma correction when drawing to the screen (boolean)
t.window.x = nil -- The x-coordinate of the window's position in the specified display (number)
t.window.y = nil -- The y-coordinate of the window's position in the specified display (number)
t.modules.audio = true -- Enable the audio module (boolean)
t.modules.event = true -- Enable the event module (boolean)
t.modules.graphics = true -- Enable the graphics module (boolean)
t.modules.image = true -- Enable the image module (boolean)
t.modules.joystick = true -- Enable the joystick module (boolean)
t.modules.keyboard = true -- Enable the keyboard module (boolean)
t.modules.math = true -- Enable the math module (boolean)
t.modules.mouse = true -- Enable the mouse module (boolean)
t.modules.physics = true -- Enable the physics module (boolean)
t.modules.sound = true -- Enable the sound module (boolean)
t.modules.system = true -- Enable the system module (boolean)
t.modules.timer = true -- Enable the timer module (boolean)
t.modules.window = true -- Enable the window module (boolean)
t.modules.thread = true -- Enable the thread module (boolean)
end
--1440 x 2560

View File

@ -0,0 +1,212 @@
audio = {}
audio.__index = audio
function audio:new(f,t)
local obj={}
setmetatable(obj, audio)
obj.source=love.audio.newSource(f,t)
obj.f=f
obj.t=t or "stream"
obj.endEvent=multi:newLoop()
obj.endEvent.Pare=obj
obj.wasPlaying=false
obj.func={}
obj.func2={}
obj.func3={}
obj.func4={}
obj.endEvent:OnLoop(function(time,loop)
if not(loop.Pare:isPlaying()) and loop.Pare.wasPlaying==true and not(loop.Pare:isPaused()) then
for i=1,#loop.Pare.func do
loop.Pare:stop()
loop.Pare.wasPlaying=false
loop.Pare.endEvent:Pause()
loop.Pare.func[i](loop.Pare)
end
end
end)
obj.endEvent:Pause()
return obj
end
function audio:clone()
local _temp=audio:new(self.f,self.t)
_temp.source=self.source:clone()
return _temp
end
--Mutators
function audio:play()
if self:isPaused() then
for i=1,#self.func4 do
self.func4[i](self)
end
self:resume()
else
for i=1,#self.func3 do
self.func3[i](self)
end
self.source:play()
self.wasPlaying=true
self.endEvent:Resume()
end
end
function audio:stop()
self.source:stop()
self.wasPlaying=true
self.endEvent:Pause()
end
function audio:pause()
for i=1,#self.func2 do
self.func2[i](self)
end
self.source:pause()
end
function audio:resume()
self.source:resume()
end
function audio:rewind()
self.source:rewind()
end
function audio:setAttenuationDistances(r,m)
self.source:setAttenuationDistances(r,m)
end
function audio:setCone(innerAngle, outerAngle, outerVolume)
self.source:setCone(innerAngle, outerAngle, outerVolume)
end
function audio:setDirection(x, y, z)
self.source:setDirection(x, y, z)
end
function audio:setLooping(loop)
self.source:setLooping(loop)
end
function audio:setPitch(pitch)
self.source:setPitch(pitch)
end
function audio:setPosition(x, y, z)
self.source:setPosition(x, y, z)
end
function audio:setRelative(enable)
self.source:setRelative(enable)
end
function audio:setRolloff(rolloff)
self.source:setRolloff(rolloff)
end
function audio:setVelocity(x, y, z)
self.source:setVelocity(x, y, z)
end
function audio:setVolume(volume)
self.source:setVolume(volume)
end
function audio:setVolumeLimits(min, max)
self.source:setVolumeLimits(min, max)
end
function audio:seek(offset,unit)
self.source:seek(offset,unit)
end
--Assessors
function audio:isPlaying()
return self.source:isPlaying()
end
function audio:isPaused()
return self.source:isPaused()
end
function audio:isStopped()
return self.source:isStopped()
end
function audio:isLooping()
return self.source:isLooping()
end
function audio:isStatic()
return self.source:isStatic()
end
function audio:isRelative()
return self.source:isRelative()
end
function audio:getAttenuationDistances()
return self.source:getAttenuationDistances()
end
function audio:getChannels()
return self.source:getChannels()
end
function audio:getCone()
return self.source:getCone()
end
function audio:getDirection()
return self.source:getDirection()
end
function audio:getPitch()
return self.source:getPitch()
end
function audio:getPosition()
return self.source:getPosition()
end
function audio:getRolloff()
return self.source:getRolloff()
end
function audio:getVelocity()
return self.source:getVelocity()
end
function audio:getVolume()
return self.source:getVolume()
end
function audio:getVolumeLimits()
return self.source:getVolumeLimits()
end
function audio:tell(unit)
return self.source:tell(unit)
end
function audio:type()
return self.source:type()
end
function audio:typeOf()
return self.source:typeOf()
end
--Events
function audio:onResume(func)
table.insert(self.func4,func)
end
function audio:onPlay(func)
table.insert(self.func3,func)
end
function audio:onPause(func)
table.insert(self.func2,func)
end
function audio:onStop(func)
table.insert(self.func,func)
end
--[[
Object:type |Done
Object:typeOf |Done
Source:clone |Done
Source:getAttenuationDistances |Done
Source:getChannels |Done
Source:getCone |Done
Source:getDirection |Done
Source:getPitch |Done
Source:getPosition |Done
Source:getRolloff |Done
Source:getVelocity |Done
Source:getVolume |Done
Source:getVolumeLimits |Done
Source:isLooping |Done
Source:isPaused |Done
Source:isPlaying |Done
Source:isRelative |Done
Source:isStatic |Done
Source:isStopped |Done
Source:pause |Done
Source:play |Done
Source:resume |Done
Source:rewind |Done
Source:seek |Done
Source:setAttenuationDistances |Done
Source:setCone |Done
Source:setDirection |Done
Source:setLooping |Done
Source:setPitch |Done
Source:setPosition |Done
Source:setRelative |Done
Source:setRolloff |Done
Source:setVelocity |Done
Source:setVolume |Done
Source:setVolumeLimits |Done
Source:stop |Done
Source:tell |Done
]]

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,319 @@
if table.unpack then
unpack=table.unpack
end
function table.val_to_str ( v )
if "string" == type( v ) then
v = string.gsub( v, "\n", "\\n" )
if string.match( string.gsub(v,"[^'\"]",""), '^"+$' ) then
return "'" .. v .. "'"
end
return '"' .. string.gsub(v,'"', '\\"' ) .. '"'
else
return "table" == type( v ) and table.tostring( v ) or
tostring( v )
end
end
function table.key_to_str ( k )
if "string" == type( k ) and string.match( k, "^[_%a][_%a%d]*$" ) then
return k
else
return "[" .. table.val_to_str( k ) .. "]"
end
end
function table.tostring( tbl )
local result, done = {}, {}
for k, v in ipairs( tbl ) do
table.insert( result, table.val_to_str( v ) )
done[ k ] = true
end
for k, v in pairs( tbl ) do
if not done[ k ] then
table.insert( result,
table.key_to_str( k ) .. "=" .. table.val_to_str( v ) )
end
end
return "{" .. table.concat( result, "," ) .. "}"
end
function table.merge(t1, t2)
t1,t2= t1 or {},t2 or {}
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
Library={}
function Library.optimize(func)
local test=Library.convert(func)
rawset(test,"link",{})
rawset(test,"last","")
rawset(test,"org",func)
test:inject(function(...)
rawset(test,"last",table.tostring({...}))
if test.link[test.last]~=nil then
return Library.forceReturn(unpack(test.link[test.last]))
end
return {...}
end,1)
test:inject(function(...)
test.link[test.last]={test.org(...)}
return test.org(...)
end)
return test
end
function Library.forceReturn(...)
return {[0]="\1\7\6\3\2\99\125",...}
end
function Library.inject(lib,dat,arg)
if type(lib)=="table" then
if type(dat)=="table" then
table.merge(lib,dat)
elseif type(dat)=="string" then
if lib.Version and dat:match("(%d-)%.(%d-)%.(%d-)") then
lib.Version={dat:match("(%d+)%.(%d+)%.(%d+)")}
elseif dat=="meta" and type(arg)=="table" then
local _mt=getmetatable(lib) or {}
local mt={}
table.merge(mt,arg)
table.merge(_mt,mt)
setmetatable(lib,_mt)
elseif dat=="compat" then
lib["getVersion"]=function(self) return self.Version[1].."."..self.Version[2].."."..self.Version[3] end
if not lib.Version then
lib.Version={1,0,0}
end
elseif dat=="inhert" then
if not(lib["!%"..arg.."%!"]) then print("Wrong Password!!") return end
lib["!%"..arg.."%!"].__index=lib["!!%"..arg.."%!!"]
end
elseif type(dat)=="function" then
for i,v in pairs(lib) do
dat(lib,i,v)
end
end
elseif type(lib)=="function" or type(lib)=="userdata" then
if lib==unpack then
print("function unpack cannot yet be injected!")
return unpack
elseif lib==pairs then
print("function pairs cannot yet be injected!")
return lib
elseif lib==ipairs then
print("function ipairs cannot yet be injected!")
return lib
elseif lib==type then
print("function type cannot yet be injected!")
return lib
end
temp={}
local mt={
__call=function(t,...)
local consume,MainRet,init={},{},{...}
local tt={}
for i=1,#t.__Link do
tt={}
if t.__Link[i]==t.__Main then
if #consume~=0 then
MainRet={t.__Link[i](unpack(consume))}
else
MainRet={t.__Link[i](unpack(init))}
end
else
if i==1 then
consume=(t.__Link[i](unpack(init)))
else
if type(MainRet)=="table" then
table.merge(tt,MainRet)
end
if type(consume)=="table" then
table.merge(tt,consume)
end
consume={t.__Link[i](unpack(tt))}
end
if i==#t.__Link then
return unpack(consume)
end
if consume then if consume[0]=="\1\7\6\3\2\99\125" then consume[0]=nil return unpack(consume) end end
end
end
if type(MainRet)=="table" then
table.merge(tt,MainRet)
end
if type(consume)=="table" then
table.merge(tt,consume)
end
return unpack(tt)
end,
}
temp.__Link={lib}
temp.__Main=lib
temp.__self=temp
function temp:inject(func,i)
if i then
table.insert(self.__Link,i,func)
else
table.insert(self.__Link,func)
end
end
function temp:consume(func)
for i=1,#self.__Link do
if self.__Link[i]==self.__Main then
self.__Link[i]=func
self.__self.__Main=func
return true
end
end
return false
end
setmetatable(temp,mt)
Library.protect(temp,"lolz")
return temp
else
return "arg1 must be a table or a function"
end
end
function Library.parse(lib)
for i,v in pairs(lib) do
print(i,v)
end
end
function Library.protect(lib,pass)
pass=pass or "*"
local mt={}
local test={
__index = lib,
__newindex = function(tab, key, value)
local t,b=key:find(tab["!%"..pass.."%!"].__pass,1,true)
if t then
local _k=key:sub(b+1)
rawset(tab,_k,value)
else
error("Cannot alter a protected library!")
end
end,
__metatable = false,
__pass=pass or "*"
}
local _mt=getmetatable(lib) or {}
table.merge(mt,_mt)
table.merge(mt,test)
lib["!%"..pass.."%!"]=test
lib["!!%"..pass.."%!!"]=lib
local temp=setmetatable({},mt)
for i,v in pairs(_G) do
if v==lib then
_G[i]=temp
Library(function(link)
link[i]=v
end)
end
end
end
function Library.unprotect(lib,pass)
if not(lib["!%"..pass.."%!"]) then print("Wrong Password or Library is not Protected!") return end
if lib["!%"..pass.."%!"].__pass==pass then
lib["!%"..pass.."%!"].__newindex=lib["!!%"..pass.."%!!"]
lib["!%"..pass.."%!"].__index=nil
lib["!%"..pass.."%!"].__newindex=nil
lib["!%"..pass.."%!"].__metatable = true
setmetatable(lib["!!%"..pass.."%!!"],lib["!%"..pass.."%!"])
for i,v in pairs(_G) do
if v==lib then
_G[i]=lib["!!%"..pass.."%!!"]
end
end
lib["!!%"..pass.."%!!"]["!%"..pass.."%!"]=nil
lib["!!%"..pass.."%!!"]["!!%"..pass.."%!!"]=nil
else
print("Wrong Password!!!")
end
end
function Library.addPoll(lib,polldata,ref)
lib.__polldata={}
Library.inject(lib.__polldata,polldata)
if type(ref)=="table" then
Library.inject(ref,"meta",{__newindex=function(t,k,v)
t[k].__polldata=polldata
end})
end
end
function Library.newPollData(t)
local temp={}
temp.__onPolled=function() end
temp.__pollData=false
temp.__advDisc=""
temp.__pollcalls=-1 -- infinte
for i,v in pairs(t) do
if type(v)=="string" then
temp.__advDisc=v
elseif type(v)=="number" then
temp.__pollcalls=v
elseif type(v)=="table" then
temp[v[1]]=v[2]
elseif type(v)=="function" then
temp.__onPolled=v
elseif type(v)=="boolean" then
temp.__pollData=v
else
temp.__userdata=v
end
end
return temp
end
function Library.convert(...)
local temp,rets={...},{}
for i=1,#temp do
if type(temp[i])=="function" then
table.insert(rets,Library.inject(temp[i]))
else
error("Takes only functions and returns in order from functions given. arg # "..i.." is not a function!!! It is a "..type(temp[i]))
end
end
return unpack(rets)
end
function Library.convertIn(...)
local temp,list={...},{}
for i=1,#temp do
if type(temp[i])=="table" then
for k,v in pairs(temp[i]) do
if type(v)=="function" then
temp[i][k]=Library.inject(temp[i][k])
end
end
else
error("Takes only tables! Arg "..i.." isn't it is a "..type(temp[i]))
end
end
end
function Library.newInjectedFunction()
return Library.convert(function(...) return unpack{...} end)
end
function Library.capulate(lib)
Library.inject(lib,"meta",{
__index=function(t,k,v)
for i,_v in pairs(t) do
if k:lower()==i:lower() then
return t[i]
end
end
end,
__newindex=function(t,k,v)
rawset(t,k:lower(),v)
end
})
end
local link={MainLibrary=Library}
Library.inject(Library,"meta",{
__Link=link,
__call=function(self,func) func(link) end,
})
--Library.protect(Library,"N@#P!KLkk1(93320")

View File

@ -0,0 +1,2 @@
These libraries Namely the MultiManager, bin, and the Library, libraries will be documented because i plan on sharing them
everything else will mostlikely not be documented

598
LoveKaraoke/core/T1.lua Normal file
View File

@ -0,0 +1,598 @@
require("love.timer")
require("love.system")
require("love.sound")
require("love.physics")
require("love.mouse")
require("love.math")
require("love.keyboard")
require("love.joystick")
require("love.image")
require("love.font")
require("love.filesystem")
require("love.event")
require("love.audio")
require("love.graphics")
require("love.window")
_defaultfont = love.graphics.getFont()
gui = {}
function gui.getTile(i,x,y,w,h)-- returns imagedata
if type(i)=="userdata" then
-- do nothing
else
error("getTile invalid args!!! Usage: ImageElement:getTile(x,y,w,h) or gui:getTile(imagedata,x,y,w,h)")
end
local iw,ih=i:getDimensions()
local id,_id=i:getData(),love.image.newImageData(w,h)
for _x=x,w+x-1 do
for _y=y,h+y-1 do
_id:setPixel(_x-x,_y-y,id:getPixel(_x,_y))
end
end
return love.graphics.newImage(_id)
end
multi = {}
multi.Version="4.0.0"
multi.__index = multi
multi.Mainloop={}
multi.Tasks={}
multi.Tasks2={}
multi.Garbage={}
multi.Children={}
multi.Paused={}
multi.MasterId=0
multi.Active=true
multi.Id=-1
multi.Type="MainInt"
multi.Rest=0
-- System
os.sleep=love.timer.sleep
function multi:newBase(ins)
if not(self.Type=="MainInt" or self.Type=="int") then error("Can only create an object on multi or an interface obj") return false end
local c = {}
if self.Type=="int" then
setmetatable(c, self.Parent)
else
setmetatable(c, self)
end
c.Active=true
c.func={}
c.Id=0
c.Act=function() end
c.Parent=self
if ins then
table.insert(self.Mainloop,ins,c)
else
table.insert(self.Mainloop,c)
end
self.MasterId=self.MasterId+1
return c
end
function multi:reboot(r)
self.Mainloop={}
self.Tasks={}
self.Tasks2={}
self.Garbage={}
self.Children={}
self.Paused={}
self.MasterId=0
self.Active=true
self.Id=-1
if r then
for i,v in pairs(_G) do
if type(i)=="table" then
if i.Parent and i.Id and i.Act then
i={}
end
end
end
end
end
function multi:getChildren()
return self.Mainloop
end
--Processor
function multi:Do_Order()
for _D=#self.Mainloop,1,-1 do
if self.Mainloop[_D]~=nil then
self.Mainloop[_D].Id=_D
self.Mainloop[_D]:Act()
end
if self.Mainloop[_D].rem then
table.remove(self.Mainloop,_D)
end
end
if self.Rest>0 then
os.sleep(self.Rest)
end
end
function multi:benchMark(sec)
local temp=self:newLoop(function(t,self)
if os.clock()-self.init>self.sec then
print(self.c.." steps in "..self.sec.." second(s)")
self.tt(self.sec)
self:Destroy()
else
self.c=self.c+1
end
end)
function temp:OnBench(func)
self.tt=func
end
self.tt=function() end
temp.sec=sec
temp.init=os.clock()
temp.c=0
return temp
end
function multi:newInterface()
if not(self.Type=="MainInt") 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.Id=0
c.Type="int"
c.Mainloop={}
c.Tasks={}
c.Tasks2={}
c.Garbage={}
c.Children={}
c.Paused={}
c.MasterId=0
c.Active=true
c.Id=-1
c.Rest=0
function c:Start()
if self.l then
self.l:Resume()
else
self.l=self.Parent:newLoop(function(dt) c:uManager(dt) end)
end
end
function c:Stop()
if self.l then
self.l:Pause()
end
end
function c:Remove()
self:Destroy()
self.l:Destroy()
end
return c
end
--Helpers
function multi:FreeMainEvent()
self.func={}
end
function multi:isPaused()
return not(self.Active)
end
function multi:Pause(n)
if self.Type=="int" or self.Type=="MainInt" then
self.Active=false
if not(n) then
local c=self:getChildren()
for i=1,#c do
c[i]:Pause()
end
else
self:hold(n)
end
else
if not(n) then
self.Active=false
if self.Parent.Mainloop[self.Id]~=nil then
table.remove(self.Parent.Mainloop,self.Id)
table.insert(self.Parent.Paused,self)
self.Id=#self.Parent.Paused
end
else
self:hold(n)
end
end
end
function multi:Resume()
if self.Type=="int" or self.Type=="MainInt" then
self.Active=true
local c=self:getChildren()
for i=1,#c do
c[i]:Resume()
end
else
if self:isPaused() then
self.Active=true
for i=1,#self.Parent.Paused do
if self.Parent.Paused[i]==self then
table.remove(self.Parent.Paused,i)
return
end
end
table.insert(self.Parent.Mainloop,self)
end
end
end
function multi:Destroy()
if self.Type=="int" or self.Type=="MainInt" then
local c=self:getChildren()
for i=1,#c do
c[i]:Destroy()
end
else
self.rem=true
end
end
function multi:hold(task)
self:Pause()
if type(task)=="number" then
local alarm=self:newAlarm(task)
while alarm.Active==true do
if love then
self.Parent.lManager()
else
self.Parent.Do_Order()
end
end
alarm:Destroy()
self:Resume()
elseif type(task)=="function" then
local env=self.Parent:newEvent(task)
env:OnEvent(function(envt) envt:Pause() envt:Stop() end)
while env.Active do
if love then
self.Parent.lManager()
else
self.Parent.Do_Order()
end
end
env:Destroy()
self:Resume()
else
print("Error Data Type!!!")
end
end
function multi:oneTime(func,...)
if not(self.Type=="MainInt" or self.Type=="int") then
for _k=1,#self.Parent.Tasks2 do
if self.Parent.Tasks2[_k]==func then
return false
end
end
table.insert(self.Parent.Tasks2,func)
func(...)
return true
else
for _k=1,#self.Tasks2 do
if self.Tasks2[_k]==func then
return false
end
end
table.insert(self.Tasks2,func)
func(...)
return true
end
end
--Constructors
function multi:newEvent(task)
local c=self:newBase()
c.Type="Event"
c.Task=task or function() end
function c:Act()
if self.Task(self) and self.Active==true then
self:Pause()
for _E=1,#self.func do
self.func[_E](self)
end
end
end
function c:OnEvent(func)
table.insert(self.func,func)
end
return c
end
function multi:newAlarm(set)
local c=self:newBase()
c.Type="Alarm"
c.timer=os.clock()
c.set=set or 0
function c:Act()
if self.Active==true then
if os.clock()-self.timer>=self.set then
self:Pause()
for i=1,#self.func do
self.func[i](self)
end
end
end
end
function c:Reset(n)
if n then self.set=n end
self.timer=os.clock()
self:Resume()
end
function c:OnRing(func)
table.insert(self.func,func)
end
return c
end
function multi:newTask(func)
table.insert(self.Tasks,func)
end
function multi:newLoop(func)
local c=self:newBase()
c.Type="Loop"
if func then
c.func={func}
end
function c:Act()
if self.Active==true then
for i=1,#self.func do
self.func[i](os.clock()-self.Parent.Start,self)
end
end
end
function c:OnLoop(func)
table.insert(self.func,func)
end
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.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.Active==true then
for i=1,#self.func do
self.func[i](self.pos,self)
end
self.pos=self.pos+self.count
end
end
end
self.spos=self.spos+1
if self.spos>=self.skip then
self.spos=0
end
end
function c:OnStep(func)
table.insert(self.func,1,func)
end
function c:OnEnd(func)
table.insert(self.funcE,func)
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()
end
c:OnStep(function(p,s)
if s.count>0 and s.endAt==p then
for fe=1,#s.funcE do
s.funcE[fe](s)
end
s.pos=s.start-1
elseif s.count<0 and s.endAt==p then
for fe=1,#s.funcE do
s.funcE[fe](s)
end
s.pos=s.start-1
end
end)
return c
end
function multi:newTStep(start,reset,count,set)
local c=self:newBase()
think=1
c.Type="TStep"
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=os.clock()
c.set=set or 1
function c:Update(start,reset,count,set)
self.start=start or self.start
self.pos=start
self.endAt=reset or self.endAt
self.set=set or self.set
self.count=count or self.count or 1
self.timer=os.clock()
self:Resume()
end
function c:Act()
if self.Active then
if os.clock()-self.timer>=self.set then
self:Reset()
for i=1,#self.func do
self.func[i](self.pos,self)
end
if self.endAt==self.pos then
for fe=1,#self.funcE do
self.funcE[fe](self)
end
self.pos=self.start-1
end
self.pos=self.pos+self.count
end
end
end
function c:OnEnd(func)
table.insert(self.funcE,func)
end
function c:Reset(n)
if n then self.set=n end
self.timer=os.clock()
self:Resume()
end
function c:OnStep(func)
table.insert(self.func,func)
end
return c
end
function multi:newTrigger(func)
local c=self:newBase()
c.Type="Trigger"
c.trigfunc=func or function() end
function c:Fire(...)
self:trigfunc(self,...)
end
return c
end
--Managers
function multi:mainloop()
for i=1,#self.Tasks do
self.Tasks[i](self)
end
self.Start=os.clock()
while self.Active do
self:Do_Order()
end
end
function multi._tFunc(self,dt)
for i=1,#self.Tasks do
self.Tasks[i](self)
end
print("once!")
if dt then
self.pump=true
end
self.pumpvar=dt
self.Start=os.clock()
end
function multi:uManager(dt)
self:oneTime(self._tFunc,self,dt)
self:Do_Order()
end
multi.drawF={}
function multi:dManager()
for ii=1,#multi.drawF do
multi.drawF[ii]()
end
end
function multi:onDraw(func)
table.insert(self.drawF,func)
end
function multi:lManager()
if love.event then
love.event.pump()
for e,a,b,c,d in love.event.poll() do
if e == "quit" then
if not love.quit or not love.quit() then
if love.audio then
love.audio.stop()
end
return nil
end
end
love.handlers[e](a,b,c,d)
end
end
if love.timer then
love.timer.step()
dt = love.timer.getDelta()
end
if love.update then love.update(dt) end
multi:uManager(dt)
if love.window and love.graphics and love.window.isCreated() then
love.graphics.clear()
love.graphics.origin()
if love.draw then love.draw() end
multi.dManager()
love.graphics.setColor(255,255,255,255)
if multi.draw then multi.draw() end
love.graphics.present()
end
end
Thread={}
Thread.Name="Thread 1"
Thread.ChannelThread = love.thread.getChannel("Easy1")
Thread.ChannelMain = love.thread.getChannel("EasyMain")
Thread.Global = {}
function Thread:packTable(G)
function escapeStr(str)
local temp=""
for i=1,#str do
temp=temp.."\\"..string.byte(string.sub(str,i,i))
end
return temp
end
function ToStr(t)
local dat="{"
for i,v in pairs(t) do
if type(i)=="number" then
i="["..i.."]="
else
i=i.."="
end
if type(v)=="string" then
dat=dat..i.."\""..v.."\","
elseif type(v)=="number" then
dat=dat..i..v..","
elseif type(v)=="boolean" then
dat=dat..i..tostring(v)..","
elseif type(v)=="table" and not(G==v) then
dat=dat..i..ToStr(v)..","
--elseif type(v)=="table" and G==v then
-- dat=dat..i.."assert(loadstring(\"return self\")),"
elseif type(v)=="function" then
dat=dat..i.."assert(loadstring(\""..escapeStr(string.dump(v)).."\")),"
end
end
return string.sub(dat,1,-2).."}"
end
return "return "..ToStr(G)
end
function Thread:Send(name,var)
arg3="1"
if type(var)=="table" then
var=Thread:packTable(var)
arg3="table"
end
self.ChannelMain:push({name,var,arg3})
end
function Thread:UnPackChannel()
local c=self.ChannelThread:getCount()
for i=1,c do
local temp=self.ChannelThread:pop()
if temp[1] and temp[2] then
if temp[1]=="func" and type(temp[2])=="string" then
loadstring(temp[2])(temp[3])
elseif temp[1]=="table" then
_G[temp[3]]=loadstring(temp[2])()
else
_G[temp[1]]=temp[2]
end
end
end
if #multi:getChildren()<2 then
os.sleep(.05)
end
end
function Thread:boost(func,name)
self:Send(name,string.dump(func))
end
function Thread.mainloop()
Thread:UnPackChannel()
end
Thread.MainThread=false
multi:newLoop():OnLoop(Thread.mainloop)
multi:mainloop()

598
LoveKaraoke/core/T2.lua Normal file
View File

@ -0,0 +1,598 @@
require("love.timer")
require("love.system")
require("love.sound")
require("love.physics")
require("love.mouse")
require("love.math")
require("love.keyboard")
require("love.joystick")
require("love.image")
require("love.font")
require("love.filesystem")
require("love.event")
require("love.audio")
require("love.graphics")
require("love.window")
_defaultfont = love.graphics.getFont()
gui = {}
function gui.getTile(i,x,y,w,h)-- returns imagedata
if type(i)=="userdata" then
-- do nothing
else
error("getTile invalid args!!! Usage: ImageElement:getTile(x,y,w,h) or gui:getTile(imagedata,x,y,w,h)")
end
local iw,ih=i:getDimensions()
local id,_id=i:getData(),love.image.newImageData(w,h)
for _x=x,w+x-1 do
for _y=y,h+y-1 do
_id:setPixel(_x-x,_y-y,id:getPixel(_x,_y))
end
end
return love.graphics.newImage(_id)
end
multi = {}
multi.Version="4.0.0"
multi.__index = multi
multi.Mainloop={}
multi.Tasks={}
multi.Tasks2={}
multi.Garbage={}
multi.Children={}
multi.Paused={}
multi.MasterId=0
multi.Active=true
multi.Id=-1
multi.Type="MainInt"
multi.Rest=0
-- System
os.sleep=love.timer.sleep
function multi:newBase(ins)
if not(self.Type=="MainInt" or self.Type=="int") then error("Can only create an object on multi or an interface obj") return false end
local c = {}
if self.Type=="int" then
setmetatable(c, self.Parent)
else
setmetatable(c, self)
end
c.Active=true
c.func={}
c.Id=0
c.Act=function() end
c.Parent=self
if ins then
table.insert(self.Mainloop,ins,c)
else
table.insert(self.Mainloop,c)
end
self.MasterId=self.MasterId+1
return c
end
function multi:reboot(r)
self.Mainloop={}
self.Tasks={}
self.Tasks2={}
self.Garbage={}
self.Children={}
self.Paused={}
self.MasterId=0
self.Active=true
self.Id=-1
if r then
for i,v in pairs(_G) do
if type(i)=="table" then
if i.Parent and i.Id and i.Act then
i={}
end
end
end
end
end
function multi:getChildren()
return self.Mainloop
end
--Processor
function multi:Do_Order()
for _D=#self.Mainloop,1,-1 do
if self.Mainloop[_D]~=nil then
self.Mainloop[_D].Id=_D
self.Mainloop[_D]:Act()
end
if self.Mainloop[_D].rem then
table.remove(self.Mainloop,_D)
end
end
if self.Rest>0 then
os.sleep(self.Rest)
end
end
function multi:benchMark(sec)
local temp=self:newLoop(function(t,self)
if os.clock()-self.init>self.sec then
print(self.c.." steps in "..self.sec.." second(s)")
self.tt(self.sec)
self:Destroy()
else
self.c=self.c+1
end
end)
function temp:OnBench(func)
self.tt=func
end
self.tt=function() end
temp.sec=sec
temp.init=os.clock()
temp.c=0
return temp
end
function multi:newInterface()
if not(self.Type=="MainInt") 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.Id=0
c.Type="int"
c.Mainloop={}
c.Tasks={}
c.Tasks2={}
c.Garbage={}
c.Children={}
c.Paused={}
c.MasterId=0
c.Active=true
c.Id=-1
c.Rest=0
function c:Start()
if self.l then
self.l:Resume()
else
self.l=self.Parent:newLoop(function(dt) c:uManager(dt) end)
end
end
function c:Stop()
if self.l then
self.l:Pause()
end
end
function c:Remove()
self:Destroy()
self.l:Destroy()
end
return c
end
--Helpers
function multi:FreeMainEvent()
self.func={}
end
function multi:isPaused()
return not(self.Active)
end
function multi:Pause(n)
if self.Type=="int" or self.Type=="MainInt" then
self.Active=false
if not(n) then
local c=self:getChildren()
for i=1,#c do
c[i]:Pause()
end
else
self:hold(n)
end
else
if not(n) then
self.Active=false
if self.Parent.Mainloop[self.Id]~=nil then
table.remove(self.Parent.Mainloop,self.Id)
table.insert(self.Parent.Paused,self)
self.Id=#self.Parent.Paused
end
else
self:hold(n)
end
end
end
function multi:Resume()
if self.Type=="int" or self.Type=="MainInt" then
self.Active=true
local c=self:getChildren()
for i=1,#c do
c[i]:Resume()
end
else
if self:isPaused() then
self.Active=true
for i=1,#self.Parent.Paused do
if self.Parent.Paused[i]==self then
table.remove(self.Parent.Paused,i)
return
end
end
table.insert(self.Parent.Mainloop,self)
end
end
end
function multi:Destroy()
if self.Type=="int" or self.Type=="MainInt" then
local c=self:getChildren()
for i=1,#c do
c[i]:Destroy()
end
else
self.rem=true
end
end
function multi:hold(task)
self:Pause()
if type(task)=="number" then
local alarm=self:newAlarm(task)
while alarm.Active==true do
if love then
self.Parent.lManager()
else
self.Parent.Do_Order()
end
end
alarm:Destroy()
self:Resume()
elseif type(task)=="function" then
local env=self.Parent:newEvent(task)
env:OnEvent(function(envt) envt:Pause() envt:Stop() end)
while env.Active do
if love then
self.Parent.lManager()
else
self.Parent.Do_Order()
end
end
env:Destroy()
self:Resume()
else
print("Error Data Type!!!")
end
end
function multi:oneTime(func,...)
if not(self.Type=="MainInt" or self.Type=="int") then
for _k=1,#self.Parent.Tasks2 do
if self.Parent.Tasks2[_k]==func then
return false
end
end
table.insert(self.Parent.Tasks2,func)
func(...)
return true
else
for _k=1,#self.Tasks2 do
if self.Tasks2[_k]==func then
return false
end
end
table.insert(self.Tasks2,func)
func(...)
return true
end
end
--Constructors
function multi:newEvent(task)
local c=self:newBase()
c.Type="Event"
c.Task=task or function() end
function c:Act()
if self.Task(self) and self.Active==true then
self:Pause()
for _E=1,#self.func do
self.func[_E](self)
end
end
end
function c:OnEvent(func)
table.insert(self.func,func)
end
return c
end
function multi:newAlarm(set)
local c=self:newBase()
c.Type="Alarm"
c.timer=os.clock()
c.set=set or 0
function c:Act()
if self.Active==true then
if os.clock()-self.timer>=self.set then
self:Pause()
for i=1,#self.func do
self.func[i](self)
end
end
end
end
function c:Reset(n)
if n then self.set=n end
self.timer=os.clock()
self:Resume()
end
function c:OnRing(func)
table.insert(self.func,func)
end
return c
end
function multi:newTask(func)
table.insert(self.Tasks,func)
end
function multi:newLoop(func)
local c=self:newBase()
c.Type="Loop"
if func then
c.func={func}
end
function c:Act()
if self.Active==true then
for i=1,#self.func do
self.func[i](os.clock()-self.Parent.Start,self)
end
end
end
function c:OnLoop(func)
table.insert(self.func,func)
end
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.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.Active==true then
for i=1,#self.func do
self.func[i](self.pos,self)
end
self.pos=self.pos+self.count
end
end
end
self.spos=self.spos+1
if self.spos>=self.skip then
self.spos=0
end
end
function c:OnStep(func)
table.insert(self.func,1,func)
end
function c:OnEnd(func)
table.insert(self.funcE,func)
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()
end
c:OnStep(function(p,s)
if s.count>0 and s.endAt==p then
for fe=1,#s.funcE do
s.funcE[fe](s)
end
s.pos=s.start-1
elseif s.count<0 and s.endAt==p then
for fe=1,#s.funcE do
s.funcE[fe](s)
end
s.pos=s.start-1
end
end)
return c
end
function multi:newTStep(start,reset,count,set)
local c=self:newBase()
think=1
c.Type="TStep"
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=os.clock()
c.set=set or 1
function c:Update(start,reset,count,set)
self.start=start or self.start
self.pos=start
self.endAt=reset or self.endAt
self.set=set or self.set
self.count=count or self.count or 1
self.timer=os.clock()
self:Resume()
end
function c:Act()
if self.Active then
if os.clock()-self.timer>=self.set then
self:Reset()
for i=1,#self.func do
self.func[i](self.pos,self)
end
if self.endAt==self.pos then
for fe=1,#self.funcE do
self.funcE[fe](self)
end
self.pos=self.start-1
end
self.pos=self.pos+self.count
end
end
end
function c:OnEnd(func)
table.insert(self.funcE,func)
end
function c:Reset(n)
if n then self.set=n end
self.timer=os.clock()
self:Resume()
end
function c:OnStep(func)
table.insert(self.func,func)
end
return c
end
function multi:newTrigger(func)
local c=self:newBase()
c.Type="Trigger"
c.trigfunc=func or function() end
function c:Fire(...)
self:trigfunc(self,...)
end
return c
end
--Managers
function multi:mainloop()
for i=1,#self.Tasks do
self.Tasks[i](self)
end
self.Start=os.clock()
while self.Active do
self:Do_Order()
end
end
function multi._tFunc(self,dt)
for i=1,#self.Tasks do
self.Tasks[i](self)
end
print("once!")
if dt then
self.pump=true
end
self.pumpvar=dt
self.Start=os.clock()
end
function multi:uManager(dt)
self:oneTime(self._tFunc,self,dt)
self:Do_Order()
end
multi.drawF={}
function multi:dManager()
for ii=1,#multi.drawF do
multi.drawF[ii]()
end
end
function multi:onDraw(func)
table.insert(self.drawF,func)
end
function multi:lManager()
if love.event then
love.event.pump()
for e,a,b,c,d in love.event.poll() do
if e == "quit" then
if not love.quit or not love.quit() then
if love.audio then
love.audio.stop()
end
return nil
end
end
love.handlers[e](a,b,c,d)
end
end
if love.timer then
love.timer.step()
dt = love.timer.getDelta()
end
if love.update then love.update(dt) end
multi:uManager(dt)
if love.window and love.graphics and love.window.isCreated() then
love.graphics.clear()
love.graphics.origin()
if love.draw then love.draw() end
multi.dManager()
love.graphics.setColor(255,255,255,255)
if multi.draw then multi.draw() end
love.graphics.present()
end
end
Thread={}
Thread.Name="Thread 2"
Thread.ChannelThread = love.thread.getChannel("Easy2")
Thread.ChannelMain = love.thread.getChannel("EasyMain")
Thread.Global = {}
function Thread:packTable(G)
function escapeStr(str)
local temp=""
for i=1,#str do
temp=temp.."\\"..string.byte(string.sub(str,i,i))
end
return temp
end
function ToStr(t)
local dat="{"
for i,v in pairs(t) do
if type(i)=="number" then
i="["..i.."]="
else
i=i.."="
end
if type(v)=="string" then
dat=dat..i.."\""..v.."\","
elseif type(v)=="number" then
dat=dat..i..v..","
elseif type(v)=="boolean" then
dat=dat..i..tostring(v)..","
elseif type(v)=="table" and not(G==v) then
dat=dat..i..ToStr(v)..","
--elseif type(v)=="table" and G==v then
-- dat=dat..i.."assert(loadstring(\"return self\")),"
elseif type(v)=="function" then
dat=dat..i.."assert(loadstring(\""..escapeStr(string.dump(v)).."\")),"
end
end
return string.sub(dat,1,-2).."}"
end
return "return "..ToStr(G)
end
function Thread:Send(name,var)
arg3="2"
if type(var)=="table" then
var=Thread:packTable(var)
arg3="table"
end
self.ChannelMain:push({name,var,arg3})
end
function Thread:UnPackChannel()
local c=self.ChannelThread:getCount()
for i=1,c do
local temp=self.ChannelThread:pop()
if temp[1] and temp[2] then
if temp[1]=="func" and type(temp[2])=="string" then
loadstring(temp[2])(temp[3])
elseif temp[1]=="table" then
_G[temp[3]]=loadstring(temp[2])()
else
_G[temp[1]]=temp[2]
end
end
end
if #multi:getChildren()<2 then
os.sleep(.05)
end
end
function Thread:boost(func,name)
self:Send(name,string.dump(func))
end
function Thread.mainloop()
Thread:UnPackChannel()
end
Thread.MainThread=false
multi:newLoop():OnLoop(Thread.mainloop)
multi:mainloop()

596
LoveKaraoke/core/T3.lua Normal file
View File

@ -0,0 +1,596 @@
require("love.timer")
require("love.system")
require("love.sound")
require("love.physics")
require("love.mouse")
require("love.math")
require("love.keyboard")
require("love.joystick")
require("love.image")
require("love.font")
require("love.filesystem")
require("love.event")
require("love.audio")
require("love.graphics")
require("love.window")
_defaultfont = love.graphics.getFont()
gui = {}
function gui.getTile(i,x,y,w,h)-- returns imagedata
if type(i)=="userdata" then
-- do nothing
else
error("getTile invalid args!!! Usage: ImageElement:getTile(x,y,w,h) or gui:getTile(imagedata,x,y,w,h)")
end
local iw,ih=i:getDimensions()
local id,_id=i:getData(),love.image.newImageData(w,h)
for _x=x,w+x-1 do
for _y=y,h+y-1 do
_id:setPixel(_x-x,_y-y,id:getPixel(_x,_y))
end
end
return love.graphics.newImage(_id)
end
multi = {}
multi.Version="4.0.0"
multi.__index = multi
multi.Mainloop={}
multi.Tasks={}
multi.Tasks2={}
multi.Garbage={}
multi.Children={}
multi.Paused={}
multi.MasterId=0
multi.Active=true
multi.Id=-1
multi.Type="MainInt"
multi.Rest=0
-- System
os.sleep=love.timer.sleep
function multi:newBase(ins)
if not(self.Type=="MainInt" or self.Type=="int") then error("Can only create an object on multi or an interface obj") return false end
local c = {}
if self.Type=="int" then
setmetatable(c, self.Parent)
else
setmetatable(c, self)
end
c.Active=true
c.func={}
c.Id=0
c.Act=function() end
c.Parent=self
if ins then
table.insert(self.Mainloop,ins,c)
else
table.insert(self.Mainloop,c)
end
self.MasterId=self.MasterId+1
return c
end
function multi:reboot(r)
self.Mainloop={}
self.Tasks={}
self.Tasks2={}
self.Garbage={}
self.Children={}
self.Paused={}
self.MasterId=0
self.Active=true
self.Id=-1
if r then
for i,v in pairs(_G) do
if type(i)=="table" then
if i.Parent and i.Id and i.Act then
i={}
end
end
end
end
end
function multi:getChildren()
return self.Mainloop
end
--Processor
function multi:Do_Order()
for _D=#self.Mainloop,1,-1 do
if self.Mainloop[_D]~=nil then
self.Mainloop[_D].Id=_D
self.Mainloop[_D]:Act()
end
if self.Mainloop[_D].rem then
table.remove(self.Mainloop,_D)
end
end
if self.Rest>0 then
os.sleep(self.Rest)
end
end
function multi:benchMark(sec)
local temp=self:newLoop(function(t,self)
if os.clock()-self.init>self.sec then
print(self.c.." steps in "..self.sec.." second(s)")
self.tt(self.sec)
self:Destroy()
else
self.c=self.c+1
end
end)
function temp:OnBench(func)
self.tt=func
end
self.tt=function() end
temp.sec=sec
temp.init=os.clock()
temp.c=0
return temp
end
function multi:newInterface()
if not(self.Type=="MainInt") 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.Id=0
c.Type="int"
c.Mainloop={}
c.Tasks={}
c.Tasks2={}
c.Garbage={}
c.Children={}
c.Paused={}
c.MasterId=0
c.Active=true
c.Id=-1
c.Rest=0
function c:Start()
if self.l then
self.l:Resume()
else
self.l=self.Parent:newLoop(function(dt) c:uManager(dt) end)
end
end
function c:Stop()
if self.l then
self.l:Pause()
end
end
function c:Remove()
self:Destroy()
self.l:Destroy()
end
return c
end
--Helpers
function multi:FreeMainEvent()
self.func={}
end
function multi:isPaused()
return not(self.Active)
end
function multi:Pause(n)
if self.Type=="int" or self.Type=="MainInt" then
self.Active=false
if not(n) then
local c=self:getChildren()
for i=1,#c do
c[i]:Pause()
end
else
self:hold(n)
end
else
if not(n) then
self.Active=false
if self.Parent.Mainloop[self.Id]~=nil then
table.remove(self.Parent.Mainloop,self.Id)
table.insert(self.Parent.Paused,self)
self.Id=#self.Parent.Paused
end
else
self:hold(n)
end
end
end
function multi:Resume()
if self.Type=="int" or self.Type=="MainInt" then
self.Active=true
local c=self:getChildren()
for i=1,#c do
c[i]:Resume()
end
else
if self:isPaused() then
self.Active=true
for i=1,#self.Parent.Paused do
if self.Parent.Paused[i]==self then
table.remove(self.Parent.Paused,i)
return
end
end
table.insert(self.Parent.Mainloop,self)
end
end
end
function multi:Destroy()
if self.Type=="int" or self.Type=="MainInt" then
local c=self:getChildren()
for i=1,#c do
c[i]:Destroy()
end
else
self.rem=true
end
end
function multi:hold(task)
self:Pause()
if type(task)=="number" then
local alarm=self:newAlarm(task)
while alarm.Active==true do
if love then
self.Parent.lManager()
else
self.Parent.Do_Order()
end
end
alarm:Destroy()
self:Resume()
elseif type(task)=="function" then
local env=self.Parent:newEvent(task)
env:OnEvent(function(envt) envt:Pause() envt:Stop() end)
while env.Active do
if love then
self.Parent.lManager()
else
self.Parent.Do_Order()
end
end
env:Destroy()
self:Resume()
else
print("Error Data Type!!!")
end
end
function multi:oneTime(func,...)
if not(self.Type=="MainInt" or self.Type=="int") then
for _k=1,#self.Parent.Tasks2 do
if self.Parent.Tasks2[_k]==func then
return false
end
end
table.insert(self.Parent.Tasks2,func)
func(...)
return true
else
for _k=1,#self.Tasks2 do
if self.Tasks2[_k]==func then
return false
end
end
table.insert(self.Tasks2,func)
func(...)
return true
end
end
--Constructors
function multi:newEvent(task)
local c=self:newBase()
c.Type="Event"
c.Task=task or function() end
function c:Act()
if self.Task(self) and self.Active==true then
self:Pause()
for _E=1,#self.func do
self.func[_E](self)
end
end
end
function c:OnEvent(func)
table.insert(self.func,func)
end
return c
end
function multi:newAlarm(set)
local c=self:newBase()
c.Type="Alarm"
c.timer=os.clock()
c.set=set or 0
function c:Act()
if self.Active==true then
if os.clock()-self.timer>=self.set then
self:Pause()
for i=1,#self.func do
self.func[i](self)
end
end
end
end
function c:Reset(n)
if n then self.set=n end
self.timer=os.clock()
self:Resume()
end
function c:OnRing(func)
table.insert(self.func,func)
end
return c
end
function multi:newTask(func)
table.insert(self.Tasks,func)
end
function multi:newLoop(func)
local c=self:newBase()
c.Type="Loop"
if func then
c.func={func}
end
function c:Act()
if self.Active==true then
for i=1,#self.func do
self.func[i](os.clock()-self.Parent.Start,self)
end
end
end
function c:OnLoop(func)
table.insert(self.func,func)
end
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.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.Active==true then
for i=1,#self.func do
self.func[i](self.pos,self)
end
self.pos=self.pos+self.count
end
end
end
self.spos=self.spos+1
if self.spos>=self.skip then
self.spos=0
end
end
function c:OnStep(func)
table.insert(self.func,1,func)
end
function c:OnEnd(func)
table.insert(self.funcE,func)
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()
end
c:OnStep(function(p,s)
if s.count>0 and s.endAt==p then
for fe=1,#s.funcE do
s.funcE[fe](s)
end
s.pos=s.start-1
elseif s.count<0 and s.endAt==p then
for fe=1,#s.funcE do
s.funcE[fe](s)
end
s.pos=s.start-1
end
end)
return c
end
function multi:newTStep(start,reset,count,set)
local c=self:newBase()
think=1
c.Type="TStep"
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=os.clock()
c.set=set or 1
function c:Update(start,reset,count,set)
self.start=start or self.start
self.pos=start
self.endAt=reset or self.endAt
self.set=set or self.set
self.count=count or self.count or 1
self.timer=os.clock()
self:Resume()
end
function c:Act()
if self.Active then
if os.clock()-self.timer>=self.set then
self:Reset()
for i=1,#self.func do
self.func[i](self.pos,self)
end
if self.endAt==self.pos then
for fe=1,#self.funcE do
self.funcE[fe](self)
end
self.pos=self.start-1
end
self.pos=self.pos+self.count
end
end
end
function c:OnEnd(func)
table.insert(self.funcE,func)
end
function c:Reset(n)
if n then self.set=n end
self.timer=os.clock()
self:Resume()
end
function c:OnStep(func)
table.insert(self.func,func)
end
return c
end
function multi:newTrigger(func)
local c=self:newBase()
c.Type="Trigger"
c.trigfunc=func or function() end
function c:Fire(...)
self:trigfunc(self,...)
end
return c
end
--Managers
function multi:mainloop()
for i=1,#self.Tasks do
self.Tasks[i](self)
end
self.Start=os.clock()
while self.Active do
self:Do_Order()
end
end
function multi._tFunc(self,dt)
for i=1,#self.Tasks do
self.Tasks[i](self)
end
print("once!")
if dt then
self.pump=true
end
self.pumpvar=dt
self.Start=os.clock()
end
function multi:uManager(dt)
self:oneTime(self._tFunc,self,dt)
self:Do_Order()
end
multi.drawF={}
function multi:dManager()
for ii=1,#multi.drawF do
multi.drawF[ii]()
end
end
function multi:onDraw(func)
table.insert(self.drawF,func)
end
function multi:lManager()
if love.event then
love.event.pump()
for e,a,b,c,d in love.event.poll() do
if e == "quit" then
if not love.quit or not love.quit() then
if love.audio then
love.audio.stop()
end
return nil
end
end
love.handlers[e](a,b,c,d)
end
end
if love.timer then
love.timer.step()
dt = love.timer.getDelta()
end
if love.update then love.update(dt) end
multi:uManager(dt)
if love.window and love.graphics and love.window.isCreated() then
love.graphics.clear()
love.graphics.origin()
if love.draw then love.draw() end
multi.dManager()
love.graphics.setColor(255,255,255,255)
if multi.draw then multi.draw() end
love.graphics.present()
end
end
Thread={}
Thread.Name="Thread 3"
Thread.ChannelThread = love.thread.getChannel("Easy3")
Thread.ChannelMain = love.thread.getChannel("EasyMain")
Thread.Global = {}
function Thread:packTable(G)
function escapeStr(str)
local temp=""
for i=1,#str do
temp=temp.."\\"..string.byte(string.sub(str,i,i))
end
return temp
end
function ToStr(t)
local dat="{"
for i,v in pairs(t) do
if type(i)=="number" then
i="["..i.."]="
else
i=i.."="
end
if type(v)=="string" then
dat=dat..i.."\""..v.."\","
elseif type(v)=="number" then
dat=dat..i..v..","
elseif type(v)=="boolean" then
dat=dat..i..tostring(v)..","
elseif type(v)=="table" and not(G==v) then
dat=dat..i..ToStr(v)..","
--elseif type(v)=="table" and G==v then
-- dat=dat..i.."assert(loadstring(\"return self\")),"
elseif type(v)=="function" then
dat=dat..i.."assert(loadstring(\""..escapeStr(string.dump(v)).."\")),"
end
end
return string.sub(dat,1,-2).."}"
end
return "return "..ToStr(G)
end
function Thread:Send(name,var)
arg3="3"
if type(var)=="table" then
var=Thread:packTable(var)
arg3="table"
end
self.ChannelMain:push({name,var,arg3})
end
function Thread:UnPackChannel()
local c=self.ChannelThread:getCount()
for i=1,c do
local temp=self.ChannelThread:pop()
if temp[1] and temp[2] then
if temp[1]=="func" and type(temp[2])=="string" then
loadstring(temp[2])(temp[3])
else
_G[temp[1]]=temp[2]
end
end
end
if #multi:getChildren()<2 then
os.sleep(.05)
end
end
function Thread:boost(func,name)
self:Send(name,string.dump(func))
end
function Thread.mainloop()
Thread:UnPackChannel()
end
Thread.MainThread=false
multi:newLoop():OnLoop(Thread.mainloop)
multi:mainloop()

596
LoveKaraoke/core/T4.lua Normal file
View File

@ -0,0 +1,596 @@
require("love.timer")
require("love.system")
require("love.sound")
require("love.physics")
require("love.mouse")
require("love.math")
require("love.keyboard")
require("love.joystick")
require("love.image")
require("love.font")
require("love.filesystem")
require("love.event")
require("love.audio")
require("love.graphics")
require("love.window")
_defaultfont = love.graphics.getFont()
gui = {}
function gui.getTile(i,x,y,w,h)-- returns imagedata
if type(i)=="userdata" then
-- do nothing
else
error("getTile invalid args!!! Usage: ImageElement:getTile(x,y,w,h) or gui:getTile(imagedata,x,y,w,h)")
end
local iw,ih=i:getDimensions()
local id,_id=i:getData(),love.image.newImageData(w,h)
for _x=x,w+x-1 do
for _y=y,h+y-1 do
_id:setPixel(_x-x,_y-y,id:getPixel(_x,_y))
end
end
return love.graphics.newImage(_id)
end
multi = {}
multi.Version="4.0.0"
multi.__index = multi
multi.Mainloop={}
multi.Tasks={}
multi.Tasks2={}
multi.Garbage={}
multi.Children={}
multi.Paused={}
multi.MasterId=0
multi.Active=true
multi.Id=-1
multi.Type="MainInt"
multi.Rest=0
-- System
os.sleep=love.timer.sleep
function multi:newBase(ins)
if not(self.Type=="MainInt" or self.Type=="int") then error("Can only create an object on multi or an interface obj") return false end
local c = {}
if self.Type=="int" then
setmetatable(c, self.Parent)
else
setmetatable(c, self)
end
c.Active=true
c.func={}
c.Id=0
c.Act=function() end
c.Parent=self
if ins then
table.insert(self.Mainloop,ins,c)
else
table.insert(self.Mainloop,c)
end
self.MasterId=self.MasterId+1
return c
end
function multi:reboot(r)
self.Mainloop={}
self.Tasks={}
self.Tasks2={}
self.Garbage={}
self.Children={}
self.Paused={}
self.MasterId=0
self.Active=true
self.Id=-1
if r then
for i,v in pairs(_G) do
if type(i)=="table" then
if i.Parent and i.Id and i.Act then
i={}
end
end
end
end
end
function multi:getChildren()
return self.Mainloop
end
--Processor
function multi:Do_Order()
for _D=#self.Mainloop,1,-1 do
if self.Mainloop[_D]~=nil then
self.Mainloop[_D].Id=_D
self.Mainloop[_D]:Act()
end
if self.Mainloop[_D].rem then
table.remove(self.Mainloop,_D)
end
end
if self.Rest>0 then
os.sleep(self.Rest)
end
end
function multi:benchMark(sec)
local temp=self:newLoop(function(t,self)
if os.clock()-self.init>self.sec then
print(self.c.." steps in "..self.sec.." second(s)")
self.tt(self.sec)
self:Destroy()
else
self.c=self.c+1
end
end)
function temp:OnBench(func)
self.tt=func
end
self.tt=function() end
temp.sec=sec
temp.init=os.clock()
temp.c=0
return temp
end
function multi:newInterface()
if not(self.Type=="MainInt") 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.Id=0
c.Type="int"
c.Mainloop={}
c.Tasks={}
c.Tasks2={}
c.Garbage={}
c.Children={}
c.Paused={}
c.MasterId=0
c.Active=true
c.Id=-1
c.Rest=0
function c:Start()
if self.l then
self.l:Resume()
else
self.l=self.Parent:newLoop(function(dt) c:uManager(dt) end)
end
end
function c:Stop()
if self.l then
self.l:Pause()
end
end
function c:Remove()
self:Destroy()
self.l:Destroy()
end
return c
end
--Helpers
function multi:FreeMainEvent()
self.func={}
end
function multi:isPaused()
return not(self.Active)
end
function multi:Pause(n)
if self.Type=="int" or self.Type=="MainInt" then
self.Active=false
if not(n) then
local c=self:getChildren()
for i=1,#c do
c[i]:Pause()
end
else
self:hold(n)
end
else
if not(n) then
self.Active=false
if self.Parent.Mainloop[self.Id]~=nil then
table.remove(self.Parent.Mainloop,self.Id)
table.insert(self.Parent.Paused,self)
self.Id=#self.Parent.Paused
end
else
self:hold(n)
end
end
end
function multi:Resume()
if self.Type=="int" or self.Type=="MainInt" then
self.Active=true
local c=self:getChildren()
for i=1,#c do
c[i]:Resume()
end
else
if self:isPaused() then
self.Active=true
for i=1,#self.Parent.Paused do
if self.Parent.Paused[i]==self then
table.remove(self.Parent.Paused,i)
return
end
end
table.insert(self.Parent.Mainloop,self)
end
end
end
function multi:Destroy()
if self.Type=="int" or self.Type=="MainInt" then
local c=self:getChildren()
for i=1,#c do
c[i]:Destroy()
end
else
self.rem=true
end
end
function multi:hold(task)
self:Pause()
if type(task)=="number" then
local alarm=self:newAlarm(task)
while alarm.Active==true do
if love then
self.Parent.lManager()
else
self.Parent.Do_Order()
end
end
alarm:Destroy()
self:Resume()
elseif type(task)=="function" then
local env=self.Parent:newEvent(task)
env:OnEvent(function(envt) envt:Pause() envt:Stop() end)
while env.Active do
if love then
self.Parent.lManager()
else
self.Parent.Do_Order()
end
end
env:Destroy()
self:Resume()
else
print("Error Data Type!!!")
end
end
function multi:oneTime(func,...)
if not(self.Type=="MainInt" or self.Type=="int") then
for _k=1,#self.Parent.Tasks2 do
if self.Parent.Tasks2[_k]==func then
return false
end
end
table.insert(self.Parent.Tasks2,func)
func(...)
return true
else
for _k=1,#self.Tasks2 do
if self.Tasks2[_k]==func then
return false
end
end
table.insert(self.Tasks2,func)
func(...)
return true
end
end
--Constructors
function multi:newEvent(task)
local c=self:newBase()
c.Type="Event"
c.Task=task or function() end
function c:Act()
if self.Task(self) and self.Active==true then
self:Pause()
for _E=1,#self.func do
self.func[_E](self)
end
end
end
function c:OnEvent(func)
table.insert(self.func,func)
end
return c
end
function multi:newAlarm(set)
local c=self:newBase()
c.Type="Alarm"
c.timer=os.clock()
c.set=set or 0
function c:Act()
if self.Active==true then
if os.clock()-self.timer>=self.set then
self:Pause()
for i=1,#self.func do
self.func[i](self)
end
end
end
end
function c:Reset(n)
if n then self.set=n end
self.timer=os.clock()
self:Resume()
end
function c:OnRing(func)
table.insert(self.func,func)
end
return c
end
function multi:newTask(func)
table.insert(self.Tasks,func)
end
function multi:newLoop(func)
local c=self:newBase()
c.Type="Loop"
if func then
c.func={func}
end
function c:Act()
if self.Active==true then
for i=1,#self.func do
self.func[i](os.clock()-self.Parent.Start,self)
end
end
end
function c:OnLoop(func)
table.insert(self.func,func)
end
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.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.Active==true then
for i=1,#self.func do
self.func[i](self.pos,self)
end
self.pos=self.pos+self.count
end
end
end
self.spos=self.spos+1
if self.spos>=self.skip then
self.spos=0
end
end
function c:OnStep(func)
table.insert(self.func,1,func)
end
function c:OnEnd(func)
table.insert(self.funcE,func)
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()
end
c:OnStep(function(p,s)
if s.count>0 and s.endAt==p then
for fe=1,#s.funcE do
s.funcE[fe](s)
end
s.pos=s.start-1
elseif s.count<0 and s.endAt==p then
for fe=1,#s.funcE do
s.funcE[fe](s)
end
s.pos=s.start-1
end
end)
return c
end
function multi:newTStep(start,reset,count,set)
local c=self:newBase()
think=1
c.Type="TStep"
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=os.clock()
c.set=set or 1
function c:Update(start,reset,count,set)
self.start=start or self.start
self.pos=start
self.endAt=reset or self.endAt
self.set=set or self.set
self.count=count or self.count or 1
self.timer=os.clock()
self:Resume()
end
function c:Act()
if self.Active then
if os.clock()-self.timer>=self.set then
self:Reset()
for i=1,#self.func do
self.func[i](self.pos,self)
end
if self.endAt==self.pos then
for fe=1,#self.funcE do
self.funcE[fe](self)
end
self.pos=self.start-1
end
self.pos=self.pos+self.count
end
end
end
function c:OnEnd(func)
table.insert(self.funcE,func)
end
function c:Reset(n)
if n then self.set=n end
self.timer=os.clock()
self:Resume()
end
function c:OnStep(func)
table.insert(self.func,func)
end
return c
end
function multi:newTrigger(func)
local c=self:newBase()
c.Type="Trigger"
c.trigfunc=func or function() end
function c:Fire(...)
self:trigfunc(self,...)
end
return c
end
--Managers
function multi:mainloop()
for i=1,#self.Tasks do
self.Tasks[i](self)
end
self.Start=os.clock()
while self.Active do
self:Do_Order()
end
end
function multi._tFunc(self,dt)
for i=1,#self.Tasks do
self.Tasks[i](self)
end
print("once!")
if dt then
self.pump=true
end
self.pumpvar=dt
self.Start=os.clock()
end
function multi:uManager(dt)
self:oneTime(self._tFunc,self,dt)
self:Do_Order()
end
multi.drawF={}
function multi:dManager()
for ii=1,#multi.drawF do
multi.drawF[ii]()
end
end
function multi:onDraw(func)
table.insert(self.drawF,func)
end
function multi:lManager()
if love.event then
love.event.pump()
for e,a,b,c,d in love.event.poll() do
if e == "quit" then
if not love.quit or not love.quit() then
if love.audio then
love.audio.stop()
end
return nil
end
end
love.handlers[e](a,b,c,d)
end
end
if love.timer then
love.timer.step()
dt = love.timer.getDelta()
end
if love.update then love.update(dt) end
multi:uManager(dt)
if love.window and love.graphics and love.window.isCreated() then
love.graphics.clear()
love.graphics.origin()
if love.draw then love.draw() end
multi.dManager()
love.graphics.setColor(255,255,255,255)
if multi.draw then multi.draw() end
love.graphics.present()
end
end
Thread={}
Thread.Name="Thread 4"
Thread.ChannelThread = love.thread.getChannel("Easy4")
Thread.ChannelMain = love.thread.getChannel("EasyMain")
Thread.Global = {}
function Thread:packTable(G)
function escapeStr(str)
local temp=""
for i=1,#str do
temp=temp.."\\"..string.byte(string.sub(str,i,i))
end
return temp
end
function ToStr(t)
local dat="{"
for i,v in pairs(t) do
if type(i)=="number" then
i="["..i.."]="
else
i=i.."="
end
if type(v)=="string" then
dat=dat..i.."\""..v.."\","
elseif type(v)=="number" then
dat=dat..i..v..","
elseif type(v)=="boolean" then
dat=dat..i..tostring(v)..","
elseif type(v)=="table" and not(G==v) then
dat=dat..i..ToStr(v)..","
--elseif type(v)=="table" and G==v then
-- dat=dat..i.."assert(loadstring(\"return self\")),"
elseif type(v)=="function" then
dat=dat..i.."assert(loadstring(\""..escapeStr(string.dump(v)).."\")),"
end
end
return string.sub(dat,1,-2).."}"
end
return "return "..ToStr(G)
end
function Thread:Send(name,var)
arg3="4"
if type(var)=="table" then
var=Thread:packTable(var)
arg3="table"
end
self.ChannelMain:push({name,var,arg3})
end
function Thread:UnPackChannel()
local c=self.ChannelThread:getCount()
for i=1,c do
local temp=self.ChannelThread:pop()
if temp[1] and temp[2] then
if temp[1]=="func" and type(temp[2])=="string" then
loadstring(temp[2])(temp[3])
else
_G[temp[1]]=temp[2]
end
end
end
if #multi:getChildren()<2 then
os.sleep(.05)
end
end
function Thread:boost(func,name)
self:Send(name,string.dump(func))
end
function Thread.mainloop()
Thread:UnPackChannel()
end
Thread.MainThread=false
multi:newLoop():OnLoop(Thread.mainloop)
multi:mainloop()

View File

@ -0,0 +1,158 @@
Thread={}
Thread.ChannelT1 = love.thread.getChannel("Easy1")
Thread.ChannelT2 = love.thread.getChannel("Easy2")
Thread.ChannelT3 = love.thread.getChannel("Easy3")
Thread.ChannelT4 = love.thread.getChannel("Easy4")
Thread.ChannelMain = love.thread.getChannel("EasyMain")
Thread.Name = "Thread Main"
Thread.n=0
Thread.count=1
function Thread:packTable(G)
function escapeStr(str)
local temp=""
for i=1,#str do
temp=temp.."\\"..string.byte(string.sub(str,i,i))
end
return temp
end
function ToStr(t)
local dat="{"
for i,v in pairs(t) do
if type(i)=="number" then
i="["..i.."]="
else
i=i.."="
end
if type(v)=="string" then
dat=dat..i.."\""..v.."\","
elseif type(v)=="number" then
dat=dat..i..v..","
elseif type(v)=="boolean" then
dat=dat..i..tostring(v)..","
elseif type(v)=="table" and not(G==v) then
dat=dat..i..bin.ToStr(v)..","
--elseif type(v)=="table" and G==v then
-- dat=dat..i.."assert(loadstring(\"return self\")),"
elseif type(v)=="function" then
dat=dat..i.."assert(loadstring(\""..escapeStr(string.dump(v)).."\")),"
end
end
return string.sub(dat,1,-2).."}"
end
return ToStr(G)
end
Thread.last={}
function Thread:GetStatus()
print(self.n.." Threads Exist!!!")
for i=1,self.n do
print("\tThread "..i.." Running: "..tostring(self["Thread"..i]:isRunning()))
if not(self["Thread"..i]:isRunning()) then
print("\t\t"..self["Thread"..i]:getError())
end
end
end
function Thread:Start(n)
local x=love.system.getProcessorCount()
if x>1 then
x=x-1
else
x=1
end
n=n or x
if n<1 then
print("Must be atleast 1 thread running!!!")
return
end
if n>4 then
print("Must be no more than 4 threads running!!!")
return
end
for i=1,n do
self["Thread"..i]=love.thread.newThread("Libs/T"..i..".lua")
self["Thread"..i]:start()
end
Thread.n=n
end
function Thread:RestartBroken()
for i=1,self.n do
if self["Thread"..i]:isRunning()==false then
self["Thread"..i]:start()
end
Thread:Boost(Thread.last[1],Thread.last[2])
end
end
function Thread:Send(name,var,arg3)
if self.n>0 then
if type(var)=="table" then
var=Thread:packTable(var)
arg3=name
name="table"
end
self["ChannelT"..((self.count-1)%self.n)+1]:push({name,var,arg3})
self.count=self.count+1
end
end
function Thread:SendAll(name,var,arg3)
if self.n>0 then
for i=1,self.n do
if type(var)=="table" then
var=Thread:packTable(var)
arg3=name
name="table"
end
self["ChannelT"..i]:push({name,var,arg3})
end
end
end
function Thread:UnPackChannel()
local c=self.ChannelMain:getCount()
for i=1,c do
local temp=self.ChannelMain:pop()
if temp[3]=="table" then
_G[temp[1]]=assert(loadstring(temp[2]))()
else
if Thread.OnDataRecieved then
Thread.OnDataRecieved(temp[1],temp[2],temp[3])
end
_G[temp[1]]=temp[2]
end
end
end
function Thread:Boost(func,name)
if Thread.last[1]==nil then
return
end
Thread.last={func,name}
name=name or "nil"
if self.n>0 then
self:Send("func",string.dump(func),name)
end
end
function Thread:SendLibs(func,name)
name=name or "nil"
if self.n>0 then
self:SendAll("func",string.dump(func),name)
end
end
function Thread.mainloop()
if Thread.n>0 then
Thread:UnPackChannel()
end
end
Thread.MainThread=true
local loop = multi:newLoop()
loop:OnLoop(Thread.mainloop)
OnThreadError=multi:newConnection()
function love.threaderror(thread, errorstr)
Thread:GetStatus()
Thread:RestartBroken()
Thread:GetStatus()
OnThreadError:Fire(thread,errorstr)
end
multi:newTask(function()
math.randomseed(math.floor(os.time()/2))
for i=1,Thread.n do
Thread["ChannelT"..i]:push({"randseed",math.random(-1000000,1000000)})
Thread["ChannelT"..i]:push({"func",string.dump(function() math.randomseed(randseed) end),"randomizing"})
end
end)

797
LoveKaraoke/core/Utils.lua Normal file
View File

@ -0,0 +1,797 @@
-- os Additions
function os.getSystemBit()
if (os.getenv('PROCESSOR_ARCHITEW6432')=='AMD64' or os.getenv('PROCESSOR_ARCHITECTURE')=='AMD64') then
return 64
else
return 32
end
end
function os.sleep(n)
if not n then n=0 end
local t0 = os.clock()
while os.clock() - t0 <= n do end
end
function os.pause(msg)
if msg ~= nil then
print(msg)
end
io.read()
end
function os.batCmd(cmd)
io.mkFile('temp.bat',cmd)
local temp = os.execute([[temp.bat]])
io.delFile('temp.bat')
return temp
end
function os._getOS()
if package.config:sub(1,1)=='\\' then
return 'windows'
else
return 'unix'
end
end
function os.getOS(t)
if not t then
return os._getOS()
end
if os._getOS()=='unix' then
fh,err = io.popen('uname -o 2>/dev/null','r')
if fh then
osname = fh:read()
end
if osname then return osname end
end
local winver='Unknown Version'
local a,b,c=os.capture('ver'):match('(%d+).(%d+).(%d+)')
local win=a..'.'..b..'.'..c
if type(t)=='string' then
win=t
end
if win=='4.00.950' then
winver='95'
elseif win=='4.00.1111' then
winver='95 OSR2'
elseif win=='4.00.1381' then
winver='NT 4.0'
elseif win=='4.10.1998' then
winver='98'
elseif win=='4.10.2222' then
winver='98 SE'
elseif win=='4.90.3000' then
winver='ME'
elseif win=='5.00.2195' then
winver='2000'
elseif win=='5.1.2600' then
winver='XP'
elseif win=='5.2.3790' then
winver='Server 2003'
elseif win=='6.0.6000' then
winver='Vista/Windows Server 2008'
elseif win=='6.0.6002' then
winver='Vista SP2'
elseif win=='6.1.7600' then
winver='7/Windows Server 2008 R2'
elseif win=='6.1.7601' then
winver='7 SP1/Windows Server 2008 R2 SP1'
elseif win=='6.2.9200' then
winver='8/Windows Server 2012'
elseif win=='6.3.9600' then
winver='8.1/Windows Server 2012'
elseif win=='6.4.9841' then
winver='10 Technical Preview 1'
elseif win=='6.4.9860' then
winver='10 Technical Preview 2'
elseif win=='6.4.9879' then
winver='10 Technical Preview 3'
elseif win=='10.0.9926' then
winver='10 Technical Preview 4'
end
return 'Windows '..winver
end
function os.getLuaArch()
return (#tostring({})-7)*4
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 os.capture(cmd, raw)
local f = assert(io.popen(cmd, 'r'))
local s = assert(f:read('*a'))
f:close()
if raw then return s end
s = string.gsub(s, '^%s+', '')
s = string.gsub(s, '%s+$', '')
s = string.gsub(s, '[\n\r]+', ' ')
return s
end
function os.getCurrentUser()
return os.getenv('$USER') or os.getenv('USERNAME')
end
-- string Additions
function string.trim(s)
local from = s:match"^%s*()"
return from > #s and "" or s:match(".*%S", from)
end
function string.random(n)
local str = ''
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
h = math.random(1,#strings)
str = str..''..strings[h]
end
return str
end
function string.linesToTable(s)
local t = {}
local i = 0
while true do
i = string.find(s, '\n', i+1)
if i == nil then return t end
table.insert(t, i)
end
end
function string.lines(str)
local t = {}
local function helper(line) table.insert(t, line) return '' end
helper((str:gsub('(.-)\r?\n', helper)))
return t
end
function string.split(str, pat)
local t = {} -- NOTE: use {n = 0} in Lua-5.0
local fpat = '(.-)' .. pat
local last_end = 1
local s, e, cap = str:find(fpat, 1)
while s do
if s ~= 1 or cap ~= '' then
table.insert(t,cap)
end
last_end = e+1
s, e, cap = str:find(fpat, last_end)
end
if last_end <= #str then
cap = str:sub(last_end)
table.insert(t, cap)
end
return t
end
function string.shuffle(inputStr)
math.randomseed(os.time());
local outputStr = '';
local strLength = string.len(inputStr);
while (strLength ~=0) do
local pos = math.random(strLength);
outputStr = outputStr..string.sub(inputStr,pos,pos);
inputStr = inputStr:sub(1, pos-1) .. inputStr:sub(pos+1);
strLength = string.len(inputStr);
end
return outputStr;
end
function string.genKeys(chars,a,f,s,GG)
if GG then
chars=string.rep(chars,a)
end
if s then
chars=string.shuffle(chars)
end
b=#chars
if a==0 then return end
local taken = {} local slots = {}
for i=1,a do slots[i]=0 end
for i=1,b do taken[i]=false end
local index = 1
local tab={}
for i=1,#chars do
table.insert(tab,chars:sub(i,i))
end
while index > 0 do repeat
repeat slots[index] = slots[index] + 1
until slots[index] > b or not taken[slots[index]]
if slots[index] > b then
slots[index] = 0
index = index - 1
if index > 0 then
taken[slots[index]] = false
end
break
else
taken[slots[index]] = true
end
if index == a then
local tt={}
for i=1,a do
table.insert(tt,tab[slots[i]])
end
f(table.concat(tt))
taken[slots[index]] = false
break
end
index = index + 1
until true end
end
-- io Additions
function io.getInput(msg)
if msg ~= nil then
io.write(msg)
end
return io.read()
end
function io.scanDir(directory)
directory=directory or io.getDir()
local i, t, popen = 0, {}, io.popen
if os.getOS()=='unix' then
for filename in popen('ls -a \''..directory..'\''):lines() do
i = i + 1
t[i] = filename
end
else
for filename in popen('dir \''..directory..'\' /b'):lines() do
i = i + 1
t[i] = filename
end
end
return t
end
function io.buildFromTree(tbl, indent,folder)
if not indent then indent = 0 end
if not folder then folder = '' end
for k, v in pairs(tbl) do
formatting = string.rep(' ', indent) .. k .. ':'
if type(v) == 'table' then
if not(io.dirExists(folder..string.sub(formatting,1,-2))) then
io.mkDir(folder..string.sub(formatting,1,-2))
end
io.buildFromTree(v,0,folder..string.sub(formatting,1,-2)..'\\')
else
a=string.find(tostring(v),':',1,true)
if a then
file=string.sub(tostring(v),1,a-1)
data=string.sub(tostring(v),a+1)
io.mkFile(folder..file,data,'wb')
else
io.mkFile(folder..v,'','wb')
end
end
end
end
function io.cpFile(path,topath)
if os.getOS()=='unix' then
os.execute('cp '..file1..' '..file2)
else
os.execute('Copy '..path..' '..topath)
end
end
function io.delDir(directoryname)
if os.getOS()=='unix' then
os.execute('rm -rf '..directoryname)
else
os.execute('rmdir '..directoryname..' /s /q')
end
end
function io.delFile(path)
os.remove(path)
end
function io.mkDir(dirname)
os.execute('mkdir "' .. dirname..'"')
end
function io.mkFile(filename,data,tp)
if not(tp) then tp='wb' end
if not(data) then data='' end
file = io.open(filename, tp)
if file==nil then return end
file:write(data)
file:close()
end
function io.movFile(path,topath)
io.cpFile(path,topath)
io.delFile(path)
end
function io.listFiles(dir)
if not(dir) then dir='' end
local f = io.popen('dir \''..dir..'\'')
if f then
return f:read('*a')
else
print('failed to read')
end
end
function io.getDir(dir)
if not dir then return io.getWorkingDir() end
if os.getOS()=='unix' then
return os.capture('cd '..dir..' ; cd')
else
return os.capture('cd '..dir..' & cd')
end
end
function io.getWorkingDir()
return io.popen'cd':read'*l'
end
function io.fileExists(path)
g=io.open(path or '','r')
if path =='' then
p='empty path'
return nil
end
if g~=nil and true or false then
p=(g~=nil and true or false)
end
if g~=nil then
io.close(g)
else
return false
end
return p
end
function io.fileCheck(file_name)
if not file_name then print('No path inputed') return false end
local file_found=io.open(file_name, 'r')
if file_found==nil then
file_found=false
else
file_found=true
end
return file_found
end
function io.dirExists(strFolderName)
strFolderName = strFolderName or io.getDir()
local fileHandle, strError = io.open(strFolderName..'\\*.*','r')
if fileHandle ~= nil then
io.close(fileHandle)
return true
else
if string.match(strError,'No such file or directory') then
return false
else
return true
end
end
end
function io.getAllItems(dir)
local t=os.capture("cd \""..dir.."\" & dir /a-d | find",true):lines()
return t
end
function io.listItems(dir)
if io.dirExists(dir) then
temp=io.listFiles(dir) -- current directory if blank
if io.getDir(dir)=='C:\\\n' then
a,b=string.find(temp,'C:\\',1,true)
a=a+2
else
a,b=string.find(temp,'..',1,true)
end
temp=string.sub(temp,a+2)
list=string.linesToTable(temp)
temp=string.sub(temp,1,list[#list-2])
slist=string.lines(temp)
table.remove(slist,1)
table.remove(slist,#slist)
temp={}
temp2={}
for i=1,#slist do
table.insert(temp,string.sub(slist[i],40,-1))
end
return temp
else
return nil
end
end
function io.getDirectories(dir,l)
if dir then
dir=dir..'\\'
else
dir=''
end
local temp2=io.scanDir(dir)
for i=#temp2,1,-1 do
if io.fileExists(dir..temp2[i]) then
table.remove(temp2,i)
elseif l then
temp2[i]=dir..temp2[i]
end
end
return temp2
end
function io.getFiles(dir,l)
if dir then
dir=dir..'\\'
else
dir=''
end
local temp2=io.scanDir(dir)
for i=#temp2,1,-1 do
if io.dirExists(dir..temp2[i]) then
table.remove(temp2,i)
elseif l then
temp2[i]=dir..temp2[i]
end
end
return temp2
end
function io.getFullName(name)
local temp=name or arg[0]
if string.find(temp,'\\',1,true) or string.find(temp,'/',1,true) then
temp=string.reverse(temp)
a,b=string.find(temp,'\\',1,true)
if not(a) or not(b) then
a,b=string.find(temp,'/',1,true)
end
return string.reverse(string.sub(temp,1,b-1))
end
return temp
end
function io.getName(file)
local name=io.getFullName(file)
name=string.reverse(name)
a,b=string.find(name,'.',1,true)
name=string.sub(name,a+1,-1)
return string.reverse(name)
end
function io.readFile(file)
local f = io.open(file, 'rb')
local content = f:read('*all')
f:close()
return content
end
function io.getExtension(file)
local file=io.getFullName(file)
file=string.reverse(file)
local a,b=string.find(file,'.',0,true)
local temp=string.sub(file,1,b)
return string.reverse(temp)
end
function io.pathToTable(path)
local p=io.splitPath(path)
local temp={}
temp[p[1]]={}
local last=temp[p[1]]
for i=2,#p do
snd=last
last[p[i]]={}
last=last[p[i]]
end
return temp,last,snd
end
function io.splitPath(str)
return string.split(str,'[\\/]+')
end
function io.parseDir(dir,t)
io.tempFiles={}
function _p(dir)
local dirs=io.getDirectories(dir,true)
local files=io.getFiles(dir,true)
for i=1,#files do
p,l,s=io.pathToTable(files[i])
if t then
s[io.getFullName(files[i])]=io.readFile(files[i])
else
s[io.getFullName(files[i])]=io.open(files[i],'r+')
end
table.merge(io.tempFiles,p)
end
for i=1,#dirs do
table.merge(io.tempFiles,io.pathToTable(dirs[i]))
_p(dirs[i],t)
end
end
_p(dir)
return io.tempFiles
end
function io.parsedir(dir,f)
io.tempFiles={}
function _p(dir,f)
local dirs=io.getDirectories(dir,true)
local files=io.getFiles(dir,true)
for i=1,#files do
if not f then
table.insert(io.tempFiles,files[i])
else
f(files[i])
end
end
for i=1,#dirs do
_p(dirs[i],f)
end
end
_p(dir,f)
return io.tempFiles
end
function io.driveReady(drive)
drive=drive:upper()
if not(drive:find(':',1,true)) then
drive=drive..':'
end
drives=io.getDrives()
for i=1,#drives do
if drives[i]==drive then
return true
end
end
return false
end
function io.getDrives()
if os.getOS()=='windows' then
local temp={}
local t1=os.capture('wmic logicaldisk where drivetype=2 get deviceid, volumename',true)
local t2=os.capture('wmic logicaldisk where drivetype=3 get deviceid, volumename',true)
for drive,d2 in t1:gmatch('(.:)%s-(%w+)') do
if #d2>1 then
table.insert(temp,drive)
end
end
for drive in t2:gmatch('(.:)') do
table.insert(temp,drive)
end
return temp
end
error('Command is windows only!')
end
-- table Additions
function table.dump(t,indent)
local names = {}
if not indent then indent = '' end
for n,g in pairs(t) do
table.insert(names,n)
end
table.sort(names)
for i,n in pairs(names) do
local v = t[n]
if type(v) == 'table' then
if(v==t) then
print(indent..tostring(n)..': <-')
else
print(indent..tostring(n)..':')
table.dump(v,indent..' ')
end
else
if type(v) == 'function' then
print(indent..tostring(n)..'()')
else
print(indent..tostring(n)..': '..tostring(v))
end
end
end
end
function table.alphanumsort(o)
local function padnum(d) local dec, n = string.match(d, '(%.?)0*(.+)')
return #dec > 0 and ('%.12f'):format(d) or ('%s%03d%s'):format(dec, #n, n)
end
table.sort(o, function(a,b) return tostring(a):gsub('%.?%d+',padnum)..('%3d'):format(#b)< tostring(b):gsub('%.?%d+',padnum)..('%3d'):format(#a) end)
return o
end
function table.foreach(t,f)
for i,v in pairs(t) do
f(v)
end
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
function table.print(tbl, indent)
if not indent then indent = 0 end
for k, v in pairs(tbl) do
formatting = string.rep(' ', indent) .. k .. ': '
if type(v) == 'table' then
print(formatting)
table.print(v, indent+1)
else
print(formatting .. tostring(v))
end
end
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
function table.clear(t)
for k in pairs (t) do
t[k] = nil
end
end
function table.copy(t)
function deepcopy(orig)
local orig_type = type(orig)
local copy
if orig_type == 'table' then
copy = {}
for orig_key, orig_value in next, orig, nil do
copy[deepcopy(orig_key)] = deepcopy(orig_value)
end
setmetatable(copy, deepcopy(getmetatable(orig)))
else -- number, string, boolean, etc
copy = orig
end
return copy
end
return deepcopy(t)
end
function table.swap(tab,i1,i2)
tab[i1],tab[i2]=tab[i2],tab[i1]
end
function table.append(t1, ...)
t1,t2= t1 or {},{...}
for k,v in pairs(t2) do
t1[#t1+1]=t2[k]
end
return t1
end
function table.compare(t1, t2,d)
if d then
return table.deepCompare(t1,t2)
end
--if #t1 ~= #t2 then return false end
if #t2>#t1 then
for i=1,#t2 do
if t1[i] ~= t2[i] then
return false,t2[i]
end
end
else
for i=1,#t1 do
if t1[i] ~= t2[i] then
return false,t2[i]
end
end
end
return true
end
function table.deepCompare(t1,t2)
if t1==t2 then return true end
if (type(t1)~='table') then return false end
local mt1 = getmetatable(t1)
local mt2 = getmetatable(t2)
if( not table.deepCompare(mt1,mt2) ) then return false end
for k1,v1 in pairs(t1) do
local v2 = t2[k1]
if( not table.deepCompare(v1,v2) ) then return false end
end
for k2,v2 in pairs(t2) do
local v1 = t1[k2]
if( not table.deepCompare(v1,v2) ) then return false end
end
return true
end
function table.has(t,_v)
for i,v in pairs(t) do
if v==_v then
return true
end
end
return false
end
function table.reverse(tab)
local size = #tab
local newTable = {}
for i,v in ipairs (tab) do
newTable[size-i] = v
end
for i=1,#newTable do
tab[i]=newTable[i]
end
end
-- Math Additions
local Y = function(g) local a = function(f) return f(f) end return a(function(f) return g(function(x) local c=f(f) return c(x) end) end) end
local F = function(f) return function(n)if n == 0 then return 1 else return n*f(n-1) end end end
math.factorial = Y(F)
math.fib={}
math.fib.fibL={}
setmetatable(math.fib,{__call=function(self,n)
if n<=2 then
return 1
else
if self.fibL[n] then
return self.fibL[n]
else
local t=math.fib(n-1)+math.fib(n-2)
self.fibL[n]=t
return t
end
end
end})
local floor,insert = math.floor, table.insert
function math.basen(n,b)
n = floor(n)
if not b or b == 10 then return tostring(n) end
local digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
local t = {}
local sign = ''
if n < 0 then
sign = '-'
n = -n
end
repeat
local d = (n % b) + 1
n = floor(n / b)
insert(t, 1, digits:sub(d,d))
until n == 0
return sign .. table.concat(t,'')
end
function math.convbase(n,b,tb)
return math.basen(tonumber(tostring(n),b),tb)
end
if BigNum then
function BigNum.mod(a,b)
return a-((a/b)*b)
end
local floor,insert = math.floor, table.insert
function math.basen(n,b)
n = BigNum.new(n)
if not b or b == 10 then return tostring(n) end
local digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
local t = {}
local sign = ''
if n < BigNum.new(0) then
sign = '-'
n = -n
end
repeat
local d = BigNum.mod(n , b) + 1
n = n/b
d=tonumber(tostring(d))
insert(t, 1, digits:sub(d,d))
until tonumber(tostring(n)) == 0
return sign .. table.concat(t,'')
end
function math.to10(n,b)
local num=tostring(n)
local sum=BigNum.new()
local digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
for i=1,#num do
local v=digits:find(num:sub(i,i),1,true)
sum=sum+BigNum.new(tonumber(v)-1)*BigNum.pow(BigNum.new(b),BigNum.new(#num-i))
end
return sum
end
function math.convbase(n,b,tb)
return math.basen(math.to10(n,b),tb)
end
end
function math.numfix(n,x)
local str=tostring(n)
if #str<x then
str=('0'):rep(x-#str)..str
end
return str
end
-- Misc Additions
function smartPrint(...)
local args={...}
for i=1,#args do
if type(args[i])=='table' then
table.print(args[i])
else
print(args[i])
end
end
end
function totable(v)
if type(v)=='table' then return v end
return {v}
end
print(math.factorial(2))

3207
LoveKaraoke/core/bin.lua Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,103 @@
os.sleep=love.timer.sleep
function bin.load(file,s,r)
content, size = love.filesystem.read(file)
local temp=bin.new(content)
temp.filepath=file
return temp
end
function bin:tofile(filename)
if not(filename) or self.Stream then return nil end
love.filesystem.write(filename,self.data)
end
function bin.stream(file,l)
error("Sorry streaming is not available when using love2d :(, I am looking for a solution though :)")
end
function love.run()
if love.math then
love.math.setRandomSeed(os.time())
end
if love.event then
love.event.pump()
end
if love.load then love.load(arg) end
if love.timer then love.timer.step() end
local dt = 0
while true do
-- Process events.
if love.event then
love.event.pump()
for e,a,b,c,d in love.event.poll() do
if e == "quit" then
if not love.quit or not love.quit() then
if love.audio then
love.audio.stop()
end
return
end
end
love.handlers[e](a,b,c,d)
end
end
if love.timer then
love.timer.step()
dt = love.timer.getDelta()
end
if love.update then love.update(dt) end
if multi.boost then
for i=1,multi.boost-1 do
multi:uManager(dt)
end
end
multi:uManager(dt)
if love.window and love.graphics and love.window.isCreated() then
love.graphics.clear()
love.graphics.origin()
if love.draw then love.draw() end
multi.dManager()
love.graphics.setColor(255,255,255,255)
if multi.draw then multi.draw() end
love.graphics.present()
end
end
end
multi.drawF={}
function multi:dManager()
for ii=1,#multi.drawF do
multi.drawF[ii]()
end
end
function multi:onDraw(func,i)
i=i or 1
table.insert(self.drawF,i,func)
end
function multi:lManager()
if love.event then
love.event.pump()
for e,a,b,c,d in love.event.poll() do
if e == "quit" then
if not love.quit or not love.quit() then
if love.audio then
love.audio.stop()
end
return nil
end
end
love.handlers[e](a,b,c,d)
end
end
if love.timer then
love.timer.step()
dt = love.timer.getDelta()
end
if love.update then love.update(dt) end
multi:uManager(dt)
if love.window and love.graphics and love.window.isCreated() then
love.graphics.clear()
love.graphics.origin()
if love.draw then love.draw() end
multi.dManager()
love.graphics.setColor(255,255,255,255)
if multi.draw then multi.draw() end
love.graphics.present()
end
end

View File

@ -0,0 +1,106 @@
Hey guys, I am currently working on a net library which aims to make Async servers/clients a piece of cake. It is still in heavy development, but a release will be made soon. The basic features are done and work great. The modules are being worked on though.
Right now I have the core and 4 modules finished
+CORE <-- The base library
+net.identity <-- makes registration and login for users seamless
+net.sft <-- a Simple File Transfer module.
NOTE: Once net.aft Is complete net.sft will no longer be supported
+net.aft <-- an Advanced File Transfer module.
NOTE: As of right now user can only download files in aft
+net.chatting <-- allows for chatting between users
I will go into detail on how the core and 'stable' modules work
[color=#FF0000][b]Creating A Server[/b][/color]
EXAMPLE:1
[code]
--[[Filename: server.lua
+Dependencies:
+the net library
+the bin library
+the multimanager library
+luasocket
I suggest using luajit when creating the server for that extra boost in performance!
]]
require("net")
-- All you need is a port for the server init
server=net:newServer(12345)
--server=net:newTCPServer(12345)
--[[The commented line above creates a TCP server
CID: Client ID, unique for each user connected to the server. If you are using a TCP Server then you get the handle used for communication
IP_OR_HANDLE, works much like CID_OR_HANDLE, where you get a handle if using TCP
The reason for this is so modules could be made for both types of servers and work flawlessly, The only exception is the sft and aft module, TCP is recommended to ensure things are sent in order
PORT_OR_IP is like the above as well... Think of it this way UDP_TCP for each argument
]]
server:OnDataRecived(function(serv,data,CID_OR_HANDLE,IP_OR_HANDLE,PORT_OR_IP)
if data=="Hello Server!" then -- the client say hi lets say something back
serv:send(IP_OR_HANDLE,"Hey client!",PORT_OR_IP,CID_OR_HANDLE)-- cid only needs to be passed for UDP Servers, you can exclude the cid argument if you want though. The server stores ports and IPs for each CID while they are logged on!
end
end)
multi:mainloop()
[/code]
This is the basic setup for a server where you can send data back and forth. Note: modules that receive data will bypass triggering this event to save CPU time... Only events that are important get triggered If you want to speed up your code and make your event triggered when data you want gets passed use "!eventname! ...rest_of_your_data_here"
For example You would do this:
EXAMPLE:2
[code]
--[[
Assume your client is sending this data: "!gamedata! MOVE bob 10 10"
take a look at the new server OnDataRecieved event
]]
require("net")
server=net:newServer(12345)
world={["bob"]={x=0,y=0}}
server:OnDataRecived(function(serv,data,CID_OR_HANDLE,IP_OR_HANDLE,PORT_OR_IP)
local cmd,entity,arg1,arg2=data:match("!gamedata! (%S+) (%S+) (%S+) (%S+)")
if cmd=="MOVE" then
--lets move bob
world[entity].x=tonumber(arg1)
world[entity].y=tonumber(arg2)
--Now lets tell every client connected what happened
serv:sendAll("!gamedata! UPDATE "..entity.." "..arg1.." "..arg2) -- note only data is needed now!
end
end,"gamedata") -- notice this string right here it allows the CORE to know that you should call this when !gamedata! is within a method. This is useful especally when you have many connections to the server OnDataRecieved event
multi:mainloop()
[/code]
Now that was fun... But what if I'm sending binary data over to the client... Doesn't non-ASCII tend to get messed up?
Yes it does and even when using TCP data tends to not always transfer right
net.normalize(data) converts data into all ASCII characters to keep your data safe, so even binary data can be passed. Since this uses base64 encoding every 3 characters become 4 so your data size becomes a bit bigger. To avoid problems keep all data being normalized data under 384 characters! The data here is converted into 512 bytes.
net.denormalize(data) takes the normalized and converts it back to what it was originally.
Take a look at this example
EXAMPLE:3
[code]
--Assume your client is sending this data: "!gamedata! MSG bob NORMILZED_MSG NIL" empty data so pattern matches
require("net")
function CheckMSG(data)
-- TODO: modify the message for things that break our rules
returns data
end
server=net:newServer(12345)
world={["bob"]={x=0,y=0}}
server:OnDataRecived(function(serv,data,CID_OR_HANDLE,IP_OR_HANDLE,PORT_OR_IP)
local cmd,entity,arg1,arg2=data:match("!gamedata! (%S+) (%S+) (%S+) (%S+)")
if cmd=="MOVE" then
--lets move bob
world[entity].x=tonumber(arg1) -- we are sending the numbers as binary data
world[entity].y=tonumber(arg2)
--Now lets tell every client connected what happened
serv:sendAll("!gamedata! UPDATE "..entity.." "..arg1.." "..arg2) -- note only data is needed now!
elseif cmd=="MSG" then -- client is sending a message NOTE: I have a module that handles chatting however for example sake here is a simple chat message
--Lets look at the data We normalize the data because we can have \n characters in the data and TCP/UDP are line based so that would mess up packets!
local msg=CheckMSG(net.denormalize(arg1))
serv:sendAll("!gamedata! MSG "..entity.." "..msg.." NIL")
end
end,"gamedata") -- notice this string right here it allows the CORE to know that you should call this when !gamedata! is within a method. This is useful especally when you have many connections to the server OnDataRecieved event
multi:mainloop()
[/code]

View File

@ -0,0 +1,58 @@
require("parseManager")
require("net.identity")
require("net.aft")
net:registerModule("admin",{1,0,0})
-- Localhost does not need to log in to connect to the server and do whatever... This inculdes modules that you are writing for the server
-- LAN connections can request data from the server without logging in, but must log in to alter server settings
-- WAN connections can only request or alter settings if they are logged in
--[[
User levels: 1 - SERVEROWNER/localhost (Autodetect)
2 - ADMIN
3 - Modded User
4 - Privileged User
5 - Regular User
6 - Restricted User
7 - USER DEFINED
8 - USER DEFINED
9 - USER DEFINED
10 - Banned User
]]
if not io.dirExists("-ADMINS-") then
io.mkdir("-ADMINS-")
end
if not io.fileExists("-ADMINS-/LEVELS-List.dat") then
io.mkfile("-ADMINS-/LEVELS-List.dat")
end
net.statuslist={}
net.OnServerCreated:connect(function(s)
s.OnDataRecieved(function(self,data,CID_OR_HANDLE,IP_OR_HANDLE,PORT_OR_IP)
local IP=tonumber(IP_OR_HANDLE) or tonumber(PORT_OR_IP)
end,"admin")
function s:setUserLevel(user,n)
--
end
function s:makeAdmin(user)
--
end
function s:makeMod(user)
--
end
function s:makePrivileged(user)
--
end
function s:restrict(user)
--
end
function s:ban(user)
--
end
function s:getUserLevel(user)
--
end
end)
net.OnClientCreated:connect(function(c)
c.OnDataRecieved(function(self,data)
--
end,"admin")
end)

View File

@ -0,0 +1,325 @@
require("net")
--General Stuff
--[[ What this module does!
Allows reliable transfer of files over the internet!
Hash testing and piece transfer
TODO: Add uploading support... For now use sft less intensive on the client/server
]]
net:registerModule("aft",{2,1,0})
net.aft.transfers={}
net.aft.sinks={}
net.aft.cache={}
net.aft.preload={}
net.aft.pieceSize=768 -- max data packet for b64 that can safely be transfered without erroring! DO NOT CHANGE!!!
function net.aft:init() -- calling this initilizes the library and binds it to the servers and clients created
--Server Stuff
net.OnServerCreated:connect(function(s)
print("The aft(Advance File Transfer) Module has been loaded onto the server!")
if s.Type~="tcp" then
print("It is recomended that you use tcp to transfer files!")
end
s.OnUploadRequest=multi:newConnection() -- create an aft event
s.OnDownloadRequest=multi:newConnection()
s.OnClientClosed:connect(function(self,reason,cid,ip,port)
if net.aft.transfers[cid] then
for i,v in pairs(net.aft.transfers[cid]) do
v.resendAlarm:Destroy()
end
net.aft.transfers[cid]=nil
end
end)
function s:preloadFile(name)
net.aft.preload[name]={}
local temp=bin.stream(name)
temp:segmentedRead(768,function(data)
local unpackedDATA1=data:sub(1,384)
local unpackedDATA2=data:sub(385)
local packedDATA=net.normalize(unpackedDATA1)..net.normalize(unpackedDATA2)
table.insert(net.aft.preload,packedDATA)
end)
end
function s:isPreloaded(name)
return net.aft.preload[name]~=nil
end
s.allowSmallFileCaching=false
s.cachable=10 -- 10 MBs
s.OnDataRecieved(function(self,data,cid,ip,port) -- when the server recieves data this method is triggered
local cmd,arg1,arg2=data:match("!aft! (%S+) (%S+) (%S+)")
--print(cmd,arg1,arg2)
if cmd=="PIECE" then
local FID,piecenum=arg1,tonumber(arg2)
local pp=piecenum-1
net.aft.transfers[cid][FID].resendAlarm:Reset()
if net.aft.transfers[cid][FID] then
if pp>net.aft.transfers[cid][FID].pieces-1 then
self:send(ip,"!aft! DOWNLOAD INVALID_PIECENUM NIL NIL NIL",port)
print("ERROR 101")
else
if self:isPreloaded(name) then
self:send(ip,net.aft.preload[name][piecenum],port)
return
end
if self.allowSmallFileCaching then
if net.aft.cache[net.aft.transfers[cid][FID].name] then
if net.aft.cache[net.aft.transfers[cid][FID].name][piecenum] then
self:send(ip,net.aft.cache[net.aft.transfers[cid][FID].name][piecenum],port)
return
end
end
end
local ddata
local unpackedDATA=net.aft.transfers[cid][FID].sink:sub((pp*net.aft.pieceSize)+1,(pp+1)*net.aft.pieceSize)
local num=#unpackedDATA
if num<384 then
ddata="!aft! TRANS "..piecenum.." "..FID.." | "..net.normalize(unpackedDATA)
else
local unpackedDATA1=unpackedDATA:sub(1,384)
local unpackedDATA2=unpackedDATA:sub(385)
local packedDATA=net.normalize(unpackedDATA1)..net.normalize(unpackedDATA2)
ddata="!aft! TRANS "..piecenum.." "..FID.." | "..packedDATA
end
net.aft.transfers[cid][FID].resendAlarm.piecenum=piecenum
net.aft.transfers[cid][FID].resendAlarm.hash="" -- not needed anymore
net.aft.transfers[cid][FID].resendAlarm.packedDATA=packedDATA
-- SAFE
if self.allowSmallFileCaching then
net.aft.cache[net.aft.transfers[cid][FID].name][piecenum]=ddata
end
self:send(ip,ddata,port)
end
else
self:send(ip,"!aft! DOWNLOAD INVALID_FID NIL NIL NIL",port)
print("ERROR 102")
end
elseif cmd=="UPLOAD" then -- here we set up the spot for file writing
local FID,filename=arg1:match("(.-)|(.+)")
local struct={
FID=FID,
filename=filename,
numpieces=tonumber(arg2) or -1
}
if struct.numpieces==-1 then -- error handling catch it all :)
self:send(ip,"!aft! UPLOAD UPLOAD_REFUSED INVALID_NUMBER_OF_PIECES | |",port)
return
end
self.OnUploadRequest:Fire(struct,cid,ip,port)
if not(struct.deny) then -- block request or allow it
-- If we are allowed to lets do this
if not(net.aft.transfers.DOWNLOADS) then
net.aft.transfers.DOWNLOADS={}
end
if not(net.aft.transfers.DOWNLOADS[FID]) then
net.aft.transfers.DOWNLOADS[FID]={}
end
bin.new(""):tofile(struct.filename)
net.aft.transfers.DOWNLOADS[struct.FID].sink=struct.sink or bin.stream(struct.filename,false)
net.aft.transfers.DOWNLOADS[struct.FID].currentPiece=1
net.aft.transfers.DOWNLOADS[struct.FID].numPieces=tonumber(arg2)
--we got that setup... Lets Request a piece now!
self:send(ip,"!aft! PIECE 1 "..FID.." | |",port) -- request piece # 1
else
self:send(ip,"!aft! UPLOAD UPLOAD_REFUSED "..(struct.reason or "UNSPECIFIED_ERROR").." | |",port)
end
elseif cmd=="TRANS" then
local FID,piece=arg1:match("(.-)|(.+)")
local piece=tonumber(piece) or -1
if pieces==-1 then -- error handling catch it all :)
self:send(ip,"!aft! UPLOAD UPLOAD_CANCLED PIECE_DATA_MALFORMED | |",port)
return
end
if #arg2<512 then
net.aft.transfers.DOWNLOADS[FID].sink:tackE(net.denormalize(arg2))
else
net.aft.transfers.DOWNLOADS[FID].sink:tackE(net.denormalize(arg2:sub(1,512))..net.denormalize(arg2:sub(513)))
end
-- request the next piece
if piece==net.aft.transfers.DOWNLOADS[FID].numPieces then
-- We are done!
self:send(ip,"!aft! DONE "..FID.." | | |",port)
net.aft.transfers.DOWNLOADS[FID].sink:close() -- close the file
else
self:send(ip,"!aft! PIECE "..piece+1 .." "..FID.." | |",port)
end
elseif cmd=="REQUEST" then
local filename=arg1
local struct={
filename=filename
}
self.OnDownloadRequest:Fire(self,struct)
if io.fileExists(struct.filename) or struct.handle then
local FID=bin.new(filename):getRandomHash(16)
if struct.handle then
FID=struct.handle:getRandomHash(16)
end
if not net.aft.transfers[cid] then
net.aft.transfers[cid]={} -- setup server-client filestream
end
net.aft.transfers[cid][FID]={}
net.aft.transfers[cid][FID].name=struct.filename
if struct.handle then
net.aft.transfers[cid][FID].sink=struct.handle
else
net.aft.transfers[cid][FID].sink=bin.stream(struct.filename,false)
end
net.aft.transfers[cid][FID].size=net.aft.transfers[cid][FID].sink:getSize()
net.aft.transfers[cid][FID].pieces=math.ceil(net.aft.transfers[cid][FID].size/net.aft.pieceSize)
net.aft.transfers[cid][FID].resendAlarm=multi:newAlarm(.25)
net.aft.transfers[cid][FID].resendAlarm:OnRing(function(alarm)
if not(alarm.packedDATA) then return end
self:send(ip,"!aft! TRANS "..alarm.piecenum.." "..FID.." | "..alarm.packedDATA,port)
alarm:Reset()
end)
if self.allowSmallFileCaching then
if net.aft.transfers[cid][FID].size<=1024*self.cachable then -- 10 MB or smaller can be cached
net.aft.cache[struct.filename]={}
end
end
self:send(ip,"!aft! START "..net.aft.transfers[cid][FID].pieces.." "..FID.." "..filename.." NIL",port)
else
self:send(ip,"!aft! DOWNLOAD REQUEST_REFUSED NIL NIL NIL",port)
print("ERROR 103")
end
elseif cmd=="COMPLETE" then
net.aft.transfers[cid][arg1].resendAlarm:Destroy()
net.aft.transfers[cid][arg1]=nil
end
end,"aft") -- some new stuff
end)
--Client Stuff
net.OnClientCreated:connect(function(c)
c.OnPieceRecieved=multi:newConnection()
c.OnTransferStarted=multi:newConnection()
c.OnTransferCompleted=multi:newConnection()
c.OnFileRequestFailed=multi:newConnection() -- create an aft event
c.OnFileUploadFailed=multi:newConnection() -- not yet must ensure oneway works well first
c.OnDataRecieved(function(self,data) -- when the client recieves data this method is triggered
local cmd,pieces,FID,arg1,arg2=data:match("!aft! (%S+) (%S+) (%S+) (%S+) (%S+)")
--print(cmd,pieces,FID,arg1,arg2)
if cmd=="START" then-- FID filename #pieces
local struct={
FID=FID,
filename=arg1,
numpieces=tonumber(pieces)
}
self.OnTransferStarted:Fire(self,struct)
local fid,filename,np=struct.FID,struct.filename,struct.numpieces
local sink=""
if type(net.aft.sinks[filename])=="table" then
sink=net.aft.sinks[filename]
sink.file=filename
else
if net.aft.sinks[filename] then
bin.new():tofile(net.aft.sinks[filename])
sink=bin.stream(net.aft.sinks[filename],false)
else
bin.new():tofile(filename)
sink=bin.stream(filename,false)
end
end
net.aft.transfers[FID]={}
net.aft.transfers[FID].name=sink.file
net.aft.transfers[FID].sink=sink
net.aft.transfers[FID].currentPiece=1
net.aft.transfers[FID].piecesRecieved=0
net.aft.transfers[FID].numpieces=tonumber(pieces)
c:requestPiece(FID,1)
elseif cmd=="DONE" then
local FID=pieces
print(net.aft.transfers.UPLOADS[FID].name.." has Finished Uploading!")
self.OnTransferCompleted:Fire(self,net.aft.transfers[FID].name,"U")
net.aft.transfers[FID]=nil -- clean up
elseif cmd=="PIECE" then -- Server is asking for a piece to some file
local pieces=tonumber(pieces)
local pp=pieces-1
local unpackedDATA=net.aft.transfers.UPLOADS[FID].sink:sub((pp*net.aft.pieceSize)+1,(pp+1)*net.aft.pieceSize)
local num=#unpackedDATA
if num<384 then
self:send("!aft! TRANS "..FID.."|"..pieces.." "..net.normalize(unpackedDATA))
else
local unpackedDATA1=unpackedDATA:sub(1,384)
local unpackedDATA2=unpackedDATA:sub(385)
local packedDATA=net.normalize(unpackedDATA1)..net.normalize(unpackedDATA2)
self:send("!aft! TRANS "..FID.."|"..pieces.." "..packedDATA)
end
elseif cmd=="TRANS" then-- self,data,FID,piecenum,hash
if self.autoNormalization==false then -- if we already handled normalization in the main data packet then don't redo
local ddata
if #arg2<512 then
ddata=net.denormalize(arg2)
else
ddata=net.denormalize(arg2:sub(1,512))..net.denormalize(arg2:sub(513))
end
struct={
data=ddata,
FID=FID,
piecenum=tonumber(pieces),
numpieces=net.aft.transfers[FID].numpieces,
hash=arg1,
name=net.aft.transfers[FID].name,
}
else
struct={
data=arg2,
FID=FID,
piecenum=tonumber(pieces),
numpieces=net.aft.transfers[FID].numpieces,
hash=arg1,
name=net.aft.transfers[FID].name,
}
end
net.aft.transfers[FID].currentPiece=tonumber(pieces)
self.OnPieceRecieved:Fire(self,struct)
local data,FID,piecenum,hash=struct.data,struct.FID,struct.piecenum,struct.hash
net.aft.transfers[FID].sink:tackE(data)
net.aft.transfers[FID].piecesRecieved=net.aft.transfers[FID].piecesRecieved+1
if net.aft.transfers[FID].numpieces==net.aft.transfers[FID].piecesRecieved then
print(net.aft.transfers[FID].name.." has finished downloading!")
net.aft.transfers[FID].sink:close()
self:send("!aft! COMPLETE "..FID.." NIL") -- for clean up purposes
self.OnTransferCompleted:Fire(self,net.aft.transfers[FID].name)
else
self:requestPiece(FID,piecenum+1) -- get next piece
end
elseif cmd=="DOWNLOAD" then
local msg=FID
self.OnFileRequestFailed:Fire(msg)
print("Download Error!",msg)
elseif cmd=="UPLOAD" then
local msg=FID
self.OnFileUploadFailed:Fire(msg)
print("Upload Error!",msg)
end
end,"aft")
function c:requestFile(filename,sink) -- sinks data through a bin-stream sink if the filename you want otherwise the filename is used instead
self:send("!aft! REQUEST "..filename.." NIL")
if sink then
net.aft.sinks[filename]=sink
end
end
function c:requestUpload(filename)
if io.fileExists(filename) then
local FID=bin.new(filename):getRandomHash(16) -- We need this, but its not as important for client as it is for servers
local file=bin.stream(filename)
if not net.aft.transfers.UPLOADS then
net.aft.transfers.UPLOADS={}
end
if not net.aft.transfers.UPLOADS[FID] then
net.aft.transfers.UPLOADS[FID]={}
end
net.aft.transfers.UPLOADS[FID].sink=file -- client file management is much simpler since we only have to worry about 1 reciever/sender
net.aft.transfers.UPLOADS[FID].name=filename
net.aft.transfers.UPLOADS[FID].size=file:getSize()
net.aft.transfers.UPLOADS[FID].pieces=math.ceil(net.aft.transfers.UPLOADS[FID].size/net.aft.pieceSize)
self:send("!aft! UPLOAD "..FID.."|"..filename.." "..net.aft.transfers.UPLOADS[FID].pieces)-- Lets send the FID we will be using and the number of pieces the server should look out for
else
self.OnFileUploadFailed:Fire("File specified not found! "..filename.." does not exist!")
end
end
function c:requestPiece(FID,piecenum)
self:send("!aft! PIECE "..FID.." "..piecenum)
end
end)
end
if net.autoInit then
net.aft.init()
end

View File

@ -0,0 +1,273 @@
function net:newAUDPServer(port,servercode)
local c={}
c.udp=assert(socket.udp())
c.udp:settimeout(0)
c.udp:setsockname("*", port)
c.ips={}
c.Type="udp"
c.port=port
c.ids={}
c.servercode=servercode
c.bannedIPs={}
c.bannedCIDs={}
c.autoNormalization=false
function c:setUpdateRate(n)
print("Not needed in a audp server!")
end
function c:banCID(cid)
table.insert(self.bannedCIDs,cid)
end
function c:banIP(ip)
table.insert(self.bannedIPs,cid)
end
function c:send(ip,data,port,cid)
if self.autoNormalization then
data=net.normalize(data)
end
if self.servercode then
cid=cid or self:CIDFrom(ip,port)
if not self.ips[cid] then
print("Can't determine cid from client... sending the client a new one!")
local cid=net.resolveID(self)
print("Sending unique cid to client: "..cid)
self.ips[cid]={ip,port,0,self.servercode==nil}
print(ip)
self.udp:sendto("I!"..cid,ip,port)
if self.servercode then
self.udp:sendto("S!",ip,port)
end
return
end
if net.inList(self.bannedIPs,ip) or net.inList(self.bannedCIDs,cid) then
self.udp:sendto("BANNED CLIENT", ip, port or self.port)
elseif self.ips[cid][4] then
self.udp:sendto(data, ip, port or self.port)
elseif self.ips[cid][4]==false then
self.udp:sendto("Make sure your server code is correct!", ip, port)
end
else
self.udp:sendto(data, ip, port or self.port)
end
end
function c:pollClientModules(ip,port)
self:send(ip,"L!",port)
end
function c:CIDFrom(ip,port)
for i,v in pairs(self.ips) do
if(ip==v[1] and v[2]==port) then
return i
end
end
end
function c:sendAll(data)
for i,v in pairs(self.ips) do
self:send(v[1],data,v[2],i)
end
end
function c:sendAllBut(data,cid)
for i,v in pairs(self.ips) do
if i~=cid then
self:send(v[1],data,v[2],i)
end
end
end
function c:clientRegistered(cid)
return self.ips[cid]
end
function c:clientLoggedIn(cid)
if not self.clientRegistered(cid) then
return nil
end
return self.ips[cid][4]
end
function c:update()
local data,ip,port=self.udp:receivefrom()
if net.inList(self.bannedIPs,ip) or net.inList(self.bannedCIDs,cid) then
print("We will ingore data from a banned client!")
return
end
if data then
if self.autoNormalization then
data=net.denormalize(data)
end
if data:sub(1,4)=="pong" then
self.ips[data:sub(5,-1)][3]=os.clock()
elseif data:sub(1,2)=="S!" then
local cid=self:CIDFrom(ip,port)
if data:sub(3,-1)==self.servercode then
print("Servercode Accepted: "..self.servercode)
if self.ips[cid] then
self.ips[cid][4]=true
else
print("Server can't keep up! CID: "..cid.." has been skipped! Sending new CID to the client!")
local cid=net.resolveID(self)
print("Sending unique cid to client: "..cid)
self.ips[cid]={ip,port,0,self.servercode==nil}
print(ip)
self.udp:sendto("I!"..cid,ip,port)
if self.servercode then
self.udp:sendto("S!",ip,port)
end
end
else
self.udp:sendto("Make sure your server code is correct!", ip, port)
end
elseif data:sub(1,2)=="C!" then
local hook=(data:sub(11,-1)):match("!(.-)!")
self.OnDataRecieved:getConnection(hook):Fire(self,data:sub(11,-1),data:sub(3,10),ip,port)
elseif data:sub(1,2)=="E!" then
self.ips[data:sub(3,10)]=nil
obj.ids[data:sub(3,10)]=false
self.OnClientClosed:Fire(self,"Client Closed Connection!",data:sub(3,10),ip,port)
elseif data=="I!" then
local cid=net.resolveID(self)
print("Sending unique cid to client: "..cid)
self.ips[cid]={ip,port,os.clock(),self.servercode==nil}
print(ip)
self.udp:sendto("I!"..cid,ip,port)
if self.servercode then
self.udp:sendto("S!",ip,port)
end
self.OnClientConnected:Fire(self,cid,ip,port)
elseif data:sub(1,2)=="L!" then
cid,cList=data:sub(3,10),data:sub(11,-1)
local list={}
for m,v in cList:gmatch("(%S-):(%S-)|") do
list[m]=v
end
self.OnClientsModulesList:Fire(list,cid,ip,port)
end
end
for cid,dat in pairs(self.ips) do
if not((os.clock()-dat[3])<65) then
self.ips[cid]=nil
self.OnClientClosed:Fire(self,"Client lost Connection: ping timeout",cid,ip,port)
end
end
end
c.OnClientsModulesList=multi:newConnection()
c.OnDataRecieved=multi:newConnection()
c.OnClientClosed=multi:newConnection()
c.OnClientConnected=multi:newConnection()
c.connectiontest=multi:newAlarm(30)
c.connectiontest.link=c
c.connectiontest:OnRing(function(alarm)
--print("pinging clients!")
alarm.link:sendAll("ping")
alarm:Reset()
end)
multi:newLoop(function()
c:update()
end)
net.OnServerCreated:Fire(c)
return c
end
function net:newAUDPClient(host,port,servercode,nonluaServer)
local c={}
c.ip=assert(socket.dns.toip(host))
c.udp=assert(socket.udp())
c.udp:settimeout(0)
c.cid="NIL"
c.lastPing=0
c.Type="udp"
c.servercode=servercode
c.autoReconnect=true
c.autoNormalization=false
function c:pollPing(n)
return not((os.clock()-self.lastPing)<(n or 60))
end
function c:send(data)
if self.autoNormalization then
data=net.normalize(data)
end
self.udp:send("C!"..self.cid..data)
end
function c:sendRaw(data)
if self.autoNormalization then
data=net.normalize(data)
end
self.udp:send(data)
end
function c:getCID()
if self:IDAssigned() then
return self.cid
end
end
function c:close()
self:send("E!")
end
function c:IDAssigned()
return self.cid~="NIL"
end
function c:update()
local data=self.udp:receive()
if data then
if self.autoNormalization then
data=net.denormalize(data)
end
if data:sub(1,2)=="I!" then
self.cid=data:sub(3,-1)
self.OnClientReady:Fire(self)
elseif data=="S!" then
self.udp:send("S!"..(self.servercode or ""))
elseif data=="L!" then
local mods=""
local m=""
for i=1,#net.loadedModules do
m=net.loadedModules[i]
mods=mods..m..":"..net.getModuleVersion(m).."|"
end
self.udp:send("L!"..self.cid..mods)
elseif data=="ping" then
self.lastPing=os.clock()
self.OnPingRecieved:Fire(self)
self.udp:send("pong"..self.cid)
else
local hook=data:match("!(.-)!")
self.OnDataRecieved:getConnection(hook):Fire(self,data)
end
end
end
function c:reconnect()
if not nonluaServer then
self.cid="NIL"
c.udp:send("I!")
end
self.OnConnectionRegained:Fire(self)
end
c.pingEvent=multi:newEvent(function(self) return self.link:pollPing() end)
c.pingEvent:OnEvent(function(self)
if self.link.autoReconnect then
self.link.OnServerNotAvailable:Fire("Connection to server lost: ping timeout! Attempting to reconnect...")
self.link.OnClientDisconnected:Fire(self,"closed")
self.link:reconnect()
else
self.link.OnServerNotAvailable:Fire("Connection to server lost: ping timeout!")
self.link.OnClientDisconnected:Fire(self,"closed")
end
end)
c.pingEvent.link=c
c.OnPingRecieved=multi:newConnection()
c.OnDataRecieved=multi:newConnection()
c.OnServerNotAvailable=multi:newConnection()
c.OnClientReady=multi:newConnection()
c.OnClientDisconnected=multi:newConnection()
c.OnConnectionRegained=multi:newConnection()
c.notConnected=multi:newFunction(function(self)
self:hold(3)
if self.link:IDAssigned()==false then
self.link.OnServerNotAvailable:Fire("Can't connect to the server: no response from server")
end
end)
c.notConnected.link=c
if not nonluaServer then
c.udp:send("I!")
end
multi:newLoop(function()
c:update()
end)
multi:newJob(function() c.notConnected() end)
net.OnClientCreated:Fire(c)
return c
end

View File

@ -0,0 +1,134 @@
require("net")
require("net.identity")
--General Stuff
--[[ What this module does!
Adds
net.chatting:init()
server:OnChatRecieved(function({user,msg}) end)
client:OnChatRecieved(function(user,msg) end)
client:sendChat(user,msg)
]]
net:registerModule("chatting",{3,0,0})
net.chatting.users={}
function net.chatting:getUserIdFromIP(ip)
return net.chatting.users[ip]
end
function net.chatting:init() -- calling this initilizes the library and binds it to the servers and clients created
--Server Stuff
net.OnServerCreated:connect(function(s)
print("The Chatting Module has been loaded onto the server!")
s.OnUserLoggedIn(function(user,cid,ip,port,dTable)
dTable=loadstring("return "..dTable)()
local USERID=bin.new(user):getHash(32)
net.chatting.users[USERID]={dTable.nick,cid,ip,port,dTable} -- add users that log in to the userlist
net.chatting.users[ip]=USERID -- add users that log in to the userlist
local users={}
for i,v in pairs(net.chatting.users) do
if type(i)~="userdata" then
table.insert(users,i.."|"..net.chatting.users[i][1])
end
end
table.insert(users,"")
for i,v in pairs(s.ips) do
s:send(v,"!chatting! $Users|NIL|NIL '"..table.concat(users,",").."'")
end
end)
s.OnUserLoggerOut(function(self,user)
local USERID=bin.new(user):getHash(32)
local ip=net.chatting.users[USERID]
print(USERID)
net.chatting.users[USERID]=nil
net.chatting.users[ip]=nil
end)
s.OnDataRecieved(function(self,data,cid,ip,port) -- when the server recieves data this method is triggered
--First Lets make sure we are getting chatting data
local user,msg = data:match("!chatting! (%S-) (.+)")
if user and msg then
if user:sub(1,1)=="$" then
local cmd,arg=user:match("$(.+)|(.+)")
print("Using extended chatting protocal!")
if cmd=="DM" then
local struct={ -- pack the info up as a table so the server can do filtering and whatnot to the chat
user=user,
msg=net.denormalize(msg)
}
self.OnChatRecieved:Fire(struct,"PRIVATE")
print("USERID",arg)
self:send(net.chatting.users[arg][3],"!chatting! $DM|"..net.chatting.users[arg][1].."|"..net.chatting:getUserIdFromIP(ip).." "..net.normalize(struct.msg).."",net.chatting.users[arg][4])
elseif cmd=="getUsers" then
local users={}
for i,v in pairs(net.chatting.users) do
if type(i)~="userdata" then
table.insert(users,i.."|"..net.chatting.users[i][1])
end
end
table.insert(users,"")
self:send(ip,"!chatting! $Users|NIL|NIL "..table.concat(users,",").."",port)
end
else
local struct={ -- pack the info up as a table so the server can do filtering and whatnot to the chat
user=user,
msg=net.denormalize(msg)
}
self.OnChatRecieved:Fire(struct,"GROUP") -- trigger the chat event
local USERID=net.chatting:getUserIdFromIP(ip)
for i,v in pairs(self.ips) do
if ip==v then
self:send(v,"!chatting! 1|"..struct.user.."|"..USERID.." "..net.normalize(struct.msg).."")
else
self:send(v,"!chatting! 0|"..struct.user.."|"..USERID.." "..net.normalize(struct.msg).."")
end
end
end
end
end,"chatting")
s.rooms={}
function s:regesterRoom(roomname)
self.rooms[roomname]={}
end
s.OnChatRecieved=multi:newConnection() -- create a chat event
end)
--Client Stuff
net.OnClientCreated:connect(function(c)
c.OnDataRecieved(function(self,data) -- when the client recieves data this method is triggered
--First Lets make sure we are getting chatting data
local isself,user,USERID,msg = data:match("!chatting! (%S-)|(%S-)|(%S-) (.+)")
if not isself then return end
if tonumber(isself) then
--This is the client so our job here is done
local msg=net.denormalize(msg)
self.OnChatRecieved:Fire(user,msg,({["1"]=true, ["0"]=false})[isself],USERID) -- trigger the chat event
elseif isself:sub(1,1)=="$" then
local cmd=isself:match("$(.+)")
if cmd=="DM" then
local msg=net.denormalize(msg)
c.OnPrivateChatRecieved:Fire(user,msg,USERID)
elseif cmd=="Users" then
local tab={}
for ID,nick in msg:gmatch("(%S-)|(%S-),") do
tab[nick]=ID
end
c.OnUserList:Fire(tab)
end
end
end,"chatting")
function c:sendChat(user,msg)
self:send("!chatting! "..user.." "..net.normalize(msg).."")
end
function c:sendChatTo(user,touser,msg)
self:send("!chatting! $DM|"..touser.." "..net.normalize(msg).."")
end
function c:getUserList()
self:send("!chatting! $getUsers|NIL NIL")
end
function c:getChatFrom(userID)
self:send("!chatting! getPrivateChat|NIL "..userID) -- add if time permits
end
c.OnPrivateChatRecieved=multi:newConnection()
c.OnUserList=multi:newConnection()
c.OnChatRecieved=multi:newConnection() -- create a chat event
end)
end
if net.autoInit then
net.chatting:init()
end

View File

@ -0,0 +1,15 @@
require("parseManager")
require("net.identity") -- serversie module
net:registerModule("db",{1,0,0})
net.OnServerCreated:connect(function(s)
s.OnUserLoggedIn(function(user,cid,ip,port,dTable) -- dealing with userdata
--
end)
function s:createTable(PKey,fmt,path)
--
end
end)
--keys are case insensitive, hex and regular base 10 numbers are allowed as well as other structures
--We define a table below with keys and their max size

View File

@ -0,0 +1,24 @@
require("net")
net:registerModule("eft",{1,0,0})
--[[
This module provides a dedicated socket for file transfer
This allows us to do some more complex stuff with it
The only data that is non file stuff is the initial handshake
CMDs are done on the general socket while transfers are done on the file socket
]]
net.OnServerCreated:connect(function(s)
print("The eft(Expert File Transfer) Module has been loaded onto the server!")
if s.Type~="tcp" then
print("It is recomended that you use tcp to transfer files!")
end
s.OnDataRecieved(function(self,data,CID_OR_HANDLE,IP_OR_HANDLE,PORT_OR_IP)
--
end,"eft")
--
end)
net.OnClientCreated:connect(function(c)
c.OnDataRecieved(function(self,data)
--
end,"eft")
--
end)

View File

@ -0,0 +1,54 @@
require("net.identity")
net:registerModule("email",{1,0,0})
-- needs rewriting, where it uses the identity module and can send an email. EX: server:email(user,subject,body)
smtp = require 'socket.smtp'
ssl = require 'ssl' -- server side only so noworries for love2d users
function net.email.init(from,user,pass)
net.OnServerCreated:connect(function(s)
s.from=from
s.user=user
s.pass=pass
function s:sendMessage(subject, body, dTable)
local msg = {
headers = {
from = '<'..dTable.email..'>'
to = dTable.nick..' <'..dTable.email..'>',
subject = subject
},
body = body
}
local ok, err = smtp.send {
from = '<'..self.from..'>',
rcpt = '<'..dTable.email..'>',
source = smtp.message(msg),
user = self.user,
password = self.pass,
server = 'smtp.gmail.com',
port = 465,
create = net.sslCreate
}
if not ok then
print("Mail send failed", err) -- better error handling required
end
end
end)
end
function net.sslCreate()
local sock = socket.tcp()
return setmetatable({
connect = function(_, host, port)
local r, e = sock:connect(host, port)
if not r then return r, e end
sock = ssl.wrap(sock, {mode='client', protocol='tlsv1'})
return sock:dohandshake()
end
}, {
__index = function(t,n)
return function(_, ...)
return sock[n](sock, ...)
end
end
})
end

View File

@ -0,0 +1,53 @@
require("net.identity")
net:registerModule("email",{1,0,0})
smtp = require 'socket.smtp'
ssl = require 'ssl'
function net.email.init(from,user,pass)
net.OnServerCreated:connect(function(s)
s.from=from
s.user=user
s.pass=pass
function s:sendMessage(subject, body, dTable)
local msg = {
headers = {
from = '<'..dTable.email..'>'
to = dTable.nick..' <'..dTable.email..'>',
subject = subject
},
body = body
}
local ok, err = smtp.send {
from = '<'..self.from..'>',
rcpt = '<'..dTable.email..'>',
source = smtp.message(msg),
user = self.user,
password = self.pass,
server = 'smtp.gmail.com',
port = 465,
create = net.sslCreate
}
if not ok then
print("Mail send failed", err) -- better error handling required
end
end
end)
end
function net.sslCreate()
local sock = socket.tcp()
return setmetatable({
connect = function(_, host, port)
local r, e = sock:connect(host, port)
if not r then return r, e end
sock = ssl.wrap(sock, {mode='client', protocol='tlsv1'})
return sock:dohandshake()
end
}, {
__index = function(t,n)
return function(_, ...)
return sock[n](sock, ...)
end
end
})
end

View File

@ -0,0 +1,266 @@
require("net")
--General Stuff
--[[ What this module does!
Adds
net.identity:init()
]]
net:registerModule("identity",{2,1,0})--1.0.1 Note: Added eaiser ways to get user data using only cid
function net.hash(text,n)
n=n or 16
return bin.new(text.."jgmhktyf"):getHash(n)
end
net.identity.UIDS={}
net.identity.UIDS.ids={}
function net.identity:init() -- calling this initilizes the library and binds it to the servers and clients created
--Server Stuff
net.OnServerCreated(function(s)
s.userFolder="./USERS"
print("The identity Module has been loaded onto the server!")
function s:_isRegistered(user)
return io.fileExists(self.userFolder..net.hash(user)..".dat")
end
function s:getUserData(user)
local userdata=bin.load(self.userFolder..net.hash(user)..".dat")
local nick,dTable=userdata:match("%S-|%S-|(%S-)|(.+)")
return nick,loadstring("return "..(dTable or "{}"))()
end
function s:modifyUserData(user,oldpass,pass,nick,dTable)
if self:_isRegistered(user) then
local userdata=bin.load(self.userFolder..net.hash(user)..".dat")
local args={}
local _pass,_nick,_dTable=userdata:match("%S-|(%S-)|(%S-)|(.+)")
if oldpass~=_pass then
args.invalidPass=true
pass=_pass
end
if not nick then nick=_nick args.invalidNick=true end
table.merge(_dTable or {}, dTable or {})
bin.new(string.format("%s|%s|%s|%s\n",user,pass,nick,dTable)):tofile(self.userFolder..net.hash(user)..".dat")
else
return false
end
end
function s:getUserCred(user)
local userdata=bin.load(self.userFolder..net.hash(user)..".dat")
return userdata:match("%S-|(%S-)|")
end
function s:getUSERID(cid)
return (net.identity.UIDS[cid] or "User Not Logged In!")
end
function s:getUsername(cid)
return self:userLoggedIn(cid)
end
function s:getNickname(cid)
return self.loggedIn[self:getUsername(cid)].nick
end
function s:getdTable(cid)
return self.loggedIn[self:getUsername(cid)]
end
function s:getUserDat(cid)
return self:getUserDataHandle(self:getUsername(cid))
end
function s:getNickFromUSERID(USERID)
return bin.load(self.userFolder..net.hash(user)..".dat"):match("%S-|%S-|(%S-)|")
end
function s:userLoggedIn(cid)
for i,v in pairs(self.loggedIn) do
if v.cid==cid then
return i
end
end
return false
end
function s:setDataLocation(loc)
if not io.dirExists(loc) then
io.mkDir(loc)
end
self.userFolder=loc
end
s:setDataLocation("USERS/")
function s:logoutUser(user)
net.identity.UIDS.ids[user.UID]=nil
self.loggedIn[user]=nil
end
function s:loginUser(user,cid)
net.identity.UIDS[cid]=net.hash(user)
local nick,dTable=self:getUserData(user)
self.loggedIn[user]={}
table.merge(self.loggedIn[user],dTable or {})
self.loggedIn[user].cid=cid
self.loggedIn[user].nick=nick
self.loggedIn[user].UID=net.resolveID(net.identity.UIDS)
return self.loggedIn[user]
end
function s:getUserDataHandle(user)
return self.loggedIn[user]
end
function s:syncUserData(user,ip,port)
local handle=self:getUserDataHandle(user)
self:send(ip,"!identity! SYNC <-|"..bin.ToStr(handle).."|->",port)
end
s.loggedIn={}
s.allowDuplicateNicks=true
s.minimumNickLength=4
s.minimumUserLength=4
s.OnUserRegistered=multi:newConnection()
s.OnUserLoggedIn=multi:newConnection()
s.OnUserLoggerOut=multi:newConnection()
s.OnAlreadyLoggedIn=multi:newConnection()
s.OnPasswordForgotten=multi:newConnection()
s.OnDataRecieved:connect(function(self,data,cid,ip,port) -- when the server recieves data this method is triggered
local cmd,arg1,arg2,arg3,arg4 = data:match("!identity! (%S-) '(.-)' '(.-)' '(.-)' <%-|(.+)|%->")
if cmd=="register" then
local user,pass,nick,dTable = arg1,arg2,arg3,arg4
if self:_isRegistered(user) then
self:send(ip,"!identity! REGISTERED <-|"..user.."|->",port)
else
if not(self.userFolder:sub(-1,-1)=="/" or self.userFolder:sub(-1,-1)=="\\") then
self.userFolder=self.userFolder.."/"
end
local rets=self.OnUserRegistered:Fire(user,pass,nick,loadstring("return "..(dTable or "{}"))())
if #user<=self.minimumUserLength then
self:send(ip,"!identity! REGISTERREFUSED <-|Username too short|->",port)
return
end
if #user<=self.minimumNickLength then
self:send(ip,"!identity! REGISTERREFUSED <-|Nickname too short|->",port)
return
end
for i=1,#rets do
if rets[i][1]==false then
print("Server refused to accept registration request!")
self:send(ip,"!identity! REGISTERREFUSED <-|Unspecified Error|->",port)
return
end
end
multi:newFunction(function(func) -- anom func, allows for fancy multitasking
local dupnickfilemanager=bin.stream(self.userFolder.."Nicks.dat",false)
local isValid=func:newCondition(function() return t~=nil end)
local tab={}
local t=dupnickfilemanager:getBlock("s")
if self.allowDuplicateNicks==false then
while func:condition(isValid) do
tab[#tab]=t
if t==nick then
self:send(ip,"!identity! REGISTERREFUSED <-|Duplicate Nicks are not allowed|->",port)
dupnickfilemanager:close()
return
end
end
t=dupnickfilemanager:getBlock("s")
end
dupnickfilemanager:addBlock(nick.."|"..bin.new(user):getHash(32))
dupnickfilemanager:close()
bin.new(string.format("%s|%s|%s|%s\n",user,pass,nick,dTable)):tofile(self.userFolder..net.hash(user)..".dat")
self:send(ip,"!identity! REGISTEREDGOOD <-|"..user.."|->",port)
func=nil -- we dont want 1000s+ of these anom functions lying around
return
end)()-- lets call the function
end
return
elseif cmd=="login" then
local user,pass = arg1,arg2
local _pass=s:getUserCred(user)
if not(self:_isRegistered(user)) then
self:send(ip,"!identity! LOGINBAD <-|nil|->",port)
return
end
if pass==_pass then
if self:userLoggedIn(cid) then
self.OnAlreadyLoggedIn:Fire(self,user,cid,ip,port)
self:send(ip,"!identity! ALREADYLOGGEDIN <-|nil|->",port)
return
end
local handle=self:loginUser(user,cid) -- binds the cid to username
self:send(ip,"!identity! LOGINGOOD <-|"..bin.ToStr(handle).."|->",port)
self.OnUserLoggedIn:Fire(user,cid,ip,port,bin.ToStr(handle))
return
else
self:send(ip,"!identity! LOGINBAD <-|nil|->",port)
return
end
elseif cmd=="logout" then
self:logoutUser(user)
self.OnClientClosed:Fire(self,"User logged out!",cid,ip,port)
elseif cmd=="sync" then
local dTable = loadstring("return "..(arg4 or "{}"))()
local handle = self:getUserDataHandle(self:userLoggedIn(cid))
table.merge(handle,dTable)
elseif cmd=="pass" then
local user=arg1
if self:_isRegistered(user) then
self.OnPasswordForgotten:Fire(arg1,cid)
self:send(ip,"!identity! PASSREQUESTHANDLED <-|NONE|->",port)
else
self:send(ip,"!identity! NOUSER <-|"..user.."|->",port)
end
end
end)
s.OnClientClosed:connect(function(self,reason,cid,ip,port)
self.OnUserLoggerOut:Fire(self,self:userLoggedIn(cid),cid,reason)
end)
end,"identity")
--Client Stuff
net.OnClientCreated:connect(function(c)
c.userdata={}
c.OnUserLoggedIn=multi:newConnection()
c.OnBadLogin=multi:newConnection()
c.OnUserAlreadyRegistered=multi:newConnection()
c.OnUserAlreadyLoggedIn=multi:newConnection()
c.OnUserRegistered=multi:newConnection()
c.OnNoUserWithName=multi:newConnection()
c.OnPasswordRequest=multi:newConnection()
c.OnUserRegisterRefused=multi:newConnection()
function c:logout()
self:send("!identity! logout 'NONE' 'NONE' 'NONE' <-|nil|->")
end
c.OnDataRecieved(function(self,data) -- when the client recieves data this method is triggered
local cmd,arg1 = data:match("!identity! (%S-) <%-|(.+)|%->")
if cmd=="REGISTERED" then
self.OnUserAlreadyRegistered:Fire(self,arg1)
elseif cmd=="REGISTEREDGOOD" then
self.OnUserRegistered:Fire(self,arg1)
elseif cmd=="REGISTERREFUSED" then
self.OnUserRegisterRefused:Fire(self,arg1)
elseif cmd=="ALREADYLOGGEDIN" then
self.OnUserAlreadyLoggedIn:Fire(self,arg1)
elseif cmd=="LOGINBAD" then
self.OnBadLogin:Fire(self)
elseif cmd=="LOGINGOOD" then
local dTable=loadstring("return "..(arg1 or "{}"))()
table.merge(self.userdata,dTable)
self.OnUserLoggedIn:Fire(self,self.userdata)
elseif cmd=="SYNC" then
local dTable=loadstring("return "..(arg1 or "{}"))()
table.merge(self.userdata,dTable)
elseif cmd=="NOUSER" then
self.OnNoUserWithName:Fire(self,arg1)
elseif cmd=="PASSREQUESTHANDLED" then
self.OnPasswordRequest:Fire(self)
end
end,"identity")
function c:syncUserData()
self:send(string.format("!identity! sync 'NONE' 'NONE' 'NONE' <-|%s|->",bin.ToStr(dTable)))
end
function c:forgotPass(user)
self:send(string.format("!identity! pass '%s' 'NONE' 'NONE' <-|nil|->",user))
end
function c:getUserDataHandle()
return self.userdata
end
function c:logIn(user,pass)
self:send(string.format("!identity! login '%s' '%s' 'NONE' <-|nil|->",user,net.hash(pass)))
end
function c:register(user,pass,nick,dTable)
if dTable then
self:send(string.format("!identity! register '%s' '%s' '%s' <-|%s|->",user,net.hash(pass),nick,bin.ToStr(dTable)))
else
self:send(string.format("!identity! register '%s' '%s' '%s' <-|nil|->",user,net.hash(pass),nick))
end
end
end)
end
if net.autoInit then
net.identity:init()
end

View File

@ -0,0 +1,70 @@
require("net.identity")
require("net.aft")
require("net.users")
require("net.db")
net:registerModule("inbox",{1,0,0})
--self.OnUserLoggedIn:Fire(user,cid,ip,port,bin.ToStr(handle))
--allows the storing of messages that the user can recieve and view whenever. Allows user to also send messeges to users that are even offline!
--requires an account setup and nick name to be set at account creation
if not io.dirExists("INBOX") then
io.mkDir("INBOX")
end
net.inbox.dbfmt=db.format([=[
[INBOX]{
string MSG 0x800 -- contents of the message
}
[MAIL]{
string NAME 0x20 -- username
string UID 0x10 -- User ID
string NICK 0x20 -- Nickname
number[3] DATE -- list of numbers
table INBO INBOX -- Inbox
}
]=])
net.OnServerCreated:connect(function(s)
s.OnUserLoggedIn(function(user,cid,ip,port,dTable)
if not io.dirExists("INBOX/"..self:getUSERID(cid)) then -- Make sure inbox stuff is set up
io.mkDir("INBOX/"..self:getUSERID(cid))
bin.new():tofile("info.dat")
end
end)
s.OnDataRecieved(function(self,data,CID_OR_HANDLE,IP_OR_HANDLE,PORT_OR_IP)
if self:userLoggedIn(cid) then -- If the user is logged in we do the tests
local cmd,arg1,arg2=data:match("!inbox! (%S+) (%S+) (%S+)")
if cmd=="SEND" then
--
elseif cmd=="LIST" then
--
elseif cmd=="OPEN" then
--
elseif cmd=="DELETE" then
--
elseif cmd=="CLEAR" then
--
end
else
return
end
end,"inbox")
end)
net.OnClientCreated:connect(function(c)
c.OnDataRecieved(function(self,data)
--
end,"inbox")
function c:sendMessage(USERID,msg) -- USERID who, msg being sent. Server handles time stamps
self:send("!inbox! SEND "..USERID.." "..msg)
end
function c:checkInbox() -- returns list of msgIDs
self:send("!inbox! LIST NIL NIL")
end
function c:checkMsg(msgId)
self:send("!inbox! OPEN "..msgId.." NIL") -- server sends back msg content as a file
end
function c:deleteMessage(msgID)
self:send("!inbox! DELETE "..msgId.." NIL")
end
function c:clearInbox()
self:send("!inbox! CLEAR NIL NIL")
end
--
end)

View File

@ -0,0 +1,664 @@
--[[
UPCOMMING ADDITIONS
AUDP - advance udp/ Ensures packets arrive and handles late packets.
P2P - peer to peer (Server to set up initial connection)
Relay - offput server load (locally)
Threading - Simple threading (UDP/AUDP Only)
Priority handling
]]
--[[
TODO: Finish stuff for Priority handling
]]
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
function string.trim(s)
local from = s:match"^%s*()"
return from > #s and "" or s:match(".*%S", from)
end
socket=require("socket")
http=require("socket.http")
mime=require("mime")
net={}
net.Version={2,0,0} -- This will probably stay this version for quite a while... The modules on the otherhand will be more inconsistant
net._VERSION="2.0.0"
net.OnServerCreated=multi:newConnection()
net.OnClientCreated=multi:newConnection()
net.loadedModules={}
net.autoInit=true
function net.normalize(input)
local enc=mime.b64(input)
return enc
end
function net.denormalize(input)
local unenc=mime.unb64(input)
return unenc
end
function net.getLocalIP()
local someRandomIP = "192.168.1.122"
local someRandomPort = "3102"
local mySocket = socket.udp()
mySocket:setpeername(someRandomIP,someRandomPort)
local dat = (mySocket:getsockname())
mySocket:close()
return dat
end
function net.getExternalIP()
local data=http.request("http://whatismyip.org/")
return data:match("600;\">(%d-.%d-.%d-.%d-)</span>")
end
function net:registerModule(mod,version)
if net[mod] then
error("Module by the name: "..mod.." has already been registered! Remember some modules are internal and use certain names!")
end
table.insert(self.loadedModules,mod)
net[mod]={}
if version then
net[mod].Version=version
net[mod]._VERSION=version[1].."."..version[2].."."..version[3]
else
net[mod].Version={1,0,0}
net[mod]._VERSION={1,0,0}
end
return {Version=version,_VERSION=version[1].."."..version[2].."."..version[3]}
end
function net.getModuleVersion(ext)
if not ext then
return string.format("%d.%d.%d",net.Version[1],net.Version[2],net.Version[3])
end
return string.format("%d.%d.%d",net[ext].Version[1],net[ext].Version[2],net[ext].Version[3])
end
function net.resolveID(obj)
local num=math.random(10000000,99999999)
if obj[tostring(num)] then
return net.resolveID(obj)
end
obj.ids[tostring(num)]=true
return tostring(num)
end
function net.inList(list,dat)
for i,v in pairs(list) do
if v==dat then
return true
end
end
return false
end
function net.setTrigger(funcW,funcE)
multi:newTrigger(func)
end
net:registerModule("net",net.Version)
-- Client broadcast
function net:newCastedClient(name) -- connects to the broadcasted server
local listen = socket.udp() -- make a new socket
listen:setsockname(net.getLocalIP(), 11111)
listen:settimeout(0)
local timer=multi:newTimer()
while true do
local data, ip, port = listen:receivefrom()
if timer:Get()>3 then
error("Timeout! Server by the name: "..name.." has not been found!")
end
if data then
local n,tp,ip,port=data:match("(%S-)|(%S-)|(%S-):(%d+)")
if n:match(name) then
print("Found Server!",n,tp,ip,port)
if tp=="tcp" then
return net:newTCPClient(ip,tonumber(port))
else
return net:newClient(ip,tonumber(port))
end
end
end
end
end
-- UDP Stuff
function net:newServer(port,servercode)
local c={}
c.udp=assert(socket.udp())
c.udp:settimeout(0)
c.udp:setsockname("*", port)
c.ips={}
c.Type="udp"
c.port=port
c.ids={}
c.servercode=servercode
c.bannedIPs={}
c.bannedCIDs={}
c.autoNormalization=false
function c:setUpdateRate(n)
print("Not needed in a udp server!")
end
function c:banCID(cid)
table.insert(self.bannedCIDs,cid)
end
function c:banIP(ip)
table.insert(self.bannedIPs,cid)
end
c.broad=socket.udp()
c.hostip=net.getLocalIP()
function c:broadcast(name)
local loop=multi:newTLoop(function(dt,loop)
self.broad:setoption('broadcast',true)
self.broad:sendto(name.."|"..self.Type.."|"..self.hostip..":"..self.port, "255.255.255.255", 11111)
self.broad:setoption('broadcast',false)
end,1)
end
function c:send(ip,data,port,cid)
if self.autoNormalization then
data=net.normalize(data)
end
if self.servercode then
cid=cid or self:CIDFrom(ip,port)
if not self.ips[cid] then
print("Can't determine cid from client... sending the client a new one!")
local cid=net.resolveID(self)
print("Sending unique cid to client: "..cid)
self.ips[cid]={ip,port,0,self.servercode==nil}
print(ip)
self.udp:sendto("I!"..cid,ip,port)
if self.servercode then
self.udp:sendto("S!",ip,port)
end
return
end
if net.inList(self.bannedIPs,ip) or net.inList(self.bannedCIDs,cid) then
self.udp:sendto("BANNED CLIENT", ip, port or self.port)
elseif self.ips[cid][4] then
self.udp:sendto(data, ip, port or self.port)
elseif self.ips[cid][4]==false then
self.udp:sendto("Make sure your server code is correct!", ip, port)
end
else
self.udp:sendto(data, ip, port or self.port)
end
end
function c:pollClientModules(ip,port)
self:send(ip,"L!",port)
end
function c:CIDFrom(ip,port)
for i,v in pairs(self.ips) do
if(ip==v[1] and v[2]==port) then
return i
end
end
end
function c:sendAll(data)
for i,v in pairs(self.ips) do
self:send(v[1],data,v[2],i)
end
end
function c:sendAllBut(data,cid)
for i,v in pairs(self.ips) do
if i~=cid then
self:send(v[1],data,v[2],i)
end
end
end
function c:clientRegistered(cid)
return self.ips[cid]
end
function c:clientLoggedIn(cid)
if not self.clientRegistered(cid) then
return nil
end
return self.ips[cid][4]
end
function c:update()
local data,ip,port=self.udp:receivefrom()
if net.inList(self.bannedIPs,ip) or net.inList(self.bannedCIDs,cid) then
print("We will ingore data from a banned client!")
return
end
if data then
if self.autoNormalization then
data=net.denormalize(data)
end
if data:sub(1,4)=="pong" then
--print("Recieved pong from: "..data:sub(5,-1))
self.ips[data:sub(5,-1)][3]=os.clock()
elseif data:sub(1,2)=="S!" then
local cid=self:CIDFrom(ip,port)
if data:sub(3,-1)==self.servercode then
print("Servercode Accepted: "..self.servercode)
if self.ips[cid] then
self.ips[cid][4]=true
else
print("Server can't keep up! CID: "..cid.." has been skipped! Sending new CID to the client!")
local cid=net.resolveID(self)
print("Sending unique cid to client: "..cid)
self.ips[cid]={ip,port,0,self.servercode==nil}
print(ip)
self.udp:sendto("I!"..cid,ip,port)
if self.servercode then
self.udp:sendto("S!",ip,port)
end
end
else
self.udp:sendto("Make sure your server code is correct!", ip, port)
end
elseif data:sub(1,2)=="C!" then
local hook=(data:sub(11,-1)):match("!(.-)!")
self.OnDataRecieved:getConnection(hook):Fire(self,data:sub(11,-1),data:sub(3,10),ip,port)
elseif data:sub(1,2)=="E!" then
self.ips[data:sub(3,10)]=nil
obj.ids[data:sub(3,10)]=false
self.OnClientClosed:Fire(self,"Client Closed Connection!",data:sub(3,10),ip,port)
elseif data=="I!" then
local cid=net.resolveID(self)
print("Sending unique cid to client: "..cid)
self.ips[cid]={ip,port,os.clock(),self.servercode==nil}
print(ip)
self.udp:sendto("I!"..cid,ip,port)
if self.servercode then
self.udp:sendto("S!",ip,port)
end
self.OnClientConnected:Fire(self,cid,ip,port)
elseif data:sub(1,2)=="L!" then
cid,cList=data:sub(3,10),data:sub(11,-1)
local list={}
for m,v in cList:gmatch("(%S-):(%S-)|") do
list[m]=v
end
self.OnClientsModulesList:Fire(list,cid,ip,port)
end
end
for cid,dat in pairs(self.ips) do
if not((os.clock()-dat[3])<65) then
self.ips[cid]=nil
self.OnClientClosed:Fire(self,"Client lost Connection: ping timeout",cid,ip,port)
end
end
end
c.OnClientsModulesList=multi:newConnection()
c.OnDataRecieved=multi:newConnection()
c.OnClientClosed=multi:newConnection()
c.OnClientConnected=multi:newConnection()
c.connectiontest=multi:newAlarm(30)
c.connectiontest.link=c
c.connectiontest:OnRing(function(alarm)
--print("pinging clients!")
alarm.link:sendAll("ping")
alarm:Reset()
end)
multi:newLoop(function()
c:update()
end)
net.OnServerCreated:Fire(c)
return c
end
function net:newClient(host,port,servercode,nonluaServer)
local c={}
c.ip=assert(socket.dns.toip(host))
c.udp=assert(socket.udp())
c.udp:settimeout(0)
c.udp:setpeername(c.ip, port)
c.cid="NIL"
c.lastPing=0
c.Type="udp"
c.servercode=servercode
c.autoReconnect=true
c.autoNormalization=false
function c:pollPing(n)
return not((os.clock()-self.lastPing)<(n or 60))
end
function c:send(data)
if self.autoNormalization then
data=net.normalize(data)
end
self.udp:send("C!"..self.cid..data)
end
function c:sendRaw(data)
if self.autoNormalization then
data=net.normalize(data)
end
self.udp:send(data)
end
function c:getCID()
if self:IDAssigned() then
return self.cid
end
end
function c:close()
self:send("E!")
end
function c:IDAssigned()
return self.cid~="NIL"
end
function c:update()
local data=self.udp:receive()
if data then
if self.autoNormalization then
data=net.denormalize(data)
end
if data:sub(1,2)=="I!" then
self.cid=data:sub(3,-1)
self.OnClientReady:Fire(self)
elseif data=="S!" then
self.udp:send("S!"..(self.servercode or ""))
elseif data=="L!" then
local mods=""
local m=""
for i=1,#net.loadedModules do
m=net.loadedModules[i]
mods=mods..m..":"..net.getModuleVersion(m).."|"
end
self.udp:send("L!"..self.cid..mods)
elseif data=="ping" then
self.lastPing=os.clock()
self.OnPingRecieved:Fire(self)
self.udp:send("pong"..self.cid)
else
local hook=data:match("!(.-)!")
self.OnDataRecieved:getConnection(hook):Fire(self,data)
end
end
end
function c:reconnect()
if not nonluaServer then
self.cid="NIL"
c.udp:send("I!")
end
self.OnConnectionRegained:Fire(self)
end
c.pingEvent=multi:newEvent(function(self) return self.link:pollPing() end)
c.pingEvent:OnEvent(function(self)
if self.link.autoReconnect then
self.link.OnServerNotAvailable:Fire("Connection to server lost: ping timeout! Attempting to reconnect...")
self.link.OnClientDisconnected:Fire(self,"closed")
self.link:reconnect()
else
self.link.OnServerNotAvailable:Fire("Connection to server lost: ping timeout!")
self.link.OnClientDisconnected:Fire(self,"closed")
end
end)
c.pingEvent.link=c
c.OnPingRecieved=multi:newConnection()
c.OnDataRecieved=multi:newConnection()
c.OnServerNotAvailable=multi:newConnection()
c.OnClientReady=multi:newConnection()
c.OnClientDisconnected=multi:newConnection()
c.OnConnectionRegained=multi:newConnection()
c.notConnected=multi:newFunction(function(self)
self:hold(3)
if self.link:IDAssigned()==false then
self.link.OnServerNotAvailable:Fire("Can't connect to the server: no response from server")
end
end)
c.notConnected.link=c
if not nonluaServer then
c.udp:send("I!")
end
multi:newLoop(function()
c:update()
end)
multi:newJob(function() c.notConnected() end)
net.OnClientCreated:Fire(c)
return c
end
--TCP Stuff
function net:newTCPServer(port)
local c={}
c.tcp=assert(socket.bind("*", port))
c.tcp:settimeout(0)
c.ip,c.port=c.tcp:getsockname()
c.ips={}
c.port=port
c.ids={}
c.bannedIPs={}
c.Type="tcp"
c.rMode="*l"
c.sMode="*l"
c.updaterRate=1
c.autoNormalization=false
c.updates={}
c.links={}
c.broad=socket.udp()
c.hostip=net.getLocalIP()
function c:broadcast(name)
local loop=multi:newTLoop(function(dt,loop)
self.broad:setoption('broadcast',true)
self.broad:sendto(name.."|"..self.Type.."|"..self.hostip..":"..self.port, "255.255.255.255", 11111)
self.broad:setoption('broadcast',false)
end,1)
end
function c:setUpdateRate(n)
self.updaterRate=n
end
function c:setReceiveMode(mode)
self.rMode=mode
end
function c:setSendMode(mode)
self.rMode=mode
end
function c:banCID(cid)
print("Function not supported on a tcp server!")
end
function c:banIP(ip)
table.insert(self.bannedIPs,cid)
end
function c:send(handle,data)
if self.autoNormalization then
data=net.normalize(data)
end
if self.sMode=="*l" then
handle:send(data.."\n")
else
handle:send(data)
end
end
function c:sendAllData(handle,data)
if self.autoNormalization then
data=net.normalize(data)
end
handle:send(data)
end
function c:pollClientModules(ip,port)
self:send(ip,"L!",port)
end
function c:CIDFrom(ip,port)
print("Method not supported when using a TCP Server!")
return "CIDs in TCP work differently!"
end
function c:sendAll(data)
for i,v in pairs(self.ips) do
self:send(v,data)
end
end
function c:sendAllBut(data,cid)
for i,v in pairs(self.ips) do
if not(cid==i) then
self:send(v,data)
end
end
end
function c:clientRegistered(cid)
return self.ips[cid]
end
function c:clientLoggedIn(cid)
return self.ips[cid]
end
function c:getUpdater(cid)
return self.updates[cid]
end
function c:update()
local client = self.tcp:accept(self.rMode)
if not client then return end
table.insert(self.ips,client)
client:settimeout(0)
--client:setoption('tcp-nodelay', true)
client:setoption('keepalive', true)
ip,port=client:getpeername()
if ip and port then
print("Got connection from: ",ip,port)
local updater=multi:newUpdater(skip)
self.updates[client]=updater
self.OnClientConnected:Fire(self,self.client,self.client,ip)
updater:OnUpdate(function(self)
local data, err = self.client:receive(self.rMode or self.Link.rMode)
if err=="closed" then
for i=1,#self.Link.ips do
if self.Link.ips[i]==self.client then
table.remove(self.Link.ips,i)
end
end
self.Link.OnClientClosed:Fire(self.Link,"Client Closed Connection!",self.client,self.client,ip)
self.Link.links[self.client]=nil -- lets clean up
self:Destroy()
end
if data then
if self.autoNormalization then
data=net.denormalize(data)
end
if net.inList(self.Link.bannedIPs,ip) then
print("We will ingore data from a banned client!")
return
end
local hook=data:match("!(.-)!")
self.Link.OnDataRecieved:getConnection(hook):Fire(self.Link,data,self.client,self.client,ip,self)
if data:sub(1,2)=="L!" then
cList=data
local list={}
for m,v in cList:gmatch("(%S-):(%S-)|") do
list[m]=v
end
self.Link.OnClientsModulesList:Fire(list,self.client,self.client,ip)
end
end
end)
updater:setSkip(self.updaterRate)
updater.client=client
updater.Link=self
function updater:setReceiveMode(mode)
self.rMode=mode
end
self.links[client]=updater
end
end
c.OnClientsModulesList=multi:newConnection()
c.OnDataRecieved=multi:newConnection()
c.OnClientClosed=multi:newConnection()
c.OnClientConnected=multi:newConnection()
multi:newLoop(function()
c:update()
end)
net.OnServerCreated:Fire(c)
return c
end
function net:newTCPClient(host,port)
local c={}
c.ip=assert(socket.dns.toip(host))
c.port=port
c.tcp=socket.connect(c.ip,port)
if not c.tcp then
print("Can't connect to the server: no response from server")
return false
end
c.tcp:settimeout(0)
--c.tcp:setoption('tcp-nodelay', true)
c.tcp:setoption('keepalive', true)
c.Type="tcp"
c.autoReconnect=true
c.rMode="*l"
c.sMode="*l"
c.autoNormalization=false
function c:setReceiveMode(mode)
self.rMode=mode
end
function c:setSendMode(mode)
self.sMode=mode
end
function c:send(data)
if self.autoNormalization then
data=net.normalize(data)
end
if self.sMode=="*l" then
ind,err=self.tcp:send(data.."\n")
else
ind,err=self.tcp:send(data)
end
if err=="closed" then
self.OnClientDisconnected:Fire(self,err)
elseif err=="timeout" then
self.OnClientDisconnected:Fire(self,err)
elseif err then
print(err)
end
end
function c:sendRaw(data)
if self.autoNormalization then
data=net.normalize(data)
end
self.tcp:send(data)
end
function c:getCID()
return "No Cid on a tcp client!"
end
function c:close()
self.tcp:close()
end
function c:IDAssigned()
return true
end
function c:update()
if not self.tcp then return end
local data,err=self.tcp:receive()
if err=="closed" then
self.OnClientDisconnected:Fire(self,err)
elseif err=="timeout" then
self.OnClientDisconnected:Fire(self,err)
elseif err then
print(err)
end
if data then
if self.autoNormalization then
data=net.denormalize(data)
end
local hook=data:match("!(.-)!")
self.OnDataRecieved:getConnection(hook):Fire(self,data)
end
end
function c:reconnect()
multi:newFunction(function(func)
self.tcp=socket.connect(self.ip,self.port)
if self.tcp==nil then
print("Can't connect to the server: No response from server!")
func:hold(3)
self:reconnect()
return
end
self.OnConnectionRegained:Fire(self)
self.tcp:settimeout(0)
--self.tcp:setoption('tcp-nodelay', true)
self.tcp:setoption('keepalive', true)
end)
end
c.event=multi:newEvent(function(event)
return event.link:IDAssigned()
end)
c.event:OnEvent(function(event)
event.link.OnClientReady:Fire(event.link)
end)
c.event.link=c
c.OnClientReady=multi:newConnection()
c.OnClientDisconnected=multi:newConnection()
c.OnDataRecieved=multi:newConnection()
c.OnConnectionRegained=multi:newConnection()
multi:newLoop(function()
c:update()
end)
net.OnClientCreated:Fire(c)
return c
end

View File

@ -0,0 +1,7 @@
net.OnServerCreated:connect(function(s)
print("The logging Module has been loaded onto the server!")
s.OnDataRecieved:fConnect(function(self,data,cid,ip,port)
log(tostring(ip)..":"..tostring(port),"Server-log.log")
log(data,"Server-log.log")
end)
end)

View File

@ -0,0 +1,7 @@
net.OnServerCreated:connect(function(s)
print("The logging Module has been loaded onto the server!")
s.OnDataRecieved:fConnect(function(self,data,cid,ip,port)
log(tostring(ip)..":"..tostring(port),"Server-log.log")
log(data,"Server-log.log")
end)
end)

View File

@ -0,0 +1,31 @@
net:registerModule("p2p",{1,0,0})
net.p2p.peerdata={}
--[[
PID(peerID)=<CID|IP|PORT>
]]
function net.newP2PClient(host,port)
--
end
function net.aft:init() -- calling this initilizes the library and binds it to the servers and clients created
--Server Stuff
net.OnServerCreated:connect(function(s)
print("The aft(Advance File Transfer) Module has been loaded onto the server!")
if s.Type~="udp" then
print("As of right now p2p is only avaliable using udp!")
return "ERR_NOT_UDP"
end
s.OnDataRecieved(function(self,data,cid,ip,port) -- when the server recieves data this method is triggered
--
end,"p2p") -- some new stuff
end)
--Client Stuff
net.OnClientCreated:connect(function(c)
c.OnDataRecieved(function(self,data) -- when the client recieves data this method is triggered
--
end,"p2p")
end)
end
if net.autoInit then
net.aft.init()
end

View File

@ -0,0 +1,31 @@
net:registerModule("p2p",{1,0,0})
net.p2p.peerdata={}
--[[
PID(peerID)=<CID|IP|PORT>
]]
function net.newP2PClient(host,port)
--
end
function net.aft:init() -- calling this initilizes the library and binds it to the servers and clients created
--Server Stuff
net.OnServerCreated:connect(function(s)
print("The aft(Advance File Transfer) Module has been loaded onto the server!")
if s.Type~="udp" then
print("As of right now p2p is only avaliable using udp!")
return "ERR_NOT_UDP"
end
s.OnDataRecieved(function(self,data,cid,ip,port) -- when the server recieves data this method is triggered
--
end,"p2p") -- some new stuff
end)
--Client Stuff
net.OnClientCreated:connect(function(c)
c.OnDataRecieved(function(self,data) -- when the client recieves data this method is triggered
--
end,"p2p")
end)
end
if net.autoInit then
net.aft.init()
end

View File

@ -0,0 +1,24 @@
require("net")
net:registerModule("relay",{1,0,0})
net.OnServerCreated:connect(function(s)
s.OnDataRecieved(function(self,data,CID_OR_HANDLE,IP_OR_HANDLE,PORT_OR_IP)
local IP=""
if self.Type=="tcp" then
error("Relays only work when using UDP or AUDP! They will not function with TCP! Support may one day be added for TCP!")
else
IP=IP_OR_HANDLE
end
local cmd,dat=data:match("!relay! (%S-) (.+)")
if cmd == "RELAY" then
--
elseif cmd == "LOAD" then
local cpuload=multi:getLoad()
self:send(IP_OR_HANDLE,"!relay! LOAD "..cpuload,PORT_OR_IP)
end
end,"relay")
end)
net.OnClientCreated:connect(function(c)
c.OnDataRecieved(function(self,data)
--
end,"relay")
end)

View File

@ -0,0 +1,48 @@
require("net")
--General Stuff
--[[ What this module does!
Adds
net.settings:init()
server:regSetting(namespace,setting)
]]
net:registerModule("settings",{1,0,0})
net.settings.config={}
function net.settings:init() -- calling this initilizes the library and binds it to the servers and clients created
--Server Stuff
net.OnServerCreated:connect(function(s)
print("The Settings Module has been loaded onto the server!")
s.OnDataRecieved(function(self,data,cid,ip,port) -- when the server recieves data this method is triggered
local namespace,args=data:match("!settings! (%s+) (.+)")
local args
if namespace then
for i,v in pairs(net.settings.config) do
args={data:match(v[1])}
if #args~=0 then
v[2]:Fire(self,data,cid,ip,port,unpack(args))
break
end
end
end
end,"settings")
function s:regSetting(namespace,settings)
if not net.settings.config[namespace] then
net.settings.config[namespace]={}
end
local connection=multi:newConnection()
table.insert(net.settings.config[namespace],{"!settings! "..namespace.." "..settings,connection})
return connection
end
end)
--Client Stuff
net.OnClientCreated:connect(function(c)
c.OnDataRecieved:(function(self,data) -- when the client recieves data this method is triggered
--First Lets make sure we are getting Setting data
end,"setings")
function sendSetting(namespace,args)
self:send("!settings! "..namespace.." "..args)
end
end)
end
if net.autoInit then
net.settings:init()
end

View File

@ -0,0 +1,48 @@
require("net")
--General Stuff
--[[ What this module does!
Adds
net.settings:init()
server:regSetting(namespace,setting)
]]
net:registerModule("settings",{1,0,0})
net.settings.config={}
function net.settings:init() -- calling this initilizes the library and binds it to the servers and clients created
--Server Stuff
net.OnServerCreated:connect(function(s)
print("The Settings Module has been loaded onto the server!")
s.OnDataRecieved(function(self,data,cid,ip,port) -- when the server recieves data this method is triggered
local namespace,args=data:match("!settings! (%s+) (.+)")
local args
if namespace then
for i,v in pairs(net.settings.config) do
args={data:match(v[1])}
if #args~=0 then
v[2]:Fire(self,data,cid,ip,port,unpack(args))
break
end
end
end
end,"settings")
function s:regSetting(namespace,settings)
if not net.settings.config[namespace] then
net.settings.config[namespace]={}
end
local connection=multi:newConnection()
table.insert(net.settings.config[namespace],{"!settings! "..namespace.." "..settings,connection})
return connection
end
end)
--Client Stuff
net.OnClientCreated:connect(function(c)
c.OnDataRecieved:(function(self,data) -- when the client recieves data this method is triggered
--First Lets make sure we are getting Setting data
end,"setings")
function sendSetting(namespace,args)
self:send("!settings! "..namespace.." "..args)
end
end)
end
if net.autoInit then
net.settings:init()
end

View File

@ -0,0 +1,179 @@
require("net")
--General Stuff
--[[ What this module does!
Adds
]]
function io.fileExists(path)
g=io.open(path or '','r')
if path =='' then
p='empty path'
return nil
end
if g~=nil and true or false then
p=(g~=nil and true or false)
end
if g~=nil then
io.close(g)
else
return false
end
return p
end
net:registerModule("sft",{1,0,0})
function net.sft:init() -- calling this initilizes the library and binds it to the servers and clients created
--Server Stuff
net.OnServerCreated:connect(function(s)
print("The sft(Simple File Transfer) Module has been loaded onto the server!")
if s.Type~="tcp" then
print("It is recomended that you use tcp to transfer files!")
end
s.transfers={}
s.OnUploadRequest=multi:newConnection() -- create a sft event
s.OnFileUploaded=multi:newConnection() -- create a sft event
s.OnDownloadRequest=multi:newConnection()
s.OnDataRecieved(function(self,data,cid,ip,port) -- when the server recieves data this method is triggered
--First Lets make sure we are getting sft data
--filename,dat=data:match("!sft! (%S-) (%S+)")
local cmd,arg1,arg2=data:match("!sft! (%S-) (%S-) (.+)")
if cmd=="tstart" then
local rets=self.OnUploadRequest:Fire(self,cid,ip,port)
for i=1,#rets do
if rets[i][1]==false then
print("Server refused to accept upload request!")
self:send(ip,"!sft! CANTUPLOAD NIL NIL",port)
return
end
end
local ID,streamable=arg1:match("(.+)|(.+)")
local file,hash=arg2:match("(.+)|(.+)")
if streamable~="NIL" then
self.transfers[ID]={bin.stream(streamable,false),hash,file}
else
self.transfers[ID]={bin.new(""),hash,file}
end
return
elseif cmd=="transfer" then
if self.transfers[arg1]~=nil then
self.transfers[arg1][1]:tackE(bin.fromhex(arg2))
--print(self.transfers[arg1][1]:getSize())
end
return
elseif cmd=="tend" then
if self.transfers[arg1]~=nil then
if self.transfers[arg1][1]:getHash(32)==self.transfers[arg1][2] then
self.OnFileUploaded:Fire(self,self.transfers[arg1][1],self.transfers[arg1][3],"Hash Good!")
else
print("Hash Error!")
self.OnFileUploaded:Fire(self,self.transfers[arg1][1],self.transfers[arg1][3],"Hash Bad!")
end
self.transfers[arg1]=nil
end
return
end
local filename=cmd
local dat=arg1
if filename==nil then return end
local rets=self.OnDownloadRequest:Fire(self,cid,ip,port)
for i=1,#rets do
if rets[i][1]==false then
print("Server refused to accept download request!")
self:send(ip,"!sft! CANTREQUEST NIL NIL",port)
return
end
end
if io.fileExists(filename) then
--Lets first load the file
local file=bin.stream(filename,false)
local size=file:getSize()
local pieceSize=512
local pieces=math.ceil(size/pieceSize)
local step=multi:newStep(1,pieces)
step.TransferID=tostring(math.random(1000,9999))
step.sender=self
step.ip=ip
step.port=port
step.pieceSize=pieceSize
step:OnStart(function(self)
self.sender:send(self.ip,"!sft! TSTART "..self.TransferID.."|"..dat.." "..filename.."|"..file:getHash(32),self.port)
end)
step:OnStep(function(pos,self)
self:hold(.01)
self.sender:send(self.ip,"!sft! TRANSFER "..self.TransferID.." "..bin.tohex(file:sub(((self.pieceSize*pos)+1)-self.pieceSize,self.pieceSize*pos)),self.port)
end)
step:OnEnd(function(self)
self.sender:send(self.ip,"!sft! TEND "..self.TransferID.." NIL",self.port)
end)
else
self:send(ip,"!sft! CANTREQUEST NIL NIL",port)
end
end,"sft")
end)
--Client Stuff
net.OnClientCreated:connect(function(c)
c.transfers={}
c.OnTransferStarted=multi:newConnection() -- create a sft event
c.OnTransferFinished=multi:newConnection() -- create a sft event
c.OnFileRequestFailed=multi:newConnection() -- create a sft event
c.OnFileUploadFailed=multi:newConnection() -- create a sft event
c.OnDataRecieved(function(self,data) -- when the client recieves data this method is triggered
--First Lets make sure we are getting sft data
local cmd,arg1,arg2=data:match("!sft! (%S-) (%S-) (.+)")
if cmd=="TSTART" then
local ID,streamable=arg1:match("(.+)|(.+)")
local file,hash=arg2:match("(.+)|(.+)")
if streamable~="NIL" then
self.transfers[ID]={bin.stream(streamable,false),hash,file}
else
self.transfers[ID]={bin.new(""),hash,file}
end
self.OnTransferStarted:Fire(self)
elseif cmd=="TRANSFER" then
self.transfers[arg1][1]:tackE(bin.fromhex(arg2))
elseif cmd=="TEND" then
if self.transfers[arg1][1]:getHash(32)==self.transfers[arg1][2] then
self.OnTransferFinished:Fire(self,self.transfers[arg1][1],self.transfers[arg1][3],"Hash Good!")
else
print("Hash Error!")
self.OnTransferFinished:Fire(self,self.transfers[arg1][1],self.transfers[arg1][3],"Hash Bad!")
end
self.transfers[arg1]=nil
elseif cmd=="CANTREQUEST" then
self.OnFileRequestFailed:Fire(self,"Could not request the file for some reason!")
elseif cmd=="CANTUPLOAD" then
self.OnFileUploadFailed:Fire(self,"Could not upload the file for some reason!")
end
end,"sft")
function c:uploadFile(filename)
if io.fileExists(filename) then
local file=bin.stream(filename,false)
local size=file:getSize()
local pieceSize=512
local pieces=math.ceil(size/pieceSize)
local step=multi:newStep(1,pieces)
step.TransferID=tostring(math.random(1000,9999))
step.sender=self
step.pieceSize=pieceSize
step:OnStart(function(self)
self.sender:send("!sft! tstart "..self.TransferID.."|NIL "..filename.."|"..file:getHash(32))
end)
step:OnStep(function(pos,self)
self:hold(.01)
self.sender:send("!sft! transfer "..self.TransferID.." "..bin.tohex(file:sub(((self.pieceSize*pos)+1)-self.pieceSize,self.pieceSize*pos)))
end)
step:OnEnd(function(self)
print("Request done!")
self.sender:send("!sft! tend "..self.TransferID.." NIL")
end)
else
self.OnFileUploadFailed:Fire(self,filename,"File does not exist!")
end
end
function c:requestFile(filename)
self:send("!sft! "..filename.." NIL NIL NIL")
end
end)
end
if net.autoInit then
net.sft.init()
end

View File

@ -0,0 +1,44 @@
print([[
the 'admin' module STATUS: 0
the 'aft' module STATUS: 9
the 'chatting' module STATUS: 5
the 'db' module STATUS: 1
the 'email' module STATUS: 2
the 'identity' module STATUS: 7
the 'inbox' module STATUS: 1
the 'init' module STATUS: 9 Not a module, but the core of the program
the 'logging' module STATUS: 8
the 'p2p' module STATUS: 1
the 'settings' module STATUS: 3
the 'sft' module STATUS: 10
the 'status' module STATUS: 5
the 'threading' module STATUS: 1
the 'users' module STATUS: 1
the 'version' module STATUS: 2
STATUS:
0: The idea is there but no progress to actual coding has been made
1: The idea has been coded and thoughts well defined. However only a frame is existing atm
2: The module has limited functionality, but gets its basic function through
3: The module has decent functionality, but needs much more
4: The module functions are stable and works great, however not all of the original idea has been completed
5: The module functions are stable and works great, most of the functions have been fleshed out aside for a few minor ones
6: The module has all features completed and is stable for average use, minor bugs are present.
7: The module has few to no bugs, and is currently being made more secure
8: The module is very secure and odds are it wont crash unless they were implemented wrong
9: The module has most if not all exception handled so even incorrect implementation will be 'error free' Probably wont change, but if it does it will be a huge change!
10: The module is never going to change from its current status! It is done.
NOTE: once a module reaches a status of 4, it will always be backward compatable. server-client relations will be safe!
Each module has a version
1.0.0
A.M.m
addition.Major.minor
whenever a new addition is added the A is increased... You must update to make use of that feature
When a major addition or change Major is incresed. This can be code breaking... However I will try my best to make at least client side code compatible with future changes
when a minor addition or change is made minor in incresed
Why client side stabality in code changes?
Until the update module can seamless update your modules I will ensure that client code can work regardless of server version starting from this version
]])

View File

@ -0,0 +1,41 @@
--[=[ About This Module!
This module is server side only! (Might add client side if requested)
Aim is to make each lane (thread) have no more than 'n' number of connections
This module hyjacks the multi:newConnection() function to seemlessly add support for threads without you having to change much
As long as each server-client connection is isolated you should be fine
The chatting module however IS NOT an isolated module, so take a look at how data was handled in that module to allow for both
threaded and non threaded use
How?
When this module is loaded all server creation is altered by passing a proxyServer instead of an actual server object
for example:
proxy=net:newTCPServer(12345)
proxy:OnDataRecieved(function(self,data,cid,ip,port)
self:send("My data!")
end)
the real server instance could be on any of the threads. Careful! While using this is seemless becareful of IO opperations!
]=]
--~ net:registerModule("threading",{1,0,0})
--~ if not(lanes) then error("Require the lanes module!") end
--~ local serverlinda = lanes.linda()
--~ net.threading.newServer=net.newServer -- store the original method
--~ net.threading.newTCPServer=net.newTCPServer -- store the original method
--~ net.threading.proxy={} -- namespace for the proxy stuff. Because of the desgin intention of both UDP/TCP servers Only one proxy is needed
print({})
--~ lanes=require("lanes")
--~ serverlinda = lanes.linda()
--~ mt={
--~ __index=function(t,k) print("IND",t,k) end,
--~ __newindex=function(t,k,v) print("NewIND",t,k,v) end,
--~ }
--~ test={}
--~ setmetatable(test,mt)
--~ test.a="hi"
--~ test.a=true
--~ g=test['a']
--~ print(test.b)
--~ setmetatable(net.threading.proxy,mt) -- set the proxies metatable, to prevent bleeding only create one server.

View File

@ -0,0 +1,37 @@
--[=[ About This Module!
This module is server side only! (Might add client side if requested)
Aim is to make each lane (thread) have no more than 'n' number of connections
This module hyjacks the multi:newConnection() function to seemlessly add support for threads without you having to change much
As long as each server-client connection is isolated you should be fine
The chatting module however IS NOT an isolated module, so take a look at how data was handled in that module to allow for both
threaded and non threaded use
How?
When this module is loaded all server creation is altered by passing a proxyServer instead of an actual server object
for example:
proxy=net:newTCPServer(12345)
proxy:OnDataRecieved(function(self,data,cid,ip,port)
self:send("My data!")
end)
the real server instance could be on any of the threads. Careful! While using this is seemless becareful of IO opperations!
]=]
--~ net:registerModule("threading",{1,0,0})
--~ if not(lanes) then error("Require the lanes module!") end
--~ local serverlinda = lanes.linda()
--~ net.threading.newServer=net.newServer -- store the original method
--~ net.threading.newTCPServer=net.newTCPServer -- store the original method
--~ net.threading.proxy={} -- namespace for the proxy stuff. Because of the desgin intention of both UDP/TCP servers Only one proxy is needed
lanes=require("lanes")
serverlinda = lanes.linda()
mt={
__index=function(t,k) print("IND",t,k) end,
__newindex=function(t,k,v) print("NewIND",t,k,v) end,
}
test={}
setmetatable(test,mt)
test.a="hi"
test.a=true
g=test['a']
print(test.b)
--~ setmetatable(net.threading.proxy,mt) -- set the proxies metatable, to prevent bleeding only create one server.

View File

@ -0,0 +1,37 @@
require("net.identity") --[[ This module extends the functions of the identity module
It aims to make the handling of users who are not online more seamless.
Without this handling offline users is a pain.
]]
net:registerModule("users",{1,0,0})
net.users.online={} -- all online users and offline users
net.users.offline={} -- all online users and offline users
net.OnServerCreated:connect(function(s)
s.OnUserLoggedIn(function(user,cid,ip,port,dTable)
--
end)
s.OnDataRecieved(function(self,data,CID_OR_HANDLE,IP_OR_HANDLE,PORT_OR_IP)
if self:userLoggedIn(cid) then -- If the user is logged in we do the tests
--
else
return
end
end,"users")
end)
net.OnClientCreated:connect(function(c)
c.OnUserList=multi:newConnection()
c.OnDataRecieved(function(self,data)
--
end,"users")
function c:searchUsers(nickname) -- sends a query to the server, returns list of userids and nicks close to the query
--
end
function c:addFriend(USERID)
--
end
function c:removeFriend(USERID)
--
end
function c:getFriends(USERID)
--
end
end)

View File

@ -0,0 +1,43 @@
require("net")
require("net.aft")
net:registerModule("version",{1,0,0}) -- allows communication of versions for modules
net.version.HOSTS={
["Lua"]=1,
["LuaJIT"]=1,
["Love2d"]=2, -- Yes love2d uses luaJIT, but the filesystem works a bit differently
["Corona"]=3,
}
net.version.OS={
["Windows"]=1,
["Unix"]=2,
["RPI"]=3,
}
--net.version.EOL="\60\69\110\100\45\79\102\45\70\105\108\101\45\84\114\97\110\115\102\101\114\62"
net.OnServerCreated:connect(function(s)
s.OnDataRecieved(function(self,data,CID_OR_HANDLE,IP_OR_HANDLE,PORT_OR_IP)
local cmd,arg1,arg2=data:match("!version! ")
end,"version")
s.OnClientConnected(function(self,CID_OR_HANDLE,IP_OR_HANDLE,PORT_OR_IP)
multi:newFunction(function(func) -- anom func, allows for fancy multitasking
multi:newFunction(function(self)
local range=self:newRange()
for i in range(1,#self.loadedModules) do
local mod=self.loadedModules[i]
self:send(IP_OR_HANDLE,"!version! CHECK "..mod.." NIL",PORT_OR_IP) -- sends command to client to return the version of the module
end
end)()
func=nil -- we dont want 1000s+ of these anom functions lying around
end)()-- lets call the function
self:send(IP_OR_HANDLE,"!version! HOST NIL NIL",PORT_OR_IP)
end)
end)
net.OnClientCreated:connect(function(c)
c.OnDataRecieved(function(self,data)
local cmd,module,arg1=data:match("!version! (%S+) (%S+) (%S+)")
if cmd=="CHECK" then
self:send("!version! VER "..self.loadedModules[module].." "..net.getModuleVersion(module))
elseif cmd=="UPDATE" then
--
end
end,"version")
end)

View File

@ -0,0 +1,37 @@
AICM={}
AICM.functions={
getAICMVersion=function(self)
return "1.0.0"
end,
}
function AICM:InitSyntax(obj,name)
obj:debug("Now using the Artificial Intelligence Communication module!")
obj.OnExtendedBlock(self.blockModule)
obj.OnCustomSyntax(self.syntaxModule)
obj:define(self.functions)
end
AICM.syntaxModule=function(self,line)
pVars,mStr=line:match("p%((.-)%)(.+)")
if pVars then
local vRef,vars=pVars:match("(.-):(.+)")
if vars:find(",") then
vars={unpack(vars:split(","))}
else
vars={vars}
end
tab={self:varExists(vRef):match(mStr)} -- self:varExists allows for all internal structures to just work
for i=1,#tab do
if vars[i] then
self._variables[vars[i]]=tab[i]
end
end
self:p() -- requried to progress the script
return {
text=line,
Type="AICMModule"
}
end
end
AICM.blockModule=function(obj,name,t,chunk,filename)
--
end

View File

@ -0,0 +1,71 @@
EBIM={}
EBIM.functions={
getEBIMVersion=function(self)
return "1.0.0"
end,
}
EBIM.registry={}
function EBIM:registerEBlock(name,func)
self.registry[name]=func
end
function EBIM:InitSyntax(obj,name)
obj:debug("Now using the Extended Block Interface module!")
obj.OnExtendedBlock(self.blockModule)
obj.OnCustomSyntax(self.syntaxModule)
obj:define(self.functions)
end
EBIM.syntaxModule=function(self,line)
local cmd,args=line:match("(.-) (.+):")
if cmd then
local goal=nil
local _tab={}
for i=self.pos+1,#self._cblock do
if self._cblock[i]=="end"..cmd then
goal=i
break
else
table.insert(_tab,self._cblock[i])
end
end
if goal==nil then
self:pushError("'end"..cmd.."' Expected to close '"..cmd.."'")
end
if EBIM.registry[cmd] then
EBIM.registry[cmd](self,args,_tab)
self.pos=goal+1
else
self:pushError("Unknown command: "..cmd)
end
return {
Type="EBIM-Data",
text=cmd.." Block"
}
else
return
end
end
EBIM.blockModule=function(obj,name,t,chunk,filename)
--print(">: ",obj,name,t,chunk,filename)
end
EBIM:registerEBlock("string",function(self,args,tab)
local str={}
for i=1,#tab do
table.insert(str,tab[i])
end
self:setVariable(args,table.concat(str,"\n"))
end)
EBIM:registerEBlock("list",function(self,args,tab)
local str={}
for i=1,#tab do
table.insert(str,self:varExists(tab[i]))
end
self:setVariable(args,str)
end)
EBIM:registerEBlock("dict",function(self,args,tab)
local str={}
for i=1,#tab do
local a,b=tab[i]:match("(.-):%s*(.+)")
str[a]=self:varExists(b)
end
self:setVariable(args,str)
end)

View File

@ -0,0 +1,44 @@
-- In an attempt to speed up my library I will use a virtual machine that runs bytecode
compiler={}
compiler.cmds={ -- list of all of the commands
EVAL="\01", -- evaluate
SPLT="\02", -- split
TRIM="\03", -- trim
VEXT="\04", -- variable exists
ILST="\05", -- is a list
LSTR="\06", -- load string
FCAL="\07", -- Function call
SVAR="\08", -- set variable
LOAD="\09", -- load file
LAOD="\10", -- _load file
DEFN="\11", -- define external functions
HCBK="\12", -- Has c Block
CMBT="\13", -- combine truths
SETB="\14", -- set block
STRT="\15", -- start
PERR="\16", -- push error
PROG="\17", -- progress
PHED="\18", -- parse header
SSLT="\19", -- split string
NEXT="\20", -- next
-- Needs refining... One step at a time right!
}
function compiler:compile(filename) -- compiles the code into bytecode
-- First we load the code but don't run it
local engine=parseManager:load(filename)
-- This captures all of the methods and important info. This also ensures that the compiler and interperter stay in sync!
local bytecodeheader=bin.new() -- header will contain the order of blocks and important flags
local bytecode=bin.newDataBuffer() -- lets leave it at unlimited size because we don't know how long it will need to be
local functions={} -- will be populated with the important methods that must be preloaded
local prebytecode={} -- this contains bytecode that has yet to be sterilized
for blockname,blockdata in pairs(engine._chunks) do
-- lets get some variables ready
local code,_type,nextblock,filename=blockdata[1],blockdata[2],blockdata.next,blockdata.file
-- note nextblock may be nil on 2 condidions. The first is when the leaking flag is disabled and the other is when the block in question was the last block defined
local lines=bin._lines(code)
print("\n["..blockname.."]\n")
for i=1,#lines do
print(lines[i])
end
end
end

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,10 @@
engine={}
function engine:init(bytecodeFile)
self.code=bin.load(bytecodeFile).data
end
--[[OP-CODES
]]
function engine:run(assessors)
--
end

BIN
LoveKaraoke/core/test.dat Normal file

Binary file not shown.

18
LoveKaraoke/core/test.lua Normal file
View File

@ -0,0 +1,18 @@
require("Library")
local clock = os.clock
function sleep(n) -- seconds
local t0 = clock()
while clock() - t0 <= n do end
end
function tester(test)
sleep(1)
return test*10
end
--~ require("bin")
--~ test=bin.namedBlockManager()
--~ test["name"]="Ryan"
--~ test["age"]=21
--~ test:tofile("test.dat")
--~ test2=bin.namedBlockManager("test.dat")
--~ print(test2["name"])
--~ print(test2["age"])

View File

@ -0,0 +1,223 @@
--[[
love-microphone
Device.lua
Holds methods for the Device class to be used by the love-microphone core.
]]
local ffi = require("ffi")
local al = require("love-microphone.openal")
local Device = {}
local formats = {
[8] = al.AL_FORMAT_MONO8,
[16] = al.AL_FORMAT_MONO16
}
--[[
Device Device:new(string? deviceName, [int frequency, float sampleLength, uint format])
deviceName: The device to open. Specify nil to get the default device.
frequency: The sample rate in Hz to open the source at; defaults to 22050 Hz.
sampleLength: How long in seconds a sample should be; defaults to 0.5 s. Directly affects latency.
format: How many bits per sample; defaults to 16.
Creates a new Device object corresponding to the given microphone.
Will not check for duplicate handles on the same device, have care.
]]
function Device:new(name, frequency, sampleLength, format)
if (name ~= nil and type(name) ~= "string") then
return nil, "Invalid argument #1: Device name must be of type 'string' if given."
end
if (frequency ~= nil and (type(frequency) ~= "number" or frequency % 1 ~= 0)) then
return nil, "Invalid argument #2: Frequency must of type 'number' and an integer if given."
end
if (sampleLength ~= nil and type(sampleLength) ~= "number") then
return nil, "Invalid argument #3: Sample length must be of type 'number' if given."
end
if (format ~= nil and (type(format) ~= "number" or format % 1 ~= 0)) then
return nil, "Invalid argument #4: Format must be of type 'number' and an integer if given."
end
frequency = frequency or 22050
sampleLength = sampleLength or 0.5
format = format or 16
local fastAsPossible = false
if (sampleLength == 0) then
sampleLength = 0.1
fastAsPossible = true
end
local alFormat = formats[format]
if (not alFormat) then
return nil, "Invalid argument #4: Format must be a valid OpenAL bit depth (8 or 16) if given."
end
-- Convert sampleLength to be in terms of audio samples
sampleSize = math.floor(frequency * sampleLength)
local alcdevice = al.alcCaptureOpenDevice(name, frequency, alFormat, sampleSize * 2)
-- Create our actual microphone device object
local internal = {}
for key, value in pairs(Device) do
if (key ~= "new") then
internal[key] = value
end
end
-- Set some private fields
internal._sampleSize = sampleSize
-- Samples should be read as quickly as possible!
if (fastAsPossible) then
internal._fastAsPossible = true
internal._sampleSize = nil
else
-- We can only use an internal buffer if we have fixed buffer sizing.
internal._buffer = love.sound.newSoundData(sampleSize, frequency, format, 1)
end
internal._alcdevice = alcdevice
internal._format = format
internal._alformat = alFormat
internal._name = name
internal._valid = true
internal._samplesIn = ffi.new("ALCint[1]")
internal._frequency = frequency
internal._dataCallback = nil
-- Wrap everything in a convenient userdata
local wrap = newproxy(true)
local meta = getmetatable(wrap)
meta.__index = internal
meta.__newindex = internal
meta.__gc = internal.close
return wrap
end
--[[
void Device:setDataCallback(void callback(Device device, SoundData data)?)
callback: The function to receive the data
Sets the function that this microphone will call when it receives a buffer full of data.
Send no arguments to remove the current callback.
By default, tries to call love.microphonedata.
]]
function Device:setDataCallback(callback)
if (callback and type(callback) ~= "function") then
return nil, "Invalid argument #1: Callback must be of type 'function' if given."
end
self._dataCallback = callback
end
--[[
bool Device:start()
Starts recording audio with this microphone.
Returns true if successful.
]]
function Device:start()
if (not self._valid) then
return false, "Device is closed."
end
al.alcCaptureStart(self._alcdevice)
return true
end
--[[
bool Device:stop()
Stops recording audio with this microphone.
Returns true if successful.
]]
function Device:stop()
if (not self._valid) then
return false, "Device is closed."
end
al.alcCaptureStop(self._alcdevice)
return true
end
--[[
bool Device:close()
Closes the microphone object and stops it from being used.
Returns true if successful.
]]
function Device:close()
if (not self._valid) then
return false, "Device already closed."
end
al.alcCaptureStop(self._alcdevice)
al.alcCaptureCloseDevice(self._alcdevice)
self._valid = false
microphone._devices[self._name] = nil
return true
end
--[[
void Device:poll()
Polls the microphone for data, updates the buffer, and calls any registered callbacks if there is data.
]]
function Device:poll()
al.alcGetIntegerv(self._alcdevice, al.ALC_CAPTURE_SAMPLES, 1, self._samplesIn)
-- fastAsPossible requires variable buffer sizing; we can't reuse the internal buffer.
local samplesIn = self._samplesIn[0]
if (self._fastAsPossible) then
if (samplesIn == 0) then
return
end
local samples = samplesIn
local buffer = love.sound.newSoundData(samples, self._frequency, self._format, 1)
al.alcCaptureSamples(self._alcdevice, buffer:getPointer(), samples)
if (self._dataCallback) then
self:_dataCallback(buffer)
elseif (love.microphonedata) then
love.microphonedata(self, buffer)
end
elseif (samplesIn >= self._sampleSize) then
local samples = self._sampleSize
local buffer
local buffer = self._buffer
al.alcCaptureSamples(self._alcdevice, buffer:getPointer(), samples)
if (self._dataCallback) then
self:_dataCallback(buffer)
elseif (love.microphonedata) then
love.microphonedata(self, buffer)
end
end
end
--[[
uint Device:getBitDepth()
Returns the bit depth of the microphone.
]]
function Device:getBitDepth()
return self._format
end
return Device

View File

@ -0,0 +1,237 @@
--[[
love-microphone
QueueableSource.lua
Provides a QueueableSource object, pseudo-inheriting from Source.
See http://love2d.org/wiki/Source for better documentation on
most methods except queue and new.
]]
local ffi = require("ffi")
local al = require("love-microphone.openal")
local QueueableSource = {}
local typecheck = {
Object = true,
QueueableSource = true
}
--[[
alFormat getALFormat(SoundData data)
data: The SoundData to query.
Returns the correct alFormat enum value for the given SoundData.
]]
local function getALFormat(data)
local stereo = data:getChannels() == 2
local deep = data:getBitDepth() == 16
if (stereo) then
if (deep) then
return al.AL_FORMAT_STEREO16
else
return al.AL_FORMAT_STEREO8
end
end
if (deep) then
return al.AL_FORMAT_MONO16
else
return al.AL_FORMAT_MONO8
end
end
--[[
QueueableSource QueueableSource:new(uint bufferCount=16)
bufferCount: The number of buffers to use to hold queued sounds.
Creates a new QueueableSource object.
]]
function QueueableSource:new(bufferCount)
if (bufferCount) then
if (type(bufferCount) ~= "number" or bufferCount % 1 ~= 0 or bufferCount < 0) then
return nil, "Invalid argument #1: bufferCount must be a positive integer if given."
end
else
bufferCount = 16
end
local new = {}
for key, value in pairs(self) do
if (key ~= "new") then
new[key] = value
end
end
local pBuffers = ffi.new("ALuint[?]", bufferCount)
al.alGenBuffers(bufferCount, pBuffers)
local freeBuffers = {}
for i = 0, bufferCount - 1 do
table.insert(freeBuffers, pBuffers[i])
end
local pSource = ffi.new("ALuint[1]")
al.alGenSources(1, pSource)
al.alSourcei(pSource[0], al.AL_LOOPING, 0)
new._bufferCount = bufferCount
new._pBuffers = pBuffers
new._freeBuffers = freeBuffers
new._source = pSource[0]
new._pAvailable = ffi.new("ALint[1]")
new._pBufferHolder = ffi.new("ALuint[16]")
local wrapper = newproxy(true)
getmetatable(wrapper).__index = new
getmetatable(wrapper).__gc = function(self)
al.alSourceStop(new._source)
al.alSourcei(new._source, al.AL_BUFFER, 0)
al.alDeleteSources(1, pSource)
al.alDeleteBuffers(bufferCount, pBuffers)
end
return wrapper
end
--[[
string QueueableSource:type()
Returns the string name of the class, "QueueableSource".
]]
function QueueableSource:type()
return "QueueableSource"
end
--[[
bool QueueableSource:typeOf(string type)
type: The type to check against.
Returns whether the object matches the given type.
]]
function QueueableSource:typeOf(type)
return typecheck[type]
end
--[[
void QueueableSource:queue(SoundData data) (Success)
(void, string) QueueableSource:queue(SoundData data) (Failure)
data: The SoundData to queue for playback.
Queues a new SoundData to play.
Will fail and return nil and an error message if no buffers were available.
]]
function QueueableSource:queue(data)
self:step()
if (#self._freeBuffers == 0) then
return nil, "No free buffers were available to playback the given audio."
end
local top = table.remove(self._freeBuffers, 1)
al.alBufferData(top, getALFormat(data), data:getPointer(), data:getSize(), data:getSampleRate())
al.alSourceQueueBuffers(self._source, 1, ffi.new("ALuint[1]", top))
end
--[[
void QueueableSource:step()
Opens up queues that have been used.
Called automatically by queue.
]]
function QueueableSource:step()
al.alGetSourcei(self._source, al.AL_BUFFERS_PROCESSED, self._pAvailable)
if (self._pAvailable[0] > 0) then
al.alSourceUnqueueBuffers(self._source, self._pAvailable[0], self._pBufferHolder)
for i = 0, self._pAvailable[0] - 1 do
table.insert(self._freeBuffers, self._pBufferHolder[i])
end
end
end
--[[
void QueueableSource:clear()
Stops playback and clears all queued data.
]]
function QueueableSource:clear()
self:pause()
for i = 0, self._bufferCount - 1 do
al.alSourceUnqueueBuffers(self._source, self._bufferCount, self._pBuffers)
table.insert(self._freeBuffers, self._pBuffers[i])
end
end
--[[
uint QueueableSource:getFreeBufferCount()
Returns the number of free buffers for queueing sounds with this QueueableSource.
]]
function QueueableSource:getFreeBufferCount()
return #self._freeBuffers
end
--[[
void QueueableSource:play()
Begins playing audio.
]]
function QueueableSource:play()
if (not self:isPlaying()) then
al.alSourcePlay(self._source)
end
end
--[[
bool QueueableSource:isPlaying()
Returns whether the source is playing audio.
]]
function QueueableSource:isPlaying()
local state = ffi.new("ALint[1]")
al.alGetSourcei(self._source, al.AL_SOURCE_STATE, state)
return (state[0] == al.AL_PLAYING)
end
--[[
void QueueableSource:pause()
Stops playing audio.
]]
function QueueableSource:pause()
if (not self:isPaused()) then
al.alSourcePause(self._source)
end
end
--[[
void QueueableSource:isPaused()
Returns whether the source is paused.
]]
function QueueableSource:isPaused()
local state = ffi.new("ALint[1]")
al.alGetSourcei(self._source, al.AL_SOURCE_STATE, state)
return (state[0] == al.AL_PAUSED)
end
--[[
void QueueableSource:SetVolume(number volume)
Sets the volume of the source.
]]
function QueueableSource:setVolume(volume)
al.alSourcef(self._source, al.AL_GAIN, volume)
end
return QueueableSource

View File

@ -0,0 +1,97 @@
--[[
love-microphone
init.lua
Main file for love-microphone, creates the microphone namespace.
]]
local ffi = require("ffi")
local al = require("love-microphone.openal")
local Device = require("love-microphone.Device")
local QueueableSource = require("love-microphone.QueueableSource")
local microphone = {
_devices = {}
}
--[[
(int major, int minor, int revision) microphone.getVersion()
Returns the version of love-microphone currently running.
]]
function microphone.getVersion()
return 0, 6, 0
end
--[[
void microphone.import()
Imports the module into love.microphone. Not always desired.
]]
function microphone.import()
love.microphone = microphone
end
--[[
QueueableSource microphone.newQueueableSource()
Creates a new QueueableSource object to play lists of SoundData.
]]
function microphone.newQueueableSource(bufferCount)
return QueueableSource:new(bufferCount)
end
--[[
Device microphone.openDevice(string? deviceName, [int frequency, float sampleLength, uint format])
deviceName: The device to open. Specify nil to get the default device.
frequency: The sample rate in Hz to open the source at; defaults to 22050 Hz.
sampleLength: How long in seconds a sample should be; defaults to 0.5 s. Directly affects latency.
format: Number of bits per audio sample; defaults to 16. Must be 8 or 16.
Open a new microphone device or returns an existing opened device
]]
function microphone.openDevice(name, frequency, sampleLength, format)
if (microphone._devices[name]) then
return microphone._devices[name]
end
local device, err = Device:new(name, frequency, sampleLength, format)
if (not device) then
return nil, err
end
microphone._devices[name or microphone.getDefaultDeviceName()] = device
return device
end
--[[
string[] microphone.getDeviceList()
Returns a list of microphones on the system.
]]
function microphone.getDeviceList()
local pDeviceList = al.alcGetString(nil, al.ALC_CAPTURE_DEVICE_SPECIFIER)
local list = {}
while (pDeviceList[0] ~= 0) do
local str = ffi.string(pDeviceList)
pDeviceList = pDeviceList + #str + 1
table.insert(list, str)
end
return list
end
--[[
string microphone.getDefaultDeviceName()
Returns the name of the default microphone.
]]
function microphone.getDefaultDeviceName()
return ffi.string(al.alcGetString(nil, al.ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER))
end
return microphone

View File

@ -0,0 +1,381 @@
--[[
love-microphone
openal.lua
LuaJIT FFI binding for OpenAL-soft
]]
local ffi = require("ffi")
-- Load OpenAL32.dll on Windows (from LOVE) or use ffi.C
local openal = (ffi.os == "Windows") and ffi.load("openal32") or ffi.C
--alc.h
ffi.cdef([[
enum {
ALC_INVALID = 0, //Deprecated
ALC_VERSION_0_1 = 1,
ALC_FALSE = 0,
ALC_TRUE = 1,
ALC_FREQUENCY = 0x1007,
ALC_REFRESH = 0x1008,
ALC_SYNC = 0x1009,
ALC_MONO_SOURCES = 0x1010,
ALC_STEREO_SOURCES = 0x1011,
ALC_NO_ERROR = 0,
ALC_INVALID_DEVICE = 0xA001,
ALC_INVALID_CONTEXT = 0xA002,
ALC_INVALID_ENUM = 0xA003,
ALC_INVALID_VALUE = 0xA004,
ALC_OUT_OF_MEMORY = 0xA005,
ALC_MAJOR_VERSION = 0x1000,
ALC_MINOR_VERSION = 0x1001,
ALC_ATTRIBUTES_SIZE = 0x1002,
ALC_ALL_ATTRIBUTES = 0x1003,
ALC_DEFAULT_DEVICE_SPECIFIER = 0x1004,
ALC_DEVICE_SPECIFIER = 0x1005,
ALC_EXTENSIONS = 0x1006,
ALC_EXT_CAPTURE = 1,
ALC_CAPTURE_DEVICE_SPECIFIER = 0x310,
ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER = 0x311,
ALC_CAPTURE_SAMPLES = 0x312,
ALC_DEFAULT_ALL_DEVICES_SPECIFIER = 0x1012,
ALC_ALL_DEVICES_SPECIFIER = 0x1013
};
typedef struct ALCdevice_struct ALCdevice;
typedef struct ALCcontext_struct ALCcontext;
typedef char ALCboolean;
typedef char ALCchar;
typedef signed char ALCbyte;
typedef unsigned char ALCubyte;
typedef short ALCshort;
typedef unsigned short ALCushort;
typedef int ALCint;
typedef unsigned int ALCuint;
typedef int ALCsizei;
typedef int ALCenum;
typedef float ALCfloat;
typedef double ALCdouble;
typedef void ALCvoid;
ALCcontext* alcCreateContext(ALCdevice *device, const ALCint* attrlist);
ALCboolean alcMakeContextCurrent(ALCcontext *context);
void alcProcessContext(ALCcontext *context);
void alcSuspendContext(ALCcontext *context);
void alcDestroyContext(ALCcontext *context);
ALCcontext* alcGetCurrentContext(void);
ALCdevice* alcGetContextsDevice(ALCcontext *context);
ALCdevice* alcOpenDevice(const ALCchar *devicename);
ALCboolean alcCloseDevice(ALCdevice *device);
ALCenum alcGetError(ALCdevice *device);
ALCboolean alcIsExtensionPresent(ALCdevice *device, const ALCchar *extname);
void* alcGetProcAddress(ALCdevice *device, const ALCchar *funcname);
ALCenum alcGetEnumValue(ALCdevice *device, const ALCchar *enumname);
const ALCchar* alcGetString(ALCdevice *device, ALCenum param);
void alcGetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values);
ALCdevice* alcCaptureOpenDevice(const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize);
ALCboolean alcCaptureCloseDevice(ALCdevice *device);
void alcCaptureStart(ALCdevice *device);
void alcCaptureStop(ALCdevice *device);
void alcCaptureSamples(ALCdevice *device, ALCvoid *buffer, ALCsizei samples);
typedef ALCcontext* (*LPALCCREATECONTEXT)(ALCdevice *device, const ALCint *attrlist);
typedef ALCboolean (*LPALCMAKECONTEXTCURRENT)(ALCcontext *context);
typedef void (*LPALCPROCESSCONTEXT)(ALCcontext *context);
typedef void (*LPALCSUSPENDCONTEXT)(ALCcontext *context);
typedef void (*LPALCDESTROYCONTEXT)(ALCcontext *context);
typedef ALCcontext* (*LPALCGETCURRENTCONTEXT)(void);
typedef ALCdevice* (*LPALCGETCONTEXTSDEVICE)(ALCcontext *context);
typedef ALCdevice* (*LPALCOPENDEVICE)(const ALCchar *devicename);
typedef ALCboolean (*LPALCCLOSEDEVICE)(ALCdevice *device);
typedef ALCenum (*LPALCGETERROR)(ALCdevice *device);
typedef ALCboolean (*LPALCISEXTENSIONPRESENT)(ALCdevice *device, const ALCchar *extname);
typedef void* (*LPALCGETPROCADDRESS)(ALCdevice *device, const ALCchar *funcname);
typedef ALCenum (*LPALCGETENUMVALUE)(ALCdevice *device, const ALCchar *enumname);
typedef const ALCchar* (*LPALCGETSTRING)(ALCdevice *device, ALCenum param);
typedef void (*LPALCGETINTEGERV)(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values);
typedef ALCdevice* (*LPALCCAPTUREOPENDEVICE)(const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize);
typedef ALCboolean (*LPALCCAPTURECLOSEDEVICE)(ALCdevice *device);
typedef void (*LPALCCAPTURESTART)(ALCdevice *device);
typedef void (*LPALCCAPTURESTOP)(ALCdevice *device);
typedef void (*LPALCCAPTURESAMPLES)(ALCdevice *device, ALCvoid *buffer, ALCsizei samples);
]])
--al.h
ffi.cdef([[
enum {
AL_NONE = 0,
AL_FALSE = 0,
AL_TRUE = 1,
AL_SOURCE_RELATIVE = 0x202,
AL_CONE_INNER_ANGLE = 0x1001,
AL_CONE_OUTER_ANGLE = 0x1002,
AL_PITCH = 0x1003,
AL_POSITION = 0x1004,
AL_DIRECTION = 0x1005,
AL_VELOCITY = 0x1006,
AL_LOOPING = 0x1007,
AL_BUFFER = 0x1009,
AL_GAIN = 0x100A,
AL_MIN_GAIN = 0x100D,
AL_MAX_GAIN = 0x100E,
AL_ORIENTATION = 0x100F,
AL_SOURCE_STATE = 0x1010,
AL_INITIAL = 0x1011,
AL_PLAYING = 0x1012,
AL_PAUSED = 0x1013,
AL_STOPPED = 0x1014,
AL_BUFFERS_QUEUED = 0x1015,
AL_BUFFERS_PROCESSED = 0x1016,
AL_REFERENCE_DISTANCE = 0x1020,
AL_ROLLOFF_FACTOR = 0x1021,
AL_CONE_OUTER_GAIN = 0x1022,
AL_MAX_DISTANCE = 0x1023,
AL_SEC_OFFSET = 0x1024,
AL_SAMPLE_OFFSET = 0x1025,
AL_BYTE_OFFSET = 0x1026,
AL_SOURCE_TYPE = 0x1027,
AL_STATIC = 0x1028,
AL_STREAMING = 0x1029,
AL_UNDETERMINED = 0x1030,
AL_FORMAT_MONO8 = 0x1100,
AL_FORMAT_MONO16 = 0x1101,
AL_FORMAT_STEREO8 = 0x1102,
AL_FORMAT_STEREO16 = 0x1103,
AL_FREQUENCY = 0x2001,
AL_BITS = 0x2002,
AL_CHANNELS = 0x2003,
AL_SIZE = 0x2004,
AL_UNUSED = 0x2010,
AL_PENDING = 0x2011,
AL_PROCESSED = 0x2012,
AL_NO_ERROR = 0,
AL_INVALID_NAME = 0xA001,
AL_INVALID_ENUM = 0xA002,
AL_INVALID_VALUE = 0xA003,
AL_INVALID_OPERATION = 0xA004,
AL_OUT_OF_MEMORY = 0xA005,
AL_VENDOR = 0xB001,
AL_VERSION = 0xB002,
AL_RENDERER = 0xB003,
AL_EXTENSIONS = 0xB004,
AL_DOPPLER_FACTOR = 0xC000,
AL_DOPPLER_VELOCITY = 0xC001,
AL_SPEED_OF_SOUND = 0xC003,
AL_DISTANCE_MODEL = 0xD000,
AL_INVERSE_DISTANCE = 0xD001,
AL_INVERSE_DISTANCE_CLAMPED = 0xD002,
AL_LINEAR_DISTANCE = 0xD003,
AL_LINEAR_DISTANCE_CLAMPED = 0xD004,
AL_EXPONENT_DISTANCE = 0xD005,
AL_EXPONENT_DISTANCE_CLAMPED = 0xD006
};
typedef char ALboolean;
typedef char ALchar;
typedef signed char ALbyte;
typedef unsigned char ALubyte;
typedef short ALshort;
typedef unsigned short ALushort;
typedef int ALint;
typedef unsigned int ALuint;
typedef int ALsizei;
typedef int ALenum;
typedef float ALfloat;
typedef double ALdouble;
typedef void ALvoid;
void alDopplerFactor(ALfloat value);
void alDopplerVelocity(ALfloat value);
void alSpeedOfSound(ALfloat value);
void alDistanceModel(ALenum distanceModel);
void alEnable(ALenum capability);
void alDisable(ALenum capability);
ALboolean alIsEnabled(ALenum capability);
const ALchar* alGetString(ALenum param);
void alGetBooleanv(ALenum param, ALboolean *values);
void alGetIntegerv(ALenum param, ALint *values);
void alGetFloatv(ALenum param, ALfloat *values);
void alGetDoublev(ALenum param, ALdouble *values);
ALboolean alGetBoolean(ALenum param);
ALint alGetInteger(ALenum param);
ALfloat alGetFloat(ALenum param);
ALdouble alGetDouble(ALenum param);
ALenum alGetError(void);
ALboolean alIsExtensionPresent(const ALchar *extname);
void* alGetProcAddress(const ALchar *fname);
ALenum alGetEnumValue(const ALchar *ename);
void alListenerf(ALenum param, ALfloat value);
void alListener3f(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3);
void alListenerfv(ALenum param, const ALfloat *values);
void alListeneri(ALenum param, ALint value);
void alListener3i(ALenum param, ALint value1, ALint value2, ALint value3);
void alListeneriv(ALenum param, const ALint *values);
void alGetListenerf(ALenum param, ALfloat *value);
void alGetListener3f(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3);
void alGetListenerfv(ALenum param, ALfloat *values);
void alGetListeneri(ALenum param, ALint *value);
void alGetListener3i(ALenum param, ALint *value1, ALint *value2, ALint *value3);
void alGetListeneriv(ALenum param, ALint *values);
void alGenSources(ALsizei n, ALuint *sources);
void alDeleteSources(ALsizei n, const ALuint *sources);
ALboolean alIsSource(ALuint source);
void alSourcef(ALuint source, ALenum param, ALfloat value);
void alSource3f(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3);
void alSourcefv(ALuint source, ALenum param, const ALfloat *values);
void alSourcei(ALuint source, ALenum param, ALint value);
void alSource3i(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3);
void alSourceiv(ALuint source, ALenum param, const ALint *values);
void alGetSourcef(ALuint source, ALenum param, ALfloat *value);
void alGetSource3f(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3);
void alGetSourcefv(ALuint source, ALenum param, ALfloat *values);
void alGetSourcei(ALuint source, ALenum param, ALint *value);
void alGetSource3i(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3);
void alGetSourceiv(ALuint source, ALenum param, ALint *values);
void alSourcePlayv(ALsizei n, const ALuint *sources);
void alSourceStopv(ALsizei n, const ALuint *sources);
void alSourceRewindv(ALsizei n, const ALuint *sources);
void alSourcePausev(ALsizei n, const ALuint *sources);
void alSourcePlay(ALuint source);
void alSourceStop(ALuint source);
void alSourceRewind(ALuint source);
void alSourcePause(ALuint source);
void alSourceQueueBuffers(ALuint source, ALsizei nb, const ALuint *buffers);
void alSourceUnqueueBuffers(ALuint source, ALsizei nb, ALuint *buffers);
void alGenBuffers(ALsizei n, ALuint *buffers);
void alDeleteBuffers(ALsizei n, const ALuint *buffers);
ALboolean alIsBuffer(ALuint buffer);
void alBufferData(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq);
void alBufferf(ALuint buffer, ALenum param, ALfloat value);
void alBuffer3f(ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3);
void alBufferfv(ALuint buffer, ALenum param, const ALfloat *values);
void alBufferi(ALuint buffer, ALenum param, ALint value);
void alBuffer3i(ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3);
void alBufferiv(ALuint buffer, ALenum param, const ALint *values);
void alGetBufferf(ALuint buffer, ALenum param, ALfloat *value);
void alGetBuffer3f(ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3);
void alGetBufferfv(ALuint buffer, ALenum param, ALfloat *values);
void alGetBufferi(ALuint buffer, ALenum param, ALint *value);
void alGetBuffer3i(ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3);
void alGetBufferiv(ALuint buffer, ALenum param, ALint *values);
typedef void (*LPALENABLE)(ALenum capability);
typedef void (*LPALDISABLE)(ALenum capability);
typedef ALboolean (*LPALISENABLED)(ALenum capability);
typedef const ALchar* (*LPALGETSTRING)(ALenum param);
typedef void (*LPALGETBOOLEANV)(ALenum param, ALboolean *values);
typedef void (*LPALGETINTEGERV)(ALenum param, ALint *values);
typedef void (*LPALGETFLOATV)(ALenum param, ALfloat *values);
typedef void (*LPALGETDOUBLEV)(ALenum param, ALdouble *values);
typedef ALboolean (*LPALGETBOOLEAN)(ALenum param);
typedef ALint (*LPALGETINTEGER)(ALenum param);
typedef ALfloat (*LPALGETFLOAT)(ALenum param);
typedef ALdouble (*LPALGETDOUBLE)(ALenum param);
typedef ALenum (*LPALGETERROR)(void);
typedef ALboolean (*LPALISEXTENSIONPRESENT)(const ALchar *extname);
typedef void* (*LPALGETPROCADDRESS)(const ALchar *fname);
typedef ALenum (*LPALGETENUMVALUE)(const ALchar *ename);
typedef void (*LPALLISTENERF)(ALenum param, ALfloat value);
typedef void (*LPALLISTENER3F)(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3);
typedef void (*LPALLISTENERFV)(ALenum param, const ALfloat *values);
typedef void (*LPALLISTENERI)(ALenum param, ALint value);
typedef void (*LPALLISTENER3I)(ALenum param, ALint value1, ALint value2, ALint value3);
typedef void (*LPALLISTENERIV)(ALenum param, const ALint *values);
typedef void (*LPALGETLISTENERF)(ALenum param, ALfloat *value);
typedef void (*LPALGETLISTENER3F)(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3);
typedef void (*LPALGETLISTENERFV)(ALenum param, ALfloat *values);
typedef void (*LPALGETLISTENERI)(ALenum param, ALint *value);
typedef void (*LPALGETLISTENER3I)(ALenum param, ALint *value1, ALint *value2, ALint *value3);
typedef void (*LPALGETLISTENERIV)(ALenum param, ALint *values);
typedef void (*LPALGENSOURCES)(ALsizei n, ALuint *sources);
typedef void (*LPALDELETESOURCES)(ALsizei n, const ALuint *sources);
typedef ALboolean (*LPALISSOURCE)(ALuint source);
typedef void (*LPALSOURCEF)(ALuint source, ALenum param, ALfloat value);
typedef void (*LPALSOURCE3F)(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3);
typedef void (*LPALSOURCEFV)(ALuint source, ALenum param, const ALfloat *values);
typedef void (*LPALSOURCEI)(ALuint source, ALenum param, ALint value);
typedef void (*LPALSOURCE3I)(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3);
typedef void (*LPALSOURCEIV)(ALuint source, ALenum param, const ALint *values);
typedef void (*LPALGETSOURCEF)(ALuint source, ALenum param, ALfloat *value);
typedef void (*LPALGETSOURCE3F)(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3);
typedef void (*LPALGETSOURCEFV)(ALuint source, ALenum param, ALfloat *values);
typedef void (*LPALGETSOURCEI)(ALuint source, ALenum param, ALint *value);
typedef void (*LPALGETSOURCE3I)(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3);
typedef void (*LPALGETSOURCEIV)(ALuint source, ALenum param, ALint *values);
typedef void (*LPALSOURCEPLAYV)(ALsizei n, const ALuint *sources);
typedef void (*LPALSOURCESTOPV)(ALsizei n, const ALuint *sources);
typedef void (*LPALSOURCEREWINDV)(ALsizei n, const ALuint *sources);
typedef void (*LPALSOURCEPAUSEV)(ALsizei n, const ALuint *sources);
typedef void (*LPALSOURCEPLAY)(ALuint source);
typedef void (*LPALSOURCESTOP)(ALuint source);
typedef void (*LPALSOURCEREWIND)(ALuint source);
typedef void (*LPALSOURCEPAUSE)(ALuint source);
typedef void (*LPALSOURCEQUEUEBUFFERS)(ALuint source, ALsizei nb, const ALuint *buffers);
typedef void (*LPALSOURCEUNQUEUEBUFFERS)(ALuint source, ALsizei nb, ALuint *buffers);
typedef void (*LPALGENBUFFERS)(ALsizei n, ALuint *buffers);
typedef void (*LPALDELETEBUFFERS)(ALsizei n, const ALuint *buffers);
typedef ALboolean (*LPALISBUFFER)(ALuint buffer);
typedef void (*LPALBUFFERDATA)(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq);
typedef void (*LPALBUFFERF)(ALuint buffer, ALenum param, ALfloat value);
typedef void (*LPALBUFFER3F)(ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3);
typedef void (*LPALBUFFERFV)(ALuint buffer, ALenum param, const ALfloat *values);
typedef void (*LPALBUFFERI)(ALuint buffer, ALenum param, ALint value);
typedef void (*LPALBUFFER3I)(ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3);
typedef void (*LPALBUFFERIV)(ALuint buffer, ALenum param, const ALint *values);
typedef void (*LPALGETBUFFERF)(ALuint buffer, ALenum param, ALfloat *value);
typedef void (*LPALGETBUFFER3F)(ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3);
typedef void (*LPALGETBUFFERFV)(ALuint buffer, ALenum param, ALfloat *values);
typedef void (*LPALGETBUFFERI)(ALuint buffer, ALenum param, ALint *value);
typedef void (*LPALGETBUFFER3I)(ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3);
typedef void (*LPALGETBUFFERIV)(ALuint buffer, ALenum param, ALint *values);
typedef void (*LPALDOPPLERFACTOR)(ALfloat value);
typedef void (*LPALDOPPLERVELOCITY)(ALfloat value);
typedef void (*LPALSPEEDOFSOUND)(ALfloat value);
typedef void (*LPALDISTANCEMODEL)(ALenum distanceModel);
]])
return openal

72
LoveKaraoke/main.lua Normal file
View File

@ -0,0 +1,72 @@
require("core.Library")
GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager and requires the entire multi library
require("core.GuiManager")
require("core.bin")
gui.ff.Color=Color.Black
t=gui:newTextLabel("",0,0,300,100)
t.Color=Color.purple
microphone = require("love-microphone")
function peakAmplitude(sounddata)
local peak_amp = -math.huge
for t = 0,sounddata:getSampleCount()-1 do
local amp = math.abs(sounddata:getSample(t)) -- |s(t)|
peak_amp = math.max(peak_amp, amp)
end
return peak_amp
end
function rmsAmplitude(sounddata)
local amp = 0
for t = 0,sounddata:getSampleCount()-1 do
amp = amp + sounddata:getSample(t)^2 -- (s(t))^2
end
return math.sqrt(amp / sounddata:getSampleCount())
end
local device,source
local record={}
local recording=false
function love.load()
print("Opening microphone:", microphone.getDefaultDeviceName())
device = microphone.openDevice(nil, nil, 0)
source = microphone.newQueueableSource()
device:setDataCallback(function(device, data)
if recording then
table.insert(record,{data,os.clock()})
test:setDualDim(nil,nil,nil,nil,nil,nil,peakAmplitude(data))
test2:setDualDim(nil,nil,nil,nil,nil,nil,rmsAmplitude(data))
end
end)
device:start()
multi:newLoop(function() device:poll() end)
end
test0=t:newTextButton("Start Recording","Start Recording",0,0,100,30)
test0:centerX()
test0:centerY()
test0:ApplyGradient{Color.Green,Color.Darken(Color.Green,.25)}
test0:OnReleased(function(b,self)
if self.text=="Start Recording" then
self.text="Stop Recording"
recording=true
else
test0.text="Playing Back!"
recording=false
local step=multi:newStep(1,#record)
step:OnStep(function(self,pos)
source:queue(record[pos][1])
if pos>1 then
self:hold(record[pos][2]-record[pos-1][2])
end
end)
step:OnEnd(function(self)
record={}
self:Destroy()
loop2:Destroy()
test0.text="Start Recording"
end)
loop2=multi:newLoop(function() source:play() end)
end
end)
test=t:newFrame("BAR",0,0,0,30,0,0,0)
test:ApplyGradient{Color.Green,Color.Darken(Color.Green,.25)}
test2=t:newFrame("BAR",0,70,0,30,0,0,0)
test2:ApplyGradient{Color.Green,Color.Darken(Color.Green,.25)}
--test:centerY()

View File

@ -0,0 +1 @@
require("multi")

View File

@ -0,0 +1,305 @@
--[[
MIT License
Copyright (c) 2017 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, sublicense, 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.
]]
multi.OnObjectCreated(function(obj)
if obj.Type=="loop" then
function obj:Act()
for i=1,#self.func do
self.func[i](self.Parent.clock()-self.Start,self)
end
end
elseif obj.Type=="step" then
function obj: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.pos,self)
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
elseif obj.Type=="tstep" then
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.pos,self)
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
end
end)
if thread then
function multi:newThreadedLoop(name,func)
local c=self:newTBase()
c.Type='loopThread'
c.Start=os.clock()
if func then
c.func={func}
end
function c:tofile(path)
local m=bin.new()
m:addBlock(self.Type)
m:addBlock(self.func)
m:addBlock(self.Active)
m:tofile(path)
end
function c:Resume()
self.rest=false
end
function c:Pause()
self.rest=true
end
function c:OnLoop(func)
table.insert(self.func,func)
end
c.rest=false
c.updaterate=0
c.restRate=.75
multi:newThread(name,function(ref)
while true do
if c.rest then
thread.sleep(c.restRate)
else
for i=1,#c.func do
c.func[i](os.clock()-self.Start,c)
end
thread.sleep(c.updaterate)
end
end
end)
self:create(c)
return c
end
function multi:newThreadedStep(name,start,reset,count,skip)
local c=self:newTBase()
local think=1
c.Type='stepThread'
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:tofile(path)
local m=bin.new()
m:addBlock(self.Type)
m:addBlock(self.func)
m:addBlock(self.funcE)
m:addBlock(self.funcS)
m:addBlock({pos=self.pos,endAt=self.endAt,skip=self.skip,spos=self.spos,count=self.count,start=self.start})
m:addBlock(self.Active)
m:tofile(path)
end
function c:Resume()
self.rest=false
end
function c:Pause()
self.rest=true
end
c.Reset=c.Resume
function c:OnStart(func)
table.insert(self.funcS,func)
end
function c:OnStep(func)
table.insert(self.func,1,func)
end
function c:OnEnd(func)
table.insert(self.funcE,func)
end
function c:Break()
self.rest=true
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()
end
c.updaterate=0
c.restRate=.1
multi:newThread(name,function(ref)
while true do
if c.rest then
ref:sleep(c.restRate)
else
if c~=nil then
if c.spos==0 then
if c.pos==c.start then
for fe=1,#c.funcS do
c.funcS[fe](c)
end
end
for i=1,#c.func do
c.func[i](c.pos,c)
end
c.pos=c.pos+c.count
if c.pos-c.count==c.endAt then
c:Pause()
for fe=1,#c.funcE do
c.funcE[fe](c)
end
c.pos=c.start
end
end
end
c.spos=c.spos+1
if c.spos>=c.skip then
c.spos=0
end
ref:sleep(c.updaterate)
end
end
end)
self:create(c)
return c
end
function multi:newThreadedTStep(name,start,reset,count,set)
local c=self:newTBase()
local think=1
c.Type='tstepThread'
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=os.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=os.clock()
self:Resume()
end
function c:tofile(path)
local m=bin.new()
m:addBlock(self.Type)
m:addBlock(self.func)
m:addBlock(self.funcE)
m:addBlock(self.funcS)
m:addBlock({pos=self.pos,endAt=self.endAt,skip=self.skip,timer=self.timer,count=self.count,start=self.start,set=self.set})
m:addBlock(self.Active)
m:tofile(path)
end
function c:Resume()
self.rest=false
end
function c:Pause()
self.rest=true
end
function c:OnStart(func)
table.insert(self.funcS,func)
end
function c:OnStep(func)
table.insert(self.func,func)
end
function c:OnEnd(func)
table.insert(self.funcE,func)
end
function c:Break()
self.Active=nil
end
function c:Reset(n)
if n then self.set=n end
self.timer=os.clock()
self:Resume()
end
c.updaterate=0
c.restRate=0
multi:newThread(name,function(ref)
while true do
if c.rest then
thread.sleep(c.restRate)
else
if os.clock()-c.timer>=c.set then
c:Reset()
if c.pos==c.start then
for fe=1,#c.funcS do
c.funcS[fe](c)
end
end
for i=1,#c.func do
c.func[i](c.pos,c)
end
c.pos=c.pos+c.count
if c.pos-c.count==c.endAt then
c:Pause()
for fe=1,#c.funcE do
c.funcE[fe](c)
end
c.pos=c.start
end
end
thread.skip(c.updaterate)
end
end
end)
self:create(c)
return c
end
end

View File

@ -0,0 +1,114 @@
--[[
MIT License
Copyright (c) 2017 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, sublicense, 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.
]]
require("multi")
os.sleep=love.timer.sleep
function love.run()
if love.math then
love.math.setRandomSeed(os.time())
end
if love.event then
love.event.pump()
end
if love.load then love.load(arg) end
if love.timer then love.timer.step() end
local dt = 0
while true do
-- Process events.
if love.event then
love.event.pump()
for e,a,b,c,d in love.event.poll() do
if e == "quit" then
if not love.quit or not love.quit() then
if love.audio then
love.audio.stop()
end
return
end
end
love.handlers[e](a,b,c,d)
end
end
if love.timer then
love.timer.step()
dt = love.timer.getDelta()
end
if love.update then love.update(dt) end
if multi.boost then
for i=1,multi.boost-1 do
multi:uManager(dt)
end
end
multi:uManager(dt)
if love.window and love.graphics and love.window.isCreated() then
love.graphics.clear()
love.graphics.origin()
if love.draw then love.draw() end
multi.dManager()
love.graphics.setColor(255,255,255,255)
if multi.draw then multi.draw() end
love.graphics.present()
end
end
end
multi.drawF={}
function multi:dManager()
for ii=1,#multi.drawF do
multi.drawF[ii]()
end
end
function multi:onDraw(func,i)
i=i or 1
table.insert(self.drawF,i,func)
end
function multi:lManager()
if love.event then
love.event.pump()
for e,a,b,c,d in love.event.poll() do
if e == "quit" then
if not love.quit or not love.quit() then
if love.audio then
love.audio.stop()
end
return nil
end
end
love.handlers[e](a,b,c,d)
end
end
if love.timer then
love.timer.step()
dt = love.timer.getDelta()
end
if love.update then love.update(dt) end
multi:uManager(dt)
if love.window and love.graphics and love.window.isCreated() then
love.graphics.clear()
love.graphics.origin()
if love.draw then love.draw() end
multi.dManager()
love.graphics.setColor(255,255,255,255)
if multi.draw then multi.draw() end
love.graphics.present()
end
end

2141
LoveKaraoke/multi/init.lua Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,133 @@
--[[
MIT License
Copyright (c) 2017 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, sublicense, 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.
]]
function os.getOS()
if package.config:sub(1,1)=='\\' then
return 'windows'
else
return 'unix'
end
end
-- Step 1 get lanes
lanes=require("lanes").configure()
--~ package.path="lua/?/init.lua;lua/?.lua;"..package.path
require("multi") -- get it all and have it on all lanes
function multi:canSystemThread()
return true
end
local multi=multi
-- Step 2 set up the linda objects
local __GlobalLinda = lanes.linda() -- handles global stuff
local __SleepingLinda = lanes.linda() -- handles sleeping stuff
-- For convience a GLOBAL table will be constructed to handle requests
local GLOBAL={}
setmetatable(GLOBAL,{
__index=function(t,k)
return __GlobalLinda:get(k)
end,
__newindex=function(t,k,v)
__GlobalLinda:set(k,v)
end,
})
-- Step 3 rewrite the thread methods to use lindas
local THREAD={}
function THREAD.set(name,val)
__GlobalLinda:set(name,val)
end
function THREAD.get(name)
__GlobalLinda:get(name)
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 THREAD.waitFor(name)
local function wait()
math.randomseed(os.time())
__SleepingLinda:receive(.001,randomString(12))
end
repeat wait() until __GlobalLinda:get(name)
return __GlobalLinda:get(name)
end
function THREAD.testFor(name,val,sym)
--
end
function THREAD.getCores()
return THREAD.__CORES
end
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.kill() -- trigger the lane destruction
-- coroutine.yield({"_kill_",":)"})
end
--[[ Step 4 We need to get sleeping working to handle timing... We want idle wait, not busy wait
Idle wait keeps the CPU running better where busy wait wastes CPU cycles... Lanes does not have a sleep method
however, a linda recieve will in fact be a idle wait! So we use that and wrap it in a nice package]]
function THREAD.sleep(n)
math.randomseed(os.time())
__SleepingLinda:receive(n,randomString(12))
end
function THREAD.hold(n)
local function wait()
math.randomseed(os.time())
__SleepingLinda:receive(.001,randomString(12))
end
repeat wait() until n()
end
-- Step 5 Basic Threads!
function multi:newSystemThread(name,func)
local c={}
local __self=c
c.name=name
c.Type="sthread"
c.thread=lanes.gen("*", func)()
function c:kill()
--self.status:Destroy()
self.thread:cancel()
print("Thread: '"..self.name.."' has been stopped!")
end
c.status=multi:newUpdater(multi.Priority_IDLE)
c.status.link=c
c.status:OnUpdate(function(self)
local v,err,t=self.link.thread:join(.001)
if err then
multi.OnError:Fire(self.link,err)
print("Error in systemThread: '"..self.link.name.."' <"..err..">")
self:Destroy()
end
end)
return c
end
print("Integrated Lanes!")
multi.integration={} -- for module creators
multi.integration.GLOBAL=GLOBAL
multi.integration.THREAD=THREAD
require("multi.integration.shared")
return {init=function() return GLOBAL,THREAD end}

View File

@ -0,0 +1,359 @@
require("multi.compat.love2d")
function multi:canSystemThread()
return true
end
multi.integration={}
multi.integration.love2d={}
multi.integration.love2d.ThreadBase=[[
tab={...}
__THREADNAME__=tab[2]
__THREADID__=tab[1]
require("love.filesystem")
require("love.system")
require("love.timer")
require("multi")
GLOBAL={}
setmetatable(GLOBAL,{
__index=function(t,k)
__sync__()
return __proxy__[k]
end,
__newindex=function(t,k,v)
__sync__()
__proxy__[k]=v
if type(v)=="userdata" then
__MainChan__:push(v)
else
__MainChan__:push("SYNC "..type(v).." "..k.." "..resolveData(v))
end
end,
})
function __sync__()
local data=__mythread__:pop()
while data do
love.timer.sleep(.001)
if type(data)=="string" then
local cmd,tp,name,d=data:match("(%S-) (%S-) (%S-) (.+)")
if name=="__DIEPLZ"..__THREADID__.."__" then
error("Thread: "..__THREADID__.." has been stopped!")
end
if cmd=="SYNC" then
__proxy__[name]=resolveType(tp,d)
end
else
__proxy__[name]=data
end
data=__mythread__:pop()
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
return ToStr(v)
elseif type(v)=="function" then
return dump(v)
elseif type(v)=="string" or type(v)=="number" or type(v)=="bool" or type(v)=="nil" then
return 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)
repeat __sync__() 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)
repeat __sync__() until n()
end
multi:newLoop(function(self)
self:Pause()
local ld=multi:getLoad()
self:Resume()
if ld<80 then
love.timer.sleep(.01)
end
end)
updater=multi:newUpdater()
updater:OnUpdate(__sync__)
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.integration.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
return ToStr(v)
elseif type(v)=="function" then
return dump(v)
elseif type(v)=="string" or type(v)=="number" or type(v)=="bool" or type(v)=="nil" then
return 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.integration.love2d.ThreadBase:gsub("INSERT_USER_CODE",dump(func)))
c.thread:start(c.ID,c.name)
function c:kill()
multi.integration.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.integration.GLOBAL=GLOBAL
multi.integration.THREAD=THREAD
updater=multi:newUpdater()
updater:OnUpdate(function(self)
local data=multi.integration.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.integration.love2d.mainChannel:pop()
end
end)
require("multi.integration.shared")
print("Integrated Love2d!")
return {
init=function(t)
if t then
if t.threadNamespace then
multi.integration.THREADNAME=t.threadNamespace
multi.integration.love2d.ThreadBase:gsub("sThread",t.threadNamespace)
end
if t.globalNamespace then
multi.integration.GLOBALNAME=t.globalNamespace
multi.integration.love2d.ThreadBase:gsub("GLOBAL",t.globalNamespace)
end
end
return GLOBAL,THREAD
end
}

View File

@ -0,0 +1,438 @@
--[[
MIT License
Copyright (c) 2017 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, sublicense, 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.
]]
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:newSystemThreadedQueue(name) -- in love2d this will spawn a channel on both ends
local c={} -- where we will store our object
c.name=name -- set the name this is important for the love2d side
if love then -- check love
if love.thread then -- make sure we can use the threading module
function c:init() -- create an init function so we can mimic on bith love2d and lanes
self.chan=love.thread.getChannel(self.name) -- create channel by the name self.name
function self:push(v) -- push to the channel
self.chan:push({type(v),resolveData(v)})
end
function self:pop() -- pop from the channel
local tab=self.chan:pop()
--print(tab)
if not tab then return end
return resolveType(tab[1],tab[2])
end
function self:peek()
local tab=self.chan:peek()
--print(tab)
if not tab then return end
return resolveType(tab[1],tab[2])
end
GLOBAL[self.name]=self -- send the object to the thread through the global interface
return self -- return the object
end
return c
else
error("Make sure you required the love.thread module!") -- tell the user if he/she didn't require said module
end
else
c.linda=lanes.linda() -- lanes is a bit eaiser, create the linda on the main thread
function c:push(v) -- push to the queue
self.linda:send("Q",v)
end
function c:pop() -- pop the queue
return ({self.linda:receive(0,"Q")})[2]
end
function c:peek()
return self.linda:get("Q")
end
function c:init() -- mimic the feature that love2d requires, so code can be consistent
return self
end
multi.integration.GLOBAL[name]=c -- send the object to the thread through the global interface
end
return c
end
function multi:systemThreadedBenchmark(n,p)
n=n or 1
local cores=multi.integration.THREAD.getCores()
local queue=multi:newSystemThreadedQueue("QUEUE")
multi.integration.GLOBAL["__SYSTEMBENCHMARK__"]=n
local sThread=multi.integration.THREAD
local GLOBAL=multi.integration.GLOBAL
for i=1,cores do
multi:newSystemThread("STHREAD_BENCH",function()
require("multi")
if multi:getPlatform()=="love2d" then
GLOBAL=_G.GLOBAL
sThread=_G.sThread
end -- we cannot have upvalues... in love2d globals not locals must be used
queue=sThread.waitFor("QUEUE"):init() -- always wait for when looking for a variable at the start of the thread!
multi:benchMark(sThread.waitFor("__SYSTEMBENCHMARK__")):OnBench(function(self,count)
queue:push(count)
multi:Stop()
end)
multi:mainloop()
end)
end
local c={}
c.tt=function() end
c.p=p
function c:OnBench(func)
self.tt=func
end
multi:newThread("THREAD_BENCH",function()
thread.sleep(n+.1)
GLOBAL["QUEUE"]=nil -- time to clean up
local num=0
data=queue:pop()
while data do
num=num+data
data=queue:pop()
end
if p then
print(tostring(p)..num)
end
c.tt(c,num)
end)
return c
end
function multi:newSystemThreadedTable(name,n)
local c={} -- where we will store our object
c.name=name -- set the name this is important for the love2d side
c.cores=n
c.hasT={}
if love then -- check love
if love.thread then -- make sure we can use the threading module
function c:init() -- create an init function so we can mimic on bith love2d and lanes
self.tab={}
self.chan=love.thread.getChannel(self.name) -- create channel by the name self.name
function self:waitFor(name) -- pop from the channel
repeat self:sync() until self[name]
return self[name]
end
function self:sync()
local data=self.chan:peek()
if data then
local cmd,tp,name,d=data:match("(%S-) (%S-) (%S-) (.+)")
if not self.hasT[name] then
if type(data)=="string" then
if cmd=="SYNC" then
self.tab[name]=resolveType(tp,d) -- this is defined in the loveManager.lua file
self.hasT[name]=true
end
else
self.tab[name]=data
end
self.chan:pop()
end
end
end
function self:reset(name)
self.hasT[core]=nil
end
setmetatable(self,{
__index=function(t,k)
self:sync()
return self.tab[k]
end,
__newindex=function(t,k,v)
self:sync()
self.tab[k]=v
if type(v)=="userdata" then
self.chan:push(v)
else
for i=1,self.cores do
self.chan:push("SYNC "..type(v).." "..k.." "..resolveData(v)) -- this is defined in the loveManager.lua file
end
end
end,
})
GLOBAL[self.name]=self -- send the object to the thread through the global interface
return self -- return the object
end
return c
else
error("Make sure you required the love.thread module!") -- tell the user if he/she didn't require said module
end
else
c.linda=lanes.linda() -- lanes is a bit eaiser, create the linda on the main thread
function c:waitFor(name)
while self[name]==nil do
-- Waiting
end
return self[name]
end
function c:sync()
return -- just so we match the love2d side
end
function c:init() -- set the metatable
setmetatable(self,{
__index=function(t,k)
return self.linda:get(k)
end,
__newindex=function(t,k,v)
self.linda:set(k,v)
end,
})
return self
end
multi.integration.GLOBAL[name]=c -- send the object to the thread through the global interface
end
return c
end
function multi:newSystemThreadedJobQueue(numOfCores)
local c={}
c.jobnum=1
c.cores=numOfCores or multi.integration.THREAD.getCores()
c.queueIN=multi:newSystemThreadedQueue("THREADED_JQ"):init()
c.queueOUT=multi:newSystemThreadedQueue("THREADED_JQO"):init()
c.queueALL=multi:newSystemThreadedQueue("THREADED_QALL"):init()
c.REG=multi:newSystemThreadedQueue("THREADED_JQ_F_REG"):init()
-- registerJob(name,func)
-- pushJob(...)
function c:registerJob(name,func)
for i=1,self.cores do
self.REG:push({name,func})
end
end
function c:pushJob(name,...)
self.queueOUT:push({self.jobnum,name,...})
self.jobnum=self.jobnum+1
end
local GLOBAL=multi.integration.GLOBAL -- set up locals incase we are using lanes
local sThread=multi.integration.THREAD -- set up locals incase we are using lanes
function c:doToAll(func)
local TaskName=multi.randomString(16)
for i=1,self.cores do
self.queueALL:push({TaskName,func})
end
end
function c:start()
print("Starting the stuff")
self:doToAll(function()
print("JOB QUEUE STARTED!")
_G["__started__"]=true
SFunc()
end)
end
GLOBAL["__JQ_COUNT__"]=c.cores
for i=1,c.cores do
multi:newSystemThread("System Threaded Job Queue Worker Thread #"..i,function()
require("multi")
__sleep__=.001
if love then -- lets make sure we don't reference upvalues if using love2d
GLOBAL=_G.GLOBAL
sThread=_G.sThread
__sleep__=.1
end
JQI=sThread.waitFor("THREADED_JQO"):init() -- Grab it
JQO=sThread.waitFor("THREADED_JQ"):init() -- Grab it
REG=sThread.waitFor("THREADED_JQ_F_REG"):init() -- Grab it
QALL=sThread.waitFor("THREADED_QALL"):init() -- Grab it
sThread.sleep(.1) -- lets wait for things to work out
GLOBAL["THREADED_JQ"]=nil -- remove it
GLOBAL["THREADED_JQO"]=nil -- remove it
GLOBAL["THREADED_JQ_F_REG"]=nil -- remove it
QALLT={}
FUNCS={}
SFunc=multi:newFunction(function(self)
MainLoop:Pause()
self:hold(.1)
MainLoop:Resume()
self:Pause()
end)
multi:newLoop(function()
local rd=REG:peek()
if rd then
if not FUNCS[rd[1]] then
FUNCS[rd[1]]=rd[2]
print(rd[1],FUNCS[rd[1]])
rd=nil -- lets clean up
REG:pop()
end
end
local d=QALL:peek()
if d then
if not QALLT[d[1]] then
QALLT[d[1]]=true
d[2]()
d=nil -- lets clean up
QALL:pop()
end
end
end)
setmetatable(_G,{
__index=function(t,k)
return FUNCS[k]
end
})
MainLoop=multi:newLoop(function(self)
if __started__ then
local job=JQI:pop()
if job then
local d=QALL:peek()
if d then
if not QALLT[d[1]] then
QALLT[d[1]]=true
d[2]()
d=nil -- lets clean up
QALL:pop()
end
end
local ID=table.remove(job,1) -- return and remove
local name=table.remove(job,1) -- return and remove
if FUNCS[name] then
JQO:push({ID,FUNCS[name](unpack(job))})
else
self:hold(function() return FUNCS[name] end)
JQO:push({ID,FUNCS[name](unpack(job))})
end
end
end
end)
if not love then
multi:mainloop()
end
end)
end
c.OnJobCompleted=multi:newConnection()
c.updater=multi:newLoop(function(self)
local data=self.link.queueIN:pop()
while data do
if data then
self.link.OnJobCompleted:Fire(unpack(data))
end
data=self.link.queueIN:pop()
end
end)
c.updater.link=c
return c
end
--[[if love then
if love.thread then
function multi:newSystemThreadedJobQueue(numOfCores)
local c={}
c.jobnum=1
c.cores=numOfCores or multi.integration.THREAD.getCores()
c.queueIN=multi:newSystemThreadedQueue("THREADED_JQ"):init()
c.queueOUT=multi:newSystemThreadedQueue("THREADED_JQO"):init()
c.queueALL=multi:newSystemThreadedQueue("THREADED_QALL"):init()
function c:registerJob(name,func)
GLOBAL["__TJQ__"..name.."__"]=func
end
function c:pushJob(name,...)
self.queueOUT:push({self.jobnum,name,...})
self.jobnum=self.jobnum+1
end
local GLOBAL=multi.integration.GLOBAL
local sThread=multi.integration.THREAD
function c:doToAll(func)
local TaskName=multi.randomString(16)
for i=1,self.cores do
self.queueALL:push(TaskName)
end
self:registerJob(TaskName,func)
end
GLOBAL["__JQ_COUNT__"]=c.cores
for i=1,c.cores do
multi:newSystemThread("System Threaded Job Queue Worker Thread #"..i,function()
GLOBAL=_G.GLOBAL
sThread=_G.sThread
local JQI=sThread.waitFor("THREADED_JQO"):init() -- Grab it
local JQO=sThread.waitFor("THREADED_JQ"):init() -- Grab it
local QALL=sThread.waitFor("THREADED_QALL"):init() -- Grab it
sThread.sleep(.1) -- lets wait for things to work out
setmetatable(_G,{
__index=function(t,k,v)
return GLOBAL["__TJQ__"..k.."__"]
end
})
GLOBAL["THREADED_JQ"]=nil -- remove it
GLOBAL["THREADED_JQO"]=nil -- remove it
local QALLT={}
multi:newLoop(function()
local job=JQI:pop()
local d=QALL:peek()
if d then
if not QALLT[d] then
QALLT[d]=true
_G[d]()
QALL:pop()
end
end
if job then
local ID=table.remove(job,1) -- return and remove
local name=table.remove(job,1) -- return and remove
local ret={sThread.waitFor("__TJQ__"..name.."__")(unpack(job))} -- unpack the rest
JQO:push({ID,ret})
end
end)
end)
end
c.OnJobCompleted=multi:newConnection()
c.updater=multi:newLoop(function(self)
local data=self.link.queueIN:pop()
while data do
if data then
self.link.OnJobCompleted:Fire(unpack(data))
end
data=self.link.queueIN:pop()
end
end)
c.updater.link=c
return c
end
end
end]]
function multi:newSystemThreadedExecute(cmd)
local c={}
local GLOBAL=multi.integration.GLOBAL -- set up locals incase we are using lanes
local sThread=multi.integration.THREAD -- set up locals incase we are using lanes
local name="Execute_Thread"..multi.randomString(16)
c.name=name
GLOBAL[name.."CMD"]=cmd
multi:newSystemThread(name,function()
if love then -- lets make sure we don't reference upvalues if using love2d
GLOBAL=_G.GLOBAL
sThread=_G.sThread
name=__THREADNAME__ -- global data same as the name we used in this functions creation
end -- Lanes should take the local upvalues ^^^
cmd=sThread.waitFor(name.."CMD")
local ret=os.execute(cmd)
GLOBAL[name.."R"]=ret
end)
c.OnCMDFinished=multi:newConnection()
c.looper=multi:newLoop(function(self)
local ret=GLOBAL[self.link.name.."R"]
if ret then
self.link.OnCMDFinished:Fire(ret)
self:Destroy()
end
end)
c.looper.link=c
return c
end

View File

@ -0,0 +1,395 @@
--[[
MIT License
Copyright (c) 2017 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, sublicense, 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.
]]
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:newSystemThreadedQueue(name) -- in love2d this will spawn a channel on both ends
local c={} -- where we will store our object
c.name=name -- set the name this is important for the love2d side
if love then -- check love
if love.thread then -- make sure we can use the threading module
function c:init() -- create an init function so we can mimic on bith love2d and lanes
self.chan=love.thread.getChannel(self.name) -- create channel by the name self.name
function self:push(v) -- push to the channel
self.chan:push({type(v),resolveData(v)})
end
function self:pop() -- pop from the channel
local tab=self.chan:pop()
--print(tab)
if not tab then return end
return resolveType(tab[1],tab[2])
end
function self:peek()
local tab=self.chan:peek()
--print(tab)
if not tab then return end
return resolveType(tab[1],tab[2])
end
GLOBAL[self.name]=self -- send the object to the thread through the global interface
return self -- return the object
end
return c
else
error("Make sure you required the love.thread module!") -- tell the user if he/she didn't require said module
end
else
c.linda=lanes.linda() -- lanes is a bit eaiser, create the linda on the main thread
function c:push(v) -- push to the queue
self.linda:send("Q",v)
end
function c:pop() -- pop the queue
return ({self.linda:receive(0,"Q")})[2]
end
function c:peek()
return self.linda:get("Q")
end
function c:init() -- mimic the feature that love2d requires, so code can be consistent
return self
end
multi.integration.GLOBAL[name]=c -- send the object to the thread through the global interface
end
return c
end
function multi:systemThreadedBenchmark(n,p)
n=n or 1
local cores=multi.integration.THREAD.getCores()
local queue=multi:newSystemThreadedQueue("QUEUE")
multi.integration.GLOBAL["__SYSTEMBENCHMARK__"]=n
local sThread=multi.integration.THREAD
local GLOBAL=multi.integration.GLOBAL
for i=1,cores do
multi:newSystemThread("STHREAD_BENCH",function()
require("multi")
if multi:getPlatform()=="love2d" then
GLOBAL=_G.GLOBAL
sThread=_G.sThread
end -- we cannot have upvalues... in love2d globals not locals must be used
queue=sThread.waitFor("QUEUE"):init() -- always wait for when looking for a variable at the start of the thread!
multi:benchMark(sThread.waitFor("__SYSTEMBENCHMARK__")):OnBench(function(self,count)
queue:push(count)
multi:Stop()
end)
multi:mainloop()
end)
end
local c={}
c.tt=function() end
c.p=p
function c:OnBench(func)
self.tt=func
end
multi:newThread("THREAD_BENCH",function()
thread.sleep(n+.1)
GLOBAL["QUEUE"]=nil -- time to clean up
local num=0
data=queue:pop()
while data do
num=num+data
data=queue:pop()
end
if p then
print(tostring(p)..num)
end
c.tt(c,num)
end)
return c
end
function multi:newSystemThreadedTable(name,n)
local c={} -- where we will store our object
c.name=name -- set the name this is important for the love2d side
c.cores=n
c.hasT={}
if love then -- check love
if love.thread then -- make sure we can use the threading module
function c:init() -- create an init function so we can mimic on bith love2d and lanes
self.tab={}
self.chan=love.thread.getChannel(self.name) -- create channel by the name self.name
function self:waitFor(name) -- pop from the channel
repeat self:sync() until self[name]
return self[name]
end
function self:sync()
local data=self.chan:peek()
if data then
local cmd,tp,name,d=data:match("(%S-) (%S-) (%S-) (.+)")
if not self.hasT[name] then
if type(data)=="string" then
if cmd=="SYNC" then
self.tab[name]=resolveType(tp,d) -- this is defined in the loveManager.lua file
self.hasT[name]=true
end
else
self.tab[name]=data
end
self.chan:pop()
end
end
end
function self:reset(name)
self.hasT[core]=nil
end
setmetatable(self,{
__index=function(t,k)
self:sync()
return self.tab[k]
end,
__newindex=function(t,k,v)
self:sync()
self.tab[k]=v
if type(v)=="userdata" then
self.chan:push(v)
else
for i=1,#self.cores do
self.chan:push("SYNC "..type(v).." "..k.." "..resolveData(v)) -- this is defined in the loveManager.lua file
end
end
end,
})
GLOBAL[self.name]=self -- send the object to the thread through the global interface
return self -- return the object
end
return c
else
error("Make sure you required the love.thread module!") -- tell the user if he/she didn't require said module
end
else
c.linda=lanes.linda() -- lanes is a bit eaiser, create the linda on the main thread
function c:waitFor(name)
while self[name]==nil do
-- Waiting
end
return self[name]
end
function c:sync()
return -- just so we match the love2d side
end
function c:init() -- set the metatable
setmetatable(self,{
__index=function(t,k)
return self.linda:get(k)
end,
__newindex=function(t,k,v)
self.linda:set(k,v)
end,
})
return self
end
multi.integration.GLOBAL[name]=c -- send the object to the thread through the global interface
end
return c
end
function multi:newSystemThreadedJobQueue(numOfCores)
local c={}
c.jobnum=1
c.cores=numOfCores or multi.integration.THREAD.getCores()
c.queueIN=multi:newSystemThreadedQueue("THREADED_JQ"):init()
c.queueOUT=multi:newSystemThreadedQueue("THREADED_JQO"):init()
c.queueALL=multi:newSystemThreadedQueue("THREADED_QALL"):init()
c.REG=multi:newSystemThreadedTable("THREADED_JQ_F_REG",c.cores):init()
-- registerJob(name,func)
-- pushJob(...)
function c:registerJob(name,func)
self.REG[name]=func
end
function c:pushJob(name,...)
self.queueOUT:push({self.jobnum,name,...})
self.jobnum=self.jobnum+1
end
local GLOBAL=multi.integration.GLOBAL -- set up locals incase we are using lanes
local sThread=multi.integration.THREAD -- set up locals incase we are using lanes
function c:doToAll(func)
local TaskName=multi.randomString(16)
for i=1,self.cores do
self.queueALL:push(TaskName)
end
self:registerJob(TaskName,func)
end
GLOBAL["__JQ_COUNT__"]=c.cores
for i=1,c.cores do
multi:newSystemThread("System Threaded Job Queue Worker Thread #"..i,function()
require("multi")
__sleep__=.001
if love then -- lets make sure we don't reference upvalues if using love2d
GLOBAL=_G.GLOBAL
sThread=_G.sThread
__sleep__=.1
end
JQI=sThread.waitFor("THREADED_JQO"):init() -- Grab it
JQO=sThread.waitFor("THREADED_JQ"):init() -- Grab it
FGLOBAL=sThread.waitFor("THREADED_JQ_F_REG"):init() -- Grab it
QALL=sThread.waitFor("THREADED_QALL"):init() -- Grab it
sThread.sleep(.1) -- lets wait for things to work out
setmetatable(_G,{
__index=FGLOBAL
})
GLOBAL["THREADED_JQ"]=nil -- remove it
GLOBAL["THREADED_JQO"]=nil -- remove it
GLOBAL["THREADED_JQ_F_REG"]=nil -- remove it
QALLT={}
multi:newLoop(function()
sThread.sleep(__sleep__) -- lets allow cpu time for other processes on our system!
local job=JQI:pop()
local d=QALL:peek()
if d then
if not QALLT[d] then
QALLT[d]=true
print("?")
FGLOBAL:waitFor(d)()
QALL:pop()
end
end
if job then
local ID=table.remove(job,1) -- return and remove
local name=table.remove(job,1) -- return and remove
local ret={FGLOBAL:waitFor(name)(unpack(job))} -- unpack the rest
JQO:push({ID,ret})
end
end)
if not love then
multi:mainloop()
end
end)
end
c.OnJobCompleted=multi:newConnection()
c.updater=multi:newLoop(function(self)
local data=self.link.queueIN:pop()
while data do
if data then
self.link.OnJobCompleted:Fire(unpack(data))
end
data=self.link.queueIN:pop()
end
end)
c.updater.link=c
return c
end
--[[if love then
if love.thread then
function multi:newSystemThreadedJobQueue(numOfCores)
local c={}
c.jobnum=1
c.cores=numOfCores or multi.integration.THREAD.getCores()
c.queueIN=multi:newSystemThreadedQueue("THREADED_JQ"):init()
c.queueOUT=multi:newSystemThreadedQueue("THREADED_JQO"):init()
c.queueALL=multi:newSystemThreadedQueue("THREADED_QALL"):init()
function c:registerJob(name,func)
GLOBAL["__TJQ__"..name.."__"]=func
end
function c:pushJob(name,...)
self.queueOUT:push({self.jobnum,name,...})
self.jobnum=self.jobnum+1
end
local GLOBAL=multi.integration.GLOBAL
local sThread=multi.integration.THREAD
function c:doToAll(func)
local TaskName=multi.randomString(16)
for i=1,self.cores do
self.queueALL:push(TaskName)
end
self:registerJob(TaskName,func)
end
GLOBAL["__JQ_COUNT__"]=c.cores
for i=1,c.cores do
multi:newSystemThread("System Threaded Job Queue Worker Thread #"..i,function()
GLOBAL=_G.GLOBAL
sThread=_G.sThread
local JQI=sThread.waitFor("THREADED_JQO"):init() -- Grab it
local JQO=sThread.waitFor("THREADED_JQ"):init() -- Grab it
local QALL=sThread.waitFor("THREADED_QALL"):init() -- Grab it
sThread.sleep(.1) -- lets wait for things to work out
setmetatable(_G,{
__index=function(t,k,v)
return GLOBAL["__TJQ__"..k.."__"]
end
})
GLOBAL["THREADED_JQ"]=nil -- remove it
GLOBAL["THREADED_JQO"]=nil -- remove it
local QALLT={}
multi:newLoop(function()
local job=JQI:pop()
local d=QALL:peek()
if d then
if not QALLT[d] then
QALLT[d]=true
_G[d]()
QALL:pop()
end
end
if job then
local ID=table.remove(job,1) -- return and remove
local name=table.remove(job,1) -- return and remove
local ret={sThread.waitFor("__TJQ__"..name.."__")(unpack(job))} -- unpack the rest
JQO:push({ID,ret})
end
end)
end)
end
c.OnJobCompleted=multi:newConnection()
c.updater=multi:newLoop(function(self)
local data=self.link.queueIN:pop()
while data do
if data then
self.link.OnJobCompleted:Fire(unpack(data))
end
data=self.link.queueIN:pop()
end
end)
c.updater.link=c
return c
end
end
end]]
function multi:newSystemThreadedExecute(cmd)
local c={}
local GLOBAL=multi.integration.GLOBAL -- set up locals incase we are using lanes
local sThread=multi.integration.THREAD -- set up locals incase we are using lanes
local name="Execute_Thread"..multi.randomString(16)
c.name=name
GLOBAL[name.."CMD"]=cmd
multi:newSystemThread(name,function()
if love then -- lets make sure we don't reference upvalues if using love2d
GLOBAL=_G.GLOBAL
sThread=_G.sThread
name=__THREADNAME__ -- global data same as the name we used in this functions creation
end -- Lanes should take the local upvalues ^^^
cmd=sThread.waitFor(name.."CMD")
local ret=os.execute(cmd)
GLOBAL[name.."R"]=ret
end)
c.OnCMDFinished=multi:newConnection()
c.looper=multi:newLoop(function(self)
local ret=GLOBAL[self.link.name.."R"]
if ret then
self.link.OnCMDFinished:Fire(ret)
self:Destroy()
end
end)
c.looper.link=c
return c
end