Cannot get information from a module on other scripts

  1. I want CFrames that are saved in my Module located at ServerScriptService to not be nil’s in any other script located at ServerScriptService.

  2. My issue is that I have a module called SavedEnemies in ServerScriptService which saves CFrames of enemies. Yet, whenever I try to get the said CFrames from an other script located at ServerScriptService, it only returns nil, and the entire Module’s table is empty.

  3. It saves them as [EnemyIndex] = CFrame. Yes, EnemyIndex is not a nil. Yes, CFrame is not a nil.
    I’ve tried adding functions to the module, Update(Index, CF) and Get(Index). The script that updates the module is working fine and, even after waiting for time, even if I do another require for the module, it would still return the said CFrame through Get() function easily.
    I’ve tried to move the system to another place. In fact, I initially made the system in an other game, but when I took it to another place with little changes (no direct changes to the CFrame save method), it stopped working completely.
    No, there’s also nothing that could set CFrames as nils. Already checked that.

Here’s the part of the EnemyModule module that updates the CFrames.
image
“SavingModule” is “SavedEnemies” module, which gets required at the very start of both TowerFunctions and EnemyModule.

Here’s the module itself. I know that you can just do [Index] = CF, but as I said, I tried multiple solutions and this was one of them, still didn’t work.

Here’s the line in TowerFunctions module that tries to get CFrames. It keeps spamming “nil”, and no, Index is not nil and is the same as the one that was saved.
image

All of them are placed in ServerScriptService.

spent like three hours trying to find the issue :sob:

2 Likes

as
dddb

Works for me

it indeed works, but not for me. I took the exact same system and moved it to another place without doing direct changes to the cf saving system at all as I said before, and this said system does work in the place where I did it initially. I have NO idea what causes it not to work.

Is Index Number value ?

did you try use variable(-s) on functions as i provided the image of my code (honestly this is just basically used for just increasing perfromance)

also try fire it in console
dasdad

Index is an IntValue, but it saves its .Value in the table. Also, your code has a different Index usage, mine one is made because I use buffer to replicate enemies on client to save some data, so it saves the Index used in the buffer rather than 1, 2, 3 and so on.

not sure what you meant by “Variables on functions”

I’m not good at scripting and I don’t understand a lot of things, but I think this style of scripting would work for your script.

-- set
_G.mySharedValue = 42

-- get
print(_G.mySharedValue)  --> 42

:scroll: Script 1 — DataManager

_G.GameData = {
    Players = {},
    Version = 1.0
}


function _G.AddPlayer(player)
    _G.GameData.Players[player.UserId] = {
        Name = player.Name,
        Score = 0,
    }
end

function _G.UpdateScore(player, points)
    local pData = _G.GameData.Players[player.UserId]
    if pData then
        pData.Score += points
        print(player.Name .. "'s score:", pData.Score)
    end
end

:scroll: Script 2 — PlayerHandler

game.Players.PlayerAdded:Connect(function(player)

    if _G.AddPlayer then
        _G.AddPlayer(player)
        print("Player added:", player.Name)
    else
        warn("_G.AddPlayer not found!")
    end
end)

from what I’ve heard, _G is less optimized compared to modules. Also, forgot to mention, but I DID attempt to use _G. Same results. I have no idea what even happens because it saves everything just fine and even prints that it was saved, yet not any other script can access it.

We don’t really have a lot to go off here… Could you maybe share the rest of the TowerFunctions module (or only the relevant parts if you don’t want to share everything)? Is there anything else in the console other than the nulls?

Also I suggest to move the enemies table into the module’s local scope, this way it can only be set internally, and it can’t accidentally be cleared by other scripts:

local enemyModule = {}
local _enemies = {}

function enemyModule.Update(index: number, cframe: CFrame): ()
  _enemies[index] = cframe
end

function enemyModule.Get(index: number): ()
  return _enemies[index]
end

return table.freeze(enemyModule) --disallow modifying this module at runtime.

The exported interface is exactly the same, you’re just no longer exposing the table. Users are now forced to use the exported methods to interact with the data, in a more controlled way.

I mentioned everything that could affect the said module, everything else is just irrelevant to the issue. Also no, console always prints nil in any script I try to use it on, whether it be Saved.Enemies[Index], Saved.Get(Index), Saved.Get(2) (2 is always the Index of the first spawned enemy). The only time it would print CFrame is if I will do the print right after saving the CFrame, AKA do print(SavingModule.Get(Index)) after SavingModule.Update(Index).
(Saved and SavingModule are the same modules, just variable named differently in different scripts)

What does the script you sent exactly does? I’ll try to add that to my module right now
EDIT: Oh wait, I assume table.freeze just makes it so you cannot do module[index] = cframe, right? I do not really need that because I’m switching it back later on once I find out how to actually receive CFrames from the module.

I think you should create an objects value
That is, each time your script does instance.new and creates one in a properties section and then uses that one script to extract it from that section
I think it can solve your problem

ObjectValues won’t help me at all because I’m saving CFrames, I do not need to find Enemies there, and no CFrameValues as well. I’m doing a client replication in order to optimize Network Receive.

I got help from artificial intelligence and I think it gave me the solution because it understood that the problem was becoming nil.

2️⃣ Execution order problem

All scripts in ServerScriptService start at the same time.

If the reader script (TowerFunctions) runs before the saver script (EnemyModule), the module table is not yet initialized → you get nil.

💡 Solutions:

Using Events or BindableEvents

When the CFrame is saved, fire an Event and other scripts wait for the Event to be received.

Require and getter function

In the module, keep the data in a private table and always access it via a Get() function.

Make sure that the Update function is called before reading the data.

Be aware of execution order

If you don't want to use Events, you should schedule the scripts so that the saver runs before the reader (e.g. with task.wait() before accessing).

3️⃣ Summary

Main problem: Script execution order in ServerScriptService. Module table is read by reader script before being filled by save script.

Symptoms: Module table is empty or returns nil even with Require again.

Standard solution:

Use Events to notify.

Or wait before accessing data.

Eh, sorry, but no, that’s not really how it works because both EnemyModule and TowerFunctions have sort of a loop to do that. Also, even if I were to summon an enemy before placing a tower, same results.

1 Like

Just tried something else, made two new scripts, made one set an Index to cframe(1,1,1), and another to Get(Index) after a second, which worked. Now I’m even more confused because that’s pretty much what happens in EnemyModule and TowerFunctions! The only time the module gets used is to Update CFrame in EnemyModule and to Get CFrame in TowerFunctions, no other script or function uses the module at all.

The table.freeze call makes the table readonly (as you’ve already discovered), so no one can change the functions or keys inside the table. However you can still add cframes to the enemies table, because it has been moved into the local scope, and is no longer in the module table. This is simply a suggestion for controlled access.

Clearly, the problem does not lie in your save and load functions, since these seem to work fine. The problem must be with how you are calling them, where are you calling Update and UpdateModule? Could you share those snippets?

UpdateModule gets called inside the EnemyModule, inside the Enemy Movement loop everytime a task.wait() passes, which does UpdateModule(Enemy, CFrameSaved) (will also redo that to Heartbeat, but not about that right now). Movement itself works fine, UpdateModule does get called.
CFrameSaved is initially set to the CFrame of the first waypoint, but gets changed down below the movement function.


It’s ALSO being called whenever the enemy dies or gets to the last waypoint of the map, but we’re not talking about that, and dw, I tested that already, Update doesn’t get used there at all while the enemy is alive and walking.

I’m calling Update in the UpdateModule function, yeah.

I’m trying to do the Get() in TowerFunctions module, to be exact in the GetEnemy() function which finds the target for the towers. GetEnemy() is used, but it keeps printing nil on that line.

I just checked the game where I took the system from to see the unchanged version and the way it saves is EXACTLY the same, so I infact did not change my system at all. Even the module where it saves to is the same, so at this point Roblox gotta be kidding me. Thought it was because my modules were not getting required at the very start of the game (in order for functions and stuff to load/process) and the place with original system did that, but no, after I added a script that requires them, still nothing works.

It seems like your iterating over some sort of collection, I assume these are the waypoints of a generic tower game.

I have two major questions, what exactly is forceposition? Currently I think whenever an enemy of yours touches or passes some waypoint, that waypoint, or some script that manages them, sets this attribute to the position of the waypoint, so that the loop picks it up and advances. If this is true, then why don’t you just attach an attribute changed callback on forceposition and do the required changes there, it would move some workload outside of the loop, which is generally better. The second question is, how is CFrameSaved updated?

ForcePosition is used whenever I gotta change the position of the enemy outside the enemy script, for example if a tower could move enemies. CFrameSaved is updated by setting it to the next step CFrame of the enemy. My movement system uses Bezier Curve, so it does some calculations and then sets CFrameSaved to the CFrame it got. Those are irrelevant to the issue though because I tried to save anything else, not just CFrames, still doesn’t work.

At this point your probably use would be numbervalue for index (intvalue is number too but it’s different from numbervalue).

Intvalue is like you can do 1.2 while numbervalue you cannot do that as it would only do 1.

Also do the enemies of index is counted (like moduletable[#moduletable+index]) ? (because that how worked the image i provided of my code).

The problem is probably about enemies’s thing (a ai of your game) about index or something else as how CFrame is saved (you could add to function to define variable CFrame like this CF:CFrame same goes to index and add :number to it)

bf25f547007d15944c91e09a87ffa573ee1c2290