Adventure Game Studio

AGS Support => Advanced Technical Forum => Topic started by: Monsieur OUXX on Mon 18/01/2016 15:42:55

Title: AGS new keywords: Help me sorting it all out
Post by: Monsieur OUXX on Mon 18/01/2016 15:42:55
My habits in AGS programming are old, and my brain is still stuck with the features and keywords from 10 years ago. Help me transition to newer AGS programming features!

- what's the difference between protected, writeprotected, and readonly?
- what does "autoptr" mean? (please tell me even if it's not supported?) -- when I google "ags autoptr" I only get results from git, but Monkey uses it all the time.
- please direct me to the post about "managed struct" which I can't seem to find anymore? (EDIT: found this (http://www.adventuregamestudio.co.uk/forums/index.php?topic=43625.msg580591#msg580591))
- what's the benefit of adding "attribute", inside a struct? (EDIT: OK so apparently this one is not new at all! I've found this (http://www.adventuregamestudio.co.uk/forums/index.php?topic=42258.msg560357#msg560357).)



So I've found here (http://www.adventuregamestudio.co.uk/forums/index.php?topic=51050.0) that managed structs are very limited and cannot contain pointers or dynamic arrays,, and yet for years Monkey has been doing stuff like this :
Code (ags) Select

autoptr managed struct StackData
{
    protected int _idata;
};

struct Stack
{
  protected StackData _items[];
};

void set_Capacity(this Stack*, int value)
{
  StackData items[] = new StackData[value];
  this._items = items;
}

//And then in the code:
...
Stack myStack;
myStack.set_Capacity(100);



What is this wizardry???

Title: Re: AGS new keywords: Help me sorting it all out
Post by: Crimson Wizard on Mon 18/01/2016 18:12:40
There was this wiki article that explained some of them:
http://www.adventuregamestudio.co.uk/wiki/Keyword:_attribute
Title: Re: AGS new keywords: Help me sorting it all out
Post by: Monsieur OUXX on Tue 19/01/2016 08:59:57
Quote from: Crimson Wizard on Mon 18/01/2016 18:12:40
There was this wiki article that explained some of them:
http://www.adventuregamestudio.co.uk/wiki/Keyword:_attribute

Thanks. This is all very confusing I must say.
Title: Re: AGS new keywords: Help me sorting it all out
Post by: monkey0506 on Tue 19/01/2016 15:12:43
Quote from: Monsieur OUXX on Mon 18/01/2016 15:42:55and yet for years Monkey has been doing stuff

Long before Chris Jones even considered opening up the AGS source code, I've prided myself on pushing the limits of what could and could not be done. I discovered a lot of hidden potential, well before it could be seen on Github. :=

Quote from: Monsieur OUXX on Mon 18/01/2016 15:42:55- what's the difference between protected, writeprotected, and readonly?

protected: This struct member can only be accessed from within a struct function (including extender functions) by using the this keyword:

Spoiler
Code (ags) Select
struct MyStruct
{
  protected int bar;
  import void Foo();
};

void MyStruct::Foo()
{
  this.bar = 5; // inside a struct function, using this keyword -- fine
  Display("%d", this.bar); // fine
}

MyStruct myStruct; // instance
myStruct.bar = 10; // outside a struct function, not using this -- will not compile!
Display("%d", myStruct.bar); // not using this, will not compile
[close]

writeprotected: Like protected, except you can get the value from anywhere. Only writing the value is protected.

Spoiler
Code (ags) Select
struct MyStruct2
{
  writeprotected int bar;
  import void Foo();
};

void MyStruct2::Foo()
{
  this.bar = 5; // using this, fine
  Display("%d", this.bar); // fine
}

MyStruct2 myStruct2;
myStruct2.bar = 10; // writeprotected -- will not compile
Display("%d", myStruct2.bar); // reading the value -- fine
[close]

readonly: A readonly member can never be written to, even if using this. It is not useful for regular members at all, and is only useful for attributes.

Spoiler
Code (ags) Select
struct MyStruct3
{
  readonly int bar;
  import void Foo();
};

void MyStruct3::Foo()
{
  this.bar = 5; // will not compile
  Display("%d", this.bar); // fine, but always displays 0 (int default value)
}

MyStruct3 myStruct3;
myStruct3.bar = 10; // will not compile
Display("%d", myStruct3.bar); // fine, displays 0
[close]

Quote from: Monsieur OUXX on Mon 18/01/2016 15:42:55- what does "autoptr" mean? (please tell me even if it's not supported?) -- when I google "ags autoptr" I only get results from git, but Monkey uses it all the time.

autoptr means automatic pointer, and is useful in AGS when working with managed struct types, which are the only struct types in AGS which you can create pointers to. In AGS, literally all this does is prevent you from having to type the asterisk when defining an instance or reference.

Spoiler
Code (ags) Select
managed struct MyManagedStruct // no autoptr
{
  // ... members ...
};

MyManagedStruct *mms = new MyManagedStruct; // create a new instance, and store a pointer to it (with asterisk)

autoptr managed struct MyManagedAutoptrStruct // with autoptr
{
  // ... members ...
};

MyManagedAutoptrStruct mmas = new MyManagedAutoptrStruct; // creates a new instance, and store a pointer to it (without asterisk)
[close]

Literally everything else about the way you use these pointers is the same.

Quote from: Monsieur OUXX on Mon 18/01/2016 15:42:55- please direct me to the post about "managed struct" which I can't seem to find anymore? (EDIT: found this (http://www.adventuregamestudio.co.uk/forums/index.php?topic=43625.msg580591#msg580591))

I'm not sure if there's a specific post you're trying to recall, but let me explain...

managed struct means that AGS is handling the memory management for the instances of this struct. The latest versions of AGS allow user-defined structs to be dynamically instanciated (e.g., with the new keyword), if they are managed structs (this is a relatively recent change, in the 3.4.* branch). Due to the way the AGS compiler works, a dynamic instance cannot have pointers to other dynamic instances, or else it will leak that memory. This is something that hopefully will be changed in future versions of AGS, but for now, a managed struct cannot have any pointers. The built-in types like GUI are permitted to have pointers via a new builtin keyword, but that is because those pointers are guaranteed to be cleaned up by AGS (things like GUIs and GUIControls aren't even dynamically allocated). A builtin struct cannot be dynamically instanciated (nor can it be statically instanciated either, you can only store a pointer to an existing instance, like gui[0]).

Additionally, I will note that historically managed had similar behavior to the way that builtin works now. Most of the modules in which I used the managed keyword, I did so simply to prevent users from instanciating the struct, rather than to indicate that the user was meant to somehow use pointers to that struct (recent modules being an exception). The above example of autoptr should be sufficient to show how to instanciate a managed struct. Everything else works exactly as you would expect when using a pointer to that struct instance (meaning, yes, managed struct pointers are passed by reference when used as function parameters, and can modify the original object!).

Quote from: Monsieur OUXX on Mon 18/01/2016 15:42:55- what's the benefit of adding "attribute", inside a struct? (EDIT: OK so apparently this one is not new at all! I've found this (http://www.adventuregamestudio.co.uk/forums/index.php?topic=42258.msg560357#msg560357).)

The thread you linked to, and CW here in this thread, both link to the wiki article I wrote on the attribute keyword. For the record, that keyword has existed and worked exactly the same way since AGS 2.7. In fact, if you use the first method (http://www.adventuregamestudio.co.uk/wiki/Keyword:_attribute#Defining_an_attribute) the wiki article describes (importing the get_* and set_* functions directly in the struct definition), then you can use attributes in AGS 2.7, no problem. The functionality hasn't changed. You do seem to still be confused as to what it means though, so I'll try to clear that up.

When you have some member like "int Value;" in a struct definition, you are allocating enough memory for an int named "Value". Every struct instance allocates its own memory, so every struct instance has a separate int named "Value". An attribute works similarly, with the important distinction that it does not allocate memory. If it doesn't allocate memory, then what does a statement like "inst.Value = 5;" mean? Well, instead of allocating memory, an attribute generates a function call. Every attribute has at least one function associated to it, the getter. The getter is not defined for you. You must provide one, or else your scripts that rely on it will fail. My personal preference is to define the getter as an extender method. This prevents having to type an import declaration in the script header, preventing clutter, and it also makes sure that the user doesn't see the function. The user seeing the getter function wouldn't hurt them, but it might be confusing. The getter always takes the name of the attribute, with the prefix get_.

Spoiler
Code (ags) Select
// header

struct MyStruct
{
  import attribute int Value; // note that attributes are always imports; they can be protected, writeprotected, readonly, or none of the above
};

// script

int get_Value(this MyStruct*) // getter for MyStruct.Value; return type is the type of Value, and there are no additional parameters
{
  return SOME_VALUE; // TODO: return some meaningful result...
}
[close]

Bear in mind what I said that the attribute does not allocate memory. So now we have a getter function, but what are we getting? With no memory allocated, we don't have a "Value" to return. This can be useful if the value is some calculated result, or even if you're returning some value stored elsewhere, such as if your struct has a reason to return one of the game options, as returned by GetGameOption. You could make your attribute "Value" work as a synonym for calling GetGameOption.

Since "Value" can be written to, we'll also provide a setter, a function to set the value. This also takes the name of the attribute, with the prefix set_.

Spoiler
Code (ags) Select
// header

struct MyStruct
{
  import attribute bool PixelPerfect; // attributes can be of any type that is valid as a return or parameter type
};

// script

bool get_PixelPerfect(this MyStruct*) // return type for PixelPerfect is bool
{
  return GetGameOption(OPT_PIXELPERFECT);
}

void set_PixelPerfect(this MyStruct*, bool value) // setter: return type is void and takes exactly one parameter of the same type as the attribute
{
  SetGameOption(OPT_PIXELPERFECT, value);
}
[close]

I don't mean to reiterate the entire wiki article here, but I just wanted to show that attributes can be used pretty much any place you would otherwise use a function. The real benefit of attributes comes in the fact that they are used like regular data members, not functions.

Spoiler
Code (ags) Select
MyStruct myStruct;
myStruct.PixelPerfect = !myStruct.PixelPerfect; // toggle pixel perfect


Without attributes, this example would look something more like this:

Code (ags) Select
SetGameOption(OPT_PIXELPERFECT, !GetGameOption(OPT_PIXELPERFECT));

These both do the same thing.
[close]

Their importance becomes more apparent when modifying the existing value. Being able to use compound operators like += or -- is much more efficient than nested function calls.

The wiki article goes more into attributes, of course, but as a final note on them I'll point out that a common use case would be to have an attribute and a protected member with similar names. The attribute could be used as the public access to the protected member. When the user sets some value using the attribute, you can check if the value is valid in the setter, and immediately alert the user if something has gone wrong. If all is well, then you can store the value in the protected member using the this keyword to access the member.
Title: Re: AGS new keywords: Help me sorting it all out
Post by: Monsieur OUXX on Tue 19/01/2016 15:45:43
Thanks a lot for all the effort put into writing this. I hope I'll be able to digest all that without shouting "Oh, fuck that shit" in the process. I really, really want to be able to adapt your stack module to my own purposes.
Title: Re: AGS new keywords: Help me sorting it all out
Post by: Crimson Wizard on Tue 19/01/2016 15:50:58
It is worth to mention, that readonly keyword must be the first keyword in the line, otherwise it causes compilation error. This is a bug we are fixing in AGS 3.3.5.
Title: Re: AGS new keywords: Help me sorting it all out
Post by: monkey0506 on Tue 19/01/2016 15:55:53
P.S. readonly predates 2.7 (confirmed that it existed in 2.62). Everything else that you asked about (except autoptr) was introduced in 2.7 (protected, writeprotected, managed, and attribute!! attributes can be used by user scripts in 2.7, but the accessors must be imports as extenders didn't come about until 3.0). autoptr was introduce in 2.71, with the String type (which is autoptr). :=

This means that literally everything you asked about is:

Quote from: Monsieur OUXX on Mon 18/01/2016 15:42:55features and keywords from 10 years ago

Sauce (http://www.adventuregamestudio.co.uk/wiki/AGS_Version_history) 8-)  "2006-01-07 - ags_271.zip"
Title: Re: AGS new keywords: Help me sorting it all out
Post by: Monsieur OUXX on Tue 19/01/2016 17:39:18
Quote from: monkey0506 on Tue 19/01/2016 15:55:53
This means that literally everything you asked about is features and keywords from 10 years ago
Well I think that demonstrates pretty well how "attractive" they all are, and how much even a regular AGS scripter wants to try diving into this (mostly unsupported) mess. Ten years later I'm still cold-feet about it.
Title: Re: AGS new keywords: Help me sorting it all out
Post by: Snarky on Tue 19/01/2016 18:03:47
I'm not sure it demonstrates anything. Or have you been trying to figure this out for 10 years?

Of course, the reason to keep them undocumented and unsupported is precisely to discourage scripters from messing with it, as none of this should be necessary in the course of normal AGS coding, except maybe writeprotected.
Title: Re: AGS new keywords: Help me sorting it all out
Post by: Crimson Wizard on Tue 19/01/2016 18:20:04
attributes are super useful, all the newest built-in OO-types show that. They are basically like properties in C#.
I've been using them in modules I was writing for other people over time. "readonly", "protected" and "writeprotected" are ways to incapsulate things - which is pretty much standard nowadays.

autoptr is kinda specific, and may be confusing, since the rest of AGS uses *. I think it was added mainly for convenience of using String type.

I believe it is a shame this was not added to the Manual. BTW, I created an issue in the tracker about this.
Title: Re: AGS new keywords: Help me sorting it all out
Post by: monkey0506 on Tue 19/01/2016 19:42:39
I use all of these features extensively in my modules, and I'm sure I've talked about most of them in some capacity. Things like attributes are definitely not something I'd consider essential to the course of your everyday adventure game project, and yet without them, code such as the following would be impossible:

Code (ags) Select
  ScrollingDialog.ScrollArrows.Float = eFloatRight;
  ScrollingDialog.ScrollArrows.Up.NormalGraphic = 10;
  ScrollingDialog.ScrollArrows.Up.MouseOverGraphic = 9;
  ScrollingDialog.ScrollArrows.Up.PushedGraphic = 10;
  ScrollingDialog.ScrollArrows.Up.Padding.Top = 4;
  ScrollingDialog.ScrollArrows.Up.Padding.Bottom = 2;
  ScrollingDialog.ScrollArrows.Up.Padding.Right = 5;
  ScrollingDialog.ScrollArrows.Down.NormalGraphic = 8;
  ScrollingDialog.ScrollArrows.Down.MouseOverGraphic = 7;
  ScrollingDialog.ScrollArrows.Down.PushedGraphic = 8;
  ScrollingDialog.ScrollArrows.Down.Padding.Top = 2;
  ScrollingDialog.ScrollArrows.Down.Padding.Right = 5;


(That's actual, functioning code I'm working on for A Night at Camp Ravenwood, btw)

And I agree that a feature like attributes deserves an explanation in the manual.
Title: Re: AGS new keywords: Help me sorting it all out
Post by: Snarky on Wed 20/01/2016 00:30:27
Well, coding in Java recently, I find that I don't really miss properties all that much. I must admit that code sample does look nifty though.
Title: Re: AGS new keywords: Help me sorting it all out
Post by: monkey0506 on Wed 20/01/2016 01:40:51
If it weren't for the fact that Java allows you to call methods directly on the result of a function, then I'd be inclined to call you a liar. Properties/attributes are really nice when you find yourself using compound operators like ++ or += frequently. Incremental assignment with direct accessor calls is beyond annoying.
Title: Re: AGS new keywords: Help me sorting it all out
Post by: Snarky on Wed 20/01/2016 08:43:51
But how often do you really need to increment member variables outside of the class? I find it's pretty rare, and if there is some variable that you'll need to adjust a lot relative to its current value, you can always add an addToValue() method.
Title: Re: AGS new keywords: Help me sorting it all out
Post by: Crimson Wizard on Wed 20/01/2016 12:25:20
I do not see why it has to be ++/-- reason; I simply like properties/attribute because they make code look simplier; which could easy be a subjective view.
E: what I mean is that with properties you basically write "A" or "A = B", instead of "GetA()" and "SetA(B)".

Perhaps I did not emphasize that enough in my previous answer, but I think access rights (writeprotected, etc) are far more important, especially for non-static / non-singleton user types (in these cases you may find a way to hide internal data inside the script module).
Title: Re: AGS new keywords: Help me sorting it all out
Post by: Monsieur OUXX on Thu 21/01/2016 10:43:23
Well all the above is important but my main concern is really to ba able to design my data structures without realizing at the very end that it's forbidden by AGS because at some point I used a pointer or a struct or a dynamic array inside something else.
Title: Re: AGS new keywords: Help me sorting it all out
Post by: monkey0506 on Thu 21/01/2016 14:00:35
As a general rule of thumb, if you need to pass it as a function parameter or store it as a struct member, try to use managed struct so you can use pointers. However, managed struct cannot store pointers itself -- including String and dynamic arrays. There are ways of coping with that limitation of no pointers, depending what exactly you need, but that varies greatly on a case-by-case basis.

For example, if you can guarantee that the text won't exceed 200 characters, you can get away with faking a String member of a managed struct by using attributes:

Spoiler
Code (ags) Select
// header

managed struct MyStruct
{
  import attribute String Name;
  protected string name; // where the data is actually stored, protected to prevent unwanted access
  // TODO: protected bool isNull; -- if preserving the "null" String state is important
};


Code (ags) Select
// script

String get_Name(this MyStruct*)
{
  return this.name; // string -> String conversion is automatic
}

#ifdef STRICT_STRINGS
import void StrCopy(string dest, const string source); // import this if "Enforce new-style strings" is selected
// import other old-style string functions as needed, but for the most part you can get by with the new-style String methods and only use StrCopy just-in-time to save the results
#endif // STRICT_STRINGS

void set_Name(this MyStruct*, String value)
{
  if (value == null)
  {
    value = "";
  }
  else if (value.Length > 200)
  {
    // AbortGame("Attempted to set Name of more than 200 characters! Name must not exceed 200 characters max!"); // hard method
    value = value.Truncate(200); // soft method
  }
  StrCopy(this.name, value);
}


Code (ags) Select
// usage

MyStruct *ms = new MyStruct;
ms.Name = "Hello";
ms.Name = ms.Name.Append(" World!");
Display("%s", ms.Name); // displays Hello World!
[close]

Dynamic arrays can't really be worked around in any reasonable way (creating some kind of memory pool would necessitatively eat up more memory to avoid copying/resizing than just using a static array, so it's self-defeating). Custom struct pointers could be worked around using a memory pool, similar to the String cache in the Stack module (but would require manual destruction or the memory pool could grow unnecessarily large). And likewise for Strings if you don't want to enforce the 200-char restriction.

So, there are options, but considering how long AGS got along without even being able to create pointers to custom structs, I'd say that you can probably manage without pointers inside managed structs for the time being...
Title: Re: AGS new keywords: Help me sorting it all out
Post by: Crimson Wizard on Thu 21/01/2016 14:15:47
Well, custom managed structs appeared to be like that totally unintentional. When I found out how "easy" it is to implement them in the engine, I was too excited to check all the consequences. When we found out, these structs were already in released version. So we decided to let them stay (but with a restriction).
Title: Re: AGS new keywords: Help me sorting it all out
Post by: monkey0506 on Thu 21/01/2016 20:26:16
Even with the restriction, they're really very useful to have. It allows much more dynamic coding, like the dialog snippet I provided above. The alternative of public fields like ScrollingDialog.DownArrowPaddingTop, or worse, a public function like ScrollingDialog.SetDownArrowPaddingTop(int) seems really nasty in comparison. When we can have pointers as members of managed structs, and hopefully someday features like constructors and destructors, then obviously it will be all the more useful to have. Nevertheless, this (mostly) plugs a long-standing hole in the language, and I, for one, am grateful for that much. :=
Title: Re: AGS new keywords: Help me sorting it all out
Post by: Monsieur OUXX on Tue 12/04/2016 15:39:39
I didn't understand this sentence :
QuoteThe built-in types like GUI are permitted to have pointers via the builtin keyword
Since forever, I've been having pointers to, let's say, GUIButtons, inside my structs (I believe? Or did I fantasize this?).

So where does "builtin" step in? Is it used only in the built-in AGS script headers (not visible in the Editor, obviously), or are you sayin gthat they could be of benefit somewhere else, for the end-scripter?
Title: Re: AGS new keywords: Help me sorting it all out
Post by: Crimson Wizard on Tue 12/04/2016 16:56:57
Quote from: Monsieur OUXX on Tue 12/04/2016 15:39:39
I didn't understand this sentence :
QuoteThe built-in types like GUI are permitted to have pointers via the builtin keyword
Since forever, I've been having pointers to, let's say, GUIButtons, inside my structs (I believe? Or did I fantasize this?).

So where does "builtin" step in? Is it used only in the built-in AGS script headers (not visible in the Editor, obviously), or are you sayin gthat they could be of benefit somewhere else, for the end-scripter?

"Builtin" allows pointers INSIDE managed struct, not pointers TO managed struct. Pointers TO managed struct were always allowed (this is how managed structs are used in general).

Builtin keyword was introduced in 3.4.0, and is applied to managed types, which instances are created only by engine (on its own rules).
There is no way this keyword can benefit scripter, because after applying "builtin" keyword to your own struct you can no longer create objects of these types.

E: Actually, I think that no "builtin" structs have a managed pointer declared inside them either (even though they could), they rather have properties that return these pointers.
Title: Re: AGS new keywords: Help me sorting it all out
Post by: Monsieur OUXX on Wed 13/04/2016 13:06:09
Quote from: Crimson Wizard on Tue 12/04/2016 16:56:57
There is no way this keyword can benefit scripter, because after applying "builtin" keyword to your own struct you can no longer create objects of these types.

OK thanks