Linux multi-monitor fullscreen support
[ryzomcore.git] / ryzom / common / src / game_share / mirror.cpp
bloba850b2b6ec8288751dce1a6eab3d37550315a7ee
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) 2010 Matt RAYKOWSKI (sfb) <matt.raykowski@gmail.com>
6 // Copyright (C) 2014 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 //
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU Affero General Public License as
10 // published by the Free Software Foundation, either version 3 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Affero General Public License for more details.
18 // You should have received a copy of the GNU Affero General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "stdpch.h"
24 #include "mirror.h"
25 #include "synchronised_message.h"
26 #include "tick_proxy_time_measure.h"
28 #ifdef DEBUG_NEW
29 #define new DEBUG_NEW
30 #endif
32 using namespace NLMISC;
33 using namespace NLNET;
34 using namespace std;
36 #ifdef NL_OS_WINDOWS
37 # ifdef FAST_MIRROR
38 # pragma message(NL_LOC_MSG "Using **** FAST_MIRROR ****")
39 # else
40 # pragma message(NL_LOC_MSG "Not using FAST_MIRROR")
41 # endif
42 #endif // NL_OS_WINDOWS
45 const string MirrorVersion = string("1.10-")+string(ListRowSizeString); // ADDED: Unidirectional Mode (don't wait for delta)
47 CMirror *MirrorInstance = NULL;
49 extern NLMISC::TTime TimeBeforeTickUpdate;
51 extern CVariable<sint32> TotalSpeedLoop;
53 extern CVariable<sint16> ProcessMirrorUpdatesSpeed;
54 extern CVariable<sint16> ReceiveMessagesViaMirrorSpeed;
56 uint32 TimeInMirrorCallback = 0;
57 uint32 TotalMirrorCallbackCalled = 0;
59 CVariable<uint32> MirrorCallbackCount("ms", "MirrorCallbackCount", "Mirror synchronised callback counter per update", 0, 100);
60 CVariable<uint32> MirrorCallbackTime("ms", "MirrorCallbackTime", "Mirror synchronised callback time per tick (in ms)", 0, 100);
63 CMirroredDataSet *TNDataSets::InvalidDataSet; // not used because expection thrown instead
65 struct CEntityAndPropName
67 CEntityId EntityId;
68 string PropName;
71 const std::string WatchNotUpdatedStr = "not updated yet";
73 static std::string MWATCH0=WatchNotUpdatedStr,MWATCH1=WatchNotUpdatedStr,
74 MWATCH2=WatchNotUpdatedStr,MWATCH3=WatchNotUpdatedStr,
75 MWATCH4=WatchNotUpdatedStr,MWATCH5=WatchNotUpdatedStr,
76 MWATCH6=WatchNotUpdatedStr,MWATCH7=WatchNotUpdatedStr,
77 MWATCH8=WatchNotUpdatedStr,MWATCH9=WatchNotUpdatedStr;
78 static std::string *mWatchStrings[]=
80 &MWATCH0,&MWATCH1,&MWATCH2,&MWATCH3,&MWATCH4,
81 &MWATCH5,&MWATCH6,&MWATCH7,&MWATCH8,&MWATCH9
83 static CEntityAndPropName *WatchedPropValues[sizeof(mWatchStrings)/sizeof(mWatchStrings[0])]=
85 NULL, NULL, NULL, NULL, NULL,
86 NULL, NULL, NULL, NULL, NULL
89 NLMISC_CATEGORISED_VARIABLE(mirror, string, MWATCH0, "watch string 0");
90 NLMISC_CATEGORISED_VARIABLE(mirror, string, MWATCH1, "watch string 1");
91 NLMISC_CATEGORISED_VARIABLE(mirror, string, MWATCH2, "watch string 2");
92 NLMISC_CATEGORISED_VARIABLE(mirror, string, MWATCH3, "watch string 3");
93 NLMISC_CATEGORISED_VARIABLE(mirror, string, MWATCH4, "watch string 4");
94 NLMISC_CATEGORISED_VARIABLE(mirror, string, MWATCH5, "watch string 5");
95 NLMISC_CATEGORISED_VARIABLE(mirror, string, MWATCH6, "watch string 6");
96 NLMISC_CATEGORISED_VARIABLE(mirror, string, MWATCH7, "watch string 7");
97 NLMISC_CATEGORISED_VARIABLE(mirror, string, MWATCH8, "watch string 8");
98 NLMISC_CATEGORISED_VARIABLE(mirror, string, MWATCH9, "watch string 9");
102 * Callback called at MS start-up
104 void cbMSUpDn( const string& /* serviceName */, TServiceId serviceId, void *upOrDn )
106 MirrorInstance->detectLocalMS( serviceId, (bool)(upOrDn!=NULL) );
111 * Callback when any service up or down is detected
113 void cbAnyServiceUpDn( const string& serviceName, TServiceId serviceId, void *vEventType )
115 // here we have an integer value stored in the 'void*' vEventType and we need to convert back to an integer
116 // for this we use pointer arithmetic with char* pointers in order to avoid compiler warnings
117 MirrorInstance->processServiceEvent( serviceName, serviceId, (CMirror::TServiceEventType)((char*)vEventType-(char*)NULL) );
122 * Callback called when any service (the mirror of which is ready) is detected
124 * A service broadcasts the SMIRUB message when its mirror becomes ready (up), so that
125 * other services can react to the new service (for example by sending messages to it).
126 * If a service B is launched after a service A is ready, B will not receive SMIRUB.
127 * That's why A has got to send a SMIRUR (reply) to B to tell that its mirror is up,
128 * when receiving the SMIRUB (broadcast) from B.
130 void cbServiceMirrorUpForSMIRUB( const string& /* serviceName */, TServiceId serviceId, void * )
132 // If the incoming service is connected after us and we already have mirror ready, tell it
133 if ( MirrorInstance->mirrorIsReady() )
135 CMessage msgout( "SMIRUR" ); // 'Service has MIRror system Up Reply'
136 MirrorInstance->pushEntityRanges( msgout );
137 sendMessageViaMirror( serviceId, msgout ); // via mirror to ensure that MARCS was sent to all remote MS before their client services get SMIRU (otherwise their potential messages couldn't be routed through their MS)
139 // else "SMIRU" will be broadcasted to all when mirror gets ready
144 * Callback when receiving the first game cycle
146 void cbSyncGameCycle()
148 if ( ! MirrorInstance->mirrorIsReady() )
150 MirrorInstance->testMirrorReadyStatus();
153 MirrorInstance->userSyncCallback();
158 * Declare the current service as an owner of the entities of the specified entity type.
159 * It means the current service will create (at most maxNbEntities entities) and remove
160 * this kind of entities (it can remove only the ones it has created) in all the datasets
161 * corresponding to the entity type.
162 * Another service can be a co-owner of the same entity type, but it will create and
163 * remove other entity ids.
165 void CMirror::declareEntityTypeOwner( uint8 entityTypeId, sint32 maxNbEntities )
167 // Ask the local mirror for a range of maxNbEntities in the datasets corresponding
168 // to entityTypeId
169 SEND_MSG_2P_TO_MIRROR( DET, entityTypeId, maxNbEntities );
170 MIRROR_INFO( "MIRROR: Declaring %d max entities of type %hu", maxNbEntities, (uint16)entityTypeId );
172 TEntitiesCreatedInEntityType newEntityTypeOwned( maxNbEntities );
174 // Set the datasetlist
175 TNDataSets::iterator ids;
176 for ( ids=_NDataSets.begin(); ids!=_NDataSets.end(); ++ids )
178 /*vector<uint8>::iterator it;
179 for ( it=GET_NDATASET(ids)._EntityTypesFilter.begin(); it!=GET_NDATASET(ids)._EntityTypesFilter.end(); ++it )
181 nldebug( "%d", (*it) );
183 if ( find( GET_NDATASET(ids)._EntityTypesFilter.begin(), GET_NDATASET(ids)._EntityTypesFilter.end(), entityTypeId ) != GET_NDATASET(ids)._EntityTypesFilter.end() )
185 newEntityTypeOwned.DataSetList.push_back( &GET_NDATASET(ids) );
189 if ( ! _EntityTypesOwned.insert( make_pair( entityTypeId, newEntityTypeOwned ) ).second )
191 nlwarning( "MIRROR: Cannot declare owning twice the same entity type" );
194 ++_PendingEntityTypesRanges;
199 * Declare the current service as a user of the entities of the specified entity type.
200 * It means the current service wants to be receive the corresponding entities. It will
201 * be notified about their additions and removals.
202 * If more than one service is an owner of a particular entity type, declaring or not
203 * as a user the same entity type will specify if the service wants to receive or not
204 * the entities created by other owners; of course the service is always aware of the
205 * entities it creates, although the mirror system does not notify them to itself.
207 /*void CMirror::declareEntityTypeUser( uint8 entityTypeId )
214 * Note: there is not undeclaration of the current service as an owner of the entities
215 * of the specified entity type. This is done automatically when the service shuts down.
220 * RG
222 void CMirror::receiveRangesForEntityType( CMessage& msgin )
224 // Get "entitytype { <dataset, first, last> } and map to entity type for add/removeEntity
225 string datasetName;
226 TDataSetIndex first, last;
228 TServiceId declaratorServiceId;
229 msgin.serial( declaratorServiceId );
230 //nlassert( declaratorServiceId == IService::getServiceId() );
232 uint8 entityTypeId;
233 msgin.serial( entityTypeId );
234 TEntityTypesOwned::iterator ito = _EntityTypesOwned.find( entityTypeId );
236 uint8 nbDatasets;
237 msgin.serial( nbDatasets );
238 for ( sint i=0; i!=(sint)nbDatasets; ++i )
240 msgin.serial( datasetName );
241 msgin.serial( first, last );
242 if ( first != INVALID_DATASET_INDEX )
244 TNDataSets::iterator ids = _NDataSets.find( datasetName );
245 if ( ids != _NDataSets.end() )
247 // Fill range manager
248 const TEntityRange& entityRange = GET_NDATASET(ids).addEntityTypeRange( entityTypeId, first, last );
250 // Fill homogeneous information about ranges
251 GET_NDATASET(ids).addDeclaredEntityRange( entityTypeId, entityRange, declaratorServiceId );
253 // Now, it's already done in declareEntityTypesOwned()
254 /*if ( ito != _EntityTypesOwned.end() )
256 GET_ENTITY_TYPE_OWNED(ito).DataSetList.push_back( &GET_NDATASET(ids) );
258 nlassert( find( GET_ENTITY_TYPE_OWNED(ito).DataSetList.begin(), GET_ENTITY_TYPE_OWNED(ito).DataSetList.end(), &GET_NDATASET(ids) ) != GET_ENTITY_TYPE_OWNED(ito).DataSetList.end() );
261 else
263 string errorStr;
264 msgin.serial( errorStr );
265 nlerror( "MIRROR: Could not acquire range for row management of entity type %hu in dataset %s. Reason:\n%s", (uint16)entityTypeId, datasetName.c_str(), errorStr.c_str() );
269 --_PendingEntityTypesRanges;
271 testMirrorReadyStatus();
274 Note:
275 An entity type can have several datasets (ex: player has 'common', 'playercharacteristics')
276 A dataset can have several entity types owned (ex: dataset 'common' with player, npc)
282 * Add an entity into the mirror
284 bool CMirror::addEntity(bool fillEntityId, NLMISC::CEntityId& entityId, bool declare )
286 // Find the structure of entities created for the entity type
287 TEntityTypesOwned::iterator ito = _EntityTypesOwned.find( entityId.getType() );
288 if ( (ito != _EntityTypesOwned.end()) &&
289 (GET_ENTITY_TYPE_OWNED(ito).CurrentNb < GET_ENTITY_TYPE_OWNED(ito).MaxNb) )
291 // For each dataset, add entity
292 TDataSetList& datasetList = GET_ENTITY_TYPE_OWNED(ito).DataSetList;
293 if ( datasetList.empty() )
295 nlwarning( "MIRROR: No dataset to add entity %s (check if the %s is online and has good paths)", entityId.toString().c_str(), RANGE_MANAGER_SERVICE.c_str() );
297 bool allOk = true;
298 uint nbAdded = 0;
299 TDataSetList::iterator idsl;
300 for ( idsl=datasetList.begin(); idsl!=datasetList.end(); ++idsl )
302 uint res = (*idsl)->addEntityToDataSet(fillEntityId, entityId, declare );
303 if ( res == 0 )
304 allOk = false;
305 nbAdded += res;
306 // in any case, only set the entityId from the first dataset
307 fillEntityId = false;
309 if ( nbAdded != 0 )
310 ++(GET_ENTITY_TYPE_OWNED(ito).CurrentNb);
311 return allOk;
313 else
315 nlwarning( "MIRROR: Cannot add entity %s", entityId.toString().c_str() );
316 return false;
324 * Add an entity into the mirror. The caller must be an owner of the corresponding
325 * entity type and it must not create more entities than maxNbEntities (see
326 * declareEntityTypeOwner()). The entity will be added into all the datasets
327 * corresponding to the entity type (possibly with a different row in each one).
329 * The entity will not be declared to all services until you call declareEntity(),
330 * either in CMirror (global) or in CMirroredDataSet (per dataset).
332 * Returns false if part or all of the process failed (for example, if the entity
333 * already exists in a dataset).
335 bool CMirror::createEntity( NLMISC::CEntityId& entityId, bool fillEntityId )
337 return addEntity( fillEntityId, entityId, false );
341 * Declare the entity (previously added by createEntity()) in all datasets
342 * concerned by its entity type.
344 * Returns false in case of failure.
346 * \seealso CMirroredDataSet::declareEntity() for alternate version.
348 bool CMirror::declareEntity( const NLMISC::CEntityId& entityId )
350 // Find the structure of entities created for the entity type
351 TEntityTypesOwned::iterator ito = _EntityTypesOwned.find( entityId.getType() );
352 if ( ito != _EntityTypesOwned.end() )
354 // For each dataset, declare entity
355 TDataSetList& datasetList = GET_ENTITY_TYPE_OWNED(ito).DataSetList;
356 #ifdef NL_DEBUG
357 if ( datasetList.empty() )
359 nlwarning( "MIRROR: No dataset to add entity %s (check if the %s is online and has good paths)", entityId.toString().c_str(), RANGE_MANAGER_SERVICE.c_str() );
360 return false;
362 #endif
363 TDataSetList::iterator idsl;
364 for ( idsl=datasetList.begin(); idsl!=datasetList.end(); ++idsl )
366 TDataSetRow entityIndex = (*idsl)->getDataSetRow( entityId );
367 if ( entityIndex.isValid() )
369 (*idsl)->declareEntity( entityIndex );
372 return true;
374 else
376 #ifdef NL_DEBUG
377 nlwarning( "MIRROR: Cannot declare entity %s", entityId.toString().c_str() );
378 #endif
379 return false;
385 * Remove an entity from the mirror. The caller must be an owner of the corresponding
386 * entity type (see declareEntityTypeOwner()) and it must have added the specified
387 * entity before (see addEntity()).
389 * Returns false if part or all of the process failed.
391 bool CMirror::removeEntity( const CEntityId& entityId )
393 // Find the structure of entities created for the entity type
394 TEntityTypesOwned::iterator ito = _EntityTypesOwned.find( entityId.getType() );
395 if ( (ito != _EntityTypesOwned.end()) &&
396 (GET_ENTITY_TYPE_OWNED(ito).CurrentNb > 0 ) )
398 // For each dataset, ask for removal of entity (which will be deferred)
399 TDataSetList& datasetList = GET_ENTITY_TYPE_OWNED(ito).DataSetList;
400 uint nbRemoved = 0;
401 bool allOk = true;
402 TDataSetList::iterator idsl;
403 for ( idsl=datasetList.begin(); idsl!=datasetList.end(); ++idsl )
405 uint res = (*idsl)->removeEntityFromDataSet( entityId );
406 if ( res == 0 )
407 allOk = false;
408 nbRemoved += res;
410 if ( nbRemoved != 0 ) // fix
411 --(GET_ENTITY_TYPE_OWNED(ito).CurrentNb);
412 return allOk;
414 else
416 nlwarning( "MIRROR: Cannot remove entity %s", entityId.toString().c_str() );
417 return false;
425 void CMirror::receiveSMIdToAccessPropertySegment( NLNET::CMessage& msgin )
427 // Read info from local Mirror Service
428 string propName;
429 sint32 smid;
430 uint32 dataTypeSize;
431 msgin.serial( propName );
432 msgin.serial( smid );
433 msgin.serial( dataTypeSize );
435 void *pt = _PropAllocator.accessPropertySegment( propName, smid );
436 setPropertyInfo( propName, pt, dataTypeSize );
438 testMirrorReadyStatus();
443 * Set the segment pointer in the property container
445 void CMirror::setPropertyInfo( std::string& propName, void *segmentPt, uint32 dataTypeSize )
447 TPropertiesInMirror::iterator ipm = _PropAllocator._PropertiesInMirror.find( propName );
448 if ( ipm != _PropAllocator._PropertiesInMirror.end() )
450 TPropertyInfo& propInfo = GET_PROPERTY_INFO(ipm);
451 CMirroredDataSet *ds = propInfo.DataSet;
452 if ( ds )
454 #ifdef NL_DEBUG
455 if ( propInfo.PropertyIndex != INVALID_PROPERTY_INDEX ) // avoid entity ids
457 // Check data type size consistency between mirror client and mirror service
458 uint32 clientDataSize = ds->getDataSizeOfProp( propInfo.PropertyIndex );
459 if ( clientDataSize != dataTypeSize )
461 nlwarning( "MIRROR: %s/P%hd (%s): data size %u does not match with the size on the local MS (%u)", ds->name().c_str(), propInfo.PropertyIndex, propName.c_str(), clientDataSize, dataTypeSize );
464 #endif
465 // Set pointers
466 ds->setPropertyPointer( propName, propInfo.PropertyIndex, segmentPt, false,
467 dataTypeSize, propInfo.flagReadOnly(), propInfo.flagMonitorAssignment() );
474 * Access segments for non-subscribed properties allocated on the local MS (useful for cleaning a whole row when creating an entity)
475 * Added: V1.8
477 * Justification:
478 * --------------
479 * Example: Let say one machine holds two services A and B. Another machine holds a service C.
480 * A is the owner of a dataset range (it will create entities in the dataset).
481 * B is the writer of a property P (it will init and write P values after A has created an entity).
482 * C is the reader of P.
483 * A must know the segment for P, even if it does not subscribe to P. Indeed, when A creates an entity E,
484 * it has to clean the row E for all the properties allocated on the machine.
485 * If A did not clean E.P, B would not always have a blank E.P (but C would).
486 * Then if B wrote E.P=V when it detected the creation of E, when the row E would be
487 * reassigned the new assignment E.P=V would overwrite an existing same V => the value would
488 * not be emitted to the other machine and C would remain with its 0 value in E.P.
490 * Note:
491 * -----
492 * Whenever a service starts, after declaring the properties it subscribes, it is sent by the local MS
493 * the list of other properties, to access the segments. The service won't start until the list is
494 * received, so that it never creates entity without cleaning all the properties, even the non-subscribes
495 * ones.
496 * Whenever another service starts, our service is sent by the local MS the new properties the other
497 * service uses. Our service may create some entities before the message is received, but it is not
498 * a problem because, as the non-subscribed properties are newly created, they can't have non-null
499 * content. Our service can remove some entities, but it can recreate an entity in the same raw before
500 * the message is received, because a row reassignment is blocked for sufficient time (usually 100 game
501 * cycles).
503 void CMirror::applyListOfOtherProperties( CMessage& msgin, uint nbProps, const char *msgName, bool isInitial )
505 for ( uint32 i=0; i!=nbProps; ++i )
507 uint32 sheet;
508 msgin.serial( sheet );
509 CSheetId sheetId( sheet );
510 TSDataSets::iterator isd = _SDataSets.find( sheetId );
511 TPropertyIndex propIndex;
512 sint32 smid;
513 uint32 dataTypeSize;
514 msgin.serial( propIndex );
515 msgin.serial( smid );
516 msgin.serial( dataTypeSize );
517 if ( isd != _SDataSets.end() )
519 // Set pointers from smid if the property is not already subscribed
520 if ( GET_SDATASET(isd)._PropertyContainer.PropertyValueArrays[propIndex].Values == NULL )
522 void *segmentPt = _PropAllocator.accessOtherPropertySegment( smid );
523 string emptyPropName;
524 GET_SDATASET(isd).setPropertyPointer( emptyPropName, propIndex, segmentPt, false, dataTypeSize, false, false ); // not readonly because killList needs write access (checked in debug mode) in CMirroredDataSet::addEntityToDataSet()
525 MIRROR_DEBUG( "MIRROR: Added other property for P%hd smid %d", propIndex, smid );
528 else
530 nlwarning( "Invalid dataset %s in %s", sheetId.toString().c_str(), msgName );
534 if ( isInitial )
536 _ListOfOtherPropertiesReceived = true;
537 testMirrorReadyStatus();
545 bool CMirror::datasetMatchesEntityTypesOwned( const CMirroredDataSet& dataset ) const
547 TEntityTypesOwned::const_iterator ieto;
548 for ( ieto=_EntityTypesOwned.begin(); ieto!=_EntityTypesOwned.end(); ++ieto )
550 const TDataSetList& dslist = GET_ENTITY_TYPE_OWNED(ieto).DataSetList;
551 TDataSetList::const_iterator idsl;
552 if ( find( dslist.begin(), dslist.end(), &dataset ) != dslist.end() )
554 return true;
557 return false;
562 * Receive trackers (access or ignore them)
564 void CMirror::receiveTracker( bool entitiesOrProp, NLNET::CMessage& msgin )
566 uint8 nb;
567 string name;
568 msgin.serial( name ); // for entities (ATE): dataset name; for properties (ATP): property name
569 msgin.serial( nb );
570 static uint nbSent = 0;
572 if ( entitiesOrProp )
574 MIRROR_DEBUG( "MIRROR:ROWMGT:ATE> Receiving %hu trackers for rows of %s", (uint16)nb, name.c_str() );
577 CMirroredDataSet& dataset = _NDataSets[name];
579 for ( uint i=0; i!=nb; ++i )
581 bool self; // self <=> tracker for reading. !self <=> tracker for writing
582 sint32 smidAdd, smidRemove;
583 msgin.serial( self );
584 msgin.serial( smidAdd );
585 msgin.serial( smidRemove );
586 nlassert( smidAdd != InvalidSMId );
587 nlassert( smidRemove != InvalidSMId );
588 sint32 mutidAdd, mutidRemove;
589 msgin.serial( mutidAdd );
590 msgin.serial( mutidRemove );
591 nlassert( mutidAdd != InvalidSMId ); // same ids as smids
592 nlassert( mutidRemove != InvalidSMId );
593 TServiceId serviceIdTracker;
594 msgin.serial( serviceIdTracker );
595 bool isInitialTransmitTracker;
596 msgin.serial( isInitialTransmitTracker );
598 // Ignore trackers corresponding to datasets in which we can't create any entities
599 // (the entity types owned were declared at the same time as when sending the subscribe
600 // message 'DATASETS' to the MS, i.e. when receiving the trackers the entity types
601 // owned are necessarily set).
602 bool trackerAdded = false;
603 if ( self || datasetMatchesEntityTypesOwned( dataset ) )
605 // Add entity management tracker
606 dataset.accessEntityTrackers( self, smidAdd, smidRemove, mutidAdd, mutidRemove );
607 trackerAdded = true;
610 CMessage msgout;
612 // If the tracker belongs to a local service, reply to it, otherwise reply to the local MS.
613 // Note: if the tracker belongs to a local service who just exited, the local MS will have to detect it and do nothing.
614 // Note: corresponds to (!self) && CUnifiedNetwork::getInstance()->getServiceName( serviceIdTracker ).empty()
615 TServiceId destServiceId = ( CUnifiedNetwork::getInstance()->isServiceLocal( serviceIdTracker ) ? serviceIdTracker : localMSId() );
616 TServiceId from = IService::getInstance()->getServiceId();
617 if ( (! self) && trackerAdded )
619 // Send acknowledge of "add tracker for entities", because the destination service
620 // could have added some entities before we got the tracker, so it has to send
621 // its entity list to us for sync
622 msgout.setType( "FWD_ACKATE" ); // sent via MS to ensure ordering on the dest service!
624 else
626 msgout.setType( "FWD_ACKATE_FCO" ); // "for counter only"
628 msgout.serial( destServiceId );
629 msgout.serial( from );
630 msgout.serial( isInitialTransmitTracker );
631 msgout.serial( name ); // dataset name
632 msgout.serial( smidAdd );
633 dataset.serialOutOwnedRanges( msgout ); // required at least for the local case
634 // If there is no owned range, no entity can have been added (yet)
635 // but we still have to send the message, in order to unblock the tracker
636 CUnifiedNetwork::getInstance()->send( localMSId(), msgout );
637 MIRROR_INFO( "MIRROR:ROWMGT:ATE> Sent %s to %hu for %s, tracker of %hu",
638 msgout.getName().c_str(),
639 destServiceId.get(),
640 name.c_str(),
641 serviceIdTracker.get() );
642 ++nbSent;
645 catch(const EMirror& )
647 nlwarning( "MIRROR:ROWMGT:ATE> Invalid dataset name %s for adding tracker", name.c_str() );
650 else
652 MIRROR_DEBUG( "MIRROR:PROPMGT:ATP> Receiving %hu trackers for property %s", (uint16)nb, name.c_str() );
653 TPropertyIndex propIndex;
654 CMirroredDataSet *ds = getDataSetByPropName( name, propIndex );
655 if ( ds )
657 // Reset PropTrackersPending for the property
658 TPropertiesInMirror::iterator ipm = _PropAllocator._PropertiesInMirror.find( name );
659 nlassert( ipm != _PropAllocator._PropertiesInMirror.end() );
660 GET_PROPERTY_INFO(ipm).PropTrackersPending = false;
662 // Access the property trackers
663 for ( uint i=0; i!=nb; ++i )
665 bool self; // self <=> tracker for reading. !self <=> tracker for writing
666 sint32 smid;
667 sint32 mutid;
668 TServiceId serviceIdTracker;
669 msgin.serial( self );
670 msgin.serial( smid );
671 msgin.serial( mutid );
672 msgin.serial( serviceIdTracker );
673 // Add property tracker (if allocated)
674 if ( smid != InvalidSMId )
676 ds->accessNewPropTracker( self, propIndex, smid, mutid );
678 if ( ! self )
680 // Send acknowledge of "add tracker for property changes"
682 // If the tracker belongs to a local service, reply to it, otherwise reply to the local MS.
683 // Note: if the tracker belongs to a local service who just exited, the local MS will have to detect it and do nothing.
684 // Note: corresponds to CUnifiedNetwork::getInstance()->getServiceName( serviceIdTracker ).empty()
685 TServiceId destServiceId = ( CUnifiedNetwork::getInstance()->isServiceLocal( serviceIdTracker ) ? serviceIdTracker : localMSId() );
686 TServiceId from = IService::getInstance()->getServiceId();
687 CMessage msgout( "FWD_ACKATP" );
688 msgout.serial( destServiceId ); // sent via MS to ensure ordering on the dest service!
689 msgout.serial( from );
690 msgout.serial( name ); // property name
691 msgout.serial( smid );
692 CUnifiedNetwork::getInstance()->send( localMSId(), msgout );
694 else
696 CMessage msgout( "FWD_ACKATP_FCO" ); // "for counter only"
697 CUnifiedNetwork::getInstance()->send( localMSId(), msgout );
700 else
702 CMessage msgout( "FWD_ACKATP_FCO" ); // "for counter only"
703 CUnifiedNetwork::getInstance()->send( localMSId(), msgout );
704 MIRROR_DEBUG( "MIRROR: \t(tracker is disabled)" );
706 ++nbSent;
707 //nldebug( "Sent FWD properties %u", nbSent );
710 else
712 nlwarning( "MIRROR:PROPMGT:ATP> Invalid property name %s for adding tracker", name.c_str() );
716 testMirrorReadyStatus();
721 * Scan for existing entities in the received ranges
723 void CMirror::receiveAcknowledgeAddEntityTracker( NLNET::CMessage& msgin, TServiceId serviceId )
725 bool isInitialTransmitTracker;
726 string datasetname;
727 msgin.serial( isInitialTransmitTracker ); // not used here, only by MS
728 msgin.serial( datasetname );
729 vector<TServiceId8> blankIgnoreList;
732 CMirroredDataSet& dataset = _NDataSets[datasetname];
734 // Fill new service's EntityTrackerAdding with the current used dataset rows (not already removed),
735 // so that it will be notified of the entities that exist already, in the range of the sender
736 if ( dataset._PropertyContainer.EntityIdArray.EntityIds && dataset._SelfEntityTrackers[ADDING].isAllocated() ) // only if already allocated
738 uint32 nbRanges, smid;
739 msgin.serial( smid, nbRanges );
740 for ( uint i=0; i!=nbRanges; ++i )
742 TDataSetIndex first, last;
743 msgin.serial( first, last );
744 uint nbAdded = dataset.scanAndResyncExistingEntities( first, last+1, false, blankIgnoreList );
745 MIRROR_INFO( "MIRROR:ROWMGT:ACKATE> AddingTracker (smid %d): filled %u entities in %s from new %s, range [%d..%d]", dataset._SelfEntityTrackers[ADDING].smid(), nbAdded, dataset.name().c_str(), CUnifiedNetwork::getInstance()->getServiceUnifiedName(serviceId).c_str(), first, last );
747 if ( nbRanges == 0 )
749 MIRROR_INFO( "MIRROR:ROWMGT:ACKATE> AddingTracker (smid %d): no entity to add", smid );
752 else
754 // WORKAROUND:
755 // If it occurs when starting, it's not a problem because SC_EI will follow
756 // (AND SC_EI must scan also entities from local services)
757 // TODO:
758 // Handle this case, and set SC_EI back to non-local entities only
759 MIRROR_INFO( "MIRROR:ROWMGT:ACKATE> %s not allocated yet while receiving tracker ack", dataset._PropertyContainer.EntityIdArray.EntityIds?"Entity tracker":"Entity Id storage" );
763 catch(const EMirror& )
765 nlwarning( "MIRROR: Invalid dataset name %s for receiving ack of addEntityTracker", datasetname.c_str() );
771 * Scan for non-null property values
773 void CMirror::receiveAcknowledgeAddPropTracker( NLNET::CMessage& msgin, TServiceId serviceId )
775 string propName;
776 // uint32 smid;
777 msgin.serial( propName );
778 //msgin.serial( smid );
779 TPropertyIndex propIndex;
780 CMirroredDataSet *ds = getDataSetByPropName( propName, propIndex );
781 if ( ds )
783 // Fill tracker with non-zero values
784 if ( ds->_PropertyContainer.PropertyValueArrays[propIndex].Values ) // only if already allocated
786 // Find self prop tracker for the specified property
787 CChangeTrackerClientProp *tracker = ds->getSelfPropTracker( propIndex );
788 if ( ! tracker )
790 //nlwarning( "PROPMGT: Tracker not found for property %s in %s", propName.c_str(), ds->name().c_str() );
791 MIRROR_INFO( "MIRROR:PROPMGT:ACKATP> Tracker for prop %s not received yet while receiving tracker ack (normal only if pointing to group)!", propName.c_str() );
793 else if ( tracker->isAllocated() )
795 uint nbChanges = ds->scanAndResyncProp( propIndex, tracker, serviceId );
796 MIRROR_INFO( "MIRROR:PROPMGT:ACKATP> Tracker (smid %d): filled %u changes for %s/P%hd from new %s", tracker->smid(), nbChanges, ds->name().c_str(), propIndex, CUnifiedNetwork::getInstance()->getServiceUnifiedName(serviceId).c_str() );
797 // Note: this will lead to unjustified changes! More are better than not enough!
799 else
801 // Should not occur since ack forwarded by the MS
802 nlwarning( "MIRROR:PROPMGT:ACKATP> Tracker not allocated while receiving tracker ack for prop %s!!", propName.c_str() );
805 else
807 // Should not occur since ack forwarded by the MS
808 nlwarning( "MIRROR:PROPMGT:ACKATP> Values not ready for %s/P%hd!!", ds->name().c_str(), propIndex );
811 else
813 nlwarning( "MIRROR:PROPMGT:ACKATP> Invalid property name %s for receiving ack of AddPropTracker", propName.c_str() );
821 void CMirror::deleteTracker( CChangeTrackerClient& tracker, std::vector<CChangeTrackerClient>& vect )
823 MIRROR_DEBUG( "MIRROR: Releasing tracker with smid %d", tracker.smid() );
825 // Release the tracker
826 if ( ! tracker.isPointingToGroupTracker() )
827 tracker.release();
829 // Delete the tracker object
830 tracker.~CChangeTrackerClient();
831 tracker = vect.back();
832 vect.pop_back();
837 * Can handle case of tracker not known
839 void CMirror::releaseTrackers( NLNET::CMessage& msgin )
841 vector<sint32> smidsToFindAndRemove;
842 msgin.serialCont( smidsToFindAndRemove );
843 #ifdef NL_DEBUG
844 vector<sint32>::iterator istfar;
845 for ( istfar=smidsToFindAndRemove.begin(); istfar!=smidsToFindAndRemove.end(); ++istfar )
847 if ( (*istfar) != InvalidSMId )
849 MIRROR_INFO( "MIRROR: Need to remove tracker with smid %d", (*istfar) );
852 #endif
854 // Browse datasets
855 TSDataSets::iterator ids;
856 for ( ids=_SDataSets.begin(); ids!=_SDataSets.end(); ++ids )
858 CMirroredDataSet& dataset = GET_SDATASET(ids);
860 sint32 eti;
861 for ( eti=REMOVING; eti>=ADDING; --eti )
863 for (uint i = 0 ; i < dataset._EntityTrackers[eti].size(); )
865 if ( find( smidsToFindAndRemove.begin(), smidsToFindAndRemove.end(), dataset._EntityTrackers[eti][i].smid() ) != smidsToFindAndRemove.end() )
866 deleteTracker( dataset._EntityTrackers[eti][i], dataset._EntityTrackers[eti] );
867 else
868 ++i; // deleteTracker() moves the last entry to the deleted place
870 /* ace: deleting an entry in a vector invalidates all iterators
871 TTrackerListForEnt::iterator itl;
872 for ( itl=dataset._EntityTrackers[eti].begin(); itl<dataset._EntityTrackers[eti].end(); )
874 if ( find( smidsToFindAndRemove.begin(), smidsToFindAndRemove.end(), (*itl).smid() ) != smidsToFindAndRemove.end() )
875 deleteTracker( (*itl), dataset._EntityTrackers[eti] );
876 else
877 ++itl; // deleteTracker() moves the last entry to the deleted place
882 TPropertyIndex propIndex;
883 for ( propIndex=0; propIndex!= dataset.nbProperties(); ++propIndex )
885 // TODO: Skip properties not declared
886 for (uint i=0; i < dataset._PropTrackers[propIndex].size(); )
888 if ( find( smidsToFindAndRemove.begin(), smidsToFindAndRemove.end(), dataset._PropTrackers[propIndex][i].smid() ) != smidsToFindAndRemove.end() )
889 deleteTracker( dataset._PropTrackers[propIndex][i], dataset._PropTrackers[propIndex] );
890 else
891 ++i; // deleteTracker() moves the last entry to the deleted place
893 /* ace: deleting an entry in a vector invalidates all iterators
894 TTrackerListForProp::iterator itp;
895 for ( itp=dataset._PropTrackers[propIndex].begin(); itp<dataset._PropTrackers[propIndex].end(); )
897 if ( find( smidsToFindAndRemove.begin(), smidsToFindAndRemove.end(), (*itp).smid() ) != smidsToFindAndRemove.end() )
898 deleteTracker( (*itp), dataset._PropTrackers[propIndex] );
899 else
900 ++itp; // deleteTracker() moves the last entry to the deleted place
911 void CMirror::scanAndResyncEntitiesExceptIgnored( const std::vector<NLNET::TServiceId8>& creatorIdsToIgnore )
913 // Open self entity trackers
914 for ( TSDataSets::iterator itd=_SDataSets.begin(); itd!=_SDataSets.end(); ++itd )
916 CMirroredDataSet& dataset = GET_SDATASET(itd);
917 dataset.enableEntityNotification();
919 // Here, we could clear the entity trackers, so that the first notification session
920 // would report only the entities filled by scanAndResyncExistingEntities() below.
921 // But it would necessary to block the MS or any other service to write in it.
922 // Letting the trackers as they are is not harmful. Reasons:
923 // First
924 // - An online entity not in the tracker will be rescanned and added, thus notified
925 // Ex(*): an entity created by S1 before S2 connected
926 // - An online entity in the adding tracker will be rescanned thus notified (only once, it can't be added twice)
927 // Ex(*): an entity created by S1 after MS2 added S2's entity trackers
928 // - An offline entity in the adding tracker will not be notified ("lifetime too short")
929 // Ex(*): an entity added by S1 after MS2 added S2's entity trackers, removed since
930 // Ex(**):an entity added by S1 before S2 first connection, removed before now
931 // Then (removing notification MUST come after adding notification!)
932 // - An offline entity in the removg tracker will not be notified ("lifetime too short") because it's addition not notified so not in the entity map
933 // Ex(*): an entity added by S1 after MS2 added S2's entity trackers, removed since
934 // - An online entity in the removg tracker would be notified if its addition notification has added it into the entityId map
935 // It can't occur, because a row can't be reassigned before a minimal delay (100 gc) greater than the delay needed to receive SC_EI (10 gc).
936 // (*) S1 MS1 MS2 S2+-+ Example on S2 at second connection
937 // (**)S1 MS1 MS2 S2+ Example on S2 at first connection
940 // Prepare to execute "mirror ready commands" (note: they will be executed when receiving UMM from the MS for proper synchronization) and tell other services we are ready
941 testMirrorReadyStatus();
942 nlassert( _MirrorGotReadyLevel2 );
944 // Add existing entities
945 for ( TSDataSets::iterator itd=_SDataSets.begin(); itd!=_SDataSets.end(); ++itd )
947 CMirroredDataSet& dataset = GET_SDATASET(itd);
948 uint nbAdded = dataset.scanAndResyncExistingEntities( 0, dataset.maxNbRows(), false, creatorIdsToIgnore );
949 MIRROR_INFO( "MIRROR:ROWMGT:SC_EI> Filled %u entities in %s, with %u creators ignored", nbAdded, dataset.name().c_str(), creatorIdsToIgnore.size() );
955 * Scan the entities to find if some are missing (and add them) (debug feature)
957 void CMirror::rescanExistingEntities( CMirroredDataSet& dataset, NLMISC::CLog& log, bool addUnknown )
959 if ( addUnknown )
961 vector<TServiceId8> blankIgnoreList;
962 uint nbAdded = dataset.scanAndResyncExistingEntities( 0, dataset.maxNbRows(), true, blankIgnoreList );
963 log.displayNL( "MIRROR:ROWMGT: Filled %u entities in %s!", nbAdded, dataset.name().c_str() );
965 else
967 log.displayNL( "Online entities not known by this service:" );
968 dataset.displayUnknownEntities( 0, dataset.maxNbRows(), log );
974 * Callback for msg receive from mirror service: receive shared memory id and access memory
976 void cbRecvSMIdToAccessPropertySegment( NLNET::CMessage& msgin, const std::string &/* serviceName */, TServiceId /* serviceId */ )
978 MirrorInstance->receiveSMIdToAccessPropertySegment( msgin );
985 void cbRecvRangesForEntityType( NLNET::CMessage& msgin, const std::string &/* serviceName */, TServiceId /* serviceId */ )
987 MirrorInstance->receiveRangesForEntityType( msgin );
994 void cbRecvAddEntityTracker( NLNET::CMessage& msgin, const std::string &/* serviceName */, TServiceId /* serviceId */ )
996 MirrorInstance->receiveTracker( true, msgin );
1003 void cbRecvAddPropTracker( NLNET::CMessage& msgin, const std::string &/* serviceName */, TServiceId /* serviceId */ )
1005 MirrorInstance->receiveTracker( false, msgin );
1012 void cbRecvAcknowledgeAddEntityTracker( NLNET::CMessage& msgin, const std::string &/* serviceName */, TServiceId /* serviceId */ )
1014 TServiceId from;
1015 msgin.serial( from );
1016 MirrorInstance->receiveAcknowledgeAddEntityTracker( msgin, from );
1023 void cbRecvAcknowledgeAddPropTracker( NLNET::CMessage& msgin, const std::string &/* serviceName */, TServiceId /* serviceId */ )
1025 TServiceId from;
1026 msgin.serial( from );
1027 MirrorInstance->receiveAcknowledgeAddPropTracker( msgin, from );
1034 void cbReleaseTrackers( NLNET::CMessage& msgin, const std::string &/* serviceName */, TServiceId /* serviceId */ )
1036 MirrorInstance->releaseTrackers( msgin );
1043 void cbAllMirrorsOnline( NLNET::CMessage& msgin, const std::string &/* serviceName */, TServiceId /* serviceId */ )
1045 sint16 nbOfMirrors;
1046 string versionStr = "prior to 1.5";
1047 bool hasVersion = false;
1048 msgin.serial( nbOfMirrors );
1051 msgin.serial( versionStr );
1052 hasVersion = true;
1054 catch (const EStreamOverflow&)
1056 if ( (! hasVersion) || (MirrorVersion != versionStr) )
1057 nlerror( "Mirror version mismatch! This service: %s; Local MS: %s", MirrorVersion.c_str(), versionStr.c_str() );
1058 MirrorInstance->initAfterMirrorUp();
1063 * Receive the broadcasting of a service that has its mirror system ready
1065 void cbRecvServiceHasMirrorReadyBroadcast( NLNET::CMessage& msgin, const std::string &serviceName, TServiceId serviceId )
1067 cbServiceMirrorUpForSMIRUB( serviceName, serviceId, 0 );
1069 MirrorInstance->receiveServiceHasMirrorReady( serviceName, serviceId, msgin );
1074 * Receive the reply of a SMIRUB
1076 void cbRecvServiceHasMirrorReadyReply( NLNET::CMessage& msgin, const std::string &serviceName, TServiceId serviceId )
1078 MirrorInstance->receiveServiceHasMirrorReady( serviceName, serviceId, msgin );
1083 * Set service mirror/up/down callback
1085 void CMirror::setServiceMUDCallback( const string &serviceName, NLNET::TUnifiedNetCallback cb, void *arg, bool back, bool synchronizedCallback, TServiceEventType et )
1087 nlassert( cb != NULL );
1089 TCallbackArgItemM cbItem;
1090 cbItem.Cb = cb;
1091 cbItem.Arg = arg;
1092 cbItem.Synchronized = synchronizedCallback;
1093 cbItem.EventType = et;
1095 if ( serviceName == "*" )
1097 if ( back )
1098 _ServiceUpDnUniCallbacks.push_back( cbItem );
1099 else
1100 _ServiceUpDnUniCallbacks.insert( _ServiceUpDnUniCallbacks.begin(), cbItem );
1102 else
1104 if ( back )
1105 _ServiceUpDnCallbacks[serviceName].push_back( cbItem );
1106 else
1107 _ServiceUpDnCallbacks[serviceName].insert( _ServiceUpDnCallbacks[serviceName].begin(), cbItem );
1113 * Set callbacks to call when a particular service has its mirror system ready. See .h
1115 void CMirror::setServiceMirrorUpCallback( const string &serviceName, NLNET::TUnifiedNetCallback cb, void *arg, bool back, bool synchronizedCallback )
1117 setServiceMUDCallback( serviceName, cb, arg, back, synchronizedCallback, ETMirrorUp );
1122 * This is the counterpart of setServiceMirrorUpCallback(). See .h
1124 void CMirror::setServiceMirrorDownCallback( const std::string &serviceName, NLNET::TUnifiedNetCallback cb, void *arg, bool back, bool synchronizedCallback )
1126 setServiceMUDCallback( serviceName, cb, arg, back, synchronizedCallback, ETMirrorDn );
1131 * This is similar to CUnifiedNetwork::setServiceUpCallback() but it allows you to spawn or despawn
1132 * entities in the callback if you set synchronizedCallback to true (see isRunningSynchronizedCode())
1134 void CMirror::setServiceUpCallback( const std::string &serviceName, NLNET::TUnifiedNetCallback cb, void *arg, bool back, bool synchronizedCallback )
1136 setServiceMUDCallback( serviceName, cb, arg, back, synchronizedCallback, ETServiceUp );
1141 * This is similar to CUnifiedNetwork::setServiceUpCallback() but it allows you to spawn or despawn
1142 * entities in the callback if you set synchronizedCallback to true (see isRunningSynchronizedCode())
1144 void CMirror::setServiceDownCallback( const std::string &serviceName, NLNET::TUnifiedNetCallback cb, void *arg, bool back, bool synchronizedCallback )
1146 setServiceMUDCallback( serviceName, cb, arg, back, synchronizedCallback, ETServiceDn );
1151 * Receive the broadcasting of a service that has its mirror system ready
1153 void CMirror::receiveServiceHasMirrorReady( const std::string& serviceName, TServiceId serviceId, NLNET::CMessage& msgin )
1155 if ( _ServicesMirrorUpNotified.find( serviceId ) != _ServicesMirrorUpNotified.end() )
1157 // This occurs when A and B cross-send a SMIRUB msg to each other at the same time
1158 // (then A and B send SMIRUR, making A and B come here)
1159 //MIRROR_DEBUG( "MIRROR: Ignoring second ServiceHasMirrorReady from %s-%hu", serviceName.c_str(), serviceId.get() );
1160 return;
1162 MIRROR_INFO( "MIRROR: Service %s-%hu has mirror ready", serviceName.c_str(), serviceId.get() );
1164 // Read the entity ranges declared by the sender service
1167 uint32 nbDatasets;
1168 msgin.serial( nbDatasets );
1169 for ( uint32 i=0; i!=nbDatasets; ++i )
1171 CSheetId dsSheetId;
1172 msgin.serial( dsSheetId );
1174 TEntityRangeOfType entityRanges;
1175 msgin.serialCont( entityRanges );
1177 TSDataSets::iterator it = _SDataSets.find( dsSheetId );
1178 if ( it != _SDataSets.end() )
1180 CMirroredDataSet& dataset = (*it).second;
1181 dataset.addDeclaredEntityRanges( entityRanges, serviceId );
1185 catch (const EStreamOverflow&)
1187 nlwarning( "Received SMIRU from old version service %s-%hu", serviceName.c_str(), serviceId.get() );
1190 // Callbacks
1191 processServiceEvent( serviceName, serviceId, ETMirrorUp );
1196 * React to a service mirror/up/down event
1198 void CMirror::processServiceEvent( const std::string &serviceName, TServiceId serviceId, TServiceEventType et )
1200 switch ( et )
1202 case ETMirrorUp:
1204 // Store which 'service mirror up' are known
1205 _ServicesMirrorUpNotified.insert( serviceId );
1206 break;
1208 case ETServiceDn:
1210 // Check if a 'service down' must call a 'service mirror down' callback as well
1211 TNotifiedServices::iterator isn = _ServicesMirrorUpNotified.find( serviceId );
1212 if ( isn != _ServicesMirrorUpNotified.end() )
1214 processServiceEvent( serviceName, serviceId, ETMirrorDn );
1215 _ServicesMirrorUpNotified.erase( isn );
1217 break;
1219 default:;
1222 // Callbacks
1223 TNameMappedCallbackM::iterator it = _ServiceUpDnCallbacks.find( serviceName );
1224 if ( it != _ServiceUpDnCallbacks.end() )
1226 // For MirrorUp event requested, call them, if queue them if flagged as synchronized
1227 for ( list<TCallbackArgItemM>::const_iterator it2=(*it).second.begin(); it2!=(*it).second.end(); ++it2 )
1229 const TCallbackArgItemM& cbItem = (*it2);
1230 if ( cbItem.EventType != et )
1231 continue;
1233 if ( cbItem.Synchronized )
1235 _SynchronizedServiceUpDnCallbackQueue.push_back( TCallbackArgItemMExt( cbItem, serviceName, serviceId ) );
1237 else
1239 if ( cbItem.Cb )
1240 cbItem.Cb( serviceName, serviceId, cbItem.Arg );
1245 // For *
1246 for ( uint c=0; c!=_ServiceUpDnUniCallbacks.size(); ++c )
1248 const TCallbackArgItemM& cbItem = _ServiceUpDnUniCallbacks[c];
1249 if ( cbItem.EventType != et )
1250 continue;
1252 if ( cbItem.Synchronized )
1254 _SynchronizedServiceUpDnCallbackQueue.push_back( TCallbackArgItemMExt( cbItem, serviceName, serviceId ) );
1256 else
1258 if ( cbItem.Cb != NULL )
1259 cbItem.Cb( serviceName, serviceId, cbItem.Arg );
1266 * Return true if the specified service is known as "mirror ready"
1268 bool CMirror::serviceHasMirrorReady( NLNET::TServiceId serviceId ) const
1270 return (_ServicesMirrorUpNotified.find( serviceId ) != _ServicesMirrorUpNotified.end());
1275 * Return true if the service reported by cbServiceDown("MS") or cbServiceUp("MS") is the local Mirror Service
1277 bool CMirror::detectLocalMS( TServiceId serviceId, bool upOrDown )
1279 /* // OLD CODE
1280 TSockId hostid;
1281 CCallbackNetBase *cnb = CUnifiedNetwork::getInstance()->getNetBase( (TServiceId)serviceId, hostid );
1282 if ( cnb && (cnb->hostAddress( hostid ).ipAddress() == CInetAddress::localHost().ipAddress()) )*/
1284 if ( CUnifiedNetwork::getInstance()->isServiceLocal( serviceId ) )
1286 if ( upOrDown )
1288 _PropAllocator.setLocalMirrorServiceId( serviceId );
1289 _AwaitingAllMirrorsOnline = true;
1291 // TODO: if MS resync supported, reset closure state back (see below)
1293 else
1295 _PropAllocator.setLocalMirrorServiceId( TServiceId::InvalidId );
1296 nlwarning( "MIRROR: Local Mirror Service %hu is down! (%s)", serviceId.get(), CInetAddress::localHost().ipAddress().c_str() );
1298 // Allow closure, otherwise the service won't quit (no more UMM message received)
1299 // However, the tickReleaseFunc (if provided) must be called.
1300 if ( _TickReleaseFunc )
1302 _IsExecutingSynchronizedCode = true; // prevent assertion failure
1303 _TickReleaseFunc(); // no need of synchronization... the MS is down
1304 _IsExecutingSynchronizedCode = false;
1306 IService::getInstance()->clearForClosure();
1307 IService::getInstance()->exit(); // we're not supporting MS resync at the moment => quit now
1309 return true;
1311 return false;
1316 * Info: display the properties allocated on the mirror
1318 void CMirror::displayProperties( NLMISC::CLog& log ) const
1320 TPropertiesInMirror::const_iterator ipim;
1321 log.displayNL( "Allocated properties:" );
1322 for ( ipim=_PropAllocator._PropertiesInMirror.begin(); ipim!=_PropAllocator._PropertiesInMirror.end(); ++ipim )
1324 if ( GET_PROPERTY_INFO(ipim).Segment )
1326 log.displayNL( "%s: %p", (*ipim).first.c_str(), GET_PROPERTY_INFO(ipim).Segment );
1330 TSDataSets::const_iterator ids;
1331 for ( ids=_SDataSets.begin(); ids!=_SDataSets.end(); ++ids )
1333 log.displayNL( "* DataSet %s (sheetid %s) *", GET_SDATASET(ids).name().c_str(), GET_SDATASET(ids).sheetId().toString().c_str() );
1334 log.displayNL( " EntityId: %p", GET_SDATASET(ids)._PropertyContainer.EntityIdArray.EntityIds );
1335 log.displayNL( " Properties:" );
1336 for ( uint i=0; i!=GET_SDATASET(ids)._PropertyContainer.PropertyValueArrays.size(); ++i )
1338 log.displayNL( " %p", GET_SDATASET(ids)._PropertyContainer.PropertyValueArrays[i].Values );
1345 * Info: display the number of active entities, created by the current mirror
1347 void CMirror::displayEntityTypesOwned( NLMISC::CLog& log ) const
1349 TEntityTypesOwned::const_iterator ieto;
1350 for ( ieto=_EntityTypesOwned.begin(); ieto!=_EntityTypesOwned.end(); ++ieto )
1352 log.displayNL( "* EntityType %hu *", (uint16)(*ieto).first );
1353 log.displayNL( " %d entities (max %d), concerning %u datasets", GET_ENTITY_TYPE_OWNED(ieto).CurrentNb, GET_ENTITY_TYPE_OWNED(ieto).MaxNb, GET_ENTITY_TYPE_OWNED(ieto).DataSetList.size() );
1356 TSDataSets::const_iterator ids;
1357 for ( ids=_SDataSets.begin(); ids!=_SDataSets.end(); ++ids )
1359 log.displayNL( "* DataSet %s *", GET_SDATASET(ids).name().c_str() );
1360 TEntityRangeOfType::const_iterator irot;
1361 for ( irot=GET_SDATASET(ids)._EntityTypesRanges.begin(); irot!=GET_SDATASET(ids)._EntityTypesRanges.end(); ++irot )
1363 log.displayNL( " EntityType %hu", (uint16)(*irot).first );
1364 log.displayNL( " Range: E%d to E%d", GET_ENTITY_TYPE_RANGE(irot).first(), GET_ENTITY_TYPE_RANGE(irot).last() );
1365 log.displayNL( " NextFree: %d; Number of released indexes: %u", GET_ENTITY_TYPE_RANGE(irot).nextFree(), GET_ENTITY_TYPE_RANGE(irot).nbReleasedIndexes() );
1372 * Info: display the contents of the rows corresponding to the entity id
1374 void CMirror::displayRows( const NLMISC::CEntityId& entityId, NLMISC::CLog& log ) const
1376 bool found = false;
1378 log.displayNL("Dumping row for entity %s:", entityId.toString().c_str());
1379 TSDataSets::const_iterator first(_SDataSets.begin()), last(_SDataSets.end());
1380 for (; first != last; ++first)
1382 const CMirroredDataSet &ds = first->second;
1383 TDataSetRow dsr = ds.getDataSetRow(entityId);
1385 if (dsr.isValid())
1387 found = true;
1389 const CSheetId &dataSetId = first->first;
1390 log.displayNL("Dumping properties in dataset '%s':", dataSetId.toString().c_str());
1392 for (sint16 i=0; i<ds.nbProperties(); ++i)
1394 // retrive the i'th propertie
1395 TPropertiesInMirror::const_iterator it(_PropAllocator._PropertiesInMirror.begin());
1397 for (; it != _PropAllocator._PropertiesInMirror.end(); ++it)
1399 if (it->second.PropertyIndex == i && it->second.DataSet == &ds)
1401 // we found the property
1402 ds.displayPropValue( it->first, dsr, i, it->second.getFlagsString().c_str(), log );
1403 break;
1412 TPropertiesInMirror::const_iterator ipim;
1413 log.displayNL( "* Properties of entity %s (values, timestamps...) *", entityId.toString().c_str() );
1414 for ( ipim=_PropAllocator._PropertiesInMirror.begin(); ipim!=_PropAllocator._PropertiesInMirror.end(); ++ipim )
1416 const string& propName = (*ipim).first;
1417 if ( GET_PROPERTY_INFO(ipim).allocated() )
1419 CMirroredDataSet *dataset = GET_PROPERTY_INFO(ipim).DataSet;
1420 if ( dataset )
1422 TDataSetRow entityIndex = dataset->getDataSetRow( entityId );
1423 if ( entityIndex.isValid() )
1425 TPropertyIndex propertyIndex = dataset->getPropertyIndex( propName );
1426 if ( propertyIndex != INVALID_PROPERTY_INDEX ) // skip _EID_...
1428 dataset->displayPropValue( propName, entityIndex, propertyIndex, GET_PROPERTY_INFO(ipim).getFlagsString().c_str(), log );
1430 found = true;
1434 // else // this occurs if the service did not declare the property
1435 // {
1436 // log.displayNL( "%s: property not allocated", propName.c_str() );
1437 // }
1440 if ( ! found )
1442 log.displayNL( "No such entity in any dataset" );
1448 * Debug: change a value from a string
1450 void CMirror::changeValue( const NLMISC::CEntityId& entityId, const std::string& propName, const std::string& valueStr )
1452 bool propok = true;
1453 TPropertiesInMirror::iterator ipim = _PropAllocator._PropertiesInMirror.find( propName );
1454 if ( ipim != _PropAllocator._PropertiesInMirror.end() )
1456 const string& propName = (*ipim).first;
1457 if ( GET_PROPERTY_INFO(ipim).allocated() )
1459 CMirroredDataSet *dataset = GET_PROPERTY_INFO(ipim).DataSet;
1460 if ( dataset )
1462 TDataSetRow entityIndex = dataset->getDataSetRow( entityId );
1463 if ( entityIndex.isValid() )
1465 TPropertyIndex propertyIndex = dataset->getPropertyIndex( propName );
1466 if ( propertyIndex != INVALID_PROPERTY_INDEX )
1468 dataset->setValueFromString( entityIndex, propertyIndex, valueStr );
1470 else propok = false;
1472 else
1474 nlwarning( "MIRROR: Entity %s not found in dataset %s", entityId.toString().c_str(), dataset->name().c_str() );
1477 else propok = false;
1479 else propok = false;
1481 else propok = false;
1482 if ( ! propok )
1484 nlwarning( "MIRROR: Property %s not found or allocated", propName.c_str() );
1490 * Constructor
1492 CMirror::CMirror() :
1493 _PendingEntityTypesRanges(0),
1494 _ReadyL1Callback(NULL),
1495 _NotificationCallback(NULL),
1496 _UserSyncCallback(NULL),
1497 _MirrorAllReady(false),
1498 _MirrorGotReadyLevel1(false),
1499 _MirrorGotReadyLevel2(false),
1500 _ListOfOtherPropertiesReceived(false),
1501 _AwaitingAllMirrorsOnline(false),
1502 _IsExecutingSynchronizedCode(false),
1503 _ClosureRequested(false),
1504 MonitoredEntity(CEntityId::Unknown)
1506 nlassert( ! MirrorInstance ); // singleton check
1507 MirrorInstance = this;
1514 void CMirror::initAfterMirrorUp()
1516 if ( _MirrorAllReady )
1518 if ( _AwaitingAllMirrorsOnline )
1520 // This is when a MS has been shutdown while we were using it! When it cames back,
1521 // send our mirror info to it
1522 resyncMirrorInfo();
1523 _AwaitingAllMirrorsOnline = false; // now if receiving a "mirrors online" msg, it will not mean it's our local MS
1526 else
1528 doInitMirrorLevel1();
1534 * Closure clearance callback
1536 bool cbRequestClosure()
1538 return MirrorInstance->requestClosure();
1545 void CMirror::doInitMirrorLevel1()
1547 if ( _MirrorGotReadyLevel1 )
1548 return;
1550 _AwaitingAllMirrorsOnline = false; // see in initAfterMirrorUp()
1552 nlassert( mirrorServiceIsUp() ); // could fail if the serviceUp from the NS arrives later than the MIRO from the MS!
1553 MIRROR_INFO( "MIRROR: Local Mirror Service found (service %hu)", localMSId().get() );
1555 MIRROR_INFO( "MIRROR: %u datasets used by this service", _DataSetsToLoad.size() );
1556 if ( _SDataSetSheets.empty() )
1558 nlwarning( "MIRROR: No dataset found, check if dataset.packed_sheets and the georges sheets are in the path" );
1561 // Inform the MS about the datasets we know (and this will add us in the client services list of the MS)
1562 CMessage msgoutDATASETS ( "DATASETS" );
1563 msgoutDATASETS.serialCont( _DataSetsToLoad );
1564 msgoutDATASETS.serial( _MTRTag );
1565 CUnifiedNetwork::getInstance()->send( localMSId(), msgoutDATASETS );
1567 // Init the needed CDataSetMS objects from the corresponding TDataSetSheets
1568 uint16 dsIndex = 0;
1569 string skippedDataSets;
1570 sint nbSkipped = 0;
1571 TSDataSetSheets::iterator ism;
1572 for ( ism=_SDataSetSheets.begin(); ism!=_SDataSetSheets.end(); ++ism )
1574 TDataSetSheet& dataSetSheet = (*ism).second;
1575 if ( find( _DataSetsToLoad.begin(), _DataSetsToLoad.end(), dataSetSheet.DataSetName ) != _DataSetsToLoad.end() )
1577 MIRROR_INFO( "MIRROR: \tDataset: %s", dataSetSheet.DataSetName.c_str() );
1579 // Create a CDataSetMS and insert into _SDataSets
1580 CMirroredDataSet newDataSet;
1581 pair<TSDataSets::iterator,bool> res = _SDataSets.insert( make_pair( (*ism).first, newDataSet ) );
1583 // Init CMirroredDataSet object (request allocation of shared memory for entity ids)
1584 GET_SDATASET(res.first).init( (*ism).first, dataSetSheet, &_PropAllocator );
1586 // Insert into _NDataSets
1587 _NDataSets.insert( make_pair( GET_SDATASET(res.first).name(), &GET_SDATASET(res.first) ) );
1589 storeDatasetPtToQuickArray( dsIndex++, &GET_SDATASET(res.first) );
1591 else
1593 skippedDataSets += " " + (*ism).second.DataSetName;
1594 ++nbSkipped;
1598 MIRROR_INFO( "MIRROR: %u datasets loaded", _SDataSets.size() );
1599 MIRROR_INFO( "MIRROR: Skipped datasets:%s", skippedDataSets.empty()?" (none)":skippedDataSets.c_str() );
1600 if ( _DataSetsToLoad.size() + nbSkipped > _SDataSetSheets.size() )
1602 nlwarning( "MIRROR: Some required datasets are missing in sheetid.bin" );
1605 // We don't need the sheet information structures anymore
1606 _SDataSetSheets.clear();
1608 // Call the user callback
1609 _ReadyL1Callback( this );
1611 // Request the list of other properties (that will be cleaned when creating an entity, even if this service did not subscribe them)
1612 CMessage msgoutGop( "GOP" ); // Get Other Properties
1613 CUnifiedNetwork::getInstance()->send( localMSId(), msgoutGop );
1615 // Set closure clearance callback (not in CMirror::init()) because
1616 // launching the service with -Q would never exit if the MS is not up
1617 IService::getInstance()->setClosureClearanceCallback( cbRequestClosure );
1619 _MirrorGotReadyLevel1 = true;
1624 * Send to a reconnecting MS the state of our mirror info
1626 void CMirror::resyncMirrorInfo()
1628 CMessage msgout( "SYNCMI" );
1630 // Resync entity types owned and trackers
1631 uint16 nbdatasets = (uint16)_SDataSets.size();
1632 msgout.serial( nbdatasets );
1633 TSDataSets:: iterator ids;
1634 for ( ids=_SDataSets.begin(); ids!=_SDataSets.end(); ++ids )
1636 msgout.serial( const_cast<string&>(GET_SDATASET(ids).name()) );
1637 GET_SDATASET(ids).serialOutMirrorInfo( msgout );
1640 // Resync properties
1641 _PropAllocator.serialOutMirrorInfo( msgout );
1643 CUnifiedNetwork::getInstance()->send( localMSId(), msgout );
1649 CMirror::~CMirror()
1651 // release();
1655 * Release - mandatory to be called by the user if the object is static,
1656 * no matter if it is called again by the destructor
1658 void CMirror::release()
1660 // Execute mirror release command before destroying all the data
1661 executeMirrorReleaseCommands();
1663 // Release the trackers
1664 TSDataSets::iterator ids;
1665 for ( ids=_SDataSets.begin(); ids!=_SDataSets.end(); ++ids )
1667 CMirroredDataSet& ds = GET_SDATASET(ids);
1669 ds._SelfEntityTrackers[ADDING].release();
1670 ds._SelfEntityTrackers[REMOVING].release();
1672 TSelfPropTrackers::iterator isp;
1673 for ( isp=ds._SelfPropTrackers.begin(); isp!=ds._SelfPropTrackers.end(); ++isp )
1674 (*isp).release();
1676 TTrackerListForEnt::iterator iet;
1677 for ( iet=ds._EntityTrackers[ADDING].begin(); iet!=ds._EntityTrackers[ADDING].end(); ++iet )
1678 (*iet).release();
1679 for ( iet=ds._EntityTrackers[REMOVING].begin(); iet!=ds._EntityTrackers[REMOVING].end(); ++iet )
1680 (*iet).release();
1682 TTrackersLists::iterator itl;
1683 for ( itl=ds._PropTrackers.begin(); itl!=ds._PropTrackers.end(); ++itl )
1685 TTrackerListForProp::iterator ipt;
1686 for ( ipt=(*itl).begin(); ipt!=(*itl).end(); ++ipt )
1688 (*ipt).release();
1695 #ifdef NL_DEBUG
1697 // Helper functor for testMirrorReadyStatus
1698 class PropIndexIs : public binary_function< CChangeTrackerClientProp, TPropertyIndex, bool >
1700 public:
1701 bool operator() ( const CChangeTrackerClientProp& tracker, TPropertyIndex propIndex ) const
1703 return (tracker.propIndex() == propIndex);
1707 #endif
1711 * Set _MirrorAllReady to true if the conditions are met
1713 void CMirror::testMirrorReadyStatus()
1715 if ( _MirrorAllReady )
1716 return;
1718 // Check if mirrors and range manager are online
1719 if ( ! _MirrorGotReadyLevel1 )
1721 MIRROR_DEBUG( "MIRROR: Still waiting for mirrors and range manager to be online" );
1722 return;
1725 // Check that we have received the first game cycle
1726 if ( CTickEventHandler::getGameCycle() == 0 )
1728 return;
1731 // Check if mirror service is up and entity ranges ready
1732 if ( ! mirrorIsUp() )
1734 //MIRROR_DEBUG( "MIRROR: Still waiting for entity ranges ready" );
1735 return;
1738 // For each dataset
1739 TNDataSets::iterator ids;
1740 for ( ids=_NDataSets.begin(); ids!=_NDataSets.end(); ++ids )
1742 // Test if SC_EI was received
1743 if ( ! GET_NDATASET(ids)._IsEntityNotificationEnabled )
1745 MIRROR_DEBUG( "MIRROR: Still waiting for SC_EI" );
1746 return;
1749 // Test if entity ids are allocated
1750 if ( ! propIsAllocated( string("_EId_") + (*ids).first ) )
1752 MIRROR_DEBUG( "MIRROR: %s: Still waiting for allocation of entity ids", (*ids).first.c_str() );
1753 return;
1756 // Test if the self entity trackers are allocated
1757 if ( ! GET_NDATASET(ids)._SelfEntityTrackers[ADDING].isAllocated() && GET_NDATASET(ids)._SelfEntityTrackers[REMOVING].isAllocated() ) // note: if one is allocated, the other one should be
1759 MIRROR_DEBUG( "MIRROR: %s: Still waiting for self entity trackers", (*ids).first.c_str() );
1760 return;
1764 // For each declared property
1765 TPropertiesInMirror::iterator ipm;
1766 for ( ipm=_PropAllocator._PropertiesInMirror.begin(); ipm!=_PropAllocator._PropertiesInMirror.end(); ++ipm )
1768 TPropertyInfo& propinfo = GET_PROPERTY_INFO(ipm);
1770 // Test if a declared property is still pending (i.e. not allocated yet)
1771 if ( propinfo.Pending )
1773 MIRROR_DEBUG( "MIRROR: Still waiting for allocation of property %s/%hd", propinfo.DataSet->name().c_str(), propinfo.PropertyIndex );
1774 return;
1777 // Among the allocated properties, test the prop trackers
1778 if ( propinfo.allocated() )
1780 // Test if the prop tracker list has been received
1781 if ( propinfo.PropTrackersPending && (propinfo.PropertyIndex != INVALID_PROPERTY_INDEX) ) // ignore "entity id" propinfo
1783 MIRROR_DEBUG( "MIRROR: Still waiting for property tracker list of %s/%hd", propinfo.DataSet->name().c_str(), propinfo.PropertyIndex );
1784 return;
1787 #ifdef NL_DEBUG
1788 // Additional tests
1789 if ( propinfo.flagNotifyChanges() && (!propinfo.flagGroupNotifiedByOtherProp()) )
1791 nlassert( propinfo.DataSet );
1792 TSelfPropTrackers::iterator ipt = find_if( propinfo.DataSet->_SelfPropTrackers.begin(), propinfo.DataSet->_SelfPropTrackers.end(), bind2nd( PropIndexIs(), propinfo.PropertyIndex ) );
1793 if ( ipt == propinfo.DataSet->_SelfPropTrackers.end() )
1795 nlwarning( "MIRROR: Property %s/%hd with notification of changes: selfprop tracker not received", propinfo.DataSet->name().c_str(), propinfo.PropertyIndex );
1798 #endif
1802 // Expect the list of non-subscribed properties allocated on the local MS
1803 if ( ! _ListOfOtherPropertiesReceived )
1805 MIRROR_DEBUG( "MIRROR: Still waiting for LOP" );
1806 return;
1809 // Success
1810 if ( ! _MirrorGotReadyLevel2 )
1812 // Broadcast our ready state to all other services!
1813 CMessage msgout( "SMIRUB" ); // 'Service has MIRror system Up Broadcast'
1814 pushEntityRanges( msgout );
1815 sendMessageViaMirrorToAll( msgout ); // via mirror to ensure that MARCS was sent to all remote MS before their client services get SMIRU (otherwise their potential messages couldn't be routed through their MS)
1817 nlinfo( "Mirror system ready, %s", (_MTRTag==AllTag) ? "all MTR Tags" : toString( "MTR Tag=%d", _MTRTag ).c_str() );
1819 if ( ! _NotificationCallback )
1821 nlerror( "No notification callback has been set, entities won't be updated in the mirror!" );
1824 // The start callbacks & commands will be executed when receiving the first UMM
1825 // because they need to be synchronized with the MS
1827 _MirrorGotReadyLevel2 = true;
1833 * Execute start callbacks & commands
1835 void CMirror::executeMirrorReady2CallbacksAndStartCommands()
1837 // Ready level2 callbacks
1838 vector<TMirrorReadyCallback>::const_iterator icb;
1839 for ( icb=_ReadyL2Callbacks.begin(); icb!=_ReadyL2Callbacks.end(); ++icb )
1841 (*icb)(this);
1844 // Start commands when mirror ready
1845 string cmdRoot("StartCommandsWhenMirrorReady");
1846 vector<string> posts;
1848 if (IService::getInstance()->haveArg('m'))
1850 string s = IService::getInstance()->getArg('m');
1851 NLMISC::explode(s, string(":"), posts, false);
1853 else
1854 // add an empty string
1855 posts.push_back(string());
1857 CConfigFile::CVar *var;
1858 for (uint i=0; i<posts.size(); ++i)
1860 string varName = cmdRoot + posts[i];
1861 if ((var = IService::getInstance()->ConfigFile.getVarPtr (varName)) != NULL)
1863 MIRROR_INFO( "MIRROR: Executing '%s'...", varName.c_str() );
1864 for (uint i = 0; i < var->size(); i++)
1866 ICommand::execute (var->asString(i), *InfoLog);
1874 * Execute start callbacks & commands
1876 void CMirror::executeMirrorReleaseCommands()
1878 // Start commands when mirror ready
1879 string cmdRoot("CommandsWhenMirrorRelease");
1880 vector<string> posts;
1882 if (IService::getInstance()->haveArg('m'))
1884 string s = IService::getInstance()->getArg('m');
1885 NLMISC::explode(s, string(":"), posts, false);
1887 else
1888 // add an empty string
1889 posts.push_back(string());
1891 CConfigFile::CVar *var;
1892 for (uint i=0; i<posts.size(); ++i)
1894 string varName = cmdRoot + posts[i];
1895 if ((var = IService::getInstance()->ConfigFile.getVarPtr (varName)) != NULL)
1897 MIRROR_INFO( "MIRROR: Executing '%s'...", varName.c_str() );
1898 for (uint i = 0; i < var->size(); i++)
1900 ICommand::execute (var->asString(i), *InfoLog);
1911 void CMirror::pushEntityRanges( CMessage& msgout )
1913 uint32 nbDatasets = (uint32)_SDataSets.size();
1914 msgout.serial( nbDatasets );
1915 for ( TSDataSets::iterator ids=_SDataSets.begin(); ids!=_SDataSets.end(); ++ids )
1917 msgout.serial( const_cast<CSheetId&>((*ids).first) );
1918 msgout.serialCont( (*ids).second._EntityTypesRanges );
1924 * Global routine to be called by update
1926 void CMirror::updateWatches()
1928 if ( mirrorIsReady() )
1930 for (uint i=0; i!=sizeof(mWatchStrings)/sizeof(mWatchStrings[0]); ++i )
1932 if ( WatchedPropValues[i] )
1933 getPropValueStr( WatchedPropValues[i]->EntityId, WatchedPropValues[i]->PropName, *(mWatchStrings[i]) );
1934 else
1935 mWatchStrings[i]->clear();
1944 void CMirror::getPropValueStr( const NLMISC::CEntityId& entityId, const std::string& propName, std::string& result ) const
1946 bool propok = true;
1947 TPropertiesInMirror::const_iterator ipim = _PropAllocator._PropertiesInMirror.find( propName );
1948 if ( ipim != _PropAllocator._PropertiesInMirror.end() )
1950 const string& propName = (*ipim).first;
1951 if ( GET_PROPERTY_INFO(ipim).allocated() )
1953 CMirroredDataSet *dataset = GET_PROPERTY_INFO(ipim).DataSet;
1954 if ( dataset )
1956 TDataSetRow entityIndex = dataset->getDataSetRow( entityId );
1957 if ( entityIndex.isValid() )
1959 TPropertyIndex propertyIndex = dataset->getPropertyIndex( propName );
1960 if ( propertyIndex != INVALID_PROPERTY_INDEX )
1962 dataset->getValueToString( entityIndex, propertyIndex, result );
1963 result = toString( "%s,%s=%s", entityId.toString().c_str(), propName.c_str(), result.c_str() );
1965 else propok = false;
1967 else
1969 result = toString( "<%s not found>", entityId.toString().c_str() );
1972 else propok = false;
1974 else propok = false;
1976 else propok = false;
1977 if ( ! propok )
1979 result = toString( "<%s not found>", propName.c_str() );
1985 * Provide a callback that will be called the first time mirrorIsReady() return true.
1986 * Note: immediately after having called all the callbacks passed to this method, the
1987 * commands founds in StartCommandsWhenMirrorReady (in the config file) will be executed.
1989 void CMirror::addCallbackWhenMirrorReadyForUse( TMirrorReadyCallback cbLevel2 )
1991 nlassert( cbLevel2 );
1992 _ReadyL2Callbacks.push_back( cbLevel2 );
1997 * Send the transport class to a specified service using the service id
1999 //void CMirrorTransportClass::send( uint8 sid )
2001 /*// Experimental: forwarding without copy
2002 CMessage& msgout = write();
2003 msgout.changeType( "MT1MSG" ); // same size as CT_MSG
2004 msgout.serial( sid );
2005 CUnifiedNetwork::getInstance()->send( MirrorInstance->localMSId(), msgout );*/
2007 /*CMessage msgout( "MTC_MSG" );
2008 CMessage& ctmsg = write();
2009 msgout.serialBuffer( const_cast<uint8*>(ctmsg.buffer()+ctmsg.getHeaderSize()), ctmsg.length()-ctmsg.getHeaderSize() );
2010 CUnifiedNetwork::getInstance()->send( sid, msgout );
2011 nldebug( "%u: Sending MTC to service %hu", CTickEventHandler::getGameCycle(), (uint16)sid );*/
2013 // See in class declaration
2018 * Send the transport class to a specified service using the service name
2020 //void CMirrorTransportClass::send( std::string serviceName )
2022 /*// Experimental: forwarding without copy
2023 CMessage& msgout = write();
2024 msgout.changeType( "MT2MSG" ); // same size as CT_MSG
2025 msgout.serial( serviceName );
2026 uint8 sns = serviceName.size();
2027 msgout.serial( sns );
2028 CUnifiedNetwork::getInstance()->send( MirrorInstance->localMSId(), msgout );*/
2030 /*CMessage msgout( "MTC_MSG" );
2031 CMessage& ctmsg = write();
2032 msgout.serialBuffer( const_cast<uint8*>(ctmsg.buffer()+ctmsg.getHeaderSize()), ctmsg.length()-ctmsg.getHeaderSize() );
2033 CUnifiedNetwork::getInstance()->send( serviceName, msgout );
2034 nldebug( "%u: Sending MTC to %s", CTickEventHandler::getGameCycle(), serviceName.c_str() );*/
2036 // See in class declaration
2040 // Standard transport class callback
2041 //extern void cbTCReceiveMessage (CMessage &msgin, const string &name, uint16 sid);
2045 * Mirror transport class callback
2047 /*void cbMirrorTCReceiveMessage( CMessage &msgin, const string &name, uint16 sid )
2049 //nldebug( "Receiving MTC from service %hu", sid );
2050 MirrorInstance->_BufferedMTC.push_back( make_pair(msgin,sid) );
2055 * The mirror transport class are applied at the end of the service "tick update" method,
2056 * to ensure all entities have been processed.
2057 * NOW this is replaced by the message chain provided in the message UMM, and the notification callback
2058 * is called before.
2060 /*void CMirror::applyBufferedTransportClasses()
2062 CBufferedMirrorTCs::const_iterator it;
2063 for ( it=_BufferedMTC.begin(); it!=_BufferedMTC.end(); ++it )
2065 nldebug( "%u: Applying MTC from service %hu", CTickEventHandler::getGameCycle(), (*it).second );
2066 //string senderName; // blank because not used
2067 cbTCReceiveMessage( const_cast<CMessage&>((*it).first), "", (*it).second ); // change the sid: sender instead of forwarder MS
2069 _BufferedMTC.clear();
2074 * Receive the UMM Message (callback)
2076 void cbUpdateMirrorAndReceiveMessages( CMessage &msgin, const string&, TServiceId )
2078 MirrorInstance->updateMirrorAndReceiveMessages( msgin );
2081 // update the mirror counter var
2082 MirrorCallbackCount = TotalMirrorCallbackCalled;
2083 MirrorCallbackTime = TimeInMirrorCallback;
2085 // reset the counters
2086 TimeInMirrorCallback = 0;
2087 TotalMirrorCallbackCalled = 0;
2092 * Receive the UMM Message
2094 void CMirror::updateMirrorAndReceiveMessages( CMessage& msgin )
2096 _IsExecutingSynchronizedCode = true;
2098 // Synchronized release code
2099 if ( _ClosureRequested )
2101 if ( _TickReleaseFunc ) // should be non-null when we get here anyway
2102 _TickReleaseFunc();
2103 IService::getInstance()->clearForClosure();
2104 return;
2107 // Start callbacks & commands
2108 if ( (!_MirrorAllReady) && _MirrorGotReadyLevel2 )
2110 _MirrorAllReady = true;
2111 executeMirrorReady2CallbacksAndStartCommands();
2112 IService::getInstance()->clearCurrentStatus("WaitingMirrorReady");
2115 TAccurateTime TimeBeforeUMM = getAccurateTime();
2117 // Synchronized Service Up / Service Mirror Up / Service Down / Service Mirror Down callbacks
2118 for ( std::vector<TCallbackArgItemMExt>::const_iterator iscq=_SynchronizedServiceUpDnCallbackQueue.begin(); iscq!=_SynchronizedServiceUpDnCallbackQueue.end(); ++iscq )
2120 const TCallbackArgItemMExt& cbItem = (*iscq);
2121 if ( cbItem.Cb )
2122 cbItem.Cb( cbItem.ServiceName, cbItem.ServiceId, cbItem.Arg );
2124 _SynchronizedServiceUpDnCallbackQueue.clear();
2126 // Trigger the notification of mirror changes
2127 if ( mirrorIsReady() && _NotificationCallback )
2129 _NotificationCallback();
2131 TAccurateTime TimeBeforeReceiveMsgViaMirror = getAccurateTime();
2132 ProcessMirrorUpdatesSpeed = (sint16)accTimeToMs( TimeBeforeReceiveMsgViaMirror - TimeBeforeUMM );
2134 // Apply the messages
2135 uint32 nbMsgs;
2136 msgin.serial( nbMsgs );
2137 if ( mirrorIsReady() )
2139 uint32 i;
2140 for ( i=0; i!=nbMsgs; ++i )
2142 // --------------------------------------------------------------------------------------------------------------
2143 // timer code added here by Sadge
2144 // copied feom unified_network.cpp
2145 static map<string, CHTimer> timers;
2146 map<string, CHTimer>::iterator it;
2149 H_AUTO(L5UCHTimerOverhead);
2150 string callbackName = "USRCB_" + msgin.getName();
2151 it = timers.find(callbackName);
2152 if(it == timers.end())
2154 it = timers.insert(make_pair(callbackName, CHTimer(NULL))).first;
2155 (*it).second.setName((*it).first.c_str());
2158 // --------------------------------------------------------------------------------------------------------------
2160 (*it).second.before();
2161 TServiceId senderId;
2162 msgin.serial( senderId );
2163 sint32 msgSize, msgPos;
2164 msgin.serial( msgSize );
2166 TTime before = CTime::getLocalTime();
2168 // Modify msgin so that the upcoming submessages looks like a normal message for the corresponding callback
2169 msgPos = msgin.getPos();
2170 string name = msgin.lockSubMessage( msgSize ); // fake the length for the callback
2171 TUnifiedMsgCallback cb = CUnifiedNetwork::getInstance()->findCallback( name );
2172 string sn = CUnifiedNetwork::getInstance()->getServiceName( senderId );
2173 if ( cb && !sn.empty())
2174 cb( msgin, sn, senderId );
2175 msgin.unlockSubMessage(); // set original length back and skip sub message
2176 (*it).second.after();
2178 TTime after = CTime::getLocalTime();
2179 // sum the time used to do callback
2180 TimeInMirrorCallback += uint32(after-before);
2181 TotalMirrorCallbackCalled++;
2184 else if ( nbMsgs != 0 )
2186 string names;
2187 uint32 i;
2188 for ( i=0; i!=nbMsgs; ++i )
2190 TServiceId senderId;
2191 msgin.serial( senderId );
2192 sint32 msgSize, msgPos;
2193 msgin.serial( msgSize );
2194 msgPos = msgin.getPos();
2195 std::string messageName = msgin.lockSubMessage( msgSize );
2197 // Get the class name if the message is a Transport Class
2198 if (messageName == "CT_MSG")
2200 string className;
2201 CTransportClass::readHeader(msgin, className);
2202 messageName += "__" + className;
2205 // Report the name of the message
2206 if (messageName != "SMIRUB") // keep quiet if receiving a SMIRUB (Service Mirror Up Broadcast) at this time (info will be sent by SMIRUR instead)
2207 names += messageName + " ";
2209 msgin.unlockSubMessage(); // skip sub message
2211 if (!names.empty())
2213 // This should not occur much anymore, as sendMessageViaMirror() no longer sends if !serviceHasMirrorReady()
2214 nlwarning( "MIRROR: MSG: %u messages received via MS before mirror is ready, discarded: %s", nbMsgs, names.c_str() );
2218 _IsExecutingSynchronizedCode = false;
2220 TAccurateTime TimeAfterUMM = getAccurateTime();
2221 ReceiveMessagesViaMirrorSpeed = sint16(accTimeToMs( TimeAfterUMM - TimeBeforeReceiveMsgViaMirror ));
2222 TotalSpeedLoop = accTimeToMs( TimeAfterUMM - TimeBeforeTickUpdate );
2228 void cbscanAndResyncEntitiesExceptIgnored( CMessage &msgin, const string&, TServiceId )
2230 vector<TServiceId8> creatorIdsToIgnore;
2231 msgin.serialCont( creatorIdsToIgnore );
2232 MirrorInstance->scanAndResyncEntitiesExceptIgnored( creatorIdsToIgnore );
2239 void cbApplyListOfOtherProperties( CMessage &msgin, const string&, TServiceId )
2241 uint32 nbProps;
2242 msgin.serial( nbProps );
2243 MirrorInstance->applyListOfOtherProperties( msgin, nbProps, "LOP", true );
2250 void cbAddListOfOtherProperties( CMessage &msgin, const string&, TServiceId )
2252 MirrorInstance->applyListOfOtherProperties( msgin, 1, "AOP", false );
2257 * Callback array
2259 TUnifiedCallbackItem MirrorCbArray [] =
2261 { "RAP", cbRecvSMIdToAccessPropertySegment },
2262 { "RG", cbRecvRangesForEntityType },
2263 { "ATE", cbRecvAddEntityTracker },
2264 { "ATP", cbRecvAddPropTracker },
2265 { "ACKATE", cbRecvAcknowledgeAddEntityTracker },
2266 { "ACKATP", cbRecvAcknowledgeAddPropTracker },
2267 { "RT", cbReleaseTrackers },
2268 { "MIRO", cbAllMirrorsOnline },
2269 { "SMIRUB", cbRecvServiceHasMirrorReadyBroadcast },
2270 { "SMIRUR", cbRecvServiceHasMirrorReadyReply },
2271 { "UMM", cbUpdateMirrorAndReceiveMessages },
2272 { "LOP", cbApplyListOfOtherProperties },
2273 { "AOP", cbAddListOfOtherProperties },
2274 { "SC_EI", cbscanAndResyncEntitiesExceptIgnored }
2276 const sint NB_MIRROR_CALLBACKS = sizeof(MirrorCbArray)/sizeof(MirrorCbArray[0]);
2282 void cbTickUpdateFunc()
2284 MirrorInstance->onTick();
2289 * Initialize. Call before doing any mirror-related action.
2291 void CMirror::init( std::vector<std::string>& dataSetsToLoad,
2292 TMirrorReadyCallback cbLevel1,
2293 TNotificationCallback tickUpdateFunc,
2294 TNotificationCallback tickSyncFunc,
2295 TNotificationCallback tickReleaseFunc,
2296 TMTRTag tag )
2298 IService::getInstance()->setCurrentStatus("WaitingMirrorReady");
2300 _DataSetsToLoad = dataSetsToLoad;
2301 _ReadyL1Callback = cbLevel1;
2302 nlassert( _ReadyL1Callback );
2304 bool expediteTock = false;
2305 CConfigFile::CVar *varExpTock = IService::getInstance()->ConfigFile.getVarPtr( "ExpediteTOCK" );
2306 if ( varExpTock )
2307 expediteTock = (varExpTock->asInt() == 1);
2308 _TickUpdateFunc = tickUpdateFunc;
2309 _TickReleaseFunc = tickReleaseFunc;
2310 CTickEventHandler::init( cbTickUpdateFunc, tickSyncFunc, expediteTock );
2311 _UserSyncCallback = CTickEventHandler::setSyncCallback( cbSyncGameCycle, true );
2313 // Register to ServiceUp and ServiceDown
2314 CUnifiedNetwork::getInstance()->setServiceUpCallback( "MS", cbMSUpDn, (void*)true );
2315 CUnifiedNetwork::getInstance()->setServiceDownCallback( "MS", cbMSUpDn, (void*)false );
2317 // in both of the following lines we have an integer value which we want to pass in the 'void*' third parameter to SetService....Callback
2318 // for this we use pointer arithmetic with char* pointers in order to avoid compiler warnings
2319 CUnifiedNetwork::getInstance()->setServiceUpCallback( "*", cbAnyServiceUpDn, (void*)((char*)NULL+ETServiceUp) );
2320 CUnifiedNetwork::getInstance()->setServiceDownCallback( "*", cbAnyServiceUpDn, (void*)((char*)NULL+ETServiceDn) );
2322 // Init mirror callbacks
2323 CUnifiedNetwork::getInstance()->addCallbackArray( MirrorCbArray, NB_MIRROR_CALLBACKS );
2325 // Load the sheets of the datasets
2326 // if the 'GeorgePaths' config file var exists then we try to perform a mini-scan for sheet files
2327 if (IService::isServiceInitialized() && (IService::getInstance()->ConfigFile.getVarPtr(std::string("GeorgePaths"))!=NULL))
2329 loadForm("dataset", IService::getInstance()->WriteFilesDirectory.toString()+"datasets.packed_sheets", _SDataSetSheets, false, false);
2332 // if we haven't succeeded in minimal scan (or 'GeorgePaths' wasn't found in config file) then perform standard scan
2333 if (_SDataSetSheets.empty())
2335 loadForm("dataset", IService::getInstance()->WriteFilesDirectory.toString()+"datasets.packed_sheets", _SDataSetSheets, true);
2338 // Set the tag
2339 nlassert( (tag >= AllTag) && (tag != ExcludedTag) );
2340 _MTRTag = tag;
2342 MIRROR_INFO( "MIRROR: MV%s - %s - Expecting detection of local Mirror Service...", MirrorVersion.c_str(), (tag==AllTag) ? "No Tag" : NLMISC::toString("Tag=%hu", (uint16)_MTRTag).c_str() );
2343 #ifdef STORE_CHANGE_SERVICEIDS
2344 MIRROR_INFO( "MIRROR: > Storing change service ids ENABLED" );
2345 #endif
2346 #ifdef CHECK_DATASETROW_VALIDITY
2347 MIRROR_INFO( "MIRROR: > Checking datasetrow validity ENABLED" );
2348 #endif
2350 #ifdef FAST_MIRROR
2351 MIRROR_INFO( "MIRROR: > Fast mirror mode ENABLED" );
2352 #else
2353 MIRROR_INFO( "MIRROR: > Fast mirror mode DISABLED" );
2354 #endif
2360 * Closure clearance callback
2362 bool CMirror::requestClosure()
2364 _ClosureRequested = true;
2365 return (_TickReleaseFunc == NULL); // quit immediately if there is no tick release callback
2370 * Execute user callback when receiving first game cycle
2372 void CMirror::userSyncCallback()
2374 if( _UserSyncCallback )
2375 _UserSyncCallback();
2382 void sendMessageViaMirror( const std::string& destServiceName, CMessage& msgout )
2384 if (MirrorInstance->localMSId()==TServiceId::InvalidId)
2386 nlwarning("Ignoring attempt to send a message via the mirror before mirror ready: %s",msgout.getName().c_str());
2387 return;
2390 CMessage carrierMsgout( "FWDMSG" ); // to (possibly) multiple services
2391 /*vector<uint16> vec = CUnifiedNetwork::getInstance()->getConnectionList(), vecDest( vec.size() );
2392 vector<uint16>::const_iterator it;
2393 for ( it=vec.begin(); it!=vec.end(); ++it )
2395 if ( CUnifiedNetwork::getInstance()->getServiceName( *it ) == destServiceName )
2396 vecDest.push_back( *it );
2398 uint8 counter = (uint8)vecDest.size();
2399 carrierMsgout.serial( counter );
2400 for ( it=vecDest.begin(); it!=vecDest.end(); ++it )
2402 carrierMsgout.serialCont( *it );
2404 uint8 counter = 0;
2405 sint32 counterPos = carrierMsgout.reserve( 1 );
2406 vector<TServiceId> vec = CUnifiedNetwork::getInstance()->getConnectionList();
2407 for ( vector<TServiceId>::const_iterator it=vec.begin(); it!=vec.end(); ++it )
2409 if ( CUnifiedNetwork::getInstance()->getServiceName( *it ) == destServiceName )
2411 // Exclude services that are not known as "mirror ready" (would produce a warning in the MS)
2412 if ( ! MirrorInstance->serviceHasMirrorReady( *it ) )
2414 nlinfo("Discarding service %s-%hu in sendMessageViaMirror because not 'mirror ready'", destServiceName.c_str(), (*it).get() );
2415 continue;
2417 nlWrite(carrierMsgout, serial, (*it) );
2418 ++counter;
2421 if ( counter != 0 )
2423 carrierMsgout.poke( counter, counterPos );
2424 carrierMsgout.serialBuffer( const_cast<uint8*>(msgout.buffer()), msgout.length() );
2425 CUnifiedNetwork::getInstance()->send( MirrorInstance->localMSId(), carrierMsgout );
2427 else
2429 // Don't send if no corresponding service online (0 would be interpreted as a broadcast!)
2430 string msgName;
2431 msgout.invert();
2432 getNameOfMessageOrTransportClass( msgout, msgName );
2433 MIRROR_DEBUG( "MIRROR: MSG: No service %s is online (%s)", destServiceName.c_str(), msgName.c_str() );
2441 void sendMessageViaMirror( TServiceId destServiceId, CMessage& msgout )
2443 if (MirrorInstance->localMSId()==TServiceId::InvalidId)
2445 nlwarning("Ignoring attempt to send a message via the mirror before mirror ready: %s",msgout.getName().c_str());
2446 return;
2449 CMessage carrierMsgout( "FWDMSG" ); // to single service
2450 uint8 counter = 1;
2451 carrierMsgout.serial( counter );
2452 carrierMsgout.serial( destServiceId );
2453 carrierMsgout.serialBuffer( const_cast<uint8*>(msgout.buffer()), msgout.length() );
2454 CUnifiedNetwork::getInstance()->send( MirrorInstance->localMSId(), carrierMsgout );
2461 void sendMessageViaMirrorToAll( NLNET::CMessage& msgout )
2463 if (MirrorInstance->localMSId()==TServiceId::InvalidId)
2465 nlwarning("Ignoring attempt to send a message via the mirror before mirror ready: %s",msgout.getName().c_str());
2466 return;
2469 CMessage carrierMsgout( "FWDMSG" );
2470 uint8 counter = MSG_BROADCAST;
2471 carrierMsgout.serial( counter );
2472 carrierMsgout.serialBuffer( const_cast<uint8*>(msgout.buffer()), msgout.length() );
2473 CUnifiedNetwork::getInstance()->send( MirrorInstance->localMSId(), carrierMsgout );
2478 * displayMirrorRow command
2480 NLMISC_CATEGORISED_COMMAND(mirror, displayMirrorRow, "Display the contents of the mirror for an entity (all datasets)", "[<CEntityId> | <dataset> <entityIndex>]" )
2482 CEntityId id;
2483 switch( args.size() )
2485 case 0: id = MirrorInstance->MonitoredEntity; break;
2486 case 1: id.fromString( args[0].c_str() ); break;
2487 case 2:
2491 CMirroredDataSet& dataset = MirrorInstance->getDataSet( args[0] );
2492 TDataSetIndex entityIndex;
2493 fromString(args[1], entityIndex);
2494 if ( (entityIndex < dataset.maxNbRows()) && (entityIndex!=INVALID_DATASET_INDEX) )
2496 id = dataset.getEntityId( dataset.getCurrentDataSetRow( entityIndex ) );
2497 log.displayNL( "%s", id.toString().c_str() );
2499 else
2501 log.displayNL( "Invalid entity index provided" );
2502 return true;
2505 catch (const EMirror&)
2507 log.displayNL( "Dataset not found" );
2508 return true;
2510 break;
2512 default: return false;
2515 MirrorInstance->displayRows( id, log );
2516 return true;
2521 * setMirrorValue command
2523 NLMISC_CATEGORISED_COMMAND(mirror, setMirrorValue, "Change a value in the mirror", "[<CEntityId>] <propName> <value>" )
2525 CEntityId id;
2526 switch( args.size() )
2528 case 2: id = MirrorInstance->MonitoredEntity; break;
2529 case 3: id.fromString( args[0].c_str() ); break;
2531 default: return false;
2534 const size_t nargs = args.size();
2535 const char *afld = args[nargs-2].c_str();
2536 const char *aval = args[nargs-1].c_str();
2537 static char sval[64];
2539 // for equivalence with display of mirror values in ai_server
2540 // if input value is in form AAAA:BBBB:CCCC:DDDD -> treat as 64bit 0xDDDDCCCCBBBBAAAA
2541 std::string::size_type i = args[nargs-1].find( ':', 0 );
2542 if( std::string::npos != i )
2544 uint w0, w1, w2, w3;
2545 sscanf( aval, "%x:%x:%x:%x", &w0, &w1, &w2, &w3 );
2546 uint64 u = static_cast<uint16>(w3);
2547 u <<= 16;
2548 u |= static_cast<uint16>(w2);
2549 u <<= 16;
2550 u |= static_cast<uint16>(w1);
2551 u <<= 16;
2552 u |= static_cast<uint16>(w0);
2553 string strval = NLMISC::toString( u );
2554 // _ui64toa( u, &sval[0], 10 ); // Microsoft only
2555 aval = strval.c_str();
2558 MirrorInstance->changeValue( id, afld, aval );
2559 return true;
2564 * addMirrorWatch command
2566 NLMISC_CATEGORISED_COMMAND(mirror, addMirrorWatch, "Watch a value of a property of an entity", "[<CEntityId>] <propName>" )
2568 if ( args.size() < 1 )
2569 return false;
2570 CEntityId id;
2571 if( args.size() == 2 )
2572 id.fromString( args[0].c_str() );
2573 else
2575 id = MirrorInstance->MonitoredEntity;
2576 log.displayNL( "Only propName provided; using monitored entity %s, propName %s", id.toString().c_str(), args[args.size()-1].c_str() );
2579 uint i = 0;
2580 bool found = false;
2581 while ( ! found )
2583 if ( i > 9 )
2585 log.displayNL( "No more watch available" );
2586 return true;
2588 if ( ! WatchedPropValues[i] )
2590 WatchedPropValues[i] = new CEntityAndPropName();
2591 WatchedPropValues[i]->EntityId = id;
2592 WatchedPropValues[i]->PropName = args[args.size()-1];
2593 log.displayNL( "Watch %u set", i );
2594 found = true;
2596 ++i;
2598 return true;
2603 * setMirrorWatch command
2605 NLMISC_CATEGORISED_COMMAND(mirror, setMirrorWatch, "Set a particular watch of a property value", "watchNb [<CEntityId>] <propName>" )
2607 if ( args.size() < 2 )
2608 return false;
2609 CEntityId id;
2610 if( args.size() == 3 )
2611 id.fromString( args[1].c_str() );
2612 else
2614 id = MirrorInstance->MonitoredEntity;
2615 log.displayNL( "Only propName provided; using monitored entity %s", id.toString().c_str() );
2618 uint i;
2619 fromString(args[0], i);
2620 if ( ! WatchedPropValues[i] )
2622 WatchedPropValues[i] = new CEntityAndPropName();
2623 WatchedPropValues[i]->EntityId = id;
2624 WatchedPropValues[i]->PropName = args[args.size()-1];
2625 log.displayNL( "Watch %u set", i );
2627 return true;
2632 * removeMirrorWatch
2634 NLMISC_CATEGORISED_COMMAND(mirror, removeMirrorWatch, "Remove a particular watch", "watchNb" )
2636 if ( args.size() < 1 )
2637 return false;
2638 uint i;
2639 fromString(args[0], i);
2640 if ( WatchedPropValues[i] )
2642 delete WatchedPropValues[i];
2643 WatchedPropValues[i] = NULL;
2644 mWatchStrings[i]->clear();
2645 log.displayNL( "Watch %u removed", i );
2647 else
2648 log.displayNL( "Watch %u not found", i );
2649 return true;
2654 * displayMirrorProperties command
2656 NLMISC_CATEGORISED_COMMAND(mirror, displayMirrorProperties, "Display the properties allocated in the mirror", "" )
2658 MirrorInstance->displayProperties( log );
2659 return true;
2664 * displayMirrorEntities command
2666 NLMISC_CATEGORISED_COMMAND(mirror, displayMirrorEntities, "Display all of part of the entities known in a dataset (255=all)", "[<dataset> [<sortByDataSetRow=0> [<onlyEntityType=255> [<onlyCreatorId=255> [<onlyDynamicId=255>]]]]]" )
2668 CMirroredDataSet *dataset = NULL;
2669 bool sortByDataSetRow = false;
2670 uint8 onlyEntityType = 0xFF;
2671 uint8 onlyCreatorId = 0xFF;
2672 uint8 onlyDynamicId = 0xFF;
2673 if ( args.empty() )
2675 if ( MirrorInstance->dsBegin() == MirrorInstance->dsEnd() )
2677 log.displayNL( "No dataset found" );
2678 return true;
2680 dataset = &GET_NDATASET(MirrorInstance->dsBegin());
2681 log.displayNL( "Displaying only first dataset" );
2683 else try
2685 dataset = &MirrorInstance->getDataSet( args[0] );
2686 if ( args.size() > 1 )
2688 sortByDataSetRow = (args[1] == "1");
2689 if ( args.size() > 2 )
2691 fromString(args[2], onlyEntityType);
2692 if ( args.size() > 3 )
2694 fromString(args[3], onlyCreatorId);
2695 if ( args.size() > 4 )
2697 fromString(args[4], onlyDynamicId);
2703 catch (const EMirror&)
2705 log.displayNL( "Dataset not found" );
2707 dataset->displayEntities( log, onlyEntityType, onlyCreatorId, onlyDynamicId, false, true, !sortByDataSetRow, sortByDataSetRow );
2708 return true;
2712 * lookForMirrorValue command
2714 NLMISC_COMMAND( lookForMirrorValue, "Look for values with criteria (Value can be * for any value, or a value in the format displayed in displayMirrorRow)", "<dataset> <propName> <valueString> [<onlyEntityType=255> [<onlyCreatorId=255> [<onlyDynamicId=255>]]]" )
2716 if ( args.size() > 2 )
2720 string propName;
2721 bool anyValue = false;
2722 string valueSearchedStr;
2723 uint8 onlyEntityType = 0xFF;
2724 uint8 onlyCreatorId = 0xFF;
2725 uint8 onlyDynamicId = 0xFF;
2726 propName = args[1];
2727 if ( args.size() > 2 )
2729 if ( args[2] == "*" )
2730 anyValue = true;
2731 else
2732 valueSearchedStr = args[2];
2733 if ( args.size() > 3 )
2735 fromString(args[3], onlyEntityType);
2736 if ( args.size() > 4 )
2738 fromString(args[4], onlyCreatorId);
2739 if ( args.size() > 5 )
2741 fromString(args[5], onlyDynamicId);
2746 MirrorInstance->getDataSet( args[0] ).lookForValue( log, propName, anyValue, valueSearchedStr, onlyEntityType, onlyCreatorId, onlyDynamicId, false, true, false, true );
2748 catch (const EMirror&)
2750 log.displayNL( "Dataset not found" );
2752 return true;
2754 return false;
2758 * displayMirrorTrackers command
2760 NLMISC_CATEGORISED_COMMAND(mirror, displayMirrorTrackers, "Display the trackers for a dataset", "<dataset>" )
2762 if ( args.size() == 1 )
2766 MirrorInstance->getDataSet( args[0] ).displayTrackers( log );
2768 catch (const EMirror&)
2770 log.displayNL( "Dataset not found" );
2772 return true;
2774 return false;
2779 * displayMirrorProperties command
2781 NLMISC_CATEGORISED_COMMAND(mirror, displayMirrorDatasets, "Display the datasets loaded and some stats", "" )
2783 TNDataSets::iterator ids;
2784 for ( ids=MirrorInstance->dsBegin(); ids!=MirrorInstance->dsEnd(); ++ids )
2786 log.displayNL( "Dataset %s: %u entities online, %u offline (%d max rows), %hd properties, sheet %s", (*ids).first.c_str(), GET_NDATASET(ids).getNbOnlineEntities(), GET_NDATASET(ids).getNbOfflineEntities(), GET_NDATASET(ids).maxNbRows(), GET_NDATASET(ids).nbProperties(), GET_NDATASET(ids).sheetId().toString().c_str() );
2788 return true;
2793 * displayMirrorEntityTypesOwned command
2795 NLMISC_CATEGORISED_COMMAND(mirror, displayMirrorEntityTypesOwned, "Display the entity types owned by the service and related info", "" )
2797 MirrorInstance->displayEntityTypesOwned( log );
2798 return true;
2803 * rescanExistingEntities command
2805 NLMISC_CATEGORISED_COMMAND(mirror, rescanExistingEntities, "Scan the entities to find if some are missing and add them (debug feature)", "<dataset>" )
2807 if ( args.empty() )
2808 return false;
2812 CMirroredDataSet& dataset = MirrorInstance->getDataSet( args[0] );
2813 MirrorInstance->rescanExistingEntities( dataset, log, true );
2815 catch (const EMirror&)
2817 log.displayNL( "Dataset not found" );
2819 return true;
2824 * displayUnknownOnlineEntities command
2826 NLMISC_CATEGORISED_COMMAND(mirror, displayUnknownOnlineEntities, "Scan the entities to find the online entities of which this service is not aware", "<dataset>" )
2828 if ( args.empty() )
2829 return false;
2833 CMirroredDataSet& dataset = MirrorInstance->getDataSet( args[0] );
2834 MirrorInstance->rescanExistingEntities( dataset, log, false );
2836 catch (const EMirror&)
2838 log.displayNL( "Dataset not found" );
2840 return true;
2845 * monitorMirrorEntity command
2847 NLMISC_CATEGORISED_COMMAND(mirror, monitorMirrorEntity, "Set/unset an entity for mirror commands when no argument provided", "[<CEntityId> | <dataset> <entityIndex>]" )
2849 switch( args.size() )
2851 case 0: MirrorInstance->MonitoredEntity = CEntityId::Unknown; break;
2852 case 1: MirrorInstance->MonitoredEntity.fromString( args[0].c_str() ); break;
2853 case 2:
2857 CMirroredDataSet& dataset = MirrorInstance->getDataSet( args[0] );
2858 TDataSetIndex entityIndex;
2859 fromString(args[1], entityIndex);
2860 if ( (entityIndex < (uint32)dataset.maxNbRows()) && (entityIndex!=INVALID_DATASET_INDEX) )
2861 MirrorInstance->MonitoredEntity = dataset.getEntityId( dataset.getCurrentDataSetRow( entityIndex ) );
2862 else
2863 log.displayNL( "Invalid entity index provided" );
2865 catch (const EMirror&)
2867 log.displayNL( "Dataset not found" );
2869 break;
2871 default: return false;
2873 log.displayNL( "The monitored entity is %s", MirrorInstance->MonitoredEntity.isUnknownId() ?"not set or unknown" : MirrorInstance->MonitoredEntity.toString().c_str() );
2874 return true;
2877 NLMISC_CATEGORISED_DYNVARIABLE(mirror, sint32, MainNbEntities, "Number of online entities in dataset fe_temp" )
2879 // We can only read the value
2880 if ( get )
2882 if ( MirrorInstance && MirrorInstance->mirrorIsReady() )
2886 *pointer = MirrorInstance->getDataSet( "fe_temp" ).getNbOnlineEntities();
2888 catch (const EMirror&)
2890 *pointer = -2; // silent
2893 else
2894 *pointer = -1;
2898 NLMISC_CATEGORISED_DYNVARIABLE(mirror, sint32, LocalEntities, "Number of online local entities in dataset fe_temp" )
2900 // We can only read the value
2901 if ( get )
2903 if ( MirrorInstance && MirrorInstance->mirrorIsReady() )
2907 *pointer = MirrorInstance->getDataSet( "fe_temp" ).getNbOwnedEntities();
2909 catch (const EMirror&)
2911 *pointer = -2; // silent
2914 else
2915 *pointer = -1;
2920 NLMISC_CATEGORISED_DYNVARIABLE(mirror, string, MirrorVersion, "Version of mirror client" )
2922 if ( get )
2923 *pointer = MirrorVersion;
2926 namespace NLNET
2928 extern bool createMessage (CMessage &msgout, const vector<string> &args, CLog &log);
2931 NLMISC_CATEGORISED_COMMAND(mirror, msgoutViaMirror, "Send a message to a specified service, via mirror (ex: msgoutViaMirror 131 REGISTER u32 10 b 1 f 1.5)", "msgout <ServiceName>|<ServiceId> <MessageName> [<ParamType> <Param>]*" )
2933 if(args.size() < 2) return false;
2935 if (!MirrorInstance->mirrorIsReady())
2937 log.displayNL ("Mirror is not ready");
2938 return false;
2941 uint16 sId;
2942 fromString(args[0], sId);
2943 TServiceId serviceId(sId);
2944 string serviceName = args[0];
2945 string messageName = args[1];
2947 if (serviceId.get() > 255)
2949 log.displayNL ("Service Id %d must be between [1;255]", serviceId.get());
2950 return false;
2953 if ((args.size()-2) % 2 != 0)
2955 log.displayNL ("The number of parameter must be a multiple of 2");
2956 return false;
2959 CMessage msg (messageName);
2961 if (!NLNET::createMessage (msg, args, log))
2962 return false;
2964 if (serviceId.get() != 0)
2965 sendMessageViaMirror (serviceId, msg);
2966 else
2967 sendMessageViaMirror (serviceName, msg);
2969 return true;