--[[ Datastream Module By Janorkie and Deco da Man ]]-- require "glon" AddCSLuaFile "includes/modules/datastream.lua" AddCSLuaFile "includes/modules/glon.lua" function string.split(str,d) local t = {} local len = str:len() local i = 0 while i*d < len do t[i+1] = str:sub(i*d+1,(i+1)*d) i=i+1 end return t end if SERVER then local table = table local string = string local umsg = umsg local concommand = concommand local util = util local hook = hook local math = math local type = type local pairs = pairs local tostring = tostring local tonumber = tonumber local RecipientFilter = RecipientFilter local pcall = pcall local error = error local glon = glon local PCMod = PCMod or { GM = GAMEMODE } module "datastream" local _outgoing = {} local _incoming = {} local _hooks = {} function Hook(h,f) local hk = {} hk.handler = h hk.func = f _hooks[h] = hk end function StreamToClients( rcp, h, d, cb ) local o = {} // Recipient information local rt = type(rcp) if rt == "RecipientFilter" then o.rf = rcp elseif rt == "table" then o.rf = RecipientFilter() for k,v in pairs(rcp) do o.rf:AddPlayer(v) end elseif rt == "Player" then o.rf = rcp else error(("Invalid type %q given to datastream.StreamToClients for recipients!"):format(rcp)) end // Data information o.decdata = d o.encdata = "" o.sent = 0 o.size = 0 // Operation state o.block = 1 o.state = 1 // Operation handlers o.handler = h o.callback = cb // Put the operation into the queue and finish up o.id = table.insert(_outgoing,o) return o.id end function DownstreamActive() return #_incoming > 0 end function GetProgress(id) if _outgoing[id] then return _outgoing[id].sent end end local function CallStreamHook(pl,h,id,encdat,decdat) if not _hooks[h] then error("DataStreamServer: Unhandled stream "..h.."!") return end if hook.Call("CompletedIncomingStream",PCMod.GM,pl,h,id,encdat,decdat) then return end _hooks[h].func(pl,h,id,encdat,decdat) end local function StreamTick() for id,o in pairs(_outgoing) do if o.state == 1 then // Processing stage. if o.decdata then local enc = "" local b,err = pcall(glon.encode, o.decdata) if b then enc=err else error("DataStreamServer Encoding Error: "..err.." (operation "..o.id..")") end o.encdata = string.split(enc,128) o.state = 2 else o.state = 3 end umsg.Start("DSStart",o.rf) umsg.Long(o.id) umsg.String(o.handler) umsg.End() break elseif o.state == 2 then // Sending stage. umsg.Start("DSPacket",o.rf) umsg.Long(o.id) umsg.String(o.encdata[o.block]) umsg.End() o.block = o.block+1 if o.block > #o.encdata then o.state = 3 end break elseif o.state == 3 then // Ending stage umsg.Start("DSEnd",o.rf) umsg.Long(o.id) umsg.End() if o.callback then o.callback(o.id) end _outgoing[o.id] = nil break end end end hook.Add("Tick","DatastreamTick",StreamTick) // Default stuff that will be put in base gamemode local function AcceptStream() return true end hook.Add("AcceptStream","AcceptStream",AcceptStream) // Messages from the client local function DSRequest(pl,cmd,args) if args[1] ~= "\1" then return end local h = args[2] local id = tonumber(args[3]) if hook.Call("AcceptStream",PCMod.GM,pl,h,id) then local o = { pl = pl, handler = h, buffer = "", } o.id = table.insert(_incoming,o) umsg.Start("DSRequestOK",pl) umsg.Long(id) umsg.Long(o.id) umsg.End() else umsg.Start("DSRequestDenied",pl) umsg.Long(id) umsg.End() end end concommand.Add("__dsr",DSRequest) local function DSPacket(pl,cmd,args) if args[1] ~= "\1" then return end local id = tonumber(args[3]) if not _incoming[id] then return end local data = args[2] _incoming[id].buffer = _incoming[id].buffer..data umsg.Start("DSPacketReceived",pl) umsg.Long(id) umsg.Long(_incoming[id].buffer:len()) umsg.End() end concommand.Add("__dsp",DSPacket) local function DSEnd(pl,cmd,args) if args[1] ~= "\1" then return end local id = tonumber(args[2]) if not _incoming[id] then return end local reptab = {["\\"]="\\",["n"]="\n"} local encdat = string.gsub(_incoming[id].buffer,"\\(.)",reptab) local decdat = "" local b,err = pcall(glon.decode,encdat) if b then decdat=err else error("DataStreamServer Decoding Error: "..err.." (operation "..id..")") end CallStreamHook(pl,_incoming[id].handler,id,encdat,decdat) _incoming[id] = nil; umsg.Start("DSConfirmEnd",pl) umsg.Long(id) umsg.End() end concommand.Add("__dse",DSEnd) elseif CLIENT then local table = table local string = string local usermessage = usermessage local hook = hook local math = math local os = os local util = util local RunConsoleCommand = RunConsoleCommand local LocalPlayer = LocalPlayer local type = type local pairs = pairs local tostring = tostring local RecipientFilter = RecipientFilter local pcall = pcall local error = error local glon = glon module "datastream" local _outgoing = {} local _incoming = {} local _tempout = {} local _hooks = {} function Hook(h,f) local hk = {} hk.handler = h hk.func = f _hooks[h] = hk end function StreamToServer(h,d,cb,acb) local o = { // Data information decdata = d, encdata = "", sent = 0, size = 0, // Operation state block = 1, state = 1, // Operation handlers handler = h, callback = cb, acallback = acb, } o.id = table.insert(_tempout,o) RunConsoleCommand("__dsr","\1",h,o.id) return o.id end function DownstreamActive() return #_incoming > 0 end function GetProgress(id) if _outgoing[id] then return _outgoing[id].sent end end local function CallStreamHook(h,id,encdat,decdat) if not _hooks[h] then error("DataStreamClient: Unhandled stream "..h.."!") return end hook.Call("CompletedIncomingStream",PCMod.GM,h,id,encdat,decdat) _hooks[h].func(h,id,encdat,decdat) end local function StreamTick() for id,o in pairs(_outgoing) do if o.state == 1 then // Processing stage. if o.decdata then local enc = "" local b,err = pcall(glon.encode, o.decdata) if b then enc=err else error("DataStreamClient Encoding Error: "..err.." (operation "..o.id..")") end o.encdata = string.split(string.gsub(string.gsub(enc,"\\","\\\\"),"\n","\\n"),128) o.state = 2 else o.state = 3 end break elseif o.state == 2 then // Sending stage. RunConsoleCommand("__dsp","\1",o.encdata[o.block],o.id) o.block = o.block+1 if o.block > #o.encdata then o.state = 3 end break elseif o.state == 3 then // Ending stage RunConsoleCommand("__dse","\1",o.id) o.state = 4 end end end hook.Add("Tick","DatastreamTick",StreamTick) // Usermessages from the server local function DSStart(data) local id = data:ReadLong() local handler = data:ReadString() local o = { id = id, handler = handler, buffer = "", } _incoming[id] = o end usermessage.Hook("DSStart",DSStart) local function DSPacket(data) local id = data:ReadLong() if not _incoming[id] then return end local data = data:ReadString() _incoming[id].buffer = _incoming[id].buffer..data end usermessage.Hook("DSPacket",DSPacket) local function DSEnd(data) local id = data:ReadLong() if not _incoming[id] then return end local encdat = _incoming[id].buffer local decdat = "" local b,err = pcall(glon.decode,encdat) if b then decdat=err else error("DataStreamClient Decoding Error: "..err.." (operation "..id..")") end CallStreamHook(_incoming[id].handler,id,encdat,decdat) _incoming[id] = nil end usermessage.Hook("DSEnd",DSEnd) local function DSConfirmEnd(data) local id = data:ReadLong() local o = _outgoing[id] if not o then return end if o.callback then o.callback(o.id) end _outgoing[o.id] = nil end usermessage.Hook("DSConfirmEnd",DSConfirmEnd) // Accept/Deny Handlers local function DSRequestOK(data) local tempid = data:ReadLong() local o = _tempout[tempid] if !o then return end _tempout[tempid] = nil; o.id = data:ReadLong() _outgoing[o.id] = o if o.acallback then o.acallback(true,tempid,o.id) end end usermessage.Hook("DSRequestOK",DSRequestOK) local function DSRequestDenied(data) local tempid = data:ReadLong() local o = _tempout[tempid] if not o then return end _tempout[tempid] = nil if o.acallback then o.acallback(false, tempid) end end usermessage.Hook("DSRequestDenied",DSRequestDenied) // Packet Confirmation Message local function DSPacketReceived(data) local id = data:ReadLong() if not _outgoing[id] then return end _outgoing[id].sent = data:ReadLong() end usermessage.Hook("DSPacketReceived",DSPacketReceived) end