Hi everyone, I just recently was trying to optimize my player data storage for a project that i’m working on and I ran into a slight problem. I’ll try my best to explain what that problem is down below.
Alright, so how I have it set up in my game is that when the player joins the game, the server fetches their player data from the datastore, and then calls the module script and adds their data to a table within the module script. This here is how I do that:
--Server
local ServerData = require(game:getService("ServerScriptService"):WaitForChild("GlobalDataHandler"))
game.Players.PlayerAdded:connect(function(player)
local playerData = PlayerDatastore:GetAsync(player.UserId) or 0
if playerData == 0 then
print(player.Name .. " is new!! Setting up inventory!")
PlayerDatastore:SetAsync(player.UserId,NewTemplate)
playerData = NewTemplate
else
local ServerDataTemplate =
{
PlayerID = player.UserId,
PlayerData = playerData
}
ServerData:AddData(ServerDataTemplate)
end
end)
Now where exactly the issue begins is when I update the playerData value using :UpdataData(), it adds to every single players data in the server, which is quite confusing because when :UpdateData() is called it only prints out a single player name instead of multiple.
--ModuleScript
local Module = {}
ServerPlayerData = {}
function Module.UpdateData(player,newData)
print(player.Name)
for ServerPlayerDataIndex,PlayerDataEntry in pairs(ServerPlayerData) do
if PlayerDataEntry.PlayerID == player.UserId then
PlayerDataEntry.PlayerData = newData
LocalDataRemote:FireClient(player,PlayerDataEntry.PlayerData)
break
end
end
end
function Module.AddData(playerDataTemplate)
local player = game:GetService("Players"):GetPlayerByUserId(playerDataTemplate.PlayerID)
if player == nil then print("Player was not found, could not add data.") return end
table.insert(ServerPlayerData,playerDataTemplate)
LocalDataRemote:FireClient(player,playerDataTemplate.PlayerData)
end
function Module.RemoveData(player)
for ServerPlayerDataIndex,PlayerDataEntry in pairs(ServerPlayerData) do
if PlayerDataEntry.PlayerID == player.UserId then
table.remove(ServerPlayerData,ServerPlayerDataIndex)
end
end
end
function Module.RequestData(player)
if player ==nil then print("No player") return end
for ServerPlayerDataIndex,PlayerDataEntry in pairs(ServerPlayerData) do
if PlayerDataEntry.PlayerID == player.UserId then
return PlayerDataEntry.PlayerData
end
end
end
return Module
This is my first time using ModuleScripts for anything, so i’m not quite sure if i’m using this in the correct way, so if anyone could tell me what my error is in this, I would greatly appreciate it!
local t = {}
t.name = "test"
t.func = function(self)
print(self.name)
end
--t:func() == t.func(t)
When using the colon syntax on a method in a table, the first parameter
is the table. Because you are calling The function AddData with a colon, the first argument is the module table and not ServerPlayerData. All you need to do is
You are mixing up the colon and the dot syntax, for the dot syntax, the first parameter needs to be self if you are using self, but in the colon, the self parameter is done automatically
Ya I didn’t know if there was a difference between the colon and the dot, so thanks for that information guys. I’m actually getting the same error though that I had before, where it just adds data to every player. (BTW, I added more code to the original post so we’re on the same page, my bad on not doing it sooner)
Here is an example of me calling the RequestData method within the module and then UpdateData:
--Server
ClickDetector = script.Parent.ClickDetector
ServerData = require(game:GetService("ServerScriptService"):WaitForChild("GlobalDataHandler"))
valueBeingAdded= "blah"
ClickDetector.MouseClick:connect(function(player)
local testData = ServerData:RequestData(player)
if testData == nil then return end
local stringTable = testData.stringTable
table.insert(stringTable,valueBeingAdded)
ServerData.UpdateData(player,testData)
end)
I don’t see any problem with this, maybe try changing :connect to :Connect(with the capital ‘C’) cause :connect is deprecated, so maybe that’s the problem, I don’t think it is but it’s worth a try
The only solution I could think of, it discontinue the use of a loop, and then, turn the array into a dictionary, so, the key will be the player user ID and the table will store the data, so, the AddData method will look like
function AddData(plr, playerDataTemplate)
local player = game:GetService("Players"):GetPlayerByUserId(playerDataTemplate.PlayerID)
if player == nil then print("Player was not found, could not add data.") return end
ServerPlayerData[plr.UserId] = playerDataTemplate
LocalDataRemote:FireClient(player,playerDataTemplate)
end
and on your lines where you do loops, do ServerPlayerData[plr.UserId] instead, so you UpdateData` method will look like
function Module.UpdateData(player,newData)
print(player.Name)
ServerPlayerData[player.UserId] = newData
LocalDataRemote:FireClient(player,newData)
end
on you PlayerAdded, it should look like:
game.Players.PlayerAdded:Connect(function(player)
local playerData = PlayerDatastore:GetAsync(player.UserId) or 0
if playerData == 0 then
print(player.Name .. " is new!! Setting up inventory!")
PlayerDatastore:SetAsync(player.UserId,NewTemplate)
playerData = NewTemplate
else
local ServerDataTemplate = playerData
ServerData:AddData(player, ServerDataTemplate)
end
end)
Caught your issue, you are using the dot syntax to define your functions but you are using the colon syntax to call it(specifically, the AddData method), you are passing in the table the method is in as the first parameter in the colon syntax, you will have to call all your methods with the dot syntax, cause that is how you defined them in the module. Sorry for the late answer.
Edit: same thing for the RequestData method
So how exactly would that cause the issue of adding to the table of every player in the server data? I have them correctly switched to their respective colon/dot methods in my code, so I don’t think that’s quite it. I think what’s happened is it’s just different there because I switched them over because of what that other guy (as well as yourself)had said and posted the code with that edit in there.
I will tell you, when doing the colon operator, the table is automaticly passed in, here is an example
local module = {}
-- defining the method
function module.printMessage(self, message) -- if you don't pass self, the colon operator does not work the expected way
print(message)
end
-- is exactly the same as
function module:printMessage(message) -- as you can see, "self" is a hidden parameter in the colon operator
print(message)
end
-- calling the method
module.printMessage(module, "hi") -- we have to pass the table the method is in (self) when calling with the dot
-- is exactly the same as
module:printMessage("hi") -- "self" is passed in automatically when using the colon weather you need it or not
edit: you are setting the methods with the dot, but calling them with the colon, you aren’t doing this in the module script, but in the script requiring the module script is calling the methods wrong
I suggest using Dictionaries when holding player data in ServerPlayerData. As it’s faster (in a large majority of cases) to look for player data, edit, and just overall easier to implement.
local ServerPlayerData = {}
function Module:AddData(playerDataTemplate)
ServerPlayerData[playerDataTemplate.PlayerId] = playerDataTemplate.PlayerData
end
function Module:RemoveData(player)
ServerPlayerData[player.UserId] = nil
end
function Module:UpdateData(player, newData)
ServerPlayerData[player.UserId] = newData
end
function Module.RequestData(player)
if player == nil then
print("No player")
return
else
return ServerPlayerData[player.UserId]
end
end
The table here has a key of the userId and it’s value is the player’s data.
For getting data it’s simply: ServerPlayerData[player.UserId]
For setting data it’s: ServerPlayerData[player.UserId] = someValue
Right, I get that there is the passing of self and all that when using different characters within the methods, but how does that relate to the problem that i’m having? I’m passing in a dictionary when I write the data to ServerPlayerData, and that’s giving the correct data regardless so I don’t quite understand why it would even matter if it’s sending self or not?
Ok, I will show you the problems in the script if you don’t understand, so, the problem is in the server script
-- main script
local ServerData = require(game:getService("ServerScriptService"):WaitForChild("GlobalDataHandler"))
game.Players.PlayerAdded:connect(function(player)
local playerData = PlayerDatastore:GetAsync(player.UserId) or 0
if playerData == 0 then
print(player.Name .. " is new!! Setting up inventory!")
PlayerDatastore:SetAsync(player.UserId,NewTemplate)
playerData = NewTemplate
else
local ServerDataTemplate =
{
PlayerID = player.UserId,
PlayerData = playerData
}
ServerData:AddData(ServerDataTemplate) -- should be "ServerData.AddData(ServerDataTemplate)"
end
end)
ClickDetector = script.Parent.ClickDetector
ServerData = require(game:GetService("ServerScriptService"):WaitForChild("GlobalDataHandler"))
valueBeingAdded= "blah"
ClickDetector.MouseClick:connect(function(player)
local testData = ServerData:RequestData(player) -- should be "ServerData.RequestData(player)"
if testData == nil then return end
local stringTable = testData.stringTable
table.insert(stringTable,valueBeingAdded)
ServerData.UpdateData(player,testData)
end)
note: this is copy and pasted, so it might not look right
I already was doing that before they commented, I just didn’t include that in the code since I felt it wasn’t need-to-know information. It seems this may be left unsolved.
If you want to debug your code, you will have to use print statements on a lot, the Lua debugger is broken, so that is not an option (unless it works for you)