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)
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
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
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.