this is meant to live in the samples/wasm
directory of angelscript
to build/test
requirements on NixOS
- python3
- cmake
- emscripten
- nodejs
emcmake cmake -B build -S .
make
node ../bin/angelscript-wasm.js
build | |
bin |
cmake_minimum_required(VERSION 2.6) | |
project(angelscript-wasm) | |
add_executable( | |
angelscript-wasm | |
${CMAKE_CURRENT_SOURCE_DIR}/../../add_on/scriptstdstring/scriptstdstring.cpp | |
${CMAKE_CURRENT_SOURCE_DIR}/main.cpp | |
) | |
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../angelscript/projects/cmake angelscript) | |
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../angelscript/include) | |
target_link_libraries(angelscript-wasm ${ANGELSCRIPT_LIBRARY_NAME}) | |
set_target_properties(angelscript-wasm PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin) |
#include <iostream> // cout | |
#include <assert.h> // assert() | |
#include <string.h> // strstr() | |
#include <angelscript.h> | |
#include "../../add_on/scriptstdstring/scriptstdstring.h" | |
using namespace std; | |
void MessageCallback(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); | |
} | |
int CompileScript(asIScriptEngine *engine, string &script) { | |
// Add the script sections that will be compiled into executable code. | |
// If we want to combine more than one file into the same script, then | |
// we can call AddScriptSection() several times for the same module and | |
// the script engine will treat them all as if they were one. The script | |
// section name, will allow us to localize any errors in the script code. | |
asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE); | |
int r = mod->AddScriptSection("script", &script[0], script.size()); | |
if( r < 0 ) | |
{ | |
cout << "AddScriptSection() failed" << endl; | |
return -1; | |
} | |
// Compile the script. If there are any compiler messages they will | |
// be written to the message stream that we set right after creating the | |
// script engine. If there are no errors, and no warnings, nothing will | |
// be written to the stream. | |
r = mod->Build(); | |
if( r < 0 ) | |
{ | |
cout << "Build() failed" << endl; | |
return -1; | |
} | |
// The engine doesn't keep a copy of the script sections after Build() has | |
// returned. So if the script needs to be recompiled, then all the script | |
// sections must be added again. | |
// If we want to have several scripts executing at different times but | |
// that have no direct relation with each other, then we can compile them | |
// into separate script modules. Each module use their own namespace and | |
// scope, so function names, and global variables will not conflict with | |
// each other. | |
return 0; | |
} | |
// Function implementation with generic script interface | |
void PrintString_Generic(asIScriptGeneric *gen) | |
{ | |
string *str = (string*)gen->GetArgAddress(0); | |
cout << *str; | |
} | |
void ConfigureEngine(asIScriptEngine *engine) | |
{ | |
RegisterStdString(engine); | |
int r = engine->RegisterGlobalFunction("void Print(string &in)", asFUNCTION(PrintString_Generic), asCALL_GENERIC); assert( r >= 0 ); | |
} | |
int RunApplication() | |
{ | |
int r; | |
// Create the script engine | |
asIScriptEngine *engine = asCreateScriptEngine(); | |
if( engine == 0 ) | |
{ | |
cout << "Failed to create script engine." << endl; | |
return -1; | |
} | |
// The script compiler will write any compiler messages to the callback. | |
engine->SetMessageCallback(asFUNCTION(MessageCallback), 0, asCALL_CDECL); | |
// Configure the script engine with all the functions, | |
// and variables that the script should be able to use. | |
ConfigureEngine(engine); | |
string script = "float calc(float a, float b) {" | |
" Print(\"Received: \" + a + \", \" + b + \" \\n\");" | |
" return a * b;" | |
"}"; | |
// Compile the script code | |
r = CompileScript(engine, script); | |
if( r < 0 ) | |
{ | |
engine->Release(); | |
return -1; | |
} | |
// Create a context that will execute the script. | |
asIScriptContext *ctx = engine->CreateContext(); | |
if( ctx == 0 ) | |
{ | |
cout << "Failed to create the context." << endl; | |
engine->Release(); | |
return -1; | |
} | |
// Find the function for the function we want to execute. | |
asIScriptFunction *func = engine->GetModule(0)->GetFunctionByDecl("float calc(float, float)"); | |
if( func == 0 ) | |
{ | |
cout << "The function 'float calc(float, float)' was not found." << endl; | |
ctx->Release(); | |
engine->Release(); | |
return -1; | |
} | |
// Prepare the script context with the function we wish to execute. Prepare() | |
// must be called on the context before each new script function that will be | |
// executed. Note, that if you intend to execute the same function several | |
// times, it might be a good idea to store the function returned by | |
// GetFunctionByDecl(), so that this relatively slow call can be skipped. | |
r = ctx->Prepare(func); | |
if( r < 0 ) | |
{ | |
cout << "Failed to prepare the context." << endl; | |
ctx->Release(); | |
engine->Release(); | |
return -1; | |
} | |
// Now we need to pass the parameters to the script function. | |
ctx->SetArgFloat(0, 3.14159265359f); | |
ctx->SetArgFloat(1, 2.71828182846f); | |
// Execute the function | |
cout << "Executing the script." << endl; | |
cout << "---" << endl; | |
r = ctx->Execute(); | |
cout << "---" << endl; | |
if( r != asEXECUTION_FINISHED ) | |
{ | |
// The execution didn't finish as we had planned. Determine why. | |
if( r == asEXECUTION_ABORTED ) | |
cout << "The script was aborted before it could finish. Probably it timed out." << endl; | |
else if( r == asEXECUTION_EXCEPTION ) | |
{ | |
cout << "The script ended with an exception." << endl; | |
// Write some information about the script exception | |
asIScriptFunction *func = ctx->GetExceptionFunction(); | |
cout << "func: " << func->GetDeclaration() << endl; | |
cout << "modl: " << func->GetModuleName() << endl; | |
cout << "sect: " << func->GetScriptSectionName() << endl; | |
cout << "line: " << ctx->GetExceptionLineNumber() << endl; | |
cout << "desc: " << ctx->GetExceptionString() << endl; | |
} | |
else | |
cout << "The script ended for some unforeseen reason (" << r << ")." << endl; | |
} | |
else | |
{ | |
// Retrieve the return value from the context | |
float returnValue = ctx->GetReturnFloat(); | |
cout << "The script function returned: " << returnValue << endl; | |
} | |
// We must release the contexts when no longer using them | |
ctx->Release(); | |
// Shut down the engine | |
engine->ShutDownAndRelease(); | |
return 0; | |
} | |
int main(int argc, char **argv) | |
{ | |
RunApplication(); | |
return 0; | |
} |