Should I use metatables or ModuleScripts for storing data?

I’m making an RPG, and I’ve only just started learning about metatables properly. My code is getting a revamp, so I’m wondering if it’d be a better idea to store all of the variables (Health, MaxHealth, CombatEnabled etc.) as a metatable attached to the player or to keep it in a ModuleScript.

If it was in a metatable attached to the player or their character, would I be able to detect when Health changes on both the client and server?

Also, I’ll be doing this with enemies too. I don’t know if I should just keep all of the info like targets, damage taken, health etc. in a ModuleScript or use metatables. Is it just preference?

3 Likes

I don’t personally see the benefit of using metatables here. Metatables are used for overriding operators (i.e. change the index operator “table.index” or “table[index]” to look in another table if it doesn’t exist in the table being indexed, The addition operator “+” to add two matrices together, etc.)

Modules can be used to store data but I often use them for configuration, not for data organization. I don’t personally see a benefit to doing that. Modules are most often used where one piece of code / data needs to be used in multiple scripts.

I personally think you should either use a folder full of Value objects, or have a script with BindableFunctions to act as getters and do all the processing in that script.

4 Likes

I could certainly see the appeal in wanting to link the index of a metatable to your player - it makes your code look nice and feel more logical, because then you could do something like this, which is certainly useful, but not entirely necessary:

View Code
local player = setmetatable({
	CombatEnabled = true;
}, {
	__index = game.Players.LocalPlayer;
});

print(player.Name, "has combat", player.CombatEnabled and "enabled." or "disabled.");

The problem is that a lot of people tend to go overboard and over-complicate their entire game by using metatables. If you’ve only just started learning about metatables properly, as you said, then it’d probably be best to stick with something else for now, because your goal is to complete your RPG and get it released. You don’t want to get stuck playing around with use cases for your metatables and being stumped with the rest of your code because you made it into a complex metatable jungle.

If you learn more and find better uses than simply indexing your metatables, such as creating powerful custom objects, then I say knock yourself out! I just wouldn’t use them simply for storing data.

5 Likes

I’m not sure that I understand how you’re storing this data. I don’t know what “store all of the variables … as a metatable” means.

Could you post an example of how you’re doing this? Could you also elaborate on what “attached to the player” means?


Generally, tables store data, and metatables store behavior. The metatable won’t change, but the table will. Since the table has a metatable, they work together to give you data + behavior.

Neither tables nor metatables give you any sort of automagic server → client syncing capabilities. You’ll have to handle that yourself with Remote or Value objects. One particular thing to note is that metatables do not get transferred through Remotes. This should be fine, however, since metatables should be static. You should be able to call setmetatable(data, behaviorMetatable) to get a metatable set up for the data table that you receive.


I can see a very good use of ModuleScripts and metatables here. By combining the two, you can get an “object” that automatically gets/sets underlying Value objects in a folder. This simplifies your code in a lot of places, since the module + metatable can take care of a lot of the work. The module + metatable can handle creating the Value objects, ensuring the right Value object is made for the value type being set, and possibly serializing/deserializing data.

Rather than use BindableFunctions, one can use modules and call the functions directly. You can have tables “attached” to instances and do something like Module.getAttached(instance) to get the relevant table. If this table has a metatable, you can then do things like Module.getAttached(player):damage(50) to damage the player.

In this case, the table contains a reference to the player, and the metatable contains the damage method in its __index. The table is describing the data (player), and the metatable is describing the behavior (how to damage the player).


I definitely agree. I follow object-oriented programming with my table/metatable organization for the most part, so it’s really easy for me to add on to. On the other hand, it’s taken me a lot of time to get a system that I like and that I’m comfortable with.

If you want to complete your RPG, use what you know and are familiar with.
If you want to learn metatables, or anything new, mess around with it on a project you don’t mind staying uncomplete or becoming a mess internally. Once you have something you know works and is structured well enough, start using it in projects you care about.

12 Likes

Just a thing to note; it’ll probably not be a good idea to store player data in ModuleScript.

You would need to require it every time you wanted to see the updated information, and there is no way to check when the ModuleScript’s contents get ‘globally’ changed.

A ModuleScript would only work in this case as a useful middleman for parsing data stored in the game as objects.

3 Likes