Hey! I made a data module using profile service and others. However, I am not sure if it could be condensed, have added type notations, or more promises. Any help would be appreciated. Pardon the length of the code.
type Receipt = {
PlayerId: number,
PurchaseId: number,
ProductId: number
}
local Players = game:GetService"Players"
local ReplicatedStorage = game:GetService"ReplicatedStorage"
local MarketPlaceService = game:GetService"MarketplaceService"
local Knit = require(ReplicatedStorage.Packages.Knit)
local Promise = require(ReplicatedStorage.Packages.Promise)
local Structure = require(script.Parent.Structure)
local ProfileService = require(ReplicatedStorage.Packages.ProfileService)
local ReplicaService = require(ReplicatedStorage.Packages.ReplicaService)
local Profiles, Replicas = {}, {}
local ProfileClassToken = ReplicaService.NewClassToken("Data")
local ProfileStore = ProfileService.GetProfileStore("Data", Structure.UserData)
local DataService = Knit.CreateService {
Name = "DataService"
}
local function PlayerAdded(Player: Player)
local StartTime = tick()
local Profile = ProfileStore:LoadProfileAsync("Player_" .. Player.UserId)
if Profile then
Profile:AddUserId(Player.UserId)
Profile:Reconcile()
Profile:ListenToRelease(function()
Profiles[Player] = nil
Replicas[Player]:Destroy()
Replicas[Player] = nil
Player:Kick("DataService: Profile was released.")
end)
if Player:IsDescendantOf(Players) then
Profiles[Player] = Profile
local Replica = ReplicaService.NewReplica {
ClassToken = ProfileClassToken,
Tags = {["Player"] = Player},
Data = Profile.Data,
Replication = "All"
}
Replicas[Player] = Replica
warn(Player.Name .. "'s profile has been loaded. ".. "(" .. string.sub(tostring(tick() - StartTime), 1, 5) .. ")")
else
Profile:Release()
end
else
Player:Kick("DataService: Profile is nil.")
end
end
local function PlayerProfileAsync(Player: Player): {}
local Profile = Profiles[Player]
while Profile == nil and Player:IsDescendantOf(Players) do
task.wait()
Profile = Profiles[Player]
end
return Profile
end
local function GrantProduct(Player: Player, ID: number)
local Profile = Profiles[Player]
local ProductFunction = Structure.Products[ID]
if ProductFunction ~= nil then
ProductFunction(Profile)
else
warn("DataService: ProductId " .. tostring(ID) .. " has not been defined in the 'Products' table.")
end
end
function PurchaseAsync(Profile, ID: number, Callback): EnumItem
if not Profile:IsActive() then
return Enum.ProductPurchaseDecision.NotProcessedYet
else
local MetaData = Profile.MetaData
local PurchaseIDs = MetaData.MetaTags.ProfilePurchaseIds
if PurchaseIDs == nil then
PurchaseIDs = {}
MetaData.MetaTags.ProfilePurchaseIds = PurchaseIDs
end
if table.find(PurchaseIDs, ID) == nil then
while #PurchaseIDs >= Structure.PurchaseIDLog do
table.remove(PurchaseIDs, 1)
end
table.insert(PurchaseIDs, ID)
task.spawn(Callback)
end
local Result = nil
local function LatestMetaTag()
local SavedPurchaseIds = MetaData.MetaTagsLatest.ProfilePurchaseIds
if SavedPurchaseIds ~= nil and table.find(SavedPurchaseIds, ID) ~= nil then
Result = Enum.ProductPurchaseDecision.PurchaseGranted
end
end
LatestMetaTag()
local MetaTagsConnection = Profile.MetaTagsUpdated:Connect(function()
LatestMetaTag()
if not Profile:IsActive() and Result == nil then
Result = Enum.ProductPurchaseDecision.NotProcessedYet
end
end)
while Result == nil do
task.wait()
end
MetaTagsConnection:Disconnect()
return Result
end
end
local function ProcessReceipt(Info: Receipt): EnumItem
local Player = Players:GetPlayerByUserId(Info.PlayerId)
if Player == nil then
return Enum.ProductPurchaseDecision.NotProcessedYet
end
local Profile = PlayerProfileAsync(Player)
if Profile ~= nil then
return PurchaseAsync(
Profile,
Info.PurchaseId,
function()
GrantProduct(Player, Info.ProductId)
end
)
else
return Enum.ProductPurchaseDecision.NotProcessedYet
end
end
function DataService:GetReplica(Player: Player)
return Promise.new(function(Resolve, Reject)
if typeof(Player) == "Instance" and not Player:IsDescendantOf(Players) then
error("DataService: Value passed is not a player.")
end
if not Profiles[Player] or not Replicas[Player] then
repeat
if Player then
task.wait()
else
Reject("DataService: Player has left the game.")
end
until Profiles[Player] and Replicas[Player]
end
local Profile, Replica = Profiles[Player], Replicas[Player]
if Profile and Profile:IsActive() then
if Replica and Replica:IsActive() then
Resolve(Replica)
else
Reject("DataService: Replica did not exist or was not active.")
end
else
Reject("DataService: Profile did not exist or was not active.")
end
end)
end
function DataService:KnitInit()
for _, Player in Players:GetPlayers() do
task.spawn(PlayerAdded, Player)
end
MarketPlaceService.ProcessReceipt = ProcessReceipt
Players.PlayerAdded:Connect(PlayerAdded)
Players.PlayerRemoving:Connect(function(Player)
if Profiles[Player] then
Profiles[Player]:Release()
end
end)
end
return DataService