Added intergration loveManager

Adds multi.intergrations.loveManager,lua
Created an example file for you to look at
This commit is contained in:
2017-06-24 22:46:44 -04:00
parent f34b200979
commit cf22651949
161 changed files with 22984 additions and 16 deletions
@@ -0,0 +1,212 @@
audio = {}
audio.__index = audio
function audio:new(f,t)
local obj={}
setmetatable(obj, audio)
obj.source=love.audio.newSource(f,t)
obj.f=f
obj.t=t or "stream"
obj.endEvent=multi:newLoop()
obj.endEvent.Pare=obj
obj.wasPlaying=false
obj.func={}
obj.func2={}
obj.func3={}
obj.func4={}
obj.endEvent:OnLoop(function(time,loop)
if not(loop.Pare:isPlaying()) and loop.Pare.wasPlaying==true and not(loop.Pare:isPaused()) then
for i=1,#loop.Pare.func do
loop.Pare:stop()
loop.Pare.wasPlaying=false
loop.Pare.endEvent:Pause()
loop.Pare.func[i](loop.Pare)
end
end
end)
obj.endEvent:Pause()
return obj
end
function audio:clone()
local _temp=audio:new(self.f,self.t)
_temp.source=self.source:clone()
return _temp
end
--Mutators
function audio:play()
if self:isPaused() then
for i=1,#self.func4 do
self.func4[i](self)
end
self:resume()
else
for i=1,#self.func3 do
self.func3[i](self)
end
self.source:play()
self.wasPlaying=true
self.endEvent:Resume()
end
end
function audio:stop()
self.source:stop()
self.wasPlaying=true
self.endEvent:Pause()
end
function audio:pause()
for i=1,#self.func2 do
self.func2[i](self)
end
self.source:pause()
end
function audio:resume()
self.source:resume()
end
function audio:rewind()
self.source:rewind()
end
function audio:setAttenuationDistances(r,m)
self.source:setAttenuationDistances(r,m)
end
function audio:setCone(innerAngle, outerAngle, outerVolume)
self.source:setCone(innerAngle, outerAngle, outerVolume)
end
function audio:setDirection(x, y, z)
self.source:setDirection(x, y, z)
end
function audio:setLooping(loop)
self.source:setLooping(loop)
end
function audio:setPitch(pitch)
self.source:setPitch(pitch)
end
function audio:setPosition(x, y, z)
self.source:setPosition(x, y, z)
end
function audio:setRelative(enable)
self.source:setRelative(enable)
end
function audio:setRolloff(rolloff)
self.source:setRolloff(rolloff)
end
function audio:setVelocity(x, y, z)
self.source:setVelocity(x, y, z)
end
function audio:setVolume(volume)
self.source:setVolume(volume)
end
function audio:setVolumeLimits(min, max)
self.source:setVolumeLimits(min, max)
end
function audio:seek(offset,unit)
self.source:seek(offset,unit)
end
--Assessors
function audio:isPlaying()
return self.source:isPlaying()
end
function audio:isPaused()
return self.source:isPaused()
end
function audio:isStopped()
return self.source:isStopped()
end
function audio:isLooping()
return self.source:isLooping()
end
function audio:isStatic()
return self.source:isStatic()
end
function audio:isRelative()
return self.source:isRelative()
end
function audio:getAttenuationDistances()
return self.source:getAttenuationDistances()
end
function audio:getChannels()
return self.source:getChannels()
end
function audio:getCone()
return self.source:getCone()
end
function audio:getDirection()
return self.source:getDirection()
end
function audio:getPitch()
return self.source:getPitch()
end
function audio:getPosition()
return self.source:getPosition()
end
function audio:getRolloff()
return self.source:getRolloff()
end
function audio:getVelocity()
return self.source:getVelocity()
end
function audio:getVolume()
return self.source:getVolume()
end
function audio:getVolumeLimits()
return self.source:getVolumeLimits()
end
function audio:tell(unit)
return self.source:tell(unit)
end
function audio:type()
return self.source:type()
end
function audio:typeOf()
return self.source:typeOf()
end
--Events
function audio:onResume(func)
table.insert(self.func4,func)
end
function audio:onPlay(func)
table.insert(self.func3,func)
end
function audio:onPause(func)
table.insert(self.func2,func)
end
function audio:onStop(func)
table.insert(self.func,func)
end
--[[
Object:type |Done
Object:typeOf |Done
Source:clone |Done
Source:getAttenuationDistances |Done
Source:getChannels |Done
Source:getCone |Done
Source:getDirection |Done
Source:getPitch |Done
Source:getPosition |Done
Source:getRolloff |Done
Source:getVelocity |Done
Source:getVolume |Done
Source:getVolumeLimits |Done
Source:isLooping |Done
Source:isPaused |Done
Source:isPlaying |Done
Source:isRelative |Done
Source:isStatic |Done
Source:isStopped |Done
Source:pause |Done
Source:play |Done
Source:resume |Done
Source:rewind |Done
Source:seek |Done
Source:setAttenuationDistances |Done
Source:setCone |Done
Source:setDirection |Done
Source:setLooping |Done
Source:setPitch |Done
Source:setPosition |Done
Source:setRelative |Done
Source:setRolloff |Done
Source:setVelocity |Done
Source:setVolume |Done
Source:setVolumeLimits |Done
Source:stop |Done
Source:tell |Done
]]
File diff suppressed because it is too large Load Diff
@@ -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")
@@ -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
@@ -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()
@@ -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()
@@ -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()
@@ -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()
@@ -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)
@@ -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))
File diff suppressed because it is too large Load Diff
@@ -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
@@ -0,0 +1,106 @@
Hey guys, I am currently working on a net library which aims to make Async servers/clients a piece of cake. It is still in heavy development, but a release will be made soon. The basic features are done and work great. The modules are being worked on though.
Right now I have the core and 4 modules finished
+CORE <-- The base library
+net.identity <-- makes registration and login for users seamless
+net.sft <-- a Simple File Transfer module.
NOTE: Once net.aft Is complete net.sft will no longer be supported
+net.aft <-- an Advanced File Transfer module.
NOTE: As of right now user can only download files in aft
+net.chatting <-- allows for chatting between users
I will go into detail on how the core and 'stable' modules work
[color=#FF0000][b]Creating A Server[/b][/color]
EXAMPLE:1
[code]
--[[Filename: server.lua
+Dependencies:
+the net library
+the bin library
+the multimanager library
+luasocket
I suggest using luajit when creating the server for that extra boost in performance!
]]
require("net")
-- All you need is a port for the server init
server=net:newServer(12345)
--server=net:newTCPServer(12345)
--[[The commented line above creates a TCP server
CID: Client ID, unique for each user connected to the server. If you are using a TCP Server then you get the handle used for communication
IP_OR_HANDLE, works much like CID_OR_HANDLE, where you get a handle if using TCP
The reason for this is so modules could be made for both types of servers and work flawlessly, The only exception is the sft and aft module, TCP is recommended to ensure things are sent in order
PORT_OR_IP is like the above as well... Think of it this way UDP_TCP for each argument
]]
server:OnDataRecived(function(serv,data,CID_OR_HANDLE,IP_OR_HANDLE,PORT_OR_IP)
if data=="Hello Server!" then -- the client say hi lets say something back
serv:send(IP_OR_HANDLE,"Hey client!",PORT_OR_IP,CID_OR_HANDLE)-- cid only needs to be passed for UDP Servers, you can exclude the cid argument if you want though. The server stores ports and IPs for each CID while they are logged on!
end
end)
multi:mainloop()
[/code]
This is the basic setup for a server where you can send data back and forth. Note: modules that receive data will bypass triggering this event to save CPU time... Only events that are important get triggered If you want to speed up your code and make your event triggered when data you want gets passed use "!eventname! ...rest_of_your_data_here"
For example You would do this:
EXAMPLE:2
[code]
--[[
Assume your client is sending this data: "!gamedata! MOVE bob 10 10"
take a look at the new server OnDataRecieved event
]]
require("net")
server=net:newServer(12345)
world={["bob"]={x=0,y=0}}
server:OnDataRecived(function(serv,data,CID_OR_HANDLE,IP_OR_HANDLE,PORT_OR_IP)
local cmd,entity,arg1,arg2=data:match("!gamedata! (%S+) (%S+) (%S+) (%S+)")
if cmd=="MOVE" then
--lets move bob
world[entity].x=tonumber(arg1)
world[entity].y=tonumber(arg2)
--Now lets tell every client connected what happened
serv:sendAll("!gamedata! UPDATE "..entity.." "..arg1.." "..arg2) -- note only data is needed now!
end
end,"gamedata") -- notice this string right here it allows the CORE to know that you should call this when !gamedata! is within a method. This is useful especally when you have many connections to the server OnDataRecieved event
multi:mainloop()
[/code]
Now that was fun... But what if I'm sending binary data over to the client... Doesn't non-ASCII tend to get messed up?
Yes it does and even when using TCP data tends to not always transfer right
net.normalize(data) converts data into all ASCII characters to keep your data safe, so even binary data can be passed. Since this uses base64 encoding every 3 characters become 4 so your data size becomes a bit bigger. To avoid problems keep all data being normalized data under 384 characters! The data here is converted into 512 bytes.
net.denormalize(data) takes the normalized and converts it back to what it was originally.
Take a look at this example
EXAMPLE:3
[code]
--Assume your client is sending this data: "!gamedata! MSG bob NORMILZED_MSG NIL" empty data so pattern matches
require("net")
function CheckMSG(data)
-- TODO: modify the message for things that break our rules
returns data
end
server=net:newServer(12345)
world={["bob"]={x=0,y=0}}
server:OnDataRecived(function(serv,data,CID_OR_HANDLE,IP_OR_HANDLE,PORT_OR_IP)
local cmd,entity,arg1,arg2=data:match("!gamedata! (%S+) (%S+) (%S+) (%S+)")
if cmd=="MOVE" then
--lets move bob
world[entity].x=tonumber(arg1) -- we are sending the numbers as binary data
world[entity].y=tonumber(arg2)
--Now lets tell every client connected what happened
serv:sendAll("!gamedata! UPDATE "..entity.." "..arg1.." "..arg2) -- note only data is needed now!
elseif cmd=="MSG" then -- client is sending a message NOTE: I have a module that handles chatting however for example sake here is a simple chat message
--Lets look at the data We normalize the data because we can have \n characters in the data and TCP/UDP are line based so that would mess up packets!
local msg=CheckMSG(net.denormalize(arg1))
serv:sendAll("!gamedata! MSG "..entity.." "..msg.." NIL")
end
end,"gamedata") -- notice this string right here it allows the CORE to know that you should call this when !gamedata! is within a method. This is useful especally when you have many connections to the server OnDataRecieved event
multi:mainloop()
[/code]
@@ -0,0 +1,58 @@
require("parseManager")
require("net.identity")
require("net.aft")
net:registerModule("admin",{1,0,0})
-- Localhost does not need to log in to connect to the server and do whatever... This inculdes modules that you are writing for the server
-- LAN connections can request data from the server without logging in, but must log in to alter server settings
-- WAN connections can only request or alter settings if they are logged in
--[[
User levels: 1 - SERVEROWNER/localhost (Autodetect)
2 - ADMIN
3 - Modded User
4 - Privileged User
5 - Regular User
6 - Restricted User
7 - USER DEFINED
8 - USER DEFINED
9 - USER DEFINED
10 - Banned User
]]
if not io.dirExists("-ADMINS-") then
io.mkdir("-ADMINS-")
end
if not io.fileExists("-ADMINS-/LEVELS-List.dat") then
io.mkfile("-ADMINS-/LEVELS-List.dat")
end
net.statuslist={}
net.OnServerCreated:connect(function(s)
s.OnDataRecieved(function(self,data,CID_OR_HANDLE,IP_OR_HANDLE,PORT_OR_IP)
local IP=tonumber(IP_OR_HANDLE) or tonumber(PORT_OR_IP)
end,"admin")
function s:setUserLevel(user,n)
--
end
function s:makeAdmin(user)
--
end
function s:makeMod(user)
--
end
function s:makePrivileged(user)
--
end
function s:restrict(user)
--
end
function s:ban(user)
--
end
function s:getUserLevel(user)
--
end
end)
net.OnClientCreated:connect(function(c)
c.OnDataRecieved(function(self,data)
--
end,"admin")
end)
@@ -0,0 +1,325 @@
require("net")
--General Stuff
--[[ What this module does!
Allows reliable transfer of files over the internet!
Hash testing and piece transfer
TODO: Add uploading support... For now use sft less intensive on the client/server
]]
net:registerModule("aft",{2,1,0})
net.aft.transfers={}
net.aft.sinks={}
net.aft.cache={}
net.aft.preload={}
net.aft.pieceSize=768 -- max data packet for b64 that can safely be transfered without erroring! DO NOT CHANGE!!!
function net.aft:init() -- calling this initilizes the library and binds it to the servers and clients created
--Server Stuff
net.OnServerCreated:connect(function(s)
print("The aft(Advance File Transfer) Module has been loaded onto the server!")
if s.Type~="tcp" then
print("It is recomended that you use tcp to transfer files!")
end
s.OnUploadRequest=multi:newConnection() -- create an aft event
s.OnDownloadRequest=multi:newConnection()
s.OnClientClosed:connect(function(self,reason,cid,ip,port)
if net.aft.transfers[cid] then
for i,v in pairs(net.aft.transfers[cid]) do
v.resendAlarm:Destroy()
end
net.aft.transfers[cid]=nil
end
end)
function s:preloadFile(name)
net.aft.preload[name]={}
local temp=bin.stream(name)
temp:segmentedRead(768,function(data)
local unpackedDATA1=data:sub(1,384)
local unpackedDATA2=data:sub(385)
local packedDATA=net.normalize(unpackedDATA1)..net.normalize(unpackedDATA2)
table.insert(net.aft.preload,packedDATA)
end)
end
function s:isPreloaded(name)
return net.aft.preload[name]~=nil
end
s.allowSmallFileCaching=false
s.cachable=10 -- 10 MBs
s.OnDataRecieved(function(self,data,cid,ip,port) -- when the server recieves data this method is triggered
local cmd,arg1,arg2=data:match("!aft! (%S+) (%S+) (%S+)")
--print(cmd,arg1,arg2)
if cmd=="PIECE" then
local FID,piecenum=arg1,tonumber(arg2)
local pp=piecenum-1
net.aft.transfers[cid][FID].resendAlarm:Reset()
if net.aft.transfers[cid][FID] then
if pp>net.aft.transfers[cid][FID].pieces-1 then
self:send(ip,"!aft! DOWNLOAD INVALID_PIECENUM NIL NIL NIL",port)
print("ERROR 101")
else
if self:isPreloaded(name) then
self:send(ip,net.aft.preload[name][piecenum],port)
return
end
if self.allowSmallFileCaching then
if net.aft.cache[net.aft.transfers[cid][FID].name] then
if net.aft.cache[net.aft.transfers[cid][FID].name][piecenum] then
self:send(ip,net.aft.cache[net.aft.transfers[cid][FID].name][piecenum],port)
return
end
end
end
local ddata
local unpackedDATA=net.aft.transfers[cid][FID].sink:sub((pp*net.aft.pieceSize)+1,(pp+1)*net.aft.pieceSize)
local num=#unpackedDATA
if num<384 then
ddata="!aft! TRANS "..piecenum.." "..FID.." | "..net.normalize(unpackedDATA)
else
local unpackedDATA1=unpackedDATA:sub(1,384)
local unpackedDATA2=unpackedDATA:sub(385)
local packedDATA=net.normalize(unpackedDATA1)..net.normalize(unpackedDATA2)
ddata="!aft! TRANS "..piecenum.." "..FID.." | "..packedDATA
end
net.aft.transfers[cid][FID].resendAlarm.piecenum=piecenum
net.aft.transfers[cid][FID].resendAlarm.hash="" -- not needed anymore
net.aft.transfers[cid][FID].resendAlarm.packedDATA=packedDATA
-- SAFE
if self.allowSmallFileCaching then
net.aft.cache[net.aft.transfers[cid][FID].name][piecenum]=ddata
end
self:send(ip,ddata,port)
end
else
self:send(ip,"!aft! DOWNLOAD INVALID_FID NIL NIL NIL",port)
print("ERROR 102")
end
elseif cmd=="UPLOAD" then -- here we set up the spot for file writing
local FID,filename=arg1:match("(.-)|(.+)")
local struct={
FID=FID,
filename=filename,
numpieces=tonumber(arg2) or -1
}
if struct.numpieces==-1 then -- error handling catch it all :)
self:send(ip,"!aft! UPLOAD UPLOAD_REFUSED INVALID_NUMBER_OF_PIECES | |",port)
return
end
self.OnUploadRequest:Fire(struct,cid,ip,port)
if not(struct.deny) then -- block request or allow it
-- If we are allowed to lets do this
if not(net.aft.transfers.DOWNLOADS) then
net.aft.transfers.DOWNLOADS={}
end
if not(net.aft.transfers.DOWNLOADS[FID]) then
net.aft.transfers.DOWNLOADS[FID]={}
end
bin.new(""):tofile(struct.filename)
net.aft.transfers.DOWNLOADS[struct.FID].sink=struct.sink or bin.stream(struct.filename,false)
net.aft.transfers.DOWNLOADS[struct.FID].currentPiece=1
net.aft.transfers.DOWNLOADS[struct.FID].numPieces=tonumber(arg2)
--we got that setup... Lets Request a piece now!
self:send(ip,"!aft! PIECE 1 "..FID.." | |",port) -- request piece # 1
else
self:send(ip,"!aft! UPLOAD UPLOAD_REFUSED "..(struct.reason or "UNSPECIFIED_ERROR").." | |",port)
end
elseif cmd=="TRANS" then
local FID,piece=arg1:match("(.-)|(.+)")
local piece=tonumber(piece) or -1
if pieces==-1 then -- error handling catch it all :)
self:send(ip,"!aft! UPLOAD UPLOAD_CANCLED PIECE_DATA_MALFORMED | |",port)
return
end
if #arg2<512 then
net.aft.transfers.DOWNLOADS[FID].sink:tackE(net.denormalize(arg2))
else
net.aft.transfers.DOWNLOADS[FID].sink:tackE(net.denormalize(arg2:sub(1,512))..net.denormalize(arg2:sub(513)))
end
-- request the next piece
if piece==net.aft.transfers.DOWNLOADS[FID].numPieces then
-- We are done!
self:send(ip,"!aft! DONE "..FID.." | | |",port)
net.aft.transfers.DOWNLOADS[FID].sink:close() -- close the file
else
self:send(ip,"!aft! PIECE "..piece+1 .." "..FID.." | |",port)
end
elseif cmd=="REQUEST" then
local filename=arg1
local struct={
filename=filename
}
self.OnDownloadRequest:Fire(self,struct)
if io.fileExists(struct.filename) or struct.handle then
local FID=bin.new(filename):getRandomHash(16)
if struct.handle then
FID=struct.handle:getRandomHash(16)
end
if not net.aft.transfers[cid] then
net.aft.transfers[cid]={} -- setup server-client filestream
end
net.aft.transfers[cid][FID]={}
net.aft.transfers[cid][FID].name=struct.filename
if struct.handle then
net.aft.transfers[cid][FID].sink=struct.handle
else
net.aft.transfers[cid][FID].sink=bin.stream(struct.filename,false)
end
net.aft.transfers[cid][FID].size=net.aft.transfers[cid][FID].sink:getSize()
net.aft.transfers[cid][FID].pieces=math.ceil(net.aft.transfers[cid][FID].size/net.aft.pieceSize)
net.aft.transfers[cid][FID].resendAlarm=multi:newAlarm(.25)
net.aft.transfers[cid][FID].resendAlarm:OnRing(function(alarm)
if not(alarm.packedDATA) then return end
self:send(ip,"!aft! TRANS "..alarm.piecenum.." "..FID.." | "..alarm.packedDATA,port)
alarm:Reset()
end)
if self.allowSmallFileCaching then
if net.aft.transfers[cid][FID].size<=1024*self.cachable then -- 10 MB or smaller can be cached
net.aft.cache[struct.filename]={}
end
end
self:send(ip,"!aft! START "..net.aft.transfers[cid][FID].pieces.." "..FID.." "..filename.." NIL",port)
else
self:send(ip,"!aft! DOWNLOAD REQUEST_REFUSED NIL NIL NIL",port)
print("ERROR 103")
end
elseif cmd=="COMPLETE" then
net.aft.transfers[cid][arg1].resendAlarm:Destroy()
net.aft.transfers[cid][arg1]=nil
end
end,"aft") -- some new stuff
end)
--Client Stuff
net.OnClientCreated:connect(function(c)
c.OnPieceRecieved=multi:newConnection()
c.OnTransferStarted=multi:newConnection()
c.OnTransferCompleted=multi:newConnection()
c.OnFileRequestFailed=multi:newConnection() -- create an aft event
c.OnFileUploadFailed=multi:newConnection() -- not yet must ensure oneway works well first
c.OnDataRecieved(function(self,data) -- when the client recieves data this method is triggered
local cmd,pieces,FID,arg1,arg2=data:match("!aft! (%S+) (%S+) (%S+) (%S+) (%S+)")
--print(cmd,pieces,FID,arg1,arg2)
if cmd=="START" then-- FID filename #pieces
local struct={
FID=FID,
filename=arg1,
numpieces=tonumber(pieces)
}
self.OnTransferStarted:Fire(self,struct)
local fid,filename,np=struct.FID,struct.filename,struct.numpieces
local sink=""
if type(net.aft.sinks[filename])=="table" then
sink=net.aft.sinks[filename]
sink.file=filename
else
if net.aft.sinks[filename] then
bin.new():tofile(net.aft.sinks[filename])
sink=bin.stream(net.aft.sinks[filename],false)
else
bin.new():tofile(filename)
sink=bin.stream(filename,false)
end
end
net.aft.transfers[FID]={}
net.aft.transfers[FID].name=sink.file
net.aft.transfers[FID].sink=sink
net.aft.transfers[FID].currentPiece=1
net.aft.transfers[FID].piecesRecieved=0
net.aft.transfers[FID].numpieces=tonumber(pieces)
c:requestPiece(FID,1)
elseif cmd=="DONE" then
local FID=pieces
print(net.aft.transfers.UPLOADS[FID].name.." has Finished Uploading!")
self.OnTransferCompleted:Fire(self,net.aft.transfers[FID].name,"U")
net.aft.transfers[FID]=nil -- clean up
elseif cmd=="PIECE" then -- Server is asking for a piece to some file
local pieces=tonumber(pieces)
local pp=pieces-1
local unpackedDATA=net.aft.transfers.UPLOADS[FID].sink:sub((pp*net.aft.pieceSize)+1,(pp+1)*net.aft.pieceSize)
local num=#unpackedDATA
if num<384 then
self:send("!aft! TRANS "..FID.."|"..pieces.." "..net.normalize(unpackedDATA))
else
local unpackedDATA1=unpackedDATA:sub(1,384)
local unpackedDATA2=unpackedDATA:sub(385)
local packedDATA=net.normalize(unpackedDATA1)..net.normalize(unpackedDATA2)
self:send("!aft! TRANS "..FID.."|"..pieces.." "..packedDATA)
end
elseif cmd=="TRANS" then-- self,data,FID,piecenum,hash
if self.autoNormalization==false then -- if we already handled normalization in the main data packet then don't redo
local ddata
if #arg2<512 then
ddata=net.denormalize(arg2)
else
ddata=net.denormalize(arg2:sub(1,512))..net.denormalize(arg2:sub(513))
end
struct={
data=ddata,
FID=FID,
piecenum=tonumber(pieces),
numpieces=net.aft.transfers[FID].numpieces,
hash=arg1,
name=net.aft.transfers[FID].name,
}
else
struct={
data=arg2,
FID=FID,
piecenum=tonumber(pieces),
numpieces=net.aft.transfers[FID].numpieces,
hash=arg1,
name=net.aft.transfers[FID].name,
}
end
net.aft.transfers[FID].currentPiece=tonumber(pieces)
self.OnPieceRecieved:Fire(self,struct)
local data,FID,piecenum,hash=struct.data,struct.FID,struct.piecenum,struct.hash
net.aft.transfers[FID].sink:tackE(data)
net.aft.transfers[FID].piecesRecieved=net.aft.transfers[FID].piecesRecieved+1
if net.aft.transfers[FID].numpieces==net.aft.transfers[FID].piecesRecieved then
print(net.aft.transfers[FID].name.." has finished downloading!")
net.aft.transfers[FID].sink:close()
self:send("!aft! COMPLETE "..FID.." NIL") -- for clean up purposes
self.OnTransferCompleted:Fire(self,net.aft.transfers[FID].name)
else
self:requestPiece(FID,piecenum+1) -- get next piece
end
elseif cmd=="DOWNLOAD" then
local msg=FID
self.OnFileRequestFailed:Fire(msg)
print("Download Error!",msg)
elseif cmd=="UPLOAD" then
local msg=FID
self.OnFileUploadFailed:Fire(msg)
print("Upload Error!",msg)
end
end,"aft")
function c:requestFile(filename,sink) -- sinks data through a bin-stream sink if the filename you want otherwise the filename is used instead
self:send("!aft! REQUEST "..filename.." NIL")
if sink then
net.aft.sinks[filename]=sink
end
end
function c:requestUpload(filename)
if io.fileExists(filename) then
local FID=bin.new(filename):getRandomHash(16) -- We need this, but its not as important for client as it is for servers
local file=bin.stream(filename)
if not net.aft.transfers.UPLOADS then
net.aft.transfers.UPLOADS={}
end
if not net.aft.transfers.UPLOADS[FID] then
net.aft.transfers.UPLOADS[FID]={}
end
net.aft.transfers.UPLOADS[FID].sink=file -- client file management is much simpler since we only have to worry about 1 reciever/sender
net.aft.transfers.UPLOADS[FID].name=filename
net.aft.transfers.UPLOADS[FID].size=file:getSize()
net.aft.transfers.UPLOADS[FID].pieces=math.ceil(net.aft.transfers.UPLOADS[FID].size/net.aft.pieceSize)
self:send("!aft! UPLOAD "..FID.."|"..filename.." "..net.aft.transfers.UPLOADS[FID].pieces)-- Lets send the FID we will be using and the number of pieces the server should look out for
else
self.OnFileUploadFailed:Fire("File specified not found! "..filename.." does not exist!")
end
end
function c:requestPiece(FID,piecenum)
self:send("!aft! PIECE "..FID.." "..piecenum)
end
end)
end
if net.autoInit then
net.aft.init()
end
@@ -0,0 +1,273 @@
function net:newAUDPServer(port,servercode)
local c={}
c.udp=assert(socket.udp())
c.udp:settimeout(0)
c.udp:setsockname("*", port)
c.ips={}
c.Type="udp"
c.port=port
c.ids={}
c.servercode=servercode
c.bannedIPs={}
c.bannedCIDs={}
c.autoNormalization=false
function c:setUpdateRate(n)
print("Not needed in a audp server!")
end
function c:banCID(cid)
table.insert(self.bannedCIDs,cid)
end
function c:banIP(ip)
table.insert(self.bannedIPs,cid)
end
function c:send(ip,data,port,cid)
if self.autoNormalization then
data=net.normalize(data)
end
if self.servercode then
cid=cid or self:CIDFrom(ip,port)
if not self.ips[cid] then
print("Can't determine cid from client... sending the client a new one!")
local cid=net.resolveID(self)
print("Sending unique cid to client: "..cid)
self.ips[cid]={ip,port,0,self.servercode==nil}
print(ip)
self.udp:sendto("I!"..cid,ip,port)
if self.servercode then
self.udp:sendto("S!",ip,port)
end
return
end
if net.inList(self.bannedIPs,ip) or net.inList(self.bannedCIDs,cid) then
self.udp:sendto("BANNED CLIENT", ip, port or self.port)
elseif self.ips[cid][4] then
self.udp:sendto(data, ip, port or self.port)
elseif self.ips[cid][4]==false then
self.udp:sendto("Make sure your server code is correct!", ip, port)
end
else
self.udp:sendto(data, ip, port or self.port)
end
end
function c:pollClientModules(ip,port)
self:send(ip,"L!",port)
end
function c:CIDFrom(ip,port)
for i,v in pairs(self.ips) do
if(ip==v[1] and v[2]==port) then
return i
end
end
end
function c:sendAll(data)
for i,v in pairs(self.ips) do
self:send(v[1],data,v[2],i)
end
end
function c:sendAllBut(data,cid)
for i,v in pairs(self.ips) do
if i~=cid then
self:send(v[1],data,v[2],i)
end
end
end
function c:clientRegistered(cid)
return self.ips[cid]
end
function c:clientLoggedIn(cid)
if not self.clientRegistered(cid) then
return nil
end
return self.ips[cid][4]
end
function c:update()
local data,ip,port=self.udp:receivefrom()
if net.inList(self.bannedIPs,ip) or net.inList(self.bannedCIDs,cid) then
print("We will ingore data from a banned client!")
return
end
if data then
if self.autoNormalization then
data=net.denormalize(data)
end
if data:sub(1,4)=="pong" then
self.ips[data:sub(5,-1)][3]=os.clock()
elseif data:sub(1,2)=="S!" then
local cid=self:CIDFrom(ip,port)
if data:sub(3,-1)==self.servercode then
print("Servercode Accepted: "..self.servercode)
if self.ips[cid] then
self.ips[cid][4]=true
else
print("Server can't keep up! CID: "..cid.." has been skipped! Sending new CID to the client!")
local cid=net.resolveID(self)
print("Sending unique cid to client: "..cid)
self.ips[cid]={ip,port,0,self.servercode==nil}
print(ip)
self.udp:sendto("I!"..cid,ip,port)
if self.servercode then
self.udp:sendto("S!",ip,port)
end
end
else
self.udp:sendto("Make sure your server code is correct!", ip, port)
end
elseif data:sub(1,2)=="C!" then
local hook=(data:sub(11,-1)):match("!(.-)!")
self.OnDataRecieved:getConnection(hook):Fire(self,data:sub(11,-1),data:sub(3,10),ip,port)
elseif data:sub(1,2)=="E!" then
self.ips[data:sub(3,10)]=nil
obj.ids[data:sub(3,10)]=false
self.OnClientClosed:Fire(self,"Client Closed Connection!",data:sub(3,10),ip,port)
elseif data=="I!" then
local cid=net.resolveID(self)
print("Sending unique cid to client: "..cid)
self.ips[cid]={ip,port,os.clock(),self.servercode==nil}
print(ip)
self.udp:sendto("I!"..cid,ip,port)
if self.servercode then
self.udp:sendto("S!",ip,port)
end
self.OnClientConnected:Fire(self,cid,ip,port)
elseif data:sub(1,2)=="L!" then
cid,cList=data:sub(3,10),data:sub(11,-1)
local list={}
for m,v in cList:gmatch("(%S-):(%S-)|") do
list[m]=v
end
self.OnClientsModulesList:Fire(list,cid,ip,port)
end
end
for cid,dat in pairs(self.ips) do
if not((os.clock()-dat[3])<65) then
self.ips[cid]=nil
self.OnClientClosed:Fire(self,"Client lost Connection: ping timeout",cid,ip,port)
end
end
end
c.OnClientsModulesList=multi:newConnection()
c.OnDataRecieved=multi:newConnection()
c.OnClientClosed=multi:newConnection()
c.OnClientConnected=multi:newConnection()
c.connectiontest=multi:newAlarm(30)
c.connectiontest.link=c
c.connectiontest:OnRing(function(alarm)
--print("pinging clients!")
alarm.link:sendAll("ping")
alarm:Reset()
end)
multi:newLoop(function()
c:update()
end)
net.OnServerCreated:Fire(c)
return c
end
function net:newAUDPClient(host,port,servercode,nonluaServer)
local c={}
c.ip=assert(socket.dns.toip(host))
c.udp=assert(socket.udp())
c.udp:settimeout(0)
c.cid="NIL"
c.lastPing=0
c.Type="udp"
c.servercode=servercode
c.autoReconnect=true
c.autoNormalization=false
function c:pollPing(n)
return not((os.clock()-self.lastPing)<(n or 60))
end
function c:send(data)
if self.autoNormalization then
data=net.normalize(data)
end
self.udp:send("C!"..self.cid..data)
end
function c:sendRaw(data)
if self.autoNormalization then
data=net.normalize(data)
end
self.udp:send(data)
end
function c:getCID()
if self:IDAssigned() then
return self.cid
end
end
function c:close()
self:send("E!")
end
function c:IDAssigned()
return self.cid~="NIL"
end
function c:update()
local data=self.udp:receive()
if data then
if self.autoNormalization then
data=net.denormalize(data)
end
if data:sub(1,2)=="I!" then
self.cid=data:sub(3,-1)
self.OnClientReady:Fire(self)
elseif data=="S!" then
self.udp:send("S!"..(self.servercode or ""))
elseif data=="L!" then
local mods=""
local m=""
for i=1,#net.loadedModules do
m=net.loadedModules[i]
mods=mods..m..":"..net.getModuleVersion(m).."|"
end
self.udp:send("L!"..self.cid..mods)
elseif data=="ping" then
self.lastPing=os.clock()
self.OnPingRecieved:Fire(self)
self.udp:send("pong"..self.cid)
else
local hook=data:match("!(.-)!")
self.OnDataRecieved:getConnection(hook):Fire(self,data)
end
end
end
function c:reconnect()
if not nonluaServer then
self.cid="NIL"
c.udp:send("I!")
end
self.OnConnectionRegained:Fire(self)
end
c.pingEvent=multi:newEvent(function(self) return self.link:pollPing() end)
c.pingEvent:OnEvent(function(self)
if self.link.autoReconnect then
self.link.OnServerNotAvailable:Fire("Connection to server lost: ping timeout! Attempting to reconnect...")
self.link.OnClientDisconnected:Fire(self,"closed")
self.link:reconnect()
else
self.link.OnServerNotAvailable:Fire("Connection to server lost: ping timeout!")
self.link.OnClientDisconnected:Fire(self,"closed")
end
end)
c.pingEvent.link=c
c.OnPingRecieved=multi:newConnection()
c.OnDataRecieved=multi:newConnection()
c.OnServerNotAvailable=multi:newConnection()
c.OnClientReady=multi:newConnection()
c.OnClientDisconnected=multi:newConnection()
c.OnConnectionRegained=multi:newConnection()
c.notConnected=multi:newFunction(function(self)
self:hold(3)
if self.link:IDAssigned()==false then
self.link.OnServerNotAvailable:Fire("Can't connect to the server: no response from server")
end
end)
c.notConnected.link=c
if not nonluaServer then
c.udp:send("I!")
end
multi:newLoop(function()
c:update()
end)
multi:newJob(function() c.notConnected() end)
net.OnClientCreated:Fire(c)
return c
end
@@ -0,0 +1,134 @@
require("net")
require("net.identity")
--General Stuff
--[[ What this module does!
Adds
net.chatting:init()
server:OnChatRecieved(function({user,msg}) end)
client:OnChatRecieved(function(user,msg) end)
client:sendChat(user,msg)
]]
net:registerModule("chatting",{3,0,0})
net.chatting.users={}
function net.chatting:getUserIdFromIP(ip)
return net.chatting.users[ip]
end
function net.chatting:init() -- calling this initilizes the library and binds it to the servers and clients created
--Server Stuff
net.OnServerCreated:connect(function(s)
print("The Chatting Module has been loaded onto the server!")
s.OnUserLoggedIn(function(user,cid,ip,port,dTable)
dTable=loadstring("return "..dTable)()
local USERID=bin.new(user):getHash(32)
net.chatting.users[USERID]={dTable.nick,cid,ip,port,dTable} -- add users that log in to the userlist
net.chatting.users[ip]=USERID -- add users that log in to the userlist
local users={}
for i,v in pairs(net.chatting.users) do
if type(i)~="userdata" then
table.insert(users,i.."|"..net.chatting.users[i][1])
end
end
table.insert(users,"")
for i,v in pairs(s.ips) do
s:send(v,"!chatting! $Users|NIL|NIL '"..table.concat(users,",").."'")
end
end)
s.OnUserLoggerOut(function(self,user)
local USERID=bin.new(user):getHash(32)
local ip=net.chatting.users[USERID]
print(USERID)
net.chatting.users[USERID]=nil
net.chatting.users[ip]=nil
end)
s.OnDataRecieved(function(self,data,cid,ip,port) -- when the server recieves data this method is triggered
--First Lets make sure we are getting chatting data
local user,msg = data:match("!chatting! (%S-) (.+)")
if user and msg then
if user:sub(1,1)=="$" then
local cmd,arg=user:match("$(.+)|(.+)")
print("Using extended chatting protocal!")
if cmd=="DM" then
local struct={ -- pack the info up as a table so the server can do filtering and whatnot to the chat
user=user,
msg=net.denormalize(msg)
}
self.OnChatRecieved:Fire(struct,"PRIVATE")
print("USERID",arg)
self:send(net.chatting.users[arg][3],"!chatting! $DM|"..net.chatting.users[arg][1].."|"..net.chatting:getUserIdFromIP(ip).." "..net.normalize(struct.msg).."",net.chatting.users[arg][4])
elseif cmd=="getUsers" then
local users={}
for i,v in pairs(net.chatting.users) do
if type(i)~="userdata" then
table.insert(users,i.."|"..net.chatting.users[i][1])
end
end
table.insert(users,"")
self:send(ip,"!chatting! $Users|NIL|NIL "..table.concat(users,",").."",port)
end
else
local struct={ -- pack the info up as a table so the server can do filtering and whatnot to the chat
user=user,
msg=net.denormalize(msg)
}
self.OnChatRecieved:Fire(struct,"GROUP") -- trigger the chat event
local USERID=net.chatting:getUserIdFromIP(ip)
for i,v in pairs(self.ips) do
if ip==v then
self:send(v,"!chatting! 1|"..struct.user.."|"..USERID.." "..net.normalize(struct.msg).."")
else
self:send(v,"!chatting! 0|"..struct.user.."|"..USERID.." "..net.normalize(struct.msg).."")
end
end
end
end
end,"chatting")
s.rooms={}
function s:regesterRoom(roomname)
self.rooms[roomname]={}
end
s.OnChatRecieved=multi:newConnection() -- create a chat event
end)
--Client Stuff
net.OnClientCreated:connect(function(c)
c.OnDataRecieved(function(self,data) -- when the client recieves data this method is triggered
--First Lets make sure we are getting chatting data
local isself,user,USERID,msg = data:match("!chatting! (%S-)|(%S-)|(%S-) (.+)")
if not isself then return end
if tonumber(isself) then
--This is the client so our job here is done
local msg=net.denormalize(msg)
self.OnChatRecieved:Fire(user,msg,({["1"]=true, ["0"]=false})[isself],USERID) -- trigger the chat event
elseif isself:sub(1,1)=="$" then
local cmd=isself:match("$(.+)")
if cmd=="DM" then
local msg=net.denormalize(msg)
c.OnPrivateChatRecieved:Fire(user,msg,USERID)
elseif cmd=="Users" then
local tab={}
for ID,nick in msg:gmatch("(%S-)|(%S-),") do
tab[nick]=ID
end
c.OnUserList:Fire(tab)
end
end
end,"chatting")
function c:sendChat(user,msg)
self:send("!chatting! "..user.." "..net.normalize(msg).."")
end
function c:sendChatTo(user,touser,msg)
self:send("!chatting! $DM|"..touser.." "..net.normalize(msg).."")
end
function c:getUserList()
self:send("!chatting! $getUsers|NIL NIL")
end
function c:getChatFrom(userID)
self:send("!chatting! getPrivateChat|NIL "..userID) -- add if time permits
end
c.OnPrivateChatRecieved=multi:newConnection()
c.OnUserList=multi:newConnection()
c.OnChatRecieved=multi:newConnection() -- create a chat event
end)
end
if net.autoInit then
net.chatting:init()
end
@@ -0,0 +1,15 @@
require("parseManager")
require("net.identity") -- serversie module
net:registerModule("db",{1,0,0})
net.OnServerCreated:connect(function(s)
s.OnUserLoggedIn(function(user,cid,ip,port,dTable) -- dealing with userdata
--
end)
function s:createTable(PKey,fmt,path)
--
end
end)
--keys are case insensitive, hex and regular base 10 numbers are allowed as well as other structures
--We define a table below with keys and their max size
@@ -0,0 +1,24 @@
require("net")
net:registerModule("eft",{1,0,0})
--[[
This module provides a dedicated socket for file transfer
This allows us to do some more complex stuff with it
The only data that is non file stuff is the initial handshake
CMDs are done on the general socket while transfers are done on the file socket
]]
net.OnServerCreated:connect(function(s)
print("The eft(Expert File Transfer) Module has been loaded onto the server!")
if s.Type~="tcp" then
print("It is recomended that you use tcp to transfer files!")
end
s.OnDataRecieved(function(self,data,CID_OR_HANDLE,IP_OR_HANDLE,PORT_OR_IP)
--
end,"eft")
--
end)
net.OnClientCreated:connect(function(c)
c.OnDataRecieved(function(self,data)
--
end,"eft")
--
end)
@@ -0,0 +1,54 @@
require("net.identity")
net:registerModule("email",{1,0,0})
-- needs rewriting, where it uses the identity module and can send an email. EX: server:email(user,subject,body)
smtp = require 'socket.smtp'
ssl = require 'ssl' -- server side only so noworries for love2d users
function net.email.init(from,user,pass)
net.OnServerCreated:connect(function(s)
s.from=from
s.user=user
s.pass=pass
function s:sendMessage(subject, body, dTable)
local msg = {
headers = {
from = '<'..dTable.email..'>'
to = dTable.nick..' <'..dTable.email..'>',
subject = subject
},
body = body
}
local ok, err = smtp.send {
from = '<'..self.from..'>',
rcpt = '<'..dTable.email..'>',
source = smtp.message(msg),
user = self.user,
password = self.pass,
server = 'smtp.gmail.com',
port = 465,
create = net.sslCreate
}
if not ok then
print("Mail send failed", err) -- better error handling required
end
end
end)
end
function net.sslCreate()
local sock = socket.tcp()
return setmetatable({
connect = function(_, host, port)
local r, e = sock:connect(host, port)
if not r then return r, e end
sock = ssl.wrap(sock, {mode='client', protocol='tlsv1'})
return sock:dohandshake()
end
}, {
__index = function(t,n)
return function(_, ...)
return sock[n](sock, ...)
end
end
})
end
@@ -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
@@ -0,0 +1,266 @@
require("net")
--General Stuff
--[[ What this module does!
Adds
net.identity:init()
]]
net:registerModule("identity",{2,1,0})--1.0.1 Note: Added eaiser ways to get user data using only cid
function net.hash(text,n)
n=n or 16
return bin.new(text.."jgmhktyf"):getHash(n)
end
net.identity.UIDS={}
net.identity.UIDS.ids={}
function net.identity:init() -- calling this initilizes the library and binds it to the servers and clients created
--Server Stuff
net.OnServerCreated(function(s)
s.userFolder="./USERS"
print("The identity Module has been loaded onto the server!")
function s:_isRegistered(user)
return io.fileExists(self.userFolder..net.hash(user)..".dat")
end
function s:getUserData(user)
local userdata=bin.load(self.userFolder..net.hash(user)..".dat")
local nick,dTable=userdata:match("%S-|%S-|(%S-)|(.+)")
return nick,loadstring("return "..(dTable or "{}"))()
end
function s:modifyUserData(user,oldpass,pass,nick,dTable)
if self:_isRegistered(user) then
local userdata=bin.load(self.userFolder..net.hash(user)..".dat")
local args={}
local _pass,_nick,_dTable=userdata:match("%S-|(%S-)|(%S-)|(.+)")
if oldpass~=_pass then
args.invalidPass=true
pass=_pass
end
if not nick then nick=_nick args.invalidNick=true end
table.merge(_dTable or {}, dTable or {})
bin.new(string.format("%s|%s|%s|%s\n",user,pass,nick,dTable)):tofile(self.userFolder..net.hash(user)..".dat")
else
return false
end
end
function s:getUserCred(user)
local userdata=bin.load(self.userFolder..net.hash(user)..".dat")
return userdata:match("%S-|(%S-)|")
end
function s:getUSERID(cid)
return (net.identity.UIDS[cid] or "User Not Logged In!")
end
function s:getUsername(cid)
return self:userLoggedIn(cid)
end
function s:getNickname(cid)
return self.loggedIn[self:getUsername(cid)].nick
end
function s:getdTable(cid)
return self.loggedIn[self:getUsername(cid)]
end
function s:getUserDat(cid)
return self:getUserDataHandle(self:getUsername(cid))
end
function s:getNickFromUSERID(USERID)
return bin.load(self.userFolder..net.hash(user)..".dat"):match("%S-|%S-|(%S-)|")
end
function s:userLoggedIn(cid)
for i,v in pairs(self.loggedIn) do
if v.cid==cid then
return i
end
end
return false
end
function s:setDataLocation(loc)
if not io.dirExists(loc) then
io.mkDir(loc)
end
self.userFolder=loc
end
s:setDataLocation("USERS/")
function s:logoutUser(user)
net.identity.UIDS.ids[user.UID]=nil
self.loggedIn[user]=nil
end
function s:loginUser(user,cid)
net.identity.UIDS[cid]=net.hash(user)
local nick,dTable=self:getUserData(user)
self.loggedIn[user]={}
table.merge(self.loggedIn[user],dTable or {})
self.loggedIn[user].cid=cid
self.loggedIn[user].nick=nick
self.loggedIn[user].UID=net.resolveID(net.identity.UIDS)
return self.loggedIn[user]
end
function s:getUserDataHandle(user)
return self.loggedIn[user]
end
function s:syncUserData(user,ip,port)
local handle=self:getUserDataHandle(user)
self:send(ip,"!identity! SYNC <-|"..bin.ToStr(handle).."|->",port)
end
s.loggedIn={}
s.allowDuplicateNicks=true
s.minimumNickLength=4
s.minimumUserLength=4
s.OnUserRegistered=multi:newConnection()
s.OnUserLoggedIn=multi:newConnection()
s.OnUserLoggerOut=multi:newConnection()
s.OnAlreadyLoggedIn=multi:newConnection()
s.OnPasswordForgotten=multi:newConnection()
s.OnDataRecieved:connect(function(self,data,cid,ip,port) -- when the server recieves data this method is triggered
local cmd,arg1,arg2,arg3,arg4 = data:match("!identity! (%S-) '(.-)' '(.-)' '(.-)' <%-|(.+)|%->")
if cmd=="register" then
local user,pass,nick,dTable = arg1,arg2,arg3,arg4
if self:_isRegistered(user) then
self:send(ip,"!identity! REGISTERED <-|"..user.."|->",port)
else
if not(self.userFolder:sub(-1,-1)=="/" or self.userFolder:sub(-1,-1)=="\\") then
self.userFolder=self.userFolder.."/"
end
local rets=self.OnUserRegistered:Fire(user,pass,nick,loadstring("return "..(dTable or "{}"))())
if #user<=self.minimumUserLength then
self:send(ip,"!identity! REGISTERREFUSED <-|Username too short|->",port)
return
end
if #user<=self.minimumNickLength then
self:send(ip,"!identity! REGISTERREFUSED <-|Nickname too short|->",port)
return
end
for i=1,#rets do
if rets[i][1]==false then
print("Server refused to accept registration request!")
self:send(ip,"!identity! REGISTERREFUSED <-|Unspecified Error|->",port)
return
end
end
multi:newFunction(function(func) -- anom func, allows for fancy multitasking
local dupnickfilemanager=bin.stream(self.userFolder.."Nicks.dat",false)
local isValid=func:newCondition(function() return t~=nil end)
local tab={}
local t=dupnickfilemanager:getBlock("s")
if self.allowDuplicateNicks==false then
while func:condition(isValid) do
tab[#tab]=t
if t==nick then
self:send(ip,"!identity! REGISTERREFUSED <-|Duplicate Nicks are not allowed|->",port)
dupnickfilemanager:close()
return
end
end
t=dupnickfilemanager:getBlock("s")
end
dupnickfilemanager:addBlock(nick.."|"..bin.new(user):getHash(32))
dupnickfilemanager:close()
bin.new(string.format("%s|%s|%s|%s\n",user,pass,nick,dTable)):tofile(self.userFolder..net.hash(user)..".dat")
self:send(ip,"!identity! REGISTEREDGOOD <-|"..user.."|->",port)
func=nil -- we dont want 1000s+ of these anom functions lying around
return
end)()-- lets call the function
end
return
elseif cmd=="login" then
local user,pass = arg1,arg2
local _pass=s:getUserCred(user)
if not(self:_isRegistered(user)) then
self:send(ip,"!identity! LOGINBAD <-|nil|->",port)
return
end
if pass==_pass then
if self:userLoggedIn(cid) then
self.OnAlreadyLoggedIn:Fire(self,user,cid,ip,port)
self:send(ip,"!identity! ALREADYLOGGEDIN <-|nil|->",port)
return
end
local handle=self:loginUser(user,cid) -- binds the cid to username
self:send(ip,"!identity! LOGINGOOD <-|"..bin.ToStr(handle).."|->",port)
self.OnUserLoggedIn:Fire(user,cid,ip,port,bin.ToStr(handle))
return
else
self:send(ip,"!identity! LOGINBAD <-|nil|->",port)
return
end
elseif cmd=="logout" then
self:logoutUser(user)
self.OnClientClosed:Fire(self,"User logged out!",cid,ip,port)
elseif cmd=="sync" then
local dTable = loadstring("return "..(arg4 or "{}"))()
local handle = self:getUserDataHandle(self:userLoggedIn(cid))
table.merge(handle,dTable)
elseif cmd=="pass" then
local user=arg1
if self:_isRegistered(user) then
self.OnPasswordForgotten:Fire(arg1,cid)
self:send(ip,"!identity! PASSREQUESTHANDLED <-|NONE|->",port)
else
self:send(ip,"!identity! NOUSER <-|"..user.."|->",port)
end
end
end)
s.OnClientClosed:connect(function(self,reason,cid,ip,port)
self.OnUserLoggerOut:Fire(self,self:userLoggedIn(cid),cid,reason)
end)
end,"identity")
--Client Stuff
net.OnClientCreated:connect(function(c)
c.userdata={}
c.OnUserLoggedIn=multi:newConnection()
c.OnBadLogin=multi:newConnection()
c.OnUserAlreadyRegistered=multi:newConnection()
c.OnUserAlreadyLoggedIn=multi:newConnection()
c.OnUserRegistered=multi:newConnection()
c.OnNoUserWithName=multi:newConnection()
c.OnPasswordRequest=multi:newConnection()
c.OnUserRegisterRefused=multi:newConnection()
function c:logout()
self:send("!identity! logout 'NONE' 'NONE' 'NONE' <-|nil|->")
end
c.OnDataRecieved(function(self,data) -- when the client recieves data this method is triggered
local cmd,arg1 = data:match("!identity! (%S-) <%-|(.+)|%->")
if cmd=="REGISTERED" then
self.OnUserAlreadyRegistered:Fire(self,arg1)
elseif cmd=="REGISTEREDGOOD" then
self.OnUserRegistered:Fire(self,arg1)
elseif cmd=="REGISTERREFUSED" then
self.OnUserRegisterRefused:Fire(self,arg1)
elseif cmd=="ALREADYLOGGEDIN" then
self.OnUserAlreadyLoggedIn:Fire(self,arg1)
elseif cmd=="LOGINBAD" then
self.OnBadLogin:Fire(self)
elseif cmd=="LOGINGOOD" then
local dTable=loadstring("return "..(arg1 or "{}"))()
table.merge(self.userdata,dTable)
self.OnUserLoggedIn:Fire(self,self.userdata)
elseif cmd=="SYNC" then
local dTable=loadstring("return "..(arg1 or "{}"))()
table.merge(self.userdata,dTable)
elseif cmd=="NOUSER" then
self.OnNoUserWithName:Fire(self,arg1)
elseif cmd=="PASSREQUESTHANDLED" then
self.OnPasswordRequest:Fire(self)
end
end,"identity")
function c:syncUserData()
self:send(string.format("!identity! sync 'NONE' 'NONE' 'NONE' <-|%s|->",bin.ToStr(dTable)))
end
function c:forgotPass(user)
self:send(string.format("!identity! pass '%s' 'NONE' 'NONE' <-|nil|->",user))
end
function c:getUserDataHandle()
return self.userdata
end
function c:logIn(user,pass)
self:send(string.format("!identity! login '%s' '%s' 'NONE' <-|nil|->",user,net.hash(pass)))
end
function c:register(user,pass,nick,dTable)
if dTable then
self:send(string.format("!identity! register '%s' '%s' '%s' <-|%s|->",user,net.hash(pass),nick,bin.ToStr(dTable)))
else
self:send(string.format("!identity! register '%s' '%s' '%s' <-|nil|->",user,net.hash(pass),nick))
end
end
end)
end
if net.autoInit then
net.identity:init()
end
@@ -0,0 +1,70 @@
require("net.identity")
require("net.aft")
require("net.users")
require("net.db")
net:registerModule("inbox",{1,0,0})
--self.OnUserLoggedIn:Fire(user,cid,ip,port,bin.ToStr(handle))
--allows the storing of messages that the user can recieve and view whenever. Allows user to also send messeges to users that are even offline!
--requires an account setup and nick name to be set at account creation
if not io.dirExists("INBOX") then
io.mkDir("INBOX")
end
net.inbox.dbfmt=db.format([=[
[INBOX]{
string MSG 0x800 -- contents of the message
}
[MAIL]{
string NAME 0x20 -- username
string UID 0x10 -- User ID
string NICK 0x20 -- Nickname
number[3] DATE -- list of numbers
table INBO INBOX -- Inbox
}
]=])
net.OnServerCreated:connect(function(s)
s.OnUserLoggedIn(function(user,cid,ip,port,dTable)
if not io.dirExists("INBOX/"..self:getUSERID(cid)) then -- Make sure inbox stuff is set up
io.mkDir("INBOX/"..self:getUSERID(cid))
bin.new():tofile("info.dat")
end
end)
s.OnDataRecieved(function(self,data,CID_OR_HANDLE,IP_OR_HANDLE,PORT_OR_IP)
if self:userLoggedIn(cid) then -- If the user is logged in we do the tests
local cmd,arg1,arg2=data:match("!inbox! (%S+) (%S+) (%S+)")
if cmd=="SEND" then
--
elseif cmd=="LIST" then
--
elseif cmd=="OPEN" then
--
elseif cmd=="DELETE" then
--
elseif cmd=="CLEAR" then
--
end
else
return
end
end,"inbox")
end)
net.OnClientCreated:connect(function(c)
c.OnDataRecieved(function(self,data)
--
end,"inbox")
function c:sendMessage(USERID,msg) -- USERID who, msg being sent. Server handles time stamps
self:send("!inbox! SEND "..USERID.." "..msg)
end
function c:checkInbox() -- returns list of msgIDs
self:send("!inbox! LIST NIL NIL")
end
function c:checkMsg(msgId)
self:send("!inbox! OPEN "..msgId.." NIL") -- server sends back msg content as a file
end
function c:deleteMessage(msgID)
self:send("!inbox! DELETE "..msgId.." NIL")
end
function c:clearInbox()
self:send("!inbox! CLEAR NIL NIL")
end
--
end)
@@ -0,0 +1,664 @@
--[[
UPCOMMING ADDITIONS
AUDP - advance udp/ Ensures packets arrive and handles late packets.
P2P - peer to peer (Server to set up initial connection)
Relay - offput server load (locally)
Threading - Simple threading (UDP/AUDP Only)
Priority handling
]]
--[[
TODO: Finish stuff for Priority handling
]]
function table.merge(t1, t2)
for k,v in pairs(t2) do
if type(v) == 'table' then
if type(t1[k] or false) == 'table' then
table.merge(t1[k] or {}, t2[k] or {})
else
t1[k] = v
end
else
t1[k] = v
end
end
return t1
end
function string.trim(s)
local from = s:match"^%s*()"
return from > #s and "" or s:match(".*%S", from)
end
socket=require("socket")
http=require("socket.http")
mime=require("mime")
net={}
net.Version={2,0,0} -- This will probably stay this version for quite a while... The modules on the otherhand will be more inconsistant
net._VERSION="2.0.0"
net.OnServerCreated=multi:newConnection()
net.OnClientCreated=multi:newConnection()
net.loadedModules={}
net.autoInit=true
function net.normalize(input)
local enc=mime.b64(input)
return enc
end
function net.denormalize(input)
local unenc=mime.unb64(input)
return unenc
end
function net.getLocalIP()
local someRandomIP = "192.168.1.122"
local someRandomPort = "3102"
local mySocket = socket.udp()
mySocket:setpeername(someRandomIP,someRandomPort)
local dat = (mySocket:getsockname())
mySocket:close()
return dat
end
function net.getExternalIP()
local data=http.request("http://whatismyip.org/")
return data:match("600;\">(%d-.%d-.%d-.%d-)</span>")
end
function net:registerModule(mod,version)
if net[mod] then
error("Module by the name: "..mod.." has already been registered! Remember some modules are internal and use certain names!")
end
table.insert(self.loadedModules,mod)
net[mod]={}
if version then
net[mod].Version=version
net[mod]._VERSION=version[1].."."..version[2].."."..version[3]
else
net[mod].Version={1,0,0}
net[mod]._VERSION={1,0,0}
end
return {Version=version,_VERSION=version[1].."."..version[2].."."..version[3]}
end
function net.getModuleVersion(ext)
if not ext then
return string.format("%d.%d.%d",net.Version[1],net.Version[2],net.Version[3])
end
return string.format("%d.%d.%d",net[ext].Version[1],net[ext].Version[2],net[ext].Version[3])
end
function net.resolveID(obj)
local num=math.random(10000000,99999999)
if obj[tostring(num)] then
return net.resolveID(obj)
end
obj.ids[tostring(num)]=true
return tostring(num)
end
function net.inList(list,dat)
for i,v in pairs(list) do
if v==dat then
return true
end
end
return false
end
function net.setTrigger(funcW,funcE)
multi:newTrigger(func)
end
net:registerModule("net",net.Version)
-- Client broadcast
function net:newCastedClient(name) -- connects to the broadcasted server
local listen = socket.udp() -- make a new socket
listen:setsockname(net.getLocalIP(), 11111)
listen:settimeout(0)
local timer=multi:newTimer()
while true do
local data, ip, port = listen:receivefrom()
if timer:Get()>3 then
error("Timeout! Server by the name: "..name.." has not been found!")
end
if data then
local n,tp,ip,port=data:match("(%S-)|(%S-)|(%S-):(%d+)")
if n:match(name) then
print("Found Server!",n,tp,ip,port)
if tp=="tcp" then
return net:newTCPClient(ip,tonumber(port))
else
return net:newClient(ip,tonumber(port))
end
end
end
end
end
-- UDP Stuff
function net:newServer(port,servercode)
local c={}
c.udp=assert(socket.udp())
c.udp:settimeout(0)
c.udp:setsockname("*", port)
c.ips={}
c.Type="udp"
c.port=port
c.ids={}
c.servercode=servercode
c.bannedIPs={}
c.bannedCIDs={}
c.autoNormalization=false
function c:setUpdateRate(n)
print("Not needed in a udp server!")
end
function c:banCID(cid)
table.insert(self.bannedCIDs,cid)
end
function c:banIP(ip)
table.insert(self.bannedIPs,cid)
end
c.broad=socket.udp()
c.hostip=net.getLocalIP()
function c:broadcast(name)
local loop=multi:newTLoop(function(dt,loop)
self.broad:setoption('broadcast',true)
self.broad:sendto(name.."|"..self.Type.."|"..self.hostip..":"..self.port, "255.255.255.255", 11111)
self.broad:setoption('broadcast',false)
end,1)
end
function c:send(ip,data,port,cid)
if self.autoNormalization then
data=net.normalize(data)
end
if self.servercode then
cid=cid or self:CIDFrom(ip,port)
if not self.ips[cid] then
print("Can't determine cid from client... sending the client a new one!")
local cid=net.resolveID(self)
print("Sending unique cid to client: "..cid)
self.ips[cid]={ip,port,0,self.servercode==nil}
print(ip)
self.udp:sendto("I!"..cid,ip,port)
if self.servercode then
self.udp:sendto("S!",ip,port)
end
return
end
if net.inList(self.bannedIPs,ip) or net.inList(self.bannedCIDs,cid) then
self.udp:sendto("BANNED CLIENT", ip, port or self.port)
elseif self.ips[cid][4] then
self.udp:sendto(data, ip, port or self.port)
elseif self.ips[cid][4]==false then
self.udp:sendto("Make sure your server code is correct!", ip, port)
end
else
self.udp:sendto(data, ip, port or self.port)
end
end
function c:pollClientModules(ip,port)
self:send(ip,"L!",port)
end
function c:CIDFrom(ip,port)
for i,v in pairs(self.ips) do
if(ip==v[1] and v[2]==port) then
return i
end
end
end
function c:sendAll(data)
for i,v in pairs(self.ips) do
self:send(v[1],data,v[2],i)
end
end
function c:sendAllBut(data,cid)
for i,v in pairs(self.ips) do
if i~=cid then
self:send(v[1],data,v[2],i)
end
end
end
function c:clientRegistered(cid)
return self.ips[cid]
end
function c:clientLoggedIn(cid)
if not self.clientRegistered(cid) then
return nil
end
return self.ips[cid][4]
end
function c:update()
local data,ip,port=self.udp:receivefrom()
if net.inList(self.bannedIPs,ip) or net.inList(self.bannedCIDs,cid) then
print("We will ingore data from a banned client!")
return
end
if data then
if self.autoNormalization then
data=net.denormalize(data)
end
if data:sub(1,4)=="pong" then
--print("Recieved pong from: "..data:sub(5,-1))
self.ips[data:sub(5,-1)][3]=os.clock()
elseif data:sub(1,2)=="S!" then
local cid=self:CIDFrom(ip,port)
if data:sub(3,-1)==self.servercode then
print("Servercode Accepted: "..self.servercode)
if self.ips[cid] then
self.ips[cid][4]=true
else
print("Server can't keep up! CID: "..cid.." has been skipped! Sending new CID to the client!")
local cid=net.resolveID(self)
print("Sending unique cid to client: "..cid)
self.ips[cid]={ip,port,0,self.servercode==nil}
print(ip)
self.udp:sendto("I!"..cid,ip,port)
if self.servercode then
self.udp:sendto("S!",ip,port)
end
end
else
self.udp:sendto("Make sure your server code is correct!", ip, port)
end
elseif data:sub(1,2)=="C!" then
local hook=(data:sub(11,-1)):match("!(.-)!")
self.OnDataRecieved:getConnection(hook):Fire(self,data:sub(11,-1),data:sub(3,10),ip,port)
elseif data:sub(1,2)=="E!" then
self.ips[data:sub(3,10)]=nil
obj.ids[data:sub(3,10)]=false
self.OnClientClosed:Fire(self,"Client Closed Connection!",data:sub(3,10),ip,port)
elseif data=="I!" then
local cid=net.resolveID(self)
print("Sending unique cid to client: "..cid)
self.ips[cid]={ip,port,os.clock(),self.servercode==nil}
print(ip)
self.udp:sendto("I!"..cid,ip,port)
if self.servercode then
self.udp:sendto("S!",ip,port)
end
self.OnClientConnected:Fire(self,cid,ip,port)
elseif data:sub(1,2)=="L!" then
cid,cList=data:sub(3,10),data:sub(11,-1)
local list={}
for m,v in cList:gmatch("(%S-):(%S-)|") do
list[m]=v
end
self.OnClientsModulesList:Fire(list,cid,ip,port)
end
end
for cid,dat in pairs(self.ips) do
if not((os.clock()-dat[3])<65) then
self.ips[cid]=nil
self.OnClientClosed:Fire(self,"Client lost Connection: ping timeout",cid,ip,port)
end
end
end
c.OnClientsModulesList=multi:newConnection()
c.OnDataRecieved=multi:newConnection()
c.OnClientClosed=multi:newConnection()
c.OnClientConnected=multi:newConnection()
c.connectiontest=multi:newAlarm(30)
c.connectiontest.link=c
c.connectiontest:OnRing(function(alarm)
--print("pinging clients!")
alarm.link:sendAll("ping")
alarm:Reset()
end)
multi:newLoop(function()
c:update()
end)
net.OnServerCreated:Fire(c)
return c
end
function net:newClient(host,port,servercode,nonluaServer)
local c={}
c.ip=assert(socket.dns.toip(host))
c.udp=assert(socket.udp())
c.udp:settimeout(0)
c.udp:setpeername(c.ip, port)
c.cid="NIL"
c.lastPing=0
c.Type="udp"
c.servercode=servercode
c.autoReconnect=true
c.autoNormalization=false
function c:pollPing(n)
return not((os.clock()-self.lastPing)<(n or 60))
end
function c:send(data)
if self.autoNormalization then
data=net.normalize(data)
end
self.udp:send("C!"..self.cid..data)
end
function c:sendRaw(data)
if self.autoNormalization then
data=net.normalize(data)
end
self.udp:send(data)
end
function c:getCID()
if self:IDAssigned() then
return self.cid
end
end
function c:close()
self:send("E!")
end
function c:IDAssigned()
return self.cid~="NIL"
end
function c:update()
local data=self.udp:receive()
if data then
if self.autoNormalization then
data=net.denormalize(data)
end
if data:sub(1,2)=="I!" then
self.cid=data:sub(3,-1)
self.OnClientReady:Fire(self)
elseif data=="S!" then
self.udp:send("S!"..(self.servercode or ""))
elseif data=="L!" then
local mods=""
local m=""
for i=1,#net.loadedModules do
m=net.loadedModules[i]
mods=mods..m..":"..net.getModuleVersion(m).."|"
end
self.udp:send("L!"..self.cid..mods)
elseif data=="ping" then
self.lastPing=os.clock()
self.OnPingRecieved:Fire(self)
self.udp:send("pong"..self.cid)
else
local hook=data:match("!(.-)!")
self.OnDataRecieved:getConnection(hook):Fire(self,data)
end
end
end
function c:reconnect()
if not nonluaServer then
self.cid="NIL"
c.udp:send("I!")
end
self.OnConnectionRegained:Fire(self)
end
c.pingEvent=multi:newEvent(function(self) return self.link:pollPing() end)
c.pingEvent:OnEvent(function(self)
if self.link.autoReconnect then
self.link.OnServerNotAvailable:Fire("Connection to server lost: ping timeout! Attempting to reconnect...")
self.link.OnClientDisconnected:Fire(self,"closed")
self.link:reconnect()
else
self.link.OnServerNotAvailable:Fire("Connection to server lost: ping timeout!")
self.link.OnClientDisconnected:Fire(self,"closed")
end
end)
c.pingEvent.link=c
c.OnPingRecieved=multi:newConnection()
c.OnDataRecieved=multi:newConnection()
c.OnServerNotAvailable=multi:newConnection()
c.OnClientReady=multi:newConnection()
c.OnClientDisconnected=multi:newConnection()
c.OnConnectionRegained=multi:newConnection()
c.notConnected=multi:newFunction(function(self)
self:hold(3)
if self.link:IDAssigned()==false then
self.link.OnServerNotAvailable:Fire("Can't connect to the server: no response from server")
end
end)
c.notConnected.link=c
if not nonluaServer then
c.udp:send("I!")
end
multi:newLoop(function()
c:update()
end)
multi:newJob(function() c.notConnected() end)
net.OnClientCreated:Fire(c)
return c
end
--TCP Stuff
function net:newTCPServer(port)
local c={}
c.tcp=assert(socket.bind("*", port))
c.tcp:settimeout(0)
c.ip,c.port=c.tcp:getsockname()
c.ips={}
c.port=port
c.ids={}
c.bannedIPs={}
c.Type="tcp"
c.rMode="*l"
c.sMode="*l"
c.updaterRate=1
c.autoNormalization=false
c.updates={}
c.links={}
c.broad=socket.udp()
c.hostip=net.getLocalIP()
function c:broadcast(name)
local loop=multi:newTLoop(function(dt,loop)
self.broad:setoption('broadcast',true)
self.broad:sendto(name.."|"..self.Type.."|"..self.hostip..":"..self.port, "255.255.255.255", 11111)
self.broad:setoption('broadcast',false)
end,1)
end
function c:setUpdateRate(n)
self.updaterRate=n
end
function c:setReceiveMode(mode)
self.rMode=mode
end
function c:setSendMode(mode)
self.rMode=mode
end
function c:banCID(cid)
print("Function not supported on a tcp server!")
end
function c:banIP(ip)
table.insert(self.bannedIPs,cid)
end
function c:send(handle,data)
if self.autoNormalization then
data=net.normalize(data)
end
if self.sMode=="*l" then
handle:send(data.."\n")
else
handle:send(data)
end
end
function c:sendAllData(handle,data)
if self.autoNormalization then
data=net.normalize(data)
end
handle:send(data)
end
function c:pollClientModules(ip,port)
self:send(ip,"L!",port)
end
function c:CIDFrom(ip,port)
print("Method not supported when using a TCP Server!")
return "CIDs in TCP work differently!"
end
function c:sendAll(data)
for i,v in pairs(self.ips) do
self:send(v,data)
end
end
function c:sendAllBut(data,cid)
for i,v in pairs(self.ips) do
if not(cid==i) then
self:send(v,data)
end
end
end
function c:clientRegistered(cid)
return self.ips[cid]
end
function c:clientLoggedIn(cid)
return self.ips[cid]
end
function c:getUpdater(cid)
return self.updates[cid]
end
function c:update()
local client = self.tcp:accept(self.rMode)
if not client then return end
table.insert(self.ips,client)
client:settimeout(0)
--client:setoption('tcp-nodelay', true)
client:setoption('keepalive', true)
ip,port=client:getpeername()
if ip and port then
print("Got connection from: ",ip,port)
local updater=multi:newUpdater(skip)
self.updates[client]=updater
self.OnClientConnected:Fire(self,self.client,self.client,ip)
updater:OnUpdate(function(self)
local data, err = self.client:receive(self.rMode or self.Link.rMode)
if err=="closed" then
for i=1,#self.Link.ips do
if self.Link.ips[i]==self.client then
table.remove(self.Link.ips,i)
end
end
self.Link.OnClientClosed:Fire(self.Link,"Client Closed Connection!",self.client,self.client,ip)
self.Link.links[self.client]=nil -- lets clean up
self:Destroy()
end
if data then
if self.autoNormalization then
data=net.denormalize(data)
end
if net.inList(self.Link.bannedIPs,ip) then
print("We will ingore data from a banned client!")
return
end
local hook=data:match("!(.-)!")
self.Link.OnDataRecieved:getConnection(hook):Fire(self.Link,data,self.client,self.client,ip,self)
if data:sub(1,2)=="L!" then
cList=data
local list={}
for m,v in cList:gmatch("(%S-):(%S-)|") do
list[m]=v
end
self.Link.OnClientsModulesList:Fire(list,self.client,self.client,ip)
end
end
end)
updater:setSkip(self.updaterRate)
updater.client=client
updater.Link=self
function updater:setReceiveMode(mode)
self.rMode=mode
end
self.links[client]=updater
end
end
c.OnClientsModulesList=multi:newConnection()
c.OnDataRecieved=multi:newConnection()
c.OnClientClosed=multi:newConnection()
c.OnClientConnected=multi:newConnection()
multi:newLoop(function()
c:update()
end)
net.OnServerCreated:Fire(c)
return c
end
function net:newTCPClient(host,port)
local c={}
c.ip=assert(socket.dns.toip(host))
c.port=port
c.tcp=socket.connect(c.ip,port)
if not c.tcp then
print("Can't connect to the server: no response from server")
return false
end
c.tcp:settimeout(0)
--c.tcp:setoption('tcp-nodelay', true)
c.tcp:setoption('keepalive', true)
c.Type="tcp"
c.autoReconnect=true
c.rMode="*l"
c.sMode="*l"
c.autoNormalization=false
function c:setReceiveMode(mode)
self.rMode=mode
end
function c:setSendMode(mode)
self.sMode=mode
end
function c:send(data)
if self.autoNormalization then
data=net.normalize(data)
end
if self.sMode=="*l" then
ind,err=self.tcp:send(data.."\n")
else
ind,err=self.tcp:send(data)
end
if err=="closed" then
self.OnClientDisconnected:Fire(self,err)
elseif err=="timeout" then
self.OnClientDisconnected:Fire(self,err)
elseif err then
print(err)
end
end
function c:sendRaw(data)
if self.autoNormalization then
data=net.normalize(data)
end
self.tcp:send(data)
end
function c:getCID()
return "No Cid on a tcp client!"
end
function c:close()
self.tcp:close()
end
function c:IDAssigned()
return true
end
function c:update()
if not self.tcp then return end
local data,err=self.tcp:receive()
if err=="closed" then
self.OnClientDisconnected:Fire(self,err)
elseif err=="timeout" then
self.OnClientDisconnected:Fire(self,err)
elseif err then
print(err)
end
if data then
if self.autoNormalization then
data=net.denormalize(data)
end
local hook=data:match("!(.-)!")
self.OnDataRecieved:getConnection(hook):Fire(self,data)
end
end
function c:reconnect()
multi:newFunction(function(func)
self.tcp=socket.connect(self.ip,self.port)
if self.tcp==nil then
print("Can't connect to the server: No response from server!")
func:hold(3)
self:reconnect()
return
end
self.OnConnectionRegained:Fire(self)
self.tcp:settimeout(0)
--self.tcp:setoption('tcp-nodelay', true)
self.tcp:setoption('keepalive', true)
end)
end
c.event=multi:newEvent(function(event)
return event.link:IDAssigned()
end)
c.event:OnEvent(function(event)
event.link.OnClientReady:Fire(event.link)
end)
c.event.link=c
c.OnClientReady=multi:newConnection()
c.OnClientDisconnected=multi:newConnection()
c.OnDataRecieved=multi:newConnection()
c.OnConnectionRegained=multi:newConnection()
multi:newLoop(function()
c:update()
end)
net.OnClientCreated:Fire(c)
return c
end
@@ -0,0 +1,7 @@
net.OnServerCreated:connect(function(s)
print("The logging Module has been loaded onto the server!")
s.OnDataRecieved:fConnect(function(self,data,cid,ip,port)
log(tostring(ip)..":"..tostring(port),"Server-log.log")
log(data,"Server-log.log")
end)
end)
@@ -0,0 +1,7 @@
net.OnServerCreated:connect(function(s)
print("The logging Module has been loaded onto the server!")
s.OnDataRecieved:fConnect(function(self,data,cid,ip,port)
log(tostring(ip)..":"..tostring(port),"Server-log.log")
log(data,"Server-log.log")
end)
end)
@@ -0,0 +1,31 @@
net:registerModule("p2p",{1,0,0})
net.p2p.peerdata={}
--[[
PID(peerID)=<CID|IP|PORT>
]]
function net.newP2PClient(host,port)
--
end
function net.aft:init() -- calling this initilizes the library and binds it to the servers and clients created
--Server Stuff
net.OnServerCreated:connect(function(s)
print("The aft(Advance File Transfer) Module has been loaded onto the server!")
if s.Type~="udp" then
print("As of right now p2p is only avaliable using udp!")
return "ERR_NOT_UDP"
end
s.OnDataRecieved(function(self,data,cid,ip,port) -- when the server recieves data this method is triggered
--
end,"p2p") -- some new stuff
end)
--Client Stuff
net.OnClientCreated:connect(function(c)
c.OnDataRecieved(function(self,data) -- when the client recieves data this method is triggered
--
end,"p2p")
end)
end
if net.autoInit then
net.aft.init()
end
@@ -0,0 +1,31 @@
net:registerModule("p2p",{1,0,0})
net.p2p.peerdata={}
--[[
PID(peerID)=<CID|IP|PORT>
]]
function net.newP2PClient(host,port)
--
end
function net.aft:init() -- calling this initilizes the library and binds it to the servers and clients created
--Server Stuff
net.OnServerCreated:connect(function(s)
print("The aft(Advance File Transfer) Module has been loaded onto the server!")
if s.Type~="udp" then
print("As of right now p2p is only avaliable using udp!")
return "ERR_NOT_UDP"
end
s.OnDataRecieved(function(self,data,cid,ip,port) -- when the server recieves data this method is triggered
--
end,"p2p") -- some new stuff
end)
--Client Stuff
net.OnClientCreated:connect(function(c)
c.OnDataRecieved(function(self,data) -- when the client recieves data this method is triggered
--
end,"p2p")
end)
end
if net.autoInit then
net.aft.init()
end
@@ -0,0 +1,24 @@
require("net")
net:registerModule("relay",{1,0,0})
net.OnServerCreated:connect(function(s)
s.OnDataRecieved(function(self,data,CID_OR_HANDLE,IP_OR_HANDLE,PORT_OR_IP)
local IP=""
if self.Type=="tcp" then
error("Relays only work when using UDP or AUDP! They will not function with TCP! Support may one day be added for TCP!")
else
IP=IP_OR_HANDLE
end
local cmd,dat=data:match("!relay! (%S-) (.+)")
if cmd == "RELAY" then
--
elseif cmd == "LOAD" then
local cpuload=multi:getLoad()
self:send(IP_OR_HANDLE,"!relay! LOAD "..cpuload,PORT_OR_IP)
end
end,"relay")
end)
net.OnClientCreated:connect(function(c)
c.OnDataRecieved(function(self,data)
--
end,"relay")
end)
@@ -0,0 +1,48 @@
require("net")
--General Stuff
--[[ What this module does!
Adds
net.settings:init()
server:regSetting(namespace,setting)
]]
net:registerModule("settings",{1,0,0})
net.settings.config={}
function net.settings:init() -- calling this initilizes the library and binds it to the servers and clients created
--Server Stuff
net.OnServerCreated:connect(function(s)
print("The Settings Module has been loaded onto the server!")
s.OnDataRecieved(function(self,data,cid,ip,port) -- when the server recieves data this method is triggered
local namespace,args=data:match("!settings! (%s+) (.+)")
local args
if namespace then
for i,v in pairs(net.settings.config) do
args={data:match(v[1])}
if #args~=0 then
v[2]:Fire(self,data,cid,ip,port,unpack(args))
break
end
end
end
end,"settings")
function s:regSetting(namespace,settings)
if not net.settings.config[namespace] then
net.settings.config[namespace]={}
end
local connection=multi:newConnection()
table.insert(net.settings.config[namespace],{"!settings! "..namespace.." "..settings,connection})
return connection
end
end)
--Client Stuff
net.OnClientCreated:connect(function(c)
c.OnDataRecieved:(function(self,data) -- when the client recieves data this method is triggered
--First Lets make sure we are getting Setting data
end,"setings")
function sendSetting(namespace,args)
self:send("!settings! "..namespace.." "..args)
end
end)
end
if net.autoInit then
net.settings:init()
end
@@ -0,0 +1,48 @@
require("net")
--General Stuff
--[[ What this module does!
Adds
net.settings:init()
server:regSetting(namespace,setting)
]]
net:registerModule("settings",{1,0,0})
net.settings.config={}
function net.settings:init() -- calling this initilizes the library and binds it to the servers and clients created
--Server Stuff
net.OnServerCreated:connect(function(s)
print("The Settings Module has been loaded onto the server!")
s.OnDataRecieved(function(self,data,cid,ip,port) -- when the server recieves data this method is triggered
local namespace,args=data:match("!settings! (%s+) (.+)")
local args
if namespace then
for i,v in pairs(net.settings.config) do
args={data:match(v[1])}
if #args~=0 then
v[2]:Fire(self,data,cid,ip,port,unpack(args))
break
end
end
end
end,"settings")
function s:regSetting(namespace,settings)
if not net.settings.config[namespace] then
net.settings.config[namespace]={}
end
local connection=multi:newConnection()
table.insert(net.settings.config[namespace],{"!settings! "..namespace.." "..settings,connection})
return connection
end
end)
--Client Stuff
net.OnClientCreated:connect(function(c)
c.OnDataRecieved:(function(self,data) -- when the client recieves data this method is triggered
--First Lets make sure we are getting Setting data
end,"setings")
function sendSetting(namespace,args)
self:send("!settings! "..namespace.." "..args)
end
end)
end
if net.autoInit then
net.settings:init()
end
@@ -0,0 +1,179 @@
require("net")
--General Stuff
--[[ What this module does!
Adds
]]
function io.fileExists(path)
g=io.open(path or '','r')
if path =='' then
p='empty path'
return nil
end
if g~=nil and true or false then
p=(g~=nil and true or false)
end
if g~=nil then
io.close(g)
else
return false
end
return p
end
net:registerModule("sft",{1,0,0})
function net.sft:init() -- calling this initilizes the library and binds it to the servers and clients created
--Server Stuff
net.OnServerCreated:connect(function(s)
print("The sft(Simple File Transfer) Module has been loaded onto the server!")
if s.Type~="tcp" then
print("It is recomended that you use tcp to transfer files!")
end
s.transfers={}
s.OnUploadRequest=multi:newConnection() -- create a sft event
s.OnFileUploaded=multi:newConnection() -- create a sft event
s.OnDownloadRequest=multi:newConnection()
s.OnDataRecieved(function(self,data,cid,ip,port) -- when the server recieves data this method is triggered
--First Lets make sure we are getting sft data
--filename,dat=data:match("!sft! (%S-) (%S+)")
local cmd,arg1,arg2=data:match("!sft! (%S-) (%S-) (.+)")
if cmd=="tstart" then
local rets=self.OnUploadRequest:Fire(self,cid,ip,port)
for i=1,#rets do
if rets[i][1]==false then
print("Server refused to accept upload request!")
self:send(ip,"!sft! CANTUPLOAD NIL NIL",port)
return
end
end
local ID,streamable=arg1:match("(.+)|(.+)")
local file,hash=arg2:match("(.+)|(.+)")
if streamable~="NIL" then
self.transfers[ID]={bin.stream(streamable,false),hash,file}
else
self.transfers[ID]={bin.new(""),hash,file}
end
return
elseif cmd=="transfer" then
if self.transfers[arg1]~=nil then
self.transfers[arg1][1]:tackE(bin.fromhex(arg2))
--print(self.transfers[arg1][1]:getSize())
end
return
elseif cmd=="tend" then
if self.transfers[arg1]~=nil then
if self.transfers[arg1][1]:getHash(32)==self.transfers[arg1][2] then
self.OnFileUploaded:Fire(self,self.transfers[arg1][1],self.transfers[arg1][3],"Hash Good!")
else
print("Hash Error!")
self.OnFileUploaded:Fire(self,self.transfers[arg1][1],self.transfers[arg1][3],"Hash Bad!")
end
self.transfers[arg1]=nil
end
return
end
local filename=cmd
local dat=arg1
if filename==nil then return end
local rets=self.OnDownloadRequest:Fire(self,cid,ip,port)
for i=1,#rets do
if rets[i][1]==false then
print("Server refused to accept download request!")
self:send(ip,"!sft! CANTREQUEST NIL NIL",port)
return
end
end
if io.fileExists(filename) then
--Lets first load the file
local file=bin.stream(filename,false)
local size=file:getSize()
local pieceSize=512
local pieces=math.ceil(size/pieceSize)
local step=multi:newStep(1,pieces)
step.TransferID=tostring(math.random(1000,9999))
step.sender=self
step.ip=ip
step.port=port
step.pieceSize=pieceSize
step:OnStart(function(self)
self.sender:send(self.ip,"!sft! TSTART "..self.TransferID.."|"..dat.." "..filename.."|"..file:getHash(32),self.port)
end)
step:OnStep(function(pos,self)
self:hold(.01)
self.sender:send(self.ip,"!sft! TRANSFER "..self.TransferID.." "..bin.tohex(file:sub(((self.pieceSize*pos)+1)-self.pieceSize,self.pieceSize*pos)),self.port)
end)
step:OnEnd(function(self)
self.sender:send(self.ip,"!sft! TEND "..self.TransferID.." NIL",self.port)
end)
else
self:send(ip,"!sft! CANTREQUEST NIL NIL",port)
end
end,"sft")
end)
--Client Stuff
net.OnClientCreated:connect(function(c)
c.transfers={}
c.OnTransferStarted=multi:newConnection() -- create a sft event
c.OnTransferFinished=multi:newConnection() -- create a sft event
c.OnFileRequestFailed=multi:newConnection() -- create a sft event
c.OnFileUploadFailed=multi:newConnection() -- create a sft event
c.OnDataRecieved(function(self,data) -- when the client recieves data this method is triggered
--First Lets make sure we are getting sft data
local cmd,arg1,arg2=data:match("!sft! (%S-) (%S-) (.+)")
if cmd=="TSTART" then
local ID,streamable=arg1:match("(.+)|(.+)")
local file,hash=arg2:match("(.+)|(.+)")
if streamable~="NIL" then
self.transfers[ID]={bin.stream(streamable,false),hash,file}
else
self.transfers[ID]={bin.new(""),hash,file}
end
self.OnTransferStarted:Fire(self)
elseif cmd=="TRANSFER" then
self.transfers[arg1][1]:tackE(bin.fromhex(arg2))
elseif cmd=="TEND" then
if self.transfers[arg1][1]:getHash(32)==self.transfers[arg1][2] then
self.OnTransferFinished:Fire(self,self.transfers[arg1][1],self.transfers[arg1][3],"Hash Good!")
else
print("Hash Error!")
self.OnTransferFinished:Fire(self,self.transfers[arg1][1],self.transfers[arg1][3],"Hash Bad!")
end
self.transfers[arg1]=nil
elseif cmd=="CANTREQUEST" then
self.OnFileRequestFailed:Fire(self,"Could not request the file for some reason!")
elseif cmd=="CANTUPLOAD" then
self.OnFileUploadFailed:Fire(self,"Could not upload the file for some reason!")
end
end,"sft")
function c:uploadFile(filename)
if io.fileExists(filename) then
local file=bin.stream(filename,false)
local size=file:getSize()
local pieceSize=512
local pieces=math.ceil(size/pieceSize)
local step=multi:newStep(1,pieces)
step.TransferID=tostring(math.random(1000,9999))
step.sender=self
step.pieceSize=pieceSize
step:OnStart(function(self)
self.sender:send("!sft! tstart "..self.TransferID.."|NIL "..filename.."|"..file:getHash(32))
end)
step:OnStep(function(pos,self)
self:hold(.01)
self.sender:send("!sft! transfer "..self.TransferID.." "..bin.tohex(file:sub(((self.pieceSize*pos)+1)-self.pieceSize,self.pieceSize*pos)))
end)
step:OnEnd(function(self)
print("Request done!")
self.sender:send("!sft! tend "..self.TransferID.." NIL")
end)
else
self.OnFileUploadFailed:Fire(self,filename,"File does not exist!")
end
end
function c:requestFile(filename)
self:send("!sft! "..filename.." NIL NIL NIL")
end
end)
end
if net.autoInit then
net.sft.init()
end
@@ -0,0 +1,44 @@
print([[
the 'admin' module STATUS: 0
the 'aft' module STATUS: 9
the 'chatting' module STATUS: 5
the 'db' module STATUS: 1
the 'email' module STATUS: 2
the 'identity' module STATUS: 7
the 'inbox' module STATUS: 1
the 'init' module STATUS: 9 Not a module, but the core of the program
the 'logging' module STATUS: 8
the 'p2p' module STATUS: 1
the 'settings' module STATUS: 3
the 'sft' module STATUS: 10
the 'status' module STATUS: 5
the 'threading' module STATUS: 1
the 'users' module STATUS: 1
the 'version' module STATUS: 2
STATUS:
0: The idea is there but no progress to actual coding has been made
1: The idea has been coded and thoughts well defined. However only a frame is existing atm
2: The module has limited functionality, but gets its basic function through
3: The module has decent functionality, but needs much more
4: The module functions are stable and works great, however not all of the original idea has been completed
5: The module functions are stable and works great, most of the functions have been fleshed out aside for a few minor ones
6: The module has all features completed and is stable for average use, minor bugs are present.
7: The module has few to no bugs, and is currently being made more secure
8: The module is very secure and odds are it wont crash unless they were implemented wrong
9: The module has most if not all exception handled so even incorrect implementation will be 'error free' Probably wont change, but if it does it will be a huge change!
10: The module is never going to change from its current status! It is done.
NOTE: once a module reaches a status of 4, it will always be backward compatable. server-client relations will be safe!
Each module has a version
1.0.0
A.M.m
addition.Major.minor
whenever a new addition is added the A is increased... You must update to make use of that feature
When a major addition or change Major is incresed. This can be code breaking... However I will try my best to make at least client side code compatible with future changes
when a minor addition or change is made minor in incresed
Why client side stabality in code changes?
Until the update module can seamless update your modules I will ensure that client code can work regardless of server version starting from this version
]])
@@ -0,0 +1,41 @@
--[=[ About This Module!
This module is server side only! (Might add client side if requested)
Aim is to make each lane (thread) have no more than 'n' number of connections
This module hyjacks the multi:newConnection() function to seemlessly add support for threads without you having to change much
As long as each server-client connection is isolated you should be fine
The chatting module however IS NOT an isolated module, so take a look at how data was handled in that module to allow for both
threaded and non threaded use
How?
When this module is loaded all server creation is altered by passing a proxyServer instead of an actual server object
for example:
proxy=net:newTCPServer(12345)
proxy:OnDataRecieved(function(self,data,cid,ip,port)
self:send("My data!")
end)
the real server instance could be on any of the threads. Careful! While using this is seemless becareful of IO opperations!
]=]
--~ net:registerModule("threading",{1,0,0})
--~ if not(lanes) then error("Require the lanes module!") end
--~ local serverlinda = lanes.linda()
--~ net.threading.newServer=net.newServer -- store the original method
--~ net.threading.newTCPServer=net.newTCPServer -- store the original method
--~ net.threading.proxy={} -- namespace for the proxy stuff. Because of the desgin intention of both UDP/TCP servers Only one proxy is needed
print({})
--~ lanes=require("lanes")
--~ serverlinda = lanes.linda()
--~ mt={
--~ __index=function(t,k) print("IND",t,k) end,
--~ __newindex=function(t,k,v) print("NewIND",t,k,v) end,
--~ }
--~ test={}
--~ setmetatable(test,mt)
--~ test.a="hi"
--~ test.a=true
--~ g=test['a']
--~ print(test.b)
--~ setmetatable(net.threading.proxy,mt) -- set the proxies metatable, to prevent bleeding only create one server.
@@ -0,0 +1,37 @@
--[=[ About This Module!
This module is server side only! (Might add client side if requested)
Aim is to make each lane (thread) have no more than 'n' number of connections
This module hyjacks the multi:newConnection() function to seemlessly add support for threads without you having to change much
As long as each server-client connection is isolated you should be fine
The chatting module however IS NOT an isolated module, so take a look at how data was handled in that module to allow for both
threaded and non threaded use
How?
When this module is loaded all server creation is altered by passing a proxyServer instead of an actual server object
for example:
proxy=net:newTCPServer(12345)
proxy:OnDataRecieved(function(self,data,cid,ip,port)
self:send("My data!")
end)
the real server instance could be on any of the threads. Careful! While using this is seemless becareful of IO opperations!
]=]
--~ net:registerModule("threading",{1,0,0})
--~ if not(lanes) then error("Require the lanes module!") end
--~ local serverlinda = lanes.linda()
--~ net.threading.newServer=net.newServer -- store the original method
--~ net.threading.newTCPServer=net.newTCPServer -- store the original method
--~ net.threading.proxy={} -- namespace for the proxy stuff. Because of the desgin intention of both UDP/TCP servers Only one proxy is needed
lanes=require("lanes")
serverlinda = lanes.linda()
mt={
__index=function(t,k) print("IND",t,k) end,
__newindex=function(t,k,v) print("NewIND",t,k,v) end,
}
test={}
setmetatable(test,mt)
test.a="hi"
test.a=true
g=test['a']
print(test.b)
--~ setmetatable(net.threading.proxy,mt) -- set the proxies metatable, to prevent bleeding only create one server.
@@ -0,0 +1,37 @@
require("net.identity") --[[ This module extends the functions of the identity module
It aims to make the handling of users who are not online more seamless.
Without this handling offline users is a pain.
]]
net:registerModule("users",{1,0,0})
net.users.online={} -- all online users and offline users
net.users.offline={} -- all online users and offline users
net.OnServerCreated:connect(function(s)
s.OnUserLoggedIn(function(user,cid,ip,port,dTable)
--
end)
s.OnDataRecieved(function(self,data,CID_OR_HANDLE,IP_OR_HANDLE,PORT_OR_IP)
if self:userLoggedIn(cid) then -- If the user is logged in we do the tests
--
else
return
end
end,"users")
end)
net.OnClientCreated:connect(function(c)
c.OnUserList=multi:newConnection()
c.OnDataRecieved(function(self,data)
--
end,"users")
function c:searchUsers(nickname) -- sends a query to the server, returns list of userids and nicks close to the query
--
end
function c:addFriend(USERID)
--
end
function c:removeFriend(USERID)
--
end
function c:getFriends(USERID)
--
end
end)
@@ -0,0 +1,43 @@
require("net")
require("net.aft")
net:registerModule("version",{1,0,0}) -- allows communication of versions for modules
net.version.HOSTS={
["Lua"]=1,
["LuaJIT"]=1,
["Love2d"]=2, -- Yes love2d uses luaJIT, but the filesystem works a bit differently
["Corona"]=3,
}
net.version.OS={
["Windows"]=1,
["Unix"]=2,
["RPI"]=3,
}
--net.version.EOL="\60\69\110\100\45\79\102\45\70\105\108\101\45\84\114\97\110\115\102\101\114\62"
net.OnServerCreated:connect(function(s)
s.OnDataRecieved(function(self,data,CID_OR_HANDLE,IP_OR_HANDLE,PORT_OR_IP)
local cmd,arg1,arg2=data:match("!version! ")
end,"version")
s.OnClientConnected(function(self,CID_OR_HANDLE,IP_OR_HANDLE,PORT_OR_IP)
multi:newFunction(function(func) -- anom func, allows for fancy multitasking
multi:newFunction(function(self)
local range=self:newRange()
for i in range(1,#self.loadedModules) do
local mod=self.loadedModules[i]
self:send(IP_OR_HANDLE,"!version! CHECK "..mod.." NIL",PORT_OR_IP) -- sends command to client to return the version of the module
end
end)()
func=nil -- we dont want 1000s+ of these anom functions lying around
end)()-- lets call the function
self:send(IP_OR_HANDLE,"!version! HOST NIL NIL",PORT_OR_IP)
end)
end)
net.OnClientCreated:connect(function(c)
c.OnDataRecieved(function(self,data)
local cmd,module,arg1=data:match("!version! (%S+) (%S+) (%S+)")
if cmd=="CHECK" then
self:send("!version! VER "..self.loadedModules[module].." "..net.getModuleVersion(module))
elseif cmd=="UPDATE" then
--
end
end,"version")
end)
@@ -0,0 +1,37 @@
AICM={}
AICM.functions={
getAICMVersion=function(self)
return "1.0.0"
end,
}
function AICM:InitSyntax(obj,name)
obj:debug("Now using the Artificial Intelligence Communication module!")
obj.OnExtendedBlock(self.blockModule)
obj.OnCustomSyntax(self.syntaxModule)
obj:define(self.functions)
end
AICM.syntaxModule=function(self,line)
pVars,mStr=line:match("p%((.-)%)(.+)")
if pVars then
local vRef,vars=pVars:match("(.-):(.+)")
if vars:find(",") then
vars={unpack(vars:split(","))}
else
vars={vars}
end
tab={self:varExists(vRef):match(mStr)} -- self:varExists allows for all internal structures to just work
for i=1,#tab do
if vars[i] then
self._variables[vars[i]]=tab[i]
end
end
self:p() -- requried to progress the script
return {
text=line,
Type="AICMModule"
}
end
end
AICM.blockModule=function(obj,name,t,chunk,filename)
--
end
@@ -0,0 +1,71 @@
EBIM={}
EBIM.functions={
getEBIMVersion=function(self)
return "1.0.0"
end,
}
EBIM.registry={}
function EBIM:registerEBlock(name,func)
self.registry[name]=func
end
function EBIM:InitSyntax(obj,name)
obj:debug("Now using the Extended Block Interface module!")
obj.OnExtendedBlock(self.blockModule)
obj.OnCustomSyntax(self.syntaxModule)
obj:define(self.functions)
end
EBIM.syntaxModule=function(self,line)
local cmd,args=line:match("(.-) (.+):")
if cmd then
local goal=nil
local _tab={}
for i=self.pos+1,#self._cblock do
if self._cblock[i]=="end"..cmd then
goal=i
break
else
table.insert(_tab,self._cblock[i])
end
end
if goal==nil then
self:pushError("'end"..cmd.."' Expected to close '"..cmd.."'")
end
if EBIM.registry[cmd] then
EBIM.registry[cmd](self,args,_tab)
self.pos=goal+1
else
self:pushError("Unknown command: "..cmd)
end
return {
Type="EBIM-Data",
text=cmd.." Block"
}
else
return
end
end
EBIM.blockModule=function(obj,name,t,chunk,filename)
--print(">: ",obj,name,t,chunk,filename)
end
EBIM:registerEBlock("string",function(self,args,tab)
local str={}
for i=1,#tab do
table.insert(str,tab[i])
end
self:setVariable(args,table.concat(str,"\n"))
end)
EBIM:registerEBlock("list",function(self,args,tab)
local str={}
for i=1,#tab do
table.insert(str,self:varExists(tab[i]))
end
self:setVariable(args,str)
end)
EBIM:registerEBlock("dict",function(self,args,tab)
local str={}
for i=1,#tab do
local a,b=tab[i]:match("(.-):%s*(.+)")
str[a]=self:varExists(b)
end
self:setVariable(args,str)
end)
@@ -0,0 +1,44 @@
-- In an attempt to speed up my library I will use a virtual machine that runs bytecode
compiler={}
compiler.cmds={ -- list of all of the commands
EVAL="\01", -- evaluate
SPLT="\02", -- split
TRIM="\03", -- trim
VEXT="\04", -- variable exists
ILST="\05", -- is a list
LSTR="\06", -- load string
FCAL="\07", -- Function call
SVAR="\08", -- set variable
LOAD="\09", -- load file
LAOD="\10", -- _load file
DEFN="\11", -- define external functions
HCBK="\12", -- Has c Block
CMBT="\13", -- combine truths
SETB="\14", -- set block
STRT="\15", -- start
PERR="\16", -- push error
PROG="\17", -- progress
PHED="\18", -- parse header
SSLT="\19", -- split string
NEXT="\20", -- next
-- Needs refining... One step at a time right!
}
function compiler:compile(filename) -- compiles the code into bytecode
-- First we load the code but don't run it
local engine=parseManager:load(filename)
-- This captures all of the methods and important info. This also ensures that the compiler and interperter stay in sync!
local bytecodeheader=bin.new() -- header will contain the order of blocks and important flags
local bytecode=bin.newDataBuffer() -- lets leave it at unlimited size because we don't know how long it will need to be
local functions={} -- will be populated with the important methods that must be preloaded
local prebytecode={} -- this contains bytecode that has yet to be sterilized
for blockname,blockdata in pairs(engine._chunks) do
-- lets get some variables ready
local code,_type,nextblock,filename=blockdata[1],blockdata[2],blockdata.next,blockdata.file
-- note nextblock may be nil on 2 condidions. The first is when the leaking flag is disabled and the other is when the block in question was the last block defined
local lines=bin._lines(code)
print("\n["..blockname.."]\n")
for i=1,#lines do
print(lines[i])
end
end
end
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,10 @@
engine={}
function engine:init(bytecodeFile)
self.code=bin.load(bytecodeFile).data
end
--[[OP-CODES
]]
function engine:run(assessors)
--
end
Binary file not shown.
@@ -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"])