Initial code push

This commit is contained in:
Foereaper
2021-07-16 01:08:10 +02:00
parent cce176570d
commit 59d8c37120
13 changed files with 1440 additions and 1 deletions

218
Server/.lib/smallfolk.lua Normal file
View File

@ -0,0 +1,218 @@
local M = {}
Smallfolk = M
local expect_object, dump_object
local error, tostring, pairs, type, floor, huge, concat = error, tostring, pairs, type, math.floor, math.huge, table.concat
local dump_type = {}
function dump_type:string(nmemo, memo, acc)
local nacc = #acc
acc[nacc + 1] = '"'
acc[nacc + 2] = self:gsub('"', '""')
acc[nacc + 3] = '"'
return nmemo
end
function dump_type:number(nmemo, memo, acc)
acc[#acc + 1] = ("%.17g"):format(self)
return nmemo
end
function dump_type:table(nmemo, memo, acc)
--[[
if memo[self] then
acc[#acc + 1] = '@'
acc[#acc + 1] = tostring(memo[self])
return nmemo
end
nmemo = nmemo + 1
]]
memo[self] = nmemo
acc[#acc + 1] = '{'
local nself = #self
for i = 1, nself do -- don't use ipairs here, we need the gaps
nmemo = dump_object(self[i], nmemo, memo, acc)
acc[#acc + 1] = ','
end
for k, v in pairs(self) do
if type(k) ~= 'number' or floor(k) ~= k or k < 1 or k > nself then
nmemo = dump_object(k, nmemo, memo, acc)
acc[#acc + 1] = ':'
nmemo = dump_object(v, nmemo, memo, acc)
acc[#acc + 1] = ','
end
end
acc[#acc] = acc[#acc] == '{' and '{}' or '}'
return nmemo
end
function dump_object(object, nmemo, memo, acc)
if object == true then
acc[#acc + 1] = 't'
elseif object == false then
acc[#acc + 1] = 'f'
elseif object == nil then
acc[#acc + 1] = 'n'
elseif object ~= object then
if (''..object):sub(1,1) == '-' then
acc[#acc + 1] = 'N'
else
acc[#acc + 1] = 'Q'
end
elseif object == huge then
acc[#acc + 1] = 'I'
elseif object == -huge then
acc[#acc + 1] = 'i'
else
local t = type(object)
if not dump_type[t] then
error('cannot dump type ' .. t)
end
return dump_type[t](object, nmemo, memo, acc)
end
return nmemo
end
function M.dumps(object)
local nmemo = 0
local memo = {}
local acc = {}
dump_object(object, nmemo, memo, acc)
return concat(acc)
end
local function invalid(i)
error('invalid input at position ' .. i)
end
local nonzero_digit = {['1'] = true, ['2'] = true, ['3'] = true, ['4'] = true, ['5'] = true, ['6'] = true, ['7'] = true, ['8'] = true, ['9'] = true}
local is_digit = {['0'] = true, ['1'] = true, ['2'] = true, ['3'] = true, ['4'] = true, ['5'] = true, ['6'] = true, ['7'] = true, ['8'] = true, ['9'] = true}
local function expect_number(string, start)
local i = start
local head = string:sub(i, i)
if head == '-' then
i = i + 1
head = string:sub(i, i)
end
if nonzero_digit[head] then
repeat
i = i + 1
head = string:sub(i, i)
until not is_digit[head]
elseif head == '0' then
i = i + 1
head = string:sub(i, i)
else
invalid(i)
end
if head == '.' then
local oldi = i
repeat
i = i + 1
head = string:sub(i, i)
until not is_digit[head]
if i == oldi + 1 then
invalid(i)
end
end
if head == 'e' or head == 'E' then
i = i + 1
head = string:sub(i, i)
if head == '+' or head == '-' then
i = i + 1
head = string:sub(i, i)
end
if not is_digit[head] then
invalid(i)
end
repeat
i = i + 1
head = string:sub(i, i)
until not is_digit[head]
end
return tonumber(string:sub(start, i - 1)), i
end
local expect_object_head = {
t = function(string, i) return true, i end,
f = function(string, i) return false, i end,
n = function(string, i) return nil, i end,
Q = function(string, i) return -(0/0), i end,
N = function(string, i) return 0/0, i end,
I = function(string, i) return 1/0, i end,
i = function(string, i) return -1/0, i end,
['"'] = function(string, i)
local nexti = i - 1
repeat
nexti = string:find('"', nexti + 1, true) + 1
until string:sub(nexti, nexti) ~= '"'
return string:sub(i, nexti - 2):gsub('""', '"'), nexti
end,
['0'] = function(string, i)
return expect_number(string, i - 1)
end,
['{'] = function(string, i, tables)
local nt, k, v = {}
local j = 1
tables[#tables + 1] = nt
if string:sub(i, i) == '}' then
return nt, i + 1
end
while true do
k, i = expect_object(string, i, tables)
if string:sub(i, i) == ':' then
v, i = expect_object(string, i + 1, tables)
nt[k] = v
else
nt[j] = k
j = j + 1
end
local head = string:sub(i, i)
if head == ',' then
i = i + 1
elseif head == '}' then
return nt, i + 1
else
invalid(i)
end
end
end,
--[[
['@'] = function(string, i, tables)
local match = string:match('^%d+', i)
local ref = tonumber(match)
if tables[ref] then
return tables[ref], i + #match
end
invalid(i)
end,
]]
}
expect_object_head['1'] = expect_object_head['0']
expect_object_head['2'] = expect_object_head['0']
expect_object_head['3'] = expect_object_head['0']
expect_object_head['4'] = expect_object_head['0']
expect_object_head['5'] = expect_object_head['0']
expect_object_head['6'] = expect_object_head['0']
expect_object_head['7'] = expect_object_head['0']
expect_object_head['8'] = expect_object_head['0']
expect_object_head['9'] = expect_object_head['0']
expect_object_head['-'] = expect_object_head['0']
expect_object_head['.'] = expect_object_head['0']
expect_object = function(string, i, tables)
local head = string:sub(i, i)
if expect_object_head[head] then
return expect_object_head[head](string, i + 1, tables)
end
invalid(i)
end
function M.loads(string, maxsize)
if #string > (maxsize or 10000) then
error 'input too large'
end
return (expect_object(string, 1, {}))
end
return M

145
Server/SMH.lua Normal file
View File

@ -0,0 +1,145 @@
local smallfolk = smallfolk or require("smallfolk")
local SMH = {}
local links = {}
function SMH.OnReceive(event, sender, _type, prefix, _, target)
if not sender or not target or not sender.GetName or not target.GetName or type(sender) ~= "userdata" or type(target) ~= "userdata" then
return
end
if sender:GetName() == target:GetName() and _type == 7 then
local source, functionId, link, linkCount, MSG = prefix:match("(%D)(%d%d%d)(%d%d)(%d%d)(.+)");
if not source or not functionId or not link or not linkCount or not MSG then
return
end
if(source == "C") then
functionId, link, linkCount = tonumber(functionId), tonumber(link), tonumber(linkCount);
links[sender:GetGUIDLow()] = links[sender:GetGUIDLow()] or {}
links[sender:GetGUIDLow()][functionId] = links[sender:GetGUIDLow()][functionId] or {count = 0};
links[sender:GetGUIDLow()][functionId][link] = MSG;
links[sender:GetGUIDLow()][functionId].count = links[sender:GetGUIDLow()][functionId].count + 1;
if (links[sender:GetGUIDLow()][functionId].count ~= linkCount) then
return
end
local fullMessage = table.concat(links[sender:GetGUIDLow()][functionId]);
links[sender:GetGUIDLow()][functionId] = {count = 0};
local VarTable = ParseMessage(fullMessage)
if not VarTable then
return
end
if not(SMH[VarTable[1]]) then
return
end
local func = SMH[VarTable[1]][functionId]
if func then
_G[func](sender, VarTable)
end
return
end
end
end
RegisterServerEvent(30, SMH.OnReceive)
function RegisterClientRequests(config)
-- If a config table with the Prefix already exists, abort loading it into the register.
if(SMH[config.Prefix]) then
return;
end
-- Create subtable for PrefixName
SMH[config.Prefix] = {}
-- Insert function ID and function name into the register table.
for functionId, functionName in pairs(config.Functions) do
SMH[config.Prefix][functionId] = functionName
end
end
function ParseMessage(str)
local output = {}
local valTemp = {}
local typeTemp = {}
local delim = {"", "", "", "", ""}
local valMatch = "[^"..table.concat(delim).."]+"
local typeMatch = "["..table.concat(delim).."]+"
-- Get values
for value in str:gmatch(valMatch) do
table.insert(valTemp, value)
end
-- Get type from delimiter
for varType in str:gmatch(typeMatch) do
for k, v in pairs(delim) do
if(v == varType) then
table.insert(typeTemp, k)
end
end
end
-- Convert value to correct type
for k, v in pairs(valTemp) do
local varType = typeTemp[k]
if(varType == 3) then -- Ints
v = tonumber(v)
elseif(varType == 4) then -- Tables
v = smallfolk.loads(v)
elseif(varType == 5) then -- Booleans
if(v == "true") then v = true else v = false end
end
table.insert(output, v)
end
valTemp = nil
typeTemp = nil
return output
end
function Player:SendServerResponse(prefix, functionId, ...)
-- ♠ = Prefix prefix
-- ♥ = ArgumentPrefix for Strings
-- ♚ = ArgumentPrefix for Ints
-- ♛ = ArgumentPrefix for Tables
-- ♜ = ArgumentPrefix for Boolean
local arg = {...}
local splitLength = 230
local msg = "" .. prefix
for _, v in pairs(arg) do
if(type(v) == "string") then
msg = msg .. ""
elseif(type(v) == "number") then
msg = msg .. ""
elseif(type(v) == "table") then
-- use Smallfolk to convert table structure to string
v = smallfolk.dumps(v)
msg = msg .. ""
elseif(type(v) == "boolean") then
v = tostring(v)
msg = msg .. ""
end
msg = msg .. v
end
local splits = math.ceil(msg:len() / splitLength)
local send
local counter = 1
for i=1, msg:len(), splitLength do
send = string.format("%01s%02d%03d%03d", "S", functionId, counter, splits)
if ((i + splitLength) > msg:len()) then
send = send .. msg:sub(i, msg:len())
else
send = send .. msg:sub(i, i + splitLength - 1)
end
counter = counter + 1
self:SendAddonMessage(send, "", 7, self)
end
end

144
Server/SMH.lua.bak Normal file
View File

@ -0,0 +1,144 @@
local smallfolk = smallfolk or require("smallfolk")
local SMH = {}
local links = {}
function SMH.OnReceive(event, sender, _type, prefix, _, target)
if not sender or not target or not sender.GetName or not target.GetName or type(sender) ~= "userdata" or type(target) ~= "userdata" then
return
end
if sender:GetName() == target:GetName() and _type == 7 then
local source, functionId, link, linkCount, MSG = prefix:match("(%D)(%d%d%d)(%d%d)(%d%d)(.+)");
if not source or not functionId or not link or not linkCount or not MSG then
return
end
if(source == "C") then
functionId, link, linkCount = tonumber(functionId), tonumber(link), tonumber(linkCount);
links[sender:GetGUIDLow()] = links[sender:GetGUIDLow()] or {}
links[sender:GetGUIDLow()][functionId] = links[sender:GetGUIDLow()][functionId] or {count = 0};
links[sender:GetGUIDLow()][functionId][link] = MSG;
links[sender:GetGUIDLow()][functionId].count = links[sender:GetGUIDLow()][functionId].count + 1;
if (links[sender:GetGUIDLow()][functionId].count ~= linkCount) then
return
end
local fullMessage = table.concat(links[sender:GetGUIDLow()][functionId]);
links[sender:GetGUIDLow()][functionId] = {count = 0};
local VarTable = ParseMessage(fullMessage)
if not VarTable then
return
end
if not(SMH[VarTable[1]]) then
return
end
local func = SMH[VarTable[1]][functionId]
if func then
_G[func](sender, VarTable)
end
return
end
end
end
RegisterServerEvent(30, SMH.OnReceive)
function RegisterClientRequests(config)
-- If a config table with the Prefix already exists, abort loading it into the register.
if(SMH[config.Prefix]) then
return;
end
-- Create subtable for PrefixName
SMH[config.Prefix] = {}
-- Insert function ID and function name into the register table.
for functionId, functionName in pairs(config.Functions) do
SMH[config.Prefix][functionId] = functionName
end
end
function ParseMessage(str)
local output = {}
local valTemp = {}
local typeTemp = {}
local delim = {"", "", "", "", ""}
local valMatch = "[^"..table.concat(delim).."]+"
local typeMatch = "["..table.concat(delim).."]+"
-- Get values
for value in str:gmatch(valMatch) do
table.insert(valTemp, value)
end
-- Get type from delimiter
for varType in str:gmatch(typeMatch) do
for k, v in pairs(delim) do
if(v == varType) then
table.insert(typeTemp, k)
end
end
end
-- Convert value to correct type
for k, v in pairs(valTemp) do
local varType = typeTemp[k]
if(varType == 3) then -- Ints
v = tonumber(v)
elseif(varType == 4) then -- Tables
v = smallfolk.loads(v)
elseif(varType == 5) then -- Booleans
if(v == "true") then v = true else v = false end
end
table.insert(output, v)
end
valTemp = nil
typeTemp = nil
return output
end
function Player:SendServerResponse(prefix, functionId, ...)
-- ♠ = Prefix prefix
-- ♥ = ArgumentPrefix for Strings
-- ♚ = ArgumentPrefix for Ints
-- ♛ = ArgumentPrefix for Tables
-- ♜ = ArgumentPrefix for Boolean
local arg = {...}
local splitLength = 230
local msg = "" .. prefix
for _, v in pairs(arg) do
if(type(v) == "string") then
msg = msg .. ""
elseif(type(v) == "number") then
msg = msg .. ""
elseif(type(v) == "table") then
-- use Smallfolk to convert table structure to string
v = smallfolk.dumps(v)
msg = msg .. ""
elseif(type(v) == "boolean") then
v = tostring(v)
msg = msg .. ""
end
msg = msg .. v
end
local splits = math.ceil(msg:len() / splitLength)
local send
local counter = 1
for i=1, msg:len(), splitLength do
send = string.format("%01s%02d%03d%03d", "S", functionId, counter, splits)
if ((i + splitLength) > msg:len()) then
send = send .. msg:sub(i, msg:len())
else
send = send .. msg:sub(i, i + splitLength - 1)
end
counter = counter + 1
self:SendAddonMessage(send, "", 7, self)
end
end