diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0e60427772d..4fedc4cf515 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1042,7 +1042,7 @@ if(${PLAYER_BUILD_EXECUTABLE} AND ${PLAYER_TARGET_PLATFORM} STREQUAL "SDL2")
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 ASYNCIFY -s ASYNCIFY_IGNORE_INDIRECT -s MINIFY_HTML=0 -s MODULARIZE -s EXPORT_NAME='createEasyRpgPlayer' --bind --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 "-s ALLOW_MEMORY_GROWTH -s MINIFY_HTML=0 -s MODULARIZE -s EXIT_RUNTIME -s EXPORT_NAME='createEasyRpgPlayer' --bind --pre-js ${PLAYER_JS_PREJS} --post-js ${PLAYER_JS_POSTJS} -sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE=['$autoResumeAudioContext','$dynCall']")
set_source_files_properties("src/platform/sdl/main.cpp" PROPERTIES
OBJECT_DEPENDS "${PLAYER_JS_PREJS};${PLAYER_JS_POSTJS};${PLAYER_JS_SHELL}")
@@ -1054,6 +1054,7 @@ if(${PLAYER_BUILD_EXECUTABLE} AND ${PLAYER_TARGET_PLATFORM} STREQUAL "SDL2")
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)
diff --git a/Makefile.am b/Makefile.am
index 7b638936e79..33e0c8eaf24 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -480,6 +480,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/libretro/audio.cpp \
src/platform/libretro/audio.h \
src/platform/libretro/clock.cpp \
diff --git a/resources/emscripten/emscripten-post.js b/resources/emscripten/emscripten-post.js
index f7c79754592..53a90053d77 100644
--- a/resources/emscripten/emscripten-post.js
+++ b/resources/emscripten/emscripten-post.js
@@ -40,10 +40,9 @@ Module.initApi = function() {
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).then(function() {
- Module._free(buf);
- Module.api.refreshScene();
- });
+ Module.api_private.uploadSavegameStep2(slot, buf, result.length);
+ Module._free(buf);
+ Module.api.refreshScene();
};
reader.readAsArrayBuffer(save);
});
diff --git a/resources/emscripten/emscripten-pre.js b/resources/emscripten/emscripten-pre.js
index fa337511a19..3ffbd3a9c02 100644
--- a/resources/emscripten/emscripten-pre.js
+++ b/resources/emscripten/emscripten-pre.js
@@ -1,6 +1,7 @@
// Note: The `Module` context is already initialized as an
// empty object by emscripten even before the pre script
-Module = { ...Module,
+// Do not replace Object.assign with "...Module". Breaks the Acorn optimizer
+Module = Object.assign({}, Module, {
preRun: [],
postRun: [],
@@ -42,7 +43,7 @@ Module = { ...Module,
Module.totalDependencies = Math.max(Module.totalDependencies, left);
Module.setStatus(left ? `Preparing... (${Module.totalDependencies - left}/${Module.totalDependencies})` : 'Downloading game data...');
}
-};
+});
/**
* Parses the current location query to setup a specific game
diff --git a/src/platform/clock.h b/src/platform/clock.h
index 2a43fa1b394..160523ec0dd 100644
--- a/src/platform/clock.h
+++ b/src/platform/clock.h
@@ -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"
diff --git a/src/platform/emscripten/clock.h b/src/platform/emscripten/clock.h
new file mode 100644
index 00000000000..3e787266cf3
--- /dev/null
+++ b/src/platform/emscripten/clock.h
@@ -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 .
+ */
+
+#ifndef EP_EMSCRIPTEN_CLOCK_H
+#define EP_EMSCRIPTEN_CLOCK_H
+
+#include
+
+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
+ static void SleepFor(std::chrono::duration dt);
+
+ static constexpr const char* Name();
+};
+
+inline EmscriptenClock::time_point EmscriptenClock::now() {
+ return clock::now();
+}
+
+template
+inline void EmscriptenClock::SleepFor(std::chrono::duration dt) {
+ // Browser handles sleep
+}
+
+constexpr const char* EmscriptenClock::Name() {
+ return "Emscripten";
+}
+
+#endif
diff --git a/src/platform/emscripten/interface.cpp b/src/platform/emscripten/interface.cpp
index 4de70bce8a3..ec9a154efea 100644
--- a/src/platform/emscripten/interface.cpp
+++ b/src/platform/emscripten/interface.cpp
@@ -58,7 +58,7 @@ bool Emscripten_Interface_Private::UploadSavegameStep2(int slot, int buffer_addr
auto fs = FileFinder::Save();
std::string name = Scene_Save::GetSaveFilename(fs, slot);
- std::istream is(new Filesystem_Stream::InputMemoryStreamBuf(lcf::Span(reinterpret_cast(buffer_addr), size)));
+ std::istream is(new Filesystem_Stream::InputMemoryStreamBufView(lcf::Span(reinterpret_cast(buffer_addr), size)));
if (!lcf::LSD_Reader::Load(is)) {
Output::Warning("Selected file is not a valid savegame");
diff --git a/src/player.cpp b/src/player.cpp
index 773cc6794d1..b3b57169e89 100644
--- a/src/player.cpp
+++ b/src/player.cpp
@@ -206,8 +206,11 @@ void Player::Run() {
Game_Clock::ResetFrame(Game_Clock::now());
// Main loop
+#ifdef EMSCRIPTEN
+ emscripten_set_main_loop(Player::MainLoop, 0, 0);
+#elif defined(USE_LIBRETRO)
// libretro invokes the MainLoop through a retro_run-callback
-#ifndef USE_LIBRETRO
+#else
while (Transition::instance().IsActive() || (Scene::instance && Scene::instance->type != Scene::Null)) {
MainLoop();
}
@@ -249,9 +252,6 @@ void Player::MainLoop() {
auto frame_limit = DisplayUi->GetFrameLimit();
if (frame_limit == Game_Clock::duration()) {
-#ifdef EMSCRIPTEN
- emscripten_sleep(0);
-#endif
return;
}
@@ -261,11 +261,6 @@ void Player::MainLoop() {
if (Game_Clock::now() < next) {
iframe.End();
Game_Clock::SleepFor(next - now);
- } else {
-#ifdef EMSCRIPTEN
- // Yield back to browser once per frame
- emscripten_sleep(0);
-#endif
}
}
@@ -381,6 +376,8 @@ int Player::GetFrames() {
void Player::Exit() {
Graphics::UpdateSceneCallback();
#ifdef EMSCRIPTEN
+ emscripten_cancel_main_loop();
+
BitmapRef surface = DisplayUi->GetDisplaySurface();
std::string message = "It's now safe to turn off\n your browser.";
Text::Draw(*surface, 84, DisplayUi->GetHeight() / 2 - 30, *Font::DefaultBitmapFont(), Color(221, 123, 64, 255), message);