Hello, nothing much to say, but I have a wrapper for ProfileService that I use for most of my game and I want to know how I can optimize it for the code to be more efficient. There are no problems with the code, I just want it to optimize it (if I can).
Code:
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local ProfileService = require(script.ProfileService)
local ReplicaService = require(script.ReplicaService)
local Signal = require(script.Signal)
local Config = require(script.Config)
local ProfileRemoving = require(script.ProfileRemoving)
local ProfileServicePlus = {
ProfileAdded = Signal.new(),
ProfileRemoving = ProfileRemoving.Signal,
Initialized = false,
}
local DEBUG_MODE = true
local Global_Update_Types = {}
local Loaded_Profiles = {}
local Profile_Replicas = {}
local Loaded_Profile_Stores = {}
local function debugPrint(message)
if DEBUG_MODE then
print(message)
end
end
local function debugWarn(message)
if DEBUG_MODE then
warn(message)
end
end
local function handleLockedUpdate(profile: table, update)
local globalUpdates = profile.GlobalUpdates
debugWarn("[PSPlus]: New locked update added")
local updateID = update[1]
local updateData = update[2]
local listener = Global_Update_Types[updateData.Type]
if listener ~= nil then
task.spawn(listener, profile, updateData)
else
debugWarn(("[PSPlus]: No listener found for update %s!"):format(updateID))
end
globalUpdates:ClearLockedUpdate(updateID)
debugWarn("[PSPlus]: Cleared locked update")
end
local function handleGlobalUpdates(profile)
local globalUpdates = profile.GlobalUpdates
for i, update in pairs(globalUpdates:GetActiveUpdates()) do
globalUpdates:LockActiveUpdate(update[1])
end
for i, lockedUpdate in pairs(globalUpdates:GetLockedUpdates()) do
handleLockedUpdate(profile, lockedUpdate)
end
globalUpdates:ListenToNewActiveUpdate(function(updateID, updateData)
globalUpdates:LockActiveUpdate(updateID)
end)
globalUpdates:ListenToNewLockedUpdate(function(updateID, updateData)
handleLockedUpdate(profile, {updateID, updateData})
end)
end
local function getDefaultKey(player: string, profileStoreKey: string): string
local playerKey = profileStoreKey.. "-%s"
return playerKey:format(tostring(player.UserId))
end
local function getPlayerkey(player: string, profileStore: table)
local playerKey
local profileKey = profileStore._Key
local stringStart, stringEnd = string.find(profileKey, "UserId")
local keyStart = string.sub(profileKey, 0, stringStart - 1)
local keyEnd = string.sub(profileKey, stringEnd + 1, #profileKey)
playerKey = keyStart.. player.UserId.. keyEnd
return playerKey
end
local function loadPlayerProfile(player, storeKey, not_released_handler)
local loadedProfileStore = Loaded_Profile_Stores[storeKey]
local data = Config.GAME_PROFILE_TEMPLATES.PLAYER_PROFILES[storeKey]
local playerKey
if data._Key ~= nil then
playerKey = getPlayerkey(player, data)
else
playerKey = getDefaultKey(player, storeKey)
end
local playerProfile = loadedProfileStore:LoadProfileAsync(
playerKey,
not_released_handler or Config.DEFAULT_NOT_RELEASED_HANDLER
)
if playerProfile then
playerProfile:Reconcile()
playerProfile:ListenToRelease(function()
player:Kick("Profile could've loaded on another Roblox server")
end)
if player:IsDescendantOf(Players) then
Loaded_Profiles[storeKey][playerKey] = playerProfile
playerProfile.Player = player
playerProfile.DataChanged = Signal.new()
playerProfile.ProfileKey = playerKey
handleGlobalUpdates(playerProfile)
local replica = ReplicaService.NewReplica({
ClassToken = ReplicaService.NewClassToken(playerKey),
Tags = {Player = player},
Data = playerProfile.Data,
Replication = "All",
})
function playerProfile:SetData(dataToSet, newData)
local oldData = playerProfile.Data[dataToSet]
if typeof(oldData) ~= typeof(newData) then return end
playerProfile.Data[dataToSet] = newData
replica:SetValue({dataToSet}, newData)
playerProfile.DataChanged:Fire(dataToSet, newData)
end
Profile_Replicas[data._Key] = replica
ProfileServicePlus.ProfileAdded:Fire(playerProfile, loadedProfileStore)
return playerProfile, playerKey
else
debugPrint("[PSPlus]: Player left before profile loaded, releasing profile")
playerProfile:release()
end
else
player:Kick("Profile could not load because other Roblox servers were trying to load it")
end
end
local function loadNonPlayerProfile(storeKey, not_released_handler)
local loadedProfileStore = Loaded_Profile_Stores[storeKey]
local data = Config.GAME_PROFILE_TEMPLATES.NON_PLAYER_PROFILES[storeKey]
local profile = loadedProfileStore:LoadProfileAsync(
data._Key,
not_released_handler or Config.DEFAULT_NOT_RELEASED_HANDLER
)
if profile then
profile:Reconcile()
profile:ListenToRelease(function()
warn("Profile coud have loaded on another Roblox server")
end)
Loaded_Profiles[storeKey][data._Key] = profile
profile.DataChanged = Signal.new()
profile.ProfileKey = data._Key
handleGlobalUpdates(profile)
local replica = ReplicaService.NewReplica({
ClassToken = ReplicaService.NewClassToken(data._Key),
Tags = {},
Data = profile.Data,
Replication = "All",
})
function profile:SetData(dataToSet, newData)
local oldData = profile.Data[dataToSet]
profile.Data[dataToSet] = newData
replica:SetValue({dataToSet}, newData)
profile.DataChanged:Fire(dataToSet, newData)
end
Profile_Replicas[data._Key] = replica
ProfileServicePlus.ProfileAdded:Fire(profile, loadedProfileStore)
return profile
end
end
local function onPlayerAdded(player)
local total = 0
local loaded = 0
for _, _ in pairs(Config.GAME_PROFILE_TEMPLATES.PLAYER_PROFILES) do
total += 1
end
for storeKey, data in pairs(Config.GAME_PROFILE_TEMPLATES.PLAYER_PROFILES) do
if data._LoadOnJoin == false then
continue
end
local playerProfile, profileKey = loadPlayerProfile(player, storeKey)
if playerProfile then
Loaded_Profiles[storeKey][profileKey] = playerProfile
loaded += 1
end
end
debugPrint(("[PSPlus]: Successfully loaded %s/%s profiles"):format(loaded, total))
end
local function onPlayerRemoving(player)
for storeKey, profileStore in pairs(Loaded_Profiles) do
local data = Config.GAME_PROFILE_TEMPLATES.PLAYER_PROFILES[storeKey]
local profileKey = getPlayerkey(player, data)
local playerProfile = profileStore[profileKey]
if playerProfile ~= nil then
playerProfile:Release()
end
end
end
local function Init()
ProfileServicePlus.Initialized = true
for _, template in pairs(Config.GAME_PROFILE_TEMPLATES) do
for storeKey, data in pairs(template) do
Loaded_Profiles[storeKey] = {}
Loaded_Profile_Stores[storeKey] = ProfileService.GetProfileStore(
storeKey,
data
)
Loaded_Profile_Stores[storeKey].Key = storeKey
end
end
for _, player in ipairs(Players:GetPlayers()) do
task.spawn(onPlayerAdded, player)
end
Players.PlayerAdded:Connect(onPlayerAdded)
Players.PlayerRemoving:Connect(onPlayerRemoving)
print("[PSPlus]: Initialized ProfileServicePlus")
end
if ProfileServicePlus.Initialized == false then
Init()
end
function ProfileServicePlus:AddGlobalUpdateType(updateType, listener, overwriteExisting)
if overwriteExisting then
Global_Update_Types[updateType] = listener
else
if Global_Update_Types[updateType] ~= nil then
warn(("[PSPlus]: Global update type %s already exists!"):format(updateType))
return
end
Global_Update_Types[updateType] = listener
end
end
function ProfileServicePlus:GetPlayerProfileKey(storeKey: string, player: Instance)
local profileStore = Config.GAME_PROFILE_TEMPLATES.PLAYER_PROFILES[storeKey]
return getPlayerkey(player, profileStore)
end
function ProfileServicePlus:GetProfileStore(storeKey: string)
local profileStore = Loaded_Profile_Stores[storeKey]
return profileStore
end
function ProfileServicePlus:GetProfile(profileKey, storeKey)
local profile = Loaded_Profiles[storeKey][profileKey]
return profile
end
function ProfileServicePlus:WaitForProfileAsync(storeKey, profileKey, timeout)
local looping = true
timeout = timeout or 20
task.delay(timeout, function()
looping = false
if Loaded_Profiles[storeKey][profileKey] == nil then
warn(("Infinite yield possible on Loaded_Profiles[%s][%s]"):format(storeKey, profileKey))
end
end)
while looping and Loaded_Profiles[storeKey][profileKey] == nil do
task.wait()
end
return Loaded_Profiles[storeKey][profileKey]
end
function ProfileServicePlus:GetReplicaOfProfile(profile)
return Profile_Replicas[profile.ProfileKey]
end
function ProfileServicePlus:LoadProfileAsync(storeKey, not_released_handler, player)
if player then
return loadPlayerProfile(player, storeKey, not_released_handler)
end
return loadNonPlayerProfile(storeKey, not_released_handler)
end
function ProfileServicePlus:GetProfiles(profileStoreKey)
local profiles = Loaded_Profiles[profileStoreKey]
return profiles or Loaded_Profiles
end
return ProfileServicePlus