Files
Leviathan/Client/Game/game/Utility/KCommandBuilder.cpp
T
2026-06-01 12:46:52 +02:00

604 lines
18 KiB
C++

#include "stdafx.h"
#include "KCommandBuilder.h"
#include <dump/XException.h>
#include <toolkit/XEnv.h>
#include "SBotManager.h"
#include "SGameMessage.h"
#include <toolkit/XStringUtil.h>
#include "LuaVM.h"
extern SBotManager* g_pSBotMng;
namespace
{
/*
// 필터링
// : 우선처리
// 사용1) 타입에 관계없이 공통으로 사용하는 경우
// 사용2) sub Type 으로 정하기에는 너무 방대한 경우
//
// ex)
// 입력 : > ?opt.intf.MINIMAP_OPEN
// 결과 : opt.intf.MINIMAP_OPEN 0;
*/
enum
{
FILTER_GET = 0,
FILTER_RUN,
};
const int g_nFilterTypeCount = 2;
const char* g_szFilterType[g_nFilterTypeCount] =
{
"?", // get
"!", // run old-style game command : 기존 커맨드 명령어를 지원하기 위해서 ENV 등록되지 않은 경우에라도 ! 를 붙이면 자동 등록.
};
/*
// 예약어
// : 우선처리
// : 히스토리에 남지 않는다.
// 사용1) 출력용인 경우, list/history 등
//
// ex)
// 입력 : > history opt
// 결과 : opt 에 관련된 history 출력된다.
*/
enum
{
RESERVED_HISTORY = 0,
RESERVED_LIST,
RESERVED_HELP,
RESERVED_REFRESH,
RESERVED_SET,
RESERVED_GET,
};
const int g_nReservdCount = 6;
const char* g_szReserv[g_nReservdCount] =
{
"history", // history
"list", // env key list
"help", // help
"refresh", // refresh -> 게임에 apply, update
"set",
"get", // ? <-- 로 처리
};
/*
// 커맨드 키
// : 커맨드 키 카테고리
// : 작업자들은 새로운 카테고리 추가시 꼭 타입 추가 요망!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//
// ex)
// 입력 : > game.game_light_ambient 0 0 0
// 결과 : 입력문을 게임에 적용한 후 결과 출력
*/
enum
{
KEY_GAME = 0,
KEY_OPT,
KEY_DEBUG,
KEY_LUA,
KEY_DB,
};
const char* g_szKeyType[] =
{
"game", // 게임
"opt", // 옵션
"debug", // build 나 oprint 의 경우 env 에 등록된 얘들 등
"lua", // 스크립트 과련
"db", // 디비 관련
};
// 옵션 서브 타입
enum
{
OPT_KEY_INTF = 0,
OPT_KEY_GP,
OPT_KEY_SOUND,
};
const char* g_szOptKeyType[] =
{
"intf", // 인터페이스
"gp", // 그래픽
"sound", // 사운드
};
// 디버거 서브 타입
enum
{
DEBUG_KEY_INTF = 0,
};
const char* g_szDebugType[] =
{
"intf", // 인터페이스 관련
};
const int g_nMaxHistoryNum = 100;
typedef struct tagHISTORY_DATA
{
std::string m_strType;
std::string m_strData;
} HISTORY_DATA;
};
struct KCommandEnvReceiver : public XEnvStruct::XEnvEventReceiver
{
public:
KCommandEnvReceiver() {};
~KCommandEnvReceiver()
{
m_vecGameList.clear();
m_vecOptionList.clear();
m_vecDebugList.clear();
m_vecLuaList.clear();
m_vecDBList.clear();
m_vecENVKeyList.clear();
};
virtual void onEnvInsert( const char* szFullKey )
{
m_vecENVKeyList.push_back( szFullKey );
onEnvUpdate(szFullKey);
};
virtual void onEnvUpdate( const char* szFullKey )
{
// key 에 따라
std::vector<std::string> vecKeyList;
XStringUtil::Split( szFullKey, vecKeyList, "." );
if( !_stricmp( szFullKey, "debug.mask" ) )
{
_set_oprint_mask( ENV().GetString( "debug.mask", "" ).c_str() );
}
if( vecKeyList.size() < 2 )
{
onProcErrorConsole( szFullKey );
return;
}
onProcConsole( szFullKey );
};
// Priority processing of filtering
bool onNotifyFilter( std::vector<std::string> vecStringList )
{
std::string strFilter = vecStringList[0].substr( 0, 1 );
std::string strKey = vecStringList[0].substr( 1, vecStringList[0].size()-1 );
// ? : 출력
if( ::_stricmp(strFilter.c_str(), g_szFilterType[FILTER_GET]) == 0 )
{
std::vector<std::string> vecValueList;
if( ENV().IsExist(strKey.c_str()) )
vecValueList.push_back( ENV().GetString(strKey.c_str()) );
onProcConsole( vecStringList[0].c_str(), vecValueList, true );
}
return true;
};
// 예약어 우선 처리
bool onNotifyReservedType( std::vector<std::string> vecStringList )
{
// help 처리
if( ::_stricmp(vecStringList[0].c_str(), g_szReserv[RESERVED_HELP]/*help*/) == 0 ) return onHelpUpdate();
if( vecStringList.size() < 2 ) return onProcErrorConsole( vecStringList[0].c_str() );
std::string strKey;
XStringUtil::Format( strKey, "%s.%s", vecStringList[0].c_str(), vecStringList[1].c_str() );
std::vector<std::string> vecValueList;
// get 처리 : ? 필터링 처리와 같다
if( ::_stricmp(vecStringList[0].c_str(), g_szReserv[RESERVED_GET]/*get*/) == 0 )
{
std::string strEnvKey = vecStringList[1];
std::vector<std::string> vecValueList;
if( ENV().IsExist(strEnvKey.c_str()) )
vecValueList.push_back( ENV().GetString(strEnvKey.c_str()) );
onProcConsole( strKey.c_str(), vecValueList, true );
return true;
}
// history 처리
if( ::_stricmp(vecStringList[0].c_str(), g_szReserv[RESERVED_HISTORY]/*history*/) == 0 )
getHistoryList( vecStringList[1].c_str(), vecValueList );
// list 처리
else if( ::_stricmp(vecStringList[0].c_str(), g_szReserv[RESERVED_LIST]/*list*/) == 0 )
vecValueList.assign( m_vecENVKeyList.begin(), m_vecENVKeyList.end() );
// 서브가 있으면 서브까지 검사
if( !vecValueList.empty() ) checkSubList( vecStringList[1].c_str(), vecValueList );
bool bOut = true;
// refresh 는 아무 처리 없이 넘기면 된다.
if( ::_stricmp(vecStringList[0].c_str(), g_szReserv[RESERVED_REFRESH]/*refresh*/) == 0 )
bOut = false;
onProcConsole( strKey.c_str(), vecValueList, bOut );
return true;
};
// help
bool onHelpUpdate( const char* szKey = NULL )
{
std::string strValue;
//if( szKey == NULL )
//{
strValue = "■ 예약어와 필터링<br>";
strValue += ": 출력용이거나 우선 처리되어야 하는 경우 정의<br>";
strValue += ": history 에 남지 않는다.<br>";
strValue += "<br>";
strValue += "▷ 예약어<br>";
strValue += ": history - 입력 로그 ex) history game, history opt.intf ..<br>";
strValue += ": list - ENV 에 등록된 커맨드 ex) list game, list opt.gp ..<br>";
strValue += ": help - 도움말 ex) help<br>";
strValue += ": refresh - 화면 갱신 ex) refresh opt.intf.MINIMAP_OPEN ..<br>";
strValue += ": set - ENV 최초 등록할 경우 사용 ex) set game.game_light_ambient 0 0 0<br>";
strValue += ": get - ENV 등록된 커맨드의 값 출력 ex) get game.game_light_ambient<br>";
strValue += "<br>";
strValue += "▷ 필터링<br>";
strValue += ": ? - 커맨드의 결과값 도출 ex) ?otp.intf.MINIAMP_OPEN -> MINIMAP_OPEN 의 결과값을 얻는다.<br>";
strValue += ": ! - 무조건 ENV 등록(기존 커맨드 사용위해 예외처리) ex) !otp.intf.MINIAMP_OPEN<br>";
strValue += "<br>";
strValue += "<br>";
strValue += "■ 커맨드 타입과 서브 타입<br>";
strValue += ": 커맨드는 타입별로 정의하여 사용하여야 한다. !작업자 필독 요망!<br>";
strValue += "<br>";
strValue += "▷ 커맨드 타입<br>";
strValue += " - game : 게임으로 분류된 커맨드<br>";
strValue += " - opt : 옵션으로 분류된 커맨드<br>";
strValue += " - debug : env 에 등록된 build 내용, oprint 내용 등<br>";
strValue += " - lua : 스크립트로 분류된 커맨드<br>";
strValue += " - db : database 의 내용 확인 등, ex) > db.isitemicon 1234 -> 1234 가 아이템 db 에 있는지 검사<br>";
strValue += "<br>";
strValue += "▷ 서브 타입<br>";
strValue += " - 리스트 관리를 위해서 각 커맨드 타입의 서브 타입을 정의해서 사용해야 함.<br>";
strValue += " ex) opt.intf, opt.gp, opt.sound ... opt 의 서브 타입들<br>";
//}
//else
//{
// std::vector<std::string> vecKeyList;
// XStringUtil::Split( szKey, vecKeyList, "." );
//}
if( !strValue.empty() )
{
std::vector<std::string> vecValueList;
vecValueList.push_back( strValue );
onProcConsole( "help", vecValueList, true );
}
return true;
};
// 커맨드 타입
int getCommandType( const char* szKey )
{
std::vector<std::string> vecKeyList;
XStringUtil::Split( szKey, vecKeyList, "." );
if( ::_stricmp(vecKeyList[0].c_str(), g_szKeyType[SMSG_CONSOLE_BUILDER::TYPE_GAME]) == 0 ) return SMSG_CONSOLE_BUILDER::TYPE_GAME;
else if( ::_stricmp(vecKeyList[0].c_str(), g_szKeyType[SMSG_CONSOLE_BUILDER::TYPE_OPTION]) == 0 )return SMSG_CONSOLE_BUILDER::TYPE_OPTION;
else if( ::_stricmp(vecKeyList[0].c_str(), g_szKeyType[SMSG_CONSOLE_BUILDER::TYPE_DEBUG]) == 0 ) return SMSG_CONSOLE_BUILDER::TYPE_DEBUG;
else if( ::_stricmp(vecKeyList[0].c_str(), g_szKeyType[SMSG_CONSOLE_BUILDER::TYPE_LUA]) == 0 ) return SMSG_CONSOLE_BUILDER::TYPE_LUA;
else if( ::_stricmp(vecKeyList[0].c_str(), g_szKeyType[SMSG_CONSOLE_BUILDER::TYPE_DB]) == 0 ) return SMSG_CONSOLE_BUILDER::TYPE_DB;
return SMSG_CONSOLE_BUILDER::TYPE_GAME;
};
// 히스토리 리스트
void getHistoryList( const char* szKey, std::vector<std::string> & vecHistoryList )
{
std::vector<std::string> vecKeyList;
XStringUtil::Split( szKey, vecKeyList, "." );
std::vector<HISTORY_DATA> vecHistoryDataList;
if( ::_stricmp(vecKeyList[0].c_str(), g_szKeyType[SMSG_CONSOLE_BUILDER::TYPE_GAME]) == 0 ) vecHistoryDataList.assign(m_vecGameList.begin(), m_vecGameList.end());
if( ::_stricmp(vecKeyList[0].c_str(), g_szKeyType[SMSG_CONSOLE_BUILDER::TYPE_OPTION]) == 0 ) vecHistoryDataList.assign(m_vecOptionList.begin(), m_vecOptionList.end());
if( ::_stricmp(vecKeyList[0].c_str(), g_szKeyType[SMSG_CONSOLE_BUILDER::TYPE_DEBUG]) == 0 ) vecHistoryDataList.assign(m_vecDebugList.begin(), m_vecDebugList.end());
if( ::_stricmp(vecKeyList[0].c_str(), g_szKeyType[SMSG_CONSOLE_BUILDER::TYPE_LUA]) == 0 ) vecHistoryDataList.assign(m_vecLuaList.begin(), m_vecLuaList.end());
if( ::_stricmp(vecKeyList[0].c_str(), g_szKeyType[SMSG_CONSOLE_BUILDER::TYPE_DB]) == 0 ) vecHistoryDataList.assign(m_vecDBList.begin(), m_vecDBList.end());
if( vecHistoryDataList.empty() ) return;
std::string strData;
std::vector<HISTORY_DATA>::iterator it = vecHistoryDataList.begin();
while( it != vecHistoryDataList.end() )
{
HISTORY_DATA data = (*it);
strData = data.m_strType;
strData += " ";
strData += data.m_strData;
vecHistoryList.push_back( strData );
it++;
}
};
// 리스트에서 서브가 있으면 서브까지 일치할때 저장하고 서브가 없으면 리턴
void checkSubList( const char* szKey, std::vector<std::string> & vecList )
{
std::vector<std::string> vecKeyList;
XStringUtil::Split( szKey, vecKeyList, "." );
// 서브 카테고리 가지고 있는지 검사
bool bIsHasSub = ( vecKeyList.size() > 1 ) ? true : false;
std::vector<std::string> vecValueList;
std::string strData;
std::vector<std::string>::iterator it = vecList.begin();
while( it != vecList.end() )
{
std::vector<std::string> vecDataList;
XStringUtil::Split( (*it).c_str(), vecDataList );
std::string strKey = vecDataList[0];
std::vector<std::string> vecDataKeyList;
XStringUtil::Split( strKey.c_str(), vecDataKeyList, "." );
std::string str = vecDataKeyList[0];
if( vecDataKeyList.size() > 2 )
{
str += ".";
str += vecDataKeyList[1];
}
std::string strDataKey = ( bIsHasSub ) ? str : vecDataKeyList[0];
if( ::_stricmp(strDataKey.c_str(), szKey) == 0 ) vecValueList.push_back( (*it) );
it++;
}
vecList.clear();
if( !vecValueList.empty() ) vecList.assign( vecValueList.begin(), vecValueList.end() );
};
// 에러 리턴
bool onProcErrorConsole( const char* szKey )
{
SMSG_CONSOLE_BUILDER msg;
msg.m_strKey = szKey;
msg.m_bOut = true;
msg.m_nCommandType = getCommandType(szKey);
msg.m_vecValueList.clear();
msg.m_nValueCount = static_cast<int>(msg.m_vecValueList.size());
if( g_pSBotMng ) g_pSBotMng->ProcConsole( &msg );
return true;
};
void onProcConsole( const char* szKey )
{
SMSG_CONSOLE_BUILDER msg;
msg.m_strKey = szKey;
msg.m_bOut = false;
msg.m_nCommandType = getCommandType(szKey);
std::string strKey = szKey;
if( msg.m_nCommandType == SMSG_CONSOLE_BUILDER::TYPE_GAME )
{
std::vector<std::string> vecKeyList;
XStringUtil::Split( szKey, vecKeyList, "." );
strKey = strKey.substr( vecKeyList[0].size()+1, strKey.size()-vecKeyList[0].size() );
msg.m_strKey = strKey;
}
if( ENV().IsExist( szKey ) )
{
std::vector<std::string> vecValueList;
XStringUtil::Split( ENV().GetString(szKey).c_str(), vecValueList, ";" );
msg.m_vecValueList.assign( vecValueList.begin(), vecValueList.end() );
}
msg.m_nValueCount = static_cast<int>(msg.m_vecValueList.size());
// 히스토리 등록
AddHistory( szKey, msg.m_nCommandType );
if( g_pSBotMng ) g_pSBotMng->ProcConsole( &msg );
};
void onProcConsole( const char* szKey, std::vector<std::string> vecValueList, bool bOut = false )
{
SMSG_CONSOLE_BUILDER msg;
msg.m_strKey = szKey;
msg.m_bOut = bOut;
msg.m_nCommandType = getCommandType(szKey);
msg.m_nValueCount = static_cast<int>(vecValueList.size());
msg.m_vecValueList.assign( vecValueList.begin(), vecValueList.end() );
if( g_pSBotMng ) g_pSBotMng->ProcConsole( &msg );
};
void AddHistory( const char* szKey, int nType )
{
if( !ENV().IsExist(szKey) ) return;
HISTORY_DATA data;
data.m_strType = szKey;
data.m_strData = ENV().GetString(szKey);
switch(nType)
{
case SMSG_CONSOLE_BUILDER::TYPE_GAME: m_vecGameList.push_back( data ); return;
case SMSG_CONSOLE_BUILDER::TYPE_OPTION: m_vecOptionList.push_back( data ); return;
case SMSG_CONSOLE_BUILDER::TYPE_DEBUG: m_vecDebugList.push_back( data ); return;
case SMSG_CONSOLE_BUILDER::TYPE_LUA: m_vecLuaList.push_back( data ); return;
case SMSG_CONSOLE_BUILDER::TYPE_DB: m_vecDBList.push_back( data ); return;
}
}
private:
std::vector<HISTORY_DATA> m_vecGameList;
std::vector<HISTORY_DATA> m_vecOptionList;
std::vector<HISTORY_DATA> m_vecDebugList;
std::vector<HISTORY_DATA> m_vecLuaList;
std::vector<HISTORY_DATA> m_vecDBList;
std::vector<std::string> m_vecENVKeyList;
};
KCommandEnvBuilder& GetCommandBuilder()
{
static KCommandEnvBuilder builder;
return builder;
}
KCommandEnvBuilder::KCommandEnvBuilder()
{
m_pEnvReceiver = new KCommandEnvReceiver;
ENV().SetEventReceiver( m_pEnvReceiver );
}
KCommandEnvBuilder::~KCommandEnvBuilder()
{
delete m_pEnvReceiver;
m_pEnvReceiver = NULL;
}
// set
void KCommandEnvBuilder::BuildCommand( std::vector<std::string> vecStringList )
{
if( vecStringList.empty() ) return;
if( m_pEnvReceiver == NULL ) return;
// 필터링 우선 처리
if( IsFiltering( vecStringList ) ) return;
// 예약어 우선 처리
if( IsReservedType( vecStringList ) ) return;
std::string strKey = vecStringList[0];
bool bCheck = true;
// ! 인지 검사
bCheck = CheckFiltering( strKey );
// Set 인지 검사
if( bCheck ) bCheck = CheckReservedType( strKey, vecStringList );
int nType = CheckType( strKey );
// 찾는 키가 없으므로 에러 리턴
if( bCheck && !ENV().IsExist(strKey.c_str()) )
{
m_pEnvReceiver->onProcErrorConsole( strKey.c_str() );
return;
}
// 이도 저도 아니면 타입에 따라
if( vecStringList.size() > 1 )
{
std::string strValue;
for( int i = 1; i < static_cast<int>(vecStringList.size()); i++ )
{
if( i != 1 ) strValue += ";";
strValue += vecStringList[i];
}
ENV().Set( strKey.c_str(), strValue.c_str() );
}
// 값이 없으면 실행만 하면 되므로 env 넣지 않고 바로 호출
else {
//
// NOTE! 이전의 똑같은 msg에서 env에 들어있던 값이 파라미터로 들어가는 수가 있다!
//
// 수정 by 최지영(2006-06-07) : 게임의 다른 부분에 영향????
//
ENV().Remove(strKey.c_str()); // 따라서 삭제
m_pEnvReceiver->onEnvUpdate( strKey.c_str() );
}
}
const bool KCommandEnvBuilder::IsFiltering( std::vector<std::string> vecStringList )
{
std::string strFilter = vecStringList[0].substr( 0, 1 );
for( int i = 0; i < g_nFilterTypeCount; i++ )
{
if( ::_stricmp(strFilter.c_str(), g_szFilterType[i]) == 0 )
{
if( ::_stricmp(strFilter.c_str(), g_szFilterType[FILTER_RUN]) == 0 ) return false;
return m_pEnvReceiver->onNotifyFilter( vecStringList );
}
}
return false;
}
const bool KCommandEnvBuilder::IsReservedType( std::vector<std::string> vecStringList )
{
for( int i = 0; i < g_nReservdCount; i++ )
{
if( ::_stricmp(vecStringList[0].c_str(), g_szReserv[i]) == 0 )
{
// Set 할 경우 등록시키는 절차로..
if( ::_stricmp(vecStringList[0].c_str(), g_szReserv[RESERVED_SET]) == 0 )
return false;
return m_pEnvReceiver->onNotifyReservedType( vecStringList );
}
}
return false;
}
const int KCommandEnvBuilder::CheckType( std::string & strKey )
{
std::vector<std::string> vecStringList;
XStringUtil::Split( strKey.c_str(), vecStringList, "." );
if( vecStringList.size() > 1 )
{
if( ::_stricmp(vecStringList[0].c_str(), g_szKeyType[SMSG_CONSOLE_BUILDER::TYPE_OPTION]) == 0 ) return SMSG_CONSOLE_BUILDER::TYPE_OPTION;
if( ::_stricmp(vecStringList[0].c_str(), g_szKeyType[SMSG_CONSOLE_BUILDER::TYPE_DEBUG]) == 0 ) return SMSG_CONSOLE_BUILDER::TYPE_DEBUG;
if( ::_stricmp(vecStringList[0].c_str(), g_szKeyType[SMSG_CONSOLE_BUILDER::TYPE_LUA]) == 0 ) return SMSG_CONSOLE_BUILDER::TYPE_LUA;
if( ::_stricmp(vecStringList[0].c_str(), g_szKeyType[SMSG_CONSOLE_BUILDER::TYPE_GAME]) == 0 ) return SMSG_CONSOLE_BUILDER::TYPE_GAME;
}
XStringUtil::Format( strKey, "%s.%s", g_szKeyType[SMSG_CONSOLE_BUILDER::TYPE_GAME], vecStringList[0].c_str() );
return SMSG_CONSOLE_BUILDER::TYPE_GAME;
}
// 예외 처리 : ! 인 경우는 key exist 유효성 검사 하지 않고 무조건 Set 해 주어야 한다.
const bool KCommandEnvBuilder::CheckFiltering( std::string & strKey )
{
std::string strFilter = strKey.substr( 0, 1 );
if( ::_stricmp(strFilter.c_str(), g_szFilterType[FILTER_RUN]) == 0 )
{
strKey = strKey.substr( 1, strKey.size()-1 );
return false;
}
return true;
}
// Set 예약어 : key exist 유효성 검사 하지 않고 무조건 Set 해준다.
const bool KCommandEnvBuilder::CheckReservedType( std::string & strKey, std::vector<std::string>& vecStringList )
{
if( ::_stricmp(strKey.c_str(), g_szReserv[RESERVED_SET]) == 0 && vecStringList.size() > 2 )
{
strKey = vecStringList[1];
vecStringList.erase( vecStringList.begin() );
return false;
}
return true;
}