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("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]
197 if (!Args
.parse(cmdline
)) return 1;
199 if (!Args
.parse(argc
, argv
)) return 1;
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
;
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
))
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"))
273 #endif // TEST_CRASH_COUNTER
275 HInstance
= hInstance
;
277 // Get the bitmap size
278 HRSRC hrsrc
= FindResource(HInstance
, MAKEINTRESOURCE(IDB_SLASH_SCREEN
), RT_BITMAP
);
280 HGLOBAL hBitmap
= LoadResource (HInstance
, hrsrc
);
282 BITMAP
*bitmap
= (BITMAP
*)LockResource(hBitmap
);
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
);
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
);
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);
303 for (i
=0; i
<files
.size(); i
++)
305 if (toLowerAscii(CFile::getExtension (files
[i
])) == "ttf")
306 CFile::deleteFile (files
[i
]);
311 // TODO for Linux : splashscreen
318 CSteamClient steamClient
;
320 if (steamClient
.init())
321 LoginCustomParameters
= "&steam_auth_session_ticket=" + steamClient
.getAuthSessionTicket();
325 LoginCustomParameters
+= "&dbg=1";
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")
337 RYZOM_CATCH("Pre-Login Init")
339 // Log the client and choose from shard
341 if (!ClientCfg
.Local
&& (ClientCfg
.TestBrowser
|| ClientCfg
.FSHost
.empty()))
343 if (login() == false)
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);
356 // delete all logs and displayers when we're not using logs macros anymore
358 CLog::releaseProcessName();
360 // delete the Nel context
369 RYZOM_TRY("Post-Login Init")
371 RYZOM_CATCH("Post-Login Init")
372 #endif // TEST_CRASH_COUNTER
374 //////////////////////////////////////////
375 // The real main loop
376 //////////////////////////////////////////
377 #ifdef TEST_CRASH_COUNTER
379 #else // TEST_CRASH_COUNTER
381 #endif // TEST_CRASH_COUNTER
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
))
398 RYZOM_CATCH("Connection")
400 ///////////////////////////////
401 // Initialize the main loop. //
402 RYZOM_TRY("Main loop initialisation")
404 RYZOM_CATCH("Main loop initialisation")
406 //////////////////////////////////////////////////
407 // Main loop (biggest part of the application). //
408 RYZOM_TRY("Main loop")
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
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);
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
443 RYZOM_TRY("Releasing")
445 RYZOM_CATCH("Releasing")
447 #if FINAL_VERSION || defined (TEST_CRASH_COUNTER)
449 #endif // FINAL_VERSION
451 // delete all logs and displayers when we're not using logs macros anymore
453 CLog::releaseProcessName();
454 // CCoTask::releaseInstance();
456 // delete the Nel context
460 /// always do a sanity check for early (in the sense of production days) memory coruption detection
461 // _CrtCheckMemory();
464 // EXIT of the Application.