1787 lines
83 KiB
Lua
1787 lines
83 KiB
Lua
local ____lualib = require("lualib_bundle")
|
|
local __TS__Class = ____lualib.__TS__Class
|
|
local Map = ____lualib.Map
|
|
local __TS__New = ____lualib.__TS__New
|
|
local __TS__ArrayIsArray = ____lualib.__TS__ArrayIsArray
|
|
local __TS__TypeOf = ____lualib.__TS__TypeOf
|
|
local __TS__ObjectKeys = ____lualib.__TS__ObjectKeys
|
|
local __TS__ArrayJoin = ____lualib.__TS__ArrayJoin
|
|
local __TS__ArrayForEach = ____lualib.__TS__ArrayForEach
|
|
local __TS__ArrayFilter = ____lualib.__TS__ArrayFilter
|
|
local __TS__ArraySort = ____lualib.__TS__ArraySort
|
|
local __TS__ObjectValues = ____lualib.__TS__ObjectValues
|
|
local __TS__ArraySome = ____lualib.__TS__ArraySome
|
|
local __TS__ArrayPushArray = ____lualib.__TS__ArrayPushArray
|
|
local __TS__ArrayMap = ____lualib.__TS__ArrayMap
|
|
local Set = ____lualib.Set
|
|
local __TS__ArrayIndexOf = ____lualib.__TS__ArrayIndexOf
|
|
local ____exports = {}
|
|
local ____server_config = require("server_config")
|
|
local SERVER_CONFIG = ____server_config.SERVER_CONFIG
|
|
local ____api_helper = require("api_helper")
|
|
local setApiHeaders = ____api_helper.setApiHeaders
|
|
local setApiHeadersLong = ____api_helper.setApiHeadersLong
|
|
local ____store_manager = require("store_manager")
|
|
local StoreManager = ____store_manager.StoreManager
|
|
local ____hero_list_table = require("tables.hero_list_table")
|
|
local HERO_LIST_TABLE = ____hero_list_table.HERO_LIST_TABLE
|
|
local function rawPrintFn(____, ...)
|
|
_G:print(...)
|
|
end
|
|
local ENABLE_VERBOSE_MINI_PROFILE_LOGS = false
|
|
local ____print = ENABLE_VERBOSE_MINI_PROFILE_LOGS and rawPrintFn or (function(____, ...) return nil end)
|
|
____exports.MiniProfileServer = __TS__Class()
|
|
local MiniProfileServer = ____exports.MiniProfileServer
|
|
MiniProfileServer.name = "MiniProfileServer"
|
|
MiniProfileServer.____file_path = "scripts/vscripts/mini_profile_server.lua"
|
|
function MiniProfileServer.prototype.____constructor(self)
|
|
self.serverUrl = SERVER_CONFIG.API_URL
|
|
self.grantedProfileRewardsInSession = __TS__New(Map)
|
|
self.grantedHeroRankRewardsInSession = __TS__New(Map)
|
|
self.profileLevelRewards = {
|
|
{level = 5, freeCurrency = 100},
|
|
{level = 10, freeCurrency = 200},
|
|
{level = 15, freeCurrency = 350},
|
|
{level = 20, freeCurrency = 500},
|
|
{level = 25, freeCurrency = 700},
|
|
{level = 30, freeCurrency = 900},
|
|
{level = 40, freeCurrency = 1400},
|
|
{level = 50, freeCurrency = 2000},
|
|
{level = 75, freeCurrency = 3500},
|
|
{level = 100, freeCurrency = 5000},
|
|
{level = 150, freeCurrency = 5500},
|
|
{level = 200, freeCurrency = 6000},
|
|
{level = 300, freeCurrency = 7000},
|
|
{level = 400, freeCurrency = 8000},
|
|
{level = 500, freeCurrency = 9000},
|
|
{level = 600, freeCurrency = 10000},
|
|
{level = 700, freeCurrency = 11000},
|
|
{level = 800, freeCurrency = 12500},
|
|
{level = 900, freeCurrency = 14000},
|
|
{level = 1000, freeCurrency = 16000}
|
|
}
|
|
self:setupEventListeners()
|
|
end
|
|
function MiniProfileServer.getInstance(self)
|
|
if not ____exports.MiniProfileServer.instance then
|
|
____exports.MiniProfileServer.instance = __TS__New(____exports.MiniProfileServer)
|
|
end
|
|
return ____exports.MiniProfileServer.instance
|
|
end
|
|
function MiniProfileServer.prototype.setupEventListeners(self)
|
|
CustomGameEventManager:RegisterListener(
|
|
"request_player_profile",
|
|
function(source, event)
|
|
local playerId = event.PlayerID
|
|
local playerName = event.player_name
|
|
local requestedSteamId = event.steam_id
|
|
local requestedSteamId64 = event.steam_id_64
|
|
local steamId
|
|
local steamId64
|
|
if requestedSteamId and requestedSteamId ~= "" then
|
|
steamId = tostring(requestedSteamId)
|
|
if requestedSteamId64 and requestedSteamId64 ~= "" and requestedSteamId64 ~= "null" then
|
|
steamId64 = tostring(requestedSteamId64)
|
|
local steamIdStr = tostring(steamId)
|
|
local steamIdPrefix = string.sub(steamIdStr, 1, 7)
|
|
if string.len(steamIdStr) > 10 and steamIdPrefix == "7656119" then
|
|
local base64Str = "76561197960265728"
|
|
local steamId64Str = steamIdStr
|
|
local accountId = self:subtractBigInts(steamId64Str, base64Str)
|
|
steamId = accountId
|
|
____print(nil, (("[MiniProfileServer] Using provided Steam ID64: " .. steamId64) .. ", converted to Account ID: ") .. steamId)
|
|
else
|
|
____print(nil, (("[MiniProfileServer] Using provided Steam ID32: " .. steamId) .. ", Steam ID64: ") .. steamId64)
|
|
end
|
|
else
|
|
local steamIdStr = tostring(steamId)
|
|
local steamIdPrefix = string.sub(steamIdStr, 1, 7)
|
|
if string.len(steamIdStr) > 10 and steamIdPrefix == "7656119" then
|
|
steamId64 = steamIdStr
|
|
local base64Str = "76561197960265728"
|
|
local accountId = self:subtractBigInts(steamIdStr, base64Str)
|
|
steamId = accountId
|
|
____print(nil, (("[MiniProfileServer] Steam ID64 detected: " .. steamId64) .. ", converted to Account ID: ") .. steamId)
|
|
else
|
|
local accountIdNum = tonumber(steamId)
|
|
if accountIdNum then
|
|
local base64Str = "76561197960265728"
|
|
local accountIdStr = tostring(accountIdNum)
|
|
local steamId64Str = self:addBigInts(accountIdStr, base64Str)
|
|
steamId64 = steamId64Str
|
|
____print(nil, (("[MiniProfileServer] Account ID detected: " .. steamId) .. ", converted to Steam ID64: ") .. steamId64)
|
|
else
|
|
if playerId ~= -1 and PlayerResource:GetPlayer(playerId) then
|
|
steamId64 = tostring(PlayerResource:GetSteamID(playerId))
|
|
else
|
|
steamId64 = steamId
|
|
end
|
|
end
|
|
end
|
|
end
|
|
else
|
|
steamId = tostring(PlayerResource:GetSteamAccountID(playerId))
|
|
steamId64 = tostring(PlayerResource:GetSteamID(playerId))
|
|
end
|
|
local requestingPlayer = EntIndexToHScript(source)
|
|
local requestingPlayerId = requestingPlayer ~= nil and requestingPlayer ~= nil and requestingPlayer:GetPlayerID() or playerId
|
|
local requesterSteamId = tostring(PlayerResource:GetSteamAccountID(requestingPlayerId))
|
|
local isOwnProfileRequest = requesterSteamId == steamId
|
|
self:loadPlayerProfileFromServer(
|
|
requestingPlayerId,
|
|
steamId,
|
|
playerName,
|
|
steamId64,
|
|
isOwnProfileRequest
|
|
)
|
|
end
|
|
)
|
|
CustomGameEventManager:RegisterListener(
|
|
"request_match_players",
|
|
function(source, event)
|
|
local requestingPlayer = EntIndexToHScript(source)
|
|
if not requestingPlayer then
|
|
return
|
|
end
|
|
local matchIdRaw = event.match_id or event.game_id or event.id
|
|
local matchId = tonumber(tostring(matchIdRaw)) or 0
|
|
local rowId = tonumber(tostring(event.row_id or 0)) or 0
|
|
local steamId = tostring(event.steam_id or "")
|
|
if matchId <= 0 then
|
|
CustomGameEventManager:Send_ServerToPlayer(requestingPlayer, "match_players_data", {error = "Некорректный ID матча", match_id = 0, players = {}})
|
|
return
|
|
end
|
|
self:loadMatchPlayersFromServer(requestingPlayer, matchId, rowId, steamId)
|
|
end
|
|
)
|
|
CustomGameEventManager:RegisterListener(
|
|
"request_store_currency",
|
|
function(source, event)
|
|
local playerId = event.PlayerID
|
|
if playerId == nil or playerId == nil or not PlayerResource:IsValidPlayerID(playerId) then
|
|
return
|
|
end
|
|
self:syncHeroRankRewardsForPlayer(playerId)
|
|
end
|
|
)
|
|
end
|
|
function MiniProfileServer.prototype.syncHeroRankRewardsForPlayer(self, playerId)
|
|
local steamId = tostring(PlayerResource:GetSteamAccountID(playerId))
|
|
if not steamId or steamId == "0" then
|
|
return
|
|
end
|
|
self:loadHeroAchievementsForProfile(
|
|
steamId,
|
|
playerId,
|
|
true,
|
|
function(____, _heroAchievementsObject, grantedNow)
|
|
local grantedCount = #self:normalizeArrayLike(grantedNow)
|
|
if grantedCount > 0 then
|
|
____print(
|
|
nil,
|
|
(("[MiniProfileServer] Store-trigger reward sync: player=" .. tostring(playerId)) .. ", granted=") .. tostring(grantedCount)
|
|
)
|
|
end
|
|
end
|
|
)
|
|
end
|
|
function MiniProfileServer.prototype.createPlayerProfile(self, playerId, steamId, playerName)
|
|
local request = CreateHTTPRequest("POST", self.serverUrl .. "/player")
|
|
setApiHeadersLong(nil, request)
|
|
request:SetHTTPRequestRawPostBody(
|
|
"application/json",
|
|
json.encode({steam_id = steamId, player_name = playerName})
|
|
)
|
|
request:Send(function(result)
|
|
local player = PlayerResource:GetPlayer(playerId)
|
|
if not player then
|
|
return
|
|
end
|
|
if result.StatusCode >= 200 and result.StatusCode < 300 then
|
|
do
|
|
pcall(function()
|
|
local decoded = {json.decode(result.Body)}
|
|
local responseData = nil
|
|
if __TS__ArrayIsArray(decoded) and #decoded > 0 then
|
|
responseData = decoded[1]
|
|
elseif decoded and type(decoded) == "table" then
|
|
responseData = decoded
|
|
end
|
|
if responseData then
|
|
StoreManager:getInstance():handleLoginPlayerApiResponse(playerId, responseData)
|
|
end
|
|
end)
|
|
end
|
|
self:loadPlayerProfileFromServer(playerId, steamId, playerName)
|
|
else
|
|
CustomGameEventManager:Send_ServerToPlayer(player, "player_profile_data", {error = "Не удалось создать профиль", steam_id = steamId, player_name = playerName})
|
|
end
|
|
end)
|
|
end
|
|
function MiniProfileServer.prototype.loadPlayerProfileFromServer(self, playerId, steamId, playerName, steamId64, shouldGrantLevelRewards)
|
|
if shouldGrantLevelRewards == nil then
|
|
shouldGrantLevelRewards = false
|
|
end
|
|
local steamIdStr = tostring(steamId)
|
|
local steamIdNum = tonumber(steamIdStr)
|
|
if steamIdNum and steamIdNum > 1000000000000 then
|
|
local base64 = 76561197960265730
|
|
local accountId = math.floor(steamIdNum - base64)
|
|
steamIdStr = tostring(accountId)
|
|
____print(nil, (("[MiniProfileServer] Converted 64-bit Steam ID " .. steamId) .. " to 32-bit Account ID ") .. steamIdStr)
|
|
end
|
|
____print(nil, (("[MiniProfileServer] Загрузка профиля: steam_id=" .. steamIdStr) .. ", player=") .. playerName)
|
|
local request = CreateHTTPRequest("GET", (self.serverUrl .. "/player/") .. steamIdStr)
|
|
setApiHeaders(nil, request)
|
|
request:Send(function(result)
|
|
do
|
|
local function ____catch(____error)
|
|
local player = PlayerResource:GetPlayer(playerId)
|
|
if player then
|
|
CustomGameEventManager:Send_ServerToPlayer(player, "player_profile_data", {error = "Ошибка обработки данных", steam_id = steamId, player_name = playerName})
|
|
end
|
|
end
|
|
local ____try, ____hasReturned, ____returnValue = pcall(function()
|
|
local player = PlayerResource:GetPlayer(playerId)
|
|
if not player then
|
|
return true
|
|
end
|
|
if result.StatusCode == 0 then
|
|
CustomGameEventManager:Send_ServerToPlayer(player, "player_profile_data", {error = "Сервер недоступен", steam_id = steamId, player_name = playerName})
|
|
return true
|
|
end
|
|
if result.StatusCode >= 200 and result.StatusCode < 300 then
|
|
do
|
|
local function ____catch(e)
|
|
____print(
|
|
nil,
|
|
"[MiniProfileServer] Ошибка обработки данных профиля: " .. tostring(e)
|
|
)
|
|
CustomGameEventManager:Send_ServerToPlayer(player, "player_profile_data", {error = "Ошибка обработки данных профиля", steam_id = steamId, player_name = playerName})
|
|
end
|
|
local ____try, ____hasReturned, ____returnValue = pcall(function()
|
|
local responseBodyStr = tostring(result.Body or "")
|
|
local responseBodyPreview = string.len(responseBodyStr) > 1000 and string.sub(responseBodyStr, 1, 1000) .. "... [truncated]" or responseBodyStr
|
|
____print(nil, "[MiniProfileServer] Response Body (preview): " .. responseBodyPreview)
|
|
local responseData = self:decodeJsonSafe(result.Body)
|
|
if responseData == nil then
|
|
____print(
|
|
nil,
|
|
"[MiniProfileServer] Ошибка JSON профиля: body_length=" .. tostring(string.len(responseBodyStr))
|
|
)
|
|
CustomGameEventManager:Send_ServerToPlayer(player, "player_profile_data", {error = "Ошибка парсинга JSON", steam_id = steamId, player_name = playerName})
|
|
return true
|
|
end
|
|
local responseDataAny = responseData
|
|
____print(
|
|
nil,
|
|
(("[MiniProfileServer] Decoded response type: " .. __TS__TypeOf(responseData)) .. ", isArray: ") .. tostring(__TS__ArrayIsArray(responseData))
|
|
)
|
|
____print(
|
|
nil,
|
|
"[MiniProfileServer] Decoded response keys: " .. (type(responseData) == "table" and __TS__ArrayJoin(
|
|
__TS__ObjectKeys(responseData),
|
|
", "
|
|
) or "N/A")
|
|
)
|
|
local data = nil
|
|
local responseItems = {}
|
|
local topLevelObjects = {}
|
|
if responseDataAny and type(responseDataAny) == "table" then
|
|
for k in pairs(responseDataAny) do
|
|
local v = responseDataAny[k]
|
|
if v and type(v) == "table" then
|
|
topLevelObjects[#topLevelObjects + 1] = v
|
|
end
|
|
end
|
|
end
|
|
local looksLikeProfilePayload = responseDataAny and type(responseDataAny) == "table" and (responseDataAny.profile ~= nil or responseDataAny.stats ~= nil or responseDataAny.recentGames ~= nil or responseDataAny.recent_games ~= nil or responseDataAny.games ~= nil or responseDataAny.history ~= nil)
|
|
if looksLikeProfilePayload then
|
|
data = responseDataAny
|
|
____print(nil, "[MiniProfileServer] Using profile payload object directly")
|
|
elseif __TS__ArrayIsArray(responseDataAny) then
|
|
local reconstructed = {}
|
|
local reconstructedCount = 0
|
|
__TS__ArrayForEach(
|
|
topLevelObjects,
|
|
function(____, obj)
|
|
local objAny = obj
|
|
local keyNamed = tostring(objAny.key or "")
|
|
local valueNamed = objAny.value
|
|
local keyIndexed = tostring(objAny[1] or objAny["1"] or "")
|
|
local ____temp_0
|
|
if objAny[2] ~= nil then
|
|
____temp_0 = objAny[2]
|
|
else
|
|
____temp_0 = objAny["2"]
|
|
end
|
|
local valueIndexed = ____temp_0
|
|
local finalKey = keyNamed ~= "" and keyNamed or keyIndexed
|
|
local ____temp_1
|
|
if keyNamed ~= "" then
|
|
____temp_1 = valueNamed
|
|
else
|
|
____temp_1 = valueIndexed
|
|
end
|
|
local finalValue = ____temp_1
|
|
if finalKey ~= "" and finalValue ~= nil then
|
|
reconstructed[finalKey] = finalValue
|
|
reconstructedCount = reconstructedCount + 1
|
|
end
|
|
end
|
|
)
|
|
if reconstructedCount > 0 then
|
|
data = reconstructed
|
|
____print(
|
|
nil,
|
|
"[MiniProfileServer] Reconstructed object from key/value pairs, keys=" .. table.concat(
|
|
__TS__ObjectKeys(reconstructed),
|
|
", "
|
|
)
|
|
)
|
|
elseif #topLevelObjects > 0 then
|
|
local sample = topLevelObjects[1]
|
|
local sampleKeys = {}
|
|
for k in pairs(sample) do
|
|
sampleKeys[#sampleKeys + 1] = tostring(k)
|
|
end
|
|
local ____temp_10 = ("[MiniProfileServer] array-like sample keys=" .. table.concat(sampleKeys, ", ")) .. " "
|
|
local ____tostring_5 = tostring
|
|
local ____opt_result_4
|
|
if sample ~= nil then
|
|
____opt_result_4 = sample.key
|
|
end
|
|
local ____tostring_5_result_9 = ____tostring_5(____opt_result_4)
|
|
local ____opt_result_8
|
|
if sample ~= nil then
|
|
____opt_result_8 = sample.value
|
|
end
|
|
local ____temp_27 = ____temp_10 .. ((("key=" .. ____tostring_5_result_9) .. " value_type=") .. __TS__TypeOf(____opt_result_8)) .. " "
|
|
local ____tostring_18 = tostring
|
|
local ____opt_result_13
|
|
if sample ~= nil then
|
|
____opt_result_13 = sample[1]
|
|
end
|
|
local ____opt_result_13_17 = ____opt_result_13
|
|
if not ____opt_result_13_17 then
|
|
local ____opt_result_16
|
|
if sample ~= nil then
|
|
____opt_result_16 = sample["1"]
|
|
end
|
|
____opt_result_13_17 = ____opt_result_16
|
|
end
|
|
local ____tostring_18_result_26 = ____tostring_18(____opt_result_13_17)
|
|
local ____opt_result_21
|
|
if sample ~= nil then
|
|
____opt_result_21 = sample[2]
|
|
end
|
|
local ____opt_result_21_25 = ____opt_result_21
|
|
if ____opt_result_21_25 == nil then
|
|
local ____opt_result_24
|
|
if sample ~= nil then
|
|
____opt_result_24 = sample["2"]
|
|
end
|
|
____opt_result_21_25 = ____opt_result_24
|
|
end
|
|
____print(
|
|
nil,
|
|
____temp_27 .. (("idx1=" .. ____tostring_18_result_26) .. " idx2_type=") .. __TS__TypeOf(____opt_result_21_25)
|
|
)
|
|
end
|
|
if not data then
|
|
for ____, obj in ipairs(topLevelObjects) do
|
|
if obj and type(obj) == "table" and (obj.profile ~= nil or obj.stats ~= nil or obj.recentGames ~= nil or obj.recent_games ~= nil or obj.games ~= nil or obj.history ~= nil) then
|
|
data = obj
|
|
____print(nil, "[MiniProfileServer] Found payload object inside array-like response")
|
|
break
|
|
end
|
|
end
|
|
end
|
|
if not data then
|
|
responseItems = self:normalizeArrayLike(responseDataAny)
|
|
if #responseItems > 0 then
|
|
data = responseItems[1]
|
|
____print(
|
|
nil,
|
|
"[MiniProfileServer] Using array-like response, items=" .. tostring(#responseItems)
|
|
)
|
|
end
|
|
end
|
|
elseif responseDataAny and type(responseDataAny) == "table" and responseDataAny.value ~= nil then
|
|
data = responseDataAny.value
|
|
____print(nil, "[MiniProfileServer] Using responseData.value as data")
|
|
elseif responseDataAny and type(responseDataAny) == "table" then
|
|
data = responseDataAny
|
|
____print(nil, "[MiniProfileServer] Using object directly as data")
|
|
else
|
|
____print(nil, "[MiniProfileServer] Failed to decode response")
|
|
CustomGameEventManager:Send_ServerToPlayer(player, "player_profile_data", {error = "Ошибка декодирования данных", steam_id = steamId, player_name = playerName})
|
|
return true
|
|
end
|
|
if data then
|
|
local function collectObjectKeys(____, obj)
|
|
local keys = {}
|
|
if not obj or type(obj) ~= "table" then
|
|
return keys
|
|
end
|
|
for key in pairs(obj) do
|
|
keys[#keys + 1] = tostring(key)
|
|
end
|
|
return keys
|
|
end
|
|
if not data or type(data) ~= "table" then
|
|
for ____, item in ipairs(responseItems) do
|
|
if item and type(item) == "table" then
|
|
data = item
|
|
break
|
|
end
|
|
end
|
|
end
|
|
if not data or type(data) ~= "table" then
|
|
____print(nil, "[MiniProfileServer] ❌ Некорректный формат данных профиля (data is not object)")
|
|
CustomGameEventManager:Send_ServerToPlayer(player, "player_profile_data", {error = "Некорректный формат данных профиля", steam_id = steamId, player_name = playerName})
|
|
return true
|
|
end
|
|
____print(
|
|
nil,
|
|
"[MiniProfileServer] Data keys: " .. table.concat(
|
|
collectObjectKeys(nil, data),
|
|
", "
|
|
)
|
|
)
|
|
____print(
|
|
nil,
|
|
"[MiniProfileServer] data.recentGames exists: " .. tostring(data.recentGames ~= nil)
|
|
)
|
|
____print(
|
|
nil,
|
|
(("[MiniProfileServer] data.recentGames type: " .. __TS__TypeOf(data.recentGames)) .. ", isArray: ") .. tostring(__TS__ArrayIsArray(data.recentGames))
|
|
)
|
|
if data.recentGames then
|
|
local ____Array_isArray_result_28
|
|
if __TS__ArrayIsArray(data.recentGames) then
|
|
____Array_isArray_result_28 = data.recentGames.length
|
|
else
|
|
____Array_isArray_result_28 = "N/A"
|
|
end
|
|
____print(
|
|
nil,
|
|
"[MiniProfileServer] data.recentGames length: " .. tostring(____Array_isArray_result_28)
|
|
)
|
|
end
|
|
local profileData = data.profile or data
|
|
if #responseItems > 0 then
|
|
for ____, item in ipairs(responseItems) do
|
|
if item and type(item) == "table" and item.profile and type(item.profile) == "table" then
|
|
profileData = item.profile
|
|
break
|
|
end
|
|
end
|
|
end
|
|
local nameFromDB = profileData.name or playerName
|
|
local ____tostring_32 = tostring
|
|
local ____opt_result_31
|
|
if profileData ~= nil then
|
|
____opt_result_31 = profileData.name
|
|
end
|
|
local ____tostring_32_result_37 = ____tostring_32(____opt_result_31)
|
|
local ____tostring_36 = tostring
|
|
local ____opt_result_35
|
|
if profileData ~= nil then
|
|
____opt_result_35 = profileData.level
|
|
end
|
|
local ____temp_47 = ((("[MiniProfileServer] profileData name=" .. ____tostring_32_result_37) .. " level=") .. ____tostring_36(____opt_result_35)) .. " "
|
|
local ____tostring_41 = tostring
|
|
local ____opt_result_40
|
|
if profileData ~= nil then
|
|
____opt_result_40 = profileData.free_currency
|
|
end
|
|
local ____tostring_41_result_46 = ____tostring_41(____opt_result_40)
|
|
local ____tostring_45 = tostring
|
|
local ____opt_result_44
|
|
if profileData ~= nil then
|
|
____opt_result_44 = profileData.donate_currency
|
|
end
|
|
____print(
|
|
nil,
|
|
____temp_47 .. (("free_currency=" .. ____tostring_41_result_46) .. " donate_currency=") .. ____tostring_45(____opt_result_44)
|
|
)
|
|
local grantedLevelRewardsNow = {}
|
|
if shouldGrantLevelRewards then
|
|
do
|
|
local function ____catch(grantError)
|
|
____print(
|
|
nil,
|
|
"[MiniProfileServer] Ошибка выдачи наград профиля: " .. tostring(grantError)
|
|
)
|
|
grantedLevelRewardsNow = {}
|
|
end
|
|
local ____try, ____hasReturned = pcall(function()
|
|
local profileLevel = tonumber(profileData.level) or 1
|
|
grantedLevelRewardsNow = self:grantPendingProfileLevelRewards(playerId, profileLevel)
|
|
end)
|
|
if not ____try then
|
|
____catch(____hasReturned)
|
|
end
|
|
end
|
|
end
|
|
local grantedLevelRewardsObject = {}
|
|
__TS__ArrayForEach(
|
|
grantedLevelRewardsNow,
|
|
function(____, reward, index)
|
|
grantedLevelRewardsObject[tostring(index)] = reward
|
|
end
|
|
)
|
|
local recentGames = data.recentGames or data.recent_games or data.games or data.history or data.match_history or data.stats and (data.stats.recentGames or data.stats.recent_games or data.stats.games or data.stats.history) or data.profile and (data.profile.recentGames or data.profile.recent_games or data.profile.games or data.profile.history or data.profile.match_history)
|
|
local function collectObjectValues(____, obj)
|
|
local out = {}
|
|
if not obj or type(obj) ~= "table" then
|
|
return out
|
|
end
|
|
for key in pairs(obj) do
|
|
out[#out + 1] = obj[key]
|
|
end
|
|
return out
|
|
end
|
|
local isRecentGamesEmptyObject = recentGames and type(recentGames) == "table" and not __TS__ArrayIsArray(recentGames) and #collectObjectKeys(nil, recentGames) == 0
|
|
if (not recentGames or isRecentGamesEmptyObject) and #responseItems > 0 then
|
|
for ____, item in ipairs(responseItems) do
|
|
do
|
|
if not item or type(item) ~= "table" then
|
|
goto __continue93
|
|
end
|
|
local candidate = item.recentGames or item.recent_games or item.games or item.history or item.match_history or item.stats and (item.stats.recentGames or item.stats.recent_games or item.stats.games or item.stats.history) or item.profile and (item.profile.recentGames or item.profile.recent_games or item.profile.games or item.profile.history or item.profile.match_history)
|
|
if not candidate then
|
|
goto __continue93
|
|
end
|
|
if __TS__ArrayIsArray(candidate) then
|
|
if #collectObjectValues(nil, candidate) > 0 then
|
|
recentGames = candidate
|
|
break
|
|
end
|
|
elseif type(candidate) == "table" and #collectObjectKeys(nil, candidate) > 0 then
|
|
recentGames = candidate
|
|
break
|
|
end
|
|
end
|
|
::__continue93::
|
|
end
|
|
end
|
|
local extractGamesArray
|
|
extractGamesArray = function(____, source)
|
|
if not source then
|
|
return {}
|
|
end
|
|
if type(source) == "string" then
|
|
do
|
|
local function ____catch(e)
|
|
return true, {}
|
|
end
|
|
local ____try, ____hasReturned, ____returnValue = pcall(function()
|
|
local decoded = {json.decode(source)}
|
|
return true, extractGamesArray(nil, decoded)
|
|
end)
|
|
if not ____try then
|
|
____hasReturned, ____returnValue = ____catch(____hasReturned)
|
|
end
|
|
if ____hasReturned then
|
|
return ____returnValue
|
|
end
|
|
end
|
|
end
|
|
if __TS__ArrayIsArray(source) then
|
|
local values = collectObjectValues(nil, source)
|
|
if #values > 0 then
|
|
return __TS__ArrayFilter(
|
|
values,
|
|
function(____, item) return item and type(item) == "table" end
|
|
)
|
|
end
|
|
return {}
|
|
end
|
|
if type(source) == "table" then
|
|
local directValues = __TS__ArrayFilter(
|
|
collectObjectValues(nil, source),
|
|
function(____, item) return item and type(item) == "table" end
|
|
)
|
|
if #directValues > 0 then
|
|
return directValues
|
|
end
|
|
local nested = source.items or source.data or source.rows or source.matches or source.history or source.recentGames or source.recent_games
|
|
if nested then
|
|
return extractGamesArray(nil, nested)
|
|
end
|
|
end
|
|
return {}
|
|
end
|
|
local gamesExtracted = extractGamesArray(nil, recentGames)
|
|
do
|
|
local function ____catch(sortError)
|
|
____print(
|
|
nil,
|
|
"[MiniProfileServer] Не удалось отсортировать recentGames: " .. tostring(sortError)
|
|
)
|
|
end
|
|
local ____try, ____hasReturned = pcall(function()
|
|
if type(Date) == "function" then
|
|
__TS__ArraySort(
|
|
gamesExtracted,
|
|
function(____, a, b)
|
|
local dateA = __TS__New(Date, a.game_start or a.gameStart or a.created_at or 0):getTime()
|
|
local dateB = __TS__New(Date, b.game_start or b.gameStart or b.created_at or 0):getTime()
|
|
return dateB - dateA
|
|
end
|
|
)
|
|
end
|
|
end)
|
|
if not ____try then
|
|
____catch(____hasReturned)
|
|
end
|
|
end
|
|
recentGames = gamesExtracted
|
|
____print(
|
|
nil,
|
|
"[MiniProfileServer] Extracted games: " .. tostring(#gamesExtracted)
|
|
)
|
|
____print(
|
|
nil,
|
|
(("[MiniProfileServer] Profile loaded for " .. tostring(nameFromDB)) .. ", recentGames count: ") .. tostring(__TS__ArrayIsArray(recentGames) and #recentGames or "not an array")
|
|
)
|
|
____print(
|
|
nil,
|
|
(("[MiniProfileServer] recentGames type: " .. __TS__TypeOf(recentGames)) .. ", isArray: ") .. tostring(__TS__ArrayIsArray(recentGames))
|
|
)
|
|
if not recentGames or type(recentGames) == "table" and #collectObjectKeys(nil, recentGames) == 0 then
|
|
____print(
|
|
nil,
|
|
(("[MiniProfileServer] recentGames пустой. Data keys=" .. table.concat(
|
|
collectObjectKeys(nil, data),
|
|
", "
|
|
)) .. ", profile keys=") .. (profileData and type(profileData) == "table" and table.concat(
|
|
collectObjectKeys(nil, profileData),
|
|
", "
|
|
) or "N/A")
|
|
)
|
|
end
|
|
local gamesToSend = {}
|
|
if __TS__ArrayIsArray(recentGames) then
|
|
gamesToSend = self:normalizeArrayLike(recentGames)
|
|
elseif recentGames and type(recentGames) == "table" then
|
|
gamesToSend = __TS__ArrayFilter(
|
|
collectObjectValues(nil, recentGames),
|
|
function(____, item) return item and type(item) == "table" end
|
|
)
|
|
end
|
|
if #gamesToSend == 0 then
|
|
____print(nil, "[MiniProfileServer] recentGames пустой в /player. Пробуем fallback /player/:id/history")
|
|
self:loadRecentGamesFallback(
|
|
player,
|
|
steamIdStr,
|
|
profileData,
|
|
data.stats or data,
|
|
nameFromDB,
|
|
steamId64 or steamId,
|
|
grantedLevelRewardsObject
|
|
)
|
|
return true
|
|
end
|
|
self:loadHeroAchievementsForProfile(
|
|
steamIdStr,
|
|
playerId,
|
|
shouldGrantLevelRewards,
|
|
function(____, heroAchievementsObject, grantedHeroRankRewardsObject)
|
|
self:sendProfileToClient(
|
|
player,
|
|
profileData,
|
|
data.stats or data,
|
|
gamesToSend,
|
|
steamId,
|
|
nameFromDB,
|
|
steamId64 or steamId,
|
|
grantedLevelRewardsObject,
|
|
heroAchievementsObject,
|
|
grantedHeroRankRewardsObject
|
|
)
|
|
end
|
|
)
|
|
end
|
|
end)
|
|
if not ____try then
|
|
____hasReturned, ____returnValue = ____catch(____hasReturned)
|
|
end
|
|
if ____hasReturned then
|
|
return true, ____returnValue
|
|
end
|
|
end
|
|
elseif result.StatusCode == 404 then
|
|
self:createPlayerProfile(playerId, steamId, playerName)
|
|
else
|
|
CustomGameEventManager:Send_ServerToPlayer(
|
|
player,
|
|
"player_profile_data",
|
|
{
|
|
error = "Ошибка сервера: " .. tostring(result.StatusCode),
|
|
steam_id = steamId,
|
|
player_name = playerName
|
|
}
|
|
)
|
|
end
|
|
end)
|
|
if not ____try then
|
|
____hasReturned, ____returnValue = ____catch(____hasReturned)
|
|
end
|
|
if ____hasReturned then
|
|
return ____returnValue
|
|
end
|
|
end
|
|
end)
|
|
end
|
|
function MiniProfileServer.prototype.normalizeArrayLike(self, input)
|
|
if not input then
|
|
return {}
|
|
end
|
|
local function hasGameFields(____, obj)
|
|
if not obj or type(obj) ~= "table" then
|
|
return false
|
|
end
|
|
return not not (obj.hero or obj.hero_name or obj.heroName or obj.result or obj.match_result or obj.difficulty or obj.difficulty_name or obj.mode)
|
|
end
|
|
local unwrapKnownContainers
|
|
unwrapKnownContainers = function(____, value)
|
|
if not value or type(value) ~= "table" then
|
|
return value
|
|
end
|
|
local containerKeys = {
|
|
"history",
|
|
"recentGames",
|
|
"games",
|
|
"items",
|
|
"rows",
|
|
"data",
|
|
"result",
|
|
"payload"
|
|
}
|
|
for ____, key in ipairs(containerKeys) do
|
|
local nested = value[key]
|
|
if nested and type(nested) == "table" then
|
|
if key == "data" and type(nested) == "table" then
|
|
local deep = unwrapKnownContainers(nil, nested)
|
|
if deep ~= nested or __TS__ArrayIsArray(deep) then
|
|
return deep
|
|
end
|
|
end
|
|
if __TS__ArrayIsArray(nested) then
|
|
return nested
|
|
end
|
|
if hasGameFields(nil, nested) then
|
|
return {nested}
|
|
end
|
|
local nestedValues = __TS__ArrayFilter(
|
|
__TS__ObjectValues(nested),
|
|
function(____, v) return v and type(v) == "table" end
|
|
)
|
|
if #nestedValues > 0 then
|
|
return nested
|
|
end
|
|
end
|
|
end
|
|
return value
|
|
end
|
|
local unwrapped = unwrapKnownContainers(nil, input)
|
|
if unwrapped ~= input then
|
|
return self:normalizeArrayLike(unwrapped)
|
|
end
|
|
if __TS__ArrayIsArray(input) then
|
|
local arr = input
|
|
local len = #arr
|
|
if type(len) == "number" and len > 0 then
|
|
if __TS__ArraySome(
|
|
arr,
|
|
function(____, item) return hasGameFields(nil, item) end
|
|
) then
|
|
return __TS__ArrayFilter(
|
|
arr,
|
|
function(____, item) return item and type(item) == "table" end
|
|
)
|
|
end
|
|
local expanded = {}
|
|
__TS__ArrayForEach(
|
|
arr,
|
|
function(____, item)
|
|
local normalized = self:normalizeArrayLike(item)
|
|
if #normalized > 0 then
|
|
__TS__ArrayPushArray(expanded, normalized)
|
|
end
|
|
end
|
|
)
|
|
if #expanded > 0 then
|
|
return expanded
|
|
end
|
|
return __TS__ArrayFilter(
|
|
arr,
|
|
function(____, item) return item and type(item) == "table" end
|
|
)
|
|
end
|
|
end
|
|
local numericEntries = {}
|
|
local otherValues = {}
|
|
if type(input) == "table" then
|
|
for key in pairs(input) do
|
|
if not input.hasOwnProperty or rawget(input, key) ~= nil then
|
|
local value = input[key]
|
|
local numericKey = tonumber(tostring(key))
|
|
if numericKey ~= nil and numericKey ~= nil then
|
|
numericEntries[#numericEntries + 1] = {idx = numericKey, value = value}
|
|
else
|
|
otherValues[#otherValues + 1] = value
|
|
end
|
|
end
|
|
end
|
|
end
|
|
if #numericEntries > 0 then
|
|
__TS__ArraySort(
|
|
numericEntries,
|
|
function(____, a, b) return a.idx - b.idx end
|
|
)
|
|
return __TS__ArrayFilter(
|
|
__TS__ArrayMap(
|
|
numericEntries,
|
|
function(____, entry) return entry.value end
|
|
),
|
|
function(____, item) return item and type(item) == "table" end
|
|
)
|
|
end
|
|
if hasGameFields(nil, input) then
|
|
return {input}
|
|
end
|
|
local flattened = {}
|
|
__TS__ArrayForEach(
|
|
__TS__ArrayFilter(
|
|
otherValues,
|
|
function(____, item) return item and type(item) == "table" end
|
|
),
|
|
function(____, item)
|
|
local normalized = self:normalizeArrayLike(item)
|
|
if #normalized > 0 then
|
|
__TS__ArrayPushArray(flattened, normalized)
|
|
else
|
|
flattened[#flattened + 1] = item
|
|
end
|
|
end
|
|
)
|
|
return flattened
|
|
end
|
|
function MiniProfileServer.prototype.loadRecentGamesFallback(self, player, steamId, profileData, statsData, playerName, steamId64, grantedLevelRewardsObject)
|
|
if grantedLevelRewardsObject == nil then
|
|
grantedLevelRewardsObject = {}
|
|
end
|
|
local fallbackRequest = CreateHTTPRequest("GET", ((self.serverUrl .. "/player/") .. steamId) .. "/history?limit=10&offset=0")
|
|
setApiHeaders(nil, fallbackRequest)
|
|
fallbackRequest:Send(function(result)
|
|
if result.StatusCode >= 200 and result.StatusCode < 300 then
|
|
do
|
|
local function ____catch(e)
|
|
____print(nil, "[MiniProfileServer] Fallback history parse error")
|
|
end
|
|
local ____try, ____hasReturned, ____returnValue = pcall(function()
|
|
local decoded = {json.decode(result.Body)}
|
|
local games = self:normalizeArrayLike(decoded)
|
|
____print(
|
|
nil,
|
|
("[MiniProfileServer] Fallback history loaded: " .. tostring(#games)) .. " games"
|
|
)
|
|
self:loadHeroAchievementsForProfile(
|
|
steamId,
|
|
player:GetPlayerID(),
|
|
false,
|
|
function(____, heroAchievementsObject, grantedHeroRankRewardsObject)
|
|
self:sendProfileToClient(
|
|
player,
|
|
profileData,
|
|
statsData,
|
|
games,
|
|
steamId,
|
|
playerName,
|
|
steamId64,
|
|
grantedLevelRewardsObject,
|
|
heroAchievementsObject,
|
|
grantedHeroRankRewardsObject
|
|
)
|
|
end
|
|
)
|
|
return true
|
|
end)
|
|
if not ____try then
|
|
____hasReturned, ____returnValue = ____catch(____hasReturned)
|
|
end
|
|
if ____hasReturned then
|
|
return ____returnValue
|
|
end
|
|
end
|
|
else
|
|
____print(
|
|
nil,
|
|
"[MiniProfileServer] Fallback history HTTP " .. tostring(result.StatusCode)
|
|
)
|
|
end
|
|
self:loadHeroAchievementsForProfile(
|
|
steamId,
|
|
player:GetPlayerID(),
|
|
false,
|
|
function(____, heroAchievementsObject, grantedHeroRankRewardsObject)
|
|
self:sendProfileToClient(
|
|
player,
|
|
profileData,
|
|
statsData,
|
|
{},
|
|
steamId,
|
|
playerName,
|
|
steamId64,
|
|
grantedLevelRewardsObject,
|
|
heroAchievementsObject,
|
|
grantedHeroRankRewardsObject
|
|
)
|
|
end
|
|
)
|
|
end)
|
|
end
|
|
function MiniProfileServer.prototype.sendProfileToClient(self, player, profileData, statsData, gamesToSend, steamId, playerName, steamId64, grantedLevelRewardsObject, heroAchievementsObject, grantedHeroRankRewardsObject)
|
|
if grantedLevelRewardsObject == nil then
|
|
grantedLevelRewardsObject = {}
|
|
end
|
|
if heroAchievementsObject == nil then
|
|
heroAchievementsObject = {}
|
|
end
|
|
if grantedHeroRankRewardsObject == nil then
|
|
grantedHeroRankRewardsObject = {}
|
|
end
|
|
____print(
|
|
nil,
|
|
(("[MiniProfileServer] Профиль отправлен: games=" .. tostring(#gamesToSend)) .. ", steam64=") .. steamId64
|
|
)
|
|
local gamesObject = {}
|
|
__TS__ArrayForEach(
|
|
gamesToSend,
|
|
function(____, game, index)
|
|
gamesObject[tostring(index)] = game
|
|
end
|
|
)
|
|
CustomGameEventManager:Send_ServerToPlayer(player, "player_profile_data", {
|
|
profile = profileData,
|
|
stats = statsData,
|
|
recentGames = gamesObject,
|
|
steam_id = steamId,
|
|
player_name = playerName,
|
|
player_steamid = steamId64,
|
|
profile_level_rewards_granted = grantedLevelRewardsObject,
|
|
hero_achievements = heroAchievementsObject,
|
|
hero_rank_rewards_granted = grantedHeroRankRewardsObject
|
|
})
|
|
end
|
|
function MiniProfileServer.prototype.loadHeroAchievementsForProfile(self, steamId, playerId, shouldGrantRankRewards, callback)
|
|
local allHeroes = self:getAllAvailableHeroes(playerId)
|
|
local request = CreateHTTPRequest("GET", ((self.serverUrl .. "/player/") .. steamId) .. "/history?limit=5000&offset=0")
|
|
setApiHeaders(nil, request)
|
|
request:Send(function(result)
|
|
____print(
|
|
nil,
|
|
(("[MiniProfileServer] /history status=" .. tostring(result.StatusCode)) .. " steam_id=") .. steamId
|
|
)
|
|
if result.StatusCode < 200 or result.StatusCode >= 300 then
|
|
callback(
|
|
nil,
|
|
self:buildHeroAchievementsObject(allHeroes, {}),
|
|
{}
|
|
)
|
|
return
|
|
end
|
|
do
|
|
local function ____catch(e)
|
|
callback(
|
|
nil,
|
|
self:buildHeroAchievementsObject(allHeroes, {}),
|
|
{}
|
|
)
|
|
end
|
|
local ____try, ____hasReturned = pcall(function()
|
|
local decoded = self:decodeJsonSafe(result.Body)
|
|
local games = self:normalizeArrayLike(decoded)
|
|
____print(
|
|
nil,
|
|
"[MiniProfileServer] history entries=" .. tostring(#games)
|
|
)
|
|
do
|
|
local i = 0
|
|
while i < math.min(5, #games) do
|
|
local g = games[i + 1]
|
|
local ____i_60 = i
|
|
local ____tostring_59 = tostring
|
|
local ____opt_result_50
|
|
if g ~= nil then
|
|
____opt_result_50 = g.hero
|
|
end
|
|
local ____opt_result_50_54 = ____opt_result_50
|
|
if not ____opt_result_50_54 then
|
|
local ____opt_result_53
|
|
if g ~= nil then
|
|
____opt_result_53 = g.hero_name
|
|
end
|
|
____opt_result_50_54 = ____opt_result_53
|
|
end
|
|
local ____opt_result_50_54_58 = ____opt_result_50_54
|
|
if not ____opt_result_50_54_58 then
|
|
local ____opt_result_57
|
|
if g ~= nil then
|
|
____opt_result_57 = g.heroName
|
|
end
|
|
____opt_result_50_54_58 = ____opt_result_57
|
|
end
|
|
local ____temp_69 = (("[MiniProfileServer] raw[" .. tostring(____i_60)) .. "] hero=") .. ____tostring_59(____opt_result_50_54_58 or "")
|
|
local ____tostring_68 = tostring
|
|
local ____opt_result_63
|
|
if g ~= nil then
|
|
____opt_result_63 = g.result
|
|
end
|
|
local ____opt_result_63_67 = ____opt_result_63
|
|
if not ____opt_result_63_67 then
|
|
local ____opt_result_66
|
|
if g ~= nil then
|
|
____opt_result_66 = g.match_result
|
|
end
|
|
____opt_result_63_67 = ____opt_result_66
|
|
end
|
|
local ____temp_74 = ____temp_69 .. " result=" .. ____tostring_68(____opt_result_63_67 or "")
|
|
local ____tostring_73 = tostring
|
|
local ____opt_result_72
|
|
if g ~= nil then
|
|
____opt_result_72 = g.is_win
|
|
end
|
|
local ____temp_91 = ____temp_74 .. " is_win=" .. ____tostring_73(____opt_result_72)
|
|
local ____tostring_90 = tostring
|
|
local ____opt_result_77
|
|
if g ~= nil then
|
|
____opt_result_77 = g.difficulty
|
|
end
|
|
local ____opt_result_77_81 = ____opt_result_77
|
|
if not ____opt_result_77_81 then
|
|
local ____opt_result_80
|
|
if g ~= nil then
|
|
____opt_result_80 = g.difficulty_name
|
|
end
|
|
____opt_result_77_81 = ____opt_result_80
|
|
end
|
|
local ____opt_result_77_81_85 = ____opt_result_77_81
|
|
if not ____opt_result_77_81_85 then
|
|
local ____opt_result_84
|
|
if g ~= nil then
|
|
____opt_result_84 = g.mode
|
|
end
|
|
____opt_result_77_81_85 = ____opt_result_84
|
|
end
|
|
local ____opt_result_77_81_85_89 = ____opt_result_77_81_85
|
|
if not ____opt_result_77_81_85_89 then
|
|
local ____opt_result_88
|
|
if g ~= nil then
|
|
____opt_result_88 = g.game_mode
|
|
end
|
|
____opt_result_77_81_85_89 = ____opt_result_88
|
|
end
|
|
____print(
|
|
nil,
|
|
____temp_91 .. " difficulty=" .. ____tostring_90(____opt_result_77_81_85_89 or "")
|
|
)
|
|
i = i + 1
|
|
end
|
|
end
|
|
local aggregated = self:aggregateHeroAchievements(games)
|
|
local heroAchievementsObject = self:buildHeroAchievementsObject(allHeroes, aggregated)
|
|
local grantedHeroRankRewardsObject = {}
|
|
if shouldGrantRankRewards then
|
|
grantedHeroRankRewardsObject = self:grantPendingHeroRankRewards(playerId, heroAchievementsObject)
|
|
end
|
|
callback(nil, heroAchievementsObject, grantedHeroRankRewardsObject)
|
|
end)
|
|
if not ____try then
|
|
____catch(____hasReturned)
|
|
end
|
|
end
|
|
end)
|
|
end
|
|
function MiniProfileServer.prototype.getAllAvailableHeroes(self, playerId)
|
|
local heroes = {}
|
|
local storeManager = StoreManager:getInstance()
|
|
local enabledHeroes = self:getEnabledHeroesFromHeroList()
|
|
for heroName in pairs(enabledHeroes) do
|
|
do
|
|
local heroInfo = HERO_LIST_TABLE[heroName]
|
|
local isDonateHero = (heroInfo and heroInfo.isDonate) == true
|
|
if isDonateHero and not storeManager:hasUnlockedHero(
|
|
playerId,
|
|
tostring(heroName),
|
|
heroInfo.storeItemId
|
|
) then
|
|
goto __continue185
|
|
end
|
|
heroes[#heroes + 1] = tostring(heroName)
|
|
end
|
|
::__continue185::
|
|
end
|
|
__TS__ArraySort(
|
|
heroes,
|
|
function(____, a, b) return a < b and -1 or (a > b and 1 or 0) end
|
|
)
|
|
return heroes
|
|
end
|
|
function MiniProfileServer.prototype.getEnabledHeroesFromHeroList(self)
|
|
local enabledHeroes = {}
|
|
local rawKv = LoadKeyValues("scripts/npc/herolist.txt")
|
|
local ____temp_96 = rawKv and rawKv.CustomHeroList
|
|
if ____temp_96 == nil then
|
|
____temp_96 = rawKv
|
|
end
|
|
local heroList = ____temp_96
|
|
if heroList then
|
|
for heroName in pairs(heroList) do
|
|
local count = tonumber(tostring(heroList[heroName])) or 0
|
|
if count > 0 or count == -1 then
|
|
enabledHeroes[tostring(heroName)] = true
|
|
end
|
|
end
|
|
end
|
|
if #__TS__ObjectKeys(enabledHeroes) == 0 then
|
|
for heroName in pairs(HERO_LIST_TABLE) do
|
|
enabledHeroes[tostring(heroName)] = true
|
|
end
|
|
end
|
|
return enabledHeroes
|
|
end
|
|
function MiniProfileServer.prototype.aggregateHeroAchievements(self, games)
|
|
local stats = {}
|
|
local enabledHeroes = self:getEnabledHeroesFromHeroList()
|
|
local function normalizeDifficultyKey(____, rawDifficulty)
|
|
local key = string.lower(tostring(rawDifficulty or "normal"))
|
|
if key == "easy" or key == "difficulty_easy" or key == "лёгкая" or key == "легкая" then
|
|
return "easy"
|
|
end
|
|
if key == "normal" or key == "difficulty_normal" or key == "обычная" or key == "normal_mode" then
|
|
return "normal"
|
|
end
|
|
if key == "hard" or key == "difficulty_hard" or key == "сложная" then
|
|
return "hard"
|
|
end
|
|
if key == "impossible" or key == "difficulty_impossible" or key == "невозможная" or key == "imposible" then
|
|
return "impossible"
|
|
end
|
|
if key == "death_sentence" or key == "difficulty_death_sentence" or key == "смертельный приговор" or key == "deathsentence" then
|
|
return "death_sentence"
|
|
end
|
|
return "normal"
|
|
end
|
|
local function getRankByDifficulty(____, difficulty)
|
|
local key = normalizeDifficultyKey(nil, difficulty)
|
|
if key == "easy" then
|
|
return 1
|
|
end
|
|
if key == "normal" then
|
|
return 3
|
|
end
|
|
if key == "hard" then
|
|
return 6
|
|
end
|
|
if key == "impossible" then
|
|
return 8
|
|
end
|
|
if key == "death_sentence" then
|
|
return 10
|
|
end
|
|
return 0
|
|
end
|
|
local function isWinResult(____, game)
|
|
local ____opt_result_99
|
|
if game ~= nil then
|
|
____opt_result_99 = game.is_win
|
|
end
|
|
local directWin = ____opt_result_99
|
|
if directWin == true or directWin == 1 or tostring(directWin) == "1" then
|
|
return true
|
|
end
|
|
local ____tonumber_103 = tonumber
|
|
local ____opt_result_102
|
|
if game ~= nil then
|
|
____opt_result_102 = game.result
|
|
end
|
|
local numericResult = ____tonumber_103(____opt_result_102)
|
|
if numericResult == 1 then
|
|
return true
|
|
end
|
|
local ____tostring_111 = tostring
|
|
local ____opt_result_106
|
|
if game ~= nil then
|
|
____opt_result_106 = game.result
|
|
end
|
|
local ____opt_result_106_110 = ____opt_result_106
|
|
if not ____opt_result_106_110 then
|
|
local ____opt_result_109
|
|
if game ~= nil then
|
|
____opt_result_109 = game.match_result
|
|
end
|
|
____opt_result_106_110 = ____opt_result_109
|
|
end
|
|
local key = string.lower(____tostring_111(____opt_result_106_110 or ""))
|
|
return key == "win" or key == "victory" or key == "won" or key == "radiant_win" or key == "dire_win" or key == "success"
|
|
end
|
|
local function normalizeHeroName(____, rawHero)
|
|
local base = tostring(rawHero or "")
|
|
if base == "" then
|
|
return ""
|
|
end
|
|
if string.sub(base, 1, 14) == "npc_dota_hero_" then
|
|
return base
|
|
end
|
|
local normalized = "npc_dota_hero_" .. base
|
|
if enabledHeroes[normalized] ~= nil then
|
|
return normalized
|
|
end
|
|
return base
|
|
end
|
|
__TS__ArrayForEach(
|
|
games,
|
|
function(____, game, index)
|
|
if not game or type(game) ~= "table" then
|
|
return
|
|
end
|
|
local hero = normalizeHeroName(nil, game.hero or game.hero_name or game.heroName)
|
|
if hero == "" or string.sub(hero, 1, 14) ~= "npc_dota_hero_" then
|
|
return
|
|
end
|
|
if not (enabledHeroes[hero] ~= nil) then
|
|
return
|
|
end
|
|
if not (stats[hero] ~= nil) then
|
|
stats[hero] = {games = 0, wins = 0, impossibleWins = 0, bestRank = 0}
|
|
end
|
|
local heroStats = stats[hero]
|
|
heroStats.games = heroStats.games + 1
|
|
local difficulty = normalizeDifficultyKey(nil, game.difficulty or game.difficulty_name or game.mode or game.game_mode)
|
|
local win = isWinResult(nil, game)
|
|
if index < 8 then
|
|
local ____temp_125 = (((((("[MiniProfileServer] normalized[" .. tostring(index)) .. "] hero=") .. hero) .. " difficulty=") .. difficulty) .. " win=") .. tostring(win)
|
|
local ____tostring_119 = tostring
|
|
local ____opt_result_114
|
|
if game ~= nil then
|
|
____opt_result_114 = game.result
|
|
end
|
|
local ____opt_result_114_118 = ____opt_result_114
|
|
if not ____opt_result_114_118 then
|
|
local ____opt_result_117
|
|
if game ~= nil then
|
|
____opt_result_117 = game.match_result
|
|
end
|
|
____opt_result_114_118 = ____opt_result_117
|
|
end
|
|
local ____tostring_119_result_124 = ____tostring_119(____opt_result_114_118 or "")
|
|
local ____tostring_123 = tostring
|
|
local ____opt_result_122
|
|
if game ~= nil then
|
|
____opt_result_122 = game.is_win
|
|
end
|
|
____print(
|
|
nil,
|
|
____temp_125 .. ((" raw_result=" .. ____tostring_119_result_124) .. " raw_is_win=") .. ____tostring_123(____opt_result_122)
|
|
)
|
|
end
|
|
if win then
|
|
heroStats.wins = heroStats.wins + 1
|
|
heroStats.bestRank = math.max(
|
|
heroStats.bestRank,
|
|
getRankByDifficulty(nil, difficulty)
|
|
)
|
|
if difficulty == "impossible" then
|
|
heroStats.impossibleWins = heroStats.impossibleWins + 1
|
|
end
|
|
end
|
|
end
|
|
)
|
|
local keys = __TS__ObjectKeys(stats)
|
|
____print(
|
|
nil,
|
|
"[MiniProfileServer] aggregated heroes=" .. tostring(#keys)
|
|
)
|
|
__TS__ArrayForEach(
|
|
keys,
|
|
function(____, heroName, idx)
|
|
if idx >= 8 then
|
|
return
|
|
end
|
|
local s = stats[heroName]
|
|
____print(
|
|
nil,
|
|
((((("[MiniProfileServer] stats[" .. heroName) .. "] games=") .. tostring(s.games)) .. " wins=") .. tostring(s.wins)) .. ((" best_rank=" .. tostring(s.bestRank)) .. " impossible_wins=") .. tostring(s.impossibleWins)
|
|
)
|
|
end
|
|
)
|
|
return stats
|
|
end
|
|
function MiniProfileServer.prototype.buildHeroAchievementsObject(self, allHeroes, aggregated)
|
|
local out = {}
|
|
__TS__ArrayForEach(
|
|
allHeroes,
|
|
function(____, heroName, index)
|
|
local heroStats = aggregated[heroName] or ({games = 0, wins = 0, impossibleWins = 0, bestRank = 0})
|
|
local games = heroStats.games
|
|
local wins = heroStats.wins
|
|
local impossibleWins = heroStats.impossibleWins
|
|
local winRate = games > 0 and math.floor(wins / games * 1000) / 10 or 0
|
|
out[tostring(index)] = {
|
|
hero = heroName,
|
|
games = games,
|
|
wins = wins,
|
|
win_rate = winRate,
|
|
best_rank = heroStats.bestRank,
|
|
impossible_wins = impossibleWins
|
|
}
|
|
end
|
|
)
|
|
return out
|
|
end
|
|
function MiniProfileServer.prototype.getHeroRankRewardAmount(self, tier)
|
|
if tier == "rank1" then
|
|
return 100
|
|
end
|
|
if tier == "rank3" then
|
|
return 200
|
|
end
|
|
if tier == "rank6" then
|
|
return 400
|
|
end
|
|
if tier == "rank8a" then
|
|
return 800
|
|
end
|
|
if tier == "rank8b" then
|
|
return 1200
|
|
end
|
|
if tier == "rank8c" then
|
|
return 2000
|
|
end
|
|
return 0
|
|
end
|
|
function MiniProfileServer.prototype.getHeroAchievementTier(self, bestRank, impossibleWins)
|
|
if bestRank >= 8 then
|
|
if impossibleWins >= 100 then
|
|
return "rank8c"
|
|
end
|
|
if impossibleWins >= 10 then
|
|
return "rank8b"
|
|
end
|
|
return "rank8a"
|
|
end
|
|
if bestRank >= 6 then
|
|
return "rank6"
|
|
end
|
|
if bestRank >= 3 then
|
|
return "rank3"
|
|
end
|
|
if bestRank >= 1 then
|
|
return "rank1"
|
|
end
|
|
return "rank0"
|
|
end
|
|
function MiniProfileServer.prototype.grantPendingHeroRankRewards(self, playerId, heroAchievementsObject)
|
|
local storeManager = StoreManager:getInstance()
|
|
if not self.grantedHeroRankRewardsInSession:has(playerId) then
|
|
self.grantedHeroRankRewardsInSession:set(
|
|
playerId,
|
|
__TS__New(Set)
|
|
)
|
|
end
|
|
local sessionGranted = self.grantedHeroRankRewardsInSession:get(playerId)
|
|
local grantedNow = {}
|
|
local rewardsToCheck = {
|
|
"rank1",
|
|
"rank3",
|
|
"rank6",
|
|
"rank8a",
|
|
"rank8b",
|
|
"rank8c"
|
|
}
|
|
local achievementsArray = self:normalizeArrayLike(heroAchievementsObject)
|
|
local grantedIndex = 0
|
|
__TS__ArrayForEach(
|
|
achievementsArray,
|
|
function(____, entry)
|
|
local hero = tostring(entry.hero or "")
|
|
if hero == "" then
|
|
return
|
|
end
|
|
local bestRank = tonumber(entry.best_rank) or 0
|
|
local impossibleWins = tonumber(entry.impossible_wins) or 0
|
|
local currentTier = self:getHeroAchievementTier(bestRank, impossibleWins)
|
|
local currentTierIndex = __TS__ArrayIndexOf(rewardsToCheck, currentTier)
|
|
if currentTierIndex < 0 then
|
|
return
|
|
end
|
|
do
|
|
local i = 0
|
|
while i <= currentTierIndex do
|
|
do
|
|
local tier = rewardsToCheck[i + 1]
|
|
local amount = self:getHeroRankRewardAmount(tier)
|
|
if amount <= 0 then
|
|
goto __continue248
|
|
end
|
|
local rewardItemId = (("hero_rank_reward_" .. hero) .. "_") .. tier
|
|
if sessionGranted:has(rewardItemId) then
|
|
____print(nil, "[MiniProfileServer] reward skip (session) " .. rewardItemId)
|
|
goto __continue248
|
|
end
|
|
if storeManager:hasPurchasedItem(playerId, rewardItemId) then
|
|
____print(nil, "[MiniProfileServer] reward skip (already purchased) " .. rewardItemId)
|
|
sessionGranted:add(rewardItemId)
|
|
goto __continue248
|
|
end
|
|
local added = storeManager:addFreeCurrency(playerId, amount)
|
|
if not added then
|
|
____print(
|
|
nil,
|
|
(("[MiniProfileServer] reward addFreeCurrency failed " .. rewardItemId) .. " amount=") .. tostring(amount)
|
|
)
|
|
goto __continue248
|
|
end
|
|
storeManager:savePurchaseToServer(playerId, rewardItemId, "hero_rank_reward")
|
|
____print(
|
|
nil,
|
|
(("[MiniProfileServer] reward granted " .. rewardItemId) .. " amount=") .. tostring(amount)
|
|
)
|
|
sessionGranted:add(rewardItemId)
|
|
grantedNow[tostring(grantedIndex)] = {hero = hero, tier = tier, freeCurrency = amount}
|
|
grantedIndex = grantedIndex + 1
|
|
end
|
|
::__continue248::
|
|
i = i + 1
|
|
end
|
|
end
|
|
end
|
|
)
|
|
return grantedNow
|
|
end
|
|
function MiniProfileServer.prototype.loadMatchPlayersFromServer(self, player, matchId, rowId, steamId)
|
|
if rowId == nil then
|
|
rowId = 0
|
|
end
|
|
if steamId == nil then
|
|
steamId = ""
|
|
end
|
|
local idsToTry = {}
|
|
local function addId(____, value)
|
|
if value > 0 and __TS__ArrayIndexOf(idsToTry, value) == -1 then
|
|
idsToTry[#idsToTry + 1] = value
|
|
end
|
|
end
|
|
addId(nil, matchId)
|
|
addId(nil, rowId)
|
|
local endpoints = {}
|
|
__TS__ArrayForEach(
|
|
idsToTry,
|
|
function(____, id)
|
|
endpoints[#endpoints + 1] = ((self.serverUrl .. "/game/") .. tostring(id)) .. "/players"
|
|
end
|
|
)
|
|
if steamId ~= "" then
|
|
__TS__ArrayForEach(
|
|
idsToTry,
|
|
function(____, id)
|
|
endpoints[#endpoints + 1] = ((((self.serverUrl .. "/player/") .. steamId) .. "/history?match_id=") .. tostring(id)) .. "&limit=10&offset=0"
|
|
endpoints[#endpoints + 1] = ((((self.serverUrl .. "/player/") .. steamId) .. "/history?game_id=") .. tostring(id)) .. "&limit=10&offset=0"
|
|
endpoints[#endpoints + 1] = ((((self.serverUrl .. "/player/") .. steamId) .. "/history?id=") .. tostring(id)) .. "&limit=10&offset=0"
|
|
end
|
|
)
|
|
end
|
|
local function collectObjectValues(____, obj)
|
|
local out = {}
|
|
if not obj or type(obj) ~= "table" then
|
|
return out
|
|
end
|
|
for key in pairs(obj) do
|
|
out[#out + 1] = obj[key]
|
|
end
|
|
return out
|
|
end
|
|
local extractPlayers
|
|
extractPlayers = function(____, source)
|
|
if not source then
|
|
return {}
|
|
end
|
|
if __TS__ArrayIsArray(source) then
|
|
local normalizedArray = __TS__ArrayFilter(
|
|
self:normalizeArrayLike(source),
|
|
function(____, item) return item and type(item) == "table" end
|
|
)
|
|
local playersFromParty = {}
|
|
__TS__ArrayForEach(
|
|
normalizedArray,
|
|
function(____, item)
|
|
local nestedParty = item.party_players or item.partyPlayers or item.players or item.participants or item.match_players
|
|
if nestedParty then
|
|
local nested = extractPlayers(nil, nestedParty)
|
|
__TS__ArrayForEach(
|
|
nested,
|
|
function(____, p)
|
|
local ____temp_126 = #playersFromParty + 1
|
|
playersFromParty[____temp_126] = p
|
|
return ____temp_126
|
|
end
|
|
)
|
|
end
|
|
end
|
|
)
|
|
if #playersFromParty > 0 then
|
|
return playersFromParty
|
|
end
|
|
return normalizedArray
|
|
end
|
|
if type(source) == "table" then
|
|
local directPlayers = source.players or source.participants or source.match_players or source.party_players or source.partyPlayers or source.items or source.rows or source.data
|
|
if directPlayers then
|
|
return extractPlayers(nil, directPlayers)
|
|
end
|
|
return __TS__ArrayFilter(
|
|
collectObjectValues(nil, source),
|
|
function(____, item) return item and type(item) == "table" end
|
|
)
|
|
end
|
|
return {}
|
|
end
|
|
local tryRequest
|
|
tryRequest = function(____, index)
|
|
if index >= #endpoints then
|
|
CustomGameEventManager:Send_ServerToPlayer(player, "match_players_data", {error = "Не удалось загрузить участников матча", match_id = matchId, players = {}})
|
|
return
|
|
end
|
|
local request = CreateHTTPRequest("GET", endpoints[index + 1])
|
|
setApiHeaders(nil, request)
|
|
request:Send(function(result)
|
|
if result.StatusCode < 200 or result.StatusCode >= 300 then
|
|
____print(
|
|
nil,
|
|
(("[MiniProfileServer] match players endpoint failed: " .. endpoints[index + 1]) .. " status=") .. tostring(result.StatusCode)
|
|
)
|
|
tryRequest(nil, index + 1)
|
|
return
|
|
end
|
|
local decoded = self:decodeJsonSafe(result.Body)
|
|
if decoded == nil then
|
|
____print(nil, "[MiniProfileServer] match players decode failed: " .. endpoints[index + 1])
|
|
tryRequest(nil, index + 1)
|
|
return
|
|
end
|
|
local players = extractPlayers(nil, decoded)
|
|
if #players == 0 and index < #endpoints - 1 then
|
|
____print(nil, ("[MiniProfileServer] Endpoint " .. endpoints[index + 1]) .. " returned 0 players, trying next")
|
|
tryRequest(nil, index + 1)
|
|
return
|
|
end
|
|
local playersObject = {}
|
|
__TS__ArrayForEach(
|
|
players,
|
|
function(____, item, idx)
|
|
playersObject[tostring(idx)] = item
|
|
end
|
|
)
|
|
CustomGameEventManager:Send_ServerToPlayer(player, "match_players_data", {match_id = matchId, players = playersObject})
|
|
end)
|
|
end
|
|
tryRequest(nil, 0)
|
|
end
|
|
function MiniProfileServer.prototype.addBigInts(self, a, b)
|
|
local maxLen = math.max(
|
|
string.len(a),
|
|
string.len(b)
|
|
)
|
|
a = string.rep(
|
|
"0",
|
|
maxLen - string.len(a)
|
|
) .. a
|
|
b = string.rep(
|
|
"0",
|
|
maxLen - string.len(b)
|
|
) .. b
|
|
local result = ""
|
|
local carry = 0
|
|
do
|
|
local i = maxLen
|
|
while i >= 1 do
|
|
local digitA = tonumber(string.sub(a, i, i)) or 0
|
|
local digitB = tonumber(string.sub(b, i, i)) or 0
|
|
local sum = digitA + digitB + carry
|
|
result = tostring(sum % 10) .. result
|
|
carry = math.floor(sum / 10)
|
|
i = i - 1
|
|
end
|
|
end
|
|
if carry > 0 then
|
|
result = tostring(carry) .. result
|
|
end
|
|
return result
|
|
end
|
|
function MiniProfileServer.prototype.subtractBigInts(self, a, b)
|
|
local maxLen = math.max(
|
|
string.len(a),
|
|
string.len(b)
|
|
)
|
|
a = string.rep(
|
|
"0",
|
|
maxLen - string.len(a)
|
|
) .. a
|
|
b = string.rep(
|
|
"0",
|
|
maxLen - string.len(b)
|
|
) .. b
|
|
local result = ""
|
|
local borrow = 0
|
|
do
|
|
local i = maxLen
|
|
while i >= 1 do
|
|
local digitA = tonumber(string.sub(a, i, i)) or 0
|
|
local digitB = tonumber(string.sub(b, i, i)) or 0
|
|
local diff = digitA - digitB - borrow
|
|
if diff < 0 then
|
|
diff = diff + 10
|
|
borrow = 1
|
|
else
|
|
borrow = 0
|
|
end
|
|
result = tostring(diff) .. result
|
|
i = i - 1
|
|
end
|
|
end
|
|
result = (string.gsub(result, "^0+", ""))
|
|
if result == "" then
|
|
result = "0"
|
|
end
|
|
return result
|
|
end
|
|
function MiniProfileServer.prototype.decodeJsonSafe(self, rawBody)
|
|
do
|
|
local function ____catch(e)
|
|
local body = tostring(rawBody)
|
|
local firstObj = {string.find(body, "{", 1, true)}
|
|
local firstArr = {string.find(body, "[", 1, true)}
|
|
local first = nil
|
|
if firstObj ~= nil and firstArr ~= nil then
|
|
first = math.min(firstObj[1], firstArr[1])
|
|
elseif firstObj ~= nil then
|
|
first = firstObj[1]
|
|
elseif firstArr ~= nil then
|
|
first = firstArr[1]
|
|
end
|
|
if first == nil then
|
|
return true, nil
|
|
end
|
|
local lastObj = {string.find(body, "}", -1, true)}
|
|
local lastArr = {string.find(body, "]", -1, true)}
|
|
local last = nil
|
|
if lastObj ~= nil and lastArr ~= nil then
|
|
last = math.max(lastObj[1], lastArr[1])
|
|
elseif lastObj ~= nil then
|
|
last = lastObj[1]
|
|
elseif lastArr ~= nil then
|
|
last = lastArr[1]
|
|
end
|
|
if last == nil or last < first then
|
|
return true, nil
|
|
end
|
|
local sliced = string.sub(body, first, last)
|
|
do
|
|
local function ____catch(e2)
|
|
return true, nil
|
|
end
|
|
local ____try, ____hasReturned, ____returnValue = pcall(function()
|
|
return true, {json.decode(sliced)}
|
|
end)
|
|
if not ____try then
|
|
____hasReturned, ____returnValue = ____catch(____hasReturned)
|
|
end
|
|
if ____hasReturned then
|
|
return true, ____returnValue
|
|
end
|
|
end
|
|
end
|
|
local ____try, ____hasReturned, ____returnValue = pcall(function()
|
|
return true, {json.decode(rawBody)}
|
|
end)
|
|
if not ____try then
|
|
____hasReturned, ____returnValue = ____catch(____hasReturned)
|
|
end
|
|
if ____hasReturned then
|
|
return ____returnValue
|
|
end
|
|
end
|
|
end
|
|
function MiniProfileServer.prototype.grantPendingProfileLevelRewards(self, playerId, profileLevel)
|
|
if profileLevel <= 0 then
|
|
return {}
|
|
end
|
|
local storeManager = StoreManager:getInstance()
|
|
if not self.grantedProfileRewardsInSession:has(playerId) then
|
|
self.grantedProfileRewardsInSession:set(
|
|
playerId,
|
|
__TS__New(Set)
|
|
)
|
|
end
|
|
local sessionGrantedSet = self.grantedProfileRewardsInSession:get(playerId)
|
|
local grantedNow = {}
|
|
__TS__ArrayForEach(
|
|
self.profileLevelRewards,
|
|
function(____, reward)
|
|
if reward.level > profileLevel then
|
|
return
|
|
end
|
|
local rewardItemId = "profile_level_reward_" .. tostring(reward.level)
|
|
if sessionGrantedSet:has(rewardItemId) then
|
|
return
|
|
end
|
|
if storeManager:hasPurchasedItem(playerId, rewardItemId) then
|
|
sessionGrantedSet:add(rewardItemId)
|
|
return
|
|
end
|
|
local added = storeManager:addFreeCurrency(playerId, reward.freeCurrency)
|
|
if not added then
|
|
return
|
|
end
|
|
storeManager:savePurchaseToServer(playerId, rewardItemId, "profile_level_reward")
|
|
sessionGrantedSet:add(rewardItemId)
|
|
grantedNow[#grantedNow + 1] = {level = reward.level, freeCurrency = reward.freeCurrency}
|
|
____print(
|
|
nil,
|
|
(((("[MiniProfileServer] Выдана награда за уровень " .. tostring(reward.level)) .. ": +") .. tostring(reward.freeCurrency)) .. " осколков игроку ") .. tostring(playerId)
|
|
)
|
|
end
|
|
)
|
|
return grantedNow
|
|
end
|
|
if IsServer() then
|
|
(function()
|
|
Timers:CreateTimer(
|
|
1,
|
|
function()
|
|
do
|
|
local function ____catch(____error)
|
|
return true, nil
|
|
end
|
|
local ____try, ____hasReturned, ____returnValue = pcall(function()
|
|
local miniProfileServer = ____exports.MiniProfileServer:getInstance()
|
|
return true, nil
|
|
end)
|
|
if not ____try then
|
|
____hasReturned, ____returnValue = ____catch(____hasReturned)
|
|
end
|
|
if ____hasReturned then
|
|
return ____returnValue
|
|
end
|
|
end
|
|
end
|
|
)
|
|
end)(nil)
|
|
end
|
|
return ____exports
|