Questions about ModuleScripts: Strange, unintended behavior?

ok thank you i will go test this out now, so you are saying that if on the client i require the same module script from two different local scripts and update a value in that module it will update across both scripts, even if it does work i think my way of implementing it with one large table declared above the module would be more effective.

The getter/setter pattern is un-needed in this situation and would just look weird. But yes, modules required on the same machine share the same cache.

1 Like

I just tested it out and what your saying is true, i confirmed by using a bindable event that fired after the value was updated and I disagree with your statement that it looks weird though.

Use functions to manipulate (sometimes) data within modules. It would be easy to know the data flow of what is being added and manipulated this way,

I’m not sure about the race condition thing.
I’ve put scriptB in a loop so it would print the value even after scriptA updated it/added a new value/table.

Same thing still happens.
I’ve tried things like making scriptB wait a few seconds so scriptA can add a new value to the modulescript, no difference.

I really can’t figure out why modules do this.
Even the wiki doesn’t say anything about this behavior.
I’ve searched the wiki several times about module scripts.

This seems like unexpected behavior. If you have a small reproducible file you might want to share it here and if it consists you could file a bug report. ModuleScript return values should be cached.

So this is actually unintended behavior of the engine?
All scripts are supposed to be able to edit/add/remove things to a table and all other scripts are supposed to be able to immediately see it?
If that is so then I might indeed make a place file with the experiments so I can report the bug.

There is no problem with using _G. It’s as secure as ModuleScripts and as fast as modulescripts. Don’t spread the rumor of _G being worse than ModuleScripts. It’s simply not true.

Back to the main problem, it prints 0 because of how lua works with table lengths and stuff. I myself find this still confusing and # doesn’t always give the table length. Only when the table is an array and not a dictionary. #{1,2,3} -- 3 but #{["B"]=2,["T"]=3} -- 0. If you want to get the true size of a table you’ll have to do something like:

function getSize(t)
local i = 0
for _,_ in pairs(t) do
i = i + 1
end
return i
end
--sorry for no tabs, it doesn't work for me in the devforums for some reason

Like I said before, I myself don’t exactly know how lua tables work with length. You should ask someone else about that.

I will look a bit deeper into this, also thanks about the _G thing.
I’m also uncertain why some people say it’s bad.
You can’t lose any important data or anything if you organize it in some way and don’t accidentally overwrite variables, right?
I still would like to look deeper and get more answers before I’ll mark a solution.
I might consider making a test place file to show you the issues I’m having, etc.
Thanks for all the help everyone currently is providing me though.
I just want to be sure and certain about things before I do this in a actual game and risk having to change the way how anything works, etc.
This may be very important in some projects.

_G is just like ModuleScripts but it’s 1 big table instaid of a script. The rumors about _G being worse are fake and you can get people far more skilled than me to tell you the same. The only difference is at micro level and you really shouldn’t worry about things that small. Use ModuleScripts or _G or if you’re like me, both.

Testing sounds like a good idea too, I’ll also do some tests /research myself with tables.

There is a lot of speculation on _G and its supposed downfalls concerning security and performance which I won’t go into, however if you think about it because _G is shared between all server scripts it can get messy to store lots of similar data in _G. This is not the case with ModuleScripts, because scripts can choose which ModuleScripts to require. This is why I would not advise using it for long-term projects.

I might want to use _G for long term projects actually since it’s much easier to store NPCs and targets, I prefer not to use Module script tables since other script apparently can only edit them indirectly through functions in the module.
Sharing the table between all game scripts and adding new values to it don’t seem to work as supposed to/expected.
I do not use _G a lot actually,
I just want my target table to be global for all AI in my game to access for example (sometimes also non-AI), and I want live, real time table updates (Immediate update across all scripts in the entire game when target/npc added/removed, but also tables that do not contain target/npc data).

(Also, typed this from my iPad in the night, there might be typos or so in my post.)

Modules are shared and so are the values stored within them. That means that the table between the two is indeed the same exact table and is in the same exact memory location. You can see this by printing out the table. The reason that ipairs and # do not work is because the indexes of testtable are strings. You need to use pairs for this and the table length must be updated using a pairs loop.

_G will not really help in this case except for ease of use. It has the same exact functionality as a ModuleScript which returns a table.

I believe the reason that # and ipairs work in scriptA is because of something to do with the way scripts behave when setting string indexes in the new lua VM.

(Also I believe the reason # doesn’t work on this type of table is because it internally uses the same functionality that ipairs does)

module script experiment.rbxl (17.1 KB)

Alright, I’ve created a place file.
There is 1 module script.
ScriptA (The script that writes/adds values to the module).

Then there is ScriptB
(The one that reads the values, etc).

There is a numberic table (testtable) and a table that uses strings as variable names (testtable2)
I’ve tried doing the #table_name on both
and I use pairs, ipairs and next, table on both tables.

I got mixed results.
You can check out the place file for yourself if you would like.
Using pairs() and next, table does seem to show the recently added values, etc somehow.
I am not 100% sure what is intended and what isn’t intended to happen.

Somehow some of the functions only work on the numeric and some only work on the string table, I am completely confused by this tbh.
If someone could explain me that, I’d really appreciate it.

Edit: My current conclusion currently is that the non-numeric table does update across scripts, but apparentl only pairs (not ipairs) can loop through it and # doesn’t work on them.
Not sure what is happening to the numeric table though.

This is expected of non numeric tables. ipairs is designed only for numeric indices without holes, and # only counts the array part of the table.

I am beginning to understand that now after doing some experimenting.
I thought I already knew a lot about tables because I’ve never ran into this issue before, but apparently there still is a lot to learn about them.

Anyways, since I can’t use ipairs or numeric loops on non-numeric tables.
How will I make NPCs look for the nearest target (zombies and such) since my npc tables look like this?

example_npctable[workspace.npcs.testnpc] = {
 root = workspace.npcs.testnpc.HumanoidRootPart;
 human = workspace.npcs.testnpc.Humanoid
 vars = {
  variable1 = 12;
  variable2 = true;
  variable3 = "sample text";
 }
}

This is not what my actual table actually looks like (the variable names and such),
but just giving an idea of how I store NPCs in a global table (either in a module or _G).
This way all I need to do is destory the AI script, then remove the npc from the npc table and all information is gone, no data leaks.

I know I can just use pairs for a table like this, but I want something as fast and cheap as possible.
(Numeric loops are apparently faster than (i)pairs, am I correct? But I can’t use them in tables like this.)

Numeric iteration is faster but you cannot use them for non-numerical indexes and for arrays with holes in them (as the length operator does not work on arrays with holes in them). So, you can either deal with it and use pairs (or write/use a similar iterator function) or you can use an array to hold the mobs. However, this might come at the cost of having to write a function for finding the mob from the mob object like so:

local example_npctable = {}

example_npctable[#example_npctable + 1] = {
  Model = workspace.npcs.testnpc; 
  --Other variables
}

local function getMobByModel(model)
  for i = 1,#example_npctable do
    if example_npctable[i].Model == model then
      return example_npctable[i]
    end
  end
end

local info = getMobByModel(workspace.npcs.testnpc)

I’d recommend having to deal with pairs in this use-case because unless you have a lot of mobs (in which case the table iteration won’t be your worst concern), the difference is negligible. If you’re that concerned and the amount of mobs is too large (or maybe you’re iterating a lot), then you can use BinaryTrees or similar structuring systems so that iteration is fast.

Ok let’s say, I want 200 NPCs in my game.
-NPCs need to be easy accessable, i’d rather not use loops to find a NPC to edit it’s variables, etc.
-I want to use the fastest possible loop for finding nearest enemy/target.
-I want to quickly remove a dead NPC from a table, in a instant, with some table delete function perhaps. (Preferably cheap since A LOT of NPC might spawn/die every x seconds possibly, expect it to be for some quick paced PvE game, even EvE.)

If I can get advice on what would be the best thing to do, I’d be happy.

Also was wondering,
does

for x = 1, 10 do

end

Work on a non-numeric / non-array table if I manually keep track of the table’s size every time something is added/removed?
And I mean as in having a variable like this.

local tablesize = 129 —This is the table’s size + how many NPCs are currently alive.

^ If I were to increase/decrease this every time something was added/removed from the table and manually keep track of it’s size,
could I use this in a numeric loop on a non-numeric table?
Does Table[1] still work on tables that don’t use numeric indexes?

(Typed this from my iPad, let me know if there are typos/mistakes.)

I would recommend just using pairs. Custom iterators aren’t worth it. Pairs is only a fraction of a second slower than ipairs and especially with the new VM

Though still curious.
Can you use a numeric loop on a string index table
if you manually kept track of a table’s size?
Not sure if that counts as “custom”
but just really curious to it.
I currently don’t have access to a PC to try this out.

Edit: Also, I think that fraction of a second does matter since many NPCs will need to look for the nearest target and loop through the whole table as quick as possible.