--No shared for brevity so we have to rethrow crap in.
--Constants
FLG_NEWSTREAM = 0
FLG_DATA = 1
FLG_CLOSE = 4
FLG_RESET = 5
TYP_NUM = 105
TYP_STR = 106
TYP_PTR_STR = 107
TYP_PTR_TAB = 108
MAX_STR = 128
DEBUG = true
--Variables
stream = {}
streamc = {}
filter = {}
streams = {}
function debugMsg(s)
if DEBUG then Msg(s.."\n") end
end
function Stream(name,scp)
--starts a new container stream and shits it all up and ready.
local strm = startStream(name)
debugMsg("New master-stream ("..name..")")
table.insert(scp.strmindex,name)
strm.queue = {}
for _,v in pairs(scp:GetPlayers()) do
strm:AddStream(startStreamPlayer(name,v))
debugMsg("Adding sub-stream for ("..tostring(v)..")")
end
return strm
end
function StreamAdd(name,ply)
--o_O
streams[name]:AddStream(ply)
end
function startStream(name)
--this is a container
local o = {}
for k,v in pairs(streamc) do o[k]=v end
o.streams = {} --individual player streams
streams[name] = o
return o
end
function streamc:AddStream(strm)
--adds an internal stream to a containers table, and links it's queue
table.insert(self.streams,strm)
strm.queue = self.queue
end
function streamc:Run()
--runs a stream internal thingy
for _,v in pairs(self.streams) do
v:Run()
end
end
function startStreamPlayer(name,ply)
local o = {}
--really dumb way to do this
for k,v in pairs(stream) do o[k]=v end
o.data = {} --holds local copy of data.
o.sync = {} --holds sync info (what to send etc etc)
o.name = name --stream name
o.rcp = RecipientFilter()
o.rcp:AddPlayer(ply)
o.qPos = 1
o.wait = 10
o.hook = 0
o:Start()
return o;
end
function streamFilter()
return filter:New()
end
function stream:Start()
--notifies client of new stream (internal)
self:Send(FLG_NEWSTREAM,self.name,TYP_PTR)
end
function stream:Send(flag,name,typ,id,pid,data)
//this all around function sends all data
//it sends the queue shit or FLAGS and what not
//(flags like Closer and Reseter'r)
umsg.Start("stream_umsg")
umsg.Short(flag)
umsg.String(self.name)
if (flag == FLG_DATA) then
/*debugMsg("\nSending data!")
debugMsg("Stream:"..self.name)
debugMsg("Name:"..name)
debugMsg("Type:"..typ)
debugMsg("ID:"..id)
debugMsg("PID:"..pid)
if data then
debugMsg("Data:"..data)
end
debugMsg("This should show up on the client now!\n")*/
umsg.Short(typ) //hurr I forgot this...
umsg.Long(id)
umsg.Long(pid)
if (typ == TYP_NUM) then
umsg.Long(data)
elseif (typ == TYP_STR) then
umsg.String(data)
end --num,string or nothing (pointer)
umsg.String(name)
end
umsg.End()
end
function streamc:Queue(name,data,pid) //parses and queues up data.
local typ = type(data) //(no return, tables in internal streams are linked to the container's
local pid = pid or 0
if (typ == "number") then
table.insert(self.queue,{name=name,typ=TYP_NUM,data=data,pid=pid,data=data})
local id = #self.queue
local tab = self.queue[id]
tab.id=id
tab.pid=pid
tab.name=name
elseif (typ == "table") then
table.insert(self.queue,{name=name,typ=TYP_PTR_TAB,id=#self.queue+1,pid=pid})
self:TableParse(data,#self.queue)
elseif (typ == "string") then
if #data > MAX_STR then //this chunk loops infintely, we need to fix that...
table.insert(self.queue,{name=name,typ=TYP_PTR_STR,id=#self.queue+1,pid=pid})
local sid = #self.queue
local chk = true
while (chk) do
if #data > MAX_STR then
local addStr=data:sub(0,127)
data = data:gsub(addStr,'')
self:Queue("",addStr,sid)
else
chk=false
end
end
else
table.insert(self.queue,{name=name,typ=TYP_STR,id=#self.queue+1,pid=pid,data=data})
end
end
end
function streamc:TableParse(t, tid)
for i,v in pairs(t) do
self:Queue(i,v,tid)
end
end
function stream:Run()
//runs all the streaming bits, gets tables data and sends it with the umsg'r
self.hook = self.hook + 1
if self.hook > self.wait then
if (self.qPos <= #self.queue) then //only send data if the queue is bigger than
local qTab = self.queue[self.qPos] //our current position (has new data)
local typ = qTab.typ
local id = qTab.id
local pid = qTab.pid
local data = qTab.data or false
local name = qTab.name or ""
self:Send(FLG_DATA,name,typ,id,pid,data) //send with data flag and all info.
self.qPos = self.qPos + 1
end
self.hook = 0
end
end --called by container (I was gonna hook this >.>)
function stream:Close() --internal stream close
self:Send(FLG_CLOSE)
end
function streamc:Close() --closes all child streams and deletes itself(!)
for k,v in pairs(self.streams) do
v:Close()
end
streams[self.name] = nil --bam!
end
function streamc:Reset() --makes a constainer call all it's child streams and resets them.
self.queue = {}
for _,v in pairs(self.streams) do
v.qPos = 1
self:Send(FLG_RESET)
end
end
function filter:New()
--this is a weird work around to the lack of getting players from a recipient filter, lets me do crazy shit.
local o = {}
for k,v in pairs(filter) do o[k]=v end
o.strmindex = {}
o.players = {}
o.playerhook = function (ply) if self.all then self:AddPlayer(ply) for k,v in pairs(self.strmindex) do StreamAdd(v,ply) end end end
hook.Add("PlayerInitialSpawn","weird thing"..math.random(1,10000),o.playerhook)
return o
end
function filter:AddPlayer(ply)
table.insert(self.players,ply)
end
function filter:AddAllPlayers(ply)
self.all = true -- we're sending to all make sure of that
local a = player.GetAll()
for _,v in pairs(a) do
self:AddPlayer(v)
end
end
function filter:GetPlayers()
return self.players
end
function streamHook()
for _,v in pairs(streams) do
v:Run()
end
end
hook.Add("Think","server side stream hook",streamHook)
--test code
myFil = streamFilter()
myFil:AddAllPlayers()
mStream = Stream("test_stream",myFil) --have to self populate...
timer.Simple(15,function () tStream = startStreamPlayer("test_stream",player.GetByID(1)) tStream:Start() mStream:AddStream(tStream) tStream.wait = 2 end)
test_tab = {}
for i=1,100 do
test_tab[i]=(i+1)/2^3
end
for i=1,100 do
test_tab[math.random(1,100)]="THIS IS RANDOM OLOL?"
end
mStream:Queue("test_data",test_tab)
--Clientside
--No shared for brevity so we have to rethrow crap in.
--Constants
FLG_NEWSTREAM = 0
FLG_DATA = 1 --Data is being sent
FLG_CLOSE = 4
FLG_RESET = 5
TYP_NUM = 105
TYP_STR = 106
TYP_PTR_STR = 107
TYP_PTR_TAB = 108
DEBUG = true --for awhile, keep it this way.
--Variables
stream = {}
strTable = {} --holds all streams.
function debugMsg(s)
if DEBUG then Msg(s.."\n") end
end
function streamHook(data)
--shorts are friggen long, I was hoping for like flag passing, this could do fuckin' math.
local flags = data:ReadShort() --first short, is ALWAYS, the flags.
local name = data:ReadString() --first string is the stream name
debugMsg("Stream:"..name)
debugMsg("Flags:"..tostring(flags))
if (flags == FLG_NEWSTREAM) then
stream:New(name)
elseif (flags == FLG_DATA) then
local sndtype = data:ReadShort() --second short is type
local sndid = data:ReadLong() --first long is this ones id
local sndpid = data:ReadLong() --2nd long is a pointer id (itself if not applicable)
debugMsg("Data Sent!")
debugMsg("Type:"..sndtype)
debugMsg("ID:"..sndid)
debugMsg("Pointer:"..sndpid)
local snddata
if (sndtype == TYP_NUM) then
snddata = data:ReadLong()
elseif (sndtype == TYP_STR) then
snddata = data:ReadString() --a part of a string probably
elseif (sndtype == TYP_PTR_STR or sndtype == TYP_PTR_TAB) then
snddata = false --PTR is a table or string (they're cut up into chunks)
end
local sndname = data:ReadString() --name
debugMsg("Name:"..sndname)
if snddata then debugMsg("Data:"..snddata.."\n\n") end
--chuck all that into our function
if not strTable[name] then debugMsg("No stream ("..name..")") return end
strTable[name]:RecievePacket(sndname,sndtype,sndid,sndpid,snddata)
elseif (flags == FLG_CLOSE) then
strTable[name] = nil --gets rid of our stream
elseif (flags == FLG_RESET) then --clears data and reset the queue (server and client side)
strTable[name].queue = {}
strTable[name].data = {}
end
end
usermessage.Hook("stream_umsg",streamHook)
function stream:New(name)
local o = {}
for k,v in pairs(self) do o[k]=v end
o.queue = {}
o.data = {}
o.name = name
strTable[name] = o
end
function stream:GetTable()
return self.data
end
function stream:RecievePacket(name,stype,id,pid,data)
local tab = self.queue
if (pid ~= 0) then
if (tab[pid] ~= nil) then
if (tab[pid].type == TYP_PTR_TAB) then
tab = tab[pid].ptr
elseif (tab[pid].type == TYP_PTR_STR) then
self.queue[pid].ptr[name]=self.queue[pid].ptr[name]..data //crazy line of code o_O
return
end
end
end
if (tab == self.queue) then
tab = self.data //if we haven't changed to another table, change to self.data
end
if (stype == TYP_PTR_TAB) then
debugMsg("New Table pointer: ("..name..")")
tab[name] = {}
self.queue[id] = {}
self.queue[id].ptr = tab[name]
self.queue[id].type = stype
elseif (stype == TYP_PTR_STR) then
tab[name] = ""
self.queue[id] = {}
self.queue[id].ptr = tab
self.queue[id].name = name
self.queue[id].type = stype
else
tab[name]=data //short and sweet (finally)
end
end