// Ryzom - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #ifndef NL_DISTANCE_PRIORITIZER_H #define NL_DISTANCE_PRIORITIZER_H #include "nel/misc/types_nl.h" #include "fe_types.h" #include "vision_array.h" #include "processing_spreader.h" #include "entity_container.h" #include "history.h" #include extern TClientId verbosePropertiesSent; #ifdef NL_RELEASE #define LOG_WHAT_IS_SENT ; #else #define LOG_WHAT_IS_SENT if ( (verbosePropertiesSent!=0) && (verbosePropertiesSent!=TVPNodeServer::PrioContext.ClientId) ) {} else nldebug #endif /** * Comparison functor * Helps sorting in decreasing order the entities seen by a client, by priority */ struct TComparePairsByPriority { //TComparePairsByPriority( CVisionArray *va, TClientId clientId ) : _VisionArray(va), _ClientId(clientId) {} // CHANGED BEN TComparePairsByPriority( CVisionArray *va, TClientId clientId ) { _ClientStateArray = va->getClientStateArray(clientId); } bool operator() ( CLFECOMMON::TCLEntityId first, CLFECOMMON::TCLEntityId second ) { //return ( _VisionArray->getPairState( _ClientId, first ).Priority > _VisionArray->getPairState( _ClientId, second ).Priority ); // CHANGED BEN return _ClientStateArray[first].getPrio() > _ClientStateArray[second].getPrio(); } //CVisionArray *_VisionArray; //TClientId _ClientId; TPairState* _ClientStateArray; }; class CHistory; class CClientHost; class CVisionProvider; void fillSHEET( TOutBox& outbox, CLFECOMMON::TPropIndex ); void fillBEHAVIOUR( TOutBox& outbox, CLFECOMMON::TPropIndex ); void fillNAME_STRING_ID( TOutBox& outbox, CLFECOMMON::TPropIndex ); void fillTARGET_LIST( TOutBox& outbox, CLFECOMMON::TPropIndex ); void fillVISUAL_FX( TOutBox& outbox, CLFECOMMON::TPropIndex ); void fillBARS( TOutBox& outbox, CLFECOMMON::TPropIndex ); void fillVisualPropertyABC( TOutBox& outbox, CLFECOMMON::TPropIndex ); void fillCONTEXTUAL( TOutBox& outbox, CLFECOMMON::TPropIndex propIndex ); void fillMODE( TOutBox& outbox, CLFECOMMON::TPropIndex ); void fillGUILD_SYMBOL( TOutBox& outbox, CLFECOMMON::TPropIndex ); void fillGUILD_NAME_ID( TOutBox& outbox, CLFECOMMON::TPropIndex ); void fillEVENT_FACTION_ID( TOutBox& outbox, CLFECOMMON::TPropIndex ); void fillPVP_MODE( TOutBox& outbox, CLFECOMMON::TPropIndex ); void fillPVP_CLAN( TOutBox& outbox, CLFECOMMON::TPropIndex ); void fillOWNER_PEOPLE( TOutBox& outbox, CLFECOMMON::TPropIndex ); void fillOUTPOST_INFOS( TOutBox& outbox, CLFECOMMON::TPropIndex ); void fillRowProperty( TOutBox& outbox, CLFECOMMON::TPropIndex propIndex ); class CDistancePrioritizer; typedef void (*TFillFunc) (TOutBox&, CLFECOMMON::TPropIndex); /* * Visual property tree node */ class TVPNodeServer : public CLFECOMMON::TVPNodeBase { public: struct TPrioContext { CDistancePrioritizer *Prioritizer; CClientHost *ClientHost; TClientId ClientId; CLFECOMMON::TCLEntityId Slot; CEntity *Sentity; TEntityIndex EntityIndex; //TPairState *PairState; CLFECOMMON::TCoord DistanceCE; NLMISC::TGameCycle Timestamp; bool IsTarget; bool PositionAlreadySent; sint32 ZCache; }; // Static data static TPrioContext PrioContext; TFillFunc FillFunc; /// Flattened Node Tree (speed up server VP tree accesses) static TVPNodeServer* FlatVPTree[CLFECOMMON::NB_VISUAL_PROPERTIES]; /* * propagateBackBranchHasPayload used to walk through VPNode tree build. * Yet, this tree is invariable, so scanning processing is invariable too. * So I build a simple array of all nodes to be examined (all nodes except leaves.) */ static const bool FalseBoolPayLoad; struct CSortedFlatVPTreeItem { CSortedFlatVPTreeItem() : Node(NULL), APayLoad(&FalseBoolPayLoad), BPayLoad(&FalseBoolPayLoad) { } TVPNodeServer* Node; const bool* APayLoad; const bool* BPayLoad; }; struct CSortedFlatVPTreeFillItem { CSortedFlatVPTreeFillItem() : Node(NULL), NextIfNoPayload(0) { } TVPNodeServer* Node; uint NextIfNoPayload; }; /// Reordered Node tree (used by fastPropagateBackBranchHasPayload()) static std::vector SortedFlatVPTree; static void fastPropagateBackBranchHasPayload() { std::vector::iterator it; for (it=SortedFlatVPTree.begin(); it!=SortedFlatVPTree.end(); ++it) (*it).Node->BranchHasPayload = ( *((*it).APayLoad) || *((*it).BPayLoad) ); } /// Reordered Node tree (used by fastFillDiscreetProperties) static std::vector SortedFlatVPTreeFill; static void fastFillDiscreetProperties(TOutBox& outbox) { uint i = 0, size = (uint)SortedFlatVPTreeFill.size(); while (i < size) { CSortedFlatVPTreeFillItem& item = SortedFlatVPTreeFill[i++]; TVPNodeServer* node = item.Node; //nlinfo("examine node %p:%d", node, node->PropIndex); if (!node->isLeaf()) { outbox.serialBitAndLog( node->BranchHasPayload ); if (!node->BranchHasPayload) i = item.NextIfNoPayload; } else { // The payload bit depends on the success of fillDiscreetProperty, so it is serialized inside node->FillFunc( outbox, node->PropIndex ); } } } /// Constructor TVPNodeServer() : TVPNodeBase(), FillFunc(NULL) {} //bool isLeaf() const { return FillFunc != NULL; } // for discreet ones TVPNodeServer *a() { return (TVPNodeServer*)VPA; } TVPNodeServer *b() { return (TVPNodeServer*)VPB; } TVPNodeServer *parent() { return (TVPNodeServer*)VPParent; } /// Set the flags in the entire tree using the flags in the leaves bool propagateBackBranchHasPayload() { //nldebug( "Level: %u - Node=%p A=%p B=%p", getLevel(), this, VPA, VPB ); if ( isLeaf() ) { return BranchHasPayload; } else { bool leftBranch = (a() && a()->propagateBackBranchHasPayload()); bool rightBranch = (b() && b()->propagateBackBranchHasPayload()); BranchHasPayload = (leftBranch || rightBranch); return BranchHasPayload; } } /// Fill the values of the discreet properties the PropIndexes of which are in the leaves, using the BranchHasPayload flags void fillDiscreetProperties( TOutBox& outbox ) { //nlinfo("examine node %p:%d", this, PropIndex); if ( BranchHasPayload ) { //nldebug( "Level: %u - Node=%p A=%p B=%p", getLevel(), this, VPA, VPB ); if ( isLeaf() ) { // The payload bit depends on the success of fillDiscreetProperty, so it is serialized inside (*FillFunc)( outbox, PropIndex ); } else { outbox.serialBitAndLog( BranchHasPayload ); if ( a() ) a()->fillDiscreetProperties( outbox ); if ( b() ) b()->fillDiscreetProperties( outbox ); } } else { outbox.serialBitAndLog( BranchHasPayload ); } } #define INIT_FLAT_VP_TREE( vpname ) FlatVPTree[ CLFECOMMON::PROPERTY_##vpname ] = (TVPNodeServer*)(discreetRoot->get##vpname##node()) #define SET_FILLFUNC( name ) INIT_FLAT_VP_TREE(name); ((TVPNodeServer*)(discreetRoot->get##name##node()))->FillFunc = fill##name #define SET_FILLFUNC2( nameProp, nameFunc ) INIT_FLAT_VP_TREE(nameProp); ((TVPNodeServer*)(discreetRoot->get##nameProp##node()))->FillFunc = fill##nameFunc sint buildTree() { sint res = TVPNodeBase::buildTree(); TVPNodeBase *discreetRoot = VPB->VPB; // setup position a orientation manually FlatVPTree[ CLFECOMMON::PROPERTY_POSITION ] = (TVPNodeServer*)VPA; FlatVPTree[ CLFECOMMON::PROPERTY_POSX ] = FlatVPTree[ CLFECOMMON::PROPERTY_POSITION ]; FlatVPTree[ CLFECOMMON::PROPERTY_POSY ] = FlatVPTree[ CLFECOMMON::PROPERTY_POSITION ]; FlatVPTree[ CLFECOMMON::PROPERTY_POSZ ] = FlatVPTree[ CLFECOMMON::PROPERTY_POSITION ]; FlatVPTree[ CLFECOMMON::PROPERTY_ORIENTATION ] = (TVPNodeServer*)(VPB->VPA); SET_FILLFUNC( SHEET ); SET_FILLFUNC( BEHAVIOUR ); SET_FILLFUNC( NAME_STRING_ID ); SET_FILLFUNC( TARGET_LIST ); FlatVPTree[ CLFECOMMON::PROPERTY_TARGET_LIST_0 ] = FlatVPTree[ CLFECOMMON::PROPERTY_TARGET_LIST ]; FlatVPTree[ CLFECOMMON::PROPERTY_TARGET_LIST_1 ] = FlatVPTree[ CLFECOMMON::PROPERTY_TARGET_LIST ]; FlatVPTree[ CLFECOMMON::PROPERTY_TARGET_LIST_2 ] = FlatVPTree[ CLFECOMMON::PROPERTY_TARGET_LIST ]; FlatVPTree[ CLFECOMMON::PROPERTY_TARGET_LIST_3 ] = FlatVPTree[ CLFECOMMON::PROPERTY_TARGET_LIST ]; SET_FILLFUNC( VISUAL_FX ); SET_FILLFUNC( CONTEXTUAL ); SET_FILLFUNC( MODE ); SET_FILLFUNC( BARS ); SET_FILLFUNC2( VPA, VisualPropertyABC ); SET_FILLFUNC2( VPB, VisualPropertyABC ); SET_FILLFUNC2( VPC, VisualPropertyABC ); SET_FILLFUNC2( TARGET_ID, RowProperty ); SET_FILLFUNC2( ENTITY_MOUNTED_ID, RowProperty ); SET_FILLFUNC2( RIDER_ENTITY_ID, RowProperty ); SET_FILLFUNC( GUILD_SYMBOL ); SET_FILLFUNC( GUILD_NAME_ID ); SET_FILLFUNC( EVENT_FACTION_ID ); SET_FILLFUNC( PVP_MODE ); SET_FILLFUNC( PVP_CLAN ); SET_FILLFUNC( OWNER_PEOPLE ); SET_FILLFUNC( OUTPOST_INFOS ); for (uint i=0; iinitSortedFlatVPTreeFill(); return res; } void initSortedFlatVPTree(); void initSortedFlatVPTreeFill(); #define GET_VP_NODE(name) TVPNodeServer::getServerVPNode(PROPERTY_##name) static TVPNodeServer* getServerVPNode(CLFECOMMON::TPropIndex prop) { return FlatVPTree[prop]; } void deleteBranches() { if ( a() ) { a()->deleteBranches(); delete a(); } if ( b() ) { b()->deleteBranches(); delete b(); } } void deleteA() { if ( a() ) { a()->deleteBranches(); delete a(); } } void deleteB() { if ( b() ) { b()->deleteBranches(); delete b(); } } void displayStatesOfTreeLeaves() { std::string /*s1,*/ s2; displayStatesOfTreeLeaves( /*s1,*/ s2 ); if ( ! s2.empty() /*s1 != "000000000"*/ ) { //nldebug( "%u: Filled the properties for C%hu S%hu: %s", CTickEventHandler::getGameCycle(), PrioContext.ClientId, (uint16)PrioContext.Slot, s1.c_str() ); LOG_WHAT_IS_SENT( "%u: Filled props of C%hu S%hu: %s", CTickEventHandler::getGameCycle(), PrioContext.ClientId, (uint16)PrioContext.Slot, s2.c_str() ); } } void displayStatesOfTreeLeaves( /*std::string& s1,*/ std::string& s2 ) { if ( isLeaf() ) { //s1 += (BranchHasPayload?"1":"0"); if ( BranchHasPayload ) s2 += std::string(CLFECOMMON::getPropText( PropIndex )) + std::string(" "); } else { if ( a() ) a()->displayStatesOfTreeLeaves( /*s1,*/ s2 ); if ( b() ) b()->displayStatesOfTreeLeaves( /*s1,*/ s2 ); } } /// From the root, get the node mode (using hardcoded mapping as in CDistancePrioritizer::init()) TVPNodeServer *getModeNodeFromRoot() { //nlassert( b()->b()->b()->a()->a()->PropIndex == CLFECOMMON::PROPERTY_MODE ); return b()->b()->b()->a()->a(); } }; /** * * \author Olivier Cado * \author Nevrax France * \date 2002 */ class CDistancePrioritizer { public: /// Constructor CDistancePrioritizer() : _VisionArray(NULL), _VisionProvider(NULL), _DistanceDeltaRatio(10) { for ( uint c=0; c!=MAX_NB_CLIENTS; ++c ) { _CurrentEntitiesToStudy[c] = 0; } } /// Destructor ~CDistancePrioritizer() { _VisualPropertyTreeRoot->deleteBranches(); } /// Init void init( CVisionArray *va, CVisionProvider *vp, CHistory *hy ); /// Calculate the priorities void calculatePriorities(); /// Called when processing the vision received from the GPMS (and when a client connects, for slot 0) void addEntitySeenByClient( TClientId clientId, CLFECOMMON::TCLEntityId slot ) { _PrioritizedEntitiesByClient[clientId].push_back( slot ); if ( slot == 0 ) { _VisionArray->getPairState( clientId, slot ).setSteadyPrio( 100 ); } } /// Called when processing the vision received from the GPMS void removeEntitySeenByClient( TClientId clientId, CLFECOMMON::TCLEntityId slot ) { std::vector& vec = _PrioritizedEntitiesByClient[clientId]; std::vector::iterator is = std::find( vec.begin(), vec.end(), slot ); if ( is != vec.end() ) { (*is) = vec.back(); // it breaks the sorting, nevermind vec.pop_back(); } } /// Called when a client leaves void removeAllEntitiesSeenByClient( TClientId clientId ) { _PrioritizedEntitiesByClient[clientId].clear(); } /// void fillOutBox( CClientHost& client, TOutBox& outbox ); /// Set/change the distance/delta ratio that triggers the sending of a position void setDistanceDeltaRatioForPos( uint32 ddratio ) { _DistanceDeltaRatio = ddratio; } CProcessingSpreader SortSpreader; protected: /// Begin a browsing cycle void initDispatchingCycle( TClientId clientId ) { _CurrentEntitiesToStudy[clientId] = 0; } /// Browse the entities seen in order of priority; returns INVALID_SLOT if no more CLFECOMMON::TCLEntityId getNextEntityToStudy( TClientId clientId ) { if ( ! _DissassociationsToResend[clientId].empty() ) { CLFECOMMON::TCLEntityId slot = _DissassociationsToResend[clientId].front(); _DissassociationsToResend[clientId].pop_front(); #ifdef NL_DEBUG nldebug( "Returning dissassociation to resend for C%hu S%hu", clientId, (uint16)slot ); #endif return slot; } else { uint& current = _CurrentEntitiesToStudy[clientId]; if ( current < _PrioritizedEntitiesByClient[clientId].size() ) return _PrioritizedEntitiesByClient[clientId][current++]; else return CLFECOMMON::INVALID_SLOT; } } /// void serialSlotHeader( CClientHost& client, CEntity *sentity, TPairState& pairState, CLFECOMMON::TCLEntityId slot, TOutBox& outbox ); /// Called by calculatePriorities() void updatePriorityOfEntitiesSeenByClient( TClientId clientId ) { CLFECOMMON::TCLEntityId slot; TPairState* states = _VisionArray->getClientStateArray(clientId) + 1; for ( slot=1; slot!=MAX_SEEN_ENTITIES_PER_CLIENT; ++slot ) { states->updatePrio(); ++states; //_VisionArray->getPairState( clientId, slot ).updatePrio(); // CHANGED BEN } } /// Called by calculatePriorities() void sortEntitiesOfClient( TClientId clientId ) { std::sort( _PrioritizedEntitiesByClient[clientId].begin(), _PrioritizedEntitiesByClient[clientId].end(), TComparePairsByPriority(_VisionArray, clientId) ); /*#ifdef NL_DEBUG std::string s1 = "Sorted slots:", s2 = "Distances(m):", s3 = "Priorities:"; std::vector::iterator iv; for ( iv=_PrioritizedEntitiesByClient[clientId].begin(); iv!=_PrioritizedEntitiesByClient[clientId].end(); ++iv ) { s1 += NLMISC::toString( " %hu", (uint16)(*iv) ); //s2 += NLMISC::toString( " %d", _VisionArray->getPairState( clientId, *iv ).DistanceCE/1000 ); //s3 += NLMISC::toString( " %.1f", _VisionArray->getPairState( clientId, *iv ).Priority ); } nldebug( "%s", s1.c_str() ); #endif*/ } /// Test the criterion for the position of the entity 'slot' seen by 'clientId' bool positionHasChangedEnough(); /// Test the criterion for the orientation of the entity 'slot' seen by 'clientId' + initialized bool orientationHasChangedEnough(const CPropertyHistory::CPropertyEntry& entry, float angleRatio ); // Test the criterion for thetaIntMode bool thetaIntModehasChanged(const CPropertyHistory::CPropertyEntry& entry); /// Fill the BranchHasPayload flags of the tree, using the rules deciding if a discreet property need to be sent void arbitrateAllDiscreetProperties(const CPropertyHistory::CEntityEntry& entry); /// Fill the BranchHasPayload flags of the tree, using the rules deciding if a discreet property need to be sent void arbitrateNPCDiscreetProperties(const CPropertyHistory::CEntityEntry& entry); /// Fill the BranchHasPayload flags of the tree, using the rules deciding if a discreet property need to be sent void arbitrateCreatureDiscreetProperties(const CPropertyHistory::CEntityEntry& entry); /// Fill the BranchHasPayload flags of the tree, using the rules deciding if a discreet property need to be sent void arbitrateForageSourceDiscreetProperties(const CPropertyHistory::CEntityEntry& entry); /// Fill the BranchHasPayload flags of the tree, using the rules deciding if a discreet property need to be sent void arbitrateCommonPosAndMode(const CPropertyHistory::CEntityEntry& entry); /// Fill the BranchHasPayload flags of the tree, using the rules deciding if a discreet property need to be sent void arbitrateSlot0PosAndMode(const CPropertyHistory::CEntityEntry& entry); // Get distance threshold CLFECOMMON::TCoord getDistThreshold( CLFECOMMON::TPropIndex propIndex ) { //nlassert( propertyid < CLFECOMMON::NB_PROPERTIES ); return _DistThresholdTable[propIndex]; } void arbitrateDiscreetBehaviourProperty(const CPropertyHistory::CEntityEntry& entry, CEntity *sentity); public: #ifdef STORE_MIRROR_VP_IN_CLASS /* * Test the criterion for discreet properties + initialized */ template bool discreetPropertyHasChanged(const CPropertyHistory::CPropertyEntry& entry, const CMirrorPropValueRO& currentValue, CLFECOMMON::TPropIndex propIndex, T* ) const { // Although the client should already know the sheet id of the controlled player, let's send it //if ( (propIndex==CLFECOMMON::PROPERTY_SHEET) && (TVPNodeServer::PrioContext.Slot == 0) ) // return false; if (entry.HasValue) { #ifdef NL_DEBUG if ( (propIndex==CLFECOMMON::PROPERTY_SHEET) && (currentValue() != *((T*)&(entry.LastSent))) ) LOG_WHAT_IS_SENT( "C%hu S%hu: sheet changes from %u to %u", TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.Slot, (uint32)entry.LastSent, asUInt32(currentValue()) ); #endif return (currentValue() != *((T*)&(entry.LastSent))); } else { TPropertyIndex dsPropertyIndex = CEntityContainer::propertyIndexInDataSetToVisualPropIndex( propIndex ); #ifdef NL_DEBUG bool res = TVPNodeServer::PrioContext.Sentity->propertyIsInitialized( propIndex, dsPropertyIndex, TVPNodeServer::PrioContext.EntityIndex, (T*)NULL ); if (res) { // Not sent yet //nldebug( "No history yet for C%hu - slot %hu - prop %hu", TVPNodeServer::PrioContext.ClientHost->clientId(), (uint16)TVPNodeServer::PrioContext.Slot, propIndex ); if ( (propIndex==CLFECOMMON::PROPERTY_SHEET) ) { CMirrorPropValueRO currentValue( TheDataset, TVPNodeServer::PrioContext.EntityIndex, dsPropertyIndex ); LOG_WHAT_IS_SENT( "C%hu S%hu: sheet initializes to %u", TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.Slot, asUInt32(currentValue()) ); } } return res; #else return TVPNodeServer::PrioContext.Sentity->propertyIsInitialized( propIndex, dsPropertyIndex, TVPNodeServer::PrioContext.EntityIndex, (T*)NULL ); #endif } } /* * Test the criterion + initialized: overload for row properties (specialization syntax is too much different among compilers) */ bool discreetPropertyHasChanged(const CPropertyHistory::CPropertyEntry& entry, const CMirrorPropValueRO& currentValue, CLFECOMMON::TPropIndex propIndex, TDataSetRow* ) const { if (entry.HasValue) { //nldebug( "History for C%hu - slot %hu - prop %hu: %"NL_I64"u", TVPNodeServer::PrioContext.ClientHost->clientId(), (uint16)TVPNodeServer::PrioContext.Slot, propIndex, lastsent_value ); return (currentValue().getIndex() != *((TDataSetIndex*)&(entry.LastSent))); } else { TPropertyIndex dsPropertyIndex = CEntityContainer::propertyIndexInDataSetToVisualPropIndex( propIndex ); return TVPNodeServer::PrioContext.Sentity->propertyIsInitialized( propIndex, dsPropertyIndex, TVPNodeServer::PrioContext.EntityIndex, (TDataSetRow*)NULL ); } } #else // STORE_MIRROR_VP_IN_CLASS /* * Test the criterion for discreet properties + initialized */ template bool discreetPropertyHasChanged(const CPropertyHistory::CPropertyEntry& entry, TPropIndex propIndex, T* ) const { // Although the client should already know the sheet id of the controlled player, let's send it //if ( (propIndex==CLFECOMMON::PROPERTY_SHEET) && (TVPNodeServer::PrioContext.Slot == 0) ) // return false; TPropertyIndex dsPropertyIndex = CEntityContainer::propertyIndexInDataSetToVisualPropIndex( propIndex ); if (entry.HasValue) { //nldebug( "History for C%hu - slot %hu - prop %hu: %"NL_I64"u", TVPNodeServer::PrioContext.ClientHost->clientId(), (uint16)TVPNodeServer::PrioContext.Slot, propIndex, lastsent_value ); CMirrorPropValueRO currentValue( TheDataset, TVPNodeServer::PrioContext.EntityIndex, dsPropertyIndex ); #ifdef NL_DEBUG if ( (propIndex==CLFECOMMON::PROPERTY_SHEET) && (currentValue() != *((T*)&(entry.LastSent))) ) LOG_WHAT_IS_SENT( "C%hu S%hu: sheet changes from %u to %u", TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.Slot, (uint32)entry.LastSent, asUInt32(currentValue()) ); #endif return (currentValue() != *((T*)&(entry.LastSent))); } else { #ifdef NL_DEBUG bool res = TVPNodeServer::PrioContext.Sentity->propertyIsInitialized( propIndex, dsPropertyIndex, TVPNodeServer::PrioContext.EntityIndex, (T*)NULL ); if (res) { // Not sent yet //nldebug( "No history yet for C%hu - slot %hu - prop %hu", TVPNodeServer::PrioContext.ClientHost->clientId(), (uint16)TVPNodeServer::PrioContext.Slot, propIndex ); if ( (propIndex==CLFECOMMON::PROPERTY_SHEET) ) { CMirrorPropValueRO currentValue( TheDataset, TVPNodeServer::PrioContext.EntityIndex, dsPropertyIndex ); LOG_WHAT_IS_SENT( "C%hu S%hu: sheet initializes to %u", TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.Slot, asUInt32(currentValue()) ); } } return res; #else return TVPNodeServer::PrioContext.Sentity->propertyIsInitialized( propIndex, dsPropertyIndex, TVPNodeServer::PrioContext.EntityIndex, (T*)NULL ); #endif } } /* * Test the criterion + initialized: overload for row properties (specialization syntax is too much different among compilers) */ bool discreetPropertyHasChanged(const CPropertyHistory::CPropertyEntry& entry, TPropIndex propIndex, TDataSetRow* ) const { TPropertyIndex dsPropertyIndex = CEntityContainer::propertyIndexInDataSetToVisualPropIndex( propIndex ); if (entry.HasValue) { //nldebug( "History for C%hu - slot %hu - prop %hu: %"NL_I64"u", TVPNodeServer::PrioContext.ClientHost->clientId(), (uint16)TVPNodeServer::PrioContext.Slot, propIndex, lastsent_value ); CMirrorPropValueRO currentValue( TheDataset, TVPNodeServer::PrioContext.EntityIndex, dsPropertyIndex ); return (currentValue().getIndex() != *((TDataSetIndex*)&(entry.LastSent))); } else { return TVPNodeServer::PrioContext.Sentity->propertyIsInitialized( propIndex, dsPropertyIndex, TVPNodeServer::PrioContext.EntityIndex, (TDataSetRow*)NULL ); } } #endif // STORE_MIRROR_VP_IN_CLASS /* * Test the criterion for target list + initialized */ template bool targetListHasChanged(const CPropertyHistory::CPropertyEntry& entry, CLFECOMMON::TPropIndex propIndex, T* ) const { // Although the client should already know the sheet id of the controlled player, let's send it //if ( (propIndex==CLFECOMMON::PROPERTY_SHEET) && (TVPNodeServer::PrioContext.Slot == 0) ) // return false; TPropertyIndex dsPropertyIndex = CEntityContainer::propertyIndexInDataSetToVisualPropIndex( propIndex ); if (entry.HasValue) { //nldebug( "History for C%hu - slot %hu - prop %hu: %"NL_I64"u", TVPNodeServer::PrioContext.ClientHost->clientId(), (uint16)TVPNodeServer::PrioContext.Slot, propIndex, lastsent_value ); NLMISC::TGameCycle mirrorCycle = TheDataset.getChangeTimestamp( dsPropertyIndex, TVPNodeServer::PrioContext.EntityIndex ); return (mirrorCycle != entry.LastSent); } else { return TheDataset.getChangeTimestamp( dsPropertyIndex, TVPNodeServer::PrioContext.EntityIndex ) != 0; } } /// Test the criterion for the specified property of the entity 'slot' seen by 'clientId' bool entityIsWithinDistanceThreshold( CLFECOMMON::TPropIndex propIndex ); /// Fill the specified dicreet property void fillDiscreetProperty( TOutBox& outbox, CLFECOMMON::TPropIndex propIndex ); /// void pushDissociationToResend( TClientId clientId, CLFECOMMON::TCLEntityId slot ) { _DissassociationsToResend[clientId].push_back( slot ); } CVisionArray* getVisionArray() { return _VisionArray; } private: std::vector _PrioritizedEntitiesByClient [MAX_NB_CLIENTS]; std::list _DissassociationsToResend [MAX_NB_CLIENTS]; uint _CurrentEntitiesToStudy [MAX_NB_CLIENTS]; TVPNodeServer *_VisualPropertyTreeRoot; CVisionArray *_VisionArray; CVisionProvider *_VisionProvider; CHistory *_History; /// Distance threshold CLFECOMMON::TCoord _DistThresholdTable [CLFECOMMON::NB_VISUAL_PROPERTIES]; uint32 _DistanceDeltaRatio; }; #define A a() #define B b() #define Parent parent() #endif // NL_DISTANCE_PRIORITIZER_H /* End of distance_prioritizer.h */