From 9ae4bb0fe1f2caf14c616d901cea203ad0d6c4be Mon Sep 17 00:00:00 2001 From: Roman Turchin Date: Mon, 30 Dec 2024 19:17:34 -0500 Subject: [PATCH] Add build target "web" --- .gitignore | 7 ++ Makefile.web | 37 +++++++++ files/web/dist/index.html | 60 ++++++++++++++ files/web/readme | 14 ++++ files/web/sdl2_mixer.py | 126 +++++++++++++++++++++++++++++ src/dist/Makefile | 8 ++ src/dist/Makefile.web | 40 +++++++++ src/dist/web/Makefile | 55 +++++++++++++ src/engine/thread.cpp | 3 +- src/fheroes2/game/fheroes2.cpp | 3 +- src/fheroes2/game/game_hotkeys.cpp | 7 ++ src/fheroes2/game/game_io.cpp | 8 +- src/fheroes2/system/settings.cpp | 8 +- 13 files changed, 372 insertions(+), 4 deletions(-) create mode 100644 Makefile.web create mode 100644 files/web/dist/index.html create mode 100644 files/web/readme create mode 100644 files/web/sdl2_mixer.py create mode 100644 src/dist/Makefile.web create mode 100644 src/dist/web/Makefile diff --git a/.gitignore b/.gitignore index af8057fc82b..bddbe0d8d26 100644 --- a/.gitignore +++ b/.gitignore @@ -96,3 +96,10 @@ android/app/src/main/assets/files/ # Mac magic folders .DS_Store + +#web build artifacts +*.wasm +*.wasm.map +fheroes2.js +fheroes2.data +src/dist/web/dist/* diff --git a/Makefile.web b/Makefile.web new file mode 100644 index 00000000000..f935cbe2080 --- /dev/null +++ b/Makefile.web @@ -0,0 +1,37 @@ +########################################################################### +# fheroes2: https://github.com/ihhub/fheroes2 # +# Copyright (C) 2021 - 2024 # +# # +# This program 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 2 of the License, or # +# (at your option) any later version. # +# # +# This program 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 this program; if not, write to the # +# Free Software Foundation, Inc., # +# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # +########################################################################### + +# Options: +# +# FHEROES2_STRICT_COMPILATION: build in strict compilation mode (turns warnings into errors) +# FHEROES2_WITH_DEBUG: build in debug mode +# FHEROES2_DATA: set the built-in path to the fheroes2 data directory (e.g. /usr/share/fheroes2) + +PROJECT_NAME := fheroes2 +PROJECT_VERSION := $(file < version.txt) + +.PHONY: all clean + +all: + $(MAKE) -C src/dist PLATFORM=web + +clean: + $(MAKE) -C src/dist PLATFORM=web clean + $(MAKE) -C files/lang clean diff --git a/files/web/dist/index.html b/files/web/dist/index.html new file mode 100644 index 00000000000..75dc8b98336 --- /dev/null +++ b/files/web/dist/index.html @@ -0,0 +1,60 @@ + + + fheroes2 + + + + + + + + diff --git a/files/web/readme b/files/web/readme new file mode 100644 index 00000000000..deeb60a7bc8 --- /dev/null +++ b/files/web/readme @@ -0,0 +1,14 @@ +building with pthread: +1. modify src/dist/Makefile.web to enable pthread support +2. modify src/engine/thread.cpp to allow spawn threads +3. use patch sdl2_mixer.py; PR and issue description https://github.com/emscripten-core/emscripten/pull/23094 +4. check how to enable SharedArrayBuffer, options: serve with custom headers, use service worker to intercept request, use custom browser flags + +docker run --rm -v $(pwd):/src emscripten/emsdk:3.1.74 \ + sh -c \ + 'cp -f files/web/sdl2_mixer.py /emsdk/upstream/emscripten/tools/ports/sdl2_mixer.py && apt update && apt install -y gettext && emmake make -f Makefile.web' + +building without pthread: +docker run --rm -v $(pwd):/src emscripten/emsdk:3.1.74 \ + sh -c \ + 'apt update && apt install -y gettext && emmake make -f Makefile.web' diff --git a/files/web/sdl2_mixer.py b/files/web/sdl2_mixer.py new file mode 100644 index 00000000000..a4723fd6152 --- /dev/null +++ b/files/web/sdl2_mixer.py @@ -0,0 +1,126 @@ +# Copyright 2016 The Emscripten Authors. All rights reserved. +# Emscripten is available under two separate licenses, the MIT license and the +# University of Illinois/NCSA Open Source License. Both these licenses can be +# found in the LICENSE file. + +import os + +TAG = 'release-2.8.0' +HASH = '494ccd74540f74e717f7e4f1dc7f96398c0f4b1883ab00c4a76b0c7239bd2c185cb4358a35ef47819c49e7c14dac7c37b98a29c7b5237478121571f5e7ac4dfc' + +deps = ['sdl2'] +variants = { + 'sdl2_mixer-mp3': {'SDL2_MIXER_FORMATS': ["mp3"]}, + 'sdl2_mixer-none': {'SDL2_MIXER_FORMATS': []}, + 'sdl2_mixer-mp3-mt': {'SDL2_MIXER_FORMATS': ["mp3"], 'PTHREADS': 1}, + 'sdl2_mixer-none-mt': {'SDL2_MIXER_FORMATS': [], 'PTHREADS': 1}, +} + + +def needed(settings): + return settings.USE_SDL_MIXER == 2 + + +def get_lib_name(settings): + settings.SDL2_MIXER_FORMATS.sort() + formats = '-'.join(settings.SDL2_MIXER_FORMATS) + + libname = 'libSDL2_mixer' + if formats != '': + libname += '-' + formats + if settings.PTHREADS: + libname += '-mt' + libname += '.a' + + return libname + + +def get(ports, settings, shared): + sdl_build = os.path.join(ports.get_build_dir(), 'sdl2') + assert os.path.exists(sdl_build), 'You must use SDL2 to use SDL2_mixer' + ports.fetch_project('sdl2_mixer', f'https://github.com/libsdl-org/SDL_mixer/archive/{TAG}.zip', sha512hash=HASH) + libname = get_lib_name(settings) + + def create(final): + source_path = ports.get_dir('sdl2_mixer', 'SDL_mixer-' + TAG) + flags = [ + '-sUSE_SDL=2', + '-O2', + '-DMUSIC_WAV', + ] + + if "ogg" in settings.SDL2_MIXER_FORMATS: + flags += [ + '-sUSE_VORBIS', + '-DMUSIC_OGG', + ] + + if "mp3" in settings.SDL2_MIXER_FORMATS: + flags += [ + '-sUSE_MPG123', + '-DMUSIC_MP3_MPG123', + ] + + if "mod" in settings.SDL2_MIXER_FORMATS: + flags += [ + '-sUSE_MODPLUG', + '-DMUSIC_MOD_MODPLUG', + ] + + if "mid" in settings.SDL2_MIXER_FORMATS: + flags += [ + '-DMUSIC_MID_TIMIDITY', + ] + + if settings.PTHREADS: + flags.append('-pthread') + + build_dir = ports.clear_project_build('sdl2_mixer') + include_path = os.path.join(source_path, 'include') + includes = [ + include_path, + os.path.join(source_path, 'src'), + os.path.join(source_path, 'src', 'codecs') + ] + ports.build_port( + source_path, + final, + build_dir, + flags=flags, + exclude_files=[ + 'playmus.c', + 'playwave.c', + 'main.c', + ], + exclude_dirs=[ + 'native_midi', + 'external', + 'Xcode', + ], + includes=includes, + ) + + ports.install_headers(include_path, target='SDL2') + + return [shared.cache.get_lib(libname, create, what='port')] + + +def clear(ports, settings, shared): + shared.cache.erase_lib(get_lib_name(settings)) + + +def process_dependencies(settings): + settings.USE_SDL = 2 + if "ogg" in settings.SDL2_MIXER_FORMATS: + deps.append('vorbis') + settings.USE_VORBIS = 1 + if "mp3" in settings.SDL2_MIXER_FORMATS: + deps.append('mpg123') + settings.USE_MPG123 = 1 + if "mod" in settings.SDL2_MIXER_FORMATS: + deps.append('libmodplug') + settings.USE_MODPLUG = 1 + + +def show(): + return 'sdl2_mixer (-sUSE_SDL_MIXER=2 or --use-port=sdl2_mixer; zlib license)' diff --git a/src/dist/Makefile b/src/dist/Makefile index 4e1c76db630..b8d4dce37fb 100644 --- a/src/dist/Makefile +++ b/src/dist/Makefile @@ -159,7 +159,11 @@ ifndef FHEROES2_WITH_SYSTEM_SMACKER $(MAKE) -C thirdparty/libsmacker CCFLAGS="$(CCFLAGS_TP)" CFLAGS="$(CFLAGS_TP)" CXXFLAGS="$(CXXFLAGS_TP)" CPPFLAGS="$(CPPFLAGS_TP)" endif $(MAKE) -C engine +ifeq ($(PLATFORM),web) + $(MAKE) -C web +else $(MAKE) -C fheroes2 +endif ifdef FHEROES2_WITH_TOOLS $(MAKE) -C tools endif @@ -169,5 +173,9 @@ ifndef FHEROES2_WITH_SYSTEM_SMACKER $(MAKE) -C thirdparty/libsmacker clean endif $(MAKE) -C engine clean +ifeq ($(PLATFORM),web) + $(MAKE) -C web clean +else $(MAKE) -C fheroes2 clean +endif $(MAKE) -C tools clean diff --git a/src/dist/Makefile.web b/src/dist/Makefile.web new file mode 100644 index 00000000000..aea8d5329a4 --- /dev/null +++ b/src/dist/Makefile.web @@ -0,0 +1,40 @@ +########################################################################### +# fheroes2: https://github.com/ihhub/fheroes2 # +# Copyright (C) 2021 - 2024 # +# # +# This program 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 2 of the License, or # +# (at your option) any later version. # +# # +# This program 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 this program; if not, write to the # +# Free Software Foundation, Inc., # +# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # +########################################################################### + +CCFLAGS := $(filter-out -pthread,$(CCFLAGS)) --use-port=sdl2_mixer \ + --use-port=sdl2 \ + --use-port=zlib + +LDFLAGS := $(filter-out -pthread,$(LDFLAGS)) -sENVIRONMENT=web \ + --preload-file ../../../files/data/resurrection.h2d@/files/data/resurrection.h2d \ + --preload-file ../../../files/lang/@/files/lang/ \ + --preload-file ../../../files/soundfonts/fheroes2.sf3@/files/soundfonts/fheroes2.sf3 \ + --preload-file ../../../data/@/data/ \ + -sSTACK_SIZE=262144 \ + -sINITIAL_MEMORY=64mb \ + -sENVIRONMENT=web \ + -sASYNCIFY \ + -sASYNCIFY_STACK_SIZE=20480 \ + -lidbfs.js + +ifdef FHEROES2_WITH_DEBUG +LDFLAGS := $(LDFLAGS) -gsource-map +CCFLAGS := $(CCFLAGS) -gsource-map +endif diff --git a/src/dist/web/Makefile b/src/dist/web/Makefile new file mode 100644 index 00000000000..f08c7ebdce2 --- /dev/null +++ b/src/dist/web/Makefile @@ -0,0 +1,55 @@ +########################################################################### +# fheroes2: https://github.com/ihhub/fheroes2 # +# Copyright (C) 2021 - 2024 # +# # +# This program 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 2 of the License, or # +# (at your option) any later version. # +# # +# This program 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 this program; if not, write to the # +# Free Software Foundation, Inc., # +# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # +########################################################################### + +DEPLIBS := ../engine/libengine.a +CCFLAGS := $(CCFLAGS) -I../../engine + +ifndef FHEROES2_WITH_SYSTEM_SMACKER +DEPLIBS := $(DEPLIBS) ../thirdparty/libsmacker/libsmacker.a +CCFLAGS := $(CCFLAGS) -I../../thirdparty/libsmacker +endif + +SOURCEROOT := ../../fheroes2 +SOURCEDIRS := $(filter %/,$(wildcard $(SOURCEROOT)/*/)) +SOURCES := $(wildcard $(SOURCEROOT)/*/*.cpp) + +VPATH := $(SOURCEDIRS) + +.PHONY: all clean + +all: fheroes2.js + +fheroes2.js: $(notdir $(patsubst %.cpp, %.o, $(SOURCES))) $(DEPLIBS) + xgettext -d fheroes2.js --language=JavaScript -F -k_ -k_n:1,2 -o fheroes2.pot $(sort $(SOURCES)) + $(MAKE) -C ../../../files/lang + $(CXX) -o $@ $^ $(LIBS) $(LDFLAGS) + rm -f *.d *.o *.tmp *.pot~ *.pot + mkdir -p dist + mv -f fheroes2.* dist + cp ../../../files/web/dist/* dist + +%.o: %.cpp + $(CXX) -c -MD $< $(addprefix -I, $(SOURCEDIRS)) $(CCFLAGS) $(CXXFLAGS) $(CPPFLAGS) + +include $(wildcard *.d) + +clean: + rm -f *.d *.o fheroes2.js fheroes2.wasm fheroes2.wasm.* fheroes2.data fheroes2.pot fheroes2.pot~ + rm -rf dist diff --git a/src/engine/thread.cpp b/src/engine/thread.cpp index 10f86736900..d1b85419443 100644 --- a/src/engine/thread.cpp +++ b/src/engine/thread.cpp @@ -29,13 +29,14 @@ namespace MultiThreading { if ( !_worker ) { _runFlag = true; +#if !defined( __EMSCRIPTEN__ ) // disable pthread for web target _worker = std::make_unique( AsyncManager::_workerThread, this ); - { std::unique_lock lock( _mutex ); _masterNotification.wait( lock, [this] { return !_runFlag; } ); } +#endif } } diff --git a/src/fheroes2/game/fheroes2.cpp b/src/fheroes2/game/fheroes2.cpp index 10c68533628..51af2b7e988 100644 --- a/src/fheroes2/game/fheroes2.cpp +++ b/src/fheroes2/game/fheroes2.cpp @@ -337,10 +337,11 @@ int main( int argc, char ** argv ) if ( conf.isShowIntro() ) { fheroes2::showTeamInfo(); - +#if !defined( __EMSCRIPTEN__ ) Video::ShowVideo( "NWCLOGO.SMK", Video::VideoAction::PLAY_TILL_VIDEO_END ); Video::ShowVideo( "CYLOGO.SMK", Video::VideoAction::PLAY_TILL_VIDEO_END ); Video::ShowVideo( "H2XINTRO.SMK", Video::VideoAction::PLAY_TILL_VIDEO_END ); +#endif } try { diff --git a/src/fheroes2/game/game_hotkeys.cpp b/src/fheroes2/game/game_hotkeys.cpp index be067217449..d86f21c2b35 100644 --- a/src/fheroes2/game/game_hotkeys.cpp +++ b/src/fheroes2/game/game_hotkeys.cpp @@ -51,6 +51,10 @@ #include "ui_dialog.h" #include "ui_language.h" +#ifdef __EMSCRIPTEN__ +#include +#endif + namespace { struct HotKeyEventInfo @@ -463,6 +467,9 @@ void Game::HotKeySave() const std::string & data = getHotKeyFileContent(); file.write( data.data(), data.size() ); +#ifdef __EMSCRIPTEN__ + EM_ASM(FS.syncfs(err => err && console.warn("Error saving:", err))); +#endif } void Game::globalKeyDownEvent( const fheroes2::Key key, const int32_t modifier ) diff --git a/src/fheroes2/game/game_io.cpp b/src/fheroes2/game/game_io.cpp index b4e9c0d7bb0..4293da6a0d4 100644 --- a/src/fheroes2/game/game_io.cpp +++ b/src/fheroes2/game/game_io.cpp @@ -47,6 +47,10 @@ #include "world.h" #include "zzlib.h" +#ifdef __EMSCRIPTEN__ +#include +#endif + namespace { const std::string autoSaveName{ "AUTOSAVE" }; @@ -154,7 +158,9 @@ bool Game::Save( const std::string & filePath, const bool autoSave /* = false */ if ( !autoSave ) { Game::SetLastSaveName( filePath ); } - +#ifdef __EMSCRIPTEN__ + EM_ASM(FS.syncfs(err => err && console.warn("Error saving:", err))); +#endif return true; } diff --git a/src/fheroes2/system/settings.cpp b/src/fheroes2/system/settings.cpp index 20d5e9636ed..b2447177968 100644 --- a/src/fheroes2/system/settings.cpp +++ b/src/fheroes2/system/settings.cpp @@ -47,6 +47,10 @@ #include "ui_language.h" #include "version.h" +#ifdef __EMSCRIPTEN__ +#include +#endif + #define STRINGIFY( DEF ) #DEF #define EXPANDDEF( DEF ) STRINGIFY( DEF ) @@ -358,7 +362,9 @@ bool Settings::Save( const std::string_view fileName ) const const std::string & data = String(); file.write( data.data(), data.size() ); - +#ifdef __EMSCRIPTEN__ + EM_ASM( FS.syncfs( err = > err && console.warn( "Error saving:", err ) ) ); +#endif return true; }