How do I update a module script's variables in roblox studio? - lua

So I have scriptOne (module) and scriptTwo (local script I think). I use scriptTwo to require() and change the variables with that. But how do I update the values in scriptOne (that I also want to access from other scripts as well) to these new values?
scriptOne code
local module = {}
module.test = 100
while true do
wait(1)
print(module.test)
end
return module
scriptTwo code
local data = require(workspace.playerStats)
data.test = 0

changing variables inside of a modulescript is global, change a variable and every script using the module gets the new variable
the reason why the code you provided doesnt work is because of the while loop. the loop yields and cant proceed, meaning it cant also return the module so the server script is waiting forever.

The problem is the while loop in the ModuleScript. ModuleScripts are used by require() to share variables. In your case, your ModuleScript has a infinite loop, which doesn't let require() return, and gets the second script stuck. You are probably using the while loop for debugging the variable, so I'd recommend moving it to the second script.

Related

Is there an easy way to get all global variables defined in a Lua code file?

Everybody knows that variables in Lua, if not explicitly defined as "local", will be global. This will sometimes cause problems, like overriding library functions, or unexpectedly providing a value for another global variable with the same name. So it should be very helpful if there's a way to find all global variables that is defined in a single Lua code file.
However, I failed to find any clue on this seemingly quite-popular problem. The best answer I can get online is using _G to print all global variables in the environment, which isn't of much help. I'm currently coding Lua in Intellij Idea with Emmylua, a powerful tool that can show global variables in a special style, and it can easily trace a global variable to its definition; but when the code becomes quite long, this will not help much either.
So basically, I just want to get a list of global variables defined in a given Lua code file. Either with a tool or with a wonderful function. If it can make things easier, we may presume the code file is a module. If it can further print the definition locations for these global variables, that's even better. Can somebody help me?
Lua doesn't have a way to tell when or where a global was introduced.
In the special that the value is a function, debug.getinfo may be able to help by telling you where the function is defined (which is often but not always the same place where the function is made global).
You can capture the needed information at the time the global is introduced. This can be done by setting a metatable with a __newindex method on the global table. This method will be called when a new global is introduced (but not when an existing global is overridden). In this method, you can figure out where the caller came from with debug.getinfo. Also beware, if any of your other code is trying to use a metatable on the global environment, you must play nicely with it. (It can only have one metatable.)
You can also avoid using the global table. One in-between way of doing this is to override the environment. In Lua 5.2 and Lua 5.3, this is done by declaring a local table called _ENV -- all accesses to the global table will instead access this table. (Actually, global accesses always use _ENV and _ENV is _G by default.) You can make this mostly invisible by giving this _ENV a metatable that forwards accesses to _G (or whatever other environment). The difference here is that __newindex will still be called even if a binding exists in _G, so this method can detect overrides.
Using _ENV, though is inherently local to a scope (e.g. each file needs to override it). Such a hook could be installed globally as well though. If you load your modules manually with the load function (unlikely), you can just supply a custom _ENV as an argument. If you use require, it is possible to get a hold of the loaded file before it is executed by overriding (or monkey patching) the Lua searcher in package.searchers[2]. This is the built-in function that require calls to find the file in your filesystem and then load it. The return value is the loaded function which require then runs. So, after it is loaded but before it is returned back to require, you could use debug.setupvalue to override the default _ENV value (if any).
Example code (only lightly tested):
local global_info = {}
local default_searcher2 = package.searchers[2]
package.searchers[2] = function(...)
local result = default_searcher2(...)
local parent_environment = _G
local my_env = setmetatable({}, {
__index = parent_environment,
__newindex = function(self, k, v)
local new_info = debug.getinfo(2)
-- keeping rich data like this could be a memory leak
-- if some globals are assigned repeatedly, but that
-- may still be okay in a debugging scenario
local history = global_info[k]
if history == nil then
history = {}
global_info[k] = history
end
table.insert(history, {info = new_info, value = v})
parent_environment[k] = v
end,
})
if type(result) == "function" then
debug.setupvalue(result, 1, my_env)
end
return result
end
function gethistory(name)
local history = global_info[name]
if history == nil then
print('"' .. name .. '" has never been defined...')
else
print('History for "' .. name .. '":')
for _, record in ipairs(history) do
print(record.info.short_src .. ": " .. record.info.currentline)
end
end
end
Note that the hook here will only apply to files required after this code has been run, and basically only applies to Lua files (not C libs) that get included via the built-in require. It doesn't set a metatable on the global environment, so not conflict there, but it could be circumvented if files access _G directly (or e.g. setup access to _G instead of _ENV in their own _ENV tables). Such things can also be accounted for, but it can be a rabbit hole depending on how "invisible" you need this patch to be.
In Lua 5.1, instead of _ENV, you have setfenv which I believe can be used to similar effect.
Also note that all the methods I'm outlining can only detect global accesses that actually get executed at runtime.
Yes. Local versus global is a binding issue, which is primarily established at compile-time. Setting a variable is, of course, well-determined at compile-time.
Lua provides the luac compiler, which takes the argument -l for list.
In Lua 5.1, there is the opcode SETGLOBAL. A column indicates the line number of the statement and the comment indicates the name of the global.
In 5.2 and later, there is the opcode SETTABUP. A column indicates the line number of the statement and the comment indicates the name of the table and key. "Globals" are in the table referenced by the _ENV upvalue.
So, you can easily find the line number of any statement that sets a global variable with the tools Lua provides.
BTW—under many module systems, a module script would not set any global variables.

What does this piece of lua code from awesome wm do?

Take a look at this code:
local urgent = {}
local capi =
{
client = client,
}
local client
do
client = setmetatable({}, {
__index = function(_, k)
client = require("awful.client")
return client[k]
end,
__newindex = error -- Just to be sure in case anything ever does this
})
end
I'm having trouble understanding what it does. It's from the awesome-wm project. These are the things I'm having trouble understanding:
client = client in the declaration of capi
setmetatable stuff inside do-end
client = client in the declaration of capi
This is defining what portion of the capi is available in this file's scope, If you look at the client.lua file you will see that the capi defined in it has client, mouse, screen, and awesome.
For each item defined in the capi table there is a corresponding .c file. These files define objects such as client. urgent.lua has visibility of that object, likely it is a global variable, that is how we can set client = client the second client refers to the global variable.
Here is an example of 2 files:
main.lua
bar = "Hello World!"
local foo = require('foo')
print(foo.bar)
foo.lua
local foo = {
bar = bar
}
return foo
The print function in main.lua will result in Hello World!
setmetatable stuff inside do-end
Here by warping the setmetatable in a do-end block the code is executing in a restricted scope. This is normally done to contain the block's local variables so that they do not persist after the code's execution.
That said that is not the purpose of this block as the block has no local variables. As I see it, the blocking is simply to show that the object being modified is the local variable of client and not the global variable of client.
Additionally the metatable here is used to prevent circular dependency loops, this is mentioned comments in some of the places where similar code appears in the project, such as client.lua where local screen is defined.
#Nifim answer is excellent. I just want to add more context on why this code exist in its proper historical context. Before Lua 5.2, the module system was different. There was a magic module() function defined in the core Lua library. When you made a module, you had to first make local version of all global variables before calling module() because otherwise it would run in its own global environment. "capi" stands for "Core API" or "C (language) API" depending on the weather. If Awesome was written today with all the knowledge we now have, there would not be a public "C language" API and they would always be hidden in the private section to increase flexibility. Right now setting "c.my_own_property" do a couple round trips between capi.client and awful.client just to accommodate all the legacy constraints.
Now, the metatable magic is a Lua pattern called meta-lazy-loading. Because the urgent is a submodule of awful.client, it cannot directly import awful.client without causing a circular dependency. Over time, as Awesome APIs became better defined, more and more refactoring were made and they often introduced weird dependencies to maintain some degree of backward compatibility. In the best universe, we would have disregarded all users config and just re-engineered the whole code to avoid these circular dependencies. However every time we do that all users of the said APIs wake up one morning and they cannot login into their computer anymore. So this kind of workaround exist to prevent such events in return for some weird code and maintenance burden.

{LUA} How to fire another script in script?

I have a question from Lua/Roblox!
Basically, I want to fire a script from a script. This may sound like a stupid question, but actually it isn't :P
For example:
I have a script: script1 in ServerScriptStorage.
And, I want to code it to fire contents of script2.
Examples:
Content of script1:
game.Players.PlayerAdded:Connect(function()
HERE SCRIPT2 FIRING!
end)
Content of script2:
print("This message is triggered by event in script!")
This is fairly simple task I suppose, so please give me the SIMPLEST and SHORTEST version of code. I don't need any exclusives such like launching 2 script in 1. I'm a begginer script, so please keep it simple.
Thanks, NorteX.
In pure Lua, using dofile would probably make the most sense. However, in Roblox, the approach must be much different. The way I would recommend doing this is using a ModuleScript for "Script2". Then you would load the script using require(). Because "requiring" a script caches the returned value for future "requires", this means that the contents of the ModuleScript will only be executed once. Thus, if you have code you want to run multiple times, you should encapsulate it in a function that the ModuleScript returns.
Here's how the code would look like given your setup:
Script1:
local script2 = require(game.ServerScriptService.Script2)
game.Players.PlayerAdded:Connect(function(player)
script2()
end)
Script2:
-- In game.ServerScriptService.Script2 as a ModuleScript
return function()
print("This message is triggered by event in script!")
end
Check out the documentation for ModuleScripts to understand more about them.
workspace.SCRIPT2.Disabled = true -- Disables SCRIPT2 , you can remove this and manually disable it in SCRIPT2's properties.
game.Players.PlayerAdded:Connect(function()
workspace.SCRIPT2.Disabled = false -- Activates SCRIPT2. You can alter the "disabled" state multiple time to make it reboot and operate more than once.
end)
Also, you could replace workspace.SCRIPT2.Disabled by the location where your second script is, by example, workspace.FolderOne.scripts.SCRIPT2.Disabled . Just be sure it points to the script and keeps the "disabled" part on, so it knows to disable / enable it.

Running script for a group to access functions within group parts

I was just wondering in roblox if anyone has ever come upon a situation where they needed to run scripts from another script. My situation is that I am making a control point system for a game. I need to be able to know if the other points are captured in order to capture the next ones so I am attempting to write a controller on top of it but i am not sure exactly how to access the functions from within the control point script.
Yes! ROBLOX provides a very useful feature called ModuleScripts for the very purpose of calling scripts from other scripts. The icon for ModuleScripts looks the same as the icon for regular Scripts except with a little brick in the bottom-right.
The way it works is that regular Scripts can call ModuleScripts in the game using the special require() function. The easiest way to explain this is with an example.
To begin, let's imagine that we have a Script and a ModuleScript. The Script will be located at game.Workspace.normalScript, and the ModuleScript will be located inside a brick called part at game.Workspace.part.moduleScript.
moduleScript will contain the following code:
script.Parent.Transparency = .5 --"Parent" is the part since this ModuleScript is located inside the part
Now, normalScript will contain the following code:
require(game.Workspace.part.moduleScript)
When you run the game, normalScript will execute moduleScript, changing the transparency of part to .5. When one calls require() on a ModuleScript, it will act as though it were a normal function being called. moduleScript acted as though it were a function, and ModuleScripts in general act the same way as functions for the most part.
This also means that ModuleScripts can return values like functions. For example, if we have the following code in moduleScript:
return 3+3
Now, our script will contain the following code:
local number = require(game.Workspace.moduleScript)
print(number) --> 6
This code will print "6" to the console since moduleScript returned 6. As you might guess, this means that ModuleScripts have many more uses than simply remotely executing code.
Here are two more examples of uses of ModuleScripts:
1) Returning functions:
moduleScript:
return function()
print("hey")
end
normalScript:
local func = require(game.Workspace.moduleScript)
func() --> hey
2) Returning modules such as apple below:
moduleScript:
local apple = {}
apple.flavor = "sweet"
return apple
normalScript:
local fruit = require(game.Workspace.moduleScript)
print(fruit.flavor) --> sweet
These are rather silly examples of ModuleScript uses, but ModuleScripts can actually be very powerful tools. For some cool examples, visit the ROBLOX Wiki page on ModuleScripts and scroll about halfway down.

How to call functions in other script files in Roblox

I have a script file embedded in the Workspace that contains functions. I would like call these functions from script files embedded in child objects of the Workspace. I don't want to have to copy and paste these functions into multiple script files. I figured the object oriented approach would be best if its possible.
An alternative to _G is to use the also globally avaliable table, shared. Shared is used the same way _G is, but you must specify "shared" before the variable identifier, unlike _G, where you can merely write the name of the variable without the _G (not anymore in ROBLOX). Shared is used in the following context:
shared["variablename"] = value
Just like in the global stack, _G.
Example usage of shared:
Script 1
shared["rprint"] = function(array) for i,v in pairs(array) do print(i, v) end end
Script 2
repeat wait() until shared["rprint"]
shared.rprint({"Hello, ", "How", " are", " you?"})
The output of this script would be "Hello, How are you?"
The simplest way would be to use _G or shared.
In one script,
_G.myFunction = function(Arguments)
-- blah
end
In another script, you would use this code.
repeat wait() until _G.myFunction ~= nil
_G.myFunction()
This would also work with the global table shared, instead of _G.
I know it has been said before, but just use the normal _G or shared to access it.
Script one
_G.myFunction = function()
print("Hello, myFunction!")
end
Script two
repeat wait() until _G.myFunction()
_G.myFunction()
Output
Hello, myFunction!
I would use BindableEvents or RemoteEvents. I think this is a better approach than using _G. This will allow you to keep everything local. You can use Bindableevents and RemoteEvents to trigger functions and send as much data as you need back and forth between scripts. Use BindableEvents for server/server communication and RemoteEvents for server/client-client/server communications.
http://wiki.roblox.com/index.php?title=API:Class/BindableEvent
You can make the function global. In
one script do this:
_G.myFunction = function() print("Hello World") end
In another script do this:
repeat wait() until myFunction myFunction()
By defining a function is _G you must
wait for the script to execute
assigning the function, then you can
call the function even without
specifying _G.
This won't work because, due to ROBLOX updates, you now have to index _G whenever you access items inside it.
You cannot use dofile() in ROBLOX, either, as I saw mentioned above.
In answer to the question: you need to create a function in one script into the global table - _G, by adding _G.MyFunction = function(parameters) end. In another script, you need to access it inside the _G table - _G.MyFunction().
A common problem that appears for ROBLOX scripters is that you try to access your function inside _G before it is created. A simple way to solve this is to add a wait until it is created, as suggested from Camoy's post:
repeat wait() until _G.MyFunction()
You can make the function global. In one script do this:
_G.myFunction = function() print("Hello World") end
In another script do this:
repeat wait() until myFunction myFunction()
By defining a function is _G you must wait for the script to execute assigning the function, then you can call the function even without specifying _G.
You could use Module Scripts which were thankfully added. You could put the functions in there, then call and use them anywhere else!

Resources