bool EventConnection::readConnectAccept(BitStream *stream, NetConnection::TerminationReason &reason) { if(!Parent::readConnectAccept(stream, reason)) return false; stream->read(&mEventClassCount); // Number of RPCs the remote server is willing to support U32 myCount = NetClassRep::getNetClassCount(getNetClassGroup(), NetClassTypeEvent); // Number we, the client, support if(mEventClassCount > myCount) // Normally, these should be equal. If the server is not willing to support { // as many RPCs as we want to use, then bail. logprintf(LogConsumer::LogConnection, "Connection to master failed due to a disagreement on the number of RPCs supported."); return false; } if(!NetClassRep::isVersionBorderCount(getNetClassGroup(), NetClassTypeEvent, mEventClassCount)) { logprintf(LogConsumer::LogConnection, "Connection to master failed due to incompatible versions."); return false; } mEventClassBitSize = getNextBinLog2(mEventClassCount); clearSendEvents(); clearRecvEvents(); return true; }
// Reads the NetEvent class count max that the remote host is requesting. // If this host has MORE NetEvent classes declared, the mEventClassCount // is set to the requested count, and is verified to lie on a boundary between versions. // This gets run when someone is connecting to us bool EventConnection::readConnectRequest(BitStream *stream, NetConnection::TerminationReason &reason) { if(!Parent::readConnectRequest(stream, reason)) return false; U32 remoteClassCount; stream->read(&remoteClassCount); U32 localClassCount = NetClassRep::getNetClassCount(getNetClassGroup(), NetClassTypeEvent); // Essentially a count of RPCs // If remote client has more classes defined than we do, hope/assume they're defined in the same order, so that we at least agree // on the available set of RPCs. // This implies the client is higher version than the server if(localClassCount <= remoteClassCount) mEventClassCount = localClassCount; // We're only willing to support as many as we have else // We have more RPCs on the local machine ==> implies server is higher version than client { mEventClassCount = remoteClassCount; // We're willing to support the number of classes the client has // Check if the next RPC is a higher version than the current one specified by mEventClassCount if(!NetClassRep::isVersionBorderCount(getNetClassGroup(), NetClassTypeEvent, mEventClassCount)) { reason = ReasonIncompatibleRPCCounts; return false; // If not, abort connection } } mEventClassVersion = NetClassRep::getClass(getNetClassGroup(), NetClassTypeEvent, mEventClassCount-1)->getClassVersion(); mEventClassBitSize = getNextBinLog2(mEventClassCount); clearSendEvents(); clearRecvEvents(); return true; }
NetEvent *EventConnection::unpackNetEvent(BitStream *bstream) { U32 endingPosition; if(mConnectionParameters.mDebugObjectSizes) endingPosition = bstream->readInt(BitStreamPosBitSize); U32 classId = bstream->readInt(mEventClassBitSize); if(classId >= mEventClassCount) { setLastError("Invalid packet -- classId too high."); return NULL; } NetEvent *evt = (NetEvent *) Object::create(getNetClassGroup(), NetClassTypeEvent, classId); if(!evt) { setLastError("Invalid packet -- couldn't create event."); return NULL; } // Check if the direction this event moves is a valid direction. if( (evt->getEventDirection() == NetEvent::DirUnset) || (evt->getEventDirection() == NetEvent::DirServerToClient && isConnectionToClient()) || (evt->getEventDirection() == NetEvent::DirClientToServer && isConnectionToServer()) ) { setLastError("Invalid Packet -- event direction wrong. %s", evt->getClassName()); delete evt; return NULL; } evt->unpack(this, bstream); if(mErrorBuffer[0]) { delete evt; return NULL; } if(mConnectionParameters.mDebugObjectSizes) { TNLAssert(((endingPosition - bstream->getBitPosition()) & ~(~0 << BitStreamPosBitSize)) == 0, avar("Unpack did not match pack for event of class %s.", evt->getClassName()) ); } return evt; }
void GhostConnection::readPacket(BitStream *bstream) { Parent::readPacket(bstream); if(mConnectionParameters.mDebugObjectSizes) { U32 sum = bstream->readInt(32); TNLAssert(sum == DebugChecksum, "Invalid checksum."); } if(!doesGhostTo()) return; if(!bstream->readFlag()) return; S32 idSize; idSize = bstream->readInt( 3 ); idSize += 3; // while there's an object waiting... while(bstream->readFlag()) { U32 index; //S32 startPos = bstream->getCurPos(); index = (U32) bstream->readInt(idSize); if(bstream->readFlag()) // is this ghost being deleted? { TNLAssert(mLocalGhosts[index] != NULL, "Error, NULL ghost encountered."); if(mLocalGhosts[index]) { mLocalGhosts[index]->onGhostRemove(); delete mLocalGhosts[index]; mLocalGhosts[index] = NULL; } } else { U32 endPosition = 0; if(mConnectionParameters.mDebugObjectSizes) endPosition = bstream->readInt(BitStreamPosBitSize); if(!mLocalGhosts[index]) // it's a new ghost... cool { S32 classId = bstream->readClassId(NetClassTypeObject, getNetClassGroup()); if(classId == -1) { setLastError("Invalid packet."); return; } NetObject *obj = (NetObject *) Object::create(getNetClassGroup(), NetClassTypeObject, classId); if(!obj) { setLastError("Invalid packet."); return; } obj->mOwningConnection = this; obj->mNetFlags = NetObject::IsGhost; // object gets initial update before adding to the manager obj->mNetIndex = index; mLocalGhosts[index] = obj; NetObject::mIsInitialUpdate = true; mLocalGhosts[index]->unpackUpdate(this, bstream); NetObject::mIsInitialUpdate = false; if(!obj->onGhostAdd(this)) { if(!mErrorBuffer[0]) setLastError("Invalid packet."); return; } if(mRemoteConnection) { GhostConnection *gc = static_cast<GhostConnection *>(mRemoteConnection.getPointer()); obj->mServerObject = gc->resolveGhostParent(index); } } else { mLocalGhosts[index]->unpackUpdate(this, bstream); } if(mConnectionParameters.mDebugObjectSizes) { TNLAssert(bstream->getBitPosition() == endPosition, avar("unpackUpdate did not match packUpdate for object of class %s. Expected %d bits, got %d bits.", mLocalGhosts[index]->getClassName(), endPosition, bstream->getBitPosition()) ); } if(mErrorBuffer[0]) return; } } }
void GhostConnection::writePacket(BitStream *bstream, PacketNotify *pnotify) { Parent::writePacket(bstream, pnotify); GhostPacketNotify *notify = static_cast<GhostPacketNotify *>(pnotify); if(mConnectionParameters.mDebugObjectSizes) bstream->writeInt(DebugChecksum, 32); notify->ghostList = NULL; if(!doesGhostFrom()) return; if(!bstream->writeFlag(mGhosting && mScopeObject.isValid())) return; // fill a packet (or two) with ghosting data // 2. call scoped objects' priority functions if the flag set is nonzero // A removed ghost is assumed to have a high priority // 3. call updates based on sorted priority until the packet is // full. set flags to zero for all updated objects GhostInfo *walk; for(S32 i = mGhostZeroUpdateIndex - 1; i >= 0; i--) { if(!(mGhostArray[i]->flags & GhostInfo::InScope)) detachObject(mGhostArray[i]); } U32 maxIndex = 0; for(S32 i = mGhostZeroUpdateIndex - 1; i >= 0; i--) { walk = mGhostArray[i]; if(walk->index > maxIndex) maxIndex = walk->index; // clear out any kill objects that haven't been ghosted yet if((walk->flags & GhostInfo::KillGhost) && (walk->flags & GhostInfo::NotYetGhosted)) { freeGhostInfo(walk); continue; } // don't do any ghost processing on objects that are being killed // or in the process of ghosting else if(!(walk->flags & (GhostInfo::KillingGhost | GhostInfo::Ghosting))) { if(walk->flags & GhostInfo::KillGhost) walk->priority = 10000; else walk->priority = walk->obj->getUpdatePriority(mScopeObject, walk->updateMask, walk->updateSkipCount); } else walk->priority = 0; } GhostRef *updateList = NULL; qsort(mGhostArray, mGhostZeroUpdateIndex, sizeof(GhostInfo *), UQECompare); // reset the array indices... for(S32 i = mGhostZeroUpdateIndex - 1; i >= 0; i--) mGhostArray[i]->arrayIndex = i; S32 sendSize = 1; while(maxIndex >>= 1) sendSize++; if(sendSize < 3) sendSize = 3; bstream->writeInt(sendSize - 3, 3); // 0-7 3 bit number U32 count = 0; // for(S32 i = mGhostZeroUpdateIndex - 1; i >= 0 && !bstream->isFull(); i--) { GhostInfo *walk = mGhostArray[i]; if(walk->flags & (GhostInfo::KillingGhost | GhostInfo::Ghosting)) continue; size_t updateStart = bstream->getBitPosition(); U32 updateMask = walk->updateMask; U32 retMask = 0; bstream->writeFlag(true); bstream->writeInt(walk->index, sendSize); if(!bstream->writeFlag(walk->flags & GhostInfo::KillGhost)) { // this is an update of some kind: if(mConnectionParameters.mDebugObjectSizes) bstream->advanceBitPosition(BitStreamPosBitSize); size_t startPos = bstream->getBitPosition(); if(walk->flags & GhostInfo::NotYetGhosted) { S32 classId = walk->obj->getClassId(getNetClassGroup()); bstream->writeClassId(classId, NetClassTypeObject, getNetClassGroup()); NetObject::mIsInitialUpdate = true; } // update the object retMask = walk->obj->packUpdate(this, updateMask, bstream); if(NetObject::mIsInitialUpdate) { NetObject::mIsInitialUpdate = false; walk->obj->getClassRep()->addInitialUpdate(bstream->getBitPosition() - startPos); } else walk->obj->getClassRep()->addPartialUpdate(bstream->getBitPosition() - startPos); if(mConnectionParameters.mDebugObjectSizes){ size_t bpL = bstream->getBitPosition(); U32 bp = static_cast<U32>(bpL); TNLAssert(bp == bpL, "This stream position is too long"); bstream->writeIntAt(bp, BitStreamPosBitSize, startPos - BitStreamPosBitSize); } TNLLogMessageV(LogGhostConnection, ("GhostConnection %s GHOST %d", walk->obj->getClassName(), bstream->getBitPosition() - 16 - startPos)); TNLAssert((retMask & (~updateMask)) == 0, "Cannot set new bits in packUpdate return"); } // check for packet overrun, and rewind this update if there // was one: if(bstream->getBitSpaceAvailable() < MinimumPaddingBits) { bstream->setBitPosition(updateStart); bstream->clearError(); break; } // otherwise, create a record of this ghost update and // attach it to the packet. GhostRef *upd = new GhostRef; upd->nextRef = updateList; updateList = upd; if(walk->lastUpdateChain) walk->lastUpdateChain->updateChain = upd; walk->lastUpdateChain = upd; upd->ghost = walk; upd->ghostInfoFlags = 0; upd->updateChain = NULL; if(walk->flags & GhostInfo::KillGhost) { walk->flags &= ~GhostInfo::KillGhost; walk->flags |= GhostInfo::KillingGhost; walk->updateMask = 0; upd->mask = updateMask; ghostPushToZero(walk); upd->ghostInfoFlags = GhostInfo::KillingGhost; } else { if(walk->flags & GhostInfo::NotYetGhosted) { walk->flags &= ~GhostInfo::NotYetGhosted; walk->flags |= GhostInfo::Ghosting; upd->ghostInfoFlags = GhostInfo::Ghosting; } walk->updateMask = retMask; if(!retMask) ghostPushToZero(walk); upd->mask = updateMask & ~retMask; walk->updateSkipCount = 0; count++; } } // count # of ghosts have been updated, // mGhostZeroUpdateIndex # of ghosts remain to be updated. // no more objects... bstream->writeFlag(false); notify->ghostList = updateList; }
bool EventConnection::postNetEvent(NetEvent *theEvent) { // Check if the direction this event moves is a valid direction. TNLAssertV( (theEvent->getEventDirection() != NetEvent::DirUnset) && (theEvent->getEventDirection() != NetEvent::DirServerToClient || !isConnectionToServer()) && (theEvent->getEventDirection() != NetEvent::DirClientToServer || !isConnectionToClient()), ("Trying to send wrong event direction in %s", theEvent->getClassName())); S32 classId = theEvent->getClassId(getNetClassGroup()); if(U32(classId) >= mEventClassCount && getConnectionState() == Connected) { theEvent->incRef(); theEvent->decRef(); // Avoids some type of memory leak by deleting here if nothing reference it. return false; } theEvent->notifyPosted(this); EventNote *event = mEventNoteChunker.alloc(); event->mEvent = theEvent; event->mNextEvent = NULL; if(event->mEvent->mGuaranteeType == NetEvent::GuaranteedOrdered) { event->mSeqCount = mNextSendEventSeq++; if(!mSendEventQueueHead) mSendEventQueueHead = event; else mSendEventQueueTail->mNextEvent = event; mSendEventQueueTail = event; } else if(event->mEvent->mGuaranteeType == NetEvent::GuaranteedOrderedBigData) { BitStream bstream; const U32 start = 0; const U32 partsSize = 512; if(mConnectionParameters.mDebugObjectSizes) bstream.advanceBitPosition(BitStreamPosBitSize); S32 classId = event->mEvent->getClassId(getNetClassGroup()); bstream.writeInt(classId, mEventClassBitSize); event->mEvent->pack(this, &bstream); logprintf(LogConsumer::LogEventConnection, "EventConnection %s: WroteEvent %s - %d bits", getNetAddressString(), event->mEvent->getDebugName(), bstream.getBitPosition() - start); if(mConnectionParameters.mDebugObjectSizes) bstream.writeIntAt(bstream.getBitPosition(), BitStreamPosBitSize, start); U32 size = bstream.getBytePosition(); for(U32 i=0; i<size; i+=partsSize) { if(i+partsSize < size) { ByteBuffer *bytebuffer = new ByteBuffer(&bstream.getBuffer()[i], partsSize); bytebuffer->takeOwnership(); // may have to use this to prevent errors. s2rTNLSendDataParts(0, ByteBufferPtr(bytebuffer)); } else { ByteBuffer *bytebuffer = new ByteBuffer(&bstream.getBuffer()[i], size-i); bytebuffer->takeOwnership(); s2rTNLSendDataParts(1, ByteBufferPtr(bytebuffer)); } } mEventNoteChunker.free(event); } else { event->mSeqCount = InvalidSendEventSeq; if(!mUnorderedSendEventQueueHead) mUnorderedSendEventQueueHead = event; else mUnorderedSendEventQueueTail->mNextEvent = event; mUnorderedSendEventQueueTail = event; } return true; }
void EventConnection::writePacket(BitStream *bstream, PacketNotify *pnotify) { Parent::writePacket(bstream, pnotify); EventPacketNotify *notify = static_cast<EventPacketNotify *>(pnotify); bool have_something_to_send = bstream->getBitPosition() >= 128; if(mConnectionParameters.mDebugObjectSizes) bstream->writeInt(DebugChecksum, 32); EventNote *packQueueHead = NULL, *packQueueTail = NULL; while(mUnorderedSendEventQueueHead) { if(bstream->isFull()) break; // get the first event EventNote *ev = mUnorderedSendEventQueueHead; ConnectionStringTable::PacketEntry *strEntry = getCurrentWritePacketNotify()->stringList.stringTail; bstream->writeFlag(true); S32 start = bstream->getBitPosition(); if(mConnectionParameters.mDebugObjectSizes) bstream->advanceBitPosition(BitStreamPosBitSize); S32 classId = ev->mEvent->getClassId(getNetClassGroup()); bstream->writeInt(classId, mEventClassBitSize); ev->mEvent->pack(this, bstream); logprintf(LogConsumer::LogEventConnection, "EventConnection %s: WroteEvent %s - %d bits", getNetAddressString(), ev->mEvent->getDebugName(), bstream->getBitPosition() - start); if(mConnectionParameters.mDebugObjectSizes) bstream->writeIntAt(bstream->getBitPosition(), BitStreamPosBitSize, start); // check for packet overrun, and rewind this update if there // was one: if(!bstream->isValid() || bstream->getBitPosition() >= mWriteMaxBitSize) { mStringTable->packetRewind(&getCurrentWritePacketNotify()->stringList, strEntry); // we never sent those stuff (TableStringEntry), so let it drop TNLAssert(have_something_to_send || bstream->getBitPosition() < mWriteMaxBitSize, "Packet too big to send"); if(have_something_to_send) { bstream->setBitPosition(start - 1); bstream->clearError(); break; } else //if(bstream->getBitPosition() < MaxPacketDataSize*8 - MinimumPaddingBits) { TNLAssertV(false, ("%s Packet too big to send, one or more events may be unable to send", ev->mEvent->getDebugName())); // dequeue the event: mUnorderedSendEventQueueHead = ev->mNextEvent; ev->mNextEvent = NULL; ev->mEvent->notifyDelivered(this, false); mEventNoteChunker.free(ev); bstream->setBitPosition(start - 1); bstream->clearError(); break; } } have_something_to_send = true; // dequeue the event and add this event onto the packet queue mUnorderedSendEventQueueHead = ev->mNextEvent; ev->mNextEvent = NULL; if(!packQueueHead) packQueueHead = ev; else packQueueTail->mNextEvent = ev; packQueueTail = ev; } bstream->writeFlag(false); S32 prevSeq = -2; while(mSendEventQueueHead) { if(bstream->isFull()) break; // if the event window is full, stop processing if(mSendEventQueueHead->mSeqCount > mLastAckedEventSeq + 126) break; // get the first event EventNote *ev = mSendEventQueueHead; S32 eventStart = bstream->getBitPosition(); ConnectionStringTable::PacketEntry *strEntry = getCurrentWritePacketNotify()->stringList.stringTail; bstream->writeFlag(true); if(!bstream->writeFlag(ev->mSeqCount == prevSeq + 1)) bstream->writeInt(ev->mSeqCount, 7); prevSeq = ev->mSeqCount; if(mConnectionParameters.mDebugObjectSizes) bstream->advanceBitPosition(BitStreamPosBitSize); S32 start = bstream->getBitPosition(); S32 classId = ev->mEvent->getClassId(getNetClassGroup()); bstream->writeInt(classId, mEventClassBitSize); ev->mEvent->pack(this, bstream); ev->mEvent->getClassRep()->addInitialUpdate(bstream->getBitPosition() - start); logprintf(LogConsumer::LogEventConnection, "EventConnection %s: WroteEvent %s - %d bits", getNetAddressString(), ev->mEvent->getDebugName(), bstream->getBitPosition() - start); if(mConnectionParameters.mDebugObjectSizes) bstream->writeIntAt(bstream->getBitPosition(), BitStreamPosBitSize, start - BitStreamPosBitSize); // check for packet overrun, and rewind this update if there // was one: if(!bstream->isValid() || bstream->getBitPosition() >= mWriteMaxBitSize) { mStringTable->packetRewind(&getCurrentWritePacketNotify()->stringList, strEntry); // we never sent those stuff (TableStringEntry), so let it drop if(have_something_to_send) { bstream->setBitPosition(eventStart); bstream->clearError(); break; } else { TNLAssertV(false, ("%s Packet too big to send, one or more events may be unable to send", ev->mEvent->getDebugName())); for(EventNote *walk = ev->mNextEvent; walk; walk = walk->mNextEvent) walk->mSeqCount--; // removing a GuaranteedOrdered needs to re-order mSeqCount mNextSendEventSeq--; // dequeue the event: mSendEventQueueHead = ev->mNextEvent; ev->mNextEvent = NULL; ev->mEvent->notifyDelivered(this, false); mEventNoteChunker.free(ev); bstream->setBitPosition(eventStart); bstream->clearError(); break; } } have_something_to_send = true; // dequeue the event: mSendEventQueueHead = ev->mNextEvent; ev->mNextEvent = NULL; if(!packQueueHead) packQueueHead = ev; else packQueueTail->mNextEvent = ev; packQueueTail = ev; } for(EventNote *ev = packQueueHead; ev; ev = ev->mNextEvent) ev->mEvent->notifySent(this); notify->eventList = packQueueHead; bstream->writeFlag(0); }
void EventConnection::writeConnectRequest(BitStream *stream) { Parent::writeConnectRequest(stream); stream->write(NetClassRep::getNetClassCount(getNetClassGroup(), NetClassTypeEvent)); // Essentially a count of RPCs (c2s, s2c, etc.) }