--Server --No shared for brevity so we have to rethrow crap in. --Constants FLG_NEWSTREAM = 0 FLG_DATA = 1 TYP_NUM = 105 TYP_STR = 106 TYP_PTR = 107 MAX_STR = 128 --Variables stream = {} streamc = {} filter = {} streams = {} function Stream(name,scp) local strm = startStream(name) table.insert(scp.strmindex,name) for _,v in pairs(scp:GetPlayers()) strm:AddStream(startStreamPlayer(name,v)) end strm.queue = {} return strm end function StreamAdd(name,ply) streams[name]:AddStream(ply) for k,v in pairs(self.queue) do streams[name]:Queue(v) end end function startStream(name) --this is a container for k,v in pairs(streamc) do o[k]=v end o.streams = {} --individual player streams end function streamc:AddStream(strm) table.insert(self.streams,strm) end function streamc:Run() 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.wait = 10 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) id = id or #self.data pid = pid or 0 umsg.Start("stream_umsg") umsg.Short(flag) umsg.String(name) umsg.Short(id) umsg.Short(pid) if (typ == TYP_NUM) then umsg.Long(data) elseif (typ = TYP_STR) umsg.String(data) end --num,string or nothing (pointer) umsg.End() end function streamc:Queue(name,data,pid) local typ = type(data) local pid = pid or 0 if (typ == "number") then table.insert(self.queue,{typ=TYP_NUM,data=data,pid=pid}) 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,{typ=TYP_PRT_TAB,id=#self.queue+1,pid=pid}) self:TableParse(data,#self.queue) elseif (typ == "string") then if #data > MAX_STR then table.insert(self.queue,{typ=TYP_PRT_STR,id=#self.queue+1,pid=pid}) local sid = #self.queue while (true) do if #data > MAX_STR then local addStr=data:sub(0,127) data = data:gsub(addStr,'') self:Queue("",addStr,sid) else break end end else table.insert(self.queue,{typ=TYP_STR,id=#self.queue+1,pid=pid}) 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() if not self.hook then self.hook = 0 end self.hook = self.hook + 1 if self.hook > self.wait then --do stream code here. end end function filter:New() 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) 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 stream:Run() end end hook.Add("Think","server side stream hook",streamHook) --Client --No shared for brevity so we have to rethrow crap in. --Constants FLG_NEWSTREAM = 0 FLG_DATA = 1 --Data is being sent TYP_NUM = 105 TYP_STR = 106 TYP_PTR_STR = 107 TYP_PTR_TAB = 107 --Variables stream = {} strTable = {} --holds all streams. 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 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) 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 = true --PTR is a table or string (they're cut up into chunks) end --chuck all that into our function strTable[name]:RecievePacket(name,sndtype,sndid,sndpid,snddata) end end usermessage.Hook("stream_umsg",streamHook) function stream:New(name) local o = {} for k,v in paris(self) do o[k]=v end o.queue = {} o.data = {} o.name = name stream[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.data[tab[pid].ptr]=self.data[tab[pid].ptr]..data --gnarly line of code o_O return end end if (stype == TYP_PTR_TAB) then 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 = self.queue[id].type = stype end if (tab ~= self.queue) then tab = self.data --if we haven't changed to another table, change to self.data end tab[name]=data --short and sweet (finally) end