Problem saving tables with metatables to datastore

I have encountered an issue while using the DataStoreService. When I save a table to the datastore using SetAsync(), I find that I am getting an incomplete table when GetAsync().

Here is the code that I am using:

local DataStoreService = game:GetService("DataStoreService")
local playerData = DataStoreService:GetDataStore("PlayerData")

local buffUnit = {
    playerId = nil, 
    buffId = 0, 
    value = 0, 
    calType = "" add, multiple
}
buffUnit.__index = buffUnit

local test = {}
setmetatable(test, buffUnit)

playerData:SetAsync("test", test)
local downLoadTest = playerData:GetAsync("test")

print(downLoadTest.playerId)  -- nil

It seems that attributes in the metatable don’t really belong to the child table. However, OOP performs better in many situations. Is there any convenient way to save a table with a metatable?

1 Like

Although I don’t see what scenario you’d have where a metatable would need to change so significantly as to be saved anyway. Usually the metatables you use are boilerplate enough that you’d be able to apply them to specific types of table/information storage.

If you need to save some external attributes, including those that could be contained in a metatable, you can serialise in a similar way as normal table values/game data.

Thanks for replying, buddy! Here’s the situation: I have two types of animals, each with a name, HP, speed, etc. To represent them, I created a metatable like this:

local animal = {
    name = "not set",
    hp = 100,
    speed = 10
}
animal.__index = animal

then i have a rabbit and a tiger:

local rabbit = setmetatable({}, animal)
rabbit.ownerId = 123

local tiger = setmetatable({}, animal)
tiger.targetId = 213

However, when a player leaves the game and I try to save either the rabbit or the tiger, its name, HP, and speed are lost.

And now I know that setmetatable() doesn’t really copy attributes from parent tables to child tables, but rather ‘rents’ them.

I don’t think metatables are necessary for what you want to do. In Java or C++ the sort of inheritance you’re describing makes perfect sense in most scenarios, but in Lua that’s not necessarily the case.

(mainly due to objects being a type in and of themselves, rather than pseudo-objects in Lua) - this isn’t to say you shouldn’t use them, just be adequately discerning

Either way, if you did want to do this you’d have to serialise the data in some way so you could reapply the correct metatables. i.e save a keyword for the animal which can then be used to apply the correct metatable. Any saved data could then be applied to the metatable also.

Metatables used in this way are meant to save memory by using the same values and methods for many objects you’re making. But, your using it in a way that will cause bugs because these are all independant variables to the animals that are being used as global variables. So you don’t need metatables for this.

Okay, I’m fairly new to Lua and Roblox, before this, I only knew some Python. But now, I feel more confident using metatables in a proper way. Thank you very much!

Hmm, python. This is what your script would look like in python:

# Why does the devforum make python look ugly 🤨
class Animal:
    Name = "Untitled"
    Health = 100
    Speed = 10

class Rabbit:
    def __init__(self, id):
        self.OwnerId = id
    
    def __setattr__(self, i, v):
        setattr(Animal, i, v)
    
    def __getattr__(self, i):
        return getattr(Animal, i)

a = Rabbit(123)
a.Name = "Spots"

b = Rabbit(123)
b.Name = "Furry"

print("My first pet is", a.Name) #: My first pet is Furry
print("My second pet is", b.Name) #: My second pet is Furry

From some dumb python executor:
image

This is happening because the properties are not inherited, but are still being used. In lua, this would be what you want:

local Animal = {}
Animal.__index = Animal

function Animal.new(id)
    local self = {}
    self.Type = id
    self.Name = "Untitled"
    self.Health = 100
    self.Speed = 10
    return setmetatable(self, Animal)
end

local a = Animal.new("Rabbit")
a.Name = "Spots"

local b = Animal.new("Rabbit")
b.Name = "Furry"

print(a.Name) --> Spots
print(b.Name) --> Furry
1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.