feat: take_powerup VFX, rank fix, admin chat management
- Wire take_powerup AnimatedSprite3D on powerup pickup via add_powerup_from_item() - Make take_powerup animation one-shot (loop: false) - Fix rank Position label hidden at game start (visible = false, only shows when score > 0) - Competition ranking for tied scores in main.gd - Lobby Chat admin tab: system prefix, max messages, wipe, purge old, save config - Chat Storage admin tab: list/browse/delete individual channel messages - Backend RPCs: admin_get_chat_config, admin_set_chat_config, admin_purge_old_messages, admin_list_channel_messages, admin_delete_channel_message - Chat config applied on lobby join (max_messages, prefix injection)
This commit is contained in:
@@ -336,6 +336,157 @@ function admin.rpc_admin_get_player_list(context, payload)
|
||||
return nk.json_encode({ players = players })
|
||||
end
|
||||
|
||||
-- =============================================================================
|
||||
-- Lobby Chat Management
|
||||
-- =============================================================================
|
||||
|
||||
function admin.rpc_admin_get_chat_config(context, payload)
|
||||
utils.require_admin(context)
|
||||
local configObjs = nk.storage_read({{
|
||||
collection = "config",
|
||||
key = "lobby_chat",
|
||||
user_id = utils.SYSTEM_USER_ID
|
||||
}})
|
||||
local config = { prefix = "", max_messages = 50, max_age_days = 0 }
|
||||
if configObjs and #configObjs > 0 and configObjs[1].value then
|
||||
local val = configObjs[1].value
|
||||
config.prefix = val.prefix or ""
|
||||
config.max_messages = val.max_messages or 50
|
||||
config.max_age_days = val.max_age_days or 0
|
||||
end
|
||||
return nk.json_encode({ config = config })
|
||||
end
|
||||
|
||||
function admin.rpc_admin_set_chat_config(context, payload)
|
||||
utils.require_admin(context)
|
||||
local request = nk.json_decode(payload or "{}")
|
||||
local config = {
|
||||
prefix = request.prefix or "",
|
||||
max_messages = request.max_messages or 50,
|
||||
max_age_days = request.max_age_days or 0
|
||||
}
|
||||
nk.storage_write({{
|
||||
collection = "config",
|
||||
key = "lobby_chat",
|
||||
user_id = utils.SYSTEM_USER_ID,
|
||||
value = config,
|
||||
permission_read = 2,
|
||||
permission_write = 0
|
||||
}})
|
||||
nk.logger_info("[AdminChat] Chat config updated by " .. context.user_id)
|
||||
return nk.json_encode({ success = true })
|
||||
end
|
||||
|
||||
function admin.rpc_admin_purge_old_messages(context, payload)
|
||||
utils.require_admin(context)
|
||||
local request = nk.json_decode(payload or "{}")
|
||||
local channelId = request.channel_id or ""
|
||||
local maxAgeDays = request.max_age_days or 0
|
||||
|
||||
if channelId == "" then
|
||||
error("channel_id is required")
|
||||
end
|
||||
if maxAgeDays <= 0 then
|
||||
error("max_age_days must be > 0")
|
||||
end
|
||||
|
||||
local cutoff = os.time() - (maxAgeDays * 86400)
|
||||
local deleted = 0
|
||||
local cursor = ""
|
||||
|
||||
repeat
|
||||
local status, result = pcall(nk.channel_messages_list, channelId, 100, false, cursor)
|
||||
if not status then break end
|
||||
|
||||
local messages = result.messages or {}
|
||||
for _, msg in ipairs(messages) do
|
||||
-- Parse create_time to compare against cutoff
|
||||
local msgTime = 0
|
||||
if msg.create_time then
|
||||
-- Try ISO format: "2024-01-15T10:30:00Z"
|
||||
local y, m, d, hh, mm, ss = msg.create_time:match("(%d+)%-(%d+)%-(%d+)T(%d+):(%d+):(%d+)")
|
||||
if y then
|
||||
msgTime = os.time({
|
||||
year = tonumber(y), month = tonumber(m), day = tonumber(d),
|
||||
hour = tonumber(hh), min = tonumber(mm), sec = tonumber(ss)
|
||||
})
|
||||
else
|
||||
-- Try unix timestamp string
|
||||
msgTime = tonumber(msg.create_time) or 0
|
||||
end
|
||||
end
|
||||
|
||||
if msgTime > 0 and msgTime < cutoff then
|
||||
pcall(nk.channel_message_remove, channelId, msg.message_id)
|
||||
deleted = deleted + 1
|
||||
end
|
||||
end
|
||||
|
||||
cursor = result.next_cursor or ""
|
||||
until cursor == ""
|
||||
|
||||
nk.logger_info("[AdminChat] Purged " .. deleted .. " messages older than " .. maxAgeDays .. " days by " .. context.user_id)
|
||||
return nk.json_encode({ success = true, deleted = deleted })
|
||||
end
|
||||
|
||||
function admin.rpc_admin_list_channel_messages(context, payload)
|
||||
utils.require_admin(context)
|
||||
local request = nk.json_decode(payload or "{}")
|
||||
local channelId = request.channel_id or ""
|
||||
local limit = request.limit or 50
|
||||
local cursor = request.cursor or ""
|
||||
local forward = request.forward == nil and true or request.forward
|
||||
|
||||
if channelId == "" then
|
||||
error("channel_id is required")
|
||||
end
|
||||
|
||||
local status, result = pcall(nk.channel_messages_list, channelId, limit, forward, cursor)
|
||||
if not status then
|
||||
error("Failed to list messages: " .. tostring(result))
|
||||
end
|
||||
|
||||
local msgs = {}
|
||||
if result and result.messages then
|
||||
for _, msg in ipairs(result.messages) do
|
||||
table.insert(msgs, {
|
||||
message_id = msg.message_id,
|
||||
sender_id = msg.sender_id,
|
||||
username = msg.username,
|
||||
content = msg.content,
|
||||
create_time = msg.create_time,
|
||||
update_time = msg.update_time,
|
||||
channel_id = msg.channel_id
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
return nk.json_encode({
|
||||
messages = msgs,
|
||||
next_cursor = result.next_cursor or "",
|
||||
cache_cursor = result.cache_cursor or ""
|
||||
})
|
||||
end
|
||||
|
||||
function admin.rpc_admin_delete_channel_message(context, payload)
|
||||
utils.require_admin(context)
|
||||
local request = nk.json_decode(payload or "{}")
|
||||
local channelId = request.channel_id or ""
|
||||
local messageId = request.message_id or ""
|
||||
|
||||
if channelId == "" or messageId == "" then
|
||||
error("channel_id and message_id are required")
|
||||
end
|
||||
|
||||
local status, err = pcall(nk.channel_message_remove, channelId, messageId)
|
||||
if not status then
|
||||
error("Failed to delete message: " .. tostring(err))
|
||||
end
|
||||
|
||||
nk.logger_info("[AdminChat] Deleted message " .. messageId .. " from channel " .. channelId .. " by " .. context.user_id)
|
||||
return nk.json_encode({ success = true })
|
||||
end
|
||||
|
||||
-- Register RPCs
|
||||
nk.register_rpc(admin.rpc_admin_kick_player, "admin_kick_player")
|
||||
nk.register_rpc(admin.rpc_admin_ban_player, "admin_ban_player")
|
||||
@@ -349,6 +500,11 @@ nk.register_rpc(admin.rpc_admin_topup_gold, "admin_topup_gold")
|
||||
nk.register_rpc(admin.rpc_admin_clear_global_chat, "admin_clear_global_chat")
|
||||
nk.register_rpc(admin.rpc_admin_list_users, "admin_list_users")
|
||||
nk.register_rpc(admin.rpc_admin_delete_users, "admin_delete_users")
|
||||
nk.register_rpc(admin.rpc_admin_get_chat_config, "admin_get_chat_config")
|
||||
nk.register_rpc(admin.rpc_admin_set_chat_config, "admin_set_chat_config")
|
||||
nk.register_rpc(admin.rpc_admin_purge_old_messages, "admin_purge_old_messages")
|
||||
nk.register_rpc(admin.rpc_admin_list_channel_messages, "admin_list_channel_messages")
|
||||
nk.register_rpc(admin.rpc_admin_delete_channel_message, "admin_delete_channel_message")
|
||||
|
||||
nk.logger_info("LUA TEST: admin module loaded successfully")
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ local nk = require("nakama")
|
||||
local utils = {}
|
||||
|
||||
utils.ADMIN_ROLES = { ["admin"] = true, ["moderator"] = true, ["owner"] = true }
|
||||
utils.SYSTEM_USER_ID = "00000000-0000-0000-0000-000000000000"
|
||||
|
||||
function utils.is_admin(context)
|
||||
if not context.user_id then return false end
|
||||
|
||||
Reference in New Issue
Block a user