feat: 2.3.2
This commit is contained in:
@@ -0,0 +1,249 @@
|
||||
local nk = require("nakama")
|
||||
local utils = require("lua.utils")
|
||||
|
||||
local leaderboard = {}
|
||||
|
||||
function leaderboard.rpc_get_leaderboard_stats(context, payload)
|
||||
local status, records_or_err = pcall(nk.leaderboard_records_list, "global_high_score", nil, 50, nil)
|
||||
|
||||
if not status then
|
||||
nk.logger_error("Failed to get native leaderboard stats: " .. tostring(records_or_err))
|
||||
return nk.json_encode({ leaderboard = {} })
|
||||
end
|
||||
|
||||
local leaderboardData = {}
|
||||
local ownerRecords = records_or_err.records or {}
|
||||
|
||||
for _, record in ipairs(ownerRecords) do
|
||||
local metadata = {}
|
||||
if record.metadata then
|
||||
local s, m = pcall(nk.json_decode, record.metadata)
|
||||
if s then metadata = m end
|
||||
end
|
||||
|
||||
table.insert(leaderboardData, {
|
||||
user_id = record.owner_id,
|
||||
username = record.username,
|
||||
display_name = record.username, -- Native lua leaderboard returns owner_id, username, score, subscore, num_score, max_num_score, metadata, create_time, update_time
|
||||
avatar_url = metadata.avatar_url or "",
|
||||
loadout_character = metadata.loadout_character or "Copper",
|
||||
high_score = record.score or 0,
|
||||
games_played = metadata.games_played or 0,
|
||||
games_won = metadata.games_won or 0
|
||||
})
|
||||
end
|
||||
|
||||
return nk.json_encode({ leaderboard = leaderboardData })
|
||||
end
|
||||
|
||||
function leaderboard.rpc_submit_score(context, payload)
|
||||
if not context.user_id then error("Not authenticated") end
|
||||
|
||||
local request = nk.json_decode(payload or "{}")
|
||||
local score = tonumber(request.score) or 0
|
||||
local account = nk.account_get_id(context.user_id)
|
||||
|
||||
local metadata = {
|
||||
games_played = request.games_played or 0,
|
||||
games_won = request.games_won or 0,
|
||||
avatar_url = request.avatar_url or account.user.avatar_url or "",
|
||||
loadout_character = request.loadout_character or "Copper"
|
||||
}
|
||||
|
||||
local status, err = pcall(nk.leaderboard_record_write,
|
||||
"global_high_score",
|
||||
context.user_id,
|
||||
account.user.username,
|
||||
score,
|
||||
0,
|
||||
metadata
|
||||
)
|
||||
|
||||
if not status then
|
||||
nk.logger_error("Failed to submit score for " .. context.user_id .. ": " .. tostring(err))
|
||||
error("Failed to submit score")
|
||||
end
|
||||
|
||||
nk.logger_info("Score submitted for user " .. context.user_id .. ": " .. score)
|
||||
return nk.json_encode({ success = true })
|
||||
end
|
||||
|
||||
function leaderboard.rpc_sync_leaderboard(context, payload)
|
||||
if not context.user_id then error("Not authenticated") end
|
||||
|
||||
local status, result = pcall(nk.storage_list, nil, "stats", 100, "")
|
||||
if not status then error("Sync failed: " .. tostring(result)) end
|
||||
|
||||
local statsObjects = result.objects or {}
|
||||
local userGroup = {}
|
||||
|
||||
for _, obj in ipairs(statsObjects) do
|
||||
local userId = obj.user_id
|
||||
local value = obj.value
|
||||
|
||||
if not userGroup[userId] then
|
||||
userGroup[userId] = {
|
||||
high_score = value.high_score or 0,
|
||||
games_played = value.games_played or 0,
|
||||
games_won = value.games_won or 0,
|
||||
avatar_url = value.avatar_url or "",
|
||||
loadout_character = value.loadout_character or ""
|
||||
}
|
||||
else
|
||||
userGroup[userId].high_score = math.max(userGroup[userId].high_score, value.high_score or 0)
|
||||
userGroup[userId].games_played = math.max(userGroup[userId].games_played, value.games_played or 0)
|
||||
userGroup[userId].games_won = math.max(userGroup[userId].games_won, value.games_won or 0)
|
||||
end
|
||||
|
||||
if obj.key == "game_stats" or userGroup[userId].avatar_url == "" then
|
||||
if value.avatar_url then userGroup[userId].avatar_url = value.avatar_url end
|
||||
if value.loadout_character then userGroup[userId].loadout_character = value.loadout_character end
|
||||
end
|
||||
end
|
||||
|
||||
local statusProf, profileResult = pcall(nk.storage_list, nil, "profiles", 100, "")
|
||||
if statusProf and profileResult and profileResult.objects then
|
||||
for _, obj in ipairs(profileResult.objects) do
|
||||
if obj.key == "profile" then
|
||||
local userId = obj.user_id
|
||||
local value = obj.value
|
||||
|
||||
if not userGroup[userId] then
|
||||
userGroup[userId] = { high_score = 0, games_played = 0, games_won = 0, avatar_url = "", loadout_character = "" }
|
||||
end
|
||||
|
||||
if value.avatar_url and userGroup[userId].avatar_url == "" then
|
||||
userGroup[userId].avatar_url = value.avatar_url
|
||||
end
|
||||
if value.loadout_character and userGroup[userId].loadout_character == "" then
|
||||
userGroup[userId].loadout_character = value.loadout_character
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local count = 0
|
||||
local debugLogs = {}
|
||||
|
||||
for uid, stats in pairs(userGroup) do
|
||||
local s, err = pcall(function()
|
||||
local account = nk.account_get_id(uid)
|
||||
local avatar = stats.avatar_url
|
||||
if not avatar or avatar == "" then
|
||||
avatar = account.user.avatar_url
|
||||
end
|
||||
if not avatar or avatar == "" then
|
||||
avatar = "res://assets/graphics/character_selection/sc_characters/sc_copper.png"
|
||||
end
|
||||
|
||||
local meta = {
|
||||
games_played = stats.games_played or 0,
|
||||
games_won = stats.games_won or 0,
|
||||
avatar_url = avatar,
|
||||
loadout_character = stats.loadout_character or "Copper"
|
||||
}
|
||||
nk.leaderboard_record_write("global_high_score", uid, account.user.username, stats.high_score, 0, meta)
|
||||
count = count + 1
|
||||
end)
|
||||
if not s then
|
||||
table.insert(debugLogs, "Error user " .. uid .. ": " .. tostring(err))
|
||||
nk.logger_error("Failed to sync record for " .. uid .. ": " .. tostring(err))
|
||||
end
|
||||
end
|
||||
|
||||
nk.logger_info("Synced " .. count .. " records to leaderboard by user " .. context.user_id)
|
||||
return nk.json_encode({ success = true, synced = count, objects_found = #statsObjects, debug = debugLogs })
|
||||
end
|
||||
|
||||
function leaderboard.rpc_reset_stats(context, payload)
|
||||
if not context.user_id then error("Not authenticated") end
|
||||
|
||||
pcall(nk.leaderboard_record_delete, "global_high_score", context.user_id)
|
||||
|
||||
local zeros = { games_played = 0, games_won = 0, high_score = 0, total_kills = 0, total_deaths = 0 }
|
||||
nk.storage_write({{
|
||||
collection = "stats",
|
||||
key = "game_stats",
|
||||
user_id = context.user_id,
|
||||
value = zeros,
|
||||
permission_read = 2,
|
||||
permission_write = 1
|
||||
}})
|
||||
|
||||
return nk.json_encode({ success = true })
|
||||
end
|
||||
|
||||
function leaderboard.rpc_admin_update_stats(context, payload)
|
||||
utils.require_admin(context)
|
||||
|
||||
local request = nk.json_decode(payload)
|
||||
local targetUserId = request.user_id
|
||||
local stats = request.stats
|
||||
|
||||
if not targetUserId or not stats then
|
||||
error("User ID and stats are required")
|
||||
end
|
||||
|
||||
nk.storage_write({{
|
||||
collection = "stats",
|
||||
key = "game_stats",
|
||||
user_id = targetUserId,
|
||||
value = stats,
|
||||
permission_read = 1,
|
||||
permission_write = 0
|
||||
}})
|
||||
|
||||
local account = nk.account_get_id(targetUserId)
|
||||
local score = stats.high_score or 0
|
||||
local metadata = {
|
||||
games_played = stats.games_played or 0,
|
||||
games_won = stats.games_won or 0,
|
||||
avatar_url = account.user.avatar_url or "",
|
||||
loadout_character = stats.loadout_character or "Copper"
|
||||
}
|
||||
|
||||
nk.leaderboard_record_write("global_high_score", targetUserId, account.user.username, score, 0, metadata)
|
||||
|
||||
nk.logger_info("Stats updated for user " .. targetUserId .. " by admin " .. context.user_id)
|
||||
return nk.json_encode({ success = true })
|
||||
end
|
||||
|
||||
function leaderboard.rpc_admin_delete_stats(context, payload)
|
||||
utils.require_admin(context)
|
||||
|
||||
local request = nk.json_decode(payload)
|
||||
local targetUserId = request.user_id
|
||||
|
||||
if not targetUserId then error("User ID is required") end
|
||||
|
||||
nk.storage_delete({
|
||||
{ collection = "stats", key = "stats", user_id = targetUserId },
|
||||
{ collection = "stats", key = "game_stats", user_id = targetUserId }
|
||||
})
|
||||
|
||||
pcall(nk.leaderboard_record_delete, "global_high_score", targetUserId)
|
||||
|
||||
nk.logger_info("Stats deleted for user " .. targetUserId .. " by admin " .. context.user_id)
|
||||
return nk.json_encode({ success = true })
|
||||
end
|
||||
|
||||
function leaderboard.rpc_admin_sync_leaderboard(context, payload)
|
||||
utils.require_admin(context)
|
||||
return leaderboard.rpc_sync_leaderboard(context, payload)
|
||||
end
|
||||
|
||||
nk.register_rpc(leaderboard.rpc_get_leaderboard_stats, "get_leaderboard_stats")
|
||||
nk.register_rpc(leaderboard.rpc_submit_score, "submit_score")
|
||||
nk.register_rpc(leaderboard.rpc_sync_leaderboard, "sync_leaderboard")
|
||||
nk.register_rpc(leaderboard.rpc_reset_stats, "reset_stats")
|
||||
nk.register_rpc(leaderboard.rpc_admin_update_stats, "admin_update_stats")
|
||||
nk.register_rpc(leaderboard.rpc_admin_delete_stats, "admin_delete_stats")
|
||||
nk.register_rpc(leaderboard.rpc_admin_sync_leaderboard, "admin_sync_leaderboard")
|
||||
|
||||
-- Create default native leaderboard
|
||||
-- id: "global_high_score", authoritative: true, sort: "desc", operator: "best", reset: None
|
||||
pcall(nk.leaderboard_create, "global_high_score", true, "desc", "best", nil, {})
|
||||
|
||||
nk.logger_info("LUA TEST: leaderboard module loaded")
|
||||
|
||||
return leaderboard
|
||||
Reference in New Issue
Block a user