Hello,
I am attempting to project my already existing datastore onto a leaderboard, but I keep getting this error.
10:50:38.643 DataStore API services are not enabled! Leaderboard will not function. - Server - TimePlayedClass:57
The datastore API is on, and I am frankly stuck here. If anyone wants a certain code snippet I will provide that. But here is the leaderboard script.
--[[
TimePlayedClass, Modified for XP @2025
Displays top 10 players by XP from the OrderedDataStore.
This script's responsibility is to DISPLAY data, not to save it.
]]
local PlayersService = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
local RunService = game:GetService("RunService")
local ServerStorage = game:GetService("ServerStorage")
-- Assuming your main DataStoreModule saves XP to the OrderedDataStore this script reads from.
local Config = require(script.Parent.Settings)
local TimePlayedClass = {}
TimePlayedClass.__index = TimePlayedClass
-- CONSTRUCTOR
function TimePlayedClass.new()
local new = {}
setmetatable(new, TimePlayedClass)
-- Load config settings
new._dataStoreName = Config.DATA_STORE
new._dataStoreStatName = Config.NAME_OF_STAT -- This is the key for the OrderedDataStore
new._boardUpdateDelay = Config.LEADERBOARD_UPDATE * 60
-- Unused properties from the original script have been removed for clarity
-- new._useLeaderstats, new._nameLeaderstats, etc.
new._show1stPlaceAvatar = Config.SHOW_1ST_PLACE_AVATAR
if new._show1stPlaceAvatar == nil then new._show1stPlaceAvatar = true end
new._doDebug = Config.DO_DEBUG
-- This should be an OrderedDataStore for leaderboards
new._orderedDataStore = DataStoreService:GetOrderedDataStore(new._dataStoreName)
new._scoreBlock = script.Parent.ScoreBlock
new._updateBoardTimer = script.Parent.UpdateBoardTimer.Timer.TextLabel
new._apiServicesEnabled = false
new._isMainScript = nil -- This logic should be reviewed or simplified
-- Caches for player info to reduce API calls
new._usernameCache = {}
new._thumbnailCache = {}
return new
end
-- INITIALIZATION
function TimePlayedClass:_init()
local success, message = pcall(function()
self._apiServicesEnabled = DataStoreService:IsApiAccessEnabled()
end)
if not success or not self._apiServicesEnabled then
warn("DataStore API services are not enabled! Leaderboard will not function.")
return
end
-- Initial board update
self:_updateBoard()
-- Start the board update loop
coroutine.wrap(function()
while wait(1) do
for i = self._boardUpdateDelay, 1, -1 do
self._updateBoardTimer.Text = "Updating in: " .. i
wait(1)
end
self:_updateBoard()
end
end)()
end
-- Clears all entries from the board UI
function TimePlayedClass:_clearBoard()
for _, v in pairs(script.Parent.Main.Scores:GetChildren()) do
if v:IsA("Frame") then
v:Destroy()
end
end
end
-- Formats the raw XP value for display
function TimePlayedClass:_formatScore(value)
-- Using commas for large numbers improves readability
local formatted = tostring(value)
local k = 1
while true do
formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
if (k==0) then
break
end
end
return formatted
end
-- Fetches top players from the OrderedDataStore and updates the UI
function TimePlayedClass:_updateBoard()
if self._doDebug then print("Attempting to update leaderboard...") end
local success, pages = pcall(function()
-- Get the top 10 players, descending order
return self._orderedDataStore:GetSortedAsync(false, 10)
end)
if not success then
warn("Failed to get data from OrderedDataStore:", pages)
return
end
self:_clearBoard()
local data = pages:GetCurrentPage()
if #data == 0 and self._doDebug then
print("Leaderboard is empty.")
end
for rank, v in ipairs(data) do
local userId = tonumber(string.gsub(v.key, self._dataStoreStatName, ""))
local xp = v.value
local username = self:_getUsernameAsync(userId)
if username then
local newScoreBlock = self._scoreBlock:Clone()
newScoreBlock.Player.Text = username
newScoreBlock.Rank.Text = "#" .. rank
newScoreBlock.Score.Text = self:_formatScore(xp) -- Use new formatter
newScoreBlock.Name = userId
newScoreBlock.Parent = script.Parent.Main.Scores
-- Handle 1st place avatar
if rank == 1 and self._show1stPlaceAvatar then
local thumbnail = self:_getThumbnailAsync(userId)
if thumbnail then
script.Parent.FirstPlace.Avatar.Image = thumbnail
script.Parent.FirstPlace.PlayerName.Text = username
end
end
end
end
if self._doDebug then print("Leaderboard updated successfully.") end
end
-- Helper functions for getting user data with caching
function TimePlayedClass:_getUsernameAsync(userId)
if self._usernameCache[userId] then
return self._usernameCache[userId]
end
local success, result = pcall(function()
return PlayersService:GetNameFromUserIdAsync(userId)
end)
if success then
self._usernameCache[userId] = result
return result
else
warn("Could not get username for UserId:", userId, "| Error:", result)
return nil
end
end
function TimePlayedClass:_getThumbnailAsync(userId)
if self._thumbnailCache[userId] then
return self._thumbnailCache[userId]
end
local success, result = pcall(function()
return PlayersService:GetUserThumbnailAsync(userId, Enum.ThumbnailType.HeadShot, Enum.ThumbnailSize.Size420x420)
end)
if success then
self._thumbnailCache[userId] = result
return result
else
warn("Could not get thumbnail for UserId:", userId, "| Error:", result)
return nil
end
end
-- Main execution
local Leaderboard = TimePlayedClass.new()
Leaderboard:_init()