Adventure Game Studio | Forums

AGS Support => Modules & Plugins => Topic started by: Monsieur OUXX on 18 Jun 2012, 02:40

Title: AGS Easy Plugin (v0.8a): Create a full engine plugin in one click-TESTERS NEEDED
Post by: Monsieur OUXX on 18 Jun 2012, 02:40
That tutorial (http://www.adventuregamestudio.co.uk/yabb/index.php?topic=43629.msg580625#msg580625) by Calin gave me the following idea:

(http://img16.imageshack.us/img16/2638/61616278.png)


What does it do?
- You feed this small command line program with a single, tiny file containing one or more "struct" definitions, in AGS syntax.
- It generates all the files you need to create the corresponding plugin.

Basically you won't have to worry about those things anymore: Copy-pasting one billion getters/setters, remembering the names of the interfaces, registering your functions in the plugin, etc.
Easy Plugin does it for you!

(http://img444.imageshack.us/img444/3510/explanations2small.png)

Can I try it?
Yes. If you've been good (or if you're as handsome as Dualnames).
- You need a Java Virtual Machine installed. If you don't have one, go get one : www.java.com/en/download/ (Ags Easy Plugin was tested against Java 6 (update 32) 32-bits, on a Windows 7 professional 64-bit -- it means it should also work with Java 7, possibly 64-bits flavour).
- Download the example :
(http://img840.imageshack.us/img840/5042/downloadbuttonvn.png) (http://shutupload.com/dl/82bf645ce370/).
- Run the file test.bat

You will find all the files produced in the "output" subdirectory. Yes, that's how much boring typing Easy Plugin spared you.

Why do I get Warnings when I run the example?
The example shows you that Easy Plugin also warns you when you forget to declare a "Create" function in your struct, or when the types don't match.


OK, now I see what it does. Can I use it for my own project?
Yes, simply edit the contents of the file "input.easypluginDefinition".
You can try to run AGSEasyPlugin.jar without parameters to see the expected command line parameters.


I want to change the way it works. Is this Open Source?
Yes. And VERY easy to modify for your own needs.
- What you need is to follow the steps described in this video : http://vimeo.com/groups/29150/videos/8001326 It takes no more than 10 minutes to set up the whole environment!
IMPORTANT: I didn't manage to have ANTLR IDE work with the latest versions od Eclipse and ANTLR. I had to use Eclipse Galileo SR1 (32 bits) and ANTLR 3.2. Apart from that, you can use the latest versions of the libraries shown in the video.
- Download the project files : http://shutupload.com/dl/407f7aba991f/
But you don't need to get the source code to use AGS Easy Plugin. As it is, you can just run it.


Why Java?
- Because it's almost a clone of C# except you don't have to go through the hassle of having a copy of Visual C# (even with the express edition you have to register)
- Because AGS is moving towards being cross-platform. This program would work on Mac or UNIX with minor changes.
- Because I'm sure you won't need some redistributable package to make it work.


HELP WANTED
At the moment it works pretty well, but you can help me improving it by toying with it and telling me if I forgot something.
For example, I was rather focused on automating the output, but I might have produced semantically wrong code in C++ (forgotten a function, etc.)

Title: Re: AGS Easy Plugin (v0.8a): Create a full engine plugin in one click-TESTERS NEEDED
Post by: monkey0506 on 18 Jun 2012, 05:25
- You need a Java Virtual Machine installed. If you don't have one, go get one : www.java.com/en/download/
- Because I'm sure you won't need some redistributable package to make it work.

So, which one is it? :P Not to say most users probably won't have the Java VM installed, but that's just as much a "redistributable package" as the .NET Framework.

Other than that, it looks pretty nice (without having tried it out). A couple things come to mind though...

What if I define my AGS structs like this?

Code: Adventure Game Studio
  1. managed struct Point3D; // forward declaration of struct
  2.  
  3. managed struct Point2D
  4. {
  5.   import attribute Point3D *AsPoint3D;
  6.   import attribute int X;
  7.   import attribute int Y;
  8.   readonly import static attribute int DefaultX; // $AUTOCOMPLETESTATICONLY$
  9.   readonly import static attribute int DefaultY; // $AUTOCOMPLETESTATICONLY$
  10.   import static Point2D* Create(); // $AUTOCOMPLETESTATICONLY$
  11. };
  12.  
  13. managed struct Point3D extends Point2D
  14. {
  15.   import attribute int Z;
  16.   readonly import static attribute int DefaultZ; // $AUTOCOMPLETESTATICONLY$
  17. };

I've added the Default* properties because otherwise the Create function would have to be overloaded on the Point3D class (which AGS doesn't allow), or it would have to have an unnecessary and unused third parameter on the base class.

Does your exporter allow derived structs? It absolutely must do so.

Although it shouldn't hurt anything, would your exporter link a setter function for the Default* properties? Because AGS would never call it.

I'm also interested to see if the $AUTOCOMPLETESTATICONLY$ comment tags would properly carry over into the C++ scripts.

Another interesting point about this would be the fact that with extender methods, simple polymorphism is possible...which your exporter could take advantage of. Could be a bit messy to actually incorporate on the plugin side of things, or at least as part of your automated tool. Would still be fun to see that in action.

In any case, good work (again, said without testing it out :D).

Edit: I'm not saying that this particular inheritance is an especially good one...but I think it gets the point across (ignore the fact that you'd have to cast the pointer every time you create a Point3D :P).
Title: Re: AGS Easy Plugin (v0.8a): Create a full engine plugin in one click-TESTERS NEEDED
Post by: Calin Leafshade on 18 Jun 2012, 06:31
This is nice.

One of the reasons that i havent made some more plugins with some simple types (like Point and Size and StringList and so on) is because its such a ball ache to create the classes and the boiler plate.

Monkey also makes excellent points.

Also, i would *really* consider C# here (superior string manipulation) or, since its just processing, python or something. But thats really just personal preference.
Title: Re: AGS Easy Plugin (v0.8a): Create a full engine plugin in one click-TESTERS NEEDED
Post by: Monsieur OUXX on 18 Jun 2012, 07:30
So, which JVM is it? :P
Edited first post.

that's just as much a "redistributable package" as the .NET Framework.
Yes, but the one big difference is that you often realize afterwards that you're missing a redistributable package. I hate that. Like, you have a cryptic error message about a missing DLL, and you post about it on the forum, and you're being answered: "Oh, do you have whatever redistributable package? I would have shipped it with the app but it's legally forbidden by Microsoft's license" :/ I prefer to say FIRST install Java (any Java!) and THEN you're 99% certain it will work.

Didn't try it
Please do!!! I want to make sure it will work out-of-the-box on most systems.

A couple things come to mind though...
Please list some specific constructions that you'd like the internal grammar to recognize and process AND what it should give in C++. I'm not comfortable enough to infer the expected C++ output and then realize I was all wrong (for example, what should readonly attributes become in C++?)
I'll implement them. The only exception might be "// $AUTOCOMPLETESTATICONLY$" because maintaining comments is a pain.


since its just processing
I'm not sure what you mean. That sort of compiler is getting away from basic text swapping, and relies instead on building grammar trees, which ANTLR does, independently from the language. It could easily produce a C# code, but I've chosen Java by design. I admit that struct definitions are rather linear, so building grammar trees is a bit overkill, but it's a warmup for more complex syntactic tools to come.

OK guys, now start making engine plugins already! ;-)

EDIT:

v0.8a :
Everything you can see in the example's screenshot, in the first post

Target for v0.9a :
Readonly attributes...............DONE (not in downloadable version yet)
static attributes.....................DONE (not in downloadable version yet)
forward declaration...............DONE (not in downloadable version yet)
AutoComplete........................DONE (not in downloadable version yet)
Attributes default value.........DONE (not in downloadable version yet)
Inheritance............................ WiP
Generation of Arrays code.....NOT STARTED



Title: Re: AGS Easy Plugin (v0.8a): Create a full engine plugin in one click-TESTERS NEEDED
Post by: Monsieur OUXX on 18 Jun 2012, 11:51
QUESTIONS
1) forward declarations
I'm a bit embarrassed for the forward declarations.
How does "editor->RegisterScriptHeader(myStructScriptHeader);" work? Every time I call this function, does it concatenate "myStructScriptHeader" to any other header previously registered, making it one big script header for the plugin?
At the moment, Easy Plugin only works with structs definitions (as opposed to forward declarations) making one script header per struct. I wouldn't know where to fit a simple struct declaration.

Could you give me an example of what you expect to find in the C++ files regarding forward declarations?

2) Default attributes
I didn't understand what you were talking about with "DefaultXXX". Do attributes starting with "Default" have a special status in AGS plugins?
Title: Re: AGS Easy Plugin (v0.8a): Create a full engine plugin in one click-TESTERS NEEDED
Post by: monkey0506 on 18 Jun 2012, 12:38
AGS only allows forward struct declarations for the purpose of declaring pointers to them. You can't create an instance based on a forward declaration.

I would say in most instances, it can be conceived that the forward struct declaration should be included in the script header of the struct definition immediately following it. In this case, Point3D's forward declaration would be registered in the Point2D header string.

As far as the C++ side, it allows forward declaration of classes. So I would expect that.

Regarding the "Default*" attributes I added, that was just as part of the example. They would just be a friendly way to determine what the default values populated by the Create method would be (seeing as I used a parameterless Create method, for the reasons stated above). They're not special in any way. Apart from being readonly and static which apparently wasn't previously supported. :P

Regarding the readonly attributes, I could give a crap less if they were fully read/write on the C++ side, but on the AGS side you only need to register a getter, not a setter.

You said that you implemented the $AUTOCOMPLETESTATICONLY$ comment tag...what about $AUTOCOMPLETEIGNORE$ and $AUTOCOMPLETENOINHERIT$? :D

And I guess we'll just have to agree to disagree about the "redistributable package" because I honestly don't understand the validity of using that as the argument (portability, etc. yes, but not just ignoring the fact that external redistributable packages are required either way).

Oh, and 99% of the time, I will prefer native code over engine plugins, because it's automatically portable (doesn't require platform-dependent rebuilds) and is therefore easier to maintain.
Title: Re: AGS Easy Plugin (v0.8a): Create a full engine plugin in one click-TESTERS NEEDED
Post by: Monsieur OUXX on 18 Jun 2012, 15:06
AGS only allows forward struct declarations for the purpose of declaring pointers to them. You can't create an instance based on a forward declaration.
I would say in most instances, it can be conceived that the forward struct declaration should be included in the script header of the struct definition immediately following it. In this case, Point3D's forward declaration would be registered in the Point2D header string.

I understand forward declaration, but you didn't answer my question: How many "char*" script headers do I need in the plugin? One with all the structs, or several (one for each struct)?  In case there is only one, can I still register it little bit by little bit?
Title: Re: AGS Easy Plugin (v0.8a): Create a full engine plugin in one click-TESTERS NEEDED
Post by: monkey0506 on 18 Jun 2012, 15:12
As far as I can tell you could register it all at once, which would be equivalent to putting it all in the same ASH file, or you can register it separately, which would be like putting it in separate ASH files. The order in which they're registered would indicate which header was considered "on top" or first. So as long as the forward declaration is registered before the struct referencing it then it should be fine.

...I'm not sure why that was unclear before...so hopefully I've clarified that?
Title: Re: AGS Easy Plugin (v0.8a): Create a full engine plugin in one click-TESTERS NEEDED
Post by: Monsieur OUXX on 18 Jun 2012, 16:20
...I'm not sure why that was unclear before...so hopefully I've clarified that?

Yes thanks. That was unclear because I've never written an engine plugin in my life, and believe it or not it takes a little time to get the whole picture :)
Title: Re: AGS Easy Plugin (v0.8a): Create a full engine plugin in one click-TESTERS NEEDED
Post by: Monsieur OUXX on 19 Jun 2012, 23:00
Double-posting to bump a question to Monkey.

You wrote : "I'm also interested to see if the $AUTOCOMPLETESTATICONLY$ comment tags would properly carry over into the C++ scripts."

I thought $AUTOCOMPLETExxx$ special comments were only for AGS scripts? I've propagaed it to the script header in the plugin, but I have no idea how to achieve autocomplete in Visual Studio.
I'm going to Google it, but do you have suggestions?

Title: Re: AGS Easy Plugin (v0.8a): Create a full engine plugin in one click-TESTERS NEEDED
Post by: monkey0506 on 20 Jun 2012, 03:59
Well you're registering the AGS script header in C++ code, aren't you?

What I mean is that I wanted to be sure the tag would be persisted here:

Code: C++
  1. const char *header = "import void DoSomething(); // $AUTOCOMPLETEIGNORE$"; // << the $AUTOCOMPLETEIGNORE$ tag is part of the AGS script header

It bears no relevance in Visual Studio. There may or may not be a way of doing something similar, but what really is the point there? The point on the AGS side is to prevent the end-user from calling code that they shouldn't be. If it's in the plugin, they're only going to call whatever is exposed on the AGS side, so it works the same.

I only brought it up because I wasn't sure if your exporter was using the raw input directly or whether it was generating the headers from what it parsed. Based on what you've said about registering different headers for each struct, I would assume the latter, which means the exporter would have to parse the commented tag in the input, and then generate the same tag in the output.
Title: Re: AGS Easy Plugin (v0.8a): Create a full engine plugin in one click-TESTERS NEEDED
Post by: Monsieur OUXX on 20 Jun 2012, 10:54
> I wanted to be sure the tag would be persisted here:

So it's a yes: After your comment, I made it persist, along with the 2 others you mentioned.
It doesn't keep other comments, but it keeps special comments (I have also added special comments formatted like /** this **/ that will allow to initialize the default values (DEfaultX, etc.) directly from the original pseudo-AGS struct definitions file. That's going to be good. Oh yes.


============


Regarding Polymorphism, here is how I see it :
(I wrote the code below quickly from the top of my head, so please pardon any syntax error or C++ errors or confusion between pointers (->) and normal attributes (.) )


ORIGINAL PSEUDO-AGS FILE
Code: Adventure Game Studio
  1.     managed struct Point3D; // forward declaration of struct
  2.      
  3.     managed struct Point2D
  4.     {
  5.       readonly import attribute Point3D *AsPoint3D; //any attribute that matches the pattern "Type* AsType" will be forced to be readonly and will have an auto-generated getter for type casting
  6.       import attribute int X;
  7.       readonly import static attribute int DefaultX /** = 66 **/; // $AUTOCOMPLETESTATICONLY$ //note the special comment /** **/
  8.       import static Point2D* Create(); // $AUTOCOMPLETESTATICONLY$  // ONLY STRUCTS THAT DO NOT INHERIT FROM OTHER STRUCTS ARE ALLOWED TO HAVE AN EXPLICIT "CONSTRUCTOR" LIKE "Create".
  9.     };
  10.      
  11.     managed struct Point3D extends Point2D //Point3D inherits from Point2D. It's forbidden to have an explicit "Create" constructor
  12.     {
  13.       import attribute int Z;
  14.       readonly import static attribute int DefaultZ  /** = 77 **/; // $AUTOCOMPLETESTATICONLY$ //note the special comment /** **/
  15.       //Point3D inherits from Point2D, so there will also be an auto-generated "Point3D* Point3DfromPoint2D(Point2D*)" method in the final files, used by Point2D's "AsPoint3D"
  16.     };
  17.  

Point2D.h
Code: Adventure Game Studio
  1.     class Point2D
  2.     {
  3.       public:
  4.         int X;
  5.         static int DefaultX; // $AUTOCOMPLETESTATICONLY$ //as per C++ syntax, value is initialized in the .cpp
  6.         Point3D* AsPoint3D();
  7.         Point2D();
  8.         ~Point2D();
  9.     }
  10.  

Point2D.cpp (fragment)
Code: Adventure Game Studio
  1. (...)
  2.  
  3. Point2D::Point2D()
  4. {
  5.     this->X = Point2D->DefaultX;
  6. }
  7.  
  8. (...)
  9.  
  10. Point2D::DefaultX = 66;
  11.  
  12. Point3D* Point2D::AsPoint3D()
  13. {
  14.     //we use the utility method defined in Point3D : "Point3D* Point3DfromPoint2D(Point2D*)"
  15.     return Point3D.Point3DfromPoint2D(this);
  16. }
  17.  


agsplugin.cpp
Code: Adventure Game Studio
  1. (...)
  2.  
  3. //just to mention that AsPoint3d* is a readonly attribute in AGS, but an actual method in C++
  4. Point3D* Point2D_get_AsPoint3D(Point2D* obj){
  5.    return obj->AsPoint3D();
  6. }
  7. (...)
  8.  

Point3D.h
Code: Adventure Game Studio
  1.     class Point3D : Point2D
  2.     {
  3.       public:
  4.         int Z;
  5.         static int DefaultZ; // $AUTOCOMPLETESTATICONLY$ //as per C++ syntax, value is initialized in the .cpp
  6.  
  7.         Point3D();
  8.         ~Point3D();
  9.         Point3D* Point3DfromPoint2D(Point2D*);
  10.     }
  11.  


Point3D.cpp (fragment)
Code: Adventure Game Studio
  1. (...)
  2.  
  3. Point3D::Point3D()
  4. {
  5.     //X is initialized in Point2D's constructor
  6.     this->Z = Point3D->DefaultZ;  
  7. }
  8.  
  9. (...)
  10.  
  11. Point3D::DefaultZ = 77;
  12.  
  13. Point3D* Point3D::Point3DfromPoint2D(Point2D* obj)
  14. {
  15.     Point3D* castObject = new Point3D();
  16.    
  17.     castObject->X = obj->X; //we must do this for all attributes of obj
  18.     return castObject;
  19. }
  20.  

PS: I'm thinking of generating by default an Array class for every struct declared. This way, the end-scripter can store any object defined in your plugin's script header without having to go through the hassle of asking you to implement it.
Title: Re: AGS Easy Plugin (v0.8a): Create a full engine plugin in one click-TESTERS NEEDED
Post by: monkey0506 on 20 Jun 2012, 16:59
I don't see anything polymorphic about that. You do understand the difference between polymorphism and inheritance, yes?

AGS allows simple polymorphism due to the fact that an extender method of a derived class can silently override any base class method (although I don't think it works for overriding accessor functions). So it is perfectly legal to define an extender method such as:

Code: Adventure Game Studio
  1. void BringToFront(this Label*)
  2. {
  3.   AbortGame("No.");
  4.   GUIControl *gcthis = this; // get a base class pointer
  5.   gcthis.BringToFront(); // call base class method
  6. }

Then if you call:

Code: Adventure Game Studio
  1. lblStatusline.BringToFront();

Your game would explode. Essentially this is equivalent to marking every method of every base class as virtual, and extender methods of derived classes as override. AGS just does it implicitly. It's probably a quirk, and definitely not "by convention", but it's technically feasible and CJ himself never seemed to think that there would be anything wrong with using it (aside from the fact that it's "unsupported" and could possibly be broken in future versions, which used to go for a lot of things, like arrays and structs lol).

Oh, and why did you put the $AUTOCOMPLETESTATICONLY$ tag in the C++ source? That doesn't belong (read as: do anything) there. :P

And I was thinking about it, that Point2D.AsPoint3D member should definitely be marked as $AUTOCOMPLETENOINHERIT$ as Point3D.AsPoint3D is just redundant. :D

Also, isn't it typically conventional for derived class constructors to have an overload that takes a base class member as a parameter to handle copying member data? If C++ pointers were type-safe then you could simply recast the pointer, but they aren't so...

Edit: I've modified the above example of polymorphism to include calling base class methods from overridden methods.
Title: Re: AGS Easy Plugin (v0.8a): Create a full engine plugin in one click-TESTERS NEEDED
Post by: Monsieur OUXX on 20 Jun 2012, 17:04
I don't see anything polymorphic about that.

Well, I built my code on your illustration of polymorphism. I thought you what you called polymorphism was the possibility of using .AsPoint3D whenever the scripter wants to pass a Point2D to a method that requires a Point3D.
If that's not what you meant, then please give a code snippet to illustrate what code you want 1) recognized, and 2) Produced!
Preserving motivation is really not your skill :)
 
Title: Re: AGS Easy Plugin (v0.8a): Create a full engine plugin in one click-TESTERS NEEDED
Post by: monkey0506 on 20 Jun 2012, 17:20
Well, I built my code on your illustration of polymorphism.

I never said that the example of inheritance I gave was polymorphic:

I'm not saying that this particular inheritance is an especially good one...

Another interesting point about this would be the fact that with extender methods, simple polymorphism is possible...which your exporter could take advantage of. Could be a bit messy to actually incorporate on the plugin side of things, or at least as part of your automated tool. Would still be fun to see that in action.

I thought you what you called polymorphism was the possibility of using .AsPoint3D whenever the scripter wants to pass a Point2D to a method that requires a Point3D.

If the constructed object is a Point2D then AsPoint3D should always be null, so this doesn't even make sense. A Point3D could be constructed from a Point2D, but that doesn't mean that AsPoint3D should be creating a new object. AsPoint3D should only return a valid pointer if the constructed object is a Point3D to begin with.

Code: C++
  1. class Point2D
  2. {
  3.   // ...
  4. };
  5.  
  6. class Point3D : Point2D
  7. {
  8.   // ...
  9. };
  10.  
  11. Point2D *p2d = new Pointer2D(); // valid
  12. Point3D *p3d = new Pointer3D(); // valid
  13. Point2D *p2dto3d = new Pointer3D(); // valid
  14. p3d = static_cast<Point3D*>(p2dto3d); // valid
  15. p3d = static_cast<Point3D*>(p2d); // invalid, pointer is now misaligned and will cause memory leaks or other fatal issues

This is not polymorphism. This is inheritance.

If that's not what you meant, then please give a code snippet to illustrate what code you want 1) recognized, and 2) Produced!

"what I meant" and "what I called polymorphism" were the same thing: polymorphism (http://en.wikipedia.org/wiki/Polymorphism_(computer_science)).

This is a programming concept. I wasn't aware I would be required to teach you what polymorphism is. I have provided a code snippet (in my last post) of polymorphism in AGS.

Preserving motivation is really not your skill :)

I don't mean to sound cross or anything, but perhaps I'm making too many assumptions about your skills as a programmer. I don't consider myself very advanced (in terms of languages like C++), but you seem to be asking a lot of very basic questions. I'm not upset at you, but I don't think you have a right to be upset at me for assuming you would know what polymorphism is.
Title: Re: AGS Easy Plugin (v0.8a): Create a full engine plugin in one click-TESTERS NEEDED
Post by: Monsieur OUXX on 20 Jun 2012, 18:18
Wow, the link to wikipedia was really the final nail in the coffin. I'm just offering a tool that saves typing.

I'm not saying you're assuming I know what polymorphism is, and I'm not sure why you think I'm upset at you. But I'm asking how you'd see polymorphism working (in concrete situations) on every object produced by the plugin, taking in account AGS' limitations (e.g. the impossibility to overload functions). The examples you just gave are a step forward towards that.

In the case of AGS, "polymorphism" remains a vague notion, and is not universal. That's why I'm asking questions. I'm trying to meet your request, and to offer polymorphism as a bonus. To do that I must match AGS limitations with C++ possibilities, and avoid the syntactic tricks of AGS that only you know (I'd have never known that extenders can be overridden but not getters and setters).

===

On with the technical discussion

Point2D *p2dto3d = new Pointer3D(); // valid in C++
p3d = static_cast<Point3D*>(p2dto3d); // valid in C++

But this type of behaviour is not implemented in AGS script (or is it?). That's why I suggested .AsPoint3D. It's "by copy", which doesn't make much sense (as you said it should work directly on the original object), but it's better than nothing as long as the scripter doesn't change the attributes values...
But I realize that it was silly now. Yet, I'm still not sure how you picture things. Should the getter of every attributes in a Point2D first check if the AsPoint3D attribute is set , and in that case return the attributes of that object? (the attributes of the Point2D object would remain unused).

===

> why did you put the $AUTOCOMPLETESTATICONLY$ tag in the C++ source?
Because I don't care. I'm just copy-pasting stuff around, and trying to focus on the important things.

===

Attributes : do they exist in AGS scripts? I can't find them in the help file. Neither can I find getters and setters. But I also know that I often have trouble finding the newest 3.x scripting stuff in the help file. So I'm not sure if it effectively does something to use keyword "attribute" in a regular module.
Title: Re: AGS Easy Plugin (v0.8a): Create a full engine plugin in one click-TESTERS NEEDED
Post by: monkey0506 on 20 Jun 2012, 20:57
I linked to Wikipedia because you seemed baffled by my use of the term "polymorphism", and I don't know what you know (just as you don't know what I know). I'm still trying to sort that out (as, I'm sure, are you).

In terms of AGS polymorphism is limited by the fact that AGS doesn't typically allow function overloads. However, as I showed above, in the case of derived classes, it is possible thanks to extender methods (but only overloading a function from a base class in the derived class, you can't have multiple extenders with the same name in the same class, or use an extender to override an existing class method). So that covers the basic principle of polymorphism that the derived class can have an overload of a function with the same name, return type, and parameter set (although in the case of AGS the return type and parameter set can be changed and still override the base class function). Another principle of polymorphism that AGS doesn't cover is that if the constructed object is a derived class object, in true polymorphic classes, the derived class method would be called when the virtual method is called on the base class pointer. AGS doesn't allow that, although it works in our favor as calling the method from the base class pointer is presently the only way in AGS to call the base class function if the derived class overloads it.

Regarding pointer casting, AGS will allow you to cast a derived class object into a base class pointer. That is always allowed with inheritance (in every language I've seen):

Code: Adventure Game Studio
  1. GUIControl *labelAsGC = lblStatusline;

This works fine because the Label class is derived from GUIControl. AGS does not currently have a way to perform the pointer cast in the opposite direction though, which is why I suggested adding a Pointer3D* into the Pointer2D class. It's also the reason why GUIControl has AsButton, AsLabel, AsListBox, etc.

When the C++ Point3D object is constructed, it inherently is a Point2D object as well, just with some additional properties added. So, you do not need to be copying the data from a Point2D into a newly constructed Point3D every time Point2D.AsPoint3D is called. Point2D.AsPoint3D should be acting as a cast. Since C++ pointers are not type safe, I had to look it up (http://stackoverflow.com/questions/351845/finding-the-type-of-an-object-in-c), but it seems that using C++'s dynamic_cast will return null if the pointer is invalid:

Code: C++
  1. Point3D* Point3D::Point3DfromPoint2D(Point2D* obj)
  2. {
  3.     return dynamic_cast<Point3D*>(obj);
  4. }

If the constructed object stored in the Point2D* is not a Point3D, then this will return NULL. That is, if I do this:

Code: Adventure Game Studio
  1. Point2D *p2d = Point2D.Create();
  2. Point3D *p3d = p2d.AsPoint3D;

Then p3d should always be null. With what you had, it would be constructing a new Point3D and returning that, which is not desirable behavior in this case (what if GUIControl.AsLabel was constructing new labels all the time??).

Having the autocomplete tags in the C++ source doesn't matter. Leave it or don't, I just wanted to make sure you were clear (since you previously asked). ;)

Regarding attributes, the attribute keyword first showed up in AGS 2.7, and is used by virtually every user-accessible property of every managed AGS type (with some exceptions that were excluded, primarily for legacy reasons). These attributes act virtually the same as a property in C#, and are simply encapsulation for underlying data. If you're writing a plugin, you would register accessor methods like this:

Code: C++
  1.         engine->RegisterScriptFunction("Point2D::get_AsPoint3D", Point2D_get_AsPoint3D);

All this is doing is registering a function with the AGS engine. The exact same effect could be achieved by importing the "get_AsPoint3D" method as part of the struct, or by defining an extender method for the class with the same name. The function would be linked at compile time, based on whether it was registered by the plugin, by a struct import, or by an extender method. Use of attributes outside of the engine source and/or plugins is not supported, although it is fully functional. Several of my modules depend on it inherently, but then again, so does most of the AGS engine.

As an example of accessors in native AGScript:

Code: Adventure Game Studio
  1. struct Attributes
  2. {
  3.   readonly import static attribute int ReadonlyStaticInt;
  4.   writeprotected import static attribute int WriteprotectedStaticInt;
  5.   import static attribute int StaticInt;
  6.   readonly import attribute float ReadonlyFloat;
  7.   protected float readonlyFloat; // backing field
  8.   writeprotected import attribute float WriteprotectedFloat;
  9.   protected float writeprotectedFloat;
  10.   import attribute float Float;
  11.   protected float _float;
  12.   import attribute String Strings[];
  13.   protected String strings[50];
  14.   readonly import attribute int StringCount;
  15. };
  16.  
  17. int Attributes_readonlyStaticInt; // backing field
  18. int Attributes_writeprotectedStaticInt;
  19. int Attributes_staticInt;
  20.  
  21. int get_ReadonlyStaticInt(this Attributes*) // linked at compile time as the getter; return type matches type of attribute
  22. {
  23.   // technically this is being called statically, so use of the 'this' pointer here is undefined
  24.   return Attributes_readonlyStaticInt;
  25. }
  26.  
  27. // no setter required (or called) for readonly
  28.  
  29. int get_WriteprotectedStaticInt(this Attributes*)
  30. {
  31.   return Attributes_writeprotectedStaticInt;
  32. }
  33.  
  34. void set_WriteprotectedStaticInt(this Attributes*, int value) // setter is required for writeprotected (as it can actually be called)
  35. {
  36.   // feel free to validate value, just as you would with any accessor function
  37.   Attributes_writeprotectedStaticInt = value; // you could omit this to treat it as readonly, but then it could only be set by the backing field directly
  38. }
  39.  
  40. int get_StaticInt(this Attributes*)
  41. {
  42.   return Attributes_staticInt;
  43. }
  44.  
  45. void set_StaticInt(this Attributes*, int value) // the name 'value' isn't required, but I like it as a type of convention
  46. {
  47.   Attributes_staticInt = value;
  48. }
  49.  
  50. float get_ReadonlyFloat(this Attributes*) // this is being called on an instance, so 'this' is valid; again, return type matches
  51. {
  52.   return this.readonlyFloat; // return backing field
  53. }
  54.  
  55. float get_WriteprotectedFloat(this Attributes*)
  56. {
  57.   return this.writeprotectedFloat;
  58. }
  59.  
  60. void set_WriteprotectedFloat(this Attributes*, float value) // note that type of 'value' will always match type of attribute
  61. {
  62.   this.writeprotectedFloat = value;
  63. }
  64.  
  65. float get_Float(this Attributes*)
  66. {
  67.   return this._float;
  68. }
  69.  
  70. void set_Float(this Attributes*, float value)
  71. {
  72.   this._float = value;
  73. }
  74.  
  75. String geti_Strings(this Attributes*, int index) // notice for indexed properties the name is 'geti' and it takes an index parameter (again, name is irrelevant but conventional)
  76. {
  77.   // bound checking?
  78.   return this.strings[index];
  79. }
  80.  
  81. void seti_Strings(this Attributes*, int index, String value)
  82. {
  83.   this.strings[index] = value;
  84. }
  85.  
  86. int get_StringCount(this Attributes*)
  87. {
  88.   return 50;
  89. }

Warning: If you define an attribute in AGScript, you cannot use the attribute in the same script as which the accessor methods for it exist. Doing so will cause your game to violently explode. And you will die. Violently. You can use them in any later script where they are defined (so if the struct is in a header, any later script), just not the one with the accessor methods. Or any script prior to the one with the accessor methods.

Usage would be the same:

Code: Adventure Game Studio
  1. Attributes a;
  2. a.Strings[12] = "Hello World";

Finally, please feel free to read Extender Methods mean Polymorphism! (http://www.adventuregamestudio.co.uk/wiki/?title=Extender_Methods_mean_Polymorphism!) and Keyword: attribute (http://www.adventuregamestudio.co.uk/wiki/?title=Keyword:_attribute) in the wiki. That's kind of why I put them there. :P
Title: Re: AGS Easy Plugin (v0.8a): Create a full engine plugin in one click-TESTERS NEEDED
Post by: Monsieur OUXX on 21 Jun 2012, 15:31
Very interesting post, most helpful.


1) About my early version of "AsPoint3D"

Let's stop talking about that. I suggested that only because I thought this construction was forbidden in AGS script :
Code: Adventure Game Studio
  1. Point2D* = AnyFunctionThatReturnsAPoint3D_evenIfPoint3DextendsPoint2D();
  2.  
I thought that The GUIControl / AsLabel,AsXXX,... thing was a unique exception, only for GUIControls, built directly in the engine code, and that it couldn't be reproduced even with the use of a plugin.
So I came with that stupid workaround of returning a copy of the Point2D object, turned into a Point3D. for me it was a poor hack.
I thought "OK, it's not the same object, but if the developer doesn't alter the fields and only works with pointer members, then he might at least be able to do something out of it, and call the functions that expect a Point3D when all he has is something declared as a Point2D".
Anyway - then again, let's stop rubbing my face into it.

2) About overload and override

  • (a) AGS doesn't typically allow function overloads.
  • (b) but overload is possible thanks to extender methods
  • (c) you can't have multiple extenders with the same name in the same class
  • (d) you can't use an extender to override an existing class method
  • (e) (using extenders only) you can overload a function from a base class in the derived class
  • (f) (using extenders only) the return type and parameter set can be changed and still override the base class function

Alright, tell me if I misinterpreted some of it:
Code: Adventure Game Studio
  1.  
  2. struct Point3D;
  3.  
  4. struct Point2D {
  5.    import int foo();
  6.    import int foo(int a); //forbidden because of (a), and there is already a class function "foo"
  7. };
  8.  
  9. struct fooStruct {
  10.    import int foo(); //allowed (fooStruct is not related to Point2D)
  11. }
  12.  
  13. struct Point3D extends Point2D {
  14.    import int foo(); //forbidden because of (a) (yes, I do know that in that specific case, it's actually override, not overload)
  15.    import int foo(int a); //forbidden because of (a) (this foo overloads "foo()" from Point2D and would override "foo(int a)" from Point2D)
  16. };
  17.  
  18. int foo(this Point2D*) //forbidden because of (d) and there is already a class method "foo" in Point2D
  19. {
  20.    //
  21. }
  22.  
  23. int foo(this Point2D*, int newParam) //still forbidden because of (d) even though the params are different
  24. {
  25.     ///stuff
  26. }
  27.  
  28.  
  29. int foo2(this Point2D*) //allowed (regular extender)
  30. {
  31.   //
  32. }
  33.  
  34. int foo2(this Point2D*, int newParam) //forbidden because of (c), because there is already a foo2
  35. {
  36.   //
  37. }
  38.  
  39. float foo(this Point3D*, int newParam) //allowed because of (e).
  40.                                       //It doesn't matter if the params are the same as in "Point2D::foo()"
  41. {
  42.   //
  43. }  
  44.  
  45. int foo2(this Point3D*) //allowed (override of Point2D's foo2, thanks to (e))
  46. //int foo2(this Point3D*, int newParam) //would also work because of (f)
  47. {
  48.   //
  49. }
  50.  
  51. //Imagine this:
  52. Point2D* p = Point3D.Create();
  53. p.foo(66); //it calls the code of "float foo(this Point3D*, int newParam)"
  54. p.foo(); // what happens? Was "Point2D::foo()" definitely overridden by the extender, even with this prototype, or only overloaded?
  55.  
  56. //if I declared "foo2(this Point3D*, int newParam)":
  57. p.foo2(); //what would this do? Was "int foo2(this Point2D*)" only overloaded by "foo2(this Point3D*, int newParam)", or completely overridden?
  58.  

Note: I'm going to test the snippet above in AGS. If you don't feel like answering, just ignore the questions.


3) About casting in C++

Point2D.AsPoint3D should be acting as a cast. Since C++ pointers are not type safe, I had to look it up (http://stackoverflow.com/questions/351845/finding-the-type-of-an-object-in-c), but it seems that using C++'s dynamic_cast will return null if the pointer is invalid:
Code: C++
  1. Point3D* Point3D::Point3DfromPoint2D(Point2D* obj)
  2. {
  3.     return dynamic_cast<Point3D*>(obj);
  4. }
If the constructed object stored in the Point2D* is not a Point3D, then this will return NULL.
I'm happy the cast returns null, I wasn't sure if I'd have to store the type internally to check it before casting. It might still be interesting in order to be able to test from the script what is a) the declared class of a variable, 2) The actual class of a variable, 3) all in-between classes it comprises. (e.g. in Java : "instanceof" versus "getClass()").

I'll have to refresh my memories of static_cast versus dynamic_cast, it's always been a bit blurry for me.

4) Keyword "attribute"


Regarding attributes, the attribute keyword first showed up in AGS 2.7, and is used by virtually every user-accessible property of every managed AGS type.
(...)
Use of attributes outside of the engine source and/or plugins is not supported, although it is fully functional.
The reason why I was asking is because I was hoping to test attributes behaviour in a regular-ass script, and wasn't sure if I'd have to implement getters and setters, and if that would work outside of a plugin.
The example you gave ("struct Attributes { ... };") shows it does.

5) Registering getters and setters (http://upload.wikimedia.org/wikipedia/commons/2/2e/Can_Setter_dog_GFDL.jpg)

If you're writing a plugin, you would register accessor methods like this:
Code: C++
  1.         engine->RegisterScriptFunction("Point2D::get_AsPoint3D", Point2D_get_AsPoint3D);
It's already doing it, mimicking the tutorial of Calin.


6) Keywords "readonly", "writeprotected"

As an example of accessors in native AGScript:
struct Attributes { ... } ;

Thanks a lot for this, now I know what keywords to expect and how to implement them.

There are a few things I'm not sure I fully understood :
1. Why do you always declare attributes in double ("Strings" versus "strings", "Float" versus "_float") ? Can't you set directly "Strings[ x]" and "Float" in their respective setters? Oh wait. Is this a workaround for what you said : "If you define an attribute in AGScript, you cannot use the attribute in the same script as which the accessor methods for it exist."?

2. The difference between readonly and writeprotected -- for that I'll check the manual/wiki. So far I understand that in both cases, the value is not set, but that in the case of writeprotected, the function is still called (in case there is some side processing to perform). But then why did you set the value of "Attributes_writeprotectedStaticInt" in the setter? Because of that, in the end it behaves as a regular attribute doesn't it?

3. Shouldn't this take a String instead of an int? "void seti_Strings(this Attributes*, int index, int value)"

7) Wiki
Finally, please feel free to read Extender Methods mean Polymorphism! (http://www.adventuregamestudio.co.uk/wiki/?title=Extender_Methods_mean_Polymorphism!) and Keyword: attribute (http://www.adventuregamestudio.co.uk/wiki/?title=Keyword:_attribute) in the wiki. That's kind of why I put them there. :P
Yes, I will! Thanks for the links! It's always a bit awkward to know where to search: Manual or wiki -- or even to remember the wiki exists. I miss almost 100% of the wiki articles, even when searching with Google.
The wiki doesn't have a very good visibility on the website/forums, and it's a pity because it contains invaluable information.
A bit off-topic, but do you have a suggestion to bind it more efficiently from the new HAT? (example: Having an additional folder in the "contents" of the help file. This folder would contain the wiki articles, but their pages would just say: This article is not an official AGS article and is hosted on the internet. Click on this link to open it in your web browser" -- this solution would also allow to add the keywords to the help index).

8) Attributes "AsXXX"
I now have a clearer vision of how that should be implemented in C++.

The only thing that concerns me is how to decide programmatically what "AsXXX" should be added to the base class.
Indeed, Point2D can have many children classes. That means I need to compute a list of all children classes, and then add the corresponding "AsXXX" to the base class.
Also I still haven't decided if "AsXXX" attributes should be added explicitly by the developer to the script definition*, or if they should be added automatically in the last moment to the script header.

Maybe a hybrid version would be to add automatically all the "AsXXX" that can be computed directly from the current script definition*, but also allow the developer to add more manually in the very same definition*, in order to anticipate other plugins that co-exist with this one.

* I'm talking about the AGS pseudo-code that's being fed to EasyPlugin -- not the output script header.
Title: Re: AGS Easy Plugin (v0.8a): Create a full engine plugin in one click-TESTERS NEEDED
Post by: monkey0506 on 21 Jun 2012, 17:26
Very interesting post, most helpful.

You're welcome. :)

1) About my early version of "AsPoint3D"

Let's stop talking about that. I suggested that only because I thought this construction was forbidden in AGS script :
Code: Adventure Game Studio
  1. Point2D* = AnyFunctionThatReturnsAPoint3D_evenIfPoint3DextendsPoint2D();
  2.  
I thought that The GUIControl / AsLabel,AsXXX,... thing was a unique exception, only for GUIControls, built directly in the engine code, and that it couldn't be reproduced even with the use of a plugin........

The reason the GUIControl.As(Button/Label/ListBox/...) properties exist is because AGS allows casting pointers to a base class, but not to a derived class:

Code: Adventure Game Studio
  1. GUIControl *gc = lblStatusline; // works fine, every Label is also a GUIControl
  2. Label *lbl = gc; // does NOT work, not every GUIControl is a Label (and AGS has no explicit cast operators)

So the AsXXX properties would exist only for casting a base class pointer back into a pointer to the underlying derived class object (if it is indeed a derived class object and not a base class object). Not trying to rub this in your face or anything, just want to make sure it's clear what it's for (and why I included the AsPoint3D in my original example code).

2) About overload and override

  • (a) AGS doesn't typically allow function overloads.
  • (b) but overload is possible thanks to extender methods
  • (c) you can't have multiple extenders with the same name in the same class
  • (d) you can't use an extender to override an existing class method
  • (e) (using extenders only) you can overload a function from a base class in the derived class
  • (f) (using extenders only) the return type and parameter set can be changed and still override the base class function

Code: Adventure Game Studio
  1. //Imagine this:
  2. Point2D* p = Point3D.Create();
  3. p.foo(66); //it calls the code of "float foo(this Point3D*, int newParam)"
  4. p.foo(); // what happens? Was "Point2D::foo()" definitely overridden by the extender, even with this prototype, or only overloaded?
  5.  
  6. //if I declared "foo2(this Point3D*, int newParam)":
  7. p.foo2(); //what would this do? Was "int foo2(this Point2D*)" only overloaded by "foo2(this Point3D*, int newParam)", or completely overridden?

Interesting points, I don't think I actually fully tested them, but my gut instinct would be that they have been completely overridden and the parameterless methods would in this case be inaccessible. One note though, you cast the Point3D into a Point2D pointer. In AGS this means that the base class functions would be called, not the derived class functions (again, this is something that goes against true polymorphism, but CJ never intended this behavior to begin with :P).

3) About casting in C++

Point2D.AsPoint3D should be acting as a cast. Since C++ pointers are not type safe, I had to look it up (http://stackoverflow.com/questions/351845/finding-the-type-of-an-object-in-c), but it seems that using C++'s dynamic_cast will return null if the pointer is invalid:

Code: C++
  1. Point3D* Point3D::Point3DfromPoint2D(Point2D* obj)
  2. {
  3.     return dynamic_cast<Point3D*>(obj);
  4. }

If the constructed object stored in the Point2D* is not a Point3D, then this will return NULL.

I'm happy the cast returns null, I wasn't sure if I'd have to store the type internally to check it before casting. It might still be interesting in order to be able to test from the script what is a) the declared class of a variable, 2) The actual class of a variable, 3) all in-between classes it comprises. (e.g. in Java : "instanceof" versus "getClass()").

I'll have to refresh my memories of static_cast versus dynamic_cast, it's always been a bit blurry for me.

Regarding type checking, you could look into using typeid and/or dynamic_cast. static_cast is capable of performing unsafe casts, returning a pointer to an object that doesn't exist or is of a completely different and incompatible type. dynamic_cast is used to prevent this by checking the run-time type and returning NULL if the cast is invalid (or if you're casting a reference instead of a pointer, it would throw a bad_cast exception). CPlusPlus.com has an article on typecasting in C++ (http://www.cplusplus.com/doc/tutorial/typecasting/). Note that, as also noted there, dynamic_cast and typeid both rely on Run-Time Type Information (RTTI) which may be turned off by default in some compilers. Shouldn't be an issue for the end user to turn it on though (as far as I can tell).

5) Registering getters and setters (http://upload.wikimedia.org/wikipedia/commons/2/2e/Can_Setter_dog_GFDL.jpg)

If you're writing a plugin, you would register accessor methods like this:
Code: C++
  1.         engine->RegisterScriptFunction("Point2D::get_AsPoint3D", Point2D_get_AsPoint3D);

It's already doing it, mimicking the tutorial of Calin.

Of course, just explaining the process, trying to make it clear where I was going.

6) Keywords "readonly", "writeprotected"

As an example of accessors in native AGScript:
struct Attributes { ... } ;

Thanks a lot for this, now I know what keywords to expect and how to implement them.

No problem! :)

There are a few things I'm not sure I fully understood :
1. Why do you always declare attributes in double ("Strings" versus "strings", "Float" versus "_float") ? Can't you set directly "Strings[ x]" and "Float" in their respective setters? Oh wait. Is this a workaround for what you said : "If you define an attribute in AGScript, you cannot use the attribute in the same script as which the accessor methods for it exist."?

No, this is entirely unrelated. Think about it, when you're designing the plugin, you're linking the accessor functions directly to C++ methods. Those methods themselves are not ints or floats, they don't, themselves, store the data. Attributes in AGS are the same as properties in C# - they don't store anything, they only provide the accessors. I provided the protected (non-attribute) data members in the same fashion as you would define a field in C# - a place to store the data for use with the attribute. Without this, the struct would have the accessor methods, but no actual variables associated with them.

2. The difference between readonly and writeprotected -- for that I'll check the manual/wiki. So far I understand that in both cases, the value is not set, but that in the case of writeprotected, the function is still called (in case there is some side processing to perform). But then why did you set the value of "Attributes_writeprotectedStaticInt" in the setter? Because of that, in the end it behaves as a regular attribute doesn't it?

I covered this in my article in the wiki. Basically if an attribute is defined in AGS as readonly then it is just that. It can never be written to in AGS. Ever. Not even by members/methods of the same class. If an attribute is defined in AGS as writeprotected then outside the class, you do not have any write access. However, members/methods within the class (including extender methods) can still write to it. Therefore, if an attribute is writeprotected, then it must have a setter linked.

3. Shouldn't this take a String instead of an int? "void seti_Strings(this Attributes*, int index, int value)"

Yes, that was a typo. ;)

A bit off-topic, but do you have a suggestion to bind it more efficiently from the new HAT? (example: Having an additional folder in the "contents" of the help file. This folder would contain the wiki articles, but their pages would just say: This article is not an official AGS article and is hosted on the internet. Click on this link to open it in your web browser" -- this solution would also allow to add the keywords to the help index).

I think that we should automate a way of generating the wiki version of the manual, and then set edit rights to the page. Anyone could still edit the discussion page, but the wiki articles themselves should be protected. I think that this would be the best way of going about this.

8) Attributes "AsXXX"
I now have a clearer vision of how that should be implemented in C++.

The only thing that concerns me is how to decide programmatically what "AsXXX" should be added to the base class.
Indeed, Point2D can have many children classes. That means I need to compute a list of all children classes, and then add the corresponding "AsXXX" to the base class.
Also I still haven't decided if "AsXXX" attributes should be added explicitly by the developer to the script definition*, or if they should be added automatically in the last moment to the script header.

IMO, the struct being fed into your exporter should be the full AGS class. You shouldn't be expected to glean information like this out of nothing. If I know I'm going to be defining some derived classes and that AGS has no explicit cast methods, then it becomes my responsibility as the author of the plugin, not yours as the author of the exporter tool, to define the classes appropriately.

Maybe a hybrid version would be to add automatically all the "AsXXX" that can be computed directly from the current script definition*, but also allow the developer to add more manually in the very same definition*, in order to anticipate other plugins that co-exist with this one.

* I'm talking about the AGS pseudo-code that's being fed to EasyPlugin -- not the output script header.

Well of course the end-user could go back and manually enter other AsXXX properties (or anything) into the script headers that are being registered, but again I think this falls beyond the scope of things you should be accounting for, and falls into the hands of the user.
Title: Re: AGS Easy Plugin (v0.8a): Create a full engine plugin in one click-TESTERS NEEDED
Post by: Monsieur OUXX on 21 Jun 2012, 22:28
There are a few things I'm not sure I fully understood :
1. Why do you always declare attributes in double ("Strings" versus "strings", "Float" versus "_float") ? Can't you set directly "Strings[ x]" and "Float" in their respective setters? Oh wait. Is this a workaround for what you said : "If you define an attribute in AGScript, you cannot use the attribute in the same script as which the accessor methods for it exist."?

No, this is entirely unrelated. Think about it, when you're designing the plugin, you're linking the accessor functions directly to C++ methods. Those methods themselves are not ints or floats, they don't, themselves, store the data. Attributes in AGS are the same as properties in C# - they don't store anything, they only provide the accessors. I provided the protected (non-attribute) data members in the same fashion as you would define a field in C# - a place to store the data for use with the attribute. Without this, the struct would have the accessor methods, but no actual variables associated with them.

OK, I understand why you had to do it in the example written in AGS script. But in the plugin, I'll just make the utility member hidden in the C++ class, to avoid confusing the end-scripter with 2 homonymous members. The coder can still make it exposed if he wants before he compiles the plugin, by adding it to the script header and all.

===

I'm still not sure how I'll have a factory/pseudo-constructor for Point3D objects, as the name "Create" will be already taken by Point2D. Unless the overloading/overridin restriction doesn't apply to static member functions?
The easy solution would be to have Point2D::CreatePoint2D and Point3D::CreatePoint3D, but it's not very elegant.


Title: Re: AGS Easy Plugin (v0.8a): Create a full engine plugin in one click-TESTERS NEEDED
Post by: monkey0506 on 21 Jun 2012, 22:44
Well the C++ classes themselves would be the backing data, so for a plugin I wouldn't even define those. I'd only define them if I was writing code using attributes in native AGScript (which I do all the time, all of my most recent modules rely on them). So in reality, I wouldn't expect to see those "extra" properties in the file being fed into your exporter (and wouldn't worry about handling it as a special case).

Edit: Hey, if nothing else this is a good place for me to dump some of the information I've learned about the engine's capabilities that 99% of other users know nothing about (and even CJ himself has been surprised by some of). ;D
Title: Re: AGS Easy Plugin (v0.8a): Create a full engine plugin in one click-TESTERS NEEDED
Post by: Monsieur OUXX on 22 Jun 2012, 10:07
Hey, if nothing else this is a good place for me to dump some of the information I've learned about the engine's capabilities that 99% of other users know nothing about (and even CJ himself has been surprised by some of). ;D

Yes, that's why I was trying to make you spit it out :) I've noticed you know all the tricks about AGS scripting language, that drive me crazy every time because I have to re-learn them by trial-and-error after each 6-months break in AGS programming, when I've forgotten it all.

What do you think about that "Create" thing? There can be a Create function only for the base class, so I'm not sure how to implement constructors for the children. I'll go with creating constructors with different names for each child class, but my senses tingle, there's got to be another way...
Title: Re: AGS Easy Plugin (v0.8a): Create a full engine plugin in one click-TESTERS NEEDED
Post by: monkey0506 on 22 Jun 2012, 18:45
Well you actually got me really hoping that I would be able to override static methods, but no such luck. Because the original method would be static, but then the extender method overriding it would be non-static, you can't call it statically. Presumably if we had static extender methods then this would be possible.

By the way, in order for the polymorphic override to work (not just compile, but actually be called) it has to be in a separate script from where the original function definition is at (see, I learn new stuff all the time).

Having to cast the pointer (read as: call the AsXXX property) every time you create a member of a derived class seems ridiculous, but for now I can't think of a better way to get around this than simply having differently named constructors. Perhaps the exporter should (for the time being) generate constructors such as "Class.CreateClass" rather than just "Class.Create". It does seem redundant, but this is probably the best we've got (for now).
Title: Re: AGS Easy Plugin (v0.8a): Create a full engine plugin in one click-TESTERS NEEDED
Post by: Monsieur OUXX on 25 Jun 2012, 10:19
Perhaps the exporter should (for the time being) generate constructors such as "Class.CreateClass" rather than just "Class.Create". It does seem redundant, but this is probably the best we've got (for now).

Yes, that's the naming "convention" I was going to adopt in case I'm going that way.
Now I have to find the motivation to do all the scripts tests to be 200% sure of what works and what doesn't, regarding extenders overriding/overloading :-) :-( :-) :-(

 
Title: Re: AGS Easy Plugin (v0.8a): Create a full engine plugin in one click-TESTERS NEEDED
Post by: Monsieur OUXX on 25 Jun 2012, 17:05
Code: Adventure Game Studio
  1. //Imagine this:
  2. Point2D* p = Point3D.Create();
  3. p.foo(66); //it calls the code of "float foo(this Point3D*, int newParam)"
  4. p.foo(); // what happens? Was "Point2D::foo()" definitely overridden by the extender, even with this prototype, or only overloaded?
  5.  
  6. //if I declared "foo2(this Point3D*, int newParam)":
  7. p.foo2(); //what would this do? Was "int foo2(this Point2D*)" only overloaded by "foo2(this Point3D*, int newParam)", or completely overridden?

Interesting points, I don't think I actually fully tested them, but my gut instinct would be that they have been completely overridden and the parameterless methods would in this case be inaccessible.
One note though, you cast the Point3D into a Point2D pointer. In AGS this means that the base class functions would be called, not the derived class functions (again, this is something that goes against true polymorphism, but CJ never intended this behavior to begin with :P).

OK, part from the fact that this is giving me a headache, I just wanted to mention that I tested the code above (Not with a pointer obtained via Point3D.Create(), though, but instead with "Point3D p;" for the time being).
the Point2D versions of the functions are indeed entirely replaced. The AGS compiler forbids the call to "p.foo();" AND to "p.foo2();". In both cases, the parameter-less version is gone.
Title: Re: AGS Easy Plugin (v0.8a): Create a full engine plugin in one click-TESTERS NEEDED
Post by: monkey0506 on 25 Jun 2012, 18:09
To be honest, that's exactly what I expected. I just wasn't sure that I had personally tested those specific cases.
Title: Re: AGS Easy Plugin (v0.8a): Create a full engine plugin in one click-TESTERS NEEDED
Post by: Monsieur OUXX on 26 Jun 2012, 09:20
One note though, you cast the Point3D into a Point2D pointer. In AGS this means that the base class functions would be called, not the derived class functions

I wonder what happens with this :
Code: Adventure Game Studio
  1. Point2D* p2d = Point3D.Create();
  2. Point3D* p3d = p2d;
  3. p3d.foo();
  4.  
It will call Point3D::foo() I suppose (I'll double-check later today). So, it always calls the function defined in the apparent type, doesn't it?
Title: Re: AGS Easy Plugin (v0.8a): Create a full engine plugin in one click-TESTERS NEEDED
Post by: monkey0506 on 26 Jun 2012, 15:12
Code: Adventure Game Studio
  1. Point2D* p2d = Point3D.Create();
  2. Point3D* p3d = p2d;
  3. p3d.foo();

That doesn't make any sense. The line:

Code: Adventure Game Studio
  1. Point3D* p3d = p2d;

Is invalid. AGS doesn't allow casting to derived-type pointers. Hence the need for the AsPoint3D property (which for the record *I* suggested, in my original code snippet :P). And yes, AGS always calls/links functions based on the calling ("apparent") type, not necessarily the underlying object type (which I have noted as a regression against true polymorphism, but, as I noted before, also provides the only way to call base class methods, at least until proper polymorphism can be implemented).
Title: Re: AGS Easy Plugin (v0.8a): Create a full engine plugin in one click-TESTERS NEEDED
Post by: Monsieur OUXX on 27 Jun 2012, 09:31
That doesn't make any sense. The line "Point3D* p3d = p2d;" is invalid because AGS doesn't allow casting to derived-type pointers. Hence the need for the AsPoint3D property.
Yeah, yeah, you got the idea; that was just a quick example of switching back to Point3D* to make the apparent type match the real type.