Because Data table is nil I think.
That doesn’t make sense though. When you assign a variable a specified integer, printing it returns the integer back regardless if it has value or not.
local test = 0
print(test)
--Output prints 0
I’m very frustrated and I’m not sure how to go about this.
Can you show the Datamanager module’s :Get()
Method?
It looks like It is returning nil.
local Players = game:GetService("Players")
local ProfileService = require(game.ReplicatedStorage.ProfileService)
local ProfileStore = ProfileService.GetProfileStore(
"Player",
{
coins = 0;
gems = 0;
}
)
local Profiles = {}
local function onPlayerAdded(player)
local profile = ProfileStore:LoadProfileAsync(
"Player_" .. player.UserId,
"ForceLoad"
)
if profile then
profile:ListenToRelease(function()
Profiles[player] = nil
player:Kick()
end)
if player:IsDescendantOf(Players) then
Profiles[player] = profile
else
profile:Release()
end
else
player:Kick()
end
end
local function onPlayerRemoving(player)
local profile = Profiles[player]
if profile then
profile:Release()
end
end
Players.PlayerAdded:Connect(onPlayerAdded)
Players.PlayerRemoving:Connect(onPlayerRemoving)
local DataManager = {}
--:Get() Method
function DataManager:Get(player)
local profile = Profiles[player]
if profile then
return profile
end
end
return DataManager
You should add this piece of code before playerAdded event:
for _,i in pairs(Players:GetPlayers()) do
coroutine.wrap(onPlayerAdded)()
end
We do this because sometime the script misses the playerAdded event.
final script:
local Players = game:GetService("Players")
local ProfileService = require(game.ReplicatedStorage.ProfileService)
local ProfileStore = ProfileService.GetProfileStore(
"Player",
{
coins = 0;
gems = 0;
}
)
local Profiles = {}
local function onPlayerAdded(player)
local profile = ProfileStore:LoadProfileAsync(
"Player_" .. player.UserId,
"ForceLoad"
)
if profile then
profile:ListenToRelease(function()
Profiles[player] = nil
player:Kick()
end)
if player:IsDescendantOf(Players) then
Profiles[player] = profile
else
profile:Release()
end
else
player:Kick()
end
end
local function onPlayerRemoving(player)
local profile = Profiles[player]
if profile then
profile:Release()
end
end
for _,i in pairs(Players:GetPlayers()) do
coroutine.wrap(onPlayerAdded)()
end
Players.PlayerAdded:Connect(onPlayerAdded)
Players.PlayerRemoving:Connect(onPlayerRemoving)
local DataManager = {}
--:Get() Method
function DataManager:Get(player)
local profile = Profiles[player]
if profile then
return profile
end
end
return DataManager
Unfortunately, even with this added, it still gives me the same error regarding to concatenating string with nil.
Sorry i made a mistake in it I saw now here’s thee edited snippet
for _,player in pairs(Players:GetPlayers()) do
coroutine.wrap(onPlayerAdded)(player)
end
I still have the same result.
Though, I was doing some research and apparently searching inside of an array in a for
loop will not work and return the elements inside the array as nil. Of course, this is just me speculating if this is the actual core problem.
Is there anyway I can define the player in the ServerScript without using a for
loop?
Here is my script If it helps:
-- Stats Service
-- Beastcraft_Gaming
-- January 6, 2021
local DatastoreService: AeroService = {Client = {}}
local Players = game:GetService("Players")
local ProfileService
DatastoreService.Profiles = {}
local ProfileTemplate: table = {
["Cash"] = 100,
["Exp"] = 0,
["Level"] = 0
}
function DatastoreService:Start()
local ProfileStore = ProfileService.GetProfileStore(
"Player_Stats",
ProfileTemplate
)
local function PlayerAdded(player)
local profile = ProfileStore:LoadProfileAsync(
"Player_Stats_"..player.UserId,
"ForceLoad"
)
if (profile ~= nil) then
profile:Reconcile()
profile:ListenToRelease(function()
DatastoreService.Profiles[player] = nil
player:Kick()
end)
if (player:IsDescendantOf(Players) == true) then
DatastoreService.Profiles[player] = profile
--self.Services.PlayerData.LeaderBoardService:MakeLeaderBoard(player, {["Cash"] = profile.Data.Cash, ["Exp"] = profile.Data.Exp, ['Level'] = profile.Data.Level})
self:Fire("leaderstats", player, {["Cash"] = profile.Data.Cash, ["Exp"] = profile.Data.Exp, ['Level'] = profile.Data.Level})
else
profile:Release()
end
else
player:Kick("There was a problem while loading your Data please rejoin")
end
end
for _,player in pairs(Players:GetPlayers()) do
coroutine.wrap(PlayerAdded)(player)
end
Players.PlayerAdded:Connect(PlayerAdded)
Players.PlayerRemoving:Connect(function(player)
local profile = DatastoreService.Profiles[player]
if (profile ~= nil) then
profile:Release()
end
end)
end
function DatastoreService:Init()
ProfileService = self.Modules.ProfileService
self:RegisterEvent("leaderstats")
self:RegisterEvent("UpdateLeaderstats")
end
return DatastoreService
I use AeroGameFramework If some lines confuses you.
This actually inspired me to go research other tutorials of how people set up ProfileService. (To be specific @EncodedLua.)
His tutorial helped out a lot and I all seems to be working great. Can’t wait to tinker with it more!
I’d love to see a data backup implementation with this. This is an awesome module!
I’ve been playing around with this module for a while and I have a follow up question.
I’m interested in the idea of creating a vender where you can add more to your currency, either coins or gems. The wiki for this module clearly states:
ProfileService is supposed to be a ModuleScript which you can place inside your Roblox game’s ServerScriptService or wherever else is preferred. ProfileService can only be used server-side
In a hypothetical scenario, what are the repercussions of me placing this module in ReplicatedStorage (rather than ServerScriptService) and attempting to require the module in this path to increase the client’s data? Should I resort to using remote events (and leave the module in the ServerScriptService)?
If so, how would I even approach this?
That sounds like you’re relying too much on the client, and you shouldn’t do that since exploiters would be able to modify their own data and cheat. All of the code for managing data should be on the server-side.
How else can you wheel that information to the client without using a localscript? (Which is pretty much the only way since you can only use local scripts for textbuttons/guis and not serverscripts.)
You can send a RemoteEvent everytime a variable for the player changes, or use a ValueObject parented to the player, but if you use ValueObjects then exploiters can see other player’s data.
ReplicaService is the best way to do this though.
This module cannot be required on a client script as It relies on datastorservice and datastoreservice only works on a server script
Is it possible to convert ProfileService data back to just normal datastores?
Like if I wanted to stop using ProfileService and wanted to convert back to how normally people save data.
Also I’m not switching, just wanted to know if there’s a way incase it was needed.
A rough way of doing this is storing the profile’s .Data when it is loaded, releasing it and clearing the profile, and then calling WipeProfileAsync on the datastore key. From there you can either overwrite the key in the same datastore or just transfer to a completely new one, and make a new key in it with the data you retrieved from the profile.
How frequent can I call GlobalUpdateProfileAsync
before expecting any problems?
Hey, how can I save the add a value to another player in the script?