User Save Data
Usage
User Save Data provides an isomorphic way to persist data on a user. A lot of the time, I want an item that a user is interacting with to save something to the user - like the time last interacted, or something like that. Something that really only matters to the world entity.
This is my solution.
Installation
Search for User Save Data in community packages.
Drag the User Save Data template onto the user User.
Drag the User Save Data template onto the user User.
Usage
This package makes 2 methods available to you.
SaveData(key, value)
SaveData accepts a string key as the first argument, followed by a string or number as the second argument.
This method works on both the server and the client.
GetData(key)
GetData accepts a single key, and will return the data for this key.
This method works on both the server and the client.
onSaveDataReady
The saveDataScript exposes an event called onSaveDataReady that will fire on the server and local client when the data has been retrieved from the server.
This is useful if you have something in your game that depends on save data, and you need to wait for it to be loaded before initializing it. For example, take a widget:
-- During initializing of the widget, we want to show how much money the user has, -- but if we do this before the data is loaded, we'll get the incorrect value function MyWidget:LocalInit() self.widget = self:GetEntity().myWidget -- We listen to the onSaveDataReady event here, allowing us to only initialize the frontend -- when the data is available self:GetEntity().saveDataScript.properties.onSaveDataReady:Listen(self, "UpdateWidget") end function MyWidget:UpdateWidget() self.widget.js.data.money = self:GetEntity().saveDataScript:GetData("money") end
Example
This is an example pulled out of one of my script. It’s an interactable object that a user can interact with after some amount of time. I’ve smooshed it together for readability reasons.
Consider this method:
function TriggerPaymentScript:TimeLeft(user) -- Fetch the time since last update off the user's save data local lastTime = user.saveDataScript:GetData("trigger-last-used") local cooldownSeconds = self.properties.cooldownTime * 60 local timePassed = GetWorld():GetUTCTime() - lastTime return cooldownSeconds - timePassed end
This method interacts with the user’s save data. The cool thing about the User Save Data package is we can actually use this on the client and the server.
So this code runs on the server when a user interacts with an entity in the world.
-- User interacts with something, it runs this function TriggerPaymentScript:PerformActivity(player) local user = player:GetUser() local diff = self:TimeLeft(user) if diff < 0 then -- Do Stuff else -- Don't do stuff end end
Now, the important part - this entity also has a world widget.
function TriggerPaymentScript:UpdateTimer(user) -- This is important, we're running this function on the client if IsServer() then self:SendToAllClients("UpdateTimer", user) return end if user ~- GetWorld():GetLocalUser() then return end -- We're using the _same_ function we used in PerformActivity to get the time left, -- even though save data is only accessible on the server, User Save Data lets us access it self.widget.js.data.timeLeft = self:TimeLeft(user) end
So we get to share the same code to calculate the time left on both the server and the client, and get to keep all of the related logic all nicely encapsulated on the concerned world entity, rather than mixing and matching between the world and the user