1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
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.
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/>.
22 #include "property_dispatcher.h"
23 #include "vision_array.h"
24 #include "property_id_translator.h"
25 #include "frontend_service.h"
30 using namespace NLMISC
;
31 using namespace CLFECOMMON
;
37 CPropertyDispatcher::CPropertyDispatcher() :
39 _PropTranslator( NULL
),
43 //_NbSentParcelsInCycle( 0 )
47 _NextParcel
.Prio
= DISABLE_PRIORITY
;
48 _NextParcel
.Offset
= INVALID_OFFSET
;
55 void CPropertyDispatcher::init( TClientId 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
);
77 if ( parcelptr
.Offset
< shelf
.size() )
79 TPropParcel
& parcel
= getParcel( shelf
, parcelptr
.Offset
);
82 TPropParcel
& lastone
= lastParcel( shelf
);
83 if ( &parcel
!= &lastone
)
86 _VisionArray
->updateLink( _ClientId
, parcel
, parcelptr
.Offset
);
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
)
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
)
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 )
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
160 // Display debug info
161 //printShelves( "after initDispatcherCycle", false );
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
)
184 if ( _NextParcel
.Prio
< LAST_PRIO_SENT
)
186 // "Carriage return" to the next shelf
188 _NextParcel
.Offset
= 0;
190 // Skip any empty shelves
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
) )
213 // Detect end of data
214 if ( _PropShelves
[_NextParcel
.Prio
].empty() )
216 _NextParcel
.Offset
= INVALID_OFFSET
;
220 // Check if the current parcel is enabled, otherwise advance to next one
221 if ( ! getParcel( _NextParcel
).Enabled
)
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;
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
;
272 //nlinfo( "FESEND: Sent discrete property %hu to client %hu slot %hu", propertyid, _ClientId, (uint16)parcel.CeId );
276 // Advance to next parcel
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
)
292 removeProp( *(*irl
) );
295 (*irl
)->Prio
= DISABLE_PRIORITY
;
296 (*irl
)->Offset
= INVALID_OFFSET
;
299 // Clear the remove list
305 * Return the number of enabled parcels in a shelf
307 TPropParcelOffset
CPropertyDispatcher::nbEnabledParcels( TPriority prio
)
309 nlassert( prio
<= NB_PRIORITIES
);
311 TPropertyShelf::iterator ips
;
312 for ( ips
=_PropShelves
[prio
].begin(); ips
!=_PropShelves
[prio
].end(); ++ips
)
314 if ( (*ips
).Enabled
)
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
334 _HPThreshold = _HPThreshold - _HPTDelta/2.0f;
335 //nlinfo( "FEPRIO: Stabilizing HPThreshold (+) to %.2f", _HPThreshold );
339 _HPThreshold *= 2.0f;
340 //nlinfo( "FEPRIO: Rising HPThreshold to %.2f, %u estimated actions", _HPThreshold, CFrontEndService::instance()->SentActionsLastCycle );
346 // Decrease threshold when HIGHEST_PRIORITY is overloaded
347 if ( nbhpparcels > _NbSentParcelsInCycle )
351 _HPThreshold = _HPThreshold - _HPTDelta/2.0f;
352 //nlinfo( "FEPRIO: Stabilizing HPThreshold (-) to %.2f", _HPThreshold );
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 );
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;
369 //nlinfo( "FEPRIO: Fixing HPThreshold to %.2f", _HPThreshold );
370 } //nlinfo( "FEPRIO: HPThreshold is %.2f", _HPThreshold );
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() );
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();
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 );
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;
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";
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";
448 for ( p
=0; p
!=NB_PRIORITIES
; ++p
)
450 if ( ! _PropShelves
[p
].empty() )
453 str
+= NLMISC::toString(p
) + ':';
454 TPropertyShelf::const_iterator ipr
;
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
) + " ";
465 //ss << getPropText( parcel.PropIndex );
466 str
+= NLMISC::toString(getPropText( parcel
.PropIndex
));
468 //ss << parcel.PropIndex;
469 str
+= NLMISC::toString(parcel
.PropIndex
);
471 //ss << ' ' << parcel.Enabled;
472 str
+= " " + NLMISC::toString(parcel
.Enabled
);
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())) )
493 nlwarning( "FEPRIO: %s", str
.c_str() );
496 nlerror( "Property Dispatcher: integrity check failed" );
502 * Display the sizes of the shelves
504 void CPropertyDispatcher::printShelfSizes() const
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
)
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
) );
538 if ( _PropShelves
[prio
][ofst
].Enabled
)
542 if ( _PropShelves
[prio
][ofst
].PropIndex
> LAST_CONTINUOUS_PROPERTY
)
546 if ( _PropShelves
[prio
][ofst
].CeId
== 0 )
554 if ( propindex
== 0xFF )
555 nlinfo( "FEPRIO: Searching for all property indexes" );
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]" )
568 TClientId clientid
= 0;
569 bool hidedisabled
= 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);
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
);
603 NLMISC_COMMAND( displayPropShelfSizes
, "Display the sizes of the shelves of the property dispatcher", "[<clientid>|0]" )
606 TClientId clientid
= 0;
607 if ( args
.size() > 0 )
609 clientid
= atoi(args
[0].c_str());
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
);
626 NLMISC_COMMAND( countClientsOrPropertiesInShelves
, "Count the number of clients or properties of a certain type in the property shelves", "[<clientid|0> [<propindex>]]" )
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());
641 log
.displayNL( "FEPRIO: Searching for all clients" );
643 log
.displayNL( "FEPRIO: Searching for client %hu", clientid
);
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
);