// Ryzom - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #include "stdpch.h" #include "data_set_base.h" #include // Dependancy on NLNET::IService only for config file access for TDataSetSheet::getConfigDataSetSize() #include #include "ryzom_entity_id.h" using namespace std; using namespace NLMISC; using namespace NLNET; using namespace NLGEORGES; // The following variable controls the logging of nlinfo and nldebug messages produced by the MIRROR_INFO and MIRROR_DEBUG macros CVariable VerboseMIRROR("ms","VerboseMIRROR","Enable verbose logging in Mirror operations",true,0,true); #ifdef MIRROR_LIST_ROW_32BITS const char *ListRowSizeString = "L32"; #else const char *ListRowSizeString = "L16"; #endif /* * Constructor */ CDataSetBase::CDataSetBase() : _MaxNbRows(INVALID_DATASET_INDEX) //, _SizeOfRow(Row32) { } /* * Initialize */ void CDataSetBase::init( const NLMISC::CSheetId& sheetId, const TDataSetSheet& properties ) { /*string dataSetName = "player"; TPropertyIndex nbProperties = 4; _PropIndexToType.resize( nbProperties ); _PropIndexToType[0] = TypeSint32; _PropIndexToType[1] = TypeSint32; _PropIndexToType[2] = TypeSint32; _PropIndexToType[3] = TypeSint32; _DataSetName = dataSetName;*/ nlctassert( sizeof(TDataSetRow) == 4 ); _SheetId = sheetId; _DataSetName = properties.DataSetName; _MaxNbRows = properties.getConfigDataSetSize(); //_SizeOfRow = (_MaxNbRows>65534) ? Row32 : ((_MaxNbRows>254)?Row16:Row8); //nlinfo( "\t Dataset %s: Rows will be transfered using %s-bit index", _DataSetName.c_str(), (_SizeOfRow==Row32)?"32":((_SizeOfRow==Row16)?"16":"8") ); _PropIndexToType = properties.PropIndexToType; fillDataSizeByProp(); _EntityTypesFilter = properties.EntityTypesFilter; _PropIsList = properties.PropIsList; _PropertyContainer.init( properties.NbProperties ); } /* * Fill property info. * When the flag initValues is set: * If the counts must be initialised from a remote MS, use the vector ptCountsToSet (it will be read * then deleted) and timestamp, otherwise set NULL. * If propName is the special entityId property, the behaviour is different (propName need not exist) */ void CDataSetBase::setPropertyPointer( std::string& propName, TPropertyIndex propIndex, void *segmentPt, bool initValues, uint32 dataTypeSize, bool isReadOnly, bool mustMonitorAssignment, std::vector *ptCountsToSet, NLMISC::TGameCycle /* timestamp */, uint32 segmentSize ) { uint8 *endPt = (uint8*)segmentPt; // Handle special case of _CEntityId_ if ( propName.substr(0,LENGTH_OF_ENTITYID_PREFIX) == ENTITYID_PREFIX ) { _PropertyContainer.EntityIdArray.EntityIds = (CEntityId*)segmentPt; _PropertyContainer.EntityIdArray.OnlineBitfieldPt = (uint32*)((uint8*)segmentPt + (sizeof(CEntityId)*maxNbRows())); _PropertyContainer.EntityIdArray.OnlineTimestamps = (NLMISC::TGameCycle*)(((uint8*)_PropertyContainer.EntityIdArray.OnlineBitfieldPt) + maxNbRows()/8 + 4); _PropertyContainer.EntityIdArray.Counts = ((uint8*)_PropertyContainer.EntityIdArray.OnlineTimestamps) + maxNbRows()*sizeof(NLMISC::TGameCycle); _PropertyContainer.EntityIdArray.SpawnerServiceIds = ((TServiceId8*)_PropertyContainer.EntityIdArray.Counts) + maxNbRows()*sizeof(TServiceId8); endPt = ((uint8*)_PropertyContainer.EntityIdArray.SpawnerServiceIds) + maxNbRows()*sizeof(TServiceId8); // In the Mirror Service, initialize the value if ( initValues ) { // Set the entityid array to the default value for ( TDataSetIndex i=0; i!=(uint32)maxNbRows(); ++i ) { _PropertyContainer.EntityIdArray.EntityIds[i] = CEntityId::Unknown; _PropertyContainer.EntityIdArray.setOnline( i, false ); if ( ptCountsToSet != NULL ) _PropertyContainer.EntityIdArray.Counts[i] = (*ptCountsToSet)[i]; else _PropertyContainer.EntityIdArray.Counts[i] = 1; // begins at 1, 0 is reserved for "no counter" // Always init the online timestamps to 0 (otherwise the assert in declareEntity() will fail) _PropertyContainer.EntityIdArray.OnlineTimestamps[i] = 0; _PropertyContainer.EntityIdArray.SpawnerServiceIds[i] = TServiceId8(0); } if ( ptCountsToSet != NULL ) { delete ptCountsToSet; } } } else { if ( (uint)propIndex < (uint)nbProperties() ) { _PropertyContainer.PropertyValueArrays[propIndex].ChangeTimestamps = (TGameCycle*)segmentPt; #ifdef STORE_CHANGE_SERVICEIDS _PropertyContainer.PropertyValueArrays[propIndex].ChangeServiceIds = (TServiceId8*)((uint8*)segmentPt + (sizeof(TGameCycle)*maxNbRows())); _PropertyContainer.PropertyValueArrays[propIndex].Values = (void*)((uint8*)_PropertyContainer.PropertyValueArrays[propIndex].ChangeServiceIds + (sizeof(TServiceId8)*maxNbRows())); #else _PropertyContainer.PropertyValueArrays[propIndex].Values = (void*)((uint8*)segmentPt + (sizeof(TGameCycle)*maxNbRows())); #endif _PropertyContainer.PropertyValueArrays[propIndex].DataTypeSize = dataTypeSize; #ifdef NL_DEBUG _PropertyContainer.PropertyValueArrays[propIndex].IsReadOnly = isReadOnly; _PropertyContainer.PropertyValueArrays[propIndex].IsMonitored = mustMonitorAssignment; #endif // Cell container for list properties if ( _PropIsList[ propIndex ] ) { uint8 *ptFreeCellsFront = ((uint8*)(_PropertyContainer.PropertyValueArrays[propIndex].Values)) + sizeof(TSharedListRow)*maxNbRows(); _PropertyContainer.PropertyValueArrays[propIndex].ListCellContainer = ptFreeCellsFront + sizeof(TSharedListRow); // In the Mirror Service, set up the list headers (1 per datasetrow) and container if ( initValues ) { // Init the headers TSharedListRow *listHeaders = (TSharedListRow*)(_PropertyContainer.PropertyValueArrays[propIndex].Values); for ( TDataSetIndex i=0; i!=(uint32)maxNbRows(); ++i ) { listHeaders[i] = INVALID_SHAREDLIST_ROW; } uint32 datasize = getDataSizeOfProp( propIndex ); //nldebug( "%p + %u bytes + %u", _PropertyContainer.PropertyValueArrays[propIndex].Values, sizeof(TSharedListRow)*maxNbRows(), sizeof(TSharedListRow) ); //nldebug( "%p", _PropertyContainer.PropertyValueArrays[propIndex].ListCellContainer ); //nldebug( "Segment size: %u", segmentSize ); nldebug( "Shared List container uses %u bytes (at %u bytes per cell)", segmentSize - ( (uint8*)(_PropertyContainer.PropertyValueArrays[propIndex].ListCellContainer) - (uint8*)segmentPt ), sizeof(TSharedListRow)+datasize ); //nldebug( "End at %p", (uint8*)segmentPt + segmentSize ); // Init the list of free cells TSharedListRow *freeCellsFront = (TSharedListRow*)ptFreeCellsFront; freeCellsFront = 0; uint8 *cellPt = (uint8*)(_PropertyContainer.PropertyValueArrays[propIndex].ListCellContainer); for ( TSharedListRow j=0; j!=NB_SHAREDLIST_CELLS-1 /*TEMP*/; ++j ) { TSharedListRow *nextField = (TSharedListRow*)cellPt; *nextField = j+1; cellPt += sizeof(TSharedListRow) + datasize; // assumes #pragma pack! } TSharedListRow *nextField = (TSharedListRow*)cellPt; *nextField = INVALID_SHAREDLIST_ROW; endPt = ((uint8*)_PropertyContainer.PropertyValueArrays[propIndex].ListCellContainer) + (NB_SHAREDLIST_CELLS*(sizeof(TSharedListRow) + datasize)); // assumes #pragma pack! } } else { _PropertyContainer.PropertyValueArrays[propIndex].ListCellContainer = NULL; if ( initValues ) { endPt = ((uint8*)_PropertyContainer.PropertyValueArrays[propIndex].Values) + maxNbRows()*dataTypeSize; } } } } if ( initValues ) { // Check that the total size of segment is sufficient nlassert( ((uint8*)segmentPt) + segmentSize >= endPt ); } } /* * Fill the datasize array using the prop to type array */ void CDataSetBase::fillDataSizeByProp() { _DataSizeByProp.resize( _PropIndexToType.size() ); vector::iterator itd; vector::const_iterator itp; for ( itp=_PropIndexToType.begin(), itd=_DataSizeByProp.begin(); itp!=_PropIndexToType.end(); ++itp, ++itd ) { switch ( *itp ) { case TypeUint8: *itd = 1; break; case TypeSint8: *itd = 1; break; case TypeUint16: *itd = 2; break; case TypeSint16: *itd = 2; break; case TypeUint32: *itd = 4; break; case TypeSint32: *itd = 4; break; case TypeUint64: *itd = 8; break; case TypeSint64: *itd = 8; break; case TypeFloat: *itd = 4; break; case TypeDouble: *itd = 8; break; case TypeCEntityId: *itd = 8; break; case TypeBool: *itd = 1; break; default: *itd = 0; break; } } } /* * Check that the template argument T passed to CMirrorPropValue has the right size */ void CDataSetBase::checkTemplateSize( uint32 passedSize, TPropertyIndex propIndex, const TDataSetRow& entityIndex) const { nlassertex( passedSize == _PropertyContainer.PropertyValueArrays[propIndex].DataTypeSize, ("Wrong CMirrorPropValue template argument: size=%u (expected: %u) (%s/E%d/P%hd)", passedSize, _PropertyContainer.PropertyValueArrays[propIndex].DataTypeSize, name().c_str(), entityIndex.getIndex(), propIndex ) ); } /* * Read the sheet */ void TDataSetSheet::readGeorges( const NLMISC::CSmartPtr &form, const NLMISC::CSheetId &sheetId ) { if( (UForm*)form ) { // Get the name of the dataset DataSetName = sheetId.toString(); DataSetName.erase( DataSetName.find_last_of( '.' ) ); // erase .dataset MIRROR_INFO( "MIRROR: Reading sheet for dataset '%s'", DataSetName.c_str() ); UFormElm& root = form->getRootNode(); // Get the max number of rows (will be used only if not overriden in the .cfg) if ( root.getValueByName( MaxNbRows, "max number of rows or entities" ) ) { MIRROR_INFO( "MIRROR: \tMaxNbRows = %d in sheet", MaxNbRows ); } else { MaxNbRows = 0; nlinfo( "MaxNbRows of %s not specified in sheet, expecting value in .cfg", DataSetName.c_str() ); } // Get property types and names UFormElm *arrayProperties; if( root.getNodeByName( &arrayProperties, "properties" ) && arrayProperties ) { uint size; nlverify( arrayProperties->getArraySize(size) ); nlassert( size < 0x10000 ); NbProperties = size; PropertyNames.resize( NbProperties ); PropIndexToType.resize( NbProperties ); //Weights.resize( NbProperties ); PropIsList.resize( NbProperties ); for ( sint p=0; p!=NbProperties; ++p ) { const UFormElm *property = NULL; if ( arrayProperties->getArrayNode( &property, p ) && property ) { // Name of the property property->getValueByName( PropertyNames[p], "name" ); // Type of the property string typeStr; property->getValueByName( typeStr, "type" ); TTypeOfProp theType; if ( typeStr == "uint8" ) theType = TypeUint8; else if ( typeStr == "sint8" ) theType = TypeSint8; else if ( typeStr == "uint16" ) theType = TypeUint16; else if ( typeStr == "sint16" ) theType = TypeSint16; else if ( typeStr == "uint32" ) theType = TypeUint32; else if ( typeStr == "sint32" ) theType = TypeSint32; else if ( typeStr == "uint64" ) theType = TypeUint64; else if ( typeStr == "sint64" ) theType = TypeSint64; else if ( typeStr == "float" ) theType = TypeFloat; else if ( typeStr == "double" ) theType = TypeDouble; else if ( typeStr == "entityid" ) theType = TypeCEntityId; else if ( typeStr == "boolean" ) theType = TypeBool; else { nlerror( "MIRROR: CDataSetBase::readGeorges: unknown data type '%s'", typeStr.c_str() ); // not forcing to TypeUint64 anymore theType = TypeUint64; } PropIndexToType[p] = theType; // Weight /*sint32 weight; if ( ! property->getValueByName( weight, "weight" ) ) { weight = 1; } if ( weight > 255 ) { nlwarning( "Weights must be lower than 256" ); weight = 255; } Weights[p] = (uint8)weight; */ // List or single value? bool b; if ( ! property->getValueByName( b, "is a list" ) ) { b = false; } PropIsList[p] = b; MIRROR_INFO( "MIRROR: \tProperty '%s' : Type %s%s"/*", Weight %d"*/, PropertyNames[p].c_str(), PropIsList[p]?"list of ":"", typeStr.c_str()/*, Weights[p]*/ ); // persistant //property->getValueByName(propElt.Persistant, "persistant"); // mirror //property->getValueByName(propElt.Mirror, "mirror"); } } } else { nlwarning( "MIRROR: CDataSetBase::readGeorges: 'properties' not found" ); } // Get entity type filter UFormElm *arrayEntityTypes; if ( root.getNodeByName( &arrayEntityTypes, "entity types" ) && arrayEntityTypes ) { uint size; nlverify( arrayEntityTypes->getArraySize(size) ); nlassert( size < 0x10000 ); EntityTypesFilter.resize( size ); for ( uint et=0; et!=size; ++et ) { string typeName; arrayEntityTypes->getArrayValue( typeName, et ); EntityTypesFilter[et] = RYZOMID::fromString(typeName); MIRROR_INFO( "MIRROR: \tLinking to entity type %hu", (uint16)EntityTypesFilter[et] ); } } else { nlwarning( "MIRROR: CDataSetBase::readGeorges: 'entity types' not found" ); } } } /* * Serial (for fast binary sheet loading) */ void TDataSetSheet::serial( NLMISC::IStream& s ) { s.serial( DataSetName ); s.serial( MaxNbRows ); s.serial( NbProperties ); if ( s.isReading() ) { PropIndexToType.resize( NbProperties ); PropertyNames.resize( NbProperties ); //Weights.resize( NbProperties ); PropIsList.resize( NbProperties ); } for ( TPropertyIndex p=0; p!=NbProperties; ++p ) { s.serial( PropertyNames[p] ); s.serialEnum( PropIndexToType[p] ); MIRROR_DEBUG( "MIRROR: Serializing %s, type %u", PropertyNames[p].c_str(), PropIndexToType[p] ); //s.serial( Weights[p] ); bool b = PropIsList[p]; s.serial( b ); PropIsList[p] = b; } s.serialCont( EntityTypesFilter ); } /* * Return the dataset size to use. * If found in the .cfg, use the value of the property DataSetSize + DataSetName. * If not found, use the value in the sheet. */ TDataSetIndex TDataSetSheet::getConfigDataSetSize() const { string cfgVarName = "DatasetSize" + DataSetName; CConfigFile::CVar *cfgVar = NLNET::IService::getInstance()->ConfigFile.getVarPtr( cfgVarName ); TDataSetIndex result; if ( cfgVar ) { result = cfgVar->asInt(); nlinfo( "\t%s: %u (from cfg)", cfgVarName.c_str(), result ); } else { result = MaxNbRows; nlinfo( "\t%s: %u (from packed sheet)", cfgVarName.c_str(), result ); } return result; }