Hey y’all! Recently, I’ve been working on Datastores, and I’ve ran into this issue regarding Instance values. As we all know, you usually use an IntValue in the player object or players leaderstats to track and hold the players money. This is useful, as it allows both the client and server to access the Value. However, I ran into the issue of “Just how many instances can you have?” I have one folder that holds plenty of BoolValues to check if a player owns a weapon, or vehicle, or item. All of these instances can add up, and that’s where my issue comes in.
Just how many instances should you have? Can you have even? Should I try to figure out another way around using plenty of instance values, or should I keep using this system?
I know some people would say don’t use instances in the first place but I disagree.
Data holding objects are good for:
A few, distinct number values (coins, exp, gems)
A couple bool values (owns 1 of the 3 gamepasses in the game)
Data not requiring more than ~5 values
Data holding objects are not good for:
Many number values (car1 level, car2 level, car3 level…)
Many bool values (car1 boosted?, car2 boosted?, car3 boosted?..)
Certain data requiring more than ~5 values
Where should I store those values then?
A great way to store values is in a module script. This gives you a lot of freedom because you can store any type of value in nested tables for easy access. Clients won’t be able to set data in the module so, unless you need completely obscured data, you are fine putting a module script in something like ReplicatedStorage. Then, once you need to get data, just require the module and index the data.
Edit: I should note that when using a module, changes on the server DO NOT sync with the client like most things on roblox unless required again. The easiest way to sync data is probably through Replica as stated by others unless you are okay with the client not getting current data from the server.
You dont have to worry about datastore limits unless you are really storing huge amounts of data, and you are not adding large overhead for no reason. If you want an exact number:
Example: StringValue of size 2KB (2 thousand characters, extremely unlikely)
Strings in 1MB: 1/0.002 = 500
500 very long strings in 1MB
Example: IntValue of size 16Bytes (8 bytes for the value, 8 bytes for the key)
Integers in 1MB: 1/0.000016 = 62500
62500 integers in 1MB
Keep in mind that each instance added is yet another instance, and more instances will drop performance.
As @EffBeeCee said, you should look into modules and/or tables for storing data, as it is more memory efficient than hundreds of individual instances.
local MyData = {} --Example use of tables
MyData["Coins"] = 5
MyData["OtherCurrencies"] = {} --Example of nested tables
MyData["OtherCurrencies"]["Gems"] = 8
print(MyData["Coins"]) --Prints 5
print(MyData["OtherCurrencies"]["Gems"]) --Prints 8
Hmm, I attempted something like this as I thought “Well Modules are just really big tables that I can hse anywhere, so why not use it for this?” And unfortunately, due to my lack of experience, I couldn’t get it to work properly. However now that you say this I may need to try it.
But before I do, I need some help:
Is it possible to change the values of a module script outside of that module script, and get still be able to get the table correctly afterwards? When I tried to do it beforehand with a Keybinds system I was making, I could update the values, however when I would print out the same module later, it would act weird.
Anyway or idea you can give me to go around this? I was most likely doing something wrong.
It surprises me how much data can be stored in one datastore. Using nested tables is a good idea though.
So far, my method is to use a bunch of instances, and when the player leaves I get a table template from a module script and input all the values into there. Then when the player rejoins, all the data ia sent back. It works perfectly, it just acts strange. I also like using instances too as both the client and server can access them at all times, and you can listen to .Changed events. I believe I’ll use some for the XP, Level, and Money values in my game, but the rest may not be needed.
Aslong as you’re aware of the extra performance being taken by using instances i would say you’re fine. The method you’re using for saving data works aswell.
Do you perhaps mean doing this?:
--Script 1
local DataModule = require(PathToModule)
if not DataModule[PlayerName] then DataModule[PlayerName] = {} end
--Above line makes the specified playername get their own table
DataModule[PlayerName]["Coins"] = 5
--Script 2
local DataModule = require(PathToModule)
print(DataModule[PlayerName]["Coins"]) --prints 5, aslong as script 1 runs before script 2
I usually dont do data management in this way, but im fairly certain it would work
No, the old system I was thinking of was copying a new Module into the player object with a preset table where it’s data was set by a server script to the players data. I’ll have to get on my computer and show an example.
Had to find out a lot with making my own data stores framework open-sourced but this is kinda an intresting one. Generally, you can get away with a ton since the maximum that can be saved per save slot is 4MB (which if you’re not working with a tycoon, it’s difficult to get to even half of that).
Bottom line, don’t worry about how many you can have, it’s actually very difficult to reach the save slot limit anyways.
How much data I can hold isn’t the issue, it’s how I can store that data and keep it accessible by the player and server, which updates consistently that is. You mentioned tycoon which actually makes me curious; how do Tycoons hold and save data?
Am I right in saying that you don’t want to do it the conventional way of storing it in the player instance (or as an instence type anywhere in the gamespace)? Also, tycoons can be very flexible themselves in how they do it, smaller and more traditional ones might do it with bool values where as other’s like ones that can be more free might save and serialise instances themselves. I guess it’s just creator preference
Honestly, having had to try figure out a lot of complex stuff to do with data management and data frameworks in Roblox, everyone has a personal preference and a valid reason for their methods. Here’s my take.
Putting values in the Player object is perfectly valid and has been a convention of player data and data stores within the Roblox engine ever since they introduced DataStoresService. I use it in my own open-sourced framework (though keep in mind that is designed for anyone of any skill level to easily and quickly use so that’s something else to consider). On that topic, it is way easier to access and manipulate then other methods.
Putting them in a dictionary table (like a jagged array) is also an extremely good idea and this is what happens in data loss prevention modules. This way can kinda feel a little more tricky but the security it gives you is great if you want to prevent hackers even thought you’ll need it in there for leaderstats anyways.
I guess it ultimately depends on what kind of game your making (which will also depend on it’s player data dependency’s) and what you need it to do. I have an open-sourced framework for data stores management that I can provide that works by puttint them in the player instance but allows you to be able to change and manipulate what and how things are saved or you could go for something like PlayerStores (or your own custom made) if your game need more security guarantees.
This is why this pattern of unpacking Datastores data into ValueObject instances continues to be used today, even by games which do not use classic LeaderStats. The tradeoffs vs using Lua tables and RemoteEvents are:
Pros (of value objects)
Free replication of value changes to the client, no need for RemoteEvents
Instances are visible in the Datamodel and easy to inspect while testing and debugging
Fast initial implementation, and OK when you have just a couple of stored values
Cons:
Scales poorly, as each value (bool, int, etc) has the memory footprint of a full Instance, using more than 10 times the memory per value than the same data in a variable, Lua table, or Attribute.
Slower to unpack large data, since creating new Instances is expensive
Needs code to pack/unpack from the object values into a table in order to store in Datastores.
Think about that last one: You need to make a Lua table to put the data into a Datastore. So with value objects, you pay the full cost of creating and operating on the instances, and you have to make the Lua table anyways… the same Lua table a lot of developers just use as the in-memory copy of the data!
The biggest drawback of moving to a Lua Table solution is that you have to do a little bit more work up front to set up the RemoteEvents to broadcast changes to the player data from server to client(s). Normally, this involves writing a ModuleScript wrapper around the player data table, or using metamethods on the table to track when things change and clients need to be updated. It’s not a huge deal, just a small amount of boilerplate code, but it does require a bit more Lua programming knowledge than using value objects.
A middle-ground option is Attributes. Server scripts can add Attributes right on the Player object if you want the values for All players to be available on ALL clients (e.g. for public leader stats), or to the PlayerGui if you want the values to be private and only replicated to the player whose data it is (e.g. for unlocked items, or showing wallet currency amount). These are lighter-weight than value objects in terms of memory and CPU usage, so they scale better much than one instance per value, and they replicated server-to-client just like value objects. What you lose is direct LeaderStats compatibility though, if that’s a real concern (never has been for me, but YMMV).
It’s been such a hot topic for data management in Roblox for years and I find it so funny how we can’t come to a solution because there are so many options with so many reasons. I think if you need it to be an instance anyways then it’s always best that it starts off as an instance anyways reguardless of method. (also completely forgot attributes was a possible option)
Because they are not instances, a lot of developers don’t even know they’re a thing! The whole point of Attributes was to solve the problem of developers being able to add their own data to instances, without the memory cost of value objects. They are intended to be a direct replacement for most types of value objects with better memory and runtime performance. They support more data types than value objects do, the exception is that they don’t support object references, so cannot replace ObjectValue instances. But that’s a moot point here, because you can’t store object references in a Datastore anyways.
This whole discussion has been incredibly interesting!
I may have to mess around with attributes. I’ve always been aware of their existence, but have never actually used them. Thanks for enlightening me on that!
A method I might do is this:
For things that are always changing like Money, Level, and XP, I’ll use instance values (or if I get comfortable with attributes, most definitely them considering what you said), and for things that don’t require the client really checking, then I’ll go ahead and use the module setup for that.
Also loving the input’s on this giving me some ideas. I literally just noticed that attributes also have change detection event’s as well which I swear wasn’t there the last time I checked and I’ve literally just went though the pro’s and con’s of each saving type.
My final conclusion is this:
Instances if you need to be able to retrieve content that will be publicly viewable or just need an easier way of viewing tabled/arrayed content
Lua Table’s (like jagged arrays) if you actually understand what you’re doing to a great extent AND you need to be able to have tabled/arrayed content saved and/or need higher security (like from a module but honestly it’s not that big of a deal is the majority of cases)
Attributes if you’re focusing on easy to use and easy to access data that is primarily focused on values (tabled content is possible in attributes as long as you understand how to retrieve individual lua table content)
Honestly, really glad that I noticed this forum post and I’ll need to work on implementing these options to my own frameworks better.
You could probably have many hundreds per player, and it would possibly be fine (not talking about datastore limits, just instances). Instantiating all of them might start taking a couple milliseconds though