Link: Model
Download: Download (5.1 KB)
Summary (v1.0.0)
“You’re relying on the CLIENT to store data?”
Don’t worry, I’ll explain!
This module does the opposite of what you’d expect with data storage systems. Typically, the server gets, changes, and saves a player’s data. With this module, the server lets the client change the data while the server still saves it. To prevent misuse, the module provides no methods to the server side to get the user’s data. Only the client can manage it.
Now it’s easy to save information that only the local client would need to know.
Here’s some examples:
- Local notes on an item, promocode, map, or person
- A drawing the user hasn’t published yet
- Trade history
- Easter eggs found
- Check if tutorial is completed already
- Check if particular tutorial hints have been shown already
- New user checks
Remember, if anybody other than the local player or the server needs this information, this module would not be used correctly. You should instead link this with your own data system!
Exploit Concern
The obvious worry is exploits. What can exploiters do if this system is used?
Exploiters will have full control over this data. But, they also had full control over the entire client anyways, which is the only thing the data is actually used on… essentially giving them no extra power!
The only thing they could do is cause the datastore to be filled, causing slight lag when loading and saving. However, measures have been made to reduce this. Their data cannot exceed 75% of the character limit in their Datastore JSON. Encoding or decoding this JSON takes 25 ms max on average, assuming the maximum number of characters allowed using this system. However, normal usage should be even quicker than this!
Example Usage
For all of these examples, do the following:
- Add a folder called ‘Modules’ in
game.ReplicatedStorage
- Place the Cookie module inside the folder
- Add the following script to ServerScriptService:
require(game.ReplicatedStorage:WaitForChild("Modules").Cookie)
1: User’s unpublished drawings saved
Summary
SaveDrawing.rbxm (15.6 KB)
Here’s a preview of the client code:
local buttons = script.Parent.Buttons
local label = script.Parent.Label
local RunService = game:GetService("RunService")
local CaptureService = game:GetService("CaptureService")
local UserInputService = game:GetService("UserInputService")
local scale = label.UIScale.Scale
local size = label.AbsoluteSize / scale
local image = Instance.new("EditableImage")
image.Size = size
local offset = game.GuiService:GetGuiInset()
local radius = 5
--\\ Private Methods
local function getColor(): Color3
if UserInputService:IsKeyDown(Enum.KeyCode.LeftAlt) then
return Color3.new(1,1,1)
else
return Color3.new()
end
end
local function update(cookie: Cookie.Cookie)
cookie:Set(image:ReadPixels(Vector2.zero, size))
end
--\\ Modules
local Modules = game.ReplicatedStorage:WaitForChild("Modules")
local Cookie = require(Modules.Cookie)
--\\ Connections
local cookie = Cookie.new(`Drawing {size.X}x{size.Y}`, false)
local current = cookie:Get()
if current then
image:WritePixels(Vector2.zero, size, current)
else
image:DrawRectangle(Vector2.zero, size, Color3.new(1,1,1), 0)
end
image.Parent = label
update(cookie)
buttons.Clear.MouseButton1Click:Connect(function()
image:DrawRectangle(Vector2.zero, size, Color3.new(1,1,1), 0)
update(cookie)
end)
do
local down = false
local last = nil
label.MouseButton1Down:Connect(function(x, y)
down = true
last = (Vector2.new(x, y) - label.AbsolutePosition - offset) / scale
image:DrawCircle(last, radius, getColor(), 0)
end)
label.MouseMoved:Connect(function(x, y)
if last and down then
local now = (Vector2.new(x, y) - label.AbsolutePosition - offset) / scale
local iter = 10
for i = 1, iter do
image:DrawCircle(last:Lerp(now, i / iter), radius, getColor(), 0)
end
last = now
end
end)
label.MouseButton1Up:Connect(function()
if last then
last = nil
down = false
update(cookie)
end
end)
label.MouseLeave:Connect(function()
if last then
last = nil
down = false
update(cookie)
end
end)
end
And the result!
2: Note-to-self
Summary
Download (5.1 KB)
Here’s a preview of the client code:
local input = script.Parent.Input
local RunService = game:GetService("RunService")
--\\ Modules
local Modules = game.ReplicatedStorage:WaitForChild("Modules")
local Cookie = require(Modules.Cookie)
--\\ Connections
local cookie = Cookie.new("Note", "Hello world!")
cookie:SetReliable(false)
input.Text = cookie:Get()
RunService.RenderStepped:Connect(function()
cookie:Set(input.Text)
end)
And the result!
This type of functionality is used in Discord when you write something down in a channel without publishing the message yet!
API
local Super = require(path.to.module)
local cookie = Super.new("API", true)
Super.new(id: string, default: any?): Cookie
This constructor creates a new cookie.
It may yield if the module is still retrieving data from the server.
id:
The name of the cookie
default:
The value to be used if the cookie’s value is nil( YIELDING ) ( CLIENT )
Super:GetAll(): {Cookie}
Returns all the active cookies created by the client.
It will not return cookies that were previously saved, but have not been created by the constructor yet.( CLIENT )
Super:ClearAll()
Deletes cookie data on the client and server.
Deleted cookies will continue to function, but they will stop being saved.( CLIENT )
cookie:GetId(): string
That’s right, it returns the cookie’s Id!
cookie:Get(): any
Returns the cookie’s current value.
cookie:Set(value: any)
Set’s the cookies value. This updates the value on the server using a remote.
All cookie data for each player will not save if it exceeds the character limit. The limit can be found near the top of the module.
cookie:SetReliable(reliable: boolean)
If reliable, updating will use a RemoteEvent. Otherwise, it will use an UnreliableRemoteEvent when updating.
cookie:IsActive(): boolean
Returns whether the cookie is active or not.
If unactive, the cookie will not save. If a cookie fails to load in the first place, it will also be inactive.
Conclusion
You can get the model here!
If the link doesn’t work, you can also get it here: Download (5.1 KB)
If the asset isn’t public, you may have to remove the PackageLink inside the module! Roblox is giving me issues when I try to make it public.
Find a bug? Reply with the script that causes it, as well as background information on what you’re trying to do.
Made something cool with this? We’d love to see what you’ve made!