Mount More Weapons or How to Get More Mask Bits
by Derk Adams · 11/17/2004 (1:22 am) · 20 comments
Download Code File
Background:
One of my requirements for my Mecha game was to be able to mount a lot of weapons on the character model. My initial inquiries were answered with 32 being the limit. That turned out to be true and false. True in that the maximum number of mount points for a model is 32. But the maximum number of mounted images turned out to be 8. It is also the first 8, so I couldn't spread those 8 images over the 32 mount points.
Discussion:
To enable more than 8 image mounts, it was necessary to remove the image updates from the general object mask. Once removed, a full 32 bit mask was used for tracking the image changes.
Acknowledgements:
The resource Skin Modifiers by David Michael was very useful in pointing me in the right direction. Also, it will be necessary to use Mount Image with Mount Point by Xavier Amado to use the mount points that this resource adds.
Key:
I use the same indication of modifications as a patch file. A line beginning with a "-" is to be removed and a line beginning with a "+" is to be added. Be sure to remove the "+" when you actually put the line into your code. A few lines around the changes are shown to give context to the changes. The triple dot "..." is showing that information has been removed for brevity purposes.
Development Environment:
November 2004 - July 2005
Head 1.2.2 - 1.3
Win32
Single Player - Multi Player
Implementation:
The number of bits necessary for the mask depend on the "power of 2" that you are using. In the default state 8 images takes up 3 bits. This is changed to 5 bits for 32 images. That means, if you only want 4 images, you can drop it to 2 bits to conserve the amount of data being transfered over the network.
EngineFile: /game/shapeBase.h
Search for:
Background:
One of my requirements for my Mecha game was to be able to mount a lot of weapons on the character model. My initial inquiries were answered with 32 being the limit. That turned out to be true and false. True in that the maximum number of mount points for a model is 32. But the maximum number of mounted images turned out to be 8. It is also the first 8, so I couldn't spread those 8 images over the 32 mount points.
Discussion:
To enable more than 8 image mounts, it was necessary to remove the image updates from the general object mask. Once removed, a full 32 bit mask was used for tracking the image changes.
Acknowledgements:
The resource Skin Modifiers by David Michael was very useful in pointing me in the right direction. Also, it will be necessary to use Mount Image with Mount Point by Xavier Amado to use the mount points that this resource adds.
Key:
I use the same indication of modifications as a patch file. A line beginning with a "-" is to be removed and a line beginning with a "+" is to be added. Be sure to remove the "+" when you actually put the line into your code. A few lines around the changes are shown to give context to the changes. The triple dot "..." is showing that information has been removed for brevity purposes.
Development Environment:
November 2004 - July 2005
Head 1.2.2 - 1.3
Win32
Single Player - Multi Player
Implementation:
The number of bits necessary for the mask depend on the "power of 2" that you are using. In the default state 8 images takes up 3 bits. This is changed to 5 bits for 32 images. That means, if you only want 4 images, you can drop it to 2 bits to conserve the amount of data being transfered over the network.
EngineFile: /game/shapeBase.h
...
// The thread and image limits should not be changed without
// also changing the ShapeBaseMasks enum values declared
// further down.
MaxSoundThreads = 4, ///< Should be a power of 2
MaxScriptThreads = 4, ///< Should be a power of 2
- MaxMountedImages = 8, ///< Should be a power of 2
+ MaxMountedImages = 32, ///< Should be a power of 2
...
MountedImage();
~MountedImage();
};
MountedImage mMountedImageList[MaxMountedImages];
+ U32 mImageMaskBits;
...
enum ShapeBaseMasks {
NameMask = Parent::NextFreeMask,
DamageMask = Parent::NextFreeMask << 1,
NoWarpMask = Parent::NextFreeMask << 2,
MountedMask = Parent::NextFreeMask << 3,
CloakMask = Parent::NextFreeMask << 4,
ShieldMask = Parent::NextFreeMask << 5,
InvincibleMask = Parent::NextFreeMask << 6,
SkinMask = Parent::NextFreeMask << 7,
+ ImageMask = Parent::NextFreeMask << 8,
- SoundMaskN = Parent::NextFreeMask << 8, ///< Extends + MaxSoundThreads bits
+ SoundMaskN = Parent::NextFreeMask << 9, ///< Extends + MaxSoundThreads bits
ThreadMaskN = SoundMaskN << MaxSoundThreads, ///< Extends + MaxScriptThreads bits
- ImageMaskN = ThreadMaskN << MaxScriptThreads, ///< Extends + MaxMountedImage bits
- NextFreeMask = ImageMaskN << MaxMountedImages
+ NextFreeMask = ThreadMaskN << MaxScriptThreads
};
enum BaseMaskConstants {
SoundMask = (SoundMaskN << MaxSoundThreads) - SoundMaskN,
ThreadMask = (ThreadMaskN << MaxScriptThreads) - ThreadMaskN,
- ImageMask = (ImageMaskN << MaxMountedImages) - ImageMaskN
};
...EngineFile: /game/shapeBase.cc...
mLightTime = 0;
damageDir.set(0, 0, 1);
+ mImageMaskBits = 0;
}
...
U32 ShapeBase::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
{
...
// mask off images that aren't updated
for(i = 0; i < MaxMountedImages; i++)
if(!mMountedImageList[i].dataBlock)
- mask &= ~(ImageMaskN << i);
+ mImageMaskBits &= ~(1 << i);
...
if (stream->writeFlag(mask & ImageMask)) {
for (int i = 0; i < MaxMountedImages; i++) {
- if (stream->writeFlag(mask & (ImageMaskN << i))) {
+ if (stream->writeFlag(mImageMaskBits & (1 << i))) {
MountedImage& image = mMountedImageList[i];
if (stream->writeFlag(image.dataBlock))
stream->writeInt(image.dataBlock->getId() - DataBlockObjectIdFirst, DataBlockObjectIdBitSize);
con->packStringHandleU(stream, image.skinNameHandle);
stream->writeFlag(image.wet);
stream->writeFlag(image.ammo);
stream->writeFlag(image.loaded);
stream->writeFlag(image.target);
stream->writeFlag(image.triggerDown);
- stream->writeInt(image.fireCount,3);
+ stream->writeInt(image.fireCount,5);
if (mask & InitialUpdateMask)
stream->writeFlag(isImageFiring(i));
}
}
}
...
}
...
void ShapeBase::unpackUpdate(NetConnection *con, BitStream *stream)
{
...
StringHandle skinDesiredNameHandle = con->unpackStringHandleU(stream);
image.wet = stream->readFlag();
image.ammo = stream->readFlag();
image.loaded = stream->readFlag();
image.target = stream->readFlag();
image.triggerDown = stream->readFlag();
- int count = stream->readInt(3);
+ int count = stream->readInt(5);
...EngineFile: /game/shapeImage.ccSearch for:
setMaskBits(ImageMaskN << imageSlot);and replace with:
mImageMaskBits |= (1 << imageSlot);
setMaskBits(ImageMask);I have 9 instances of this.About the author
Recent Blogs
• Damage Texture with Transparency• Projectile Spread
• Plan for Derk Adams
• Continuous Laser
• Plan for Derk Adams
#2
I have used your resource and it is central to my game system. Thank you very much for making it available. However, it still was limited to the first 8 mount points. Anything above that just got put at the base point of the character.
Thanks.
11/17/2004 (9:54 am)
Xavier,I have used your resource and it is central to my game system. Thank you very much for making it available. However, it still was limited to the first 8 mount points. Anything above that just got put at the base point of the character.
Thanks.
#3
11/17/2004 (7:52 pm)
The forum post that is linked to this resource is Changing MaxMountedImages.
#4
11/21/2004 (6:58 pm)
Very nice job. It's as useful for freeing up 8 mask bits as for providing more mountable weapons.
#5
Fatal: (h:\torque\engine\sim\netobject.cc @ 43) Invalid net mask bits set.
Any clues?
12/12/2004 (6:01 pm)
After following this resource and triple checking it on a clean and current HEAD it fails on mission load with the following error:Fatal: (h:\torque\engine\sim\netobject.cc @ 43) Invalid net mask bits set.
Any clues?
#6
I haven't tested it on a current HEAD. I am using version 1.2.2. I don't understand why it is affecting netobject.
It will be a while before I update to a new HEAD and retest everything.
Anyone else have any suggestions?
EDIT: Double check the mask section to be sure you removed all the lines you needed to before adding the new ones. If you double up on the mask bits, it might give you the error you are getting.
12/12/2004 (6:11 pm)
Bryce,I haven't tested it on a current HEAD. I am using version 1.2.2. I don't understand why it is affecting netobject.
It will be a while before I update to a new HEAD and retest everything.
Anyone else have any suggestions?
EDIT: Double check the mask section to be sure you removed all the lines you needed to before adding the new ones. If you double up on the mask bits, it might give you the error you are getting.
#7
Dunno which time it is called that it fails, but I'm gonna sniff this out a little further tonight.
12/13/2004 (3:42 am)
setMaskBit is the function that throws the assert.Dunno which time it is called that it fails, but I'm gonna sniff this out a little further tonight.
#8
I was just wondering - do you have any ideas if there's any performance hit from this? The guys from GG seemed to suggest it could impact performance significantly
02/08/2005 (10:42 am)
ps, I made this change successfully - good show :)I was just wondering - do you have any ideas if there's any performance hit from this? The guys from GG seemed to suggest it could impact performance significantly
#9
Yes, it will have an impact, you are sending an additional 32 bits every tick, there by doubling the amount. I don't know exactly what impact it would have, but my focus is single player and lan play.
02/08/2005 (7:27 pm)
Andrew,Yes, it will have an impact, you are sending an additional 32 bits every tick, there by doubling the amount. I don't know exactly what impact it would have, but my focus is single player and lan play.
#10
Also, is there a specific reason why it should be a power of 2?
07/11/2005 (10:37 pm)
Interestingly, 1.3 has MaxMountedImages = 4, not 8 like in 1.2.2. I'm sure this was reduced for a good reason, but does anyone know why? I want 9 mounts, but I can work with 8. I've changed it to 8 and it seems to work okay, but I haven't checked all subclasses of ShapeBase to make sure they don't go over the 32 bit limit in the mask.Also, is there a specific reason why it should be a power of 2?
#11
They reduced it in 1.3 to give more bits to other functions. There are 32 bits that are used for each packet. There is nothing special about a power of 2, but developers think in powers of 2 because the computer does. The issue, as you mentioned is to not go over 32 for any mask. I chose to use the entire 32 for the mount point because I needed them. In such a case, I needed to add a whole new packet to be sent.
Update:
I have upgraded to 1.3 and multiplayer and it works. However, I forgot to comment that it is necessary to use Mount Image with Mount Point to be able to mount images to the mount points this resource creates.
Thanks
07/17/2005 (7:11 am)
David,They reduced it in 1.3 to give more bits to other functions. There are 32 bits that are used for each packet. There is nothing special about a power of 2, but developers think in powers of 2 because the computer does. The issue, as you mentioned is to not go over 32 for any mask. I chose to use the entire 32 for the mount point because I needed them. In such a case, I needed to add a whole new packet to be sent.
Update:
I have upgraded to 1.3 and multiplayer and it works. However, I forgot to comment that it is necessary to use Mount Image with Mount Point to be able to mount images to the mount points this resource creates.
Thanks
#12
07/17/2005 (4:57 pm)
Thanks Derk. yep, have had no trouble with it set to 8 this last week, all looks good. Yeah, I have that resource applied - we are doing this for bombs on a plane, and unmounting the image when the bomb drops. Looks sweet :)
#14
Does anyone have a working version of ShapeBase.h and ShapeBase.cc that used this that I caould use to do a merge on?
08/29/2006 (2:59 pm)
Derk, you said "Also, it will be necessary to use Mount Image with Mount Point by Xavier Amado to use the mount points that this resource adds". Does this mean that I need to add that code too? Also have you made this work in TGE 1.4?Does anyone have a working version of ShapeBase.h and ShapeBase.cc that used this that I caould use to do a merge on?
#15
Yes and yes. My resource only increases the number of supported mount points. However, to actually mount something to those mount points you will need to implement Amado's resource. I am currently using it in 1.4 and I cannot send you a shapebase file because I have made many more changes then just that resource.
Thanks.
08/29/2006 (3:07 pm)
Ronald,Yes and yes. My resource only increases the number of supported mount points. However, to actually mount something to those mount points you will need to implement Amado's resource. I am currently using it in 1.4 and I cannot send you a shapebase file because I have made many more changes then just that resource.
Thanks.
#17
Firstly, if you don't expect to use all 32 image slots that having a full 32 bit mask provides, you can set MaxMountedImages to any value you fancy, it no longer needs to be pow2, so if you know you will only need 12 slots, setting it to 12 will save a few bits whenever ImageMask is set as well as a little memory.
The above code in the InitialUpdateMask section doesn't look correct to me. AFAIK you want to force an update of all images that are valid for the initial update. As mImageMaskBits starts off at 0, masking out images that are not used won't have any effect.
Instead, shouldn't you be masking in valid datablocks?
The other thing I noticed is in the writing of each images data
As you're setting the mImageMaskBits with
In various places. Consider the scenario where mImageMaskBits is 0. So initially no mounted image data will be sent, instead we'd have writeFlag write false MaxMountedImage times. Now consider imageSlot 0 is updated and the appropriate mask bits or'd in. The next call to packUpdate would result in 1 writeFlag true and the rest false, I.E the transmitting of image 0's data and not the rest.
That's as you would expect. However, consider that we down the line make a change to slot 3. This gets or'd in and you would expect it's data to be sent, but, bit 0 is still set so in addition to slot 3's data you're also getting slot 0's data sent again.
From what I can see, once a change has been made to every slot, then any subsequent change to just a single slot would result in all active slots been resent rather than just those that have been updated.
The way this used to work is that 8 bits were used out of the "mask" and you didn't need to worry about clearing these bits since the net code already handled that during the collapseDirtyList call.
I'm not sure of the best way to resolve this. You could add an else to reset only when ImageMask bit is 0, which would make sure you're only clearing the image bits after collapseDirtyList has been called, but there might (I'm not sure) be a situation where ImageMask is set on several consecutive calls but for different slots, resulting in retransmission of unchanged slots and a bit of a waste of bandwidth.
I have a feeling there's a better way to deal resetting the mask though, so if anyone can shed any light let me know.
11/09/2007 (5:29 pm)
I know this is an old resource, but as it still seems to be getting plenty of use I thought I'd add a few comments. I've just done a similar change to this resource and noticed a few areas you may have overlooked or I've mis-understood in which case I'd appreciate someone correcting me :)Firstly, if you don't expect to use all 32 image slots that having a full 32 bit mask provides, you can set MaxMountedImages to any value you fancy, it no longer needs to be pow2, so if you know you will only need 12 slots, setting it to 12 will save a few bits whenever ImageMask is set as well as a little memory.
// mask off images that aren't updated
for(i = 0; i < MaxMountedImages; i++)
if(!mMountedImageList[i].dataBlock)
mImageMaskBits &= ~(1 << i);The above code in the InitialUpdateMask section doesn't look correct to me. AFAIK you want to force an update of all images that are valid for the initial update. As mImageMaskBits starts off at 0, masking out images that are not used won't have any effect.
Instead, shouldn't you be masking in valid datablocks?
mImageMaskBits = 0;
for(i = 0; i < MaxMountedImages; i++)
if(mMountedImageList[i].dataBlock)
mImageMaskBits |= (1 << i);The other thing I noticed is in the writing of each images data
if (stream->writeFlag(mask & ImageMask)) {
for (int i = 0; i < MaxMountedImages; i++)
if (stream->writeFlag(mImageMaskBits & (1 << i)))
{
MountedImage& image = mMountedImageList[i];As you're setting the mImageMaskBits with
mImageMaskBits |= (1 << imageSlot);
setMaskBits(ImageMask);In various places. Consider the scenario where mImageMaskBits is 0. So initially no mounted image data will be sent, instead we'd have writeFlag write false MaxMountedImage times. Now consider imageSlot 0 is updated and the appropriate mask bits or'd in. The next call to packUpdate would result in 1 writeFlag true and the rest false, I.E the transmitting of image 0's data and not the rest.
That's as you would expect. However, consider that we down the line make a change to slot 3. This gets or'd in and you would expect it's data to be sent, but, bit 0 is still set so in addition to slot 3's data you're also getting slot 0's data sent again.
From what I can see, once a change has been made to every slot, then any subsequent change to just a single slot would result in all active slots been resent rather than just those that have been updated.
The way this used to work is that 8 bits were used out of the "mask" and you didn't need to worry about clearing these bits since the net code already handled that during the collapseDirtyList call.
I'm not sure of the best way to resolve this. You could add an else to reset only when ImageMask bit is 0, which would make sure you're only clearing the image bits after collapseDirtyList has been called, but there might (I'm not sure) be a situation where ImageMask is set on several consecutive calls but for different slots, resulting in retransmission of unchanged slots and a bit of a waste of bandwidth.
I have a feeling there's a better way to deal resetting the mask though, so if anyone can shed any light let me know.
#18
04/18/2008 (7:44 am)
To get weapons mounted in slots > #3 to fire, I believe you also need to make this change in ShapeBase::setImageState() in shapeImage.cc, from:image.fireCount = (image.fireCount + 1) & 0x7;to:
image.fireCount = (image.fireCount + 1) & 0x63;
#19
Seems to work well in my project(Ruin Online).
04/18/2008 (4:42 pm)
I modified some code and use 32 triggers along with this resource to fire all the slots.Seems to work well in my project(Ruin Online).
#20
03/02/2010 (1:19 pm)
I know old post but looking at implementing now. Did you guys have any bandwidth issues in Ruin Online implementing 32? Is it sending packets for only for items mounted? 
Torque 3D Owner Xavier "eXoDuS" Amado
Default Studio Name