khanat-opennel-code/code/ryzom/server/src/frontend_service/client_host.h

613 lines
16 KiB
C++

// 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/>.
#ifndef NL_CLIENT_HOST_H
#define NL_CLIENT_HOST_H
#include "nel/misc/types_nl.h"
#include "nel/misc/vector.h"
#include "nel/misc/time_nl.h"
#include "nel/net/inet_address.h"
#include "nel/net/login_cookie.h"
#include "fe_types.h"
#include "fe_receive_task.h"
#include "game_share/action.h"
#include "client_entity_id_translator.h"
#include "impulse_encoder.h"
#include "entity_container.h"
#include "game_share/ryzom_entity_id.h"
#include "game_share/entity_types.h"
#include "game_share/welcome_service_itf.h"
#include <vector>
#include <deque>
const uint32 FirstClientId = 1;
const uint16 InvalidClientId = 0xFFFF;
namespace NLNET
{
class CUdpSock;
};
struct TPairState;
/**
* CClientIdPool
*/
class CClientIdPool
{
public:
/// Constructor
CClientIdPool()
{
TClientId i;
for ( i=0; i<=MaxNbClients; ++i )
{
_UsedPool[i] = false;
}
}
/// Get a free Id
TClientId getNewClientId()
{
TClientId i;
for ( i=FirstClientId; i<=MaxNbClients; ++i )
{
if ( ! _UsedPool[i] )
{
_UsedPool[i] = true;
return i;
}
}
return InvalidClientId;
}
/// Release an Id
void releaseId( TClientId id )
{
_UsedPool[id] = false;
}
private:
bool _UsedPool [MAX_NB_CLIENTS+1];
};
/// Get string for association state
const char *associationStateToString( uint8 as );
/**
* Client host
* \author Olivier Cado
* \author Nevrax France
* \date 2001
*/
class CClientHost
{
public:
/// Constructor
CClientHost( const NLNET::CInetAddress& addr, TClientId id ) :
Uid (0xFFFFFFFF),
InstanceId(0xFFFFFFFF),
StartupRole(WS::TUserRole::ur_player),
NbFreeEntityItems( 255 ),
PrioAmount( 0.0f ),
_Address(addr),
_ClientId(id),
_Id(NLMISC::CEntityId::Unknown),
_EntityIndex(),
_SendNumber(0),
_SendSyncTick(0),
_Synchronized(false),
_Disconnected(false),
_FirstReceiveNumber(0),
_ReceiveNumber(0xFFFFFFFF),
_ReceiveTime(0),
_DatagramLost(0),
_DatagramRepeated(0),
//SheetId(CLFECOMMON::INVALID_SHEETID),
//_ToggleBit(false),
//_OutBoxMeter(0),
IdTranslator(),
ImpulseEncoder(),
LastReceivedAck(0xFFFFFFFF),
ImpulseMultiPartNumber (0),
AuthorizedCharSlot(~0),
LastReceivedGameCycle(0),
LastSentSync(1),
//
LastDummy(~0),
LastSentDummy(~0),
LastSentCounter(0),
LastCounterTime(0),
//
//AvailableImpulseBitsize( "AvailImpulseBitsize", MaxImpulseBitSizes[2] ),
NbActionsSentAtCycle(0),
QuitId(0)
{
IdTranslator.setId( id );
ImpulseEncoder.setClientHost( this );
ConnectionState = Synchronize;
initClientBandwidth();
}
/// Destructor
~CClientHost();
/// Return IP and port
const NLNET::CInetAddress& address() { return _Address; }
/// Return client Id
TClientId clientId() const { return _ClientId; }
/// Return the entity index (for access in the entity container)
TEntityIndex entityIndex() const { return _EntityIndex; }
/// Return the id
const NLMISC::CEntityId& eId() const { return _Id; }
/// Set the entity index
void setEntityIndex( const TEntityIndex& ei );
/// Set the CEntityId
void setEId( const NLMISC::CEntityId& assigned_id );
/// Prepare a clean new outbox with current values
void setupOutBox( TOutBox& outbox );
/// Prepare a clean system header
void setupSystemHeader( TOutBox& outbox, uint8 code);
/// Compute host stats
void computeHostStats( const TReceivedMessage& msgin, uint32 currentcounter, bool updateAcknowledge );
/// Increment send number and return it
uint32 getNextSendNumber() { return ++_SendNumber; }
/// R access to last send number
uint32 sendNumber() const { return _SendNumber; }
/// R/W access to first receive number
uint32& firstReceiveNumber() { return _FirstReceiveNumber; }
/// R/W access to latest receive number
uint32& receiveNumber() { return _ReceiveNumber; }
/// R/W access to the toggle bit
//bool& toggleBit() { return _ToggleBit; }
/// Set receive time now
void setReceiveTimeNow();
/// Return receive time
NLMISC::TTime receiveTime() const { return _ReceiveTime; }
uint32 datagramLost() const { return _DatagramLost; }
void resetDatagramLost() { _DatagramLost = 0; }
uint32 datagramRepeated() const { return _DatagramRepeated; }
/// Setup sync for tick measures with client connection
void setFirstSentPacket(uint32 sentPacket, NLMISC::TGameCycle atTick)
{
#ifdef HALF_FREQUENCY_SENDING_TO_CLIENT
_SendSyncTick = atTick - sentPacket*2;
#else
_SendSyncTick = atTick - sentPacket;
#endif
}
/// Convert ack contained in client packet into tick date
NLMISC::TGameCycle getPacketTickDate(uint32 receivedPacketAck) const { return _SendSyncTick+receivedPacketAck; }
/// Get sync value for this client;
NLMISC::TGameCycle getSync() const { return _SendSyncTick; }
void disconnect() { _Disconnected = true; }
bool isDisconnected() { return _Disconnected; }
// Reset client vision
void resetClientVision();
/// Set clienthost to synchronize state
void setSynchronizeState()
{
ConnectionState = Synchronize;
}
/// Set clienthost to synchronize state
void setConnectedState()
{
ConnectionState = Connected;
}
/// Set clienthost to synchronize state
void setStalledState()
{
ConnectionState = Stalled;
}
/// Set clienthost to probe state
void setProbeState()
{
ConnectionState = Probe;
LastSentProbe = 0;
LastProbeTime = 0;
NumConsecutiveProbes = 0;
LastReceivedProbe = 0;
}
/// Set clienthost to ForceSynchronize state (i.e. Synchronize must not be replaced by Connected, as a sync must be sent to the client)
void setForceSynchronizeState()
{
ConnectionState = ForceSynchronize;
}
/// Initialize the counter/flag
void initSendCycle( bool initialState )
{
_WhenToSend = initialState;
}
/// Update the counter/flag
void incSendCycle()
{
#ifdef HALF_FREQUENCY_SENDING_TO_CLIENT
_WhenToSend = !_WhenToSend;
#endif
}
/// Return true if the counter/flag state is "to send" for the current cycle
bool whenToSend()
{
#ifdef HALF_FREQUENCY_SENDING_TO_CLIENT
return _WhenToSend;
#else
return true;
#endif
}
/// display nlinfo
void displayClientProperties( bool full=true, bool allProps=false, bool sortByDistance=false,NLMISC::CLog *log = NLMISC::InfoLog ) const;
/// display nlinfo for one slot
void displaySlotProperties( CLFECOMMON::TCLEntityId e, bool full=false, NLMISC::CLog *log = NLMISC::InfoLog ) const;
/// display nlinfo (1 line only)
void displayShortProps(NLMISC::CLog *log = NLMISC::InfoLog) const;
/// Return the cardinal direction from the player to the seen entity
const char * getDirection( CEntity *seenEntity, const TEntityIndex& seenEntityIndex ) const;
/// Initialize the client bandwidth (calls setClientBandwidth)
void initClientBandwidth();
/// Change the client bandwidth (set the nomimal size)
void setClientBandwidth( sint32 cbw )
{
_MaxOutboxSizeInBit = cbw;
_BitBandwidthUsageAvg = _MaxOutboxSizeInBit;
_BitImpulsionUsageAvg = MaxImpulseBitSizes[2];
_ImpulsionPrevRemainingActions = 0;
}
/// Return the current maximum number of bits that can fit in the outbox
sint32 getCurrentThrottle() const
{
return std::min( (sint32)(_MaxOutboxSizeInBit*2-_BitBandwidthUsageAvg), (sint32)(_MaxOutboxSizeInBit*3/2) );
}
/// Update the average bits filled that determine the throttle
void updateThrottle( TOutBox& outbox )
{
#ifdef NL_DEBUG
sint32 prevThrottle = getCurrentThrottle();
#endif
// Update the average of bits sent
_BitBandwidthUsageAvg = (_BitBandwidthUsageAvg*(_SendNumber-1) + outbox.getPosInBit()) / _SendNumber;
#ifdef NL_DEBUG
sint32 currThrottle = getCurrentThrottle();
if ( currThrottle != prevThrottle )
nldebug( "NFC: Client %hu, packet %u: throttle %d, %d filled now, %d average (%d kbps), %d nominal", _ClientId, _SendNumber, currThrottle, outbox.getPosInBit(), _BitBandwidthUsageAvg, _BitBandwidthUsageAvg*5/1000, _MaxOutboxSizeInBit );
#endif
}
/**
* Calculate the current maximum number of bits that can fit for impulsions,
* and set AvailableImpulsionBitSize in the mirror, using:
* - The number of bits filled (possibly exceeding the max when sending forced actions (database))
* - The number of remaining actions not forced
*/
void setImpulsionThrottle( sint32 currentNbBitsFilled, sint32 nominalBitSize, uint nbRemainingActions )
{
if ( !_EntityIndex.isValid() )
return;
if ( ((_ImpulsionPrevRemainingActions>12) && (nbRemainingActions > _ImpulsionPrevRemainingActions * 5/4))
|| _ImpulsionPrevRemainingActions>100 )
{
// The connection does not manage to send all the impulsions that come from the back-end, stop the database impulsions
CMirrorPropValue<uint16> availableImpulseBitsize( TheDataset, _EntityIndex, DSFirstPropertyAvailableImpulseBitSize );
availableImpulseBitsize = 0;
#ifdef NL_DEBUG
nldebug( "NFC: Client %hu: Blocking the AvailableImpulseBitsize to prevent impulsion congestion", _ClientId );
#endif
}
else
{
// The remaining actions number is stable, calculate the available bitsize
_BitImpulsionUsageAvg = (_BitImpulsionUsageAvg*(_SendNumber-1) + currentNbBitsFilled) / _SendNumber;
sint32 availBitsize;
if ( _BitImpulsionUsageAvg < nominalBitSize )
availBitsize = nominalBitSize;
else
availBitsize = std::max( (sint32)0, nominalBitSize*2 - _BitImpulsionUsageAvg );
CMirrorPropValue<uint16> availableImpulseBitsize( TheDataset, _EntityIndex, DSFirstPropertyAvailableImpulseBitSize );
#ifdef NL_DEBUG
if ( availBitsize != availableImpulseBitsize )
nldebug( "NFC: Client %hu, packet %u: AvailableImpulseBitsize %u, %d filled now, %d average, %d nominal", _ClientId, _SendNumber, availBitsize, currentNbBitsFilled, _BitImpulsionUsageAvg, nominalBitSize );
#endif
availableImpulseBitsize = (uint16)availBitsize;
}
_ImpulsionPrevRemainingActions = nbRemainingActions;
}
/// Force the impulsion throttle to 0 to prevent overflooding, when the FS does send any impulsion.
void setIdleImpulsionThrottle()
{
if ( !_EntityIndex.isValid() )
return;
CMirrorPropValue<uint16> availableImpulseBitsize( TheDataset, _EntityIndex, DSFirstPropertyAvailableImpulseBitSize );
availableImpulseBitsize = 0;
#ifdef NL_DEBUG
nldebug( "NFC: Client %hu: Blocking the AvailableImpulseBitsize to prevent impulsion congestion", _ClientId );
#endif
// The FS must not send any impulsion at this time
_ImpulsionPrevRemainingActions = 0;
}
// get Pair state
TPairState& getPairState(CLFECOMMON::TCLEntityId e);
// get Pair state
const TPairState& getPairState(CLFECOMMON::TCLEntityId e) const;
/// User identifier
TUid Uid;
/// User name (put on the NeL Launcher, transmitted by the login system)
std::string UserName;
/// User privilege (put on the NeL Launcher, transmitted by the login system)
std::string UserPriv;
/// User extended data (put on the NeL Launcher, transmitted by the login system)
std::string UserExtended;
/// Language Id
std::string LanguageId;
/// Login cookie
NLNET::CLoginCookie LoginCookie;
/// Startup instance
uint32 InstanceId;
/// Startup role
WS::TUserRole StartupRole;
/// Sheet identifier
//CLFECOMMON::TSheetId SheetId;
/// Number of free entity items
uint16 NbFreeEntityItems;
// Used to differenciate different multi part impulse
uint8 ImpulseMultiPartNumber;
// The only character slot that will be granted to connect [0..4], or 0xF (15) for any character
uint8 AuthorizedCharSlot;
/// States of the client connection
enum
{
Synchronize = 0,
Connected,
Probe,
Stalled,
// Disconnect, // Appears to be unused at least for now
ServerDown,
ForceSynchronize, // prevents Synchronize to be replaced by Connected
};
/// The current state of the client connection
uint ConnectionState;
/// The last received probe;
uint32 LastReceivedProbe;
/// The number of consecutive probes received lastly
uint32 NumConsecutiveProbes;
/// The last sent probe;
uint32 LastSentProbe;
/// The last sent sync
uint32 LastSentSync;
/// The time the last probe was sent
NLMISC::TTime LastProbeTime;
/// Stat
float PrioAmount;
/// Counter check
uint32 LastSentCounter;
/// Counter time
NLMISC::TTime LastCounterTime;
/// @name Generic action multipart handling structures
//@{
struct CGenericMultiPartTemp
{
CGenericMultiPartTemp () : NbBlock(0xFFFFFFFF) { }
uint32 NbBlock;
uint32 NbCurrentBlock;
uint32 TempSize;
std::vector<std::vector<uint8> > Temp;
std::vector<bool> BlockReceived;
void set (CLFECOMMON::CActionGenericMultiPart *agmp, CClientHost *client);
};
std::vector<CGenericMultiPartTemp> GenericMultiPartTemp;
//@}
/// Quit Id
uint32 QuitId;
private:
/// Client IP and port
NLNET::CInetAddress _Address;
/// Client Id
TClientId _ClientId;
/// Entity Id
TEntityIndex _EntityIndex;
/// CEntityId
NLMISC::CEntityId _Id;
/// Latest send number
uint32 _SendNumber;
/// Counter (or flag) determining when to send the prioritized properties to the client
bool _WhenToSend;
/// First tick on front end, corresponding to the first packet sent
NLMISC::TGameCycle _SendSyncTick;
bool _Synchronized;
bool _Disconnected;
/// First receive number
uint32 _FirstReceiveNumber;
/// Latest receive number
uint32 _ReceiveNumber;
/// Bit for important actions
//bool _ToggleBit;
/// Timestamp of latest receiving
NLMISC::TTime _ReceiveTime;
/// Stat
uint32 _DatagramLost;
/// Stat
uint32 _DatagramRepeated;
/// Nominal size
sint32 _MaxOutboxSizeInBit;
/// Number of bits that can be added to the nominal size (>0), or that must be removed from the nominal size (<0)
sint32 _DeltaBitsAllowed;
/// Previous number of actions not sent by the impulse encoder
uint32 _ImpulsionPrevRemainingActions;
/// Average of total bits sent
sint32 _BitBandwidthUsageAvg;
/// Average of impulsion bits sent
sint32 _BitImpulsionUsageAvg;
public:
// Client <- frontend entity id translator
CClientEntityIdTranslator IdTranslator;
/// Impulse management
CImpulseEncoder ImpulseEncoder;
/// Last ack number received from client
uint32 LastReceivedAck;
///
NLMISC::TGameCycle LastReceivedGameCycle;
uint32 LastDummy;
uint32 LastSentDummy;
// Estimation of available size in bit per cycle
//CPropertyBaseType<uint16> AvailableImpulseBitsize;
///
uint NbActionsSentAtCycle;
#ifdef NL_DEBUG
uint8 MoveNumber;
#endif
};
/**
*
*/
class CLimboClient
{
public:
CLimboClient( CClientHost* client ) :
AddrFrom(client->address()), Uid(client->Uid), UserName(client->UserName), UserPriv(client->UserPriv), UserExtended(client->UserExtended),
LanguageId(client->LanguageId), QuitId(client->QuitId)
{
// Set limbo timeout start
Timeout = NLMISC::CTime::getLocalTime();
LoginCookie = client->LoginCookie;
}
NLNET::CInetAddress AddrFrom;
TUid Uid;
std::string UserName, UserPriv, UserExtended, LanguageId;
uint32 QuitId;
NLMISC::TTime Timeout;
NLNET::CLoginCookie LoginCookie;
};
#endif // NL_CLIENT_HOST_H
/* End of client_host.h */