2013-10-23 17:08:09 +00:00
// NeLNS - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// 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/>.
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif // HAVE_CONFIG_H
# ifndef NELNS_CONFIG
# define NELNS_CONFIG ""
# endif // NELNS_CONFIG
# ifndef NELNS_LOGS
# define NELNS_LOGS ""
# endif // NELNS_LOGS
# include "nel/misc/types_nl.h"
# include <stdio.h>
# include <ctype.h>
# include <math.h>
# include <list>
# include "nel/misc/debug.h"
# include "nel/misc/config_file.h"
# include "nel/misc/displayer.h"
# include "nel/misc/command.h"
# include "nel/misc/variable.h"
# include "nel/misc/log.h"
# include "nel/misc/file.h"
# include "nel/misc/path.h"
# include "nel/net/service.h"
# include "nel/net/unified_network.h"
# include "nel/net/login_cookie.h"
# include "welcome_service.h"
using namespace std ;
using namespace NLMISC ;
using namespace NLNET ;
using namespace WS ;
CVariable < sint > PlayerLimit (
" ws " , " PlayerLimit " , " Rough max number of players accepted on this shard (-1 for Unlimited) " ,
5000 ,
0 , true ) ;
// Forward declaration of callback cbShardOpen (see ShardOpen variable)
void cbShardOpen ( IVariable & var ) ;
// Forward declaration of callback cbShardOpenStateFile (see ShardOpenStateFile variable)
void cbShardOpenStateFile ( IVariable & var ) ;
// Forward declaration of callback cbUsePatchMode
void cbUsePatchMode ( IVariable & var ) ;
// Types of open state
enum TShardOpenState
{
ClosedForAll = 0 ,
OpenOnlyForAllowed = 1 ,
OpenForAll = 2
} ;
static bool AllowDispatchMsgToLS = false ;
/**
* ShardOpen
* true if shard is open to public
* 0 means closed for all but : DEV :
* 1 means open only for groups in config file ( see OpenGroups variable ) and : DEV :
* 2 means open for all
*/
CVariable < uint > ShardOpen ( " ws " , " ShardOpen " , " Indicates if shard is open to public (0 closed for all but :DEV:, 1 open only for groups in cfg, 2 open for all) " , 2 , 0 , true , cbShardOpen ) ;
/**
* ShardOpenStateFile
* true if shard is open to public
*/
CVariable < string > ShardOpenStateFile ( " ws " , " ShardOpenStateFile " , " Name of the file that contains ShardOpen state " , " " , 0 , true , cbShardOpenStateFile ) ;
/**
* OpenGroups
*/
CVariable < string > OpenGroups ( " ws " , " OpenGroups " , " list of groups allowed at ShardOpen Level 1 " , " " , 0 , true ) ;
/**
* OpenFrontEndThreshold
* The FS balance algorithm works like this :
* - select the least loaded frontend
* - if this frontend has more than the OpenFrontEndThreshold
* - try to open a new frontend
* - reselect least loaded frontend
*/
CVariable < uint > OpenFrontEndThreshold ( " ws " , " OpenFrontEndThreshold " , " Limit number of players on all FS to decide to open a new FS " , 800 , 0 , true ) ;
/**
* Use Patch mode
*/
CVariable < bool > UsePatchMode ( " ws " , " UsePatchMode " , " Use Frontends as Patch servers (at FS startup) " , true , 0 , true , cbUsePatchMode ) ;
/**
* Use Patch mode
*/
CVariable < bool > DontUseLS ( " ws " , " DontUseLS " , " Don't use the login service " , false , 0 , true ) ;
// Shortcut to the module instance
//CWelcomeServiceMod *CWelcomeServiceMod::_Instance = NULL;
/**
* Using expected services and current running service instances , this class
* reports a main " online status " .
*/
class COnlineServices
{
public :
/// Set expected instances. Ex: { "TICKS", "FS", "FS", "FS" }
void setExpectedInstances ( CConfigFile : : CVar & var )
{
// Reset "expected" counters (but don't clear the map, keep the running instances)
CInstances : : iterator ici ;
for ( ici = _Instances . begin ( ) ; ici ! = _Instances . end ( ) ; + + ici )
{
( * ici ) . second . Expected = 0 ;
}
// Rebuild "expected" counters
for ( uint i = 0 ; i ! = var . size ( ) ; + + i )
{
+ + _Instances [ var . asString ( i ) ] . Expected ;
}
}
/// Add a service instance
void addInstance ( const std : : string & serviceName )
{
+ + _Instances [ serviceName ] . Running ;
}
/// Remove a service instance
void removeInstance ( const std : : string & serviceName )
{
CInstances : : iterator ici = _Instances . find ( serviceName ) ;
if ( ici ! = _Instances . end ( ) )
{
- - ( * ici ) . second . Running ;
// Remove from the map only if not part of the expected list
if ( ( ( * ici ) . second . Expected = = 0 ) & & ( ( * ici ) . second . Running = = 0 ) )
{
_Instances . erase ( ici ) ;
}
}
else
{
nlwarning ( " Can't remove instance of %s " , serviceName . c_str ( ) ) ;
}
}
/// Check if all expected instances are online
bool getOnlineStatus ( ) const
{
CInstances : : const_iterator ici ;
for ( ici = _Instances . begin ( ) ; ici ! = _Instances . end ( ) ; + + ici )
{
if ( ! ici - > second . isOnlineAsExpected ( ) )
return false ;
}
return true ;
}
/// Display contents
void display ( NLMISC : : CLog & log = * NLMISC : : DebugLog )
{
CInstances : : const_iterator ici ;
for ( ici = _Instances . begin ( ) ; ici ! = _Instances . end ( ) ; + + ici )
{
log . displayNL ( " %s: %s (%u expected, %u running) " ,
( * ici ) . first . c_str ( ) ,
( * ici ) . second . Expected ? ( ( * ici ) . second . isOnlineAsExpected ( ) ? " ONLINE " : " MISSING " ) : " OPTIONAL " ,
( * ici ) . second . Expected , ( * ici ) . second . Running ) ;
}
}
private :
struct TInstanceCounters
{
TInstanceCounters ( ) : Expected ( 0 ) , Running ( 0 ) { }
// If not expected, count as online as well
bool isOnlineAsExpected ( ) const { return Running > = Expected ; }
uint Expected ;
uint Running ;
} ;
typedef std : : map < std : : string , TInstanceCounters > CInstances ;
CInstances _Instances ;
} ;
/// Online services
COnlineServices OnlineServices ;
/// Main online status
bool OnlineStatus ;
/// Send changes of status to the LS
void reportOnlineStatus ( bool newStatus )
{
if ( newStatus ! = OnlineStatus & & AllowDispatchMsgToLS )
{
if ( ! DontUseLS )
{
CMessage msgout ( " OL_ST " ) ;
msgout . serial ( newStatus ) ;
CUnifiedNetwork : : getInstance ( ) - > send ( " LS " , msgout ) ;
}
if ( CWelcomeServiceMod : : isInitialized ( ) )
{
// send a status report to welcome service client
CWelcomeServiceMod : : getInstance ( ) - > reportWSOpenState ( newStatus ) ;
}
OnlineStatus = newStatus ;
}
}
/// Set the version of the shard. you have to increase it each time the client-server protocol changes.
/// You have to increment the client too (the server and client version must be the same to run correctly)
static const uint32 ServerVersion = 1 ;
/// Contains the correspondance between userid and the FES connection where the userid is connected.
map < uint32 , TServiceId > UserIdSockAssociations ;
// ubi hack
string FrontEndAddress ;
enum TFESState
{
PatchOnly ,
AcceptClientOnly
} ;
struct CFES
{
CFES ( TServiceId sid ) : SId ( sid ) , NbPendingUsers ( 0 ) , NbUser ( 0 ) , State ( PatchOnly ) { }
TServiceId SId ; // Connection to the front end
uint32 NbPendingUsers ; // Number of not yet connected users (but rooted to this frontend)
uint32 NbUser ; // Number of user currently connected on this front end
TFESState State ; // State of frontend (patching/accepting clients)
std : : string PatchAddress ; // Address of frontend patching server
uint32 getUsersCountHeuristic ( ) const
{
return NbUser + NbPendingUsers ;
}
void setToAcceptClients ( )
{
if ( State = = AcceptClientOnly )
return ;
// tell FS to accept client
State = AcceptClientOnly ;
CMessage msgOpenFES ( " FS_ACCEPT " ) ;
CUnifiedNetwork : : getInstance ( ) - > send ( SId , msgOpenFES ) ;
// report state to LS
bool dummy ;
reportStateToLS ( dummy , true ) ;
}
void reportStateToLS ( bool & reportPatching , bool alive = true )
{
// report to LS
bool patching = ( State = = PatchOnly ) ;
if ( alive & & patching )
reportPatching = true ;
if ( AllowDispatchMsgToLS )
{
if ( ! DontUseLS )
{
CMessage msgout ( " REPORT_FS_STATE " ) ;
msgout . serial ( SId ) ;
msgout . serial ( alive ) ;
msgout . serial ( patching ) ;
msgout . serial ( PatchAddress ) ;
CUnifiedNetwork : : getInstance ( ) - > send ( " LS " , msgout ) ;
}
}
}
} ;
list < CFES > FESList ;
/*
* Find the best front end service for a new connecting user ( return NULL if there is no suitable FES ) .
* Additionally , calculate totalNbUsers .
*/
CFES * findBestFES ( uint & totalNbUsers )
{
totalNbUsers = 0 ;
CFES * best = NULL ;
for ( list < CFES > : : iterator it = FESList . begin ( ) ; it ! = FESList . end ( ) ; + + it )
{
CFES & fes = * it ;
if ( fes . State = = AcceptClientOnly )
{
if ( best = = NULL | | best - > getUsersCountHeuristic ( ) > fes . getUsersCountHeuristic ( ) )
best = & fes ;
totalNbUsers + = fes . NbUser ;
}
}
return best ;
}
/**
* Select a frontend in patch mode to open
* Returns true if a new FES was open , false if no FES could be open
*/
bool openNewFES ( )
{
for ( list < CFES > : : iterator it = FESList . begin ( ) ; it ! = FESList . end ( ) ; + + it )
{
if ( ( * it ) . State = = PatchOnly )
{
nlinfo ( " openNewFES: ask the FS %d to accept clients " , it - > SId . get ( ) ) ;
// switch FES to AcceptClientOnly
( * it ) . setToAcceptClients ( ) ;
return true ;
}
}
return false ;
}
void displayFES ( )
{
nlinfo ( " There's %d FES in the list: " , FESList . size ( ) ) ;
for ( list < CFES > : : iterator it = FESList . begin ( ) ; it ! = FESList . end ( ) ; it + + )
{
nlinfo ( " > %u NbUser:%d NbPendingUser:%d " , it - > SId . get ( ) , it - > NbUser , it - > NbPendingUsers ) ;
}
nlinfo ( " End of the list " ) ;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////// CONNECTION TO THE FRONT END SERVICE ///////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void cbFESShardChooseShard ( CMessage & msgin , const std : : string & serviceName , TServiceId sid )
{
// the WS answer a user authorize
string reason ;
CLoginCookie cookie ;
string addr ;
//
// S09: receive "SCS" message from FES and send the "SCS" message to the LS
//
CMessage msgout ( " SCS " ) ;
msgin . serial ( reason ) ;
msgout . serial ( reason ) ;
msgin . serial ( cookie ) ;
msgout . serial ( cookie ) ;
if ( reason . empty ( ) )
{
msgin . serial ( addr ) ;
// if we set the FontEndAddress in the welcome_service.cfg we use this address
if ( FrontEndAddress . empty ( ) )
{
msgout . serial ( addr ) ;
}
else
{
msgout . serial ( FrontEndAddress ) ;
}
uint32 nbPendingUser ;
msgin . serial ( nbPendingUser ) ;
// update the pending user count for this shard
for ( list < CFES > : : iterator it = FESList . begin ( ) ; it ! = FESList . end ( ) ; it + + )
{
if ( it - > SId = = sid )
{
it - > NbPendingUsers = nbPendingUser ;
break ;
}
}
/*
// OBSOLETE: LS doesn't read patching URLs
// build patch server list
std : : string PatchURLS ;
for ( list < CFES > : : iterator it = FESList . begin ( ) ; it ! = FESList . end ( ) ; + + it )
{
if ( ( * it ) . State = = PatchOnly & & ! ( * it ) . PatchAddress . empty ( ) )
{
if ( ! PatchURLS . empty ( ) )
PatchURLS + = ' | ' ;
PatchURLS + = ( * it ) . PatchAddress ;
}
}
msgout . serial ( PatchURLS ) ;
*/
}
if ( PendingFeResponse . find ( cookie ) ! = PendingFeResponse . end ( ) )
{
nldebug ( " ERLOG: SCS recvd from %s-%hu => sending %s to SU " , serviceName . c_str ( ) , sid . get ( ) , cookie . toString ( ) . c_str ( ) ) ;
// this response is not waited by LS
TPendingFEResponseInfo & pfri = PendingFeResponse . find ( cookie ) - > second ;
pfri . WSMod - > frontendResponse ( pfri . WaiterModule , pfri . UserId , reason , cookie , addr ) ;
// cleanup pending record
PendingFeResponse . erase ( cookie ) ;
}
else
{
nldebug ( " ERLOG: SCS recvd from %s-%hu, but pending %s not found " , serviceName . c_str ( ) , sid . get ( ) , cookie . toString ( ) . c_str ( ) ) ;
// return the result to the LS
if ( ! DontUseLS )
{
CUnifiedNetwork : : getInstance ( ) - > send ( " LS " , msgout ) ;
}
}
}
// This function is call when a FES accepted a new client or lost a connection to a client
void cbFESClientConnected ( CMessage & msgin , const std : : string & serviceName , TServiceId sid )
{
//
// S15: receive "CC" message from FES and send "CC" message to the "LS"
//
CMessage msgout ( " CC " ) ;
uint32 userid ;
msgin . serial ( userid ) ;
msgout . serial ( userid ) ;
uint8 con ;
msgin . serial ( con ) ;
msgout . serial ( con ) ;
if ( ! DontUseLS )
{
CUnifiedNetwork : : getInstance ( ) - > send ( " LS " , msgout ) ;
}
// add or remove the user number really connected on this shard
uint32 totalNbOnlineUsers = 0 , totalNbPendingUsers = 0 ;
for ( list < CFES > : : iterator it = FESList . begin ( ) ; it ! = FESList . end ( ) ; it + + )
{
if ( it - > SId = = sid )
{
if ( con )
{
( * it ) . NbUser + + ;
// the client connected, it's no longer pending
if ( ( * it ) . NbPendingUsers > 0 )
( * it ) . NbPendingUsers - - ;
}
else
{
if ( ( * it ) . NbUser ! = 0 )
( * it ) . NbUser - - ;
}
}
totalNbOnlineUsers + = ( * it ) . NbUser ;
totalNbPendingUsers + = ( * it ) . NbPendingUsers ;
}
if ( CWelcomeServiceMod : : isInitialized ( ) )
CWelcomeServiceMod : : getInstance ( ) - > updateConnectedPlayerCount ( totalNbOnlineUsers , totalNbPendingUsers ) ;
if ( con )
{
// we know that this user is on this FES
UserIdSockAssociations . insert ( make_pair ( userid , sid ) ) ;
}
else
{
// remove the user
UserIdSockAssociations . erase ( userid ) ;
}
}
// This function is called when a FES rejected a client' cookie
void cbFESRemovedPendingCookie ( CMessage & msgin , const std : : string & serviceName , TServiceId sid )
{
CLoginCookie cookie ;
msgin . serial ( cookie ) ;
nldebug ( " ERLOG: RPC recvd from %s-%hu => %s removed " , serviceName . c_str ( ) , sid . get ( ) , cookie . toString ( ) . c_str ( ) , cookie . toString ( ) . c_str ( ) ) ;
// client' cookie rejected, no longer pending
uint32 totalNbOnlineUsers = 0 , totalNbPendingUsers = 0 ;
for ( list < CFES > : : iterator it = FESList . begin ( ) ; it ! = FESList . end ( ) ; it + + )
{
if ( ( * it ) . SId = = sid )
{
if ( ( * it ) . NbPendingUsers > 0 )
- - ( * it ) . NbPendingUsers ;
}
totalNbOnlineUsers + = ( * it ) . NbUser ;
totalNbPendingUsers + = ( * it ) . NbPendingUsers ;
}
if ( CWelcomeServiceMod : : isInitialized ( ) )
{
CWelcomeServiceMod : : getInstance ( ) - > pendingUserLost ( cookie ) ;
CWelcomeServiceMod : : getInstance ( ) - > updateConnectedPlayerCount ( totalNbOnlineUsers , totalNbPendingUsers ) ;
}
}
// This function is called by FES to setup its PatchAddress
void cbFESPatchAddress ( CMessage & msgin , const std : : string & serviceName , TServiceId sid )
{
std : : string address ;
msgin . serial ( address ) ;
bool acceptClients ;
msgin . serial ( acceptClients ) ;
nldebug ( " Received patch server address '%s' from service %s %d " , address . c_str ( ) , serviceName . c_str ( ) , sid . get ( ) ) ;
for ( list < CFES > : : iterator it = FESList . begin ( ) ; it ! = FESList . end ( ) ; it + + )
{
if ( ( * it ) . SId = = sid )
{
nldebug ( " Affected patch server address '%s' to frontend %s %d " , address . c_str ( ) , serviceName . c_str ( ) , sid . get ( ) ) ;
if ( ! UsePatchMode . get ( ) & & ! acceptClients )
{
// not in patch mode, force fs to accept clients
acceptClients = true ;
( * it ) . setToAcceptClients ( ) ;
}
( * it ) . PatchAddress = address ;
( * it ) . State = ( acceptClients ? AcceptClientOnly : PatchOnly ) ;
if ( acceptClients )
nldebug ( " Frontend %s %d reported to accept client, patching unavailable for that server " , address . c_str ( ) , serviceName . c_str ( ) , sid . get ( ) ) ;
else
nldebug ( " Frontend %s %d reported to be in patching mode " , address . c_str ( ) , serviceName . c_str ( ) , sid . get ( ) ) ;
bool dummy ;
( * it ) . reportStateToLS ( dummy ) ;
break ;
}
}
}
// This function is called by FES to setup the right number of players (if FES was already present before WS launching)
void cbFESNbPlayers ( CMessage & msgin , const std : : string & serviceName , TServiceId sid )
{
// *********** WARNING *******************
// This version of the callback is deprecated, the system
// now use cbFESNbPlayers2 that report the pending user count
// as well as the number of connected players.
// It is kept for backward compatibility only.
// ***************************************
uint32 nbPlayers ;
msgin . serial ( nbPlayers ) ;
uint32 totalNbOnlineUsers = 0 , totalNbPendingUsers = 0 ;
for ( list < CFES > : : iterator it = FESList . begin ( ) ; it ! = FESList . end ( ) ; it + + )
{
if ( ( * it ) . SId = = sid )
{
nldebug ( " Frontend '%d' reported %d online users " , sid . get ( ) , nbPlayers ) ;
( * it ) . NbUser = nbPlayers ;
if ( nbPlayers ! = 0 & & ( * it ) . State = = PatchOnly )
{
nlwarning ( " Frontend %d is in state PatchOnly, yet reports to have online %d players, state AcceptClientOnly is forced (FS_ACCEPT message sent) " ) ;
( * it ) . setToAcceptClients ( ) ;
}
}
totalNbOnlineUsers + = ( * it ) . NbUser ;
totalNbPendingUsers + = ( * it ) . NbPendingUsers ;
}
if ( CWelcomeServiceMod : : isInitialized ( ) )
CWelcomeServiceMod : : getInstance ( ) - > updateConnectedPlayerCount ( totalNbOnlineUsers , totalNbPendingUsers ) ;
}
// This function is called by FES to setup the right number of players (if FES was already present before WS launching)
void cbFESNbPlayers2 ( CMessage & msgin , const std : : string & serviceName , TServiceId sid )
{
uint32 nbPlayers ;
uint32 nbPendingPlayers ;
msgin . serial ( nbPlayers ) ;
msgin . serial ( nbPendingPlayers ) ;
uint32 totalNbOnlineUsers = 0 , totalNbPendingUsers = 0 ;
for ( list < CFES > : : iterator it = FESList . begin ( ) ; it ! = FESList . end ( ) ; it + + )
{
CFES & fes = * it ;
if ( fes . SId = = sid )
{
nldebug ( " Frontend '%d' reported %d online users " , sid . get ( ) , nbPlayers ) ;
fes . NbUser = nbPlayers ;
fes . NbPendingUsers = nbPendingPlayers ;
if ( nbPlayers ! = 0 & & fes . State = = PatchOnly )
{
nlwarning ( " Frontend %d is in state PatchOnly, yet reports to have online %d players, state AcceptClientOnly is forced (FS_ACCEPT message sent) " ) ;
( * it ) . setToAcceptClients ( ) ;
}
}
totalNbOnlineUsers + = fes . NbUser ;
totalNbPendingUsers + = fes . NbPendingUsers ;
}
if ( CWelcomeServiceMod : : isInitialized ( ) )
CWelcomeServiceMod : : getInstance ( ) - > updateConnectedPlayerCount ( totalNbOnlineUsers , totalNbPendingUsers ) ;
}
/*
* Set Shard open state
*/
void setShardOpenState ( TShardOpenState state , bool writeInVar = true )
{
if ( writeInVar )
ShardOpen = state ;
if ( AllowDispatchMsgToLS )
{
if ( ! DontUseLS )
{
// send to LS current shard state
CMessage msgout ( " SET_SHARD_OPEN " ) ;
uint8 shardOpenState = ( uint8 ) state ;
msgout . serial ( shardOpenState ) ;
CUnifiedNetwork : : getInstance ( ) - > send ( " LS " , msgout ) ;
}
}
}
/*
* Set Shard Open State
* uint8 Open State ( 0 closed for all , 1 open for groups in cfg , 2 open for all )
*/
void cbSetShardOpen ( CMessage & msgin , const std : : string & serviceName , TServiceId sid )
{
uint8 shardOpenState ;
msgin . serial ( shardOpenState ) ;
if ( shardOpenState > OpenForAll )
{
shardOpenState = OpenForAll ;
}
setShardOpenState ( ( TShardOpenState ) shardOpenState ) ;
}
// forward declaration to callback
void cbShardOpenStateFile ( IVariable & var ) ;
/*
* Restore Shard Open state from config file or from file if found
*/
void cbRestoreShardOpen ( CMessage & msgin , const std : : string & serviceName , TServiceId sid )
{
// first restore state from config file
CConfigFile : : CVar * var = IService : : getInstance ( ) - > ConfigFile . getVarPtr ( " ShardOpen " ) ;
if ( var ! = NULL )
{
setShardOpenState ( ( TShardOpenState ) var - > asInt ( ) ) ;
}
// then restore state from state file, if it exists
cbShardOpenStateFile ( ShardOpenStateFile ) ;
}
// a new front end connecting to me, add it
void cbFESConnection ( const std : : string & serviceName , TServiceId sid , void * arg )
{
FESList . push_back ( CFES ( ( TServiceId ) sid ) ) ;
nldebug ( " new FES connection: sid %u " , sid . get ( ) ) ;
displayFES ( ) ;
bool dummy ;
FESList . back ( ) . reportStateToLS ( dummy ) ;
if ( ! UsePatchMode . get ( ) )
{
FESList . back ( ) . setToAcceptClients ( ) ;
}
}
// a front end closes the connection, deconnect him
void cbFESDisconnection ( const std : : string & serviceName , TServiceId sid , void * arg )
{
nldebug ( " new FES disconnection: sid %u " , sid . get ( ) ) ;
for ( list < CFES > : : iterator it = FESList . begin ( ) ; it ! = FESList . end ( ) ; it + + )
{
if ( ( * it ) . SId = = sid )
{
// send a message to the LS to say that all players from this FES are offline
map < uint32 , TServiceId > : : iterator itc = UserIdSockAssociations . begin ( ) ;
map < uint32 , TServiceId > : : iterator nitc = itc ;
while ( itc ! = UserIdSockAssociations . end ( ) )
{
nitc + + ;
if ( ( * itc ) . second = = sid )
{
// bye bye little player
uint32 userid = ( * itc ) . first ;
nlinfo ( " Due to a frontend crash, removed the player %d " , userid ) ;
if ( ! DontUseLS )
{
CMessage msgout ( " CC " ) ;
msgout . serial ( userid ) ;
uint8 con = 0 ;
msgout . serial ( con ) ;
CUnifiedNetwork : : getInstance ( ) - > send ( " LS " , msgout ) ;
}
UserIdSockAssociations . erase ( itc ) ;
}
itc = nitc ;
}
bool dummy ;
( * it ) . reportStateToLS ( dummy , false ) ;
// remove the FES
FESList . erase ( it ) ;
break ;
}
}
// Update the welcome service client with the new count of connection
uint32 totalNbOnlineUsers = 0 , totalNbPendingUsers = 0 ;
for ( list < CFES > : : iterator it = FESList . begin ( ) ; it ! = FESList . end ( ) ; it + + )
{
const CFES & fes = * it ;
totalNbOnlineUsers + = fes . NbUser ;
totalNbPendingUsers + = fes . NbPendingUsers ;
}
if ( CWelcomeServiceMod : : isInitialized ( ) )
CWelcomeServiceMod : : getInstance ( ) - > updateConnectedPlayerCount ( totalNbOnlineUsers , totalNbPendingUsers ) ;
displayFES ( ) ;
}
//
void cbServiceUp ( const std : : string & serviceName , TServiceId sid , void * arg )
{
OnlineServices . addInstance ( serviceName ) ;
bool online = OnlineServices . getOnlineStatus ( ) ;
reportOnlineStatus ( online ) ;
// send shard id to service
sint32 shardId ;
if ( IService : : getInstance ( ) - > haveArg ( ' S ' ) )
{
// use the command line param if set
shardId = atoi ( IService : : getInstance ( ) - > getArg ( ' S ' ) . c_str ( ) ) ;
}
else if ( IService : : getInstance ( ) - > ConfigFile . exists ( " ShardId " ) )
{
// use the config file param if set
shardId = IService : : getInstance ( ) - > ConfigFile . getVar ( " ShardId " ) . asInt ( ) ;
}
else
{
shardId = - 1 ;
}
if ( shardId = = - 1 )
{
nlerror ( " ShardId variable must be valid (>0) " ) ;
}
CMessage msgout ( " R_SH_ID " ) ;
msgout . serial ( shardId ) ;
CUnifiedNetwork : : getInstance ( ) - > send ( sid , msgout ) ;
}
//
void cbServiceDown ( const std : : string & serviceName , TServiceId sid , void * arg )
{
OnlineServices . removeInstance ( serviceName ) ;
bool online = OnlineServices . getOnlineStatus ( ) ;
reportOnlineStatus ( online ) ;
}
// Callback Array for message from FES
TUnifiedCallbackItem FESCallbackArray [ ] =
{
{ " SCS " , cbFESShardChooseShard } ,
{ " CC " , cbFESClientConnected } ,
{ " RPC " , cbFESRemovedPendingCookie } ,
{ " FEPA " , cbFESPatchAddress } ,
{ " NBPLAYERS " , cbFESNbPlayers } ,
{ " NBPLAYERS2 " , cbFESNbPlayers2 } ,
{ " SET_SHARD_OPEN " , cbSetShardOpen } ,
{ " RESTORE_SHARD_OPEN " , cbRestoreShardOpen } ,
} ;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////// CONNECTION TO THE LOGIN SERVICE ///////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void cbLSChooseShard ( CMessage & msgin , const std : : string & serviceName , TServiceId sid )
{
// the LS warns me that a new client want to come in my shard
nldebug ( " ERLOG: CS recvd from %s-%hu " , serviceName . c_str ( ) , sid . get ( ) ) ;
//
// S07: receive the "CS" message from LS and send the "CS" message to the selected FES
//
CLoginCookie cookie ;
msgin . serial ( cookie ) ;
string userName , userPriv , userExtended ;
msgin . serial ( userName ) ;
try
{
msgin . serial ( userPriv ) ;
}
catch ( Exception & )
{
nlwarning ( " LS didn't give me the user privilege for user '%s', set to empty " , userName . c_str ( ) ) ;
}
try
{
msgin . serial ( userExtended ) ;
}
catch ( Exception & )
{
nlwarning ( " LS didn't give me the extended data for user '%s', set to empty " , userName . c_str ( ) ) ;
}
string ret = lsChooseShard ( userName , cookie , userPriv , userExtended , WS : : TUserRole : : ur_player , 0xffffffff , ~ 0 ) ;
if ( ! ret . empty ( ) )
{
// send back an error message to LS
CMessage msgout ( " SCS " ) ;
msgout . serial ( ret ) ;
msgout . serial ( cookie ) ;
CUnifiedNetwork : : getInstance ( ) - > send ( sid , msgout ) ;
}
}
//void cbLSChooseShard (CMessage &msgin, const std::string &serviceName, uint16 sid)
std : : string lsChooseShard ( const std : : string & userName ,
const CLoginCookie & cookie ,
const std : : string & userPriv ,
const std : : string & userExtended ,
WS : : TUserRole userRole ,
uint32 instanceId ,
uint32 charSlot )
{
// the LS warns me that a new client want to come in my shard
//
// S07: receive the "CS" message from LS and send the "CS" message to the selected FES
//
/*
uint totalNbUsers ;
CFES * best = findBestFES ( totalNbUsers ) ;
if ( best = = NULL )
{
// answer the LS that we can't accept the user
CMessage msgout ( " SCS " ) ;
string reason = " No front-end server available " ;
msgout . serial ( reason ) ;
msgout . serial ( cookie ) ;
CUnifiedNetwork : : getInstance ( ) - > send ( sid , msgout ) ;
return ;
}
*/
uint totalNbUsers ;
CFES * best = findBestFES ( totalNbUsers ) ;
// could not find a good FES or best FES has more players than balance limit
if ( best = = NULL | | best - > getUsersCountHeuristic ( ) > = OpenFrontEndThreshold )
{
// open a new frontend
openNewFES ( ) ;
// reselect best FES (will return newly open FES, or previous if no more FES available)
best = findBestFES ( totalNbUsers ) ;
// check there is a FES available
if ( best = = NULL )
{
// answer the LS that we can't accept the user
return " No front-end server available " ;
}
}
bool authorizeUser = false ;
bool forceAuthorize = false ;
if ( userPriv = = " :DEV: " )
{
// devs have all privileges
authorizeUser = true ;
forceAuthorize = true ;
}
else if ( ShardOpen ! = ClosedForAll )
{
const std : : string & allowedGroups = OpenGroups ;
bool userInOpenGroups = ( ! userPriv . empty ( ) & & ! allowedGroups . empty ( ) & & allowedGroups . find ( userPriv ) ! = std : : string : : npos ) ;
// open for all or user is privileged
authorizeUser = ( ShardOpen = = OpenForAll | | userInOpenGroups ) ;
// let authorized users to force access even if limit is reached
forceAuthorize = userInOpenGroups ;
}
bool shardLimitReached = ( ( PlayerLimit . get ( ) ! = - 1 ) & & ( totalNbUsers > = ( uint ) PlayerLimit . get ( ) ) ) ;
if ( ! forceAuthorize & & ( ! authorizeUser | | shardLimitReached ) )
{
// answer the LS that we can't accept the user
CMessage msgout ( " SCS " ) ;
string reason ;
if ( shardLimitReached )
return " The shard is currently full, please try again in 5 minutes. " ;
else
return " The shard is closed. " ;
}
CMessage msgout ( " CS " ) ;
msgout . serial ( const_cast < CLoginCookie & > ( cookie ) ) ;
msgout . serial ( const_cast < string & > ( userName ) , const_cast < string & > ( userPriv ) , const_cast < string & > ( userExtended ) ) ;
msgout . serial ( instanceId ) ;
msgout . serial ( charSlot ) ;
CUnifiedNetwork : : getInstance ( ) - > send ( best - > SId , msgout ) ;
best - > NbPendingUsers + + ;
// Update counts
uint32 totalNbOnlineUsers = 0 , totalNbPendingUsers = 0 ;
for ( list < CFES > : : iterator it = FESList . begin ( ) ; it ! = FESList . end ( ) ; + + it )
{
totalNbOnlineUsers + = ( * it ) . NbUser ;
totalNbPendingUsers + = ( * it ) . NbPendingUsers ;
}
if ( CWelcomeServiceMod : : isInitialized ( ) )
CWelcomeServiceMod : : getInstance ( ) - > updateConnectedPlayerCount ( totalNbOnlineUsers , totalNbPendingUsers ) ;
return " " ;
}
void cbFailed ( CMessage & msgin , const std : : string & serviceName , TServiceId sid )
{
// I can't connect to the Login Service, just nlerror ();
string reason ;
msgin . serial ( reason ) ;
nlerror ( reason . c_str ( ) ) ;
}
bool disconnectClient ( uint32 userId )
{
map < uint32 , TServiceId > : : iterator it = UserIdSockAssociations . find ( userId ) ;
if ( it = = UserIdSockAssociations . end ( ) )
{
nlinfo ( " Login service ask to disconnect user %d, he is not connected here, so ignoring " , userId ) ;
return false ;
}
else
{
CMessage msgout ( " DC " ) ;
msgout . serial ( userId ) ;
CUnifiedNetwork : : getInstance ( ) - > send ( it - > second , msgout ) ;
return true ;
}
}
void cbLSDisconnectClient ( CMessage & msgin , const std : : string & serviceName , TServiceId sid )
{
// the LS tells me that i have to disconnect a client
uint32 userid ;
msgin . serial ( userid ) ;
disconnectClient ( userid ) ;
}
// connection to the LS, send the identification message
void cbLSConnection ( const std : : string & serviceName , TServiceId sid , void * arg )
{
sint32 shardId ;
if ( IService : : getInstance ( ) - > haveArg ( ' S ' ) )
{
// use the command line param if set
shardId = atoi ( IService : : getInstance ( ) - > getArg ( ' S ' ) . c_str ( ) ) ;
}
else if ( IService : : getInstance ( ) - > ConfigFile . exists ( " ShardId " ) )
{
// use the config file param if set
shardId = IService : : getInstance ( ) - > ConfigFile . getVar ( " ShardId " ) . asInt ( ) ;
}
else
{
shardId = - 1 ;
}
if ( shardId = = - 1 )
{
nlerror ( " ShardId variable must be valid (>0) " ) ;
}
CMessage msgout ( " WS_IDENT " ) ;
msgout . serial ( shardId ) ;
CUnifiedNetwork : : getInstance ( ) - > send ( sid , msgout ) ;
nlinfo ( " Connected to %s-%hu and sent identification with shardId '%d' " , serviceName . c_str ( ) , sid . get ( ) , shardId ) ;
// send state to LS
setShardOpenState ( ( TShardOpenState ) ( ShardOpen . get ( ) ) , false ) ;
//
if ( ! DontUseLS )
{
CMessage msgrpn ( " REPORT_NO_PATCH " ) ;
CUnifiedNetwork : : getInstance ( ) - > send ( " LS " , msgrpn ) ;
}
bool reportPatching = false ;
list < CFES > : : iterator itfs ;
for ( itfs = FESList . begin ( ) ; itfs ! = FESList . end ( ) ; + + itfs )
( * itfs ) . reportStateToLS ( reportPatching ) ;
}
// Callback for detection of config file change about "ExpectedServices"
void cbUpdateExpectedServices ( CConfigFile : : CVar & var )
{
OnlineServices . setExpectedInstances ( var ) ;
}
/*
* ShardOpen update functions / callbacks etc .
*/
/**
* updateShardOpenFromFile ( )
* Update ShardOpen from a file .
* Read a line of text in the file , converts it to int ( atoi ) , then casts into bool for ShardOpen .
*/
void updateShardOpenFromFile ( const std : : string & filename )
{
CIFile f ;
if ( ! f . open ( filename ) )
{
nlwarning ( " Failed to update ShardOpen from file '%s', couldn't open file " , filename . c_str ( ) ) ;
return ;
}
try
{
char readBuffer [ 256 ] ;
f . getline ( readBuffer , 256 ) ;
setShardOpenState ( ( TShardOpenState ) atoi ( readBuffer ) ) ;
nlinfo ( " Updated ShardOpen state to '%u' from file '%s' " , ShardOpen . get ( ) , filename . c_str ( ) ) ;
}
catch ( Exception & e )
{
nlwarning ( " Failed to update ShardOpen from file '%s', exception raised while getline() '%s' " , filename . c_str ( ) , e . what ( ) ) ;
}
}
std : : string ShardOpenStateFileName ;
/**
* cbShardOpen ( )
* Callback for ShardOpen
*/
void cbShardOpen ( IVariable & var )
{
setShardOpenState ( ( TShardOpenState ) ( ShardOpen . get ( ) ) , false ) ;
}
/**
* cbShardOpenStateFile ( )
* Callback for ShardOpenStateFile
*/
void cbShardOpenStateFile ( IVariable & var )
{
// remove previous file change callback
if ( ! ShardOpenStateFileName . empty ( ) )
{
CFile : : removeFileChangeCallback ( ShardOpenStateFileName ) ;
nlinfo ( " Removed callback for ShardOpenStateFileName file '%s' " , ShardOpenStateFileName . c_str ( ) ) ;
}
ShardOpenStateFileName = var . toString ( ) ;
if ( ! ShardOpenStateFileName . empty ( ) )
{
// set new callback for the file
CFile : : addFileChangeCallback ( ShardOpenStateFileName , updateShardOpenFromFile ) ;
nlinfo ( " Set callback for ShardOpenStateFileName file '%s' " , ShardOpenStateFileName . c_str ( ) ) ;
// and update state from file...
updateShardOpenFromFile ( ShardOpenStateFileName ) ;
}
}
/**
* cbUsePatchMode ( )
* Callback for UsePatchMode
*/
void cbUsePatchMode ( IVariable & var )
{
// if patch mode not set, set all fs in patching mode to accept clients now
if ( ! UsePatchMode . get ( ) )
{
nlinfo ( " UsePatchMode disabled, switch all patching servers to actual frontends " ) ;
list < CFES > : : iterator it ;
for ( it = FESList . begin ( ) ; it ! = FESList . end ( ) ; + + it )
{
if ( ( * it ) . State = = PatchOnly )
{
( * it ) . setToAcceptClients ( ) ;
}
}
}
}
// Callback Array for message from LS
TUnifiedCallbackItem LSCallbackArray [ ] =
{
{ " CS " , cbLSChooseShard } ,
{ " DC " , cbLSDisconnectClient } ,
{ " FAILED " , cbFailed } ,
} ;
class CWelcomeService : public IService
{
public :
/// Init the service, load the universal time.
void init ( )
{
string FrontendServiceName = ConfigFile . getVar ( " FrontendServiceName " ) . asString ( ) ;
try { FrontEndAddress = ConfigFile . getVar ( " FrontEndAddress " ) . asString ( ) ; } catch ( Exception & ) { }
nlinfo ( " Waiting frontend services named '%s' " , FrontendServiceName . c_str ( ) ) ;
CUnifiedNetwork : : getInstance ( ) - > setServiceUpCallback ( FrontendServiceName , cbFESConnection , NULL ) ;
CUnifiedNetwork : : getInstance ( ) - > setServiceDownCallback ( FrontendServiceName , cbFESDisconnection , NULL ) ;
CUnifiedNetwork : : getInstance ( ) - > setServiceUpCallback ( " * " , cbServiceUp , NULL ) ;
CUnifiedNetwork : : getInstance ( ) - > setServiceDownCallback ( " * " , cbServiceDown , NULL ) ;
// add a connection to the LS
string LSAddr ;
if ( haveArg ( ' T ' ) )
{
// use the command line param if set
LSAddr = getArg ( ' T ' ) ;
}
else if ( ConfigFile . exists ( " LSHost " ) )
{
// use the config file param if set
LSAddr = ConfigFile . getVar ( " LSHost " ) . asString ( ) ;
}
if ( haveArg ( ' S ' ) )
{
// use the command line param if set
uint shardId = atoi ( IService : : getInstance ( ) - > getArg ( ' S ' ) . c_str ( ) ) ;
nlinfo ( " Using shard id %u from command line '%s' " , shardId , IService : : getInstance ( ) - > getArg ( ' S ' ) . c_str ( ) ) ;
anticipateShardId ( shardId ) ;
}
else if ( ConfigFile . exists ( " ShardId " ) )
{
// use the config file param if set
uint shardId = IService : : getInstance ( ) - > ConfigFile . getVar ( " ShardId " ) . asInt ( ) ;
nlinfo ( " Using shard id %u from config file '%s' " , shardId , IService : : getInstance ( ) - > ConfigFile . getVar ( " ShardId " ) . asString ( ) . c_str ( ) ) ;
anticipateShardId ( shardId ) ;
}
// the config file must have a valid address where the login service is
nlassert ( ! LSAddr . empty ( ) ) ;
// add default port if not set by the config file
if ( LSAddr . find ( " : " ) = = string : : npos )
LSAddr + = " :49999 " ;
AllowDispatchMsgToLS = true ;
if ( ConfigFile . getVarPtr ( " DontUseLSService " ) = = NULL
| | ! ConfigFile . getVar ( " DontUseLSService " ) . asBool ( ) )
{
// We are using NeL Login Service
CUnifiedNetwork : : getInstance ( ) - > addCallbackArray ( LSCallbackArray , sizeof ( LSCallbackArray ) / sizeof ( LSCallbackArray [ 0 ] ) ) ;
if ( ! DontUseLS )
{
CUnifiedNetwork : : getInstance ( ) - > setServiceUpCallback ( " LS " , cbLSConnection , NULL ) ;
CUnifiedNetwork : : getInstance ( ) - > addService ( " LS " , LSAddr ) ;
}
}
// List of expected service instances
ConfigFile . setCallback ( " ExpectedServices " , cbUpdateExpectedServices ) ;
cbUpdateExpectedServices ( ConfigFile . getVar ( " ExpectedServices " ) ) ;
/*
* read config variable ShardOpenStateFile to update
*
*/
cbShardOpenStateFile ( ShardOpenStateFile ) ;
// // create a welcome service module (for SU comm)
// IModuleManager::getInstance().createModule("WelcomeService", "ws", "");
// // plug the module in the default gateway
// NLMISC::CCommandRegistry::getInstance().execute("ws.plug wg", InfoLog());
}
bool update ( )
{
// update the service status
removeStatusTag ( " DEV_ONLY " ) ;
removeStatusTag ( " RESTRICTED " ) ;
removeStatusTag ( " Open " ) ;
if ( ShardOpen = = 0 )
addStatusTag ( " DEV_ONLY " ) ;
else if ( ShardOpen = = 1 )
addStatusTag ( " RESTRICTED " ) ;
else if ( ShardOpen = = 2 )
addStatusTag ( " Open " ) ;
return true ;
}
} ;
static const char * getCompleteServiceName ( const IService * theService )
{
static std : : string s ;
s = " welcome_service " ;
if ( theService - > haveLongArg ( " wsname " ) )
{
s + = " _ " + theService - > getLongArg ( " wsname " ) ;
}
if ( theService - > haveLongArg ( " fullwsname " ) )
{
s = theService - > getLongArg ( " fullwsname " ) ;
}
return s . c_str ( ) ;
}
static const char * getShortServiceName ( const IService * theService )
{
static std : : string s ;
s = " WS " ;
if ( theService - > haveLongArg ( " shortwsname " ) )
{
s = theService - > getLongArg ( " shortwsname " ) ;
}
return s . c_str ( ) ;
}
// Service instantiation
NLNET_SERVICE_MAIN ( CWelcomeService , getShortServiceName ( scn ) , getCompleteServiceName ( scn ) , 0 , FESCallbackArray , NELNS_CONFIG , NELNS_LOGS ) ;
// welcome service module
//class CWelcomeServiceMod :
// public CEmptyModuleCommBehav<CEmptyModuleServiceBehav<CEmptySocketBehav<CModuleBase> > >,
// public WS::CWelcomeServiceSkel
//{
// void onProcessModuleMessage(IModuleProxy *sender, const CMessage &message)
// {
// if (CWelcomeServiceSkel::onDispatchMessage(sender, message))
// return;
//
// nlwarning("Unknown message '%s' received by '%s'",
// message.getName().c_str(),
// getModuleName().c_str());
// }
//
//
// ////// CWelcomeServiceSkel implementation
//
// // ask the welcome service to welcome a user
// virtual void welcomeUser(NLNET::IModuleProxy *sender, uint32 userId, const std::string &userName, const CLoginCookie &cookie, const std::string &priviledge, const std::string &exPriviledge, WS::TUserRole mode, uint32 instanceId)
// {
// string ret = lsChooseShard(userName,
// cookie,
// priviledge,
// exPriviledge,
// mode,
// instanceId);
//
// if (!ret.empty())
// {
// // TODO : correct this
// string fsAddr;
// CWelcomeServiceClientProxy wsc(sender);
// wsc.welcomeUserResult(this, userId, ret.empty(), fsAddr);
// }
// }
//
// // ask the welcome service to disconnect a user
// virtual void disconnectUser(NLNET::IModuleProxy *sender, uint32 userId)
// {
// nlstop;
// }
//
//};
namespace WS
{
void CWelcomeServiceMod : : onModuleUp ( IModuleProxy * proxy )
{
if ( proxy - > getModuleClassName ( ) = = " RingSessionManager " )
{
if ( _RingSessionManager ! = NULL )
{
nlwarning ( " WelcomeServiceMod::onModuleUp : receiving module up for RingSessionManager '%s', but already have it as '%s', replacing it " ,
proxy - > getModuleName ( ) . c_str ( ) ,
_RingSessionManager - > getModuleName ( ) . c_str ( ) ) ;
}
// store this module as the ring session manager
_RingSessionManager = proxy ;
// say hello to our new friend (transmit fixed session id if set in config file)
nlinfo ( " Registering welcome service module into session manager '%s' " , proxy - > getModuleName ( ) . c_str ( ) ) ;
uint32 sessionId = 0 ;
CConfigFile : : CVar * varFixedSessionId = IService : : getInstance ( ) - > ConfigFile . getVarPtr ( " FixedSessionId " ) ;
if ( varFixedSessionId )
sessionId = varFixedSessionId - > asInt ( ) ;
CWelcomeServiceClientProxy wscp ( proxy ) ;
wscp . registerWS ( this , IService : : getInstance ( ) - > getShardId ( ) , sessionId , OnlineServices . getOnlineStatus ( ) ) ;
// Send counts
uint32 totalNbOnlineUsers = 0 , totalNbPendingUsers = 0 ;
for ( list < CFES > : : iterator it = FESList . begin ( ) ; it ! = FESList . end ( ) ; + + it )
{
totalNbOnlineUsers + = ( * it ) . NbUser ;
totalNbPendingUsers + = ( * it ) . NbPendingUsers ;
}
CWelcomeServiceMod : : getInstance ( ) - > updateConnectedPlayerCount ( totalNbOnlineUsers , totalNbPendingUsers ) ;
}
else if ( proxy - > getModuleClassName ( ) = = " LoginService " )
{
_LoginService = proxy ;
}
}
void CWelcomeServiceMod : : onModuleDown ( IModuleProxy * proxy )
{
if ( _RingSessionManager = = proxy )
{
// remove this module as the ring session manager
_RingSessionManager = NULL ;
}
else if ( _LoginService = = proxy )
_LoginService = NULL ;
}
void CWelcomeServiceMod : : welcomeUser ( NLNET : : IModuleProxy * sender , uint32 charId , const std : : string & userName , const CLoginCookie & cookie , const std : : string & priviledge , const std : : string & exPriviledge , WS : : TUserRole mode , uint32 instanceId )
{
nldebug ( " ERLOG: welcomeUser(%u,%s,%s,%s,%s,%u,%u) " , charId , userName . c_str ( ) , cookie . toString ( ) . c_str ( ) , priviledge . c_str ( ) , exPriviledge . c_str ( ) , ( uint ) mode . getValue ( ) , instanceId ) ;
string ret = lsChooseShard ( userName ,
cookie ,
priviledge ,
exPriviledge ,
mode ,
instanceId ,
charId & 0xF ) ;
uint32 userId = charId > > 4 ;
if ( ! ret . empty ( ) )
{
nldebug ( " ERLOG: lsChooseShard returned an error => welcomeUserResult " ) ;
// TODO : correct this
string fsAddr ;
CWelcomeServiceClientProxy wsc ( sender ) ;
wsc . welcomeUserResult ( this , userId , false , fsAddr , ret ) ;
}
else
{
nldebug ( " ERLOG: lsChooseShard OK => adding to pending " ) ;
TPendingFEResponseInfo pfri ;
pfri . WSMod = this ;
pfri . UserId = userId ;
pfri . WaiterModule = sender ;
PendingFeResponse . insert ( make_pair ( cookie , pfri ) ) ;
}
}
void CWelcomeServiceMod : : pendingUserLost ( const NLNET : : CLoginCookie & cookie )
{
if ( ! _LoginService )
return ;
CLoginServiceProxy ls ( _LoginService ) ;
ls . pendingUserLost ( this , cookie ) ;
}
// register the module
NLNET_REGISTER_MODULE_FACTORY ( CWelcomeServiceMod , " WelcomeService " ) ;
} // namespace WS
//
// Variables
//
NLMISC_DYNVARIABLE ( uint32 , OnlineUsersNumber , " number of connected users on this shard " )
{
// we can only read the value
if ( get )
{
uint32 nbusers = 0 ;
for ( list < CFES > : : iterator it = FESList . begin ( ) ; it ! = FESList . end ( ) ; it + + )
{
nbusers + = ( * it ) . NbUser ;
}
* pointer = nbusers ;
}
}
//
// Commands
//
NLMISC_COMMAND ( frontends , " displays the list of all registered front ends " , " " )
{
if ( args . size ( ) ! = 0 ) return false ;
log . displayNL ( " Display the %d registered front end : " , FESList . size ( ) ) ;
for ( list < CFES > : : iterator it = FESList . begin ( ) ; it ! = FESList . end ( ) ; it + + )
{
// log.displayNL ("> FE %u: nb estimated users: %u nb users: %u, nb pending users : %u",
log . displayNL ( " > FE %u: nb users: %u, nb pending users : %u " ,
it - > SId . get ( ) ,
it - > NbUser ,
it - > NbPendingUsers ) ;
}
log . displayNL ( " End ot the list " ) ;
return true ;
}
NLMISC_COMMAND ( users , " displays the list of all registered users " , " " )
{
if ( args . size ( ) ! = 0 ) return false ;
log . displayNL ( " Display the %d registered users : " , UserIdSockAssociations . size ( ) ) ;
for ( map < uint32 , TServiceId > : : iterator it = UserIdSockAssociations . begin ( ) ; it ! = UserIdSockAssociations . end ( ) ; it + + )
{
log . displayNL ( " > %u SId=%u " , ( * it ) . first , ( * it ) . second . get ( ) ) ;
}
log . displayNL ( " End ot the list " ) ;
return true ;
}
NLMISC_COMMAND ( displayOnlineServices , " Display the online service instances " , " " )
{
OnlineServices . display ( log ) ;
return true ;
}
NLMISC_VARIABLE ( bool , OnlineStatus , " Main online status of the shard " ) ;