diff --git a/.github/workflows/emscripten.yml b/.github/workflows/emscripten.yml new file mode 100644 index 0000000000000..7fc3dafeb2425 --- /dev/null +++ b/.github/workflows/emscripten.yml @@ -0,0 +1,31 @@ +name: Emscripten build + +on: + workflow_call: + +# 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: 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/.github/workflows/matrix.yml b/.github/workflows/matrix.yml index 00ab97af93da6..7acddfe73c0f8 100644 --- a/.github/workflows/matrix.yml +++ b/.github/workflows/matrix.yml @@ -348,3 +348,8 @@ jobs: name: cata_test path: tests/cata_test* if-no-files-found: ignore + 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 06783e4d22a34..3e090b2f1ebac 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,16 @@ 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 && success() + run: | + ./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 diff --git a/Makefile b/Makefile index 302af0f54231f..9ad83ba4c77fe 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'] -fexceptions + + ifneq ($(RELEASE), 1) + EMCC_COMMON_FLAGS += -g + endif + + CXXFLAGS += $(EMCC_COMMON_FLAGS) + LDFLAGS += $(EMCC_COMMON_FLAGS) + + LDFLAGS += -sFORCE_FILESYSTEM + 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-data/web/index.html b/build-data/web/index.html new file mode 100644 index 0000000000000..08f29a4ba5beb --- /dev/null +++ b/build-data/web/index.html @@ -0,0 +1,304 @@ + + + + + + + Cataclysm: Dark Days Ahead + + + + + + + + + + + + + + +
+ + + +
Loading...
+ + + + + + 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.sh b/build-scripts/prepare-web.sh new file mode 100755 index 0000000000000..84665e5512ffa --- /dev/null +++ b/build-scripts/prepare-web.sh @@ -0,0 +1,37 @@ +#!/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 + +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""@/" --lz4 + +mkdir -p build/ +cp \ + build-data/web/index.html \ + cataclysm-tiles.{data,data.js,js,wasm} \ + data/font/Terminus.ttf \ + build +cp data/cataicon.ico build/favicon.ico 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/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 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..e10b8abf27863 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; @@ -265,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() ) { @@ -302,7 +310,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 +1919,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 ) ); }