Resolve "Toggle Free Look with Hotkey"
[ryzomcore.git] / ryzom / server / src / frontend_service / prioritizer.cpp
blob6d0b8f2031ae690b128ca4334dd4be29284fcc62
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 "prioritizer.h"
22 #include "vision_array.h"
23 #include "property_dispatcher.h"
24 #include "property_id_translator.h"
25 #include "history.h"
26 #include "frontend_service.h"
27 #include "fe_stat.h"
29 using namespace CLFECOMMON;
33 /* Threshold for Distance/Delta.
34 * Ex: RATIO_MAX_THRESHOLD = 2
36 * Delta
37 * |--------|--------|
38 * D|L.......H......H/
39 * i| /
40 * s| /
41 * t| /
42 * a| /
43 * n| /
44 * c|45 /
45 * e| /
46 * |/
47 * o
48 * Observer
50 * If Delta exceeds Distance/2, the priority shelf (bucket) is the highest one (H)
51 * Else the shelves cover linearly the length between 0 and Distance/2.
53 * Note: the highest priority is 0, the lowest is NB_PRIORITIES-1.
56 const sint RATIO_MAX_THRESHOLD = 2;
59 // Deprecated
61 const TCoord CPrioritizer::DeltaMinThreshold = 200; // 20 cm (all smaller deltas will trigger min priority)
62 const TCoord CPrioritizer::DeltaMaxThreshold = 12000; // 12 m (all higher deltas will trigger max priority)
63 const TCoord CPrioritizer::DistMinThreshold = 500; // 50 cm (all nearer distances will trigger max priority if moving)
64 const TCoord CPrioritizer::DistMaxThreshold = 600000; // 600 m (all further distances will trigger min priority)
67 const TCoord CPrioritizer::MaxDelta = 2000000; // about 2000 m
71 * Constructor
73 CPrioritizer::CPrioritizer() :
74 _PrioStrategy( DistanceDelta ),
75 _VisionArray( NULL ),
76 _PropTranslator( NULL ),
77 _History( NULL )
83 * Initialization
85 void CPrioritizer::init( CVisionArray *va,
86 CPropertyIdTranslator* pt, CHistory *h )
88 _VisionArray = va;
89 _PropTranslator = pt;
90 _History = h;
92 PositionSpreader.init();
93 OrientationSpreader.init();
94 DiscreetSpreader.init();
99 * Set the priority strategy
101 /*void CPrioritizer::setPrioStrategy( TPrioStrategy ps )
103 _PrioStrategy = ps;
104 switch( _PrioStrategy )
106 case DistanceOnly:
107 MaxRatio = (float)DistMaxThreshold;
108 break;
109 case DistanceDelta:
110 MaxRatio = DistMaxThreshold / DeltaMinThreshold; // distance/delta, const max
111 break;
112 default:
113 nlwarning( "Invalid priority strategy set, resetting to DistanceDelta" );
114 setPrioStrategy( DistanceDelta );
120 * Calculate the priorities for the current cycle
122 void CPrioritizer::calculatePriorities()
124 /*switch( _PrioStrategy )
126 case DistanceOnly:
127 calculatePrioDistance();
128 break;
129 case DistanceDelta:*/
131 calculatePriorityOfPosition();
132 calculatePriorityOfOrientation();
133 calculatePriorityOfDiscreet();
135 /* break;
136 default:
137 nlstop;
144 * Calculate using strategy DistanceOnly
146 /*void CPrioritizer::calculatePrioDistance()
149 // Process the pairs selected by the Pair Selector
151 uint32 uprio; // using uint32 because uint8 can be too short in some cases
153 const TPairCE *pairCE;
154 while ( (pairCE = _PairSelector->selectNextPair()) != NULL )
156 CVisionArray::TVAItem& item = _VisionArray->vaItem( pairCE->ClientId, pairCE->CeId );
158 // Discard if client has left
159 if ( item.DistanceCE != DISCARD_PAIR )
161 // For each property corresponding to the entity type
162 TPropIndex p;
163 for ( p=0; p<_PropTranslator->nbProperties( item.EntityIndex ); ++p )
165 // Calculate priority only if the update status is ToUpdate,
166 // meaning that a change has not been sent to the client (especially
167 // for a discreet property ; for a continuous property the status
168 // is always ToUpdate)
170 if ( item.Properties[p].UpdateStatus == ToUpdate )
172 CClientHost *clienthost = CFrontEndService::instance()->sendSub()->clientIdCont()[pairCE->ClientId];
173 //nlassertex( clienthost, ("c%hu", clientid) );
174 float hpthreshold = clienthost->PropDispatcher.hpThreshold();
176 // Clamp distance so that very near entities get the highest priority
177 if ( (float)(item.DistanceCE) < hpthreshold )
179 uprio = HIGHEST_PRIORITY;
181 else
183 // Linear priority, excluding HIGHEST_PRIORITY
184 nlassert( MaxRatio > hpthreshold );
185 uprio = (uint32)((((float)item.DistanceCE-hpthreshold) * (float)(NB_PRIORITIES-2) / (MaxRatio-hpthreshold)) + 1.0f);
186 nlassert( uprio > HIGHEST_PRIORITY );
188 // Clamp priority
189 if ( uprio >= NB_PRIORITIES )
191 uprio = NB_PRIORITIES - 1;
195 // Set the priority of the pair
196 clienthost->PropDispatcher.setPriority( pairCE->CeId, p, _VisionArray->prioLoc( pairCE->ClientId, pairCE->CeId, p ), (TPriority)uprio );
209 * Calculate position priorities using strategy DistanceDelta
211 void CPrioritizer::calculatePriorityOfPosition()
213 if ( PositionSpreader.mustProcessNow() )
216 #ifdef MEASURE_FRONTEND_TABLES
217 DistCntFrame.setGameTick();
218 DeltaCntFrame.setGameTick();
219 PrioCntFrame.setGameTick();
220 DistCntFrame.reset( -1 );
221 DeltaCntFrame.reset( -1 );
222 PrioCntFrame.reset( -1 );
223 #endif
225 THostMap::iterator icm;
226 sint clientmapindex, outerBoundIndex;
227 PositionSpreader.getProcessingBounds( icm, clientmapindex, outerBoundIndex );
228 sint bucket;
230 while ( clientmapindex < outerBoundIndex )
232 CClientHost *clienthost = GETCLIENTA(icm);
233 TVAProp tvaPos = _VisionArray->tvaProp( clienthost->clientId(), PROPERTY_POSITION );
235 for ( sint e=0; e!=MAX_SEEN_ENTITIES_PER_CLIENT; ++e )
237 // Calculate priority only if the update status is ToUpdate (even for a continuous prop)
238 if ( tvaPos[e].PropState.UpdateStatus == ToUpdate )
240 TEntityIndex entityIndex = _VisionArray->getEntityIndex( clienthost->clientId(), (TCLEntityId)e );
242 if ( entityIndex.isValid() )
245 // Get delta
246 TCoord delta = getDeltaPos( entityIndex, clienthost->clientId(), e );
248 #ifdef MEASURE_FRONTEND_TABLES
249 if ( clienthost->clientId() == 1 )
251 DistCntFrame.SeenEntities[e] = distanceCE;
252 DeltaCntFrame.SeenEntities[e] = delta;
254 #endif
256 // Do not set priority if delta is zero (steady entities...)
257 if ( delta != 0 )
259 TCoord distanceCE = _VisionArray->distanceCE( clienthost->clientId(), (TCLEntityId)e );
260 float prioRatio = (float)distanceCE / (float)delta;
262 // See explanation of formula on top of this file
263 if ( prioRatio < RATIO_MAX_THRESHOLD )
265 bucket = HIGHEST_PRIORITY;
267 else
269 bucket = LOWEST_PRIORITY - (sint)((float)(LOWEST_PRIORITY*RATIO_MAX_THRESHOLD) / prioRatio);
272 #ifdef MEASURE_FRONTEND_TABLES
273 if ( clienthost->clientId() == 1 )
275 PrioCntFrame.SeenEntities[e] = bucket;
277 #endif
279 // Set the priority of the pair
280 clienthost->PropDispatcher.setPriority( e, PROPERTY_POSITION, tvaPos[e].PropState, (TPriority)bucket );
282 /*nlinfo( "FEPRIO: Set cep %hu-%hu-%hu to priority %u",
283 (uint16)pairCE->ClientId, (uint16)pairCE->CeId, (uint16)p, uprio );*/
289 ++clientmapindex;
290 ++icm;
293 PositionSpreader.endProcessing( icm );
295 #ifdef MEASURE_FRONTEND_TABLES
296 DistCntFrame.commit( DistCntClt1 );
297 DeltaCntFrame.commit( DeltaCntClt1 );
298 PrioCntFrame.commit( PrioCntClt1 );
299 #endif
303 PositionSpreader.incCycle();
308 * Calculate orientation priorities using strategy DistanceDelta
310 void CPrioritizer::calculatePriorityOfOrientation()
312 if ( OrientationSpreader.mustProcessNow() )
314 THostMap::iterator icm;
315 sint clientmapindex, outerBoundIndex;
316 OrientationSpreader.getProcessingBounds( icm, clientmapindex, outerBoundIndex );
317 sint bucket;
319 while ( clientmapindex < outerBoundIndex )
321 CClientHost *clienthost = GETCLIENTA(icm);
322 TVAProp tvaOrt = _VisionArray->tvaProp( clienthost->clientId(), PROPERTY_ORIENTATION );
324 for ( sint e=0; e!=MAX_SEEN_ENTITIES_PER_CLIENT; ++e )
326 // Calculate priority only if the update status is ToUpdate (even for a continuous prop)
327 if ( tvaOrt[e].PropState.UpdateStatus == ToUpdate )
329 TEntityIndex entityIndex = _VisionArray->getEntityIndex( clienthost->clientId(), (TCLEntityId)e );
331 if ( entityIndex.isValid() )
334 // Get delta
335 TCoord delta = getDeltaOrientation( entityIndex, clienthost->clientId(), e );
337 // Do not set priority if delta is zero (steady entities...)
338 if ( delta != 0 )
340 TCoord distanceCE = _VisionArray->distanceCE( clienthost->clientId(), (TCLEntityId)e );
341 float prioRatio = (float)distanceCE / (float)delta;
343 // See explanation of formula on top of this file
344 if ( prioRatio < RATIO_MAX_THRESHOLD )
346 bucket = HIGHEST_PRIORITY;
348 else
350 bucket = LOWEST_PRIORITY - (sint)((float)(LOWEST_PRIORITY*RATIO_MAX_THRESHOLD) / prioRatio);
353 // Set the priority of the pair
354 clienthost->PropDispatcher.setPriority( e, PROPERTY_ORIENTATION, tvaOrt[e].PropState, (TPriority)bucket );
356 /*nlinfo( "FEPRIO: Set cep %hu-%hu-%hu to priority %u",
357 (uint16)pairCE->ClientId, (uint16)pairCE->CeId, (uint16)p, uprio );*/
363 ++clientmapindex;
364 ++icm;
367 OrientationSpreader.endProcessing( icm );
370 OrientationSpreader.incCycle();
375 * Calculate discreet props priorities using distance threshold
377 void CPrioritizer::calculatePriorityOfDiscreet()
379 if ( DiscreetSpreader.mustProcessNow() )
381 THostMap::iterator icm;
382 sint clientmapindex, outerBoundIndex;
383 DiscreetSpreader.getProcessingBounds( icm, clientmapindex, outerBoundIndex );
384 sint bucket;
386 while ( clientmapindex < outerBoundIndex )
388 CClientHost *clienthost = GETCLIENTA(icm);
390 for ( sint e=0; e!=MAX_SEEN_ENTITIES_PER_CLIENT; ++e )
392 TEntityIndex entityIndex = _VisionArray->getEntityIndex( clienthost->clientId(), (TCLEntityId)e );
393 if ( entityIndex.isValid() )
395 TCoord distanceCE = _VisionArray->distanceCE( clienthost->clientId(), (TCLEntityId)e );
397 // For each discreet property corresponding to the entity type
398 for ( sint p=FIRST_DISCREET_PROPINDEX; p<_PropTranslator->nbProperties( entityIndex ); ++p )
400 // Calculate priority only if the update status is ToUpdate,
401 // meaning that a change has not been sent to the client
403 if ( _VisionArray->tvaProp( clienthost->clientId(), p )[e].PropState.UpdateStatus == ToUpdate )
405 // Get TProperty from TPropIndex
406 TProperty propertyid = _PropTranslator->getPropertyId( entityIndex, p );
408 if ( distanceCE < _PropTranslator->getDistThreshold( propertyid ) )
410 bucket = HIGHEST_PRIORITY;
412 else
414 bucket = LOWEST_PRIORITY;
417 // Set the priority of the pair
418 clienthost->PropDispatcher.setPriority( e, p, _VisionArray->prioLoc( clienthost->clientId(), e, p ), (TPriority)bucket );
420 //nlinfo( "FEPRIO: Set cep %hu-%hu-%hu to priority %u",
421 // (uint16)pairCE->ClientId, (uint16)pairCE->CeId, (uint16)p, uprio );
427 ++clientmapindex;
428 ++icm;
431 DiscreetSpreader.endProcessing( icm );
434 DiscreetSpreader.incCycle();
439 bool TraceDelta = true;
443 * Return the delta corresponding to a property
445 inline TCoord CPrioritizer::getDeltaPos( TEntityIndex entityindex, TClientId clientid, TCLEntityId ceid )
447 // Position special case
448 uint32 lastSentMileage = _History->getMileage( clientid, ceid );
450 if ( lastSentMileage != 0 )
452 CFrontEndPropertyReceiver::SEntity *entity = CFrontEndPropertyReceiver::getEntity( entityindex );
454 // Calculate difference distance between current and lastsent mileage (unsigned to allow mileage overflow)
455 uint32 d = (entity->Mileage - lastSentMileage);//(uint32)frand(100000.0);
457 // Ignore not significant deltas (maybe not needed anymore with integer calculation)
458 if ( d < 5 )
460 //nlinfo( "Low position delta = %u", d );
461 return 0;
463 else
465 /*if ( TraceDelta )
466 nlinfo( "%d", d );*/
467 return (TCoord)d;
470 else
472 // Not sent yet, set max delta
473 //nlinfo( "FEPRIO: getDelta(position) : first time" );
474 return MaxDelta;
476 //nlinfo( "Delta: %.2f (current: %s last: %s)", delta, current_entitypos.asString().c_str(), lastsent_entitypos.asString().c_str() );
481 inline TCoord CPrioritizer::getDeltaOrientation( TEntityIndex entityindex, TClientId clientid, TCLEntityId ceid )
483 bool histohasvalue;
484 const CFrontEndPropertyReceiver::TPropertiesValue current_value = CFrontEndPropertyReceiver::getEntity( entityindex )->properties[PROPERTY_ORIENTATION];
485 const CFrontEndPropertyReceiver::TPropertiesValue& lastsent_value = _History->getPropertyEntry( clientid, ceid, PROPERTY_ORIENTATION, histohasvalue ).LastSent;
486 if ( histohasvalue )
488 // Orientation is a float angle in radian
489 const float& newangle = *((float*)&current_value);//frand(6.28);
490 const float& oldangle = *((float*)&lastsent_value);
491 float deltaAngle = (float)fabs( (float)(newangle - oldangle) );
492 deltaAngle = (float)fmod( deltaAngle+(2*Pi), (2*Pi) );
494 // Delta=1 m corresponds to Pi
495 //nlinfo( "getDelta(theta) : dA=%g", deltaAngle );
496 if ( deltaAngle > Pi )
497 return (TCoord)(2000.0f - (deltaAngle * 1000.0f / Pi));
498 else
499 return (TCoord)(deltaAngle * 1000.0f / Pi);
501 else
503 // Not sent yet, set max delta
504 return MaxDelta;
509 NLMISC_COMMAND( forcePriority, "Force a priority", "<clientid> <slot> <propindex> <priority>" )
511 // check args, if there s not the right number of parameter, return bad
512 if(args.size() != 4) return false;
514 // get the values
515 TClientId clientid = atoi(args[0].c_str());
516 sint slot = atoi(args[1].c_str());
517 TPropIndex propindex = atoi(args[2].c_str());
518 TPriority priority = atoi(args[3].c_str());
520 if ( (clientid <= MaxNbClients) && (slot < MAX_SEEN_ENTITIES_PER_CLIENT) && (propindex < NB_VISUAL_PROPERTIES/*16*/) && ( priority < NB_PRIORITIES ) )
522 CClientHost *clienthost = CFrontEndService::instance()->sendSub()->clientIdCont()[clientid];
523 //nlassertex( clienthost, ("c%hu", clientid) );
524 clienthost->PropDispatcher.setPriority( slot, propindex, CFrontEndService::instance()->PrioSub.VisionArray.prioLoc( clientid, slot, propindex ), priority );
526 else
528 log.displayNL( "Invalid argument value(s)" );
530 return true;
534 /*NLMISC_COMMAND( setPriorityStrategy, "Change the priority strategy, distance-delta or distance only", "<strategy>1/0" )
536 // check args, if there s not the right number of parameter, return bad
537 if(args.size() != 1) return false;
539 // get the values
540 sint strat = atoi(args[0].c_str());
542 switch( strat )
544 case 0: CFrontEndService::instance()->PrioSub.Prioritizer.setPrioStrategy( CPrioritizer::DistanceOnly ); break;
545 case 1: CFrontEndService::instance()->PrioSub.Prioritizer.setPrioStrategy( CPrioritizer::DistanceDelta ); break;
546 default: return false;
549 THostMap::iterator ihm;
550 for ( ihm=CFrontEndService::instance()->receiveSub()->clientMap().begin(); ihm!=CFrontEndService::instance()->receiveSub()->clientMap().end(); ++ihm )
552 GETCLIENTA(ihm)->PropDispatcher.resetThreshold();
554 return true;
558 NLMISC_COMMAND( getPriorityStrategy, "Get the priority strategy", "" )
560 switch( CFrontEndService::instance()->PrioSub.Prioritizer.prioStrategy() )
562 case CPrioritizer::DistanceOnly: log.displayNL( "Distance only" ); break;
563 case CPrioritizer::DistanceDelta: log.displayNL( "Distance-delta" ); break;
565 return true;
569 NLMISC_COMMAND( getPriorityThresholds, "Get the threshold constants", "" )
571 log.displayNL( "MinDelta=%d MaxDelta=%d MinDist=%d MaxDist=%d",
572 CPrioritizer::DeltaMinThreshold, CPrioritizer::DeltaMaxThreshold,
573 CPrioritizer::DistMinThreshold, CPrioritizer::DistMaxThreshold );
574 return true;
578 NLMISC_COMMAND( getDelta, "Get the delta for a priority", "<client> <slot> <propindex>" )
580 // check args, if there s not the right number of parameter, return bad
581 if(args.size() != 3) return false;
583 // get the values
584 TClientId clientid = atoi(args[0].c_str());
585 sint slot = atoi(args[1].c_str());
586 TPropIndex propindex = atoi(args[2].c_str());
588 if ( (clientid <= MaxNbClients) && (CFrontEndService::instance()->sendSub()->clientIdCont()[clientid] != NULL) )
590 if ( slot < MAX_SEEN_ENTITIES_PER_CLIENT )
592 TEntityIndex entityindex = CFrontEndService::instance()->PrioSub.VisionArray.getEntityIndex( clientid, (TCLEntityId)slot );
593 if ( propindex > 15 )
594 return false;
596 TProperty propertyid = CFrontEndService::instance()->PrioSub.PropTranslator.getPropertyId( entityindex, propindex );
598 if ( propertyid == PROPERTY_POSITION )
599 log.displayNL( "%d", CFrontEndService::instance()->PrioSub.Prioritizer.getDeltaPos( entityindex, clientid, (TCLEntityId)slot ) );
600 else if ( propertyid == PROPERTY_ORIENTATION )
601 log.displayNL( "%d", CFrontEndService::instance()->PrioSub.Prioritizer.getDeltaOrientation( entityindex, clientid, (TCLEntityId)slot ) );
602 else
603 log.displayNL( "Invalid property id" );
605 else
607 log.displayNL( "Invalid slot" );
610 else
612 log.displayNL( "There is no such a client id" );
614 return true;
618 /*NLMISC_COMMAND( delta, "Trace delta", "0/1" )
620 if ( args.size() == 0 )
621 return false;
623 TraceDelta = ( atoi(args[0].c_str()) == 1 );
624 return true;