Game Development Community

Loading enums in a datablock...

by Daniel Buckmaster · in Torque Game Engine · 12/25/2007 (11:00 am) · 9 replies

I've set up a custom struct in a datablock that includes an enumerated value, similar to the way ShapeBaseImageData stores its state data (recoil states, etc., are enums). I've copied the way ShapeBaseImageData initialises these fields in the console, but I'm getting run-time errors when I load a level - in consoleTypes.cc, at the line
AssertFatal(tbl, "Null enum table passed to getDataTypeEnum()");
tbl represents an EnumTable object, and the problem is that it's sort of not there. The first reference I can find to the EnumTable object is as a part of an AbstractClassRep::Field, the 'table' member. I have no idea why it's not there, but it's got something to do with the datablock field that represents the enum in my struct. Does anyone have experience in dealing with this?

EDIT: Huh. My 4 latest posts each have 5 other posts in between them :P. What a random thing to notice...

About the author

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


#1
12/26/2007 (4:10 am)
I eventually decided to just sidestep the problem. Since values in an enum are simply a fancy way of storing an integet, I made the datablock field an integer rather than an enum. Then when I copied the datablock fields into the structs, I converted the integer into an enum value, so I can use it in code.
This gave me a new problem - I'm getting an 'invalid packet from server' error when I start a mission. This problem doesn't seem to be connected to my custom clas, though, so it's probably just a script issue.
Next up is changing the integer datablock value into a string, so people can type in strings corresponding to the enum value names, instead of having to put in a number. I'll just use a block of if-elses to convert from a string to an enum value.
#2
12/26/2007 (4:32 am)
I'd prefer the enum method as it is exactly what you want, only built in.

Did you provide an enum table?
excerpt:
static EnumTable::Enums enumRecoilStates[] =
{
   { ShapeBaseImageData::StateData::NoRecoil,     "NoRecoil" },
   { ShapeBaseImageData::StateData::LightRecoil,  "LightRecoil" },
   { ShapeBaseImageData::StateData::MediumRecoil, "MediumRecoil" },
   { ShapeBaseImageData::StateData::HeavyRecoil,  "HeavyRecoil" },
};
static EnumTable EnumRecoilState(4, &enumRecoilStates[0]);
#3
12/26/2007 (5:37 am)
Hmm. That's a big resounding no. That would seem to solve the problem - thanks!
Is it just that I ask questions only you among the community know how to solve, or...? ;)

EDIT: nope. I get the exact same error as before. I'm having a look at debug information...
Okay, I have no idea what's going on. Do I have to do something special with the enum table?

EDIT: Think I found the problem - addField...

Okay, I got rid of this problem by using addField properly. Now I've gone back to the previous error that I ran into after making sure that this worked. Stupid invalid packets :[
#4
12/26/2007 (6:57 pm)
Post copies of your packData/unpackData methods for your datablock. Usually an invalid packet error is caused by these methods being out of sync.
#5
12/27/2007 (1:45 am)
I thought that must have been it, because the error only shows up when I include my new class in the build. I can't spot anything, though...

For reference, here's the struct that the data is stored in:
struct WatchedField {
		const char* name;
		F32 minVal;
		F32 maxVal;
		F32 decayVal;
		enum WarnType {
			OnMin, OnMax,
			OnIncTo, OnDecTo,
			OnChange,
			NoWarn,
			NumWarnBits = 3,
		} warnType;
		F32 warnVals [4];
	} watchedFields [NumWatchedFields];

void RangedWeaponData::packData(BitStream* stream)
{
	Parent::packData(stream);

	if(stream->writeFlag(mProjectile))
		stream->writeRangedU32(packed? SimObjectId(mProjectile):
		mProjectile->getId(),DataBlockObjectIdFirst,DataBlockObjectIdLast);

	U32 i, j;
	for(i = 0; i < NumWatchedFields; i++)
	{
		if(stream->writeFlag(watchedFields[i].name && watchedFields[i].name[0]))
		{
			WatchedField& f = watchedFields[i];

			stream->writeString(f.name);
			stream->write(f.minVal);
			stream->write(f.maxVal);
			stream->write(f.decayVal);
			stream->writeInt(f.warnType,WatchedField::NumWarnBits);
			for(j = 0; j < 4; j++)
				stream->write(f.warnVals[j]);
		}
	}
}

void RangedWeaponData::unpackData(BitStream* stream)
{
	Parent::unpackData(stream);

	mProjectile = (stream->readFlag() ?
		(ProjectileData*)stream->readRangedU32(DataBlockObjectIdFirst,
		DataBlockObjectIdLast) : 0);

	U32 i, j;
	for(i = 0; i < NumWatchedFields; i++)
	{
		if(stream->readFlag())
		{
			WatchedField& f = watchedFields[i];

			f.name = stream->readSTString();
			stream->read(&f.minVal);
			stream->read(&f.maxVal);
			stream->read(&f.decayVal);
			f.warnType = (WatchedField::WarnType)stream->readInt(WatchedField::NumWarnBits);
			for(j = 0; j < 4; j++)
				stream->read(&f.warnVals[j]);
		}
	}
	watchedFieldsLoaded = true;
}
#6
12/27/2007 (1:07 pm)
Well that seems okay to me. Does your object have pack/unpack methods?

I'd put break points in your pack/unpack methods and make sure they seem to be writing/reading properly. Use your debugger to check values (make sure what is written is what is read).
#7
12/28/2007 (3:16 am)
That's an idea - breakpointing the methods themselves. I wasn't sure wht could be causing the invalid packet thing (which dumps me from my level with a dialog), so I just searched for 'invalid packet' in the engine code and breakpointed all those to see which one was being called. None of them, apparently...

Here's the object pack/unpack, just in case:
(First, the struct objects use, which is different to the datablock struct:)
struct WatchedField {
		bool active;
		F32 val;
		F32 prevVal;
	} watchedFields[RangedWeaponData::NumWatchedFields];
U32 RangedWeapon::packUpdate(NetConnection *connection, U32 mask, BitStream *stream)
{
	U32 retMask = Parent::packUpdate(connection,mask,stream);

	stream->writeFlag(mPrimaryTriggerDown);
	stream->writeFlag(mSecondaryTriggerDown);

	stream->writeInt(mPrimaryTriggerOnTime,RangedWeapon::TriggerTimeBitSize);
	stream->writeInt(mPrimaryTriggerOffTime,RangedWeapon::TriggerTimeBitSize);
	stream->writeInt(mSecondaryTriggerOnTime,RangedWeapon::TriggerTimeBitSize);
	stream->writeInt(mSecondaryTriggerOffTime,RangedWeapon::TriggerTimeBitSize);

	if(mask & WatchedFieldUpdateMask)
	{
		for(U32 i = 0; i < RangedWeaponData::NumWatchedFields; i++)
			if(stream->writeFlag(watchedFields[i].active))
			{
				stream->write(watchedFields[i].val);
				stream->write(watchedFields[i].prevVal);
			}
	}

	return retMask;
}

void RangedWeapon::unpackUpdate(NetConnection *connection, BitStream *stream)
{
	Parent::unpackUpdate(connection,stream);

	mPrimaryTriggerDown = stream->readFlag();
	mSecondaryTriggerDown = stream->readFlag();

	mPrimaryTriggerOnTime = stream->readInt(RangedWeapon::TriggerTimeBitSize);
	mPrimaryTriggerOffTime = stream->readInt(RangedWeapon::TriggerTimeBitSize);
	mSecondaryTriggerOnTime = stream->readInt(RangedWeapon::TriggerTimeBitSize);
	mSecondaryTriggerOffTime = stream->readInt(RangedWeapon::TriggerTimeBitSize);

	for(U32 i = 0; i < RangedWeaponData::NumWatchedFields; i++)
	{
		watchedFields[i].active = stream->readFlag();
		if(watchedFields[i].active)
		{
			stream->read(&watchedFields[i].val);
			stream->read(&watchedFields[i].prevVal);
		}
	}
}

EDIT
From breakpoints on pack/unpack datablock methods, it seems that while an object for mProjectile is being packed, when it gets unpacked, it goes all screwy. Could that be a cause for an 'invalid packet'?

EDIT
This is really damn frustrating. The invalid packet thing only actually happens about half the time - sometimes my levels load fine, other times they get invalid packeted about a second after spawning. Makes it a bugger to debug properly. I'd still like to just breakpoint the piece of code that tells the scripts when there's an invalid packet.
And a nice addition to the engine would be to say which packet was invalid :P
#8
12/28/2007 (10:18 am)
Well, from what I can tell from just the code, you seem to have the possibility of reading data that wasn't ever sent.

U32 RangedWeapon::packUpdate(NetConnection *connection, U32 mask, BitStream *stream)
{
	U32 retMask = Parent::packUpdate(connection,mask,stream);

	stream->writeFlag(mPrimaryTriggerDown);
	stream->writeFlag(mSecondaryTriggerDown);

	stream->writeInt(mPrimaryTriggerOnTime,RangedWeapon::TriggerTimeBitSize);
	stream->writeInt(mPrimaryTriggerOffTime,RangedWeapon::TriggerTimeBitSize);
	stream->writeInt(mSecondaryTriggerOnTime,RangedWeapon::TriggerTimeBitSize);
	stream->writeInt(mSecondaryTriggerOffTime,RangedWeapon::TriggerTimeBitSize);

        [b]// Check if we should sent watched field updates[/b]
	if(mask & WatchedFieldUpdateMask)
	{
		for(U32 i = 0; i < RangedWeaponData::NumWatchedFields; i++)
			if(stream->writeFlag(watchedFields[i].active))
			{
				stream->write(watchedFields[i].val);
				stream->write(watchedFields[i].prevVal);
			}
	}

	return retMask;
}

void RangedWeapon::unpackUpdate(NetConnection *connection, BitStream *stream)
{
	Parent::unpackUpdate(connection,stream);

	mPrimaryTriggerDown = stream->readFlag();
	mSecondaryTriggerDown = stream->readFlag();

	mPrimaryTriggerOnTime = stream->readInt(RangedWeapon::TriggerTimeBitSize);
	mPrimaryTriggerOffTime = stream->readInt(RangedWeapon::TriggerTimeBitSize);
	mSecondaryTriggerOnTime = stream->readInt(RangedWeapon::TriggerTimeBitSize);
	mSecondaryTriggerOffTime = stream->readInt(RangedWeapon::TriggerTimeBitSize);

        [b]// Read watched field updates whether we sent them or not[/b]
	for(U32 i = 0; i < RangedWeaponData::NumWatchedFields; i++)
	{
		watchedFields[i].active = stream->readFlag();
		if(watchedFields[i].active)
		{
			stream->read(&watchedFields[i].val);
			stream->read(&watchedFields[i].prevVal);
		}
	}
}

Maybe that could be your problem...?
#9
12/28/2007 (12:50 pm)
Ooh. That's probably a bit of an issue :P. I'll fix that up and see what happens.

EDIT: Seems to be fixed! Thank you for your astute reading :) You are my official hero ;)