-- Moderation at the matchmaker: bans and blocks. -- -- Laravel owns every report, block, ban and appeal. Nakama's only part is to -- ask, once per ticket add, "is this player banned, and who must they never be -- seated with" (POST /s2s/moderation/gate), and to enforce the answer on the -- ticket itself - see stamp_matchmaker_properties in main.lua. -- -- FAILS OPEN. Any transport error, timeout or non-2xx returns nil and the ticket -- goes in unmodified. Queueing is built to survive Laravel being briefly down -- (the tier cache exists for the same reason), and the client refuses to queue -- a banned account on its own before it ever reaches here. Logged loudly, so a -- gate that is silently down does not go unnoticed. local nk = require("nakama") local config = require("config") local util = require("util") local M = {} -- A user id as a matchmaker query term. -- -- Dashes stripped so the indexer keeps it as ONE term rather than five, and a -- letter in front so bleve's query parser can never read an all-digit id as a -- number. Both sides must use this same function or the exclusion never matches. function M.token(user_id) return "u" .. tostring(user_id):gsub("-", "") end -- { banned, until, exclude = { nakama_user_id, ... } }, or nil to mean "do -- nothing" (moderation off, Laravel unreachable, or an unexpected reply). function M.gate(context, user_id) local settings = util.moderation(context) if not settings.enabled then return nil end local ok, status, body = util.laravel_call(settings, "POST", "/s2s/moderation/gate", { nakama_user_id = user_id }, config.MODERATION_TIMEOUT_MS) if not ok or type(body) ~= "table" then nk.logger_warn(("moderation gate unavailable for %s (status %s) - ticket NOT checked") :format(tostring(user_id), tostring(status))) return nil end if type(body.exclude) ~= "table" then body.exclude = {} end return body end return M