add gs
This commit is contained in:
@@ -0,0 +1,845 @@
|
||||
--[[
|
||||
// GameData.lua
|
||||
|
||||
// Fetches data for a game to be used to fill out
|
||||
// the details of that game
|
||||
]]
|
||||
local XboxUpdateBadgeEndpoints = settings():GetFFlag("XboxUpdateBadgeEndpoints")
|
||||
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
local ShellModules = Modules:FindFirstChild("Shell")
|
||||
|
||||
local Http = require(ShellModules:FindFirstChild('Http'))
|
||||
local Utility = require(ShellModules:FindFirstChild('Utility'))
|
||||
|
||||
local ConvertMyPlaceNameInXboxAppFlag = Utility.IsFastFlagEnabled("ConvertMyPlaceNameInXboxApp")
|
||||
local EventHub = require(ShellModules:FindFirstChild('EventHub'))
|
||||
local CreateCacheData = require(ShellModules:FindFirstChild('CachedData'))
|
||||
local GlobalSettings = require(ShellModules:FindFirstChild('GlobalSettings'))
|
||||
local XboxAppState = require(ShellModules:FindFirstChild('AppState'))
|
||||
|
||||
local GameDetail = require(Modules.Shell.Models.GameDetail)
|
||||
local RecommendedGame = require(Modules.Shell.Models.RecommendedGame)
|
||||
local VoteData = require(Modules.Shell.Models.VoteData)
|
||||
|
||||
local ThirdPartyUserService = nil
|
||||
pcall(function()ThirdPartyUserService = game:GetService('ThirdPartyUserService') end)
|
||||
|
||||
local GameData = {}
|
||||
|
||||
local gameCreatorCache = {}
|
||||
function GameData:GetGameCreatorAsync(placeId)
|
||||
if placeId then
|
||||
if not gameCreatorCache[placeId] then
|
||||
local gameDataByPlaceId = self:GetGameDataAsync(placeId)
|
||||
gameCreatorCache[placeId] = gameDataByPlaceId:GetCreatorName()
|
||||
end
|
||||
return gameCreatorCache[placeId]
|
||||
end
|
||||
end
|
||||
|
||||
function GameData:ExtractGeneratedUsername(gameName)
|
||||
local tempUsername = string.match(gameName, "^([0-9a-fA-F]+)'s Place$")
|
||||
if tempUsername and #tempUsername == 32 then
|
||||
return tempUsername
|
||||
end
|
||||
end
|
||||
|
||||
-- Fix places that have been made with incorrect temporary usernames
|
||||
-- creatorName is optional and must be used when querying a game that is
|
||||
-- not the current user's creation
|
||||
function GameData:GetFilteredGameName(gameName, creatorName)
|
||||
if ConvertMyPlaceNameInXboxAppFlag and gameName and type(gameName) == 'string' then
|
||||
local tempUsername = self:ExtractGeneratedUsername(gameName)
|
||||
if tempUsername then
|
||||
local realUsername = creatorName or XboxAppState.store:getState().RobloxUser.robloxName
|
||||
if realUsername then
|
||||
local newGameName = string.gsub(gameName, tempUsername, realUsername, 1)
|
||||
if newGameName then
|
||||
return newGameName
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return gameName
|
||||
end
|
||||
|
||||
function GameData:GetGameDataAsync(placeId)
|
||||
local this = {}
|
||||
|
||||
local result = Http.GetGameDetailsAsync(placeId)
|
||||
if not result then
|
||||
Utility.DebugLog("GameData:GetGameDataAsync() failed to get web response for placeId "..tostring(placeId))
|
||||
result = {}
|
||||
end
|
||||
|
||||
this.Data = result
|
||||
|
||||
--[[ Public API ]]--
|
||||
function this:GetCreatorName()
|
||||
return self.Data["Builder"] or ""
|
||||
end
|
||||
function this:GetDescription()
|
||||
return self.Data["Description"] or ""
|
||||
end
|
||||
function this:GetIsFavoritedByUser()
|
||||
return self.Data["IsFavoritedByUser"] or false
|
||||
end
|
||||
function this:GetLastUpdated()
|
||||
return self.Data["Updated"] or ""
|
||||
end
|
||||
function this:GetCreationDate()
|
||||
return self.Data["Created"] or ""
|
||||
end
|
||||
function this:GetMaxPlayers()
|
||||
return self.Data["MaxPlayers"] or 0
|
||||
end
|
||||
--IsExperimental means not Filtering Enabled
|
||||
function this:GetIsExperimental()
|
||||
return self.Data["IsExperimental"] or false
|
||||
end
|
||||
function this:GetCreatorUserId()
|
||||
return self.Data["BuilderId"]
|
||||
end
|
||||
|
||||
--[[ Async Public API ]]--
|
||||
function this:GetVoteDataAsync()
|
||||
local result = Http.GetGameVotesAsync(placeId)
|
||||
if not result then
|
||||
Utility.DebugLog("GameData:GetVoteDataAsync() failed to get web response for placeId "..tostring(placeId))
|
||||
end
|
||||
|
||||
local voteData = {}
|
||||
local voteTable = result and result["VotingModel"] or nil
|
||||
|
||||
if voteTable then
|
||||
voteData.UpVotes = voteTable["UpVotes"] or 0
|
||||
voteData.DownVotes = voteTable["DownVotes"] or 0
|
||||
voteData.UserVote = voteTable["UserVote"]
|
||||
voteData.CanVote = voteTable["CanVote"] or false
|
||||
voteData.CantVoteReason = voteTable["ReasonForNotVoteable"] or "PlayGame"
|
||||
end
|
||||
|
||||
return voteData
|
||||
end
|
||||
|
||||
function this:GetGameIconIdAsync()
|
||||
local iconId = nil
|
||||
local result = Http.GetGameIconIdAsync(placeId)
|
||||
if result then
|
||||
iconId = result["ImageId"]
|
||||
-- use placeId as backup
|
||||
if not iconId then
|
||||
iconId = placeId
|
||||
end
|
||||
end
|
||||
|
||||
return iconId
|
||||
end
|
||||
|
||||
function this:GetRecommendedGamesAsync()
|
||||
local result = Http.GetRecommendedGamesAsync(placeId)
|
||||
if not result then
|
||||
Utility.DebugLog("GameData:GetRecommendedGamesAsync() failed to get web response for placeId "..tostring(placeId))
|
||||
return {}
|
||||
end
|
||||
|
||||
local recommendedGames = {}
|
||||
for i = 1, #result do
|
||||
local data = result[i]
|
||||
if data then
|
||||
local game = {}
|
||||
-- Temp fix for fixing game names
|
||||
game.Name = GameData:GetFilteredGameName(data["GameName"], data["Creator"] and data["Creator"]["CreatorName"])
|
||||
game.PlaceId = data["PlaceId"]
|
||||
game.IconId = data["ImageId"]
|
||||
table.insert(recommendedGames, game)
|
||||
end
|
||||
end
|
||||
|
||||
return recommendedGames
|
||||
end
|
||||
|
||||
function this:GetThumbnailIdsAsync()
|
||||
local result = Http.GetGameThumbnailsAsync(placeId)
|
||||
if not result then
|
||||
Utility.DebugLog("GameData:GetThumbnailIdsAsync() failed to get web response for placeId "..tostring(placeId))
|
||||
return {}
|
||||
end
|
||||
|
||||
local thumbIds = {}
|
||||
local thumbIdTable = result["thumbnails"]
|
||||
if thumbIdTable then
|
||||
for i = 1, #thumbIdTable do
|
||||
local data = thumbIdTable[i]
|
||||
-- AssetTypeId of 1 is a Image (33 is a video if can ever play videos)
|
||||
if data and data["AssetTypeId"] == 1 then
|
||||
local assetId = data["AssetId"]
|
||||
if assetId then
|
||||
table.insert(thumbIds, assetId)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return thumbIds
|
||||
end
|
||||
|
||||
function this:GetBadgeDataAsync()
|
||||
local result = Http.GetGameBadgeDataAsync(placeId)
|
||||
if not result then
|
||||
Utility.DebugLog("GameData:GetBadgeDataAsync() failed to get web response for placeId "..tostring(placeId))
|
||||
return {}
|
||||
end
|
||||
|
||||
local badgeData = {}
|
||||
local badgeTable = result["GameBadges"]
|
||||
if badgeTable then
|
||||
for i = 1, #badgeTable do
|
||||
local data = badgeTable[i]
|
||||
if data then
|
||||
local badge = {}
|
||||
badge.Name = data["Name"]
|
||||
badge.Description = data["Description"]
|
||||
badge.AssetId = data["BadgeAssetId"]
|
||||
badge.IsOwned = data["IsOwned"]
|
||||
badge.Order = i
|
||||
table.insert(badgeData, badge)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
table.sort(badgeData, function(a, b)
|
||||
if a["IsOwned"] == true and b["IsOwned"] == true then
|
||||
return a.Order < b.Order
|
||||
elseif a["IsOwned"] then
|
||||
return true
|
||||
elseif b["IsOwned"] then
|
||||
return false
|
||||
end
|
||||
return a.Order < b.Order
|
||||
end)
|
||||
|
||||
return badgeData
|
||||
end
|
||||
|
||||
--[[ Post Public API ]]--
|
||||
function this:PostFavoriteAsync()
|
||||
local result = Http.PostFavoriteToggleAsync(placeId)
|
||||
local success = result and result["success"] == true
|
||||
EventHub:dispatchEvent(EventHub.Notifications["FavoriteToggle"], success)
|
||||
if not success then
|
||||
local reason = "Failed"
|
||||
-- the floodcheck message is "Whoa. Slow Down.". So if there is a message,
|
||||
-- let's just say flood check?
|
||||
if result and result["message"] then
|
||||
reason = "FloodCheck"
|
||||
end
|
||||
return success, reason
|
||||
else
|
||||
self.Data["IsFavoritedByUser"] = not self:GetIsFavoritedByUser()
|
||||
end
|
||||
return success
|
||||
end
|
||||
|
||||
function this:PostVoteAsync(status)
|
||||
local result = Http.PostGameVoteAsync(placeId, status)
|
||||
if not result then
|
||||
return nil
|
||||
end
|
||||
|
||||
local success = result["Success"] == true
|
||||
if not success then
|
||||
return success, result["ModalType"]
|
||||
end
|
||||
|
||||
return success
|
||||
end
|
||||
|
||||
-- Temp fix for fixing game names
|
||||
if this.Data then
|
||||
this.Data.Name = GameData:GetFilteredGameName(this.Data.Name, this:GetCreatorName())
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
|
||||
--New GameData Cached
|
||||
local UserChangedCount = 0
|
||||
local maxGameCachedDataCount = 5000
|
||||
local currGameCachedDataCount = 0
|
||||
local gameCachedData = {}
|
||||
|
||||
if ThirdPartyUserService then
|
||||
ThirdPartyUserService.ActiveUserSignedOut:connect(function()
|
||||
GameData:FlushGameData()
|
||||
UserChangedCount = UserChangedCount + 1
|
||||
end)
|
||||
end
|
||||
|
||||
function GameData:UpdateGameData(game)
|
||||
local placeId = game.placeId
|
||||
local name = game.name
|
||||
local creatorName = game.creatorName
|
||||
local creatorId = game.creatorId
|
||||
local iconId = game.iconId
|
||||
local imageToken = game.imageToken
|
||||
local voteData =
|
||||
{
|
||||
UpVotes = game.totalUpVotes,
|
||||
DownVotes = game.totalDownVotes,
|
||||
}
|
||||
if not gameCachedData[placeId] then
|
||||
gameCachedData[placeId] = {}
|
||||
local this = gameCachedData[placeId]
|
||||
this.UpdateDebounce = true
|
||||
this.RelatedGuiObjects = {}
|
||||
this.AccessCount = 1
|
||||
|
||||
local function GetGameDetailsRefreshInterval()
|
||||
return GlobalSettings.GameDetailsRefreshInterval
|
||||
end
|
||||
--Game Details Data
|
||||
local function RefreshGameDetailsAsync(gameData)
|
||||
local startCount = UserChangedCount
|
||||
local valid = false
|
||||
local result = Http.GetGameDetailsAsync(gameData.PlaceId)
|
||||
if not result then
|
||||
Utility.DebugLog("RefreshGameDetailsAsync() failed to get web response for placeId "..tostring(gameData.PlaceId))
|
||||
else
|
||||
local gameDetail = GameDetail.fromJsonData(result)
|
||||
if startCount == UserChangedCount then
|
||||
--for CreatorName and Name, we use the data from initialization unless they were not provided when init
|
||||
gameData.CreatorName = gameData.CreatorName or gameDetail.creatorName or ""
|
||||
gameData.Name = gameData.Name or GameData:GetFilteredGameName(gameDetail.name, gameData.CreatorName)
|
||||
|
||||
gameData.Description = gameDetail.description or ""
|
||||
gameData.IsFavorited = gameDetail.isFavoritedByUser or false
|
||||
gameData.LastUpdated = gameDetail.updated or ""
|
||||
gameData.CreationDate = gameDetail.created or ""
|
||||
gameData.MaxPlayers = gameDetail.maxPlayers or 0
|
||||
gameData.IsExperimental = gameDetail.isExperimental or false
|
||||
gameData.CreatorUserId = gameDetail.builderId
|
||||
gameData.UniverseId = gameDetail.universeId or 0
|
||||
valid = true
|
||||
end
|
||||
end
|
||||
|
||||
return valid
|
||||
end
|
||||
local GameDetailsCachedData = CreateCacheData(this, nil, GetGameDetailsRefreshInterval, RefreshGameDetailsAsync)
|
||||
|
||||
--Vote Data
|
||||
local function RefreshVoteDataAsync(gameData)
|
||||
local startCount = UserChangedCount
|
||||
local valid = false
|
||||
local result = Http.GetGameVotesAsync(gameData.PlaceId)
|
||||
if not result then
|
||||
Utility.DebugLog("RefreshVoteDataAsync() failed to get web response for placeId "..tostring(gameData.PlaceId))
|
||||
else
|
||||
if startCount == UserChangedCount then
|
||||
local voteData = {}
|
||||
local voteTable = result and result["VotingModel"] or nil
|
||||
|
||||
if voteTable then
|
||||
local gameVoteData = VoteData.fromJsonData(voteTable)
|
||||
voteData.UpVotes = gameVoteData.upVotes or 0
|
||||
voteData.DownVotes = gameVoteData.downVotes or 0
|
||||
voteData.UserVote = gameVoteData.userVote
|
||||
voteData.CanVote = gameVoteData.canVote or false
|
||||
voteData.CantVoteReason = gameVoteData.reasonForNotVoteable or "PlayGame"
|
||||
gameData.VoteData = voteData
|
||||
valid = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return valid
|
||||
end
|
||||
local VoteCachedData = CreateCacheData(this, nil, GetGameDetailsRefreshInterval, RefreshVoteDataAsync)
|
||||
|
||||
--Recommended Games
|
||||
local function RefreshRecommendedGamesAsync(gameData)
|
||||
local startCount = UserChangedCount
|
||||
local valid = false
|
||||
local result = Http.GetRecommendedGamesAsync(gameData.PlaceId)
|
||||
if not result then
|
||||
Utility.DebugLog("RefreshRecommendedGamesAsync() failed to get web response for placeId "..tostring(gameData.PlaceId))
|
||||
else
|
||||
if startCount == UserChangedCount then
|
||||
--clear old data
|
||||
if gameData.RecommendedGames then
|
||||
for i = 1, #gameData.RecommendedGames do
|
||||
local recommendedGamePlaceId = gameData.RecommendedGames[i]
|
||||
if recommendedGamePlaceId then
|
||||
local recommendedGameData = GameData:GetGameData(recommendedGamePlaceId)
|
||||
if recommendedGameData then
|
||||
recommendedGameData.AccessCount = recommendedGameData.AccessCount - 1
|
||||
end
|
||||
end
|
||||
gameData.RecommendedGames[i]= nil
|
||||
end
|
||||
end
|
||||
--TODO: use game model
|
||||
local recommendedGames = {}
|
||||
for i = 1, #result do
|
||||
local data = result[i]
|
||||
if data then
|
||||
local placeId;
|
||||
local recommendedGame = RecommendedGame.fromJsonData(data)
|
||||
local creatorName = recommendedGame.creatorName
|
||||
recommendedGame.name = GameData:GetFilteredGameName(recommendedGame.name, creatorName)
|
||||
placeId = recommendedGame.placeId
|
||||
GameData:UpdateGameData(recommendedGame)
|
||||
table.insert(recommendedGames, placeId)
|
||||
end
|
||||
end
|
||||
gameData.RecommendedGames = recommendedGames
|
||||
valid = true
|
||||
end
|
||||
end
|
||||
|
||||
return valid
|
||||
end
|
||||
|
||||
local RecommendedGamesCachedData = CreateCacheData(this, nil, GetGameDetailsRefreshInterval, RefreshRecommendedGamesAsync)
|
||||
|
||||
--ThumbnailIds
|
||||
local function RefreshThumbnailIdsAsync(gameData)
|
||||
local startCount = UserChangedCount
|
||||
local valid = false
|
||||
local result = Http.GetGameThumbnailsAsync(gameData.PlaceId)
|
||||
if not result then
|
||||
Utility.DebugLog("RefreshThumbnailIdsAsync() failed to get web response for placeId "..tostring(gameData.PlaceId))
|
||||
else
|
||||
if startCount == UserChangedCount then
|
||||
local thumbIds = {}
|
||||
local thumbIdTable = result["thumbnails"]
|
||||
if thumbIdTable then
|
||||
for i = 1, #thumbIdTable do
|
||||
local data = thumbIdTable[i]
|
||||
-- AssetTypeId of 1 is a Image (33 is a video if can ever play videos)
|
||||
if data and data["AssetTypeId"] == 1 then
|
||||
local assetId = data["AssetId"]
|
||||
if assetId then
|
||||
table.insert(thumbIds, assetId)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
gameData.ThumbnailIds = thumbIds
|
||||
valid = true
|
||||
end
|
||||
end
|
||||
return valid
|
||||
end
|
||||
|
||||
local ThumbnailIdsCachedData = CreateCacheData(this, nil, GetGameDetailsRefreshInterval, RefreshThumbnailIdsAsync)
|
||||
|
||||
--Badges
|
||||
local function RefreshBadgeDataAsync(gameData)
|
||||
if XboxUpdateBadgeEndpoints then
|
||||
local startCount = UserChangedCount
|
||||
|
||||
local badgesData = {}
|
||||
local awardedBadges = {}
|
||||
local nextPageCursor;
|
||||
|
||||
repeat
|
||||
local result = Http.GetBadgesForUniverseAsync(gameData.UniverseId, nextPageCursor)
|
||||
if result and result.data then
|
||||
local badgeIds = {}
|
||||
for _,badgeData in ipairs(result.data) do
|
||||
if badgeData.enabled then
|
||||
table.insert(badgesData, badgeData)
|
||||
table.insert(badgeIds, badgeData.id)
|
||||
end
|
||||
end
|
||||
|
||||
if #badgeIds > 0 then
|
||||
local awardedBadgesResult = Http.GetUserAwardedBadgesAsync(
|
||||
XboxAppState.store:getState().RobloxUser.rbxuid,
|
||||
badgeIds
|
||||
)
|
||||
|
||||
if awardedBadgesResult and awardedBadgesResult.data then
|
||||
for _,badge in ipairs(awardedBadgesResult.data) do
|
||||
awardedBadges[badge.badgeId] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
nextPageCursor = result.nextPageCursor
|
||||
else
|
||||
nextPageCursor = nil
|
||||
end
|
||||
until nextPageCursor == nil
|
||||
|
||||
if startCount ~= UserChangedCount then
|
||||
return false
|
||||
end
|
||||
|
||||
local finalBadgesData = {}
|
||||
for i, badge in ipairs(badgesData) do
|
||||
local item = {
|
||||
Name = badge.name,
|
||||
Description = badge.description,
|
||||
AssetId = badge.iconImageId,
|
||||
Order = i,
|
||||
}
|
||||
|
||||
if awardedBadges[item.AssetId] then
|
||||
item.IsOwned = true
|
||||
end
|
||||
|
||||
table.insert(finalBadgesData, item)
|
||||
end
|
||||
|
||||
table.sort(finalBadgesData, function(a, b)
|
||||
if a["IsOwned"] == true and b["IsOwned"] == true then
|
||||
return a.Order < b.Order
|
||||
elseif a["IsOwned"] then
|
||||
return true
|
||||
elseif b["IsOwned"] then
|
||||
return false
|
||||
end
|
||||
return a.Order < b.Order
|
||||
end)
|
||||
|
||||
gameData.BadgeData = finalBadgesData
|
||||
return true
|
||||
end
|
||||
|
||||
local startCount = UserChangedCount
|
||||
local valid = false
|
||||
local result = Http.GetGameBadgeDataAsync(gameData.PlaceId)
|
||||
if not result then
|
||||
Utility.DebugLog("GameData:GetBadgeDataAsync() failed to get web response for placeId "..tostring(gameData.PlaceId))
|
||||
else
|
||||
if startCount == UserChangedCount then
|
||||
local badgeData = {}
|
||||
local badgeTable = result["GameBadges"]
|
||||
if badgeTable then
|
||||
for i = 1, #badgeTable do
|
||||
local data = badgeTable[i]
|
||||
if data then
|
||||
local badge = {}
|
||||
badge.Name = data["Name"]
|
||||
badge.Description = data["Description"]
|
||||
badge.AssetId = data["BadgeAssetId"]
|
||||
badge.IsOwned = data["IsOwned"]
|
||||
badge.Order = i
|
||||
table.insert(badgeData, badge)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
table.sort(badgeData, function(a, b)
|
||||
if a["IsOwned"] == true and b["IsOwned"] == true then
|
||||
return a.Order < b.Order
|
||||
elseif a["IsOwned"] then
|
||||
return true
|
||||
elseif b["IsOwned"] then
|
||||
return false
|
||||
end
|
||||
return a.Order < b.Order
|
||||
end)
|
||||
|
||||
gameData.BadgeData = badgeData
|
||||
valid = true
|
||||
end
|
||||
end
|
||||
return valid
|
||||
end
|
||||
|
||||
local BadgeCachedData = CreateCacheData(this, nil, GetGameDetailsRefreshInterval, RefreshBadgeDataAsync)
|
||||
|
||||
--[[ Async Public API ]]--
|
||||
--We don't want to run so many loading funcs in the BG at intervals,
|
||||
--so, these reload will be made when the data is requested
|
||||
function this:GetGameDetailsAsync()
|
||||
GameDetailsCachedData:Refresh()
|
||||
local gameData = self or {}
|
||||
self.OnGetGameDetailsEnd:fire(gameData)
|
||||
return gameData
|
||||
end
|
||||
|
||||
function this:GetVoteDataAsync()
|
||||
VoteCachedData:Refresh()
|
||||
local voteData = self.VoteData or {}
|
||||
self.OnGetVoteDataEnd:fire(voteData)
|
||||
return voteData
|
||||
end
|
||||
|
||||
function this:GetRecommendedGamesAsync()
|
||||
RecommendedGamesCachedData:Refresh()
|
||||
local recommendedGames = self.RecommendedGames or {}
|
||||
self.OnGetRecommendedGamesEnd:fire(recommendedGames)
|
||||
return recommendedGames
|
||||
end
|
||||
|
||||
function this:GetThumbnailIdsAsync()
|
||||
ThumbnailIdsCachedData:Refresh()
|
||||
local thumbnailIds = self.ThumbnailIds or {}
|
||||
self.OnGetThumbnailIdsEnd:fire(thumbnailIds)
|
||||
return thumbnailIds
|
||||
end
|
||||
|
||||
function this:GetBadgeDataAsync()
|
||||
BadgeCachedData:Refresh()
|
||||
local badgeData = self.BadgeData or {}
|
||||
self.OnGetBadgeDataEnd:fire(badgeData)
|
||||
return badgeData
|
||||
end
|
||||
|
||||
--We force update IconId whenever it get called
|
||||
local debounceGetGameIconIdAsync = false
|
||||
function this:GetGameIconIdAsync()
|
||||
while debounceGetGameIconIdAsync do
|
||||
wait()
|
||||
end
|
||||
debounceGetGameIconIdAsync = true
|
||||
|
||||
local startCount = UserChangedCount
|
||||
local iconId = nil
|
||||
local result = Http.GetGameIconIdAsync(placeId)
|
||||
if result then
|
||||
iconId = result["ImageId"]
|
||||
-- use placeId as backup
|
||||
if not iconId then
|
||||
iconId = placeId
|
||||
end
|
||||
end
|
||||
|
||||
if startCount == UserChangedCount then
|
||||
self.IconId = iconId
|
||||
end
|
||||
debounceGetGameIconIdAsync = false
|
||||
return iconId
|
||||
end
|
||||
|
||||
function this:SetCanVote(value)
|
||||
local voteData = self.VoteData
|
||||
voteData.CanVote = value
|
||||
voteData.CantVoteReason = value and "" or "PlayGame"
|
||||
end
|
||||
|
||||
|
||||
--[[ Post Public API ]]--
|
||||
function this:PostFavoriteAsync()
|
||||
local result = Http.PostFavoriteToggleAsync(placeId)
|
||||
local success = result and result["success"] == true
|
||||
local reason = nil
|
||||
if not success then
|
||||
reason = "Failed"
|
||||
-- the floodcheck message is "Whoa. Slow Down.". So if there is a message,
|
||||
-- let's just say flood check?
|
||||
if result and result["message"] then
|
||||
reason = "FloodCheck"
|
||||
end
|
||||
else
|
||||
self.IsFavorited = not self.IsFavorited
|
||||
end
|
||||
EventHub:dispatchEvent(EventHub.Notifications["FavoriteToggle"], success, self.PlaceId)
|
||||
|
||||
if not success then
|
||||
return success, reason
|
||||
else
|
||||
return success
|
||||
end
|
||||
end
|
||||
|
||||
function this:PostVoteAsync(newVote)
|
||||
local result = Http.PostGameVoteAsync(placeId, newVote)
|
||||
if not result then
|
||||
return nil
|
||||
end
|
||||
|
||||
local success = result["Success"] == true
|
||||
if not success then
|
||||
return success, result["ModalType"]
|
||||
else
|
||||
local voteData = self.VoteData
|
||||
local prevVote = voteData.UserVote
|
||||
if newVote == true then
|
||||
voteData.UpVotes = (voteData.UpVotes or 0) + 1
|
||||
if prevVote == false then
|
||||
voteData.DownVotes = (voteData.DownVotes or 0) - 1
|
||||
end
|
||||
elseif newVote == false then
|
||||
voteData.DownVotes = (voteData.DownVotes or 0) + 1
|
||||
if prevVote == true then
|
||||
voteData.UpVotes = (voteData.UpVotes or 0) - 1
|
||||
end
|
||||
elseif newVote == nil then
|
||||
if prevVote == true then
|
||||
voteData.UpVotes = (voteData.UpVotes or 0) - 1
|
||||
elseif prevVote == false then
|
||||
voteData.DownVotes = (voteData.DownVotes or 0) - 1
|
||||
end
|
||||
end
|
||||
voteData.UserVote = newVote
|
||||
end
|
||||
|
||||
return success
|
||||
end
|
||||
|
||||
--Init Data and Signals
|
||||
this.OnGetGameDetailsEnd = Utility.Signal()
|
||||
this.OnGetVoteDataEnd = Utility.Signal()
|
||||
this.OnGetRecommendedGamesEnd = Utility.Signal()
|
||||
this.OnGetThumbnailIdsEnd = Utility.Signal()
|
||||
this.OnGetBadgeDataEnd = Utility.Signal()
|
||||
|
||||
if game then
|
||||
this.PlaceId = placeId
|
||||
this.Name = GameData:GetFilteredGameName(name, creatorName)
|
||||
this.CreatorName = creatorName
|
||||
this.IconId = iconId
|
||||
this.ImageToken = imageToken
|
||||
--these two are optional
|
||||
this.VoteData = voteData
|
||||
this.CreatorUserId = creatorId
|
||||
else
|
||||
this.PlaceId = placeId
|
||||
--No init data, we fetch the essential ones (name, creatorName, iconId)
|
||||
this:GetGameDetailsAsync()
|
||||
this:GetGameIconIdAsync()
|
||||
end
|
||||
|
||||
this.UpdateDebounce = false
|
||||
currGameCachedDataCount = currGameCachedDataCount + 1
|
||||
|
||||
--Need to clear Cached Data now
|
||||
if currGameCachedDataCount > maxGameCachedDataCount then
|
||||
local gameCachedDataSorted = {}
|
||||
for key in pairs(gameCachedData) do
|
||||
--Add key attribute
|
||||
gameCachedData[key].Key = key
|
||||
--Recalc the Total AccessCount for every cached data
|
||||
gameCachedData[key].TotalAccessCount = 0
|
||||
if gameCachedData[key].RelatedGuiObjects then
|
||||
local newRelatedGuiObjects = {}
|
||||
for i = 1, #gameCachedData[key].RelatedGuiObjects do
|
||||
local relatedGuiObject = gameCachedData[key].RelatedGuiObjects[i]
|
||||
if relatedGuiObject and relatedGuiObject:IsA("GuiObject") and relatedGuiObject.Parent then
|
||||
gameCachedData[key].TotalAccessCount = gameCachedData[key].TotalAccessCount + 1
|
||||
table.insert(newRelatedGuiObjects, relatedGuiObject)
|
||||
end
|
||||
end
|
||||
gameCachedData[key].TotalAccessCount = gameCachedData[key].TotalAccessCount + gameCachedData[key].AccessCount
|
||||
gameCachedData[key].RelatedGuiObjects = newRelatedGuiObjects
|
||||
end
|
||||
table.insert(gameCachedDataSorted, gameCachedData[key])
|
||||
end
|
||||
|
||||
currGameCachedDataCount = #gameCachedDataSorted
|
||||
--Double check, in case we miscounted the currGameCachedDataCount
|
||||
if currGameCachedDataCount > maxGameCachedDataCount then
|
||||
--Put not used data in the back
|
||||
table.sort(gameCachedDataSorted, function(a, b)
|
||||
return a.TotalAccessCount > b.TotalAccessCount
|
||||
end)
|
||||
|
||||
--Make sure the removed data is not used anywhere and clear all not in use cached data
|
||||
--kind of optimization so we don't clear caching for each single overloaded item
|
||||
while gameCachedDataSorted[#gameCachedDataSorted] and gameCachedDataSorted[#gameCachedDataSorted].TotalAccessCount <= 0 do
|
||||
local RemovingKey = gameCachedDataSorted[#gameCachedDataSorted].Key
|
||||
--Clear RecommendedGames Access
|
||||
if gameCachedData[RemovingKey].RecommendedGames then
|
||||
local recommendedGames = gameCachedData[RemovingKey].RecommendedGames
|
||||
for i = 1, #recommendedGames do
|
||||
local recommendedGamePlaceId = recommendedGames[i]
|
||||
if recommendedGamePlaceId then
|
||||
local recommendedGameData = GameData:GetGameData(recommendedGamePlaceId)
|
||||
if recommendedGameData then
|
||||
recommendedGameData.AccessCount = recommendedGameData.AccessCount - 1
|
||||
end
|
||||
end
|
||||
recommendedGames[i]= nil
|
||||
end
|
||||
end
|
||||
gameCachedData[RemovingKey] = nil
|
||||
gameCachedDataSorted[#gameCachedDataSorted] = nil
|
||||
end
|
||||
currGameCachedDataCount = #gameCachedDataSorted
|
||||
end
|
||||
end
|
||||
else
|
||||
local gameData = gameCachedData[placeId]
|
||||
gameData.UpdateDebounce = true
|
||||
gameData.AccessCount = gameData.AccessCount and gameData.AccessCount + 1 or 1
|
||||
gameData.PlaceId = placeId
|
||||
gameData.Name = GameData:GetFilteredGameName(name, creatorName)
|
||||
gameData.CreatorName = creatorName
|
||||
|
||||
--We only use the IconId comes with init to avoid game button icon updates
|
||||
if not gameData.IconId then
|
||||
gameData.IconId = iconId
|
||||
end
|
||||
--We only use the IconId comes with init to avoid game button icon updates
|
||||
if not gameData.ImageToken then
|
||||
gameData.ImageToken = imageToken
|
||||
end
|
||||
|
||||
gameData.VoteData = voteData
|
||||
gameData.CreatorUserId = creatorId
|
||||
gameData.UpdateDebounce = false
|
||||
end
|
||||
end
|
||||
|
||||
function GameData:GetGameData(placeId, asyncFetch)
|
||||
if placeId then
|
||||
if asyncFetch then
|
||||
if gameCachedData[placeId] then
|
||||
--if asyncFetch, we wait until the update is done
|
||||
while gameCachedData[placeId] and gameCachedData[placeId].UpdateDebounce do
|
||||
wait()
|
||||
end
|
||||
else
|
||||
--Will refetch data if the data has been flushed from cached data,
|
||||
--should never happen, as we don't clear cached data as long as there is still any access to it
|
||||
local game = {
|
||||
placeId = placeId
|
||||
}
|
||||
GameData:UpdateGameData(game)
|
||||
end
|
||||
end
|
||||
return gameCachedData[placeId]
|
||||
end
|
||||
end
|
||||
|
||||
--This is the most robust way to ensure that we don't flush cached gamedata when it's still used on some guiObject.
|
||||
--Make sure the guiObject already has Parent set up when added
|
||||
function GameData:AddRelatedGuiObject(placeId, guiObject)
|
||||
if placeId and guiObject and guiObject:IsA("GuiObject") and guiObject.Parent then
|
||||
local gameData = GameData:GetGameData(placeId)
|
||||
if gameData then
|
||||
table.insert(gameData.RelatedGuiObjects, guiObject)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--This is the most robust way to ensure that we don't flush cached gamedata when it's still used on sort pages.
|
||||
function GameData:ChangeGameDataAccessCount(placeId, changeValue)
|
||||
if placeId then
|
||||
local gameData = GameData:GetGameData(placeId)
|
||||
if gameData then
|
||||
gameData.AccessCount = gameData.AccessCount + changeValue
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function GameData:FlushGameData()
|
||||
for placeId,_ in pairs(gameCachedData) do
|
||||
gameCachedData[placeId] = nil
|
||||
end
|
||||
gameCachedData = {}
|
||||
currGameCachedDataCount = 0
|
||||
end
|
||||
|
||||
return GameData
|
||||
Reference in New Issue
Block a user