Strange problem with updating data via ModuleScript

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

function Module:AddData(self, playerDataTemplate) 

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

1 Like

At the end of your module, you need to do return module
Edit: you might have done that, where are you calling the UpdateData method?

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

Nope, sadly nothing has changed. Thanks for trying.

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)

I think you get the point from here

1 Like

Hmm let me try this out, maybe it will fix my problem.

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 understand what you’re saying and i’ve tried your fix, but it doesn’t solve the problem of all players having the same data.

Then try what @Arkiuma said that’s the only solution I can thing of

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)

Yeah, I tried multiple print statements before even posting this and I was utterly confused so I went here as my last resort.