Hey! I’m trying to update the value of a Module’s table and retrieve it from 2 scripts in the same context (both are client), but it doesn’t seem to update. The value I assign only seems to get assigned on the same script where I change it:
-- Module
local PlayerData = {
}
return PlayerData
local PlayerData = require(ReplicatedStorage:WaitForChild("PlayerData"))
PlayerData["new_value"]="hey"
print(PlayerData["new_value"]) -- Output: "hey"
-- Attempt to retrieve the value
local PlayerData = require(ReplicatedStorage:WaitForChild("PlayerData"))
task.wait(3) -- Avoid race condition; without a doubt the value gets assigned on the other script
print(PlayerData["new_value"]) -- Output: nil
Why does only the script where it gets assigned return "hey"? What is a solution to this issue??? I’d like for both to return "hey" (aka. have the updated version of the table).
I know a workaround to this would be using the dreaded global shared or _G tables, but everywhere I’ve looked people talk about them like they’re artifacts from hell made by the devil himself, so I’d like to avoid them if possible.
This seems to fix it! The flaw in my logic was that I was requiring it once and using the result of the require global assuming it would dynamically update. (My game’s code is not like the example I gave)
For my usecase here (changing dynamic global tables), would it be a bad idea to use shared or _G? I don’t see any practical differences between shared VS my approach here other than the fact that I’m abstracting it by using modules instead of writing to a table directly.
I’m glad that solved it!
About your question, _G works too, but maybe just use a module (for flexibility) and use _G for other things (i.e. know when the other script loads the module)
local PlayerData = require(ReplicatedStorage:WaitForChild("PlayerData"))
PlayerData["new_value"]="hey"
_G.PlayerDataLoaded = true
-- Attempt to retrieve the value
while not _G.PlayerDataLoaded do task.wait() end
local PlayerData = require(ReplicatedStorage:WaitForChild("PlayerData"))
print(PlayerData["new_value"]) -- Output: "hey" (SORRY I FORGOT TO EDIT THIS LOL)
I was thinking of directly shared mostly because this is what I have to type anytime I want to read or write data from the module
require(PlayerData)[plr.Name].Inventory.Weapon
require(PlayerData)[plr.Name].Inventory.Weapon = "Test"
-- It's not too bad in isolation, but it gets ugly very quickly, eg:
weaponsFolder[require(PlayerData)[plr.Name].Inventory.Weapon]:Clone().Parent=plr.Character
while with shared I could get away with
shared.PlayerData[plr.Name].Inventory.Weapon
shared.PlayerData[plr.Name].Inventory.Weapon = "Test"
-- Looks a bit cleaner
weaponsFolder[shared.PlayerData[plr.Name].Inventory.Weapon]:Clone().Parent=plr.Character
In regards to your approach:
Any subsequent attempts to read PlayerData with this approach will result in an outdated table, since it doesn’t dynamically update.
Hey there, after a bit of research I’ve come across the actual reason why this happens. Our assumptions were both wrong:
As I checked here, it would’ve made no sense for the table to not be accessible when they share memory addresses.
This turns out to be a feature of LocalScripts. When a LocalScript runs on the client, it has its own Lua environment and does not share the same state as other LocalScripts. This means that if you have a module script that is loaded by multiple LocalScripts, each LocalScript will have its own copy of the module script’s state.
If you modify the state of the module from one LocalScript, the changes will not be reflected in other LocalScripts, because each LocalScript has its own copy of the module script’s state.
In other words, continuously running require like I implied does not work.
This is not the case for scripts running in the server, since they share the same enviornment and state (meaning the Module CAN act as a global table).
Sadly, the only workaround for globally saving a table on the client is to use shared or _G.
An alternative would be to make the table into a structure of Folders and Values, and read from it that way.
Hello, are there any additional details to where you are parenting your local scripts? I cannot reproduce this when both are parented to StarterPlayerScripts
(I could only repro If i parented one of the local scripts under an Actor, Actors do have a separate environment)
One LocalScript is parented to PlayerGui, the other one’s in ReplicatedFirst. Despite returning the same memory address for the table, they do not share the state of it. (eg. if script 1 changes something, script 2 will not see it)
I’ve created my implementation using shared & the problems have gone away. All LocalScripts have read and write access to the updated version of the PlayerData table.