/*--------------------------------------------------------- Name: table.serialise( tbl, size ) table.deserialise( str, ... ) Desc: Converts an arbitrary table containing vectors, angles, bools, players, entities, etc to & from a compact string. Notes: Pass a second integer argument to table.serialise and it'll automatically split the resultant strings into chunks and return them as seperate values Strings can't currently contain the characters: { } = ; Need to look at lines 10 & 34 and encode/decode delimiter characters.---------------------------------------------------------*/// Helper function for table.serialiselocal function SerialiseChunk( chunk, tables ) // get the data type local type = type( chunk ) //see what data type this chunk represents and return the proper value if type == "number" then return 'N:'..chunk elseif type == "string" then return string.format('S:%q', chunk ) // todo escape/encode delimiter characters elseif type == "boolean" then return 'B:'..tostring( chunk ):sub( 1,1 ) elseif type == "Entity" then if chunk == GetWorldEntity() then return 'E:W' elseif chunk == NULL then return 'E:N' else return 'E:'..chunk:EntIndex() end elseif type == "Vector" then return string.format( "V:%g,%g,%g", chunk.x, chunk.y, chunk.z ) elseif type == "Angle" then return string.format( "A:%g,%g,%g", chunk.pitch, chunk.yaw, chunk.roll ) elseif type == "Player" then return 'P:'..chunk:UniqueID() elseif type == "table" then local ID = tostring( chunk ):sub( 8 ) // tables[ID] must exist before the table is serialised else we could end up with an infinite loop. if !tables[ID] then tables[ID] = true tables[ID] = SerialiseTableKeyValues( chunk, tables ) end return 'T:'..ID endend// Helper function for table.serialiselocal function DeSerialiseChunk( chunk, tables ) // get the data type and value local type,val = chunk:match("(.):(.+)") //see what data type this chunk represents and return the proper value if type == "N" then return val elseif type == "S" then return val:sub(2, -2) // todo de-escape/de-encode delimiter characters elseif type == "B" then return val == "t" elseif type == "E" then if val == "W" then return GetWorldEntity() elseif val == "N" then return NULL else return Entity( val ) end elseif type == "V" then return Vector( val:match("(.-),(.-),(.+)") ) elseif type == "A" then return Angle( val:match("(.-),(.-),(.+)") ) elseif type == "P" then return player.GetByUniqueID( val ) elseif type == "T" then local t = {} if !tables[ val ] then tables[ val ] = {} end table.insert( tables[ val ], t ) return t endend// Helper function for table.serialisefunction SerialiseTableKeyValues( tbl, tables ) local temp = {} local DoKeys = !table.IsSequential( tbl ) // we don't need to save table keys if the table is sequential for k,v in pairs( tbl ) do local str = SerialiseChunk( v, tables ) if DoKeys && str then str = SerialiseChunk( k, tables ) .."=".. str end table.insert( temp, str ) end return tempend/*--------------------------------------------------------- Name: table.serialise( tbl, size ) table.deserialise( str, ... ) Desc: Converts an arbitrary table containing vectors, angles, bools, players, entities, etc to & from a compact string. Notes: Pass a second integer argument to table.serialise and it'll automatically split the resultant strings into chunks and return them as seperate values Strings can't currently contain the characters: { } = ; Need to look at lines 10 & 34 and encode/decode delimiter characters.---------------------------------------------------------*/function table.serialise( t,len ) local tables = {} local str = "" local strings = {} local len = len local headID = tostring(t):sub(8) local head = SerialiseTableKeyValues( t, tables ) // distinguish the main table from nested tables tables[headID] = nil tables["H"..headID] = head for k,v in pairs( tables ) do // concat key values for each table str = str..k.."{".. table.concat( v, ";" ) ..";}" // if string is too long split it and start a new one if ( len && #str >= len ) then table.insert( strings, str:sub(1,len) ) str = str:sub( len+1 ) end end table.insert( strings, str ) // unpack strings and return them as seperate values return unpack(strings)end// Pass this function the output from the previous onefunction table.deserialise( ... ) //re-combine all strings we got passed local block = table.concat( unpack( arg ) ) local tables = {} local subtables = {} local head = nil // I'm not too good at regex, so there's probably a more efficient way of doing lines 120,127,129 for ID,chunk in block:gmatch('(%w+){(.-)}') do // Get each table chunk // check if this table is the trunk if ID:sub(1,1)== "H" then ID = ID:sub(2); head = ID end tables[ID] = {} for kv in chunk:gmatch('(.-);') do // Split each table block into k/v pairs local k,v = kv:match('(.-)=(.+)') // deserialise each k/v pair and add to the new table // if the table keys are sequential digits they won't be included in the string, so check for that. if !k then v = DeSerialiseChunk( kv, subtables ) table.insert( tables[ID], v) else k = DeSerialiseChunk( k, subtables ) v = DeSerialiseChunk( v, subtables ) tables[ID][k] = v end end end // Restore table references for ID,tbls in pairs( subtables ) do for _,tbl in pairs( tbls ) do table.Merge( tbl, tables[ID] ) end end return tables[ head ]end