Ghosting questions
by Anthony Lovell · in Torque Game Engine · 08/15/2006 (7:58 am) · 4 replies
Problem one: I cannot ghost NetObjects for some reason.
I create a NetObject on the server whose mNetMask = Ghostable.
I set up the established GhostConnection on the server to this:
c->setGhostFrom(true);
c->activateGhosting();
c->objectLocalScopeAlways(&myNetObject);
the client's connection is setGhostTo(true).
The client's log reports:
LogGhostConnection: Got GhostingStarting 1
The server's log reports:
LogGhostConnection: Ghosting activated - 1
LogGhostConnection: Got ready for normal ghosts 1 1
And yet the client never receives a ghost of the net object.
What other steps am I missing?
Is it necessary to set objectLocalScopeAlways() on all NetObjects which will use this static scoping BEFORE calling activateGhosting()?
----- on a related topic:
I see that the non-static scoping uses a call objectInScope() -- but there is no means by which an object can be declared to be OUT of scope. How do things fall out of scope under normal conditions -- simply by a failure for the scoping object to call objectInScope() on them when asked its opinion on the matter?
tone
I create a NetObject on the server whose mNetMask = Ghostable.
I set up the established GhostConnection on the server to this:
c->setGhostFrom(true);
c->activateGhosting();
c->objectLocalScopeAlways(&myNetObject);
the client's connection is setGhostTo(true).
The client's log reports:
LogGhostConnection: Got GhostingStarting 1
The server's log reports:
LogGhostConnection: Ghosting activated - 1
LogGhostConnection: Got ready for normal ghosts 1 1
And yet the client never receives a ghost of the net object.
What other steps am I missing?
Is it necessary to set objectLocalScopeAlways() on all NetObjects which will use this static scoping BEFORE calling activateGhosting()?
----- on a related topic:
I see that the non-static scoping uses a call objectInScope() -- but there is no means by which an object can be declared to be OUT of scope. How do things fall out of scope under normal conditions -- simply by a failure for the scoping object to call objectInScope() on them when asked its opinion on the matter?
tone
#2
On your server, you should also call: setGhostTo(false)
On your client, you should also call: setGhostFrom(false)
I'm not sure if this is 'really' necessary, or if it will solve your problem, but it's worth a try.
In my application, I do not use objectLocalScopeAlways(), but I do have some objects that will always be in scope. I simply call setScopeObject(), and in the performScopeQuery() function, I set all the NetObjects to be in scope, using objectInScope(). This works for me, and it leaves room for later algorithmic improvement if I choose to cull certain objects from scope.
In response to "Related Topic":
You are right - failure to call objectInScope will force the object to be out of scope for that update.
Edit: I see in another post you made, you got your problem solved. I am curious as to what you did, and if you got your original method working.
08/16/2006 (9:12 am)
In response to Problem #1:On your server, you should also call: setGhostTo(false)
On your client, you should also call: setGhostFrom(false)
I'm not sure if this is 'really' necessary, or if it will solve your problem, but it's worth a try.
In my application, I do not use objectLocalScopeAlways(), but I do have some objects that will always be in scope. I simply call setScopeObject(), and in the performScopeQuery() function, I set all the NetObjects to be in scope, using objectInScope(). This works for me, and it leaves room for later algorithmic improvement if I choose to cull certain objects from scope.
In response to "Related Topic":
You are right - failure to call objectInScope will force the object to be out of scope for that update.
Edit: I see in another post you made, you got your problem solved. I am curious as to what you did, and if you got your original method working.
#3
First, the *possible* bug I found is in ghostConnection.cc's ::prepateWritePacket() code. It just looked wrong, especially when comparing it to the TGE version
Second, I was failing to see my NetObjects ghosted, and it turns out that one reason was that I had no scoping object -- but there are certainly ways to use ghosting that do not rely on this mechanism. A quick pair of fixes to GhostConnection's ::writePacket() method allowed this to work when there was a null mScopingObject
first fix:
and further down in the same function:
I will post right after this a helpful little bit that allows the server to publish a NetObject globally to an entire NetInterface -- all existing client connections will see it, and new joiners will see it. It is a very simple data model for objects you want EVERYONE to see.
tone
08/16/2006 (9:15 am)
Ok. I got this working with some difficulty. I may have found a bug, and I certainly found a needless constraint in GhostConnection which I can advise others in how to overcome.First, the *possible* bug I found is in ghostConnection.cc's ::prepateWritePacket() code. It just looked wrong, especially when comparing it to the TGE version
void GhostConnection::prepareWritePacket()
{
Parent::prepareWritePacket();
#ifdef MY_FIX
if(!doesGhostFrom() || !mGhosting)
return;
#else
if(!doesGhostFrom() && !mGhosting) // this original TNL code seems wrong? TONE
return;
#endifSecond, I was failing to see my NetObjects ghosted, and it turns out that one reason was that I had no scoping object -- but there are certainly ways to use ghosting that do not rely on this mechanism. A quick pair of fixes to GhostConnection's ::writePacket() method allowed this to work when there was a null mScopingObject
first fix:
void GhostConnection::writePacket(BitStream *bstream, PacketNotify *pnotify)
{
Parent::writePacket(bstream, pnotify);
GhostPacketNotify *notify = static_cast<GhostPacketNotify *>(pnotify);
if(mConnectionParameters.mDebugObjectSizes)
bstream->writeInt(DebugChecksum, 32);
notify->ghostList = NULL;
if(!doesGhostFrom())
return;
#ifdef MY_FIX
if(!bstream->writeFlag(mGhosting))
return;
#else
if(!bstream->writeFlag(mGhosting && mScopeObject.isValid()))
return;
#endifand further down in the same function:
// don't do any ghost processing on objects that are being killed
// or in the process of ghosting
else if(!(walk->flags & (GhostInfo::KillingGhost | GhostInfo::Ghosting)))
{
if(walk->flags & GhostInfo::KillGhost)
walk->priority = 10000;
#ifdef MY_FIX // this code should be made to work without a scoping object, right?
else if (mScopeObject.isNull())
walk->priority = F32(walk->updateSkipCount) * 0.1f; // cribbed from NetObject::getUpdatePriority()
#endif
else
walk->priority = walk->obj->getUpdatePriority(mScopeObject, walk->updateMask, walk->updateSkipCount);
}
else
walk->priority = 0;I will post right after this a helpful little bit that allows the server to publish a NetObject globally to an entire NetInterface -- all existing client connections will see it, and new joiners will see it. It is a very simple data model for objects you want EVERYONE to see.
tone
#4
And, a bit further down in ::objectInScope:
And, finally...
I hope this helps make this code easier to track when others are hitting snags in future
tone
08/16/2006 (9:24 am)
For the sake of completeness, in trying to mind Ben Garney's suggestion to monitor the logging output, I found many places where things could "go wrong" which did not generate any logging material at all. The following logging mods make GhostConnection a little more explicative when things are not going as the coder may have intended:void GhostConnection::objectLocalScopeAlways(NetObject *obj)
{
// TONE added this log msg
if(!doesGhostFrom()) {
TNLLogMessageV(LogGhostConnection, ("WARNING: objectLocalScopeAlways() called, but !doesGhostFrom()"));
return;
}void GhostConnection::objectLocalClearAlways(NetObject *obj)
{
// TONE added this log msg
if(!doesGhostFrom()) {
TNLLogMessageV(LogGhostConnection, ("WARNING: objectLocalClearAlways() called, but !doesGhostFrom()"));
return;
}void GhostConnection::objectInScope(NetObject *obj)
{
// TONE added these log messages
if (!mScoping) {
TNLLogMessageV(LogGhostConnection, ("WARNING: objectInScope() called, but !mScoping"));
return;
}
if (!doesGhostFrom()) {
TNLLogMessageV(LogGhostConnection, ("WARNING: objectInScope() called, but !doesGhostFrom()"));
return;
}
if (!obj->isGhostable()) {
TNLLogMessageV(LogGhostConnection, ("WARNING: objectInScope() called for !%s->isGhostable()", obj->getClassName()));
return;
}
if (obj->isScopeLocal() && !isLocalConnection()) {
TNLLogMessageV(LogGhostConnection, ("WARNING: objectInScope() called for a %s which isScopeLocal() on non-local connection", obj->getClassName()));
return;
}
// end of TONE's alterationsAnd, a bit further down in ::objectInScope:
// TONE added this warning
if (mGhostFreeIndex == MaxGhostCount) {
TNLLogMessageV(LogGhostConnection, ("ERROR: objectInScopeCalled, but maxGhosts=%d has been reached!", MaxGhostCount));
return;
}And, finally...
void GhostConnection::activateGhosting()
{
if(!doesGhostFrom()) {
// TONE added this logging message
TNLLogMessageV(LogGhostConnection, ("ERROR: activateGhosting() called, but !doesGhostFrom()"));
return;
}I hope this helps make this code easier to track when others are hitting snags in future
tone
Torque Owner Anthony Lovell
tnlNetObject.h declares NetObject has a friend class GhostAlwaysObjectEvent; -- but no such class exists in the project
A comment in this header file also indicates a mNetFlags mask bit of "ScopeAlways", and yet this is also missing in action.
I think perhaps the objectLocalScopeAlways() method means only to scope always to the local client (a function whose utility escapes me).
Is there no simple means by which the server can place a NetObject in persistent scope of a given connection, and a flipside means by which it can remove it from such scope?
tone