Add infos into target window
[ryzomcore.git] / ryzom / server / src / frontend_service / client_host.cpp
blob053cfe2b79ae7503180e06971afcb11d572e32af
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "stdpch.h"
21 #include "client_host.h"
22 #include "frontend_service.h"
24 #include "game_share/entity_types.h" // required for ifdef
26 #include "id_impulsions.h"
27 #include "uid_impulsions.h"
29 using namespace std;
30 using namespace NLMISC;
31 using namespace CLFECOMMON;
34 #ifdef HALF_FREQUENCY_SENDING_TO_CLIENT
35 //#pragma message ("HALF_FREQUENCY_SENDING_TO_CLIENT")
36 #else
37 //#pragma message ("FULL_FREQUENCY_SENDING_TO_CLIENT")
38 #endif
41 /**
42 * Comparison functor
43 * Helps sorting by distance the entities seen by a client, by priority
45 struct TComparePairsByDistance
47 TComparePairsByDistance( CVisionArray *va, TClientId clientId ) : _VisionArray(va), _ClientId(clientId) {}
49 bool operator() ( CLFECOMMON::TCLEntityId first, CLFECOMMON::TCLEntityId second )
51 return ( _VisionArray->getPairState( _ClientId, first ).DistanceCE < _VisionArray->getPairState( _ClientId, second ).DistanceCE );
54 TClientId _ClientId;
55 CVisionArray *_VisionArray;
58 // get Pair state
59 inline TPairState& CClientHost::getPairState(TCLEntityId e)
61 return CFrontEndService::instance()->PrioSub.VisionArray.getPairState( _ClientId, e );
63 // get Pair state
64 inline const TPairState& CClientHost::getPairState(TCLEntityId e) const
66 return CFrontEndService::instance()->PrioSub.VisionArray.getPairState( _ClientId, e );
71 * Prepare a clean new outbox with current values
74 void CClientHost::setupOutBox( TOutBox& outbox )
76 // only fill outbox headers if connected
77 // in system mode, fill is done manually because packets are not sent at each update
78 nlassert ( ConnectionState == Connected );
79 // a. Add the send number belonging to the destination client
80 uint32 sendnumber = getNextSendNumber();
81 outbox.serialAndLog1( sendnumber );
83 // b. System bit
84 bool systemMode = false;
85 outbox.serialBitAndLog( systemMode );
87 // b. Add the latest receive number belonging to the destination client
88 outbox.serialAndLog1( _ReceiveNumber );
90 // c. Add the "toggle" bit for important actions
91 //OutBox.serialBit( _ToggleBit );
95 * Prepare a clean system header
97 void CClientHost::setupSystemHeader( TOutBox& outbox, uint8 code)
99 // Only setup header for special states such as Synchronize, Probe...
100 nlassert(ConnectionState != Connected);
102 // checks the outbox is really cleared
103 nlassert(outbox.length() == 0);
105 // a. Add the send number belonging to the destination client
106 uint32 sendnumber = getNextSendNumber();
107 outbox.serialAndLog1( sendnumber );
109 // b. System bit
110 bool systemMode = true;
111 outbox.serialBitAndLog( systemMode );
113 // c. System message code
114 outbox.serialAndLog1( code );
119 * Set receive time now
121 void CClientHost::setReceiveTimeNow()
123 _ReceiveTime = CTime::getLocalTime();
128 * Initialize the client bandwidth
130 void CClientHost::initClientBandwidth()
132 setClientBandwidth( CFrontEndService::instance()->sendSub()->clientBandwidth() );
137 * CClientHost: Compute host stats
139 void CClientHost::computeHostStats( const TReceivedMessage& msgin, uint32 currentcounter, bool updateAcknowledge )
141 if ( _ReceiveNumber == 0xFFFFFFFF )
143 _FirstReceiveNumber = currentcounter;
144 if (updateAcknowledge)
145 _ReceiveNumber = currentcounter;
147 else if ( currentcounter <= _ReceiveNumber )
149 ++_DatagramRepeated;
151 else if ( currentcounter > _ReceiveNumber+1 )
153 _DatagramLost += currentcounter-(_ReceiveNumber+1);
154 if (updateAcknowledge)
155 _ReceiveNumber = currentcounter;
157 else if (updateAcknowledge)
159 _ReceiveNumber = currentcounter;
167 const char *associationStateToString( uint8 as )
169 switch( as )
171 case TPairState::UnusedAssociation: return "Unused"; break;
172 case TPairState::AwaitingAssAck: return "Assocn"; break;
173 case TPairState::NormalAssociation: return "Normal"; break;
174 case TPairState::AwaitingDisAck: return "Disass"; break;
177 // CHANGED BEN
178 case CClientEntityIdTranslator::CEntityInfo::UnusedAssociation : return "Unused"; break;
179 case CClientEntityIdTranslator::CEntityInfo::AwaitingAssAck : return "Assocn"; break;
180 case CClientEntityIdTranslator::CEntityInfo::NormalAssociation : return "Normal"; break;
181 case CClientEntityIdTranslator::CEntityInfo::AwaitingDisAck : return "Disass"; break;
183 /*case CClientEntityIdTranslator::CEntityInfo::SubstitutionBeforeDisAck : return "Substitution before dis ack"; break;
184 case CClientEntityIdTranslator::CEntityInfo::SubstitutionAfterDisAck : return "Substitution after dis ack"; break;
185 case CClientEntityIdTranslator::CEntityInfo::CancelledSubstitution : return "Cancelled substitution"; break;*/
186 default: return "INVALID ASSOCIATION CODE";
191 inline std::string getUserName( const TEntityIndex& entityIndex )
193 string name;
194 if ( entityIndex.isValid() )
196 TClientId clientId = CFrontEndService::instance()->receiveSub()->EntityToClient.getClientId( entityIndex );
197 if ( clientId != INVALID_CLIENT )
199 CClientHost *client = CFrontEndService::instance()->receiveSub()->clientIdCont()[clientId];
200 if ( client )
202 name = string(" ") + client->UserName;
206 return name;
211 * display nlinfo
213 void CClientHost::displayClientProperties( bool full, bool allProps, bool sortByDistance, NLMISC::CLog *log ) const
215 // General properties
216 bool invision = false;
217 TSheetId sheetId = INVALID_SHEETID;
218 string sheetIdS = "_";
219 CEntity *sentity = NULL;
220 if ( (_EntityIndex.isValid()) && (_EntityIndex.getIndex() < (uint32)TheDataset.maxNbRows()) )
222 sentity = TheEntityContainer->getEntity( _EntityIndex );
223 if ( sentity )
225 invision = true;
226 CMirrorPropValueRO<uint32> propSheet( TheDataset, _EntityIndex, DSPropertySHEET );
227 sheetId = propSheet();
228 if ( sentity->propertyIsInitialized( PROPERTY_SHEET, DSPropertySHEET, _EntityIndex, (TYPE_SHEET*)NULL ) )
230 sheetIdS = CSheetId(sheetId).toString();
235 const char *notset = "(not set)";
236 log->displayNL( "C%hu E%s %s %s sheet %s Uid %u, %s, %s", _ClientId, _EntityIndex.isValid() ? toString("%u", _EntityIndex.getIndex()).c_str() : notset, _Id.isUnknownId() ? notset : _Id.toString().c_str(), getEntityName( _EntityIndex ).c_str(), (sheetId!=~0) ? toString( "%s (%u)", sheetIdS.c_str(), sheetId).c_str() : notset, Uid, UserName.c_str(), invision?"in vision":"not in vision" );
237 if ( sentity )
239 log->displayNL( "Position (m): %d %d %d - Local: %d %d %d - Mode: %s", sentity->posXm( _EntityIndex ), sentity->posYm( _EntityIndex ), sentity->posZm( _EntityIndex ), sentity->posLocalXm( _EntityIndex ), sentity->posLocalYm( _EntityIndex ), sentity->posLocalZm( _EntityIndex ), (sentity->z( _EntityIndex )&0x1)?"Relative":"Absolute" );
241 log->displayNL( "Host %s, %s, %s", _Address.asString().c_str(), _Disconnected?"disconnected":"connected", _Synchronized?"synchronized":"not synchronized" );
242 log->displayNL( "Latest activity %u ms ago, state %s", (uint32)(CTime::getLocalTime()-_ReceiveTime),
243 ConnectionState==Synchronize?"SYNC":ConnectionState==Connected?"CONN":ConnectionState==Probe?"PROBE":ConnectionState==ForceSynchronize?"FORCE_SYNC":"DISC" );
244 #ifdef HALF_FREQUENCY_SENDING_TO_CLIENT
245 sint freq = 5;
246 #else
247 sint freq = 10;
248 #endif
249 if ( _EntityIndex.isValid())
251 CMirrorPropValueRO<uint16> availableImpulseBitsize( TheDataset, _EntityIndex, DSFirstPropertyAvailableImpulseBitSize );
252 log->displayNL( "Bit bandwidth usage feeding client at %d Hz: %d bps (max 13312), current throttle %d; including impulsion %d, database throttle %d",
253 freq, _BitBandwidthUsageAvg*freq, getCurrentThrottle()*freq, _BitImpulsionUsageAvg*freq, availableImpulseBitsize*freq );
255 // Impulsion stats
256 log->displayNL( "Nb messages in impulse queues: %u %u %u", ImpulseEncoder.queueSize(0), ImpulseEncoder.queueSize(1), ImpulseEncoder.queueSize(2) );
259 if ( full )
261 log->displayNL( "Vision slots (%hu free):", NbFreeEntityItems );
263 // Client sees *
264 vector<sint> slots;
266 sint e;
267 for ( e=0; e!=MAX_SEEN_ENTITIES_PER_CLIENT; ++e )
269 //uint8 assState;
270 //if ( (assState = CFrontEndService::instance()->PrioSub.VisionArray.getAssociationState( this, (TCLEntityId)e )) != CClientEntityIdTranslator::CEntityInfo::UnusedAssociation ) // CHANGED BEN
271 if ( getPairState( (TCLEntityId)e ).AssociationState != TPairState::UnusedAssociation )
273 slots.push_back( e );
277 if ( sortByDistance )
279 sort( slots.begin(), slots.end(), TComparePairsByDistance( &(CFrontEndService::instance()->PrioSub.VisionArray), _ClientId ) );
282 vector<sint>::iterator islot;
283 for ( islot=slots.begin(); islot!=slots.end(); ++islot )
285 displaySlotProperties( *islot, allProps, log );
288 // Client is seen by * on this front-end
289 /*if ( (_EntityIndex.isValid()) && (_EntityIndex < TheDataSet.maxNbRows()) )
291 log->displayNL( "Visibility of this entity by other clients on this front-end:" );
292 TObserverList::const_iterator iol;
293 for ( iol= CFrontEndService::instance()->PrioSub.VisionProvider.observerList( _EntityIndex ).begin();
294 iol!=CFrontEndService::instance()->PrioSub.VisionProvider.observerList( _EntityIndex ).end();
295 ++iol )
297 log->displayNL( "* Seen by client %hu using slot %hu", (*iol).ClientId, (uint16)(*iol).Slot );
300 else
302 log->displayNL( "Cannot access the observer list" );
305 TheEntityContainer->mirrorInstance().displayRows( _Id, *log );
311 * display nlinfo for one slot
313 void CClientHost::displaySlotProperties( CLFECOMMON::TCLEntityId e, bool full, NLMISC::CLog *log ) const
315 //const CClientEntityIdTranslator::CEntityInfo& info = IdTranslator.getInfo( (TCLEntityId)e );
316 TEntityIndex seenEntityIndex = getPairState( e ).EntityIndex;
317 if ( ! seenEntityIndex.isValid() )
318 return;
319 CEntity *seenEntity = TheEntityContainer->getEntity( seenEntityIndex );
320 CAction::TValue vlsx, vlsy, vlsz;
321 CFrontEndService::instance()->history()->getPosition( _ClientId, e, vlsx, vlsy, vlsz ); // always the absolute pos
322 TCoord lsx = ((sint32)vlsx)/1000, lsy = ((sint32)vlsy)/1000, lsz = ((sint32)vlsz)/1000;
323 TCoord sentMileage = CFrontEndService::instance()->history()->getMileage( _ClientId, e );
324 TCoord mileageDelta = seenEntity->Mileage - sentMileage;
325 const TPairState& pairState = getPairState( e );
326 string sheetId, sheetIdS;
327 if ( seenEntity->propertyIsInitialized( PROPERTY_SHEET, DSPropertySHEET, seenEntityIndex, (TYPE_SHEET*)NULL ) )
329 CMirrorPropValueRO<uint32> propSheet( TheDataset, seenEntityIndex, DSPropertySHEET );
330 sheetId = toString("%u", propSheet() );
331 sheetIdS = CSheetId(propSheet()).toString();
333 else
335 sheetId = "_";
336 sheetIdS = "_";
338 const CEntityId& seenEntityId = TheDataset.getEntityId( seenEntityIndex );
339 log->displayNL( "* Slot %d E%u %s %s st%s %s sheet %s (%s) dist(m) %0.1f %s pos %d %d %d, sent %d %d %d %sdelta %d prio %.1f ab %hu",
341 seenEntityIndex.getIndex(),
342 (seenEntityId.getType()==RYZOMID::player)?(string("PLAYER")+getUserName(seenEntityIndex)).c_str():RYZOMID::toString( (RYZOMID::TTypeId)seenEntityId.getType() ).c_str(),
343 seenEntityId.toString().c_str(),
344 associationStateToString( getPairState( e ).AssociationState),
345 getEntityName(seenEntityIndex).c_str(),
346 sheetId.c_str(),
347 sheetIdS.c_str(),
348 (float)pairState.DistanceCE/1000.0f,
349 getDirection( seenEntity, seenEntityIndex ),
350 seenEntity->posXm( seenEntityIndex ),
351 seenEntity->posYm( seenEntityIndex ),
352 seenEntity->posZm( seenEntityIndex ),
353 lsx, lsy, lsz,
354 (sentMileage==0)?"(not sent yet) ":"", mileageDelta,
355 pairState.getPrio(), (uint16)(pairState.AssociationChangeBits & 0x3) );
357 if ( full )
359 log->displayNL( "| Visual properties of E%u: ", seenEntityIndex.getIndex() );
360 seenEntity->displayProperties( seenEntityIndex, log, _ClientId, e );
366 * Return the cardinal direction from the player to the seen entity
368 const char * CClientHost::getDirection( CEntity *seenEntity, const TEntityIndex& seenEntityIndex ) const
370 if ( !_EntityIndex.isValid() )
371 return "-";
372 CEntity *entity = TheEntityContainer->getEntity( _EntityIndex );
373 if ( entity == NULL )
374 return "-";
376 float dx = (float)(seenEntity->X() - entity->X());
377 float dy = (float)(seenEntity->Y() - entity->Y());
378 float angle = (float)fmod( atan2(dy, dx) + (2*Pi), 2*Pi );
379 uint direction = (uint) ( 8.0f*angle/((float)Pi) );
380 nlassert ( direction<16 );
381 const char * txts[] =
383 "E",
384 "ENE",
385 "NE",
386 "NNE",
387 "N",
388 "NNW",
389 "NW",
390 "WNW",
391 "W",
392 "WSW",
393 "SW",
394 "SSW",
395 "S",
396 "SSE",
397 "SE",
398 "ESE",
401 return txts[direction];
406 * display nlinfo (1 line only)
408 void CClientHost::displayShortProps(NLMISC::CLog *log) const
410 // General properties
411 bool invision = false;
412 if ( (_EntityIndex.isValid()) && (_EntityIndex.getIndex() < (uint32)TheDataset.maxNbRows()) )
414 CEntity *sentity = TheEntityContainer->getEntity( _EntityIndex );
415 if ( sentity != NULL )
416 invision = true;
418 log->displayNL( "Client %hu: E%u %s '%s' uid %u '%s' %s, %s", _ClientId, _EntityIndex.getIndex(), _Id.toString().c_str(), getEntityName(_EntityIndex).c_str(), Uid, UserName.c_str(), invision?"in vision":"not in vision", _Address.asString().c_str() );
424 * Set the CEntityId
426 void CClientHost::setEId( const NLMISC::CEntityId& assigned_id )
428 _Id = assigned_id;
433 * Destructor
435 CClientHost::~CClientHost()
437 //REMOVE_PROPERTY_FROM_EMITER( _Id, uint16, "AvailImpulseBitsize" );
438 //_Id = CEntityId::Unknown;
443 * Set the entity index
445 void CClientHost::setEntityIndex( const TEntityIndex& ei )
447 _EntityIndex = ei;
448 CFrontEndService::instance()->PrioSub.VisionArray.setEntityIndex( _ClientId, 0, ei );
452 void CClientHost::CGenericMultiPartTemp::set (CActionGenericMultiPart *agmp, CClientHost *client)
454 if (NbBlock == 0xFFFFFFFF)
456 // new GenericMultiPart
457 NbBlock = agmp->NbBlock;
458 NbCurrentBlock = 0;
459 TempSize = 0;
460 Temp.clear();
461 Temp.resize(NbBlock);
462 BlockReceived.resize(NbBlock);
463 for (uint i = 0; i < NbBlock; i++)
464 BlockReceived[i] = false;
467 nlassert (NbBlock == agmp->NbBlock);
468 nlassert (NbBlock > agmp->Part);
470 // check if the block was already received
471 if (BlockReceived[agmp->Part])
473 return;
476 Temp[agmp->Part] = agmp->PartCont;
477 BlockReceived[agmp->Part] = true;
479 NbCurrentBlock++;
480 TempSize += (uint32)agmp->PartCont.size();
482 if (NbCurrentBlock == NbBlock)
484 // reform the total action
485 CBitMemStream bms(true);
487 uint8 *ptr = bms.bufferToFill (TempSize);
489 for (uint i = 0; i < Temp.size (); i++)
491 memcpy (ptr, &(Temp[i][0]), Temp[i].size());
492 ptr += Temp[i].size();
495 NbBlock = 0xFFFFFFFF;
497 if ( client->eId().isUnknownId() )
499 routeImpulsionUidFromClient(bms, client->Uid, client->LastReceivedGameCycle);
501 else
503 routeImpulsionIdFromClient(bms, client->eId(), client->LastReceivedGameCycle);
508 void CClientHost::resetClientVision()
510 // Get all entities seen by this client
511 sint e;
512 for ( e=0; e!=MAX_SEEN_ENTITIES_PER_CLIENT; ++e )
514 TEntityIndex entityindex = getPairState( (TCLEntityId)e ).EntityIndex;
516 //if ( CFrontEndService::instance()->PrioSub.VisionArray.getAssociationState( this, (TCLEntityId)e ) != CClientEntityIdTranslator::CEntityInfo::UnusedAssociation ) // CHANGED BEN
517 if (getPairState( (TCLEntityId)e ).AssociationState != TPairState::UnusedAssociation )
519 // Remove from observer list: the clients who see the entity (and its ceid for them)
520 /*if ( entityindex != INVALID_ENTITY_INDEX )
522 CFrontEndService::instance()->PrioSub.VisionProvider.removeFromObserverList( entityindex, clienthost->clientId(), (TCLEntityId)e );
524 else
526 nlwarning( "Invalid entity index while trying to remove slot %hu from observer list of leaving client %hu", (uint16)e, clienthost->clientId() );
529 // Remove pair from history
530 CFrontEndService::instance()->history()->removeEntityOfClient( e, clientId() );
532 // No need to remove Id because they are stored in the client object, which will be deleted
535 CFrontEndService::instance()->PrioSub.Prioritizer.removeAllEntitiesSeenByClient( clientId() );
537 // Reset items
538 for ( e=0; e!=MAX_SEEN_ENTITIES_PER_CLIENT; ++e )
540 // Reset EntityIndex
541 getPairState(e).resetItem();
542 getPairState(e).resetAssociation();