• 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!

[TFS 1.X] Pet System - Scripters, I challange you to make it together!

Saving whole tables is really really good. The bad thing is performance.
You only do it when shutting down or starting up, so no players are online anyway.
 
Saving whole tables is really really good. The bad thing is performance.
If well executed it has far better performence then a database query.
We load the chunk once into ram and clear it once the player logs out, thus giving a far better performence.
Plus we can directly access/edit the data from everywhere easily if it's loaded into ram.
You only do it when shutting down or starting up, so no players are online anyway.
My load/save idea consists more of a chunk loading type, we only load the chunks which we actually use, so we don't buffer up the ram usage to much.
That's why I'm splitting the pets into a player lua file, once the player login his pet chunk is loaded into ram, once he logs out we save his pet and clear the space in the ram with the garbage collector.
I've been working towards stability and performence lately and keeping everything in ram which has basicly no usage to wait and save it at the server shutdown is a bad habbit, it slows down the server by heaps and thus decreasing performence and stability.
 
What's the problem with storages? I made a Pet System (some yeard ago, for TFS 0.3.6 i think) and all the info was saved with players storage (pet exp, mana/health, level) and you just have to create a lib containing all pet info (i.e: spells, exprate, etc)
 
What's the problem with storages? I made a Pet System (some yeard ago, for TFS 0.3.6 i think) and all the info was saved with players storage (pet exp, mana/health, level) and you just have to create a lib containing all pet info (i.e: spells, exprate, etc)
TFS 1.0 is much different than 0.3.6 when it comes to storages.
 
You only do it when shutting down or starting up, so no players are online anyway.
Oh sorry. I was thinking in using it in Player( Its mean loading on login and saving on logout ).

What's the problem with storages? I made a Pet System (some yeard ago, for TFS 0.3.6 i think) and all the info was saved with players storage (pet exp, mana/health, level) and you just have to create a lib containing all pet info (i.e: spells, exprate, etc)

Storages on tfs 1.0 only accepts integer as keys and values. Very bad.
 
Code:
function table.serialize(t, depth)
    local depth = depth or 0
    local ret, first = '{\n', true
    for k, v in pairs(t) do
        if not first then
            ret = ret .. ',\n'           
        end

        ret = ret .. ('  '):rep(depth + 1)
      
        if type(k) == 'number' then
            ret = ret .. '[' .. k .. '] = '
        elseif type(k) == 'string' then
            ret = ret .. '["' .. tostring(k):gsub('"', '\\"') .. '"] = '
        else
            error('Unhandled key type')
        end

        if type(v) == 'number' then
            ret = ret .. v
        elseif type(v) == 'string' then
            ret = ret .. v:gsub('"', '\\"')
        elseif type(v) == 'table' then
            ret = ret .. table.serialize(v, depth + 1)
        elseif type(v) == 'boolean' or type(v) == 'nil' then
            ret = ret .. (v == true and 'true' or v == false and 'false' or 'nil')
        else
            error('Unhandled value type')
        end
        first = false
    end
    ret = ret .. '\n' .. ('  '):rep(depth) .. '}'
  
    return ret
end

function table.unserialize(str)
    return loadstring('return ' .. str)()
end

In case you wanna save your globalstorages or another table.
Summ it looks neat, but can you give example how to use it and how to execute it.

I thougt just save values into the player and fetch them.
 
TFS 1.0 is much different than 0.3.6 when it comes to storages.
Oh sorry. I was thinking in using it in Player( Its mean loading on login and saving on logout ).



Storages on tfs 1.0 only accepts integer as keys and values. Very bad.

I know, but you can use easily integers. Just set a base and work with that.
Code:
STORAGE_BASE = 1000
PET_EXP = STORAGE_BASE + 1
PET_HEALTH = STORAGE_BASE + 2
.
.
.
 
Thats exactly what i'm done Darkhaos.
 
isn't it good practice to make the save data every X hours instead of startup and shutdown, just incase of an unexpected crash?
 
answer is ssd or caching last used files?
I do not see a reason why you would need a ssd for such a case, we don't talk about writing 200k lines in a file each execute, so this happens in a blink of an eye for a normal hdd aswell.
Another thing is loading files in tiny chunks just when you need them, instead of loading big chunks where you don't need half of the stuff for now.

I've edited the post btw, so don't wonder why I'm quoting before averatec even wrote it :p
 
Last edited:
Here's the first progress contains creating/saving and loading the pets.
pet2.png

the picture above just shows an example of how the creation looks like when it's beeing saved in the lua file.

needs to go into global.lua (if someone feels like testing it :))
Code:
pet = {}

function table.serialize(t, depth) -- credits Summ, I've just tweaked it some.
  local depth = depth or 0
  local ret, first = '\n' .. ('  '):rep(depth) .. '{\n', true
  for k, v in pairs(t) do
  if not first then
  ret = ret .. ',\n'
  end

  ret = ret .. ('  '):rep(depth + 1)

  if type(k) == 'number' then
  ret = ret .. '[' .. k .. '] = '
  elseif type(k) == 'string' and t[k] ~= nil then
  ret = ret .. '["' .. tostring(k):gsub('"', '\\"') .. '"] = '
  else
  error('Unhandled key type')
  end

  if type(v) == 'number' then
  ret = ret .. v
  elseif type(v) == 'string' then
  ret = ret .. '"'.. v ..'"'
  elseif type(v) == 'table' then
  ret = ret .. table.serialize(v, depth + 1)
  elseif type(v) == 'boolean' or type(v) == 'nil' then
  ret = ret .. (v == true and 'true' or v == false and 'false' or 'nil')
  else
  error('Unhandled value type')
  end
  first = false
  end
  ret = ret .. '\n' .. ('  '):rep(depth) .. '}'

  return ret
end

function Player.savePets(self)
   local file = io.open("data/pets/".. self:getName() ..".lua", "w")
   file:write('pet["'.. self:getName() ..'"] =' .. table.serialize(pet[self:getName()]))
   file:close()
end

function Player.loadPets(self)
   dofile("data/pets/".. self:getName() ..".lua")
end

function Player.createPet(self, name, outfit, healthMax, manaMax, spells, petType)
   if pet[self:getName()] ~= nil then
     if pet[self:getName()][name] ~= nil then
       return self:sendCancelMessage("You already have a pet with that name.")
     end
   else
     pet[self:getName()] = {}
   end
   pet[self:getName()][name] = {}
   pet[self:getName()][name].outfit = outfit
   pet[self:getName()][name].healthMax = healthMax
   pet[self:getName()][name].health = healthMax
   pet[self:getName()][name].manaMax = manaMax
   pet[self:getName()][name].mana = manaMax
   pet[self:getName()][name].level = 1
   pet[self:getName()][name].experience = 0
   pet[self:getName()][name].petType = petType
   pet[self:getName()][name].spells = spells
   pet[self:getName()][name].backpack = {}
   self:savePets()
end
You just need to create a folder called "pets" in the data folder.

Now I can get myself into the core and create everything around it :)
 
Really like how it's going @Evil Hero, i let you finish the core and then i can update and add some features ^^
 
Here's the first progress contains creating/saving and loading the pets.
pet2.png

the picture above just shows an example of how the creation looks like when it's beeing saved in the lua file.

needs to go into global.lua (if someone feels like testing it :))
Code:
pet = {}

function table.serialize(t, depth) -- credits Summ, I've just tweaked it some.
  local depth = depth or 0
  local ret, first = '\n' .. ('  '):rep(depth) .. '{\n', true
  for k, v in pairs(t) do
  if not first then
  ret = ret .. ',\n'
  end

  ret = ret .. ('  '):rep(depth + 1)

  if type(k) == 'number' then
  ret = ret .. '[' .. k .. '] = '
  elseif type(k) == 'string' and t[k] ~= nil then
  ret = ret .. '["' .. tostring(k):gsub('"', '\\"') .. '"] = '
  else
  error('Unhandled key type')
  end

  if type(v) == 'number' then
  ret = ret .. v
  elseif type(v) == 'string' then
  ret = ret .. '"'.. v ..'"'
  elseif type(v) == 'table' then
  ret = ret .. table.serialize(v, depth + 1)
  elseif type(v) == 'boolean' or type(v) == 'nil' then
  ret = ret .. (v == true and 'true' or v == false and 'false' or 'nil')
  else
  error('Unhandled value type')
  end
  first = false
  end
  ret = ret .. '\n' .. ('  '):rep(depth) .. '}'

  return ret
end

function Player.savePets(self)
   local file = io.open("data/pets/".. self:getName() ..".lua", "w")
   file:write('pet["'.. self:getName() ..'"] =' .. table.serialize(pet[self:getName()]))
   file:close()
end

function Player.loadPets(self)
   dofile("data/pets/".. self:getName() ..".lua")
end

function Player.createPet(self, name, outfit, healthMax, manaMax, spells, petType)
   if pet[self:getName()] ~= nil then
     if pet[self:getName()][name] ~= nil then
       return self:sendCancelMessage("You already have a pet with that name.")
     end
   else
     pet[self:getName()] = {}
   end
   pet[self:getName()][name] = {}
   pet[self:getName()][name].outfit = outfit
   pet[self:getName()][name].healthMax = healthMax
   pet[self:getName()][name].health = healthMax
   pet[self:getName()][name].manaMax = manaMax
   pet[self:getName()][name].mana = manaMax
   pet[self:getName()][name].level = 1
   pet[self:getName()][name].experience = 0
   pet[self:getName()][name].petType = petType
   pet[self:getName()][name].spells = spells
   pet[self:getName()][name].backpack = {}
   self:savePets()
end
You just need to create a folder called "pets" in the data folder.

Now I can get myself into the core and create everything around it :)

Store self:getName() in a variable to not call everytime.
 
Agree i would done something like this:

Code:
function Player.savePets(self)
    local playerName = self:getName()
    local file = io.open("data/pets/".. playerName ..".lua", "w")
    file:write('pet["'.. playerName ..'"] =' .. table.serialize(pet[playerName]))
    file:close()
end

function Player.createPet(self, name, outfit, healthMax, manaMax, spells, petType)
    local playerName = self:getName()
    if not pet[playerName] then
        pet[playerName] = {}
    end
  
    if pet[playerName][name] then
        return self:sendCancelMessage("You already have a pet with that name.")
    end
  
    pet[playerName][name] = {}
    pet[playerName][name].outfit = outfit
    pet[playerName][name].healthMax = healthMax
    pet[playerName][name].health = healthMax
    pet[playerName][name].manaMax = manaMax
    pet[playerName][name].mana = manaMax
    pet[playerName][name].level = 1
    pet[playerName][name].experience = 0
    pet[playerName][name].petType = petType
    pet[playerName][name].spells = spells
    pet[playerName][name].backpack = {}
    self:savePets()
end
 
So proud lf the community :D
Keep it up everyone.

Kind Regards,
Eldin.
 
If people don't make use of the lua garbage collector then my solution is better in this case.
If you declare variables they'll stay in the ram until they're not accessable and cleared by the garbage collector.
I know that I'm talking here about silly little numbers but keep under consideration that if you use this method everywhere (let's assume we have 1000+ different variables used) then this sum's up after a while.
Another thing why I avoided using a variable here is that there is no necessity to change the function at all times as it shall be static not dynamic, so people wont start screwing around with it.
 
@Evil Hero
Why don't you store the pet data in player storages instead of inventing a system which basically already exists?
 
Well there are a few points on why I want to make it in a different way.

1)
I want to show people another way of storing data which is sololy handled by lua and does not require any database type.

2)
In comparison to storagevalues for players, I can decide if I want to load the data or not (which I cannot with said storagevalues)

3)
We use a simple and modular way, where we can install addons on the go without re scripting the core itself.
example:
if we want the pet to say certain phrases, we could do it easily like:
Code:
pet[player:getName()][petname].say = {}
pet[player:getName()][petname].say[1] = "I rip them apart"
pet[player:getName()][petname].say[2] = "You are my bro"
once the pet is saved this will automaticly be added to the file then, so customizing and adding things is far easier.
If we wanted to do the same with storagevalues it would require editing the lib of it, which could end up in making misstakes and we are addressing even more storages then which could be used for other stuff.

4)
We can save a certain pet whenever we want and unload them if we don't have any usage for them now (thus not storing them in ram for nothing)

5)
We have a good visual file, where we can see all the information and edit them, without the need to enter a database.


I could probably go on even further but those are definitely the most obvious points.
I don't say it's bad to use storages, I'm basicly just trying to invent something new, which could be used pretty good for certain cases.
 
The thing i like with Evil Hero, it makes the pets pretty unique with their own files :)
 
Back
Top