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/>.
22 #include "nel/misc/types_nl.h"
23 #include "nel/misc/config_file.h"
24 #include "nel/misc/thread.h"
25 #include "nel/misc/progress_callback.h"
27 #include "game_share/bnp_patch.h"
29 #include "login_xdelta.h"
31 extern NLMISC::CConfigFile ConfigFile
;
35 class CScanDataThread
;
37 class CDownloadThread
;
40 // Useful for using an external downloader (BitTorrent) use of interface with CGameDownloader from client_background_rd.exe using as install program
41 // Forward all messages to CRyzomControl that will forward to CGameDownloader (The engine of the install software)
42 class IAsyncDownloader
45 virtual ~IAsyncDownloader(){}
46 virtual void addToDownloadList(const std::string
&/* patchName */, const std::string
&/* sourceName */, uint32
/* timestamp */, const std::string
& /* extractPath */, uint32
/* sevenZipSize */, uint32
/* size */){}
47 virtual void onFileInstall(const std::string
& /* filename */, uint32
/* index */, uint32
/* filecount */){}
48 virtual void onFileInstallFinished(){}
49 virtual void onFileDownloading(const std::string
& /* sourceName */, uint32
/* rate */, uint32
/* fileIndex */, uint32
/* fileCount */, uint64
/* fileSize */, uint64
/* fullSize */){}
50 virtual void onFileInstalling(const std::string
& /* sourceName */, uint32
/* rate */, uint32
/* fileIndex */, uint32
/* fileCount */, uint64
/* fileSize */, uint64
/* fullSize */){}
51 virtual void onFileDownloadFinished(){}
52 virtual void fatalError(const std::string
& /* errorId */, const std::string
& /* param1 */, const std::string
& /* param2 */){}
55 // To define alternate log
56 class IPatchManagerStateListener
59 virtual void setState (bool /* bOutputToLog */, const std::string
&/* ucsNewState */){}
62 // Get Info of file to install
63 class CInstallThreadEntry
66 CInstallThreadEntry(){ Timestamp
= 0; }
67 CInstallThreadEntry(const char* patchName
, const char* sourceName
, uint32 timestamp
, const char* extractPath
, uint32 size
, uint32 sZipFileSize
)
68 :PatchName(patchName
), SourceName(sourceName
), Timestamp(timestamp
),Size(size
),SZipFileSize(sZipFileSize
),ExtractPath(extractPath
) {}
69 std::string PatchName
;
70 std::string SourceName
;
74 std::string ExtractPath
;
79 * class managing the patch system
80 * \author Matthieu 'TrapII' Besson
81 * \author Vianney 'Ace' Lecroart
82 * \author Nevrax France
92 std::string FileName
; // Dest file for the patch
93 std::string SrcFileName
; // Optional src file to which apply patch (empty unless some version of the file, possibly with
94 // all patchs applied, was found in the /unpack directory due to a download by the background downloader)
95 std::vector
<uint32
> Patches
;
96 std::vector
<uint32
> PatcheSizes
;
97 uint32 LastFileDate
; // Used during patching to set the date of the file to the Date of the last patch
98 bool LocalFileToDelete
; // Used during patching to know if we have to delete local file prior to apply patches
100 bool Incremental
; // Used during patching to know if we have to xdelta against a ref file
101 std::string ExtractPath
; // Used during patching to extract a bnp file to a specific directory
102 uint32 FinalFileSize
; // just for info, the final size after patch
103 uint32 SZFileSize
; // Size of the SZ file
111 uint32 Size
; // Patch size for this category (all files to patch)
112 sint32 Req
; // -1 or a value in Required cat (used only for optcat)
113 uint32 SZipSize
; // SevenZipSize
114 uint32 FinalFileSize
; // uncompressed SevenZipSize
115 SCat() { Size
= 0; Req
= -1; SZipSize
= 0; FinalFileSize
=0;}
118 std::vector
<SCat
> NonOptCat
;
119 std::vector
<SCat
> ReqCat
;
120 std::vector
<SCat
> OptCat
;
130 // return bitfield of available patchs, indexed by BGDownloader::TDownloadID
131 uint32
getAvailablePatchsBitfield() const;
136 /// Singleton method : Get the unique patch manager instance
137 static CPatchManager
* getInstance()
139 if (_Instance
== NULL
)
140 _Instance
= new CPatchManager();
144 // serverPath is the name where to find all patches, files description and so on
145 // serverVersion is a string describing the version of the description file
146 void init(const std::vector
<std::string
>& patchURIs
, const std::string
&serverPath
, const std::string
&serverVersion
);
148 /// Get the version with the Description file (must be done after init)
149 std::string
getClientVersion ();
151 bool isVerboseLog() { return VerboseLog
; }
153 void setVerboseLog(bool b
) { VerboseLog
= b
; }
155 // Get the string information about what the threads are doing
156 // Return true if the state has changed
157 bool getThreadState (std::string
&state
, std::vector
<std::string
> &stateLog
);
159 /** Get the error message (filled after a patch of check)
160 * May be empty if the cause of error is unknown or unhandled
162 const std::string
&getLastErrorMessage() { return _ErrorMessage
; }
164 // ---------------------
165 // First Part : Checking
166 // ---------------------
168 // start the check of bnp (download description file DescFilename, look at the version of bnps)
169 // init must have been done
170 void startCheckThread(bool includeBackgroundPatch
);
172 // if the checkThread ended and is ok then the getDesc* methods can be called
173 bool isCheckThreadEnded(bool &ok
);
175 // Get all the optional categories to display for patching
176 void getInfoToDisp(SPatchInfo
&piOut
);
178 void forceStopCheckThread();
179 // Used by installation software
180 void startInstallThread(const std::vector
<CInstallThreadEntry
>& entries
);
181 // Used by installation software
182 void startDownloadThread(const std::vector
<CInstallThreadEntry
>& entries
);
183 // ----------------------------------------------
184 // Second Part : Downloading and Applying patches
185 // ----------------------------------------------
187 /** start to patch with a set of files
188 * \param commitPatch When true, the patched version of the file will replace the original file, else the patched version
189 * will be left in the /unpack directory and the previous version in /data will remain unchanged.
190 * The background downloader should set this to false.
193 void startPatchThread(const std::vector
<std::string
> &CategoriesSelected
, bool commitPatch
);
195 // Return true if patch has ended
196 // Ok is set to true if the patch is ended and the patch process is ok (if the patch is already ended ok is false)
197 bool isPatchThreadEnded (bool &ok
);
200 void forceStopPatchThread();
202 bool mustLaunchBatFile() const { return MustLaunchBatFile
; }
203 void createBatchFile(CProductDescriptionForClient
&descFile
, bool wantRyzomRestart
= true, bool useBatchFile
= true);
204 void executeBatchFile();
205 void deleteBatchFile();
208 // TODO : modify it for the second part ...
209 // Maybe we can't get patchs size ... currently, it only returns 1!
211 int getTotalFilesToGet();
212 int getCurrentFilesToGet();
213 int getPatchingSize();
215 // Return % of current file that has been downloaded/patched so far
216 float getCurrentFileProgress() const;
218 bool isDownloadInProgress() { return DownloadInProgress
; }
220 // ----------------------------------------------
221 // ScanData Part : optional task which verify all data
222 // ----------------------------------------------
224 // start the full check of BNP
225 void startScanDataThread();
227 // if the scan data thread has ended
228 bool isScanDataThreadEnded(bool &ok
);
230 // ask to stop the Scan Data thread. NB: for security, the thread will continue to the current scanned file,
231 // then stop. Hence, you must still wait isScanDataThreadEnded() return true
232 void askForStopScanDataThread();
234 // If the scan thread is not running, touch the files with error, and return the number of corrupted files
235 uint
applyScanDataResult();
237 // get the current info Log for data Scan (true if some change from last get, else text is not filled)
238 bool getDataScanLog(std::string
&text
);
240 CProductDescriptionForClient
&getDescFile() { return DescFile
; }
242 NLMISC::IThread
*getCurrThread() const { return Thread
; }
243 // set an external state listener (enable to log) infos
244 void setStateListener(IPatchManagerStateListener
* stateListener
);
246 // set an external downloader (useful for installer using BitTorrent protocole)
247 void setAsyncDownloader(IAsyncDownloader
* asyncDownloader
);
248 // get the external downloader (useful for installer using BitTorrent protocole or NULL)
249 IAsyncDownloader
* getAsyncDownloader() const;
251 // By default the name used is the name of the current executable.
252 // But for external torrent downloader we must set "client_ryzom_rd.exe"
253 void setRyzomFilename(const std::string
& ryzomFilename
) { RyzomFilename
= ryzomFilename
; }
255 // Used when client data is not located in current directory
256 void setClientRootPath(const std::string
& clientRootPath
);
258 // Used by installation software to download a file only if necessary
259 static bool download(const std::string
& patchPath
, const std::string
& sourcePath
,
260 const std::string
& tmpDirectory
, uint32 timestamp
);
261 // Used by installation software to create install.bat file (not used)
262 static bool extract(const std::string
& patchPath
,
263 const std::vector
<std::string
>& sourceFilename
,
264 const std::vector
<std::string
>& extractPath
,
265 const std::string
& updateBatchFilename
,
266 const std::string
& exectName
,
268 // Installation software must be able to indicate if ryzom must be stated at end of installation
269 void setStartRyzomAtEnd(bool startAtEnd
){ _StartRyzomAtEnd
= startAtEnd
; }
270 // Forward message to installation software if needed
271 void fatalError(const std::string
& errorId
, const std::string
& param1
, const std::string
& param2
);
273 const std::string
& getServerVersion () { return ServerVersion
; }
276 // Methods used by patch & check threads
277 // -------------------------------------
279 friend class CCheckThread
;
280 friend class CPatchThread
;
281 friend class CScanDataThread
;
282 friend class CInstallThread
;
283 friend class CDownloadThread
;
284 friend class CPatchThreadDownloadProgress
;
286 // Set the thread state (called by threads to let us know what they are doing)
287 void setState (bool bOutputToLog
, const std::string
&ucsState
);
290 /// Get the version of the server given during init()
293 /// Read the description file (throw exception if it doesn't exist)
294 void readDescFile(sint32 nVersion
);
296 /// Read the description of the highest client version file found
297 void readClientVersionAndDescFile();
299 void setRWAccess (const std::string
&filename
, bool bThrowException
=true);
301 std::string
deleteFile (const std::string
&filename
, bool bThrowException
=true, bool bWarning
=true);
303 // Rename src to dst throw an exception if we cant
304 void renameFile (const std::string
&src
, const std::string
&dst
);
306 // Accessors used by the patch thread
307 const std::string
&getDescFilename() { return DescFilename
; }
308 const std::string
&getUpdateBatchFilename() { return UpdateBatchFilename
; }
309 const std::string
&getRyzomFilename() { return RyzomFilename
; }
311 CPatchThread
*getPatchThread() { return PatchThread
; }
313 // Get a file from the server and decompress it if zipped
314 void getServerFile (const std::string
&name
, bool bZipped
= false, const std::string
& destName
="", NLMISC::IProgressCallback
*progress
= NULL
);
315 void downloadFileWithCurl (const std::string
&source
, const std::string
&dest
, NLMISC::IProgressCallback
*progress
= NULL
);
316 void downloadFile (const std::string
&source
, const std::string
&dest
, NLMISC::IProgressCallback
*progress
= NULL
);
317 // Decompress zipped file override destination file
318 void decompressFile (const std::string
&filename
);
319 void applyDate (const std::string
&sFilename
, uint32 nDate
);
321 void getPatchFromDesc(SFileToPatch
&ftpOut
, const CBNPFile
&fIn
, bool forceCheckSumTest
);
323 bool bnpUnpack(const std::string
&srcBigfile
, const std::string
&dstPath
, std::vector
<std::string
> &vFilenames
);
325 // stop the threads (called when knowing the thread ended)
326 void stopCheckThread();
327 void stopPatchThread();
328 void stopScanDataThread();
330 // add a file to the scan data log
331 void addDataScanLogCorruptedFile(const SFileToPatch
&ftp
);
332 void clearDataScanLog();
333 static void getCorruptedFileInfo(const SFileToPatch
&ftp
, std::string
&sTranslate
);
335 static bool downloadAndUnpack(const std::string
& patchPath
, const std::string
& sourceFilename
, const std::string
& extractPath
, const std::string
& tmpDirectory
, uint32 timestamp
);
336 // Forward message to Installation Software
337 void onFileInstallFinished();
338 void onFileDownloadFinished();
339 void onFileDownloading(const std::string
& sourceName
, uint32 rate
, uint32 fileIndex
, uint32 fileCount
, uint64 fileSize
, uint64 fullSize
);
340 void onFileInstalling(const std::string
& sourceName
, uint32 rate
, uint32 fileIndex
, uint32 fileCount
, uint64 fileSize
, uint64 fullSize
);
344 static int downloadProgressFunc(void *foo
, double t
, double d
, double ultotal
, double ulnow
);
345 static int validateProgress(void *foo
, double t
, double d
, double ultotal
, double ulnow
);
347 class MyPatchingCB
: public CXDeltaPatch::ICallBack
350 std::string patchFilename
;
351 std::string srcFilename
;
353 virtual void progress(float f
);
355 friend class CPatchManager::MyPatchingCB
;
362 /// The singleton's instance
363 static CPatchManager
* _Instance
;
369 CPatchServer(const std::string
& serverPath
)
371 ServerPath
= serverPath
;
374 if (!ServerPath
.empty() && ServerPath
[ServerPath
.size()-1] != '/')
377 std::string::size_type pos
= ServerPath
.find ("@");
378 if (pos
!= std::string::npos
)
379 DisplayedServerPath
= "http://" + ServerPath
.substr (pos
+1);
381 DisplayedServerPath
= ServerPath
;
384 std::string ServerPath
;
385 std::string DisplayedServerPath
;
390 std::vector
<CPatchServer
> PatchServers
;
394 std::string ServerPath
;
395 std::string DisplayedServerPath
;
396 std::string ServerVersion
;
398 bool DownloadInProgress
;
401 std::string DescFilename
;
403 CProductDescriptionForClient DescFile
;
404 std::vector
<SFileToPatch
> FilesToPatch
;
405 std::vector
<std::string
> OptionalCat
;
407 std::string _ErrorMessage
;
410 CPatchThread
*PatchThread
;
411 CCheckThread
*CheckThread
;
412 CScanDataThread
*ScanDataThread
;
413 CInstallThread
*InstallThread
;
414 CDownloadThread
*DownloadThread
;
415 NLMISC::IThread
*Thread
;
421 std::vector
<std::string
> StateLog
;
428 NLMISC::CSynchronized
<CState
> State
;
429 std::string LogSeparator
;
430 std::string CurrentFile
;
433 // Useful pathes and names
435 /// Now deprecated : the launcher is the client ryzom
436 std::string RyzomFilename
;
437 std::string UpdateBatchFilename
;
438 std::string UpgradeBatchFilename
;
440 // Where the client get all delta and desc file
441 std::string ClientPatchPath
; // Temporary path
442 std::string ReadableClientDataPath
; // Where original data can be found
443 std::string WritableClientDataPath
; // Where data can be written
445 /// Output useful information for debugging in the log file
448 bool MustLaunchBatFile
; // And then relaunch ryzom
450 // For Data Scan, list of files with checksum error => should delete them
451 struct CDataScanState
453 std::vector
<SFileToPatch
> FilesWithScanDataError
;
460 typedef NLMISC::CSynchronized
<CDataScanState
> TSyncDataScanState
;
461 TSyncDataScanState DataScanState
;
462 // The date to apply when file is buggy: use default of year 2001, just to have a coherent date
463 enum {DefaultResetDate
= 31 * 366 * 24 * 3600};
464 // The file size of a an empty BNP
465 // If the BNP Header format change, please modify 'EmptyBnpFileSize' as needed
466 enum {EmptyBnpFileSize
= 8};
467 // Use an external downloader: (BitTorrent)
468 IAsyncDownloader
* _AsyncDownloader
;
469 IPatchManagerStateListener
* _StateListener
;// Alternate log
470 bool _StartRyzomAtEnd
;
472 // used by threads to signal error at the end of execution
473 void setErrorMessage(const std::string
&message
);
478 * \author Matthieu 'TrapII' Besson
479 * \author Vianney 'Ace' Lecroart
480 * \author Nevrax France
483 class CCheckThread
: public NLMISC::IRunnable
488 CCheckThread(bool includeBackgroundPatch
);
492 bool Ended
; // true if the thread have ended
493 bool CheckOk
; // true if the check is good
496 int TotalFileToCheck
;
497 int CurrentFileChecked
;
498 bool IncludeBackgroundPatch
;
507 * class managing the patch getting files from server and applying patches
508 * \author Matthieu 'TrapII' Besson
509 * \author Vianney 'Ace' Lecroart
510 * \author Nevrax France
513 class CPatchThread
: public NLMISC::IRunnable
518 bool Ended
; // true if the thread have ended the patch
519 bool PatchOk
; // true if the patch was good
524 CPatchThread(bool commitPatch
);
526 // Clear the list of files to patch
529 // Add files to be patched (avoid multiple time the same file)
530 void add(const CPatchManager::SFileToPatch
&ftp
);
532 int getNbFileToPatch() { return (int)AllFilesToPatch
.size(); }
533 int getCurrentFilePatched() { return (int) floorf(CurrentFilePatched
); }
534 int getPatchingSize() { return PatchSizeProgress
; }
535 float getCurrentFileProgress() const { return fmodf(CurrentFilePatched
, 1.f
); }
539 std::vector
<CPatchManager::SFileToPatch
> AllFilesToPatch
;
540 float CurrentFilePatched
;
541 int PatchSizeProgress
;
545 friend class CPatchThreadDownloadProgress
;
549 void processFile (CPatchManager::SFileToPatch
&rFTP
);
550 void xDeltaPatch(const std::string
&patch
, const std::string
&src
, const std::string
&out
);
554 * This thread peforms the installation of file that have been downloaded via asynchrous download (BitTorrent)
555 * It unzip, delete obsolete file, extract file from bnp
559 class CInstallThread
: public NLMISC::IRunnable
562 CInstallThread(const std::vector
<CInstallThreadEntry
> entries
):_Entries(entries
){}
564 std::vector
<CInstallThreadEntry
> _Entries
;
567 class CDownloadThread
: public NLMISC::IRunnable
570 CDownloadThread(const std::vector
<CInstallThreadEntry
> entries
):_Entries(entries
){}
572 std::vector
<CInstallThreadEntry
> _Entries
;
576 * This thread perform a checksum check on all existing files registered in the local .idx
577 * \author Lionel Berenguier
578 * \author Nevrax France
581 class CScanDataThread
: public NLMISC::IRunnable
590 // Written by MainThread, read by thread
591 bool AskForCancel
; // true if the main thread ask to cancel the task
593 // Written by thread, read by Main Thread
594 bool Ended
; // true if the thread have ended
595 bool CheckOk
; // true if the check is good
597 int CurrentFileScanned
;
608 /* End of login_patch.h */