1608 lines
59 KiB
C++
1608 lines
59 KiB
C++
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
|
|
// 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 <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
#include "stdpch.h"
|
|
#include "game_share/action_factory.h"
|
|
#include "game_share/action_position.h"
|
|
#include "game_share/action_sint64.h"
|
|
#include "game_share/mode_and_behaviour.h"
|
|
#include "game_share/entity_types.h"
|
|
#include "distance_prioritizer.h"
|
|
#include "client_host.h"
|
|
#include "vision_provider.h"
|
|
|
|
#include <nel/misc/command.h>
|
|
#include "frontend_service.h"
|
|
#ifdef TEST_LOST_PACKET
|
|
#include <nel/misc/variable.h>
|
|
#endif
|
|
|
|
using namespace std;
|
|
using namespace CLFECOMMON;
|
|
using namespace NLMISC;
|
|
using namespace NLNET;
|
|
|
|
|
|
// Test feature to simulate a lost packet when sending a new sheet. Works with 1 client only.
|
|
#ifdef TEST_LOST_PACKET
|
|
CVariable<bool> TestPacketLost( "test", "TestPacketLost", "", true, 0, 1 );
|
|
TGameCycle TestPacketLostTimer = 0;
|
|
TCLEntityId TestPacketLostSlot = 0;
|
|
#endif
|
|
|
|
TClientId verbosePropertiesSent = INVALID_CLIENT;
|
|
|
|
// TODO-Minimal visual bandwith
|
|
/*sint32 NbMinimalVisualBits;
|
|
|
|
void cbNbMinimalVisualBytesChanged( IVariable& var );
|
|
|
|
CVariable<sint32> NbMinimalVisualBytes( "fe", "NbMinimalVisualBytes", "Min number of bytes for vision per msg", 50, 0, true, cbNbMinimalVisualBytesChanged, true );
|
|
|
|
void cbNbMinimalVisualBytesChanged( IVariable& var )
|
|
{
|
|
NbMinimalVisualBits = NbMinimalVisualBytes.get() * 8;
|
|
}*/
|
|
|
|
|
|
/// Init
|
|
void CDistancePrioritizer::init( CVisionArray *va, CVisionProvider *vp, CHistory *hy )
|
|
{
|
|
_VisionArray = va;
|
|
_VisionProvider = vp;
|
|
_History = hy;
|
|
SortSpreader.init();
|
|
|
|
for ( sint i=0; i!=NB_VISUAL_PROPERTIES; ++i )
|
|
_DistThresholdTable[i] = -1;
|
|
|
|
_VisualPropertyTreeRoot = (TVPNodeServer*)NewNode();
|
|
sint nbProp = _VisualPropertyTreeRoot->buildTree();
|
|
|
|
// Check that the last property matches the number of properties-1
|
|
if ( nbProp != NB_VISUAL_PROPERTIES )
|
|
{
|
|
nlwarning( "Found %hu properties in the sheets, the prioritizer knows %hu properties", NB_VISUAL_PROPERTIES, nbProp );
|
|
}
|
|
|
|
// Init distance thresholds
|
|
CLFECOMMON::initThresholdTable( _DistThresholdTable );
|
|
}
|
|
|
|
|
|
/*
|
|
* Calculate the priorities
|
|
*/
|
|
void CDistancePrioritizer::calculatePriorities()
|
|
{
|
|
// Sort
|
|
if ( SortSpreader.mustProcessNow() )
|
|
{
|
|
THostMap::iterator icm;
|
|
sint clientmapindex, outerBoundIndex;
|
|
SortSpreader.getProcessingBounds( icm, clientmapindex, outerBoundIndex );
|
|
|
|
while ( clientmapindex < outerBoundIndex )
|
|
{
|
|
CClientHost *clienthost = GETCLIENTA(icm);
|
|
|
|
// Prioritize only at the opposite time of sending for a particular client
|
|
if ( ! clienthost->whenToSend() )
|
|
{
|
|
// Update priorities
|
|
updatePriorityOfEntitiesSeenByClient( clienthost->clientId() );
|
|
|
|
// Sort entities by decreasing priority
|
|
sortEntitiesOfClient( clienthost->clientId() );
|
|
}
|
|
|
|
++clientmapindex;
|
|
++icm;
|
|
}
|
|
|
|
SortSpreader.endProcessing( icm );
|
|
}
|
|
SortSpreader.incCycle();
|
|
}
|
|
|
|
/*
|
|
#define arbitrateDiscreetProperty( name ) \
|
|
get##name##node()->BranchHasPayload = entityIsWithinDistanceThreshold( PROPERTY_##name ) && discreetPropertyHasChanged( PROPERTY_##name, (TYPE_##name*)NULL )
|
|
|
|
#define arbitrateDiscreetPropertyWithoutThreshold( name ) \
|
|
get##name##node()->BranchHasPayload = discreetPropertyHasChanged( PROPERTY_##name, (TYPE_##name*)NULL )
|
|
|
|
#define arbitrateTargetList( name ) \
|
|
get##name##node()->BranchHasPayload = targetListHasChanged( PROPERTY_##name, (TYPE_##name*)NULL )
|
|
*/
|
|
|
|
#ifdef STORE_MIRROR_VP_IN_CLASS
|
|
|
|
#define arbitrateDiscreetProperty( entry, name ) \
|
|
GET_VP_NODE(name)->BranchHasPayload = \
|
|
entityIsWithinDistanceThreshold( PROPERTY_##name ) \
|
|
&& discreetPropertyHasChanged( entry.Properties[PROPERTY_##name], sentity->VP_##name, PROPERTY_##name, (TYPE_##name*)NULL )
|
|
|
|
#define arbitrateDiscreetPropertyWithoutThreshold( entry, name ) \
|
|
GET_VP_NODE(name)->BranchHasPayload = \
|
|
discreetPropertyHasChanged( entry.Properties[PROPERTY_##name], sentity->VP_##name, PROPERTY_##name, (TYPE_##name*)NULL )
|
|
|
|
#else // STORE_MIRROR_VP_IN_CLASS
|
|
|
|
#define arbitrateDiscreetProperty( entry, name ) \
|
|
GET_VP_NODE(name)->BranchHasPayload = \
|
|
entityIsWithinDistanceThreshold( PROPERTY_##name ) \
|
|
&& discreetPropertyHasChanged( entry.Properties[PROPERTY_##name], PROPERTY_##name, (TYPE_##name*)NULL )
|
|
|
|
#define arbitrateDiscreetPropertyWithoutThreshold( entry, name ) \
|
|
GET_VP_NODE(name)->BranchHasPayload = \
|
|
discreetPropertyHasChanged( entry.Properties[PROPERTY_##name], PROPERTY_##name, (TYPE_##name*)NULL )
|
|
|
|
#define arbitrateTargetList( entry, name ) \
|
|
|
|
#endif // STORE_MIRROR_VP_IN_CLASS
|
|
|
|
|
|
#define arbitrateNeverSendProperty( name ) \
|
|
GET_VP_NODE(name)->BranchHasPayload = false
|
|
|
|
#define arbitrateTargetList( entry, name ) \
|
|
GET_VP_NODE(name)->BranchHasPayload = \
|
|
targetListHasChanged( entry.Properties[PROPERTY_##name], PROPERTY_##name, (TYPE_##name*)NULL )
|
|
|
|
|
|
#define DECLARE_AP(name) CActionSint64 *ap = CActionFactory::getInstance()->getVolatilePropAction( TVPNodeServer::PrioContext.Slot, PROPERTY_##name )
|
|
#define DECLARE_AP_INDEX(index) CActionSint64 *ap = CActionFactory::getInstance()->getVolatilePropAction( TVPNodeServer::PrioContext.Slot, index )
|
|
#define REMOVE_AP()
|
|
|
|
/*
|
|
#define DECLARE_AP(name) CActionSint64 *ap = (CActionSint64*)CActionFactory::getInstance()->createByPropIndex( TVPNodeServer::PrioContext.Slot, PROPERTY_##name )
|
|
#define DECLARE_AP_INDEX(index) CActionSint64 *ap = (CActionSint64*)CActionFactory::getInstance()->createByPropIndex( TVPNodeServer::PrioContext.Slot, index )
|
|
#define REMOVE_AP() CActionFactory::getInstance()->remove( (CAction*&)ap )
|
|
*/
|
|
|
|
|
|
/*
|
|
*
|
|
*/
|
|
void CDistancePrioritizer::fillOutBox( CClientHost& client, TOutBox& outbox )
|
|
{
|
|
H_AUTO(FillOutBox)
|
|
|
|
TClientId clientId = client.clientId();
|
|
initDispatchingCycle( clientId );
|
|
#ifdef NL_DEBUG
|
|
client.MoveNumber = 0;
|
|
#endif
|
|
|
|
if (!client.entityIndex().isValid())
|
|
return;
|
|
|
|
// initialize common context part
|
|
TVPNodeServer::PrioContext.Prioritizer = this;
|
|
TVPNodeServer::PrioContext.ClientHost = &client;
|
|
TVPNodeServer::PrioContext.ClientId = clientId;
|
|
CMirrorPropValueRO<TYPE_TARGET_ID> targetIndexOfClient( TheDataset, client.entityIndex(), DSPropertyTARGET_ID );
|
|
|
|
// TODO-Minimal visual bandwith
|
|
/*sint32 initialPosInBit = outbox.getPosInBit();*/
|
|
sint32 currentPosInBit;
|
|
while ( true )
|
|
{
|
|
currentPosInBit = outbox.getPosInBit();
|
|
|
|
// Browse the seen entities, sorted by distance
|
|
TCLEntityId slot = getNextEntityToStudy( clientId );
|
|
if ( slot == INVALID_SLOT )
|
|
{
|
|
// Exit when all the pairs have been successfully filled
|
|
#ifdef TEST_LOST_PACKET
|
|
if ( (TestPacketLostTimer!=0) && (CTickEventHandler::getGameCycle() >= TestPacketLostTimer) )
|
|
{
|
|
nldebug( "Negative ack for %hu %hu", client.clientId(), TestPacketLostSlot );
|
|
_History->_PacketHistory.negativeAck( client.clientId(), TestPacketLostSlot, PROPERTY_SHEET, 0 );
|
|
TestPacketLostTimer = 0;
|
|
}
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
// TODO-Minimal visual bandwith
|
|
// Always allow to write a minimal amount of visual properties
|
|
/*if ( currentPosInBit - initialPosInBit >= NbMinimalVisualBits )*/
|
|
{
|
|
// Don't fill if the free space is lower than 32 bits (checked once per pair, not once per property)
|
|
if ( currentPosInBit + 32 > client.getCurrentThrottle() )
|
|
{
|
|
// Exit when the size limit has been reached before all the pairs have been filled
|
|
#ifdef NL_DEBUG
|
|
uint nbRemainingPairs = (uint)_PrioritizedEntitiesByClient[clientId].size() - _CurrentEntitiesToStudy[clientId];
|
|
if ( nbRemainingPairs > 0 )
|
|
LOG_WHAT_IS_SENT( "%u: C%hu S%hu: %u pairs remaining", CTickEventHandler::getGameCycle(), clientId, (uint16)slot, nbRemainingPairs );
|
|
LOG_WHAT_IS_SENT( "C%hu: outbox full (%d bits)", clientId, currentPosInBit );
|
|
#endif
|
|
#ifdef TEST_LOST_PACKET
|
|
if ( (TestPacketLostTimer!=0) && (CTickEventHandler::getGameCycle() >= TestPacketLostTimer) )
|
|
{
|
|
nldebug( "Negative ack for %hu %hu", client.clientId(), TestPacketLostSlot );
|
|
_History->_PacketHistory.negativeAck( client.clientId(), TestPacketLostSlot, PROPERTY_SHEET, 0 );
|
|
TestPacketLostTimer = 0;
|
|
}
|
|
#endif
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Get the entity corresponding to the client/slot pair
|
|
TPairState& pairState = _VisionArray->getPairState( clientId, slot );
|
|
CEntity* sentity = NULL;
|
|
|
|
TEntityIndex entityIndex = pairState.EntityIndex;
|
|
TVPNodeServer::PrioContext.EntityIndex = entityIndex;
|
|
|
|
if ( entityIndex.isValid() )
|
|
sentity = TheEntityContainer->getEntity( entityIndex );
|
|
|
|
if ( pairState.associationSuppressed() )
|
|
{
|
|
// Pure unassociation case: we must send an empty block
|
|
serialSlotHeader( client, NULL, pairState, slot, outbox );
|
|
uint32 bits = 0;
|
|
outbox.serialAndLog2( bits, 2 ); // 2 bits for pos & other properties
|
|
//nldebug( "Pure unassociation of C%hu S%hu", clientId, (uint16)slot );
|
|
|
|
// The first time, sentity is non-null. If this is a resending (after a neg-ack), it's null.
|
|
if ( sentity )
|
|
_VisionProvider->postRemovePair( clientId, slot );
|
|
}
|
|
else
|
|
{
|
|
if ( sentity == NULL )
|
|
continue;
|
|
|
|
H_BEFORE(OneSlotArbitrate)
|
|
|
|
H_BEFORE(OneSlotArbitrateInit)
|
|
|
|
// Initialize the context
|
|
TVPNodeServer::PrioContext.Slot = slot;
|
|
TVPNodeServer::PrioContext.EntityIndex = entityIndex;
|
|
TVPNodeServer::PrioContext.Sentity = sentity;
|
|
//TVPNodeServer::PrioContext.PairState = &pairState;
|
|
TVPNodeServer::PrioContext.DistanceCE = pairState.DistanceCE;
|
|
TVPNodeServer::PrioContext.Timestamp = 0;
|
|
TVPNodeServer::PrioContext.IsTarget = (targetIndexOfClient() == entityIndex);
|
|
TVPNodeServer::PrioContext.PositionAlreadySent = (_History->getMileage( clientId, slot ) != 0);
|
|
TVPNodeServer::PrioContext.ZCache = sentity->z(entityIndex); // setup Z cache for all later mirror access to entity->z()
|
|
|
|
const CPropertyHistory::CEntityEntry& entry = _History->getEntityEntry(TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.Slot);
|
|
|
|
// Debug display
|
|
//nlinfo( "Preparing a block to client %hu (%s) for slot %hu", clientId, client.eId().toString().c_str(), (uint16)slot );
|
|
|
|
// Continuous properties
|
|
uint8 seenEntityType = TheDataset.getEntityId( entityIndex ).getType();
|
|
|
|
H_AFTER(OneSlotArbitrateInit)
|
|
|
|
bool thetaIntMode = false;
|
|
|
|
// Arbitrate all discreet properties
|
|
switch (seenEntityType)
|
|
{
|
|
default:
|
|
arbitrateAllDiscreetProperties(entry);
|
|
break;
|
|
|
|
case RYZOMID::npc:
|
|
arbitrateNPCDiscreetProperties(entry);
|
|
break;
|
|
|
|
case RYZOMID::creature:
|
|
arbitrateCreatureDiscreetProperties(entry);
|
|
break;
|
|
|
|
case RYZOMID::forageSource:
|
|
arbitrateForageSourceDiscreetProperties(entry);
|
|
|
|
thetaIntMode = true;
|
|
|
|
break;
|
|
}
|
|
|
|
H_BEFORE(OneSlotArbitratePropagate)
|
|
|
|
// Propagate back BranchHasPayload flags
|
|
//_VisualPropertyTreeRoot->propagateBackBranchHasPayload();
|
|
TVPNodeServer::fastPropagateBackBranchHasPayload();
|
|
|
|
H_AFTER(OneSlotArbitratePropagate)
|
|
|
|
H_AFTER(OneSlotArbitrate)
|
|
|
|
// ******** Fill the buffer with the properties ********
|
|
|
|
// Fill the header
|
|
if ( _VisualPropertyTreeRoot->BranchHasPayload )
|
|
{
|
|
H_AUTO(OneSlotFill)
|
|
|
|
//nldebug( "Filling for C%hu, pass %u, BEFORE HEADER: bitpos: %d", clientId, ++i, outbox.getPosInBit() );
|
|
//nlinfo( "C%hu S%hu AB%hu", clientId, (uint16)slot, (uint32)pairState.AssociationChangeBits );
|
|
serialSlotHeader( client, sentity, pairState, slot, outbox );
|
|
|
|
//nldebug( "AFTER HEADER: pos: %d", outbox.getPosInBit() );
|
|
|
|
// Fill the position if required
|
|
//TVPNodeServer *currentNode = _VisualPropertyTreeRoot;
|
|
|
|
outbox.serialBitAndLog( GET_VP_NODE(POSITION)->BranchHasPayload );
|
|
if ( GET_VP_NODE(POSITION)->BranchHasPayload )
|
|
{
|
|
//CActionPosition *ap = (CActionPosition*)(CActionFactory::getInstance()->createByPropIndex( slot, PROPERTY_POSITION ));
|
|
//ap->PropertyCode = PROPERTY_POSITION;
|
|
CActionPosition* ap = CActionFactory::getInstance()->getVolatilePositionAction(slot);
|
|
|
|
// When the mode is transmitted or the entity is not in local mode (first bit of Z), transmit the *absolute* position
|
|
if ( (! (TVPNodeServer::PrioContext.ZCache & 0x1)) ||
|
|
(GET_VP_NODE(POSITION)->BranchHasPayload && GET_VP_NODE(MODE)->BranchHasPayload) )
|
|
{
|
|
ap->Position[0] = sentity->X();
|
|
ap->Position[1] = sentity->Y();
|
|
//ap->Position[2] = sentity->z( entityIndex );
|
|
ap->Position[2] = TVPNodeServer::PrioContext.ZCache;
|
|
ap->IsRelative = false;
|
|
LOG_WHAT_IS_SENT( "%u: C%hu S%hu: Filling ABSPOS: %d %d %d m", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, sentity->posXm( entityIndex ), sentity->posYm( entityIndex ), sentity->posZm( entityIndex ) );
|
|
}
|
|
else
|
|
{
|
|
CMirrorPropValueRO<sint32> propLX( TheDataset, entityIndex, DSPropertyLocalX );
|
|
CMirrorPropValueRO<sint32> propLY( TheDataset, entityIndex, DSPropertyLocalY );
|
|
CMirrorPropValueRO<sint32> propLZ( TheDataset, entityIndex, DSPropertyLocalZ );
|
|
ap->Position[0] = propLX();
|
|
ap->Position[1] = propLY();
|
|
ap->Position[2] = propLZ();
|
|
ap->IsRelative = true;
|
|
LOG_WHAT_IS_SENT( "%u: C%hu S%hu: Filling RELPOS: %d %d %d mm", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, ap->Position[0], ap->Position[1], ap->Position[2] );
|
|
}
|
|
|
|
CActionFactory::getInstance()->packFast( ap, outbox );
|
|
_History->storePosition( clientId, client.sendNumber(), ap, sentity->Mileage, TVPNodeServer::PrioContext.IsTarget, TVPNodeServer::PrioContext.Timestamp );
|
|
//CActionFactory::getInstance()->remove( (CAction*&)ap );
|
|
++client.NbActionsSentAtCycle;
|
|
}
|
|
|
|
// Fill the orientation if required
|
|
//currentNode = currentNode->B;
|
|
TVPNodeServer *currentNode = _VisualPropertyTreeRoot->B;
|
|
|
|
outbox.serialBitAndLog( currentNode->BranchHasPayload );
|
|
if ( currentNode->BranchHasPayload )
|
|
{
|
|
outbox.serialBitAndLog( currentNode->A->BranchHasPayload );
|
|
if ( currentNode->A->BranchHasPayload )
|
|
{
|
|
//CActionSint64 *ap = (CActionSint64*)(CActionFactory::getInstance()->createByPropIndex( slot, PROPERTY_ORIENTATION ));
|
|
//ap->PropertyCode = PROPERTY_ORIENTATION; // useless: already set by createByPropIndex
|
|
DECLARE_AP(ORIENTATION);
|
|
|
|
CMirrorPropValueRO<float> prop( TheDataset, entityIndex, DSPropertyORIENTATION );
|
|
LOG_WHAT_IS_SENT( "%u: Filling buffer for C%hu S%hu ORIENT (P%hu) : %.1f", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, prop() );
|
|
|
|
uint32 value = ( thetaIntMode ? (uint32)(prop()) : *((uint32*)&(prop())) );
|
|
|
|
ap->setAndPackValue( value, outbox );
|
|
_History->store( clientId, client.sendNumber(), ap );
|
|
|
|
// TODO: send less bits for forage source entity
|
|
|
|
//CActionFactory::getInstance()->remove( (CAction*&)ap );
|
|
REMOVE_AP();
|
|
|
|
++client.NbActionsSentAtCycle;
|
|
}
|
|
|
|
// Fill the required discreet properties if required
|
|
currentNode->B->fillDiscreetProperties( outbox );
|
|
//nlinfo("end fill");
|
|
//TVPNodeServer::fastFillDiscreetProperties( outbox );
|
|
//nlinfo("end fastfill");
|
|
}
|
|
}
|
|
|
|
#ifdef NL_DEBUG
|
|
if ( _VisualPropertyTreeRoot->A->BranchHasPayload )
|
|
{
|
|
//CClientEntityIdTranslator::CEntityInfo& info = client.IdTranslator.getInfo( slot ); // CHANGED BEN
|
|
//if ( info.AssociationState == CClientEntityIdTranslator::CEntityInfo::AwaitingAssAck ) // CHANGED BEN
|
|
if ( _VisionArray->getAssociationState(clientId, slot) == TPairState::AwaitingAssAck )
|
|
{
|
|
nlwarning( "C%hu S%hu: Sending position but sheet id not sent", clientId, (uint16)slot );
|
|
client.displaySlotProperties( slot );
|
|
}
|
|
}
|
|
|
|
//if ( (verbosePropertiesSent==9999) || (verbosePropertiesSent==TVPNodeServer::PrioContext.ClientId) )
|
|
//{
|
|
// nldebug( "To C%hu", clientId );
|
|
// outbox.displayStream();
|
|
//}
|
|
#endif
|
|
|
|
// ******** End of iteration ********
|
|
//_VisualPropertyTreeRoot->displayStatesOfTreeLeaves();
|
|
//nldebug( "C%hu S%hu: end of pair block at bitpos %d", clientId, (uint16)slot, outbox.getPosInBit() );
|
|
}
|
|
|
|
// Reset the priority of the pair, even if nothing was filled
|
|
if ( slot != 0 )
|
|
pairState.resetPrio(); // note: nothing can remain to be filled, as we allow to exceed the size limit
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Test the criterion for the position of the entity 'slot' seen by 'clientId'
|
|
*/
|
|
bool CDistancePrioritizer::positionHasChangedEnough()
|
|
{
|
|
// TEMP: do not send if local mode (because the local position is useless for the mektoub)
|
|
if ( TVPNodeServer::PrioContext.ZCache & 0x1 )
|
|
return false;
|
|
|
|
uint32 lastSentMileage = _History->getMileage( TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.Slot );
|
|
if ( lastSentMileage != 0 )
|
|
{
|
|
// Calculate difference distance between current and lastsent mileage (unsigned to allow mileage overflow)
|
|
if ( (TVPNodeServer::PrioContext.Sentity->Mileage - lastSentMileage) * _DistanceDeltaRatio > (uint32)(TVPNodeServer::PrioContext.DistanceCE) )
|
|
{
|
|
#ifdef NL_DEBUG
|
|
++(TVPNodeServer::PrioContext.ClientHost->MoveNumber);
|
|
#endif
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if (verbosePropertiesSent==0)
|
|
{
|
|
nldebug("Ignoring move: Mileage=%u, lastSentMileage=%u, _DistanceDeltaRatio=%u, DistanceCE=%u",
|
|
(uint32) TVPNodeServer::PrioContext.Sentity->Mileage,
|
|
(uint32) lastSentMileage,
|
|
(uint32) _DistanceDeltaRatio,
|
|
(uint32) TVPNodeServer::PrioContext.DistanceCE );
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Not sent yet
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Test the criterion for thetaIntMode
|
|
*/
|
|
bool CDistancePrioritizer::thetaIntModehasChanged(const CPropertyHistory::CPropertyEntry& entry)
|
|
{
|
|
if ( ! entityIsWithinDistanceThreshold( PROPERTY_ORIENTATION ) )
|
|
return false;
|
|
|
|
if ( entry.HasValue )
|
|
{
|
|
CMirrorPropValueRO<float> currentTheta( TheDataset, TVPNodeServer::PrioContext.EntityIndex, DSPropertyORIENTATION );
|
|
return ( (uint32)currentTheta != (uint32)entry.LastSent );
|
|
}
|
|
else
|
|
{
|
|
return TVPNodeServer::PrioContext.Sentity->propertyIsInitialized( PROPERTY_ORIENTATION, DSPropertyORIENTATION, TVPNodeServer::PrioContext.EntityIndex, (TYPE_ORIENTATION*)NULL );
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Test the criterion for the orientation of the entity 'slot' seen by 'clientId' + initialized
|
|
*/
|
|
bool CDistancePrioritizer::orientationHasChangedEnough(const CPropertyHistory::CPropertyEntry& entry, float angleRatio )
|
|
{
|
|
if ( ! entityIsWithinDistanceThreshold( PROPERTY_ORIENTATION ) )
|
|
return false;
|
|
|
|
if ( entry.HasValue )
|
|
{
|
|
CMirrorPropValueRO<float> currentAngle( TheDataset, TVPNodeServer::PrioContext.EntityIndex, DSPropertyORIENTATION );
|
|
|
|
/*
|
|
// Orientation is a float angle in radian
|
|
const float& oldangle = *((float*)&(entry.LastSent));
|
|
float deltaAngle = (float)fabs( (float)(currentAngle() - oldangle) );
|
|
deltaAngle = (float)fmod( deltaAngle+(2*Pi), (2*Pi) );
|
|
|
|
//nldebug( "getDelta(theta) : dA=%g", deltaAngle );
|
|
return ( deltaAngle > (float)Pi/angleRatio ); // the orientation is useful only when the pos does not change
|
|
*/
|
|
|
|
float oldangle;
|
|
entry.getValue(oldangle);
|
|
float deltaAngle = (float)( Pi - fabs(fmod(currentAngle()-oldangle+4*Pi, 2*Pi)-Pi) ); // deltaAngle is in [0, 2*Pi]
|
|
|
|
//nldebug( "getDelta(theta) : dA=%g", deltaAngle );
|
|
return ( deltaAngle*angleRatio > (float)Pi ); // the orientation is useful only when the pos does not change
|
|
|
|
}
|
|
else
|
|
{
|
|
// Not sent yet => always sent theta, even if it's zero (anyway, it's unlikely to be exactly 0.0: remind that this initial float is determined, for bots, by leveldesigners with the mouse)
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Test the criterion for the specified property of the entity 'slot' seen by 'clientId'
|
|
*/
|
|
inline bool CDistancePrioritizer::entityIsWithinDistanceThreshold( TPropIndex propIndex )
|
|
{
|
|
// Compare distance with the threshold
|
|
//nldebug( "C%hu - slot %hu - prop %hu: DISTANCE=%d THRESHOL=%d", TVPNodeServer::PrioContext.ClientHost->clientId(), (uint16)TVPNodeServer::PrioContext.Slot, propIndex, TVPNodeServer::PrioContext.DistanceCE, TheEntityTranslator->getDistThreshold( propertyid ) );
|
|
return ( TVPNodeServer::PrioContext.DistanceCE < getDistThreshold( propIndex ) );
|
|
}
|
|
|
|
/*
|
|
* Special arbitrate case for BEHAVIOUR property
|
|
* Threshold in this case should be the same than the target list threshold when
|
|
* the behaviour is a range attack or projectile behaviour
|
|
*/
|
|
#ifndef NL_DEBUG
|
|
inline
|
|
#endif
|
|
void CDistancePrioritizer::arbitrateDiscreetBehaviourProperty(const CPropertyHistory::CEntityEntry& entry, CEntity* sentity)
|
|
{
|
|
#ifdef STORE_MIRROR_VP_IN_CLASS
|
|
const CMirrorPropValueRO<TYPE_BEHAVIOUR>& propBehav = sentity->VP_BEHAVIOUR;
|
|
#else
|
|
CMirrorPropValueRO<TYPE_BEHAVIOUR> propBehav( TheDataset, TVPNodeServer::PrioContext.EntityIndex, DSPropertyBEHAVIOUR );
|
|
#endif
|
|
if (!discreetPropertyHasChanged( entry.Properties[PROPERTY_BEHAVIOUR], propBehav, PROPERTY_BEHAVIOUR, (TYPE_BEHAVIOUR*)NULL ))
|
|
{
|
|
GET_VP_NODE(BEHAVIOUR)->BranchHasPayload = false;
|
|
return;
|
|
}
|
|
const MBEHAV::CBehaviour &behav = propBehav();
|
|
TPropIndex refDistanceProperty;
|
|
switch(behav.Behaviour)
|
|
{
|
|
case MBEHAV::CAST_OFF_SUCCESS:
|
|
refDistanceProperty = PROPERTY_TARGET_LIST;
|
|
break;
|
|
case MBEHAV::CAST_OFF_LINK:
|
|
refDistanceProperty = PROPERTY_TARGET_LIST;
|
|
break;
|
|
case MBEHAV::CAST_CUR_SUCCESS:
|
|
refDistanceProperty = PROPERTY_TARGET_LIST;
|
|
break;
|
|
case MBEHAV::CAST_CUR_LINK:
|
|
refDistanceProperty = PROPERTY_TARGET_LIST;
|
|
break;
|
|
case MBEHAV::CAST_MIX_SUCCESS:
|
|
refDistanceProperty = PROPERTY_TARGET_LIST;
|
|
break;
|
|
case MBEHAV::CAST_MIX_LINK:
|
|
refDistanceProperty = PROPERTY_TARGET_LIST;
|
|
break;
|
|
case MBEHAV::RANGE_ATTACK:
|
|
refDistanceProperty = PROPERTY_TARGET_LIST;
|
|
break;
|
|
case MBEHAV::CAST_ACID:
|
|
refDistanceProperty = PROPERTY_TARGET_LIST;
|
|
break;
|
|
case MBEHAV::CAST_BLIND:
|
|
refDistanceProperty = PROPERTY_TARGET_LIST;
|
|
break;
|
|
case MBEHAV::CAST_COLD:
|
|
refDistanceProperty = PROPERTY_TARGET_LIST;
|
|
break;
|
|
case MBEHAV::CAST_ELEC:
|
|
refDistanceProperty = PROPERTY_TARGET_LIST;
|
|
break;
|
|
case MBEHAV::CAST_FEAR:
|
|
refDistanceProperty = PROPERTY_TARGET_LIST;
|
|
break;
|
|
case MBEHAV::CAST_FIRE:
|
|
refDistanceProperty = PROPERTY_TARGET_LIST;
|
|
break;
|
|
case MBEHAV::CAST_HEALHP:
|
|
refDistanceProperty = PROPERTY_TARGET_LIST;
|
|
break;
|
|
case MBEHAV::CAST_MAD:
|
|
refDistanceProperty = PROPERTY_TARGET_LIST;
|
|
break;
|
|
case MBEHAV::CAST_POISON:
|
|
refDistanceProperty = PROPERTY_TARGET_LIST;
|
|
break;
|
|
case MBEHAV::CAST_ROOT:
|
|
refDistanceProperty = PROPERTY_TARGET_LIST;
|
|
break;
|
|
case MBEHAV::CAST_ROT:
|
|
refDistanceProperty = PROPERTY_TARGET_LIST;
|
|
break;
|
|
case MBEHAV::CAST_SHOCK:
|
|
refDistanceProperty = PROPERTY_TARGET_LIST;
|
|
break;
|
|
case MBEHAV::CAST_SLEEP:
|
|
refDistanceProperty = PROPERTY_TARGET_LIST;
|
|
break;
|
|
case MBEHAV::CAST_SLOW:
|
|
refDistanceProperty = PROPERTY_TARGET_LIST;
|
|
break;
|
|
case MBEHAV::CAST_STUN:
|
|
refDistanceProperty = PROPERTY_TARGET_LIST; // valid distance should be the same than the target list
|
|
// because target list and behaviour are sent together
|
|
break;
|
|
default:
|
|
refDistanceProperty = PROPERTY_BEHAVIOUR;
|
|
break;
|
|
}
|
|
//
|
|
GET_VP_NODE(BEHAVIOUR)->BranchHasPayload = entityIsWithinDistanceThreshold(refDistanceProperty);
|
|
}
|
|
|
|
|
|
/*
|
|
* Fill the BranchHasPayload flags of the tree, using the rules deciding if a discreet property need to be sent
|
|
*/
|
|
void CDistancePrioritizer::arbitrateAllDiscreetProperties(const CPropertyHistory::CEntityEntry& entry)
|
|
{
|
|
H_AUTO(arbitrateAllDiscreetProperties);
|
|
|
|
CEntity* sentity = TVPNodeServer::PrioContext.Sentity;
|
|
|
|
arbitrateDiscreetPropertyWithoutThreshold( entry, SHEET );
|
|
arbitrateDiscreetBehaviourProperty( entry, sentity );
|
|
|
|
// Don't limit to the distance threshold when triggering sending of the name of the target
|
|
if ( TVPNodeServer::PrioContext.IsTarget )
|
|
arbitrateDiscreetPropertyWithoutThreshold( entry, NAME_STRING_ID );
|
|
else
|
|
arbitrateDiscreetProperty( entry, NAME_STRING_ID );
|
|
|
|
//arbitrateDiscreetProperty( entry, TARGET_ID ); // now done in fillOutBox() in "mode switch"
|
|
arbitrateDiscreetProperty( entry, CONTEXTUAL );
|
|
arbitrateDiscreetPropertyWithoutThreshold( entry, MODE );
|
|
arbitrateDiscreetProperty( entry, VPA );
|
|
arbitrateDiscreetProperty( entry, VPB );
|
|
arbitrateDiscreetProperty( entry, VPC );
|
|
arbitrateDiscreetPropertyWithoutThreshold( entry, ENTITY_MOUNTED_ID );
|
|
arbitrateDiscreetPropertyWithoutThreshold( entry, RIDER_ENTITY_ID );
|
|
|
|
arbitrateTargetList( entry, TARGET_LIST );
|
|
arbitrateDiscreetProperty( entry, VISUAL_FX );
|
|
|
|
arbitrateDiscreetProperty( entry, GUILD_SYMBOL );
|
|
arbitrateDiscreetProperty( entry, GUILD_NAME_ID );
|
|
arbitrateDiscreetProperty( entry, EVENT_FACTION_ID );
|
|
arbitrateDiscreetProperty( entry, PVP_MODE );
|
|
arbitrateDiscreetProperty( entry, PVP_CLAN );
|
|
arbitrateNeverSendProperty( OWNER_PEOPLE );
|
|
arbitrateDiscreetProperty( entry, OUTPOST_INFOS );
|
|
|
|
if (TVPNodeServer::PrioContext.Slot != 0)
|
|
{
|
|
arbitrateCommonPosAndMode(entry);
|
|
arbitrateDiscreetProperty( entry, TARGET_ID );
|
|
arbitrateDiscreetProperty( entry, BARS );
|
|
}
|
|
else
|
|
{
|
|
arbitrateSlot0PosAndMode(entry);
|
|
arbitrateDiscreetPropertyWithoutThreshold( entry, TARGET_ID ); // no need of threshold
|
|
// never send BARS for User Player (no need since sent with the USER:BARS message)
|
|
arbitrateNeverSendProperty( BARS );
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Fill the BranchHasPayload flags of the tree, using the rules deciding if a discreet property need to be sent
|
|
*/
|
|
void CDistancePrioritizer::arbitrateNPCDiscreetProperties(const CPropertyHistory::CEntityEntry& entry)
|
|
{
|
|
H_AUTO(arbitrateNPCDiscreetProperties);
|
|
|
|
CEntity* sentity = TVPNodeServer::PrioContext.Sentity;
|
|
|
|
arbitrateDiscreetPropertyWithoutThreshold( entry, SHEET );
|
|
arbitrateDiscreetBehaviourProperty(entry, sentity);
|
|
|
|
// Don't limit to the distance threshold when triggering sending of the name of the target
|
|
if ( TVPNodeServer::PrioContext.IsTarget )
|
|
arbitrateDiscreetPropertyWithoutThreshold( entry, NAME_STRING_ID );
|
|
else
|
|
arbitrateDiscreetProperty( entry, NAME_STRING_ID );
|
|
|
|
arbitrateDiscreetProperty( entry, TARGET_ID ); // NPC can never be in Slot 0
|
|
|
|
// Specific distance for NPCs' contextual property
|
|
GET_VP_NODE(CONTEXTUAL)->BranchHasPayload =
|
|
TVPNodeServer::PrioContext.DistanceCE < THRESHOLD_CONTEXTUAL_NPC
|
|
&& discreetPropertyHasChanged( entry.Properties[PROPERTY_CONTEXTUAL], sentity->VP_CONTEXTUAL, PROPERTY_CONTEXTUAL, (TYPE_CONTEXTUAL*)NULL );
|
|
|
|
arbitrateDiscreetPropertyWithoutThreshold( entry, MODE );
|
|
arbitrateDiscreetProperty( entry, BARS );
|
|
arbitrateDiscreetProperty( entry, VPA );
|
|
arbitrateDiscreetProperty( entry, VPB );
|
|
arbitrateDiscreetProperty( entry, VPC );
|
|
|
|
arbitrateDiscreetPropertyWithoutThreshold( entry, ENTITY_MOUNTED_ID );
|
|
|
|
arbitrateNeverSendProperty( RIDER_ENTITY_ID );
|
|
|
|
arbitrateTargetList( entry, TARGET_LIST );
|
|
arbitrateDiscreetProperty( entry, VISUAL_FX );
|
|
arbitrateDiscreetProperty( entry, GUILD_SYMBOL );
|
|
arbitrateDiscreetProperty( entry, GUILD_NAME_ID );
|
|
arbitrateNeverSendProperty( EVENT_FACTION_ID );
|
|
arbitrateNeverSendProperty( PVP_MODE );
|
|
arbitrateNeverSendProperty( PVP_CLAN );
|
|
arbitrateNeverSendProperty( OWNER_PEOPLE );
|
|
arbitrateDiscreetProperty( entry, OUTPOST_INFOS );
|
|
|
|
arbitrateCommonPosAndMode(entry);
|
|
}
|
|
|
|
/*
|
|
* Fill the BranchHasPayload flags of the tree, using the rules deciding if a discreet property need to be sent
|
|
*/
|
|
void CDistancePrioritizer::arbitrateCreatureDiscreetProperties(const CPropertyHistory::CEntityEntry& entry)
|
|
{
|
|
H_AUTO(arbitrateCreatureDiscreetProperties);
|
|
|
|
CEntity* sentity = TVPNodeServer::PrioContext.Sentity;
|
|
|
|
arbitrateDiscreetPropertyWithoutThreshold( entry, SHEET );
|
|
arbitrateDiscreetBehaviourProperty(entry, sentity);
|
|
|
|
// Don't limit to the distance threshold when triggering sending of the name of the target
|
|
if ( TVPNodeServer::PrioContext.IsTarget )
|
|
arbitrateDiscreetPropertyWithoutThreshold( entry, NAME_STRING_ID );
|
|
else
|
|
arbitrateDiscreetProperty( entry, NAME_STRING_ID );
|
|
|
|
arbitrateDiscreetProperty( entry, TARGET_ID ); // Creature can never be in Slot 0
|
|
arbitrateDiscreetProperty( entry, CONTEXTUAL );
|
|
arbitrateDiscreetPropertyWithoutThreshold( entry, MODE );
|
|
arbitrateDiscreetProperty( entry, BARS );
|
|
|
|
arbitrateNeverSendProperty( VPA );
|
|
arbitrateDiscreetProperty( entry, VPB );
|
|
arbitrateNeverSendProperty( VPC );
|
|
arbitrateNeverSendProperty( ENTITY_MOUNTED_ID );
|
|
|
|
arbitrateDiscreetPropertyWithoutThreshold( entry, RIDER_ENTITY_ID );
|
|
arbitrateTargetList( entry, TARGET_LIST );
|
|
|
|
arbitrateNeverSendProperty( VISUAL_FX );
|
|
arbitrateNeverSendProperty( GUILD_SYMBOL );
|
|
arbitrateNeverSendProperty( GUILD_NAME_ID );
|
|
arbitrateNeverSendProperty( EVENT_FACTION_ID );
|
|
arbitrateNeverSendProperty( PVP_MODE );
|
|
arbitrateNeverSendProperty( PVP_CLAN );
|
|
arbitrateDiscreetProperty( entry, OWNER_PEOPLE );
|
|
arbitrateNeverSendProperty( OUTPOST_INFOS );
|
|
|
|
arbitrateCommonPosAndMode(entry);
|
|
}
|
|
|
|
/*
|
|
* Fill the BranchHasPayload flags of the tree, using the rules deciding if a discreet property need to be sent
|
|
*/
|
|
void CDistancePrioritizer::arbitrateForageSourceDiscreetProperties(const CPropertyHistory::CEntityEntry& entry)
|
|
{
|
|
H_AUTO(arbitrateForageSourceDiscreetProperties);
|
|
|
|
CEntity* sentity = TVPNodeServer::PrioContext.Sentity;
|
|
|
|
arbitrateDiscreetPropertyWithoutThreshold( entry, SHEET );
|
|
arbitrateNeverSendProperty( BEHAVIOUR );
|
|
|
|
arbitrateDiscreetProperty( entry, NAME_STRING_ID );
|
|
|
|
arbitrateDiscreetProperty( entry, TARGET_ID ); // ForageSource can never be in Slot 0
|
|
arbitrateDiscreetProperty( entry, CONTEXTUAL );
|
|
|
|
arbitrateNeverSendProperty( MODE );
|
|
|
|
arbitrateDiscreetProperty( entry, BARS );
|
|
|
|
arbitrateNeverSendProperty( VPA );
|
|
arbitrateNeverSendProperty( VPB );
|
|
arbitrateNeverSendProperty( VPC );
|
|
arbitrateNeverSendProperty( ENTITY_MOUNTED_ID );
|
|
arbitrateNeverSendProperty( RIDER_ENTITY_ID );
|
|
|
|
arbitrateTargetList( entry, TARGET_LIST );
|
|
arbitrateDiscreetProperty( entry, VISUAL_FX );
|
|
|
|
arbitrateNeverSendProperty( GUILD_SYMBOL );
|
|
arbitrateNeverSendProperty( GUILD_NAME_ID );
|
|
arbitrateNeverSendProperty( EVENT_FACTION_ID );
|
|
arbitrateNeverSendProperty( PVP_MODE );
|
|
arbitrateNeverSendProperty( PVP_CLAN );
|
|
arbitrateNeverSendProperty( OWNER_PEOPLE );
|
|
arbitrateNeverSendProperty( OUTPOST_INFOS );
|
|
|
|
GET_VP_NODE(POSITION)->BranchHasPayload = sentity->positionIsInitialized() && positionHasChangedEnough();
|
|
GET_VP_NODE(ORIENTATION)->BranchHasPayload = thetaIntModehasChanged(entry.Properties[PROPERTY_ORIENTATION]);
|
|
}
|
|
|
|
|
|
/*
|
|
* Fill the BranchHasPayload flags of the tree, using the rules deciding if a discreet property need to be sent
|
|
*/
|
|
inline void CDistancePrioritizer::arbitrateCommonPosAndMode(const CPropertyHistory::CEntityEntry& entry)
|
|
{
|
|
// Position if changed enough (if not carried by mode)
|
|
// Orientation if changed > 60 degrees (it's head angle only) or in first block (useful for static entities such as bot objects)
|
|
bool modeIsChanging = GET_VP_NODE(MODE)->BranchHasPayload;
|
|
bool sheetIsChanging = GET_VP_NODE(SHEET)->BranchHasPayload;
|
|
bool posIsReady = TVPNodeServer::PrioContext.Sentity->positionIsInitialized();
|
|
|
|
GET_VP_NODE(POSITION)->BranchHasPayload = (!modeIsChanging) && posIsReady && positionHasChangedEnough();
|
|
GET_VP_NODE(ORIENTATION)->BranchHasPayload = (sheetIsChanging && posIsReady) || orientationHasChangedEnough( entry.Properties[PROPERTY_ORIENTATION], 36.0f ); // 5 degrees
|
|
}
|
|
|
|
/*
|
|
* Fill the BranchHasPayload flags of the tree, using the rules deciding if a discreet property need to be sent
|
|
*/
|
|
inline void CDistancePrioritizer::arbitrateSlot0PosAndMode(const CPropertyHistory::CEntityEntry& entry)
|
|
{
|
|
arbitrateNeverSendProperty(POSITION);
|
|
arbitrateNeverSendProperty(ORIENTATION);
|
|
}
|
|
|
|
|
|
/*
|
|
*
|
|
*/
|
|
inline void CDistancePrioritizer::serialSlotHeader( CClientHost& client, CEntity *sentity, TPairState& pairState, CLFECOMMON::TCLEntityId slot, TOutBox& outbox )
|
|
{
|
|
// Slot (8 bits)
|
|
#ifdef NL_DEBUG
|
|
sint beginbitpos = outbox.getPosInBit();
|
|
#endif
|
|
outbox.serialAndLog1( slot );
|
|
|
|
// Association change bits (2 bits)
|
|
uint32 associationBits = (uint32)pairState.AssociationChangeBits;
|
|
outbox.serialAndLog2( associationBits, 2 );
|
|
if ( pairState.AssociationChangeBits != pairState.PrevAssociationBits )
|
|
{
|
|
//LOG_WHAT_IS_SENT( "slot %hu ab %u beginpos %d endpos %d beginbitpos %d endbitpos %d", (uint16)slot, associationBits, beginbitpos/8, outbox.getPosInBit()/8, beginbitpos, outbox.getPosInBit() );
|
|
pairState.PrevAssociationBits = pairState.AssociationChangeBits; // & 0x3;
|
|
_History->storeDisassociation( client.clientId(), slot, client.sendNumber(), pairState.AssociationChangeBits );
|
|
// pairState.AssociationChangeBits &= 0x7F;
|
|
}
|
|
|
|
|
|
// Timestamp (1 or 5 bits, depending on the type of entity) (TVPNodeServer::PrioContext.Timestamp initialized to 0)
|
|
uint32 timestampDelta = 0;
|
|
if ( sentity )
|
|
{
|
|
const CEntityId& seenEntityId = TheDataset.getEntityId( TVPNodeServer::PrioContext.EntityIndex );
|
|
if ( seenEntityId.getType() == RYZOMID::player )
|
|
{
|
|
// For players, always set the timestamp delta, using TickPosition
|
|
// Note: discreet property change times won't be accurate
|
|
TVPNodeServer::PrioContext.Timestamp = sentity->TickPosition;
|
|
timestampDelta = CTickEventHandler::getGameCycle() - sentity->TickPosition;
|
|
if ( timestampDelta > 15 ) // clamp to 4bit
|
|
timestampDelta = 15;
|
|
timestampDelta |= 0x10; // 'timestampIsThere bit': first bit is bit 5 (high to low order)
|
|
}
|
|
else if ( seenEntityId.getType() >= RYZOMID::object )
|
|
{
|
|
// For non-players/non-bots types (e.g. bags), set the timestamp delta if entity is being spawned to the client
|
|
//if ( _VisualPropertyTreeRoot->B->B->getSHEETnode()->BranchHasPayload ) // assumes this is done after arbitrateDiscreetProperties() // CHANGED BEN
|
|
if ( GET_VP_NODE(SHEET)->BranchHasPayload ) // assumes this is done after arbitrateDiscreetProperties()
|
|
{
|
|
TVPNodeServer::PrioContext.Timestamp = TheDataset.getOnlineTimestamp( TVPNodeServer::PrioContext.EntityIndex );
|
|
timestampDelta = CTickEventHandler::getGameCycle() - TVPNodeServer::PrioContext.Timestamp;
|
|
if ( timestampDelta > 15 ) // clamp to 4bit
|
|
timestampDelta = 15;
|
|
timestampDelta |= 0x10; // 'timestampIsThere bit': first bit is bit 5 (high to low order)
|
|
}
|
|
}
|
|
// For bots, the timestamp is not needed, the client will take _ServerGameCycle
|
|
}
|
|
|
|
outbox.serialAndLog2( timestampDelta, (timestampDelta!=0) ? 5 : 1 );
|
|
}
|
|
|
|
#ifdef STORE_MIRROR_VP_IN_CLASS
|
|
|
|
#define caseFillAction( name ) ap->setValue64( TVPNodeServer::PrioContext.Sentity->VP_##name() );
|
|
|
|
#else // STORE_MIRROR_VP_IN_CLASS
|
|
|
|
#define caseFillAction( name ) \
|
|
CMirrorPropValueRO<TYPE_##name> prop( TheDataset, TVPNodeServer::PrioContext.EntityIndex, DSProperty##name ); \
|
|
ap->setValue64( prop() );
|
|
|
|
#endif // STORE_MIRROR_VP_IN_CLASS
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* SHEET
|
|
*/
|
|
void fillSHEET( TOutBox& outbox, TPropIndex )
|
|
{
|
|
#ifdef NL_DEBUG
|
|
// In non-debug cases, it is done in CVisionProvider::addPair()
|
|
//CClientEntityIdTranslator::CEntityInfo& info = TVPNodeServer::PrioContext.ClientHost->IdTranslator.getInfo( TVPNodeServer::PrioContext.Slot ); // CHANGED BEN
|
|
//info.AssociationState = CClientEntityIdTranslator::CEntityInfo::NormalAssociation; // CHANGED BEN
|
|
TVPNodeServer::PrioContext.Prioritizer->getVisionArray()->setAssociationState( TVPNodeServer::PrioContext.ClientId,
|
|
TVPNodeServer::PrioContext.Slot,
|
|
TPairState::NormalAssociation);
|
|
#endif
|
|
bool payloadBit = true;
|
|
outbox.serialBitAndLog( payloadBit );
|
|
|
|
//CActionSint64 *ap = (CActionSint64*)CActionFactory::getInstance()->createByPropIndex( TVPNodeServer::PrioContext.Slot, PROPERTY_SHEET ); // CHANGED BEN
|
|
DECLARE_AP(SHEET);
|
|
|
|
// Pack sheet and compressed row into the action
|
|
#ifdef STORE_MIRROR_VP_IN_CLASS
|
|
CMirrorPropValueRO<TYPE_SHEET>& prop = TVPNodeServer::PrioContext.Sentity->VP_SHEET;
|
|
#else
|
|
CMirrorPropValueRO<TYPE_SHEET> prop( TheDataset, TVPNodeServer::PrioContext.EntityIndex, DSPropertySHEET );
|
|
#endif
|
|
|
|
uint32 sheetValue = prop();
|
|
TDataSetIndex compressedRow = TVPNodeServer::PrioContext.EntityIndex.getCompressedIndex();
|
|
uint64 value = (uint64)sheetValue | (((uint64)compressedRow) << 32);
|
|
ap->setValue64( value );
|
|
LOG_WHAT_IS_SENT( "%u: Filling buffer for C%hu S%hu P%hu SHEET at bitpos %d - value %"NL_I64"u", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, PROPERTY_SHEET, outbox.getPosInBit(), ap->getValue() );
|
|
|
|
// Add row into the action
|
|
ap->packFast( outbox );
|
|
CFrontEndService::instance()->history()->store( TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.ClientHost->sendNumber(), ap );
|
|
//CActionFactory::getInstance()->remove( (CAction*&)ap );
|
|
REMOVE_AP();
|
|
++(TVPNodeServer::PrioContext.ClientHost->NbActionsSentAtCycle);
|
|
|
|
// Include alias if non-null in mirror (only for mission giver NPCs)
|
|
CMirrorPropValueRO<TYPE_ALIAS> aliasProp( TheDataset, TVPNodeServer::PrioContext.EntityIndex, DSPropertyNPC_ALIAS );
|
|
if (aliasProp() != 0)
|
|
{
|
|
bool aliasBit = true;
|
|
outbox.serialBitAndLog( aliasBit );
|
|
outbox.serialAndLog1( const_cast<TYPE_ALIAS&>(aliasProp()) ); // no need to store in history, alias never changes for an entity
|
|
}
|
|
else
|
|
{
|
|
bool aliasBit = false;
|
|
outbox.serialBitAndLog( aliasBit );
|
|
}
|
|
|
|
#ifdef TEST_LOST_PACKET
|
|
if ( TestPacketLost.get() )
|
|
{
|
|
nldebug( "This SHEET sending will be dropped..." );
|
|
TestPacketLostTimer = CTickEventHandler::getGameCycle() + 10;
|
|
TestPacketLost = false;
|
|
TestPacketLostSlot = TVPNodeServer::PrioContext.Slot;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
* BEHAVIOUR
|
|
*/
|
|
void fillBEHAVIOUR( TOutBox& outbox, TPropIndex )
|
|
{
|
|
bool payloadBit = true;
|
|
outbox.serialBitAndLog( payloadBit );
|
|
//CActionSint64 *ap = (CActionSint64*)CActionFactory::getInstance()->createByPropIndex( TVPNodeServer::PrioContext.Slot, PROPERTY_BEHAVIOUR );
|
|
DECLARE_AP(BEHAVIOUR);
|
|
caseFillAction( BEHAVIOUR )
|
|
LOG_WHAT_IS_SENT( "%u: Filling buffer for C%hu S%hu P%hu BEHAVIOUR at bitpos %d - value %"NL_I64"u", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, PROPERTY_BEHAVIOUR, outbox.getPosInBit(), ap->getValue() );
|
|
ap->packFast( outbox );
|
|
CFrontEndService::instance()->history()->store( TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.ClientHost->sendNumber(), ap );
|
|
//CActionFactory::getInstance()->remove( (CAction*&)ap );
|
|
REMOVE_AP();
|
|
++(TVPNodeServer::PrioContext.ClientHost->NbActionsSentAtCycle);
|
|
}
|
|
|
|
|
|
/*
|
|
* NAME_STRING_ID
|
|
*/
|
|
void fillNAME_STRING_ID( TOutBox& outbox, TPropIndex )
|
|
{
|
|
bool payloadBit = true;
|
|
outbox.serialBitAndLog( payloadBit );
|
|
//CActionSint64 *ap = (CActionSint64*)CActionFactory::getInstance()->createByPropIndex( TVPNodeServer::PrioContext.Slot, PROPERTY_NAME_STRING_ID );
|
|
DECLARE_AP(NAME_STRING_ID);
|
|
caseFillAction( NAME_STRING_ID )
|
|
LOG_WHAT_IS_SENT( "%u: Filling buffer for C%hu S%hu P%hu NAME_STRING_ID at bitpos %d - value %"NL_I64"u", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, PROPERTY_NAME_STRING_ID, outbox.getPosInBit(), ap->getValue() );
|
|
ap->packFast( outbox );
|
|
CFrontEndService::instance()->history()->store( TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.ClientHost->sendNumber(), ap );
|
|
//CActionFactory::getInstance()->remove( (CAction*&)ap );
|
|
REMOVE_AP();
|
|
++(TVPNodeServer::PrioContext.ClientHost->NbActionsSentAtCycle);
|
|
}
|
|
|
|
/*
|
|
* TARGET_LIST
|
|
*/
|
|
static vector<TCLEntityId> TargetSlotsList(256);
|
|
|
|
NLMISC_COMMAND(displayTargetList,"Display the target list of an entity","<entityId>")
|
|
{
|
|
if ( args.size() > 1 )
|
|
return false;
|
|
|
|
CEntityId entity;
|
|
|
|
entity.fromString(args[0].c_str());
|
|
|
|
CMirrorPropValueList<uint32> targets(TheDataset,
|
|
entity,
|
|
"TargetList");
|
|
CMirrorPropValueList<uint32>::iterator it;
|
|
|
|
it = targets.begin();
|
|
|
|
if (it != targets.end())
|
|
{
|
|
const uint32 *effectCycle = &((*it)());
|
|
log.displayNL("TargetStamp: %d", *effectCycle);
|
|
++it;
|
|
}
|
|
|
|
for (; it!=targets.end(); ++it)
|
|
{
|
|
uint32 index = (*it)();
|
|
log.displayNL("Target: %d", TDataSetRow::createFromRawIndex(index).getIndex());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void fillTARGET_LIST( TOutBox& outbox, TPropIndex )
|
|
{
|
|
CClientHost *client = TVPNodeServer::PrioContext.ClientHost;
|
|
|
|
CMirrorPropValueList<uint32> targets(TheDataset,
|
|
TVPNodeServer::PrioContext.EntityIndex,
|
|
DSPropertyTARGET_LIST );
|
|
CMirrorPropValueList<uint32>::iterator it;
|
|
|
|
TargetSlotsList.clear();
|
|
|
|
it = targets.begin();
|
|
|
|
for (; it!=targets.end(); )
|
|
{
|
|
TDataSetRow index = TDataSetRow::createFromRawIndex((*it)());
|
|
|
|
++it;
|
|
// check list overflow
|
|
if (it == targets.end())
|
|
break;
|
|
|
|
// distance to target (in 1/127 of 100m)
|
|
uint32 dt = *(&((*it)()));
|
|
|
|
// translate slot
|
|
TCLEntityId slot = client->IdTranslator.getCEId(index);
|
|
if (slot != INVALID_SLOT)
|
|
{
|
|
TargetSlotsList.push_back(slot);
|
|
TargetSlotsList.push_back((uint8)dt);
|
|
}
|
|
|
|
++it;
|
|
}
|
|
|
|
// serialises branch has payload
|
|
bool payLoad = true;
|
|
outbox.serialBitAndLog(payLoad);
|
|
|
|
// restricts to 256 entities
|
|
uint longListSize = (uint)TargetSlotsList.size();
|
|
if (longListSize > 32)
|
|
longListSize = 32;
|
|
|
|
uint8 listSize = (uint8)longListSize;
|
|
|
|
// serialises short size
|
|
outbox.serialAndLog1(listSize);
|
|
|
|
// serialises slot list
|
|
if (listSize > 0)
|
|
outbox.serialBuffer(&(TargetSlotsList[0]), listSize);
|
|
|
|
//CActionSint64 *ap = (CActionSint64*)CActionFactory::getInstance()->createByPropIndex( TVPNodeServer::PrioContext.Slot, PROPERTY_TARGET_LIST );
|
|
DECLARE_AP(TARGET_LIST);
|
|
ap->setValue64( TheDataset.getChangeTimestamp( DSPropertyTARGET_LIST, TVPNodeServer::PrioContext.EntityIndex ) );
|
|
CFrontEndService::instance()->history()->store( TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.ClientHost->sendNumber(), ap );
|
|
//CActionFactory::getInstance()->remove( (CAction*&)ap );
|
|
REMOVE_AP();
|
|
|
|
LOG_WHAT_IS_SENT( "%u: Filling buffer for C%hu S%hu P%hu TARGET_LIST at bitpos %d", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, PROPERTY_TARGET_LIST, outbox.getPosInBit() );
|
|
}
|
|
|
|
|
|
/*
|
|
* BARS
|
|
*/
|
|
void fillBARS( TOutBox& outbox, TPropIndex )
|
|
{
|
|
bool payloadBit = true;
|
|
outbox.serialBitAndLog( payloadBit );
|
|
//CActionSint64 *ap = (CActionSint64*)CActionFactory::getInstance()->createByPropIndex( TVPNodeServer::PrioContext.Slot, PROPERTY_BARS );
|
|
DECLARE_AP(BARS);
|
|
caseFillAction( BARS )
|
|
LOG_WHAT_IS_SENT( "%u: Filling buffer for C%hu S%hu P%hu BARS at bitpos %d - value %"NL_I64"u", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, PROPERTY_BARS, outbox.getPosInBit(), ap->getValue() );
|
|
ap->packFast( outbox );
|
|
CFrontEndService::instance()->history()->store( TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.ClientHost->sendNumber(), ap );
|
|
//CActionFactory::getInstance()->remove( (CAction*&)ap );
|
|
REMOVE_AP();
|
|
++(TVPNodeServer::PrioContext.ClientHost->NbActionsSentAtCycle);
|
|
}
|
|
|
|
|
|
/*
|
|
* VPA, VPB, VPC
|
|
* Assumes:
|
|
* - They have the same mirror size
|
|
* - There mirror dataset property index is contiguous (VPC = VPB + 1 = VPA + 2)
|
|
*/
|
|
void fillVisualPropertyABC( TOutBox& outbox, TPropIndex propIndex )
|
|
{
|
|
bool payloadBit = true;
|
|
outbox.serialBitAndLog( payloadBit );
|
|
//CActionSint64 *ap = (CActionSint64*)CActionFactory::getInstance()->createByPropIndex( TVPNodeServer::PrioContext.Slot, propIndex );
|
|
DECLARE_AP_INDEX(propIndex);
|
|
CMirrorPropValueRO<TYPE_VPA> prop( TheDataset, TVPNodeServer::PrioContext.EntityIndex, propIndex-PROPERTY_VPA+DSPropertyVPA ); \
|
|
ap->setValue64( prop() );
|
|
LOG_WHAT_IS_SENT( "%u: Filling buffer for C%hu S%hu P%hu %s at bitpos %d - value %"NL_I64"u", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, propIndex, CLFECOMMON::getPropText( propIndex ), outbox.getPosInBit(), ap->getValue() );
|
|
ap->packFast( outbox );
|
|
CFrontEndService::instance()->history()->store( TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.ClientHost->sendNumber(), ap );
|
|
//CActionFactory::getInstance()->remove( (CAction*&)ap );
|
|
REMOVE_AP();
|
|
++(TVPNodeServer::PrioContext.ClientHost->NbActionsSentAtCycle);
|
|
}
|
|
|
|
|
|
/*
|
|
* CONTEXTUAL
|
|
*/
|
|
void fillCONTEXTUAL( TOutBox& outbox, TPropIndex propIndex )
|
|
{
|
|
bool payloadBit = true;
|
|
outbox.serialBitAndLog( payloadBit );
|
|
//CActionSint64 *ap = (CActionSint64*)CActionFactory::getInstance()->createByPropIndex( TVPNodeServer::PrioContext.Slot, PROPERTY_CONTEXTUAL );
|
|
DECLARE_AP(CONTEXTUAL);
|
|
caseFillAction( CONTEXTUAL )
|
|
LOG_WHAT_IS_SENT( "%u: Filling buffer for C%hu S%hu P%hu CONTEXTUAL at bitpos %d - value %"NL_I64"u", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, PROPERTY_CONTEXTUAL, outbox.getPosInBit(), ap->getValue() );
|
|
ap->packFast( outbox );
|
|
CFrontEndService::instance()->history()->store( TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.ClientHost->sendNumber(), ap );
|
|
//CActionFactory::getInstance()->remove( (CAction*&)ap );
|
|
REMOVE_AP();
|
|
++(TVPNodeServer::PrioContext.ClientHost->NbActionsSentAtCycle);
|
|
}
|
|
|
|
/*
|
|
* VISUAL_FX
|
|
*/
|
|
void fillVISUAL_FX( TOutBox& outbox, TPropIndex propIndex )
|
|
{
|
|
bool payloadBit = true;
|
|
outbox.serialBitAndLog( payloadBit );
|
|
//CActionSint64 *ap = (CActionSint64*)CActionFactory::getInstance()->createByPropIndex( TVPNodeServer::PrioContext.Slot, PROPERTY_VISUAL_FX );
|
|
DECLARE_AP(VISUAL_FX);
|
|
caseFillAction( VISUAL_FX )
|
|
LOG_WHAT_IS_SENT( "%u: Filling buffer for C%hu S%hu P%hu VISUAL_FX at bitpos %d - value %"NL_I64"u", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, PROPERTY_VISUAL_FX, outbox.getPosInBit(), ap->getValue() );
|
|
ap->packFast( outbox );
|
|
CFrontEndService::instance()->history()->store( TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.ClientHost->sendNumber(), ap );
|
|
//CActionFactory::getInstance()->remove( (CAction*&)ap );
|
|
REMOVE_AP();
|
|
++(TVPNodeServer::PrioContext.ClientHost->NbActionsSentAtCycle);
|
|
}
|
|
|
|
/*
|
|
* MODE
|
|
*/
|
|
void fillMODE( TOutBox& outbox, TPropIndex )
|
|
{
|
|
// Fill for mode special case
|
|
bool payloadBit = true;
|
|
outbox.serialBitAndLog( payloadBit );
|
|
//CActionSint64 *ap = (CActionSint64*)CActionFactory::getInstance()->createByPropIndex( TVPNodeServer::PrioContext.Slot, PROPERTY_MODE );
|
|
DECLARE_AP(MODE);
|
|
uint64 modeLong; // uses 8+4+16+16 = 44 bits
|
|
|
|
// Mode value (on 8 bits)
|
|
CMirrorPropValue<MBEHAV::TMode> prop( TheDataset, TVPNodeServer::PrioContext.EntityIndex, DSPropertyMODE );
|
|
|
|
// Store in history
|
|
ap->setValue64( prop().RawModeAndParam );
|
|
CFrontEndService::instance()->history()->store( TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.ClientHost->sendNumber(), ap );
|
|
|
|
// Game cycle when mode changed (4 bits -> 1.6 second max)
|
|
uint32 tickModeDelta = CTickEventHandler::getGameCycle() - prop.getTimestamp();
|
|
if ( tickModeDelta > 15 ) tickModeDelta = 15;
|
|
|
|
// Pack all with position 2D when mode changed, or combat angle (16 bits * 2)
|
|
/*
|
|
// OBSOLETE: MBEHAV::COMBAT_FLOAT no longer used
|
|
if ( prop().Mode == MBEHAV::COMBAT_FLOAT )
|
|
{
|
|
uint64 theta = (uint64)(*(uint32*)&(prop().Theta));
|
|
modeLong = ((uint64)(prop().Mode)) | ((uint64)(tickModeDelta << 8)) | (theta << 12);
|
|
LOG_WHAT_IS_SENT( "%u: Filling buffer for C%hu S%hu P%hu MODE at bitpos %d : %u [theta=%g dt=%u]", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, PROPERTY_MODE, outbox.getPosInBit(), prop().Mode, prop().Theta, tickModeDelta );
|
|
}
|
|
else
|
|
*/
|
|
{
|
|
uint64 posModeX16, posModeY16;
|
|
if ( TVPNodeServer::PrioContext.PositionAlreadySent )
|
|
{
|
|
// Take the pos from the mode change
|
|
posModeX16 = prop().Pos.X16;
|
|
posModeY16 = prop().Pos.Y16;
|
|
}
|
|
else
|
|
{
|
|
// Take the current pos
|
|
posModeX16 = TVPNodeServer::PrioContext.Sentity->X() >> 4; // TODO: make a method for this formula
|
|
posModeY16 = TVPNodeServer::PrioContext.Sentity->Y() >> 4;
|
|
tickModeDelta = 1;
|
|
}
|
|
modeLong = ((uint64)((uint8)(prop().Mode))) | ((uint64)(tickModeDelta << 8)) | (posModeX16 << 12) | (posModeY16 << 28);
|
|
LOG_WHAT_IS_SENT( "%u: Filling buffer for C%hu S%hu P%hu MODE at bitpos %d : %u [x16=%hu y16=%hu dt=%u] %s", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, PROPERTY_MODE, outbox.getPosInBit(), prop().Mode, prop().Pos.X16, prop().Pos.Y16, tickModeDelta, TVPNodeServer::PrioContext.PositionAlreadySent?"":" WithFirstPos" );
|
|
|
|
// The pos might be null (if mode set before 1st pos). The client has to handle the case.
|
|
/*#ifdef NL_DEBUG
|
|
if ( posModeX16==0 || posModeY16==0 )
|
|
nlwarning( "E%d: The pos16 in the mode is %hu %hu", TVPNodeServer::PrioContext.EntityIndex, (uint16)posModeX16, (uint16)posModeY16 );
|
|
#endif*/
|
|
}
|
|
|
|
// Fill
|
|
ap->setAndPackValue( modeLong, outbox );
|
|
//CActionFactory::getInstance()->remove( (CAction*&)act );
|
|
REMOVE_AP();
|
|
++(TVPNodeServer::PrioContext.ClientHost->NbActionsSentAtCycle);
|
|
}
|
|
|
|
/*
|
|
* GUILD_NAME_ID
|
|
*/
|
|
void fillGUILD_NAME_ID( TOutBox& outbox, TPropIndex )
|
|
{
|
|
bool payloadBit = true;
|
|
outbox.serialBitAndLog( payloadBit );
|
|
//CActionSint64 *ap = (CActionSint64*)CActionFactory::getInstance()->createByPropIndex( TVPNodeServer::PrioContext.Slot, PROPERTY_GUILD_NAME_ID );
|
|
DECLARE_AP(GUILD_NAME_ID);
|
|
caseFillAction( GUILD_NAME_ID )
|
|
LOG_WHAT_IS_SENT( "%u: Filling buffer for C%hu S%hu P%hu GUILD_NAME_ID at bitpos %d - value %"NL_I64"u", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, PROPERTY_GUILD_NAME_ID, outbox.getPosInBit(), ap->getValue() );
|
|
ap->packFast( outbox );
|
|
CFrontEndService::instance()->history()->store( TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.ClientHost->sendNumber(), ap );
|
|
//CActionFactory::getInstance()->remove( (CAction*&)ap );
|
|
REMOVE_AP();
|
|
++(TVPNodeServer::PrioContext.ClientHost->NbActionsSentAtCycle);
|
|
}
|
|
|
|
/*
|
|
* GUILD_SYMBOL
|
|
*/
|
|
void fillGUILD_SYMBOL( TOutBox& outbox, TPropIndex )
|
|
{
|
|
bool payloadBit = true;
|
|
outbox.serialBitAndLog( payloadBit );
|
|
//CActionSint64 *ap = (CActionSint64*)CActionFactory::getInstance()->createByPropIndex( TVPNodeServer::PrioContext.Slot, PROPERTY_GUILD_SYMBOL );
|
|
DECLARE_AP(GUILD_SYMBOL);
|
|
caseFillAction( GUILD_SYMBOL )
|
|
LOG_WHAT_IS_SENT( "%u: Filling buffer for C%hu S%hu P%hu GUILD_SYMBOL at bitpos %d - value %"NL_I64"u", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, PROPERTY_GUILD_SYMBOL, outbox.getPosInBit(), ap->getValue() );
|
|
ap->packFast( outbox );
|
|
CFrontEndService::instance()->history()->store( TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.ClientHost->sendNumber(), ap );
|
|
//CActionFactory::getInstance()->remove( (CAction*&)ap );
|
|
REMOVE_AP();
|
|
++(TVPNodeServer::PrioContext.ClientHost->NbActionsSentAtCycle);
|
|
}
|
|
|
|
|
|
/*
|
|
* EVENT_FACTION_ID
|
|
*/
|
|
void fillEVENT_FACTION_ID( TOutBox& outbox, TPropIndex )
|
|
{
|
|
bool payloadBit = true;
|
|
outbox.serialBitAndLog( payloadBit );
|
|
//CActionSint64 *ap = (CActionSint64*)CActionFactory::getInstance()->createByPropIndex( TVPNodeServer::PrioContext.Slot, PROPERTY_EVENT_FACTION_ID );
|
|
DECLARE_AP(EVENT_FACTION_ID);
|
|
caseFillAction( EVENT_FACTION_ID )
|
|
LOG_WHAT_IS_SENT( "%u: Filling buffer for C%hu S%hu P%hu EVENT_FACTION_ID at bitpos %d - value %"NL_I64"u", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, PROPERTY_EVENT_FACTION_ID, outbox.getPosInBit(), ap->getValue() );
|
|
ap->packFast( outbox );
|
|
CFrontEndService::instance()->history()->store( TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.ClientHost->sendNumber(), ap );
|
|
//CActionFactory::getInstance()->remove( (CAction*&)ap );
|
|
REMOVE_AP();
|
|
++(TVPNodeServer::PrioContext.ClientHost->NbActionsSentAtCycle);
|
|
}
|
|
|
|
|
|
/*
|
|
* PVP_MODE
|
|
*/
|
|
void fillPVP_MODE( TOutBox& outbox, TPropIndex )
|
|
{
|
|
bool payloadBit = true;
|
|
outbox.serialBitAndLog( payloadBit );
|
|
//CActionSint64 *ap = (CActionSint64*)CActionFactory::getInstance()->createByPropIndex( TVPNodeServer::PrioContext.Slot, PROPERTY_PVP_MODE );
|
|
DECLARE_AP(PVP_MODE);
|
|
caseFillAction( PVP_MODE )
|
|
LOG_WHAT_IS_SENT( "%u: Filling buffer for C%hu S%hu P%hu PVP_MODE at bitpos %d - value %"NL_I64"u", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, PROPERTY_PVP_MODE, outbox.getPosInBit(), ap->getValue() );
|
|
ap->packFast( outbox );
|
|
CFrontEndService::instance()->history()->store( TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.ClientHost->sendNumber(), ap );
|
|
//CActionFactory::getInstance()->remove( (CAction*&)ap );
|
|
REMOVE_AP();
|
|
++(TVPNodeServer::PrioContext.ClientHost->NbActionsSentAtCycle);
|
|
}
|
|
|
|
|
|
/*
|
|
* PVP_CLAN
|
|
*/
|
|
void fillPVP_CLAN( TOutBox& outbox, TPropIndex )
|
|
{
|
|
bool payloadBit = true;
|
|
outbox.serialBitAndLog( payloadBit );
|
|
//CActionSint64 *ap = (CActionSint64*)CActionFactory::getInstance()->createByPropIndex( TVPNodeServer::PrioContext.Slot, PROPERTY_PVP_CLAN );
|
|
DECLARE_AP(PVP_CLAN);
|
|
caseFillAction( PVP_CLAN )
|
|
LOG_WHAT_IS_SENT( "%u: Filling buffer for C%hu S%hu P%hu PVP_CLAN at bitpos %d - value %"NL_I64"u", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, PROPERTY_PVP_CLAN, outbox.getPosInBit(), ap->getValue() );
|
|
ap->packFast( outbox );
|
|
CFrontEndService::instance()->history()->store( TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.ClientHost->sendNumber(), ap );
|
|
//CActionFactory::getInstance()->remove( (CAction*&)ap );
|
|
REMOVE_AP();
|
|
++(TVPNodeServer::PrioContext.ClientHost->NbActionsSentAtCycle);
|
|
}
|
|
|
|
|
|
/*
|
|
* OWNER_PEOPLE
|
|
*/
|
|
void fillOWNER_PEOPLE( TOutBox& outbox, TPropIndex )
|
|
{
|
|
bool payloadBit = true;
|
|
outbox.serialBitAndLog( payloadBit );
|
|
DECLARE_AP(OWNER_PEOPLE);
|
|
caseFillAction( OWNER_PEOPLE )
|
|
LOG_WHAT_IS_SENT( "%u: Filling buffer for C%hu S%hu P%hu OWNER_PEOPLE at bitpos %d - value %"NL_I64"u", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, PROPERTY_OWNER_PEOPLE, outbox.getPosInBit(), ap->getValue() );
|
|
ap->packFast( outbox );
|
|
CFrontEndService::instance()->history()->store( TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.ClientHost->sendNumber(), ap );
|
|
REMOVE_AP();
|
|
++(TVPNodeServer::PrioContext.ClientHost->NbActionsSentAtCycle);
|
|
}
|
|
|
|
|
|
/*
|
|
* OUTPOST_INFOS
|
|
*/
|
|
void fillOUTPOST_INFOS( TOutBox& outbox, TPropIndex )
|
|
{
|
|
bool payloadBit = true;
|
|
outbox.serialBitAndLog( payloadBit );
|
|
DECLARE_AP(OUTPOST_INFOS);
|
|
caseFillAction( OUTPOST_INFOS )
|
|
LOG_WHAT_IS_SENT( "%u: Filling buffer for C%hu S%hu P%hu OUTPOST_INFOS at bitpos %d - value %"NL_I64"u", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, PROPERTY_OUTPOST_INFOS, outbox.getPosInBit(), ap->getValue() );
|
|
ap->packFast( outbox );
|
|
CFrontEndService::instance()->history()->store( TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.ClientHost->sendNumber(), ap );
|
|
REMOVE_AP();
|
|
++(TVPNodeServer::PrioContext.ClientHost->NbActionsSentAtCycle);
|
|
}
|
|
|
|
|
|
/*
|
|
* TARGET_ID, ENTITY_MOUNTED, RIDER_ENTITY
|
|
*/
|
|
void fillRowProperty( TOutBox& outbox, TPropIndex propIndex )
|
|
{
|
|
bool payloadBit = true;
|
|
TCLEntityId slot;
|
|
CMirrorPropValueRO<TDataSetRow> prop( TheDataset, TVPNodeServer::PrioContext.EntityIndex, CEntityContainer::propertyIndexInDataSetToVisualPropIndex( propIndex ) );
|
|
TEntityIndex targetindex(prop());
|
|
if ( !targetindex.isValid() )
|
|
{
|
|
// No target
|
|
slot = INVALID_SLOT;
|
|
}
|
|
else
|
|
{
|
|
TEntityIndex seenEntityIndex = TVPNodeServer::PrioContext.EntityIndex;
|
|
const char *propName = getPropText( propIndex );
|
|
LOG_WHAT_IS_SENT( "%u: About to send client %hu (%s) the %s '%u --> %u'", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientHost->clientId(), TVPNodeServer::PrioContext.ClientHost->eId().toString().c_str(), propName, seenEntityIndex.getIndex(), targetindex.getIndex() );
|
|
if ( targetindex == TVPNodeServer::PrioContext.ClientHost->entityIndex() )
|
|
{
|
|
// The entity targets the client
|
|
slot = 0;
|
|
}
|
|
else if ( targetindex == seenEntityIndex )
|
|
{
|
|
// The entity targets itself
|
|
slot = INVALID_SLOT;
|
|
}
|
|
else
|
|
{
|
|
// Translate CEntityId to slot: get entityid, then slot
|
|
TCLEntityId result = TVPNodeServer::PrioContext.ClientHost->IdTranslator.getCEId( targetindex );
|
|
if ( result != INVALID_SLOT )
|
|
{
|
|
slot = result;
|
|
}
|
|
else
|
|
{
|
|
LOG_WHAT_IS_SENT( "%s slot not found: E%u", propName, targetindex.getIndex() );
|
|
payloadBit = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Fill for property target/mount special case
|
|
outbox.serialBitAndLog( payloadBit );
|
|
if ( payloadBit )
|
|
{
|
|
LOG_WHAT_IS_SENT( "%u: Filling buffer for C%hu S%hu P%hu %s at bitpos %d - slot %hu", CTickEventHandler::getGameCycle(), TVPNodeServer::PrioContext.ClientId, (uint16)TVPNodeServer::PrioContext.Slot, propIndex, CLFECOMMON::getPropText( propIndex ), outbox.getPosInBit(), (uint16)slot );
|
|
//CActionSint64 *ap = (CActionSint64*)CActionFactory::getInstance()->createByPropIndex( TVPNodeServer::PrioContext.Slot, propIndex );
|
|
DECLARE_AP_INDEX(propIndex);
|
|
ap->setValue64( prop().getIndex() );
|
|
|
|
// Store the entity index into the history
|
|
CFrontEndService::instance()->history()->store( TVPNodeServer::PrioContext.ClientId, TVPNodeServer::PrioContext.ClientHost->sendNumber(), ap );
|
|
|
|
// The value that will be sent is the slot, not the entity index
|
|
ap->setAndPackValue( slot, outbox );
|
|
|
|
//CActionFactory::getInstance()->remove( (CAction*&)ap );
|
|
REMOVE_AP();
|
|
++(TVPNodeServer::PrioContext.ClientHost->NbActionsSentAtCycle);
|
|
}
|
|
}
|
|
|
|
|
|
TVPNodeServer::TPrioContext TVPNodeServer::PrioContext;
|
|
|
|
// Flattened Node Tree
|
|
TVPNodeServer* TVPNodeServer::FlatVPTree[CLFECOMMON::NB_VISUAL_PROPERTIES];
|
|
|
|
// Reordered Node tree (used by fastPropagateBackBranchHasPayload())
|
|
std::vector<TVPNodeServer::CSortedFlatVPTreeItem> TVPNodeServer::SortedFlatVPTree;
|
|
const bool TVPNodeServer::FalseBoolPayLoad = false;
|
|
|
|
// Reordered Node tree (used by fastFillDiscreetProperties)
|
|
std::vector<TVPNodeServer::CSortedFlatVPTreeFillItem> TVPNodeServer::SortedFlatVPTreeFill;
|
|
|
|
void TVPNodeServer::initSortedFlatVPTree()
|
|
{
|
|
if (isLeaf())
|
|
return;
|
|
|
|
CSortedFlatVPTreeItem item;
|
|
|
|
item.Node = this;
|
|
|
|
if (a())
|
|
{
|
|
item.APayLoad = &(a()->BranchHasPayload);
|
|
a()->initSortedFlatVPTree();
|
|
}
|
|
if (b())
|
|
{
|
|
item.BPayLoad = &(b()->BranchHasPayload);
|
|
b()->initSortedFlatVPTree();
|
|
}
|
|
|
|
SortedFlatVPTree.push_back(item);
|
|
}
|
|
|
|
void TVPNodeServer::initSortedFlatVPTreeFill()
|
|
{
|
|
uint thisItem = (uint)SortedFlatVPTreeFill.size();
|
|
SortedFlatVPTreeFill.push_back(CSortedFlatVPTreeFillItem());
|
|
|
|
SortedFlatVPTreeFill[thisItem].Node = this;
|
|
|
|
if (a()) a()->initSortedFlatVPTreeFill();
|
|
if (b()) b()->initSortedFlatVPTreeFill();
|
|
|
|
SortedFlatVPTreeFill[thisItem].NextIfNoPayload = (uint)SortedFlatVPTreeFill.size();
|
|
}
|
|
|
|
|
|
namespace CLFECOMMON
|
|
{
|
|
// Factory for TVPNodeBase::buildTree()
|
|
TVPNodeBase *NewNode()
|
|
{
|
|
return (TVPNodeBase*) new TVPNodeServer();
|
|
}
|
|
};
|
|
|
|
|
|
#ifdef NL_DEBUG
|
|
|
|
NLMISC_DYNVARIABLE( uint32, MoveNumber, "MoveNumber of entities seen by monitored client" )
|
|
{
|
|
if ( get )
|
|
{
|
|
CFrontEndService *fe = CFrontEndService::instance();
|
|
//nlassert( fe->MonitoredClient <= MAX_NB_CLIENTS );
|
|
CClientHost *client = fe->receiveSub()->clientIdCont()[fe->MonitoredClient];
|
|
if ( client )
|
|
{
|
|
*pointer = client->MoveNumber;
|
|
}
|
|
else
|
|
{
|
|
*pointer = 9999;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
NLMISC_COMMAND(verbosePropertiesSent,"Turn on or off or check the state of verbose logging of what is sent","<clientId> | all | off")
|
|
{
|
|
if ( args.size() > 1 )
|
|
return false;
|
|
|
|
if ( args.size() == 1 )
|
|
{
|
|
if ( args[0] == string("all") )
|
|
verbosePropertiesSent = 0;
|
|
else if ( args[0] == string("off") )
|
|
verbosePropertiesSent = INVALID_CLIENT;
|
|
else
|
|
NLMISC::fromString(args[0], verbosePropertiesSent);
|
|
}
|
|
|
|
log.displayNL( "verbosePropertiesSent is %s", (verbosePropertiesSent==INVALID_CLIENT)?"off":((verbosePropertiesSent==0)?"all":toString("C%hu", verbosePropertiesSent).c_str()) );
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|