Clients/Client2018/content/internal/AppShell/Modules/Shell/PackageData.lua

807 lines
26 KiB
Lua

--[[
// PackageData.lua
// Created by Kip Turner, Bo Zhang
// Copyright Roblox 2017
]]
local CoreGui = game:GetService("CoreGui")
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
local Modules = GuiRoot:FindFirstChild("Modules")
local ShellModules = Modules:FindFirstChild("Shell")
local ContentProvider = game:GetService("ContentProvider")
local MarketplaceService = game:GetService('MarketplaceService')
local PlatformService = nil
pcall(function()PlatformService = game:GetService('PlatformService') end)
local ThirdPartyUserService = nil
pcall(function()ThirdPartyUserService = game:GetService('ThirdPartyUserService') end)
local Utility = require(ShellModules:FindFirstChild('Utility'))
local Http = require(ShellModules:FindFirstChild('Http'))
local UserData = require(ShellModules:FindFirstChild('UserData'))
local EventHub = require(ShellModules:FindFirstChild('EventHub'))
local GlobalSettings = require(ShellModules:FindFirstChild('GlobalSettings'))
local ReloaderManager = require(ShellModules:FindFirstChild('ReloaderManager'))
local CreateCacheData = require(ShellModules:FindFirstChild('CachedData'))
local ThumbnailLoader = require(ShellModules:FindFirstChild('ThumbnailLoader'))
local XboxAppState = require(ShellModules:FindFirstChild('AppState'))
local RequestingWearAsset = false
local function AwaitWearAssetRequest()
while RequestingWearAsset do wait(0.1) end
end
local RequestingBuyAsset = false
local function AwaitBuyAssetRequest()
while RequestingBuyAsset do wait(0.1) end
end
local function PreloadCharacterAppearanceAsync()
local character = nil
local success = pcall(function()
character = game.Players:GetCharacterAppearanceAsync(UserData:GetLocalUserIdAsync())
end)
if character then
ContentProvider:PreloadAsync({ character })
end
return success
end
--Hard code the assetId to productId map
local AvatarAssetId_XboxProductIdMap =
{
['807301633'] = '899a379d-0a66-4b07-8bcd-29b1e38699ba'; --Boy Avatar
['807340263'] = '7dba5b02-02be-4442-814e-9b9ebf6d66bf'; --Girl Avatar
}
local function CreatePackageItem(data)
local this = {}
this.Owned = false
this.Wearing = false
this.OwnershipChanged = Utility.Signal()
this.IsWearingChanged = Utility.Signal()
local productInfo = nil
local partIds = {}
function this:GetAssetId()
local assetId = data and data['AssetId']
if not assetId then
assetId = data and data['Item'] and data['Item']['AssetId']
end
return tonumber(assetId)
end
function this:OpenAvatarDetailInXboxStore()
local assetId = self:GetAssetId()
if PlatformService then
local XboxProductId = AvatarAssetId_XboxProductIdMap[tostring(assetId)]
if XboxProductId then
PlatformService:OpenProductDetail(XboxProductId)
end
end
end
function this:IsXboxAddOn()
return AvatarAssetId_XboxProductIdMap[tostring(self:GetAssetId())] and true or false
end
function this:GetProductIdAsync()
while not productInfo do wait() end
return productInfo and productInfo['ProductId']
end
function this:GetPartIdsAsync()
while not partIds do wait() end
return partIds
end
function this:UpdatePartIdsAsync()
local assetId = self:GetAssetId()
if assetId then
while not partIds do wait() end
--Reset partIds
partIds = nil
local response = Http.GetPackageAssetsAsync(assetId)
if response then
partIds = response["assetIds"]
end
if partIds == nil then
partIds = {}
else
table.sort(partIds)
end
end
return partIds
end
function this:IsWearing()
return self.Wearing
end
function this:SetWearing(newWearing)
if newWearing ~= self.Wearing then
self.Wearing = newWearing
self.IsWearingChanged:fire(newWearing)
end
end
function this:BuyAsync()
EventHub:dispatchEvent(EventHub.Notifications["AvatarPurchaseBegin"], self:GetAssetId())
Utility.DebugLog("Do buy", 'productId', self:GetProductIdAsync(), 'robuxPrice', self:GetRobuxPrice())
AwaitBuyAssetRequest()
RequestingBuyAsset = true
local purchaseResult = Http.PurchaseProductAsync(self:GetProductIdAsync(), self:GetRobuxPrice(), self:GetCreatorId(), 1)
RequestingBuyAsset = false
local nowOwns = purchaseResult and purchaseResult['TransactionVerb'] == 'bought'
if nowOwns then
EventHub:dispatchEvent(EventHub.Notifications["AvatarPurchaseSuccess"], self:GetAssetId(), nowOwns)
end
return purchaseResult
end
function this:IsOwned()
return self.Owned
end
function this:SetOwned(newOwned)
if self.Owned ~= newOwned then
self.Owned = newOwned
self.OwnershipChanged:fire(newOwned)
end
end
function this:GetRobuxPrice()
local robuxPrice = data and data['PriceInRobux']
if not robuxPrice then
robuxPrice = data and data['Product'] and data['Product']['PriceInRobux']
end
local isPublicDomain = data and data['IsPublicDomain'] == true
if isPublicDomain == nil then
isPublicDomain = data and data['Product'] and data['Product']['IsPublicDomain'] == true
end
if not robuxPrice and isPublicDomain then
robuxPrice = 0
end
return robuxPrice
end
function this:GetCreatorId()
return data and data['Creator'] and data['Creator']['Id']
end
function this:WearAsync()
local assetId = self:GetAssetId()
if assetId then
EventHub:dispatchEvent(EventHub.Notifications["AvatarEquipBegin"], assetId)
AwaitWearAssetRequest()
RequestingWearAsset = true
local result;
result = Http.SetWearingAssetsAsync(self:GetPartIdsAsync())
RequestingWearAsset = false
if result and result['success'] == true then
EventHub:dispatchEvent(EventHub.Notifications["AvatarEquipSuccess"], assetId)
end
return result
end
end
function this:GetName()
local resultPackageName = self:GetFullName()
if resultPackageName then
local colonPosition = string.find(resultPackageName, ":")
if colonPosition then
resultPackageName = string.sub(resultPackageName, 1, colonPosition - 1)
end
else
resultPackageName = "Unknown"
end
return resultPackageName
end
function this:GetFullName()
local name = data and data['Name']
if not name then
name = data and data['Item'] and data["Item"]['Name']
end
return name or "Unknown"
end
function this:GetDescriptionAsync()
while not productInfo do wait() end
return productInfo and productInfo['Description']
end
spawn(function()
productInfo = MarketplaceService:GetProductInfo(this:GetAssetId())
if productInfo == nil then productInfo = {} end
end)
--We get partIds when we create package
this:UpdatePartIdsAsync()
return this
end
local PackageData = {}
do
--Cached Data
--InternalPackageDataCache only gets updated at intervals
local InternalPackageDataCache = nil
--VisiblePackageDataCache gets updated by user operation
--and will be checked and updated when GetCachedData() is called
local VisiblePackageDataCache = nil
local ProfileImageThumbnailLoader = nil
local UpdateFuncId = nil
--Start Image Update Times for wearing
local ProfileImageUpdateForWearing = 0
--Bool to check whether we update pane while updating caching data
local visiblePackageUpdating = false
local function ResetCacheData()
InternalPackageDataCache = nil
VisiblePackageDataCache = nil
ProfileImageThumbnailLoader = nil
UpdateFuncId = nil
ProfileImageUpdateForWearing = 0
visiblePackageUpdating = false
end
local function deepcopy(orig)
local orig_type = type(orig)
local copy
if orig_type == 'table' then
copy = {}
for orig_key, orig_value in next, orig, nil do
copy[deepcopy(orig_key)] = deepcopy(orig_value)
end
setmetatable(copy, deepcopy(getmetatable(orig)))
else -- number, string, boolean, etc
copy = orig
end
return copy
end
local UserChangedCount = 0
--Add to cope with avatar purchase through Xbox Store
local ConsumePurchasedConn = nil
local function OnUserAccountChanged()
local startCount = UserChangedCount
ResetCacheData()
spawn(function()
if startCount == UserChangedCount then
spawn(PreloadCharacterAppearanceAsync)
local RefreshInterval = GlobalSettings.AvatarPaneRefreshInterval
ReloaderManager:removeReloader("PackageData")
UpdateFuncId = ReloaderManager:addReloaderFunc("PackageData", PackageData.UpdateCachedDataAsync, RefreshInterval)
ReloaderManager:callReloaderFunc("PackageData", UpdateFuncId)
Utility.DisconnectEvent(ConsumePurchasedConn)
if PlatformService then
ConsumePurchasedConn = PlatformService.ConsumePurchased:connect(function(platformPurchaseResult, purchasedConsumablesInfo)
if platformPurchaseResult == 3 then
spawn(function()
PackageData:UpdatePurchasedConsumablesAsync(purchasedConsumablesInfo)
end)
end
end)
end
end
end)
end
EventHub:addEventListener(EventHub.Notifications["AuthenticationSuccess"], "PackageData", OnUserAccountChanged)
local function OnUserSignOut()
UserChangedCount = UserChangedCount + 1
ReloaderManager:removeReloader("PackageData")
EventHub:removeEventListener(EventHub.Notifications["AvatarEquipBegin"], "VisiblePackageDataCache")
EventHub:removeEventListener(EventHub.Notifications["AvatarPurchaseBegin"], "VisiblePackageDataCache")
EventHub:removeEventListener(EventHub.Notifications["AvatarEquipSuccess"], "VisiblePackageDataCache")
EventHub:removeEventListener(EventHub.Notifications["AvatarPurchaseSuccess"], "VisiblePackageDataCache")
EventHub:removeEventListener(EventHub.Notifications["CharacterUpdated"], "VisiblePackageDataCache")
EventHub:removeEventListener(EventHub.Notifications["CharacterEquipped"], "VisiblePackageDataCache")
Utility.DisconnectEvent(ConsumePurchasedConn)
end
if ThirdPartyUserService then
ThirdPartyUserService.ActiveUserSignedOut:connect(OnUserSignOut)
end
local function GetAvailableXboxCatalogPackagesAsync()
local isFinalPage = false
local packages = {}
local index = 0
local count = 100
repeat
local result = nil
Utility.ExponentialRepeat(
function() return result == nil end,
function()result = Http.GetXboxProductsAsync(index, count) end,
2)
if result then
local items = result['Products']
if items then
if #items < count then
isFinalPage = true
end
for _, itemInfo in pairs(items) do
table.insert(packages, itemInfo)
end
end
end
index = index + count
until result == nil or isFinalPage
if isFinalPage then
return packages
end
end
local function GetOwnedCatalogPackageIdsByUserAsync(userId)
local packages = Http.GetUserOwnedPackagesAsync(userId)
if packages then
local data = packages['IsValid'] and packages['Data']
local items = data and data['Items']
local result = {}
if items then
for _, itemInfo in pairs(items) do
local assetId = itemInfo and itemInfo['Item'] and itemInfo['Item']['AssetId']
result[assetId] = itemInfo
end
end
return result
end
end
local function getCatalogPackagesAsync()
local xboxCatalogPackages = GetAvailableXboxCatalogPackagesAsync()
local myPackages = GetOwnedCatalogPackageIdsByUserAsync(XboxAppState.store:getState().RobloxUser.rbxuid)
if xboxCatalogPackages and myPackages then
local result = {}
result.Packages = {}
result.OwnedInfo = {}
--We use array to store xboxCatalogPackages as the order is important
--and then use result.OwnedInfo to store OwnedInfo to avoid duplicate with myPackages
for _, packageInfo in pairs(xboxCatalogPackages) do
local package = CreatePackageItem(packageInfo)
local assetId = package:GetAssetId()
local owned = (myPackages[assetId] ~= nil)
result.OwnedInfo[assetId] = owned
table.insert(result.Packages, package)
end
for assetId, packageInfo in pairs(myPackages) do
if result.OwnedInfo[assetId] == nil then
local package = CreatePackageItem(packageInfo)
result.OwnedInfo[assetId] = true
table.insert(result.Packages, package)
end
end
return result
end
end
--Get new packages and owned info
function PackageData:GetPackagesAndOwnedInfoAsync()
local startCount = UserChangedCount
local result = nil
Utility.ExponentialRepeat(
function() return result == nil and startCount == UserChangedCount end,
function()result = getCatalogPackagesAsync() end,
3)
if startCount ~= UserChangedCount then
result = nil
end
return result
end
--Utility function to compare arrays
local function CompareAssetIdArrays(arr1, arr2)
if type(arr1) == 'table' and type(arr2) == 'table' then
if #arr1 == #arr2 then
for i = 1, #arr1 do
if tonumber(arr1[i]) ~= tonumber(arr2[i]) then
return false
end
end
return true
end
end
return false
end
--Get WearingAssetId from packages
function PackageData:GetWearingPackageAssetIdAsync(packages)
local startCount = UserChangedCount
local newWearingAssetId = nil
local rbxuid = XboxAppState.store:getState().RobloxUser.rbxuid
if rbxuid then
if packages then
local response = Http.GetCurrentlyWearingAsync(rbxuid)
if not response then
return
end
local wornAssetIds = response["assetIds"]
for _, package in pairs(packages) do
if CompareAssetIdArrays(package:GetPartIdsAsync(), wornAssetIds) then
newWearingAssetId = tonumber(package:GetAssetId())
break
end
end
end
end
if startCount ~= UserChangedCount then
newWearingAssetId = nil
end
return newWearingAssetId
end
function PackageData:GetProfileImageAsync()
local startCount = UserChangedCount
local newProfileImage = {}
if ProfileImageThumbnailLoader then
ProfileImageThumbnailLoader:Cancel()
end
ProfileImageThumbnailLoader = ThumbnailLoader:Create(newProfileImage, XboxAppState.store:getState().RobloxUser.rbxuid,
ThumbnailLoader.AvatarSizes.Size352x352, ThumbnailLoader.AssetType.Avatar, true)
ProfileImageThumbnailLoader:LoadAsync(false, false)
--If user has changed or we fail to get the ImageUrl, return nil
if newProfileImage.Image == "" or startCount ~= UserChangedCount then
newProfileImage = nil
end
return newProfileImage
end
--Called at intervals, try to fetch latest CachedData
local debounceUpdateCachedData = false
function PackageData.UpdateCachedDataAsync()
if debounceUpdateCachedData then
while debounceUpdateCachedData do wait() end
end
debounceUpdateCachedData = true
local startCount = UserChangedCount
local maxRetry = 2
local valid = false
Utility.ExponentialRepeat(
function() return valid == false and startCount == UserChangedCount end,
function() valid = PackageData:UpdateCachedData() end,
maxRetry)
debounceUpdateCachedData = false
end
--Three Utility functions to update data
local function UpdateDataProfileImage(data, newProfileImage)
if not data then return end
data.ProfileImage:Update(CreateCacheData(newProfileImage, tick()))
end
local function UpdateDataWearing(data, newWearingAssetId)
if not data then return end
local packages = data.Packages.Data
data.WearingAssetId:Update(CreateCacheData(newWearingAssetId, tick()))
for _, package in pairs(packages) do
if package then
if package:IsOwned() then
package:SetWearing(newWearingAssetId == package:GetAssetId())
else
package:SetWearing(false)
end
end
end
end
local function UpdateDataOwned(data, newOwnedAssetId, newOwned)
if not data then return end
local packages = data.Packages.Data
if data.OwnedInfo then
if data.OwnedInfo[newOwnedAssetId] then
data.OwnedInfo[newOwnedAssetId]:Update(CreateCacheData(newOwned, tick()))
else
data.OwnedInfo[newOwnedAssetId] = CreateCacheData(newOwned, tick())
end
end
for _, package in pairs(packages) do
if package then
if package:GetAssetId() == newOwnedAssetId then
package:SetOwned(newOwned)
if not newOwned then --We can't wear not owned packages
package:SetWearing(false)
end
break
end
end
end
end
function PackageData:UpdateCachedData()
local validUpdate = false
local startCount = UserChangedCount
visiblePackageUpdating = false
--Get Packages and OwnedInfo
local newResult = PackageData:GetPackagesAndOwnedInfoAsync()
local newPackages = newResult and newResult.Packages
local newOwnedInfo = newResult and newResult.OwnedInfo
--Must make sure we get valid Packages and OwnedInfo
if newResult and newPackages and newOwnedInfo then
local newPackageDataCache = {}
newPackageDataCache.Packages = CreateCacheData(newPackages, tick())
newPackageDataCache.OwnedInfo = {}
if newOwnedInfo then
for assetId, owned in pairs(newOwnedInfo) do
newPackageDataCache.OwnedInfo[assetId] = CreateCacheData(owned, tick())
end
end
--Get WearingAssetId
local newWearingAssetId = PackageData:GetWearingPackageAssetIdAsync(newPackages)
newPackageDataCache.WearingAssetId = CreateCacheData(newWearingAssetId, tick())
--Get ProfileImage
local newProfileImage = PackageData:GetProfileImageAsync()
newPackageDataCache.ProfileImage = CreateCacheData(newProfileImage, tick())
--Make sure the user doesn't change, then
--if the user worn/purchased while we update in BG, we damp the data as it may be stale
if startCount == UserChangedCount and not visiblePackageUpdating then
--New CachedData
InternalPackageDataCache = newPackageDataCache
InternalPackageDataCache.Version = tick()
--VisiblePackageDataCache is the CacheData used in app and will turn into visible elements
--user can operate on these visible elements and update VisiblePackageDataCache like wear/buy and so on
--We need the PackageDataCache to init VisiblePackageDataCache
if not VisiblePackageDataCache then
VisiblePackageDataCache = deepcopy(InternalPackageDataCache)
VisiblePackageDataCache.OnProfileImageUpdateBegin = Utility.Signal()
VisiblePackageDataCache.OnProfileImageUpdateEnd = Utility.Signal()
VisiblePackageDataCache.OnDifferentWearing = Utility.Signal()
VisiblePackageDataCache.OnDifferentOwned = Utility.Signal()
--Each single pacakge can fire AvatarEquipSuccess and AvatarPurchaseSuccess.
--As whenever one single package data changes, other packages(previous wearing/not owned) may also be influenced,
--so we need listen to the signals to update all packages wearing and owned
EventHub:removeEventListener(EventHub.Notifications["AvatarEquipBegin"], "VisiblePackageDataCache")
EventHub:removeEventListener(EventHub.Notifications["AvatarPurchaseBegin"], "VisiblePackageDataCache")
EventHub:removeEventListener(EventHub.Notifications["AvatarEquipSuccess"], "VisiblePackageDataCache")
EventHub:removeEventListener(EventHub.Notifications["AvatarPurchaseSuccess"], "VisiblePackageDataCache")
--Listen to Character update/equip from Avatar Editor
EventHub:removeEventListener(EventHub.Notifications["CharacterUpdated"], "VisiblePackageDataCache")
EventHub:removeEventListener(EventHub.Notifications["CharacterEquipped"], "VisiblePackageDataCache")
EventHub:addEventListener(EventHub.Notifications["AvatarEquipBegin"], "VisiblePackageDataCache", function(assetId) visiblePackageUpdating = true end)
EventHub:addEventListener(EventHub.Notifications["AvatarPurchaseBegin"], "VisiblePackageDataCache", function(assetId) visiblePackageUpdating = true end)
EventHub:addEventListener(EventHub.Notifications["AvatarEquipSuccess"], "VisiblePackageDataCache",
function(assetId, skipProfileImageUpdate)
local startCount = UserChangedCount
UpdateDataWearing(VisiblePackageDataCache, assetId)
VisiblePackageDataCache.OnDifferentWearing:fire(assetId)
spawn(PreloadCharacterAppearanceAsync)
if not skipProfileImageUpdate then
--Update Profile Image for wearing
ProfileImageUpdateForWearing = ProfileImageUpdateForWearing + 1
local thisProfileImageUpdateForWearing = ProfileImageUpdateForWearing
VisiblePackageDataCache.OnProfileImageUpdateBegin:fire()
visiblePackageUpdating = true
local newProfileImage = PackageData:GetProfileImageAsync()
if startCount == UserChangedCount and VisiblePackageDataCache then
if thisProfileImageUpdateForWearing == ProfileImageUpdateForWearing and newProfileImage then
UpdateDataProfileImage(VisiblePackageDataCache, newProfileImage)
VisiblePackageDataCache.OnProfileImageUpdateEnd:fire(newProfileImage)
else
VisiblePackageDataCache.OnProfileImageUpdateEnd:fire()
end
end
end
end)
EventHub:addEventListener(EventHub.Notifications["AvatarPurchaseSuccess"], "VisiblePackageDataCache",
function(assetId, owned)
UpdateDataOwned(VisiblePackageDataCache, assetId, owned)
VisiblePackageDataCache.OnDifferentOwned:fire(assetId, owned)
end)
--CharacterUpdate: If anything changed on character, reload the profile image
EventHub:addEventListener(EventHub.Notifications["CharacterUpdated"], "VisiblePackageDataCache",
function()
local startCount = UserChangedCount
--Update Profile Image for wearing
ProfileImageUpdateForWearing = ProfileImageUpdateForWearing + 1
local thisProfileImageUpdateForWearing = ProfileImageUpdateForWearing
VisiblePackageDataCache.OnProfileImageUpdateBegin:fire()
visiblePackageUpdating = true
local newProfileImage = PackageData:GetProfileImageAsync()
if startCount == UserChangedCount and VisiblePackageDataCache then
if thisProfileImageUpdateForWearing == ProfileImageUpdateForWearing and newProfileImage then
UpdateDataProfileImage(VisiblePackageDataCache, newProfileImage)
VisiblePackageDataCache.OnProfileImageUpdateEnd:fire(newProfileImage)
else
VisiblePackageDataCache.OnProfileImageUpdateEnd:fire()
end
end
end)
--If we equipped any asset on character, make wearing update
EventHub:addEventListener(EventHub.Notifications["CharacterEquipped"], "VisiblePackageDataCache",
function(newAssets, skipProfileImageUpdate)
--Get assets array from store assets
local assetsArray = {}
for _, assetIds in pairs(newAssets) do
for _, assetId in pairs(assetIds) do
table.insert(assetsArray, tonumber(assetId))
end
end
table.sort(assetsArray)
local newWearingAssetId = nil
local curWearingAssetId = nil
local packages = VisiblePackageDataCache.Packages.Data
--Compare package assetids with wearing assetids
for _, package in pairs(packages) do
if package:IsWearing() then
curWearingAssetId = tonumber(package:GetAssetId())
end
end
for _, package in pairs(packages) do
if CompareAssetIdArrays(package:GetPartIdsAsync(), assetsArray) then
newWearingAssetId = tonumber(package:GetAssetId())
break
end
end
if curWearingAssetId ~= newWearingAssetId then
--Fire AvatarEquipSuccess event for achievement and wearing update
EventHub:dispatchEvent(EventHub.Notifications["AvatarEquipSuccess"], newWearingAssetId, skipProfileImageUpdate)
end
end)
end
validUpdate = true
end
end
return validUpdate
end
--"Sync" call to get VisiblePackageDataCache unless VisiblePackageDataCache is not initialized
function PackageData:GetCachedData()
--If not cached data, we wait until UpdateCachedData is done
if not VisiblePackageDataCache then
while debounceUpdateCachedData do wait() end
--If still no data, try to fetch VisiblePackageDataCache again manually
if not VisiblePackageDataCache then
ReloaderManager:callReloaderFunc("PackageData", UpdateFuncId)
end
else
if InternalPackageDataCache then
--We merge VisiblePackageDataCache with InternalPackageDataCache if it's Version is behind
--use Update to make sure we are using the latest cached data
if VisiblePackageDataCache.Version < InternalPackageDataCache.Version then
VisiblePackageDataCache.Packages:Update(InternalPackageDataCache.Packages)
VisiblePackageDataCache.WearingAssetId:Update(InternalPackageDataCache.WearingAssetId)
VisiblePackageDataCache.ProfileImage:Update(InternalPackageDataCache.ProfileImage)
for assetId,_ in pairs(InternalPackageDataCache.OwnedInfo) do
if VisiblePackageDataCache.OwnedInfo[assetId] then
VisiblePackageDataCache.OwnedInfo[assetId]:Update(InternalPackageDataCache.OwnedInfo[assetId])
else
VisiblePackageDataCache.OwnedInfo[assetId] = InternalPackageDataCache.OwnedInfo[assetId]
end
end
VisiblePackageDataCache.Version = InternalPackageDataCache.Version
end
end
end
if VisiblePackageDataCache then
--Update Packages Owned and Wearing based on the merged data
for _, package in pairs(VisiblePackageDataCache.Packages.Data) do
local assetId = package:GetAssetId()
local wearingAssetId = VisiblePackageDataCache.WearingAssetId.Data
if VisiblePackageDataCache.OwnedInfo[assetId].Data then
package:SetOwned(true)
package:SetWearing(wearingAssetId == assetId)
else
package:SetOwned(false)
package:SetWearing(false)
end
end
end
return VisiblePackageDataCache
end
--Check whether the VisiblePackageDataCache has been initialized
function PackageData:HasCachedData()
return VisiblePackageDataCache ~= nil
end
--Update Avatar Pane for purchasing consumables
local debounceUpdatePurchasedConsumables = false
function PackageData:UpdatePurchasedConsumablesAsync(purchasedConsumablesInfo)
local startCount = UserChangedCount
--Wait until we get VisiblePackageDataCache
if not VisiblePackageDataCache then
while debounceUpdateCachedData do wait() end
end
while debounceUpdatePurchasedConsumables do wait() end
debounceUpdatePurchasedConsumables = true
if startCount == UserChangedCount and purchasedConsumablesInfo and #purchasedConsumablesInfo > 0 and VisiblePackageDataCache then
--Check if any consumable is an Avatar
local purchasedAvatarAssetIdMap = {}
local purchasedAvatar = false
for _, consumable in pairs(purchasedConsumablesInfo) do
if consumable and tostring(consumable['Type']) == 'Avatar' then
purchasedAvatarAssetIdMap[tostring(consumable['Roblox_AssetId'])] = true
purchasedAvatar = true
end
end
if purchasedAvatar then
local consumedAssetIds = {}
for _, package in pairs(VisiblePackageDataCache.Packages.Data) do
local assetId = package:GetAssetId()
if purchasedAvatarAssetIdMap[tostring(assetId)] then
table.insert(consumedAssetIds, assetId)
end
end
--if user consumed Avatar and still in the same session, we update the owned
if startCount == UserChangedCount and VisiblePackageDataCache then
--inform the BG update that we are updating the visible packages
visiblePackageUpdating = true
for i = 1, #consumedAssetIds do
EventHub:dispatchEvent(EventHub.Notifications["AvatarPurchaseSuccess"], consumedAssetIds[i], true)
end
end
end
end
debounceUpdatePurchasedConsumables = false
end
end
return PackageData