Added files
This commit is contained in:
parent
48eb46ec50
commit
ca2259a7df
39
LoveKaraoke/conf.lua
Normal file
39
LoveKaraoke/conf.lua
Normal 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
|
||||
212
LoveKaraoke/core/AudioManager.lua
Normal file
212
LoveKaraoke/core/AudioManager.lua
Normal 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
|
||||
]]
|
||||
4027
LoveKaraoke/core/GuiManager.lua
Normal file
4027
LoveKaraoke/core/GuiManager.lua
Normal file
File diff suppressed because it is too large
Load Diff
319
LoveKaraoke/core/Library.lua
Normal file
319
LoveKaraoke/core/Library.lua
Normal 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")
|
||||
2
LoveKaraoke/core/README.txt
Normal file
2
LoveKaraoke/core/README.txt
Normal 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
598
LoveKaraoke/core/T1.lua
Normal 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
598
LoveKaraoke/core/T2.lua
Normal 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
596
LoveKaraoke/core/T3.lua
Normal 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
596
LoveKaraoke/core/T4.lua
Normal 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()
|
||||
158
LoveKaraoke/core/ThreadManager.lua
Normal file
158
LoveKaraoke/core/ThreadManager.lua
Normal 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
797
LoveKaraoke/core/Utils.lua
Normal 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
3207
LoveKaraoke/core/bin.lua
Normal file
File diff suppressed because it is too large
Load Diff
103
LoveKaraoke/core/lovebind.lua
Normal file
103
LoveKaraoke/core/lovebind.lua
Normal 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
|
||||
106
LoveKaraoke/core/net/Documentation.txt
Normal file
106
LoveKaraoke/core/net/Documentation.txt
Normal 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]
|
||||
58
LoveKaraoke/core/net/admin.lua
Normal file
58
LoveKaraoke/core/net/admin.lua
Normal 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)
|
||||
325
LoveKaraoke/core/net/aft.lua
Normal file
325
LoveKaraoke/core/net/aft.lua
Normal 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
|
||||
273
LoveKaraoke/core/net/audp.lua
Normal file
273
LoveKaraoke/core/net/audp.lua
Normal 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
|
||||
134
LoveKaraoke/core/net/chatting.lua
Normal file
134
LoveKaraoke/core/net/chatting.lua
Normal 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
|
||||
15
LoveKaraoke/core/net/db.lua
Normal file
15
LoveKaraoke/core/net/db.lua
Normal 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
|
||||
|
||||
|
||||
24
LoveKaraoke/core/net/eft.lua
Normal file
24
LoveKaraoke/core/net/eft.lua
Normal 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)
|
||||
54
LoveKaraoke/core/net/email.lua
Normal file
54
LoveKaraoke/core/net/email.lua
Normal 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
|
||||
|
||||
53
LoveKaraoke/core/net/emailWIP.lua
Normal file
53
LoveKaraoke/core/net/emailWIP.lua
Normal 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
|
||||
|
||||
266
LoveKaraoke/core/net/identity.lua
Normal file
266
LoveKaraoke/core/net/identity.lua
Normal 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
|
||||
70
LoveKaraoke/core/net/inbox.lua
Normal file
70
LoveKaraoke/core/net/inbox.lua
Normal 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)
|
||||
664
LoveKaraoke/core/net/init.lua
Normal file
664
LoveKaraoke/core/net/init.lua
Normal 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
|
||||
7
LoveKaraoke/core/net/logging.lua
Normal file
7
LoveKaraoke/core/net/logging.lua
Normal 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)
|
||||
7
LoveKaraoke/core/net/loggingWIP.lua
Normal file
7
LoveKaraoke/core/net/loggingWIP.lua
Normal 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)
|
||||
31
LoveKaraoke/core/net/p2p.lua
Normal file
31
LoveKaraoke/core/net/p2p.lua
Normal 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
|
||||
31
LoveKaraoke/core/net/p2pWIP.lua
Normal file
31
LoveKaraoke/core/net/p2pWIP.lua
Normal 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
|
||||
24
LoveKaraoke/core/net/relay.lua
Normal file
24
LoveKaraoke/core/net/relay.lua
Normal 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)
|
||||
48
LoveKaraoke/core/net/settings.lua
Normal file
48
LoveKaraoke/core/net/settings.lua
Normal 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
|
||||
48
LoveKaraoke/core/net/settingsWIP.lua
Normal file
48
LoveKaraoke/core/net/settingsWIP.lua
Normal 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
|
||||
179
LoveKaraoke/core/net/sft.lua
Normal file
179
LoveKaraoke/core/net/sft.lua
Normal 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
|
||||
44
LoveKaraoke/core/net/status.lua
Normal file
44
LoveKaraoke/core/net/status.lua
Normal 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
|
||||
]])
|
||||
41
LoveKaraoke/core/net/threading.lua
Normal file
41
LoveKaraoke/core/net/threading.lua
Normal 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.
|
||||
|
||||
37
LoveKaraoke/core/net/threadingWIP.lua
Normal file
37
LoveKaraoke/core/net/threadingWIP.lua
Normal 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.
|
||||
|
||||
37
LoveKaraoke/core/net/users.lua
Normal file
37
LoveKaraoke/core/net/users.lua
Normal 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)
|
||||
43
LoveKaraoke/core/net/version.lua
Normal file
43
LoveKaraoke/core/net/version.lua
Normal 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)
|
||||
37
LoveKaraoke/core/parseManager/AICM.lua
Normal file
37
LoveKaraoke/core/parseManager/AICM.lua
Normal 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
|
||||
71
LoveKaraoke/core/parseManager/EBIM.lua
Normal file
71
LoveKaraoke/core/parseManager/EBIM.lua
Normal 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)
|
||||
44
LoveKaraoke/core/parseManager/bytecode.lua
Normal file
44
LoveKaraoke/core/parseManager/bytecode.lua
Normal 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
|
||||
1277
LoveKaraoke/core/parseManager/init.lua
Normal file
1277
LoveKaraoke/core/parseManager/init.lua
Normal file
File diff suppressed because it is too large
Load Diff
10
LoveKaraoke/core/parseManager/interpreter.lua
Normal file
10
LoveKaraoke/core/parseManager/interpreter.lua
Normal 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
BIN
LoveKaraoke/core/test.dat
Normal file
Binary file not shown.
18
LoveKaraoke/core/test.lua
Normal file
18
LoveKaraoke/core/test.lua
Normal 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"])
|
||||
223
LoveKaraoke/love-microphone/Device.lua
Normal file
223
LoveKaraoke/love-microphone/Device.lua
Normal 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
|
||||
237
LoveKaraoke/love-microphone/QueueableSource.lua
Normal file
237
LoveKaraoke/love-microphone/QueueableSource.lua
Normal 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
|
||||
97
LoveKaraoke/love-microphone/init.lua
Normal file
97
LoveKaraoke/love-microphone/init.lua
Normal 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
|
||||
381
LoveKaraoke/love-microphone/openal.lua
Normal file
381
LoveKaraoke/love-microphone/openal.lua
Normal 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
72
LoveKaraoke/main.lua
Normal 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()
|
||||
1
LoveKaraoke/multi/all.lua
Normal file
1
LoveKaraoke/multi/all.lua
Normal file
@ -0,0 +1 @@
|
||||
require("multi")
|
||||
305
LoveKaraoke/multi/compat/backwards[1,5,0].lua
Normal file
305
LoveKaraoke/multi/compat/backwards[1,5,0].lua
Normal 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
|
||||
114
LoveKaraoke/multi/compat/love2d.lua
Normal file
114
LoveKaraoke/multi/compat/love2d.lua
Normal 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
2141
LoveKaraoke/multi/init.lua
Normal file
File diff suppressed because it is too large
Load Diff
133
LoveKaraoke/multi/integration/lanesManager.lua
Normal file
133
LoveKaraoke/multi/integration/lanesManager.lua
Normal 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}
|
||||
359
LoveKaraoke/multi/integration/loveManager.lua
Normal file
359
LoveKaraoke/multi/integration/loveManager.lua
Normal 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
|
||||
}
|
||||
438
LoveKaraoke/multi/integration/shared.lua
Normal file
438
LoveKaraoke/multi/integration/shared.lua
Normal 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
|
||||
395
LoveKaraoke/multi/integration/sharedw.lua
Normal file
395
LoveKaraoke/multi/integration/sharedw.lua
Normal 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
|
||||
Loading…
x
Reference in New Issue
Block a user