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

Lists of unique numbers kept in ‘player storage’

Gesior.pl

Mega Noob&LOL 2012
Senator
Joined
Sep 18, 2007
Messages
2,966
Solutions
99
Reaction score
3,383
Location
Poland
GitHub
gesior
Friend asked me about code to keep ‘auto loot lists’ in storage.
(Player can configure few lists of items and then just switch between them, when he goes to other spawn with different loot.)
Thing he needed was something like c++ std::set, but in Lua. With default configuration player can keep up to 10 lists of up to 100 unique numbers (in my friend case - item ids).

Maybe someone need something similar or want to learn some programming by reading this algorithm.

Lists are kept in player storage in format:
  • first element of list is number of elements in list
  • next elements are elements of list

Some other rules:
  • adding elements with duplicated value does nothing (unique list)
  • removing element from list move all 'next elements' one position closer to start of list, there can't be gaps in list

Lua:
-- config
local listsStartStorage = 550000000
local maximumListsCount = 10
local maximumElementsPerList = 100

-- variables for calculations
local listOffset = maximumElementsPerList + 1

function lists_getLists(player)
    local result = {}
    for i = 1, maximumListsCount do
        result[i] = lists_getListElementsCount(player, i)
    end

    return result
end

function lists_getListElementsCount(player, listId)
    local startStorage = listsStartStorage + listOffset * listId
    return math.max(0, player:getStorage(startStorage))
end

function lists_getListElements(player, listId)
    local result = {}
    local startStorage = listsStartStorage + listOffset * listId
    local listElementsCount = lists_getListElementsCount(player, listId)
    for i = startStorage + 1, startStorage + listElementsCount do
        table.insert(result, player:getStorage(i))
    end

    return result
end

function lists_removeAllListElements(player, listId)
    local startStorage = listsStartStorage + listOffset * listId
    local listElementsCount = lists_getListElementsCount(player, listId)
    for i = startStorage + 1, startStorage + listElementsCount + 1 do
        player:setStorage(i, -1)
    end
    player:setStorage(startStorage, -1)
end

function lists_getListElement(player, listId, elementKey)
    local elementStorage = listsStartStorage + listOffset * listId + elementKey
    return player:getStorage(elementStorage)
end

local function lists_assertListId(listId)
    assert(listId >= 1, 'minimum list id is 1')
    assert(listId <= maximumListsCount, 'maximum list id is 1' .. maximumListsCount)
end

local function lists_assertElementKey(elementKey)
    assert(elementKey >= 1, 'minimum list element id is 1')
    assert(elementKey <= maximumElementsPerList, 'maximum list element id is ' .. maximumElementsPerList)
end

-- this is for internal use only, should not be used by user
local function lists_setListElement(player, listId, elementKey, elementValue)
    lists_assertListId(listId)
    lists_assertElementKey(elementKey)
    local elementStorage = listsStartStorage + listOffset * listId + elementKey
    player:setStorage(elementStorage, elementValue)
end

-- this is for internal use only, should not be used by user
local function lists_setListElementsCount(player, listId, count)
    lists_assertListId(listId)
    lists_assertElementKey(count)
    local startStorage = listsStartStorage + listOffset * listId
    player:setStorage(startStorage, count)
end

function lists_addListElement(player, listId, elementValue)
    local elements = lists_getListElements(player, listId)
    for k, v in pairs(elements) do
        if v == elementValue then
            return
        end
    end

    local elementsCount = lists_getListElementsCount(player, listId)
    lists_setListElement(player, listId, elementsCount + 1, elementValue)
    lists_setListElementsCount(player, listId, elementsCount + 1)
end

function lists_removeListElement(player, listId, elementValue)
    local elements = lists_getListElements(player, listId)
    local elementToRemovePosition = nil
    for k, v in pairs(elements) do
        if v == elementValue then
            elementToRemovePosition = k
            break
        end
    end

    if elementToRemovePosition then
        local elementsCount = lists_getListElementsCount(player, listId)
        for i = elementToRemovePosition + 1, elementsCount  do
            local nextElementValue = lists_getListElement(player, listId, i)
            lists_setListElement(player, listId, i - 1, nextElementValue)
        end
        lists_setListElement(player, listId, elementsCount, -1)
        lists_setListElementsCount(player, listId, elementsCount - 1)
    end
end

I also wrote tests for these functions. Tests that I can run without OTS. I often use Online Lua Compiler - Online Lua Editor - Online Lua IDE - Lua Coding Online - Practice Lua Online - Execute Lua Online - Compile Lua Online - Run Lua Online (https://www.tutorialspoint.com/execute_lua_online.php) to run code like this. In this case I had to create 'fake player object', to make it get/set storages:
Lua:
Player = {}
function Player:new()
    o = {storages = {}}
    setmetatable(o, self)
    self.__index = self
    return o
end

function Player.getStorage(self, k)
    if (self.storages[k]) then
        return self.storages[k]
    end
    return -1
end

function Player.setStorage(self, k, v)
    if (v == -1) then
        v = nil
    end
    self.storages[k] = v
end

function Player.dumpStorages(self)
    local storages = 'storages: '
    for k, v in pairs(self.storages) do
        storages = storages .. k .. '=' .. v .. ', '
    end
    print(storages)
end

local function printPlayerListsElementsCount(player)
    local playerLists = lists_getLists(player)
    for k, v in pairs(playerLists) do
        local listElementsString = ''
        for k2, v2 in pairs(lists_getListElements(player, k)) do
            listElementsString = listElementsString .. ' ' .. k2 .. '=' .. v2 .. ', '
        end
        print ('list', k, 'elements', v, 'values', listElementsString)
    end
end

player = Player:new()

print()
player:dumpStorages()
print('start - empty list')
printPlayerListsElementsCount(player)

print()
player:dumpStorages()
lists_addListElement(player, 2, 2001)
lists_addListElement(player, 2, 2002)
print('added 2 elements to list 2')
player:dumpStorages()
printPlayerListsElementsCount(player)

print()
player:dumpStorages()
lists_addListElement(player, 1, 1001)
lists_addListElement(player, 1, 1002)
lists_addListElement(player, 1, 1003)
lists_addListElement(player, 1, 1004)
lists_addListElement(player, 1, 1005)
print('added 5 elements to list 1')
player:dumpStorages()
printPlayerListsElementsCount(player)

print()
player:dumpStorages()
lists_removeListElement(player, 1, 1001)
print('removed first element from list 1')
player:dumpStorages()
printPlayerListsElementsCount(player)

print()
player:dumpStorages()
lists_removeListElement(player, 1, 1005)
print('removed last element from list 1')
player:dumpStorages()
printPlayerListsElementsCount(player)

print()
player:dumpStorages()
lists_removeListElement(player, 1, 1003)
print('removed middle element from list 1')
player:dumpStorages()
printPlayerListsElementsCount(player)

print()
player:dumpStorages()
lists_removeAllListElements(player, 1)
print('removed all elements from list 1')
player:dumpStorages()
 
I think that it would be better in c++. You could just serialize the data and save it to a new sql table/column on player logout.
 
I think that it would be better in c++. You could just serialize the data and save it to a new sql table/column on player logout.
I told that to my friend, but he has some old protocol server - without 12+ 'in game autoloot window' - and wanted to make all on talkactions and storages. He wanted to implement fast 'switch loot list', because players go to other monsters spawn and they got to remove every item from 'current loot list' and add all new items with !loot add,dragon shield etc.
Now players can add 10 loot lists for 10 spawns and switch them with 1 talkaction.

If anyone is interested about adding C++ loot list (tested using otservbr implementation). I did tests for kasteria.pl and std::set is faster than std::vector only with 50+ items on loot list. Most of players define shorter loot lists, so std::vector - used by otservbr - is optimal.
 
Back
Top