netDownload.cpp
Engine/source/sim/netDownload.cpp
Classes:
class
Public Functions
ConsoleDocClass(FileChunkEvent , "@brief Used by <a href="/coding/class/classnetconnection/">NetConnection</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> sending/receiving chunks of <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">data.\n\n</a>" "Not intended <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> game development, <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> editors or internal use <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">only.\n\n</a> " " @internal" )
ConsoleDocClass(FileDownloadRequestEvent , "@brief Used by <a href="/coding/class/classnetconnection/">NetConnection</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> transmitting requests <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> obtain files from server during <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">loading.\n\n</a>" "Not intended <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> game development, <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> editors or internal use <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">only.\n\n</a> " " @internal" )
Detailed Description
Public Functions
ConsoleDocClass(FileChunkEvent , "@brief Used by <a href="/coding/class/classnetconnection/">NetConnection</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> sending/receiving chunks of <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">data.\n\n</a>" "Not intended <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> game development, <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> editors or internal use <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">only.\n\n</a> " " @internal" )
ConsoleDocClass(FileDownloadRequestEvent , "@brief Used by <a href="/coding/class/classnetconnection/">NetConnection</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> transmitting requests <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> obtain files from server during <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">loading.\n\n</a>" "Not intended <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> game development, <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> editors or internal use <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">only.\n\n</a> " " @internal" )
IMPLEMENT_CO_NETEVENT_V1(FileChunkEvent )
IMPLEMENT_CO_NETEVENT_V1(FileDownloadRequestEvent )
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "platform/platform.h" 25#include "core/dnet.h" 26#include "console/simBase.h" 27#include "console/engineAPI.h" 28#include "sim/netConnection.h" 29#include "core/stream/bitStream.h" 30#include "core/stream/fileStream.h" 31#include "sim/netObject.h" 32 33class FileDownloadRequestEvent : public NetEvent 34{ 35public: 36 typedef NetEvent Parent; 37 enum 38 { 39 MaxFileNames = 31, 40 }; 41 42 U32 nameCount; 43 char mFileNames[MaxFileNames][256]; 44 45 FileDownloadRequestEvent(Vector<char*> *nameList = NULL) 46 { 47 nameCount = 0; 48 if(nameList) 49 { 50 nameCount = nameList->size(); 51 52 if(nameCount > MaxFileNames) 53 nameCount = MaxFileNames; 54 55 for(U32 i = 0; i < nameCount; i++) 56 { 57 dStrcpy(mFileNames[i], (*nameList)[i], 256); 58 //Con::printf("Sending request for file %s", mFileNames[i]); 59 } 60 } 61 } 62 63 virtual void pack(NetConnection *, BitStream *bstream) 64 { 65 bstream->writeRangedU32(nameCount, 0, MaxFileNames); 66 for(U32 i = 0; i < nameCount; i++) 67 bstream->writeString(mFileNames[i]); 68 } 69 70 virtual void write(NetConnection *, BitStream *bstream) 71 { 72 bstream->writeRangedU32(nameCount, 0, MaxFileNames); 73 for(U32 i = 0; i < nameCount; i++) 74 bstream->writeString(mFileNames[i]); 75 } 76 77 virtual void unpack(NetConnection *, BitStream *bstream) 78 { 79 nameCount = bstream->readRangedU32(0, MaxFileNames); 80 for(U32 i = 0; i < nameCount; i++) 81 bstream->readString(mFileNames[i]); 82 } 83 84 virtual void process(NetConnection *connection) 85 { 86 U32 i; 87 for(i = 0; i < nameCount; i++) 88 if(connection->startSendingFile(mFileNames[i])) 89 break; 90 if(i == nameCount) 91 connection->startSendingFile(NULL); // none of the files were sent 92 } 93 94 DECLARE_CONOBJECT(FileDownloadRequestEvent); 95 96}; 97 98IMPLEMENT_CO_NETEVENT_V1(FileDownloadRequestEvent); 99 100ConsoleDocClass( FileDownloadRequestEvent, 101 "@brief Used by NetConnection for transmitting requests to obtain files from server during loading.\n\n" 102 "Not intended for game development, for editors or internal use only.\n\n " 103 "@internal"); 104 105class FileChunkEvent : public NetEvent 106{ 107public: 108 typedef NetEvent Parent; 109 enum 110 { 111 ChunkSize = 63, 112 }; 113 114 U8 chunkData[ChunkSize]; 115 U32 chunkLen; 116 117 FileChunkEvent(U8 *data = NULL, U32 len = 0) 118 { 119 if(data) 120 dMemcpy(chunkData, data, len); 121 chunkLen = len; 122 } 123 124 virtual void pack(NetConnection *, BitStream *bstream) 125 { 126 bstream->writeRangedU32(chunkLen, 0, ChunkSize); 127 bstream->write(chunkLen, chunkData); 128 } 129 130 virtual void write(NetConnection *, BitStream *bstream) 131 { 132 bstream->writeRangedU32(chunkLen, 0, ChunkSize); 133 bstream->write(chunkLen, chunkData); 134 } 135 136 virtual void unpack(NetConnection *, BitStream *bstream) 137 { 138 chunkLen = bstream->readRangedU32(0, ChunkSize); 139 bstream->read(chunkLen, chunkData); 140 } 141 142 virtual void process(NetConnection *connection) 143 { 144 connection->chunkReceived(chunkData, chunkLen); 145 } 146 147 virtual void notifyDelivered(NetConnection *nc, bool madeIt) 148 { 149 if(!nc->isRemoved()) 150 nc->sendFileChunk(); 151 } 152 153 DECLARE_CONOBJECT(FileChunkEvent); 154}; 155 156IMPLEMENT_CO_NETEVENT_V1(FileChunkEvent); 157 158ConsoleDocClass( FileChunkEvent, 159 "@brief Used by NetConnection for sending/receiving chunks of data.\n\n" 160 "Not intended for game development, for editors or internal use only.\n\n " 161 "@internal"); 162 163void NetConnection::sendFileChunk() 164{ 165 U8 buffer[FileChunkEvent::ChunkSize]; 166 U32 len = FileChunkEvent::ChunkSize; 167 if(len + mCurrentFileBufferOffset > mCurrentFileBufferSize) 168 len = mCurrentFileBufferSize - mCurrentFileBufferOffset; 169 170 if(!len) 171 { 172 delete mCurrentDownloadingFile; 173 mCurrentDownloadingFile = NULL; 174 return; 175 } 176 177 mCurrentFileBufferOffset += len; 178 mCurrentDownloadingFile->read(len, buffer); 179 postNetEvent(new FileChunkEvent(buffer, len)); 180} 181 182bool NetConnection::startSendingFile(const char *fileName) 183{ 184 if(!fileName || Con::getBoolVariable("$NetConnection::neverUploadFiles")) 185 { 186 sendConnectionMessage(SendNextDownloadRequest); 187 return false; 188 } 189 190 mCurrentDownloadingFile = FileStream::createAndOpen( fileName, Torque::FS::File::Read ); 191 if(!mCurrentDownloadingFile) 192 { 193 // the server didn't have the file, so send a 0 byte chunk: 194 Con::printf("No such file '%s'.", fileName); 195 postNetEvent(new FileChunkEvent(NULL, 0)); 196 return false; 197 } 198 199 Con::printf("Sending file '%s'.", fileName); 200 mCurrentFileBufferSize = mCurrentDownloadingFile->getStreamSize(); 201 mCurrentFileBufferOffset = 0; 202 203 // always have 32 file chunks (64 bytes each) in transit 204 sendConnectionMessage(FileDownloadSizeMessage, mCurrentFileBufferSize); 205 for(U32 i = 0; i < 32; i++) 206 sendFileChunk(); 207 return true; 208} 209 210void NetConnection::sendNextFileDownloadRequest() 211{ 212 // see if we've already downloaded this file... 213 while(mMissingFileList.size() && (Torque::FS::IsFile(mMissingFileList[0]) || Con::getBoolVariable("$NetConnection::neverDownloadFiles"))) 214 { 215 dFree(mMissingFileList[0]); 216 mMissingFileList.pop_front(); 217 } 218 219 if(mMissingFileList.size()) 220 { 221 postNetEvent(new FileDownloadRequestEvent(&mMissingFileList)); 222 } 223 else 224 { 225 fileDownloadSegmentComplete(); 226 } 227} 228 229 230void NetConnection::chunkReceived(U8 *chunkData, U32 chunkLen) 231{ 232 if(chunkLen == 0) 233 { 234 // the server didn't have the file... apparently it's one we don't need... 235 dFree(mCurrentFileBuffer); 236 mCurrentFileBuffer = NULL; 237 dFree(mMissingFileList[0]); 238 mMissingFileList.pop_front(); 239 return; 240 } 241 if(chunkLen + mCurrentFileBufferOffset > mCurrentFileBufferSize) 242 { 243 setLastError("Invalid file chunk from server."); 244 return; 245 } 246 dMemcpy(((U8 *) mCurrentFileBuffer) + mCurrentFileBufferOffset, chunkData, chunkLen); 247 mCurrentFileBufferOffset += chunkLen; 248 if(mCurrentFileBufferOffset == mCurrentFileBufferSize) 249 { 250 // this file's done... 251 // save it to disk: 252 FileStream *stream; 253 254 Con::printf("Saving file %s.", mMissingFileList[0]); 255 if((stream = FileStream::createAndOpen( mMissingFileList[0], Torque::FS::File::Write )) == NULL) 256 { 257 setLastError("Couldn't open file downloaded by server."); 258 return; 259 } 260 261 dFree(mMissingFileList[0]); 262 mMissingFileList.pop_front(); 263 stream->write(mCurrentFileBufferSize, mCurrentFileBuffer); 264 delete stream; 265 mNumDownloadedFiles++; 266 dFree(mCurrentFileBuffer); 267 mCurrentFileBuffer = NULL; 268 sendNextFileDownloadRequest(); 269 } 270 else 271 { 272 Con::executef("onFileChunkReceived", mMissingFileList[0], Con::getIntArg(mCurrentFileBufferOffset), Con::getIntArg(mCurrentFileBufferSize)); 273 } 274} 275 276