Game Development Community

List Datablocks by Type

by Michael Perry · 06/12/2007 (12:19 pm) · 12 comments

While developing two in house tools for Zombie Shortbus (AFX Creator and Weaponcrafter), I discovered I needed a list of datablocks specified by their type. For instance, I wanted to fill a GuiListCtrl with the names of all "ParticleEmitterData" datablocks loaded during game play.

With these functions, I have can now implement tool features such as picking an existing datablock out of list, displaying it's attributes in a new GUI, modifying the attributes, and saving out the new datablock (plus re-executing), without ever opening up a script file.

I'm sure there are other applications that you might discover. The first code section is the actual engine change you need to make. The second code section is a script example for using the new function, getSpecificDatablocks().

Enjoy, and let me know if you have any suggestions for improvement or find bugs.

Add the following to the bottom of engine\console\consoleFunctions.cc:
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;
}

Script implementation, written in server\scripts\game.cs:
[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);
   }
}

About the author

Associate Producer / Project Manager for GarageGames


#1
06/12/2007 (6:26 pm)
This could really help to improve the editor. In behaviors I'm making, I can't select certain datablocks that I really wish I could be selecting. I hope we can find some more uses for it. The less hand editing of script files the better! ;) I'm already thinking of ideas.. If you can select AudioProfiles maybe we can make some select Audio guis.

Edit: It doesn't seem to be finding the AudioProfile datablocks for me, but hopefully it's possible.
#2
06/13/2007 (10:49 am)
Hmm, worked for me Joe.

getDatablocks("AudioProfile"); echoed out all the AudioProfiles for me
#3
06/13/2007 (1:05 pm)
I put the code into tools/main.cs so that I could use this in the editor. What I'm trying for is inside a behavior, to fill an enum so I can select an AudioProfile for the object to use. I exec() my datablock scripts beforehand, but maybe it's not working because of some timing issue with behaviors (which seems to happen a lot).
#4
06/13/2007 (1:10 pm)
Hmmm. I can't really test or debug this since I don't own TGB Pro....

Are other datablocks showing up for you? If so, I think I can walk you through the debug steps to find out why certain ones aren't showing up...
#5
06/15/2007 (6:17 am)
Sounds like a very useful resource.
We were planning on implementing something similar but to additionally dump a list of all the datablocks which not only exist but have also actually been used. The idea being to cull out DBs which aren't actually in use and get a slight speed-up in connection times.
#6
06/15/2007 (6:25 am)
That is exactly what we were hoping to get to as well, Orion =).

I know a big request of developers is the functionality of culling datablocks and objects to only what is needed, for the mission being loaded. I'm hoping this function helps my team, and others, get to that point.

Btw. I've found myself using this function for a lot of debugging lately. I've been writing some custom classes that have been failing to register, and the first thing I immediately check is if the datablocks are actually loaded in the game. Saved me a lot of time.
#7
06/15/2007 (6:40 am)
hah, nice!
#8
10/08/2007 (3:38 pm)
This fills a need for me and was something I was looking for, thanks. However I think it is useful to point out one thing. As designed I believe it only works on the Server for server defined datablocks. (Many of the usage examples above were server only editor applications.)

My current use is for a remote client to display a list of available object types to select from. This is adequate for the server side to obtain the list of datablocks of a particular type, but other techniques will need to be used for getting a display list describing those datablocks from the server to the client's GUI.
#9
10/09/2007 (6:18 am)
Ohhhh k... My response was eaten.

@Matthew - Interesting observation. I'm barely awake right now, but I feel this will work from a client. I'll give it a spin now.
#10
10/09/2007 (6:33 am)
Ok. So I just put the sample script function getDatablocks() in a client script. I did not host a server. I passed "playerData" into the function, and received the data I needed.
#11
10/09/2007 (8:14 am)
You're right, I was too restrictive. ;( I should have said something more like: it isn't intended to work on remote clients for datablocks defined on the server.
#12
10/09/2007 (9:08 am)
Hmmm....welllll...there was no intention other than grabbing all the loaded datablocks regardless if you are the client or server.

You mentioned in your first post that mods would have to be made to grab the list of datablocks from the server to the client's GUI. Have you attempted this yet?