mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-15 21:03:46 +03:00
458 lines
10 KiB
Lua
458 lines
10 KiB
Lua
--[[
|
|
| This file was obtained through the combined efforts
|
|
| of Madbluntz & Plymouth Antiquarian Society.
|
|
|
|
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
|
| Maloy, DrPepper10 @ RIP, Atle!
|
|
|
|
|
| Visit for more: https://plymouth.thetwilightzone.ru/
|
|
--]]
|
|
|
|
if SAM_LOADED then return end
|
|
|
|
--[[
|
|
|
|
DEVELOPMENTAL VERSION;
|
|
|
|
VERSION 1.2.2
|
|
Copyright thelastpenguin™
|
|
|
|
You may use this for any purpose as long as:
|
|
- You don't remove this copyright notice.
|
|
- You don't claim this to be your own.
|
|
- You properly credit the author, thelastpenguin™, if you publish your work based on (and/or using) this.
|
|
|
|
If you modify the code for any purpose, the above still applies to the modified code.
|
|
|
|
The author is not held responsible for any damages incured from the use of pon, you use it at your own risk.
|
|
|
|
DATA TYPES SUPPORTED:
|
|
- tables - k,v - pointers
|
|
- strings - k,v - pointers
|
|
- numbers - k,v
|
|
- booleans- k,v
|
|
|
|
- Vectors - k,v
|
|
- Angles - k,v
|
|
- Entities- k,v
|
|
- Players - k,v
|
|
|
|
CHANGE LOG
|
|
V 1.1.0
|
|
- Added Vehicle, NPC, NextBot, Player, Weapon
|
|
V 1.2.0
|
|
- Added custom handling for k,v tables without any array component.
|
|
V 1.2.1
|
|
- fixed deserialization bug.
|
|
|
|
THANKS TO...
|
|
- VERCAS for the inspiration.
|
|
]]
|
|
local pon = {}
|
|
sam.pon = pon
|
|
|
|
do
|
|
local type = sam.type
|
|
local IsColor = IsColor
|
|
local tonumber = tonumber
|
|
local format = string.format
|
|
local encode = {}
|
|
local cacheSize = 0
|
|
|
|
encode['table'] = function(self, tbl, output, cache)
|
|
if cache[tbl] then
|
|
output[#output + 1] = format('(%x)', cache[tbl])
|
|
|
|
return
|
|
else
|
|
cacheSize = cacheSize + 1
|
|
cache[tbl] = cacheSize
|
|
end
|
|
|
|
local first = next(tbl, nil)
|
|
local predictedNumeric = 1
|
|
|
|
-- starts with a numeric dealio
|
|
if first == 1 then
|
|
output[#output + 1] = '{'
|
|
|
|
for k, v in next, tbl do
|
|
if k == predictedNumeric then
|
|
predictedNumeric = predictedNumeric + 1
|
|
local tv = type(v)
|
|
|
|
if tv == 'string' then
|
|
local pid = cache[v]
|
|
|
|
if pid then
|
|
output[#output + 1] = format('(%x)', pid)
|
|
else
|
|
cacheSize = cacheSize + 1
|
|
cache[v] = cacheSize
|
|
self.string(self, v, output, cache)
|
|
end
|
|
elseif IsColor(v) then
|
|
self.Color(self, v, output, cache)
|
|
else
|
|
self[tv](self, v, output, cache)
|
|
end
|
|
else
|
|
break
|
|
end
|
|
end
|
|
|
|
predictedNumeric = predictedNumeric - 1
|
|
else
|
|
predictedNumeric = nil
|
|
end
|
|
|
|
if predictedNumeric == nil then
|
|
output[#output + 1] = '[' -- no array component
|
|
else
|
|
output[#output + 1] = '~' -- array component came first so shit needs to happen
|
|
end
|
|
|
|
for k, v in next, tbl, predictedNumeric do
|
|
local tk, tv = type(k), type(v)
|
|
if not self[tk] or not self[tv] then continue end
|
|
|
|
-- WRITE KEY
|
|
if tk == 'string' then
|
|
local pid = cache[k]
|
|
|
|
if pid then
|
|
output[#output + 1] = format('(%x)', pid)
|
|
else
|
|
cacheSize = cacheSize + 1
|
|
cache[k] = cacheSize
|
|
self.string(self, k, output, cache)
|
|
end
|
|
elseif IsColor(v) then
|
|
self.Color(self, v, output, cache)
|
|
else
|
|
self[tk](self, k, output, cache)
|
|
end
|
|
|
|
-- WRITE VALUE
|
|
if tv == 'string' then
|
|
local pid = cache[v]
|
|
|
|
if pid then
|
|
output[#output + 1] = format('(%x)', pid)
|
|
else
|
|
cacheSize = cacheSize + 1
|
|
cache[v] = cacheSize
|
|
self.string(self, v, output, cache)
|
|
end
|
|
elseif IsColor(v) then
|
|
self.Color(self, v, output, cache)
|
|
else
|
|
self[tv](self, v, output, cache)
|
|
end
|
|
end
|
|
|
|
output[#output + 1] = '}'
|
|
end
|
|
|
|
-- ENCODE STRING
|
|
local gsub = string.gsub
|
|
|
|
encode['string'] = function(self, str, output)
|
|
--if tryCache(str, output then return end
|
|
local estr, count = gsub(str, ';', "\\;")
|
|
|
|
if count == 0 then
|
|
output[#output + 1] = '\'' .. str .. ';'
|
|
else
|
|
output[#output + 1] = '"' .. estr .. '";'
|
|
end
|
|
end
|
|
|
|
-- ENCODE NUMBER
|
|
encode['number'] = function(self, num, output)
|
|
if num % 1 == 0 then
|
|
if num < 0 then
|
|
output[#output + 1] = format('x%x;', -num)
|
|
else
|
|
output[#output + 1] = format('X%x;', num)
|
|
end
|
|
else
|
|
output[#output + 1] = tonumber(num) .. ';'
|
|
end
|
|
end
|
|
|
|
-- ENCODE BOOLEAN
|
|
encode['boolean'] = function(self, val, output)
|
|
output[#output + 1] = val and 't' or 'f'
|
|
end
|
|
|
|
-- ENCODE VECTOR
|
|
encode['Vector'] = function(self, val, output)
|
|
output[#output + 1] = ('v' .. val.x .. ',' .. val.y) .. (',' .. val.z .. ';')
|
|
end
|
|
|
|
-- ENCODE ANGLE
|
|
encode['Angle'] = function(self, val, output)
|
|
output[#output + 1] = ('a' .. val.p .. ',' .. val.y) .. (',' .. val.r .. ';')
|
|
end
|
|
|
|
encode['Entity'] = function(self, val, output)
|
|
output[#output + 1] = 'E' .. (IsValid(val) and (val:EntIndex() .. ';') or '#')
|
|
end
|
|
|
|
encode['Player'] = encode['Entity']
|
|
encode['Vehicle'] = encode['Entity']
|
|
encode['Weapon'] = encode['Entity']
|
|
encode['NPC'] = encode['Entity']
|
|
encode['NextBot'] = encode['Entity']
|
|
encode['PhysObj'] = encode['Entity']
|
|
|
|
encode['Color'] = function(self, val, output)
|
|
output[#output + 1] = ('C' .. val.r .. ',' .. val.g .. ',' .. val.b) .. (',' .. val.a .. ';')
|
|
end
|
|
|
|
encode['console'] = function(self, val, output)
|
|
output[#output + 1] = 's'
|
|
end
|
|
|
|
encode['nil'] = function(self, val, output)
|
|
output[#output + 1] = '?'
|
|
end
|
|
|
|
encode.__index = function(key)
|
|
ErrorNoHalt('Type: ' .. key .. ' can not be encoded. Encoded as as pass-over value.')
|
|
|
|
return encode['nil']
|
|
end
|
|
|
|
do
|
|
local concat = table.concat
|
|
|
|
function pon.encode(tbl)
|
|
local output = {nil, nil, nil, nil, nil, nil, nil, nil}
|
|
cacheSize = 0
|
|
encode['table'](encode, tbl, output, {})
|
|
|
|
return concat(output)
|
|
end
|
|
end
|
|
end
|
|
|
|
do
|
|
local tonumber = tonumber
|
|
local find, sub, gsub, Explode = string.find, string.sub, string.gsub, string.Explode
|
|
local Vector, Angle, Entity = Vector, Angle, Entity
|
|
local decode = {}
|
|
|
|
decode['{'] = function(self, index, str, cache)
|
|
local cur = {}
|
|
cache[#cache + 1] = cur
|
|
local k, v, tk, tv = 1, nil, nil, nil
|
|
|
|
while (true) do
|
|
tv = sub(str, index, index)
|
|
|
|
if not tv or tv == '~' then
|
|
index = index + 1
|
|
break
|
|
end
|
|
|
|
if tv == '}' then return index + 1, cur end
|
|
-- READ THE VALUE
|
|
index = index + 1
|
|
index, v = self[tv](self, index, str, cache)
|
|
cur[k] = v
|
|
k = k + 1
|
|
end
|
|
|
|
while (true) do
|
|
tk = sub(str, index, index)
|
|
|
|
if not tk or tk == '}' then
|
|
index = index + 1
|
|
break
|
|
end
|
|
|
|
-- READ THE KEY
|
|
index = index + 1
|
|
index, k = self[tk](self, index, str, cache)
|
|
-- READ THE VALUE
|
|
tv = sub(str, index, index)
|
|
index = index + 1
|
|
index, v = self[tv](self, index, str, cache)
|
|
cur[k] = v
|
|
end
|
|
|
|
return index, cur
|
|
end
|
|
|
|
decode['['] = function(self, index, str, cache)
|
|
local cur = {}
|
|
cache[#cache + 1] = cur
|
|
local k, v, tk, tv = 1, nil, nil, nil
|
|
|
|
while (true) do
|
|
tk = sub(str, index, index)
|
|
|
|
if not tk or tk == '}' then
|
|
index = index + 1
|
|
break
|
|
end
|
|
|
|
-- READ THE KEY
|
|
index = index + 1
|
|
index, k = self[tk](self, index, str, cache)
|
|
if not k then continue end
|
|
-- READ THE VALUE
|
|
tv = sub(str, index, index)
|
|
index = index + 1
|
|
|
|
if not self[tv] then
|
|
print('did not find type: ' .. tv)
|
|
end
|
|
|
|
index, v = self[tv](self, index, str, cache)
|
|
cur[k] = v
|
|
end
|
|
|
|
return index, cur
|
|
end
|
|
|
|
-- STRING
|
|
decode['"'] = function(self, index, str, cache)
|
|
local finish = find(str, '";', index, true)
|
|
local res = gsub(sub(str, index, finish - 1), '\\;', ';')
|
|
index = finish + 2
|
|
cache[#cache + 1] = res
|
|
|
|
return index, res
|
|
end
|
|
|
|
-- STRING NO ESCAPING NEEDED
|
|
decode['\''] = function(self, index, str, cache)
|
|
local finish = find(str, ';', index, true)
|
|
local res = sub(str, index, finish - 1)
|
|
index = finish + 1
|
|
cache[#cache + 1] = res
|
|
|
|
return index, res
|
|
end
|
|
|
|
-- NUMBER
|
|
decode['n'] = function(self, index, str)
|
|
index = index - 1
|
|
local finish = find(str, ';', index, true)
|
|
local num = tonumber(sub(str, index, finish - 1))
|
|
index = finish + 1
|
|
|
|
return index, num
|
|
end
|
|
|
|
decode['0'] = decode['n']
|
|
decode['1'] = decode['n']
|
|
decode['2'] = decode['n']
|
|
decode['3'] = decode['n']
|
|
decode['4'] = decode['n']
|
|
decode['5'] = decode['n']
|
|
decode['6'] = decode['n']
|
|
decode['7'] = decode['n']
|
|
decode['8'] = decode['n']
|
|
decode['9'] = decode['n']
|
|
decode['-'] = decode['n']
|
|
|
|
-- positive hex
|
|
decode['X'] = function(self, index, str)
|
|
local finish = find(str, ';', index, true)
|
|
local num = tonumber(sub(str, index, finish - 1), 16)
|
|
index = finish + 1
|
|
|
|
return index, num
|
|
end
|
|
|
|
-- negative hex
|
|
decode['x'] = function(self, index, str)
|
|
local finish = find(str, ';', index, true)
|
|
local num = -tonumber(sub(str, index, finish - 1), 16)
|
|
index = finish + 1
|
|
|
|
return index, num
|
|
end
|
|
|
|
-- POINTER
|
|
decode['('] = function(self, index, str, cache)
|
|
local finish = find(str, ')', index, true)
|
|
local num = tonumber(sub(str, index, finish - 1), 16)
|
|
index = finish + 1
|
|
|
|
return index, cache[num]
|
|
end
|
|
|
|
-- BOOLEAN. ONE DATA TYPE FOR YES, ANOTHER FOR NO.
|
|
decode['t'] = function(self, index) return index, true end
|
|
decode['f'] = function(self, index) return index, false end
|
|
|
|
-- VECTOR
|
|
decode['v'] = function(self, index, str)
|
|
local finish = find(str, ';', index, true)
|
|
local vecStr = sub(str, index, finish - 1)
|
|
index = finish + 1 -- update the index.
|
|
local segs = Explode(',', vecStr, false)
|
|
|
|
return index, Vector(tonumber(segs[1]), tonumber(segs[2]), tonumber(segs[3]))
|
|
end
|
|
|
|
-- ANGLE
|
|
decode['a'] = function(self, index, str)
|
|
local finish = find(str, ';', index, true)
|
|
local angStr = sub(str, index, finish - 1)
|
|
index = finish + 1 -- update the index.
|
|
local segs = Explode(',', angStr, false)
|
|
|
|
return index, Angle(tonumber(segs[1]), tonumber(segs[2]), tonumber(segs[3]))
|
|
end
|
|
|
|
-- ENTITY
|
|
decode['E'] = function(self, index, str)
|
|
if str[index] == '#' then
|
|
index = index + 1
|
|
|
|
return index, NULL
|
|
else
|
|
local finish = find(str, ';', index, true)
|
|
local num = tonumber(sub(str, index, finish - 1))
|
|
index = finish + 1
|
|
|
|
return index, Entity(num)
|
|
end
|
|
end
|
|
|
|
-- COLOR
|
|
decode['C'] = function(self, index, str)
|
|
local finish = find(str, ';', index, true)
|
|
local colStr = sub(str, index, finish - 1)
|
|
index = finish + 1 -- update the index.
|
|
local segs = Explode(',', colStr, false)
|
|
|
|
return index, Color(segs[1], segs[2], segs[3], segs[4])
|
|
end
|
|
|
|
-- PLAYER
|
|
decode['P'] = function(self, index, str)
|
|
local finish = find(str, ';', index, true)
|
|
local num = tonumber(sub(str, index, finish - 1))
|
|
index = finish + 1
|
|
|
|
return index, Entity(num) or NULL
|
|
end
|
|
|
|
-- NIL
|
|
decode['?'] = function(self, index) return index + 1, nil end
|
|
-- SAM CONSOLE
|
|
decode['s'] = function(self, index) return index, sam.console end
|
|
|
|
function pon.decode(data)
|
|
local _, res = decode[sub(data, 1, 1)](decode, 2, data, {})
|
|
|
|
return res
|
|
end
|
|
end |