1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "nel/misc/types_nl.h"
26 #include "nel/misc/config_file.h"
27 #include "nel/misc/thread.h"
28 #include "nel/misc/progress_callback.h"
30 #include "game_share/bnp_patch.h"
32 #include "login_xdelta.h"
34 extern NLMISC::CConfigFile ConfigFile
;
38 class CScanDataThread
;
40 class CDownloadThread
;
43 // Useful for using an external downloader (BitTorrent) use of interface with CGameDownloader from client_background_rd.exe using as install program
44 // Forward all messages to CRyzomControl that will forward to CGameDownloader (The engine of the install software)
45 class IAsyncDownloader
48 virtual ~IAsyncDownloader(){}
49 virtual void addToDownloadList(const std::string
&/* patchName */, const std::string
&/* sourceName */, uint32
/* timestamp */, const std::string
& /* extractPath */, uint32
/* sevenZipSize */, uint32
/* size */){}
50 virtual void onFileInstall(const std::string
& /* filename */, uint32
/* index */, uint32
/* filecount */){}
51 virtual void onFileInstallFinished(){}
52 virtual void onFileDownloading(const std::string
& /* sourceName */, uint32
/* rate */, uint32
/* fileIndex */, uint32
/* fileCount */, uint64
/* fileSize */, uint64
/* fullSize */){}
53 virtual void onFileInstalling(const std::string
& /* sourceName */, uint32
/* rate */, uint32
/* fileIndex */, uint32
/* fileCount */, uint64
/* fileSize */, uint64
/* fullSize */){}
54 virtual void onFileDownloadFinished(){}
55 virtual void fatalError(const std::string
& /* errorId */, const std::string
& /* param1 */, const std::string
& /* param2 */){}
58 // To define alternate log
59 class IPatchManagerStateListener
62 virtual void setState (bool /* bOutputToLog */, const std::string
&/* ucsNewState */){}
65 // Get Info of file to install
66 class CInstallThreadEntry
69 CInstallThreadEntry(){ Timestamp
= 0; }
70 CInstallThreadEntry(const char* patchName
, const char* sourceName
, uint32 timestamp
, const char* extractPath
, uint32 size
, uint32 sZipFileSize
)
71 :PatchName(patchName
), SourceName(sourceName
), Timestamp(timestamp
),Size(size
),SZipFileSize(sZipFileSize
),ExtractPath(extractPath
) {}
72 std::string PatchName
;
73 std::string SourceName
;
77 std::string ExtractPath
;
82 * class managing the patch system
83 * \author Matthieu 'TrapII' Besson
84 * \author Vianney 'Ace' Lecroart
85 * \author Nevrax France
95 std::string FileName
; // Dest file for the patch
96 std::string SrcFileName
; // Optional src file to which apply patch (empty unless some version of the file, possibly with
97 // all patchs applied, was found in the /unpack directory due to a download by the background downloader)
98 std::vector
<uint32
> Patches
;
99 std::vector
<uint32
> PatcheSizes
;
100 uint32 LastFileDate
; // Used during patching to set the date of the file to the Date of the last patch
101 bool LocalFileToDelete
; // Used during patching to know if we have to delete local file prior to apply patches
102 bool LocalFileExists
;
103 bool Incremental
; // Used during patching to know if we have to xdelta against a ref file
104 std::string ExtractPath
; // Used during patching to extract a bnp file to a specific directory
105 uint32 FinalFileSize
; // just for info, the final size after patch
106 uint32 SZFileSize
; // Size of the SZ file
114 uint32 Size
; // Patch size for this category (all files to patch)
115 sint32 Req
; // -1 or a value in Required cat (used only for optcat)
116 uint32 SZipSize
; // SevenZipSize
117 uint32 FinalFileSize
; // uncompressed SevenZipSize
118 SCat() { Size
= 0; Req
= -1; SZipSize
= 0; FinalFileSize
=0;}
121 std::vector
<SCat
> NonOptCat
;
122 std::vector
<SCat
> ReqCat
;
123 std::vector
<SCat
> OptCat
;
133 // return bitfield of available patchs, indexed by BGDownloader::TDownloadID
134 uint32
getAvailablePatchsBitfield() const;
139 /// Singleton method : Get the unique patch manager instance
140 static CPatchManager
* getInstance()
142 if (_Instance
== NULL
)
143 _Instance
= new CPatchManager();
147 // serverPath is the name where to find all patches, files description and so on
148 // serverVersion is a string describing the version of the description file
149 void init(const std::vector
<std::string
>& patchURIs
, const std::string
&serverPath
, const std::string
&serverVersion
);
151 /// Get the version with the Description file (must be done after init)
152 std::string
getClientVersion ();
154 bool isVerboseLog() { return VerboseLog
; }
156 void setVerboseLog(bool b
) { VerboseLog
= b
; }
158 // Get the string information about what the threads are doing
159 // Return true if the state has changed
160 bool getThreadState (std::string
&state
, std::vector
<std::string
> &stateLog
);
162 /** Get the error message (filled after a patch of check)
163 * May be empty if the cause of error is unknown or unhandled
165 const std::string
&getLastErrorMessage() { return _ErrorMessage
; }
167 // ---------------------
168 // First Part : Checking
169 // ---------------------
171 // start the check of bnp (download description file DescFilename, look at the version of bnps)
172 // init must have been done
173 void startCheckThread(bool includeBackgroundPatch
);
175 // if the checkThread ended and is ok then the getDesc* methods can be called
176 bool isCheckThreadEnded(bool &ok
);
178 // Get all the optional categories to display for patching
179 void getInfoToDisp(SPatchInfo
&piOut
);
181 void forceStopCheckThread();
182 // Used by installation software
183 void startInstallThread(const std::vector
<CInstallThreadEntry
>& entries
);
184 // Used by installation software
185 void startDownloadThread(const std::vector
<CInstallThreadEntry
>& entries
);
186 // ----------------------------------------------
187 // Second Part : Downloading and Applying patches
188 // ----------------------------------------------
190 /** start to patch with a set of files
191 * \param commitPatch When true, the patched version of the file will replace the original file, else the patched version
192 * will be left in the /unpack directory and the previous version in /data will remain unchanged.
193 * The background downloader should set this to false.
196 void startPatchThread(const std::vector
<std::string
> &CategoriesSelected
, bool commitPatch
);
198 // Return true if patch has ended
199 // 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)
200 bool isPatchThreadEnded (bool &ok
);
203 void forceStopPatchThread();
205 bool mustLaunchBatFile() const { return MustLaunchBatFile
; }
206 void createBatchFile(CProductDescriptionForClient
&descFile
, bool wantRyzomRestart
= true, bool useBatchFile
= true);
207 void executeBatchFile();
208 void deleteBatchFile();
211 // TODO : modify it for the second part ...
212 // Maybe we can't get patchs size ... currently, it only returns 1!
214 int getTotalFilesToGet();
215 int getCurrentFilesToGet();
216 int getPatchingSize();
218 // Return % of current file that has been downloaded/patched so far
219 float getCurrentFileProgress() const;
221 bool isDownloadInProgress() { return DownloadInProgress
; }
223 // ----------------------------------------------
224 // ScanData Part : optional task which verify all data
225 // ----------------------------------------------
227 // start the full check of BNP
228 void startScanDataThread();
230 // if the scan data thread has ended
231 bool isScanDataThreadEnded(bool &ok
);
233 // ask to stop the Scan Data thread. NB: for security, the thread will continue to the current scanned file,
234 // then stop. Hence, you must still wait isScanDataThreadEnded() return true
235 void askForStopScanDataThread();
237 // If the scan thread is not running, touch the files with error, and return the number of corrupted files
238 uint
applyScanDataResult();
240 // get the current info Log for data Scan (true if some change from last get, else text is not filled)
241 bool getDataScanLog(std::string
&text
);
243 CProductDescriptionForClient
&getDescFile() { return DescFile
; }
245 NLMISC::IThread
*getCurrThread() const { return Thread
; }
246 // set an external state listener (enable to log) infos
247 void setStateListener(IPatchManagerStateListener
* stateListener
);
249 // set an external downloader (useful for installer using BitTorrent protocole)
250 void setAsyncDownloader(IAsyncDownloader
* asyncDownloader
);
251 // get the external downloader (useful for installer using BitTorrent protocole or NULL)
252 IAsyncDownloader
* getAsyncDownloader() const;
254 // By default the name used is the name of the current executable.
255 // But for external torrent downloader we must set "client_ryzom_rd.exe"
256 void setRyzomFilename(const std::string
& ryzomFilename
) { RyzomFilename
= ryzomFilename
; }
258 // Used when client data is not located in current directory
259 void setClientRootPath(const std::string
& clientRootPath
);
261 // Used by installation software to download a file only if necessary
262 static bool download(const std::string
& patchPath
, const std::string
& sourcePath
,
263 const std::string
& tmpDirectory
, uint32 timestamp
);
264 // Used by installation software to create install.bat file (not used)
265 static bool extract(const std::string
& patchPath
,
266 const std::vector
<std::string
>& sourceFilename
,
267 const std::vector
<std::string
>& extractPath
,
268 const std::string
& updateBatchFilename
,
269 const std::string
& exectName
,
271 // Installation software must be able to indicate if ryzom must be stated at end of installation
272 void setStartRyzomAtEnd(bool startAtEnd
){ _StartRyzomAtEnd
= startAtEnd
; }
273 // Forward message to installation software if needed
274 void fatalError(const std::string
& errorId
, const std::string
& param1
, const std::string
& param2
);
275 bool bnpUnpack(const std::string
&srcBigfile
, const std::string
&dstPath
, std::vector
<std::string
> &vFilenames
);
277 const std::string
& getServerVersion () { return ServerVersion
; }
280 // Methods used by patch & check threads
281 // -------------------------------------
283 friend class CCheckThread
;
284 friend class CPatchThread
;
285 friend class CScanDataThread
;
286 friend class CInstallThread
;
287 friend class CDownloadThread
;
288 friend class CPatchThreadDownloadProgress
;
290 // Set the thread state (called by threads to let us know what they are doing)
291 void setState (bool bOutputToLog
, const std::string
&ucsState
);
294 /// Get the version of the server given during init()
297 /// Read the description file (throw exception if it doesn't exist)
298 void readDescFile(sint32 nVersion
);
300 /// Read the description of the highest client version file found
301 void readClientVersionAndDescFile();
303 void setRWAccess (const std::string
&filename
, bool bThrowException
=true);
305 std::string
deleteFile (const std::string
&filename
, bool bThrowException
=true, bool bWarning
=true);
307 // Rename src to dst throw an exception if we cant
308 void renameFile (const std::string
&src
, const std::string
&dst
);
310 // Accessors used by the patch thread
311 const std::string
&getDescFilename() { return DescFilename
; }
312 const std::string
&getUpdateBatchFilename() { return UpdateBatchFilename
; }
313 const std::string
&getRyzomFilename() { return RyzomFilename
; }
315 CPatchThread
*getPatchThread() { return PatchThread
; }
317 // Get a file from the server and decompress it if zipped
318 void getServerFile (const std::string
&name
, bool bZipped
= false, const std::string
& destName
="", NLMISC::IProgressCallback
*progress
= NULL
);
319 void downloadFileWithCurl (const std::string
&source
, const std::string
&dest
, NLMISC::IProgressCallback
*progress
= NULL
);
320 void downloadFile (const std::string
&source
, const std::string
&dest
, NLMISC::IProgressCallback
*progress
= NULL
);
321 // Decompress zipped file override destination file
322 void decompressFile (const std::string
&filename
);
323 void applyDate (const std::string
&sFilename
, uint32 nDate
);
325 void getPatchFromDesc(SFileToPatch
&ftpOut
, const CBNPFile
&fIn
, bool forceCheckSumTest
);
327 // stop the threads (called when knowing the thread ended)
328 void stopCheckThread();
329 void stopPatchThread();
330 void stopScanDataThread();
332 // add a file to the scan data log
333 void addDataScanLogCorruptedFile(const SFileToPatch
&ftp
);
334 void clearDataScanLog();
335 static void getCorruptedFileInfo(const SFileToPatch
&ftp
, std::string
&sTranslate
);
337 static bool downloadAndUnpack(const std::string
& patchPath
, const std::string
& sourceFilename
, const std::string
& extractPath
, const std::string
& tmpDirectory
, uint32 timestamp
);
338 // Forward message to Installation Software
339 void onFileInstallFinished();
340 void onFileDownloadFinished();
341 void onFileDownloading(const std::string
& sourceName
, uint32 rate
, uint32 fileIndex
, uint32 fileCount
, uint64 fileSize
, uint64 fullSize
);
342 void onFileInstalling(const std::string
& sourceName
, uint32 rate
, uint32 fileIndex
, uint32 fileCount
, uint64 fileSize
, uint64 fullSize
);
346 static int downloadProgressFunc(void *foo
, double t
, double d
, double ultotal
, double ulnow
);
347 static int validateProgress(void *foo
, double t
, double d
, double ultotal
, double ulnow
);
349 class MyPatchingCB
: public CXDeltaPatch::ICallBack
352 std::string patchFilename
;
353 std::string srcFilename
;
355 virtual void progress(float f
);
357 friend class CPatchManager::MyPatchingCB
;
364 /// The singleton's instance
365 static CPatchManager
* _Instance
;
371 CPatchServer(const std::string
& serverPath
)
373 ServerPath
= serverPath
;
376 if (!ServerPath
.empty() && ServerPath
[ServerPath
.size()-1] != '/')
379 std::string::size_type pos
= ServerPath
.find ("@");
380 if (pos
!= std::string::npos
)
381 DisplayedServerPath
= "http://" + ServerPath
.substr (pos
+1);
383 DisplayedServerPath
= ServerPath
;
386 std::string ServerPath
;
387 std::string DisplayedServerPath
;
392 std::vector
<CPatchServer
> PatchServers
;
396 std::string ServerPath
;
397 std::string DisplayedServerPath
;
398 std::string ServerVersion
;
400 bool DownloadInProgress
;
403 std::string DescFilename
;
405 CProductDescriptionForClient DescFile
;
406 std::vector
<SFileToPatch
> FilesToPatch
;
407 std::vector
<std::string
> OptionalCat
;
409 std::string _ErrorMessage
;
412 CPatchThread
*PatchThread
;
413 CCheckThread
*CheckThread
;
414 CScanDataThread
*ScanDataThread
;
415 CInstallThread
*InstallThread
;
416 CDownloadThread
*DownloadThread
;
417 NLMISC::IThread
*Thread
;
423 std::vector
<std::string
> StateLog
;
430 NLMISC::CSynchronized
<CState
> State
;
431 std::string LogSeparator
;
432 std::string CurrentFile
;
435 // Useful pathes and names
437 /// Now deprecated : the launcher is the client ryzom
438 std::string RyzomFilename
;
439 std::string UpdateBatchFilename
;
440 std::string UpgradeBatchFilename
;
442 // Where the client get all delta and desc file
443 std::string ClientPatchPath
; // Temporary path
444 std::string ReadableClientDataPath
; // Where original data can be found
445 std::string WritableClientDataPath
; // Where data can be written
447 /// Output useful information for debugging in the log file
450 bool MustLaunchBatFile
; // And then relaunch ryzom
452 // For Data Scan, list of files with checksum error => should delete them
453 struct CDataScanState
455 std::vector
<SFileToPatch
> FilesWithScanDataError
;
462 typedef NLMISC::CSynchronized
<CDataScanState
> TSyncDataScanState
;
463 TSyncDataScanState DataScanState
;
464 // The date to apply when file is buggy: use default of year 2001, just to have a coherent date
465 enum {DefaultResetDate
= 31 * 366 * 24 * 3600};
466 // The file size of a an empty BNP
467 // If the BNP Header format change, please modify 'EmptyBnpFileSize' as needed
468 enum {EmptyBnpFileSize
= 8};
469 // Use an external downloader: (BitTorrent)
470 IAsyncDownloader
* _AsyncDownloader
;
471 IPatchManagerStateListener
* _StateListener
;// Alternate log
472 bool _StartRyzomAtEnd
;
474 // used by threads to signal error at the end of execution
475 void setErrorMessage(const std::string
&message
);
480 * \author Matthieu 'TrapII' Besson
481 * \author Vianney 'Ace' Lecroart
482 * \author Nevrax France
485 class CCheckThread
: public NLMISC::IRunnable
490 CCheckThread(bool includeBackgroundPatch
);
494 bool Ended
; // true if the thread have ended
495 bool CheckOk
; // true if the check is good
498 int TotalFileToCheck
;
499 int CurrentFileChecked
;
500 bool IncludeBackgroundPatch
;
509 * class managing the patch getting files from server and applying patches
510 * \author Matthieu 'TrapII' Besson
511 * \author Vianney 'Ace' Lecroart
512 * \author Nevrax France
515 class CPatchThread
: public NLMISC::IRunnable
520 bool Ended
; // true if the thread have ended the patch
521 bool PatchOk
; // true if the patch was good
526 CPatchThread(bool commitPatch
);
528 // Clear the list of files to patch
531 // Add files to be patched (avoid multiple time the same file)
532 void add(const CPatchManager::SFileToPatch
&ftp
);
534 int getNbFileToPatch() { return (int)AllFilesToPatch
.size(); }
535 int getCurrentFilePatched() { return (int) floorf(CurrentFilePatched
); }
536 int getPatchingSize() { return PatchSizeProgress
; }
537 float getCurrentFileProgress() const { return fmodf(CurrentFilePatched
, 1.f
); }
541 std::vector
<CPatchManager::SFileToPatch
> AllFilesToPatch
;
542 float CurrentFilePatched
;
543 int PatchSizeProgress
;
547 friend class CPatchThreadDownloadProgress
;
551 void processFile (CPatchManager::SFileToPatch
&rFTP
);
552 bool xDeltaPatch(const std::string
&patch
, const std::string
&src
, const std::string
&out
);
556 * This thread peforms the installation of file that have been downloaded via asynchrous download (BitTorrent)
557 * It unzip, delete obsolete file, extract file from bnp
561 class CInstallThread
: public NLMISC::IRunnable
564 CInstallThread(const std::vector
<CInstallThreadEntry
> entries
):_Entries(entries
){}
566 std::vector
<CInstallThreadEntry
> _Entries
;
569 class CDownloadThread
: public NLMISC::IRunnable
572 CDownloadThread(const std::vector
<CInstallThreadEntry
> entries
):_Entries(entries
){}
574 std::vector
<CInstallThreadEntry
> _Entries
;
578 * This thread perform a checksum check on all existing files registered in the local .idx
579 * \author Lionel Berenguier
580 * \author Nevrax France
583 class CScanDataThread
: public NLMISC::IRunnable
592 // Written by MainThread, read by thread
593 bool AskForCancel
; // true if the main thread ask to cancel the task
595 // Written by thread, read by Main Thread
596 bool Ended
; // true if the thread have ended
597 bool CheckOk
; // true if the check is good
599 int CurrentFileScanned
;
610 /* End of login_patch.h */