Right to Erasure / Deleting Player Data from Datastores Tutorial

Roblox got rid of it’s right to erasure player data deletion tutorial (thanks Roblox, VERY helpful /sarcasm) so with the power of the Wayback Machine, I figured I’d repost it here for all to use, so finally the confusion of right to erasure requests dies down.

Before I continue, though…

WHAT IS RIGHT TO ERASURE?

Some laws in specific countries (and California in the US) allow a user to be “forgotten” online if they wish to, deleting their profile and, in the case of Roblox, requesting all developers of all games they played to delete their data from their games.
This is the user’s right to be erased from a platform.

THIS IS A LEGAL THING. You have to remove this data if you get a request from Roblox asking you to do so.

Clarifications of things I’ve seen happen:

  • You DO NOT have to delete your whole game.
  • You SHOULD NOT just “remove the datastore” because this does not qualify as removing a player’s data, their data is still in DatastoreService.
  • You SHOULD NOT remove ALL player data, if your game relies on its datastores for saving progress, etc.

Anyway, onto the (copypasted) tutorial. Some minor edits have been made for clarity.

Removing Personal Information

If you’re asked by Roblox to delete personal information about an individual who has exercised their right under GDPR or CCPA, you may need to delete specific data from your experience’s data stores.
A common pattern for identifying Roblox users in a data store is by their unique UserId prefixed by Player_, for instance Player_12345678.
To create a console command script which deletes player data, follow the steps below.

1. (In Studio) Open your experience’s starting place.

2. Inside ServerStorage, create a BindableEvent and rename it RemovePlayerData.
Data-Removal-BindableEvent

3. Inside ServerScriptService, create a new Script and rename it ConsoleEvent.
Data-Removal-ConsoleEvent

4. Paste the following code into the new script. Note that RemoveAsync() is the required method for removing a key from the data store.

local ServerStorage = game:GetService("ServerStorage")
local DataStoreService = game:GetService("DataStoreService")
local removePlayerDataEvent = ServerStorage:WaitForChild("RemovePlayerData")

-- Reference to player data store (replace "PlayerData" with the name of your data store)

local playerData = DataStoreService:GetDataStore("TimeData")

local function onRemovePlayerDataEvent(userID)
-- Pattern for data store player key, for instance "Player_12345678"
local dataStoreKey = "Player_" .. userID
local success, err = pcall(function()
return playerData:RemoveAsync(dataStoreKey)
end)
if success then
warn("Removed player data for user ID '" .. userID .. "'")
else
warn(err)
end
end

removePlayerDataEvent.Event:Connect(onRemovePlayerDataEvent)

Remember that this script assumes the existence of a data store named PlayerData with data keys patterned as Player_XXXXXXXX, where XXXXXXXX is the user’s unique Roblox ID. If your experience’s data store is named differently or its data keys formatted differently, you’ll need to adjust lines 6 and 10.

5. Publish the place, then run it in the Roblox client (game Roblox, as in, join your game normally via the Play button) (not within Studio).

6. Once in the experience, open the Developer Console by pressing F9 or typing /console into the chat.

7. In the Log section, click the Server tab.

8. In the console’s command line, enter the following command, where XXXXXXXX is the user’s ID provided to you by Roblox:
game.ServerStorage.RemovePlayerData:Fire("XXXXXXXX")

Assuming a player data key was located with the Player_XXXXXXXX pattern, you’ll see a console message indicating it was successfully removed from the data store:

If you have multiple datastores, you may have to use multiple copies of this script and its BindableEvent to full clear your datastores.

EDIT: REMOVING ONE PLAYER’S DATA FROM ALL DATASTORES AT ONCE

I haven’t tested this as of writing, but @Master3395 made a version of this script to remove a player’s data from all datastores, as long as the player’s stored ID is the same in all datastores and all datastores are located in ServerStorage.

IMPORTANT IF YOU’RE REPLYING:

Don’t ask me (personally) questions about this, I barely know datastores myself and am NOT a good scripter, and there are bound to be people in the replies who know more than I do. Ask them instead.
I’m just reposting this because Roblox removed it for some reason.

38 Likes

Nice tutorial! Is there a way you could make this process automatic or do you have to do it manually all the time?

2 Likes

When you say “datastore” are you referring the player’s individual data, or the DataStore Instance itself?


You can use the webhooks and datastore apis to automate requests. I am not an experienced web developer, so I cannot give you code examples

2 Likes

The datastore itself, as well as removing it from scripts. Doesn’t qualify as deleting player data, which is what right to erasure asks for.

I mean, yeah, probably, but…

1 Like

I’m not 100% sure this will work, but I’ve added functionality so you can use the script to delete data from all datastores or specify a datastore name in the command.

To delete data universally (all data stores):

game.ServerStorage.RemovePlayerData:Fire("4309939")

To delete data from a specific data store:

game.ServerStorage.RemovePlayerData:Fire("4309939", "SpecificDataStoreName")
Code
local ServerStorage = game:GetService("ServerStorage")
local DataStoreService = game:GetService("DataStoreService")
local removePlayerDataEvent = ServerStorage:WaitForChild("RemovePlayerData")

-- Function to delete player data
local function onRemovePlayerDataEvent(userID, dataStoreName)
    local dataStoreKey = "Player_" .. userID -- Key format for identifying player data
    
    if dataStoreName then
        -- Handle specific DataStore deletion
        local success, err = pcall(function()
            local store = DataStoreService:GetDataStore(dataStoreName)
            store:RemoveAsync(dataStoreKey)
        end)
        
        if success then
            print("Successfully removed data for user ID '" .. userID .. "' in data store: " .. dataStoreName)
        else
            warn("Error removing data for user ID '" .. userID .. "' in data store: " .. dataStoreName .. " | Error: " .. tostring(err))
        end
    else
        -- Handle universal DataStore deletion
        local success, dataStoreList = pcall(function()
            return DataStoreService:ListDataStoresAsync()
        end)

        if not success or not dataStoreList then
            warn("Failed to retrieve data stores: " .. (dataStoreList or "Unknown error"))
            return
        end

        while true do
            local dataStoreInfo = dataStoreList:GetCurrentPage()
            
            for _, dataStore in ipairs(dataStoreInfo) do
                local success, err = pcall(function()
                    local store = DataStoreService:GetDataStore(dataStore.Name)
                    store:RemoveAsync(dataStoreKey)
                end)
                
                if success then
                    print("Successfully removed data for user ID '" .. userID .. "' in data store: " .. dataStore.Name)
                else
                    warn("Error removing data for user ID '" .. userID .. "' in data store: " .. dataStore.Name .. " | Error: " .. tostring(err))
                end
            end
            
            if dataStoreList.IsFinished then
                break
            else
                dataStoreList:AdvanceToNextPageAsync()
            end
        end
    end
end

removePlayerDataEvent.Event:Connect(onRemovePlayerDataEvent)
1 Like

Ohhh, this is awesome. I might give this a whirl later. The problem with the code even I normally use is that it’s basically impossible to verify if it worked or not, so that may also be the case here, but I’ll try it out.