// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
# include "stdpch.h"
# include "nel/3d/u_driver.h"
# include "far_tp.h"
# include "net_manager.h"
# include "connection.h"
# include "interface_v3/interface_manager.h"
# include "login_patch.h"
# include "init_main_loop.h"
# include "weather.h"
# include "time_client.h"
# include "timed_fx_manager.h"
# include "world_database_manager.h"
# include "continent_manager.h"
# include "user_entity.h"
# include "entities.h"
# include "interface_v3/input_handler_manager.h"
# include "interface_v3/bot_chat_page_all.h"
# include "sound_manager.h"
# include "actions_client.h"
# include "r2/editor.h"
# include "http_client.h"
# include "global.h"
# include "release.h"
# include "nel/misc/string_conversion.h"
# include "debug_client.h"
# include "session_browser_impl.h"
# include "game_share/security_check.h"
# include "client_chat_manager.h"
# include "bg_downloader_access.h"
# include "login_progress_post_thread.h"
using namespace NLMISC ;
using namespace NLNET ;
using namespace NL3D ;
using namespace RSMGR ;
using namespace R2 ;
extern CClientChatManager ChatMngr ;
extern CVariable < uint16 > SBSPortOffset ;
// ***************************************************************************
// Login state machine
// ***************************************************************************
// Adapted from "nel/misc/string_conversion.h"
# define NL_BEGIN_CLASS_STRING_CONVERSION_TABLE(__class, __type) \
static const NLMISC : : CStringConversion < __class : : __type > : : CPair __type # # _nl_string_conversion_table [ ] = \
{
# define NL_END_CLASS_STRING_CONVERSION_TABLE(__class, __type, __tableName, __defaultValue) \
} ; \
NLMISC : : CStringConversion < __class : : __type > \
__tableName ( __type # # _nl_string_conversion_table , sizeof ( __type # # _nl_string_conversion_table ) \
/ sizeof ( __type # # _nl_string_conversion_table [ 0 ] ) , __class : : __defaultValue ) ;
# define NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(__class, val) { #val, __class::val},
NL_BEGIN_CLASS_STRING_CONVERSION_TABLE ( CLoginStateMachine , TState )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , st_start )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , st_login )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , st_auto_login )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , st_shard_list )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , st_start_config )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , st_scan_data )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , st_display_eula )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , st_check_patch )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , st_display_cat )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , st_patch )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , st_close_client )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , st_reboot_screen )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , st_restart_client )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , st_connect )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , st_browser_screen )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , st_ingame )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , st_leave_shard )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , st_enter_far_tp_main_loop )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , st_disconnect )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , st_reconnect_fs )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , st_reconnect_select_char )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , st_reconnect_ready )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , st_exit_global_menu )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , st_reconnect_error )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , st_create_account )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , st_end )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , st_unknown )
NL_END_CLASS_STRING_CONVERSION_TABLE ( CLoginStateMachine , TState , StateConversion , st_unknown )
NL_BEGIN_CLASS_STRING_CONVERSION_TABLE ( CLoginStateMachine , TEvent )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , ev_quit )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , ev_skip_all_login )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , ev_init_done )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , ev_game_conf )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , ev_data_scan )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , ev_close_data_scan )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , ev_login_ok )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , ev_bad_login )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , ev_shard_selected )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , ev_patch_needed )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , ev_no_patch )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , ev_run_patch )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , ev_close_patch )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , ev_accept_eula )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , ev_decline_eula )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , ev_reboot )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , ev_ingame_return )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , ev_far_tp_main_loop_entered )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , ev_connect )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , ev_conn_failed )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , ev_conn_dropped )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , ev_enter_game )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , ev_self_reconnected )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , ev_chars_received )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , ev_no_user_char )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , ev_ready_received )
// NL_CLASS_STRING_CONVERSION_TABLE_ENTRY(CLoginStateMachine, ev_reselect_char)
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , ev_reconnect_ok_received )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , ev_global_menu_exited )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , ev_relog )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , ev_create_account )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , ev_close_create_account )
NL_CLASS_STRING_CONVERSION_TABLE_ENTRY ( CLoginStateMachine , ev_unknown )
NL_END_CLASS_STRING_CONVERSION_TABLE ( CLoginStateMachine , TEvent , EventConversion , ev_unknown )
//-----------------------------------------------
// toString :
//-----------------------------------------------
const std : : string & CLoginStateMachine : : toString ( CLoginStateMachine : : TState state )
{
return StateConversion . toString ( state ) ;
}
//-----------------------------------------------
// toString :
//-----------------------------------------------
const std : : string & CLoginStateMachine : : toString ( CLoginStateMachine : : TEvent event )
{
return EventConversion . toString ( event ) ;
}
# define SM_BEGIN_EVENT_TABLE \
for ( ; ! isTerminationRequested ( ) ; ) \
{ \
TEvent ev = waitEvent ( ) ; \
# define SM_END_EVENT_TABLE \
} \
# define SM_EVENT(eventId, stateId) \
if ( ev = = eventId ) \
{ \
try \
{ \
COFile outputF ; \
string sLog = NLMISC : : toString ( " [%s] %s -> %s \n " , CLoginStateMachine : : toString ( ev ) . c_str ( ) , CLoginStateMachine : : toString ( _CurrentState ) . c_str ( ) , CLoginStateMachine : : toString ( stateId ) . c_str ( ) ) ; \
if ( outputF . open ( getLogDirectory ( ) + " error_join.log " , true , true ) ) \
{ \
outputF . serialBuffer ( ( uint8 * ) ( & sLog [ 0 ] ) , ( uint ) sLog . size ( ) ) ; \
outputF . close ( ) ; \
} \
} \
catch ( Exception & ) \
{ } \
_CurrentState = stateId ; \
break ; \
} \
extern std : : string LoginLogin , LoginPassword ;
extern bool noUserChar ;
extern bool userChar ;
extern bool serverReceivedReady ;
extern bool CharNameValidArrived ;
extern bool FirstFrame ;
extern bool IsInRingSession ;
extern void selectTipsOfTheDay ( uint tips ) ;
# define BAR_STEP_TP 2
CLoginStateMachine : : TEvent CLoginStateMachine : : waitEvent ( )
{
nlassert ( CCoTask : : getCurrentTask ( ) = = this ) ;
while ( _NextEvents . empty ( ) & & ! isTerminationRequested ( ) )
{
// wait until someone push an event on the state machine
yield ( ) ;
if ( isTerminationRequested ( ) )
return ev_quit ;
}
TEvent ev = _NextEvents . front ( ) ;
_NextEvents . pop_front ( ) ;
return ev ;
}
void CLoginStateMachine : : pushEvent ( TEvent eventId )
{
// set the next event
_NextEvents . push_back ( eventId ) ;
if ( CCoTask : : getCurrentTask ( ) ! = this )
{
/// resume the state machine to advance state
resume ( ) ;
}
}
void CLoginStateMachine : : run ( )
{
// state machine coroutine
while ( _CurrentState ! = st_end & & ! isTerminationRequested ( ) )
{
switch ( _CurrentState )
{
case st_start :
/// initial state
if ( ! ClientCfg . TestBrowser )
{
if ( LoginLogin . empty ( ) )
{
// standard procedure
SM_BEGIN_EVENT_TABLE
SM_EVENT ( ev_init_done , st_login ) ;
SM_EVENT ( ev_skip_all_login , st_ingame ) ;
SM_EVENT ( ev_quit , st_end ) ;
SM_END_EVENT_TABLE
}
else
{
// login 2, bypass the login screen
SM_BEGIN_EVENT_TABLE
SM_EVENT ( ev_init_done , st_auto_login ) ;
SM_EVENT ( ev_skip_all_login , st_ingame ) ;
SM_EVENT ( ev_quit , st_end ) ;
SM_END_EVENT_TABLE
}
}
// else
// {
// // browser test mode
// SM_BEGIN_EVENT_TABLE
// SM_EVENT(ev_init_done, st_browser_screen);
// SM_EVENT(ev_quit, st_end);
// SM_END_EVENT_TABLE
// }
break ;
case st_login :
/// display login screen and options
{
initLoginScreen ( ) ;
if ( ClientCfg . R2Mode )
{
// r2 mode
SM_BEGIN_EVENT_TABLE
SM_EVENT ( ev_login_ok , st_check_patch ) ;
SM_EVENT ( ev_game_conf , st_start_config ) ;
SM_EVENT ( ev_data_scan , st_scan_data ) ;
SM_EVENT ( ev_create_account , st_create_account ) ;
SM_EVENT ( ev_bad_login , st_login ) ;
SM_EVENT ( ev_quit , st_end ) ;
SM_END_EVENT_TABLE
}
else
{
// legacy mode
SM_BEGIN_EVENT_TABLE
SM_EVENT ( ev_login_ok , st_shard_list ) ;
SM_EVENT ( ev_game_conf , st_start_config ) ;
SM_EVENT ( ev_data_scan , st_scan_data ) ;
SM_EVENT ( ev_create_account , st_create_account ) ;
SM_EVENT ( ev_bad_login , st_login ) ;
SM_EVENT ( ev_quit , st_end ) ;
SM_END_EVENT_TABLE
}
}
break ;
case st_auto_login :
initAutoLogin ( ) ;
// if (ClientCfg.R2Mode)
{
// r2 mode
SM_BEGIN_EVENT_TABLE
SM_EVENT ( ev_login_ok , st_check_patch ) ;
SM_EVENT ( ev_quit , st_end ) ;
SM_END_EVENT_TABLE
}
// else
// {
// // legacy mode
// SM_BEGIN_EVENT_TABLE
// SM_EVENT(ev_login_ok, st_check_patch);
// SM_EVENT(ev_quit, st_end);
// SM_END_EVENT_TABLE
// }
break ;
case st_shard_list :
/// display the shard list
initShardDisplay ( ) ;
SM_BEGIN_EVENT_TABLE
SM_EVENT ( ev_shard_selected , st_check_patch ) ;
SM_EVENT ( ev_quit , st_end ) ;
SM_END_EVENT_TABLE
break ;
case st_start_config :
/// launch the configurator and close ryzom
break ;
case st_scan_data :
/// run the check data thread
initDataScan ( ) ;
SM_BEGIN_EVENT_TABLE
SM_EVENT ( ev_close_data_scan , st_login ) ;
SM_EVENT ( ev_quit , st_end ) ;
SM_END_EVENT_TABLE
break ;
case st_create_account :
if ( initCreateAccount ( ) )
{
SM_BEGIN_EVENT_TABLE
SM_EVENT ( ev_close_create_account , st_login ) ;
SM_EVENT ( ev_quit , st_end ) ;
SM_END_EVENT_TABLE
}
else
{
// return to login menu if an error occured
_CurrentState = st_login ;
}
break ;
case st_display_eula :
{
/// display the eula and wait for validation
if ( ClientCfg . SkipEULA )
{
// we don't want to see eula, auto accept
pushEvent ( ev_accept_eula ) ;
}
bool mustReboot = false ;
if ( isBGDownloadEnabled ( ) )
{
mustReboot = CBGDownloaderAccess : : getInstance ( ) . mustLaunchBatFile ( ) ;
}
else
{
mustReboot = CPatchManager : : getInstance ( ) - > mustLaunchBatFile ( ) ;
}
if ( mustReboot )
{
// skip eula and show reboot screen
_CurrentState = st_reboot_screen ;
break ;
}
initEula ( ) ;
// Login sequence was reordered
// if (ClientCfg.R2Mode)
// {
// // ring mode
// SM_BEGIN_EVENT_TABLE
// SM_EVENT(ev_accept_eula, st_browser_screen);
// SM_EVENT(ev_quit, st_end);
// SM_END_EVENT_TABLE
// }
// else
// {
// // legacy mode
SM_BEGIN_EVENT_TABLE
SM_EVENT ( ev_accept_eula , st_connect ) ;
SM_EVENT ( ev_quit , st_end ) ;
SM_END_EVENT_TABLE
// }
}
break ;
case st_check_patch :
/// check the data to check if patch needed
CLoginProgressPostThread : : getInstance ( ) . step ( CLoginStep ( LoginStep_PostLogin , " login_step_post_login " ) ) ;
if ( ! ClientCfg . PatchWanted )
{
// client don't want to be patched !
_CurrentState = st_display_eula ;
break ;
}
initPatchCheck ( ) ;
SM_BEGIN_EVENT_TABLE
if ( isBGDownloadEnabled ( ) )
{
SM_EVENT ( ev_patch_needed , st_patch ) ; // no choice for patch content when background downloader is used
}
else
{
SM_EVENT ( ev_patch_needed , st_display_cat ) ;
}
SM_EVENT ( ev_no_patch , st_display_eula ) ;
SM_EVENT ( ev_quit , st_end ) ;
SM_END_EVENT_TABLE
break ;
case st_display_cat :
initCatDisplay ( ) ;
SM_BEGIN_EVENT_TABLE
SM_EVENT ( ev_run_patch , st_patch ) ;
SM_EVENT ( ev_quit , st_end ) ;
SM_END_EVENT_TABLE
break ;
case st_patch :
/// run the patch process and display progress
initPatch ( ) ;
SM_BEGIN_EVENT_TABLE
SM_EVENT ( ev_close_patch , st_display_eula ) ;
SM_EVENT ( ev_quit , st_end ) ;
SM_END_EVENT_TABLE
break ;
case st_close_client :
/// terminate the client and quit
break ;
case st_reboot_screen :
/// display the reboot screen and wait validation
initReboot ( ) ;
SM_BEGIN_EVENT_TABLE
SM_EVENT ( ev_reboot , st_end ) ;
SM_EVENT ( ev_quit , st_end ) ;
SM_END_EVENT_TABLE
break ;
case st_restart_client :
/// restart the client with login bypass params
break ;
case st_connect :
/// connect to the FS (start the 'in game' mode)
ConnectToShard ( ) ;
if ( ClientCfg . R2Mode )
{
SM_BEGIN_EVENT_TABLE
SM_EVENT ( ev_enter_game , st_ingame ) ;
SM_END_EVENT_TABLE
}
else
{
SM_BEGIN_EVENT_TABLE
SM_EVENT ( ev_enter_game , st_end ) ;
SM_EVENT ( ev_conn_failed , st_shard_list ) ;
SM_END_EVENT_TABLE
}
break ;
// case st_browser_screen:
// /// show the outgame browser
//
// if (!ClientCfg.TestBrowser)
// {
// // in test browser mode, the browser is already init
// initWebBrowser();
// }
//
// SM_BEGIN_EVENT_TABLE
// SM_EVENT(ev_connect, st_connect);
// SM_EVENT(ev_relog, st_login);
// SM_EVENT(ev_quit, st_end);
// SM_END_EVENT_TABLE
//
// break;
case st_ingame :
SM_BEGIN_EVENT_TABLE
//SM_EVENT(ev_chars_received, st_ingame); // ignored for normal login procedure
//SM_EVENT(ev_no_user_char, st_ingame); // "
//SM_EVENT(ev_ready_received, st_ingame); // "
//SM_EVENT(ev_global_menu_exited, st_ingame); "
SM_EVENT ( ev_connect , st_leave_shard ) ; // connect to another shard
SM_END_EVENT_TABLE
break ;
case st_leave_shard :
/*
* Far TP / Server Hop / Reselect character entry point
*/
// Server Hop part 1: We are not in main loop but still at character selection
// => make a hop to the specified server without doing anything with interface
// Far TP part 1.1: From the ingame main loop, the admin html box gives us an event ev_connect for the destination shard.
// Note: the admin html box is run by CInputHandlerManager::getInstance()->pumpEvents() in the main loop.
// Tip: to see where a co-task is resumed from, just add a breakpoint on the end of CCoTask::resume().
CInterfaceManager : : getInstance ( ) - > runActionHandler ( " quit_ryzom " , NULL , " " ) ;
if ( ! FarTP . isIngame ( ) ) // assumes there is no Far TP starting between char selection and main loop, see below
{
crashLogAddServerHopEvent ( ) ;
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)
SM_BEGIN_EVENT_TABLE
SM_EVENT ( ev_ingame_return , st_disconnect ) ;
SM_END_EVENT_TABLE
}
else
{
if ( FarTP . isReselectingChar ( ) )
crashLogAddReselectPersoEvent ( ) ;
else
crashLogAddFarTpEvent ( ) ;
if ( NetMngr . getConnectionState ( ) = = CNetworkConnection : : Disconnect )
FarTP . onServerQuitOk ( ) ; // don't wait for onServerQuitOK() because the EGS is down
SM_BEGIN_EVENT_TABLE
SM_EVENT ( ev_ingame_return , st_enter_far_tp_main_loop ) ;
SM_EVENT ( ev_enter_game , st_ingame ) ;
SM_END_EVENT_TABLE
}
break ;
case st_enter_far_tp_main_loop :
// if bgdownloader is used, then pause it
pauseBGDownloader ( ) ;
// Far TP part 1.2: let the main loop finish the current frame.
// This is called when CONNECTION:SERVER_QUIT_OK is received (from NetMngr.update() in main loop).
SM_BEGIN_EVENT_TABLE
SM_EVENT ( ev_far_tp_main_loop_entered , st_disconnect ) ;
SM_END_EVENT_TABLE
break ;
case st_disconnect :
// Far TP part 2: disconnect from the FS and unload shard-specific data (called from farTPmainLoop())
FarTP . disconnectFromPreviousShard ( ) ;
SM_BEGIN_EVENT_TABLE
SM_EVENT ( ev_connect , st_reconnect_fs ) ;
SM_END_EVENT_TABLE
break ;
case st_reconnect_fs :
// Server Hop part 3.1: connect to the new shard (called from globalMenu())
// Far TP part 3.1: connect to the new shard (called from farTPmainLoop())
FarTP . connectToNewShard ( ) ;
SM_BEGIN_EVENT_TABLE
SM_EVENT ( ev_self_reconnected , st_ingame ) ;
SM_EVENT ( ev_chars_received , st_reconnect_select_char ) ;
SM_EVENT ( ev_no_user_char , st_reconnect_error ) ;
SM_EVENT ( ev_conn_failed , st_reconnect_error ) ;
SM_EVENT ( ev_conn_dropped , st_reconnect_error ) ;
SM_END_EVENT_TABLE
break ;
case st_reconnect_select_char :
// Server Hop part 3.2: bypass character selection ui & select the same character.
// Far TP part 3.2: bypass character selection ui & select the same character.
// This is called from farTPmainloop(), when CONNECTION:USER_CHARS is received.
if ( ! FarTP . isReselectingChar ( ) )
{
FarTP . selectCharAndEnter ( ) ;
}
// Far TP part 3.2bis: see in farTPmainLoop()
if ( ! FarTP . isIngame ( ) )
{
SM_BEGIN_EVENT_TABLE
SM_EVENT ( ev_ready_received , st_exit_global_menu ) ;
SM_EVENT ( ev_conn_dropped , st_reconnect_error ) ;
SM_END_EVENT_TABLE
}
else
{
SM_BEGIN_EVENT_TABLE
SM_EVENT ( ev_ready_received , st_reconnect_ready ) ;
SM_EVENT ( ev_conn_dropped , st_reconnect_error ) ;
if ( FarTP . isReselectingChar ( ) & & ( ev = = ev_connect ) )
{
// Inside this Character Reselect we embed a new Server Hop
FarTP . beginEmbeddedServerHop ( ) ;
_CurrentState = st_leave_shard ;
break ;
}
SM_END_EVENT_TABLE
}
break ;
case st_exit_global_menu :
// Server Hop part 3.3
// Stay in Server Hop state until the global menu has been exited
SM_BEGIN_EVENT_TABLE
SM_EVENT ( ev_global_menu_exited , FarTP . isServerHopEmbedded ( ) ? st_reconnect_ready : st_ingame ) ;
SM_END_EVENT_TABLE
break ;
case st_reconnect_ready :
// Far TP part 3.3: send ready.
// This is called from farTPmainloop(), when CONNECTION:READY is received.
if ( FarTP . isServerHopEmbedded ( ) )
FarTP . endEmbeddedServerHop ( ) ;
SM_BEGIN_EVENT_TABLE
SM_EVENT ( ev_enter_game , st_ingame ) ;
SM_EVENT ( ev_conn_dropped , st_reconnect_error ) ;
SM_END_EVENT_TABLE
break ;
case st_reconnect_error :
// Far TP failed
FarTP . onFailure ( ) ;
SM_BEGIN_EVENT_TABLE
SM_EVENT ( ev_quit , st_end ) ;
SM_END_EVENT_TABLE
break ;
default :
nlwarning ( " Unhandeled state " ) ;
break ;
}
}
}
// ***************************************************************************
// Far TP
// ***************************************************************************
CFarTP FarTP ;
extern std : : string CurrentCookie ;
extern string ClientApp ;
namespace R2
{
extern bool ReloadUIFlag ;
}
/*
* requestFarTPToSession
*/
bool CFarTP : : requestFarTPToSession ( TSessionId sessionId , uint8 charSlot , CFarTP : : TJoinMode joinMode , bool bailOutIfSessionVanished )
{
if ( _HookedForEditor )
{
// Redirect Far TP to editor instead of following instructions
_HookedForEditor = false ;
return FarTP . requestFarTPToSession ( ( TSessionId ) 0 , charSlot , CFarTP : : LaunchEditor , true ) ; // allow bailing out in case of edition session not reachable
}
// call the join session using the session browser interface
uint32 charId = ( NetMngr . getLoginCookie ( ) . getUserId ( ) < < 4 ) + ( 0xf & charSlot ) ;
// Clean out for next Far TP
_SessionIdToJoinFast = 0 ;
CSessionBrowserImpl & sb = CSessionBrowserImpl : : getInstance ( ) ;
sb . init ( NULL ) ;
// sb.setAuthInfo(NetMngr.getLoginCookie());
// sb.connectItf(CInetAddress("borisb", 80));
sb . CurrentJoinMode = joinMode ;
// send the join session
// _JoinSessionResultReceived = false;
try
{
switch ( joinMode )
{
case LaunchEditor :
{
retryJoinEdit :
TSessionId sessionId ( 0 ) ;
sb . joinEditSession ( charId , ClientApp ) ;
bool ret = sb . waitOneMessage ( CSessionBrowserImpl : : getMessageName ( " on_joinSessionResult " ) ) ;
if ( ! ret )
throw " Protocol error " ;
if ( sb . _LastJoinSessionResult = = 2 )
{
// the edit session did not exist, create a new one
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 ) ;
ret = sb . waitOneMessage ( CSessionBrowserImpl : : getMessageName ( " on_scheduleSessionResult " ) ) ;
if ( ! ret | | sb . _LastScheduleSessionResult ! = 0 ) throw " schedule error " ;
sessionId = sb . _LastScheduleSessionId ;
// invite the char in the session
sb . inviteCharacter ( charId , sessionId , charId , TSessionPartStatus : : sps_edit_invited ) ;
ret = sb . waitOneMessage ( CSessionBrowserImpl : : getMessageName ( " on_invokeResult " ) ) ;
if ( ! ret | | sb . _LastInvokeResult ! = 0 ) throw " Invitation Error " ;
// now, start the edit session
sb . startSession ( charId , sessionId ) ;
ret = sb . waitOneMessage ( CSessionBrowserImpl : : getMessageName ( " on_invokeResult " ) ) ;
if ( ! ret | | sb . _LastInvokeResult ! = 0 ) throw " start session error " ;
// retry to join
goto retryJoinEdit ;
}
else if ( sb . _LastJoinSessionResult = = 15 )
{
sessionId = sb . _LastJoinSessionId ;
// the session was closed, the SU has put it in planned state, start it
sb . startSession ( charId , sessionId ) ;
ret = sb . waitOneMessage ( CSessionBrowserImpl : : getMessageName ( " on_invokeResult " ) ) ;
if ( ! ret | | sb . _LastInvokeResult ! = 0 ) throw " start session error (2), no DSS available " ;
// retry to join
goto retryJoinEdit ;
}
else if ( sb . _LastJoinSessionResult ! = 0 )
{
// any other error are not recoverable from here
throw " join session error " ;
}
// ok, the join is accepted !, the other far TP work is done in the callback
}
break ;
case JoinSession :
{
sb . joinSession ( charId , sessionId , ClientApp ) ;
bool ret = sb . waitOneMessage ( CSessionBrowserImpl : : getMessageName ( " on_joinSessionResult " ) ) ;
if ( ! ret )
throw " Protocol error " ;
if ( sb . _LastJoinSessionResult = = 16 )
{
# ifdef NL_OS_WINDOWS
# pragma message (NL_LOC_WRN "inform the player that he is banned from the ring")
# endif // NL_OS_WINDOWS
throw " User ban from the ring " ;
}
if ( sb . _LastJoinSessionResult ! = 0 )
{
throw " Join session error " ;
}
// ok, the join is accepted !, the other far TP work is done in the callback
}
break ;
case JoinMainland :
//retryJoinMainland:
{
TSessionId sessionId ( 0 ) ;
sb . joinMainland ( charId , ClientApp ) ;
bool ret = sb . waitOneMessage ( CSessionBrowserImpl : : getMessageName ( " on_joinSessionResult " ) ) ;
if ( ! ret )
throw " Protocol error " ;
if ( sb . _LastJoinSessionResult ! = 0 )
{
// some error during the join
throw " Error joining session " ;
}
// ok, the join is accepted !, the other far TP work is done in the callback
}
break ;
default :
nlstop ;
}
return true ;
}
//
// const string url = _URLBase + string(_URLTable[joinMode]);
// string res;
// try
// {
// // Get the address of a front-end service via http
// if (!HttpClient.connect(url))
// throw 1;
// switch (joinMode)
// {
// case CFarTP::LaunchEditor:
// if (!HttpClient.sendGetWithCookie(url, "ryzomId", CurrentCookie, toString("charSlot=%u", charSlot)))
// throw 2;
// break;
// case CFarTP::JoinSession:
// if (!HttpClient.sendPostWithCookie(url, "ryzomId", CurrentCookie, toString("sessionId=%u&charSlot=%u", sessionId, charSlot)))
// throw 2;
// break;
// case CFarTP::JoinMainland:
// if (!HttpClient.sendPostWithCookie(url, "ryzomId", CurrentCookie, "ml="))
// throw 2;
// break;
// }
// if (!HttpClient.receive(res))
// throw 3;
// HttpClient.disconnect();
// string::size_type luaPos = res.find("<lua>");
// if (luaPos == string::npos)
// throw 4;
// res = res.substr(luaPos + 5);
// res = res.substr(0, res.find("</lua>"));
// nlinfo("Found server for %ssession %u", joinMode==CFarTP::LaunchEditor ? "editing " : "", sessionId);
//
// // Begin Far TP
// CInterfaceManager *pIM = CInterfaceManager::getInstance();
// pIM->executeLuaScript(res, true);
// return true;
// }
catch ( char * errorMsg )
{
// // Get more details on the error
// string httpErrorStr;
// if (errorNum < 4) // we didn't receive an answer
// httpErrorStr = toString(" (HTTP error %u)", errorNum);
// if ( errorNum == 4 )
// {
// string::size_type posEndOfFirstLine = res.find( "\n" );
// if ( posEndOfFirstLine == string::npos )
// {
// // Invalid http answer
// httpErrorStr = toString(" (HTTP invalid)");
// }
// else
// {
// // Http status not OK (e.g. "404 Not Found")
// httpErrorStr = res.substr( 0, posEndOfFirstLine );
// if ( httpErrorStr.find( "200 OK" ) == string::npos )
// httpErrorStr = " (" + httpErrorStr + ")";
// else
// httpErrorStr.clear();
// }
// }
bool requestRetToMainland = /*httpErrorStr.empty() &&*/ bailOutIfSessionVanished ;
bool isAttemptingEmbeddedServerHop = isReselectingChar ( ) & & ( joinMode ! = CFarTP : : JoinSession ) ;
bool letReturnToCharSelect = ( ! isIngame ( ) ) | | isAttemptingEmbeddedServerHop ;
// User-friendly message
CInterfaceManager * pIM = CInterfaceManager : : getInstance ( ) ;
if ( letReturnToCharSelect )
{
// // Hide all buttons except Quit. If !requestRetToMainland, we will show them back at the end of connectToNewShard().
// pIM->runActionHandler( "proc", NULL, "charsel_disable_buttons" );
// pIM->runActionHandler( "set", NULL, "target_property=ui:outgame:charsel:quit_but:active|value=1" );
CInterfaceElement * btnOk = pIM - > getElementFromId ( " ui:outgame:charsel:message_box:ok " ) ;
if ( btnOk )
btnOk - > setActive ( ! requestRetToMainland ) ;
// Hide the black screen i.e. force showing the interface
CInterfaceElement * charSelBlackScreen = pIM - > getElementFromId ( " ui:outgame:charsel:black_screen " ) ;
if ( charSelBlackScreen )
{
CViewBase * charSelBlackScreenBitmap = dynamic_cast < CViewBase * > ( charSelBlackScreen ) ;
if ( charSelBlackScreenBitmap )
charSelBlackScreenBitmap - > setAlpha ( 0 ) ;
}
}
pIM - > messageBoxWithHelp (
CI18N : : get ( requestRetToMainland ? " uiSessionVanishedFarTP " : " uiSessionUnreachable " ) + ucstring ( errorMsg ) ,
letReturnToCharSelect ? " ui:outgame:charsel " : " ui:interface " ) ;
// Info in the log
string outErrorMsg = toString ( " Could not join %ssession %u: '%s' error \n " , joinMode = = CFarTP : : LaunchEditor ? " editing " : " " , sessionId . asInt ( ) , errorMsg ) ;
nlwarning ( outErrorMsg . c_str ( ) ) ;
// if ( httpErrorStr.empty() )
// {
// string delimiter = "Content-Type: text/html";
// string::size_type delimPos = res.find( delimiter );
// if ( delimPos != string::npos )
// res = res.substr( delimPos + delimiter.size() );
// }
// uint pos = 0;
// do
// {
// WarningLog->displayRaw( res.substr( pos, 200 ).c_str() ); // truncates long strings
// pos += 200;
// }
// while ( pos < res.size() );
WarningLog - > displayRawNL ( " " ) ;
// Save this error (regular log file is deleted at every startup)
// res = res + "\n\n";
try
{
COFile outputF ;
if ( outputF . open ( getLogDirectory ( ) + " error_join.log " , true , true ) )
{
time_t currentTime ;
time ( & currentTime ) ;
string headerS = NLMISC : : toString ( " \n \n %s%s \n \n " , asctime ( localtime ( & currentTime ) ) , outErrorMsg . c_str ( ) ) ;
outputF . serialBuffer ( ( uint8 * ) ( & headerS [ 0 ] ) , ( uint ) headerS . size ( ) ) ;
// outputF.serialBuffer( (uint8*)(&res[0]), res.size() );
outputF . close ( ) ;
}
}
catch ( Exception & )
{ }
// If the session is not a permanent session and has vanished, pop the position
if ( requestRetToMainland )
{
requestReturnToPreviousSession ( sessionId ) ;
LastGameCycle = NetMngr . getCurrentServerTick ( ) ;
NetMngr . send ( NetMngr . getCurrentServerTick ( ) ) ;
}
else if ( letReturnToCharSelect )
{
// Show all buttons except 'New character' so that the character can retry entering game or choose another character.
pIM - > runActionHandler ( " proc " , NULL , " charsel_enable_buttons " ) ;
pIM - > runActionHandler ( " set " , NULL , " target_property=ui:outgame:charsel:create_new_but:active|value=0 " ) ;
CInterfaceGroup * charselGroup = dynamic_cast < CInterfaceGroup * > ( pIM - > getElementFromId ( " ui:outgame:charsel " ) ) ;
if ( charselGroup )
pIM - > runActionHandler ( " proc " , charselGroup , " charsel_init_buttons " ) ;
}
return false ;
}
}
/*
* hookNextFarTPForEditor
*/
void CFarTP : : hookNextFarTPForEditor ( )
{
_HookedForEditor = true ;
}
/*
* requestReturnToPreviousSession
*/
void CFarTP : : requestReturnToPreviousSession ( TSessionId rejectedSessionId )
{
const string msgName = " CONNECTION:RET_MAINLAND " ;
CBitMemStream out ;
nlverify ( GenericMsgHeaderMngr . pushNameToStream ( msgName , out ) ) ;
out . serial ( PlayerSelectedSlot ) ;
out . serial ( rejectedSessionId ) ;
NetMngr . push ( out ) ;
nlinfo ( " %s sent " , msgName . c_str ( ) ) ;
}
/*
* requestReconnection
*/
void CFarTP : : requestReconnection ( )
{
_ReselectingChar = true ;
requestFarTPToSession ( TSessionId ( ~ 0u ) , ~ 0 , CFarTP : : JoinMainland , false ) ;
}
const char * CFarTP : : _URLTable [ CFarTP : : NbJoinModes ] =
{
" edit_session.php " ,
" join_session.php " ,
" join_shard.php "
} ;
/*
* States
*/
bool CFarTP : : isFarTPInProgress ( ) const
{
CLoginStateMachine : : TState state = LoginSM . getCurrentState ( ) ;
return ( state = = CLoginStateMachine : : st_leave_shard | |
state = = CLoginStateMachine : : st_enter_far_tp_main_loop | |
state = = CLoginStateMachine : : st_disconnect | |
state = = CLoginStateMachine : : st_reconnect_fs | |
state = = CLoginStateMachine : : st_reconnect_select_char | |
state = = CLoginStateMachine : : st_reconnect_ready | |
state = = CLoginStateMachine : : st_reconnect_error ) ;
}
bool CFarTP : : isServerHopInProgress ( ) const
{
CLoginStateMachine : : TState state = LoginSM . getCurrentState ( ) ;
return ( state = = CLoginStateMachine : : st_leave_shard | |
state = = CLoginStateMachine : : st_reconnect_fs | |
state = = CLoginStateMachine : : st_reconnect_select_char | |
state = = CLoginStateMachine : : st_exit_global_menu | |
state = = CLoginStateMachine : : st_reconnect_error ) ; // TODO: error handling
}
// from begin of Far TP to disconnection
bool CFarTP : : isLeavingShard ( ) const
{
CLoginStateMachine : : TState state = LoginSM . getCurrentState ( ) ;
return ( state = = CLoginStateMachine : : st_leave_shard | |
state = = CLoginStateMachine : : st_enter_far_tp_main_loop | |
state = = CLoginStateMachine : : st_disconnect ) ;
}
// from reconnection to game entering
bool CFarTP : : isJoiningShard ( ) const
{
CLoginStateMachine : : TState state = LoginSM . getCurrentState ( ) ;
return ( state = = CLoginStateMachine : : st_reconnect_fs | |
state = = CLoginStateMachine : : st_reconnect_select_char | |
state = = CLoginStateMachine : : st_reconnect_ready | |
state = = CLoginStateMachine : : st_reconnect_error ) ;
}
/*
* Events
*/
void CFarTP : : onServerQuitOk ( )
{
game_exit_request = false ;
ryzom_exit_request = false ;
if ( LoginSM . getCurrentState ( ) = = CLoginStateMachine : : st_leave_shard )
LoginSM . pushEvent ( CLoginStateMachine : : ev_ingame_return ) ;
}
void CFarTP : : onServerQuitAbort ( )
{
game_exit_request = false ;
ryzom_exit_request = false ;
if ( LoginSM . getCurrentState ( ) = = CLoginStateMachine : : st_leave_shard )
LoginSM . pushEvent ( CLoginStateMachine : : ev_enter_game ) ;
_ReselectingChar = false ;
}
void CFarTP : : disconnectFromPreviousShard ( )
{
CInterfaceManager * pIM = CInterfaceManager : : getInstance ( ) ;
if ( isIngame ( ) )
{
// Display background (TODO: not Kami)
beginLoading ( StartBackground ) ;
UseEscapeDuringLoading = false ;
// Play music and fade out the Game Sound
if ( SoundMngr )
{
SoundMngr - > playEventMusic ( " Loading Music Loop.ogg " , CSoundManager : : LoadingMusicXFade , true ) ;
SoundMngr - > fadeOutGameSound ( ClientCfg . SoundTPFade ) ;
}
// Change the tips
selectTipsOfTheDay ( rand ( ) ) ;
// Start progress bar and display background
ProgressBar . reset ( BAR_STEP_TP ) ;
ucstring nmsg ( " Loading... " ) ;
ProgressBar . newMessage ( ClientCfg . buildLoadingString ( nmsg ) ) ;
ProgressBar . progress ( 0 ) ;
}
// Disconnect from the FS
NetMngr . disconnect ( ) ;
if ( isIngame ( ) )
{
// Save the R2EDEnabled flag to know if we are switching it when we reconnect
_PreviousR2EdEnabled = ClientCfg . R2EDEnabled ;
// Release R2 editor if applicable
R2 : : getEditor ( ) . autoConfigRelease ( IsInRingSession ) ;
}
if ( isReselectingChar ( ) )
{
releaseMainLoopReselect ( ) ;
}
else
{
// String manager: remove all waiting callbacks and removers
// (if some interface stuff has not received its string yet, its remover will get useless)
STRING_MANAGER : : CStringManagerClient : : release ( false ) ;
// Ugly globals
userChar = false ;
noUserChar = false ;
serverReceivedReady = false ;
CharNameValidArrived = false ;
UserCharPosReceived = false ;
SabrinaPhraseBookLoaded = false ;
}
// Restart the network manager, the interface sync counter and the entities
pIM - > resetShardSpecificData ( ) ;
/*
ServerToLocal autocopy stuff :
When reinit will reset server counters to 0 in the server database , onServerChange ( )
will be called and data will be copied since CInterfaceManager : : _LocalSyncActionCounter = = 0
( done in resetShardSpecificData ( ) above )
If we do the resetShardSpecificData ( ) after , scenari could arise where Local database could not be reseted
*/
NetMngr . reinit ( ) ;
if ( ! isReselectingChar ( ) )
{
nlinfo ( " FarTP: calling EntitiesMngr.reinit() " ) ;
EntitiesMngr . reinit ( ) ;
}
LoginSM . pushEvent ( CLoginStateMachine : : ev_connect ) ;
}
void CFarTP : : connectToNewShard ( )
{
// TODO: start commands?
// Connect to the next FS
NetMngr . initCookie ( Cookie , FSAddr ) ;
// connect the session browser to the new shard
NLNET : : CInetAddress sbsAddress ( CSessionBrowserImpl : : getInstance ( ) . getFrontEndAddress ( ) ) ;
sbsAddress . setPort ( sbsAddress . port ( ) + SBSPortOffset ) ;
CSessionBrowserImpl : : getInstance ( ) . connectItf ( sbsAddress ) ;
string result ;
NetMngr . connect ( result ) ;
if ( ! result . empty ( ) )
{
_Reason = new string ( result ) ;
LoginSM . pushEvent ( CLoginStateMachine : : ev_conn_failed ) ;
return ;
}
// Reinit the string manager cache.
STRING_MANAGER : : CStringManagerClient : : instance ( ) - > initCache ( FSAddr , ClientCfg . LanguageCode ) ;
// reset the chat mode
ChatMngr . resetChatMode ( ) ;
// The next step will be triggered by the CONNECTION:USER_CHARS msg from the server
}
// return to character selection screen
bool CFarTP : : reselectCharacter ( )
{
if ( ! reconnection ( ) )
{
// The user clicked the Quit button
releaseOutGame ( ) ;
return false ;
}
return true ;
}
void CFarTP : : selectCharAndEnter ( )
{
CBitMemStream out ;
nlverify ( GenericMsgHeaderMngr . pushNameToStream ( " CONNECTION:SELECT_CHAR " , out ) ) ;
CSelectCharMsg SelectCharMsg ;
SelectCharMsg . c = ( uint8 ) PlayerSelectedSlot ; // PlayerSelectedSlot has not been reset
out . serial ( SelectCharMsg ) ;
NetMngr . push ( out ) ;
/*//Obsolete
CBitMemStream out2 ;
nlverify ( GenericMsgHeaderMngr . pushNameToStream ( " CONNECTION:ENTER " , out2 ) ) ;
NetMngr . push ( out2 ) ;
*/
LastGameCycle = NetMngr . getCurrentServerTick ( ) ;
NetMngr . send ( NetMngr . getCurrentServerTick ( ) ) ;
// if (!isIngame())
// globalMenu will exit when WaitServerAnswer and serverReceivedReady (triggered by the CONNECTION:READY msg) are true
// else
// Next step will be triggered by the CONNECTION:READY msg from the server
}
void CFarTP : : sendReady ( )
{
if ( isReselectingChar ( ) )
{
initMainLoop ( ) ;
}
else
{
// Set season
RT . updateRyzomClock ( NetMngr . getCurrentServerTick ( ) , ryzomGetLocalTime ( ) * 0.001 ) ;
DayNightCycleHour = ( float ) RT . getRyzomTime ( ) ;
CurrSeason = RT . getRyzomSeason ( ) ;
RT . updateRyzomClock ( NetMngr . getCurrentServerTick ( ) , ryzomGetLocalTime ( ) * 0.001 ) ;
DayNightCycleHour = ( float ) RT . getRyzomTime ( ) ;
ManualSeasonValue = RT . getRyzomSeason ( ) ;
// Reset all fx (no need to CTimedFXManager::getInstance().reset() and EntitiesMngr.getGroundFXManager().reset() because the entities have been removed)
CProjectileManager : : getInstance ( ) . reset ( ) ;
FXMngr . reset ( ) ; // (must be done after EntitiesMngr.release())
EntitiesMngr . getGroundFXManager ( ) . init ( Scene , ClientCfg . GroundFXMaxDist , ClientCfg . GroundFXMaxNB , ClientCfg . GroundFXCacheSize ) ;
// Get the sheet for the user from the CFG.
// Initialize the user and add him into the entity manager.
// DO IT AFTER: Database, Collision Manager, PACS, scene, animations loaded.
CSheetId userSheet ( ClientCfg . UserSheet ) ;
nlinfo ( " FarTP: calling EntitiesMngr.create(0, userSheet.asInt()) " ) ;
TNewEntityInfo emptyEntityInfo ;
emptyEntityInfo . reset ( ) ;
EntitiesMngr . create ( 0 , userSheet . asInt ( ) , emptyEntityInfo ) ;
// Wait for the start position (USER_CHAR) and set the continent
waitForUserCharReceived ( ) ;
CInterfaceManager * pIM = CInterfaceManager : : getInstance ( ) ;
if ( ClientCfg . R2EDEnabled ! = _PreviousR2EdEnabled )
{
// Reload textures, keys and interface config if we are switch between playing and r2 mode
// Must be done after receiving the current R2EDEnabled flag (USER_CHAR)
pIM - > loadIngameInterfaceTextures ( ) ;
// Unload (and save if leaving normal playing mode) keys and interface config
// Instead of doing it in disconnectFromPreviousShard(), we do it here, only when it's needed
ClientCfg . R2EDEnabled = ! ClientCfg . R2EDEnabled ;
pIM - > uninitInGame0 ( ) ;
ClientCfg . R2EDEnabled = ! ClientCfg . R2EDEnabled ;
ActionsContext . removeAllCombos ( ) ;
if ( ! ClientCfg . R2EDEnabled )
{
// Remove all existing keys and load them back, and load new interface config
pIM - > loadKeys ( ) ;
pIM - > hideAllWindows ( ) ;
pIM - > loadInterfaceConfig ( ) ;
}
else
{
R2 : : ReloadUIFlag = true ; // in R2ED mode the CEditor class deals with it
}
}
pIM - > configureQuitDialogBox ( ) ; // must be called after waitForUserCharReceived() to know the ring config
ContinentMngr . select ( UserEntity - > pos ( ) , ProgressBar ) ; // IMPORTANT : must select continent after ui init, because ui init also load landmarks (located in the icfg file)
// landmarks would be invisible else (RT 12239)
// Update Network until current tick increase.
LastGameCycle = NetMngr . getCurrentServerTick ( ) ;
while ( LastGameCycle = = NetMngr . getCurrentServerTick ( ) )
{
// Event server get events
CInputHandlerManager : : getInstance ( ) - > pumpEventsNoIM ( ) ;
// Update Network.
NetMngr . update ( ) ;
CCDBNodeBranch : : flushObserversCalls ( ) ;
// Be nice to the system
nlSleep ( 100 ) ;
}
LastGameCycle = NetMngr . getCurrentServerTick ( ) ;
ProgressBar . progress ( 1 ) ;
// Create the message for the server to create the character.
CBitMemStream out ;
if ( GenericMsgHeaderMngr . pushNameToStream ( " CONNECTION:READY " , out ) )
{
out . serial ( ClientCfg . LanguageCode ) ;
NetMngr . push ( out ) ;
NetMngr . send ( NetMngr . getCurrentServerTick ( ) ) ;
}
// To be sure server crash is not fault of client
ConnectionReadySent = true ; // must be called before BotChatPageAll->initAfterConnectionReady()
// To reset the inputs.
CInputHandlerManager : : getInstance ( ) - > pumpEventsNoIM ( ) ;
if ( BotChatPageAll & & ( ! ClientCfg . R2EDEnabled ) )
BotChatPageAll - > initAfterConnectionReady ( ) ;
// Transition from background to game
FirstFrame = true ;
}
ProgressBar . finish ( ) ;
LoginSM . pushEvent ( CLoginStateMachine : : ev_enter_game ) ;
if ( _DSSDown )
{
_DSSDown = false ;
onDssDown ( true ) ;
}
}
void CFarTP : : onFailure ( )
{
// Display message
string reason ;
if ( _Reason )
{
reason + = " : " + ( * _Reason ) ;
delete _Reason ;
_Reason = NULL ;
}
else if ( noUserChar )
{
reason + = " : no characters found! " ;
}
Driver - > systemMessageBox ( ( " Unable to join shard " + reason ) . c_str ( ) , " Error " , UDriver : : okType , UDriver : : exclamationIcon ) ;
// TODO: recover from error
LoginSM . pushEvent ( CLoginStateMachine : : ev_quit ) ;
}
void CFarTP : : onDssDown ( bool forceReturn )
{
if ( ! forceReturn )
{
// If leaving shard, don't bother with DSS "downitude"
if ( isLeavingShard ( ) )
return ;
// If joining shard, store event and launch it at the end of reconnection
if ( isJoiningShard ( ) )
{
_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)
return ;
}
}
CInterfaceManager * pIM = CInterfaceManager : : getInstance ( ) ;
pIM - > messageBoxWithHelp ( CI18N : : get ( " uiDisconnected " ) ) ;
requestReturnToPreviousSession ( ) ;
}
extern bool loginFinished ;
extern bool loginOK ;
void CFarTP : : joinSessionResult ( uint32 /* userId */ , TSessionId /* sessionId */ , uint32 /* result */ , const std : : string & /* shardAddr */ , const std : : string & /* participantStatus */ )
{
// _LastJoinSessionResultMsg = result;
//
// _JoinSessionResultReceived = true;
//
// if (result == 0)
// {
// // ok, the join is successful
//
// FSAddr = shardAddr;
//
// loginFinished = true;
// loginOK = true;
//
// LoginSM.pushEvent(CLoginStateMachine::ev_connect);
//
// }
// else
// {
// // TODO : display the error message on client screen and log
// nlstop;
// }
}
void CFarTP : : setJoinSessionResult ( TSessionId sessionId , const CSecurityCode & securityCode )
{
_SessionIdToJoinFast = sessionId ;
_SecurityCodeForDisconnection = securityCode ;
}
void CFarTP : : writeSecurityCodeForDisconnection ( NLMISC : : IStream & msgout )
{
CSecurityCheckForFastDisconnection : : forwardSecurityCode ( msgout , _SessionIdToJoinFast , _SecurityCodeForDisconnection ) ;
}
// Not run by the cotask but within the main loop
void CFarTP : : farTPmainLoop ( )
{
ConnectionReadySent = false ;
LoginSM . pushEvent ( CLoginStateMachine : : ev_far_tp_main_loop_entered ) ;
uint nbRecoSelectCharReceived = 0 ;
bool welcomeWindow = true ;
// Update network until the end of the FarTP process, before resuming the main loop
while ( ! ConnectionReadySent )
{
// Event server get events
CInputHandlerManager : : getInstance ( ) - > pumpEventsNoIM ( ) ;
// Update Network.
NetMngr . update ( ) ;
CCDBNodeBranch : : flushObserversCalls ( ) ;
// TODO: resend in case the last datagram sent was lost?
// // check if we can send another dated block
// if (NetMngr.getCurrentServerTick() != serverTick)
// {
// //
// serverTick = NetMngr.getCurrentServerTick();
// NetMngr.send(serverTick);
// }
// else
// {
// // Send dummy info
// NetMngr.send();
// }
if ( LoginSM . getCurrentState ( ) = = CLoginStateMachine : : st_reconnect_select_char )
{
// Far TP part 3.2bis: go to character selection dialog
// This is done outside the co-routine because it may need to co-routine to do an embedded server hop
if ( FarTP . isReselectingChar ( ) )
{
+ + nbRecoSelectCharReceived ;
if ( nbRecoSelectCharReceived < = 1 )
{
ClientCfg . SelectCharacter = - 1 ; // turn off character autoselection
if ( ! FarTP . reselectCharacter ( ) ) // it should not return here in farTPmainLoop() in the same state otherwise this would be called twice
return ;
}
else
nlwarning ( " Received more than one st_reconnect_select_char event " ) ;
}
}
else if ( LoginSM . getCurrentState ( ) = = CLoginStateMachine : : st_reconnect_ready )
{
// Don't call sendReady() within the cotask but within the main loop, as it contains
// event/network loops that could trigger a global exit().
sendReady ( ) ;
welcomeWindow = ! isReselectingChar ( ) ;
}
// Be nice to the system
nlSleep ( 100 ) ;
}
// active/desactive welcome window
if ( welcomeWindow )
initWelcomeWindow ( ) ;
}