Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411283 Posts in 69325 Topics- by 58380 Members - Latest Member: bob1029

March 29, 2024, 05:03:41 AM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsCommunityTownhallForum IssuesArchived subforums (read only)TutorialsAltering Source Code at Run-Time
Pages: [1]
Print
Author Topic: Altering Source Code at Run-Time  (Read 5566 times)
AndrewFM
Level 2
**


whoops


View Profile WWW
« on: April 11, 2010, 06:28:42 PM »

In most programming languages, the code within your program is static during run-time. Dynamic actions are usually simulated by branching to different sections of your code using conditional statements.

However, Game Maker provides some powerful functions to temporarily alter, append to, or delete from your source code during run-time. Those functions are what we are going to explore in this tutorial.


Part One
The execute_string() and execute_file() Functions

execute_string(codestr) accepts a single string argument. The function will take that string, and execute it as if it were a piece of code. For example:

Code:
execute_string("foo += 1;");

That would increment the variable foo by 1. However, in that example, we used a static string. The power of execute_string() can only be seen when we use it dynamically. Consider this next example:

Code:
code = "foo += 1;"; //Executed once at game initialization.
...
execute_string(code); //Executed some place else

This will do the same thing as the previous example. However, unlike the previous example, we're now using a variable. This is important to note, because the values of non-constant variables can be changed while the program is running. This means that by changing the value of code during run-time, we can change the code that gets executed.

Another important thing to realize is that by using string concatenation, we can alter fragments of a code action that couldn't otherwise be edited. In the statement foo += 1;, the only thing that could normally be changed by utilizing variables is the value "1". The arithmetic operator += could not be changed, and the variable being incremented, foo, also could not be changed. No matter what trickery you used, those two things would normally have to be a static part of your code.

However, consider this next example:

Code:
operators[0] = "+=";
operators[1] = "-=";
operators[2] = "*=";
operators[3] = "/=";
usage = -1;

while (usage < 0 || usage > 3)
  usage = get_integer("Add(0), Subtract(1), Multiply(2), or Divide(3)?",0);

execute_string("foo "+operators[usage]+" 1;");

As you can see, we were able to explicitly alter the operator that was being used, using nothing but a variable, and string concatenation. A bulky if/else structure was completely avoided.

For a more practical example of this, think of Game Maker's Script system. It is fairly primitive compared to the function building of most object oriented languages, and does not allow you to pass in an array as an argument. However, by passing the name of an array as a String, and using some string concatenation trickery, we can use execute_string() to bypass that restriction:

Code:
/*
SCRIPT scr_arrayAdd(arrayName,index,value);

Adds a specified value to an index of an array.

arrayName: The name of an array to add a value to.
index: The index of the array to add to.
value: The value to add.
*/

execute_string(argument0 + "[" + string(argument1) + "] += " + string(argument2) + ";");

Code:
scr_arrayAdd("foo",0,1); //foo[0] += 1;
scr_arrayAdd("foo",1,4); //foo[1] += 4;

execute_file(filename) is another function that works very similarly to execute_string(). The only difference is that you pass it a file name, rather than a String. It takes the text file you specify, reads it in, and then executes the contents of the file as code.

Unlike execute_string(), which will normally be used to execute a single code action at a time, execute_file() can be used to execute large batches of code. However, there's some stuff you need to watch out for.

Whenever you use execute_string(), or execute_file(), it does not append the data to your source code. Every time that function is reached, it takes the specified code, compiles it, executes it, then discards it. This is an intensive process, and with any large quantity of code being executed in this way, you will begin to experience slowdowns. Ironically, this problem is even worse with execute_file(), because it is adding another step onto the process. Not only must it compile, execute, and discard, it must also read from a file.

Luckily, there is a solution to this. Game Maker has some other functions which allow you to temporarily append code to your source code during run-time. Using those functions, the code is only compiled once, and from that point on, it's no more intensive than if the code had been hard-coded by yourself in the code editor. We shall talk about those methods in...


Part Two
The Appending and Deletion Functions

In this section, we will explore the following functions:
  • object_add()
  • object_delete()
  • object_event_add(ind,evtype,evnumb,codestr)
  • object_event_clear(ind,evtype,evnumb)

object_add() takes no arguments, and allows you to add a new object to your game, during run-time. This object will exist until you delete it, or until the game is closed. The function returns the reference to the new object, so make sure to store it in a variable:

Code:
global.obj_new = object_add();

By convention, you should always make that variable global, because all objects in your game should be accessible from anywhere (This is not to be confused with instances). The new object will contain no events, and no code; it'll be exactly how a new object would be when you press the "Create an Object" button in the editor.

You can delete an object with object_delete(ind), where ind is the object to delete. Like object_add, this is not a permanent operation; everything will be reverted back to normal when the program is closed.

object_event_add(ind,evtype,evnumb,codestr) will append code to the end of an event in a specified object. ind is the object to add the code to. evtype and evnumb specify which event the code should be added to (Refer to the event_perform() section of the GM Help File for details on what to put for these arguments). If the event does not exist, it'll be created. codestr is a string containing the code to be added to the event.

While this will typically be used to add data to the new objects we create with object_add(), it can also be used to append data to already existent objects.

Now, track back to execute_file() from the previous section. If we wanted to use execute_file() to execute code every step (which would create too much lag to be practical), we could use these new functions to replicate that functionality instead:

Code:
code = ""; //Will hold the code from the file

//////////////////////////////
//READ FILE
//////////////////////////////
data = file_text_open_read("file.txt");

while(!file_text_eof())
{
  code += file_text_read_string(data);
  file_text_readln();
}

file_text_close(data);

//////////////////////////////
//CREATE NEW OBJECT WITH CODE
//////////////////////////////
global.obj_new = object_add(); //Adds the new object
object_event_add(global.obj_new,ev_step,ev_step_normal,code); //Adds the code to the step event
instance_create(0,0,global.obj_new); //Creates a new instance of the new object

Notice that in the above code, I added all of the code to a single string, and added that string to the object's event. It had to be done this way, because despite the fact that object_event_add() appends to an event, we cannot add the code line by line in this case.

The reason behind this is that the new code you add is complied immediately as you use the object_event_add() function. If the code statement added is syntactically incorrect, it will throw off an error. For example, lets assume we tried to add the following code to the object's event line by line:

Code:
if (foo > 0)
{
  foo -= 1;
}

That would throw off three consecutive errors. It would tell you that "if (foo > 0)" is invalid, because there is no action to execute if the if statement is true. It would tell you that "{" and "}" are invalid, because they're used alone, out of context. The only thing that wouldn't throw off an error is "foo -= 1;", because that's a valid piece of code by itself.

For this reason, it's safest to read in the file, and add all of it to a single string, then add that string to the object. However, you must also be careful when doing this. Your entire code will essentially be represented on a single line. If you don't take this into account, some things can cause your code to error out strangely, and unexpectedly. For example, if you include a single line comment anywhere in your code, EVERYTHING else in the code after that comment will be commented out.

object_event_clear(ind,evtype,evnumb) will take an event in an object, and delete all of the code that is inside it. We can use this either when we no longer need the event to execute our code, or when we want to rewrite the entire event from scratch. Ponder that last statement for a second, as this is the revelation we were working towards.

Lets imagine you were working on a piece of code. A terrible, tedious, trial-and-error based piece of code, which requires endless tweaking. Now, enlightened after reading this tutorial, you pull that code out of the editor, and throw it in a text file. You write a short script to read the code from the file, and add it to the correct event in your object.

Now, you add a code to be executed when you press a certain button. The code does something along the lines of this:

Code:
object_event_clear(...); //Clear the contents of the event you added

//Read the code from the file again

object_event_add(...); //Add the code back to the event you just cleared

Now, during run-time, you can edit the text file, press the button in-game, and the game will instantly update itself with the new code. No need to restart or recompile the game. All it takes is a single button press, and about 1/15th of a second worth of processing time.
« Last Edit: April 12, 2010, 03:07:47 AM by AndrewFM » Logged
Angelcape
Level 0
*


View Profile
« Reply #1 on: April 11, 2010, 07:00:37 PM »

Wow, way to like, 1-up me on this.
Logged
Linus
Level 0
***


View Profile
« Reply #2 on: April 11, 2010, 11:02:21 PM »

Hum.
It is, perhaps, noteworthy to mention that Dynamic Programming is something completely different in computer science. Namely, a method for constructing algorithms when solutions can be optimized based on previous steps.
What you're doing is more akin to scripting, but lacks the safety net of the sandboxing a script has.
If, at any point in your code, via for example string concatenation, the player is given access to modify the string, they'd have full access to all local variables where it is executed and all global APIs as well. Now, I don't know much about GM, but in other programming environments, this is considered bad. Shrug


Linus
Logged
Kekskiller
Guest
« Reply #3 on: April 11, 2010, 11:52:01 PM »

The possibility of adding code onthefly itself sounds more like procedurally generated code.
Logged
AndrewFM
Level 2
**


whoops


View Profile WWW
« Reply #4 on: April 12, 2010, 02:38:27 AM »

Hum.
It is, perhaps, noteworthy to mention that Dynamic Programming is something completely different in computer science. Namely, a method for constructing algorithms when solutions can be optimized based on previous steps.
What you're doing is more akin to scripting, but lacks the safety net of the sandboxing a script has.
If, at any point in your code, via for example string concatenation, the player is given access to modify the string, they'd have full access to all local variables where it is executed and all global APIs as well. Now, I don't know much about GM, but in other programming environments, this is considered bad. Shrug


Linus

Thanks for mentioning that. I just kind of came up with that name off the top of my head. I'll go edit the first post to fix up the confusion.

I never thought of that other issue, though. However, Game Maker itself is pretty bad with that to begin with. While there is a such thing as local variables in GM, there's no such thing as private variables. An object's local variables are treated as public variables, so they are still accessible to all other objects via the object's reference.
« Last Edit: April 12, 2010, 03:08:00 AM by AndrewFM » Logged
Skofo
Level 10
*****



View Profile
« Reply #5 on: April 12, 2010, 10:05:34 AM »

Sounds messy. Concerned
Logged

If you wish to make a video game from scratch, you must first invent the universe.
moi
Level 10
*****


DILF SANTA


View Profile WWW
« Reply #6 on: April 12, 2010, 11:24:50 AM »

What's the practical reason of doing that in GM ?
Logged

subsystems   subsystems   subsystems
Linus
Level 0
***


View Profile
« Reply #7 on: April 12, 2010, 02:15:02 PM »

Since I haven't ever used GM, I can't say for sure if capabilities for this already exist, however:

With run-time editing of code, you could do several things that you'd otherwise use a scripting language for, at the same time making it more flexible at the cost of security.
What I mean is, you could have drop-down boxes that contain code and, perhaps, a pause button. You'd hit pause, modify the code and run it again, giving you real-time updates on your game behaviour.
Other than this, I don't see it enabling any new possibilities. Rather, it gives you new ways to do old things. Whether or not you prefer doing them the old way is a different question. :p
Logged
AndrewFM
Level 2
**


whoops


View Profile WWW
« Reply #8 on: April 12, 2010, 03:46:23 PM »

In terms of problem solving, as Linus said, there's usually a way to solve the problem without using this. For the most part, you'll turn to more familiar and comfortable methods. Not to mention, when you use this, the code can start to look ugly, and unintelligible. Just look at the scr_arrayAdd() example in the first post; that's certainly not pretty looking.

However, I do find that in some circumstances, this can lend to an easier solution to a problem. For example, consider programming a calculator. You can have the individual buttons on the calculator append numbers and operators onto a string, and then have the equals button execute that string.

It's sort of akin to using a recursive algorithm rather than a loop. Sure, all recursive algorithms can be written as loops, and recursion is generally not as efficient, but certain problems can lend themselves to using it.

The most common reason I use this is not so much for the problem solving, but more for the capability it can give you to edit code on the fly (As expressed in Part 2). I find it's extremely convenient being able to tweak aspects of your game while it's running. This is opposed to using a debug console, writing code to manually tweak it via developer hotkeys, or just restarting the game over and over for every little change you make to the code.
Logged
Angelcape
Level 0
*


View Profile
« Reply #9 on: April 12, 2010, 04:03:07 PM »

I think this is a very useful way to debug your game without stopping, looking at the code, and then making some small adjustments and then rerunning your game.  Also, there is no run-time editing in GM.
Logged
BlueSweatshirt
Level 10
*****

the void


View Profile WWW
« Reply #10 on: April 12, 2010, 04:14:03 PM »

The only real advantage I see(that truly is an advantage) is that it'd be much easier to update the game-- Updating and changing some object files and such, rather than sending out a whole new packaged game file.
Logged

Codestar
Level 1
*

One man, one game


View Profile WWW
« Reply #11 on: April 13, 2010, 06:55:01 AM »

You know... there are dll's for using python and lua in gamemaker that could do pretty much all of this and more.  Giggle
Logged



Check out my current game project:
Mine to the sky
ஒழுக்கின்மை (Paul Eres)
Level 10
*****


Also known as रिंकू.


View Profile WWW
« Reply #12 on: April 20, 2010, 09:56:57 AM »

What's the practical reason of doing that in GM ?

very little. the most practical use is a console: allowing you to type in code as you play the game, and have it be executed after you type it in. useful for development. or for meta-games, level editors, etc.

but execute_string() is so incredibly slow that you'd have to be a moron to use it in the step or draw event. definitely restrict it to individual events that happen once every few seconds or something.
Logged

Pages: [1]
Print
Jump to:  

Theme orange-lt created by panic