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; | |
| } |