Files
2026-06-01 12:46:52 +02:00

1121 lines
30 KiB
C++

#include "../../include/toolkit/ExclusiveLogicLock.h"
#include "../../include/toolkit/ILock.h"
#include "../../include/toolkit/XEnv.h"
#include "../../include/mmo/ArcadiaServer.h"
#include "../../include/mmo/ArRegion.h"
#include "../../include/mmo/ArBoxCollision.h"
#include "../../include/mmo/ArScheduler.h"
ArcadiaServer * s_pArcadiaServer;
__declspec( thread ) bool s_LockFlag = false;
struct ArcadiaData
{
~ArcadiaData()
{
delete pRgnMgr;
}
ArRegionContainer *pRgnMgr;
ExclusiveLogicLock< ArBoxCollision, XCriticalSection > rgnLock;
ArScheduler scheduler;
};
ArcadiaServer::ArcadiaServer()
{
m_pData = NULL;
m_pIntf = NULL;
s_pArcadiaServer = this;
}
ArcadiaServer::~ArcadiaServer()
{
DeInit();
}
ArcadiaServer & ArcadiaServer::Instance()
{
static ArcadiaServer inst;
return inst;
}
ArcadiaIntf* ArcadiaServer::m_pIntf;
ArcadiaData* ArcadiaServer::m_pData;
static int _getArTime()
{
return GetArTime();
}
bool ArcadiaServer::Init( ArcadiaIntf *pIntf, AR_UNIT map_width, AR_UNIT map_height, void (*scheduler_thread_init_func)( int ), bool bMonitoringThread )
{
m_nUserCount = 0;
m_nNPCCount = 0;
m_nItemCount = 0;
m_nMapWidth = map_width;
m_nMapHeight = map_height;
m_pIntf = pIntf;
g_nRegionSize = ENV().GetInt( "engine.region_size", 180 );
ENV().Set( "engine.region_size", g_nRegionSize );
m_pData = new ArcadiaData;
if( !m_pData->scheduler.Init( ENV().GetInt( "engine.scheduler_count", 4 ), scheduler_thread_init_func, bMonitoringThread ) )
{
delete m_pData;
return false;
}
m_pData->pRgnMgr = new ArRegionContainer( m_nMapWidth, m_nMapHeight );
ENV().Bind( "engine.lock", (int*)&m_pData->rgnLock.GetLockedThreadCount() );
ENV().Bind( "engine.wait", (int*)&m_pData->rgnLock.GetWaitingThreadCount() );
ENV().Bind( "engine.count_client", (int*)&GetUserCount() );
ENV().Bind( "engine.count_movable", (int*)&GetNPCCount() );
ENV().Bind( "engine.count_static", (int*)&GetItemCount() );
ENV().Bind( "engine.ar_time", _getArTime );
ENV().Bind( "engine.use_region_debug", (int*)&g_bUseRegionDebug );
return true;
}
bool ArcadiaServer::DeInit()
{
if( m_pData )
{
m_pData->scheduler.DeInit();
delete m_pData;
}
m_pData = NULL;
return true;
}
LONG ArcadiaServer::GetLockedThreadCount()
{
return m_pData->rgnLock.GetLockedThreadCount();
};
LONG ArcadiaServer::GetWaitingThreadCount()
{
return m_pData->rgnLock.GetWaitingThreadCount();
}
void sendEnterMessage( const ArObject * _pClient, ArObject * pObject, bool bAbsolute = false )
{
// 만약 pClient 의 이전 관심영역에 있는거라면 무시한다.
const ArObject * pClient = static_cast< const ArObject * >( _pClient );
if( pObject->GetObjectType() == ArObject::MOVABLE_OBJECT )
{
// pClient 에게 보여지고 있는 것이므로 pObject 는 활성화된다.
pObject->bIsNearClient = true;
// _oprint( "ADD BY SERVER : %I64d\n", pObject->GetHandle() );
if( pObject->GetFinalPriority() < ArObject::UPDATE_PRIORITY_NORMAL )
ArcadiaServer::Instance().SetObjectPriority( pObject, ArObject::UPDATE_PRIORITY_NORMAL );
}
ArcadiaServer::GetIntf()->SendEnterMessage( pClient, pObject );
}
// 강제 이동 메시지
struct _SendForceMoveMessage : ArObjectFunctor
{
_SendForceMoveMessage( const ArObject * _pWho )
{
pWho = _pWho;
}
virtual void operator()( ArObject * pClient ) const
{
//if( pClient == pWho ) return;
ArcadiaServer::GetIntf()->SendForceMoveMessage( pClient, static_cast< const ArObject* >( pWho ) );
}
const ArObject * pWho;
};
// 해당 리전의 녀석들에게 보낼때
struct _SendMoveMessage : ArObjectFunctor
{
_SendMoveMessage( const ArObject * _pWho )
{
pWho = _pWho;
}
virtual void operator()( ArObject * pClient ) const
{
//if( pClient == pWho ) return;
ArcadiaServer::GetIntf()->SendMoveMessage( pClient, static_cast< const ArObject* >( pWho ) );
}
const ArObject * pWho;
};
void ArcadiaServer::MoveObject( ArObject* pObject, const ArPosition & newPos, const float face )
{
AR_TIME tm = GetArTime();
ArPosition oldPos = pObject->GetPos();
unsigned rx = GetRegionX( newPos.x );
unsigned ry = GetRegionY( newPos.y );
unsigned prx = pObject->GetRX();
unsigned pry = pObject->GetRY();
pObject->SetCurrentXY( newPos.x, newPos.y );
pObject->SetFace( face );
pObject->StopMove();
ArPosition _newPos = newPos;
onMoveObject( pObject, oldPos, _newPos );
pObject->lastStepTime = tm;
pObject->bIsRegionChanging = true;
if( prx != rx || pry != ry )
{
enterProc( pObject, prx, pry );
pObject->prev_rx = prx;
pObject->prev_ry = pry;
}
pObject->bIsRegionChanging = false;
struct _ArRegionFunctor : ArRegionFunctor
{
_ArRegionFunctor( const ArObject * _pObject )
{
nCnt = 0;
pObject = _pObject;
}
void operator()( const ArRegion * pRegion )
{
_SendForceMoveMessage func( pObject );
nCnt += pRegion->DoEachClient( func );
}
unsigned nCnt;
const ArObject *pObject;
} _rf( pObject );
// 근처에 이동 메세지 전송
m_pData->pRgnMgr->DoEachVisibleRegion( pObject->GetRX(), pObject->GetRY(), pObject->GetLayer(), _rf );
}
bool ArcadiaServer::SetMultipleMove( ArObject* pObject, const ArPosition & curPos, const std::vector< ArPosition > & newPos, unsigned char speed, bool bAbsoluteMove, AR_TIME t, bool bBroadcastMove )
{
assert( newPos.size() );
if( !bAbsoluteMove && !m_pIntf->onSetMove( pObject, curPos, newPos.back() ) ) return false;
{
const ArPosition oldPos = pObject->GetPos();
pObject->SetCurrentXY( curPos.x, curPos.y );
const ArPosition newPos = pObject->GetPos();
onMoveObject( pObject, oldPos, newPos );
enterProc( pObject, oldPos.GetRX(), oldPos.GetRY() );
}
pObject->SetMultipleMove( newPos, speed, t );
if( !bBroadcastMove )
return true;
struct _ArRegionFunctor : ArRegionFunctor
{
_ArRegionFunctor( const ArObject * _pObject )
{
nCnt = 0;
pObject = _pObject;
}
void operator()( const ArRegion * pRegion )
{
_SendMoveMessage func( pObject );
nCnt += pRegion->DoEachClient( func );
}
unsigned nCnt;
const ArObject *pObject;
} _rf( pObject );
// 근처에 이동 메세지 전송
m_pData->pRgnMgr->DoEachVisibleRegion( pObject->GetRX(), pObject->GetRY(), pObject->GetLayer(), _rf );
// 플레이어에게 요청한 이동 메세지가 처리되었음을 알려주자
if( pObject->GetObjectType() == ArObject::CLIENT_OBJECT )
{
//m_pIntf->SendMoveAckMessage( pObject, tm, speed );
}
else if( pObject->GetObjectType() == ArObject::MOVABLE_OBJECT )
{
// { 만약 NPC 가 이동한 것인데 아무에게도 메세지가 전송되지 않았다면
// 명백히 근처에 클라이언트가 없는 것이다.
if( _rf.nCnt )
{
pObject->bIsNearClient = true;
if( pObject->GetPriority() == ArObject::UPDATE_PRIORITY_NULL ||
pObject->GetPriority() == ArObject::UPDATE_PRIORITY_IDLE )
{
SetObjectPriority( pObject, ArObject::UPDATE_PRIORITY_NORMAL );
}
}
else
{
pObject->bIsNearClient = false;
}
// }
}
return true;
}
bool ArcadiaServer::SetMove( ArObject* pObject, const ArPosition & curPos, const ArPosition & newPos, unsigned char speed, bool bAbsoluteMove, AR_TIME t, bool bBroadcastMove )
{
if( !bAbsoluteMove && !m_pIntf->onSetMove( pObject, curPos, newPos ) ) return false;
if( pObject->IsMoving() )
{
{
const ArPosition oldPos = pObject->GetPos();
pObject->SetCurrentXY( curPos.x, curPos.y );
const ArPosition newPos = pObject->GetPos();
onMoveObject( pObject, oldPos, newPos );
enterProc( pObject, oldPos.GetRX(), oldPos.GetRY() );
}
pObject->SetMove( newPos, speed, t );
} else pObject->SetMove( newPos, speed, t );
if( !bBroadcastMove )
return true;
struct _ArRegionFunctor : ArRegionFunctor
{
_ArRegionFunctor( const ArObject * _pObject )
{
nCnt = 0;
pObject = _pObject;
}
void operator()( const ArRegion * pRegion )
{
_SendMoveMessage func( pObject );
nCnt += pRegion->DoEachClient( func );
}
unsigned nCnt;
const ArObject *pObject;
} _rf( pObject );
// 근처에 이동 메세지 전송
m_pData->pRgnMgr->DoEachVisibleRegion( pObject->GetRX(), pObject->GetRY(), pObject->GetLayer(), _rf );
// 플레이어에게 요청한 이동 메세지가 처리되었음을 알려주자
if( pObject->GetObjectType() == ArObject::CLIENT_OBJECT )
{
//m_pIntf->SendMoveAckMessage( pObject, tm, speed );
}
else if( pObject->GetObjectType() == ArObject::MOVABLE_OBJECT )
{
// { 만약 NPC 가 이동한 것인데 아무에게도 메세지가 전송되지 않았다면
// 명백히 근처에 클라이언트가 없는 것이다.
if( _rf.nCnt )
{
pObject->bIsNearClient = true;
if( pObject->GetPriority() == ArObject::UPDATE_PRIORITY_NULL ||
pObject->GetPriority() == ArObject::UPDATE_PRIORITY_IDLE )
{
SetObjectPriority( pObject, ArObject::UPDATE_PRIORITY_NORMAL );
}
}
else
{
pObject->bIsNearClient = false;
}
// }
}
return true;
}
void ArcadiaServer::setObjectPriority( ArSchedulerObject * pObj, ArSchedulerObject::AR_OBJECT_PRIORITY priority )
{
if( !m_pData )
return;
m_pData->scheduler.SetObjectPriority( pObj, priority );
}
void ArcadiaServer::onRegionChange( ArObject* pObject, AR_TIME update_time, bool bIsStopMessage )
{
unsigned prx = pObject->GetRX();
unsigned pry = pObject->GetRY();
pObject->bIsRegionChanging = true;
AR_TIME tc = ( bIsStopMessage ? 10 : 0 );
step( pObject, pObject->lastStepTime + update_time + tc );
// prev_rx, prev_ry 변경
if( pObject->GetRX() != prx || pObject->GetRY() != pry )
{
enterProc( pObject, prx, pry );
pObject->prev_rx = prx;
pObject->prev_ry = pry;
}
pObject->bIsRegionChanging = false;
}
int ArcadiaServer::arcadia_lock( struct ArBoxCollision * pLockInfo, const char * filename, int linenumber )
{
/*
if( s_LockFlag )
{
assert( 0 );
_cprint( "DOUBLE LOCK!\n" );
FILE *fp = fopen( "DOUBLE_LOCK.txt", "a+" );
if( fp ) fclose( fp );
}
*/
s_LockFlag = true;
return m_pData->rgnLock.LockLogic( *pLockInfo, filename, linenumber );
}
ArcadiaLock ArcadiaServer::_LockWorld( const char * filename, int linenumber )
{
ArBoxCollision box( true );
return ArcadiaLock( arcadia_lock( &box, filename, linenumber ) );
}
ArcadiaLock ArcadiaServer::_LockWithVisibleRange( const char * filename, int linenumber, unsigned rx, unsigned ry, short layer )
{
ArBoxCollision box( rx, ry, layer );
return ArcadiaLock( arcadia_lock( &box, filename, linenumber ) );
}
ArcadiaLock ArcadiaServer::_LockArea( const char * filename, int linenumber, unsigned rx1, unsigned ry1, unsigned rx2, unsigned ry2, short layer )
{
ArBoxCollision box( rx1, ry1, rx2, ry2, layer );
return ArcadiaLock( arcadia_lock( &box, filename, linenumber ) );
}
ArcadiaLock ArcadiaServer::_LockObjectWithSpecificRange( const char * filename, int linenumber, const ArObject *pObject, unsigned range )
{
unsigned rx = pObject->GetRX();
unsigned ry = pObject->GetRY();
short layer = pObject->GetLayer();
ArBoxCollision box( rx, ry, range, layer );
ArcadiaLock _lock = ArcadiaLock( arcadia_lock( &box, filename, linenumber ) );
while( rx != pObject->GetRX() || ry != pObject->GetRY() )
{
UnLock( &_lock );
rx = pObject->GetRX();
ry = pObject->GetRY();
layer = pObject->GetLayer();
ArBoxCollision new_box( rx, ry, range, layer );
_lock = ArcadiaLock( arcadia_lock( &new_box, filename, linenumber ) );
}
return _lock;
}
ArcadiaLock ArcadiaServer::_LockObjectWithVisibleRange( const char * filename, int linenumber, const ArObject *pObject )
{
unsigned rx = pObject->GetRX();
unsigned ry = pObject->GetRY();
short layer = pObject->GetLayer();
ArBoxCollision box( rx, ry, layer );
ArcadiaLock _lock = ArcadiaLock( arcadia_lock( &box, filename, linenumber ) );
while( rx != pObject->GetRX() || ry != pObject->GetRY() || layer != pObject->GetLayer() )
{
UnLock( &_lock );
rx = pObject->GetRX();
ry = pObject->GetRY();
layer = pObject->GetLayer();
ArBoxCollision new_box( rx, ry, layer );
_lock = ArcadiaLock( arcadia_lock( &new_box, filename, linenumber ) );
}
return _lock;
}
ArcadiaLock ArcadiaServer::_LockObjectWithSpecificRegion( const char * filename, int linenumber, const ArObject * pObject, unsigned rx, unsigned ry, short layer )
{
// 다른 Lock 계열 함수는 layer 인자가 생략돼서 기본값(-1)이 들어오면 전체 레이어에 락을 걸지만 이 함수는 기본값이 -2로 다르며,
// 기본값으로 들어오면(인자가 입력되지 않았으면) pObject의 layer에만 락을 거는 것으로, -1로 인자가 명시적으로 들어오면 전체 레이어에 락을 거는 것으로 처리됨.
unsigned rx2 = pObject->GetRX();
unsigned ry2 = pObject->GetRY();
short prev_layer = pObject->GetLayer();
// 두 개의 서로 다른 레이어에 대해 락을 거는 것은 불가능하므로 pObject와 지정 Region이 서로 다른 레이어에 있다면 전체 레이어에 대해 락을 걸어야 함(레이어 번호 -1)
short lock_layer = ( layer == -2 ) ? prev_layer : ( ( prev_layer == layer ) ? layer : -1 );
ArBoxCollision box( rx, ry, rx2, ry2, lock_layer );
ArcadiaLock _lock = ArcadiaLock( arcadia_lock( &box, filename, linenumber ) );
while( rx2 != pObject->GetRX() || ry2 != pObject->GetRY() || prev_layer != pObject->GetLayer() )
{
UnLock( &_lock );
rx2 = pObject->GetRX();
ry2 = pObject->GetRY();
prev_layer = pObject->GetLayer();
lock_layer = ( layer == -2 ) ? prev_layer : ( ( prev_layer == layer ) ? layer : -1 );
ArBoxCollision new_box( rx, ry, rx2, ry2, lock_layer );
_lock = ArcadiaLock( arcadia_lock( &new_box, filename, linenumber ) );
}
return _lock;
}
ArcadiaLock ArcadiaServer::_LockObject( const char * filename, int linenumber, const ArObject *pObject )
{
unsigned rx = pObject->GetRX();
unsigned ry = pObject->GetRY();
short layer = pObject->GetLayer();
ArBoxCollision box( rx, ry, static_cast< unsigned >( -1 ), layer );
ArcadiaLock _lock = ArcadiaLock( arcadia_lock( &box, filename, linenumber ) );
while( rx != pObject->GetRX() || ry != pObject->GetRY() || layer != pObject->GetLayer() )
{
UnLock( &_lock );
rx = pObject->GetRX();
ry = pObject->GetRY();
layer = pObject->GetLayer();
ArBoxCollision new_box( rx, ry, static_cast< unsigned >( -1 ), layer );
_lock = ArcadiaLock( arcadia_lock( &new_box, filename, linenumber ) );
}
return _lock;
}
ArcadiaLock ArcadiaServer::_LockObjects( const char * filename, int linenumber, const ArObject * pObject1, const ArObject * pObject2 )
{
unsigned rx1 = pObject1->GetRX();
unsigned ry1 = pObject1->GetRY();
short layer1 = pObject1->GetLayer();
unsigned rx2 = pObject2->GetRX();
unsigned ry2 = pObject2->GetRY();
short layer2 = pObject2->GetLayer();
// 두 개의 서로 다른 레이어에 락을 같이 거는 것은 불가능하므로 두 Object가 서로 다른 레이어에 있다면 전체 레이어에 대해 락을 걸어야 함(레이어 번호 -1)
short lock_layer = ( layer1 == layer2 ) ? layer1 : -1;
ArBoxCollision box( rx1, ry1, rx2, ry2, lock_layer );
ArcadiaLock _lock = ArcadiaLock( arcadia_lock( &box, filename, linenumber ) );
while( true )
{
if( rx1 != pObject1->GetRX() || ry1 != pObject1->GetRY() || layer1 != pObject1->GetLayer() )
{
UnLock( &_lock );
rx1 = pObject1->GetRX();
ry1 = pObject1->GetRY();
layer1 = pObject1->GetLayer();
lock_layer = ( layer1 == layer2 ) ? layer1 : -1;
}
else if( rx2 != pObject2->GetRX() || ry2 != pObject2->GetRY() || layer2 != pObject2->GetLayer() )
{
UnLock( &_lock );
rx2 = pObject2->GetRX();
ry2 = pObject2->GetRY();
layer2 = pObject2->GetLayer();
lock_layer = ( layer1 == layer2 ) ? layer1 : -1;
}
else
{
// 두 Object가 모두 락 안에 잡혔음
break;
}
ArBoxCollision new_box( rx1, ry1, rx2, ry2, lock_layer );
_lock = ArcadiaLock( arcadia_lock( &new_box, filename, linenumber ) );
}
return _lock;
}
void ArcadiaServer::UnLock( ArcadiaLock * pLockHandle )
{
if( pLockHandle->handle < 0 ) return;
m_pData->rgnLock.UnLock( pLockHandle->handle );
s_LockFlag = false;
}
const bool ArcadiaServer::IsLocked( const unsigned int rx, const unsigned int ry, const short layer, const bool bPartial )
{
return m_pData->rgnLock.IsLocked( ArBoxCollision( rx, ry, layer ), bPartial );
}
const bool ArcadiaServer::IsLocked( unsigned rx1, unsigned ry1, unsigned rx2, unsigned ry2, const short layer, const bool bPartial )
{
return m_pData->rgnLock.IsLocked( ArBoxCollision( rx1, ry1, rx2, ry2, layer ), bPartial );
}
const bool ArcadiaServer::IsLocked( const ArObject *pObject, const bool bPartial )
{
return IsLocked( pObject->GetRX(), pObject->GetRY(), pObject->GetLayer(), bPartial );
}
unsigned ArcadiaServer::BroadcastToSpecificRegion( unsigned rx, unsigned ry, unsigned range, unsigned char layer, const void * msg )
{
struct _MessageSender : ArObjectFunctor
{
_MessageSender( const void *_msg, ArcadiaIntf *_pIntf ) { msg = _msg; pIntf = _pIntf; }
void operator()( ArObject *pObj ) const
{
pIntf->SendGameMessage( pObj, msg );
}
ArcadiaIntf * pIntf;
const void *msg;
} _sender( msg, m_pIntf );
struct Sender : ArRegionFunctor
{
Sender( ArObjectFunctor *_pFo ) { pFo = _pFo; }
void operator()( const struct ArRegion * pRegion )
{
pRegion->DoEachClient( *pFo );
}
ArObjectFunctor * pFo;
} _fo( &_sender );
m_pData->pRgnMgr->DoEachSpecificRegion( rx, ry, range, layer, _fo );
return 0;
}
unsigned ArcadiaServer::Broadcast( unsigned rx, unsigned ry, unsigned char layer, const void * msg )
{
struct _MessageSender : ArObjectFunctor
{
_MessageSender( const void *_msg, ArcadiaIntf *_pIntf ) { msg = _msg; pIntf = _pIntf; }
void operator()( ArObject *pObj ) const
{
pIntf->SendGameMessage( pObj, msg );
}
ArcadiaIntf * pIntf;
const void *msg;
} _sender( msg, m_pIntf );
struct Sender : ArRegionFunctor
{
Sender( ArObjectFunctor *_pFo ) { pFo = _pFo; }
void operator()( const struct ArRegion * pRegion )
{
pRegion->DoEachClient( *pFo );
}
ArObjectFunctor * pFo;
} _fo( &_sender );
m_pData->pRgnMgr->DoEachVisibleRegion( rx, ry, layer, _fo );
return 0;
}
unsigned ArcadiaServer::Broadcast( unsigned rx1, unsigned ry1, unsigned rx2, unsigned ry2, unsigned char layer, const void * msg )
{
struct _MessageSender : ArObjectFunctor
{
_MessageSender( const void *_msg, ArcadiaIntf *_pIntf ) { msg = _msg; pIntf = _pIntf; }
void operator()( ArObject *pObj ) const
{
pIntf->SendGameMessage( pObj, msg );
}
ArcadiaIntf * pIntf;
const void *msg;
} _sender( msg, m_pIntf );
struct Sender : ArRegionFunctor
{
Sender( ArObjectFunctor *_pFo ) { pFo = _pFo; }
void operator()( const struct ArRegion * pRegion )
{
pRegion->DoEachClient( *pFo );
}
ArObjectFunctor * pFo;
} _fo( &_sender );
m_pData->pRgnMgr->DoEachVisibleRegion( rx1, ry1, rx2, ry2, layer, _fo );
return 0;
}
void ArcadiaServer::AddObject( ArObject* pObject )
{
assert( !pObject->IsInWorld() );
ArRegion *pRegion = m_pData->pRgnMgr->GetRegion( pObject );
struct _ArRegionFunctor : ArRegionFunctor
{
// 서로에게 ENTER 메세지를 보낼때
struct _SendEnterMessageEachOther : ArObjectFunctor
{
_SendEnterMessageEachOther( ArObject * _pWho ) { pWho = _pWho; }
void operator()( ArObject * pClient ) const
{
if( pClient == pWho ) return;
if( pClient->GetObjectType() == ArObject::CLIENT_OBJECT ) sendEnterMessage( pClient, pWho, true );
if( pWho->GetObjectType() == ArObject::CLIENT_OBJECT ) sendEnterMessage( pWho, pClient, true );
}
ArObject * pWho;
};
// 일방적으로 ENTER 메세지를 보낼때
struct _SendEnterMessage : ArObjectFunctor
{
_SendEnterMessage( const ArObject * _pClient ) { pClient = _pClient; }
void operator()( ArObject * pObject ) const
{
sendEnterMessage( pClient, pObject, true );
}
const ArObject * pClient;
};
_ArRegionFunctor( ArObject * _pObject ) { pObject = _pObject; }
void operator()( const ArRegion * pRegion )
{
_SendEnterMessageEachOther enter_msg_func( pObject );
pRegion->DoEachClient( enter_msg_func );
if( pObject->GetObjectType() == ArObject::CLIENT_OBJECT )
{
_SendEnterMessage enter( pObject );
pRegion->DoEachStaticObject( enter );
pRegion->DoEachMovableObject( enter );
}
}
ArObject *pObject;
} _rf( pObject );
// 근처에 메세지 전송
m_pData->pRgnMgr->DoEachVisibleRegion( pObject->GetRX(), pObject->GetRY(), pObject->GetLayer(), _rf );
switch( pObject->GetObjectType() )
{
case ArObject::STATIC_OBJECT: InterlockedIncrement( &m_nItemCount ); return pRegion->AddStaticObject( pObject );
case ArObject::MOVABLE_OBJECT: InterlockedIncrement( &m_nNPCCount ); return pRegion->AddMovableObject( pObject );
case ArObject::CLIENT_OBJECT: InterlockedIncrement( &m_nUserCount ); return pRegion->AddClientObject( pObject );
}
}
void ArcadiaServer::RemoveObject( ArObject* pObject )
{
ArRegion *pRegion = m_pData->pRgnMgr->GetRegion( pObject );
if( g_bUseRegionDebug && !IsLocked( pObject->GetRX(), pObject->GetRY(), pObject->GetRX(), pObject->GetRY(), pObject->GetLayer() ) )
{
assert( 0 );
XSEH::InvokeUnhandledException( STATUS_ILLEGAL_INSTRUCTION );
}
switch( pObject->GetObjectType() )
{
case ArObject::STATIC_OBJECT: InterlockedDecrement( &m_nItemCount ); pRegion->RemoveStaticObject( pObject ); break;
case ArObject::MOVABLE_OBJECT: InterlockedDecrement( &m_nNPCCount ); pRegion->RemoveMovableObject( pObject ); break;
case ArObject::CLIENT_OBJECT: InterlockedDecrement( &m_nUserCount ); pRegion->RemoveClientObject( pObject ); break;
}
struct _ArRegionFunctor : ArRegionFunctor
{
// Leave 메세지를 보냄
struct _SendLeaveMessage : ArObjectFunctor
{
_SendLeaveMessage( const ArObject * _pWho )
{
pWho = _pWho;
}
virtual void operator()( ArObject * pClient ) const
{
if( pClient->GetObjectType() == ArObject::CLIENT_OBJECT ) ArcadiaServer::GetIntf()->SendLeaveMessage( pClient, pWho );
}
const ArObject * pWho;
};
_ArRegionFunctor( const ArObject * _pObject ) { pObject = _pObject; }
void operator()( const ArRegion * pRegion )
{
_SendLeaveMessage func( pObject );
pRegion->DoEachClient( func );
}
const ArObject *pObject;
} _rf( pObject );
// 근처에 메세지 전송
m_pData->pRgnMgr->DoEachVisibleRegion( pObject->GetRX(), pObject->GetRY(), pObject->GetLayer(), _rf );
}
void ArcadiaServer::step( ArObject * pObject, AR_TIME tm )
{
const ArPosition oldPos = pObject->GetPos();
pObject->Step( tm );
const ArPosition newPos = pObject->GetPos();
onMoveObject( pObject, oldPos, newPos );
pObject->lastStepTime = tm;
}
void ArcadiaServer::onMoveObject( ArObject * pObject, const ArPosition & oldPos, const ArPosition & newPos )
{
unsigned prev_rx = oldPos.GetRX();
unsigned prev_ry = oldPos.GetRY();
unsigned rx = newPos.GetRX();
unsigned ry = newPos.GetRY();
short layer = pObject->GetLayer();
if( prev_rx != rx || prev_ry != ry )
{
if( g_bUseRegionDebug && !IsLocked( prev_rx, prev_ry, rx, ry, layer ) )
{
assert( 0 );
XSEH::InvokeUnhandledException( STATUS_ILLEGAL_INSTRUCTION );
}
m_pData->pRgnMgr->GetRegion( prev_rx, prev_ry, pObject->GetLayer() )->RemoveObject( pObject );
m_pData->pRgnMgr->GetRegion( pObject )->AddObject( pObject );
}
}
void ArcadiaServer::enterProc( ArObject * pObject, unsigned prx, unsigned pry )
{
if( pObject->GetRX() == prx && pObject->GetRY() == pry ) return;
// 서로 enter 처리
struct _ArRegionFunctor : ArRegionFunctor
{
_ArRegionFunctor()
{
bIsSent = false;
pObject = NULL;
bIsClientObject = false;
}
void operator()( const struct ArRegion * pRegion )
{
if( !pRegion ) return;
// 서로에게 ENTER 메세지를 보낼때
struct _SendEnterMessageEachOther : ArObjectFunctor
{
_SendEnterMessageEachOther( ArObject * _pWho ) { pWho = _pWho; }
virtual void operator()( ArObject * pClient ) const
{
if( pClient == pWho ) return;
sendEnterMessage( pClient, pWho );
if( pWho->GetObjectType() == ArObject::CLIENT_OBJECT ) sendEnterMessage( pWho, pClient );
}
ArObject * pWho;
};
// 일방적인 ENTER 메세지를 보낼때
struct _SendEnterMessage : ArObjectFunctor
{
_SendEnterMessage( ArObject * _pClient ) { pClient = _pClient; }
virtual void operator()( ArObject * pObject ) const
{
sendEnterMessage( pClient, pObject );
// pClient 에게 ENTER 메세지를
if( pObject->GetObjectType() == ArObject::MOVABLE_OBJECT )
{
pObject->bIsNearClient = true;
if( pObject->GetFinalPriority() < ArObject::UPDATE_PRIORITY_NORMAL )
ArcadiaServer::Instance().SetObjectPriority( pObject, ArObject::UPDATE_PRIORITY_NORMAL );
}
}
ArObject * pClient;
};
_SendEnterMessageEachOther enter_each( pObject );
if( pRegion->DoEachClient( enter_each ) ) bIsSent = true;
if( pObject->GetObjectType() == ArObject::CLIENT_OBJECT )
{
_SendEnterMessage enter( pObject );
pRegion->DoEachStaticObject( enter );
pRegion->DoEachMovableObject( enter );
}
}
ArObject *pObject;
bool bIsClientObject;
bool bIsSent;
};
// { enter 메세지 처리를 위한 함수객체 생성
_ArRegionFunctor fo;
fo.pObject = pObject;
fo.bIsClientObject = ( pObject->GetObjectType() == ArObject::CLIENT_OBJECT ? true : false );
// }
// 새로 진입한 영역에 enter 메세지를 보낸다.
m_pData->pRgnMgr->DoEachNewRegion( pObject->GetRX(), pObject->GetRY(), prx, pry, pObject->GetLayer(), fo );
// 자신이 한번이라도 enter 메세지를 보낸적이 있다면 근처에 클라이언트가 있는 것이므로 활성화 시킨다.
if( fo.bIsSent )
{
pObject->bIsNearClient = true;
if( pObject->GetObjectType() == ArObject::MOVABLE_OBJECT && pObject->GetFinalPriority() < ArObject::UPDATE_PRIORITY_NORMAL )
{
SetObjectPriority( pObject, ArObject::UPDATE_PRIORITY_NORMAL );
}
}
// region 변경이 완료되었음을 통지한다.
if( pObject->GetObjectType() == ArObject::CLIENT_OBJECT ) m_pIntf->SendRegionAckMessage( pObject, pObject->GetRX(), pObject->GetRY() );
}
void ArcadiaServer::DeleteObject( ArSchedulerObject* pObject )
{
assert( !pObject->IsDeleteRequested() );
if( pObject->GetPriority() != ArObject::UPDATE_PRIORITY_IDLE )
{
SetObjectPriority( pObject, ArObject::UPDATE_PRIORITY_IDLE );
}
m_pData->scheduler.DeleteObject( pObject );
}
void ArcadiaServer::EnumMovableObject( const ArPosition & pos, unsigned char layer, AR_UNIT range, std::vector< AR_HANDLE > * pvResult, bool bIncludeClient, bool bIncludeNPC )
{
struct _ArRegionFunctor : ArRegionFunctor
{
struct _ArObjectFunctor : ArObjectFunctor
{
_ArObjectFunctor( _ArRegionFunctor * _pParent ) { pParent = _pParent; }
void operator()( ArObject * pObj ) const
{
const ArPosition c_pos = pObj->GetCurrentPosition( pParent->t );
if( pParent->left > c_pos.x ) return;
if( pParent->right < c_pos.x ) return;
if( pParent->top > c_pos.y ) return;
if( pParent->bottom < c_pos.y ) return;
AR_UNIT dist = c_pos.GetDistance( pParent->pos );
if( dist < pParent->range ) pParent->pvResult->push_back( pObj->GetHandle() );
}
_ArRegionFunctor * pParent;
};
_ArRegionFunctor( std::vector< AR_HANDLE > * _pvResult, const ArPosition & _pos, AR_UNIT _range, bool _bIncludeClient, bool _bIncludeNPC ) : pos( _pos ), bIncludeClient( _bIncludeClient ), bIncludeNPC( _bIncludeNPC )
{
range = _range;
left = _pos.x - _range;
right = _pos.x + _range;
top = _pos.y - _range;
bottom = _pos.y + _range;
pvResult = _pvResult;
t = GetArTime();
}
void operator()( const ArRegion * pRegion )
{
_ArObjectFunctor func( this );
if( bIncludeClient ) pRegion->DoEachClient( func );
if( bIncludeNPC ) pRegion->DoEachMovableObject( func );
}
std::vector< AR_HANDLE > *pvResult;
AR_TIME t;
const ArPosition & pos;
AR_UNIT left, right, top, bottom;
AR_UNIT range;
bool bIncludeClient, bIncludeNPC;
private:
const _ArRegionFunctor& operator=( const _ArRegionFunctor& ) { return *this; }
} _rf( pvResult, pos, range, bIncludeClient, bIncludeNPC );
m_pData->pRgnMgr->DoEachVisibleRegion( pos.GetRX(), pos.GetRY(), layer, _rf );
}
void ArcadiaServer::EnumMovableObjectInEveryRegion( int rx1, int ry1, int rx2, int ry2, unsigned char layer, std::vector< AR_HANDLE > * pvResult, bool bIncludeClient, bool bIncludeNPC )
{
struct _ArRegionFunctor : ArRegionFunctor
{
struct _ArObjectFunctor : ArObjectFunctor
{
_ArObjectFunctor( _ArRegionFunctor * _pParent ) { pParent = _pParent; }
void operator()( ArObject * pObj ) const
{
pParent->pvResult->push_back( pObj->GetHandle() );
}
_ArRegionFunctor * pParent;
};
_ArRegionFunctor( std::vector< AR_HANDLE > * _pvResult, bool _bIncludeClient, bool _bIncludeNPC ) : bIncludeClient( _bIncludeClient ), bIncludeNPC( _bIncludeNPC )
{
pvResult = _pvResult;
t = GetArTime();
}
void operator()( const ArRegion * pRegion )
{
_ArObjectFunctor func( this );
if( bIncludeClient ) pRegion->DoEachClient( func );
if( bIncludeNPC ) pRegion->DoEachMovableObject( func );
}
std::vector< AR_HANDLE > *pvResult;
AR_TIME t;
bool bIncludeClient, bIncludeNPC;
} _rf( pvResult, bIncludeClient, bIncludeNPC );
m_pData->pRgnMgr->DoEachRegion( rx1, ry1, rx2, ry2, layer, _rf );
}
void ArcadiaServer::EnumMovableObject( int rx1, int ry1, int rx2, int ry2, unsigned char layer, std::vector< AR_HANDLE > * pvResult, bool bIncludeClient, bool bIncludeNPC )
{
struct _ArRegionFunctor : ArRegionFunctor
{
struct _ArObjectFunctor : ArObjectFunctor
{
_ArObjectFunctor( _ArRegionFunctor * _pParent ) { pParent = _pParent; }
void operator()( ArObject * pObj ) const
{
pParent->pvResult->push_back( pObj->GetHandle() );
}
_ArRegionFunctor * pParent;
};
_ArRegionFunctor( std::vector< AR_HANDLE > * _pvResult, bool _bIncludeClient, bool _bIncludeNPC ) : bIncludeClient( _bIncludeClient ), bIncludeNPC( _bIncludeNPC )
{
pvResult = _pvResult;
t = GetArTime();
}
void operator()( const ArRegion * pRegion )
{
_ArObjectFunctor func( this );
if( bIncludeClient ) pRegion->DoEachClient( func );
if( bIncludeNPC ) pRegion->DoEachMovableObject( func );
}
std::vector< AR_HANDLE > *pvResult;
AR_TIME t;
bool bIncludeClient, bIncludeNPC;
} _rf( pvResult, bIncludeClient, bIncludeNPC );
m_pData->pRgnMgr->DoEachVisibleRegion( rx1, ry1, rx2, ry2, layer, _rf );
}
void ArcadiaServer::DoEachVisibleRegion( int rx, int ry, unsigned char layer, ArRegionFunctor & _fo )
{
m_pData->pRgnMgr->DoEachVisibleRegion( rx, ry, layer, _fo );
}
void ArcadiaServer::DoEachRegion( int rx1, int ry1, int rx2, int ry2, unsigned char layer, ArRegionFunctor & _fo )
{
m_pData->pRgnMgr->DoEachRegion( rx1, ry1, rx2, ry2, layer, _fo );
}
void ArcadiaServer::DoEachVisibleRegion( int rx1, int ry1, int rx2, int ry2, unsigned char layer, ArRegionFunctor & _fo )
{
m_pData->pRgnMgr->DoEachVisibleRegion( rx1, ry1, rx2, ry2, layer, _fo );
}