List of Datablocks by Type
by Michael Perry · in Torque Game Engine · 06/11/2007 (7:13 am) · 11 replies
Greetings all!
I need to grab all loaded datablocks by their type (Item, or Player, etc) and store them in a GUI List. I've done some searching already and haven't found an acceptable thread containing the solution.
I was hoping for a simple script solution, but I posted here in case an engine change was required.
I'm aware of the following engine snippets:
Anyone know of a thread containing the solution, or have a recommendation? All help is appreciated, and much thanks in advance! =)
I need to grab all loaded datablocks by their type (Item, or Player, etc) and store them in a GUI List. I've done some searching already and haven't found an acceptable thread containing the solution.
I was hoping for a simple script solution, but I posted here in case an engine change was required.
I'm aware of the following engine snippets:
Sim::findObject("Armor"); [b]// Finds a specific datablock by name[/b]
// gRootGroup - [b] Contains all datablocks loaded in game[/b]Anyone know of a thread containing the solution, or have a recommendation? All help is appreciated, and much thanks in advance! =)
About the author
Programmer.
#2
06/11/2007 (8:13 am)
Yup. The GuiTreeViewCtrl loads everything based on gRootGroup. The only problem is isolation the specific datablocks, and not using GuiTreeViewCtrl. I want to throw the datablocks and their names in a GuiListCtrl.
#3
Rethinking it, my post wasn't very helpful...I was planning on giving you a place to start (model it after the editor), but I've gone over and contradicted myself.
Back to the original question: I don't think very many things are exposed to the script, but it should be pretty easy to do in the C++.
You should be able to call
Maybe this will take you further though:
Hope this helps.
06/11/2007 (10:42 am)
Done more looking, but I think the EditorTree which derives from GuiTreeViewCtrl reads the mission file to populate itself, not gRootGroup. It finds objects and gets their fields from gRootGroup based on a selection event.Rethinking it, my post wasn't very helpful...I was planning on giving you a place to start (model it after the editor), but I've gone over and contradicted myself.
Back to the original question: I don't think very many things are exposed to the script, but it should be pretty easy to do in the C++.
You should be able to call
Sim::getRootGroupwhich will return a pointer to the rootgroup.
Maybe this will take you further though:
for (SimSetIterator obj(Sim::getRootGroup()); *obj; ++obj)
{
...whatever you want to do with said the obj...
}Hope this helps.
#4
In your example, once I have something in obj, what variable contains the datablock type? How would I check to see if it were a Player type or Item type?
That's where I am currently stumped at, and then I have to think about passing that entire list of strings back to the script side of things.
Fun with the engine, always a good time =)
*EDIT*
......ahem.....
"Wow, Michael that's an interesting question....how would you get the type? Did you ever think of calling getType() on the object and comparing it to an existing typemask...?"
"....i r stupid...."
*EDIT*
06/11/2007 (10:49 am)
Well, I was already that far. I'm aware of what exists in the engine, it's just a matter of sorting it and grabbing what I need.In your example, once I have something in obj, what variable contains the datablock type? How would I check to see if it were a Player type or Item type?
That's where I am currently stumped at, and then I have to think about passing that entire list of strings back to the script side of things.
Fun with the engine, always a good time =)
*EDIT*
......ahem.....
"Wow, Michael that's an interesting question....how would you get the type? Did you ever think of calling getType() on the object and comparing it to an existing typemask...?"
"....i r stupid...."
*EDIT*
#5
So, I now have a list to iterate through (thanks for that), and a function to call for checking.
That leaves two steps:
1. Passing in the type to check against
2. Returning a list containing the names of all datablocks of said type.
Passing in the type as a parameter shouldn't be too hard, but determining the type of list to return could be a pain.
I probably only need the name associated with the datablock object, so returning a char* containing space separated strings (names) would allow the creation of a script function to parse out each name and store it in the list.
*EDIT 1*- Which now brings me to string manipulation. Without the handy STL string class which I've become way too dependent on, progression has come to a screeching halt until I figure out a way to put all of the names into a string for returning. So close...more to come...
*EDIT 2*- dStrcat seems like the function to use in this scenario. So now I'll have a master string that I will add to using dStrcat. At the end of the function I'll return the master string and parse that in script...phew...
06/11/2007 (11:13 am)
Alright, so step 1 has been accomplished:So, I now have a list to iterate through (thanks for that), and a function to call for checking.
That leaves two steps:
1. Passing in the type to check against
2. Returning a list containing the names of all datablocks of said type.
Passing in the type as a parameter shouldn't be too hard, but determining the type of list to return could be a pain.
I probably only need the name associated with the datablock object, so returning a char* containing space separated strings (names) would allow the creation of a script function to parse out each name and store it in the list.
*EDIT 1*- Which now brings me to string manipulation. Without the handy STL string class which I've become way too dependent on, progression has come to a screeching halt until I figure out a way to put all of the names into a string for returning. So close...more to come...
*EDIT 2*- dStrcat seems like the function to use in this scenario. So now I'll have a master string that I will add to using dStrcat. At the end of the function I'll return the master string and parse that in script...phew...
#6
So, I have no compile errors, crashes, or warnings. However, the code I've put in bold shows the bug I'm dealing with. No matter what I pass in, I never make it through the last logic check. I know I've had this problem before.
I've tried passing in all of the following, which do exist in the mission:
$TypeMasks::InteriorObjectType
$TypeMasks::TerrainObjectType
$TypeMasks::ShapeBaseObjectType
$TypeMasks::PlayerObjectType
$TypeMasks::CorpseObjectType
Any clues?
*EDIT*- Bit manipulation and logic error strikes again! The == needed to change to & for the check to work properly, since the mTypeMask of some objects are not just the descriptive type, but a combination of multiple masks =).
The new bold section shows a crash that occurs for dStrcat(), so...onward I go!
If you see any glaring errors for string manipulation, or know why this might be crashing, please let me know
06/11/2007 (12:38 pm)
Right-O! I'm pretty sure I have the functionality and string manipulation nailed, which has left me with debugging:ConsoleFunction(getSpecificDatablocks, const char*, 2, 2, "getSpecificDatablocks(typeMask); get all datablocks of \"typeMask\" in string list format")
{
char* retBuffer = Con::getReturnBuffer(1024);
retBuffer = "";
for (SimSetIterator itr(Sim::getRootGroup()); *itr; ++itr)
{
// Get our object
SimObject* obj = static_cast<SimObject*>(*itr);
// Get the object name
const char* name = obj->getName();
if(!name)
continue; // No sense crashing the compiler or adding nameless datablocks
// What datablock type are we looking for
U32 searchType = dAtoi(argv[1]);
// What is the object's type
U32 objType = obj->getType();
// Compare types
if(searchType & objType)
{
[b]dStrcat(retBuffer, name);[/b]
Con::printf("Object of set found");
}
}
return retBuffer;
}So, I have no compile errors, crashes, or warnings. However, the code I've put in bold shows the bug I'm dealing with. No matter what I pass in, I never make it through the last logic check. I know I've had this problem before.
I've tried passing in all of the following, which do exist in the mission:
$TypeMasks::InteriorObjectType
$TypeMasks::TerrainObjectType
$TypeMasks::ShapeBaseObjectType
$TypeMasks::PlayerObjectType
$TypeMasks::CorpseObjectType
Any clues?
*EDIT*- Bit manipulation and logic error strikes again! The == needed to change to & for the check to work properly, since the mTypeMask of some objects are not just the descriptive type, but a combination of multiple masks =).
The new bold section shows a crash that occurs for dStrcat(), so...onward I go!
If you see any glaring errors for string manipulation, or know why this might be crashing, please let me know
#7
No more crashes, just one bug. Here's the new code:
I'm too used to Torquescript. I was setting a pointer (retBuffer) to a bad value (""), instead of performing a dStrcpy.
So, I am now grabbing objects of a specific type from all loaded objects in a game! Hurray!
The last bug I can find is that objects are being added twice, which I assume is due to ghosting. So, I'm going to fix this quirk then move on to script functionality...getting close now...
06/11/2007 (1:11 pm)
I think I figure out problems faster if I just keep making mistakes, talking them out on the thread, and then posting results =). No more crashes, just one bug. Here's the new code:
ConsoleFunction(getSpecificDatablocks, const char*, 2, 2, "getSpecificDatablocks(typeMask); get all datablocks of \"typeMask\" in string list format")
{
char* retBuffer = Con::getReturnBuffer(1024);
dStrcpy(retBuffer, "");
for (SimSetIterator itr(Sim::getRootGroup()); *itr; ++itr)
{
SimObject* obj = static_cast<SimObject*>(*itr);
const char* name = obj->getName();
if(name)
{
if(!dStrcmp(name, "Terrain"))
int breakHere = 1;
}
else
continue;
U32 searchType = dAtoi(argv[1]);
U32 objType = obj->getType();
if(searchType & objType)
{
dStrcat(retBuffer, name);
Con::printf("Object of set found");
}
}
return retBuffer;
}I'm too used to Torquescript. I was setting a pointer (retBuffer) to a bad value (""), instead of performing a dStrcpy.
So, I am now grabbing objects of a specific type from all loaded objects in a game! Hurray!
The last bug I can find is that objects are being added twice, which I assume is due to ghosting. So, I'm going to fix this quirk then move on to script functionality...getting close now...
#8
Works beautifully, not to mention it is a lot cleaner and requires less iteration. I'll post the script implementation tomorrow.
06/11/2007 (8:40 pm)
So, I found a much better solution for iterating through datablocks and getting all the info I need:ConsoleFunction(getSpecificDatablocks, const char*, 2, 2, "getSpecificDatablocks(typeMask); get all datablocks of \"typeMask\" in string list format")
{
// Create a return buffer (string format)
char* retBuffer = Con::getReturnBuffer(1024);
// Clear out existing data in the return buffer
dStrcpy(retBuffer, "");
// Get the typemaks we are searching for
const char* searchType = argv[1];
// Get all of our datablocks into one variable, a datablock group
SimDataBlockGroup * grp = Sim::getDataBlockGroup();
// Traverse the datablock group
for(SimDataBlockGroup::iterator i = grp->begin(); i != grp->end(); i++)
{
// Grab our current iterator's datablock
SimDataBlock * datablock = dynamic_cast<SimDataBlock*>(*i);
// Skip non-datablocks if we somehow encounter them.
if(!datablock)
continue;
// Get datablock name for matching
const char* name = datablock->getNamespace()->mName;
// Safety check, just in case
if(!name)
continue;
// Match datablock types
if(!dStrcmp(searchType, name))
{
dStrcat(retBuffer, datablock->getName());
dStrcat(retBuffer, " ");
}
}
return retBuffer;
}Works beautifully, not to mention it is a lot cleaner and requires less iteration. I'll post the script implementation tomorrow.
#9
I thought the functionality was working beautifully, except I was only checking AudioDescription, AudioProfile, and ParticleData. When I tested PlayerData, ParticleEmitterData, and SplashData, the search failed.
What I forgot (very stupid of me), is that there is a namespace hierarchy. As an example, AudioDescription derives from SimDatablock. So, AudioDescription->NameSpace->name will always be found in the datablock variable I'm using, since it is an immediate child of the datablock base class.
After debugging with the "PlayerData" parameter, I was able to find the actual string containing "PlayerData" was located in datablock->namespace->parent->parent->name.
The end result was creating a helper function that would traverse the "family tree" of a datablock looking for our search parameter (such as "PlayerData"). If I found the value I was looking for, I continued on with the rest of the functionality. If the search term was not located in the "family tree", I returned 0 (null string), forcing the SimDataBlockGroup::iterator to skip to the next iteration of the loop.
With that in place, the function was finally working EXACTLY how it was intended. When I began testing for datablocks that have been instantiated many times, I tried using more memory than I allocated for the returnBuffer. The last step was to create a more dynamic method of storing the strings. I ended up using Vector, and keeping track of the memory I was going to allocate with each successful iteration. Once the datablock looping was complete, I allocated the memory for the returnBuffer and dumped the contents of the Vector in. Yay completion!
Script implementation:
PHEW! I love a good challenge! =)
*EDIT*- The final, working code is now up. Let me know if you find bugs
06/12/2007 (7:15 am)
After a good night's rest, I was able to find the rest of the bugs with the code. Here is the updated version, with an explanation of bug-fixing at the end:static const char* findNamespace(SimDataBlock * datablock, const char* searchValue)
{
// Grab the namespace
Namespace* pTemp = datablock->getNamespace();
// Traverse the family tree looking for our
// long lost parent
while(pTemp->mParent)
{
// Compare our names...are you my Mommy?
if(!dStrcmp(searchValue, pTemp->mName))
{
// We have found the parent we are looking for, get their name
return pTemp->mName;
}
// This isn't the parent you are looking for, go higher in the tree
pTemp = pTemp->mParent;
}
// We didn't find the parent, return 0 (poor, orphaned datablock (= )
return 0;
}
ConsoleFunction(getSpecificDatablocks, const char*, 2, 2, "getSpecificDatablocks(typeMask); get all datablocks of \"typeMask\" in string list format")
{
U32 iBufferSize = 0;
Vector<const char*> myVec;
// Get the typemaks we are searching for
const char* searchType = argv[1];
// Get all of our datablocks into one variable, a datablock group
SimDataBlockGroup * grp = Sim::getDataBlockGroup();
// Traverse the datablock group
for(SimDataBlockGroup::iterator i = grp->begin(); i != grp->end(); i++)
{
// Grab our current iterator's datablock
SimDataBlock * datablock = dynamic_cast<SimDataBlock*>(*i);
// Skip non-datablocks if we somehow encounter them.
if(!datablock)
continue;
// Get datablock name for matching
const char* name = findNamespace(datablock, searchType);
// Safety check, just in case
if(!name)
continue;
// Match datablock types
if(!dStrcmp(searchType, name))
{
// Keep track of our overall memory we are going
// to allocate for our return buffer
iBufferSize += dStrlen(datablock->getName())+ 1;
// Push the string back
myVec.push_back(datablock->getName());
}
}
// Create a return buffer (string format)
char* retBuffer = Con::getReturnBuffer(iBufferSize);
// Clear out the return buffer (paranoia check)
dStrcpy(retBuffer, "");
// Iterate our vector and fill up our returnBuffer
for(int i = 0; i < myVec.size(); i++)
{
dStrcat(retBuffer, myVec[i]);
dStrcat(retBuffer," ");
}
// Return
return retBuffer;
}I thought the functionality was working beautifully, except I was only checking AudioDescription, AudioProfile, and ParticleData. When I tested PlayerData, ParticleEmitterData, and SplashData, the search failed.
What I forgot (very stupid of me), is that there is a namespace hierarchy. As an example, AudioDescription derives from SimDatablock. So, AudioDescription->NameSpace->name will always be found in the datablock variable I'm using, since it is an immediate child of the datablock base class.
After debugging with the "PlayerData" parameter, I was able to find the actual string containing "PlayerData" was located in datablock->namespace->parent->parent->name.
The end result was creating a helper function that would traverse the "family tree" of a datablock looking for our search parameter (such as "PlayerData"). If I found the value I was looking for, I continued on with the rest of the functionality. If the search term was not located in the "family tree", I returned 0 (null string), forcing the SimDataBlockGroup::iterator to skip to the next iteration of the loop.
With that in place, the function was finally working EXACTLY how it was intended. When I began testing for datablocks that have been instantiated many times, I tried using more memory than I allocated for the returnBuffer. The last step was to create a more dynamic method of storing the strings. I ended up using Vector
Script implementation:
[b]// Try passing in "PlayerData", "AudioProfile", and others[/b]
function getDatablocks(%name)
{
[b] // Get the list of datablocks[/b]
%list = getSpecificDatablocks(%name);
[b] // How many did we find?[/b]
%count = getWordCount(%list);
echo("Number of " @ %name @ " datablocks: " @ %count);
[b] // Loop through the list, grab each datablock, and print it out to the console[/b]
for(%i = 0; %i < %count; %i++)
{
%name = getWord(%list, %i);
echo(%name);
}
}PHEW! I love a good challenge! =)
*EDIT*- The final, working code is now up. Let me know if you find bugs
#10
06/12/2007 (8:46 am)
LOL, sorry I wasn't as helpful as I'd hoped :) Congratulations!
#11
I've submitted the resource, which is now pending: List Datablocks by Type
06/12/2007 (9:02 am)
Any help provided was appreciated Aaron =).I've submitted the resource, which is now pending: List Datablocks by Type
Torque Owner Aaron Moore