1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 //-----------------------------------------------------------------------------
19 //-----------------------------------------------------------------------------
22 #include "nel/misc/debug.h"
23 #include "nel/misc/singleton.h"
24 #include "nel/misc/variable.h"
27 #include "game_share/utils.h"
30 #include "file_manager.h"
31 #include "file_repository.h"
32 #include "patchman_constants.h"
35 //-------------------------------------------------------------------------------------------------
37 //-------------------------------------------------------------------------------------------------
40 using namespace NLMISC
;
41 using namespace NLNET
;
42 using namespace PATCHMAN
;
45 //-------------------------------------------------------------------------------------------------
47 //-------------------------------------------------------------------------------------------------
52 //-----------------------------------------------------------------------------
53 // methods CFileRepository
54 //-----------------------------------------------------------------------------
56 // std::string CFileRepository::buildModuleManifest() const
58 // // make sure we've been initialised
59 //// nlassert(_Parent!=NULL);
61 // return "isFileRepository";
63 CFileRepository::CFileRepository()
69 _FileRequestHistorySize
= 0;
70 _FileInfoHistorySize
= 0;
73 void CFileRepository::init(NLNET::IModule
* parent
,const NLMISC::CSString
& rootDirectory
)
75 CFileRepositorySkel::init(parent
);
76 _Interceptor
.init(this, parent
);
78 _Directory
= CFileManager::getInstance().getRepositoryDirectory(rootDirectory
);
79 _AdministeredModuleWrapper
.init(dynamic_cast<CAdministeredModuleBase
*>(parent
));
82 void CFileRepository::onModuleUp(IModuleProxy
*module
)
84 // make sure we've been initialised
85 nlassert(_Parent
!=NULL
);
87 if (NLMISC::CSString(module
->getModuleManifest()).contains(ManifestEntryIsFileReceiver
))
89 CFileReceiverProxy
rr(module
);
90 rr
.setupSubscriptions(_Parent
);
91 _AdministeredModuleWrapper
.registerProgress("Receiver up: "+module
->getModuleName());
95 void CFileRepository::onModuleDown(IModuleProxy
*module
)
97 // make sure we've been initialised
98 nlassert(_Parent
!=NULL
);
100 // if the module has subscribed to listen to stuff then end its subscriptions
101 onFileRepositoryModuleDown(module
);
104 void CFileRepository::onFileRepositoryModuleDown(IModuleProxy
*module
)
106 // make sure we've been initialised
107 nlassert(_Parent
!=NULL
);
109 // if the module has subscribed to listen to stuff then end its subscriptions
110 unsubscribeAll(module
);
113 void CFileRepository::onModuleUpdate()
115 H_AUTO(CFileRepository_onModuleUpdate
);
117 // make sure we've been initialised
118 nlassert(_Parent
!=NULL
);
120 // update subscriptions
121 _broadcastFileInfoChanges(_FileInfoChanges
);
123 // clear out the changes container (now that it's been treated)
124 if (!_FileInfoChanges
.empty())
126 _AdministeredModuleWrapper
.registerProgress(NLMISC::toString("updated %d files",_FileInfoChanges
.size()));
127 _FileInfoChanges
.clear();
131 std::string
CFileRepository::buildModuleManifest() const
133 // make sure we've been initialised
134 nlassert(_Parent
!=NULL
);
136 return ManifestEntryIsFileRepository
;
139 void CFileRepository::rescanFull()
141 // make sure we've been initialised
142 nlassert(_Parent
!=NULL
);
144 // delegate to the _Directory object
145 _Directory
->rescanFull(this);
148 void CFileRepository::rescanPartial()
150 // make sure we've been initialised
151 nlassert(_Parent
!=NULL
);
153 // delegate to the _Directory object
154 _Directory
->rescanPartial(this);
157 void CFileRepository::updateFile(const NLMISC::CSString
& fileName
)
159 // make sure we've been initialised
160 nlassert(_Parent
!=NULL
);
162 // delegate to the _Directory object
163 _Directory
->updateFile(fileName
,SFileInfo::RECALCULATE_IF_CHANGED
,this);
166 void CFileRepository::getFileInfo(const NLMISC::CSString
& fileSpec
,TFileInfoVector
& result
,const NLNET::IModuleProxy
*sender
) const
168 // make sure we've been initialised
169 nlassert(_Parent
!=NULL
);
171 // delegate to the _Directory object
172 _Directory
->getFileInfo(fileSpec
,result
,const_cast<CFileRepository
*>(this),sender
);
175 TRepositoryDirectoryPtr
CFileRepository::getRepositoryDirectory()
180 void CFileRepository::getFile(const NLMISC::CSString
& fileName
,NLMISC::CSString
& resultData
,const NLNET::IModuleProxy
* sender
) const
182 // make sure we've been initialised
183 nlassert(_Parent
!=NULL
);
185 // clear out the result container before we begin...
188 // delegate to the _Directory object
189 _Directory
->getFile(fileName
,resultData
,const_cast<CFileRepository
*>(this),sender
);
192 void CFileRepository::dump(NLMISC::CLog
& log
)
194 // make sure we've been initialised
195 nlassert(_Parent
!=NULL
);
197 log
.displayNL("-----------------------------------");
198 log
.displayNL("Recent Info requests (%u in all)",_FileInfoCount
);
199 log
.displayNL("-----------------------------------");
200 for (THistory::iterator it
=_FileInfoHistory
.begin(); it
!=_FileInfoHistory
.end();++it
)
202 log
.displayNL(" '%s'",it
->c_str());
204 log
.displayNL("-----------------------------------");
205 log
.displayNL("Recent Download requests (%u in all)",_FileRequestCount
);
206 log
.displayNL("-----------------------------------");
207 for (THistory::iterator it
=_FileRequestHistory
.begin(); it
!=_FileRequestHistory
.end();++it
)
209 log
.displayNL(" '%s'",it
->c_str());
211 log
.displayNL("-----------------------------------");
212 log
.displayNL("Active Subscriptions");
213 log
.displayNL("-----------------------------------");
214 for (TSubscribers::iterator it
=_Subscribers
.begin(); it
!=_Subscribers
.end();++it
)
216 log
.displayNL(" '%s'",it
->first
.c_str());
220 void CFileRepository::setMaxHistorySize(uint32 maxHistorySize
)
222 // make sure we've been initialised
223 nlassert(_Parent
!=NULL
);
225 _MaxHistorySize
= maxHistorySize
;
227 // if the new limit is bigger than the request history size then prune down the history list
228 while (_FileRequestHistorySize
> _MaxHistorySize
)
230 _FileRequestHistory
.pop_back();
231 --_FileRequestHistorySize
;
234 // if the new limit is bigger than the info history size then prune down the history list
235 while (_FileInfoHistorySize
> _MaxHistorySize
)
237 _FileInfoHistory
.pop_front();
238 --_FileInfoHistorySize
;
242 uint32
CFileRepository::getMaxHistorySize() const
244 // make sure we've been initialised
245 nlassert(_Parent
!=NULL
);
247 return _MaxHistorySize
;
250 void CFileRepository::_broadcastFileInfoChanges(const TFileInfoVector
& fileInfoChanges
)
252 // make sure we've been initialised
253 nlassert(_Parent
!=NULL
);
255 // update subscriptions
256 for (TSubscribers::iterator sit
=_Subscribers
.begin(); sit
!=_Subscribers
.end();++sit
)
258 // setup a filespec object for this subscriber
259 CFileSpec
fileSpec(sit
->first
.splitFrom('@').strip());
261 // build a vector of files that matches the subscription's request
262 TFileInfoVector infoVector
;
264 // run through the changes that we've accumulated since the last update...
265 for (TFileInfoVector::const_iterator cit
=fileInfoChanges
.begin(); cit
!=fileInfoChanges
.end();++cit
)
267 // if this change is for a file that matches the subscriber's filespec then addit to our result vector
268 if (fileSpec
.matches(cit
->FileName
) && cbValidateFileInfoRequest(sit
->second
,cit
->FileName
))
270 infoVector
.push_back(*cit
);
274 // if we found changes that interest this subscriber then dispatch them
275 if (!infoVector
.empty())
277 CFileReceiverProxy
client(sit
->second
);
278 client
.cbFileInfo(_Parent
,infoVector
);
283 void CFileRepository::requestFileInfo(NLNET::IModuleProxy
*sender
,const NLMISC::CSString
& fileSpec
)
285 // make sure we've been initialised
286 nlassert(_Parent
!=NULL
);
288 // setup an object to receive the file info and fill it in
289 TFileInfoVector result
;
290 getFileInfo(fileSpec
,result
,sender
);
292 // get hold of a proxy for the module who sent the request and return the file info to them
293 CFileReceiverProxy
fr(sender
);
294 fr
.cbFileInfo(_Parent
,result
);
299 // add our new history record
300 _FileInfoHistory
.push_front((sender
==NULL
?"<local> ":"<"+sender
->getModuleName()+"> ")+fileSpec
);
301 ++_FileInfoHistorySize
;
303 // if we're grown too big then prune the oldest entry
304 if (_FileInfoHistorySize
>_MaxHistorySize
)
306 _FileInfoHistory
.pop_back();
307 --_FileInfoHistorySize
;
310 // deal with progress / state info
311 _AdministeredModuleWrapper
.registerProgress("File info request: "+sender
->getModuleName()+" "+fileSpec
);
312 _AdministeredModuleWrapper
.setStateVariable("InfoReq",NLMISC::toString(_FileInfoCount
));
315 void CFileRepository::requestFileData(NLNET::IModuleProxy
*sender
, const NLMISC::CSString
&fileName
, uint32 startOffset
, uint32 numBytes
)
317 // make sure we've been initialised
318 nlassert(_Parent
!=NULL
);
320 // load the file chunk that's being requested
324 // allow the overloadable validation callback a chance to prohibit read
325 ok
= cbValidateDownloadRequest(sender
,fileName
);
327 // load the file (if validation was favorable)
328 CSString fullFileName
= _Directory
->getRootDirectory()+fileName
;
329 ok
&= CFileManager::getInstance().load(fullFileName
,startOffset
,numBytes
,result
);
331 // get hold of a proxy for the module who sent the request and return the file info to them
332 CFileReceiverProxy
rr(sender
);
333 if (ok
&& !result
.empty())
335 rr
.cbFileData(_Parent
,fileName
,startOffset
,NLNET::TBinBuffer((const uint8
*)&result
[0],(uint32
)result
.size()));
339 rr
.cbFileDataFailure(_Parent
,fileName
);
345 // add our new history record
346 _FileRequestHistory
.push_front((sender
==NULL
?"<local> ":"<"+sender
->getModuleName()+"> ")+fileName
);
347 ++_FileRequestHistorySize
;
349 // if we're grown too big then prune the oldest entry
350 if (_FileRequestHistorySize
>_MaxHistorySize
)
352 _FileRequestHistory
.pop_back();
353 --_FileRequestHistorySize
;
356 // deal with progress / state info
357 uint32 fileLen
=CFileManager::getInstance().getFileSize(fullFileName
);
358 _AdministeredModuleWrapper
.registerProgress("File data: "+sender
->getModuleName()+" "+fileName
+NLMISC::toString("(%d..%d/%d)",startOffset
,startOffset
+numBytes
,fileLen
));
359 _AdministeredModuleWrapper
.setStateVariable("DataReq",NLMISC::toString(_FileRequestCount
));
361 // set state variables to reflect 'end of file reached'
362 if (fileLen
<startOffset
+numBytes
)
364 _AdministeredModuleWrapper
.registerError("Invalid request from: "+sender
->getModuleName()+" for: '"+fileName
+"'");
365 _AdministeredModuleWrapper
.registerProgress("Failed file: "+fileName
);
367 else if (fileLen
==startOffset
+numBytes
)
369 _AdministeredModuleWrapper
.registerProgress("Finished file: "+fileName
);
373 void CFileRepository::subscribe(NLNET::IModuleProxy
*sender
, const NLMISC::CSString
&fileSpec
)
375 // make sure we've been initialised
376 nlassert(_Parent
!=NULL
);
378 // build the string that we'll use to represent this subscription
379 NLMISC::CSString subscriptionString
= sender
->getModuleName()+" @ "+fileSpec
;
381 // make sure the entry doesn't exist in our subscribers set
382 DROP_IF(_Subscribers
.find(subscriptionString
)!=_Subscribers
.end(),"Ignoring dumplicate request for the same subscription: '"+subscriptionString
+'\'',return);
384 // display a fancy message
385 _AdministeredModuleWrapper
.registerProgress("Subscribe '"+subscriptionString
+"'");
387 // get hold of the info on the requested files as it stands right now
388 TFileInfoVector fileInfoVector
;
389 getFileInfo(fileSpec
,fileInfoVector
,sender
);
391 // dispatch the info to the sender
392 CFileReceiverProxy
client(sender
);
393 client
.cbFileInfo(_Parent
,fileInfoVector
);
395 // add this entry to our subscriptions set
396 _Subscribers
[subscriptionString
]= sender
;
399 void CFileRepository::unsubscribe(NLNET::IModuleProxy
*sender
, const NLMISC::CSString
&fileSpec
)
401 // make sure we've been initialised
402 nlassert(_Parent
!=NULL
);
404 // build the string that we'll use to represent this subscription
405 NLMISC::CSString subscriptionString
= sender
->getModuleName()+" @ "+fileSpec
;
407 // display a fancy message
408 _AdministeredModuleWrapper
.registerProgress("Unsubscribe '"+subscriptionString
+"'");
410 // make sure that this subscription really exists
411 DROP_IF(_Subscribers
.find(subscriptionString
)==_Subscribers
.end(),"Ignoring unsubscribe for unknown subscription: '"+subscriptionString
+'\'',return);
413 // remove the subscription
414 _Subscribers
.erase(subscriptionString
);
417 void CFileRepository::unsubscribeAll(NLNET::IModuleProxy
*sender
)
419 // make sure we've been initialised
420 nlassert(_Parent
!=NULL
);
422 // build the string that this module's subscriptions will contain
423 NLMISC::CSString subscriptionString
= sender
->getModuleName()+" @ ";
425 // iterate over our subscriptions looking for matches to erase
426 TSubscribers::iterator last
;
427 TSubscribers::iterator it
;
428 for (it
=_Subscribers
.begin();it
!=_Subscribers
.end();)
430 // take a copy of the iterator and increment to point at the next element to be processed
434 // see if the last element needs to be erased
435 if (last
->second
==sender
)
437 // delegate to standard 'unsubscribe' to do the work
438 unsubscribe(sender
,last
->first
.splitFrom('@').strip());
443 void CFileRepository::getInfo(NLNET::IModuleProxy
*sender
, const NLMISC::CSString
&fileSpec
)
445 // make sure we've been initialised
446 nlassert(_Parent
!=NULL
);
448 _AdministeredModuleWrapper
.registerProgress("getInfo "+sender
->getModuleName()+" "+fileSpec
);
449 subscribe(sender
,fileSpec
);
450 unsubscribe(sender
,fileSpec
);
453 void CFileRepository::cbFileInfoUpdate(const SFileInfo
& fileInfo
)
455 // make sure we've been initialised
456 nlassert(_Parent
!=NULL
);
458 _AdministeredModuleWrapper
.registerProgress(NLMISC::toString("cbFileInfoUpdate: %s (%d bytes)",fileInfo
.FileName
.c_str(),fileInfo
.FileSize
));
460 // add an entry to our changes list
461 _FileInfoChanges
.push_back(fileInfo
);
464 void CFileRepository::cbFileInfoErased(const NLMISC::CSString
& fileName
)
466 // make sure we've been initialised
467 nlassert(_Parent
!=NULL
);
469 _AdministeredModuleWrapper
.registerProgress("cbFileInfoErased: "+fileName
);
471 // add an entry to our changes list (this is an empty entry with just the filename set to represent a deleted file)
473 fileInfo
.FileName
= fileName
;
474 _FileInfoChanges
.push_back(fileInfo
);
477 NLMISC_CLASS_COMMAND_IMPL(CFileRepository
, incRescan
)
479 // make sure we've been initialised
480 nlassert(_Parent
!=NULL
);
485 CFileRepository::rescanPartial();
490 NLMISC_CLASS_COMMAND_IMPL(CFileRepository
, fullRescan
)
492 // make sure we've been initialised
493 nlassert(_Parent
!=NULL
);
498 CFileRepository::rescanFull();
503 NLMISC_CLASS_COMMAND_IMPL(CFileRepository
, getFile
)
505 // make sure we've been initialised
506 nlassert(_Parent
!=NULL
);
511 NLMISC::CSString data
;
512 CFileRepository::getFile(args
[0],data
,NULL
);
513 log
.displayNL("Retrieved %u bytes of data for file %s, starting: %s",data
.size(), args
[0].c_str(), data
.left(20).quote().c_str());
518 NLMISC_CLASS_COMMAND_IMPL(CFileRepository
, getFileInfo
)
520 // make sure we've been initialised
521 nlassert(_Parent
!=NULL
);
523 // make sure a filespec was given
527 // lookup the set of files
528 TFileInfoVector fileInfoVector
;
529 getFileInfo(args
[0],fileInfoVector
,NULL
);
531 // display a summary info message
532 log
.displayNL("Result of info request '%s': %d matches",args
[0].c_str(),fileInfoVector
.size());
533 log
.displayNL("- %-32s %10s %10s %s","checksum","time","size","name");
535 // iterate over results, displaying the info
536 for (TFileInfoVector::iterator it
= fileInfoVector
.begin(); it
!= fileInfoVector
.end(); ++it
)
538 log
.displayNL("- %-32s %10u %10u %s",it
->Checksum
.toString().c_str(),it
->FileTime
,it
->FileSize
,it
->FileName
.c_str());
543 NLMISC_CLASS_COMMAND_IMPL(CFileRepository
, updateFile
)
545 // make sure we've been initialised
546 nlassert(_Parent
!=NULL
);
551 CFileRepository::updateFile(args
[0]);
556 NLMISC_CLASS_COMMAND_IMPL(CFileRepository
, MaxHistorySize
)
558 // make sure we've been initialised
559 nlassert(_Parent
!=NULL
);
565 uint32 newVal
= NLMISC::CSString(args
[0]).atoui();
566 if (newVal
==0 && args
[0]!="0")
569 CFileRepository::setMaxHistorySize(newVal
);
574 log
.displayNL("MaxHistorySize %u",_MaxHistorySize
);
581 } // end of namespace