Merge branch '138-toggle-free-look-with-hotkey' into main/gingo-test
[ryzomcore.git] / ryzom / client / src / client.cpp
blob10d5eb5809a893daf6d64f36aaeeee2b95468d61
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2019 Winch Gate Property Limited
3 //
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>
7 //
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/>.
23 #include "stdpch.h"
25 //////////////
26 // INCLUDES //
27 //////////////
28 // Misc.
29 #include "nel/misc/types_nl.h"
31 #ifdef NL_OS_WINDOWS
32 #include <shellapi.h>
33 #else
34 #include <csignal>
35 #endif
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
44 #undef FINAL_VERSION
45 #define FINAL_VERSION 1
46 #endif // TEST_CRASH_COUNTER
48 // Client
49 #include "resource.h"
50 #include "init.h"
51 #include "login.h"
52 #include "login_patch.h"
53 #include "connection.h"
54 #include "init_main_loop.h"
55 #include "main_loop.h"
56 #include "release.h"
57 #include "client_cfg.h"
58 #include "far_tp.h"
59 #include "user_agent.h"
61 #ifdef RZ_USE_STEAM
62 #include "steam_client.h"
63 #endif
65 ///////////
66 // USING //
67 ///////////
68 using namespace std;
69 using namespace NLMISC;
70 using namespace NLNET;
73 // Macros
76 #ifdef DEBUG_NEW
77 #define new DEBUG_NEW
78 #endif
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; }
91 /////////////
92 // GLOBALS //
93 /////////////
94 static uint32 Version = 1; // Client Version.
96 string Cookie;
97 string FSAddr;
100 ///////////////
101 // FUNCTIONS //
102 ///////////////
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 //---------------------------------------------------
118 // MAIN :
119 // Entry for the Application.
120 //---------------------------------------------------
121 #ifdef NL_OS_WINDOWS
123 // enable optimus for NVIDIA cards
124 extern "C"
126 _declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
129 void pump ()
131 // Display the window
132 MSG msg;
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
147 return FALSE;
150 HWND SlashScreen = NULL;
151 HINSTANCE HInstance;
153 int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE /* hPrevInstance */, LPSTR cmdline, int /* nCmdShow */)
154 #else
155 int main(int argc, char **argv)
156 #endif
158 #if defined(_MSC_VER) && defined(_DEBUG)
159 _CrtSetDbgFlag (_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
160 #endif
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;
168 #endif
170 // don't create log.log anymore because client.log is used
171 createDebug(NULL, false);
173 INelContext::getInstance().setWindowedApplication(true);
175 #ifndef NL_DEBUG
176 INelContext::getInstance().getDebugLog()->removeDisplayer("DEFAULT_SD");
177 INelContext::getInstance().getInfoLog()->removeDisplayer("DEFAULT_SD");
178 INelContext::getInstance().getWarningLog()->removeDisplayer("DEFAULT_SD");
179 #endif // NL_DEBUG
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]
197 #ifdef NL_OS_WINDOWS
198 if (!Args.parse(cmdline)) return 1;
199 #else
200 if (!Args.parse(argc, argv)) return 1;
201 #endif
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;
219 // decode password
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))
233 LoginCharsel = -1;
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"))
272 __debugbreak();
274 #endif // TEST_CRASH_COUNTER
276 HInstance = hInstance;
278 // Get the bitmap size
279 HRSRC hrsrc = FindResource(HInstance, MAKEINTRESOURCE(IDB_SLASH_SCREEN), RT_BITMAP);
280 nlassert (hrsrc);
281 HGLOBAL hBitmap = LoadResource (HInstance, hrsrc);
282 nlassert (hBitmap);
283 BITMAP *bitmap = (BITMAP*)LockResource(hBitmap);
284 nlassert (bitmap);
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);
290 RECT rect;
291 RECT rectDesktop;
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);
297 pump ();
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);
303 uint i;
304 for (i=0; i<files.size(); i++)
306 if (toLowerAscii(CFile::getExtension (files[i])) == "ttf")
307 CFile::deleteFile (files[i]);
311 #else
312 // TODO for Linux : splashscreen
313 #endif
315 // initialize log
316 initLog();
318 #ifdef RZ_USE_STEAM
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;
333 #endif
335 #if !FINAL_VERSION
336 LoginCustomParameters += "&dbg=1";
337 #endif
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")
347 prelogInit();
348 RYZOM_CATCH("Pre-Login Init")
350 // Log the client and choose from shard
351 RYZOM_TRY("Login")
352 if (!ClientCfg.Local && (ClientCfg.TestBrowser || ClientCfg.FSHost.empty()))
354 if (login() == false)
356 quitCrashReport ();
358 #if !FINAL_VERSION
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);
363 #endif
365 release();
367 // delete all logs and displayers when we're not using logs macros anymore
368 destroyDebug();
369 CLog::releaseProcessName();
371 // delete the Nel context
372 delete appContext;
374 return EXIT_SUCCESS;
377 RYZOM_CATCH("Login")
379 // Finish inits
380 RYZOM_TRY("Post-Login Init")
381 postlogInit();
382 RYZOM_CATCH("Post-Login Init")
383 #endif // TEST_CRASH_COUNTER
385 //////////////////////////////////////////
386 // The real main loop
387 //////////////////////////////////////////
388 #ifdef TEST_CRASH_COUNTER
389 bool ok = false;
390 #else // TEST_CRASH_COUNTER
391 bool ok = true;
392 #endif // TEST_CRASH_COUNTER
393 while(ok)
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))
406 releaseOutGame();
407 break;
409 RYZOM_CATCH("Connection")
411 ///////////////////////////////
412 // Initialize the main loop. //
413 RYZOM_TRY("Main loop initialisation")
414 initMainLoop();
415 RYZOM_CATCH("Main loop initialisation")
417 //////////////////////////////////////////////////
418 // Main loop (biggest part of the application). //
419 RYZOM_TRY("Main loop")
420 ok = !mainLoop();
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
433 if(ClientCfg.Local)
434 break;
437 #if !FINAL_VERSION
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);
442 #endif
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
453 // Final release
454 RYZOM_TRY("Releasing")
455 release();
456 RYZOM_CATCH("Releasing")
458 #if FINAL_VERSION || defined (TEST_CRASH_COUNTER)
459 quitCrashReport ();
460 #endif // FINAL_VERSION
462 // delete all logs and displayers when we're not using logs macros anymore
463 destroyDebug();
464 CLog::releaseProcessName();
465 // CCoTask::releaseInstance();
467 // delete the Nel context
468 delete appContext;
470 #ifdef NL_OS_WINDOWS
471 /// always do a sanity check for early (in the sense of production days) memory coruption detection
472 // _CrtCheckMemory();
473 #endif
475 // EXIT of the Application.
476 return EXIT_SUCCESS;
478 } // main/WinMain