Add files via upload
This commit is contained in:
parent
e27bcae50c
commit
85b3e6f19b
212
Libs/AudioManager.lua
Normal file
212
Libs/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
|
||||
]]
|
||||
319
Libs/Library.lua
Normal file
319
Libs/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")
|
||||
1391
Libs/MultiManager.lua
Normal file
1391
Libs/MultiManager.lua
Normal file
File diff suppressed because it is too large
Load Diff
2
Libs/README.txt
Normal file
2
Libs/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
Libs/T1.lua
Normal file
598
Libs/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
Libs/T2.lua
Normal file
598
Libs/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
Libs/T3.lua
Normal file
596
Libs/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
Libs/T4.lua
Normal file
596
Libs/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
Libs/ThreadManager.lua
Normal file
158
Libs/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
Libs/Utils.lua
Normal file
797
Libs/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))
|
||||
2675
Libs/bin.lua
Normal file
2675
Libs/bin.lua
Normal file
File diff suppressed because it is too large
Load Diff
103
Libs/lovebind.lua
Normal file
103
Libs/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
|
||||
BIN
Libs/test.dat
Normal file
BIN
Libs/test.dat
Normal file
Binary file not shown.
18
Libs/test.lua
Normal file
18
Libs/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"])
|
||||
BIN
engine/OpenAL32.dll
Normal file
BIN
engine/OpenAL32.dll
Normal file
Binary file not shown.
BIN
engine/SDL2.dll
Normal file
BIN
engine/SDL2.dll
Normal file
Binary file not shown.
1031
engine/changes.txt
Normal file
1031
engine/changes.txt
Normal file
File diff suppressed because it is too large
Load Diff
BIN
engine/game.ico
Normal file
BIN
engine/game.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 361 KiB |
977
engine/license.txt
Normal file
977
engine/license.txt
Normal file
@ -0,0 +1,977 @@
|
||||
This software uses LOVE:
|
||||
|
||||
LOVE is Copyright (c) 2006-2016 LOVE Development Team
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
|
||||
---------
|
||||
|
||||
This software uses LuaJIT:
|
||||
|
||||
LuaJIT is Copyright (c) 2005-2015 Mike Pall
|
||||
|
||||
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.
|
||||
|
||||
---------
|
||||
|
||||
This software uses ENet:
|
||||
|
||||
Copyright (c) 2002-2014 Lee Salzman
|
||||
|
||||
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.
|
||||
|
||||
---------
|
||||
|
||||
This software uses lua-enet:
|
||||
|
||||
Copyright (C) 2011 by Leaf Corcoran
|
||||
|
||||
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.
|
||||
|
||||
---------
|
||||
|
||||
This software uses UTF8-CPP:
|
||||
|
||||
Copyright 2006 Nemanja Trifunovic
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
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, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
---------
|
||||
|
||||
This software uses the following LGPL libraries on Windows, Mac OS X, Linux,
|
||||
and Android:
|
||||
|
||||
- libmpg123
|
||||
Website: http://www.mpg123.de/
|
||||
Source download: http://sourceforge.net/projects/mpg123/files/latest/download
|
||||
- OpenAL Soft
|
||||
Website: http://kcat.strangesoft.net/openal.html
|
||||
Source download: http://kcat.strangesoft.net/openal.html#download
|
||||
|
||||
Following are the LGPL and GPL license texts:
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code
|
||||
keep intact all notices of the absence of any warranty and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
BIN
engine/love.dll
Normal file
BIN
engine/love.dll
Normal file
Binary file not shown.
BIN
engine/love.exe
Normal file
BIN
engine/love.exe
Normal file
Binary file not shown.
BIN
engine/love.ico
Normal file
BIN
engine/love.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 361 KiB |
BIN
engine/lua51.dll
Normal file
BIN
engine/lua51.dll
Normal file
Binary file not shown.
BIN
engine/mpg123.dll
Normal file
BIN
engine/mpg123.dll
Normal file
Binary file not shown.
BIN
engine/msvcp120.dll
Normal file
BIN
engine/msvcp120.dll
Normal file
Binary file not shown.
BIN
engine/msvcr120.dll
Normal file
BIN
engine/msvcr120.dll
Normal file
Binary file not shown.
96
engine/readme.txt
Normal file
96
engine/readme.txt
Normal file
@ -0,0 +1,96 @@
|
||||
LÖVE is an *awesome* framework you can use to make 2D games in Lua. It's free, open-source, and works on Windows, Mac OS X, Linux, Android, and iOS.
|
||||
|
||||
[](https://ci.appveyor.com/project/AlexSzpakowski/love)
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
We use our [wiki][wiki] for documentation.
|
||||
If you need further help, feel free to ask on our [forums][forums], and last but not least there's the irc channel [#love on OFTC][irc].
|
||||
|
||||
Compilation
|
||||
-----------
|
||||
|
||||
###Windows
|
||||
Follow the instructions at the [megasource][megasource] repository page.
|
||||
|
||||
###*nix
|
||||
Run `platform/unix/automagic` from the repository root, then run ./configure and make.
|
||||
|
||||
$ platform/unix/automagic
|
||||
$ ./configure
|
||||
$ make
|
||||
|
||||
###Mac OS X
|
||||
Download the required frameworks from [here][dependencies] and place them in `/Library/Frameworks/`.
|
||||
|
||||
Then use the Xcode project found at `platform/xcode/love.xcodeproj` to build the `love-macosx` target.
|
||||
|
||||
###iOS
|
||||
Download the required libraries from [here][dependencies-ios] and place the `include` and `libraries` folders
|
||||
into the `platform/xcode/ios` folder.
|
||||
|
||||
Then use the Xcode project found at `platform/xcode/love.xcodeproj` to build the `love-ios` target.
|
||||
|
||||
See `readme-iOS.rtf` for more information.
|
||||
|
||||
###Android
|
||||
Visit the [Android build repository][android-repository] for build instructions.
|
||||
|
||||
Repository information
|
||||
----------------------
|
||||
|
||||
We use the 'default' branch for development, and therefore it should not be considered stable.
|
||||
Also used is the 'minor' branch, which is used for features in the next minor version and it is
|
||||
not our development target (which would be the next revision - version numbers are formatted major.minor.revision.)
|
||||
|
||||
We tag all our releases (since we started using mercurial), and have binary downloads available for them.
|
||||
|
||||
Experimental changes are developed in the separate [love-experiments][love-experiments] repository.
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
The best places to contribute are through the Bitbucket issue tracker and the official IRC channel.
|
||||
For code contributions, pull requests and patches are welcome. Be sure to read the [source code style guide][codestyle].
|
||||
|
||||
Builds
|
||||
------
|
||||
|
||||
Releases are found in the 'downloads' section on bitbucket, are linked on [the site][site],
|
||||
and there's a ppa for ubuntu, [ppa:bartbes/love-stable][stableppa].
|
||||
|
||||
There are also unstable/nightly builds:
|
||||
|
||||
- Most can be found [here][builds].
|
||||
- For ubuntu linux they are in [ppa:bartbes/love-unstable][unstableppa]
|
||||
- For arch linux there's [love-hg][aur] in the AUR.
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
- SDL2
|
||||
- OpenGL 2.1+ / OpenGL ES 2+
|
||||
- OpenAL
|
||||
- Lua / LuaJIT / LLVM-lua
|
||||
- FreeType
|
||||
- PhysicsFS
|
||||
- ModPlug
|
||||
- mpg123
|
||||
- Vorbisfile
|
||||
- Theora
|
||||
|
||||
[site]: http://love2d.org
|
||||
[wiki]: http://love2d.org/wiki
|
||||
[forums]: http://love2d.org/forums
|
||||
[irc]: irc://irc.oftc.net/love
|
||||
[dependencies]: http://love2d.org/sdk
|
||||
[dependencies-ios]: https://bitbucket.org/rude/love/downloads/love-0.10.0-ios-libraries.zip
|
||||
[megasource]: https://bitbucket.org/rude/megasource
|
||||
[builds]: http://love2d.org/builds
|
||||
[stableppa]: https://launchpad.net/~bartbes/+archive/love-stable
|
||||
[unstableppa]: https://launchpad.net/~bartbes/+archive/love-unstable
|
||||
[aur]: http://aur.archlinux.org/packages/love-hg
|
||||
[love-experiments]: https://bitbucket.org/bartbes/love-experiments
|
||||
[codestyle]: https://love2d.org/wiki/Code_Style
|
||||
[android-repository]: https://bitbucket.org/MartinFelis/love-android-sdl2
|
||||
BIN
lua5.1.dll
Normal file
BIN
lua5.1.dll
Normal file
Binary file not shown.
51
net/chatting.lua
Normal file
51
net/chatting.lua
Normal file
@ -0,0 +1,51 @@
|
||||
require("net")
|
||||
--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",{1,0,0})
|
||||
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.OnDataRecieved:connect(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
|
||||
local struct={ -- pack the info up as a table so the server can do filtering and whatnot to the chat
|
||||
user=user,
|
||||
msg=msg
|
||||
}
|
||||
self.OnChatRecieved:Fire(struct) -- trigger the chat event
|
||||
self:sendAll("!chatting! "..struct.user.." '"..struct.msg.."'")
|
||||
end
|
||||
end)
|
||||
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:connect(function(self,data) -- when the client 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
|
||||
--This is the client so our job here is done
|
||||
self.OnChatRecieved:Fire(user,msg) -- trigger the chat event
|
||||
end
|
||||
end)
|
||||
function c:sendChat(user,msg)
|
||||
self:send("!chatting! "..user.." '"..msg.."'")
|
||||
end
|
||||
c.OnChatRecieved=multi:newConnection() -- create a chat event
|
||||
end)
|
||||
end
|
||||
if net.autoInit then
|
||||
net.chatting:init()
|
||||
end
|
||||
53
net/email.lua
Normal file
53
net/email.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
|
||||
|
||||
192
net/identity.lua
Normal file
192
net/identity.lua
Normal file
@ -0,0 +1,192 @@
|
||||
require("net")
|
||||
--General Stuff
|
||||
--[[ What this module does!
|
||||
Adds
|
||||
net.identity:init()
|
||||
|
||||
]]
|
||||
net:registerModule("identity",{1,0,0})
|
||||
function net.hash(text,n)
|
||||
n=n or 16
|
||||
return bin.new(text.."jgmhktyf"):getHash(n)
|
||||
end
|
||||
function net.identity:init() -- calling this initilizes the library and binds it to the servers and clients created
|
||||
--Server Stuff
|
||||
net.OnServerCreated:connect(function(s)
|
||||
s.userFolder="./"
|
||||
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:getUserCred(user)
|
||||
local userdata=bin.load(self.userFolder..net.hash(user)..".dat")
|
||||
return userdata:match("%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)
|
||||
self.userFolder=loc
|
||||
end
|
||||
function s:loginUserOut(user)
|
||||
self.loggedIn[user]=nil
|
||||
end
|
||||
function s:loginUserIn(user,cid)
|
||||
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
|
||||
return self:getUserDataHandle(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.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 "{}"))())
|
||||
for i=1,#rets do
|
||||
if rets[i][1]==false then
|
||||
print("Server refused to accept registration request!")
|
||||
self:send(ip,"!identity! REGISTERREFUSED <-|NIL|->",port)
|
||||
return
|
||||
end
|
||||
end
|
||||
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)
|
||||
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
|
||||
print(pass,_pass)
|
||||
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:loginUserIn(user,cid) -- binds the cid to username
|
||||
self:send(ip,"!identity! LOGINGOOD <-|"..bin.ToStr(handle).."|->",port)
|
||||
self.OnUserLoggedIn:Fire(user,cid,ip,port)
|
||||
return
|
||||
else
|
||||
self:send(ip,"!identity! LOGINBAD <-|nil|->",port)
|
||||
return
|
||||
end
|
||||
elseif cmd=="logout" then
|
||||
self:loginUserOut(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)
|
||||
--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:connect(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)
|
||||
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
|
||||
483
net/init.lua
Normal file
483
net/init.lua
Normal file
@ -0,0 +1,483 @@
|
||||
function string.trim(s)
|
||||
local from = s:match"^%s*()"
|
||||
return from > #s and "" or s:match(".*%S", from)
|
||||
end
|
||||
socket=require("socket")
|
||||
net={}
|
||||
net.Version={1,0,0}
|
||||
net.OnServerCreated=multi:newConnection()
|
||||
net.OnClientCreated=multi:newConnection()
|
||||
net.loadedModules={}
|
||||
net.autoInit=true
|
||||
function net:registerModule(mod,version)
|
||||
table.insert(self.loadedModules,mod)
|
||||
net[mod]={}
|
||||
if version then
|
||||
net[mod].Version=version
|
||||
else
|
||||
net[mod].Version={1,0,0}
|
||||
end
|
||||
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
|
||||
-- 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={}
|
||||
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
|
||||
function c:send(ip,data,port,cid)
|
||||
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 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
|
||||
self.OnDataRecieved: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
|
||||
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.OnPongRecieved=multi:newConnection()
|
||||
c.OnClientClosed=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:setpeername(c.ip, port)
|
||||
c.udp:settimeout(0)
|
||||
c.cid="NIL"
|
||||
c.lastPing=0
|
||||
c.Type="udp"
|
||||
c.servercode=servercode
|
||||
c.autoReconnect=true
|
||||
function c:pollPing(n)
|
||||
return not((os.clock()-self.lastPing)<(n or 60))
|
||||
end
|
||||
function c:send(data)
|
||||
self.udp:send("C!"..self.cid..data)
|
||||
end
|
||||
function c:sendRaw(data)
|
||||
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 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
|
||||
self.OnDataRecieved:Fire(self,data)
|
||||
end
|
||||
end
|
||||
end
|
||||
function c:reconnect()
|
||||
if not nonluaServer then
|
||||
self.cid="NIL"
|
||||
c.udp:send("I!")
|
||||
end
|
||||
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:reconnect()
|
||||
else
|
||||
self.link.OnServerNotAvailable:Fire("Connection to server lost: ping timeout!")
|
||||
end
|
||||
end)
|
||||
c.pingEvent.link=c
|
||||
c.OnPingRecieved=multi:newConnection()
|
||||
c.OnDataRecieved=multi:newConnection()
|
||||
c.OnServerNotAvailable=multi:newConnection()
|
||||
c.OnClientReady=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
|
||||
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.sMode=="*l" then
|
||||
handle:send(data.."\n")
|
||||
else
|
||||
handle:send(data)
|
||||
end
|
||||
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: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)
|
||||
updater:OnUpdate(function(self)
|
||||
local data, err = self.client:receive(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:Destroy()
|
||||
end
|
||||
if data then
|
||||
if net.inList(self.Link.bannedIPs,ip) then
|
||||
print("We will ingore data from a banned client!")
|
||||
return
|
||||
end
|
||||
self.Link.OnDataRecieved:Fire(self.Link,data,self.client,self.client,ip)
|
||||
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
|
||||
end
|
||||
end
|
||||
c.OnClientsModulesList=multi:newConnection()
|
||||
c.OnDataRecieved=multi:newConnection()
|
||||
c.OnClientClosed=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=post
|
||||
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"
|
||||
function c:setReceiveMode(mode)
|
||||
self.rMode=mode
|
||||
end
|
||||
function c:setSendMode(mode)
|
||||
self.sMode=mode
|
||||
end
|
||||
function c:send(data)
|
||||
if self.sMode=="*l" then
|
||||
self.tcp:send(data.."\n")
|
||||
else
|
||||
self.tcp:send(data)
|
||||
end
|
||||
end
|
||||
function c:sendRaw(data)
|
||||
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()
|
||||
local data=self.tcp:receive()
|
||||
if data then
|
||||
self.OnDataRecieved:Fire(self,data)
|
||||
end
|
||||
end
|
||||
function c:reconnect()
|
||||
self.ip=assert(socket.dns.toip(host))
|
||||
self.tcp=socket.connect(self.ip,self.port)
|
||||
if not self.tcp then
|
||||
print("Can't connect to the server: no response from server")
|
||||
return
|
||||
end
|
||||
self.tcp:settimeout(0)
|
||||
self.tcp:setoption('tcp-nodelay', true)
|
||||
self.tcp:setoption('keepalive', true)
|
||||
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.OnDataRecieved=multi:newConnection()
|
||||
multi:newLoop(function()
|
||||
c:update()
|
||||
end)
|
||||
net.OnClientCreated:Fire(c)
|
||||
return c
|
||||
end
|
||||
179
net/sft.lua
Normal file
179
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:connect(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)
|
||||
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:connect(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)
|
||||
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
|
||||
1
runclient.bat
Normal file
1
runclient.bat
Normal file
@ -0,0 +1 @@
|
||||
start engine\love.exe .\client
|
||||
1
runserver.bat
Normal file
1
runserver.bat
Normal file
@ -0,0 +1 @@
|
||||
start server.exe
|
||||
BIN
server.exe
Normal file
BIN
server.exe
Normal file
Binary file not shown.
6
server.lua
Normal file
6
server.lua
Normal file
@ -0,0 +1,6 @@
|
||||
package.path="?/init.lua;"..package.path
|
||||
require("Libs/MultiManager") -- allows for multitasking
|
||||
require("net") -- Loads the networking library
|
||||
require("net.chatting") -- loads the networking chatting module
|
||||
server=net:newTCPServer(12345) -- starts a server with the port 12345 on local host
|
||||
multi:mainloop() -- starts the mainloop to keep the server going
|
||||
133
socket.lua
Normal file
133
socket.lua
Normal file
@ -0,0 +1,133 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- LuaSocket helper module
|
||||
-- Author: Diego Nehab
|
||||
-- RCS ID: $Id: socket.lua,v 1.22 2005/11/22 08:33:29 diego Exp $
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Declare module and import dependencies
|
||||
-----------------------------------------------------------------------------
|
||||
local base = _G
|
||||
local string = require("string")
|
||||
local math = require("math")
|
||||
local socket = require("socket.core")
|
||||
module("socket")
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Exported auxiliar functions
|
||||
-----------------------------------------------------------------------------
|
||||
function connect(address, port, laddress, lport)
|
||||
local sock, err = socket.tcp()
|
||||
if not sock then return nil, err end
|
||||
if laddress then
|
||||
local res, err = sock:bind(laddress, lport, -1)
|
||||
if not res then return nil, err end
|
||||
end
|
||||
local res, err = sock:connect(address, port)
|
||||
if not res then return nil, err end
|
||||
return sock
|
||||
end
|
||||
|
||||
function bind(host, port, backlog)
|
||||
local sock, err = socket.tcp()
|
||||
if not sock then return nil, err end
|
||||
sock:setoption("reuseaddr", true)
|
||||
local res, err = sock:bind(host, port)
|
||||
if not res then return nil, err end
|
||||
res, err = sock:listen(backlog)
|
||||
if not res then return nil, err end
|
||||
return sock
|
||||
end
|
||||
|
||||
try = newtry()
|
||||
|
||||
function choose(table)
|
||||
return function(name, opt1, opt2)
|
||||
if base.type(name) ~= "string" then
|
||||
name, opt1, opt2 = "default", name, opt1
|
||||
end
|
||||
local f = table[name or "nil"]
|
||||
if not f then base.error("unknown key (".. base.tostring(name) ..")", 3)
|
||||
else return f(opt1, opt2) end
|
||||
end
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Socket sources and sinks, conforming to LTN12
|
||||
-----------------------------------------------------------------------------
|
||||
-- create namespaces inside LuaSocket namespace
|
||||
sourcet = {}
|
||||
sinkt = {}
|
||||
|
||||
BLOCKSIZE = 2048
|
||||
|
||||
sinkt["close-when-done"] = function(sock)
|
||||
return base.setmetatable({
|
||||
getfd = function() return sock:getfd() end,
|
||||
dirty = function() return sock:dirty() end
|
||||
}, {
|
||||
__call = function(self, chunk, err)
|
||||
if not chunk then
|
||||
sock:close()
|
||||
return 1
|
||||
else return sock:send(chunk) end
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
sinkt["keep-open"] = function(sock)
|
||||
return base.setmetatable({
|
||||
getfd = function() return sock:getfd() end,
|
||||
dirty = function() return sock:dirty() end
|
||||
}, {
|
||||
__call = function(self, chunk, err)
|
||||
if chunk then return sock:send(chunk)
|
||||
else return 1 end
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
sinkt["default"] = sinkt["keep-open"]
|
||||
|
||||
sink = choose(sinkt)
|
||||
|
||||
sourcet["by-length"] = function(sock, length)
|
||||
return base.setmetatable({
|
||||
getfd = function() return sock:getfd() end,
|
||||
dirty = function() return sock:dirty() end
|
||||
}, {
|
||||
__call = function()
|
||||
if length <= 0 then return nil end
|
||||
local size = math.min(socket.BLOCKSIZE, length)
|
||||
local chunk, err = sock:receive(size)
|
||||
if err then return nil, err end
|
||||
length = length - string.len(chunk)
|
||||
return chunk
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
sourcet["until-closed"] = function(sock)
|
||||
local done
|
||||
return base.setmetatable({
|
||||
getfd = function() return sock:getfd() end,
|
||||
dirty = function() return sock:dirty() end
|
||||
}, {
|
||||
__call = function()
|
||||
if done then return nil end
|
||||
local chunk, err, partial = sock:receive(socket.BLOCKSIZE)
|
||||
if not err then return chunk
|
||||
elseif err == "closed" then
|
||||
sock:close()
|
||||
done = 1
|
||||
return partial
|
||||
else return nil, err end
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
sourcet["default"] = sourcet["until-closed"]
|
||||
|
||||
source = choose(sourcet)
|
||||
|
||||
BIN
socket/core.dll
Normal file
BIN
socket/core.dll
Normal file
Binary file not shown.
281
socket/ftp.lua
Normal file
281
socket/ftp.lua
Normal file
@ -0,0 +1,281 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- FTP support for the Lua language
|
||||
-- LuaSocket toolkit.
|
||||
-- Author: Diego Nehab
|
||||
-- RCS ID: $Id: ftp.lua,v 1.45 2007/07/11 19:25:47 diego Exp $
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Declare module and import dependencies
|
||||
-----------------------------------------------------------------------------
|
||||
local base = _G
|
||||
local table = require("table")
|
||||
local string = require("string")
|
||||
local math = require("math")
|
||||
local socket = require("socket")
|
||||
local url = require("socket.url")
|
||||
local tp = require("socket.tp")
|
||||
local ltn12 = require("ltn12")
|
||||
module("socket.ftp")
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Program constants
|
||||
-----------------------------------------------------------------------------
|
||||
-- timeout in seconds before the program gives up on a connection
|
||||
TIMEOUT = 60
|
||||
-- default port for ftp service
|
||||
PORT = 21
|
||||
-- this is the default anonymous password. used when no password is
|
||||
-- provided in url. should be changed to your e-mail.
|
||||
USER = "ftp"
|
||||
PASSWORD = "anonymous@anonymous.org"
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Low level FTP API
|
||||
-----------------------------------------------------------------------------
|
||||
local metat = { __index = {} }
|
||||
|
||||
function open(server, port, create)
|
||||
local tp = socket.try(tp.connect(server, port or PORT, TIMEOUT, create))
|
||||
local f = base.setmetatable({ tp = tp }, metat)
|
||||
-- make sure everything gets closed in an exception
|
||||
f.try = socket.newtry(function() f:close() end)
|
||||
return f
|
||||
end
|
||||
|
||||
function metat.__index:portconnect()
|
||||
self.try(self.server:settimeout(TIMEOUT))
|
||||
self.data = self.try(self.server:accept())
|
||||
self.try(self.data:settimeout(TIMEOUT))
|
||||
end
|
||||
|
||||
function metat.__index:pasvconnect()
|
||||
self.data = self.try(socket.tcp())
|
||||
self.try(self.data:settimeout(TIMEOUT))
|
||||
self.try(self.data:connect(self.pasvt.ip, self.pasvt.port))
|
||||
end
|
||||
|
||||
function metat.__index:login(user, password)
|
||||
self.try(self.tp:command("user", user or USER))
|
||||
local code, reply = self.try(self.tp:check{"2..", 331})
|
||||
if code == 331 then
|
||||
self.try(self.tp:command("pass", password or PASSWORD))
|
||||
self.try(self.tp:check("2.."))
|
||||
end
|
||||
return 1
|
||||
end
|
||||
|
||||
function metat.__index:pasv()
|
||||
self.try(self.tp:command("pasv"))
|
||||
local code, reply = self.try(self.tp:check("2.."))
|
||||
local pattern = "(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)"
|
||||
local a, b, c, d, p1, p2 = socket.skip(2, string.find(reply, pattern))
|
||||
self.try(a and b and c and d and p1 and p2, reply)
|
||||
self.pasvt = {
|
||||
ip = string.format("%d.%d.%d.%d", a, b, c, d),
|
||||
port = p1*256 + p2
|
||||
}
|
||||
if self.server then
|
||||
self.server:close()
|
||||
self.server = nil
|
||||
end
|
||||
return self.pasvt.ip, self.pasvt.port
|
||||
end
|
||||
|
||||
function metat.__index:port(ip, port)
|
||||
self.pasvt = nil
|
||||
if not ip then
|
||||
ip, port = self.try(self.tp:getcontrol():getsockname())
|
||||
self.server = self.try(socket.bind(ip, 0))
|
||||
ip, port = self.try(self.server:getsockname())
|
||||
self.try(self.server:settimeout(TIMEOUT))
|
||||
end
|
||||
local pl = math.mod(port, 256)
|
||||
local ph = (port - pl)/256
|
||||
local arg = string.gsub(string.format("%s,%d,%d", ip, ph, pl), "%.", ",")
|
||||
self.try(self.tp:command("port", arg))
|
||||
self.try(self.tp:check("2.."))
|
||||
return 1
|
||||
end
|
||||
|
||||
function metat.__index:send(sendt)
|
||||
self.try(self.pasvt or self.server, "need port or pasv first")
|
||||
-- if there is a pasvt table, we already sent a PASV command
|
||||
-- we just get the data connection into self.data
|
||||
if self.pasvt then self:pasvconnect() end
|
||||
-- get the transfer argument and command
|
||||
local argument = sendt.argument or
|
||||
url.unescape(string.gsub(sendt.path or "", "^[/\\]", ""))
|
||||
if argument == "" then argument = nil end
|
||||
local command = sendt.command or "stor"
|
||||
-- send the transfer command and check the reply
|
||||
self.try(self.tp:command(command, argument))
|
||||
local code, reply = self.try(self.tp:check{"2..", "1.."})
|
||||
-- if there is not a a pasvt table, then there is a server
|
||||
-- and we already sent a PORT command
|
||||
if not self.pasvt then self:portconnect() end
|
||||
-- get the sink, source and step for the transfer
|
||||
local step = sendt.step or ltn12.pump.step
|
||||
local readt = {self.tp.c}
|
||||
local checkstep = function(src, snk)
|
||||
-- check status in control connection while downloading
|
||||
local readyt = socket.select(readt, nil, 0)
|
||||
if readyt[tp] then code = self.try(self.tp:check("2..")) end
|
||||
return step(src, snk)
|
||||
end
|
||||
local sink = socket.sink("close-when-done", self.data)
|
||||
-- transfer all data and check error
|
||||
self.try(ltn12.pump.all(sendt.source, sink, checkstep))
|
||||
if string.find(code, "1..") then self.try(self.tp:check("2..")) end
|
||||
-- done with data connection
|
||||
self.data:close()
|
||||
-- find out how many bytes were sent
|
||||
local sent = socket.skip(1, self.data:getstats())
|
||||
self.data = nil
|
||||
return sent
|
||||
end
|
||||
|
||||
function metat.__index:receive(recvt)
|
||||
self.try(self.pasvt or self.server, "need port or pasv first")
|
||||
if self.pasvt then self:pasvconnect() end
|
||||
local argument = recvt.argument or
|
||||
url.unescape(string.gsub(recvt.path or "", "^[/\\]", ""))
|
||||
if argument == "" then argument = nil end
|
||||
local command = recvt.command or "retr"
|
||||
self.try(self.tp:command(command, argument))
|
||||
local code = self.try(self.tp:check{"1..", "2.."})
|
||||
if not self.pasvt then self:portconnect() end
|
||||
local source = socket.source("until-closed", self.data)
|
||||
local step = recvt.step or ltn12.pump.step
|
||||
self.try(ltn12.pump.all(source, recvt.sink, step))
|
||||
if string.find(code, "1..") then self.try(self.tp:check("2..")) end
|
||||
self.data:close()
|
||||
self.data = nil
|
||||
return 1
|
||||
end
|
||||
|
||||
function metat.__index:cwd(dir)
|
||||
self.try(self.tp:command("cwd", dir))
|
||||
self.try(self.tp:check(250))
|
||||
return 1
|
||||
end
|
||||
|
||||
function metat.__index:type(type)
|
||||
self.try(self.tp:command("type", type))
|
||||
self.try(self.tp:check(200))
|
||||
return 1
|
||||
end
|
||||
|
||||
function metat.__index:greet()
|
||||
local code = self.try(self.tp:check{"1..", "2.."})
|
||||
if string.find(code, "1..") then self.try(self.tp:check("2..")) end
|
||||
return 1
|
||||
end
|
||||
|
||||
function metat.__index:quit()
|
||||
self.try(self.tp:command("quit"))
|
||||
self.try(self.tp:check("2.."))
|
||||
return 1
|
||||
end
|
||||
|
||||
function metat.__index:close()
|
||||
if self.data then self.data:close() end
|
||||
if self.server then self.server:close() end
|
||||
return self.tp:close()
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- High level FTP API
|
||||
-----------------------------------------------------------------------------
|
||||
local function override(t)
|
||||
if t.url then
|
||||
local u = url.parse(t.url)
|
||||
for i,v in base.pairs(t) do
|
||||
u[i] = v
|
||||
end
|
||||
return u
|
||||
else return t end
|
||||
end
|
||||
|
||||
local function tput(putt)
|
||||
putt = override(putt)
|
||||
socket.try(putt.host, "missing hostname")
|
||||
local f = open(putt.host, putt.port, putt.create)
|
||||
f:greet()
|
||||
f:login(putt.user, putt.password)
|
||||
if putt.type then f:type(putt.type) end
|
||||
f:pasv()
|
||||
local sent = f:send(putt)
|
||||
f:quit()
|
||||
f:close()
|
||||
return sent
|
||||
end
|
||||
|
||||
local default = {
|
||||
path = "/",
|
||||
scheme = "ftp"
|
||||
}
|
||||
|
||||
local function parse(u)
|
||||
local t = socket.try(url.parse(u, default))
|
||||
socket.try(t.scheme == "ftp", "wrong scheme '" .. t.scheme .. "'")
|
||||
socket.try(t.host, "missing hostname")
|
||||
local pat = "^type=(.)$"
|
||||
if t.params then
|
||||
t.type = socket.skip(2, string.find(t.params, pat))
|
||||
socket.try(t.type == "a" or t.type == "i",
|
||||
"invalid type '" .. t.type .. "'")
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
local function sput(u, body)
|
||||
local putt = parse(u)
|
||||
putt.source = ltn12.source.string(body)
|
||||
return tput(putt)
|
||||
end
|
||||
|
||||
put = socket.protect(function(putt, body)
|
||||
if base.type(putt) == "string" then return sput(putt, body)
|
||||
else return tput(putt) end
|
||||
end)
|
||||
|
||||
local function tget(gett)
|
||||
gett = override(gett)
|
||||
socket.try(gett.host, "missing hostname")
|
||||
local f = open(gett.host, gett.port, gett.create)
|
||||
f:greet()
|
||||
f:login(gett.user, gett.password)
|
||||
if gett.type then f:type(gett.type) end
|
||||
f:pasv()
|
||||
f:receive(gett)
|
||||
f:quit()
|
||||
return f:close()
|
||||
end
|
||||
|
||||
local function sget(u)
|
||||
local gett = parse(u)
|
||||
local t = {}
|
||||
gett.sink = ltn12.sink.table(t)
|
||||
tget(gett)
|
||||
return table.concat(t)
|
||||
end
|
||||
|
||||
command = socket.protect(function(cmdt)
|
||||
cmdt = override(cmdt)
|
||||
socket.try(cmdt.host, "missing hostname")
|
||||
socket.try(cmdt.command, "missing command")
|
||||
local f = open(cmdt.host, cmdt.port, cmdt.create)
|
||||
f:greet()
|
||||
f:login(cmdt.user, cmdt.password)
|
||||
f.try(f.tp:command(cmdt.command, cmdt.argument))
|
||||
if cmdt.check then f.try(f.tp:check(cmdt.check)) end
|
||||
f:quit()
|
||||
return f:close()
|
||||
end)
|
||||
|
||||
get = socket.protect(function(gett)
|
||||
if base.type(gett) == "string" then return sget(gett)
|
||||
else return tget(gett) end
|
||||
end)
|
||||
|
||||
104
socket/headers.lua
Normal file
104
socket/headers.lua
Normal file
@ -0,0 +1,104 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- Canonic header field capitalization
|
||||
-- LuaSocket toolkit.
|
||||
-- Author: Diego Nehab
|
||||
-----------------------------------------------------------------------------
|
||||
local socket = require("socket")
|
||||
socket.headers = {}
|
||||
local _M = socket.headers
|
||||
|
||||
_M.canonic = {
|
||||
["accept"] = "Accept",
|
||||
["accept-charset"] = "Accept-Charset",
|
||||
["accept-encoding"] = "Accept-Encoding",
|
||||
["accept-language"] = "Accept-Language",
|
||||
["accept-ranges"] = "Accept-Ranges",
|
||||
["action"] = "Action",
|
||||
["alternate-recipient"] = "Alternate-Recipient",
|
||||
["age"] = "Age",
|
||||
["allow"] = "Allow",
|
||||
["arrival-date"] = "Arrival-Date",
|
||||
["authorization"] = "Authorization",
|
||||
["bcc"] = "Bcc",
|
||||
["cache-control"] = "Cache-Control",
|
||||
["cc"] = "Cc",
|
||||
["comments"] = "Comments",
|
||||
["connection"] = "Connection",
|
||||
["content-description"] = "Content-Description",
|
||||
["content-disposition"] = "Content-Disposition",
|
||||
["content-encoding"] = "Content-Encoding",
|
||||
["content-id"] = "Content-ID",
|
||||
["content-language"] = "Content-Language",
|
||||
["content-length"] = "Content-Length",
|
||||
["content-location"] = "Content-Location",
|
||||
["content-md5"] = "Content-MD5",
|
||||
["content-range"] = "Content-Range",
|
||||
["content-transfer-encoding"] = "Content-Transfer-Encoding",
|
||||
["content-type"] = "Content-Type",
|
||||
["cookie"] = "Cookie",
|
||||
["date"] = "Date",
|
||||
["diagnostic-code"] = "Diagnostic-Code",
|
||||
["dsn-gateway"] = "DSN-Gateway",
|
||||
["etag"] = "ETag",
|
||||
["expect"] = "Expect",
|
||||
["expires"] = "Expires",
|
||||
["final-log-id"] = "Final-Log-ID",
|
||||
["final-recipient"] = "Final-Recipient",
|
||||
["from"] = "From",
|
||||
["host"] = "Host",
|
||||
["if-match"] = "If-Match",
|
||||
["if-modified-since"] = "If-Modified-Since",
|
||||
["if-none-match"] = "If-None-Match",
|
||||
["if-range"] = "If-Range",
|
||||
["if-unmodified-since"] = "If-Unmodified-Since",
|
||||
["in-reply-to"] = "In-Reply-To",
|
||||
["keywords"] = "Keywords",
|
||||
["last-attempt-date"] = "Last-Attempt-Date",
|
||||
["last-modified"] = "Last-Modified",
|
||||
["location"] = "Location",
|
||||
["max-forwards"] = "Max-Forwards",
|
||||
["message-id"] = "Message-ID",
|
||||
["mime-version"] = "MIME-Version",
|
||||
["original-envelope-id"] = "Original-Envelope-ID",
|
||||
["original-recipient"] = "Original-Recipient",
|
||||
["pragma"] = "Pragma",
|
||||
["proxy-authenticate"] = "Proxy-Authenticate",
|
||||
["proxy-authorization"] = "Proxy-Authorization",
|
||||
["range"] = "Range",
|
||||
["received"] = "Received",
|
||||
["received-from-mta"] = "Received-From-MTA",
|
||||
["references"] = "References",
|
||||
["referer"] = "Referer",
|
||||
["remote-mta"] = "Remote-MTA",
|
||||
["reply-to"] = "Reply-To",
|
||||
["reporting-mta"] = "Reporting-MTA",
|
||||
["resent-bcc"] = "Resent-Bcc",
|
||||
["resent-cc"] = "Resent-Cc",
|
||||
["resent-date"] = "Resent-Date",
|
||||
["resent-from"] = "Resent-From",
|
||||
["resent-message-id"] = "Resent-Message-ID",
|
||||
["resent-reply-to"] = "Resent-Reply-To",
|
||||
["resent-sender"] = "Resent-Sender",
|
||||
["resent-to"] = "Resent-To",
|
||||
["retry-after"] = "Retry-After",
|
||||
["return-path"] = "Return-Path",
|
||||
["sender"] = "Sender",
|
||||
["server"] = "Server",
|
||||
["smtp-remote-recipient"] = "SMTP-Remote-Recipient",
|
||||
["status"] = "Status",
|
||||
["subject"] = "Subject",
|
||||
["te"] = "TE",
|
||||
["to"] = "To",
|
||||
["trailer"] = "Trailer",
|
||||
["transfer-encoding"] = "Transfer-Encoding",
|
||||
["upgrade"] = "Upgrade",
|
||||
["user-agent"] = "User-Agent",
|
||||
["vary"] = "Vary",
|
||||
["via"] = "Via",
|
||||
["warning"] = "Warning",
|
||||
["will-retry-until"] = "Will-Retry-Until",
|
||||
["www-authenticate"] = "WWW-Authenticate",
|
||||
["x-mailer"] = "X-Mailer",
|
||||
}
|
||||
|
||||
return _M
|
||||
350
socket/http.lua
Normal file
350
socket/http.lua
Normal file
@ -0,0 +1,350 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- HTTP/1.1 client support for the Lua language.
|
||||
-- LuaSocket toolkit.
|
||||
-- Author: Diego Nehab
|
||||
-- RCS ID: $Id: http.lua,v 1.70 2007/03/12 04:08:40 diego Exp $
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Declare module and import dependencies
|
||||
-------------------------------------------------------------------------------
|
||||
local socket = require("socket")
|
||||
local url = require("socket.url")
|
||||
local ltn12 = require("ltn12")
|
||||
local mime = require("mime")
|
||||
local string = require("string")
|
||||
local base = _G
|
||||
local table = require("table")
|
||||
module("socket.http")
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Program constants
|
||||
-----------------------------------------------------------------------------
|
||||
-- connection timeout in seconds
|
||||
TIMEOUT = 60
|
||||
-- default port for document retrieval
|
||||
PORT = 80
|
||||
-- user agent field sent in request
|
||||
USERAGENT = socket._VERSION
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Reads MIME headers from a connection, unfolding where needed
|
||||
-----------------------------------------------------------------------------
|
||||
local function receiveheaders(sock, headers)
|
||||
local line, name, value, err
|
||||
headers = headers or {}
|
||||
-- get first line
|
||||
line, err = sock:receive()
|
||||
if err then return nil, err end
|
||||
-- headers go until a blank line is found
|
||||
while line ~= "" do
|
||||
-- get field-name and value
|
||||
name, value = socket.skip(2, string.find(line, "^(.-):%s*(.*)"))
|
||||
if not (name and value) then return nil, "malformed reponse headers" end
|
||||
name = string.lower(name)
|
||||
-- get next line (value might be folded)
|
||||
line, err = sock:receive()
|
||||
if err then return nil, err end
|
||||
-- unfold any folded values
|
||||
while string.find(line, "^%s") do
|
||||
value = value .. line
|
||||
line = sock:receive()
|
||||
if err then return nil, err end
|
||||
end
|
||||
-- save pair in table
|
||||
if headers[name] then headers[name] = headers[name] .. ", " .. value
|
||||
else headers[name] = value end
|
||||
end
|
||||
return headers
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Extra sources and sinks
|
||||
-----------------------------------------------------------------------------
|
||||
socket.sourcet["http-chunked"] = function(sock, headers)
|
||||
return base.setmetatable({
|
||||
getfd = function() return sock:getfd() end,
|
||||
dirty = function() return sock:dirty() end
|
||||
}, {
|
||||
__call = function()
|
||||
-- get chunk size, skip extention
|
||||
local line, err = sock:receive()
|
||||
if err then return nil, err end
|
||||
local size = base.tonumber(string.gsub(line, ";.*", ""), 16)
|
||||
if not size then return nil, "invalid chunk size" end
|
||||
-- was it the last chunk?
|
||||
if size > 0 then
|
||||
-- if not, get chunk and skip terminating CRLF
|
||||
local chunk, err, part = sock:receive(size)
|
||||
if chunk then sock:receive() end
|
||||
return chunk, err
|
||||
else
|
||||
-- if it was, read trailers into headers table
|
||||
headers, err = receiveheaders(sock, headers)
|
||||
if not headers then return nil, err end
|
||||
end
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
socket.sinkt["http-chunked"] = function(sock)
|
||||
return base.setmetatable({
|
||||
getfd = function() return sock:getfd() end,
|
||||
dirty = function() return sock:dirty() end
|
||||
}, {
|
||||
__call = function(self, chunk, err)
|
||||
if not chunk then return sock:send("0\r\n\r\n") end
|
||||
local size = string.format("%X\r\n", string.len(chunk))
|
||||
return sock:send(size .. chunk .. "\r\n")
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Low level HTTP API
|
||||
-----------------------------------------------------------------------------
|
||||
local metat = { __index = {} }
|
||||
|
||||
function open(host, port, create)
|
||||
-- create socket with user connect function, or with default
|
||||
local c = socket.try((create or socket.tcp)())
|
||||
local h = base.setmetatable({ c = c }, metat)
|
||||
-- create finalized try
|
||||
h.try = socket.newtry(function() h:close() end)
|
||||
-- set timeout before connecting
|
||||
h.try(c:settimeout(TIMEOUT))
|
||||
h.try(c:connect(host, port or PORT))
|
||||
-- here everything worked
|
||||
return h
|
||||
end
|
||||
|
||||
function metat.__index:sendrequestline(method, uri)
|
||||
local reqline = string.format("%s %s HTTP/1.1\r\n", method or "GET", uri)
|
||||
return self.try(self.c:send(reqline))
|
||||
end
|
||||
|
||||
function metat.__index:sendheaders(headers)
|
||||
local h = "\r\n"
|
||||
for i, v in base.pairs(headers) do
|
||||
h = i .. ": " .. v .. "\r\n" .. h
|
||||
end
|
||||
self.try(self.c:send(h))
|
||||
return 1
|
||||
end
|
||||
|
||||
function metat.__index:sendbody(headers, source, step)
|
||||
source = source or ltn12.source.empty()
|
||||
step = step or ltn12.pump.step
|
||||
-- if we don't know the size in advance, send chunked and hope for the best
|
||||
local mode = "http-chunked"
|
||||
if headers["content-length"] then mode = "keep-open" end
|
||||
return self.try(ltn12.pump.all(source, socket.sink(mode, self.c), step))
|
||||
end
|
||||
|
||||
function metat.__index:receivestatusline()
|
||||
local status = self.try(self.c:receive(5))
|
||||
-- identify HTTP/0.9 responses, which do not contain a status line
|
||||
-- this is just a heuristic, but is what the RFC recommends
|
||||
if status ~= "HTTP/" then return nil, status end
|
||||
-- otherwise proceed reading a status line
|
||||
status = self.try(self.c:receive("*l", status))
|
||||
local code = socket.skip(2, string.find(status, "HTTP/%d*%.%d* (%d%d%d)"))
|
||||
return self.try(base.tonumber(code), status)
|
||||
end
|
||||
|
||||
function metat.__index:receiveheaders()
|
||||
return self.try(receiveheaders(self.c))
|
||||
end
|
||||
|
||||
function metat.__index:receivebody(headers, sink, step)
|
||||
sink = sink or ltn12.sink.null()
|
||||
step = step or ltn12.pump.step
|
||||
local length = base.tonumber(headers["content-length"])
|
||||
local t = headers["transfer-encoding"] -- shortcut
|
||||
local mode = "default" -- connection close
|
||||
if t and t ~= "identity" then mode = "http-chunked"
|
||||
elseif base.tonumber(headers["content-length"]) then mode = "by-length" end
|
||||
return self.try(ltn12.pump.all(socket.source(mode, self.c, length),
|
||||
sink, step))
|
||||
end
|
||||
|
||||
function metat.__index:receive09body(status, sink, step)
|
||||
local source = ltn12.source.rewind(socket.source("until-closed", self.c))
|
||||
source(status)
|
||||
return self.try(ltn12.pump.all(source, sink, step))
|
||||
end
|
||||
|
||||
function metat.__index:close()
|
||||
return self.c:close()
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- High level HTTP API
|
||||
-----------------------------------------------------------------------------
|
||||
local function adjusturi(reqt)
|
||||
local u = reqt
|
||||
-- if there is a proxy, we need the full url. otherwise, just a part.
|
||||
if not reqt.proxy and not PROXY then
|
||||
u = {
|
||||
path = socket.try(reqt.path, "invalid path 'nil'"),
|
||||
params = reqt.params,
|
||||
query = reqt.query,
|
||||
fragment = reqt.fragment
|
||||
}
|
||||
end
|
||||
return url.build(u)
|
||||
end
|
||||
|
||||
local function adjustproxy(reqt)
|
||||
local proxy = reqt.proxy or PROXY
|
||||
if proxy then
|
||||
proxy = url.parse(proxy)
|
||||
return proxy.host, proxy.port or 3128
|
||||
else
|
||||
return reqt.host, reqt.port
|
||||
end
|
||||
end
|
||||
|
||||
local function adjustheaders(reqt)
|
||||
-- default headers
|
||||
local lower = {
|
||||
["user-agent"] = USERAGENT,
|
||||
["host"] = reqt.host,
|
||||
["connection"] = "close, TE",
|
||||
["te"] = "trailers"
|
||||
}
|
||||
-- if we have authentication information, pass it along
|
||||
if reqt.user and reqt.password then
|
||||
lower["authorization"] =
|
||||
"Basic " .. (mime.b64(reqt.user .. ":" .. reqt.password))
|
||||
end
|
||||
-- override with user headers
|
||||
for i,v in base.pairs(reqt.headers or lower) do
|
||||
lower[string.lower(i)] = v
|
||||
end
|
||||
return lower
|
||||
end
|
||||
|
||||
-- default url parts
|
||||
local default = {
|
||||
host = "",
|
||||
port = PORT,
|
||||
path ="/",
|
||||
scheme = "http"
|
||||
}
|
||||
|
||||
local function adjustrequest(reqt)
|
||||
-- parse url if provided
|
||||
local nreqt = reqt.url and url.parse(reqt.url, default) or {}
|
||||
-- explicit components override url
|
||||
for i,v in base.pairs(reqt) do nreqt[i] = v end
|
||||
if nreqt.port == "" then nreqt.port = 80 end
|
||||
socket.try(nreqt.host and nreqt.host ~= "",
|
||||
"invalid host '" .. base.tostring(nreqt.host) .. "'")
|
||||
-- compute uri if user hasn't overriden
|
||||
nreqt.uri = reqt.uri or adjusturi(nreqt)
|
||||
-- ajust host and port if there is a proxy
|
||||
nreqt.host, nreqt.port = adjustproxy(nreqt)
|
||||
-- adjust headers in request
|
||||
nreqt.headers = adjustheaders(nreqt)
|
||||
return nreqt
|
||||
end
|
||||
|
||||
local function shouldredirect(reqt, code, headers)
|
||||
return headers.location and
|
||||
string.gsub(headers.location, "%s", "") ~= "" and
|
||||
(reqt.redirect ~= false) and
|
||||
(code == 301 or code == 302) and
|
||||
(not reqt.method or reqt.method == "GET" or reqt.method == "HEAD")
|
||||
and (not reqt.nredirects or reqt.nredirects < 5)
|
||||
end
|
||||
|
||||
local function shouldreceivebody(reqt, code)
|
||||
if reqt.method == "HEAD" then return nil end
|
||||
if code == 204 or code == 304 then return nil end
|
||||
if code >= 100 and code < 200 then return nil end
|
||||
return 1
|
||||
end
|
||||
|
||||
-- forward declarations
|
||||
local trequest, tredirect
|
||||
|
||||
function tredirect(reqt, location)
|
||||
local result, code, headers, status = trequest {
|
||||
-- the RFC says the redirect URL has to be absolute, but some
|
||||
-- servers do not respect that
|
||||
url = url.absolute(reqt.url, location),
|
||||
source = reqt.source,
|
||||
sink = reqt.sink,
|
||||
headers = reqt.headers,
|
||||
proxy = reqt.proxy,
|
||||
nredirects = (reqt.nredirects or 0) + 1,
|
||||
create = reqt.create
|
||||
}
|
||||
-- pass location header back as a hint we redirected
|
||||
headers = headers or {}
|
||||
headers.location = headers.location or location
|
||||
return result, code, headers, status
|
||||
end
|
||||
|
||||
function trequest(reqt)
|
||||
-- we loop until we get what we want, or
|
||||
-- until we are sure there is no way to get it
|
||||
local nreqt = adjustrequest(reqt)
|
||||
local h = open(nreqt.host, nreqt.port, nreqt.create)
|
||||
-- send request line and headers
|
||||
h:sendrequestline(nreqt.method, nreqt.uri)
|
||||
h:sendheaders(nreqt.headers)
|
||||
-- if there is a body, send it
|
||||
if nreqt.source then
|
||||
h:sendbody(nreqt.headers, nreqt.source, nreqt.step)
|
||||
end
|
||||
local code, status = h:receivestatusline()
|
||||
-- if it is an HTTP/0.9 server, simply get the body and we are done
|
||||
if not code then
|
||||
h:receive09body(status, nreqt.sink, nreqt.step)
|
||||
return 1, 200
|
||||
end
|
||||
local headers
|
||||
-- ignore any 100-continue messages
|
||||
while code == 100 do
|
||||
headers = h:receiveheaders()
|
||||
code, status = h:receivestatusline()
|
||||
end
|
||||
headers = h:receiveheaders()
|
||||
-- at this point we should have a honest reply from the server
|
||||
-- we can't redirect if we already used the source, so we report the error
|
||||
if shouldredirect(nreqt, code, headers) and not nreqt.source then
|
||||
h:close()
|
||||
return tredirect(reqt, headers.location)
|
||||
end
|
||||
-- here we are finally done
|
||||
if shouldreceivebody(nreqt, code) then
|
||||
h:receivebody(headers, nreqt.sink, nreqt.step)
|
||||
end
|
||||
h:close()
|
||||
return 1, code, headers, status
|
||||
end
|
||||
|
||||
local function srequest(u, b)
|
||||
local t = {}
|
||||
local reqt = {
|
||||
url = u,
|
||||
sink = ltn12.sink.table(t)
|
||||
}
|
||||
if b then
|
||||
reqt.source = ltn12.source.string(b)
|
||||
reqt.headers = {
|
||||
["content-length"] = string.len(b),
|
||||
["content-type"] = "application/x-www-form-urlencoded"
|
||||
}
|
||||
reqt.method = "POST"
|
||||
end
|
||||
local code, headers, status = socket.skip(1, trequest(reqt))
|
||||
return table.concat(t), code, headers, status
|
||||
end
|
||||
|
||||
request = socket.protect(function(reqt, body)
|
||||
if base.type(reqt) == "string" then return srequest(reqt, body)
|
||||
else return trequest(reqt) end
|
||||
end)
|
||||
251
socket/smtp.lua
Normal file
251
socket/smtp.lua
Normal file
@ -0,0 +1,251 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- SMTP client support for the Lua language.
|
||||
-- LuaSocket toolkit.
|
||||
-- Author: Diego Nehab
|
||||
-- RCS ID: $Id: smtp.lua,v 1.46 2007/03/12 04:08:40 diego Exp $
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Declare module and import dependencies
|
||||
-----------------------------------------------------------------------------
|
||||
local base = _G
|
||||
local coroutine = require("coroutine")
|
||||
local string = require("string")
|
||||
local math = require("math")
|
||||
local os = require("os")
|
||||
local socket = require("socket")
|
||||
local tp = require("socket.tp")
|
||||
local ltn12 = require("ltn12")
|
||||
local mime = require("mime")
|
||||
module("socket.smtp")
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Program constants
|
||||
-----------------------------------------------------------------------------
|
||||
-- timeout for connection
|
||||
TIMEOUT = 60
|
||||
-- default server used to send e-mails
|
||||
SERVER = "localhost"
|
||||
-- default port
|
||||
PORT = 25
|
||||
-- domain used in HELO command and default sendmail
|
||||
-- If we are under a CGI, try to get from environment
|
||||
DOMAIN = os.getenv("SERVER_NAME") or "localhost"
|
||||
-- default time zone (means we don't know)
|
||||
ZONE = "-0000"
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
-- Low level SMTP API
|
||||
-----------------------------------------------------------------------------
|
||||
local metat = { __index = {} }
|
||||
|
||||
function metat.__index:greet(domain)
|
||||
self.try(self.tp:check("2.."))
|
||||
self.try(self.tp:command("EHLO", domain or DOMAIN))
|
||||
return socket.skip(1, self.try(self.tp:check("2..")))
|
||||
end
|
||||
|
||||
function metat.__index:mail(from)
|
||||
self.try(self.tp:command("MAIL", "FROM:" .. from))
|
||||
return self.try(self.tp:check("2.."))
|
||||
end
|
||||
|
||||
function metat.__index:rcpt(to)
|
||||
self.try(self.tp:command("RCPT", "TO:" .. to))
|
||||
return self.try(self.tp:check("2.."))
|
||||
end
|
||||
|
||||
function metat.__index:data(src, step)
|
||||
self.try(self.tp:command("DATA"))
|
||||
self.try(self.tp:check("3.."))
|
||||
self.try(self.tp:source(src, step))
|
||||
self.try(self.tp:send("\r\n.\r\n"))
|
||||
return self.try(self.tp:check("2.."))
|
||||
end
|
||||
|
||||
function metat.__index:quit()
|
||||
self.try(self.tp:command("QUIT"))
|
||||
return self.try(self.tp:check("2.."))
|
||||
end
|
||||
|
||||
function metat.__index:close()
|
||||
return self.tp:close()
|
||||
end
|
||||
|
||||
function metat.__index:login(user, password)
|
||||
self.try(self.tp:command("AUTH", "LOGIN"))
|
||||
self.try(self.tp:check("3.."))
|
||||
self.try(self.tp:command(mime.b64(user)))
|
||||
self.try(self.tp:check("3.."))
|
||||
self.try(self.tp:command(mime.b64(password)))
|
||||
return self.try(self.tp:check("2.."))
|
||||
end
|
||||
|
||||
function metat.__index:plain(user, password)
|
||||
local auth = "PLAIN " .. mime.b64("\0" .. user .. "\0" .. password)
|
||||
self.try(self.tp:command("AUTH", auth))
|
||||
return self.try(self.tp:check("2.."))
|
||||
end
|
||||
|
||||
function metat.__index:auth(user, password, ext)
|
||||
if not user or not password then return 1 end
|
||||
if string.find(ext, "AUTH[^\n]+LOGIN") then
|
||||
return self:login(user, password)
|
||||
elseif string.find(ext, "AUTH[^\n]+PLAIN") then
|
||||
return self:plain(user, password)
|
||||
else
|
||||
self.try(nil, "authentication not supported")
|
||||
end
|
||||
end
|
||||
|
||||
-- send message or throw an exception
|
||||
function metat.__index:send(mailt)
|
||||
self:mail(mailt.from)
|
||||
if base.type(mailt.rcpt) == "table" then
|
||||
for i,v in base.ipairs(mailt.rcpt) do
|
||||
self:rcpt(v)
|
||||
end
|
||||
else
|
||||
self:rcpt(mailt.rcpt)
|
||||
end
|
||||
self:data(ltn12.source.chain(mailt.source, mime.stuff()), mailt.step)
|
||||
end
|
||||
|
||||
function open(server, port, create)
|
||||
local tp = socket.try(tp.connect(server or SERVER, port or PORT,
|
||||
TIMEOUT, create))
|
||||
local s = base.setmetatable({tp = tp}, metat)
|
||||
-- make sure tp is closed if we get an exception
|
||||
s.try = socket.newtry(function()
|
||||
s:close()
|
||||
end)
|
||||
return s
|
||||
end
|
||||
|
||||
-- convert headers to lowercase
|
||||
local function lower_headers(headers)
|
||||
local lower = {}
|
||||
for i,v in base.pairs(headers or lower) do
|
||||
lower[string.lower(i)] = v
|
||||
end
|
||||
return lower
|
||||
end
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
-- Multipart message source
|
||||
-----------------------------------------------------------------------------
|
||||
-- returns a hopefully unique mime boundary
|
||||
local seqno = 0
|
||||
local function newboundary()
|
||||
seqno = seqno + 1
|
||||
return string.format('%s%05d==%05u', os.date('%d%m%Y%H%M%S'),
|
||||
math.random(0, 99999), seqno)
|
||||
end
|
||||
|
||||
-- send_message forward declaration
|
||||
local send_message
|
||||
|
||||
-- yield the headers all at once, it's faster
|
||||
local function send_headers(headers)
|
||||
local h = "\r\n"
|
||||
for i,v in base.pairs(headers) do
|
||||
h = i .. ': ' .. v .. "\r\n" .. h
|
||||
end
|
||||
coroutine.yield(h)
|
||||
end
|
||||
|
||||
-- yield multipart message body from a multipart message table
|
||||
local function send_multipart(mesgt)
|
||||
-- make sure we have our boundary and send headers
|
||||
local bd = newboundary()
|
||||
local headers = lower_headers(mesgt.headers or {})
|
||||
headers['content-type'] = headers['content-type'] or 'multipart/mixed'
|
||||
headers['content-type'] = headers['content-type'] ..
|
||||
'; boundary="' .. bd .. '"'
|
||||
send_headers(headers)
|
||||
-- send preamble
|
||||
if mesgt.body.preamble then
|
||||
coroutine.yield(mesgt.body.preamble)
|
||||
coroutine.yield("\r\n")
|
||||
end
|
||||
-- send each part separated by a boundary
|
||||
for i, m in base.ipairs(mesgt.body) do
|
||||
coroutine.yield("\r\n--" .. bd .. "\r\n")
|
||||
send_message(m)
|
||||
end
|
||||
-- send last boundary
|
||||
coroutine.yield("\r\n--" .. bd .. "--\r\n\r\n")
|
||||
-- send epilogue
|
||||
if mesgt.body.epilogue then
|
||||
coroutine.yield(mesgt.body.epilogue)
|
||||
coroutine.yield("\r\n")
|
||||
end
|
||||
end
|
||||
|
||||
-- yield message body from a source
|
||||
local function send_source(mesgt)
|
||||
-- make sure we have a content-type
|
||||
local headers = lower_headers(mesgt.headers or {})
|
||||
headers['content-type'] = headers['content-type'] or
|
||||
'text/plain; charset="iso-8859-1"'
|
||||
send_headers(headers)
|
||||
-- send body from source
|
||||
while true do
|
||||
local chunk, err = mesgt.body()
|
||||
if err then coroutine.yield(nil, err)
|
||||
elseif chunk then coroutine.yield(chunk)
|
||||
else break end
|
||||
end
|
||||
end
|
||||
|
||||
-- yield message body from a string
|
||||
local function send_string(mesgt)
|
||||
-- make sure we have a content-type
|
||||
local headers = lower_headers(mesgt.headers or {})
|
||||
headers['content-type'] = headers['content-type'] or
|
||||
'text/plain; charset="iso-8859-1"'
|
||||
send_headers(headers)
|
||||
-- send body from string
|
||||
coroutine.yield(mesgt.body)
|
||||
end
|
||||
|
||||
-- message source
|
||||
function send_message(mesgt)
|
||||
if base.type(mesgt.body) == "table" then send_multipart(mesgt)
|
||||
elseif base.type(mesgt.body) == "function" then send_source(mesgt)
|
||||
else send_string(mesgt) end
|
||||
end
|
||||
|
||||
-- set defaul headers
|
||||
local function adjust_headers(mesgt)
|
||||
local lower = lower_headers(mesgt.headers)
|
||||
lower["date"] = lower["date"] or
|
||||
os.date("!%a, %d %b %Y %H:%M:%S ") .. (mesgt.zone or ZONE)
|
||||
lower["x-mailer"] = lower["x-mailer"] or socket._VERSION
|
||||
-- this can't be overriden
|
||||
lower["mime-version"] = "1.0"
|
||||
return lower
|
||||
end
|
||||
|
||||
function message(mesgt)
|
||||
mesgt.headers = adjust_headers(mesgt)
|
||||
-- create and return message source
|
||||
local co = coroutine.create(function() send_message(mesgt) end)
|
||||
return function()
|
||||
local ret, a, b = coroutine.resume(co)
|
||||
if ret then return a, b
|
||||
else return nil, a end
|
||||
end
|
||||
end
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
-- High level SMTP API
|
||||
-----------------------------------------------------------------------------
|
||||
send = socket.protect(function(mailt)
|
||||
local s = open(mailt.server, mailt.port, mailt.create)
|
||||
local ext = s:greet(mailt.domain)
|
||||
s:auth(mailt.user, mailt.password, ext)
|
||||
s:send(mailt)
|
||||
s:quit()
|
||||
return s:close()
|
||||
end)
|
||||
123
socket/tp.lua
Normal file
123
socket/tp.lua
Normal file
@ -0,0 +1,123 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- Unified SMTP/FTP subsystem
|
||||
-- LuaSocket toolkit.
|
||||
-- Author: Diego Nehab
|
||||
-- RCS ID: $Id: tp.lua,v 1.22 2006/03/14 09:04:15 diego Exp $
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Declare module and import dependencies
|
||||
-----------------------------------------------------------------------------
|
||||
local base = _G
|
||||
local string = require("string")
|
||||
local socket = require("socket")
|
||||
local ltn12 = require("ltn12")
|
||||
module("socket.tp")
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Program constants
|
||||
-----------------------------------------------------------------------------
|
||||
TIMEOUT = 60
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Implementation
|
||||
-----------------------------------------------------------------------------
|
||||
-- gets server reply (works for SMTP and FTP)
|
||||
local function get_reply(c)
|
||||
local code, current, sep
|
||||
local line, err = c:receive()
|
||||
local reply = line
|
||||
if err then return nil, err end
|
||||
code, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)"))
|
||||
if not code then return nil, "invalid server reply" end
|
||||
if sep == "-" then -- reply is multiline
|
||||
repeat
|
||||
line, err = c:receive()
|
||||
if err then return nil, err end
|
||||
current, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)"))
|
||||
reply = reply .. "\n" .. line
|
||||
-- reply ends with same code
|
||||
until code == current and sep == " "
|
||||
end
|
||||
return code, reply
|
||||
end
|
||||
|
||||
-- metatable for sock object
|
||||
local metat = { __index = {} }
|
||||
|
||||
function metat.__index:check(ok)
|
||||
local code, reply = get_reply(self.c)
|
||||
if not code then return nil, reply end
|
||||
if base.type(ok) ~= "function" then
|
||||
if base.type(ok) == "table" then
|
||||
for i, v in base.ipairs(ok) do
|
||||
if string.find(code, v) then
|
||||
return base.tonumber(code), reply
|
||||
end
|
||||
end
|
||||
return nil, reply
|
||||
else
|
||||
if string.find(code, ok) then return base.tonumber(code), reply
|
||||
else return nil, reply end
|
||||
end
|
||||
else return ok(base.tonumber(code), reply) end
|
||||
end
|
||||
|
||||
function metat.__index:command(cmd, arg)
|
||||
if arg then
|
||||
return self.c:send(cmd .. " " .. arg.. "\r\n")
|
||||
else
|
||||
return self.c:send(cmd .. "\r\n")
|
||||
end
|
||||
end
|
||||
|
||||
function metat.__index:sink(snk, pat)
|
||||
local chunk, err = c:receive(pat)
|
||||
return snk(chunk, err)
|
||||
end
|
||||
|
||||
function metat.__index:send(data)
|
||||
return self.c:send(data)
|
||||
end
|
||||
|
||||
function metat.__index:receive(pat)
|
||||
return self.c:receive(pat)
|
||||
end
|
||||
|
||||
function metat.__index:getfd()
|
||||
return self.c:getfd()
|
||||
end
|
||||
|
||||
function metat.__index:dirty()
|
||||
return self.c:dirty()
|
||||
end
|
||||
|
||||
function metat.__index:getcontrol()
|
||||
return self.c
|
||||
end
|
||||
|
||||
function metat.__index:source(source, step)
|
||||
local sink = socket.sink("keep-open", self.c)
|
||||
local ret, err = ltn12.pump.all(source, sink, step or ltn12.pump.step)
|
||||
return ret, err
|
||||
end
|
||||
|
||||
-- closes the underlying c
|
||||
function metat.__index:close()
|
||||
self.c:close()
|
||||
return 1
|
||||
end
|
||||
|
||||
-- connect with server and return c object
|
||||
function connect(host, port, timeout, create)
|
||||
local c, e = (create or socket.tcp)()
|
||||
if not c then return nil, e end
|
||||
c:settimeout(timeout or TIMEOUT)
|
||||
local r, e = c:connect(host, port)
|
||||
if not r then
|
||||
c:close()
|
||||
return nil, e
|
||||
end
|
||||
return base.setmetatable({c = c}, metat)
|
||||
end
|
||||
|
||||
297
socket/url.lua
Normal file
297
socket/url.lua
Normal file
@ -0,0 +1,297 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- URI parsing, composition and relative URL resolution
|
||||
-- LuaSocket toolkit.
|
||||
-- Author: Diego Nehab
|
||||
-- RCS ID: $Id: url.lua,v 1.38 2006/04/03 04:45:42 diego Exp $
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Declare module
|
||||
-----------------------------------------------------------------------------
|
||||
local string = require("string")
|
||||
local base = _G
|
||||
local table = require("table")
|
||||
module("socket.url")
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Module version
|
||||
-----------------------------------------------------------------------------
|
||||
_VERSION = "URL 1.0.1"
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Encodes a string into its escaped hexadecimal representation
|
||||
-- Input
|
||||
-- s: binary string to be encoded
|
||||
-- Returns
|
||||
-- escaped representation of string binary
|
||||
-----------------------------------------------------------------------------
|
||||
function escape(s)
|
||||
return string.gsub(s, "([^A-Za-z0-9_])", function(c)
|
||||
return string.format("%%%02x", string.byte(c))
|
||||
end)
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Protects a path segment, to prevent it from interfering with the
|
||||
-- url parsing.
|
||||
-- Input
|
||||
-- s: binary string to be encoded
|
||||
-- Returns
|
||||
-- escaped representation of string binary
|
||||
-----------------------------------------------------------------------------
|
||||
local function make_set(t)
|
||||
local s = {}
|
||||
for i,v in base.ipairs(t) do
|
||||
s[t[i]] = 1
|
||||
end
|
||||
return s
|
||||
end
|
||||
|
||||
-- these are allowed withing a path segment, along with alphanum
|
||||
-- other characters must be escaped
|
||||
local segment_set = make_set {
|
||||
"-", "_", ".", "!", "~", "*", "'", "(",
|
||||
")", ":", "@", "&", "=", "+", "$", ",",
|
||||
}
|
||||
|
||||
local function protect_segment(s)
|
||||
return string.gsub(s, "([^A-Za-z0-9_])", function (c)
|
||||
if segment_set[c] then return c
|
||||
else return string.format("%%%02x", string.byte(c)) end
|
||||
end)
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Encodes a string into its escaped hexadecimal representation
|
||||
-- Input
|
||||
-- s: binary string to be encoded
|
||||
-- Returns
|
||||
-- escaped representation of string binary
|
||||
-----------------------------------------------------------------------------
|
||||
function unescape(s)
|
||||
return string.gsub(s, "%%(%x%x)", function(hex)
|
||||
return string.char(base.tonumber(hex, 16))
|
||||
end)
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Builds a path from a base path and a relative path
|
||||
-- Input
|
||||
-- base_path
|
||||
-- relative_path
|
||||
-- Returns
|
||||
-- corresponding absolute path
|
||||
-----------------------------------------------------------------------------
|
||||
local function absolute_path(base_path, relative_path)
|
||||
if string.sub(relative_path, 1, 1) == "/" then return relative_path end
|
||||
local path = string.gsub(base_path, "[^/]*$", "")
|
||||
path = path .. relative_path
|
||||
path = string.gsub(path, "([^/]*%./)", function (s)
|
||||
if s ~= "./" then return s else return "" end
|
||||
end)
|
||||
path = string.gsub(path, "/%.$", "/")
|
||||
local reduced
|
||||
while reduced ~= path do
|
||||
reduced = path
|
||||
path = string.gsub(reduced, "([^/]*/%.%./)", function (s)
|
||||
if s ~= "../../" then return "" else return s end
|
||||
end)
|
||||
end
|
||||
path = string.gsub(reduced, "([^/]*/%.%.)$", function (s)
|
||||
if s ~= "../.." then return "" else return s end
|
||||
end)
|
||||
return path
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Parses a url and returns a table with all its parts according to RFC 2396
|
||||
-- The following grammar describes the names given to the URL parts
|
||||
-- <url> ::= <scheme>://<authority>/<path>;<params>?<query>#<fragment>
|
||||
-- <authority> ::= <userinfo>@<host>:<port>
|
||||
-- <userinfo> ::= <user>[:<password>]
|
||||
-- <path> :: = {<segment>/}<segment>
|
||||
-- Input
|
||||
-- url: uniform resource locator of request
|
||||
-- default: table with default values for each field
|
||||
-- Returns
|
||||
-- table with the following fields, where RFC naming conventions have
|
||||
-- been preserved:
|
||||
-- scheme, authority, userinfo, user, password, host, port,
|
||||
-- path, params, query, fragment
|
||||
-- Obs:
|
||||
-- the leading '/' in {/<path>} is considered part of <path>
|
||||
-----------------------------------------------------------------------------
|
||||
function parse(url, default)
|
||||
-- initialize default parameters
|
||||
local parsed = {}
|
||||
for i,v in base.pairs(default or parsed) do parsed[i] = v end
|
||||
-- empty url is parsed to nil
|
||||
if not url or url == "" then return nil, "invalid url" end
|
||||
-- remove whitespace
|
||||
-- url = string.gsub(url, "%s", "")
|
||||
-- get fragment
|
||||
url = string.gsub(url, "#(.*)$", function(f)
|
||||
parsed.fragment = f
|
||||
return ""
|
||||
end)
|
||||
-- get scheme
|
||||
url = string.gsub(url, "^([%w][%w%+%-%.]*)%:",
|
||||
function(s) parsed.scheme = s; return "" end)
|
||||
-- get authority
|
||||
url = string.gsub(url, "^//([^/]*)", function(n)
|
||||
parsed.authority = n
|
||||
return ""
|
||||
end)
|
||||
-- get query stringing
|
||||
url = string.gsub(url, "%?(.*)", function(q)
|
||||
parsed.query = q
|
||||
return ""
|
||||
end)
|
||||
-- get params
|
||||
url = string.gsub(url, "%;(.*)", function(p)
|
||||
parsed.params = p
|
||||
return ""
|
||||
end)
|
||||
-- path is whatever was left
|
||||
if url ~= "" then parsed.path = url end
|
||||
local authority = parsed.authority
|
||||
if not authority then return parsed end
|
||||
authority = string.gsub(authority,"^([^@]*)@",
|
||||
function(u) parsed.userinfo = u; return "" end)
|
||||
authority = string.gsub(authority, ":([^:]*)$",
|
||||
function(p) parsed.port = p; return "" end)
|
||||
if authority ~= "" then parsed.host = authority end
|
||||
local userinfo = parsed.userinfo
|
||||
if not userinfo then return parsed end
|
||||
userinfo = string.gsub(userinfo, ":([^:]*)$",
|
||||
function(p) parsed.password = p; return "" end)
|
||||
parsed.user = userinfo
|
||||
return parsed
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Rebuilds a parsed URL from its components.
|
||||
-- Components are protected if any reserved or unallowed characters are found
|
||||
-- Input
|
||||
-- parsed: parsed URL, as returned by parse
|
||||
-- Returns
|
||||
-- a stringing with the corresponding URL
|
||||
-----------------------------------------------------------------------------
|
||||
function build(parsed)
|
||||
local ppath = parse_path(parsed.path or "")
|
||||
local url = build_path(ppath)
|
||||
if parsed.params then url = url .. ";" .. parsed.params end
|
||||
if parsed.query then url = url .. "?" .. parsed.query end
|
||||
local authority = parsed.authority
|
||||
if parsed.host then
|
||||
authority = parsed.host
|
||||
if parsed.port then authority = authority .. ":" .. parsed.port end
|
||||
local userinfo = parsed.userinfo
|
||||
if parsed.user then
|
||||
userinfo = parsed.user
|
||||
if parsed.password then
|
||||
userinfo = userinfo .. ":" .. parsed.password
|
||||
end
|
||||
end
|
||||
if userinfo then authority = userinfo .. "@" .. authority end
|
||||
end
|
||||
if authority then url = "//" .. authority .. url end
|
||||
if parsed.scheme then url = parsed.scheme .. ":" .. url end
|
||||
if parsed.fragment then url = url .. "#" .. parsed.fragment end
|
||||
-- url = string.gsub(url, "%s", "")
|
||||
return url
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Builds a absolute URL from a base and a relative URL according to RFC 2396
|
||||
-- Input
|
||||
-- base_url
|
||||
-- relative_url
|
||||
-- Returns
|
||||
-- corresponding absolute url
|
||||
-----------------------------------------------------------------------------
|
||||
function absolute(base_url, relative_url)
|
||||
if base.type(base_url) == "table" then
|
||||
base_parsed = base_url
|
||||
base_url = build(base_parsed)
|
||||
else
|
||||
base_parsed = parse(base_url)
|
||||
end
|
||||
local relative_parsed = parse(relative_url)
|
||||
if not base_parsed then return relative_url
|
||||
elseif not relative_parsed then return base_url
|
||||
elseif relative_parsed.scheme then return relative_url
|
||||
else
|
||||
relative_parsed.scheme = base_parsed.scheme
|
||||
if not relative_parsed.authority then
|
||||
relative_parsed.authority = base_parsed.authority
|
||||
if not relative_parsed.path then
|
||||
relative_parsed.path = base_parsed.path
|
||||
if not relative_parsed.params then
|
||||
relative_parsed.params = base_parsed.params
|
||||
if not relative_parsed.query then
|
||||
relative_parsed.query = base_parsed.query
|
||||
end
|
||||
end
|
||||
else
|
||||
relative_parsed.path = absolute_path(base_parsed.path or "",
|
||||
relative_parsed.path)
|
||||
end
|
||||
end
|
||||
return build(relative_parsed)
|
||||
end
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Breaks a path into its segments, unescaping the segments
|
||||
-- Input
|
||||
-- path
|
||||
-- Returns
|
||||
-- segment: a table with one entry per segment
|
||||
-----------------------------------------------------------------------------
|
||||
function parse_path(path)
|
||||
local parsed = {}
|
||||
path = path or ""
|
||||
--path = string.gsub(path, "%s", "")
|
||||
string.gsub(path, "([^/]+)", function (s) table.insert(parsed, s) end)
|
||||
for i = 1, table.getn(parsed) do
|
||||
parsed[i] = unescape(parsed[i])
|
||||
end
|
||||
if string.sub(path, 1, 1) == "/" then parsed.is_absolute = 1 end
|
||||
if string.sub(path, -1, -1) == "/" then parsed.is_directory = 1 end
|
||||
return parsed
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Builds a path component from its segments, escaping protected characters.
|
||||
-- Input
|
||||
-- parsed: path segments
|
||||
-- unsafe: if true, segments are not protected before path is built
|
||||
-- Returns
|
||||
-- path: corresponding path stringing
|
||||
-----------------------------------------------------------------------------
|
||||
function build_path(parsed, unsafe)
|
||||
local path = ""
|
||||
local n = table.getn(parsed)
|
||||
if unsafe then
|
||||
for i = 1, n-1 do
|
||||
path = path .. parsed[i]
|
||||
path = path .. "/"
|
||||
end
|
||||
if n > 0 then
|
||||
path = path .. parsed[n]
|
||||
if parsed.is_directory then path = path .. "/" end
|
||||
end
|
||||
else
|
||||
for i = 1, n-1 do
|
||||
path = path .. protect_segment(parsed[i])
|
||||
path = path .. "/"
|
||||
end
|
||||
if n > 0 then
|
||||
path = path .. protect_segment(parsed[n])
|
||||
if parsed.is_directory then path = path .. "/" end
|
||||
end
|
||||
end
|
||||
if parsed.is_absolute then path = "/" .. path end
|
||||
return path
|
||||
end
|
||||
Loading…
x
Reference in New Issue
Block a user