Pages: 1 2 »
  Print  
Author Topic: Accessing with types and passing by strong reference  (Read 7465 times)
Offline (Unknown gender) TheExDeus
Posted on: July 14, 2014, 06:42:19 am

Developer
Joined: Apr 2008
Posts: 1860

View Profile
I think I have made a topic about this before, but I can't remember, so here it goes again, with some new insights.
As we all know in GM every ID for resources (LGM like sprites, background or runtime like ds_grid) is an integer. I always lowed this in GM, because it makes learning A LOT easier, as in C++ this usually involves pointers or references. But this makes code messy, as the engine doesn't know if the number "5" gives is an ID for sprite or a background. Here are some suggestions:
1) I previously suggested we just use classes (both return and pass to functions), as the user wouldn't know any better. He might be confused why "sprite_id*5" doesn't work, but that shouldn't work in any case. But it does mean problems with variables. By default every variable is a "variant", which means "variant" would have to cast to "Sprite" and "Background" and so on. I don't think that is possible, as it might only cast to "*Sprite" at best, and that would be required for this to work:
Code: (EDL) [Select]
sprite_id = spr_some_guy;We should pass references anyway, as I don't want to copy data in this instance.
2) Create a typedef which basically wraps integers. This seems like the best option and in my ultimate wisdom I didn't come up with it before. Basically we would have a type define as "Sprite_ID" and it would basically be an integer in every shape or form. The only difference is that if I pass a number 5 of type "Sprite_ID", then I know it's a sprite ID, not something else. The problem here is that we still need to cast variant to these types. Like this:
Code: (EDL) [Select]
a = 5; //Here variant of type variant with value 5
a = spr_some_guy; //Here the variant needs to be cast, which I don't know if is even possible taking into account the POD nature
Also, this would mean we ALWAYS would have to use sprite name or the ID returned by sprite_add, to reference this sprite. This means we couldn't just load an integer from a file, and then use it as an ID. But the user shouldn't be doing that anyway, as he cannot know which ID the sprite will be assigned to. So I think we are okay in this regard. Slightly break GM compatibility, but greatly increases the future prospects we could have. Like we could replace draw_sprite, draw_background and draw_surface with one function - draw_image. And we could even allow draw_sprite's image frame argument to be supported via an overload.
3) Another way which would only partly fix this, is to have a unified ID vector. Like all sprites, backgrounds, rooms, instances etc. would use the same map or vector. This would mean every ID would be 100% unique, and thus identifiable with a certain kind of resource. But this greatly reduces the maximum of amount of resources possible (maximum integer can hold, which might not be enough if they include all instance of all rooms and even all tiles).

But I don't really suggest totally getting rid of all those functions. I think they can stay for the time being as they are required for GM compatibility, and we are quite used to them. What I actually need this functionality for is new stuff, especially extensions. Like I am in process of making a GUI extension (something on the lines mentioned in the wiki here: http://enigma-dev.org/docs/Wiki/GUI_Functions) and I want to support these two case:
Code: (EDL) [Select]
button_set_image(button,spr_button1);
button_set_image_hover(button,bg_button1);
Instead of:
Code: (EDL) [Select]
button_set_image_sprite(button,spr_button1);
button_set_image_hover_background(button,bg_button1);
And the second case is with my own classes:
Code: (EDL) [Select]
button_set_parent(button, window1);
window_set_parent(window1, window2);
Instead of bazillion variants like this:
Code: (EDL) [Select]
button_set_parent_window(button, window1);
window_set_parent_window(window1, window2);
So basically I want the ID returned by functions like button_create(), not to be an integer without any type information, but identifiable as an instance of a class.

One way to fix this would be to ask users to code in C++. So they could in fact write:
Code: (EDL) [Select]
Button button1 = button_create();And then use "button1" everywhere as it's a C++ class. But I don't want that. I want the user to be able, to write:
Code: (EDL) [Select]
button1 = button_create();
In my case maybe the third option could work, as I basically would have a GUI_MAP having all the GUI elements which would mean that every one will have a unique ID. But I wouldn't be able to use this for other resources like sprites or backgrounds.

So this topic is mostly addressed to Josh, as he deals with EDL. How would you propose to fix this?
Logged
Offline (Unknown gender) sorlok_reaves
Reply #1 Posted on: July 14, 2014, 10:19:37 am
Contributor
Joined: Dec 2013
Posts: 260

View Profile
This would have the added benefit of fixing a weird interaction with "assume default variables are 0" and having identifiers just be integers. So this would be a great feature from my point of view!

1) I previously suggested we just use classes (both return and pass to functions), as the user wouldn't know any better. He might be confused why "sprite_id*5" doesn't work, but that shouldn't work in any case. But it does mean problems with variables. By default every variable is a "variant", which means "variant" would have to cast to "Sprite" and "Background" and so on. I don't think that is possible, as it might only cast to "*Sprite" at best, and that would be required for this to work

variants (or possibly vars?) contain a "type" in addition to a value. So storing a "class sprite_id" in a variant can be done by storing the integer value in the variant's "value", and storing an enum (sprite_id) in the variant's type. Then, when casting to a "sprite_id", you can fail (or return -1) if the types don't match.


Also, this would mean we ALWAYS would have to use sprite name or the ID returned by sprite_add, to reference this sprite. This means we couldn't just load an integer from a file, and then use it as an ID. But the user shouldn't be doing that anyway, as he cannot know which ID the sprite will be assigned to.

Would this prevent external music files from working correctly? Those are often distributed with a game in a separate directory, and loaded dynamically at runtime. (I may be misunderstanding how your typedef solution is supposed to work.)
Logged
Offline (Male) Rusky
Reply #2 Posted on: July 14, 2014, 10:21:02 am

Resident Troll
Joined: Feb 2008
Posts: 954
MSN Messenger - rpjohnst@gmail.com
View Profile WWW Email
typedefs are interchangeable- it would have to be a struct with an int member to actually enforce the types. It would be interesting to add the resource types to the possibilities for variant rather than casting everything.
Logged
Offline (Unknown gender) Darkstar2
Reply #3 Posted on: July 14, 2014, 12:15:33 pm
Member
Joined: Jan 2014
Posts: 1238

View Profile Email
You've mentioned the implications on reducing / limiting resources available to use (instances, etc.)  This could have an impact for larger games development, can't we keep the existing functions/method and add a new set in parallel or your last C++ suggestion is good, and people could create their wrappers ?

Logged
Offline (Unknown gender) TheExDeus
Reply #4 Posted on: July 14, 2014, 12:43:33 pm

Developer
Joined: Apr 2008
Posts: 1860

View Profile
Quote
Would this prevent external music files from working correctly? Those are often distributed with a game in a separate directory, and loaded dynamically at runtime. (I may be misunderstanding how your typedef solution is supposed to work.)
If you use sound_add() or something, then it would return Sound_ID, so there wouldn't be a problem. What I meant was that if you load something that returns an integer, then you wouldn't be able to use it as a resource without specific casting. I guess you could still always do "sound = (Sound_ID)5;". But this shouldn't really be a problem, because any function that actually creates or adds a resource, should return the right type.

Quote
You've mentioned the implications on reducing / limiting resources available to use (instances, etc.)
It's only valid for the third solution. Of course don't get me wrong, 32bit int can store from 0 to 4,294,967,295 - 64bit can store 0 to 18,446,744,073,709,551,615. Those numbers are so large, it's very improbable you would have issues. Problems could arise if we port to some embedded devices where int maybe only 16bits (though I don't know any except microcontrollers which ENIGMA will never support), then you could only have 0 to 65,535. But as I mentioned in the post - this solution doesn't work on custom resources, because then we would require a much complex system that allows adding stuff from extensions to this container. It also removes (or rather complicates) some possibilities for threaded resource management, as multiple threads would use the same container.

Quote
typedefs are interchangeable- it would have to be a struct with an int member to actually enforce the types. It would be interesting to add the resource types to the possibilities for variant rather than casting everything.
True, compiler will not error for wrong types. Maybe we only need EDL parser to error at this? But that is why I am waiting Josh's input on this. He would probably have to add it too.
Logged
Offline (Unknown gender) Darkstar2
Reply #5 Posted on: July 14, 2014, 02:16:26 pm
Member
Joined: Jan 2014
Posts: 1238

View Profile Email
In any case I have some plans on my own for ENIGMA for large game developers, I am planning to add dynamic resources handling, packing/unpacking resources from external files directly in memory, and every aspect of the game including rooms etc to outside files, making possible multi GB games and IDE remaining compact and lightning fast.

So in theory the limitations would be for instances/resources present in memory, once external resource handling is into play all that would be irrelevant.  That is the system used by nearly all commercial games now anyway.

As far as threaded resources management, that is exactly where the dynamic resource handling falls under. Ability for such developers to use progression bars or dynamic loading screens, with animation, progression, etc whilst the resource(s) is/are being loaded.
Logged
Offline (Unknown gender) TheExDeus
Reply #6 Posted on: July 14, 2014, 05:36:57 pm

Developer
Joined: Apr 2008
Posts: 1860

View Profile
It doesn't matter where the resources reside. You still need to have a way to reference them. And you do it via an ID. That is what this topic is about.
Logged
Offline (Unknown gender) sorlok_reaves
Reply #7 Posted on: July 14, 2014, 08:42:02 pm
Contributor
Joined: Dec 2013
Posts: 260

View Profile
If you use sound_add() or something, then it would return Sound_ID, so there wouldn't be a problem. What I meant was that if you load something that returns an integer, then you wouldn't be able to use it as a resource without specific casting. I guess you could still always do "sound = (Sound_ID)5;". But this shouldn't really be a problem, because any function that actually creates or adds a resource, should return the right type.

Ok, that makes sense.
Logged
Offline (Unknown gender) TheExDeus
Reply #8 Posted on: July 25, 2014, 02:07:34 pm

Developer
Joined: Apr 2008
Posts: 1860

View Profile
So does Josh have an opinion on this? Should we start ditching ID's and maps, and start passing classes?
Logged
Offline (Male) Goombert
Reply #9 Posted on: July 25, 2014, 03:14:50 pm

Developer
Location: Cappuccino, CA
Joined: Jan 2013
Posts: 2993

View Profile
Studio returns pointers for a lot of resources now. One of these resources turned into pointers were textures, the reason is because people always had to use -1 to specify no texture on a model. I really don't get why they just didn't overload the function, but w/e.
http://gmc.yoyogames.com/index.php?showtopic=609599
Logged
I think it was Leonardo da Vinci who once said something along the lines of "If you build the robots, they will make games." or something to that effect.

Offline (Unknown gender) TheExDeus
Reply #10 Posted on: July 25, 2014, 04:12:23 pm

Developer
Joined: Apr 2008
Posts: 1860

View Profile
I think we should use it for everything. But we must overload the default variable type (variant or var) to support that. Because then at least I will be able to figure out what is actually passed to a function. GM:S seem to have a "ptr" type specific to textures. So you cannot use it for anything else, because it's not really descriptive. What I mean is actually less to do with regular pointers, but to references. I want textures to be "Texture", so when I make a function "draw_texture(Texture &texture, gs_scalar x, gs_scalar y){}" it would error if I pass a sprite, background, object, sound or anything else.
« Last Edit: July 25, 2014, 04:14:36 pm by TheExDeus » Logged
Offline (Male) Goombert
Reply #11 Posted on: July 25, 2014, 08:28:40 pm

Developer
Location: Cappuccino, CA
Joined: Jan 2013
Posts: 2993

View Profile
Yeah but that partially defeats the purpose of a variant type for what you want to use it for.
Logged
I think it was Leonardo da Vinci who once said something along the lines of "If you build the robots, they will make games." or something to that effect.

Offline (Male) Rusky
Reply #12 Posted on: July 25, 2014, 08:41:56 pm

Resident Troll
Joined: Feb 2008
Posts: 954
MSN Messenger - rpjohnst@gmail.com
View Profile WWW Email
Make variants for all the resources then.
Logged
Offline (Male) Josh @ Dreamland
Reply #13 Posted on: July 25, 2014, 09:45:16 pm

Prince of all Goldfish
Developer
Location: Pittsburgh, PA, USA
Joined: Feb 2008
Posts: 2949

View Profile Email
Yes and no.

For things you create arbitrarily, you are right—these could be greatly benefited by classes. I would probably have it that var would not be allowed to represent these classes, except possibly through the C# approach. There's no denying how extremely useful it would be to have object-oriented file, UI, and data structure code.

Instances, you are beginning to push the limit. It is useful to be able to say, at very least for the purpose of static analysis, that other is  an obj_wall, or that instance_nearest(x, y, obj_missile) is an obj_missile. But do we really want to have you check inst instanceOf obj_missile, then cast? I'd hear an argument either way.

Resources, you have hit the limit. There is little reason to ever have spr_some_guy be anything but an integer. Let's choose a raw, gaping sore in GML as an example: sounds. Users have always been plagued by how large their audio resources end up being in GM. In the golden days, large audio files corrupted game files left and right, and even when they did not, insane save times kicked in. Pissed-off users would eventually be driven to store their sounds externally. We just recently got bitten in the ass by Round II of this issue as it became clear that optimizing sound storage for insert instead of access was the better move. This is wrong on so many levels.

The beauty of the resource tree is that your resources are managed for you—you don't need to know anything about where, when, or how sound0 was loaded. You just play it and it works. On your side, it's an integer; you can't get any simpler an opaque pointer than that. Behind the scenes, that sound may or may not be loaded, or actually even exist. We can replace this mechanism with some sort of weak reference class, but why would we? An integer serves the purpose fine.

I wouldn't argue against the use of, eg, typedef size_t sprite_t; to allow us to offer object-oriented features for these resources. But then, does spr.exists() really make any sense?

Just some considerations. I would be fine with a statically typed var of some sort, honestly.
« Last Edit: July 25, 2014, 09:46:52 pm by Josh @ Dreamland » Logged
"That is the single most cryptic piece of code I have ever seen." -Master PobbleWobble
"I disapprove of what you say, but I will defend to the death your right to say it." -Evelyn Beatrice Hall, Friends of Voltaire
Offline (Male) Rusky
Reply #14 Posted on: July 25, 2014, 10:49:44 pm

Resident Troll
Joined: Feb 2008
Posts: 954
MSN Messenger - rpjohnst@gmail.com
View Profile WWW Email
Using integers for resources is anything but opaque. The whole point of this exercise would be to separate resources into different types so you don't accidentally use one when you mean the other. With integers, you get weird behavior; with tracking them, you get an error message instead. That's one of the biggest reasons to use a pointer internally for textures- it's too easy for people to accidentally use a sprite or background instead and have it work in simple examples and break for no apparent reason later on.
Logged
Pages: 1 2 »
  Print