diff --git a/.gitignore b/.gitignore index 1c85d0e..cbe2ac3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .idea cmake-build-* +example/dist diff --git a/CMakeLists.txt b/CMakeLists.txt index 6d4a3e8..f632b9f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,8 @@ cmake_minimum_required(VERSION 3.16) project(screepsxx CXX) -option(SCREEPSXX_ENABLE_PCH ON "Enable precompiled headers") +option(SCREEPSXX_ENABLE_PCH "Enable precompiled headers" ON) +option(SCREEPSXX_BUILD_EXAMPLE "Build example WASM application" OFF) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED TRUE) @@ -26,3 +27,7 @@ if (SCREEPSXX_ENABLE_PCH) ) endif() + +if (SCREEPSXX_BUILD_EXAMPLE) + add_subdirectory(example) +endif() diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt new file mode 100644 index 0000000..85af810 --- /dev/null +++ b/example/CMakeLists.txt @@ -0,0 +1,19 @@ +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s ENVIRONMENT=shell") + +add_executable(example loop.cpp) +target_link_libraries(example screepsxx) +target_link_options(example PUBLIC -sMODULARIZE=1 --no-entry --bind) + +# Collect all artifacts in 'dist' directory +add_custom_command(TARGET example POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/example.wasm ${CMAKE_CURRENT_SOURCE_DIR}/dist/my_module.wasm) +add_custom_command(TARGET example POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/example.js ${CMAKE_CURRENT_SOURCE_DIR}/dist/my_loader.js) +add_custom_command(TARGET example POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/js/main.js ${CMAKE_CURRENT_SOURCE_DIR}/dist/main.js) +add_custom_command(TARGET example POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/js/wasm_loader.js ${CMAKE_CURRENT_SOURCE_DIR}/dist/wasm_loader.js) + +# You can add post-build step to automatically upload artifacts to +# official Screeps server using Python script in 'tools' directory. +# To do so, uncomment following lines and set SCREEPS_TOKEN environment +# variable to your Screeps API token (https://docs.screeps.com/auth-tokens.html). +# +#find_package(Python COMPONENTS Interpreter REQUIRED) +#add_custom_command(TARGET example POST_BUILD COMMAND ${Python_EXECUTABLE} ${screepsxx_SOURCE_DIR}/tools/upload.py ${CMAKE_CURRENT_SOURCE_DIR}/dist $ENV{SCREEPS_TOKEN}) diff --git a/example/js/main.js b/example/js/main.js new file mode 100644 index 0000000..91a598c --- /dev/null +++ b/example/js/main.js @@ -0,0 +1,14 @@ +'use strict'; + +const wasm_loader = require('wasm_loader') + +var mod; +wasm_loader('my_loader', 'my_module').then((instance) => { + console.log("WASM module loaded."); + mod = instance; +}); + +module.exports.loop = function () { + if (mod !== undefined) + mod.loop(); +} diff --git a/example/js/wasm_loader.js b/example/js/wasm_loader.js new file mode 100644 index 0000000..d2a8fcc --- /dev/null +++ b/example/js/wasm_loader.js @@ -0,0 +1,24 @@ +'use strict'; + +module.exports = ((mod_js, mod_wasm, opts) => { + + const mod_file = require(mod_js); + const bin_file = require(mod_wasm); + + opts = opts || {}; + + opts.wasmBinary = bin_file; + opts.print = opts.print || ((text) => console.log(text)); + opts.printErr = opts.printErr || ((text) => console.log(`error: ${text}`)); + opts.onAbort = opts.onAbort || (() => console.log('WASM aborted!!!')); + + // == don't call main() + if (typeof opts.noInitialRun === "undefined") + opts.noInitialRun = true; + + // == don't terminate after returning from main() + if (typeof opts.noExitRuntime === "undefined") + opts.noExitRuntime = true; + + return mod_file(opts); +}); diff --git a/example/loop.cpp b/example/loop.cpp new file mode 100644 index 0000000..13d1690 --- /dev/null +++ b/example/loop.cpp @@ -0,0 +1,20 @@ +#include +#include + +#include +#include + +EMSCRIPTEN_KEEPALIVE +extern "C" void loop() +{ + Screeps::Context::update(); + + auto creeps = Screeps::Game.creeps(); + for (auto& creep : creeps) + creep.second.say("screepsxx"); +} + +EMSCRIPTEN_BINDINGS(loop) +{ + emscripten::function("loop", &loop); +} diff --git a/tools/upload.py b/tools/upload.py new file mode 100644 index 0000000..d960bf5 --- /dev/null +++ b/tools/upload.py @@ -0,0 +1,66 @@ +import os +import sys +import glob +import base64 + +import requests +import humanize + + +def get_file_list(directory): + return glob.glob(directory + '/*') + + +def read_file_to_json_string(filename): + file_ext = os.path.splitext(os.path.basename(filename))[1] + if file_ext == '.js': + with open(filename, 'r') as reader: + return reader.read() + else: + with open(filename, 'rb') as reader: + return {'binary': base64.b64encode(reader.read()).decode('utf-8')} + + +def upload_files(files, token, branch): + modules = {} + for file in files: + name = os.path.splitext(os.path.basename(file))[0] + content = read_file_to_json_string(file) + modules[name] = content + print('Read {} from {}'.format(humanize.naturalsize(os.path.getsize(file)), file)) + + request_body = { + 'branch': branch, + 'modules': modules + } + + headers = {'X-Token': token} + r = requests.post('https://screeps.com/api/user/code', headers=headers, json=request_body) + response = r.json() + if 'ok' in response: + print('done') + return 0 + elif 'error' in response: + print('failed: ' + response['error']) + return 1 + else: + print('unexpected response: ' + r.text) + return 1 + + +argc = len(sys.argv) +if argc != 3 and argc != 4: + print('Usage: upload.py [branch=default]') + exit(1) + +target_directory = sys.argv[1] +token = sys.argv[2] + +if argc == 4: + branch = sys.argv[3] +else: + branch = 'default' + +files = get_file_list(target_directory) +status = upload_files(files, token, branch) +exit(status)