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/>.
21 #include "prioritizer.h"
22 #include "vision_array.h"
23 #include "property_dispatcher.h"
24 #include "property_id_translator.h"
26 #include "frontend_service.h"
29 using namespace CLFECOMMON
;
33 /* Threshold for Distance/Delta.
34 * Ex: RATIO_MAX_THRESHOLD = 2
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;
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
73 CPrioritizer::CPrioritizer() :
74 _PrioStrategy( DistanceDelta
),
76 _PropTranslator( NULL
),
85 void CPrioritizer::init( CVisionArray
*va
,
86 CPropertyIdTranslator
* pt
, CHistory
*h
)
92 PositionSpreader
.init();
93 OrientationSpreader
.init();
94 DiscreetSpreader
.init();
99 * Set the priority strategy
101 /*void CPrioritizer::setPrioStrategy( TPrioStrategy ps )
104 switch( _PrioStrategy )
107 MaxRatio = (float)DistMaxThreshold;
110 MaxRatio = DistMaxThreshold / DeltaMinThreshold; // distance/delta, const max
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 )
127 calculatePrioDistance();
129 case DistanceDelta:*/
131 calculatePriorityOfPosition();
132 calculatePriorityOfOrientation();
133 calculatePriorityOfDiscreet();
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
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;
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 );
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 );
225 THostMap::iterator icm
;
226 sint clientmapindex
, outerBoundIndex
;
227 PositionSpreader
.getProcessingBounds( icm
, clientmapindex
, outerBoundIndex
);
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() )
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
;
256 // Do not set priority if delta is zero (steady entities...)
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
;
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
;
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 );*/
293 PositionSpreader
.endProcessing( icm
);
295 #ifdef MEASURE_FRONTEND_TABLES
296 DistCntFrame
.commit( DistCntClt1
);
297 DeltaCntFrame
.commit( DeltaCntClt1
);
298 PrioCntFrame
.commit( PrioCntClt1
);
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
);
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() )
335 TCoord delta
= getDeltaOrientation( entityIndex
, clienthost
->clientId(), e
);
337 // Do not set priority if delta is zero (steady entities...)
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
;
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 );*/
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
);
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
;
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 );
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)
460 //nlinfo( "Low position delta = %u", d );
472 // Not sent yet, set max delta
473 //nlinfo( "FEPRIO: getDelta(position) : first time" );
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
)
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
;
488 // Orientation is a float angle in radian
489 const float& newangle
= *((float*)¤t_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
));
499 return (TCoord
)(deltaAngle
* 1000.0f
/ Pi
);
503 // Not sent yet, set max delta
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;
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
);
528 log
.displayNL( "Invalid argument value(s)" );
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;
540 sint strat = atoi(args[0].c_str());
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();
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;
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 );
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;
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 )
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
) );
603 log
.displayNL( "Invalid property id" );
607 log
.displayNL( "Invalid slot" );
612 log
.displayNL( "There is no such a client id" );
618 /*NLMISC_COMMAND( delta, "Trace delta", "0/1" )
620 if ( args.size() == 0 )
623 TraceDelta = ( atoi(args[0].c_str()) == 1 );