コード例 #1
0
ファイル: action_factory.cpp プロジェクト: mixxit/solinia
/* Pack an action to a bit stream. Set transmitTimestamp=true for server-->client,
 * false for client-->server. If true, set the current gamecycle.
 */
void CActionFactory::pack (CAction *action, NLMISC::CBitMemStream &message, NLMISC::TGameCycle /* currentCycle */ )
{
	//H_BEFORE(FactoryPack);
	//sint32 val = message.getPosInBit ();

	//

	if (action->Code < 4)
	{
		// short code (0 1 2 3)
		bool shortcode = true;
		uint32 code = action->Code;
		message.serialAndLog1 (shortcode);
		message.serialAndLog2 (code, 2);
	}
	else
	{
		bool shortcode = false;
		message.serialAndLog1 (shortcode);
		message.serialAndLog1 (action->Code);
	}

	action->pack (message);
	//H_AFTER(FactoryPack);

	//OLIV: nlassertex (message.getPosInBit () - val == (sint32)CActionFactory::getInstance()->size (action), ("CActionFactory::pack () : action %d packed %u bits, should be %u, size() is wrong", action->Code, message.getPosInBit () - val, CActionFactory::getInstance()->size (action)));

//	nlinfo ("ac:%p pack one action in message %d %hu %u %d", action, action->Code, (uint16)(action->CLEntityId), val, message.getPosInBit()-val);
}
コード例 #2
0
// ***************************************************************************
void CClientChatManager::processTellString(NLMISC::CBitMemStream& bms, IChatDisplayer &chatDisplayer)
{
	CChatMsg chatMsg;

	// Serial. For tell message, there is no chat mode, coz we know we are in tell mode !
	bms.serial (chatMsg.CompressedIndex);
	bms.serial (chatMsg.SenderNameId);
	bms.serial (chatMsg.Content);

	if (PermanentlyBanned) return;

	chatMsg.ChatMode = (uint8) CChatGroup::tell;

	// If !complete, wait
	ucstring senderStr;
	bool	complete = true;
	complete &= STRING_MANAGER::CStringManagerClient::instance()->getString(chatMsg.SenderNameId, senderStr);
	if (!complete)
	{
		_ChatBuffer.push_back(CChatMsgNode(chatMsg, true));
		nldebug("<impulseTell> Received TELL, put in buffer : waiting association");
		return;
	}

	// display
	ucstring	ucstr;
	buildTellSentence(senderStr, chatMsg.Content, ucstr);
	chatDisplayer.displayTell(/*chatMsg.CompressedIndex, */ucstr, senderStr);
}
/*
 * Push one change to the stream (permanent mode)
 */
void	CCDBSynchronised::pushDeltaPermanent( NLMISC::CBitMemStream& s, CCDBStructNodeLeaf *node, uint32& bitsize )
{
	TCDBDataIndex index = node->getDataIndex();

	// Test if the property type is valid.
	if ( node->type() > ICDBStructNode::UNKNOWN && node->type() < ICDBStructNode::Nb_Prop_Type )
	{
		// Serialize the Property Value according to the Property Type.
		uint64 value;
		value = (uint64)_DataContainer.getValue64( index ); // "delta from 0"

		if ( node->type() == ICDBStructNode::TEXT )
		{
			s.serialAndLog2( value, 32 );
			bitsize += 32;
			if ( VerboseDatabase )
				nldebug( "CDB: Pushing permanent value %"NL_I64"d (TEXT-32) for index %d prop %s", (sint64)value, index, node->buildTextId().toString().c_str() );
		}
		else
		{
			s.serialAndLog2( value, (uint)node->type() );
			bitsize += (uint32)node->type();
			if ( VerboseDatabase )
				nldebug( "CDB: Pushing permanent value %"NL_I64"d (%u bits) for index %d prop %s", (sint64)value, (uint32)node->type(), index, node->buildTextId().toString().c_str() );
		}
	}
	else
		nlwarning("CCDBStructNodeLeaf::writeDeltaPermanent : Property Type Unknown ('%d') -> not serialized.", (uint)node->type());
}
コード例 #4
0
// ***************************************************************************
void CClientChatManager::processTellString2(NLMISC::CBitMemStream& bms, IChatDisplayer &chatDisplayer)
{
	// serial
	CChatMsg2 chatMsg;
	chatMsg.ChatMode = CChatGroup::tell;
	bms.serial(chatMsg.CompressedIndex);
	bms.serial(chatMsg.SenderNameId);
	bms.serial(chatMsg.PhraseId);

	// if !complete, wait
	ucstring senderStr;
	ucstring rawMessage;
	bool	complete = true;
	complete &= STRING_MANAGER::CStringManagerClient::instance()->getString(chatMsg.SenderNameId, senderStr);
	complete &= STRING_MANAGER::CStringManagerClient::instance()->getDynString(chatMsg.PhraseId, rawMessage);
	if (!complete)
	{
		_ChatBuffer.push_back(CChatMsgNode(chatMsg, true));
		nldebug("<impulseTell> Received TELL, put in buffer : waiting association");
		return;
	}

	// display
	ucstring	ucstr;
	buildTellSentence(senderStr, rawMessage, ucstr);
	chatDisplayer.displayTell(/*chatMsg.CompressedIndex, */ucstr, senderStr);
}
コード例 #5
0
// ***************************************************************************************
void CBotChatPageDynamicMission::sendChoices()
{
	uint k;
	#ifdef NL_DEBUG
		for(k = 0; k < DYNAMIC_MISSION_NUM_CHOICES; ++k)
		{
			nlassert(_Choice[k] != -1);
		}
	#endif
	NLMISC::CBitMemStream out;
	static const char *msgName = "BOTCHAT:DM_CHOICE";
	if(GenericMsgHeaderMngr.pushNameToStream(msgName, out))
	{
		for(k = 0; k < DYNAMIC_MISSION_NUM_CHOICES; ++k)
		{
			uint8 u8Choice = (uint8) _Choice[k];
			out.serial(u8Choice);
		}
		NetMngr.push(out);
	}
	else
	{
		nlwarning(" unknown message name %s", msgName);
	}
}
// ***************************************************************************************
void CBotChatPageMission::acceptMission()
{
	if (!_CurrSel) return;
	sint index = _CurrSel->getIndexInParent();
	if (index < 0) return;
	// send msg to server
	NLMISC::CBitMemStream out;
	static const char *msgName;
	if(_MType==MISSION_DESC::ZCCharge)
		msgName= "BOTCHAT:DUTY_APPLY";
	else
		msgName= "BOTCHAT:PICK_MISSION";
	if(GenericMsgHeaderMngr.pushNameToStream(msgName, out))
	{
		uint8 missionIndex = (uint8) index;
		out.serial(missionIndex);
		NetMngr.push(out);
	}
	else
	{
		nlwarning(" unknown message name %s", msgName);
	}
	// close the selection box
	activateWindow(WIN_BOT_CHAT_ACCEPT_MISSION, false);
	// close the botchat
	CBotChatManager::getInstance()->setCurrPage(NULL);
	_CurrSel = NULL;
}
コード例 #7
0
    virtual void execute (CCtrlBase * /* pCaller */, const string &Params)
    {
        CInterfaceManager *pIM = CInterfaceManager::getInstance();
        string guildNameWin = getParam(Params, "guild");
        string IconWin = getParam(Params, "icon");
        string guildDescWin = getParam(Params, "desc");

        CGroupEditBox *pGEB = dynamic_cast<CGroupEditBox*>(CWidgetManager::getInstance()->getElementFromId(guildNameWin));
        if (pGEB == NULL) return;

        CDBCtrlSheet *pCS = dynamic_cast<CDBCtrlSheet*>(CWidgetManager::getInstance()->getElementFromId(IconWin));
        if (pCS == NULL) return;

        CGroupEditBox *pDesc = dynamic_cast<CGroupEditBox*>(CWidgetManager::getInstance()->getElementFromId(guildDescWin));

        ucstring guildName = pGEB->getInputString();

        ucstring guildDesc;
        if (pDesc != NULL) guildDesc = pDesc->getInputString();

        uint64 icon = CGuildManager::iconMake((uint8)pCS->getGuildBack(), (uint8)pCS->getGuildSymbol(),
                                              pCS->getInvertGuildSymbol(), pCS->getGuildColor1(), pCS->getGuildColor2());

        const string msgName = "GUILD:CREATE";
        NLMISC::CBitMemStream out;
        if(GenericMsgHeaderMngr.pushNameToStream(msgName, out))
        {
            out.serial( guildName );
            out.serial( icon );
            out.serial( guildDesc );
            NetMngr.push(out);
        }
        //CBotChatManager::getInstance()->setCurrPage(NULL);
    }
コード例 #8
0
// ***************************************************************************
void CClientChatManager::processChatString2(NLMISC::CBitMemStream& bms, IChatDisplayer &chatDisplayer)
{
	CChatMsg2 chatMsg;
	bms.serial( chatMsg );
	if (PermanentlyBanned) return;
	CChatGroup::TGroupType	type = static_cast<CChatGroup::TGroupType>(chatMsg.ChatMode);
	ucstring	senderStr;
	ucstring	rawMessage;

	// here, the type cannot be dyn_chat (no DynChatId in the message) => discard
	if(type==CChatGroup::dyn_chat)
	{
		nlwarning("Client don't support dyn_chat with CChatMsg2 messages => '%x' aborted", chatMsg.PhraseId);
		return;
	}

	// if !complete, wait
	bool	complete = true;
	complete &= STRING_MANAGER::CStringManagerClient::instance()->getString(chatMsg.SenderNameId, senderStr);
	complete &= STRING_MANAGER::CStringManagerClient::instance()->getDynString(chatMsg.PhraseId, rawMessage);
	if (!complete)
	{
		_ChatBuffer.push_back(CChatMsgNode(chatMsg, false));
		//nldebug("<impulseChat> Received CHAT, put in buffer : waiting association");
		return;
	}

	rawMessage += ucstring(" ");
	rawMessage += chatMsg.CustomTxt;

	// display
	ucstring	ucstr;
	buildChatSentence(chatMsg.CompressedIndex, senderStr, rawMessage, type, ucstr);
	chatDisplayer.displayChat(chatMsg.CompressedIndex, ucstr, rawMessage, type, CEntityId::Unknown, senderStr);
}
コード例 #9
0
// ***************************************************************************
void CClientChatManager::processChatStringWithNoSender( NLMISC::CBitMemStream& bms, CChatGroup::TGroupType type, IChatDisplayer &chatDisplayer)
{
	nlassert(type!=CChatGroup::dyn_chat);

	// serial
	CChatMsg2 chatMsg;
	uint32 phraseID;
	bms.serial(phraseID);
	if (PermanentlyBanned) return;
	chatMsg.CompressedIndex = INVALID_DATASET_INDEX;
	chatMsg.SenderNameId = 0;
	chatMsg.ChatMode = type;
	chatMsg.PhraseId = phraseID;
	ucstring ucstr;

	// if !complete, wait
	bool	complete = STRING_MANAGER::CStringManagerClient::instance()->getDynString(chatMsg.PhraseId, ucstr);
	if (!complete)
	{
		_ChatBuffer.push_back(CChatMsgNode(chatMsg, false));
		//nldebug("<impulseDynString> Received CHAT, put in buffer : waiting association");
		return;
	}

	// diplay
	ucstring senderName("");
	chatDisplayer.displayChat(INVALID_DATASET_INDEX, ucstr, ucstr, type, CEntityId::Unknown, senderName);
}
コード例 #10
0
	void fillBitMemStream( const CCharacterInfos *charInfo, CStringManager::TLanguages language, const CStringManager::TReplacement &rep, NLMISC::CBitMemStream &bms)
	{
		uint32 eventFactionId = SM->translateEventFaction(Identifier);

		// serial the string ID
		bms.serial(eventFactionId);
	}
コード例 #11
0
void CStringManager::CParameterTraits::fillBitMemStream( const CCharacterInfos *charInfo,TLanguages language, const TReplacement &rep, NLMISC::CBitMemStream &bms)
{
	const CStringManager::CEntityWords &ew = SM->getEntityWords(language, ParamId.Type);
	std::string rowName = NLMISC::strlwr(getParameterId());
	uint32 stringId;
	stringId = ew.getStringId(rowName, rep.Format);
	bms.serial(stringId);
}
/*
 * Write the id to a bit stream for server-client comms, and return the size of data written
 */
inline sint ICDBStructNode::CBinId::writeToBitMemStream( NLMISC::CBitMemStream& f ) const
{
	sint bitsize = 0;
	for ( uint i=0; i<Ids.size(); i++ )
	{
		uint32& nodeIndex = const_cast<uint32&>(Ids[i].first);
		f.serialAndLog2( nodeIndex, Ids[i].second );
		bitsize += Ids[i].second;
	}
	return bitsize;
}
コード例 #13
0
ファイル: action_factory.cpp プロジェクト: mixxit/solinia
/* Unpack some actions from a bit stream. Set transmitTimestamp=true for server-->client,
 * false for client-->server. If true, set the current gamecycle.
 */
void CActionFactory::unpack (NLMISC::CBitMemStream &message, std::vector <CAction *>& actions, NLMISC::TGameCycle /* currentCycle */ )
{
	actions.clear ();

	static int n = 0;
	n++;
	while ((sint32)message.length() * 8 - message.getPosInBit () >= 8)
	{
		TActionCode code;

		bool shortcode;
		message.serial (shortcode);

		if (shortcode)
		{
			code = 0;
			uint32 val;
			message.serial (val, 2);
			code = (TActionCode) val;
		}
		else
		{
			message.serial (code);
		}

		CAction *action = create (INVALID_SLOT, code);

		//nlinfo ("m%d size: p:%d s:%d c:%d (actionsize: %d) slot:%hu", n, message.getPosInBit (), message.length() * 8, code, action->size(), (uint16)action->CLEntityId);

		if (action == NULL)
		{
			nlwarning ("Unpacking an action with unknown code, skip it (%u)", code);
		}
		else
		{
			action->unpack (message);
			actions.push_back (action);
		}
	}
}
コード例 #14
0
	void fillBitMemStream( const CCharacterInfos *charInfo, CStringManager::TLanguages language, const CStringManager::TReplacement &rep, NLMISC::CBitMemStream &bms)
	{

		uint32 nameId1 = SM->storeString(Identifier);
		uint32 nameId2 = SM->translateShortName(nameId1);
		const ucstring &name = SM->getString(nameId2);
		if (!name.empty() && name[0] == '$')
		{
			// this name is a generic name, translate the title
			ucstring title = name.substr(1, name.size()-2);
			nameId2 = SM->translateTitle(title.toString(), language);
		}
		// serial the string ID
		bms.serial(nameId2);
	}
コード例 #15
0
	void fillBitMemStream( const CCharacterInfos *charInfo,CStringManager::TLanguages language, const CStringManager::TReplacement &rep, NLMISC::CBitMemStream &bms)
	{
		// need to evaluate the name of the bot : should be in the charinfo
		ucstring temp;

		CCharacterInfos	*botInfo = IOS->getCharInfos(EId);
		if (botInfo != 0)
		{
			if (!botInfo->ShortName.empty())
			{
				uint32 index = botInfo->ShortNameIndex;
				bms.serial(index);
				return;
			}
			if (!botInfo->Title.empty())
			{
				uint32 index = SM->translateTitle(botInfo->Title, language);
				bms.serial(index);
				return;
			}
		}
		// hum, no name available
		CParameterTraitsEntity::fillBitMemStream(charInfo, language, rep, bms);
	}
コード例 #16
0
	void fillBitMemStream( const CCharacterInfos *charInfo,CStringManager::TLanguages language, const CStringManager::TReplacement &rep, NLMISC::CBitMemStream &bms)
	{
		ucstring temp;

		CCharacterInfos	*playerInfo = IOS->getCharInfos(EId);
		if (playerInfo != 0)
		{
			if (!playerInfo->ShortName.empty())
			{
				uint32 index = playerInfo->ShortNameIndex;
				bms.serial(index);
				return;
			}
		}
		// hum, no name available
		CParameterTraitsEntity::fillBitMemStream(charInfo,language, rep, bms);
	}
コード例 #17
0
	/// fill overloaded to deals with ring user defined item with custom names
	void fillBitMemStream( const CCharacterInfos *charInfo, CStringManager::TLanguages language, const CStringManager::TReplacement &rep, NLMISC::CBitMemStream &bms)
	{
		// check if SheetId if in the table or user item
		const CStringManager::TRingUserItemInfos &itemInfos = SM->getUserItems();
		CStringManager::TRingUserItemInfos::const_iterator it(itemInfos.find(SheetId));
		if (it != itemInfos.end() && !it->second.empty())
		{
			// this item has some user name, check the aiInstance
			const vector<CStringManager::TRingUserItemInfo> &userItemInfos = it->second;
			for (uint i=0; i<userItemInfos.size(); ++i)
			{
				if (userItemInfos[i].AIInstance == charInfo->AIInstance)
				{
					// ok, this item is renamed !

					// use the user name for a list a predefined column :
					//    name, named, nameda, p, pd
					if (	rep.Format == "name"
						||	rep.Format == "named"
						||	rep.Format == "nameda"
						||	rep.Format == "p"
						||	rep.Format == "pd")
					{
						nlWrite(bms, serial, userItemInfos[i].ItemNameId);
					}
					else
					{
						// not a valid replacement, return a 'backspace' character
						static uint32 noString = SM->storeString(ucstring()+ucchar(8));
						bms.serial(noString);
					}

					/// return here ---------------------
					return;
				}
			}
		}

		// normal item name, use the base function instead
		CParameterTraitsSheet::fillBitMemStream( charInfo, language, rep, bms);
	}
コード例 #18
0
// ***************************************************************************
void	CClientChatManager::processChatString( NLMISC::CBitMemStream& bms, IChatDisplayer &chatDisplayer)
{
	// before displaying anything, must ensure dynamic channels are up to date
	// NB: only processChatString() have to do this. Other methods cannot be in dyn_chat mode
	updateDynamicChatChannels(chatDisplayer);

	// serial
	CChatMsg chatMsg;
	bms.serial( chatMsg );
	CChatGroup::TGroupType	type = static_cast<CChatGroup::TGroupType>(chatMsg.ChatMode);
	ucstring	senderStr;

	bool complete = true;
	complete &= STRING_MANAGER::CStringManagerClient::instance()->getString(chatMsg.SenderNameId, senderStr);

	if (type == CChatGroup::dyn_chat)
	{
		// retrieve the DBIndex from the dynamic chat id
		sint32 dbIndex = ChatMngr.getDynamicChannelDbIndexFromId(chatMsg.DynChatChanID);
		// if the client database is not yet up to date, put the chat message in buffer
		if (dbIndex < 0)
			complete = false;
	}

	// if !complete, wait
	if (!complete)
	{
		_ChatBuffer.push_back(CChatMsgNode(chatMsg, false));
		//nldebug("<impulseChat> Received CHAT, put in buffer : waiting association");
		return;
	}

	// display
	ucstring	ucstr;
	buildChatSentence(chatMsg.CompressedIndex, senderStr, chatMsg.Content, type, ucstr);
	chatDisplayer.displayChat(chatMsg.CompressedIndex, ucstr, chatMsg.Content, type, chatMsg.DynChatChanID, senderStr);
}
コード例 #19
0
	void fillBitMemStream( const CCharacterInfos *charInfo,CStringManager::TLanguages language, const CStringManager::TReplacement &rep, NLMISC::CBitMemStream &bms)
	{
		if ( EId == CEntityId::Unknown && _AIAlias != 0)
		{							
			CAIAliasManager& aiAliasMgr = IOS->getAIAliasManager();
			if (aiAliasMgr.is(_AIAlias)) 
			{

				static const string NAME("name");
				CSString format = rep.Format;
				if (format.left(4) != NAME)
				{
					// can't replace this parameter, write a del char
					const static string s("\010");
					uint32 index = SM->storeString(s);
					bms.serial(index);
					return;
				}
				else
				{
					uint32 index ;
					index = aiAliasMgr.getShortNameIndex(_AIAlias);
					if (index)
					{
						bms.serial(index);
						return;
					}
					index = aiAliasMgr.getTitleIndex(_AIAlias, language);
					if (index)
					{
						bms.serial(index);
						return;
					}
				}
				// No translated title or translated phrase found send ''
				//		ucstring temp(EId.toString());
				const ucstring NoName("''");
				uint32 index = SM->storeString(NoName);
				bms.serial(index);
				return;
			}
		}

		if (EId.getType() == RYZOMID::player || EId.getType() == RYZOMID::npc)
		{	
			CCharacterInfos	*playerInfo = IOS->getCharInfos(EId);
			if (playerInfo != 0)
			{
				static const string NAME("name");
				CSString format = rep.Format;
				if (format.left(4) != NAME)
				{
					// can't replace this parameter, write a del char
					const static string s("\010");
					uint32 index = SM->storeString(s);
					bms.serial(index);
					return;
				}
				else
				{
					if (!playerInfo->ShortName.empty())
					{
						uint32 index = playerInfo->ShortNameIndex;
						bms.serial(index);
						return;
					}
					if (!playerInfo->Title.empty())
					{
						uint32 index = SM->translateTitle(playerInfo->Title, language);
						// we must match this index against the function table
						bms.serial(index);
						return;
					}
				}
			}
		}

		// special case fauna entity or unnamed npc (i.e. npc not registered with a character name, like fauna in npc state machine)
		if (EId.getType() == RYZOMID::creature || EId.getType() == RYZOMID::npc)
		{
			// a big FAKE
			STRING_MANAGER::TParamType realType = ParamId.Type;
			ParamId.Type = STRING_MANAGER::creature;
			CParameterTraits::fillBitMemStream(charInfo, language, rep, bms);

			// restore normal type
			ParamId.Type = realType;
			return;
		}

		// no info on the name, just send the EID as string.
//		ucstring temp(EId.toString());
		const ucstring NoName("''");
		uint32 index = SM->storeString(NoName);
		bms.serial(index);
	}
コード例 #20
0
	/// Fill a bitmem strean with the parameter value.
	virtual void fillBitMemStream( const CCharacterInfos *charInfo, CStringManager::TLanguages language, const CStringManager::TReplacement &rep, NLMISC::CBitMemStream &bms)
	{
		uint32 id = SM->storeString(Literal);
		bms.serial(id);
	}
コード例 #21
0
	void fillBitMemStream( const CCharacterInfos *charInfo,CStringManager::TLanguages language, const CStringManager::TReplacement &rep, NLMISC::CBitMemStream &bms)
	{
		bms.serial(StringId);
	}
コード例 #22
0
	void fillBitMemStream( const CCharacterInfos *charInfo,CStringManager::TLanguages language, const CStringManager::TReplacement &rep, NLMISC::CBitMemStream &bms)
	{
		// TODO : serial only 48 bits
		bms.serial(Money);
	}
//-----------------------------------------------
//	writePermanentDelta
//
// TODO: Maybe an optimization will be required to prevent sending null properties
// when a character is added to a CCDBGroup.
//-----------------------------------------------
bool	CCDBSynchronised::writePermanentDelta( NLMISC::CBitMemStream& s )
{
	static uint nbPermaDeltaSent = 0;

	// Test the changed property count and make room to store the number of property pushed (poked later)
	uint origChangedPropertyCount = _DataContainer.getPermanentChangedPropertyCount();
	if ( origChangedPropertyCount == 0 )
		return false;
	uint bitposOfNbChanges = s.getPosInBit();
	uint32 dummy = 0;
	s.serial( dummy, CDBChangedPropertyCountBitSize ); // optimising s.reserveBits( CDBChangedPropertyCountBitSize )

	// Browse changes and write them
	uint32 nbChanges = 0;

	uint32 bitsize = CDBChangedPropertyCountBitSize; // initialize with the size of the reserved bits for the number of changes
	TCDBDataIndex dataIndex = _DataContainer.getPermanentFirstChanged();
	while ( dataIndex != CDB_LAST_CHANGED
			/*&& (bitsize < maxBitSize)*/ )
	{
		++nbPermaDeltaSent;

		// Retrieve the structure node corresponding to the index
		ICDBStructNode *node = CCDBStructBanks::instance()->getNodeFromDataIndex( _Bank, dataIndex );
		nlassert( node );

		if ( node->isAtomic() ) // counts for 1 change
		{
#ifdef NL_DEBUG
			nlassert( dynamic_cast<CCDBStructNodeBranch*>(node) ); // should not be leaf because the main tracker pushes the atom group index for atomic leaves
#endif
			// Build and push the binary atom id
			ICDBStructNode::CBinId binId;
			(static_cast<CCDBStructNodeBranch*>(node))->buildBinIdFromLeaf( binId );
			bitsize += binId.writeToBitMemStream( s );
			//nlinfo( "CDB/ATOM: Written bin id %s", binId.toString().c_str() );
			//_DataContainer.displayAtomChanges( node->getDataIndex() );

			// Make room to store the atom bitfield
			uint bitposOfAtomBitfield = s.getPosInBit();
			uint indexInAtom = 0;
			node->foreachLeafCall( cbNop, indexInAtom, NULL ); // count the number of siblings
			uint nbAtomElements = indexInAtom;
			s.reserveBits( nbAtomElements );
			bitsize += nbAtomElements;
			//nlinfo( "CDB/ATOM: Reserved %u bits (%d)", nbAtomElements, s.getPosInBit()-bitposOfAtomBitfield );

			// Browse the siblings of the atom node, and push the deltas for the properties marked as changes, updating the bitfield
			TPushAtomChangeStruct arg;
			arg.CdbSync = this;
			arg.BitSize = &bitsize;
			arg.S = &s;
			arg.AtomBitfield.resize( nbAtomElements );
			indexInAtom = 0;
			node->foreachLeafCall( cbPushDeltaOfLeafInAtomIfChangedPermanent, indexInAtom, (void*)&arg );
			
			// Fill the placeholder with the bitfield
			s.pokeBits( arg.AtomBitfield, bitposOfAtomBitfield );
			if ( VerboseDatabase )
			{
				nldebug( "CDB/ATOM: Bitfield: %s", arg.AtomBitfield.toString().c_str() );
			}

		}
		else
		{
#ifdef NL_DEBUG
			nlassert( dynamic_cast<CCDBStructNodeLeaf*>(node) );
#endif

			// Push the binary property id
			bitsize += static_cast<CCDBStructNodeLeaf*>(node)->binLeafId().writeToBitMemStream( s );

			// Push the value
			pushDeltaPermanent( s, static_cast<CCDBStructNodeLeaf*>(node), bitsize );
		}

		dataIndex = _DataContainer.getPermanentNextChanged( dataIndex );
		++nbChanges;
	}

	// Fill the placeholder with the number of changes
	s.poke( nbChanges, bitposOfNbChanges, CDBChangedPropertyCountBitSize );
	//s.displayStream( "writeDelta" );

#ifdef TRACE_SET_VALUE
	if ( VerboseDatabase )
		nldebug( "%u: CDB: Permanent Delta pushed (%u changes written, %u remaining)", CTickEventHandler::getGameCycle(), nbChanges, getChangedPropertyCount() );
#endif

	nldebug( "Filled %u permanent changes", nbPermaDeltaSent );

	return true;
}