netGhost.cpp
Engine/source/sim/netGhost.cpp
Classes:
class
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>" )
UQECompare(const void * a, const void * b)
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