Namespace Upgrade Patch (Object Oriented Scripts)
by Jack Oneal · 01/05/2007 (2:01 pm) · 22 comments
Download Code File
This patch should work on all version of TGE and TSE. It has been tested on TSE MS4.
TGE comes with a built in and fully functional script engine that has basic inheritance similar to that of Java or c++. The majority of this system is completely hidden from the scriptors and unknown to most engine coders. This system is in full force and extremely simple with very little error checking. It is quite easy for a script to run a foul of this system with out even knowing it, leading to subtle logic errors and fatal errors.
The most basic error is a namespace collision, ie where two distinct objects with the same class name. Most scriptors lack a full knowledge of all the classes with in the game, there are over 50 of them in the vanilla release. Its quite easy to name an object the same of one of the parent classes. When this happens, the code is overriding code in places other than expected. The new logic error that has been introduced most likely wont be fatal, but will lead undefined behavior for any other objects with the same parent namespace. While sitting down and walking the code in the debugger is great way to learn, this leads to massive downtime for usually simple code.
The most dangerous errors can be caused by use of the scriptObject class. This class was upgraded using a patch that was incorporated into TGE around v1.4. The patch by Bryan Edds can be found here:
www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=6335
This patch introduced a full object oriented system to TGE. A very nice upgrade which this modification is partially based. This system how ever did not account for a scriptors lack of knowledge of the namespace system. Since scriptors can't even access the Namespace system, this could lead to very entertaining errors, usually fatal. Here is an example of the problem:
Once this piece of code is executed, the game will crash but not at the code. It will crash the next time any code calling a SimObject is called. Thats every object in the game. This is an error that is almost impossible to debug or to find as the code stands with out engine debugger access. If your lucky, it will start an infinite loop with the code following the declaration.
For those who don't understand what this command has done, its not all that simple. SimObject is the master class for every object in the game. SimObject is a class type, but it is also a Namespace. This is the hidden Namespace system already built into the game to allow inheritance in the engine and scripts. When a class is created in the engine (in c++), the Namespace is setup automatically by the engine macros. This is completely hidden unless you sit down and follow the code paths, which usually isn't a problem if the engine coder doesn't make a mistake in their inheritance, but the complier helps out a lot here. The system creates a Namespace tree that is identical to the inheritance tree of c++ (without multiple inheritance), which is really smart. This allows a quite command to walk up the namespaces each time a script calls "parent::". This is part of the patch from Bryan Edds, as previously all scripts would have to specifically use their parent's class name instead of parent. The parent command allow a nice blind inheritance system which allows for polymorphism, but also introduced so many more subtle errors. Here is the inheritance tree from ScriptObject which can easily be printed with this modification.
The following code is how a script would use the parent command.
When the code is executed, with the given Namespace from above. The code would be interpretered as follows by the script engine.
Now this is a great system, but its quite mysterious and can be very buggy. Lets take a look at the bad code again.
This would completely fubar Namespace tree. Causing
as compared to the normal
Notice a recursive definition that is now part of SimObject's Namespace. This will either lead to a fatal error or an infinite loop. This error isn't possible with the new system, it will error out and kill the command, usually allowing the game to load. Catching this error with the current code base would require someone to literally walk the data structures associated with the hidden built in Namespace system, good luck with that. This is an error that is impossible to fix with out engine code access, leaving poor scriptors with a bug that can't be fixed or diagnosed. I would assume many people have hit up against, just as I have when attempting to do any advanced code.
It would be reasonable to say scrap the new system, its just a patch after all. Instead, I decided to fix and upgrade the system. Note the following is vast simplification of the process. The changes started with stripping out the "classname" and "superclassname" variables from ScriptObject and placing them inside of SimObject, along with their code. This has been done by others, I'm not claiming original credit for this. Now the system can be access by all the code, which is great. Now to fix those subtle errors the system introduces. The use of the super class system is flawed as it only allows inheritance in a predefined object. The ability to chain namespace together was required, leading to
This simple command would allow the code to control how the namespace were chained and linked together. This function can be called at anytime, allowing unchaining and dynamic changes. The first implication of this is that pure abstract code can be created and then chained into the system. The second implication of this is that the namespace chains can be modified by scriptors later on. This is allows an insane amount of polymorphism that can be added extremely easily with out requiring the ability to modify the original code. Something modders have dreamed of for a long time. A mod can be made, written separate from the base code and then linked into the code base. When there is an update in the original code base, it should not break modded code or require a complete rewrite of the modded code. This is something T2 could not do, leading to many mods that just outright modified the base code and were not easily upgradeable (some couldn't be even modified for basic changes).
Here is simple example of how the system works.
The scriptor does not have to take any special code to declare the new classes (namespace) in the scripts. Just make the code outright, there is no order required and new functions can be declared at anytime. This system also works with the preexisting package system, but their use isn't really required anymore. There are two functions of two new classes, Pilot and AiPilot. This are declared and exist unchained into the Namespace system until the chainNameSpaces is called. Allowing nice abstract code to be created. When chainNameSpaces is called, the AiPilot is made a child of Pilot which is made a child of PlayerData (an engine class). Note, when using this system you will need to be aware that Datablocks and normal objects are two distinct objects to the engine with their own separate Namespaces.
To see how the command modified the namespaces, here is a before an after.
A call to AiPilot::eject will link to Pilot::eject and then to PlayerData. This is exactly the same as C++ and Java work, but you can dynamically modify the chains here. The best method to learn the system is write some sample functions and then call chainNameSpace() and dumpNameSpace() to see how the system works. You will notice, the chain command will error out on most logic errors (all that I could think of while writing it and testing it for almost a year of use). With the new system, there is a need to allow scriptors to debug and control the Namespace chains with ZERO engine debugging. Not all scriptors have engine access and modders never will. The first functions are designed to print out the Namespace chains to assist when chaining the namespaces.
DumpNameSpaces and dumpNameSpace are designed to dump out all the existing namespace chains to allow scriptors to see whats going on. The following is the list of chains that already exist with the default engine that were completely hidden before.
The format of the dumps are always the same. It will show the Namespace and then the package associated with the given namespace. The package is usually (null), which means the Namespace is not associated with a package. The Namespace is then dumped and a linked list of the Parent namespace is shown to the top parent. Note there is a special global Namespace for all functions with out a namespace, this Namespace is just "" in c++ and is an empty string. I will refer to this Namespace as the root Namespace (as TGE's internal code refers to it). All namespace are a child of the root namespace, but dump may or may not display this. This is because the Namespace system doesn't always link to the root, but it is always implied. I'm not sure why the original coders did this, but it seams to work with out a problem. If you see a class with brackets next to the name, this is the class's package. "GuiTextCtrl[SuperGuiYay]" is the class GuiTextCtrl in the package named SuperGuiYay.
One of the things scriptors have always been needing are a list of functions and their operands that are built into the engine. These are usually mysterious and hidden but that has now been fixed. This function will list out all the functions associated with a given namespace. Two examples are given.
Notice how the functions are given. Some have their operands and a description while others do not. This is how the engine coders setup the functions and is not part of the Namespace command. This will give a list of the commands, calling the commands can then be used to find operational details. Now all functions are visible and can be found by scriptors quite easily.
As the Namespace system is now visible and usable, debugging the system is required. The normal trace command will always dump extra crap that just has to be filtered. This modificaiton of the trace command will allow scriptors to watch a single Namespace at a time. This is a great way to work on your code and your code alone, while not having to deal with other problems. Debugging is a lot easier.
The three above functions are designed to allow the scripts to be aware of the Namespace of objects. Since all objects have a Namespace and a given Namespace chain, objects can now be checked against this chain instead of their single class. This is same as the automatic upcasting in c++ and Java. Here is an example of their usage.
This allows code to work off a given Namespace instead of the opaque classname and types. These commands exist to allow pure polymorphism of the code. Instead of having to check if a given weapon is a certain known type, the script can simple check if the given object is a child of the weapon namespace. This allows addition of new weapons later, with ZERO modifications to this code. Instead of building tightly linked systems that require large elseifs or switch commands, you can simple check if the object is of the right class. This will help modders use preexisting code bases with out having to modify your code to get their code to work on top of it.
This patch should work on all version of TGE and TSE. It has been tested on TSE MS4.
TGE comes with a built in and fully functional script engine that has basic inheritance similar to that of Java or c++. The majority of this system is completely hidden from the scriptors and unknown to most engine coders. This system is in full force and extremely simple with very little error checking. It is quite easy for a script to run a foul of this system with out even knowing it, leading to subtle logic errors and fatal errors.
The most basic error is a namespace collision, ie where two distinct objects with the same class name. Most scriptors lack a full knowledge of all the classes with in the game, there are over 50 of them in the vanilla release. Its quite easy to name an object the same of one of the parent classes. When this happens, the code is overriding code in places other than expected. The new logic error that has been introduced most likely wont be fatal, but will lead undefined behavior for any other objects with the same parent namespace. While sitting down and walking the code in the debugger is great way to learn, this leads to massive downtime for usually simple code.
The most dangerous errors can be caused by use of the scriptObject class. This class was upgraded using a patch that was incorporated into TGE around v1.4. The patch by Bryan Edds can be found here:
www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=6335
This patch introduced a full object oriented system to TGE. A very nice upgrade which this modification is partially based. This system how ever did not account for a scriptors lack of knowledge of the namespace system. Since scriptors can't even access the Namespace system, this could lead to very entertaining errors, usually fatal. Here is an example of the problem:
datablock PlayerData(HeavyArmor)
{
className = SimObject
SuperClassName = PlayerData
}Once this piece of code is executed, the game will crash but not at the code. It will crash the next time any code calling a SimObject is called. Thats every object in the game. This is an error that is almost impossible to debug or to find as the code stands with out engine debugger access. If your lucky, it will start an infinite loop with the code following the declaration.
For those who don't understand what this command has done, its not all that simple. SimObject is the master class for every object in the game. SimObject is a class type, but it is also a Namespace. This is the hidden Namespace system already built into the game to allow inheritance in the engine and scripts. When a class is created in the engine (in c++), the Namespace is setup automatically by the engine macros. This is completely hidden unless you sit down and follow the code paths, which usually isn't a problem if the engine coder doesn't make a mistake in their inheritance, but the complier helps out a lot here. The system creates a Namespace tree that is identical to the inheritance tree of c++ (without multiple inheritance), which is really smart. This allows a quite command to walk up the namespaces each time a script calls "parent::". This is part of the patch from Bryan Edds, as previously all scripts would have to specifically use their parent's class name instead of parent. The parent command allow a nice blind inheritance system which allows for polymorphism, but also introduced so many more subtle errors. Here is the inheritance tree from ScriptObject which can easily be printed with this modification.
==>dumpnamespace("scriptobject");
Namespace: "ScriptObject" Package: "(null)"
ScriptObject -> SimObjectThe following code is how a script would use the parent command.
function ScriptObject::somefunction(%this)
{
parent::somefunction(%this);
//do stuff
}When the code is executed, with the given Namespace from above. The code would be interpretered as follows by the script engine.
function ScriptObject::somefunction(%this)
{
SimObject::somefunction(%this);
//do stuff
}Now this is a great system, but its quite mysterious and can be very buggy. Lets take a look at the bad code again.
datablock PlayerData(HeavyArmor)
{
className = SimObject
SuperClassName = PlayerData
}This would completely fubar Namespace tree. Causing
==>dumpnamespace("SimObject");
Namespace: "ScriptObject" Package: "(null)"
SimObject -> PlayerData -> ShapeBaseData -> GameBaseData -> SimDataBlock -> SimObjectas compared to the normal
==>dumpnamespace("playerdata");
Namespace: "PlayerData" Package: "(null)"
PlayerData -> ShapeBaseData -> GameBaseData -> SimDataBlock -> SimObjectNotice a recursive definition that is now part of SimObject's Namespace. This will either lead to a fatal error or an infinite loop. This error isn't possible with the new system, it will error out and kill the command, usually allowing the game to load. Catching this error with the current code base would require someone to literally walk the data structures associated with the hidden built in Namespace system, good luck with that. This is an error that is impossible to fix with out engine code access, leaving poor scriptors with a bug that can't be fixed or diagnosed. I would assume many people have hit up against, just as I have when attempting to do any advanced code.
It would be reasonable to say scrap the new system, its just a patch after all. Instead, I decided to fix and upgrade the system. Note the following is vast simplification of the process. The changes started with stripping out the "classname" and "superclassname" variables from ScriptObject and placing them inside of SimObject, along with their code. This has been done by others, I'm not claiming original credit for this. Now the system can be access by all the code, which is great. Now to fix those subtle errors the system introduces. The use of the super class system is flawed as it only allows inheritance in a predefined object. The ability to chain namespace together was required, leading to
ConsoleFunction(chainNameSpaces, bool, 3, 999, "bool chainNameSpaces(child, <parent1...parentN>) Chains Namespaces Together")
This simple command would allow the code to control how the namespace were chained and linked together. This function can be called at anytime, allowing unchaining and dynamic changes. The first implication of this is that pure abstract code can be created and then chained into the system. The second implication of this is that the namespace chains can be modified by scriptors later on. This is allows an insane amount of polymorphism that can be added extremely easily with out requiring the ability to modify the original code. Something modders have dreamed of for a long time. A mod can be made, written separate from the base code and then linked into the code base. When there is an update in the original code base, it should not break modded code or require a complete rewrite of the modded code. This is something T2 could not do, leading to many mods that just outright modified the base code and were not easily upgradeable (some couldn't be even modified for basic changes).
Here is simple example of how the system works.
function Pilot::eject(%this)
{
parent::eject(%this);
//eject the pilot
}
function AiPilot::eject(%this)
{
parent::eject(%this);
//kill the pilot
}
chainNameSpaces("AiPilot", "Pilot", "PlayerData");The scriptor does not have to take any special code to declare the new classes (namespace) in the scripts. Just make the code outright, there is no order required and new functions can be declared at anytime. This system also works with the preexisting package system, but their use isn't really required anymore. There are two functions of two new classes, Pilot and AiPilot. This are declared and exist unchained into the Namespace system until the chainNameSpaces is called. Allowing nice abstract code to be created. When chainNameSpaces is called, the AiPilot is made a child of Pilot which is made a child of PlayerData (an engine class). Note, when using this system you will need to be aware that Datablocks and normal objects are two distinct objects to the engine with their own separate Namespaces.
To see how the command modified the namespaces, here is a before an after.
//before chainNameSpaces() is called
==>dumpnamespace("Pilot");
Namespace: "Pilot" Package: "(null)"
Pilot
==>dumpnamespace("AiPilot");
Namespace: "AiPilot" Package: "(null)"
AiPilot
//Chain call
==>chainNameSpaces("AiPilot", "Pilot", "PlayerData");
//After
==>dumpnamespace("AiPilot");
Namespace: "AiPilot" Package: "(null)"
AiPilot -> Pilot -> PlayerData -> ShapeBaseData -> GameBaseData -> SimDataBlock -> SimObjectA call to AiPilot::eject will link to Pilot::eject and then to PlayerData. This is exactly the same as C++ and Java work, but you can dynamically modify the chains here. The best method to learn the system is write some sample functions and then call chainNameSpace() and dumpNameSpace() to see how the system works. You will notice, the chain command will error out on most logic errors (all that I could think of while writing it and testing it for almost a year of use). With the new system, there is a need to allow scriptors to debug and control the Namespace chains with ZERO engine debugging. Not all scriptors have engine access and modders never will. The first functions are designed to print out the Namespace chains to assist when chaining the namespaces.
ConsoleFunction(DumpNameSpaces, void,1,3,"([string Name = 0, string Package = 0]) List all the NameSpaces.") ConsoleFunction(dumpNameSpace, void,2,2,"(string Namespace) List all the NameSpaces parent to namespace. Enter \"0\" to list all of the certain type.")
DumpNameSpaces and dumpNameSpace are designed to dump out all the existing namespace chains to allow scriptors to see whats going on. The following is the list of chains that already exist with the default engine that were completely hidden before.
==>dumpnamespaces(); Namespace: "sgLightEditorNewDBName" Package: "(null)" sgLightEditorNewDBName -> GuiTextEditCtrl -> GuiTextCtrl -> GuiControl -> SimGroup -> SimSet -> SimObject Namespace: "sgLightEditorNewDB" Package: "(null)" sgLightEditorNewDB -> GuiControl -> SimGroup -> SimSet -> SimObject << List trimmed for this doc but in the console, this will list for several pages >>
The format of the dumps are always the same. It will show the Namespace and then the package associated with the given namespace. The package is usually (null), which means the Namespace is not associated with a package. The Namespace is then dumped and a linked list of the Parent namespace is shown to the top parent. Note there is a special global Namespace for all functions with out a namespace, this Namespace is just "" in c++ and is an empty string. I will refer to this Namespace as the root Namespace (as TGE's internal code refers to it). All namespace are a child of the root namespace, but dump may or may not display this. This is because the Namespace system doesn't always link to the root, but it is always implied. I'm not sure why the original coders did this, but it seams to work with out a problem. If you see a class with brackets next to the name, this is the class's package. "GuiTextCtrl[SuperGuiYay]" is the class GuiTextCtrl in the package named SuperGuiYay.
ConsoleFunction(dumpNameSpaceFunctions, void,2,3,"(string Namespace, [string package = 0]) List All the functions for Namespace")
One of the things scriptors have always been needing are a list of functions and their operands that are built into the engine. These are usually mysterious and hidden but that has now been fixed. This function will list out all the functions associated with a given namespace. Two examples are given.
==>dumpnamespacefunctions(SimObject);
saveobj.save(fileName, <selectedOnly>)
setNameobj.setName(newName)
getNameobj.getName()
getClassNameobj.getClassName()
getIdobj.getId()
getGroupobj.getGroup()
deleteobj.delete()
getNameSpace() Retrieves the Namespace of the Object.
isChildNameSpace(Namespace Parent, [string Package]) Checks if the Namspace of the object is a child of the Parent Namespace.
scheduleobject.schedule(time, command, <arg1...argN>);
hasFunctionobj.hasFunction(funcName)
dumpobj.dump(): dump available fields and functions of object to console. use dumpF for just the fields.
dumpFobj.dumpF(): dump available fields of object to console.
getTypeobj.getType()
isMethodobj.isMethod(string method name)
==>dumpnamespacefunctions("Player");
getStateReturn the current state name.
getDamageLocation(Point3F pos)
setArmThread(string sequenceName)
setActionThread(string sequenceName, bool hold, bool fsp)
setControlObject(ShapeBase obj)
getControlObjectGet the current control object.
clearControlObject
checkDismountPoint(Point3F oldPos, Point3F pos)Notice how the functions are given. Some have their operands and a description while others do not. This is how the engine coders setup the functions and is not part of the Namespace command. This will give a list of the commands, calling the commands can then be used to find operational details. Now all functions are visible and can be found by scriptors quite easily.
ConsoleFunction(trace, void, 2, 3, "(bool traceActive[, Namespace trackOnlyNamespace = 0]) Traces every function call.")
As the Namespace system is now visible and usable, debugging the system is required. The normal trace command will always dump extra crap that just has to be filtered. This modificaiton of the trace command will allow scriptors to watch a single Namespace at a time. This is a great way to work on your code and your code alone, while not having to deal with other problems. Debugging is a lot easier.
ConsoleFunction(IsNameSpaceChild, bool,3,5,"(Namespace Child, Namespace Parent, [string ChildPackage = 0, string parentPackage = 0]) Check is Child is a child of Parent.") ConsoleMethod(SimObject, getNameSpace, const char *, 2, 2, "() Retrieves the Namespace of the Object.") ConsoleMethod(SimObject, isChildNameSpace, bool, 3, 4, "(Namespace Parent, [string Package]) Checks if the Namspace of the object is a child of the Parent Namespace.")
The three above functions are designed to allow the scripts to be aware of the Namespace of objects. Since all objects have a Namespace and a given Namespace chain, objects can now be checked against this chain instead of their single class. This is same as the automatic upcasting in c++ and Java. Here is an example of their usage.
$obj-> isChildNameSpace(%other->getNameSpace()); //is the same as IsNameSpaceChild($obj-> getNameSpace (), %other->getNameSpace());
This allows code to work off a given Namespace instead of the opaque classname and types. These commands exist to allow pure polymorphism of the code. Instead of having to check if a given weapon is a certain known type, the script can simple check if the given object is a child of the weapon namespace. This allows addition of new weapons later, with ZERO modifications to this code. Instead of building tightly linked systems that require large elseifs or switch commands, you can simple check if the object is of the right class. This will help modders use preexisting code bases with out having to modify your code to get their code to work on top of it.
#2
Impressive in it's elegance. I have battled with branching namespaces where I would like to double up such as:
AIBattleArmorData -> BattleArmorData -> PlayerData
AIInfantryData -> InfantryData -> PlayerData
Since AIBattleArmor and AIInfantryData are just copies to work with the namespace, I could with your system do:
AIInfantryData - > InfantryData -> PlayerData
AIInfantryData - > BattleArmorData -> PlayerData
Thanks
01/05/2007 (2:37 pm)
Jack,Impressive in it's elegance. I have battled with branching namespaces where I would like to double up such as:
AIBattleArmorData -> BattleArmorData -> PlayerData
AIInfantryData -> InfantryData -> PlayerData
Since AIBattleArmor and AIInfantryData are just copies to work with the namespace, I could with your system do:
AIInfantryData - > InfantryData -> PlayerData
AIInfantryData - > BattleArmorData -> PlayerData
Thanks
#3
AIInfantryData - > InfantryData -> PlayerData
AIInfantryData - > BattleArmorData -> PlayerData
Your example would have previously lead to an infinite loop in the game engine. The mod corrects this by merely warning you of this error and denying the command.
You might want to try something similiar to this
BattleArmorData -> AIInfantryData - > InfantryData -> PlayerData
Or possibly spliting up your code to allow more scalabilty. Remember you have two classes associated with every object, shapebase and datablock.
AIPlayer -> Player
PlayerController -> Player
with Datablocks:
BattleArmorData - > InfantryData -> PlayerData
This will split up your controlling code and your setup. This way your datablocks can be setup to be universal and unaware if AI is using them or a human controller is.
01/05/2007 (10:12 pm)
The system only allows a single parent relationship. AIInfantryData - > InfantryData -> PlayerData
AIInfantryData - > BattleArmorData -> PlayerData
Your example would have previously lead to an infinite loop in the game engine. The mod corrects this by merely warning you of this error and denying the command.
You might want to try something similiar to this
BattleArmorData -> AIInfantryData - > InfantryData -> PlayerData
Or possibly spliting up your code to allow more scalabilty. Remember you have two classes associated with every object, shapebase and datablock.
AIPlayer -> Player
PlayerController -> Player
with Datablocks:
BattleArmorData - > InfantryData -> PlayerData
This will split up your controlling code and your setup. This way your datablocks can be setup to be universal and unaware if AI is using them or a human controller is.
#4
So, I can only have single lines, not overlapping ones, bummer. However, the notification of a conflict is still very helpful.
One of my issues is that sometimes I want a object class to inherit from two different lines. I end up having to duplicate variables. I thought I was going to be able to "force" the inheritance lines with your resource.
Thanks.
01/06/2007 (7:36 am)
Jack,So, I can only have single lines, not overlapping ones, bummer. However, the notification of a conflict is still very helpful.
One of my issues is that sometimes I want a object class to inherit from two different lines. I end up having to duplicate variables. I thought I was going to be able to "force" the inheritance lines with your resource.
Thanks.
#5
From what it appears, you are attempting to having different variables for your different classes. I suggest using the seperate and already existing object and its associated datablock. Setup your code for the object to be universal and change according to the respective datablock. Its usually better to refactor your design to seperate the config from the code than to do a real complicated inheirtance scheme.
01/06/2007 (9:10 pm)
The script engine only allows a single parent to each namespace. The use of parent:: would not be usable with multiple parents. The namespaces are held in a tree, to change this would require a complete rewrite of the script engine. You can how ever make your own namespaces and do a explicit namespace relationship. You can doing this by entering the namespace manually, this method doesnt use the namespace mod. From what it appears, you are attempting to having different variables for your different classes. I suggest using the seperate and already existing object and its associated datablock. Setup your code for the object to be universal and change according to the respective datablock. Its usually better to refactor your design to seperate the config from the code than to do a real complicated inheirtance scheme.
#6
This is great work. I have a couple of quick questions in the hopes of solidifying my understanding...
I'm not sure I fully understand what you meant by: "...use of the super class system is flawed as it only allows inheritance in a predefined object..." Is your point here that a pure abstract class could be put into the chain and then defined later?
Also when you say: "...The second implication of this is that the namespace chains can be modified by scriptors later on..." How is changing the call to chainNameSpaces different from changing the "class" and "superclass" variables on the datablocks?
Finally, wont this patch "break" existing mods that use the class and superclass variables to link the namespaces? Any datablock using those variables would need to have a call to chainNamespaces?
I think your system sounds awesome and I fully intend to integrate it into my project. Your explanation is also very complete and I want to make sure that I understand all of your points.
Thanks for all of your time.
Todd
01/08/2007 (12:44 pm)
Hey Jack,This is great work. I have a couple of quick questions in the hopes of solidifying my understanding...
I'm not sure I fully understand what you meant by: "...use of the super class system is flawed as it only allows inheritance in a predefined object..." Is your point here that a pure abstract class could be put into the chain and then defined later?
Also when you say: "...The second implication of this is that the namespace chains can be modified by scriptors later on..." How is changing the call to chainNameSpaces different from changing the "class" and "superclass" variables on the datablocks?
Finally, wont this patch "break" existing mods that use the class and superclass variables to link the namespaces? Any datablock using those variables would need to have a call to chainNamespaces?
I think your system sounds awesome and I fully intend to integrate it into my project. Your explanation is also very complete and I want to make sure that I understand all of your points.
Thanks for all of your time.
Todd
#7
The main meaning behind the sentence is that with the previous system, you had to have a script object that was created and then the classes would be linked. This system removes that requirement completly. As you said, it allows the creation of pure abstract class that is not attached to any object compile time. You can then later chain (or unchain) in the class where you choose and when you choose.
Setting "class" and "superclass" is a compile time event (secondary compile, when the script object is declared) only. You can change it after but it will not relink the namespaces, it effectively becomes a ghost command. Attempting to change it afterword expecting the namespaces to change will lead to extremely difficult bugs to find as this system doesnt not modify this for backwards compat . If a scripter/modder down the road wants to modify your class, they will have to modify your source code to make it work. This system removes that requirement and allows the namespaces to be modified at any time.
Negative. The functionality of "class" and "superclass" is unmodified at functional level. You can set these and expect they will work exactly the same, unless you made a mistake and were actually breaking the namespaces which you will recieve a nice warning about in the console. This two variables only existed previously in the scriptobject class (and it children), this system extends this functionality to every object in the game (that scripts can touch).
As the doc said, this is fully backwards compatible. If your code suffered from one of the logic errors noted in the doc above, it probably doesnt work already and you will now get a warning about it at runtime. I have tested this on several games already and it has not broken any scripts that Im aware of. This system is completely passive and wont interact with the scripts unless it is used.
01/08/2007 (1:20 pm)
Quote:
I'm not sure I fully understand what you meant by: "...use of the super class system is flawed as it only allows inheritance in a predefined object..." Is your point here that a pure abstract class could be put into the chain and then defined later?
The main meaning behind the sentence is that with the previous system, you had to have a script object that was created and then the classes would be linked. This system removes that requirement completly. As you said, it allows the creation of pure abstract class that is not attached to any object compile time. You can then later chain (or unchain) in the class where you choose and when you choose.
Quote:
Also when you say: "...The second implication of this is that the namespace chains can be modified by scriptors later on..." How is changing the call to chainNameSpaces different from changing the "class" and "superclass" variables on the datablocks?
Setting "class" and "superclass" is a compile time event (secondary compile, when the script object is declared) only. You can change it after but it will not relink the namespaces, it effectively becomes a ghost command. Attempting to change it afterword expecting the namespaces to change will lead to extremely difficult bugs to find as this system doesnt not modify this for backwards compat . If a scripter/modder down the road wants to modify your class, they will have to modify your source code to make it work. This system removes that requirement and allows the namespaces to be modified at any time.
Quote:
Finally, wont this patch "break" existing mods that use the class and superclass variables to link the namespaces? Any datablock using those variables would need to have a call to chainNamespaces?
Negative. The functionality of "class" and "superclass" is unmodified at functional level. You can set these and expect they will work exactly the same, unless you made a mistake and were actually breaking the namespaces which you will recieve a nice warning about in the console. This two variables only existed previously in the scriptobject class (and it children), this system extends this functionality to every object in the game (that scripts can touch).
As the doc said, this is fully backwards compatible. If your code suffered from one of the logic errors noted in the doc above, it probably doesnt work already and you will now get a warning about it at runtime. I have tested this on several games already and it has not broken any scripts that Im aware of. This system is completely passive and wont interact with the scripts unless it is used.
Quote:=)
I think your system sounds awesome and I fully intend to integrate it into my project. Your explanation is also very complete and I want to make sure that I understand all of your points.
#8
Thank you again for the very complete explanations. This system would be worth implementing if ALL it did was provide the warnings on the logic errors. Now that I understand some of your points better, the added functionality is really going to help us with debugging and keeping our code modular.
This is one of those resources that reaffirms the strength and helpfulness of the GG community. Thank you for sharing.
Todd
01/08/2007 (1:43 pm)
Jack,Thank you again for the very complete explanations. This system would be worth implementing if ALL it did was provide the warnings on the logic errors. Now that I understand some of your points better, the added functionality is really going to help us with debugging and keeping our code modular.
This is one of those resources that reaffirms the strength and helpfulness of the GG community. Thank you for sharing.
Todd
#9
First I manually patched this into TGE 1.5 sdk, so some of my problems may possibly arrive from their. However I had on one small compliation problem and that as to do with simbase.h, my interpretation of the patchfile lead me to place mClassName and mSuperClassName as private, making these public allowed the compilation to complete.
The executable loads up fine and I am able to use all your examples without causing a hang on the stock TGE examples. Except when I deliberately messup the namespace calling. Your first example, im given to understand that this patch should catch such errors and prevent the game for hanging, crashing. However it does hang when I preform that action.
Also my own mods hang, Now I expected this as Im 100% sure I have messed up namespace calling hence my interest in this resource.
Have you tried it on TGE 1.5? Any help you could offer would be greatly appreciated.
Another point as well, whilst messing with the trace console function I traced simobject from tutorial.base main menu. Loading up the world editor caused a fatal error to occur whilst I was tracing.
Fatal: (\torque1.5sdk\engine\platformwin32\winstrings.cc @ 301) dSprintf wrote to more memory than the specified buffer size - Stack Corruption Possible.
A little more digging and I discovered that the program would appear to infinitley loop in the bool Namespace::isChild(Namespace *parent)
01/25/2007 (7:45 am)
Hi jack, excellent resource, but I have run into a few problems. First I manually patched this into TGE 1.5 sdk, so some of my problems may possibly arrive from their. However I had on one small compliation problem and that as to do with simbase.h, my interpretation of the patchfile lead me to place mClassName and mSuperClassName as private, making these public allowed the compilation to complete.
The executable loads up fine and I am able to use all your examples without causing a hang on the stock TGE examples. Except when I deliberately messup the namespace calling. Your first example, im given to understand that this patch should catch such errors and prevent the game for hanging, crashing. However it does hang when I preform that action.
Also my own mods hang, Now I expected this as Im 100% sure I have messed up namespace calling hence my interest in this resource.
Have you tried it on TGE 1.5? Any help you could offer would be greatly appreciated.
Another point as well, whilst messing with the trace console function I traced simobject from tutorial.base main menu. Loading up the world editor caused a fatal error to occur whilst I was tracing.
Fatal: (\torque1.5sdk\engine\platformwin32\winstrings.cc @ 301) dSprintf wrote to more memory than the specified buffer size - Stack Corruption Possible.
A little more digging and I discovered that the program would appear to infinitley loop in the bool Namespace::isChild(Namespace *parent)
#10
It looks like you have a memory leak but most likely, that is a side effect of another bug. Can you load up the game and run
Usually when the engine hangs in isChild(), there is a corrupted tree with a child and parent being the same (at some point in the tree). The mod directly looks for this error while linking, but this error can be caused by other code (mainly manually declared cpp objects) the mod doesnt control.
01/25/2007 (11:23 pm)
I havent tried it on tge 1.5 yet.It looks like you have a memory leak but most likely, that is a side effect of another bug. Can you load up the game and run
dumpnamespaces();and email me the log. If that doesnt crash, then run your code and email me that log too. That should help with tracking down the cause of problem.
Usually when the engine hangs in isChild(), there is a corrupted tree with a child and parent being the same (at some point in the tree). The mod directly looks for this error while linking, but this error can be caused by other code (mainly manually declared cpp objects) the mod doesnt control.
#11
I can send you the patched source code as well I patched onto a clean distribution of 1.5sdk so you should be able to repeat the bug.
01/26/2007 (4:44 am)
Ok will do Jack, is the email in your profile valid?I can send you the patched source code as well I patched onto a clean distribution of 1.5sdk so you should be able to repeat the bug.
#12
Can you also run the dump command before and after you load your scripts.
01/26/2007 (10:36 am)
Yes.Can you also run the dump command before and after you load your scripts.
#13
01/27/2007 (1:27 pm)
All requested info has been sent to you email address. Except for dump after I run my scripts, due to hanging of the game.
#14
01/27/2007 (10:00 pm)
It appears that gg changed some vital stuff in tse4.2/tge1.5. Im working on a patch now, thanx for the logs.
#15
Changes:
Added more intensive checks for invalid linking.
Added infinite loop checks for namespace (incase namespace is broke via mod or by external code).
02/04/2007 (7:33 pm)
Updated version has been uploaded. It will now patch cleanly into tse 2.4 and tge 1.5 (thanks ben for testing tge). Changes:
Added more intensive checks for invalid linking.
Added infinite loop checks for namespace (incase namespace is broke via mod or by external code).
#16
02/13/2007 (6:27 am)
Is this patch for TGB also? I think that inheritance in TGB is a little different from TGE and TSE.
#17
02/13/2007 (10:43 am)
I dont have a license for TGB to test it with. If TGB is based on TGE's script engine, then it should patch in clean. If you want to try, that would be helpful.
#18
For detail's sake, I was having the problem that a function was being called on a completely wrong datablock.
I had something like this:
When datablock methods would get invoked on a DemoPlayer it would call the method from PlayerBody instead of AiPlayerBody. Even though the object's datablock was named 'DemoPlayer' and the class field read 'AiPlayerBody'. It seems that in the chain when the class was changed from AiPlayerBody the methods were not relinked to the new ones but kept their reference to the old ones (those in PlayerBody).
A couple questions.
1. Do I need the namespace unlinking code in ScriptObject::onRemove()
2. How about ScriptGroup and ScriptClass? They have similar code for namespace management that I dont think is necessary anymore. I did the same changes to ScriptGroup as you did to ScriptObject and there hasn't been any complaints by my engine. ScriptClass however is a little different and I'm not sure I want to muck around in there. Did you modify these classes at all?
09/28/2007 (4:27 am)
This resource fixed a script bug I was having clean away! Thank you Jack.For detail's sake, I was having the problem that a function was being called on a completely wrong datablock.
I had something like this:
datablock PlayerData(ControlPlayerData) { class = PlayerBody; }
datablock PlayerData(AiPlayerData : ControlPlayerData) { class = AiPlayerBody; }
datablock PlayerData(DemoPlayer : AiPlayerData) { }When datablock methods would get invoked on a DemoPlayer it would call the method from PlayerBody instead of AiPlayerBody. Even though the object's datablock was named 'DemoPlayer' and the class field read 'AiPlayerBody'. It seems that in the chain when the class was changed from AiPlayerBody the methods were not relinked to the new ones but kept their reference to the old ones (those in PlayerBody).
A couple questions.
1. Do I need the namespace unlinking code in ScriptObject::onRemove()
2. How about ScriptGroup and ScriptClass? They have similar code for namespace management that I dont think is necessary anymore. I did the same changes to ScriptGroup as you did to ScriptObject and there hasn't been any complaints by my engine. ScriptClass however is a little different and I'm not sure I want to muck around in there. Did you modify these classes at all?
#19
1.
Unlinking your namespaces when you delete your script objects is a great way to cause a huge number of bugs. What happens when you have multiple script objects that are a child of the script object being removed? They will be cutoff and will cause an extremely difficult bug to develop. What happens if the classname has been changed somehow in game? You will be unlinking the wrong classes. What happens when there are more than one instance of the object ingame and one is removed? The namespaces will be unlinked, while the surviving script objects will be orphaned.
Thats why the code is removed by the patch, its just plain dangerous.
2.
I dont remember these classes {scriptgroup, scriptclass} being here when I wrote the patch. It appears GG merged in a newer version of the oo script patch. They definitely stink of a hack job
As it stands, I would suggest you do not touch this code and should not dare to use it. They appear to be a different method of creating inheritance via real objects and not namespaces as my patch has done, which is redundant if you think about it as it is still based on the namespace system to actually implement the inheritance.
If you need to use scriptgroup just strip out all the code. My code will just do the job it is attempting to do as it is based in simobject, which scriptgroup inherits from. It looks like someone just copy and pasted directly from the scriptobject class to create the scriptgroup class.
Script class seams to use a new librarygroup class. As it doesnt appear to affect the namespaces, I would suggest to not edit it.
09/28/2007 (5:56 pm)
Hehe, its apparent you expected java or c++ style inheritance and not just copying which vanilla tse/tge implements. I'm glad the patch fixed it for you.1.
Unlinking your namespaces when you delete your script objects is a great way to cause a huge number of bugs. What happens when you have multiple script objects that are a child of the script object being removed? They will be cutoff and will cause an extremely difficult bug to develop. What happens if the classname has been changed somehow in game? You will be unlinking the wrong classes. What happens when there are more than one instance of the object ingame and one is removed? The namespaces will be unlinked, while the surviving script objects will be orphaned.
Thats why the code is removed by the patch, its just plain dangerous.
2.
I dont remember these classes {scriptgroup, scriptclass} being here when I wrote the patch. It appears GG merged in a newer version of the oo script patch. They definitely stink of a hack job
//----------------------------------------------------------------------------- // Script Class placeholder //----------------------------------------------------------------------------- class ScriptClass : public SimObject
As it stands, I would suggest you do not touch this code and should not dare to use it. They appear to be a different method of creating inheritance via real objects and not namespaces as my patch has done, which is redundant if you think about it as it is still based on the namespace system to actually implement the inheritance.
If you need to use scriptgroup just strip out all the code. My code will just do the job it is attempting to do as it is based in simobject, which scriptgroup inherits from. It looks like someone just copy and pasted directly from the scriptobject class to create the scriptgroup class.
Script class seams to use a new librarygroup class. As it doesnt appear to affect the namespaces, I would suggest to not edit it.
#20
09/29/2007 (6:45 am)
Well actually I did the patch by hand and it makes no reference to removing anything from ScriptObject's onRemove method (I've just rechecked it to make sure). Unless I am reading the patch wrong. 
Torque 3D Owner Caylo Gypsyblood