dobrograd-13-06-2022/garrysmod/addons/admin-sg/lua/modules/sh_netstream.lua
Jonny_Bro (Nikita) e4d5311906 first commit
2023-11-16 15:01:19 +05:00

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;