Roblox Studio · Lesson 25

Save the record: First introduction to DataStore

First use the offline practice mode to understand reading and saving, and then learn to use pcall and UpdateAsync to protect the highest record.

10–15 years old 90 minutes DataStore / pcall Hands-on practice Testing and Saving

Course overview

Let the highest record remain, and learn to take it seriously.

The previous gold coins and tasks only live in the current server. DataStore allows data to still exist after leaving it, but the network request will fail, so "click to save" does not mean "saved successfully".

This course is divided into two rounds. The first cycle defaults to PRACTICE=true, using the memory practice of the current server only; the second cycle is for you and your parents or teachers to prepare an independent test experience before switching to the real DataStore. Practice mode does not cross Stop saving.

01 · BEFORE

Before starting

Understand servers, Attributes, ProximityPrompt and pcall. It is recommended to study in two sessions, completing the practice mode first.

02 · MAKE

Your work

Press a scoring station to increase 10 points, press a save station to save the highest record; Output clearly displays practice or cloud results.

03 · CHECK

Complete flag

The highest record will not be reduced; overwriting is prohibited when the read fails; "save successfully" cannot be output when the read fails.

Official screenshot of Creator Hub’s Data Stores Manager, not the successful save certificate of this lesson
Figure 25-1: Official Data Stores Manager interface reference: It is used to view cloud data, which does not mean that this lesson has been saved successfully. Practice mode only simulates saves within the current server.Roblox Creator Documentation · CC BY 4.0 · The original image has not been modified · Description of the permit

Learning route

Prepare first, then do it; after seeing the result, remember to save it.

Every time a small step is completed, it is tested. Say what you expect to see first, and then use Play and Output to check it. Don't rush to write all the code at once.

1

01 · Do the saving exercise without internet connection first

Place two small Parts in the new Baseplate: ScoreStation and SaveStation, and add ProximityPrompt respectively. One gives points for the round and one tries to save.

2

02·Why merge with the highest value

Each player uses UserId to form a data key, which is more stable than player nicknames. When saving, use UpdateAsync to read the current value and return a larger record to prevent the old server from writing higher scores lower.

3

03 · Let every read and write have results

The code is a little longer, you can read the setup first, and then read the two Prompt callbacks. Don't memorize it all at once; first find the two rules of "save after successful loading" and "the highest record value is the larger value".

4

04· Prepare the test environment before switching to the cloud

The real DataStore can only be accessed by the server. Please use a stand-alone testing experience with a parent or teacher to enable the required API access as per your current Studio settings; do not connect exercises to live game data.

5

05·Success, failure and unsavedness must be clearly stated

Resetting after Stop in practice mode is expected behavior and cannot be used as proof of successful cross-session saves. Cloud mode must read the same or higher record after re-entering to complete this verification.

Step by step

01 · Do the saving exercise without internet connection first

Place two small Parts in the new Baseplate: ScoreStation and SaveStation, and add ProximityPrompt respectively. One gives points for the round and one tries to save.

This is not a full-line player data system. It only demonstrates that you save the " highest record only up and down " manually, and does not exit automatic saving, currency transactions or complex session locks.

  1. The two stations have Size=6,2,6, Position are -8,1,-10 and 8,1,-10 respectively, Anchored=true.
  2. The ActionText of the two Prompts are "Practice Score" and "Save Record" respectively, and MaxActivationDistance=10.
  3. Create ServerScriptService > BestScore, keep PRACTICE=true.

Step by step

02·Why merge with the highest value

Each player uses UserId to form a data key, which is more stable than player nicknames. When saving, use UpdateAsync to read the current value and return a larger record to prevent the old server from writing higher scores lower.

The callback of UpdateAsync may be retried, so it only calculates the return value, does not play sound effects, does not issue rewards, and cannot task.wait. When the read fails, 0 is not regarded as the real old data and continues to be overwritten.

  1. Remember, RunScore is the current score, BestScore is the highest known record.
  2. The following readValue will reject data that does not meet expectations and will not quietly clear bad data.

Step by step

03 · Let every read and write have results

The code is a little longer, you can read the setup first, and then read the two Prompt callbacks. Don't memorize it all at once; first find the two rules of "save after successful loading" and "the highest record value is the larger value".

Both sites have server distance checks and cooling. The practice mode follows the same process as the cloud mode, except that the underlying storage is different and the Output prefix will clearly distinguish it.

  1. Paste the complete code, wait for loaded output after Play.
  2. Press the scoring station three times, and then press the save station, you should get the highest record of 30 points.
  3. Press again immediately after saving, and it should be stopped by the ten-second cooldown.
ServerScriptService > BestScore · Manually save the highest record prototypeLuau
local Players = game:GetService("Players")
local PRACTICE = true -- 先保持 true;内存练习不跨 Stop 保存
local store = nil
if not PRACTICE then
    store = game:GetService("DataStoreService"):GetDataStore("Lesson25_Best_v1")
end
local memory = {}
local ready, saving, lastSave, lastScore = {}, {}, {}, {}
local scoreStation = workspace:WaitForChild("ScoreStation")
local saveStation = workspace:WaitForChild("SaveStation")
local mode = PRACTICE and "[PRACTICE]" or "[DATASTORE]"

local function readValue(value)
    if value == nil then return 0 end
    assert(typeof(value) == "number" and value == value, "Invalid stored data")
    assert(value >= 0 and value < math.huge and value % 1 == 0, "Invalid score")
    return value
end
local function near(player, station)
    local character = player.Character
    local root = character and character:FindFirstChild("HumanoidRootPart")
    local hum = character and character:FindFirstChildOfClass("Humanoid")
    return root and hum and hum.Health > 0 and (root.Position - station.Position).Magnitude <= 12
end
local function setup(player)
    player:SetAttribute("RunScore", 0)
    local key = "u_" .. player.UserId
    local ok, value = pcall(function()
        if PRACTICE then return readValue(memory[key]) end
        return readValue(store:GetAsync(key))
    end)
    if player.Parent ~= Players then return end
    if not ok then warn(mode, "Load failed; saving disabled", value); return end
    ready[player] = true
    player:SetAttribute("BestScore", value)
    print(mode, player.Name, "loaded", value)
end
Players.PlayerAdded:Connect(setup)
for _, player in ipairs(Players:GetPlayers()) do task.spawn(setup, player) end

scoreStation.ProximityPrompt.Triggered:Connect(function(player)
    if not ready[player] or not near(player, scoreStation) then return end
    local now = os.clock()
    if now - (lastScore[player] or -math.huge) < 0.5 then return end
    lastScore[player] = now
    local score = math.min(9990, (player:GetAttribute("RunScore") or 0) + 10)
    player:SetAttribute("RunScore", score)
    print(mode, "RunScore", score)
end)

saveStation.ProximityPrompt.Triggered:Connect(function(player)
    if not ready[player] or saving[player] or not near(player, saveStation) then return end
    local now = os.clock()
    if now - (lastSave[player] or -math.huge) < 10 then return end
    lastSave[player] = now
    saving[player] = true
    local key = "u_" .. player.UserId
    local score = player:GetAttribute("RunScore") or 0
    local ok, result = pcall(function()
        if PRACTICE then
            memory[key] = math.max(readValue(memory[key]), score)
            return memory[key]
        end
        return store:UpdateAsync(key, function(old)
            return math.max(readValue(old), score)
        end)
    end)
    saving[player] = nil
    if not ok then warn(mode, "Save failed; try again later", result); return end
    if player.Parent == Players then player:SetAttribute("BestScore", result) end
    print(mode, "Saved best:", result)
end)
Players.PlayerRemoving:Connect(function(player)
    ready[player], lastSave[player], lastScore[player] = nil, nil, nil
end)

Step by step

04· Prepare the test environment before switching to the cloud

The real DataStore can only be accessed by the server. Please use a stand-alone testing experience with a parent or teacher to enable the required API access as per your current Studio settings; do not connect exercises to live game data.

This is permission and cloud data operations, not the threshold for this lesson to be completed. Without a suitable test environment, PRECTICE=true completes process learning, does not use another person’s account number or bypass limits.

  1. Save a local copy first, and then confirm that the goal is to test the experience independently.
  2. Reads the official Data Street documents below and checks the current Studio API access settings.
  3. After the environment is ready, change PRACTICE to false and run again.
  4. Only after seeing [DATASTORE] Saved best can you re-enter the verification of persistent records.

Step by step

05·Success, failure and unsavedness must be clearly stated

Resetting after Stop in practice mode is expected behavior and cannot be used as proof of successful cross-session saves. Cloud mode must read the same or higher record after re-entering to complete this verification.

This top value combination is for record, and it is not suitable to increase or reduce gold coins. Writing all currencies as math.max will allow the money spent to come back; do not use an example to different data rules.

  1. Practice mode: Confirm the output with PRACTICE, which will not be retained after Stop.
  2. Cloud mode: save high scores first, and then re-enter verification; saving low scores cannot reduce the highest record.
  3. Loading or saving error: keep the error message, pause the next step, and cannot show success.
  4. Manually save before exit and wait for success; this lesson does not automatically exit saving.
This lesson uses Output to observe simulated reading and writing or actual request results
Figure 25-2: Official Output interface reference: distinguish PRACTICE simulation feedback, actual request success and error. Only when the re-read is truly successful can the cloud archive be verified.Roblox Creator Documentation · CC BY 4.0 · The original image has not been modified · Description of the permit

Homework

It's your turn to change and make your own version.

1 Drawing reading and writing process

Draw four routes of successful loading, failed loading, successful saving, and failed saving.

2 Distinguish two modes

Indicate which one is not saved across Stop and which one must have an independent cloud test environment.

3 Select: Archived State Text

Use Attribute to display "Saving/Saved/Save Failed", but the status is still determined by the server.

Completion criteria:First verify the work in Playtest, then click Stop and save it as "Initials_Lesson25" or a copy of the corresponding Homework; do not overwrite the runnable version of the previous lesson.

Exit questions

Answer it yourself first, and then expand on it.

Why does key use UserId?

It is suitable to correspond to the player stably, and the nickname may change.

Why can't I write 0 directly after reading fails?

Reading failure does not mean that old data do not exist, writing 0 may overwrite real records.

Can rewards be issued through UpdateAsync callback?

It shouldn't, it may retry, it should just calculate and return the new value.

Can the highest record math.max be directly used for gold coin balance?

No, the balance will decrease and different data design is required.

Explore further

This is where we start when we need to check the data.

The text code can be copied directly. First confirm the script type, placement, and whether this code is a complete version or a small experiment; do not run multiple versions of the same script superimposed. The source of the interface reference picture has been marked. The object names in the picture may be different from those in this lesson. Please refer to the operation steps.

Download the code and placement instructions for this lesson