Hi there, I made a new playerClass before implementing the profileservice and I am trying to work around it now. Right now this is the profile class
local module = {}
local template = {
Name = "";
leaderstats = {
Spins = 0;
};
Upgrades = {
IncrementSpeed = 1
};
plr = nil;
GottenUgc = {};
LuckBoost = 0;
}
module.__index = module
function module.newWithData(plr,data)
local metaTable = setmetatable(data,module)
metaTable.Data.Name = plr.Name
metaTable.Data.plr = plr
return metaTable
end
--function module.New(plr)
-- local data = setmetatable(template,module)
-- data.Name = plr.Name
-- data.plr = plr
-- return data
--end
function module:MakeLeaderstats(plr)
local leaderstats = Instance.new("Folder")
leaderstats.Parent = plr
leaderstats.Name = "leaderstats"
local spins = Instance.new("IntValue")
spins.Parent = leaderstats
spins.Name = "Spins"
spins.Value = 0
end
function module:UpdateSpins()
print(self)
local leaderstats = self.Data.plr.leaderstats
leaderstats.Spins.Value += 1
self.Data.leaderstats.Spins += 1
end
function module:CanSpin() : boolean
print(self)
if self.Data.leaderstats.Spins - 2 >= 0 then
print("User has adequate spins")
return true
end
print("User is poor")
return false
end
function module:HasUgc(id)
return table.find(self.Data.GottenUgc,id)
end
function module:ClaimedUgc(id)
print("find index of ugc and destroy it :))")
local index = table.find(self.GottenUgc,id)
table.remove(self.Data.GottenUgc,index)
end
function module:GotUGC(id)
table.insert(self.Data.GottenUgc,id)
end
function module:SpendSpins()
if self.Data.leaderstats.Spins - 2 >= 0 then
self.Data.leaderstats.Spins -= 2
self.Data.plr.leaderstats.Spins.Value = self.Data.leaderstats.Spins
end
end
function module:GiveCustomSpins(number)
self.Data.leaderstats.Spins += number
self.Data.plr.leaderstats.Spins.Value += number
end
function module:GetSpins()
return self.Data.leaderstats.Spins
end
function module:giveSpin()
local spins = self:GetSpins()
spins += 1;
self:UpdateSpins()
end
return module
The thing is now that when I try to do profile:Release after I fetch it in another module that holds all of these profiles, I get an error
local playerClass = require(script.playerClass)
local profileService = require(script.ProfileService)
local plrs = game:GetService("Players")
local dataStoreKey = "someKey"
local template = {
Name = "";
leaderstats = {
Spins = 0;
};
Upgrades = {
IncrementSpeed = 1
};
plr = nil;
GottenUgc = {};
LuckBoost = 0;
}
local playerStore = profileService.GetProfileStore(
dataStoreKey, template
)
local players = {
-- [PlayerName] = {} this is the template
}
local profiles = {
}
function players.makePlot(plr)
local unclaimed = workspace.Unclaimed:GetChildren()
local playersPlot = unclaimed[1]
unclaimed[1].Name = plr.Name
unclaimed[1].Parent = workspace.Claimed
return playersPlot
end
function players.addPlayer(plr : Player)
local profile = playerStore:LoadProfileAsync("Player_"..plr.UserId)
local existingPlrData
if profile ~= nil then
profile:AddUserId(plr.UserId)
profile:Reconcile()
profile:ListenToRelease(function()
players[plr] = nil
profiles[plr] = nil
plr:Kick()
end)
if plr:IsDescendantOf(plrs) == true then
existingPlrData = playerClass.newWithData(plr,profile)
players[plr.Name] = existingPlrData
profiles[plr] = profile
else
profile:Release()
end
else
plr:Kick()
end
existingPlrData:MakeLeaderstats(plr)
return existingPlrData
end
function players.fetchData(plr)
local plrData = players[plr.Name]
return plrData
end
function players.fetchProfile(plr)
return profiles[plr]
end
game.Players.PlayerRemoving:Connect(function(plr : Player)
local profile = profiles[plr]
print(profile)
if profile ~= nil then
profile:Release()
end
end)
return players
The release function is in profileservice, i require it in the second script in the top and load a profile using LoadProfileAsync which provides the :Release() function
Do you use table.clear, or set all table keys to nil during or after execution?
It seems the profile isn’t being properly cleaned up (as in removed from cache, at least that’s what I’m assuming) when released.
Also ensure your metatable is being set up properly, as that’s also a pretty good way to get this exact error.
Double also, make sure you’re using table.clone when creating new data tables, as using the same one will cause the original reference to it to get cleaned up, cleaning up future references.
But this is how I should do it as per the documentation.
Also ensure your metatable is being set up properly, as that’s also a pretty good way to get this exact error.
It used to work, but what I am guessing is that now the profile-service functions are being cleared up and replaced, not sure thought.
Double also, make sure you’re using table.clone when creating new data tables, as using the same one will cause the original reference to it to get cleaned up, cleaning up future references.
function module.newWithData(plr,data)
local metaTable = setmetatable(data,module)
metaTable.Data.Name = plr.Name
metaTable.Data.plr = plr
return metaTable
end
uses the data variable to set up a new metatable. What does this data table reference to, and is the same exact value reused every time this function is called? In the script you provided, I can see where it’s called, but not enough information is given to see what the value is exactly.
Ahh, that’s what you mean. The data variable is the profile that was just loaded
In the second script I sent in the original post I had this block
if plr:IsDescendantOf(plrs) == true then
existingPlrData = playerClass.newWithData(plr,profile)
players[plr.Name] = existingPlrData
profiles[plr] = profile
else
The profile (which is then named data in the function) is this:
local profile = playerStore:LoadProfileAsync("Player_"..plr.UserId)
So I just set a metatable of the profile with all the functions with the module to have all those functions too since they are pretty important, the functions of the module work but it seems that when the user is removed from the server, the profileServices functions are lost
Edit: ProfileService uses the template I provide it to create the user data and then in the documentation it adds the profile to a table, but I want the profile to also have the functions of the playerClass so I make a new class with playerClass.New(plr,profile) and make a metatable. All works well until playerRemoving which then, profile, loses all the functions (or at least :Release() and :IsActive()
Try using table.clone and see what that yields you.
function module.newWithData(plr,data)
local metaTable = setmetatable(table.clone(data),module)
metaTable.Data.Name = plr.Name
metaTable.Data.plr = plr
return metaTable
end
You can also use this DeepCopy function, as table.clone is just a shallow copy, for a more complete table copy with isolated sub-tables too.
local function DeepCopy(t: {any})
local Tables = {}
local function Main(t: {any})
local Copy
if (typeof(t) == "table") then
local Value = Tables[t]
if (Value ~= nil) then
--warn(`[Table] Potential C stack overflow! ({tostring(t)})`)
Copy = Value
else
Copy = {}
Tables[t] = Copy
for i, v in next, t do
Copy[Main(i)] = Main(v)
end
end
elseif (typeof(t) == "userdata") then
Copy = newproxy(true)
local Metatable = getmetatable(t)
if (typeof(Metatable) == "table") then
setmetatable(Copy, Metatable)
else
warn(`[Table] Issue copying userdata: metatable.__metatable is set!`)
end
else
Copy = t
end
return Copy
end
local Result = Main(t)
table.clear(Tables)
Tables = nil
return Result
end
Doing the table.clone() almost crashed studio and I got these errors:
DataStoreService: CantStoreValue: Cannot store Dictionary in data store. Data stores can only accept valid UTF-8 characters. API: UpdateAsync, Data Store: _playerStore_ - Studio
[ProfileService]: DataStore API error [Store:"_playerStore_";Key:"Player_134551117"] - "104: Cannot store Dictionary in data store. Data stores can only accept valid UTF-8 characters."
[ProfileService]: Entered critical state
I’m going to need more information (regarding ProfileService) in order to help you. Without the complete method run-down behind the :Release() function, I cannot help you.
There are function calls within the Release function. The IsActive function can be ignored, but I need a copy of the SaveProfileAsync function.
The issue is that the previous metatable of profile (data param) is overwritten with module.
local t = setmetatable({}, {__index = function(self, k) return "original" end})
print(t.key) --> original
setmetatable(t, {})
print(t.key) --> nil
Personally I rather keep the metatable and have profile be a member of the object instead of being the object itself, but you can also take the other paths.
If the metatable of profile is only class Profile, you could set playerClass to inherit from Profile through a chain of two metatables.
But I want the player class to get the player’s data, not the whole ProfileService. in the profile, it also contains the user’s data, ProfileService doesn’t.
Personally I rather keep the metatable and have profile be a member of the object instead of being the object itself, but you can also take the other paths.
I just realized also that it isn’t even the problem of the metatable. I am keeping a separate folder with the profiles, because I thought it would be good idea to keep those separate. So I am just putting the profile in the profiles table. So it shouldn’t get overridden by anything.
Ohhh you’re using the ProfileService from Mad Studio, that makes so much more sense my bad
The segment in the second script provided,
if plr:IsDescendantOf(plrs) == true then
existingPlrData = playerClass.newWithData(plr,profile)
players[plr.Name] = existingPlrData
profiles[plr] = profile
else
profile:Release()
end
is conflicting with the PlayerRemoving connection at the bottom,
game.Players.PlayerRemoving:Connect(function(plr : Player)
local profile = profiles[plr]
print(profile)
if profile ~= nil then
profile:Release()
end
end)
I’m pretty sure the ListenToRelease uses BindableEvents, which by default assumes Deferred thread behavior, aka task.defer.
It’s best to just clean the variables on the listener and when it’s called (and should take immediate action), so…
if plr:IsDescendantOf(plrs) == true then
existingPlrData = playerClass.newWithData(plr,profile)
players[plr.Name] = existingPlrData
profiles[plr] = profile
else
players[plr] = nil
profiles[plr] = nil
profile:Release()
end
You can also set Workspace.SignalBehavior to Immediate to eliminate all root causes of this particular issue for all instances of :Release().
Ohhh you’re using the ProfileService from Mad Studio, that makes so much more sense my bad
Haha, yeah sorry for not clarifying.
I’m pretty sure the ListenToRelease uses BindableEvents, which by default assumes Deferred thread behavior, aka task.defer .
It’s best to just clean the variables on the listener and when it’s called (and should take immediate action), so…
This fixed my :Release method error not existing, but now I am having an issue with IsActive()
ServerScriptService.playerModule.ProfileService:2399: attempt to call missing method 'IsActive' of table