How can a script retain its values through different script loading in Lua? - c++

My current problem is that I have several enemies share the same A.I. script, and one other object that does something different. The function in the script is called AILogic. I want these enemies to move independently, but this is proving to be an issue. Here is what I've tried.
1) Calling dofile in the enemy's constructor, and then calling its script function in its Update function which happens in every game loop. The problem with this is that Lua just uses the script of the last enemy constructed, so all of the enemies are running the same script in the Update function. Thus, the object I described above that doesn't use the same script for it's A.I. is using the other enemies' script.
2) Calling dofile in the Update function, and then calling its script function immediately after. The problem with this is that dofile is called in every object's update function, so after the AILogic function runs and data for that script is updated, the whole thing just gets reset when dofile is called again for another enemy. My biggest question here is whether there is some way to retain the values in the script, even when I switch to running a different one.
I've read about function environments in Lua, but I'm not quite sure how to implement them correctly. Is this the right direction? Any advice is appreciated, thanks.
Edit: I've also considered creating a separate place to store that data rather than doing it in the Lua script.
Edit2: Added some sample code. (Just a test to get the functionality working).
-- Slime's Script
local count = 0;
function AILogic( Slime )
--Make the slime move in circles(err a square)
if count < 4 then
Slime:MoveDir( 0 );
elseif count < 8 then
Slime:MoveDir( 2 );
elseif count < 12 then
Slime:MoveDir( 1 );
elseif count < 16 then
Slime:MoveDir( 3 );
else
count = 0;
end
count = count + 1;
end

The lua interpreter runs each line as its own chunk which means that locals have line scope, so the example code can't be run as-is. It either needs to be run all at once (no line breaks), without locals, or run in a do ... end block.
As to the question in the OP. If you want to share the exact same function (that is the same function at runtime) then the function needs to take the data as arguments. If, however, you are ok with using the same code but different (runtime) functions than you can use closures to hold the local/individual data.
local function make_counter()
local count = 0
return function ()
local c = count
count = count + 1
return c
end
end
c1 = make_counter()
c2 = make_counter()
c3 = make_counter()
print(c1())
print(c1())
print(c1())
print(c1())
print(c2())
print(c3())
print(c2())
print(c3())
print(c2())
print(c3())
Alternatively, you could play with the environment of the function each time it is called, but that will only work correctly for some cases (depends on what the internals of the function are).

The canonical reference for this is link text. Explaining this briefly we'll work off the following code from the site:
a = 1
local newgt = {} -- create new environment
setmetatable(newgt, {__index = _G})
setfenv(1, newgt) -- set it
The first line sets up the (global) variable "a". You can view this as setting default values for your code. (Keep in mind that in Lua all variables are global unless you declare them with "local".)
The next line creates a table that will be your new environment. It is local to the function/chunk you're executing in so it won't be trashed by anything else that runs now or later.
The third line is the beginnings of the magic. To understand it you're going to have to understand metamethods In essence, however, you're using Lua's metamagic to ensure that any global names that aren't defined in your soon-to-be function environment get resolved in the context of your old global environment. Basically it means if you use a name that's not in your function environment, Lua will automagically hunt in the global environment you used to have to find the name. (In a word: inheritance.)
The fourth line is where you get what you're looking for. Setfenv(1,...) means that this changes the environment for your current function. (You could use 2 for the calling function, 3 for the calling function's caller, etc. on up the line.) The second parameter is the table you just set up, complete with inheritance of the old behaviour. Your function is now executing in a new global environment. It has all the names and values of the old environment handy (including functions and that global variable "a" you put in). If, however, you WRITE to a name it will not overwrite the global state. It will overwrite your local copy of it.
Consider the following subsequent code:
a = 10
b = 20
What you have done now is made your function environment table look like this:
{a = 10, b=20}
Your "global" environment, in short, contains two variables only: a (value 10) and b (value 20). When you access "a" later you'll get your local copy with 10 -- the old global value stored in your metatable is shadowed now and is still set to 1 -- and if you access "b" you'll get 20, despite the original global state likely not even having a variable "b" to access. And you'll still be able to access all the functions, etc. you've defined before this point as well.
Edited to add test code to debug OP's problem.
I put the following code into "junk.lua":
a = 1
local newgt = {}
setmetatable(newgt, {__index = _G})
setfenv(1, newgt)
print(a)
a = 10
print(a)
print(newgt)
The output of it is as follows:
$ lua junk.lua
1
10
table: 0x976d040
This is using Lua 5.1.4. What is the output you're seeing?

Related

Saving a "Measurement File" (Trace) while debugging in C++ with GDB

I am facing the following issue: In our project, we have a main class with a main method called "run". This method calls hundreds of other functions, classes etc.
We are now calling this run method in a test in a for loop multiple times, something like that:
for(float test_time = 0; test_time < 10.0; test_time += 0.005){
outputStruct = mainClass.run(inputStruct);
}
I now want to save all local variables of all functions and methods and all member variables of all included objects that are seen when this for loop is executed. And I want to have a snapshot of this for each loop iteration. So in this example, there should be like 2000 snapshots of all my variables.
Is this somehow possible? I see that GDB has some "trace" functionality, but it's not clear for me how I can tell GDB that it should save everything that was "seen" while executing the mainClass.run method. It should "only" remember the last state of each member and local variable. And when test_time increments, it can finalize the current snapshot and create a new one for the next time slot.
Is something like this possible? Since our usecase is some physics based scenario, it is every interesting to see how certain values change over time in a plot later. I don't mind what the format of the output file of GDB is, it will be parsed later anyway, as long as the information is somehow inside. Of course, the cleaner the file looks the better :).
Thank you for your support guys!

Lazy evaluation of Lauterbach macro - is it possible?

I'm currently writing some kind of a "skeleton" for tests to be performed using Lauterbach scripts.
In this skeleton I want to have a part in which all test specific definitions shall be done, e.g. functions to set breaks on, variables to be altered etc. This part shall be just near the top of the script file, so that other users do not have to go through the complete script file, changing values here and there.
Some of the variables that'll be used are defined function-local within the C-code to be tested. So, these become available to the Lauterbach script only once the scope of that function has been entered - which is deeply within the skeleton script code.
Is there a way to define a macro for these variables just way before the scope has been entered?
Let's give some example structure:
LOCAL &funcToTest // the function we want to test
LOCAL &varToBeSet // a variable within the function we want to alter
LOCAL &valueToBeSet // the value we want to set &varToBeSet to
... // some more definitions here
&funcToTest=someFunc
&varToBeSet=status
&valueToBeSet=1
... // some test code following here that sets up log files, screen areas
... // start the program to be tested etc.
IF (Register(PC)==ADDRESS.OFFSET(&funcToTest))
(
// OK - we've hit the breakpoint inside the function to test
... // Run to some point where we can set the local variable
Var.Set &varToBeSet=&valueToBeSet
... // Go on with the program and see what happens - this will be logged
)
The problem is that Lauterbach complains at the line &varToBeSet=status
with Symbol not found in this context - which is correct, because it is a local variable.
Looking at the symbols via View->Symbols->SymbolsTreeView (or by giving the command Symbol.List.Tree) I can find the symbol (in this particular case found under the node some_module.some_function.status). Clicking on it gives the information in the TRACE32 status status line \\some_app\some_module\some_func\status with type (auto STATUS), scope local, location stack.
Changing my script to read &varToBeSet=\\some_app\some_module\some_func\status instead of &varToBeSet=status, however, does not help much. In this case Lauterbach complains with no access to that symbol.
Is there a way, I can delay evaluation of the macro to some point where it is actually used instead of having it evaluated when it is defined?
Use quotes:
&varToBeSet="\\some_app\some_module\some_func\status"

Maintaining lua/c++ variables through through multiple script calls

I'm pretty new to lua scripting, but want to incorporate scripting into my game engine. Currently I have lua linked up to c++ in a basic manner where I can call methods from c++ classes and vise-versa. Lua scripts are setup as components and you can essentially attach scripts to a game object. However, I'm having an issue where my variables are being reset each time a script is called. Here is a basic example of a script:
--lua script--
input = InputManager()
function initalize()
end
function update()
if(input:KeyDown(KEY_SPACE)) then
print("Random before: ", input.random)
input.random = input.random + 10
print("Random after: ", input.random)
end
end
Initialize is called when the script component is attached and update is called every frame. In this example, in my c++ InputManager class that I bind up to lua, I have a integer random that is initialized to 5. I'm using lunafive properties to control the getting and setting of variables.
When I hit space i expect the first iteration to print out:
Random before: 5
Random after: 15
And then the next iteration:
Random before: 15
Random after: 25
However, every iteration I get that first result of 5 and 15. I realize this is because I'm loading the script each update method so it is probably just creating a new instance of InputManager.
How do I go about creating an instance of an object (i.e InputManager) and maintain that same instance every time I call update.
That way I could do something like this:
input = nil
function initalize()
input = InputManager()
end
function update()
print(input.random)
end
and not have an undefined instance of input once update is called. Hopefully this all makes sense, if not I can clarify! Any help is much appreciated!

Functions using global variables as inputs

So, I'm writing some python scripts and I have some code like the one shown below:
a = 3
dict = {"run":runMiles(a)}
a = 5
The runMiles func takes one variable that is an int. For some reason when dict["run"] is called, the variable doesn't seem to use the "new" variable. It is important to realize that both a and dict are global variables
It does so because the function runMiles() executes at the time this line dict = {"run":runMiles(a)} executes and when you call dict["run"] it will only give the return value got after execution of the function and does not execute the function again to fetch the value of "run" and thus the value does not get updated.

Lua preserving global values

I use Lua for my game engine logic. My main game loop is not done in Lua. Only special nodes in my scene hierarchy have Lua scripts attached. These scripts are executed every frame. The problem I face is that I need to keep global variable values from one frame to another.
My temporary solution looks like this:
finish = useBool("finish", false)
timer = useInt("timer", 0)
showTimer = useBool("showTimer", true)
startTimer = useInt("startTimer", 0)
play0 = useBool("play0", false)
play1 = useBool("play1", false)
play2 = useBool("play2", false)
play3 = useBool("play3", false)
delta = useInt("delta", 0)
gameOverTime = useInt("gameOverTime", 5000)
finishTime = useInt("finishTime", 5000)
checkPoint = useInt("checkPoint", 255)
<...> Game logic <...>
setInt("message", message);
setInt("checkPoint", checkPoint)
setInt("finishTime", finishTime)
setInt("gameOverTime", gameOverTime)
setInt("timer", timer)
setBool("play3", play3)
setBool("play2", play2)
setBool("play1", play1)
setBool("play0", play0)
setInt("startTimer", startTimer)
setBool("showTimer", showTimer)
setInt("timer", timer)
setBool("finish", finish);
I call special methods that retrieve global variables from hash maps in C++ at the beginning and I set them again at the end of the script.
Is there a way to do this implicitly?
Is it a bad design to use Lua not as the main game loop?
Well, while there is nothing technically wrong with your solution, you might start to notice some performance issues if you end up with a lot of global variables (something you should, in general, avoid).
With that said, there is room for improvement. For example:
At the beginning of the script, check if your global variable is nil. If it is, then you can initialize it, if not, this is probably not the first time you're running the script, so leave it unmodified. But that means a lot of pesky if-else statements, which one can easily forget about. We can do better!
I would recommend looking at Chapter 14: The Environment, from the Programming in Lua book. Here's a quick quote from the intro:
Lua keeps all its global variables in a regular table, called the environment. ... The other (actually the main) advantage is that we can manipulate this table as any other table. To facilitate such manipulations, Lua stores the environment itself in a global variable _G. (Yes, _G._G is equal to _G.)
Since _G is a table, it also has a metatable, so you can define __index and __newindex metamethods to handle access to and creation of global variables. You can find examples of this in section 14.2. Go read the whole chapter, it's not that long (if you're unfamiliar with metamethods and metatables, also look through chapter 13 - this is where Lua really shines in terms of flexibility).
Now that we've covered the trivial and normal methods, let's look at the overkill end of the spectrum. As an example I'll look at Unity's approach to scripting. A Unity javascript usually defines variables, functions, and types. Any variables defined outside of the scope of methods or types are persisted between frames because the script itself is not executed every frame. Instead, they let the script define functions and call the functions at the appropriate time. So if you want something executed every frame - you put it in the Update function. Every script can define it's own Update function because it has it's own scope. So every frame the scripting engine goes through all objects, checks if the script's scope has an Update method and calls it.
Back to Lua - a solution like this would involve creating separate environments for each object/script/whatever your node is. Then, instead of executing the script attached to your node every frame, your main loop will go through all the nodes and run a function inside of their environment. You can also switch environments, so you can set the global environment to your node's env before executing it, and then switch back when you're done. This allows your scripts to use globals as they see fit, have them persisted between frames and excludes the possibility of name collisions or global namespace pollution. Additionally you can use metamethods to nest the node's environment inside the actual global environment or inside an API environment with helper methods (basically, if __index does not find something it looks it up in a parent).