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

215 lines
9.0 KiB
C++

#pragma once
#include <vector>
#include "ArObject.h"
#include "ArOption.h"
#include "../toolkit/XConsole.h"
#include "../toolkit/SafeTickCount.h"
/*
ArcadiaServer 의 주요 기능은 다음과 같다.
. 맵상의 object 들의 이동처리
. 임의의 지역에 대한 lock (SMP 관련)
Arcadia 엔진은 기본적으로 Network I/O 에 사용되는 메세지 프로토콜을 정의하지 않는다.
Arcadia 는 메세지가 포함해야할 기본적인 정보들만을 노출시키고 있으며 그것의 실제적인
구현은 전적으로 사용자에게 달려 있다.
이를테면 "플레이어가 x,y 좌표로 이동한다" 를 다루기 위해 필요한 것은
"ARCADIA_MOVE_MESSAGE" 같은 메세지 스트럭쳐가 아니라 단지 x, y 좌표일 뿐이다.
*/
struct ArcadiaIntf
{
virtual void SendEnterMessage( const ArObject * pClient, const ArObject * pObj ) = 0;
virtual void SendLeaveMessage( const ArObject * pClient, const ArObject * pObj ) = 0;
virtual void SendForceMoveMessage( const ArObject * pClient, const ArObject * pObj ) = 0;
virtual void SendMoveMessage( const ArObject * pClient, const ArObject * pObj ) = 0;
virtual void SendMoveAckMessage( const ArObject * pClient, AR_TIME tm, unsigned char speed ) = 0;
virtual void SendRegionAckMessage( const ArObject * pClient, unsigned rx, unsigned ry ) = 0;
virtual void SendGameMessage( const ArObject * pClient, const void *Message ) = 0;
virtual bool onSetMove( ArObject * pObj, const ArPosition & oldPos, const ArPosition & newPos ) { return true; }
virtual void onNearRegion( ArObject * pClient, ArObject * pObj ) {}
};
struct ArcadiaLock
{
int handle;
ArcadiaLock( int _handle ) : handle( _handle )
{
}
};
class ArcadiaServer
{
public:
virtual ~ArcadiaServer();
bool Init( ArcadiaIntf * pIntf, AR_UNIT map_width, AR_UNIT map_height, void (*scheduler_thread_init_func)( int ), bool bMonitoringThread = false );
bool DeInit();
// priority 를 변경할 필요가 없을경우, call instruction 을 피해가기 위해 인라인으로 뽑았음.
inline void SetObjectPriority( ArSchedulerObject * obj, ArSchedulerObject::AR_OBJECT_PRIORITY priority )
{
// 이미 등록되어 있으면 무시~
if( obj->GetPriority() == priority &&
obj->GetPendingPriority() == -1 ) return;
if( obj->GetPendingPriority() == priority ) return;
setObjectPriority( obj, priority );
}
// DeleteObject 호출시 자동으로 스케쥴러에서 제거되고, 이후 적절한 때에 ProcDelete() 가 호출 된다.
void DeleteObject( ArSchedulerObject* pObject );
void onRegionChange( ArObject* pObject, AR_TIME update_time, bool bIsStopMessage );
// 이하 함수들에서 layer 관련 변수가 short 타입인 이유는 unsigned char 타입에서 -1과 255가 같은 값으로 취급되는 문제를 피하기 위함
ArcadiaLock _LockWorld( const char * filename, int linenumber );
ArcadiaLock _LockWithVisibleRange( const char * filename, int linenumber, unsigned rx, unsigned ry, short layer = -1 );
ArcadiaLock _LockObjectWithVisibleRange( const char * filename, int linenumber, const ArObject * pObject );
ArcadiaLock _LockObjectWithSpecificRegion( const char * filename, int linenumber, const ArObject * pObject, unsigned rx, unsigned ry, short layer = -2 );
ArcadiaLock _LockObject( const char * filename, int linenumber, const ArObject * pObject );
ArcadiaLock _LockObjects( const char * filename, int linenumber, const ArObject * pObject1, const ArObject * pObject2 );
ArcadiaLock _LockArea( const char * filename, int linenumber, unsigned rx1, unsigned ry1, unsigned rx2, unsigned ry2, short layer = -1 ); // region_update 로만 사용해야함
ArcadiaLock _LockObjectWithSpecificRange( const char * filename, int linenumber, const ArObject *pObject, unsigned range );
void UnLock( ArcadiaLock * pLockHandle );
const bool IsLocked( const unsigned int rx, const unsigned int ry, const short layer = -1, const bool bPartial = false );
const bool IsLocked( unsigned rx1, unsigned ry1, unsigned rx2, unsigned ry2, const short layer = -1, const bool bPartial = false );
const bool IsLocked( const ArObject *pObject, const bool bPartial = false );
unsigned Broadcast( unsigned rx, unsigned ry, unsigned char layer, const void * msg );
unsigned Broadcast( unsigned rx1, unsigned ry1, unsigned rx2, unsigned ry2, unsigned char layer, const void * msg );
unsigned BroadcastToSpecificRegion( unsigned rx, unsigned ry, unsigned range, unsigned char layer, const void * msg );
// Lock 을 해야함.
void AddObject( ArObject* pObject );
void RemoveObject( ArObject* pObject );
void EnumMovableObject( const ArPosition & pos, unsigned char layer, AR_UNIT range, std::vector< AR_HANDLE > * pvResult, bool bIncludeClient = true, bool bIncludeNPC = true );
void EnumMovableObject( int rx1, int ry1, int rx2, int ry2, unsigned char layer, std::vector< AR_HANDLE > * pvResult, bool bIncludeClient = true, bool bIncludeNPC = true );
void EnumMovableObjectInEveryRegion( int rx1, int ry1, int rx2, int ry2, unsigned char layer, std::vector< AR_HANDLE > * pvResult, bool bIncludeClient = true, bool bIncludeNPC = true );
void DoEachVisibleRegion( int rx, int ry, unsigned char layer, struct ArRegionFunctor & _fo );
void DoEachVisibleRegion( int rx1, int ry1, int rx2, int ry2, unsigned char layer, ArRegionFunctor & _fo );
void DoEachRegion( int rx1, int ry1, int rx2, int ry2, unsigned char layer, ArRegionFunctor & _fo );
bool SetMultipleMove( ArObject* pObject, const ArPosition & curPos, const std::vector< ArPosition > & newPos, unsigned char speed, bool bAbsoluteMove, AR_TIME t, bool bBroadcastMove = true );
bool SetMove( ArObject* pObject, const ArPosition & curPos, const ArPosition & newPos, unsigned char speed, bool bAbsoluteMove, AR_TIME t, bool bBroadcastMove = true );
void MoveObject( ArObject* pObject, const ArPosition & newPos, const float face );
static struct ArcadiaIntf* GetIntf() { return m_pIntf; }
LONG GetLockedThreadCount();
LONG GetWaitingThreadCount();
const LONG & GetUserCount() { return m_nUserCount; }
const LONG & GetNPCCount() { return m_nNPCCount; }
const LONG & GetItemCount() { return m_nItemCount; }
static ArcadiaServer & Instance();
protected:
private:
int arcadia_lock( struct ArBoxCollision * pLockInfo, const char * filename, int linenumber );
void setObjectPriority( ArSchedulerObject * pObj, ArSchedulerObject::AR_OBJECT_PRIORITY priority );
ArcadiaServer();
void step( ArObject* pObject, AR_TIME tm );
void onMoveObject( ArObject* pObject, const ArPosition & oldPos, const ArPosition & newPos );
void enterProc( ArObject* pObject, unsigned prx, unsigned pry );
static struct ArcadiaIntf* m_pIntf;
static struct ArcadiaData* m_pData;
AR_UNIT m_nMapWidth, m_nMapHeight;
LONG m_nUserCount;
LONG m_nNPCCount;
LONG m_nItemCount;
};
struct ArcadiaAutoLock
{
ArcadiaAutoLock()
: plockhandle(NULL)
, lockHandle(0)
, tLockStartTick( GetSafeTickCount() )
, szFileName( "??" )
, nLineNumber( 0 )
{
}
ArcadiaAutoLock( ArcadiaLock & lock, const char *fn = "??", int ln = 0 )
: lockHandle( lock.handle )
, szFileName( fn )
, nLineNumber( ln )
, tLockStartTick( GetSafeTickCount() )
{
plockhandle = &lockHandle;
}
~ArcadiaAutoLock()
{
unsigned int lock_time = GetSafeTickCount() - tLockStartTick;
if( plockhandle ) ArcadiaServer::Instance().UnLock( plockhandle );
extern volatile int g_bUseLockDelayLogging;
if( !g_bUseLockDelayLogging )
return;
if( lock_time > 1000 )
{
// 로그를 남기자..
_lprint( "LockPerfLog.txt", "%s : %d [%d]\n", szFileName, nLineNumber, lock_time );
}
}
void set( ArcadiaLock & lock, const char *fn = "??", int ln = 0 )
{
szFileName = fn;
nLineNumber = ln;
tLockStartTick = GetSafeTickCount();
lockHandle.handle = lock.handle;
plockhandle = &lockHandle;
}
const char* szFileName;
int nLineNumber;
unsigned int tLockStartTick;
ArcadiaLock* plockhandle;
ArcadiaLock lockHandle;
};
// For information on __VA_ARGS__, see http://msdn.microsoft.com/en-us/library/ms177415(VS.80).aspx (variable-parameter macro definitions)
#define LockWorld() _LockWorld( __FILE__, __LINE__ )
// Syntax: Lock( rx, ry[, layer] )
#define LockWithVisibleRange( rx, ry, ... ) _LockWithVisibleRange( __FILE__, __LINE__, rx, ry, __VA_ARGS__ )
// Syntax: LockArea( rx1, ry1, rx2, ry2[, layer] )
#define LockArea( rx1, ry1, rx2, ry2, ... ) _LockArea( __FILE__, __LINE__, rx1, ry1, rx2, ry2, __VA_ARGS__ )
// Syntax: LockObjectWithSpecificRegion( pObject, rx, ry[, layer] )
#define LockObjectWithSpecificRegion( pObject, rx, ry, ... ) _LockObjectWithSpecificRegion( __FILE__, __LINE__, pObject, rx, ry, __VA_ARGS__ )
#define LockObjectWithVisibleRange( pObject ) _LockObjectWithVisibleRange( __FILE__, __LINE__, pObject )
#define LockObject( pObject ) _LockObject( __FILE__, __LINE__, pObject )
#define LockObjects( pObject1, pObject2 ) _LockObjects( __FILE__, __LINE__, pObject1, pObject2 )
#define LockObjectWithSpecificRange( pObject, range ) _LockObjectWithSpecificRange( __FILE__, __LINE__, pObject, range )
#define ARCADIA_LOCK(x) ArcadiaAutoLock _lock( x, __FILE__, __LINE__ )