From b0dd56659d79417cc522874dd3c5dcc2db28bb75 Mon Sep 17 00:00:00 2001 From: HoofedEar <1261392+HoofedEar@users.noreply.github.com> Date: Wed, 3 Dec 2025 17:57:26 -0800 Subject: [PATCH] a working baseline for Emscripten builds --- 3rdParty/Lua/CMakeLists.txt | 4 ++++ CMake/emscripten_pre.js | 45 +++++++++++++++++++++++++++++++++++++ CMakeLists.txt | 18 +++++++++++---- Source/diablo.cpp | 4 ++++ Source/engine/dx.cpp | 9 ++++++++ 5 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 CMake/emscripten_pre.js diff --git a/3rdParty/Lua/CMakeLists.txt b/3rdParty/Lua/CMakeLists.txt index 04147a54d..a031ffca4 100644 --- a/3rdParty/Lua/CMakeLists.txt +++ b/3rdParty/Lua/CMakeLists.txt @@ -26,6 +26,10 @@ if(CMAKE_SYSTEM_NAME MATCHES "Darwin" AND DARWIN_MAJOR_VERSION VERSION_EQUAL 8) # localtime_r gmtime_r find_package(MacportsLegacySupport REQUIRED) target_link_libraries(lua_static PRIVATE MacportsLegacySupport::MacportsLegacySupport) +elseif(EMSCRIPTEN) + # Enable pthread support for Emscripten to match SDL2's USE_PTHREADS=1 + target_compile_options(lua_static PUBLIC -pthread) + target_link_options(lua_static PUBLIC -pthread) elseif(TARGET_PLATFORM STREQUAL "dos") target_compile_definitions(lua_static PUBLIC -DLUA_USE_C89) elseif(ANDROID AND ("${ANDROID_ABI}" STREQUAL "armeabi-v7a" OR "${ANDROID_ABI}" STREQUAL "x86")) diff --git a/CMake/emscripten_pre.js b/CMake/emscripten_pre.js new file mode 100644 index 000000000..ba9cf6177 --- /dev/null +++ b/CMake/emscripten_pre.js @@ -0,0 +1,45 @@ +// Pre-load MPQ files from the server directory into Emscripten virtual filesystem +Module['preRun'] = Module['preRun'] || []; +Module['preRun'].push(function() { + // List of MPQ files to try loading (in priority order) + var mpqFiles = [ + 'diabdat.mpq', + 'DIABDAT.MPQ', + 'spawn.mpq', + 'hellfire.mpq', + 'hfmonk.mpq', + 'hfmusic.mpq', + 'hfvoice.mpq', + 'hfbard.mpq', + 'hfbarb.mpq' + ]; + + // Create a promise-based loading system + var loadPromises = mpqFiles.map(function(filename) { + return new Promise(function(resolve) { + fetch(filename) + .then(function(response) { + if (response.ok) { + return response.arrayBuffer(); + } + throw new Error('File not found'); + }) + .then(function(data) { + console.log('Loading ' + filename + ' into virtual filesystem...'); + FS.writeFile('/' + filename, new Uint8Array(data)); + console.log('Successfully loaded ' + filename); + resolve(); + }) + .catch(function() { + // File doesn't exist, skip silently + resolve(); + }); + }); + }); + + // Wait for all MPQ files to load before continuing + Module.addRunDependency('loadMPQs'); + Promise.all(loadPromises).then(function() { + Module.removeRunDependency('loadMPQs'); + }); +}); diff --git a/CMakeLists.txt b/CMakeLists.txt index 08767a37e..9d6bc4652 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -271,7 +271,10 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang" AND NOT PS4) if(APPLE) add_link_options("$<$>:LINKER:-dead_strip>") else() - add_link_options("$<$>:LINKER:--gc-sections,--as-needed>") + add_link_options("$<$>:LINKER:--gc-sections>") + if(NOT EMSCRIPTEN) + add_link_options("$<$>:LINKER:--as-needed>") + endif() endif() endif() @@ -301,7 +304,7 @@ endif() # Not a genexp because CMake doesn't support it # https://gitlab.kitware.com/cmake/cmake/-/issues/20546 -if(NOT DISABLE_LTO) +if(NOT DISABLE_LTO AND NOT EMSCRIPTEN) # LTO if supported: include(CheckIPOSupported) check_ipo_supported(RESULT is_ipo_supported OUTPUT lto_error) @@ -369,7 +372,7 @@ else() Packaging/windows/devilutionx.rc Packaging/apple/LaunchScreen.storyboard) - if(CMAKE_STRIP AND NOT DEVILUTIONX_DISABLE_STRIP) + if(CMAKE_STRIP AND NOT DEVILUTIONX_DISABLE_STRIP AND NOT EMSCRIPTEN) add_custom_command( TARGET ${BIN_TARGET} POST_BUILD COMMAND $<$,$>:${CMAKE_STRIP}> @@ -398,7 +401,14 @@ include(Assets) include(Mods) if(EMSCRIPTEN) - target_link_options(${BIN_TARGET} PRIVATE --preload-file assets) + target_link_options(${BIN_TARGET} PRIVATE + --preload-file assets + -sFORCE_FILESYSTEM=1 + -sALLOW_MEMORY_GROWTH=1 + -sASYNCIFY + ) + # Add JavaScript to load MPQ files from the server directory at runtime + target_link_options(${BIN_TARGET} PRIVATE --pre-js ${CMAKE_CURRENT_SOURCE_DIR}/CMake/emscripten_pre.js) endif() if(NOT USE_SDL1 AND NOT UWP_LIB) diff --git a/Source/diablo.cpp b/Source/diablo.cpp index 4f51b7a76..da35c39ed 100644 --- a/Source/diablo.cpp +++ b/Source/diablo.cpp @@ -168,7 +168,11 @@ bool gbGameLoopStartup; bool forceSpawn; bool forceDiablo; int sgnTimeoutCurs; +#ifdef __EMSCRIPTEN__ +bool gbShowIntro = false; // Skip intro videos in browser for performance +#else bool gbShowIntro = true; +#endif /** To know if these things have been done when we get to the diablo_deinit() function */ bool was_archives_init = false; /** To know if surfaces have been initialized or not */ diff --git a/Source/engine/dx.cpp b/Source/engine/dx.cpp index 60c792ec8..d7a77acf5 100644 --- a/Source/engine/dx.cpp +++ b/Source/engine/dx.cpp @@ -17,6 +17,10 @@ #include #endif +#ifdef __EMSCRIPTEN__ +#include +#endif + #include "controls/control_mode.hpp" #include "controls/plrctrls.h" #include "engine/render/primitive_render.hpp" @@ -259,6 +263,11 @@ void RenderPresent() } SDL_RenderPresent(renderer); +#ifdef __EMSCRIPTEN__ + // Yield to browser to allow rendering + emscripten_sleep(1); +#endif + if (*GetOptions().Graphics.frameRateControl != FrameRateControl::VerticalSync) { LimitFrameRate(); }