ProStore3 - An elegant object modeling DataStore manager


Documentation | Source

Introduction

ProStore3 is a simple open source library used to easily manage and dynamically update a Roblox’s game database. It uses a session lock approach in which it stores and manipulates the users data from the moment they join the session to the moment that they leave.

This library allows you to add a database to your game in literal seconds! It has basically no learning curve and works out of the box!

I’ve been using this library for the past 10 months and decided to make a post about it now that I’m fully sure that it’s not only stable as it is scalable to larger projects.
In it currently being used by multiple games with hundreds of active players including Roblox Events.

Examples of a game using it:

Why would you use it?

  • Saves a huge amount of time
  • Very easy to edit/get data at run-time
  • Data can be dynamically modified in the future meaning that adding a new value won’t overwrite any existing data
  • Forces a schema similar to Mongoose or any SQL based database
  • Helper events to easily update visual information in relation to user data

Tutorial

This tutorial will show you how to set up ProStore3 and its following advantages!

Installation

First you will need to download ProStore3 and drag it into your project. You can either download it from GitHub’s main branch and drag and drop into your Rojo project or download the free model.

I personally recommend keeping your copy somewhere around ServerScriptService

Setting up

There are 2 important files inside of ProStore3. These are the schema.lua and settings.lua. These 2 files will determine the data schema of your game and how should the database behave internally.

Information about the Settings: :wrench:Settings | ProStore3 (prooheckcp.github.io)

Schema

In this file you should return an object that resembles how you want a players database to look like. The values you set will be used as default values. In the given example, as an example, the level will be locked into a number type and the player will start at level 1 when he first joins the game.

One of the greatest features about ProStore3 is that you can add more values into this schema at the future (or remove) and it won’t corrupt any already existing data.

return {
    Level = 1,
    Inventory = {},
    Profile = {
        SomeInt = 2,
        Currency = 100,
        NestedProfile = {
            MoreNested = {
                Another = {
                    value = 3
                }
            }
        }
    }
}

Using ProStore3

As soon as you require ProStore3 from any file in the game the library will start working and saving/loading players’ data! So no more setting up is required! But how exactly do we access/modify data from the player’s schema?

local ServerScriptService = game:GetService("ServerScriptService")
local ProStore3 = require(ServerScriptService.ProStore3)

ProStore3.PlayerJoined:Connect(function(player : Player)
    local level : number = ProStore3.Get(player, "Level")
    local currency : number = ProStore3.Get(player, "Profile.Currency")

    print("Level: ", level, " Currency: ", currency) -- Level: 1, Currency: 100

    ProStore3.Set(player, "Level", 3)
    print(ProStore3.Get(player, "Level")) -- 3
end)

And that’s about it! You can start using the library as soon as u finish writing your schema as easy as that!

Events

ProStore3 also comes with a few events to help working.
PlayerJoined (I would highly recommend using this instead of Players.PlayerJoined event as it gets called after the players data finishes loading)
PlayerLeft
DataUpdated

Limitations

ProStore3 was designed to only work for players. You need a Player instance in order to get and or update data. It cannot be used for stuff like analytics and global data.

Fun Fact

This is the third time I write a fully managed DataStore library hence the name “ProStore3” instead of just “ProStore”.

Please leave your feedback down and or a⭐on the GitHub

16 Likes

Thats cool not gonna lie, I could use this

Watch out loleris, heres something new

Is it better than ps

I wouldn’t say it is better but rather an easier-to-use alternative that follows a different approach to databases. The main goal of this library is to force you into using schemas for data consistency (similar to how mongoose forces MongoDB into using schemas)

1 Like

Ok, easier to use is better than nothing

Would love to add it into my Plugin, If that’s of course fine for you.

It’s completely open source, feel free to do whatever u want with it

1 Like

That’s plugin is hella cool and easy to understand, for real. But I have one question. How do I bring out data on client-side? For example, show amount of money you currently have. I tried ProStore3.Get from local script (client-side) and it says DataStore can’t be accessed from client. I really have no idea how can I show amount of something on, for example, TextLabel.

Use remote events/functions to pull the data.

Yeah, that helps. I’m pulling it by Remote Funciton. Thanks for help!

how do I setup the __dynamic option? it has both true and false under the default schema you provided

__dynamic refers only to the scope it is declared in. In the example one of the tables is dynamic and the other isn’t. By default all tables have dynamic set to false

oh alright, so how do i make sure dynamic array is true? https://gyazo.com/41bfad0874a79b3715afac478f831cf5

Inside of the Schema you should add __Dynamic, let’s take this as an example

return {
   money = 0,
   inventory = {}
}

In this case you will want the Inventory to be dynamic to change the elements inside of it, you can simply add “__dynamic = true” inside of it to make the inventory dynamic

return {
  money = 0,
  inventory = {__dynamic = true}
}

Now that you added this ProStore will respect the inventory array as dynamic and will allow you to make changes to it

Hello, I am rewriting my game’s data to use this module, but I’m already encountering a major issue. I have set up a new DataStore name and when I join in Studio, data is properly given to my player but the ‘firstTime’ value is always false, even though it should be the first time I’ve joined (according to my data). SaveInStudio and LoadInStudio are both true, and I was hoping to use the firstTime feature.

I’ve been seeing a lot of creative implementations of alternatives to ProfileService. Of course, as it stands, ProfileService typically ends up on top with more features like session locking, but typically when it comes to “just the data store itself” there’s a better way to do it than with what ProfileService provides.

A DataStore with no API call limits or size limits, tada, it exists: Boatbomber’s FreedumbStore.

A DataStore with very simple ways to access it and a baked schema, tada, it exists: this module.

Sure, if you would want session locking and all of the other features a service like ProfileService implements you’d have to do it yourself. But since ProfileService is already open source, you can simply look at what’s there and find out how to integrate such a thing yourself.

That’s odd, it should be set the first time to true when it cannot find the user’s data in the DataStore, I will check it first thing tomorrow and fix it if that’s an issue. I’ll keep you updated about it

I’m considering making a big update to this library since it will soon be used on a pretty large-scale project, ProfileService is def something I will be looking into when I do it. ProStore was originally designed around “Mongoose” and “DataStore2”, I had barely used PS so I didn’t look into its features at the time

1 Like

I would also recommend looking into FreedumbStore, it may be very technical but it would essentially allow your datastore module to be able to avoid Roblox’s DataStoreService limitations (API request timeout, GlobalDataStore size limit).

I am now encountering another issue, I am attempting to set the value of a key that’s located as such: PlayerTable → Telemetry → ChosenFirstCar(key).

When using .Set, I am using “Telemetry.ChosenFirstCar” as the Path, but ProStore tells me that the path does not exist even when it clearly does (printed the GetTable).

I have actually found a solution, in your recursiveFind function, you currently check for ‘if not value’ when you should be comparing the value to nil in the case of boolean values. In my case, the ChosenFirstCar value was false, so recursiveFind falsely believed it is the same as the value/key not existing.

1 Like