Open-Sourced DataStoreLoader API using OOP

Really hate coding to save a player’s data? Well hate no more with my DataStoreLoaderObject Class that has type annotation and easy usage.
Ive decided to make this for no reason at all.

======================== Contructor ========================

Construct
Code Example on how to create a new DataStoreLoaderObject or loader for short

It only takes the datastore name to create one, if none was given then it will return nil and error. You can optionally put a table as default data if data was unsuccessfully loaded / saved.

It has a registry so you can get a loader if it was created

Get
Code example on getting a loader

But you can just use CreateNewDataStoreLoaderObjectSync as it returns existing loaders if there are ones.

=================== Loader API ==================
Methods -----------------------------------------------------


GetDataStore() : returns the datastore associated with the name given
GetName() : returns only the datastore’s name
GetDefaultData() : returns the default data if there is one else nil


LoadDataSync(Key, Callback) : Loads data. Takes a key and an optional callback. After the data is loaded, it will be registered in the session which is necessary for the other methods. After the data loads, the callback will be called giving Success and the Data


WaitForDataLoadAsync(Key, Timeout, Callback) : Just like LoadDataSync but it waits indefinitely just for data to load. It runs on another thread to return an unique OperationId string. The data is loaded for Timeout seconds which is clamped between 0 seconds and 10 minutes, defaultly 2 minutes. After the data is loaded or the wait was cancelled, it will call Callback.

CancelWaitForDataLoad(OperationId) : Using the operation id returned by the WaitForDataLoadAsync method, will cancel the wait and in the callback of the method will get false and nil.

GetSessionInfo() : returns a table which is the session and the SessionLock which prevents data duping and loss. A session is a table full of keys loaded by LoadDataSync or WaitForDataLoadAsync, nothing can be registered in the session without loading or raw modifying.


GetPath(Key, Path : {string}) : returns the value in a path of a data. It takes a table full of strings or keys which tell the hierchary. This is made for nested values. If the path leads to nothing then it returns nil

SetPath(Key, Path : {string}, NewValue) : It sets a value of a key using the path for nested values. If no more path was found then it creates a new table until it reaches its goal meaning it creates intermediete tables on the way. Sets the last value of the path to NewValue

SetPathNoCreate(Key, Path : {string}, NewValue) : Just like SetPath but it only sets a value if the path exists. If the path is unexisting then it will stop, else it will set the value of the last value of the path to NewValue meaning it doesnt create new paths.

PathExists(Key, Path : {string}) : Checks if a path exists in Key. If it does exist then it will return true, else false.

For keeping track of data or Session Methods

Serialize(Key) : returns a deep copy of the session or a key value without its session lock.

GetSession(Key) : returns the raw session or a raw key value.

ResetToDefault(Key) : resets the value of all or a key to DefaultData if there is one. If there is no key given then it will save all data and reset the session to an empty table.

ExportSession() : returns a deep copy of the session.

Set(Key, Value) : sets the value of Key to Value.

RemoveKey(Key, Path : {string}) : removes a key or sets a key to nil using Path in Key.

Increment(Key, Path : {string}, Amount) : increments a number value by Amount in a Path of Key.

Append(Key, Path : {string}, Value) : appends a table or adds Value to the end of the table which is the last key of Path of Key. If path doesnt exist then it stops.
Merge(Key, Dictionary : {}) : merges the entire key session with Dictionary.


SaveDataSync(Key, Callback) : Saves data for Key and whether it was successful or not, Callback giving the key is called if it exists.

CleanUpSession(Key) : Cleans up the session of Key or if none was given then the entire session. Recommended after a player leaves the server.

GetValueFromStoreAsync(Key, Callback) : Runs on another thread, returns a value using

Key. Then calls Callback with the 1st argument as the success and the 2nd as the value got.

Destroy() : Attempts to save keys, cancel operations, destroy signals, and unregisters.

Events -------------------------------------------------------

OnDataLoaded : Fires after data successfully loads, returns the key and the data that was loaded.

OnDataLoadFailed : Fires after data fails to load, returns the key that was attempted to load.

OnDataSaved : Fires after data successfully saves, returns the key that was saved
OnDataSaveFailed : Fires after data fails to save, returns the key that was attempted to
save.

OnSessionChanged : Fires after the following methods called SetPath("SetPath"), SetPathNoCreate("SetPath"), ResetToDefault("Reset"), Set("Set"), RemoveKey("Remove"), Increment("Increment"), Append("Append"), Merge("Merge"). The strings next to the method names are the action name returned as the argument #2 of the callback of the connection.
Example Code :

loader.OnSessionChanged:Connect(Key, Action)
    print("Session was changed for "..Key.." by "..Action)
end

OnSessionReset : Fires after ResetToDefault was called. The 2nd argument of the callback of the connection will always be "Reset" and the 1st is the key associated with the reset.

OnDestroying : Fires after Destroy is called.

Here’s the module sources.

DataStoreLoaderModule / MainModule

DatastoreLoaderModule.lua (27.7 KB)

I dont think you have to read all of that since there’s type annotation and explanatory parameters, method names, and signal names.

This is my first API and havent really tested it yet though :upside_down_face:

isn’t it supposed to be “Async” instead of “sync”?

1 Like


I’d recommend you to use custom signal instead of BindableEvent. Signal+ as an example.
image
A memory leak, because you’re making a new table with the same content each constructor call.

Also every “Connect” methods are pretty much useless. and only output higher memory and CPU cost, which is fixable with using custom signal.

It’s pretty good for first API, well done.

2 Likes

Not only is it bloated and over-engineered with fake OOP, it doesn’t even solve the core problem.
No real session locking, no proper autosave, and half the API exists just to compensate for its own design flaws.
I made a ~200 line functional/FP implementation that actually handles session locking, waiting, and consistency - because it focuses on the job instead of ceremony.

2 Likes

You so pro guy, but all this is for nothing D:: may you explain whats real OOP?

is not sync work with the flow of the consumers (scripts that use this) and async is from somewhere else?

will use this on my next API, thx

there’s no formal “Real OOP” nor “Fake OOP”. The main concept of object in Luau (and Lua) is tables. Formally, people call it a “prototype-oriented programming”, where objects are created through a premade prototype. The metatables allow to create class-alike structure (which is made kind of badly).

Yarik sees Luau’s POP as not using metatables in general, which is reasoned by his desire for heavily perfomant code, but it results in slight OR huge (depends on amount of objects and fields) memory impact.

This POP method is very reasonable for server because it has a pretty huge amount of RAM available (6.4 gb at minimum, source - Even More Server Memory, where (usually) 1400-2500 megabytes will be used for engine instances, server itself and etc., so around 4 gigabytes free RAM). For client it may differ as people have different devices with different specifications and different running apps at the playtime. It’s up to you to decide to optimize RAM or CPU.

The said things are negligible. The real danger is still not the metatables but the structure and code. You should worry about making an efficient structure first, and after that think on using metatables or not.

1 Like

I don’t think you understand what these words mean

1 Like

??? soo what do they mean then brother

so dont my thing do dat? or that isnt how funcs works

datastore get async yields but it is asynchronous?