void ReadyEvent::SendReadyStateQuery(unsigned eventId, SystemAddress address) { RakNet::BitStream bs; bs.Write((MessageID)ID_READY_EVENT_QUERY); bs.Write(eventId); SendUnified(&bs, HIGH_PRIORITY, RELIABLE_ORDERED, channel, address, false); }
void ConnectionGraph::OnConnectionGraphRequest(Packet *packet) { char password[256]; RakNet::BitStream inBitstream(packet->data, packet->length, false); inBitstream.IgnoreBits(8); stringCompressor->DecodeString(password,256,&inBitstream); if (pw && pw[0] && strcmp(pw, password)!=0) return; #ifdef _CONNECTION_GRAPH_DEBUG_PRINT RAKNET_DEBUG_PRINTF("ID_CONNECTION_GRAPH_REPLY "); #endif RakNet::BitStream outBitstream; outBitstream.Write((MessageID)ID_CONNECTION_GRAPH_REPLY); stringCompressor->EncodeString(pw,256,&outBitstream); SerializeWeightedGraph(&outBitstream, graph); SendUnified(&outBitstream, LOW_PRIORITY, RELIABLE_ORDERED, connectionGraphChannel, packet->systemAddress, false); #ifdef _CONNECTION_GRAPH_DEBUG_PRINT RAKNET_DEBUG_PRINTF("from %i to %i\n", peer->GetInternalID().port, packet->systemAddress.port); #endif // Add packet->systemAddress to the participant list if it is not already there AddParticipant(packet->systemAddress); }
void ConnectionGraph2::OnNewConnection(SystemAddress systemAddress, RakNetGUID rakNetGUID, bool isIncoming) { (void) isIncoming; // Send all existing systems to new connection RakNet::BitStream bs; bs.Write((MessageID)ID_REMOTE_NEW_INCOMING_CONNECTION); bs.Write((unsigned int)1); bs.Write(systemAddress); bs.Write(rakNetGUID); SendUnified(&bs,HIGH_PRIORITY,RELIABLE_ORDERED,0,systemAddress,true); // Send everyone to the new guy DataStructures::List<SystemAddress> addresses; DataStructures::List<RakNetGUID> guids; rakPeerInterface->GetSystemList(addresses, guids); bs.Reset(); bs.Write((MessageID)ID_REMOTE_NEW_INCOMING_CONNECTION); BitSize_t writeOffset = bs.GetWriteOffset(); bs.Write((unsigned int) addresses.Size()); unsigned int i; unsigned int count=0; for (i=0; i < addresses.Size(); i++) { if (addresses[i]==systemAddress) continue; bs.Write(addresses[i]); bs.Write(guids[i]); count++; } if (count>0) { BitSize_t writeOffset2 = bs.GetWriteOffset(); bs.SetWriteOffset(writeOffset); bs.Write(count); bs.SetWriteOffset(writeOffset2); SendUnified(&bs,HIGH_PRIORITY,RELIABLE_ORDERED,0,systemAddress,false); } RemoteSystem* remoteSystem = RakNet::OP_NEW<RemoteSystem>(__FILE__,__LINE__); remoteSystem->guid=rakNetGUID; remoteSystems.Insert(rakNetGUID,remoteSystem,true,__FILE__,__LINE__); }
void RakVoice::RequestVoiceChannel(RakNetGUID recipient) { // Send a reliable ordered message to the other system to open a voice channel RakNet::BitStream out; out.Write((unsigned char)ID_RAKVOICE_OPEN_CHANNEL_REQUEST); out.Write((int32_t)sampleRate); SendUnified(&out, HIGH_PRIORITY, RELIABLE_ORDERED,0,recipient,false); }
void RakVoice::CloseVoiceChannel(RakNetGUID recipient) { FreeChannelMemory(recipient); // Send a message to the remote system telling them to close the channel RakNet::BitStream out; out.Write((unsigned char)ID_RAKVOICE_CLOSE_CHANNEL); SendUnified(&out, HIGH_PRIORITY, RELIABLE_ORDERED,0,recipient,false); }
void ReadyEvent::SendReadyUpdate(unsigned eventIndex, unsigned systemIndex, bool forceIfNotDefault) { ReadyEventNode *ren = readyEventNodeList[eventIndex]; RakNet::BitStream bs; // I do this rather than write true or false, so users that do not use BitStreams can still read the data if ((ren->eventStatus != ren->systemList[systemIndex].lastSentStatus) || (forceIfNotDefault && ren->eventStatus != ID_READY_EVENT_UNSET)) { bs.Write(ren->eventStatus); bs.Write(ren->eventId); SendUnified(&bs, HIGH_PRIORITY, RELIABLE_ORDERED, channel, ren->systemList[systemIndex].systemAddress, false); ren->systemList[systemIndex].lastSentStatus = ren->eventStatus; } }
void Router::SendTree(PacketPriority priority, PacketReliability reliability, char orderingChannel, DataStructures::Tree<ConnectionGraph::SystemAddressAndGroupId> *tree, const char *data, BitSize_t bitLength, RakNet::BitStream *out, SystemAddressList *recipients) { BitSize_t outputOffset; // Write routing identifer out->Write((MessageID)ID_ROUTE_AND_MULTICAST); // Write the send parameters out->WriteCompressed((unsigned char)priority); out->WriteCompressed((unsigned char)reliability); out->WriteCompressed((unsigned char)orderingChannel); // Write the user payload length out->Write((unsigned int)bitLength); // out->PrintBits(); // payload->PrintBits(); out->AlignWriteToByteBoundary(); // payload->AlignReadToByteBoundary(); // out->Write(payload, payload->GetNumberOfUnreadBits()); // out->PrintBits(); if ((bitLength % 8)==0) out->Write(data, BITS_TO_BYTES(bitLength)); else out->WriteBits((const unsigned char*)data, bitLength, false); // Save where to start writing per-system data outputOffset=out->GetWriteOffset(); // Write every child of the root of the tree (SystemAddress, isRecipient, branch) unsigned i; for (i=0; i < tree->children.Size(); i++) { // Start writing at the per-system data byte out->SetWriteOffset(outputOffset); // Write our external IP to designate the sender out->Write(rakPeerInterface->GetExternalID(tree->children[i]->data.systemAddress)); // Serialize the tree SerializePreorder(tree->children[i], out, recipients); // Send to the first hop #ifdef _DO_PRINTF RAKNET_DEBUG_PRINTF("%i sending to %i\n", rakPeerInterface->GetExternalID(tree->children[i]->data.systemAddress).port, tree->children[i]->data.systemAddress.port); #endif SendUnified(out, priority, reliability, orderingChannel, tree->children[i]->data.systemAddress, false); } }
void RakVoice::CloseAllChannels(void) { RakNet::BitStream out; out.Write((unsigned char)ID_RAKVOICE_CLOSE_CHANNEL); // Free the memory for all channels unsigned index; for (index=0; index < voiceChannels.Size(); index++) { SendUnified(&out, HIGH_PRIORITY, RELIABLE_ORDERED,0,voiceChannels[index]->guid,false); FreeChannelMemory(index,false); } voiceChannels.Clear(false, __FILE__, __LINE__); }
void RakVoice::OnOpenChannelRequest(Packet *packet) { if (voiceChannels.HasData(packet->guid)) return; // If the system is not initialized, just return if (bufferedOutput==0) return; OpenChannel(packet); RakNet::BitStream out; out.Write((unsigned char)ID_RAKVOICE_OPEN_CHANNEL_REPLY); out.Write((int32_t)sampleRate); SendUnified(&out, HIGH_PRIORITY, RELIABLE_ORDERED,0,packet->systemAddress,false); }
void ConnectionGraph::OnConnectionGraphReply(Packet *packet) { unsigned char password[256]; RakNet::BitStream inBitstream(packet->data, packet->length, false); inBitstream.IgnoreBits(8); stringCompressor->DecodeString((char*)password,256,&inBitstream); if (pw && pw[0] && strcmp(pw, (const char*)password)!=0) return; // Serialize the weighted graph and send it to them RakNet::BitStream outBitstream; outBitstream.Write((MessageID)ID_CONNECTION_GRAPH_UPDATE); #ifdef _CONNECTION_GRAPH_DEBUG_PRINT RAKNET_DEBUG_PRINTF("ID_CONNECTION_GRAPH_UPDATE "); #endif // Send our current graph to the sender SerializeWeightedGraph(&outBitstream, graph); // Write the systems that have processed this graph so we don't resend to these systems outBitstream.Write((unsigned short) 1); outBitstream.Write(rakPeerInterface->GetExternalID(packet->systemAddress)); #ifdef _CONNECTION_GRAPH_DEBUG_PRINT RAKNET_DEBUG_PRINTF("from %i to %i\n", peer->GetInternalID().port, packet->systemAddress.port); #endif SendUnified(&outBitstream, LOW_PRIORITY, RELIABLE_ORDERED, connectionGraphChannel, packet->systemAddress, false); // Add packet->systemAddress to the participant list if it is not already there AddParticipant(packet->systemAddress); if (DeserializeWeightedGraph(&inBitstream, rakPeerInterface)==false) return; // Forward the updated graph to all current participants DataStructures::OrderedList<SystemAddress,SystemAddress> ignoreList; ignoreList.Insert(packet->systemAddress,packet->systemAddress, true, __FILE__, __LINE__); BroadcastGraphUpdate(ignoreList, rakPeerInterface); }
void ConnectionGraph2::OnClosedConnection(SystemAddress systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) { // Send notice to all existing connections RakNet::BitStream bs; if (lostConnectionReason==LCR_CONNECTION_LOST) bs.Write((MessageID)ID_REMOTE_CONNECTION_LOST); else bs.Write((MessageID)ID_REMOTE_DISCONNECTION_NOTIFICATION); bs.Write(systemAddress); bs.Write(rakNetGUID); SendUnified(&bs,HIGH_PRIORITY,RELIABLE_ORDERED,0,systemAddress,true); bool objectExists; unsigned int idx = remoteSystems.GetIndexFromKey(rakNetGUID, &objectExists); if (objectExists) { RakNet::OP_DELETE(remoteSystems[idx],__FILE__,__LINE__); remoteSystems.RemoveAtIndex(idx); } }
void LightweightDatabaseServer::OnQueryRequest(Packet *packet) { RakNet::BitStream inBitstream(packet->data, packet->length, false); LightweightDatabaseServer::DatabaseTable *databaseTable = DeserializeClientHeader(&inBitstream, rakPeerInterface, packet, 0); if (databaseTable==0) return; if (databaseTable->allowRemoteQuery==false) return; unsigned char numColumnSubset; RakNet::BitStream outBitstream; unsigned i; if (inBitstream.Read(numColumnSubset)==false) return; unsigned char columnName[256]; unsigned columnIndicesSubset[256]; unsigned columnIndicesCount; for (i=0,columnIndicesCount=0; i < numColumnSubset; i++) { stringCompressor->DecodeString((char*)columnName, 256, &inBitstream); unsigned colIndex = databaseTable->table.ColumnIndex((char*)columnName); if (colIndex!=(unsigned)-1) columnIndicesSubset[columnIndicesCount++]=colIndex; } unsigned char numNetworkedFilters; if (inBitstream.Read(numNetworkedFilters)==false) return; DatabaseFilter networkedFilters[256]; for (i=0; i < numNetworkedFilters; i++) { if (networkedFilters[i].Deserialize(&inBitstream)==false) return; } unsigned rowIds[256]; unsigned char numRowIDs; if (inBitstream.Read(numRowIDs)==false) return; for (i=0; i < numRowIDs; i++) inBitstream.Read(rowIds[i]); // Convert the safer and more robust networked database filter to the more efficient form the table actually uses. DataStructures::Table::FilterQuery tableFilters[256]; unsigned numTableFilters=0; for (i=0; i < numNetworkedFilters; i++) { tableFilters[numTableFilters].columnIndex=databaseTable->table.ColumnIndex(networkedFilters[i].columnName); if (tableFilters[numTableFilters].columnIndex==(unsigned)-1) continue; if (networkedFilters[i].columnType!=databaseTable->table.GetColumns()[tableFilters[numTableFilters].columnIndex].columnType) continue; tableFilters[numTableFilters].operation=networkedFilters[i].operation; // It's important that I store a pointer to the class here or the destructor of the class will deallocate the cell twice tableFilters[numTableFilters++].cellValue=&(networkedFilters[i].cellValue); } DataStructures::Table queryResult; databaseTable->table.QueryTable(columnIndicesSubset, columnIndicesCount, tableFilters, numTableFilters, rowIds, numRowIDs, &queryResult); outBitstream.Write((MessageID)ID_DATABASE_QUERY_REPLY); TableSerializer::SerializeTable(&queryResult, &outBitstream); SendUnified(&outBitstream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false); }
void RakVoice::Update(void) { unsigned i,j, bytesAvailable, speexFramesAvailable, speexBlockSize; unsigned bytesWaitingToReturn; int bytesWritten; VoiceChannel *channel; char *inputBuffer; char tempOutput[2048]; // 1 byte for ID, and 2 bytes(short) for Message number static const int headerSize=sizeof(unsigned char) + sizeof(unsigned short); // First byte is ID for RakNet tempOutput[0]=ID_RAKVOICE_DATA; RakNetTime currentTime = RakNet::GetTime(); // Size of VoiceChannel::incomingBuffer and VoiceChannel::outgoingBuffer arrays unsigned totalBufferSize=bufferSizeBytes * FRAME_OUTGOING_BUFFER_COUNT; // Allow all channels to write, and set the output to zero in preparation if (zeroBufferedOutput) { for (i=0; i < bufferedOutputCount; i++) bufferedOutput[i]=0.0f; for (i=0; i < voiceChannels.Size(); i++) voiceChannels[i]->copiedOutgoingBufferToBufferedOutput=false; zeroBufferedOutput=false; } // For each channel for (i=0; i < voiceChannels.Size(); i++) { channel=voiceChannels[i]; if (currentTime - channel->lastSend > 50) // Throttle to 20 sends a second { channel->isSendingVoiceData=false; // Circular buffer so I have to do this to count how many bytes are available if (channel->outgoingWriteIndex>=channel->outgoingReadIndex) bytesAvailable=channel->outgoingWriteIndex-channel->outgoingReadIndex; else bytesAvailable=channel->outgoingWriteIndex + (totalBufferSize-channel->outgoingReadIndex); // Speex returns how many frames it encodes per block. Each frame is of byte length sampleSize. speexBlockSize = channel->speexOutgoingFrameSampleCount * SAMPLESIZE; #ifdef PRINT_DEBUG_INFO static int lastPrint=0; if (i==0 && currentTime-lastPrint > 2000) { lastPrint=currentTime; unsigned bytesWaitingToReturn; if (channel->incomingReadIndex <= channel->incomingWriteIndex) bytesWaitingToReturn=channel->incomingWriteIndex-channel->incomingReadIndex; else bytesWaitingToReturn=totalBufferSize-channel->incomingReadIndex+channel->incomingWriteIndex; printf("%i bytes to send. incomingMessageNumber=%i. bytesWaitingToReturn=%i.\n", bytesAvailable, channel->incomingMessageNumber, bytesWaitingToReturn ); } #endif #ifdef _TEST_LOOPBACK /* if (bufferSizeBytes<bytesAvailable) { printf("Update: bytesAvailable=%i writeIndex=%i readIndex=%i\n",bytesAvailable, channel->outgoingWriteIndex, channel->outgoingReadIndex); memcpy(channel->incomingBuffer + channel->incomingWriteIndex, channel->outgoingBuffer+channel->outgoingReadIndex, bufferSizeBytes); channel->incomingWriteIndex=(channel->incomingWriteIndex+bufferSizeBytes) % totalBufferSize; channel->outgoingReadIndex=(channel->outgoingReadIndex+bufferSizeBytes) % totalBufferSize; } return; */ #endif // Find out how many frames we can read out of the buffer for speex to encode and send these out. speexFramesAvailable = bytesAvailable / speexBlockSize; // Encode all available frames and send them unreliable sequenced if (speexFramesAvailable > 0) { SpeexBits speexBits; speex_bits_init(&speexBits); while (speexFramesAvailable-- > 0) { speex_bits_reset(&speexBits); // If the input data would wrap around the buffer, copy it to another buffer first if (channel->outgoingReadIndex + speexBlockSize >= totalBufferSize) { #ifdef _DEBUG RakAssert(speexBlockSize < 2048-1); #endif unsigned t; for (t=0; t < speexBlockSize; t++) tempOutput[t+headerSize]=channel->outgoingBuffer[t%totalBufferSize]; inputBuffer=tempOutput+headerSize; } else inputBuffer=channel->outgoingBuffer+channel->outgoingReadIndex; #ifdef _DEBUG /* printf("In: "); if (shortSampleType) { short *blah = (short*) inputBuffer; for (int p=0; p < 5; p++) { printf("%.i ", blah[p]); } } else { float *blah = (float*) inputBuffer; for (int p=0; p < 5; p++) { printf("%.3f ", blah[p]); } } printf("\n"); */ #endif int is_speech=1; // Run preprocessor if required if (defaultDENOISEState||defaultVADState){ is_speech=speex_preprocess((SpeexPreprocessState*)channel->pre_state,(spx_int16_t*) inputBuffer, NULL ); } if ((is_speech)||(!defaultVADState)){ is_speech = speex_encode_int(channel->enc_state, (spx_int16_t*) inputBuffer, &speexBits); } channel->outgoingReadIndex=(channel->outgoingReadIndex+speexBlockSize)%totalBufferSize; // If no speech detected, don't send this frame if ((!is_speech)&&(defaultVADState)){ continue; } channel->isSendingVoiceData=true; #ifdef _DEBUG // printf("Update: bytesAvailable=%i writeIndex=%i readIndex=%i\n",bytesAvailable, channel->outgoingWriteIndex, channel->outgoingReadIndex); #endif bytesWritten = speex_bits_write(&speexBits, tempOutput+headerSize, 2048-headerSize); #ifdef _DEBUG // If this assert hits then you need to increase the size of the temp buffer, but this is really a bug because // voice packets should never be bigger than a few hundred bytes. RakAssert(bytesWritten!=2048-headerSize); #endif // static int bytesSent=0; // bytesSent+= bytesWritten+headerSize; // printf("bytesSent=%i\n", bytesSent); #ifdef PRINT_DEBUG_INFO static int voicePacketsSent=0; printf("%i ", voicePacketsSent++); #endif // at +1, because the first byte in the buffer has the ID for RakNet. memcpy(tempOutput+1, &channel->outgoingMessageNumber, sizeof(unsigned short)); channel->outgoingMessageNumber++; RakNet::BitStream tempOutputBs((unsigned char*) tempOutput,bytesWritten+headerSize,false); SendUnified(&tempOutputBs, HIGH_PRIORITY, UNRELIABLE,0,channel->guid,false); if (loopbackMode) { Packet p; p.length=bytesWritten+1; p.data=(unsigned char*)tempOutput; p.guid=channel->guid; p.systemAddress=rakPeerInterface->GetSystemAddressFromGuid(p.guid); OnVoiceData(&p); } } speex_bits_destroy(&speexBits); channel->lastSend=currentTime; } } // As sound buffer blocks fill up, I add their values to RakVoice::bufferedOutput . Then when the user calls ReceiveFrame they get that value, already // processed. This is necessary because that function needs to run as fast as possible so I remove all processing there that I can. Otherwise the sound // plays back distorted and popping if (channel->copiedOutgoingBufferToBufferedOutput==false) { if (channel->incomingReadIndex <= channel->incomingWriteIndex) bytesWaitingToReturn=channel->incomingWriteIndex-channel->incomingReadIndex; else bytesWaitingToReturn=totalBufferSize-channel->incomingReadIndex+channel->incomingWriteIndex; if (bytesWaitingToReturn==0) { channel->bufferOutput=true; } else if (channel->bufferOutput==false || bytesWaitingToReturn > bufferSizeBytes*2) { // Block running this again until the user calls ReceiveFrame since every call to ReceiveFrame only gets zero or one output blocks from // each channel channel->copiedOutgoingBufferToBufferedOutput=true; // Stop buffering output. We won't start buffering again until there isn't enough data to read. channel->bufferOutput=false; // Cap to the size of the output buffer. But we do write less if less is available, with the rest silence if (bytesWaitingToReturn > bufferSizeBytes) { bytesWaitingToReturn=bufferSizeBytes; } else { // Align the write index so when we increment the partial block read (which is always aligned) it computes out to 0 bytes waiting channel->incomingWriteIndex=channel->incomingReadIndex+bufferSizeBytes; if (channel->incomingWriteIndex==totalBufferSize) channel->incomingWriteIndex=0; } short *in = (short *) (channel->incomingBuffer+channel->incomingReadIndex); for (j=0; j < bytesWaitingToReturn / SAMPLESIZE; j++) { // Write short to float so if the range goes over the range of a float we can still add and subtract the correct final value. // It will be clamped at the end bufferedOutput[j]+=in[j%(totalBufferSize/SAMPLESIZE)]; } // Update the read index. Always update by bufferSizeBytes, not bytesWaitingToReturn. // if bytesWaitingToReturn < bufferSizeBytes then the rest is silence since this means the buffer ran out or we stopped sending. channel->incomingReadIndex+=bufferSizeBytes; if (channel->incomingReadIndex==totalBufferSize) channel->incomingReadIndex=0; // printf("%f %f\n", channel->incomingReadIndex/(float)bufferSizeBytes, channel->incomingWriteIndex/(float)bufferSizeBytes); } } } }
void AutopatcherServer::Update(void) { ResultTypeAndBitstream* rtab; while (threadPool.HasOutputFast() && threadPool.HasOutput()) { rtab = threadPool.GetOutput(); if (rtab->operation==ResultTypeAndBitstream::GET_PATCH) { if (rtab->fatalError==false) { if (rtab->patchList->fileList.Size()) fileListTransfer->Send(rtab->patchList, 0, rtab->systemAddress, rtab->setId, priority, orderingChannel, false, repository); rtab->bitStream1.Write((unsigned char) ID_AUTOPATCHER_FINISHED_INTERNAL); stringCompressor->EncodeString(rtab->currentDate.C_String(),64,&rtab->bitStream1); } else { rtab->bitStream1.Write((unsigned char) ID_AUTOPATCHER_REPOSITORY_FATAL_ERROR); stringCompressor->EncodeString(repository->GetLastError(), 256, &rtab->bitStream1); } RakNet::OP_DELETE(rtab->patchList, __FILE__, __LINE__); } else if (rtab->operation==ResultTypeAndBitstream::GET_CHANGELIST_SINCE_DATE) { if (rtab->fatalError==false) { if (rtab->deletedFiles->fileList.Size()) { rtab->bitStream1.Write((unsigned char) ID_AUTOPATCHER_DELETION_LIST); rtab->deletedFiles->Serialize(&rtab->bitStream1); } if (rtab->addedFiles->fileList.Size()) { rtab->bitStream2.Write((unsigned char) ID_AUTOPATCHER_CREATION_LIST); rtab->addedFiles->Serialize(&rtab->bitStream2); stringCompressor->EncodeString(rtab->currentDate.C_String(),64,&rtab->bitStream2); rtab->addedFiles->Clear(); } else { rtab->bitStream2.Write((unsigned char) ID_AUTOPATCHER_FINISHED); stringCompressor->EncodeString(rtab->currentDate.C_String(),64,&rtab->bitStream2); } } else { rtab->bitStream2.Write((unsigned char) ID_AUTOPATCHER_REPOSITORY_FATAL_ERROR); stringCompressor->EncodeString(repository->GetLastError(), 256, &rtab->bitStream2); } RakNet::OP_DELETE(rtab->deletedFiles, __FILE__, __LINE__); RakNet::OP_DELETE(rtab->addedFiles, __FILE__, __LINE__); } if (rtab->bitStream1.GetNumberOfBitsUsed()>0) SendUnified(&(rtab->bitStream1), priority, RELIABLE_ORDERED, orderingChannel, rtab->systemAddress, false); if (rtab->bitStream2.GetNumberOfBitsUsed()>0) SendUnified(&(rtab->bitStream2), priority, RELIABLE_ORDERED, orderingChannel, rtab->systemAddress, false); RakNet::OP_DELETE(rtab, __FILE__, __LINE__); } }
void FileListTransfer::Send(FileList *fileList, RakPeerInterface *rakPeer, SystemAddress recipient, unsigned short setID, PacketPriority priority, char orderingChannel, bool compressData, IncrementalReadInterface *_incrementalReadInterface, unsigned int _chunkSize) { (void) compressData; if (callback) fileList->SetCallback(callback); unsigned int i, totalLength; RakNet::BitStream outBitstream; bool sendReference; char *dataBlocks[2]; int lengths[2]; totalLength=0; for (i=0; i < fileList->fileList.Size(); i++) { const FileListNode &fileListNode = fileList->fileList[i]; totalLength+=fileListNode.fileLengthBytes; } // Write the chunk header, which contains the frequency table, the total number of files, and the total number of bytes bool anythingToWrite; outBitstream.Write((MessageID)ID_FILE_LIST_TRANSFER_HEADER); outBitstream.Write(setID); anythingToWrite=fileList->fileList.Size()>0; outBitstream.Write(anythingToWrite); if (anythingToWrite) { outBitstream.WriteCompressed(fileList->fileList.Size()); outBitstream.WriteCompressed(totalLength); if (rakPeer) rakPeer->Send(&outBitstream, priority, RELIABLE_ORDERED, orderingChannel, recipient, false); else SendUnified(&outBitstream, priority, RELIABLE_ORDERED, orderingChannel, recipient, false); for (i=0; i < fileList->fileList.Size(); i++) { outBitstream.Reset(); sendReference = fileList->fileList[i].isAReference && _incrementalReadInterface!=0; if (sendReference) { StoreForPush(fileList->fileList[i].context, setID, fileList->fileList[i].filename, fileList->fileList[i].fullPathToFile, i, fileList->fileList[i].fileLengthBytes, fileList->fileList[i].dataLengthBytes, recipient, priority, orderingChannel, _incrementalReadInterface, _chunkSize); continue; } outBitstream.Write((MessageID)ID_FILE_LIST_TRANSFER_FILE); outBitstream.Write(fileList->fileList[i].context); outBitstream.Write(setID); stringCompressor->EncodeString(fileList->fileList[i].filename, 512, &outBitstream); outBitstream.WriteCompressed(i); outBitstream.WriteCompressed(fileList->fileList[i].dataLengthBytes); // Original length in bytes outBitstream.AlignWriteToByteBoundary(); dataBlocks[0]=(char*) outBitstream.GetData(); lengths[0]=outBitstream.GetNumberOfBytesUsed(); dataBlocks[1]=fileList->fileList[i].data; lengths[1]=fileList->fileList[i].dataLengthBytes; if (rakPeer) rakPeer->SendList(dataBlocks,lengths,2,priority, RELIABLE_ORDERED, orderingChannel, recipient, false); else SendListUnified(dataBlocks,lengths,2,priority, RELIABLE_ORDERED, orderingChannel, recipient, false); } } else { if (rakPeer) rakPeer->Send(&outBitstream, priority, RELIABLE_ORDERED, orderingChannel, recipient, false); else SendUnified(&outBitstream, priority, RELIABLE_ORDERED, orderingChannel, recipient, false); } }
void FileListTransfer::OnReferencePush(Packet *packet, bool fullFile) { // fullFile is always true for TCP, since TCP does not return SPLIT_PACKET_NOTIFICATION RakNet::BitStream refPushAck; refPushAck.Write((MessageID)ID_FILE_LIST_REFERENCE_PUSH_ACK); SendUnified(&refPushAck,HIGH_PRIORITY, RELIABLE, 0, packet->systemAddress, false); FileListTransferCBInterface::OnFileStruct onFileStruct; RakNet::BitStream inBitStream(packet->data, packet->length, false); inBitStream.IgnoreBits(8); unsigned int partCount=0; unsigned int partTotal=1; unsigned int partLength=0; onFileStruct.fileData=0; if (fullFile==false) { // Disable endian swapping on reading this, as it's generated locally in ReliabilityLayer.cpp inBitStream.ReadBits( (unsigned char* ) &partCount, BYTES_TO_BITS(sizeof(partCount)), true ); inBitStream.ReadBits( (unsigned char* ) &partTotal, BYTES_TO_BITS(sizeof(partTotal)), true ); inBitStream.ReadBits( (unsigned char* ) &partLength, BYTES_TO_BITS(sizeof(partLength)), true ); inBitStream.IgnoreBits(8); // The header is appended to every chunk, which we continue to read after this statement block } inBitStream.Read(onFileStruct.context); inBitStream.Read(onFileStruct.setID); FileListReceiver *fileListReceiver; if (fileListReceivers.Has(onFileStruct.setID)==false) { return; } fileListReceiver=fileListReceivers.Get(onFileStruct.setID); if (fileListReceiver->allowedSender!=packet->systemAddress) { #ifdef _DEBUG RakAssert(0); #endif return; } #ifdef _DEBUG RakAssert(fileListReceiver->gotSetHeader==true); #endif if (stringCompressor->DecodeString(onFileStruct.fileName, 512, &inBitStream)==false) { #ifdef _DEBUG RakAssert(0); #endif return; } inBitStream.ReadCompressed(onFileStruct.fileIndex); inBitStream.ReadCompressed(onFileStruct.finalDataLength); unsigned int offset; unsigned int chunkLength; inBitStream.ReadCompressed(offset); inBitStream.ReadCompressed(chunkLength); // if (chunkLength==0) // return; bool lastChunk; inBitStream.Read(lastChunk); bool finished = lastChunk && fullFile; if (fullFile==false) fileListReceiver->partLength=partLength; FLR_MemoryBlock mb; if (fileListReceiver->pushedFiles.Has(onFileStruct.fileIndex)==false) { if (chunkLength > 1000000000 || onFileStruct.finalDataLength > 1000000000) { RakAssert("FileListTransfer::OnReferencePush: file too large" && 0); return; } mb.allocatedLength=onFileStruct.finalDataLength; mb.block = (char*) rakMalloc_Ex(onFileStruct.finalDataLength, __FILE__, __LINE__); if (mb.block==0) { notifyOutOfMemory(__FILE__, __LINE__); return; } fileListReceiver->pushedFiles.SetNew(onFileStruct.fileIndex, mb); } else mb=fileListReceiver->pushedFiles.Get(onFileStruct.fileIndex); if (offset+chunkLength > mb.allocatedLength) { // Overrun RakAssert("FileListTransfer::OnReferencePush: Write would overrun allocated block" && 0); return; } // Read header uncompressed so the data is byte aligned, for speed onFileStruct.compressedTransmissionLength=(unsigned int) onFileStruct.finalDataLength; unsigned int unreadBits = inBitStream.GetNumberOfUnreadBits(); unsigned int unreadBytes = BITS_TO_BYTES(unreadBits); unsigned int amountToRead; if (fullFile) amountToRead=chunkLength; else amountToRead=unreadBytes; inBitStream.AlignReadToByteBoundary(); if (fullFile || (rakPeerInterface->GetSplitMessageProgressInterval() != 0 && (int)partCount==rakPeerInterface->GetSplitMessageProgressInterval())) { inBitStream.Read(mb.block+offset, amountToRead); } onFileStruct.setCount=fileListReceiver->setCount; onFileStruct.setTotalCompressedTransmissionLength=fileListReceiver->setTotalCompressedTransmissionLength; onFileStruct.setTotalFinalLength=fileListReceiver->setTotalFinalLength; onFileStruct.fileData=mb.block; if (finished) { if (fileListReceiver->downloadHandler->OnFile(&onFileStruct)) rakFree_Ex(onFileStruct.fileData, __FILE__, __LINE__ ); fileListReceiver->pushedFiles.Delete(onFileStruct.fileIndex); fileListReceiver->filesReceived++; // If this set is done, free the memory for it. if ((int) fileListReceiver->setCount==fileListReceiver->filesReceived) { if (fileListReceiver->downloadHandler->OnDownloadComplete()==false) { fileListReceiver->downloadHandler->OnDereference(); fileListReceivers.Delete(onFileStruct.setID); if (fileListReceiver->deleteDownloadHandler) RakNet::OP_DELETE(fileListReceiver->downloadHandler, __FILE__, __LINE__); RakNet::OP_DELETE(fileListReceiver, __FILE__, __LINE__); } } } else { unsigned int totalNotifications; unsigned int currentNotificationIndex; unsigned int unreadBytes; if (rakPeerInterface==0 || rakPeerInterface->GetSplitMessageProgressInterval()==0) { totalNotifications = onFileStruct.finalDataLength / chunkLength + 1; currentNotificationIndex = offset / chunkLength; unreadBytes = mb.allocatedLength - ((offset+1)*chunkLength); } else { totalNotifications = onFileStruct.finalDataLength / fileListReceiver->partLength + 1; if (fullFile==false) currentNotificationIndex = (offset+partCount*fileListReceiver->partLength) / fileListReceiver->partLength ; else currentNotificationIndex = (offset+chunkLength) / fileListReceiver->partLength ; unreadBytes = onFileStruct.finalDataLength - ((currentNotificationIndex+1) * fileListReceiver->partLength); } fileListReceiver->downloadHandler->OnFileProgress(&onFileStruct, currentNotificationIndex, totalNotifications, unreadBytes, mb.block); } return; }
void FileListTransfer::Send(FileList *fileList, RakPeerInterface *rakPeer, SystemAddress recipient, unsigned short setID, PacketPriority priority, char orderingChannel, bool compressData, IncrementalReadInterface *_incrementalReadInterface, unsigned int _chunkSize) { (void) compressData; if (callback) fileList->SetCallback(callback); unsigned int i, totalLength; RakNet::BitStream outBitstream; bool sendReference; const char *dataBlocks[2]; int lengths[2]; totalLength=0; for (i=0; i < fileList->fileList.Size(); i++) { const FileListNode &fileListNode = fileList->fileList[i]; totalLength+=fileListNode.fileLengthBytes; } // Write the chunk header, which contains the frequency table, the total number of files, and the total number of bytes bool anythingToWrite; outBitstream.Write((MessageID)ID_FILE_LIST_TRANSFER_HEADER); outBitstream.Write(setID); anythingToWrite=fileList->fileList.Size()>0; outBitstream.Write(anythingToWrite); if (anythingToWrite) { outBitstream.WriteCompressed(fileList->fileList.Size()); outBitstream.WriteCompressed(totalLength); if (rakPeer) rakPeer->Send(&outBitstream, priority, RELIABLE_ORDERED, orderingChannel, recipient, false); else SendUnified(&outBitstream, priority, RELIABLE_ORDERED, orderingChannel, recipient, false); DataStructures::Queue<FileToPush*> filesToPush; for (i=0; i < fileList->fileList.Size(); i++) { sendReference = fileList->fileList[i].isAReference && _incrementalReadInterface!=0; if (sendReference) { FileToPush *fileToPush = RakNet::OP_NEW<FileToPush>(__FILE__,__LINE__); fileToPush->fileListNode.context=fileList->fileList[i].context; fileToPush->setIndex=i; fileToPush->fileListNode.filename=fileList->fileList[i].filename; fileToPush->fileListNode.fullPathToFile=fileList->fileList[i].fullPathToFile; fileToPush->fileListNode.fileLengthBytes=fileList->fileList[i].fileLengthBytes; fileToPush->fileListNode.dataLengthBytes=fileList->fileList[i].dataLengthBytes; // fileToPush->systemAddress=recipient; fileToPush->setID=setID; fileToPush->packetPriority=priority; fileToPush->orderingChannel=orderingChannel; fileToPush->currentOffset=0; fileToPush->incrementalReadInterface=_incrementalReadInterface; fileToPush->chunkSize=_chunkSize; filesToPush.Push(fileToPush,__FILE__,__LINE__); } else { outBitstream.Reset(); outBitstream.Write((MessageID)ID_FILE_LIST_TRANSFER_FILE); outBitstream.Write(fileList->fileList[i].context); outBitstream.Write(setID); stringCompressor->EncodeString(fileList->fileList[i].filename, 512, &outBitstream); outBitstream.WriteCompressed(i); outBitstream.WriteCompressed(fileList->fileList[i].dataLengthBytes); // Original length in bytes outBitstream.AlignWriteToByteBoundary(); dataBlocks[0]=(char*) outBitstream.GetData(); lengths[0]=outBitstream.GetNumberOfBytesUsed(); dataBlocks[1]=fileList->fileList[i].data; lengths[1]=fileList->fileList[i].dataLengthBytes; SendListUnified(dataBlocks,lengths,2,priority, RELIABLE_ORDERED, orderingChannel, recipient, false); } } if (filesToPush.IsEmpty()==false) { FileToPushRecipient *ftpr=0; filesToPushAllSameAddressMutex.Lock(); for (unsigned int i=0; i < filesToPushAllSameAddress.Size(); i++) { if (filesToPushAllSameAddress[i]->systemAddress==recipient) { ftpr=filesToPushAllSameAddress[i]; break; } } if (ftpr==0) { ftpr = RakNet::OP_NEW<FileToPushRecipient>(__FILE__,__LINE__); ftpr->systemAddress=recipient; filesToPushAllSameAddress.Push(ftpr, __FILE__,__LINE__); } while (filesToPush.IsEmpty()==false) { ftpr->filesToPush.Push(filesToPush.Pop(), __FILE__,__LINE__); } filesToPushAllSameAddressMutex.Unlock(); SendIRIToAddress(recipient); return; } } else { if (rakPeer) rakPeer->Send(&outBitstream, priority, RELIABLE_ORDERED, orderingChannel, recipient, false); else SendUnified(&outBitstream, priority, RELIABLE_ORDERED, orderingChannel, recipient, false); } }
void ReplicaManager::Destruct(Replica *replica, SystemAddress systemAddress, bool broadcast) { RakAssert(replica); bool sendTimestamp; bool objectExists; unsigned replicatedObjectsIndex; replicatedObjectsIndex = replicatedObjects.GetIndexFromKey(replica, &objectExists); if (objectExists==false) return; // For each existing participant, send a packet telling them of this object destruction RakNet::BitStream outBitstream, userDataBitStream; unsigned i,tempIndex; bool replicaReferenced; ParticipantStruct *participantStruct; replicaReferenced=false; for (i=0; i < participantList.Size(); i++) { participantStruct=participantList[i]; if ((broadcast==true && systemAddress!=participantStruct->systemAddress) || (broadcast==false && systemAddress==participantStruct->systemAddress)) { // Remove any remote object state tracking for this object, for this player tempIndex = participantStruct->remoteObjectList.GetIndexFromKey(replica, &objectExists); if (objectExists) { // Send the destruction packet immediately if (replica->GetNetworkID()!=UNASSIGNED_NETWORK_ID && (replicatedObjects[replicatedObjectsIndex].allowedInterfaces & REPLICA_SEND_DESTRUCTION)) { userDataBitStream.Reset(); userDataBitStream.Write((MessageID)ID_REPLICA_MANAGER_DESTRUCTION); userDataBitStream.Write(replica->GetNetworkID()); sendTimestamp=false; ReplicaReturnResult res = replica->SendDestruction(&userDataBitStream, participantStruct->systemAddress, &sendTimestamp); if (res==REPLICA_PROCESSING_DONE) { outBitstream.Reset(); if (sendTimestamp) { outBitstream.Write((MessageID)ID_TIMESTAMP); outBitstream.Write(RakNet::GetTime()); outBitstream.Write(&userDataBitStream); SendUnified(&outBitstream, HIGH_PRIORITY, RELIABLE_ORDERED, sendChannel, participantStruct->systemAddress, false); } else SendUnified(&userDataBitStream, HIGH_PRIORITY, RELIABLE_ORDERED, sendChannel, participantStruct->systemAddress, false); } } participantStruct->remoteObjectList.RemoveAtIndex(tempIndex); } // Remove any pending commands that reference this object, for this player tempIndex = GetCommandListReplicaIndex(participantStruct->commandList, replica, &objectExists); // tempIndex = participantStruct->commandList.GetIndexFromKey(replica, &objectExists); if (objectExists) participantStruct->commandList.RemoveAtIndex(tempIndex); } else if (replicaReferenced==false) { bool objectExists; GetCommandListReplicaIndex(participantStruct->commandList, replica, &objectExists); // See if any commands or objects reference replica //if (participantStruct->commandList.HasData(replica)) if (objectExists) replicaReferenced=true; else if (participantStruct->remoteObjectList.HasData(replica)) replicaReferenced=true; } } // Remove replica from the list if no commands and no remote objects reference it if (replicaReferenced==false) replicatedObjects.RemoveAtIndex(replicatedObjectsIndex); }
PluginReceiveResult Router::OnReceive(Packet *packet) { if (packet->data[0]==ID_ROUTE_AND_MULTICAST || (packet->length>5 && packet->data[0]==ID_TIMESTAMP && packet->data[5]==ID_ROUTE_AND_MULTICAST)) { #ifdef _DO_PRINTF RAKNET_DEBUG_PRINTF("%i got routed message from %i\n", peer->GetExternalID(packet->systemAddress).port, packet->systemAddress.port); #endif RakNetTime timestamp; PacketPriority priority; PacketReliability reliability; unsigned char orderingChannel; SystemAddress originalSender; RakNet::BitStream out; BitSize_t outStartingOffset; unsigned int payloadBitLength; unsigned payloadWriteByteOffset; RakNet::BitStream incomingBitstream(packet->data, packet->length, false); incomingBitstream.IgnoreBits(8); if (packet->data[0]==ID_TIMESTAMP) { incomingBitstream.Read(timestamp); out.Write((MessageID)ID_TIMESTAMP); out.Write(timestamp); incomingBitstream.IgnoreBits(8); } // Read the send parameters unsigned char c; incomingBitstream.ReadCompressed(c); priority=(PacketPriority)c; incomingBitstream.ReadCompressed(c); reliability=(PacketReliability)c; incomingBitstream.ReadCompressed(orderingChannel); incomingBitstream.Read(payloadBitLength); out.Write((MessageID)ID_ROUTE_AND_MULTICAST); out.WriteCompressed((unsigned char)priority); out.WriteCompressed((unsigned char)reliability); out.WriteCompressed((unsigned char)orderingChannel); out.Write(payloadBitLength); out.AlignWriteToByteBoundary(); incomingBitstream.AlignReadToByteBoundary(); payloadWriteByteOffset=(unsigned int) BITS_TO_BYTES(out.GetWriteOffset()); out.Write(&incomingBitstream, payloadBitLength); // This write also does a read on incomingBitStream if (restrictByType) { RakNet::BitStream t(out.GetData()+payloadWriteByteOffset, sizeof(unsigned char), false); MessageID messageID; t.Read(messageID); if (allowedTypes.HasData(messageID)==false) return RR_STOP_PROCESSING_AND_DEALLOCATE; // Don't route restricted types } incomingBitstream.Read(originalSender); out.Write(originalSender); outStartingOffset=out.GetWriteOffset(); // Deserialize the root bool hasData=false; SystemAddress recipient; unsigned short numberOfChildren; incomingBitstream.Read(hasData); incomingBitstream.Read(recipient); // This should be our own address if (incomingBitstream.ReadCompressed(numberOfChildren)==false) { #ifdef _DEBUG RakAssert(0); #endif return RR_STOP_PROCESSING_AND_DEALLOCATE; } unsigned childIndex; bool childHasData=false; SystemAddress childRecipient; unsigned short childNumberOfChildren; SystemAddress immediateRecipient; immediateRecipient=UNASSIGNED_SYSTEM_ADDRESS; int pendingNodeCount=0; for (childIndex=0; childIndex < numberOfChildren; childIndex++) { while (pendingNodeCount!=-1) { // Copy out the serialized subtree for this child incomingBitstream.Read(childHasData); incomingBitstream.Read(childRecipient); if (!incomingBitstream.ReadCompressed(childNumberOfChildren)) return RR_STOP_PROCESSING_AND_DEALLOCATE; if (immediateRecipient==UNASSIGNED_SYSTEM_ADDRESS) { immediateRecipient=childRecipient; } pendingNodeCount+=childNumberOfChildren-1; out.Write(childHasData); out.Write(childRecipient); out.WriteCompressed(childNumberOfChildren); } #ifdef _DO_PRINTF RAKNET_DEBUG_PRINTF("%i routing to %i\n", peer->GetExternalID(packet->systemAddress).port, immediateRecipient.port); #endif // Send what we got so far SendUnified(&out, priority, reliability, orderingChannel, immediateRecipient, false); // Restart writing the per recipient data out.SetWriteOffset(outStartingOffset); // Reread the top level node immediateRecipient=UNASSIGNED_SYSTEM_ADDRESS; pendingNodeCount=0; } // Write the user payload to the packet struct if this is a destination and change the sender and return true if (hasData) { #ifdef _DO_PRINTF RAKNET_DEBUG_PRINTF("%i returning payload to user\n", peer->GetExternalID(packet->systemAddress).port); #endif if (packet->data[0]==ID_TIMESTAMP ) { memcpy( packet->data + sizeof(RakNetTime)+sizeof(unsigned char), out.GetData()+payloadWriteByteOffset, BITS_TO_BYTES(payloadBitLength) ); packet->bitSize=BYTES_TO_BITS(sizeof(RakNetTime)+sizeof(unsigned char))+payloadBitLength; } else { memcpy( packet->data, out.GetData()+payloadWriteByteOffset, BITS_TO_BYTES(payloadBitLength) ); packet->bitSize=payloadBitLength; } packet->length=(unsigned int) BITS_TO_BYTES(packet->bitSize); packet->systemAddress.systemIndex=(SystemIndex)-1; packet->systemAddress=originalSender; return RR_CONTINUE_PROCESSING; } // Absorb return RR_STOP_PROCESSING_AND_DEALLOCATE; } return RR_CONTINUE_PROCESSING; }
void ReplicaManager::Update(void) { if (participantList.Size()==0) return; // Check for recursive calls, which is not supported and should not happen #ifdef _DEBUG RakAssert(inUpdate==false); inUpdate=true; #endif unsigned participantIndex, remoteObjectListIndex, replicatedObjectsIndex; ReplicaReturnResult res; bool sendTimestamp; ParticipantStruct *participantStruct; unsigned commandListIndex; RakNet::BitStream outBitstream, userDataBitstream; RakNetTime currentTime; bool objectExists; PacketPriority priority; PacketReliability reliability; ReceivedCommand *receivedCommand; Replica *replica; // unsigned int userFlags; unsigned char command; currentTime=0; // For each participant for (participantIndex=0; participantIndex < participantList.Size(); participantIndex++) { participantStruct = participantList[participantIndex]; // Sends the download complete packet // If callDownloadCompleteCB is true then check all the remaining objects starting at commandListIndex // I scan every frame in case the callback returns false to delay, and after that time a new object is Replicated if (participantStruct->callDownloadCompleteCB) { bool anyHasConstruction; unsigned j; anyHasConstruction=false; for (j=0; j < participantStruct->commandList.Size(); j++) { if (participantStruct->commandList[j].command & REPLICA_EXPLICIT_CONSTRUCTION) { anyHasConstruction=true; break; } } // If none have REPLICA_EXPLICIT_CONSTRUCTION, send ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE and set callDownloadCompleteCB false if (anyHasConstruction==false) { ReplicaReturnResult sendDLComplete; userDataBitstream.Reset(); if (_sendDownloadCompleteCB) sendDLComplete=_sendDownloadCompleteCB->SendDownloadComplete(&userDataBitstream, currentTime, participantStruct->systemAddress, this); // If you return false, this will be called again next update else sendDLComplete=REPLICA_CANCEL_PROCESS; if (sendDLComplete==REPLICA_PROCESSING_DONE) { outBitstream.Reset(); outBitstream.Write((MessageID)ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE); outBitstream.Write(&userDataBitstream, userDataBitstream.GetNumberOfBitsUsed()); SendUnified(&outBitstream, HIGH_PRIORITY, RELIABLE_ORDERED, sendChannel, participantStruct->systemAddress, false); participantStruct->callDownloadCompleteCB=false; } else if (sendDLComplete==REPLICA_CANCEL_PROCESS) { participantStruct->callDownloadCompleteCB=false; } else { RakAssert(sendDLComplete==REPLICA_PROCESS_LATER); // else REPLICA_PROCESS_LATER } } } // For each CommandStruct to send for (commandListIndex=0; commandListIndex < participantStruct->commandList.Size(); commandListIndex++) { // Only call RakNet::GetTime() once because it's slow if (currentTime==0) currentTime=RakNet::GetTime(); replica=participantStruct->commandList[commandListIndex].replica; command=participantStruct->commandList[commandListIndex].command; // userFlags=participantStruct->commandList[commandListIndex].userFlags; replicatedObjectsIndex=replicatedObjects.GetIndexFromKey(replica, &objectExists); #ifdef _DEBUG RakAssert(objectExists); #endif if (objectExists==false) continue; // If construction is set, call SendConstruction. The only precondition is that the object was not already created, // which was checked in ReplicaManager::Replicate if (command & REPLICA_EXPLICIT_CONSTRUCTION) { if (replicatedObjects[replicatedObjectsIndex].allowedInterfaces & REPLICA_SEND_CONSTRUCTION) { userDataBitstream.Reset(); sendTimestamp=false; res=replica->SendConstruction(currentTime, participantStruct->systemAddress, participantStruct->commandList[commandListIndex].userFlags, &userDataBitstream, &sendTimestamp); if (res==REPLICA_PROCESSING_DONE) { outBitstream.Reset(); // If SendConstruction returns true and writes to outBitStream, do this send. Clear the construction command. Then process the next command for this CommandStruct, if any. if (sendTimestamp) { outBitstream.Write((MessageID)ID_TIMESTAMP); outBitstream.Write(currentTime); } outBitstream.Write((MessageID)ID_REPLICA_MANAGER_CONSTRUCTION); // It's required to send an NetworkID if available. // Problem: // A->B->C // | | // D->E // // A creates. // B->C->E->D->B will cycle forever. // Fix is to always include an networkID. Objects are not created if that object id already is present. if (replica->GetNetworkID()!=UNASSIGNED_NETWORK_ID) { outBitstream.Write(true); outBitstream.Write(replica->GetNetworkID()); } else outBitstream.Write(false); outBitstream.Write(&userDataBitstream, userDataBitstream.GetNumberOfBitsUsed()); SendUnified(&outBitstream, HIGH_PRIORITY, RELIABLE_ORDERED, sendChannel, participantStruct->systemAddress, false); // Turn off this bit participantStruct->commandList[commandListIndex].command &= 0xFF ^ REPLICA_EXPLICIT_CONSTRUCTION; // Add the object to the participant's object list, indicating this object has been remotely created RemoteObject remoteObject; remoteObject.replica=replica; //remoteObject.inScope=defaultScope; remoteObject.inScope=false; remoteObject.lastSendTime=0; remoteObject.userFlags=participantStruct->commandList[commandListIndex].userFlags; // Create an entry for this object. We do this now, even if the user might refuse the SendConstruction override, // because that call may be delayed and other commands sent while that is pending. We always do the REPLICA_EXPLICIT_CONSTRUCTION call first. participantStruct->remoteObjectList.Insert(remoteObject.replica,remoteObject, true, __FILE__,__LINE__); } else if (res==REPLICA_PROCESS_IMPLICIT) { // Turn off this bit participantStruct->commandList[commandListIndex].command &= 0xFF ^ REPLICA_EXPLICIT_CONSTRUCTION; // Add the object to the participant's object list, indicating this object has been remotely created RemoteObject remoteObject; remoteObject.replica=replica; //remoteObject.inScope=defaultScope; remoteObject.inScope=false; remoteObject.lastSendTime=0; remoteObject.userFlags=participantStruct->commandList[commandListIndex].userFlags; // Create an entry for this object. We do this now, even if the user might refuse the SendConstruction override, // because that call may be delayed and other commands sent while that is pending. We always do the REPLICA_EXPLICIT_CONSTRUCTION call first. participantStruct->remoteObjectList.Insert(remoteObject.replica,remoteObject, true, __FILE__,__LINE__); } else if (res==REPLICA_PROCESS_LATER) { continue; } else // REPLICA_CANCEL_PROCESS { RakAssert(res==REPLICA_CANCEL_PROCESS); participantStruct->commandList[commandListIndex].command=0; } } else { // Don't allow construction, or anything else for this object, as the construction send call is disallowed participantStruct->commandList[commandListIndex].command=0; } } else if (command & REPLICA_IMPLICIT_CONSTRUCTION) { // Turn off this bit participantStruct->commandList[commandListIndex].command &= 0xFF ^ REPLICA_IMPLICIT_CONSTRUCTION; // Add the object to the participant's object list, indicating this object is assumed to be remotely created RemoteObject remoteObject; remoteObject.replica=replica; //remoteObject.inScope=defaultScope; remoteObject.inScope=false; remoteObject.lastSendTime=0; remoteObject.userFlags=participantStruct->commandList[commandListIndex].userFlags; // Create an entry for this object. We do this now, even if the user might refuse the SendConstruction override, // because that call may be delayed and other commands sent while that is pending. We always do the REPLICA_EXPLICIT_CONSTRUCTION call first. participantStruct->remoteObjectList.Insert(remoteObject.replica,remoteObject, true, __FILE__,__LINE__); } // The remaining commands, SendScopeChange and Serialize, require the object the command references exists on the remote system, so check that remoteObjectListIndex = participantStruct->remoteObjectList.GetIndexFromKey(replica, &objectExists); if (objectExists) { command = participantStruct->commandList[commandListIndex].command; // Process SendScopeChange. if ((command & (REPLICA_SCOPE_TRUE | REPLICA_SCOPE_FALSE))) { if (replica->GetNetworkID()==UNASSIGNED_NETWORK_ID) continue; // Not set yet so call this later. if (replicatedObjects[replicatedObjectsIndex].allowedInterfaces & REPLICA_SEND_SCOPE_CHANGE) { bool scopeTrue = (command & REPLICA_SCOPE_TRUE)!=0; // Only send scope changes if the requested change is different from what they already have if (participantStruct->remoteObjectList[remoteObjectListIndex].inScope!=scopeTrue) { userDataBitstream.Reset(); sendTimestamp=false; res=replica->SendScopeChange(scopeTrue, &userDataBitstream, currentTime, participantStruct->systemAddress, &sendTimestamp); if (res==REPLICA_PROCESSING_DONE) { // If the user returns true and does write to outBitstream, do this send. Clear the scope change command. Then process the next command for this CommandStruct, if any. outBitstream.Reset(); if (sendTimestamp) { outBitstream.Write((MessageID)ID_TIMESTAMP); outBitstream.Write(currentTime); } outBitstream.Write((MessageID)ID_REPLICA_MANAGER_SCOPE_CHANGE); outBitstream.Write(replica->GetNetworkID()); outBitstream.Write(&userDataBitstream, userDataBitstream.GetNumberOfBitsUsed()); SendUnified(&outBitstream, HIGH_PRIORITY, RELIABLE_ORDERED, sendChannel, participantStruct->systemAddress, false); // Set the scope for this object and system participantStruct->remoteObjectList[remoteObjectListIndex].inScope=scopeTrue; // If scope is true, turn on serialize, since you virtually always want to serialize when an object goes in scope if (scopeTrue && autoSerializeInScope) participantStruct->commandList[commandListIndex].command |= REPLICA_SERIALIZE; // Turn off these bits - Call is processed participantStruct->commandList[commandListIndex].command &= 0xFF ^ (REPLICA_SCOPE_TRUE | REPLICA_SCOPE_FALSE); } else if (res==REPLICA_CANCEL_PROCESS) { // Turn off these bits - Call is canceled participantStruct->commandList[commandListIndex].command &= 0xFF ^ (REPLICA_SCOPE_TRUE | REPLICA_SCOPE_FALSE); } else { // If the user returns false and the scope is currently set to false, just continue with another CommandStruct. Don't process serialization until scoping is resolved first. if (scopeTrue==false) continue; // If the user returns false and the scope is currently set to false, process the next command for this CommandStruct, if any. } } } else { // Turn off these bits - Call is disallowed participantStruct->commandList[commandListIndex].command &= 0xFF ^ (REPLICA_SCOPE_TRUE | REPLICA_SCOPE_FALSE); // Set the scope - even if the actual send is disabled we might still be able to serialize. participantStruct->remoteObjectList[remoteObjectListIndex].inScope=(command & REPLICA_SCOPE_TRUE)!=0; } } command = participantStruct->commandList[commandListIndex].command; // Process Serialize if ((command & REPLICA_SERIALIZE)) { if (replica->GetNetworkID()==UNASSIGNED_NETWORK_ID) continue; // Not set yet so call this later. // If scope is currently false for this object in the RemoteObject list, cancel this serialize as the scope changed before the serialization went out if (participantStruct->remoteObjectList[remoteObjectListIndex].inScope && (replicatedObjects[replicatedObjectsIndex].allowedInterfaces & REPLICA_SEND_SERIALIZE)) { do { userDataBitstream.Reset(); priority=HIGH_PRIORITY; reliability=RELIABLE_ORDERED; sendTimestamp=false; res=replica->Serialize(&sendTimestamp, &userDataBitstream, participantStruct->remoteObjectList[remoteObjectListIndex].lastSendTime, &priority, &reliability, currentTime, participantStruct->systemAddress, participantStruct->remoteObjectList[remoteObjectListIndex].userFlags); if (res==REPLICA_PROCESSING_DONE || res==REPLICA_PROCESS_AGAIN) { participantStruct->remoteObjectList[remoteObjectListIndex].lastSendTime=currentTime; outBitstream.Reset(); if (sendTimestamp) { outBitstream.Write((MessageID)ID_TIMESTAMP); outBitstream.Write(currentTime); } outBitstream.Write((MessageID)ID_REPLICA_MANAGER_SERIALIZE); outBitstream.Write(replica->GetNetworkID()); outBitstream.Write(&userDataBitstream, userDataBitstream.GetNumberOfBitsUsed()); SendUnified(&outBitstream, priority, reliability, sendChannel, participantStruct->systemAddress, false); // Clear the serialize bit when done if (res==REPLICA_PROCESSING_DONE) participantStruct->commandList[commandListIndex].command &= 0xFF ^ REPLICA_SERIALIZE; // else res==REPLICA_PROCESS_AGAIN so it will repeat the enclosing do {} while(); loop } else if (res==REPLICA_CANCEL_PROCESS) { // Clear the serialize bit participantStruct->commandList[commandListIndex].command &= 0xFF ^ REPLICA_SERIALIZE; } else { // if the user returns REPLICA_PROCESS_LATER, just continue with another CommandStruct. RakAssert(res==REPLICA_PROCESS_LATER); } } while(res==REPLICA_PROCESS_AGAIN); } else { // Cancel this serialize participantStruct->commandList[commandListIndex].command &= 0xFF ^ REPLICA_SERIALIZE; } } } } // Go through the command list and delete all cleared commands, from back to front. It is more efficient to do this than to delete them as we process commandListIndex=participantStruct->commandList.Size(); if (commandListIndex>0) { #ifdef _MSC_VER #pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant #endif while (1) { if (participantStruct->commandList[commandListIndex-1].command==0) { // If this is the last item in the list, and it probably is, then it just changes a number rather than shifts the entire array participantStruct->commandList.RemoveAtIndex(commandListIndex-1); } if (--commandListIndex==0) break; } } // Now process queued receives while (participantStruct->pendingCommands.Size()) { receivedCommand=participantStruct->pendingCommands.Pop(); participantStruct=GetParticipantBySystemAddress(receivedCommand->systemAddress); if (participantStruct) { res=ProcessReceivedCommand(participantStruct, receivedCommand); // Returning false means process this command again later if (res==REPLICA_PROCESS_LATER) { // Push the command back in the queue participantStruct->pendingCommands.PushAtHead(receivedCommand, 0, __FILE__,__LINE__); // Stop processing, because all processing is in order break; } else { RakAssert(res==REPLICA_CANCEL_PROCESS); } } // Done with this command, so delete it RakNet::OP_DELETE(receivedCommand->userData, __FILE__, __LINE__); RakNet::OP_DELETE(receivedCommand, __FILE__, __LINE__); } } #ifdef _DEBUG inUpdate=false; #endif }