1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2019 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2010 Robert TIMM (rti) <mail@rtti.de>
6 // Copyright (C) 2015-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU Affero General Public License as
10 // published by the Free Software Foundation, either version 3 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Affero General Public License for more details.
18 // You should have received a copy of the GNU Affero General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
29 #include "nel/misc/types_nl.h"
37 #include "nel/misc/debug.h"
38 #include "nel/misc/command.h"
39 #include "nel/net/tcp_sock.h"
40 #include "nel/misc/cmd_args.h"
42 //#define TEST_CRASH_COUNTER
43 #ifdef TEST_CRASH_COUNTER
45 #define FINAL_VERSION 1
46 #endif // TEST_CRASH_COUNTER
52 #include "login_patch.h"
53 #include "connection.h"
54 #include "init_main_loop.h"
55 #include "main_loop.h"
57 #include "client_cfg.h"
59 #include "user_agent.h"
62 #include "steam_client.h"
69 using namespace NLMISC
;
70 using namespace NLNET
;
81 // RYZOM_TRY and RYZOM_CATCH aim is to catch differently in dev and final version
82 // In final version, we catch everything and nlerror the problem to display a NeL message box
83 // In dev version, we just catch EFatalError() and we leave the OS to catch the exception to enable us to cancel/debug it
86 // We don't catch(...) because these exception are already trapped with the se_translation that generate the NeL message box
88 #define RYZOM_TRY(_block) try { nlinfo(_block" of Ryzom...");
89 #define RYZOM_CATCH(_block) nlinfo(_block" of Ryzom success"); } catch(const EFatalError &) { return EXIT_FAILURE; }
94 static uint32 Version
= 1; // Client Version.
104 static CTcpSock CrashCounterSock
;
106 void quitCrashReport ()
108 //if (NLMISC::CFile::fileExists(getLogDirectory() + "ryzom_started"))
109 //CFile::deleteFile (getLogDirectory() + "ryzom_started");
110 // must disconnect now, else could crash at dtor time because nldebug -> access a new INelContext()
111 contReset(CrashCounterSock
);
114 // make it global if other classes/functions want to access to it
115 NLMISC::CCmdArgs Args
;
117 //---------------------------------------------------
119 // Entry for the Application.
120 //---------------------------------------------------
123 // enable optimus for NVIDIA cards
126 _declspec(dllexport
) DWORD NvOptimusEnablement
= 0x00000001;
131 // Display the window
133 while (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
))
135 TranslateMessage(&msg
);
136 DispatchMessage(&msg
);
140 INT_PTR CALLBACK
MyDialogProc(
141 HWND hwndDlg
, // handle to dialog box
142 UINT uMsg
, // message
143 WPARAM wParam
, // first message parameter
144 LPARAM lParam
// second message parameter
150 HWND SlashScreen
= NULL
;
153 int WINAPI
WinMain (HINSTANCE hInstance
, HINSTANCE
/* hPrevInstance */, LPSTR cmdline
, int /* nCmdShow */)
155 int main(int argc
, char **argv
)
158 #if defined(_MSC_VER) && defined(_DEBUG)
159 _CrtSetDbgFlag (_CRTDBG_ALLOC_MEM_DF
| _CRTDBG_LEAK_CHECK_DF
);
162 // init the Nel context
163 CApplicationContext
*appContext
= new CApplicationContext
;
165 // disable nldebug messages in logs in FV
166 #if FINAL_VERSION && defined(NL_RELEASE)
167 DisableNLDebug
= true;
170 // don't create log.log anymore because client.log is used
171 createDebug(NULL
, false);
173 INelContext::getInstance().setWindowedApplication(true);
176 INelContext::getInstance().getDebugLog()->removeDisplayer("DEFAULT_SD");
177 INelContext::getInstance().getInfoLog()->removeDisplayer("DEFAULT_SD");
178 INelContext::getInstance().getWarningLog()->removeDisplayer("DEFAULT_SD");
181 Args
.setVersion(getDisplayVersion());
182 Args
.setDescription("Ryzom client");
183 Args
.addArg("n", "nopatch", "value", "Use this to not use patch system");
184 Args
.addArg("p", "profile", "id", "Use this profile to determine what directory to use by default");
185 Args
.addAdditionalArg("login", "Login to use", true, false);
186 Args
.addAdditionalArg("password", "Password to use", true, false);
187 Args
.addAdditionalArg("shard_id", "Shard ID to use", true, false);
188 Args
.addAdditionalArg("slot", "Char slot to use", true, false);
190 #ifdef TEST_CRASH_COUNTER
191 Args
.addArg("", "crash", "", "Crash client before init");
192 Args
.addArg("", "break", "", "Create a break point");
193 Args
.addArg("", "release", "", "Crash client after init");
194 #endif // TEST_CRASH_COUNTER
196 // extract the 2 or 3 first param (argv[1], argv[2] and argv[3]) it must be <login> <password> [shardId]
198 if (!Args
.parse(cmdline
)) return 1;
200 if (!Args
.parse(argc
, argv
)) return 1;
203 // no shard id in ring mode
204 std::string sLoginShardId
;
206 if (Args
.haveAdditionalArg("login"))
208 LoginLogin
= Args
.getAdditionalArg("login").front();
210 if (Args
.haveAdditionalArg("password"))
212 LoginPassword
= Args
.getAdditionalArg("password").front();
214 // password in hexadecimal
215 if (LoginPassword
.compare(0, 2, "0x") == 0)
217 std::string decodedPassword
;
220 if (fromHexa(LoginPassword
.substr(2), decodedPassword
))
222 // only use it if real hexadecimal
223 LoginPassword
= decodedPassword
;
227 if (Args
.haveAdditionalArg("shard_id"))
228 sLoginShardId
= Args
.getAdditionalArg("shard_id").front();
230 if (Args
.haveAdditionalArg("slot"))
232 if (!fromString(Args
.getAdditionalArg("slot").front(), LoginCharsel
))
238 if (sLoginShardId
.empty() || !fromString(sLoginShardId
, LoginShardId
))
239 LoginShardId
= std::numeric_limits
<uint32
>::max();
241 // if client_default.cfg is not in current directory, use application default directory
242 if (Args
.haveArg("p") || !CFile::isExists("client_default.cfg"))
244 std::string currentPath
= CPath::getApplicationDirectory("Ryzom");
246 // create parent directory
247 if (!CFile::isExists(currentPath
)) CFile::createDirectory(currentPath
);
249 // append profile ID to directory
250 if (Args
.haveArg("p"))
252 currentPath
= NLMISC::CPath::standardizePath(currentPath
) + Args
.getArg("p").front();
254 if (!CFile::isExists(currentPath
)) CFile::createDirectory(currentPath
);
257 if (!CPath::setCurrentPath(currentPath
)) return 1;
260 #ifdef TEST_CRASH_COUNTER
261 if (Args
.haveLongArg("crash"))
263 volatile int toto
= *(int*)0;
265 #endif // TEST_CRASH_COUNTER
267 #if defined(NL_OS_WINDOWS)
269 #ifdef TEST_CRASH_COUNTER
270 if (Args
.haveLongArg("break"))
274 #endif // TEST_CRASH_COUNTER
276 HInstance
= hInstance
;
278 // Get the bitmap size
279 HRSRC hrsrc
= FindResource(HInstance
, MAKEINTRESOURCE(IDB_SLASH_SCREEN
), RT_BITMAP
);
281 HGLOBAL hBitmap
= LoadResource (HInstance
, hrsrc
);
283 BITMAP
*bitmap
= (BITMAP
*)LockResource(hBitmap
);
285 int width
= bitmap
->bmWidth
;
286 int height
= bitmap
->bmHeight
;
288 // Look the command line to see if we have a cookie and a addr
289 SlashScreen
= CreateDialog (hInstance
, MAKEINTRESOURCE(IDD_SLASH_SCREEN
), NULL
, MyDialogProc
);
292 GetWindowRect (SlashScreen
, &rect
);
293 GetWindowRect (GetDesktopWindow (), &rectDesktop
);
294 SetWindowPos (SlashScreen
, HWND_TOP
, (rectDesktop
.right
-rectDesktop
.left
-width
)/2, (rectDesktop
.bottom
-rectDesktop
.top
-height
)/2, width
, height
, 0);
295 ShowWindow (SlashScreen
, SW_SHOW
);
299 // Delete all the .ttf file in the /data directory
301 vector
<string
> files
;
302 NLMISC::CPath::getPathContent ("data", false, false, true, files
, NULL
, true);
304 for (i
=0; i
<files
.size(); i
++)
306 if (toLowerAscii(CFile::getExtension (files
[i
])) == "ttf")
307 CFile::deleteFile (files
[i
]);
312 // TODO for Linux : splashscreen
319 CSteamClient steamClient
;
321 if (steamClient
.init()){
322 LoginCustomParameters
= "&steam_auth_session_ticket=" + steamClient
.getAuthSessionTicket();
323 string steamLanguage
= steamClient
.GameLanguageWebApiFormat();
324 //change language full to right format
326 if (steamLanguage
!= ClientCfg
.LanguageCode
)
328 nlinfo("Force Apply Steam Laguage: %s, current CFG Language %s", steamLanguage
.c_str(), ClientCfg
.LanguageCode
.c_str());
329 ClientCfg
.ForceLanguage
= true;
330 ClientCfg
.LanguageCode
= steamLanguage
;
336 LoginCustomParameters
+= "&dbg=1";
339 // initialize patch manager and set the ryzom full path, before it's used
340 CPatchManager
*pPM
= CPatchManager::getInstance();
341 pPM
->setRyzomFilename(Args
.getProgramPath() + Args
.getProgramName());
343 /////////////////////////////////
344 // Initialize the application. //
345 #ifndef TEST_CRASH_COUNTER
346 RYZOM_TRY("Pre-Login Init")
348 RYZOM_CATCH("Pre-Login Init")
350 // Log the client and choose from shard
352 if (!ClientCfg
.Local
&& (ClientCfg
.TestBrowser
|| ClientCfg
.FSHost
.empty()))
354 if (login() == false)
359 // display the file access logger stats and clear out memory allocated for the file access logger
360 //ICommand::execute("iFileAccessLogDisplay",*NLMISC::InfoLog);
361 //ICommand::execute("iFileAccessLogStop",*NLMISC::InfoLog);
362 //ICommand::execute("iFileAccessLogClear",*NLMISC::InfoLog);
367 // delete all logs and displayers when we're not using logs macros anymore
369 CLog::releaseProcessName();
371 // delete the Nel context
380 RYZOM_TRY("Post-Login Init")
382 RYZOM_CATCH("Post-Login Init")
383 #endif // TEST_CRASH_COUNTER
385 //////////////////////////////////////////
386 // The real main loop
387 //////////////////////////////////////////
388 #ifdef TEST_CRASH_COUNTER
390 #else // TEST_CRASH_COUNTER
392 #endif // TEST_CRASH_COUNTER
395 // hulud : memory snapshot to track reconnection memory leak
396 /* static int pass = 0;
397 string filename = "z_mem_connection_" + toString (pass++) + ".csv";
398 nlverify (NLMEMORY::StatisticsReport (filename.c_str(), true)); */
400 //////////////////////////////////////////
401 // Manage the connection to the server. //
402 RYZOM_TRY("Connection")
403 // If the connection return false we just want to quit the game
404 if(!connection(Cookie
, FSAddr
))
409 RYZOM_CATCH("Connection")
411 ///////////////////////////////
412 // Initialize the main loop. //
413 RYZOM_TRY("Main loop initialisation")
415 RYZOM_CATCH("Main loop initialisation")
417 //////////////////////////////////////////////////
418 // Main loop (biggest part of the application). //
419 RYZOM_TRY("Main loop")
421 RYZOM_CATCH("Main loop")
423 /////////////////////////////
424 // Release all the memory. //
425 if (!FarTP
.isReselectingChar())
427 RYZOM_TRY("Main loop releasing")
428 releaseMainLoop(!ok
);
429 RYZOM_CATCH("Main loop releasing")
432 // Offline client quit now
438 // display the file access logger stats and clear out memory allocated for the file access logger
439 //ICommand::execute("iFileAccessLogDisplay",*NLMISC::InfoLog);
440 //ICommand::execute("iFileAccessLogStop",*NLMISC::InfoLog);
441 //ICommand::execute("iFileAccessLogClear",*NLMISC::InfoLog);
444 //CFile::createEmptyFile(getLogDirectory() + "during_release");
446 #ifdef TEST_CRASH_COUNTER
447 if (Args
.haveLongArg("release"))
449 volatile int toto
= *(int*)0;
451 #endif // TEST_CRASH_COUNTER
454 RYZOM_TRY("Releasing")
456 RYZOM_CATCH("Releasing")
458 #if FINAL_VERSION || defined (TEST_CRASH_COUNTER)
460 #endif // FINAL_VERSION
462 // delete all logs and displayers when we're not using logs macros anymore
464 CLog::releaseProcessName();
465 // CCoTask::releaseInstance();
467 // delete the Nel context
471 /// always do a sanity check for early (in the sense of production days) memory coruption detection
472 // _CrtCheckMemory();
475 // EXIT of the Application.