1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2011 Winch Gate Property Limited
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>
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/>.
22 #include "nel/3d/u_driver.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"
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"
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"
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"
58 using namespace NLMISC
;
59 using namespace NLNET
;
61 using namespace RSMGR
;
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) \
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 //-----------------------------------------------
151 //-----------------------------------------------
152 const std::string
& CLoginStateMachine::toString(CLoginStateMachine::TState state
)
154 return StateConversion
.toString(state
);
157 //-----------------------------------------------
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) \
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() ); \
188 catch (const Exception &) \
190 _CurrentState = stateId; \
195 #define SM_EVENT(eventId, stateId) \
198 _CurrentState = stateId; \
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
214 CLoginStateMachine::TEvent
CLoginStateMachine::waitEvent()
216 nlassert(CCoTask::getCurrentTask() == this);
218 while (_NextEvents
.empty() && !isTerminationRequested())
220 // wait until someone push an event on the state machine
223 if (isTerminationRequested())
227 TEvent ev
= _NextEvents
.front();
228 _NextEvents
.pop_front();
233 void CLoginStateMachine::pushEvent(TEvent eventId
)
235 // set the next event
236 _NextEvents
.push_back(eventId
);
238 if (CCoTask::getCurrentTask() != this)
240 /// resume the state machine to advance state
246 void CLoginStateMachine::run()
248 // state machine coroutine
250 while (_CurrentState
!= st_end
&& !isTerminationRequested())
252 switch(_CurrentState
)
257 if (!ClientCfg
.TestBrowser
)
259 if (LoginPassword
.empty())
261 if (!LoginCustomParameters
.empty() && LoginLogin
.empty())
263 // alternate login procedure
265 SM_EVENT(ev_init_done
, st_alt_login
);
266 SM_EVENT(ev_skip_all_login
, st_ingame
);
267 SM_EVENT(ev_quit
, st_end
);
272 // standard procedure
274 SM_EVENT(ev_init_done
, st_login
);
275 SM_EVENT(ev_skip_all_login
, st_ingame
);
276 SM_EVENT(ev_quit
, st_end
);
282 // login 2, bypass the login screen
284 SM_EVENT(ev_init_done
, st_auto_login
);
285 SM_EVENT(ev_skip_all_login
, st_ingame
);
286 SM_EVENT(ev_quit
, st_end
);
293 // // browser test mode
294 // SM_BEGIN_EVENT_TABLE
295 // SM_EVENT(ev_init_done, st_browser_screen);
296 // SM_EVENT(ev_quit, st_end);
297 // SM_END_EVENT_TABLE
302 /// display login screen and options
306 if (ClientCfg
.R2Mode
)
310 SM_EVENT(ev_login_ok
, st_check_patch
);
311 SM_EVENT(ev_game_conf
, st_start_config
);
312 SM_EVENT(ev_data_scan
, st_scan_data
);
313 SM_EVENT(ev_create_account
, st_create_account
);
314 SM_EVENT(ev_bad_login
, st_login
);
315 SM_EVENT(ev_quit
, st_end
);
322 SM_EVENT(ev_login_ok
, st_shard_list
);
323 SM_EVENT(ev_game_conf
, st_start_config
);
324 SM_EVENT(ev_data_scan
, st_scan_data
);
325 SM_EVENT(ev_create_account
, st_create_account
);
326 SM_EVENT(ev_bad_login
, st_login
);
327 SM_EVENT(ev_quit
, st_end
);
335 // if (ClientCfg.R2Mode)
339 SM_EVENT(ev_login_ok
, st_check_patch
);
340 SM_EVENT(ev_quit
, st_end
);
346 // SM_BEGIN_EVENT_TABLE
347 // SM_EVENT(ev_login_ok, st_check_patch);
348 // SM_EVENT(ev_quit, st_end);
349 // SM_END_EVENT_TABLE
355 // if (ClientCfg.R2Mode)
359 SM_EVENT(ev_login_not_alt
, st_login
);
360 SM_EVENT(ev_login_ok
, st_check_patch
);
361 SM_EVENT(ev_quit
, st_end
);
367 // SM_BEGIN_EVENT_TABLE
368 // SM_EVENT(ev_login_ok, st_check_patch);
369 // SM_EVENT(ev_quit, st_end);
370 // SM_END_EVENT_TABLE
374 /// display the shard list
378 SM_EVENT(ev_shard_selected
, st_check_patch
);
379 SM_EVENT(ev_quit
, st_end
);
383 case st_start_config
:
384 /// launch the configurator and close ryzom
387 /// run the check data thread
391 SM_EVENT(ev_close_data_scan
, st_login
);
392 SM_EVENT(ev_quit
, st_end
);
396 case st_create_account
:
398 if (initCreateAccount())
401 SM_EVENT(ev_login_ok
, st_check_patch
);
402 SM_EVENT(ev_close_create_account
, st_login
);
403 SM_EVENT(ev_quit
, st_end
);
408 // return to login menu if an error occurred
409 _CurrentState
= st_login
;
413 case st_display_eula
:
415 /// display the eula and wait for validation
417 if (ClientCfg
.SkipEULA
)
419 // we don't want to see eula, auto accept
420 pushEvent(ev_accept_eula
);
423 bool mustReboot
= false;
425 #ifdef RYZOM_BG_DOWNLOADER
426 if (isBGDownloadEnabled())
428 mustReboot
= CBGDownloaderAccess::getInstance().mustLaunchBatFile();
433 mustReboot
= CPatchManager::getInstance()->mustLaunchBatFile();
438 // skip eula and show reboot screen
439 _CurrentState
= st_reboot_screen
;
445 // Login sequence was reordered
446 // if (ClientCfg.R2Mode)
449 // SM_BEGIN_EVENT_TABLE
450 // SM_EVENT(ev_accept_eula, st_browser_screen);
451 // SM_EVENT(ev_quit, st_end);
452 // SM_END_EVENT_TABLE
458 SM_EVENT(ev_accept_eula
, st_connect
);
459 SM_EVENT(ev_quit
, st_end
);
465 /// check the data to check if patch needed
466 CLoginProgressPostThread::getInstance().step(CLoginStep(LoginStep_PostLogin
, "login_step_post_login"));
467 if (!ClientCfg
.PatchWanted
)
469 // client don't want to be patched !
470 _CurrentState
= st_display_eula
;
475 #ifdef RYZOM_BG_DOWNLOADER
476 if (isBGDownloadEnabled())
478 SM_EVENT(ev_patch_needed
, st_patch
); // no choice for patch content when background downloader is used
483 SM_EVENT(ev_patch_needed
, st_display_cat
);
485 SM_EVENT(ev_no_patch
, st_display_eula
);
486 SM_EVENT(ev_quit
, st_end
);
494 SM_EVENT(ev_run_patch
, st_patch
);
495 SM_EVENT(ev_quit
, st_end
);
500 /// run the patch process and display progress
504 SM_EVENT(ev_close_patch
, st_display_eula
);
505 SM_EVENT(ev_quit
, st_end
);
508 case st_close_client
:
509 /// terminate the client and quit
511 case st_reboot_screen
:
512 /// display the reboot screen and wait validation
516 SM_EVENT(ev_reboot
, st_end
);
517 SM_EVENT(ev_quit
, st_end
);
521 case st_restart_client
:
522 /// restart the client with login bypass params
525 /// connect to the FS (start the 'in game' mode)
528 if (ClientCfg
.R2Mode
)
531 SM_EVENT(ev_enter_game
, st_ingame
);
537 SM_EVENT(ev_enter_game
, st_end
);
538 SM_EVENT(ev_conn_failed
, st_shard_list
);
542 // case st_browser_screen:
543 // /// show the outgame browser
545 // if (!ClientCfg.TestBrowser)
547 // // in test browser mode, the browser is already init
551 // SM_BEGIN_EVENT_TABLE
552 // SM_EVENT(ev_connect, st_connect);
553 // SM_EVENT(ev_relog, st_login);
554 // SM_EVENT(ev_quit, st_end);
555 // SM_END_EVENT_TABLE
560 //SM_EVENT(ev_chars_received, st_ingame); // ignored for normal login procedure
561 //SM_EVENT(ev_no_user_char, st_ingame); // "
562 //SM_EVENT(ev_ready_received, st_ingame); // "
563 //SM_EVENT(ev_global_menu_exited, st_ingame); "
564 SM_EVENT(ev_connect
, st_leave_shard
); // connect to another shard
569 * Far TP / Server Hop / Reselect character entry point
572 // Server Hop part 1: We are not in main loop but still at character selection
573 // => make a hop to the specified server without doing anything with interface
574 // Far TP part 1.1: From the ingame main loop, the admin html box gives us an event ev_connect for the destination shard.
575 // Note: the admin html box is run by CInputHandlerManager::getInstance()->pumpEvents() in the main loop.
576 // Tip: to see where a co-task is resumed from, just add a breakpoint on the end of CCoTask::resume().
577 CAHManager::getInstance()->runActionHandler("quit_ryzom", NULL
, "");
579 if (!FarTP
.isIngame()) // assumes there is no Far TP starting between char selection and main loop, see below
581 crashLogAddServerHopEvent();
583 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)
586 SM_EVENT(ev_ingame_return
, st_disconnect
);
591 if(FarTP
.isReselectingChar())
592 crashLogAddReselectPersoEvent();
594 crashLogAddFarTpEvent();
596 if (NetMngr
.getConnectionState() == CNetworkConnection::Disconnect
)
597 FarTP
.onServerQuitOk(); // don't wait for onServerQuitOK() because the EGS is down
600 SM_EVENT(ev_ingame_return
, st_enter_far_tp_main_loop
);
601 SM_EVENT(ev_enter_game
, st_ingame
);
605 case st_enter_far_tp_main_loop
:
606 // if bgdownloader is used, then pause it
607 #ifdef RYZOM_BG_DOWNLOADER
612 // Far TP part 1.2: let the main loop finish the current frame.
613 // This is called when CONNECTION:SERVER_QUIT_OK is received (from NetMngr.update() in main loop).
616 SM_EVENT(ev_far_tp_main_loop_entered
, st_disconnect
);
620 // Far TP part 2: disconnect from the FS and unload shard-specific data (called from farTPmainLoop())
621 // FarTP.disconnectFromPreviousShard();
624 SM_EVENT(ev_connect
, st_reconnect_fs
);
627 case st_reconnect_fs
:
628 // Server Hop part 3.1: connect to the new shard (called from globalMenu())
629 // Far TP part 3.1: connect to the new shard (called from farTPmainLoop())
630 FarTP
.connectToNewShard();
633 SM_EVENT(ev_self_reconnected
, st_ingame
);
634 SM_EVENT(ev_chars_received
, st_reconnect_select_char
);
635 SM_EVENT(ev_no_user_char
, st_reconnect_error
);
636 SM_EVENT(ev_conn_failed
, st_reconnect_error
);
637 SM_EVENT(ev_conn_dropped
, st_reconnect_error
);
640 case st_reconnect_select_char
:
641 // Server Hop part 3.2: bypass character selection ui & select the same character.
642 // Far TP part 3.2: bypass character selection ui & select the same character.
643 // This is called from farTPmainloop(), when CONNECTION:USER_CHARS is received.
644 if( !FarTP
.isReselectingChar() )
646 FarTP
.selectCharAndEnter();
648 // Far TP part 3.2bis: see in farTPmainLoop()
650 if (!FarTP
.isIngame())
653 SM_EVENT(ev_ready_received
, st_exit_global_menu
);
654 SM_EVENT(ev_conn_dropped
, st_reconnect_error
);
660 SM_EVENT(ev_ready_received
, st_reconnect_ready
);
661 SM_EVENT(ev_conn_dropped
, st_reconnect_error
);
662 if ( FarTP
.isReselectingChar() && (ev
== ev_connect
) )
664 // Inside this Character Reselect we embed a new Server Hop
665 FarTP
.beginEmbeddedServerHop();
666 _CurrentState
= st_leave_shard
;
672 case st_exit_global_menu
:
673 // Server Hop part 3.3
674 // Stay in Server Hop state until the global menu has been exited
676 SM_EVENT(ev_global_menu_exited
, FarTP
.isServerHopEmbedded() ? st_reconnect_ready
: st_ingame
);
679 case st_reconnect_ready
:
680 // Far TP part 3.3: send ready.
681 // This is called from farTPmainloop(), when CONNECTION:READY is received.
683 if ( FarTP
.isServerHopEmbedded() )
684 FarTP
.endEmbeddedServerHop();
687 SM_EVENT(ev_enter_game
, st_ingame
);
688 SM_EVENT(ev_conn_dropped
, st_reconnect_error
);
691 case st_reconnect_error
:
696 SM_EVENT(ev_quit
, st_end
);
700 nlwarning("Unhandeled state");
707 // ***************************************************************************
709 // ***************************************************************************
713 extern std::string CurrentCookie
;
714 extern string ClientApp
;
719 extern bool ReloadUIFlag
;
724 * requestFarTPToSession
726 bool CFarTP::requestFarTPToSession(TSessionId sessionId
, uint8 charSlot
, CFarTP::TJoinMode joinMode
, bool bailOutIfSessionVanished
)
728 if (_HookedForEditor
)
730 // Redirect Far TP to editor instead of following instructions
731 _HookedForEditor
= false;
732 return FarTP
.requestFarTPToSession((TSessionId
)0, charSlot
, CFarTP::LaunchEditor
, true); // allow bailing out in case of edition session not reachable
735 // call the join session using the session browser interface
736 uint32 charId
= (NetMngr
.getLoginCookie().getUserId()<<4)+(0xf&charSlot
);
738 // Clean out for next Far TP
739 _SessionIdToJoinFast
= 0;
741 CSessionBrowserImpl
&sb
= CSessionBrowserImpl::getInstance();
743 // sb.setAuthInfo(NetMngr.getLoginCookie());
744 // sb.connectItf(CInetAddress("borisb", 80));
746 sb
.CurrentJoinMode
= joinMode
;
747 // send the join session
748 // _JoinSessionResultReceived = false;
756 TSessionId
sessionId(0);
757 sb
.joinEditSession(charId
, ClientApp
);
759 bool ret
= sb
.waitOneMessage(CSessionBrowserImpl::getMessageName("on_joinSessionResult"));
762 throw "Protocol error";
763 if (sb
._LastJoinSessionResult
== 2)
765 // the edit session did not exist, create a new one
766 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);
767 ret
= sb
.waitOneMessage(CSessionBrowserImpl::getMessageName("on_scheduleSessionResult"));
768 if (!ret
|| sb
._LastScheduleSessionResult
!= 0) throw "schedule error";
769 sessionId
= sb
._LastScheduleSessionId
;
771 // invite the char in the session
772 sb
.inviteCharacter(charId
, sessionId
, charId
, TSessionPartStatus::sps_edit_invited
);
773 ret
= sb
.waitOneMessage(CSessionBrowserImpl::getMessageName("on_invokeResult"));
774 if (!ret
|| sb
._LastInvokeResult
!= 0) throw "Invitation Error";
776 // now, start the edit session
777 sb
.startSession(charId
, sessionId
);
778 ret
= sb
.waitOneMessage(CSessionBrowserImpl::getMessageName("on_invokeResult"));
779 if (!ret
|| sb
._LastInvokeResult
!= 0) throw "start session error";
784 else if (sb
._LastJoinSessionResult
== 15)
786 sessionId
= sb
._LastJoinSessionId
;
787 // the session was closed, the SU has put it in planned state, start it
788 sb
.startSession(charId
, sessionId
);
789 ret
= sb
.waitOneMessage(CSessionBrowserImpl::getMessageName("on_invokeResult"));
790 if (!ret
|| sb
._LastInvokeResult
!= 0) throw "start session error (2), no DSS available";
794 else if (sb
._LastJoinSessionResult
!= 0)
796 // any other error are not recoverable from here
797 throw "join session error";
800 // ok, the join is accepted !, the other far TP work is done in the callback
805 sb
.joinSession(charId
, sessionId
, ClientApp
);
807 bool ret
= sb
.waitOneMessage(CSessionBrowserImpl::getMessageName("on_joinSessionResult"));
810 throw "Protocol error";
811 if (sb
._LastJoinSessionResult
== 16)
813 // #pragma message (NL_LOC_WRN "inform the player that he is banned from the ring")
814 throw "User ban from the ring";
816 if (sb
._LastJoinSessionResult
!= 0)
818 throw "Join session error";
820 // ok, the join is accepted !, the other far TP work is done in the callback
826 TSessionId
sessionId(0);
827 sb
.joinMainland(charId
, ClientApp
);
829 bool ret
= sb
.waitOneMessage(CSessionBrowserImpl::getMessageName("on_joinSessionResult"));
832 throw "Protocol error";
833 if (sb
._LastJoinSessionResult
!= 0)
835 // some error during the join
836 throw "Error joining session";
838 // ok, the join is accepted !, the other far TP work is done in the callback
849 // const string url = _URLBase + string(_URLTable[joinMode]);
853 // // Get the address of a front-end service via http
854 // if (!HttpClient.connect(url))
858 // case CFarTP::LaunchEditor:
859 // if (!HttpClient.sendGetWithCookie(url, "ryzomId", CurrentCookie, toString("charSlot=%u", charSlot)))
862 // case CFarTP::JoinSession:
863 // if (!HttpClient.sendPostWithCookie(url, "ryzomId", CurrentCookie, toString("sessionId=%u&charSlot=%u", sessionId, charSlot)))
866 // case CFarTP::JoinMainland:
867 // if (!HttpClient.sendPostWithCookie(url, "ryzomId", CurrentCookie, "ml="))
871 // if (!HttpClient.receive(res))
873 // HttpClient.disconnect();
874 // string::size_type luaPos = res.find("<lua>");
875 // if (luaPos == string::npos)
877 // res = res.substr(luaPos + 5);
878 // res = res.substr(0, res.find("</lua>"));
879 // nlinfo("Found server for %ssession %u", joinMode==CFarTP::LaunchEditor ? "editing " : "", sessionId);
882 // CInterfaceManager *pIM = CInterfaceManager::getInstance();
883 // pIM->executeLuaScript(res, true);
886 catch ( char *errorMsg
)
888 // // Get more details on the error
889 // string httpErrorStr;
890 // if (errorNum < 4) // we didn't receive an answer
891 // httpErrorStr = toString(" (HTTP error %u)", errorNum);
892 // if ( errorNum == 4 )
894 // string::size_type posEndOfFirstLine = res.find( "\n" );
895 // if ( posEndOfFirstLine == string::npos )
897 // // Invalid http answer
898 // httpErrorStr = toString(" (HTTP invalid)");
902 // // Http status not OK (e.g. "404 Not Found")
903 // httpErrorStr = res.substr( 0, posEndOfFirstLine );
904 // if ( httpErrorStr.find( "200 OK" ) == string::npos )
905 // httpErrorStr = " (" + httpErrorStr + ")";
907 // httpErrorStr.clear();
910 bool requestRetToMainland
= /*httpErrorStr.empty() &&*/ bailOutIfSessionVanished
;
911 bool isAttemptingEmbeddedServerHop
= isReselectingChar() && (joinMode
!= CFarTP::JoinSession
);
912 bool letReturnToCharSelect
= (!isIngame()) || isAttemptingEmbeddedServerHop
;
914 // User-friendly message
915 CInterfaceManager
*pIM
= CInterfaceManager::getInstance();
916 if ( letReturnToCharSelect
)
918 // // Hide all buttons except Quit. If !requestRetToMainland, we will show them back at the end of connectToNewShard().
919 // CAHManager::getInstance()->runActionHandler( "proc", NULL, "charsel_disable_buttons" );
920 // CAHManager::getInstance()->runActionHandler( "set", NULL, "target_property=ui:outgame:charsel:quit_but:active|value=1" );
922 CInterfaceElement
*btnOk
= CWidgetManager::getInstance()->getElementFromId("ui:outgame:charsel:message_box:ok");
924 btnOk
->setActive( ! requestRetToMainland
);
926 // Hide the black screen i.e. force showing the interface
927 CInterfaceElement
*charSelBlackScreen
= CWidgetManager::getInstance()->getElementFromId("ui:outgame:charsel:black_screen");
928 if (charSelBlackScreen
)
930 CViewBase
*charSelBlackScreenBitmap
= dynamic_cast<CViewBase
*>(charSelBlackScreen
);
931 if (charSelBlackScreenBitmap
)
932 charSelBlackScreenBitmap
->setAlpha(0);
935 pIM
->messageBoxWithHelp(
936 CI18N::get(requestRetToMainland
? "uiSessionVanishedFarTP" : "uiSessionUnreachable") + errorMsg
,
937 letReturnToCharSelect
? "ui:outgame:charsel" : "ui:interface");
940 string outErrorMsg
= toString("Could not join %ssession %u: '%s' error\n", joinMode
==CFarTP::LaunchEditor
? "editing " : "", sessionId
.asInt(), errorMsg
);
941 nlwarning( outErrorMsg
.c_str() );
942 // if ( httpErrorStr.empty() )
944 // string delimiter = "Content-Type: text/html";
945 // string::size_type delimPos = res.find( delimiter );
946 // if ( delimPos != string::npos )
947 // res = res.substr( delimPos + delimiter.size() );
952 // WarningLog->displayRaw( res.substr( pos, 200 ).c_str() ); // truncates long strings
955 // while ( pos < res.size() );
956 WarningLog
->displayRawNL( "" );
958 // Save this error (regular log file is deleted at every startup)
959 // res = res + "\n\n";
963 if ( outputF.open( getLogDirectory() + "error_join.log", true, true ) )
966 time( ¤tTime );
967 string headerS = NLMISC::toString( "\n\n%s%s\n\n", asctime(localtime(¤tTime)), outErrorMsg.c_str() );
968 outputF.serialBuffer( (uint8*)(&headerS[0]), (uint)headerS.size() );
969 // outputF.serialBuffer( (uint8*)(&res[0]), res.size() );
973 catch (const Exception &)
977 // If the session is not a permanent session and has vanished, pop the position
978 if ( requestRetToMainland
)
980 requestReturnToPreviousSession( sessionId
);
981 LastGameCycle
= NetMngr
.getCurrentServerTick();
982 NetMngr
.send(NetMngr
.getCurrentServerTick());
984 else if ( letReturnToCharSelect
)
986 // Show all buttons except 'New character' so that the character can retry entering game or choose another character.
987 CAHManager::getInstance()->runActionHandler( "proc", NULL
, "charsel_enable_buttons" );
988 CAHManager::getInstance()->runActionHandler( "set", NULL
, "target_property=ui:outgame:charsel:create_new_but:active|value=0" );
990 CInterfaceGroup
* charselGroup
= dynamic_cast<CInterfaceGroup
*>(CWidgetManager::getInstance()->getElementFromId("ui:outgame:charsel"));
992 CAHManager::getInstance()->runActionHandler( "proc", charselGroup
, "charsel_init_buttons" );
1001 * hookNextFarTPForEditor
1003 void CFarTP::hookNextFarTPForEditor()
1005 _HookedForEditor
= true;
1009 * requestReturnToPreviousSession
1011 void CFarTP::requestReturnToPreviousSession(TSessionId rejectedSessionId
)
1013 const char *msgName
= "CONNECTION:RET_MAINLAND";
1015 nlverify(GenericMsgHeaderMngr
.pushNameToStream(msgName
, out
));
1016 out
.serial(PlayerSelectedSlot
);
1017 out
.serial(rejectedSessionId
);
1019 nlinfo("%s sent", msgName
);
1023 * requestReconnection
1025 void CFarTP::requestReconnection()
1027 _ReselectingChar
= true;
1028 if (!requestFarTPToSession(TSessionId(std::numeric_limits
<uint16
>::max()), std::numeric_limits
<uint8
>::max(), CFarTP::JoinMainland
, false))
1029 _ReselectingChar
= false;
1033 const char * CFarTP::_URLTable
[CFarTP::NbJoinModes
] =
1045 bool CFarTP::isFarTPInProgress() const
1047 CLoginStateMachine::TState state
= LoginSM
.getCurrentState();
1048 return (state
== CLoginStateMachine::st_leave_shard
||
1049 state
== CLoginStateMachine::st_enter_far_tp_main_loop
||
1050 state
== CLoginStateMachine::st_disconnect
||
1051 state
== CLoginStateMachine::st_reconnect_fs
||
1052 state
== CLoginStateMachine::st_reconnect_select_char
||
1053 state
== CLoginStateMachine::st_reconnect_ready
||
1054 state
== CLoginStateMachine::st_reconnect_error
);
1057 bool CFarTP::isServerHopInProgress() const
1059 CLoginStateMachine::TState state
= LoginSM
.getCurrentState();
1060 return (state
== CLoginStateMachine::st_leave_shard
||
1061 state
== CLoginStateMachine::st_reconnect_fs
||
1062 state
== CLoginStateMachine::st_reconnect_select_char
||
1063 state
== CLoginStateMachine::st_exit_global_menu
||
1064 state
== CLoginStateMachine::st_reconnect_error
); // TODO: error handling
1068 // from begin of Far TP to disconnection
1069 bool CFarTP::isLeavingShard() const
1071 CLoginStateMachine::TState state
= LoginSM
.getCurrentState();
1072 return (state
== CLoginStateMachine::st_leave_shard
||
1073 state
== CLoginStateMachine::st_enter_far_tp_main_loop
||
1074 state
== CLoginStateMachine::st_disconnect
);
1077 // from reconnection to game entering
1078 bool CFarTP::isJoiningShard() const
1080 CLoginStateMachine::TState state
= LoginSM
.getCurrentState();
1081 return (state
== CLoginStateMachine::st_reconnect_fs
||
1082 state
== CLoginStateMachine::st_reconnect_select_char
||
1083 state
== CLoginStateMachine::st_reconnect_ready
||
1084 state
== CLoginStateMachine::st_reconnect_error
);
1092 void CFarTP::onServerQuitOk()
1094 game_exit_request
= false;
1095 ryzom_exit_request
= false;
1097 if (LoginSM
.getCurrentState() == CLoginStateMachine::st_leave_shard
)
1098 LoginSM
.pushEvent(CLoginStateMachine::ev_ingame_return
);
1101 void CFarTP::onServerQuitAbort()
1103 game_exit_request
= false;
1104 ryzom_exit_request
= false;
1106 if (LoginSM
.getCurrentState() == CLoginStateMachine::st_leave_shard
)
1107 LoginSM
.pushEvent(CLoginStateMachine::ev_enter_game
);
1108 _ReselectingChar
= false;
1111 void CFarTP::disconnectFromPreviousShard()
1113 CInterfaceManager
*pIM
= CInterfaceManager::getInstance();
1117 // Display background (TODO: not Kami)
1118 beginLoading (StartBackground
);
1119 UseEscapeDuringLoading
= false;
1122 selectTipsOfTheDay (rand());
1124 // Start progress bar and display background
1125 ProgressBar
.reset (BAR_STEP_TP
);
1126 string
nmsg("Loading...");
1127 ProgressBar
.newMessage ( ClientCfg
.buildLoadingString(nmsg
) );
1128 ProgressBar
.progress(0);
1130 // Play music and fade out the Game Sound
1133 SoundMngr
->fadeOutGameSound(ClientCfg
.SoundTPFade
);
1135 // Stop and enable music
1136 SoundMngr
->stopMusic(0);
1137 SoundMngr
->setupFadeSound(0.0f
, 1.0f
);
1139 // Loading Music Loop.ogg
1140 LoadingMusic
= ClientCfg
.LoadingMusic
;
1141 // SoundMngr->playEventMusic(LoadingMusic, CSoundManager::LoadingMusicXFade, true);
1142 SoundMngr
->playMusic(LoadingMusic
, 0, false, true, true);
1146 // Disconnect from the FS
1147 NetMngr
.disconnect();
1151 // Save the R2EDEnabled flag to know if we are switching it when we reconnect
1152 _PreviousR2EdEnabled
= ClientCfg
.R2EDEnabled
;
1154 // Release R2 editor if applicable
1155 R2::getEditor().autoConfigRelease(IsInRingSession
);
1158 if( isReselectingChar() )
1160 releaseMainLoopReselect();
1164 // String manager: remove all waiting callbacks and removers
1165 // (if some interface stuff has not received its string yet, its remover will get useless)
1166 STRING_MANAGER::CStringManagerClient::release( false );
1171 serverReceivedReady
= false;
1172 CharNameValidArrived
= false;
1173 UserCharPosReceived
= false;
1174 SabrinaPhraseBookLoaded
= false;
1177 // Restart the network manager, the interface sync counter and the entities
1178 pIM
->resetShardSpecificData();
1181 ServerToLocal autocopy stuff:
1182 When reinit will reset server counters to 0 in the server database, onServerChange()
1183 will be called and data will be copied since CInterfaceManager::_LocalSyncActionCounter==0
1184 (done in resetShardSpecificData() above)
1185 If we do the resetShardSpecificData() after, scenari could arise where Local database could not be reseted
1189 if (isIngame() && !isReselectingChar())
1191 nlinfo("FarTP: calling EntitiesMngr.reinit()");
1192 EntitiesMngr
.reinit();
1194 LoginSM
.pushEvent(CLoginStateMachine::ev_connect
);
1197 void CFarTP::connectToNewShard()
1199 // TODO: start commands?
1201 // Connect to the next FS
1202 NetMngr
.initCookie(Cookie
, FSAddr
);
1204 // connect the session browser to the new shard
1205 NLNET::CInetAddress
sbsAddress(CSessionBrowserImpl::getInstance().getFrontEndAddress());
1206 sbsAddress
.setPort(sbsAddress
.port()+SBSPortOffset
);
1207 CSessionBrowserImpl::getInstance().connectItf(sbsAddress
);
1210 NetMngr
.connect(result
);
1211 if (!result
.empty())
1213 _Reason
= new string(result
);
1214 LoginSM
.pushEvent(CLoginStateMachine::ev_conn_failed
);
1218 // Reinit the string manager cache.
1219 STRING_MANAGER::CStringManagerClient::instance()->initCache(FSAddr
, ClientCfg
.LanguageCode
);
1221 // reset the chat mode
1222 ChatMngr
.resetChatMode();
1224 // The next step will be triggered by the CONNECTION:USER_CHARS msg from the server
1227 // return to character selection screen
1228 bool CFarTP::reselectCharacter()
1230 if ( ! reconnection() )
1232 // The user clicked the Quit button
1239 void CFarTP::selectCharAndEnter()
1242 nlverify (GenericMsgHeaderMngr
.pushNameToStream("CONNECTION:SELECT_CHAR", out
));
1243 CSelectCharMsg SelectCharMsg
;
1244 SelectCharMsg
.c
= (uint8
)PlayerSelectedSlot
; // PlayerSelectedSlot has not been reset
1245 out
.serial(SelectCharMsg
);
1249 nlverify(GenericMsgHeaderMngr.pushNameToStream("CONNECTION:ENTER", out2));
1252 LastGameCycle
= NetMngr
.getCurrentServerTick();
1253 NetMngr
.send(NetMngr
.getCurrentServerTick());
1256 // globalMenu will exit when WaitServerAnswer and serverReceivedReady (triggered by the CONNECTION:READY msg) are true
1258 // Next step will be triggered by the CONNECTION:READY msg from the server
1261 void CFarTP::sendReady()
1263 if ( isReselectingChar() )
1270 RT
.updateRyzomClock(NetMngr
.getCurrentServerTick());
1271 DayNightCycleHour
= (float)RT
.getRyzomTime();
1272 CurrSeason
= RT
.getRyzomSeason();
1273 RT
.updateRyzomClock(NetMngr
.getCurrentServerTick());
1274 DayNightCycleHour
= (float)RT
.getRyzomTime();
1275 ManualSeasonValue
= RT
.getRyzomSeason();
1277 // Reset all fx (no need to CTimedFXManager::getInstance().reset() and EntitiesMngr.getGroundFXManager().reset() because the entities have been removed)
1278 CProjectileManager::getInstance().reset();
1279 FXMngr
.reset(); // (must be done after EntitiesMngr.release())
1280 EntitiesMngr
.getGroundFXManager().init(Scene
, ClientCfg
.GroundFXMaxDist
, ClientCfg
.GroundFXMaxNB
, ClientCfg
.GroundFXCacheSize
);
1282 // Get the sheet for the user from the CFG.
1283 // Initialize the user and add him into the entity manager.
1284 // DO IT AFTER: Database, Collision Manager, PACS, scene, animations loaded.
1285 CSheetId
userSheet(ClientCfg
.UserSheet
);
1286 nlinfo("FarTP: calling EntitiesMngr.create(0, userSheet.asInt())");
1287 TNewEntityInfo emptyEntityInfo
;
1288 emptyEntityInfo
.reset();
1289 EntitiesMngr
.create(0, userSheet
.asInt(), emptyEntityInfo
);
1291 // Wait for the start position (USER_CHAR) and set the continent
1292 waitForUserCharReceived();
1295 CInterfaceManager
*pIM
= CInterfaceManager::getInstance();
1296 if ( ClientCfg
.R2EDEnabled
!= _PreviousR2EdEnabled
)
1298 // Reload textures, keys and interface config if we are switch between playing and r2 mode
1299 // Must be done after receiving the current R2EDEnabled flag (USER_CHAR)
1300 pIM
->loadIngameInterfaceTextures();
1302 // Unload (and save if leaving normal playing mode) keys and interface config
1303 // Instead of doing it in disconnectFromPreviousShard(), we do it here, only when it's needed
1304 ClientCfg
.R2EDEnabled
= ! ClientCfg
.R2EDEnabled
;
1305 pIM
->uninitInGame0();
1306 CItemGroupManager::getInstance()->uninit();
1308 ClientCfg
.R2EDEnabled
= ! ClientCfg
.R2EDEnabled
;
1309 ActionsContext
.removeAllCombos();
1311 if ( ! ClientCfg
.R2EDEnabled
)
1313 // Remove all existing keys and load them back, and load new interface config
1315 CWidgetManager::getInstance()->hideAllWindows();
1316 pIM
->loadInterfaceConfig();
1317 pIM
->loadLandmarks();
1321 R2::ReloadUIFlag
= true; // in R2ED mode the CEditor class deals with it
1324 pIM
->configureQuitDialogBox(); // must be called after waitForUserCharReceived() to know the ring config
1326 ContinentMngr
.select(UserEntity
->pos(), ProgressBar
); // IMPORTANT : must select continent after ui init, because ui init also load landmarks (located in the icfg file)
1327 // landmarks would be invisible else (RT 12239)
1331 // Update Network until current tick increase.
1332 LastGameCycle
= NetMngr
.getCurrentServerTick();
1333 while (LastGameCycle
== NetMngr
.getCurrentServerTick())
1335 // Event server get events
1336 CInputHandlerManager::getInstance()->pumpEventsNoIM();
1339 IngameDbMngr
.flushObserverCalls();
1340 NLGUI::CDBManager::getInstance()->flushObserverCalls();
1341 // Be nice to the system
1344 LastGameCycle
= NetMngr
.getCurrentServerTick();
1345 ProgressBar
.progress(1);
1347 // Create the message for the server to create the character.
1349 if(GenericMsgHeaderMngr
.pushNameToStream("CONNECTION:READY", out
))
1351 out
.serial(ClientCfg
.LanguageCode
);
1353 NetMngr
.send(NetMngr
.getCurrentServerTick());
1356 // To be sure server crash is not fault of client
1357 ConnectionReadySent
= true; // must be called before BotChatPageAll->initAfterConnectionReady()
1359 // To reset the inputs.
1360 CInputHandlerManager::getInstance()->pumpEventsNoIM();
1362 if(BotChatPageAll
&& (! ClientCfg
.R2EDEnabled
))
1363 BotChatPageAll
->initAfterConnectionReady();
1365 // Transition from background to game
1369 ProgressBar
.finish();
1371 LoginSM
.pushEvent(CLoginStateMachine::ev_enter_game
);
1380 void CFarTP::onFailure()
1386 reason
+= ": " + (*_Reason
);
1390 else if (noUserChar
)
1392 reason
+= ": no characters found!";
1394 Driver
->systemMessageBox(("Unable to join shard"+reason
).c_str(), "Error", UDriver::okType
, UDriver::exclamationIcon
);
1396 // TODO: recover from error
1398 LoginSM
.pushEvent(CLoginStateMachine::ev_quit
);
1401 void CFarTP::onDssDown(bool forceReturn
)
1405 // If leaving shard, don't bother with DSS "downitude"
1406 if (isLeavingShard())
1408 // If joining shard, store event and launch it at the end of reconnection
1409 if (isJoiningShard())
1411 _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)
1416 CInterfaceManager
*pIM
= CInterfaceManager::getInstance();
1417 pIM
->messageBoxWithHelp(CI18N::get("uiDisconnected"));
1418 requestReturnToPreviousSession();
1421 extern bool loginFinished
;
1422 void setLoginFinished( bool f
);
1423 extern bool loginOK
;
1425 void CFarTP::joinSessionResult(uint32
/* userId */, TSessionId
/* sessionId */, uint32
/* result */, const std::string
&/* shardAddr */, const std::string
&/* participantStatus */)
1427 // _LastJoinSessionResultMsg = result;
1429 // _JoinSessionResultReceived = true;
1433 // // ok, the join is successful
1435 // FSAddr = shardAddr;
1437 // setLoginFinished( true );
1440 // LoginSM.pushEvent(CLoginStateMachine::ev_connect);
1445 // // TODO : display the error message on client screen and log
1451 void CFarTP::setJoinSessionResult(TSessionId sessionId
, const CSecurityCode
& securityCode
)
1453 _SessionIdToJoinFast
= sessionId
;
1454 _SecurityCodeForDisconnection
= securityCode
;
1457 void CFarTP::writeSecurityCodeForDisconnection(NLMISC::IStream
& msgout
)
1459 CSecurityCheckForFastDisconnection::forwardSecurityCode(msgout
, _SessionIdToJoinFast
, _SecurityCodeForDisconnection
);
1462 // Not run by the cotask but within the main loop
1463 void CFarTP::farTPmainLoop()
1465 ConnectionReadySent
= false;
1466 LoginSM
.pushEvent(CLoginStateMachine::ev_far_tp_main_loop_entered
);
1468 disconnectFromPreviousShard();
1470 uint nbRecoSelectCharReceived
= 0;
1472 bool welcomeWindow
= true;
1474 // Update network until the end of the FarTP process, before resuming the main loop
1475 while (!ConnectionReadySent
)
1477 // Event server get events
1478 CInputHandlerManager::getInstance()->pumpEventsNoIM();
1482 IngameDbMngr
.flushObserverCalls();
1483 NLGUI::CDBManager::getInstance()->flushObserverCalls();
1485 // TODO: resend in case the last datagram sent was lost?
1486 // // check if we can send another dated block
1487 // if (NetMngr.getCurrentServerTick() != serverTick)
1490 // serverTick = NetMngr.getCurrentServerTick();
1491 // NetMngr.send(serverTick);
1495 // // Send dummy info
1499 if (LoginSM
.getCurrentState() == CLoginStateMachine::st_reconnect_select_char
)
1501 // Far TP part 3.2bis: go to character selection dialog
1502 // This is done outside the co-routine because it may need to co-routine to do an embedded server hop
1503 if ( FarTP
.isReselectingChar() )
1505 ++nbRecoSelectCharReceived
;
1506 if ( nbRecoSelectCharReceived
<= 1 )
1508 ClientCfg
.SelectCharacter
= -1; // turn off character autoselection
1509 if ( ! FarTP
.reselectCharacter() ) // it should not return here in farTPmainLoop() in the same state otherwise this would be called twice
1513 nlwarning( "Received more than one st_reconnect_select_char event" );
1516 else if (LoginSM
.getCurrentState() == CLoginStateMachine::st_reconnect_ready
)
1518 // Don't call sendReady() within the cotask but within the main loop, as it contains
1519 // event/network loops that could trigger a global exit().
1521 welcomeWindow
= !isReselectingChar();
1524 // Be nice to the system
1528 // active/desactive welcome window
1530 initWelcomeWindow();