--Server --No shared for brevity so we have to rethrow crap in.--ConstantsFLG_NEWSTREAM = 0FLG_DATA = 1TYP_NUM = 105TYP_STR = 106TYP_PTR = 107MAX_STR = 128--Variablesstream = {}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 strmendfunction StreamAdd(name,ply) streams[name]:AddStream(ply) for k,v in pairs(self.queue) do streams[name]:Queue(v) endendfunction startStream(name) --this is a container for k,v in pairs(streamc) do o[k]=v end o.streams = {} --individual player streamsendfunction streamc:AddStream(strm) table.insert(self.streams,strm)endfunction streamc:Run() for _,v in pairs(self.streams) do v:Run() endendfunction 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;endfunction streamFilter() return filter:New()endfunction stream:Start() --notifies client of new stream (internal) self:Send(FLG_NEWSTREAM,self.name,TYP_PTR)endfunction 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()endfunction 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 endendfunction streamc:TableParse(t, tid) for i,v in pairs(t) do self:Queue(i,v,tid) endendfunction 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. endendfunction 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 oendfunction filter:AddPlayer(ply) table.insert(self.players,ply)endfunction 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) endendfunction filter:GetPlayers() return self.playersendfunction streamHook() for _,v in pairs(streams) do stream:Run() endendhook.Add("Think","server side stream hook",streamHook)--Client--No shared for brevity so we have to rethrow crap in.--ConstantsFLG_NEWSTREAM = 0FLG_DATA = 1 --Data is being sentTYP_NUM = 105TYP_STR = 106TYP_PTR_STR = 107TYP_PTR_TAB = 107--Variablesstream = {}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) endendusermessage.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] = oendfunction stream:GetTable() return self.dataendfunction 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