Files
Leviathan/Library/Internal/include/mmo/ArRegion.h
T
2026-06-01 12:46:52 +02:00

592 lines
16 KiB
C++

#pragma once
#include <vector>
#include "ArObject.h"
#include "ArOption.h"
#include "../toolkit/XMemoryPool.h"
#include "../toolkit/ILock.h"
#include "../toolkit/XConsole.h"
#include "../logging/FileLog.h"
#include "../dump/XExceptionHandler.h"
/*
Arcadia 는 object 들이 이동함에 따라 이들사이의 enter/leave 를 처리해야 한다.
해서 Arcadia 는 맵상에 존재하는 object 들에 대한 정보를 참조할 필요가 있는데,
해서 object 들의 정보를 저장하기 위해 REGION 이라는 단위를 사용한다.
맵은 REGION_SIZE 만큼의 크기를 가진 REGION 들로 나뉘어진다.
그리고 해당 REGION 에 위치하는 모든 ArObject 들의 포인터는 ArRegion 에 등록된다.
ArRegion 은 x, y 를 기준으로 2차원으로 나뉘어고 (z는 무시된다) MAX_LAYER 만큼의 층을 갖는다.
여러개의 ArRegion 은 ArRegionContainer 가 관리한다.
여기서 실제 공간의 크기만큼 ArRegion 을 생성하는것은 실제로는 사용되지 않는 지역이
더 많기 때문에굉장한 메모리 낭비이다. 해서 모든 위치에 대한 ArRegion 을 생성하지 않고
ArRegionContainer 는 포인터 배열만 가지고 있다가, 해당 ArRegion 의 위치에 최초의
개체가 등록될때, ArRegion 을 생성한다.
맵상의 object 들이 이동함에 따라 그들이 속해있는 REGION 도 바뀌게 되는데
이러한 REGION 변경 시에는 해당 REGION 들에 대해 AddObject, RemoveObject 등으로
그것을 알려 주어야 한다.
*/
struct ArRegionFunctor
{
virtual void operator()( const struct ArRegion * pRegion ) = 0;
};
inline void debug_region_list( std::vector< ArObject* > & v )
{
//return true;
if( !g_bUseRegionDebug )
return;
for( int i = 0; i < (int)v.size(); i++ )
{
if( v[i]->region_index != i )
{
XSEH::InvokeUnhandledException( 0xC000000DL );
}
}
}
struct ArRegion
{
ArRegion()
{
x = 0;
y = 0;
layer = 0;
#ifdef _MEM_USAGE_DEBUG
XSEH::IncreaseAllocCount( "ArRegion" );
#endif
}
~ArRegion()
{
#ifdef _MEM_USAGE_DEBUG
XSEH::DecreaseAllocCount( "ArRegion" );
#endif
}
void AddStaticObject( ArObject* obj ) { addObject( obj, m_vStaticObject ); }
void AddMovableObject( ArObject* obj ) { addObject( obj, m_vMOVABLEObject ); }
void AddClientObject( ArObject* obj ) { addObject( obj, m_vClientObject ); }
void AddObject( ArObject* obj )
{
switch( obj->GetObjectType() ) {
case ArObject::STATIC_OBJECT: AddStaticObject( obj ); break;
case ArObject::MOVABLE_OBJECT: AddMovableObject( obj ); break;
case ArObject::CLIENT_OBJECT: AddClientObject( obj ); break;
}
}
void RemoveStaticObject( ArObject* obj ) { removeObject( obj, m_vStaticObject ); }
void RemoveMovableObject( ArObject* obj ) { removeObject( obj, m_vMOVABLEObject ); }
void RemoveClientObject( ArObject* obj ) { removeObject( obj, m_vClientObject ); }
void RemoveObject( ArObject* obj )
{
switch( obj->GetObjectType() ) {
case ArObject::STATIC_OBJECT: RemoveStaticObject( obj ); break;
case ArObject::MOVABLE_OBJECT: RemoveMovableObject( obj ); break;
case ArObject::CLIENT_OBJECT: RemoveClientObject( obj ); break;
}
}
size_t ClientCount() { return m_vClientObject.size(); }
unsigned DoEachClient( ArObjectFunctor & _fo ) const
{
std::vector< ArObject* >::const_iterator it;
for( it = m_vClientObject.begin(); it != m_vClientObject.end(); ++it )
{
_fo( *it );
}
return static_cast< unsigned >( m_vClientObject.size() );
}
unsigned DoEachStaticObject( ArObjectFunctor & _fo ) const
{
std::vector< ArObject* >::const_iterator it;
for( it = m_vStaticObject.begin(); it != m_vStaticObject.end(); ++it )
{
_fo( *it );
}
return static_cast< unsigned >( m_vStaticObject.size() );
}
unsigned DoEachMovableObject( ArObjectFunctor & _fo ) const
{
std::vector< ArObject* >::const_iterator it;
for( it = m_vMOVABLEObject.begin(); it != m_vMOVABLEObject.end(); ++it )
{
_fo( *it );
}
return static_cast< unsigned >( m_vMOVABLEObject.size() );
}
unsigned x, y;
unsigned char layer;
private:
void addObject( ArObject* obj, std::vector< ArObject* > & v )
{
assert( !obj->bIsInWorld );
debug_region_list( v );
v.push_back( obj );
obj->region_index = static_cast<int>( v.size() - 1 );
if( obj->region_index < 0 ) assert( 0 );
debug_region_list( v );
obj->bIsInWorld = true;
obj->pRegion = this;
};
void removeObject( ArObject* obj, std::vector< ArObject* > & v )
{
assert( obj->bIsInWorld );
debug_region_list( v );
if( v.empty() )
{
FILELOG( "REMOVE_OBJECT ERROR 1" );
_cprint( "REMOVE_OBJECT ERROR 1" );
assert( !"REMOVE_OBJECT ERROR 1" );
}
if( v[obj->region_index] != obj )
{
FILELOG( "REMOVE_OBJECT ERROR 2" );
_cprint( "REMOVE_OBJECT ERROR 2" );
assert( !"REMOVE_OBJECT ERROR 2" );
}
// { 마지막 원소가 아니라면 마지막 원소를 현재 위치로 가져온다음..
if( obj != v.back() )
{
v[obj->region_index] = v.back();
v[obj->region_index]->region_index = obj->region_index;
}
// }
// 마지막 원소 제거
v.pop_back();
obj->region_index = -1;
obj->bIsInWorld = false;
obj->pRegion = NULL;
debug_region_list( v );
};
std::vector< ArObject* > m_vStaticObject;
std::vector< ArObject* > m_vMOVABLEObject;
std::vector< ArObject* > m_vClientObject;
};
inline ArRegion* allocRegion()
{
return new ArRegion;
//return m_MemPool.Alloc();
}
inline void freeRegion( ArRegion* p )
{
delete p;
//m_MemPool.Free( p );
}
struct ArRegionContainer
{
enum
{
REGION_BLOCK_COUNT = 100,
};
ArRegionContainer( AR_UNIT map_width, AR_UNIT map_height )
{
m_MapWidth = map_width;
m_MapHeight = map_height;
m_nRegionWidth = unsigned int( m_MapWidth / g_nRegionSize + 1 );
m_nRegionHeight = unsigned int( m_MapHeight / g_nRegionSize + 1 );
m_nRegionBlockWidth = m_nRegionWidth / REGION_BLOCK_COUNT + 1;
m_nRegionBlockHeight = m_nRegionHeight / REGION_BLOCK_COUNT + 1;
initRegion();
}
~ArRegionContainer()
{
deinitRegion();
}
inline bool IsValidPosition( AR_UNIT x, AR_UNIT y )
{
if( x < 0 || y < 0 || x >= m_MapWidth || y >= m_MapHeight ) return false;
return true;
}
inline bool IsValidPosition( const ArPosition & pos )
{
return IsValidPosition( pos.x, pos.y );
}
inline bool IsValidRegion( unsigned rx, unsigned ry, unsigned char layer = 0 )
{
if( layer >= MAX_LAYER )
{
//throw XException( "-_-; 리전범위 (LAYER)" );
return false;
}
if( rx >= m_nRegionWidth || ry >= m_nRegionHeight )
{
//char buf[256];
//s_sprintf( buf, _countof( buf ), "-_-; 리전범위 (%d %d)", rx, ry );
//throw XException( buf );
return false;
}
return true;
}
inline ArRegion* IfClientExistRegion( unsigned rx, unsigned ry, unsigned char layer = 0 )
{
ArRegion * pRegion = IfExistRegion( rx, ry, layer );
if( !pRegion ) return NULL;
if( pRegion->ClientCount() ) return pRegion;
return NULL;
}
inline ArRegion* IfExistRegion( unsigned rx, unsigned ry, unsigned char layer = 0 )
{
if( !IsValidRegion( rx, ry, layer ) ) return NULL;
return getRegionPtr( rx, ry, layer );
}
// 없으면 할당해서 리턴한다.
inline ArRegion* GetRegion( unsigned rx, unsigned ry, unsigned char layer = 0 )
{
if( !IsValidRegion( rx, ry, layer ) ) return NULL;
return getRegion( rx, ry, layer );
}
// 없으면 할당해서 리턴한다.
inline ArRegion* GetRegion( AR_UNIT x, AR_UNIT y, unsigned char layer = 0 )
{
return GetRegion( GetRegionX( x ), GetRegionY( y ), layer );
}
// 없으면 할당해서 리턴한다.
inline ArRegion* GetRegion( const ArPosition & pos, unsigned char layer )
{
return GetRegion( pos.x, pos.y, layer );
}
// 없으면 할당해서 리턴한다.
inline ArRegion* GetRegion( ArObject * pObject )
{
return GetRegion( pObject->GetRX(), pObject->GetRY(), pObject->GetLayer() );
}
inline void DoEachSpecificRegion( unsigned rx, unsigned ry, unsigned range, unsigned char layer, ArRegionFunctor & Functor )
{
int left, right, top, bottom;
left = (std::max)( int(rx - range), 0 );
right = (std::min)( int(rx + range), (int)m_nRegionWidth-1 );
top = (std::max)( int(ry - range), 0 );
bottom = (std::min)( int(ry + range), (int)m_nRegionHeight-1 );
for( int x = left; x <= right; x++)
{
for( int y = top; y <= bottom; y++)
{
if( IsVisibleRegion( rx, ry, x, y ) )
{
ArRegion * pRegion = getRegionPtr( x, y, layer );
if( pRegion ) Functor( pRegion );
}
}
}
}
inline void DoEachVisibleRegion( unsigned rx, unsigned ry, unsigned char layer, ArRegionFunctor & Functor )
{
int left, right, top, bottom;
left = (std::max)( (int)rx - VISIBLE_REGION_RANGE, 0 );
right = (std::min)( (int)rx + VISIBLE_REGION_RANGE, (int)m_nRegionWidth-1 );
top = (std::max)( (int)ry - VISIBLE_REGION_RANGE, 0 );
bottom = (std::min)( (int)ry + VISIBLE_REGION_RANGE, (int)m_nRegionHeight-1 );
for( int x = left; x <= right; x++)
{
for( int y = top; y <= bottom; y++)
{
if( IsVisibleRegion( rx, ry, x, y ) )
{
ArRegion * pRegion = getRegionPtr( x, y, layer );
if( pRegion ) Functor( pRegion );
}
}
}
}
inline void DoEachNewRegion( unsigned rx, unsigned ry, unsigned prx, unsigned pry, unsigned char layer, ArRegionFunctor & Functor )
{
int left = (std::max)( 0, (std::min)( (int)rx - VISIBLE_REGION_RANGE, (int)prx - VISIBLE_REGION_RANGE ) );
int top = (std::max)( 0, (std::min)( (int)ry - VISIBLE_REGION_RANGE, (int)pry - VISIBLE_REGION_RANGE ) );
int right = (std::min)( (std::max)( (int)rx + VISIBLE_REGION_RANGE, (int)prx + VISIBLE_REGION_RANGE ), (int)m_nRegionWidth-1 );
int bottom = (std::min)( (std::max)( (int)ry + VISIBLE_REGION_RANGE, (int)pry + VISIBLE_REGION_RANGE ),(int)m_nRegionHeight-1 );
int nIsCurrentPos, nIsPrevPos;
for( int y = top; y <= bottom; y++ )
{
for( int x = left; x <= right; x++ )
{
nIsCurrentPos = IsVisibleRegion( rx, ry, x, y );
nIsPrevPos = IsVisibleRegion( prx, pry, x, y );
if( nIsCurrentPos && !nIsPrevPos )
{
ArRegion * pRegion = getRegionPtr( x, y, layer );
Functor( pRegion );
}
}
}
}
inline void DoEachVisibleRegion( unsigned rx1, unsigned ry1, unsigned rx2, unsigned ry2, unsigned char layer, ArRegionFunctor & Functor )
{
int left = (std::max)( 0, (std::min)( (int)rx1 - VISIBLE_REGION_RANGE, (int)rx2 - VISIBLE_REGION_RANGE ) );
int top = (std::max)( 0, (std::min)( (int)ry1 - VISIBLE_REGION_RANGE, (int)ry2 - VISIBLE_REGION_RANGE ) );
int right = (std::min)( (std::max)( (int)rx1 + VISIBLE_REGION_RANGE, (int)rx2 + VISIBLE_REGION_RANGE ), (int)m_nRegionWidth-1 );
int bottom = (std::min)( (std::max)( (int)ry1 + VISIBLE_REGION_RANGE, (int)ry2 + VISIBLE_REGION_RANGE ),(int)m_nRegionHeight-1 );
int nPos1, nPos2;
ArRegion * pRegion;
for( int y = top; y <= bottom; y++ )
{
for( int x = left; x <= right; x++ )
{
nPos1 = IsVisibleRegion( rx1, ry1, x, y );
nPos2 = IsVisibleRegion( rx2, ry2, x, y );
if( nPos1 || (!nPos1 && nPos2 ) )
{
pRegion = getRegionPtr( x, y, layer );
if( pRegion ) Functor( pRegion );
}
}
}
}
inline void DoEachRegion( unsigned rx1, unsigned ry1, unsigned rx2, unsigned ry2, unsigned char layer, ArRegionFunctor & Functor )
{
int left = (std::max)( 0, (std::min)( (int)rx1 - VISIBLE_REGION_RANGE, (int)rx2 - VISIBLE_REGION_RANGE ) );
int top = (std::max)( 0, (std::min)( (int)ry1 - VISIBLE_REGION_RANGE, (int)ry2 - VISIBLE_REGION_RANGE ) );
int right = (std::min)( (std::max)( (int)rx1 + VISIBLE_REGION_RANGE, (int)rx2 + VISIBLE_REGION_RANGE ), (int)m_nRegionWidth-1 );
int bottom = (std::min)( (std::max)( (int)ry1 + VISIBLE_REGION_RANGE, (int)ry2 + VISIBLE_REGION_RANGE ),(int)m_nRegionHeight-1 );
ArRegion * pRegion;
for( int y = top; y <= bottom; y++ )
{
for( int x = left; x <= right; x++ )
{
pRegion = getRegionPtr( x, y, layer );
if( pRegion ) Functor( pRegion );
}
}
}
private:
struct ArRegionBlock
{
ArRegionBlock()
{
THREAD_SYNCRONIZE( m_Lock );
for( unsigned i = 0; i < MAX_LAYER; i++ )
{
//m_pRegion[i] = new ArRegion*[ REGION_BLOCK_COUNT * REGION_BLOCK_COUNT ];
//ZeroMemory( m_pRegion[i], sizeof( ArRegion* ) * REGION_BLOCK_COUNT * REGION_BLOCK_COUNT );
m_pRegion[i] = NULL;
}
}
~ArRegionBlock()
{
THREAD_SYNCRONIZE( m_Lock );
for( unsigned i = 0; i < MAX_LAYER; i++ )
{
if( m_pRegion[i] )
{
for( unsigned x = 0; x < REGION_BLOCK_COUNT * REGION_BLOCK_COUNT; x++ )
{
if( m_pRegion[i][x] ) freeRegion( m_pRegion[i][x] );
}
delete [] m_pRegion[i];
}
}
}
ArRegion* getRegionPtr( unsigned rx, unsigned ry, unsigned char layer )
{
THREAD_SYNCRONIZE( m_Lock );
if( !m_pRegion[layer] )
return NULL;
return m_pRegion[layer][ ry * REGION_BLOCK_COUNT + rx ];
}
ArRegion* getRegion( unsigned rx, unsigned ry, unsigned char layer )
{
THREAD_SYNCRONIZE( m_Lock );
if( m_pRegion[layer] && m_pRegion[layer][ ry * REGION_BLOCK_COUNT + rx ] )
return m_pRegion[layer][ ry * REGION_BLOCK_COUNT + rx ];
if( !m_pRegion[layer] )
{
m_pRegion[layer] = new ArRegion*[ REGION_BLOCK_COUNT * REGION_BLOCK_COUNT ];
ZeroMemory( m_pRegion[layer], sizeof( ArRegion* ) * REGION_BLOCK_COUNT * REGION_BLOCK_COUNT );
}
m_pRegion[layer][ ry * REGION_BLOCK_COUNT + rx ] = allocRegion();
m_pRegion[layer][ ry * REGION_BLOCK_COUNT + rx ]->x = rx;
m_pRegion[layer][ ry * REGION_BLOCK_COUNT + rx ]->y = ry;
m_pRegion[layer][ ry * REGION_BLOCK_COUNT + rx ]->layer = layer;
return m_pRegion[layer][ ry * REGION_BLOCK_COUNT + rx ];
}
ArRegion** m_pRegion[ MAX_LAYER ];
XSpinLock m_Lock;
};
void initRegion()
{
THREAD_SYNCRONIZE( m_Lock );
m_pRegionBlock = new ArRegionBlock*[ m_nRegionBlockWidth * m_nRegionBlockHeight ];
ZeroMemory( m_pRegionBlock, sizeof( ArRegionBlock* ) * m_nRegionBlockWidth * m_nRegionBlockHeight );
}
void deinitRegion()
{
THREAD_SYNCRONIZE( m_Lock );
for( unsigned x = 0; x < m_nRegionBlockWidth * m_nRegionBlockHeight; x++ )
{
if( m_pRegionBlock[x] ) delete m_pRegionBlock[x];
}
delete [] m_pRegionBlock;
}
ArRegionBlock* getRegionBlockPtr( unsigned rcx, unsigned rcy )
{
THREAD_SYNCRONIZE( m_Lock );
ArRegionBlock *pBlock = m_pRegionBlock[ rcy * m_nRegionBlockWidth + rcx ];
return pBlock;
}
ArRegionBlock* getRegionBlock( unsigned rcx, unsigned rcy )
{
THREAD_SYNCRONIZE( m_Lock );
ArRegionBlock *pBlock = m_pRegionBlock[ rcy * m_nRegionBlockWidth + rcx ];
if( pBlock )
return pBlock;
m_pRegionBlock[ rcy * m_nRegionBlockWidth + rcx ] = new ArRegionBlock;
return m_pRegionBlock[ rcy * m_nRegionBlockWidth + rcx ];
}
bool isExistRegion( unsigned rx, unsigned ry, unsigned char layer )
{
unsigned rcx = rx / REGION_BLOCK_COUNT;
unsigned rcy = ry / REGION_BLOCK_COUNT;
ArRegionBlock* pBlock = getRegionBlockPtr( rcx, rcy );
if( !pBlock ) return false;
ArRegion *pRegion = pBlock->getRegionPtr( rx % REGION_BLOCK_COUNT, ry % REGION_BLOCK_COUNT, layer );
if( !pRegion ) return false;
return true;
}
ArRegion* getRegionPtr( unsigned rx, unsigned ry, unsigned char layer )
{
unsigned rcx = rx / REGION_BLOCK_COUNT;
unsigned rcy = ry / REGION_BLOCK_COUNT;
ArRegionBlock* pBlock = getRegionBlockPtr( rcx, rcy );
if( !pBlock ) return NULL;
return pBlock->getRegionPtr( rx % REGION_BLOCK_COUNT, ry % REGION_BLOCK_COUNT, layer );
}
ArRegion* getRegion( unsigned rx, unsigned ry, unsigned char layer )
{
unsigned rcx = rx / REGION_BLOCK_COUNT;
unsigned rcy = ry / REGION_BLOCK_COUNT;
ArRegionBlock* pBlock = getRegionBlock( rcx, rcy );
ArRegion * pRegion = pBlock->getRegion( rx % REGION_BLOCK_COUNT, ry % REGION_BLOCK_COUNT, layer );
return pRegion;
}
AR_UNIT m_MapWidth, m_MapHeight;
unsigned m_nRegionWidth;
unsigned m_nRegionHeight;
unsigned m_nRegionBlockWidth;
unsigned m_nRegionBlockHeight;
ArRegionBlock** m_pRegionBlock;
XSpinLock m_Lock;
//ArRegion** m_pRegion[MAX_LAYER];
//XUnitPool<ArRegion> m_MemPool;
};