netGhost.cpp

Engine/source/sim/netGhost.cpp

More...

Classes:

Public Defines

define
DebugChecksum() 0xF00DBAAD

Public Variables

Public Functions

ConsoleDocClass(GhostAlwaysObjectEvent , "@brief Legacy or soon <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be locked down <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" "Not intended <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> game development, <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> editors or internal use <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">only.\n\n</a> " " @internal" )
DefineEngineMethod(NetConnection , getGhostsActive , S32 , () , "@brief Provides the number of active ghosts on the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">connection.\n\n</a>" "@returns The number of active <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ghosts.\n</a>" "@see @ref ghosting_scoping <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> description of the ghosting <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">system.\n\n</a>" )

Detailed Description

Public Defines

DebugChecksum() 0xF00DBAAD

Public Variables

U32 gGhostUpdates 

Public Functions

ConsoleDocClass(GhostAlwaysObjectEvent , "@brief Legacy or soon <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be locked down <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" "Not intended <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> game development, <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> editors or internal use <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">only.\n\n</a> " " @internal" )

DefineEngineMethod(NetConnection , getGhostsActive , S32 , () , "@brief Provides the number of active ghosts on the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">connection.\n\n</a>" "@returns The number of active <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ghosts.\n</a>" "@see @ref ghosting_scoping <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> description of the ghosting <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">system.\n\n</a>" )

IMPLEMENT_CO_NETEVENT_V1(GhostAlwaysObjectEvent )

UQECompare(const void * a, const void * b)

   1
   2//-----------------------------------------------------------------------------
   3// Copyright (c) 2012 GarageGames, LLC
   4//
   5// Permission is hereby granted, free of charge, to any person obtaining a copy
   6// of this software and associated documentation files (the "Software"), to
   7// deal in the Software without restriction, including without limitation the
   8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
   9// sell copies of the Software, and to permit persons to whom the Software is
  10// furnished to do so, subject to the following conditions:
  11//
  12// The above copyright notice and this permission notice shall be included in
  13// all copies or substantial portions of the Software.
  14//
  15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  21// IN THE SOFTWARE.
  22//-----------------------------------------------------------------------------
  23
  24#include "platform/platform.h"
  25#include "core/dnet.h"
  26#include "console/simBase.h"
  27#include "sim/netConnection.h"
  28#include "core/stream/bitStream.h"
  29#include "sim/netObject.h"
  30//#include "core/resManager.h"
  31#include "console/console.h"
  32#include "console/consoleTypes.h"
  33#include "console/engineAPI.h"
  34
  35#define DebugChecksum 0xF00DBAAD
  36
  37Signal<void()>    NetConnection::smGhostAlwaysDone;
  38
  39extern U32 gGhostUpdates;
  40
  41class GhostAlwaysObjectEvent : public NetEvent
  42{
  43   SimObjectId objectId;
  44   U32 ghostIndex;
  45   NetObject *object;
  46   bool validObject;
  47public:
  48   typedef NetEvent Parent;
  49   GhostAlwaysObjectEvent(NetObject *obj = NULL, U32 index = 0)
  50   {
  51      if(obj)
  52      {
  53         objectId = obj->getId();
  54         ghostIndex = index;
  55      }
  56      else
  57      {
  58         objectId = 0;
  59         ghostIndex = 0;
  60      }
  61      object = NULL;
  62      validObject = false;
  63   }
  64   ~GhostAlwaysObjectEvent()
  65      { delete object; }
  66
  67   void pack(NetConnection *ps, BitStream *bstream)
  68   {
  69      bstream->writeInt(ghostIndex, NetConnection::GhostIdBitSize);
  70
  71      NetObject *obj = (NetObject *) Sim::findObject(objectId);
  72      if(bstream->writeFlag(obj != NULL))
  73      {
  74         S32 classId = obj->getClassId(ps->getNetClassGroup());
  75         bstream->writeClassId(classId, NetClassTypeObject, ps->getNetClassGroup());
  76#ifdef TORQUE_NET_STATS
  77         U32 beginSize = bstream->getBitPosition();
  78#endif
  79         U32 retMask = obj->packUpdate(ps, 0xFFFFFFFF, bstream);
  80         if ( retMask != 0 ) obj->setMaskBits( retMask );
  81
  82#ifdef TORQUE_NET_STATS
  83         obj->getClassRep()->updateNetStatPack(0xFFFFFFFF, bstream->getBitPosition() - beginSize);
  84#endif
  85      }
  86   }
  87   void write(NetConnection *ps, BitStream *bstream)
  88   {
  89      bstream->writeInt(ghostIndex, NetConnection::GhostIdBitSize);
  90      if(bstream->writeFlag(validObject))
  91      {
  92         S32 classId = object->getClassId(ps->getNetClassGroup());
  93         bstream->writeClassId(classId, NetClassTypeObject, ps->getNetClassGroup());
  94#ifdef TORQUE_NET_STATS
  95         U32 beginSize = bstream->getBitPosition();
  96#endif
  97         U32 retMask = object->packUpdate(ps, 0xFFFFFFFF, bstream);
  98         if ( retMask != 0 ) object->setMaskBits( retMask );
  99#ifdef TORQUE_NET_STATS
 100         object->getClassRep()->updateNetStatPack(0xFFFFFFFF, bstream->getBitPosition() - beginSize);
 101#endif
 102      }
 103   }
 104   void unpack(NetConnection *ps, BitStream *bstream)
 105   {
 106      ghostIndex = bstream->readInt(NetConnection::GhostIdBitSize);
 107
 108      if(bstream->readFlag())
 109      {
 110         S32 classId = bstream->readClassId(NetClassTypeObject, ps->getNetClassGroup());
 111         if(classId == -1)
 112         {
 113            ps->setLastError("Invalid packet. (invalid ghost class id)");
 114            return;
 115         }
 116         object = (NetObject *) ConsoleObject::create(ps->getNetClassGroup(), NetClassTypeObject, classId);
 117         if(!object)
 118         {
 119            ps->setLastError("Invalid packet. (failed to created from class id)");
 120            return;
 121         }
 122         object->mNetFlags = NetObject::IsGhost;
 123         object->mNetIndex = ghostIndex;
 124
 125#ifdef TORQUE_NET_STATS
 126         U32 beginSize = bstream->getBitPosition();
 127#endif
 128         object->unpackUpdate(ps, bstream);
 129#ifdef TORQUE_NET_STATS
 130         object->getClassRep()->updateNetStatUnpack(bstream->getBitPosition() - beginSize);
 131#endif
 132         validObject = true;
 133      }
 134      else
 135      {
 136         object = new NetObject;
 137         validObject = false;
 138      }
 139   }
 140   void process(NetConnection *ps)
 141   {
 142      Con::executef("onGhostAlwaysObjectReceived");
 143
 144      ps->setGhostAlwaysObject(object, ghostIndex);
 145      object = NULL;
 146   }
 147   DECLARE_CONOBJECT(GhostAlwaysObjectEvent);
 148};
 149
 150IMPLEMENT_CO_NETEVENT_V1(GhostAlwaysObjectEvent);
 151
 152ConsoleDocClass( GhostAlwaysObjectEvent,
 153            "@brief Legacy or soon to be locked down object.\n\n"
 154            "Not intended for game development, for editors or internal use only.\n\n "
 155            "@internal");
 156
 157DefineEngineMethod( NetConnection, getGhostsActive, S32, (),,
 158   "@brief Provides the number of active ghosts on the connection.\n\n"
 159   "@returns The number of active ghosts.\n"
 160   "@see @ref ghosting_scoping for a description of the ghosting system.\n\n")
 161{
 162   return object->getGhostsActive();
 163}
 164
 165void NetConnection::setGhostTo(bool ghostTo)
 166{
 167   if(mLocalGhosts) // if ghosting to this is already enabled, silently return
 168      return;
 169
 170   if(ghostTo)
 171   {
 172      mLocalGhosts = new NetObject *[MaxGhostCount];
 173      for(S32 i = 0; i < MaxGhostCount; i++)
 174         mLocalGhosts[i] = NULL;
 175   }
 176}
 177
 178void NetConnection::setGhostFrom(bool ghostFrom)
 179{
 180   if(mGhostArray)
 181      return;
 182
 183   if(ghostFrom)
 184   {
 185      mGhostFreeIndex = mGhostZeroUpdateIndex = 0;
 186      mGhostArray = new GhostInfo *[MaxGhostCount];
 187      mGhostRefs = new GhostInfo[MaxGhostCount];
 188      S32 i;
 189      for(i = 0; i < MaxGhostCount; i++)
 190      {
 191         mGhostRefs[i].obj = NULL;
 192         mGhostRefs[i].index = i;
 193         mGhostRefs[i].updateMask = 0;
 194      }
 195      mGhostLookupTable = new GhostInfo *[GhostLookupTableSize];
 196      for(i = 0; i < GhostLookupTableSize; i++)
 197         mGhostLookupTable[i] = 0;
 198   }
 199}
 200
 201void NetConnection::ghostOnRemove()
 202{
 203   if(mGhostArray)
 204      clearGhostInfo();
 205}
 206
 207void NetConnection::ghostPacketDropped(PacketNotify *notify)
 208{
 209   GhostRef *packRef = notify->ghostList;
 210   // loop through all the packRefs in the packet
 211
 212   while(packRef)
 213   {
 214      GhostRef *temp = packRef->nextRef;
 215
 216      U32 orFlags = 0;
 217      AssertFatal(packRef->nextUpdateChain == NULL, "Out of order notify!!");
 218
 219      // clear out the ref for this object, plus or together all
 220      // flags from updates after this
 221
 222      GhostRef **walk = &(packRef->ghost->updateChain);
 223      while(*walk != packRef)
 224      {
 225         orFlags |= (*walk)->mask;
 226         walk = &((*walk)->nextUpdateChain);
 227      }
 228      *walk = 0;
 229
 230      // for any flags we haven't updated since this (dropped) packet
 231      // or them into the mask so they'll get updated soon
 232
 233      orFlags = packRef->mask & ~orFlags;
 234
 235      if(orFlags)
 236      {
 237         if(!packRef->ghost->updateMask)
 238         {
 239            packRef->ghost->updateMask = orFlags;
 240            ghostPushNonZero(packRef->ghost);
 241         }
 242         else
 243            packRef->ghost->updateMask |= orFlags;
 244      }
 245
 246      // if this packet was ghosting an object, set it
 247      // to re ghost at it's earliest convenience
 248
 249      if(packRef->ghostInfoFlags & GhostInfo::Ghosting)
 250      {
 251         packRef->ghost->flags |= GhostInfo::NotYetGhosted;
 252         packRef->ghost->flags &= ~<a href="/coding/class/structghostinfo/">GhostInfo</a>::Ghosting;
 253      }
 254
 255      // otherwise, if it was being deleted,
 256      // set it to re-delete
 257
 258      else if(packRef->ghostInfoFlags & GhostInfo::KillingGhost)
 259      {
 260         packRef->ghost->flags |= GhostInfo::KillGhost;
 261         packRef->ghost->flags &= ~<a href="/coding/class/structghostinfo/">GhostInfo</a>::KillingGhost;
 262      }
 263
 264      delete packRef;
 265      packRef = temp;
 266   }
 267}
 268
 269void NetConnection::ghostPacketReceived(PacketNotify *notify)
 270{
 271   GhostRef *packRef = notify->ghostList;
 272
 273   // loop through all the notifies in this packet
 274
 275   while(packRef)
 276   {
 277      GhostRef *temp = packRef->nextRef;
 278
 279      AssertFatal(packRef->nextUpdateChain == NULL, "Out of order notify!!");
 280
 281      // clear this notify from the end of the object's notify
 282      // chain
 283
 284      GhostRef **walk = &(packRef->ghost->updateChain);
 285      while(*walk != packRef)
 286         walk = &((*walk)->nextUpdateChain);
 287
 288      *walk = 0;
 289
 290      // if this object was ghosting , it is now ghosted
 291
 292      if(packRef->ghostInfoFlags & GhostInfo::Ghosting)
 293         packRef->ghost->flags &= ~<a href="/coding/class/structghostinfo/">GhostInfo</a>::Ghosting;
 294
 295      // otherwise, if it was dieing, free the ghost
 296
 297      else if(packRef->ghostInfoFlags & GhostInfo::KillingGhost)
 298         freeGhostInfo(packRef->ghost);
 299
 300      delete packRef;
 301      packRef = temp;
 302   }
 303}
 304
 305struct UpdateQueueEntry
 306{
 307   F32 priority;
 308   GhostInfo *obj;
 309
 310   UpdateQueueEntry(F32 in_priority, GhostInfo *in_obj)
 311      { priority = in_priority; obj = in_obj; }
 312};
 313
 314static S32 QSORT_CALLBACK UQECompare(const void *a,const void *b)
 315{
 316   GhostInfo *ga = *((GhostInfo **) a);
 317   GhostInfo *gb = *((GhostInfo **) b);
 318
 319   F32 ret = ga->priority - gb->priority;
 320   return (ret < 0) ? -1 : ((ret > 0) ? 1 : 0);
 321}
 322
 323void NetConnection::ghostWritePacket(BitStream *bstream, PacketNotify *notify)
 324{
 325#ifdef    TORQUE_DEBUG_NET
 326   bstream->writeInt(DebugChecksum, 32);
 327#endif
 328
 329   notify->ghostList = NULL;
 330
 331   if(!isGhostingFrom())
 332      return;
 333
 334   if(!bstream->writeFlag(mGhosting))
 335      return;
 336
 337   // fill a packet (or two) with ghosting data
 338
 339   // first step is to check all our polled ghosts:
 340
 341   // 1. Scope query - find if any new objects have come into
 342   //    scope and if any have gone out.
 343   // 2. call scoped objects' priority functions if the flag set is nonzero
 344   //    A removed ghost is assumed to have a high priority
 345   // 3. call updates based on sorted priority until the packet is
 346   //    full.  set flags to zero for all updated objects
 347
 348   CameraScopeQuery camInfo;
 349
 350   camInfo.camera = NULL;
 351   camInfo.pos.set(0,0,0);
 352   camInfo.orientation.set(0,1,0);
 353   camInfo.visibleDistance = 1;
 354   camInfo.fov = (F32)(3.1415f / 4.0f);
 355   camInfo.sinFov = 0.7071f;
 356   camInfo.cosFov = 0.7071f;
 357
 358   GhostInfo *walk;
 359
 360   // only need to worry about the ghosts that have update masks set...
 361   S32 maxIndex = 0;
 362   S32 i;
 363   for(i = 0; i < mGhostZeroUpdateIndex; i++)
 364   {
 365      // increment the updateSkip for everyone... it's all good
 366      walk = mGhostArray[i];
 367      walk->updateSkipCount++;
 368      if(!(walk->flags & (GhostInfo::ScopeAlways | GhostInfo::ScopeLocalAlways)))
 369         walk->flags &= ~<a href="/coding/class/structghostinfo/#structghostinfo_1ab3e0aaa790933681106f38ca8cb99e68aae9469c6a967fd67461b046497f647e8">GhostInfo::InScope</a>;
 370   }
 371
 372   if( mScopeObject )
 373      mScopeObject->onCameraScopeQuery( this, &camInfo );
 374   doneScopingScene();
 375
 376   for(i = mGhostZeroUpdateIndex - 1; i >= 0; i--)
 377   {
 378      // [rene, 07-Mar-11] Killing ghosts depending on the camera scope queries
 379      //    seems like a bad thing to me and something that definitely has the potential
 380      //    of causing scoping to eat into bandwidth rather than preserve it.  As soon
 381      //    as an object comes back into scope, it will have to completely retransmit its
 382      //    full server-side state from scratch.
 383
 384      if(!(mGhostArray[i]->flags & GhostInfo::InScope))
 385         detachObject(mGhostArray[i]);
 386   }
 387
 388   for(i = mGhostZeroUpdateIndex - 1; i >= 0; i--)
 389   {
 390      walk = mGhostArray[i];
 391      if(walk->index > maxIndex)
 392         maxIndex = walk->index;
 393
 394      // clear out any kill objects that haven't been ghosted yet
 395      if((walk->flags & GhostInfo::KillGhost) && (walk->flags & GhostInfo::NotYetGhosted))
 396      {
 397         freeGhostInfo(walk);
 398         continue;
 399      }
 400      // don't do any ghost processing on objects that are being killed
 401      // or in the process of ghosting
 402      else if(!(walk->flags & (GhostInfo::KillingGhost | GhostInfo::Ghosting)))
 403      {
 404         if(walk->flags & GhostInfo::KillGhost)
 405            walk->priority = 10000;
 406         else
 407            walk->priority = walk->obj->getUpdatePriority(&camInfo, walk->updateMask, walk->updateSkipCount);
 408      }
 409      else
 410         walk->priority = 0;
 411   }
 412   GhostRef *updateList = NULL;
 413   dQsort(mGhostArray, mGhostZeroUpdateIndex, sizeof(GhostInfo *), UQECompare);
 414
 415   // reset the array indices...
 416   for(i = mGhostZeroUpdateIndex - 1; i >= 0; i--)
 417      mGhostArray[i]->arrayIndex = i;
 418
 419   S32 sendSize = 1;
 420   while(maxIndex >>= 1)
 421      sendSize++;
 422
 423   if(sendSize < 3)
 424      sendSize = 3;
 425
 426   bstream->writeInt(sendSize - 3, GhostIndexBitSize);
 427
 428   U32 count = 0;
 429   //
 430   for(i = mGhostZeroUpdateIndex - 1; i >= 0 && !bstream->isFull(); i--)
 431   {
 432      walk = mGhostArray[i];
 433      if(walk->flags & (GhostInfo::KillingGhost | GhostInfo::Ghosting))
 434         continue;
 435      
 436      bstream->writeFlag(true);
 437
 438      bstream->writeInt(walk->index, sendSize);
 439      U32 updateMask = walk->updateMask;
 440
 441      GhostRef *upd = new GhostRef;
 442
 443      upd->nextRef = updateList;
 444      updateList = upd;
 445      upd->nextUpdateChain = walk->updateChain;
 446      walk->updateChain = upd;
 447
 448      upd->ghost = walk;
 449      upd->ghostInfoFlags = 0;
 450
 451      if(walk->flags & GhostInfo::KillGhost)
 452      {
 453         walk->flags &= ~<a href="/coding/class/structghostinfo/">GhostInfo</a>::KillGhost;
 454         walk->flags |= GhostInfo::KillingGhost;
 455         walk->updateMask = 0;
 456         upd->mask = updateMask;
 457         ghostPushToZero(walk);
 458         upd->ghostInfoFlags = GhostInfo::KillingGhost;
 459         bstream->writeFlag(true); // killing ghost
 460      }
 461      else
 462      {
 463         bstream->writeFlag(false);
 464#ifdef TORQUE_DEBUG_NET
 465         U32 startPos = bstream->getCurPos();
 466#endif
 467         if(walk->flags & GhostInfo::NotYetGhosted)
 468         {
 469            S32 classId = walk->obj->getClassId(getNetClassGroup());
 470            bstream->writeClassId(classId, NetClassTypeObject, getNetClassGroup());
 471#ifdef TORQUE_DEBUG_NET
 472            bstream->writeInt(classId ^ DebugChecksum, 32);
 473#endif
 474
 475            walk->flags &= ~<a href="/coding/class/structghostinfo/">GhostInfo</a>::NotYetGhosted;
 476            walk->flags |= GhostInfo::Ghosting;
 477            upd->ghostInfoFlags = GhostInfo::Ghosting;
 478         }
 479#ifdef TORQUE_DEBUG_NET
 480         else {
 481            S32 classId = walk->obj->getClassId(getNetClassGroup());
 482            bstream->writeClassId(classId, NetClassTypeObject, getNetClassGroup());
 483            bstream->writeInt(classId ^ DebugChecksum, 32);
 484         }
 485#endif
 486         // update the object
 487#ifdef TORQUE_NET_STATS
 488         U32 beginSize = bstream->getBitPosition();
 489#endif
 490         U32 retMask = walk->obj->packUpdate(this, updateMask, bstream);
 491#ifdef TORQUE_NET_STATS
 492         walk->obj->getClassRep()->updateNetStatPack(updateMask, bstream->getBitPosition() - beginSize);
 493#endif
 494         DEBUG_LOG(("PKLOG %d GHOST %d: %s", getId(), bstream->getBitPosition() - 16 - startPos, walk->obj->getClassName()));
 495
 496         AssertFatal((retMask & (~updateMask)) == 0, "Cannot set new bits in packUpdate return");
 497
 498         ghostWriteExtra(walk->obj,bstream);
 499
 500         walk->updateMask = retMask;
 501         if(!retMask)
 502            ghostPushToZero(walk);
 503
 504         upd->mask = updateMask & ~retMask;
 505
 506         //PacketStream::getStats()->addBits(PacketStats::Send, bstream->getCurPos() - startPos, walk->obj->getPersistTag());
 507#ifdef TORQUE_DEBUG_NET
 508         bstream->writeInt(walk->index ^ DebugChecksum, 32);
 509#endif
 510      }
 511      walk->updateSkipCount = 0;
 512      count++;
 513   }
 514   //Con::printf("Ghosts updated: %d (%d remain)", count, mGhostZeroUpdateIndex);
 515   // no more objects...
 516   bstream->writeFlag(false);
 517   notify->ghostList = updateList;
 518}
 519
 520void NetConnection::ghostReadPacket(BitStream *bstream)
 521{
 522#ifdef    TORQUE_DEBUG_NET
 523   U32 sum = bstream->readInt(32);
 524   AssertISV(sum == DebugChecksum, "Invalid checksum.");
 525#endif
 526
 527   if(!isGhostingTo())
 528      return;
 529   if(!bstream->readFlag())
 530      return;
 531
 532   S32 idSize;
 533   idSize = bstream->readInt( GhostIndexBitSize);
 534   idSize += 3;
 535
 536   // while there's an object waiting...
 537   gGhostUpdates = 0;
 538
 539   while(bstream->readFlag())
 540   {
 541
 542     gGhostUpdates++;
 543
 544      U32 index;
 545      //S32 startPos = bstream->getCurPos();
 546      index = (U32) bstream->readInt(idSize);
 547      if(bstream->readFlag()) // is this ghost being deleted?
 548      {
 549       mGhostsActive--;
 550         AssertFatal(mLocalGhosts[index] != NULL, "Error, NULL ghost encountered.");
 551         mLocalGhosts[index]->deleteObject();
 552         mLocalGhosts[index] = NULL;
 553      }
 554      else
 555      {
 556         if(!mLocalGhosts[index]) // it's a new ghost... cool
 557         {
 558            mGhostsActive++;
 559            S32 classId = bstream->readClassId(NetClassTypeObject, getNetClassGroup());
 560            if(classId == -1)
 561            {
 562               setLastError("Invalid packet. (invalid new ghost class id)");
 563               return;
 564            }
 565
 566            NetObject *obj = (NetObject *) ConsoleObject::create(getNetClassGroup(), NetClassTypeObject, classId);
 567            if(!obj)
 568            {
 569               setLastError("Invalid packet. (failed to create new ghost)");
 570               return;
 571            }
 572            // remove all flags associated with netobject
 573            obj->mNetFlags &= ~(BIT(NetObject::MaxNetFlagBit+1)-1);
 574            // we're a ghost...
 575            obj->mNetFlags |= NetObject::IsGhost;
 576            
 577            // object gets initial update before adding to the manager
 578
 579            obj->mNetIndex = index;
 580            mLocalGhosts[index] = obj;
 581#ifdef TORQUE_DEBUG_NET
 582            U32 checksum = bstream->readInt(32);
 583            S32 origId = checksum ^ DebugChecksum;
 584            AssertISV(mLocalGhosts[index] != NULL, "Invalid dest ghost.");
 585            AssertISV(origId == mLocalGhosts[index]->getClassId(getNetClassGroup()),
 586               avar("class id mismatch for dest class %s.",
 587                  mLocalGhosts[index]->getClassName()) );
 588#endif
 589
 590            // give derived classes a chance to prepare ghost for reading
 591            ghostPreRead(mLocalGhosts[index],true);
 592
 593#ifdef TORQUE_NET_STATS
 594            U32 beginSize = bstream->getBitPosition();
 595#endif
 596            mLocalGhosts[index]->unpackUpdate(this, bstream);
 597#ifdef TORQUE_NET_STATS
 598            mLocalGhosts[index]->getClassRep()->updateNetStatUnpack(bstream->getBitPosition() - beginSize);
 599#endif
 600            // Setup the remote object pointers before
 601            // we register so that it can be used from onAdd.
 602            if( mRemoteConnection )
 603            {
 604               obj->mServerObject = mRemoteConnection->resolveObjectFromGhostIndex(index);
 605               if ( obj->mServerObject )
 606               {
 607                  obj->mServerObject->mClientObject = obj;
 608                  
 609                  // Sync selection flag as otherwise the editor will end up setting only
 610                  // server-side flags when selecting an object that hasn't been ghosted yet
 611                  // (usually the case when creating new objects).
 612                  
 613                  if( obj->mServerObject->isSelected() )
 614                     obj->setSelected( true );
 615               }
 616            }
 617
 618            if(!obj->registerObject())
 619            {
 620               if(mErrorBuffer.isEmpty())
 621                  setLastError("Invalid packet. (failed to register ghost)");
 622               return;
 623            }
 624         
 625            addObject(obj);
 626            ghostReadExtra(mLocalGhosts[index],bstream,true);
 627         }
 628         else
 629         {
 630#ifdef TORQUE_DEBUG_NET
 631            S32 classId = bstream->readClassId(NetClassTypeObject, getNetClassGroup());
 632            U32 checksum = bstream->readInt(32);
 633            S32 origId = checksum ^ DebugChecksum;
 634            AssertISV(mLocalGhosts[index] != NULL, "Invalid dest ghost.");
 635            AssertISV(origId == mLocalGhosts[index]->getClassId(getNetClassGroup()),
 636               avar("class id mismatch for dest class %s.",
 637                  mLocalGhosts[index]->getClassName()) );
 638#endif
 639            // give derived classes a chance to prepare ghost for reading
 640            ghostPreRead(mLocalGhosts[index],false);
 641
 642#ifdef TORQUE_NET_STATS
 643            U32 beginSize = bstream->getBitPosition();
 644#endif
 645            mLocalGhosts[index]->unpackUpdate(this, bstream);
 646#ifdef TORQUE_NET_STATS
 647            mLocalGhosts[index]->getClassRep()->updateNetStatUnpack(bstream->getBitPosition() - beginSize);
 648#endif
 649            ghostReadExtra(mLocalGhosts[index],bstream,false);
 650         }
 651         //PacketStream::getStats()->addBits(PacketStats::Receive, bstream->getCurPos() - startPos, ghostRefs[index].localGhost->getPersistTag());
 652#ifdef TORQUE_DEBUG_NET
 653         U32 checksum = bstream->readInt(32);
 654         S32 origIndex = checksum ^ DebugChecksum;
 655         AssertISV(origIndex == index,
 656            avar("unpackUpdate did not match packUpdate for object of class %s.",
 657               mLocalGhosts[index]->getClassName()) );
 658#endif
 659         if(mErrorBuffer.isNotEmpty())
 660            return;
 661      }
 662   }
 663}
 664
 665//-----------------------------------------------------------------------------
 666
 667
 668//-----------------------------------------------------------------------------
 669
 670void NetConnection::setScopeObject(NetObject *obj)
 671{
 672   if(((NetObject *) mScopeObject) == obj)
 673      return;
 674   mScopeObject = obj;
 675}
 676
 677void NetConnection::detachObject(GhostInfo *info)
 678{
 679   // mark it for ghost killin'
 680   info->flags |= GhostInfo::KillGhost;
 681
 682   // if the mask is in the zero range, we've got to move it up...
 683   if(!info->updateMask)
 684   {
 685      info->updateMask = 0xFFFFFFFF;
 686      ghostPushNonZero(info);
 687   }
 688   if(info->obj)
 689   {
 690      if(info->prevObjectRef)
 691         info->prevObjectRef->nextObjectRef = info->nextObjectRef;
 692      else
 693         info->obj->mFirstObjectRef = info->nextObjectRef;
 694      if(info->nextObjectRef)
 695         info->nextObjectRef->prevObjectRef = info->prevObjectRef;
 696
 697      // remove it from the lookup table
 698      U32 id = info->obj->getId();
 699      for(GhostInfo **walk = &mGhostLookupTable[id & (GhostLookupTableSize - 1)]; *walk; walk = &((*walk)->nextLookupInfo))
 700      {
 701         GhostInfo *temp = *walk;
 702         if(temp == info)
 703         {
 704            *walk = temp->nextLookupInfo;
 705            break;
 706         }
 707      }
 708      info->prevObjectRef = info->nextObjectRef = NULL;
 709      info->obj = NULL;
 710   }
 711}
 712
 713void NetConnection::freeGhostInfo(GhostInfo *ghost)
 714{
 715   AssertFatal(ghost->arrayIndex < mGhostFreeIndex, "Ghost already freed.");
 716   if(ghost->arrayIndex < mGhostZeroUpdateIndex)
 717   {
 718      AssertFatal(ghost->updateMask != 0, "Invalid ghost mask.");
 719      ghost->updateMask = 0;
 720      ghostPushToZero(ghost);
 721   }
 722   ghostPushZeroToFree(ghost);
 723   AssertFatal(ghost->updateChain == NULL, "Ack!");
 724}
 725
 726//-----------------------------------------------------------------------------
 727
 728void NetConnection::objectLocalScopeAlways(NetObject *obj)
 729{
 730   if(!isGhostingFrom())
 731      return;
 732   objectInScope(obj);
 733   for(GhostInfo *walk = mGhostLookupTable[obj->getId() & (GhostLookupTableSize - 1)]; walk; walk = walk->nextLookupInfo)
 734   {
 735      if(walk->obj != obj)
 736         continue;
 737      walk->flags |= GhostInfo::ScopeLocalAlways;
 738      return;
 739   }
 740}
 741
 742void NetConnection::objectLocalClearAlways(NetObject *obj)
 743{
 744   if(!isGhostingFrom())
 745      return;
 746   for(GhostInfo *walk = mGhostLookupTable[obj->getId() & (GhostLookupTableSize - 1)]; walk; walk = walk->nextLookupInfo)
 747   {
 748      if(walk->obj != obj)
 749         continue;
 750      walk->flags &= ~<a href="/coding/class/structghostinfo/">GhostInfo</a>::ScopeLocalAlways;
 751      return;
 752   }
 753}
 754
 755bool NetConnection::validateGhostArray()
 756{
 757   AssertFatal(mGhostZeroUpdateIndex >= 0 && mGhostZeroUpdateIndex <= mGhostFreeIndex, "Invalid update index range.");
 758   AssertFatal(mGhostFreeIndex <= MaxGhostCount, "Invalid free index range.");
 759   U32 i;
 760   for(i = 0; i < mGhostZeroUpdateIndex; i ++)
 761   {
 762      AssertFatal(mGhostArray[i]->arrayIndex == i, "Invalid array index.");
 763      AssertFatal(mGhostArray[i]->updateMask != 0, "Invalid ghost mask.");
 764   }
 765   for(; i < mGhostFreeIndex; i ++)
 766   {
 767      AssertFatal(mGhostArray[i]->arrayIndex == i, "Invalid array index.");
 768      AssertFatal(mGhostArray[i]->updateMask == 0, "Invalid ghost mask.");
 769   }
 770   for(; i < MaxGhostCount; i++)
 771   {
 772      AssertFatal(mGhostArray[i]->arrayIndex == i, "Invalid array index.");
 773   }
 774   return true;
 775}
 776
 777void NetConnection::objectInScope(NetObject *obj)
 778{
 779   if (!mScoping || !isGhostingFrom())
 780      return;
 781   if (obj->isScopeLocal() && !isLocalConnection())
 782      return;
 783
 784   S32 index = obj->getId() & (GhostLookupTableSize - 1);
 785
 786   // check if it's already in scope
 787   // the object may have been cleared out without the lookupTable being cleared
 788   // so validate that the object pointers are the same.
 789
 790   for(GhostInfo *walk = mGhostLookupTable[index ]; walk; walk = walk->nextLookupInfo)
 791   {
 792      if(walk->obj != obj)
 793         continue;
 794      walk->flags |= GhostInfo::InScope;
 795
 796      // Make sure scope always if reflected on the ghostinfo too
 797      if (obj->mNetFlags.test(NetObject::ScopeAlways))
 798         walk->flags |= GhostInfo::ScopeAlways;
 799
 800      return;
 801   }
 802
 803   if (mGhostFreeIndex == MaxGhostCount)
 804   {
 805      AssertWarn(0,"NetConnection::objectInScope: too many ghosts");
 806      return;
 807   }
 808
 809   GhostInfo *giptr = mGhostArray[mGhostFreeIndex];
 810   ghostPushFreeToZero(giptr);
 811   giptr->updateMask = 0xFFFFFFFF;
 812   ghostPushNonZero(giptr);
 813
 814   giptr->flags = GhostInfo::NotYetGhosted | GhostInfo::InScope;
 815
 816   if(obj->mNetFlags.test(NetObject::ScopeAlways))
 817      giptr->flags |= GhostInfo::ScopeAlways;
 818
 819   giptr->obj = obj;
 820   giptr->updateChain = NULL;
 821   giptr->updateSkipCount = 0;
 822
 823   giptr->connection = this;
 824
 825   giptr->nextObjectRef = obj->mFirstObjectRef;
 826   if(obj->mFirstObjectRef)
 827      obj->mFirstObjectRef->prevObjectRef = giptr;
 828   giptr->prevObjectRef = NULL;
 829   obj->mFirstObjectRef = giptr;
 830
 831   giptr->nextLookupInfo = mGhostLookupTable[index];
 832   mGhostLookupTable[index] = giptr;
 833   //AssertFatal(validateGhostArray(), "Invalid ghost array!");
 834}
 835
 836//-----------------------------------------------------------------------------
 837
 838void NetConnection::handleConnectionMessage(U32 message, U32 sequence, U32 ghostCount)
 839{
 840   if((  message == SendNextDownloadRequest
 841      || message == FileDownloadSizeMessage
 842      || message == GhostAlwaysStarting
 843      || message == GhostAlwaysDone
 844      || message == EndGhosting) && !isGhostingTo())
 845   {
 846      setLastError("Invalid packet. (not ghosting)");
 847      return;
 848   }
 849
 850   S32 i;
 851   GhostSave sv;
 852   switch(message)
 853   {
 854      case GhostAlwaysDone:
 855         mGhostingSequence = sequence;
 856         NetConnection::smGhostAlwaysDone.trigger();
 857         // ok, all the ghost always objects are now on the client... but!
 858         // it's possible that there were some file load errors...
 859         // if so, we need to indicate to the server to restart ghosting, after
 860         // we download all the files...
 861         sv.ghost = NULL;
 862         sv.index = -1;
 863         mGhostAlwaysSaveList.push_back(sv);
 864         if(mGhostAlwaysSaveList.size() == 1)
 865            loadNextGhostAlwaysObject(true);
 866         break;
 867      case ReadyForNormalGhosts:
 868         if(sequence != mGhostingSequence)
 869            return;
 870         Con::executef(this, "onGhostAlwaysObjectsReceived");
 871         Con::printf("Ghost Always objects received.");
 872         mGhosting = true;
 873         for(i = 0; i < mGhostFreeIndex; i++)
 874         {
 875            if(mGhostArray[i]->flags & GhostInfo::ScopedEvent)
 876               mGhostArray[i]->flags &= ~(GhostInfo::Ghosting | GhostInfo::ScopedEvent);
 877         }
 878         break;
 879      case EndGhosting:
 880         onEndGhosting();
 881         // just delete all the local ghosts,
 882         // and delete all the ghosts in the current save list
 883         for(i = 0; i < MaxGhostCount; i++)
 884         {
 885            if(mLocalGhosts[i])
 886            {
 887               mLocalGhosts[i]->deleteObject();
 888               mLocalGhosts[i] = NULL;
 889            }
 890         }
 891         while(mGhostAlwaysSaveList.size())
 892         {
 893            delete mGhostAlwaysSaveList[0].ghost;
 894            mGhostAlwaysSaveList.pop_front();
 895         }
 896         break;
 897      case GhostAlwaysStarting:
 898         Con::executef("onGhostAlwaysStarted", Con::getIntArg(ghostCount));
 899         break;
 900      case SendNextDownloadRequest:
 901         sendNextFileDownloadRequest();
 902         break;
 903      case FileDownloadSizeMessage:
 904         mCurrentFileBufferSize = sequence;
 905         mCurrentFileBuffer = dRealloc(mCurrentFileBuffer, mCurrentFileBufferSize);
 906         mCurrentFileBufferOffset = 0;
 907         break;
 908   }
 909}
 910
 911void NetConnection::activateGhosting()
 912{
 913   if(!isGhostingFrom())
 914      return;
 915
 916   mGhostingSequence++;
 917
 918   // iterate through the ghost always objects and InScope them...
 919   // also post em all to the other side.
 920
 921   SimSet* ghostAlwaysSet = Sim::getGhostAlwaysSet();
 922
 923   SimSet::iterator i;
 924
 925   AssertFatal((mGhostFreeIndex == 0) && (mGhostZeroUpdateIndex == 0), "Error: ghosts in the ghost list before activate.");
 926
 927   U32 sz = ghostAlwaysSet->size();
 928   S32 j;
 929
 930   for(j = 0; j < sz; j++)
 931   {
 932      U32 idx = MaxGhostCount - sz + j;
 933      mGhostArray[j] = mGhostRefs + idx;
 934      mGhostArray[j]->arrayIndex = j;
 935   }
 936   for(j = sz; j < MaxGhostCount; j++)
 937   {
 938      U32 idx = j - sz;
 939      mGhostArray[j] = mGhostRefs + idx;
 940      mGhostArray[j]->arrayIndex = j;
 941   }
 942   mScoping = true; // so that objectInScope will work
 943   for(i = ghostAlwaysSet->begin(); i != ghostAlwaysSet->end(); i++)
 944   {
 945      AssertFatal(dynamic_cast<NetObject *>(*i) != NULL, avar("Non NetObject in GhostAlwaysSet: %s", (*i)->getClassName()));
 946      NetObject *obj = (NetObject *)(*i);
 947      if(obj->mNetFlags.test(NetObject::Ghostable))
 948         objectInScope(obj);
 949   }
 950    // Send the initial ghosting connection message.
 951    sendConnectionMessage(GhostAlwaysStarting, mGhostingSequence, ghostAlwaysSet->size());
 952
 953    // If this is the connection to the local client...
 954    if (getLocalClientConnection() == this)
 955    {
 956        // Get a pointer to the local client.
 957        NetConnection* pClient = NetConnection::getConnectionToServer();
 958
 959        Con::executef("onGhostAlwaysStarted", Con::getIntArg(mGhostZeroUpdateIndex));
 960
 961        // Set up a buffer for the object send.
 962        U8 iBuffer[4096];
 963        BitStream mStream(iBuffer, 4096);
 964
 965        // Iterate through the scope always objects...
 966        for (j = mGhostZeroUpdateIndex - 1; j >= 0; j--)
 967        {
 968            AssertFatal((mGhostArray[j]->flags & GhostInfo::ScopeAlways) != 0, "NetConnection::activateGhosting:  Non-scope always in the scope always list.");
 969
 970            // Clear the ghost update mask and flags appropriately.
 971            mGhostArray[j]->updateMask = 0;
 972            ghostPushToZero(mGhostArray[j]);
 973            mGhostArray[j]->flags &= ~<a href="/coding/class/structghostinfo/">GhostInfo</a>::NotYetGhosted;
 974            mGhostArray[j]->flags |= GhostInfo::ScopedEvent;
 975
 976            // Set up a pointer to the new object.
 977            NetObject* pObject = 0;
 978
 979            // If there's a valid ghost object...
 980            if (mGhostArray[j]->obj)
 981            {
 982                // Pack the server object's update.
 983                mStream.setPosition(0);
 984                mStream.clearCompressionPoint();
 985                U32 retMask = mGhostArray[j]->obj->packUpdate(this, 0xFFFFFFFF, &mStream);
 986                if ( retMask != 0 ) 
 987                   mGhostArray[j]->obj->setMaskBits( retMask );
 988
 989                // Create a new object instance for the client.
 990                pObject = (NetObject*)ConsoleObject::create(pClient->getNetClassGroup(), NetClassTypeObject, mGhostArray[j]->obj->getClassId(getNetClassGroup()));
 991
 992                // Set the client object networking flags.
 993                pObject->mNetFlags = NetObject::IsGhost;
 994                pObject->mNetIndex = mGhostArray[j]->index;
 995
 996                // Unpack the client object's update.
 997                mStream.setPosition(0);
 998                mStream.clearCompressionPoint();
 999                pObject->unpackUpdate(pClient, &mStream);
1000            }
1001            else
1002            {
1003                // Otherwise, create a new dummy netobject.
1004                pObject = new NetObject;
1005            }
1006
1007            // Execute the appropriate console callback.
1008            Con::executef("onGhostAlwaysObjectReceived");
1009
1010            // Set the ghost always object for the client.
1011            pClient->setGhostAlwaysObject(pObject, mGhostArray[j]->index);
1012        }
1013    }
1014    else
1015    {
1016        // Iterate through the scope always objects...
1017        for (j = mGhostZeroUpdateIndex - 1; j >= 0; j--)
1018        {
1019            AssertFatal((mGhostArray[j]->flags & GhostInfo::ScopeAlways) != 0, "NetConnection::activateGhosting:  Non-scope always in the scope always list.");
1020
1021            // Clear the ghost update mask and flags appropriately.
1022            mGhostArray[j]->updateMask = 0;
1023            ghostPushToZero(mGhostArray[j]);
1024            mGhostArray[j]->flags &= ~<a href="/coding/class/structghostinfo/">GhostInfo</a>::NotYetGhosted;
1025            mGhostArray[j]->flags |= GhostInfo::ScopedEvent;
1026
1027            // Post a network event to ghost the scope always object.
1028            postNetEvent(new GhostAlwaysObjectEvent(mGhostArray[j]->obj, mGhostArray[j]->index));
1029        }
1030    }
1031
1032    // Send the ghosting always done message.
1033    sendConnectionMessage(GhostAlwaysDone, mGhostingSequence);   //AssertFatal(validateGhostArray(), "Invalid ghost array!");
1034}
1035
1036void NetConnection::clearGhostInfo()
1037{
1038   // gotta clear out the ghosts...
1039   for(PacketNotify *walk = mNotifyQueueHead; walk; walk = walk->nextPacket)
1040   {
1041      ghostPacketReceived(walk);
1042      walk->ghostList = NULL;
1043   }
1044   for(S32 i = 0; i < MaxGhostCount; i++)
1045   {
1046      if(mGhostRefs[i].arrayIndex < mGhostFreeIndex)
1047      {
1048         detachObject(&mGhostRefs[i]);
1049         freeGhostInfo(&mGhostRefs[i]);
1050      }
1051   }
1052   AssertFatal((mGhostFreeIndex == 0) && (mGhostZeroUpdateIndex == 0), "Invalid indices.");
1053}
1054
1055void NetConnection::resetGhosting()
1056{
1057   if(!isGhostingFrom())
1058      return;
1059   // stop all ghosting activity
1060   // send a message to the other side notifying of this
1061
1062   mGhosting = false;
1063   mScoping = false;
1064   sendConnectionMessage(EndGhosting, mGhostingSequence);
1065   mGhostingSequence++;
1066   clearGhostInfo();
1067   //AssertFatal(validateGhostArray(), "Invalid ghost array!");
1068}
1069
1070void NetConnection::setGhostAlwaysObject(NetObject *object, U32 index)
1071{
1072   if(!isGhostingTo())
1073   {
1074      object->deleteObject();
1075      setLastError("Invalid packet. (unexpected ghostalways)");
1076      return;
1077   }
1078   object->mNetFlags = NetObject::IsGhost;
1079   object->mNetIndex = index;
1080
1081   // while there's an object waiting...
1082   if ( isLocalConnection() ) 
1083   {
1084      object->mServerObject = mRemoteConnection->resolveObjectFromGhostIndex(index);
1085      if ( object->mServerObject )
1086         object->mServerObject->mClientObject = object;
1087   }
1088
1089   GhostSave sv;
1090   sv.ghost = object;
1091   sv.index = index;
1092   mGhostAlwaysSaveList.push_back(sv);
1093
1094   // check if we are already downloading files for a previous object:
1095   if(mGhostAlwaysSaveList.size() == 1)
1096      loadNextGhostAlwaysObject(true);  // the initial call always has "new" files
1097
1098}
1099
1100void NetConnection::fileDownloadSegmentComplete()
1101{
1102   // this is called when a the file list has finished processing...
1103   // at this point we can try again to add the object
1104   // subclasses can override this to do, for example, datablock redos.
1105   if(mGhostAlwaysSaveList.size())
1106      loadNextGhostAlwaysObject(mNumDownloadedFiles != 0);
1107}
1108
1109void NetConnection::loadNextGhostAlwaysObject(bool hadNewFiles)
1110{
1111   if(!mGhostAlwaysSaveList.size())
1112      return;
1113
1114   while(mGhostAlwaysSaveList.size())
1115   {
1116
1117      if (isLocalConnection()) hadNewFiles = false;
1118
1119      // only check for new files if this is the first load, or if new
1120      // files were downloaded from the server.
1121//       if(hadNewFiles)
1122//          gResourceManager->setMissingFileLogging(true);
1123// 
1124//       gResourceManager->clearMissingFileList();
1125      NetObject *object = mGhostAlwaysSaveList[0].ghost;
1126      U32 index = mGhostAlwaysSaveList[0].index;
1127
1128      if(!object)
1129      {
1130         // a null object is used to signify that the last ghost in the list is down
1131         mGhostAlwaysSaveList.pop_front();
1132         AssertFatal(mGhostAlwaysSaveList.size() == 0, "Error! Ghost save list should be empty!");
1133         sendConnectionMessage(ReadyForNormalGhosts, mGhostingSequence);
1134//          gResourceManager->setMissingFileLogging(false);
1135         return;
1136      }
1137      mFilesWereDownloaded = hadNewFiles;
1138
1139      if(!object->registerObject())
1140      {
1141         mFilesWereDownloaded = false;
1142         // make sure there's an error message if necessary
1143         if(mErrorBuffer.isEmpty())
1144            setLastError("Invalid packet. (failed to register ghost always)");
1145
1146         // if there were no new files, make sure the error message
1147         // is the one from the last time we tried to add this object
1148         if(!hadNewFiles)
1149         {
1150            mErrorBuffer = mLastFileErrorBuffer;
1151//             gResourceManager->setMissingFileLogging(false);
1152            return;
1153         }
1154
1155         // object failed to load, let's see if it had any missing files
1156//          if(!gResourceManager->getMissingFileList(mMissingFileList))
1157//          {
1158//             // no missing files, must be an error
1159//             // connection will automagically delete the ghost always list
1160//             // when this error is reported.
1161//             gResourceManager->setMissingFileLogging(false);
1162//             return;
1163//          }
1164
1165         // ok, copy the error buffer out to a scratch pad for now
1166         mLastFileErrorBuffer = mErrorBuffer;
1167         mErrorBuffer = String();
1168
1169         // request the missing files...
1170         mNumDownloadedFiles = 0;
1171         sendNextFileDownloadRequest();
1172         break;
1173      }
1174      mFilesWereDownloaded = false;
1175//       gResourceManager->setMissingFileLogging(false);
1176      addObject(object);
1177      mGhostAlwaysSaveList.pop_front();
1178
1179      AssertFatal(mLocalGhosts[index] == NULL, "Ghost already in table!");
1180      mLocalGhosts[index] = object;
1181      hadNewFiles = true;
1182   }
1183}
1184
1185//-----------------------------------------------------------------------------
1186
1187NetObject *NetConnection::resolveGhost(S32 id)
1188{
1189   return mLocalGhosts[id];
1190}
1191
1192NetObject *NetConnection::resolveObjectFromGhostIndex(S32 id)
1193{
1194   return mGhostRefs[id].obj;
1195}
1196
1197S32 NetConnection::getGhostIndex(NetObject *obj)
1198{
1199   if(!isGhostingFrom())
1200      return obj->mNetIndex;
1201   S32 index = obj->getId() & (GhostLookupTableSize - 1);
1202
1203   for(GhostInfo *gptr = mGhostLookupTable[index]; gptr; gptr = gptr->nextLookupInfo)
1204   {
1205      if(gptr->obj == obj && (gptr->flags & (GhostInfo::KillingGhost | GhostInfo::Ghosting | GhostInfo::NotYetGhosted | GhostInfo::KillGhost)) == 0)
1206            return gptr->index;
1207   }
1208   return -1;
1209}
1210
1211//-----------------------------------------------------------------------------
1212
1213//-----------------------------------------------------------------------------
1214
1215void NetConnection::ghostWriteStartBlock(ResizeBitStream *stream)
1216{
1217   // Ok, writing the start block for the ghosts:
1218   // here's how it goes.
1219   //
1220   // First we record out all the indices and class ids for all the objects
1221   // This is so when the objects are read in, all the objects are instantiated
1222   // before they are unpacked.  The unpack code may reference other
1223   // existing ghosts, so we want to make sure that all the ghosts are in the
1224   // table with the correct pointers before any of the unpacks are called.
1225
1226   stream->write(mGhostingSequence);
1227
1228   // first write out the indices and ids:
1229   for(U32 i = 0; i < MaxGhostCount; i++)
1230   {
1231      if(mLocalGhosts[i])
1232      {
1233         stream->writeFlag(true);
1234         stream->writeInt(i, GhostIdBitSize);
1235         stream->writeClassId(mLocalGhosts[i]->getClassId(getNetClassGroup()), NetClassTypeObject, getNetClassGroup());
1236         stream->validate();
1237      }
1238   }
1239   // mark off the end of the ghost list:
1240   // it would be more space efficient to write out a count of active ghosts followed
1241   // by index run lengths, but hey, what's a few bits here and there?
1242
1243   stream->writeFlag(false);
1244
1245   // then, for each ghost written into the start block, write the full pack update
1246   // into the start block.  For demos to work properly, packUpdate must
1247   // be callable from client objects.
1248   for(U32 i = 0; i < MaxGhostCount; i++)
1249   {
1250      if(mLocalGhosts[i])
1251      {
1252         U32 retMask = mLocalGhosts[i]->packUpdate(this, 0xFFFFFFFF, stream);
1253         if ( retMask != 0 ) mLocalGhosts[i]->setMaskBits( retMask );
1254         stream->validate();
1255      }
1256   }
1257}
1258
1259void NetConnection::ghostReadStartBlock(BitStream *stream)
1260{
1261   stream->read(&mGhostingSequence);
1262
1263   // read em back in.
1264   // first, read in the index/class id, construct the object, and place it in mLocalGhosts[i]
1265
1266   while(stream->readFlag())
1267   {
1268      U32 index = stream->readInt(GhostIdBitSize);
1269      S32 tag = stream->readClassId(NetClassTypeObject, getNetClassGroup());
1270      NetObject *obj = (NetObject *) ConsoleObject::create(getNetClassGroup(), NetClassTypeObject, tag);
1271      if(!obj)
1272      {
1273         setLastError("Invalid packet. (failed to create ghost from demo block)");
1274         return;
1275      }
1276      obj->mNetFlags = NetObject::IsGhost;
1277      obj->mNetIndex = index;
1278      mLocalGhosts[index] = obj;
1279   }
1280
1281   // now, all the ghosts are in the mLocalGhosts, so we loop
1282   // through all non-null mLocalGhosts, unpacking the objects
1283   // as we go:
1284
1285   for(U32 i = 0; i < MaxGhostCount; i++)
1286   {
1287      if(mLocalGhosts[i])
1288      {
1289         mLocalGhosts[i]->unpackUpdate(this, stream);
1290         if(!mLocalGhosts[i]->registerObject())
1291         {
1292            if(mErrorBuffer.isEmpty())
1293               setLastError("Invalid packet. (failed to register ghost from demo block)");
1294            return;
1295         }
1296         addObject(mLocalGhosts[i]);
1297      }
1298   }
1299   // MARKF - TODO - looks like we could have memory leaks here
1300   // if there are errors.
1301}
1302