From 87cf0168a0594f719497fe254e8ad2356f0a86f1 Mon Sep 17 00:00:00 2001 From: Maleclypse <54345792+Maleclypse@users.noreply.github.com> Date: Mon, 8 Jan 2024 23:11:15 -0600 Subject: [PATCH 01/12] Merge pull request #70697 from nornagon/emscripten-new add webassembly build --- .github/workflows/emscripten.yml | 59 ++++++ Makefile | 60 +++++- build-scripts/build-emscripten.sh | 7 + build-scripts/prepare-web-data.sh | 22 +++ build-scripts/prepare-web.sh | 10 + index.html | 300 ++++++++++++++++++++++++++++++ src/debug.cpp | 2 +- src/do_turn.cpp | 10 + src/filesystem.cpp | 14 ++ src/game.cpp | 11 +- src/main.cpp | 55 +++++- src/main_menu.cpp | 12 ++ src/mapsharing.cpp | 8 + src/options.cpp | 2 +- src/sdltiles.cpp | 13 +- src/ui_manager.cpp | 7 + src/worldfactory.cpp | 2 +- 17 files changed, 585 insertions(+), 9 deletions(-) create mode 100644 .github/workflows/emscripten.yml create mode 100755 build-scripts/build-emscripten.sh create mode 100755 build-scripts/prepare-web-data.sh create mode 100755 build-scripts/prepare-web.sh create mode 100644 index.html diff --git a/.github/workflows/emscripten.yml b/.github/workflows/emscripten.yml new file mode 100644 index 0000000000000..41e62cb626c17 --- /dev/null +++ b/.github/workflows/emscripten.yml @@ -0,0 +1,59 @@ +name: Emscripten build + +on: + push: + branches: + - master + paths-ignore: + - 'android/**' + - 'build-data/osx/**' + - 'doc/**' + - 'doxygen_doc/**' + - 'lang/**' + - 'lgtm/**' + - 'tools/**' + - '!tools/format/**' + - 'utilities/**' + pull_request: + branches: + - master + paths-ignore: + - 'android/**' + - 'build-data/osx/**' + - 'doc/**' + - 'doxygen_doc/**' + - 'lang/**' + - 'lgtm/**' + - 'tools/**' + - '!tools/format/**' + - 'utilities/**' + +# We only care about the latest revision of a PR, so cancel previous instances. +concurrency: + group: emscripten-build-${{ github.event.pull_request.number || github.ref_name }} + cancel-in-progress: true + +jobs: + build_catatclysm: + name: Build + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - uses: mymindstorm/setup-emsdk@v13 + + - name: Prepare web data + run: ./build-scripts/prepare-web-data.sh + + - name: Build with emcc (emscripten) + run: ./build-scripts/build-emscripten.sh + + - name: Assemble web bundle + run: ./build-scripts/prepare-web.sh + + - name: Create artifact + uses: actions/upload-artifact@v4 + with: + name: play-cdda + path: build/* diff --git a/Makefile b/Makefile index 302af0f54231f..c2e0e9c74d895 100644 --- a/Makefile +++ b/Makefile @@ -22,6 +22,8 @@ # Run: make NATIVE=win32 # OS X # Run: make NATIVE=osx +# Emscripten +# Run: make NATIVE=emscripten # Build types: # Debug (no optimizations) @@ -112,6 +114,10 @@ WARNINGS = \ -Wno-unknown-warning-option \ -Wno-dangling-reference \ -Wno-c++20-compat +ifeq ($(NATIVE), emscripten) + # The EM_ASM macro triggers this warning. + WARNINGS += -Wno-gnu-zero-variadic-macro-arguments +endif # Uncomment below to disable warnings #WARNINGS = -w DEBUGSYMS = -g @@ -144,6 +150,9 @@ VERSION = 0.H TARGET_NAME = cataclysm TILES_TARGET_NAME = $(TARGET_NAME)-tiles +ifeq ($(NATIVE), emscripten) + TILES_TARGET_NAME = $(TARGET_NAME)-tiles.js +endif TARGET = $(BUILD_PREFIX)$(TARGET_NAME) TILESTARGET = $(BUILD_PREFIX)$(TILES_TARGET_NAME) @@ -389,6 +398,8 @@ ifeq ($(RELEASE), 1) else OPTLEVEL = -O3 endif + else ifeq ($(NATIVE), emscripten) + OPTLEVEL = -Os else # MXE ICE Workaround # known bad on 4.9.3 and 4.9.4, if it gets fixed this could include a version test too @@ -457,7 +468,9 @@ else # way to turn off optimization (make NOOPT=1) entirely. OPTLEVEL = -O0 else - ifeq ($(shell $(CXX) -E -Og - < /dev/null > /dev/null 2>&1 && echo fog),fog) + ifeq ($(NATIVE),emscripten) + OPTLEVEL = -O3 + else ifeq ($(shell $(CXX) -E -Og - < /dev/null > /dev/null 2>&1 && echo fog),fog) OPTLEVEL = -Og else OPTLEVEL = -O0 @@ -618,6 +631,47 @@ ifeq ($(NATIVE), cygwin) TARGETSYSTEM=CYGWIN endif +# Emscripten +ifeq ($(NATIVE), emscripten) + CXX=emcc + LD=emcc + + # Flags that are common across compile and link phases. + EMCC_COMMON_FLAGS = -sUSE_SDL=2 -sUSE_SDL_IMAGE=2 -sUSE_SDL_TTF=2 -sSDL2_IMAGE_FORMATS=['png'] + + ifneq ($(RELEASE), 1) + EMCC_COMMON_FLAGS += -g + endif + + CXXFLAGS += $(EMCC_COMMON_FLAGS) + LDFLAGS += $(EMCC_COMMON_FLAGS) + + LDFLAGS += --preload-file web_bundle@/ + LDFLAGS += -sEXPORTED_RUNTIME_METHODS=['FS','stackTrace','jsStackTrace'] + LDFLAGS += -sINITIAL_MEMORY=512MB + LDFLAGS += -sMAXIMUM_MEMORY=4GB + LDFLAGS += -sALLOW_MEMORY_GROWTH + LDFLAGS += -sSTACK_SIZE=262144 + LDFLAGS += -sASYNCIFY + LDFLAGS += -sASYNCIFY_STACK_SIZE=16384 + LDFLAGS += -sENVIRONMENT=web + LDFLAGS += -lidbfs.js + LDFLAGS += -lembind + LDFLAGS += -sWASM_BIGINT # Browser will require BigInt support. + LDFLAGS += -sMAX_WEBGL_VERSION=2 + + ifeq ($(RELEASE), 1) + # Release-mode Linker flags. + LDFLAGS += -Os + LDFLAGS += -sLZ4 + else + # Debug mode linker flags. + LDFLAGS += -O1 # Emscripten link time is slow, so use low optimization level. + LDFLAGS += -sFS_DEBUG + LDFLAGS += -gseparate-dwarf + endif +endif + # MXE cross-compile to win32 ifneq (,$(findstring mingw32,$(CROSS))) DEFINES += -DCROSS_LINUX @@ -716,7 +770,7 @@ ifeq ($(TILES), 1) LDFLAGS += -lSDL2_mixer endif endif - else # not osx + else ifneq ($(NATIVE),emscripten) CXXFLAGS += $(shell $(PKG_CONFIG) --cflags sdl2 SDL2_image SDL2_ttf) ifeq ($(STATIC), 1) @@ -961,7 +1015,9 @@ $(TARGET): $(OBJS) ifeq ($(RELEASE), 1) ifndef DEBUG_SYMBOLS ifneq ($(BACKTRACE),1) + ifneq ($(NATIVE), emscripten) $(STRIP) $(TARGET) + endif endif endif endif diff --git a/build-scripts/build-emscripten.sh b/build-scripts/build-emscripten.sh new file mode 100755 index 0000000000000..c7ec1738a8717 --- /dev/null +++ b/build-scripts/build-emscripten.sh @@ -0,0 +1,7 @@ +#!/bin/bash +set -exo pipefail + +emsdk install 3.1.51 +emsdk activate 3.1.51 + +make -j`nproc` NATIVE=emscripten BACKTRACE=0 TILES=1 TESTS=0 RUNTESTS=0 RELEASE=1 cataclysm-tiles.js diff --git a/build-scripts/prepare-web-data.sh b/build-scripts/prepare-web-data.sh new file mode 100755 index 0000000000000..e9e636a17ed61 --- /dev/null +++ b/build-scripts/prepare-web-data.sh @@ -0,0 +1,22 @@ +#!/bin/bash +set -exo pipefail + +rm -rf web_bundle + +BUNDLE_DIR=web_bundle +DATA_DIR=$BUNDLE_DIR/data +mkdir -p $DATA_DIR +cp -R data/{core,font,fontdata.json,json,mods,names,raw,motd,credits,title,help} $DATA_DIR/ +cp -R gfx $BUNDLE_DIR/ + +# Remove .DS_Store files. +find web_bundle -name ".DS_Store" -type f -exec rm {} \; + +# Remove obsolete mods. +echo "Removing obsolete mods..." +for MOD_DIR in $DATA_DIR/mods/*/ ; do + if jq -e '.[] | select(.type == "MOD_INFO") | .obsolete' "$MOD_DIR/modinfo.json" >/dev/null; then + echo "$MOD_DIR is obsolete, excluding from web_bundle..." + rm -rf $MOD_DIR + fi +done diff --git a/build-scripts/prepare-web.sh b/build-scripts/prepare-web.sh new file mode 100755 index 0000000000000..914e88bfdbd1d --- /dev/null +++ b/build-scripts/prepare-web.sh @@ -0,0 +1,10 @@ +#!/bin/bash +set -exo pipefail + +mkdir -p build/ +cp \ + index.html \ + cataclysm-tiles.{data,js,wasm} \ + data/font/Terminus.ttf \ + build +cp data/cataicon.ico build/favicon.ico diff --git a/index.html b/index.html new file mode 100644 index 0000000000000..78d5bd7422dd4 --- /dev/null +++ b/index.html @@ -0,0 +1,300 @@ + + + + + + + Cataclysm: Dark Days Ahead + + + + + + + + + + + + + + +
+ + + +
Loading...
+ + + + + diff --git a/src/debug.cpp b/src/debug.cpp index ee525b79ca8dc..7dcd7b11b2cda 100644 --- a/src/debug.cpp +++ b/src/debug.cpp @@ -1499,7 +1499,7 @@ std::string game_info::operating_system() #endif } -#if !defined(__CYGWIN__) && !defined (__ANDROID__) && ( defined (__linux__) || defined(unix) || defined(__unix__) || defined(__unix) || ( defined(__APPLE__) && defined(__MACH__) ) || defined(CATA_IS_ON_BSD) ) // linux; unix; MacOs; BSD +#if !defined(EMSCRIPTEN) && !defined(__CYGWIN__) && !defined (__ANDROID__) && ( defined (__linux__) || defined(unix) || defined(__unix__) || defined(__unix) || ( defined(__APPLE__) && defined(__MACH__) ) || defined(CATA_IS_ON_BSD) ) // linux; unix; MacOs; BSD class FILEDeleter { public: diff --git a/src/do_turn.cpp b/src/do_turn.cpp index c522ed858d089..2455a6f0ded8c 100644 --- a/src/do_turn.cpp +++ b/src/do_turn.cpp @@ -1,5 +1,9 @@ #include "do_turn.h" +#if defined(EMSCRIPTEN) +#include +#endif + #include "action.h" #include "avatar.h" #include "bionics.h" @@ -712,5 +716,11 @@ bool do_turn() u.power_balance = u.get_power_level() - u.power_prev_turn; u.power_prev_turn = u.get_power_level(); +#if defined(EMSCRIPTEN) + // This will cause a prompt to be shown if the window is closed, until the + // game is saved. + EM_ASM( window.game_unsaved = true; ); +#endif + return false; } diff --git a/src/filesystem.cpp b/src/filesystem.cpp index 6f0249ff5be43..166a0d4c52bcd 100644 --- a/src/filesystem.cpp +++ b/src/filesystem.cpp @@ -12,6 +12,10 @@ #include #include +#if defined(EMSCRIPTEN) +#include +#endif + #include "cata_utility.h" #include "debug.h" @@ -52,6 +56,12 @@ static const std::array invalid_names = { }; #endif +static void setFsNeedsSync() +{ +#if defined(EMSCRIPTEN) + EM_ASM( window.setFsNeedsSync(); ); +#endif +} static const std::string invalid_chars = "\\/:?\"<>|"; @@ -67,6 +77,7 @@ bool assure_dir_exist( const cata_path &path ) bool assure_dir_exist( const fs::path &path ) { + setFsNeedsSync(); std::error_code ec; bool exists{false}; bool created{false}; @@ -140,6 +151,7 @@ bool remove_file( const std::string &path ) bool remove_file( const fs::path &path ) { + setFsNeedsSync(); std::error_code ec; return fs::remove( path, ec ); } @@ -151,6 +163,7 @@ bool rename_file( const std::string &old_path, const std::string &new_path ) bool rename_file( const fs::path &old_path, const fs::path &new_path ) { + setFsNeedsSync(); std::error_code ec; fs::rename( old_path, new_path, ec ); return !ec; @@ -174,6 +187,7 @@ bool remove_directory( const std::string &path ) bool remove_directory( const fs::path &path ) { std::error_code ec; + setFsNeedsSync(); return fs::remove( path, ec ); } diff --git a/src/game.cpp b/src/game.cpp index 00b54eeb40c38..dc2b4703d9c9f 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -33,6 +33,10 @@ #include #include +#if defined(EMSCRIPTEN) +#include +#endif + #include "achievement.h" #include "action.h" #include "activity_actor_definitions.h" @@ -2547,7 +2551,7 @@ input_context get_default_mode_input_context() ctxt.register_action( "debug_mode" ); ctxt.register_action( "zoom_out" ); ctxt.register_action( "zoom_in" ); -#if !defined(__ANDROID__) +#if !defined(__ANDROID__) && !defined(EMSCRIPTEN) ctxt.register_action( "toggle_fullscreen" ); #endif ctxt.register_action( "toggle_pixel_minimap" ); @@ -3417,6 +3421,11 @@ bool game::save() fout.imbue( std::locale::classic() ); fout << total_time_played.count(); } ); +#if defined(EMSCRIPTEN) + // This will allow the window to be closed without a prompt, until do_turn() + // is called. + EM_ASM( window.game_unsaved = false; ); +#endif return true; } } catch( std::ios::failure & ) { diff --git a/src/main.cpp b/src/main.cpp index e0caeb618c531..d4eda35f4f0da 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -59,6 +59,10 @@ # include // getpid() #endif +#if defined(EMSCRIPTEN) +#include +#endif + #if defined(PREFIX) # undef PREFIX # include "prefix.h" @@ -566,6 +570,47 @@ bool assure_essential_dirs_exist() } // namespace +#if defined(EMSCRIPTEN) +EM_ASYNC_JS( void, mount_idbfs, (), { + console.log( "Mounting IDBFS for persistence..." ); + FS.mkdir( '/home/web_user/.cataclysm-dda' ); + FS.mount( IDBFS, {}, '/home/web_user/.cataclysm-dda' ); + await new Promise( function( resolve, reject ) + { + FS.syncfs( true, function( err ) { + if( err ) { + reject( err ); + } else { + console.log( "Succesfully mounted IDBFS." ); + resolve(); + } + } ); + } ); + + let fsNeedsSync = false; + window.setFsNeedsSync = function setFsNeedsSync() + { + if( !fsNeedsSync ) { + requestAnimationFrame( syncFs ); + } + fsNeedsSync = true; + }; + + function syncFs() + { + console.log( "Persisting to IDBFS..." ); + FS.syncfs( false, function( err ) { + fsNeedsSync = false; + if( err ) { + console.error( err ); + } else { + console.log( "Succesfully persisted to IDBFS..." ); + } + } ); + } +} ); +#endif + #if defined(USE_WINMAIN) int APIENTRY WinMain( _In_ HINSTANCE /* hInstance */, _In_opt_ HINSTANCE /* hPrevInstance */, _In_ LPSTR /* lpCmdLine */, _In_ int /* nCmdShow */ ) @@ -585,6 +630,10 @@ int main( int argc, const char *argv[] ) flatbuffers::ClassicLocale::Get(); #endif +#if defined(EMSCRIPTEN) + mount_idbfs(); +#endif + on_out_of_scope json_member_reporting_guard{ [] { // Disable reporting unvisited members if stack unwinding leaves main early. Json::globally_report_unvisited_members( false ); @@ -624,7 +673,7 @@ int main( int argc, const char *argv[] ) #if defined(__ANDROID__) PATH_INFO::init_user_dir( external_storage_path ); #else -# if defined(USE_HOME_DIR) || defined(USE_XDG_DIR) +# if defined(USE_HOME_DIR) || defined(USE_XDG_DIR) || defined(EMSCRIPTEN) PATH_INFO::init_user_dir( "" ); # else PATH_INFO::init_user_dir( "." ); @@ -648,7 +697,11 @@ int main( int argc, const char *argv[] ) exit( 1 ); } +#if defined(EMSCRIPTEN) + setupDebug( DebugOutput::std_err ); +#else setupDebug( DebugOutput::file ); +#endif // NOLINTNEXTLINE(cata-tests-must-restore-global-state) json_error_output_colors = json_error_output_colors_t::color_tags; diff --git a/src/main_menu.cpp b/src/main_menu.cpp index 1d3676875ab59..9ee8a1fa1e7ca 100644 --- a/src/main_menu.cpp +++ b/src/main_menu.cpp @@ -13,6 +13,10 @@ #include #include +#if defined(EMSCRIPTEN) +#include +#endif + #include "auto_pickup.h" #include "avatar.h" #include "cata_scope_helpers.h" @@ -453,7 +457,9 @@ void main_menu::init_strings() vMenuItems.emplace_back( pgettext( "Main Menu", "Setings" ) ); vMenuItems.emplace_back( pgettext( "Main Menu", "Hlp" ) ); vMenuItems.emplace_back( pgettext( "Main Menu", "redits" ) ); +#if !defined(EMSCRIPTEN) vMenuItems.emplace_back( pgettext( "Main Menu", "uit" ) ); +#endif // new game menu items vNewGameSubItems.clear(); @@ -636,6 +642,10 @@ bool main_menu::opening_screen() } } +#if defined(EMSCRIPTEN) + EM_ASM( window.dispatchEvent( new Event( 'menuready' ) ); ); +#endif + while( !start ) { ui_manager::redraw(); std::string action = ctxt.handle_input(); @@ -725,9 +735,11 @@ bool main_menu::opening_screen() // also check special keys if( action == "QUIT" ) { +#if !defined(EMSCRIPTEN) if( query_yn( _( "Really quit?" ) ) ) { return false; } +#endif } else if( action == "LEFT" || action == "PREV_TAB" || action == "RIGHT" || action == "NEXT_TAB" ) { sel_line = 0; sel1 = inc_clamp_wrap( sel1, action == "RIGHT" || action == "NEXT_TAB", diff --git a/src/mapsharing.cpp b/src/mapsharing.cpp index 96a4a0e83b3e7..a0bf810325bad 100644 --- a/src/mapsharing.cpp +++ b/src/mapsharing.cpp @@ -16,6 +16,10 @@ #include "platform_win.h" #endif +#if defined(EMSCRIPTEN) +#include +#endif + bool MAP_SHARING::sharing; bool MAP_SHARING::competitive; bool MAP_SHARING::worldmenu; @@ -160,4 +164,8 @@ void ofstream_wrapper::close() // Leave the temp path, so the user can move it if possible. throw std::runtime_error( "moving temporary file \"" + temp_path.u8string() + "\" failed" ); } + +#if defined(EMSCRIPTEN) + EM_ASM( window.setFsNeedsSync(); ); +#endif } diff --git a/src/options.cpp b/src/options.cpp index 50c164d3f0c03..1ae829a8d0ea0 100644 --- a/src/options.cpp +++ b/src/options.cpp @@ -3930,7 +3930,7 @@ std::string options_manager::show( bool ingame, const bool world_options_only, b calendar::set_eternal_night( ::get_option( "ETERNAL_TIME_OF_DAY" ) == "night" ); calendar::set_eternal_day( ::get_option( "ETERNAL_TIME_OF_DAY" ) == "day" ); -#if !defined(__ANDROID__) && (defined(TILES) || defined(_WIN32)) +#if !defined(EMSCRIPTEN) && !defined(__ANDROID__) && (defined(TILES) || defined(_WIN32)) if( terminal_size_changed ) { int scaling_factor = get_scaling_factor(); point TERM( ::get_option( "TERMINAL_X" ), ::get_option( "TERMINAL_Y" ) ); diff --git a/src/sdltiles.cpp b/src/sdltiles.cpp index 67ae517317e59..e7884ebff7f1c 100644 --- a/src/sdltiles.cpp +++ b/src/sdltiles.cpp @@ -95,6 +95,10 @@ #include "worldfactory.h" #endif +#if defined(EMSCRIPTEN) +#include +#endif + #define dbg(x) DebugLog((x),D_SDL) << __FILE__ << ":" << __LINE__ << ": " static const oter_type_str_id oter_type_forest_trail( "forest_trail" ); @@ -249,7 +253,7 @@ static void WinCreate() SDL_SetHint( SDL_HINT_RENDER_SCALE_QUALITY, get_option( "SCALING_MODE" ).c_str() ); } -#if !defined(__ANDROID__) +#if !defined(__ANDROID__) && !defined(EMSCRIPTEN) if( get_option( "FULLSCREEN" ) == "fullscreen" ) { window_flags |= SDL_WINDOW_FULLSCREEN; fullscreen = true; @@ -302,7 +306,7 @@ static void WinCreate() ) ); throwErrorIf( !::window, "SDL_CreateWindow failed" ); -#if !defined(__ANDROID__) +#if !defined(__ANDROID__) && !defined(EMSCRIPTEN) // On Android SDL seems janky in windowed mode so we're fullscreen all the time. // Fullscreen mode is now modified so it obeys terminal width/height, rather than // overwriting it with this calculation. @@ -1911,6 +1915,11 @@ void resize_term( const int cell_w, const int cell_h ) void toggle_fullscreen_window() { + // Can't enter fullscreen on Emscripten. +#if defined(EMSCRIPTEN) + return; +#endif + static int restore_win_w = get_option( "TERMINAL_X" ) * fontwidth * scaling_factor; static int restore_win_h = get_option( "TERMINAL_Y" ) * fontheight * scaling_factor; diff --git a/src/ui_manager.cpp b/src/ui_manager.cpp index 2a253d7870e6a..f20302ad4e775 100644 --- a/src/ui_manager.cpp +++ b/src/ui_manager.cpp @@ -14,6 +14,10 @@ #include "point.h" #include "sdltiles.h" // IWYU pragma: keep +#if defined(EMSCRIPTEN) +#include +#endif + using ui_stack_t = std::vector>; static bool redraw_in_progress = false; @@ -427,6 +431,9 @@ void ui_adaptor::redraw_invalidated() } } } while( restart_redrawing ); +#if defined(EMSCRIPTEN) + emscripten_sleep( 1 ); +#endif } void ui_adaptor::screen_resized() diff --git a/src/worldfactory.cpp b/src/worldfactory.cpp index 0d29c24547abc..f69cbf04a724f 100644 --- a/src/worldfactory.cpp +++ b/src/worldfactory.cpp @@ -408,7 +408,7 @@ void worldfactory::init() for( auto &origin_file : get_files_from_path( ".", origin_path, false ) ) { std::string filename = origin_file.substr( origin_file.find_last_of( "/\\" ) ); - if( rename( origin_file.c_str(), ( newworld->folder_path() + filename ).c_str() ) ) { + if( rename_file( origin_file, ( newworld->folder_path() + filename ) ) ) { debugmsg( "Error while moving world files: %s. World may have been corrupted", strerror( errno ) ); } From 81cea2517bb24614d012563371b03aece1a86a2d Mon Sep 17 00:00:00 2001 From: akrieger Date: Fri, 2 Feb 2024 16:12:10 -0800 Subject: [PATCH 02/12] Merge pull request #71436 from nornagon/emscripten-hidpi fix wrong scaling on hidpi screens in emscripten --- src/sdltiles.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sdltiles.cpp b/src/sdltiles.cpp index e7884ebff7f1c..e10b8abf27863 100644 --- a/src/sdltiles.cpp +++ b/src/sdltiles.cpp @@ -269,6 +269,10 @@ static void WinCreate() window_flags |= SDL_WINDOW_MAXIMIZED; } #endif +#if defined(EMSCRIPTEN) + // Without this, the game only displays in the top-left 1/4 of the window. + window_flags &= ~SDL_WINDOW_ALLOW_HIGHDPI; +#endif int display = std::stoi( get_option( "DISPLAY" ) ); if( display < 0 || display >= SDL_GetNumVideoDisplays() ) { From 696b8ae3e4aa434d81ec7fcfb3bd99611e3077fd Mon Sep 17 00:00:00 2001 From: Jeremy Rose Date: Tue, 20 Feb 2024 00:54:10 -0800 Subject: [PATCH 03/12] Add some basic exception reporting for emscripten (#71853) --- index.html | 5 ++++- src/emscripten_exception.cpp | 13 +++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 src/emscripten_exception.cpp diff --git a/index.html b/index.html index 78d5bd7422dd4..5292ea2e9e70f 100644 --- a/index.html +++ b/index.html @@ -287,7 +287,10 @@ }, }; Module.setStatus("Downloading..."); - window.onerror = function (event) { + window.onerror = function (event, source, lineno, colno, error) { + if (typeof error === 'number' && Module.getExceptionMessage) { + console.log(Module.getExceptionMessage(error)); + } alert("Exception thrown, see JavaScript console."); }; window.onbeforeunload = (e) => { diff --git a/src/emscripten_exception.cpp b/src/emscripten_exception.cpp new file mode 100644 index 0000000000000..573f18071205c --- /dev/null +++ b/src/emscripten_exception.cpp @@ -0,0 +1,13 @@ +#if defined(EMSCRIPTEN) +#include + +std::string getExceptionMessage( intptr_t exceptionPtr ) +{ + return std::string( reinterpret_cast( exceptionPtr )->what() ); +} + +EMSCRIPTEN_BINDINGS( Bindings ) +{ + emscripten::function( "getExceptionMessage", &getExceptionMessage ); +} +#endif From c64d2c9794c344d13cec521ebabb949e9145b6a2 Mon Sep 17 00:00:00 2001 From: Maleclypse <54345792+Maleclypse@users.noreply.github.com> Date: Wed, 10 Jan 2024 22:36:38 -0600 Subject: [PATCH 04/12] Merge pull request #70805 from nornagon/hide-index-html Hide index.html in build-data/web --- index.html => build-data/web/index.html | 0 build-scripts/prepare-web.sh | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename index.html => build-data/web/index.html (100%) diff --git a/index.html b/build-data/web/index.html similarity index 100% rename from index.html rename to build-data/web/index.html diff --git a/build-scripts/prepare-web.sh b/build-scripts/prepare-web.sh index 914e88bfdbd1d..93e96c3746dac 100755 --- a/build-scripts/prepare-web.sh +++ b/build-scripts/prepare-web.sh @@ -3,7 +3,7 @@ set -exo pipefail mkdir -p build/ cp \ - index.html \ + build-data/web/index.html \ cataclysm-tiles.{data,js,wasm} \ data/font/Terminus.ttf \ build From 84d1ad76f589e8804f276a0b6b27eede0d225922 Mon Sep 17 00:00:00 2001 From: Maleclypse <54345792+Maleclypse@users.noreply.github.com> Date: Fri, 12 Jan 2024 16:26:01 -0600 Subject: [PATCH 05/12] Merge pull request #70870 from nornagon/patch-17 add WebAssembly build to release workflow --- .github/workflows/release.yml | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 06783e4d22a34..3b2b7722f6c1f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -162,6 +162,14 @@ jobs: artifact: android-bundle ext: aab content: application/aap + - name: WebAssembly Bundle + os: ubuntu-latest + mxe: none + artifact: wasm + ext: zip + content: application/zip + sound: 0 + wasm: true name: ${{ matrix.name }} runs-on: ${{ matrix.os }} permissions: write-all @@ -217,11 +225,14 @@ jobs: shasum -a 256 -c ./build-scripts/libbacktrace-${{ matrix.mxe }}-w64-mingw32-sha256 sudo tar -xzf libbacktrace-${{ matrix.mxe }}-w64-mingw32.tar.gz --exclude=LICENSE -C /opt/mxe/usr/${{ matrix.mxe }}-w64-mingw32.static.gcc12 - name: Install dependencies (Linux) - if: runner.os == 'Linux' && matrix.mxe == 'none' && matrix.android == 'none' + if: runner.os == 'Linux' && matrix.mxe == 'none' && matrix.android == 'none' && !matrix.wasm run: | sudo apt-get update sudo apt-get install libncursesw5-dev libsdl2-dev libsdl2-ttf-dev libsdl2-image-dev \ libsdl2-mixer-dev libpulse-dev ccache gettext parallel + - name: Install Emscripten (WebAssembly) + if: matrix.wasm + uses: mymindstorm/setup-emsdk@v13 - name: Install runtime dependencies (mac) if: runner.os == 'macOS' uses: BrettDong/setup-sdl2-frameworks@v1 @@ -250,10 +261,17 @@ jobs: run: | lang/compile_mo.sh all - name: Build CDDA (linux) - if: runner.os == 'Linux' && matrix.mxe == 'none' && matrix.android == 'none' && matrix.artifact != 'linux-objectcreator-x64' + if: runner.os == 'Linux' && matrix.mxe == 'none' && matrix.android == 'none' && matrix.artifact != 'linux-objectcreator-x64' && !matrix.wasm run: | make -j$((`nproc`+0)) TILES=${{ matrix.tiles }} SOUND=${{ matrix.tiles }} RELEASE=1 LOCALIZE=1 LANGUAGES=all BACKTRACE=1 PCH=0 bindist mv cataclysmdda-0.H.tar.gz cdda-${{ matrix.artifact }}-${{ needs.release.outputs.timestamp }}.tar.gz + - name: Build CDDA (WebAssembly) + if: matrix.wasm + run: | + ./build-scripts/prepare-web-data.sh + ./build-scripts/build-emscripten.sh + ./build-scripts/prepare-web.sh + (cd build && zip ../cdda-${{ matrix.artifact }}-${{ needs.release.outputs.timestamp }}.${{ matrix.ext }} *) - name: Login to GitHub Container Registry if: matrix.artifact == 'windows-objectcreator-x64' || matrix.artifact == 'linux-objectcreator-x64' uses: docker/login-action@v2 From 62762e0e8407296cb7ed463fc52fc6d1c813aa5d Mon Sep 17 00:00:00 2001 From: akrieger Date: Tue, 20 Feb 2024 22:24:43 -0800 Subject: [PATCH 06/12] Merge pull request #71886 from nornagon/emscripten-separate-data [wasm] prepare web bundle data separately from build --- .github/workflows/emscripten.yml | 3 --- .github/workflows/release.yml | 1 - Makefile | 2 +- build-data/web/index.html | 1 + build-scripts/prepare-web-data.sh | 22 ---------------------- build-scripts/prepare-web.sh | 24 +++++++++++++++++++++++- 6 files changed, 25 insertions(+), 28 deletions(-) delete mode 100755 build-scripts/prepare-web-data.sh diff --git a/.github/workflows/emscripten.yml b/.github/workflows/emscripten.yml index 41e62cb626c17..f657f1e7e7a60 100644 --- a/.github/workflows/emscripten.yml +++ b/.github/workflows/emscripten.yml @@ -43,9 +43,6 @@ jobs: - uses: mymindstorm/setup-emsdk@v13 - - name: Prepare web data - run: ./build-scripts/prepare-web-data.sh - - name: Build with emcc (emscripten) run: ./build-scripts/build-emscripten.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3b2b7722f6c1f..cde1410c02d96 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -268,7 +268,6 @@ jobs: - name: Build CDDA (WebAssembly) if: matrix.wasm run: | - ./build-scripts/prepare-web-data.sh ./build-scripts/build-emscripten.sh ./build-scripts/prepare-web.sh (cd build && zip ../cdda-${{ matrix.artifact }}-${{ needs.release.outputs.timestamp }}.${{ matrix.ext }} *) diff --git a/Makefile b/Makefile index c2e0e9c74d895..119e5d20d39f6 100644 --- a/Makefile +++ b/Makefile @@ -646,7 +646,7 @@ ifeq ($(NATIVE), emscripten) CXXFLAGS += $(EMCC_COMMON_FLAGS) LDFLAGS += $(EMCC_COMMON_FLAGS) - LDFLAGS += --preload-file web_bundle@/ + LDFLAGS += -sFORCE_FILESYSTEM LDFLAGS += -sEXPORTED_RUNTIME_METHODS=['FS','stackTrace','jsStackTrace'] LDFLAGS += -sINITIAL_MEMORY=512MB LDFLAGS += -sMAXIMUM_MEMORY=4GB diff --git a/build-data/web/index.html b/build-data/web/index.html index 5292ea2e9e70f..08f29a4ba5beb 100644 --- a/build-data/web/index.html +++ b/build-data/web/index.html @@ -298,6 +298,7 @@ e.preventDefault(); } + diff --git a/build-scripts/prepare-web-data.sh b/build-scripts/prepare-web-data.sh deleted file mode 100755 index e9e636a17ed61..0000000000000 --- a/build-scripts/prepare-web-data.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash -set -exo pipefail - -rm -rf web_bundle - -BUNDLE_DIR=web_bundle -DATA_DIR=$BUNDLE_DIR/data -mkdir -p $DATA_DIR -cp -R data/{core,font,fontdata.json,json,mods,names,raw,motd,credits,title,help} $DATA_DIR/ -cp -R gfx $BUNDLE_DIR/ - -# Remove .DS_Store files. -find web_bundle -name ".DS_Store" -type f -exec rm {} \; - -# Remove obsolete mods. -echo "Removing obsolete mods..." -for MOD_DIR in $DATA_DIR/mods/*/ ; do - if jq -e '.[] | select(.type == "MOD_INFO") | .obsolete' "$MOD_DIR/modinfo.json" >/dev/null; then - echo "$MOD_DIR is obsolete, excluding from web_bundle..." - rm -rf $MOD_DIR - fi -done diff --git a/build-scripts/prepare-web.sh b/build-scripts/prepare-web.sh index 93e96c3746dac..caa1188082c66 100755 --- a/build-scripts/prepare-web.sh +++ b/build-scripts/prepare-web.sh @@ -1,10 +1,32 @@ #!/bin/bash set -exo pipefail +rm -rf web_bundle + +BUNDLE_DIR=web_bundle +DATA_DIR=$BUNDLE_DIR/data +mkdir -p $DATA_DIR +cp -R data/{core,font,fontdata.json,json,mods,names,raw,motd,credits,title,help} $DATA_DIR/ +cp -R gfx $BUNDLE_DIR/ + +# Remove .DS_Store files. +find web_bundle -name ".DS_Store" -type f -exec rm {} \; + +# Remove obsolete mods. +echo "Removing obsolete mods..." +for MOD_DIR in $DATA_DIR/mods/*/ ; do + if jq -e '.[] | select(.type == "MOD_INFO") | .obsolete' "$MOD_DIR/modinfo.json" >/dev/null; then + echo "$MOD_DIR is obsolete, excluding from web_bundle..." + rm -rf $MOD_DIR + fi +done + +$EMSDK/upstream/emscripten/tools/file_packager cataclysm-tiles.data --js-output=cataclysm-tiles.data.js --no-node --preload "$BUNDLE_DIR""@/" + mkdir -p build/ cp \ build-data/web/index.html \ - cataclysm-tiles.{data,js,wasm} \ + cataclysm-tiles.{data,data.js,js,wasm} \ data/font/Terminus.ttf \ build cp data/cataicon.ico build/favicon.ico From 2747aa46afcd09c806f04aecd4f7ccb5684a5a0f Mon Sep 17 00:00:00 2001 From: akrieger Date: Tue, 20 Feb 2024 22:26:42 -0800 Subject: [PATCH 07/12] Merge pull request #71883 from nornagon/emscripten-enable-exceptions [wasm] build with -fexceptions --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 119e5d20d39f6..9ad83ba4c77fe 100644 --- a/Makefile +++ b/Makefile @@ -637,7 +637,7 @@ ifeq ($(NATIVE), emscripten) LD=emcc # Flags that are common across compile and link phases. - EMCC_COMMON_FLAGS = -sUSE_SDL=2 -sUSE_SDL_IMAGE=2 -sUSE_SDL_TTF=2 -sSDL2_IMAGE_FORMATS=['png'] + EMCC_COMMON_FLAGS = -sUSE_SDL=2 -sUSE_SDL_IMAGE=2 -sUSE_SDL_TTF=2 -sSDL2_IMAGE_FORMATS=['png'] -fexceptions ifneq ($(RELEASE), 1) EMCC_COMMON_FLAGS += -g From 0401de7b96b3ab11ce6668fda90363aaa5fa2bb6 Mon Sep 17 00:00:00 2001 From: Jeremy Rose Date: Sat, 24 Feb 2024 14:06:14 -0800 Subject: [PATCH 08/12] [wasm] exclude MA mod and Ultica_iso from bundle (#71908) * [wasm] exclude MA mod from bundle * also remove Ultica_iso --- build-scripts/prepare-web.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/build-scripts/prepare-web.sh b/build-scripts/prepare-web.sh index caa1188082c66..cc48f05ad3713 100755 --- a/build-scripts/prepare-web.sh +++ b/build-scripts/prepare-web.sh @@ -21,6 +21,11 @@ for MOD_DIR in $DATA_DIR/mods/*/ ; do fi done +echo "Removing MA mod..." +rm -rf $DATA_DIR/mods/MA +echo "Removing Ultica_iso tileset..." +rm -rf $BUNDLE_DIR/gfx/Ultica_iso + $EMSDK/upstream/emscripten/tools/file_packager cataclysm-tiles.data --js-output=cataclysm-tiles.data.js --no-node --preload "$BUNDLE_DIR""@/" mkdir -p build/ From e3ad4c8d7b19da0201fad32f5f72b037922c22c7 Mon Sep 17 00:00:00 2001 From: Maleclypse <54345792+Maleclypse@users.noreply.github.com> Date: Tue, 27 Feb 2024 09:29:03 -0600 Subject: [PATCH 09/12] Merge pull request #72011 from nornagon/patch-23 [wasm] add --lz4 to file_packager --- build-scripts/prepare-web.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-scripts/prepare-web.sh b/build-scripts/prepare-web.sh index cc48f05ad3713..84665e5512ffa 100755 --- a/build-scripts/prepare-web.sh +++ b/build-scripts/prepare-web.sh @@ -26,7 +26,7 @@ rm -rf $DATA_DIR/mods/MA echo "Removing Ultica_iso tileset..." rm -rf $BUNDLE_DIR/gfx/Ultica_iso -$EMSDK/upstream/emscripten/tools/file_packager cataclysm-tiles.data --js-output=cataclysm-tiles.data.js --no-node --preload "$BUNDLE_DIR""@/" +$EMSDK/upstream/emscripten/tools/file_packager cataclysm-tiles.data --js-output=cataclysm-tiles.data.js --no-node --preload "$BUNDLE_DIR""@/" --lz4 mkdir -p build/ cp \ From 9888b29b97b2663e0e9ae68edcef42580dd94b66 Mon Sep 17 00:00:00 2001 From: akrieger Date: Thu, 9 May 2024 22:55:47 -0700 Subject: [PATCH 10/12] Build emscripten after matrix build. (#73615) --- .github/workflows/emscripten.yml | 27 +-------------------------- .github/workflows/matrix.yml | 4 ++++ 2 files changed, 5 insertions(+), 26 deletions(-) diff --git a/.github/workflows/emscripten.yml b/.github/workflows/emscripten.yml index f657f1e7e7a60..7fc3dafeb2425 100644 --- a/.github/workflows/emscripten.yml +++ b/.github/workflows/emscripten.yml @@ -1,32 +1,7 @@ name: Emscripten build on: - push: - branches: - - master - paths-ignore: - - 'android/**' - - 'build-data/osx/**' - - 'doc/**' - - 'doxygen_doc/**' - - 'lang/**' - - 'lgtm/**' - - 'tools/**' - - '!tools/format/**' - - 'utilities/**' - pull_request: - branches: - - master - paths-ignore: - - 'android/**' - - 'build-data/osx/**' - - 'doc/**' - - 'doxygen_doc/**' - - 'lang/**' - - 'lgtm/**' - - 'tools/**' - - '!tools/format/**' - - 'utilities/**' + workflow_call: # We only care about the latest revision of a PR, so cancel previous instances. concurrency: diff --git a/.github/workflows/matrix.yml b/.github/workflows/matrix.yml index 00ab97af93da6..df4f8e8e6ae60 100644 --- a/.github/workflows/matrix.yml +++ b/.github/workflows/matrix.yml @@ -348,3 +348,7 @@ jobs: name: cata_test path: tests/cata_test* if-no-files-found: ignore + emscripten: + needs: [ varied_builds ] + uses: ./.github/workflows/emscripten.yml + secrets: inherit From b9ea618e0cf32f7fe9d8a7e156c3130d1cf8861b Mon Sep 17 00:00:00 2001 From: Kevin Granade Date: Sat, 11 May 2024 13:46:22 -0700 Subject: [PATCH 11/12] skip emscripten build for draft PRs (#73668) * skip emscripten build for draft PRs * reenable enscripten builds for releases * only execute enscripten build if other builds have succeeded --- .github/workflows/matrix.yml | 1 + .github/workflows/release.yml | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/.github/workflows/matrix.yml b/.github/workflows/matrix.yml index df4f8e8e6ae60..7acddfe73c0f8 100644 --- a/.github/workflows/matrix.yml +++ b/.github/workflows/matrix.yml @@ -351,4 +351,5 @@ jobs: emscripten: needs: [ varied_builds ] uses: ./.github/workflows/emscripten.yml + if: ${{ success() && github.event.pull_request.draft == false }} secrets: inherit diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cde1410c02d96..e5c9839e93a04 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -365,3 +365,8 @@ jobs: fi - run: | gh release upload cdda-0.H-${{ needs.release.outputs.timestamp }} cdda-${{ matrix.artifact }}-${{ needs.release.outputs.timestamp }}.${{ matrix.ext }} + emscripten: + needs: [ builds ] + if: ${{ success() }} + uses: ./.github/workflows/emscripten.yml + secrets: inherit From 71884f1d857bab11d33734762f029d65e5286a76 Mon Sep 17 00:00:00 2001 From: Kevin Granade Date: Sun, 12 May 2024 07:27:06 -0700 Subject: [PATCH 12/12] Remove redundant emscripten build (#73693) Turns out this was already being built --- .github/workflows/release.yml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e5c9839e93a04..3e090b2f1ebac 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -266,7 +266,7 @@ jobs: make -j$((`nproc`+0)) TILES=${{ matrix.tiles }} SOUND=${{ matrix.tiles }} RELEASE=1 LOCALIZE=1 LANGUAGES=all BACKTRACE=1 PCH=0 bindist mv cataclysmdda-0.H.tar.gz cdda-${{ matrix.artifact }}-${{ needs.release.outputs.timestamp }}.tar.gz - name: Build CDDA (WebAssembly) - if: matrix.wasm + if: matrix.wasm && success() run: | ./build-scripts/build-emscripten.sh ./build-scripts/prepare-web.sh @@ -365,8 +365,3 @@ jobs: fi - run: | gh release upload cdda-0.H-${{ needs.release.outputs.timestamp }} cdda-${{ matrix.artifact }}-${{ needs.release.outputs.timestamp }}.${{ matrix.ext }} - emscripten: - needs: [ builds ] - if: ${{ success() }} - uses: ./.github/workflows/emscripten.yml - secrets: inherit