--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