Linux multi-monitor fullscreen support
[ryzomcore.git] / ryzom / client / src / client.cpp
blob5fb236ad94c5b3a696f572faca65bcb49d594b3e
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("p", "profile", "id", "Use this profile to determine what directory to use by default");
184 Args.addAdditionalArg("login", "Login to use", true, false);
185 Args.addAdditionalArg("password", "Password to use", true, false);
186 Args.addAdditionalArg("shard_id", "Shard ID to use", true, false);
187 Args.addAdditionalArg("slot", "Char slot to use", true, false);
189 #ifdef TEST_CRASH_COUNTER
190 Args.addArg("", "crash", "", "Crash client before init");
191 Args.addArg("", "break", "", "Create a break point");
192 Args.addArg("", "release", "", "Crash client after init");
193 #endif // TEST_CRASH_COUNTER
195 // extract the 2 or 3 first param (argv[1], argv[2] and argv[3]) it must be <login> <password> [shardId]
196 #ifdef NL_OS_WINDOWS
197 if (!Args.parse(cmdline)) return 1;
198 #else
199 if (!Args.parse(argc, argv)) return 1;
200 #endif
202 // no shard id in ring mode
203 std::string sLoginShardId;
205 if (Args.haveAdditionalArg("login"))
207 LoginLogin = Args.getAdditionalArg("login").front();
209 if (Args.haveAdditionalArg("password"))
211 LoginPassword = Args.getAdditionalArg("password").front();
213 // password in hexadecimal
214 if (LoginPassword.compare(0, 2, "0x") == 0)
216 std::string decodedPassword;
218 // decode password
219 if (fromHexa(LoginPassword.substr(2), decodedPassword))
221 // only use it if real hexadecimal
222 LoginPassword = decodedPassword;
226 if (Args.haveAdditionalArg("shard_id"))
227 sLoginShardId = Args.getAdditionalArg("shard_id").front();
229 if (Args.haveAdditionalArg("slot"))
231 if (!fromString(Args.getAdditionalArg("slot").front(), LoginCharsel))
232 LoginCharsel = -1;
237 if (sLoginShardId.empty() || !fromString(sLoginShardId, LoginShardId))
238 LoginShardId = std::numeric_limits<uint32>::max();
240 // if client_default.cfg is not in current directory, use application default directory
241 if (Args.haveArg("p") || !CFile::isExists("client_default.cfg"))
243 std::string currentPath = CPath::getApplicationDirectory("Ryzom");
245 // create parent directory
246 if (!CFile::isExists(currentPath)) CFile::createDirectory(currentPath);
248 // append profile ID to directory
249 if (Args.haveArg("p"))
251 currentPath = NLMISC::CPath::standardizePath(currentPath) + Args.getArg("p").front();
253 if (!CFile::isExists(currentPath)) CFile::createDirectory(currentPath);
256 if (!CPath::setCurrentPath(currentPath)) return 1;
259 #ifdef TEST_CRASH_COUNTER
260 if (Args.haveLongArg("crash"))
262 volatile int toto = *(int*)0;
264 #endif // TEST_CRASH_COUNTER
266 #if defined(NL_OS_WINDOWS)
268 #ifdef TEST_CRASH_COUNTER
269 if (Args.haveLongArg("break"))
271 __debugbreak();
273 #endif // TEST_CRASH_COUNTER
275 HInstance = hInstance;
277 // Get the bitmap size
278 HRSRC hrsrc = FindResource(HInstance, MAKEINTRESOURCE(IDB_SLASH_SCREEN), RT_BITMAP);
279 nlassert (hrsrc);
280 HGLOBAL hBitmap = LoadResource (HInstance, hrsrc);
281 nlassert (hBitmap);
282 BITMAP *bitmap = (BITMAP*)LockResource(hBitmap);
283 nlassert (bitmap);
284 int width = bitmap->bmWidth;
285 int height = bitmap->bmHeight;
287 // Look the command line to see if we have a cookie and a addr
288 SlashScreen = CreateDialog (hInstance, MAKEINTRESOURCE(IDD_SLASH_SCREEN), NULL, MyDialogProc);
289 RECT rect;
290 RECT rectDesktop;
291 GetWindowRect (SlashScreen, &rect);
292 GetWindowRect (GetDesktopWindow (), &rectDesktop);
293 SetWindowPos (SlashScreen, HWND_TOP, (rectDesktop.right-rectDesktop.left-width)/2, (rectDesktop.bottom-rectDesktop.top-height)/2, width, height, 0);
294 ShowWindow (SlashScreen, SW_SHOW);
296 pump ();
298 // Delete all the .ttf file in the /data directory
300 vector<string> files;
301 NLMISC::CPath::getPathContent ("data", false, false, true, files, NULL, true);
302 uint i;
303 for (i=0; i<files.size(); i++)
305 if (toLowerAscii(CFile::getExtension (files[i])) == "ttf")
306 CFile::deleteFile (files[i]);
310 #else
311 // TODO for Linux : splashscreen
312 #endif
314 // initialize log
315 initLog();
317 #ifdef RZ_USE_STEAM
318 CSteamClient steamClient;
320 if (steamClient.init())
321 LoginCustomParameters = "&steam_auth_session_ticket=" + steamClient.getAuthSessionTicket();
322 #endif
324 #if !FINAL_VERSION
325 LoginCustomParameters += "&dbg=1";
326 #endif
328 // initialize patch manager and set the ryzom full path, before it's used
329 CPatchManager *pPM = CPatchManager::getInstance();
330 pPM->setRyzomFilename(Args.getProgramPath() + Args.getProgramName());
332 /////////////////////////////////
333 // Initialize the application. //
334 #ifndef TEST_CRASH_COUNTER
335 RYZOM_TRY("Pre-Login Init")
336 prelogInit();
337 RYZOM_CATCH("Pre-Login Init")
339 // Log the client and choose from shard
340 RYZOM_TRY("Login")
341 if (!ClientCfg.Local && (ClientCfg.TestBrowser || ClientCfg.FSHost.empty()))
343 if (login() == false)
345 quitCrashReport ();
347 #if !FINAL_VERSION
348 // display the file access logger stats and clear out memory allocated for the file access logger
349 //ICommand::execute("iFileAccessLogDisplay",*NLMISC::InfoLog);
350 //ICommand::execute("iFileAccessLogStop",*NLMISC::InfoLog);
351 //ICommand::execute("iFileAccessLogClear",*NLMISC::InfoLog);
352 #endif
354 release();
356 // delete all logs and displayers when we're not using logs macros anymore
357 destroyDebug();
358 CLog::releaseProcessName();
360 // delete the Nel context
361 delete appContext;
363 return EXIT_SUCCESS;
366 RYZOM_CATCH("Login")
368 // Finish inits
369 RYZOM_TRY("Post-Login Init")
370 postlogInit();
371 RYZOM_CATCH("Post-Login Init")
372 #endif // TEST_CRASH_COUNTER
374 //////////////////////////////////////////
375 // The real main loop
376 //////////////////////////////////////////
377 #ifdef TEST_CRASH_COUNTER
378 bool ok = false;
379 #else // TEST_CRASH_COUNTER
380 bool ok = true;
381 #endif // TEST_CRASH_COUNTER
382 while(ok)
384 // hulud : memory snapshot to track reconnection memory leak
385 /* static int pass = 0;
386 string filename = "z_mem_connection_" + toString (pass++) + ".csv";
387 nlverify (NLMEMORY::StatisticsReport (filename.c_str(), true)); */
389 //////////////////////////////////////////
390 // Manage the connection to the server. //
391 RYZOM_TRY("Connection")
392 // If the connection return false we just want to quit the game
393 if(!connection(Cookie, FSAddr))
395 releaseOutGame();
396 break;
398 RYZOM_CATCH("Connection")
400 ///////////////////////////////
401 // Initialize the main loop. //
402 RYZOM_TRY("Main loop initialisation")
403 initMainLoop();
404 RYZOM_CATCH("Main loop initialisation")
406 //////////////////////////////////////////////////
407 // Main loop (biggest part of the application). //
408 RYZOM_TRY("Main loop")
409 ok = !mainLoop();
410 RYZOM_CATCH("Main loop")
412 /////////////////////////////
413 // Release all the memory. //
414 if (!FarTP.isReselectingChar())
416 RYZOM_TRY("Main loop releasing")
417 releaseMainLoop(!ok);
418 RYZOM_CATCH("Main loop releasing")
421 // Offline client quit now
422 if(ClientCfg.Local)
423 break;
426 #if !FINAL_VERSION
427 // display the file access logger stats and clear out memory allocated for the file access logger
428 //ICommand::execute("iFileAccessLogDisplay",*NLMISC::InfoLog);
429 //ICommand::execute("iFileAccessLogStop",*NLMISC::InfoLog);
430 //ICommand::execute("iFileAccessLogClear",*NLMISC::InfoLog);
431 #endif
433 //CFile::createEmptyFile(getLogDirectory() + "during_release");
435 #ifdef TEST_CRASH_COUNTER
436 if (Args.haveLongArg("release"))
438 volatile int toto = *(int*)0;
440 #endif // TEST_CRASH_COUNTER
442 // Final release
443 RYZOM_TRY("Releasing")
444 release();
445 RYZOM_CATCH("Releasing")
447 #if FINAL_VERSION || defined (TEST_CRASH_COUNTER)
448 quitCrashReport ();
449 #endif // FINAL_VERSION
451 // delete all logs and displayers when we're not using logs macros anymore
452 destroyDebug();
453 CLog::releaseProcessName();
454 // CCoTask::releaseInstance();
456 // delete the Nel context
457 delete appContext;
459 #ifdef NL_OS_WINDOWS
460 /// always do a sanity check for early (in the sense of production days) memory coruption detection
461 // _CrtCheckMemory();
462 #endif
464 // EXIT of the Application.
465 return EXIT_SUCCESS;
467 } // main/WinMain