C++ Value Transfer
by John Eckhardt · in Technical Issues · 02/04/2008 (12:44 pm) · 18 replies
I have a very strange C++ error. I have implemented this resource: http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=4943 (actually made up my own code and then found out that was exactly the same)
The only change that I made was to use a bit more efficient way of packing up the team id. It's in S32 instead of F32. Maybe that could be improved more.
Here's my problem. I correctly assign teams to my bots (and it remembers them correctly as tested with console methods). The two of them attack eachother because they are on opposite teams and know it. One is on my team, so one should appear with Red text while the other should have Green text for their name.
It does not happen this way. I tracked down the part where it fails.
in guiShapeNameHud::onRender:
Both bots' theirTeam is 1. No matter if their team is 1 or 0 or 2, obj->getTeam() returns 1.
I thought this might be some strange type return, but when I changed the team of the bot to 13, it was on the opposite team of me. Good, I thought, until I discovered that obj->getTeam() returned 5, not 13 And again, it returned the proper 13 in console/script.
Help!
The only change that I made was to use a bit more efficient way of packing up the team id. It's in S32 instead of F32. Maybe that could be improved more.
Here's my problem. I correctly assign teams to my bots (and it remembers them correctly as tested with console methods). The two of them attack eachother because they are on opposite teams and know it. One is on my team, so one should appear with Red text while the other should have Green text for their name.
It does not happen this way. I tracked down the part where it fails.
in guiShapeNameHud::onRender:
S32 ourTeam = control->getTeam(); S32 theirTeam = obj->getTeam(); bool isEnemy = ourTeam != theirTeam;
Both bots' theirTeam is 1. No matter if their team is 1 or 0 or 2, obj->getTeam() returns 1.
I thought this might be some strange type return, but when I changed the team of the bot to 13, it was on the opposite team of me. Good, I thought, until I discovered that obj->getTeam() returned 5, not 13 And again, it returned the proper 13 in console/script.
Help!
#2
packUpdate:
unpackUpdate:
That should be enough bits for 0, 1, 2 and 3 positive. I tried using an U32, but the same problem occured and the consoleMethod did not want to return an U32, but it was fine with an S32.
Then as I ran into the problem that this thread's all about, I changed all related variables and functions in shapeBase.cc/.h over to a S32 with no visible improvement.
02/04/2008 (1:56 pm)
I thought it might be something like that, but then why would %id.getTeam(); return 13 instead of 5 in script?packUpdate:
...
if(!stream->writeFlag(mask & (NameMask | DamageMask | SoundMask |
ThreadMask | ImageMask | CloakMask | MountedMask | InvincibleMask |
ShieldMask | SkinMask | ServerIdMask | ExpMask | TeamMask)))
return retMask;
if (stream->writeFlag((mask & TeamMask) && !(mask & InitialUpdateMask))) {
stream->writeInt(mTeam, 3);
}
if (stream->writeFlag(mask & ExpMask)) {
stream->writeInt(mExpLevel,4);
stream->writeInt(mExperience, 8);
stream->writeFloat(mClampF(mExpPct, 0.f, 1.f), DamageLevelBits);
}
if (stream->writeFlag(mask & DamageMask)) {
stream->writeFloat(mClampF(mDamage / mDataBlock->maxDamage, 0.f, 1.f), DamageLevelBits);
stream->writeInt(mDamageState,NumDamageStateBits);
stream->writeNormalVector( damageDir, 8 );
}
...unpackUpdate:
void ShapeBase::unpackUpdate(NetConnection *con, BitStream *stream)
{
Parent::unpackUpdate(con, stream);
mLastRenderFrame = sLastRenderFrame; // make sure we get a process after the event...
if(!stream->readFlag())
return;
if (stream->readFlag()) {
mTeam = stream->readInt(3);
}
if (stream->readFlag()) {
mExpLevel = stream->readInt(4);
mExperience = stream->readInt(8);
mExpPct = stream->readFloat(DamageLevelBits);
}
if (stream->readFlag()) {
mDamage = mClampF(stream->readFloat(DamageLevelBits) * mDataBlock->maxDamage, 0.f, mDataBlock->maxDamage);
DamageState prevState = mDamageState;
mDamageState = DamageState(stream->readInt(NumDamageStateBits));
stream->readNormalVector( &damageDir, 8 );
if (prevState != Destroyed && mDamageState == Destroyed && isProperlyAdded())
blowUp();
updateDamageLevel();
updateDamageState();
}
...That should be enough bits for 0, 1, 2 and 3 positive. I tried using an U32, but the same problem occured and the consoleMethod did not want to return an U32, but it was fine with an S32.
Then as I ran into the problem that this thread's all about, I changed all related variables and functions in shapeBase.cc/.h over to a S32 with no visible improvement.
#3
which including one bit for the sign, is really only two bits, so i'm surprised you're seeing a value of 5.
i would expect the only possible values to be 0, 1, 2, 3, -1, -2.
try increasing the number of bits from 3 to 5 or so. it's not like people are going to be changing teams often, so it's okay to use a couple extra bits.
i don't know why script saw 13; perhaps you were calling the script method on the server-side copy of the object ?
02/04/2008 (2:04 pm)
Yeah, so writeInt(mTeam, 3) is going to use 3 bits to write mTeam,which including one bit for the sign, is really only two bits, so i'm surprised you're seeing a value of 5.
i would expect the only possible values to be 0, 1, 2, 3, -1, -2.
try increasing the number of bits from 3 to 5 or so. it's not like people are going to be changing teams often, so it's okay to use a couple extra bits.
i don't know why script saw 13; perhaps you were calling the script method on the server-side copy of the object ?
#4
Then the enemy that wasn't my team correctly displayed his red name text. Yay sort of.
It would appear that the server side object not being properly mirrored to the client is the root of this problem.
So now, how would I go about making sure it sends the team value over to the client's object? I tried setMaskBits(TeamMask); in the ShapeBase::onAdd and ::onNewDatablock but that didn't work.
My script is as such:
Again, this does properly set the server side teams, but it doesn't mirror to the client. Therefore, I do not see the red text name on the enemy.
I made a test function to call from the console to set an object's team, and it worked, properly setting the team of the bot. Now the only thing I can see different between the spawn script function and this test function is that the test function wasn't called directly after the bot spawned.
02/04/2008 (3:58 pm)
Ok, I made it up to 5 bits. Here's what changed: Nothing until I manually applied the teams from the console (%obj.setTeam(0)).Then the enemy that wasn't my team correctly displayed his red name text. Yay sort of.
It would appear that the server side object not being properly mirrored to the client is the root of this problem.
So now, how would I go about making sure it sends the team value over to the client's object? I tried setMaskBits(TeamMask); in the ShapeBase::onAdd and ::onNewDatablock but that didn't work.
My script is as such:
%player = AIPlayer::spawn(%name,%pos, %type);
if (isObject(%player))
{
%player.setOrders("landmark");
%player.intellectLoop();
%data = %player.getDatablock();
%player.setInventory(HealthKit, 1);
%player.setInventory(%data.startWeapon, 1);
%player.setInventory(%data.startAmmoType, %data.startAmmoAmount);
%player.mountImage(%data.startWeaponImage, 0);
%player.incExperience(%xp);
%player.setTeam(%team);
return %player;
} else
return 0;Again, this does properly set the server side teams, but it doesn't mirror to the client. Therefore, I do not see the red text name on the enemy.
I made a test function to call from the console to set an object's team, and it worked, properly setting the team of the bot. Now the only thing I can see different between the spawn script function and this test function is that the test function wasn't called directly after the bot spawned.
#5
but make sure that the consoleMethod for SetTeam() sets the netmask too.
02/04/2008 (4:04 pm)
Well i'm guessing at the code here,but make sure that the consoleMethod for SetTeam() sets the netmask too.
#6
I had the console print out a warning every time a team ID was changed (in the pack update) and that was only ever printed when I manually called obj.setTeam(x). So the problem still seems to be that the client doesn't get the updated values when the .setTeam is called directly after the .spawn.
I was also running into this problem when I added in 'experience' and it wasn't getting properly mirrored right after the spawn.
02/05/2008 (1:09 pm)
Good question there, I went ahead and renamed the C++ function to setTeam2 so I could make sure I was calling the console method. Nothing changed. This should set the netmask because it calls the function.ConsoleMethod( ShapeBase, setTeam, void, 3, 3, "(int team)")
{
object->setTeam2(dAtoi(argv[2]));
}I had the console print out a warning every time a team ID was changed (in the pack update) and that was only ever printed when I manually called obj.setTeam(x). So the problem still seems to be that the client doesn't get the updated values when the .setTeam is called directly after the .spawn.
I was also running into this problem when I added in 'experience' and it wasn't getting properly mirrored right after the spawn.
#7
You haven't shown us the function ::setTeam2(..), but unless you specifically set the mask as being dirty, nothing does it for you.
02/05/2008 (2:44 pm)
Quote:
This should set the netmask because it calls the function.
You haven't shown us the function ::setTeam2(..), but unless you specifically set the mask as being dirty, nothing does it for you.
#8
I believe that sets the mask dirty.
02/05/2008 (2:54 pm)
void ShapeBase::setTeam2(S32 team) {
mTeam = team;
setMaskBits(TeamMask);
}I believe that sets the mask dirty.
#9
Why the additional check in this if statement? That is guaranteeing that if the team is set before the client actually connects (which is probably pretty standard, since AI's are spawned during mission startup, but before the client enters the game), the team will not be sent--it's the initial update to that client.
02/05/2008 (3:02 pm)
if (stream->writeFlag((mask & TeamMask) [b]&& !(mask & InitialUpdateMask)[/b])) {
stream->writeInt(mTeam, 3);
}Why the additional check in this if statement? That is guaranteeing that if the team is set before the client actually connects (which is probably pretty standard, since AI's are spawned during mission startup, but before the client enters the game), the team will not be sent--it's the initial update to that client.
#10
02/05/2008 (3:05 pm)
Good catch stephen! i blew right past that.
#11
02/05/2008 (3:06 pm)
Heh..I did too, several times :(
#12
02/05/2008 (3:36 pm)
No luck with that... Got rid of that extra test and nothing has changed. I am lost on this one...
#13
It's got to be something subtle that we're simply not seeing in your snippets--I can think of a couple of possibilities, but need to see the entire code for the changes to know for sure.
02/05/2008 (4:11 pm)
Ok, this one does have me severely puzzled. Can you send me your shapeBase.cc/cpp file, your aiPlayer.cc/cpp, and the entire file where you are spawning the AI (TorqueScript)?It's got to be something subtle that we're simply not seeing in your snippets--I can think of a couple of possibilities, but need to see the entire code for the changes to know for sure.
#14
02/05/2008 (4:13 pm)
Also, try putting debug prints near your writeInt() and readInt() to confirm whether it is indeed a ghosting issue.
#15
This means that for whatever reason, the mask bits aren't dirty for the team mask when the object is added. At least I think that's what it means.
02/06/2008 (10:39 am)
I put the debug print by my readInt and it only displays when I personally through script (a time after the bot/player is spawned) call %obj.setTeam(x);This means that for whatever reason, the mask bits aren't dirty for the team mask when the object is added. At least I think that's what it means.
#16
This happens when the spawn call and the .setTeam function are only 9 lines apart.
When I use the .setTeam function a bit later, after the object's been around a few seconds, it updates the client mask as well. So when I call the function manually (or by trigger or something) it correctly updates the client and the team color text displays properly.
This is not the only case in which this happens; I have 'experience' linked into the ShapeBase class, but it doesn't properly update the client when the update function is called directly after the spawn. But call it later, and it updates just fine.
My .cc and .h files are yours to command. Please help!
02/14/2008 (7:55 pm)
Yes, after further experimentation, it appears that when the object is 'spawned' and then a value is immediately changed, the mask bits aren't dirty, so the client doesn't recieve the updated values, but the server does.This happens when the spawn call and the .setTeam function are only 9 lines apart.
When I use the .setTeam function a bit later, after the object's been around a few seconds, it updates the client mask as well. So when I call the function manually (or by trigger or something) it correctly updates the client and the team color text displays properly.
This is not the only case in which this happens; I have 'experience' linked into the ShapeBase class, but it doesn't properly update the client when the update function is called directly after the spawn. But call it later, and it updates just fine.
My .cc and .h files are yours to command. Please help!
#17
I've compared the obj.setShapeName and how it updates to my obj.setTeam function and they are the same. But somehow, when I call from script setShapeName, it updates over to the client, but when I call setTeam, it does not update the client.
So still no forward progress.
03/03/2008 (8:32 am)
Another bit of progress that I've made on this is that if I just send the team id during every packUpdate, it works properly. However, I don't want to do that because it'd be a lot of useless bits that I send when I don't have to.I've compared the obj.setShapeName and how it updates to my obj.setTeam function and they are the same. But somehow, when I call from script setShapeName, it updates over to the client, but when I call setTeam, it does not update the client.
So still no forward progress.
#18
03/03/2008 (8:56 am)
Just wanted to note, from the top of the thread, that the server side script would have returned 13 and not 5 because it is the real,unpacked value. The bad value is on client side, any logical corruption happening in the packing/unpacking.
Associate Orion Elenzil
Real Life Plus
Using S32 instead of F32 is a great idea.
Or rather, using F32 is a bizarre idea.
If you don't need negative TeamIDs, you could use U32 instead.