Resolve "Toggle Free Look with Hotkey"
[ryzomcore.git] / ryzom / server / src / frontend_service / property_dispatcher.cpp
blob88a7e1a8a3d1e9d61d64c7f5943cb2714800610d
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"
22 #include "property_dispatcher.h"
23 #include "vision_array.h"
24 #include "property_id_translator.h"
25 #include "frontend_service.h"
26 #include "fe_stat.h"
27 //#include <sstream>
29 using namespace std;
30 using namespace NLMISC;
31 using namespace CLFECOMMON;
35 * Constructor
37 CPropertyDispatcher::CPropertyDispatcher() :
38 _VisionArray( NULL ),
39 _PropTranslator( NULL ),
40 _ClientId( 0 )
41 /*_HPThreshold( 0 ),
42 _HPTDelta( 0 ),*/
43 //_NbSentParcelsInCycle( 0 )
44 /*NbSetPrio( 0 ),
45 NbReenable( 0 )*/
47 _NextParcel.Prio = DISABLE_PRIORITY;
48 _NextParcel.Offset = INVALID_OFFSET;
53 * Initialization
55 void CPropertyDispatcher::init( TClientId clientid )
57 _ClientId = clientid;
58 _VisionArray = &(CFrontEndService::instance()->PrioSub.VisionArray);
59 _PropTranslator = &(CFrontEndService::instance()->PrioSub.PropTranslator);
61 // Initialize highest priority threshold from theorical maxratio
62 //_HPThreshold = CFrontEndService::instance()->PrioSub.Prioritizer.MaxRatio / NB_PRIORITIES;
67 * Remove from the property dispatcher shelves, or does nothing if offset is INVALID_OFFSET
69 void CPropertyDispatcher::removeProp( const TPropParcelPtr& parcelptr )
71 // Remove corresponding item in shelves (do nothing if there is no item yet)
72 if ( parcelptr.Offset != INVALID_OFFSET )
74 //nlinfo( "FEPRIO: Removing prop from dispatcher: %u-%u", parcelptr.Prio, parcelptr.Offset );
75 TPropertyShelf& shelf = getShelf( parcelptr.Prio );
76 //TMPDEBUG
77 if ( parcelptr.Offset < shelf.size() )
79 TPropParcel& parcel = getParcel( shelf, parcelptr.Offset );
81 // Remove parcel
82 TPropParcel& lastone = lastParcel( shelf );
83 if ( &parcel != &lastone )
85 parcel = lastone;
86 _VisionArray->updateLink( _ClientId, parcel, parcelptr.Offset );
88 shelf.pop_back();
90 else
92 nlwarning( "Cannot removeProp with invalid offset %hd", parcelptr.Offset );
99 * Remove all corresponding properties from the property dispatcher
101 void CPropertyDispatcher::removePropsFromShelves( TClientId clientid, TCLEntityId ceid )
103 sint ppi = 0;
104 TPropParcelPtr& parcelptr = _VisionArray->prioLoc( clientid, ceid, ppi );
105 removeProp( parcelptr );
106 parcelptr.Prio = DISABLE_PRIORITY;
107 parcelptr.Offset = INVALID_OFFSET;
109 // Skip special props
111 for ( ppi=3; ppi!=MAX_PROPERTIES_PER_ENTITY; ++ppi )
113 TPropParcelPtr& parcelptr = _VisionArray->prioLoc( clientid, ceid, ppi );
114 removeProp( parcelptr );
115 parcelptr.Prio = DISABLE_PRIORITY;
116 parcelptr.Offset = INVALID_OFFSET;
122 * Make a new enabled parcel in the shelf corresponding to parcelptr.Prio and fill the parcelptr.Offset
124 void CPropertyDispatcher::addProp( TPropParcelPtr& parcelptr, TCLEntityId ceid, TPropIndex propindex )
126 TPropParcel parcel;
127 parcel.CeId = ceid;
128 parcel.PropIndex = propindex;
129 parcel.Enabled = true;
131 // Add parcel and fill the offset field
132 TPropertyShelf& shelf = getShelf( parcelptr.Prio );
133 shelf.push_back( parcel );
134 parcelptr.Offset = shelf.size() - 1;
136 //printShelves( "after addProp", true );
141 * Initialize dispatch cycle
143 void CPropertyDispatcher::initDispatcherCycle( /*bool rescalePriorities*/ )
145 // Adjust threshold for highest priority selection
146 /*if ( rescalePriorities )
148 adjustHPThreshold();
151 // Set the pointer ready to advance to the first parcel
152 _NextParcel.Prio = INITIAL_PRIORITY;
153 _NextParcel.Offset = -1;
155 //_NbSentParcelsInCycle = 0;
157 // Move pointer to first parcel
158 incNextParcel();
160 // Display debug info
161 //printShelves( "after initDispatcherCycle", false );
162 //printShelfSizes();
167 * Increment pointer to next parcel
169 void CPropertyDispatcher::incNextParcel()
171 if ( _NextParcel.Offset < nbParcels(_NextParcel.Prio) - 1 )
173 // Advance to the next parcel in the same shelf
174 ++_NextParcel.Offset;
176 // Check if the current parcel is enabled, otherwise advance to next one
177 if ( ! getParcel( _NextParcel ).Enabled )
179 incNextParcel();
182 else
184 if ( _NextParcel.Prio < LAST_PRIO_SENT )
186 // "Carriage return" to the next shelf
187 ++_NextParcel.Prio;
188 _NextParcel.Offset = 0;
190 // Skip any empty shelves
191 skipEmptyShelves();
193 else
195 // Don't go past LAST_PRIO_SENT
196 _NextParcel.Offset = INVALID_OFFSET;
203 * Skip any empty shelves
205 inline void CPropertyDispatcher::skipEmptyShelves()
207 // Skip empty shelves
208 while ( _PropShelves[_NextParcel.Prio].empty() && (_NextParcel.Prio < LAST_PRIO_SENT) )
210 ++_NextParcel.Prio;
213 // Detect end of data
214 if ( _PropShelves[_NextParcel.Prio].empty() )
216 _NextParcel.Offset = INVALID_OFFSET;
218 else
220 // Check if the current parcel is enabled, otherwise advance to next one
221 if ( ! getParcel( _NextParcel ).Enabled )
223 incNextParcel();
230 * Return which property to send next, or NULL if there is no more.
232 //const TPropParcel *CPropertyDispatcher::getNextParcel()
236 * Set status: true if the latest parcel got by getNextParcel() can be sent in the current cycle, otherwise false
237 * If true, the parcel gets disabled.
238 * Must be called after getNextParcel() every time getNextParcel() is called
240 void CPropertyDispatcher::setParcelStatusTrue()
242 // Status is set when the parcel was sent
243 if ( _NextParcel.Offset != INVALID_OFFSET )
245 //++_NbSentParcelsInCycle;
246 TPropParcel& parcel = getParcel( _NextParcel );
248 // Continuous or discreet property ?
249 //if ( _PropTranslator->isContinuous( _VisionArray->getEntityIndex( _ClientId, parcel.CeId ), parcel.PropIndex ) )
250 if ( parcel.PropIndex < FIRST_DISCREET_PROPINDEX )
252 // Continuous: disable the parcel
253 parcel.Enabled = false;
255 #ifdef MEASURE_FRONTEND_TABLES
256 // Debug: this is ok because the position is a common prop and its propertyid equals the propindex
257 if ( (_ClientId == 1) && (parcel.PropIndex == PROPERTY_POSITION) )
259 PosSentCntFrame.SeenEntities[parcel.CeId] = 1;
261 #endif
264 else
266 // Discreet: ask parcel removal and set update status to Updating
267 TPropState& propstate = _VisionArray->propState( _ClientId, parcel );
268 addToRemoveList( &propstate );
269 propstate.UpdateStatus = Updating;
271 // DEBUG DISPLAY
272 //nlinfo( "FESEND: Sent discrete property %hu to client %hu slot %hu", propertyid, _ClientId, (uint16)parcel.CeId );
276 // Advance to next parcel
277 incNextParcel();
283 * Remove the parcels that have been added to the remove list (call it after a cycle)
285 void CPropertyDispatcher::flush()
287 // Remove all parcels referenced in the remove list
288 TRemoveList::iterator irl;
289 for ( irl=_RemoveList.begin(); irl!=_RemoveList.end(); ++irl )
291 // Remove parcel
292 removeProp( *(*irl) );
294 // Remove link
295 (*irl)->Prio = DISABLE_PRIORITY;
296 (*irl)->Offset = INVALID_OFFSET;
299 // Clear the remove list
300 _RemoveList.clear();
305 * Return the number of enabled parcels in a shelf
307 TPropParcelOffset CPropertyDispatcher::nbEnabledParcels( TPriority prio )
309 nlassert( prio <= NB_PRIORITIES );
310 sint32 nb = 0;
311 TPropertyShelf::iterator ips;
312 for ( ips=_PropShelves[prio].begin(); ips!=_PropShelves[prio].end(); ++ips )
314 if ( (*ips).Enabled )
315 ++nb;
317 return (TPropParcelOffset)nb;
322 * Adjust HPThreshold (dichotomic)
324 /*void CPropertyDispatcher::adjustHPThreshold()
326 uint32 nbhpparcels = (uint32)nbEnabledParcels( HIGHEST_PRIORITY );
327 float hpoldvalue = _HPThreshold;
328 //nlinfo( "NbHP=%u NbSentParcelsInCycle=%u", (uint32)nbParcels( HIGHEST_PRIORITY ), _NbSentParcelsInCycle );
329 if ( nbhpparcels < _NbSentParcelsInCycle / 2 )
331 // Increase threshold when HIGHEST_PRIORITY can be filled more
332 if ( _HPTDelta < 0 )
334 _HPThreshold = _HPThreshold - _HPTDelta/2.0f;
335 //nlinfo( "FEPRIO: Stabilizing HPThreshold (+) to %.2f", _HPThreshold );
337 else
339 _HPThreshold *= 2.0f;
340 //nlinfo( "FEPRIO: Rising HPThreshold to %.2f, %u estimated actions", _HPThreshold, CFrontEndService::instance()->SentActionsLastCycle );
341 //printShelfSizes();
344 else
346 // Decrease threshold when HIGHEST_PRIORITY is overloaded
347 if ( nbhpparcels > _NbSentParcelsInCycle )
349 if ( _HPTDelta > 0 )
351 _HPThreshold = _HPThreshold - _HPTDelta/2.0f;
352 //nlinfo( "FEPRIO: Stabilizing HPThreshold (-) to %.2f", _HPThreshold );
354 else
356 _HPThreshold /= 2.0f; // decrease threshold if HIGHEST_PRIORITY is crowded
357 //nlinfo( "FEPRIO: Lowering HPThreshold to %.2f, %u estimated actions", _HPThreshold, CFrontEndService::instance()->SentActionsLastCycle );
358 //printShelfSizes();
363 float maxratio = CFrontEndService::instance()->PrioSub.Prioritizer.MaxRatio;
364 if ( _HPThreshold >= maxratio )
366 // Fix threshold if it is too big after being calculated in adjustHPThreshold()
367 _HPThreshold = maxratio* 0.8f;
368 _HPTDelta = 0.0f;
369 //nlinfo( "FEPRIO: Fixing HPThreshold to %.2f", _HPThreshold );
370 } //nlinfo( "FEPRIO: HPThreshold is %.2f", _HPThreshold );
371 else
373 _HPTDelta = _HPThreshold - hpoldvalue;
379 * Return the amount of priorities used corresponding to the specified number of actions (e.g. 1.2 means priority 0 is full and priority 1 is 20% filled)
381 float CPropertyDispatcher::getPrioRatio()
383 // FEATURE DISABLED (_NbSentParcelsInCycle not calculated)
384 uint32 nbactions = 0;//_NbSentParcelsInCycle;
385 //nlinfo( "NbSent: %u size0: %u", _NbSentParcelsInCycle, _PropShelves[0].size() );
386 float ratio = 0.0f;
387 uint32 amount = 0;
388 TPriority p;
389 for ( p=0; p!=NB_PRIORITIES; ++p )
391 uint32 nbenabledinshelf = nbEnabledParcels(p);
392 if ( nbactions - amount > nbenabledinshelf )
394 // Accumulate amount of full shelf
395 amount += _PropShelves[p].size();
396 ratio += 1.0f;
398 else
400 // Accumulate amount of last shelf (decimals)
401 if ( ! _PropShelves[p].empty() )
403 ratio += (float)(nbactions - amount) / (float)nbenabledinshelf;
405 //comment Ben nlinfo( "FEPRIO: %u HP of %u total => ratio %.1f last=%u", _PropShelves[0].size(), nbactions, ratio, p );
406 return ratio;
409 return ratio;
414 * Display the contents of the shelves
416 void CPropertyDispatcher::printShelves( const char *title, bool checkIntegrity, bool proptext, bool hidedisabled ) const
418 bool integrity = true;
419 //stringstream ss;
420 string str;
421 string check;
422 if ( checkIntegrity )
424 check = " checking integrity";
426 //ss << "Client: " << _ClientId << /*" HPThreshold: " << _HPThreshold <<*/ " Next parcel: ";
427 str += "Client: " + NLMISC::toString(_ClientId) + /*" HPThreshold: " << NLMISC::toString(_HPThreshold) <<*/ " Next parcel: ";
428 if ( _NextParcel.Offset == -1 )
430 //ss << "Ready to start" << endl;
431 str += "Ready to start\n";
433 else if ( _NextParcel.Offset == INVALID_OFFSET )
435 //ss << "INVALID_OFFSET (prio=" << _NextParcel.Prio << ")" << endl;
436 str += "INVALID_OFFSET (prio=" + NLMISC::toString(_NextParcel.Prio) + ")\n";
438 else
440 //ss << "prio " << _NextParcel.Prio << " offset " << _NextParcel.Offset << endl;
441 str += "prio " + NLMISC::toString(_NextParcel.Prio) + " offset " + NLMISC::toString(_NextParcel.Offset) + "\n";
443 //ss << "Property shelves " << title << check << ":" << endl;
444 str += "Property shelves " + NLMISC::toString(title) + NLMISC::toString(check) + ":\n";
445 TPriority p;
447 // Scan all shelves
448 for ( p=0; p!=NB_PRIORITIES; ++p )
450 if ( ! _PropShelves[p].empty() )
452 //ss << p << ':';
453 str += NLMISC::toString(p) + ':';
454 TPropertyShelf::const_iterator ipr;
456 // Scan all parcels
457 for ( ipr=_PropShelves[p].begin(); ipr!=_PropShelves[p].end(); ++ipr )
459 const TPropParcel& parcel = (*ipr);
460 if ( (!hidedisabled) || parcel.Enabled )
462 //ss << ' ' << parcel.CeId << ' ';
463 str += " " + NLMISC::toString(parcel.CeId) + " ";
464 if ( proptext )
465 //ss << getPropText( parcel.PropIndex );
466 str += NLMISC::toString(getPropText( parcel.PropIndex ));
467 else
468 //ss << parcel.PropIndex;
469 str += NLMISC::toString(parcel.PropIndex);
470 if ( !hidedisabled )
471 //ss << ' ' << parcel.Enabled;
472 str += " " + NLMISC::toString(parcel.Enabled);
473 //ss << " -";
474 str += " -";
477 // Check integrity (debugging feature)
478 if ( checkIntegrity )
480 TPropParcelPtr parcelptr = _VisionArray->propState( _ClientId, parcel );
481 if ( ! (parcelptr.Prio == p) &&
482 (parcelptr.Offset == TPropParcelOffset(ipr-_PropShelves[p].begin())) )
484 integrity = false;
488 //ss << endl;
489 str += "\n";
492 // ss << ends;
493 nlwarning( "FEPRIO: %s", str.c_str() );
494 if ( ! integrity )
496 nlerror( "Property Dispatcher: integrity check failed" );
502 * Display the sizes of the shelves
504 void CPropertyDispatcher::printShelfSizes() const
506 sint prio;
507 //InfoLog->display( "FEPRIO: Threshold: %.2f Parcels by prio:", _HPThreshold );
508 InfoLog->display( "FEPRIO: Parcels by prio:" );
509 for ( prio=HIGHEST_PRIORITY; prio!=NB_PRIORITIES; ++prio )
511 InfoLog->displayRaw( " %u", _PropShelves[prio].size() );
513 InfoLog->displayRawNL( "" );
518 * Count the number of clients or properties of a certain type in the property shelves
520 void CPropertyDispatcher::displayCounts( TPropIndex propindex )
522 sint prio = 0;
523 sint ofst = 0;
524 sint matching = 0;
525 sint enabled_matching = 0;
526 sint discrete_matching = 0;
527 sint self_matching = 0; // slot 0
529 while ( prio < NB_PRIORITIES )
531 for ( ofst = 0; ofst != (sint)_PropShelves[prio].size(); ++ofst )
533 bool propok = ( (propindex == 0xFF) || (propindex == _PropShelves[prio][ofst].PropIndex) );
534 if ( propok )
536 ++matching;
538 if ( _PropShelves[prio][ofst].Enabled)
540 ++enabled_matching;
542 if ( _PropShelves[prio][ofst].PropIndex > LAST_CONTINUOUS_PROPERTY )
544 ++discrete_matching;
546 if ( _PropShelves[prio][ofst].CeId == 0 )
548 ++self_matching;
552 ++prio;
554 if ( propindex == 0xFF )
555 nlinfo( "FEPRIO: Searching for all property indexes" );
556 else
557 // warning: mixing property/propindex (TEMP)
558 nlinfo( "FEPRIO: Searching for propindex %hu (%s)", (uint16)propindex, getPropText(propindex) );
560 nlinfo( "FEPRIO: Found %d matching properties, including %d enabled, %d discrete, %d self-vision", matching, enabled_matching, discrete_matching, self_matching );
565 NLMISC_COMMAND( displayPropShelves, "Display the shelves of the property dispatcher", "[<clientid>|0] [<hideDisabled>0/1] [<propText>1/0] [<checkIntegrity>0/1]" )
567 // Parse arguments
568 TClientId clientid = 0;
569 bool hidedisabled = false;
570 bool check = false;
571 bool proptext = true;
572 if ( args.size() > 0 )
574 clientid = atoi(args[0].c_str());
575 if ( args.size() > 1 )
577 hidedisabled = (atoi(args[1].c_str()) != 0);
578 if ( args.size() > 2 )
580 check = (atoi(args[2].c_str()) != 0);
581 if ( args.size() > 3 )
583 proptext = (atoi(args[3].c_str()) != 0);
589 // Display
590 THostMap& clientmap = CFrontEndService::instance()->instance()->receiveSub()->clientMap();
591 THostMap::iterator icm;
592 for ( icm=clientmap.begin(); icm!=clientmap.end(); ++icm )
594 if ( (clientid == 0) || (clientid == GETCLIENTA(icm)->clientId()) )
596 GETCLIENTA(icm)->PropDispatcher.printShelves( "command", check, proptext, hidedisabled, &log );
599 return true;
603 NLMISC_COMMAND( displayPropShelfSizes, "Display the sizes of the shelves of the property dispatcher", "[<clientid>|0]" )
605 // Parse arguments
606 TClientId clientid = 0;
607 if ( args.size() > 0 )
609 clientid = atoi(args[0].c_str());
612 // Display
613 THostMap& clientmap = CFrontEndService::instance()->instance()->receiveSub()->clientMap();
614 THostMap::iterator icm;
615 for ( icm=clientmap.begin(); icm!=clientmap.end(); ++icm )
617 if ( (clientid == 0) || (clientid == GETCLIENTA(icm)->clientId()) )
619 GETCLIENTA(icm)->PropDispatcher.printShelfSizes(&log);
622 return true;
626 NLMISC_COMMAND( countClientsOrPropertiesInShelves, "Count the number of clients or properties of a certain type in the property shelves", "[<clientid|0> [<propindex>]]" )
628 // Parse arguments
629 TClientId clientid = 0;
630 TPropIndex propindex = 0xFF;
631 if ( args.size() > 0 )
633 clientid = atoi(args[0].c_str());
634 if ( args.size() > 1 )
636 propindex = atoi(args[1].c_str());
640 if ( clientid == 0 )
641 log.displayNL( "FEPRIO: Searching for all clients" );
642 else
643 log.displayNL( "FEPRIO: Searching for client %hu", clientid );
645 // Count
646 THostMap& clientmap = CFrontEndService::instance()->instance()->receiveSub()->clientMap();
647 THostMap::iterator icm;
648 for ( icm=clientmap.begin(); icm!=clientmap.end(); ++icm )
650 if ( (clientid == 0) || (clientid == GETCLIENTA(icm)->clientId()) )
652 GETCLIENTA(icm)->PropDispatcher.displayCounts( propindex, &log );
655 return true;