Files
Dota-Zombie-Invasion/scripts/vscripts/contracts/contract_backend_adapter.lua
T
achmad 72b73c4dd6 feat: replace CreateHTTPRequest with CreateHTTPRequestScriptVM
Allows the game client to make HTTP API calls from a listen server
(local lobby) instead of requiring a Steam dedicated server.
CreateHTTPRequestScriptVM has the exact same API signature but works
in both dedicated server and listen server contexts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 17:36:08 +07:00

193 lines
7.5 KiB
Lua

local ____lualib = require("lualib_bundle")
local __TS__ArrayIsArray = ____lualib.__TS__ArrayIsArray
local ____exports = {}
local ____server_config = require("server_config")
local SERVER_CONFIG = ____server_config.SERVER_CONFIG
local ____api_helper = require("api_helper")
local encodeApiBody = ____api_helper.encodeApiBody
local setApiHeaders = ____api_helper.setApiHeaders
local setApiHeadersLong = ____api_helper.setApiHeadersLong
local function API()
return SERVER_CONFIG.API_URL
end
--- MVP: верхняя граница множителя с бэка (п.19 плана).
____exports.CONTRACT_MULTIPLIER_MAX_MVP = 10
function ____exports.requestIdDrop(self, sessionOrMatchKey, steamId, rollIndex)
return (((("drop:" .. sessionOrMatchKey) .. ":") .. steamId) .. ":") .. tostring(rollIndex)
end
function ____exports.requestIdNominate(self, sessionId, steamId, contractInstanceId)
return (((("nominate:" .. sessionId) .. ":") .. steamId) .. ":") .. contractInstanceId
end
function ____exports.requestIdVote(self, sessionId, voterSteamId, contractInstanceId)
return (((("vote:" .. sessionId) .. ":") .. voterSteamId) .. ":") .. contractInstanceId
end
function ____exports.requestIdFinalize(self, sessionId)
return "finalize:" .. sessionId
end
function ____exports.requestIdLinkMatch(self, sessionId, matchId)
return (("link-match:" .. sessionId) .. ":") .. matchId
end
local function decodeJsonBody(self, result)
if result.Body == nil or result.Body == nil or result.Body == "" then
return nil
end
do
local function ____catch()
return true, nil
end
local ____try, ____hasReturned, ____returnValue = pcall(function()
return true, {json.decode(result.Body)}
end)
if not ____try then
____hasReturned, ____returnValue = ____catch()
end
if ____hasReturned then
return ____returnValue
end
end
end
--- П.19: валидный множитель для применения в игре, иначе undefined.
function ____exports.clampContractMultiplierFromBackend(self, raw)
if type(raw) ~= "number" then
return nil
end
local n = raw
if n ~= n then
return nil
end
if n == math.huge or n == -math.huge then
return nil
end
if n < 1 or n > ____exports.CONTRACT_MULTIPLIER_MAX_MVP then
return nil
end
return n
end
local function extractContractsArray(self, data)
if not data then
return {}
end
if __TS__ArrayIsArray(data) then
return data
end
if data.contracts and __TS__ArrayIsArray(data.contracts) then
return data.contracts
end
return {}
end
function ____exports.contractAdapterGetPlayerContracts(self, steamId, callback)
local url = (API(nil) .. "/contracts/player/") .. steamId
local req = CreateHTTPRequestScriptVM("GET", url)
setApiHeaders(nil, req)
req:Send(function(result)
if result.StatusCode >= 200 and result.StatusCode < 300 then
local data = decodeJsonBody(nil, result)
callback(
nil,
extractContractsArray(nil, data),
false
)
return
end
print((("[contract_backend_adapter] GET player contracts HTTP " .. tostring(result.StatusCode)) .. " steam=") .. steamId)
callback(nil, nil, true)
end)
end
function ____exports.contractAdapterSaveDroppedContract(self, steamId, requestId, draft, callback)
local url = API(nil) .. "/contracts/drop"
local req = CreateHTTPRequestScriptVM("POST", url)
setApiHeadersLong(nil, req)
req:SetHTTPRequestRawPostBody(
"application/json",
encodeApiBody(nil, {request_id = requestId, steam_id = steamId, contract = draft})
)
req:Send(function(result)
if result.StatusCode < 200 or result.StatusCode >= 300 then
print((((("[contract_backend_adapter] drop HTTP " .. tostring(result.StatusCode)) .. " steam=") .. steamId) .. " body=") .. tostring(result.Body))
callback(nil, nil, false)
return
end
local data = decodeJsonBody(nil, result)
if data and data.ok == false then
print("[contract_backend_adapter] drop ok=false steam=" .. steamId)
callback(nil, nil, false)
return
end
local c = data and data.contract
if c and c.contract_instance_id then
callback(nil, c, true)
return
end
print("[contract_backend_adapter] drop missing contract in body steam=" .. steamId)
callback(nil, nil, false)
end)
end
function ____exports.contractAdapterNominate(self, sessionId, requestId, steamId, contractInstanceId, callback)
local url = ((API(nil) .. "/contracts/session/") .. sessionId) .. "/nominate"
local req = CreateHTTPRequestScriptVM("POST", url)
setApiHeaders(nil, req)
req:SetHTTPRequestRawPostBody(
"application/json",
encodeApiBody(nil, {request_id = requestId, steam_id = steamId, contract_instance_id = contractInstanceId})
)
req:Send(function(result)
callback(nil, result.StatusCode >= 200 and result.StatusCode < 300)
end)
end
function ____exports.contractAdapterVote(self, sessionId, requestId, voterSteamId, contractInstanceId, callback)
local url = ((API(nil) .. "/contracts/session/") .. sessionId) .. "/vote"
local req = CreateHTTPRequestScriptVM("POST", url)
setApiHeaders(nil, req)
req:SetHTTPRequestRawPostBody(
"application/json",
encodeApiBody(nil, {request_id = requestId, voter_steam_id = voterSteamId, contract_instance_id = contractInstanceId})
)
req:Send(function(result)
callback(nil, result.StatusCode >= 200 and result.StatusCode < 300)
end)
end
function ____exports.contractAdapterFinalizeContractVoting(self, sessionId, requestId, matchId, localWinnerContractInstanceId, candidatesSnapshot, votesSnapshot, callback)
local url = ((API(nil) .. "/contracts/session/") .. sessionId) .. "/finalize"
local req = CreateHTTPRequestScriptVM("POST", url)
setApiHeadersLong(nil, req)
local body = {
request_id = requestId,
session_id = sessionId,
match_id = matchId,
local_winner_contract_instance_id = localWinnerContractInstanceId,
candidates_snapshot = candidatesSnapshot,
votes_snapshot = votesSnapshot
}
req:SetHTTPRequestRawPostBody(
"application/json",
encodeApiBody(nil, body)
)
req:Send(function(result)
if result.StatusCode < 200 or result.StatusCode >= 300 then
print((("[contract_backend_adapter] finalize HTTP " .. tostring(result.StatusCode)) .. " session=") .. sessionId)
callback(nil, nil, false)
return
end
local data = decodeJsonBody(nil, result)
if not data or type(data.ok) ~= "boolean" then
print("[contract_backend_adapter] finalize bad body session=" .. sessionId)
callback(nil, nil, false)
return
end
callback(nil, data, true)
end)
end
function ____exports.contractAdapterLinkSessionToMatch(self, sessionId, requestId, matchId, callback)
local url = ((API(nil) .. "/contracts/session/") .. sessionId) .. "/link-match"
local req = CreateHTTPRequestScriptVM("POST", url)
setApiHeaders(nil, req)
req:SetHTTPRequestRawPostBody(
"application/json",
encodeApiBody(nil, {request_id = requestId, match_id = matchId})
)
req:Send(function(result)
callback(nil, result.StatusCode >= 200 and result.StatusCode < 300)
end)
end
return ____exports