• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

[Lib] Serializing and Deserializing many values into/from single storage value.

tarjei

Necronian Engineer
Joined
May 25, 2008
Messages
505
Reaction score
126
Location
Poland
MULTI STORAGE LIBRARY

Hello, fellows. Today I bring you the most awesome thing I could think of about storage values. I was really annoyed by using many separete storage values to hold information in single event. If you have ever heard about packets you, it work exactly like that.

It uses Binary Stream class originally made by Colex. I edited it a bit, though he is th creator.

Main class cosist of some help function, but the most important is for you know know 4 of them.

:new(storage) Creates new multistorage object.
:addToRead(type) You gotta read all values in proper order, to do that you have to make proper stucture and you do that with this method.
:serialize(cid) Compile all values into one stream and saves it in storage.
:deserialize(cid) Decompile values from storage basing on structure you provided with :addToRead method.

To people that wonder about __ZERO in BinaryStream class. Well, I had issue with passing binarystreams to C. I had to gsub embedded zeroes (0x0) to something else I can reverse later to 0x0 bytes during deserialization.. Uff

Ok Here are needed libs.
BinaryStream.lua
Lua:
--[[
    Binary Stream
Allows you to read, edit and create new binary files
Developed by Colex (Alexandre Santos)
]]--
 
require("bit")
 
_BINARY_STREAM = {
    Position = 0
}
 
BinaryStream = function(filename)
    local obj = {}
	local file = nil
	
	if filename then
     file = io.open(filename, "rb")
	end 
	
    if not (file) then
       -- print("Warrning: file wasnt opened")
    end
 
    setmetatable(obj, _BINARY_STREAM)
    _BINARY_STREAM.__index = _BINARY_STREAM
 
    obj["Buffer"] = file~= nil and file:read("*all") or ""
    obj["Length"] = obj.Buffer:len()
    obj["FileName"] = filename
    if file~= nil then file:close() end
    return obj
end
 
---Read Functions
 
_BINARY_STREAM.ReadByte = function(self)
    self.Position = self.Position + 1
    return self.Buffer:byte(self.Position)
end
_BINARY_STREAM.Check = function(self, ...)
    for i = 1, table.maxv(arg) do --to get rid of embedded zeroes
		if arg[i] == 0 then arg[i] = "__ZERO" end
	end
	return unpack(arg)
end

_BINARY_STREAM.ReadInt16 = function(self, reversed)
 
	local l1,l2 = 0
	if not(reversed) then
     l1 = self:ReadByte()
     l2 = bit.lshift(self:ReadByte(), 8)

	else
     l1 = bit.lshift(self:ReadByte(), 8)
     l2 = self:ReadByte()	 
	end
 
	
    return   bit.bor(l1,l2)
end
 

_BINARY_STREAM.ReadInt32 = function(self, reversed)
	local l1,l2,l3,l4 = 0
	if not(reversed) then
     l1 = self:ReadByte()
     l2 = bit.lshift(self:ReadByte(), 8)
     l3 = bit.lshift(self:ReadByte(), 16)
     l4 = bit.lshift(self:ReadByte(), 24)
	else
     l1 = bit.lshift(self:ReadByte(),24)
     l2 = bit.lshift(self:ReadByte(),16)
     l3 = bit.lshift(self:ReadByte(), 8)
     l4 = self:ReadByte()	 
	end
	
 
    return  bit.bor(l1,l2,l3,l4)
end
 _BINARY_STREAM.ReadString = function(self)
    local len = self:ReadInt16(true)
	local ret = ""
	for i = 0, len - 1 do
		local char = self:ReadByte()
		if char == nil then break end
		ret = ret ..string.char(char)
	end
 
	print("Reading Streang| Len| "..len)
    return ret
end

--Write Functions
 
_BINARY_STREAM.WriteByte = function(self, byte, insert)
    self.Position = self.Position + 1
	if type(byte) == "number" then
	   byte = string.char(byte)
	end
    if insert then
        self.Buffer = self.Buffer:sub(0,self.Position-1)..byte..self.Buffer:sub(self.Position+1)
    else
        self.Buffer = self.Buffer..byte
    end
end
  _BINARY_STREAM.WriteString = function(self, str)
    local len = str:len()
	print("[WriteStringg] str:"..str.." Len: "..len)
	self:WriteInt16(len)
	self.Buffer = self.Buffer..str
 
	

end
_BINARY_STREAM.WriteInt16 = function(self, int16, insert)
		local l1 = bit.rshift(int16, 8)
		local l2 = int16 - bit.lshift(l1,8)
	l1,l2 = self:Check(l1,l2)
	
    self:WriteByte(l1, insert)
    self:WriteByte(l2, insert)
end
 
_BINARY_STREAM.WriteInt32 = function(self, int32, insert)
		local l1 = bit.rshift(int32, 24)
		local l2 = bit.rshift(int32, 16) - bit.lshift(l1,8)
		local l3 = bit.rshift(int32, 8) - bit.lshift(l1,16) - bit.lshift(l2,8)
		local l4 = int32 - bit.lshift(l1,24) - bit.lshift(l2,16) - bit.lshift(l3,8)
	
	l1,l2,l3,l4 = self:Check(l1,l2,l3,l4)
	
    self:WriteByte(l4, insert)
    self:WriteByte(l3, insert)
    self:WriteByte(l2, insert)
    self:WriteByte(l1, insert)
end
 
--Insert Function
 
_BINARY_STREAM.InsertByte = function(self, byte)
    self:WriteByte(byte, true)
end
 
_BINARY_STREAM.InsertInt16 = function(self, int16)
    self:WriteInt16(int16, true)
end
 
_BINARY_STREAM.InsertInt32 = function(self, int32)
    self:WriteInt32(int32, true)
end
 
 
 
--Position Functions
 
_BINARY_STREAM.Skip = function(self, bytes)
    self.Position = self.Position + bytes
end
 
_BINARY_STREAM.Seek = function(self, pos, origin)
    if (origin == 0) then --Begin
        self.Position = pos
    elseif (origin == 1) then --Current Position
        self.Position = self.Position + pos
    elseif (origin == 2) then --End
        self.Position = self.length + pos
    end
end
 function _BINARY_STREAM.Tell(self)
	return self.Position
 end
 
 _BINARY_STREAM.resize = function(self, lastpos)
	local len = self.Buffer:len()
	if lastpos < len then
	  return
	end
	self:Seek(len,0)
	for i = 0, lastpos - len do
		
		self:WriteByte(0)
	end
end
_BINARY_STREAM.paste = function(self, buff)
	self.Buffer = self.Buffer:sub(0,self.Position-1)..buff..self.Buffer:sub(self.Position+buff:len())
end
_BINARY_STREAM.cut = function(self, from, to)
	return self.Buffer:sub(from, to)
end
--Save function
 
_BINARY_STREAM.Save = function(self, filename)
    filename = filename or self.FileName
	--print("Saving..")
    local file = io.open(filename, "wb")
    file:write(self.Buffer)
    file:close()
end

MultiStorage.lua
Lua:
MultiStorage = {
	valid = {
		"byte", "int16", "int32", "string"
	},
	struct = {}
}

function MultiStorage:new(storage)
		local obj = {}
		
		self.__index = self
		obj.struct={}
		obj.storage = storage
		setmetatable(obj,self)
		return obj
end

function MultiStorage:setStructure(arr)
	self.struct = arr
end
function MultiStorage:addToRead(t, amount)
	amount = amount ~= nil and amount or 1

		for i = 1, amount do
		 table.insert(self.struct,t)
		end
	
end

function MultiStorage:isValidType(t)
	local ret = false
	for k ,v in pairs(self.valid) do
		if v == t then ret = true end
	end
	
end
function MultiStorage:getStream(cid) 
	return getCreatureStorage(cid,self.storage)
end

function MultiStorage:write(stream, ...)
	if stream == nil then
		return error("Stream given is nil")
	end
	for i = 1, table.maxv(arg) - 1 do
		print("Write"..i.." of type:" ..tostring(t))
		local t = self.struct[i]
		if t == "byte" then
			stream:WriteByte(arg[i])
			print("byte"..i)
		elseif t == "int16" then
			stream:WriteInt16(arg[i])
			print("Int16"..i)
		elseif t == "int32" then
			print("Int32"..i)
			stream:WriteInt32(arg[i])
		elseif t == "string" and type(arg[i]) == "string" then
			print("string"..i)
			stream:WriteString(arg[i])

		end
	end
	
end
function MultiStorage:read(stream, t,cid)
	if stream == nil then
		return error("Stream given is nil")
	end
	if t == "byte" then
		return stream:ReadByte()
	elseif t == "int16" then
		return stream:ReadInt16(true)
	elseif t == "int32" then
		return stream:ReadInt32(true)
	elseif t == "string" then
		local ret = stream:ReadString()
		doCreatureSay(cid,"ReadingString" .. ret,1)
		return ret
	else
	 return error("Invalid reading parameter.")
	end
	
end
function MultiStorage:serialize(cid, ...)
	local stream = BinaryStream()
	self:write(stream, unpack(arg))
	local Buf = stream.Buffer
	
	doCreatureSay(cid, "BufLen:"..Buf:len(),1)
	--Buf = self:byteZeroToString(Buf)
	doCreatureSay(cid,"BUFFERafte: "..Buf,1)
	doCreatureSay(cid, "BufLen:"..Buf:len(),1)
	doCreatureSetStorage(cid, self.storage, Buf)
	print(getCreatureStorage(cid,self.storage))
end
function MultiStorage:deserialize(cid)
	local stream = BinaryStream()
	local Buffer = self:getStream(cid)

	Buffer = self:StringToByteZero(Buffer)
	stream.Buffer = Buffer
	local ret = {}
	if type(Buffer) == "number" and Buffer < 0 then
			return error("Stream is empty")
	end
	for i = 1, table.maxv(self.struct) do
		ret[i] = self:read(stream, self.struct[i],cid)
	end
	return ret
end
function MultiStorage:byteZeroToString(s)
	
	return string.gsub(s, "\0",key)
end
function MultiStorage:StringToByteZero(s)
	local key = "__ZERO"
	print("[StringToByteZero] LEN "..s:len())
	
	s = string.gsub(s, key,"\0")
	print("[StringToByteZero] LEN after"..s:len())
	return s
end

Yes recently I am a vicious beast, so I encourage you peple to use this, since its really easy to get along with it, and it wont make such chaos in storage values as tradional way of using it. <- Espesially to event makars/sellers, if they consider to use it maybe I will buy it to save my time.
Now seriosly, I will show some example maybe.

Code:
local multi = MultiStorage:new(2222)
multi:addToRead("string",3)
multi:addToRead("int16")

So above we defined a multisotrage with some structure. This structure is neccesery to be defined before we start using it otherwise its useless. Here I want it to
compile 3x strings
Code:
multi:addToRead("string",3)
and one number (there are 3 types as in C - byte, uint16 and uint32) ( I have no idea how it handles negative numbers and I don't care at the moment)
Code:
multi:addToRead("int16")

Now when we have such a beautifull structure, we can use serialization to write some values to players storage:

Lua:
function onUse(cid, item, fromPosition, itemEx, toPosition)
	multi:serialize(cid,"Posejdon", "Midas", "Zeus", 1234) 
      -- We SHOULD (ofc we can) pass arguments of types defined as structure says. Otherwise it will be deserialised badly.
   
 return true
end
Deserialization example
Lua:
function onUse(cid, item, fromPosition, itemEx, toPosition)
	local values = multi:deserialize(cid) -- It returns array with values in order we saved them with serialize file (if our structure is correct ofc ) 
        for k, v in pairs(values) do
               print(k..." "...v)
        end
      
   
 return true
end
Output of printing from example XD
Code:
[06/01/2013 09:49:03] 1 Posejdon
[06/01/2013 09:49:03] 2 Midas
[06/01/2013 09:49:03] 3 Zeus
[06/01/2013 09:49:04] 4 1234

I will keep improving it, so it is more dynamic in its structure.

Advantages:
- You need single storage to put many unrelated data into it.

Disadvantages:
- You need to define a structure of this stream before you start using it.
 
Back
Top