Exemple #1
0
bool CloudClient::Get(CloudQuery *keyQuery, RakNetGUID systemIdentifier)
{
	SLNet::BitStream bsOut;
	bsOut.Write((MessageID)ID_CLOUD_GET_REQUEST);
	keyQuery->Serialize(true, &bsOut);
	bsOut.WriteCasted<uint16_t>(0); // Specific systems
	SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false);
	return true;
}
Exemple #2
0
void CloudClient::Release(DataStructures::List<CloudKey> &keys, RakNetGUID systemIdentifier)
{
	SLNet::BitStream bsOut;
	bsOut.Write((MessageID)ID_CLOUD_RELEASE_REQUEST);
	RakAssert(keys.Size() < (uint16_t)-1 );
	bsOut.WriteCasted<uint16_t>(keys.Size());
	for (uint16_t i=0; i < keys.Size(); i++)	
	{
		keys[i].Serialize(true,&bsOut);
	}
	SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false);
}
PluginReceiveResult TwoWayAuthentication::OnHashedNonceAndPassword(Packet *packet)
{
	SLNet::BitStream bsIn(packet->data, packet->length, false);
	bsIn.IgnoreBytes(sizeof(MessageID)*2);

	char remoteHashedNonceAndPw[HASHED_NONCE_AND_PW_LENGTH];
	unsigned short requestId;
	bsIn.Read(requestId);
	SLNet::RakString passwordIdentifier;
	bsIn.Read(passwordIdentifier);
	bsIn.ReadAlignedBytes((unsigned char *) remoteHashedNonceAndPw,HASHED_NONCE_AND_PW_LENGTH);

	// Look up used nonce from requestId
	char usedNonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH];
	if (nonceGenerator.GetNonceById(usedNonce, requestId, packet, true)==false)
		return RR_STOP_PROCESSING_AND_DEALLOCATE;

	DataStructures::HashIndex skhi = passwords.GetIndexOf(passwordIdentifier.C_String());
	if (skhi.IsInvalid()==false)
	{
		char hashedThisNonceAndPw[HASHED_NONCE_AND_PW_LENGTH];
		Hash(usedNonce, passwords.ItemAtIndex(skhi), hashedThisNonceAndPw);
		if (memcmp(hashedThisNonceAndPw, remoteHashedNonceAndPw,HASHED_NONCE_AND_PW_LENGTH)==0)
		{
			// Pass
			SLNet::BitStream bsOut;
			bsOut.Write((MessageID)ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_SUCCESS);
			bsOut.WriteAlignedBytes((const unsigned char*) usedNonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH);
			bsOut.WriteAlignedBytes((const unsigned char*) remoteHashedNonceAndPw,HASHED_NONCE_AND_PW_LENGTH);
			bsOut.Write(passwordIdentifier);
			SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet,false);

			// Incoming success, modify packet header to tell user
			PushToUser(ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_SUCCESS, passwordIdentifier, packet);

			return RR_STOP_PROCESSING_AND_DEALLOCATE;
		}
	}

	// Incoming failure, modify arrived packet header to tell user
	packet->data[0]=(MessageID) ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_FAILURE;
	
	SLNet::BitStream bsOut;
	bsOut.Write((MessageID)ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_FAILURE);
	bsOut.WriteAlignedBytes((const unsigned char*) usedNonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH);
	bsOut.WriteAlignedBytes((const unsigned char*) remoteHashedNonceAndPw,HASHED_NONCE_AND_PW_LENGTH);
	bsOut.Write(passwordIdentifier);
	SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet,false);

	return RR_CONTINUE_PROCESSING;
}
Exemple #4
0
bool CloudClient::Get(CloudQuery *keyQuery, DataStructures::List<RakNetGUID> &specificSystems, RakNetGUID systemIdentifier)
{
	SLNet::BitStream bsOut;
	bsOut.Write((MessageID)ID_CLOUD_GET_REQUEST);
	keyQuery->Serialize(true, &bsOut);
	bsOut.WriteCasted<uint16_t>(specificSystems.Size());
	RakAssert(specificSystems.Size() < (uint16_t)-1 );
	for (uint16_t i=0; i < specificSystems.Size(); i++)
	{
		bsOut.Write(specificSystems[i]);
	}
	SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false);
	return true;
}
Exemple #5
0
void CloudClient::Post(CloudKey *cloudKey, const unsigned char *data, uint32_t dataLengthBytes, RakNetGUID systemIdentifier)
{
	RakAssert(cloudKey);
	
	SLNet::BitStream bsOut;
	bsOut.Write((MessageID)ID_CLOUD_POST_REQUEST);
	cloudKey->Serialize(true,&bsOut);
	if (data==0)
		dataLengthBytes=0;
	bsOut.Write(dataLengthBytes);
	if (dataLengthBytes>0)
		bsOut.WriteAlignedBytes((const unsigned char*) data, dataLengthBytes);
	SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false);
}
void TwoWayAuthentication::OnNonceRequest(Packet *packet)
{
	SLNet::BitStream bsIn(packet->data, packet->length, false);
	bsIn.IgnoreBytes(sizeof(MessageID)*2);

	char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH];
	unsigned short requestId;
	nonceGenerator.GetNonce(nonce,&requestId,packet);

	SLNet::BitStream bsOut;
	bsOut.Write((MessageID)ID_TWO_WAY_AUTHENTICATION_NEGOTIATION);
	bsOut.Write((MessageID)ID_NONCE_REPLY);
	bsOut.Write(requestId);
	bsOut.WriteAlignedBytes((const unsigned char*) nonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH);
	SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet,false);
}
Exemple #7
0
void CloudClient::Unsubscribe(DataStructures::List<CloudKey> &keys, DataStructures::List<RakNetGUID> &specificSystems, RakNetGUID systemIdentifier)
{
	SLNet::BitStream bsOut;
	bsOut.Write((MessageID)ID_CLOUD_UNSUBSCRIBE_REQUEST);
	RakAssert(keys.Size() < (uint16_t)-1 );
	bsOut.WriteCasted<uint16_t>(keys.Size());
	for (uint16_t i=0; i < keys.Size(); i++)	
	{
		keys[i].Serialize(true,&bsOut);
	}
	bsOut.WriteCasted<uint16_t>(specificSystems.Size());
	RakAssert(specificSystems.Size() < (uint16_t)-1 );
	for (uint16_t i=0; i < specificSystems.Size(); i++)
	{
		bsOut.Write(specificSystems[i]);
	}
	SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false);
}
bool TwoWayAuthentication::Challenge(SLNet::RakString identifier, AddressOrGUID remoteSystem)
{
	DataStructures::HashIndex skhi = passwords.GetIndexOf(identifier.C_String());
	if (skhi.IsInvalid())
		return false;

	SLNet::BitStream bsOut;
	bsOut.Write((MessageID)ID_TWO_WAY_AUTHENTICATION_NEGOTIATION);
	bsOut.Write((MessageID)ID_NONCE_REQUEST);
	SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,remoteSystem,false);

	PendingChallenge pc;
	pc.identifier=identifier;
	pc.remoteSystem=remoteSystem;
	pc.time= SLNet::GetTime();
	pc.sentHash=false;
	outgoingChallenges.Push(pc,_FILE_AND_LINE_);

	return true;
}
void TwoWayAuthentication::PushToUser(MessageID messageId, SLNet::RakString password, SLNet::AddressOrGUID remoteSystem)
{
	SLNet::BitStream output;
	output.Write(messageId);
	if (password.IsEmpty()==false)
		output.Write(password);
	Packet *p = AllocatePacketUnified(output.GetNumberOfBytesUsed());
	p->systemAddress=remoteSystem.systemAddress;
	p->systemAddress.systemIndex=(SystemIndex)-1;
	p->guid=remoteSystem.rakNetGuid;
	p->wasGeneratedLocally=true;
	memcpy(p->data, output.GetData(), output.GetNumberOfBytesUsed());
	rakPeerInterface->PushBackPacket(p, true);
}
Exemple #10
0
void TwoWayAuthentication::OnNonceReply(Packet *packet)
{
	SLNet::BitStream bsIn(packet->data, packet->length, false);
	bsIn.IgnoreBytes(sizeof(MessageID)*2);

	char thierNonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH];
	unsigned short requestId;
	bsIn.Read(requestId);
	bsIn.ReadAlignedBytes((unsigned char *) thierNonce,TWO_WAY_AUTHENTICATION_NONCE_LENGTH);

	// Lookup one of the negotiations for this guid/system address
	AddressOrGUID aog(packet);
	unsigned int i;
	for (i=0; i < outgoingChallenges.Size(); i++)
	{
		if (outgoingChallenges[i].remoteSystem==aog && outgoingChallenges[i].sentHash==false)
		{
			outgoingChallenges[i].sentHash=true;

			// Get the password for this identifier
			DataStructures::HashIndex skhi = passwords.GetIndexOf(outgoingChallenges[i].identifier.C_String());
			if (skhi.IsInvalid()==false)
			{
				SLNet::RakString password = passwords.ItemAtIndex(skhi);

				// Hash their nonce with password and reply
				char hashedNonceAndPw[HASHED_NONCE_AND_PW_LENGTH];
				Hash(thierNonce, password, hashedNonceAndPw);

				// Send
				SLNet::BitStream bsOut;
				bsOut.Write((MessageID)ID_TWO_WAY_AUTHENTICATION_NEGOTIATION);
				bsOut.Write((MessageID)ID_HASHED_NONCE_AND_PASSWORD);
				bsOut.Write(requestId);
				bsOut.Write(outgoingChallenges[i].identifier); // Identifier helps the other system lookup the password quickly.
				bsOut.WriteAlignedBytes((const unsigned char*) hashedNonceAndPw,HASHED_NONCE_AND_PW_LENGTH);
				SendUnified(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet,false);
			}

			return;
		}
	}
}
int SendIRIToAddressCB(FileListTransfer::ThreadData threadData, bool *returnOutput, void* perThreadData)
{
	(void) perThreadData;

	FileListTransfer *fileListTransfer = threadData.fileListTransfer;
	SystemAddress systemAddress = threadData.systemAddress;
	unsigned short setId = threadData.setId;
	*returnOutput=false;

	// Was previously using GetStatistics to get outgoing buffer size, but TCP with UnifiedSend doesn't have this
	unsigned int bytesRead;	
	const char *dataBlocks[2];
	int lengths[2];
	unsigned int smallFileTotalSize=0;
	SLNet::BitStream outBitstream;
	unsigned int ftpIndex;

	fileListTransfer->fileToPushRecipientListMutex.Lock();
	for (ftpIndex=0; ftpIndex < fileListTransfer->fileToPushRecipientList.Size(); ftpIndex++)
	{
		FileListTransfer::FileToPushRecipient *ftpr = fileListTransfer->fileToPushRecipientList[ftpIndex];
		// Referenced by both ftpr and list
		ftpr->AddRef();

		fileListTransfer->fileToPushRecipientListMutex.Unlock();

		if (ftpr->systemAddress==systemAddress && ftpr->setId==setId)
		{
			FileListTransfer::FileToPush *ftp;
			////ftpr->filesToPushMutex.Lock();
			ftp = ftpr->filesToPush.Pop();
			////ftpr->filesToPushMutex.Unlock();

			// Read and send chunk. If done, delete at this index
			void *buff = rakMalloc_Ex(ftp->chunkSize, _FILE_AND_LINE_);
			if (buff==0)
			{
				////ftpr->filesToPushMutex.Lock();
				ftpr->filesToPush.PushAtHead(ftp,0,_FILE_AND_LINE_);
				////ftpr->filesToPushMutex.Unlock();

				ftpr->Deref();
				notifyOutOfMemory(_FILE_AND_LINE_);
				return 0;
			}

			// Read the next file chunk
			bytesRead=ftp->incrementalReadInterface->GetFilePart(ftp->fileListNode.fullPathToFile, ftp->currentOffset, ftp->chunkSize, buff, ftp->fileListNode.context);

			bool done = ftp->fileListNode.dataLengthBytes == ftp->currentOffset+bytesRead;
			while (done && ftp->currentOffset==0 && smallFileTotalSize<ftp->chunkSize)
			{
				////ftpr->filesToPushMutex.Lock();
				// The reason for 2 is that ID_FILE_LIST_REFERENCE_PUSH gets ID_FILE_LIST_REFERENCE_PUSH_ACK. WIthout ID_FILE_LIST_REFERENCE_PUSH_ACK, SendIRIToAddressCB would not be called again
				if (ftpr->filesToPush.Size()<2)
				{
					////ftpr->filesToPushMutex.Unlock();
					break;
				}
				////ftpr->filesToPushMutex.Unlock();

				// Send all small files at once, rather than wait for ID_FILE_LIST_REFERENCE_PUSH. But at least one ID_FILE_LIST_REFERENCE_PUSH must be sent
				outBitstream.Reset();
				outBitstream.Write((MessageID)ID_FILE_LIST_TRANSFER_FILE);
				// outBitstream.Write(ftp->fileListNode.context);
				outBitstream << ftp->fileListNode.context;
				outBitstream.Write(setId);
				StringCompressor::Instance()->EncodeString(ftp->fileListNode.filename, 512, &outBitstream);
				outBitstream.WriteCompressed(ftp->setIndex);
				outBitstream.WriteCompressed(ftp->fileListNode.dataLengthBytes); // Original length in bytes
				outBitstream.AlignWriteToByteBoundary();
				dataBlocks[0]=(char*) outBitstream.GetData();
				lengths[0]=outBitstream.GetNumberOfBytesUsed();
				dataBlocks[1]=(const char*) buff;
				lengths[1]=bytesRead;

				fileListTransfer->SendListUnified(dataBlocks,lengths,2,ftp->packetPriority, RELIABLE_ORDERED, ftp->orderingChannel, systemAddress, false);

				// LWS : fixed freed pointer reference
//				unsigned int chunkSize = ftp->chunkSize;
				SLNet::OP_DELETE(ftp,_FILE_AND_LINE_);
				smallFileTotalSize+=bytesRead;
				//done = bytesRead!=ftp->chunkSize;
				////ftpr->filesToPushMutex.Lock();
				ftp = ftpr->filesToPush.Pop();
				////ftpr->filesToPushMutex.Unlock();

				bytesRead=ftp->incrementalReadInterface->GetFilePart(ftp->fileListNode.fullPathToFile, ftp->currentOffset, ftp->chunkSize, buff, ftp->fileListNode.context);
				done = ftp->fileListNode.dataLengthBytes == ftp->currentOffset+bytesRead;
			}


			outBitstream.Reset();
			outBitstream.Write((MessageID)ID_FILE_LIST_REFERENCE_PUSH);
			// outBitstream.Write(ftp->fileListNode.context);
			outBitstream << ftp->fileListNode.context;
			outBitstream.Write(setId);
			StringCompressor::Instance()->EncodeString(ftp->fileListNode.filename, 512, &outBitstream);
			outBitstream.WriteCompressed(ftp->setIndex);
			outBitstream.WriteCompressed(ftp->fileListNode.dataLengthBytes); // Original length in bytes
			outBitstream.WriteCompressed(ftp->currentOffset);
			ftp->currentOffset+=bytesRead;
			outBitstream.WriteCompressed(bytesRead);
			outBitstream.Write(done);

			for (unsigned int flpcIndex=0; flpcIndex < fileListTransfer->fileListProgressCallbacks.Size(); flpcIndex++)
				fileListTransfer->fileListProgressCallbacks[flpcIndex]->OnFilePush(ftp->fileListNode.filename, ftp->fileListNode.fileLengthBytes, ftp->currentOffset-bytesRead, bytesRead, done, systemAddress, setId);

			dataBlocks[0]=(char*) outBitstream.GetData();
			lengths[0]=outBitstream.GetNumberOfBytesUsed();
			dataBlocks[1]=(char*) buff;
			lengths[1]=bytesRead;
			//rakPeerInterface->SendList(dataBlocks,lengths,2,ftp->packetPriority, RELIABLE_ORDERED, ftp->orderingChannel, ftp->systemAddress, false);
			char orderingChannel = ftp->orderingChannel;
			PacketPriority packetPriority = ftp->packetPriority;

			// Mutex state: FileToPushRecipient (ftpr) has AddRef. fileToPushRecipientListMutex not locked.
			if (done)
			{
				// Done
				//unsigned short setId = ftp->setID;
				SLNet::OP_DELETE(ftp,_FILE_AND_LINE_);

				////ftpr->filesToPushMutex.Lock();
				if (ftpr->filesToPush.Size()==0)
				{
					////ftpr->filesToPushMutex.Unlock();

					for (unsigned int flpcIndex=0; flpcIndex < fileListTransfer->fileListProgressCallbacks.Size(); flpcIndex++)
						fileListTransfer->fileListProgressCallbacks[flpcIndex]->OnFilePushesComplete(systemAddress, setId);

					// Remove ftpr from fileToPushRecipientList
					fileListTransfer->RemoveFromList(ftpr);
				}
				else
				{
					////ftpr->filesToPushMutex.Unlock();
				}
			}
			else
			{
				////ftpr->filesToPushMutex.Lock();
				ftpr->filesToPush.PushAtHead(ftp,0,_FILE_AND_LINE_);
				////ftpr->filesToPushMutex.Unlock();
			}
			// ftpr out of scope
			ftpr->Deref();

			// 2/12/2012 Moved this line at after the if (done) block above.
			// See http://www.jenkinssoftware.com/forum/index.php?topic=4768.msg19738#msg19738
			fileListTransfer->SendListUnified(dataBlocks,lengths,2, packetPriority, RELIABLE_ORDERED, orderingChannel, systemAddress, false);

			rakFree_Ex(buff, _FILE_AND_LINE_ );
			return 0;
		}
		else
		{
			ftpr->Deref();
			fileListTransfer->fileToPushRecipientListMutex.Lock();
		}
	}

	fileListTransfer->fileToPushRecipientListMutex.Unlock();

	return 0;
}
void FileListTransfer::OnReferencePush(Packet *packet, bool isTheFullFile)
{
	SLNet::BitStream refPushAck;
	if (isTheFullFile==false)
	{
		// 12/23/09 Why do I care about ID_DOWNLOAD_PROGRESS for reference pushes?
		// 2/16/2012 I care because a reference push is 16 megabytes by default. Also, if it is the last file "if (ftpr->filesToPush.Size()<2)" or total file size exceeds smallFileTotalSize it always sends a reference push.
//		return;
	}

	FileListTransferCBInterface::OnFileStruct onFileStruct;
	SLNet::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 (isTheFullFile==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 flrMemoryBlock
	}

	inBitStream >> onFileStruct.context;
	inBitStream.Read(onFileStruct.setID);

	// This is not a progress notification, it is actually the entire packet
	if (isTheFullFile==true)
	{
		refPushAck.Write((MessageID)ID_FILE_LIST_REFERENCE_PUSH_ACK);
		refPushAck.Write(onFileStruct.setID);
		SendUnified(&refPushAck,HIGH_PRIORITY, RELIABLE, 0, packet->systemAddress, false);
	}

	// inBitStream.Read(onFileStruct.context);
	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::Instance()->DecodeString(onFileStruct.fileName, 512, &inBitStream)==false)
	{
#ifdef _DEBUG
		RakAssert(0);
#endif
		return;
	}

	inBitStream.ReadCompressed(onFileStruct.fileIndex);
	inBitStream.ReadCompressed(onFileStruct.byteLengthOfThisFile);
	unsigned int offset;
	unsigned int chunkLength;
	inBitStream.ReadCompressed(offset);
	inBitStream.ReadCompressed(chunkLength);

	bool lastChunk=false;
	inBitStream.Read(lastChunk);
	bool finished = lastChunk && isTheFullFile;

	if (isTheFullFile==false)
		fileListReceiver->partLength=partLength;

	FLR_MemoryBlock mb;
	if (fileListReceiver->pushedFiles.Has(onFileStruct.fileIndex)==false)
	{
		if (onFileStruct.byteLengthOfThisFile <= SLNET_MAX_RETRIEVABLE_FILESIZE)
			mb.flrMemoryBlock = (char*)rakMalloc_Ex(onFileStruct.byteLengthOfThisFile, _FILE_AND_LINE_);
		else
			mb.flrMemoryBlock = nullptr;
		fileListReceiver->pushedFiles.SetNew(onFileStruct.fileIndex, mb);
	}
	else
	{
		mb=fileListReceiver->pushedFiles.Get(onFileStruct.fileIndex);
	}
	
	unsigned int unreadBits = inBitStream.GetNumberOfUnreadBits();
	unsigned int unreadBytes = BITS_TO_BYTES(unreadBits);
	unsigned int amountToRead;
	if (isTheFullFile)
		amountToRead=chunkLength;
	else
		amountToRead=unreadBytes;

	inBitStream.AlignReadToByteBoundary();

	FileListTransferCBInterface::FileProgressStruct fps;

	if (isTheFullFile)
	{
		if (mb.flrMemoryBlock)
		{
			// Either the very first block, or a subsequent block and allocateIrIDataChunkAutomatically was true for the first block
			memcpy(mb.flrMemoryBlock+offset, inBitStream.GetData()+BITS_TO_BYTES(inBitStream.GetReadOffset()), amountToRead);
			fps.iriDataChunk=mb.flrMemoryBlock+offset;
		}
		else
		{
			// In here mb.flrMemoryBlock is null
			// This means the first block explicitly deallocated the memory, and no blocks will be permanently held by RakNet
			fps.iriDataChunk=(char*) inBitStream.GetData()+BITS_TO_BYTES(inBitStream.GetReadOffset());
		}

		onFileStruct.bytesDownloadedForThisFile=offset+chunkLength;
		fileListReceiver->setTotalDownloadedLength+=chunkLength;
		onFileStruct.bytesDownloadedForThisSet=fileListReceiver->setTotalDownloadedLength;
	}
	else
	{
		onFileStruct.bytesDownloadedForThisFile=offset+partLength*partCount;
		onFileStruct.bytesDownloadedForThisSet=fileListReceiver->setTotalDownloadedLength+partCount*partLength;
		fps.iriDataChunk=(char*) inBitStream.GetData()+BITS_TO_BYTES(inBitStream.GetReadOffset());
	}

	onFileStruct.numberOfFilesInThisSet=fileListReceiver->setCount;
//	onFileStruct.setTotalCompressedTransmissionLength=fileListReceiver->setTotalCompressedTransmissionLength;
	onFileStruct.byteLengthOfThisSet=fileListReceiver->setTotalFinalLength;
	// Note: mb.flrMemoryBlock may be null here
	onFileStruct.fileData=mb.flrMemoryBlock;
	onFileStruct.senderSystemAddress=packet->systemAddress;
	onFileStruct.senderGuid=packet->guid;

	unsigned int totalNotifications;
	unsigned int currentNotificationIndex;
	if (chunkLength==0 || chunkLength==onFileStruct.byteLengthOfThisFile)
		totalNotifications=1;
	else
		totalNotifications = onFileStruct.byteLengthOfThisFile / chunkLength + 1;

	if (chunkLength==0)
		currentNotificationIndex = 0;
	else
		currentNotificationIndex = offset / chunkLength; 

	fps.onFileStruct=&onFileStruct;
	fps.partCount=currentNotificationIndex;
	fps.partTotal=totalNotifications;
	fps.dataChunkLength=amountToRead;
	fps.firstDataChunk=mb.flrMemoryBlock;
	fps.allocateIrIDataChunkAutomatically=true;
	fps.onFileStruct->fileData=mb.flrMemoryBlock;
	fps.iriWriteOffset=offset;
	fps.senderSystemAddress=packet->systemAddress;
	fps.senderGuid=packet->guid;

	if (finished)
	{
		char *oldFileData=fps.onFileStruct->fileData;
		if (fps.partCount==0)
			fps.firstDataChunk=fps.iriDataChunk;
		if (fps.partTotal==1)
			fps.onFileStruct->fileData=fps.iriDataChunk;
		fileListReceiver->downloadHandler->OnFileProgress(&fps);

		// Incremental read interface sent us a file chunk
		// This is the last file chunk we were waiting for to consider the file done
		if (fileListReceiver->downloadHandler->OnFile(&onFileStruct))
			rakFree_Ex(oldFileData, _FILE_AND_LINE_ );
		fileListReceiver->pushedFiles.Delete(onFileStruct.fileIndex);

		fileListReceiver->filesReceived++;

		// If this set is done, free the memory for it.
		if ((int) fileListReceiver->setCount==fileListReceiver->filesReceived)
		{
			FileListTransferCBInterface::DownloadCompleteStruct dcs;
			dcs.setID=fileListReceiver->setID;
			dcs.numberOfFilesInThisSet=fileListReceiver->setCount;
			dcs.byteLengthOfThisSet=fileListReceiver->setTotalFinalLength;
			dcs.senderSystemAddress=packet->systemAddress;
			dcs.senderGuid=packet->guid;

			if (fileListReceiver->downloadHandler->OnDownloadComplete(&dcs)==false)
			{
				fileListReceiver->downloadHandler->OnDereference();
				fileListReceivers.Delete(onFileStruct.setID);
				if (fileListReceiver->deleteDownloadHandler)
					SLNet::OP_DELETE(fileListReceiver->downloadHandler, _FILE_AND_LINE_);
				SLNet::OP_DELETE(fileListReceiver, _FILE_AND_LINE_);
			}
		}
	}
	else
	{
		if (isTheFullFile)
		{
			// 12/23/09 Don't use OnReferencePush anymore, just use OnFileProgress
			fileListReceiver->downloadHandler->OnFileProgress(&fps);

			if (fps.allocateIrIDataChunkAutomatically==false)
			{
				rakFree_Ex(fileListReceiver->pushedFiles.Get(onFileStruct.fileIndex).flrMemoryBlock, _FILE_AND_LINE_ );
				fileListReceiver->pushedFiles.Get(onFileStruct.fileIndex).flrMemoryBlock=0;
			}
		}
		else
		{
			// This is a download progress notification for a file chunk using incremental read interface
			// We don't have all the data for this chunk yet

			totalNotifications = onFileStruct.byteLengthOfThisFile / fileListReceiver->partLength + 1;
			if (isTheFullFile==false)
				currentNotificationIndex = (offset+partCount*fileListReceiver->partLength) / fileListReceiver->partLength ;
			else
				currentNotificationIndex = (offset+chunkLength) / fileListReceiver->partLength ;
			unreadBytes = onFileStruct.byteLengthOfThisFile - ((currentNotificationIndex+1) * fileListReceiver->partLength);
			fps.partCount=currentNotificationIndex;
			fps.partTotal=totalNotifications;

// 2/19/2013 Why was this check here? It prevent smaller progress notifications
//			if (rakPeerInterface)
			{
				// Thus chunk is incomplete
				fps.iriDataChunk=0;

				fileListReceiver->downloadHandler->OnFileProgress(&fps);
			}
		}
	}
}
void FileListTransfer::Send(FileList *fileList, SLNet::RakPeerInterface *rakPeer, SystemAddress recipient, unsigned short setID, PacketPriority priority, char orderingChannel, IncrementalReadInterface *_incrementalReadInterface, unsigned int _chunkSize)
{
	for (unsigned int flpcIndex=0; flpcIndex < fileListProgressCallbacks.Size(); flpcIndex++)
		fileList->AddCallback(fileListProgressCallbacks[flpcIndex]);

	unsigned int i, totalLength;
	SLNet::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.dataLengthBytes;
	}

	// 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 = SLNet::OP_NEW<FileToPush>(_FILE_AND_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_AND_LINE_);
			}
			else
			{
				outBitstream.Reset();
				outBitstream.Write((MessageID)ID_FILE_LIST_TRANSFER_FILE);
				outBitstream << fileList->fileList[i].context;
				// outBitstream.Write(fileList->fileList[i].context);
				outBitstream.Write(setID);
				StringCompressor::Instance()->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;

			fileToPushRecipientListMutex.Lock();
			for (i=0; i < fileToPushRecipientList.Size(); i++)
			{
				if (fileToPushRecipientList[i]->systemAddress==recipient && fileToPushRecipientList[i]->setId==setId)
				{
// 					ftpr=fileToPushRecipientList[i];
// 					ftpr->AddRef();
// 					break;
					RakAssert("setId already in use for this recipient" && 0);
				}
			}
			fileToPushRecipientListMutex.Unlock();

			//if (ftpr==0)
			//{
				ftpr = SLNet::OP_NEW<FileToPushRecipient>(_FILE_AND_LINE_);
				ftpr->systemAddress=recipient;
				ftpr->setId=setID;
				ftpr->refCount=2; // Allocated and in the list
				fileToPushRecipientList.Push(ftpr, _FILE_AND_LINE_);
			//}
			while (filesToPush.IsEmpty()==false)
			{
				////ftpr->filesToPushMutex.Lock();
				ftpr->filesToPush.Push(filesToPush.Pop(), _FILE_AND_LINE_);
				////ftpr->filesToPushMutex.Unlock();
			}
			// ftpr out of scope
			ftpr->Deref();
			SendIRIToAddress(recipient, setID);
			return;
		}
		else
		{
			for (unsigned int flpcIndex=0; flpcIndex < fileListProgressCallbacks.Size(); flpcIndex++)
				fileListProgressCallbacks[flpcIndex]->OnFilePushesComplete(recipient, setID);
		}
	}
	else
	{
		for (unsigned int flpcIndex=0; flpcIndex < fileListProgressCallbacks.Size(); flpcIndex++)
			fileListProgressCallbacks[flpcIndex]->OnFilePushesComplete(recipient, setID);

		if (rakPeer)
			rakPeer->Send(&outBitstream, priority, RELIABLE_ORDERED, orderingChannel, recipient, false);
		else
			SendUnified(&outBitstream, priority, RELIABLE_ORDERED, orderingChannel, recipient, false);
	}
}