Scripting Helpers is winding down operations and is now read-only. More info→
Ad
Log in to vote
0

What's the best way to handle a large table of constants?

Asked by 7 years ago

So I have a lot of object data in my game (to provide constants for different swords, monsters, etc.) and I wanted to know which of these two methods below is more efficient for organization and data processing:

1) The first way is to just have a module script that returns the large table directly, like so

return {
[1] = {AssetId = 1, ...}
,
[2] = {AssetId = 2, ...}
,
//*5000 more lines of code*
}

OR

2) Have a function inside the Module Script that returns only the chunk of data that you ask it for.

local ModuleScript = {}

ModuleScript.getAsset = function(AssetId)
    if AssetId == 1 then
        return {
            ItemId = 1,
            Image = "",
        }
    elseif AssetId == 2 then
        return {
            ItemId = 2,
            Image = "",
        }
    else
        //...*5000 more lines of code*
    end
end

return ModleScript

I'm just struggling to choose which method I should utilize for managing data in my game, since the first method looks cleaner but I feel like the second method is more efficient.

If there's an even better method that's the best of both worlds, please, please, please tell me! Your efforts will not be unappreciated.

Thanks, Denfeet

0
Why not do "ItemId = AssetId"? hiimgoodpack 2009 — 7y

1 answer

Log in to vote
0
Answered by 7 years ago

In terms of lua processing, putting the information into a table (your first option) will be tons faster than using an "if elseif" chain. Lua can read the value for any given key extremely quickly, but it has to consider every "if" condition separately.

You seem to be thinking that it will take time for lua to "return" the table to whatever is asking for the information. However, lua won't be copying it at all - it will make the table once and then simply copy a reference to the table as needed (and a reference is as fast as anything, including a single number). Further, once a ModuleScript has been run, it won't run again - whatever it does is saved to memory for the rest of the server's life.

In terms of memory, the first option is better too -- it won't be making a lot of temporary tables. It will have a single larger table that will always be in use, but I don't think that'll be a problem unless each entry has thousands of variables (where-as you currently show 2 in your demonstration, ItemID and Image. Even having 100 variables for each of 1000 objects should be okay).

More important is how easy it will be for you to manage the data. If every object has the same fields, you might consider using a function:

local data = {}
local function Item(id, image)
    local item = {ItemId = id, Image = image}
    data[id] = item
    return item
end
Item(1, "")
Item(2, "")
--Occasionally need a custom field?
local sword = Item(3, "")
sword.Special = 5
--Regularly need a custom field?
local function Weapon(id, image, damage)
    local item = Item(id, image)
    item.Damage = damage
    return item
end
local sword2 = Weapon(4, "", 5)
local betterSword = Weapon(5, "", 10)
--etc
return data

This set up lets you do some interesting things:

  • You could give each of these item tables functions
  • You could error if an argument was not provided or was the wrong type (which would help with data entry problems). ex:
local function Weapon(id, image, damage)
    if type(damage) ~= "number" then error("Damage must be a number") end
    local item = Item(id, image)
    item.Damage = damage
    return item
end
local sword3 = Weapon(6, "", "", 12) -- accidentally put in an extra argument; now it will error

There's also a more complicated option where you provide small tables into functions (sort of like named arguments):

local data = {}
local idKey = "ID"
local itemKeys = {[idKey]="ItemId", Image="Image"} -- ex, idKey (or "ID") is what you will provide to the function and "ItemId" is what the function will store in data
local weaponKeys = {dmg="Damage"}
local function Gen(keys, base) -- Generate a function to create an item, verifying that the keys are correct. 'base' is an optional base function.
    return function(stats)
        local item = base and base(stats) or {}
        data[stats.ID] = item -- record item (may have already been done, but that's okay)
        for k, v in pairs(keys) do -- verify that all keys have been provided
            if not stats[k] then error("Item did not have key " .. k) end
            item[v] = stats[k]
        end
        return item
    end
end
local Item = Gen(itemKeys) -- Now, when Item is called, it will make sure that the table has ID and Image before assigning them to the item (under the names ItemId and Image), adding the item to 'data', and returning it
local Weapon = Gen(weaponKeys, Item)
Item{ID=1, Image=""} -- This is a shortcut syntax for saying Item({ID=1, Image=""})
Weapon{ID=4, Image="", dmg=5}
return data

All this said, especially if you're not comfortable using what I've suggested, just using tables is perfectly fine. The only downside is that it might be more difficult to catch errors and it might be harder to change (if you ever need to change the name of a field or something) - but Find & Replace can help a lot too.

(This paragraph only applies if your script is downloaded to the client.) Though 5000 lines of information isn't necessarily too much of a strain, if you find that it does become an issue for clients (you should test this first, though), you could set it up so that the information stays on the server except for when needed. Doing so would mean that the client doesn't ever receive your 5000 line script, it just asks the server (through some RemoteFunction) for information on whatever object(s) it needs to know about. If any one player rarely needs information on many of your objects, this would save on start-up time (at the cost of a bit of latency when you do end up needing the information, since it would have to stop and ask the server for it instead of already knowing what it is).

0
I feel like you made this simple question way too complicated. CootKitty 311 — 7y
0
This answer goes really in depth and i think i'll use the first option. The only problem left is security, because exploiters could easily get into the data but many of my other clientside scripts are dependent on it denfeet 9 — 7y
0
With Filtering Enabled on, exploiters can't mess with the data (except client-side), though they will be able to see it. If you found my answer helpful, consider accepting it. chess123mate 5873 — 7y
Ad

Answer this question