Game Development Community

Datablock fix for bidirectional/server ghosting TGE 1.3

by Entr0py · 04/11/2006 (2:02 pm) · 5 comments

In stock TGE, datablocks are created on the client as the same object Id as the sender. In my game I already have datablocks stored that I do not want overwritten, so I modified it to search for datablocks by name instead of Id. If a datablock of the same name is found, it is overwritten and the old Id is retained, otherwise it will find an unassigned Id to create the object. This resource has been tested and works with TGE 1.3 but newer versions of TGE have not been tested.

in gamebase.h

right under:
private:
   GameBaseData*     mDataBlock;
   StringTableEntry  mNameTag;

added this:

StringTableEntry  mDataBlockName;

right under:
/// Returns the datablock for this object.
   GameBaseData* getDataBlock()  { return mDataBlock; }

added this:

StringTableEntry getDataBlockName()   { return mDataBlockName; }

I wanted gamebase items with datablocks to search for it's datablock by name.

here's my GameBase::packUpdate in Gamebase.cc:

U32 GameBase::packUpdate(NetConnection *, U32 mask, BitStream *stream)
{
   // Check the mask for the ScaleMask; if it's true, pass that in.
   if (stream->writeFlag( mask & ScaleMask ) ) {
       mathWrite( *stream, Parent::getScale() );
   }
   if (stream->writeFlag((mask & DataBlockMask) && mDataBlock != NULL)) {
//    stream->writeRangedU32(mDataBlock->getId(),
//                             DataBlockObjectIdFirst,
//                            DataBlockObjectIdLast);
      stream->writeString(mDataBlock->getName());
   } 
   return 0;
}

here's my GameBase::unpackUpdate in gameBase.cc:

void GameBase::unpackUpdate(NetConnection *con, BitStream *stream)
{
const char * string;
   if (stream->readFlag()) {
      VectorF scale;
      mathRead( *stream, &scale );
      setScale( scale );
   }
   if (stream->readFlag()) {
      GameBaseData* dptr = 0;
//     SimObjectId id = stream->readRangedU32(DataBlockObjectIdFirst,
//                                            DataBlockObjectIdLast);
      mDataBlockName = stream->readSTString();
        
//      if (!Sim::findObject(id,dptr) || !setDataBlock(dptr))
//        con->setLastError("Invalid packet GameBase::unpackUpdate()");
      if (!Sim::findObject(mDataBlockName,dptr) || !setDataBlock(dptr))
        con->setLastError("Invalid packet GameBase::unpackUpdate()");
        
   }
}

Set the datablock event to search by names as well.

in GameConnectionEvents.h:

right under:
U32 mMissionSequence;

added this:
const char * mId;


here's my SimDataBlockEvent::SimDataBlockEvent

SimDataBlockEvent::SimDataBlockEvent(SimDataBlock* obj, U32 index, U32 total, U32 missionSequence)
{
   mObj = NULL;
   mIndex = index;
   mTotal = total;
   mMissionSequence = missionSequence;
   mProcess = false;
   mId = NULL;
   if(obj)
   {
      // use getname() instead of getID() (stock tge looked up by Id)
      mId = obj->getName();  
      id = obj->getId();   
//      AssertFatal(id >= DataBlockObjectIdFirst && id <= DataBlockObjectIdLast,
//                  "Out of range event data block id... check simBase.h");
//      Con::printf("queuing data block: %d", mIndex);
   }
}

in SimDataBlockEvent::pack right after:

AssertFatal(obj,
                  "SimDataBlockEvent:: Data blocks cannot be deleted");

added this:

bstream->writeString( mId );

in SimDataBlockEvent::unpack right after:

mProcess = true;

added this:

mId = bstream->readSTString();

here's my SimDataBlockEvent::process in gameConnectionEvents.cc:

void SimDataBlockEvent::process(NetConnection *cptr)
{
   if(mProcess)
   {
 
      SimDataBlock* obj;
      char *errorBuffer = NetConnection::getErrorBuffer();

     //call the console function to set the number of blocks to be sent
        Con::executef(3, "onDataBlockObjectReceived", Con::getIntArg(mIndex), Con::getIntArg(mTotal));
 
      if (Sim::findObject(mId,obj))
      {
//         U8 buf[1500];
//         BitStream stream(buf, 1500);
//         mObj->packData(&stream);
//         stream.setPosition(0);
//        obj->unpackData(&stream);
//         obj->preload(false, errorBuffer); 
    // found a datablock of the same name, let's overwrite it
    id = obj->getId();
      } else {
      // no pre-existing datablock found, find an unused ID
      id=1000;  // change to whatever you want, i just prefer local datablock ids to be under 1000
      while(Sim::findObject(id,obj))
      id++;
   }
         bool reg = mObj->registerObject(id);
         cptr->addObject(mObj);
         mObj->assignName(mId);
         GameConnection *conn = dynamic_cast<GameConnection *>(cptr);
         if(conn)
         {
            conn->preloadDataBlock(mObj);
            mObj = NULL;
         }
   }
}

#1
03/28/2006 (8:45 pm)
If you want bidirectional ghosting you can change simDataBlockEvent from a CLIENTEVENT to a NETEVENT in gameconnectionevents.cc and you will have to set ghostto(true) ghostfrom(true) in two places in gameconnection.cc (both as initiator and not initiator)

Beware if you do this however, clients will be able to send datablocks to the server, so you will want to setup some sort of security to keep this from happening.


Also, be sure to always name all your datablocks or they will be overwritten!
#2
04/18/2006 (6:20 pm)
@Entropy, this is fascinating, it could in theory be a base to allow multiple servers to hand datablocks back and forth and allow for seamless zone transitions.

I don't know if this would work properly or not but on the surface at least you could do something like this...
Keep 2 or more missions going, assign each server to a section of terrain.
When the OnLeave mission area function is being called, loop through everything that is currently scoped to the client and hand that info off to the new server, then have the client connect to the new server. The new server now knows what was in scope to the client as it transitioned into the zone and can slowly start to delete objects from the scope of the client that it is not responsible for and add objects that it is responsible for.

Anyways thats how I see this being used. The actual implementation is a bit beyond me at the moment, but it is a totally fascinating idea.
#3
04/19/2006 (6:31 pm)
That is precisely what I am doing, I just submitted this as a resource because it was a pain trying to figure out why my datablocks were all messed up. Hopefully this saves you guys a lot of trouble.
#4
04/30/2006 (4:24 pm)
Cool resource, Entr0py. It's amazing what a few changed lines of code can get you. :)
#5
04/03/2007 (12:25 am)
cool. i'm gonna try this..
thanks!