Hi! I am re-introducing a player data saving library which aims to have every feature a developer could need for player data storage, while remaining extremely easy to use.
you can find it here: DMOP Player Save Library - Creator Store
The reason for the re-introduction is the addition of custom data types, which brought DMOP from useful to a godsend for me. I plan to keep the library free until I add a lot more of the base roblox data types.
Features
- Supports data structuring
- Supports all basic data types
- Supports ordered and associative arrays
- Currently supports 3 roblox data types (CFrame, Vector3, Color3)
- Supports custom data types (very easy to do)
- Built-in autosave and load (which is easy to disable)
- TableToModel instancing function for custom data types
- ModelToTable parsing function for reading saved data
- Player data loaded bindable event and optional remote event
General Overview
(I will copy and paste this first section from my first post)It functions by using a DataModel stored in ServerStorage. Within the DataModel folder, Player save data can be structured, like so:
This setup (assuming DMOP is in ServerScriptService, and Kills is an IntValue) is all a developer would need to do to setup leaderboard stat persistence.
-
Data is saved using the DataStoreService in a DataStore called PlayerData using the UserId of the associated player
-
Any Value object who’s property can be saved with DataStore is supported.
-
Folders are considered to be tables by default
-
The name of items is their key in the table
Arrays
DMOP also comes with advanced functionality, such as declaring indexed arrays:
This structure declares that PlayerData.PlayerTags is an ordered array, and items in that array get their value from the name of the folder in PlayerTags.
- Do note that that the exact ordering of these arrays will not reliably be preserved due to the use of GetChildren() for saving these arrays
- These are best used as lists of basic types which can allow for repeated values
DMOP supports associative arrays as well, which are very similar but also have exclusive functionality:
This structure declares that PlayerData.PlayerTags is an array, and items in that array get their key from the folder name in PlayerTags.
- In the example pictured above, the value of these items in memory is an empty table.
Structured Arrays
This is where the power of DMOP starts to shine, if you use an associative array, you can further define the structure of the array:
This structure declares that PlayerData.PlayerItems is an associative array. I have specified that items in that array have an ordered array called ItemTags, as well as an integer value called Count.
After specifying this, data can be added to a Player.PlayerItems instance following the structure, and it will be saved when they leave or the server shuts down.
The Interface:TableToModel()
function can be used for instantiation using Lua tables following the structure you have specified.
Example using the most recent structure pictured:
Player first joins:
Instancing script:
local model = game:GetService("ServerStorage").DataModel.PlayerItems.key
local params = {
Count = 1,
ItemTags = { "Heavy", "Sturdy" }
}
Interface:TableToModel(params, model, "Rock").Parent = plr.PlayerItems
Player after instancing:
This is just a one item example, but you can have as many of these items with unique IDs as you want (because PlayerItems is an associative array).
Data Type Support
DMOP currently supports 3 roblox data types by default - CFrame, Vector3, and Color3
You define a folder in the DataModel to be one of these special types with the syntax “name-type”, like so (the type is not case sensitive):
This structure defines a PlotInfo directory in the Player instance. Within PlotInfo is an associative array called PlacedStructures. Items in the PlacedStructures array have a CFrame value called CFrame, and a Color3 value called Color.
When calling TableToModel
, the Roblox Instanced data types can be used. For example, this is how you would instance a member of PlacedStructures:
local model = game:GetService("ServerStorage").DataModel.PlotInfo.PlacedStructures.key
local params = {
CFrame = SomeCFrameInstance,
Color = SomeColor3Instance
}
Interface:TableToModel(params, model, "ExampleObjectID").Parent = plr.PlotInfo.PlacedStructures
Side Note:
- Although I have stated multiple times in this article that you CAN instance items this way, It is much more performant (and readable) to only do this when necessary for the ENTIRE array. For example, the previous code becomes this and runs when a write to the MemoryStore in the Player instance is needed:
local PlacedArrayModel = game:GetService("ServerStorage").DataModel.PlotInfo.PlacedStructures
local playerPlacedTable = {
[1] = {
CFrame = SomeCFrameInstance,
Color = SomeColor3Instance
}
-- ... and so on for every value to save
}
plr.PlotInfo.PlacedStructures:Destroy()
Interface:TableToModel(playerPlacedTable, PlacedArrayModel).Parent = plr.PlotInfo
- Do keep in mind that it is possible to use
Interface:ModelToTable()
to obtain a table such asplayerPlacedTable
instead of creating it yourself like I showed just now
Custom Data Types
Any associative array can be turned into a data type that DMOP can parse, and this functionality is also what allows DMOP to serialize/deserialize roblox instanced data types.
For example, if we use the previous structure and wish to make a custom data type for placed objects, we can simply duplicate the data type template which is a child of the Interface
module, name it to wanted type name (all lowercase to be valid), and then directly drag-and-drop the structure from ServerStorage into the module. Like so:
The DataModel can then be refactored like this:
And it has the same functionality as before, but now placeable
is a re-usable data type
There is no issue with defining multiple custom types that use each other. For example, I can make another data type which has a placeable
value within it.
If you want to add support for more Roblox data types, I would suggest reading the other module scripts inside the Interface
module to understand how to do that. It is also quite simple, although I will not explain that here.
Conclusion
I hope you all find DMOP as useful as I have. With respect to saving and loading player data, it has eliminated my struggles entirely. It allows me to focus on structuring my save data effectively rather than having to carefully manage serialization, deserialization, and save/load functionality.
There are many more use cases for the library DMOP provides other than player save data structuring. You can call the TableToModel
and ModelToTable
using any DataModel, so you have the freedom to parse structured values as you please.
Let me know if you have any feedback or questions! I will always try to respond as fast as possible.