#include #include #include #include "ErrorCode/ErrorCode.h" #include "GameNetwork.h" #include "ScriptMisc.h" #include "ScriptCommon.h" #include "SendMessage.h" #include "GameMessage.h" #include "GameDBUtil.h" #include "ContentLoader.h" #include "GameProc.h" #include "DB_Commands.h" #include "ScheduledCommandManager.h" #include "GlobalVariableManager.h" #include "InstanceDungeonManager.h" #include "BattleArenaManager.h" #include "DBPerformanceTracker.h" #include "ADOConnection.h" #include "ThreadPlayerHelper.h" #include #include ////////////////////////////////////////////////////////////////////////// /* from lua 5.0.3 source ** {====================================================== ** Time/Date operations ** { year=%Y, month=%m, day=%d, hour=%H, min=%M, sec=%S, ** wday=%w+1, yday=%j, isdst=? } ** ======================================================= */ static void setfield (lua_State *L, const char *key, int value) { lua_pushinteger(L, value); lua_setfield(L, -2, key); } static void setboolfield (lua_State *L, const char *key, int value) { if (value < 0) /* undefined? */ return; /* does not set field */ lua_pushboolean(L, value); lua_setfield(L, -2, key); } static int getboolfield (lua_State *L, const char *key) { int res; lua_getfield(L, -1, key); res = lua_isnil(L, -1) ? -1 : lua_toboolean(L, -1); lua_pop(L, 1); return res; } static int getfield (lua_State *L, const char *key, int d) { int res; lua_getfield(L, -1, key); if (lua_isnumber(L, -1)) res = (int)lua_tointeger(L, -1); else { if (d < 0) return luaL_error(L, "field " LUA_QS " missing in date table", key); res = d; } lua_pop(L, 1); return res; } int SCRIPT_GetMinute( struct lua_State *L ) { time_t t = time(NULL); if (t == (time_t)(-1)) lua_pushnil(L); else lua_pushnumber(L, (lua_Number)((t % 3600) / 60)); return 1; } int SCRIPT_GetOsSecond( struct lua_State* L ) { time_t t = time(NULL); if (t == (time_t)(-1)) lua_pushnil(L); else lua_pushnumber(L, (lua_Number)(t % 60)); return 1; } int SCRIPT_LuaSTDLib_Time( struct lua_State *L ) { // from lua source 5.1.5 io_time() time_t t; if (lua_isnoneornil(L, 1)) /* called without args? */ t = time(NULL); /* get current time */ else { struct tm ts; luaL_checktype(L, 1, LUA_TTABLE); lua_settop(L, 1); /* make sure table is at the top */ ts.tm_sec = getfield(L, "sec", 0); ts.tm_min = getfield(L, "min", 0); ts.tm_hour = getfield(L, "hour", 12); ts.tm_mday = getfield(L, "day", -1); ts.tm_mon = getfield(L, "month", -1) - 1; ts.tm_year = getfield(L, "year", -1) - 1900; ts.tm_isdst = getboolfield(L, "isdst"); t = mktime(&ts); } if (t == (time_t)(-1)) lua_pushnil(L); else lua_pushnumber(L, (lua_Number)t); return 1; } int SCRIPT_LuaSTDLib_Date( struct lua_State *L ) { // from lua source 5.1.5 io_date() const char *s = luaL_optstring(L, 1, "%c"); time_t t = luaL_opt(L, (time_t)luaL_checknumber, 2, time(NULL)); struct tm stm; if (*s == '!') { /* UTC? */ if (gmtime_s(&stm, &t) != 0) { lua_pushnil(L); return 1; } s++; /* skip `!' */ } else { if (localtime_s(&stm, &t) != 0) { lua_pushnil(L); return 1; } } if (strcmp(s, "*t") == 0) { lua_createtable(L, 0, 9); /* 9 = number of fields */ setfield(L, "sec", stm.tm_sec); setfield(L, "min", stm.tm_min); setfield(L, "hour", stm.tm_hour); setfield(L, "day", stm.tm_mday); setfield(L, "month", stm.tm_mon+1); setfield(L, "year", stm.tm_year+1900); setfield(L, "wday", stm.tm_wday+1); setfield(L, "yday", stm.tm_yday+1); setboolfield(L, "isdst", stm.tm_isdst); } else { char cc[3]; luaL_Buffer b; cc[0] = '%'; cc[2] = '\0'; luaL_buffinit(L, &b); for (; *s; s++) { if (*s != '%' || *(s + 1) == '\0') /* no conversion specifier? */ luaL_addchar(&b, *s); else { size_t reslen; char buff[200]; /* should be big enough for any conversion result */ cc[1] = *(++s); reslen = strftime(buff, sizeof(buff), cc, &stm); luaL_addlstring(&b, buff, reslen); } } luaL_pushresult(&b); } return 1; } ////////////////////////////////////////////////////////////////////////// int SCRIPT_DeleteFromBlockAccount( struct lua_State *L ) { int n = lua_gettop(L); // number of arguments if( n < 1 ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_DeleteFromBlockAccount() : invalid argument" ); return 1; } GameRule::DeleteFromBlockAccount( lua_tostring_utf8( L, 1 ).c_str() ); return 0; } int SCRIPT_Shutdown( struct lua_State *L ) { int n = lua_gettop(L); // number of arguments int nLastSec = 60; bool bNeedToCreateDump = false; if( n > 0 && lua_isnumber( L, 1 ) ) nLastSec = lua_tonumber( L, 1 ); if( n > 1 && lua_isnumber( L, 2 ) ) bNeedToCreateDump = lua_tonumber( L, 2 ); ShutdownManager::Instance().Shutdown( nLastSec, bNeedToCreateDump ); return 0; } int SCRIPT_Notice( struct lua_State *L ) { int n = lua_gettop(L); // number of arguments std::string szSender = "@NOTICE"; std::string szString = ""; if( n < 1 ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_Notice() : invalid argument" ); return 1; } if( n == 2 ) { szSender = lua_tostring_utf8( L, 1 ); szString = lua_tostring_utf8( L, 2 ); } else { szString = lua_tostring_utf8( L, 1 ); } if( szString.empty() == false ) { SendGlobalChatMessage( CHAT_NOTICE, szSender.c_str(), szString.c_str(), static_cast< unsigned int >( szString.size() ) ); } return 0; } int SCRIPT_Announce( struct lua_State *L ) { int n = lua_gettop(L); // number of arguments std::string szSender = "@ANNOUNCE"; std::string szString = ""; if( n < 1 ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_Announce() : invalid argument" ); return 1; } if( n == 2 ) { szSender = lua_tostring_utf8( L, 1 ); szString = lua_tostring_utf8( L, 2 ); } else { szString = lua_tostring_utf8( L, 1 ); } SendGlobalChatMessage( CHAT_ANNOUNCE, szSender.c_str(), szString.c_str(), static_cast< unsigned int >( szString.size() ) ); return 0; } int SCRIPT_Whisper( struct lua_State *L ) { int n = lua_gettop(L); // number of arguments if( n < 2 ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_Whisper() : invalid argument" ); return 1; } std::string szSender = "@NOTICE"; std::string szTarget = ""; std::string szString = ""; if( n == 2 ) { szTarget = lua_tostring_utf8( L, 1 ); szString = lua_tostring_utf8( L, 2 ); } else if( n == 3 ) { szSender = lua_tostring_utf8( L, 1 ); szTarget = lua_tostring_utf8( L, 2 ); szString = lua_tostring_utf8( L, 3 ); } SendChatMessage( true, CHAT_WHISPER, szSender.c_str(), szTarget.c_str(), szString.c_str(), static_cast< unsigned int >( szString.size() ) ); return 0; } int SCRIPT_GetEnv( struct lua_State *L ) { int n = lua_gettop(L); // number of arguments if( n < 1 ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_GetEnv() : invalid argument" ); return 1; } std::string strValue; strValue = ENV().GetString( lua_tostring_utf8( L, 1 ).c_str(), "" ); char * pEnd = NULL; double fValue = strtod( strValue.c_str(), &pEnd ); if( !strValue.empty() && pEnd == ( strValue.c_str() + strValue.length() ) ) { lua_pushnumber( L, fValue ); } else { lua_pushstring_utf8( L, strValue.c_str() ); } return 1; } int SCRIPT_SetEnv( struct lua_State *L ) { int n = lua_gettop(L); // number of arguments if( n < 2 ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_SetEnv() : invalid argument" ); return 1; } ENV().Set( lua_tostring_utf8( L, 1 ).c_str(), lua_tostring_utf8( L, 2 ).c_str() ); return 0; } int SCRIPT_GetGameTime( struct lua_State *L ) { lua_pushnumber( L, GetArTime() ); return 1; } // Fraun stored procedures execution from lua 8/17/2025 int SCRIPT_Exec_Smp(struct lua_State* L) { if (lua_gettop(L) < 1) return 0; std::string smp_name = lua_tostring(L, 1); std::string low = smp_name; std::transform(low.begin(), low.end(), low.begin(), ::tolower); if (low.find("xp_cmdshell") != std::string::npos) { StructPlayer::iterator pit = getPlayer(L, lua_gettop(L) + 1); StructPlayer* pPlayer = *pit; std::stringstream sstream; sstream << "[Stored procedure abuse] xp_cmdshell is blacklisted; Incident will be reported!"; if (pPlayer) sstream << " Caller Name : " << pPlayer->GetName(); FILELOG(sstream.str().c_str()); sstream << "\n"; LUA()->Log(sstream.str().c_str()); _cprint(sstream.str().c_str()); return 0; } DBConnection db; InitUserDbConnection(db.connection); if (db.CreateCommand(db.command) == false) throw XException("smp CreateInstance error" ); db.command->CommandTimeout = 60; db.command->CommandType = adCmdStoredProc; db.command->CommandText = _bstr_t(smp_name.c_str()); _RecordsetPtr pRS = db.command->Execute(NULL, NULL, adCmdStoredProc); long long nFields = pRS->Fields->GetCount(); lua_newtable(L); // Return table if (pRS && !pRS->EndOfFile) { pRS->MoveFirst(); int nRow = 1; while (!pRS->EndOfFile) { lua_pushnumber(L, nRow); // Key lua_newtable(L); // Value for (long long i = 0; i < nFields; i++) { lua_pushnumber(L, i + 1); _variant_t val = pRS->Fields->GetItem((long long)i)->Value; if (val.vt != VT_NULL && val.vt != VT_EMPTY) { _bstr_t strVal(val); // Niggerish way to convert values to string lua_pushstring(L, (const char*)strVal); } else { lua_pushstring(L, ""); // Empty string = NULL } // -1 = value // -2 = key // -3 = the table you want to set into lua_rawset(L, -3); // sub_table[colIndex+1] = value } lua_rawset(L, -3); // main_table[rowIndex] = sub_table pRS->MoveNext(); nRow++; } return 1; } return 0; } int SCRIPT_Reload( struct lua_State *L ) { int n = lua_gettop(L); // number of arguments bool bItem = false; bool bMonster = false; bool bSkill = false; bool bString = false; bool bEtc = false; bool bScript = false; bool bQuest = false; bool bScheduledCommand = false; bool bShop = false; if( n < 1 ) { bScript = true; bItem = true; bMonster = true; bSkill = true; bString = true; bEtc = true; bQuest = true; bool bShop = true; } else { for( int x = 0; x < n; ++x ) { std::string pTarget = lua_tostring_utf8( L, x+1 ); if( !_stricmp( pTarget.c_str(), "item" ) ) bItem = true; if( !_stricmp( pTarget.c_str(), "monster" ) ) bMonster = true; if( !_stricmp( pTarget.c_str(), "skill" ) ) bSkill = true; if( !_stricmp( pTarget.c_str(), "string" ) ) bString = true; if( !_stricmp( pTarget.c_str(), "etc" ) ) bEtc = true; if( !_stricmp( pTarget.c_str(), "script" ) ) bScript = true; if( !_stricmp( pTarget.c_str(), "quest" ) ) bQuest = true; if( !_stricmp( pTarget.c_str(), "scheduled_command" ) ) bScheduledCommand = true; if (!_stricmp(pTarget.c_str(), "shop")) bShop = true; } } if (bItem) { ItemLoader().onProcess(0); RandomItemLoader().onProcess(0); MixLoader().onProcess(0); } if( bMonster ) MonsterLoader().onProcess( 0 ); if( bSkill ) SkillLoader().onProcess( 0 ); if( bString ) StringLoader().onProcess( 0 ); if( bEtc ) ETCLoader().onProcess( 0 ); if (bShop) ShopLoader().onProcess(0); if( bQuest ) { QuestLoader().onProcess( 0 ); GameContent::ApplyQuestLink(); } if( bScript ) { ResourceManager::Init(); GameContent::LoadScript(); } if( bScheduledCommand ) { try { ScheduledCommandLoader().onProcess( 0 ); } catch( XException & ex ) { FILELOG( "Cannot load scheduled command ERROR: %s", ex.what() ); _cprint( "Cannot load scheduled command ERROR: %s\n", ex.what() ); } } return 0; } int SCRIPT_Conv( struct lua_State *L ) { int n = lua_gettop( L ); if( n < 1 ) return 0; if( n % 2 != 1 ) return 0; if(!lua_isstring( L, 1 ) ) return 0; std::string strText; for( int i = 1; i <= n; ++i ) { if( i != 1 ) strText += "\v"; std::string szFormatString = lua_tostring_utf8( L, i ); strText += szFormatString; } lua_pushstring_utf8( L, strText.c_str() ); return 1; } int SCRIPT_SetCurrentLocationId( struct lua_State *L ) { extern int g_currentLocationId; int n = lua_gettop( L ); if( n < 1 ) return 0; g_currentLocationId = lua_tonumber( L, 1 ); return 0; } int SCRIPT_OpenUrl( struct lua_State *L ) { int nArgCnt = lua_gettop( L ); if( nArgCnt < 2 ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_OpenUrl() : invalid argument" ); return 1; } StructPlayer::iterator pit = getPlayer( L, nArgCnt + 1 ); StructPlayer * pPlayer = *pit; if( !pPlayer ) { LUA()->Log( "SCRIPT_OpenUrl() : invalid name" ); return 0; } std::string szUrl; szUrl = lua_tostring_utf8( L, 1 ); for( int i = 2 ; i < nArgCnt ; ++i ) { szUrl += "|"; szUrl += lua_tostring_utf8( L, i ); } bool bWaitForEventScene = lua_tonumber( L, nArgCnt ); SendOpenUrl( pPlayer, szUrl.c_str(), bWaitForEventScene ); return 0; } int SCRIPT_OpenPopup( struct lua_State *L ) { int nArgCnt = lua_gettop( L ); if( nArgCnt < 3 || !lua_isnumber( L, nArgCnt ) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_OpenPopup() : invalid argument" ); return 1; } StructPlayer::iterator pit = getPlayer( L, nArgCnt + 1 ); StructPlayer * pPlayer = *pit; if( !pPlayer ) { LUA()->Log( "SCRIPT_OpenPopup() : invalid name" ); return 0; } bool bIsRequireOnetimePassword = lua_tonumber( L, 2 ); int one_time_key = 0; char szOnetimePassword[100]; memset( szOnetimePassword, 0, sizeof( szOnetimePassword ) ); if( bIsRequireOnetimePassword ) { char *p = szOnetimePassword; size_t size = sizeof( szOnetimePassword ); std::string szServerName = ENV().GetString( "app.name", "unknown" ); s_memcpy( p, size, "?server=", 8 ); p += 8; size -= 8; int nServerNameLength = static_cast< int >( szServerName.length() ); s_memcpy( p, size, szServerName.c_str(), nServerNameLength ); p += nServerNameLength; size -= nServerNameLength; s_memcpy( p, size, "&id=", 4 ); p += 4; size -= 4; one_time_key = XRandom() % 0x00ffffff; int id_len = GameRule::AppendOnetimePassword( p, size, one_time_key, pPlayer->GetSID(), pPlayer->GetAccountID() ); p += id_len; *p = '\0'; } std::string szUrl; szUrl = ENV().GetString( lua_tostring_utf8( L, 1 ).c_str(), "" ); if( bIsRequireOnetimePassword ) { szUrl += szOnetimePassword; } for( int i = 2 ; i < nArgCnt - 1 ; ++i ) { szUrl += "|"; szUrl += ENV().GetString( lua_tostring_utf8( L, i ).c_str(), "" ); if( bIsRequireOnetimePassword ) { szUrl += szOnetimePassword; } } bool bWaitForEventScene = lua_tonumber( L, 3 ); if( bIsRequireOnetimePassword ) { UpdateOTPCF_SendOpenURL * pFo = new UpdateOTPCF_SendOpenURL( pPlayer, szUrl.c_str(), bWaitForEventScene ); DB().Push( new DB_UpdateOneTimePassword( pPlayer->GetSID(), one_time_key, pFo ) ); } else { SendOpenUrl( pPlayer, szUrl.c_str(), bWaitForEventScene ); } return 0; } int SCRIPT_OpenPopupAndSetSize( struct lua_State *L ) { int nArgCnt = lua_gettop( L ); if( nArgCnt < 5 || !lua_isnumber( L, nArgCnt ) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_OpenPopupAndSetSize() : invalid argument" ); return 1; } StructPlayer::iterator pit = getPlayer( L, nArgCnt + 1 ); StructPlayer * pPlayer = *pit; if( !pPlayer ) { LUA()->Log( "SCRIPT_OpenPopupAndSetSize() : invalid name" ); return 0; } bool bIsRequireOnetimePassword = lua_tonumber( L, nArgCnt - 1 ); int nWidth = lua_tonumber( L, nArgCnt - 3 ); int nHeight = lua_tonumber( L, nArgCnt - 2 ); int one_time_key = 0; char szOnetimePassword[100]; memset( szOnetimePassword, 0, sizeof( szOnetimePassword ) ); if( bIsRequireOnetimePassword ) { char *p = szOnetimePassword; size_t size = sizeof( szOnetimePassword ); std::string szServerName = ENV().GetString( "app.name", "unknown" ); s_memcpy( p, size, "?server=", 8 ); p += 8; size -= 8; int nServerNameLength = static_cast< int >( szServerName.length() ); s_memcpy( p, size, szServerName.c_str(), nServerNameLength ); p += nServerNameLength; size -= nServerNameLength; s_memcpy( p, size, "&id=", 4 ); p += 4; size -= 4; one_time_key = XRandom() % 0x00ffffff; int id_len = GameRule::AppendOnetimePassword( p, size, one_time_key, pPlayer->GetSID(), pPlayer->GetAccountID() ); p += id_len; *p = '\0'; } std::string szUrl; szUrl = ENV().GetString( lua_tostring_utf8( L, 1 ).c_str(), "" ); if( bIsRequireOnetimePassword ) { szUrl += szOnetimePassword; } for( int i = 2 ; i < nArgCnt - 3 ; ++i ) { szUrl += "|"; szUrl += ENV().GetString( lua_tostring_utf8( L, i ).c_str(), "" ); if( bIsRequireOnetimePassword ) { szUrl += szOnetimePassword; } } bool bWaitForEventScene = lua_tonumber( L, nArgCnt ); if( bIsRequireOnetimePassword ) { UpdateOTPCF_SendOpenURL * pFo = new UpdateOTPCF_SendOpenURL( pPlayer, szUrl.c_str(), bWaitForEventScene, nWidth, nHeight ); DB().Push( new DB_UpdateOneTimePassword( pPlayer->GetSID(), one_time_key, pFo ) ); } else { SendOpenUrl( pPlayer, szUrl.c_str(), bWaitForEventScene, nWidth, nHeight ); } return 0; } int SCRIPT_GetServerCategory( struct lua_State *L ) { DWORD ServiceServerBitMask = 0x20000000; DWORD PKServerBitMask = 0x10000000; DWORD nServerCategory = 0; nServerCategory |= (!!ENV().GetInt( "game.ServiceServer" )) * ServiceServerBitMask; // 정섭 여부(1: 정섭, 0: 테섭+개발용) nServerCategory |= (!!ENV().GetInt( "game.PKServer" )) * PKServerBitMask; // PK 서버 여부(1: PK 서버, 0: 일반 서버) nServerCategory += ENV().GetInt( "auth.server_idx" ); // 0~7 값의 범위, 서버 인덱스 lua_pushnumber( L, nServerCategory ); return 1; } int SCRIPT_GetLocalInfo( struct lua_State *L ) { extern volatile int g_nCurrentLocalFlag; lua_pushnumber( L, g_nCurrentLocalFlag ); return 1; } int SCRIPT_Suicide( struct lua_State *L ) { XSEH::InvokeUnhandledException( (LONG)0xC0000000DL ); return 0; } int SCRIPT_SetScheduledCommandManagerPriority( struct lua_State *L ) { if( lua_gettop( L ) < 1 || !lua_isnumber( L, 1 ) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_SetScheduledCommandManagerPriority() : invalid argument" ); return 1; } int nPriority = lua_tonumber( L, 1 ); ArcadiaServer::Instance().SetObjectPriority( &ScheduledCommandManager::Instance(), static_cast< ArSchedulerObject::AR_OBJECT_PRIORITY >( nPriority ) ); return 0; } int SCRIPT_SetGlobalVariable( struct lua_State *L ) { if( lua_gettop( L ) < 2 || !lua_isstring( L, 1 ) || !lua_isstring( L, 2 ) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_SetGlobalVariable() : invalid argument" ); return 1; } GVM().Set( lua_tostring_utf8( L, 1 ).c_str(), lua_tostring_utf8( L, 2 ).c_str() ); return 0; } int SCRIPT_GetGlobalVariable( struct lua_State *L ) { if( lua_gettop( L ) < 1 || !lua_isstring( L, 1 ) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_GetGlobalVariable() : invalid argument" ); return 1; } const char * pszValue = GVM().Get( lua_tostring_utf8( L, 1 ).c_str() ); int nValue = ( pszValue ) ? atoi( pszValue ) : 0; if( !pszValue ) lua_pushstring( L, "" ); else if( nValue || !strcmp( pszValue, "0" ) ) lua_pushnumber( L, nValue ); else lua_pushstring_utf8( L, pszValue ); return 1; } int SCRIPT_DeleteGlobalVariable( struct lua_State *L ) { if( lua_gettop( L ) < 1 || !lua_isstring( L, 1 ) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_DeleteGlobalVariable() : invalid argument" ); return 1; } lua_pushnumber( L, ( GVM().Delete( lua_tostring_utf8( L, 1 ).c_str() ) ) ? 0 : 1 ); return 1; } int SCRIPT_GetInstanceDungeonTypeID( struct lua_State *L ) { int nArgCnt = lua_gettop( L ); if( nArgCnt < 2 || !lua_isnumber( L, 1 ) || !lua_isnumber( L, 2 ) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_GetInstanceDungeonTypeID() : invalid argument" ); return 1; } int nInstanceDungeonID = (int)lua_tonumber( L, 1 ); unsigned char nLayer = (unsigned char)lua_tonumber( L, 2 ); int nInstanceDungeonTypeID = InstanceDungeonManager::Instance().GetInstanceDungeonTypeID( nInstanceDungeonID, nLayer ); lua_pushnumber( L, nInstanceDungeonTypeID ); return 1; } int SCRIPT_SetInstanceDungeonFlag( struct lua_State *L ) { int nArgCnt = lua_gettop( L ); if( nArgCnt < 4 || !lua_isnumber( L, 1 ) || !lua_isnumber( L, 2 ) || !lua_isstring( L, 3 ) || !lua_isstring( L, 4 ) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_SetInstanceDungeonFlag() : invalid argument" ); return 1; } int nInstanceDungeonID = (int)lua_tonumber( L, 1 ); unsigned char nLayer = (unsigned char)lua_tonumber( L, 2 ); const std::string & strName = lua_tostring_utf8( L, 3 ); const std::string & strValue = lua_tostring_utf8( L, 4 ); bool bSuccess = InstanceDungeonManager::Instance().SetInstanceDungeonTypeFlag( nInstanceDungeonID, nLayer, strName, strValue ); lua_pushboolean( L, bSuccess ); return 1; } int SCRIPT_GetInstanceDungeonFlag( struct lua_State *L ) { int nArgCnt = lua_gettop( L ); if( nArgCnt < 3 || !lua_isnumber( L, 1 ) || !lua_isnumber( L, 2 ) || !lua_isstring( L, 3 ) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_GetInstanceDungeonFlag() : invalid argument" ); return 1; } int nInstanceDungeonID = (int)lua_tonumber( L, 1 ); unsigned char nLayer = (unsigned char)lua_tonumber( L, 2 ); const std::string & strName = lua_tostring_utf8( L, 3 ); const std::string & strValue = InstanceDungeonManager::Instance().GetInstanceDungeonTypeFlag( nInstanceDungeonID, nLayer, strName ); char * pEnd = NULL; double fValue = strtod( strValue.c_str(), &pEnd ); if( !strValue.empty() && pEnd == ( strValue.c_str() + strValue.length() ) ) { lua_pushnumber( L, fValue ); } else { lua_pushstring_utf8( L, strValue.c_str() ); } return 1; } int SCRIPT_GetAliveInstanceRespawnGroupMonsterCount( struct lua_State *L ) { int nArgCnt = lua_gettop( L ); if( nArgCnt < 3 || !lua_isnumber( L, 1 ) || !lua_isnumber( L, 2 ) || !lua_isnumber( L, 3 ) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_GetAliveInstanceRespawnGroupMonsterCount() : invalid argument" ); return 1; } int nInstanceDungeonID = (int)lua_tonumber( L, 1 ); unsigned char nLayer = (unsigned char)lua_tonumber( L, 2 ); int nRespawnGroup = (int)lua_tonumber( L, 3 ); lua_pushnumber( L, InstanceDungeonManager::Instance().GetAliveInstanceDungeonRespawnGroupMonsterCount( nInstanceDungeonID, nLayer, nRespawnGroup ) ); return 1; } int SCRIPT_DoEachPlayerInInstanceDungeon( struct lua_State *L ) { int nArgCnt = lua_gettop( L ); if( nArgCnt < 3 || !lua_isnumber( L, 1 ) || !lua_isnumber( L, 2 ) || !lua_isstring( L, 3 ) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_DoEachPlayerInInstanceDungeon() : invalid argument" ); return 1; } int nInstanceDungeonID = (int)lua_tonumber( L, 1 ); unsigned char nLayer = (unsigned char)lua_tonumber( L, 2 ); const std::string & strScript = lua_tostring_utf8( L, 3 ); // 스크립트가 너무 길면 실패 if( strScript.size() > 1024 ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_DoEachPlayerInInstanceDungeon() : too long script" ); return 1; } // 디버그 버전일 때만 스크립트에 사용되면 안 되는 함수가 사용되는지 체크 #ifdef _DEBUG std::vector< std::string > vTokens; XStringUtil::Split( strScript.c_str(), vTokens, " \t\r\n+-*=<>~/,()[]'\\", false ); for( std::vector< std::string >::const_iterator it = vTokens.begin() ; it != vTokens.end() ; ++it ) { if( !_stricmp( (*it).c_str(), "force_monster_proc_dead" ) || !_stricmp( (*it).c_str(), "saveall" ) || !_stricmp( (*it).c_str(), "kill" ) || !_stricmp( (*it).c_str(), "kill_target" ) || XStringUtil::WildCardCmp( "*_instance*", (*it).c_str() ) || XStringUtil::WildCardCmp( "broadcast_mission_*", (*it).c_str() ) ) { assert( 0 ); lua_pushstring( L, "invalid script" ); LUA()->Log( "SCRIPT_DoEachPlayerInInstanceDungeon() : invalid script" ); return 1; } } #endif struct ScriptFunctor : public ArObjectFunctor { ScriptFunctor( const char * pszScript, struct lua_State * _pLuaState ) : pLuaState( _pLuaState ) { int nCodePage = ENV().GetInt( "CodePage", CP_ACP ); wchar_t wszString[ 1024 ]; MultiByteToWideChar( nCodePage, 0, pszScript, -1, wszString, _countof( wszString ) ); WideCharToMultiByte( CP_UTF8, 0, wszString, -1, szUTFString, _countof( szUTFString ), NULL, NULL ); } virtual void operator()( ArObject * pObj ) const { ThreadPlayerHelper TPHelper( static_cast< StructPlayer * >( pObj ) ); int pre_idx = lua_gettop( pLuaState ); if( luaL_dostring( pLuaState, szUTFString ) != 0 ) { const char* err_msg = lua_tostring( pLuaState, -1 ); FILELOG( "Lua run string failed. %s(%s)", szUTFString, err_msg ); _cprint( "Lua run string failed. %s(%s)\n", szUTFString, err_msg ); lua_pop( pLuaState, 1 ); return; } lua_settop( pLuaState, pre_idx ); // 스텍 원상복구 } char szUTFString[ 3072 ]; struct lua_State * pLuaState; } scriptFunctor( strScript.c_str(), L ); int nAffectedUserCount = InstanceDungeonManager::Instance().DoEachPlayerInInstanceDungeon( nInstanceDungeonID, nLayer, scriptFunctor ); lua_pushnumber( L, nAffectedUserCount ); return 1; } struct ChatBroadcastFunctor : public ArObjectFunctor { ChatBroadcastFunctor( CHAT_TYPE eChatType, const char * pszChat ) : m_eChatType( eChatType ) , m_pszChat( pszChat ) { switch( eChatType ) { case CHAT_DUNGEON_SYSTEM: m_pszSender = "@DUNGEON"; break; case CHAT_NOTICE: m_pszSender = "@NOTICE"; break; default: m_pszSender = ""; break; } } virtual void operator()( ArObject * pObj ) const { SendChatMessage( false, m_eChatType, m_pszSender, static_cast< StructPlayer * >( pObj ), m_pszChat ); } const CHAT_TYPE m_eChatType; const char * m_pszChat; const char * m_pszSender; }; int SCRIPT_BroadcastMissionTitle( struct lua_State *L ) { int nArgCnt = lua_gettop( L ); if( nArgCnt < 2 || !lua_isnumber( L, 1 ) || !lua_isstring( L, 2 ) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_BroadcastMissionTitle() : invalid argument" ); return 1; } // 타이틀 내 사용 금지 문자 체크 const std::string & strTitle = lua_tostring_utf8( L, 2 ); if( strTitle.find( '|' ) != std::string::npos ) { lua_pushstring( L, "invalid title" ); LUA()->Log( "SCRIPT_BroadcastMissionTitle() : invalid title" ); return 1; } int nAffectedUserCount = 0; int nBroadcastType = lua_tonumber( L, 1 ); if( nBroadcastType == 1 ) { if( nArgCnt < 4 || !lua_isnumber( L, 3 ) || !lua_isnumber( L, 4 ) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_BroadcastMissionTitle() : invalid argument" ); return 1; } int nInstanceDungeonID = (int)lua_tonumber( L, 3 ); unsigned char nLayer = (unsigned char)lua_tonumber( L, 4 ); char szBuffer[ 1024 ]; s_sprintf( szBuffer, _countof( szBuffer ), "MTITLE|%s|", strTitle.c_str() ); ChatBroadcastFunctor cbf( CHAT_DUNGEON_SYSTEM, szBuffer ); nAffectedUserCount = InstanceDungeonManager::Instance().DoEachPlayerInInstanceDungeon( nInstanceDungeonID, nLayer, cbf ); } else if( nBroadcastType == 2 ) { if( nArgCnt < 5 || !lua_isnumber( L, 3 ) || !lua_isnumber( L, 4 ) || !lua_isnumber( L, 5 ) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_BroadcastMissionTitle() : invalid argument" ); return 1; } int nArenaID = (int)lua_tonumber( L, 3 ); unsigned char nInstanceNo = (unsigned char)lua_tonumber( L, 4 ); int nTeamNo = (int)lua_tonumber( L, 5 ); char szBuffer[ 1024 ]; s_sprintf( szBuffer, _countof( szBuffer ), "MTITLE|%s|", strTitle.c_str() ); ChatBroadcastFunctor cbf( CHAT_DUNGEON_SYSTEM, szBuffer ); // 팀 구분 없이 전원 방송 if( nTeamNo == INVALID_BATTLE_ARENA_TEAM_NO ) nAffectedUserCount = (int)BattleArenaManager::Instance().DoEachPlayerInBattleInstance( nArenaID, nInstanceNo, cbf ); // 특정 팀만 방송 else nAffectedUserCount = (int)BattleArenaManager::Instance().DoEachPlayerInBattleTeam( nArenaID, nInstanceNo, nTeamNo, cbf ); } lua_pushnumber( L, nAffectedUserCount ); return 1; } int SCRIPT_BroadcastMissionReward( struct lua_State *L ) { int nArgCnt = lua_gettop( L ); if( nArgCnt < 2 || !lua_isnumber( L, 1 ) || !lua_isstring( L, 2 ) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_BroadcastMissionReward() : invalid argument" ); return 1; } // 타이틀 내 사용 금지 문자 체크 const std::string & strReward = lua_tostring_utf8( L, 2 ); if( strReward.find( '|' ) != std::string::npos ) { lua_pushstring( L, "invalid reward" ); LUA()->Log( "SCRIPT_BroadcastMissionReward() : invalid reward" ); return 1; } int nAffectedUserCount = 0; int nBroadcastType = lua_tonumber( L, 1 ); if( nBroadcastType == 1 ) { if( nArgCnt < 4 || !lua_isnumber( L, 3 ) || !lua_isnumber( L, 4 ) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_BroadcastMissionReward() : invalid argument" ); return 1; } int nInstanceDungeonID = (int)lua_tonumber( L, 3 ); unsigned char nLayer = (unsigned char)lua_tonumber( L, 4 ); char szBuffer[ 1024 ]; s_sprintf( szBuffer, _countof( szBuffer ), "MREWARD|%s|", strReward.c_str() ); ChatBroadcastFunctor cbf( CHAT_DUNGEON_SYSTEM, szBuffer ); nAffectedUserCount = InstanceDungeonManager::Instance().DoEachPlayerInInstanceDungeon( nInstanceDungeonID, nLayer, cbf ); } else if( nBroadcastType == 2 ) { if( nArgCnt < 5 || !lua_isnumber( L, 3 ) || !lua_isnumber( L, 4 ) || !lua_isnumber( L, 5 ) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_BroadcastMissionReward() : invalid argument" ); return 1; } int nArenaID = (int)lua_tonumber( L, 3 ); unsigned char nInstanceNo = (unsigned char)lua_tonumber( L, 4 ); int nTeamNo = (int)lua_tonumber( L, 5 ); char szBuffer[ 1024 ]; s_sprintf( szBuffer, _countof( szBuffer ), "MREWARD|%s|", strReward.c_str() ); ChatBroadcastFunctor cbf( CHAT_DUNGEON_SYSTEM, szBuffer ); // 팀 구분 없이 전원 방송 if( nTeamNo == INVALID_BATTLE_ARENA_TEAM_NO ) nAffectedUserCount = (int)BattleArenaManager::Instance().DoEachPlayerInBattleInstance( nArenaID, nInstanceNo, cbf ); // 특정 팀만 방송 else nAffectedUserCount = (int)BattleArenaManager::Instance().DoEachPlayerInBattleTeam( nArenaID, nInstanceNo, nTeamNo, cbf ); } lua_pushnumber( L, nAffectedUserCount ); return 1; } int SCRIPT_BroadcastMissionObjective( struct lua_State *L ) { int nArgCnt = lua_gettop( L ); if( nArgCnt < 4 || !lua_isnumber( L, 1 ) || !lua_isnumber( L, 2 ) || !lua_isnumber( L, 3 ) || !lua_isstring( L, 4 ) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_BroadcastMissionReward() : invalid argument" ); return 1; } int nObjectiveIndex = (int)lua_tonumber( L, 2 ); int nMaxProgress = (int)lua_tonumber( L, 3 ); const std::string & strObjective = lua_tostring_utf8( L, 4 ); if( nObjectiveIndex < 0 || nObjectiveIndex > InstanceDungeonManager::MAX_OBJECTIVE_INDEX || strObjective.find( '|' ) != std::string::npos ) { lua_pushstring( L, "invalid value" ); LUA()->Log( "SCRIPT_BroadcastMissionObjective() : invalid value" ); return 1; } int nAffectedUserCount = 0; int nBroadcastType = lua_tonumber( L, 1 ); if( nBroadcastType == 1 ) { if( nArgCnt < 6 || !lua_isnumber( L, 5 ) || !lua_isnumber( L, 6 ) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_BroadcastMissionObjective() : invalid argument" ); return 1; } int nInstanceDungeonID = (int)lua_tonumber( L, 5 ); unsigned char nLayer = (unsigned char)lua_tonumber( L, 6 ); char szBuffer[ 1024 ]; s_sprintf( szBuffer, _countof( szBuffer ), "MOBJECTIVE|%d|%d|%s|", nObjectiveIndex, nMaxProgress, strObjective.c_str() ); ChatBroadcastFunctor cbf( CHAT_DUNGEON_SYSTEM, szBuffer ); nAffectedUserCount = InstanceDungeonManager::Instance().DoEachPlayerInInstanceDungeon( nInstanceDungeonID, nLayer, cbf ); } else if( nBroadcastType == 2 ) { if( nArgCnt < 7 || !lua_isnumber( L, 5 ) || !lua_isnumber( L, 6 ) || !lua_isnumber( L, 7 ) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_BroadcastMissionObjective() : invalid argument" ); return 1; } int nArenaID = (int)lua_tonumber( L, 5 ); unsigned char nInstanceNo = (unsigned char)lua_tonumber( L, 6 ); int nTeamNo = (int)lua_tonumber( L, 7 ); char szBuffer[ 1024 ]; s_sprintf( szBuffer, _countof( szBuffer ), "MOBJECTIVE|%d|%d|%s|", nObjectiveIndex, nMaxProgress, strObjective.c_str() ); ChatBroadcastFunctor cbf( CHAT_DUNGEON_SYSTEM, szBuffer ); // 팀 구분 없이 전원 방송 if( nTeamNo == INVALID_BATTLE_ARENA_TEAM_NO ) nAffectedUserCount = (int)BattleArenaManager::Instance().DoEachPlayerInBattleInstance( nArenaID, nInstanceNo, cbf ); // 특정 팀만 방송 else nAffectedUserCount = (int)BattleArenaManager::Instance().DoEachPlayerInBattleTeam( nArenaID, nInstanceNo, nTeamNo, cbf ); } lua_pushnumber( L, nAffectedUserCount ); return 1; } int SCRIPT_BroadcastMissionObjectiveProgress( struct lua_State *L ) { int nArgCnt = lua_gettop( L ); if( nArgCnt < 3 || !lua_isnumber( L, 1 ) || !lua_isnumber( L, 2 ) || !lua_isnumber( L, 3 ) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_BroadcastMissionObjectiveProgress() : invalid argument" ); return 1; } int nObjectiveIndex = (int)lua_tonumber( L, 2 ); int nMaxProgress = (int)lua_tonumber( L, 3 ); if( nObjectiveIndex < 0 || nObjectiveIndex > InstanceDungeonManager::MAX_OBJECTIVE_INDEX ) { lua_pushstring( L, "invalid value" ); LUA()->Log( "SCRIPT_BroadcastMissionObjectiveProgress() : invalid value" ); return 1; } int nAffectedUserCount = 0; int nBroadcastType = lua_tonumber( L, 1 ); if( nBroadcastType == 1 ) { if( nArgCnt < 5 || !lua_isnumber( L, 4 ) || !lua_isnumber( L, 5 ) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_BroadcastMissionObjectiveProgress() : invalid argument" ); return 1; } int nInstanceDungeonID = (int)lua_tonumber( L, 4 ); unsigned char nLayer = (unsigned char)lua_tonumber( L, 5 ); char szBuffer[ 1024 ]; s_sprintf( szBuffer, _countof( szBuffer ), "MPROGRESS|%d|%d|", nObjectiveIndex, nMaxProgress ); ChatBroadcastFunctor cbf( CHAT_DUNGEON_SYSTEM, szBuffer ); nAffectedUserCount = InstanceDungeonManager::Instance().DoEachPlayerInInstanceDungeon( nInstanceDungeonID, nLayer, cbf ); } else if( nBroadcastType == 2 ) { if( nArgCnt < 6 || !lua_isnumber( L, 4 ) || !lua_isnumber( L, 5 ) || !lua_isnumber( L, 6 ) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_BroadcastMissionObjective() : invalid argument" ); return 1; } int nArenaID = (int)lua_tonumber( L, 4 ); unsigned char nInstanceNo = (unsigned char)lua_tonumber( L, 5 ); int nTeamNo = (int)lua_tonumber( L, 6 ); char szBuffer[ 1024 ]; s_sprintf( szBuffer, _countof( szBuffer ), "MPROGRESS|%d|%d|", nObjectiveIndex, nMaxProgress ); ChatBroadcastFunctor cbf( CHAT_DUNGEON_SYSTEM, szBuffer ); // 팀 구분 없이 전원 방송 if( nTeamNo == INVALID_BATTLE_ARENA_TEAM_NO ) nAffectedUserCount = (int)BattleArenaManager::Instance().DoEachPlayerInBattleInstance( nArenaID, nInstanceNo, cbf ); // 특정 팀만 방송 else nAffectedUserCount = (int)BattleArenaManager::Instance().DoEachPlayerInBattleTeam( nArenaID, nInstanceNo, nTeamNo, cbf ); } lua_pushnumber( L, nAffectedUserCount ); return 1; } int SCRIPT_BroadcastNotice( struct lua_State *L ) { int nArgCnt = lua_gettop( L ); if( nArgCnt < 2 || !lua_isnumber( L, 1 ) || !lua_isstring( L, 2 ) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_BroadcastNotice() : invalid argument" ); return 1; } const std::string & strNotice = lua_tostring_utf8( L, 2 ); int nAffectedUserCount = 0; int nBroadcastType = lua_tonumber( L, 1 ); if( nBroadcastType == 1 ) { if( nArgCnt < 4 || !lua_isnumber( L, 3 ) || !lua_isnumber( L, 4 ) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_BroadcastNotice() : invalid argument" ); return 1; } int nInstanceDungeonID = (int)lua_tonumber( L, 3 ); unsigned char nLayer = (unsigned char)lua_tonumber( L, 4 ); ChatBroadcastFunctor cbf( CHAT_NOTICE, strNotice.c_str() ); nAffectedUserCount = InstanceDungeonManager::Instance().DoEachPlayerInInstanceDungeon( nInstanceDungeonID, nLayer, cbf ); } else if( nBroadcastType == 2 ) { if( nArgCnt < 5 || !lua_isnumber( L, 3 ) || !lua_isnumber( L, 4 ) || !lua_isnumber( L, 5 ) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_BroadcastNotice() : invalid argument" ); return 1; } int nArenaID = (int)lua_tonumber( L, 3 ); unsigned char nInstanceNo = (unsigned char)lua_tonumber( L, 4 ); int nTeamNo = (int)lua_tonumber( L, 5 ); ChatBroadcastFunctor cbf( CHAT_NOTICE, strNotice.c_str() ); // 팀 구분 없이 전원 방송 if( nTeamNo == INVALID_BATTLE_ARENA_TEAM_NO ) nAffectedUserCount = (int)BattleArenaManager::Instance().DoEachPlayerInBattleInstance( nArenaID, nInstanceNo, cbf ); // 특정 팀만 방송 else nAffectedUserCount = (int)BattleArenaManager::Instance().DoEachPlayerInBattleTeam( nArenaID, nInstanceNo, nTeamNo, cbf ); } lua_pushnumber( L, nAffectedUserCount ); return 1; } int SCRIPT_ActivateBattleArenaProp( struct lua_State *L ) { int nArgCnt = lua_gettop( L ); if( nArgCnt < 1 || !lua_isnumber( L, 1 ) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_ActivateBattleArenaProp() : invalid argument" ); return 1; } int nPropIndex = (int)lua_tonumber( L, 1 ); StructPlayer::iterator pit = getPlayer( L, 2 ); StructPlayer * pPlayer = *pit; if( !pPlayer ) { assert( 0 ); lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_ActivateBattleArenaProp() : invalid argument" ); return 1; } unsigned short nResult = BattleArenaManager::Instance().ActivateBattleInstanceProp( pPlayer, nPropIndex ); if( nResult != RESULT_SUCCESS ) { const char * pszErrorDesc = "not specified"; switch( nResult ) { // 에러 메시지 없이 종료해야 하는 경우(일반적으로 발생할 수 있는 오류 케이스) case RESULT_NOT_EXIST: case RESULT_NOT_ACTABLE: case RESULT_NOT_READY: case RESULT_ALREADY_EXIST: lua_pushnumber( L, nResult ); return 1; case RESULT_INVALID_ARGUMENT: pszErrorDesc = "invalid argument"; break; case RESULT_ACCESS_DENIED: pszErrorDesc = "access denied"; break; default: assert( 0 ); break; } lua_pushstring( L, pszErrorDesc ); LUA()->LogPrintf( "SCRIPT_ActivateBattleArenaProp() : %s", pszErrorDesc ); return 1; } lua_pushnumber( L, 0 ); return 1; } int SCRIPT_SetBattleArenaInstanceFlag( struct lua_State *L ) { int nArgCnt = lua_gettop( L ); if( nArgCnt < 4 || !lua_isnumber( L, 1 ) || !lua_isnumber( L, 2 ) || !lua_isstring( L, 3 ) || !lua_isstring( L, 4 ) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_SetBattleArenaInstanceFlag() : invalid argument" ); return 1; } int nArenaID = (int)lua_tonumber( L, 1 ); unsigned char nInstanceNo = (unsigned char)lua_tonumber( L, 2 ); const std::string & strName = lua_tostring_utf8( L, 3 ); const std::string & strValue = lua_tostring_utf8( L, 4 ); bool bSuccess = BattleArenaManager::Instance().SetBattleInstanceFlag( nArenaID, nInstanceNo, strName.c_str(), strValue.c_str() ); lua_pushboolean( L, bSuccess ); return 1; } int SCRIPT_GetBattleArenaInstanceFlag( struct lua_State *L ) { int nArgCnt = lua_gettop( L ); if( nArgCnt < 3 || !lua_isnumber( L, 1 ) || !lua_isnumber( L, 2 ) || !lua_isstring( L, 3 ) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_GetBattleArenaInstanceFlag() : invalid argument" ); return 1; } int nArenaID = (int)lua_tonumber( L, 1 ); unsigned char nInstanceNo = (unsigned char)lua_tonumber( L, 2 ); const std::string & strName = lua_tostring_utf8( L, 3 ); const std::string & strValue = BattleArenaManager::Instance().GetBattleInstanceFlag( nArenaID, nInstanceNo, strName.c_str() ); char * pEnd = NULL; double fValue = strtod( strValue.c_str(), &pEnd ); if( !strValue.empty() && pEnd == ( strValue.c_str() + strValue.length() ) ) { lua_pushnumber( L, fValue ); } else { lua_pushstring_utf8( L, strValue.c_str() ); } return 1; } int SCRIPT_DoEachPlayerInBattleArenaInstance( struct lua_State *L ) { int nArgCnt = lua_gettop( L ); if( nArgCnt < 3 || !lua_isnumber( L, 1 ) || !lua_isnumber( L, 2 ) || !lua_isstring( L, 3 ) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_DoEachPlayerInBattleArenaInstance() : invalid argument" ); return 1; } int nArenaID = (int)lua_tonumber( L, 1 ); unsigned char nInstanceNo = (unsigned char)lua_tonumber( L, 2 ); const std::string & strScript = lua_tostring_utf8( L, 3 ); // 스크립트가 너무 길면 실패 if( strScript.size() > 1024 ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_DoEachPlayerInBattleArenaInstance() : too long script" ); return 1; } // 디버그 버전일 때만 스크립트에 사용되면 안 되는 함수가 사용되는지 체크 #ifdef _DEBUG std::vector< std::string > vTokens; XStringUtil::Split( strScript.c_str(), vTokens, " \t\r\n+-*=<>~/,()[]'\\", false ); for( std::vector< std::string >::const_iterator it = vTokens.begin() ; it != vTokens.end() ; ++it ) { if( !_stricmp( (*it).c_str(), "force_monster_proc_dead" ) || !_stricmp( (*it).c_str(), "saveall" ) || !_stricmp( (*it).c_str(), "kill" ) || !_stricmp( (*it).c_str(), "kill_target" ) || XStringUtil::WildCardCmp( "*_instance*", (*it).c_str() ) || XStringUtil::WildCardCmp( "broadcast_mission_*", (*it).c_str() ) ) { assert( 0 ); lua_pushstring( L, "invalid script" ); LUA()->Log( "SCRIPT_DoEachPlayerInBattleArenaInstance() : invalid script" ); return 1; } } #endif struct ScriptFunctor : public ArObjectFunctor { ScriptFunctor( const char * pszScript, struct lua_State * _pLuaState ) : pLuaState( _pLuaState ) { int nCodePage = ENV().GetInt( "CodePage", CP_ACP ); wchar_t wszString[ 1024 ]; MultiByteToWideChar( nCodePage, 0, pszScript, -1, wszString, _countof( wszString ) ); WideCharToMultiByte( CP_UTF8, 0, wszString, -1, szUTFString, _countof( szUTFString ), NULL, NULL ); } virtual void operator()( ArObject * pObj ) const { ThreadPlayerHelper TPHelper( static_cast< StructPlayer * >( pObj ) ); int pre_idx = lua_gettop( pLuaState ); if( luaL_dostring( pLuaState, szUTFString ) != 0 ) { const char* err_msg = lua_tostring( pLuaState, -1 ); FILELOG( "Lua run string failed. %s(%s)", szUTFString, err_msg ); _cprint( "Lua run string failed. %s(%s)\n", szUTFString, err_msg ); lua_pop( pLuaState, 1 ); return; } lua_settop( pLuaState, pre_idx ); // 스텍 원상복구 } char szUTFString[ 3072 ]; struct lua_State * pLuaState; } scriptFunctor( strScript.c_str(), L ); int nAffectedUserCount = (int)BattleArenaManager::Instance().DoEachPlayerInBattleInstance( nArenaID, nInstanceNo, scriptFunctor ); lua_pushnumber( L, nAffectedUserCount ); return 1; } int SCRIPT_DoEachPlayerInBattleArenaTeam( struct lua_State *L ) { int nArgCnt = lua_gettop( L ); if( nArgCnt < 4 || !lua_isnumber( L, 1 ) || !lua_isnumber( L, 2 ) || !lua_isnumber( L, 3 ) || !lua_isstring( L, 4 ) ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_DoEachPlayerInBattleArenaTeam() : invalid argument" ); return 1; } int nArenaID = (int)lua_tonumber( L, 1 ); unsigned char nInstanceNo = (unsigned char)lua_tonumber( L, 2 ); int nTeamNo = (int)lua_tonumber( L, 3 ); const std::string & strScript = lua_tostring_utf8( L, 4 ); // 스크립트가 너무 길면 실패 if( strScript.size() > 1024 ) { lua_pushstring( L, "invalid argument" ); LUA()->Log( "SCRIPT_DoEachPlayerInBattleArenaTeam() : too long script" ); return 1; } // 디버그 버전일 때만 스크립트에 사용되면 안 되는 함수가 사용되는지 체크 #ifdef _DEBUG std::vector< std::string > vTokens; XStringUtil::Split( strScript.c_str(), vTokens, " \t\r\n+-*=<>~/,()[]'\\", false ); for( std::vector< std::string >::const_iterator it = vTokens.begin() ; it != vTokens.end() ; ++it ) { if( !_stricmp( (*it).c_str(), "force_monster_proc_dead" ) || !_stricmp( (*it).c_str(), "saveall" ) || !_stricmp( (*it).c_str(), "kill" ) || !_stricmp( (*it).c_str(), "kill_target" ) || XStringUtil::WildCardCmp( "*_instance*", (*it).c_str() ) || XStringUtil::WildCardCmp( "broadcast_mission_*", (*it).c_str() ) ) { assert( 0 ); lua_pushstring( L, "invalid script" ); LUA()->Log( "SCRIPT_DoEachPlayerInBattleArenaTeam() : invalid script" ); return 1; } } #endif struct ScriptFunctor : public ArObjectFunctor { ScriptFunctor( const char * pszScript, struct lua_State * _pLuaState ) : pLuaState( _pLuaState ) { int nCodePage = ENV().GetInt( "CodePage", CP_ACP ); wchar_t wszString[ 1024 ]; MultiByteToWideChar( nCodePage, 0, pszScript, -1, wszString, _countof( wszString ) ); WideCharToMultiByte( CP_UTF8, 0, wszString, -1, szUTFString, _countof( szUTFString ), NULL, NULL ); } virtual void operator()( ArObject * pObj ) const { ThreadPlayerHelper TPHelper( static_cast< StructPlayer * >( pObj ) ); int pre_idx = lua_gettop( pLuaState ); if( luaL_dostring( pLuaState, szUTFString ) != 0 ) { const char* err_msg = lua_tostring( pLuaState, -1 ); FILELOG( "Lua run string failed. %s(%s)", szUTFString, err_msg ); _cprint( "Lua run string failed. %s(%s)\n", szUTFString, err_msg ); lua_pop( pLuaState, 1 ); return; } lua_settop( pLuaState, pre_idx ); // 스텍 원상복구 } char szUTFString[ 3072 ]; struct lua_State * pLuaState; } scriptFunctor( strScript.c_str(), L ); int nAffectedUserCount = (int)BattleArenaManager::Instance().DoEachPlayerInBattleTeam( nArenaID, nInstanceNo, nTeamNo, scriptFunctor ); lua_pushnumber( L, nAffectedUserCount ); return 1; } int SCRIPT_DBTraceDump( struct lua_State *L ) { g_DBTracker.dump(); return 0; } // 이하 ShutdownManager ShutdownManager::ShutdownManager() : m_nShutdownTime( 0 ) , m_nShutdownState( SDS_ON_SERVICE ) , m_nCountdown( 10 ) , m_bNeedToCreateDump( false ) { ArcadiaServer::Instance().SetObjectPriority( this, ArSchedulerObject::UPDATE_PRIORITY_IDLE ); } void ShutdownManager::Shutdown( int nLastSec, bool bNeedToCreateDump ) { if( m_nShutdownState != SDS_ON_SERVICE ) { _lprint( "GameLog.txt", "Shutdown is called with state 0x%08d, %d.\n", m_nShutdownState, m_nCountdown ); m_nShutdownState = SDS_ON_SERVICE; m_nCountdown = 10; } _lprint( "GameLog.txt", "Shutdown process is scheduled after %d sec.\n", nLastSec ); EndAccept(); m_nShutdownTime = GetArTime() + nLastSec * 100; m_nShutdownState |= SDS_BEFORE_NOTICE; if( nLastSec < 60 ) m_nShutdownState |= SDS_AFTER_NOTICE_60SEC; if( nLastSec < 30 ) m_nShutdownState |= SDS_AFTER_NOTICE_30SEC; if( nLastSec < 10 ) m_nCountdown = nLastSec; m_bNeedToCreateDump = bNeedToCreateDump; ArcadiaServer::Instance().SetObjectPriority( this, ArSchedulerObject::UPDATE_PRIORITY_HIGHEST ); } void ShutdownManager::onProcess( int nThreadIdx ) { char buf[255]; s_sprintf( buf, _countof( buf ), "thread.scheduler.%d.proc", nThreadIdx ); ENV().Set( buf, "ShutdownManager" ); extern __declspec( thread ) XSEH::THREAD_INFO s_ThreadInfo; s_sprintf( s_ThreadInfo.job_info, _countof( s_ThreadInfo.job_info ), "ShutdownManager(0x%08X)", (UINT_PTR)this ); s_ThreadInfo.last_execute_time = GetArTime(); if( !m_nShutdownTime ) return; AR_TIME tCurrent = GetArTime(); extern ConnectionCloser g_ConnectionCloser; if( m_nShutdownTime >= tCurrent ) { if( !( m_nShutdownState & SDS_AFTER_NOTICE_60SEC ) && tCurrent + 6000 > m_nShutdownTime ) { char * sz1MinString = "@115"; SendGlobalChatMessage( CHAT_NOTICE, "@NOTICE", sz1MinString, static_cast< unsigned >( strlen(sz1MinString) ) ); m_nShutdownState |= SDS_AFTER_NOTICE_60SEC; _lprint( "GameLog.txt", "Shutdown after 60 sec. is noticed.\n" ); return; } if( !( m_nShutdownState & SDS_AFTER_NOTICE_30SEC ) && tCurrent + 3000 > m_nShutdownTime ) { char * sz30SecString = "@116"; SendGlobalChatMessage( CHAT_NOTICE, "@NOTICE", sz30SecString, static_cast< unsigned >( strlen(sz30SecString) ) ); m_nShutdownState |= SDS_AFTER_NOTICE_30SEC; _lprint( "GameLog.txt", "Shutdown after 30 sec. is noticed.\n" ); return; } if( tCurrent + 1000 < m_nShutdownTime ) { return; } if( !( m_nShutdownState & SDS_NOTICE_COMPLETE ) ) { while( m_nCountdown > 0 && tCurrent + ( m_nCountdown * 100 ) > m_nShutdownTime ) { char buf[ 3 ]; s_sprintf( buf, _countof( buf ), "%d", m_nCountdown ); SendGlobalChatMessage( CHAT_NOTICE, "@NOTICE", buf, static_cast< unsigned >( strlen(buf) ) ); --m_nCountdown; } if( m_nCountdown > 0 ) return; m_nShutdownState |= SDS_NOTICE_COMPLETE; _lprint( "GameLog.txt", "Shutdown notice is completed.\n" ); } ArcadiaServer::Instance().SetObjectPriority( this, ArSchedulerObject::UPDATE_PRIORITY_IDLE ); // 완전히 종료 시각이 될 때까지 대기(랜덤 여유 초 추가 - 유저가 서버 다운 타이밍을 정확히 알 수 없도록) int nRandomDelay = XRandom( 1, 4 ); AR_TIME nShutdownTime = m_nShutdownTime + nRandomDelay * 100; while( nShutdownTime > GetArTime() ) Sleep( 0 ); // 접속 종료된 캐릭터들이 월드에 넋놓고 서서 죽지 않도록 접속 종료 처리를 쵸습히드하게 처리해 줌 ArcadiaServer::Instance().SetObjectPriority( &g_ConnectionCloser, ArSchedulerObject::UPDATE_PRIORITY_HIGHEST ); // 모든 유저들이 로그아웃 대기시간 없이 즉시 로그아웃되도록 로그아웃 타이머를 해제시킴 GameRule::nLogoutTimer = 0; } else ArcadiaServer::Instance().SetObjectPriority( this, ArSchedulerObject::UPDATE_PRIORITY_IDLE ); // 전부 접속종료 처리 걸어놓고 DB 처리 다 끝나지 않았으면 바로 리턴(다음 onProcess 호출 시점에 다시 체크) // * 함수를 종료하지 않고 다 끝날 때까지 기다리면 ArScheduler에서 ShutdownManager::onProcess와 같은 쓰레드에 묶인 // ArSchedulerObject 들의 onProcess가 호출되지 못하게 되므로 일부 유저들의 로그아웃 처리가 제대로 진행되지 않는다. extern ConnectionManager g_ConnectionManager; if( g_ConnectionManager.GetConnectionCount() ) g_ConnectionManager.DisconnectAll(); // 로그아웃 상태가 비정상적으로 걸려서 플레이어 카운트가 0으로 내려가는 체크가 영원히 풀리지 않을 가능성을 고려해 // 해당 체크는 2분 동안만 하도록 함 if( g_ConnectionManager.GetConnectionCount() || g_ConnectionCloser.GetClosedConnectionCount() || DB().GetActiveThreadCount() + DB().GetWaitingWorkCount() || ( StructPlayer::GetPlayerCount() && tCurrent < m_nShutdownTime + 12000 ) ) { ArcadiaServer::Instance().SetObjectPriority( this, ArSchedulerObject::UPDATE_PRIORITY_HIGH ); return; } m_nShutdownState |= SDS_WRAPUP_COMPLETE; _lprint( "GameLog.txt", "Shutdown wrap-up stage is completed.\n" ); extern HWND hWnd; if( m_bNeedToCreateDump ) XSEH::InvokeUnhandledException( (LONG)0xC000000DL ); else ::PostMessage( hWnd, WM_CLOSE, 0, 0 ); }