Scripting:STNE Classes
From STNE Wiki
Classes in STNE
Firstly, you need to understand that the script compiler is not a binary compiler. It is a front end lexical analyzer (lex an) for Visual Basic and .Net. The reason you need to understand this is because it means that operator syntax comes from VB, so there are some differences that you might not expect. It also means that you can use a VB reference for some of the operators (such as logical not, and null pointer reference). Since the lex an reprocesses into VB (Keep in mind this comes from experience with the engine, not from direct knowledge) this can be quite helpful, and confusing at the same time.
Classes in STNE are similar to classes in a fully classful language, with a few major exceptions:
- The classes are not inheritable
- The use of the This pointer is required to access class methods from within the class
- Declarations are used as static references to the class
- There is no scoping of variables or methods (private/protected/public)
- Operator overloading has not been tested by myself, but I doubt it's included.
- Since classes are not inheritable, there is no method or variable overloading
There are also some caveats you should be aware of that pertain to the lexical analyzer.
- Additional ending braces (}) can cause the lex an to issue no warnings during compile, yet not producing usable code
- Prototyping is not supported (to the best of my knowledge)
- Since prototyping is not supported, implicit casting is used with varibales, with the default type being String (This includes declarations of varibales).
- Function or method mismatches can cause a null pointer exception during compile, without giving a reference point as to where (eg no line number of the occurrence)
- Line terminators (eg ';') are not necessary. A new line compliments the ; if the code is written on one line. (Not recommended, but usable)
While there are many other anomalies, the basic lex an is sound enough to do some rather fancy coding. Seeing how this feature is left out of many online games, the engine is laudable. Even better, while there is more work necessary to make is robust, it is actually maintained along with STNE changes.
So, why bother to write a classfull script?
- Data objects can be reused and even stored in memory in lists
- Rather than suck server memory dry (which will abort your script), you can instantiate the class and then free the memory when complete
- You can avoid memory sucking multi-dimensional arrays
- You can write more complete self documenting code that is easier to maintain
To create a static class, all you need to is declare it:
Class MyMath { … }
Once it is declared, any method declared is also available. This of course doesn't work well with variables... They need memory allocation. You can create a 'property' of the class by simply returning a static value as thus:
Class MyMath { Function Pi() As Double { Return 3.14; } Function Circumference(radius As Double) As Double { Return MyMath.Pi * radius * radius; // Or Math.Pow(radius, 2.0); } }
Notice that when we called Pi from Circumference, we used the accessor MyMath. Since this is going to be used statically, you do not want to use a This pointer, as that requires you to instantiate the class. The majority of the functionality you will want from a class is handled by the static methods, but you have to remember that while you can call a static method from an instantiation, the inverse is not true.
Class MyMath { Function Pi() As Double { Return 3.14; } Function Circumference(radius As Double) As Double { Return This.Pi * radius * radius; // Or MyMath.Pi * Math.Pow(radius, 2.0); } // This will fail miserably. Since the class cannot be instantiated in this form Var cir As Double = MyMath.Circumference(3.0);
In order to create an instantiated class, you need to have two components. The new method in the class, and the assignment in the code. The new method can be empty, but it has to exist to instantiate the class.
Class MyMath { Var Radius As Double Function New() { ; // Null instruction, essentially a placeholder } Function New(r As Double) { Radius = r; } ... } Var mmath As MyMath = New MyMath(); // This could be written Var mmath As New MyMath();
In order to access a method within the class, you need to use a reference pointer (Either statically via the class name, or dynamically via the This pointer). The lex an will only look for functions that are global to the script if you don't specifically reference the class. In most cases, this is not an issue (annoying yes, an issue, not really) unless you name a global function the same as a class method. Name your global functions differently than your class functions, it will help you in the long run.
Class MyMath { Function Pi() As Double { Return 3.14; } Function Output (message As String) { WriteLine (“MyMath: “ & message); } Function ShowCircumference() { MyMath.Output(This.Circumference); } ... }
Also, notice the lack of the function parameter list in the call to This.Circumference within ShowCircumference(). If there are no parameters necessary (eg a procedure), the parameter list can be left off (As if you were accessing a variable in the class).
In order to free the memory used by an instantiation, you have to either leave the scope of the delcartion (return from a function), or assign the reference to a null pointer. The next time the garbage collector runs, it will free the memory. The garbage is a mechanism that free resources and performs other various maintenance operations automatically.
Class MyMath { Function Pi() As Double { Return 3.14; } Function Circumference(radius As Double) As Double { Return MyMath.Pi * radius * radius; // Or Math.Pow(radius, 2.0); } } Function C (cir As Double) As Double { Var mmath As MyMath = New MyMath(); // This could be written Var mmath As New MyMath(); Return mmath.Circumference(cir); } // Pointer is now unassigned, the garbage collector will reclaim the memory Var mmath As MyMath = New MyMath(); // This could be written Var mmath As New MyMath(); … mmath = Nothing; // Let the garbage collector reclaim the memory
To use another class within you classes, you just need to instantiate the other class and manipulate it like you would with any other function. To allow access to the methods of that class, you simply wrap the other functions methods within your own.
Class MyPoint { Var Point As SPoint; Function New(newx As Integer, newy As Integer) { Point = New Spoint(newx, newy); } Function New(newpoint As SPoint) { Point = newpoint; } Function X () As Integer { Return Point.X; } Function Y () As Integer { Return Point.Y; } ... }
Classes in STNE can be extremely useful for self documenting code and for maintaining a small memory footprint for your script. Although STNE script classes are more simplistic that a full classful language, this does not detract much from the the usefulness of them.
Code used for this exercise:
Class MyMath { Var Radius As Double Function New() { ; } Function New(r As Double) { Radius = r; } Function Radius(c As Double) As Double { Return Math.Sqrt(c / MyMath.Pi); } Function Pi() As Double { Return 3.14; } Function Circumference(radius As Double) As Double { Return MyMath.Pi * radius * radius; // Or MyMath.Pi * Math.Pow(radius, 2.0); } Function Circumference() As Double { Return MyMath.Circumference(Radius); } Function Output (message As String) { WriteLine ("MyMath: " & message); } Function ShowCircumference() { MyMath.Output("C=" & This.Circumference); } } Class MyPoint { Var Point As SPoint; New(newx As Integer, newy As Integer) { Point = New Spoint(newx, newy); } New(newpoint As SPoint) { Point = newpoint; } Function X () As Integer { Return Point.X; } Function Y () As Integer { Return Point.Y; } } WriteLine(MyMath.Circumference(3)); Var m As MyMath = New MyMath(3); WriteLine(m.Circumference()); MyMath.Output(MyMath.Circumference(3)); MyMath.ShowCircumference (3);
--Miltiades (En-1:56599) 21:19, 18 October 2011 (CEST)