Game Development Community

A very strange issue with objects' properties being changed by a console method

by Daniel Buckmaster · in General Discussion · 10/01/2009 (3:10 am) · 4 replies

Disclaimer: this thread relates to a custom object I'm implementing, so any problems are self-caused. But I'd really love to know if anyone can help.

Along with the million other things on my plate right now, I've knocked up a quick Behaviour Tree object. The details of what a BT is aren't important, but here are the bare bones of the implementation:
The BT object is inherited from ScriptObject. To make this possible, I had to move the definition of ScriptObject out of scriptObject.cc into simBase.h.
Inside BT is declared a Node struct. Each Node has a name and a parameter (both const char* type), as well as pointers to parent and child Nodes. Each BT object has a Vector<Node*>.
In an addNode method, I create a new Node object and set its name and parameter based on values passed in from scripts. I also set a parent node for it, based on a name search.

Now, I'm adding nodes in script like this:
function ExampleBehaviourTree::createTree(%this)
{
   //Add a node named "root" with no parent or parameter
   %this.addNode("root","","");
      //Add a node named "behaviour1" with parent "root"
      %this.addNode("behaviour1","root","");
      //Add another node named "behaviour2" with parent "root" also
      %this.addNode("behaviour2","root","");
}
Now, stepping through the addNode method, it seems to work fine the first time. A node is created, added to the tree, and its name and parameter are set properly.

The second time it's called, to add the node "behaviour1", things are screwy. Even before the addNode method is called by the console method, I can browse inside the BT object, and I can see that the node I created last time has been altered. Its name has changed to "behaviour1", and its parameter to "our1".

Wha...??

It seems like the input to the current execution of the console method has somehow been stuck into the Node object that I created last time the console method was called. What could possibly be causing this?

I'm not going to post up the full class here (it's not that big), but if you think you know what's going on and need to see the source, shoot me an email. I'd be really grateful if anyone has even an inkling of what could be causing this.



EDIT: I tried separating the Node to an external class. No dice.

EDIT: Tried inheriting from SimObject instead of ScriptObject. Still buggered up.

EDIT: Tried inheriting from GameBase with a datablock. Nothing. It must be something I'm defining wrong.

About the author

Studying mechatronic engineering and computer science at the University of Sydney. Game development is probably my most time-consuming hobby!


#1
10/02/2009 (8:36 pm)
It sounds like you're just copying string (const char *) pointers rather than copying the string itself. For efficiency reasons, Torque reuses the memory that it passes into your ConsoleFunction/ConsoleMethodss from scripts, so they technically only last as long as your function is executing. If you want to persistently retain the string data, you have several options:

1) Copy the string into a fixed-size array with dStrcpy(). The drawback is that this limits the size of the string you can store. (Though typically you probably won't need more than 512 or maybe 1024 bytes, but you know what you need better than I do.)

2) Use StringTable->insert(). This inserts the string into the global string table (case-insensitive unless you tell it otherwise). You should only use this with strings that are static or likely to be reused many times (e.g. property names), as strings in the global string table are never released from memory. As long as you are not using dynamic strings with this, though, you should be OK.

3) Use dStrdup() to duplicate the string. This puts the onus on you to then dFree() the string when you are finished with it; else you will have a memory leak. A suitable place to do this would be in your Node destructor, but you will also need to free the old string if you replace it with a new one.
#2
10/02/2009 (9:35 pm)
That seems to have been exactly the problem! Thank you very much for pointing that out - now I think about it, it does make sense. I guess that's why I should have started learning C++ with the basics, not with modifying Torque ;P.

I've gone with StringTable->insert for now, since it required the least modifications to my code ;P. I think I might switch to dStrcpy, though. The strings I'm storing are static, but I don't really know how often they'll be used (once all the nodes are created, they rely on direct parent/child pointers rather than names).

Thank you once again! This problem was having me tearing my hair out :P.

EDIT: One further, slightly related question that I thought of when you mentioned freeing strings. In the destructor for my BT object, should I be deleting all the Nodes in its node vector?
#3
10/02/2009 (10:05 pm)
If your Nodes aren't shared anywhere else, then I would say yes (and again also if you ever remove or replace them individually).

As I understand it (or at least so the code comments with the boldface "***WARNING***" tell me), Torque's Vector<> template class is not like STL's std::vector in that it does not call the destructor on its elements when they are removed -- it's only for POD (plain old data) types. In this case, that doesn't matter, because you're using Vector<Node *> and not Vector<Node>, so you'd have to manually delete your allocated Nodes either way, but it's something to be aware of nonetheless.
#4
10/03/2009 (12:04 am)
Okay, thanks. Hopefully once I start uni (engineering/computer science) I'll be a little more knowledgable about this sort of stuff. For now, all my knowledge of C++ comes from implementing resources :P.
BehaviourTree::~BehaviourTree()
{
   for(U32 i = 0; i < nodes.size(); i++)
      delete nodes[i];
   nodes.clear();
}

EDIT: I think that code might be wrong :P. I'll figure it out.

EDIT: for the record:
while(nodes.size())
{
   delete nodes[0];
   nodes.erase((U32)0);
}