GreedyDataService || The simplest datastore solution that is session-locked and automated

GreedyDataService

A datastore solution that simplifies the player data management workflow

About

GreedyDataService is a simple module that allows you to easily edit player data from any server scope.

Highlighted features:

  • Simplicity; ability to easily get and edit user data, even if they are not in the server
  • Scalability; use in both your personal and professional projects
  • Automation for leaderstats; it can easily create leaderstat values for you, and updating the leaderstats value objects will thus update the values internally and still save (for those who use this workflow!)
  • Readability; the API is readable and easily understandable, allowing the developer(s) to quickly adjust to it
  • Session locking; prevent those pesky data exploits/bugs!

So, why should I use this over other data solutions?

I want to emphasize the ease of use for this resource. It’s simplicity is unrivaled, you will not find an easier data management system.

Many data management modules require more setup, meaning you become the one responsibile for managing datastore limits, setup structure, sanity checking, etc. With GreedyDataService, all of that friction is lifted; simply intialize a player’s data session with one line of code, which, if you want, also manages the public leaderstats values you see in the player list.

I made this resource because it feels like a huge waste of time setting up data management code with other resources. This module satisfies my needs and speeds up development!

Example script

local playersService = game:GetService("Players")

local greedyDataService = require(script.Parent.GreedyDataService)

local function playerAdded(player : Player)
	local leaderstatsSession = greedyDataService:loadPlayer(player)

	leaderstatsSession.data.Money = 1000 -- Sets "Money" to 1000
	leaderstatsSession:set("XP", 500) -- Sets "XP" to 500
	-- leaderstatsSession.data.whatever = value is the
	-- same as leaderstatsSession:set("whatever", value)

	-- For the rest of this, instead of using methods, I'll index .data

	leaderstatsSession.data("Joins", function(lastNumberOfJoins)
		return lastNumberOfJoins + 1
	end) -- Increments "Joins" by 1

	-- You can also do leaderstatsSession.data.Joins += 1

	if leaderstatsSession.data.Status == "new" then
		-- If a player is new, then say they are a rookie
		leaderstatsSession.data.Status = "rookie"

	elseif leaderstatsSession.data.Joins > 10 then
		-- If they joined more than 10 times, they are a visitor!
		leaderstatsSession.data.Status = "visitor"

	elseif leaderstatsSession.data.Joins > 100 then
		-- Wow, over 100 visits! They are now a regular.
		leaderstatsSession.data.Status = "regular"
	end

	print(leaderstatsSession:get("Status"))
	-- Prints "rookie", "visitor", or "regular"

	print(leaderstatsSession.data.Status)
	-- Should print the same thing again!

	table.insert(leaderstatsSession.data.Inventory, `DailyReward#{leaderstatsSession.data.Joins}`) -- Add something to a table
	print(leaderstatsSession.data.Inventory) -- Will get bigger every time you join!
	
	for i = 1, 3 do
		leaderstatsSession.data.TestBool = not leaderstatsSession.data.TestBool
		print(leaderstatsSession.data.TestBool)
	end
end

for _, player in playersService:GetPlayers() do
	playerAdded(player)
end

playersService.PlayerAdded:Connect(playerAdded)

:point_up: Key takeaway: local leaderstatsSession = greedyDataService:loadPlayer(player) was the only line of code needed to load and initialize the data.

Installation

There are currently two ways to install this: Wally and Roblox File

Wally

Keep an eye! This is coming to Wally soon.

Roblox File

You can download the Roblox file by going to releases.

More Info

:warning: This is a new resource and may be unstable. Let me know about any issues!

Here is an uncopylocked example place to help you understand this more! Below is the YouTube video walking you through this:

Patreon

If you appreciate this resource, please consider subscribing to my Patreon page for $1 a month! Everything helps and I mega-appreciate it :sparkling_heart:

39 Likes

This looks really interesting!

Would adding support for global leaderboards be something you would consider adding? I love the simplicity of this and its a pain to deal with OrderedDatastores when trying to create leaderboards.

5 Likes

Absoutely! I need this functionality anyways.

5 Likes

This looks like a pretty cool way of handling datastores damn

5 Likes

Can you elaborate on this further?

Seems like a questionable design choice to me.

6 Likes

This is made possible with boatbomber’s HeatUp module:

You are still using data stores, but memory service is responsible for changing your data saving keys, bypassing the data store limits.

5 Likes

I’ve added a YouTube video to help you all out!

3 Likes

Roblox imposes a 6 second cooldown per key (across all your servers) in Datastores

I’m pretty sure that’s false.

Datastores operate on a queue system - if you exceed 60 (+ numPlayers * 10) requests per minute in a server, subsequent calls will throttle.
Short of the inherent ping delay that comes with a HTTP call, there is no delay between calls to speak of, provided they don’t exceed the queue limit.
Feel free to test this though, maybe there’s something I don’t know of.

You realistically shouldn’t be hitting datastore limits either if you implement a server cache, do all your data operations on said cache, and only save the cache to datastores when you are irrevocably done with it.
That’s how ProfileService does it anyways, and it works.

It’s not entirely your fault here, I just don’t see the point of HeatUp at all.

7 Likes

I think this was increased, but there is a cooldown per key. HeatUp removes the stress about this and replaces it with the much more flexible memory store service limits.

This also simplifies things since the code doesn’t have to do a bunch of caching and stuff, which in turn makes it more readable.

4 Likes

I also don’t see why a cache would make things unreadable here.
It literally just means that instead of operating on a datastore (which comes with its own problems), you operate on a server cache (eg: a module script).

5 Likes

Thanks for letting me know about that announcement.

I looked at the current limits and this is what I found:

Based on this I think it’s still much better to use MemoryStoreService:

Is it overkill? Probably. Nonetheless, it doesn’t harm anything to use it.

4 Likes

Whatever floats your boat here, there’s just two things to keep in mind here:

  • If you do proceed with MemoryStores, you are now subject to twice the amount of HTTP calls you will need to make.
    That’s twice as likely your data operations may drop and mess something up.

  • If you decide to drop this feature entirely, you will basically be making a carbon copy of ProfileService.

8 Likes

I should say not quite, since the setup of this is easier. Having to create and resolve sessions in profile service requires a lot of boilerplate code, while this just requires one single function call. I made this out of frustration coming from how unnessesary it is to write the boilerplate for profile service, and I wanted something simpler to speed up development.

I may drop heatup tho.

4 Likes

That’s fair.

I wouldn’t use this myself still because I value reliability over convenience when it comes to data, but I think it’s fine for simple projects.

5 Likes

I’ll definitely keep stress testing this as I do want it to be scalable for large projects, but as of now, definitely just test it in smaller scopes!

6 Likes

Could be useful for leaderstats and those kinds of stuff.
Nice work, i may be seeing myself using it when i am testing stuff.

6 Likes

That’s not a negative, that’s a huge seller for ProfileService. Much like BridgeNet2, although its API can be used raw, you get the most out of these resources when you write wrappers or libraries around them. As for the “complexity of resolving sessions” being spoken of here, it can be handed off to ProfileService with the ForceLoad string - you don’t need to concern yourself with any of that unless you want tailored session management logic. The setup is not as complex as you think.

The “unrivaled simplicity” of this module wrestles away control from developers about how to reason about data loading and session conflicts. Boilerplate can surely be annoying, though ProfileService is highly designed for that use case - it can be standalone, or integrate with your own code, which I found in every case (production-level experiences to be specific) that writing wrappers for ProfileService makes it shine the best, even better if it’s tailored.

A lot of developers are creating new resources to create competition against ProfileService which is respectable, but simplicity is not a high selling point especially if most of the loading logic is black boxed into the module itself and it doesn’t offer feature parity or superiority for more complex use cases given that this is suggested for dedicated projects as well.

My two cents: if developers are having trouble with ProfileService, it would be better to just write universally applicable wrappers for everyone to take advantage of so they can have the full benefits of ProfileService and more of the complex things handled already by said wrapper. It would be nice to see developers creating wrappers rather than adding to the already saturated market of less-feature alternatives over some small gripes with existing battle-tested designs.

I like ProfileService and Lapis on the account that they allow a great amount of extensibility. On the other hand though, this resource doesn’t sell me much beyond the point of “simplicity” which is a very common footgun for resource developers to sell on. Simple doesn’t necessarily mean better, it could even be harmful to an experience’s data architecture.

The section that tries to market itself as an alternative (“why should this be used over others”) is not really that convincing and tries to tack on real benefits as demerits against using them. I could write a wrapper for ProfileService that handles exactly all the same features offered by this resource without requiring developers to need to do “heavy setup”. Now, this doesn’t mean objectively that this resource shouldn’t be used - you have the freedom to pick whichever resources you feel best suit your needs and level of expertise as well as how willing you are to learn its application. It just doesn’t feel like a strong seller if you want to enter the saturated DataStore resources market.

10 Likes

Agreed. And I also believe there are a couple of wrappers for ProfileService anyways.

2 Likes

You should make it so that the client can access their own data through the same module!

This would make getting public/private data easier, and may also make saving incompatiable data types (Colors, Vector2, etc) easier. It would use the same methods, except Setter methods wouldn’t work, and they can only get the localplayer’s data.

3 Likes

Fair points, but something I would like to mention is that simplicity over functionality isn’t always bad, that is, if that functionality isn’t being used. In my experience, my wrappers for it don’t do anything extra than what’s needed for just basic saving and loading, which is why, at this point, why not use this instead?

Part of me is frustrated that there are so many data store libraries on the DevForum since it does just feel like a “here we go again” type feeling.

“Simplicity” is a bit cliche on the forum here, but in this case I think it’s a fair sell. Lots of dirty work is automated, including session locking/unlocking, playerstat leaderboards, value/valueset declaration, reconciliation, and of course the base functionality.

I see myself using this over others now since I don’t use the extra functionality of external libraries and prefer a straightforward approach to set up a game with ease.

Which features would you like to see specifically? Admittedly it is a bit unhelpful to not know, albeit your feedback is nonetheless valuable.

I would like to point out that I am working on gdpr compliance.

Last note, I also am trying to gather feedback from other experienced resource creators, including loleris himself, to help improve this. Consider this an early baby!

3 Likes