Skip to content

Commit

Permalink
Merge pull request #2862 from Ghabry/emscripten2
Browse files Browse the repository at this point in the history
Emscripten: Add Api and remove Asynchify
  • Loading branch information
fdelapena authored Dec 20, 2023
2 parents dc933f5 + 64e96d1 commit 77d97bb
Show file tree
Hide file tree
Showing 24 changed files with 431 additions and 52 deletions.
20 changes: 15 additions & 5 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1124,7 +1124,11 @@ if(${PLAYER_BUILD_EXECUTABLE} AND ${PLAYER_TARGET_PLATFORM} MATCHES "^SDL(1|2)$"
set(PLAYER_JS_POSTJS "${CMAKE_CURRENT_SOURCE_DIR}/resources/emscripten/emscripten-post.js")
set(PLAYER_JS_SHELL "${CMAKE_CURRENT_SOURCE_DIR}/resources/emscripten/emscripten-shell.html")

set_property(TARGET ${EXE_NAME} PROPERTY LINK_FLAGS "-s ALLOW_MEMORY_GROWTH -s EXIT_RUNTIME -s ASYNCIFY -s ASYNCIFY_IGNORE_INDIRECT -s MINIFY_HTML=0 --pre-js ${PLAYER_JS_PREJS} --post-js ${PLAYER_JS_POSTJS} -sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE=['$autoResumeAudioContext','$dynCall']")
set_property(TARGET ${EXE_NAME} PROPERTY LINK_FLAGS
"-sALLOW_MEMORY_GROWTH -sMINIFY_HTML=0 -sMODULARIZE -sEXPORT_NAME=createEasyRpgPlayer \
-sEXIT_RUNTIME --bind --pre-js ${PLAYER_JS_PREJS} --post-js ${PLAYER_JS_POSTJS} \
-sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE=['$autoResumeAudioContext','$dynCall'] \
-sEXPORTED_FUNCTIONS=_main,_malloc,_free")
set_source_files_properties("src/platform/sdl/main.cpp" PROPERTIES
OBJECT_DEPENDS "${PLAYER_JS_PREJS};${PLAYER_JS_POSTJS};${PLAYER_JS_SHELL}")

Expand All @@ -1133,6 +1137,13 @@ if(${PLAYER_BUILD_EXECUTABLE} AND ${PLAYER_TARGET_PLATFORM} MATCHES "^SDL(1|2)$"
set_property(TARGET ${EXE_NAME} APPEND_STRING PROPERTY LINK_FLAGS " --shell-file ${PLAYER_JS_SHELL}")
endif()

target_compile_options(${PROJECT_NAME} PUBLIC -fno-exceptions)

target_sources(${PROJECT_NAME} PRIVATE
src/platform/emscripten/clock.h
src/platform/emscripten/interface.cpp
src/platform/emscripten/interface.h)

target_link_libraries(${EXE_NAME} "idbfs.js")

set_target_properties(${EXE_NAME} PROPERTIES OUTPUT_NAME "${PLAYER_JS_OUTPUT_NAME}")
Expand Down Expand Up @@ -1413,7 +1424,10 @@ if(PLAYER_ENABLE_TESTS)
if(CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
target_compile_definitions(test_runner_player PUBLIC EP_NATIVE_TEST_PATH=\"${CMAKE_CURRENT_SOURCE_DIR}/tests/assets\")
target_compile_definitions(test_runner_player PUBLIC EP_TEST_PATH=\"/assets\")
target_compile_definitions(test_runner_player PUBLIC DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS=1)

target_link_libraries(test_runner_player "nodefs.js")
set_property(TARGET test_runner_player PROPERTY LINK_FLAGS "-sALLOW_MEMORY_GROWTH --bind")
else()
target_compile_definitions(test_runner_player PUBLIC EP_TEST_PATH=\"${CMAKE_CURRENT_SOURCE_DIR}/tests/assets\")
endif()
Expand All @@ -1427,10 +1441,6 @@ if(PLAYER_ENABLE_TESTS)
endif()
add_dependencies(check_player test_runner_player)
add_dependencies(check check_player)

if(CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
set_property(TARGET test_runner_player APPEND_STRING PROPERTY LINK_FLAGS "-s ALLOW_MEMORY_GROWTH")
endif()
endif()

# Instrumentation framework
Expand Down
3 changes: 3 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,9 @@ EXTRA_DIST += \
src/platform/android/filesystem_saf.h \
src/platform/android/org_easyrpg_player_player_EasyRpgPlayerActivity.cpp \
src/platform/android/org_easyrpg_player_player_EasyRpgPlayerActivity.h \
src/platform/emscripten/clock.h \
src/platform/emscripten/interface.cpp \
src/platform/emscripten/interface.h \
src/platform/emscripten/main.cpp \
src/platform/libretro/audio.cpp \
src/platform/libretro/audio.h \
Expand Down
45 changes: 40 additions & 5 deletions resources/emscripten/emscripten-post.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,50 @@
FS.mkdir("easyrpg");
FS.chdir("easyrpg");

if (Module.EASYRPG_GAME.length > 0) {
FS.mkdir(Module.EASYRPG_GAME);
FS.chdir(Module.EASYRPG_GAME);
if (Module.game.length > 0) {
FS.mkdir(Module.game);
FS.chdir(Module.game);
}

// Use IDBFS for save file storage when the filesystem was not
// overwritten by a custom emscripten shell file
if (typeof Module.EASYRPG_FS === "undefined") {
Module.EASYRPG_FS = IDBFS;
if (Module.saveFs === undefined) {
Module.saveFs = IDBFS;
}

Module.initApi = function() {
Module.api_private.download_js = function(buffer, size, filename) {
const blob = new Blob([Module.HEAPU8.slice(buffer, buffer + size)]);
const link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = UTF8ToString(filename);
link.click();
link.remove();
}

Module.api_private.uploadSavegame_js = function(slot) {
let saveFile = document.getElementById('easyrpg_saveFile');
if (saveFile == null) {
saveFile = document.createElement('input');
saveFile.type = 'file';
saveFile.id = 'easyrpg_saveFile';
saveFile.style.display = 'none';
saveFile.addEventListener('change', function(evt) {
const save = evt.target.files[0];
const reader = new FileReader();
reader.onload = function (file) {
const result = new Uint8Array(file.currentTarget.result);
var buf = Module._malloc(result.length);
Module.HEAPU8.set(result, buf);
Module.api_private.uploadSavegameStep2(slot, buf, result.length);
Module._free(buf);
Module.api.refreshScene();
};
reader.readAsArrayBuffer(save);
});
}
saveFile.click();
}
}

// Display the nice end message forever
Expand Down
15 changes: 10 additions & 5 deletions resources/emscripten/emscripten-pre.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// Note: The `Module` context is already initialized as an
// empty object by emscripten even before the pre script
Module = {
EASYRPG_GAME: "",

Module = { ...Module,
preRun: [onPreRun],
postRun: [],

Expand Down Expand Up @@ -67,7 +65,8 @@ function parseArgs () {

// Filesystem is not ready when processing arguments, store path to game
if (tmp[0] === "game" && tmp.length > 1) {
Module.EASYRPG_GAME = tmp[1].toLowerCase();
Module.game = tmp[1].toLowerCase();
continue;
}

result.push("--" + tmp[0]);
Expand All @@ -91,7 +90,7 @@ function parseArgs () {
function onPreRun () {
// Retrieve save directory from persistent storage before using it
FS.mkdir("Save");
FS.mount(Module.EASYRPG_FS, {}, 'Save');
FS.mount(Module.saveFs, {}, 'Save');

// For preserving the configuration. Shared across website
FS.mkdir("/home/web_user/.config");
Expand All @@ -103,6 +102,12 @@ function onPreRun () {
Module.setStatus('Downloading...');
Module.arguments = ["easyrpg-player", ...parseArgs()];

if (Module.game === undefined) {
Module.game = "";
} else {
Module.arguments.push("--game", Module.game);
}

// Catch all errors occuring inside the window
window.addEventListener('error', (event) => {
// workaround chrome bug: See https://github.com/EasyRPG/Player/issues/2806
Expand Down
15 changes: 15 additions & 0 deletions resources/emscripten/emscripten-shell.html
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,23 @@
const keys = new Map();
const keysDown = new Map();
const canvas = document.getElementById('canvas');
let easyrpgPlayer;
let lastTouchedId;

// Launch the Player and configure it
window.addEventListener('load', (event) => {
createEasyRpgPlayer({
game: undefined,
saveFs: undefined
}).then(function(Module) {
// Module is ready
easyrpgPlayer = Module;
easyrpgPlayer.initApi();

// Custom code here
});
});

// Make EasyRPG player embeddable
canvas.addEventListener('mouseenter', () => window.focus());
canvas.addEventListener('click', () => window.focus());
Expand Down
2 changes: 1 addition & 1 deletion src/bitmap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ Bitmap::Bitmap(Bitmap const& source, Rect const& src_rect, bool transparent) {
Blit(0, 0, source, src_rect, Opacity::Opaque());
}

bool Bitmap::WritePNG(Filesystem_Stream::OutputStream& os) const {
bool Bitmap::WritePNG(std::ostream& os) const {
size_t const width = GetWidth(), height = GetHeight();
size_t const stride = width * 4;

Expand Down
2 changes: 1 addition & 1 deletion src/bitmap.h
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ class Bitmap {
* @param os output stream that PNG will be output.
* @return true if success, otherwise false.
*/
bool WritePNG(Filesystem_Stream::OutputStream&) const;
bool WritePNG(std::ostream& os) const;

/**
* Gets the background color
Expand Down
3 changes: 2 additions & 1 deletion src/image_png.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <cstring>
#include <csetjmp>
#include <vector>
#include <fstream>

#include "output.h"
#include "image_png.h"
Expand Down Expand Up @@ -250,7 +251,7 @@ static void flush_stream(png_structp out_ptr) {
reinterpret_cast<Filesystem_Stream::OutputStream*>(png_get_io_ptr(out_ptr))->flush();
}

bool ImagePNG::WritePNG(Filesystem_Stream::OutputStream& os, uint32_t width, uint32_t height, uint32_t* data) {
bool ImagePNG::WritePNG(std::ostream& os, uint32_t width, uint32_t height, uint32_t* data) {
png_structp write = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!write) {
Output::Warning("Bitmap::WritePNG: error in png_create_write");
Expand Down
2 changes: 1 addition & 1 deletion src/image_png.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
namespace ImagePNG {
bool ReadPNG(const void* buffer, bool transparent, int& width, int& height, void*& pixels);
bool ReadPNG(Filesystem_Stream::InputStream& is, bool transparent, int& width, int& height, void*& pixels);
bool WritePNG(Filesystem_Stream::OutputStream& os, uint32_t width, uint32_t height, uint32_t* data);
bool WritePNG(std::ostream& os, uint32_t width, uint32_t height, uint32_t* data);
}

#endif
8 changes: 7 additions & 1 deletion src/output.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
# include <android/log.h>
#elif defined(EMSCRIPTEN)
# include <emscripten.h>
# include "platform/emscripten/interface.h"
#elif defined(__vita__)
# include <psp2/kernel/processmgr.h>
#endif
Expand Down Expand Up @@ -275,12 +276,17 @@ void Output::Quit() {
}

bool Output::TakeScreenshot() {
#ifdef EMSCRIPTEN
Emscripten_Interface::TakeScreenshot();
return true;
#else
int index = 0;
std::string p;
do {
p = "screenshot_" + std::to_string(index++) + ".png";
} while(FileFinder::Save().Exists(p));
return TakeScreenshot(p);
#endif
}

bool Output::TakeScreenshot(StringView file) {
Expand All @@ -293,7 +299,7 @@ bool Output::TakeScreenshot(StringView file) {
return false;
}

bool Output::TakeScreenshot(Filesystem_Stream::OutputStream& os) {
bool Output::TakeScreenshot(std::ostream& os) {
return DisplayUi->GetDisplaySurface()->WritePNG(os);
}

Expand Down
2 changes: 1 addition & 1 deletion src/output.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ namespace Output {
* @param os output stream that PNG will be stored.
* @return true if success, otherwise false.
*/
bool TakeScreenshot(Filesystem_Stream::OutputStream& os);
bool TakeScreenshot(std::ostream& os);

/**
* Shows/Hides the output log overlay.
Expand Down
3 changes: 3 additions & 0 deletions src/platform/clock.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ using Platform_Clock = NxClock;
#elif defined(__vita__)
#include "platform/psvita/clock.h"
using Platform_Clock = Psp2Clock;
#elif defined(EMSCRIPTEN)
#include "platform/emscripten/clock.h"
using Platform_Clock = EmscriptenClock;
#elif defined(USE_LIBRETRO)
// Only use libretro clock on platforms with no custom clock
#include "platform/libretro/clock.h"
Expand Down
56 changes: 56 additions & 0 deletions src/platform/emscripten/clock.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* This file is part of EasyRPG Player.
*
* EasyRPG Player is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EasyRPG Player is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EasyRPG Player. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef EP_EMSCRIPTEN_CLOCK_H
#define EP_EMSCRIPTEN_CLOCK_H

#include <chrono>

struct EmscriptenClock {
using clock = std::chrono::steady_clock;

using rep = clock::rep;
using period = clock::period;
using duration = clock::duration;
using time_point = clock::time_point;

static constexpr bool is_steady = clock::is_steady;

/** Get current time */
static time_point now();

/** Sleep for the specified duration */
template <typename R, typename P>
static void SleepFor(std::chrono::duration<R,P> dt);

static constexpr const char* Name();
};

inline EmscriptenClock::time_point EmscriptenClock::now() {
return clock::now();
}

template <typename R, typename P>
inline void EmscriptenClock::SleepFor(std::chrono::duration<R,P> dt) {
// Browser handles sleep
}

constexpr const char* EmscriptenClock::Name() {
return "Emscripten";
}

#endif
Loading

0 comments on commit 77d97bb

Please sign in to comment.