// Ryzom - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #include "stdpch.h" ////////////// // INCLUDES // ////////////// // Misc. #include "nel/misc/types_nl.h" #if defined(NL_OS_WINDOWS) #include #include #endif #include "nel/misc/debug.h" #include "nel/misc/command.h" #include "nel/net/tcp_sock.h" //#define TEST_CRASH_COUNTER #ifdef TEST_CRASH_COUNTER #include "nel/net/email.h" #undef FINAL_VERSION #define FINAL_VERSION 1 #endif // TEST_CRASH_COUNTER // game share #include "game_share/ryzom_version.h" // Client #include "resource.h" #include "init.h" #include "login.h" #include "login_patch.h" #include "connection.h" #include "init_main_loop.h" #include "main_loop.h" #include "release.h" #include "client_cfg.h" #include "far_tp.h" /////////// // USING // /////////// using namespace std; using namespace NLMISC; using namespace NLNET; // // Macros // // // RYZOM_TRY and RYZOM_CATCH aim is to catch differently in dev and final version // In final version, we catch everything and nlerror the problem to display a NeL message box // In dev version, we just catch EFatalError() and we leave the OS to catch the exception to enable us to cancel/debug it // // We don't catch(...) because these exception are already trapped with the se_translation that generate the NeL message box #define RYZOM_TRY(_block) try { nlinfo(_block" of Ryzom..."); #define RYZOM_CATCH(_block) nlinfo(_block" of Ryzom success"); } catch(EFatalError &) { return EXIT_FAILURE; } ///////////// // GLOBALS // ///////////// static uint32 Version = 1; // Client Version. string Cookie; string FSAddr; /////////////// // FUNCTIONS // /////////////// static CTcpSock CrashCounterSock; void quitCrashReport () { if (NLMISC::CFile::fileExists("ryzom_started")) CFile::deleteFile ("ryzom_started"); // must disconnect now, else could crash at dtor time because nldebug -> access a new INelContext() contReset(CrashCounterSock); } //--------------------------------------------------- // MAIN : // Entry for the Application. //--------------------------------------------------- #ifdef NL_OS_WINDOWS void pump () { // Display the window MSG msg; while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } } INT_PTR CALLBACK MyDialogProc( HWND hwndDlg, // handle to dialog box UINT uMsg, // message WPARAM wParam, // first message parameter LPARAM lParam // second message parameter ) { return FALSE; } HWND SlashScreen = NULL; HINSTANCE HInstance; static bool connect() { string server = "crashcounter.nevrax.com"; if(CrashCounterSock.connected()) return true; try { // add the default port if no port in the cfg if(server.find(':') == string::npos) server+=":80"; CrashCounterSock.connect(CInetAddress(server)); if(!CrashCounterSock.connected()) { nlwarning("Can't connect to web server '%s'", server.c_str()); goto end; } } catch(Exception &e) { nlwarning("Can't connect to web server '%s': %s", server.c_str(), e.what()); goto end; } return true; end: if(CrashCounterSock.connected()) CrashCounterSock.close (); return false; } // *************************************************************************** static bool send(const string &url) { if (CrashCounterSock.connected()) { string buffer = "GET " + url + " HTTP/1.0\n" "Host: crashcounter.nevrax.com\n" "User-agent: Ryzom\n" "\n"; uint32 size = (uint32)buffer.size(); if(!url.empty()) { if(CrashCounterSock.send((uint8 *)buffer.c_str(), size, false) != CSock::Ok) { nlwarning ("Can't send data to the server"); return false; } } return true; } return false; } // *************************************************************************** static bool receive(string &res) { if (CrashCounterSock.connected()) { uint32 size; res = ""; uint8 buf[1024]; for(;;) { size = 1023; if (CrashCounterSock.receive((uint8*)buf, size, false) == CSock::Ok) { buf[1023] = '\0'; res += (char*)buf; //nlinfo("block received '%s'", buf); } else { buf[size] = '\0'; res += (char*)buf; //nlwarning ("server connection closed"); break; } } //nlinfo("all received '%s'", res.c_str()); return true; } else return false; } string CrashFeedback = "CRASHED"; INT_PTR CALLBACK ReportDialogProc( HWND hwndDlg, // handle to dialog box UINT uMsg, // message WPARAM wParam, // first message parameter LPARAM lParam // second message parameter ) { switch (uMsg) { case WM_INITDIALOG: { RECT rect; RECT rectDesktop; GetWindowRect (hwndDlg, &rect); GetWindowRect (GetDesktopWindow (), &rectDesktop); SetWindowPos (hwndDlg, HWND_TOPMOST, (rectDesktop.right-rectDesktop.left-rect.right+rect.left)/2, (rectDesktop.bottom-rectDesktop.top-rect.bottom+rect.top)/2, 0, 0, SWP_NOSIZE); } break; case WM_COMMAND: switch (LOWORD(wParam)) { case FROZEN: CrashFeedback = "USER_FROZEN"; EndDialog (hwndDlg, IDOK); break; case REBOOTED: CrashFeedback = "USER_REBOOTED"; EndDialog (hwndDlg, IDOK); break; case WINDOWED: CrashFeedback = "USER_WINDOWED"; EndDialog (hwndDlg, IDOK); break; case NO_WINDOW: CrashFeedback = "USER_NO_WINDOW"; EndDialog (hwndDlg, IDOK); break; case KILLED: CrashFeedback = "USER_KILLED"; EndDialog (hwndDlg, IDOK); break; case NOT_CRASHED: CrashFeedback = "USER_NOT_CRASHED"; EndDialog (hwndDlg, IDOK); break; case CRASHED: CrashFeedback = "CRASHED"; EndDialog (hwndDlg, IDOK); break; } break; } return FALSE; } void initCrashReport () { // bool crashed = CFile::isExists ("ryzom_started"); bool during_release = false; bool exception_catched = false; bool breakpointed = false; bool dumped = false; bool report_failed = false; bool report_refused = false; bool report_sent = false; if (crashed && CFile::isExists ("during_release")) during_release = CFile::getFileModificationDate ("ryzom_started") <= CFile::getFileModificationDate ("during_release"); if (crashed && CFile::isExists ("exception_catched")) exception_catched = CFile::getFileModificationDate ("ryzom_started") <= CFile::getFileModificationDate ("exception_catched"); if (crashed && CFile::isExists ("breakpointed")) breakpointed = CFile::getFileModificationDate ("ryzom_started") <= CFile::getFileModificationDate ("breakpointed"); if (crashed && CFile::isExists ("nel_debug.dmp")) dumped = CFile::getFileModificationDate ("ryzom_started") <= CFile::getFileModificationDate ("nel_debug.dmp"); if (crashed && CFile::isExists ("report_failed")) report_failed = CFile::getFileModificationDate ("ryzom_started") <= CFile::getFileModificationDate ("report_failed"); if (crashed && CFile::isExists ("report_refused")) report_refused = CFile::getFileModificationDate ("ryzom_started") <= CFile::getFileModificationDate ("report_refused"); if (crashed && CFile::isExists ("report_sent")) report_sent = CFile::getFileModificationDate ("ryzom_started") <= CFile::getFileModificationDate ("report_sent"); FILE *file = fopen ("ryzom_started", "wb"); fclose (file); connect(); if (report_sent) send("/?crashtype=REPORT_SENT"); else if (report_refused) send("/?crashtype=REPORT_REFUSED"); else if (report_failed) send("/?crashtype=REPORT_FAILED"); else if (dumped) send("/?crashtype=DUMPED"); else if (breakpointed) send("/?crashtype=BREAKPOINTED"); else if (exception_catched) send("/?crashtype=EXCEPTION_CATCHED"); else if (during_release) send("/?crashtype=DURING_RELEASE"); else if (crashed) { DialogBox (HInstance, MAKEINTRESOURCE(IDD_CRASH_INFORMATION), NULL, ReportDialogProc); send("/?crashtype="+CrashFeedback); } else send("/?crashtype=NOT_CRASHED"); string res; receive(res); #ifdef TEST_CRASH_COUNTER MessageBox (NULL, res.c_str(), res.c_str(), MB_OK); #endif // TEST_CRASH_COUNTER } int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE /* hPrevInstance */, LPSTR cmdline, int /* nCmdShow */) #else int main(int argc, char **argv) #endif { // init the Nel context CApplicationContext *appContext = new CApplicationContext; // temporary buffer to store Ryzom full path char filename[1024]; #if defined(NL_OS_WINDOWS) /* Windows bug: When the Window IconeMode is in "ThumbNails" mode, the current path is set to "document settings"..... Force the path to be the path of the exe */ { #ifdef NL_FINAL_VERSION char str[4096]; uint len= GetModuleFileName(NULL, str, 4096); if(len && len<4096) { str[len]= 0; string path= CFile::getPath(str); // if(!path.empty()) // CPath::setCurrentPath(path.c_str()); } #endif // NL_FINAL_VERSION } string sCmdLine = cmdline; #if FINAL_VERSION if (sCmdLine.find("/multi") == string::npos) // If '/multi' not found { HANDLE mutex = CreateMutex (NULL, false, "RyzomClient"); if (mutex && GetLastError() == ERROR_ALREADY_EXISTS) exit (0); } initCrashReport (); #endif // FINAL_VERSION // Set default email value for reporting error #ifdef TEST_CRASH_COUNTER initCrashReport (); setReportEmailFunction ((void*)sendEmail); setDefaultEmailParams ("smtp.nevrax.com", "", "hulud@nevrax.com"); if (string(cmdline) == "/crash") volatile int toto = *(int*)0; if (string(cmdline) == "/break") __asm { int 3 }; #endif // TEST_CRASH_COUNTER HInstance = hInstance; // Get the bitmap size HRSRC hrsrc = FindResource(HInstance, MAKEINTRESOURCE(IDB_SLASH_SCREEN), RT_BITMAP); nlassert (hrsrc); HGLOBAL hBitmap = LoadResource (HInstance, hrsrc); nlassert (hBitmap); BITMAP *bitmap = (BITMAP*)LockResource(hBitmap); nlassert (bitmap); int width = bitmap->bmWidth; int height = bitmap->bmHeight; // Look the command line to see if we have a cookie and a addr SlashScreen = CreateDialog (hInstance, MAKEINTRESOURCE(IDD_SLASH_SCREEN), NULL, MyDialogProc); RECT rect; RECT rectDesktop; GetWindowRect (SlashScreen, &rect); GetWindowRect (GetDesktopWindow (), &rectDesktop); SetWindowPos (SlashScreen, HWND_TOP, (rectDesktop.right-rectDesktop.left-width)/2, (rectDesktop.bottom-rectDesktop.top-height)/2, width, height, 0); ShowWindow (SlashScreen, SW_SHOW); pump (); // extract the 2 or 3 first param (argv[1], argv[2] and argv[3]) it must be [shardId] vector res; explode(sCmdLine, std::string(" "), res, true); // no shard id in ring mode if (res.size() >= 3) { LoginLogin = res[0]; LoginPassword = res[1]; if (!fromString(res[2], LoginShardId)) LoginShardId = -1; } else if (res.size() >= 2) { LoginLogin = res[0]; LoginPassword = res[1]; LoginShardId = -1; } GetModuleFileName(GetModuleHandle(NULL), filename, 1024); #else // TODO for Linux : splashscreen if (argc >= 3) { LoginLogin = argv[1]; LoginPassword = argv[2]; if (!fromString(argv[3], LoginShardId)) LoginShardId = -1; } else if (argc >= 2) { LoginLogin = argv[1]; LoginPassword = argv[2]; LoginShardId = -1; } strcpy(filename, argv[0]); #endif // initialize patch manager and set the ryzom full path, before it's used CPatchManager *pPM = CPatchManager::getInstance(); pPM->setRyzomFilename(NLMISC::CFile::getFilename(filename)); // Delete the .bat file because it s not useful anymore if (NLMISC::CFile::fileExists("updt_nl.bat")) NLMISC::CFile::deleteFile("updt_nl.bat"); if (NLMISC::CFile::fileExists("bug_report.exe")) NLMISC::CFile::deleteFile("bug_report.exe"); if (NLMISC::CFile::fileExists("bug_report_r.exe")) NLMISC::CFile::deleteFile("bug_report_r.exe"); if (NLMISC::CFile::fileExists("bug_report_rd.exe")) NLMISC::CFile::deleteFile("bug_report_rd.exe"); if (NLMISC::CFile::fileExists("bug_report_df.exe")) NLMISC::CFile::deleteFile("bug_report_df.exe"); if (NLMISC::CFile::fileExists("bug_report_d.exe")) NLMISC::CFile::deleteFile("bug_report_d.exe"); // Delete all the .ttf file in the /data directory { vector files; NLMISC::CPath::getPathContent ("data", false, false, true, files, NULL, true); uint i; for (i=0; i