// Include
#include "gamesrv.h"

#include "protocol.h"
#include "protocol_Cheat.h"
#include "commonnetworkmsgstruct.h"

#include "item_common.h"
#include "tarot_common.h"
#include "party_common.h"
#include "chat_common.h"
#include "Cheat_Common.h"
#include "Guild_Common.h"
#include "MakeSkill_Common.h"

#include "player.h"
#include "npc.h"
#include "item.h"
#include "monster.h"
#include "Guild.h"
#include "Gathering.h"
#include "statuscalc_server.h"
#include "rangecheck.h"
#include "pointerhashmap.h"
#include "Duel_Common.h"
#include "PvPScript.h"
#include "NpcScript.h"
#include "ShortcutScript.h"
#include "Drop.h"

#include "ClientInfo.h"

// Local definitions
#pragma warning( disable: 4127 )

/* macro for SQL Class checking */
#define SQL_ACCOUNT_CHECK(sqlAccount)       \
sqlAccount = g_gameSrv->GetSQLAccount( );   \
if ( sqlAccount == NULL )                   \
return false;

#define SQL_GAME_CHECK(sqlGame)             \
sqlGame = g_gameSrv->GetSQLGame( );         \
if ( sqlGame == NULL )                      \
return false;

/* macro for UDP Class checking */
#define SENDER_CHECK(sender)                \
sender = g_gameSrv->GetSender( );           \
if ( sender == NULL )                       \
return false;

/* macro for TCP Class checking */
#define LOGIN_CLIENT_CHECK(loginClient)     \
loginClient = g_gameSrv->GetLoginClient( ); \
if ( loginClient == NULL )                  \
return false;


#pragma pack(1) // 1Byte 정렬 설정

struct Game2Game : Packet
{
	unsigned long  ipv4;
	unsigned short port;
};

#pragma pack( ) // 1Byte 정렬 해제


// Global data
cGameProcess* cGameProcess::mGameProcess = NULL;

BYTE SY_KEY_TABLE[16][4]=
{
	{ 0x17,0xb3,0x67,0xaa },	//0
	{ 0x53,0xf2,0x74,0xb1 },	//1
	{ 0x8c,0x72,0x59,0x32 },	//2
	{ 0x98,0xea,0x55,0x29 },	//3
	{ 0x4B,0x1C,0x15,0x11 },	//4
	{ 0xE8,0x9B,0x3A,0x8A },	//5
	{ 0x0F,0xAD,0x37,0x6C },	//6
	{ 0xD9,0x64,0x28,0x5C },	//7
	{ 0xB3,0xB7,0xEC,0x7B },	//8
	{ 0x9C,0x13,0xFE,0xF5 },	//9
	{ 0x5C,0x55,0x6A,0xD8 },	//10
	{ 0x6C,0xA9,0xDB,0x64 },	//11
	{ 0x5A,0xBE,0xF2,0xF6 },	//12
	{ 0xA6,0x4D,0x8F,0xB9 },	//13
	{ 0x67,0x9F,0x7D,0xF6 },	//14
	{ 0xCA,0x6A,0xA9,0xEF },	//15
};

BYTE SY_RKEY_TABLE[16][4]=
{
	{ 0xC0,0x3C,0x06,0xEA },	//0
	{ 0xB9,0x78,0xB3,0x45 },	//1
	{ 0x88,0x48,0xC0,0x71 },	//2
	{ 0x85,0x4C,0xB6,0xE7 },	//3
	{ 0x98,0xAE,0xD9,0xBF },	//4
	{ 0x2D,0x83,0x06,0x55 },	//5
	{ 0x44,0x95,0xAF,0x99 },	//6
	{ 0x43,0xC8,0x53,0x63 },	//7
	{ 0x3B,0xB1,0x60,0x0B },	//8
	{ 0x19,0x2D,0x57,0xC6 },	//9
	{ 0x68,0xCC,0x56,0x3A },	//10
	{ 0x5C,0xF8,0x90,0x4A },	//11
	{ 0xA1,0x8A,0x0B,0x70 },	//12
	{ 0x28,0x45,0x80,0xB1 },	//13
	{ 0x71,0x9D,0x4A,0x80 },	//14
	{ 0x54,0x1B,0x0F,0x9C },	//15
};

// cGameProcess Constructor.
cGameProcess::cGameProcess(DWORD serverNum, DWORD channelNum, TCHAR* inetnum) : mServerNum(serverNum), mChannelNum(channelNum), mServerInetnum(inetnum)
{
	mGameProcess         = this;

	mRequestImportData   = 0;
	mCompleteImportData  = 0;

	mRequestData         = 0;
	mCompleteData        = 0;

	mPVPData.cid         = 0;
	mPVPData.status      = _E_PVP_CLOSED_;
	mPVPData.timer       = 0;

	mFrame               = 0;
	mFpsTick             = 0;
	mThreadPriority      = false;
	mThreadPriorityTick  = 0;

	mElapsedTick         = 0;
	mMinElapsedTick      = 0;
	mMaxElapsedTick      = 0;

	// 채널 정보 초기화
	mType                = _E_ST_NORMAL_MAP_;
	mStatus              = _E_STATUS_CLOSED_;

	// 채널 리스트 초기화
	ZeroMemory( mChannelList, sizeof(mChannelList) );

	mTTLTick             = 0;
	mStatusSyncTick      = 0;

	// During Time 멤버 변수 초기화
	mDuringSkill         = 0;
	mDuringObject        = 0;
	mDuringAI            = 0;
	mDuringDuel          = 0;
	mDuringPVP           = 0;
	mDuringGrid          = 0;

	// MsgRoot & Last Packet Size
	mLastCategory        = 0;
	mLastProtocol        = 0;
	mLastPacketSize      = 0;

	// MapServer 멤버 변수 초기화
	mpAppTimer           = NULL;
	mpMemoryPool         = NULL;
	mpRandomTable        = NULL;
	mpPackedData         = NULL;
	mpGatheringScript    = NULL;
	mpAIManager          = NULL;
	mpSkillManager       = NULL;
	mpGridManager        = NULL;
	mpObjectManager      = NULL;
	mpStageScript		 = NULL;
	mpItemManager        = NULL;
	mpCooltimePool       = NULL;
	mpTakeDamagePool	 = NULL;
	mpItemCountPool      = NULL;
	mpPartyManager		 = NULL;
	mpTriggerManager	 = NULL;
	mpQuestManager		 = NULL;
	mpDuelManager        = NULL;
	mpPVPManager         = NULL;
	mpTitleManager		 = NULL;
	mpGuildManager		 = NULL;

	mAccumTime           = 0;
}

// ~cGameProcess Destructor.
cGameProcess::~cGameProcess(void)
{
	mGameProcess = NULL;
}

// Initialize Method - 순서에 주의!
bool cGameProcess::Initialize(char* ipAddr, unsigned short port, unsigned short numWorkerThreads, unsigned short bufferLength)
{
	switch ( mChannelNum )
	{
	case CHN_11:
		mType = _E_ST_ID_PVP_;
		break;
	default:
		mType = _E_ST_NORMAL_MAP_;
		break;
	}

	// cFileSystem 클래스 생성
	mpFileSystem = new cFileSystem( );

	// cAppTimer 클래스 생성
	mpAppTimer = new cAppTimer( );

	// cMemoryPool 클래스 생성
	mpMemoryPool = new cMemoryPool( );

	// cRandomTable 클래스 생성 및 초기화
	mpRandomTable = new cRandomTable( );
	if( !mpRandomTable->Initialize( ) )
		return false;

	// cStageScript 클래스 생성 및 초기화
	mpStageScript = new cStageScript( );
	if( !mpStageScript->Init( ) )
		return false;

	// cGridManager 클래스 생성 및 초기화
	mpGridManager = new cGridManager( );
	if( !mpGridManager->Init( _E_GMT_3X3_, mType ) )
		return false;

	// cGatheringScript 클래스 생성 및 초기화
	mpGatheringScript = new cGatheringScript( );
	if( !mpGatheringScript->Init( ) )
		return false;

	// cObjectManager 클래스 생성 및 초기화
	mpObjectManager = new cObjectManager( );
	if( !mpObjectManager->Init( mType ) )
		return false;		

	// cSkillManager 클래스 생성 및 초기화
	mpSkillManager = new cSkillManager( );
	if( !mpSkillManager->Init( ) )
		return false;

	// cAIManager 클래스 생성 및 초기화
	mpAIManager = new cAIManager( );
	if( !mpAIManager->Init( mType ) )
		return false;

	// cItemManager 클래스 생성 및 초기화
	mpItemManager = new cItemManager( );
	if ( !mpItemManager->Init( ) )
		return false;

	mpCooltimePool = new cCooltimePool( );
	if ( mpCooltimePool == NULL )
		return false;

	mpTakeDamagePool = new cTakeDamagePool( );
	if ( mpTakeDamagePool == NULL )
		return false;

	mpItemCountPool = new cItemCountPool( );
	if ( mpItemCountPool == NULL )
		return false;

	// cPartyManager 클래스 생성 및 초기화
	mpPartyManager = new cPartyManager( );
	if( !mpPartyManager->Init() )
		return false;

	// cTriggerManager 클래스 생성 및 초기화
	mpTriggerManager = new cTriggerManager( );
	if( !mpTriggerManager->Init() )
		return false;

	// cQuestManager 클래스 생성 및 초기화
	mpQuestManager = new cQuestManager( );
	if( !mpQuestManager->Init() )
		return false;

	// cDuelManager 클래스 생성 및 초기화
	mpDuelManager = new cDuelManager( );
	if( !mpDuelManager->Init() )
		return false;

	// cPvPManager 클래스 생성 및 초기화
	mpPVPManager = new cPVPManager( );
	if( !mpPVPManager->Init() )
		return false;

	// cTitleManager 클래스 생성 및 초기화
	mpTitleManager = new cTitleManager( );
	if( !mpTitleManager->Init() )
		return false;

	// cGuildManager 클래스 생성
	mpGuildManager = new cGuildManager( );
	if( mpGuildManager == NULL )
		return false;

	// cNoteManager 클래스 생성
	mpNoteManager = new cNoteManager( );
	if( mpNoteManager == NULL )
		return false;

	// 타이머 시작
	if ( mpAppTimer != NULL )
		mpAppTimer->Start( );

	// IocpServer 초기화.
	return cIocpServer::Initialize( ipAddr, port, numWorkerThreads, bufferLength );
}

// Shutdown Method - 순서에 주위!
void cGameProcess::Shutdown(DWORD maxWait)
{
	// IocpServer 종료.
	cIocpServer::Shutdown( maxWait );

	if ( mpAppTimer != NULL )
		mpAppTimer->Stop();

	SAFE_DELETE( mpNoteManager );
	SAFE_RELEASE_DELETE( mpGuildManager );
	SAFE_RELEASE_DELETE( mpTitleManager );
	SAFE_RELEASE_DELETE( mpPVPManager );
	SAFE_RELEASE_DELETE( mpDuelManager );
	SAFE_DELETE( mpQuestManager );
	SAFE_DELETE( mpTriggerManager );
	SAFE_DELETE( mpPartyManager );
	SAFE_DELETE( mpItemCountPool );
	SAFE_DELETE( mpTakeDamagePool );
	SAFE_DELETE( mpCooltimePool );
	SAFE_DELETE( mpItemManager );
	SAFE_RELEASE_DELETE( mpStageScript );
	SAFE_RELEASE_DELETE( mpAIManager );
	SAFE_DELETE( mpSkillManager );
	SAFE_RELEASE_DELETE( mpObjectManager );
	SAFE_RELEASE_DELETE( mpGatheringScript );
	SAFE_RELEASE_DELETE( mpGridManager );
	SAFE_DELETE( mpRandomTable );
	SAFE_DELETE( mpMemoryPool );
	SAFE_DELETE( mpAppTimer );
	SAFE_DELETE( mpFileSystem );
}

// CloseCID Method
bool cGameProcess::CloseCID(DWORD cid, bool how)
{
	cCSLock           lock( &mCs );
	PerSocketContext* perSocketContext = mSocketContextPool->GetCID( cid );
	if ( perSocketContext != NULL )
	{
		if ( how == true )
			shutdown( perSocketContext->socket, SD_RECEIVE );
		else
			Close( perSocketContext );
		return true;
	}
	return false;
}

// GetIoContext Method
PerIoContext* cGameProcess::GetIoContext(u_long request)
{
	return mIoContextPool->GetIoContext( NULL, request );
}

// ReleaseIoContext Method
void cGameProcess::ReleaseIoContext(PerIoContext* perIoContext, char* callStack)
{
	DWORD error = 0;
	mIoContextPool->ReleaseIoContext( perIoContext /*false, &error*/ );
	if ( error != 0 )
	{
		if ( callStack != NULL )
		{
			PostServerEvent( "WARNING - cGameProcess::ReleaseIoContext:Error(=0x%08xh) / CallStack(=%s).", error, callStack );
		}
		else
		{
			PostServerEvent( "WARNING - cGameProcess::ReleaseIoContext:Error(=0x%08xh)", error );
		}
	}
}

// GetSQL Method
char* cGameProcess::GetSQL(void** handle, UINT_PTR iParam)
{
	PerIoContext* perIoContext = NULL;

	if ( iParam >= DEFAULT_SQL_ACCOUNT && iParam < MAX_SQL_ACCOUNT )
	{
		perIoContext = g_gameSrv->GetSQLAccount( )->Get( );
	}
	else if ( iParam >= DEFAULT_SQL_GAME && iParam < MAX_SQL_GAME )
	{
		perIoContext = g_gameSrv->GetSQLGame( )->Get( );
	}
	else if ( iParam >= DEFAULT_GAME_PROCESS && iParam < MAX_GAME_PROCESS )
	{
		perIoContext = mIoContextPool->GetIoContext( NULL, IOCP_REQUEST_CALLBACK );
	}
	else
		PostServerEvent( "WARNING - cGameProcess::GetSQL:iParam(=0x%04dxh)", iParam );

	if ( perIoContext != NULL )
	{
		(*handle) = perIoContext;
		perIoContext->iParam = iParam;
		return perIoContext->buffer;
	}
	else
		return NULL;
}

// SendSQL Method
bool cGameProcess::SendSQL(void* handle, unsigned long length)
{
	PerIoContext* perIoContext = (PerIoContext*)handle;
	BOOL          retvalue     = FALSE;

	perIoContext->offset = length;

	if ( perIoContext->iParam >= DEFAULT_SQL_ACCOUNT && perIoContext->iParam < MAX_SQL_ACCOUNT )
	{
		// SQLAccount를 통해 SQLServer로 요청.
		retvalue = g_gameSrv->GetSQLAccount( )->QueueRequest( NULL, (LPOVERLAPPED)perIoContext, perIoContext->offset );
	}
	else if ( perIoContext->iParam >= DEFAULT_SQL_GAME && perIoContext->iParam < MAX_SQL_GAME )
	{
		// cSQLGame를 통해 SQLServer로 요청.
		retvalue = g_gameSrv->GetSQLGame( )->QueueRequest( NULL, (LPOVERLAPPED)perIoContext, perIoContext->offset );
	}
	else
		PostServerEvent( "WARNING - cGameProcess::SendSQL:iParam(=0x%04dxh)", perIoContext->iParam );

	return (retvalue == TRUE) ? true : false;
}
bool cGameProcess::SendSQL(PerSocketContext* perSocketContext, void* handle, unsigned long length, long commonDatabase)
{
	PerIoContext* perIoContext = (PerIoContext*)handle;
	BOOL          retvalue     = FALSE;

	perIoContext->socket = perSocketContext->socket;
	perIoContext->offset = length;

	if ( perIoContext->iParam >= DEFAULT_SQL_ACCOUNT && perIoContext->iParam < MAX_SQL_ACCOUNT )
	{
		// SQLAccount를 통해 SQLServer로 요청.
		retvalue = g_gameSrv->GetSQLAccount( )->QueueRequest( (ULONG_PTR)perSocketContext, (LPOVERLAPPED)perIoContext, perIoContext->offset );
	}
	else if ( perIoContext->iParam >= DEFAULT_SQL_GAME && perIoContext->iParam < MAX_SQL_GAME )
	{
		// cSQLGame를 통해 SQLServer로 요청.
		retvalue = g_gameSrv->GetSQLGame( )->QueueRequest( (ULONG_PTR)perSocketContext, (LPOVERLAPPED)perIoContext, perIoContext->offset );
	}
	else
		PostServerEvent( "WARNING - cGameProcess::SendSQL:iParam(=0x%04dxh)", perIoContext->iParam );

	if ( retvalue == TRUE )
	{
		ClientInfo* clientInfo = (ClientInfo*)perSocketContext->buffer;
		clientInfo->commonDatabase |= commonDatabase;
		return true;
	}
	else
		return false;
}
bool cGameProcess::SendSQL(unsigned long connectionIdx, void* handle, unsigned long length, long commonDatabase)
{
	PerSocketContext* perSocketContext = mSocketContextPool->GetCID( connectionIdx );
	if ( perSocketContext != NULL )
	{
		return SendSQL( perSocketContext, handle, length, commonDatabase );
	}
	else
		return false;
}

// ReleaseSQL Method
void cGameProcess::ReleaseSQL(void* handle, unsigned long length)
{
	PerIoContext* perIoContext = (PerIoContext*)handle;

	perIoContext->offset = length;
	if ( perIoContext->iParam >= DEFAULT_SQL_ACCOUNT && perIoContext->iParam < MAX_SQL_ACCOUNT )
	{
		g_gameSrv->GetSQLAccount( )->Release( perIoContext );
	}
	else if ( perIoContext->iParam >= DEFAULT_SQL_GAME && perIoContext->iParam < MAX_SQL_GAME )
	{
		g_gameSrv->GetSQLGame( )->Release( perIoContext );
	}
	else if ( perIoContext->iParam >= DEFAULT_GAME_PROCESS && perIoContext->iParam < MAX_GAME_PROCESS )
	{
		DWORD error = 0;
		mIoContextPool->ReleaseIoContext( perIoContext /*false, &error*/ );
		if ( error != 0 )
			PostServerEvent( "WARNING - cGameProcess::ReleaseSQL:Error(=0x%08xh)", error );
	}
	else
		PostServerEvent( "WARNING - cGameProcess::ReleaseSQL:iParam(=0x%04dxh)", perIoContext->iParam );
}

// SendToUser Method
bool cGameProcess::SendToUser(unsigned long connectionIdx, char* msg, unsigned long length)
{
	cCSLock  lock( &mCs );

	HANDLE   handle  = NULL;
	MSGROOT* msgRoot = (MSGROOT*)GetMsgRoot( &handle, connectionIdx );

	if ( msgRoot != NULL )
	{
		memcpy( msgRoot, msg, length );
		return SendMsgRoot( handle, length );
	}
	else
		return false;
}

// GetMsgRoot Method
MSGROOT* cGameProcess::GetMsgRoot(void** handle, PerSocketContext* perSocketContext, char category, char protocol)
{
	PerIoContext*     perIoContext     = NULL;
	Packet*           packet           = NULL;
	MSGROOT*          msgRoot          = NULL;

	perIoContext = mIoContextPool->GetIoContext( perSocketContext->socket, IOCP_REQUEST_WRITE );
	if ( perIoContext != NULL )
	{
		packet       = (Packet*)perIoContext->buffer;

		packet->ver  = PHVer;
		packet->hlen = PHLen;
		packet->tos  = TOS_GAME;
		packet->tlen = packet->hlen;

		(*handle) = perIoContext;
		msgRoot   = (MSGROOT*)(perIoContext->buffer + packet->hlen);
			msgRoot->Category = category;
			msgRoot->Protocol = protocol;
		return msgRoot;
	}
	else
		return NULL;
}
MSGROOT* cGameProcess::GetMsgRoot(void** handle, unsigned long connectionIdx, char category, char protocol)
{
	cCSLock           lock( &mCs );
	PerSocketContext* perSocketContext = mSocketContextPool->GetCID( connectionIdx );
	return ( perSocketContext != NULL ) ? GetMsgRoot( handle, perSocketContext, category, protocol ) : NULL;
}

// SendMsgRoot Method
bool cGameProcess::SendMsgRoot(void* handle, unsigned long length)
{
	PerIoContext* perIoContext = (PerIoContext*)handle;
	Packet*       packet       = (Packet*)perIoContext->buffer;

	packet->tlen = packet->hlen + (u_short)length;

	perIoContext->offset = packet->tlen;
	return SendPost( perIoContext );
}

// SendMsgRoot Method
void cGameProcess::SendMsgRoot(char* buffer, unsigned long& internal, unsigned long& internalHigh, char* msg, int msgLen)
{
	DWORD             cid;
	PerSocketContext* perSocketContext;

	while ( internal < internalHigh )
	{
		cid = *(DWORD*)(buffer + internal);
		perSocketContext = mSocketContextPool->GetCID( cid );
		internal += sizeof(DWORD);

		if ( perSocketContext != NULL )
		{
			HANDLE        handle  = NULL;
			MSGROOT*      msgRoot = GetMsgRoot( &handle, perSocketContext );

			if ( msgRoot != NULL )
			{
				memcpy( msgRoot, msg, msgLen );
				SendMsgRoot( handle, msgLen );
			}
		}
	}
}

// ReleaseMsgRoot Method
void cGameProcess::ReleaseMsgRoot(void* handle, unsigned long length)
{
	PerIoContext* perIoContext = (PerIoContext*)handle;
	Packet*       packet       = (Packet*)perIoContext->buffer;
	DWORD         error        = 0;

	packet->tlen = packet->hlen + (u_short)length;

	perIoContext->offset = packet->tlen;
	mIoContextPool->ReleaseIoContext( perIoContext /*false, &error*/ );
	if ( error != 0 )
		PostServerEvent( "WARNING - cGameProcess::ReleaseMsgRoot:Error(=0x%08xh)", error );
}

// QuickSend Method
bool cGameProcess::QuickSend(cBaseObject* object, char* msg, int length)
{
	PerIoContext* perIoContext = mIoContextPool->GetIoContext( );
	DWORD         error        = 0;

	mpGridManager->MakeCID( object, sizeof(DWORD), perIoContext->buffer, perIoContext->length, perIoContext->InternalHigh );
	SendMsgRoot( perIoContext->buffer, perIoContext->Internal, perIoContext->InternalHigh, msg, length );

	mIoContextPool->ReleaseIoContext( perIoContext /*false, &error*/ );
	if ( error != 0 )
		PostServerEvent( "WARNING - cGameProcess::QuickSend:Error(=0x%08xh)", error );
	return true;
}

// QuickSendExcept Method
bool cGameProcess::QuickSendExcept(cBaseObject* object, char* msg, int length)
{
	PerIoContext* perIoContext = mIoContextPool->GetIoContext();
	DWORD         error        = 0;

	mpGridManager->MakeCIDExcept( object, sizeof(DWORD), perIoContext->buffer, perIoContext->length, perIoContext->InternalHigh );
	SendMsgRoot( perIoContext->buffer, perIoContext->Internal, perIoContext->InternalHigh, msg, length );

	mIoContextPool->ReleaseIoContext( perIoContext /*false, &error*/ );
	if ( error != 0 )
		PostServerEvent( "WARNING - cGameProcess::QuickSendExcept:Error(=0x%08xh)", error );
	return true;
}

// QuickSendMap Method
bool cGameProcess::QuickSendMap(cBaseObject* object, char* msg, int length)
{
	PerIoContext* perIoContext = mIoContextPool->GetIoContext( );
	DWORD         error        = 0;

	mpGridManager->MakeCIDMap( object, sizeof(DWORD), perIoContext->buffer, perIoContext->length, perIoContext->InternalHigh );
	SendMsgRoot( perIoContext->buffer, perIoContext->Internal, perIoContext->InternalHigh, msg, length );

	mIoContextPool->ReleaseIoContext( perIoContext /*false, &error*/ );
	if ( error != 0 )
		PostServerEvent( "WARNING - cGameProcess::QuickSendMap:Error(=0x%08xh)", error );
	return true;
}

// QuickSendMap Method
bool cGameProcess::QuickSendMap(unsigned short mapNumber, char* msg, int length)
{
	PerIoContext* perIoContext = mIoContextPool->GetIoContext( );
	DWORD         error        = 0;

	mpGridManager->MakeCIDMap( mapNumber, sizeof(DWORD), perIoContext->buffer, perIoContext->length, perIoContext->InternalHigh );
	SendMsgRoot( perIoContext->buffer, perIoContext->Internal, perIoContext->InternalHigh, msg, length );

	mIoContextPool->ReleaseIoContext( perIoContext /*false, &error*/ );
	if ( error != 0 )
		PostServerEvent( "WARNING - cGameProcess::QuickSendMap:Error(=0x%08xh)", error );
	return true;
}

// QuickSendMap Method
bool cGameProcess::QuickSendExceptMap(cPlayer* object1, cPlayer* object2, char* msg, int length)
{
	PerIoContext* perIoContext = mIoContextPool->GetIoContext( );
	DWORD         error        = 0;

	mpGridManager->MakeCIDExceptMap( object1, object2, sizeof(DWORD), perIoContext->buffer, perIoContext->length, perIoContext->InternalHigh );
	SendMsgRoot( perIoContext->buffer, perIoContext->Internal, perIoContext->InternalHigh, msg, length );

	mIoContextPool->ReleaseIoContext( perIoContext /*false, &error*/ );
	if ( error != 0 )
		PostServerEvent( "WARNING - cGameProcess::QuickSendExceptMap:Error(=0x%08xh)", error );
	return true;
}

// PostServerEvent Method
bool cGameProcess::PostServerEvent(LPCTSTR format, ...)
{
	bool retvalue = false;
	if ( g_gameSrv != NULL )
	{
		LPVOID  msgBuf = NULL;
		DWORD   bufferLength;

		va_list args;

		va_start( args, format );

		bufferLength = _vscprintf( format, args ) + 1;
		msgBuf       = malloc( bufferLength );

		vsprintf( (char*)msgBuf, format, args );

		va_end( args );

		if ( msgBuf != NULL )
		{
			if( CONSOLEPRINT == true )
			{
				/// 에러 로그를 console 화면에 출력
				printf("\n%s",msgBuf);
				retvalue = true;
			}
			else
				retvalue = g_gameSrv->PostServerEvent( (char*)msgBuf );

			free( msgBuf );
		}
	}
	return retvalue;
}

// PostMemberEvent Method
bool cGameProcess::PostMemberEvent(char type, char category, long memberIdx, char* message)
{
	cSender* sender = NULL;
	SENDER_CHECK( sender );
	return sender->PostMemberEvent( type, category, memberIdx, message );
}

// PostCharacterEvent Method
bool cGameProcess::PostCharacterEvent(char category, long memberIdx, long serverIdx, long characterIdx, char* message)
{
	cSender* sender = NULL;
	SENDER_CHECK( sender );
	return sender->PostCharacterEvent( category, memberIdx, serverIdx, characterIdx, message );
}

// PostMoneyEvent Method
bool cGameProcess::PostMoneyEvent(long characterIdx, long before, long after, LPCTSTR format, ...)
{
	cSender* sender   = NULL;
	bool     retvalue = false;
	SENDER_CHECK( sender );

	LPVOID  msgBuf = NULL;
	DWORD   bufferLength;

	va_list args;

	va_start( args, format );

	bufferLength = _vscprintf( format, args ) + 1;
	msgBuf       = malloc( bufferLength );

	vsprintf( (char*)msgBuf, format, args );

	va_end( args );

	if ( msgBuf != NULL )
	{
		retvalue = sender->PostMoneyEvent( mServerNum, characterIdx, before, after, (char*)msgBuf );
		free( msgBuf );
	}

	return retvalue;
}

// PostDepositEvent Method
bool cGameProcess::PostDepositEvent(long characterIdx, long before, long after, char* message)
{
	cSender* sender = NULL;
	SENDER_CHECK( sender );
	return sender->PostDepositEvent( mServerNum, characterIdx, before, after, message );
}

// PostInventoryEvent Method
bool cGameProcess::PostInventoryEvent(char category, long characterIdx, TB_INVENTORY* inventory, char* message)
{
	cSender* sender = NULL;
	SENDER_CHECK( sender );
	return sender->PostInventoryEvent( mServerNum,
									   category,
									   inventory->idx,
									   characterIdx,
									   inventory->itemDefineIndex,
									   inventory->number,
									   inventory->count,
									   inventory->enhanced,
									   inventory->seal,
									   message );
}

bool cGameProcess::PostQuestEvent( char category, long characterIdx, long questIdx, char* message )
{
	cSender* sender = NULL;
	SENDER_CHECK( sender );
	return sender->PostQuestEvent( mServerNum, category, characterIdx, questIdx, message );
}

bool cGameProcess::PostGuildEvent( long characterIdx, long guildIdx, char guildPosition, char* message )
{
	cSender* sender = NULL;
	SENDER_CHECK( sender );
	return sender->PostGuildEvent( mServerNum, characterIdx, guildIdx, guildPosition, message );
}

// PostPVPStatus Method
bool cGameProcess::PostPVPStatus(BYTE status)
{
	cSender* sender = NULL;
	SENDER_CHECK( sender );
	long cid = MAKECID( (WORD)mServerNum, (WORD)mChannelNum );
	return sender->PostPVPStatus( cid, status );
}

bool cGameProcess::PostPvPNotice( unsigned char leftMinute)
{
	cSender* sender = NULL;
	SENDER_CHECK( sender );
	long cid = MAKECID( (WORD)mServerNum, (WORD)mChannelNum );
	return sender->PostPVPNotice( cid, leftMinute );
}

bool cGameProcess::PostGuildCreate( GUILD_CREATE* guildCreate, cPlayer* player )
{
	cSender* sender = NULL;
	SENDER_CHECK( sender );
	long cid = MAKECID( (WORD)mServerNum, (WORD)mChannelNum );

	/// 정보구성
	MSG_SYN_POSTGUILD_CREATE msg;
	::memset( &msg, 0, sizeof(msg) );
	msg.Category = NM_GUILD;
	msg.Protocol = NM_GUILDPOST_CREATE_SYN;
	msg.idx			= guildCreate->idx;
	msg.masterIndex = player->GetObjectID();
    msg.level		= player->GetLevel();
	msg.job			= player->GetJob();
	msg.mapnum		= player->GetMapNumber();
	msg.position	= player->GetGuildPosition();
	wcscpy( msg.name, guildCreate->name );
	wcscpy( msg.masterName, guildCreate->masterName );

	return sender->PostGuildSync( cid, (char*)&msg, sizeof(MSG_SYN_POSTGUILD_CREATE) );
}

bool cGameProcess::PostGuildDelete( long idx )
{
	cSender* sender = NULL;
	SENDER_CHECK( sender );
	long cid = MAKECID( (WORD)mServerNum, (WORD)mChannelNum );

	MSG_SYN_POSTGUILD_DELETE msg;
	::memset( &msg, 0, sizeof(MSG_SYN_POSTGUILD_DELETE) );
	msg.Category = NM_GUILD;
	msg.Protocol = NM_GUILDPOST_DELETE_SYN;
	msg.idx		 = idx;

	return sender->PostGuildSync( cid, (char*)&msg, sizeof(MSG_SYN_POSTGUILD_DELETE) );
}

bool cGameProcess::PostGuildUserAdd( long idx, cPlayer* player )
{
	cSender* sender = NULL;
	SENDER_CHECK( sender );
	long cid = MAKECID( (WORD)mServerNum, (WORD)mChannelNum );

	MSG_SYN_POSTGUILD_USERADD msg;
	::memset( &msg, 0, sizeof(MSG_SYN_POSTGUILD_USERADD) );
	msg.Category = NM_GUILD;
	msg.Protocol = NM_GUILDPOST_USERADD_SYN;
	msg.idx		 = idx;
	msg.characterIdx = player->GetObjectID();
	msg.level		= player->GetLevel();
	msg.job			= player->GetJob();
	msg.mapnum		= player->GetMapNumber();
	msg.position	= player->GetGuildPosition();
	wcscpy( msg.name, player->GetName() );

	return sender->PostGuildSync( cid, (char*)&msg, sizeof(MSG_SYN_POSTGUILD_USERADD) );
}

bool cGameProcess::PostGuildUserCut( long idx, long characterIdx )
{
	cSender* sender = NULL;
	SENDER_CHECK( sender );
	long cid = MAKECID( (WORD)mServerNum, (WORD)mChannelNum );

	MSG_SYN_POSTGUILD_USERCUT msg;
	::memset( &msg, 0, sizeof(MSG_SYN_POSTGUILD_USERCUT) );
	msg.Category = NM_GUILD;
	msg.Protocol = NM_GUILDPOST_USERCUT_SYN;
	msg.idx			 = idx;
	msg.characterIdx = characterIdx;

	return sender->PostGuildSync( cid, (char*)&msg, sizeof(MSG_SYN_POSTGUILD_USERCUT) );
}

bool cGameProcess::PostGuildUserOut( long idx, long characterIdx )
{
	cSender* sender = NULL;
	SENDER_CHECK( sender );
	long cid = MAKECID( (WORD)mServerNum, (WORD)mChannelNum );

	MSG_SYN_POSTGUILD_USEROUT msg;
	::memset( &msg, 0, sizeof(MSG_SYN_POSTGUILD_USEROUT) );
	msg.Category = NM_GUILD;
	msg.Protocol = NM_GUILDPOST_USEROUT_SYN;
	msg.idx			 = idx;
	msg.characterIdx = characterIdx;

	return sender->PostGuildSync( cid, (char*)&msg, sizeof(MSG_SYN_POSTGUILD_USEROUT) );
}

bool cGameProcess::PostGuildGivePosition( long idx, long characterIdx, char position )
{
	cSender* sender = NULL;
	SENDER_CHECK( sender );
	long cid = MAKECID( (WORD)mServerNum, (WORD)mChannelNum );

	MSG_SYN_POSTGUILD_GIVEPOSITION msg;
	::memset( &msg, 0, sizeof(MSG_SYN_POSTGUILD_GIVEPOSITION) );
	msg.Category = NM_GUILD;
	msg.Protocol = NM_GUILDPOST_GIVEPOSITION_SYN;
	msg.idx		 = idx;
	msg.characterIdx = characterIdx;
	msg.position = position;

	return sender->PostGuildSync( cid, (char*)&msg, sizeof(MSG_SYN_POSTGUILD_GIVEPOSITION) );
}

bool cGameProcess::PostGuildUpdateMark( long idx, long markIdx, BYTE* mark, int size )
{
	cSender* sender = NULL;
	SENDER_CHECK( sender );
	long cid = MAKECID( (WORD)mServerNum, (WORD)mChannelNum );

	MSG_SYN_POSTGUILD_UPDATEMARK msg;
	::memset( &msg, 0, sizeof(MSG_SYN_POSTGUILD_UPDATEMARK) );
	msg.Category = NM_GUILD;
	msg.Protocol = NM_GUILDPOST_UPDATEMARK_SYN;
	msg.idx		  = idx;
	msg.markIndex = markIdx;
	memcpy( &msg.mark, mark, size );

	return sender->PostGuildSync( cid, (char*)&msg, sizeof(MSG_SYN_POSTGUILD_UPDATEMARK) );
}

bool cGameProcess::PostGuildNotice( long idx, wchar_t* notice )
{
	cSender* sender = NULL;
	SENDER_CHECK( sender );
	long cid = MAKECID( (WORD)mServerNum, (WORD)mChannelNum );

	MSG_SYN_POSTGUILD_NOTICE msg;
	::memset( &msg, 0, sizeof(MSG_SYN_POSTGUILD_NOTICE) );
	msg.Category = NM_GUILD;
	msg.Protocol = NM_GUILDPOST_NOTICE_SYN;
	msg.idx		 = idx;
	wcscpy( msg.notice, notice );

	return sender->PostGuildSync( cid, (char*)&msg, msg.GetMsgLength() );
}

bool cGameProcess::PostGuildChat( long idx, wchar_t* name, wchar_t* message, sInventory* inventory )
{
	cSender* sender = NULL;
	SENDER_CHECK( sender );
	long cid = MAKECID( (WORD)mServerNum, (WORD)mChannelNum );

	MSG_SYN_POSTGUILD_CHAT msg;
	::memset( &msg, 0, sizeof(MSG_SYN_POSTGUILD_CHAT) );
	msg.Category = NM_GUILD;
	msg.Protocol = NM_GUILDPOST_CHAT_SYN;
	msg.idx		 = idx;
	wcscpy( msg.Name, name );
	wcscpy( msg.Message, message );
    memcpy( &msg.inventory, inventory, sizeof(sInventory) );

	return sender->PostGuildSync( cid, (char*)&msg, msg.GetMsgLength() );
}

bool cGameProcess::PostGuildUpdateChannel( long idx, long characterIdx, bool connect )
{
	cSender* sender = NULL;
	SENDER_CHECK( sender );
	long cid = MAKECID( (WORD)mServerNum, (WORD)mChannelNum );

	MSG_SYN_POSTGUILD_CHANNEL msg;
	::memset( &msg, 0, sizeof(MSG_SYN_POSTGUILD_CHANNEL) );
	msg.Category = NM_GUILD;
	msg.Protocol = NM_GUILDPOST_UPDATECHANNEL_SYN;
	msg.idx		 = idx;
	msg.characterIdx = characterIdx;
	msg.channelNum = ( connect == true ) ? GetChannelNum() : 0;

	return sender->PostGuildSync( cid, (char*)&msg, sizeof(MSG_SYN_POSTGUILD_CHANNEL) );
}

bool cGameProcess::PostGuildUpdateInfo( long idx, long characterIdx, char level, long job )
{
	cSender* sender = NULL;
	SENDER_CHECK( sender );
	long cid = MAKECID( (WORD)mServerNum, (WORD)mChannelNum );

	MSG_SYN_POSTGUILD_UPDATE msg;
	::memset( &msg, 0, sizeof(MSG_SYN_POSTGUILD_UPDATE) );
	msg.Category = NM_GUILD;
	msg.Protocol = NM_GUILDPOST_UPDATEINFO_SYN;
	msg.idx		 = idx;
	msg.characterIdx = characterIdx;
	msg.level		 = level;
	msg.job			 = job;
	
	return sender->PostGuildSync( cid, (char*)&msg, sizeof(MSG_SYN_POSTGUILD_UPDATE) );
}

bool cGameProcess::PostGuildNote( GUILDNOTE_INSERT* guildNoteInsert )
{
	cSender* sender = NULL;
	SENDER_CHECK( sender );
	long cid = MAKECID( (WORD)mServerNum, (WORD)mChannelNum );

	PerIoContext* perIoContext = GetIoContext( IOCP_REQUEST_WRITE );
	MSG_SYN_POSTGUILD_NOTESEND* sendMsg = (MSG_SYN_POSTGUILD_NOTESEND*)perIoContext->buffer;
	if( sendMsg != NULL )
	{
		long length = sizeof(MSG_SYN_POSTGUILD_NOTESEND) - sizeof(sendMsg->table);

		sendMsg->Category = NM_GUILD;
		sendMsg->Protocol = NM_GUILDPOST_NOTE_SYN;
		sendMsg->rowCount = guildNoteInsert->rowCount;

		// sNote == TB_NOTE
		tbNOTE* table = sendMsg->table;
		TB_NOTE* data = guildNoteInsert->table;
		for( int i = 0; i < guildNoteInsert->rowCount; ++i, ++table, ++data )
		{
			sendMsg->characterIdx2[i] = guildNoteInsert->characterIdx2[i];
			*table = *data;
		}

		length += (sendMsg->rowCount * sizeof(sendMsg->table));
		sender->PostGuildSync( cid, (char*)sendMsg, length );

		perIoContext->offset = length;
	}
	
	ReleaseIoContext( perIoContext );
	return true;
}

/// 다른 서버에 길드 접속정보 요청
bool cGameProcess::PostGuildRequestConnect()
{
	if( mCompleteImport.guild == false )
	{
		PostServerEvent( "FAIL PostGuildRequestConnect : 길드정보가 아직 로드되지 않았습니다" );
	}

	/// Log
	PostGuildEvent( mServerNum, mChannelNum, 0, "동기화 : PostGuildRequestConnect()");

	cSender* sender = NULL;
	SENDER_CHECK( sender );
	long cid = MAKECID( (WORD)mServerNum, (WORD)mChannelNum );

	return sender->PostGuildConnectSync( cid );
}

bool cGameProcess::PostMonRegenStart( unsigned long switchGroup )
{
	cSender* sender = NULL;
	SENDER_CHECK( sender );
	long cid = MAKECID( (WORD)mServerNum, (WORD)mChannelNum );

	MSG_SYN_POSTMONSTER_SWITCHREGEN_START msg;
	::memset( &msg, 0, sizeof(MSG_SYN_POSTMONSTER_SWITCHREGEN_START) );
	msg.Category = NM_MONSTER;
	msg.Protocol = NM_MONSTERPOST_SWITCHREGEN_START_SYN;
	msg.mSwitchRegen = switchGroup;

	return sender->PostMonsterSync( cid, (char*)&msg, sizeof(MSG_SYN_POSTMONSTER_SWITCHREGEN_START) );
}

bool cGameProcess::PostMonRegenStop( unsigned long switchGroup )
{
	cSender* sender = NULL;
	SENDER_CHECK( sender );
	long cid = MAKECID( (WORD)mServerNum, (WORD)mChannelNum );

	MSG_SYN_POSTMONSTER_SWITCHREGEN_STOP msg;
	::memset( &msg, 0, sizeof(MSG_SYN_POSTMONSTER_SWITCHREGEN_STOP) );
	msg.Category = NM_MONSTER;
	msg.Protocol = NM_MONSTERPOST_SWITCHREGEN_STOP_SYN;
	msg.mSwitchRegen = switchGroup;

	return sender->PostMonsterSync( cid, (char*)&msg, sizeof(MSG_SYN_POSTMONSTER_SWITCHREGEN_STOP) );
}

/// 다른채널로부터 귓속말 받음
bool cGameProcess::PostChatWhisper( DWORD recvChannel, unsigned long senderIdx, wchar_t* senderName, wchar_t* recvName, wchar_t* message, sInventory* inven )
{
	cSender* sender = NULL;
	SENDER_CHECK( sender );
	long cid = MAKECID( (WORD)mServerNum, (WORD)mChannelNum );

	MSG_SYN_POSTCHAT_WHISPER msg;
	::memset( &msg, 0, sizeof(MSG_SYN_POSTCHAT_WHISPER) );
	msg.Category = NM_CHAT;
	msg.Protocol = NM_CHATPOST_WHISPER_SYN;
	msg.sendChannelNum	= mChannelNum;
	msg.recvChannelNum  = recvChannel;
	msg.senderIndex		= senderIdx;

	wcscpy( msg.senderName, senderName );
	wcscpy( msg.recvName, recvName );
	wcscpy( msg.message, message );

	msg.inventory = *inven;

	return sender->PostChatSync( cid, (char*)&msg, msg.GetMsgLength() );
}

/// 받은 귓속말에대한 결과를 다시 보내줌
bool cGameProcess::PostChatWhisperResult( DWORD recvChannel, int errorCode, unsigned long senderIdx, wchar_t* recvName, wchar_t* message, sInventory* inven )
{
	cSender* sender = NULL;
	SENDER_CHECK( sender );
	long cid = MAKECID( (WORD)mServerNum, (WORD)mChannelNum );

	MSG_SYN_POSTCHAT_WHISPERRESULT msg;
	::memset( &msg, 0, sizeof(MSG_SYN_POSTCHAT_WHISPERRESULT) );
	msg.Category = NM_CHAT;
	msg.Protocol = NM_CHATPOST_WHISPER_RESULTSYN;
	msg.recvChannelNum  = recvChannel;
	msg.error			= errorCode;
	msg.senderIndex		= senderIdx;

	wcscpy( msg.recvName, recvName );
	wcscpy( msg.message, message );

	msg.inventory = *inven;

	return sender->PostChatSync( cid, (char*)&msg, msg.GetMsgLength() );
}

bool cGameProcess::PostChatNote( DWORD recvChannel, wchar_t* recvName, tbNOTE* note )
{
	cSender* sender = NULL;
	SENDER_CHECK( sender );
	long cid = MAKECID( (WORD)mServerNum, (WORD)mChannelNum );
	
	MSG_SYN_POSTCHAT_NOTESEND msg;
	::memset( &msg, 0, sizeof(MSG_SYN_POSTCHAT_NOTESEND) );
	msg.Category = NM_CHAT;
	msg.Protocol = NM_CHATPOST_NOTESEND_SYN;
	msg.recvChannelNum	= recvChannel;
	msg.table			= *note;
	wcscpy( msg.recvName, recvName );

	return sender->PostChatSync( cid, (char*)&msg, sizeof(MSG_SYN_POSTCHAT_NOTESEND) );
}

// VerifyItemDefine Method
bool cGameProcess::VerifyItemDefine(void)
{
	HANDLE handle = NULL;
	return (GetSQL( &handle, SQL_GAME_PROCESS_VERIFY_ITEM_DEFINE ) != NULL) ? SendSQL( handle, sizeof(VERIFY_ITEM_TABLE) ) : false;
}

// VerifyItemAbility Method
bool cGameProcess::VerifyItemAbility(void)
{
	HANDLE handle = NULL;
	return (GetSQL( &handle, SQL_GAME_PROCESS_VERIFY_ITEM_ABILITY ) != NULL) ? SendSQL( handle, sizeof(VERIFY_ITEM_TABLE) ) : false;
}

// VerifyItemCooltime2 Method
bool cGameProcess::VerifyItemCooltime2(void)
{
	HANDLE handle = NULL;
	return (GetSQL( &handle, SQL_GAME_PROCESS_VERIFY_ITEM_COOLTIME2 ) != NULL) ? SendSQL( handle, sizeof(VERIFY_ITEM_TABLE) ) : false;
}

// VerifyItemLimit Method
bool cGameProcess::VerifyItemLimit(void)
{
	HANDLE handle = NULL;
	return (GetSQL( &handle, SQL_GAME_PROCESS_VERIFY_ITEM_LIMIT ) != NULL) ? SendSQL( handle, sizeof(VERIFY_ITEM_TABLE) ) : false;
}

// VerifyItemCard Method
bool cGameProcess::VerifyItemCard(void)
{
	HANDLE handle = NULL;
	return (GetSQL( &handle, SQL_GAME_PROCESS_VERIFY_ITEM_CARD ) != NULL) ? SendSQL( handle, sizeof(VERIFY_ITEM_TABLE) ) : false;
}

// VerifyItemCard Method
bool cGameProcess::VerifyItemCardSlot(void)
{
	HANDLE handle = NULL;
	return (GetSQL( &handle, SQL_GAME_PROCESS_VERIFY_ITEM_CARD_SLOT ) != NULL) ? SendSQL( handle, sizeof(VERIFY_ITEM_TABLE) ) : false;
}

// VerifyItemTarot Method
bool cGameProcess::VerifyItemTarot(void)
{
	HANDLE handle = NULL;
	return (GetSQL( &handle, SQL_GAME_PROCESS_VERIFY_ITEM_TAROT ) != NULL) ? SendSQL( handle, sizeof(VERIFY_ITEM_TABLE) ) : false;
}

// VerifyItemEnhanced Method
bool cGameProcess::VerifyItemEnhanced(void)
{
	HANDLE handle = NULL;
	return (GetSQL( &handle, SQL_GAME_PROCESS_VERIFY_ITEM_ENHANCED ) != NULL) ? SendSQL( handle, sizeof(VERIFY_ITEM_TABLE) ) : false;
}

// VerifyItemEnhancedRate Method
bool cGameProcess::VerifyItemEnhancedRate(void)
{
	HANDLE handle = NULL;
	return (GetSQL( &handle, SQL_GAME_PROCESS_VERIFY_ITEM_ENHANCEDRATE ) != NULL) ? SendSQL( handle, sizeof(VERIFY_ITEM_TABLE) ) : false;
}

// VerifyItemDisjoint Method
bool cGameProcess::VerifyItemDisjoint(void)
{
	HANDLE handle = NULL;
	return (GetSQL( &handle, SQL_GAME_PROCESS_VERIFY_ITEM_DISJOINT ) != NULL) ? SendSQL( handle, sizeof(VERIFY_ITEM_TABLE) ) : false;
}

// VerifyItemChange Method
bool cGameProcess::VerifyItemChange(void)
{
	HANDLE handle = NULL;
	return (GetSQL( &handle, SQL_GAME_PROCESS_VERIFY_ITEM_CHANGE ) != NULL) ? SendSQL( handle, sizeof(VERIFY_ITEM_TABLE) ) : false;
}

// ItemDefine Method
bool cGameProcess::ItemDefine(long idx)
{
	HANDLE             handle          = NULL;
	ITEM_DEFINE_TABLE* itemDefineTable = (ITEM_DEFINE_TABLE*)GetSQL( &handle, SQL_GAME_PROCESS_ITEM_DEFINE );

	itemDefineTable->idx = idx;

	return SendSQL( handle, sizeof(ITEM_DEFINE_TABLE) );
}

// ItemAbility Method
bool cGameProcess::ItemAbility(long idx)
{
	HANDLE              handle           = NULL;
	ITEM_ABILITY_TABLE* itemAbilityTable = (ITEM_ABILITY_TABLE*)GetSQL( &handle, SQL_GAME_PROCESS_ITEM_ABILITY );

	itemAbilityTable->idx = idx;

	return SendSQL( handle, sizeof(ITEM_ABILITY_TABLE) );
}

// ItemCooltime2 Method
bool cGameProcess::ItemCooltime2(long idx)
{
	HANDLE                handle             = NULL;
	ITEM_COOLTIME2_TABLE* itemCooltime2Table = (ITEM_COOLTIME2_TABLE*)GetSQL( &handle, SQL_GAME_PROCESS_ITEM_COOLTIME2 );
	long                  length             = sizeof(ITEM_COOLTIME2_TABLE) - sizeof(itemCooltime2Table->table);

	itemCooltime2Table->idx = idx;

	return SendSQL( handle, length );
}

// ItemLimit Method
bool cGameProcess::ItemLimit(long idx)
{
	HANDLE            handle         = NULL;
	ITEM_LIMIT_TABLE* itemLimitTable = (ITEM_LIMIT_TABLE*)GetSQL( &handle, SQL_GAME_PROCESS_ITEM_LIMIT );

	itemLimitTable->idx = idx;

	return SendSQL( handle, sizeof(ITEM_LIMIT_TABLE) );
}

// ItemCard Method
bool cGameProcess::ItemCard(long idx)
{
	HANDLE           handle        = NULL;
	ITEM_CARD_TABLE* itemCardTable = (ITEM_CARD_TABLE*)GetSQL( &handle, SQL_GAME_PROCESS_ITEM_CARD );

	itemCardTable->idx = idx;

	return SendSQL( handle, sizeof(ITEM_CARD_TABLE) );
}

// ItemCardSlot Method
bool cGameProcess::ItemCardSlot(long idx)
{
	HANDLE                handle            = NULL;
	ITEM_CARD_SLOT_TABLE* itemCardSlotTable = (ITEM_CARD_SLOT_TABLE*)GetSQL( &handle, SQL_GAME_PROCESS_ITEM_CARD_SLOT );

	itemCardSlotTable->idx = idx;

	return SendSQL( handle, sizeof(ITEM_CARD_SLOT_TABLE) );
}

// ItemTarot Method
bool cGameProcess::ItemTarot(long idx)
{
	HANDLE            handle         = NULL;
	ITEM_TAROT_TABLE* itemTarotTable = (ITEM_TAROT_TABLE*)GetSQL( &handle, SQL_GAME_PROCESS_ITEM_TAROT );

	itemTarotTable->idx = idx;

	return SendSQL( handle, sizeof(ITEM_TAROT_TABLE) );
}

// ItemEnhanced Method
bool cGameProcess::ItemEnhanced(long idx)
{
	HANDLE               handle            = NULL;
	ITEM_ENHANCED_TABLE* itemEnhancedTable = (ITEM_ENHANCED_TABLE*)GetSQL( &handle, SQL_GAME_PROCESS_ITEM_ENHANCED );

	itemEnhancedTable->idx = idx;

	return SendSQL( handle, sizeof(ITEM_ENHANCED_TABLE) );
}

// ItemEnhancedRate Method
bool cGameProcess::ItemEnhancedRate(long idx)
{
	HANDLE                    handle                = NULL;
	ITEM_ENHANCED_RATE_TABLE* itemEnhancedRateTable = (ITEM_ENHANCED_RATE_TABLE*)GetSQL( &handle, SQL_GAME_PROCESS_ITEM_ENHANCEDRATE );

	itemEnhancedRateTable->idx = idx;

	return SendSQL( handle, sizeof(ITEM_ENHANCED_RATE_TABLE) );
}

// ItemDisjoint Method
bool cGameProcess::ItemDisjoint(long idx)
{
	HANDLE               handle            = NULL;
	ITEM_DISJOINT_TABLE* itemDisjointTable = (ITEM_DISJOINT_TABLE*)GetSQL( &handle, SQL_GAME_PROCESS_ITEM_DISJOINT );

	itemDisjointTable->idx = idx;

	return SendSQL( handle, sizeof(ITEM_DISJOINT_TABLE) );
}

// ItemChange Method
bool cGameProcess::ItemChange(long idx)
{
	HANDLE             handle          = NULL;
	ITEM_CHANGE_TABLE* itemChangeTable = (ITEM_CHANGE_TABLE*)GetSQL( &handle, SQL_GAME_PROCESS_ITEM_CHANGE );

	itemChangeTable->idx = idx;

	return SendSQL( handle, sizeof(ITEM_CHANGE_TABLE) );
}

// VerifyDefaultItems Method
bool cGameProcess::VerifyDefaultItems(void)
{
	HANDLE handle = NULL;
	return (GetSQL( &handle, SQL_GAME_PROCESS_VERIFY_DEFAULT_ITEMS ) != NULL) ? SendSQL( handle, sizeof(VERIFY_DEFAULT_ITEMS) ) : false;
}

bool cGameProcess::VerifyGuild(void)
{
	HANDLE handle = NULL;
	return (GetSQL( &handle, SQL_GAME_PROCESS_VERIFY_GUILD ) != NULL ) ? SendSQL( handle, sizeof(VERIFY_GUILD) ) : false;
}

bool cGameProcess::GuildSelect( long idx )
{
	HANDLE handle = NULL;
	GUILD_SELECT* guildSelect = (GUILD_SELECT*)GetSQL( &handle, SQL_GAME_PROCESS_GUILD_SELECT );

	guildSelect->idx = idx;

	return SendSQL( handle, sizeof(GUILD_SELECT) );
}

bool cGameProcess::GuildUserSelect( long idx )
{
	HANDLE			  handle          = NULL;
	GUILDUSER_SELECT* guildUserSelect = (GUILDUSER_SELECT*)GetSQL( &handle, SQL_GAME_PROCESS_GUILDUSER_SELECT );
	unsigned long	  length		  = sizeof(GUILDUSER_SELECT)-sizeof(guildUserSelect->table);

	guildUserSelect->idx = idx;

	return SendSQL( handle, length );
}

// ChannelCheck Method
bool cGameProcess::ChannelCheck(short serverNum, short channelNum, char* inetnum, unsigned long ipv4, unsigned short port)
{
	HANDLE         handle = NULL;
	CHANNEL_CHECK* channelCheck = (CHANNEL_CHECK*)GetSQL( &handle, SQL_GAME_PROCESS_CHANNEL_CHECK );

	channelCheck->serverNum  = serverNum;
	channelCheck->channelNum = channelNum;
	strcpy( channelCheck->inetnum, inetnum );
	channelCheck->ipv4       = ipv4;
	channelCheck->port       = port;
	channelCheck->retvalue   = 0;

	return SendSQL( handle, sizeof(CHANNEL_CHECK) );
}

// InitGameDB Method
bool cGameProcess::InitGameDB( )
{
	HANDLE     handle   = NULL;
	INIT_GAME* initGame = (INIT_GAME*)GetSQL( &handle, SQL_REQUEST_GAME_INIT );

	initGame->channelNum = mChannelNum;

	return SendSQL( handle, sizeof(INIT_GAME) );
}

// ShutdownGameDB Method
bool cGameProcess::ShutdownGameDB( )
{
	HANDLE         handle       = NULL;
	SHUTDOWN_GAME* shutdownGame = (SHUTDOWN_GAME*)GetSQL( &handle, SQL_REQUEST_GAME_SHUTDOWN );

	shutdownGame->channelNum = mChannelNum;

	return SendSQL( handle, sizeof(SHUTDOWN_GAME) );
}

// ShutdownAccountDB Method
bool cGameProcess::ShutdownAccountDB( )
{
	HANDLE            handle          = NULL;
	SHUTDOWN_ACCOUNT* shutdownAccount = (SHUTDOWN_ACCOUNT*)GetSQL( &handle, SQL_REQUEST_ACCOUNT_SHUTDOWN );

	shutdownAccount->serverNum  = (short)mServerNum;
	shutdownAccount->channelNum = (short)mChannelNum;

	return SendSQL( handle, sizeof(SHUTDOWN_ACCOUNT) );
}

// CharacterList Method
bool cGameProcess::CharacterList(PerSocketContext* perSocketContext, long memberIdx)
{
	HANDLE          handle        = NULL;
	CHARACTER_LIST* characterList = (CHARACTER_LIST*)GetSQL( &handle, SQL_GAME_PROCESS_CHARACTER_LIST );
	unsigned long   length        = sizeof(CHARACTER_LIST) - sizeof(characterList->table);

	characterList->userIdx = memberIdx;

	return SendSQL( perSocketContext, handle, length );
}

// InventorySelect Method
bool cGameProcess::InventorySelect(PerSocketContext* perSocketContext, long characterIdx)
{
	HANDLE            handle          = NULL;
	INVENTORY_SELECT* inventorySelect = (INVENTORY_SELECT*)GetSQL( &handle, SQL_GAME_PROCESS_INVENTORY_SELECT );
	unsigned long     length          = sizeof(INVENTORY_SELECT) - sizeof(inventorySelect->table);

	inventorySelect->characterIdx = characterIdx;

	return SendSQL( perSocketContext, handle, length );
}

// InventoryDelete Method
bool cGameProcess::InventoryDelete(PerSocketContext* perSocketContext, INVENTORY_WARNING warning, long characterIdx, long inventoryIdx)
{
	HANDLE            handle          = NULL;
	INVENTORY_DELETE* inventoryDelete = (INVENTORY_DELETE*)GetSQL( &handle, SQL_GAME_PROCESS_INVENTORY_DELETE );

	inventoryDelete->warning      = warning;
	inventoryDelete->characterIdx = characterIdx;
	inventoryDelete->inventoryIdx = inventoryIdx;

	return SendSQL( perSocketContext, handle, sizeof(INVENTORY_DELETE) );
}

// InventoryCooltimeSelect Method
bool cGameProcess::InventoryCooltimeSelect(PerSocketContext* perSocketContext, long characterIdx)
{
	HANDLE                     handle                  = NULL;
	INVENTORY_COOLTIME_SELECT* inventoryCooltimeSelect = (INVENTORY_COOLTIME_SELECT*)GetSQL( &handle, SQL_GAME_PROCESS_INVENTORY_COOLTIME_SELECT );
	unsigned long              length                  = sizeof(INVENTORY_COOLTIME_SELECT) - sizeof(inventoryCooltimeSelect->table);

	inventoryCooltimeSelect->characterIdx = characterIdx;

	return SendSQL( perSocketContext, handle, length );
}

// InventorySwap Method
bool cGameProcess::InventorySwap(PerSocketContext* perSocketContext, long idx1, short number1, long idx2, short number2)
{
	HANDLE          handle        = NULL;
	INVENTORY_SWAP* inventorySwap = (INVENTORY_SWAP*)GetSQL( &handle, SQL_GAME_PROCESS_INVENTORY_SWAP );

	inventorySwap->idx1    = idx1;
	inventorySwap->number1 = number1;
	inventorySwap->idx2    = idx2;
	inventorySwap->number2 = number2;

	return SendSQL( perSocketContext, handle, sizeof(INVENTORY_SWAP) );
}

// InventoryMove Method
bool cGameProcess::InventoryMove(PerSocketContext* perSocketContext, long characterIdx, long idx, short number)
{
	HANDLE          handle        = NULL;
	INVENTORY_MOVE* inventoryMove = (INVENTORY_MOVE*)GetSQL( &handle, SQL_GAME_PROCESS_INVENTORY_MOVE );

	inventoryMove->characterIdx = characterIdx;
	inventoryMove->idx          = idx;
	inventoryMove->number       = number;

	return SendSQL( perSocketContext, handle, sizeof(INVENTORY_MOVE) );
}

// InventoryExcept Method
bool cGameProcess::InventoryExcept(PerSocketContext* perSocketContext, long characterIdx, long idx, short number)
{
	HANDLE            handle          = NULL;
	INVENTORY_EXCEPT* inventoryExcept = (INVENTORY_EXCEPT*)GetSQL( &handle, SQL_GAME_PROCESS_INVENTORY_EXCEPT );

	inventoryExcept->characterIdx = characterIdx;
	inventoryExcept->idx          = idx;
	inventoryExcept->number       = number;

	return SendSQL( perSocketContext, handle, sizeof(INVENTORY_EXCEPT) );
}

// InventoryMerge Method
bool cGameProcess::InventoryMerge(PerSocketContext* perSocketContext, long idx1, long idx2)
{
	HANDLE           handle         = NULL;
	INVENTORY_MERGE* inventoryMerge = (INVENTORY_MERGE*)GetSQL( &handle, SQL_GAME_PROCESS_INVENTORY_MERGE );
	unsigned long    length         = sizeof(INVENTORY_MERGE) - sizeof(inventoryMerge->table);

	inventoryMerge->idx1 = idx1;
	inventoryMerge->idx2 = idx2;

	return SendSQL( perSocketContext, handle, length );
}

// NoteSelect Method
bool cGameProcess::NoteSelect(PerSocketContext* perSocketContext, long characterIdx)
{
	HANDLE        handle     = NULL;
	NOTE_SELECT*  noteSelect = (NOTE_SELECT*)GetSQL( &handle, SQL_GAME_PROCESS_NOTE_SELECT );
	unsigned long length     = sizeof(NOTE_SELECT) - sizeof(noteSelect->table);

	noteSelect->characterIdx = characterIdx;

	return SendSQL( perSocketContext, handle, length );
}

// NoteInsert Method
bool cGameProcess::NoteInsert(PerSocketContext* perSocketContext, long characterIdx1, wchar_t* characterName2, wchar_t* message)
{
	HANDLE       handle     = NULL;
	NOTE_INSERT* noteInsert = (NOTE_INSERT*)GetSQL( &handle, SQL_GAME_PROCESS_NOTE_INSERT );

	noteInsert->characterIdx1 = characterIdx1;
	wcscpy( noteInsert->characterName2, characterName2 );
	wcscpy( noteInsert->message, message );

	return SendSQL( perSocketContext, handle, sizeof(NOTE_INSERT) );
}

// NoteDelete Method
bool cGameProcess::NoteDelete(PerSocketContext* perSocketContext, long characterIdx, long rowCount, long idx[1])
{
	HANDLE        handle     = NULL;
	NOTE_DELETE*  noteDelete = (NOTE_DELETE*)GetSQL( &handle, SQL_GAME_PROCESS_NOTE_DELETE );
	unsigned long length     = sizeof(NOTE_DELETE) - sizeof(noteDelete->idx);

	noteDelete->characterIdx = characterIdx;
	noteDelete->rowCount     = rowCount;
	for ( long i = 0; i < noteDelete->rowCount; i++ )
	{
		noteDelete->idx[ i ] = idx[ i ];
	}

	length += noteDelete->rowCount * sizeof(noteDelete->idx);
	return SendSQL( perSocketContext, handle, length );
}

// GuildNoteInsert Method
bool cGameProcess::GuildNoteInsert( PerSocketContext* perSocketContext, long characterIdx1, wchar_t* message, long rowCount, long* characterIdx2 )
{
	HANDLE					handle			= NULL;
	GUILDNOTE_INSERT*		guildNoteInsert = (GUILDNOTE_INSERT*)GetSQL( &handle, SQL_GAME_PROCESS_GUILDNOTE_INSERT );

	guildNoteInsert->characterIdx1  = characterIdx1;
	wcscpy( guildNoteInsert->message, message );

	guildNoteInsert->rowCount		= rowCount;
	for( long i = 0; i < guildNoteInsert->rowCount; i++, characterIdx2++ )
	{
		guildNoteInsert->characterIdx2[ i ] = *characterIdx2;
	}
	
	return SendSQL( perSocketContext, handle, sizeof(GUILDNOTE_INSERT) );
}

// FriendSelect Method
bool cGameProcess::FriendSelect(PerSocketContext* perSocketContext, long characterIdx)
{
	HANDLE         handle       = NULL;
	FRIEND_SELECT* friendSelect = (FRIEND_SELECT*)GetSQL( &handle, SQL_GAME_PROCESS_FRIEND_SELECT );
	unsigned long  length       = sizeof(FRIEND_SELECT) - sizeof(friendSelect->table);

	friendSelect->characterIdx = characterIdx;

	return SendSQL( perSocketContext, handle, length );
}

// ItemTarotResult Method
bool cGameProcess::ItemTarotResult(PerSocketContext* perSocketContext, long itemDefineIndex, long value)
{
	HANDLE             handle          = NULL;
	ITEM_TAROT_RESULT* itemTarotResult = (ITEM_TAROT_RESULT*)GetSQL( &handle, SQL_GAME_PROCESS_ITEM_TAROT_RESULT );

	itemTarotResult->itemDefineIndex = itemDefineIndex;
	itemTarotResult->value           = value;

	return SendSQL( perSocketContext, handle, sizeof(ITEM_TAROT_RESULT) );
}

// SkillSelect Method
bool cGameProcess::SkillSelect( PerSocketContext* perSocketContext, unsigned long characterIdx )
{
	HANDLE        handle      = NULL;
	SKILL_SELECT* skillSelect = (SKILL_SELECT*)GetSQL( &handle, SQL_GAME_PROCESS_SKILL_SELECT );
	unsigned long length      = sizeof(SKILL_SELECT) - sizeof(skillSelect->mTable);

	skillSelect->mCharacterIdx = characterIdx;

	return SendSQL( perSocketContext, handle, length );
}

// SkillInsert Method
bool cGameProcess::SkillInsert( PerSocketContext* perSocketContext, unsigned long characterIdx, unsigned long skillClassIdx, unsigned long money, unsigned short sp, unsigned char skillLevel, unsigned long skillPoint )
{
	HANDLE        handle      = NULL;
	SKILL_INSERT* skillInsert = (SKILL_INSERT*)GetSQL( &handle, SQL_GAME_PROCESS_SKILL_INSERT );

	skillInsert->mCharacterIdx  = characterIdx;
	skillInsert->mSkillClassIdx = skillClassIdx;
	skillInsert->mMoney         = money;
	skillInsert->mSP            = sp;
	skillInsert->skillLevel		= skillLevel;
	skillInsert->skillPoint		= skillPoint;
	skillInsert->mRetvalue		= -1;

	return SendSQL( perSocketContext, handle, sizeof(SKILL_INSERT) );
}

// SkillUpdate Method
bool cGameProcess::SkillUpdate( PerSocketContext* perSocketContext, unsigned long characterIdx, unsigned long skillClassIdx, unsigned char skillStep, unsigned long money, unsigned short sp, unsigned char skillLevel, unsigned long skillPoint )
{
	HANDLE        handle      = NULL;
	SKILL_UPDATE* skillUpdate = (SKILL_UPDATE*)GetSQL( &handle, SQL_GAME_PROCESS_SKILL_UPDATE );

	skillUpdate->mCharacterIdx  = characterIdx;
	skillUpdate->mSkillClassIdx = skillClassIdx;
	skillUpdate->mSkillStep     = skillStep;
	skillUpdate->mMoney         = money;
	skillUpdate->mSP            = sp;
	skillUpdate->skillLevel		= skillLevel;
	skillUpdate->skillPoint		= skillPoint;
	skillUpdate->mRetvalue		= -1;

	return SendSQL( perSocketContext, handle, sizeof(SKILL_UPDATE) );
}

// SkillCoolTime Method
bool cGameProcess::SkillCoolTime( PerSocketContext* perSocketContext, unsigned long characterIdx )
{
	HANDLE          handle        = NULL;
	SKILL_COOLTIME* skillCoolTime = (SKILL_COOLTIME*)GetSQL( &handle, SQL_GAME_PROCESS_SKILL_COOLTIME );
	unsigned long   length        = sizeof(SKILL_COOLTIME) - sizeof(skillCoolTime->mTable);

	if ( !SKILLMANAGER->SaveSkillCoolTime( characterIdx, skillCoolTime ) )
	{
		length += skillCoolTime->mRowCount * sizeof(TB_CHARACTER_SKILL);
		// 저장 할 쿨타임이 없는 경우	
		ReleaseSQL( handle, length );
		return true;
	}

	length += skillCoolTime->mRowCount * sizeof(TB_CHARACTER_SKILL);
	return SendSQL( perSocketContext, handle, length );
}

// RecipeTime Method
bool cGameProcess::RecipeCoolTime( PerSocketContext* perSocketContext, cPlayer* hero )
{
	HANDLE          handle        = NULL;
	RECIPE_COOLTIME* pCoolTime = (RECIPE_COOLTIME*)GetSQL( &handle, SQL_GAME_PROCESS_RECIPE_COOLTIME );
	unsigned long   length        = sizeof(RECIPE_COOLTIME) - sizeof(pCoolTime->mTable);
	
	if ( !hero->SaveRecipeCoolTime( pCoolTime ) )
	{
		length += pCoolTime->mRowCount * sizeof(TB_RECIPE_COOLTIME);
		// 저장 할 쿨타임이 없는 경우	
		ReleaseSQL( handle, length );
		return true;
	}

	length += pCoolTime->mRowCount * sizeof(TB_RECIPE_COOLTIME);
	return SendSQL( perSocketContext, handle, length );
}

// SkillInfluenceSelect Method
bool cGameProcess::SkillInfluenceSelect( PerSocketContext* perSocketContext, unsigned long characterIdx )
{
	HANDLE                  handle          = NULL;
	SKILL_INFLUENCE_SELECT* influenceSelect = (SKILL_INFLUENCE_SELECT*)GetSQL( &handle, SQL_GAME_PROCESS_INFLUENCE_SELECT );
	unsigned long           length          = sizeof(SKILL_INFLUENCE_SELECT) - sizeof(influenceSelect->mTable);

	influenceSelect->mCharacterIdx = characterIdx;

	return SendSQL( perSocketContext, handle, length );
}

// SkillInfluenceDelete Method
bool cGameProcess::SkillInfluenceDelete( PerSocketContext* perSocketContext, unsigned long characterIdx )
{
	HANDLE                  handle          = NULL;
	SKILL_INFLUENCE_DELETE* influenceDelete = (SKILL_INFLUENCE_DELETE*)GetSQL( &handle, SQL_GAME_PROCESS_INFLUENCE_DELETE );

	influenceDelete->mCharacterIdx = characterIdx;

	return SendSQL( perSocketContext, handle, sizeof(SKILL_INFLUENCE_DELETE) );
}

// SkillInfluenceInsert Method
bool cGameProcess::SkillInfluenceInsert( PerSocketContext* perSocketContext, unsigned long characterIdx )
{
	HANDLE                  handle          = NULL;
	SKILL_INFLUENCE_INSERT* influenceInsert = (SKILL_INFLUENCE_INSERT*)GetSQL( &handle, SQL_GAME_PROCESS_INFLUENCE_INSERT );
	unsigned long           length          = sizeof(SKILL_INFLUENCE_INSERT) - sizeof(influenceInsert->mTable);

	if ( !SKILLMANAGER->SavePlayerInfluence( characterIdx, influenceInsert ) )
	{
		length += influenceInsert->mRowCount * sizeof(TB_CHARACTER_INFLUENCE);
		// 저장 효과가 없는 경우	
		ReleaseSQL( handle, length );
		return true;
	}

	length += influenceInsert->mRowCount * sizeof(TB_CHARACTER_INFLUENCE);
	return SendSQL( perSocketContext, handle, length );
}

// MakeSkillSelect Method
bool cGameProcess::MakeSkillSelect( PerSocketContext* perSocketContext, unsigned long characterIdx )
{
	HANDLE handle = NULL;
	MAKESKILL_SELECT* pMakeSkill= (MAKESKILL_SELECT*)GetSQL( &handle, SQL_GAME_PROCESS_MAKESKILL_SELECT );
	unsigned long length = sizeof(MAKESKILL_SELECT) - sizeof(pMakeSkill->mTable);

	pMakeSkill->mCharacterIdx = characterIdx;

	return SendSQL( perSocketContext, handle, length );
}

bool cGameProcess::FortuneSelect( PerSocketContext* perSocketContext, unsigned long characterIdx )
{
	HANDLE				handle = NULL;
	FORTUNE_SELECT*		fortuneSelect = (FORTUNE_SELECT*)GetSQL( &handle, SQL_GAME_PROCESS_FORTUNE_SELECT );

	fortuneSelect->characterIdx = characterIdx;

	/// 초기화
	TIMESTAMP_STRUCT* thru = fortuneSelect->thru;
	for( int i = 0; i < 5; ++i, ++thru )
	{
		thru->year		= 1900;
		thru->month		= 1;
		thru->day		= 1;
		thru->hour		= 0;
		thru->minute	= 0;
		thru->second	= 0;
		thru->fraction  = 0;
	}

	return SendSQL( perSocketContext, handle, sizeof(FORTUNE_SELECT) );
}

bool cGameProcess::WhisperMemberSelect( PerSocketContext* perSocketContext, wchar_t* recvName, wchar_t* message, short slotIndex )
{
	HANDLE					handle = NULL;
	WHISPERMEMBER_SELECT*	memberSelect = (WHISPERMEMBER_SELECT*)GetSQL( &handle, SQL_GAME_PROCESS_WHISPER_MEMBER_SELECT );
	
	memberSelect->channelNum = 0;
	wcscpy( memberSelect->recvName, recvName );
	wcscpy( memberSelect->message, message );
	memberSelect->slotIndex = slotIndex;

	return SendSQL( perSocketContext, handle, sizeof(WHISPERMEMBER_SELECT) );
}

bool cGameProcess::NoteMemberSelect( PerSocketContext* perSocketContext, wchar_t* recvName, TB_NOTE* note )
{
	HANDLE				handle = NULL;
	NOTEMEMBER_SELECT*	memberSelect = (NOTEMEMBER_SELECT*)GetSQL( &handle, SQL_GAME_PROCESS_NOTE_MEMBER_SELECT );

	// TB_NOTE == tbNOTE
	memberSelect->table = *note;
	wcscpy( memberSelect->recvName, recvName );
	memberSelect->channelNum = 0;

	return SendSQL( perSocketContext, handle, sizeof(NOTEMEMBER_SELECT) );
}

// StallSellDelete Method
bool cGameProcess::ShortcutSelect(PerSocketContext* perSocketContext, unsigned long characterIdx)
{
	HANDLE           handle         = NULL;
	SHORTCUT_SELECT* shortcutSelect = (SHORTCUT_SELECT*)GetSQL( &handle, SQL_GAME_PROCESS_SHORTCUT_SELECT );

	shortcutSelect->characterIdx = characterIdx;
	shortcutSelect->rowCount	 = 0;

	return SendSQL( perSocketContext, handle, sizeof(SHORTCUT_SELECT) );
}

bool cGameProcess::QuestSelect( PerSocketContext* perSocketContext, long characterIdx )
{
	HANDLE        handle      = NULL;
	QUEST_SELECT* questSelect = (QUEST_SELECT*)GetSQL( &handle, SQL_GAME_PROCESS_QUEST_SELECT );
	unsigned long length      = sizeof(QUEST_SELECT)-sizeof(questSelect->table);

	questSelect->characterIdx = characterIdx;

	return SendSQL( perSocketContext, handle, length );
}

bool cGameProcess::QuestCompleteSelect( PerSocketContext* perSocketContext, long characterIdx )
{
	HANDLE			handle		  = NULL;
	QUEST_ENDLIST*  questEndList = (QUEST_ENDLIST*)GetSQL( &handle, SQL_GAME_PROCESS_QUEST_ENDLISTSELECT );
	unsigned long	length      = sizeof(QUEST_ENDLIST)-sizeof(questEndList->questIdx);

	questEndList->characterIdx = characterIdx;

	return SendSQL( perSocketContext, handle, length );
}

bool cGameProcess::QuestRemove(PerSocketContext* perSocketContext, long dbIdx )
{
	HANDLE            handle      = NULL;
	QUEST_REMOVE*	  questRemove = (QUEST_REMOVE*)GetSQL( &handle, SQL_GAME_PROCESS_QUEST_REMOVE );

	questRemove->idx = dbIdx;

	return SendSQL( perSocketContext, handle, sizeof(QUEST_REMOVE) );
}

bool cGameProcess::TitleSelect( PerSocketContext* perSocketContext, long characterIdx )
{
	HANDLE			handle		= NULL;
	TITLE_SELECT*	titleSelect = (TITLE_SELECT*)GetSQL( &handle, SQL_GAME_PROCESS_TITLE_SELECT );

	titleSelect->characterIdx	= characterIdx;

	return SendSQL( perSocketContext, handle, sizeof(TITLE_SELECT) );
}

bool cGameProcess::GuildCreate( PerSocketContext* perSocketContext, long characterIdx, wchar_t* guildName, wchar_t* masterName )
{
	HANDLE			handle		= NULL;
	GUILD_CREATE*	guildCreate = (GUILD_CREATE*)GetSQL( &handle, SQL_GAME_PROCESS_GUILD_CREATE );

	guildCreate->characterIdx = characterIdx;
	wcscpy( guildCreate->name, guildName );
	wcscpy( guildCreate->masterName, masterName );
	guildCreate->money = -GUILDLIMIT_MONEY;

	return SendSQL( perSocketContext, handle, sizeof(GUILD_CREATE) );
}

bool cGameProcess::GuildDelete(PerSocketContext* perSocketContext, long idx, long characterIdx )
{
	HANDLE			handle		= NULL;
	GUILD_DELETE*	guildDelete = (GUILD_DELETE*)GetSQL( &handle, SQL_GAME_PROCESS_GUILD_DELETE );

	guildDelete->idx = idx;
	guildDelete->charaterIdx = characterIdx;

	return SendSQL( perSocketContext, handle, sizeof(GUILD_DELETE) );
}

bool cGameProcess::GuildUserAdd( PerSocketContext* perSocketContext, long idx, long characterIdx )
{
	HANDLE			handle		 = NULL;
	GUILDUSER_ADD*	guildUserAdd = (GUILDUSER_ADD*)GetSQL( &handle, SQL_GAME_PROCESS_GUILDUSER_ADD );

	guildUserAdd->idx = idx;
	guildUserAdd->charaterIdx = characterIdx;

	return SendSQL( perSocketContext, handle, sizeof(GUILDUSER_ADD) );
}

bool cGameProcess::GuildUserOut( PerSocketContext* perSocketContext, long idx, long characterIdx )
{
	HANDLE			handle		 = NULL;
	GUILDUSER_OUT*	guildUserOut = (GUILDUSER_OUT*)GetSQL( &handle, SQL_GAME_PROCESS_GUILDUSER_OUT );

	guildUserOut->idx = idx;
	guildUserOut->charaterIdx = characterIdx;

	return SendSQL( perSocketContext, handle, sizeof(GUILDUSER_OUT) );
}

bool cGameProcess::GuildUserCut( PerSocketContext* perSocketContext, long idx, long characterIdx )
{
	HANDLE			handle		 = NULL;
	GUILDUSER_OUT*	guildUserOut = (GUILDUSER_OUT*)GetSQL( &handle, SQL_GAME_PROCESS_GUILDUSER_CUT );

	guildUserOut->idx = idx;
	guildUserOut->charaterIdx = characterIdx;

	return SendSQL( perSocketContext, handle, sizeof(GUILDUSER_OUT) );
}

bool cGameProcess::GuildUserGivePosition( PerSocketContext* perSocketContext, long idx, long characterIdx, char position )
{
	HANDLE			handle		= NULL;
	GUILDUSER_GIVEPOSITION*		guildUserGivePosition = (GUILDUSER_GIVEPOSITION*)GetSQL( &handle, SQL_GAME_PROCESS_GUILDUSER_GIVEPOSITION );

	guildUserGivePosition->idx			= idx;
	guildUserGivePosition->characterIdx = characterIdx;
	guildUserGivePosition->position		= position;

	return SendSQL( perSocketContext, handle, sizeof(GUILDUSER_GIVEPOSITION) );
}

bool cGameProcess::GuildMarkUpdate( PerSocketContext* perSocketContext, long idx, long markIdx, BYTE* mark )
{
	HANDLE						handle = NULL;
	GUILDMARK_UPDATE*  guildMarkUpdate = (GUILDMARK_UPDATE*)GetSQL( &handle, SQL_GAME_PROCESS_GUILDMARK_UPDATE );
	
	guildMarkUpdate->idx		= idx;
	guildMarkUpdate->markIdx	= markIdx;
	memcpy( &guildMarkUpdate->mark, mark, sizeof(TB_GUILDMARK));

	int size = sizeof(TB_GUILDMARK);
	size;

	return SendSQL( perSocketContext, handle, sizeof(GUILDMARK_UPDATE) );
}

// ChannelSync Method
void cGameProcess::ChannelSync( )
{
	long cid = MAKECID((WORD)mServerNum, (WORD)mChannelNum);
	g_gameSrv->GetSender( )->PostChStatus( cid, (BYTE)mStatus );
}

// ChannelStatus Method
void cGameProcess::ChannelStatus(long cid, BYTE status)
{
	if ( mServerNum == HIWORD( cid ) )
	{
		cCSLock lock( &mCs );
		WORD    id = LOWORD( cid ) - 1;

		if ( mChannelNum == LOWORD( cid ) )
		{
			switch ( status )
			{
			case _E_STATUS_RUNABLE_:
				if ( mStatus == _E_STATUS_RUNABLE_ )
				{
					mComplete.channelSyn = true;
					mStatus = _E_STATUS_RUNNING_S1_;
					g_gameSrv->GetSender( )->PostChStatus( cid, (BYTE)mStatus );
				}
				break;
			case _E_STATUS_RUNNING_S1_:
			case _E_STATUS_RUNNING_S2_:
			case _E_STATUS_RUNNING_S3_:
				break;
			case _E_STATUS_FIN_WAIT1_:
			case _E_STATUS_FIN_WAIT2_:
			case _E_STATUS_LAST_ACK_:
				g_gameSrv->GetSender( )->PostChStatus( cid, (BYTE)mStatus );
				break;
			}
		}
		if ( id < MAX_CHANNEL )
		{
			ChannelList* channel = (mChannelList + id);

			switch ( status )
			{
			case _E_STATUS_RUNABLE_:
				if ( !channel->used )
				{
					channel->used = true;
					channel->status = status;
				}
				break;
			case _E_STATUS_RUNNING_S1_:
			case _E_STATUS_RUNNING_S2_:
			case _E_STATUS_RUNNING_S3_:
				if ( !channel->used )
				{
					channel->used = true;
				}
				channel->status = status;
				break;
			case _E_STATUS_CLOSING_:	// 정상종료
			case _E_STATUS_FIN_WAIT1_:	// 마지막 대기 1
			case _E_STATUS_FIN_WAIT2_:	// 마지막 대기 2
			case _E_STATUS_LAST_ACK_:	// 최종확인
			case _E_STATUS_DELETE_:		// 강제삭제
			case _E_STATUS_ERROR_:		// 오류종료
				if ( channel->used )
					ZeroMemory( channel, sizeof(ChannelList) );
				break;
			}
		}
	}
}

// ServerNotice Method
void cGameProcess::ServerNotice(long cid, wchar_t* message)
{
	if ( mServerNum == (DWORD)HIWORD( cid ) && mChannelNum != (DWORD)LOWORD( cid ) )
	{
		cCSLock lock( &mCs );

		MSG_SYN_CHEAT_NOTICE_SERVER syn;
			syn.Category = NM_CHEAT;
			syn.Protocol = NM_CHEAT_NOTICE_SERVER_SYN;
			wcscpy( syn.mMessage, message );
			syn.mMessage[MAX_CHAT_SIZE] = 0;
		OBJECTMANAGER->SendNotice( (char*)&syn, syn.GetMsgLength( ) );
	}
}

// ChatShout Method
void cGameProcess::ChatShout(long cid, unsigned short numfOfMap, long characterIdx, wchar_t* name, wchar_t* message)
{
	if ( mServerNum == (DWORD)HIWORD( cid ) && mChannelNum != (DWORD)LOWORD( cid ) && mChannelNum != CHN_11 )
	{
		cCSLock lock( &mCs );

		MSG_SYN_CHAT_SHOUT sendMsg;
		memset( &sendMsg, 0, sizeof(sendMsg) );

		sendMsg.Category     = NM_CHAT;
		sendMsg.Protocol     = NM_CHAT_SHOUT_SYN;
		sendMsg.CharacterIdx = characterIdx;
		wcscpy( sendMsg.Name, name );
		wcscpy( sendMsg.Message, message );

		QuickSendMap( numfOfMap, (char*)&sendMsg, sendMsg.GetMsgLength( ) );
	}
}

// PVPStatus Method
void cGameProcess::PVPStatus(long cid, BYTE status)
{
	if ( mServerNum == (DWORD)HIWORD( cid ) && mChannelNum != (DWORD)LOWORD( cid ) )
	{
		cCSLock lock( &mCs );
		switch ( status )
		{
		case _E_PVP_CLOSED_:
			mPVPData.timer = 0;
			break;
		case _E_PVP_RUNABLE_:
			mpObjectManager->SendMsgSynPVPJoin( );
			mPVPData.timer = GetAccumTime() + PVP_DM_JOIN_TIME;
			break;
		case _E_PVP_RUNNING_:
			mPVPData.timer = 0;
			break;
		case _E_PVP_CLOSING_:
			mPVPData.timer = 0;
			break;
		}
		mPVPData.cid    = cid;
		mPVPData.status = (PVP_STATUS)status;
	}
}

// PVPStatus Method
void cGameProcess::PVPNotice( long cid, unsigned char leftMinute )
{
	if ( mServerNum == (DWORD)HIWORD( cid ) && mChannelNum != (DWORD)LOWORD( cid ) )
	{
		OBJECTMANAGER->SendPVPNotice( leftMinute );
	}
}

// GuildSync Method
void cGameProcess::GuildSync(long cid, char* msg, int /*len*/)
{
	if ( mServerNum == (DWORD)HIWORD( cid ) && mChannelNum != (DWORD)LOWORD( cid ) )
	{
		MSGROOT* tempMsg = (MSGROOT*)msg;
		switch( tempMsg->Protocol )
		{
		case NM_GUILDPOST_CREATE_SYN:
			{
				cCSLock lock( &mCs );

				MSG_SYN_POSTGUILD_CREATE* pmsg = (MSG_SYN_POSTGUILD_CREATE*)msg;
				mpGuildManager->SynAddGuild( (void*)pmsg, (DWORD)LOWORD( cid ) );
			}
			break;
		case NM_GUILDPOST_DELETE_SYN:
			{
				cCSLock lock( &mCs );

				MSG_SYN_POSTGUILD_DELETE* pmsg = (MSG_SYN_POSTGUILD_DELETE*)msg;
				mpGuildManager->DeleteGuild( pmsg->idx );
			}
			break;
		case NM_GUILDPOST_USERADD_SYN:
			{
				cCSLock lock( &mCs );

				MSG_SYN_POSTGUILD_USERADD* pmsg = (MSG_SYN_POSTGUILD_USERADD*)msg;
				mpGuildManager->SynAddGuildUser( (void*)pmsg, (DWORD)LOWORD( cid ) );
			}
			break;
		case NM_GUILDPOST_USERCUT_SYN:
			{
				cCSLock lock( &mCs );

				MSG_SYN_POSTGUILD_USERCUT* pmsg = (MSG_SYN_POSTGUILD_USERCUT*)msg;
				mpGuildManager->SynCutGuilduser( (void*)pmsg );
			}
			break;
		case NM_GUILDPOST_USEROUT_SYN:
			{
				cCSLock lock( &mCs );

				MSG_SYN_POSTGUILD_USEROUT* pmsg = (MSG_SYN_POSTGUILD_USEROUT*)msg;
				mpGuildManager->SynOutGuilduser( (void*)pmsg );
			}
			break;
		case NM_GUILDPOST_GIVEPOSITION_SYN:
			{
				cCSLock lock( &mCs );

				MSG_SYN_POSTGUILD_GIVEPOSITION* pmsg = (MSG_SYN_POSTGUILD_GIVEPOSITION*)msg;

				cGuild* guild = mpGuildManager->GetGuild( pmsg->idx );
				mpGuildManager->SendGuildChangePosition( guild, pmsg->characterIdx, pmsg->position );
			}
			break;
		case NM_GUILDPOST_UPDATEMARK_SYN:
			{
				cCSLock lock( &mCs );

				MSG_SYN_POSTGUILD_UPDATEMARK* pmsg = (MSG_SYN_POSTGUILD_UPDATEMARK*)msg;
				mpGuildManager->SynUpdateMark( (void*)pmsg );
			}
			break;
		case NM_GUILDPOST_NOTICE_SYN:
			{
				cCSLock lock( &mCs );

				MSG_SYN_POSTGUILD_NOTICE* pmsg = (MSG_SYN_POSTGUILD_NOTICE*)msg;
				mpGuildManager->SendGuildNotice( pmsg->idx, pmsg->notice );
			}
			break;
		case NM_GUILDPOST_CHAT_SYN:
			{
				MSG_SYN_POSTGUILD_CHAT* pmsg = (MSG_SYN_POSTGUILD_CHAT*)msg;
				mpGuildManager->SynGuildChat( (void*)pmsg );
			}
			break;
		case NM_GUILDPOST_UPDATECHANNEL_SYN:
			{
				cCSLock lock( &mCs );

				MSG_SYN_POSTGUILD_CHANNEL* pmsg = (MSG_SYN_POSTGUILD_CHANNEL*)msg;
				mpGuildManager->GuildUpdateChannel( pmsg->idx, pmsg->characterIdx, pmsg->channelNum );
			}
			break;
		case NM_GUILDPOST_UPDATEINFO_SYN:
			{
				cCSLock lock( &mCs );
		
				MSG_SYN_POSTGUILD_UPDATE* pmsg = (MSG_SYN_POSTGUILD_UPDATE*)msg;
				mpGuildManager->SynUpdateGuildUserInfo( (void*)pmsg );
			}
			break;
		case NM_GUILDPOST_NOTE_SYN:
			{
				cCSLock lock( &mCs );

				MSG_SYN_POSTGUILD_NOTESEND* pmsg = (MSG_SYN_POSTGUILD_NOTESEND*)msg;
				mpGuildManager->SynGuildNote( (void*)pmsg );
			}
			break;
		case NM_GUILDPOST_CONNECT_SYN:
			{
				cCSLock lock( &mCs );
				
				MSG_SYN_POSTGUILD_CONNECT* pmsg = (MSG_SYN_POSTGUILD_CONNECT*)msg;

				/// 내 채널이 요청한 정보일때만 처리한다
				DWORD sendChannelNum = (DWORD)LOWORD( cid );
				if( pmsg->reqChannelNum == mChannelNum )
				{
					mpGuildManager->SynGuildConnect( (void*)pmsg, sendChannelNum );
				}
			}
			break;
		}
	}
}

void cGameProcess::GuildConnectSync( long cid )
{
	if ( mServerNum == (DWORD)HIWORD( cid ) && mChannelNum != (DWORD)LOWORD( cid ) )
	{
		cCSLock lock( &mCs );
		mpGuildManager->SendGuildConnect( (DWORD)LOWORD( cid ) );
	}
}

void cGameProcess::MonsterSync( long cid, char* msg, int /*len*/ )
{
	if ( mServerNum == (DWORD)HIWORD( cid ) && mChannelNum != (DWORD)LOWORD( cid ) )
	{
		MSGROOT* tempMsg = (MSGROOT*)msg;
		switch( tempMsg->Protocol )
		{
		case NM_MONSTERPOST_SWITCHREGEN_START_SYN:
			{
				cCSLock lock( &mCs );

				MSG_SYN_POSTMONSTER_SWITCHREGEN_START* pStart = (MSG_SYN_POSTMONSTER_SWITCHREGEN_START*)msg;
				AIMANAGER->SwitchRegenStart( pStart->mSwitchRegen );
			}
			break;
		case NM_MONSTERPOST_SWITCHREGEN_STOP_SYN:
			{
				cCSLock lock( &mCs );

				MSG_SYN_POSTMONSTER_SWITCHREGEN_STOP* pStop = (MSG_SYN_POSTMONSTER_SWITCHREGEN_STOP*)msg;
				AIMANAGER->SwitchRegenStop( pStop->mSwitchRegen );
			}
			break;
		}
	}
}

void cGameProcess::ChatSync( long cid, char* msg, int /*len*/ )
{
	if ( mServerNum == (DWORD)HIWORD( cid ) && mChannelNum != (DWORD)LOWORD( cid ) )
	{
		MSGROOT* tempMsg = (MSGROOT*)msg;
		switch( tempMsg->Protocol )
		{
		case NM_CHATPOST_WHISPER_SYN:	/// 다른 채널로부터 귓말 옴
			{
				cCSLock lock( &mCs );

				/// 내 채널에 온 정보면 처리
				MSG_SYN_POSTCHAT_WHISPER* pmsg = (MSG_SYN_POSTCHAT_WHISPER*)msg;
				if( pmsg->recvChannelNum == mChannelNum )
				{
					/// 검색후 결과를 리턴해준다
					int error = ERROR_CHAT_WHISPER_DISCONNECT;

					cPlayer* findPlayer = mpObjectManager->GetPlayer( pmsg->recvName );
					if( findPlayer != NULL  )
					{
						cPlayer* gameInUser = mpGridManager->GetPlayer( findPlayer->GetObjectID() );
						if( gameInUser != NULL )
						{
							sGameOption* toOption1 = gameInUser->GetOptionData();
							if( toOption1->option1.rejectionWhisper == false )	
							{
								error = ERROR_CHAT_WHISPER_SUCCESS;

								/// 해당유저에게 귓말 전송
								HANDLE                handle  = NULL;
								MSG_SYN_CHAT_WHISPER* sendMsg = (MSG_SYN_CHAT_WHISPER*)GetMsgRoot( &handle, gameInUser->GetConnectionIdx( ), NM_CHAT, NM_CHAT_WHISPER_SYN );
								if ( sendMsg != NULL )
								{
									wcscpy( sendMsg->Name, pmsg->senderName );			// 보낸 캐릭터 이름
									wcscpy( sendMsg->Message, pmsg->message );		// 보낸 메시지
									sendMsg->inventory = pmsg->inventory;
									SendMsgRoot( handle, sendMsg->GetMsgLength( ) );	// 발송.
								}
							}
							else
							{
								/// 거부로인한 귓말전송 불가
								error = ERROR_CHAT_WHISPER_OPTIONREFUSE;
							}
						}
					}

					/// 귓말 전송 결과를 다시 보내준다
					if( PostChatWhisperResult( pmsg->sendChannelNum, error, pmsg->senderIndex, pmsg->recvName, pmsg->message, &pmsg->inventory )== false )
					{
						PostServerEvent( "FAIL - SQL_GAME_PROCESS_WHISPER_MEMBER_SELECT PostChatWhisperResult(%d,%d)", pmsg->senderIndex, pmsg->recvChannelNum );
					}
				}
			}
			break;
		case NM_CHATPOST_WHISPER_RESULTSYN:		/// 보낸 귓말에 대한 결과를 받음
			{
				cCSLock lock( &mCs );
				
				MSG_SYN_POSTCHAT_WHISPERRESULT* pmsg = (MSG_SYN_POSTCHAT_WHISPERRESULT*)msg;
				if( pmsg->recvChannelNum == mChannelNum )
				{
					/// 귓말을 보냈었던 유저에게 결과를 알려줌
					cPlayer* sendPlayer = mpGridManager->GetPlayer( pmsg->senderIndex );
					if( sendPlayer != NULL )
					{
						HANDLE                handle  = NULL;
						MSG_RES_CHAT_WHISPER* sendMsg = (MSG_RES_CHAT_WHISPER*)GetMsgRoot( &handle, sendPlayer->GetConnectionIdx(), NM_CHAT, NM_CHAT_WHISPER_RES );
						if( sendMsg != NULL )
						{
							sendMsg->ErrorCode = pmsg->error;

							if ( pmsg->error == ERROR_CHAT_WHISPER_SUCCESS )
							{
								wcscpy( sendMsg->Name, pmsg->recvName );				// 받는 캐릭터이름.
								wcscpy( sendMsg->Message, pmsg->message );		// 보낸 메시지.
								sendMsg->inventory = pmsg->inventory;
							}

							SendMsgRoot( handle, sendMsg->GetMsgLength( ) );	// 발송.
						}
					}
				}
			}
			break;
		case NM_CHATPOST_NOTESEND_SYN:
			{
				cCSLock lock( &mCs );

				MSG_SYN_POSTCHAT_NOTESEND* pmsg = (MSG_SYN_POSTCHAT_NOTESEND*)msg;
				if( pmsg->recvChannelNum == mChannelNum )
				{
					/// 다른 채널에서 쪽지를 받으면 처리
					cPlayer* findPlayer = mpObjectManager->GetPlayer( pmsg->recvName );
					if( findPlayer != NULL )
					{
						/// 받을 유저가 게임에 존재할때만 처리한다
						cPlayer* gameInUser = mpGridManager->GetPlayer( findPlayer->GetObjectID() );
						if( gameInUser != NULL )
						{
							if( mpNoteManager->AddNote( gameInUser->GetObjectID(), &pmsg->table ) == true )
							{
								/// 추가된 내용 쏴주기
								HANDLE				handle = NULL;
								MSG_SYN_NOTE_SEND* sendMsg = (MSG_SYN_NOTE_SEND*)GetMsgRoot( &handle, gameInUser->GetConnectionIdx(), NM_CHAT, NM_CHAT_NOTE_SEND_SYN );
								if( sendMsg != NULL )
								{
									TB_NOTE* note = &pmsg->table;

									sendMsg->table.Idx    = note->idx;
									sendMsg->table.Year   = note->registerDate.year;
									sendMsg->table.Month  = note->registerDate.month;
									sendMsg->table.Day    = note->registerDate.day;
									sendMsg->table.Hour   = note->registerDate.hour;
									sendMsg->table.Minute = note->registerDate.minute;
									wcscpy( sendMsg->table.CharacterName, note->characterName );
									wcscpy( sendMsg->table.Message, note->message );
									sendMsg->table.RemainTime = note->remainTime;

									SendMsgRoot( handle, sizeof(MSG_SYN_NOTE_SEND) );
								}
							}
						}
					}
				}
			}
			break;
		}
	}
}

// IsCharacterName Method
bool cGameProcess::IsCharacterName(wchar_t* name, int len)
{
	wchar_t code     = 0;
	bool    retvalue = true;

	// MIN_NAME_SIZE
	if ( wcslen( name ) < 2 )
		return false;

	for ( int i = 0; i < len && (*name) != 0 && retvalue == true; i++, name++ )
	{
		code = (*name);

		// 숫자
		if (code >= 0x0030 && code <= 0x0039)
			retvalue = true;
		// 영문
		else if ( (code >= 0x0041 && code <= 0x005A) || (code >= 0x0061 && code <= 0x007A) )
			retvalue = true;
		// 한글
		else if (code >= 0XAC00 && code <= 0XD7A3)
			retvalue = true;
		/*-- 중문
		else if (code >= 0X4E00 && code <= 0X9FBB)
			retvalue = true;
		*/
		// 외-오류
		else
			retvalue = false;
	}

	return retvalue;
}

// ResMsgRoot Method
bool cGameProcess::ResMsgRoot(PerSocketContext* perSocketContext, char category, char protocol)
{
	HANDLE handle = NULL;
	return ( GetMsgRoot( &handle, perSocketContext, category, protocol ) != NULL ) ? SendMsgRoot( handle, sizeof(MSGROOT) ) : false;
}

// ResMsgError Method
bool cGameProcess::ResMsgError(PerSocketContext* perSocketContext, char category, char protocol, long error)
{
	HANDLE     handle  = NULL;
	MSG_ERROR* sendMsg = (MSG_ERROR*)GetMsgRoot( &handle, perSocketContext, category, protocol );
	if ( sendMsg != NULL )
	{
		sendMsg->ErrorCode = error;
		return SendMsgRoot( handle, sizeof(MSG_ERROR) );
	}
	return false;
}
bool cGameProcess::ResMsgError( unsigned long connectionIdx, char category, char protocol, long error)
{
	HANDLE     handle  = NULL;
	MSG_ERROR* sendMsg = (MSG_ERROR*)GetMsgRoot( &handle, connectionIdx, category, protocol );
	if ( sendMsg != NULL )
	{
		sendMsg->ErrorCode = error;
		return SendMsgRoot( handle, sizeof(MSG_ERROR) );
	}
	return false;
}

// ResCharacterList Method
// 참조: 유니코드 사용
bool cGameProcess::ResCharacterList(PerSocketContext* perSocketContext, long rowCount, TB_CHARACTER* character)
{
	HANDLE              handle  = NULL;
	MSG_CHARACTER_LIST* sendMsg = (MSG_CHARACTER_LIST*)GetMsgRoot( &handle, perSocketContext, NM_USER, NM_USER_CHARACTER_LIST_RES );
	if ( sendMsg != NULL )
	{
		MSG_CHARACTER_BASEINFO* baseInfo = NULL;
		unsigned long           length   = sizeof(MSG_CHARACTER_LIST) - sizeof(sendMsg->BaseInfo);

		sPlayerInfo*            playerInfo;
		sPlayerWeaponInfo*      weaponInfo;
		sPlayerWearInfo*        wearInfo;

		baseInfo = sendMsg->BaseInfo;
		for ( long i = 0; i < rowCount; i++, baseInfo++, character++ )
		{
			// sPlayerInfo
			playerInfo = &baseInfo->PlayerInfo;
			playerInfo->CharacterIdx             = character->idx;
			wcscpy( playerInfo->strName, character->name );
			playerInfo->Level                    = character->level;
			playerInfo->Race                     = character->race;
			playerInfo->Gender                   = character->gender;
			playerInfo->Job                      = (ePLAYER_JOB)character->job;
			playerInfo->HeadInfo[ eHEAD_HAIR ]   = character->hair;
			playerInfo->HeadInfo[ eHEAD_FACE ]   = character->face;

			// sPlayerWeaponInfo
			weaponInfo = &baseInfo->WeaponInfo;
			weaponInfo->WeaponIdx[ eHAND_LEFT  ] = character->leftHand;
			weaponInfo->WeaponIdx[ eHAND_RIGHT ] = character->rightHand;
			weaponInfo->WeaponEnhanced[ eHAND_LEFT  ] = character->leftHandEnhanced;
			weaponInfo->WeaponEnhanced[ eHAND_RIGHT ] = character->rightHandEnhanced;

			// sPlayerWearInfo
			wearInfo = &baseInfo->WearInfo;
			wearInfo->WearIdx[ eWEAR_BODY1 ]     = character->body1;
			wearInfo->WearIdx[ eWEAR_BODY2 ]     = character->body2;
			wearInfo->WearIdx[ eWEAR_HAND  ]     = character->hand;
			wearInfo->WearIdx[ eWEAR_FOOT  ]     = character->foot;
			wearInfo->WearIdx[ eWEAR_HAT   ]     = character->hat;
			wearInfo->Earring                    = character->earring;
			wearInfo->Necklace                   = character->necklace;
			wearInfo->Brooch                     = character->brooch;
			wearInfo->Bracelet                   = character->bracelet;
			wearInfo->Ring                       = character->ring;

			// Etc...
			baseInfo->MapNum   = character->mapNum;
			baseInfo->StandIdx = character->standIdx;

			sendMsg->CharacterNum += 1;
			length += sizeof(MSG_CHARACTER_BASEINFO);
		}
		return SendMsgRoot( handle, length );
	}
	return false;
}

// ResChannelList Method
bool cGameProcess::ResChannelList(PerSocketContext* perSocketContext)
{
	HANDLE               handle  = NULL;
	MSG_RES_CHANNELLIST* sendMsg = (MSG_RES_CHANNELLIST*)GetMsgRoot( &handle, perSocketContext, NM_USER, NM_USER_CHANNEL_LIST_RES );
	if ( sendMsg != NULL )
	{
		unsigned long  length   = sizeof(MSG_RES_CHANNELLIST) - sizeof(sendMsg->Channels);
		unsigned long& rowCount = sendMsg->RowCount;
		ChannelList*   channels = mChannelList;

		sendMsg->ChannelNo = mChannelNum;
		for ( u_long i = CHN_01; i <= CHN_10 && i < MAX_CHANNEL; i++, channels++ )
		{
			if ( channels->used )
			{
				sendMsg->Channels[ rowCount ].NumOfChannel = i;
				sendMsg->Channels[ rowCount ].Status       = channels->status;
				rowCount += 1;
			}
		}
		length += (rowCount * sizeof(sendMsg->Channels));
		return SendMsgRoot( handle, length );
	}
	return false;
}

// BatchComplete Method
bool cGameProcess::BatchComplete(DWORD currentTick, PerSocketContext* perSocketContext, void* ptr)
{
	ClientInfo*   clientInfo = (ClientInfo*)perSocketContext->buffer;
	cPlayer*      hero       = clientInfo->hero;
	unsigned long heroIdx    = (hero != NULL ? hero->GetObjectID( ) : 0);

	Packet*       packet     = (Packet*)ptr;
	u_short       packetLen  = packet->tlen - packet->hlen;
	MSGROOT*      msgRoot    = (MSGROOT*)((char*)ptr + packet->hlen);

	Verbose->BatchComplete( &mCs, ptr );

	mLastCategory   = msgRoot->Category;
	mLastProtocol   = msgRoot->Protocol;
	mLastPacketSize = packetLen;

	switch ( msgRoot->Category )
	{
	case NM_USER:
		/*-- MSGROOT::Protocol 필터링 - 알수없는 경우 소켓연결을 해제한다.
		*/
		if ( (unsigned char)msgRoot->Protocol >= NM_USER_MAX )
		{
			PostServerEvent( "END TASK - UNKNOWN NM_USER::PROTOCOL(=%d):INETNUM(=%s).", msgRoot->Protocol, inet_ntoa( perSocketContext->addr.sin_addr) );
			return false;
		}

		switch ( msgRoot->Protocol )
		{
		case NM_USER_GAMESRV_REQ:
			if ( !clientInfo->commonDb.check )
			{
				MSG_REQ_GAMESRV* msg = (MSG_REQ_GAMESRV*)msgRoot;

				HANDLE        handle      = NULL;
				MEMBER_CHECK* memberCheck = (MEMBER_CHECK*)GetSQL( &handle, SQL_GAME_PROCESS_MEMBER_CHECK );

				memberCheck->loginIdx   = msg->LoginIdx;
				memberCheck->memberIdx  = msg->MemberIdx;
				memberCheck->serverNum  = (short)mServerNum;
				memberCheck->channelNum = (short)mChannelNum;
				memberCheck->retvalue   = 0;

				clientInfo->commonDb.check = SendSQL( perSocketContext, handle, sizeof( MEMBER_CHECK ) );
			}
			break;
		case NM_USER_CHANNEL_LIST_REQ:
			if ( clientInfo->complete.check && !clientInfo->request.channelList )
			{
				clientInfo->request.channelList = true;
				clientInfo->delay.channelList = GetTickCount( ) + 500;
			}
			break;
		case NM_USER_CHARACTER_LIST_REQ:
			if ( clientInfo->complete.check && !clientInfo->request.characterList && !clientInfo->commonDb.characterList )
			{
				clientInfo->request.characterList = true;
				clientInfo->delay.characterList = GetTickCount( ) + 500;
			}
			break;
		case NM_USER_CHARACTER_NAME_REQ:
			if ( clientInfo->complete.check && !clientInfo->commonDb.characterName )
			{
				MSG_REQ_CHARACTER_NAME* msg = (MSG_REQ_CHARACTER_NAME*)msgRoot;

				if ( IsCharacterName( msg->Name, MAX_NAME_BUFFER_SIZE ) )
				{
					HANDLE          handle        = NULL;
					CHARACTER_NAME* characterName = (CHARACTER_NAME*)GetSQL( &handle, SQL_GAME_PROCESS_CHARACTER_NAME );

					wcscpy( characterName->name, msg->Name );

					clientInfo->commonDb.characterName = SendSQL( perSocketContext, handle, sizeof(CHARACTER_NAME) );
				}
				else
				{
					ResMsgError( perSocketContext, NM_USER, (char)NM_USER_CHARACTER_NAME_RES, ERROR_CHARACTER_NAME_RULE );
				}
			}
			break;
		case NM_USER_CHARACTER_CREATE_REQ:
			if ( clientInfo->complete.check && !clientInfo->commonDb.characterInsert )
			{
				MSG_REQ_CHARACTER_CREATE* msg      = (MSG_REQ_CHARACTER_CREATE*)msgRoot;
				bool                      retvalue = true;

				// Name   검사
				msg->Name[MAX_NAME_SIZE] = 0;
				if ( IsCharacterName( msg->Name, MAX_NAME_BUFFER_SIZE ) == false )
					retvalue = false;
				// Race   검사
				else if ( (unsigned char)msg->Race >= eRACE_MAX )
					retvalue = false;
				// Gender 검사
				else if ( (unsigned char)msg->Gender >= eGENDER_MAX )
					retvalue = false;
				// Stand Idx 검사
				else if ( msg->StandIdx >= 5 )
					retvalue = false;
				// Defail 검사
				else
				{
					switch ( msg->Race )
					{
					case eRACE_HUMAN:
						switch ( msg->Job )
						{
						case ePLAYER_FIGHTER:
						case ePLAYER_MAGE:
							break;
						default:
							retvalue = false;
							break;
						}
						break;
					case eRACE_BEAST:
						switch ( msg->Job )
						{
						case ePLAYER_WARRIOR:
						case ePLAYER_ROGUE:
							break;
						default:
							retvalue = false;
							break;
						}
						break;
					case eRACE_ELF:
						switch ( msg->Job )
						{
						case ePLAYER_RANGER:
						case ePLAYER_SHAMAN:
							break;
						default:
							retvalue = false;
							break;
						}
						break;
					}
				}

				msg->Hair;
				msg->Face;

				sTargetPos pos = STAGESCRIPT->GetStageChangePos( CHARACTER_CREATE_POS_IDX );
				if( pos.mMapNumber == 0 )
					retvalue = false;

				if ( retvalue == true )
				{
					HANDLE            handle          = NULL;
					CHARACTER_INSERT* characterInsert = (CHARACTER_INSERT*)GetSQL( &handle, SQL_GAME_PROCESS_CHARACTER_INSERT );
					unsigned long     length          = sizeof(CHARACTER_INSERT) - sizeof(characterInsert->table);

					wcscpy( characterInsert->name, msg->Name );

					characterInsert->race      = msg->Race;
					characterInsert->gender    = msg->Gender;
					characterInsert->job       = msg->Job;

					characterInsert->hair      = msg->Hair;
					characterInsert->face      = msg->Face;

					characterInsert->xPos      = pos.mPosX;
					characterInsert->yPos      = pos.mPosY;

					characterInsert->hp        = 10000;
					characterInsert->mp        = 10000;

					characterInsert->standIdx  = msg->StandIdx;
					characterInsert->userIdx   = clientInfo->memberIdx;

					clientInfo->commonDb.characterInsert = SendSQL( perSocketContext, handle, length );

				}
				else
				{
					ResMsgError( perSocketContext, NM_USER, NM_USER_CHARACTER_CREATE_RES, ERROR_CHARACTER_CREATE_RULE );
				}
			}
			break;
		case NM_USER_CHARACTER_DELETE_REQ:
			if ( clientInfo->complete.check && !clientInfo->commonDb.characterDelete )
			{
				MSG_REQ_CHARACTER_DELETE* msg = (MSG_REQ_CHARACTER_DELETE*)msgRoot;

				HANDLE            handle          = NULL;
				CHARACTER_DELETE* characterDelete = (CHARACTER_DELETE*)GetSQL( &handle, SQL_GAME_PROCESS_CHARACTER_DELETE );

				characterDelete->idx     = msg->CharacterIdx;
				characterDelete->userIdx = clientInfo->memberIdx;

				clientInfo->commonDb.characterDelete = SendSQL( perSocketContext, handle, sizeof(CHARACTER_DELETE) );
			}
			break;
		case NM_USER_COME_IN_GAME_REQ:
			if ( clientInfo->complete.check && !clientInfo->commonDb.comeinGame )
			{
				MSG_REQ_COMEIN_GAME* msg = (MSG_REQ_COMEIN_GAME*)msgRoot;

				HANDLE       handle      = NULL;
				COMEIN_GAME* comeinGame  = (COMEIN_GAME*)GetSQL( &handle, SQL_GAME_PROCESS_COMEIN_GAME );

				comeinGame->loginIdx       = clientInfo->loginIdx;
				comeinGame->serverNum      = (short)mServerNum;
				comeinGame->channelNum     = (short)msg->NumOfChannel;
				comeinGame->lastChannel    = (short)mChannelNum;
				comeinGame->characterIdx   = msg->CharacterIdx;
				comeinGame->instantDungeon = 0;

				clientInfo->commonDb.comeinGame = SendSQL( perSocketContext, handle, sizeof(COMEIN_GAME) );
			}
			break;
		case NM_USER_GAMEIN_REQ:
			if ( clientInfo->complete.characterSelect && !clientInfo->request.gameIn )
			{
				clientInfo->request.gameIn = true;
			}
			break;
		case NM_USER_LOADDATA_REQ:
			if ( clientInfo->complete.characterSelect )
			{
				// 070622 PKH  몬스터 모델 정보 & NPC 모델 정보 전송
				mpObjectManager->SendMsgMonsterModelNumberToPlayer( perSocketContext->cid, hero->GetMapNumber( ) );
				mpObjectManager->SendMsgNpcModelNumberToPlayer( perSocketContext->cid, hero->GetMapNumber( ) );
			}
			break;
		}
		break; // case NM_USER:

	case NM_PLAYER:
		/*-- ClientInfo::Complete 필터링 - Check / CharacterSelect / GameIn.
		*/
		if ( (clientInfo->completeData & 0x0007) != 0x0007 )
			return false;

		/*-- MSGROOT::Protocol 필터링 - 알수없는 경우 소켓연결을 해제한다.
		*/
		if ( (unsigned char)msgRoot->Protocol >= NM_PLAYER_MAX )
		{
			PostServerEvent( "END TASK - UNKNOWN NM_PLAYER::PROTOCOL(=%d):MEMBER_IDX(=%d):CHARACTER_IDX(=%d):INETNUM(=%s).",
							 msgRoot->Protocol,
							 clientInfo->memberIdx,
							 clientInfo->characterIdx,
							 inet_ntoa( perSocketContext->addr.sin_addr) );
			return false;
		}

		switch( msgRoot->Protocol )
		{
		case NM_PLAYER_GAMEFINISH_REQ:
			if ( hero->IsRequestRejection() == true || ( hero->GetState() == eOBJECT_STATE_IDLE || hero->GetState() == ePLAYER_STATE_ITEMPICK ) == false )
			{
				ResMsgError( perSocketContext, NM_PLAYER, NM_PLAYER_GAMEFINISH_RES, ERROR_GAMEFINISH_FAIL );
			}
			else
			{
				hero->ChangeState( eOBJECT_STATE_STOP );
				hero->SetStateStop( eSTOP_GAMEFINISH );	
				hero->StartRequestRejection( eREQREJCT_GAMEFINISH );

				ResMsgError( perSocketContext, NM_PLAYER, NM_PLAYER_GAMEFINISH_RES, ERROR_GAMEFINISH_SUCCESS );
			}
			break;
		case NM_PLAYER_GAMEFINISH_CANCEL_REQ:
			hero->EndRequestRejection( eREQREJCT_GAMEFINISH );
			break;
		case NM_PLAYER_DISCONNECT_REQ:
			if ( hero->GetRequestRejection() != eREQREJCT_GAMEFINISH )
			{
				ResMsgError( perSocketContext, NM_PLAYER, NM_PLAYER_DISCONNECT_RES, ERROR_GAMEFINISH_FAIL );
			}
			else
			{
				ResMsgError( perSocketContext, NM_PLAYER, NM_PLAYER_DISCONNECT_RES, ERROR_GAMEFINISH_SUCCESS );
			}
			break;
		case NM_PLAYER_GAMEOUT_REQ:
			if ( clientInfo->complete.gameIn )
			{
				if( hero->GetRequestRejection() == eREQREJCT_GAMEFINISH )
					clientInfo->request.gameOut = true;
				else
					ResMsgError( perSocketContext, NM_PLAYER, NM_PLAYER_DISCONNECT_RES, ERROR_GAMEFINISH_FAIL );
			}
			break;
		case NM_PLAYER_MAPCHANGE_REQ:
			if ( clientInfo->complete.gameIn )
			{
				MSG_REQ_PLAYER_MAPCHANGE* reqMsg  = (MSG_REQ_PLAYER_MAPCHANGE*)msgRoot;

				if( hero->MapChange( reqMsg->mMapChangePosIndex ) == false )
				{
					// 맵이동 응답 메시지 - 실패
					// 이동 성공메세지는 여기서 보내지 않고 후에 맵이동에 필요한 데이타와 함게 보낸다.
					HANDLE     handle  = NULL;
					MSG_ERROR* sendMsg = (MSG_ERROR*)GetMsgRoot( &handle, perSocketContext, NM_PLAYER, NM_PLAYER_MAPCHANGE_RES );

					sendMsg->ErrorCode = 1;
					SendMsgRoot( handle, sizeof(MSG_ERROR) );

					return true;
				}
			}
			break;
		case NM_PLAYER_MOVE_REQ:
			{
				bool isMove = false;

				// 플레이어 상태 확인
				if( hero->ChangeState( eOBJECT_STATE_MOVE ) == true )
				{
					MSG_REQ_MOVE* pmsg = (MSG_REQ_MOVE*)msgRoot;

					NiPoint2 start( pmsg->startX, pmsg->startY );
					NiPoint2 goal( pmsg->destX, pmsg->destY );
					if( hero->IsMovePossible( start, goal ) == true )	/// 직선 방향 이동 가능 코스인지 체크
					{
						// 061208 PKH 이동 목적지를 설정
						hero->SetMoveTargetPos( pmsg->destX, pmsg->destY );
						hero->InitRangeTarget();

						if( hero->GetCheatHideMode() == false )
						{
							// 다른 플레이어들에게도 알림
							MSG_SYN_MOVE Msg;
							Msg.Category     = NM_PLAYER;
							Msg.Protocol     = NM_PLAYER_MOVE_SYN;
							Msg.characterIdx = heroIdx;
							Msg.destX        = pmsg->destX;
							Msg.destY        = pmsg->destY;
							Msg.moveSpeed    = hero->GetMoveSpeed();
							NETWORK2->QuickSendExcept( hero, (char*)&Msg, sizeof(Msg) );
						}

						isMove = true;
					}
				}

				if( isMove == false )
				{
					if( hero->GetState() == eOBJECT_STATE_MOVE )
						hero->ChangeState( eOBJECT_STATE_IDLE );

					// 070625 PKH 실패 메세지 발송 - 서버상의 좌표로 클라이언트를 옮기게 함
					HANDLE            handle = NULL;
					MSG_RES_ERR_MOVE* msg    = (MSG_RES_ERR_MOVE*)GetMsgRoot( &handle, perSocketContext, NM_PLAYER, NM_PLAYER_MOVE_RES );

					msg->destX = hero->GetXPos();
					msg->destY = hero->GetYPos();
					SendMsgRoot( handle, sizeof(MSG_RES_ERR_MOVE) );
				}
			}
			break;
		case NM_PLAYER_ACTIONMOVE_REQ:
			{
				bool isMove = false;

				// 플레이어 상태 확인
				if( hero->ChangeState( eOBJECT_STATE_MOVE ) == true )
				{
					MSG_REQ_ACTIONMOVE* pmsg = (MSG_REQ_ACTIONMOVE*)msgRoot;

					NiPoint2 start( pmsg->startX, pmsg->startY );
					NiPoint2 goal( pmsg->destX, pmsg->destY );
					if( hero->IsMovePossible( start, goal ) == true )	/// 직선 방향 이동 가능 코스인지 체크
					{
						hero->SetRangeTarget( pmsg->targetX, pmsg->targetY, pmsg->targetInfo, pmsg->range );
						hero->SetMoveTargetPos( pmsg->destX, pmsg->destY );

						if( hero->GetCheatHideMode() == false )
						{
							// 다른 플레이어들에게도 알림
							MSG_SYN_ACTIONMOVE Msg;
							Msg.Category = NM_PLAYER;
							Msg.Protocol = NM_PLAYER_ACTIONMOVE_SYN;
							Msg.characterIdx = heroIdx;
							Msg.destX = pmsg->destX;
							Msg.destY = pmsg->destY;
							Msg.targetX = pmsg->targetX;
							Msg.targetY = pmsg->targetY;
							Msg.range = pmsg->range;
							Msg.targetInfo = pmsg->targetInfo;
							Msg.moveSpeed = hero->GetMoveSpeed();
							NETWORK2->QuickSendExcept( hero, (char*)&Msg, sizeof(Msg) );
						}

						isMove = true;
					}
				}

				if( isMove == false )
				{
					if( hero->GetState() == eOBJECT_STATE_MOVE )
						hero->ChangeState( eOBJECT_STATE_IDLE );

					// 070625 PKH 실패 메세지 발송 - 서버상의 좌표로 클라이언트를 옮기게 함
					HANDLE            handle  = NULL;
					MSG_RES_ERR_MOVE* sendMsg = (MSG_RES_ERR_MOVE*)GetMsgRoot( &handle, perSocketContext, NM_PLAYER, NM_PLAYER_ACTIONMOVE_RES );

					sendMsg->destX = hero->GetXPos();
					sendMsg->destY = hero->GetYPos();
					SendMsgRoot( handle, sizeof(MSG_RES_ERR_MOVE) );
				}
			}
			break;
		case NM_PLAYER_RESURRECTION_REQ:
			{
				MSG_REQ_PLAYER_RESURRECTION* reqMsg = (MSG_REQ_PLAYER_RESURRECTION*)msgRoot;
				
				hero->Resurrection( reqMsg->mFlag );
			}
			break;
		case NM_PLAYER_SHORTCUT_LIST_REQ:
			if ( clientInfo->complete.shortcut )
			{
				clientInfo->request.shortcutSend = true;
			}
			break;
		case NM_PLAYER_SHORTCUT_UPDATE_REQ:
			if ( clientInfo->complete.shortcut )
			{
				MSG_REQ_PLAYER_SHORTCUT_UPDATE* msg      = (MSG_REQ_PLAYER_SHORTCUT_UPDATE*)msgRoot;
				sShortcut*                      shortcut = msg->mShortcut;

				for ( int i = 0; i < MAX_SHORTCUT_SIZE; i++, shortcut++ )
				{
					if ( shortcut->mType == eSHORTCUT_ITEM && shortcut->mInventoryIdx == 0 )
					{
						memset( shortcut, 0, sizeof(sShortcut) );
					}
					else if ( shortcut->mType >= eSHORTCUT_UNKNOWN )
					{
						memset( shortcut, 0, sizeof(sShortcut) );
					}
				}

				memcpy( clientInfo->shortcut.binary, msg->mShortcut, sizeof(msg->mShortcut) );
			}
			break;
		case NM_PLAYER_OPTION_UPDATE_REQ:
			if ( clientInfo->complete.characterOption )
			{
				MSG_REQ_PLAYER_OPTION_UPDATE* msg = (MSG_REQ_PLAYER_OPTION_UPDATE*)msgRoot;

				if ( clientInfo->optionData1 != msg->option1 )
				{
					clientInfo->optionData1 = msg->option1;
					hero->SetOptionData( clientInfo->optionData1 );

					HANDLE            handle          = NULL;
					CHARACTER_OPTION* characterOption = (CHARACTER_OPTION*)GetSQL( &handle, SQL_GAME_PROCESS_CHARACTER_OPTION );

					characterOption->idx     = heroIdx;
					characterOption->option1 = clientInfo->optionData1;

					SendSQL( perSocketContext, handle, sizeof(CHARACTER_OPTION) );
				}

				ResMsgError( perSocketContext, NM_PLAYER, NM_PLAYER_OPTION_UPDATE_RES, ERROR_PLAYER_OPTION_UPDATE_SUCCESS );
			}
			break;
		case NM_PLAYER_STATE_IDLE_REQ:
			{
				MSG_REQ_PLAYER_STATE_IDLE* pReqMsg = (MSG_REQ_PLAYER_STATE_IDLE*)msgRoot;

				hero->SetStateIdle( pReqMsg->mPlayerStateIdle );

				// 다른 플레이어들에게도 알림
				MSG_SYN_PLAYER_STATE_IDLE synMsg;

				synMsg.Category         = NM_PLAYER;
				synMsg.Protocol         = NM_PLAYER_STATE_IDLE_SYN;
				synMsg.mCharacterIdx    = heroIdx;
				synMsg.mPlayerStateIdle = pReqMsg->mPlayerStateIdle;

				NETWORK2->QuickSendExcept( hero, (char*)&synMsg, sizeof(synMsg) );
			}
			break;
		case NM_PLAYER_STATE_SITDOWN_REQ:
			{
				MSG_REQ_PLAYER_STATE_SITDWON* pReqMsg = (MSG_REQ_PLAYER_STATE_SITDWON*)msgRoot;

				unsigned char errCode = 0;

				// 앉기
				if( pReqMsg->mSitDown == true )
				{
					// 상태 변경 실패
					if( hero->ChangeState( ePLAYER_STATE_SITDOWN ) == false )
						errCode = 1;
				}
				else	// 일어서기
				{
					// 상태 변경 실패
					if( hero->GetState() != ePLAYER_STATE_SITDOWN || hero->ChangeState( eOBJECT_STATE_IDLE ) == false )
						errCode = 1;
				}

				HANDLE                        handle = NULL;
				MSG_RES_PLAYER_STATE_SITDWON* msg    = (MSG_RES_PLAYER_STATE_SITDWON*)GetMsgRoot( &handle, perSocketContext, NM_PLAYER, NM_PLAYER_STATE_SITDOWN_RES );

				msg->ErrorCode = errCode;
				msg->mSitDown  = hero->GetState() == ePLAYER_STATE_SITDOWN ? true : false;
				SendMsgRoot( handle, sizeof(MSG_RES_PLAYER_STATE_SITDWON) );

				if( errCode == 0 )
				{
					// 다른 플레이어들에게도 알림
					MSG_SYN_PLAYER_STATE_SITDWON synMsg;

					synMsg.Category      = NM_PLAYER;
					synMsg.Protocol      = NM_PLAYER_STATE_SITDOWN_SYN;
					synMsg.mCharacterIdx = heroIdx;
					synMsg.mSitDown      = hero->GetState() == ePLAYER_STATE_SITDOWN ? true : false;

					NETWORK2->QuickSendExcept( hero, (char*)&synMsg, sizeof(synMsg) );
				}
			}
			break;
		case NM_PLAYER_COMMUNITYSKILL_REQ:
			{
				MSG_REQ_PLAYER_COMMUNITYSKILL* pReqMsg = (MSG_REQ_PLAYER_COMMUNITYSKILL*)msgRoot;

				try
				{
					/// 상태 체크
					if( hero->GetState() != eOBJECT_STATE_IDLE )
						throw 1;

					/// 몬스터 변신중엔 생활스킬 불가
					if( hero->GetChgMonsterIdx() != 0 )
						throw 1;

					throw 0;
				}
				catch ( int error )
				{
					HANDLE            handle  = NULL;
					MSG_RES_PLAYER_COMMUNITYSKILL* sendMsg = (MSG_RES_PLAYER_COMMUNITYSKILL*)GetMsgRoot( &handle, perSocketContext, NM_PLAYER, NM_PLAYER_COMMUNITYSKILL_RES );
					if ( sendMsg != NULL )
					{
						sendMsg->ErrorCode          = error;
						sendMsg->mCommunitySkillIdx = pReqMsg->mCommunitySkillIdx;
						SendMsgRoot( handle, sizeof(MSG_RES_PLAYER_COMMUNITYSKILL) );
					}
					else
					{
						assert(NULL);
						PostServerEvent("NM_PLAYER_COMMUNITYSKILL_RES - GetMsgRoot( &handle, perSocketContext ) error");
					}

					if( error == 0 )
					{
						hero->SetCommunitySkill( pReqMsg->mCommunitySkillIdx );

						// 다른 플레이어들에게도 알림
						MSG_SYN_PLAYER_COMMUNITYSKILL synMsg;

						synMsg.Category      = NM_PLAYER;
						synMsg.Protocol      = NM_PLAYER_COMMUNITYSKILL_SYN;
						synMsg.mCharacterIdx = heroIdx;
						synMsg.mCommunitySkillIdx = pReqMsg->mCommunitySkillIdx;

						NETWORK2->QuickSendExcept( hero, (char*)&synMsg, sizeof(synMsg) );		
					}
				}
			}
			break;
		//case NM_PLAYER_MONSTER_ANI_REQ:
		//	{
		//		MSG_REQ_PLAYER_MONSTER_ANI* pReqMsg = (MSG_REQ_PLAYER_MONSTER_ANI*)msgRoot;

		//		try
		//		{
		//			/// 상태 체크
		//			if( hero->GetState() != eOBJECT_STATE_IDLE )
		//				throw 1;	

		//			/// 몬스터 변신체크
		//			if( hero->GetChgMonsterIdx() == 0 )
		//				throw 1;

		//			throw 0;
		//		}
		//		catch ( int error )
		//		{
		//			/// 실패 인경우만 hero에게 전송
		//			HANDLE            handle  = NULL;
		//			MSG_ERROR* sendMsg = (MSG_ERROR*)GetMsgRoot( &handle, perSocketContext, NM_PLAYER, NM_PLAYER_MONSTER_ANI_RES );
		//			if ( sendMsg != NULL )
		//			{
		//				sendMsg->ErrorCode = error;
		//				SendMsgRoot( handle, sizeof(MSG_ERROR) );
		//			}
		//			else
		//			{
		//				assert(NULL);
		//				PostServerEvent("NM_PLAYER_MONSTER_ANI_REQ - GetMsgRoot( &handle, perSocketContext ) error");
		//			}

		//			if( error != 0 )
		//			{
		//				// 성공 인경우 다른 플레이어들에게도 알림
		//				MSG_SYN_PLAYER_MONSTER_ANI synMsg;
		//				synMsg.Category = NM_PLAYER;
		//				synMsg.Protocol = NM_PLAYER_MONSTER_ANI_SYN;
		//				synMsg.mCharacterIdx = heroIdx;
		//				synMsg.mMonsterAni = pReqMsg->mMonsterAni;

		//				NETWORK2->QuickSendExcept( hero, (char*)&synMsg, sizeof(synMsg) );		
		//			}
		//		}
		//	}
		//	break;
		case NM_PLAYER_STATICFACE_REQ:
			{
				MSG_REQ_PLAYER_STATICFACE* pReqMsg = (MSG_REQ_PLAYER_STATICFACE*)msgRoot;

				hero->SetStaticFaceIndex( pReqMsg->mStaticFaceIdx );

				MSG_SYN_PLAYER_STATICFACE synMsg;
				synMsg.Category      = NM_PLAYER;
				synMsg.Protocol      = NM_PLAYER_STATICFACE_SYN;
				synMsg.mCharacterIdx = heroIdx;
				synMsg.mStaticFaceIdx = pReqMsg->mStaticFaceIdx;

				NETWORK2->QuickSend( hero, (char*)&synMsg, sizeof(synMsg) );
			}
			break;
		case NM_PLAYER_COLLECTIONBOOK_OPEN_REQ:
			{
				try
				{
					if( hero->IsRequestRejection() == true )
						throw ERROR_PLAYER_COLLECTIONBOOK_FAIL;

					if( hero->ChangeState( eOBJECT_STATE_STOP ) == false )
						throw ERROR_PLAYER_COLLECTIONBOOK_FAIL;

					hero->SetStateStop( eSTOP_COLLECTIONBOOK );
					throw ERROR_PLAYER_COLLECTIONBOOK_SUCCESS;

				}
				catch ( int error )
				{
					ResMsgError( perSocketContext, NM_PLAYER, NM_PLAYER_COLLECTIONBOOK_OPEN_RES, error );
				}
				
			}
			break;
		case NM_PLAYER_COLLECTIONBOOK_CLOSE_REQ:
			{
				if( hero->GetState() == eOBJECT_STATE_STOP || hero->GetStateStop() == eSTOP_COLLECTIONBOOK )
				{
					hero->ChangeState( eOBJECT_STATE_IDLE );
					hero->SetStateStop( eSTOP_NONE );
					ResMsgError( perSocketContext, NM_PLAYER, NM_PLAYER_COLLECTIONBOOK_CLOSE_RES, ERROR_PLAYER_COLLECTIONBOOK_SUCCESS );
				}
				else
				{
					ResMsgError( perSocketContext, NM_PLAYER, NM_PLAYER_COLLECTIONBOOK_CLOSE_RES, ERROR_PLAYER_COLLECTIONBOOK_FAIL );
				}

			}
			break;
		case NM_PLAYER_TITLE_SET_REQ:
			{
				/// 호칭변경 요청
				MSG_REQ_PLAYER_TITLE_SET* pmsg = (MSG_REQ_PLAYER_TITLE_SET*)msgRoot;

				try
				{
					if( hero->GetStateDie() == true )
						throw ERROR_PLAYER_TITLE_SET_FAIL;

					bool ret = hero->ChangeTitle( pmsg->mTitleIndex );
					throw (ret == true) ? ERROR_PLAYER_TITLE_SET_SUCCESS : ERROR_PLAYER_TITLE_SET_FAIL;
				}
				catch ( int error )
				{
					ResMsgError( perSocketContext, NM_PLAYER, NM_PLAYER_TITLE_SET_RES, error );
				}
			}
			break;
		case NM_PLAYER_TODAYWORD_CHANGE_REQ:
			{
				MSG_REQ_PLAYER_TODAYWORD_CHANGE* pmsg = (MSG_REQ_PLAYER_TODAYWORD_CHANGE*)msgRoot;

				try
				{
					if( hero->GetStateDie() == true )
						throw ERROR_PLAYER_TODAYWORD_CHANGE_FAIL;
	
					hero->ChangeTodayWord( (ULONG_PTR)perSocketContext, pmsg->mWord, pmsg->mWordColor );
				}
				catch ( int error )
				{
					ResMsgError( perSocketContext, NM_PLAYER, NM_PLAYER_TODAYWORD_CHANGE_RES, error );
				}
			}
			break;
		case NM_PLAYER_VEHICLE_END_REQ:
			{
				/// 탈것 해제
				hero->EndVehicleClient( true );
			}
			break;
		case NM_PLAYER_SKILLRESET_REQ:
			/// if( ClientInfo.	/// 사용 플래그 체크
			{
				MSG_REQ_PLAYER_SKILLRESET* pmsg = (MSG_REQ_PLAYER_SKILLRESET*)msgRoot;

				unsigned char error = hero->SkillReset( pmsg->mSlotNum );

				ResMsgError( perSocketContext, NM_PLAYER, NM_PLAYER_SKILLRESET_RES, error );

				if( error == ERROR_SKILLRESET_SUCCESS )
				{					
					/// 사용 플레그 킴
					/// ClientInfo.
				}
			}
			break;
		}
		break; // NM_PLAYER

	case NM_ITEM:
		/*-- ClientInfo::Complete 필터링 - Check / CharacterSelect / GameIn / Inventory
		*/
		if ( (clientInfo->completeData & 0x000F) != 0x000F )
			return false;

		/*-- MSGROOT::Protocol 필터링 - 알수없는 경우 소켓연결을 해제한다.
		*/
		if ( (unsigned char)msgRoot->Protocol >= NM_ITEM_MAX )
		{
			PostServerEvent( "END TASK - UNKNOWN NM_ITEM::PROTOCOL(=%d):MEMBER_IDX(=%d):CHARACTER_IDX(=%d):INETNUM(=%s).",
							 msgRoot->Protocol,
							 clientInfo->memberIdx,
							 clientInfo->characterIdx,
							 inet_ntoa( perSocketContext->addr.sin_addr) );
			return false;
		}

		if ( !clientInfo->complete.inventory )
			return false;

		switch ( msgRoot->Protocol )
		{
		case NM_ITEM_MOV_INVENTORY_REQ:
			{
				MSG_REQ_ITEM_MOV_INVENTORY* msg = (MSG_REQ_ITEM_MOV_INVENTORY*)msgRoot;

				try
				{
					if ( clientInfo->inventoryDatabase )
						throw ERROR_ITEM_MOV_INVENTORY_NOTYET;

					if ( hero->GetStateDie( ) )
						throw ERROR_ITEM_MOV_INVENTORY_FAIL;

					if ( msg->number1 == msg->number2 )
						throw ERROR_ITEM_MOV_INVENTORY_FAIL;

					if ( !hero->IsInventoryRange( msg->number1 ) || !hero->IsInventoryRange( msg->number2 ) )
						throw ERROR_ITEM_MOV_INVENTORY_FAIL;

					if ( hero->IsInventoryEquip( msg->number1 ) && hero->IsInventoryEquip( msg->number2 ) )
						throw ERROR_ITEM_MOV_INVENTORY_FAIL;

					if ( hero->IsInventoryQuest( msg->number1 ) != hero->IsInventoryQuest( msg->number2 ) )
						throw ERROR_ITEM_MOV_INVENTORY_FAIL;

					if ( hero->IsInventoryMall( msg->number1 ) != hero->IsInventoryMall( msg->number2 ) )
						throw ERROR_ITEM_MOV_INVENTORY_FAIL;

					if ( hero->IsInventoryWarehouse( msg->number1 ) != hero->IsInventoryWarehouse( msg->number2 ) )
						throw ERROR_ITEM_MOV_INVENTORY_FAIL;

					TB_INVENTORY* inventory1 = hero->SelectInventory( msg->number1 );
					TB_INVENTORY* inventory2 = hero->SelectInventory( msg->number2 );

					if ( inventory1->apply != InventoryApplyNone || inventory2->apply != InventoryApplyNone )
						throw ERROR_ITEM_MOV_INVENTORY_FAIL;

					/*-- 인벤토리 이동중, 병합 처리.
					*/
					if ( hero->IsInventoryMerge( msg->number1, msg->number2 ) == true )
					{
						clientInfo->inventoryDb.merge = InventoryMerge( perSocketContext, inventory1->idx, inventory2->idx );
						throw ERROR_ITEM_MOV_INVENTORY_MERGE;
					}

					/*-- 인벤토리 이동 처리.
					*/
					unsigned short except = msg->number2;
					int            depth  = 0;

					if ( hero->IsInventoryMove( msg->number1, msg->number2, except, depth ) == false )
						throw ERROR_ITEM_MOV_INVENTORY_FAIL;

					/*-- 'depth' 가 0 일경우 move, 아닐경우 swap 처리 한다.
					'swap' 과 'move' 는 처리 순서가 서로 다르므로 주의해야 한다.
					*/
					if ( depth != 0 )
					{
						// Except DB & SRV
						if ( except != msg->number2 )
						{
							TB_INVENTORY*  exceptInventory = hero->SelectInventory( except );
							short          number          = hero->GetEmptyBagNumber( );

							if ( number == INVENTORY_BAG_NONE )
								throw ERROR_ITEM_MOV_INVENTORY_FAIL;

							clientInfo->inventoryDb.except = InventoryExcept( perSocketContext, heroIdx, exceptInventory->idx, number );
							if ( clientInfo->inventoryDb.except )
							{
								hero->InventoryExcept( except, number );
							}
						}
						// Swap DB & SRV
						clientInfo->inventoryDb.swap = InventorySwap( perSocketContext
																	 ,inventory1->idx
																	 ,inventory1->number
																	 ,inventory2->idx
																	 ,inventory2->number );
						if ( clientInfo->inventoryDb.swap )
						{
							hero->InventorySwap( msg->number1, msg->number2 );
						}
						throw ERROR_ITEM_MOV_INVENTORY_SUCCESS;
					}
					else
					{
						// Move DB & SRV
						clientInfo->inventoryDb.move = InventoryMove( perSocketContext, heroIdx, inventory1->idx, msg->number2 );
						if ( clientInfo->inventoryDb.move )
						{
							hero->InventoryMove( msg->number1, msg->number2 );
						}
						// Except DB & SRV
						if ( except != msg->number2 )
						{
							TB_INVENTORY* releaseInventory = hero->SelectInventory( except );

							clientInfo->inventoryDb.except = InventoryExcept( perSocketContext, heroIdx, releaseInventory->idx, msg->number1 );
							if ( clientInfo->inventoryDb.except )
							{
								hero->InventoryExcept( except, msg->number1 );
							}
						}
						throw ERROR_ITEM_MOV_INVENTORY_SUCCESS;
					}
				}
				catch ( int error )
				{
					ResMsgError( perSocketContext, NM_ITEM, NM_ITEM_MOV_INVENTORY_RES, error );
				}

				if ( hero->IsInventoryEquip( msg->number1 ) || hero->IsInventoryEquip( msg->number2 ) )
				{
					// 착용정보 변경 요청.
					clientInfo->request.characterEquip  = true;
					clientInfo->request.inventoryUpdate = true;
				}
			}
			break;	// switch ( msgRoot->Protocol )
		case NM_ITEM_USE_INVENTORY_REQ:
			try
			{
				if ( clientInfo->inventoryDatabase )
					throw ERROR_ITEM_USE_INVENTORY_NOTYET;

				hero->ItemUseInventory( (ULONG_PTR)perSocketContext, (MSG_REQ_ITEM_USE_INVENTORY*)msgRoot );
				clientInfo->inventoryDb.use = true;
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_ITEM, NM_ITEM_USE_INVENTORY_RES, error );
			}
			break;	// switch ( msgRoot->Protocol )
		case NM_ITEM_DEL_INVENTORY_REQ:
			try
			{
				if ( clientInfo->inventoryDatabase )
					throw ERROR_ITEM_DEL_INVENTORY_NOTYET;

				hero->ItemDelInventory( (ULONG_PTR)perSocketContext, (MSG_REQ_ITEM_DEL_INVENTORY*)msgRoot );
				clientInfo->inventoryDb.remove = true;
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_ITEM, NM_ITEM_DEL_INVENTORY_RES, error );
			}
			break;
		case NM_ITEM_SWI_INVENTORY_REQ:
			try
			{
				hero->ItemSwiInventory( (ULONG_PTR)perSocketContext, (MSG_REQ_ITEM_SWI_INVENTORY*)msgRoot );

				// 착용정보 변경 요청.
				clientInfo->request.characterEquip = true;
				clientInfo->complete.characterActiveWeapon = true;
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_ITEM, NM_ITEM_SWI_INVENTORY_RES, error );
			}
			break;
		case NM_ITEM_DIVIDE_INVENTORY_REQ:
			try
			{
				if ( clientInfo->inventoryDatabase )
					throw ERROR_ITEM_DIVIDE_INVENTORY_NOTYET;

				hero->ItemDivideInventory( (ULONG_PTR)perSocketContext, (MSG_REQ_ITEM_DIVIDE_INVENTORY*)msgRoot );
				clientInfo->inventoryDb.divide = true;
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_ITEM, NM_ITEM_DIVIDE_INVENTORY_RES, error );
			}
			break;
		case NM_ITEM_ENHANCED_START_REQ:
			{
				try
				{
					/// db 사용부 체크
					if ( clientInfo->inventoryDatabase )
						throw ERROR_ITEM_ENHANCED_START_FAIL;

					/// 상태 변경
					if( hero->ChangeState( eOBJECT_STATE_STOP ) == false )
						throw ERROR_ITEM_ENHANCED_START_FAIL;

					hero->SetStateStop( eSTOP_ENHANCED );

					throw ERROR_ITEM_ENHANCED_START_SUCCESS;
				}
				catch ( int error )
				{
					/// 성공 / 실패 메세지 발송
					ResMsgError( perSocketContext, NM_ITEM, NM_ITEM_ENHANCED_START_RES, error );

					if( ERROR_ITEM_ENHANCED_START_SUCCESS == error )
					{
						/// 성공시 모두에게 알림
						MSG_SYN_ITEM_ENHANCED_START sync;
						sync.Category = NM_ITEM;
						sync.Protocol = NM_ITEM_ENHANCED_START_SYN;
						sync.mCharacterIdx = heroIdx;
						QuickSendExcept( hero, (char*)&sync, sizeof( MSG_SYN_ITEM_ENHANCED_START ) );

						/// db 사용 플레그를 킴
						clientInfo->inventoryDb.enhanced = true;
					}
				}
			}
			break;
		case NM_ITEM_ENHANCED_END_REQ:
			if( clientInfo->inventoryDb.enhanced == true )
			{
				try
				{
					if( !( hero->GetState() == eOBJECT_STATE_STOP && hero->GetStateStop() == eSTOP_ENHANCED ) )
						throw ERROR_ITEM_ENHANCED_START_FAIL;

					hero->ItemEnhancedInventory( (ULONG_PTR)perSocketContext, (MSG_REQ_ITEM_ENHANCED_END*)msgRoot );
				}
				catch ( int error )
				{
					hero->ChangeState( eOBJECT_STATE_IDLE );
					hero->SetStateStop( eSTOP_NONE );

					/// 실패 메세지 발송
					ResMsgError( perSocketContext, NM_ITEM, NM_ITEM_ENHANCED_END_RES, error );

					/// 실패를 모두에게 알림
					MSG_SYN_ITEM_ENHANCED_END sync;
					sync.Category = NM_ITEM;
					sync.Protocol = NM_ITEM_ENHANCED_START_SYN;
					sync.mCharacterIdx = heroIdx;
					sync.ErrorCode = ERROR_ITEM_ENHANCED_START_FAIL;
					QuickSendExcept( hero, (char*)&sync, sizeof( MSG_SYN_ITEM_ENHANCED_END ) );	

					/// db 처리부 종료
					clientInfo->inventoryDb.enhanced = false;
				}
			}
			break;
		case NM_ITEM_ENHANCED_CANCEL_REQ:
			if( clientInfo->inventoryDb.enhanced )
			{
				/// 상태 풀어줌
				if( hero->GetState() == eOBJECT_STATE_STOP && hero->GetStateStop() == eSTOP_ENHANCED )
				{
					hero->ChangeState( eOBJECT_STATE_IDLE );
					hero->SetStateStop( eSTOP_NONE );
				}

				/// hero외의 플레이어에게 취소 확인 메세지 발송
				MSG_SYN_ITEM_ENHANCED_CANCEL sync;
				sync.Category = NM_ITEM;
				sync.Protocol = NM_ITEM_ENHANCED_CANCEL_SYN;
				sync.mCharacterIdx = heroIdx;
				QuickSendExcept( hero, (char*)&sync, sizeof( MSG_SYN_ITEM_ENHANCED_CANCEL ) );

				clientInfo->inventoryDb.enhanced = false;
			}
			break;
		case NM_ITEM_DISJOINT_INVENTORY_REQ:
			try
			{
				if ( clientInfo->inventoryDatabase )
					throw ERROR_ITEM_DISJOINT_INVENTORY_NOTYET;

				hero->ItemDisjointInventory( (ULONG_PTR)perSocketContext, (MSG_REQ_ITEM_DISJOINT_INVENTORY*)msgRoot );
				clientInfo->inventoryDb.disjoint = true;
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_ITEM, NM_ITEM_DISJOINT_INVENTORY_RES, error );
			}
			break;
		case NM_ITEM_PUT_CARD_INVENTORY_REQ:
			try
			{
				if ( clientInfo->inventoryDatabase )
					throw ERROR_ITEM_PUT_CARD_INVENTORY_NOTYET;

				hero->ItemPutCardInventory( (ULONG_PTR)perSocketContext, (MSG_REQ_ITEM_PUT_CARD_INVENTORY*)msgRoot );
				clientInfo->inventoryDb.putCard = true;
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_ITEM, NM_ITEM_PUT_CARD_INVENTORY_RES, error );
			}
			break;
		case NM_ITEM_CHANGE_INVENTORY_REQ:
			try
			{
				if ( clientInfo->inventoryDatabase )
					throw ERROR_ITEM_PUT_CARD_INVENTORY_NOTYET;

				hero->ItemChangeInventory( (ULONG_PTR)perSocketContext, (MSG_REQ_ITEM_CHANGE_INVENTORY*)msgRoot );
				clientInfo->inventoryDb.change = true;
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_ITEM, NM_ITEM_CHANGE_INVENTORY_RES, error );
			}
			break;

		/*-- 거래-사용자간 [요청 / 응답 / 등록 / 삭제 / 수정 / 완료 / 재수정 / 취소 / 종료]
		*/
		case NM_ITEM_EXCHANGE_ASK_REQ:
			try
			{
				if ( clientInfo->complete.pvpJoin )
					throw ERROR_ITEM_EXCHANGE_ASK_FAIL;

				hero->ExchangeAsk( (MSG_REQ_ITEM_EXCHANGE_ASK*)msgRoot );
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_ITEM, NM_ITEM_EXCHANGE_ASK_RES, error );
			}
			break;
		case NM_ITEM_EXCHANGE_REP_REQ:
			try
			{
				hero->ExchangeReply( (MSG_REQ_ITEM_EXCHANGE_REP*)msgRoot );
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_ITEM, NM_ITEM_EXCHANGE_REP_RES, error );
			}
			break;
		case NM_ITEM_EXCHANGE_ADD_REQ:
			try
			{
				hero->ExchangeAdd( (MSG_REQ_ITEM_EXCHANGE_ADD*)msgRoot );
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_ITEM, NM_ITEM_EXCHANGE_ADD_RES, error );
			}
			break;
		case NM_ITEM_EXCHANGE_DEL_REQ:
			try
			{
				hero->ExchangeDel( (MSG_REQ_ITEM_EXCHANGE_DEL*)msgRoot );
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_ITEM, NM_ITEM_EXCHANGE_DEL_RES, error );
			}
			break;
		case NM_ITEM_EXCHANGE_OK_REQ:
			try
			{
				hero->ExchangeOk( (MSG_REQ_ITEM_EXCHANGE_OK*)msgRoot );
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_ITEM, NM_ITEM_EXCHANGE_OK_RES, error );
			}
			break;
		case NM_ITEM_EXCHANGE_RETRY_REQ:
			try
			{
				hero->ExchangeRetry( (MSG_REQ_ITEM_EXCHANGE_RETRY*)msgRoot );
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_ITEM, NM_ITEM_EXCHANGE_RETRY_RES, error );
			}
			break;
		case NM_ITEM_EXCHANGE_CANCEL_REQ:
			try
			{
				hero->ExchangeCancel( (MSG_REQ_ITEM_EXCHANGE_CANCEL*)msgRoot );
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_ITEM, NM_ITEM_EXCHANGE_CANCEL_RES, error );
			}
			break;
		case NM_ITEM_EXCHANGE_END_REQ:
			try
			{
				hero->ExchangeEnd( (MSG_REQ_ITEM_EXCHANGE_END*)msgRoot );
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_ITEM, NM_ITEM_EXCHANGE_END_RES, error );
			}
			break;

		/*-- 노점-판매 관련 [검색 / 열기 / 닫기 / 등록 / 삭제 / 수정 / 참여 / 닫기 / 구입]
		*/
		case NM_ITEM_STALL_SELL_SEARCH_REQ:
			try
			{
				if ( clientInfo->commonDb.stallSellSearch )
					throw ERROR_ITEM_STALL_SELL_SEARCH_NOTYET;

				hero->StallSellSearch( (ULONG_PTR)perSocketContext, (MSG_REQ_ITEM_STALL_SELL_SEARCH*)msgRoot );
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_ITEM, NM_ITEM_STALL_SELL_SEARCH_RES, error );
			}
			break;
		case NM_ITEM_STALL_SELL_OPEN_REQ:
			try
			{
				if ( clientInfo->complete.pvpJoin )
					throw ERROR_ITEM_STALL_SELL_OPEN_FAIL;

				hero->StallSellOpen( (ULONG_PTR)perSocketContext, (MSG_REQ_ITEM_STALL_SELL_OPEN*)msgRoot );
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_ITEM, NM_ITEM_STALL_SELL_OPEN_RES, error );
			}
			break;
		case NM_ITEM_STALL_SELL_CLOSE_REQ:
			try
			{
				hero->StallSellClose( (ULONG_PTR)perSocketContext, (MSG_REQ_ITEM_STALL_SELL_CLOSE*)msgRoot );
				clientInfo->commonDb.stallSellClear = true;
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_ITEM, NM_ITEM_STALL_SELL_CLOSE_RES, error );
			}
			break;
		case NM_ITEM_STALL_SELL_RENAME_REQ:
			try
			{
				hero->StallSellRename( (ULONG_PTR)perSocketContext, (MSG_REQ_ITEM_STALL_SELL_RENAME*)msgRoot );
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_ITEM, NM_ITEM_STALL_SELL_RENAME_RES, error );
			}
			break;
		case NM_ITEM_STALL_SELL_ADD_REQ:
			try
			{
				hero->StallSellAdd( (ULONG_PTR)perSocketContext, (MSG_REQ_ITEM_STALL_SELL_ADD*)msgRoot );
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_ITEM, NM_ITEM_STALL_SELL_ADD_RES, error );
			}
			break;
		case NM_ITEM_STALL_SELL_DEL_REQ:
			try
			{
				hero->StallSellDel( (ULONG_PTR)perSocketContext, (MSG_REQ_ITEM_STALL_SELL_DEL*)msgRoot );
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_ITEM, NM_ITEM_STALL_SELL_DEL_RES, error );
			}
			break;
		case NM_ITEM_STALL_SELL_MOD_REQ:
			try
			{
				hero->StallSellMod( (ULONG_PTR)perSocketContext, (MSG_REQ_ITEM_STALL_SELL_MOD*)msgRoot );
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_ITEM, NM_ITEM_STALL_SELL_MOD_RES, error );
			}
			break;
		case NM_ITEM_STALL_SELL_JOIN_REQ:
			try
			{
				if ( clientInfo->complete.pvpJoin )
					throw ERROR_ITEM_STALL_SELL_JOIN_FAIL;

				hero->StallSellJoin( (ULONG_PTR)perSocketContext, (MSG_REQ_ITEM_STALL_SELL_JOIN*)msgRoot );
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_ITEM, NM_ITEM_STALL_SELL_JOIN_RES, error );
			}
			break;
		case NM_ITEM_STALL_SELL_LEAVE_REQ:
			try
			{
				hero->StallSellLeave( (ULONG_PTR)perSocketContext, (MSG_REQ_ITEM_STALL_SELL_LEAVE*)msgRoot );
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_ITEM, NM_ITEM_STALL_SELL_LEAVE_RES, error );
			}
			break;
		case NM_ITEM_STALL_SELL_GET_REQ:
			try
			{
				hero->StallSellGet( (ULONG_PTR)perSocketContext, (MSG_REQ_ITEM_STALL_SELL_GET*)msgRoot );
				clientInfo->inventoryDb.stallSellGet = true;
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_ITEM, NM_ITEM_STALL_SELL_GET_RES, error );
			}
			break;

		/*-- 줍기 관련 [열기 / 닫기 / 줍기]
		*/
		case NM_ITEM_GET_OPEN_REQ:
			try
			{
				hero->ItemGetOpen( (MSG_REQ_ITEM_GET_OPEN*)msgRoot );
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_ITEM, NM_ITEM_GET_OPEN_RES, error );
			}
			break;
		case NM_ITEM_GET_CLOSE_REQ:
			try
			{
				hero->ItemGetClose( (MSG_REQ_ITEM_GET_CLOSE*)msgRoot );
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_ITEM, NM_ITEM_GET_CLOSE_RES, error );
			}
			break;
		case NM_ITEM_GET_REQ:
			try
			{
				if ( clientInfo->inventoryDatabase )
					throw ERROR_ITEM_GET_FAIL;

				hero->ItemGet( (MSG_REQ_ITEM_GET*)msgRoot );
				clientInfo->inventoryDb.get = true;
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_ITEM, NM_ITEM_GET_RES, error );
			}
			break;

		case NM_ITEM_COLLECT_REQ:
			try
			{
				if ( clientInfo->inventoryDatabase )
					throw ERROR_ITEM_COLLECT_NOTYET;

				hero->ItemCollect( (ULONG_PTR)perSocketContext, (MSG_REQ_ITEM_COLLECT*)msgRoot );
				clientInfo->inventoryDb.collect = true;
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_ITEM, NM_ITEM_COLLECT_RES, error );
			}
			break;
		}
		break;	// switch ( msgRoot->Category )

	case NM_CHAT:
		/*-- ClientInfo::Complete 필터링 - Check / CharacterSelect / GameIn
		*/
		if ( (clientInfo->completeData & 0x0007) != 0x0007 )
			return false;

		/*-- MSGROOT::Protocol 필터링 - 알수없는 경우 소켓연결을 해제한다.
		*/
		if ( (unsigned char)msgRoot->Protocol >= NM_CHAT_MAX )
		{
			PostServerEvent( "END TASK - UNKNOWN NM_CHAT::PROTOCOL(=%d):MEMBER_IDX(=%d):CHARACTER_IDX(=%d):INETNUM(=%s).",
							 msgRoot->Protocol,
							 clientInfo->memberIdx,
							 clientInfo->characterIdx,
							 inet_ntoa( perSocketContext->addr.sin_addr) );
			return false;
		}

		// 채팅지연.
		if ( clientInfo->delay.chatting > currentTick )
			return true;
		else
			clientInfo->delay.chatting = (currentTick + 100);

		switch( msgRoot->Protocol )
		{
		case NM_CHAT_NORMAL_REQ:
			if ( hero->IsBlock( ) == true )
			{
				HANDLE        handle = NULL;
				MSG_RES_CHAT* msg    = (MSG_RES_CHAT*)GetMsgRoot( &handle, perSocketContext, NM_CHAT, NM_CHAT_NORMAL_RES );

				struct tm validThru;
				validThru = *localtime( &hero->mBlockValidThru );

				msg->ErrorCode = ERROR_CHAT_BLOCK;
				msg->validTrue.wYear   = (WORD)(validThru.tm_year + 1900);
				msg->validTrue.wMonth  = (WORD)(validThru.tm_mon  + 1);
				msg->validTrue.wDay    = (WORD)validThru.tm_mday;
				msg->validTrue.wHour   = (WORD)validThru.tm_hour;
				msg->validTrue.wMinute = (WORD)validThru.tm_min;
				msg->validTrue.wSecond = (WORD)validThru.tm_sec;

				SendMsgRoot( handle, sizeof(MSG_RES_CHAT) );
			}
			else if ( packetLen <= sizeof(MSG_REQ_CHAT) )
			{
				MSG_REQ_CHAT* recvMsg = (MSG_REQ_CHAT*)msgRoot;
				recvMsg->Message[ MAX_CHAT_SIZE ] = 0;	// NULL 오류방지.

				MSG_SYN_CHAT sendMsg;
				memset( &sendMsg, 0, sizeof(sendMsg) );

				sendMsg.Category     = NM_CHAT;
				sendMsg.Protocol     = NM_CHAT_NORMAL_SYN;
				sendMsg.CharacterIdx = heroIdx;
				sendMsg.emotionIdx	 = recvMsg->emotionIdx;
				wcscpy( sendMsg.Name, hero->GetName( ) );
				wcscpy( sendMsg.Message, recvMsg->Message );

				TB_INVENTORY* inventory = hero->SelectInventory( recvMsg->InventoryNumber );
				if( hero->IsInventory( inventory ) == true )
					Inventory2sInventory( &sendMsg.inventory, inventory );

				/// pvp 팀 메세지 발송체크
				if( hero->SendPvPTeamChat( (char*)&sendMsg, sendMsg.GetMsgLength( ) ) == false )
                    QuickSend( hero, (char*)&sendMsg, sendMsg.GetMsgLength( ) );
			}
			break;

		case NM_CHAT_SHOUT_REQ:

			/// pvp 중이면 외치기 불가
			if( hero->GetPvPDMIdx() != 0 )
				break;
			
			if ( hero->IsBlock( ) == true )
			{
				HANDLE        handle = NULL;
				MSG_RES_CHAT* msg    = (MSG_RES_CHAT*)GetMsgRoot( &handle, perSocketContext, NM_CHAT, NM_CHAT_NORMAL_RES );

				struct tm validThru;
				validThru = *localtime( &hero->mBlockValidThru );

				msg->ErrorCode = ERROR_CHAT_BLOCK;
				msg->validTrue.wYear   = (WORD)(validThru.tm_year + 1900);
				msg->validTrue.wMonth  = (WORD)(validThru.tm_mon  + 1);
				msg->validTrue.wDay    = (WORD)validThru.tm_mday;
				msg->validTrue.wHour   = (WORD)validThru.tm_hour;
				msg->validTrue.wMinute = (WORD)validThru.tm_min;
				msg->validTrue.wSecond = (WORD)validThru.tm_sec;

				SendMsgRoot( handle, sizeof(MSG_RES_CHAT) );
			}
			else if ( packetLen <= sizeof(MSG_REQ_CHAT_SHOUT) )
			{
				MSG_REQ_CHAT_SHOUT* recvMsg = (MSG_REQ_CHAT_SHOUT*)msgRoot;
				recvMsg->Message[ MAX_CHAT_SIZE ] = 0;	// NULL 오류방지.

				MSG_SYN_CHAT_SHOUT sendMsg;
				memset( &sendMsg, 0, sizeof(sendMsg) );

				sendMsg.Category     = NM_CHAT;
				sendMsg.Protocol     = NM_CHAT_SHOUT_SYN;
				sendMsg.CharacterIdx = heroIdx;
				wcscpy( sendMsg.Name, hero->GetName( ) );
				wcscpy( sendMsg.Message, recvMsg->Message );

				QuickSendMap( hero, (char*)&sendMsg, sendMsg.GetMsgLength( ) );

				/// 전체 쏘기 (맵, 채널, 서버)
				cSender* sender = g_gameSrv->GetSender( );
				if ( sender != NULL )
					sender->PostChatShout( MAKECID((WORD)mServerNum,(WORD)mChannelNum) , hero->GetMapNumber( ), sendMsg.CharacterIdx, sendMsg.Name, sendMsg.Message );
			}
			break;

		case NM_CHAT_GUILD_REQ:
			/// pvp 중이면 길드말 불가
			if( hero->GetPvPDMIdx() != 0 )
				break;

			if ( hero->IsBlock( ) == true )
			{
				HANDLE        handle = NULL;
				MSG_RES_CHAT* msg    = (MSG_RES_CHAT*)GetMsgRoot( &handle, perSocketContext, NM_CHAT, NM_CHAT_NORMAL_RES );

				struct tm validThru;
				validThru = *localtime( &hero->mBlockValidThru );

				msg->ErrorCode = ERROR_CHAT_BLOCK;
				msg->validTrue.wYear   = (WORD)(validThru.tm_year + 1900);
				msg->validTrue.wMonth  = (WORD)(validThru.tm_mon  + 1);
				msg->validTrue.wDay    = (WORD)validThru.tm_mday;
				msg->validTrue.wHour   = (WORD)validThru.tm_hour;
				msg->validTrue.wMinute = (WORD)validThru.tm_min;
				msg->validTrue.wSecond = (WORD)validThru.tm_sec;

				SendMsgRoot( handle, sizeof(MSG_RES_CHAT) );
			}
			else if ( packetLen <= sizeof(MSG_REQ_CHAT_GUILD) )
			{
				MSG_REQ_CHAT_GUILD* recvMsg = (MSG_REQ_CHAT_GUILD*)msgRoot;
				recvMsg->Message[ MAX_CHAT_SIZE ] = 0;	// NULL 오류방지.

				mpGuildManager->SendGuildChat( hero, recvMsg->Message, recvMsg->InventoryNumber );

				/// SYN : 길드 채팅
				TB_INVENTORY*	inventory = hero->SelectInventory( recvMsg->InventoryNumber );
				sInventory		heroInven;
				memset( &heroInven, 0, sizeof(heroInven) );

				if( hero->IsInventory( inventory ) == true )
				{
					Inventory2sInventory( &heroInven, inventory );
				}

				if( PostGuildChat( hero->GetGuildIndex(), hero->GetName(), recvMsg->Message, &heroInven ) == false )
				{
					PostGuildEvent( hero->GetObjectID(), hero->GetGuildIndex(), hero->GetGuildPosition(), "FAIL - PostGuildChat()" );
				}
			}
			break;

		case NM_CHAT_TRADE_REQ:
			if ( hero->IsBlock( ) == true )
			{
				HANDLE        handle = NULL;
				MSG_RES_CHAT* msg    = (MSG_RES_CHAT*)GetMsgRoot( &handle, perSocketContext, NM_CHAT, NM_CHAT_NORMAL_RES );

				struct tm validThru;
				validThru = *localtime( &hero->mBlockValidThru );

				msg->ErrorCode = ERROR_CHAT_BLOCK;
				msg->validTrue.wYear   = (WORD)(validThru.tm_year + 1900);
				msg->validTrue.wMonth  = (WORD)(validThru.tm_mon  + 1);
				msg->validTrue.wDay    = (WORD)validThru.tm_mday;
				msg->validTrue.wHour   = (WORD)validThru.tm_hour;
				msg->validTrue.wMinute = (WORD)validThru.tm_min;
				msg->validTrue.wSecond = (WORD)validThru.tm_sec;

				SendMsgRoot( handle, sizeof(MSG_RES_CHAT) );
			}
			else if( packetLen <= sizeof(MSG_REQ_CHAT_TRADE) )
			{
				MSG_REQ_CHAT_TRADE* recvMsg = (MSG_REQ_CHAT_TRADE*)msgRoot;
				recvMsg->Message[ MAX_CHAT_SIZE ] = 0;

				MSG_SYN_CHAT_TRADE sendMsg;
				memset( &sendMsg, 0, sizeof(sendMsg) );

				sendMsg.Category	= NM_CHAT;
				sendMsg.Protocol	= NM_CHAT_TRADE_SYN;
				wcscpy( sendMsg.Name, hero->GetName() );
				wcscpy( sendMsg.Message, recvMsg->Message );

				TB_INVENTORY*	inventory = hero->SelectInventory( recvMsg->InventoryNumber );
				if( hero->IsInventory( inventory ) == true )
					Inventory2sInventory( &sendMsg.inventory, inventory );

				QuickSendMap( hero, (char*)&sendMsg, sendMsg.GetMsgLength() );
			}
			break;

		case NM_CHAT_WHISPER_REQ:
			if ( hero->IsBlock( ) == true )
			{
				HANDLE        handle = NULL;
				MSG_RES_CHAT* msg    = (MSG_RES_CHAT*)GetMsgRoot( &handle, perSocketContext, NM_CHAT, NM_CHAT_NORMAL_RES );

				struct tm validThru;
				validThru = *localtime( &hero->mBlockValidThru );

				msg->ErrorCode = ERROR_CHAT_BLOCK;
				msg->validTrue.wYear   = (WORD)(validThru.tm_year + 1900);
				msg->validTrue.wMonth  = (WORD)(validThru.tm_mon  + 1);
				msg->validTrue.wDay    = (WORD)validThru.tm_mday;
				msg->validTrue.wHour   = (WORD)validThru.tm_hour;
				msg->validTrue.wMinute = (WORD)validThru.tm_min;
				msg->validTrue.wSecond = (WORD)validThru.tm_sec;

				SendMsgRoot( handle, sizeof(MSG_RES_CHAT) );
			}
			else if ( packetLen <= sizeof(MSG_REQ_CHAT_SHOUT) )
			{
				MSG_REQ_CHAT_WHISPER* recvMsg  = (MSG_REQ_CHAT_WHISPER*)msgRoot;

				recvMsg->Name[ MAX_NAME_SIZE ]    = 0;  // NULL 오류방지.
				recvMsg->Message[ MAX_CHAT_SIZE ] = 0;  // "

				if ( wcslen( recvMsg->Name ) > 0 && (recvMsg->InventoryNumber != (-1) || wcslen( recvMsg->Message ) > 0) && wcsicmp( recvMsg->Name, hero->GetName( ) ) != 0 )
				{
					cPlayer* findPlayer = mpObjectManager->GetPlayer( recvMsg->Name );

					/// 내 채널 접속중
					if ( findPlayer != NULL )
					{
						// 받는 캐릭터에게.
						/// 실제 게임 접속중인지 검사
						cPlayer* gameInUser = mpGridManager->GetPlayer( findPlayer->GetObjectID() );
						if( gameInUser != NULL )
						{
							// 옵션 상태 검사
							sGameOption* toOption1 = gameInUser->GetOptionData();
							if( toOption1->option1.rejectionWhisper == false )	
							{
								HANDLE                handle  = NULL;
								MSG_SYN_CHAT_WHISPER* sendMsg = (MSG_SYN_CHAT_WHISPER*)GetMsgRoot( &handle, gameInUser->GetConnectionIdx( ), NM_CHAT, NM_CHAT_WHISPER_SYN );
								if ( sendMsg != NULL )
								{
									/// 링크 아이템 정보 
									TB_INVENTORY* inventory = hero->SelectInventory( recvMsg->InventoryNumber );
									if ( hero->IsInventory( inventory ) == true )
										Inventory2sInventory( &sendMsg->inventory, inventory );

									wcscpy( sendMsg->Name, hero->GetName( ) );			// 보낸 캐릭터 이름
									wcscpy( sendMsg->Message, recvMsg->Message );		// 보낸 메시지
									SendMsgRoot( handle, sendMsg->GetMsgLength( ) );	// 발송.
								}
							}
						}

						// 보낸 캐릭터에게.
						if ( hero != NULL )
						{
							HANDLE                handle  = NULL;
							MSG_RES_CHAT_WHISPER* sendMsg = (MSG_RES_CHAT_WHISPER*)GetMsgRoot( &handle, perSocketContext, NM_CHAT, NM_CHAT_WHISPER_RES );

							/// 링크 아이템 정보 
							TB_INVENTORY* inventory = hero->SelectInventory( recvMsg->InventoryNumber );

							if ( hero->IsInventory( inventory ) == true )
								Inventory2sInventory( &sendMsg->inventory, inventory );

							if ( gameInUser != NULL )
							{
								sGameOption* toOption1 = gameInUser->GetOptionData( );
								sendMsg->ErrorCode = (toOption1->option1.rejectionWhisper == true) ? ERROR_CHAT_WHISPER_OPTIONREFUSE : ERROR_CHAT_WHISPER_SUCCESS;
								wcscpy( sendMsg->Name, recvMsg->Name );				// 받는 캐릭터이름.
								wcscpy( sendMsg->Message, recvMsg->Message );		// 보낸 메시지.
								SendMsgRoot( handle, sendMsg->GetMsgLength( ) );	// 발송.
							}
							else
							{
								sendMsg->ErrorCode = ERROR_CHAT_WHISPER_DISCONNECT;
								SendMsgRoot( handle, sendMsg->GetMsgLength( ) );	// 발송.
							}
						}
					}
					/// 내 채널 접속중 아님.
					else
					{
						if( !clientInfo->commonDb.memberSelect )
						{
							/// 내 채널 접속중이 아니면, DB 검색
							clientInfo->commonDb.memberSelect = WhisperMemberSelect( perSocketContext, recvMsg->Name, recvMsg->Message, recvMsg->InventoryNumber );
						}
					}
				}
			}
			break;

		case NM_CHAT_NOTE_SEND_REQ:
			if ( !clientInfo->commonDb.noteInsert )
			{
				MSG_REQ_NOTE_SEND* recvMsg  = (MSG_REQ_NOTE_SEND*)msgRoot;
				wchar_t*           heroName = hero->GetName( );

				recvMsg->Name[ MAX_NAME_SIZE ] = 0;
				recvMsg->Message[ MAX_NOTE_MESSAGE_SIZE ] = 0;

				if ( wcslen( recvMsg->Name ) > 0 && wcslen( recvMsg->Message ) > 0 && wcsicmp( recvMsg->Name, heroName ) != 0 )
				{
					clientInfo->commonDb.noteInsert = NoteInsert( perSocketContext, heroIdx, recvMsg->Name, recvMsg->Message );
				}

				//cPlayer* player = mpObjectManager->GetPlayer( recvMsg->Name );
				//if( player != NULL )
				//{
				//	sGameOption* toOption1 = player->GetOptionData();
				//	if( toOption1->option1.rejectionNote == false )	
				//	{
				//		clientInfo->commonDb.noteInsert = NoteInsert( perSocketContext, heroIdx, recvMsg->Name, recvMsg->Message );
				//	}
				//	else
				//	{
				//		/// 거부상태
				//		ResMsgError( perSocketContext, NM_CHAT, NM_CHAT_NOTE_SEND_RES, ERROR_CHAT_NOTE_OPTIONREFUSE );
				//	}
				//}
			}
			break;
		case NM_CHAT_NOTE_DEL_REQ:
			if ( !clientInfo->commonDb.noteDelete )
			{
				MSG_REQ_NOTE_DEL* msg = (MSG_REQ_NOTE_DEL*)msgRoot;
				clientInfo->commonDb.noteDelete = NoteDelete( perSocketContext, heroIdx, msg->RowCount, msg->Idx );
			}
			break;
		case NM_CHAT_GUILDNOTE_SEND_REQ:
			{
				MSG_REQ_NOTE_SEND* recvMsg = (MSG_REQ_NOTE_SEND*)msgRoot;
				try
				{
					// DB 사용중인지 체크
					if ( clientInfo->commonDb.noteInsert )
						throw ERROR_CHAT_NOTE_UNKNOWN;

					recvMsg->Name[ MAX_NAME_SIZE ] = 0;
					recvMsg->Message[ MAX_NOTE_MESSAGE_SIZE ] = 0;

					if ( wcslen( recvMsg->Message ) == 0 )
						throw ERROR_CHAT_NOTE_UNKNOWN;
					
					// 길드 가입 검사
					unsigned long guildIdx = hero->GetGuildIndex();
					if( guildIdx == 0 )
						throw ERROR_CHAT_NOTE_GUILD;

					cGuild* guild = mpGuildManager->GetGuild( guildIdx );
					if( guild == NULL )
						throw ERROR_CHAT_NOTE_UNKNOWN;

					long rowCount = 0;
					long userIdx[50];
					memset( userIdx, 0, sizeof(userIdx) );

					/// 인덱스 담기
					typedef tPointerHashMap<unsigned long, void*> cGuildUserMap;
					cGuildUserMap* mUserMap = guild->GetGuildUser();
					if( mUserMap != NULL )
					{
						cGuildUserMap::cIterator i = mUserMap->Begin();
						cGuildUserMap::cIterator end = mUserMap->End();
						for( ; i != end; ++i )
						{
							TB_GUILD_USER* p = (TB_GUILD_USER*)(i->mSecond);
							userIdx[ rowCount ] = p->characterIdx;

							rowCount++;
						}
					}

					clientInfo->commonDb.noteInsert = GuildNoteInsert( perSocketContext, heroIdx, recvMsg->Message, rowCount, userIdx );
				}
				catch ( int error )
				{
					ResMsgError( perSocketContext, NM_CHAT, NM_CHAT_GUILDNOTE_SEND_RES, error );
				}
			}
			break;
		case NM_CHAT_FRIEND_LIST_REQ:
			if ( !clientInfo->commonDb.friendSelect )
			{
				FriendSelect( perSocketContext, heroIdx );
			}
			break;
		case NM_CHAT_FRIEND_ADD_REQ:
			if ( packetLen <= sizeof(MSG_REQ_FRIEND_ADD) && !clientInfo->commonDb.friendInsert )
			{
				MSG_REQ_FRIEND_ADD* recvMsg  = (MSG_REQ_FRIEND_ADD*)msgRoot;
				wchar_t*            heroName = hero->GetName( );

				recvMsg->Name[ MAX_NAME_SIZE ] = 0; // NULL 오류방지.

				// 동일 이름일경우 & 이름이 없는 경우 무시 .
				if ( wcslen( recvMsg->Name ) > 0 && wcsicmp( recvMsg->Name, heroName ) != 0 )
				{
					HANDLE         handle       = NULL;
					FRIEND_INSERT* friendInsert = (FRIEND_INSERT*)GetSQL( &handle, SQL_GAME_PROCESS_FRIEND_INSERT );

					friendInsert->characterIdx1 = heroIdx;
					wcscpy( friendInsert->characterName, recvMsg->Name );

					SendSQL( perSocketContext, handle, sizeof(FRIEND_INSERT), COMMON_DB_FRIEND_INSERT );
				}
			}
			break;
		case NM_CHAT_FRIEND_DEL_REQ:
			if ( !clientInfo->commonDb.friendDelete )
			{
				MSG_REQ_FRIEND_DEL* msg = (MSG_REQ_FRIEND_DEL*)msgRoot;

				HANDLE         handle       = NULL;
				FRIEND_DELETE* friendDelete = (FRIEND_DELETE*)GetSQL( &handle, SQL_GAME_PROCESS_FRIEND_DELETE );

				friendDelete->idx           = msg->Idx;
				friendDelete->characterIdx1 = heroIdx;

				SendSQL( perSocketContext, handle, sizeof(FRIEND_DELETE), COMMON_DB_FRIEND_DELETE );
			}
			break;
		case NM_CHAT_FRIEND_REP_REQ:
			if ( !clientInfo->commonDb.friendUpdate )
			{
				MSG_REQ_FRIEND_REP* msg = (MSG_REQ_FRIEND_REP*)msgRoot;

				HANDLE         handle       = NULL;
				FRIEND_UPDATE* friendUpdate = (FRIEND_UPDATE*)GetSQL( &handle, SQL_GAME_PROCESS_FRIEND_UPDATE );

				friendUpdate->idx           = msg->Idx;
				friendUpdate->characterIdx1 = heroIdx;

				SendSQL( perSocketContext, handle, sizeof(FRIEND_UPDATE), COMMON_DB_FRIEND_UPDATE );
			}
			break;

		case NM_CHAT_PARTY_REQ:
			if ( hero->IsBlock( ) == true )
			{
				HANDLE        handle = NULL;
				MSG_RES_CHAT* msg    = (MSG_RES_CHAT*)GetMsgRoot( &handle, perSocketContext, NM_CHAT, NM_CHAT_NORMAL_RES );

				struct tm validThru;
				validThru = *localtime( &hero->mBlockValidThru );

				msg->ErrorCode = ERROR_CHAT_BLOCK;
				msg->validTrue.wYear   = (WORD)(validThru.tm_year + 1900);
				msg->validTrue.wMonth  = (WORD)(validThru.tm_mon  + 1);
				msg->validTrue.wDay    = (WORD)validThru.tm_mday;
				msg->validTrue.wHour   = (WORD)validThru.tm_hour;
				msg->validTrue.wMinute = (WORD)validThru.tm_min;
				msg->validTrue.wSecond = (WORD)validThru.tm_sec;

				SendMsgRoot( handle, sizeof(MSG_RES_CHAT) );
			}
			else if ( packetLen <= sizeof(MSG_REQ_CHAT_PARTY) )
			{
				MSG_REQ_CHAT_PARTY* recvMsg = (MSG_REQ_CHAT_PARTY*)msgRoot;
				recvMsg->Message[ MAX_CHAT_SIZE ] = 0;

				TB_INVENTORY*	inventory = hero->SelectInventory( recvMsg->InventoryNumber );
				bool IsInventory = hero->IsInventory( inventory );

				/// 보내야할 인벤정보가 있으면
				if ( wcslen( recvMsg->Message ) > 0 || IsInventory )
				{
					// 파티가 있으면 send
					unsigned long partyIndex = hero->GetPartyIndex( );
					cParty* party = mpPartyManager->GetParty( partyIndex );
					if ( party != NULL )
					{
						unsigned long* userArr = party->GetUserArr( );
						unsigned int   count   = party->GetCount( );

						for ( unsigned int i = 0; i < count; ++i, ++userArr )
						{ 
							cPlayer* user = OBJECTMANAGER->GetPlayer( (*userArr) );
							if ( user != NULL )
							{
								HANDLE              handle  = NULL;
								MSG_SYN_CHAT_PARTY* sendMsg = (MSG_SYN_CHAT_PARTY*)GetMsgRoot( &handle, user->GetConnectionIdx( ), NM_CHAT, NM_CHAT_PARTY_SYN );
								if ( sendMsg != NULL )
								{
									wcscpy( sendMsg->Name, hero->GetName( ) );
									wcscpy( sendMsg->Message, recvMsg->Message );

									if( IsInventory )
										Inventory2sInventory( &sendMsg->inventory, inventory );

									SendMsgRoot( handle, sendMsg->GetMsgLength( ) );
								}
							}
						}
					}

				}
			}
			break;

		case NM_CHAT_GM_NORMAL_REQ:
			if ( packetLen <= sizeof(MSG_REQ_CHAT) )
			{
				MSG_REQ_CHAT* recvMsg = (MSG_REQ_CHAT*)msgRoot;
				recvMsg->Message[ MAX_CHAT_SIZE ] = 0;	// NULL 오류방지.

				MSG_SYN_CHAT sendMsg;
				memset( &sendMsg, 0, sizeof(sendMsg) );

				sendMsg.Category     = NM_CHAT;
				sendMsg.Protocol     = NM_CHAT_GM_NORMAL_SYN;
				sendMsg.CharacterIdx = heroIdx;
				sendMsg.emotionIdx	 = recvMsg->emotionIdx;
				wcscpy( sendMsg.Name, hero->GetName( ) );
				wcscpy( sendMsg.Message, recvMsg->Message );

				TB_INVENTORY*	inventory = hero->SelectInventory( recvMsg->InventoryNumber );
				if( hero->IsInventory( inventory ) == true )
					Inventory2sInventory( &sendMsg.inventory, inventory );

				/// pvp 팀 메세지 발송체크
				if( hero->SendPvPTeamChat( (char*)&sendMsg, sendMsg.GetMsgLength( ) ) == false )
					QuickSend( hero, (char*)&sendMsg, sendMsg.GetMsgLength( ) );
			}
			break;
		}
		break; // NM_CHAT

	case NM_SKILL:
		/*-- ClientInfo::Complete 필터링 - Check / CharacterSelect / GameIn
		*/
		if ( (clientInfo->completeData & 0x0007) != 0x0007 )
			return false;

		/*-- MSGROOT::Protocol 필터링 - 알수없는 경우 소켓연결을 해제한다.
		*/
		if ( (unsigned char)msgRoot->Protocol >= NM_SKILL_MAX )
		{
			PostServerEvent( "END TASK - UNKNOWN NM_SKILL::PROTOCOL(=%d):MEMBER_IDX(=%d):CHARACTER_IDX(=%d):INETNUM(=%s).",
							 msgRoot->Protocol,
							 clientInfo->memberIdx,
							 clientInfo->characterIdx,
							 inet_ntoa( perSocketContext->addr.sin_addr) );
			return false;
		}

		switch( msgRoot->Protocol )
		{
		case NM_SKILL_USED_REQ:
			{
				MSG_REQ_SKILL_USED* pmsg = (MSG_REQ_SKILL_USED*)msgRoot;

				// 스킬공격과 노멀 공격 구분
				if( pmsg->mSkillClassIndex <= NORMAL_ATTACK_SKILL_MAX )
				{
					SKILLMANAGER->PlayerNormalAttackRequest( heroIdx, pmsg );
				}
				else
				{
					SKILLMANAGER->PlayerObjectSkillRequest( heroIdx, pmsg, false );					
				}
			}
			break;
		case NM_SKILL_CAST_USED_REQ:
			{
				MSG_REQ_SKILL_USED* pmsg = (MSG_REQ_SKILL_USED*)msgRoot;
				SKILLMANAGER->PlayerObjectSkillRequest( heroIdx, pmsg, true );
			}
			break;
		case NM_SKILL_POS_USED_REQ:
			{
				MSG_REQ_POS_USED* pmsg = (MSG_REQ_POS_USED*)msgRoot;
				SKILLMANAGER->PlayerPositionSkillRequest( heroIdx, pmsg, false );
			}break;
		case NM_SKILL_POS_CAST_USED_REQ:
			{
				MSG_REQ_POS_USED* pmsg = (MSG_REQ_POS_USED*)msgRoot;
				SKILLMANAGER->PlayerPositionSkillRequest( heroIdx, pmsg, true );
			}break;
		case NM_SKILL_USED_MON_REQ:
			{
				MSG_REQ_SKILL_USED* pmsg = (MSG_REQ_SKILL_USED*)msgRoot;
				SKILLMANAGER->PlayerMonSkillRequest( heroIdx, pmsg, false );
			}
			break;
		case NM_SKILL_CAST_USED_MON_REQ:
			{
				MSG_REQ_SKILL_USED* pmsg = (MSG_REQ_SKILL_USED*)msgRoot;
				SKILLMANAGER->PlayerMonSkillRequest( heroIdx, pmsg, true );
			}
			break;
		case NM_SKILL_ITEMMAPCHANGE_REQ:
			{
				MSG_REQ_SKILL_ITEMMAPCHANGE* pmsg = (MSG_REQ_SKILL_ITEMMAPCHANGE*)msgRoot;

				SKILLMANAGER->MapChangeSkillRequest( heroIdx, pmsg );
			}
			break;
		case NM_SKILL_ITEMVEHICLE_REQ:
			{
				MSG_REQ_SKILL_VEHICLE* pmsg = (MSG_REQ_SKILL_VEHICLE*)msgRoot;

				SKILLMANAGER->VehiCleSkillRequest( heroIdx, pmsg );
			}
			break;
		case NM_SKILL_CAST_CANCEL_REQ:
			{
				MSG_REQ_SKILL_CAST_CANCEL* pReqMsg = (MSG_REQ_SKILL_CAST_CANCEL*)msgRoot;
				
				SKILLMANAGER->CastSkillCancel( pReqMsg->mAttacker );				
			}
			break;
		case NM_SKILL_INFLUENCE_DELETE_REQ:
			{
				MSG_REQ_INFLUENCE_DELETE* msg = (MSG_REQ_INFLUENCE_DELETE*)msgRoot;

				SKILLMANAGER->ClientDeleteInfluenceObject( heroIdx, msg->mUniqueIdx );
			}
			break;
		case NM_SKILL_INFLUENCE_AURA_DELETE_REQ:
			{
				MSG_REQ_INFLUENCE_AURA_DELETE* msg = (MSG_REQ_INFLUENCE_AURA_DELETE*)msgRoot;

				if( SKILLMANAGER->ClientAuraInfluenceOff( heroIdx, msg->mUniqueIdx, msg->mSkillClassIdx ) == false )
					SKILLMANAGER->AuraInfluenceOff( heroIdx, msg->mSkillClassIdx );
			}
			break;
		case NM_SKILL_RESURRECTION_RES:	// 스킬 부활 확인
			{
				/// 부활 처리 통합
				hero->Resurrection( eRESURRECTIONTYPE_SKILL );

				//hero->SkillResurrection( );
				//// 부활 메세지 싱크
				//MSG_SYN_SKILL_RESURRECTION synMsg;

				//synMsg.Category = NM_SKILL;
				//synMsg.Protocol = NM_SKILL_RESURRECTION_SYN;
				//synMsg.mTarget.index = heroIdx;
				//synMsg.mTarget.type  = eOBJECTTYPE_PLAYER;
				//synMsg.mTargetHP = hero->GetHP();
				//synMsg.mTargetMP = hero->GetMP();

				//NETWORK2->QuickSend( hero, (char*)&synMsg, sizeof(synMsg) );
			}
			break;
		}
		break; // NM_SKILL

	case NM_TAROT:
		/*-- ClientInfo::Complete 필터링 - Check / CharacterSelect / GameIn
		*/
		if ( (clientInfo->completeData & 0x0007) != 0x0007 )
			return false;

		/*-- MSGROOT::Protocol 필터링 - 알수없는 경우 소켓연결을 해제한다.
		*/
		if ( (unsigned char)msgRoot->Protocol >= NM_TAROT_MAX )
		{
			PostServerEvent( "END TASK - UNKNOWN NM_TAROT::PROTOCOL(=%d):MEMBER_IDX(=%d):CHARACTER_IDX(=%d):INETNUM(=%s).",
							 msgRoot->Protocol,
							 clientInfo->memberIdx,
							 clientInfo->characterIdx,
							 inet_ntoa( perSocketContext->addr.sin_addr) );
			return false;
		}

		switch( msgRoot->Protocol )
		{
		case NM_TAROT_SEEKER_OPEN_REQ:
			try
			{
				if ( clientInfo->complete.pvpJoin )
					throw ERROR_TAROT_SEEKER_OPEN_FAIL;

				hero->TarotSeekerOpen( (ULONG_PTR)perSocketContext, (MSG_REQ_TAROT_SEEKER_OPEN*)msgRoot );
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_TAROT, NM_TAROT_SEEKER_OPEN_RES, error );
			}
			break;

		case NM_TAROT_SEEKER_CLOSE_REQ:
			try
			{
				hero->TarotSeekerClose( (ULONG_PTR)perSocketContext, (MSG_REQ_TAROT_SEEKER_CLOSE*)msgRoot );
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_TAROT, NM_TAROT_SEEKER_CLOSE_RES, error );
			}
			break;

		case NM_TAROT_SEEKER_JOIN_REQ:
			try
			{
				hero->TarotSeekerJoin( (ULONG_PTR)perSocketContext, (MSG_REQ_TAROT_SEEKER_JOIN*)msgRoot );
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_TAROT, NM_TAROT_SEEKER_JOIN_RES, error );
			}
			break;

		case NM_TAROT_SEEKER_RESULT_REQ:
			try
			{
				hero->TarotSeekerResult( (ULONG_PTR)perSocketContext, (MSG_REQ_TAROT_SEEKER_RESULT*)msgRoot );
				clientInfo->commonDb.spreadValue = true;
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_TAROT, NM_TAROT_SEEKER_RESULT_RES, error );
			}
			break;
		case NM_TAROT_SEEKER_ACCEPT_REQ:
			try
			{
				hero->TarotSeekerAccept( (ULONG_PTR)perSocketContext, (MSG_REQ_TAROT_SEEKER_ACCEPT*)msgRoot );
				clientInfo->commonDb.tarotResult = true;
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_TAROT, NM_TAROT_SEEKER_ACCEPT_RES, error );
			}
			break;

		case NM_TAROT_READER_READY_REQ:
			try
			{
				if ( clientInfo->complete.pvpJoin )
					throw ERROR_TAROT_READER_READY_FAIL;

				hero->TarotReaderReady( (ULONG_PTR)perSocketContext, (MSG_REQ_TAROT_READER_READY*)msgRoot );
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_TAROT, NM_TAROT_READER_READY_RES, error );
			}
			break;
		case NM_TAROT_READER_OPEN_REQ:
			try
			{
				hero->TarotReaderOpen( (ULONG_PTR)perSocketContext, (MSG_REQ_TAROT_READER_OPEN*)msgRoot );
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_TAROT, NM_TAROT_READER_OPEN_RES, error );
			}
			break;

		case NM_TAROT_READER_CLOSE_REQ:
			try
			{
				hero->TarotReaderClose( (ULONG_PTR)perSocketContext, (MSG_REQ_TAROT_READER_CLOSE*)msgRoot );
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_TAROT, NM_TAROT_READER_CLOSE_RES, error );
			}
			break;
		}
		break; // NM_TAROT

	case NM_NPC:
		/*-- ClientInfo::Complete 필터링 - Check / CharacterSelect / GameIn
		*/
		if ( (clientInfo->completeData & 0x0007) != 0x0007 )
			return false;

		/*-- MSGROOT::Protocol 필터링 - 알수없는 경우 소켓연결을 해제한다.
		*/
		if ( (unsigned char)msgRoot->Protocol >= NM_NPC_MAX )
		{
			PostServerEvent( "END TASK - UNKNOWN NM_NPC::PROTOCOL(=%d):MEMBER_IDX(=%d):CHARACTER_IDX(=%d):INETNUM(=%s).",
							 msgRoot->Protocol,
							 clientInfo->memberIdx,
							 clientInfo->characterIdx,
							 inet_ntoa( perSocketContext->addr.sin_addr) );
			return false;
		}

		switch ( msgRoot->Protocol )
		{
		case NM_NPC_OPEN_REQ:	/// npc 대화 시작
			{
				MSG_REQ_NPC_OPEN* pmsg = (MSG_REQ_NPC_OPEN*)msgRoot;

				unsigned long retValue = OBJECTMANAGER->IsNpcTalk( pmsg->mNpcIdx, hero, eNPCTALK_OPEN, 0 );

				// 성공/실패 메세지 발송
				HANDLE            handle  = NULL;
				MSG_RES_NPC_OPEN* sendMsg = (MSG_RES_NPC_OPEN*)GetMsgRoot( &handle, perSocketContext, NM_NPC, NM_NPC_OPEN_RES );

				sendMsg->ErrorCode = retValue;
				sendMsg->mNpcIdx   = pmsg->mNpcIdx;
				sendMsg->mPosX     = hero->GetXPos();
				sendMsg->mPosY     = hero->GetYPos();

				SendMsgRoot( handle, sizeof(MSG_RES_NPC_OPEN) );

				if( retValue == ERROR_NPC_OPEN_SUCCESS )
				{
					// 다른 플레이어들에게도 알림
					MSG_SYN_NPC_OPEN synMsg;
					synMsg.Category = NM_NPC;
					synMsg.Protocol = NM_NPC_OPEN_SYN;
					synMsg.mCharacterIdx = hero->GetObjectID();
					synMsg.mPosX = hero->GetXPos();
					synMsg.mPosY = hero->GetYPos();
					NETWORK2->QuickSendExcept( hero, (char*)&synMsg, sizeof(synMsg) );
				}
				else
				{
					if( !( retValue == ERROR_NPC_OPEN_ALREADY || retValue == ERROR_NPC_OPEN_RANGE ) )
						NETWORK2->PostServerEvent( "player[%d,%d,%d] return[%d]NM_NPC_OPEN_REQ retValue != ERROR_NPC_OPEN_SUCCESS", 
						hero->GetObjectID(), hero->GetState(), hero->GetStateStop(), retValue );
				}
			}
			break;
		case NM_NPC_CLOSE_REQ:	/// 대화 종료
			{
				/// 대화중인 npc인덱스를 제거
				hero->SetNpcIdx( 0 );

				/// 상태 변경
				if( hero->ChangeState( eOBJECT_STATE_IDLE ) == true )
				{
					/// 정지 상태 변경
					hero->SetStateStop( eSTOP_NONE );

					// 다른 플레이어들에게도 알림
					MSG_SYN_NPC_CLOSE synMsg;
					synMsg.Category = NM_NPC;
					synMsg.Protocol = NM_NPC_CLOSE_SYN;
					synMsg.mCharacterIdx = hero->GetObjectID();
					NETWORK2->QuickSendExcept( hero, (char*)&synMsg, sizeof(synMsg) );
				}
			}
			break;
		case NM_NPC_QUESTLIST_RESEND_REQ:
			{
				MSG_REQ_NPC_QUESTLIST_RESEND* msg = (MSG_REQ_NPC_QUESTLIST_RESEND*)msgRoot;

				/// 퀘스트 신규가능 목록 리스트 보내기
				/// 기존에 대화하던 npc와 동일해야한다
				try
				{
					if( msg->npcIndex != hero->GetNpcIdx() )
						throw ERROR_QUESTLIST_FAIL;

					if( hero->SendNpcNewQuestList( msg->npcIndex ) == false )
						throw ERROR_QUESTLIST_FAIL;
				}
				catch ( int error )
				{
					ResMsgError( perSocketContext, NM_NPC, NM_NPC_QUESTLIST_RES, error );
				}
			}
			break;
		case NM_NPC_SKILL_ADD_REQ:
			{
				MSG_REQ_NPC_SKILL_ADD* msg = (MSG_REQ_NPC_SKILL_ADD*)msgRoot;

				/// npc 인증
				unsigned long retValue = OBJECTMANAGER->IsNpcTalk( msg->mNpcIdx, hero, eNPCTALK_SKILLADD, msg->mSkillClassIdx );
				if( retValue != ERROR_NPC_OPEN_SUCCESS )
				{
					NETWORK2->PostServerEvent( "player[%d,%d,%d] return[%d]NM_NPC_SKILL_ADD_REQ retValue != ERROR_NPC_OPEN_SUCCESS", 
						hero->GetObjectID(), hero->GetState(), hero->GetStateStop(), retValue );

					ResMsgError( perSocketContext, NM_NPC, NM_NPC_SKILL_ADD_ERR_RES, SKILL_ADD_ERR_NPC );
					break;
				}

				//////////////////////////////////////////////////////////////////////////
				/// 가능 여부 체크
				//////////////////////////////////////////////////////////////////////////

				/// 스킬 관련 DB 사용중
				if ( clientInfo->skillDb.skillDbUse )
				{
					ResMsgError( perSocketContext, NM_NPC, NM_NPC_SKILL_ADD_ERR_RES, SKILL_ADD_ERR_DBERROR );                    
					break;
				}

				unsigned long needMoney;
				unsigned short needSP;

				// skill 이외에 다른 객체에서 감산을 해야할 수치를 얻어옴
				if( !SKILLMANAGER->NeedAddPlayerHaveSkill( msg->mSkillClassIdx, msg->mSkillStep, &needMoney, &needSP ) )
				{
					/// 스크립트 오류
					ResMsgError( perSocketContext, NM_NPC, NM_NPC_SKILL_ADD_ERR_RES, SKILL_ADD_ERR_SCRIPT );
					break;
				}

				/// 필요 금액 체크
				if( hero->GetMoney() < needMoney )
				{
					ResMsgError( perSocketContext, NM_NPC, NM_NPC_SKILL_ADD_ERR_RES, SKILL_ADD_ERR_MONEY );
					break;
				}
				// 필요 sp 체크
				if( hero->GetSkillPoint() < needSP )
				{
					ResMsgError( perSocketContext, NM_SKILL, NM_NPC_SKILL_ADD_ERR_RES, SKILL_ADD_ERR_SP );
					break;
				}

				//////////////////////////////////////////////////////////////////////////
				/// 실제 적용
				//////////////////////////////////////////////////////////////////////////

				/// 스킬 추가
				unsigned char errorcode = SKILLMANAGER->AddPlayerHaveSkill( heroIdx, msg->mSkillClassIdx, msg->mSkillStep, 0 );
				if( errorcode != SKILL_ADD_ERR_SUCCESS )
				{
					ResMsgError( perSocketContext, NM_NPC, NM_NPC_SKILL_ADD_ERR_RES, errorcode );
					break;
				}

				/// 금액,sp를 적용
				if( needMoney > 0 )
				{
					sObject npc = { eOBJECTTYPE_NPC, msg->mNpcIdx };
					if( hero->AddMoney( npc, -((long)needMoney) ) != -((long)needMoney) )
					{
						// 필요항목 오류
						assert(NULL);
						PostServerEvent("ERROR - NM_SKILL_ADD_REQ can't AddMoney");
					}
				}
				/// 포인트 감소
				if( hero->SkillPointMinus( needSP ) == false )
				{
					// 필요항목 오류
					assert(NULL);
					PostServerEvent("ERROR - NM_SKILL_ADD_REQ can't SkillPointMinus");
				}

				// 스킬 DB저장
				if( msg->mSkillStep == 0 )
				{
					// 스킬 신규생성 DB추가
					clientInfo->skillDb.skillDbUse = SkillInsert( perSocketContext, heroIdx, msg->mSkillClassIdx, 
						needMoney, needSP, hero->GetSkillLevel(), hero->GetSkillPoint() );
				}
				else
				{
					// 스킬 단계갱신 DB변경
					clientInfo->skillDb.skillDbUse = SkillUpdate( perSocketContext, heroIdx, msg->mSkillClassIdx, msg->mSkillStep,
						needMoney, needSP, hero->GetSkillLevel(), hero->GetSkillPoint() );
				}

			}
			break;
		case NM_NPC_ITEM_MOV2_INVENTORY_REQ:
			try
			{
				MSG_REQ_NPC_ITEM_MOV2_INVENTORY* pReqMsg = (MSG_REQ_NPC_ITEM_MOV2_INVENTORY*)msgRoot;

				/// npc 인증
				unsigned long retValue = OBJECTMANAGER->IsNpcTalk( pReqMsg->npcIdx, hero, eNPCTALK_INVENTORY, 0 );
				if( retValue != ERROR_NPC_OPEN_SUCCESS )
				{
					NETWORK2->PostServerEvent( "player[%d,%d,%d] return[%d]NM_NPC_ITEM_MOV2_INVENTORY_REQ retValue != ERROR_NPC_OPEN_SUCCESS", 
						hero->GetObjectID(), hero->GetState(), hero->GetStateStop(), retValue );
					throw ERROR_ITEM_MOV2_INVENTORY_NPC;
				}

				if ( clientInfo->inventoryDatabase )
					throw ERROR_ITEM_MOV2_INVENTORY_NOTYET;

				hero->ItemMov2Inventory( (ULONG_PTR)perSocketContext, pReqMsg );
				clientInfo->inventoryDb.move2 = true;
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_NPC, NM_NPC_ITEM_MOV2_INVENTORY_RES, error );
			}
			break;
		case NM_NPC_ITEM_SELL_REQ:
			try
			{
				MSG_REQ_NPC_ITEM_SELL* pReqMsg = (MSG_REQ_NPC_ITEM_SELL*)msgRoot;

				/// npc 인증
				unsigned long retValue = OBJECTMANAGER->IsNpcTalk( pReqMsg->npcIdx, hero, eNPCTALK_ITEMSELL, 0 );
				if( retValue != ERROR_NPC_OPEN_SUCCESS )
				{
					NETWORK2->PostServerEvent( "player[%d,%d,%d] return[%d]NM_NPC_ITEM_SELL_REQ retValue != ERROR_NPC_OPEN_SUCCESS", 
						hero->GetObjectID(), hero->GetState(), hero->GetStateStop(), retValue );
					throw ERROR_ITEM_SELL_NPC;
				}

				if ( clientInfo->inventoryDatabase )
					throw ERROR_ITEM_SELL_NOTYET;

				hero->ItemSell( (ULONG_PTR)perSocketContext, pReqMsg );
				clientInfo->inventoryDb.sell = true;
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_NPC, NM_NPC_ITEM_SELL_RES, error );
			}
			break;
		case NM_NPC_ITEM_BUY_REQ:
			try
			{
				MSG_REQ_NPC_ITEM_BUY* pReqMsg = (MSG_REQ_NPC_ITEM_BUY*)msgRoot;

				/// npc 인증
				unsigned long retValue = OBJECTMANAGER->IsNpcTalk( pReqMsg->npcIdx, hero, eNPCTALK_ITEMBUY, pReqMsg->itemDefineIndex );
				if(  retValue != ERROR_NPC_OPEN_SUCCESS )
				{
					NETWORK2->PostServerEvent( "player[%d,%d,%d] return[%d]NM_NPC_ITEM_SELL_REQ retValue != ERROR_NPC_OPEN_SUCCESS", 
						hero->GetObjectID(), hero->GetState(), hero->GetStateStop(), retValue );
					throw ERROR_ITEM_BUY_NPC;
				}

				if ( clientInfo->inventoryDatabase )
					throw ERROR_ITEM_BUY_NOTYET;

				hero->ItemBuy( (ULONG_PTR)perSocketContext, pReqMsg );
				clientInfo->inventoryDb.buy = true;
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_NPC, NM_NPC_ITEM_BUY_RES, error );
			}
			break;
		case NM_NPC_DEPOSIT_REQ:
			try
			{
				hero->NpcDeposit( (MSG_REQ_NPC_DEPOSIT*)msgRoot );
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_NPC, NM_NPC_DEPOSIT_RES, error );
			}
			break;
		case NM_NPC_WITHDRAW_REQ:
			try
			{
				hero->NpcWithdraw( (MSG_REQ_NPC_WITHDRAW*)msgRoot );
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_NPC, NM_NPC_WITHDRAW_RES, error );
			}
			break;
		case NM_NPC_QUEST_ADD_REQ:
			{
				/// 퀘스트 신규 등록 요청 
				MSG_REQ_QUEST_ADD* pmsg = (MSG_REQ_QUEST_ADD*)msgRoot;

				try
				{
					/// DB 사용중인지 체크
					if( clientInfo->questDb.questInsert )
						throw ERROR_QUEST_ADD_NOTYET;

					/// npc 인증
					unsigned long retValue = OBJECTMANAGER->IsNpcTalk( pmsg->npcIndex, hero, eNPCTALK_QUESTADD, pmsg->questIndex );
					if( retValue != ERROR_NPC_OPEN_SUCCESS )
					{
						NETWORK2->PostServerEvent( "player[%d,%d,%d] return[%d]NM_NPC_QUEST_ADD_REQ retValue != ERROR_NPC_OPEN_SUCCESS",
							hero->GetObjectID(), hero->GetState(), hero->GetStateStop(), retValue );
						throw ERROR_QUEST_ADD_NPC;
					}

					/// 신규 가능한 퀘스트 조건 검색
					int errorCode = hero->IsRegistQuest( pmsg->type, pmsg->questIndex, pmsg->npcIndex );
					if( errorCode != ERROR_QUEST_ADD_SUCCESS )
						throw errorCode;

					/// 신규퀘스트
					errorCode = hero->SaveQuestInsert( (ULONG_PTR)perSocketContext, heroIdx, pmsg->questIndex );
					if( errorCode != ERROR_QUEST_ADD_SUCCESS )
						throw errorCode;

					clientInfo->questDb.questInsert = true;
					clientInfo->inventoryDb.reward = true;
				}
				catch ( int error )
				{
					ResMsgError( perSocketContext, NM_NPC, NM_NPC_QUEST_ADD_RES, error );
				}
			}
			break;
		case NM_NPC_QUEST_COMPLETE_REQ:
			{
				/// 퀘스트 진행 완료 요청
				MSG_REQ_NPC_QUEST_COMPLETE* pmsg = (MSG_REQ_NPC_QUEST_COMPLETE*)msgRoot;

				try
				{
					/// DB 사용중인지 검사
					if( clientInfo->questDb.questComplete )
						throw ERROR_QUEST_COMPLETE_NOTYET;

					/// 보유중인 퀘스트인지 검사
					int dbIndex = hero->IsKeepQuest( pmsg->arrayIdx, pmsg->questIndex );
					if( dbIndex == -1 )
						throw ERROR_QUEST_COMPLETE_FAIL;

					/// npc 인증
					unsigned long retValue = OBJECTMANAGER->IsNpcTalk( pmsg->npcIndex, hero, eNPCTALK_QUESTREWARD, pmsg->questIndex );
					if( retValue != ERROR_NPC_OPEN_SUCCESS )
					{
						NETWORK2->PostServerEvent( "player[%d,%d,%d] return[%d]NM_NPC_QUEST_COMPLETE_REQ retValue != ERROR_NPC_OPEN_SUCCESS", 
							hero->GetObjectID(), hero->GetState(), hero->GetStateStop(), retValue );
						throw ERROR_QUEST_REWARD_NPC;
					}

					/// 완료가능한 퀘스트인지 검사 (의무조건 충족 검사)
					if( !mpQuestManager->IsPossibleComplete( heroIdx, pmsg->arrayIdx ) )
						throw ERROR_QUEST_COMPLETE_POSSIBLE;

					/// 진행완료 검사 및 요청 
					int errorCode = hero->SaveQuestComplete( (ULONG_PTR)perSocketContext, pmsg, dbIndex );
					if( errorCode != ERROR_QUEST_COMPLETE_SUCCESS )
						throw errorCode;

					clientInfo->questDb.questComplete = true;
					clientInfo->inventoryDb.reward = true;
				}
				catch ( int error )
				{
					ResMsgError( perSocketContext, NM_NPC, NM_NPC_QUEST_COMPLETE_RES, error );
				}
			}
			break;
		case NM_NPC_QUEST_REWARD_REQ:
			{
				/// 퀘스트 완료 요청 
				MSG_REQ_NPC_QUEST_REWARD* pmsg = (MSG_REQ_NPC_QUEST_REWARD*)msgRoot;

				try
				{
					/// DB 사용중인지 검사
					if( clientInfo->questDb.questReward )
						throw ERROR_QUEST_REWARD_NOTYET;

					/// 보유퀘스트 검사
					int dbIndex = hero->IsKeepQuest( pmsg->arrayIdx, pmsg->questIndex );
					if( dbIndex == -1 )
						throw ERROR_QUEST_REWARD_FAIL;

					/// npc 인증
					unsigned long retValue = OBJECTMANAGER->IsNpcTalk( pmsg->npcIndex, hero, eNPCTALK_QUESTREWARD, pmsg->questIndex );
					if( retValue != ERROR_NPC_OPEN_SUCCESS )
					{
						NETWORK2->PostServerEvent( "player[%d,%d,%d] return[%d]NM_NPC_QUEST_REWARD_REQ retValue != ERROR_NPC_OPEN_SUCCESS", 
							hero->GetObjectID(), hero->GetState(), hero->GetStateStop(), retValue );
						throw ERROR_QUEST_REWARD_NPC;
					}

					/// 보상용 검사 및 요청 
					int errorCode = hero->SaveQuestReward( (ULONG_PTR)perSocketContext, pmsg, dbIndex );
					if( errorCode != ERROR_QUEST_REWARD_SUCCESS )
						throw errorCode;

					clientInfo->questDb.questReward = true;
					clientInfo->inventoryDb.reward = true;
				}
				catch ( int error )
				{
					ResMsgError( perSocketContext, NM_NPC, NM_NPC_QUEST_REWARD_RES, error );
				}
			}
			break;
		case NM_NPC_GUILD_CREATE_REQ:
			{
				// 길드 생성 요청
				MSG_REQ_NPC_GUILD_CREATE* pmsg = (MSG_REQ_NPC_GUILD_CREATE*)msgRoot;
				try
				{
					// DB 사용중인지 체크
					if ( clientInfo->guildDb.guildCreate )
						throw ERROR_NPC_GUILD_CREATE_FAIL;

					// 1. npc 인증
					unsigned long retValue = OBJECTMANAGER->IsNpcTalk( pmsg->npcIndex, hero, eNPCTALK_GUILDCREATE, 0 );
					if( retValue != ERROR_NPC_OPEN_SUCCESS )
					{
						NETWORK2->PostServerEvent( "player[%d,%d,%d] return[%d]NM_NPC_GUILD_CREATE_REQ retValue != ERROR_NPC_OPEN_SUCCESS", 
													hero->GetObjectID(), hero->GetState(), hero->GetStateStop(), retValue );
						throw ERROR_NPC_GUILD_CREATE_NPC;
					}

					// 2. 길드 가입중인지 검사
					sPlayerExrInfo* exrInfo = hero->GetExrInfo( );
					if ( exrInfo->mGuildIndex != 0 )
						throw ERROR_NPC_GUILD_CREATE_ALREADY;

					// 3. 길드명 검사
					if ( wcslen( pmsg->name ) == 0 || wcslen( pmsg->name ) > MAX_GUILDNAME_SIZE )
						throw ERROR_NPC_GUILD_CREATE_ERRORNAME;

					// 4. 중복검사
					if ( mpGuildManager->FindGuildName( pmsg->name ) )
						throw ERROR_NPC_GUILD_CREATE_EXISTNAME;

					// 5. 제한 조건검사 ( 레벨, 금액 )
					sPlayerInfo* info = hero->GetPlayerInfo();
					if( info->Level < GUILDLIMIT_LEVEL || hero->GetMoney() < GUILDLIMIT_MONEY )
						throw ERROR_NPC_GUILD_CREATE_CONDITION;

					// 세팅
					pmsg->name[ MAX_GUILDNAME_SIZE ] = 0;
					clientInfo->guildDb.guildCreate = GuildCreate( perSocketContext, heroIdx, pmsg->name, hero->GetName() ); 
				}
				catch ( int error )
				{
					ResMsgError( perSocketContext, NM_NPC, NM_NPC_GUILD_CREATE_RES, error );					
				}
			}
			break;
		case NM_NPC_TAROT_OPEN_REQ:
			{
				/// 운세 비트 세팅후 리턴	
				time_t    ltime;
				time( &ltime );

				unsigned long value = 0;
				tm presentThru		= *localtime( &ltime );

				TIMESTAMP_STRUCT thru = TIMER->ConvertTMtoTS( presentThru );
				TIMESTAMP_STRUCT* heroThru = 0;

				for( int i = 0; i < 5; ++i )
				{
					value = value << 4;

					heroThru = hero->GetFortuneThru( i );
					if( heroThru )
					{
						/// 동일 년, 월, 일이면 리턴
						if( thru.year  == heroThru->year && 
							thru.month == heroThru->month &&
							thru.day   == heroThru->day )
						{
							value = value | 1;
						}
					}
					else
					{
						/// 오류
						PostServerEvent( "NM_NPC_TAROT_OPEN_REQ - not exist fortuneThru data, heroIdx = %d", hero->GetObjectID() );
						continue;
					}
				}

				HANDLE					 handle = NULL;
				MSG_RES_NPC_TAROT_OPEN* sendmsg = (MSG_RES_NPC_TAROT_OPEN*)GetMsgRoot( &handle, perSocketContext, NM_NPC, NM_NPC_TAROT_OPEN_RES );
				sendmsg->fortuneData = value;
				NETWORK2->SendMsgRoot( handle, sizeof(MSG_RES_NPC_TAROT_OPEN) );
			}
			break;
		case NM_NPC_TAROT_JOIN_REQ:
			{
				// NPC 점집 이용 요청
				MSG_REQ_NPC_TAROT_JOIN* pmsg = (MSG_REQ_NPC_TAROT_JOIN*)msgRoot;

				try
				{
					// 1. npc 인증 및 금액 체크
					unsigned long retValue = OBJECTMANAGER->IsNpcTalk( pmsg->npcIndex, hero, eNPCTALK_NPCTAROT, 0 );
					if( retValue != ERROR_NPC_OPEN_SUCCESS )
					{
						PostServerEvent( "player[%d,%d,%d] return[%d]NM_NPC_TAROT_JOIN_REQ retValue != ERROR_NPC_OPEN_SUCCESS", hero->GetObjectID(), hero->GetState(), hero->GetStateStop(), retValue );
						throw ERROR_NPC_TAROT_JOIN_FAIL;
					}

					/// 2. 금액 지불 처리
					cNpc* pNpc = GRIDMANAGER->GetNpc( pmsg->npcIndex );
					if( pNpc == NULL )
					{
						PostServerEvent("NM_NPC_TAROT_JOIN_REQ [%d,%d,%d,%d,%d] pNpc == NULL", pmsg->npcIndex, hero->GetNpcIdx(), hero->GetObjectID(), hero->GetState(), hero->GetStateStop() );
						throw ERROR_NPC_TAROT_JOIN_FAIL;
					}
					
					unsigned long npcClassIdx = pNpc->GetRaceGender();
					sNpcFuncData* data = NPCSCRIPT->GetNpcFuncData( npcClassIdx );
					if( !data )
						throw ERROR_NPC_TAROT_JOIN_FAIL;

					/// 차감
					hero->AddMoney( hero->GetObject( ), -((long)data->mTarotPrice) );

					// 성공 리턴
					throw ERROR_NPC_TAROT_JOIN_SUCESS;
				}
				catch ( int error )
				{
					ResMsgError( perSocketContext, NM_NPC, NM_NPC_TAROT_JOIN_RES, error );	
				}
			}
			break;
		case NM_NPC_TAROT_FORTUNE_REQ:
			{
				MSG_REQ_NPC_TAROT_FORTUNE* pmsg = (MSG_REQ_NPC_TAROT_FORTUNE*)msgRoot;

				try
				{
					// 운세 체크
					TIMESTAMP_STRUCT* heroThru = hero->GetFortuneThru( pmsg->fortuneProp );
					if( heroThru == NULL )
						throw ERROR_NPC_TAROT_FORTUNE_FAIL;

					time_t    ltime;
					time( &ltime );

					tm presentThru = *localtime( &ltime );
					TIMESTAMP_STRUCT thru = TIMER->ConvertTMtoTS( presentThru );

					/// 동일 년, 월, 일이면 리턴
					if( thru.year  == heroThru->year && 
						thru.month == heroThru->month &&
						thru.day   == heroThru->day    )
						throw ERROR_NPC_TAROT_FORTUNE_ONCE;

					// 운세 저장
					*heroThru = thru;

					// 성공 리턴
					throw ERROR_NPC_TAROT_FORTUNE_SUCESS;
				}
				catch ( int error )
				{
					ResMsgError( perSocketContext, NM_NPC, NM_NPC_TAROT_FORTUNE_RES, error );	
				}
			}
			break;
		}
		break; // NM_NPC

	case NM_CHEAT:
		/*-- ClientInfo::Complete 필터링 - Check / CharacterSelect / GameIn
		*/
		if ( (clientInfo->completeData & 0x0007) != 0x0007 )
			return false;

		if ( clientInfo->slevel == 0 )
		{
			PostServerEvent( "END TASK - SLEVEL(=%d) NM_CHEAT::PROTOCOL(=%d):MEMBER_IDX(=%d):CHARACTER_IDX(=%d):INETNUM(=%s).",
							 clientInfo->slevel,
							 msgRoot->Protocol,
							 clientInfo->memberIdx,
							 clientInfo->characterIdx,
							 inet_ntoa( perSocketContext->addr.sin_addr) );
			return false;
		}

		/*-- MSGROOT::Protocol 필터링 - 알수없는 경우 소켓연결을 해제한다.
		*/
		if ( (unsigned char)msgRoot->Protocol >= NM_CHEAT_MAX )
		{
			PostServerEvent( "END TASK - UNKNOWN NM_CHEAT::PROTOCOL(=%d):MEMBER_IDX(=%d):CHARACTER_IDX(=%d):INETNUM(=%s).",
							 msgRoot->Protocol,
							 clientInfo->memberIdx,
							 clientInfo->characterIdx,
							 inet_ntoa( perSocketContext->addr.sin_addr) );
			return false;
		}

		switch( msgRoot->Protocol )
		{
		case NM_CHEAT_IN_REQ:
			{
				hero->SetCheatHideMode( true );
				hero->SetCheatUndeadMode( true );

				HANDLE handle = NULL;
				if ( GetMsgRoot( &handle, perSocketContext, NM_CHEAT, NM_CHEAT_IN_RES ) != NULL )
					NETWORK2->SendMsgRoot( handle, sizeof(MSG_RES_CHEAT_IN) );
			}
			break;
		case NM_CHEAT_SPEEDUP_REQ:
			hero->SetCheatSpeedUp( ((MSG_REQ_CHEAT_SPEEDUP*)msgRoot)->mSpeedUp );
			break;
		case NM_CHEAT_HIDE_REQ:
			{
				MSG_REQ_CHEAT_HIDE* recvMsg = (MSG_REQ_CHEAT_HIDE*)msgRoot;

				hero->SetCheatHideMode( recvMsg->mMode );

				HANDLE              handle  = NULL;
				MSG_RES_CHEAT_HIDE* sendMsg = (MSG_RES_CHEAT_HIDE*)GetMsgRoot( &handle, perSocketContext, NM_CHEAT, NM_CHEAT_HIDE_RES );
					sendMsg->mMode = recvMsg->mMode;
				SendMsgRoot( handle, sizeof(MSG_RES_CHEAT_HIDE) );
			}
			break;
		case NM_CHEAT_UNDEAD_REQ:
			{
				MSG_REQ_CHEAT_UNDEAD* recvMsg = (MSG_REQ_CHEAT_UNDEAD*)msgRoot;

				hero->SetCheatUndeadMode( recvMsg->mMode );

				HANDLE                handle  = NULL;
				MSG_RES_CHEAT_UNDEAD* sendMsg = (MSG_RES_CHEAT_UNDEAD*)GetMsgRoot( &handle, perSocketContext, NM_CHEAT, NM_CHEAT_UNDEAD_RES );
					sendMsg->mMode = recvMsg->mMode;
				SendMsgRoot( handle, sizeof(MSG_RES_CHEAT_UNDEAD) );
			}
			break;
		case NM_CHEAT_INFO_REQ:
			{
				MSG_REQ_CHEAT_INFO* recvMsg = (MSG_REQ_CHEAT_INFO*)msgRoot;
				recvMsg->mCharName[MAX_NAME_SIZE]=0;

				cPlayer* player = OBJECTMANAGER->GetPlayer( recvMsg->mCharName );
				if ( player != NULL )
				{
					HANDLE              handle  = NULL;
					MSG_RES_CHEAT_INFO* sendMsg = (MSG_RES_CHEAT_INFO*)NETWORK2->GetMsgRoot( &handle, perSocketContext, NM_CHEAT, NM_CHEAT_INFO_RES );
						sendMsg->mMapNum = player->GetMapNumber( );
						sendMsg->mPosX   = player->GetXPos( );
						sendMsg->mPosY   = player->GetYPos( );
						sendMsg->mJob    = player->GetJob( );
						sendMsg->mLevel  = player->GetLevel( );
						sendMsg->mMoney  = player->GetMoney( );
					NETWORK2->SendMsgRoot( handle, sizeof(MSG_RES_CHEAT_INFO) );
				}
			}
			break;
		case NM_CHEAT_KICK_REQ:
			{
				MSG_REQ_CHEAT_KICK* recvMsg = (MSG_REQ_CHEAT_KICK*)msgRoot;
				recvMsg->mCharName[MAX_NAME_SIZE]=0;

				char error = ERROR_CHEAT_KICK_SUCCESS;
				cPlayer* player = OBJECTMANAGER->GetPlayer( recvMsg->mCharName );
				if ( player != NULL )
				{
					CloseCID( perSocketContext->cid, true );

					HANDLE handle = NULL;
					if ( NETWORK2->GetMsgRoot( &handle, player->GetConnectionIdx(), NM_CHEAT, NM_CHEAT_KICK_RES ) != NULL )
						NETWORK2->SendMsgRoot( handle, sizeof(MSG_RES_CHEAT_KICK) );
				}
				else
					error = ERROR_CHEAT_UNKNOWN_USER;

				HANDLE handle = NULL;
				MSG_RES_CHEAT_KICK_GM* sendMsg = (MSG_RES_CHEAT_KICK_GM*)GetMsgRoot( &handle, perSocketContext, NM_CHEAT, NM_CHEAT_KICK_RES_GM );
				if( sendMsg != NULL )
				{
					sendMsg->ErrorCode = error;
					wcscpy( sendMsg->mCharName, recvMsg->mCharName );
					SendMsgRoot( handle, sizeof(MSG_RES_CHEAT_KICK_GM) );
				}
			}
			break;
		case NM_CHEAT_RECOVER_REQ:
			{
				hero->CheatRecover( );

				HANDLE                 handle  = NULL;
				MSG_RES_CHEAT_RECOVER* sendMsg = (MSG_RES_CHEAT_RECOVER*)GetMsgRoot( &handle, perSocketContext, NM_CHEAT, NM_CHEAT_RECOVER_RES );
					sendMsg->mMaxHP = hero->GetHP( );
					sendMsg->mMaxMP = hero->GetMP( );
				SendMsgRoot( handle, sizeof(MSG_RES_CHEAT_RECOVER) );

				MSG_SYN_CHEAT_RECOVER syn;
					syn.Category  = NM_CHEAT;
					syn.Protocol  = NM_CHEAT_RECOVER_SYN;
					syn.mObjIndex = heroIdx;
					syn.mMaxHP    = hero->GetHP();
					syn.mMaxMP    = hero->GetMP();
				QuickSendExcept( hero, (char*)&syn, sizeof(MSG_SYN_CHEAT_RECOVER) );
			}
			break;
		case NM_CHEAT_REBIRTH_REQ:
			{
				hero->CheatRebirth( );

				HANDLE                 handle  = NULL;
				MSG_RES_CHEAT_REBIRTH* sendMsg = (MSG_RES_CHEAT_REBIRTH*)GetMsgRoot( &handle, perSocketContext, NM_CHEAT, NM_CHEAT_REBIRTH_RES );
					sendMsg->mMaxHP = hero->GetHP();
					sendMsg->mMaxMP = hero->GetMP();
				NETWORK2->SendMsgRoot( handle, sizeof(MSG_RES_CHEAT_REBIRTH) );

				MSG_SYN_CHEAT_REBIRTH syn;
					syn.Category  = NM_CHEAT;
					syn.Protocol  = NM_CHEAT_REBIRTH_SYN;
					syn.mObjIndex = heroIdx;
					syn.mMaxHP    = hero->GetHP();
					syn.mMaxMP    = hero->GetMP();
				QuickSendExcept( hero, (char*)&syn, sizeof(MSG_SYN_CHEAT_REBIRTH) );
			}
			break;
		case NM_CHEAT_TELEPORT_REQ:
			{
				MSG_REQ_CHEAT_TELEPORT* recvMsg = (MSG_REQ_CHEAT_TELEPORT*)msgRoot;

				if ( hero->GetStateDie( ) == false )
				{
					// 061208 PKH 이동 목적지를 설정
					hero->SetMoveTargetPos( recvMsg->mDestX, recvMsg->mDestY );
					hero->SetPos( recvMsg->mDestX, recvMsg->mDestY );

					if( hero->GetCheatHideMode( ) == false )
					{
						// 다른 플레이어들에게도 알림
						MSG_SYN_CHEAT_TELEPORT syn;
							syn.Category  = NM_CHEAT;
							syn.Protocol  = NM_CHEAT_TELEPORT_SYN;
							syn.mObjIndex = heroIdx;
							syn.mDestX    = recvMsg->mDestX;
							syn.mDestY    = recvMsg->mDestY;
						QuickSendExcept( hero, (char*)&syn, sizeof(MSG_SYN_CHEAT_TELEPORT) );
					}
				}
			}
			break;
		case NM_CHEAT_GO_REQ:
			{
				MSG_REQ_CHEAT_GO* recvMsg = (MSG_REQ_CHEAT_GO*)msgRoot;

				if ( hero->GetMapNumber() == recvMsg->mMapNum )
				{
					hero->SetMoveTargetPos( recvMsg->mDestX, recvMsg->mDestY );
					hero->SetPos( recvMsg->mDestX, recvMsg->mDestY );

					if ( hero->GetCheatHideMode( ) == true )
					{
						HANDLE                  handle = 0;
						MSG_SYN_CHEAT_TELEPORT* synMsg = (MSG_SYN_CHEAT_TELEPORT*)NETWORK2->GetMsgRoot( &handle, perSocketContext, NM_CHEAT, NM_CHEAT_TELEPORT_SYN );
							synMsg->mObjIndex = heroIdx;
							synMsg->mDestX    = recvMsg->mDestX;
							synMsg->mDestY    = recvMsg->mDestY;
						NETWORK2->SendMsgRoot( handle, sizeof(MSG_SYN_CHEAT_TELEPORT) );
					}
					else
					{
						// 다른 플레이어들에게도 알림
						MSG_SYN_CHEAT_TELEPORT syn;
							syn.Category  = NM_CHEAT;
							syn.Protocol  = NM_CHEAT_TELEPORT_SYN;
							syn.mObjIndex = heroIdx;
							syn.mDestX    = recvMsg->mDestX;
							syn.mDestY    = recvMsg->mDestY;
						NETWORK2->QuickSend( hero, (char*)&syn, sizeof(syn) );
					}
				}
				// 이동 성공메세지는 여기서 보내지 않고 후에 맵이동에 필요한 데이타와 함게 보낸다.
				else if ( hero->CheatMapChange( recvMsg->mMapNum, recvMsg->mDestX, recvMsg->mDestY ) == false )
				{
					// 맵이동 응답 메시지 - 실패
					ResMsgError( perSocketContext, NM_PLAYER, NM_PLAYER_MAPCHANGE_RES, 1 );
				}
			}
			break;
		case NM_CHEAT_CHASE_REQ:
			{
				char				 error = ERROR_CHEAT_NO_ERROR;
				MSG_REQ_CHEAT_CHASE* recvMsg = (MSG_REQ_CHEAT_CHASE*)msgRoot;
				recvMsg->mCharName[MAX_NAME_SIZE]=0;

				cPlayer* player = OBJECTMANAGER->GetPlayer( recvMsg->mCharName );
				if ( player != NULL )
				{
					if ( player->GetMapNumber() == hero->GetMapNumber() )
					{
						hero->SetMoveTargetPos( player->GetXPos(), player->GetYPos() );
						hero->SetPos( player->GetXPos(), player->GetYPos() );

						if ( hero->GetCheatHideMode() == true )
						{
							HANDLE                  handle = NULL;
							MSG_SYN_CHEAT_TELEPORT* syn    = (MSG_SYN_CHEAT_TELEPORT*)GetMsgRoot( &handle, perSocketContext, NM_CHEAT, NM_CHEAT_TELEPORT_SYN );
								syn->mObjIndex = heroIdx;
								syn->mDestX = player->GetXPos();
								syn->mDestY = player->GetYPos();
							SendMsgRoot( handle, sizeof(MSG_SYN_CHEAT_TELEPORT) );
						}
						else
						{
							// 다른 플레이어들에게도 알림
							MSG_SYN_CHEAT_TELEPORT syn;
								syn.Category  = NM_CHEAT;
								syn.Protocol  = NM_CHEAT_TELEPORT_SYN;
								syn.mObjIndex = heroIdx;
								syn.mDestX    = player->GetXPos();
								syn.mDestY    = player->GetYPos();
							NETWORK2->QuickSend( hero, (char*)&syn, sizeof(MSG_SYN_CHEAT_TELEPORT) );
						}
					}
					// 이동 성공메세지는 여기서 보내지 않고 후에 맵이동에 필요한 데이타와 함게 보낸다.
					else if( hero->CheatMapChange( player->GetMapNumber(), player->GetXPos(), player->GetYPos() ) == false )
					{
						// 맵이동 응답 메시지 - 실패
						ResMsgError( perSocketContext, NM_PLAYER, NM_PLAYER_MAPCHANGE_RES, 1 );
					}
				}
				else
					error = ERROR_CHEAT_UNKNOWN_USER ;

				HANDLE handle = NULL;
				MSG_RES_CHEAT_CHASE_GM* sendMsg = (MSG_RES_CHEAT_CHASE_GM*)GetMsgRoot( &handle, perSocketContext, NM_CHEAT, NM_CHEAT_CHASE_RES_GM );
				if( sendMsg != NULL )
				{
					sendMsg->ErrorCode = error;
					wcscpy( sendMsg->mCharName, recvMsg->mCharName );
					SendMsgRoot( handle, sizeof(MSG_RES_CHEAT_CHASE_GM) );
				}
			}
			break;
		case NM_CHEAT_MOVE_REQ:
			{
				MSG_REQ_CHEAT_MOVE* recvMsg = (MSG_REQ_CHEAT_MOVE*)msgRoot;
				recvMsg->mCharName[MAX_NAME_SIZE]=0;
				
				char error      = ERROR_CHEAT_NO_ERROR;
				cPlayer* player = OBJECTMANAGER->GetPlayer( recvMsg->mCharName );
				if ( player != NULL )
				{
					if ( player->GetMapNumber() == recvMsg->mMapNum )
					{
						player->SetMoveTargetPos( recvMsg->mDestX, recvMsg->mDestY );
						player->SetPos( recvMsg->mDestX, recvMsg->mDestY );

						if ( player->GetCheatHideMode( ) == true )
						{
							HANDLE                  handle = 0;
							MSG_SYN_CHEAT_TELEPORT* syn    = (MSG_SYN_CHEAT_TELEPORT*)GetMsgRoot( &handle, player->GetConnectionIdx( ), NM_CHEAT, NM_CHEAT_TELEPORT_SYN );
							if ( syn != NULL )
							{
								syn->mObjIndex = player->GetObjectID();
								syn->mDestX    = recvMsg->mDestX;
								syn->mDestY    = recvMsg->mDestY;
								NETWORK2->SendMsgRoot( handle, sizeof(MSG_SYN_CHEAT_TELEPORT) );
							}
						}
						else
						{
							// 다른 플레이어들에게도 알림
							MSG_SYN_CHEAT_TELEPORT syn;
								syn.Category  = NM_CHEAT;
								syn.Protocol  = NM_CHEAT_TELEPORT_SYN;
								syn.mObjIndex = player->GetObjectID();
								syn.mDestX    = recvMsg->mDestX;
								syn.mDestY    = recvMsg->mDestY;
							NETWORK2->QuickSend( player, (char*)&syn, sizeof(syn) );
						}
					}
					// 이동 성공메세지는 여기서 보내지 않고 후에 맵이동에 필요한 데이타와 함게 보낸다.
					else if ( player->CheatMapChange( recvMsg->mMapNum, recvMsg->mDestX, recvMsg->mDestY ) == false )
					{
						// 맵이동 응답 메시지 - 실패
						ResMsgError( perSocketContext, NM_PLAYER, NM_PLAYER_MAPCHANGE_RES, 1 );
					}
				}
				else 
					error = ERROR_CHEAT_UNKNOWN_USER ;

				HANDLE handle = NULL;
				MSG_RES_CHEAT_MOVE_GM* sendMsg = (MSG_RES_CHEAT_MOVE_GM*)GetMsgRoot( &handle, perSocketContext, NM_CHEAT, NM_CHEAT_MOVE_RES_GM );
				if( sendMsg != NULL )
				{
					sendMsg->ErrorCode = error;
					wcscpy( sendMsg->mCharName, recvMsg->mCharName );
					SendMsgRoot( handle, sizeof(MSG_RES_CHEAT_MOVE_GM) );
				}
			}
			break;
		case NM_CHEAT_CALL_REQ:
			{
				MSG_REQ_CHEAT_CALL* recvMsg = (MSG_REQ_CHEAT_CALL*)msgRoot;
				recvMsg->mCharName[MAX_NAME_SIZE]=0;
				int err_code = ERROR_CHEAT_NO_ERROR;
				cPlayer* player = OBJECTMANAGER->GetPlayer( recvMsg->mCharName );
				if ( player != NULL )
				{
					if ( player->GetMapNumber() == hero->GetMapNumber() )
					{
						player->SetMoveTargetPos( hero->GetXPos(), hero->GetYPos() );
						player->SetPos( hero->GetXPos(), hero->GetYPos() );

						if ( player->GetCheatHideMode() == true )
						{
							HANDLE                  handle = 0;
							MSG_SYN_CHEAT_TELEPORT* syn    = (MSG_SYN_CHEAT_TELEPORT*)GetMsgRoot( &handle, player->GetConnectionIdx(), NM_CHEAT, NM_CHEAT_TELEPORT_SYN );
							if ( syn != NULL )
							{
								syn->mObjIndex = player->GetObjectID( );
								syn->mDestX    = hero->GetXPos( );
								syn->mDestY    = hero->GetYPos( );
								NETWORK2->SendMsgRoot( handle, sizeof(MSG_SYN_CHEAT_TELEPORT) );
							}
						}
						else
						{
							// 다른 플레이어들에게도 알림
							MSG_SYN_CHEAT_TELEPORT syn;
								syn.Category  = NM_CHEAT;
								syn.Protocol  = NM_CHEAT_TELEPORT_SYN;
								syn.mObjIndex = player->GetObjectID( );
								syn.mDestX    = hero->GetXPos( );
								syn.mDestY    = hero->GetYPos( );
							NETWORK2->QuickSend( player, (char*)&syn, sizeof(syn) );
						}
					}
					// 이동 성공메세지는 여기서 보내지 않고 후에 맵이동에 필요한 데이타와 함게 보낸다.
					else if( player->CheatMapChange( hero->GetMapNumber(), hero->GetXPos(), hero->GetYPos() ) == false )
					{
						// 맵이동 응답 메시지 - 실패
						ResMsgError( perSocketContext, NM_PLAYER, NM_PLAYER_MAPCHANGE_RES, 1 );
					}

					 
				}
				else
					err_code = ERROR_CHEAT_UNKNOWN_USER ;

				// GM에게 치트 성공 메시지 보내기
				HANDLE                 handle = 0;
				MSG_RES_CHEAT_CALL_GM* res = (MSG_RES_CHEAT_CALL_GM*)GetMsgRoot( &handle, perSocketContext, NM_CHEAT, NM_CHEAT_CALL_RES_GM );
				if ( res != NULL )
				{
					res->ErrorCode = err_code;
					wcscpy( res->mCharName, recvMsg->mCharName );
					NETWORK2->SendMsgRoot( handle, sizeof(MSG_RES_CHEAT_CALL_GM) );
				}
			}
			break;
		case NM_CHEAT_NO_CHAT_REQ:
			{
				MSG_REQ_CHEAT_NO_CHAT* recvMsg = (MSG_REQ_CHEAT_NO_CHAT*)msgRoot;
				recvMsg->mCharName[MAX_NAME_SIZE]=0;
				
				int      err_code = ERROR_CHEAT_NO_ERROR;
				cPlayer* player = OBJECTMANAGER->GetPlayer( recvMsg->mCharName );
                if ( player == NULL )
					err_code = ERROR_CHEAT_UNKNOWN_USER ;

				HANDLE                  handle = 0;
				MSG_RES_CHEAT_NO_CHAT_GM*  res = (MSG_RES_CHEAT_NO_CHAT_GM*)GetMsgRoot( &handle, perSocketContext, NM_CHEAT, NM_CHEAT_NO_CHAT_RES_GM );
				if ( res != NULL )
				{
					res->ErrorCode = err_code;
					wcscpy( res->mCharName, recvMsg->mCharName );
					NETWORK2->SendMsgRoot( handle, sizeof(MSG_RES_CHEAT_NO_CHAT_GM) );
				}

				if ( player != NULL )
				{
					HANDLE                  handle               = NULL;
					CHARACTER_BLOCK_INSERT* characterBlockInsert = (CHARACTER_BLOCK_INSERT*)GetSQL( &handle, SQL_GAME_PROCESS_CHARACTER_BLOCK_INSERT );

					characterBlockInsert->blockCharacterIdx = (long)player->GetObjectID( );
					characterBlockInsert->adminCharacterIdx = heroIdx;
					characterBlockInsert->validTrue         = (long)recvMsg->mDuration * 60;

					SendSQL( perSocketContext, handle, sizeof(CHARACTER_BLOCK_INSERT) );
				}
			}
			break;
		case NM_CHEAT_ALLOW_CHAT_REQ:
			{
				MSG_REQ_CHEAT_ALLOW_CHAT* recvMsg = (MSG_REQ_CHEAT_ALLOW_CHAT*)msgRoot;
				recvMsg->mCharName[MAX_NAME_SIZE]=0;
				
				int      err_code = ERROR_CHEAT_NO_ERROR;
				cPlayer* player  = OBJECTMANAGER->GetPlayer( recvMsg->mCharName );
				if ( player == NULL )
					err_code = ERROR_CHEAT_UNKNOWN_USER ;

				HANDLE                  handle = 0;
				MSG_RES_CHEAT_ALLOW_CHAT_GM*  res = (MSG_RES_CHEAT_ALLOW_CHAT_GM*)GetMsgRoot( &handle, perSocketContext, NM_CHEAT, NM_CHEAT_ALLOW_CHAT_RES_GM );
				if ( res != NULL )
				{
					
					res->ErrorCode = err_code;
					wcscpy( res->mCharName, recvMsg->mCharName );
					NETWORK2->SendMsgRoot( handle, sizeof(MSG_RES_CHEAT_ALLOW_CHAT_GM) );
				}

				if ( player != NULL )
				{
					HANDLE                  handle               = NULL;
					CHARACTER_BLOCK_INSERT* characterBlockInsert = (CHARACTER_BLOCK_INSERT*)GetSQL( &handle, SQL_GAME_PROCESS_CHARACTER_BLOCK_INSERT );

					characterBlockInsert->blockCharacterIdx = (long)player->GetObjectID( );
					characterBlockInsert->adminCharacterIdx = heroIdx;
					characterBlockInsert->validTrue         = (long)0;

					SendSQL( perSocketContext, handle, sizeof(CHARACTER_BLOCK_INSERT) );
				}
			}
			break;
		case NM_CHEAT_STOP_REQ:
			{
				MSG_REQ_CHEAT_STOP* recvMsg = (MSG_REQ_CHEAT_STOP*)msgRoot;
				recvMsg->mCharName[MAX_NAME_SIZE] = 0;

				int      err_code = ERROR_CHEAT_NO_ERROR;
				cPlayer* player  = OBJECTMANAGER->GetPlayer( recvMsg->mCharName );
				if ( player != NULL )
					player->CheatStop( recvMsg->mDuration );
				else
					err_code = ERROR_CHEAT_UNKNOWN_USER ;

				HANDLE                  handle = 0;
				MSG_RES_CHEAT_STOP_GM*  res = (MSG_RES_CHEAT_STOP_GM*)GetMsgRoot( &handle, perSocketContext, NM_CHEAT, NM_CHEAT_STOP_RES_GM );
				if ( res != NULL )
				{
					res->ErrorCode = ERROR_CHEAT_NO_ERROR;
					wcscpy( res->mCharName, recvMsg->mCharName );
					NETWORK2->SendMsgRoot( handle, sizeof(MSG_RES_CHEAT_STOP_GM) );
				}
			}
			break;
		case NM_CHEAT_CANCEL_STOP_REQ:
			{
				MSG_REQ_CHEAT_CANCEL_STOP* recvMsg = (MSG_REQ_CHEAT_CANCEL_STOP*)msgRoot;
				recvMsg->mCharName[MAX_NAME_SIZE]=0;

				int      err_code = ERROR_CHEAT_STOP_SUCCESS;
				cPlayer* player = OBJECTMANAGER->GetPlayer( recvMsg->mCharName );
				if ( player != NULL )
				{
					if( player->GetStateStop() != eSTOP_CHEATSTOP )
						err_code = ERROR_CHEAT_STOP_NOT;

					player->CheatCancelStop( );
				}
				else
					err_code = ERROR_CHEAT_UNKNOWN_USER ;

				
				HANDLE                         handle = 0;
				MSG_RES_CHEAT_CANCEL_STOP_GM*  res = (MSG_RES_CHEAT_CANCEL_STOP_GM*)GetMsgRoot( &handle, perSocketContext, NM_CHEAT, NM_CHEAT_CANCEL_STOP_RES_GM );
				if ( res != NULL )
				{
					res->ErrorCode = err_code;
					wcscpy( res->mCharName, recvMsg->mCharName );
					NETWORK2->SendMsgRoot( handle, sizeof(MSG_RES_CHEAT_CANCEL_STOP_GM) );
				}
			}
			break;
		case NM_CHEAT_NOTICE_SERVER_REQ:
			{
				MSG_REQ_CHEAT_NOTICE_SERVER* recvMsg = (MSG_REQ_CHEAT_NOTICE_SERVER*)msgRoot;
				recvMsg->mMessage[MAX_CHAT_SIZE]=0;

				if ( wcslen( recvMsg->mMessage ) > 0 )
				{
					MSG_SYN_CHEAT_NOTICE_SERVER syn;
						syn.Category = NM_CHEAT;
						syn.Protocol = NM_CHEAT_NOTICE_SERVER_SYN;
						wcscpy( syn.mMessage, recvMsg->mMessage );
						syn.mMessage[MAX_CHAT_SIZE] = 0;
					OBJECTMANAGER->SendNotice( (char*)&syn, syn.GetMsgLength( ) );

					cSender* sender = g_gameSrv->GetSender( );
					if ( sender != NULL )
						sender->PostServerNotice( MAKECID((WORD)mServerNum,(WORD)mChannelNum), syn.mMessage );
				}
			}
			break;
		case NM_CHEAT_NOTICE_CHANNEL_REQ:
			{
				MSG_REQ_CHEAT_NOTICE_CHANNEL* recvMsg = (MSG_REQ_CHEAT_NOTICE_CHANNEL*)msgRoot;
				recvMsg->mMessage[MAX_CHAT_SIZE]=0;

				if ( wcslen( recvMsg->mMessage ) > 0 )
				{
					MSG_SYN_CHEAT_NOTICE_CHANNEL syn;
						syn.Category = NM_CHEAT;
						syn.Protocol = NM_CHEAT_NOTICE_CHANNEL_SYN;
						wcscpy( syn.mMessage, recvMsg->mMessage );
						syn.mMessage[MAX_CHAT_SIZE] = 0;
					OBJECTMANAGER->SendNotice( (char*)&syn, syn.GetMsgLength( ) );
				}
			}
			break;
		case NM_CHEAT_NOTICE_AREA_REQ:
			{
				MSG_REQ_CHEAT_NOTICE_AREA* recvMsg = (MSG_REQ_CHEAT_NOTICE_AREA*)msgRoot;
				recvMsg->mMessage[MAX_CHAT_SIZE]=0;

				if ( wcslen( recvMsg->mMessage ) > 0 )
				{
					MSG_SYN_CHEAT_NOTICE_AREA syn;
						syn.Category = NM_CHEAT;
						syn.Protocol = NM_CHEAT_NOTICE_AREA_SYN;
						wcscpy( syn.mMessage, recvMsg->mMessage );
					NETWORK2->QuickSendMap( hero, (char*)&syn, syn.GetMsgLength() );
				}
			}
			break;
		case NM_CHEAT_REMOVE_ITEM_REQ:
			OBJECTMANAGER->CheatRemoveItemRange( hero, 5000.0f );
			break;
		case NM_CHEAT_REMOVE_MONSTER_REQ:
			OBJECTMANAGER->CheatRemoveMonsterRange( hero, 5000.0f );
			break;
		case NM_CHEAT_LEVELUP_REQ:
			hero->LevelUp( ((MSG_REQ_CHEAT_LEVELUP*)msgRoot)->mAddedValue );
			break;
		case NM_CHEAT_SKILLUP_REQ:
			hero->SkillLevelUp( ((MSG_REQ_CHEAT_SKILLUP*)msgRoot)->mAddedValue );
			break;
		case NM_CHEAT_MAKE_REQ:
			try
			{
				if ( clientInfo->inventoryDatabase )
					throw ERROR_CHEAT_MAKE_NOTYET;

				MSG_REQ_CHEAT_MAKE* recvMsg    = (MSG_REQ_CHEAT_MAKE*)msgRoot;
				TB_ITEM_DEFINE*     itemDefine = ITEMMANAGER->GetItemDefineByIndex( recvMsg->mItemIndex );
				short               number     = hero->GetEmptyBagNumber( );

				if ( itemDefine!= NULL && recvMsg->mCount > 0 && number != INVENTORY_BAG_NONE )
				{
					HANDLE            handle          = NULL;
					INVENTORY_INSERT* inventoryInsert = (INVENTORY_INSERT*)NETWORK2->GetSQL( &handle, SQL_GAME_PROCESS_INVENTORY_INSERT );
					TB_INVENTORY*     table           = inventoryInsert->table;
					long&             rowCount        = inventoryInsert->rowCount;
					long              length          = sizeof(INVENTORY_INSERT) - sizeof(inventoryInsert->table);

					short remainCount = recvMsg->mCount;
					short shortCount;

					inventoryInsert->characterIdx = hero->GetObjectID( );

					while ( remainCount > 0 && number != INVENTORY_BAG_NONE )
					{
						hero->CreateInventory( itemDefine, number, remainCount, &shortCount );

						table->idx = 0;
						table->itemDefineIdx   = itemDefine->idx;
						table->itemDefineIndex = itemDefine->index;
						table->number          = number;
						table->count           = (+shortCount);
						table->enhanced        = recvMsg->mEnhance;
						table->cardSlot1       = recvMsg->mCardSlot[0];
						table->cardSlot2       = recvMsg->mCardSlot[1];
						table->cardSlot3       = recvMsg->mCardSlot[2];
						table->cardSlot4       = recvMsg->mCardSlot[3];
						table->cardSlot5       = recvMsg->mCardSlot[4];

						table++;
						rowCount++;

						remainCount = remainCount - shortCount;
						number      = hero->GetEmptyBagNumber( (++number) );
					}

					length += (sizeof(inventoryInsert->table) * inventoryInsert->rowCount);

					clientInfo->inventoryDb.insert = NETWORK2->SendSQL( perSocketContext, handle, length );
					if ( clientInfo->inventoryDb.insert == false )
					{
						NETWORK2->ReleaseSQL( handle, length );
						throw ERROR_CHEAT_MAKE_FAIL;
					}
				}
				else
					throw ERROR_CHEAT_MAKE_FAIL;
			}
			catch ( int error )
			{
				ResMsgError( perSocketContext, NM_CHEAT, NM_CHEAT_MAKE_RES, error );
			}
			break;
		case NM_CHEAT_GOLD_REQ:
			{
				MSG_REQ_CHEAT_GOLD* recvMsg = (MSG_REQ_CHEAT_GOLD*)msgRoot;
				hero->AddMoney( hero->GetObject( ), recvMsg->mValue );
			}
			break;
		case NM_CHEAT_SUMMON_REQ:
			{
				MSG_REQ_CHEAT_SUMMON* recvMsg = (MSG_REQ_CHEAT_SUMMON*)msgRoot;
				AIMANAGER->CheatSummonMonRegen( hero->GetMapNumber(), hero->GetXPos(), hero->GetYPos(), recvMsg->mMonsterNum, recvMsg->mValue );
			}
			break;
		case NM_CHEAT_REMOVE_REQ:
			OBJECTMANAGER->CheatRemoveMonster( ((MSG_REQ_CHEAT_REMOVE*)msgRoot)->mObjIndex );
			break;

		case NM_CHEAT_KILL_REQ:
			OBJECTMANAGER->CheatKillMonster( ((MSG_REQ_CHEAT_KILL*)msgRoot)->mObjIndex, hero->GetObjectID() );
			break;
		}
		break; // NM_CHEAT

	case NM_PARTY:
		/*-- ClientInfo::Complete 필터링 - Check / CharacterSelect / GameIn
		*/
		if ( (clientInfo->completeData & 0x0007) != 0x0007 )
			return false;

		/*-- MSGROOT::Protocol 필터링 - 알수없는 경우 소켓연결을 해제한다.
		*/
		if ( (unsigned char)msgRoot->Protocol >= NM_PARTY_MAX )
		{
			PostServerEvent( "END TASK - UNKNOWN NM_PARTY::PROTOCOL(=%d):MEMBER_IDX(=%d):CHARACTER_IDX(=%d):INETNUM(=%s).",
							 msgRoot->Protocol,
							 clientInfo->memberIdx,
							 clientInfo->characterIdx,
							 inet_ntoa( perSocketContext->addr.sin_addr) );
			return false;
		}

		switch( msgRoot->Protocol )
		{
		case NM_PARTY_ADD_REQ:
			{
				MSG_REQ_PARTY_ADD* pmsg = (MSG_REQ_PARTY_ADD*)msgRoot;
				pmsg->Name[ MAX_NAME_SIZE ] = 0;

				try
				{
					wchar_t* heroName = hero->GetName( );

					// 동일 이름일경우 무시
					if( wcsicmp( pmsg->Name, heroName ) == 0 )
						throw ERROR_PARTY_ADD_FAIL;

					if( hero->IsRequestRejection() == true )		/// 신청자가 신청 가능 상태인지 검사
						throw ERROR_PARTY_ADD_STATUS;

					cPlayer* other = mpObjectManager->GetPlayer( pmsg->Name );
					if( other == NULL )
						throw ERROR_PARTY_ADD_USER;					/// 초대한캐릭터 없음.

					if( other->IsRequestRejection() == true )		/// 신청자가 메세지받고있으면 에러!!!
						throw ERROR_PARTY_ADD_STATUS;

					if( other->GetGameIn() == false )				/// 맵이동등 상황이 아님.
						throw ERROR_PARTY_ADD_MAP;

					sGameOption* toOption1 = other->GetOptionData();
					if( toOption1->option1.rejectionParty == true )	/// 옵션 거부
						throw ERROR_PARTY_ADD_OPTIONREFUSE;

					ePLAYER_PARTYSTATE heroState = hero->GetPartyState();
					if( heroState == ePARTY_RECVJOIN )
						throw ERROR_PARTY_ADD_STATUS;				// 자기자신이 다른파티로부터 파티신청받고 있음

					if( heroState == ePARTY_SENDJOIN )
						throw ERROR_PARTY_ADD_CONTINUE;				// 자기자신이 초대중인 유저가 있으므로 초대불가.

					// pvp 중인경우 같은 팀인지 확인
					if( other->GetPvPDMIdx() != 0 && hero->GetPvPDMIdx() != 0 && other->GetPvPDMTeam() != hero->GetPvPDMTeam() )
						throw ERROR_PARTY_ADD_STATUS;

					/// 파티결성 상태에서 다른 유저 추가시
					if( heroState == ePARTY_COMPLETE && hero->GetPartyIndex() > 0 )
					{
						cParty* party = mpPartyManager->GetParty( hero->GetPartyIndex() );
						if ( !party )
						{
							PostServerEvent( "SQL_GAME_PROCESS_PARTY_USERIDX_SEARCH - party is null(%d)", hero->GetPartyIndex() );
							throw ERROR_PARTY_ADD_CREATEFAIL;
						}

						/// 1. 파티장 검사
						if( hero->GetObjectID() != party->GetLeader() )
							throw ERROR_PARTY_ADD_NOTLEADER;

						/// 2. 파티인원 검사
						if( !(party->GetCount() < MAX_PARTY) )
							throw ERROR_PARTY_ADD_COUNT;
					}

					/// 초대받은 유저 상태 검사
					ePLAYER_PARTYSTATE otherState = other->GetPartyState();
					if( otherState == ePARTY_COMPLETE )
						throw ERROR_PARTY_ADD_COMPLETE;									/// 이미 다른 파티중

					if( otherState == ePARTY_RECVJOIN || otherState == ePARTY_SENDJOIN )
						throw ERROR_PARTY_ADD_STATUS;									/// 불가 상태

					/// 신청한 유저 - 신청성공 알림
					ResMsgError( perSocketContext, NM_PARTY, NM_PARTY_ADD_RES, ERROR_PARTY_ADD_SUCCESS );

					/// 초대 받은 유저
					HANDLE		        toHandle = NULL;
					MSG_SYN_PARTY_ADD*	synMsg = (MSG_SYN_PARTY_ADD*)GetMsgRoot( &toHandle, other->GetConnectionIdx(), NM_PARTY, NM_PARTY_ADD_SYN );
					if( synMsg != NULL )
					{
						wcscpy( synMsg->Name, hero->GetName() );
						SendMsgRoot( toHandle, sizeof(MSG_SYN_PARTY_ADD) );
					}

					/// 신청 정보 저장
					mpPartyManager->AddRequest( other->GetObjectID(), hero->GetObjectID() );

					// 플레이어 상태 변경
					hero->SetPartyState( ePARTY_SENDJOIN );
					other->SetPartyState( ePARTY_RECVJOIN );
					other->StartRequestRejection( eREQREJCT_PARTY );
				}
				catch ( int errorCode )
				{
					ResMsgError( perSocketContext, NM_PARTY, NM_PARTY_ADD_RES, errorCode );
				}
			}
			break;
		case NM_PARTY_REP_REQ:
			{
				MSG_REQ_PARTY_REP* pmsg = (MSG_REQ_PARTY_REP*)msgRoot;

				try
				{
					unsigned long reqUserIdx = mpPartyManager->GetRequest( heroIdx );

					// 세팅 초기화
					hero->SetPartyState( ePARTY_NONE );
					hero->EndRequestRejection( eREQREJCT_PARTY );
					mpPartyManager->DelRequest( heroIdx );

					/// 자기자신이면 오류
					if( heroIdx == reqUserIdx )
						throw ERROR_PARTY_REP_FAIL;

					/// 나를 초대한 유저를 찾기
					cPlayer* from = mpObjectManager->GetPlayer( reqUserIdx );
					if ( from == NULL )
						throw ERROR_PARTY_REP_FAIL;

					/// 응답에 관한 처리
					unsigned long partyIndex = from->GetPartyIndex();
					if( pmsg->Reply == 0 )	
					{	
						/// 거절시
						ePLAYER_PARTYSTATE state = ( partyIndex > 0 ) ? ePARTY_COMPLETE : ePARTY_NONE;
						from->SetPartyState( state );

						ResMsgError( from->GetConnectionIdx(), NM_PARTY, NM_PARTY_ADD_RES, ERROR_PARTY_ADD_REFUSE );
						ResMsgError( perSocketContext, NM_PARTY, NM_PARTY_REP_RES, ERROR_PARTY_REP_REFUSE );
					}	
					else
					{
						/// 신청한 유저의 상태 원래대로 되돌리기
						from->SetPartyState( ePARTY_COMPLETE );

						/// 1. 파티 생성시
						if( partyIndex == 0 )
						{
							unsigned long index = mpPartyManager->AddParty( reqUserIdx, heroIdx );
							if ( index > 0 )
							{
								from->SetPartyIndex( index );
								hero->SetPartyState( ePARTY_COMPLETE );
								hero->SetPartyIndex( index );

								/// 생성 알림
								ResMsgError( from->GetConnectionIdx(), NM_PARTY, NM_PARTY_ADD_RES, ERROR_PARTY_ADD_CREATE_SUCCESS );
								ResMsgError( perSocketContext, NM_PARTY, NM_PARTY_ADD_RES, ERROR_PARTY_ADD_CREATE_SUCCESS );

								/// 파티정보 send
								cParty* party = mpPartyManager->GetParty( index );
								if( party != NULL )
								{
									mpPartyManager->ResPartyList( from, party );
									mpPartyManager->ResPartyList( hero, party );
								}
							}
							else
							{
								ResMsgError( from->GetConnectionIdx(), NM_PARTY, NM_PARTY_ADD_RES, ERROR_PARTY_ADD_CREATEFAIL );
								ResMsgError( perSocketContext, NM_PARTY, NM_PARTY_REP_RES, ERROR_PARTY_REP_FAIL );
								PostServerEvent( "Error NM_PARTY_REP_REQ : failed to create party!" );
							}
						}
						/// 2. 파티에 추가시
						else
						{
							cParty* party = mpPartyManager->GetParty( partyIndex );
							if ( party == NULL )
							{
								ResMsgError( from->GetConnectionIdx(), NM_PARTY, NM_PARTY_ADD_RES, ERROR_PARTY_ADD_CREATEFAIL );
								ResMsgError( perSocketContext, NM_PARTY, NM_PARTY_REP_RES, ERROR_PARTY_REP_FAIL );
								PostServerEvent( "Error NM_PARTY_REP_REQ : failed to find party(%d)!", partyIndex );
								return false;
							}

							/// 파티인원 초과
							if( !(party->GetCount() < MAX_PARTY) )
								throw ERROR_PARTY_REP_FULL;

							party->AddUser( heroIdx );
							hero->SetPartyState( ePARTY_COMPLETE );
							hero->SetPartyIndex( partyIndex );

							/// 파티정보 send
							mpPartyManager->ResPartyList( hero, party );			/// 새로들어온 파티원은 파티리스트 send
							mpPartyManager->ResPartyAddOne( heroIdx, party );		/// 기존유저에게는 추가된 파티원 정보 send
						}
					}
				}
				catch ( int error )
				{
					ResMsgError( perSocketContext, NM_PARTY, NM_PARTY_REP_RES, error );
				}
			}
			break; // NM_PARTY_REP_REQ
		case NM_PARTY_OUT_REQ:
			{
				// 파티 탈퇴 요청
				mpPartyManager->PartyOutPlayer( clientInfo->hero );
			}
			break;
		case NM_PARTY_CHANGELEADER_REQ:
			{
				// 파티장 위임 요청
				MSG_REQ_PARTY_CHANGELEADER* pmsg = (MSG_REQ_PARTY_CHANGELEADER*)msgRoot;
				mpPartyManager->PartyChangeLeader( clientInfo->hero, pmsg->UserIndex );
			}
			break;
		case NM_PARTY_CHANGEDIVIDE_REQ:
			{
				// 파티 분배타입 변경 요청 
				MSG_REQ_PARTY_CHANGEDIVIDE* pmsg = (MSG_REQ_PARTY_CHANGEDIVIDE*)msgRoot;
				mpPartyManager->PartyChangeDivide( clientInfo->hero, pmsg->type );
			}
			break;
		case NM_PARTY_THROW_REQ:
			{
				// 파티원 추방 요청 
				MSG_REQ_PARTY_THROW* pmsg     = (MSG_REQ_PARTY_THROW*)msgRoot;
				cPlayer*             toPlayer = mpObjectManager->GetPlayer( pmsg->UserIndex );
				unsigned long        partyIdx = hero->GetPartyIndex( );
				try
				{
					if ( toPlayer == NULL )
						throw ERROR_PARTY_THROW_FAIL;

					if ( hero == toPlayer )
						throw ERROR_PARTY_THROW_FAIL;

					// 파티가 없거나, 같은 파티가 아니라면 
					if ( (partyIdx == 0 || toPlayer->GetPartyIndex( ) == 0) || (partyIdx != toPlayer->GetPartyIndex( )) )
						throw ERROR_PARTY_THROW_FAIL;

					cParty* party = mpPartyManager->GetParty( partyIdx );
					if ( party == NULL )
						throw ERROR_PARTY_THROW_FAIL;

					// 권한없음
					if ( party->GetLeader( ) != heroIdx )
						throw ERROR_PARTY_THROW_LEADER;

					// 해당 유저 추방
					{
						party->DeleteUser( pmsg->UserIndex );
						toPlayer->SetPartyIndex( 0 );
						toPlayer->SetPartyState( ePARTY_NONE );

						// 파티장에게 추방성공 알림
						ResMsgError( perSocketContext, NM_PARTY, NM_PARTY_THROW_RES, ERROR_PARTY_THROW_SUCESS );

						HANDLE handle = NULL;
						if ( GetMsgRoot( &handle, toPlayer->GetConnectionIdx( ), NM_PARTY, NM_PARTY_CUT_SYN ) )
							SendMsgRoot( handle, sizeof(MSG_SYN_PARTY_CUT) );
					}

					// 남은 파티원들 처리
					// 남은유저가 2명이상이면 파티추방 알리기
					if( party->GetCount( ) >= 2 )
					{
						HANDLE         handle     = NULL;
						cPlayer*       partyUser  = 0;
						unsigned long* userArr    = party->GetUserArr( );
						unsigned int   partyCount = party->GetCount( );

						for ( unsigned int i = 0; i < partyCount; ++i, ++userArr )
						{
							partyUser = OBJECTMANAGER->GetPlayer( (*userArr) );
							if ( partyUser!= NULL )
							{
								MSG_SYN_PARTY_THROW* sendMsg = (MSG_SYN_PARTY_THROW*)GetMsgRoot( &handle, partyUser->GetConnectionIdx( ), NM_PARTY, NM_PARTY_THROW_SYN );
								if ( sendMsg != NULL )
								{
									sendMsg->UserIndex = pmsg->UserIndex;
									SendMsgRoot( handle, sizeof(MSG_SYN_PARTY_THROW) );
								}
							}
						}
					}
					else
					{
						// 1명이면 파티해산
						HANDLE         handle     = NULL;
						cPlayer*       partyUser  = 0;
						unsigned long* userArr    = party->GetUserArr( );
						unsigned int   partyCount = party->GetCount( );

						for ( unsigned int i = 0; i < partyCount; ++i, ++userArr )
						{
							partyUser = OBJECTMANAGER->GetPlayer( (*userArr) );
							if ( partyUser != NULL )
							{
								partyUser->SetPartyIndex( 0 );
								partyUser->SetPartyState( ePARTY_NONE );

								if ( GetMsgRoot( &handle, partyUser->GetConnectionIdx( ), NM_PARTY, NM_PARTY_DELETE_SYN ) )
									SendMsgRoot( handle, sizeof(MSG_SYN_PARTY_DELETE) );
							}
							else
							{
								assert(0);
								PostServerEvent( "Error NM_PARTY_THROW_REQ : failed to find user(%d)", (*userArr) );
								continue;
							}
						}
						// 파티 삭제
						mpPartyManager->DeleteParty( partyIdx );
					}
				}
				catch (int error)
				{
					ResMsgError( perSocketContext, NM_PARTY, NM_PARTY_THROW_RES, error );
				}
			}
			break;
		}
		break; // NM_PARTY
	case NM_QUEST:
		/*-- ClientInfo::Complete 필터링 - Check / CharacterSelect / GameIn
		*/
		if ( (clientInfo->completeData & 0x0007) != 0x0007 )
			return false;

		/*-- MSGROOT::Protocol 필터링 - 알수없는 경우 소켓연결을 해제한다.
		*/
		if ( (unsigned char)msgRoot->Protocol >= NM_QUEST_MAX )
		{
			PostServerEvent( "END TASK - UNKNOWN NM_QUEST::PROTOCOL(=%d):MEMBER_IDX(=%d):CHARACTER_IDX(=%d):INETNUM(=%s).",
							 msgRoot->Protocol,
							 clientInfo->memberIdx,
							 clientInfo->characterIdx,
							 inet_ntoa( perSocketContext->addr.sin_addr) );
			return false;
		}

		if ( !clientInfo->complete.quest )
			return false;

		switch( msgRoot->Protocol )
		{
		case NM_QUEST_ADD_REQ:
			{
				// NPC에 의한것이 아닌 모든 경우
				// 퀘스트 신규 등록 요청 
				MSG_REQ_QUEST_ADD* pmsg = (MSG_REQ_QUEST_ADD*)msgRoot;
				try
				{
					// DB 사용중인지 체크
					if ( clientInfo->questDb.questInsert )
						throw ERROR_QUEST_ADD_NOTYET;

					// 신규 가능한 퀘스트 조건 검색
					int errorCode = hero->IsRegistQuest( eQUESTADD_ETC, pmsg->questIndex, pmsg->npcIndex );
					if ( errorCode != ERROR_QUEST_ADD_SUCCESS )
						throw errorCode;

					//신규퀘스트
					errorCode = hero->SaveQuestInsert( (ULONG_PTR)perSocketContext, heroIdx, pmsg->questIndex );
					if ( errorCode != ERROR_QUEST_ADD_SUCCESS )
						throw errorCode;

					clientInfo->questDb.questInsert = true;
					clientInfo->inventoryDb.reward = true;
				}
				catch ( int error )
				{
					ResMsgError( perSocketContext, NM_QUEST, NM_QUEST_ADD_RES, error );
				}
			}
			break;
		case NM_QUEST_ADDBYITEM_REQ:
			{
				// 의뢰아이템에 의한 퀘스트 시작
				MSG_REQ_QUEST_ADDBYITEM* pmsg = (MSG_REQ_QUEST_ADDBYITEM*)msgRoot;
				try
				{
					// DB 사용중인지 체크
					if( clientInfo->questDb.questInsert || clientInfo->inventoryDatabase )
						throw ERROR_QUEST_ADDBYITEM_NOTYET;

					// 아이템 사용조건 체크
					unsigned long questIndex = 0;
					int errorCode = hero->IsUseQuestItem( pmsg->number, questIndex );
					if ( errorCode != ERROR_QUEST_ADDBYITEM_SUCCESS )
						throw errorCode;

					// 퀘스트 가능 조건 검색
					errorCode = hero->IsRegistQuestItem( questIndex );
					if ( errorCode != ERROR_QUEST_ADDBYITEM_SUCCESS )
						throw errorCode;

					// 퀘스트 발급 
					errorCode = hero->SaveQuestInsertByItem( (ULONG_PTR)perSocketContext,
														     heroIdx,
															 questIndex,
															 (unsigned short)pmsg->number );
					if ( errorCode != ERROR_QUEST_ADDBYITEM_SUCCESS )
						throw errorCode;

					clientInfo->questDb.questInsert = true;
					clientInfo->inventoryDb.reward = true;
				}
				catch ( int error )
				{
					ResMsgError( perSocketContext, NM_QUEST, NM_QUEST_ADDBYITEM_RES, error );
				}
			}
			break;
		case NM_QUEST_DEL_REQ:
			{
				// 퀘스트 포기 요청
				MSG_REQ_QUEST_DEL* pmsg = (MSG_REQ_QUEST_DEL*)msgRoot;
				try
				{
					// DB 사용중인지 검사
					if( clientInfo->questDb.questDelete )
						throw ERROR_QUEST_DEL_NOTYET;

					// 퀘스트번호 유효성 검사
					cQuestDefine* define = mpQuestManager->GetQuestDefine( pmsg->questIndex );
					if ( define == NULL )
						throw ERROR_QUEST_DEL_QUEST;

					// 보유퀘스트 검사
					int dbIndex = hero->IsKeepQuest( pmsg->arrayIdx, pmsg->questIndex );
					if ( dbIndex == -1 )
						throw ERROR_QUEST_DEL_QUEST;

					// 포기 요청 
					if ( hero->SaveQuestDelete( (ULONG_PTR)perSocketContext, pmsg, dbIndex ) == false )
						throw ERROR_QUEST_DEL_FAIL;
					
					// 삭제 요청
					clientInfo->questDb.questDelete = true;
					clientInfo->inventoryDb.take = true;
				}
				catch ( int error )
				{
					ResMsgError( perSocketContext, NM_QUEST, NM_QUEST_DEL_RES, error );
				}
			}
			break;
		case NM_QUEST_DELAUTO_REQ:
			{
				MSG_REQ_QUEST_DELAUTO* pmsg = (MSG_REQ_QUEST_DELAUTO*)msgRoot;
				try
				{
					if ( clientInfo->questDb.questDelete )
						throw ERROR_QUEST_DELAUTO_NOTYET;

					// 퀘스트번호 유효성 검사
					cQuestDefine* define = mpQuestManager->GetQuestDefine( pmsg->questIndex );
					if ( define == NULL )
						throw ERROR_QUEST_DELAUTO_FAIL;

					// 보유퀘스트 검사
					int dbIndex = hero->IsKeepQuest( pmsg->arrayIdx, pmsg->questIndex );
					if ( dbIndex == -1 )
						throw ERROR_QUEST_DELAUTO_FAIL;

					// 포기 요청 
					if ( hero->SaveQuestDeleteAuto( (ULONG_PTR)perSocketContext, pmsg, dbIndex ) == false )
						throw ERROR_QUEST_DELAUTO_FAIL;

					// 삭제 요청
					clientInfo->questDb.questDelete = true;
					clientInfo->inventoryDb.take = true;
				}
				catch ( int error )
				{
					ResMsgError( perSocketContext, NM_QUEST, NM_QUEST_DELAUTO_RES, error );
				}
			}
			break;
		}
	case NM_DUEL:
		/*-- ClientInfo::Complete 필터링 - Check / CharacterSelect / GameIn
		*/
		if ( (clientInfo->completeData & 0x0007) != 0x0007 )
			return false;

		/*-- MSGROOT::Protocol 필터링 - 알수없는 경우 소켓연결을 해제한다.
		*/
		if ( (unsigned char)msgRoot->Protocol >= NM_DUEL_MAX )
		{
			PostServerEvent( "END TASK - UNKNOWN NM_DUEL::PROTOCOL(=%d):MEMBER_IDX(=%d):CHARACTER_IDX(=%d):INETNUM(=%s).",
							 msgRoot->Protocol,
							 clientInfo->memberIdx,
							 clientInfo->characterIdx,
							 inet_ntoa( perSocketContext->addr.sin_addr) );
			return false;
		}

		switch( msgRoot->Protocol )
		{
		case NM_DUEL_ADD_REQ:
			{
				MSG_REQ_DUEL_ADD* pMsg = (MSG_REQ_DUEL_ADD*)msgRoot;

				int error = DUELMANAGER->ReqDuel( hero->GetObjectID(), pMsg->mTargetIdx );
				if( error != ERR_DUEL_ADD_SUCCESS )
				{
					/// 결투 조건 불만족시 결투 요청자에게 실패 메세지 발송
					HANDLE            handle  = NULL;
					MSG_RES_DUEL_ADD* sendMsg = (MSG_RES_DUEL_ADD*)GetMsgRoot( &handle, perSocketContext, NM_DUEL, NM_DUEL_ADD_RES );
					sendMsg->ErrorCode = error;
					SendMsgRoot( handle, sizeof(MSG_RES_DUEL_ADD) );
				}
				else
				{
					/// 결투 조건 만족시 결투 대상자에게 수락요청 메세지 발송
					cPlayer* pTarget = OBJECTMANAGER->GetPlayer( pMsg->mTargetIdx );
					if ( pTarget != NULL )
					{
						HANDLE handle  = NULL;
						MSG_RES_DUEL_ADD* sendMsg = (MSG_RES_DUEL_ADD*)GetMsgRoot( &handle, pTarget->GetConnectionIdx(), NM_DUEL, NM_DUEL_ADD_RES );
						if ( sendMsg != NULL )
						{
							sendMsg->mAttackerIdx = hero->GetObjectID();
							sendMsg->ErrorCode = ERR_DUEL_ADD_SUCCESS;
							SendMsgRoot( handle, sizeof(MSG_RES_DUEL_ADD) );

							/// 결투 대상 설정
							pTarget->SetDuelPlayerIdx( hero->GetObjectID(), false );
							hero->SetDuelPlayerIdx( pMsg->mTargetIdx, true );		

							/// 결투 대상자 메세지 대기상태 킴
							pTarget->StartRequestRejection( eREQREJCT_REQWAIT );
						}
					}
				}
			}
			break;
		case NM_DUEL_ACCEPT_REQ:
			DUELMANAGER->DuelAllReady( (MSG_REQ_DUEL_ACCEPT*)msgRoot, hero );
			break;
		case NM_DUEL_GIVEUP_RES:
			DUELMANAGER->DuelGiveUp( hero->GetDuelIdx(), hero->GetObjectID() );
			break;
		}
		break; // NM_DUEL
	case NM_SERVERTOOL:
		/*-- TOS_SERVERTOOL 메세지는 암호화 체크를 하지 않아 tos검사를 해준다.
		*/
		if( packet->tos != TOS_SERVERTOOL )
			return false;

		/*-- MSGROOT::Protocol 필터링 - 알수없는 경우 소켓연결을 해제한다.
		*/
		if ( (unsigned char)msgRoot->Protocol >= NM_SERVERTOOL_MAX )
		{
			PostServerEvent( "END TASK - UNKNOWN NM_SERVERTOOL::PROTOCOL(=%d):MEMBER_IDX(=%d):CHARACTER_IDX(=%d):INETNUM(=%s).",
							 msgRoot->Protocol,
							 clientInfo->memberIdx,
							 clientInfo->characterIdx,
							 inet_ntoa( perSocketContext->addr.sin_addr) );
			return false;
		}

		switch( msgRoot->Protocol )
		{
		case NM_SERVERTOOL_PING_REQ:
			{
				MSG_REQ_SRVTOOL_PING* reqMsg  = (MSG_REQ_SRVTOOL_PING*)msgRoot;

				SIZE_T quotaPagedPoolUsage;
				SIZE_T quotaNonePagedPoolUsage;
				SIZE_T workingSetSize;

				mSocketContextPool->GetProcessMemoryInfo( quotaPagedPoolUsage, quotaNonePagedPoolUsage, workingSetSize );

				HANDLE                handle  = NULL;
				MSG_RES_SRVTOOL_PING* sendMsg = (MSG_RES_SRVTOOL_PING*)GetMsgRoot( &handle, perSocketContext, NM_SERVERTOOL, NM_SERVERTOOL_PING_RES );
					sendMsg->mToolSendTime = reqMsg->mToolSendTime;
					sendMsg->mUserCnt      = (unsigned long)quotaPagedPoolUsage;
					sendMsg->mUserIn       = true;
					return SendMsgRoot( handle, sizeof(MSG_RES_SRVTOOL_PING) );
			}
			break;
		case NM_SERVERTOOL_SERVER_INFO_REQ:
			{
				HANDLE                         handle  = NULL;
				MSG_RES_SERVTOOL_CHANNEL_INFO* sendMsg = (MSG_RES_SERVTOOL_CHANNEL_INFO*)GetMsgRoot( &handle, perSocketContext, NM_SERVERTOOL, NM_SERVERTOOL_SERVER_INFO_RES );
					sendMsg->mServerNum  = mServerNum;
					sendMsg->mChannelNum = mChannelNum;
					wcscpy( sendMsg->mServerName, mServerName );
				return SendMsgRoot( handle, sizeof(MSG_RES_SERVTOOL_CHANNEL_INFO) );
			}
			break;
		case NM_SERVERTOOL_CHAT_NOTICE_REQ:
			{
				MSG_REQ_CHEAT_NOTICE_CHANNEL* req     = (MSG_REQ_CHEAT_NOTICE_CHANNEL*)msgRoot;
				req->mMessage[MAX_CHAT_SIZE]=0;

				wchar_t* message = req->mMessage;

				if ( message == 0 || message[0] == 0 )
					break;

				/// 동기
				MSG_SYN_CHEAT_NOTICE_CHANNEL syn;
				memset( &syn, 0, sizeof(syn) );
					syn.Category = NM_CHEAT;
					syn.Protocol = NM_CHEAT_NOTICE_CHANNEL_SYN;
					wcscpy( syn.mMessage, message );
				OBJECTMANAGER->SendNotice( (char*)&syn, syn.GetMsgLength( ) );
			}
			break;
		}
		break; // NM_SERVERTOOL
	case NM_PVP:
		/*-- ClientInfo::Complete 필터링 - Check / CharacterSelect / GameIn
		*/
		if ( (clientInfo->completeData & 0x0007) != 0x0007 )
			return false;

		/*-- MSGROOT::Protocol 필터링 - 알수없는 경우 소켓연결을 해제한다.
		*/
		if ( (unsigned char)msgRoot->Protocol >= NM_PVP_MAX )
		{
			PostServerEvent( "END TASK - UNKNOWN NM_PVP::PROTOCOL(=%d):MEMBER_IDX(=%d):CHARACTER_IDX(=%d):INETNUM(=%s).",
							 msgRoot->Protocol,
							 clientInfo->memberIdx,
							 clientInfo->characterIdx,
							 inet_ntoa( perSocketContext->addr.sin_addr) );
			return false;
		}

		switch( msgRoot->Protocol )
		{
		case NM_PVP_DM_JOIN_REQ:
			if ( mPVPData.status == _E_PVP_RUNABLE_ )
			{
				clientInfo->request.gotoInstantDungeon = true;
			}
			break;
		case NM_PVP_RESURRECTION_REQ:
			if ( PVPMANAGER->GetPvPDMObject( hero->GetPvPDMIdx() ) != NULL )
			{
				hero->PvPResurrection( );
			}
			else
			{
				NETWORK2->PostServerEvent( "NM_PVP_RESURRECTION_REQ pDM[%d,%d,%d,%d] == NULL",
											hero->GetObjectID( ),
											hero->GetState( ),
											hero->GetStateStop( ),
											hero->GetPvPDMIdx() );
			}
			break;
		case NM_PVP_DM_ENDACCEPT_REQ:
			{
				if( mChannelNum == CHN_11 && hero->GetPvPDMIdx() == 0 )
					clientInfo->request.returnToMap = true;
			}
			break;
		case NM_PVP_DM_END_REQ:
			{
				cDeathMatchObject* pDM = PVPMANAGER->GetPvPDMObject( hero->GetPvPDMIdx() );
				if ( pDM != NULL )
				{
					pDM->SendOut( hero->GetObjectID( ), hero->GetPvPDMTeam( ) );
					clientInfo->request.returnToMap = true;
				}
				else
				{
					NETWORK2->PostServerEvent( "NM_PVP_RESURRECTION_REQ pDM[%d,%d,%d,%d] == NULL",
											   hero->GetObjectID( ),
											   hero->GetState( ),
											   hero->GetStateStop( ),
											   hero->GetPvPDMIdx() );
				}
			}
			break;
		}
		break; // NM_PVP
	case NM_GUILD:
		/*-- ClientInfo::Complete 필터링 - Check / CharacterSelect / GameIn
		*/
		if ( (clientInfo->completeData & 0x0007) != 0x0007 )
			return false;

		/*-- MSGROOT::Protocol 필터링 - 알수없는 경우 소켓연결을 해제한다.
		*/
 		if ( (unsigned char)msgRoot->Protocol >= NM_GUILD_MAX )
		{
			PostServerEvent( "END TASK - UNKNOWN NM_GUILD::PROTOCOL(=%d):MEMBER_IDX(=%d):CHARACTER_IDX(=%d):INETNUM(=%s).",
							 msgRoot->Protocol,
							 clientInfo->memberIdx,
							 clientInfo->characterIdx,
							 inet_ntoa( perSocketContext->addr.sin_addr) );
			return false;
		}

		switch ( msgRoot->Protocol )
		{
		case NM_GUILD_ADD_REQ:
			{
				// 길드 초대
				MSG_REQ_GUILD_ADD* pmsg = (MSG_REQ_GUILD_ADD*)msgRoot;

				try
				{
					// 길드가입 여부 
					if ( !( hero->GetGuildIndex() > 0 ) )
						throw ERROR_GUILD_ADD_FAIL;

					// HERO가 신청가능한 상태 검사
					if ( hero->IsRequestRejection() == true )
						throw ERROR_GUILD_ADD_STATUSHERO;

					// 이미 신청중인지 여부
					if ( hero->IsGuildAddReq() == true )
						throw ERROR_GUILD_ADD_REQUEST;

					// 상대방 이름 입력 검사
					pmsg->name[MAX_NAME_SIZE]=0;
					if ( wcslen( pmsg->name ) == 0 )
						throw ERROR_GUILD_ADD_FAIL;

					// 자기 자신 이름인지 검사
					wchar_t* heroName = hero->GetName();
					if( wcsicmp( pmsg->name, heroName ) == 0 )
						throw ERROR_GUILD_ADD_FAIL;

					/// 이름으로 유저 찾기
					cPlayer* other = mpObjectManager->GetPlayer( pmsg->name );
					if ( other != NULL )
					{
						if ( other->GetGameIn() == false )			/// 맵이동등 상황이 아님.
							throw ERROR_GUILD_ADD_FAIL;

						if ( other->GetGuildIndex() > 0 )
							throw ERROR_GUILD_ADD_HAVEGUILD;

						// other가 신청받을수 있는 상태인지 검사
						if ( other->IsRequestRejection() == true )
							throw ERROR_GUILD_ADD_STATUS;

						cGuild* guild = mpGuildManager->GetGuild( hero->GetGuildIndex() );
						if ( guild == NULL )
							throw ERROR_GUILD_ADD_FAIL;

						if ( guild->GetGuildSize() > MAX_GUILD )
							throw ERROR_GUILD_ADD_MAXGUILD;

						// 1. hero에게 신청되었음을 알림
						ResMsgError( perSocketContext, NM_GUILD, NM_GUILD_ADD_RES, ERROR_GUILD_ADD_SUCCESS );

						// 2. 상대방에게 신청하기
						HANDLE  toHandle = NULL;
						MSG_SYN_GUILD_ADD* sendMsg = (MSG_SYN_GUILD_ADD*)GetMsgRoot( &toHandle, other->GetConnectionIdx(), NM_GUILD, NM_GUILD_ADD_SYN );
						if( sendMsg != NULL )
						{
							wcscpy( sendMsg->name,		hero->GetName() );
							wcscpy( sendMsg->guildName, guild->GetGuildName() );
							SendMsgRoot( toHandle, sizeof(MSG_SYN_GUILD_ADD) );
						}

						// 신청 정보 저장
						hero->SetGuildAddReq( true );
						mpGuildManager->AddRequest( other->GetObjectID(), hero->GetObjectID() );

						// 신청받고있는 상태로 바꿈
						other->StartRequestRejection( eREQREJCT_GUILD );
					}
					else
						throw ERROR_GUILD_ADD_NOTEXIST;
				}
				catch (int error)
				{
					ResMsgError( perSocketContext, NM_GUILD, NM_GUILD_ADD_RES, error );
				}
			}
			break;
		case NM_GUILD_REPLY_REQ: 
			{
				// 길드 초대 답변 보냄
				MSG_REQ_GUILD_REPLY* pmsg = (MSG_REQ_GUILD_REPLY*)msgRoot;

				try
				{
					unsigned long otherIdx = mpGuildManager->GetRequest( heroIdx );

					// 세팅 초기화 (신청정보 삭제)
					hero->EndRequestRejection( eREQREJCT_GUILD );
					mpGuildManager->DelRequest( heroIdx );

					/// 자기자신이면 오류
					if( heroIdx == otherIdx )
						throw ERROR_GUILD_ADDREPLY_FAIL;

					cPlayer* other = mpObjectManager->GetPlayer( otherIdx );
					if( other == NULL )
						throw ERROR_GUILD_ADDREPLY_FAIL;

					// 신청상태 풀기
					other->SetGuildAddReq( false );

					// 거절 
					if( pmsg->reply == 0 )
					{
						ResMsgError( other->GetConnectionIdx(), NM_GUILD, NM_GUILD_ADD_RES, ERROR_GUILD_ADD_REFUSE );
						throw ERROR_GUILD_ADDREPLY_REFUSE;
					}
					// 수락
					else
					{
						// 1. DB 사용중인지 체크
						if ( clientInfo->guildDb.guildAdd )
							throw ERROR_GUILD_ADDREPLY_FAIL;

						// 2. 길드 존재 여부 체크
						cGuild* guild = mpGuildManager->GetGuild( other->GetGuildIndex() );
						if( guild == NULL )
							throw ERROR_GUILD_ADDREPLY_FAIL;

						// 길드에 삽입
						clientInfo->guildDb.guildAdd = GuildUserAdd( perSocketContext, other->GetGuildIndex(), heroIdx );
					}
				}
				catch (int error)
				{
					ResMsgError( perSocketContext, NM_GUILD, NM_GUILD_REPLY_RES, error );
				}
			}
			break;
		case NM_GUILD_OUT_REQ:
			{
				// 길드 탈퇴 요청
				try
				{
					if( clientInfo->guildDb.guildOut )
						throw ERROR_GUILD_OUT_FAIL;

					// 길드 가입중 아니면 
					unsigned long guildIndex = hero->GetGuildIndex();
					if( !(guildIndex > 0) )
						throw ERROR_GUILD_OUT_FAIL;

					char heroPos = hero->GetGuildPosition();
					if( heroPos == eGUILD_NONE )
						throw ERROR_GUILD_OUT_FAIL;

					// 길드장 탈퇴불가
					if( hero->GetGuildPosition() == eGUILD_MASTER )
						throw ERROR_GUILD_OUT_MASTER;

					clientInfo->guildDb.guildOut = GuildUserOut( perSocketContext, guildIndex, heroIdx );
				}
				catch (int error)
				{
					ResMsgError( perSocketContext, NM_GUILD, NM_GUILD_OUT_RES, error );
				}
			}
			break;
		case NM_GUILD_CUT_REQ:
			{
				MSG_REQ_GUILD_CUT* pmsg = (MSG_REQ_GUILD_CUT*)msgRoot;

				// 길드 추방 유청
				try
				{
					if( clientInfo->guildDb.guildCut )
						throw ERROR_GUILD_CUT_FAIL;

					// 길드 가입중 아니면 
					unsigned long guildIndex = hero->GetGuildIndex();
					if( !(guildIndex > 0) )
						throw ERROR_GUILD_CUT_FAIL;

					// 자기자신은 불가
					if( pmsg->characterIdx == heroIdx )
						throw ERROR_GUILD_CUT_FAIL;

					// 길드장 검사
					char position = hero->GetGuildPosition();
					if( position != eGUILD_MASTER )
						throw ERROR_GUILD_CUT_MASTER;

					// 내 길드원인지 검사
					TB_GUILD_USER* user = mpGuildManager->IsMyGuildUser( guildIndex, pmsg->characterIdx );
					if( user == NULL )
						throw ERROR_GUILD_CUT_FAIL;
	
					clientInfo->guildDb.guildCut = GuildUserCut( perSocketContext, guildIndex, pmsg->characterIdx );
				}
				catch (int error)
				{
					ResMsgError( perSocketContext, NM_GUILD, NM_GUILD_CUT_RES, error );
				}
			}
			break;
		case NM_GUILD_DELETE_REQ:
			{
				try
				{
					if( clientInfo->guildDb.guildDelete )
						throw ERROR_GUILD_DELETE_FAIL;

					// 길드 가입중 아니면 
					unsigned long guildIndex = hero->GetGuildIndex();
					if( !(guildIndex > 0) )
						throw ERROR_GUILD_DELETE_FAIL;

					// 길드장 검사
					char position = hero->GetGuildPosition();
					if( position != eGUILD_MASTER )
						throw ERROR_GUILD_DELETE_MASTER;

					// 길드원이 남아있는지 여부
					cGuild* guild = mpGuildManager->GetGuild( guildIndex );
					if ( guild == NULL )
						throw ERROR_GUILD_DELETE_FAIL;

					if ( guild->GetGuildSize() > 1 )
						throw ERROR_GUILD_DELETE_USEREXIST;

					clientInfo->guildDb.guildDelete = GuildDelete( perSocketContext, guildIndex, heroIdx );
				}
				catch (int error)
				{
					ResMsgError( perSocketContext, NM_GUILD, NM_GUILD_DELETE_RES, error );
				}
			}
			break;
		case NM_GUILD_GIVEPOSITION_REQ:
			{
				MSG_REQ_GUILD_GIVEPOSITION* pmsg = (MSG_REQ_GUILD_GIVEPOSITION*)msgRoot;

				try
				{	
					if( clientInfo->guildDb.guildGivePosition )
						throw ERROR_GUILD_GIVEPOSITION_FAIL;

					// 길드 가입중 아니면 
					unsigned long guildIndex = hero->GetGuildIndex();
					if( !(guildIndex > 0) )
						throw ERROR_GUILD_GIVEPOSITION_FAIL;

					// 자기자신은 불가
					if( pmsg->characterIdx == heroIdx )
						throw ERROR_GUILD_GIVEPOSITION_FAIL;

					// 권한 검사
					if( mpGuildManager->CheckChangePosition( hero, pmsg->characterIdx, pmsg->position ) == false )
						throw ERROR_GUILD_GIVEPOSITION_POSITION;

					clientInfo->guildDb.guildGivePosition = GuildUserGivePosition( perSocketContext, guildIndex, pmsg->characterIdx, pmsg->position );
				}
				catch (int error)
				{
					ResMsgError( perSocketContext, NM_GUILD, NM_GUILD_GIVEPOSITION_RES, error );
				}
			}
			break;
		case NM_GUILD_NOTICE_REQ:
			{
				MSG_REQ_GUILD_NOTICE* pmsg = (MSG_REQ_GUILD_NOTICE*)msgRoot;
				pmsg->notice[MAX_GUILD_NOTICE_SIZE]=0;

				try
				{
					// 길드 가입중 아니면 
					unsigned long guildIndex = hero->GetGuildIndex();
					if( !(guildIndex > 0) )
						throw ERROR_GUILD_NOTICE_FAIL;

					// 공지 변경 및 보내기
					if( mpGuildManager->SendGuildNotice( guildIndex, pmsg->notice ) == true )
					{
						/// SYN : 길드 공지 변경
						if( PostGuildNotice( guildIndex, pmsg->notice ) == false )
						{
							PostGuildEvent( heroIdx, guildIndex, hero->GetGuildPosition(), "FAIL - PostGuildUpdateMark()" );
						}						
					}
					else
						throw ERROR_GUILD_NOTICE_FAIL;
				}
				catch (int error)
				{
					ResMsgError( perSocketContext, NM_GUILD, NM_GUILD_NOTICE_RES, error );
				}
			}
			break;
		case NM_GUILDMARK_UPDATE_REQ:
			{
				MSG_REQ_GUILDMARK_UPDATE* pmsg = (MSG_REQ_GUILDMARK_UPDATE*)msgRoot;

				try
				{
					if( clientInfo->guildDb.guildMarkUpdate )
						throw ERROR_GUILDMARK_UPDATE_FAIL;

					// 길드 가입중 아니면 
					unsigned long guildIndex = hero->GetGuildIndex();
					if( !(guildIndex > 0) )
						throw ERROR_GUILDMARK_UPDATE_FAIL;

					// 길드장 검사
					char position = hero->GetGuildPosition();
					if( position != eGUILD_MASTER )
						throw ERROR_GUILDMARK_UPDATE_POSITION;

					// 길드 마크 인덱스 생성
					unsigned long markIdx = GUILDMAN->CreateMarkIndex();
					if( markIdx == 0 )
						throw ERROR_GUILDMARK_UPDATE_FAIL;

					clientInfo->guildDb.guildMarkUpdate = GuildMarkUpdate( perSocketContext, guildIndex, markIdx, pmsg->mark );
				}
				catch (int error)
				{
					ResMsgError( perSocketContext, NM_GUILD, NM_GUILDMARK_UPDATE_RES, error );
				}
			}
			break;
		case NM_GUILDMARK_SEND_REQ:
			{
				MSG_REQ_GUILDMARK_SEND* pmsg = (MSG_REQ_GUILDMARK_SEND*)msgRoot;

				try
				{
					if( mpGuildManager->SendGuildMark( pmsg->guildIndex, hero ) == false )
						throw ERROR_GUILDMARK_SEND_FAIL; 
				}
				catch (int error)
				{
					ResMsgError( perSocketContext, NM_GUILD, NM_GUILDMARK_SEND_RES, error );
				}
			}
			break;
		}
		break; // NM_GUILD
	case NM_GATHERING:
		{
			/*-- ClientInfo::Complete 필터링 - Check / CharacterSelect / GameIn
			*/
			if ( (clientInfo->completeData & 0x0007) != 0x0007 )
				return false;

			/*-- MSGROOT::Protocol 필터링 - 알수없는 경우 소켓연결을 해제한다.
			*/
			if ( (unsigned char)msgRoot->Protocol >= NM_GUILD_MAX )
			{
				PostServerEvent( "END TASK - UNKNOWN NM_GUILD::PROTOCOL(=%d):MEMBER_IDX(=%d):CHARACTER_IDX(=%d):INETNUM(=%s).",
					msgRoot->Protocol,
					clientInfo->memberIdx,
					clientInfo->characterIdx,
					inet_ntoa( perSocketContext->addr.sin_addr) );
				return false;
			}

			switch ( msgRoot->Protocol )
			{
			case NM_GATHERING_START_REQ:
				{
					MSG_REQ_GATHERING_START* pMsg = (MSG_REQ_GATHERING_START*)msgRoot;
					cGathering* pGathering = OBJECTMANAGER->GetGathering( pMsg->mObjectIdx );
					if( pGathering != NULL )
						pGathering->AddGatherPlayer( hero );
					else
						ResMsgError( perSocketContext, NM_GATHERING, NM_GATHERING_START_RES, ERROR_GATHERING_FAIL );
				}
				break;
			case NM_GATHERING_CANCEL_REQ:
				{
					unsigned long gatheringIdx = hero->GetGatheringIdx();
					cGathering* pGathering = OBJECTMANAGER->GetGathering( gatheringIdx );
					if( pGathering != NULL )
						pGathering->ReqEraseGatherPlayer( hero );
				}
				break;
			}
		}
		break; // NM_GATHERING
	case NM_MAKESKILL:
		{
			/*-- ClientInfo::Complete 필터링 - Check / CharacterSelect / GameIn
			*/
			if ( (clientInfo->completeData & 0x0007) != 0x0007 )
				return false;

			/*-- MSGROOT::Protocol 필터링 - 알수없는 경우 소켓연결을 해제한다.
			*/
			if ( (unsigned char)msgRoot->Protocol >= NM_MAKESKILL_MAX )
			{
				PostServerEvent( "END TASK - UNKNOWN NM_GUILD::PROTOCOL(=%d):MEMBER_IDX(=%d):CHARACTER_IDX(=%d):INETNUM(=%s).",
					msgRoot->Protocol,
					clientInfo->memberIdx,
					clientInfo->characterIdx,
					inet_ntoa( perSocketContext->addr.sin_addr) );
				return false;
			}

			switch ( msgRoot->Protocol )
			{
			case NM_MAKESKILL_ADD_REQ:
				{
					MSG_REQ_MAKESKILL_ADD* pReq = (MSG_REQ_MAKESKILL_ADD*)msgRoot;

					/// DB 사용중 체크
					if( clientInfo->skillDb.makeSkillDbUse == true )
					{
						HANDLE                 handle  = NULL;
						MSG_RES_MAKESKILL_ADD* sendMsg = (MSG_RES_MAKESKILL_ADD*)GetMsgRoot( &handle, perSocketContext, NM_MAKESKILL, NM_MAKESKILL_ADD_RES );
							sendMsg->ErrorCode = ERROR_MAKESKILL_DBUSED;
						SendMsgRoot( handle, sizeof(MSG_RES_MAKESKILL_ADD) );
						break;
					}

					int error = hero->AddMakeSkill( pReq->mSlotNum );
					if( error != ERROR_MAKESKILL_SUCCESS )
					{
						HANDLE                 handle  = NULL;
						MSG_RES_MAKESKILL_ADD* sendMsg = (MSG_RES_MAKESKILL_ADD*)GetMsgRoot( &handle, perSocketContext, NM_MAKESKILL, NM_MAKESKILL_ADD_RES );
							sendMsg->ErrorCode = error;
						SendMsgRoot( handle, sizeof(MSG_RES_MAKESKILL_ADD) );
					}

					clientInfo->skillDb.makeSkillDbUse = true;
				}
				break;
			case NM_MAKESKILL_RECIPE_ADD_REQ:
				{
					MSG_REQ_MAKESKILL_RECIPE_ADD* pReq = (MSG_REQ_MAKESKILL_RECIPE_ADD*)msgRoot;

					/// DB 사용중 체크
					if( clientInfo->skillDb.makeSkillDbUse == true )
					{
						HANDLE                        handle  = NULL;
						MSG_RES_MAKESKILL_RECIPE_ADD* sendMsg = (MSG_RES_MAKESKILL_RECIPE_ADD*)GetMsgRoot( &handle, perSocketContext, NM_MAKESKILL, NM_MAKESKILL_RECIPE_ADD_RES );
							sendMsg->ErrorCode = ERROR_MAKESKILL_DBUSED;
						SendMsgRoot( handle, sizeof(MSG_RES_MAKESKILL_RECIPE_ADD) );

						clientInfo->skillDb.makeSkillDbUse = true;
						break;
					}

					int error = hero->AddRecipe( pReq->mSlotNum );
					if( error != ERROR_MAKESKILL_SUCCESS )
					{
						HANDLE                        handle  = NULL;
						MSG_RES_MAKESKILL_RECIPE_ADD* sendMsg = (MSG_RES_MAKESKILL_RECIPE_ADD*)GetMsgRoot( &handle, perSocketContext, NM_MAKESKILL, NM_MAKESKILL_RECIPE_ADD_RES );
							sendMsg->ErrorCode = error;
						SendMsgRoot( handle, sizeof(MSG_RES_MAKESKILL_RECIPE_ADD) );
					}
				}
				break;
			case NM_MAKESKILL_DEL_REQ:
				{
					MSG_REQ_MAKESKILL_DELETE* msg = (MSG_REQ_MAKESKILL_DELETE*)msgRoot;

					/// DB 사용중 체크
					if( clientInfo->skillDb.makeSkillDbUse )
					{
						ResMsgError( perSocketContext, NM_MAKESKILL, NM_MAKESKILL_DEL_RES, ERROR_MAKESKILL_DBUSED );                    
						break;
					}

					int error = hero->ReqDelMakeSkill( msg->mMakeSkill );
					if( error != ERROR_MAKESKILL_SUCCESS )
					{
						ResMsgError( perSocketContext, NM_MAKESKILL, NM_MAKESKILL_DEL_RES, ERROR_MAKESKILL_NOTHAVE );                    
						break;
					}

					clientInfo->skillDb.makeSkillDbUse = true;
				}
				break;
			case NM_MAKESKILL_ITEMMIX_START_REQ:
				{
					try
					{
						MSG_REQ_MAKESKILL_ITEMMIX_START* pReq = (MSG_REQ_MAKESKILL_ITEMMIX_START*)msgRoot;

						/// db 사용부 체크
						if ( clientInfo->inventoryDatabase )
							throw ERROR_MAKESKILL_START_DBERROR;

						/// 조합 시작 가능 체크
						throw hero->ItemMixReq( pReq->mMakeSkill, pReq->mRecipeIdx );
					}
					catch ( int error )
					{
						/// 가능 / 불가 메세지 발송
						ResMsgError( perSocketContext, NM_MAKESKILL, NM_MAKESKILL_ITEMMIX_START_RES, error );

						/// 가능시 모두에게 알림
						if( error == ERROR_MAKESKILL_START_SUCCESS )
						{
							/// 상태 바뀜을 모두에게 전송
							MSG_SYN_MAKESKILL_ITEMMIX_START synMsg;
							synMsg.Category = NM_MAKESKILL;
							synMsg.Protocol = NM_MAKESKILL_ITEMMIX_START_SYN;
							synMsg.mCharacterIdx = heroIdx;
							NETWORK2->QuickSendExcept( hero, (char*)&synMsg, sizeof(MSG_SYN_MAKESKILL_ITEMMIX_START) );

							clientInfo->inventoryDb.itemMix = true;
						}
					}
				}
				break;
			case NM_MAKESKILL_ITEMMIX_END_REQ:
				if ( clientInfo->inventoryDb.itemMix == true )
				{
					try
					{
						/// 실제 아이템을 조합함 - db처리요청
						throw hero->ItemMixEnd();
					}
					catch ( int error )
					{
						/// 조합 실패 인경우 종료 처리
						if( error != ERROR_MAKESKILL_END_SUCCESS )
						{
							/// 실패 메세지 발송
							ResMsgError( perSocketContext, NM_MAKESKILL, NM_MAKESKILL_ITEMMIX_END_RES, error );

							/// 실패했음을 전체에 알림
							MSG_SYN_MAKESKILL_ITEMMIX_END synMsg;
							synMsg.Category = NM_MAKESKILL;
							synMsg.Protocol = NM_MAKESKILL_ITEMMIX_END_SYN;
							synMsg.mCharacterIdx = heroIdx;
							synMsg.ErrorCode = error;
							NETWORK2->QuickSendExcept( hero, (char*)&synMsg, sizeof(MSG_SYN_MAKESKILL_ITEMMIX_END) );

							/// 상태 풀어줌
							if ( hero->GetState( ) == eOBJECT_STATE_STOP && hero->GetStateStop( ) == eSTOP_ITEMMIX )
							{
								hero->ChangeState( eOBJECT_STATE_IDLE );
								hero->SetStateStop( eSTOP_NONE );
							}
						
							/// db 처리부 종료
							clientInfo->inventoryDb.itemMix = false;
						}
					}
				}
				break;
			case NM_MAKESKILL_ITEMMIX_CANCEL_REQ:
				if ( clientInfo->inventoryDb.itemMix )
				{
					/// 취소 설정
					hero->ItemMixCancel();

					/// 플레이어 상태를 풀어줌
					if ( hero->GetState( ) == eOBJECT_STATE_STOP && hero->GetStateStop( ) == eSTOP_ITEMMIX )
					{
						hero->ChangeState( eOBJECT_STATE_IDLE );
						hero->SetStateStop( eSTOP_NONE );
					}

					/// hero외의 플레이어에게 취소 확인 메세지 발송
					MSG_SYN_MAKESKILL_ITEMMIX_CANCEL synMsg;
					synMsg.Category = NM_MAKESKILL;
					synMsg.Protocol = NM_MAKESKILL_ITEMMIX_CANCEL_SYN;
					synMsg.mCharacterIdx = heroIdx;
					NETWORK2->QuickSendExcept( hero, (char*)&synMsg, sizeof(MSG_SYN_MAKESKILL_ITEMMIX_CANCEL) );

					clientInfo->inventoryDb.itemMix = false;
				}
				break;
			}
		}
		break;

	default:
		if ( (clientInfo->completeData & 0x0001) != 0x0001 )
		{
			PostServerEvent( "END TASK - UNKNOWN CATEGORY(=%d)::PROTOCOL(=%d):INETNUM(=%s).",
							 msgRoot->Category,
							 msgRoot->Protocol,
							 inet_ntoa( perSocketContext->addr.sin_addr) );
		}
		else
		{
			PostServerEvent( "END TASK - UNKNOWN CATEGORY(=%d)::PROTOCOL(=%d):MEMBER_IDX(=%d):INETNUM(=%s).",
							 msgRoot->Category,
							 msgRoot->Protocol,
							 clientInfo->memberIdx,
							 inet_ntoa( perSocketContext->addr.sin_addr) );
		}
		return false;
	}
	return true;
}

// UpdateComplete Method
bool cGameProcess::UpdateComplete(PerSocketContext* perSocketContext, bool logout, bool shutdown)
{
	ClientInfo* clientInfo = (ClientInfo*)perSocketContext->buffer;

	// Common Database.
	if ( clientInfo->commonDatabase )
		return false;

	if ( clientInfo->complete.check )
	{
		if ( clientInfo->complete.characterSelect )
		{
			// 예외 DB 처리.
			if ( clientInfo->inventoryDb.itemMix )
			{
				clientInfo->inventoryDb.itemMix = false;
			}

			if ( clientInfo->inventoryDatabase || clientInfo->skillDatabase || clientInfo->questDatabase || clientInfo->guildDatabase )
				return false;

			unsigned long heroIdx = clientInfo->characterIdx;
			cPlayer*      hero    = clientInfo->hero;

			if ( hero == NULL )
			{
				PostServerEvent( "CriticalError UpdateComplete::CharacterIdx(=%d) - cPlayer Pointer is NULL.", clientInfo->characterIdx );
				return true; // Skip - Character Update.
			}
			else if ( clientInfo->complete.pvpJoin == true )
			{
				unsigned short mapNum    = hero->GetPvPJoinBeforeMapIdx();
				NiPoint2       beforePos = hero->GetPvPJoinBeforePos();
				NiPoint2       pos       = STAGESCRIPT->CalcNearTargetMapPos( mapNum, beforePos.x, beforePos.y );

				PVPMANAGER->MemberOut( hero->GetPvPDMIdx(), hero->GetObjectID(), (ePVPDM_TEAM_TYPE)hero->GetPvPDMTeam() );
				hero->SetMapNumber( mapNum );
				hero->SetPos( pos.x, pos.y );

				clientInfo->complete.pvpJoin = false;
			}
			else if( hero->GetDuelIdx() != 0 )
			{
				hero->DuelEnd( );	// 결투 종료 처리
			}
			else if( hero->GetPartyIndex() > 0 || hero->GetRequestRejection() == eREQREJCT_PARTY )
			{
				// 파티 검사 (신청자와 신청받는유저 각기처리)
				mpPartyManager->PartyOutPlayer( hero );
			}
			else if( hero->GetRequestRejection() == eREQREJCT_GUILD )
			{
				// 길드 검사 (신청받는유저만 처리)
				mpGuildManager->PlayerMapOut( hero );
			}
			else if ( clientInfo->complete.skillInfluenceInsert )
			{
				if ( SkillInfluenceInsert( perSocketContext, heroIdx ) )
					clientInfo->complete.skillInfluenceInsert = false;
			}
			else if ( hero->IsGameFinish( ) == false )
			{
				// 강제진행.
				switch ( hero->GetState( ) )
				{
				case eOBJECT_STATE_STOP:
					switch ( hero->GetStateStop( ) )
					{
					case eSTOP_READYTAROT:
					case eSTOP_OPENTAROT:
						hero->TarotReaderShutdown( shutdown );
						break;
					case eSTOP_ENTERTAROT:
					case eSTOP_USETAROT:
						hero->TarotSeekerShutdown( shutdown );
						break;
					case eSTOP_OPENSTALL:
						hero->StallSellOpenShutdown( shutdown );
						break;
					case eSTOP_USESTALL:
						hero->StallSellUseShutdown( shutdown );
						break;
					case eSTOP_EXCHANGE:
						hero->ExchangeShutdown( shutdown );
						break;
					case eSTOP_NPCSPEECH:
					case eSTOP_COLLECTIONBOOK:
					case eSTOP_CHEATSTOP:
						hero->ChangeState( eOBJECT_STATE_IDLE );
						hero->ChangeState( eOBJECT_STATE_STOP );
						hero->SetStateStop( eSTOP_GAMEFINISH );
						break;
					case eSTOP_GAMEFINISH:
						hero->StartRequestRejection( eREQREJCT_GAMEFINISH );
						break;
					case eSTOP_ITEMMIX:
						hero->ChangeState( eOBJECT_STATE_IDLE );
						hero->ChangeState( eOBJECT_STATE_STOP );
						hero->SetStateStop( eSTOP_GAMEFINISH );
						break;
					case eSTOP_ENHANCED:
						hero->ChangeState( eOBJECT_STATE_IDLE );
						hero->ChangeState( eOBJECT_STATE_STOP );
						hero->SetStateStop( eSTOP_GAMEFINISH );
						break;
					}
					break;
				case eOBJECT_STATE_DIE:
					{
						hero->Resurrection( eRESURRECTIONTYPE_SAFE );
					}
					break;
				case eOBJECT_STATE_ATTACK:
				case ePLAYER_STATE_GATHERING:
					{
						hero->StartRequestRejection( eREQREJCT_GAMEFINISH );
						hero->ChangeState( eOBJECT_STATE_IDLE );
						hero->ChangeState( eOBJECT_STATE_STOP );
						hero->SetStateStop( eSTOP_GAMEFINISH );
					}
					break;
				default:
					hero->StartRequestRejection( eREQREJCT_GAMEFINISH );
					hero->ChangeState( eOBJECT_STATE_STOP );
					hero->SetStateStop( eSTOP_GAMEFINISH );
					break;
				}
			}
			else if ( clientInfo->complete.gameIn )
			{
				mpGridManager->RemovePlayer( hero );
				clientInfo->complete.gameIn = false;
			}
			else if ( clientInfo->complete.characterUpdate )
			{
				sPlayerInfo*    playerInfo = hero->GetPlayerInfo( );
				sPlayerExrInfo* exrInfo    = hero->GetExrInfo( );

				if ( clientInfo->playerInfo != (ULONG_PTR)playerInfo || clientInfo->exrInfo != (ULONG_PTR)exrInfo )
				{
					PostServerEvent( "CriticalError UpdateComplete::CharacterIdx(=%d) - CHARACTER UPDATE: PLAYER INFO(=0x%08x:0x%08x), EXR INFO(=0x%08x:0x%08x)",
									 clientInfo->characterIdx,
									 clientInfo->playerInfo,
									 (ULONG_PTR)playerInfo,
									 clientInfo->exrInfo,
									 (ULONG_PTR)exrInfo );
					return true; // Skip - Character Update.
				}
				else
					hero->DBUpdate();
			}
			else if ( clientInfo->complete.characterEquip )
			{
				sPlayerInfo*       playerInfo = hero->GetPlayerInfo( );
				sPlayerWearInfo*   wearInfo   = hero->GetWearInfo( );
				sPlayerWeaponInfo* weaponInfo = hero->GetWeaponInfo( );

				if ( clientInfo->playerInfo != (ULONG_PTR)playerInfo || clientInfo->wearInfo != (ULONG_PTR)wearInfo || clientInfo->weaponInfo != (ULONG_PTR)weaponInfo )
				{
					PostServerEvent( "CriticalError UpdateComplete::CharacterIdx(=%d) - CHARACTER EQUIP: PLAYER INFO(=0x%08x:0x%08x), WEAR INFO(=0x%08x:0x%08x), WEAPON INFO(=0x%08x:0x%08x)",
									 clientInfo->characterIdx,
									 clientInfo->playerInfo,
									 (ULONG_PTR)playerInfo,
									 clientInfo->wearInfo,
									 (ULONG_PTR)wearInfo,
									 clientInfo->weaponInfo,
									 (ULONG_PTR)weaponInfo );
					return true; // Skip - Character Update.
				}
				else
				{
					HANDLE           handle         = NULL;
					CHARACTER_EQUIP* characterEquip = (CHARACTER_EQUIP*)GetSQL( &handle, SQL_GAME_PROCESS_CHARACTER_EQUIP );
						characterEquip->idx               = playerInfo->CharacterIdx;
						characterEquip->hat               = wearInfo->WearIdx[ eWEAR_HAT   ];
						characterEquip->body1             = wearInfo->WearIdx[ eWEAR_BODY1 ];
						characterEquip->body2             = wearInfo->WearIdx[ eWEAR_BODY2 ];
						characterEquip->hand              = wearInfo->WearIdx[ eWEAR_HAND  ];
						characterEquip->foot              = wearInfo->WearIdx[ eWEAR_FOOT  ];
						characterEquip->earring           = wearInfo->Earring;
						characterEquip->necklace          = wearInfo->Necklace;
						characterEquip->brooch            = wearInfo->Brooch;
						characterEquip->bracelet          = wearInfo->Bracelet;
						characterEquip->ring              = wearInfo->Ring;
						characterEquip->leftHand          = weaponInfo->WeaponIdx[ eHAND_LEFT ];
						characterEquip->leftHandEnhanced  = weaponInfo->WeaponEnhanced[ eHAND_LEFT ];
						characterEquip->rightHand         = weaponInfo->WeaponIdx[ eHAND_RIGHT ];
						characterEquip->rightHandEnhanced = weaponInfo->WeaponEnhanced[ eHAND_RIGHT ];
					SendSQL( perSocketContext, handle, sizeof(CHARACTER_EQUIP), COMMON_DB_CHARACTER_EQUIP );
				}
			}
			else if ( clientInfo->complete.characterActiveWeapon )
			{
				HANDLE                   handle                = NULL;
				CHARACTER_ACTIVE_WEAPON* characterActiveWeapon = (CHARACTER_ACTIVE_WEAPON*)GetSQL( &handle, SQL_GAME_PROCESS_CHARACTER_ACTIVE_WEPON );
					characterActiveWeapon->idx          = heroIdx;
					characterActiveWeapon->activeWeapon = (char)hero->GetActiveWeapon( );
				SendSQL( perSocketContext, handle, sizeof(CHARACTER_ACTIVE_WEAPON), COMMON_DB_CHARACTER_ACTIVE_WEAPON );
			}
			else if ( clientInfo->complete.stallSell )
			{
				HANDLE            handle         = NULL;
				STALL_SELL_CLEAR* stallSellClear = (STALL_SELL_CLEAR*)GetSQL( &handle, SQL_GAME_PROCESS_STALL_SELL_CLEAR );
					stallSellClear->characterIdx = heroIdx;
				SendSQL( perSocketContext, handle, sizeof(STALL_SELL_CLEAR), COMMON_DB_STALL_SELL_CLEAR );
			}
			else if ( clientInfo->complete.stallBuy )
			{
			}
			else if ( clientInfo->complete.skillCoolTime )
			{
				if ( SkillCoolTime( perSocketContext, heroIdx ) )
					clientInfo->complete.skillCoolTime = false;
			}
			else if ( clientInfo->complete.skillRelease )
			{
				SKILLMANAGER->ReleasePlayerHaveSkill( heroIdx );	// 게임서버 보유 스킬 제거
				clientInfo->complete.skillRelease = false;
			}
			else if ( clientInfo->complete.influenceRelease )
			{
				hero->ReleaseInfluenceSet();		// 게임서버 보유 효과 제거
				clientInfo->complete.influenceRelease = false;
			}
			else if ( clientInfo->complete.recipeCoolTime )
			{
				if ( RecipeCoolTime( perSocketContext, hero ) )
					clientInfo->complete.recipeCoolTime = false;
			}
			else if ( clientInfo->complete.shortcut )
			{
				HANDLE           handle         = NULL;
				SHORTCUT_UPDATE* shortcutUpdate = (SHORTCUT_UPDATE*)GetSQL( &handle, SQL_GAME_PROCESS_SHORTCUT_UPDATE );
					shortcutUpdate->characterIdx = heroIdx;
					memcpy( &shortcutUpdate->table, &clientInfo->shortcut, sizeof(TB_SHORTCUT) );
				SendSQL( perSocketContext, handle, sizeof(SHORTCUT_UPDATE), COMMON_DB_SHORTCUT_UPDATE );
			}
			else if ( clientInfo->complete.quest )
			{
				/// 유저가 게임종료전, 소환했던 몬스터 삭제
				mpQuestManager->DeleteAllQuestMonster( heroIdx );

				/// 유저가 게임을 종료하기전에 전체 저장 
				hero->SaveQuestProgress( (ULONG_PTR)perSocketContext );
				clientInfo->complete.quest = false;
			}
			else if ( clientInfo->complete.noteSelect )
			{
				/// 유저가 종료할때 저장 맵에서 삭제
				mpNoteManager->DeletePlayerNoteMap( hero->GetObjectID() );
				clientInfo->complete.noteSelect = false;
			}
			else if ( clientInfo->complete.fortuneData )
			{
				/// 운세 시간 정보 저장
				hero->SaveFortuneData( (ULONG_PTR)perSocketContext );
				clientInfo->complete.fortuneData = false;
			}
			else if ( clientInfo->complete.inventoryCooltime )
			{
				CooltimeRoot* cooltimeRoot = hero->GetCooltimeRoot( );
				if ( cooltimeRoot != NULL )
				{
					time_t ltime;
					long   cooltime;
					time( &ltime );

					while ( cooltimeRoot->pool )
					{
						PerCooltime* perCooltime = (PerCooltime*)cooltimeRoot->pool;
						cooltime = (long)difftime( perCooltime->validThru, ltime );

						if ( cooltime > MIN_SAVE_ITEM_COOLTIME )
						{
							HANDLE                     handle                  = NULL;
							INVENTORY_COOLTIME_INSERT* inventoryCooltimeInsert = (INVENTORY_COOLTIME_INSERT*)GetSQL( &handle, SQL_GAME_PROCESS_INVENTORY_COOLTIME_INSERT );
							TB_INVENTORY_COOLTIME*     table                   = inventoryCooltimeInsert->table;

							inventoryCooltimeInsert->characterIdx = heroIdx;
							table->itemDefineIndex = perCooltime->index;
							table->cooltime1       = perCooltime->cooltime1;
							table->cooltime2       = perCooltime->cooltime2;
							table->cooltime        = cooltime*1000;

							SendSQL( perSocketContext, handle, sizeof(INVENTORY_COOLTIME_INSERT) );
						}

						COOLTIMEPOOL->ReleaseCooltime( cooltimeRoot, perCooltime );
					}
				}
				clientInfo->complete.inventoryCooltime = false;
			}
			else if ( clientInfo->complete.gameInOutComplete )
			{
				// Gameout Complete - TB_LOGIN Table 동기화.
				HANDLE            handle          = NULL;
				GAMEOUT_COMPLETE* gameoutComplete = (GAMEOUT_COMPLETE*)GetSQL( &handle, SQL_GAME_PROCESS_GAMEOUT_COMPLETE );

				gameoutComplete->loginIdx  = clientInfo->loginIdx;

				SendSQL( perSocketContext, handle, sizeof(GAMEOUT_COMPLETE) );
			}
			else
			{
				mpObjectManager->RemovePlayer( heroIdx );
				clientInfo->hero = NULL;
				clientInfo->complete.characterSelect = false;	// unselect.
			}
		}
		else if ( logout == true )
		{
			HANDLE         handle       = NULL;
			MEMBER_LOGOUT* memberLogout = (MEMBER_LOGOUT*)GetSQL( &handle, SQL_GAME_PROCESS_MEMBER_LOGOUT );

			memberLogout->loginIdx  = clientInfo->loginIdx;
			memberLogout->memberIdx = clientInfo->memberIdx;
			memberLogout->retvalue  = 0;

			SendSQL( perSocketContext, handle, sizeof(MEMBER_LOGOUT), COMMON_DB_LOGOUT );
		}
		else
			return true;	// 업데이트 완  료.

		return false;		// 업데이트 진행중.
	}

	return true;			// 업데이트 무  시.
}

// Decrypt Method
void cGameProcess::Decrypt(BYTE key, long len, char* ptr)
{
	len -= PHLen;
	ptr += PHLen;

	if ( len > 0 )
	{
		for ( long i = 0; i < len; i++, ptr++ )
		{
			(*ptr) = (*ptr) ^ SY_KEY_TABLE[key][i%3];
		}
	}
}

// Encrypt Method
void cGameProcess::Encrypt(BYTE key, long len, char* ptr)
{
	len -= PHLen;
	ptr += PHLen;

	if ( len > 0 )
	{
		for ( LONG i = 0; i < len; i++, ptr++ )
		{
			(*ptr) = (*ptr) ^ SY_KEY_TABLE[key][i%3];
		}
	}
}

// SendExec Method
bool cGameProcess::SendExec(PerIoContext* perIoContext)
{
	WSABUF wsaBuf;
	DWORD  sendBytes = 0;
	DWORD  flags     = 0;
	int    retcode;

	wsaBuf.len = perIoContext->offset;
	wsaBuf.buf = perIoContext->buffer;

	BYTE  key = (BYTE)(rand( ) & 0x0f);
	(*wsaBuf.buf) = ((*wsaBuf.buf) & 0xf0) | key;

	if( *(perIoContext->buffer + PHLen) != NM_SERVERTOOL )
		Encrypt( key, wsaBuf.len, wsaBuf.buf );

	retcode = WSASend( perIoContext->socket,
					   &wsaBuf,
					   1,
					   &sendBytes,
					   flags,
					   &(perIoContext->wsaOverlapped),
					   NULL );

	if ( (retcode == SOCKET_ERROR) && (WSAGetLastError() != WSA_IO_PENDING) )
	{
		DWORD error = 0;
		mIoContextPool->ReleaseIoContext( perIoContext /*false, &error*/ );
		if ( error != 0 )
			PostServerEvent( "WARNING - cGameProcess::SendExec:Error(=0x%08xh)", error );
		return false;
	}
	return true;
}

// AcceptComplete Method
bool cGameProcess::AcceptComplete(PerSocketContext* perSocketContext)
{
	cCSLock lock( &mCs );

	Verbose->AcceptComplete( &mCs, perSocketContext );

	// 클라이언트 메모리(버퍼) 사용량 설정.
	perSocketContext->offset = (long)sizeof(ClientInfo);

	// 게임서버 패킷정보.
	PerIoContext* perIoContext = mIoContextPool->GetIoContext( perSocketContext->socket, IOCP_REQUEST_WRITE );
	Packet*       packet       = (Packet*)perIoContext->buffer;

	packet->ver  = PHVer;
	packet->hlen = PHLen;
	packet->tos  = TOS_GAME_ACCEPT;
	packet->tlen = packet->hlen;

	perIoContext->offset = packet->tlen;
	return SendPost( perIoContext );
}

// SendComplete Method
bool cGameProcess::SendComplete(PerSocketContext* /*perSocketContext*/, PerIoContext* perIoContext, DWORD bytesTransferred)
{
	Packet* packet = (Packet*)perIoContext->buffer;

	if ( packet->tlen != bytesTransferred )
		PostServerEvent( "WARNING - cGameProcess::SendComplete - Packet::tlen(=%d), BytesTransferred(=%d)", packet->tlen, bytesTransferred );

	DWORD error = 0;
	mIoContextPool->ReleaseIoContext( perIoContext /*false, &error*/ );
	if ( error != 0 )
		PostServerEvent( "WARNING - cGameProcess::SendComplete:Error(=0x%08xh)", error );
	return true;
}

// RecvComplete Method
bool cGameProcess::RecvComplete(PerSocketContext* perSocketContext, PerIoContext* perIoContext, DWORD bytesTransferred)
{
	cCSLock lock( &mCs );

	char*   recvBuf    = (perSocketContext->buffer + (perSocketContext->offset + perSocketContext->InternalHigh));
	DWORD   recvBufLen = (perSocketContext->length - (perSocketContext->offset + perSocketContext->InternalHigh));
	u_long& recvBufOff = perSocketContext->InternalHigh;

	// 오프셋의 크기를 누적한다.
	perIoContext->offset += bytesTransferred;

	DWORD length = perIoContext->offset; // 수신된 누적버퍼의 크기
	DWORD offset = 0;                    // 수신된 누적버퍼의 오프셋

	while ( length >= PHLen )
	{
		Packet* packet    = (Packet*)(perIoContext->buffer + offset);
		DWORD   packetLen = packet->tlen;

		// 패킷의 헤더정보를 확인하여 클라이언트가 아닐경우 오류처리 한다.
		if ( !(packet->tos < TOS_MAX) || packet->hlen != PHLen )
		{
			Close( perSocketContext, perIoContext );
			return false;
		}

		// 데이터의 길이가 최소 단위보다 작을 경우 오류 처리해야 한다.
		if ( packetLen < PHLen )
		{
			Close( perSocketContext, perIoContext );
			return false;
		}

		// 데이터의 길이가 완전할때까지 이어 받기를 해야 한다.
		if ( packetLen > length )
			break;

		// Type of service를 검사한다.
		switch ( packet->tos )
		{
		case TOS_CLIENT:
			Decrypt( packet->ver, packet->tlen, (char*)packet );
		case TOS_SERVERTOOL:
			// Receive Data 복사.
			if ( packetLen <= recvBufLen )
			{
				memcpy( recvBuf, packet, packetLen );

				recvBuf    += packetLen;
				recvBufLen -= packetLen;
				recvBufOff += packetLen;
			}
			else
			{
				// Buffer Overflow (버퍼 오버플로).
				Close( perSocketContext, perIoContext );
				return false;
			}
			break;
		case TOS_TTL:
			perSocketContext->timeToLive = (GetTickCount( ) + MAX_TTL);     // 최대 03분
//			perSocketContext->timeToLive = (GetTickCount( ) + 0x1B7740);    // 최대 30분
			break;
		default:
			// Error.
			Close( perSocketContext, perIoContext );
			return false;
		}

		// 처리후 Offset 및 Length를 다시 계산한다.
		length -= packetLen;
		offset += packetLen;
	}

	// 이어 받기를 위해 데이터를 보관한한다.
	if ( offset > 0 )
	{
		PerIoContext* recvIoContext = mIoContextPool->GetIoContext( perSocketContext->socket, IOCP_REQUEST_READ );
		DWORD         error         = 0;

		if ( length > 0 )
		{
			memcpy( recvIoContext->buffer, (perIoContext->buffer + offset), length );
			recvIoContext->offset = length;
		}

		mIoContextPool->ReleaseIoContext( perIoContext /*false, &error*/ );
		if ( error != 0 )
			PostServerEvent( "WARNING - cGameProcess::RecvComplete:Error(=0x%08xh)", error );

		perIoContext = recvIoContext;
	}

	// 수신을 위해 I/O Context를 준비한다.
	return RecvPost( perIoContext );
}

// CallbackComplete Method - cCSLock lock( &mCs )는 개별 사용한다.
bool cGameProcess::CallbackComplete(PerSocketContext* perSocketContext, PerIoContext* perIoContext, DWORD /*bytesTransferred*/)
{
	try {
		switch ( perIoContext->iParam )
		{
		case SQL_GAME_PROCESS_VERIFY_ITEM_DEFINE:
			if ( mRequestImport.itemDefine )
			{
				VERIFY_ITEM_TABLE* verifyItemTable = (VERIFY_ITEM_TABLE*)perIoContext->buffer;
				PostServerEvent( "TB_ITEM_DEFINE 검사결과 입니다. Count(=%d), Return Value(=%d).", verifyItemTable->count, verifyItemTable->retvalue );

				// 아이템 정의 테이블 생성 및 가져오기...
				if ( verifyItemTable->retvalue == 0 )
				{
					mpItemManager->AllocItemDefine( verifyItemTable->count );

					for ( long idx = 1; idx <= verifyItemTable->count; idx++ )
					{
						if ( ItemDefine( idx ) == false )
							PostServerEvent( "SQL_GAME_PROCESS_VERIFY_ITEM_DEFINE에서 SQL 호출을 실패 했습니다." );
					}
				}

				mRequestImport.itemDefine = false;
			}
			break;
		case SQL_GAME_PROCESS_ITEM_DEFINE:
			{
				cCSLock lock( &mCs );
				ITEM_DEFINE_TABLE* itemDefineTable = (ITEM_DEFINE_TABLE*)perIoContext->buffer;

				// 아이템 정의 테이블 설정.
				mpItemManager->SetItemDefine( itemDefineTable->table );

				mCompleteImport.itemDefine = mpItemManager->CheckItemDefine( );
			}
			break;

		case SQL_GAME_PROCESS_VERIFY_ITEM_ABILITY:
			if ( mRequestImport.itemAbility )
			{
				VERIFY_ITEM_TABLE* verifyItemTable = (VERIFY_ITEM_TABLE*)perIoContext->buffer;
				PostServerEvent( "TB_ITEM_ABILITY 검사결과 입니다. Count(=%d), Return Value(=%d).", verifyItemTable->count, verifyItemTable->retvalue );

				// 아이템 속성 테이블 생성 및 가져오기...
				if ( verifyItemTable->retvalue == 0 )
				{
					mpItemManager->AllocItemAbility( verifyItemTable->count );

					for ( long idx = 1; idx <= verifyItemTable->count; idx++ )
					{
						if ( ItemAbility( idx ) == false )
							PostServerEvent( "SQL_GAME_PROCESS_VERIFY_ITEM_ABILITY에서 SQL 호출을 실패 했습니다." );
					}
				}

				mRequestImport.itemAbility = false;
			}
			break;
		case SQL_GAME_PROCESS_ITEM_ABILITY:
			{
				cCSLock lock( &mCs );
				ITEM_ABILITY_TABLE* itemAbilityTable = (ITEM_ABILITY_TABLE*)perIoContext->buffer;

				// 아이템 속성 테이블 설정.
				mpItemManager->SetItemAbility( itemAbilityTable->table );

				mCompleteImport.itemAbility = mpItemManager->CheckItemAbility( );
			}
			break;

		case SQL_GAME_PROCESS_VERIFY_ITEM_COOLTIME2:
			if ( mRequestImport.itemCoolitme2 )
			{
				VERIFY_ITEM_TABLE* verifyItemTable = (VERIFY_ITEM_TABLE*)perIoContext->buffer;
				PostServerEvent( "TB_ITEM_COOLTIME2 검사결과 입니다. Count(=%d), Return Value(=%d).", verifyItemTable->count, verifyItemTable->retvalue );

				// 아이템 쿨타임 테이블 생성 및 가져오기...
				if ( verifyItemTable->retvalue == 0 )
				{
					mpItemManager->AllocItemCooltime2( verifyItemTable->count );

					for ( long idx = 1; idx <= verifyItemTable->count; idx++ )
					{
						if ( ItemCooltime2( idx ) == false )
							PostServerEvent( "SQL_GAME_PROCESS_VERIFY_ITEM_COOLTIME2에서 SQL 호출을 실패 했습니다." );
					}
				}

				mRequestImport.itemCoolitme2 = false;
			}
			break;
		case SQL_GAME_PROCESS_ITEM_COOLTIME2:
			{
				cCSLock lock( &mCs );
				ITEM_COOLTIME2_TABLE* itemCooltime2Table = (ITEM_COOLTIME2_TABLE*)perIoContext->buffer;

				// 아이템 쿨타임 테이블 설정.
				mpItemManager->SetItemCooltime2( itemCooltime2Table->idx, itemCooltime2Table->rowCount, itemCooltime2Table->table );

				mCompleteImport.itemCooltime2 = mpItemManager->CheckItemCooltime2( );
			}
			break;

		case SQL_GAME_PROCESS_VERIFY_ITEM_LIMIT:
			if ( mRequestImport.itemLimit )
			{
				VERIFY_ITEM_TABLE* verifyItemTable = (VERIFY_ITEM_TABLE*)perIoContext->buffer;
				PostServerEvent( "TB_ITEM_LIMIT 검사결과 입니다. Count(=%d), Return Value(=%d).", verifyItemTable->count, verifyItemTable->retvalue );

				// 아이템 제한 테이블 생성 및 가져오기...
				if ( verifyItemTable->retvalue == 0 )
				{
					mpItemManager->AllocItemLimit( verifyItemTable->count );

					for ( long idx = 1; idx <= verifyItemTable->count; idx++ )
					{
						if ( ItemLimit( idx ) == false )
							PostServerEvent( "SQL_GAME_PROCESS_VERIFY_ITEM_LIMIT에서 SQL 호출을 실패 했습니다." );
					}
				}

				mRequestImport.itemLimit = false;
			}
			break;
		case SQL_GAME_PROCESS_ITEM_LIMIT:
			{
				cCSLock lock( &mCs );
				ITEM_LIMIT_TABLE* itemLimitTable = (ITEM_LIMIT_TABLE*)perIoContext->buffer;

				// 아이템 속성 테이블 설정.
				mpItemManager->SetItemLimit( itemLimitTable->table );

				mCompleteImport.itemLimit = mpItemManager->CheckItemLimit( );
			}
			break;

		case SQL_GAME_PROCESS_VERIFY_ITEM_CARD:
			if ( mRequestImport.itemCard )
			{
				VERIFY_ITEM_TABLE* verifyItemTable = (VERIFY_ITEM_TABLE*)perIoContext->buffer;
				PostServerEvent( "TB_ITEM_CARD 검사결과 입니다. Count(=%d), Return Value(=%d).", verifyItemTable->count, verifyItemTable->retvalue );

				// 아이템 카드 테이블 생성 및 가져오기...
				if ( verifyItemTable->retvalue == 0 )
				{
					mpItemManager->AllocItemCard( verifyItemTable->count );

					for ( long idx = 1; idx <= verifyItemTable->count; idx++ )
					{
						if ( ItemCard( idx ) == false )
							PostServerEvent( "SQL_GAME_PROCESS_VERIFY_ITEM_CARD에서 SQL 호출을 실패 했습니다." );
					}
				}

				mRequestImport.itemCard = false;
			}
			break;
		case SQL_GAME_PROCESS_ITEM_CARD:
			{
				cCSLock lock( &mCs );
				ITEM_CARD_TABLE* itemCardTable = (ITEM_CARD_TABLE*)perIoContext->buffer;

				// 아이템 카드 테이블 설정.
				mpItemManager->SetItemCard( itemCardTable->table );

				mCompleteImport.itemCard = mpItemManager->CheckItemCard( );
			}
			break;

		case SQL_GAME_PROCESS_VERIFY_ITEM_CARD_SLOT:
			if ( mRequestImport.itemCardSlot )
			{
				VERIFY_ITEM_TABLE* verifyItemTable = (VERIFY_ITEM_TABLE*)perIoContext->buffer;
				PostServerEvent( "TB_ITEM_CARD_SLOT 검사결과 입니다. Count(=%d), Return Value(=%d).", verifyItemTable->count, verifyItemTable->retvalue );

				// 아이템 카드슬롯 테이블 생성 및 가져오기...
				if ( verifyItemTable->retvalue == 0 )
				{
					mpItemManager->AllocItemCardSlot( verifyItemTable->count );

					for ( long idx = 1; idx <= verifyItemTable->count; idx++ )
					{
						if ( ItemCardSlot( idx ) == false )
							PostServerEvent( "SQL_GAME_PROCESS_VERIFY_ITEM_CARD_SLOT에서 SQL 호출을 실패 했습니다." );
					}
				}

				mRequestImport.itemCardSlot = false;
			}
			break;
		case SQL_GAME_PROCESS_ITEM_CARD_SLOT:
			{
				cCSLock lock( &mCs );
				ITEM_CARD_SLOT_TABLE* itemCardSlotTable = (ITEM_CARD_SLOT_TABLE*)perIoContext->buffer;

				// 아이템 카드슬롯 테이블 설정.
				mpItemManager->SetItemCardSlot( itemCardSlotTable->table );

				mCompleteImport.itemCardSlot = mpItemManager->CheckItemCardSlot( );
			}
			break;

		case SQL_GAME_PROCESS_VERIFY_ITEM_TAROT:
			if ( mRequestImport.itemTarot )
			{
				VERIFY_ITEM_TABLE* verifyItemTable = (VERIFY_ITEM_TABLE*)perIoContext->buffer;
				PostServerEvent( "TB_ITEM_TAROT 검사결과 입니다. Count(=%d), Return Value(=%d).", verifyItemTable->count, verifyItemTable->retvalue );

				// 아이템 타로 테이블 생성 및 가져오기...
				if ( verifyItemTable->retvalue == 0 )
				{
					mpItemManager->AllocItemTarot( verifyItemTable->count );

					for ( long idx = 1; idx <= verifyItemTable->count; idx++ )
					{
						if ( ItemTarot( idx ) == false )
							PostServerEvent( "SQL_GAME_PROCESS_VERIFY_ITEM_TAROT에서 SQL 호출을 실패 했습니다." );
					}
				}

				mRequestImport.itemTarot = false;
			}
			break;
		case SQL_GAME_PROCESS_ITEM_TAROT:
			{
				cCSLock lock( &mCs );
				ITEM_TAROT_TABLE* itemTarotTable = (ITEM_TAROT_TABLE*)perIoContext->buffer;

				// 아이템 타로 테이블 설정.
				mpItemManager->SetItemTarot( itemTarotTable->table );

				mCompleteImport.itemTarot = mpItemManager->CheckItemTarot( );;
			}
			break;
		case SQL_GAME_PROCESS_VERIFY_ITEM_ENHANCED:
			if ( mRequestImport.itemEnhanced )
			{
				VERIFY_ITEM_TABLE* verifyItemTable = (VERIFY_ITEM_TABLE*)perIoContext->buffer;
				PostServerEvent( "TB_ITEM_ENHANCED 검사결과 입니다. Count(=%d), Return Value(=%d).", verifyItemTable->count, verifyItemTable->retvalue );

				// 아이템 강화 테이블 생성 및 가져오기...
				if ( verifyItemTable->retvalue == 0 )
				{
					mpItemManager->AllocItemEnhanced( verifyItemTable->count );

					for ( long idx = 1; idx <= verifyItemTable->count; idx++ )
					{
						if ( ItemEnhanced( idx ) == false )
							PostServerEvent( "SQL_GAME_PROCESS_VERIFY_ITEM_ENHANCED에서 SQL 호출을 실패 했습니다." );
					}
				}

				mRequestImport.itemEnhanced = false;
			}
			break;
		case SQL_GAME_PROCESS_ITEM_ENHANCED:
			{
				cCSLock lock( &mCs );
				ITEM_ENHANCED_TABLE* itemEnhancedTable = (ITEM_ENHANCED_TABLE*)perIoContext->buffer;

				// 아이템 강화 테이블 설정.
				mpItemManager->SetItemEnhanced( itemEnhancedTable->table );

				mCompleteImport.itemEnhanced = mpItemManager->CheckItemEnhanced( );
			}
			break;
		case SQL_GAME_PROCESS_VERIFY_ITEM_ENHANCEDRATE:
			if ( mRequestImport.itemEnhancedRate )
			{
				VERIFY_ITEM_TABLE* verifyItemTable = (VERIFY_ITEM_TABLE*)perIoContext->buffer;
				PostServerEvent( "TB_ITEM_ENHANCED_RATE 검사결과 입니다. Count(=%d), Return Value(=%d).", verifyItemTable->count, verifyItemTable->retvalue );

				// 아이템 강화확률 테이블 생성 및 가져오기...
				if ( verifyItemTable->retvalue == 0 )
				{
					mpItemManager->AllocItemEnhancedRate( verifyItemTable->count );

					for ( long idx = 1; idx <= verifyItemTable->count; idx++ )
					{
						if ( ItemEnhancedRate( idx ) == false )
							PostServerEvent( "SQL_GAME_PROCESS_VERIFY_ITEM_ENHANCEDRATE에서 SQL 호출을 실패 했습니다." );
					}
				}

				mRequestImport.itemEnhancedRate = false;
			}
			break;
		case SQL_GAME_PROCESS_ITEM_ENHANCEDRATE:
			{
				cCSLock lock( &mCs );
				ITEM_ENHANCED_RATE_TABLE* itemEnhancedRateTable = (ITEM_ENHANCED_RATE_TABLE*)perIoContext->buffer;

				// 아이템 강화확률 테이블 설정.
				mpItemManager->SetItemEnhancedRate( itemEnhancedRateTable->table );

				mCompleteImport.itemEnhancedRate = mpItemManager->CheckItemEnhancedRate( );
			}
			break;
		case SQL_GAME_PROCESS_VERIFY_ITEM_DISJOINT:
			if ( mRequestImport.itemDisjoint )
			{
				VERIFY_ITEM_TABLE* verifyItemTable = (VERIFY_ITEM_TABLE*)perIoContext->buffer;
				PostServerEvent( "TB_ITEM_DISJOINT 검사결과 입니다. Count(=%d), Return Value(=%d).", verifyItemTable->count, verifyItemTable->retvalue );

				// 아이템 강화확률 테이블 생성 및 가져오기...
				if ( verifyItemTable->retvalue == 0 )
				{
					mpItemManager->AllocItemDisjoint( verifyItemTable->count );

					for ( long idx = 1; idx <= verifyItemTable->count; idx++ )
					{
						if ( ItemDisjoint( idx ) == false )
							PostServerEvent( "SQL_GAME_PROCESS_VERIFY_ITEM_DISJOINT에서 SQL 호출을 실패 했습니다." );
					}
				}

				mRequestImport.itemDisjoint = false;
			}
			break;
		case SQL_GAME_PROCESS_ITEM_DISJOINT:
			{
				cCSLock lock( &mCs );
				ITEM_DISJOINT_TABLE* itemDisjointTable = (ITEM_DISJOINT_TABLE*)perIoContext->buffer;

				// 아이템 분해 테이블 설정.
				mpItemManager->SetItemDisjoint( itemDisjointTable->table );

				mCompleteImport.itemDisjoint = mpItemManager->CheckItemDisjoint( );
			}
			break;
		case SQL_GAME_PROCESS_VERIFY_ITEM_CHANGE:
			if ( mRequestImport.itemChange )
			{
				VERIFY_ITEM_TABLE* verifyItemTable = (VERIFY_ITEM_TABLE*)perIoContext->buffer;
				PostServerEvent( "TB_ITEM_CHANGE 검사결과 입니다. Count(=%d), Return Value(=%d).", verifyItemTable->count, verifyItemTable->retvalue );

				// 아이템 교환 테이블 생성 및 가져오기...
				if ( verifyItemTable->retvalue == 0 )
				{
					mpItemManager->AllocItemChange( verifyItemTable->count );

					for ( long idx = 1; idx <= verifyItemTable->count; idx++ )
					{
						if ( ItemChange( idx ) == false )
							PostServerEvent( "SQL_GAME_PROCESS_VERIFY_ITEM_CHANGE에서 SQL 호출을 실패 했습니다." );
					}
				}

				mRequestImport.itemChange = false;
			}
			break;
		case SQL_GAME_PROCESS_ITEM_CHANGE:
			{
				cCSLock lock( &mCs );
				ITEM_CHANGE_TABLE* itemChangeTable = (ITEM_CHANGE_TABLE*)perIoContext->buffer;

				// 아이템 교환 테이블 설정.
				mpItemManager->SetItemChange( itemChangeTable->table );

				mCompleteImport.itemChange = mpItemManager->CheckItemChange( );
			}
			break;
		case SQL_GAME_PROCESS_VERIFY_DEFAULT_ITEMS:
			if ( mRequest.verifyDefaultItems )
			{
				cCSLock               lock( &mCs );
				VERIFY_DEFAULT_ITEMS* verifyDefaultItems = (VERIFY_DEFAULT_ITEMS*)perIoContext->buffer;
				PostServerEvent( "Default Items Table 검사결과 입니다. Count(=%d), Return Value(=%d).", verifyDefaultItems->rowCount, verifyDefaultItems->retvalue );

				mComplete.verifyDefaultItems = (verifyDefaultItems->retvalue == 0) ? true : false;
				mRequest.verifyDefaultItems  = false;
			}
			break;
		case SQL_GAME_PROCESS_VERIFY_GUILD:
			if( mRequestImport.guild )
			{
				VERIFY_GUILD* verifyGuild = (VERIFY_GUILD*)perIoContext->buffer;
				PostServerEvent( "TB_GUILD 검사결과 입니다. Count(=%d), Return Value(=%d).", verifyGuild->total, verifyGuild->retvalue );

				//// 길드 분할 select
				if ( verifyGuild->retvalue == 0 )
				{	
					if ( verifyGuild->total > 0 )
					{
						mpGuildManager->SetGuildTotalLength( verifyGuild->total );
						mpGuildManager->SetGuildLength( verifyGuild->count );

						for ( long idx = 1; idx <= verifyGuild->total; ++idx )
						{
							if ( GuildSelect( idx ) == false )
								PostServerEvent( "SQL_GAME_PROCESS_VERIFY_GUILD에서 SQL 호출을 실패 했습니다." );
						}
					}
					else
					{
						mCompleteImport.guild = true;
					}
				}

				//mCompleteImport.guild = true;
				mRequestImport.guild = false;
			}
			break;
		case SQL_GAME_PROCESS_GUILD_SELECT:
			{
				cCSLock	lock( &mCs );
				GUILD_SELECT* guildSelect = (GUILD_SELECT*)perIoContext->buffer;

				TB_GUILD* tbGuild = guildSelect->table;
				if( tbGuild->apply == 0 )
				{
					if ( mpGuildManager->VerifyGuildSelect( tbGuild ) == true )
					{
						// 길드 유저 분할 로딩
						if ( GuildUserSelect( tbGuild->idx ) == false )
							PostServerEvent( "SQL_GAME_PROCESS_VERIFY_GUILD에서 SQL 호출을 실패 했습니다." );
					}
					else
					{
						PostServerEvent( "SQL_GAME_PROCESS_GUILD_SELECT에서 길드정보 생성에 실패 했습니다" );
					}
				}

				// 길드 테이블 로딩 완료
				if( mpGuildManager->CheckGuildTotal() == true )
					mCompleteImport.guild = mpGuildManager->CheckGuildLength();
			}
			break;
		case SQL_GAME_PROCESS_GUILDUSER_SELECT:
			{
				cCSLock lock( &mCs );
				GUILDUSER_SELECT* guildUserSelect = (GUILDUSER_SELECT*)perIoContext->buffer;

				/// 길드 유저 리스트 세팅
				for( long i = 0; i < guildUserSelect->rowCount; ++i )
				{
					if ( mpGuildManager->VerifyGuildUser( guildUserSelect->idx, &guildUserSelect->table[i] ) == false )
					{
						PostServerEvent( "SQL_GAME_PROCESS_GUILDUSER_SELECT에서 길드 유저 생성에 실패 했습니다" );
					}
				}

				if( guildUserSelect->rowCount == 0 )
				{
					assert(0);
				}
			}
			break;
		case SQL_GAME_PROCESS_CHANNEL_CHECK:
			if ( mRequest.channelCheck )
			{
				cCSLock        lock( &mCs );
				CHANNEL_CHECK* channelCheck = (CHANNEL_CHECK*)perIoContext->buffer;

				wcscpy( mServerName, channelCheck->serverName );
				mChannelNum = channelCheck->channelNum;

				if ( g_verbose == true )
				{
					printf( "ServerNum (=%d), ChannelNum (=%d)에서 시작 합니다.\n", mServerNum, mChannelNum );
				}

				PostServerEvent( "ServerNum (=%d), ChannelNum (=%d)에서 시작 합니다.",
								 mServerNum,
								 mChannelNum );

				mRequest.channelCheck  = false;
				mComplete.channelCheck = true;
			}
			break;
		case SQL_REQUEST_GAME_INIT:
			if ( mRequest.initGameDB )
			{
				cCSLock    lock( &mCs );
				INIT_GAME* initGame = (INIT_GAME*)perIoContext->buffer;

				PostServerEvent( "ServerNum (=%d)::ChannelNum (=%d)의 Game Database 초기화 결과 입니다. Remove Cooltime (=%d), Remove Stall Sell (=%d).",
								 mServerNum,
								 mChannelNum,
								 initGame->removeCoolTimeCount,
								 initGame->removeSTallSellCount );

				mStatus = _E_STATUS_RUNABLE_;

				mComplete.initGameDB = true;
				mRequest.initGameDB = false;
			}
			break;
		case SQL_REQUEST_GAME_SHUTDOWN:
			if ( mRequest.shutdownGameDB )
			{
				cCSLock        lock( &mCs );
				SHUTDOWN_GAME* shutdownGame = (SHUTDOWN_GAME*)perIoContext->buffer;

				PostServerEvent( "ServerNum (=%d)::ChannelNum (=%d)의 Game Database 종료 결과 입니다. Remove Cooltime (=%d), Remove Stall Sell (=%d).",
								 mServerNum,
								 mChannelNum,
								 shutdownGame->removeCoolTimeCount,
								 shutdownGame->removeSTallSellCount );

				mComplete.shutdownGameDB = true;
				mRequest.shutdownGameDB = false;
			}
			break;
		case SQL_REQUEST_ACCOUNT_SHUTDOWN:
			{
				mComplete.shutdownAccountDB = true;
				mRequest.shutdownAccountDB = false;
			}
			break;
		case SQL_GAME_PROCESS_MEMBER_CHECK:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock     lock( &mCs );
				ClientInfo* clientInfo = (ClientInfo*)perSocketContext->buffer;

				if ( clientInfo->commonDb.check )
				{
					MEMBER_CHECK* memberCheck = (MEMBER_CHECK*)perIoContext->buffer;

					// 1. 결과 처리.
					if ( memberCheck->retvalue == 0 )
					{
						if ( mSocketContextPool->SetCID( perSocketContext, memberCheck->memberIdx ) )
						{
							clientInfo->complete.check = true;
							clientInfo->memberIdx    = memberCheck->memberIdx;
							clientInfo->loginIdx     = memberCheck->loginIdx;
							clientInfo->lastChannel  = memberCheck->lastChannel;
							clientInfo->characterIdx = memberCheck->characterIdx;
							clientInfo->slevel       = memberCheck->slevel;

							clientInfo->request.characterSelect    = (clientInfo->characterIdx > 0);
							clientInfo->request.joinInstantDungeon = (memberCheck->instantDungeon != 0 && mChannelNum == CHN_11);
							clientInfo->request.joinMap            = (memberCheck->instantDungeon != 0 && mChannelNum != CHN_11);
						}
						else
						{
							PostServerEvent( "SQL_GAME_PROCESS_MEMBER_CHECK - EXISTS MEMBER_IDX(=%d)", memberCheck->memberIdx );
							memberCheck->retvalue = 1;
						}
					}
					else
						PostServerEvent( "SQL_GAME_PROCESS_MEMBER_CHECK - EXISTS LOGIN_IDX (=%d), MEMBER_IDX (=%d)", memberCheck->loginIdx, memberCheck->memberIdx );

					// 2. 결과 전송 [0성공/1체크실패/2알수없는 오류].
					ResMsgError( perSocketContext, NM_USER, (char)NM_USER_GAMESRV_RES, memberCheck->retvalue );

					clientInfo->commonDb.check = false;		// DB 사용종료.
				}
			}
			break;
		case SQL_GAME_PROCESS_MEMBER_LOGOUT:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock     lock( &mCs );
				ClientInfo* clientInfo = (ClientInfo*)perSocketContext->buffer;

				if ( clientInfo->commonDb.logout )
				{
					MEMBER_LOGOUT* memberLogout = (MEMBER_LOGOUT*)perIoContext->buffer;

					// Logout
					clientInfo->complete.check = false;

					// 로그.
					char* inetnum = inet_ntoa( perSocketContext->addr.sin_addr );
					TCHAR message[1024];

					sprintf( message, "Type (=Success Audit), Category (=Logoff), Inetnum (=%s), Error Code (=%d)", inetnum, memberLogout->retvalue );
					PostMemberEvent( EVENT_MEMBER_SUCCESS_AUDIT, EVENT_MEMBER_LOGOUT, memberLogout->memberIdx, message );

					clientInfo->commonDb.logout = false;	// DB 사용종료.
				}
			}
			break;
		case SQL_GAME_PROCESS_COMEIN_GAME:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock     lock( &mCs );
				ClientInfo* clientInfo = (ClientInfo*)perSocketContext->buffer;

				if ( clientInfo->commonDb.comeinGame )
				{
					COMEIN_GAME* comeinGame = (COMEIN_GAME*)perIoContext->buffer;

					// 결과 전송 [0성공/1미등록서버/2알수없는 오류].
					if ( comeinGame->retvalue == 0 )
					{
						// 게임서버 이동이므로, 로그아웃 처리를 무시.
						clientInfo->complete.check = false;

						PerIoContext* sendIoContext = mIoContextPool->GetIoContext( perSocketContext->socket, IOCP_REQUEST_WRITE );
						Game2Game*    packet        = (Game2Game*)sendIoContext->buffer;

						packet->ver  = PHVer;
						packet->hlen = PHLen;
						packet->tos  = TOS_GAME_2_GAME;
						packet->tlen = sizeof(Game2Game);
						packet->ipv4 = comeinGame->ipv4;
						packet->port = comeinGame->port;

						sendIoContext->offset = packet->tlen;
						SendPost( sendIoContext );
					}
					else
						ResMsgError( perSocketContext, NM_USER, (char)NM_USER_COME_IN_GAME_RES, comeinGame->retvalue );

					clientInfo->commonDb.comeinGame = false;	// DB 사용종료.
				}
			}
			break;
		case SQL_GAME_PROCESS_GAMEIN_COMPLETE:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock     lock( &mCs );
				ClientInfo* clientInfo = (ClientInfo*)perSocketContext->buffer;

				clientInfo->complete.gameInOutComplete = true;
			}
			break;
		case SQL_GAME_PROCESS_GAMEOUT_COMPLETE:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock     lock( &mCs );
				ClientInfo* clientInfo = (ClientInfo*)perSocketContext->buffer;

				clientInfo->complete.gameInOutComplete = false;
			}
			break;
		case SQL_GAME_PROCESS_GOTO_INSTANT_DUNGEON:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock     lock( &mCs );
				ClientInfo* clientInfo = (ClientInfo*)perSocketContext->buffer;

				if ( clientInfo->commonDb.gotoInstantDungeon )
				{
					GOTO_INSTANT_DUNGEON* gotoInstantDungeon = (GOTO_INSTANT_DUNGEON*)perIoContext->buffer;

					// 결과 전송 [0성공/1미등록서버/2알수없는 오류].
					if ( gotoInstantDungeon->retvalue == 0 )
					{
						// 게임서버 이동이므로, 로그아웃 처리를 무시.
						clientInfo->complete.check = false;

						PerIoContext* sendIoContext = mIoContextPool->GetIoContext( perSocketContext->socket, IOCP_REQUEST_WRITE );
						Game2Game*    packet        = (Game2Game*)sendIoContext->buffer;

						packet->ver  = PHVer;
						packet->hlen = PHLen;
						packet->tos  = TOS_GAME_2_GAME;
						packet->tlen = sizeof(Game2Game);
						packet->ipv4 = gotoInstantDungeon->ipv4;
						packet->port = gotoInstantDungeon->port;

						sendIoContext->offset = packet->tlen;
						SendPost( sendIoContext );
					}
					else
						ResMsgError( perSocketContext, NM_PVP, NM_PVP_DM_JOIN_RES, gotoInstantDungeon->retvalue );

					clientInfo->commonDb.gotoInstantDungeon = false;	// DB 사용종료.
				}
			}
			break;
		case SQL_GAME_PROCESS_RETURN_TO_MAP:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock     lock( &mCs );
				ClientInfo* clientInfo = (ClientInfo*)perSocketContext->buffer;

				if ( clientInfo->commonDb.returnToMap )
				{
					RETURN_TO_MAP* returnToMap = (RETURN_TO_MAP*)perIoContext->buffer;

					// 결과 전송 [0성공/1미등록서버/2알수없는 오류].
					if ( returnToMap->retvalue == 0 )
					{
						// 게임서버 이동이므로, 로그아웃 처리를 무시.
						clientInfo->complete.check = false;

						PerIoContext* sendIoContext = mIoContextPool->GetIoContext( perSocketContext->socket, IOCP_REQUEST_WRITE );
						Game2Game*    packet        = (Game2Game*)sendIoContext->buffer;

						packet->ver  = PHVer;
						packet->hlen = PHLen;
						packet->tos  = TOS_GAME_2_GAME;
						packet->tlen = sizeof(Game2Game);
						packet->ipv4 = returnToMap->ipv4;
						packet->port = returnToMap->port;

						sendIoContext->offset = packet->tlen;
						SendPost( sendIoContext );
					}

					clientInfo->commonDb.returnToMap = false;	// DB 사용종료.
				}
			}
			break;

		case SQL_GAME_PROCESS_CHARACTER_LIST:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock     lock( &mCs );
				ClientInfo* clientInfo = (ClientInfo*)perSocketContext->buffer;

				if ( clientInfo->commonDb.characterList )
				{
					// 결과 전송.
					CHARACTER_LIST* characterList = (CHARACTER_LIST*)perIoContext->buffer;
					ResCharacterList( perSocketContext, characterList->rowCount, characterList->table );

					clientInfo->commonDb.characterList = false;	// DB 사용종료.
				}
			}
			break;
		case SQL_GAME_PROCESS_CHARACTER_NAME:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock     lock( &mCs );
				ClientInfo* clientInfo = (ClientInfo*)perSocketContext->buffer;

				if ( clientInfo->commonDb.characterName )
				{
					// 결과 전송.
					CHARACTER_NAME* characterName = (CHARACTER_NAME*)perIoContext->buffer;
					ResMsgError( perSocketContext, NM_USER, (char)NM_USER_CHARACTER_NAME_RES, characterName->retvalue );

					clientInfo->commonDb.characterName = false;	// DB 사용종료.
				}
			}
			break;
		case SQL_GAME_PROCESS_CHARACTER_INSERT:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock     lock( &mCs );
				ClientInfo* clientInfo = (ClientInfo*)perSocketContext->buffer;

				if ( clientInfo->commonDb.characterInsert )
				{
					CHARACTER_INSERT* characterInsert = (CHARACTER_INSERT*)perIoContext->buffer;

					if ( characterInsert->retvalue == 0 )
					{
						TB_INVENTORY* table = NULL;

						// 캐릭터 생성시 지급된 기본 아이템 내역 기록(Log).
						for ( long i = 0; i < characterInsert->rowCount; i++ )
						{
							table = &characterInsert->table[ i ];
							// Log;
							PostInventoryEvent( EVENT_INVENTORY_CREATE, characterInsert->idx, table, "DEFAULT_ITEMS" );
						}
					}

					// 결과 전송.
					ResMsgError( perSocketContext, NM_USER, NM_USER_CHARACTER_CREATE_RES, characterInsert->retvalue );

					// 캐릭터 생성로그.
					char buffer[1024];

					sprintf( buffer,
							 "Category:   Create\r\nInetnum:    %s\r\nError Code: %d",
							 inet_ntoa( perSocketContext->addr.sin_addr ),
							 characterInsert->retvalue );

					PostCharacterEvent( EVENT_CHARACTER_CREATE, clientInfo->memberIdx, mServerNum, characterInsert->idx, buffer );

					clientInfo->commonDb.characterInsert = false;	// DB 사용종료.
				}
			}
			break;
		case SQL_GAME_PROCESS_CHARACTER_DELETE:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock     lock( &mCs );
				ClientInfo* clientInfo = (ClientInfo*)perSocketContext->buffer;

				if ( clientInfo->commonDb.characterDelete )
				{
					CHARACTER_DELETE* characterDelete = (CHARACTER_DELETE*)perIoContext->buffer;

					// 결과 전송.
					HANDLE                    handle  = NULL;
					MSG_RES_CHARACTER_DELETE* sendMsg = (MSG_RES_CHARACTER_DELETE*)GetMsgRoot( &handle, perSocketContext, NM_USER, NM_USER_CHARACTER_DELETE_RES );
					sendMsg->ErrorCode    = characterDelete->retvalue;
					sendMsg->CharacterIdx = characterDelete->idx;
					SendMsgRoot( handle, sizeof(MSG_RES_CHARACTER_DELETE) );

					// 캐릭터 삭제로그.
					char buffer[1024];

					sprintf( buffer,
							 "Category:    Delete\r\nInetnum:     %s\r\nError Code: %d",
							 inet_ntoa( perSocketContext->addr.sin_addr ),
							 characterDelete->retvalue );

					PostCharacterEvent( EVENT_CHARACTER_DELETE, clientInfo->memberIdx, mServerNum, characterDelete->idx, buffer );

					clientInfo->commonDb.characterDelete = false; // DB 사용종료.
				}
			}
			break;
		case SQL_GAME_PROCESS_CHARACTER_UPDATE:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock     lock( &mCs );
				ClientInfo* clientInfo = (ClientInfo*)perSocketContext->buffer;

				if ( clientInfo->commonDb.characterUpdate )
				{
					// 1 결과 처리.
					clientInfo->commonDb.characterUpdate = false;
					clientInfo->complete.characterUpdate = false;
				}

				CHARACTER_UPDATE* characterUpdate = (CHARACTER_UPDATE*)perIoContext->buffer;
				if( characterUpdate->retvalue != 0 )
				{
					assert(NULL);
					PostServerEvent("SQL_GAME_PROCESS_CHARACTER_UPDATE[%d] ERROR[%d]", characterUpdate->retvalue,
						clientInfo->characterIdx );
				}
			}
			break;
		case SQL_GAME_PROCESS_CHARACTER_SELECT:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock     lock( &mCs );
				ClientInfo* clientInfo = (ClientInfo*)perSocketContext->buffer;

				if ( clientInfo->commonDb.characterSelect )
				{
					CHARACTER_SELECT* characterSelect = (CHARACTER_SELECT*)perIoContext->buffer;
					TB_CHARACTER*     character       = &characterSelect->character;

					/*-- 응답메시지 발송.
					*/
					try {
						// 선택 캐릭터 인덱스 확인.
						if ( characterSelect->characterIdx != character->idx )
							throw 1;

						// 070915 PKH 플레이어 map에 등록 시점 변경
						cPlayer* hero = mpObjectManager->AddPlayer( perSocketContext->cid, character->idx, character->name, (u_long)character->money, (u_long)character->deposit );
						if ( hero == NULL )
							throw 1;

						unsigned long heroIdx = hero->GetObjectID( );

						// cPlayer 클래스 포인터 연결.
						clientInfo->hero         = hero;
						clientInfo->playerInfo   = (ULONG_PTR)hero->GetPlayerInfo( );
						clientInfo->wearInfo     = (ULONG_PTR)hero->GetWearInfo( );
						clientInfo->weaponInfo   = (ULONG_PTR)hero->GetWeaponInfo( );
						clientInfo->exrInfo      = (ULONG_PTR)hero->GetExrInfo( );

						// 캐릭터 관련-DB 검색.
						clientInfo->gameInDb.heroInfo             = true;
						clientInfo->gameInDb.inventoryList        = InventorySelect( perSocketContext, heroIdx );
						clientInfo->gameInDb.inventoryCooltime    = InventoryCooltimeSelect( perSocketContext, heroIdx );
						clientInfo->gameInDb.shortcutSelect       = ShortcutSelect( perSocketContext, heroIdx );
						clientInfo->gameInDb.questList		      = QuestSelect( perSocketContext, heroIdx );
						clientInfo->gameInDb.questEndList	      = QuestCompleteSelect( perSocketContext, heroIdx );
						clientInfo->gameInDb.titleList		      = TitleSelect( perSocketContext, heroIdx );
						clientInfo->gameInDb.skillSelect          = SkillSelect( perSocketContext, heroIdx );
						clientInfo->gameInDb.skillInfluenceSelect = SkillInfluenceSelect( perSocketContext, heroIdx );
						clientInfo->gameInDb.noteList			  = NoteSelect( perSocketContext, heroIdx );
						clientInfo->gameInDb.makeSkillList        = MakeSkillSelect( perSocketContext, heroIdx );
						clientInfo->gameInDb.fortuneData		  = FortuneSelect( perSocketContext, heroIdx );

						// 캐릭터 옵션 [option1] 보관.
						clientInfo->optionData1 = character->option1;
						clientInfo->request.characterOptionSend = true;
						clientInfo->complete.characterOption = true;

						// sPlayerInfo
						sPlayerInfo* playerInfo = hero->GetPlayerInfo( );

							wcscpy( playerInfo->strName, character->name );
							playerInfo->Level  = character->level;
							playerInfo->Race   = character->race;
							playerInfo->Gender = character->gender;
							playerInfo->Job    = (ePLAYER_JOB)character->job;
							playerInfo->HeadInfo[ eHEAD_HAIR ] = character->hair;
							playerInfo->HeadInfo[ eHEAD_FACE ] = character->face;

						// sPlayerWearInfo
						sPlayerWearInfo* wearInfo = hero->GetWearInfo( );

							wearInfo->WearIdx[ eWEAR_HAT   ] = character->hat;
							wearInfo->WearIdx[ eWEAR_BODY1 ] = character->body1;
							wearInfo->WearIdx[ eWEAR_BODY2 ] = character->body2;
							wearInfo->WearIdx[ eWEAR_HAND  ] = character->hand;
							wearInfo->WearIdx[ eWEAR_FOOT  ] = character->foot;

							wearInfo->Earring  = character->earring;
							wearInfo->Necklace = character->necklace;
							wearInfo->Brooch   = character->brooch;
							wearInfo->Bracelet = character->bracelet;
							wearInfo->Ring     = character->ring;

						// 무기정보 - sPlayerWeaponInfo
						sPlayerWeaponInfo* weaponInfo = hero->GetWeaponInfo( );

							weaponInfo->WeaponIdx[ eHAND_LEFT  ] = character->leftHand;
							weaponInfo->WeaponIdx[ eHAND_RIGHT ] = character->rightHand;

							weaponInfo->WeaponEnhanced[ eHAND_LEFT  ] = character->leftHandEnhanced;
							weaponInfo->WeaponEnhanced[ eHAND_RIGHT ] = character->rightHandEnhanced;

						// sPlayerExrInfo
						sPlayerExrInfo* exrInfo = hero->GetExrInfo();

							exrInfo->mForceType       = character->mForceType;
							exrInfo->mTitleIndex	  = character->mTitleIndex;
							exrInfo->mGuildIndex	  = character->mGuildIndex;
							if ( exrInfo->mGuildIndex > 0 )
							{
								exrInfo->mGuildPosition = character->mGuildPosition;
								exrInfo->mGuildMarkIndex = mpGuildManager->GetGuildMarkIndex( exrInfo->mGuildIndex );

								wchar_t* name = mpGuildManager->GetGuildName( exrInfo->mGuildIndex );
								if ( name != NULL )
									wcscpy( exrInfo->mGuildName, name ); 
							}

						// sHeroInfo
						sHeroInfo* heroInfo = hero->GetHeroInfo();

							heroInfo->Exp              = character->exp;
							heroInfo->SkillLevel       = character->skillLevel;
							heroInfo->SkillPointRemain = (unsigned short)character->skillPointRemain;
							heroInfo->SkillPointTotal  = (unsigned short)character->skillPointTotal;
							heroInfo->SkillExp         = character->skillExp;
							heroInfo->TarotPoint       = character->tarotPoint;
							heroInfo->mPvPPoint        = character->mPvPPoint;
							heroInfo->mFirePoint       = character->mFirePoint; 
							heroInfo->mFireFriendly    = character->mFireFriendly;
							heroInfo->mWaterPoint      = character->mWaterPoint;
							heroInfo->mWaterFriendly   = character->mWaterFriendly;
							heroInfo->mWindPoint       = character->mWindPoint;
							heroInfo->mWindFriendly    = character->mWindFriendly;
							heroInfo->mEarthPoint      = character->mEarthPoint;
							heroInfo->mEarthFriendly   = character->mEarthFriendly;
							heroInfo->mMakeSkill1      = character->mMakeSkill1;
							heroInfo->mMakeSkill2      = character->mMakeSkill2;

						// Default Set
						hero->SetActiveWeapon( (eItemActiveWeapon)character->activeWeapon );
						hero->SetMapNumber( character->mapNum );
						hero->SetPos( character->xPos, character->yPos );
						hero->SetOptionData( character->option1 );


						if( mChannelNum != CHN_11 )
						{
							// 못가는 지역에 저장되 있는 경우 위치보정
							if ( AIMANAGER->IsPassible( character->mapNum, character->xPos, character->yPos, hero->GetObject() ) == false )
							{
								NiPoint2 pos = STAGESCRIPT->CalcNearTargetMapPos( hero->GetMapNumber(), hero->GetXPos(), hero->GetYPos() );
								hero->SetPos( pos.x, pos.y );

								/// 보정 후에도 못가는 지역이면 - 실패시 캐릭터 생성위치로 이동
								if ( AIMANAGER->IsPassible( character->mapNum, pos.x, pos.y, hero->GetObject() ) == false )
								{
									sTargetPos pos = STAGESCRIPT->GetStageChangePos( CHARACTER_CREATE_POS_IDX );
									hero->SetMapNumber( pos.mMapNumber );
									hero->SetPos( pos.mPosX, pos.mPosY );
								}
							}
						}

						STATUSCALC->CalcPlayerInit( hero->GetObject() );

						// HP & MP.
						hero->InitHP( character->HP > 0 ? character->HP : 10 );
						hero->InitMP( character->MP );

						// hp/mp db에서 읽어온값 임시 장소에 저장
						hero->SetInitRestHP( );
						hero->SetInitRestMP( );

						/// 호칭정보 
						hero->InitTitle( );

						if ( clientInfo->request.joinInstantDungeon )
						{
							ePVPDM_TEAM_TYPE teamType = ePVPDM_TEAMTYPE_MAX;
							switch( hero->GetForceType() )
							{
							case eFORCETYPE_FIRE:
								teamType = ePVPDM_TEAMTYPE_A;
								break;
							case eFORCETYPE_WIND:
							case eFORCETYPE_WATER:
							case eFORCETYPE_EARTH:
								teamType = ePVPDM_TEAMTYPE_B;
								break;
							default:
								NETWORK2->PostServerEvent("CallbackComplete hero[%d]->GetForceType() default:", heroIdx );
							}
							sDMInfo* pDMInfo = PVPSCRIPT->GetDmInfo( 0, hero->GetLevel() );
							unsigned char pvpDMIdx  = PVPMANAGER->JoinDeathMatch( heroIdx, teamType, pDMInfo );
							NiPoint2      goPos     = hero->GetPos();

							if ( teamType == ePVPDM_TEAMTYPE_A )
							{
								if ( STAGESCRIPT->CalcTargetMapPos( PVP_DM_START_POSIDX_A, &goPos.x, &goPos.y ) == false )
									NETWORK2->PostServerEvent("");
							}
							else
							{
								if ( STAGESCRIPT->CalcTargetMapPos( PVP_DM_START_POSIDX_B, &goPos.x, &goPos.y ) == false )
									NETWORK2->PostServerEvent("");
							}
							if ( pvpDMIdx != 0 && hero->IsPvPJoin() == false )
							{
								if ( hero->PvPMapChange( pvpDMIdx, pDMInfo->mMapNumber, (unsigned char)teamType, goPos.x, goPos.y ) == false )
								{
									// 실패처리
								}
								clientInfo->complete.pvpJoin = true;
							}
						}
						else if ( clientInfo->request.joinMap )
						{
							hero->CheatMapChange( hero->GetMapNumber( ), hero->GetXPos( ), hero->GetYPos( ) );
						}
						else
						{
							// 캐릭터 정보 발송 - 성공.
							hero->SendPlayerInfo( NM_USER, NM_USER_CHARACTER_SELECT_RES, (ULONG_PTR)perSocketContext );
							hero->SendMoney( hero->GetObject( ) );
							hero->SendDeposit( hero->GetObject( ) );
						}

						// Select On.
						clientInfo->complete.characterSelect = true;

						// Block 적용여부 검색.
						{
							HANDLE                  handle               = NULL;
							CHARACTER_BLOCK_SELECT* characterBlockSelect = (CHARACTER_BLOCK_SELECT*)GetSQL( &handle, SQL_GAME_PROCESS_CHARACTER_BLOCK_SELECT );

							characterBlockSelect->characterIdx = clientInfo->characterIdx;

							SendSQL( perSocketContext, handle, sizeof(CHARACTER_BLOCK_SELECT) );
						}
						// Gamein Complete - TB_LOGIN Table 동기화.
						{
							HANDLE           handle         = NULL;
							GAMEIN_COMPLETE* gameinComplete = (GAMEIN_COMPLETE*)GetSQL( &handle, SQL_GAME_PROCESS_GAMEIN_COMPLETE );

							gameinComplete->loginIdx  = clientInfo->loginIdx;
							wcscpy( gameinComplete->characterName, character->name );

							SendSQL( perSocketContext, handle, sizeof(GAMEIN_COMPLETE) );
						}
					} catch ( int error ) {
						// 캐릭터 정보 발송 - 실패.
						ResMsgError( perSocketContext, NM_USER, NM_USER_CHARACTER_SELECT_RES, error );
					}

					clientInfo->commonDb.characterSelect = false;	// DB; 사용종료.
				}
			}
			break;
		case SQL_GAME_PROCESS_CHARACTER_EQUIP:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock     lock( &mCs );
				ClientInfo* clientInfo = (ClientInfo*)perSocketContext->buffer;

				if ( clientInfo->commonDb.characterEquip )
				{
					clientInfo->complete.characterEquip = false;
					clientInfo->commonDb.characterEquip = false;	// DB; 사용종료.
				}
			}
			break;
		case SQL_GAME_PROCESS_CHARACTER_MONEY:
			{
				CHARACTER_MONEY* characterMoney = (CHARACTER_MONEY*)perIoContext->buffer;
				bool             skipLog        = false;
				char             buffer[260];

				switch ( characterMoney->objectType )
				{
				case eOBJECTTYPE_MONSTER:
					if ( characterMoney->objectRaceGender != 0 )
						skipLog = true; // SKIP - sprintf( buffer, "MONSTER::%d(%d):%I64d", characterMoney->objectRaceGender, characterMoney->objectIndex, characterMoney->money );
					else
						sprintf( buffer, "ERROR MONSTER::%d:%I64d", characterMoney->objectIndex, characterMoney->money );
					break;
				case eOBJECTTYPE_HERO:
					sprintf( buffer, "HERO::%d:%I64d", characterMoney->objectIndex, characterMoney->money );
					break;
				case eOBJECTTYPE_PLAYER:
					sprintf( buffer, "PLAYER::%d:%I64d", characterMoney->objectIndex, characterMoney->money );
					break;
				case eOBJECTTYPE_NPC:
					if ( characterMoney->objectRaceGender != 0 )
						sprintf( buffer, "NPC::%d(%d):%I64d", characterMoney->objectRaceGender, characterMoney->objectIndex, characterMoney->money );
					else
						sprintf( buffer, "ERROR NPC::%d:%I64d", characterMoney->objectIndex, characterMoney->money );
					break;
				case eOBJECTTYPE_ITEM:
					sprintf( buffer, "ITEM::%d:%I64d", characterMoney->objectIndex, characterMoney->money );
					break;
				default:
					sprintf( buffer, "ERROR UNKNOWN::%d:%I64d", characterMoney->objectIndex, characterMoney->money );
					break;
				}

				if ( skipLog != true )
					PostMoneyEvent( characterMoney->idx, characterMoney->befor, characterMoney->after, buffer );
			}
			break;
		case SQL_GAME_PROCESS_CHARACTER_DEPOSIT:
			{
				CHARACTER_DEPOSIT* characterDeposit = (CHARACTER_DEPOSIT*)perIoContext->buffer;
				char               buffer[260];

				if ( characterDeposit->objectType == eOBJECTTYPE_NPC )
				{
					if ( characterDeposit->objectRaceGender != 0 )
						sprintf( buffer, "NPC::%d(%d):%I64d", characterDeposit->objectRaceGender, characterDeposit->objectIndex, characterDeposit->money );
					else
						sprintf( buffer, "ERROR NPC::%d:%I64d", characterDeposit->objectIndex, characterDeposit->money );

					PostDepositEvent( characterDeposit->idx, characterDeposit->befor, characterDeposit->after, buffer );
				}
			}
			break;
		case SQL_GAME_PROCESS_CHARACTER_TAROT_POINT:
			// Skip
			break;
		case SQL_GAME_PROCESS_CHARACTER_PVP_POINT:
			// Skip
			break;
		case SQL_GAME_PROCESS_CHARACTER_LEVEL:
			{
				CHARACTER_LEVEL* characterLevel = (CHARACTER_LEVEL*)perIoContext->buffer;

				if( characterLevel->retvalue != 0 )
				{
					assert(NULL);
					PostServerEvent("SQL_GAME_PROCESS_CHARACTER_LEVEL ERROR[%d][%d,%d,%d]", characterLevel->retvalue,
						characterLevel->idx, characterLevel->level, characterLevel->levelUpPoint );
				}
			}
			break;
		case SQL_GAME_PROCESS_CHARACTER_SKILLLEVEL:
			{
				CHARACTER_SKILLLEVEL*  characterSkillLevel = (CHARACTER_SKILLLEVEL*)perIoContext->buffer;

				if( characterSkillLevel->retvalue != 0 )
				{
					PostServerEvent("SQL_GAME_PROCESS_CHARACTER_SKILLLEVEL ERROR[%d][%d,%d,%d,%d,%d,%d]", characterSkillLevel->retvalue,
						characterSkillLevel->idx, characterSkillLevel->skillLevel, characterSkillLevel->levelUpPoint,
						characterSkillLevel->skillPoint, characterSkillLevel->skillTotal, characterSkillLevel->skillUpPoint );
				}
			}
			break;
		case SQL_GAME_PROCESS_CHARACTER_ACTIVE_WEPON:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock     lock( &mCs );
				ClientInfo* clientInfo = (ClientInfo*)perSocketContext->buffer;

				if ( clientInfo->commonDb.characterActiveWeapon )
				{
					clientInfo->complete.characterActiveWeapon = false;	// Active Weapon 업데이트 완료.
					clientInfo->commonDb.characterActiveWeapon = false;	// DB; 사용종료.
				}
			}
			break;
		case SQL_GAME_PROCESS_CHARACTER_OPTION:
			// Skip
			break;
		case SQL_GAME_PROCESS_CHARACTER_FORCETYPE:
			{
				CHARACTER_FORCE*  characterForce = (CHARACTER_FORCE*)perIoContext->buffer;

				if( characterForce->mRetValue != 0 )
				{
					PostServerEvent("SQL_GAME_PROCESS_CHARACTER_FORCETYPE ERROR[%d][%d,%d]", characterForce->mRetValue,
						characterForce->mIdx, characterForce->mForceType );
				}
			}
			break;

		case SQL_GAME_PROCESS_INVENTORY_INSERT:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock lock( &mCs );

				INVENTORY_INSERT* inventoryInsert = (INVENTORY_INSERT*)perIoContext->buffer;
				TB_INVENTORY*     table           = inventoryInsert->table;

				ClientInfo* clientInfo = (ClientInfo*)perSocketContext->buffer;
				cPlayer*    hero       = clientInfo->hero;

				if ( perIoContext->requestResult == IOCP_REQUEST_SUCCESS )
				{
					if ( clientInfo->inventoryDb.insert )
					{
						HANDLE              handle     = NULL;
						MSG_RES_CHEAT_MAKE* sendMsg    = (MSG_RES_CHEAT_MAKE*)GetMsgRoot( &handle, perSocketContext, NM_CHEAT, NM_CHEAT_MAKE_RES );
						sInventory*         sinventory = sendMsg->inventory;

						sendMsg->ErrorCode = ERROR_CHEAT_MAKE_SUCCESS;

						for ( long i = 0; i < inventoryInsert->rowCount; i++, table++, sinventory++ )
						{
							hero->XCopyInventory( table ); // 고유번호 복사.

							Inventory2sInventory( sinventory, table );
							sendMsg->rowCount++;

							PostInventoryEvent( EVENT_INVENTORY_CREATE, clientInfo->characterIdx, table, "INSERT-CHEAT" );
						}

						SendMsgRoot( handle, sendMsg->GetMsgLength( ) );

						clientInfo->inventoryDb.insert = false; // DB 사용종료.
					}
				}
				else
				{
					PostServerEvent( "WARNING - SQL_GAME_PROCESS_INVENTORY_INSERT - CHARACTER_IDX(%d)", clientInfo->characterIdx );
					CloseCID( perSocketContext->cid );
				}
			}
			break;
		case SQL_GAME_PROCESS_INVENTORY_SELECT:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock lock( &mCs );

				INVENTORY_SELECT* inventorySelect = (INVENTORY_SELECT*)perIoContext->buffer;
				TB_INVENTORY*     table           = inventorySelect->table;

				ClientInfo*   clientInfo = (ClientInfo*)perSocketContext->buffer;
				cPlayer*      hero       = clientInfo->hero;
				TB_INVENTORY* inventory  = NULL;

				if ( clientInfo->gameInDb.inventoryList )
				{
					// 불러온 인벤토리 복사.
					for ( long i = 0; i < inventorySelect->rowCount; i++, table++ )
					{
						// 불러온 인벤토리의 유효성 검사.
						if ( hero->IsInventory( table ) == true )
						{
							if ( ITEMMANAGER->IsItemDefineIndex( table->itemDefineIdx ) == true )
							{
								inventory = hero->SelectInventory( table->number );
								// 인벤토리 중복 검사.
								if ( hero->IsInventory( inventory ) == false )
								{
									hero->XCopyInventory( table ); // 인벤토리 복사.
								}
								else
								{
									InventoryDelete( perSocketContext
													,IW_ERROR_DUPLICATE_NUMBER
													,clientInfo->characterIdx
													,table->idx );	// 인벤토리 중복 - 삭제.
								}
							}
							else
							{
								InventoryDelete( perSocketContext
												,IW_ERROR_ITEM_DEFINE_INDEX
												,clientInfo->characterIdx
												,table->idx );		// 인벤토리 오류 - 삭제.
							}
						}
						else if ( table->count <= 0 )
						{
							InventoryDelete( perSocketContext
											,IW_ERROR_UNDER_COUNT
											,clientInfo->characterIdx
											,table->idx );		// 인벤토리 오류 - 삭제.
						}
					}

					// complete flag
					clientInfo->complete.inventory = true;      // 인벤토리 불러오기 완료.

					// request flag
					clientInfo->request.inventorySend   = true; // 인벤토리 발송     요청.
					clientInfo->request.characterEquip  = true; // 캐릭터   장비교채 요청.
					clientInfo->request.inventoryUpdate = true; // 인벤토리 업데이트 요청.

					clientInfo->gameInDb.inventoryList = false;	// DB; 사용종료.
				}
			}
			break;
		case SQL_GAME_PROCESS_INVENTORY_DELETE:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				INVENTORY_DELETE* inventoryDelete = (INVENTORY_DELETE*)perIoContext->buffer;
				if ( !perIoContext->requestResult && !inventoryDelete->retvalue )
				{
					TB_INVENTORY* table = &inventoryDelete->table;
					switch ( inventoryDelete->warning )
					{
					case IW_ERROR_DUPLICATE_NUMBER:
						PostServerEvent( "WARNING(DUPLICATE) - SQL_GAME_PROCESS_INVENTORY_DELETE - CHARACTER_IDX(%u):INVENTORY_IDX(=%d):INVENTORY_NUMBER(=%d)"
										,inventoryDelete->characterIdx
										,table->idx
										,table->number );
						break;
					case IW_ERROR_ITEM_DEFINE_INDEX:
						PostServerEvent( "WARNING(ERROR) - SQL_GAME_PROCESS_INVENTORY_DELETE - CHARACTER_IDX(%u):INVENTORY_IDX(=%d):ITEM_DEFINE_INDEX(=%d)"
										,inventoryDelete->characterIdx
										,table->idx
										,table->itemDefineIndex );
						break;
					case IW_ERROR_UNDER_COUNT:
						PostServerEvent( "WARNING(ERROR) - SQL_GAME_PROCESS_INVENTORY_DELETE - CHARACTER_IDX(%u):INVENTORY_IDX(=%d):INVENTORY_COUNT(=%d)"
										,inventoryDelete->characterIdx
										,table->idx
										,table->count );
						break;
					}
					PostInventoryEvent( EVENT_INVENTORY_DELETE, inventoryDelete->characterIdx, table, "DELETE" );
				}
			}
			break;
		case SQL_GAME_PROCESS_INVENTORY_COOLTIME_SELECT:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock lock( &mCs );

				INVENTORY_COOLTIME_SELECT* inventoryCooltimeSelect = (INVENTORY_COOLTIME_SELECT*)perIoContext->buffer;
				TB_INVENTORY_COOLTIME*     table                   = inventoryCooltimeSelect->table;

				ClientInfo* clientInfo = (ClientInfo*)perSocketContext->buffer;
				cPlayer*    hero       = clientInfo->hero;

				for ( long i = 0; i < inventoryCooltimeSelect->rowCount; i++, table++ )
				{
					hero->AddCooltime( table );
				}

				clientInfo->complete.inventoryCooltime = true;		// 인벤토리 쿨타임 불러오기 완료.
				clientInfo->request.inventoryCooltimeSend = true;	// 인벤토리 쿨타임 발송 요청.
				clientInfo->gameInDb.inventoryCooltime = false;		// DB; 사용종료.
			}
			break;
		case SQL_GAME_PROCESS_INVENTORY_COOLTIME_INSERT:
			// SKIP
			break;
		case SQL_GAME_PROCESS_INVENTORY_REMOVE:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock lock( &mCs );

				INVENTORY_REMOVE* inventoryRemove = (INVENTORY_REMOVE*)perIoContext->buffer;
				TB_INVENTORY*     table           = &inventoryRemove->table;

				ClientInfo* clientInfo = (ClientInfo*)perSocketContext->buffer;

				if ( clientInfo->inventoryDb.remove )
				{
					if ( table->apply == InventoryApplyRemoved )
					{
						cPlayer*      hero      = clientInfo->hero;
						TB_INVENTORY* inventory = hero->SelectInventory( table->number );
						if ( table->idx == inventory->idx )
						{
							hero->RemoveInventory( inventory );											// 삭제 완료.

							PostInventoryEvent( EVENT_INVENTORY_DELETE, clientInfo->characterIdx, table, "REMOVE" );
																										// 로그 전송.
							ResMsgError( perSocketContext
										,NM_ITEM
										,NM_ITEM_DEL_INVENTORY_RES
										,ERROR_ITEM_DEL_INVENTORY_SUCCESS );							// 결과 전송 - 성공.
						}
						else
						{
							PostServerEvent( "WARNING - SQL_GAME_PROCESS_INVENTORY_REMOVE - CHARACTER_IDX(%d) TABLE_IDX(=%d) INVENTORY_IDX(=%d)"
											,clientInfo->characterIdx
											,table->idx
											,inventory->idx );											// 로그 전송.
							CloseCID( perSocketContext->cid );											// 강제 종료.
						}
					}
					else
					{
						ResMsgError( perSocketContext
									,NM_ITEM
									,NM_ITEM_DEL_INVENTORY_RES
									,ERROR_ITEM_DEL_INVENTORY_FAIL );									// 결과 전송 - 실패.
					}

					clientInfo->inventoryDb.remove = false;		// DB; 사용종료.
					clientInfo->request.inventoryUpdate = true;	// 인벤토리 업데이트 요청.
				}
			}
			break;
		case SQL_GAME_PROCESS_INVENTORY_USE:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock lock( &mCs );

				INVENTORY_USE* inventoryUse = (INVENTORY_USE*)perIoContext->buffer;
				TB_INVENTORY*  table        = inventoryUse->table;

				ClientInfo* clientInfo = (ClientInfo*)perSocketContext->buffer;

				if ( clientInfo->inventoryDb.use )
				{
					cPlayer*      hero      = clientInfo->hero;
					TB_INVENTORY* inventory = hero->SelectInventory( table->number );

					if ( table->idx == inventory->idx )
					{
						if ( table->apply == InventoryApplyRemoved )
							hero->RemoveInventory( inventory );
						else
							hero->XCopyInventory( inventory, table, XCOPY_INVENTORY_COUNT ); // inventory->count = table->count;

						ResMsgError( perSocketContext
									,NM_ITEM
									,NM_ITEM_USE_INVENTORY_RES
									,ERROR_ITEM_USE_INVENTORY_SUCCESS );							// 결과 전송 - 성공.

						PostInventoryEvent( table->apply == InventoryApplyRemoved ? EVENT_INVENTORY_DELETE : EVENT_INVENTORY_UPDATE
										   ,clientInfo->characterIdx
										   ,table
										   ,"USE" );
					}
					else
					{
						PostServerEvent( "WARNING - SQL_GAME_PROCESS_INVENTORY_USE - CHARACTER_IDX(%d) TABLE_IDX(=%d) INVENTORY_IDX(=%d)"
										,clientInfo->characterIdx
										,table->idx
										,inventory->idx );											// 로그 전송.
						CloseCID( perSocketContext->cid );											// 강제 종료.
					}

					clientInfo->inventoryDb.use = false;		// DB; 사용종료.
					clientInfo->request.inventoryUpdate = true;	// 인벤토리 업데이트 요청.
				}
			}
			break;
		case SQL_GAME_PROCESS_INVENTORY_SWAP:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock lock( &mCs );

				INVENTORY_SWAP* inventorySwap = (INVENTORY_SWAP*)perIoContext->buffer;
				TB_INVENTORY*   table         = inventorySwap->table;

				ClientInfo* clientInfo = (ClientInfo*)perSocketContext->buffer;

				if ( clientInfo->inventoryDb.swap )
				{
					if ( !inventorySwap->retvalue )
					{
						cPlayer*      hero       = clientInfo->hero;
						TB_INVENTORY* inventory1 = hero->SelectInventory( inventorySwap->number1 );
						TB_INVENTORY* inventory2 = hero->SelectInventory( inventorySwap->number2 );

						if ( inventory1->idx == table[0].idx && inventory1->number == table[0].number &&
							 inventory2->idx == table[1].idx && inventory2->number == table[1].number )
						{
						}
						else
						{
							PostServerEvent( "WARNING - SQL_GAME_PROCESS_INVENTORY_SWAP - CHARACTER_IDX(%d)"
										    ,clientInfo->characterIdx );							// 로그 전송.
							CloseCID( perSocketContext->cid );										// 강제 종료.
						}
					}
					else
					{
						PostServerEvent( "ERROR - SQL_GAME_PROCESS_INVENTORY_SWAP - CHARACTER_IDX(%d)"
										,clientInfo->characterIdx );								// 로그 전송.
						CloseCID( perSocketContext->cid );											// 강제 종료.
					}

					clientInfo->inventoryDb.swap = false;	// DB; 사용종료.
				}
			}
			break;
		case SQL_GAME_PROCESS_INVENTORY_MOVE:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock lock( &mCs );

				INVENTORY_MOVE* inventoryMove = (INVENTORY_MOVE*)perIoContext->buffer;
				TB_INVENTORY*   table         = &inventoryMove->table;

				ClientInfo*   clientInfo = (ClientInfo*)perSocketContext->buffer;
				cPlayer*      hero       = clientInfo->hero;
				TB_INVENTORY* inventory  = hero->SelectInventory( inventoryMove->number );

				if ( table->idx != inventory->idx || table->number != inventory->number )
				{
					// 로그 전송.
					PostServerEvent( "WARNING - SQL_GAME_PROCESS_INVENTORY_MOVE - CHARACTER_IDX(%d)" ,clientInfo->characterIdx );
					// 강제 종료.
					CloseCID( perSocketContext->cid );
				}

				clientInfo->inventoryDb.move = false;	// DB; 사용종료.
			}
			break;
		case SQL_GAME_PROCESS_INVENTORY_EXCEPT:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock lock( &mCs );

				INVENTORY_EXCEPT* inventoryExcept = (INVENTORY_EXCEPT*)perIoContext->buffer;
				TB_INVENTORY*     table           = &inventoryExcept->table;

				ClientInfo*   clientInfo = (ClientInfo*)perSocketContext->buffer;
				cPlayer*      hero       = clientInfo->hero;
				TB_INVENTORY* inventory  = hero->SelectInventory( inventoryExcept->number );

				if ( table->idx != inventory->idx || table->number != inventory->number )
				{
					// 로그 전송.
					PostServerEvent( "WARNING - SQL_GAME_PROCESS_INVENTORY_EXCEPT - CHARACTER_IDX(%d)" ,clientInfo->characterIdx );
					// 강제 종료.
					CloseCID( perSocketContext->cid );
				}

				clientInfo->inventoryDb.except = false;	// DB; 사용종료.
			}
			break;
		case SQL_GAME_PROCESS_INVENTORY_MERGE:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock lock( &mCs );

				INVENTORY_MERGE* inventoryMerge = (INVENTORY_MERGE*)perIoContext->buffer;
				TB_INVENTORY*    table          = inventoryMerge->table;

				ClientInfo*   clientInfo = (ClientInfo*)perSocketContext->buffer;
				cPlayer*      hero       = clientInfo->hero;
				unsigned long heroIdx    = clientInfo->characterIdx;

				bool error = false;

				if ( perIoContext->requestResult == IOCP_REQUEST_SUCCESS )
				{
					TB_INVENTORY*    inventory      = NULL;
					char             buffer[1024]   = "\0";

					for ( long i = 0; i < inventoryMerge->rowCount; i++, table++ )
					{
						inventory = hero->SelectInventory( table->number );

						if ( inventory->idx == table->idx )
						{
							if ( table->apply == InventoryApplyRemoved )
								hero->RemoveInventory( inventory );
							else
								hero->XCopyInventory( inventory, table, XCOPY_INVENTORY_COUNT ); // inventory->count = table->count;
						}
						else
							error = true;

						// Log
						sprintf( buffer, "MERGE - [INVENTORY_IDX:%d]", table->idx == inventoryMerge->idx1 ? inventoryMerge->idx2 : inventoryMerge->idx1 );
						PostInventoryEvent( table->apply == InventoryApplyRemoved ? EVENT_INVENTORY_DELETE : EVENT_INVENTORY_UPDATE, heroIdx, table, buffer );
					}

					if ( error == false )
					{
						ResMsgError( perSocketContext
									,NM_ITEM
									,NM_ITEM_MERGE_INVENTORY_RES
									,ERROR_ITEM_MERGE_INVENTORY_SUCCESS );							// 결과 전송 - 성공.
					}
				}
				else
					error = true;

				if ( error == true )
				{
					// 로그 전송.
					PostServerEvent( "ERROR - SQL_GAME_PROCESS_INVENTORY_MERGE - CHARACTER_IDX(%d)" ,clientInfo->characterIdx );
					// 강제 종료.
					CloseCID( perSocketContext->cid );
				}

				clientInfo->inventoryDb.merge = false;	// DB; 사용종료.
			}
			break;
		case SQL_GAME_PROCESS_INVENTORY_DIVIDE:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock lock( &mCs );

				INVENTORY_DIVIDE* inventoryDivide = (INVENTORY_DIVIDE*)perIoContext->buffer;

				ClientInfo*   clientInfo = (ClientInfo*)perSocketContext->buffer;
				cPlayer*      hero       = clientInfo->hero;
				unsigned long heroIdx    = hero->GetObjectID( );

				bool error      = false;

				if ( perIoContext->requestResult == IOCP_REQUEST_SUCCESS )
				{
					TB_INVENTORY* inventory1;
					TB_INVENTORY* inventory2;
					TB_INVENTORY* table;

					// 나누기에 의한 갱신
					table = &inventoryDivide->table[ 0 ];
					inventory1 = hero->SelectInventory( table->number );

					if ( table->idx == inventory1->idx && table->count == inventory1->count )
					{
						PostInventoryEvent( EVENT_INVENTORY_UPDATE, heroIdx, inventory1, "DIVIDE" );
					}
					else
						error = true;

					// 나누기에 의한 생성
					table = &inventoryDivide->table[ 1 ];
					inventory2 = hero->SelectInventory( table->number );
					inventory2->idx = table->idx;

					if ( table->itemDefineIndex == inventory2->itemDefineIndex && table->count == inventory2->count )
					{
						PostInventoryEvent( EVENT_INVENTORY_CREATE, heroIdx, inventory2, "DIVIDE" );
					}
					else
						error = true;

					if ( error == false )
					{
						// 결과.
						HANDLE                         handle     = NULL;
						MSG_RES_ITEM_DIVIDE_INVENTORY* sendMsg    = (MSG_RES_ITEM_DIVIDE_INVENTORY*)GetMsgRoot( &handle, perSocketContext, NM_ITEM, NM_ITEM_DIVIDE_INVENTORY_RES );
						sInventory*                    sinventory = sendMsg->Inventory;

						sendMsg->ErrorCode = ERROR_ITEM_DIVIDE_INVENTORY_SUCCESS;
						sendMsg->RowCount  = inventoryDivide->rowCount;

						Inventory2sInventory( sinventory, inventory1 );
						sinventory++;

						Inventory2sInventory( sinventory, inventory2 );
						sinventory++;

						SendMsgRoot( handle, sendMsg->GetMsgLength( ) );
					}
				}
				else
					error = true;

				if ( error == true )
				{
					// 오류시, 클라이언트 강제 종료.
					PostServerEvent( "WARNING - SQL_GAME_PROCESS_INVENTORY_DIVIDE - CHARACTER_IDX(%u)", clientInfo->characterIdx );
					CloseCID( perSocketContext->cid );
				}

				clientInfo->inventoryDb.divide = false;	// DB; 사용종료.
			}
			break;
		case SQL_GAME_PROCESS_INVENTORY_MOVE2:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock          lock( &mCs );

				INVENTORY_MOVE2* inventoryMove2 = (INVENTORY_MOVE2*)perIoContext->buffer;

				ClientInfo*   clientInfo = (ClientInfo*)perSocketContext->buffer;
				cPlayer*      hero       = clientInfo->hero;
				unsigned long heroIdx    = hero->GetObjectID( );

				bool error          = false;

				if ( perIoContext->requestResult == IOCP_REQUEST_SUCCESS )
				{
					TB_INVENTORY* inventory1 = NULL;
					TB_INVENTORY* inventory2 = NULL;
					TB_INVENTORY* table      = NULL;

					switch ( inventoryMove2->rowCount )
					{
					case 2:		// 분할 이동
						table = &inventoryMove2->table[ 1 ];
						inventory2 = hero->SelectInventory( table->number );
						inventory2->idx = table->idx;

						if ( table->itemDefineIndex == inventory2->itemDefineIndex && table->count == inventory2->count )
						{
							PostInventoryEvent( EVENT_INVENTORY_CREATE, heroIdx, inventory2, "MOVE2" );
						}
						else
							error = true;

					case 1:		// 전체 이동
						table = &inventoryMove2->table[ 0 ];
						inventory1 = hero->SelectInventory( table->number );

						if ( table->idx == inventory1->idx && table->count == inventory1->count )
						{
							PostInventoryEvent( EVENT_INVENTORY_UPDATE, heroIdx, inventory1, "MOVE2" );
						}
						else
							error = true;
					}

					if ( error == false )
					{
						HANDLE                           handle     = NULL;
						MSG_RES_NPC_ITEM_MOV2_INVENTORY* sendMsg    = (MSG_RES_NPC_ITEM_MOV2_INVENTORY*)GetMsgRoot( &handle, perSocketContext, NM_NPC, NM_NPC_ITEM_MOV2_INVENTORY_RES );
						sInventory*                      sinventory = sendMsg->Inventory;

						sendMsg->ErrorCode = ERROR_ITEM_MOV2_INVENTORY_SUCCESS;
						sendMsg->RowCount  = inventoryMove2->rowCount;

						switch ( sendMsg->RowCount )
						{
						case 2:
							Inventory2sInventory( sinventory, inventory2 );
							sinventory++;
						case 1:
							Inventory2sInventory( sinventory, inventory1 );
							sinventory++;
						}

						SendMsgRoot( handle, sendMsg->GetMsgLength( ) );
					}
				}

				if ( error == true )
				{
					// 오류시, 클라이언트 강제 종료.
					PostServerEvent( "WARNING - SQL_GAME_PROCESS_INVENTORY_MOVE2 - CHARACTER_IDX(%u)", clientInfo->characterIdx );
					CloseCID( perSocketContext->cid );
				}

				clientInfo->inventoryDb.move2 = false;		// DB; 사용종료.
				clientInfo->request.inventoryUpdate = true; // 인벤토리 업데이트 요청.
			}
			break;
		case SQL_GAME_PROCESS_INVENTORY_ENHANCED:
			{
				cCSLock lock( &mCs );

				INVENTORY_ENHANCED* inventoryEnhanced = (INVENTORY_ENHANCED*)perIoContext->buffer;

				ClientInfo*   clientInfo = (ClientInfo*)perSocketContext->buffer;
				cPlayer*      hero       = clientInfo->hero;
				unsigned long heroIdx    = hero->GetObjectID( );

				if ( clientInfo->inventoryDb.enhanced )
				{
					TB_INVENTORY* table = NULL;

					// 강화장비
					table = &inventoryEnhanced->table[ 0 ];
					switch ( inventoryEnhanced->result )
					{
					case RESULT_INVENTORY_ENHANCED_SUCCESS:
						hero->XCopyInventory( table ); // (*inventory) = (*table);
						PostInventoryEvent( EVENT_INVENTORY_UPDATE, heroIdx, table, "ENHANCED SUCCESS" );
						break;
					case RESULT_INVENTORY_ENHANCED_DOWN:
						hero->XCopyInventory( table ); // (*inventory) = (*table);
						PostInventoryEvent( EVENT_INVENTORY_UPDATE, heroIdx, table, "ENHANCED DOWN" );
						break;
					case RESULT_INVENTORY_ENHANCED_DESTRUCT:
						if ( table->apply == InventoryApplyRemoved )
						{
							PostInventoryEvent( EVENT_INVENTORY_DELETE, heroIdx, table, "ENHANCED DESTRUCT" );
							hero->RemoveInventory( table->number ); // memset( inventory, 0, sizeof(TB_INVENTORY) );
						}
						break;
					case RESULT_INVENTORY_ENHANCED_FAIL:
						PostInventoryEvent( EVENT_INVENTORY_UPDATE, heroIdx, table, "ENHANCED FAIL" );
						break;
					}

					// 강화카드
					table = &inventoryEnhanced->table[ 1 ];
					if ( table->apply == InventoryApplyRemoved )
					{
						PostInventoryEvent( EVENT_INVENTORY_DELETE, heroIdx, table, "ENHANCED" );
						hero->RemoveInventory( table->number ); // memset( inventory, 0, sizeof(TB_INVENTORY) );
					}

					// 결과전송
					{
						HANDLE                           handle     = NULL;
						MSG_RES_ITEM_ENHANCED_END*       sendMsg    = (MSG_RES_ITEM_ENHANCED_END*)GetMsgRoot( &handle, perSocketContext, NM_ITEM, NM_ITEM_ENHANCED_END_RES );
						TB_INVENTORY*                    inventory  = inventoryEnhanced->table;
						sInventory*                      sinventory = sendMsg->Inventory;

						sendMsg->ErrorCode = ERROR_ITEM_ENHANCED_END_SUCCESS;

						for ( int i = 0; i < 2; i++, sinventory++, inventory++ )
							Inventory2sInventory( sinventory, inventory );

						SendMsgRoot( handle, sizeof(MSG_RES_ITEM_ENHANCED_END) );

						hero->ChangeState( eOBJECT_STATE_IDLE );
						hero->SetStateStop( eSTOP_NONE );

						MSG_SYN_ITEM_ENHANCED_END sync;
						sync.Category = NM_ITEM;
						sync.Protocol = NM_ITEM_ENHANCED_END_SYN;
						sync.mCharacterIdx = heroIdx;
						sync.ErrorCode = ERROR_ITEM_ENHANCED_END_SUCCESS;
                        QuickSendExcept( hero, (char*)&sync, sizeof(MSG_SYN_ITEM_ENHANCED_END) );
					}

					clientInfo->inventoryDb.enhanced = false;	// DB; 사용종료.
					clientInfo->request.inventoryUpdate = true; // 인벤토리 업데이트 요청.
				}
			}
			break;
		case SQL_GAME_PROCESS_INVENTORY_DISJOINT:
			{
				cCSLock lock( &mCs );

				INVENTORY_DISJOINT* inventoryDisjoint = (INVENTORY_DISJOINT*)perIoContext->buffer;

				ClientInfo*   clientInfo = (ClientInfo*)perSocketContext->buffer;
				cPlayer*      hero       = clientInfo->hero;
				unsigned long heroIdx    = hero->GetObjectID( );

				if ( clientInfo->inventoryDb.disjoint )
				{
					if ( perIoContext->requestResult == IOCP_REQUEST_SUCCESS && inventoryDisjoint->retvalue == 0 )
					{
						// 비용처리.
						if ( inventoryDisjoint->characterMoney != 0 )
						{
							unsigned long befor  = hero->GetMoney( );
							long          result = hero->AddMoney( hero->GetObject( ), inventoryDisjoint->characterMoney, false );

							PostMoneyEvent( inventoryDisjoint->characterIdx, befor, hero->GetMoney( ), "INVENTORY_DISJOINT::Money(=%d)", inventoryDisjoint->characterMoney );

							if ( result != inventoryDisjoint->characterMoney )
								PostServerEvent( "Error - SQL_GAME_PROCESS_INVENTORY_DISJOINT AddMoney(=%d) != ResultMoney(=%d).", inventoryDisjoint->characterMoney, result );
						}

						// 아이템 분해 처리.
						HANDLE                           handle     = NULL;
						MSG_RES_ITEM_DISJOINT_INVENTORY* sendMsg    = (MSG_RES_ITEM_DISJOINT_INVENTORY*)GetMsgRoot( &handle, perSocketContext, NM_ITEM, NM_ITEM_DISJOINT_INVENTORY_RES );
						sInventory*                      sinventory = sendMsg->Inventory;

						TB_INVENTORY*                    table      = inventoryDisjoint->table;
						TB_INVENTORY*                    inventory  = NULL;
						bool                             newOrUpdate;

						sendMsg->ErrorCode = ERROR_ITEM_DISJOINT_INVENTORY_SUCCESS;

						for ( long i = 0; i < inventoryDisjoint->rowCount; i++, table++ )
						{
							if ( table->apply == InventoryApplyRemoved )
							{
								PostInventoryEvent( EVENT_INVENTORY_DELETE, heroIdx, table, "INVENTORY_DISJOINT" );
							}
							else
							{
								inventory   = hero->SelectInventory( table->number );
								newOrUpdate = (inventory->idx == table->idx);

								hero->XCopyInventory( inventory, table ); // (*inventory) = (*table);
								Inventory2sInventory( sinventory, inventory );
								sinventory++;

								sendMsg->RowCount++;

								PostInventoryEvent( newOrUpdate ? EVENT_INVENTORY_UPDATE : EVENT_INVENTORY_CREATE, heroIdx, inventory, "INVENTORY_DISJOINT" );
							}
						}
						SendMsgRoot( handle, sendMsg->GetMsgLength( ) );
					}
					else
					{
						ResMsgError( perSocketContext, NM_ITEM, NM_ITEM_DISJOINT_INVENTORY_RES, ERROR_ITEM_DISJOINT_INVENTORY_FAIL );

						// 분해 오류시, 클라이언트 강제 종료.
						PostServerEvent( "WARNING - SQL_GAME_PROCESS_INVENTORY_DISJOINT - CHARACTER_IDX(%u)", clientInfo->characterIdx );
						CloseCID( perSocketContext->cid );
					}

					clientInfo->inventoryDb.disjoint = false;	// DB; 사용종료
					clientInfo->request.inventoryUpdate = true; // 인벤토리 업데이트 요청.
				}
			}
			break;
		case SQL_GAME_PROCESS_INVENTORY_PUT_CARD:
			{
				cCSLock lock( &mCs );

				INVENTORY_PUT_CARD* inventoryPutCard = (INVENTORY_PUT_CARD*)perIoContext->buffer;
				TB_INVENTORY*       table1           = &inventoryPutCard->table[ 0 ];
				TB_INVENTORY*       table2           = &inventoryPutCard->table[ 1 ];

				ClientInfo*   clientInfo = (ClientInfo*)perSocketContext->buffer;
				cPlayer*      hero       = clientInfo->hero;
				unsigned long heroIdx    = hero->GetObjectID( );

				if ( clientInfo->inventoryDb.putCard )
				{
					PostInventoryEvent( EVENT_INVENTORY_UPDATE, heroIdx, table1, "PUT_CARD" );
					PostInventoryEvent( EVENT_INVENTORY_DELETE, heroIdx, table2, "PUT_CARD" );

					hero->XCopyInventory( table1 ); // (*inventory) = (*table1);
					hero->RemoveInventory( table2->number );

					HANDLE                           handle  = NULL;
					MSG_RES_ITEM_PUT_CARD_INVENTORY* sendMsg = (MSG_RES_ITEM_PUT_CARD_INVENTORY*)GetMsgRoot( &handle, perSocketContext, NM_ITEM, NM_ITEM_PUT_CARD_INVENTORY_RES );

					sendMsg->ErrorCode = ERROR_ITEM_PUT_CARD_INVENTORY_SUCCESS;
					Inventory2sInventory( &sendMsg->Inventory, table1 );

					SendMsgRoot( handle, sizeof(MSG_RES_ITEM_PUT_CARD_INVENTORY) );

					clientInfo->inventoryDb.putCard = false;    // DB; 사용종료
					clientInfo->request.inventoryUpdate = true; // 인벤토리 업데이트 요청.
					clientInfo->request.characterEquip = true;  // 착용정보 변경 요청.
				}
			}
			break;
		case SQL_GAME_PROCESS_INVENTORY_CHANGE:
			{
				cCSLock       lock( &mCs );
				ClientInfo*   clientInfo = (ClientInfo*)perSocketContext->buffer;

				if ( clientInfo->inventoryDb.change )
				{
					INVENTORY_CHANGE* inventoryChange = (INVENTORY_CHANGE*)perIoContext->buffer;
					cPlayer*          hero            = clientInfo->hero;
					unsigned long     heroIdx         = hero->GetObjectID( );

					if ( perIoContext->requestResult == IOCP_REQUEST_SUCCESS && inventoryChange->retvalue == 0 )
					{
						// 비용처리.
						if ( inventoryChange->characterMoney != 0 )
						{
							unsigned long befor  = hero->GetMoney( );
							long          result = hero->AddMoney( hero->GetObject( ), inventoryChange->characterMoney, false );

							PostMoneyEvent( inventoryChange->characterIdx, befor, hero->GetMoney( ), "INVENTORY_CHANGE::Money(=%d)", inventoryChange->characterMoney );

							if ( result != inventoryChange->characterMoney )
								PostServerEvent( "Error - SQL_GAME_PROCESS_INVENTORY_CHANGE AddMoney(=%d) != ResultMoney(=%d).", inventoryChange->characterMoney, result );
						}

						// 아이템 교환 처리
						HANDLE                         handle     = NULL;
						MSG_RES_ITEM_CHANGE_INVENTORY* sendMsg    = (MSG_RES_ITEM_CHANGE_INVENTORY*)GetMsgRoot( &handle, perSocketContext, NM_ITEM, NM_ITEM_CHANGE_INVENTORY_RES );
						sInventory*                    sinventory = sendMsg->Inventory;

						TB_INVENTORY*                  table      = inventoryChange->table;
						TB_INVENTORY*                  inventory;
						bool                           newOrUpdate;

						sendMsg->ErrorCode = ERROR_ITEM_DISJOINT_INVENTORY_SUCCESS;

						for ( long i = 0; i < inventoryChange->rowCount; i++, table++ )
						{
							if ( table->apply == InventoryApplyRemoved )
							{
								PostInventoryEvent( EVENT_INVENTORY_DELETE, heroIdx, table, "INVENTORY_CHANGE" );
							}
							else
							{
								inventory   = hero->SelectInventory( table->number );
								newOrUpdate = (inventory->idx == table->idx);
								inventory   = hero->XCopyInventory( table );

								Inventory2sInventory( sinventory, table );
								sinventory++;

								sendMsg->RowCount++;

								PostInventoryEvent( newOrUpdate ? EVENT_INVENTORY_UPDATE : EVENT_INVENTORY_CREATE, heroIdx, inventory, "INVENTORY_CHANGE" );
							}
						}

						SendMsgRoot( handle, sendMsg->GetMsgLength( ) );
					}
					else
					{
						ResMsgError( perSocketContext, NM_ITEM, NM_ITEM_CHANGE_INVENTORY_RES, ERROR_ITEM_CHANGE_INVENTORY_FAIL );

						// 교환 오류시, 클라이언트 강제 종료.
						PostServerEvent( "WARNING - SQL_GAME_PROCESS_INVENTORY_CHANGE - CHARACTER_IDX(%u)", clientInfo->characterIdx );
						CloseCID( perSocketContext->cid );
					}

					clientInfo->inventoryDb.change = false;		// DB; 사용종료.
					clientInfo->request.inventoryUpdate = true; // 인벤토리 업데이트 요청.
				}
			}
			break;
		case SQL_GAME_PROCESS_ITEM_SELL:
			{
				cCSLock       lock( &mCs );
				ClientInfo*   clientInfo = (ClientInfo*)perSocketContext->buffer;
				cPlayer*      hero       = clientInfo->hero;
				unsigned long heroIdx    = hero->GetObjectID( );

				ITEM_SELL*    itemSell   = (ITEM_SELL*)perIoContext->buffer;
				TB_INVENTORY* table      = itemSell->table;
				TB_INVENTORY* inventory;

				HANDLE                handle     = NULL;
				MSG_RES_NPC_ITEM_BUY* sendMsg    = (MSG_RES_NPC_ITEM_BUY*)GetMsgRoot( &handle, perSocketContext, NM_NPC, NM_NPC_ITEM_SELL_RES );
				sInventory*           sinventory = sendMsg->inventory;

				sendMsg->ErrorCode = ERROR_ITEM_SELL_SUCCESS;
				sendMsg->rowCount = 0;

				for ( long i = 0; i < itemSell->rowCount; i++, table++ )
				{
					inventory = hero->SelectInventory( table->number );

					if ( table->apply == InventoryApplyRemoved )
					{
						PostInventoryEvent( EVENT_INVENTORY_DELETE, heroIdx, table, "SELL-INVENTORY" );
						hero->RemoveInventory( inventory );
					}
					else
					{
						if ( inventory->idx == LONG_MAX )
							PostInventoryEvent( EVENT_INVENTORY_CREATE, heroIdx, table, "SELL-REWARD" );
						else
							PostInventoryEvent( EVENT_INVENTORY_UPDATE, heroIdx, table, "SELL-REWARD" );

						hero->XCopyInventory( table ); // (*inventory) = (*table);
					}

					Inventory2sInventory( sinventory, table );
					sinventory++;

					sendMsg->rowCount++;
				}

				SendMsgRoot( handle, sendMsg->GetMsgLength( ) );

				if ( itemSell->sellPrice > 0 )
				{
					switch ( itemSell->sellType )
					{
					case ItemSellMoney:
						{
							sObject object = { eOBJECTTYPE_NPC, itemSell->npcIdx };
							long    result = hero->AddMoney( object, itemSell->sellPrice );

							if ( result != itemSell->sellPrice )
								PostServerEvent( "WARNING - SQL_GAME_PROCESS_ITEM_SELL - CharacterIdx(%u) Money(%u)", heroIdx, hero->GetMoney( ) );
						}
						break;
					case ItemSellPvp:
						hero->AddPvPPoint( itemSell->sellPrice );
						break;
					case ItemSellTarot:
						hero->AddTarotPoint( itemSell->sellPrice );
						break;
					}
				}

				clientInfo->inventoryDb.sell = false;       // DB; 사용종료.
				clientInfo->request.inventoryUpdate = true; // 인벤토리 업데이트 요청.
			}
			break;
		case SQL_GAME_PROCESS_ITEM_BUY:
			{
				cCSLock           lock( &mCs );
				ClientInfo*       clientInfo = (ClientInfo*)perSocketContext->buffer;
				cPlayer*          hero       = clientInfo->hero;
				unsigned long     heroIdx    = hero->GetObjectID( );

				ITEM_BUY*         itemBuy    = (ITEM_BUY*)perIoContext->buffer;
				TB_INVENTORY*     table      = itemBuy->table;
				TB_INVENTORY*     inventory;

				HANDLE                handle     = NULL;
				MSG_RES_NPC_ITEM_BUY* sendMsg    = (MSG_RES_NPC_ITEM_BUY*)GetMsgRoot( &handle, perSocketContext, NM_NPC, NM_NPC_ITEM_BUY_RES );
				sInventory*           sinventory = sendMsg->inventory;

				sendMsg->ErrorCode = ERROR_ITEM_BUY_SUCCESS;
				sendMsg->rowCount  = 0;

				for ( long i = 0; i < itemBuy->rowCount; i++, table++ )
				{
					inventory = hero->SelectInventory( table->number );

					if ( table->apply == InventoryApplyRemoved )
					{
						PostInventoryEvent( EVENT_INVENTORY_DELETE, heroIdx, table, "BUY-INVENTORY" );
						hero->RemoveInventory( inventory );
					}
					else
					{
						if ( inventory->idx == LONG_MAX )
							PostInventoryEvent( EVENT_INVENTORY_CREATE, heroIdx, table, "BUY-REWARD" );
						else
							PostInventoryEvent( EVENT_INVENTORY_UPDATE, heroIdx, table, "BUY-REWARD" );

						hero->XCopyInventory( table ); // (*inventory) = (*table);
					}

					Inventory2sInventory( sinventory, table );
					sinventory++;

					sendMsg->rowCount++;
				}

				SendMsgRoot( handle, sendMsg->GetMsgLength( ) );

				if ( itemBuy->buyPrice != 0 )
				{
					switch ( itemBuy->buyType )
					{
					case ItemBuyMoney:
						{
							sObject object = { eOBJECTTYPE_NPC, itemBuy->npcIdx };
							long    result = hero->AddMoney( object, itemBuy->buyPrice );

							if ( result != itemBuy->buyPrice )
								PostServerEvent( "WARNING - SQL_GAME_PROCESS_ITEM_BUY - CharacterIdx(%u) Money(%u)", heroIdx, hero->GetMoney( ) );
						}
						break;
					case ItemBuyPvp:
						hero->AddPvPPoint( itemBuy->buyPrice );
						break;
					case ItemBuyTarot:
						hero->AddTarotPoint( itemBuy->buyPrice );
						break;
					}
				}

				clientInfo->inventoryDb.buy = false;		// DB; 사용종료.
				clientInfo->request.inventoryUpdate = true; // 인벤토리 업데이트 요청.
			}
			break;
		case SQL_GAME_PROCESS_ITEM_COLLECT:
			{
				cCSLock     lock( &mCs );
				ClientInfo* clientInfo = (ClientInfo*)perSocketContext->buffer;

				if ( clientInfo->inventoryDb.collect )
				{
					ITEM_COLLECT* itemCollect = (ITEM_COLLECT*)perIoContext->buffer;
					cPlayer*      hero        = clientInfo->hero;
					unsigned long heroIdx     = hero->GetObjectID( );

					HANDLE                handle  = NULL;
					MSG_RES_ITEM_COLLECT* sendMsg = (MSG_RES_ITEM_COLLECT*)GetMsgRoot( &handle, perSocketContext, NM_ITEM, NM_ITEM_COLLECT_RES );

					if ( !itemCollect->retvalue )
					{
						if ( itemCollect->remove.inventoryIdx )
						{
							hero->RemoveInventory( itemCollect->inventoryNumber2 );
							PostInventoryEvent( EVENT_INVENTORY_DELETE, heroIdx, &itemCollect->remove.table, "ITEM COLLECT" );
						}

						hero->RemoveInventory( itemCollect->inventoryNumber1 );
						hero->XCopyInventory( &itemCollect->update.table ); // (*inventory) = itemCollect->update.table;

						PostInventoryEvent( EVENT_INVENTORY_UPDATE, heroIdx, &itemCollect->update.table, "ITEM COLLECT" );
					}

					sendMsg->ErrorCode = (itemCollect->retvalue == 0) ? ERROR_ITEM_COLLECT_SUCCESS : ERROR_ITEM_COLLECT_FAIL;
					SendMsgRoot( handle, sizeof(MSG_RES_ITEM_COLLECT) );

					clientInfo->inventoryDb.collect = false;		// DB; 사용종료.
					clientInfo->request.inventoryUpdate = true;		// 인벤토리 업데이트 요청.
				}
			}
			break;
		case SQL_GAME_PROCESS_ITEM_GET:
			{
				cCSLock           lock( &mCs );
				ClientInfo*       clientInfo = (ClientInfo*)perSocketContext->buffer;
				cPlayer*          hero       = clientInfo->hero;
				unsigned long     heroIdx    = hero->GetObjectID( );

				ITEM_GET*         itemGet    = (ITEM_GET*)perIoContext->buffer;

				HANDLE            handle     = NULL;
				MSG_RES_ITEM_GET* sendMsg    = (MSG_RES_ITEM_GET*)GetMsgRoot( &handle, perSocketContext, NM_ITEM, NM_ITEM_GET_RES );
				sInventory*       sinventory = sendMsg->Inventory;
				int               error      = ERROR_ITEM_GET_SUCCESS;

				TB_INVENTORY*     inventory;
				TB_INVENTORY*     table;

				sendMsg->RowCount  = 0;

				for ( long i = 0; i < itemGet->rowCount; i++ )
				{
					if ( itemGet->table[ i ].retvalue == 0 )
					{
						table     = &itemGet->table[ i ].table;
						inventory = hero->SelectInventory( table->number );

						if ( inventory->apply == InventoryApplyGetAuto )
							error = ERROR_ITEM_GET_AUTO;

						hero->XCopyInventory( inventory, table ); // (*inventory) = (*table);

						Inventory2sInventory( sinventory, inventory );
						sinventory++;

						sendMsg->RowCount++;

						PostInventoryEvent( itemGet->table[ i ].newOrUpdate ? EVENT_INVENTORY_UPDATE : EVENT_INVENTORY_CREATE, heroIdx, inventory, "GET" );
					}
				}

				sendMsg->ErrorCode = error;
				SendMsgRoot( handle, sendMsg->GetMsgLength( ) );

				/// 파티에 send
				unsigned long partyIdx = hero->GetPartyIndex( );
				cParty* party = mpPartyManager->GetParty( partyIdx );
				if ( party )
				{
					cPlayer*       partyUser  = 0;
					unsigned long* userArr    = party->GetUserArr( );
					unsigned int   partyCount = party->GetCount( );

					for ( unsigned int i = 0; i < partyCount; ++i, ++userArr )
					{
						if ( (*userArr) == heroIdx )
							continue;

						partyUser = mpObjectManager->GetPlayer( (*userArr) );
						if ( !partyUser )
							continue;

						/// 획득 아이템 데이터 담기
						MSG_SYN_PARTY_ITEMGET* partySend = (MSG_SYN_PARTY_ITEMGET*)GetMsgRoot( &handle, partyUser->GetConnectionIdx(), NM_PARTY, NM_PARTY_ITEMGET_SYN );
						unsigned long		   length   = sizeof(MSG_SYN_PARTY_ITEMGET) - sizeof(partySend->ItemIndex);
						long&                  rowCount = partySend->RowCount;

						partySend->UserIndex = heroIdx;
						partySend->RowCount  = 0;

						for ( long i = 0; i < itemGet->rowCount; ++i )
						{
							if ( itemGet->table[i].retvalue == 0 )
							{
								table = &itemGet->table[i].table;

								partySend->ItemIndex[rowCount] = table->itemDefineIndex;
								rowCount++;
							}
						}

						length += (rowCount * sizeof(partySend->ItemIndex));
						SendMsgRoot( handle, length );
					}
				}
				
				clientInfo->inventoryDb.get = false;		// DB; 사용종료.
				clientInfo->request.inventoryUpdate = true; // 인벤토리 업데이트 요청.
			}
			break;
		case SQL_GAME_PROCESS_ITEM_GET_QUEST:
			{
				cCSLock           lock( &mCs );
				ClientInfo*       clientInfo   = (ClientInfo*)perSocketContext->buffer;
				cPlayer*          hero         = clientInfo->hero;
				unsigned long     heroIdx      = hero->GetObjectID( );

				ITEM_GET_QUEST*   itemGetQuest = (ITEM_GET_QUEST*)perIoContext->buffer;
				TB_INVENTORY*     inventory    = hero->XCopyInventory( &itemGetQuest->table ); // (*inventory) = (*table);

				PostInventoryEvent( itemGetQuest->newOrUpdate ? EVENT_INVENTORY_UPDATE : EVENT_INVENTORY_CREATE, heroIdx, inventory, "GET_QUEST" );

				HANDLE                  handle     = NULL;
				MSG_RES_ITEM_GET_QUEST* sendMsg    = (MSG_RES_ITEM_GET_QUEST*)GetMsgRoot( &handle, perSocketContext, NM_ITEM, NM_ITEM_GET_QUEST_RES );
				sInventory*             sinventory = &sendMsg->Inventory;

				sendMsg->ErrorCode = ERROR_ITEM_GET_QUEST_SUCCESS;
				Inventory2sInventory( sinventory, inventory );

				SendMsgRoot( handle, sizeof(MSG_RES_ITEM_GET_QUEST) );

				clientInfo->request.inventoryUpdate = true;
			}
			break;
		case SQL_GAME_PROCESS_ITEM_USE:
			{
				ITEM_USE* itemUse = (ITEM_USE*)perIoContext->buffer;

				for ( long i = 0; i < itemUse->rowCount; i++ )
				{
					if ( itemUse->table[ i ].retvalue == 0 )
					{
						TB_INVENTORY* table = &itemUse->table[ i ].table;
						PostInventoryEvent( table->apply == InventoryApplyRemoved ? EVENT_INVENTORY_DELETE : EVENT_INVENTORY_UPDATE, itemUse->characterIdx, table, "USE - SKILL" );
					}
					else
					{
						PostServerEvent( "경고! DB와 서버간의 동기화 오류 - SQL_GAME_PROCESS_ITEM_USE" );
					}
				}
			}
			break;
		case SQL_GAME_PROCESS_ITEM_EXCHANGE:
			{
				cCSLock        lock( &mCs );
				ITEM_EXCHANGE* itemExchange = (ITEM_EXCHANGE*)perIoContext->buffer;

				for ( int i0 = 0; i0 < itemExchange->rowCount; i0++ )
				{
					cPlayer* player = mpObjectManager->GetPlayer( itemExchange->table[ i0 ].characterIdx );

					// 거래금액
					if ( itemExchange->table[ i0 ].characterMoney != 0 )
					{
						PostMoneyEvent( itemExchange->table[ i0 ].characterIdx,
										itemExchange->table[ i0 ].characterBefor,
										itemExchange->table[ i0 ].characterAfter,
										"ITEM_EXCHANGE::Character(=%d):Money(=%d)",
										itemExchange->table[ i0 ].characterFrom,
										itemExchange->table[ i0 ].characterMoney );
					}

					// 거래완료.
					if ( player != NULL )
					{
						player->ExchangeEnd( itemExchange->table[ i0 ].table, itemExchange->table[ i0 ].rowCount );
						player->CalcItemWeight( );
					}
					else
						PostServerEvent( "CriticalError SQL_GAME_PROCESS_ITEM_EXCHANGE CharacterIDx(=%d) is NULL.", itemExchange->table[ i0 ].characterIdx );

					// Log - 거래아이템
					TB_INVENTORY* table = itemExchange->table[ i0 ].table;
					for ( int i1 = 0; i1 < itemExchange->table[ i0 ].rowCount; i1++, table++ )
					{
						PostInventoryEvent( EVENT_INVENTORY_UPDATE, itemExchange->table[ i0 ].characterIdx, table, "ITEM_EXCHANGE" );
					}
				}
			}
			break;
		case SQL_GAME_PROCESS_ITEM_MIX:
			{
				cCSLock lock( &mCs );

				ClientInfo*   clientInfo = (ClientInfo*)perSocketContext->buffer;
				cPlayer*      hero       = clientInfo->hero;
				unsigned long heroIdx    = hero->GetObjectID( );

				ITEM_MIX*     itemMix = (ITEM_MIX*)perIoContext->buffer;
				TB_INVENTORY* table   = itemMix->table;

				HANDLE              handle     = NULL;
				MSG_RES_ITEM_MIXED* sendMsg    = (MSG_RES_ITEM_MIXED*)GetMsgRoot( &handle, perSocketContext, NM_ITEM, NM_ITEM_MIXED_RES );
				sInventory*         sinventory = sendMsg->inventory;
				TB_INVENTORY*       inventory;

				sendMsg->rowCount = 0;
				for ( long i = 0; i < itemMix->rowCount; i++, table++ )
				{
					if ( table->apply == InventoryApplyRemoved )
					{
						PostInventoryEvent( EVENT_INVENTORY_DELETE, heroIdx, table, "ITEM_MIX" );
					}
					else
					{
						inventory = hero->SelectInventory( table->number );

						if ( inventory->idx == LONG_MAX )
							PostInventoryEvent( EVENT_INVENTORY_CREATE, heroIdx, table, "ITEM_MIX" );
						else
							PostInventoryEvent( EVENT_INVENTORY_UPDATE, heroIdx, table, "ITEM_MIX" );

						hero->XCopyInventory( table ); // (*inventory) = (*table);
					}

					Inventory2sInventory( sinventory, table );
					sinventory++;

					sendMsg->rowCount++;
				}

				SendMsgRoot( handle, sendMsg->GetMsgLength( ) );

				/// 쿨타임 갱신
				hero->ItemMixCoolTime( );

				/// 성공 메세지 발송
				ResMsgError( perSocketContext, NM_MAKESKILL, NM_MAKESKILL_ITEMMIX_END_RES, ERROR_MAKESKILL_END_SUCCESS );

				/// 성공 메세지 전체에 발송
				MSG_SYN_MAKESKILL_ITEMMIX_END synMsg;
				synMsg.Category = NM_MAKESKILL;
				synMsg.Protocol = NM_MAKESKILL_ITEMMIX_END_SYN;
				synMsg.mCharacterIdx = heroIdx;
				synMsg.ErrorCode = ERROR_MAKESKILL_END_SUCCESS;
				NETWORK2->QuickSendExcept( hero, (char*)&synMsg, sizeof(MSG_SYN_MAKESKILL_ITEMMIX_END) );

				/// 상태 풀어줌
				if ( hero->GetState( ) == eOBJECT_STATE_STOP && hero->GetStateStop( ) == eSTOP_ITEMMIX )
				{
					hero->ChangeState( eOBJECT_STATE_IDLE );
					hero->SetStateStop( eSTOP_NONE );
				}

				/// db 처리부 종료
				clientInfo->inventoryDb.itemMix = false;
				clientInfo->request.inventoryUpdate = true; // 인벤토리 업데이트 요청.
			}
			break;

		case SQL_GAME_PROCESS_ITEM_SPREAD_VALUE:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock            lock( &mCs );
				ClientInfo*        clientInfo      = (ClientInfo*)perSocketContext->buffer;
				cPlayer*           hero            = clientInfo->hero;
				ITEM_SPREAD_VALUE* itemSpreadValue = (ITEM_SPREAD_VALUE*)perIoContext->buffer;

				if ( clientInfo->commonDb.spreadValue )
				{
					HANDLE                       handle  = NULL;
					MSG_RES_TAROT_SEEKER_RESULT* sendMsg = (MSG_RES_TAROT_SEEKER_RESULT*)GetMsgRoot( &handle, perSocketContext, NM_TAROT, NM_TAROT_SEEKER_RESULT_RES );

					sendMsg->ErrorCode   = ERROR_TAROT_SEEKER_RESULT_SUCCESS;
					sendMsg->SpreadIndex = itemSpreadValue->itemDefineIndex;
					for ( long i = 0; i < 10; i++ )
					{
						sendMsg->Result[ i ].Dir            = (TAROT_SEEKER_RESULT_DIRECTION)(itemSpreadValue->values[ i ].dir - 1);
						sendMsg->Result[ i ].TarotCardIndex = itemSpreadValue->values[ i ].itemDefineIndex;
						sendMsg->Result[ i ].Value          = itemSpreadValue->values[ i ].spread;
					}
					sendMsg->ResultValue = itemSpreadValue->spreadValue;

					SendMsgRoot( handle, sizeof(MSG_RES_TAROT_SEEKER_RESULT) );

					hero->SetTarotResultValue( itemSpreadValue->spreadValue );

					clientInfo->commonDb.spreadValue = false;
				}
			}
			break;

		case SQL_GAME_PROCESS_ITEM_TAROT_RESULT:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock     lock( &mCs );
				ClientInfo* clientInfo = (ClientInfo*)perSocketContext->buffer;

				if ( clientInfo->commonDb.tarotResult )
				{
					ITEM_TAROT_RESULT* itemTarotResult = (ITEM_TAROT_RESULT*)perIoContext->buffer;
					cPlayer*           hero            = clientInfo->hero;
					sObject            heroObject      = hero->GetObject( );

					// influence_idx 가 '0'이 아닐경우 시킬의 효과 호출.
					if ( itemTarotResult->influence_idx1 > 0 )
					{
						bool retvalue = mpSkillManager->AddInfluence( heroObject, heroObject, itemTarotResult->influence_idx1, 0, true );
						if ( retvalue == false )
							PostServerEvent( "Error(%d): mpSkillManager->AddItemInfluence", itemTarotResult->influence_idx1 );
					}
					if ( itemTarotResult->influence_idx2 > 0 )
					{
						bool retvalue = mpSkillManager->AddInfluence( heroObject, heroObject, itemTarotResult->influence_idx2, 0, true );
						if ( retvalue == false )
							PostServerEvent( "Error(%d): mpSkillManager->AddItemInfluence", itemTarotResult->influence_idx2 );
					}
					if ( itemTarotResult->influence_idx3 > 0 )
					{
						bool retvalue = mpSkillManager->AddInfluence( heroObject, heroObject, itemTarotResult->influence_idx3, 0, true );
						if ( retvalue == false )
							PostServerEvent( "Error(%d): mpSkillManager->AddItemInfluence", itemTarotResult->influence_idx3 );
					}

					// 성공메시지.
					ResMsgError( perSocketContext, NM_TAROT, NM_TAROT_SEEKER_ACCEPT_RES, ERROR_TAROT_SEEKER_ACCEPT_SUCCESS );
					clientInfo->commonDb.tarotResult = false;   // DB종료.
				}
			}
			break;

		case SQL_GAME_PROCESS_STALL_SELL_SEARCH:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock	                        lock( &mCs );
				ClientInfo*                     clientInfo      = (ClientInfo*)perSocketContext->buffer;

				if ( clientInfo->commonDb.stallSellSearch )
				{
					STALL_SELL_SEARCH*              stallSellSearch = (STALL_SELL_SEARCH*)perIoContext->buffer;

					HANDLE                          handle  = NULL;
					MSG_RES_ITEM_STALL_SELL_SEARCH* sendMsg = (MSG_RES_ITEM_STALL_SELL_SEARCH*)GetMsgRoot( &handle, perSocketContext, NM_ITEM, NM_ITEM_STALL_SELL_SEARCH_RES );
					unsigned long                   length  = sizeof(MSG_RES_ITEM_STALL_SELL_SEARCH) - sizeof(sendMsg->table);
					cPlayer*                        player  = NULL;

					sendMsg->ErrorCode = ERROR_ITEM_STALL_SELL_SEARCH_SUCCESS;

					for ( int i = 0; i < stallSellSearch->rowCount; i++ )
					{
						player = mpObjectManager->GetPlayer( stallSellSearch->table[ i ].characterIdx );
						if ( player != NULL )
						{
							sendMsg->table[ i ].idx   = stallSellSearch->table[ i ].idx;
							wcscpy( sendMsg->table[ i ].characterName, player->GetName( ) );
							sendMsg->table[ i ].price = stallSellSearch->table[ i ].price;
							sendMsg->table[ i ].mapNumber = player->GetMapNumber( );
							sendMsg->table[ i ].xPos = player->GetXPos( );
							sendMsg->table[ i ].yPos = player->GetYPos( );

							sendMsg->rowCount++;
						}
					}

					length += (sendMsg->rowCount * sizeof(sendMsg->table));
					SendMsgRoot( handle, length );

					clientInfo->commonDb.stallSellSearch = false;	// DB; 사용종료.
				}
			}
			break;
		case SQL_GAME_PROCESS_STALL_SELL_INSERT:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock     lock( &mCs );
				ClientInfo* clientInfo = (ClientInfo*)perSocketContext->buffer;

				if ( !clientInfo->complete.stallSell )
				{
					clientInfo->complete.stallSell = true;			// Database에 노점 등록완료.
				}
			}
			break;
		case SQL_GAME_PROCESS_STALL_SELL_REMOVE:
			break;
		case SQL_GAME_PROCESS_STALL_SELL_CLEAR:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock     lock( &mCs );
				ClientInfo* clientInfo = (ClientInfo*)perSocketContext->buffer;

				if ( clientInfo->commonDb.stallSellClear )
				{
					clientInfo->complete.stallSell = false;			// Database에 노점 삭제완료.
					clientInfo->commonDb.stallSellClear = false;	// DB; 사용종료.
				}
			}
			break;
		case SQL_GAME_PROCESS_STALL_SELL_GET:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock     lock( &mCs );
				ClientInfo* clientInfo = (ClientInfo*)perSocketContext->buffer;
				cPlayer*    hero       = clientInfo->hero;

				if ( clientInfo->inventoryDb.stallSellGet )
				{
					STALL_SELL_GET* stallSellGet = (STALL_SELL_GET*)perIoContext->buffer;
					TB_INVENTORY*   inventory;

					unsigned long befor  = hero->GetMoney( );
					long          result = hero->AddMoney( stallSellGet->fObject, stallSellGet->tCharacterMoney, false );
					unsigned long after  = hero->GetMoney( );

					PostMoneyEvent( hero->GetObjectID( ), befor, after, "STALL_SELL_GET(BUY)::Character(=%d):Money(=%d)", stallSellGet->fCharacterIdx, stallSellGet->tCharacterMoney );

					if ( result != stallSellGet->tCharacterMoney )
						PostServerEvent( "Error - SQL_GAME_PROCESS_STALL_SELL_GET AddMoney(=%d) != ResultMoney(=%d).", stallSellGet->tCharacterMoney, result );

					HANDLE                       handle     = NULL;
					MSG_RES_ITEM_STALL_SELL_GET* sendMsg    = (MSG_RES_ITEM_STALL_SELL_GET*)GetMsgRoot( &handle, perSocketContext, NM_ITEM, NM_ITEM_STALL_SELL_GET_RES );

					sendMsg->ErrorCode = ERROR_ITEM_STALL_SELL_GET_SUCCESS;

					inventory = hero->XCopyInventory( &stallSellGet->tInventory );

					Inventory2sInventory( &sendMsg->Inventory, inventory );
					SendMsgRoot( handle, sizeof(MSG_RES_ITEM_STALL_SELL_GET) );

					PostInventoryEvent( EVENT_INVENTORY_CREATE, hero->GetObjectID( ), inventory, "STALL SELL - BUY" );

					clientInfo->inventoryDb.stallSellGet = false;	// DB; 사용종료.
					clientInfo->request.inventoryUpdate = true;     // 인벤토리 업데이트 요청.
				}
			}
			break;
		case SQL_GAME_PROCESS_NOTE_SELECT:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock      lock( &mCs );
				ClientInfo*  clientInfo = (ClientInfo*)perSocketContext->buffer;
				cPlayer*     hero       = clientInfo->hero;
				unsigned long heroIdx   = hero->GetObjectID();

				if( clientInfo->gameInDb.noteList == true )
				{
					NOTE_SELECT* noteSelect = (NOTE_SELECT*)perIoContext->buffer;
					TB_NOTE*     table      = noteSelect->table;

					/// 저장공간 생성
					if( mpNoteManager->AddPlayerNoteMap( heroIdx ) == true )
					{
						// 쪽지저장
						for ( long i = 0; i < noteSelect->rowCount; i++, table++ )
						{
							if( mpNoteManager->AddNote( heroIdx, table ) == false )
							{
								PostServerEvent("1: SQL_GAME_PROCESS_NOTE_SELECT AddNote[%d, %d]", hero->GetObjectID(), table->idx );
								assert(0);
							}
						}
					}
					else
					{
						// 등록 오류시, 클라이언트 강제 종료.
						PostServerEvent("2: SQL_GAME_PROCESS_NOTE_SELECT AddNote[%d, %d]", hero->GetObjectID() );
						CloseCID( perSocketContext->cid );
					}

					clientInfo->complete.noteSelect = true;
					clientInfo->gameInDb.noteList = false;
				}



				//NOTE_SELECT* noteSelect = (NOTE_SELECT*)perIoContext->buffer;
				//TB_NOTE*     table      = noteSelect->table;

				//// 쪽지전송.
				//HANDLE             handle  = NULL;
				//MSG_RES_NOTE_RECV* sendMsg = (MSG_RES_NOTE_RECV*)GetMsgRoot( &handle, perSocketContext );
				//unsigned long      length  = sizeof(MSG_RES_NOTE_RECV) - sizeof(sendMsg->table);

				//sendMsg->Category = NM_CHAT;
				//sendMsg->Protocol = NM_CHAT_NOTE_RECV_RES;
				//sendMsg->RowCount = 0;

				//for ( long i = 0; i < noteSelect->rowCount; i++, table  )
				//{
				//	sendMsg->table[ i ].Idx    = table->idx;
				//	sendMsg->table[ i ].Year   = table->registerDate.year;
				//	sendMsg->table[ i ].Month  = table->registerDate.month;
				//	sendMsg->table[ i ].Day    = table->registerDate.day;
				//	sendMsg->table[ i ].Hour   = table->registerDate.hour;
				//	sendMsg->table[ i ].Minute = table->registerDate.minute;
				//	wcscpy( sendMsg->table[ i ].CharacterName, table->characterName );
				//	wcscpy( sendMsg->table[ i ].Message, table->message );
				//	sendMsg->table[ i ].RemainDay = (BYTE)table->remainDay;
				//	sendMsg->RowCount++;
				//}

				//length += (sendMsg->RowCount * sizeof(sendMsg->table));
				//SendMsgRoot( handle, length );

				//clientInfo->complete.noteSelect = true;
			}
			break;
		case SQL_GAME_PROCESS_NOTE_INSERT:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock      lock( &mCs );
				ClientInfo*  clientInfo = (ClientInfo*)perSocketContext->buffer;

				if( clientInfo->commonDb.noteInsert == true )
				{
					NOTE_INSERT* noteInsert = (NOTE_INSERT*)perIoContext->buffer;

					/// 쪽지 보낸 결과 알려주기
					HANDLE             handle	= NULL;
					MSG_RES_NOTE_SEND* sendMsg	= (MSG_RES_NOTE_SEND*)GetMsgRoot( &handle, perSocketContext, NM_CHAT, NM_CHAT_NOTE_SEND_RES );
					sendMsg->ErrorCode			= noteInsert->retvalue;
					SendMsgRoot( handle, sizeof(MSG_RES_NOTE_SEND) );

					if( noteInsert->retvalue == ERROR_CHAT_NOTE_SUCCESS )
					{
						TB_NOTE* note = &noteInsert->table;

						/// 상대방이 접속중이면 
						cPlayer* player = mpObjectManager->GetPlayer( noteInsert->characterIdx2 );
						if( player != NULL )
						{
							if( mpNoteManager->AddNote( noteInsert->characterIdx2, note ) == true )
							{
								/// 추가된 내용 쏴주기
								HANDLE				handle = NULL;
								MSG_SYN_NOTE_SEND* sendMsg = (MSG_SYN_NOTE_SEND*)GetMsgRoot( &handle, player->GetConnectionIdx(), NM_CHAT, NM_CHAT_NOTE_SEND_SYN );
								if( sendMsg != NULL )
								{
									sendMsg->table.Idx    = note->idx;
									sendMsg->table.Year   = note->registerDate.year;
									sendMsg->table.Month  = note->registerDate.month;
									sendMsg->table.Day    = note->registerDate.day;
									sendMsg->table.Hour   = note->registerDate.hour;
									sendMsg->table.Minute = note->registerDate.minute;
									wcscpy( sendMsg->table.CharacterName, note->characterName );
									wcscpy( sendMsg->table.Message, note->message );
									sendMsg->table.RemainTime = note->remainTime;

									SendMsgRoot( handle, sizeof(MSG_SYN_NOTE_SEND) );
								}
							}
							else
							{
								// 등록 오류시, 클라이언트 강제 종료.
								PostServerEvent( "SQL_GAME_PROCESS_NOTE_INSERT - CHARACTER_IDX(%d, %d)", noteInsert->characterIdx2, noteInsert->table.idx );
								CloseCID( perSocketContext->cid );
							}
						}
						else
						{
							if( !clientInfo->commonDb.memberSelect )
							{
								/// 내 채널 접속중이 아니면, DB 검색
								clientInfo->commonDb.memberSelect = NoteMemberSelect( perSocketContext, noteInsert->characterName2, note );
							}
						}
					}
				
					clientInfo->commonDb.noteInsert = false;
				}

				//if( clientInfo->commonDb.noteInsert == true )
				//{
				//	NOTE_INSERT* noteInsert = (NOTE_INSERT*)perIoContext->buffer;

				//	HANDLE             handle	= NULL;
				//	MSG_RES_NOTE_SEND* sendMsg	= (MSG_RES_NOTE_SEND*)GetMsgRoot( &handle, perSocketContext, NM_CHAT, NM_CHAT_NOTE_SEND_RES );
				//	sendMsg->ErrorCode			= noteInsert->retvalue;
				//	SendMsgRoot( handle, sizeof(MSG_RES_NOTE_SEND) );

				//	/// 성공시 쪽지 리스트에 추가
				//	if( noteInsert->retvalue == 0 )
				//	{
				//		TB_NOTE* note = &noteInsert->table;

				//		cPlayer* player = mpObjectManager->GetPlayer( noteInsert->characterIdx2 );
				//		if( player != NULL )
				//		{
				//			if( mpNoteManager->AddNote( noteInsert->characterIdx2, note ) == true )
				//			{
				//				/// 추가된 내용 쏴주기
				//				HANDLE				handle = NULL;
				//				MSG_SYN_NOTE_SEND* sendMsg = (MSG_SYN_NOTE_SEND*)GetMsgRoot( &handle, player->GetConnectionIdx(), NM_CHAT, NM_CHAT_NOTE_SEND_SYN );
				//				if( sendMsg != NULL )
				//				{
				//					sendMsg->table.Idx    = note->idx;
				//					sendMsg->table.Year   = note->registerDate.year;
				//					sendMsg->table.Month  = note->registerDate.month;
				//					sendMsg->table.Day    = note->registerDate.day;
				//					sendMsg->table.Hour   = note->registerDate.hour;
				//					sendMsg->table.Minute = note->registerDate.minute;
				//					wcscpy( sendMsg->table.CharacterName, note->characterName );
				//					wcscpy( sendMsg->table.Message, note->message );
				//					sendMsg->table.RemainTime = note->remainTime;

				//					SendMsgRoot( handle, sizeof(MSG_SYN_NOTE_SEND) );
				//				}
				//			}
				//			else
				//			{
				//				// 등록 오류시, 클라이언트 강제 종료.
				//				PostServerEvent( "SQL_GAME_PROCESS_NOTE_INSERT - CHARACTER_IDX(%d, %d)", noteInsert->characterIdx2, noteInsert->table.idx );
				//				CloseCID( perSocketContext->cid );
				//			}
				//		}
				//	}
				//	
				//	clientInfo->commonDb.noteInsert = false;
				//}
							

				//HANDLE             handle  = NULL;
				//MSG_RES_NOTE_SEND* sendMsg = (MSG_RES_NOTE_SEND*)GetMsgRoot( &handle, perSocketContext, NM_CHAT, NM_CHAT_NOTE_SEND_RES );

				//if ( sendMsg != NULL )
				//{
				//	sendMsg->ErrorCode = noteInsert->retvalue;
				//	SendMsgRoot( handle, sizeof(MSG_RES_NOTE_SEND) );
				//}

				//// 성공일경우, 받는 캐릭터의 쪽지 갱신.
				//if ( noteInsert->retvalue == 0 )
				//{
				//	cPlayer* player = mpObjectManager->GetPlayer( noteInsert->characterIdx2 );
				//	if ( player != NULL )
				//	{
				//		PerSocketContext* socketContext = mSocketContextPool->GetCID( player->GetConnectionIdx( ) );
				//		if ( socketContext != NULL )
				//		{
				//			NoteSelect( socketContext, noteInsert->characterIdx2 );
				//		}
				//	}
				//}

				clientInfo->commonDb.noteInsert = false;	// DB; 사용종료.
			}
			break;
		case SQL_GAME_PROCESS_NOTE_DELETE:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock      lock( &mCs );
				ClientInfo*  clientInfo = (ClientInfo*)perSocketContext->buffer;
				cPlayer*     hero       = clientInfo->hero;

				if( clientInfo->commonDb.noteDelete == true )
				{
					NOTE_DELETE* noteDelete = (NOTE_DELETE*)perIoContext->buffer;

					for( int i = 0; i < noteDelete->rowCount; ++i )
					{
						/// 데이터 삭제
						if( mpNoteManager->DeleteNote( hero->GetObjectID(), noteDelete->idx[i] ) == false )
						{
							// 등록 오류시, 클라이언트 강제 종료.
							PostServerEvent( "SQL_GAME_PROCESS_NOTE_DELETE - CHARACTER_IDX(%d)", hero->GetObjectID() );
							CloseCID( perSocketContext->cid );
						}
					}

					/// 삭제 결과 보냄
					HANDLE            handle  = NULL;
					MSG_RES_NOTE_DEL* sendMsg = (MSG_RES_NOTE_DEL*)GetMsgRoot( &handle, perSocketContext, NM_CHAT, NM_CHAT_NOTE_DEL_RES );
					if ( sendMsg != NULL )
					{
						sendMsg->ErrorCode = 0;
						SendMsgRoot( handle, sizeof(MSG_RES_NOTE_DEL) );
					}

					/// 리스트 갱신
					mpNoteManager->SendNoteList( hero );

					clientInfo->commonDb.noteDelete = false;				// DB; 사용종료.
				}
				
				//HANDLE            handle  = NULL;
				//MSG_RES_NOTE_DEL* sendMsg = (MSG_RES_NOTE_DEL*)GetMsgRoot( &handle, perSocketContext, NM_CHAT, NM_CHAT_NOTE_DEL_RES );

				//if ( sendMsg != NULL )
				//{
				//	sendMsg->ErrorCode = 0;
				//	SendMsgRoot( handle, sizeof(MSG_RES_NOTE_DEL) );
				//}

				//NoteSelect( perSocketContext, hero->GetObjectID( ) );	// 쪽지 리스트 검색 후 전송.

				//clientInfo->commonDb.noteDelete = false;				// DB; 사용종료.
			}
			break;
		case SQL_GAME_PROCESS_GUILDNOTE_INSERT:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				/// 길드쪽지 삽입
				cCSLock      lock( &mCs );
				ClientInfo*  clientInfo = (ClientInfo*)perSocketContext->buffer;
				cPlayer*     hero       = clientInfo->hero;
				unsigned long heroIdx   = hero->GetObjectID();
				unsigned long guildIdx  = hero->GetGuildIndex();

				if( clientInfo->commonDb.noteInsert == true )
				{
					GUILDNOTE_INSERT* guildNoteInsert = (GUILDNOTE_INSERT*)perIoContext->buffer;

					if( perIoContext->requestResult == IOCP_REQUEST_SUCCESS )
					{
						/// 성공 보내기
						HANDLE             handle	= NULL;
						MSG_RES_NOTE_SEND* sendMsg	= (MSG_RES_NOTE_SEND*)GetMsgRoot( &handle, perSocketContext, NM_CHAT, NM_CHAT_GUILDNOTE_SEND_RES );
						sendMsg->ErrorCode			= 0;
						SendMsgRoot( handle, sizeof(MSG_RES_NOTE_SEND) );

						/// 쪽지 삽입
						for( long i = 0; i < guildNoteInsert->rowCount; ++i )
						{
							unsigned long playerIdx = guildNoteInsert->characterIdx2[i];
							TB_NOTE* note = &guildNoteInsert->table[i];
							if( note->retValue == 0 )
							{
								cPlayer* player = mpObjectManager->GetPlayer( playerIdx );
								if( player != NULL )
								{
									if( mpNoteManager->AddNote( playerIdx, note ) == true )
									{
										/// 추가된 내용 쏴주기
										HANDLE				handle = NULL;
										MSG_SYN_NOTE_SEND* sendMsg = (MSG_SYN_NOTE_SEND*)GetMsgRoot( &handle, player->GetConnectionIdx(), NM_CHAT, NM_CHAT_NOTE_SEND_SYN );
										if( sendMsg != NULL )
										{
											sendMsg->table.Idx    = note->idx;
											sendMsg->table.Year   = note->registerDate.year;
											sendMsg->table.Month  = note->registerDate.month;
											sendMsg->table.Day    = note->registerDate.day;
											sendMsg->table.Hour   = note->registerDate.hour;
											sendMsg->table.Minute = note->registerDate.minute;
											wcscpy( sendMsg->table.CharacterName, note->characterName );
											wcscpy( sendMsg->table.Message, note->message );
											sendMsg->table.RemainTime = note->remainTime;

											SendMsgRoot( handle, sizeof(MSG_SYN_NOTE_SEND) );
										}
									}
									else
									{
										// 등록 오류시, 클라이언트 강제 종료.
										PostServerEvent( "SQL_GAME_PROCESS_GUILDNOTE_INSERT - NOT FIND GUILD - CHARACTER_IDX(%d), GUILD_IDX[(%d)", heroIdx, guildIdx );
										CloseCID( perSocketContext->cid );
									}
								}
							}

						}

						/// SYN : 길드 쪽지 전송
						if( PostGuildNote( guildNoteInsert ) == false )
						{
							PostGuildEvent( heroIdx, guildIdx, eGUILD_NONE, "FAIL - SQL_GAME_PROCESS_GUILDNOTE_INSERT PostGuildNote()" );
						}
					}
					else
					{
						// 등록 오류시, 클라이언트 강제 종료.
						PostServerEvent( "WARNING - SQL_GAME_PROCESS_GUILDNOTE_INSERT - CHARACTER_IDX(%d), GUILD_IDX[(%d)", heroIdx, guildIdx );
						CloseCID( perSocketContext->cid );
					}
					
					clientInfo->commonDb.noteInsert = false;
				}
			}
			break;
		case SQL_GAME_PROCESS_FRIEND_SELECT:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock     lock( &mCs );
				ClientInfo* clientInfo = (ClientInfo*)perSocketContext->buffer;

				if ( clientInfo->complete.characterSelect )
				{
					FRIEND_SELECT* friendSelect = (FRIEND_SELECT*)perIoContext->buffer;
					TB_FRIEND*     friends      = NULL;
					TB_FRIEND*     table        = NULL;

					cPlayer* hero = clientInfo->hero;

					hero->ClearFriends( );
					friends = hero->SelectFriend( );
					table   = friendSelect->table;

					// 기본정보.
					for ( long i = 0; i < friendSelect->rowCount; i++, friends++, table++ )
						(*friends) = (*table);

					hero->UpdateFriend( );
					hero->SendFriendList( (ULONG_PTR)perSocketContext );
					clientInfo->complete.friendSelect = true;
				}
			}
			break;
		case SQL_GAME_PROCESS_FRIEND_INSERT:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock        lock( &mCs );
				ClientInfo*    clientInfo   = (ClientInfo*)perSocketContext->buffer;

				if ( clientInfo->commonDb.friendInsert )
				{
					cPlayer*       hero         = clientInfo->hero;
					FRIEND_INSERT* friendInsert = (FRIEND_INSERT*)perIoContext->buffer;

					HANDLE              handle  = NULL;
					MSG_RES_FRIEND_ADD* sendMsg = (MSG_RES_FRIEND_ADD*)GetMsgRoot( &handle, perSocketContext, NM_CHAT, NM_CHAT_FRIEND_ADD_RES );

					if ( sendMsg != NULL )
					{
						sendMsg->ErrorCode = friendInsert->retvalue;
						SendMsgRoot( handle, sizeof(MSG_RES_FRIEND_ADD) );
					}
					if ( friendInsert->retvalue == 0 )
					{
						// 자신의 친구리스트 갱신
						FriendSelect( perSocketContext, hero->GetObjectID( ) );

						// 친구의 친구리스트 갱신
						cPlayer*          player        = mpObjectManager->GetPlayer( friendInsert->characterIdx2 );
						PerSocketContext* socketContext = NULL;
						if ( player != NULL )
						{
							socketContext = mSocketContextPool->GetCID( player->GetConnectionIdx( ) );
							if ( socketContext != NULL )
								FriendSelect( socketContext, friendInsert->characterIdx2 );
						}
					}

					clientInfo->commonDb.friendInsert = false; // DB; 사용종료.
				}
			}
			break;
		case SQL_GAME_PROCESS_FRIEND_UPDATE:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock     lock( &mCs );
				ClientInfo* clientInfo = (ClientInfo*)perSocketContext->buffer;

				if ( clientInfo->commonDb.friendUpdate )
				{
					cPlayer*       hero         = clientInfo->hero;
					FRIEND_UPDATE* friendUpdate = (FRIEND_UPDATE*)perIoContext->buffer;

					// 자신의 친구리스트 갱신
					FriendSelect( perSocketContext, hero->GetObjectID( ) );

					// 친구의 친구리스트 갱신
					cPlayer* player = mpObjectManager->GetPlayer( friendUpdate->characterIdx2 );
					if ( player != NULL )
					{
						PerSocketContext* socketContext = mSocketContextPool->GetCID( player->GetConnectionIdx( ) );
						if ( socketContext != NULL )
						{
							FriendSelect( socketContext, friendUpdate->characterIdx2 );
						}
					}

					clientInfo->commonDb.friendUpdate = false; // DB; 사용종료.
				}
			}
			break;
		case SQL_GAME_PROCESS_FRIEND_DELETE:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock     lock( &mCs );
				ClientInfo* clientInfo = (ClientInfo*)perSocketContext->buffer;

				if ( clientInfo->commonDb.friendDelete )
				{
					cPlayer*       hero         = clientInfo->hero;
					FRIEND_DELETE* friendDelete = (FRIEND_DELETE*)perIoContext->buffer;

					// 자신의 친구리스트 갱신
					FriendSelect( perSocketContext, hero->GetObjectID( ) );

					// 친구의 친구리스트 갱신
					cPlayer* player = mpObjectManager->GetPlayer( friendDelete->characterIdx2 );
					if ( player != NULL )
					{
						PerSocketContext* socketContext = mSocketContextPool->GetCID( player->GetConnectionIdx( ) );
						if ( socketContext != NULL )
						{
							FriendSelect( socketContext, friendDelete->characterIdx2 );
						}
					}

					clientInfo->commonDb.friendDelete = false; // DB; 사용종료.
				}
			}
			break;
		case SQL_GAME_PROCESS_SKILL_SELECT:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock        lock( &mCs );
				ClientInfo*    clientInfo   = (ClientInfo*)perSocketContext->buffer;
				cPlayer*       hero         = clientInfo->hero;
				SKILL_SELECT* skillSelect = (SKILL_SELECT*)perIoContext->buffer;

				if ( SKILLMANAGER->RestorePlayerSkill( skillSelect ) )
				{
					clientInfo->gameInDb.skillSelect = false;	// DB 사용종료.
					clientInfo->complete.skillRelease = true;

					// 070828 PKH  스킬 정보 전송
					SKILLMANAGER->SendMsgSkillHaveToPlayer( perSocketContext->cid, hero->GetObjectID( ) );
					
				}
				else
					assert( NULL );
			}
			break;
		case SQL_GAME_PROCESS_SKILL_INSERT:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock        lock( &mCs );
				ClientInfo*    clientInfo   = (ClientInfo*)perSocketContext->buffer;
				cPlayer*       hero         = clientInfo->hero;
				SKILL_INSERT*  skillInsert  = (SKILL_INSERT*)perIoContext->buffer;

				HANDLE              handle  = NULL;
				if( skillInsert->mRetvalue == 0 )
				{
					MSG_RES_NPC_SKILL_ADD* sendMsg = (MSG_RES_NPC_SKILL_ADD*)GetMsgRoot( &handle, perSocketContext, NM_NPC, NM_NPC_SKILL_ADD_SUC_RES );

					if ( sendMsg != NULL )
					{
						sendMsg->mSkillClassIdx = skillInsert->mSkillClassIdx;
						SendMsgRoot( handle, sizeof(MSG_ERROR) );

						// 추가된 스킬이 패시브 스킬이면 스테이터스 재계산함
						if( SKILLSCRIPT->GetPlayerSkillInfo( skillInsert->mSkillClassIdx )->mType == eSKILLTYPE_PASSIVE )
						{
							STATUSCALC->CalcPlayerExtensionGlobal( hero->GetObject() );
						}
					}

					/// sp 정리 / 클라이언트에 전송
					if( hero )
					{
						hero->UpdateJobUsedSP();
						hero->SendSkillPoint();
					}
				}
				else if( skillInsert->mRetvalue == 3 )
				{
					MSG_RES_NPC_SKILL_ADD* sendMsg = (MSG_RES_NPC_SKILL_ADD*)GetMsgRoot( &handle, perSocketContext, NM_NPC, NM_NPC_SKILL_ADD_SUC_RES );

					if ( sendMsg != NULL )
					{
						sendMsg->mSkillClassIdx = skillInsert->mSkillClassIdx;
						SendMsgRoot( handle, sizeof(MSG_ERROR) );

						// 추가된 스킬이 패시브 스킬이면 스테이터스 재계산함
						if( SKILLSCRIPT->GetPlayerSkillInfo( skillInsert->mSkillClassIdx )->mType == eSKILLTYPE_PASSIVE )
						{
							STATUSCALC->CalcPlayerExtensionGlobal( hero->GetObject() );
						}
					}

					/// sp 정리 / 클라이언트에 전송
					if( hero )
					{
						hero->UpdateJobUsedSP();
						hero->SendSkillPoint();
					}

					assert(NULL);
					PostServerEvent( "SQL_GAME_PROCESS_SKILL_INSERT ERROR[%d][%d,%d,%d,%d]", skillInsert->mRetvalue,
						skillInsert->mCharacterIdx, skillInsert->mSkillClassIdx, skillInsert->mMoney, skillInsert->mSP );
				}
				else
				{
					MSG_ERROR* sendMsg = (MSG_ERROR*)GetMsgRoot( &handle, perSocketContext, NM_NPC, NM_NPC_SKILL_ADD_ERR_RES );

					sendMsg->ErrorCode = SKILL_ADD_ERR_DBERROR;
					SendMsgRoot( handle, sizeof(MSG_ERROR) );

					/// sp,money 복구 / 습득스킬 제거
					hero->SkillPointMinusRestore( skillInsert->mSP );

					sObject player = { hero->GetObjectType(), hero->GetObjectID() };
					if( hero->AddMoney( player, skillInsert->mMoney ) != (long)skillInsert->mMoney )
						PostServerEvent( "SQL_GAME_PROCESS_SKILL_INSERT[%d] AddMoney[%d]", hero->GetObjectID(), skillInsert->mMoney );

					if( hero->SkillPointMinusRestore( skillInsert->mSP ) == false )
						PostServerEvent( "SQL_GAME_PROCESS_SKILL_INSERT[%d] SkillPointMinusRestore[%d]", hero->GetObjectID(), skillInsert->mSP );

					SKILLMANAGER->RollBackPlayerHaveSkill( hero->GetObjectID(), skillInsert->mSkillClassIdx );

					assert(NULL);
					PostServerEvent( "SQL_GAME_PROCESS_SKILL_INSERT ERROR[%d][%d,%d,%d,%d]", skillInsert->mRetvalue,
						skillInsert->mCharacterIdx, skillInsert->mSkillClassIdx, skillInsert->mMoney, skillInsert->mSP );
				}

				clientInfo->skillDb.skillDbUse = false;	// DB; 사용종료.
			}
			break;
		case SQL_GAME_PROCESS_SKILL_UPDATE:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock        lock( &mCs );
				ClientInfo*    clientInfo   = (ClientInfo*)perSocketContext->buffer;
				cPlayer*       hero         = clientInfo->hero;
				SKILL_UPDATE*  skillUpdate  = (SKILL_UPDATE*)perIoContext->buffer;

				HANDLE              handle  = NULL;
				if( skillUpdate->mRetvalue == 0 )
				{
					MSG_RES_NPC_SKILL_ADD* sendMsg = (MSG_RES_NPC_SKILL_ADD*)GetMsgRoot( &handle, perSocketContext, NM_NPC, NM_NPC_SKILL_ADD_SUC_RES );

					if ( sendMsg != NULL )
					{
						sendMsg->mSkillClassIdx = skillUpdate->mSkillClassIdx;
						sendMsg->mSkillStep = skillUpdate->mSkillStep;

						SendMsgRoot( handle, sizeof(MSG_RES_NPC_SKILL_ADD) );

						// 변경된 스킬이 패시브 스킬이면 스테이터스 재계산함
						if( SKILLSCRIPT->GetPlayerSkillInfo( skillUpdate->mSkillClassIdx )->mType == eSKILLTYPE_PASSIVE )
						{
							STATUSCALC->CalcPlayerExtensionGlobal( hero->GetObject() );
						}
					}

					/// sp 정리 / 클라이언트에 전송
					hero->UpdateJobUsedSP();
					hero->SendSkillPoint();
				}				
				else if( skillUpdate->mRetvalue == 2 )
				{
					MSG_RES_NPC_SKILL_ADD* sendMsg = (MSG_RES_NPC_SKILL_ADD*)GetMsgRoot( &handle, perSocketContext, NM_NPC, NM_NPC_SKILL_ADD_SUC_RES );

					if ( sendMsg != NULL )
					{
						sendMsg->mSkillClassIdx = skillUpdate->mSkillClassIdx;
						sendMsg->mSkillStep = skillUpdate->mSkillStep;

						SendMsgRoot( handle, sizeof(MSG_RES_NPC_SKILL_ADD) );

						// 변경된 스킬이 패시브 스킬이면 스테이터스 재계산함
						if( SKILLSCRIPT->GetPlayerSkillInfo( skillUpdate->mSkillClassIdx )->mType == eSKILLTYPE_PASSIVE )
						{
							STATUSCALC->CalcPlayerExtensionGlobal( hero->GetObject() );
						}
					}

					/// sp 정리 / 클라이언트에 전송
					hero->UpdateJobUsedSP();
					hero->SendSkillPoint();

					/// 에러 기록
					assert(NULL);
					PostServerEvent( "SQL_GAME_PROCESS_SKILL_UPDATE ERROR[%d][%d,%d,%d,%d,%d]", skillUpdate->mRetvalue,
						skillUpdate->mCharacterIdx, skillUpdate->mSkillClassIdx, skillUpdate->mSkillStep, skillUpdate->mMoney, skillUpdate->mSP );
				}
				else
				{
					MSG_ERROR* sendMsg = (MSG_ERROR*)GetMsgRoot( &handle, perSocketContext, NM_NPC, NM_NPC_SKILL_ADD_ERR_RES );

					sendMsg->ErrorCode = SKILL_ADD_ERR_DBERROR;
					SendMsgRoot( handle, sizeof(MSG_ERROR) );

					/// sp,money 복구 / 습득스킬 제거
					hero->SkillPointMinusRestore( skillUpdate->mSP );

					sObject player = { hero->GetObjectType(), hero->GetObjectID() };
					if( hero->AddMoney( player, skillUpdate->mMoney ) != (long)skillUpdate->mMoney )
						PostServerEvent( "SQL_GAME_PROCESS_SKILL_UPDATE[%d] AddMoney[%d]", hero->GetObjectID(), skillUpdate->mMoney );

					if( hero->SkillPointMinusRestore( skillUpdate->mSP ) == false )
						PostServerEvent( "SQL_GAME_PROCESS_SKILL_UPDATE[%d] SkillPointMinusRestore[%d]", hero->GetObjectID(), skillUpdate->mSP );

					SKILLMANAGER->RollBackPlayerHaveSkill( hero->GetObjectID(), skillUpdate->mSkillClassIdx );

					/// 에러 기록
					assert(NULL);
					PostServerEvent( "SQL_GAME_PROCESS_SKILL_UPDATE ERROR[%d][%d,%d,%d,%d,%d]", skillUpdate->mRetvalue,
						skillUpdate->mCharacterIdx, skillUpdate->mSkillClassIdx, skillUpdate->mSkillStep, skillUpdate->mMoney, skillUpdate->mSP );
				}

				clientInfo->skillDb.skillDbUse = false;	// DB 사용종료.
			}
			break;
		case SQL_GAME_PROCESS_SKILL_COOLTIME:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock        lock( &mCs );
				ClientInfo*    clientInfo   = (ClientInfo*)perSocketContext->buffer;
				SKILL_COOLTIME* skillCoolTime = (SKILL_COOLTIME*)perIoContext->buffer;

				if( skillCoolTime->mRetvalue != 0 )	
				{ 
					assert(NULL); 
					PostServerEvent( "SKILL COOLTIME ERROR - DB[%d]", skillCoolTime->mRetvalue );
				}

				clientInfo->complete.skillCoolTime = false;	// DB 사용종료.
			}
			break;
		case SQL_GAME_PROCESS_INFLUENCE_SELECT:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock        lock( &mCs );
				ClientInfo*    clientInfo   = (ClientInfo*)perSocketContext->buffer;
				cPlayer*       hero         = clientInfo->hero;
				SKILL_INFLUENCE_SELECT* influenceSelect = (SKILL_INFLUENCE_SELECT*)perIoContext->buffer;

				if( influenceSelect->mRowCount > 0 )
				{
					// 효과 정보 기록
					SKILLMANAGER->RestorePlayerInfluence( influenceSelect->mCharacterIdx, influenceSelect, influenceSelect->mRowCount );

					// 로드한 데이터 삭제
					SkillInfluenceDelete( perSocketContext, influenceSelect->mCharacterIdx );
					// DB 사용시작
					clientInfo->commonDb.skillInfluenceDelete = true;
				}

				clientInfo->gameInDb.skillInfluenceSelect = false;	// DB 사용종료.
				clientInfo->complete.influenceRelease = true;

				// 효과 리스트 발송
				SKILLMANAGER->SendMsgInfluenceToPlayer( perSocketContext->cid, hero->GetObjectID( ) );
			}
			break;
		case SQL_GAME_PROCESS_INFLUENCE_DELETE:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock        lock( &mCs );
				ClientInfo*    clientInfo   = (ClientInfo*)perSocketContext->buffer;
				SKILL_INFLUENCE_DELETE* influenceDelete = (SKILL_INFLUENCE_DELETE*)perIoContext->buffer;

				if ( influenceDelete->mRetvalue != 0 )	
				{ 
					assert(NULL); 
					PostServerEvent( "SKILL INFLUENCE DELETE ERROR - DB[%d]", influenceDelete->mRetvalue );
				}

				clientInfo->commonDb.skillInfluenceDelete = false;	// DB 사용종료.
			}
			break;
		case SQL_GAME_PROCESS_INFLUENCE_INSERT:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock        lock( &mCs );
				ClientInfo*    clientInfo   = (ClientInfo*)perSocketContext->buffer;
				SKILL_INFLUENCE_INSERT* influenceInsert = (SKILL_INFLUENCE_INSERT*)perIoContext->buffer;

				if( influenceInsert->mRetvalue != 0 )	
				{ 
					assert(NULL); 
					PostServerEvent( "SKILL INFLUENCE INSERT ERROR - DB[%d]", influenceInsert->mRetvalue );
				}

				clientInfo->complete.skillInfluenceInsert = false;	// DB 사용종료.
			}
			break;
		case SQL_GAME_PROCESS_SHORTCUT_SELECT:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock          lock( &mCs );
				ClientInfo*      clientInfo     = (ClientInfo*)perSocketContext->buffer;
				SHORTCUT_SELECT* shortcutSelect = (SHORTCUT_SELECT*)perIoContext->buffer;

				/// 캐릭터 첫 생성시는 데이터가 없다
				if( shortcutSelect->rowCount == 0 )
				{
					/// 퀵슬롯 기본 스킬로 등록 세팅
					sShortcut* data = SHORTCUTSCRIPT->GetShortCut();
					memcpy( clientInfo->shortcut.binary, data, sizeof(sShortcut) * MAX_SHORTCUT_SIZE );
				}
				else
				{
					memcpy( &clientInfo->shortcut, &shortcutSelect->table, sizeof(TB_SHORTCUT) );
				}
				

				clientInfo->gameInDb.shortcutSelect = false;	// DB 사용종료.
				clientInfo->complete.shortcut = true;

				clientInfo->request.shortcutSend = true;		// 단축창 발송 요청.
			}
			break;
		case SQL_GAME_PROCESS_SHORTCUT_UPDATE:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock     lock( &mCs );
				ClientInfo* clientInfo = (ClientInfo*)perSocketContext->buffer;

				clientInfo->commonDb.shortcutUpdate = false;	// DB 사용종료.
				clientInfo->complete.shortcut = false;
			}
			break;
		case SQL_GAME_PROCESS_QUEST_INSERT:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				/// 퀘스트 신규등록결과 RES
				cCSLock		lock( &mCs );
				ClientInfo*	clientInfo = (ClientInfo*)perSocketContext->buffer;

				/// 이미 소유하고 있는 퀘스트를 절대 또 소유할수 없다!
				if ( clientInfo->questDb.questInsert && clientInfo->inventoryDb.reward )
				{
					QUEST_INSERT* questInsert = (QUEST_INSERT*)perIoContext->buffer;

					if ( perIoContext->requestResult == IOCP_REQUEST_SUCCESS )
					{
						if( questInsert->retvalue == 0 )
						{
							cPlayer*           hero    = clientInfo->hero;
							unsigned long      heroIdx = hero->GetObjectID();
							TB_QUEST_PROGRESS* table   = &questInsert->quest;

							/// 1. 아이템 제공
							if ( questInsert->rowCount > 0 )
							{
								HANDLE               handle     = NULL;
								MSG_RES_ITEM_REWARD* sendMsg    = (MSG_RES_ITEM_REWARD*)GetMsgRoot( &handle, perSocketContext, NM_ITEM, NM_ITEM_REWARD_RES );
								sInventory*          sinventory = sendMsg->inventory;

								TB_INVENTORY*        table      = questInsert->inventory;
								TB_INVENTORY*        inventory;
								bool                 newOrUpdate;

								for ( long i = 0; i < questInsert->rowCount; i++, table++ )
								{
									inventory   = hero->SelectInventory( table->number );
									newOrUpdate = (inventory->idx == table->idx);
									inventory   = hero->XCopyInventory( inventory, table ); // (*inventory) = (*table);

									Inventory2sInventory( sinventory, inventory );
									sinventory++;

									sendMsg->rowCount++;

									PostInventoryEvent( newOrUpdate ? EVENT_INVENTORY_UPDATE : EVENT_INVENTORY_CREATE, heroIdx, inventory, "QUEST INSERT" );
								}

								SendMsgRoot( handle, sendMsg->GetMsgLength( ) );

								clientInfo->request.inventoryUpdate = true;
							}


							/// 등록 결과 send
							/// 2. hero 의 퀘스트 목록 갱신 및 추가된 내용만 발송
							HANDLE			   handle  = NULL;
							MSG_RES_QUEST_ADD* sendMsg = (MSG_RES_QUEST_ADD*)GetMsgRoot( &handle, perSocketContext, NM_NPC, NM_NPC_QUEST_ADD_RES );
							if( sendMsg != NULL )
							{
								sendMsg->ErrorCode = questInsert->retvalue;

								unsigned long questIdx = 0;
								int arrIdx = hero->FindEmptyQuest();

								if( arrIdx > -1 )
								{
									TB_QUEST_PROGRESS* heroQuest = hero->GetQuest( arrIdx );
									(*heroQuest) = (*table);

									/// 시작 시간체크 저장
									cQuestDefine* define = mpQuestManager->GetQuestDefine( heroQuest->questIdx );
									if( define && define->mQuestLimit && define->mQuestLimit->mTimeType != eTIME_NONE )
									{
										heroQuest->startTime = GetAccumTime();
									}

									/// 갱신된 목록 send 
									sendMsg->table.arrIdx	   = arrIdx;
									sendMsg->table.questIdx	   = heroQuest->questIdx;
									sendMsg->table.check	   = heroQuest->check;
									sendMsg->table.restTime	   = heroQuest->restTime;
									sendMsg->table.status	   = heroQuest->status;

									questIdx = heroQuest->questIdx;

									PostQuestEvent( EVENT_QUEST_ADD, hero->GetObjectID(), heroQuest->questIdx, "ADD" );
								}
								else
								{
									PostQuestEvent( EVENT_QUEST_ADD, hero->GetObjectID(), table->questIdx, "FAIL : quest list full" );
								}

								/// 결과 보내기
								SendMsgRoot( handle, sizeof(MSG_RES_QUEST_ADD) );

								/// npc 상태 변경
								hero->SendNpcQuestStatus();

								/// 3. 그외 처리 하기
								/// 제약 사항 걸기 ( Limit )
								hero->SendLimitPenalty( questIdx );
							}
						}
						else
						{
							/// 에러결과 보내주기
							HANDLE			   handle  = NULL;
							MSG_RES_QUEST_ADD* sendMsg = (MSG_RES_QUEST_ADD*)GetMsgRoot( &handle, perSocketContext, NM_NPC, NM_NPC_QUEST_ADD_RES );
							sendMsg->ErrorCode = questInsert->retvalue;
							SendMsgRoot( handle, sizeof(MSG_RES_QUEST_ADD) );
						}
					}
					else
					{
						// 등록 오류시, 클라이언트 강제 종료.
						PostServerEvent( "WARNING - SQL_GAME_PROCESS_QUEST_INSERT - CHARACTER_IDX(%u)", clientInfo->characterIdx );
						CloseCID( perSocketContext->cid );
					}

					clientInfo->questDb.questInsert = false;
					clientInfo->inventoryDb.reward = false;		// DB; 사용종료.
				}
			}
			break;
		case SQL_GAME_PROCESS_QUEST_INSERTBYITEM:
			if( perSocketContext->socket == perIoContext->socket )
			{
				/// 의뢰아이템으로 인한 퀘스트 처리
				cCSLock			lock( &mCs );
				ClientInfo*		clientInfo = (ClientInfo*)perSocketContext->buffer;

				/// 이미 소유하고 있는 퀘스트를 절대 또 소유할수 없다!
				if ( clientInfo->questDb.questInsert && clientInfo->inventoryDb.reward )
				{
					QUEST_INSERTBYITEM*	questInsert = (QUEST_INSERTBYITEM*)perIoContext->buffer;

					if ( perIoContext->requestResult == IOCP_REQUEST_SUCCESS )
					{
						if( questInsert->retvalue == 0 )
						{
							cPlayer*           hero    = clientInfo->hero;
							unsigned long      heroIdx = hero->GetObjectID();
							TB_QUEST_PROGRESS* table   = &questInsert->quest;

							/// 2. 아이템 제공
							if ( questInsert->rowCount > 0 )
							{
								HANDLE               handle     = NULL;
								MSG_RES_ITEM_REWARD* sendMsg    = (MSG_RES_ITEM_REWARD*)GetMsgRoot( &handle, perSocketContext, NM_ITEM, NM_ITEM_REWARD_RES );
								sInventory*          sinventory = sendMsg->inventory;

								TB_INVENTORY*        table      = questInsert->inventory;
								TB_INVENTORY*        inventory;
								bool                 newOrUpdate;

								for ( long i = 0; i < questInsert->rowCount; i++, table++ )
								{
									if ( table->apply == InventoryApplyRemoved )
									{
										PostInventoryEvent( EVENT_INVENTORY_DELETE, heroIdx, table, "QUEST INSERT BY ITEM" );
									}
									else
									{
										inventory   = hero->SelectInventory( table->number );
										newOrUpdate = (inventory->idx == table->idx);
										inventory   = hero->XCopyInventory( inventory, table ); // (*inventory) = (*table);

										Inventory2sInventory( sinventory, inventory );
										sinventory++;

										sendMsg->rowCount++;

										PostInventoryEvent( newOrUpdate ? EVENT_INVENTORY_UPDATE : EVENT_INVENTORY_CREATE, heroIdx, inventory, "QUEST INSERT BY ITEM" );
									}
								}

								SendMsgRoot( handle, sendMsg->GetMsgLength( ) );

								clientInfo->request.inventoryUpdate = true;
							}


							/// 등록 결과 send
							/// 3. hero 의 퀘스트 목록 갱신 및 추가된 내용만 발송
							HANDLE                   handle  = NULL;
							MSG_RES_QUEST_ADDBYITEM* sendMsg = (MSG_RES_QUEST_ADDBYITEM*)GetMsgRoot( &handle, perSocketContext, NM_QUEST, NM_QUEST_ADDBYITEM_RES );
							if( sendMsg != NULL )
							{
								sendMsg->ErrorCode = questInsert->retvalue;

								unsigned long questIdx = 0;
								int arrIdx = hero->FindEmptyQuest();

								if ( arrIdx > -1 )
								{
									TB_QUEST_PROGRESS* heroQuest = hero->GetQuest( arrIdx );
									(*heroQuest) = (*table);

									/// 갱신된 목록 send 
									sendMsg->table.arrIdx	   = arrIdx;
									sendMsg->table.questIdx	   = heroQuest->questIdx;
									sendMsg->table.check	   = heroQuest->check;
									sendMsg->table.restTime	   = heroQuest->restTime;
									sendMsg->table.status	   = heroQuest->status;

									questIdx = heroQuest->questIdx;

									PostQuestEvent( EVENT_QUEST_ADD, hero->GetObjectID(), heroQuest->questIdx, "ADD" );
								}
								else
								{
									PostQuestEvent( EVENT_QUEST_ADD, hero->GetObjectID(), table->questIdx, "FAIL : quest list full" );
								}

								/// 결과 보내기
								SendMsgRoot( handle, sizeof(MSG_RES_QUEST_ADD) );

								/// 3. 그외 처리 하기
								/// 제약 사항 걸기 ( Limit )
								hero->SendLimitPenalty( questIdx );

								/// npc 상태 변경
								hero->SendNpcQuestStatus();
							}
						}
						else
						{
							HANDLE                   handle  = NULL;
							MSG_RES_QUEST_ADDBYITEM* sendMsg = (MSG_RES_QUEST_ADDBYITEM*)GetMsgRoot( &handle, perSocketContext, NM_QUEST, NM_QUEST_ADDBYITEM_RES );
							sendMsg->ErrorCode = questInsert->retvalue;
							SendMsgRoot( handle, sizeof(MSG_RES_QUEST_ADD) );
						}
					}
					else
					{
						// 등록 오류시, 클라이언트 강제 종료.
						PostServerEvent( "WARNING - SQL_GAME_PROCESS_QUEST_INSERTBYITEM - CHARACTER_IDX(%u)", clientInfo->characterIdx );
						CloseCID( perSocketContext->cid );
					}

					clientInfo->questDb.questInsert = false;
					clientInfo->inventoryDb.reward = false;		// DB; 사용종료.
				}
			}
			break;
		case SQL_GAME_PROCESS_QUEST_SELECT:
 			if( perSocketContext->socket == perIoContext->socket )
			{	
				cCSLock		lock( &mCs );
				ClientInfo*	clientInfo = (ClientInfo*)perSocketContext->buffer;

				/// 게임접속시에만 플래그를 세움
				/// 즉 전체리스트는 일정경우에만 보낸다.
				if( clientInfo->gameInDb.questList )
				{
					QUEST_SELECT*		questSelect = (QUEST_SELECT*)perIoContext->buffer;
					TB_QUEST_PROGRESS*	table		= questSelect->table;

					/// 초기화
					TB_QUEST_PROGRESS*  heroQuest	= NULL;
					cPlayer*			hero		= clientInfo->hero;

					hero->ClearQuestList();

					/// 
					for( long i = 0; i < questSelect->rowCount; i++, table++ )
					{
						cQuestDefine* define = mpQuestManager->GetQuestDefine( table->questIdx );

						if( mpQuestManager->IsValid( define ) == false )
						{
							// 유효하지 않은 퀘스트 - 강제삭제 후 로그 처리함.
							QuestRemove( perSocketContext, table->idx );
						}
						else
						{
							/// player 퀘스트 목록 복사
							heroQuest = hero->GetQuest( i );
							(*heroQuest) = (*table);

							/// 시작 시간체크 저장
							if( define && define->mQuestLimit && define->mQuestLimit->mTimeType != eTIME_NONE )
							{
								heroQuest->startTime = GetAccumTime();
							}
						}
					}
					
					/// 게임 인 시에 퀘스트 조건중 버프 오라 있으면 걸어주기
					hero->InitQuestAura();

					// request flag 
					clientInfo->request.questSend = true;							/// 퀘스트 리스트 발송요청.
					clientInfo->complete.quest = true;								/// complete flag - 퀘스트 리스트 가져오기 완료.
					clientInfo->gameInDb.questList = false;							/// DB 사용종료
					clientInfo->delay.questSave = GetTickCount( ) + QUEST_SAVETIME;	/// DB 저장 지연
				}
			}
			break;
		case SQL_GAME_PROCESS_QUEST_UPDATE:
			if( perSocketContext->socket == perIoContext->socket )
			{
				QUEST_UPDATE* questUpdate = (QUEST_UPDATE*)perIoContext->buffer;
				char		  buffer[1024]   = "\0";

				for( long i = 0; i < questUpdate->rowCount; ++i )
				{
					/// 업데이트 완료
					if( questUpdate->table[i].retvalue == 0 )
						sprintf( buffer, "UPDATE : [check : %ld]", questUpdate->table[i].check );
					else
						sprintf( buffer, "FAIL : [check : %ld]", questUpdate->table[i].check );

					PostQuestEvent( EVENT_QUEST_UPDATE, questUpdate->characterIdx, questUpdate->table[i].questIdx, buffer );
				}
			}
			break;
		case SQL_GAME_PROCESS_QUEST_DELETE:
			/// 해당 리퀘스트를 보낸 소켓이 맞는지 검사
			if( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock     lock( &mCs );
				ClientInfo* clientInfo = (ClientInfo*)perSocketContext->buffer;
				
				if( clientInfo->questDb.questDelete && clientInfo->inventoryDb.take )
				{
					QUEST_DELETE* questDelete = (QUEST_DELETE*)perIoContext->buffer;
					cPlayer*      hero        = clientInfo->hero;
					unsigned long heroIdx     = hero->GetObjectID();

					TB_QUEST_PROGRESS* delQuest  = hero->GetQuest(questDelete->arrIdx);
					unsigned long     questIndex = delQuest->questIdx;

					if( perIoContext->requestResult == IOCP_REQUEST_SUCCESS )
					{
						/// 삭제 성공시
						if( questDelete->retvalue == 0 )
						{
							PostQuestEvent( EVENT_QUEST_DELETE, hero->GetObjectID(), questIndex, "DELETE" );

							/// 서버메모리로부터 삭제
							hero->DeleteQuest( questDelete->arrIdx );

							/// 패널티 처리
							hero->SendFailPenalty( questIndex );
							hero->SendEndPenalty( questIndex );

							/// npc 상태 변경
							hero->SendNpcQuestStatus();

							/// 아이템 회수
							if ( questDelete->rowCount > 0 )
							{
								HANDLE             handle     = NULL;
								MSG_RES_ITEM_TAKE* sendMsg    = (MSG_RES_ITEM_TAKE*)GetMsgRoot( &handle, perSocketContext, NM_ITEM, NM_ITEM_TAKE_RES );
								sInventory*        sinventory = sendMsg->inventory;

								TB_INVENTORY*      table      = questDelete->table;
								TB_INVENTORY*      inventory;

								for ( long i = 0; i < questDelete->rowCount; i++, table++ )
								{
									inventory = hero->XCopyInventory( table ); // (*inventory) = (*table);

									Inventory2sInventory( sinventory, inventory );
									sinventory++;

									sendMsg->rowCount++;

									if ( table->apply == InventoryApplyRemoved )
									{
										PostInventoryEvent( EVENT_INVENTORY_DELETE, heroIdx, table, "QUEST TAKE" );
										hero->RemoveInventory( inventory );
									}
									else
									{
										PostInventoryEvent( EVENT_INVENTORY_UPDATE, heroIdx, table, "QUEST TAKE" );
									}
								}

								SendMsgRoot( handle, sendMsg->GetMsgLength( ) );

								clientInfo->request.inventoryUpdate = true;
							}

							ResMsgError( perSocketContext, NM_QUEST, NM_QUEST_DEL_RES, ERROR_QUEST_DEL_SUCCESS );

							/// 퀘스트 리스트 재요청
							clientInfo->complete.quest = true;
							clientInfo->request.questSend = true;
						}
						/// 실패시
						else
						{
							ResMsgError( perSocketContext, NM_QUEST, NM_QUEST_DEL_RES, questDelete->retvalue );

							char	buffer[1024]   = "\0";
							sprintf( buffer, "FAIL : [status : %d], [check : %ld]", delQuest->status, delQuest->check );
							PostQuestEvent( EVENT_QUEST_DELETE, hero->GetObjectID(), delQuest->questIdx, buffer );
						}
					}
					else
					{
						char	buffer[1024]   = "\0";
						sprintf( buffer, "FAIL : [status : %d], [check : %ld]", delQuest->status, delQuest->check );
						PostQuestEvent( EVENT_QUEST_DELETE, hero->GetObjectID(), delQuest->questIdx, buffer );

						/// 삭제 오류시, 클라이언트 강제 종료.
						PostServerEvent( "WARNING - SQL_GAME_PROCESS_QUEST_DELETE - CHARACTER_IDX(%u)", clientInfo->characterIdx );
						CloseCID( perSocketContext->cid );
					}

					/// 삭제 관련 db 사용 완료
					clientInfo->questDb.questDelete = false;
					clientInfo->inventoryDb.take = false;
				}
			}
			break;
		case SQL_GAME_PROCESS_QUEST_DELETEAUTO:
			if( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock     lock( &mCs );
				ClientInfo* clientInfo = (ClientInfo*)perSocketContext->buffer;

				if ( clientInfo->questDb.questDelete && clientInfo->inventoryDb.take )
				{				
					QUEST_DELETE* questDelete  = (QUEST_DELETE*)perIoContext->buffer;
					cPlayer*      hero         = clientInfo->hero;
					unsigned long heroIdx      = hero->GetObjectID();

					TB_QUEST_PROGRESS* delQuest   = hero->GetQuest(questDelete->arrIdx);
					unsigned long      questIndex = delQuest->questIdx;

					if ( questDelete->retvalue == 0 && perIoContext->requestResult == IOCP_REQUEST_SUCCESS )
					{
						PostQuestEvent( EVENT_QUEST_DELETE, hero->GetObjectID(), questIndex, "DELETEAUTO" );

						/// 서버메모리로부터 삭제
						hero->DeleteQuest( questDelete->arrIdx );

						/// 패널티 처리
						hero->SendFailPenalty( questIndex );
						hero->SendEndPenalty( questIndex );

						/// npc 상태 변경
						hero->SendNpcQuestStatus();

						/// 아이템 회수
						if ( questDelete->rowCount > 0 )
						{
							HANDLE             handle     = NULL;
							MSG_RES_ITEM_TAKE* sendMsg    = (MSG_RES_ITEM_TAKE*)GetMsgRoot( &handle, perSocketContext, NM_ITEM, NM_ITEM_TAKE_RES );
							sInventory*        sinventory = sendMsg->inventory;

							TB_INVENTORY*      table      = questDelete->table;
							TB_INVENTORY*      inventory;

							for ( long i = 0; i < questDelete->rowCount; i++, table++ )
							{
								inventory = hero->XCopyInventory( table ); // (*inventory) = (*table);

								Inventory2sInventory( sinventory, inventory );
								sinventory++;

								sendMsg->rowCount++;

								if ( table->apply == InventoryApplyRemoved )
								{
									PostInventoryEvent( EVENT_INVENTORY_DELETE, heroIdx, table, "QUEST TAKE" );
									hero->RemoveInventory( inventory );
								}
								else
								{
									PostInventoryEvent( EVENT_INVENTORY_UPDATE, heroIdx, table, "QUEST TAKE" );
								}
							}

							SendMsgRoot( handle, sendMsg->GetMsgLength( ) );

							clientInfo->request.inventoryUpdate = true;
						}

						ResMsgError( perSocketContext, NM_QUEST, NM_QUEST_DELAUTO_RES, questDelete->retvalue );

						/// 퀘스트 리스트 재요청
						clientInfo->complete.quest = true;
						clientInfo->request.questSend = true;
					}
					else
					{
						char	buffer[1024]   = "\0";
						sprintf( buffer, "FAIL : [status : %d], [check : %ld]", delQuest->status, delQuest->check );
						PostQuestEvent( EVENT_QUEST_DELETE, hero->GetObjectID(), delQuest->questIdx, buffer );

						// 삭제 오류시, 클라이언트 강제 종료.
						PostServerEvent( "WARNING - SQL_GAME_PROCESS_QUEST_DELETEAUTO - CHARACTER_IDX(%u)", clientInfo->characterIdx );
						CloseCID( perSocketContext->cid );
					}

					/// 삭제 관련 db 사용 완료
					clientInfo->questDb.questDelete = false;
					clientInfo->inventoryDb.take = false;
				}
			}
			break;
		case SQL_GAME_PROCESS_QUEST_COMPLETE:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				/// 퀘스트 진행 완료 요청 결과
				cCSLock			lock( &mCs );
				ClientInfo*		clientInfo = (ClientInfo*)perSocketContext->buffer;

				if ( clientInfo->questDb.questComplete && clientInfo->inventoryDb.reward )
				{
					/// DB 저장정보
					QUEST_COMPLETE*		questComplete = (QUEST_COMPLETE*)perIoContext->buffer;
					cPlayer*			hero		  = clientInfo->hero;
					unsigned long		heroIdx		  = hero->GetObjectID();

					/// 서버 저장정보
					TB_QUEST_PROGRESS*	completeQuest = hero->GetQuest( questComplete->arrIdx );

					if( perIoContext->requestResult == IOCP_REQUEST_SUCCESS )
					{
						/// 완료 성공시
						if( questComplete->retvalue == 0 )
						{
							/// 정보 저장
							completeQuest->status = questComplete->status;

							/// 
							hero->SendEndPenalty( completeQuest->questIdx );

							if ( questComplete->status == eQUEST_COMPLETE )
								PostQuestEvent( EVENT_QUEST_COMPLETE, heroIdx, completeQuest->questIdx, "COMPLETE" );
							else
								PostQuestEvent( EVENT_QUEST_COMPLETE, heroIdx, completeQuest->questIdx, "ERROR" );

							/// 아이템 회수
							if ( questComplete->rowCount > 0 )
							{
								HANDLE             handle     = NULL;
								MSG_RES_ITEM_TAKE* sendMsg    = (MSG_RES_ITEM_TAKE*)GetMsgRoot( &handle, perSocketContext, NM_ITEM, NM_ITEM_TAKE_RES );
								sInventory*        sinventory = sendMsg->inventory;

								TB_INVENTORY*      table      = questComplete->table;
								TB_INVENTORY*      inventory;

								for ( long i = 0; i < questComplete->rowCount; i++, table++ )
								{
									inventory = hero->XCopyInventory( table ); // (*inventory) = (*table);

									Inventory2sInventory( sinventory, inventory );
									sinventory++;

									sendMsg->rowCount++;

									if ( table->apply == InventoryApplyRemoved )
									{
										PostInventoryEvent( EVENT_INVENTORY_DELETE, heroIdx, table, "QUEST TAKE" );
										hero->RemoveInventory( inventory ); // memset( inventory, 0, sizeof(TB_INVENTORY) );
									}
									else
									{
										PostInventoryEvent( EVENT_INVENTORY_UPDATE, heroIdx, table, "QUEST TAKE" );
									}
								}

								SendMsgRoot( handle, sendMsg->GetMsgLength( ) );

								clientInfo->request.inventoryUpdate = true;
							}

							/// 진행결과 보내기
							HANDLE			   handle  = NULL;
							MSG_RES_NPC_QUEST_COMPLETE* sendMsg = (MSG_RES_NPC_QUEST_COMPLETE*)GetMsgRoot( &handle, perSocketContext );
							sendMsg->Category = NM_NPC;
							sendMsg->Protocol = NM_NPC_QUEST_COMPLETE_RES;
							sendMsg->ErrorCode	= questComplete->retvalue;
							sendMsg->arrayIdx	= questComplete->arrIdx;
							sendMsg->status		= questComplete->status;
							SendMsgRoot( handle, sizeof(MSG_RES_NPC_QUEST_COMPLETE) );

							/// npc의 퀘스트 상태 변경
							hero->SendNpcQuestStatus();
						}
						/// 실패시
						else
						{
							ResMsgError( perSocketContext, NM_NPC, NM_NPC_QUEST_COMPLETE_RES, questComplete->retvalue );

							/// 로그 남기기
							char	buffer[1024]   = "\0";
							sprintf( buffer, "FAIL : [status : %d], [check : %ld]", questComplete->status, questComplete->check );
							PostQuestEvent( EVENT_QUEST_COMPLETE, heroIdx, completeQuest->questIdx, buffer );
						}
					}
					else
					{
						/// 로그 남기기
						char	buffer[1024]   = "\0";
						sprintf( buffer, "FAIL : [status : %d], [check : %ld]", questComplete->status, questComplete->check );
						PostQuestEvent( EVENT_QUEST_COMPLETE, heroIdx, completeQuest->questIdx, buffer );

						// 삭제 오류시, 클라이언트 강제 종료.
						PostServerEvent( "WARNING - SQL_GAME_PROCESS_QUEST_COMPLETE - CHARACTER_IDX(%u)", clientInfo->characterIdx );
						CloseCID( perSocketContext->cid );
					}

					/// 진행완료 관련 db사용 완료
					clientInfo->questDb.questComplete = false;
					clientInfo->inventoryDb.reward  = false;
				}
			}
			break;
		case SQL_GAME_PROCESS_QUEST_REWARD:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				/// 퀘스트 완료 요청 결과			
				cCSLock			lock( &mCs );
				ClientInfo*		clientInfo = (ClientInfo*)perSocketContext->buffer;

				if ( clientInfo->questDb.questReward && clientInfo->inventoryDb.reward )
				{
					QUEST_REWARD*      questReward  = (QUEST_REWARD*)perIoContext->buffer;
					cPlayer*           hero         = clientInfo->hero;
					unsigned long      heroIdx		= hero->GetObjectID();

					TB_QUEST_PROGRESS* rewardQuest  = hero->GetQuest( questReward->arrIdx );

					if ( perIoContext->requestResult == IOCP_REQUEST_SUCCESS )
					{
						/// 성공시 처리
						unsigned long questIndex = rewardQuest->questIdx;
	
						PostQuestEvent( EVENT_QUEST_REWARD, heroIdx, questIndex, "REWARD" );

						/// 기본보상 처리
						hero->SendDefaultReward( questIndex, questReward->skillClassIdx, questReward->skillRowCount );

						/// 선택보상중 버프 처리
						for ( long i = 0; i < questReward->bufRowCount; ++i )
							hero->SendRewardBuff( questReward->bufIndex[i] );

						/// 서버메모리로부터 삭제
						hero->DeleteQuest( questReward->arrIdx );

						/// 완료된 퀘스트 목록에 추가
						hero->AddCompleteQuest( questIndex );

						/// npc의 퀘스트 상태 변경
						hero->SendNpcQuestStatus();

						if ( questReward->characterMoney > 0 )
						{
							unsigned long befor  = hero->GetMoney( );
							long          result = hero->AddMoney( hero->GetObject( ), questReward->characterMoney, false );
							if ( result == questReward->characterMoney )
							{
								PostMoneyEvent( questReward->characterIdx, befor, hero->GetMoney( ), "QUEST_REWARD::QuestReward(=%d):Money(=%d)", questReward->idx, questReward->characterMoney );
							}
							else
							{
								PostServerEvent( "Error - SQL_GAME_PROCESS_QUEST_REWARD AddMoney(=%d) != ResultMoney(=%d).", questReward->characterMoney, result );
							}
						}
						if ( questReward->itemRowCount > 0 )
						{
							HANDLE               handle     = NULL;
							MSG_RES_ITEM_REWARD* sendMsg    = (MSG_RES_ITEM_REWARD*)GetMsgRoot( &handle, perSocketContext, NM_ITEM, NM_ITEM_REWARD_RES );
							sInventory*          sinventory = sendMsg->inventory;

							TB_INVENTORY*        table      = questReward->table;
							TB_INVENTORY*        inventory;
							bool                 newOrUpdate;

							for ( long i = 0; i < questReward->itemRowCount; i++, table++ )
							{
								inventory   = hero->SelectInventory( table->number );
								newOrUpdate = (inventory->idx == table->idx);
								inventory   = hero->XCopyInventory( table ); // (*inventory) = (*table);

								Inventory2sInventory( sinventory, inventory );
								sinventory++;

								sendMsg->rowCount++;

								PostInventoryEvent( newOrUpdate ? EVENT_INVENTORY_UPDATE : EVENT_INVENTORY_CREATE, heroIdx, inventory, "QUEST REWARD" );
							}

							SendMsgRoot( handle, sendMsg->GetMsgLength( ) );
							clientInfo->request.inventoryUpdate = true;
						}
				
						ResMsgError( perSocketContext, NM_NPC, NM_NPC_QUEST_REWARD_RES, ERROR_QUEST_REWARD_SUCCESS );

						/// 보상도 끝낸 완료목록 다시 세팅
						clientInfo->gameInDb.questEndList = QuestCompleteSelect( perSocketContext, heroIdx );

						clientInfo->complete.quest = true;
						clientInfo->request.questSend = true;
					}
					else
					{
						char buffer[1024];
						sprintf( buffer, "FAIL : [status : %d], [check : %ld]", rewardQuest->status, rewardQuest->check );
						PostQuestEvent( EVENT_QUEST_REWARD, heroIdx, rewardQuest->questIdx, buffer );

						ResMsgError( perSocketContext, NM_NPC, NM_NPC_QUEST_REWARD_RES, ERROR_QUEST_REWARD_FAIL );

						// 보상 오류시, 클라이언트 강제 종료.
						PostServerEvent( "WARNING - SQL_GAME_PROCESS_QUEST_REWARD - CHARACTER_IDX(%u)", clientInfo->characterIdx );
						CloseCID( perSocketContext->cid );
					}

					/// 보상요청 관련 db사용 완료
					clientInfo->questDb.questReward = false;
					clientInfo->inventoryDb.reward  = false;
				}
			}
			break;
		case SQL_GAME_PROCESS_QUEST_ENDLISTSELECT:
			if( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock		lock( &mCs );
				ClientInfo*	clientInfo = (ClientInfo*)perSocketContext->buffer;

				if( clientInfo->gameInDb.questEndList )
				{
					cPlayer*       hero         = clientInfo->hero;
					QUEST_ENDLIST* questEndList = (QUEST_ENDLIST*)perIoContext->buffer;
					long*          questIdx     = questEndList->questIdx;

					/// 완료목록과 완료그룹목록 초기화
					hero->ClearCompleteList();
					for( long i = 0; i < questEndList->rowCount; i++, questIdx++ )
					{
						/// 완료목록 저장 
						hero->AddCompleteQuest( (*questIdx) );
					}
					
					clientInfo->gameInDb.questEndList = false;	/// DB 사용종료
				}
			}
			break;
		case SQL_GAME_PROCESS_QUEST_REMOVE:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock			lock( &mCs );

				QUEST_REMOVE*	questRemove = (QUEST_REMOVE*)perIoContext->buffer;
				ClientInfo*		clientInfo	= (ClientInfo*)perSocketContext->buffer;

				if ( perIoContext->requestResult == IOCP_REQUEST_SUCCESS )
				{
					PostQuestEvent( EVENT_QUEST_REMOVE, clientInfo->characterIdx, questRemove->idx, "REMOVE" );
				}
				else
				{
					PostServerEvent( "경고! DB와 서버간의 동기화 오류 - SQL_GAME_PROCESS_QUEST_REMOVE" );
				}
			}
			break;
		case SQL_GAME_PROCESS_TITLE_INSERT:
			if( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock       lock( &mCs );
				TITLE_INSERT* titleInsert = (TITLE_INSERT*)perIoContext->buffer;
				ClientInfo*   clientInfo  = (ClientInfo*)perSocketContext->buffer;
				cPlayer*      hero        = clientInfo->hero;

				if ( hero->AddHaveTitle( titleInsert->titleIdx ) )
				{
					hero->SendHaveTitle( titleInsert->titleIdx );
				}
			}
			break;
		case SQL_GAME_PROCESS_TITLE_SELECT:
			if( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock			lock( &mCs );
				ClientInfo*		clientInfo = (ClientInfo*)perSocketContext->buffer;

				if( clientInfo->gameInDb.titleList == true )
				{
					TITLE_SELECT*	titleSelect = (TITLE_SELECT*)perIoContext->buffer;
					TB_TITLE*		table		= titleSelect->table;
					cPlayer*		hero		= clientInfo->hero;

					/// 소유 호칭 목록 삭제
					hero->ClearHaveTitleList();

					/// 소유목록에 추가
					for( long i = 0; i < titleSelect->rowCount; i++, table++ )
					{
						bool ret = hero->AddHaveTitle( table->titleIdx );
						if( ret == false )
						{
							PostServerEvent( "SQL_GAME_PROCESS_TITLE_SELECT, heroIdx = %d titleIdx = %d", hero->GetObjectID(), table->titleIdx );
							assert(0);
							continue;
						}
					}

					clientInfo->request.titleSend = true;
					clientInfo->gameInDb.titleList = false;
					clientInfo->complete.title = true;
				}
			}
			break;
		case SQL_GAME_PROCESS_GUILD_CREATE:
			if( perSocketContext->socket == perIoContext->socket )
			{
				/// 길드 생성
				cCSLock			lock( &mCs );
				ClientInfo*		clientInfo = (ClientInfo*)perSocketContext->buffer;
				cPlayer*		hero	   = clientInfo->hero;
				
				if ( clientInfo->guildDb.guildCreate == true )
				{
					GUILD_CREATE* guildCreate = (GUILD_CREATE*)perIoContext->buffer;

					if ( perIoContext->requestResult == IOCP_REQUEST_SUCCESS )
					{
						if ( guildCreate->retvalue == 0 )
						{
							/// 1. 길드 추가.
							if ( mpGuildManager->AddGuild( guildCreate, hero ) == true )
							{
								PostGuildEvent( hero->GetObjectID(), guildCreate->idx, hero->GetGuildPosition(), "SUCCESS - SQL_GAME_PROCESS_GUILD_CREATE AddGuild()" );

								/// 2. HERO에 길드 세팅
								hero->ChangeGuild( guildCreate->idx, eGUILD_MASTER, guildCreate->name, 0 );

								/// 결과 보내기
								ResMsgError( perSocketContext, NM_NPC, NM_NPC_GUILD_CREATE_RES, ERROR_NPC_GUILD_CREATE_SUCESS );

								/// 3. 길드정보
								HANDLE                    handle = NULL;
								MSG_RES_GUILD_CREATEDATA* sendMsg = (MSG_RES_GUILD_CREATEDATA*)GetMsgRoot( &handle, perSocketContext, NM_GUILD, NM_GUILD_CREATEDATA_RES );
								sendMsg->idx = guildCreate->idx;
								wcscpy( sendMsg->name, guildCreate->name );
								SendMsgRoot( handle, sizeof(MSG_RES_GUILD_CREATEDATA) );

								/// 4. 금액 정산
								if( guildCreate->money != 0 )
								{
									unsigned long befor = hero->GetMoney();
									long		  result = hero->AddMoney( hero->GetObject(), guildCreate->money, false	);

									PostMoneyEvent( guildCreate->characterIdx, befor, hero->GetMoney(), "GUILD_CREATE::Money(=%d)", guildCreate->money );

									if ( result != guildCreate->money )
										PostServerEvent( "Error - SQL_GAME_PROCESS_GUILD_CREATE AddMoney(=%d) != ResultMoney(=%d).", guildCreate->money, result );
								}

								/// SYN : 길드 생성
								if( PostGuildCreate( guildCreate, hero ) == false )
								{
									PostGuildEvent( hero->GetObjectID(), guildCreate->idx, hero->GetGuildPosition(), "FAIL - SQL_GAME_PROCESS_GUILD_CREATE PostGuildCreate()" );
								}
							}
							else
							{
								PostGuildEvent( hero->GetObjectID(), guildCreate->idx, hero->GetGuildPosition(), "fail - SQL_GAME_PROCESS_GUILD_CREATE AddGuild()" );

								PostServerEvent( "1 : SQL_GAME_PROCESS_GUILD_CREATE, heroIdx = %d guildIndex = %d", hero->GetObjectID(), guildCreate->idx );
								CloseCID( perSocketContext->cid );
							}
						}
						else
						{
							char	buffer[1024]   = "\0";
							sprintf( buffer, "FAIL - SQL_GAME_PROCESS_GUILD_CREATE retvalue(%d)", guildCreate->retvalue );
							PostGuildEvent( hero->GetObjectID(), guildCreate->idx, hero->GetGuildPosition(), buffer );

							ResMsgError( perSocketContext, NM_NPC, NM_NPC_GUILD_CREATE_RES, guildCreate->retvalue );
							PostServerEvent( "2 : SQL_GAME_PROCESS_GUILD_CREATE, heroIdx = %d guildIndex = %d", hero->GetObjectID(), guildCreate->idx );
						}
					}
					else
					{
						// 등록 오류시, 클라이언트 강제 종료.
						PostServerEvent( "3 : SQL_GAME_PROCESS_GUILD_CREATE, heroIdx = %d guildIndex = %d", hero->GetObjectID(), guildCreate->idx );
						CloseCID( perSocketContext->cid );
					}

					clientInfo->guildDb.guildCreate = false;
				}
			}
			break;
		case SQL_GAME_PROCESS_GUILDUSER_ADD:
			if( perSocketContext->socket == perIoContext->socket )
			{
				// 길드원 추가
				cCSLock			lock( &mCs );
				ClientInfo*		clientInfo = (ClientInfo*)perSocketContext->buffer;
				cPlayer*		hero	   = clientInfo->hero;
				unsigned long	heroIdx	   = hero->GetObjectID();

				if( clientInfo->guildDb.guildAdd == true )
				{
					GUILDUSER_ADD*	guildUserAdd = (GUILDUSER_ADD*)perIoContext->buffer;

					if ( perIoContext->requestResult == IOCP_REQUEST_SUCCESS )
					{
						/// 추가 성공
						if( guildUserAdd->retvalue == 0 )
						{
							cGuild* guild = mpGuildManager->GetGuild( guildUserAdd->idx );
							if( guild && guild->AddPlayer( hero, eGUILD_GENERAL, GetChannelNum() ) == true )
							{
								PostGuildEvent( guildUserAdd->charaterIdx, guildUserAdd->idx, eGUILD_GENERAL, "SUCCESS - SQL_GAME_PROCESS_GUILDUSER_ADD AddPlayer()" );

								// HERO에 길드 세팅
								hero->ChangeGuild( guildUserAdd->idx, eGUILD_GENERAL, guild->GetGuildName(), guild->GetGuildMarkIndex() );

								// 결과 보내기
								ResMsgError( perSocketContext, NM_GUILD, NM_GUILD_REPLY_RES, ERROR_GUILD_ADDREPLY_SUCCESS );

								// HERO에 길드 리스트 보내주기
								mpGuildManager->SendGuildInfo( (ULONG_PTR)perSocketContext, hero, guildUserAdd->idx );

								// 길드원들에세 추가되었음 보내주기
								mpGuildManager->SendGuildUserAdd( guild, hero->GetObjectID(), hero->GetLevel(), hero->GetJob(), 
																		 hero->GetMapNumber(), hero->GetName(), hero->GetGuildPosition(), GetChannelNum() );

								/// SYN : 길드원 추가
								if( PostGuildUserAdd( guildUserAdd->idx, hero ) == false )
								{
									PostGuildEvent( guildUserAdd->charaterIdx, guildUserAdd->idx, eGUILD_GENERAL, "FAIL - SQL_GAME_PROCESS_GUILDUSER_ADD PostGuildUserAdd()" );
								}
							}
							else
							{
								PostGuildEvent( guildUserAdd->charaterIdx, guildUserAdd->idx, eGUILD_GENERAL, "FAIL - SQL_GAME_PROCESS_GUILDUSER_ADD AddPlayer()" );

								PostServerEvent( "1: SQL_GAME_PROCESS_GUILDUSER_ADD, heroIdx = %d guildIndex = %d", heroIdx, guildUserAdd->idx );
								CloseCID( perSocketContext->cid );
							}
						}
						else
						{
							char	buffer[1024]   = "\0";
							sprintf( buffer, "FAIL - SQL_GAME_PROCESS_GUILDUSER_ADD retvalue(%d)", guildUserAdd->retvalue );
							PostGuildEvent( guildUserAdd->charaterIdx, guildUserAdd->idx, eGUILD_NONE, buffer );

							ResMsgError( perSocketContext, NM_GUILD, NM_GUILD_REPLY_RES, ERROR_GUILD_ADDREPLY_FAIL );
							PostServerEvent( "2: SQL_GAME_PROCESS_GUILDUSER_ADD, heroIdx = %d, heroPosition = %d, characterIdx = %d, guildIndex = %d", 
											  heroIdx, hero->GetGuildPosition(), guildUserAdd->charaterIdx, guildUserAdd->idx );
						}
					}
					else
					{
						// 등록 오류시, 클라이언트 강제 종료.
						PostServerEvent( "3: SQL_GAME_PROCESS_GUILDUSER_ADD, heroIdx = %d guildIndex = %d", heroIdx, guildUserAdd->idx );
						CloseCID( perSocketContext->cid );
					}
				
					clientInfo->guildDb.guildAdd = false;
				}
			}
			break;
		case SQL_GAME_PROCESS_GUILDUSER_OUT:
			if( perSocketContext->socket == perIoContext->socket )
			{
				// 길드원 탈퇴처리
				cCSLock			lock( &mCs );
				ClientInfo*		clientInfo = (ClientInfo*)perSocketContext->buffer;
				cPlayer*		hero	   = clientInfo->hero;
				unsigned long	heroIdx	   = hero->GetObjectID();

				if( clientInfo->guildDb.guildOut == true )
				{
					GUILDUSER_OUT* guildUserOut = (GUILDUSER_OUT*)perIoContext->buffer;

					if( perIoContext->requestResult == IOCP_REQUEST_SUCCESS )
					{
						if( guildUserOut->retvalue == 0 )
						{
							cGuild* guild = mpGuildManager->GetGuild( guildUserOut->idx );
							if( guild && guild->DeletePlayer( hero->GetObjectID() ) == true )
							{
								PostGuildEvent( heroIdx, guildUserOut->idx, eGUILD_NONE, "SUCCESS - SQL_GAME_PROCESS_GUILDUSER_OUT DeletePlayer()" );

								// HERO 길드 정보 삭제
								hero->ChangeGuild( 0, eGUILD_NONE, 0, 0 );

								// 자신에게 쏘기
								HANDLE			handle = NULL;
								if( GetMsgRoot( &handle, perSocketContext, NM_GUILD, NM_GUILD_OUT_RES ) != false )
								{
									SendMsgRoot( handle, sizeof(MSG_RES_GUILD_OUT) );
								}

								// 길드원들에게 쏘기
								mpGuildManager->SendGuildUserOut( guild, hero->GetObjectID() );
								
								// 길드 신청정보 있으면 삭제
								hero->SetGuildAddReq( false );

								/// SYN : 길드 탈퇴
								if( PostGuildUserOut( guildUserOut->idx, guildUserOut->charaterIdx ) == false )
								{
									PostGuildEvent( heroIdx, guildUserOut->idx, eGUILD_NONE, "FAIL - PostGuildUserOut()" );
								}
							}
							else
							{
								PostGuildEvent( heroIdx, guildUserOut->idx, hero->GetGuildPosition(), "FAIL - SQL_GAME_PROCESS_GUILDUSER_OUT DeletePlayer()" );

								PostServerEvent( "1: SQL_GAME_PROCESS_GUILDUSER_OUT, heroIdx = %d guildIndex = %d", heroIdx, guildUserOut->idx );
								CloseCID( perSocketContext->cid );
							}
						}
						else
						{
							char	buffer[1024]   = "\0";
							sprintf( buffer, "FAIL - SQL_GAME_PROCESS_GUILDUSER_OUT retvalue(%d)", guildUserOut->retvalue );
							PostGuildEvent( heroIdx, guildUserOut->idx, hero->GetGuildPosition(), buffer );

							ResMsgError( perSocketContext, NM_GUILD, NM_GUILD_OUT_RES, ERROR_GUILD_OUT_FAIL );
							PostServerEvent( "2: SQL_GAME_PROCESS_GUILDUSER_OUT, heroIdx = %d guildIndex = %d", heroIdx, guildUserOut->idx );
						}
					}
					else
					{
						// 등록 오류시, 클라이언트 강제 종료.
						PostServerEvent( "3: SQL_GAME_PROCESS_GUILDUSER_OUT, heroIdx = %d guildIndex = %d", heroIdx, guildUserOut->idx );
						CloseCID( perSocketContext->cid );
					}

					clientInfo->guildDb.guildOut = false;
				}

			}
			break;
		case SQL_GAME_PROCESS_GUILDUSER_CUT:
			if( perSocketContext->socket == perIoContext->socket )
			{
				// 길드원 추방
				cCSLock			lock( &mCs );
				ClientInfo*		clientInfo = (ClientInfo*)perSocketContext->buffer;

				if( clientInfo->guildDb.guildCut == true )
				{
					GUILDUSER_OUT* guildUserCut = (GUILDUSER_OUT*)perIoContext->buffer;

					if( perIoContext->requestResult == IOCP_REQUEST_SUCCESS )
					{
						if( guildUserCut->retvalue == 0 )
						{
							cGuild* guild = mpGuildManager->GetGuild( guildUserCut->idx );
							if( guild && guild->DeletePlayer( guildUserCut->charaterIdx ) == true )
							{
								PostGuildEvent( guildUserCut->charaterIdx, guildUserCut->idx, eGUILD_NONE, "SUCCESS - SQL_GAME_PROCESS_GUILDUSER_CUT DeletePlayer()" );

								// 추방 성공 알림
								ResMsgError( perSocketContext, NM_GUILD, NM_GUILD_CUT_RES, ERROR_GUILD_CUT_SUCCESS );

								// 추방당한 유저가 게임중이면 처리
								cPlayer* other = mpObjectManager->GetPlayer( guildUserCut->charaterIdx );
								if( other )
								{
									other->ChangeGuild( 0, eGUILD_NONE, 0, 0 );
	
									// 추방 
									HANDLE			handle = NULL;
									if( GetMsgRoot( &handle, other->GetConnectionIdx(), NM_GUILD, NM_GUILD_CUT_SYN ) != false )
									{
										SendMsgRoot( handle, sizeof(MSG_RES_GUILD_OUT) );
									}

									// 길드 신청 정보 있으면 삭제
									other->SetGuildAddReq( false );
								}

								// 길드원들에게 쏘기
								mpGuildManager->SendGuildUserCut( guild, guildUserCut->charaterIdx );

								/// SYN : 길드원 추방
								if( PostGuildUserCut( guildUserCut->idx, guildUserCut->charaterIdx ) == false )
								{
									PostGuildEvent( guildUserCut->charaterIdx, guildUserCut->idx, eGUILD_NONE, "FAIL - SQL_GAME_PROCESS_GUILDUSER_CUT PostGuildUserCut()" );
								}
							}
							else
							{
								PostGuildEvent( guildUserCut->charaterIdx, guildUserCut->idx, eGUILD_NONE, "FAIL - SQL_GAME_PROCESS_GUILDUSER_CUT DeletePlayer()" );

								PostServerEvent( "1: SQL_GAME_PROCESS_GUILDUSER_CUT, charaterIdx = %d guildIndex = %d", guildUserCut->charaterIdx, guildUserCut->idx );
								CloseCID( perSocketContext->cid );
							}
						}
						else
						{
							char	buffer[1024]   = "\0";
							sprintf( buffer, "FAIL - SQL_GAME_PROCESS_GUILDUSER_CUT retvalue(%d)", guildUserCut->retvalue );
							PostGuildEvent( guildUserCut->charaterIdx, guildUserCut->idx, eGUILD_NONE, buffer );

							ResMsgError( perSocketContext, NM_GUILD, NM_GUILD_CUT_SYN, ERROR_GUILD_CUT_FAIL );
							PostServerEvent( "2: SQL_GAME_PROCESS_GUILDUSER_CUT, otherIdx = %d guildIndex = %d", guildUserCut->charaterIdx, guildUserCut->idx );
						}
					}
					else
					{
						// 등록 오류시, 클라이언트 강제 종료.
						PostServerEvent( "3: SQL_GAME_PROCESS_GUILDUSER_CUT, otherIdx = %d guildIndex = %d", guildUserCut->charaterIdx, guildUserCut->idx );
						CloseCID( perSocketContext->cid );
					}

					clientInfo->guildDb.guildCut = false;
				}
			}
			break;
		case SQL_GAME_PROCESS_GUILD_DELETE:
			if( perSocketContext->socket == perIoContext->socket )
			{
				// 길드해체
				cCSLock			lock( &mCs );
				ClientInfo*		clientInfo = (ClientInfo*)perSocketContext->buffer;
				cPlayer*		hero	   = clientInfo->hero;
				unsigned long	heroIdx	   = hero->GetObjectID();

				if( clientInfo->guildDb.guildDelete == true )
				{
					GUILD_DELETE* guildDelete = (GUILD_DELETE*)perIoContext->buffer;

					if( perIoContext->requestResult == IOCP_REQUEST_SUCCESS )
					{
						if( guildDelete->retvalue == 0 )
						{
							if( mpGuildManager->DeleteGuild( guildDelete->idx ) == true )
							{
								PostGuildEvent( heroIdx, guildDelete->idx, eGUILD_NONE, "SUCCESS - SQL_GAME_PROCESS_GUILD_DELETE DeleteGuild()" );

								// HERO 길드 정보 삭제
								hero->ChangeGuild( 0, eGUILD_NONE, 0, 0 );

								// 자신에게 쏘기
								ResMsgError( perSocketContext, NM_GUILD, NM_GUILD_DELETE_RES, ERROR_GUILD_DELETE_SUCCESS );

								//
								hero->SetGuildAddReq( false );

								/// SYN : 길드 삭제
								if( PostGuildDelete( guildDelete->idx ) == false )
								{
									PostGuildEvent( heroIdx, guildDelete->idx, eGUILD_NONE, "FAIL - SQL_GAME_PROCESS_GUILD_DELETE PostGuildDelete()" );
								}
							}
							else
							{
								PostGuildEvent( heroIdx, guildDelete->idx, hero->GetGuildPosition(), "FAIL - SQL_GAME_PROCESS_GUILD_DELETE DeleteGuild()" );

								PostServerEvent( "1: SQL_GAME_PROCESS_GUILD_DELETE, heroIdx = %d guildIndex = %d", heroIdx, guildDelete->idx );
								CloseCID( perSocketContext->cid );
							}
						}
						else
						{
							char	buffer[1024]   = "\0";
							sprintf( buffer, "FAIL - SQL_GAME_PROCESS_GUILD_DELETE retvalue(%d)", guildDelete->retvalue );
							PostGuildEvent( heroIdx, guildDelete->idx, hero->GetGuildPosition(), buffer );

							ResMsgError( perSocketContext, NM_GUILD, NM_GUILD_DELETE_RES, ERROR_GUILD_DELETE_FAIL );
							PostServerEvent( "2: SQL_GAME_PROCESS_GUILD_DELETE, heroIdx = %d guildIndex = %d", heroIdx, guildDelete->idx );
						}
					}
					else
					{
						// 등록 오류시, 클라이언트 강제 종료.
						PostServerEvent( "3: SQL_GAME_PROCESS_GUILD_DELETE, heroIdx = %d guildIndex = %d", heroIdx, guildDelete->idx );
						CloseCID( perSocketContext->cid );
					}
					
					clientInfo->guildDb.guildDelete = false;
				}
			}
			break;
		case SQL_GAME_PROCESS_GUILD_UPDATE:
			if( mRequest.guildDB )
			{
				/// 길드 업데이트
				cCSLock		  lock( &mCs );
				GUILD_UPDATE* guildUpdate = (GUILD_UPDATE*)perIoContext->buffer;

				/// 에러
				if( perIoContext->requestResult != IOCP_REQUEST_SUCCESS || guildUpdate->retvalue != 0 )
				{
					PostServerEvent( "SQL_GAME_PROCESS_GUILD_UPDATE - fail [%d,%d,%d]", guildUpdate->idx, perIoContext->requestResult, guildUpdate->retvalue );
				}

				/// 분할 체크
				if( GUILDMAN->CheckGuildSaveTotal() == true )
				{
					mRequest.guildDB = false;
					mComplete.guildDB = true;
				}
			}
			break;
		case SQL_GAME_PROCESS_GUILDUSER_GIVEPOSITION:
			if( perSocketContext->socket == perIoContext->socket )
			{
				/// 직위 변경
				cCSLock			lock( &mCs );
				ClientInfo*		clientInfo = (ClientInfo*)perSocketContext->buffer;
				cPlayer*		hero	   = clientInfo->hero;
				unsigned long	heroIdx	   = hero->GetObjectID();
				
				if( clientInfo->guildDb.guildGivePosition == true )
				{
					GUILDUSER_GIVEPOSITION* guildUserGivePos = (GUILDUSER_GIVEPOSITION*)perIoContext->buffer;

					if( perIoContext->requestResult == IOCP_REQUEST_SUCCESS )
					{
						if( guildUserGivePos->retvalue == 0 )
						{
							/// 해당 유저의 길드 직위를 변경한다
							cGuild* guild = mpGuildManager->GetGuild( guildUserGivePos->idx );
							if( guild )
							{
								PostGuildEvent( guildUserGivePos->characterIdx, guildUserGivePos->idx, guildUserGivePos->position, "SUCCESS - SQL_GAME_PROCESS_GUILDUSER_GIVEPOSITION" );

								/// 직위 변경 및 send
								mpGuildManager->SendGuildChangePosition( guild, guildUserGivePos->characterIdx, guildUserGivePos->position );

								/// SYN : 길드직위 변경
								if( PostGuildGivePosition( guildUserGivePos->idx, guildUserGivePos->characterIdx, guildUserGivePos->position ) == false )
								{
									PostGuildEvent( guildUserGivePos->characterIdx, guildUserGivePos->idx, guildUserGivePos->position, "FAIL - PostGuildGivePosition()" );
								}
							}
							else
							{
								PostGuildEvent( heroIdx, guildUserGivePos->idx, hero->GetGuildPosition(), "FAIL - SQL_GAME_PROCESS_GUILDUSER_GIVEPOSITION not exist guild" );

								PostServerEvent( "1: SQL_GAME_PROCESS_GUILDUSER_GIVEPOSITION, heroIdx = %d guildIndex = %d", heroIdx, guildUserGivePos->idx );
								CloseCID( perSocketContext->cid );
							}
						}
						else
						{
							char	buffer[1024]   = "\0";
							sprintf( buffer, "FAIL - SQL_GAME_PROCESS_GUILDUSER_GIVEPOSITION retvalue(%d)", guildUserGivePos->retvalue );
							PostGuildEvent( guildUserGivePos->characterIdx, guildUserGivePos->idx, eGUILD_NONE, buffer );

							ResMsgError( perSocketContext, NM_GUILD, NM_GUILD_GIVEPOSITION_RES, ERROR_GUILD_GIVEPOSITION_FAIL );
							PostServerEvent( "2: SQL_GAME_PROCESS_GUILDUSER_GIVEPOSITION, characterIdx = %d guildIndex = %d", guildUserGivePos->characterIdx, guildUserGivePos->idx );
						}
					}
					else
					{
						// 등록 오류시, 클라이언트 강제 종료.
						PostServerEvent( "3: SQL_GAME_PROCESS_GUILDUSER_GIVEPOSITION, heroIdx = %d guildIndex = %d", heroIdx, guildUserGivePos->idx );
						CloseCID( perSocketContext->cid );
					}

					clientInfo->guildDb.guildGivePosition = false;
				}
			}
			break;
		case SQL_GAME_PROCESS_GUILDMARK_UPDATE:
			if( perSocketContext->socket == perIoContext->socket )
			{
				// 길드 마크 변경
				cCSLock			lock( &mCs );
				ClientInfo*		clientInfo = (ClientInfo*)perSocketContext->buffer;
				cPlayer*		hero	   = clientInfo->hero;
				unsigned long	heroIdx	   = hero->GetObjectID();

				if( clientInfo->guildDb.guildMarkUpdate == true )
				{
					GUILDMARK_UPDATE* guildMarkUpdate = (GUILDMARK_UPDATE*)perIoContext->buffer;

					if( perIoContext->requestResult == IOCP_REQUEST_SUCCESS )
					{
						if( guildMarkUpdate->retvalue == 0 )
						{
							cGuild* guild = mpGuildManager->GetGuild( guildMarkUpdate->idx );
							if( guild )
							{
								PostGuildEvent( heroIdx, guildMarkUpdate->idx, eGUILD_MASTER, "SUCCESS - SQL_GAME_PROCESS_GUILDMARK_UPDATE" );

								// 길드 마크 정보 변경
								int size = sizeof(guildMarkUpdate->mark.binary);
								guild->UpdateMark( guildMarkUpdate->markIdx, guildMarkUpdate->mark.binary, size );

								// 길드원들에게 변경된 마크인덱스 전송
								mpGuildManager->SendGuildMarkUpdate( guild, guildMarkUpdate->markIdx );

								/// SYN : 길드 마크 변경
								if( PostGuildUpdateMark( guildMarkUpdate->idx, guildMarkUpdate->markIdx, guildMarkUpdate->mark.binary, size ) == false )
								{
									PostGuildEvent( heroIdx, guildMarkUpdate->idx, eGUILD_MASTER, "FAIL - PostGuildUpdateMark()" );
								}
							}
							else
							{
								PostGuildEvent( heroIdx, guildMarkUpdate->idx, hero->GetGuildPosition(), "FAIL - SQL_GAME_PROCESS_GUILDMARK_UPDATE not exist guild" );

								PostServerEvent( "1: SQL_GAME_PROCESS_GUILDMARK_UPDATE, heroIdx = %d guildIndex = %d, markIdx = %d", heroIdx, guildMarkUpdate->idx, guildMarkUpdate->markIdx );
								CloseCID( perSocketContext->cid );
							}
						}
						else
						{
							char	buffer[1024]   = "\0";
							sprintf( buffer, "FAIL - SQL_GAME_PROCESS_GUILDMARK_UPDATE retvalue(%d)", guildMarkUpdate->retvalue );
							PostGuildEvent( heroIdx, guildMarkUpdate->idx, eGUILD_NONE, buffer );
 
							ResMsgError( perSocketContext, NM_GUILD, NM_GUILDMARK_UPDATE_RES, ERROR_GUILDMARK_UPDATE_FAIL );
							PostServerEvent( "2: SQL_GAME_PROCESS_GUILDMARK_UPDATE, heroIdx = %d guildIndex = %d, markIdx = %d", heroIdx, guildMarkUpdate->idx, guildMarkUpdate->markIdx );
						}
					}
					else
					{
						// 등록 오류시, 클라 강제 종료
						PostServerEvent( "3: SQL_GAME_PROCESS_GUILDMARK_UPDATE, heroIdx = %d guildIndex = %d, markIdx = %d", heroIdx, guildMarkUpdate->idx, guildMarkUpdate->markIdx );
						CloseCID( perSocketContext->cid );
					}

					clientInfo->guildDb.guildMarkUpdate = false;
				}
			}
			break;
		case SQL_GAME_PROCESS_MAKESKILL_INSERT:
			if( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock			lock( &mCs );
				ClientInfo*		pClientInfo = (ClientInfo*)perSocketContext->buffer;

				MAKESKILL_INSERT* pInsert = (MAKESKILL_INSERT*)perIoContext->buffer;

				if( pInsert->mRetvalue == 0 )
				{
					cPlayer* hero = pClientInfo->hero;
					unsigned long heroIdx = hero->GetObjectID();
					hero->SetMakeSkill( pInsert );

					/// 아이템 정보 갱신
					if ( pInsert->mInventory.idx != 0 )
					{
						HANDLE               handle     = NULL;
						MSG_RES_MAKESKILL_ITEM* sendMsg    = (MSG_RES_MAKESKILL_ITEM*)GetMsgRoot( &handle, perSocketContext, NM_ITEM, NM_ITEM_MAKESKILL_RES );
						sInventory*          sinventory = &sendMsg->inventory;

						TB_INVENTORY*        table = &pInsert->mInventory;
						TB_INVENTORY*        inventory;
						bool                 newOrUpdate;

						if ( table->apply == InventoryApplyRemoved )
						{
							PostInventoryEvent( EVENT_INVENTORY_DELETE, heroIdx, table, "QUEST INSERT BY ITEM" );
						}
						else
						{
							inventory   = hero->SelectInventory( table->number );
							newOrUpdate = (inventory->idx == table->idx);
							inventory   = hero->XCopyInventory( inventory, table ); // (*inventory) = (*table);

							Inventory2sInventory( sinventory, inventory );
							sinventory++;

							PostInventoryEvent( newOrUpdate ? EVENT_INVENTORY_UPDATE : EVENT_INVENTORY_CREATE, heroIdx, inventory, "QUEST INSERT BY ITEM" );
						}

						SendMsgRoot( handle, sizeof( MSG_RES_MAKESKILL_ITEM ) );

						pClientInfo->request.inventoryUpdate = true;
					}
				}
				else
				{
					// 등록 오류시, 클라이언트 강제 종료.
					PostServerEvent( "WARNING - SQL_GAME_PROCESS_MAKESKILL_INSERT - CHARACTER_IDX(%u)", pClientInfo->characterIdx );
					CloseCID( perSocketContext->cid );
				}

				pClientInfo->skillDb.makeSkillDbUse = false;
			}
			break;
		case SQL_GAME_PROCESS_RECIPE_INSERT:
			if( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock			lock( &mCs );
				ClientInfo*		pClientInfo = (ClientInfo*)perSocketContext->buffer;

				RECIPE_INSERT* pInsert = (RECIPE_INSERT*)perIoContext->buffer;

				if( pInsert->retvalue == 0 )
				{
					cPlayer* hero = pClientInfo->hero;
					unsigned long heroIdx = hero->GetObjectID();
					hero->SetRecipe( pInsert->mMakeSkillIdx, pInsert->mRecipeIdx );

					/// 아이템 정보 갱신
					if ( pInsert->mInventory.idx != 0 )
					{
						HANDLE               handle     = NULL;
						MSG_RES_MAKESKILL_ITEM* sendMsg    = (MSG_RES_MAKESKILL_ITEM*)GetMsgRoot( &handle, perSocketContext, NM_ITEM, NM_ITEM_RECIPE_RES );
						sInventory*          sinventory = &sendMsg->inventory;

						TB_INVENTORY*        table = &pInsert->mInventory;
						TB_INVENTORY*        inventory;
						bool                 newOrUpdate;

						if ( table->apply == InventoryApplyRemoved )
						{
							PostInventoryEvent( EVENT_INVENTORY_DELETE, heroIdx, table, "QUEST INSERT BY ITEM" );
						}
						else
						{
							inventory   = hero->SelectInventory( table->number );
							newOrUpdate = (inventory->idx == table->idx);
							inventory   = hero->XCopyInventory( inventory, table ); // (*inventory) = (*table);

							Inventory2sInventory( sinventory, inventory );
							sinventory++;

							PostInventoryEvent( newOrUpdate ? EVENT_INVENTORY_UPDATE : EVENT_INVENTORY_CREATE, heroIdx, inventory, "QUEST INSERT BY ITEM" );
						}

						SendMsgRoot( handle, sizeof( MSG_RES_MAKESKILL_ITEM ) );

						pClientInfo->request.inventoryUpdate = true;
					}
				}
				else
				{
					// 등록 오류시, 클라이언트 강제 종료.
					PostServerEvent( "WARNING - SQL_GAME_PROCESS_RECIPE_INSERT - CHARACTER_IDX(%u)", pClientInfo->characterIdx );
					CloseCID( perSocketContext->cid );
				}

				pClientInfo->skillDb.makeSkillDbUse = false;
			}
			break;
		case SQL_GAME_PROCESS_CHARACTER_SKILLRESET:
			if( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock			lock( &mCs );
				ClientInfo*		pClientInfo = (ClientInfo*)perSocketContext->buffer;
				cPlayer* hero = pClientInfo->hero;

				CHARACTER_SKILL_RESET* pDb = (CHARACTER_SKILL_RESET*)perIoContext->buffer;

				HANDLE handle  = NULL;	
				if( pDb->mRetvalue == ERROR_SKILLRESET_SUCCESS )
				{
					/// 처리 성공
					hero->SetSkillReset( 0, pDb->mTable, pDb->mRowCount );
				}
				else
				{
					/// 처리 실패 메세지 발송
					MSG_SYN_PLAYER_SKILLRESET* sendMsg = (MSG_SYN_PLAYER_SKILLRESET*)GetMsgRoot( &handle, perSocketContext, 
						NM_PLAYER, NM_PLAYER_SKILLRESET_SYN );
					sendMsg->ErrorCode = pDb->mRetvalue;
					SendMsgRoot( handle, sizeof(MSG_RES_QUEST_ADD) );

					NETWORK2->PostServerEvent(" SQL_GAME_PROCESS_CHARACTER_SKILLRESET [%d]", pDb->mRetvalue );
				}

			}
			break;
		case SQL_GAME_PROCESS_RECIPE_GROUP_INSERT:
			if( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock			lock( &mCs );
				ClientInfo*		pClientInfo = (ClientInfo*)perSocketContext->buffer;

				RECIPE_GROUP_INSERT* pInsert = (RECIPE_GROUP_INSERT*)perIoContext->buffer;

				if( pInsert->mRetvalue == 0 )
				{
					cPlayer* hero = pClientInfo->hero;
					unsigned long heroIdx = hero->GetObjectID();
					hero->SetRecipeGroup( pInsert->mRecipeGroup, pInsert->mRowCnt );

					/// 아이템 정보 갱신
					if ( pInsert->mInventory.idx != 0 )
					{
						HANDLE               handle     = NULL;
						MSG_RES_MAKESKILL_ITEM* sendMsg    = (MSG_RES_MAKESKILL_ITEM*)GetMsgRoot( &handle, perSocketContext, NM_ITEM, NM_ITEM_RECIPE_RES );
						sInventory*          sinventory = &sendMsg->inventory;

						TB_INVENTORY*        table = &pInsert->mInventory;
						TB_INVENTORY*        inventory;
						bool                 newOrUpdate;

						if ( table->apply == InventoryApplyRemoved )
						{
							PostInventoryEvent( EVENT_INVENTORY_DELETE, heroIdx, table, "QUEST INSERT BY ITEM" );
						}
						else
						{
							inventory   = hero->SelectInventory( table->number );
							newOrUpdate = (inventory->idx == table->idx);
							inventory   = hero->XCopyInventory( inventory, table ); // (*inventory) = (*table);

							Inventory2sInventory( sinventory, inventory );
							sinventory++;

							PostInventoryEvent( newOrUpdate ? EVENT_INVENTORY_UPDATE : EVENT_INVENTORY_CREATE, heroIdx, inventory, "QUEST INSERT BY ITEM" );
						}

						SendMsgRoot( handle, sizeof( MSG_RES_MAKESKILL_ITEM ) );

						pClientInfo->request.inventoryUpdate = true;
					}
				}
				else
				{
					// 등록 오류시, 클라이언트 강제 종료.
					PostServerEvent( "WARNING - SQL_GAME_PROCESS_RECIPE_INSERT - CHARACTER_IDX(%u)", pClientInfo->characterIdx );
					CloseCID( perSocketContext->cid );
				}

				pClientInfo->skillDb.makeSkillDbUse = false;
			}
			break;
		case SQL_GAME_PROCESS_RECIPE_COOLTIME:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock        lock( &mCs );
				ClientInfo*    clientInfo   = (ClientInfo*)perSocketContext->buffer;
				RECIPE_COOLTIME* skillCoolTime = (RECIPE_COOLTIME*)perIoContext->buffer;

				if( skillCoolTime->mRetvalue != 0 )	
					PostServerEvent( "SQL_GAME_PROCESS_RECIPE_COOLTIME - DB[%d]", skillCoolTime->mRetvalue );

				clientInfo->complete.recipeCoolTime = false;	// DB 사용종료.
			}
			break;
		case SQL_GAME_PROCESS_MAKESKILL_SELECT:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock lock( &mCs );
				ClientInfo* pClientInfo = (ClientInfo*)perSocketContext->buffer;
				cPlayer* pHero = pClientInfo->hero;
				MAKESKILL_SELECT* pMakeSkillSelect = (MAKESKILL_SELECT*)perIoContext->buffer;

				/// 플레이어에 제조스킬을 담는다.
				if( pHero->InitMakeSkill( pMakeSkillSelect ) == false )
				{
					PostServerEvent("SQL_GAME_PROCESS_MAKESKILL_SELECT pHero[%d]->InitMakeSkill( pMakeSkillSelect ) == false", pHero->GetObjectID() );
				}

				pClientInfo->gameInDb.makeSkillList = false;
			}
			break;
		case SQL_GAME_PROCESS_MAKESKILL_DELETE:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock lock( &mCs );
				ClientInfo* pClientInfo = (ClientInfo*)perSocketContext->buffer;
				cPlayer* pHero = pClientInfo->hero;
				MAKESKILL_DELETE* pMakeSkillDelete = (MAKESKILL_DELETE*)perIoContext->buffer;

				if( pMakeSkillDelete->mRetvalue == 0 )
				{
					pHero->DelMakeSkill( pMakeSkillDelete->mMakeSkillIdx );
				}
				else
				{
					PostServerEvent("SQL_GAME_PROCESS_MAKESKILL_DELETE pMakeSkillDelete[%d,%d,%d]->mRetvalue != 0",
						pMakeSkillDelete->mCharacterIdx, pMakeSkillDelete->mMakeSkillIdx, pMakeSkillDelete->mRetvalue );

					ResMsgError( perSocketContext, NM_MAKESKILL, NM_MAKESKILL_DEL_RES, ERROR_MAKESKILL_DBERROR );
				}
				
				pClientInfo->skillDb.makeSkillDbUse = false;
			}
			break;
		case SQL_GAME_PROCESS_FORTUNE_SELECT:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock			lock( &mCs );
				ClientInfo*		clientInfo = (ClientInfo*)perSocketContext->buffer;
				cPlayer*		hero	   = clientInfo->hero;
				unsigned long	heroIdx	   = hero->GetObjectID();

				if( clientInfo->gameInDb.fortuneData == true )
				{
					FORTUNE_SELECT* fortuneSelect = (FORTUNE_SELECT*)perIoContext->buffer;

					// TIMESTAMP_STRUCT -> tm 로 변환
					TIMESTAMP_STRUCT* validThru = fortuneSelect->thru;
					
					/// 정보 복사
					TIMESTAMP_STRUCT* heroThru = 0;
					for( int i = 0; i < 5; ++i, ++validThru )
					{
						heroThru = hero->GetFortuneThru( i );
						if( heroThru )
						{
							*heroThru = *validThru;
						}
						else
						{
							/// 오류
							PostServerEvent( "SQL_GAME_PROCESS_FORTUNE_SELECT - not exist fortuneThru data, heroIdx = %d, index = %d", heroIdx, i );
							assert(0);
						}
					}

					/// 오늘의 한마디 정보 복사
					sPlayerExrInfo* exrInfo = hero->GetExrInfo();

					exrInfo->mTodayColor = fortuneSelect->color;
					wcscpy( exrInfo->mTodayWord, fortuneSelect->word ); 

					///
					clientInfo->gameInDb.fortuneData = false;
					clientInfo->complete.fortuneData = true;
				}
			}
			break;
		case SQL_GAME_PROCESS_FORTUNE_UPDATE:
			{
				cCSLock		  lock( &mCs );
				FORTUNE_UPDATE* fortuneUpdate = (FORTUNE_UPDATE*)perIoContext->buffer;
				
				/// 에러
				if( perIoContext->requestResult != IOCP_REQUEST_SUCCESS || fortuneUpdate->retvalue != 0 )
				{
					PostServerEvent( "SQL_GAME_PROCESS_FORTUNE_UPDATE - fail, heroIdx = %d", fortuneUpdate->characterIdx );
				}
			}
			break;
		case SQL_GAME_PROCESS_WHISPER_MEMBER_SELECT:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock			lock( &mCs );
				ClientInfo*		clientInfo = (ClientInfo*)perSocketContext->buffer;
				cPlayer*		hero	   = clientInfo->hero;
				unsigned long	heroIdx	   = hero->GetObjectID();

				if( clientInfo->commonDb.memberSelect == true )
				{
					WHISPERMEMBER_SELECT* memberSelect = (WHISPERMEMBER_SELECT*)perIoContext->buffer;

					if( perIoContext->requestResult == IOCP_REQUEST_SUCCESS )
					{
						if( memberSelect->channelNum > 0 )
						{
							/// 링크 아이템 정보 
							sInventory		inven;
							memset( &inven, 0, sizeof(inven) );

							TB_INVENTORY* tbInventory = hero->SelectInventory( memberSelect->slotIndex );
							if ( hero->IsInventory( tbInventory ) == true )
								Inventory2sInventory( &inven, tbInventory );

							/// 해당 유저가 다른 채널에 접속중임.
							if( PostChatWhisper( memberSelect->channelNum, heroIdx, hero->GetName(), memberSelect->recvName, memberSelect->message, &inven ) == false )
							{
								PostServerEvent( "FAIL - SQL_GAME_PROCESS_WHISPER_MEMBER_SELECT PostChatWhisper(%d,%d)", heroIdx, memberSelect->channelNum );
							}
						}
						else
						{
							/// 해당 유저가 다른 채널에 접속중이 아님
							// 보낸 캐릭터에게.
							HANDLE                handle  = NULL;
							MSG_RES_CHAT_WHISPER* sendMsg = (MSG_RES_CHAT_WHISPER*)GetMsgRoot( &handle, perSocketContext, NM_CHAT, NM_CHAT_WHISPER_RES );
							sendMsg->ErrorCode = ERROR_CHAT_WHISPER_DISCONNECT;
							SendMsgRoot( handle, sendMsg->GetMsgLength( ) );
						}
					}
					else
					{
						PostServerEvent( "SQL_GAME_PROCESS_WHISPER_MEMBER_SELECT heroIdx = %d", heroIdx );
					}

					clientInfo->commonDb.memberSelect = false;
				}
			}
			break;
		case SQL_GAME_PROCESS_NOTE_MEMBER_SELECT:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock			lock( &mCs );
				ClientInfo*		clientInfo = (ClientInfo*)perSocketContext->buffer;
				cPlayer*		hero	   = clientInfo->hero;
				unsigned long	heroIdx	   = hero->GetObjectID();

				if( clientInfo->commonDb.memberSelect == true )
				{
					NOTEMEMBER_SELECT* memberSelect = (NOTEMEMBER_SELECT*)perIoContext->buffer;

					if( perIoContext->requestResult == IOCP_REQUEST_SUCCESS )
					{
						/// 다른 채널에 접속중이면 쪽지 추가를 보내준다
						if( memberSelect->channelNum > 0 )
						{
							if( PostChatNote( memberSelect->channelNum, memberSelect->recvName, &memberSelect->table ) == false )
							{
								PostServerEvent( "FAIL - SQL_GAME_PROCESS_NOTE_MEMBER_SELECT PostChatNote(%d,%d)", heroIdx, memberSelect->channelNum );
							}
						}
					}
					else
					{
						PostServerEvent( "SQL_GAME_PROCESS_NOTE_MEMBER_SELECT heroIdx = %d", heroIdx );
					}

					clientInfo->commonDb.memberSelect = false;
				}
			}
			break;
		case SQL_GAME_PROCESS_CHARACTER_BLOCK_SELECT:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock                 lock( &mCs );
				CHARACTER_BLOCK_SELECT* characterBlockSelect = (CHARACTER_BLOCK_SELECT*)perIoContext->buffer;

				if ( characterBlockSelect->rowCount > 0 && characterBlockSelect->validTrue > 0 )
				{
					ClientInfo* clientInfo = (ClientInfo*)perSocketContext->buffer;
					cPlayer*    hero       = clientInfo->hero;

					hero->SetBlock( true, characterBlockSelect->validTrue );
				}
			}
			break;
		case SQL_GAME_PROCESS_CHARACTER_BLOCK_INSERT:
			if ( perSocketContext->socket == perIoContext->socket )
			{
				cCSLock                 lock( &mCs );
				CHARACTER_BLOCK_INSERT* characterBlockInsert = (CHARACTER_BLOCK_INSERT*)perIoContext->buffer;

				if ( perIoContext->requestResult == IOCP_REQUEST_SUCCESS )
				{
					cPlayer* player = OBJECTMANAGER->GetPlayer( characterBlockInsert->blockCharacterIdx );
					if ( player != NULL )
					{
						player->SetBlock( true, characterBlockInsert->validTrue );

						/// 유저에게 통보
						if( characterBlockInsert->validTrue != 0 )
						{
							HANDLE handle = NULL;
							if ( GetMsgRoot( &handle, player->GetConnectionIdx() , NM_CHEAT, NM_CHEAT_NO_CHAT_SYN ) != NULL )
								SendMsgRoot( handle, sizeof(MSG_SYN_CHEAT_NO_CHAT) );
						}
						else
						{
							HANDLE handle = NULL;
							if ( GetMsgRoot( &handle, player->GetConnectionIdx(), NM_CHEAT, NM_CHEAT_ALLOW_CHAT_SYN ) != NULL )
								SendMsgRoot( handle, sizeof(MSG_SYN_CHEAT_ALLOW_CHAT) );
						}

					}
				}
				else
				{
					PostServerEvent( "WARNING - SQL_GAME_PROCESS_CHARACTER_BLOCK_INSERT - BLOCK_CHARACTER_IDX(%u) / ADMIN_CHARACTER_IDX(%u)"
						            ,characterBlockInsert->blockCharacterIdx
									,characterBlockInsert->adminCharacterIdx );
				}
			}
			break;
		}
	} catch ( char* str ) {
		PostServerEvent( "Caught 'switch ( PerIoContext::iParam(=%d) )' exception type: '%s'. Throwing 'cGameProcess::CallbackComplete' exception", perIoContext->iParam, str );
		Sleep( 100 );
		throw;
	} catch ( ... ) {
		PostServerEvent( "In 'switch ( PerIoContext::iParam(=%d) )'. Throwing 'cGameProcess::CallbackComplete' exception.", perIoContext->iParam );
		Sleep( 100 );
		throw;
	}

	Verbose->CallbackComplete( &mCs, perIoContext );

	// 사용이 완료된 I/O Context는 회수한다.
	if ( perIoContext->iParam >= DEFAULT_SQL_ACCOUNT && perIoContext->iParam < MAX_SQL_ACCOUNT )
	{
		g_gameSrv->GetSQLAccount( )->Release( perIoContext );
	}
	else if ( perIoContext->iParam >= DEFAULT_SQL_GAME && perIoContext->iParam < MAX_SQL_GAME )
	{
		g_gameSrv->GetSQLGame( )->Release( perIoContext );
	}
	else if ( perIoContext->iParam >= DEFAULT_GAME_PROCESS && perIoContext->iParam < MAX_GAME_PROCESS )
	{
		DWORD error = 0;
		mIoContextPool->ReleaseIoContext( perIoContext /*false, &error*/ );
		if ( error != 0 )
			PostServerEvent( "WARNING - cGameProcess::CallbackComplete:Error(=0x%08xh)", error );
	}
	else
		PostServerEvent( "WARNING - cGameProcess::CallbackComplete:iParam(=0x%04dxh)", perIoContext->iParam );

	return true;
}

// SocketManager Method - 소켓(클라이언트) 관리.
void cGameProcess::SocketManager(DWORD currentTick)
{
	if ( mSocketContextPool != NULL )
	{
		cCSLock           lock( &mCs );
		PerSocketContext* socketContext = mSocketContextPool->GetPagedPoolUsage( );
		PerSocketContext* next          = NULL;

		ClientInfo*       clientInfo    = NULL;
		char*             recvBuf       = NULL;

		cPlayer*          hero          = NULL;
		unsigned long     heroIdx       = 0;

		while ( socketContext != NULL )
		{
			next       = socketContext->next;

			clientInfo = (ClientInfo*)socketContext->buffer;
			recvBuf    = (socketContext->buffer + socketContext->offset);

			try {
				// Receive 데이터 처리를 위한 코드.
				if ( socketContext->InternalHigh )
				{
					u_long& offset = socketContext->Internal;
					u_long& length = socketContext->InternalHigh;

					do {
						Packet* packet = (Packet*)(recvBuf + offset);

						if ( BatchComplete( currentTick, socketContext, packet ) == false )
						{
							Close( socketContext );
							break;
						}

						offset += packet->tlen;
					} while ( offset < length );

					memset( recvBuf, 0, length );
					offset = 0;
					length = 0;
				}
			} catch ( ... ) {
				PostServerEvent( "In BatchComplete Method - MsgRoot(=%d,%d,%d). Throwing 'cGameProcess::SocketManager' exception.",
								 mLastCategory,
								 mLastProtocol,
								 mLastPacketSize );
				Sleep( 100 );
				throw;
			}

			// Close Socket On.
			if ( !socketContext->status.connectionDead )
			{
				/*-- 1. SocketContext 처리.
				*/

				// Time To Live.
				if ( socketContext->timeToLive < currentTick )
				{
					Close( socketContext );
				}

				/*-- 2. Game 처리.
				*/
				hero    = clientInfo->hero;
				heroIdx = (hero != NULL ? hero->GetObjectID( ) : 0);

				/*-- Game In/Out 전용 처리.
				*/
				if ( clientInfo->complete.gameIn )
				{
					/*-- 인벤토리(업데이트) 처리 - Select / Move / Delete / Remove / Use / Sell / Buy / Move2.
					     인벤토리(착용정보) 처리 - Select / Move / Switch.
						 퀘 스 트(업데이트) 처리 - Reward
					*/
					if ( clientInfo->complete.inventory )
					{
						if ( clientInfo->request.inventoryUpdate )
						{
							// 아이템 누적 무게.
							hero->CalcItemWeight( );

							clientInfo->request.inventoryUpdate = false;
						}
						if ( clientInfo->request.characterEquip )
						{
							// 캐릭터 장비 아이템 정보 갱신.
							hero->EquipItems( );

							STATUSCALC->CalcPlayerExtensionGlobal( hero->GetObject() );

							clientInfo->request.characterEquip  = false;
							clientInfo->complete.characterEquip = true;
						}
					}
					// 맵이동 처리
					if ( hero->IsMapChange( ) && !clientInfo->commonDatabase && !clientInfo->inventoryDatabase && !clientInfo->skillDatabase )
					{
						if ( mpGridManager->RemovePlayer( hero ) == true )
						{
							// 맵이동 응답 메시지 - 성공.
							clientInfo->complete.gameIn = false;
							hero->ApplyMapTargetPos( );				// 캐릭터 맵 이동 적용.
							clientInfo->gameInDb.heroInfo = true;

							/*-- 1. 캐릭터정보(재전송). -- */

							// 캐릭터 최신정보(Character Select) - 080908 PKH 
							hero->SendPlayerInfo( NM_PLAYER, NM_PLAYER_MAPCHANGE_RES, (ULONG_PTR)socketContext );
							hero->SendMoney( hero->GetObject() );
							hero->SendDeposit( hero->GetObject( ) );
							clientInfo->request.characterOptionSend = true;		// Character Select.

							// 캐릭터 관련-DB 검색.
							clientInfo->request.inventorySend         = true;	// InventorySelect( socketContext, heroIdx );
								clientInfo->request.inventoryUpdate   = true;	// Sub InventorySelect
								clientInfo->request.characterEquip    = true;	// Sub InventorySelect
							clientInfo->request.inventoryCooltimeSend = true;	// InventoryCooltime( socketContext, heroIdx );
							clientInfo->request.shortcutSend          = true;	// ShortcutSelect( socketContext, heroIdx );
							clientInfo->request.questSend             = true;	// QuestSelect( socketContext, heroIdx );
							clientInfo->request.titleSend             = true;	// TitleSelect( socketContext, heroIdx );

							SKILLMANAGER->SendMsgSkillHaveToPlayer( socketContext->cid, heroIdx );	// SkillSelect( socketContext, heroIdx );
							SKILLMANAGER->SendMsgInfluenceToPlayer( socketContext->cid, heroIdx );	// SkillInfluenceSelect( socketContext, heroIdx );
							/// 레시피 보유목록 발송
							hero->SendMsgRecipeList();

							/*-- 2. LOAD DATA(재전송). --*/

							// MONSTER & NPC MODEL.
							mpObjectManager->SendMsgMonsterModelNumberToPlayer( socketContext->cid, hero->GetMapNumber( ) );
							mpObjectManager->SendMsgNpcModelNumberToPlayer( socketContext->cid, hero->GetMapNumber( ) );

							/*-- 3. 캐릭터 상태설정 --*/

							// 대기상태로 변경
							if ( hero->ChangeState( eOBJECT_STATE_IDLE ) == false )
							{
								assert(NULL);
								PostServerEvent( "hero[%d,%d,%d]->MapChange( true ) - ChangeState( eOBJECT_STATE_IDLE ) = false",
												 heroIdx,
												 hero->GetState(),
												 hero->GetStateStop() );
							}
							// 평화 대기 풀림
							hero->SetStateIdle( eIDLE_NORMAL );
						}
						else
						{
							// 맵이동 응답 메시지 - 실패.
							ResMsgError( socketContext, NM_PLAYER, NM_PLAYER_MAPCHANGE_RES, 1 );
						}

						hero->MapChange( false );
					}
				}
				else
				{
					// 채널리스트.
					if ( clientInfo->request.channelList && clientInfo->delay.channelList < currentTick )
					{
						clientInfo->request.channelList = !ResChannelList( socketContext );
					}
					// 캐릭터리스트.
					if ( clientInfo->request.characterList && clientInfo->delay.characterList < currentTick )
					{
						clientInfo->commonDb.characterList = CharacterList( socketContext, clientInfo->memberIdx );
						clientInfo->request.characterList = !clientInfo->commonDb.characterList;
					}
					// 캐릭터 선택.
					if ( clientInfo->request.characterSelect )
					{
						// DATABASE 요청.
						HANDLE            handle          = NULL;
						CHARACTER_SELECT* characterSelect = (CHARACTER_SELECT*)GetSQL( &handle, SQL_GAME_PROCESS_CHARACTER_SELECT );

						characterSelect->characterIdx = clientInfo->characterIdx;
						characterSelect->userIdx      = clientInfo->memberIdx;

						clientInfo->commonDb.characterSelect = SendSQL( socketContext, handle, sizeof(CHARACTER_SELECT) );
						clientInfo->request.characterSelect = false;
					}
					if ( clientInfo->gameInDatabase == 0x00000001 )
					{
						STATUSCALC->CalcPlayerExtensionGlobal( hero->GetObject() );

						// hero 정보 발송
						hero->SendHeroInfo( );

                        clientInfo->gameInDb.heroInfo = false;
					}
					// 게임-인 처리.
					if ( clientInfo->request.gameIn == true && clientInfo->gameInDatabase == 0 )
					{
						if ( mpGridManager->GetPlayer( heroIdx ) == NULL )
						{
							// 게임인 응답 메시지 - 성공.
							HANDLE          handle  = NULL;
							MSG_RES_GAMEIN* sendMsg = (MSG_RES_GAMEIN*)GetMsgRoot( &handle, socketContext, NM_USER, NM_USER_GAMEIN_RES );
							sendMsg->ErrorCode = 0;
							SendMsgRoot( handle, sizeof(MSG_RES_GAMEIN) );

							if ( mpGridManager->AddPlayer( hero ) == true )
							{
								// gameIn on.
								clientInfo->complete.gameIn               = true;
								clientInfo->complete.characterUpdate      = true;
								clientInfo->complete.skillInfluenceInsert = true;
								clientInfo->complete.skillCoolTime        = true;
								clientInfo->complete.recipeCoolTime       = true;

								/*-- 인벤토리(업데이트) 처리 - Select / Move / Delete / Remove / Use / Sell / Buy / Move2.
									 인벤토리(착용정보) 처리 - Select / Move / Switch.
								 	 퀘 스 트(업데이트) 처리 - Reward
								*/
								if ( clientInfo->complete.inventory )
								{
									if ( clientInfo->request.inventoryUpdate )
									{
										// 아이템 누적 무게.
										hero->CalcItemWeight( );

										clientInfo->request.inventoryUpdate = false;
									}
									if ( clientInfo->request.characterEquip )
									{
										// 캐릭터 장비 아이템 정보 갱신.
										hero->EquipItems( );

										STATUSCALC->CalcPlayerExtensionGlobal( hero->GetObject() );

										/// 로그인시 스텟 계산후에 db에서 읽어왔던 hp/mp값을 적용
										hero->ApplyInitRest();

										clientInfo->request.characterEquip  = false;
										clientInfo->complete.characterEquip = true;
									}
								}

								// [쪽지 & 친구] 리스트 검색 및 전송.
								if ( !clientInfo->complete.noteSelect ) NoteSelect( socketContext, heroIdx );
								if ( !clientInfo->complete.friendSelect ) FriendSelect( socketContext, heroIdx );

							}
							if ( hero->IsPvPJoin() == false )
							{
								if( mPVPData.timer != 0 && mPVPData.timer - mAccumTime  > 0 && hero->GetForceType() != eFORCETYPE_NONE )
								{
									if( hero->CheckPvPJoin() == true )
									{
										sDMInfo* pDMInfo = PVPSCRIPT->GetDmInfo( 0, hero->GetLevel() );
										if( pDMInfo != NULL )
										{
											/// pvp 참여 중이 아닌경우 참여 여부를 물어봄
											HANDLE          handle  = NULL;
											MSG_SYN_PVP_DM_JOIN* sendMsg = (MSG_SYN_PVP_DM_JOIN*)GetMsgRoot( &handle, socketContext, NM_PVP, NM_PVP_DM_JOIN_SYN );
											sendMsg->mLeftTime = mPVPData.timer - mAccumTime;
											sendMsg->mMinLevel = pDMInfo->mMinLevel;
											sendMsg->mMaxLevel = pDMInfo->mMaxLevel;
											SendMsgRoot( handle, sizeof(MSG_SYN_PVP_DM_JOIN) );
										}
									}
								}
							}
							else
							{
								/// 접속한 플레이어에게 자신의 팀원 정보 발송
								cDeathMatchObject* pDMObject = PVPMANAGER->GetPvPDMObject( hero->GetPvPDMIdx() );
								if( pDMObject != NULL )
									pDMObject->SendTeamList( hero );
							}
						}
						else
						{
							// 게임인 응답 메시지 - 실패.
							ResMsgError( socketContext, NM_USER, NM_USER_GAMEIN_RES, 1 );
						}
						clientInfo->request.gameIn = false;
					}
					if ( clientInfo->complete.characterSelect && clientInfo->request.joinInstantDungeon )
					{
						// 맵이동 처리
						if ( hero->IsMapChange( ) && !clientInfo->commonDatabase && !clientInfo->inventoryDatabase && !clientInfo->skillDatabase )
						{
							hero->ApplyMapTargetPos( );				// 캐릭터 맵 이동 적용.
							clientInfo->gameInDb.heroInfo = true;

							/*-- 1. 캐릭터정보(재전송). -- */

							// 캐릭터 최신정보(Character Select) - 080908 PKH 
							hero->SendPlayerInfo( NM_PVP, NM_PVP_DM_JOIN_RES, (ULONG_PTR)socketContext );

							hero->SendMoney( hero->GetObject() );
							hero->SendDeposit( hero->GetObject( ) );
							clientInfo->request.characterOptionSend = true;		// Character Select.

							// 캐릭터 관련-DB 검색.
							clientInfo->request.inventorySend         = true;	// InventorySelect( socketContext, heroIdx );
							clientInfo->request.inventoryUpdate   = true;	// Sub InventorySelect
							clientInfo->request.characterEquip    = true;	// Sub InventorySelect
							clientInfo->request.inventoryCooltimeSend = true;	// InventoryCooltime( socketContext, heroIdx );
							clientInfo->request.shortcutSend          = true;	// ShortcutSelect( socketContext, heroIdx );
							clientInfo->request.questSend             = true;	// QuestSelect( socketContext, heroIdx );
							clientInfo->request.titleSend             = true;	// TitleSelect( socketContext, heroIdx );

							SKILLMANAGER->SendMsgSkillHaveToPlayer( socketContext->cid, heroIdx );	// SkillSelect( socketContext, heroIdx );
							SKILLMANAGER->SendMsgInfluenceToPlayer( socketContext->cid, heroIdx );	// SkillInfluenceSelect( socketContext, heroIdx );
							/// 레시피 보유목록 발송
							hero->SendMsgRecipeList();

							/*-- 2. LOAD DATA(재전송). --*/

							// MONSTER & NPC MODEL.
							mpObjectManager->SendMsgMonsterModelNumberToPlayer( socketContext->cid, hero->GetMapNumber( ) );
							mpObjectManager->SendMsgNpcModelNumberToPlayer( socketContext->cid, hero->GetMapNumber( ) );

							/*-- 3. 캐릭터 상태설정 --*/

							// 대기상태로 변경
							if ( hero->ChangeState( eOBJECT_STATE_IDLE ) == false )
							{
								assert(NULL);
								PostServerEvent( "hero[%d,%d,%d]->MapChange( true ) - ChangeState( eOBJECT_STATE_IDLE ) = false",
												 heroIdx,
												 hero->GetState(),
												 hero->GetStateStop() );
							}
							// 평화 대기 풀림
							hero->SetStateIdle( eIDLE_NORMAL );
							hero->MapChange( false );

							clientInfo->request.joinInstantDungeon = false;
						}
					}
					if ( clientInfo->complete.characterSelect && clientInfo->request.joinMap )
					{
						// 맵이동 처리
						if ( hero->IsMapChange( ) && !clientInfo->commonDatabase && !clientInfo->inventoryDatabase && !clientInfo->skillDatabase )
						{
							hero->ApplyMapTargetPos( );				// 캐릭터 맵 이동 적용.
							clientInfo->gameInDb.heroInfo = true;

							/*-- 1. 캐릭터정보(재전송). -- */

							// 캐릭터 최신정보(Character Select) - 080908 PKH 
							hero->SendPlayerInfo( NM_PLAYER, NM_PLAYER_MAPCHANGE_RES, (ULONG_PTR)socketContext );

							hero->SendMoney( hero->GetObject() );
							hero->SendDeposit( hero->GetObject( ) );
							clientInfo->request.characterOptionSend = true;		// Character Select.

							// 캐릭터 관련-DB 검색.
							clientInfo->request.inventorySend         = true;	// InventorySelect( socketContext, heroIdx );
							clientInfo->request.inventoryUpdate   = true;	// Sub InventorySelect
							clientInfo->request.characterEquip    = true;	// Sub InventorySelect
							clientInfo->request.inventoryCooltimeSend = true;	// InventoryCooltime( socketContext, heroIdx );
							clientInfo->request.shortcutSend          = true;	// ShortcutSelect( socketContext, heroIdx );
							clientInfo->request.questSend             = true;	// QuestSelect( socketContext, heroIdx );
							clientInfo->request.titleSend             = true;	// TitleSelect( socketContext, heroIdx );

							SKILLMANAGER->SendMsgSkillHaveToPlayer( socketContext->cid, heroIdx );	// SkillSelect( socketContext, heroIdx );
							SKILLMANAGER->SendMsgInfluenceToPlayer( socketContext->cid, heroIdx );	// SkillInfluenceSelect( socketContext, heroIdx );
							/// 레시피 보유목록 발송
							hero->SendMsgRecipeList();

							/*-- 2. LOAD DATA(재전송). --*/

							// MONSTER & NPC MODEL.
							mpObjectManager->SendMsgMonsterModelNumberToPlayer( socketContext->cid, hero->GetMapNumber( ) );
							mpObjectManager->SendMsgNpcModelNumberToPlayer( socketContext->cid, hero->GetMapNumber( ) );

							/*-- 3. 캐릭터 상태설정 --*/

							// 대기상태로 변경
							if ( hero->ChangeState( eOBJECT_STATE_IDLE ) == false )
							{
								assert(NULL);
								PostServerEvent( "hero[%d,%d,%d]->MapChange( true ) - ChangeState( eOBJECT_STATE_IDLE ) = false",
												 heroIdx,
												 hero->GetState(),
												 hero->GetStateStop() );
							}
							// 평화 대기 풀림
							hero->SetStateIdle( eIDLE_NORMAL );
							hero->MapChange( false );

							clientInfo->request.joinMap = false;
						}
					}
				}
				// 게임아웃
				if ( clientInfo->request.gameOut )
				{
					if ( UpdateComplete( socketContext ) )
					{
						ResMsgError( socketContext, NM_PLAYER, NM_PLAYER_GAMEOUT_RES, ERROR_GAMEFINISH_SUCCESS );

						// 게임서버 인증 백업.
						u_long memberIdx = clientInfo->memberIdx;
						u_long loginIdx  = clientInfo->loginIdx;

						memset( clientInfo, 0, sizeof(ClientInfo) );

						clientInfo->complete.check = true;
						clientInfo->memberIdx = memberIdx;
						clientInfo->loginIdx  = loginIdx;
					}
				}
				// 인던이동
				if ( clientInfo->request.gotoInstantDungeon )
				{
					if ( UpdateComplete( socketContext ) )
					{
						// 게임서버 인증 백업.
						u_long memberIdx    = clientInfo->memberIdx;
						u_long loginIdx     = clientInfo->loginIdx;
						u_long characterIdx = clientInfo->characterIdx;

						memset( clientInfo, 0, sizeof(ClientInfo) );

						clientInfo->complete.check = true;
						clientInfo->memberIdx    = memberIdx;
						clientInfo->loginIdx     = loginIdx;
						clientInfo->characterIdx = characterIdx;

						// 인던이동기록.
						HANDLE                handle             = NULL;
						GOTO_INSTANT_DUNGEON* gotoInstantDungeon = (GOTO_INSTANT_DUNGEON*)GetSQL( &handle, SQL_GAME_PROCESS_GOTO_INSTANT_DUNGEON );

						gotoInstantDungeon->loginIdx       = loginIdx;
						gotoInstantDungeon->memberIdx      = memberIdx;
						gotoInstantDungeon->serverNum      = HIWORD( mPVPData.cid );
						gotoInstantDungeon->channelNum     = LOWORD( mPVPData.cid );;
						gotoInstantDungeon->lastChannel    = mChannelNum;
						gotoInstantDungeon->characterIdx   = characterIdx;
						gotoInstantDungeon->instantDungeon = 1;

						clientInfo->commonDb.gotoInstantDungeon = SendSQL( socketContext, handle, sizeof(GOTO_INSTANT_DUNGEON) );
						clientInfo->request.gotoInstantDungeon = false;
					}
				}
				if ( clientInfo->request.returnToMap )
				{
					if ( UpdateComplete( socketContext ) )
					{
						// 게임서버 인증 백업.
						u_long memberIdx    = clientInfo->memberIdx;
						u_long loginIdx     = clientInfo->loginIdx;
						u_long lastChannel  = clientInfo->lastChannel;
						u_long characterIdx = clientInfo->characterIdx;

						memset( clientInfo, 0, sizeof(ClientInfo) );

						clientInfo->complete.check = true;
						clientInfo->memberIdx    = memberIdx;
						clientInfo->loginIdx     = loginIdx;
						clientInfo->lastChannel  = lastChannel;
						clientInfo->characterIdx = characterIdx;

						// 맵귀환.
						HANDLE         handle      = NULL;
						RETURN_TO_MAP* returnToMap = (RETURN_TO_MAP*)GetSQL( &handle, SQL_GAME_PROCESS_RETURN_TO_MAP );

						returnToMap->loginIdx       = loginIdx;
						returnToMap->memberIdx      = memberIdx;
						returnToMap->serverNum      = mServerNum;
						returnToMap->channelNum     = lastChannel;
						returnToMap->lastChannel    = mChannelNum;
						returnToMap->characterIdx   = characterIdx;
						returnToMap->instantDungeon = 1;

						clientInfo->commonDb.returnToMap = SendSQL( socketContext, handle, sizeof(RETURN_TO_MAP) );
						clientInfo->request.returnToMap = false;
					}
				}
				// 인벤토리 발송.
				if ( clientInfo->complete.inventory && clientInfo->request.inventorySend )
				{
					hero->SendInventory( (ULONG_PTR)socketContext );
					clientInfo->request.inventorySend = false;
				}
				// 인벤토리 쿨타임 발송.
				if ( clientInfo->complete.inventoryCooltime && clientInfo->request.inventoryCooltimeSend )
				{
					hero->SendInventoryCooltime( (ULONG_PTR)socketContext );
					clientInfo->request.inventoryCooltimeSend = false;
				}
				// 퀘스트 발송 : 퀘스트관련 작업이 가능하고, 요청이 있으면 발송
				if( clientInfo->complete.quest && clientInfo->request.questSend )
				{
					hero->SendQuest( (ULONG_PTR)socketContext );
					clientInfo->request.questSend = false;
				}
				// 호칭 발송
				if( clientInfo->complete.title && clientInfo->request.titleSend )
				{
					hero->SendHaveTitleList( (ULONG_PTR)socketContext );
					clientInfo->request.titleSend = false;
				}
				// 옵션 발송.
				if ( clientInfo->complete.characterOption && clientInfo->request.characterOptionSend )
				{
					HANDLE                       handle  = NULL;
					MSG_RES_PLAYER_OPTION_VALUE* sendMsg = (MSG_RES_PLAYER_OPTION_VALUE*)GetMsgRoot( &handle, socketContext, NM_PLAYER, NM_PLAYER_OPTION_VALUE_RES );
					sendMsg->option1 = clientInfo->optionData1;
					SendMsgRoot( handle, sizeof(MSG_RES_PLAYER_OPTION_VALUE) );
					clientInfo->request.characterOptionSend = false;
				}
				// 단축창 발송.
				if ( clientInfo->complete.shortcut && clientInfo->request.shortcutSend )
				{
					HANDLE                        handle  = NULL;
					MSG_RES_PLAYER_SHORTCUT_LIST* sendMsg = (MSG_RES_PLAYER_SHORTCUT_LIST*)GetMsgRoot( &handle, socketContext, NM_PLAYER, NM_PLAYER_SHORTCUT_LIST_RES );
					memcpy( sendMsg->mShortcut, clientInfo->shortcut.binary, sizeof(sendMsg->mShortcut) );
					SendMsgRoot( handle, sizeof(MSG_RES_PLAYER_SHORTCUT_LIST) );
					clientInfo->request.shortcutSend = false;
				}
				// 친구목록 발송.
				if ( clientInfo->complete.friendSelect && clientInfo->delay.firendSend < currentTick )
				{
					if ( hero->UpdateFriend( ) == true )						// 수정목록 검사.
						hero->SendFriendList( (ULONG_PTR)socketContext );
					clientInfo->delay.firendSend = currentTick + 5000;			// 다음검사 시간.
				}
				// 퀘스트db 저장
				if ( clientInfo->complete.quest && currentTick > clientInfo->delay.questSave )
				{
					hero->SaveQuestProgress( (ULONG_PTR)socketContext );		// 퀘스트 진행정보 저장.
					clientInfo->delay.questSave = currentTick + QUEST_SAVETIME;	// 다음검사 시간.
				}
			}
			else
				socketContext->status.closeSocket = UpdateComplete( socketContext, true );

			// SocketContext 회수.
			if ( socketContext->status.closeSocket )
			{
				Verbose->CloseSocket( &mCs, socketContext );
				mSocketContextPool->ReleasePerSocketContext( socketContext );
			}
			// SocketContext 링크.
			socketContext = next;
		} // while ( socketContext != NULL )
	}
}

// ChannelManager Method - 채널 관리 - 서버상태 발송.
void cGameProcess::ChannelManager(DWORD currentTick)
{
	cSender* sender = g_gameSrv->GetSender( );
	if ( sender != NULL && mStatusSyncTick < currentTick )
	{
		long cid = MAKECID((WORD)mServerNum, (WORD)mChannelNum);
		sender->PostChStatus( cid, (BYTE)mStatus );
		mStatusSyncTick = (currentTick + STATUS_SYNC_TIME);
	}
}

// GameManager Method
void cGameProcess::GameManager(DWORD currentTick)
{
	cCSLock       lock( &mCs );

	LARGE_INTEGER frequency;
	LARGE_INTEGER beginTime;
	LARGE_INTEGER endTime;
	__int64       elapsed;

	mElapsedTime = TIMER->GetDeltaTime();
	mAccumTime   += mElapsedTime;

	QueryPerformanceFrequency( &frequency );

	// Begin - ObjectManager
	QueryPerformanceCounter( &beginTime );

	try {
		mpObjectManager->Process( mElapsedTime, mAccumTime );
	} catch ( ... ) {
		PostServerEvent( "In cObjectManager::Process Method. Throwing 'cGameProcess::GameManager' exception." );
		throw;
	}

	QueryPerformanceCounter( &endTime );

	elapsed        = endTime.QuadPart- beginTime.QuadPart; 
	mDuringObject += (double)elapsed / (double)frequency.QuadPart;

	// Begin - AIManager
	QueryPerformanceCounter( &beginTime );

	try {
		mpAIManager->Process( mElapsedTime, mAccumTime );
	} catch ( ... ) {
		PostServerEvent( "In cAIManager::Process Method. Throwing 'cGameProcess::GameManager' exception." );
		throw;
	}

	QueryPerformanceCounter( &endTime );

	elapsed    = endTime.QuadPart- beginTime.QuadPart; 
	mDuringAI += (double)elapsed / (double)frequency.QuadPart;

	// Begin - SkillManager
	QueryPerformanceCounter( &beginTime );

	try {
		mpSkillManager->Process( mElapsedTime, mAccumTime );
	} catch ( ... ) {
		PostServerEvent( "In cSkillManager::Process Method. Throwing 'cGameProcess::GameManager' exception." );
		throw;
	}

	QueryPerformanceCounter( &endTime );

	elapsed       = endTime.QuadPart- beginTime.QuadPart; 
	mDuringSkill += (double)elapsed / (double)frequency.QuadPart;

	// Begin - DuelManager
	QueryPerformanceCounter( &beginTime );

	try {
		mpDuelManager->Process( mElapsedTime, mAccumTime );
	} catch ( ... ) {
		PostServerEvent( "In cDuelManager::Process Method. Throwing 'cGameProcess::GameManager' exception." );
		throw;
	}

	QueryPerformanceCounter( &endTime );

	elapsed      = endTime.QuadPart- beginTime.QuadPart; 
	mDuringDuel += (double)elapsed / (double)frequency.QuadPart;

	// Begin - PvPManager
	QueryPerformanceCounter( &beginTime );

	try {
		if ( mChannelNum == CHN_11 )
			mpPVPManager->Process( mElapsedTime, mAccumTime );
	} catch ( ... ) {
		PostServerEvent( "In cPVPManager::Process Method. Throwing 'cGameProcess::GameManager' exception." );
		throw;
	}

	QueryPerformanceCounter( &endTime );

	elapsed      = endTime.QuadPart- beginTime.QuadPart; 
	mDuringPVP += (double)elapsed / (double)frequency.QuadPart;

	// Begin - PartyManager
	QueryPerformanceCounter( &beginTime );

	try {
		mpPartyManager->Process( mElapsedTime, mAccumTime );
	} catch ( ... ) {
		PostServerEvent( "In cPVPManager::Process Method. Throwing 'cGameProcess::GameManager' exception." );
		throw;
	}

	QueryPerformanceCounter( &endTime );

	elapsed      = endTime.QuadPart- beginTime.QuadPart; 
	mDuringPVP += (double)elapsed / (double)frequency.QuadPart;

	// Begin - GuildManager
	QueryPerformanceCounter( &beginTime );

	try {
		mpGuildManager->Process( mElapsedTime, mAccumTime );
	} catch ( ... ) {
		PostServerEvent( "In cPVPManager::Process Method. Throwing 'cGameProcess::GameManager' exception." );
		throw;
	}

	QueryPerformanceCounter( &endTime );

	elapsed      = endTime.QuadPart- beginTime.QuadPart; 
	mDuringPVP += (double)elapsed / (double)frequency.QuadPart;

	// Begin - GridManager
	QueryPerformanceCounter( &beginTime );

	try {
		mpGridManager->Process( currentTick );
	} catch ( ... ) {
		PostServerEvent( "In cGridManager::Process Method. Throwing 'cGameProcess::GameManager' exception." );
		throw;
	}

	QueryPerformanceCounter( &endTime );

	elapsed      = endTime.QuadPart- beginTime.QuadPart; 
	mDuringGrid += (double)elapsed / (double)frequency.QuadPart;
}

// BackendThreadBegin Method
void cGameProcess::BackendThreadBegin(void)
{
	while ( true )
	{
		DWORD currTickCount = GetTickCount( );
		{
			cCSLock lock( &mCs );

			// 서버종료, Thread를 나간다.
			if ( mEndServer == true )
				break; // while ( true )

			if ( mComplete.startLog == false )
			{
				mComplete.startLog = PostServerEvent( "GameProcess가 '%s:%d'에서 시작합니다.", inet_ntoa( mAddr.sin_addr ), mPort );
			}
			//else if ( mComplete.socketContext == false )
			//{
			//	mComplete.socketContext = mSocketContextPool->DefaultWorkingSize( 1024 );
			//}
			//else if ( mComplete.ioContext == false )
			//{
			//	mComplete.ioContext = mIoContextPool->DefaultWorkingSize( 16384 );
			//}
			else if ( !mComplete.importItemTables )
			{
				if ( !mRequest.importItemTables )
				{
					mRequestImport.itemDefine       = VerifyItemDefine( );
					mRequestImport.itemAbility      = VerifyItemAbility( );
					mRequestImport.itemCoolitme2    = VerifyItemCooltime2( );
					mRequestImport.itemLimit        = VerifyItemLimit( );
					mRequestImport.itemCard         = VerifyItemCard( );
					mRequestImport.itemCardSlot     = VerifyItemCardSlot( );
					mRequestImport.itemTarot        = VerifyItemTarot( );
					mRequestImport.itemEnhanced     = VerifyItemEnhanced( );
					mRequestImport.itemEnhancedRate = VerifyItemEnhancedRate( );
					mRequestImport.itemDisjoint     = VerifyItemDisjoint( );
					mRequestImport.itemChange       = VerifyItemChange( );
					mRequestImport.guild			= VerifyGuild( );

					mRequest.importItemTables = (mRequestImportData == 0xFFF);
				}
				else
					mComplete.importItemTables = (mCompleteImportData == 0xFFF);
			}
			else if ( mComplete.verifyDefaultItems == false )
			{
				if ( mRequest.verifyDefaultItems == false )
				{
					mRequest.verifyDefaultItems = VerifyDefaultItems( );
					if ( !mRequest.verifyDefaultItems )
						PostServerEvent( "Default Items Table 검사를 시작할 수 없습니다." );
				}
			}
			else if ( mComplete.channelCheck == false )
			{
				if ( mRequest.channelCheck == false )
				{
					mRequest.channelCheck = ChannelCheck( (short)mServerNum, (short)mChannelNum, mServerInetnum, inet_addr( mServerInetnum ), mPort );
					if ( !mRequest.channelCheck )							  
						PostServerEvent( "ServerNum(=%d)의 채널번호를 요청할 수 없습니다.", mServerNum );
				}
			}
			else if ( mComplete.initGameDB == false )
			{
				if ( mRequest.initGameDB == false )
				{
					mRequest.initGameDB = InitGameDB( );
					if ( !mRequest.initGameDB )
						PostServerEvent( "ServerNum(=%d)::ChannelNum(=%d)의 Game Database 초기화를 요청할 수 없습니다.", mServerNum, mChannelNum );
				}
			}
			else if ( mComplete.channelSyn == false )
			{
				ChannelManager( currTickCount );
			}
			else if( mComplete.guildConnectSyn == false )
			{
				mComplete.guildConnectSyn = PostGuildRequestConnect();
			}
			else
				break; // while ( true )
		}

		// I/O Context 처리.
		IoContextPresent( );

		// FPS 처리.
		BackendThreadFps( currTickCount );
	}
}

// BackendThreadEnd Method
void cGameProcess::BackendThreadEnd(void)
{
	PostServerEvent( "Begin - cGameProcess::BackendThreadEnd Close And Update" );
	Sleep( 100 );

	// 1. 업데이트.
	if ( mSocketContextPool != NULL )
	{
		PerSocketContext* socketContext = NULL;
		PerSocketContext* next;

		// 1-1. 통신차단
		socketContext = mSocketContextPool->GetPagedPoolUsage( );

		while ( socketContext != NULL )
		{
			Close( socketContext );
			socketContext = socketContext->next;
		}

		// 1-2. 안전종료.
		while ( mSocketContextPool->GetPagedPoolUsage( ) )
		{
			EnterCriticalSection( &mCs );

			socketContext = mSocketContextPool->GetPagedPoolUsage( );

			while ( socketContext != NULL )
			{
				next = socketContext->next;

				if ( UpdateComplete( socketContext, true, true ) )
				{
					mSocketContextPool->ReleasePerSocketContext( socketContext );
				}

				socketContext = next;
			}

			LeaveCriticalSection( &mCs );
			Sleep( 5 );
		}
	}

	PostServerEvent( "End - cGameProcess::BackendThreadEnd Close And Update" );
	Sleep( 100 );

	// 2. 서버종료.
	while ( true )
	{
		DWORD currTickCount = GetTickCount( );

		if ( mComplete.guildDB == false )
		{
			if ( mRequest.guildDB == false )
				mComplete.guildDB = mpGuildManager->SaveAllDB() == false ? true : true;
		}
		else if ( mComplete.shutdownGameDB == false )
		{
			if ( mRequest.shutdownGameDB == false )
				mRequest.shutdownGameDB = ShutdownGameDB( );
		}
		else if ( mComplete.shutdownAccountDB == false )
		{
			if ( mRequest.shutdownAccountDB == false )
				mRequest.shutdownAccountDB = ShutdownAccountDB( );
		}
		else if ( mComplete.endLog == false )
		{
			SIZE_T quotaPagedPoolUsage;
			SIZE_T quotaNonePagedPoolUsage;
			SIZE_T workingSetSize;

			// SQLAccount::IoContextPool 메모리 사용량
			g_gameSrv->GetSQLAccount( )->GetIoContextPool( quotaPagedPoolUsage, quotaNonePagedPoolUsage, workingSetSize );
			PostServerEvent( "cSQLAccount - IoContextPool Working Set Size (=%d), Quota Paged Pool Usage (=%d), Quota None Paged Pool Usage (=%d).",
							 workingSetSize,
							 quotaPagedPoolUsage,
							 quotaNonePagedPoolUsage );

			// SQLGame::IoContextPool 메모리 사용량
			g_gameSrv->GetSQLGame( )->GetIoContextPool( quotaPagedPoolUsage, quotaNonePagedPoolUsage, workingSetSize );
			PostServerEvent( "cSQLGame - IoContextPool Working Set Size (=%d), Quota Paged Pool Usage (=%d), Quota None Paged Pool Usage (=%d).",
							 workingSetSize,
							 quotaPagedPoolUsage,
							 quotaNonePagedPoolUsage );

			// SocketContextPool 메모리 사용량
			mSocketContextPool->GetProcessMemoryInfo( quotaPagedPoolUsage, quotaNonePagedPoolUsage, workingSetSize );
			PostServerEvent( "cGameProcess - SocketContextPool Working Set Size (=%d), Quota Paged Pool Usage (=%d), Quota None Paged Pool Usage (=%d).",
							 workingSetSize,
							 quotaPagedPoolUsage,
							 quotaNonePagedPoolUsage );
			Sleep( 10 );

			// IoContextPool 메모리 사용량
			mIoContextPool->GetProcessMemoryInfo( quotaPagedPoolUsage, quotaNonePagedPoolUsage, workingSetSize );
			PostServerEvent( "cGameProcess - IoContextPool Working Set Size (=%d), Quota Paged Pool Usage (=%d), Quota None Paged Pool Usage (=%d).",
							 workingSetSize,
							 quotaPagedPoolUsage,
							 quotaNonePagedPoolUsage );
			Sleep( 10 );

			// IoContextPool 메모리 분석
			PerIoContext* perIoContext  = mIoContextPool->GetPagedPoolUsage( );
			DWORD         quotaRead     = 0
						 ,quotaWrite    = 0
						 ,quotaCallback = 0;
			while ( perIoContext )
			{
				switch ( perIoContext->requestType )
				{
				case IOCP_REQUEST_READ:
					quotaRead++;
				case IOCP_REQUEST_WRITE:
					quotaWrite++;
				case IOCP_REQUEST_CALLBACK:
					quotaCallback++;
					break;
				}
				perIoContext = perIoContext->next;
			}
			PostServerEvent( "cGameProcess::IoContextPool:Read(=%d):Write(=%d):Callback(=%d)", quotaRead, quotaWrite, quotaCallback );
			Sleep( 10 );

			// GameManager 프로세스 사용률
			double duringTotal = mDuringSkill + mDuringObject + mDuringAI + mDuringDuel + mDuringPVP +mDuringGrid;
			PostServerEvent( "cGameProcess - GameManager During Time Skill (=%.2f%), Object (=%.2f%), AI (=%.2f%), Duel (=%.2f), PvP (=%.2f%), Grid (=%.2f%). Total (=%.2f).",
							 mDuringSkill,
							 mDuringObject,
							 mDuringAI,
							 mDuringDuel,
							 mDuringPVP,
							 mDuringGrid,
							 duringTotal );
			Sleep( 20 );

			// 지연시간.
			PostServerEvent( "cGameProcess - Min Elapsed Tick (=%d), Max Elapsed Tick (=%d).", mMinElapsedTick, mMaxElapsedTick );
			Sleep( 20 );

			mComplete.endLog = true;
		}
		else if ( mEndServer == true )
		{
			PostServerEvent( "관리자로부터 '중지' 요청을 받았으므로 GameProcess '%s:%d'를 종료하는 중입니다.", inet_ntoa( mAddr.sin_addr ), mPort );

			// 서버종료, Thread를 나간다.
			Sleep( 1000 );
			return;
		}

		// FPS 처리.
		BackendThreadFps( currTickCount );
	}
}

// BackendThreadProc Method
void cGameProcess::BackendThreadProc(void)
{
	DWORD currTickCount = 0;
	while ( true )
	{
		currTickCount = GetTickCount( );

		// 서버종료, Thread를 나간다.
		if ( mEndServer == true )
		{
			g_gameSrv->GetSender( )->PostChStatus( MAKECID((WORD)mServerNum, (WORD)mChannelNum), (BYTE)_E_STATUS_CLOSING_ );
			break; // while ( true )
		}

		// 1. 소켓관리.
		try {
			SocketManager( currTickCount );
		} catch ( char* str ) {
			PostServerEvent( "Caught 'SocketManager' exception type: '%s'. Throwing 'cGameProcess::BackendThreadProc' exception", str );
			Sleep( 100 );
			throw;
		} catch ( ... ) {
			PostServerEvent( "In SocketManager Method. Throwing 'cGameProcess::BackendThreadProc' exception" );
			Sleep( 100 );
			throw;
		}

		// 2. 채널관리.
		try {
			ChannelManager( currTickCount );
		} catch ( char* str ) {
			PostServerEvent( "Caught 'ChannelManager' exception type: '%s'. Throwing 'cGameProcess::BackendThreadProc' exception", str );
			Sleep( 100 );
			throw;
		} catch ( ... ) {
			PostServerEvent( "In ChannelManager Method. Throwing 'cGameProcess::BackendThreadProc' exception" );
			Sleep( 100 );
			throw;
		}

		// 3. 게임진행.
		try {
			GameManager( currTickCount );
		} catch ( char* str ) {
			PostServerEvent( "Caught 'GameManager' exception type: '%s'. Throwing 'cGameProcess::BackendThreadProc' exception", str );
			Sleep( 100 );
			throw;
		} catch ( ... ) {
			PostServerEvent( "In GameManager Method. Throwing 'cGameProcess::BackendThreadProc' exception" );
			Sleep( 100 );
			throw;
		}

		// 4. I/O Context 처리.
		try {
			IoContextPresent( );
		} catch ( char* str ) {
			PostServerEvent( "Caught 'IoContextPresent' exception type: '%s'. Throwing 'cGameProcess::BackendThreadProc' exception", str );
			Sleep( 100 );
			throw;
		} catch ( ... ) {
			PostServerEvent( "In IoContextPresent Method. Throwing 'cGameProcess::BackendThreadProc' exception" );
			Sleep( 100 );
			throw;
		}

		// 5. FPS 관리.
		try {
			BackendThreadFps( currTickCount );
		} catch ( char* str ) {
			PostServerEvent( "Caught 'BackendThreadFps' exception type: '%s'. Throwing 'cGameProcess::BackendThreadProc' exception", str );
			Sleep( 100 );
			throw;
		} catch ( ... ) {
			PostServerEvent( "In BackendThreadFps Method. Throwing 'cGameProcess::BackendThreadProc' exception" );
			Sleep( 100 );
			throw;
		}
	} // while ( true )
}

// BackendThread Method
DWORD cGameProcess::BackendThread(void)
{
	// 1. 게임서버 준비중.
	try {
		try {
			BackendThreadBegin( );
		} catch ( char* str ) {
			SIZE_T quotaPagedPoolUsage = 0
				  ,quotaNonPagedPoolUsage = 0
				  ,workingSetSize = 0;
			mIoContextPool->GetProcessMemoryInfo( quotaPagedPoolUsage, quotaNonPagedPoolUsage, workingSetSize );
			PostServerEvent( "Caught 'BackendThreadBegin' exception type: '%s'. Throwing 'cGameProcess::BackendThreadProc' exception. I/O Context PagedPool(=%d) NonePagedPool(=%d) WorkingSetSize(=%d)"
							,str
							,quotaPagedPoolUsage
							,quotaNonPagedPoolUsage
							,workingSetSize );
			Sleep( 100 );
			throw;
		} catch ( ... ) {
			PostServerEvent( "In BackendThreadBegin Method. Throwing 'cGameProcess::BackendThread' exception." );
			Sleep( 100 );
			throw;
		}

		// 2. 게임서버 처리중.
		try {
			BackendThreadProc( );
		} catch ( char* str ) {
			SIZE_T quotaPagedPoolUsage = 0
				  ,quotaNonPagedPoolUsage = 0
				  ,workingSetSize = 0;
			mIoContextPool->GetProcessMemoryInfo( quotaPagedPoolUsage, quotaNonPagedPoolUsage, workingSetSize );
			PostServerEvent( "Caught 'BackendThreadProc' exception type: '%s'. Throwing 'cGameProcess::BackendThreadProc' exception. I/O Context PagedPool(=%d) NonePagedPool(=%d) WorkingSetSize(=%d)"
							,str
							,quotaPagedPoolUsage
							,quotaNonPagedPoolUsage
							,workingSetSize );
			Sleep( 100 );
			throw;
		} catch ( ... ) {
			PostServerEvent( "In BackendThreadProc Method. Throwing 'cGameProcess::BackendThread' exception." );
			Sleep( 100 );
			throw;
		}

		// 3. 게임서버 종료중.
		try {
			BackendThreadEnd( );
		} catch ( char* str ) {
			SIZE_T quotaPagedPoolUsage = 0
				  ,quotaNonPagedPoolUsage = 0
				  ,workingSetSize = 0;
			mIoContextPool->GetProcessMemoryInfo( quotaPagedPoolUsage, quotaNonPagedPoolUsage, workingSetSize );
			PostServerEvent( "Caught 'BackendThreadEnd' exception type: '%s'. Throwing 'cGameProcess::BackendThreadProc' exception. I/O Context PagedPool(=%d) NonePagedPool(=%d) WorkingSetSize(=%d)"
							,str
							,quotaPagedPoolUsage
							,quotaNonPagedPoolUsage
							,workingSetSize );
			Sleep( 100 );
			throw;
		} catch ( ... ) {
			PostServerEvent( "In BackendThreadEnd Method. Throwing 'cGameProcess::BackendThread' exception." );
			Sleep( 100 );
			throw;
		}
	} catch ( ... ) {
		g_gameSrv->GetSender( )->PostChStatus( MAKECID((WORD)mServerNum, (WORD)mChannelNum), (BYTE)_E_STATUS_ERROR_ );

		mEndServer = true;
		BackendThreadEnd( );
		throw;
	}

	return 0;
}

// BackendThreadFps Method
void cGameProcess::BackendThreadFps(DWORD currentTick)
{
	if ( mFpsTick < currentTick )
	{
		// Console 모드 사용시 FPS 출력.
		if ( g_fps == true )
		{
			float fps = (float)mFrame / (float)(currentTick - (mFpsTick - 1000)) * 1000;
			printf( "FPS:%6.2f / FRAME:%d\tGameProcess Thread\n", fps, mFrame );
		}

		// Frame 저하를 방지하기 위해 쓰레드의 우선순위 조율.
		if ( mFrame < 30 )
		{
			// 쓰레드의 우선순위 변경.
			if ( mThreadPriority == false )
			{
				if ( SetThreadPriority( mIocpBackendThread, THREAD_PRIORITY_HIGHEST ) == TRUE )
					mThreadPriority = true;
			}

			// 1분간 지연시간 설정.
			mThreadPriorityTick = currentTick + 60000;
		}
		else if ( mThreadPriorityTick < currentTick )
		{
			// 쓰레드의 우선순위 복구.
			if ( mThreadPriority == true )
			{
				if ( SetThreadPriority( mIocpBackendThread, THREAD_PRIORITY_NORMAL ) == TRUE )
					mThreadPriority = false;
			}
		}

		// Frame 변수 초기화.
		mFpsTick = currentTick + 1000;
		mFrame   = 0;
	}
	else
		mFrame++;


	/*-- 지연시간 - CPU 과부화 방지 & 내부 동기화. --*/

	// 경과시간 기록.
	mElapsedTick    = GetTickCount( ) - currentTick;

	// 최소 지연시간 - None Sleep 지연시간 기록.
	mMinElapsedTick = max( mMinElapsedTick, mElapsedTick );

	if ( mElapsedTick < 16 )
	{
		Sleep( 16 - mElapsedTick );
	}
	else if ( mElapsedTick > 1000 )
	{
		PostServerEvent( "경고! 서버의 지연시간이 발생할 수 있습니다. ELAPSED TICK(=%d).", mElapsedTick );
	}

	// 최대 지연시간 - Sleep 지연시간 기록.
	mMaxElapsedTick = max( mMaxElapsedTick, (GetTickCount( ) - currentTick) );
}
