309 lines
No EOL
7.6 KiB
Lua
309 lines
No EOL
7.6 KiB
Lua
--[[
|
|
NetStream - 1.0.2
|
|
|
|
Alexander Grist-Hucker
|
|
http://www.revotech.org
|
|
|
|
Credits to:
|
|
Alexandru-Mihai Maftei aka Vercas for vON.
|
|
https://github.com/vercas/vON
|
|
--]]
|
|
|
|
--- ## Shared
|
|
-- Networking helper library that provides easier access to networking with chunking support.
|
|
-- @module serverguard.netstream
|
|
|
|
local type, error, pcall, pairs, AddCSLuaFile, _player = type, error, pcall, pairs, AddCSLuaFile, player;
|
|
|
|
--[[
|
|
AddCSLuaFile("includes/modules/von.lua");
|
|
require("von");
|
|
--]]
|
|
|
|
AddCSLuaFile();
|
|
|
|
serverguard.netstream = serverguard.netstream or {};
|
|
|
|
local stored = {};
|
|
local chunks = {};
|
|
|
|
local bb_write, bb_read =
|
|
include "bitbuf_glua/write.lua",
|
|
include "bitbuf_glua/read.lua"
|
|
|
|
local coded =
|
|
include "bitbuf_glua/table.lua"
|
|
|
|
local function encode(t)
|
|
local buf = bb_write()
|
|
coded.write(buf, t)
|
|
return buf:ExportAsString()
|
|
end
|
|
|
|
local function decode(t)
|
|
local read = bb_read(t)
|
|
return coded.read(read)
|
|
end
|
|
|
|
serverguard.tables = {
|
|
write = function(t)
|
|
local dat = encode(t)
|
|
net.WriteUInt(#dat, 32)
|
|
net.WriteData(dat, #dat)
|
|
end,
|
|
read = function()
|
|
return decode(net.ReadData(net.ReadUInt(32)))
|
|
end
|
|
}
|
|
local tables = serverguard.tables;
|
|
|
|
--- Splits a table into a series of strings for easier networking.
|
|
-- @see serverguard.netstream.Build
|
|
-- @table data The data to split.
|
|
-- @treturn table A table of strings representing the split data.
|
|
function serverguard.netstream.Split(data)
|
|
if (type(data) != "string") then
|
|
data = encode(data)
|
|
end;
|
|
|
|
local result = {};
|
|
|
|
for i = 1, data:len(), 0x8000 do
|
|
result[#result + 1] = data:sub(i, i + 0x7fff)
|
|
end
|
|
|
|
return result;
|
|
end;
|
|
|
|
--- Reconstructs a table of split strings.
|
|
-- @see serverguard.netstream.Split
|
|
-- @table data The data to reconstruct.
|
|
-- @treturn table The reconstructed data.
|
|
function serverguard.netstream.Build(data)
|
|
return decode(table.concat(data));
|
|
end;
|
|
|
|
--- Adds a hook to a data stream.
|
|
-- @see serverguard.netstream.Start
|
|
-- @string name Name of the data stream.
|
|
-- @func callback Function to execute when the data stream has been received.
|
|
function serverguard.netstream.Hook(name, Callback)
|
|
if (!stored[name]) then
|
|
stored[name] = {};
|
|
end;
|
|
|
|
table.insert(stored[name], Callback);
|
|
end;
|
|
|
|
function serverguard.netstream.GetStored()
|
|
return stored;
|
|
end;
|
|
|
|
if (SERVER) then
|
|
local chunkQueue = {};
|
|
|
|
util.AddNetworkString("ServerGuardDS");
|
|
util.AddNetworkString("ServerGuardChunkedDS");
|
|
|
|
--- Sends a data stream to the target. Will send to client as server and vice versa.
|
|
-- @see serverguard.netstream.Hook
|
|
-- @player player The player to send the data to. You should **not** pass this argument clientside.
|
|
-- @string name The name of the data stream to send.
|
|
-- @table data The data to send.
|
|
function serverguard.netstream.Start(ply, name, data)
|
|
net.Start("ServerGuardDS");
|
|
net.WriteString(name);
|
|
tables.write {data};
|
|
|
|
if (ply) then
|
|
if (istable(ply)) then
|
|
local recipients = RecipientFilter()
|
|
|
|
for k, v in pairs(ply) do
|
|
-- FIXME: Do player finding here or assume they are player entities?
|
|
if (isplayer(v)) then
|
|
recipients:AddPlayer(v);
|
|
elseif (isplayer(k)) then
|
|
recipients:AddPlayer(k);
|
|
end;
|
|
end;
|
|
|
|
net.Send(recipients)
|
|
else
|
|
net.Send(ply)
|
|
end;
|
|
else
|
|
net.Broadcast()
|
|
end;
|
|
end;
|
|
|
|
--- Sends a data stream to the target. This is used for larger data sets that may exceed the 64kb networking limit.
|
|
-- @see serverguard.netstream.Hook
|
|
-- @player player The player to send the data to. You should **not** pass this argument clientside.
|
|
-- @string name The name of the data stream to send.
|
|
-- @table data The data to send.
|
|
function serverguard.netstream.StartChunked(pPlayer, name, data)
|
|
local recipients;
|
|
|
|
-- Skip loop if we can
|
|
if (pPlayer) then
|
|
if (istable(pPlayer)) then
|
|
recipients = {}
|
|
local pos = -1
|
|
|
|
for k, v in pairs(pPlayer) do
|
|
if (isplayer(v)) then
|
|
pos = pos + 1
|
|
recipients[pos] = v;
|
|
elseif (isplayer(k)) then
|
|
pos = pos + 1
|
|
recipients[pos] = k;
|
|
end;
|
|
end;
|
|
|
|
if (pos == -1) then
|
|
recipients = nil
|
|
end
|
|
else
|
|
recipients = {pPlayer}
|
|
end
|
|
else
|
|
recipients = player.GetAll()
|
|
end
|
|
|
|
if (recipients) then
|
|
local splitData = serverguard.netstream.Split({data = (data or 0)});
|
|
|
|
if (splitData) then
|
|
local len = #splitData
|
|
|
|
if (len ~= 0) then
|
|
chunkQueue[name] = {
|
|
recipients = recipients,
|
|
data = splitData;
|
|
};
|
|
|
|
net.Start("ServerGuardChunkedDS");
|
|
net.WriteString(name);
|
|
net.WriteUInt(1, 32);
|
|
net.WriteUInt(len, 32);
|
|
|
|
local len = #splitData[1]
|
|
net.WriteUInt(len, 32);
|
|
net.WriteData(splitData[1], len);
|
|
net.Send(recipients);
|
|
end
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
net.Receive("ServerGuardDS", function(length, pPlayer)
|
|
local NS_DS_NAME = net.ReadString();
|
|
local NS_DS_DATA = tables.read();
|
|
|
|
if (NS_DS_NAME) then
|
|
pPlayer.nsDataStreamName = NS_DS_NAME;
|
|
pPlayer.nsDataStreamData = "";
|
|
pPlayer.nsDataStreamData = NS_DS_DATA;
|
|
|
|
if (stored[pPlayer.nsDataStreamName]) then
|
|
for k, v in pairs(stored[pPlayer.nsDataStreamName]) do
|
|
v(pPlayer, NS_DS_DATA[1]);
|
|
end;
|
|
end;
|
|
|
|
pPlayer.nsDataStreamName = nil;
|
|
pPlayer.nsDataStreamData = nil;
|
|
end;
|
|
end);
|
|
|
|
net.Receive("ServerGuardChunkedDS", function(length, player)
|
|
local NS_DS_NAME = net.ReadString();
|
|
local NS_DS_NEXT = net.ReadUInt(32);
|
|
|
|
if (!chunkQueue[NS_DS_NAME] or !chunkQueue[NS_DS_NAME].data[NS_DS_NEXT] or !table.HasValue(chunkQueue[NS_DS_NAME].recipients, player)) then
|
|
return;
|
|
end;
|
|
|
|
if (NS_DS_NEXT >= #chunkQueue[NS_DS_NAME].data) then
|
|
for k, v in pairs(chunkQueue[NS_DS_NAME].recipients) do
|
|
if (v == player) then
|
|
chunkQueue[NS_DS_NAME].recipients[k] = nil;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
net.Start("ServerGuardChunkedDS");
|
|
net.WriteString(NS_DS_NAME);
|
|
net.WriteUInt(NS_DS_NEXT, 32);
|
|
net.WriteUInt(#chunkQueue[NS_DS_NAME].data, 32);
|
|
|
|
net.WriteUInt(#chunkQueue[NS_DS_NAME].data[NS_DS_NEXT], 32);
|
|
net.WriteData(chunkQueue[NS_DS_NAME].data[NS_DS_NEXT], #chunkQueue[NS_DS_NAME].data[NS_DS_NEXT]);
|
|
net.Send(player);
|
|
end);
|
|
else
|
|
function serverguard.netstream.Start(name, data)
|
|
net.Start("ServerGuardDS");
|
|
net.WriteString(name);
|
|
tables.write {data}
|
|
net.SendToServer();
|
|
end;
|
|
|
|
net.Receive("ServerGuardDS", function(length)
|
|
local NS_DS_NAME = net.ReadString();
|
|
local NS_DS_DATA = tables.read();
|
|
|
|
if (NS_DS_NAME) then
|
|
if (stored[NS_DS_NAME]) then
|
|
for k, v in pairs(stored[NS_DS_NAME]) do
|
|
v(NS_DS_DATA[1]);
|
|
end;
|
|
end;
|
|
end;
|
|
end);
|
|
|
|
net.Receive("ServerGuardChunkedDS", function(length)
|
|
local NS_DS_NAME = net.ReadString();
|
|
local NS_DS_CURRENT = net.ReadUInt(32);
|
|
local NS_DS_TOTAL = net.ReadUInt(32);
|
|
|
|
local NS_DS_LENGTH = net.ReadUInt(32);
|
|
local NS_DS_DATA = net.ReadData(NS_DS_LENGTH);
|
|
|
|
if (!NS_DS_NAME or !NS_DS_CURRENT or !NS_DS_TOTAL or !NS_DS_LENGTH or !NS_DS_DATA) then
|
|
return;
|
|
end;
|
|
|
|
if (!chunks[NS_DS_NAME]) then
|
|
chunks[NS_DS_NAME] = {};
|
|
end;
|
|
|
|
if (NS_DS_CURRENT > 0 and NS_DS_DATA) then
|
|
table.insert(chunks[NS_DS_NAME], NS_DS_DATA);
|
|
end;
|
|
|
|
if (NS_DS_CURRENT >= NS_DS_TOTAL) then
|
|
if (chunks[NS_DS_NAME] and stored[NS_DS_NAME]) then
|
|
local bStatus, value = pcall(serverguard.netstream.Build, chunks[NS_DS_NAME]);
|
|
|
|
if (bStatus) then
|
|
for k, v in pairs(stored[NS_DS_NAME]) do
|
|
v(value.data);
|
|
end;
|
|
|
|
chunks[NS_DS_NAME] = nil;
|
|
else
|
|
ErrorNoHalt("NetStream: '"..NS_DS_NAME.."'\n"..value.."\n");
|
|
end;
|
|
end;
|
|
else
|
|
net.Start("ServerGuardChunkedDS");
|
|
net.WriteString(NS_DS_NAME);
|
|
net.WriteUInt(NS_DS_CURRENT + 1, 32);
|
|
net.SendToServer();
|
|
end;
|
|
|
|
NS_DS_NAME, NS_DS_CURRENT, NS_DS_TOTAL, NS_DS_LENGTH, NS_DS_DATA = nil, nil, nil, nil, nil;
|
|
end);
|
|
end; |