Attempting to use modulescript in place of _G

I am trying to replace my _G use with a modulescript
I have the following modulescript

local module = {}
print("GameStore Started")
module.GameData = {}

local function Init()
	module.GameData["0"] = {}
	module.GameData["0"][0] = "test"
	print("GameStore check1 ",module.GameData["0"][0])
	game.Players.PlayerAdded:connect(function(player)
		local PID = tostring(player.UserId)
		module.GameData[PID] = {}
		module.GameData[PID]["Controller"] = nil
		print("GameStore check2 ",module.GameData[PID]["Controller"])	
		module.GameData[PID]["BotsArray"] = {}
		module.GameData[PID]["BotsArray"][0] = 0
		print("GameStore check3 ",module.GameData[PID]["BotsArray"][0])	
		module.GameData[PID]["Bots"] = {}
		module.GameData[PID]["DigaBots"] = {}
		module.GameData[PID]["block4Bots"] = {}
		module.GameData[PID]["block4BotsMined"] = {}
		module.GameData[PID]["block4BotBoxs"] = {}
		module.GameData[PID]["DigaBotProgress"] = {}
		print("GameStore for player ",player.Name," Id=",PID," is ready")	
	end)
end

Init()
print("GameStore initialised")

return module

And in a local script in a tool i have this code snippet

local modules = RS:WaitForChild("Modules")
local GameStore = require(modules.GameStore)
print("GameStore is a ",typeof(GameStore))
for i,tab in pairs(GameStore) do
	print("GameStore tabName",i,tab)
	for j,entry in pairs(tab) do
		print("tabData",j,entry)
	end 
end 
print ("playersUserId=",PID)
local G = GameStore.GameData[PID]
print("G is a",typeof(G))
if typeof(G) == "table" then
	for i,tab in pairs(G) do
		print("G tabName",i,tab)
	end 
end
G["Controller"] = script.Parent

And in my output I get
PlayerName=Player1
GameStore is a table
GameStore tabName GameData table: 0xfee6fdf15058c5fc
playersUserId= -1
G is a nil

What I was hoping for was a table that had been initialised by the PlayerAdded function in the modulescript.
I was also hoping I could treat the data in G as if it was _G with write and read but have a small whisper in my ear that I cannot and have to have Get and Put functions in the modulescript.

Repro rbxl
ModuleScriptTabeClientAccessProblem.rbxl (19.7 KB)

Anyone help me on this?

This isn’t working because you’re trying to use a LocalScript to capture the player entering the game. However, by the time the LocalScript executes, the player is already in the game.

In other words, your ModuleScript is being executed after the player enters, thus you’re not seeing any data populated.

To fix this, you’d have to also add in a loop to get all the current players and create the same game data for them. However, I would argue that any such data should be managed server-side, not on the client.

1 Like

I was sort of hoping that the playeradded function in the module script Init function would be established before I try to get the table in the local script that has done the waitforchild for the player to appear in workspace.
It would not matter if another player had triggered the first run of the modulescript as the playeradded function would run once any player was added.
In any case I am now thinking that even if I do get the table setup I would have only got a copy and not access to the original in the modulescript.
If that is true there is no way to simply replace the _G with a modulescript containing a table. I would have to have a Get and an Update function to change the data so it was the same for all references to that.

ModuleScripts run once for each machine. They run completely separate for each client that calls them, or if the server calls them. Changes to one client’s module’s values won’t replicate.

2 Likes

Thanks for that.
Do you know if I am able to update the table returned by a modulescript or not?

Yes, as long as you don’t expect it to update it for each machine. If, for example, Player A adds Player B’s data to her own table, Player B won’t have anything in his table but Player A would be able to use it fine.

2 Likes

So from the code and output I supplied at the top of this post can you see a reason why I got a nil from the assignment of the table?

The PlayerAdded event doesn’t fire for the person who joined since their LocalScripts don’t load until after they joined. Other players changes to their own table don’t replicate to other players, like I already said. You should loop through existing players and set things up or just have the server handle things and fire to clients when they join.

2 Likes

OK. I think I understand what you are saying.
To further my grasp of what is happening I added some additional prints in the modulescript.
One at the start and another after the init()
As this modulescript is in replicated storage will it run?
I ask as I got no prints in the server output.
If not and I put the modulescript in serverscriptservices will it run there?

A ModuleScript runs when it is require()d in the environment it is required on. It can be anywhere.

2 Likes

Ok so I put a require into a SS script and now see the prints about starting, init and table setup for the UserId I am expecting on the server output.
The initialising seems to have been established via the PlayerAdded connection.
The localscript does a waitforchild for the player to appear in the workspace which is when I expect the playeradded connection to be run.
So in that same script when I try to get the table that should have been initialised by the playeradded in the local script why is it not there?
Adding a print like this
print("GameStore.GameData is a ",typeof(GameStore.GameData))
shows it is a table.
Putting some data into the sub tables of GameData does print out ok on the server.
So I am still not understanding why I am not getting the data on the client.
I will do some more prints and update tomorrow.

Added a repro as not able to find a fix for this.
Maybe a bug but not comfortable in raising a bug report just yet.

As I see in a different forum PlayerAdded function on client? the PlayerAdded event does not fire for your local player.

So your script works but not for your player. Try a test with more players. If it works for other players then in your Init function make sure to add your player in the table before or after connecting the listener.

The reason is that local scripts load after the player is added so the PlayerAdded event has already been fired by the time the listener is connected.

Hope this helps :slight_smile:

The PlayerAdded function in the Init is on the server and there are messages on the server as follows:
GameSetup Setup
GameStore Started
GameStore check1 test
GameStore initialised
GameStore check2 nil
GameStore check3 0
GameStore for player Player1 Id= -1 is ready

I have recopied the code from the GameStore as the original was updated during this topic.

What scripts require the GameStore module? The Init() function runs once when the module script is require-d. Do you have any non-local script that requires the GameStore module. Because that is the only way the server would print stuff.

EDIT: I checked the place you provided. Yes you do require the GameStore in SS_Setup and that is why you get the prints on the server. However the server and client do not share tables. The GameStore:Init() function runs 2 times. First in the server and then again in the client because it is required in the local script.

Now to the point. If you wish to query in a LocalScript the GameStore table then you have to manually add the local user. However if you query it in a server script all the users will already be there. The best way to do it on the client is to use a remote event or a remote function and have a server script query the module. Actually it is impossible to do it on the client because any client that joined before your client will not appear in the table.

OK now I have it.
Changed the GameSetup server script to just do the modules copy.
Changed the GameStore module code to

local module = {}
print("GameStore Started")
module.GameData = {}

function module.Init(player)
	module.GameData["0"] = {}
	module.GameData["0"][0] = "test"
	print("GameStore check1 ",module.GameData["0"][0])
	local PID = tostring(player.UserId)
	module.GameData[PID] = {}
	module.GameData[PID]["Controller"] = nil
	print("GameStore check2 ",module.GameData[PID]["Controller"])	
	module.GameData[PID]["BotsArray"] = {}
	module.GameData[PID]["BotsArray"][0] = 0
	print("GameStore check3 ",module.GameData[PID]["BotsArray"][0])	
	module.GameData[PID]["Bots"] = {}
	module.GameData[PID]["DigaBots"] = {}
	module.GameData[PID]["block4Bots"] = {}
	module.GameData[PID]["block4BotsMined"] = {}
	module.GameData[PID]["block4BotBoxs"] = {}
	module.GameData[PID]["DigaBotProgress"] = {}
	print("GameStore for player ",player.Name," Id=",PID," is ready")	
end


return module

Changed the localscript code to

print("Client")
local Players = game:GetService("Players")
local LP = Players.LocalPlayer
local PID = tostring(LP.UserId)
local PN = LP.Name 
print ("PlayerName="..PN)
local ActivePlayer = workspace:WaitForChild(PN)
local RS = game:GetService("ReplicatedStorage")
local modules = RS:WaitForChild("Modules")
local GameStore = require(modules.GameStore)
GameStore.Init(LP)
print("GameStore is a ",typeof(GameStore))
for i,tab in pairs(GameStore) do
	print("GameStore tabName",i,tab)
end 
print("GameStore.GameData is a ",typeof(GameStore.GameData))
print ("playersUserId=",PID)
print("GameStore.GameData sub table print")
for j,entry in pairs(GameStore.GameData) do
	print("GameData",j,entry)
end 
print("GameStore Check = ",GameStore.GameData[PID]["BotsArray"][0])
local G = GameStore.GameData[PID]
print("G is a",typeof(G))
if typeof(G) == "table" then
	for i,tab in pairs(G) do
		print("G tabName",i,tab)
	end 
end
G["Controller"] = script.Parent
print("ControllerParent ",G["Controller"].Parent)

and now the client output is
Client
PlayerName=Player1
GameStore Started
GameStore check1 test
GameStore check2 nil
GameStore check3 0
GameStore for player Player1 Id= -1 is ready
GameStore is a table
GameStore tabName Init function: 0xfee3e0dd2985ddc3
GameStore tabName GameData table: 0x8cf6569a2df9c593
GameStore.GameData is a table
playersUserId= -1
GameStore.GameData sub table print
GameData -1 table: 0x5e89a70793b95683
GameData 0 table: 0x24c03fd5bc9f898b
GameStore Check = 0
G is a table
G tabName block4Bots table: 0xa7d1ba4f565ee7a3
G tabName DigaBotProgress table: 0xe5b26154c5eeb8cb
G tabName BotsArray table: 0x726cec75622a749b
G tabName block4BotBoxs table: 0xfbdc68ecb42096d3
G tabName DigaBots table: 0x41a6c29d0064a7ab
G tabName Bots table: 0x6b63f5ab716ca773
G tabName block4BotsMined table: 0x11fa533ca720f77b
ControllerParent Player1

So each player gets its own GameStore and initialises it with the local player and then the tables are showing in the prints and the last test confirms that I can from the client change the table values.

1 Like

Transferring the working code back into my game has allowed me to continue with that.
I am marking your latest post as the solution as it gave me all I needed to solve my issue.
Thank you.

1 Like