Resolve "Toggle Free Look with Hotkey"
[ryzomcore.git] / ryzom / client / src / far_tp.cpp
blob73dc62fc1d6a0c33f39569aebb93be3ea3634756
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) 2013 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
6 // Copyright (C) 2014-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/>.
21 #include "stdpch.h"
22 #include "nel/3d/u_driver.h"
23 #include "far_tp.h"
24 #include "net_manager.h"
25 #include "connection.h"
26 #include "interface_v3/interface_manager.h"
27 #include "login_patch.h"
28 #include "init_main_loop.h"
29 #include "weather.h"
30 #include "time_client.h"
31 #include "timed_fx_manager.h"
32 #include "world_database_manager.h"
33 #include "continent_manager.h"
34 #include "user_entity.h"
35 #include "entities.h"
36 #include "interface_v3/input_handler_manager.h"
37 #include "interface_v3/bot_chat_page_all.h"
38 #include "sound_manager.h"
39 #include "actions_client.h"
40 #include "r2/editor.h"
41 #include "global.h"
42 #include "release.h"
43 #include "nel/misc/string_conversion.h"
44 #include "debug_client.h"
45 #include "session_browser_impl.h"
46 #include "game_share/security_check.h"
47 #include "client_chat_manager.h"
48 #include "bg_downloader_access.h"
49 #include "login_progress_post_thread.h"
50 #include "interface_v3/action_handler_base.h"
51 #include "item_group_manager.h"
52 #include "nel/misc/cmd_args.h"
54 #ifdef DEBUG_NEW
55 #define new DEBUG_NEW
56 #endif
58 using namespace NLMISC;
59 using namespace NLNET;
60 using namespace NL3D;
61 using namespace RSMGR;
62 using namespace R2;
64 extern CClientChatManager ChatMngr;
65 extern CVariable<uint16> SBSPortOffset;
67 // ***************************************************************************
68 // Login state machine
69 // ***************************************************************************
71 // Adapted from "nel/misc/string_conversion.h"
72 #define NL_BEGIN_CLASS_STRING_CONVERSION_TABLE(__class, __type) \
73 static const NLMISC::CStringConversion<__class::__type>::CPair __type##_nl_string_conversion_table[] = \
75 #define NL_END_CLASS_STRING_CONVERSION_TABLE(__class, __type, __tableName, __defaultValue) \
76 }; \
77 NLMISC::CStringConversion<__class::__type> \
78 __tableName(__type##_nl_string_conversion_table, sizeof(__type##_nl_string_conversion_table) \
79 / sizeof(__type##_nl_string_conversion_table[0]), __class::__defaultValue);
80 #define NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(__class, val) { #val, __class::val},
83 NL_BEGIN_CLASS_STRING_CONVERSION_TABLE (CLoginStateMachine, TState)
84 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, st_start)
85 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, st_login)
86 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, st_auto_login)
87 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, st_shard_list)
88 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, st_start_config)
89 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, st_scan_data)
90 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, st_display_eula)
91 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, st_check_patch)
92 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, st_display_cat)
93 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, st_patch)
94 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, st_close_client)
95 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, st_reboot_screen)
96 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, st_restart_client)
97 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, st_connect)
98 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, st_browser_screen)
99 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, st_ingame)
100 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, st_leave_shard)
101 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, st_enter_far_tp_main_loop)
102 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, st_disconnect)
103 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, st_reconnect_fs)
104 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, st_reconnect_select_char)
105 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, st_reconnect_ready)
106 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, st_exit_global_menu)
107 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, st_reconnect_error)
108 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, st_create_account)
109 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, st_end)
110 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, st_unknown)
111 NL_END_CLASS_STRING_CONVERSION_TABLE(CLoginStateMachine, TState, StateConversion, st_unknown)
113 NL_BEGIN_CLASS_STRING_CONVERSION_TABLE (CLoginStateMachine, TEvent)
114 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, ev_quit)
115 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, ev_skip_all_login)
116 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, ev_init_done)
117 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, ev_game_conf)
118 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, ev_data_scan)
119 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, ev_close_data_scan)
120 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, ev_login_ok)
121 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, ev_bad_login)
122 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, ev_shard_selected)
123 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, ev_patch_needed)
124 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, ev_no_patch)
125 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, ev_run_patch)
126 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, ev_close_patch)
127 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, ev_accept_eula)
128 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, ev_decline_eula)
129 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, ev_reboot)
130 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, ev_ingame_return)
131 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, ev_far_tp_main_loop_entered)
132 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, ev_connect)
133 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, ev_conn_failed)
134 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, ev_conn_dropped)
135 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, ev_enter_game)
136 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, ev_self_reconnected)
137 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, ev_chars_received)
138 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, ev_no_user_char)
139 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, ev_ready_received)
140 // NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, ev_reselect_char)
141 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, ev_reconnect_ok_received)
142 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, ev_global_menu_exited)
143 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, ev_relog)
144 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, ev_create_account)
145 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, ev_close_create_account)
146 NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, ev_unknown)
147 NL_END_CLASS_STRING_CONVERSION_TABLE(CLoginStateMachine, TEvent, EventConversion, ev_unknown)
149 //-----------------------------------------------
150 // toString :
151 //-----------------------------------------------
152 const std::string& CLoginStateMachine::toString(CLoginStateMachine::TState state)
154 return StateConversion.toString(state);
157 //-----------------------------------------------
158 // toString :
159 //-----------------------------------------------
160 const std::string& CLoginStateMachine::toString(CLoginStateMachine::TEvent event)
162 return EventConversion.toString(event);
166 #define SM_BEGIN_EVENT_TABLE \
167 for(;!isTerminationRequested();) \
169 TEvent ev = waitEvent(); \
171 #define SM_END_EVENT_TABLE \
175 #define SM_EVENT(eventId, stateId) \
176 if (ev == eventId) \
178 try \
180 COFile outputF; \
181 string sLog = NLMISC::toString("[%s] %s -> %s\n", CLoginStateMachine::toString(ev).c_str(), CLoginStateMachine::toString(_CurrentState).c_str(), CLoginStateMachine::toString(stateId).c_str()); \
182 if ( outputF.open( getLogDirectory() + "error_join.log", true, true ) ) \
184 outputF.serialBuffer( (uint8*)(&sLog[0]), (uint)sLog.size() ); \
185 outputF.close(); \
188 catch (const Exception &) \
189 {} \
190 _CurrentState = stateId; \
191 break; \
195 #define SM_EVENT(eventId, stateId) \
196 if (ev == eventId) \
198 _CurrentState = stateId; \
199 break; \
202 extern std::string LoginLogin, LoginPassword, LoginCustomParameters;
203 extern bool noUserChar;
204 extern bool userChar;
205 extern bool serverReceivedReady;
206 extern bool CharNameValidArrived;
207 extern bool FirstFrame;
208 extern bool IsInRingSession;
210 extern void selectTipsOfTheDay (uint tips);
211 #define BAR_STEP_TP 2
213 extern NLMISC::CCmdArgs Args;
215 CLoginStateMachine::TEvent CLoginStateMachine::waitEvent()
217 nlassert(CCoTask::getCurrentTask() == this);
219 while (_NextEvents.empty() && !isTerminationRequested())
221 // wait until someone push an event on the state machine
222 yield();
224 if (isTerminationRequested())
225 return ev_quit;
228 TEvent ev = _NextEvents.front();
229 _NextEvents.pop_front();
231 return ev;
234 void CLoginStateMachine::pushEvent(TEvent eventId)
236 // set the next event
237 _NextEvents.push_back(eventId);
239 if (CCoTask::getCurrentTask() != this)
241 /// resume the state machine to advance state
242 resume();
247 void CLoginStateMachine::run()
249 // state machine coroutine
251 while (_CurrentState != st_end && !isTerminationRequested())
253 switch(_CurrentState)
255 case st_start:
256 /// initial state
258 if (!ClientCfg.TestBrowser)
260 if (LoginPassword.empty())
262 if (!LoginCustomParameters.empty() && LoginLogin.empty())
264 // alternate login procedure
265 SM_BEGIN_EVENT_TABLE
266 SM_EVENT(ev_init_done, st_alt_login);
267 SM_EVENT(ev_skip_all_login, st_ingame);
268 SM_EVENT(ev_quit, st_end);
269 SM_END_EVENT_TABLE
271 else
273 // standard procedure
274 SM_BEGIN_EVENT_TABLE
275 SM_EVENT(ev_init_done, st_login);
276 SM_EVENT(ev_skip_all_login, st_ingame);
277 SM_EVENT(ev_quit, st_end);
278 SM_END_EVENT_TABLE
281 else
283 // login 2, bypass the login screen
284 SM_BEGIN_EVENT_TABLE
285 SM_EVENT(ev_init_done, st_auto_login);
286 SM_EVENT(ev_skip_all_login, st_ingame);
287 SM_EVENT(ev_quit, st_end);
288 SM_END_EVENT_TABLE
292 // else
293 // {
294 // // browser test mode
295 // SM_BEGIN_EVENT_TABLE
296 // SM_EVENT(ev_init_done, st_browser_screen);
297 // SM_EVENT(ev_quit, st_end);
298 // SM_END_EVENT_TABLE
299 // }
301 break;
302 case st_login:
303 /// display login screen and options
305 initLoginScreen();
307 if (ClientCfg.R2Mode)
309 // r2 mode
310 SM_BEGIN_EVENT_TABLE
311 SM_EVENT(ev_login_ok, st_check_patch);
312 SM_EVENT(ev_game_conf, st_start_config);
313 SM_EVENT(ev_data_scan, st_scan_data);
314 SM_EVENT(ev_create_account, st_create_account);
315 SM_EVENT(ev_bad_login, st_login);
316 SM_EVENT(ev_quit, st_end);
317 SM_END_EVENT_TABLE
319 else
321 // legacy mode
322 SM_BEGIN_EVENT_TABLE
323 SM_EVENT(ev_login_ok, st_shard_list);
324 SM_EVENT(ev_game_conf, st_start_config);
325 SM_EVENT(ev_data_scan, st_scan_data);
326 SM_EVENT(ev_create_account, st_create_account);
327 SM_EVENT(ev_bad_login, st_login);
328 SM_EVENT(ev_quit, st_end);
329 SM_END_EVENT_TABLE
332 break;
333 case st_auto_login:
334 initAutoLogin();
336 // if (ClientCfg.R2Mode)
338 // r2 mode
339 SM_BEGIN_EVENT_TABLE
340 SM_EVENT(ev_login_ok, st_check_patch);
341 SM_EVENT(ev_bad_login, st_display_error);
342 SM_EVENT(ev_quit, st_end);
343 SM_END_EVENT_TABLE
345 // else
346 // {
347 // // legacy mode
348 // SM_BEGIN_EVENT_TABLE
349 // SM_EVENT(ev_login_ok, st_check_patch);
350 // SM_EVENT(ev_quit, st_end);
351 // SM_END_EVENT_TABLE
352 // }
353 break;
354 case st_alt_login:
355 initAltLogin();
357 // if (ClientCfg.R2Mode)
359 // r2 mode
360 SM_BEGIN_EVENT_TABLE
361 SM_EVENT(ev_login_not_alt, st_login);
362 SM_EVENT(ev_login_ok, st_check_patch);
363 SM_EVENT(ev_quit, st_end);
364 SM_END_EVENT_TABLE
366 // else
367 // {
368 // // legacy mode
369 // SM_BEGIN_EVENT_TABLE
370 // SM_EVENT(ev_login_ok, st_check_patch);
371 // SM_EVENT(ev_quit, st_end);
372 // SM_END_EVENT_TABLE
373 // }
374 break;
375 case st_shard_list:
376 /// display the shard list
377 initShardDisplay();
379 SM_BEGIN_EVENT_TABLE
380 SM_EVENT(ev_shard_selected, st_check_patch);
381 SM_EVENT(ev_quit, st_end);
382 SM_END_EVENT_TABLE
384 break;
385 case st_start_config:
386 /// launch the configurator and close ryzom
387 break;
388 case st_scan_data:
389 /// run the check data thread
390 initDataScan();
392 SM_BEGIN_EVENT_TABLE
393 SM_EVENT(ev_close_data_scan, st_login);
394 SM_EVENT(ev_quit, st_end);
395 SM_END_EVENT_TABLE
397 break;
398 case st_create_account:
400 if (initCreateAccount())
402 SM_BEGIN_EVENT_TABLE
403 SM_EVENT(ev_login_ok, st_check_patch);
404 SM_EVENT(ev_close_create_account, st_login);
405 SM_EVENT(ev_quit, st_end);
406 SM_END_EVENT_TABLE
408 else
410 // return to login menu if an error occurred
411 _CurrentState = st_login;
414 break;
415 case st_display_eula:
417 /// display the eula and wait for validation
419 if (ClientCfg.SkipEULA)
421 // we don't want to see eula, auto accept
422 pushEvent(ev_accept_eula);
425 bool mustReboot = false;
427 #ifdef RYZOM_BG_DOWNLOADER
428 if (isBGDownloadEnabled())
430 mustReboot = CBGDownloaderAccess::getInstance().mustLaunchBatFile();
432 else
433 #endif
435 mustReboot = CPatchManager::getInstance()->mustLaunchBatFile();
438 if (mustReboot)
440 // skip eula and show reboot screen
441 _CurrentState = st_reboot_screen;
442 break;
445 initEula();
447 // Login sequence was reordered
448 // if (ClientCfg.R2Mode)
449 // {
450 // // ring mode
451 // SM_BEGIN_EVENT_TABLE
452 // SM_EVENT(ev_accept_eula, st_browser_screen);
453 // SM_EVENT(ev_quit, st_end);
454 // SM_END_EVENT_TABLE
455 // }
456 // else
457 // {
458 // // legacy mode
459 SM_BEGIN_EVENT_TABLE
460 SM_EVENT(ev_accept_eula, st_connect);
461 SM_EVENT(ev_quit, st_end);
462 SM_END_EVENT_TABLE
463 // }
465 break;
466 case st_check_patch:
467 /// check the data to check if patch needed
468 CLoginProgressPostThread::getInstance().step(CLoginStep(LoginStep_PostLogin, "login_step_post_login"));
470 if (!ClientCfg.PatchWanted || Args.haveLongArg("nopatch"))
472 // client don't want to be patched !
473 _CurrentState = st_display_eula;
474 break;
477 initPatchCheck();
478 SM_BEGIN_EVENT_TABLE
479 #ifdef RYZOM_BG_DOWNLOADER
480 if (isBGDownloadEnabled())
482 SM_EVENT(ev_patch_needed, st_patch); // no choice for patch content when background downloader is used
484 else
485 #endif
487 SM_EVENT(ev_patch_needed, st_display_cat);
489 SM_EVENT(ev_no_patch, st_display_eula);
490 SM_EVENT(ev_quit, st_end);
491 SM_END_EVENT_TABLE
493 break;
494 case st_display_cat:
495 initCatDisplay();
497 SM_BEGIN_EVENT_TABLE
498 SM_EVENT(ev_run_patch, st_patch);
499 SM_EVENT(ev_quit, st_end);
500 SM_END_EVENT_TABLE
502 break;
503 case st_display_error:
504 _CurrentState = st_login;
506 break;
507 case st_patch:
508 /// run the patch process and display progress
509 initPatch();
511 SM_BEGIN_EVENT_TABLE
512 SM_EVENT(ev_close_patch, st_display_eula);
513 SM_EVENT(ev_quit, st_end);
514 SM_END_EVENT_TABLE
515 break;
516 case st_close_client:
517 /// terminate the client and quit
518 break;
519 case st_reboot_screen:
520 /// display the reboot screen and wait validation
521 initReboot();
523 SM_BEGIN_EVENT_TABLE
524 SM_EVENT(ev_reboot, st_end);
525 SM_EVENT(ev_quit, st_end);
526 SM_END_EVENT_TABLE
528 break;
529 case st_restart_client:
530 /// restart the client with login bypass params
531 break;
532 case st_connect:
533 /// connect to the FS (start the 'in game' mode)
534 ConnectToShard();
536 if (ClientCfg.R2Mode)
538 SM_BEGIN_EVENT_TABLE
539 SM_EVENT(ev_enter_game, st_ingame);
540 SM_EVENT(ev_conn_failed, st_display_error);
541 SM_END_EVENT_TABLE
543 else
545 SM_BEGIN_EVENT_TABLE
546 SM_EVENT(ev_enter_game, st_end);
547 SM_EVENT(ev_conn_failed, st_shard_list);
548 SM_END_EVENT_TABLE
550 break;
551 // case st_browser_screen:
552 // /// show the outgame browser
554 // if (!ClientCfg.TestBrowser)
555 // {
556 // // in test browser mode, the browser is already init
557 // initWebBrowser();
558 // }
560 // SM_BEGIN_EVENT_TABLE
561 // SM_EVENT(ev_connect, st_connect);
562 // SM_EVENT(ev_relog, st_login);
563 // SM_EVENT(ev_quit, st_end);
564 // SM_END_EVENT_TABLE
566 // break;
567 case st_ingame:
568 SM_BEGIN_EVENT_TABLE
569 //SM_EVENT(ev_chars_received, st_ingame); // ignored for normal login procedure
570 //SM_EVENT(ev_no_user_char, st_ingame); // "
571 //SM_EVENT(ev_ready_received, st_ingame); // "
572 //SM_EVENT(ev_global_menu_exited, st_ingame); "
573 SM_EVENT(ev_connect, st_leave_shard); // connect to another shard
574 SM_END_EVENT_TABLE
575 break;
576 case st_leave_shard:
578 * Far TP / Server Hop / Reselect character entry point
581 // Server Hop part 1: We are not in main loop but still at character selection
582 // => make a hop to the specified server without doing anything with interface
583 // Far TP part 1.1: From the ingame main loop, the admin html box gives us an event ev_connect for the destination shard.
584 // Note: the admin html box is run by CInputHandlerManager::getInstance()->pumpEvents() in the main loop.
585 // Tip: to see where a co-task is resumed from, just add a breakpoint on the end of CCoTask::resume().
586 CAHManager::getInstance()->runActionHandler("quit_ryzom", NULL, "");
588 if (!FarTP.isIngame()) // assumes there is no Far TP starting between char selection and main loop, see below
590 crashLogAddServerHopEvent();
592 FarTP.onServerQuitOk(); // don't wait for onServerQuitOK() because the EGS will no longer send it if it requests a Far TP during "select char" (not sending USER_CHAR)
594 SM_BEGIN_EVENT_TABLE
595 SM_EVENT(ev_ingame_return, st_disconnect);
596 SM_END_EVENT_TABLE
598 else
600 if(FarTP.isReselectingChar())
601 crashLogAddReselectPersoEvent();
602 else
603 crashLogAddFarTpEvent();
605 if (NetMngr.getConnectionState() == CNetworkConnection::Disconnect)
606 FarTP.onServerQuitOk(); // don't wait for onServerQuitOK() because the EGS is down
608 SM_BEGIN_EVENT_TABLE
609 SM_EVENT(ev_ingame_return, st_enter_far_tp_main_loop);
610 SM_EVENT(ev_enter_game, st_ingame);
611 SM_END_EVENT_TABLE
613 break;
614 case st_enter_far_tp_main_loop:
615 // if bgdownloader is used, then pause it
616 #ifdef RYZOM_BG_DOWNLOADER
617 pauseBGDownloader();
618 #endif
621 // Far TP part 1.2: let the main loop finish the current frame.
622 // This is called when CONNECTION:SERVER_QUIT_OK is received (from NetMngr.update() in main loop).
624 SM_BEGIN_EVENT_TABLE
625 SM_EVENT(ev_far_tp_main_loop_entered, st_disconnect);
626 SM_END_EVENT_TABLE
627 break;
628 case st_disconnect:
629 // Far TP part 2: disconnect from the FS and unload shard-specific data (called from farTPmainLoop())
630 // FarTP.disconnectFromPreviousShard();
632 SM_BEGIN_EVENT_TABLE
633 SM_EVENT(ev_connect, st_reconnect_fs);
634 SM_END_EVENT_TABLE
635 break;
636 case st_reconnect_fs:
637 // Server Hop part 3.1: connect to the new shard (called from globalMenu())
638 // Far TP part 3.1: connect to the new shard (called from farTPmainLoop())
639 FarTP.connectToNewShard();
641 SM_BEGIN_EVENT_TABLE
642 SM_EVENT(ev_self_reconnected, st_ingame);
643 SM_EVENT(ev_chars_received, st_reconnect_select_char);
644 SM_EVENT(ev_no_user_char, st_reconnect_error);
645 SM_EVENT(ev_conn_failed, st_reconnect_error);
646 SM_EVENT(ev_conn_dropped, st_reconnect_error);
647 SM_END_EVENT_TABLE
648 break;
649 case st_reconnect_select_char:
650 // Server Hop part 3.2: bypass character selection ui & select the same character.
651 // Far TP part 3.2: bypass character selection ui & select the same character.
652 // This is called from farTPmainloop(), when CONNECTION:USER_CHARS is received.
653 if( !FarTP.isReselectingChar() )
655 FarTP.selectCharAndEnter();
657 // Far TP part 3.2bis: see in farTPmainLoop()
659 if (!FarTP.isIngame())
661 SM_BEGIN_EVENT_TABLE
662 SM_EVENT(ev_ready_received, st_exit_global_menu);
663 SM_EVENT(ev_conn_dropped, st_reconnect_error);
664 SM_END_EVENT_TABLE
666 else
668 SM_BEGIN_EVENT_TABLE
669 SM_EVENT(ev_ready_received, st_reconnect_ready);
670 SM_EVENT(ev_conn_dropped, st_reconnect_error);
671 if ( FarTP.isReselectingChar() && (ev == ev_connect) )
673 // Inside this Character Reselect we embed a new Server Hop
674 FarTP.beginEmbeddedServerHop();
675 _CurrentState = st_leave_shard;
676 break;
678 SM_END_EVENT_TABLE
680 break;
681 case st_exit_global_menu:
682 // Server Hop part 3.3
683 // Stay in Server Hop state until the global menu has been exited
684 SM_BEGIN_EVENT_TABLE
685 SM_EVENT(ev_global_menu_exited, FarTP.isServerHopEmbedded() ? st_reconnect_ready : st_ingame);
686 SM_END_EVENT_TABLE
687 break;
688 case st_reconnect_ready:
689 // Far TP part 3.3: send ready.
690 // This is called from farTPmainloop(), when CONNECTION:READY is received.
692 if ( FarTP.isServerHopEmbedded() )
693 FarTP.endEmbeddedServerHop();
695 SM_BEGIN_EVENT_TABLE
696 SM_EVENT(ev_enter_game, st_ingame);
697 SM_EVENT(ev_conn_dropped, st_reconnect_error);
698 SM_END_EVENT_TABLE
699 break;
700 case st_reconnect_error:
701 // Far TP failed
702 FarTP.onFailure();
704 SM_BEGIN_EVENT_TABLE
705 SM_EVENT(ev_quit, st_end);
706 SM_END_EVENT_TABLE
707 break;
708 default:
709 nlwarning("Unhandeled state");
710 break;
716 // ***************************************************************************
717 // Far TP
718 // ***************************************************************************
720 CFarTP FarTP;
722 extern std::string CurrentCookie;
723 extern string ClientApp;
726 namespace R2
728 extern bool ReloadUIFlag;
733 * requestFarTPToSession
735 bool CFarTP::requestFarTPToSession(TSessionId sessionId, uint8 charSlot, CFarTP::TJoinMode joinMode, bool bailOutIfSessionVanished)
737 if (_HookedForEditor)
739 // Redirect Far TP to editor instead of following instructions
740 _HookedForEditor = false;
741 return FarTP.requestFarTPToSession((TSessionId)0, charSlot, CFarTP::LaunchEditor, true); // allow bailing out in case of edition session not reachable
744 // call the join session using the session browser interface
745 uint32 charId = (NetMngr.getLoginCookie().getUserId()<<4)+(0xf&charSlot);
747 // Clean out for next Far TP
748 _SessionIdToJoinFast = 0;
750 CSessionBrowserImpl &sb = CSessionBrowserImpl::getInstance();
751 sb.init(NULL);
752 // sb.setAuthInfo(NetMngr.getLoginCookie());
753 // sb.connectItf(CInetAddress("borisb", 80));
755 sb.CurrentJoinMode = joinMode;
756 // send the join session
757 // _JoinSessionResultReceived = false;
760 switch (joinMode)
762 case LaunchEditor:
764 retryJoinEdit:
765 TSessionId sessionId(0);
766 sb.joinEditSession(charId, ClientApp);
768 bool ret = sb.waitOneMessage(CSessionBrowserImpl::getMessageName("on_joinSessionResult"));
770 if (!ret)
771 throw "Protocol error";
772 if (sb._LastJoinSessionResult == 2)
774 // the edit session did not exist, create a new one
775 sb.scheduleSession(charId, TSessionType::st_edit, "", "", TSessionLevel::sl_a, /*TAccessType::at_private, */TRuleType::rt_strict, TEstimatedDuration::et_long, 0, TAnimMode(), TRaceFilter(), TReligionFilter(), TGuildFilter(), TShardFilter(), TLevelFilter(), std::string(), TSessionOrientation(), true, true);
776 ret = sb.waitOneMessage(CSessionBrowserImpl::getMessageName("on_scheduleSessionResult"));
777 if (!ret || sb._LastScheduleSessionResult!= 0) throw "schedule error";
778 sessionId = sb._LastScheduleSessionId;
780 // invite the char in the session
781 sb.inviteCharacter(charId, sessionId, charId, TSessionPartStatus::sps_edit_invited);
782 ret = sb.waitOneMessage(CSessionBrowserImpl::getMessageName("on_invokeResult"));
783 if (!ret || sb._LastInvokeResult != 0) throw "Invitation Error";
785 // now, start the edit session
786 sb.startSession(charId, sessionId);
787 ret = sb.waitOneMessage(CSessionBrowserImpl::getMessageName("on_invokeResult"));
788 if (!ret || sb._LastInvokeResult != 0) throw "start session error";
790 // retry to join
791 goto retryJoinEdit;
793 else if (sb._LastJoinSessionResult == 15)
795 sessionId = sb._LastJoinSessionId;
796 // the session was closed, the SU has put it in planned state, start it
797 sb.startSession(charId, sessionId);
798 ret = sb.waitOneMessage(CSessionBrowserImpl::getMessageName("on_invokeResult"));
799 if (!ret || sb._LastInvokeResult != 0) throw "start session error (2), no DSS available";
800 // retry to join
801 goto retryJoinEdit;
803 else if (sb._LastJoinSessionResult != 0)
805 // any other error are not recoverable from here
806 throw "join session error";
809 // ok, the join is accepted !, the other far TP work is done in the callback
811 break;
812 case JoinSession:
814 sb.joinSession(charId, sessionId, ClientApp);
816 bool ret = sb.waitOneMessage(CSessionBrowserImpl::getMessageName("on_joinSessionResult"));
818 if (!ret)
819 throw "Protocol error";
820 if (sb._LastJoinSessionResult == 16)
822 // #pragma message (NL_LOC_WRN "inform the player that he is banned from the ring")
823 throw "User ban from the ring";
825 if (sb._LastJoinSessionResult != 0)
827 throw "Join session error";
829 // ok, the join is accepted !, the other far TP work is done in the callback
831 break;
832 case JoinMainland:
833 //retryJoinMainland:
835 TSessionId sessionId(0);
836 sb.joinMainland(charId, ClientApp);
838 bool ret = sb.waitOneMessage(CSessionBrowserImpl::getMessageName("on_joinSessionResult"));
840 if (!ret)
841 throw "Protocol error";
842 if (sb._LastJoinSessionResult != 0)
844 // some error during the join
845 throw "Error joining session";
847 // ok, the join is accepted !, the other far TP work is done in the callback
849 break;
850 default:
851 nlstop;
854 return true;
858 // const string url = _URLBase + string(_URLTable[joinMode]);
859 // string res;
860 // try
861 // {
862 // // Get the address of a front-end service via http
863 // if (!HttpClient.connect(url))
864 // throw 1;
865 // switch (joinMode)
866 // {
867 // case CFarTP::LaunchEditor:
868 // if (!HttpClient.sendGetWithCookie(url, "ryzomId", CurrentCookie, toString("charSlot=%u", charSlot)))
869 // throw 2;
870 // break;
871 // case CFarTP::JoinSession:
872 // if (!HttpClient.sendPostWithCookie(url, "ryzomId", CurrentCookie, toString("sessionId=%u&charSlot=%u", sessionId, charSlot)))
873 // throw 2;
874 // break;
875 // case CFarTP::JoinMainland:
876 // if (!HttpClient.sendPostWithCookie(url, "ryzomId", CurrentCookie, "ml="))
877 // throw 2;
878 // break;
879 // }
880 // if (!HttpClient.receive(res))
881 // throw 3;
882 // HttpClient.disconnect();
883 // string::size_type luaPos = res.find("<lua>");
884 // if (luaPos == string::npos)
885 // throw 4;
886 // res = res.substr(luaPos + 5);
887 // res = res.substr(0, res.find("</lua>"));
888 // nlinfo("Found server for %ssession %u", joinMode==CFarTP::LaunchEditor ? "editing " : "", sessionId);
890 // // Begin Far TP
891 // CInterfaceManager *pIM = CInterfaceManager::getInstance();
892 // pIM->executeLuaScript(res, true);
893 // return true;
894 // }
895 catch ( char *errorMsg )
897 // // Get more details on the error
898 // string httpErrorStr;
899 // if (errorNum < 4) // we didn't receive an answer
900 // httpErrorStr = toString(" (HTTP error %u)", errorNum);
901 // if ( errorNum == 4 )
902 // {
903 // string::size_type posEndOfFirstLine = res.find( "\n" );
904 // if ( posEndOfFirstLine == string::npos )
905 // {
906 // // Invalid http answer
907 // httpErrorStr = toString(" (HTTP invalid)");
908 // }
909 // else
910 // {
911 // // Http status not OK (e.g. "404 Not Found")
912 // httpErrorStr = res.substr( 0, posEndOfFirstLine );
913 // if ( httpErrorStr.find( "200 OK" ) == string::npos )
914 // httpErrorStr = " (" + httpErrorStr + ")";
915 // else
916 // httpErrorStr.clear();
917 // }
918 // }
919 bool requestRetToMainland = /*httpErrorStr.empty() &&*/ bailOutIfSessionVanished;
920 bool isAttemptingEmbeddedServerHop = isReselectingChar() && (joinMode != CFarTP::JoinSession);
921 bool letReturnToCharSelect = (!isIngame()) || isAttemptingEmbeddedServerHop;
923 // User-friendly message
924 CInterfaceManager *pIM = CInterfaceManager::getInstance();
925 if ( letReturnToCharSelect )
927 // // Hide all buttons except Quit. If !requestRetToMainland, we will show them back at the end of connectToNewShard().
928 // CAHManager::getInstance()->runActionHandler( "proc", NULL, "charsel_disable_buttons" );
929 // CAHManager::getInstance()->runActionHandler( "set", NULL, "target_property=ui:outgame:charsel:quit_but:active|value=1" );
931 CInterfaceElement *btnOk = CWidgetManager::getInstance()->getElementFromId("ui:outgame:charsel:message_box:ok");
932 if (btnOk)
933 btnOk->setActive( ! requestRetToMainland );
935 // Hide the black screen i.e. force showing the interface
936 CInterfaceElement *charSelBlackScreen = CWidgetManager::getInstance()->getElementFromId("ui:outgame:charsel:black_screen");
937 if (charSelBlackScreen)
939 CViewBase *charSelBlackScreenBitmap = dynamic_cast<CViewBase*>(charSelBlackScreen);
940 if (charSelBlackScreenBitmap)
941 charSelBlackScreenBitmap->setAlpha(0);
944 pIM->messageBoxWithHelp(
945 CI18N::get(requestRetToMainland ? "uiSessionVanishedFarTP" : "uiSessionUnreachable") + errorMsg,
946 letReturnToCharSelect ? "ui:outgame:charsel" : "ui:interface");
948 // Info in the log
949 string outErrorMsg = toString("Could not join %ssession %u: '%s' error\n", joinMode==CFarTP::LaunchEditor ? "editing " : "", sessionId.asInt(), errorMsg );
950 nlwarning( outErrorMsg.c_str() );
951 // if ( httpErrorStr.empty() )
952 // {
953 // string delimiter = "Content-Type: text/html";
954 // string::size_type delimPos = res.find( delimiter );
955 // if ( delimPos != string::npos )
956 // res = res.substr( delimPos + delimiter.size() );
957 // }
958 // uint pos = 0;
959 // do
960 // {
961 // WarningLog->displayRaw( res.substr( pos, 200 ).c_str() ); // truncates long strings
962 // pos += 200;
963 // }
964 // while ( pos < res.size() );
965 WarningLog->displayRawNL( "" );
967 // Save this error (regular log file is deleted at every startup)
968 // res = res + "\n\n";
969 /*try
971 COFile outputF;
972 if ( outputF.open( getLogDirectory() + "error_join.log", true, true ) )
974 time_t currentTime;
975 time( &currentTime );
976 string headerS = NLMISC::toString( "\n\n%s%s\n\n", asctime(localtime(&currentTime)), outErrorMsg.c_str() );
977 outputF.serialBuffer( (uint8*)(&headerS[0]), (uint)headerS.size() );
978 // outputF.serialBuffer( (uint8*)(&res[0]), res.size() );
979 outputF.close();
982 catch (const Exception &)
986 // If the session is not a permanent session and has vanished, pop the position
987 if ( requestRetToMainland )
989 requestReturnToPreviousSession( sessionId );
990 LastGameCycle = NetMngr.getCurrentServerTick();
991 NetMngr.send(NetMngr.getCurrentServerTick());
993 else if ( letReturnToCharSelect )
995 // Show all buttons except 'New character' so that the character can retry entering game or choose another character.
996 CAHManager::getInstance()->runActionHandler( "proc", NULL, "charsel_enable_buttons" );
997 CAHManager::getInstance()->runActionHandler( "set", NULL, "target_property=ui:outgame:charsel:create_new_but:active|value=0" );
999 CInterfaceGroup* charselGroup = dynamic_cast<CInterfaceGroup*>(CWidgetManager::getInstance()->getElementFromId("ui:outgame:charsel"));
1000 if(charselGroup)
1001 CAHManager::getInstance()->runActionHandler( "proc", charselGroup, "charsel_init_buttons" );
1004 return false;
1010 * hookNextFarTPForEditor
1012 void CFarTP::hookNextFarTPForEditor()
1014 _HookedForEditor = true;
1018 * requestReturnToPreviousSession
1020 void CFarTP::requestReturnToPreviousSession(TSessionId rejectedSessionId)
1022 const char *msgName = "CONNECTION:RET_MAINLAND";
1023 CBitMemStream out;
1024 nlverify(GenericMsgHeaderMngr.pushNameToStream(msgName, out));
1025 out.serial(PlayerSelectedSlot);
1026 out.serial(rejectedSessionId);
1027 NetMngr.push(out);
1028 nlinfo("%s sent", msgName);
1032 * requestReconnection
1034 void CFarTP::requestReconnection()
1036 _ReselectingChar = true;
1037 if (!requestFarTPToSession(TSessionId(std::numeric_limits<uint16>::max()), std::numeric_limits<uint8>::max(), CFarTP::JoinMainland, false))
1038 _ReselectingChar = false;
1042 const char * CFarTP::_URLTable[CFarTP::NbJoinModes] =
1044 "edit_session.php",
1045 "join_session.php",
1046 "join_shard.php"
1051 * States
1054 bool CFarTP::isFarTPInProgress() const
1056 CLoginStateMachine::TState state = LoginSM.getCurrentState();
1057 return (state == CLoginStateMachine::st_leave_shard ||
1058 state == CLoginStateMachine::st_enter_far_tp_main_loop ||
1059 state == CLoginStateMachine::st_disconnect ||
1060 state == CLoginStateMachine::st_reconnect_fs ||
1061 state == CLoginStateMachine::st_reconnect_select_char ||
1062 state == CLoginStateMachine::st_reconnect_ready ||
1063 state == CLoginStateMachine::st_reconnect_error);
1066 bool CFarTP::isServerHopInProgress() const
1068 CLoginStateMachine::TState state = LoginSM.getCurrentState();
1069 return (state == CLoginStateMachine::st_leave_shard ||
1070 state == CLoginStateMachine::st_reconnect_fs ||
1071 state == CLoginStateMachine::st_reconnect_select_char ||
1072 state == CLoginStateMachine::st_exit_global_menu ||
1073 state == CLoginStateMachine::st_reconnect_error); // TODO: error handling
1077 // from begin of Far TP to disconnection
1078 bool CFarTP::isLeavingShard() const
1080 CLoginStateMachine::TState state = LoginSM.getCurrentState();
1081 return (state == CLoginStateMachine::st_leave_shard ||
1082 state == CLoginStateMachine::st_enter_far_tp_main_loop ||
1083 state == CLoginStateMachine::st_disconnect);
1086 // from reconnection to game entering
1087 bool CFarTP::isJoiningShard() const
1089 CLoginStateMachine::TState state = LoginSM.getCurrentState();
1090 return (state == CLoginStateMachine::st_reconnect_fs ||
1091 state == CLoginStateMachine::st_reconnect_select_char ||
1092 state == CLoginStateMachine::st_reconnect_ready ||
1093 state == CLoginStateMachine::st_reconnect_error);
1098 * Events
1101 void CFarTP::onServerQuitOk()
1103 game_exit_request = false;
1104 ryzom_exit_request = false;
1106 if (LoginSM.getCurrentState() == CLoginStateMachine::st_leave_shard)
1107 LoginSM.pushEvent(CLoginStateMachine::ev_ingame_return);
1110 void CFarTP::onServerQuitAbort()
1112 game_exit_request = false;
1113 ryzom_exit_request = false;
1115 if (LoginSM.getCurrentState() == CLoginStateMachine::st_leave_shard)
1116 LoginSM.pushEvent(CLoginStateMachine::ev_enter_game);
1117 _ReselectingChar = false;
1120 void CFarTP::disconnectFromPreviousShard()
1122 CInterfaceManager *pIM = CInterfaceManager::getInstance();
1124 if (isIngame())
1126 // Display background (TODO: not Kami)
1127 beginLoading (StartBackground);
1128 UseEscapeDuringLoading = false;
1130 // Change the tips
1131 selectTipsOfTheDay (rand());
1133 // Start progress bar and display background
1134 ProgressBar.reset (BAR_STEP_TP);
1135 string nmsg("Loading...");
1136 ProgressBar.newMessage ( ClientCfg.buildLoadingString(nmsg) );
1137 ProgressBar.progress(0);
1139 // Play music and fade out the Game Sound
1140 if (SoundMngr)
1142 SoundMngr->fadeOutGameSound(ClientCfg.SoundTPFade);
1144 // Stop and enable music
1145 SoundMngr->stopMusic(0);
1146 SoundMngr->setupFadeSound(0.0f, 1.0f);
1148 // Loading Music Loop.ogg
1149 LoadingMusic = ClientCfg.LoadingMusic;
1150 // SoundMngr->playEventMusic(LoadingMusic, CSoundManager::LoadingMusicXFade, true);
1151 SoundMngr->playMusic(LoadingMusic, 0, false, true, true);
1155 // Disconnect from the FS
1156 NetMngr.disconnect();
1158 if (isIngame())
1160 // Save the R2EDEnabled flag to know if we are switching it when we reconnect
1161 _PreviousR2EdEnabled = ClientCfg.R2EDEnabled;
1163 // Release R2 editor if applicable
1164 R2::getEditor().autoConfigRelease(IsInRingSession);
1167 if( isReselectingChar() )
1169 releaseMainLoopReselect();
1171 else
1173 // String manager: remove all waiting callbacks and removers
1174 // (if some interface stuff has not received its string yet, its remover will get useless)
1175 STRING_MANAGER::CStringManagerClient::release( false );
1177 // Ugly globals
1178 userChar = false;
1179 noUserChar = false;
1180 serverReceivedReady = false;
1181 CharNameValidArrived = false;
1182 UserCharPosReceived = false;
1183 SabrinaPhraseBookLoaded = false;
1186 // Restart the network manager, the interface sync counter and the entities
1187 pIM->resetShardSpecificData();
1190 ServerToLocal autocopy stuff:
1191 When reinit will reset server counters to 0 in the server database, onServerChange()
1192 will be called and data will be copied since CInterfaceManager::_LocalSyncActionCounter==0
1193 (done in resetShardSpecificData() above)
1194 If we do the resetShardSpecificData() after, scenari could arise where Local database could not be reseted
1196 NetMngr.reinit();
1198 if (isIngame() && !isReselectingChar())
1200 nlinfo("FarTP: calling EntitiesMngr.reinit()");
1201 EntitiesMngr.reinit();
1203 LoginSM.pushEvent(CLoginStateMachine::ev_connect);
1206 void CFarTP::connectToNewShard()
1208 // TODO: start commands?
1210 // Connect to the next FS
1211 NetMngr.initCookie(Cookie, FSAddr);
1213 // connect the session browser to the new shard
1214 NLNET::CInetAddress sbsAddress(CSessionBrowserImpl::getInstance().getFrontEndAddress());
1215 sbsAddress.setPort(sbsAddress.port()+SBSPortOffset);
1216 CSessionBrowserImpl::getInstance().connectItf(sbsAddress);
1218 string result;
1219 NetMngr.connect(result);
1220 if (!result.empty())
1222 _Reason = new string(result);
1223 LoginSM.pushEvent(CLoginStateMachine::ev_conn_failed);
1224 return;
1227 // Reinit the string manager cache.
1228 STRING_MANAGER::CStringManagerClient::instance()->initCache(FSAddr, ClientCfg.LanguageCode);
1230 // reset the chat mode
1231 ChatMngr.resetChatMode();
1233 // The next step will be triggered by the CONNECTION:USER_CHARS msg from the server
1236 // return to character selection screen
1237 bool CFarTP::reselectCharacter()
1239 if ( ! reconnection() )
1241 // The user clicked the Quit button
1242 releaseOutGame();
1243 return false;
1245 return true;
1248 void CFarTP::selectCharAndEnter()
1250 CBitMemStream out;
1251 nlverify (GenericMsgHeaderMngr.pushNameToStream("CONNECTION:SELECT_CHAR", out));
1252 CSelectCharMsg SelectCharMsg;
1253 SelectCharMsg.c = (uint8)PlayerSelectedSlot; // PlayerSelectedSlot has not been reset
1254 out.serial(SelectCharMsg);
1255 NetMngr.push(out);
1256 /*//Obsolete
1257 CBitMemStream out2;
1258 nlverify(GenericMsgHeaderMngr.pushNameToStream("CONNECTION:ENTER", out2));
1259 NetMngr.push(out2);
1261 LastGameCycle = NetMngr.getCurrentServerTick();
1262 NetMngr.send(NetMngr.getCurrentServerTick());
1264 // if (!isIngame())
1265 // globalMenu will exit when WaitServerAnswer and serverReceivedReady (triggered by the CONNECTION:READY msg) are true
1266 // else
1267 // Next step will be triggered by the CONNECTION:READY msg from the server
1270 void CFarTP::sendReady()
1272 if ( isReselectingChar() )
1274 initMainLoop();
1276 else
1278 // Set season
1279 RT.updateRyzomClock(NetMngr.getCurrentServerTick());
1280 DayNightCycleHour = (float)RT.getRyzomTime();
1281 CurrSeason = RT.getRyzomSeason();
1282 RT.updateRyzomClock(NetMngr.getCurrentServerTick());
1283 DayNightCycleHour = (float)RT.getRyzomTime();
1284 ManualSeasonValue = RT.getRyzomSeason();
1286 // Reset all fx (no need to CTimedFXManager::getInstance().reset() and EntitiesMngr.getGroundFXManager().reset() because the entities have been removed)
1287 CProjectileManager::getInstance().reset();
1288 FXMngr.reset(); // (must be done after EntitiesMngr.release())
1289 EntitiesMngr.getGroundFXManager().init(Scene, ClientCfg.GroundFXMaxDist, ClientCfg.GroundFXMaxNB, ClientCfg.GroundFXCacheSize);
1291 // Get the sheet for the user from the CFG.
1292 // Initialize the user and add him into the entity manager.
1293 // DO IT AFTER: Database, Collision Manager, PACS, scene, animations loaded.
1294 CSheetId userSheet(ClientCfg.UserSheet);
1295 nlinfo("FarTP: calling EntitiesMngr.create(0, userSheet.asInt())");
1296 TNewEntityInfo emptyEntityInfo;
1297 emptyEntityInfo.reset();
1298 EntitiesMngr.create(0, userSheet.asInt(), emptyEntityInfo);
1300 // Wait for the start position (USER_CHAR) and set the continent
1301 waitForUserCharReceived();
1304 CInterfaceManager *pIM = CInterfaceManager::getInstance();
1305 if ( ClientCfg.R2EDEnabled != _PreviousR2EdEnabled )
1307 // Reload textures, keys and interface config if we are switch between playing and r2 mode
1308 // Must be done after receiving the current R2EDEnabled flag (USER_CHAR)
1309 pIM->loadIngameInterfaceTextures();
1311 // Unload (and save if leaving normal playing mode) keys and interface config
1312 // Instead of doing it in disconnectFromPreviousShard(), we do it here, only when it's needed
1313 ClientCfg.R2EDEnabled = ! ClientCfg.R2EDEnabled;
1314 pIM->uninitInGame0();
1315 CItemGroupManager::getInstance()->uninit();
1317 ClientCfg.R2EDEnabled = ! ClientCfg.R2EDEnabled;
1318 ActionsContext.removeAllCombos();
1320 if ( ! ClientCfg.R2EDEnabled )
1322 // Remove all existing keys and load them back, and load new interface config
1323 pIM->loadKeys();
1324 CWidgetManager::getInstance()->hideAllWindows();
1325 pIM->loadInterfaceConfig();
1326 pIM->loadLandmarks();
1328 else
1330 R2::ReloadUIFlag = true; // in R2ED mode the CEditor class deals with it
1333 pIM->configureQuitDialogBox(); // must be called after waitForUserCharReceived() to know the ring config
1335 ContinentMngr.select(UserEntity->pos(), ProgressBar); // IMPORTANT : must select continent after ui init, because ui init also load landmarks (located in the icfg file)
1336 // landmarks would be invisible else (RT 12239)
1340 // Update Network until current tick increase.
1341 LastGameCycle = NetMngr.getCurrentServerTick();
1342 while (LastGameCycle == NetMngr.getCurrentServerTick())
1344 // Event server get events
1345 CInputHandlerManager::getInstance()->pumpEventsNoIM();
1346 // Update Network.
1347 NetMngr.update();
1348 IngameDbMngr.flushObserverCalls();
1349 NLGUI::CDBManager::getInstance()->flushObserverCalls();
1350 // Be nice to the system
1351 nlSleep(100);
1353 LastGameCycle = NetMngr.getCurrentServerTick();
1354 ProgressBar.progress(1);
1356 // Create the message for the server to create the character.
1357 CBitMemStream out;
1358 if(GenericMsgHeaderMngr.pushNameToStream("CONNECTION:READY", out))
1360 out.serial(ClientCfg.LanguageCode);
1361 NetMngr.push(out);
1362 NetMngr.send(NetMngr.getCurrentServerTick());
1365 // To be sure server crash is not fault of client
1366 ConnectionReadySent = true; // must be called before BotChatPageAll->initAfterConnectionReady()
1368 // To reset the inputs.
1369 CInputHandlerManager::getInstance()->pumpEventsNoIM();
1371 if(BotChatPageAll && (! ClientCfg.R2EDEnabled))
1372 BotChatPageAll->initAfterConnectionReady();
1374 // Transition from background to game
1375 FirstFrame = true;
1378 ProgressBar.finish();
1380 LoginSM.pushEvent(CLoginStateMachine::ev_enter_game);
1382 if (_DSSDown)
1384 _DSSDown = false;
1385 onDssDown(true);
1389 void CFarTP::onFailure()
1391 // Display message
1392 string reason;
1393 if (_Reason)
1395 reason += ": " + (*_Reason);
1396 delete _Reason;
1397 _Reason = NULL;
1399 else if (noUserChar)
1401 reason += ": no characters found!";
1403 Driver->systemMessageBox(("Unable to join shard"+reason).c_str(), "Error", UDriver::okType, UDriver::exclamationIcon);
1405 // TODO: recover from error
1407 LoginSM.pushEvent(CLoginStateMachine::ev_quit);
1410 void CFarTP::onDssDown(bool forceReturn)
1412 if (!forceReturn)
1414 // If leaving shard, don't bother with DSS "downitude"
1415 if (isLeavingShard())
1416 return;
1417 // If joining shard, store event and launch it at the end of reconnection
1418 if (isJoiningShard())
1420 _DSSDown = true; // note: still, some cases will make the client hang or abort, e.g. DSS down before the client receives the start pos (in ring shard)
1421 return;
1425 CInterfaceManager *pIM= CInterfaceManager::getInstance();
1426 pIM->messageBoxWithHelp(CI18N::get("uiDisconnected"));
1427 requestReturnToPreviousSession();
1430 extern bool loginFinished;
1431 void setLoginFinished( bool f );
1432 extern bool loginOK;
1434 void CFarTP::joinSessionResult(uint32 /* userId */, TSessionId /* sessionId */, uint32 /* result */, const std::string &/* shardAddr */, const std::string &/* participantStatus */)
1436 // _LastJoinSessionResultMsg = result;
1438 // _JoinSessionResultReceived = true;
1440 // if (result == 0)
1441 // {
1442 // // ok, the join is successful
1444 // FSAddr = shardAddr;
1446 // setLoginFinished( true );
1447 // loginOK = true;
1449 // LoginSM.pushEvent(CLoginStateMachine::ev_connect);
1451 // }
1452 // else
1453 // {
1454 // // TODO : display the error message on client screen and log
1455 // nlstop;
1456 // }
1460 void CFarTP::setJoinSessionResult(TSessionId sessionId, const CSecurityCode& securityCode)
1462 _SessionIdToJoinFast = sessionId;
1463 _SecurityCodeForDisconnection = securityCode;
1466 void CFarTP::writeSecurityCodeForDisconnection(NLMISC::IStream& msgout)
1468 CSecurityCheckForFastDisconnection::forwardSecurityCode(msgout, _SessionIdToJoinFast, _SecurityCodeForDisconnection);
1471 // Not run by the cotask but within the main loop
1472 void CFarTP::farTPmainLoop()
1474 ConnectionReadySent = false;
1475 LoginSM.pushEvent(CLoginStateMachine::ev_far_tp_main_loop_entered);
1477 disconnectFromPreviousShard();
1479 uint nbRecoSelectCharReceived = 0;
1481 bool welcomeWindow = true;
1483 // Update network until the end of the FarTP process, before resuming the main loop
1484 while (!ConnectionReadySent)
1486 // Event server get events
1487 CInputHandlerManager::getInstance()->pumpEventsNoIM();
1489 // Update Network.
1490 NetMngr.update();
1491 IngameDbMngr.flushObserverCalls();
1492 NLGUI::CDBManager::getInstance()->flushObserverCalls();
1494 // TODO: resend in case the last datagram sent was lost?
1495 // // check if we can send another dated block
1496 // if (NetMngr.getCurrentServerTick() != serverTick)
1497 // {
1498 // //
1499 // serverTick = NetMngr.getCurrentServerTick();
1500 // NetMngr.send(serverTick);
1501 // }
1502 // else
1503 // {
1504 // // Send dummy info
1505 // NetMngr.send();
1506 // }
1508 if (LoginSM.getCurrentState() == CLoginStateMachine::st_reconnect_select_char)
1510 // Far TP part 3.2bis: go to character selection dialog
1511 // This is done outside the co-routine because it may need to co-routine to do an embedded server hop
1512 if ( FarTP.isReselectingChar() )
1514 ++nbRecoSelectCharReceived;
1515 if ( nbRecoSelectCharReceived <= 1 )
1517 ClientCfg.SelectCharacter = -1; // turn off character autoselection
1518 if ( ! FarTP.reselectCharacter() ) // it should not return here in farTPmainLoop() in the same state otherwise this would be called twice
1519 return;
1521 else
1522 nlwarning( "Received more than one st_reconnect_select_char event" );
1525 else if (LoginSM.getCurrentState() == CLoginStateMachine::st_reconnect_ready)
1527 // Don't call sendReady() within the cotask but within the main loop, as it contains
1528 // event/network loops that could trigger a global exit().
1529 sendReady();
1530 welcomeWindow = !isReselectingChar();
1533 // Be nice to the system
1534 nlSleep(100);
1537 // active/desactive welcome window
1538 if(welcomeWindow)
1539 initWelcomeWindow();