Questions about ModuleScripts: Strange, unintended behavior?

I wasn’t exactly able to find this question anywhere else but…

I have experimented with module scripts and noticed something odd.
Let’s say we have scriptA and scriptB.

Inside a ModuleScript we have

local mod = {}
mod.testvalue = 1
return mod

When I increase testvalue by 1 with scriptA, scriptB will immediately see it and be able to print it in the output.
scriptB will just print mod.testvalue > 2 in the output.

But when I do this

local mod = {}
mod.testtable = {}

return mod

Now I add a value/another table to testtable in the module with scriptA.
let’s say I do something like

local mod = require(path.to.module)

mod.testtable["testvariable1"] = {"string", true, 1}
mod.testtable["testvariable2"] = 12345

When I do #mod.testtable in scriptA it prints 2 (the table length)
and when I do a ipairs loop for example, it prints all values, etc as supposed to be/do.

But whenever I do this with scriptB, #mod.testtable prints 0 (despite table having values in them)
and when I do a ipairs loop it all pretends and behaves as if the table is empty.

I find this very strange.
And I’m completely confused by
why number values updated by scriptA show up in scriptB.
But why do table updates/new values added by scriptA not show up in scriptB?

Is there a explaination for it and a solution?

I might want to save NPCs and their variables in a single global module script and have other scripts read from it, etc without re-using require() (I only want to use require() once per script, and even re-requiring didn’t work in some cases btw).

Should I use _G instead if I want a global table containing NPC data or such that update real time?
Modules apparently act weird if you add new tables and values to them, scriptA can see them, but scriptB and other scripts don’t seem to notice the changes.

(P.s, please tell me if I’m in the wrong topic or if this question was asked before, I was unable to find it, also tell me if something is unclear, I tried my best at explaining my issue.)

What I would do is add a function to moduleA that returns the value we want to pass to moduleB in order to get the updated values.

For me, it prints 0 on both scripts. Generally though, you shouldn’t being using the length operator # on tables with non-numerical indexes. Also, you shouldn’t use _G.

2 Likes

But then I would have to refresh every time, plus scriptB would have a copy of the module script, which is not what I was looking for.

I want every single script to be able to use the exact same table without re-using require() or refreshing.

When I update a number value in a modulescript with scriptA, scriptB is able to see it.
But when I add a new value or table even with scriptA, scriptB pretends as if the table is empty/unchanged.
And this confuses me a lot.

I might consider using _G instead just so I don’t have to repeat require() on a module every time a value/table has been added/removed or pass values over to other scripts (which is also some form of repeating require(), I rather avoid at all costs).

Is there also a explanation to this rather strange behavior?
I would really appreciate a explanation since I’m completely confused and don’t know why modules do this.
I expected modules to work similar to _G honestly, but apparently they do not.

Edit: I posted this a bit late, I was still writing this when someone else was replying.

You do not need to re-require in order to register new updates.

a module script is essentially one large table with functions so when you require them from two different scripts yes this will happen because they are essentially two different tables in order to accomplish what you want to achieve you make the variable local outside the module:

local TestValue = 1
local module = {}
--then you have functions to get and set the value
function module.SetTestValue(newValue)
    TestValue = newValue
end

function module.GetTestValue()
    return TestValue
end
return module
--all you really need to do is just declare  the variable outside the module script
1 Like

All you have to do is declare the table outside of the module script like i said then you make function to edit the indexes with the new data and then functions get data from a specific index.

To add on to my answer, that outcome could be due to how you’re testing it. I used the following scripts:

--Script A
local mod = require(game.ReplicatedStorage.ModuleScript)

mod.testtable["testvariable1"] = {"string", true, 1}
mod.testtable["testvariable2"] = 12345

print(mod.testtable.testvariable2) --> 12345
--Script B
local mod = require(game.ReplicatedStorage.ModuleScript)

wait(5)

print(mod.testtable.testvariable2) --> 12345

As I said in a previous message, do not rely on the length operator for tables with non-numerical indexes.

1 Like

So basically the module needs a local table and I need to make functions that write/read from the table which can’t be accessed in any other way but this?

Does this also mean that if NPCs were to look for the nearest target to attack/chase, the target seeker function needs to be in the exact same module?

Do module scripts have a limit on how much functions/tables they can contain?
I would like to use a module script to replace _G.

yeah it does need one large table and it should be declare outside the beginning of the module, and im not sure about the limit i dont think there is one per say, but you can test my method and see if it works, and can you give and example of what you mean by target seeker function

Target seeker function is a function used for NPCs (zombies for example) to find the nearest players just to name something.
But all AI functions would need to be in 1 module since that one module contains all enemy/ally/player info.

well yeah and that info should be in a table declared outside the module, and did my method work for you?

Haven’t tried it yet but hope it works.
And does the table have to be declared in another script or can I just declare a table in the module and have functions in the module to read/write values from it?

yeah the table must be declared in the module for it to work for example

--in a module script
local Table = {
 ["Index1"] = "something",
 ["Index2"] = "somethingElse"
}
--[[
lol when i said outside the module i meant above the local module = {}
declaration
]]--
local module = {}
function module.SetDataFromIndex(index,data)
--[[this function takes the index that must be changed 
and the new data to update it with]]--
   if Table[index] then
      Table[index] = data
   end
end

function module.GetDataFromIndex(index)
--[[
this function takes the index and returns the 
data stored in the index, if the index does exist
]]--
   if Table[index] then
       return Table[index]
   end
end
return module

Think of ModuleScripts as temporary data holders (they pretty much are!). Modules are widely used because they can be called from multiple scripts and return the same data to each and every one of them. This allows for simplicity, and prevents people from putting the same function in 50 scripts (AKA hardcoding). Now, if you simply declare the function once in the ModuleScript, any script that needs the same function can just require the module.

The benefits of doing it this way? It prevents your scripts from getting unnecessarily messy, it’s easier to implement fixes since you don’t have to fix that one-word-typo in all the scripts you pasted the function into. Edit the function once, and everything works again. It’s magic!

But, you also need to think logically with modules. You cannot get something from a modulescript if it doesn’t exist, right? It’s like people and something they know (or not). You can’t go to someone and demand to know something about whatever, and expect them to tell you, if they never knew that information in the first place! But, if you told them first, and asked later they could tell you!

Now, you ask why some values don’t update in script B if you changed them in script A, while some others did. I don’t have the code you use to test it with, so I cannot tell for sure. But maybe you simply did something like this:

-- Script B
local module = require(path.to.module);
local testTable = module.testtable;

print(testTable.testvariable1); --> nil

This is happening since you already “cached” the testtable inside script B, and if you do that, it doesn’t “receive” the new updates. Also, maybe script A ran after script B. There isn’t a set order the scripts run.
I keep this as a rule: Don’t get data from the module scripts upon start, if they are modified by another script.

Basically, any static values should be declared in the module already, and should not be done by another script. Static values can be for example default data, “reward codes”, game configuration etc. If you declare those values in the module, then all scripts requiring it will receive the same results and you will avoid being confused.

However, there is also dynamic content in place in a module (most likely), such as a player’s data. Then it’s fine to modify it from a script.

2 Likes

What if I had a table contains more tables? Would that work the same?

1 Like

yeah u should just make functions to edit and get data from each specific table for example you could have a function called “GetDataByIndexFromTable1” and “SetDataByIndexFromTable2”

1 Like

No they are not. Module scripts required on the same machine will return the same cached result.

They’d still access the same table.

@ OP
I believe you’re just running into a race condition. The reason you’re not seeing the value “update” is because one script is reading from the module before the other one writes.

The table would still be the same, however, and your R/W operations are just out of order.

5 Likes

where did you get that information from?

This is testable in Studio, and is the expected behavior of ModuleScripts. The Wiki page also lists the fact that the result is cached after require.

4 Likes