Resolve "Toggle Free Look with Hotkey"
[ryzomcore.git] / ryzom / server / src / patchman_service / file_repository.cpp
blob64d0fb00daa1b14276784dcb083f0af960a6c714
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
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.
8 //
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 //-----------------------------------------------------------------------------
18 // includes
19 //-----------------------------------------------------------------------------
21 // nel
22 #include "nel/misc/debug.h"
23 #include "nel/misc/singleton.h"
24 #include "nel/misc/variable.h"
26 // game share
27 #include "game_share/utils.h"
29 // local
30 #include "file_manager.h"
31 #include "file_repository.h"
32 #include "patchman_constants.h"
35 //-------------------------------------------------------------------------------------------------
36 // namespaces
37 //-------------------------------------------------------------------------------------------------
39 using namespace std;
40 using namespace NLMISC;
41 using namespace NLNET;
42 using namespace PATCHMAN;
45 //-------------------------------------------------------------------------------------------------
46 // namespace PATCHMAN
47 //-------------------------------------------------------------------------------------------------
49 namespace PATCHMAN
52 //-----------------------------------------------------------------------------
53 // methods CFileRepository
54 //-----------------------------------------------------------------------------
56 // std::string CFileRepository::buildModuleManifest() const
57 // {
58 // // make sure we've been initialised
59 //// nlassert(_Parent!=NULL);
61 // return "isFileRepository";
62 // }
63 CFileRepository::CFileRepository()
65 _FileRequestCount= 0;
66 _FileInfoCount= 0;
68 _MaxHistorySize= 10;
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);
77 _Parent= 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()
177 return _Directory;
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...
186 resultData.clear();
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);
296 // update our stats
297 ++_FileInfoCount;
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
321 CSString result;
322 bool ok;
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()));
337 else
339 rr.cbFileDataFailure(_Parent,fileName);
342 // update our stats
343 ++_FileRequestCount;
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
431 last=it;
432 ++it;
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)
472 SFileInfo fileInfo;
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);
482 if (args.size()!=0)
483 return false;
485 CFileRepository::rescanPartial();
487 return true;
490 NLMISC_CLASS_COMMAND_IMPL(CFileRepository, fullRescan)
492 // make sure we've been initialised
493 nlassert(_Parent!=NULL);
495 if (args.size()!=0)
496 return false;
498 CFileRepository::rescanFull();
500 return true;
503 NLMISC_CLASS_COMMAND_IMPL(CFileRepository, getFile)
505 // make sure we've been initialised
506 nlassert(_Parent!=NULL);
508 if (args.size()!=1)
509 return true;
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());
515 return true;
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
524 if (args.size()!=1)
525 return true;
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());
540 return true;
543 NLMISC_CLASS_COMMAND_IMPL(CFileRepository, updateFile)
545 // make sure we've been initialised
546 nlassert(_Parent!=NULL);
548 if (args.size()!=1)
549 return true;
551 CFileRepository::updateFile(args[0]);
553 return true;
556 NLMISC_CLASS_COMMAND_IMPL(CFileRepository, MaxHistorySize)
558 // make sure we've been initialised
559 nlassert(_Parent!=NULL);
561 switch(args.size())
563 case 1:
565 uint32 newVal= NLMISC::CSString(args[0]).atoui();
566 if (newVal==0 && args[0]!="0")
567 break;
569 CFileRepository::setMaxHistorySize(newVal);
571 // drop through...
573 case 0:
574 log.displayNL("MaxHistorySize %u",_MaxHistorySize);
575 return true;
578 return false;
581 } // end of namespace