--[[ | 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/ --]] --[[ 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 - PhysObj - 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 = {}; _G.pon = pon; local type, count = type, table.Count ; local tonumber = tonumber ; local format = string.format; do local type, count = type, table.Count ; local tonumber = tonumber ; local format = string.format; local encode = {}; local tryCache ; 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 local lastKey = nil -- 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 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) -- 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 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 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 ) local entIndex = val == NULL and '#' or val:EntIndex(); output[ #output + 1] = 'E'..entIndex..';'; 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['nil'] = function( _, _, output ) output[ #output + 1 ] = '?;'; end setmetatable( encode, { __index = function( self, key ) local val = rawget( self, key ); if val then return val end ErrorNoHalt('Type: '..key..' can not be encoded. Encoded as as pass-over value.'); return rawget( self, 'nil' ); end }); do local empty, concat = table.Empty, table.concat ; function pon.encode( tbl ) local output = {}; cacheSize = 0; encode[ 'table' ]( encode, tbl, output, {} ); local res = concat( output ); return res; 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, cache ) 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, cache ) 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, cache ) 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, cache ) local finish = find( str, ';', index, true ); local vecStr = sub( str, index, finish - 1 ); index = finish + 1; local segs = Explode( ',', vecStr, false ); return index, Vector( segs[1], segs[2], segs[3] ); end -- ANGLE decode[ 'a' ] = function( self, index, str, cache ) local finish = find( str, ';', index, true ); local angStr = sub( str, index, finish - 1 ); index = finish + 1; 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, cache ) local finish = find( str, ';', index, true ); local num = sub( str, index, finish - 1 ); index = finish + 1; return index, num == '#' and NULL or Entity( num ); end -- PLAYER decode[ 'P' ] = decode[ 'E' ]; -- NIL decode[ '?' ] = function( _, index ) return index + 1, nil; end function pon.decode( data ) local _, res = decode[ sub( data, 1, 1 ) ]( decode, 2, data, {}); return res; end end