Usage

The purpose of this module is to save and load data with an incredible compression ratio. The basic usage of it can be seen in the following code.

What this code does is:

  • Checks if the Player already has Leaderstats when the PlayerAdded function is called.
  • If they don't have it, create a new BitBuffer.
  • After the BitBuffer is created, check if they have any data saved on the DataStore. If they don't have any, set their data to the default created at the start of the script.
  • Loads their data from the Base64 string that is stored in the DataStore using FromBase64.
  • Create the Leaderstats using the loadFromBitBuffer, which reads the Color3 first, and then the unsigned number second because that's what order they are stored in.
  • Parents the Leaderstats returned by loadFromBitBuffer to the Player.
  • When the player leaves, check if they have Leaderstats. If they do, create a new BitBuffer.
  • Run the saveToBitBuffer function, which calls WriteColor3 first followed by WriteUnsigned.
  • Sets the new data using SetAsync and ToBase64. Once that is complete, we call Destroy on the BitBuffer and set it to nil.

Important

The order of which you read and write does matter. If you don't follow the same order, your data will not be correct.

Error

I do not recommend using the data saving part of this example in your code. Instead, it's suggested to use either DataStore2 or Quenty's DataStore system.

local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")
local DataStoreService = game:GetService("DataStoreService")
local FastBitBuffer = require(ServerStorage.FastBitBuffer)

local USER_DATA = {"FavoriteColor", "Stage"}
local TOTAL_STAGES = 400
local BITS_REQUIRED = FastBitBuffer.BitsRequired(TOTAL_STAGES)
local DEFAULT_DATA do
    local bitBuffer = FastBitBuffer.new()
    bitBuffer:WriteColor3(Color3.new())
    bitBuffer:WriteUnsigned(BITS_REQUIRED, 1)
    DEFAULT_DATA = bitBuffer:ToBase64()

    bitBuffer:Destroy()
    bitBuffer = nil
end

local gameDataStore = DataStoreService:GetDataStore("GameDataStore")

local function saveToBitBuffer(bitBuffer, leaderstats)
    for _, valueName in ipairs(USER_DATA) do
        assert(leaderstats:FindFirstChild(valueName), string.format("Expected ValueObject %s to exist but it doesn't.", valueName))
    end

    bitBuffer:WriteColor3(leaderstats.favoriteColor.Value)
    bitBuffer:WriteUnsigned(BITS_REQUIRED, leaderstats.Stage.Value)
end

local function loadFromBitBuffer(bitBuffer)
    local leaderstats = Instance.new("Folder")
    leaderstats.Name = "Leaderstats"

    local favoriteColor = Instance.new("Color3Value")
    favoriteColor.Name = "FavoriteColor"
    favoriteColor.Value = bitBuffer:ReadColor3()
    favoriteColor.Parent = leaderstats

    local stage = Instance.new("IntValue")
    stage.Name = "Stage"
    stage.Value = bitBuffer:ReadUnsigned(BITS_REQUIRED)
    stage.Parent = leaderstats

    return leaderstats
end

local function playerAdded(player)
    if not player:FindFirstChild("Leaderstats") then
        local bitBuffer = FastBitBuffer.new()
        local success, playerData = pcall(gameDataStore.GetAsync, gameDataStore, player.UserId)
        if success then
            if not playerData then
                playerData = DEFAULT_DATA
                pcall(gameDataStore.SetAsync, gameDataStore, player.UserId, playerData)
            end

            bitBuffer:FromBase64(playerData)

            local leaderstats = loadFromBitBuffer(bitBuffer)
            leaderstats.Parent = player

            bitBuffer:Destroy()
            bitBuffer = nil
        end
    end
end

local function playerRemoving(player)
    local leaderstats = player:FindFirstChild("Leaderstats")
    if leaderstats then
        local bitBuffer = FastBitBuffer.new()
        saveToBitBuffer(bitBuffer, leaderstats)
        local success, error = pcall(gameDataStore.SetAsync, gameDataStore, player.UserId, bitBuffer:ToBase64())
        if not success and error then warn("Failed to save PlayerData!", error) end

        bitBuffer:Destroy()
        bitBuffer = nil
    end
end

Players.PlayerAdded:Connect(playerAdded)
Players.PlayerRemoving:Connect(playerRemoving)
for _, player in ipairs(Players:GetPlayers()) do playerAdded(player) end