ENIGMA Forums

Outsourcing saves money => Programming Help => Topic started by: legendarysnake on April 21, 2014, 09:02:30 am

Title: function error
Post by: legendarysnake on April 21, 2014, 09:02:30 am
getting error with functions "execute_string" and "variable_local_exists"
Title: Re: function error
Post by: TheExDeus on April 21, 2014, 09:37:52 am
Because they don't exist. While "variable_local_exists" is possible, but inefficient (because that would require storing all variable names as strings in the code) we don't see a real use for it. execute_string also will probably never going to be supported. We since the beginning (like GM:S until recently) use compiled C++ for the games. So we don't have any parser or JIT compiler to execute code on the fly. If you want scripting in a game, then maybe a Lua extension could be made.

Both of these functions are the "Dark side" of coding though - if you think you need them for something, then most probably you don't. They allow large security holes (like using registry_ functions in the execute_string), crashes and bugs.
Title: Re: function error
Post by: legendarysnake on April 21, 2014, 10:40:47 am
var xch,spr_frame,spr_head,spr_body;
if (image_xscale == 1) xch = floor(x);
if (image_xscale == -1) xch = ceil(x);
spr_frame = sprite_get_name(sprite_index)+'_frame';
spr_head = sprite_get_name(sprite_index)+'_head';
spr_body = sprite_get_name(sprite_index)+'_body';
spr_frame = execute_string('spr_frame='+spr_frame);
spr_head = execute_string('spr_head='+spr_head);
spr_body = execute_string('spr_body='+spr_body);

how it would be replacing execute_string?
Title: Re: function error
Post by: TheExDeus on April 21, 2014, 01:08:40 pm
Quote
var xch,spr_frame,spr_head,spr_body;
if (image_xscale == 1) xch = floor(x);
if (image_xscale == -1) xch = ceil(x);
spr_frame = sprite_get_name(sprite_index)+'_frame';
spr_head = sprite_get_name(sprite_index)+'_head';
spr_body = sprite_get_name(sprite_index)+'_body';
spr_frame = execute_string('spr_frame='+spr_frame);
spr_head = execute_string('spr_head='+spr_head);
spr_body = execute_string('spr_body='+spr_body);

how it would be replacing execute_string?
That code doesn't even make sense, because "spr_head = execute_string('spr_head='+spr_head);" would make spr_head = 0 (execute_string() by default return 0). But why can't you just do this?:
Code: [Select]
spr_frame = spr_image_frame;
spr_head = spr_image_head;
spr_body = spr_image_body;
There are many ways you could do this better. Like create a script for each different "model" and on init just run a specific script (which can be dependent on sprite_index if you want), like:
Code: [Select]
//scr_init_alien - Script for sprite_index = "alien"
spr_frame = spr_alien_frame;
spr_head = spr_alien_head;
spr_body = spr_alien_body;
And in create event:
Code: [Select]
switch (sprite_index){
  case spr_alien: spr_init_alien; break;
}

Another method if you really want to do it this way is using sprite_get_name() in a loop. Like:
Code: [Select]
//scr_find_sprite_from_string(sname) - I love very long function names like these :D
var i;
i=1;
while (true){
var sprite;
sprite = sprite_get_name(i);
if (sprite == "<undefined>") return -1;
if (sprite == argument0){
return i;
}
i+=1;
}
Then you can do scr_find_sprite_from_string(sprite_get_name(sprite_index)+'_head'). You should be fine if you use this only when loading or on create. But doing this per-step would be quite slow. And there is no valid reason you should do it per step.
Title: Re: function error
Post by: Goombert on April 21, 2014, 01:53:38 pm
Harri execute_string does return a value, I just tested the following in GM8.1
Code: (EDL) [Select]
show_message(string(execute_string("return 50;")));And sure enough I got a message back with "50" in it. The behavior is just not documented I guess.

http://enigma-dev.org/docs/Wiki/Execute_string
http://enigma-dev.org/docs/Wiki/Execute_file

They work similar to script execute.

http://enigma-dev.org/docs/Wiki/Script_execute
Title: Re: function error
Post by: TheExDeus on April 21, 2014, 03:47:05 pm
Quote
Harri execute_string does return a value, I just tested the following in GM8.1
I know it returns a value.
Quote
spr_head = 0 (execute_string() by default return 0)
Assignment on the other hand shouldn't. This should "return spr_head=spr_some_head".
Title: Re: function error
Post by: legendarysnake on April 22, 2014, 05:13:55 am
what i can use instead of variable_local_exists?
Title: Re: function error
Post by: TheExDeus on April 22, 2014, 05:50:24 am
Right now nothing. You shouldn't even use it. If you use it just for finding out if something is enabled (like if a unit has "attack"), then make a grid, a list or even better - a map - having all these parameters. Just like you would do in any other compiled language like C++ or bytecode Java. Code like "if (variable_exists("variable") == false) variable = 0;" is bad design and shouldn't be used. That is why GM:S also no longer supports execute_string and variable_exists. They made people do bad stuff with it. Especially when things like execute_string ended up being used for sprite index, which was far from what Mark Overmars made it for.
Title: Re: function error
Post by: Josh @ Dreamland on April 22, 2014, 07:40:39 am
No, Harri; what he's presented is the number one use case for execute_string. We ought to have functions to look up a resource ID by name. It would require eight bytes of memory by default until first call, at which point it'd build a tree for later use, then use it to look up the sprite.

Variables always exist, legendarysnake. In ENIGMA, the instance creation event is performed after the object creation event, so you can override variables set in create from the instance creation code in rooms. That's the only reason people ever need to use variable_local_exists. Otherwise, use a ds_map.
Title: Re: function error
Post by: TheExDeus on April 22, 2014, 01:31:42 pm
Quote
No, Harri; what he's presented is the number one use case for execute_string.
I am aware. But that wasn't the original intention for it. Mark would of just made a custom function for that if he thought it was needed.

Quote
We ought to have functions to look up a resource ID by name. It would require eight bytes of memory by default until first call, at which point it'd build a tree for later use, then use it to look up the sprite.
I guess we could. We already store resource names in the exe.

Quote
Variables always exist, legendarysnake. In ENIGMA, the instance creation event is performed after the object creation event, so you can override variables set in create from the instance creation code in rooms. That's the only reason people ever need to use variable_local_exists. Otherwise, use a ds_map.
Do variables also exist if the variables are actually used only in step event for example? Like in step you add "a = 0" and in create you add "if (variable_exists("a") .."? Because that should logically return false. The use case for variable_exists() was to check in other scripts if the object had a specific attribute. Like if I had scripts for buttons, I would make a button_add() script and in it the first line would be (if variable_exists("button_grid")==false button_grid = ds_grid_create(0,0)). So by doing this I wouldn't need to have a button_init() script. And while I did that in GM, I find it a bad thing now. And so in ENIGMA I just add _init() scripts instead.
Title: Re: function error
Post by: Josh @ Dreamland on April 27, 2014, 10:33:28 pm
What I mean is that the memory allocated for the variable exists. It's true that these values can be uninitialized, at least for var/variant. In fact, we attempt to do so now, at the user's option. We could technically allow something like this:

Code: (cpp) [Select]
bool variable_initialized(const variant& x) {
  return x.type != -1;
}

This would offer the same functionality, provided the option "treat uninitialized variables as value 0" was not set.I believe we disable that functionality in release mode, presently, but that's not to say we can't undo that. In general, though, I'm opposed to this; I prefer to be able to seamlessly convert var and variant to primitive types. Before that can happen, of course, I will still need to add checking for function calls that only take the var/variant reference. So theoretically, implementing those functions wouldn't harm too much, at that stage. But I still don't encourage the practice.

But to answer your original question, yes, if you are using the local variable a, it still exists, even if you never use it outside the step event. You are able to access it in other objects using object0.a, or whatever.

The use case you give is archaic at best. If you want other entities to assign that for you, which is arguably way too intimiate (but standard, poor practice in GM), then the solution is to set it to -1 in the create event and check that in the script or whatever. In other words, [snip=edl]if (button_grid == -1) button_grid = ds_grid_create(0,0);[/snip]. This is always safe, because the create event is always performed first. So you'll never accidentally overwrite something by setting it to -1 in create.
Title: Re: function error
Post by: Goombert on April 27, 2014, 11:50:29 pm
This might be a good time for me to ask Josh, will the following code work in ENIGMA right now?

Code: (EDL) [Select]
var ass = 4;
delete ass;
Title: Re: function error
Post by: TheExDeus on April 28, 2014, 04:30:49 am
Quote
the solution is to set it to -1 in the create event and check that in the script or whatever.
But then if I try to make it a "system" (or something akin to a "class"), then I shouldn't make the user set some random inner data structures in their create event. The whole point was that a user includes the scripts and calls button_add(...) in create and buttons_draw(...) in draw. In this case it is better to either set up everything in button_add() (which requires checking if something exists) or to create a button_init() script.
Maybe a better way would be to create a new object like obj_buttons that works like a class. And then when calling button_add() you check whether the object exists and if doesn't, then create it. If it does then add a button to it. But I wanted my scripts to allow adding buttons to many objects (so for each menu I would have a controller object having the buttons).
If we implement C++ classes, then I would probably use those.

Quote
var ass = 4;
delete ass;
That shouldn't even work in C++. You have to use (or in fact only CAN use) delete if you use "new". If you never used new, then using delete is a good way to segfault. It should be something like this:
Code: [Select]
var* ass = new var();
delete ass;
Title: Re: function error
Post by: Goombert on April 28, 2014, 04:42:08 am
My mistake, nevertheless, it does not segfault and appears to work Harri, I just tested it.
Title: Re: function error
Post by: Josh @ Dreamland on May 11, 2014, 04:43:05 pm
If that works for any reason, it's because it's getting parsed into something entirely different. But it shouldn't even compile...