1121 lines
30 KiB
C++
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 );
|
|
}
|