213 lines
8.9 KiB
Lua
213 lines
8.9 KiB
Lua
local ____lualib = require("lualib_bundle")
|
||
local __TS__ArrayIsArray = ____lualib.__TS__ArrayIsArray
|
||
local __TS__NumberIsFinite = ____lualib.__TS__NumberIsFinite
|
||
local ____exports = {}
|
||
local ____api_helper = require("api_helper")
|
||
local setApiHeaders = ____api_helper.setApiHeaders
|
||
local encodeApiBody = ____api_helper.encodeApiBody
|
||
local ____server_config = require("server_config")
|
||
local SERVER_CONFIG = ____server_config.SERVER_CONFIG
|
||
local ____contracts_registry = require("death_sentence.contracts_registry")
|
||
local normalizeDeathSentenceTitleIndex = ____contracts_registry.normalizeDeathSentenceTitleIndex
|
||
local normalizeDeathSentenceContractDurability = ____contracts_registry.normalizeDeathSentenceContractDurability
|
||
local normalizeDeathSentenceContractDurabilityMax = ____contracts_registry.normalizeDeathSentenceContractDurabilityMax
|
||
local getDeathSentenceComplicationPickCount = ____contracts_registry.getDeathSentenceComplicationPickCount
|
||
local LOG_PREFIX = "[DSContractsAPI]"
|
||
____exports.DEATH_SENTENCE_CONTRACT_ROSTER_CAP = 90
|
||
--- Старые записи (armor/movespeed/full_brutality) приводим к `none` — геймплей только через усложнения-абилки.
|
||
local function normalizeDeathSentenceTraitId(self, _v)
|
||
return "none"
|
||
end
|
||
local function isValidRarity(self, v)
|
||
return v == "common" or v == "rare" or v == "epic" or v == "legendary" or v == "mythic"
|
||
end
|
||
--- Разбор ответа GET / тела сохранённого JSON → массив экземпляров (0…CAP) или null при неверной структуре.
|
||
function ____exports.parseDeathSentenceContractRosterPayload(self, raw)
|
||
if not raw or type(raw) ~= "table" then
|
||
return nil
|
||
end
|
||
local root = raw
|
||
local wrap = root.death_sentence_contracts
|
||
if not wrap or type(wrap) ~= "table" then
|
||
return nil
|
||
end
|
||
local rosterRaw = wrap.roster
|
||
if not __TS__ArrayIsArray(rosterRaw) then
|
||
return nil
|
||
end
|
||
local out = {}
|
||
for ____, row in ipairs(rosterRaw) do
|
||
do
|
||
if not row or type(row) ~= "table" then
|
||
goto __continue8
|
||
end
|
||
local o = row
|
||
local instanceId = o.instanceId
|
||
local serialNum = type(o.serial) == "number" and o.serial or tonumber(o.serial)
|
||
if type(serialNum) ~= "number" or not __TS__NumberIsFinite(serialNum) then
|
||
goto __continue8
|
||
end
|
||
local rarity = o.rarity
|
||
local rmNum = type(o.rewardMultiplier) == "number" and o.rewardMultiplier or tonumber(o.rewardMultiplier)
|
||
if type(rmNum) ~= "number" or not __TS__NumberIsFinite(rmNum) then
|
||
goto __continue8
|
||
end
|
||
local compRaw = o.complicationIds
|
||
if type(instanceId) ~= "string" or #instanceId == 0 then
|
||
goto __continue8
|
||
end
|
||
if not isValidRarity(nil, rarity) then
|
||
goto __continue8
|
||
end
|
||
local complicationIds = {}
|
||
if __TS__ArrayIsArray(compRaw) then
|
||
for ____, c in ipairs(compRaw) do
|
||
if type(c) == "string" and #c > 0 then
|
||
complicationIds[#complicationIds + 1] = c
|
||
end
|
||
end
|
||
end
|
||
local maxComp = getDeathSentenceComplicationPickCount(nil, rarity)
|
||
while #complicationIds > maxComp do
|
||
table.remove(complicationIds)
|
||
end
|
||
local titleIndex = normalizeDeathSentenceTitleIndex(nil, o.titleIndex, instanceId)
|
||
local durability = normalizeDeathSentenceContractDurability(nil, o.durability, instanceId)
|
||
local ____o_durabilityMax_0 = o.durabilityMax
|
||
if ____o_durabilityMax_0 == nil then
|
||
____o_durabilityMax_0 = o.durability_max
|
||
end
|
||
local rawMax = ____o_durabilityMax_0
|
||
local durabilityMax = normalizeDeathSentenceContractDurabilityMax(nil, rawMax, durability, instanceId)
|
||
local inst = {
|
||
instanceId = instanceId,
|
||
serial = serialNum,
|
||
titleIndex = titleIndex,
|
||
rarity = rarity,
|
||
rewardMultiplier = rmNum,
|
||
traitId = normalizeDeathSentenceTraitId(nil, o.traitId),
|
||
complicationIds = complicationIds,
|
||
durability = durability,
|
||
durabilityMax = durabilityMax
|
||
}
|
||
local fav = o.favorite
|
||
if fav == true or fav == 1 then
|
||
inst.favorite = true
|
||
elseif fav == false or fav == 0 then
|
||
inst.favorite = false
|
||
end
|
||
local pin = o.pinned
|
||
if pin == true or pin == 1 then
|
||
inst.pinned = true
|
||
elseif pin == false or pin == 0 then
|
||
inst.pinned = false
|
||
end
|
||
out[#out + 1] = inst
|
||
end
|
||
::__continue8::
|
||
end
|
||
while #out > ____exports.DEATH_SENTENCE_CONTRACT_ROSTER_CAP do
|
||
table.remove(out)
|
||
end
|
||
return out
|
||
end
|
||
local function decodeJsonBody(self, body)
|
||
do
|
||
local function ____catch()
|
||
return true, nil
|
||
end
|
||
local ____try, ____hasReturned, ____returnValue = pcall(function()
|
||
return true, {json.decode(body)}
|
||
end)
|
||
if not ____try then
|
||
____hasReturned, ____returnValue = ____catch()
|
||
end
|
||
if ____hasReturned then
|
||
return ____returnValue
|
||
end
|
||
end
|
||
end
|
||
--- Извлечь объект из ответа API (массив из одного элемента или объект).
|
||
local function unwrapApiJsonObject(self, decoded)
|
||
if __TS__ArrayIsArray(decoded) and #decoded > 0 then
|
||
return decoded[1]
|
||
end
|
||
return decoded
|
||
end
|
||
--- Загрузить ростер с бэка для steam_id. В колбэке roster=null при ошибке / пусто / невалидно.
|
||
function ____exports.loadDeathSentenceContractsFromBackend(self, steamId, done)
|
||
local url = ((SERVER_CONFIG.API_URL .. "/player/") .. steamId) .. "/death_sentence_contracts"
|
||
local request = CreateHTTPRequest("GET", url)
|
||
setApiHeaders(nil, request)
|
||
request:Send(function(result)
|
||
if result.StatusCode < 200 or result.StatusCode >= 300 then
|
||
print((((LOG_PREFIX .. " GET fail steam=") .. steamId) .. " code=") .. tostring(result.StatusCode))
|
||
done(nil, nil)
|
||
return
|
||
end
|
||
local bodyStr = result.Body ~= nil and tostring(result.Body) or ""
|
||
if #bodyStr == 0 then
|
||
done(nil, nil)
|
||
return
|
||
end
|
||
local decoded = decodeJsonBody(nil, bodyStr)
|
||
local obj = unwrapApiJsonObject(nil, decoded)
|
||
local roster = ____exports.parseDeathSentenceContractRosterPayload(nil, obj)
|
||
if roster == nil then
|
||
print((LOG_PREFIX .. " GET parse invalid steam=") .. steamId)
|
||
done(nil, nil)
|
||
return
|
||
end
|
||
print((((LOG_PREFIX .. " GET ok steam=") .. steamId) .. " count=") .. tostring(#roster))
|
||
done(nil, roster)
|
||
end)
|
||
end
|
||
--- Сохранить ростер на бэк для одного steam_id.
|
||
function ____exports.saveDeathSentenceContractsToBackend(self, steamId, roster, done)
|
||
local url = ((SERVER_CONFIG.API_URL .. "/player/") .. steamId) .. "/death_sentence_contracts"
|
||
local request = CreateHTTPRequest("PUT", url)
|
||
setApiHeaders(nil, request)
|
||
local payload = {death_sentence_contracts = {roster = roster}}
|
||
request:SetHTTPRequestRawPostBody(
|
||
"application/json",
|
||
encodeApiBody(nil, payload)
|
||
)
|
||
request:Send(function(result)
|
||
local ok = result.StatusCode >= 200 and result.StatusCode < 300
|
||
if ok then
|
||
print((LOG_PREFIX .. " PUT ok steam=") .. steamId)
|
||
else
|
||
print((((LOG_PREFIX .. " PUT fail steam=") .. steamId) .. " code=") .. tostring(result.StatusCode))
|
||
end
|
||
if done then
|
||
done(nil, ok)
|
||
end
|
||
end)
|
||
end
|
||
--- Сохранить один и тот же ростер всем подключённым игрокам (одинаковая сетка лобби).
|
||
function ____exports.saveDeathSentenceContractsRosterToAllConnectedPlayers(self, roster)
|
||
do
|
||
local i = 0
|
||
while i < DOTA_MAX_PLAYERS do
|
||
do
|
||
local pid = i
|
||
if not PlayerResource:IsValidPlayerID(pid) then
|
||
goto __continue41
|
||
end
|
||
local steamId = PlayerResource:GetSteamAccountID(pid)
|
||
if not steamId or steamId == 0 then
|
||
goto __continue41
|
||
end
|
||
____exports.saveDeathSentenceContractsToBackend(
|
||
nil,
|
||
tostring(steamId),
|
||
roster
|
||
)
|
||
end
|
||
::__continue41::
|
||
i = i + 1
|
||
end
|
||
end
|
||
end
|
||
return ____exports
|