Last active
August 29, 2015 14:24
-
-
Save Pctg-x8/afb06f732abbf83a18ff to your computer and use it in GitHub Desktop.
ScriptEngine: Sample AngelScript Compact Loader/Runtime
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "ScriptEngine.h" | |
// place AngelScript include files into "as" directory | |
// and place add-ons folder into "as/addons" directory | |
#include "as/addons/scriptstdstring/scriptstdstring.h" | |
// AngelScript Runtime/Loader | |
#ifdef _DEBUG | |
#pragma comment(lib, "as\\lib\\angelscriptd.lib") | |
#else | |
#pragma comment(lib, "as\\lib\\angelscript.lib") | |
#endif | |
#define _supressWarnUnused(v) v; | |
ScriptEngine::ScriptEngine() | |
{ | |
this->pEngineInterface = asCreateScriptEngine(ANGELSCRIPT_VERSION); | |
assert(this->pEngineInterface != nullptr); | |
auto r = this->pEngineInterface->SetMessageCallback(asFUNCTION(ScriptEngine::engineCallback), 0, asCALL_CDECL); | |
assert(r >= 0); | |
this->RegisterGlobalProperties(); | |
this->RegisterNativeFunctions(); | |
} | |
ScriptEngine::~ScriptEngine() | |
{ | |
if (this->pRuntimeContext != nullptr) | |
{ | |
this->pRuntimeContext->Release(); | |
this->pRuntimeContext = nullptr; | |
} | |
if (this->pEngineInterface != nullptr) | |
{ | |
this->pEngineInterface->ShutDownAndRelease(); | |
this->pEngineInterface = nullptr; | |
} | |
} | |
void ScriptEngine::_assertInvariantContracts() | |
{ | |
assert(this->pEngineInterface != nullptr); | |
} | |
void ScriptEngine::_ensureModuleStarted() | |
{ | |
assert(this->pCurrentModule != nullptr); | |
} | |
void ScriptEngine::RegisterNativeFunctions() | |
{ | |
this->_assertInvariantContracts(); | |
RegisterStdString(this->pEngineInterface); | |
auto r = this->pEngineInterface->RegisterGlobalFunction("void print(const string& in)", asFUNCTION(ScriptEngine::NativeFunctions::print), asCALL_CDECL); | |
assert(r >= 0); | |
r = this->pEngineInterface->RegisterGlobalFunction("void update()", asFUNCTION(ScriptEngine::NativeFunctions::update), asCALL_CDECL); | |
assert(r >= 0); | |
r = this->pEngineInterface->RegisterGlobalFunction("bool getkey(int keycode)", asFUNCTION(ScriptEngine::NativeFunctions::getkey), asCALL_CDECL); | |
assert(r >= 0); | |
r = this->pEngineInterface->RegisterGlobalFunction("double floor(double)", asFUNCTION(ScriptEngine::NativeFunctions::floor), asCALL_CDECL); | |
assert(r >= 0); | |
r = this->pEngineInterface->RegisterGlobalFunction("double abs(double)", asFUNCTION(ScriptEngine::NativeFunctions::abs), asCALL_CDECL); | |
assert(r >= 0); | |
r = this->pEngineInterface->RegisterGlobalFunction("double sign(double)", asFUNCTION(ScriptEngine::NativeFunctions::sign), asCALL_CDECL); | |
assert(r >= 0); | |
r = this->pEngineInterface->RegisterGlobalFunction("bool getLeftKey()", asFUNCTION(ScriptEngine::NativeFunctions::leftKey), asCALL_CDECL); | |
assert(r >= 0); | |
r = this->pEngineInterface->RegisterGlobalFunction("bool getRightKey()", asFUNCTION(ScriptEngine::NativeFunctions::rightKey), asCALL_CDECL); | |
assert(r >= 0); | |
} | |
void ScriptEngine::RegisterGlobalProperties() | |
{ | |
this->_assertInvariantContracts(); | |
auto r = this->pEngineInterface->RegisterGlobalProperty("int _frameCount", &this->_gp.frameCount); | |
} | |
void ScriptEngine::RegisterProperty(const String& signature, void* ptr) | |
{ | |
this->_assertInvariantContracts(); | |
auto r = this->pEngineInterface->RegisterGlobalProperty(signature.narrow().c_str(), ptr); | |
assert(r >= 0); | |
} | |
void ScriptEngine::engineCallback(const asSMessageInfo* pMsg, void* pParam) | |
{ | |
// ref: http://www.angelcode.com/angelscript/sdk/docs/manual/doc_compile_script.html#doc_compile_script_msg | |
_supressWarnUnused(pParam); | |
switch (pMsg->type) | |
{ | |
case asMSGTYPE_ERROR: | |
OutputLog(LogDescription::Error, Widen(pMsg->section), L" (", pMsg->row, L", ", pMsg->col, L") : ERR : ", Widen(pMsg->message)); | |
break; | |
case asMSGTYPE_WARNING: | |
OutputLog(LogDescription::Error, Widen(pMsg->section), L" (", pMsg->row, L", ", pMsg->col, L") : WARN : ", Widen(pMsg->message)); | |
break; | |
case asMSGTYPE_INFORMATION: | |
OutputLog(LogDescription::Error, Widen(pMsg->section), L" (", pMsg->row, L", ", pMsg->col, L") : INFO : ", Widen(pMsg->message)); | |
break; | |
default: assert(false); | |
} | |
} | |
ScriptContext ScriptEngine::CreateContextFromScript(const FilePath& path) | |
{ | |
ScriptContext cont(this); | |
cont.MakeFromScript(path); | |
return cont; | |
} | |
void ScriptEngine::StartModule(const String& moduleName) | |
{ | |
this->_assertInvariantContracts(); | |
this->pCurrentModule = this->pEngineInterface->GetModule(moduleName.narrow().c_str(), asGM_ALWAYS_CREATE); | |
assert(this->pCurrentModule != nullptr); | |
} | |
void ScriptEngine::SelectModule(const String& moduleName) | |
{ | |
this->_assertInvariantContracts(); | |
this->pCurrentModule = this->pEngineInterface->GetModule(moduleName.narrow().c_str()); | |
assert(this->pCurrentModule != nullptr); | |
} | |
void ScriptEngine::AddScript(const FilePath& path) | |
{ | |
this->_assertInvariantContracts(); | |
this->_ensureModuleStarted(); | |
auto reader = TextReader(path); | |
auto scriptData = reader.readContents(); | |
reader.close(); | |
this->pCurrentModule->AddScriptSection(path.narrow().c_str(), scriptData.narrow().c_str()); | |
} | |
void ScriptEngine::BuildModule() | |
{ | |
this->_assertInvariantContracts(); | |
this->_ensureModuleStarted(); | |
auto r = this->pCurrentModule->Build(); | |
if (r < 0) | |
{ | |
throw std::exception("Compilation Failed"); | |
} | |
} | |
void ScriptEngine::StartExecution(const String& signature) | |
{ | |
this->_assertInvariantContracts(); | |
this->_ensureModuleStarted(); | |
auto pEntryPointFunc = this->pCurrentModule->GetFunctionByDecl(signature.narrow().c_str()); | |
if (pEntryPointFunc == nullptr) | |
{ | |
throw std::exception(Format(L"Entry-point ", signature, L" is not found").narrow().c_str()); | |
} | |
this->pRuntimeContext = this->pEngineInterface->CreateContext(); | |
assert(this->pRuntimeContext != nullptr); | |
this->pRuntimeContext->Prepare(pEntryPointFunc); | |
auto r = this->pRuntimeContext->Execute(); | |
if (r != asEXECUTION_FINISHED) | |
{ | |
// not finished? | |
if (r == asEXECUTION_EXCEPTION) | |
{ | |
// error exit | |
throw std::exception(Format(L"AngelScript Exception: ", Widen(this->pRuntimeContext->GetExceptionString())).narrow().c_str()); | |
} | |
} | |
} | |
void ScriptEngine::NativeFunctions::print(const std::string& str) | |
{ | |
OutputLog(LogDescription::App, L"Script Output: ", Widen(str)); | |
} | |
void ScriptEngine::NativeFunctions::update() | |
{ | |
auto ctx = asGetActiveContext(); | |
if (ctx != nullptr) | |
{ | |
ctx->Suspend(); | |
} | |
} | |
bool ScriptEngine::NativeFunctions::getkey(int asc_keycode) | |
{ | |
return Key((wchar)asc_keycode).pressed; // 押された瞬間の判定はスクリプト側でやってもらうことにした | |
} | |
double ScriptEngine::NativeFunctions::floor(double v){ return Math::Floor(v); } | |
double ScriptEngine::NativeFunctions::abs(double v){ return Math::Abs(v); } | |
double ScriptEngine::NativeFunctions::sign(double v){ return Math::Sign(v); } | |
bool ScriptEngine::NativeFunctions::leftKey(){ return Input::KeyLeft.pressed; } | |
bool ScriptEngine::NativeFunctions::rightKey(){ return Input::KeyRight.pressed; } | |
ScriptContext::ScriptContext(ScriptEngine* e) : engine_ref(e) | |
{ | |
} | |
ScriptContext::~ScriptContext() | |
{ | |
if (this->pContext != nullptr) | |
{ | |
this->pContext->Release(); | |
this->pContext = nullptr; | |
} | |
if (this->pModule != nullptr) | |
{ | |
this->pModule = nullptr; | |
} | |
} | |
void ScriptContext::MakeFromScript(const FilePath& path) | |
{ | |
this->engine_ref->_assertInvariantContracts(); | |
auto module_name = FileSystem::BaseName(path); | |
// Make Module | |
this->pModule = this->engine_ref->pEngineInterface->GetModule(module_name.narrow().c_str(), asGM_ALWAYS_CREATE); | |
assert(this->pModule != nullptr); | |
// Load Script | |
auto reader = TextReader(path); | |
auto scriptData = reader.readContents(); | |
reader.close(); | |
this->pModule->AddScriptSection(path.narrow().c_str(), scriptData.narrow().c_str()); | |
// Build Module | |
this->pModule->Build(); | |
// Prepare Entry point | |
this->Reenter(); | |
} | |
void ScriptContext::_ensureModuleInitialized() | |
{ | |
// invariant contract | |
assert(this->pModule != nullptr && this->pContext != nullptr); | |
} | |
void ScriptContext::Reenter() | |
{ | |
// Remake Context | |
if (this->pContext != nullptr) | |
{ | |
this->pContext->Release(); | |
this->pContext = nullptr; | |
} | |
this->pContext = this->engine_ref->pEngineInterface->CreateContext(); | |
assert(this->pContext != nullptr); | |
auto pFunction = this->pModule->GetFunctionByDecl("void main()"); | |
if (pFunction == nullptr) | |
{ | |
throw std::exception(Format(L"Method \"void main()\" is not defined.").narrow().c_str()); | |
} | |
this->pContext->Prepare(pFunction); | |
this->execTerminated = false; | |
} | |
void ScriptContext::Execute() | |
{ | |
this->_ensureModuleInitialized(); | |
if (this->execTerminated) return; | |
auto r = this->pContext->Execute(); | |
if (r != asEXECUTION_SUSPENDED) | |
{ | |
// end execution | |
this->execTerminated = true; | |
} | |
} | |
int ScriptContext::GetGlobalInt(const String& name) | |
{ | |
this->_ensureModuleInitialized(); | |
auto idx = this->pModule->GetGlobalVarIndexByDecl(Format(L"int ", name).narrow().c_str()); | |
if (idx < 0) throw std::exception(Format(L"Global Variable ", name, L" not found").narrow().c_str()); | |
auto ptr = this->pModule->GetAddressOfGlobalVar(idx); | |
assert(ptr != nullptr); | |
return *(int*)ptr; | |
} | |
double ScriptContext::GetGlobalDouble(const String& name) | |
{ | |
this->_ensureModuleInitialized(); | |
auto idx = this->pModule->GetGlobalVarIndexByDecl(Format(L"double ", name).narrow().c_str()); | |
if (idx < 0) throw std::exception(Format(L"Global Variable ", name, L" not found").narrow().c_str()); | |
auto ptr = this->pModule->GetAddressOfGlobalVar(idx); | |
assert(ptr != nullptr); | |
return *(double*)ptr; | |
} | |
Vec2 ScriptContext::GetGlobalDoubleVec2(const String& x, const String& y) | |
{ | |
return Vec2(this->GetGlobalDouble(x), this->GetGlobalDouble(y)); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
// place AngelScript include files into "as" directory | |
// and place add-ons folder into "as/addons" directory | |
#include <Siv3D.hpp> | |
#include "as/include/angelscript.h" | |
class ScriptEngine | |
{ | |
asIScriptEngine* pEngineInterface = nullptr; | |
asIScriptModule* pCurrentModule = nullptr; | |
asIScriptContext* pRuntimeContext = nullptr; | |
struct GlobalProperties | |
{ | |
int frameCount; | |
}; | |
GlobalProperties _gp; | |
public: | |
friend class ScriptContext; | |
ScriptEngine(); | |
virtual ~ScriptEngine(); | |
ScriptContext CreateContextFromScript(const FilePath& path); | |
void StartModule(const String& moduleName); | |
void SelectModule(const String& moduleName); | |
void AddScript(const FilePath& path); | |
void BuildModule(); | |
void StartExecution(const String& signature); | |
void setFrameCount(int fc){ this->_gp.frameCount = fc; } | |
template <typename FuncT, typename ObjectT> void RegisterMethod(const String& signature, const FuncT& mtd, ObjectT& object) | |
{ | |
this->pEngineInterface->RegisterGlobalFunction(signature.narrow().c_str(), asSMethodPtr<sizeof(FuncT)>::Convert(mtd), asCALL_THISCALL_ASGLOBAL, (void*)&object); | |
} | |
template <typename FuncT, typename ObjectT> void RegisterMethod(const String& signature, const FuncT& mtd, ObjectT* object) | |
{ | |
this->pEngineInterface->RegisterGlobalFunction(signature.narrow().c_str(), asSMethodPtr<sizeof(FuncT)>::Convert(mtd), asCALL_THISCALL_ASGLOBAL, object); | |
} | |
template <typename FuncT> void RegisterFunction(const String& signature, const FuncT& mtd) | |
{ | |
this->pEngineInterface->RegisterGlobalFunction(signature.narrow().c_str(), asFUNCTION(mtd), asCALL_CDECL); | |
} | |
void RegisterProperty(const String& signature, void* ptr); | |
private: | |
// Contracts | |
void _assertInvariantContracts(); | |
void _ensureModuleStarted(); | |
// Private Subprocedures | |
void RegisterNativeFunctions(); | |
void RegisterGlobalProperties(); | |
static void engineCallback(const asSMessageInfo* pMsg, void* pParam); | |
struct NativeFunctions | |
{ | |
static void print(const std::string& str); | |
static void update(); | |
static bool getkey(int asc_keycode); | |
static double floor(double v); | |
static double abs(double v); | |
static double sign(double v); | |
static bool leftKey(); | |
static bool rightKey(); | |
}; | |
}; | |
class ScriptContext | |
{ | |
ScriptEngine* engine_ref; | |
asIScriptModule* pModule = nullptr; | |
asIScriptContext* pContext = nullptr; | |
bool execTerminated = false; | |
public: | |
ScriptContext(ScriptContext&& c) | |
{ | |
this->pModule = c.pModule; | |
this->pContext = c.pContext; | |
this->engine_ref = c.engine_ref; | |
this->pContext->AddRef(); | |
} | |
ScriptContext(ScriptEngine*); | |
~ScriptContext(); | |
void MakeFromScript(const FilePath& path); | |
void Execute(); | |
void Reenter(); | |
int GetGlobalInt(const String& name); //結局使わなかったやつ | |
double GetGlobalDouble(const String& name); // 実数で変数の中身を取得する | |
Vec2 GetGlobalDoubleVec2(const String& x, const String& y); // 二変数をベクトルに入れて返す(x, yとか) | |
private: | |
void _ensureModuleInitialized(); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment