Network optimization - setMoveMaskIfReallyNeedTo()
by Orion Elenzil · 02/28/2006 (7:00 pm) · 11 comments
edit: !!! do not use this resource as presented here !!! - see post at bottom. a fix is on the way !
If you're interested in this resource, you should definitely read this thread as well.
caveat
- i've since modified this locally so that each server-side player object forces MoveMask at least every 20 frames. i don't have cycles right now to post the change, but it's not too tricky. the motivation for this was because i have custom player-vs-player collision code which will push players around on the client side, and without an occasional re-assert from the server you could push stationary players off cliffs, etc.
player.h
declarations:
after "mMountPending":
after "void checkMissionArea();"
player.cc
initialize:
in Player::Player(),
after "mVelocity.set(0,0,0);",
the core function:
at the very bottom
the main culprit:
in updatePos(), change this:
to this:
lesser culprits:
in Player::setTransform(),
change this:
to this:
last one..
in Player::setVelocity(),
change this:
to this:
edited 20060228: tightened threshhold.
edited 20060301: added introductory blurb.
If you're interested in this resource, you should definitely read this thread as well.
caveat
- i've since modified this locally so that each server-side player object forces MoveMask at least every 20 frames. i don't have cycles right now to post the change, but it's not too tricky. the motivation for this was because i have custom player-vs-player collision code which will push players around on the client side, and without an occasional re-assert from the server you could push stationary players off cliffs, etc.
player.h
declarations:
after "mMountPending":
Point3F mPosPrev; ///< Used to determine if we should actually set MoveMask. Point3F mRotPrev; ///< Used to determine if we should actually set MoveMask.
after "void checkMissionArea();"
bool setMoveMaskIfReallyNeedTo(const Point3F& pos, const Point3F& rot);
player.cc
initialize:
in Player::Player(),
after "mVelocity.set(0,0,0);",
mPosPrev.set(0,0,0); mRotPrev.set(0,0,0);
the core function:
at the very bottom
bool Player::setMoveMaskIfReallyNeedTo(const Point3F& pos, const Point3F& rot)
{
F32 dPosThresh = 0.00001f;
F32 dRotThresh = mDegToRad(0.01f);
Point3F dPos;
Point3F dRot;
dPos = pos - mPosPrev;
dRot = rot - mRotPrev;
// note these are ordered from most to least likely -
if (mFabs(dRot.z) > dRotThresh) {
setMaskBits(MoveMask);
mRotPrev = rot;
return true;
}
if (mFabs(dPos.x) > dPosThresh) {
setMaskBits(MoveMask);
mPosPrev = pos;
return true;
}
if (mFabs(dPos.y) > dPosThresh) {
setMaskBits(MoveMask);
mPosPrev = pos;
return true;
}
if (mFabs(dPos.z) > dPosThresh) {
setMaskBits(MoveMask);
mPosPrev = pos;
return true;
}
if (mFabs(dRot.x) > dRotThresh) {
setMaskBits(MoveMask);
mRotPrev = rot;
return true;
}
if (mFabs(dRot.y) > dRotThresh) {
setMaskBits(MoveMask);
mRotPrev = rot;
return true;
}
return false;
}the main culprit:
in updatePos(), change this:
setPosition(start,mRot); setMaskBits(MoveMask); updateContainer();
to this:
setPosition(start,mRot); setMoveMaskIfReallyNeedTo(start, mRot); updateContainer();
lesser culprits:
in Player::setTransform(),
change this:
setMaskBits(MoveMask | NoWarpMask);
to this:
setMoveMaskIfReallyNeedTo(pos, rot); setMaskBits(NoWarpMask);
last one..
in Player::setVelocity(),
change this:
setMaskBits(MoveMask);
to this:
Point3F pos; getTransform().getColumn(3,&pos); setMoveMaskIfReallyNeedTo(pos, mRot);
edited 20060228: tightened threshhold.
edited 20060301: added introductory blurb.
About the author
#2
- I've since realized some problems w/ this resource, please read the new blurb at the top !
- I'm not sure i totally understand what you mean by "register movement in multiple direction/rotation"..
- You might want to restructure the logic in your code there to avoid all those duplicate mFabs()'s and >'s.
03/01/2006 (5:07 pm)
Hi Westy, thanks!- I've since realized some problems w/ this resource, please read the new blurb at the top !
- I'm not sure i totally understand what you mean by "register movement in multiple direction/rotation"..
- You might want to restructure the logic in your code there to avoid all those duplicate mFabs()'s and >'s.
#3
03/08/2006 (2:30 am)
#4
I have a suspicion that it might not be as significant w/ vehicles, since i presume they're almost always in motion.
03/08/2006 (9:08 am)
I haven't used vehicle players at all,I have a suspicion that it might not be as significant w/ vehicles, since i presume they're almost always in motion.
#5
04/24/2006 (9:31 am)
is this needed for 1.4?
#6
this is definitely in the "if you don't know whether or not you need this, you don't need it" category - torque's networking is basically pretty efficient.
that said, 1.4 does call updateMoveMask() every tick for player objects the same way 1.3 does.
04/24/2006 (9:57 am)
Hey Juha -this is definitely in the "if you don't know whether or not you need this, you don't need it" category - torque's networking is basically pretty efficient.
that said, 1.4 does call updateMoveMask() every tick for player objects the same way 1.3 does.
#7
13 bytes from 100 000 connected users every tick (32ms)...
97Kb every tick, with ~31 ticks in a second...
That is 3mb per second the server is saving.
THen again... all 100 000 users need to stand still :-P
04/25/2006 (1:29 am)
This is for all those people who wants to create MMO games ;)13 bytes from 100 000 connected users every tick (32ms)...
97Kb every tick, with ~31 ticks in a second...
That is 3mb per second the server is saving.
THen again... all 100 000 users need to stand still :-P
#8
This is indeed needed for my project (all things are that helps to save some bandwitdh)
04/28/2006 (4:16 am)
I did read all the comments and the resource again and I think I finally got it :PThis is indeed needed for my project (all things are that helps to save some bandwitdh)
#9
yesterday i realized that this resource has a kinda serious flaw, which is that when a player *stops* moving, that fact is not transmited prompty to the clients, who then over-shoot the client-side ghost position & rotation, and you end up with characters "popping" back when they stop moving.
i've got a fix, but i'm not at the right computer today so i can't post it just now.
basically,
in addition to testing the first derivative of position & rotation for being > some threshhold, you need to also test the second derivative.
not so terrible.
so, a new and substantially cleaned-up version of setMoveMaskIfReallyNeedTo() is coming soon.
sorry if this has muddled up anyone's networked play!
06/08/2006 (9:04 am)
hey all -yesterday i realized that this resource has a kinda serious flaw, which is that when a player *stops* moving, that fact is not transmited prompty to the clients, who then over-shoot the client-side ghost position & rotation, and you end up with characters "popping" back when they stop moving.
i've got a fix, but i'm not at the right computer today so i can't post it just now.
basically,
in addition to testing the first derivative of position & rotation for being > some threshhold, you need to also test the second derivative.
not so terrible.
so, a new and substantially cleaned-up version of setMoveMaskIfReallyNeedTo() is coming soon.
sorry if this has muddled up anyone's networked play!
#11
hokay, improvements to setMoveMaskIfReallyNeedTo(), as per above.
player.h
additions in bold:
player.cc
in the constructor, additions in bold.
and a new version of player::setMoveMaskIfReallyNeedTo():
note there's some looseness with using the same threshhold for the first & second-order derivatives, but it seems to work well.
also this version of the routine is cleaner.
furthermore, i'm using a static counter, sMaskCountDown, which should probably be a per-instance member variable.
hopefully this works for folks and causes less grief than it saves bandwidth !
07/24/2006 (4:48 pm)
Hi! Sorry!hokay, improvements to setMoveMaskIfReallyNeedTo(), as per above.
player.h
additions in bold:
Point3F mPosPrev; ///< Used to determine if we should actually set MoveMask. Point3F mRotPrev; ///< Used to determine if we should actually set MoveMask.[b] Point3F mdPosPrev; ///< Second-order derivative. Also used for conditional moveMask. Point3F mdRotPrev; ///< Second-order derivative. Also used for conditional moveMask.[/b]
player.cc
in the constructor, additions in bold.
mPosPrev .set(0,0,0); mRotPrev .set(0,0,0);[b] mdPosPrev.set(0,0,0); mdRotPrev.set(0,0,0);[/b]
and a new version of player::setMoveMaskIfReallyNeedTo():
bool Player::setMoveMaskIfReallyNeedTo(const Point3F& pos, const Point3F& rot)
{
static U32 sMaskCountDown = 20;
moveMaskForceCountdown--;
Point3F dPos;
Point3F dRot;
Point3F ddPos;
Point3F ddRot;
dPos = pos - mPosPrev;
dRot = rot - mRotPrev;
if (moveMaskForceCountdown <= 0) {
setMaskBits(MoveMask);
moveMaskForceCountdown = sMaskCountDown;
}
else {
F32 posThresh = 0.00001f;
F32 rotThresh = mDegToRad(0.1f);
ddPos = dPos - mdPosPrev;
ddRot = dRot - mdRotPrev;
// note these are ordered from most to least likely -
if ((mFabs(ddRot.z) > rotThresh || mFabs(dRot.z) > rotThresh) ||
(mFabs(ddPos.x) > posThresh || mFabs(dPos.x) > posThresh) ||
(mFabs(ddPos.y) > posThresh || mFabs(dPos.y) > posThresh) ||
(mFabs(ddPos.z) > posThresh || mFabs(dPos.z) > posThresh) ||
(mFabs(ddRot.x) > rotThresh || mFabs(dRot.x) > rotThresh)) {
setMaskBits(MoveMask);
moveMaskForceCountdown = sMaskCountDown;
}
}
mPosPrev = pos;
mRotPrev = rot;
mdPosPrev = dPos;
mdRotPrev = dRot;
return false;
}note there's some looseness with using the same threshhold for the first & second-order derivatives, but it seems to work well.
also this version of the routine is cleaner.
furthermore, i'm using a static counter, sMaskCountDown, which should probably be a per-instance member variable.
hopefully this works for folks and causes less grief than it saves bandwidth !

Torque Owner Westy
Btw if you want to register movement in multiple direction/rotation, do something like this:
bool Player::setMoveMaskIfReallyNeedTo(const Point3F& pos, const Point3F& rot) { F32 dPosThresh = 0.00001f; F32 dRotThresh = mDegToRad(0.01f); Point3F dPos; Point3F dRot; dPos = pos - mPosPrev; dRot = rot - mRotPrev; bool moveMask = false; if (mFabs(dRot.z) > dRotThresh || mFabs(dPos.x) > dPosThresh || mFabs(dPos.y) > dPosThresh || mFabs(dPos.z) > dPosThresh || mFabs(dRot.x) > dRotThresh || mFabs(dRot.y) > dRotThresh) { setMaskBits(MoveMask); moveMask = true; }else return false; if (mFabs(dRot.z) > dRotThresh) mRotPrev = rot; if (mFabs(dPos.x) > dPosThresh) mPosPrev = pos; if (mFabs(dPos.y) > dPosThresh) mPosPrev = pos; if (mFabs(dPos.z) > dPosThresh) mPosPrev = pos; if (mFabs(dRot.x) > dRotThresh) mRotPrev = rot; if (mFabs(dRot.y) > dRotThresh) mRotPrev = rot; return true; }