Add infos into target window
[ryzomcore.git] / ryzom / server / src / frontend_service / distance_prioritizer.cpp
blobdd2597329d681752b28d28a633a5791a1ca4ec68
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2014 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "stdpch.h"
23 #include "game_share/action_factory.h"
24 #include "game_share/action_position.h"
25 #include "game_share/action_sint64.h"
26 #include "game_share/mode_and_behaviour.h"
27 #include "game_share/entity_types.h"
28 #include "distance_prioritizer.h"
29 #include "client_host.h"
30 #include "vision_provider.h"
32 #include <nel/misc/command.h>
33 #include "frontend_service.h"
34 #ifdef TEST_LOST_PACKET
35 #include <nel/misc/variable.h>
36 #endif
38 using namespace std;
39 using namespace CLFECOMMON;
40 using namespace NLMISC;
41 using namespace NLNET;
44 // Test feature to simulate a lost packet when sending a new sheet. Works with 1 client only.
45 #ifdef TEST_LOST_PACKET
46 CVariable<bool> TestPacketLost( "test", "TestPacketLost", "", true, 0, 1 );
47 TGameCycle TestPacketLostTimer = 0;
48 TCLEntityId TestPacketLostSlot = 0;
49 #endif
51 TClientId verbosePropertiesSent = INVALID_CLIENT;
53 // TODO-Minimal visual bandwith
54 /*sint32 NbMinimalVisualBits;
56 void cbNbMinimalVisualBytesChanged( IVariable& var );
58 CVariable<sint32> NbMinimalVisualBytes( "fe", "NbMinimalVisualBytes", "Min number of bytes for vision per msg", 50, 0, true, cbNbMinimalVisualBytesChanged, true );
60 void cbNbMinimalVisualBytesChanged( IVariable& var )
62 NbMinimalVisualBits = NbMinimalVisualBytes.get() * 8;
63 }*/
66 /// Init
67 void CDistancePrioritizer::init( CVisionArray *va, CVisionProvider *vp, CHistory *hy )
69 _VisionArray = va;
70 _VisionProvider = vp;
71 _History = hy;
72 SortSpreader.init();
74 for ( sint i=0; i!=NB_VISUAL_PROPERTIES; ++i )
75 _DistThresholdTable[i] = -1;
77 _VisualPropertyTreeRoot = (TVPNodeServer*)NewNode();
78 sint nbProp = _VisualPropertyTreeRoot->buildTree();
80 // Check that the last property matches the number of properties-1
81 if ( nbProp != NB_VISUAL_PROPERTIES )
83 nlwarning( "Found %hu properties in the sheets, the prioritizer knows %hu properties", NB_VISUAL_PROPERTIES, nbProp );
86 // Init distance thresholds
87 CLFECOMMON::initThresholdTable( _DistThresholdTable );
92 * Calculate the priorities
94 void CDistancePrioritizer::calculatePriorities()
96 // Sort
97 if ( SortSpreader.mustProcessNow() )
99 THostMap::iterator icm;
100 sint clientmapindex, outerBoundIndex;
101 SortSpreader.getProcessingBounds( icm, clientmapindex, outerBoundIndex );
103 while ( clientmapindex < outerBoundIndex )
105 CClientHost *clienthost = GETCLIENTA(icm);
107 // Prioritize only at the opposite time of sending for a particular client
108 if ( ! clienthost->whenToSend() )
110 // Update priorities
111 updatePriorityOfEntitiesSeenByClient( clienthost->clientId() );
113 // Sort entities by decreasing priority
114 sortEntitiesOfClient( clienthost->clientId() );
117 ++clientmapindex;
118 ++icm;
121 SortSpreader.endProcessing( icm );
123 SortSpreader.incCycle();
127 #define arbitrateDiscreetProperty( name ) \
128 get##name##node()->BranchHasPayload = entityIsWithinDistanceThreshold( PROPERTY_##name ) && discreetPropertyHasChanged( PROPERTY_##name, (TYPE_##name*)NULL )
130 #define arbitrateDiscreetPropertyWithoutThreshold( name ) \
131 get##name##node()->BranchHasPayload = discreetPropertyHasChanged( PROPERTY_##name, (TYPE_##name*)NULL )
133 #define arbitrateTargetList( name ) \
134 get##name##node()->BranchHasPayload = targetListHasChanged( PROPERTY_##name, (TYPE_##name*)NULL )
137 #ifdef STORE_MIRROR_VP_IN_CLASS
139 #define arbitrateDiscreetProperty( entry, name ) \
140 GET_VP_NODE(name)->BranchHasPayload = \
141 entityIsWithinDistanceThreshold( PROPERTY_##name ) \
142 && discreetPropertyHasChanged( entry.Properties[PROPERTY_##name], sentity->VP_##name, PROPERTY_##name, (TYPE_##name*)NULL )
144 #define arbitrateDiscreetPropertyWithoutThreshold( entry, name ) \
145 GET_VP_NODE(name)->BranchHasPayload = \
146 discreetPropertyHasChanged( entry.Properties[PROPERTY_##name], sentity->VP_##name, PROPERTY_##name, (TYPE_##name*)NULL )
148 #else // STORE_MIRROR_VP_IN_CLASS
150 #define arbitrateDiscreetProperty( entry, name ) \
151 GET_VP_NODE(name)->BranchHasPayload = \
152 entityIsWithinDistanceThreshold( PROPERTY_##name ) \
153 && discreetPropertyHasChanged( entry.Properties[PROPERTY_##name], PROPERTY_##name, (TYPE_##name*)NULL )
155 #define arbitrateDiscreetPropertyWithoutThreshold( entry, name ) \
156 GET_VP_NODE(name)->BranchHasPayload = \
157 discreetPropertyHasChanged( entry.Properties[PROPERTY_##name], PROPERTY_##name, (TYPE_##name*)NULL )
159 #define arbitrateTargetList( entry, name ) \
161 #endif // STORE_MIRROR_VP_IN_CLASS
164 #define arbitrateNeverSendProperty( name ) \
165 GET_VP_NODE(name)->BranchHasPayload = false
167 #define arbitrateTargetList( entry, name ) \
168 GET_VP_NODE(name)->BranchHasPayload = \
169 targetListHasChanged( entry.Properties[PROPERTY_##name], PROPERTY_##name, (TYPE_##name*)NULL )
172 #define DECLARE_AP(name) CActionSint64 *ap = CActionFactory::getInstance()->getVolatilePropAction( TVPNodeServer::PrioContext.Slot, PROPERTY_##name )
173 #define DECLARE_AP_INDEX(index) CActionSint64 *ap = CActionFactory::getInstance()->getVolatilePropAction( TVPNodeServer::PrioContext.Slot, index )
174 #define REMOVE_AP()
177 #define DECLARE_AP(name) CActionSint64 *ap = (CActionSint64*)CActionFactory::getInstance()->createByPropIndex( TVPNodeServer::PrioContext.Slot, PROPERTY_##name )
178 #define DECLARE_AP_INDEX(index) CActionSint64 *ap = (CActionSint64*)CActionFactory::getInstance()->createByPropIndex( TVPNodeServer::PrioContext.Slot, index )
179 #define REMOVE_AP() CActionFactory::getInstance()->remove( (CAction*&)ap )
186 void CDistancePrioritizer::fillOutBox( CClientHost& client, TOutBox& outbox )
188 H_AUTO(FillOutBox)
190 TClientId clientId = client.clientId();
191 initDispatchingCycle( clientId );
192 #ifdef NL_DEBUG
193 client.MoveNumber = 0;
194 #endif
196 if (!client.entityIndex().isValid())
197 return;
199 // initialize common context part
200 TVPNodeServer::PrioContext.Prioritizer = this;
201 TVPNodeServer::PrioContext.ClientHost = &client;
202 TVPNodeServer::PrioContext.ClientId = clientId;
203 CMirrorPropValueRO<TYPE_TARGET_ID> targetIndexOfClient( TheDataset, client.entityIndex(), DSPropertyTARGET_ID );
205 // TODO-Minimal visual bandwith
206 /*sint32 initialPosInBit = outbox.getPosInBit();*/
207 sint32 currentPosInBit;
208 while ( true )
210 currentPosInBit = outbox.getPosInBit();
212 // Browse the seen entities, sorted by distance
213 TCLEntityId slot = getNextEntityToStudy( clientId );
214 if ( slot == INVALID_SLOT )
216 // Exit when all the pairs have been successfully filled
217 #ifdef TEST_LOST_PACKET
218 if ( (TestPacketLostTimer!=0) && (CTickEventHandler::getGameCycle() >= TestPacketLostTimer) )
220 nldebug( "Negative ack for %hu %hu", client.clientId(), TestPacketLostSlot );
221 _History->_PacketHistory.negativeAck( client.clientId(), TestPacketLostSlot, PROPERTY_SHEET, 0 );
222 TestPacketLostTimer = 0;
224 #endif
225 return;
228 // TODO-Minimal visual bandwith
229 // Always allow to write a minimal amount of visual properties
230 /*if ( currentPosInBit - initialPosInBit >= NbMinimalVisualBits )*/
232 // Don't fill if the free space is lower than 32 bits (checked once per pair, not once per property)
233 if ( currentPosInBit + 32 > client.getCurrentThrottle() )
235 // Exit when the size limit has been reached before all the pairs have been filled
236 #ifdef NL_DEBUG
237 uint nbRemainingPairs = (uint)_PrioritizedEntitiesByClient[clientId].size() - _CurrentEntitiesToStudy[clientId];
238 if ( nbRemainingPairs > 0 )
239 LOG_WHAT_IS_SENT( "%u: C%hu S%hu: %u pairs remaining", CTickEventHandler::getGameCycle(), clientId, (uint16)slot, nbRemainingPairs );
240 LOG_WHAT_IS_SENT( "C%hu: outbox full (%d bits)", clientId, currentPosInBit );
241 #endif
242 #ifdef TEST_LOST_PACKET
243 if ( (TestPacketLostTimer!=0) && (CTickEventHandler::getGameCycle() >= TestPacketLostTimer) )
245 nldebug( "Negative ack for %hu %hu", client.clientId(), TestPacketLostSlot );
246 _History->_PacketHistory.negativeAck( client.clientId(), TestPacketLostSlot, PROPERTY_SHEET, 0 );
247 TestPacketLostTimer = 0;
249 #endif
250 return;
254 // Get the entity corresponding to the client/slot pair
255 TPairState& pairState = _VisionArray->getPairState( clientId, slot );
256 CEntity* sentity = NULL;
258 TEntityIndex entityIndex = pairState.EntityIndex;
259 TVPNodeServer::PrioContext.EntityIndex = entityIndex;
261 if ( entityIndex.isValid() )
262 sentity = TheEntityContainer->getEntity( entityIndex );
264 if ( pairState.associationSuppressed() )
266 // Pure unassociation case: we must send an empty block
267 serialSlotHeader( client, NULL, pairState, slot, outbox );
268 uint32 bits = 0;
269 outbox.serialAndLog2( bits, 2 ); // 2 bits for pos & other properties
270 //nldebug( "Pure unassociation of C%hu S%hu", clientId, (uint16)slot );
272 // The first time, sentity is non-null. If this is a resending (after a neg-ack), it's null.
273 if ( sentity )
274 _VisionProvider->postRemovePair( clientId, slot );
276 else
278 if ( sentity == NULL )
279 continue;
281 H_BEFORE(OneSlotArbitrate)
283 H_BEFORE(OneSlotArbitrateInit)
285 // Initialize the context
286 TVPNodeServer::PrioContext.Slot = slot;
287 TVPNodeServer::PrioContext.EntityIndex = entityIndex;
288 TVPNodeServer::PrioContext.Sentity = sentity;
289 //TVPNodeServer::PrioContext.PairState = &pairState;
290 TVPNodeServer::PrioContext.DistanceCE = pairState.DistanceCE;
291 TVPNodeServer::PrioContext.Timestamp = 0;
292 TVPNodeServer::PrioContext.IsTarget = (targetIndexOfClient() == entityIndex);
293 TVPNodeServer::PrioContext.PositionAlreadySent = (_History->getMileage( clientId, slot ) != 0);
294 TVPNodeServer::PrioContext.ZCache = sentity->z(entityIndex); // setup Z cache for all later mirror access to entity->z()
296 const CPropertyHistory::CEntityEntry& entry = _History->getEntityEntry(TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.Slot);
298 // Debug display
299 //nlinfo( "Preparing a block to client %hu (%s) for slot %hu", clientId, client.eId().toString().c_str(), (uint16)slot );
301 // Continuous properties
302 uint8 seenEntityType = TheDataset.getEntityId( entityIndex ).getType();
304 H_AFTER(OneSlotArbitrateInit)
306 bool thetaIntMode = false;
308 // Arbitrate all discreet properties
309 switch (seenEntityType)
311 default:
312 arbitrateAllDiscreetProperties(entry);
313 break;
315 case RYZOMID::npc:
316 arbitrateNPCDiscreetProperties(entry);
317 break;
319 case RYZOMID::creature:
320 arbitrateCreatureDiscreetProperties(entry);
321 break;
323 case RYZOMID::forageSource:
324 arbitrateForageSourceDiscreetProperties(entry);
326 thetaIntMode = true;
328 break;
331 H_BEFORE(OneSlotArbitratePropagate)
333 // Propagate back BranchHasPayload flags
334 //_VisualPropertyTreeRoot->propagateBackBranchHasPayload();
335 TVPNodeServer::fastPropagateBackBranchHasPayload();
337 H_AFTER(OneSlotArbitratePropagate)
339 H_AFTER(OneSlotArbitrate)
341 // ******** Fill the buffer with the properties ********
343 // Fill the header
344 if ( _VisualPropertyTreeRoot->BranchHasPayload )
346 H_AUTO(OneSlotFill)
348 //nldebug( "Filling for C%hu, pass %u, BEFORE HEADER: bitpos: %d", clientId, ++i, outbox.getPosInBit() );
349 //nlinfo( "C%hu S%hu AB%hu", clientId, (uint16)slot, (uint32)pairState.AssociationChangeBits );
350 serialSlotHeader( client, sentity, pairState, slot, outbox );
352 //nldebug( "AFTER HEADER: pos: %d", outbox.getPosInBit() );
354 // Fill the position if required
355 //TVPNodeServer *currentNode = _VisualPropertyTreeRoot;
357 outbox.serialBitAndLog( GET_VP_NODE(POSITION)->BranchHasPayload );
358 if ( GET_VP_NODE(POSITION)->BranchHasPayload )
360 //CActionPosition *ap = (CActionPosition*)(CActionFactory::getInstance()->createByPropIndex( slot, PROPERTY_POSITION ));
361 //ap->PropertyCode = PROPERTY_POSITION;
362 CActionPosition* ap = CActionFactory::getInstance()->getVolatilePositionAction(slot);
364 // When the mode is transmitted or the entity is not in local mode (first bit of Z), transmit the *absolute* position
365 if ( (! (TVPNodeServer::PrioContext.ZCache & 0x1)) ||
366 (GET_VP_NODE(POSITION)->BranchHasPayload && GET_VP_NODE(MODE)->BranchHasPayload) )
368 ap->Position[0] = sentity->X();
369 ap->Position[1] = sentity->Y();
370 //ap->Position[2] = sentity->z( entityIndex );
371 ap->Position[2] = TVPNodeServer::PrioContext.ZCache;
372 ap->IsRelative = false;
373 LOG_WHAT_IS_SENT( "%u: C%hu S%hu: Filling ABSPOS: %d %d %d m", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, sentity->posXm( entityIndex ), sentity->posYm( entityIndex ), sentity->posZm( entityIndex ) );
375 else
377 CMirrorPropValueRO<sint32> propLX( TheDataset, entityIndex, DSPropertyLocalX );
378 CMirrorPropValueRO<sint32> propLY( TheDataset, entityIndex, DSPropertyLocalY );
379 CMirrorPropValueRO<sint32> propLZ( TheDataset, entityIndex, DSPropertyLocalZ );
380 ap->Position[0] = propLX();
381 ap->Position[1] = propLY();
382 ap->Position[2] = propLZ();
383 ap->IsRelative = true;
384 LOG_WHAT_IS_SENT( "%u: C%hu S%hu: Filling RELPOS: %d %d %d mm", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, ap->Position[0], ap->Position[1], ap->Position[2] );
387 CActionFactory::getInstance()->packFast( ap, outbox );
388 _History->storePosition( clientId, client.sendNumber(), ap, sentity->Mileage, TVPNodeServer::PrioContext.IsTarget, TVPNodeServer::PrioContext.Timestamp );
389 //CActionFactory::getInstance()->remove( (CAction*&)ap );
390 ++client.NbActionsSentAtCycle;
393 // Fill the orientation if required
394 //currentNode = currentNode->B;
395 TVPNodeServer *currentNode = _VisualPropertyTreeRoot->B;
397 outbox.serialBitAndLog( currentNode->BranchHasPayload );
398 if ( currentNode->BranchHasPayload )
400 outbox.serialBitAndLog( currentNode->A->BranchHasPayload );
401 if ( currentNode->A->BranchHasPayload )
403 //CActionSint64 *ap = (CActionSint64*)(CActionFactory::getInstance()->createByPropIndex( slot, PROPERTY_ORIENTATION ));
404 //ap->PropertyCode = PROPERTY_ORIENTATION; // useless: already set by createByPropIndex
405 DECLARE_AP(ORIENTATION);
407 CMirrorPropValueRO<float> prop( TheDataset, entityIndex, DSPropertyORIENTATION );
408 LOG_WHAT_IS_SENT( "%u: Filling buffer for C%hu S%hu ORIENT (P%hu) : %.1f", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, prop() );
410 uint32 value = ( thetaIntMode ? (uint32)(prop()) : *((uint32*)&(prop())) );
412 ap->setAndPackValue( value, outbox );
413 _History->store( clientId, client.sendNumber(), ap );
415 // TODO: send less bits for forage source entity
417 //CActionFactory::getInstance()->remove( (CAction*&)ap );
418 REMOVE_AP();
420 ++client.NbActionsSentAtCycle;
423 // Fill the required discreet properties if required
424 currentNode->B->fillDiscreetProperties( outbox );
425 //nlinfo("end fill");
426 //TVPNodeServer::fastFillDiscreetProperties( outbox );
427 //nlinfo("end fastfill");
431 #ifdef NL_DEBUG
432 if ( _VisualPropertyTreeRoot->A->BranchHasPayload )
434 //CClientEntityIdTranslator::CEntityInfo& info = client.IdTranslator.getInfo( slot ); // CHANGED BEN
435 //if ( info.AssociationState == CClientEntityIdTranslator::CEntityInfo::AwaitingAssAck ) // CHANGED BEN
436 if ( _VisionArray->getAssociationState(clientId, slot) == TPairState::AwaitingAssAck )
438 nlwarning( "C%hu S%hu: Sending position but sheet id not sent", clientId, (uint16)slot );
439 client.displaySlotProperties( slot );
443 //if ( (verbosePropertiesSent==9999) || (verbosePropertiesSent==TVPNodeServer::PrioContext.ClientId) )
445 // nldebug( "To C%hu", clientId );
446 // outbox.displayStream();
448 #endif
450 // ******** End of iteration ********
451 //_VisualPropertyTreeRoot->displayStatesOfTreeLeaves();
452 //nldebug( "C%hu S%hu: end of pair block at bitpos %d", clientId, (uint16)slot, outbox.getPosInBit() );
455 // Reset the priority of the pair, even if nothing was filled
456 if ( slot != 0 )
457 pairState.resetPrio(); // note: nothing can remain to be filled, as we allow to exceed the size limit
463 * Test the criterion for the position of the entity 'slot' seen by 'clientId'
465 bool CDistancePrioritizer::positionHasChangedEnough()
467 // TEMP: do not send if local mode (because the local position is useless for the mektoub)
468 if ( TVPNodeServer::PrioContext.ZCache & 0x1 )
469 return false;
471 uint32 lastSentMileage = _History->getMileage( TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.Slot );
472 if ( lastSentMileage != 0 )
474 // Calculate difference distance between current and lastsent mileage (unsigned to allow mileage overflow)
475 if ( (TVPNodeServer::PrioContext.Sentity->Mileage - lastSentMileage) * _DistanceDeltaRatio > (uint32)(TVPNodeServer::PrioContext.DistanceCE) )
477 #ifdef NL_DEBUG
478 ++(TVPNodeServer::PrioContext.ClientHost->MoveNumber);
479 #endif
480 return true;
482 else
484 if (verbosePropertiesSent==0)
486 nldebug("Ignoring move: Mileage=%u, lastSentMileage=%u, _DistanceDeltaRatio=%u, DistanceCE=%u",
487 (uint32) TVPNodeServer::PrioContext.Sentity->Mileage,
488 (uint32) lastSentMileage,
489 (uint32) _DistanceDeltaRatio,
490 (uint32) TVPNodeServer::PrioContext.DistanceCE );
492 return false;
495 else
497 // Not sent yet
498 return true;
504 * Test the criterion for thetaIntMode
506 bool CDistancePrioritizer::thetaIntModehasChanged(const CPropertyHistory::CPropertyEntry& entry)
508 if ( ! entityIsWithinDistanceThreshold( PROPERTY_ORIENTATION ) )
509 return false;
511 if ( entry.HasValue )
513 CMirrorPropValueRO<float> currentTheta( TheDataset, TVPNodeServer::PrioContext.EntityIndex, DSPropertyORIENTATION );
514 return ( (uint32)currentTheta != (uint32)entry.LastSent );
516 else
518 return TVPNodeServer::PrioContext.Sentity->propertyIsInitialized( PROPERTY_ORIENTATION, DSPropertyORIENTATION, TVPNodeServer::PrioContext.EntityIndex, (TYPE_ORIENTATION*)NULL );
524 * Test the criterion for the orientation of the entity 'slot' seen by 'clientId' + initialized
526 bool CDistancePrioritizer::orientationHasChangedEnough(const CPropertyHistory::CPropertyEntry& entry, float angleRatio )
528 if ( ! entityIsWithinDistanceThreshold( PROPERTY_ORIENTATION ) )
529 return false;
531 if ( entry.HasValue )
533 CMirrorPropValueRO<float> currentAngle( TheDataset, TVPNodeServer::PrioContext.EntityIndex, DSPropertyORIENTATION );
536 // Orientation is a float angle in radian
537 const float& oldangle = *((float*)&(entry.LastSent));
538 float deltaAngle = (float)fabs( (float)(currentAngle() - oldangle) );
539 deltaAngle = (float)fmod( deltaAngle+(2*Pi), (2*Pi) );
541 //nldebug( "getDelta(theta) : dA=%g", deltaAngle );
542 return ( deltaAngle > (float)Pi/angleRatio ); // the orientation is useful only when the pos does not change
545 float oldangle;
546 entry.getValue(oldangle);
547 float deltaAngle = (float)( Pi - fabs(fmod(currentAngle()-oldangle+4*Pi, 2*Pi)-Pi) ); // deltaAngle is in [0, 2*Pi]
549 //nldebug( "getDelta(theta) : dA=%g", deltaAngle );
550 return ( deltaAngle*angleRatio > (float)Pi ); // the orientation is useful only when the pos does not change
553 else
555 // Not sent yet => always sent theta, even if it's zero (anyway, it's unlikely to be exactly 0.0: remind that this initial float is determined, for bots, by leveldesigners with the mouse)
556 return true;
562 * Test the criterion for the specified property of the entity 'slot' seen by 'clientId'
564 inline bool CDistancePrioritizer::entityIsWithinDistanceThreshold( TPropIndex propIndex )
566 // Compare distance with the threshold
567 //nldebug( "C%hu - slot %hu - prop %hu: DISTANCE=%d THRESHOL=%d", TVPNodeServer::PrioContext.ClientHost->clientId(), (uint16)TVPNodeServer::PrioContext.Slot, propIndex, TVPNodeServer::PrioContext.DistanceCE, TheEntityTranslator->getDistThreshold( propertyid ) );
568 return ( TVPNodeServer::PrioContext.DistanceCE < getDistThreshold( propIndex ) );
572 * Special arbitrate case for BEHAVIOUR property
573 * Threshold in this case should be the same than the target list threshold when
574 * the behaviour is a range attack or projectile behaviour
576 #ifndef NL_DEBUG
577 inline
578 #endif
579 void CDistancePrioritizer::arbitrateDiscreetBehaviourProperty(const CPropertyHistory::CEntityEntry& entry, CEntity* sentity)
581 #ifdef STORE_MIRROR_VP_IN_CLASS
582 const CMirrorPropValueRO<TYPE_BEHAVIOUR>& propBehav = sentity->VP_BEHAVIOUR;
583 #else
584 CMirrorPropValueRO<TYPE_BEHAVIOUR> propBehav( TheDataset, TVPNodeServer::PrioContext.EntityIndex, DSPropertyBEHAVIOUR );
585 #endif
586 if (!discreetPropertyHasChanged( entry.Properties[PROPERTY_BEHAVIOUR], propBehav, PROPERTY_BEHAVIOUR, (TYPE_BEHAVIOUR*)NULL ))
588 GET_VP_NODE(BEHAVIOUR)->BranchHasPayload = false;
589 return;
591 const MBEHAV::CBehaviour &behav = propBehav();
592 TPropIndex refDistanceProperty;
593 switch(behav.Behaviour)
595 case MBEHAV::CAST_OFF_SUCCESS:
596 refDistanceProperty = PROPERTY_TARGET_LIST;
597 break;
598 case MBEHAV::CAST_OFF_LINK:
599 refDistanceProperty = PROPERTY_TARGET_LIST;
600 break;
601 case MBEHAV::CAST_CUR_SUCCESS:
602 refDistanceProperty = PROPERTY_TARGET_LIST;
603 break;
604 case MBEHAV::CAST_CUR_LINK:
605 refDistanceProperty = PROPERTY_TARGET_LIST;
606 break;
607 case MBEHAV::CAST_MIX_SUCCESS:
608 refDistanceProperty = PROPERTY_TARGET_LIST;
609 break;
610 case MBEHAV::CAST_MIX_LINK:
611 refDistanceProperty = PROPERTY_TARGET_LIST;
612 break;
613 case MBEHAV::RANGE_ATTACK:
614 refDistanceProperty = PROPERTY_TARGET_LIST;
615 break;
616 case MBEHAV::CAST_ACID:
617 refDistanceProperty = PROPERTY_TARGET_LIST;
618 break;
619 case MBEHAV::CAST_BLIND:
620 refDistanceProperty = PROPERTY_TARGET_LIST;
621 break;
622 case MBEHAV::CAST_COLD:
623 refDistanceProperty = PROPERTY_TARGET_LIST;
624 break;
625 case MBEHAV::CAST_ELEC:
626 refDistanceProperty = PROPERTY_TARGET_LIST;
627 break;
628 case MBEHAV::CAST_FEAR:
629 refDistanceProperty = PROPERTY_TARGET_LIST;
630 break;
631 case MBEHAV::CAST_FIRE:
632 refDistanceProperty = PROPERTY_TARGET_LIST;
633 break;
634 case MBEHAV::CAST_HEALHP:
635 refDistanceProperty = PROPERTY_TARGET_LIST;
636 break;
637 case MBEHAV::CAST_MAD:
638 refDistanceProperty = PROPERTY_TARGET_LIST;
639 break;
640 case MBEHAV::CAST_POISON:
641 refDistanceProperty = PROPERTY_TARGET_LIST;
642 break;
643 case MBEHAV::CAST_ROOT:
644 refDistanceProperty = PROPERTY_TARGET_LIST;
645 break;
646 case MBEHAV::CAST_ROT:
647 refDistanceProperty = PROPERTY_TARGET_LIST;
648 break;
649 case MBEHAV::CAST_SHOCK:
650 refDistanceProperty = PROPERTY_TARGET_LIST;
651 break;
652 case MBEHAV::CAST_SLEEP:
653 refDistanceProperty = PROPERTY_TARGET_LIST;
654 break;
655 case MBEHAV::CAST_SLOW:
656 refDistanceProperty = PROPERTY_TARGET_LIST;
657 break;
658 case MBEHAV::CAST_STUN:
659 refDistanceProperty = PROPERTY_TARGET_LIST; // valid distance should be the same than the target list
660 // because target list and behaviour are sent together
661 break;
662 default:
663 refDistanceProperty = PROPERTY_BEHAVIOUR;
664 break;
667 GET_VP_NODE(BEHAVIOUR)->BranchHasPayload = entityIsWithinDistanceThreshold(refDistanceProperty);
672 * Fill the BranchHasPayload flags of the tree, using the rules deciding if a discreet property need to be sent
674 void CDistancePrioritizer::arbitrateAllDiscreetProperties(const CPropertyHistory::CEntityEntry& entry)
676 H_AUTO(arbitrateAllDiscreetProperties);
678 CEntity* sentity = TVPNodeServer::PrioContext.Sentity;
680 arbitrateDiscreetPropertyWithoutThreshold( entry, SHEET );
681 arbitrateDiscreetBehaviourProperty( entry, sentity );
683 // Don't limit to the distance threshold when triggering sending of the name of the target
684 if ( TVPNodeServer::PrioContext.IsTarget )
685 arbitrateDiscreetPropertyWithoutThreshold( entry, NAME_STRING_ID );
686 else
687 arbitrateDiscreetProperty( entry, NAME_STRING_ID );
689 //arbitrateDiscreetProperty( entry, TARGET_ID ); // now done in fillOutBox() in "mode switch"
690 arbitrateDiscreetProperty( entry, CONTEXTUAL );
691 arbitrateDiscreetPropertyWithoutThreshold( entry, MODE );
692 arbitrateDiscreetProperty( entry, VPA );
693 arbitrateDiscreetProperty( entry, VPB );
694 arbitrateDiscreetProperty( entry, VPC );
695 arbitrateDiscreetPropertyWithoutThreshold( entry, ENTITY_MOUNTED_ID );
696 arbitrateDiscreetPropertyWithoutThreshold( entry, RIDER_ENTITY_ID );
698 arbitrateTargetList( entry, TARGET_LIST );
699 arbitrateDiscreetProperty( entry, VISUAL_FX );
701 arbitrateDiscreetProperty( entry, GUILD_SYMBOL );
702 arbitrateDiscreetProperty( entry, GUILD_NAME_ID );
703 arbitrateDiscreetProperty( entry, EVENT_FACTION_ID );
704 arbitrateDiscreetProperty( entry, PVP_MODE );
705 arbitrateDiscreetProperty( entry, PVP_CLAN );
706 arbitrateNeverSendProperty( OWNER_PEOPLE );
707 arbitrateDiscreetProperty( entry, OUTPOST_INFOS );
709 if (TVPNodeServer::PrioContext.Slot != 0)
711 arbitrateCommonPosAndMode(entry);
712 arbitrateDiscreetProperty( entry, TARGET_ID );
713 arbitrateDiscreetProperty( entry, BARS );
715 else
717 arbitrateSlot0PosAndMode(entry);
718 arbitrateDiscreetPropertyWithoutThreshold( entry, TARGET_ID ); // no need of threshold
719 // never send BARS for User Player (no need since sent with the USER:BARS message)
720 arbitrateNeverSendProperty( BARS );
725 * Fill the BranchHasPayload flags of the tree, using the rules deciding if a discreet property need to be sent
727 void CDistancePrioritizer::arbitrateNPCDiscreetProperties(const CPropertyHistory::CEntityEntry& entry)
729 H_AUTO(arbitrateNPCDiscreetProperties);
731 CEntity* sentity = TVPNodeServer::PrioContext.Sentity;
733 arbitrateDiscreetPropertyWithoutThreshold( entry, SHEET );
734 arbitrateDiscreetBehaviourProperty(entry, sentity);
736 // Don't limit to the distance threshold when triggering sending of the name of the target
737 if ( TVPNodeServer::PrioContext.IsTarget )
738 arbitrateDiscreetPropertyWithoutThreshold( entry, NAME_STRING_ID );
739 else
740 arbitrateDiscreetProperty( entry, NAME_STRING_ID );
742 arbitrateDiscreetProperty( entry, TARGET_ID ); // NPC can never be in Slot 0
744 // Specific distance for NPCs' contextual property
745 GET_VP_NODE(CONTEXTUAL)->BranchHasPayload =
746 TVPNodeServer::PrioContext.DistanceCE < THRESHOLD_CONTEXTUAL_NPC
747 && discreetPropertyHasChanged( entry.Properties[PROPERTY_CONTEXTUAL], sentity->VP_CONTEXTUAL, PROPERTY_CONTEXTUAL, (TYPE_CONTEXTUAL*)NULL );
749 arbitrateDiscreetPropertyWithoutThreshold( entry, MODE );
750 arbitrateDiscreetProperty( entry, BARS );
751 arbitrateDiscreetProperty( entry, VPA );
752 arbitrateDiscreetProperty( entry, VPB );
753 arbitrateDiscreetProperty( entry, VPC );
755 arbitrateDiscreetPropertyWithoutThreshold( entry, ENTITY_MOUNTED_ID );
757 arbitrateNeverSendProperty( RIDER_ENTITY_ID );
759 arbitrateTargetList( entry, TARGET_LIST );
760 arbitrateDiscreetProperty( entry, VISUAL_FX );
761 arbitrateDiscreetProperty( entry, GUILD_SYMBOL );
762 arbitrateDiscreetProperty( entry, GUILD_NAME_ID );
763 arbitrateNeverSendProperty( EVENT_FACTION_ID );
764 arbitrateNeverSendProperty( PVP_MODE );
765 arbitrateNeverSendProperty( PVP_CLAN );
766 arbitrateNeverSendProperty( OWNER_PEOPLE );
767 arbitrateDiscreetProperty( entry, OUTPOST_INFOS );
769 arbitrateCommonPosAndMode(entry);
773 * Fill the BranchHasPayload flags of the tree, using the rules deciding if a discreet property need to be sent
775 void CDistancePrioritizer::arbitrateCreatureDiscreetProperties(const CPropertyHistory::CEntityEntry& entry)
777 H_AUTO(arbitrateCreatureDiscreetProperties);
779 CEntity* sentity = TVPNodeServer::PrioContext.Sentity;
781 arbitrateDiscreetPropertyWithoutThreshold( entry, SHEET );
782 arbitrateDiscreetBehaviourProperty(entry, sentity);
784 // Don't limit to the distance threshold when triggering sending of the name of the target
785 if ( TVPNodeServer::PrioContext.IsTarget )
786 arbitrateDiscreetPropertyWithoutThreshold( entry, NAME_STRING_ID );
787 else
788 arbitrateDiscreetProperty( entry, NAME_STRING_ID );
790 arbitrateDiscreetProperty( entry, TARGET_ID ); // Creature can never be in Slot 0
791 arbitrateDiscreetProperty( entry, CONTEXTUAL );
792 arbitrateDiscreetPropertyWithoutThreshold( entry, MODE );
793 arbitrateDiscreetProperty( entry, BARS );
795 arbitrateNeverSendProperty( VPA );
796 arbitrateDiscreetProperty( entry, VPB );
797 arbitrateNeverSendProperty( VPC );
798 arbitrateNeverSendProperty( ENTITY_MOUNTED_ID );
800 arbitrateDiscreetPropertyWithoutThreshold( entry, RIDER_ENTITY_ID );
801 arbitrateTargetList( entry, TARGET_LIST );
803 arbitrateNeverSendProperty( VISUAL_FX );
804 arbitrateNeverSendProperty( GUILD_SYMBOL );
805 arbitrateNeverSendProperty( GUILD_NAME_ID );
806 arbitrateNeverSendProperty( EVENT_FACTION_ID );
807 arbitrateNeverSendProperty( PVP_MODE );
808 arbitrateNeverSendProperty( PVP_CLAN );
809 arbitrateDiscreetProperty( entry, OWNER_PEOPLE );
810 arbitrateNeverSendProperty( OUTPOST_INFOS );
812 arbitrateCommonPosAndMode(entry);
816 * Fill the BranchHasPayload flags of the tree, using the rules deciding if a discreet property need to be sent
818 void CDistancePrioritizer::arbitrateForageSourceDiscreetProperties(const CPropertyHistory::CEntityEntry& entry)
820 H_AUTO(arbitrateForageSourceDiscreetProperties);
822 CEntity* sentity = TVPNodeServer::PrioContext.Sentity;
824 arbitrateDiscreetPropertyWithoutThreshold( entry, SHEET );
825 arbitrateNeverSendProperty( BEHAVIOUR );
827 arbitrateDiscreetProperty( entry, NAME_STRING_ID );
829 arbitrateDiscreetProperty( entry, TARGET_ID ); // ForageSource can never be in Slot 0
830 arbitrateDiscreetProperty( entry, CONTEXTUAL );
832 arbitrateNeverSendProperty( MODE );
834 arbitrateDiscreetProperty( entry, BARS );
836 arbitrateNeverSendProperty( VPA );
837 arbitrateNeverSendProperty( VPB );
838 arbitrateNeverSendProperty( VPC );
839 arbitrateNeverSendProperty( ENTITY_MOUNTED_ID );
840 arbitrateNeverSendProperty( RIDER_ENTITY_ID );
842 arbitrateTargetList( entry, TARGET_LIST );
843 arbitrateDiscreetProperty( entry, VISUAL_FX );
845 arbitrateNeverSendProperty( GUILD_SYMBOL );
846 arbitrateNeverSendProperty( GUILD_NAME_ID );
847 arbitrateNeverSendProperty( EVENT_FACTION_ID );
848 arbitrateNeverSendProperty( PVP_MODE );
849 arbitrateNeverSendProperty( PVP_CLAN );
850 arbitrateNeverSendProperty( OWNER_PEOPLE );
851 arbitrateNeverSendProperty( OUTPOST_INFOS );
853 GET_VP_NODE(POSITION)->BranchHasPayload = sentity->positionIsInitialized() && positionHasChangedEnough();
854 GET_VP_NODE(ORIENTATION)->BranchHasPayload = thetaIntModehasChanged(entry.Properties[PROPERTY_ORIENTATION]);
859 * Fill the BranchHasPayload flags of the tree, using the rules deciding if a discreet property need to be sent
861 inline void CDistancePrioritizer::arbitrateCommonPosAndMode(const CPropertyHistory::CEntityEntry& entry)
863 // Position if changed enough (if not carried by mode)
864 // Orientation if changed > 60 degrees (it's head angle only) or in first block (useful for static entities such as bot objects)
865 bool modeIsChanging = GET_VP_NODE(MODE)->BranchHasPayload;
866 bool sheetIsChanging = GET_VP_NODE(SHEET)->BranchHasPayload;
867 bool posIsReady = TVPNodeServer::PrioContext.Sentity->positionIsInitialized();
869 GET_VP_NODE(POSITION)->BranchHasPayload = (!modeIsChanging) && posIsReady && positionHasChangedEnough();
870 GET_VP_NODE(ORIENTATION)->BranchHasPayload = (sheetIsChanging && posIsReady) || orientationHasChangedEnough( entry.Properties[PROPERTY_ORIENTATION], 36.0f ); // 5 degrees
874 * Fill the BranchHasPayload flags of the tree, using the rules deciding if a discreet property need to be sent
876 inline void CDistancePrioritizer::arbitrateSlot0PosAndMode(const CPropertyHistory::CEntityEntry& entry)
878 arbitrateNeverSendProperty(POSITION);
879 arbitrateNeverSendProperty(ORIENTATION);
886 inline void CDistancePrioritizer::serialSlotHeader( CClientHost& client, CEntity *sentity, TPairState& pairState, CLFECOMMON::TCLEntityId slot, TOutBox& outbox )
888 // Slot (8 bits)
889 #ifdef NL_DEBUG
890 sint beginbitpos = outbox.getPosInBit();
891 #endif
892 outbox.serialAndLog1( slot );
894 // Association change bits (2 bits)
895 uint32 associationBits = (uint32)pairState.AssociationChangeBits;
896 outbox.serialAndLog2( associationBits, 2 );
897 if ( pairState.AssociationChangeBits != pairState.PrevAssociationBits )
899 //LOG_WHAT_IS_SENT( "slot %hu ab %u beginpos %d endpos %d beginbitpos %d endbitpos %d", (uint16)slot, associationBits, beginbitpos/8, outbox.getPosInBit()/8, beginbitpos, outbox.getPosInBit() );
900 pairState.PrevAssociationBits = pairState.AssociationChangeBits; // & 0x3;
901 _History->storeDisassociation( client.clientId(), slot, client.sendNumber(), pairState.AssociationChangeBits );
902 // pairState.AssociationChangeBits &= 0x7F;
906 // Timestamp (1 or 5 bits, depending on the type of entity) (TVPNodeServer::PrioContext.Timestamp initialized to 0)
907 uint32 timestampDelta = 0;
908 if ( sentity )
910 const CEntityId& seenEntityId = TheDataset.getEntityId( TVPNodeServer::PrioContext.EntityIndex );
911 if ( seenEntityId.getType() == RYZOMID::player )
913 // For players, always set the timestamp delta, using TickPosition
914 // Note: discreet property change times won't be accurate
915 TVPNodeServer::PrioContext.Timestamp = sentity->TickPosition;
916 timestampDelta = CTickEventHandler::getGameCycle() - sentity->TickPosition;
917 if ( timestampDelta > 15 ) // clamp to 4bit
918 timestampDelta = 15;
919 timestampDelta |= 0x10; // 'timestampIsThere bit': first bit is bit 5 (high to low order)
921 else if ( seenEntityId.getType() > RYZOMID::creature_end )
923 // For non-players/non-bots types (e.g. bags), set the timestamp delta if entity is being spawned to the client
924 //if ( _VisualPropertyTreeRoot->B->B->getSHEETnode()->BranchHasPayload ) // assumes this is done after arbitrateDiscreetProperties() // CHANGED BEN
925 if ( GET_VP_NODE(SHEET)->BranchHasPayload ) // assumes this is done after arbitrateDiscreetProperties()
927 TVPNodeServer::PrioContext.Timestamp = TheDataset.getOnlineTimestamp( TVPNodeServer::PrioContext.EntityIndex );
928 timestampDelta = CTickEventHandler::getGameCycle() - TVPNodeServer::PrioContext.Timestamp;
929 if ( timestampDelta > 15 ) // clamp to 4bit
930 timestampDelta = 15;
931 timestampDelta |= 0x10; // 'timestampIsThere bit': first bit is bit 5 (high to low order)
934 // For bots, the timestamp is not needed, the client will take _ServerGameCycle
937 outbox.serialAndLog2( timestampDelta, (timestampDelta!=0) ? 5 : 1 );
940 #ifdef STORE_MIRROR_VP_IN_CLASS
942 #define caseFillAction( name ) ap->setValue64( TVPNodeServer::PrioContext.Sentity->VP_##name() );
944 #else // STORE_MIRROR_VP_IN_CLASS
946 #define caseFillAction( name ) \
947 CMirrorPropValueRO<TYPE_##name> prop( TheDataset, TVPNodeServer::PrioContext.EntityIndex, DSProperty##name ); \
948 ap->setValue64( prop() );
950 #endif // STORE_MIRROR_VP_IN_CLASS
958 * SHEET
960 void fillSHEET( TOutBox& outbox, TPropIndex )
962 #ifdef NL_DEBUG
963 // In non-debug cases, it is done in CVisionProvider::addPair()
964 //CClientEntityIdTranslator::CEntityInfo& info = TVPNodeServer::PrioContext.ClientHost->IdTranslator.getInfo( TVPNodeServer::PrioContext.Slot ); // CHANGED BEN
965 //info.AssociationState = CClientEntityIdTranslator::CEntityInfo::NormalAssociation; // CHANGED BEN
966 TVPNodeServer::PrioContext.Prioritizer->getVisionArray()->setAssociationState( TVPNodeServer::PrioContext.ClientId,
967 TVPNodeServer::PrioContext.Slot,
968 TPairState::NormalAssociation);
969 #endif
970 bool payloadBit = true;
971 outbox.serialBitAndLog( payloadBit );
973 //CActionSint64 *ap = (CActionSint64*)CActionFactory::getInstance()->createByPropIndex( TVPNodeServer::PrioContext.Slot, PROPERTY_SHEET ); // CHANGED BEN
974 DECLARE_AP(SHEET);
976 // Pack sheet and compressed row into the action
977 #ifdef STORE_MIRROR_VP_IN_CLASS
978 CMirrorPropValueRO<TYPE_SHEET>& prop = TVPNodeServer::PrioContext.Sentity->VP_SHEET;
979 #else
980 CMirrorPropValueRO<TYPE_SHEET> prop( TheDataset, TVPNodeServer::PrioContext.EntityIndex, DSPropertySHEET );
981 #endif
983 uint32 sheetValue = prop();
984 TDataSetIndex compressedRow = TVPNodeServer::PrioContext.EntityIndex.getCompressedIndex();
985 uint64 value = (uint64)sheetValue | (((uint64)compressedRow) << 32);
986 ap->setValue64( value );
987 LOG_WHAT_IS_SENT( "%u: Filling buffer for C%hu S%hu P%hu SHEET at bitpos %d - value %" NL_I64 "u", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, PROPERTY_SHEET, outbox.getPosInBit(), ap->getValue() );
989 // Add row into the action
990 ap->packFast( outbox );
991 CFrontEndService::instance()->history()->store( TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.ClientHost->sendNumber(), ap );
992 //CActionFactory::getInstance()->remove( (CAction*&)ap );
993 REMOVE_AP();
994 ++(TVPNodeServer::PrioContext.ClientHost->NbActionsSentAtCycle);
996 // Include alias if non-null in mirror (only for mission giver NPCs)
997 CMirrorPropValueRO<TYPE_ALIAS> aliasProp( TheDataset, TVPNodeServer::PrioContext.EntityIndex, DSPropertyNPC_ALIAS );
998 if (aliasProp() != 0)
1000 bool aliasBit = true;
1001 outbox.serialBitAndLog( aliasBit );
1002 outbox.serialAndLog1( const_cast<TYPE_ALIAS&>(aliasProp()) ); // no need to store in history, alias never changes for an entity
1004 else
1006 bool aliasBit = false;
1007 outbox.serialBitAndLog( aliasBit );
1010 #ifdef TEST_LOST_PACKET
1011 if ( TestPacketLost.get() )
1013 nldebug( "This SHEET sending will be dropped..." );
1014 TestPacketLostTimer = CTickEventHandler::getGameCycle() + 10;
1015 TestPacketLost = false;
1016 TestPacketLostSlot = TVPNodeServer::PrioContext.Slot;
1018 #endif
1023 * BEHAVIOUR
1025 void fillBEHAVIOUR( TOutBox& outbox, TPropIndex )
1027 bool payloadBit = true;
1028 outbox.serialBitAndLog( payloadBit );
1029 //CActionSint64 *ap = (CActionSint64*)CActionFactory::getInstance()->createByPropIndex( TVPNodeServer::PrioContext.Slot, PROPERTY_BEHAVIOUR );
1030 DECLARE_AP(BEHAVIOUR);
1031 caseFillAction( BEHAVIOUR )
1032 LOG_WHAT_IS_SENT( "%u: Filling buffer for C%hu S%hu P%hu BEHAVIOUR at bitpos %d - value %" NL_I64 "u", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, PROPERTY_BEHAVIOUR, outbox.getPosInBit(), ap->getValue() );
1033 ap->packFast( outbox );
1034 CFrontEndService::instance()->history()->store( TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.ClientHost->sendNumber(), ap );
1035 //CActionFactory::getInstance()->remove( (CAction*&)ap );
1036 REMOVE_AP();
1037 ++(TVPNodeServer::PrioContext.ClientHost->NbActionsSentAtCycle);
1042 * NAME_STRING_ID
1044 void fillNAME_STRING_ID( TOutBox& outbox, TPropIndex )
1046 bool payloadBit = true;
1047 outbox.serialBitAndLog( payloadBit );
1048 //CActionSint64 *ap = (CActionSint64*)CActionFactory::getInstance()->createByPropIndex( TVPNodeServer::PrioContext.Slot, PROPERTY_NAME_STRING_ID );
1049 DECLARE_AP(NAME_STRING_ID);
1050 caseFillAction( NAME_STRING_ID )
1051 LOG_WHAT_IS_SENT( "%u: Filling buffer for C%hu S%hu P%hu NAME_STRING_ID at bitpos %d - value %" NL_I64 "u", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, PROPERTY_NAME_STRING_ID, outbox.getPosInBit(), ap->getValue() );
1052 ap->packFast( outbox );
1053 CFrontEndService::instance()->history()->store( TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.ClientHost->sendNumber(), ap );
1054 //CActionFactory::getInstance()->remove( (CAction*&)ap );
1055 REMOVE_AP();
1056 ++(TVPNodeServer::PrioContext.ClientHost->NbActionsSentAtCycle);
1060 * TARGET_LIST
1062 static vector<TCLEntityId> TargetSlotsList(256);
1064 NLMISC_COMMAND(displayTargetList,"Display the target list of an entity","<entityId>")
1066 if ( args.size() > 1 )
1067 return false;
1069 CEntityId entity;
1071 entity.fromString(args[0].c_str());
1073 CMirrorPropValueList<uint32> targets(TheDataset,
1074 entity,
1075 "TargetList");
1076 CMirrorPropValueList<uint32>::iterator it;
1078 it = targets.begin();
1080 if (it != targets.end())
1082 const uint32 *effectCycle = &((*it)());
1083 log.displayNL("TargetStamp: %d", *effectCycle);
1084 ++it;
1087 for (; it!=targets.end(); ++it)
1089 uint32 index = (*it)();
1090 log.displayNL("Target: %d", TDataSetRow::createFromRawIndex(index).getIndex());
1093 return true;
1097 void fillTARGET_LIST( TOutBox& outbox, TPropIndex )
1099 CClientHost *client = TVPNodeServer::PrioContext.ClientHost;
1101 CMirrorPropValueList<uint32> targets(TheDataset,
1102 TVPNodeServer::PrioContext.EntityIndex,
1103 DSPropertyTARGET_LIST );
1104 CMirrorPropValueList<uint32>::iterator it;
1106 TargetSlotsList.clear();
1108 it = targets.begin();
1110 for (; it!=targets.end(); )
1112 TDataSetRow index = TDataSetRow::createFromRawIndex((*it)());
1114 ++it;
1115 // check list overflow
1116 if (it == targets.end())
1117 break;
1119 // distance to target (in 1/127 of 100m)
1120 uint32 dt = *(&((*it)()));
1122 ++it;
1123 if (it == targets.end())
1124 break;
1125 uint32 damage = (*it)();
1127 // translate slot
1128 TCLEntityId slot = client->IdTranslator.getCEId(index);
1129 if (slot != INVALID_SLOT)
1131 TargetSlotsList.push_back(slot);
1132 TargetSlotsList.push_back((uint8)dt);
1133 TargetSlotsList.push_back(uint8(damage));
1134 TargetSlotsList.push_back(uint8(damage >> 8));
1137 ++it;
1140 // serialises branch has payload
1141 bool payLoad = true;
1142 outbox.serialBitAndLog(payLoad);
1144 // restricts to 256 entities
1145 uint longListSize = (uint)TargetSlotsList.size();
1146 if (longListSize > 32)
1147 longListSize = 32;
1149 uint8 listSize = (uint8)longListSize;
1151 // serialises short size
1152 outbox.serialAndLog1(listSize);
1154 // serialises slot list
1155 if (listSize > 0)
1156 outbox.serialBuffer(&(TargetSlotsList[0]), listSize);
1158 //CActionSint64 *ap = (CActionSint64*)CActionFactory::getInstance()->createByPropIndex( TVPNodeServer::PrioContext.Slot, PROPERTY_TARGET_LIST );
1159 DECLARE_AP(TARGET_LIST);
1160 ap->setValue64( TheDataset.getChangeTimestamp( DSPropertyTARGET_LIST, TVPNodeServer::PrioContext.EntityIndex ) );
1161 CFrontEndService::instance()->history()->store( TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.ClientHost->sendNumber(), ap );
1162 //CActionFactory::getInstance()->remove( (CAction*&)ap );
1163 REMOVE_AP();
1165 LOG_WHAT_IS_SENT( "%u: Filling buffer for C%hu S%hu P%hu TARGET_LIST at bitpos %d", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, PROPERTY_TARGET_LIST, outbox.getPosInBit() );
1170 * BARS
1172 void fillBARS( TOutBox& outbox, TPropIndex )
1174 bool payloadBit = true;
1175 outbox.serialBitAndLog( payloadBit );
1176 //CActionSint64 *ap = (CActionSint64*)CActionFactory::getInstance()->createByPropIndex( TVPNodeServer::PrioContext.Slot, PROPERTY_BARS );
1177 DECLARE_AP(BARS);
1178 caseFillAction( BARS )
1179 LOG_WHAT_IS_SENT( "%u: Filling buffer for C%hu S%hu P%hu BARS at bitpos %d - value %" NL_I64 "u", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, PROPERTY_BARS, outbox.getPosInBit(), ap->getValue() );
1180 ap->packFast( outbox );
1181 CFrontEndService::instance()->history()->store( TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.ClientHost->sendNumber(), ap );
1182 //CActionFactory::getInstance()->remove( (CAction*&)ap );
1183 REMOVE_AP();
1184 ++(TVPNodeServer::PrioContext.ClientHost->NbActionsSentAtCycle);
1189 * VPA, VPB, VPC
1190 * Assumes:
1191 * - They have the same mirror size
1192 * - There mirror dataset property index is contiguous (VPC = VPB + 1 = VPA + 2)
1194 void fillVisualPropertyABC( TOutBox& outbox, TPropIndex propIndex )
1196 bool payloadBit = true;
1197 outbox.serialBitAndLog( payloadBit );
1198 //CActionSint64 *ap = (CActionSint64*)CActionFactory::getInstance()->createByPropIndex( TVPNodeServer::PrioContext.Slot, propIndex );
1199 DECLARE_AP_INDEX(propIndex);
1200 CMirrorPropValueRO<TYPE_VPA> prop( TheDataset, TVPNodeServer::PrioContext.EntityIndex, propIndex-PROPERTY_VPA+DSPropertyVPA ); \
1201 ap->setValue64( prop() );
1202 LOG_WHAT_IS_SENT( "%u: Filling buffer for C%hu S%hu P%hu %s at bitpos %d - value %" NL_I64 "u", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, propIndex, CLFECOMMON::getPropText( propIndex ), outbox.getPosInBit(), ap->getValue() );
1203 ap->packFast( outbox );
1204 CFrontEndService::instance()->history()->store( TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.ClientHost->sendNumber(), ap );
1205 //CActionFactory::getInstance()->remove( (CAction*&)ap );
1206 REMOVE_AP();
1207 ++(TVPNodeServer::PrioContext.ClientHost->NbActionsSentAtCycle);
1212 * CONTEXTUAL
1214 void fillCONTEXTUAL( TOutBox& outbox, TPropIndex propIndex )
1216 bool payloadBit = true;
1217 outbox.serialBitAndLog( payloadBit );
1218 //CActionSint64 *ap = (CActionSint64*)CActionFactory::getInstance()->createByPropIndex( TVPNodeServer::PrioContext.Slot, PROPERTY_CONTEXTUAL );
1219 DECLARE_AP(CONTEXTUAL);
1220 caseFillAction( CONTEXTUAL )
1221 LOG_WHAT_IS_SENT( "%u: Filling buffer for C%hu S%hu P%hu CONTEXTUAL at bitpos %d - value %" NL_I64 "u", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, PROPERTY_CONTEXTUAL, outbox.getPosInBit(), ap->getValue() );
1222 ap->packFast( outbox );
1223 CFrontEndService::instance()->history()->store( TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.ClientHost->sendNumber(), ap );
1224 //CActionFactory::getInstance()->remove( (CAction*&)ap );
1225 REMOVE_AP();
1226 ++(TVPNodeServer::PrioContext.ClientHost->NbActionsSentAtCycle);
1230 * VISUAL_FX
1232 void fillVISUAL_FX( TOutBox& outbox, TPropIndex propIndex )
1234 bool payloadBit = true;
1235 outbox.serialBitAndLog( payloadBit );
1236 //CActionSint64 *ap = (CActionSint64*)CActionFactory::getInstance()->createByPropIndex( TVPNodeServer::PrioContext.Slot, PROPERTY_VISUAL_FX );
1237 DECLARE_AP(VISUAL_FX);
1238 caseFillAction( VISUAL_FX )
1239 LOG_WHAT_IS_SENT( "%u: Filling buffer for C%hu S%hu P%hu VISUAL_FX at bitpos %d - value %" NL_I64 "u", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, PROPERTY_VISUAL_FX, outbox.getPosInBit(), ap->getValue() );
1240 ap->packFast( outbox );
1241 CFrontEndService::instance()->history()->store( TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.ClientHost->sendNumber(), ap );
1242 //CActionFactory::getInstance()->remove( (CAction*&)ap );
1243 REMOVE_AP();
1244 ++(TVPNodeServer::PrioContext.ClientHost->NbActionsSentAtCycle);
1248 * MODE
1250 void fillMODE( TOutBox& outbox, TPropIndex )
1252 // Fill for mode special case
1253 bool payloadBit = true;
1254 outbox.serialBitAndLog( payloadBit );
1255 //CActionSint64 *ap = (CActionSint64*)CActionFactory::getInstance()->createByPropIndex( TVPNodeServer::PrioContext.Slot, PROPERTY_MODE );
1256 DECLARE_AP(MODE);
1257 uint64 modeLong; // uses 8+4+16+16 = 44 bits
1259 // Mode value (on 8 bits)
1260 CMirrorPropValue<MBEHAV::TMode> prop( TheDataset, TVPNodeServer::PrioContext.EntityIndex, DSPropertyMODE );
1262 // Store in history
1263 ap->setValue64( prop().RawModeAndParam );
1264 CFrontEndService::instance()->history()->store( TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.ClientHost->sendNumber(), ap );
1266 // Game cycle when mode changed (4 bits -> 1.6 second max)
1267 uint32 tickModeDelta = CTickEventHandler::getGameCycle() - prop.getTimestamp();
1268 if ( tickModeDelta > 15 ) tickModeDelta = 15;
1270 // Pack all with position 2D when mode changed, or combat angle (16 bits * 2)
1272 // OBSOLETE: MBEHAV::COMBAT_FLOAT no longer used
1273 if ( prop().Mode == MBEHAV::COMBAT_FLOAT )
1275 uint64 theta = (uint64)(*(uint32*)&(prop().Theta));
1276 modeLong = ((uint64)(prop().Mode)) | ((uint64)(tickModeDelta << 8)) | (theta << 12);
1277 LOG_WHAT_IS_SENT( "%u: Filling buffer for C%hu S%hu P%hu MODE at bitpos %d : %u [theta=%g dt=%u]", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, PROPERTY_MODE, outbox.getPosInBit(), prop().Mode, prop().Theta, tickModeDelta );
1279 else
1282 uint64 posModeX16, posModeY16;
1283 if ( TVPNodeServer::PrioContext.PositionAlreadySent )
1285 // Take the pos from the mode change
1286 posModeX16 = prop().Pos.X16;
1287 posModeY16 = prop().Pos.Y16;
1289 else
1291 // Take the current pos
1292 posModeX16 = TVPNodeServer::PrioContext.Sentity->X() >> 4; // TODO: make a method for this formula
1293 posModeY16 = TVPNodeServer::PrioContext.Sentity->Y() >> 4;
1294 tickModeDelta = 1;
1296 modeLong = ((uint64)((uint8)(prop().Mode))) | ((uint64)(tickModeDelta << 8)) | (posModeX16 << 12) | (posModeY16 << 28);
1297 LOG_WHAT_IS_SENT( "%u: Filling buffer for C%hu S%hu P%hu MODE at bitpos %d : %u [x16=%hu y16=%hu dt=%u] %s", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, PROPERTY_MODE, outbox.getPosInBit(), prop().Mode, prop().Pos.X16, prop().Pos.Y16, tickModeDelta, TVPNodeServer::PrioContext.PositionAlreadySent?"":" WithFirstPos" );
1299 // The pos might be null (if mode set before 1st pos). The client has to handle the case.
1300 /*#ifdef NL_DEBUG
1301 if ( posModeX16==0 || posModeY16==0 )
1302 nlwarning( "E%d: The pos16 in the mode is %hu %hu", TVPNodeServer::PrioContext.EntityIndex, (uint16)posModeX16, (uint16)posModeY16 );
1303 #endif*/
1306 // Fill
1307 ap->setAndPackValue( modeLong, outbox );
1308 //CActionFactory::getInstance()->remove( (CAction*&)act );
1309 REMOVE_AP();
1310 ++(TVPNodeServer::PrioContext.ClientHost->NbActionsSentAtCycle);
1314 * GUILD_NAME_ID
1316 void fillGUILD_NAME_ID( TOutBox& outbox, TPropIndex )
1318 bool payloadBit = true;
1319 outbox.serialBitAndLog( payloadBit );
1320 //CActionSint64 *ap = (CActionSint64*)CActionFactory::getInstance()->createByPropIndex( TVPNodeServer::PrioContext.Slot, PROPERTY_GUILD_NAME_ID );
1321 DECLARE_AP(GUILD_NAME_ID);
1322 caseFillAction( GUILD_NAME_ID )
1323 LOG_WHAT_IS_SENT( "%u: Filling buffer for C%hu S%hu P%hu GUILD_NAME_ID at bitpos %d - value %" NL_I64 "u", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, PROPERTY_GUILD_NAME_ID, outbox.getPosInBit(), ap->getValue() );
1324 ap->packFast( outbox );
1325 CFrontEndService::instance()->history()->store( TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.ClientHost->sendNumber(), ap );
1326 //CActionFactory::getInstance()->remove( (CAction*&)ap );
1327 REMOVE_AP();
1328 ++(TVPNodeServer::PrioContext.ClientHost->NbActionsSentAtCycle);
1332 * GUILD_SYMBOL
1334 void fillGUILD_SYMBOL( TOutBox& outbox, TPropIndex )
1336 bool payloadBit = true;
1337 outbox.serialBitAndLog( payloadBit );
1338 //CActionSint64 *ap = (CActionSint64*)CActionFactory::getInstance()->createByPropIndex( TVPNodeServer::PrioContext.Slot, PROPERTY_GUILD_SYMBOL );
1339 DECLARE_AP(GUILD_SYMBOL);
1340 caseFillAction( GUILD_SYMBOL )
1341 LOG_WHAT_IS_SENT( "%u: Filling buffer for C%hu S%hu P%hu GUILD_SYMBOL at bitpos %d - value %" NL_I64 "u", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, PROPERTY_GUILD_SYMBOL, outbox.getPosInBit(), ap->getValue() );
1342 ap->packFast( outbox );
1343 CFrontEndService::instance()->history()->store( TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.ClientHost->sendNumber(), ap );
1344 //CActionFactory::getInstance()->remove( (CAction*&)ap );
1345 REMOVE_AP();
1346 ++(TVPNodeServer::PrioContext.ClientHost->NbActionsSentAtCycle);
1351 * EVENT_FACTION_ID
1353 void fillEVENT_FACTION_ID( TOutBox& outbox, TPropIndex )
1355 bool payloadBit = true;
1356 outbox.serialBitAndLog( payloadBit );
1357 //CActionSint64 *ap = (CActionSint64*)CActionFactory::getInstance()->createByPropIndex( TVPNodeServer::PrioContext.Slot, PROPERTY_EVENT_FACTION_ID );
1358 DECLARE_AP(EVENT_FACTION_ID);
1359 caseFillAction( EVENT_FACTION_ID )
1360 LOG_WHAT_IS_SENT( "%u: Filling buffer for C%hu S%hu P%hu EVENT_FACTION_ID at bitpos %d - value %" NL_I64 "u", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, PROPERTY_EVENT_FACTION_ID, outbox.getPosInBit(), ap->getValue() );
1361 ap->packFast( outbox );
1362 CFrontEndService::instance()->history()->store( TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.ClientHost->sendNumber(), ap );
1363 //CActionFactory::getInstance()->remove( (CAction*&)ap );
1364 REMOVE_AP();
1365 ++(TVPNodeServer::PrioContext.ClientHost->NbActionsSentAtCycle);
1370 * PVP_MODE
1372 void fillPVP_MODE( TOutBox& outbox, TPropIndex )
1374 bool payloadBit = true;
1375 outbox.serialBitAndLog( payloadBit );
1376 //CActionSint64 *ap = (CActionSint64*)CActionFactory::getInstance()->createByPropIndex( TVPNodeServer::PrioContext.Slot, PROPERTY_PVP_MODE );
1377 DECLARE_AP(PVP_MODE);
1378 caseFillAction( PVP_MODE )
1379 LOG_WHAT_IS_SENT( "%u: Filling buffer for C%hu S%hu P%hu PVP_MODE at bitpos %d - value %" NL_I64 "u", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, PROPERTY_PVP_MODE, outbox.getPosInBit(), ap->getValue() );
1380 ap->packFast( outbox );
1381 CFrontEndService::instance()->history()->store( TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.ClientHost->sendNumber(), ap );
1382 //CActionFactory::getInstance()->remove( (CAction*&)ap );
1383 REMOVE_AP();
1384 ++(TVPNodeServer::PrioContext.ClientHost->NbActionsSentAtCycle);
1389 * PVP_CLAN
1391 void fillPVP_CLAN( TOutBox& outbox, TPropIndex )
1393 bool payloadBit = true;
1394 outbox.serialBitAndLog( payloadBit );
1395 //CActionSint64 *ap = (CActionSint64*)CActionFactory::getInstance()->createByPropIndex( TVPNodeServer::PrioContext.Slot, PROPERTY_PVP_CLAN );
1396 DECLARE_AP(PVP_CLAN);
1397 caseFillAction( PVP_CLAN )
1398 LOG_WHAT_IS_SENT( "%u: Filling buffer for C%hu S%hu P%hu PVP_CLAN at bitpos %d - value %" NL_I64 "u", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, PROPERTY_PVP_CLAN, outbox.getPosInBit(), ap->getValue() );
1399 ap->packFast( outbox );
1400 CFrontEndService::instance()->history()->store( TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.ClientHost->sendNumber(), ap );
1401 //CActionFactory::getInstance()->remove( (CAction*&)ap );
1402 REMOVE_AP();
1403 ++(TVPNodeServer::PrioContext.ClientHost->NbActionsSentAtCycle);
1408 * OWNER_PEOPLE
1410 void fillOWNER_PEOPLE( TOutBox& outbox, TPropIndex )
1412 bool payloadBit = true;
1413 outbox.serialBitAndLog( payloadBit );
1414 DECLARE_AP(OWNER_PEOPLE);
1415 caseFillAction( OWNER_PEOPLE )
1416 LOG_WHAT_IS_SENT( "%u: Filling buffer for C%hu S%hu P%hu OWNER_PEOPLE at bitpos %d - value %" NL_I64 "u", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, PROPERTY_OWNER_PEOPLE, outbox.getPosInBit(), ap->getValue() );
1417 ap->packFast( outbox );
1418 CFrontEndService::instance()->history()->store( TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.ClientHost->sendNumber(), ap );
1419 REMOVE_AP();
1420 ++(TVPNodeServer::PrioContext.ClientHost->NbActionsSentAtCycle);
1425 * OUTPOST_INFOS
1427 void fillOUTPOST_INFOS( TOutBox& outbox, TPropIndex )
1429 bool payloadBit = true;
1430 outbox.serialBitAndLog( payloadBit );
1431 DECLARE_AP(OUTPOST_INFOS);
1432 caseFillAction( OUTPOST_INFOS )
1433 LOG_WHAT_IS_SENT( "%u: Filling buffer for C%hu S%hu P%hu OUTPOST_INFOS at bitpos %d - value %" NL_I64 "u", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, PROPERTY_OUTPOST_INFOS, outbox.getPosInBit(), ap->getValue() );
1434 ap->packFast( outbox );
1435 CFrontEndService::instance()->history()->store( TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.ClientHost->sendNumber(), ap );
1436 REMOVE_AP();
1437 ++(TVPNodeServer::PrioContext.ClientHost->NbActionsSentAtCycle);
1442 * TARGET_ID, ENTITY_MOUNTED, RIDER_ENTITY
1444 void fillRowProperty( TOutBox& outbox, TPropIndex propIndex )
1446 bool payloadBit = true;
1447 TCLEntityId slot;
1448 CMirrorPropValueRO<TDataSetRow> prop( TheDataset, TVPNodeServer::PrioContext.EntityIndex, CEntityContainer::propertyIndexInDataSetToVisualPropIndex( propIndex ) );
1449 TEntityIndex targetindex(prop());
1450 if ( !targetindex.isValid() )
1452 // No target
1453 slot = INVALID_SLOT;
1455 else
1457 TEntityIndex seenEntityIndex = TVPNodeServer::PrioContext.EntityIndex;
1458 const char *propName = getPropText( propIndex );
1459 LOG_WHAT_IS_SENT( "%u: About to send client %hu (%s) the %s '%u --> %u'", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientHost->clientId(), TVPNodeServer::PrioContext.ClientHost->eId().toString().c_str(), propName, seenEntityIndex.getIndex(), targetindex.getIndex() );
1460 if ( targetindex == TVPNodeServer::PrioContext.ClientHost->entityIndex() )
1462 // The entity targets the client
1463 slot = 0;
1465 else if ( targetindex == seenEntityIndex )
1467 // The entity targets itself
1468 slot = INVALID_SLOT;
1470 else
1472 // Translate CEntityId to slot: get entityid, then slot
1473 TCLEntityId result = TVPNodeServer::PrioContext.ClientHost->IdTranslator.getCEId( targetindex );
1474 if ( result != INVALID_SLOT )
1476 slot = result;
1478 else
1480 LOG_WHAT_IS_SENT( "%s slot not found: E%u", propName, targetindex.getIndex() );
1481 payloadBit = false;
1486 // Fill for property target/mount special case
1487 outbox.serialBitAndLog( payloadBit );
1488 if ( payloadBit )
1490 LOG_WHAT_IS_SENT( "%u: Filling buffer for C%hu S%hu P%hu %s at bitpos %d - slot %hu", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, propIndex, CLFECOMMON::getPropText( propIndex ), outbox.getPosInBit(), (uint16)slot );
1491 //CActionSint64 *ap = (CActionSint64*)CActionFactory::getInstance()->createByPropIndex( TVPNodeServer::PrioContext.Slot, propIndex );
1492 DECLARE_AP_INDEX(propIndex);
1493 ap->setValue64( prop().getIndex() );
1495 // Store the entity index into the history
1496 CFrontEndService::instance()->history()->store( TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.ClientHost->sendNumber(), ap );
1498 // The value that will be sent is the slot, not the entity index
1499 ap->setAndPackValue( slot, outbox );
1501 //CActionFactory::getInstance()->remove( (CAction*&)ap );
1502 REMOVE_AP();
1503 ++(TVPNodeServer::PrioContext.ClientHost->NbActionsSentAtCycle);
1508 TVPNodeServer::TPrioContext TVPNodeServer::PrioContext;
1510 // Flattened Node Tree
1511 TVPNodeServer* TVPNodeServer::FlatVPTree[CLFECOMMON::NB_VISUAL_PROPERTIES];
1513 // Reordered Node tree (used by fastPropagateBackBranchHasPayload())
1514 std::vector<TVPNodeServer::CSortedFlatVPTreeItem> TVPNodeServer::SortedFlatVPTree;
1515 const bool TVPNodeServer::FalseBoolPayLoad = false;
1517 // Reordered Node tree (used by fastFillDiscreetProperties)
1518 std::vector<TVPNodeServer::CSortedFlatVPTreeFillItem> TVPNodeServer::SortedFlatVPTreeFill;
1520 void TVPNodeServer::initSortedFlatVPTree()
1522 if (isLeaf())
1523 return;
1525 CSortedFlatVPTreeItem item;
1527 item.Node = this;
1529 if (a())
1531 item.APayLoad = &(a()->BranchHasPayload);
1532 a()->initSortedFlatVPTree();
1534 if (b())
1536 item.BPayLoad = &(b()->BranchHasPayload);
1537 b()->initSortedFlatVPTree();
1540 SortedFlatVPTree.push_back(item);
1543 void TVPNodeServer::initSortedFlatVPTreeFill()
1545 uint thisItem = (uint)SortedFlatVPTreeFill.size();
1546 SortedFlatVPTreeFill.push_back(CSortedFlatVPTreeFillItem());
1548 SortedFlatVPTreeFill[thisItem].Node = this;
1550 if (a()) a()->initSortedFlatVPTreeFill();
1551 if (b()) b()->initSortedFlatVPTreeFill();
1553 SortedFlatVPTreeFill[thisItem].NextIfNoPayload = (uint)SortedFlatVPTreeFill.size();
1557 namespace CLFECOMMON
1559 // Factory for TVPNodeBase::buildTree()
1560 TVPNodeBase *NewNode()
1562 return (TVPNodeBase*) new TVPNodeServer();
1567 #ifdef NL_DEBUG
1569 NLMISC_DYNVARIABLE( uint32, MoveNumber, "MoveNumber of entities seen by monitored client" )
1571 if ( get )
1573 CFrontEndService *fe = CFrontEndService::instance();
1574 //nlassert( fe->MonitoredClient <= MAX_NB_CLIENTS );
1575 CClientHost *client = fe->receiveSub()->clientIdCont()[fe->MonitoredClient];
1576 if ( client )
1578 *pointer = client->MoveNumber;
1580 else
1582 *pointer = 9999;
1587 #endif
1590 NLMISC_COMMAND(verbosePropertiesSent,"Turn on or off or check the state of verbose logging of what is sent","<clientId> | all | off")
1592 if ( args.size() > 1 )
1593 return false;
1595 if ( args.size() == 1 )
1597 if ( args[0] == string("all") )
1598 verbosePropertiesSent = 0;
1599 else if ( args[0] == string("off") )
1600 verbosePropertiesSent = INVALID_CLIENT;
1601 else
1602 NLMISC::fromString(args[0], verbosePropertiesSent);
1605 log.displayNL( "verbosePropertiesSent is %s", (verbosePropertiesSent==INVALID_CLIENT)?"off":((verbosePropertiesSent==0)?"all":toString("C%hu", verbosePropertiesSent).c_str()) );
1606 return true;