Files
Leviathan/Server/GameServer/Game/Message/SendMessage.cpp
T
2026-06-01 12:46:52 +02:00

4189 lines
132 KiB
C++

#include <stdexcept>
#include <ctime>
#include <network/IConnection.h>
#include <network/XIOCPConnection.h>
#include <mmo/ArRegion.h>
#include <toolkit/XEnv.h>
#include <toolkit/bits_scramble.h>
#include <toolkit/XConsole.h>
#include <mmo/ArcadiaServer.h>
#include <toolkit/XQuadScanner.h>
#include <toolkit/XStringUtil.h>
#include <toolkit/KHash.h>
#include "LogClient/LogClient.h"
#include "ErrorCode/ErrorCode.h"
#include "GameMessage.h"
#include "LUAVM.h"
#include "SendMessage.h"
#include "GameContent.h"
#include "StructPlayer.h"
#include "StructSkill.h"
#include "StructNPC.h"
#include "StructMonster.h"
#include "StructSummon.h"
#include "StructPet.h"
#include "StructFieldProp.h"
#include "DB_Commands.h"
#include "StructItem.h"
#include "PartyManager.h"
#include "GuildManager.h"
#include "DungeonManager.h"
#include "RankingManager.h"
#include "StructWorldLocation.h"
#include "StructSkillProp.h"
#include "ThreadPlayerHelper.h"
extern volatile LONG g_nSendMessageCount;
extern volatile LONG g_nSendMessageByte;
extern IStreamSocketConnection * g_pAuthConnection;
extern IStreamSocketConnection * g_pUploadConnection;
extern IStreamSocketConnection * g_pCoordinatorConnection;
extern bool g_bUseMessageStatistics;
struct MessagingInfo
{
unsigned short msg_id;
__int64 total_size;
int count;
};
struct MessageLogger : ArSchedulerObject
{
public:
MessageLogger()
: m_nLastLogTime( GetArTime() )
, m_csLock( "MessageLogger::m_csLock" )
{
}
void Init()
{
ArcadiaServer::Instance().SetObjectPriority( this, ArSchedulerObject::UPDATE_PRIORITY_LOW );
}
void DeInit()
{
ArcadiaServer::Instance().SetObjectPriority( this, ArSchedulerObject::UPDATE_PRIORITY_NULL );
}
void SetMessagingInfo( unsigned short msg_id, __int64 msg_size, bool bIsSendMessage )
{
if( !g_bUseMessageStatistics )
return;
THREAD_SYNCRONIZE( m_csLock );
std::vector< MessagingInfo > * pvInfo = &m_vSendInfo;
if( !bIsSendMessage )
{
pvInfo = &m_vRecvInfo;
}
for( std::vector< MessagingInfo >::iterator it = pvInfo->begin(); it != pvInfo->end(); ++it )
{
if( (*it).msg_id == msg_id )
{
++(*it).count;
(*it).total_size += msg_size;
return;
}
}
MessagingInfo _info;
_info.msg_id = msg_id;
_info.total_size = msg_size;
_info.count = 1;
pvInfo->push_back( _info );
}
void LogMessagingInfo()
{
if( !g_bUseMessageStatistics )
return;
std::string strLog;
char buf[256];
THREAD_SYNCRONIZE( m_csLock );
strLog = "\r\n::Send Messaging Info::\r\n";
for( std::vector< MessagingInfo >::iterator it = m_vSendInfo.begin(); it != m_vSendInfo.end(); ++it )
{
s_sprintf( buf, _countof( buf ), "%5u\t%20I64d\t%10d\r\n", (*it).msg_id, (*it).total_size, (*it).count );
strLog += buf;
}
strLog += "\r\n::Recv Messaging Info::\r\n";
for( std::vector< MessagingInfo >::iterator it = m_vRecvInfo.begin(); it != m_vRecvInfo.end(); ++it )
{
s_sprintf( buf, _countof( buf ), "%5u\t%20I64d\t%10d\r\n", (*it).msg_id, (*it).total_size, (*it).count );
strLog += buf;
}
strLog += "\r\n\r\n";
FileLogHandler::GetFileLogHandler()->LogStringEx( NULL, "MessagingLog", strLog.c_str() );
m_vSendInfo.clear();
m_vRecvInfo.clear();
};
virtual void onProcess( int nThreadNum )
{
char buf[255];
s_sprintf( buf, _countof( buf ), "thread.scheduler.%d.proc", nThreadNum );
ENV().Set( buf, "MessageLogger" );
if( !g_bUseMessageStatistics )
return;
AR_TIME t = GetArTime();
if( m_nLastLogTime + LOG_PERIOD < t )
{
m_nLastLogTime = t;
LogMessagingInfo();
}
}
private:
static const AR_TIME LOG_PERIOD = 60000;
XCriticalSection m_csLock;
std::vector< MessagingInfo > m_vSendInfo;
std::vector< MessagingInfo > m_vRecvInfo;
AR_TIME m_nLastLogTime;
};
MessageLogger g_MessageLogger;
void InitMessageLogger()
{
g_MessageLogger.Init();
}
void DeInitMessageLogger()
{
g_MessageLogger.DeInit();
}
void SetMessagingInfo( unsigned short msg_id, __int64 msg_size, bool bIsSendMessage )
{
g_MessageLogger.SetMessagingInfo( msg_id, msg_size, bIsSendMessage );
}
// 타임싱크
void SendTimeSync( StructPlayer * pPlayer )
{
TS_TIMESYNC msg;
msg.time = GetArTime();
PendMessage( pPlayer, &msg );
#ifdef TIME_DEBUG
_cprint( "SND : TM_TIMESYNC (%d)\n", msg.time );
#endif
}
void fillItemBaseInfo( TS_ITEM_BASE_INFO *pInfo, StructItem * pItem )
{
pInfo->Code = pItem->GetItemCode();
pInfo->count = pItem->GetCount();
pInfo->Flag = *pItem->GetInstanceFlag().GetRawData();
pInfo->handle = pItem->GetHandle();
pInfo->ethereal_durability = pItem->GetCurrentEtherealDurability();
pInfo->endurance = pItem->GetCurrentEndurance();
pInfo->enhance = pItem->GetItemEnhance();
pInfo->level = pItem->GetItemLevel();
pInfo->uid = pItem->GetItemUID();
pInfo->enhance_chance = pItem->GetItemEnhanceChance();
pInfo->nAdditionalItemEffect = pItem->GetItemAdditionalEffect(); // Fraun Sky Accessories 7/12/2025
if( pItem->IsExpireItem() )
pInfo->remain_time = pItem->GetExpireTime() - time( NULL );
else
pInfo->remain_time = 0;
for( unsigned i = 0; i < ItemBase::MAX_SOCKET_NUMBER; ++i )
{
pInfo->socket[i] = pItem->GetSocketCode( i );
}
time_t tCurrent = time( NULL );
if( pItem->GetElementalEffectType() && ( !pItem->GetElementalEffectExpireTime() || pItem->GetElementalEffectExpireTime() > tCurrent ) )
{
pInfo->elemental_effect_remain_time = pItem->GetElementalEffectExpireTime() ? pItem->GetElementalEffectExpireTime() - tCurrent : -1;
pInfo->elemental_effect_type = pItem->GetElementalEffectType();
pInfo->elemental_effect_attack_point = pItem->GetElementalEffectAttackPoint();
pInfo->elemental_effect_magic_point = pItem->GetElementalEffectMagicPoint();
}
else
{
pInfo->elemental_effect_remain_time = 0;
pInfo->elemental_effect_type = Elemental::TYPE_NONE;
pInfo->elemental_effect_attack_point = 0;
pInfo->elemental_effect_magic_point = 0;
}
pInfo->appearance_code = pItem->GetAppearanceCode();
for( int nCnt = 0 ; nCnt < ItemInstance::MAX_AWAKEN_NUMBER ; ++nCnt )
{
pInfo->awakenoption.nValue1[nCnt] = pItem->GetAwakenOptionValue1( nCnt );
pInfo->awakenoption.nValue2[nCnt] = pItem->GetAwakenOptionValue2( nCnt );
}
for( int nCnt = 0 ; nCnt < ItemInstance::MAX_RANDOM_OPTION_NUMBER ; ++nCnt )
{
pInfo->random_option.nType[nCnt] = pItem->GetIdentifiedOptionType( nCnt );
pInfo->random_option.fValue1[nCnt] = pItem->GetIdentifiedOptionValue1( nCnt );
pInfo->random_option.fValue2[nCnt] = pItem->GetIdentifiedOptionValue2( nCnt );
}
if( pItem->IsSummonCard() )
{
StructSummon * pSummon = pItem->GetSummonStruct();
if( pSummon )
{
int depth = pSummon->GetTransformLevel();
int i;
for( i = 0; i < depth - 1; ++i )
{
pInfo->socket[i + 1] = pSummon->GetPrevJobLevel( i );
}
pInfo->socket[i + 1] = pSummon->GetLevel();
}
}
pInfo->summon_code = pItem->GetSummonCode();
}
void fillItemInfo( TS_ITEM_INFO *pInfo, StructItem *pItem )
{
fillItemBaseInfo( pInfo, pItem );
pInfo->wear_position = pItem->GetWearInfo();
pInfo->own_summon_handle = 0;
if( pItem->GetOwnSummonUID() > 0 )
{
pInfo->own_summon_handle = pItem->GetOwnSummonHandle();
}
if( pItem->IsInStorage() )
{
pInfo->wear_position = -2;
}
pInfo->index = pItem->GetIdx();
}
void SendDropResult( struct StructPlayer * pClient, AR_HANDLE ItemHandle, bool bIsSuccess )
{
TS_SC_DROP_RESULT msg;
msg.item_handle = ItemHandle;
msg.isAccepted = bIsSuccess;
PendMessage( pClient, &msg );
}
void SendTakeResult( struct StructPlayer * pClient, struct StructItem *pItem )
{
/*
TS_SC_TAKE_RESULT msg;
fillItemInfo( &msg.item_info, pItem );
PendMessage( pClient, &msg );
*/
}
static void pendMessage( struct IStreamSocketConnection *pConn, const struct TS_MESSAGE * pMessage )
{
if( !pConn ) return;
TS_MESSAGE* temp = const_cast< TS_MESSAGE* >( pMessage );
temp->set_check_sum();
InterlockedIncrement( &g_nSendMessageCount );
InterlockedExchangeAdd( &g_nSendMessageByte, pMessage->size );
SetMessagingInfo( pMessage->id, pMessage->size, true );
int nSize = pConn->Write( pMessage, pMessage->size );
if( nSize != pMessage->size )
{
//assert( 0 );
return;
}
}
static void pendStream( struct IStreamSocketConnection *pConn, const void *pBuffer, unsigned size )
{
if( !pConn ) return;
InterlockedIncrement( &g_nSendMessageCount );
InterlockedExchangeAdd( &g_nSendMessageByte, size );
const struct TS_MESSAGE * pMessage = static_cast< const struct TS_MESSAGE * >( pBuffer );
SetMessagingInfo( pMessage->id, pMessage->size, true );
int nSize = pConn->Write( pBuffer, size );
if( nSize != size )
{
//assert( 0 );
return;
}
}
static void pendString( struct IStreamSocketConnection *pConn, const char *szString, size_t nLength )
{
if( !pConn ) return;
int nSize = pConn->Write( szString, nLength );
}
void PendMessage( struct IStreamSocketConnection *pConn, const struct TS_MESSAGE * pMessage )
{
//if( LockIOCPConnection( pConn ) )
{
pendMessage( pConn, pMessage );
//UnLockIOCPConnection( pConn );
}
}
void PendMessage( const struct ArObject *pObj, const struct TS_MESSAGE * pMessage )
{
if( static_cast< const GameObject * >(pObj)->IsPlayer() && static_cast< const StructPlayer* >( pObj )->pConnection && static_cast< const StructPlayer* >( pObj )->pConnection->IsConnected() )
{
pendMessage( static_cast< const StructPlayer* >( pObj )->pConnection, pMessage );
}
}
void PendStream( const struct ArObject *pObj, const void *pBuffer, unsigned size )
{
if( static_cast< const GameObject * >(pObj)->IsPlayer() && static_cast< const StructPlayer* >( pObj )->pConnection && static_cast< const StructPlayer* >( pObj )->pConnection->IsConnected() )
{
pendStream( static_cast< const StructPlayer* >( pObj )->pConnection, pBuffer, size );
}
}
void PendString( struct IStreamSocketConnection *pConnection, const char *szStr )
{
pendString( pConnection, szStr, strlen( szStr ) );
}
void SendResult( const struct ArObject *pObj, unsigned short msg, unsigned short result, int value )
{
if( static_cast< const GameObject * >(pObj)->IsPlayer() && static_cast< const StructPlayer* >( pObj )->pConnection && static_cast< const StructPlayer* >( pObj )->pConnection->IsConnected() )
{
pendMessage( static_cast< const StructPlayer* >( pObj )->pConnection, &TS_SC_RESULT( msg, result, value ) );
}
}
void SendResult( IStreamSocketConnection *pConnection, unsigned short msg, unsigned short result, int value )
{
//if( LockIOCPConnection( pConnection ) )
if( pConnection && pConnection->IsConnected() )
{
pendMessage( pConnection, &TS_SC_RESULT( msg, result, value ) );
//UnLockIOCPConnection( pConnection );
}
}
void PrintfChatMessage( bool bLock, int nChatType, const char *szSenderName, struct StructPlayer *pTarget, const char *szString, ... )
{
char szBuf[2048];
va_list va;
va_start( va, szString );
s_vsprintf( szBuf, _countof(szBuf), szString, va );
va_end( va );
SendChatMessage( bLock, nChatType, szSenderName, pTarget, szBuf, (unsigned)strlen( szBuf ) );
}
void SendSelfChatMessage( int nChatType, const char *szSenderName, struct StructPlayer *pTarget, const char *szString, unsigned len )
{
SendChatMessage( false, nChatType, szSenderName,pTarget->GetHandle(), szString, len );
}
/*
void PrintfChatMessage( const char *szSenderName, struct StructPlayer *pTarget, const char *szString, ... )
{
char szBuf[2048];
va_list va;
va_start( va, szString );
s_vsprintf( szBuf, _countof(szBuf), szString, va );
va_end( va );
SendChatMessage( false, szSenderName, pTarget, szBuf );
}*/
void SendQuestMessage( int nChatType, struct StructPlayer *pTarget, const char *szString, ... )
{
char szBuf[2048];
va_list va;
va_start( va, szString );
s_vsprintf( szBuf, _countof(szBuf), szString, va );
va_end( va );
SendChatMessage( true, nChatType, "@QUEST", pTarget, szBuf );
}
void PrintfGlobalChatMessage( int nChatType, const char *szSenderName, const char *szString, ... )
{
char szBuf[2048];
va_list va;
va_start( va, szString );
s_vsprintf( szBuf, _countof(szBuf), szString, va );
va_end( va );
SendGlobalChatMessage( nChatType, szSenderName, szBuf );
}
void SendPartyChatMessage( int nChatType, const char *szSender, int nPartyID, const char *szString, unsigned len )
{
struct myPartyFunctor : PartyManager::PartyFunctor
{
myPartyFunctor( const char *s, const char *p, int t ) : szSender( s ), pString( p ), nChatType( t ) {}
virtual bool operator()( AR_HANDLE handle )
{
SendChatMessage( false, nChatType, szSender, handle, pString );
return true;
}
int nChatType;
const char *szSender;
const char *pString;
} _fo( szSender, szString, nChatType );
PartyManager::GetInstance().DoEachMember( nPartyID, _fo );
}
void SendPartyChatMessage( const char *szSender, int nPartyID, const char *szString, unsigned len )
{
SendPartyChatMessage( CHAT_PARTY, szSender, nPartyID, szString, len );
}
void PrintfPartyChatMessage( int nChatType, int nPartyID, const char *szString, ... )
{
char szBuf[2048];
va_list va;
va_start( va, szString );
s_vsprintf( szBuf, _countof(szBuf), szString, va );
va_end( va );
SendPartyChatMessage( nChatType, "@PARTY", nPartyID, szBuf );
}
void PrintfPartyChatMessage( int nPartyID, const char *szString, ... )
{
char szBuf[2048];
va_list va;
va_start( va, szString );
s_vsprintf( szBuf, _countof(szBuf), szString, va );
va_end( va );
SendPartyChatMessage( "@PARTY", nPartyID, szBuf );
}
void SendGuildChatMessage( int nChatType, const char *szSender, int nGuildID, const char *szString, unsigned len )
{
struct myGuildFunctor : GuildManager::GuildFunctor
{
myGuildFunctor( const char *s, const char *p, int t ) : szSender( s ), pString( p ), nChatType( t ) {}
virtual bool operator()( AR_HANDLE handle )
{
SendChatMessage( false, nChatType, szSender, handle, pString );
return true;
}
int nChatType;
const char *szSender;
const char *pString;
} _fo( szSender, szString, nChatType );
GuildManager::GetInstance().DoEachMember( nGuildID, _fo );
}
void SendGuildChatMessage( const char *szSender, int nGuildID, const char *szString, unsigned len )
{
SendGuildChatMessage( CHAT_GUILD, szSender, nGuildID, szString, len );
}
void PrintfGuildChatMessage( int nChatType, const char * szSender, int nGuildID, const char *szString, ... )
{
char szBuf[2048];
va_list va;
va_start( va, szString );
s_vsprintf( szBuf, _countof(szBuf), szString, va );
va_end( va );
SendGuildChatMessage( nChatType, szSender, nGuildID, szBuf );
}
void PrintfGuildChatMessage( int nChatType, int nGuildID, const char *szString, ... )
{
char szBuf[2048];
va_list va;
va_start( va, szString );
s_vsprintf( szBuf, _countof(szBuf), szString, va );
va_end( va );
SendGuildChatMessage( nChatType, "@GUILD", nGuildID, szBuf );
}
void PrintfGuildChatMessage( int nGuildID, const char *szString, ... )
{
char szBuf[2048];
va_list va;
va_start( va, szString );
s_vsprintf( szBuf, _countof(szBuf), szString, va );
va_end( va );
SendGuildChatMessage( "@GUILD", nGuildID, szBuf );
}
void PrintfAllianceChatMessage( int nChatType, int nAllianceID, const char * szSender, const char * szString, ... )
{
char szBuf[2048];
va_list va;
va_start( va, szString );
s_vsprintf( szBuf, _countof(szBuf), szString, va );
va_end( va );
std::vector< int > vAllianceMember = GuildManager::GetInstance().GetAllianceMemberID( nAllianceID );
for( std::vector< int >::iterator it = vAllianceMember.begin(); it != vAllianceMember.end(); ++it )
{
SendGuildChatMessage( nChatType, szSender, (*it), szBuf );
}
}
void PrintfAllianceChatMessage( int nAllianceID, const char * szSender, const char * szString, ... )
{
char szBuf[2048];
va_list va;
va_start( va, szString );
s_vsprintf( szBuf, _countof(szBuf), szString, va );
va_end( va );
PrintfAllianceChatMessage( CHAT_ALLIANCE_SYSTEM, nAllianceID, szSender, szBuf );
}
void PrintfAllianceChatMessage( int nAllianceID, const char * szString, ... )
{
char szBuf[2048];
va_list va;
va_start( va, szString );
s_vsprintf( szBuf, _countof(szBuf), szString, va );
va_end( va );
PrintfAllianceChatMessage( nAllianceID, "@ALLIANCE", szBuf );
}
void SendLocalChatMessage( int nChatType, unsigned rx, unsigned ry, unsigned char layer, const char *szSenderName, const char *szString, unsigned len )
{
ARCADIA_LOCK( ArcadiaServer::Instance().LockWithVisibleRange( rx, ry, layer ) );
// 메세지 헤더
TS_SC_CHAT msg;
msg.type = nChatType;
s_strcpy( msg.szSender, _countof( msg.szSender ), szSenderName );
msg.len = (unsigned char)(len + 1);
// 문자열 크기만큼 사이즈 늘림
msg.size += static_cast< unsigned >( msg.len );
if ( msg.size > SENDMSG_BUFFER_SIZE )
{
FILELOG( "SendLocalChatMessage too big : %d", msg.size );
_cprint( "SendLocalChatMessage too big : %d", msg.size );
return;
}
// 메모리 할당
char pBuffer[ SENDMSG_BUFFER_SIZE ];
// char * pBuffer = new char[ msg.size ];
// 버퍼에 헤더 복사
s_memcpy( pBuffer, sizeof( pBuffer ), &msg, sizeof(msg) );
// 버퍼에 문자열 복사
s_memcpy( pBuffer + sizeof(msg), sizeof( pBuffer )-sizeof(msg), szString, msg.len );
// '\0' 추가
pBuffer[ msg.size-1 ] = '\0';
// 메세지 전송
ArcadiaServer::Instance().Broadcast( rx, ry, layer, pBuffer );
}
void BroadcastChatMessage( int nChatType, unsigned rx, unsigned ry, unsigned char layer, const char *szSenderName, const char *szString, unsigned len )
{
// 메세지 헤더
TS_SC_CHAT msg;
msg.type = nChatType;
s_strcpy( msg.szSender, _countof( msg.szSender ), szSenderName );
msg.len = (unsigned char)(len + 1);
// 문자열 크기만큼 사이즈 늘림
msg.size += static_cast< unsigned >( msg.len );
if ( msg.size > SENDMSG_BUFFER_SIZE )
{
FILELOG( "BroadcastChatMessage too big : %d", msg.size );
_cprint( "BroadcastChatMessage too big : %d", msg.size );
return;
}
// 메모리 할당
char pBuffer[SENDMSG_BUFFER_SIZE];
// 버퍼에 헤더 복사
s_memcpy( pBuffer, sizeof( pBuffer ), &msg, sizeof(msg) );
// 버퍼에 문자열 복사
s_memcpy( pBuffer + sizeof(msg), sizeof( pBuffer ) - sizeof(msg), szString, msg.len );
// '\0' 추가
pBuffer[ msg.size-1 ] = '\0';
// 메세지 전송
ArcadiaServer::Instance().Broadcast( rx, ry, layer, pBuffer );
}
void PrintfLocalChatMessage( bool bLock, int ChatType, unsigned rx, unsigned ry, unsigned char layer, const char *szSenderName, const char * szString, ... )
{
char szBuf[2048];
va_list va;
va_start( va, szString );
int nResultLen = s_vsprintf( szBuf, _countof(szBuf), szString, va );
va_end( va );
if( nResultLen <= 0 )
return;
if( bLock )
SendLocalChatMessage( ChatType, rx, ry, layer, szSenderName, szBuf, nResultLen );
else
BroadcastChatMessage( ChatType, rx, ry, layer, szSenderName, szBuf, nResultLen );
}
void SendGuildNotify( struct StructPlayer *pPlayer )
{
char szBuf[256];
s_sprintf( szBuf, _countof( szBuf ), "GCHANGE|%u|%d", pPlayer->GetHandle(), pPlayer->GetGuildID() );
SendLocalChatMessage( CHAT_GUILD_SYSTEM, pPlayer->GetRX(), pPlayer->GetRY(), pPlayer->GetLayer(), "@GUILD", szBuf, static_cast<unsigned int>( strlen( szBuf ) ) + 1 );
}
void SendGlobalChatMessage( int ChatType, const char *szSenderName, const char *szString, unsigned len )
{
if( !len ) len = (unsigned)strlen( szString );
// 메세지 헤더
TS_SC_CHAT msg;
msg.type = ChatType;
s_strcpy( msg.szSender, _countof( msg.szSender ), szSenderName );
msg.len = (unsigned short)(len + 1);
// 문자열 크기만큼 사이즈 늘림
msg.size += static_cast< unsigned >( msg.len );
if ( msg.size > SENDMSG_BUFFER_SIZE )
{
FILELOG( "SendGlobalChatMessage too big : %d", msg.size );
_cprint( "SendGlobalChatMessage too big : %d", msg.size );
return;
}
// 메모리 할당
char pBuffer[SENDMSG_BUFFER_SIZE];
// 버퍼에 헤더 복사
s_memcpy( pBuffer, sizeof( pBuffer ), &msg, sizeof(msg) );
// 버퍼에 문자열 복사
s_memcpy( pBuffer + sizeof(msg), sizeof( pBuffer ) - sizeof( msg ), szString, msg.len );
// '\0' 추가
pBuffer[ msg.size-1 ] = '\0';
// 메세지 전송
struct _ChatSender : ArObjectFunctor
{
_ChatSender( const void *_msg ) { msg = _msg; }
void operator()( ArObject *pObj ) const
{
PendMessage( pObj,
reinterpret_cast< const TS_MESSAGE* >( msg ) );
}
const void *msg;
} _sender( pBuffer );
StructPlayer::DoEachPlayer( _sender );
StructPlayer::iterator pit( StructPlayer::get( StructPlayer::FindPlayer( szSenderName ) ) );
StructPlayer *pSender = *pit;
if( pSender )
{
SendResult( pSender, TM_CS_CHAT_REQUEST, RESULT_SUCCESS );
}
}
void SendWarpMessage( const StructPlayer *pPlayer )
{
TS_SC_WARP msg;
msg.x = pPlayer->GetX();
msg.y = pPlayer->GetY();
msg.z = pPlayer->GetZ();
msg.layer = pPlayer->GetLayer();
PendMessage( pPlayer, &msg );
}
void SendChatMessageWithLock( AR_HANDLE hTarget, const void *pMsg, unsigned len )
{
struct _MessageSender : ArObjectFunctor
{
_MessageSender( const void *_msg, unsigned int _size ) : msg( _msg ), size( _size ) {}
void operator()( ArObject *pObj ) const
{
PendStream( pObj, msg, size );
}
unsigned int size;
const void *msg;
} _sender( pMsg, len );
StructPlayer::DoPlayer( hTarget, _sender );
}
void SendChatMessageWithoutLock( AR_HANDLE hTarget, void *pBuffer, unsigned len )
{
StructPlayer::iterator pit( StructPlayer::get( hTarget ) );
StructPlayer *pPlayer = *pit;
TS_SC_CHAT *pMsg = reinterpret_cast< TS_SC_CHAT * >( pBuffer );
if( pPlayer ) PendStream( pPlayer, pMsg, len );
}
void SendChatMessage( bool bLock, int nChatType, const char *szSenderName, StructPlayer *pTarget, const char *szString, unsigned len )
{
SendChatMessage( bLock, nChatType, szSenderName, pTarget->GetHandle(), szString, len );
}
void SendChatMessage( bool bLock, int nChatType, const char *szSenderName, AR_HANDLE hTarget, const char *szString, unsigned len )
{
if( !len ) len = (unsigned)strlen( szString );
if( len > 30000 )
{
//assert( 0 );
return;
}
// 메세지 헤더
TS_SC_CHAT msg;
msg.type = nChatType;
s_strcpy( msg.szSender, _countof( msg.szSender ), szSenderName );
msg.len = (unsigned short)(len + 1);
// 문자열 크기만큼 사이즈 늘림
msg.size += static_cast< unsigned >( msg.len );
if ( msg.size > SENDMSG_BUFFER_SIZE )
{
FILELOG( "SendChatMessage too big : %d", msg.size );
_cprint( "SendChatMessage too big : %d", msg.size );
return;
}
// 메모리 할당
char pBuffer[SENDMSG_BUFFER_SIZE];
// 버퍼에 헤더 복사
s_memcpy( pBuffer, sizeof( pBuffer ), &msg, sizeof(msg) );
// 버퍼에 문자열 복사
s_memcpy( pBuffer + sizeof(msg), sizeof( pBuffer ) - sizeof( msg ), szString, msg.len );
// '\0' 추가
pBuffer[ msg.size-1 ] = '\0';
if( bLock ) SendChatMessageWithLock( hTarget, pBuffer, msg.size );
else SendChatMessageWithoutLock( hTarget, pBuffer, msg.size );
}
void SendChatMessage( bool bLock, int nChatType, const char *szSenderName, const char *szTarget, const char *szString, unsigned len )
{
if( !len ) len = (unsigned)strlen( szString );
StructPlayer::iterator pit_sender( StructPlayer::get( StructPlayer::FindPlayer( szSenderName ) ) );
StructPlayer *pSender = *pit_sender;
StructPlayer::iterator pit_target( StructPlayer::get( StructPlayer::FindPlayer( szTarget ) ) );
StructPlayer *pTarget = *pit_target;
if( !pTarget )
{
if( pSender )
SendResult( pSender, TM_CS_CHAT_REQUEST, RESULT_NOT_EXIST );
return;
}
SendChatMessage( bLock, nChatType, szSenderName, pTarget->GetHandle(), szString, len );
if( !pSender ) ENV().Set( "error_chat", 1 );
if( pSender )
SendResult( pSender, TM_CS_CHAT_REQUEST, RESULT_SUCCESS );
}
void SendYellChatMessage( int nChatType, AR_HANDLE handle, const char *szString, unsigned len )
{
StructCreature::iterator cit = StructCreature::get( handle );
if( !(*cit) )
{
FILELOG( "SendYellChatMessage invalid object : %I64d", handle );
_cprint( "SendYellChatMessage invalid object : %I64d\n", handle );
return;
}
ARCADIA_LOCK( ArcadiaServer::Instance().LockObjectWithSpecificRange( cit.get(), GameRule::CHAT_YELL_RANGE ) );
// 메세지 헤더
TS_SC_CHAT_LOCAL msg;
msg.type = nChatType;
msg.handle = handle;
msg.len = (unsigned char)(len + 1);
// 문자열 크기만큼 사이즈 늘림
msg.size += static_cast< unsigned >( msg.len );
if ( msg.size > SENDMSG_BUFFER_SIZE )
{
FILELOG( "SendYellChatMessage too big : %d", msg.size );
_cprint( "SendYellChatMessage too big : %d", msg.size );
return;
}
// 메모리 할당
char pBuffer[SENDMSG_BUFFER_SIZE];
// 버퍼에 헤더 복사
s_memcpy( pBuffer, sizeof( pBuffer ), &msg, sizeof(msg) );
// 버퍼에 문자열 복사
s_memcpy( pBuffer + sizeof(msg), sizeof( pBuffer ) - sizeof( msg ), szString, msg.len );
// '\0' 추가
pBuffer[ msg.size-1 ] = '\0';
// 메세지 전송
ArcadiaServer::Instance().BroadcastToSpecificRegion( cit.get()->GetRX(), cit.get()->GetRY(), GameRule::CHAT_YELL_RANGE, cit.get()->GetLayer(), pBuffer );
StructPlayer::iterator pit( StructPlayer::get( handle ) );
StructPlayer *pPlayer = *pit;
if( pPlayer )
{
SendResult( pPlayer, TM_CS_CHAT_REQUEST, RESULT_SUCCESS );
}
}
void SendLocalChatMessage( int nChatType, AR_HANDLE handle, const char *szString, unsigned len )
{
StructCreature::iterator cit = StructCreature::get( handle );
if( !(*cit) )
{
FILELOG( "SendLocalChatMessage invalid object : %I64d", handle );
_cprint( "SendLocalChatMessage invalid object : %I64d\n", handle );
return;
}
ARCADIA_LOCK( ArcadiaServer::Instance().LockObjectWithVisibleRange( cit.get() ) );
// 메세지 헤더
TS_SC_CHAT_LOCAL msg;
msg.type = nChatType;
msg.handle = handle;
msg.len = (unsigned char)(len + 1);
// 문자열 크기만큼 사이즈 늘림
msg.size += static_cast< unsigned >( msg.len );
if ( msg.size > SENDMSG_BUFFER_SIZE )
{
FILELOG( "SendLocalChatMessage too big : %d", msg.size );
_cprint( "SendLocalChatMessage too big : %d", msg.size );
return;
}
// 메모리 할당
char pBuffer[SENDMSG_BUFFER_SIZE];
// 버퍼에 헤더 복사
s_memcpy( pBuffer, sizeof( pBuffer ), &msg, sizeof(msg) );
// 버퍼에 문자열 복사
s_memcpy( pBuffer + sizeof(msg), sizeof( pBuffer ) - sizeof( msg ), szString, msg.len );
// '\0' 추가
pBuffer[ msg.size-1 ] = '\0';
// 메세지 전송
ArcadiaServer::Instance().Broadcast( cit.get()->GetRX(), cit.get()->GetRY(), cit.get()->GetLayer(), pBuffer );
StructPlayer::iterator pit( StructPlayer::get( handle ) );
StructPlayer *pPlayer = *pit;
if( pPlayer && pPlayer->IsPlayer() )
{
SendResult( pPlayer, TM_CS_CHAT_REQUEST, RESULT_SUCCESS );
}
}
void BroadcastTamingMessage( struct StructPlayer *pPlayer, struct StructMonster *pMonster, int mode )
{
if( !pPlayer )
return;
TS_SC_TAMING_INFO info;
info.tamer_handle = pPlayer ? pPlayer->GetHandle() : 0;
info.target_handle = pMonster->GetHandle();
info.mode = mode;
ArcadiaServer::Instance().Broadcast( pMonster->GetRX(), pMonster->GetRY(), pMonster->GetLayer(), &info );
switch( mode )
{
case TS_SC_TAMING_INFO::START:
if( pPlayer )
{
if( pPlayer->GetPartyID() )
PrintfPartyChatMessage( CHAT_PARTY_SYSTEM, pPlayer->GetPartyID(), "TAMING_START|%d|", pMonster->GetMonsterBase()->name_id );
else
PrintfChatMessage( false, CHAT_PARTY_SYSTEM, "@SYSTEM", pPlayer, "TAMING_START|%d|", pMonster->GetMonsterBase()->name_id );
}
break;
case TS_SC_TAMING_INFO::CLEAR:
case TS_SC_TAMING_INFO::FAILED:
if( pPlayer )
{
if( pPlayer->GetPartyID() )
PrintfPartyChatMessage( CHAT_PARTY_SYSTEM, pPlayer->GetPartyID(), "TAMING_FAILED|%d|", pMonster->GetMonsterBase()->name_id );
else
PrintfChatMessage( false, CHAT_PARTY_SYSTEM, "@SYSTEM", pPlayer, "TAMING_FAILED|%d|", pMonster->GetMonsterBase()->name_id );
}
break;
case TS_SC_TAMING_INFO::SUCCESS:
if( pPlayer )
{
// Fraun executes a script on successful creature taming. Inside of it, we do usually have taming announce with checks
int nSummonCode = (pMonster->IsMonsterCreatureTame()) ? pMonster->GetMonsterCreatureTameCode() : pMonster->GetTameCode();
int nSummonRate = GameContent::GetSummonInfo(nSummonCode)->rate;
int nPetNameId = GameContent::GetSummonInfo(nSummonCode)->name_id;
std::string creatureTamingCode = std::to_string(static_cast<long long>(pMonster->GetTameItemCode()));
std::string scriptText;
XStringUtil::Format(scriptText, "on_creature_taming( '%s', '%s', '%i', '%i' )", pPlayer->GetName(), creatureTamingCode, nSummonRate, nPetNameId);
LUA()->RunString(scriptText.c_str());
if( pPlayer->GetPartyID() )
PrintfPartyChatMessage( CHAT_PARTY_SYSTEM, pPlayer->GetPartyID(), "TAMING_SUCCESS|%d|", pMonster->GetMonsterBase()->name_id );
else
PrintfChatMessage( false, CHAT_PARTY_SYSTEM, "@SYSTEM", pPlayer, "TAMING_SUCCESS|%d|", pMonster->GetMonsterBase()->name_id );
}
break;
}
}
template< int FACTOR >
const short RoundUp( const c_fixed< FACTOR > fixed )
{
c_fixd< FACTOR > temp;
temp.set( fixed.get() + FACTOR - 1 );
}
void SendStatInfo( struct StructPlayer * pClient, struct StructCreature * pCreature )
{
{
TS_SC_STAT_INFO msg;
// 기본 정보 전송
msg.handle = pCreature->GetHandle();
const CreatureStatServer & stat = pCreature->GetStat();
msg.stat.stat_id = stat.stat_id;
msg.stat.strength = stat.strength;
msg.stat.vital = stat.vital;
msg.stat.dexterity = stat.dexterity;
msg.stat.agility = stat.agility;
msg.stat.intelligence = stat.intelligence;
msg.stat.mentality = stat.mentality;
msg.stat.luck = stat.luck;
const CreatureAttributeServer & attr = pCreature->GetAttribute();
msg.attribute.nCritical = attr.fCritical;
msg.attribute.nCriticalPower = attr.fCriticalPower;
msg.attribute.nAttackPointRight = attr.fAttackPointRight;
msg.attribute.nAttackPointLeft = attr.fAttackPointLeft;
msg.attribute.nDefence = attr.fDefence;
msg.attribute.nBlockDefence = attr.fBlockDefence;
msg.attribute.nMagicPoint = attr.fMagicPoint;
msg.attribute.nMagicDefence = attr.fMagicDefence;
msg.attribute.nAccuracyRight = attr.fAccuracyRight;
msg.attribute.nAccuracyLeft = attr.fAccuracyLeft;
msg.attribute.nMagicAccuracy = attr.fMagicAccuracy;
msg.attribute.nAvoid = attr.fAvoid;
msg.attribute.nMagicAvoid = attr.fMagicAvoid;
msg.attribute.nBlockChance = attr.fBlockChance;
msg.attribute.nMoveSpeed = attr.fMoveSpeed;
msg.attribute.nAttackSpeed = attr.fAttackSpeed;
msg.attribute.nAttackRange = attr.fAttackRange;
msg.attribute.nMaxWeight = attr.fMaxWeight;
msg.attribute.nCastingSpeed = attr.fCastingSpeed;
msg.attribute.nCoolTimeSpeed = attr.fCoolTimeSpeed;
msg.attribute.nItemChance = attr.fItemChance;
msg.attribute.nHPRegenPercentage = attr.fHPRegenPercentage;
msg.attribute.nHPRegenPoint = attr.fHPRegenPoint;
msg.attribute.nMPRegenPercentage = attr.fMPRegenPercentage;
msg.attribute.nMPRegenPoint = attr.fMPRegenPoint;
msg.attribute.nPerfectBlock = attr.fPerfectBlock;
msg.attribute.nMagicalDefIgnore = attr.fMagicalDefIgnore;
msg.attribute.nMagicalDefIgnoreRatio = attr.fMagicalDefIgnoreRatio;
msg.attribute.nPhysicalDefIgnore = attr.fPhysicalDefIgnore;
msg.attribute.nPhysicalDefIgnoreRatio = attr.fPhysicalDefIgnoreRatio;
msg.attribute.nMagicalPenetration = attr.fMagicalPenetration;
msg.attribute.nMagicalPenetrationRatio = attr.fMagicalPenetrationRatio;
msg.attribute.nPhysicalPenetration = attr.fPhysicalPenetration;
msg.attribute.nPhysicalPenetrationRatio = attr.fPhysicalPenetrationRatio;
msg.type = TS_SC_STAT_INFO::TOTAL;
PendMessage( pClient, &msg );
// 아이템빨 정보 전송
msg.handle = pCreature->GetHandle();
// 원본 스탯의 정수 값을 구한 뒤 이를 최종 스탯의 정수값에서 빼서 보너스 값을 구한다
// 단순 대입하지 않고 아래와 같이 구현한 까닭은 소수점 이하 값이 절삭되면 원본 스탯이 가끔 왜곡되어 나오기 때문
// 가장 좋은 방법은 애초에 원본 스탯+최종 스탯을 보내주는 것이지만 클라의 변경이 동반되므로 일단 보류
// 보너스 스탯에 대해 올림을 하는 방법도 있으나 이 경우는 똑같은 결과임에도 분기문이 들어가야 해서 기각
const CreatureStatServer & statByState = pCreature->GetStatByState();
msg.stat.stat_id = statByState.stat_id;
msg.stat.strength -= static_cast< int >( stat.strength - statByState.strength );
msg.stat.vital -= static_cast< int >( stat.vital - statByState.vital );
msg.stat.dexterity -= static_cast< int >( stat.dexterity - statByState.dexterity );
msg.stat.agility -= static_cast< int >( stat.agility - statByState.agility );
msg.stat.intelligence -= static_cast< int >( stat.intelligence - statByState.intelligence );
msg.stat.mentality -= static_cast< int >( stat.mentality - statByState.mentality );
msg.stat.luck -= static_cast< int >( stat.luck - statByState.luck );
const CreatureAttributeServer & attrByState = pCreature->GetAttributeByState();
msg.attribute.nCritical -= static_cast< int >( attr.fCritical - attrByState.fCritical );
msg.attribute.nCriticalPower -= static_cast< int >( attr.fCriticalPower - attrByState.fCriticalPower );
msg.attribute.nAttackPointRight -= static_cast< int >( attr.fAttackPointRight - attrByState.fAttackPointRight );
msg.attribute.nAttackPointLeft -= static_cast< int >( attr.fAttackPointLeft - attrByState.fAttackPointLeft );
msg.attribute.nDefence -= static_cast< int >( attr.fDefence - attrByState.fDefence );
msg.attribute.nBlockDefence -= static_cast< int >( attr.fBlockDefence - attrByState.fBlockDefence );
msg.attribute.nMagicPoint -= static_cast< int >( attr.fMagicPoint - attrByState.fMagicPoint );
msg.attribute.nMagicDefence -= static_cast< int >( attr.fMagicDefence - attrByState.fMagicDefence );
msg.attribute.nAccuracyRight -= static_cast< int >( attr.fAccuracyRight - attrByState.fAccuracyRight );
msg.attribute.nAccuracyLeft -= static_cast< int >( attr.fAccuracyLeft - attrByState.fAccuracyLeft );
msg.attribute.nMagicAccuracy -= static_cast< int >( attr.fMagicAccuracy - attrByState.fMagicAccuracy );
msg.attribute.nAvoid -= static_cast< int >( attr.fAvoid - attrByState.fAvoid );
msg.attribute.nMagicAvoid -= static_cast< int >( attr.fMagicAvoid - attrByState.fMagicAvoid );
msg.attribute.nBlockChance -= static_cast< int >( attr.fBlockChance - attrByState.fBlockChance );
msg.attribute.nMoveSpeed -= static_cast< int >( attr.fMoveSpeed - attrByState.fMoveSpeed );
msg.attribute.nAttackSpeed -= static_cast< int >( attr.fAttackSpeed - attrByState.fAttackSpeed );
msg.attribute.nAttackRange -= static_cast< int >( attr.fAttackRange - attrByState.fAttackRange );
msg.attribute.nMaxWeight -= static_cast< int >( attr.fMaxWeight - attrByState.fMaxWeight );
msg.attribute.nCastingSpeed -= static_cast< int >( attr.fCastingSpeed - attrByState.fCastingSpeed );
msg.attribute.nCoolTimeSpeed -= static_cast< int >( attr.fCoolTimeSpeed - attrByState.fCoolTimeSpeed );
msg.attribute.nItemChance -= static_cast< int >( attr.fItemChance - attrByState.fItemChance );
msg.attribute.nHPRegenPercentage -= static_cast< int >( attr.fHPRegenPercentage - attrByState.fHPRegenPercentage );
msg.attribute.nHPRegenPoint -= static_cast< int >( attr.fHPRegenPoint - attrByState.fHPRegenPoint );
msg.attribute.nMPRegenPercentage -= static_cast< int >( attr.fMPRegenPercentage - attrByState.fMPRegenPercentage );
msg.attribute.nMPRegenPoint -= static_cast< int >( attr.fMPRegenPoint - attrByState.fMPRegenPoint );
msg.attribute.nPerfectBlock -= static_cast< int >( attr.fPerfectBlock - attrByState.fPerfectBlock );
msg.attribute.nMagicalDefIgnore -= static_cast< int >( attr.fMagicalDefIgnore - attrByState.fMagicalDefIgnore );
msg.attribute.nMagicalDefIgnoreRatio -= static_cast< int >( attr.fMagicalDefIgnoreRatio - attrByState.fMagicalDefIgnoreRatio );
msg.attribute.nPhysicalDefIgnore -= static_cast< int >( attr.fPhysicalDefIgnore - attrByState.fPhysicalDefIgnore );
msg.attribute.nPhysicalDefIgnoreRatio -= static_cast< int >( attr.fPhysicalDefIgnoreRatio - attrByState.fPhysicalDefIgnoreRatio );
msg.attribute.nMagicalPenetration -= static_cast< int >( attr.fMagicalPenetration - attrByState.fMagicalPenetration );
msg.attribute.nMagicalPenetrationRatio -= static_cast< int >( attr.fMagicalPenetrationRatio - attrByState.fMagicalPenetrationRatio );
msg.attribute.nPhysicalPenetration -= static_cast< int >( attr.fPhysicalPenetration - attrByState.fPhysicalPenetration );
msg.attribute.nPhysicalPenetrationRatio -= static_cast< int >( attr.fPhysicalPenetrationRatio - attrByState.fPhysicalPenetrationRatio );
msg.type = TS_SC_STAT_INFO::BY_ITEM;
PendMessage( pClient, &msg );
}
}
void BroadcastPropertyMessage( unsigned rx, unsigned ry, unsigned char layer, AR_HANDLE handle, const char *szPropertyName, const __int64 & value )
{
TS_SC_PROPERTY msg;
s_strcpy( msg.name, _countof( msg.name ), szPropertyName );
// 모든 패킷은 ZeroMemory가 되므로 name의 마지막 바이트는 0이 되어야 한다.
// 아니면 szPropertyName이 너무 길어서 버퍼 오버런의 위험이 있다는 이야기
// 여기에서 걸리면 TS_SC_PROPERTY::name의 크기 조절할 것.
msg.handle = handle;
msg.value = value;
msg.is_number = true;
ArcadiaServer::Instance().Broadcast( rx, ry, layer, &msg );
}
void SendPropertyMessage( const struct StructPlayer *pPlayer, AR_HANDLE handle, const char *szPropertyName, const __int64 & value )
{
TS_SC_PROPERTY msg;
s_strcpy( msg.name, _countof( msg.name ), szPropertyName );
// 모든 패킷은 ZeroMemory가 되므로 name의 마지막 바이트는 0이 되어야 한다.
// 아니면 szPropertyName이 너무 길어서 버퍼 오버런의 위험이 있다는 이야기
// 여기에서 걸리면 TS_SC_PROPERTY::name의 크기 조절할 것.
msg.handle = handle;
msg.value = value;
msg.is_number = true;
PendMessage( pPlayer, &msg );
}
void SendPropertyMessage( const struct StructPlayer *pPlayer, AR_HANDLE handle, const char *szPropertyName, const char *szValue )
{
TS_SC_PROPERTY msg;
msg.size += static_cast< unsigned >( strlen(szValue)+1 );
s_strcpy( msg.name, _countof( msg.name ), szPropertyName );
// 모든 패킷은 ZeroMemory가 되므로 name의 마지막 바이트는 0이 되어야 한다.
// 아니면 szPropertyName이 너무 길어서 버퍼 오버런의 위험이 있다는 이야기
// 여기에서 걸리면 TS_SC_PROPERTY::name의 크기 조절할 것.
msg.handle = handle;
msg.is_number = false;
char szTmp[4096 + sizeof(msg)];
s_memcpy( szTmp, sizeof( szTmp ), &msg, sizeof(msg) );
s_memcpy( szTmp+sizeof(msg), sizeof( szTmp ) - sizeof( msg ), szValue, static_cast< unsigned >( strlen(szValue)+1 ) );
PendStream( pPlayer, szTmp, msg.size );
}
void SendSPMsg( const StructPlayer *pPlayer, struct StructSummon *pSummon )
{
TS_SC_SP msg;
msg.sp = pSummon->GetSP();
msg.max_sp = pSummon->GetMaxSP();
msg.handle = pSummon->GetHandle();
PendMessage( pPlayer, &msg );
}
void SendExpMsg( const StructPlayer *pPlayer, const StructCreature *pCreature )
{
TS_SC_EXP_UPDATE msg;
msg.handle = pCreature->GetHandle();
msg.jp = pCreature->GetJobPoint();
msg.exp = pCreature->GetEXP();
PendMessage( pPlayer, &msg );
}
void BroadcastLevelMsg( const struct StructCreature *pCreature )
{
TS_SC_LEVEL_UPDATE msg;
msg.handle = pCreature->GetHandle();
msg.job_level = pCreature->GetJobLevel();
msg.level = pCreature->GetLevel();
ArcadiaServer::Instance().Broadcast( pCreature->GetRX(), pCreature->GetRY(), pCreature->GetLayer(), &msg );
}
void SendLevelMsg( const struct StructPlayer *pPlayer, const StructCreature *pCreature )
{
TS_SC_LEVEL_UPDATE msg;
msg.handle = pCreature->GetHandle();
msg.job_level = pCreature->GetJobLevel();
msg.level = pCreature->GetLevel();
PendMessage( pPlayer, &msg );
}
void SendGoldChaosUpdateMsg( const StructPlayer *pPlayer )
{
TS_SC_GOLD_UPDATE msg;
msg.gold = pPlayer->GetGold().GetRawData();
msg.chaos = pPlayer->GetChaos();
PendMessage( pPlayer, &msg );
}
void SendMixResult( struct StructPlayer * pClient, AR_HANDLE *pHandle, unsigned count, unsigned int type )
{
TS_SC_MIX_RESULT msg;
msg.count = count;
msg.size += static_cast< unsigned int >( sizeof(AR_HANDLE)*count );
msg.type = type; // 특수 조합 결과 (각성, 랜덤화 등)
char szTmp[4096];
s_memcpy( szTmp, sizeof( szTmp ), &msg, sizeof(msg) );
if( pHandle && count )
s_memcpy( szTmp+sizeof(msg), sizeof( szTmp ) - sizeof( msg ), pHandle, sizeof(AR_HANDLE)*count );
PendStream( pClient, szTmp, msg.size );
}
void SendDecomposeResult( struct StructPlayer * pClient, TS_SC_DECOMPOSE_RESULT::DECOMPOSE_INFO * pItemResults, unsigned int count )
{
TS_SC_DECOMPOSE_RESULT msg;
msg.total_count = count;
msg.size += sizeof( TS_SC_DECOMPOSE_RESULT::DECOMPOSE_INFO ) * count;
char szTmp[4096];
s_memcpy( szTmp, sizeof( szTmp ), &msg, sizeof( msg ) );
s_memcpy( szTmp+sizeof( msg ), sizeof( szTmp ) - sizeof( msg ), pItemResults, sizeof( TS_SC_DECOMPOSE_RESULT::DECOMPOSE_INFO ) * count );
PendStream( pClient, szTmp, msg.size );
}
void SendItemList( struct StructPlayer * pClient, bool bIsStorage )
{
if( !pClient->GetItemCount() ) return;
size_t cnt = 0;
if( bIsStorage ) cnt = pClient->GetStorageItemCount();
else cnt = pClient->GetItemCount();
TS_SC_INVENTORY msg;
const static size_t MAX_ITEM_COUNT_PER_MSG = ( SENDMSG_BUFFER_SIZE - sizeof( msg ) ) / sizeof( TS_ITEM_INFO );
// TS_SC_INVENTORY::count가 unsigned short 이므로 한 번에 0xFFFF 개까지만 송신 가능. 버퍼가 부족한 경우 체크
assert( MAX_ITEM_COUNT_PER_MSG <= 0xFFFF );
for( size_t idx = 0 ; idx < cnt ; idx += MAX_ITEM_COUNT_PER_MSG )
{
// 한 번에 MAX_ITEM_COUNT_PER_MSG 개수만큼씩만 묶어서 보냄
msg.count = static_cast< unsigned short >( ( ( cnt - idx ) > MAX_ITEM_COUNT_PER_MSG ) ? MAX_ITEM_COUNT_PER_MSG : ( cnt - idx ) );
// 아이템 정보만큼 크기를 늘림
msg.size = static_cast< unsigned int >( sizeof( TS_SC_INVENTORY ) + sizeof( TS_ITEM_INFO ) * msg.count );
char szBuffer[ SENDMSG_BUFFER_SIZE ] = "";
// 버퍼에 헤더 복사
s_memcpy( szBuffer, sizeof( szBuffer ), &msg, sizeof(msg) );
// 버퍼에 개개의 아이템 정보 복사
TS_ITEM_INFO * pInfo = reinterpret_cast< TS_ITEM_INFO * >( szBuffer + sizeof( msg ) );
size_t nMaxItemIndex = idx + msg.count;
for( size_t i = idx ; i < nMaxItemIndex ; ++i )
{
StructItem * pItem = bIsStorage ? pClient->GetStorageItem( static_cast< unsigned int >( i ) ) : pClient->GetItem( static_cast< unsigned int >( i ) );
fillItemInfo( pInfo, pItem );
++pInfo;
}
// 메세지 전송
PendStream( pClient, szBuffer, msg.size );
}
}
void BroadcastSummonEvolutionMessage( struct StructPlayer * pClient, struct StructSummon *pSummon )
{
}
void SendAddSummonMessage( struct StructPlayer * pClient, struct StructSummon *pSummon )
{
TS_SC_ADD_SUMMON_INFO msg;
msg.summon_handle = pSummon->GetHandle(); // 소환수 핸들
msg.card_handle = pSummon->GetParentCard()->GetHandle();
msg.level = pSummon->GetLevel();
s_strcpy( msg.name, _countof( msg.name ), pSummon->GetName() ); // 소환수 이름
msg.code = pSummon->GetSummonCode();
PendMessage( pClient, &msg );
SendStatInfo( pClient, pSummon ); // 소환수 스탯정보 얻어서
// 보내주자
SendHPMPMsg( pClient, pSummon, pSummon->GetHP(), pSummon->GetMP() ); // HP/MP
SendLevelMsg( pClient, pSummon ); // 레벨
SendExpMsg( pClient, pSummon ); // 경치
SendSPMsg( pClient, pSummon );
}
void SendRemoveSummonMessage( struct StructPlayer * pClient, struct StructSummon *pSummon )
{
TS_SC_REMOVE_SUMMON_INFO msg;
msg.card_handle = pSummon->GetParentCard()->GetHandle();
PendMessage( pClient, &msg );
}
void SendAddPetMessage( struct StructPlayer * pClient, struct StructPet *pPet )
{
TS_SC_ADD_PET_INFO msg;
msg.cage_handle = ( pPet->GetParentCage() ) ? pPet->GetParentCage()->GetHandle() : 0;
msg.pet_handle = pPet->GetHandle();
s_strcpy( msg.name, _countof( msg.name ), pPet->GetName() );
msg.code = pPet->GetPetCode();
PendMessage( pClient, &msg );
}
void SendRemovePetMessage( struct StructPlayer * pClient, struct StructPet *pPet )
{
TS_SC_REMOVE_PET_INFO msg;
msg.handle = pPet->GetHandle();
PendMessage( pClient, &msg );
}
void GetWearMsg( struct StructCreature *pCreature, TS_WEAR_INFO & itemInfoMsg )
{
memset( &(itemInfoMsg.itemCode), 0, sizeof( itemInfoMsg.itemCode ) );
memset( &(itemInfoMsg.itemEnhance), 0, sizeof( itemInfoMsg.itemEnhance ) );
StructItem * pItem;
itemInfoMsg.handle = pCreature->GetHandle();
for( int i = 0; i < ItemBase::MAX_ITEM_WEAR; ++i )
{
pItem = pCreature->GetWearedItem( (ItemBase::ItemWearType) i );
if( !pItem ) continue;
itemInfoMsg.itemCode[i] = pItem->GetItemCode();
itemInfoMsg.itemEnhance[i] = pItem->GetItemEnhance();
itemInfoMsg.itemLevel[i] = pItem->GetItemLevel();
itemInfoMsg.elemental_effect_type[i] = pItem->GetElementalEffectType();
itemInfoMsg.appearance_code[i] = pItem->GetAppearanceCode();
}
if( pCreature->IsPlayer() )
{
StructPlayer * pPlayer = static_cast< StructPlayer * >(pCreature);
// 매직 넘버 -_-
// if( !itemInfoMsg.itemCode[ ItemBase::WEAR_HELM ] ) itemInfoMsg.itemCode[ ItemBase::WEAR_HELM ] = pPlayer->GetBaseModelId( 0 );
if( !itemInfoMsg.itemCode[ ItemBase::WEAR_ARMOR ] ) itemInfoMsg.itemCode[ ItemBase::WEAR_ARMOR ] = pPlayer->GetBaseModelId( 2 );
if( !itemInfoMsg.itemCode[ ItemBase::WEAR_GLOVE ] ) itemInfoMsg.itemCode[ ItemBase::WEAR_GLOVE ] = pPlayer->GetBaseModelId( 3 );
if( !itemInfoMsg.itemCode[ ItemBase::WEAR_BOOTS ] ) itemInfoMsg.itemCode[ ItemBase::WEAR_BOOTS ] = pPlayer->GetBaseModelId( 4 );
}
}
void SendWearMessage( StructPlayer *pPlayer )
{
SendWearMessage( pPlayer, pPlayer );
}
void SendWearMessage( struct StructPlayer *pPlayer, struct StructCreature *pCreature )
{
TS_WEAR_INFO msg;
GetWearMsg( pCreature, msg );
PendMessage( pPlayer, &msg );
}
void SendTotalItemWearInfoMessage( StructPlayer *pPlayer, StructCreature *pTarget )
{
if( !pPlayer || !pTarget )
return;
for( int i = 0; i < ItemBase::MAX_ITEM_WEAR; ++i )
{
StructItem * pItem = pTarget->GetWearedItem( (ItemBase::ItemWearType) i );
if( !pItem ) continue;
SendItemWearInfoMessage( pPlayer, pTarget, pItem );
}
}
void SendItemWearInfoMessage( StructPlayer *pPlayer, StructCreature *pTarget, StructItem *pItem )
{
TS_SC_ITEM_WEAR_INFO msg;
msg.target_handle = pTarget ? pTarget->GetHandle() : NULL;
msg.item_handle = pItem->GetHandle();
msg.wear_position = pItem->GetWearInfo();
msg.enhance = pItem->GetItemEnhance();
msg.elemental_effect_type = pItem->GetElementalEffectType();
msg.appearance_code = pItem->GetAppearanceCode();
PendMessage( pPlayer, &msg );
}
void SendCharacterList( struct IStreamSocketConnection * pConn, struct LOBBY_CHARACTER_INFO *pInfo, unsigned short nCount, unsigned short nLastLoginIndex )
{
if( nCount > 10 )
{
FILELOG( "SendCharacterList() : a lot of count : %d", nCount );
_cprint( "SendCharacterList() : a lot of count : %d\n", nCount );
return;
}
// 메세지 버퍼 할당
char pBuf[SENDMSG_BUFFER_SIZE];
// char *pBuf = new char[ sizeof( TS_SC_CHARACTER_LIST ) + sizeof( LOBBY_CHARACTER_INFO ) * nCount ];
// 메세지 포인터 설정
TS_SC_CHARACTER_LIST *pMsg = new (pBuf) TS_SC_CHARACTER_LIST;
LOBBY_CHARACTER_INFO *pMsgInfo = reinterpret_cast< LOBBY_CHARACTER_INFO* >( pMsg+1 );
int msg_buf_len = sizeof( pBuf ) - sizeof( TS_SC_CHARACTER_LIST );
// 메인 메세지 초기화
pMsg->size += static_cast< unsigned int >( sizeof( LOBBY_CHARACTER_INFO ) * nCount );
if ( pMsg->size > SENDMSG_BUFFER_SIZE )
{
FILELOG( "SendCharacterList too big : %d", pMsg->size );
_cprint( "SendCharacterList too big : %d", pMsg->size );
return;
}
// pMsg->current_server_time = time( NULL );
{
extern XCriticalSection g_ConnectionTagLock;
THREAD_SYNCHRONIZE( g_ConnectionTagLock );
_CONNECTION_TAG * pTag = static_cast< _CONNECTION_TAG * >( pConn->GetTag() );
pTag->vCharacterNameList.clear();
// 캐릭터 정보 추가
for( unsigned short i = 0; i < nCount ; ++i )
{
pTag->vCharacterNameList.push_back( pInfo->name );
s_memcpy( pMsgInfo, msg_buf_len, pInfo, sizeof(LOBBY_CHARACTER_INFO) );
msg_buf_len -= sizeof( LOBBY_CHARACTER_INFO );
++pMsgInfo;
++pInfo;
}
}
pMsg->count = nCount;
// 최근 선택 캐릭터 번호 추가
pMsg->last_login_index = nLastLoginIndex;
// 메세지 보내고
PendMessage( pConn, pMsg );
}
void SendItemMessage( struct StructPlayer * pPlayer, struct StructItem *pItem )
{
// 메세지 헤더
char buf[ sizeof(TS_SC_INVENTORY)+sizeof(TS_ITEM_INFO) ];
TS_SC_INVENTORY *pInvenMsg = new (buf) TS_SC_INVENTORY();
// 아이템 정보만큼 크기를 늘린다
pInvenMsg->size += sizeof(TS_ITEM_INFO);
pInvenMsg->count = 1;
// 버퍼에 개개의 아이템 정보 복사
TS_ITEM_INFO * pIt = reinterpret_cast< TS_ITEM_INFO * >( buf + sizeof(TS_SC_INVENTORY) );
fillItemInfo( pIt, pItem );
// 메세지 전송
PendMessage( pPlayer, pInvenMsg );
}
void SendCreatureEquipMessage( const StructPlayer *pPlayer, bool bShowDialog )
{
TS_EQUIP_SUMMON msg;
for( int i = 0; i < 6; ++i )
{
StructItem *pCard = pPlayer->GetSummonCardAt( i );
if( pCard ) msg.card_handle[i] = pCard->GetHandle();
else msg.card_handle[i] = 0;
}
msg.open_dialog = bShowDialog;
PendMessage( pPlayer, &msg );
}
void SendAccountResultWithAuth( struct IStreamSocketConnection * pConn, const char *szAccount, int nError )
{
SendResult( pConn, TM_CS_ACCOUNT_WITH_AUTH, nError );
}
void SendDisconnectDesc( struct IStreamSocketConnection * pConn, const TS_SC_DISCONNECT_DESC::_DISCONNECT_TYPE eDisconnectionDesc )
{
TS_SC_DISCONNECT_DESC msg;
msg.desc_id = eDisconnectionDesc;
PendMessage( pConn, &msg );
}
void SendLoginResult( struct IStreamSocketConnection * pConn, struct StructPlayer * pClient, const char nPCBangMode, const unsigned short nResult )
{
// 접속이 더이상 유효하지 않다면
/*
if( !LockIOCPConnection( pConn ) )
{
ArcadiaServer::Instance().DeleteObject( pClient );
return;
}*/
TS_LOGIN_RESULT msg;
msg.result = nResult;
if( nResult == RESULT_SUCCESS )
{
// time sync
SendTimeSync( pClient );
msg.handle = pClient->GetHandle();
msg.region_size = g_nRegionSize;
msg.cell_size = CELL_SIZE;
// 종족
msg.race = pClient->GetRace();
// 성별
msg.sex = pClient->GetSex();
// 외형 정보
msg.skin_color = pClient->GetSkinColor();
msg.hairId = pClient->GetBaseModelId( 0 );
msg.faceId = pClient->GetBaseModelId( 1 );
// 이름
s_strcpy( msg.szName, _countof( msg.szName ), pClient->GetName() );
msg.guild_id = pClient->GetGuildID();
// 로그인 핸들러 호출
ThreadPlayerHelper TPHelper( pClient );
std::string strFirstLoginHandler;
strFirstLoginHandler = "on_login( '";
strFirstLoginHandler += pClient->GetName();
strFirstLoginHandler += "' )";
// on_login 에서 SCRIPT_SetValue 가 호출되며 프로퍼티 방송이 발생함
{
ARCADIA_LOCK( ArcadiaServer::Instance().LockObjectWithVisibleRange( pClient ) );
LUA()->RunString( strFirstLoginHandler.c_str() );
}
// HP
msg.hp = pClient->GetHP();
msg.mp = pClient->GetMP();
msg.max_hp = pClient->GetMaxHP();
msg.max_mp = pClient->GetMaxMP();
pClient->SetCurrentXY( pClient->GetX(), pClient->GetY() );
// 위치 세팅
msg.x = pClient->GetX();
msg.y = pClient->GetY();
msg.z = pClient->GetZ();
msg.layer = pClient->GetLayer();
msg.face_direction = pClient->GetFace();
StructItem * pItem = pClient->GetWearedItem( ItemBase::WEAR_BACKBOARD );
msg.back_board = pItem ? pItem->GetItemBase().nCode : 0;
}
pendMessage( pConn, &msg );
// 로그인 성공이라면
if( nResult == RESULT_SUCCESS )
{
pClient->SendCharacterInfo();
// 퀵슬롯 정보는 캐쉬 아이템 서버에서는 프리패스 귀환권 지급 이후에 송신해야 함(DB_ReadCommercialStorageList)
if( !GameRule::bIsCashUsableServer )
{
// Client Info (클라 인포, 퀵 슬롯, 저장 키 정보 등) 전송
// !! 클라이언트 세팅 정보를 보낼 때에는 client_info를 항상 먼저 보내야 한다.
SendPropertyMessage( pClient, pClient->GetHandle(), "client_info", pClient->GetClientInfo() );
SendPropertyMessage( pClient, pClient->GetHandle(), "quick_slot", pClient->GetQuickSlot() );
SendPropertyMessage( pClient, pClient->GetHandle(), "current_key", pClient->GetCurrentKey() );
SendPropertyMessage( pClient, pClient->GetHandle(), "saved_key", pClient->GetSavedKey() );
}
if( pClient->GetPartyID() != 0 )
{
int nPartyID = pClient->GetPartyID();
if( PartyManager::GetInstance().onLogin( pClient->GetPartyID(), pClient ) )
{
if( PartyManager::GetInstance().IsLinkedParty( nPartyID ) )
{
PrintfLinkedPartyChatMessage( false, CHAT_PARTY_SYSTEM, "@PARTY", pClient->GetPartyID(), "LOGIN|%s|%s|", PartyManager::GetInstance().GetPartyName( pClient->GetPartyID() ).c_str(), pClient->GetAlias() );
BroadcastPartyMemberInfo( nPartyID, pClient );
BroadcastLinkedPartyMemberInfo( nPartyID, pClient );
SendPartyInfo( pClient );
SendLinkedPartyInfo( pClient );
}
else
{
PrintfPartyChatMessage( CHAT_PARTY_SYSTEM, pClient->GetPartyID(), "LOGIN|%s|%s|", PartyManager::GetInstance().GetPartyName( pClient->GetPartyID() ).c_str(), pClient->GetName() );
BroadcastPartyMemberInfo( nPartyID, pClient );
SendPartyInfo( pClient );
}
BroadcastSummonInfo( nPartyID, pClient );
SendSummonInfo( pClient );
}
}
if( pClient->GetGuildID() != 0 )
{
PrintfGuildChatMessage( CHAT_GUILD_SYSTEM, pClient->GetGuildID(), "LOGIN|%s|%s|", GuildManager::GetInstance().GetGuildName( pClient->GetGuildID() ).c_str(), pClient->GetName() );
BroadcastGuildMemberInfo( pClient->GetGuildID(), pClient );
SendGuildStaticInfo( pClient );
GuildManager::GetInstance().onLogin( pClient->GetGuildID(), pClient );
SendGuildMemberInfo( pClient );
}
SendQuestList( pClient );
// 호칭 관련 처리
SendTitleList( pClient );
SendTitleConditionList( pClient );
SendRemainTitleTime( pClient );
SendMainTitle( pClient );
SendSubTitle( pClient );
// 피씨방 유저 세팅 및 지속효과 부여
if( ENV().GetInt( "game.pcbang_bonus_server" ) )
{
ARCADIA_LOCK( ArcadiaServer::Instance().LockObjectWithVisibleRange( pClient ) );
pClient->SetPCBangMode( nPCBangMode );
}
// 친구들처리
{
SendFriendsList( pClient );
SendDenialsList( pClient );
SendStatusMessageToFriendOfPlayer( pClient, true );
}
// 아이템 쿨타임 처리
{
// 클라 로딩과 쿨타임 처리 싱크 문제 발생으로 임시로 제거
//SendItemCoolTimeInfo( pClient );
}
// 게임 중독 방지 시스템 관련 프로퍼티 송신
{
// SetContinuousPlayTime에서 스테미너 세이버 관련 지속효과 변화가 있을 경우 지역 방송 발생 함
ARCADIA_LOCK( ArcadiaServer::Instance().LockObjectWithVisibleRange( pClient ) );
AR_TIME nContinuousPlayTime = pClient->GetContinuousPlayTime();
pClient->SetContinuousPlayTime( nContinuousPlayTime ); // 메시지 방송 및 스테미너 세이버 시간 정지 처리 수행
SendPropertyMessage( pClient, pClient->GetHandle(), "playtime_limit1", GameRule::nMaxHealthyGameTime );
SendPropertyMessage( pClient, pClient->GetHandle(), "playtime_limit2", GameRule::nMaxTiredGameTime );
}
// 기부 랭킹 보상 대상자일 경우 메시지 출력
if( RankingManager::Instance().IsValidRankingScore( RankingManager::RANKING_TYPE_DONATION_REWARD, pClient->GetPlayerUID() ) )
{
SendChatMessage( false, CHAT_NOTICE, "@NOTICE", pClient, "@6748" );
}
}
//UnLockIOCPConnection( pConn );
if( GameRule::bUseLoginLogoutDebug )
{
_ctprint( "SendLoginResult (%s, %s, %hu)\n", pClient->GetAccountName(), pClient->GetName(), msg.result );
FileLogHandler::GetFileLogHandler()->LogStringEx( NULL, "LoginLogout_Debug", "SendLoginResult (%s, %s, %hu)", pClient->GetAccountName(), pClient->GetName(), msg.result );
}
// nResult != RESULT_SUCCESS 일 때에는 SendLoginResult 함수 호출 측에서 LogoutNow 호출 해 줌.
// 로그인 처리는 다 성공했는데 그 사이에 접속이 끊겨서 컨넥션이 없어진 경우에만 별도로 로그아웃 처리 해 줌
if( nResult == RESULT_SUCCESS && pClient && !pClient->pConnection )
pClient->LogoutNow( 3 );
}
void SendFriendsList( struct StructPlayer *pClient )
{
std::string strResult = "FLIST|";
struct FriendListEnumerator : public StructPlayer::FriendDenialFunctor
{
FriendListEnumerator( std::string & strFriendList, const char * pszName )
: m_strFriendList( strFriendList )
, m_pszName( pszName )
{}
virtual const bool operator()( std::string & strName )
{
m_strFriendList += strName;
AR_HANDLE hTarget = StructPlayer::FindPlayer( strName.c_str() );
StructPlayer::iterator itFriend = StructPlayer::get( hTarget );
StructPlayer * pFriend = (*itFriend);
if( pFriend )
{
std::vector< std::string >::const_iterator it = std::find( pFriend->m_vDenial.begin(), pFriend->m_vDenial.end(), m_pszName );
if( it != pFriend->m_vDenial.end() )
{ // 차단 됨(오프라인으로 보임)
m_strFriendList += "|2|";
}
else
{ // 온라인
m_strFriendList += "|1|";
}
}
else
{ // 오프라인
m_strFriendList += "|0|";
}
return true;
}
private:
std::string & m_strFriendList;
const char * m_pszName;
} _FriendListEnumerator( strResult, pClient->GetName() );
pClient->DoEachFriend( _FriendListEnumerator );
SendChatMessage( false, CHAT_FRIEND_SYSTEM, "@FRIEND", pClient, strResult.c_str() );
}
void SendDenialsList( struct StructPlayer *pClient )
{
std::string strResult = "DLIST|";
struct DenialListEnumerator : public StructPlayer::FriendDenialFunctor
{
DenialListEnumerator( std::string & strDenialList )
: m_strDenialList( strDenialList )
{}
virtual const bool operator()( std::string & strName )
{
m_strDenialList += strName;
m_strDenialList += "|";
return true;
}
private:
std::string & m_strDenialList;
} _DenialListEnumerator( strResult );
pClient->DoEachDenial( _DenialListEnumerator );
SendChatMessage( false, CHAT_FRIEND_SYSTEM, "@FRIEND", pClient, strResult.c_str() );
}
void SendStatusMessageToFriendOfPlayer( struct StructPlayer *pClient, struct StructPlayer *pTarget, bool bIsLogin )
{
std::string strResult = "FSTATUS|";
strResult += pClient->GetName();
if( bIsLogin )
{
strResult += "|1|";
}
else
{
strResult += "|0|";
}
SendChatMessage( false, CHAT_FRIEND_SYSTEM, "@FRIEND", pTarget, strResult.c_str() );
}
void SendStatusMessageToFriendOfPlayer( struct StructPlayer *pClient, bool bIsLogin )
{
std::string strResult = "FSTATUS|";
strResult += pClient->GetName();
if( bIsLogin )
{
strResult += "|1|";
}
else
{
strResult += "|0|";
}
struct FriendLoginNoticeFunctor : public StructPlayer::FriendDenialFunctor
{
FriendLoginNoticeFunctor( const StructPlayer * pClient, const std::string & strLoginNotice )
: m_pClient( pClient )
, m_strLoginNotice( strLoginNotice )
{}
virtual const bool operator()( std::string & strName )
{
AR_HANDLE hTarget = StructPlayer::FindPlayer( strName.c_str() );
StructPlayer::iterator itFriend = StructPlayer::get( hTarget );
StructPlayer * pFriend = (*itFriend);
if( pFriend )
{
std::vector< std::string >::const_iterator it = std::find( m_pClient->m_vDenial.begin(), m_pClient->m_vDenial.end(), strName );
if( it == m_pClient->m_vDenial.end() )
{
SendChatMessage( false, CHAT_FRIEND_SYSTEM, "@FRIEND", pFriend, m_strLoginNotice.c_str() );
}
}
return true;
}
private:
const StructPlayer * m_pClient;
const std::string & m_strLoginNotice;
} _FriendLoginNoticeFunctor( pClient, strResult );
pClient->DoEachFriendOf( _FriendLoginNoticeFunctor );
}
void SendItemCoolTimeInfo( struct StructPlayer *pPlayer )
{
TS_SC_ITEM_COOL_TIME msg;
AR_TIME t = GetArTime();
for( int i = 0; i < TS_SC_ITEM_COOL_TIME::MAX_ITEM_COOLTIME_GROUP; ++i )
{
msg.cool_time[i] = std::max( (int) pPlayer->GetItemCoolTime( i + 1 ) - (int) t, (int) 0 );
}
PendMessage( pPlayer, &msg );
}
void SendWindowMessage( struct StructPlayer *pPlayer, const char *szWindow, const char *szArgument, const char *szTrigger )
{
TS_SC_SHOW_WINDOW msg;
msg.window_length = static_cast<unsigned short>( strlen( szWindow ) );
msg.argument_length = static_cast<unsigned short>( strlen( szArgument ) );
msg.trigger_length = static_cast<unsigned short>( strlen( szTrigger ) );
msg.size += ( msg.window_length + msg.argument_length + msg.trigger_length );
if( msg.size > 1024 )
return;
char szTmp[ 1024 ];
char *p = szTmp;
int temp_size = sizeof( szTmp );
s_memcpy( p, temp_size, &msg, sizeof(msg) ); p += sizeof(msg); temp_size -= sizeof(msg);
s_memcpy( p, temp_size, szWindow, msg.window_length ); p += msg.window_length; temp_size -= msg.window_length;
s_memcpy( p, temp_size, szArgument, msg.argument_length ); p += msg.argument_length; temp_size -= msg.argument_length;
s_memcpy( p, temp_size, szTrigger, msg.trigger_length ); p += msg.trigger_length; temp_size -= msg.trigger_length;
PendStream( pPlayer, szTmp, msg.size );
}
void SendDialogMessage( struct StructPlayer *pPlayer, AR_HANDLE npc_handle, int type, const char *szTitle, const char *szText, const char *szMenu )
{
TS_SC_DIALOG msg;
msg.npc_handle = npc_handle;
msg.type = type;
msg.title_length = static_cast< unsigned short >( strlen(szTitle) );
msg.text_length = static_cast< unsigned short >( strlen(szText) );
msg.menu_length = static_cast< unsigned short >( strlen(szMenu) );
msg.size += ( msg.title_length + msg.text_length + msg.menu_length );
char szTmp[ 10*1024 ];
char* pBuff = szTmp;
int temp_size = sizeof( szTmp );
if( msg.size > sizeof( szTmp ) )
{
pBuff = new char[msg.size];
temp_size = msg.size;
}
char *p = pBuff;
s_memcpy( p, temp_size, &msg, sizeof(msg) ); p += sizeof(msg); temp_size -= sizeof(msg);
s_memcpy( p, temp_size, szTitle, msg.title_length ); p += msg.title_length; temp_size -= msg.title_length;
s_memcpy( p, temp_size, szText, msg.text_length ); p += msg.text_length; temp_size -= msg.text_length;
s_memcpy( p, temp_size, szMenu, msg.menu_length );
PendStream( pPlayer, pBuff, msg.size );
if( pBuff != szTmp )
{
delete [] pBuff;
}
}
void SendShowCreateGuild( struct StructPlayer *pPlayer )
{
if( !pPlayer )
return;
ARCADIA_LOCK( ArcadiaServer::Instance().LockObjectWithVisibleRange( pPlayer ) );
TS_SC_SHOW_CREATE_GUILD msg;
pPlayer->SetLastContact( "guild", 1 );
PendMessage( pPlayer, &msg );
}
void SendShowCreateAlliance( struct StructPlayer *pPlayer )
{
if( !pPlayer )
return;
ARCADIA_LOCK( ArcadiaServer::Instance().LockObjectWithVisibleRange( pPlayer ) );
TS_SC_SHOW_CREATE_ALLIANCE msg;
pPlayer->SetLastContact( "alliance", 1 );
PendMessage( pPlayer, &msg );
}
void SendMarketInfo( struct StructPlayer *pPlayer, AR_HANDLE npc_handle, struct _MARKET_INFO *pInfo )
{
// 마지막 접선한 상점 이름 저장
pPlayer->SetLastContactMarket( pInfo->strMarketName.c_str() );
// 헤더
TS_SC_MARKET msg;
msg.npc_handle = npc_handle;
msg.item_count = static_cast< unsigned short >( pInfo->vItemList.size() );
msg.size += sizeof(_MARKET_INFO::_MARKET_TAG)*msg.item_count;
char szTmp[10240];
s_memcpy( szTmp, sizeof( szTmp ), &msg, sizeof(msg) );
TS_SC_MARKET::ItemInfo * pItemInfo = reinterpret_cast< TS_SC_MARKET::ItemInfo * >( szTmp+sizeof(msg) );
for( int i = 0; i < msg.item_count; ++i )
{
pItemInfo->code = pInfo->vItemList[i].code;
pItemInfo->price = pInfo->vItemList[i].price.GetRawData();
pItemInfo->huntaholic_point = pInfo->vItemList[i].huntaholic_point;
pItemInfo->arena_point = pInfo->vItemList[i].arena_point;
++pItemInfo;
}
PendStream( pPlayer, szTmp, msg.size );
}
void SendGeneralMessageBox( struct StructPlayer *pPlayer, const char *szText )
{
TS_SC_GENERAL_MESSAGE_BOX msg;
msg.text_length = static_cast<unsigned short>( strlen( szText ) );
msg.size += msg.text_length;
if( msg.size > 1024 )
return;
char szTmp[ 1024 ];
char *p = szTmp;
int temp_size = sizeof( szTmp );
s_memcpy( p, temp_size, &msg, sizeof(msg) ); p += sizeof(msg); temp_size -= sizeof(msg);
s_memcpy( p, temp_size, szText, msg.text_length ); p += msg.text_length; temp_size -= msg.text_length;
PendStream( pPlayer, szTmp, msg.size );
}
void SendItemDestroyMessage( struct StructPlayer * pPlayer, struct StructItem *pItem )
{
TS_SC_DESTROY_ITEM msg;
msg.item_handle = pItem->GetHandle();
PendMessage( pPlayer, &msg );
}
void SendItemCountMessage( struct StructPlayer * pPlayer, struct StructItem *pItem )
{
TS_SC_UPDATE_ITEM_COUNT msg;
msg.item_handle = pItem->GetHandle();
msg.count = pItem->GetCount();
PendMessage( pPlayer, &msg );
}
void SendSkillCardInfo( struct StructItem *pItem )
{
GameObject *pPtr = GameObject::raw_get( pItem->GetOwnerHandle() );
if( !pPtr ) return;
StructPlayer *pPlayer = static_cast< StructPlayer * >( pPtr );
TS_SC_SKILLCARD_INFO msg;
msg.item_handle = pItem->GetHandle();
msg.target_handle = pItem->GetBindedCreatureHandle();
PendMessage( pPlayer, &msg );
}
void SendShowSoulStoneCraftWindow( struct StructPlayer * pPlayer )
{
TS_SC_SHOW_SOULSTONE_CRAFT_WINDOW msg;
pPlayer->SetLastContact( "SoulStoneCraft", 1 );
PendMessage( pPlayer, &msg );
}
void SendShowSoulStoneRepairWindow( struct StructPlayer * pPlayer )
{
TS_SC_SHOW_SOULSTONE_REPAIR_WINDOW msg;
pPlayer->SetLastContact( "RepairSoulStone", 1 );
PendMessage( pPlayer, &msg );
}
void GenerateStateMessage( TS_SC_STATE& msg, AR_HANDLE target_handle, const struct StructState * pState, bool bIsCancel )
{
msg.handle = target_handle;
msg.state_handle = pState->GetUID();
msg.state_code = pState->GetCode();
msg.state_value = pState->GetStateValue();
s_strcpy( msg.state_string_value, _countof( msg.state_string_value ), pState->GetStateStringValue() );
if( !bIsCancel )
{
msg.state_level = pState->GetLevel();
if( pState->IsAura() )
{
msg.end_time = (AR_TIME)(-1);
}
else
{
msg.end_time = pState->GetEndTime();
}
msg.start_time = pState->GetStartTime();
if( msg.start_time && msg.end_time > 0 && msg.start_time > msg.end_time )
{
FILELOG( "start time or end time error!! %d", msg.state_code);
_cprint( "start time or end time error!! %d \n", msg.state_code);
assert( 0 );
}
}
else
{
msg.state_level = 0;
msg.end_time = 0;
msg.start_time = 0;
}
}
void BroadcastStateMessage( struct StructCreature *pCreature, struct StructState *p, bool bIsCancel )
{
TS_SC_STATE msg;
GenerateStateMessage( msg, pCreature->GetHandle(), p, bIsCancel );
ArcadiaServer::Instance().Broadcast( pCreature->GetRX(), pCreature->GetRY(), pCreature->GetLayer(), &msg );
}
void SendStateMessage( const struct StructPlayer *pPlayer, AR_HANDLE target_handle, const struct StructState *p, bool bIsCancel )
{
TS_SC_STATE msg;
GenerateStateMessage( msg, target_handle, p, bIsCancel );
PendMessage( pPlayer, &msg );
}
void SendSkillResetMessage( const struct StructPlayer *pPlayer, const struct StructCreature *pCreature )
{
TS_SC_SKILL_LIST msg;
msg.count = 0;
msg.modification_type = TS_SC_SKILL_LIST::REFRESH;
msg.target = pCreature->GetHandle();
PendMessage( pPlayer, &msg );
}
void SendSkillMessage( const struct StructPlayer *pPlayer, const struct StructCreature *pCreature, int skill_id, bool refresh )
{
char buf[4096];
TS_SC_SKILL_LIST msg;
msg.count = 0;
msg.modification_type = refresh ? TS_SC_SKILL_LIST::REFRESH : TS_SC_SKILL_LIST::UPDATE;
msg.target = pCreature->GetHandle();
if( skill_id < 0 )
{
struct mySkillFunctor : StructCreature::SKILL_FUNCTOR
{
mySkillFunctor( const StructPlayer *p, char *q, int size ) : pPlayer( p ), pBuf( q ), nBufSize( size ), cnt(0) {}
virtual void onSkill( int sid, int skill_id, int base_skill_level, int current_skill_level, AR_TIME total_cool_time, AR_TIME remain_cool_time )
{
TS_SC_SKILL_LIST::SkillInfo info;
info.skill_id = skill_id;
info.base_skill_level = base_skill_level;
info.current_skill_level = current_skill_level;
info.total_cool_time = total_cool_time;
info.remain_cool_time = remain_cool_time;
assert( nBufSize >= sizeof(info) );
if( nBufSize >= sizeof(info) )
{
s_memcpy( pBuf, nBufSize, &info, sizeof(info) );
pBuf += sizeof(info);
nBufSize -= sizeof(info);
++cnt;
}
else
{
_cprint( "SendSkillMessage(skill_id) Creature INSUFFICIENT_BUFFER count: %d", cnt );
FILELOG( "SendSkillMessage(skill_id) Creature INSUFFICIENT_BUFFER count: %d", cnt );
}
}
unsigned cnt;
char * pBuf;
int nBufSize;
const StructPlayer *pPlayer;
} mySkillFunctorInst( pPlayer, buf + sizeof( msg ), sizeof(buf)-sizeof(msg) );
pCreature->EnumSkill( mySkillFunctorInst );
msg.count = mySkillFunctorInst.cnt;
}
else if( pCreature->GetSkill( skill_id )->GetSkillUID() > 0 )
{
TS_SC_SKILL_LIST::SkillInfo info;
info.skill_id = skill_id;
info.base_skill_level = pCreature->GetBaseSkillLevel( skill_id );
info.current_skill_level = pCreature->GetCurrentSkillLevel( skill_id );
info.total_cool_time = pCreature->GetTotalCoolTime( skill_id );
info.remain_cool_time = pCreature->GetRemainCoolTime( skill_id );
msg.count = 1;
s_memcpy( buf + sizeof(msg), sizeof( buf ) - sizeof( msg ), &info, sizeof(info) );
}
msg.size += msg.count * sizeof( TS_SC_SKILL_LIST::SkillInfo );
s_memcpy( buf, sizeof( buf ), &msg, sizeof( msg ) );
if( msg.count || msg.modification_type == TS_SC_SKILL_LIST::REFRESH ) PendStream( pPlayer, buf, msg.size );
}
void SendSkillMessage( const struct StructPlayer *pPlayer, const struct StructCreature *pCreature, std::vector< int > vSkill )
{
char buf[4096];
TS_SC_SKILL_LIST msg;
msg.count = 0;
msg.modification_type = TS_SC_SKILL_LIST::UPDATE;
msg.target = pCreature->GetHandle();
struct mySkillFunctor : StructCreature::SKILL_FUNCTOR
{
mySkillFunctor( std::vector< int > & s, char *q, int size ) : vSkill( s ), pBuf( q ), nBuffSize( size ), cnt(0) {}
virtual void onSkill( int sid, int skill_id, int base_skill_level, int current_skill_level, AR_TIME total_cool_time, AR_TIME remain_cool_time )
{
std::vector< int >::iterator it = std::find( vSkill.begin(), vSkill.end(), skill_id );
if( it != vSkill.end() )
{
TS_SC_SKILL_LIST::SkillInfo info;
info.skill_id = skill_id;
info.base_skill_level = base_skill_level;
info.current_skill_level = current_skill_level;
info.total_cool_time = total_cool_time;
info.remain_cool_time = remain_cool_time;
assert( nBuffSize >= sizeof(info) );
if( nBuffSize >= sizeof(info) )
{
s_memcpy( pBuf, nBuffSize, &info, sizeof(info) );
pBuf += sizeof(info);
nBuffSize -= sizeof(info);
++cnt;
}
else
{
_cprint( "SendSkillMessage(vSkill) Creature INSUFFICIENT_BUFFER count: %d", cnt );
FILELOG( "SendSkillMessage(vSkill) Creature INSUFFICIENT_BUFFER count: %d", cnt );
}
vSkill.erase( it );
}
}
std::vector< int > & vSkill;
char * pBuf;
int nBuffSize;
unsigned cnt;
} mySkillFunctorInst( vSkill, buf + sizeof( msg ), sizeof(buf)-sizeof(msg) );
pCreature->EnumSkill( mySkillFunctorInst );
msg.count = mySkillFunctorInst.cnt;
msg.size += msg.count * sizeof( TS_SC_SKILL_LIST::SkillInfo );
s_memcpy( buf, sizeof( buf ), &msg, sizeof( msg ) );
if( msg.count ) PendStream( pPlayer, buf, msg.size );
}
void SendAddedSkillMessage( const struct StructPlayer *pPlayer, const struct StructCreature *pCreature )
{
char buf[4096];
TS_SC_ADDED_SKILL_LIST msg;
msg.count = 0;
msg.target = pCreature->GetHandle();
struct mySkillFunctor : StructCreature::ADDED_SKILL_FUNCTOR
{
mySkillFunctor( const StructPlayer *p, char *q, int size ) : pPlayer( p ), pBuf( q ), nBuffSize( size ), cnt( 0 ) { }
virtual void onSkill( int skill_id, bool restricted_to_type, char added_skill_level )
{
TS_SC_ADDED_SKILL_LIST::AddedSkillInfo info;
info.skill_id = skill_id;
info.restricted_to_type = restricted_to_type;
info.added_skill_level = added_skill_level;
assert( nBuffSize >= sizeof(info) );
if( nBuffSize >= sizeof( info ) )
{
s_memcpy( pBuf, nBuffSize, &info, sizeof( info ) );
pBuf += sizeof( info );
nBuffSize -= sizeof( info );
cnt++;
}
else
{
_cprint( "SendAddedSkillMessage() Creature INSUFFICIENT_BUFFER count: %d", cnt );
FILELOG( "SendAddedSkillMessage() Creature INSUFFICIENT_BUFFER count: %d", cnt );
}
}
unsigned cnt;
char * pBuf;
int nBuffSize;
const StructPlayer *pPlayer;
} mySkillFunctorInst( pPlayer, buf + sizeof( msg ), sizeof( buf ) - sizeof( msg ) );
pCreature->EnumAddedSkill( mySkillFunctorInst );
msg.count = mySkillFunctorInst.cnt;
msg.size += msg.count * sizeof( TS_SC_ADDED_SKILL_LIST::AddedSkillInfo );
s_memcpy( buf, sizeof( buf ), &msg, sizeof( msg ) );
PendStream( pPlayer, buf, msg.size );
}
void SendSkillLevelMessage( const struct StructPlayer *pPlayer, const struct StructCreature *pCreature )
{
char buf[4096];
TS_SC_SKILL_LEVEL_LIST msg;
msg.count = 0;
struct mySkillFunctor : StructCreature::SKILL_FUNCTOR
{
mySkillFunctor( const StructPlayer *p, char *q, int size ) : pPlayer( p ), pBuf( q ), nBuffSize( size ), cnt(0) {}
virtual void onSkill( int sid, int skill_id, int base_skill_level, int current_skill_level, AR_TIME total_cool_time, AR_TIME remain_cool_time )
{
TS_SC_SKILL_LEVEL_LIST::SkillLevelInfo info;
info.skill_id = skill_id;
info.skill_level = base_skill_level;
assert( nBuffSize >= sizeof(info) );
if( nBuffSize >= sizeof(info) )
{
s_memcpy( pBuf, nBuffSize, &info, sizeof(info) );
pBuf += sizeof(info);
nBuffSize -= sizeof(info);
++cnt;
}
else
{
_cprint( "SendSkillLevelMessage() Creature INSUFFICIENT_BUFFER count: %d", cnt );
FILELOG( "SendSkillLevelMessage() Creature INSUFFICIENT_BUFFER count: %d", cnt );
}
}
unsigned cnt;
char * pBuf;
int nBuffSize;
const StructPlayer *pPlayer;
} mySkillFunctorInst( pPlayer, buf + sizeof( msg ), sizeof( buf ) - sizeof( msg ) );
pCreature->EnumSkill( mySkillFunctorInst );
msg.count = mySkillFunctorInst.cnt;
msg.size += msg.count * sizeof( TS_SC_SKILL_LEVEL_LIST::SkillLevelInfo );
s_memcpy( buf, sizeof( buf ), &msg, sizeof( msg ) );
PendStream( pPlayer, buf, msg.size );
}
void BroadcastSkillEffectMessage( const struct StructCreature *pCaster, const struct StructCreature *pTarget, int skill_level, int skill_id, int var1, unsigned char var2, char var3 )
{
TS_SC_SKILL_EFFECT msg;
msg.target = pTarget->GetHandle();
msg.caster = pCaster->GetHandle();
msg.var1 = var1;
msg.var2 = var2;
msg.var3 = var3;
msg.skill_level = skill_level;
msg.skill_id = skill_id;
ArcadiaServer::Instance().Broadcast( pCaster->GetRX(), pCaster->GetRY(), pCaster->GetLayer(), &msg );
}
void SendDetectRangeMessage( const struct StructPlayer *pPlayer, const struct StructCreature *pCreature )
{
TS_SC_DETECT_RANGE_UPDATE msg;
msg.handle = pCreature->GetHandle();
msg.detect_range = pCreature->GetDetectHideRange();
PendMessage( pPlayer, &msg );
}
void SendTradeCancelMessage( struct StructPlayer * pClient )
{
TS_TRADE msg;
msg.mode = TS_TRADE::CANCEL_TRADE;
msg.target_player = pClient->GetTradeTarget();
PendMessage( pClient, &msg );
}
void SendCantAttackMsg( const struct StructPlayer *pPlayer, AR_HANDLE attacker_handle, AR_HANDLE target_handle, int reason )
{
TS_SC_CANT_ATTACK msg;
msg.attacker_handle = attacker_handle;
msg.target_handle = target_handle;
msg.reason = reason;
PendMessage( pPlayer, &msg );
}
void SendAuraMsessage( const struct StructPlayer *pPlayer, const int skillId, const bool status )
{
TS_SC_AURA msg;
msg.caster = pPlayer->GetHandle();
msg.skill_id = skillId;
msg.status = status;
PendMessage( pPlayer, &msg );
}
void SendAuraMsessage( const struct StructCreature *pCreature, const int skillId, const bool status )
{
if( pCreature->IsPlayer() )
{
SendAuraMsessage( static_cast< const StructPlayer * >( pCreature ), skillId, status );
}
else if( pCreature->IsSummon() && static_cast< const StructSummon * >( pCreature )->GetMaster() )
{
SendAuraMsessage( static_cast< const StructSummon * >( pCreature )->GetMaster(), skillId, status);
}
}
// 아래 구현이 더 효율적이지 않을까 싶음
/*
const unsigned short TM_SC_PARTY_MEMBER_INFO = 518;
struct TS_SC_PARTY_MEMBER_INFO : TS_MESSAGE
{
TS_SC_PARTY_MEMBER_INFO() _INIT( TM_SC_PARTY_MEMBER_INFO )
AR_HANDLE handle;
int level;
int job_id;
unsigned char hp_percentage;
unsigned char mp_percentage;
int x;
int y;
unsigned char name_length;
char name[];
};
void SendPartyMemberInfo( struct StructPlayer* pPlayer, struct StructPlayer* pTarget )
{
char buf[256];
TS_SC_PARTY_MEMBER_INFO* msg = reinterpret_cast< char* >( buf );
msg->handle = pPlayer->GetHandle();
msg->level = pPlayer->GetLevel();
msg->job_id = pPlayer->GetJobId();
msg->hp_percentage = pPlayer->GetHPPercentage();
msg->mp_percentage = pPlayer->GetMPPercentage();
msg->x = pPlayer->GetX();
msg->y = pPlayer->GetY();
msg->name_length = strlen( pPlayer->GetName() );
s_memcpy( msg+1, sizeof( buf ) - sizeof( msg ), pPlayer->GetName(), msg->name_length );
msg->size += msg->size + msg->name_length;
PendMessage( pTarget, msg );
}
void BroadcastPartyMemberInfo( int nPartyID, struct StructPlayer *pPlayer )
{
struct myAttackTeamFunctor : PartyManager::PartyFunctor
{
myAttackTeamFunctor( StructPlayer *pPlayer ) : pPlayer( pPlayer ) {}
virtual bool operator()( AR_HANDLE handle )
{
StructPlayer *pMember = static_cast< StructPlayer * >( GameObject::raw_get( *it ) );
if( !pMember && IsVisibleRegion( pPlayer->GetRX(), pPlayer->GetRY(), pMember->GetRX(), pMember->GetRX() )
{
SendPartyMemberInfo( pPlayer, pMember );
return true;
}
else
{
return false
}
}
StructPlayer *pPlayer;
} _fo( pPlayer, szString, nChatType );
PartyManager::GetInstance().DoEachLinkedPartyMember( nPartyID, _fo );
}
*/
void BroadcastPartyMemberInfo( int nPartyID, struct StructPlayer *pPlayer )
{
char buf[256];
s_sprintf( buf, _countof( buf ), "MINFO|%u|%s|%d|%d|%d|%d|%d|%d|%d|", pPlayer->GetHandle(), pPlayer->GetAlias(), pPlayer->GetLevel(), pPlayer->GetJobId(), pPlayer->GetHPPercentage(), pPlayer->GetMPPercentage(), (int)pPlayer->GetX(), (int)pPlayer->GetY(), 2 );
SendPartyChatMessage( CHAT_PARTY_SYSTEM, "@PARTY", nPartyID, buf, (unsigned)strlen(buf) );
}
void BroadcastLinkedPartyMemberInfo( int nPartyID, struct StructPlayer *pPlayer )
{
char buf[256];
if( !PartyManager::GetInstance().IsLinkedParty( nPartyID ) )
return;
s_sprintf( buf, _countof( buf ), "RMINFO|%u|%s|%d|%d|", pPlayer->GetHandle(), pPlayer->GetAlias(), pPlayer->GetJobId(), pPlayer->GetHPPercentage() != 0 );
SendLinkedPartyChatMessage( false, CHAT_PARTY_SYSTEM, "@PARTY", nPartyID, buf );
}
void SendLinkedPartyChatMessage( bool bLock, int nChatType, const char *szSender, int nPartyID, const char *szString )
{
struct myPartyFunctor : PartyManager::PartyFunctor
{
myPartyFunctor( const char *s, const char *p, int t ) : szSender( s ), pString( p ), nChatType( t ) {}
virtual bool operator()( AR_HANDLE handle )
{
SendChatMessage( false, nChatType, szSender, handle, pString );
return true;
}
int nChatType;
const char *szSender;
const char *pString;
} _fo( szSender, szString, nChatType );
if( PartyManager::GetInstance().IsLinkedParty( nPartyID ) )
PartyManager::GetInstance().DoEachLinkedPartyMember( nPartyID, _fo );
else
PartyManager::GetInstance().DoEachMember( nPartyID, _fo );
}
/* TODO: Get player ID to check if he is in a guild and check if the guild has an alliance.
* This should make it easier to collaborate on this shit
*/
void SendAttackTeamChatMessageByGuildID( bool bLock, int nChatType, const char *szSender, int nGuildID, const char *szString )
{
int nPartyID = PartyManager::GetInstance().GetAttackTeamLeadPartyIDByGuildID( nGuildID );
if (PartyManager::GetInstance().GetPartyType( nPartyID ) == PartyManager::TYPE_RAID_ATTACKTEAM) {
if( !nPartyID )
return;
SendLinkedPartyChatMessage( bLock, nChatType, szSender, nPartyID, szString );
}
}
void PrintfLinkedPartyChatMessage( bool bLock, int nChatType, const char *szSender, int nPartyID, const char *szString, ... )
{
char szBuf[2048];
va_list va;
va_start( va, szString );
s_vsprintf( szBuf, _countof(szBuf), szString, va );
va_end( va );
SendLinkedPartyChatMessage( bLock, nChatType, szSender, nPartyID, szBuf );
}
/*
* This might be a clue since it takes the name of the sender as a parameter, the method that calls it should have info about the client.
*/
void PrintfAttackTeamChatMessageByGuildID( bool bLock, int nChatType, const char *szSender, int nGuildID, const char *szString, ... )
{
char szBuf[2048];
va_list va;
va_start( va, szString );
s_vsprintf( szBuf, _countof(szBuf), szString, va );
va_end( va );
SendAttackTeamChatMessageByGuildID( bLock, nChatType, szSender, nGuildID, szBuf);
}
void BroadcastLinkedPartyInfo( int nPartyID )
{
std::string strLinkedPartyInfo = PartyManager::GetInstance().GetLinkedPartyInfo( nPartyID );
if( !strLinkedPartyInfo.empty() )
SendLinkedPartyChatMessage( false, CHAT_RAID_SYSTEM, "@RAID", nPartyID, strLinkedPartyInfo.c_str() );
}
void SendLinkedPartyInfo( struct StructPlayer *pPlayer )
{
int nPartyID = pPlayer->GetPartyID();
if( !nPartyID )
return;
if( !PartyManager::GetInstance().IsLinkedParty( nPartyID ) )
return;
std::string strRaidPartyInfo = PartyManager::GetInstance().GetLinkedPartyInfo( nPartyID );
SendChatMessage( false, CHAT_RAID_SYSTEM, "@RAID", pPlayer, strRaidPartyInfo.c_str() );
}
void BroadcastPartyLeave( struct StructPlayer *pPlayer, bool bByBattleArena )
{
BroadcastPartyLeave( pPlayer->GetPartyID(), pPlayer->GetName(), bByBattleArena );
}
void BroadcastPartyLeave( int nPartyID, const char * pszName, bool bByBattleArena )
{
PrintfLinkedPartyChatMessage( false, CHAT_PARTY_SYSTEM, "@PARTY", nPartyID, "LEAVE|%s|%d|", pszName, ( bByBattleArena ) ? 1 : 0 );
}
void BroadcastPartyDestroy( int nPartyID, bool bByBattleArena )
{
// 시즈/레이드 공대 파티의 마스터 파티가 해산되는 경우에는 링크된 모든 파티의 파티원들에게 각각 자신의 파티가 해산되는 것을 방송
if( PartyManager::GetInstance().IsAttackTeamParty( nPartyID ) && nPartyID == PartyManager::GetInstance().GetLinkedPartyLeadPartyID( nPartyID ) )
{
struct myPartyDestroyFunctor : PartyManager::PartyFunctor
{
myPartyDestroyFunctor( bool _bByBattleArena )
: bByBattleArena( _bByBattleArena )
{}
virtual bool operator()( AR_HANDLE handle )
{
StructPlayer *pPlayer = static_cast< StructPlayer * >( GameObject::raw_get( handle ) );
if( !pPlayer ) return true;
PrintfChatMessage( false, CHAT_PARTY_SYSTEM, "@PARTY", pPlayer, "DESTROY|%s|%d|", PartyManager::GetInstance().GetPartyName( pPlayer->GetPartyID() ).c_str(), ( bByBattleArena ) ? 1 : 0 );
return true;
}
bool bByBattleArena;
} _fo( bByBattleArena );
PartyManager::GetInstance().DoEachLinkedPartyMember( nPartyID, _fo );
}
// 그 외에는 링크된 파티원 전원(링크된 파티가 없는 단일 파티라면 자기네 파티원 전원)이 해산된 파티에 대한 해산 메시지만 받으면 됨
else
{
PrintfLinkedPartyChatMessage( false, CHAT_PARTY_SYSTEM, "@PARTY", nPartyID, "DESTROY|%s|%d|", PartyManager::GetInstance().GetPartyName( nPartyID ).c_str(), ( bByBattleArena ) ? 1 : 0 );
}
}
void SendPartyInfoByID( struct StructPlayer *pPlayer, const int nPartyID )
{
std::string strPartyInfo;
// 배틀 아레나 연습 경기용 파티의 경우 경기 시작과 함께 실제 경기용 파티로 변경되므로 클라에는 무조건 실제 경기용 파티 타입으로 방송
PartyManager::_PARTY_TYPE ePartyType = PartyManager::GetInstance().GetPartyType( nPartyID );
XStringUtil::Format( strPartyInfo, "PINFO|%d|%s|%s|%d|%d|%d|%d|", nPartyID, PartyManager::GetInstance().GetPartyName( nPartyID ).c_str(),
PartyManager::GetInstance().GetLeaderDisplayName( nPartyID ).c_str(), PartyManager::GetInstance().GetShareMode( nPartyID ),
PartyManager::GetInstance().GetMaxLevel( nPartyID ), PartyManager::GetInstance().GetMinLevel( nPartyID ),
( ePartyType != PartyManager::TYPE_BATTLE_ARENA_EXERCISE_TEAM ) ? ePartyType : PartyManager::TYPE_BATTLE_ARENA_TEAM );
struct myPartyMemberTagFunctor : public PartyManager::PartyMemberTagFunctor
{
myPartyMemberTagFunctor( std::string & _strPartyInfo )
: strPartyInfo( _strPartyInfo )
{}
virtual bool operator()( PartyManager::PartyMemberTag * pMemberTag )
{
bool bIsProc = false;
do
{
if( !pMemberTag->bIsOnline )
break;
StructPlayer::iterator pit = StructPlayer::get( StructPlayer::FindPlayer( pMemberTag->strName.c_str() ) );
StructPlayer * pPlayer = *pit;
if( !pPlayer )
break;
char buf[512];
s_sprintf( buf, _countof( buf ), "%u|%s|%d|%d|%d|%d|%d|%d|%d|", pPlayer->GetHandle(), pMemberTag->strDisplayName.c_str(), pMemberTag->nLevel, pMemberTag->nJobId, pPlayer->GetHPPercentage(), pPlayer->GetMPPercentage(), (int)pPlayer->GetX(), (int)pPlayer->GetY(), 2 );
strPartyInfo += buf;
bIsProc = true;
} while( false );
if( !bIsProc )
{
char buf[512];
s_sprintf( buf, _countof( buf ), "0|%s|%d|%d|0|0|0|0|0|", pMemberTag->strDisplayName.c_str(), pMemberTag->nLevel, pMemberTag->nJobId );
strPartyInfo += buf;
}
return true;
}
std::string & strPartyInfo;
} _fo( strPartyInfo );
PartyManager::GetInstance().DoEachMemberTag( nPartyID, _fo );
SendChatMessage( false, CHAT_PARTY_SYSTEM, "@PARTY", pPlayer, strPartyInfo.c_str() );
}
void SendPartyInfo( struct StructPlayer *pPlayer )
{
int nPartyID = pPlayer->GetPartyID();
if( !nPartyID )
return;
SendPartyInfoByID( pPlayer, nPartyID );
}
void SendPartyPosition( struct StructPlayer *pPlayer )
{
int nPartyID = pPlayer->GetPartyID();
if( !nPartyID )
return;
struct myPartyFunctor : PartyManager::PartyFunctor
{
virtual bool operator()( AR_HANDLE handle )
{
StructPlayer *pPlayer = static_cast< StructPlayer * >( GameObject::raw_get( handle ) );
if( !pPlayer ) return true;
char buf[512];
s_sprintf( buf, _countof( buf ), "%u|%d|%d|", pPlayer->GetHandle(), (int)pPlayer->GetX(), (int)pPlayer->GetY() );
strList += buf;
return true;
}
std::string strList;
} _fo;
if( PartyManager::GetInstance().IsLinkedParty( nPartyID ) )
{
PartyManager::GetInstance().DoEachLinkedPartyMember( nPartyID, _fo );
PrintfLinkedPartyChatMessage( false, CHAT_PARTY_SYSTEM, "@PARTY", nPartyID, "PPOS|%s", _fo.strList.c_str() );
}
else
{
PartyManager::GetInstance().DoEachMember( nPartyID, _fo );
PrintfChatMessage( false, CHAT_PARTY_SYSTEM, "@PARTY", pPlayer, "PPOS|%s", _fo.strList.c_str() );
}
}
void BroadcastGuildMemberInfo( int nGuildID, StructPlayer *pPlayer )
{
char buf[256];
s_sprintf( buf, _countof( buf ), "MINFO|%u|%s|%d|%d|%d|%d|%d|%d|%d|%d|%d|%d|", pPlayer->GetHandle(), pPlayer->GetName(), pPlayer->GetLevel(), pPlayer->GetJobId(), pPlayer->GetGuildPermission(), pPlayer->GetHPPercentage(), pPlayer->GetMPPercentage(), (int)pPlayer->GetX(), (int)pPlayer->GetY(), 2, pPlayer->GetGuildPoint(), pPlayer->GetGuildTotalPoint() ); //AziaMafia Fix guild
SendGuildChatMessage( CHAT_GUILD_SYSTEM, "@GUILD", nGuildID, buf, (unsigned)strlen(buf) );
s_sprintf( buf, _countof( buf ), "MEMO|%s|%s|", pPlayer->GetName(), GuildManager::GetInstance().GetMemberMemo( nGuildID, pPlayer->GetSID() ).c_str() );
SendGuildChatMessage( CHAT_GUILD_SYSTEM, "@GUILD", nGuildID, buf, (unsigned)strlen(buf) );
}
void SendGuildStaticInfo( struct StructPlayer * pPlayer )
{
int nGuildID = pPlayer->GetGuildID();
if( !nGuildID )
return;
// 소유 중인 던전 ID 얻기(연합 관련 체크 적용)
int nOwnDungeonID = 0;
{
int nAllianceID = GuildManager::GetInstance().GetAllianceID( nGuildID );
int nRaidGuildID = nGuildID;
if( nAllianceID )
nRaidGuildID = GuildManager::GetInstance().GetAllianceLeaderGuildID( nAllianceID );
nOwnDungeonID = GuildManager::GetInstance().GetRaidDungeonID( nRaidGuildID );
// 레이드/시즈 진행 중이지만 주인은 아닌 경우에는 제외
if( DungeonManager::Instance().GetOwnGuildID( nOwnDungeonID ) != nRaidGuildID )
nOwnDungeonID = 0;
}
char buf[512];
s_sprintf( buf, _countof( buf ), "GINFO|%d|%s|%s|%d|%s|%d|%d|%s|%d|%d|",
nGuildID,
GuildManager::GetInstance().GetGuildName( nGuildID ).c_str(),
StructPlayer::GetPlayerName( GuildManager::GetInstance().GetLeaderSID( nGuildID ) ),
GuildManager::GetInstance().GetMemberCount( nGuildID ),
GuildManager::GetInstance().GetGuildNotice( nGuildID ).c_str(),
GuildManager::GetInstance().IsRecruitingGuild( nGuildID ),
nOwnDungeonID,
GuildManager::GetInstance().GetGuildURL( nGuildID ).c_str(),
GuildManager::GetInstance().GetGuildGrade( nGuildID ),
GuildManager::GetInstance().GetGuildPoint( nGuildID ) );
SendChatMessage( false, CHAT_GUILD_SYSTEM, "@GUILD", pPlayer, buf );
SendGuildPermissionInfo( pPlayer );
SendAllianceInfo( pPlayer );
SendGuildAdvertiseInfo( pPlayer );
// 길드의 던전 레이드/시즈 팁은 해당 UI를 열었을 때 클라이언트가 채팅 명령어를 서버로 전달하므로 그 때만 보내면 됨
//SendGuildDungeonRaidSiegeTip( pPlayer );
}
void SendGuildMemberInfo( struct StructPlayer * pPlayer )
{
int nGuildID = pPlayer->GetGuildID();
if( !nGuildID )
return;
struct myGuildFunctor : GuildManager::GuildFunctor
{
myGuildFunctor( StructPlayer * pPlayer )
: m_pPlayer( pPlayer )
{}
virtual bool operator()( AR_HANDLE handle )
{
StructPlayer * pPlayer = static_cast< StructPlayer * >( GameObject::raw_get( handle ) );
if( !pPlayer ) return true;
char buf[512];
s_sprintf( buf, _countof( buf ), "GMEMBER|%u|%s|%d|%d|%d|%d|%d|%d|%d|%d|%d|%d|", pPlayer->GetHandle(), pPlayer->GetName(), pPlayer->GetLevel(), pPlayer->GetJobId(), pPlayer->GetGuildPermission(), pPlayer->GetHPPercentage(), pPlayer->GetMPPercentage(), (int)pPlayer->GetX(), (int)pPlayer->GetY(), 2, pPlayer->GetGuildPoint(), pPlayer->GetGuildTotalPoint() );
SendChatMessage( false, CHAT_GUILD_SYSTEM, "@GUILD", m_pPlayer, buf );
return true;
}
StructPlayer * m_pPlayer;
} _fo( pPlayer );
GuildManager::GetInstance().DoEachMember( nGuildID, _fo );
std::vector< GuildManager::GuildMemberTag > vList;
GuildManager::GetInstance().GetOfflineMember( nGuildID, vList );
char buf[512];
for( std::vector< GuildManager::GuildMemberTag >::iterator it = vList.begin(); it != vList.end(); ++it )
{
s_sprintf( buf, _countof( buf ), "GMEMBER|0|%s|%d|%d|%d|0|0|0|0|0|%d|%d", StructPlayer::GetPlayerName( (*it).sid ), (*it).nLevel, (*it).nJobId, (*it).nPermission, (*it).nPoint, (*it).nTotalPoint );
SendChatMessage( false, CHAT_GUILD_SYSTEM, "@GUILD", pPlayer, buf );
}
struct myGuildMemoFunctor : GuildManager::GuildMemberTagFunctor
{
myGuildMemoFunctor( StructPlayer * pPlayer )
: m_pPlayer( pPlayer )
{}
virtual bool operator()( GuildManager::GuildMemberTag * pMemberTag )
{
char szBuffer[256];
s_sprintf( szBuffer, _countof( szBuffer ), "MEMO|%s|%s|", StructPlayer::GetPlayerName( pMemberTag->sid ), pMemberTag->strMemo.c_str() );
SendChatMessage( false, CHAT_GUILD_SYSTEM, "@GUILD", m_pPlayer, szBuffer );
return true;
}
private:
StructPlayer * m_pPlayer;
} _foMemo( pPlayer );
GuildManager::GetInstance().DoEachMemberTag( nGuildID, _foMemo );
}
void SendGuildInfo( StructPlayer *pPlayer )
{
SendGuildStaticInfo( pPlayer );
SendGuildMemberInfo( pPlayer );
}
void SendGuildPermissionInfo( struct StructPlayer *pPlayer )
{
int nGuildID = pPlayer->GetGuildID();
if( !nGuildID )
return;
char buf[512];
s_sprintf( buf, _countof( buf ), "GPERMISSION|%s|%d|%s|%d|%s|%d|%s|%d|%s|%d|%s|%d|",
// PERMISSION_MEMBER_LEAST = 1
GuildManager::GetInstance().GetPermissionName( nGuildID, 1 ).c_str(), GuildManager::GetInstance().GetPermissionSet( nGuildID, 1 ),
GuildManager::GetInstance().GetPermissionName( nGuildID, 2 ).c_str(), GuildManager::GetInstance().GetPermissionSet( nGuildID, 2 ),
GuildManager::GetInstance().GetPermissionName( nGuildID, 3 ).c_str(), GuildManager::GetInstance().GetPermissionSet( nGuildID, 3 ),
GuildManager::GetInstance().GetPermissionName( nGuildID, 4 ).c_str(), GuildManager::GetInstance().GetPermissionSet( nGuildID, 4 ),
GuildManager::GetInstance().GetPermissionName( nGuildID, 5 ).c_str(), GuildManager::GetInstance().GetPermissionSet( nGuildID, 5 ),
GuildManager::GetInstance().GetPermissionName( nGuildID, 6 ).c_str(), GuildManager::GetInstance().GetPermissionSet( nGuildID, 6 ) );
// PERMISSION_MEMBER_MAX = 6
SendChatMessage( false, CHAT_GUILD_SYSTEM, "@GUILD", pPlayer, buf );
}
void getAllianceInfo( const int nAllianceID, char * pszBuf, size_t buf_len )
{
int nLength = s_sprintf( pszBuf, buf_len, "AINFO|%d|%s|%d|%d|", nAllianceID, GuildManager::GetInstance().GetAllianceName( nAllianceID ).c_str(),
GuildManager::GetInstance().GetAllianceMemberGuildCount( nAllianceID ), GuildManager::GetInstance().GetAllianceLeaderGuildID( nAllianceID ) );
if( nLength < 0 )
{
return;
}
struct _myAllianceMemberFunctor : GuildManager::GuildListFunctor
{
_myAllianceMemberFunctor( char * pszBuffer, size_t len ) : m_pszBuffer( pszBuffer ), m_bufLen( len ) {}
virtual bool operator()( int nGuildID, const char * szGuildName, const char * szGuildLeaderName, int nGuildLeaderLevel, int nGuildMemberCount, int nRaidDungeonID )
{
int nLength = s_sprintf( m_pszBuffer, m_bufLen, "%d|%s|%s|%d|", nGuildID, szGuildName, szGuildLeaderName, nGuildMemberCount );
assert( nLength > 0 );
if( nLength > 0 && m_bufLen >= nLength )
{
m_pszBuffer += nLength;
m_bufLen -= nLength;
}
return true;
}
char * m_pszBuffer;
size_t m_bufLen;
} _fo( pszBuf + nLength, buf_len - nLength );
GuildManager::GetInstance().DoEachAllianceGuild( nAllianceID, _fo );
}
void SendAllianceInfo( struct StructPlayer *pPlayer )
{
int nGuildID = pPlayer->GetGuildID();
if( !nGuildID )
return;
int nAllianceID = GuildManager::GetInstance().GetAllianceID( nGuildID );
if( !nAllianceID )
return;
char buf[ 1024 ];
getAllianceInfo( nAllianceID, buf, _countof( buf ) );
SendChatMessage( false, CHAT_GUILD_SYSTEM, "@GUILD", pPlayer, buf );
}
void BroadcastAllianceInfo( const int nAllianceID )
{
char buf[ 1024 ];
getAllianceInfo( nAllianceID, buf, _countof( buf ) );
std::vector< int > & vGuildList = GuildManager::GetInstance().GetAllianceMemberID( nAllianceID );
for( std::vector< int >::const_iterator it = vGuildList.begin() ; it != vGuildList.end() ; ++it )
{
SendGuildChatMessage( CHAT_GUILD_SYSTEM, "@GUILD", (*it), buf );
}
}
void SendGuildAdvertiseInfo( struct StructPlayer *pPlayer )
{
int nGuildID = pPlayer->GetGuildID();
if( !nGuildID )
return;
char buf[512];
s_sprintf( buf, _countof( buf ), "GADV|%d|%d|%s|", GuildManager::GetInstance().GetGuildAdvertiseType( nGuildID ),
static_cast< int >( GuildManager::GetInstance().GetGuildAdvertiseEndTime( nGuildID ) - time( NULL ) ),
GuildManager::GetInstance().GetGuildAdvertiseComment( nGuildID ).c_str() );
SendChatMessage( false, CHAT_GUILD_SYSTEM, "@GUILD", pPlayer, buf );
}
void SendGuildDungeonRaidSiegeTip( struct StructPlayer *pPlayer )
{
int nGuildID = pPlayer->GetGuildID();
if( !nGuildID )
return;
int nAllianceID = GuildManager::GetInstance().GetAllianceID( nGuildID );
int nRaidGuildID = nGuildID;
if( nAllianceID )
nRaidGuildID = GuildManager::GetInstance().GetAllianceLeaderGuildID( nAllianceID );
int nDungeonID = GuildManager::GetInstance().GetRaidDungeonID( nRaidGuildID );
char buf[512];
// 레이드 신청 전에는 자신의 길드에 대한 정보만 송신
if( !nDungeonID )
{
s_sprintf( buf, _countof( buf ), "GRAIDSIEGETIP|0|0| | |0|0|" );
}
else
{
int nType = 0;
int nTipGuildID = 0;
AR_TIME nTipRecord = 0;
// 레이드 기간이면 레이드 1위 길드 정보 확인
if( DungeonManager::Instance().IsDungeonRaidTime( nDungeonID ) )
{
nType = 1;
DungeonManager::Instance().GetTopDungeonRaidRecord( nDungeonID, nTipGuildID, nTipRecord );
}
// 시즈 기간이면
else
{
// 주인 길드면 공격자 길드 정보 확인
int nOwnerGuildID = DungeonManager::Instance().GetOwnGuildID( nDungeonID );
if( nGuildID == nOwnerGuildID )
{
nType = 2;
nTipGuildID = DungeonManager::Instance().GetRaidGuildID( nDungeonID );
nTipRecord = DungeonManager::Instance().GetDungeonRaidRecord( nDungeonID, nTipGuildID );
}
// 공격자 길드면 주인 길드 정보 확인
else
{
nType = 3;
nTipGuildID = nOwnerGuildID;
}
}
// 팁에 해당하는 길드에 대한 추가 정보 획득 및 방송 내용 생성
if( nTipGuildID )
{
int nTipAllianceID = GuildManager::GetInstance().GetAllianceID( nTipGuildID );
// 팁 길드가 연합 소속인 경우
if( nTipAllianceID )
{
s_sprintf( buf, _countof( buf ), "GRAIDSIEGETIP|%d|%d|%s(%s)|%s|%d|%d|",
nType, nTipRecord, GuildManager::GetInstance().GetAllianceName( nTipAllianceID ).c_str(),
GuildManager::GetInstance().GetGuildName( nTipGuildID ).c_str(),
StructPlayer::GetPlayerName( GuildManager::GetInstance().GetLeaderSID( nTipGuildID ) ),
GuildManager::GetInstance().GetAllianceMemberPlayerCount( nTipAllianceID ), nDungeonID );
}
else
{
s_sprintf( buf, _countof( buf ), "GRAIDSIEGETIP|%d|%d|%s|%s|%d|%d|",
nType, nTipRecord, GuildManager::GetInstance().GetGuildName( nTipGuildID ).c_str(),
StructPlayer::GetPlayerName( GuildManager::GetInstance().GetLeaderSID( nTipGuildID ) ),
GuildManager::GetInstance().GetMemberCount( nTipGuildID ), nDungeonID );
}
}
else
{
s_sprintf( buf, _countof( buf ), "GRAIDSIEGETIP|%d|%d| | |0|%d|", nType, nTipRecord, nDungeonID );
}
}
SendChatMessage( FALSE, CHAT_GUILD_SYSTEM, "@GUILD", pPlayer, buf );
}
void SendSummonInfo( struct StructPlayer *pPlayer )
{
int nPartyID = pPlayer->GetPartyID();
if( !nPartyID )
return;
std::string strSummonInfo;
if( PartyManager::GetInstance().IsLinkedParty( nPartyID ) )
{
strSummonInfo = PartyManager::GetInstance().GetLinkedPartySummonInfo( nPartyID );
}
else
{
strSummonInfo = PartyManager::GetInstance().GetSummonInfo( nPartyID );
}
SendChatMessage( false, CHAT_PARTY_SYSTEM, "@PARTY", pPlayer, strSummonInfo.c_str() );
}
void BroadcastSummonInfo( int nPartyID, struct StructPlayer *pPlayer, bool bForce )
{
if( !nPartyID )
return;
int nMainSummonHandle = pPlayer->GetMainSummon() ? pPlayer->GetMainSummon()->GetHandle() : 0;
int nSubSummonHandle = pPlayer->GetSubSummon() ? pPlayer->GetSubSummon()->GetHandle() : 0;
if( !bForce && !nMainSummonHandle && !nSubSummonHandle )
return;
PrintfLinkedPartyChatMessage( false, CHAT_PARTY_SYSTEM, "@PARTY", nPartyID, "MSINFO|%d|%d|%d|%d|", nPartyID, pPlayer->GetHandle(), nMainSummonHandle, nSubSummonHandle );
}
void SendBeltSlotInfo( struct StructPlayer *pPlayer )
{
TS_SC_BELT_SLOT_INFO msg;
for( int i = 0; i < 8; ++i )
{
StructItem * pItem = pPlayer->GetBeltSlotCardAt( i );
msg.handle[i] = pItem ? pItem->GetHandle() : 0;
}
PendMessage( pPlayer, &msg );
}
void SendOpenStorageMessage( struct StructPlayer *pPlayer )
{
TS_SC_OPEN_STORAGE msg;
msg.maxStorageItemCount = GameRule::nMaxStorageItemCount;
PendMessage( pPlayer, &msg );
}
void SendJobInfo( struct StructPlayer *pPlayer, struct StructCreature *pCreature )
{
SendPropertyMessage( pPlayer, pCreature->GetHandle(), "job", pCreature->GetJobId() );
SendPropertyMessage( pPlayer, pCreature->GetHandle(), "job_level", pCreature->GetJobLevel() );
for( int i = 0; i < GameRule::MAX_JOB_DEPTH - 1; ++i )
{
char buf[56];
s_sprintf( buf, _countof( buf ), "job_%d", i );
SendPropertyMessage( pPlayer, pCreature->GetHandle(), buf, pCreature->GetPrevJobId( i ) );
s_sprintf( buf, _countof( buf ), "jlv_%d", i );
SendPropertyMessage( pPlayer, pCreature->GetHandle(), buf, pCreature->GetPrevJobLevel( i ) );
}
}
void SendTargetMsg( const struct StructPlayer *pPlayer, AR_HANDLE target )
{
TS_SC_TARGET msg;
msg.target = target;
PendMessage( pPlayer, &msg );
}
void SendLeaveMsg( const ArObject * pClient, const ArObject * pObj )
{
GameObject *pPtr = GameObject::raw_get( pObj->GetHandle() );
if( !pPtr )
{
return;
}
//if( pClient != pObj && pPtr->IsPlayer() && static_cast< StructPlayer * >(pPtr)->IsInvisible() )
//{
// return;
//}
TS_LEAVE msg;
msg.handle = pObj->GetHandle();
/* GameObject *pPtr = GameObject::raw_get( pObj->GetHandle() );
if( pPtr )
{
if( pPtr->IsSummon() )
{
const StructSummon * pSummon = static_cast< const StructSummon * >( pObj );
if( pSummon->GetSummonFlag() && !pSummon->IsWarping() && pSummon->GetMaster() == static_cast< const StructPlayer * >( pClient ) )
return;
}
}
*/
PendMessage( pClient, &msg );
}
void SendEnterMsg( const ArObject * pClient, const ArObject * pObj )
{
char buf[1024];
assert( sizeof(TS_ENTER) < sizeof(buf) );
TS_ENTER *pMsg = new (buf) TS_ENTER();
ArPosition pos = pObj->GetCurrentPosition( GetArTime() );
pMsg->x = pos.x;
pMsg->y = pos.y;
pMsg->z = pos.z;
//pMsg->x = pObj->GetX();
//pMsg->y = pObj->GetY();
//pMsg->z = pObj->GetZ();
pMsg->layer = pObj->GetLayer();
pMsg->handle = pObj->GetHandle();
GameObject *pPtr = GameObject::raw_get( pObj->GetHandle() );
if( !pPtr )
{
//assert( 0 );
return;
}
StructCreature *pCreature = NULL;
bool bIsNeedStateNotify = false;
switch( pObj->GetObjectType() )
{
case ArObject::STATIC_OBJECT:
{
pMsg->type = TS_ENTER::STATIC_OBJECT;
if( pPtr->IsItem() )
{
StructItem *pItem = static_cast< StructItem * >( pPtr );
pMsg->ObjType = TS_ENTER::GAME_ITEM;
TS_ENTER::ItemInfo *pItemInfo = (TS_ENTER::ItemInfo*)(pMsg+1);
pMsg->size += sizeof(TS_ENTER::ItemInfo);
pItemInfo->code = pItem->GetItemCode();
pItemInfo->cnt = pItem->GetCount();
pItemInfo->pick_up_order.drop_time = pItem->GetDropTime();
const StructItem::ITEM_PICKUP_ORDER & ItemPickupOrder = pItem->GetPickupOrder();
for( int i = 0; i < 3; i++ )
{
pItemInfo->pick_up_order.hPlayer[i] = ItemPickupOrder.hPlayer[i];
pItemInfo->pick_up_order.nPartyID[i] = ItemPickupOrder.nPartyID[i];
}
}
else if( pPtr->IsSkillProp() )
{
StructSkillProp *pSkillProp = static_cast< StructSkillProp * >( pPtr );
pMsg->ObjType = TS_ENTER::GAME_SKILL_PROP;
TS_ENTER::SkillInfo *pSkillInfo = (TS_ENTER::SkillInfo*)(pMsg+1);
pMsg->size += sizeof(TS_ENTER::SkillInfo);
pSkillInfo->caster = pSkillProp->GetCasterHandle();
pSkillInfo->skill_num = pSkillProp->GetSkillId();
pSkillInfo->start_time = pSkillProp->GetStartTime();
}
else if( pPtr->IsFieldProp() )
{
StructFieldProp *pFieldProp = static_cast< StructFieldProp * >( pPtr );
pMsg->ObjType = TS_ENTER::GAME_FIELD_PROP;
TS_ENTER::FieldPropInfo *pPropInfo = (TS_ENTER::FieldPropInfo*)(pMsg+1);
pMsg->size += sizeof(TS_ENTER::FieldPropInfo);
pPropInfo->prop_id = pFieldProp->GetPropId();
pPropInfo->fZOffset = pFieldProp->GetZOffset();
pPropInfo->fRotateX = pFieldProp->GetRotateX();
pPropInfo->fRotateY = pFieldProp->GetRotateY();
pPropInfo->fRotateZ = pFieldProp->GetRotateZ();
pPropInfo->fScaleX = pFieldProp->GetScaleX();
pPropInfo->fScaleY = pFieldProp->GetScaleY();
pPropInfo->fScaleZ = pFieldProp->GetScaleZ();
pPropInfo->bLockHeight = pFieldProp->IsHeightLocked();
pPropInfo->fLockHeight = pFieldProp->GetLockHeight();
}
break;
}
case ArObject::MOVABLE_OBJECT:
{
pMsg->type = TS_ENTER::NPC;
if( pPtr->IsNPC() )
{
pMsg->ObjType = TS_ENTER::GAME_NPC;
TS_ENTER::NPCInfo *pNPCInfo = (TS_ENTER::NPCInfo*)(pMsg+1);
pMsg->size += sizeof(TS_ENTER::NPCInfo);
pNPCInfo->face_direction = static_cast<StructNPC*>( pPtr )->GetFace();
pNPCInfo->npc_id = static_cast<StructNPC*>( pPtr )->GetNPCID();
pNPCInfo->hp = 100;
pNPCInfo->max_hp = 100;
pNPCInfo->mp = 0;
pNPCInfo->max_mp = 0;
pNPCInfo->level = static_cast<StructNPC*>( pPtr )->GetLevel();
pNPCInfo->skin_color = static_cast<StructNPC*>( pPtr )->GetSkinColor();
pNPCInfo->is_first_enter = false;
pNPCInfo->energy = static_cast<StructNPC*>( pPtr )->GetEnergyCount();
pNPCInfo->status = GetStatusCode( static_cast<StructPlayer*>( pPtr ), static_cast< const StructPlayer* >( pClient ) );
// _cprint( "NPC ENTER : %s <- %s\n", static_cast< const StructPlayer*>( pClient )->GetName(), static_cast<const StructNPC*>( pPtr )->GetName() );
}
if( pPtr->IsMonster() )
{
pMsg->ObjType = TS_ENTER::GAME_MOB;
TS_ENTER::MonsterInfo *pMonsterInfo = (TS_ENTER::MonsterInfo*)(pMsg+1);
pMsg->size += sizeof(TS_ENTER::MonsterInfo);
pMonsterInfo->face_direction = static_cast<StructMonster*>( pPtr )->GetFace();
pMonsterInfo->monster_id = bits_scramble< int, 3 >( static_cast<StructMonster*>( pPtr )->GetMonsterId() );
pMonsterInfo->race = static_cast<StructCreature*>( pPtr )->GetCreatureGroup();
pMonsterInfo->hp = static_cast<StructCreature*>( pPtr )->GetHP();
pMonsterInfo->max_hp = static_cast<StructCreature*>( pPtr )->GetMaxHP();
pMonsterInfo->mp = static_cast<StructCreature*>( pPtr )->GetMP();
pMonsterInfo->max_mp = static_cast<StructCreature*>( pPtr )->GetMaxMP();
pMonsterInfo->level = static_cast<StructCreature*>( pPtr )->GetLevel();
pMonsterInfo->skin_color = static_cast<StructCreature*>( pPtr )->GetSkinColor();
pMonsterInfo->is_first_enter = static_cast<StructCreature*>( pPtr )->IsFirstEnter();
pMonsterInfo->energy = static_cast<StructCreature*>( pPtr )->GetEnergyCount();
pMonsterInfo->is_tamed = !!static_cast<StructMonster*>( pPtr )->GetTamer();
pMonsterInfo->status = GetStatusCode( static_cast<StructPlayer*>( pPtr ), static_cast< const StructPlayer* >( pClient ) );
if( static_cast<StructCreature*>( pPtr )->IsNeedStateNotify() )
{
pCreature = static_cast<StructCreature*>( pPtr );
bIsNeedStateNotify = true;
}
}
else if( pPtr->IsSummon() )
{
pMsg->ObjType = TS_ENTER::GAME_SUMMON;
TS_ENTER::SummonInfo *pSummonInfo = (TS_ENTER::SummonInfo*)(pMsg+1);
pMsg->size += sizeof(TS_ENTER::SummonInfo);
StructSummon *pSummon = static_cast< StructSummon * >( pPtr );
if( !pSummon ) break;
pSummonInfo->face_direction = pSummon->GetFace();
pSummonInfo->summon_code = pSummon->GetSummonCode();
pSummonInfo->master_handle = pSummon->GetMaster()->GetHandle();
s_strcpy( pSummonInfo->szName, _countof( pSummonInfo->szName ), pSummon->GetName() );
pSummonInfo->race = pSummon->GetCreatureGroup();
pSummonInfo->hp = pSummon->GetHP();
pSummonInfo->max_hp = pSummon->GetMaxHP();
pSummonInfo->mp = pSummon->GetMP();
pSummonInfo->max_mp = pSummon->GetMaxMP();
pSummonInfo->level = pSummon->GetLevel();
pSummonInfo->skin_color = pSummon->GetSkinColor();
pSummonInfo->is_first_enter = pSummon->IsFirstEnter();
pSummonInfo->energy = pSummon->GetEnergyCount();
pSummonInfo->enhance = 0;
if( pSummon->GetParentCard() )
pSummonInfo->enhance = pSummon->GetParentCard()->GetItemEnhance();
if( pSummon->IsNeedStateNotify() )
{
pCreature = static_cast<StructCreature*>( pSummon );
bIsNeedStateNotify = true;
}
}
else if( pPtr->IsPet() )
{
pMsg->ObjType = TS_ENTER::GAME_PET;
TS_ENTER::PetInfo *pPetInfo = (TS_ENTER::PetInfo*)(pMsg+1);
pMsg->size += sizeof(TS_ENTER::PetInfo);
StructPet *pPet = static_cast< StructPet * >( pPtr );
pPetInfo->face_direction = pPet->GetFace();
pPetInfo->pet_code = pPet->GetPetCode();
pPetInfo->master_handle = pPet->GetMaster()->GetHandle();
s_strcpy( pPetInfo->szName, _countof( pPetInfo->szName ), pPet->GetName() );
pPetInfo->race = pPet->GetCreatureGroup();
pPetInfo->hp = 100; // 사용 안 함
pPetInfo->max_hp = 100; // 사용 안 함
pPetInfo->mp = 100; // 사용 안 함
pPetInfo->max_mp = 100; // 사용 안 함
pPetInfo->level = 1; // 사용 안 함
pPetInfo->skin_color = 0; // 사용 안 함
pPetInfo->is_first_enter = pPet->IsFirstEnter();
pPetInfo->energy = 0; // 사용 안 함
if( pPet->IsNeedStateNotify() )
{
pCreature = static_cast<StructCreature*>( pPet );
bIsNeedStateNotify = true;
}
}
else if( pPtr->IsRoamer() )
{
// StructRoamer 객체에 대한 엔터 메시지는 유저한테 보내지 않음 -_ -;
return;
}
else
{
pMsg->ObjType = TS_ENTER::GAME_NPC;
}
break;
}
case ArObject::CLIENT_OBJECT:
{
pMsg->type = TS_ENTER::PLAYER;
pMsg->ObjType = TS_ENTER::GAME_PLAYER;
TS_ENTER::PlayerInfo * pPlayerInfo = (TS_ENTER::PlayerInfo*)(pMsg+1);
pMsg->size += sizeof(TS_ENTER::PlayerInfo);
StructPlayer * pPlayer = static_cast< StructPlayer * >( pPtr );
pPlayerInfo->hp = pPlayer->GetHP();
pPlayerInfo->max_hp = pPlayer->GetMaxHP();
pPlayerInfo->mp = pPlayer->GetMP();
pPlayerInfo->max_mp = pPlayer->GetMaxMP();
pPlayerInfo->level = pPlayer->GetLevel();
pPlayerInfo->race = pPlayer->GetRace();
pPlayerInfo->skin_color = pPlayer->GetSkinColor();
// 캐릭터의 이름 세팅(가명 기능에 따른 추가 처리 포함)
{
// 가명 사용 여부 세팅과는 별개로 아레나 경기장 내부에서만 엔터 메시지를 가명으로 보내야 함
// * 보통 Enter 메시지 방송 후에 pPlayer->ChangeLocation 함수가 호출되기 때문에
// m_pWorldLocation 이나 m_nWorldLocationId 에 의존해서 아레나 경기장 내부인지 판별하면 안 됨
// 이 지점에서 유효한 정보는 좌표밖에 없음...;;;
bool bUseAlias = false;
// 가장 부담되는 GetLocationId, GetLocationType의 호출을 가능한 피하기 위해
// 가명 기능이 활성화돼있고 경기에 참여 중인 유저만 위치 체크 진행
if( pPlayer->IsUsingAlias() && pPlayer->GetBattleArenaInstanceNo() )
{
int nLocationID = GameContent::GetLocationId( pos.x, pos.y );
if( nLocationID && WorldLocationManager::Instance().GetLocationType( nLocationID ) == StructWorldLocation::LOCATION_BATTLE_ARENA )
{
s_strcpy( pPlayerInfo->szName, _countof( pPlayerInfo->szName ), pPlayer->GetAlias() );
bUseAlias = true;
}
}
if( !bUseAlias )
{
s_strcpy( pPlayerInfo->szName, _countof( pPlayerInfo->szName ), pPlayer->GetName() );
}
}
pPlayerInfo->sex = pPlayer->GetSex();
// 오토들 덕분에 생긴 찌꺼기 정보가 ObjType 0에 성별, 종족이 0으로 날아간다.
// 이런 경우는 그냥 무시해주자. 실제로 플레이어라면 둘 중 어떤 것도 0은 될 수 없다.
if( !pPlayerInfo->race || !pPlayerInfo->sex )
{
FILELOG("TS_ENTER: Invalid object info - Postion: (%d, %d, %d) / Handle: %d", pMsg->x, pMsg->y, pMsg->layer, pMsg->handle );
return;
}
// 투명인 케릭터는 엔터 메시지를 안보내기
//if( pObj != pClient && static_cast< StructCreature * >(pPtr)->IsInvisible() )
//{
// return;
//}
pPlayerInfo->face_direction = pPlayer->GetFace();
pPlayerInfo->is_first_enter = pPlayer->IsFirstEnter();
pPlayerInfo->energy = pPlayer->GetEnergyCount();
pPlayerInfo->faceId = pPlayer->GetBaseModelId( 1 );
pPlayerInfo->faceTextureId = pPlayer->GetFaceTextureId();
pPlayerInfo->hairId = pPlayer->GetBaseModelId( 0 );
pPlayerInfo->hairColorIndex = pPlayer->GetHairColorIndex();
pPlayerInfo->hairColorRGB = pPlayer->GetHairColorRGB();
pPlayerInfo->hideEquipFlag = pPlayer->GetHideEquipFlag();
pPlayerInfo->job_id = pPlayer->GetJobId();
pPlayerInfo->status = GetStatusCode( pPlayer, static_cast< const StructPlayer* >( pClient ) );
pPlayerInfo->ride_handle = pPlayer->GetRideHandle();
if( pPlayerInfo->ride_handle )
{
SendEnterMsg( pClient, pPlayer->GetRideObject() );
}
if( pPlayer->IsNeedStateNotify() )
{
pCreature = pPlayer;
bIsNeedStateNotify = true;
}
pPlayerInfo->guild_id = pPlayer->GetGuildID();
pPlayerInfo->title_code = pPlayer->GetMainTitle() ? pPlayer->GetMainTitle()->nID : 0;
StructItem * pItem = pPlayer->GetWearedItem( ItemBase::WEAR_BACKBOARD );
pPlayerInfo->back_board = pItem ? pItem->GetItemBase().nCode : 0;
break;
}
}
// 엔터 메세지 전송
PendMessage( pClient, pMsg );
// 이동중인 녀석이라면 이동정보 보내주자
if( pObj->GetObjectType() != ArObject::STATIC_OBJECT && pObj->IsMoving() )
{
SendMoveMsg( pClient, static_cast< const ArObject* >( pObj ) );
}
// 플레이어라면 아이템 정보 보내주자
if( pMsg->type == TS_ENTER::PLAYER )
{
TS_WEAR_INFO wearInfo;
GetWearMsg( static_cast<StructPlayer*>( pPtr ), wearInfo );
PendMessage( pClient, &wearInfo );
}
// 상태이상
if( bIsNeedStateNotify && pCreature )
{
const std::vector< StructState > & vStateList = pCreature->GetStateList();
for( std::vector< StructState >::const_iterator it = vStateList.begin(); it != vStateList.end(); ++it )
{
//if( (*it).IsNeedDisplay() )
//{
SendStateMessage( static_cast< const StructPlayer* >( pClient ), pCreature->GetHandle(), &(*it) );
//}
}
}
}
void SendMoveMsg( const ArObject * pClient, const ArObject * pObj )
{
size_t nSize = sizeof( TS_MOVE ) + sizeof( TS_MOVE::MOVE_INFO ) * pObj->GetTargetPosList().size();
if ( nSize > SENDMSG_BUFFER_SIZE )
{
FILELOG( "SendMoveMsg too big : %d", nSize );
_cprint( "SendMoveMsg too big : %d", nSize );
return;
}
char buf[SENDMSG_BUFFER_SIZE];
// char * buf = new char[ sizeof( TS_MOVE ) + sizeof( TS_MOVE::MOVE_INFO ) * pObj->GetTargetPosList().size() ];
TS_MOVE * pMsg = new (buf) TS_MOVE();
pMsg->handle = pObj->GetHandle();
pMsg->start_time = pObj->lastStepTime;
pMsg->speed = pObj->GetSpeed();
pMsg->count = static_cast<unsigned short>( pObj->GetTargetPosList().size() );
pMsg->size += sizeof( TS_MOVE::MOVE_INFO ) * pMsg->count;
pMsg->tlayer = pObj->GetLayer();
TS_MOVE::MOVE_INFO * pMoveInfo = reinterpret_cast< TS_MOVE::MOVE_INFO * >( pMsg + 1 );
std::vector< ArMoveVector::MOVE_INFO >::const_iterator it;
for( it = pObj->GetTargetPosList().begin(); it != pObj->GetTargetPosList().end(); ++it, ++pMoveInfo )
{
pMoveInfo->tx = (*it).end.x;
pMoveInfo->ty = (*it).end.y;
}
PendStream( pClient, buf, pMsg->size );
// delete [] buf;
}
void SendForceMoveMsg( const ArObject * pClient, const ArObject * pObj )
{
TS_FORCE_MOVE msg;
msg.handle = pObj->GetHandle();
msg.x = pObj->GetX();
msg.y = pObj->GetY();
msg.z = pObj->GetZ();
msg.layer = pObj->GetLayer();
msg.face = pObj->GetFace();
PendMessage( pClient, &msg );
}
void SendQuestList( struct StructPlayer *pPlayer )
{
// 패킷 버퍼(TS_SC_QUEST_LIST 패킷의 예측 가능한 최대 크기 만큼 할당, TS_QUEST_INFO가 TS_PENDING_QUEST_INFO 보다 크다고 가정함)
char szBuf[ SENDMSG_BUFFER_SIZE ];
memset( szBuf, 0, sizeof( szBuf ) );
TS_SC_QUEST_LIST * pMsg = new (szBuf) TS_SC_QUEST_LIST;
// 시작되었던 퀘스트 정보 버퍼에 복사
struct myActiveQuestFunctor : public StructQuestManager::QuestFunctor
{
myActiveQuestFunctor( TS_SC_QUEST_LIST::TS_QUEST_INFO * pQuestInfoBuffer ) : m_pQuestInfoBuffer( pQuestInfoBuffer ) {}
virtual bool operator()( StructQuest * pQuest )
{
m_pQuestInfoBuffer->code = pQuest->GetQuestCode();
m_pQuestInfoBuffer->nStartID = pQuest->GetQuestInstance().nStartID;
m_pQuestInfoBuffer->nProgress = ( pQuest->IsFinishable() ) ? QuestInstance::FINISHABLE : pQuest->GetProgress();
m_pQuestInfoBuffer->nTimeLimit = ( pQuest->GetTimeLimitType() != QuestBase::TIME_LIMIT_TYPE_PERMANENT ) ? pQuest->GetTimeLimit() : 0;
s_memcpy( m_pQuestInfoBuffer->nStatus, sizeof( m_pQuestInfoBuffer->nStatus ), pQuest->GetQuestInstance().nStatus, sizeof( m_pQuestInfoBuffer->nStatus ) );
if( pQuest->IsRandomQuest() )
{
m_pQuestInfoBuffer->nValue[0] = pQuest->GetRandomKey( 0 );
m_pQuestInfoBuffer->nValue[2] = pQuest->GetRandomKey( 1 );
m_pQuestInfoBuffer->nValue[4] = pQuest->GetRandomKey( 2 );
m_pQuestInfoBuffer->nValue[1] = pQuest->GetRandomValue( 0 );
m_pQuestInfoBuffer->nValue[3] = pQuest->GetRandomValue( 1 );
m_pQuestInfoBuffer->nValue[5] = pQuest->GetRandomValue( 2 );
}
++m_pQuestInfoBuffer;
return true;
}
TS_SC_QUEST_LIST::TS_QUEST_INFO * m_pQuestInfoBuffer;
} _aqfo( reinterpret_cast< TS_SC_QUEST_LIST::TS_QUEST_INFO * >( pMsg + 1 ) );
pMsg->count_active = static_cast< unsigned short >( pPlayer->DoEachActiveQuest( _aqfo ) );
// 퀘스트 정보만큼 크기를 늘린다
pMsg->size += static_cast< unsigned int >( sizeof( TS_SC_QUEST_LIST::TS_QUEST_INFO ) * pMsg->count_active );
// 시작 대기 중인 퀘스트 정보 버퍼에 복사
struct myPendingQuestFunctor : public StructQuestManager::QuestFunctor
{
myPendingQuestFunctor( TS_SC_QUEST_LIST::TS_PENDING_QUEST_INFO * pQuestInfoBuffer ) : m_pQuestInfoBuffer( pQuestInfoBuffer ) {}
virtual bool operator()( struct StructQuest * pQuest )
{
m_pQuestInfoBuffer->code = pQuest->GetQuestCode();
m_pQuestInfoBuffer->nStartID = pQuest->GetQuestInstance().nStartID;
++m_pQuestInfoBuffer;
return true;
}
TS_SC_QUEST_LIST::TS_PENDING_QUEST_INFO * m_pQuestInfoBuffer;
} _pqfo( reinterpret_cast< TS_SC_QUEST_LIST::TS_PENDING_QUEST_INFO * >( _aqfo.m_pQuestInfoBuffer ) );
pMsg->count_pending = static_cast< unsigned short >( pPlayer->DoEachPendingQuest( _pqfo ) );
// 퀘스트 정보만큼 크기를 늘린다
pMsg->size += static_cast< unsigned int >( sizeof( TS_SC_QUEST_LIST::TS_PENDING_QUEST_INFO ) * pMsg->count_pending );
// 메세지 전송
PendStream( pPlayer, pMsg, pMsg->size );
}
void SendQuestStatus( struct StructPlayer *pPlayer, struct StructQuest* pQuest )
{
if( !pQuest )
return;
TS_SC_QUEST_STATUS msg;
msg.code = pQuest->GetQuestCode();
for( int i = 0; i < QuestInstance::MAX_STATUS; ++i )
{
msg.nStatus[i] = pQuest->GetStatus( i );
}
msg.nProgress = ( pQuest->IsFinishable() ) ? QuestInstance::FINISHABLE : pQuest->GetProgress();
msg.nTimeLimit = ( pQuest->GetTimeLimitType() != QuestBase::TIME_LIMIT_TYPE_PERMANENT ) ? pQuest->GetTimeLimit() : 0;;
PendMessage( pPlayer, &msg );
}
void SendTitleList( struct StructPlayer *pPlayer )
{
char szBuf[ SENDMSG_BUFFER_SIZE ] = "";
char *pBuf = szBuf;
size_t nCount = pPlayer->GetTitleCount();
if( SENDMSG_BUFFER_SIZE <= sizeof( TS_SC_TITLE_LIST ) + sizeof( TS_SC_TITLE_LIST::TS_TITLE_INFO ) * nCount )
pBuf = new char[ sizeof( TS_SC_TITLE_LIST ) + sizeof( TS_SC_TITLE_LIST::TS_TITLE_INFO ) * nCount ];
TS_SC_TITLE_LIST * pMsg = new (pBuf) TS_SC_TITLE_LIST;
nCount = 0;
struct myTitleFunctor : public StructTitleManager::TitleFunctor
{
myTitleFunctor( TS_SC_TITLE_LIST::TS_TITLE_INFO * pTitleInfoBuffer, size_t & nCount )
: m_pTitleInfoBuffer( pTitleInfoBuffer )
, m_nCount( nCount )
{}
virtual bool operator()( StructTitle * pTitle )
{
m_pTitleInfoBuffer->code = pTitle->GetCode();
m_pTitleInfoBuffer->status = pTitle->GetStatus();
++m_pTitleInfoBuffer;
++m_nCount;
return true;
}
TS_SC_TITLE_LIST::TS_TITLE_INFO * m_pTitleInfoBuffer;
size_t & m_nCount;
} _atfo( reinterpret_cast< TS_SC_TITLE_LIST::TS_TITLE_INFO * >( pMsg + 1 ), nCount );
pPlayer->DoEachTitle( _atfo );
pMsg->count = static_cast< unsigned short >( nCount );
pMsg->size += static_cast< unsigned int >( sizeof( TS_SC_TITLE_LIST::TS_TITLE_INFO ) * pMsg->count );
// 메세지 전송
PendStream( pPlayer, pMsg, pMsg->size );
if( szBuf != pBuf )
delete[] pBuf;
}
void SendTitleConditionList( struct StructPlayer *pPlayer )
{
char szBuf[ SENDMSG_BUFFER_SIZE ] = "";
char *pBuf = szBuf;
const size_t nCount = pPlayer->GetTitleConditionCount();
if( SENDMSG_BUFFER_SIZE <= sizeof( TS_SC_TITLE_CONDITION_LIST ) + sizeof( TS_SC_TITLE_CONDITION_LIST::TS_TITLE_CONDITION_INFO ) * nCount )
pBuf = new char[ sizeof( TS_SC_TITLE_CONDITION_LIST ) + sizeof( TS_SC_TITLE_CONDITION_LIST::TS_TITLE_CONDITION_INFO ) * nCount ];
TS_SC_TITLE_CONDITION_LIST * pMsg = new (pBuf) TS_SC_TITLE_CONDITION_LIST;
struct myTitleConditionFunctor : public StructTitleManager::TitleConditionFunctor
{
myTitleConditionFunctor( TS_SC_TITLE_CONDITION_LIST::TS_TITLE_CONDITION_INFO * pTitleConditionInfoBuffer ) : m_pTitleConditionInfoBuffer( pTitleConditionInfoBuffer ) {}
virtual bool operator()( StructTitleCondition * pTitleCondition )
{
m_pTitleConditionInfoBuffer->type = pTitleCondition->GetType();
m_pTitleConditionInfoBuffer->count = pTitleCondition->GetCount();
++m_pTitleConditionInfoBuffer;
return true;
}
TS_SC_TITLE_CONDITION_LIST::TS_TITLE_CONDITION_INFO * m_pTitleConditionInfoBuffer;
} _atcfo( reinterpret_cast< TS_SC_TITLE_CONDITION_LIST::TS_TITLE_CONDITION_INFO * >( pMsg + 1 ) );
pMsg->count = static_cast< unsigned short >( pPlayer->DoEachTitleCondition( _atcfo ) );
pMsg->size += static_cast< unsigned int >( sizeof( TS_SC_TITLE_CONDITION_LIST::TS_TITLE_CONDITION_INFO ) * pMsg->count );
// 메세지 전송
PendStream( pPlayer, pMsg, pMsg->size );
if( szBuf != pBuf )
delete[] pBuf;
}
void SendRemainTitleTime( struct StructPlayer *pPlayer )
{
AR_TIME t = GetArTime();
TS_SC_REMAIN_TITLE_TIME msg;
msg.remain_title_time = pPlayer->GetRemainTitleTime() > t ? pPlayer->GetRemainTitleTime() - t : 0;
PendMessage( pPlayer, &msg );
}
void SendMainTitle( struct StructPlayer *pPlayer )
{
TS_SC_SET_MAIN_TITLE msg;
msg.handle = pPlayer->GetHandle();
msg.code = pPlayer->GetMainTitle() ? pPlayer->GetMainTitle()->nID : 0;
PendMessage( pPlayer, &msg );
}
void SendSubTitle( struct StructPlayer *pPlayer )
{
for( int i = 0; i < GameRule::SUB_TITLE_COUNT; ++i )
{
TS_SC_SET_SUB_TITLE msg;
msg.index = i;
msg.code = pPlayer->GetSubTitle( i ) ? pPlayer->GetSubTitle( i )->nID : 0;
PendMessage( pPlayer, &msg );
}
}
void SendBoothClosedMessage( struct StructPlayer* pPlayer, AR_HANDLE target )
{
TS_SC_BOOTH_CLOSED msg;
msg.target = target;
PendMessage( pPlayer, &msg );
}
void SendBoothInfo( struct StructPlayer* pPlayer, StructPlayer* pTarget )
{
char szBuf[ sizeof( TS_SC_WATCH_BOOTH ) + ( sizeof( TS_SC_WATCH_BOOTH::TS_BOOTH_ITEM_INFO ) * GameRule::MAX_BOOTH_ITEM_COUNT ) ];
TS_SC_WATCH_BOOTH * pMsg = new (szBuf) TS_SC_WATCH_BOOTH;
pMsg->target = pTarget->GetHandle();
pMsg->type = pTarget->GetBoothStatus();
const std::vector< StructPlayer::BOOTH_ITEM_INFO > & rvItemList = pTarget->GetBoothItemList();
pMsg->cnt = static_cast<unsigned short>( rvItemList.size() );
pMsg->size += sizeof( TS_SC_WATCH_BOOTH::TS_BOOTH_ITEM_INFO ) * pMsg->cnt;
TS_SC_WATCH_BOOTH::TS_BOOTH_ITEM_INFO * pBoothItemInfo = reinterpret_cast< TS_SC_WATCH_BOOTH::TS_BOOTH_ITEM_INFO * >( pMsg + 1 );
for( std::vector< StructPlayer::BOOTH_ITEM_INFO >::const_iterator it = rvItemList.begin(); it != rvItemList.end(); ++it )
{
fillItemBaseInfo( pBoothItemInfo, (*it).pItem );
pBoothItemInfo->count = (*it).cnt;
pBoothItemInfo->gold = (*it).gold;
++pBoothItemInfo;
}
PendStream( pPlayer, pMsg, pMsg->size );
}
void SendNPCStatusInVisibleRange( struct StructPlayer* pPlayer )
{
struct _ArObjectFunctor : ArObjectFunctor
{
_ArObjectFunctor( StructPlayer *_pPlayer, ArcadiaIntf *_pIntf ) { pPlayer = _pPlayer; pIntf = _pIntf; }
void operator()( ArObject *pObj ) const
{
GameObject::iterator it = GameObject::get( pObj->GetHandle() );
if( !(*it) || !(*it)->IsNPC() )
{
return;
}
StructNPC * pNPC = static_cast< StructNPC * >( (*it) );
TS_SC_STATUS_CHANGE msg;
msg.handle = pNPC->GetHandle();
msg.status = GetStatusCode( pNPC, pPlayer );
pIntf->SendGameMessage( pPlayer, &msg );
}
ArcadiaIntf * pIntf;
StructPlayer* pPlayer;
} _sender( pPlayer, ArcadiaServer::GetIntf() );
struct Sender : ArRegionFunctor
{
Sender( ArObjectFunctor *_pFo ) { pFo = _pFo; }
void operator()( const struct ArRegion * pRegion )
{
pRegion->DoEachMovableObject( *pFo );
}
ArObjectFunctor * pFo;
} _fo( &_sender );
ArcadiaServer::Instance().DoEachVisibleRegion( pPlayer->GetRX(), pPlayer->GetRY(), pPlayer->GetLayer(), _fo );
}
void SendStatusMessage( struct StructPlayer * pClient, struct StructCreature* pCreature )
{
TS_SC_STATUS_CHANGE msg;
msg.handle = pCreature->GetHandle();
msg.status = GetStatusCode( pCreature, pClient );
PendMessage( pClient, &msg );
}
void BroadcastStatusMessage( struct StructCreature* pCreature )
{
struct _ArObjectFunctor : ArObjectFunctor
{
_ArObjectFunctor( StructCreature *_pCreature, ArcadiaIntf *_pIntf ) { pCreature = _pCreature; pIntf = _pIntf; }
void operator()( ArObject *pObj ) const
{
TS_SC_STATUS_CHANGE msg;
StructPlayer * pClient = static_cast< StructPlayer* >( pObj );
msg.handle = pCreature->GetHandle();
msg.status = GetStatusCode( pCreature, pClient );
pIntf->SendGameMessage( pObj, &msg );
}
ArcadiaIntf * pIntf;
StructCreature* pCreature;
} _sender( pCreature, ArcadiaServer::GetIntf() );
struct Sender : ArRegionFunctor
{
Sender( ArObjectFunctor *_pFo ) { pFo = _pFo; }
void operator()( const struct ArRegion * pRegion )
{
pRegion->DoEachClient( *pFo );
}
ArObjectFunctor * pFo;
} _fo( &_sender );
ArcadiaServer::Instance().DoEachVisibleRegion( pCreature->GetRX(), pCreature->GetRY(), pCreature->GetLayer(), _fo );
}
void BroadcastHairInfo( struct StructPlayer * pPlayer )
{
TS_SC_HAIR_INFO msg;
msg.hPlayer = pPlayer->GetHandle();
msg.nHairID = pPlayer->GetBaseModelId( 0 );
msg.nHairColorIndex = pPlayer->GetHairColorIndex();
msg.nHairColorRGB = pPlayer->GetHairColorRGB();
ArcadiaServer::Instance().Broadcast( pPlayer->GetRX(), pPlayer->GetRY(), pPlayer->GetLayer(), &msg );
}
void BroadcastSkinInfo( struct StructPlayer * pPlayer )
{
TS_SC_SKIN_INFO msg;
msg.hPlayer = pPlayer->GetHandle();
msg.nSkinColor = pPlayer->GetSkinColor();
ArcadiaServer::Instance().Broadcast( pPlayer->GetRX(), pPlayer->GetRY(), pPlayer->GetLayer(), &msg );
}
// 이하 인증 관련 메시지
void SendGameServerLoginToAuth( const char * szServerName, const char * szServerScreenshotUrl, unsigned short nServerIdx, const char * szServerIP, int nServerPort )
{
TS_GA_LOGIN msg;
s_strcpy( msg.server_name, _countof( msg.server_name ), szServerName );
s_strcpy( msg.server_screenshot_url, _countof( msg.server_screenshot_url ), szServerScreenshotUrl );
s_strcpy( msg.server_ip, _countof( msg.server_ip ), szServerIP );
msg.server_port = nServerPort;
msg.is_adult_server = GameRule::bIsAdultServer;
msg.server_idx = nServerIdx;
if( g_pAuthConnection && g_pAuthConnection->IsConnected() )
{
PendMessage( g_pAuthConnection, &msg );
}
}
// 이하 업로드 관련 메시지
void SendGameServerLoginToUpload( const char *szServerName )
{
TS_SU_LOGIN msg;
s_strcpy( msg.server_name, _countof( msg.server_name ), szServerName );
if( g_pUploadConnection && g_pUploadConnection->IsConnected() )
{
PendMessage( g_pUploadConnection, &msg );
}
}
void fillAuctionMessageBuffer( struct TS_AUCTION_INFO *pBuffer, const class AuctionInfo *pAuctionInfo )
{
pBuffer->auction_uid = pAuctionInfo->nAuctionID;
pBuffer->bidded_price = pAuctionInfo->nHighestBiddingPrice.GetRawData();
time_t tLeftDuration = pAuctionInfo->tAuctionEnd - time( NULL );
if( tLeftDuration > GameRule::AUCTION_DURATION_MIDTERM )
pBuffer->duration_type = TS_AUCTION_INFO::DURATION_LONGTERM;
else if( tLeftDuration > GameRule::AUCTION_DURATION_SHORTTERM )
pBuffer->duration_type = TS_AUCTION_INFO::DURATION_MIDTERM;
else
pBuffer->duration_type = TS_AUCTION_INFO::DURATION_SHORTTERM;
pBuffer->instant_purchase_price = pAuctionInfo->nInstantPurchasePrice.GetRawData();
fillItemBaseInfo( &pBuffer->item_info, pAuctionInfo->pItem );
}
bool SendLoginToAuth( const char * szAccount, __int64 one_time_key )
{
if( GameRule::bUseLoginLogoutDebug )
{
_ctprint( "SendLoginToAuth (%s)\n", szAccount );
FileLogHandler::GetFileLogHandler()->LogStringEx( NULL, "LoginLogout_Debug", "SendLoginToAuth (%s)", szAccount );
}
TS_GA_CLIENT_LOGIN msg;
s_strcpy( msg.account, _countof( msg.account ), szAccount );
msg.one_time_key = one_time_key;
if( g_pAuthConnection && g_pAuthConnection->IsConnected() )
{
PendMessage( g_pAuthConnection, &msg );
return true;
}
_cprint( "authserver is not connected: %s\n", szAccount );
FILELOG( "authserver is not connected: %s", szAccount );
return false;
}
void SendLogoutToAuth( const char * szAccount, AR_TIME nContinuousPlayTime, const int nCallerIdx )
{
if( GameRule::bUseLoginLogoutDebug )
{
_ctprint( "SendLogoutToAuth (%s, %d)\n", szAccount, nCallerIdx );
FileLogHandler::GetFileLogHandler()->LogStringEx( NULL, "LoginLogout_Debug", "SendLogoutToAuth (%s, %d)", szAccount, nCallerIdx );
}
TS_GA_CLIENT_LOGOUT msg;
s_strcpy( msg.account, _countof( msg.account ), szAccount );
msg.nContinuousPlayTime = nContinuousPlayTime;
if( g_pAuthConnection && g_pAuthConnection->IsConnected() )
{
PendMessage( g_pAuthConnection, &msg );
}
}
void SendGameTime( const ArObject * pClient )
{
TS_SC_GAME_TIME msg;
msg.t = GetArTime();
msg.game_time = time( NULL );
PendMessage( pClient, &msg );
}
void SendHPMPMsg( const struct StructPlayer * pPlayer, const struct StructCreature * pCreature, int add_hp, short add_mp, bool need_to_display )
{
TS_SC_HPMP msg;
msg.handle = pCreature->GetHandle();
msg.add_hp = add_hp;
msg.add_mp = add_mp;
msg.hp = pCreature->GetHP();
msg.mp = pCreature->GetMP();
msg.max_hp = pCreature->GetMaxHP();
msg.max_mp = pCreature->GetMaxMP();
msg.need_to_display = need_to_display;
PendMessage( pPlayer, &msg );
}
void BroadcastHPMPMsg( const struct StructCreature *pCreature, int add_hp, short add_mp, bool need_to_display )
{
TS_SC_HPMP msg;
msg.handle = pCreature->GetHandle();
msg.add_hp = add_hp;
msg.add_mp = add_mp;
msg.hp = pCreature->GetHP();
msg.mp = pCreature->GetMP();
msg.max_hp = pCreature->GetMaxHP();
msg.max_mp = pCreature->GetMaxMP();
msg.need_to_display = need_to_display;
ArcadiaServer::Instance().Broadcast( pCreature->GetRX(), pCreature->GetRY(), pCreature->GetLayer(), &msg );
}
void SendOpenUrl( struct StructPlayer * pPlayer, const char *szUrl, const bool bWaitForEventScene, const int width, const int height )
{
TS_SC_OPEN_URL msg;
msg.wait_for_event_scene = bWaitForEventScene;
msg.width = width;
msg.height = height;
msg.url_len = static_cast<unsigned short>( strlen( szUrl ) );
msg.size += msg.url_len;
if( msg.size > 1024 )
return;
char szTmp[ 1024 ];
char *p = szTmp + sizeof(msg);
int temp_size = sizeof(szTmp) - sizeof(msg);
s_memcpy( p, temp_size, szUrl, msg.url_len );
p += msg.url_len;
temp_size -= msg.url_len;
s_memcpy( szTmp, sizeof( szTmp ), &msg, sizeof(msg) );
PendStream( pPlayer, szTmp, msg.size );
}
void SendUrlList( struct StructPlayer * pPlayer )
{
TS_SC_URL_LIST msg;
if( !ENV().IsExist( "game.url_list" ) )
return;
std::string szUrlList = ENV().GetString( "game.url_list" ) + "|guild_icon_upload.url|" + ENV().GetString( "game.guild_icon_base_url" ) + "/iconupload.aspx";
msg.url_list_len = static_cast<unsigned short>( szUrlList.length() );
msg.size += msg.url_list_len;
if( msg.size > 1024 )
return;
char szTmp[ 1024 ];
char *p = szTmp;
int temp_size = sizeof( szTmp );
s_memcpy( p, temp_size, &msg, sizeof(msg) ); p += sizeof(msg); temp_size -= sizeof(msg);
s_memcpy( p, temp_size, szUrlList.c_str(), msg.url_list_len ); p += msg.url_list_len; temp_size -= msg.url_list_len;
PendStream( pPlayer, szTmp, msg.size );
}
void SendShowCreatureNameChangeBox( struct StructPlayer *pPlayer, AR_HANDLE hSummon )
{
if( !pPlayer )
return;
ARCADIA_LOCK( ArcadiaServer::Instance().LockObjectWithVisibleRange( pPlayer ) );
pPlayer->SetLastContact( "creature_name_change", 1 );
TS_SC_SHOW_SUMMON_NAME_CHANGE msg;
msg.handle = hSummon;
PendMessage( pPlayer, &msg );
}
void SendHuntaholicInstanceInfo( const struct StructPlayer * pPlayer, const TS_HUNTAHOLIC_INSTANCE_INFO & info )
{
if( !pPlayer )
return;
TS_SC_HUNTAHOLIC_INSTANCE_INFO msg;
s_memcpy( &msg.info, sizeof( msg.info ), &info, sizeof( msg.info ) );
PendMessage( pPlayer, &msg );
}
void SendHuntaholicHuntingScore( const struct StructPlayer * pPlayer, const int nHuntaholicID, const int nPersonalKillCount, const int nPersonalScore, const int nKillCount, const int nScore, const double & fPointAdvantage, const int nGainPoint, const char nResultType )
{
if( !pPlayer )
return;
TS_SC_HUNTAHOLIC_HUNTING_SCORE msg;
msg.huntaholic_id = nHuntaholicID;
msg.personal_kill_count = nPersonalKillCount;
msg.personal_score = nPersonalScore;
msg.kill_count = nKillCount;
msg.score = nScore;
msg.point_advantage = fPointAdvantage;
msg.point_rate = 1.0;
msg.gain_point = nGainPoint;
msg.result_type = nResultType;
PendMessage( pPlayer, &msg );
}
void SendCompeteRequest( const struct StructPlayer * pPlayer, const unsigned char nCompeteType, const char * szRequester )
{
if( !pPlayer )
return;
TS_SC_COMPETE_REQUEST msg;
msg.compete_type = nCompeteType;
s_strcpy( msg.requester, _countof( msg.requester ), szRequester );
PendMessage( pPlayer, &msg );
}
void SendCompeteAnswer( const struct StructPlayer * pPlayer, const unsigned char nCompeteType, const unsigned char nAnswerType, const char * szRequestee )
{
if( !pPlayer )
return;
TS_SC_COMPETE_ANSWER msg;
msg.compete_type = nCompeteType;
msg.answer_type = nAnswerType;
s_strcpy( msg.requestee, _countof( msg.requestee ), szRequestee );
PendMessage( pPlayer, &msg );
}
void SendCompeteCountdown( const struct StructPlayer * pPlayer, const unsigned char nCompeteType, const char * szCompetitor, const AR_HANDLE hCompetitor )
{
if( !pPlayer )
return;
TS_SC_COMPETE_COUNTDOWN msg;
msg.compete_type = nCompeteType;
s_strcpy( msg.competitor, _countof( msg.competitor ), szCompetitor );
msg.handle_competitor = hCompetitor;
PendMessage( pPlayer, &msg );
}
void SendCompeteStart( const struct StructPlayer * pPlayer, const unsigned char nCompeteType, const char * szCompetitor )
{
if( !pPlayer )
return;
TS_SC_COMPETE_START msg;
msg.compete_type = nCompeteType;
s_strcpy( msg.competitor, _countof( msg.competitor ), szCompetitor );
PendMessage( pPlayer, &msg );
}
void SendCompeteEnd( const struct StructPlayer * pPlayer, const unsigned char nCompeteType, const unsigned char nEndType, const char * szWinner, const char * szLoser )
{
if( !pPlayer )
return;
TS_SC_COMPETE_END msg;
msg.compete_type = nCompeteType;
msg.end_type = nEndType;
s_strcpy( msg.winner, _countof( msg.winner ), szWinner );
s_strcpy( msg.loser, _countof( msg.loser ), szLoser );
PendMessage( pPlayer, &msg );
}