317 lines
9.9 KiB
C++
317 lines
9.9 KiB
C++
#include "stdafx.h"
|
||
#include "Logger.h"
|
||
#include "lua/lua.hpp"
|
||
#include "GameClient.h"
|
||
#include "PacketInjector.h"
|
||
#include "se_funcs/SE_funcs.h"
|
||
#include "ScriptEngine.h"
|
||
|
||
extern class GameClient *g_game_client; // in main.cpp
|
||
|
||
ScriptEngine *ScriptEngine::s_instance = NULL;
|
||
int ScriptEngine::s_refCount = 0;
|
||
|
||
ScriptEngine *ScriptEngine::getInstance()
|
||
{
|
||
if( ScriptEngine::s_instance == NULL )
|
||
{
|
||
ScriptEngine::s_instance = new ScriptEngine();
|
||
ScriptEngine::s_refCount++;
|
||
log_error( LOG_DEBUG, "ScriptEngine: created instance\n" );
|
||
}
|
||
return ScriptEngine::s_instance;
|
||
}
|
||
|
||
void ScriptEngine::freeInstance()
|
||
{
|
||
if( ScriptEngine::s_refCount > 0 )
|
||
{
|
||
ScriptEngine::s_refCount--;
|
||
if( ScriptEngine::s_refCount == 0 )
|
||
{
|
||
delete ScriptEngine::s_instance;
|
||
ScriptEngine::s_instance = NULL;
|
||
log_error( LOG_DEBUG, "ScriptEngine: freed instance\n" );
|
||
}
|
||
}
|
||
}
|
||
|
||
ScriptEngine::ScriptEngine()
|
||
{
|
||
L = NULL;
|
||
hThread = NULL;
|
||
memset( script_fileName, 0, sizeof(script_fileName) );
|
||
should_stop = false;
|
||
_isPaused = false;
|
||
// handlers
|
||
m_onChat_enabled = false;
|
||
m_onChat_functionName[0] = 0;
|
||
}
|
||
|
||
ScriptEngine::~ScriptEngine()
|
||
{
|
||
deinit();
|
||
}
|
||
|
||
void ScriptEngine::init()
|
||
{
|
||
if( !L )
|
||
{
|
||
L = luaL_newstate(); // open Lua virtual machine
|
||
if( L )
|
||
{
|
||
luaL_openlibs( L ); // load the Lua libraries: math, io, ..
|
||
SE_funcs_register( L ); // register main functions (l2h_*)
|
||
}
|
||
else log_error( LOG_ERROR, "ScriptEngine::init(): luaL_newstate() returned NULL!\n" );
|
||
}
|
||
}
|
||
|
||
void ScriptEngine::deinit()
|
||
{
|
||
if( L )
|
||
{
|
||
lua_close( L ); // Destroys all objects in the given Lua state
|
||
//(calling the corresponding garbage-collection metamethods, if any)
|
||
//and frees all dynamic memory used by this state
|
||
L = NULL;
|
||
if( hThread ) CloseHandle( hThread );
|
||
hThread = NULL;
|
||
should_stop = false;
|
||
_isPaused = false;
|
||
// handlers
|
||
m_onChat_enabled = false;
|
||
m_onChat_functionName[0] = 0;
|
||
}
|
||
}
|
||
|
||
void ScriptEngine::getLastFileName( char *out )
|
||
{
|
||
if( !out ) return;
|
||
out[0] = 0;
|
||
if( script_fileName[0] != 0 ) strcpy( out, script_fileName );
|
||
}
|
||
|
||
void ScriptEngine::startScriptThread( const char *filename /*= NULL */ )
|
||
{
|
||
if( hThread ) return;
|
||
DWORD dwThreadID = 0;
|
||
if( filename ) strcpy( script_fileName, filename );
|
||
hThread = (HANDLE)_beginthreadex( NULL, 0,
|
||
(unsigned int (__stdcall *)(void *))scriptEngineThread, (void *)this,
|
||
0, (unsigned int *)&dwThreadID );
|
||
}
|
||
|
||
bool ScriptEngine::stopScriptThread( DWORD dwTimeoutMsec, bool bTerminate )
|
||
{
|
||
if( !hThread ) return true;
|
||
this->should_stop = true; // "signal" to stop )
|
||
DWORD wait_res = WaitForSingleObject( hThread, dwTimeoutMsec );
|
||
if( wait_res != WAIT_OBJECT_0 ) // wait failed :(
|
||
{
|
||
if( !bTerminate ) return false; // do not do anything, just ret false
|
||
TerminateThread( hThread, 0 );
|
||
log_error( LOG_ERROR, "ScriptEngine::stopScriptThread(): script thread wait failed, TERMINATED!\n" );
|
||
this->deinit();
|
||
this->set_onChat_handler( NULL ); // disable onChat() handler
|
||
this->init();
|
||
if( L ) log_error( LOG_WARNING, "ScriptEngine::stopScriptThread(): re-initialized ScriptEngine, bugs possible ><\n" );
|
||
}
|
||
if( hThread ) CloseHandle( hThread ); // cleanup
|
||
hThread = NULL;
|
||
this->should_stop = false; // no more need to stop...
|
||
return true;
|
||
}
|
||
|
||
int ScriptEngine::isScriptThreadRunning() const
|
||
{
|
||
if( hThread == NULL ) return 0;
|
||
return 1;
|
||
}
|
||
|
||
int ScriptEngine::isScriptThreadPaused() const
|
||
{
|
||
if( _isPaused ) return 1;
|
||
return 0;
|
||
}
|
||
|
||
bool ScriptEngine::pauseScriptThread( bool bPause /*= true*/ )
|
||
{
|
||
if( !isScriptThreadRunning() ) return false;
|
||
int prev_suspend_count = 0;
|
||
if( bPause )
|
||
{
|
||
prev_suspend_count = (int)SuspendThread( hThread );
|
||
if( prev_suspend_count > 0 )
|
||
{
|
||
log_error( LOG_WARNING, "ScriptEngine::pauseScriptThread(): PAUSE: thread was already suspended, resuming again\n" );
|
||
ResumeThread( hThread );
|
||
}
|
||
_isPaused = true;
|
||
return true;
|
||
}
|
||
else
|
||
{
|
||
prev_suspend_count = ResumeThread( hThread );
|
||
if( prev_suspend_count > 1 )
|
||
{
|
||
log_error( LOG_WARNING, "ScriptEngine::pauseScriptThread(): UNPAUSE: possible that thread is still suspended!\n" );
|
||
}
|
||
_isPaused = false;
|
||
return true;
|
||
}
|
||
}
|
||
|
||
DWORD WINAPI ScriptEngine::scriptEngineThread( LPVOID lpvClass )
|
||
{
|
||
if( !lpvClass ) return 0;
|
||
ScriptEngine *pcls = (ScriptEngine *)lpvClass;
|
||
|
||
// run a Lua script here
|
||
int r = luaL_loadfile( pcls->L, pcls->script_fileName );
|
||
if( r == 0 )
|
||
{
|
||
log_error( LOG_DEBUG, "SE: file [%s] loaded.\n", pcls->script_fileName );
|
||
// Call main...
|
||
try
|
||
{
|
||
r = lua_pcall( pcls->L, 0, LUA_MULTRET, 0 );
|
||
}
|
||
catch(...)
|
||
{
|
||
log_error( LOG_ERROR, "SE: catched C++ exception after lua_pcall()\n" );
|
||
ErrorLogger_FlushLogFile();
|
||
}
|
||
|
||
if( r == 0 ) log_error( LOG_DEBUG, "SE: run OK\n" );
|
||
else
|
||
{
|
||
log_error( LOG_ERROR, "SE: file [%s] lua_pcall() error %d\n", pcls->script_fileName, r );
|
||
switch( r )
|
||
{
|
||
case LUA_ERRRUN: log_error( LOG_ERROR, "SE: LUA_ERRRUN\n" ); break;
|
||
case LUA_ERRMEM: log_error( LOG_ERROR, "SE: LUA_ERRMEM\n" ); break;
|
||
case LUA_ERRSYNTAX: log_error( LOG_ERROR, "SE: LUA_ERRSYNTAX\n" ); break;
|
||
case LUA_ERRERR: log_error( LOG_ERROR, "SE: LUA_ERRERR\n" ); break;
|
||
}
|
||
int nArgs = lua_gettop( pcls->L );
|
||
if( nArgs >= 1 )
|
||
{
|
||
if( lua_isstring( pcls->L, 1 ) )
|
||
log_error( LOG_ERROR, "SE: [%s]\n", lua_tolstring( pcls->L, 1, NULL ) );
|
||
}
|
||
lua_settop( pcls->L, 0 ); // clear stack
|
||
}
|
||
}
|
||
else
|
||
{
|
||
log_error( LOG_ERROR, "SE: file [%s] luaL_loadfile() error %d\n", pcls->script_fileName, r );
|
||
switch( r )
|
||
{
|
||
case LUA_ERRRUN: log_error( LOG_ERROR, "SE: LUA_ERRRUN\n" ); break;
|
||
case LUA_ERRMEM: log_error( LOG_ERROR, "SE: LUA_ERRMEM\n" ); break;
|
||
case LUA_ERRSYNTAX: log_error( LOG_ERROR, "SE: LUA_ERRSYNTAX\n" ); break;
|
||
case LUA_ERRERR: log_error( LOG_ERROR, "SE: LUA_ERRERR\n" ); break;
|
||
case LUA_ERRFILE: log_error( LOG_ERROR, "SE: LUA_ERRFILE\n" ); break;
|
||
}
|
||
int nArgs = lua_gettop( pcls->L );
|
||
if( nArgs >= 1 )
|
||
{
|
||
if( lua_isstring( pcls->L, 1 ) )
|
||
{
|
||
log_error( LOG_ERROR, "SE: [%s]\n", lua_tolstring( pcls->L, 1, NULL ) );
|
||
//lua_pop( pcls->L, 1 ); // really?
|
||
}
|
||
}
|
||
lua_settop( pcls->L, 0 ); // clear stack
|
||
}
|
||
pcls->set_onChat_handler( NULL ); // disable onChat() handler
|
||
if( pcls->hThread ) CloseHandle( pcls->hThread ); // cleanup
|
||
pcls->hThread = NULL; // mark thread as not running
|
||
pcls->should_stop = false;
|
||
log_error( LOG_DEBUG, "SE: scriptEngineThread() stopped [%s]\n", pcls->script_fileName );
|
||
|
||
return 1;
|
||
}
|
||
|
||
// http://www.lua.ru/doc/3.7.html
|
||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20> Lua:
|
||
// a = f('how', t.x, 14)
|
||
//
|
||
//<2F><> <20><> <20> C:
|
||
// lua_getfield(L, LUA_GLOBALSINDEX, 'f'); /* function to be called */
|
||
// lua_pushstring(L, 'how'); /* 1st argument */
|
||
// lua_getfield(L, LUA_GLOBALSINDEX, 't'); /* table to be indexed */
|
||
// lua_getfield(L, -1, 'x'); /* push result of t.x (2nd arg) */
|
||
// lua_remove(L, -2); /* remove 't' from the stack */
|
||
// lua_pushinteger(L, 14); /* 3rd argument */
|
||
// lua_call(L, 3, 1); /* call 'f' with 3 arguments and 1 result */
|
||
// lua_setfield(L, LUA_GLOBALSINDEX, 'a'); /* set global 'a' */
|
||
//
|
||
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>: <20> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>. <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.
|
||
|
||
void ScriptEngine::call_onChat( unsigned int senderObjectId, int channelId, const wchar_t *message, const wchar_t *from )
|
||
{
|
||
if( !m_onChat_enabled ) return;
|
||
if( !message ) return;
|
||
if( !isScriptThreadRunning() ) return;
|
||
if( isScriptThreadPaused() ) return;
|
||
//
|
||
pauseScriptThread( true ); // pause script thread
|
||
//
|
||
// convert strings from Unicode to ANSI
|
||
char message_ansi[1024];
|
||
char from_ansi[128];
|
||
message_ansi[0] = from_ansi[0] = 0;
|
||
WideCharToMultiByte( CP_ACP, 0, message, -1, message_ansi, sizeof(message_ansi)-1, NULL, NULL );
|
||
if( from ) WideCharToMultiByte( CP_ACP, 0, from, -1, from_ansi, sizeof(from_ansi)-1, NULL, NULL );
|
||
message_ansi[sizeof(message_ansi)-1] = 0;
|
||
from_ansi[sizeof(from_ansi)-1] = 0;
|
||
//
|
||
//lua_getfield( L, LUA_GLOBALSINDEX, m_onChat_functionName ); // function to be called
|
||
lua_getglobal( L, m_onChat_functionName ); // function to be called
|
||
lua_pushinteger( L, senderObjectId ); // param 1 - sender object ID
|
||
lua_pushinteger( L, channelId ); // param 2 - channel ID
|
||
lua_pushstring( L, message_ansi ); // param 3 - chat message
|
||
lua_pushstring( L, from_ansi ); // param 4 - sender name (only for PM)
|
||
// call onChat with 4 arguments and 0 return values: onChat( senderObjectId, channelId, message, from )
|
||
int ret = lua_pcall( L, 4, 0, 0 );
|
||
if( ret != 0 )
|
||
{
|
||
log_error( LOG_ERROR, "SE: call_onChat() [%s] error %d\n", script_fileName, ret );
|
||
switch( ret )
|
||
{
|
||
case LUA_ERRRUN: log_error( LOG_ERROR, "SE: LUA_ERRRUN\n" ); break;
|
||
case LUA_ERRMEM: log_error( LOG_ERROR, "SE: LUA_ERRMEM\n" ); break;
|
||
case LUA_ERRSYNTAX: log_error( LOG_ERROR, "SE: LUA_ERRSYNTAX\n" ); break;
|
||
case LUA_ERRERR: log_error( LOG_ERROR, "SE: LUA_ERRERR\n" ); break;
|
||
case LUA_ERRFILE: log_error( LOG_ERROR, "SE: LUA_ERRFILE\n" ); break;
|
||
}
|
||
int nArgs = lua_gettop( L );
|
||
if( nArgs >= 1 )
|
||
{
|
||
if( lua_isstring( L, 1 ) )
|
||
log_error( LOG_ERROR, "SE: [%s]\n", lua_tolstring( L, 1, NULL ) );
|
||
}
|
||
lua_settop( L, 0 ); // clear stack
|
||
}
|
||
else log_error( LOG_DEBUG, "SE: call_onChat() [%s] OK\n", script_fileName );
|
||
//
|
||
pauseScriptThread( false ); // resume script thread
|
||
//
|
||
}
|
||
|
||
void ScriptEngine::set_onChat_handler( const char *funcName )
|
||
{
|
||
if( funcName == NULL ) // disable
|
||
{
|
||
m_onChat_enabled = false;
|
||
m_onChat_functionName[0] = 0;
|
||
return;
|
||
}
|
||
m_onChat_enabled = true;
|
||
strncpy( m_onChat_functionName, funcName, sizeof(m_onChat_functionName)-1 );
|
||
m_onChat_functionName[sizeof(m_onChat_functionName)-1] = 0;
|
||
}
|
||
|