#include "CAngelScriptMgr.h"
#include "CMoveObject.h"
#include <windows.h> // timeGetTime()
#include "LogUtility.h"
#define OK 0
#define NG -1
//angelscript,c++両方で使用できるグローバル変数を定義する
float g_fdata = 0;
float g_fx = 0;
float g_fy = 0;
int g_LogLevel = LOG_WARN;
//scriptひとつひとつ読み込み、ビルドに成功した場合bsuccessをtrueに設定しておく
struct _ScriptList
{
char *scriptname; //angel script名
bool bsuccess; //scrptnameのangelscriptがビルドできたか
}ScriptList[] =
{
{ "resource/script/test11.as", false },
{ "resource/script/test12.as",false },
};
#define ANGEL_SCRIPT_NUM (sizeof(ScriptList)/sizeof(ScriptList[0]) )
//angle scritptないで使用するmethodがふえたらここに追加
//別々のscript(ファイル)にある関数でもここにはすべて記述する。 chDescriptionにはどのファイル名で定義してあるかも記述しておく
//asIScriptContextのmethodでangelscriptから戻り値を受け取ることができるのは以下の型のみ
//chFuncIDは戻り値の型が違う場合c++側で再コンパイルが必要
//GetReturnAddress(),GetReturnByte(),GetReturnDouble()
//GetReturnDWord(),GetReturnFloat(),GetReturnObject()
//GetReturnQWord(),GetReturnWord()
struct _AngelFuncList
{
int iID; //index、for,while 用途
char *chFuncID; //angle scriptのasIScriptContext->Prepare(funcId); のfuncIdに指定する文字列
char *chDescription; //angle script用の関数説明用
}AngelFuncList[] =
{
{0,"float test1()","print test file:test11.as"},
{1,"CMoveObject getscriptreturn()"," file:test11.as"},
{2,"CMoveObject getscriptreturn2( CMoveObject cmove )"," file:test11.as"},
{3,"float test2()"," file:test2.as"},
{4,"float TestCorutine()","corotine test file::test3.as"},
//{3,"int test4()",""},
};
//anglesciprtで使用する関数の数
#define ANGEL_FUNC_NUM (sizeof(AngelFuncList)/sizeof(AngelFuncList[0]) )
//このクラスのコンストラクタで、angelscriptの関数類を使えるようにしておく
CAngelScriptMgr::CAngelScriptMgr()
{
int iRet = OK;
m_pasengine = NULL;
m_pasmod = NULL;
m_pasctx = NULL;
m_pcontextMgr = NULL;
m_mapAngelfunc.clear();
//angelscriptのengine初期化
iRet = InitializeAngelEngine(); assert( iRet >= 0 );
//c++,angelscript両方で使用する変数登録
iRet = RegisterProperty(); assert( iRet >= 0 );
//angelscriptで使用したいc++のクラスを登録
iRet = RegisterMyClass(); assert( iRet >= 0 );
//angelscriptをひとつひとつ読み込みビルドできるかcheck
iRet = CheckBuild(); assert( iRet >= 0 );
SetAngelFuncMap();
}
CAngelScriptMgr::~CAngelScriptMgr()
{
if( m_pasctx != NULL )
{
m_pasctx->Release();
}
if( m_pasengine != NULL )
{
m_pasengine->Release();
}
if( m_pcontextMgr != NULL )
{
delete m_pcontextMgr;
m_pcontextMgr = NULL;
}
if( m_pasmod != NULL )
{
m_pasmod = NULL;
}
m_mapAngelfunc.clear();
}
void CAngelScriptMgr::DisplayErrorMsg( int err )
{
int ifuncid = m_pasctx->GetExceptionFunction();
asIScriptFunction *func = m_pasengine->GetFunctionDescriptorById( ifuncid );
switch( err )
{
case asEXECUTION_ABORTED:
cout << "The script was aborted before it could finish. Probably it timed out." << endl;
PUT_LOG( g_LogLevel,"The script was aborted before it could finish. Probably it timed out.\n" );
break;
case asEXECUTION_EXCEPTION:
cout << "The script ended with an exception." << endl;
cout << "func: " << func->GetDeclaration() << endl;
cout << "modl: " << func->GetModuleName() << endl;
cout << "sect: " << func->GetScriptSectionName() << endl;
cout << "line: " << m_pasctx->GetExceptionLineNumber() << endl;
cout << "desc: " << m_pasctx->GetExceptionString() << endl;
PUT_LOG( g_LogLevel,"The script ended with an exception.\n" );
PUT_LOG( g_LogLevel,"func:%s\n",func->GetDeclaration() );
PUT_LOG( g_LogLevel,"modl:%s\n", func->GetModuleName() );
PUT_LOG( g_LogLevel,"sect:%s\n", func->GetScriptSectionName() );
PUT_LOG( g_LogLevel,"line:%d\n", m_pasctx->GetExceptionLineNumber() );
PUT_LOG( g_LogLevel,"line:%d\n", m_pasctx->GetExceptionString() );
func->Release();
break;
default:
cout << "Unknown error " << endl;
PUT_LOG( g_LogLevel,"Unknown error.\n" );
break;
}
}
//angelscript内の関数に時間がかかる場合はabortを投げて例外終了させる
void LineCallback2(asIScriptContext *ctx, DWORD *timeOut)
{
// If the time out is reached we abort the script
if( *timeOut < timeGetTime() )
{
ctx->Abort();
}
// It would also be possible to only suspend the script,
// instead of aborting it. That would allow the application
// to resume the execution where it left of at a later
// time, by simply calling Execute() again.
}
//この関数では戻り値float,引数なしの関数を実行する
int CAngelScriptMgr::Excute( int index,float &fret)
{
int iRet = OK;
CAngelMap mapdata;
//int ifuncid = m_mapAngelfunc[string(chFunc)];
mapdata = m_mapAngelfunc[index];
// Create our context, prepare it, and then execute
m_pasctx = m_pasengine->CreateContext();
//anglescriptの関数を実行するための準備
m_pasctx->Prepare( mapdata.iAngelFuncID );
// We don't want to allow the script to hang the application, e.g. with an
// infinite loop, so we'll use the line callback function to set a timeout
// that will abort the script after a certain time. Before executing the
// script the timeOut variable will be set to the time when the script must
// stop executing.
DWORD timeOut;
iRet = m_pasctx->SetLineCallback( asFUNCTION( LineCallback2 ), &timeOut, asCALL_CDECL );
if( iRet < 0 )
{
cout << "Failed to set the line callback function." << endl;
PUT_LOG( g_LogLevel,"Failed to set the line callback function.\n" );
return NG;
}
// Set the timeout before executing the function. Give the function 1 sec
// to return before we'll abort it.
timeOut = timeGetTime() + 1000;
//angel scriptの実行
iRet = m_pasctx->Execute();
//angelscript内の関数に実行した場合、エラー表示を行う
if( iRet != asEXECUTION_FINISHED )
{
// The execution didn't finish as we had planned. Determine why.
if( iRet == asEXECUTION_ABORTED )
{
DisplayErrorMsg( asEXECUTION_ABORTED );
}
else if( iRet == asEXECUTION_EXCEPTION )
{
DisplayErrorMsg( asEXECUTION_EXCEPTION );
}
else
{
cout << "The script ended for some unforeseen reason (" << iRet << ")." << endl;
PUT_LOG( g_LogLevel,"The script ended for some unforeseen reason.\n" );
}
}
else
{
fret = m_pasctx->GetReturnFloat();
}
return iRet;
}
int CAngelScriptMgr::Excute( int index,CMoveObject *ret)
{
int iRet = OK;
CAngelMap mapdata;
mapdata = m_mapAngelfunc[index];
if( mapdata.iAngelFuncID < 0 )
{
return NG;
}
// Create our context, prepare it, and then execute
m_pasctx = m_pasengine->CreateContext();
//anglescriptの関数を実行するための準備
iRet = m_pasctx->Prepare( mapdata.iAngelFuncID );
//引数のセット(angel関数に渡す引数はこの関数では固定する)
iRet = m_pasctx->SetArgObject( 0,(void*)ret );
// We don't want to allow the script to hang the application, e.g. with an
// infinite loop, so we'll use the line callback function to set a timeout
// that will abort the script after a certain time. Before executing the
// script the timeOut variable will be set to the time when the script must
// stop executing.
DWORD timeOut;
iRet = m_pasctx->SetLineCallback( asFUNCTION( LineCallback2 ), &timeOut, asCALL_CDECL );
if( iRet < 0 )
{
cout << "Failed to set the line callback function." << endl;
PUT_LOG( g_LogLevel,"Failed to set the line callback function.\n" );
return NG;
}
// Add the support for co-routines
//g_pasctx->RegisterCoRoutineSupport(g_pasengine);
//contextMgr.RegisterCoRoutineSupport(engine)
// Set the timeout before executing the function. Give the function 1 sec
// to return before we'll abort it.
timeOut = timeGetTime() + 1000;
//angel scriptの実行
iRet = m_pasctx->Execute();
//angelscript内の関数に実行した場合、エラー表示を行う
if( iRet != asEXECUTION_FINISHED )
{
// The execution didn't finish as we had planned. Determine why.
if( iRet == asEXECUTION_ABORTED )
{
DisplayErrorMsg( asEXECUTION_ABORTED );
}
else if( iRet == asEXECUTION_EXCEPTION )
{
DisplayErrorMsg( asEXECUTION_EXCEPTION );
}
else
{
cout << "The script ended for some unforeseen reason (" << iRet << ")." << endl;
PUT_LOG( g_LogLevel,"The script ended for some unforeseen reason.\n" );
}
}
else
{
// Retrieve the return value from the context
//script側ではfloat を返す時は末尾にf(10.12f,3.14f)をつける。(double,floatかを区別させるため)
//angelscript側ではこの形でintの戻り値をかえせないので、g_ansを使って取得する。
//fret = m_pasctx->GetReturnFloat();
void *pret = m_pasctx->GetReturnObject();
CMoveObject *pCMove = (CMoveObject*)pret;
if( pCMove != NULL )
{
*ret = *pCMove;
}
}
return iRet;
}
int CAngelScriptMgr::GetAngelFuncNum()
{
return m_mapAngelfunc.size();
}
int CAngelScriptMgr::SetAngelFuncMap()
{
int iRet = OK;
int iangelfuncnum = 0;
for( int i = 0; i < ANGEL_SCRIPT_NUM; i++)
{
//buildに成功したangelscriptのみ読み込み
if( ScriptList[i].bsuccess == true )
{
LoadScript( ScriptList[i].scriptname );
iangelfuncnum++;
}
}
//使用できるangelscriptのみモジュールとしてbuild
BuildAngelScript();
for( int i = 0; i < ANGEL_FUNC_NUM; i++)
{
//anglescript内でのc++の関数のfuncidを取得しておく
int ifuncid = m_pasmod->GetFunctionIdByDecl( AngelFuncList[i].chFuncID );
if( ifuncid < 0 )
{
printf("The script must have the function %s. Please add it and try again.\n",AngelFuncList[i].chFuncID);
PUT_LOG( g_LogLevel,"The script must have the function %s. Please add it and try again.\n",AngelFuncList[i].chFuncID );
continue;
}
CAngelMap angeldata;
angeldata.iAngelFuncID = ifuncid;
angeldata.strFuncName = string( AngelFuncList[i].chFuncID );
m_mapAngelfunc[i] = angeldata;
}
return iRet;
}
int CAngelScriptMgr::CheckBuild()
{
int iRet = OK;
for( int i = 0; i < ANGEL_SCRIPT_NUM; i++)
{
iRet = LoadScript( ScriptList[i].scriptname );
if( iRet == OK )
{
iRet = BuildAngelScript();
if( iRet == OK )
{
//angelscriptのビルドに成功したらtrueに設定しておく
ScriptList[i].bsuccess = true;
}
else
{
printf("faile build script name %s\n",ScriptList[i].scriptname );
PUT_LOG( g_LogLevel,"faile build script name %s\n",ScriptList[i].scriptname );
}
}
}
return iRet;
}
int CAngelScriptMgr::LoadScript( char *ScriptName )
{
int iRet = OK;
FILE *f = fopen(ScriptName, "rb");
if ( f == NULL )
{
printf(" file not found\n");
PUT_LOG( g_LogLevel,"file not found\n");
return NG;
}
fseek(f, 0, SEEK_END);
//file sizeを取得する
int len = ftell( f );
fseek( f, 0, SEEK_SET );
std::string script;
script.resize(len);
size_t c = fread( &script[0], len, 1, f );
fclose(f);
//scriptのファイル名でセクションを登録する。
iRet = m_pasmod->AddScriptSection( ScriptName, &script[0], len );
return iRet;
}
//この関数はangelscriptひとつひとつチェックした後
//buildに成功したすべてのangelscriptをモジュールとして再Buildする。
int CAngelScriptMgr::BuildAngelScript()
{
int iRet = OK;
iRet = m_pasmod->Build();
if( iRet < 0 )
{
printf("angel script build failed\n" );
PUT_LOG( g_LogLevel,"angel script build failed\n");
return NG;
}
return iRet;
}
void MessageCallback2( const asSMessageInfo *msg, void *param )
{
const char *type = "ERR ";
if( msg->type == asMSGTYPE_WARNING )
{
type = "WARN";
}
else if( msg->type == asMSGTYPE_INFORMATION )
{
type = "INFO";
}
printf("%s (%d, %d) : %s : %s\n", msg->section, msg->row, msg->col, type, msg->message);
PUT_LOG( g_LogLevel,"%s (%d, %d) : %s : %s\n", msg->section, msg->row, msg->col, type, msg->message );
}
void print2( string &msg )
{
std::cout << msg << std::endl;
}
void print2( int num )
{
std::cout << num << std::endl;
}
void print2( asUINT num )
{
std::cout << num << std::endl;
}
void print2( float num )
{
std::cout << num << std::endl;
}
void print2( double num )
{
std::cout << num << std::endl;
}
int CAngelScriptMgr::RegisterProperty()
{
int iRet = OK;
iRet = m_pasengine->RegisterGlobalProperty("int g_fdata", &g_fdata); assert( iRet >= 0 );
iRet = m_pasengine->RegisterGlobalProperty("int g_fx", &g_fx); assert( iRet >= 0 );
iRet = m_pasengine->RegisterGlobalProperty("int g_fy", &g_fy); assert( iRet >= 0 );
return iRet;
}
int CAngelScriptMgr::RegisterMyClass()
{
//CMoveObject.hにあるクラスを登録
int iRet = OK;
iRet = m_pasengine->RegisterObjectType("CMoveObject", sizeof(CMoveObject), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS); assert( iRet >= 0 );
iRet = m_pasengine->RegisterObjectProperty("CMoveObject", "float fx", offsetof(CMoveObject, fx)); assert( iRet >= 0 );
iRet = m_pasengine->RegisterObjectProperty("CMoveObject", "float fy", offsetof(CMoveObject, fy)); assert( iRet >= 0 );
iRet = m_pasengine->RegisterObjectProperty("CMoveObject", "float fwidth", offsetof(CMoveObject, fwidth)); assert( iRet >= 0 );
iRet = m_pasengine->RegisterObjectProperty("CMoveObject", "float fheight", offsetof(CMoveObject, fheight)); assert( iRet >= 0 );
iRet = m_pasengine->RegisterObjectProperty("CMoveObject", "float fhitradius", offsetof(CMoveObject, fhitradius)); assert( iRet >= 0 );
iRet = m_pasengine->RegisterObjectProperty("CMoveObject", "float facceleration", offsetof(CMoveObject, facceleration)); assert( iRet >= 0 );
iRet = m_pasengine->RegisterObjectProperty("CMoveObject", "float fdirection", offsetof(CMoveObject, fdirection)); assert( iRet >= 0 );
iRet = m_pasengine->RegisterObjectProperty("CMoveObject", "float fspeed", offsetof(CMoveObject, fspeed)); assert( iRet >= 0 );
iRet = m_pasengine->RegisterObjectProperty("CMoveObject", "float fangle", offsetof(CMoveObject, fangle)); assert( iRet >= 0 );
iRet = m_pasengine->RegisterObjectProperty("CMoveObject", "float finterval", offsetof(CMoveObject, finterval)); assert( iRet >= 0 );
iRet = m_pasengine->RegisterObjectProperty("CMoveObject", "float flevel", offsetof(CMoveObject, flevel)); assert( iRet >= 0 );
iRet = m_pasengine->RegisterObjectProperty("CMoveObject", "float fkind", offsetof(CMoveObject, fkind)); assert( iRet >= 0 );
iRet = m_pasengine->RegisterObjectProperty("CMoveObject", "float fcolor", offsetof(CMoveObject, fcolor)); assert( iRet >= 0 );
iRet = m_pasengine->RegisterObjectProperty("CMoveObject", "float falpha", offsetof(CMoveObject, falpha)); assert( iRet >= 0 );
iRet = m_pasengine->RegisterObjectProperty("CMoveObject", "float falive", offsetof(CMoveObject, falive)); assert( iRet >= 0 );
iRet = m_pasengine->RegisterObjectProperty("CMoveObject", "float fscale", offsetof(CMoveObject, fscale)); assert( iRet >= 0 );
return iRet;
}
int CAngelScriptMgr::InitializeAngelEngine()
{
PUT_LOG( g_LogLevel,"start\n" );
int iRet = OK;
// Create the script engine
m_pasengine = asCreateScriptEngine(ANGELSCRIPT_VERSION);
//SHIFT-JIS対応
m_pasengine->SetEngineProperty(asEP_SCRIPT_SCANNER ,0);
// Set the message callback to receive information on errors in human readable form.
// It's recommended to do this right after the creation of the engine, because if
// some registration fails the engine may send valuable information to the message
// stream.
iRet = m_pasengine->SetMessageCallback( asFUNCTION( MessageCallback2 ), 0, asCALL_CDECL ); assert( iRet >= 0 );
if( iRet < 0 )
{
printf("failed Message Callback\n" );
PUT_LOG( g_LogLevel,"failed Message Callback\n" );
return NG;
}
m_pasmod = m_pasengine->GetModule( 0, asGM_ALWAYS_CREATE );
if( iRet < 0 )
{
printf("failed GetModule\n" );
PUT_LOG( g_LogLevel,"failed GetModule\n" );
return NG;
}
// AngelScript doesn't have a built-in string type, as there is no definite standard
// string type for C++ applications. Every developer is free to register it's own string type.
// The SDK do however provide a standard add-on for registering a string type, so it's not
// necessary to implement the registration yourself if you don't want to.
RegisterStdString( m_pasengine );
//anglescript側でprint(string),print(型)でそれぞれの値を表示できるようにする。print系は複数
iRet = m_pasengine->RegisterGlobalFunction( "void print(const string &in)", asFUNCTIONPR(print2, (string &in), void), asCALL_CDECL ); assert( iRet >= 0 );
iRet = m_pasengine->RegisterGlobalFunction( "void print(int)", asFUNCTIONPR(print2, (int), void), asCALL_CDECL ); assert( iRet >= 0 );
iRet = m_pasengine->RegisterGlobalFunction( "void print(float)", asFUNCTIONPR(print2, (float), void), asCALL_CDECL ); assert( iRet >= 0 );
iRet = m_pasengine->RegisterGlobalFunction( "void print(uint)", asFUNCTIONPR(print2, (asUINT), void), asCALL_CDECL ); assert( iRet >= 0 );
iRet = m_pasengine->RegisterGlobalFunction( "void print(double)", asFUNCTIONPR(print2, (double), void), asCALL_CDECL ); assert( iRet >= 0 );
RegisterScriptAny( m_pasengine );
// Add the support for co-routines
m_pcontextMgr = new CContextMgr();
m_pcontextMgr->RegisterCoRoutineSupport( m_pasengine );
return iRet;
}