From 5210d7ec0c5972835c1df2eae5e34146a7eccb9e Mon Sep 17 00:00:00 2001 From: Roman Turchin Date: Fri, 30 May 2025 22:22:35 -0500 Subject: [PATCH 01/11] [emscripten] initial build --- CMakeLists.txt | 16 ++++++++++------ README.md | 8 ++++++++ external/CMakeLists.txt | 2 +- extras/ai-battle/CMakeLists.txt | 1 + extras/audioDrivers/SDL/CMakeLists.txt | 13 ++++++++++--- libs/libGamedata/CMakeLists.txt | 5 +++-- libs/s25client/CMakeLists.txt | 7 ++++++- 7 files changed, 39 insertions(+), 13 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 56d4b12ffb..e80c8c6470 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,7 +27,7 @@ if(CMAKE_VERSION VERSION_LESS 3.14) list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/external/libutil/cmake/cmake_3.14") endif() -include(EnableCCache) +#include(EnableCCache) set(checkSubmodules FALSE) # Figure out RTTR_REVISION (git hash) and RTTR_VERSION (date) @@ -297,16 +297,18 @@ include(RttrTestingCfg) set(rttrContribBoostDir ${CMAKE_CURRENT_SOURCE_DIR}/contrib/boost) if(EXISTS ${rttrContribBoostDir} AND IS_DIRECTORY ${rttrContribBoostDir}) - set(BOOST_ROOT ${rttrContribBoostDir} CACHE PATH "Path to find boost at") + set(BOOST_ROOT "${rttrContribBoostDir}" CACHE PATH "Path to find boost at") + set(Boost_INCLUDE_DIR "${rttrContribBoostDir}/include" CACHE PATH "Path to find boost at") + set(Boost_LIBRARY_DIR "${rttrContribBoostDir}/lib" CACHE PATH "Path to find boost at") endif() set(BoostPackages filesystem iostreams locale program_options) if(BUILD_TESTING) # Avoid having to search for this in all tests speeding up configure - list(APPEND BoostPackages unit_test_framework) + #list(APPEND BoostPackages unit_test_framework) endif() -find_package(Boost 1.71 COMPONENTS ${BoostPackages}) +find_package(Boost 1.86 COMPONENTS ${BoostPackages}) if(NOT Boost_FOUND) message(FATAL_ERROR "You have to install boost (>=1.71) into contrib/boost or set (as CMake or environment variable) " "BOOST_ROOT (currently: '${BOOST_ROOT}', Environment: '$ENV{BOOST_ROOT}'), " @@ -315,7 +317,7 @@ if(NOT Boost_FOUND) endif() option(RTTR_USE_SYSTEM_BOOST_NOWIDE "Use system installed Boost.Nowide. Fails if not found!" "${RTTR_USE_SYSTEM_LIBS}") - +set(RTTR_USE_SYSTEM_BOOST_NOWIDE ON) if(Boost_VERSION_MINOR GREATER_EQUAL "74" OR RTTR_USE_SYSTEM_BOOST_NOWIDE) # Boost 1.73 contains Boost.Nowide, 1.74 a required fix find_package(Boost COMPONENTS nowide) @@ -335,6 +337,8 @@ add_subdirectory(external) add_subdirectory(libs) add_subdirectory(extras) +if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") +else () find_package(Gettext REQUIRED) file(GLOB RTTR_PO_FILES external/languages/*.po) set(RTTR_TRANSLATION_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/gen/languages) # Used by copyDepsToBuildDir @@ -353,7 +357,7 @@ endif() configure_file(copyDepsToBuildDir.cmake . @ONLY) add_custom_command(TARGET translations POST_BUILD COMMAND ${CMAKE_COMMAND} -D CMAKE_BUILD_TYPE=$ -P ${CMAKE_CURRENT_BINARY_DIR}/copyDepsToBuildDir.cmake) - +endif () if(ClangFormat_FOUND) add_clangformat_folder(${CMAKE_CURRENT_SOURCE_DIR}/libs TRUE) add_clangformat_folder(${CMAKE_CURRENT_SOURCE_DIR}/extras TRUE) diff --git a/README.md b/README.md index b92e100024..5675c48d9e 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,14 @@ Copyright (C) 2005 - 2025 Settlers Freaks SPDX-License-Identifier: GPL-2.0-or-later --> +# emscripten build (wasm) + +1. checkout and build `boost-1.86.0` with following command +```bash +docker run --rm -v $(pwd):/src emscripten-image sh -c './b2 install toolset=emscripten link=static runtime-link=static variant=release -j 1 --prefix=/src/contrib/boost --with-program_options --with-nowide --with-filesystem --with-iostreams --with-locale --with-thread --with-system --with-regex' +``` +2. download lua `lua-5.4.7` update config its Makefile add both C and LD flags `-fwasm-exceptions -sSUPPORT_LONGJMP=wasm` and then build +3. run standard build # Return To The Roots diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 29b9de5b03..656cad917e 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -51,7 +51,7 @@ add_subdirectory(liblobby) add_subdirectory(libsiedler2) add_subdirectory(libutil) add_subdirectory(mygettext) -add_subdirectory(s25edit) +#add_subdirectory(s25edit) if(RTTR_BUILD_UPDATER) add_subdirectory(s25update) endif() diff --git a/extras/ai-battle/CMakeLists.txt b/extras/ai-battle/CMakeLists.txt index b10b41b441..8fff7ff952 100644 --- a/extras/ai-battle/CMakeLists.txt +++ b/extras/ai-battle/CMakeLists.txt @@ -3,6 +3,7 @@ # SPDX-License-Identifier: GPL-2.0-or-later add_executable(ai-battle main.cpp HeadlessGame.cpp) +target_link_options(ai-battle PRIVATE "-fwasm-exceptions") target_link_libraries(ai-battle PRIVATE s25Main Boost::program_options Boost::nowide) if(WIN32) diff --git a/extras/audioDrivers/SDL/CMakeLists.txt b/extras/audioDrivers/SDL/CMakeLists.txt index d0db8ee59b..10699550c2 100644 --- a/extras/audioDrivers/SDL/CMakeLists.txt +++ b/extras/audioDrivers/SDL/CMakeLists.txt @@ -3,8 +3,10 @@ # SPDX-License-Identifier: GPL-2.0-or-later set(SDL_BUILDING_LIBRARY ON) +if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") +else () find_package(SDL_mixer 2.0.1) - +endif () if(NOT SDL_MIXER_FOUND) message(WARNING "SDL_mixer library not found: Not building SDL audiodriver") else() @@ -16,8 +18,13 @@ else() gather_dll_by_name(OGG libogg-0.dll) endif() - add_library(audioSDL SHARED ${RTTR_DRIVER_INTERFACE} AudioSDL.cpp AudioSDL.h) - target_link_libraries(audioSDL PRIVATE audiodrv SDL_mixer::SDL_mixer) + if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") + add_library(audioSDL STATIC ${RTTR_DRIVER_INTERFACE} AudioSDL.cpp AudioSDL.h) + target_link_libraries(audioSDL PRIVATE audiodrv) + else () + add_library(audioSDL SHARED ${RTTR_DRIVER_INTERFACE} AudioSDL.cpp AudioSDL.h) + target_link_libraries(audioSDL PRIVATE audiodrv SDL_mixer::SDL_mixer) + endif () enable_warnings(audioSDL) install(TARGETS audioSDL diff --git a/libs/libGamedata/CMakeLists.txt b/libs/libGamedata/CMakeLists.txt index eff5db1cd7..85ca93bc96 100644 --- a/libs/libGamedata/CMakeLists.txt +++ b/libs/libGamedata/CMakeLists.txt @@ -15,8 +15,9 @@ AddDirectory(lua) file(GLOB SOURCES_OTHER *.cpp *.h) source_group(other FILES ${SOURCES_OTHER}) -find_package(Lua 5.1 REQUIRED) - +#find_package(Lua 5.1 REQUIRED) +set(LUA_LIBRARIES /src/contrib/lua-5.4.7/build/lib/liblua.a) +set(LUA_INCLUDE_DIR /src/contrib/lua-5.4.7/build/include) include(GatherDll) gather_dll(Lua) diff --git a/libs/s25client/CMakeLists.txt b/libs/s25client/CMakeLists.txt index a29b0eed0f..201bc157ab 100644 --- a/libs/s25client/CMakeLists.txt +++ b/libs/s25client/CMakeLists.txt @@ -1,7 +1,12 @@ # Copyright (C) 2005 - 2021 Settlers Freaks # # SPDX-License-Identifier: GPL-2.0-or-later - +if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") + set(USE_FLAGS "-fwasm-exceptions -sSUPPORT_LONGJMP=wasm") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${USE_FLAGS}") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${USE_FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${USE_FLAGS}") +endif () # Add Icon to Application if(WIN32) set(rcDir ${PROJECT_SOURCE_DIR}/data/win32) From 5a275078640d693f3918d0f2980b0690f5de3740 Mon Sep 17 00:00:00 2001 From: Roman Turchin Date: Sat, 31 May 2025 13:31:36 -0500 Subject: [PATCH 02/11] [emscripten] modularize; emscripten main loop --- CMakeLists.txt | 4 +--- libs/s25client/CMakeLists.txt | 13 ++++++++++++- libs/s25client/s25client.cpp | 17 +++++++++++++++-- 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e80c8c6470..434c44321e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -337,8 +337,6 @@ add_subdirectory(external) add_subdirectory(libs) add_subdirectory(extras) -if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") -else () find_package(Gettext REQUIRED) file(GLOB RTTR_PO_FILES external/languages/*.po) set(RTTR_TRANSLATION_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/gen/languages) # Used by copyDepsToBuildDir @@ -357,7 +355,7 @@ endif() configure_file(copyDepsToBuildDir.cmake . @ONLY) add_custom_command(TARGET translations POST_BUILD COMMAND ${CMAKE_COMMAND} -D CMAKE_BUILD_TYPE=$ -P ${CMAKE_CURRENT_BINARY_DIR}/copyDepsToBuildDir.cmake) -endif () + if(ClangFormat_FOUND) add_clangformat_folder(${CMAKE_CURRENT_SOURCE_DIR}/libs TRUE) add_clangformat_folder(${CMAKE_CURRENT_SOURCE_DIR}/extras TRUE) diff --git a/libs/s25client/CMakeLists.txt b/libs/s25client/CMakeLists.txt index 201bc157ab..356ab807ee 100644 --- a/libs/s25client/CMakeLists.txt +++ b/libs/s25client/CMakeLists.txt @@ -5,7 +5,18 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") set(USE_FLAGS "-fwasm-exceptions -sSUPPORT_LONGJMP=wasm") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${USE_FLAGS}") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${USE_FLAGS}") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${USE_FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${USE_FLAGS} \ + -sALLOW_MEMORY_GROWTH -sINITIAL_MEMORY=128mb -sMEMORY_GROWTH_LINEAR_STEP=32mb \ + -sSTACK_SIZE=1024kb \ + -sENVIRONMENT=web,worker \ + -sPTHREAD_POOL_SIZE=4 -sPTHREAD_POOL_SIZE_STRICT=0 \ + -sMALLOC=mimalloc \ + -sMODULARIZE=1 \ + -sCASE_INSENSITIVE_FS \ + -sEXPORTED_RUNTIME_METHODS=callMain,addRunDependency,removeRunDependency \ + -sEXPORT_ALL \ + -sEXIT_RUNTIME \ + -lwebsocket.js -lidbfs.js") endif () # Add Icon to Application if(WIN32) diff --git a/libs/s25client/s25client.cpp b/libs/s25client/s25client.cpp index d6063849a2..4e510eb61c 100644 --- a/libs/s25client/s25client.cpp +++ b/libs/s25client/s25client.cpp @@ -50,6 +50,17 @@ # include #endif +#ifdef __EMSCRIPTEN__ +# include +void main_loop() +{ + if (!GAMEMANAGER.Run()) + { + emscripten_cancel_main_loop(); + } +} +#endif + namespace bfs = boost::filesystem; namespace bnw = boost::nowide; namespace po = boost::program_options; @@ -484,14 +495,16 @@ int RunProgram(po::variables_map& options) } // Hauptschleife - +#ifdef __EMSCRIPTEN__ + emscripten_set_main_loop(&main_loop, 0, true); +#else while(gameManager.Run()) { #ifndef _WIN32 killme = false; #endif // !_WIN32 } - +#endif // Spiel beenden gameManager.Stop(); libsiedler2::setAllocator(nullptr); From 4d138dc32389f01a1688596eb2d10c22c87a847b Mon Sep 17 00:00:00 2001 From: Roman Turchin Date: Wed, 4 Jun 2025 02:21:02 +0200 Subject: [PATCH 03/11] [emscripten] link drivers statically --- CMakeLists.txt | 2 +- extras/audioDrivers/SDL/AudioSDL.cpp | 4 +-- extras/audioDrivers/SDL/CMakeLists.txt | 6 ++++ extras/videoDrivers/SDL2/CMakeLists.txt | 19 ++++++++-- extras/videoDrivers/SDL2/VideoSDL2.cpp | 11 ++++-- libs/rttrConfig/src/RttrConfig.cpp | 3 +- libs/s25client/CMakeLists.txt | 39 +++++++++++---------- libs/s25client/s25client.cpp | 4 +++ libs/s25main/CMakeLists.txt | 9 +++++ libs/s25main/GameManager.cpp | 8 +++++ libs/s25main/drivers/AudioDriverWrapper.cpp | 10 ++++++ libs/s25main/drivers/AudioDriverWrapper.h | 1 + libs/s25main/drivers/VideoDriverWrapper.cpp | 15 ++++++++ libs/s25main/drivers/VideoDriverWrapper.h | 1 + 14 files changed, 105 insertions(+), 27 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 434c44321e..ff7fdf24fe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -305,7 +305,7 @@ endif() set(BoostPackages filesystem iostreams locale program_options) if(BUILD_TESTING) # Avoid having to search for this in all tests speeding up configure - #list(APPEND BoostPackages unit_test_framework) + list(APPEND BoostPackages unit_test_framework) endif() find_package(Boost 1.86 COMPONENTS ${BoostPackages}) diff --git a/extras/audioDrivers/SDL/AudioSDL.cpp b/extras/audioDrivers/SDL/AudioSDL.cpp index f3b1b1f44d..bf51bf90e7 100644 --- a/extras/audioDrivers/SDL/AudioSDL.cpp +++ b/extras/audioDrivers/SDL/AudioSDL.cpp @@ -29,7 +29,7 @@ void FreeAudioInstance(driver::IAudioDriver* driver) delete driver; } -const char* GetDriverName() +const char* GetAudioDriverName() { return "(SDL2) Audio via SDL2_mixer-Library"; } @@ -62,7 +62,7 @@ AudioSDL::~AudioSDL() */ const char* AudioSDL::GetName() const { - return GetDriverName(); + return GetAudioDriverName(); } static AudioSDL* currentInstance = nullptr; diff --git a/extras/audioDrivers/SDL/CMakeLists.txt b/extras/audioDrivers/SDL/CMakeLists.txt index 10699550c2..ba70507b93 100644 --- a/extras/audioDrivers/SDL/CMakeLists.txt +++ b/extras/audioDrivers/SDL/CMakeLists.txt @@ -4,6 +4,7 @@ set(SDL_BUILDING_LIBRARY ON) if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") + set(SDL_MIXER_FOUND TRUE) else () find_package(SDL_mixer 2.0.1) endif () @@ -20,6 +21,11 @@ else() if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") add_library(audioSDL STATIC ${RTTR_DRIVER_INTERFACE} AudioSDL.cpp AudioSDL.h) + set(USE_FLAGS "--use-port=sdl2_mixer -sSDL2_MIXER_FORMATS=ogg") + set_target_properties(audioSDL PROPERTIES + COMPILE_FLAGS "${USE_FLAGS}" + LINK_FLAGS "${USE_FLAGS}" + ) target_link_libraries(audioSDL PRIVATE audiodrv) else () add_library(audioSDL SHARED ${RTTR_DRIVER_INTERFACE} AudioSDL.cpp AudioSDL.h) diff --git a/extras/videoDrivers/SDL2/CMakeLists.txt b/extras/videoDrivers/SDL2/CMakeLists.txt index 1c89931c66..1772f5ee4e 100644 --- a/extras/videoDrivers/SDL2/CMakeLists.txt +++ b/extras/videoDrivers/SDL2/CMakeLists.txt @@ -3,11 +3,24 @@ # SPDX-License-Identifier: GPL-2.0-or-later set(SDL2_BUILDING_LIBRARY ON) +if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") + set(SDL2_FOUND TRUE) +else () find_package(SDL2 2.0.5) - +endif () if(SDL2_FOUND) - add_library(videoSDL2 SHARED ${RTTR_DRIVER_INTERFACE} VideoSDL2.cpp VideoSDL2.h icon.h icon.cpp) - target_link_libraries(videoSDL2 PRIVATE videodrv s25util::common glad Boost::nowide SDL2::SDL2) + if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") + add_library(videoSDL2 STATIC ${RTTR_DRIVER_INTERFACE} VideoSDL2.cpp VideoSDL2.h icon.h icon.cpp) + set(USE_FLAGS "--use-port=sdl2 --use-port=sdl2_image") + set_target_properties(videoSDL2 PROPERTIES + COMPILE_FLAGS "${USE_FLAGS}" + LINK_FLAGS "${USE_FLAGS}" + ) + target_link_libraries(videoSDL2 PRIVATE videodrv s25util::common glad Boost::nowide) + else () + add_library(videoSDL2 SHARED ${RTTR_DRIVER_INTERFACE} VideoSDL2.cpp VideoSDL2.h icon.h icon.cpp) + target_link_libraries(videoSDL2 PRIVATE videodrv s25util::common glad Boost::nowide SDL2::SDL2) + endif () enable_warnings(videoSDL2) if(WIN32) diff --git a/extras/videoDrivers/SDL2/VideoSDL2.cpp b/extras/videoDrivers/SDL2/VideoSDL2.cpp index 3f06c5c206..4a62d43a3e 100644 --- a/extras/videoDrivers/SDL2/VideoSDL2.cpp +++ b/extras/videoDrivers/SDL2/VideoSDL2.cpp @@ -54,7 +54,7 @@ void FreeVideoInstance(IVideoDriver* driver) delete driver; } -const char* GetDriverName() +const char* GetVideoDriverName() { return "(SDL2) OpenGL via SDL2-Library"; } @@ -68,7 +68,7 @@ VideoSDL2::~VideoSDL2() const char* VideoSDL2::GetName() const { - return GetDriverName(); + return GetVideoDriverName(); } bool VideoSDL2::Initialize() @@ -112,6 +112,7 @@ bool VideoSDL2::CreateScreen(const std::string& title, const VideoMode& size, bo if(!initialized) return false; +#ifndef __EMSCRIPTEN__ // GL-Attributes CHECK_SDL(SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, RTTR_OGL_MAJOR)); CHECK_SDL(SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, RTTR_OGL_MINOR)); @@ -128,6 +129,7 @@ bool VideoSDL2::CreateScreen(const std::string& title, const VideoMode& size, bo CHECK_SDL(SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8)); CHECK_SDL(SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8)); CHECK_SDL(SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1)); +#endif int wndPos = SDL_WINDOWPOS_CENTERED; @@ -170,6 +172,11 @@ bool VideoSDL2::CreateScreen(const std::string& title, const VideoMode& size, bo context = SDL_GL_CreateContext(window); + if(!context) + { + PrintError(SDL_GetError()); + return false; + } #ifdef _WIN32 SetWindowTextW(GetConsoleWindow(), boost::nowide::widen(title).c_str()); #endif diff --git a/libs/rttrConfig/src/RttrConfig.cpp b/libs/rttrConfig/src/RttrConfig.cpp index d6a6da1fb5..f68d85b7df 100644 --- a/libs/rttrConfig/src/RttrConfig.cpp +++ b/libs/rttrConfig/src/RttrConfig.cpp @@ -37,6 +37,7 @@ bfs::path RttrConfig::GetPrefixPath() // Get path to current executable (at least for checks) bfs::path fullExeFilepath = System::getExecutablePath(); // This should always work unless we have some missing implementation or a bad error +#ifndef __EMSCRIPTEN__ if(fullExeFilepath.empty()) { LOG.write("Could not get path to current executable\n", LogTarget::Stderr); @@ -47,7 +48,7 @@ bfs::path RttrConfig::GetPrefixPath() LOG.write("Executable not at '%1%'\n", LogTarget::Stderr) % fullExeFilepath; return ""; } - +#endif bfs::path rttrBinDir(RTTR_BINDIR); // Allow overwrite with RTTR_PREFIX_DIR diff --git a/libs/s25client/CMakeLists.txt b/libs/s25client/CMakeLists.txt index 356ab807ee..2e3340a37c 100644 --- a/libs/s25client/CMakeLists.txt +++ b/libs/s25client/CMakeLists.txt @@ -1,23 +1,7 @@ # Copyright (C) 2005 - 2021 Settlers Freaks # # SPDX-License-Identifier: GPL-2.0-or-later -if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") - set(USE_FLAGS "-fwasm-exceptions -sSUPPORT_LONGJMP=wasm") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${USE_FLAGS}") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${USE_FLAGS}") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${USE_FLAGS} \ - -sALLOW_MEMORY_GROWTH -sINITIAL_MEMORY=128mb -sMEMORY_GROWTH_LINEAR_STEP=32mb \ - -sSTACK_SIZE=1024kb \ - -sENVIRONMENT=web,worker \ - -sPTHREAD_POOL_SIZE=4 -sPTHREAD_POOL_SIZE_STRICT=0 \ - -sMALLOC=mimalloc \ - -sMODULARIZE=1 \ - -sCASE_INSENSITIVE_FS \ - -sEXPORTED_RUNTIME_METHODS=callMain,addRunDependency,removeRunDependency \ - -sEXPORT_ALL \ - -sEXIT_RUNTIME \ - -lwebsocket.js -lidbfs.js") -endif () + # Add Icon to Application if(WIN32) set(rcDir ${PROJECT_SOURCE_DIR}/data/win32) @@ -35,7 +19,26 @@ else() endif() add_executable(s25client s25client.cpp commands.cpp ${s25client_RC}) -target_link_libraries(s25client PRIVATE s25Main Boost::program_options Boost::nowide rttr::vld) +if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") + set(USE_FLAGS "-fwasm-exceptions \ + --use-port=sdl2 --use-port=sdl2_image --use-port=sdl2_mixer -sSDL2_MIXER_FORMATS=ogg") + + set_target_properties(s25client PROPERTIES + COMPILE_FLAGS "${USE_FLAGS}" + LINK_FLAGS "${USE_FLAGS} \ + -sALLOW_MEMORY_GROWTH -sINITIAL_MEMORY=128mb -sMEMORY_GROWTH_LINEAR_STEP=32mb \ + -sSTACK_SIZE=1024kb \ + -sENVIRONMENT=web \ + -sMODULARIZE \ + -sEXPORT_ALL -sEXPORTED_RUNTIME_METHODS=callMain,addRunDependency,removeRunDependency \ + -sEXIT_RUNTIME \ + -lwebsocket.js -lidbfs.js -sCASE_INSENSITIVE_FS" + SUFFIX ".js" + ) + target_link_libraries(s25client PRIVATE s25Main Boost::program_options rttr::vld) +else () + target_link_libraries(s25client PRIVATE s25Main Boost::program_options Boost::nowide rttr::vld) +endif () add_dependencies(s25client drivers) if(WIN32) diff --git a/libs/s25client/s25client.cpp b/libs/s25client/s25client.cpp index 4e510eb61c..991eda2c46 100644 --- a/libs/s25client/s25client.cpp +++ b/libs/s25client/s25client.cpp @@ -52,6 +52,9 @@ #ifdef __EMSCRIPTEN__ # include +EM_JS(void, main_ready, (), { + Module?.gameReady?.(); +}); void main_loop() { if (!GAMEMANAGER.Run()) @@ -496,6 +499,7 @@ int RunProgram(po::variables_map& options) // Hauptschleife #ifdef __EMSCRIPTEN__ + main_ready(); emscripten_set_main_loop(&main_loop, 0, true); #else while(gameManager.Run()) diff --git a/libs/s25main/CMakeLists.txt b/libs/s25main/CMakeLists.txt index 0d42d75cef..26d180cb2f 100644 --- a/libs/s25main/CMakeLists.txt +++ b/libs/s25main/CMakeLists.txt @@ -88,6 +88,15 @@ if(LIBRT) target_link_libraries(s25Main PUBLIC ${LIBRT}) endif() +if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") + set(USE_FLAGS "--use-port=sdl2 --use-port=sdl2_image --use-port=sdl2_mixer -sSDL2_MIXER_FORMATS=ogg") + set_target_properties(s25Main PROPERTIES + COMPILE_FLAGS "${USE_FLAGS}" + LINK_FLAGS "${USE_FLAGS}" + ) + target_link_libraries(s25Main PUBLIC videoSDL2 audioSDL) +endif () + if(CMAKE_SYSTEM_NAME STREQUAL "Linux") target_link_libraries(s25Main PUBLIC ${CMAKE_DL_LIBS}) # For dynamic driver loading (DriverWrapper) endif() diff --git a/libs/s25main/GameManager.cpp b/libs/s25main/GameManager.cpp index 4c33ee0552..bf8eaa0f50 100644 --- a/libs/s25main/GameManager.cpp +++ b/libs/s25main/GameManager.cpp @@ -41,7 +41,11 @@ bool GameManager::Start() settings_.Load(); /// Videotreiber laden +#ifdef __EMSCRIPTEN__ + if(!videoDriver_.LoadDriver()) +#else if(!videoDriver_.LoadDriver(settings_.driver.video)) +#endif { s25util::error(_("Video driver couldn't be loaded!")); return false; @@ -57,7 +61,11 @@ bool GameManager::Start() videoDriver_.setGuiScalePercent(settings_.video.guiScale); /// Audiodriver laden +#ifdef __EMSCRIPTEN__ + if(!audioDriver_.LoadDriver()) +#else if(!audioDriver_.LoadDriver(settings_.driver.audio)) +#endif { s25util::warning(_("Audio driver couldn't be loaded!")); // return false; diff --git a/libs/s25main/drivers/AudioDriverWrapper.cpp b/libs/s25main/drivers/AudioDriverWrapper.cpp index 41f813bf8c..9e8a24f45d 100644 --- a/libs/s25main/drivers/AudioDriverWrapper.cpp +++ b/libs/s25main/drivers/AudioDriverWrapper.cpp @@ -14,6 +14,10 @@ #include #include +#ifdef __EMSCRIPTEN__ +#include "../../../extras/audioDrivers/SDL/AudioSDL.h" +#endif + using ovectorstream = boost::interprocess::basic_ovectorstream>; AudioDriverWrapper::AudioDriverWrapper() : audiodriver_(nullptr, nullptr) {} @@ -90,6 +94,12 @@ bool AudioDriverWrapper::LoadDriver(std::unique_ptr audioD return Init(); } +bool AudioDriverWrapper::LoadDriver() +{ + audiodriver_ = Handle(CreateAudioInstance(this, VIDEODRIVER.GetMapPointer()), &FreeAudioInstance); + return Init(); +} + bool AudioDriverWrapper::LoadDriver(std::string& preference) { RTTR_Assert(!audiodriver_); diff --git a/libs/s25main/drivers/AudioDriverWrapper.h b/libs/s25main/drivers/AudioDriverWrapper.h index 65602d292f..a4218f2a8a 100644 --- a/libs/s25main/drivers/AudioDriverWrapper.h +++ b/libs/s25main/drivers/AudioDriverWrapper.h @@ -36,6 +36,7 @@ class AudioDriverWrapper : /// Loads the driver bool LoadDriver(std::unique_ptr audioDriver); bool LoadDriver(std::string& preference); + bool LoadDriver(); /// Unloads the driver resetting all open handles void UnloadDriver(); diff --git a/libs/s25main/drivers/VideoDriverWrapper.cpp b/libs/s25main/drivers/VideoDriverWrapper.cpp index 7daa4801cf..6af4ad4326 100644 --- a/libs/s25main/drivers/VideoDriverWrapper.cpp +++ b/libs/s25main/drivers/VideoDriverWrapper.cpp @@ -16,6 +16,9 @@ #include "s25util/Log.h" #include "s25util/error.h" #include +#ifdef __EMSCRIPTEN__ +#include "../../../extras/videoDrivers/SDL2/VideoSDL2.h" +#endif #include #if !defined(NDEBUG) && defined(HAVE_MEMCHECK_H) # include @@ -62,6 +65,14 @@ bool VideoDriverWrapper::LoadDriver(IVideoDriver* existingDriver) return Initialize(); } +bool VideoDriverWrapper::LoadDriver() +{ + UnloadDriver(); + videodriver = Handle(CreateVideoInstance(&WINDOWMANAGER), &FreeVideoInstance); + return Initialize(); +} + + bool VideoDriverWrapper::LoadDriver(std::string& preference) { UnloadDriver(); @@ -368,6 +379,7 @@ bool VideoDriverWrapper::LoadAllExtensions() renderer_ = std::make_unique(); if(!renderer_->initOpenGL(videodriver->GetLoaderFunction())) return false; +#ifndef __EMSCRIPTEN__ LOG.write(_("OpenGL %1%.%2% supported\n")) % GLVersion.major % GLVersion.minor; if(GLVersion.major < RTTR_OGL_MAJOR || (GLVersion.major == RTTR_OGL_MAJOR && GLVersion.minor < RTTR_OGL_MINOR)) { @@ -375,6 +387,9 @@ bool VideoDriverWrapper::LoadAllExtensions() % ((RTTR_OGL_ES) ? "ES" : "") % RTTR_OGL_MAJOR % RTTR_OGL_MINOR; return false; } +#else + LOG.write(_("OpenGL ES 2.0 (%1%")) % reinterpret_cast(glGetString(GL_VERSION)); +#endif // auf VSync-Extension testen #ifdef _WIN32 diff --git a/libs/s25main/drivers/VideoDriverWrapper.h b/libs/s25main/drivers/VideoDriverWrapper.h index 8b21b9d596..570463d82f 100644 --- a/libs/s25main/drivers/VideoDriverWrapper.h +++ b/libs/s25main/drivers/VideoDriverWrapper.h @@ -32,6 +32,7 @@ class VideoDriverWrapper : public Singleton Date: Thu, 5 Jun 2025 01:26:01 +0200 Subject: [PATCH 04/11] [emscripten] runnable build --- extras/CMakeLists.txt | 3 ++ extras/videoDrivers/SDL2/CMakeLists.txt | 2 +- extras/videoDrivers/SDL2/VideoSDL2.cpp | 2 ++ libs/s25client/CMakeLists.txt | 9 ++++-- libs/s25client/s25client.cpp | 29 ++++++++++--------- libs/s25main/CMakeLists.txt | 24 ++++++++------- libs/s25main/GameManager.cpp | 2 +- libs/s25main/GameManager.h | 2 ++ libs/s25main/TerrainRenderer.cpp | 5 ++++ libs/s25main/WindowManager.cpp | 3 ++ libs/s25main/desktops/dskTextureTest.cpp | 5 ++++ libs/s25main/drivers/VideoDriverWrapper.cpp | 14 ++++++--- libs/s25main/ogl/BufferHandle.h | 5 ++++ libs/s25main/ogl/DummyRenderer.cpp | 9 ++++-- libs/s25main/ogl/OpenGLRenderer.cpp | 9 +++++- libs/s25main/ogl/VBO.h | 5 ++++ libs/s25main/ogl/constants.h | 5 ++++ libs/s25main/ogl/glArchivItem_Bitmap.cpp | 5 ++++ libs/s25main/ogl/glArchivItem_BitmapBase.cpp | 5 ++++ .../ogl/glArchivItem_Bitmap_Direct.cpp | 5 ++++ .../ogl/glArchivItem_Bitmap_Player.cpp | 5 ++++ libs/s25main/ogl/glFont.h | 5 ++++ libs/s25main/ogl/glSmartBitmap.cpp | 5 ++++ libs/s25main/ogl/glTexturePacker.cpp | 5 ++++ libs/s25main/world/GameWorldView.cpp | 5 ++++ 25 files changed, 138 insertions(+), 35 deletions(-) diff --git a/extras/CMakeLists.txt b/extras/CMakeLists.txt index edbac0174a..70c23c7cbe 100644 --- a/extras/CMakeLists.txt +++ b/extras/CMakeLists.txt @@ -11,7 +11,10 @@ set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) add_subdirectory(audioDrivers) add_subdirectory(videoDrivers) +if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") +else() add_subdirectory(ai-battle) +endif () if(RTTR_BUNDLE AND APPLE) add_subdirectory(macosLauncher) endif() diff --git a/extras/videoDrivers/SDL2/CMakeLists.txt b/extras/videoDrivers/SDL2/CMakeLists.txt index 1772f5ee4e..a9e2449518 100644 --- a/extras/videoDrivers/SDL2/CMakeLists.txt +++ b/extras/videoDrivers/SDL2/CMakeLists.txt @@ -16,7 +16,7 @@ if(SDL2_FOUND) COMPILE_FLAGS "${USE_FLAGS}" LINK_FLAGS "${USE_FLAGS}" ) - target_link_libraries(videoSDL2 PRIVATE videodrv s25util::common glad Boost::nowide) + target_link_libraries(videoSDL2 PRIVATE videodrv s25util::common Boost::nowide) else () add_library(videoSDL2 SHARED ${RTTR_DRIVER_INTERFACE} VideoSDL2.cpp VideoSDL2.h icon.h icon.cpp) target_link_libraries(videoSDL2 PRIVATE videodrv s25util::common glad Boost::nowide SDL2::SDL2) diff --git a/extras/videoDrivers/SDL2/VideoSDL2.cpp b/extras/videoDrivers/SDL2/VideoSDL2.cpp index 4a62d43a3e..c8534118a5 100644 --- a/extras/videoDrivers/SDL2/VideoSDL2.cpp +++ b/extras/videoDrivers/SDL2/VideoSDL2.cpp @@ -10,7 +10,9 @@ #include "helpers/LSANUtils.h" #include "helpers/containerUtils.h" #include "icon.h" +#ifndef __EMSCRIPTEN__ #include "openglCfg.hpp" +#endif #include #include #include diff --git a/libs/s25client/CMakeLists.txt b/libs/s25client/CMakeLists.txt index 2e3340a37c..d3e2a36fe8 100644 --- a/libs/s25client/CMakeLists.txt +++ b/libs/s25client/CMakeLists.txt @@ -21,18 +21,21 @@ endif() add_executable(s25client s25client.cpp commands.cpp ${s25client_RC}) if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") set(USE_FLAGS "-fwasm-exceptions \ - --use-port=sdl2 --use-port=sdl2_image --use-port=sdl2_mixer -sSDL2_MIXER_FORMATS=ogg") + --use-port=sdl2 --use-port=sdl2_image --use-port=sdl2_mixer -sSDL2_MIXER_FORMATS=ogg \ + -O1 -gsource-map") set_target_properties(s25client PROPERTIES COMPILE_FLAGS "${USE_FLAGS}" LINK_FLAGS "${USE_FLAGS} \ - -sALLOW_MEMORY_GROWTH -sINITIAL_MEMORY=128mb -sMEMORY_GROWTH_LINEAR_STEP=32mb \ + -sALLOW_MEMORY_GROWTH -sINITIAL_MEMORY=256mb -sMEMORY_GROWTH_LINEAR_STEP=64mb \ -sSTACK_SIZE=1024kb \ -sENVIRONMENT=web \ -sMODULARIZE \ -sEXPORT_ALL -sEXPORTED_RUNTIME_METHODS=callMain,addRunDependency,removeRunDependency \ -sEXIT_RUNTIME \ - -lwebsocket.js -lidbfs.js -sCASE_INSENSITIVE_FS" + -sFULL_ES2 -L/src/contrib/gl4es/lib/ -lgl4es \ + -lwebsocket.js -lidbfs.js -sCASE_INSENSITIVE_FS \ + --profiling-funcs -sASSERTIONS=2" SUFFIX ".js" ) target_link_libraries(s25client PRIVATE s25Main Boost::program_options rttr::vld) diff --git a/libs/s25client/s25client.cpp b/libs/s25client/s25client.cpp index 991eda2c46..1ae137af33 100644 --- a/libs/s25client/s25client.cpp +++ b/libs/s25client/s25client.cpp @@ -55,13 +55,6 @@ EM_JS(void, main_ready, (), { Module?.gameReady?.(); }); -void main_loop() -{ - if (!GAMEMANAGER.Run()) - { - emscripten_cancel_main_loop(); - } -} #endif namespace bfs = boost::filesystem; @@ -449,6 +442,17 @@ bool InitGame(GameManager& gameManager) return true; } +#ifdef __EMSCRIPTEN__ +static void mainLoop() +{ + killme = false; + if (!GAMEMANAGER.Run()) + { + emscripten_cancel_main_loop(); + } +} +#endif + int RunProgram(po::variables_map& options) { LOG.write("%1%\n\n", LogTarget::Stdout) % GetProgramDescription(); @@ -480,11 +484,10 @@ int RunProgram(po::variables_map& options) } } - SetGlobalInstanceWrapper gameManager(setGlobalGameManager, LOG, SETTINGS, VIDEODRIVER, AUDIODRIVER, - WINDOWMANAGER); + setGlobalGameManager(new GameManager(LOG, SETTINGS, VIDEODRIVER, AUDIODRIVER, WINDOWMANAGER)); try { - if(!InitGame(gameManager)) + if(!InitGame(GAMEMANAGER)) return 2; if(options.count("map")) @@ -500,9 +503,9 @@ int RunProgram(po::variables_map& options) // Hauptschleife #ifdef __EMSCRIPTEN__ main_ready(); - emscripten_set_main_loop(&main_loop, 0, true); + emscripten_set_main_loop(&mainLoop, 0, true); #else - while(gameManager.Run()) + while(GAMEMANAGER.Run()) { #ifndef _WIN32 killme = false; @@ -510,7 +513,7 @@ int RunProgram(po::variables_map& options) } #endif // Spiel beenden - gameManager.Stop(); + GAMEMANAGER.Stop(); libsiedler2::setAllocator(nullptr); } catch(const RTTR_AssertError& error) { diff --git a/libs/s25main/CMakeLists.txt b/libs/s25main/CMakeLists.txt index 26d180cb2f..f626eb8d6b 100644 --- a/libs/s25main/CMakeLists.txt +++ b/libs/s25main/CMakeLists.txt @@ -59,11 +59,24 @@ target_link_libraries(s25Main PUBLIC s25Common rttrConfig gamedata - glad driver Boost::filesystem Boost::disable_autolinking PRIVATE BZip2::BZip2 Boost::iostreams Boost::locale Boost::nowide samplerate_cpp ) +if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") + target_link_libraries(s25Main PUBLIC videoSDL2 audioSDL) + set(USE_FLAGS "--use-port=sdl2 --use-port=sdl2_image \ + --use-port=sdl2_mixer -sSDL2_MIXER_FORMATS=ogg \ + -I/src/contrib/gl4es/include \ + -O1 -gsource-map") + set_target_properties(s25Main PROPERTIES + COMPILE_FLAGS "${USE_FLAGS}" + LINK_FLAGS "${USE_FLAGS} -sFULL_ES2 -L/src/contrib/gl4es/lib/ -lgl4es \ + --profiling-funcs -sASSERTIONS=2 -sLINKABLE" + ) +else () + target_link_libraries(s25Main PUBLIC glad) +endif () if(WIN32 OR CYGWIN) include(CheckIncludeFiles) @@ -88,15 +101,6 @@ if(LIBRT) target_link_libraries(s25Main PUBLIC ${LIBRT}) endif() -if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") - set(USE_FLAGS "--use-port=sdl2 --use-port=sdl2_image --use-port=sdl2_mixer -sSDL2_MIXER_FORMATS=ogg") - set_target_properties(s25Main PROPERTIES - COMPILE_FLAGS "${USE_FLAGS}" - LINK_FLAGS "${USE_FLAGS}" - ) - target_link_libraries(s25Main PUBLIC videoSDL2 audioSDL) -endif () - if(CMAKE_SYSTEM_NAME STREQUAL "Linux") target_link_libraries(s25Main PUBLIC ${CMAKE_DL_LIBS}) # For dynamic driver loading (DriverWrapper) endif() diff --git a/libs/s25main/GameManager.cpp b/libs/s25main/GameManager.cpp index bf8eaa0f50..578543b608 100644 --- a/libs/s25main/GameManager.cpp +++ b/libs/s25main/GameManager.cpp @@ -203,7 +203,7 @@ void GameManager::ResetAverageGFPS() gfCounter_ = FrameCounter(FrameCounter::clock::duration::max()); // Never update } -static GameManager* globalGameManager = nullptr; +GameManager* globalGameManager; GameManager& getGlobalGameManager() { diff --git a/libs/s25main/GameManager.h b/libs/s25main/GameManager.h index de13b0f603..9d99f9d853 100644 --- a/libs/s25main/GameManager.h +++ b/libs/s25main/GameManager.h @@ -49,6 +49,8 @@ class GameManager boost::optional lastSkipReport; }; +extern GameManager *globalGameManager; + GameManager& getGlobalGameManager(); void setGlobalGameManager(GameManager* gameManager); diff --git a/libs/s25main/TerrainRenderer.cpp b/libs/s25main/TerrainRenderer.cpp index 588655194e..870341a480 100644 --- a/libs/s25main/TerrainRenderer.cpp +++ b/libs/s25main/TerrainRenderer.cpp @@ -20,7 +20,12 @@ #include "libsiedler2/Archiv.h" #include "libsiedler2/ArchivItem_PaletteAnimation.h" #include "s25util/Log.h" +#ifdef __EMSCRIPTEN__ +#include "SDL/SDL.h" +#include "SDL/SDL_opengl.h" +#else #include +#endif #include #include #include diff --git a/libs/s25main/WindowManager.cpp b/libs/s25main/WindowManager.cpp index d6db704823..ab8f5c50a2 100644 --- a/libs/s25main/WindowManager.cpp +++ b/libs/s25main/WindowManager.cpp @@ -25,6 +25,9 @@ #include "s25util/Log.h" #include "s25util/MyTime.h" #include +#ifdef __EMSCRIPTEN__ +#include +#endif WindowManager::WindowManager() : cursor_(Cursor::Hand), disable_mouse(false), lastMousePos(Position::Invalid()), curRenderSize(0, 0), diff --git a/libs/s25main/desktops/dskTextureTest.cpp b/libs/s25main/desktops/dskTextureTest.cpp index e7abfac918..7134870503 100644 --- a/libs/s25main/desktops/dskTextureTest.cpp +++ b/libs/s25main/desktops/dskTextureTest.cpp @@ -14,7 +14,12 @@ #include "lua/GameDataLoader.h" #include "ogl/FontStyle.h" #include "ogl/glArchivItem_Bitmap.h" +#ifdef __EMSCRIPTEN__ +#include "SDL/SDL.h" +#include "SDL/SDL_opengl.h" +#else #include +#endif #include #include diff --git a/libs/s25main/drivers/VideoDriverWrapper.cpp b/libs/s25main/drivers/VideoDriverWrapper.cpp index 6af4ad4326..e88afb9579 100644 --- a/libs/s25main/drivers/VideoDriverWrapper.cpp +++ b/libs/s25main/drivers/VideoDriverWrapper.cpp @@ -12,12 +12,15 @@ #include "mygettext/mygettext.h" #include "ogl/DummyRenderer.h" #include "ogl/OpenGLRenderer.h" -#include "openglCfg.hpp" #include "s25util/Log.h" #include "s25util/error.h" -#include #ifdef __EMSCRIPTEN__ +#include #include "../../../extras/videoDrivers/SDL2/VideoSDL2.h" +#include "SDL/SDL_opengl.h" +#else +#include "openglCfg.hpp" +#include #endif #include #if !defined(NDEBUG) && defined(HAVE_MEMCHECK_H) @@ -67,8 +70,11 @@ bool VideoDriverWrapper::LoadDriver(IVideoDriver* existingDriver) bool VideoDriverWrapper::LoadDriver() { +#ifdef __EMSCRIPTEN__ + initialize_gl4es(); +#endif UnloadDriver(); - videodriver = Handle(CreateVideoInstance(&WINDOWMANAGER), &FreeVideoInstance); + videodriver = Handle(CreateVideoInstance(&WINDOWMANAGER), FreeVideoInstance); return Initialize(); } @@ -388,7 +394,7 @@ bool VideoDriverWrapper::LoadAllExtensions() return false; } #else - LOG.write(_("OpenGL ES 2.0 (%1%")) % reinterpret_cast(glGetString(GL_VERSION)); + LOG.write(_("OpenGL ES 2.0 (%1%)\n")) % reinterpret_cast(glGetString(GL_VERSION)); #endif // auf VSync-Extension testen diff --git a/libs/s25main/ogl/BufferHandle.h b/libs/s25main/ogl/BufferHandle.h index 1deccdd2b2..14a2763123 100644 --- a/libs/s25main/ogl/BufferHandle.h +++ b/libs/s25main/ogl/BufferHandle.h @@ -4,7 +4,12 @@ #pragma once +#ifdef __EMSCRIPTEN__ +#include "SDL/SDL.h" +#include "SDL/SDL_opengl.h" +#else #include +#endif #include namespace ogl { diff --git a/libs/s25main/ogl/DummyRenderer.cpp b/libs/s25main/ogl/DummyRenderer.cpp index 9c933f1352..34b1e7654d 100644 --- a/libs/s25main/ogl/DummyRenderer.cpp +++ b/libs/s25main/ogl/DummyRenderer.cpp @@ -3,13 +3,15 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "DummyRenderer.h" -#include "openglCfg.hpp" #include +#ifndef __EMSCRIPTEN__ +#include "openglCfg.hpp" #include +#endif namespace rttrOglMock { RTTR_IGNORE_DIAGNOSTIC("-Wmissing-declarations") - +#ifndef __EMSCRIPTEN__ void APIENTRY glGenTextures(GLsizei n, GLuint* textures) { static GLuint cur = 0; @@ -29,12 +31,14 @@ void APIENTRY glGetTexLevelParameteriv(GLenum, GLint, GLenum, GLint* params) { *params = 1; } +#endif RTTR_POP_DIAGNOSTIC } // namespace rttrOglMock bool DummyRenderer::initOpenGL(OpenGL_Loader_Proc) { +#ifndef __EMSCRIPTEN__ GLVersion = {RTTR_OGL_MAJOR, RTTR_OGL_MINOR}; #define MOCK(FUNC) FUNC = rttrOglMock::FUNC MOCK(glGenTextures); @@ -48,5 +52,6 @@ bool DummyRenderer::initOpenGL(OpenGL_Loader_Proc) MOCK(glColor4ub); MOCK(glDrawArrays); MOCK(glGetTexLevelParameteriv); +#endif return true; } diff --git a/libs/s25main/ogl/OpenGLRenderer.cpp b/libs/s25main/ogl/OpenGLRenderer.cpp index f02fba2b76..8a3e9e1427 100644 --- a/libs/s25main/ogl/OpenGLRenderer.cpp +++ b/libs/s25main/ogl/OpenGLRenderer.cpp @@ -6,8 +6,13 @@ #include "DrawPoint.h" #include "drivers/VideoDriverWrapper.h" #include "glArchivItem_Bitmap.h" +#ifdef __EMSCRIPTEN__ +#include "SDL/SDL.h" +#include "SDL/SDL_opengl.h" +#else #include "openglCfg.hpp" #include +#endif void OpenGLRenderer::synchronize() { @@ -123,8 +128,10 @@ void OpenGLRenderer::DrawLine(DrawPoint pt1, DrawPoint pt2, unsigned width, unsi bool OpenGLRenderer::initOpenGL(OpenGL_Loader_Proc loader) { -#if RTTR_OGL_ES +#if defined(RTTR_OGL_ES) return gladLoadGLES2Loader(loader) != 0; +#elif __EMSCRIPTEN__ + return loader != NULL; #else return gladLoadGLLoader(loader) != 0; #endif diff --git a/libs/s25main/ogl/VBO.h b/libs/s25main/ogl/VBO.h index e1cd4974ff..c9ecd6fd1c 100644 --- a/libs/s25main/ogl/VBO.h +++ b/libs/s25main/ogl/VBO.h @@ -8,7 +8,12 @@ #include "enum_cast.hpp" #include "ogl/BufferHandle.h" #include "ogl/constants.h" +#ifdef __EMSCRIPTEN__ +#include "SDL/SDL.h" +#include "SDL/SDL_opengl.h" +#else #include +#endif #include #include diff --git a/libs/s25main/ogl/constants.h b/libs/s25main/ogl/constants.h index 0b12d9d2c0..af55310f12 100644 --- a/libs/s25main/ogl/constants.h +++ b/libs/s25main/ogl/constants.h @@ -4,7 +4,12 @@ #pragma once +#ifdef __EMSCRIPTEN__ +#include "SDL/SDL.h" +#include "SDL/SDL_opengl.h" +#else #include +#endif namespace ogl { enum class Target : GLenum diff --git a/libs/s25main/ogl/glArchivItem_Bitmap.cpp b/libs/s25main/ogl/glArchivItem_Bitmap.cpp index 86a50fd4fc..a28faac407 100644 --- a/libs/s25main/ogl/glArchivItem_Bitmap.cpp +++ b/libs/s25main/ogl/glArchivItem_Bitmap.cpp @@ -6,7 +6,12 @@ #include "Point.h" #include "drivers/VideoDriverWrapper.h" #include "libsiedler2/PixelBufferBGRA.h" +#ifdef __EMSCRIPTEN__ +#include "SDL/SDL.h" +#include "SDL/SDL_opengl.h" +#else #include +#endif glArchivItem_Bitmap::glArchivItem_Bitmap() = default; diff --git a/libs/s25main/ogl/glArchivItem_BitmapBase.cpp b/libs/s25main/ogl/glArchivItem_BitmapBase.cpp index a17663a133..8ab97cac31 100644 --- a/libs/s25main/ogl/glArchivItem_BitmapBase.cpp +++ b/libs/s25main/ogl/glArchivItem_BitmapBase.cpp @@ -5,7 +5,12 @@ #include "glArchivItem_BitmapBase.h" #include "Loader.h" #include "drivers/VideoDriverWrapper.h" +#ifdef __EMSCRIPTEN__ +#include "SDL/SDL.h" +#include "SDL/SDL_opengl.h" +#else #include +#endif /** @class glArchivItem_BitmapBase * diff --git a/libs/s25main/ogl/glArchivItem_Bitmap_Direct.cpp b/libs/s25main/ogl/glArchivItem_Bitmap_Direct.cpp index 4ad5bc8a91..0ce0b3abac 100644 --- a/libs/s25main/ogl/glArchivItem_Bitmap_Direct.cpp +++ b/libs/s25main/ogl/glArchivItem_Bitmap_Direct.cpp @@ -5,7 +5,12 @@ #include "glArchivItem_Bitmap_Direct.h" #include "drivers/VideoDriverWrapper.h" #include "libsiedler2/PixelBufferBGRA.h" +#ifdef __EMSCRIPTEN__ +#include "SDL/SDL.h" +#include "SDL/SDL_opengl.h" +#else #include +#endif #include glArchivItem_Bitmap_Direct::glArchivItem_Bitmap_Direct() : isUpdating_(false) {} diff --git a/libs/s25main/ogl/glArchivItem_Bitmap_Player.cpp b/libs/s25main/ogl/glArchivItem_Bitmap_Player.cpp index f064e28a94..ba5492d5fc 100644 --- a/libs/s25main/ogl/glArchivItem_Bitmap_Player.cpp +++ b/libs/s25main/ogl/glArchivItem_Bitmap_Player.cpp @@ -7,7 +7,12 @@ #include "Point.h" #include "drivers/VideoDriverWrapper.h" #include "libsiedler2/PixelBufferBGRA.h" +#ifdef __EMSCRIPTEN__ +#include "SDL/SDL.h" +#include "SDL/SDL_opengl.h" +#else #include +#endif namespace { struct GL_RGBAColor diff --git a/libs/s25main/ogl/glFont.h b/libs/s25main/ogl/glFont.h index f79c08ced3..e6061623d6 100644 --- a/libs/s25main/ogl/glFont.h +++ b/libs/s25main/ogl/glFont.h @@ -9,7 +9,12 @@ #include "ogl/FontStyle.h" #include "ogl/glArchivItem_Bitmap.h" #include "s25util/colors.h" +#ifdef __EMSCRIPTEN__ +#include "SDL/SDL.h" +#include "SDL/SDL_opengl.h" +#else #include +#endif #include #include #include diff --git a/libs/s25main/ogl/glSmartBitmap.cpp b/libs/s25main/ogl/glSmartBitmap.cpp index 1784a29a4f..17c5ee7fda 100644 --- a/libs/s25main/ogl/glSmartBitmap.cpp +++ b/libs/s25main/ogl/glSmartBitmap.cpp @@ -11,7 +11,12 @@ #include "libsiedler2/ArchivItem_Bitmap_Player.h" #include "libsiedler2/PixelBufferBGRA.h" #include "s25util/colors.h" +#ifdef __EMSCRIPTEN__ +#include "SDL/SDL.h" +#include "SDL/SDL_opengl.h" +#else #include +#endif #include #include diff --git a/libs/s25main/ogl/glTexturePacker.cpp b/libs/s25main/ogl/glTexturePacker.cpp index 2641a2dfa1..33df6bf36e 100644 --- a/libs/s25main/ogl/glTexturePacker.cpp +++ b/libs/s25main/ogl/glTexturePacker.cpp @@ -8,7 +8,12 @@ #include "ogl/glTexturePackerNode.h" #include "ogl/saveBitmap.h" #include "libsiedler2/PixelBufferBGRA.h" +#ifdef __EMSCRIPTEN__ +#include "SDL/SDL.h" +#include "SDL/SDL_opengl.h" +#else #include +#endif #include #include #include diff --git a/libs/s25main/world/GameWorldView.cpp b/libs/s25main/world/GameWorldView.cpp index 5907cc91f6..0439c3f2ad 100644 --- a/libs/s25main/world/GameWorldView.cpp +++ b/libs/s25main/world/GameWorldView.cpp @@ -30,7 +30,12 @@ #include "gameData/MapConsts.h" #include "s25util/error.h" #include "s25util/warningSuppression.h" +#ifdef __EMSCRIPTEN__ +#include "SDL/SDL.h" +#include "SDL/SDL_opengl.h" +#else #include +#endif #include #include From f2335056a648006ec588a34814a5998643dc7ba6 Mon Sep 17 00:00:00 2001 From: Roman Turchin Date: Thu, 5 Jun 2025 19:52:17 +0200 Subject: [PATCH 05/11] [emscripten] lua build --- libs/libGamedata/CMakeLists.txt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/libs/libGamedata/CMakeLists.txt b/libs/libGamedata/CMakeLists.txt index 85ca93bc96..e35d20d6d1 100644 --- a/libs/libGamedata/CMakeLists.txt +++ b/libs/libGamedata/CMakeLists.txt @@ -15,9 +15,12 @@ AddDirectory(lua) file(GLOB SOURCES_OTHER *.cpp *.h) source_group(other FILES ${SOURCES_OTHER}) -#find_package(Lua 5.1 REQUIRED) -set(LUA_LIBRARIES /src/contrib/lua-5.4.7/build/lib/liblua.a) -set(LUA_INCLUDE_DIR /src/contrib/lua-5.4.7/build/include) +if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") + set(LUA_LIBRARIES /src/contrib/lua-5.4.7/build/lib/liblua.a) + set(LUA_INCLUDE_DIR /src/contrib/lua-5.4.7/build/include) +else () + find_package(Lua 5.1 REQUIRED) +endif () include(GatherDll) gather_dll(Lua) From b3c124f6f193aa6075f97e43785b5e11328ec2b2 Mon Sep 17 00:00:00 2001 From: Roman Turchin Date: Thu, 5 Jun 2025 20:06:37 +0200 Subject: [PATCH 06/11] [emscripten] revert some cmake changes --- CMakeLists.txt | 12 ++++++++---- external/CMakeLists.txt | 5 ++++- extras/ai-battle/CMakeLists.txt | 1 - 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ff7fdf24fe..5c218a751e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,7 +27,7 @@ if(CMAKE_VERSION VERSION_LESS 3.14) list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/external/libutil/cmake/cmake_3.14") endif() -#include(EnableCCache) +include(EnableCCache) set(checkSubmodules FALSE) # Figure out RTTR_REVISION (git hash) and RTTR_VERSION (date) @@ -297,9 +297,11 @@ include(RttrTestingCfg) set(rttrContribBoostDir ${CMAKE_CURRENT_SOURCE_DIR}/contrib/boost) if(EXISTS ${rttrContribBoostDir} AND IS_DIRECTORY ${rttrContribBoostDir}) - set(BOOST_ROOT "${rttrContribBoostDir}" CACHE PATH "Path to find boost at") - set(Boost_INCLUDE_DIR "${rttrContribBoostDir}/include" CACHE PATH "Path to find boost at") - set(Boost_LIBRARY_DIR "${rttrContribBoostDir}/lib" CACHE PATH "Path to find boost at") + set(BOOST_ROOT ${rttrContribBoostDir} CACHE PATH "Path to find boost at") + if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") + set(Boost_INCLUDE_DIR "${rttrContribBoostDir}/include" CACHE PATH "Path to find boost at") + set(Boost_LIBRARY_DIR "${rttrContribBoostDir}/lib" CACHE PATH "Path to find boost at") + endif () endif() set(BoostPackages filesystem iostreams locale program_options) @@ -317,7 +319,9 @@ if(NOT Boost_FOUND) endif() option(RTTR_USE_SYSTEM_BOOST_NOWIDE "Use system installed Boost.Nowide. Fails if not found!" "${RTTR_USE_SYSTEM_LIBS}") +if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") set(RTTR_USE_SYSTEM_BOOST_NOWIDE ON) +endif () if(Boost_VERSION_MINOR GREATER_EQUAL "74" OR RTTR_USE_SYSTEM_BOOST_NOWIDE) # Boost 1.73 contains Boost.Nowide, 1.74 a required fix find_package(Boost COMPONENTS nowide) diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 656cad917e..4dd2314c80 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -51,7 +51,10 @@ add_subdirectory(liblobby) add_subdirectory(libsiedler2) add_subdirectory(libutil) add_subdirectory(mygettext) -#add_subdirectory(s25edit) +if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") +else () +add_subdirectory(s25edit) +endif () if(RTTR_BUILD_UPDATER) add_subdirectory(s25update) endif() diff --git a/extras/ai-battle/CMakeLists.txt b/extras/ai-battle/CMakeLists.txt index 8fff7ff952..b10b41b441 100644 --- a/extras/ai-battle/CMakeLists.txt +++ b/extras/ai-battle/CMakeLists.txt @@ -3,7 +3,6 @@ # SPDX-License-Identifier: GPL-2.0-or-later add_executable(ai-battle main.cpp HeadlessGame.cpp) -target_link_options(ai-battle PRIVATE "-fwasm-exceptions") target_link_libraries(ai-battle PRIVATE s25Main Boost::program_options Boost::nowide) if(WIN32) From 11f89b6ca1a7f869076b03316980d6050c1b3004 Mon Sep 17 00:00:00 2001 From: Roman Turchin Date: Sat, 7 Jun 2025 14:15:51 +0200 Subject: [PATCH 07/11] [emscripten] fix some review comments --- CMakeLists.txt | 4 ++-- external/CMakeLists.txt | 3 +-- extras/CMakeLists.txt | 3 +-- extras/audioDrivers/SDL/CMakeLists.txt | 4 ++-- extras/videoDrivers/SDL2/CMakeLists.txt | 4 ++-- libs/libGamedata/CMakeLists.txt | 2 +- libs/s25client/CMakeLists.txt | 8 +++----- libs/s25main/CMakeLists.txt | 7 +++---- libs/s25main/drivers/DriverWrapper.cpp | 11 ++++++++++- libs/s25main/drivers/VideoDriverWrapper.cpp | 2 +- libs/s25main/ogl/OpenGLRenderer.cpp | 10 +++++----- libs/s25main/ogl/OpenGLRenderer.h | 2 ++ 12 files changed, 33 insertions(+), 27 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c218a751e..52810e6b0f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -298,7 +298,7 @@ include(RttrTestingCfg) set(rttrContribBoostDir ${CMAKE_CURRENT_SOURCE_DIR}/contrib/boost) if(EXISTS ${rttrContribBoostDir} AND IS_DIRECTORY ${rttrContribBoostDir}) set(BOOST_ROOT ${rttrContribBoostDir} CACHE PATH "Path to find boost at") - if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") + if (CMAKE_SYSTEM_NAME MATCHES "Emscripten") set(Boost_INCLUDE_DIR "${rttrContribBoostDir}/include" CACHE PATH "Path to find boost at") set(Boost_LIBRARY_DIR "${rttrContribBoostDir}/lib" CACHE PATH "Path to find boost at") endif () @@ -319,7 +319,7 @@ if(NOT Boost_FOUND) endif() option(RTTR_USE_SYSTEM_BOOST_NOWIDE "Use system installed Boost.Nowide. Fails if not found!" "${RTTR_USE_SYSTEM_LIBS}") -if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") +if (CMAKE_SYSTEM_NAME MATCHES "Emscripten") set(RTTR_USE_SYSTEM_BOOST_NOWIDE ON) endif () if(Boost_VERSION_MINOR GREATER_EQUAL "74" OR RTTR_USE_SYSTEM_BOOST_NOWIDE) diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 4dd2314c80..26b57c01bf 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -51,8 +51,7 @@ add_subdirectory(liblobby) add_subdirectory(libsiedler2) add_subdirectory(libutil) add_subdirectory(mygettext) -if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") -else () +if (NOT CMAKE_SYSTEM_NAME MATCHES "Emscripten") add_subdirectory(s25edit) endif () if(RTTR_BUILD_UPDATER) diff --git a/extras/CMakeLists.txt b/extras/CMakeLists.txt index 70c23c7cbe..ae6d382ae7 100644 --- a/extras/CMakeLists.txt +++ b/extras/CMakeLists.txt @@ -11,8 +11,7 @@ set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) add_subdirectory(audioDrivers) add_subdirectory(videoDrivers) -if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") -else() +if (NOT CMAKE_SYSTEM_NAME MATCHES "Emscripten") add_subdirectory(ai-battle) endif () if(RTTR_BUNDLE AND APPLE) diff --git a/extras/audioDrivers/SDL/CMakeLists.txt b/extras/audioDrivers/SDL/CMakeLists.txt index ba70507b93..f13fee4244 100644 --- a/extras/audioDrivers/SDL/CMakeLists.txt +++ b/extras/audioDrivers/SDL/CMakeLists.txt @@ -3,7 +3,7 @@ # SPDX-License-Identifier: GPL-2.0-or-later set(SDL_BUILDING_LIBRARY ON) -if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") +if (CMAKE_SYSTEM_NAME MATCHES "Emscripten") set(SDL_MIXER_FOUND TRUE) else () find_package(SDL_mixer 2.0.1) @@ -19,7 +19,7 @@ else() gather_dll_by_name(OGG libogg-0.dll) endif() - if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") + if (CMAKE_SYSTEM_NAME MATCHES "Emscripten") add_library(audioSDL STATIC ${RTTR_DRIVER_INTERFACE} AudioSDL.cpp AudioSDL.h) set(USE_FLAGS "--use-port=sdl2_mixer -sSDL2_MIXER_FORMATS=ogg") set_target_properties(audioSDL PROPERTIES diff --git a/extras/videoDrivers/SDL2/CMakeLists.txt b/extras/videoDrivers/SDL2/CMakeLists.txt index a9e2449518..05ec5c9202 100644 --- a/extras/videoDrivers/SDL2/CMakeLists.txt +++ b/extras/videoDrivers/SDL2/CMakeLists.txt @@ -3,13 +3,13 @@ # SPDX-License-Identifier: GPL-2.0-or-later set(SDL2_BUILDING_LIBRARY ON) -if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") +if (CMAKE_SYSTEM_NAME MATCHES "Emscripten") set(SDL2_FOUND TRUE) else () find_package(SDL2 2.0.5) endif () if(SDL2_FOUND) - if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") + if (CMAKE_SYSTEM_NAME MATCHES "Emscripten") add_library(videoSDL2 STATIC ${RTTR_DRIVER_INTERFACE} VideoSDL2.cpp VideoSDL2.h icon.h icon.cpp) set(USE_FLAGS "--use-port=sdl2 --use-port=sdl2_image") set_target_properties(videoSDL2 PROPERTIES diff --git a/libs/libGamedata/CMakeLists.txt b/libs/libGamedata/CMakeLists.txt index e35d20d6d1..ae1e037db2 100644 --- a/libs/libGamedata/CMakeLists.txt +++ b/libs/libGamedata/CMakeLists.txt @@ -15,7 +15,7 @@ AddDirectory(lua) file(GLOB SOURCES_OTHER *.cpp *.h) source_group(other FILES ${SOURCES_OTHER}) -if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") +if (CMAKE_SYSTEM_NAME MATCHES "Emscripten") set(LUA_LIBRARIES /src/contrib/lua-5.4.7/build/lib/liblua.a) set(LUA_INCLUDE_DIR /src/contrib/lua-5.4.7/build/include) else () diff --git a/libs/s25client/CMakeLists.txt b/libs/s25client/CMakeLists.txt index d3e2a36fe8..3df73dd7ad 100644 --- a/libs/s25client/CMakeLists.txt +++ b/libs/s25client/CMakeLists.txt @@ -19,10 +19,9 @@ else() endif() add_executable(s25client s25client.cpp commands.cpp ${s25client_RC}) -if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") +if (CMAKE_SYSTEM_NAME MATCHES "Emscripten") set(USE_FLAGS "-fwasm-exceptions \ - --use-port=sdl2 --use-port=sdl2_image --use-port=sdl2_mixer -sSDL2_MIXER_FORMATS=ogg \ - -O1 -gsource-map") + --use-port=sdl2 --use-port=sdl2_image --use-port=sdl2_mixer -sSDL2_MIXER_FORMATS=ogg") set_target_properties(s25client PROPERTIES COMPILE_FLAGS "${USE_FLAGS}" @@ -34,8 +33,7 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") -sEXPORT_ALL -sEXPORTED_RUNTIME_METHODS=callMain,addRunDependency,removeRunDependency \ -sEXIT_RUNTIME \ -sFULL_ES2 -L/src/contrib/gl4es/lib/ -lgl4es \ - -lwebsocket.js -lidbfs.js -sCASE_INSENSITIVE_FS \ - --profiling-funcs -sASSERTIONS=2" + -lwebsocket.js -lidbfs.js -sCASE_INSENSITIVE_FS" SUFFIX ".js" ) target_link_libraries(s25client PRIVATE s25Main Boost::program_options rttr::vld) diff --git a/libs/s25main/CMakeLists.txt b/libs/s25main/CMakeLists.txt index f626eb8d6b..aabfd033ad 100644 --- a/libs/s25main/CMakeLists.txt +++ b/libs/s25main/CMakeLists.txt @@ -67,13 +67,12 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") target_link_libraries(s25Main PUBLIC videoSDL2 audioSDL) set(USE_FLAGS "--use-port=sdl2 --use-port=sdl2_image \ --use-port=sdl2_mixer -sSDL2_MIXER_FORMATS=ogg \ - -I/src/contrib/gl4es/include \ - -O1 -gsource-map") + -I/src/contrib/gl4es/include") set_target_properties(s25Main PROPERTIES COMPILE_FLAGS "${USE_FLAGS}" - LINK_FLAGS "${USE_FLAGS} -sFULL_ES2 -L/src/contrib/gl4es/lib/ -lgl4es \ - --profiling-funcs -sASSERTIONS=2 -sLINKABLE" + LINK_FLAGS "${USE_FLAGS} -sFULL_ES2 -L/src/contrib/gl4es/lib/ -lgl4es" ) + add_definitions(-DRTTR_OGL_ES=0) else () target_link_libraries(s25Main PUBLIC glad) endif () diff --git a/libs/s25main/drivers/DriverWrapper.cpp b/libs/s25main/drivers/DriverWrapper.cpp index 5c6634bf2d..0ce62d5a05 100644 --- a/libs/s25main/drivers/DriverWrapper.cpp +++ b/libs/s25main/drivers/DriverWrapper.cpp @@ -106,8 +106,17 @@ bool DriverWrapper::CheckLibrary(const bfs::path& path, DriverType dt, std::stri nameOrError = _("Invalid API version!"); return false; } + std::string driverNameGetter; + if(dt == DriverType::Video) + { + driverNameGetter = "GetVideoDriverName"; + } + else + { + driverNameGetter = "GetAudioDriverName"; + } - auto GetDriverName = dll.get("GetDriverName"); + auto GetDriverName = dll.get(driverNameGetter); std::string createName, freeName; if(dt == DriverType::Video) { diff --git a/libs/s25main/drivers/VideoDriverWrapper.cpp b/libs/s25main/drivers/VideoDriverWrapper.cpp index e88afb9579..213e5776fa 100644 --- a/libs/s25main/drivers/VideoDriverWrapper.cpp +++ b/libs/s25main/drivers/VideoDriverWrapper.cpp @@ -383,9 +383,9 @@ bool VideoDriverWrapper::LoadAllExtensions() renderer_ = std::make_unique(); else renderer_ = std::make_unique(); +#ifndef __EMSCRIPTEN__ if(!renderer_->initOpenGL(videodriver->GetLoaderFunction())) return false; -#ifndef __EMSCRIPTEN__ LOG.write(_("OpenGL %1%.%2% supported\n")) % GLVersion.major % GLVersion.minor; if(GLVersion.major < RTTR_OGL_MAJOR || (GLVersion.major == RTTR_OGL_MAJOR && GLVersion.minor < RTTR_OGL_MINOR)) { diff --git a/libs/s25main/ogl/OpenGLRenderer.cpp b/libs/s25main/ogl/OpenGLRenderer.cpp index 8a3e9e1427..65505ef8f4 100644 --- a/libs/s25main/ogl/OpenGLRenderer.cpp +++ b/libs/s25main/ogl/OpenGLRenderer.cpp @@ -126,13 +126,13 @@ void OpenGLRenderer::DrawLine(DrawPoint pt1, DrawPoint pt2, unsigned width, unsi glEnable(GL_TEXTURE_2D); } +#if !__EMSCRIPTEN__ bool OpenGLRenderer::initOpenGL(OpenGL_Loader_Proc loader) { -#if defined(RTTR_OGL_ES) +# if RTTR_OGL_ES return gladLoadGLES2Loader(loader) != 0; -#elif __EMSCRIPTEN__ - return loader != NULL; -#else +# else return gladLoadGLLoader(loader) != 0; -#endif +# endif } +#endif \ No newline at end of file diff --git a/libs/s25main/ogl/OpenGLRenderer.h b/libs/s25main/ogl/OpenGLRenderer.h index 6cfba22e54..1648e96f54 100644 --- a/libs/s25main/ogl/OpenGLRenderer.h +++ b/libs/s25main/ogl/OpenGLRenderer.h @@ -11,7 +11,9 @@ class glArchivItem_Bitmap; class OpenGLRenderer : public IRenderer { public: +#if !__EMSCRIPTEN__ bool initOpenGL(OpenGL_Loader_Proc) override; +#endif void synchronize() override; void Draw3DBorder(const Rect& rect, bool elevated, glArchivItem_Bitmap& texture) override; void Draw3DContent(const Rect& rect, bool elevated, glArchivItem_Bitmap& texture, bool illuminated, From 5a36ad6b410100d74370eeb24dbdf3f17372a526 Mon Sep 17 00:00:00 2001 From: Roman Turchin Date: Sat, 7 Jun 2025 17:38:33 +0200 Subject: [PATCH 08/11] [emscripten] more review comments fixes --- extras/videoDrivers/SDL2/VideoSDL2.cpp | 4 ++-- libs/rttrConfig/src/RttrConfig.cpp | 2 +- libs/s25main/drivers/VideoDriverWrapper.cpp | 2 +- libs/s25main/ogl/DummyRenderer.cpp | 8 ++++---- libs/s25main/ogl/DummyRenderer.h | 2 ++ libs/s25main/ogl/IRenderer.h | 2 ++ 6 files changed, 12 insertions(+), 8 deletions(-) diff --git a/extras/videoDrivers/SDL2/VideoSDL2.cpp b/extras/videoDrivers/SDL2/VideoSDL2.cpp index c8534118a5..6c7a712d74 100644 --- a/extras/videoDrivers/SDL2/VideoSDL2.cpp +++ b/extras/videoDrivers/SDL2/VideoSDL2.cpp @@ -10,7 +10,7 @@ #include "helpers/LSANUtils.h" #include "helpers/containerUtils.h" #include "icon.h" -#ifndef __EMSCRIPTEN__ +#if !__EMSCRIPTEN__ #include "openglCfg.hpp" #endif #include @@ -114,7 +114,7 @@ bool VideoSDL2::CreateScreen(const std::string& title, const VideoMode& size, bo if(!initialized) return false; -#ifndef __EMSCRIPTEN__ +#if !__EMSCRIPTEN__ // GL-Attributes CHECK_SDL(SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, RTTR_OGL_MAJOR)); CHECK_SDL(SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, RTTR_OGL_MINOR)); diff --git a/libs/rttrConfig/src/RttrConfig.cpp b/libs/rttrConfig/src/RttrConfig.cpp index f68d85b7df..5ce625c54f 100644 --- a/libs/rttrConfig/src/RttrConfig.cpp +++ b/libs/rttrConfig/src/RttrConfig.cpp @@ -37,7 +37,7 @@ bfs::path RttrConfig::GetPrefixPath() // Get path to current executable (at least for checks) bfs::path fullExeFilepath = System::getExecutablePath(); // This should always work unless we have some missing implementation or a bad error -#ifndef __EMSCRIPTEN__ +#if !__EMSCRIPTEN__ if(fullExeFilepath.empty()) { LOG.write("Could not get path to current executable\n", LogTarget::Stderr); diff --git a/libs/s25main/drivers/VideoDriverWrapper.cpp b/libs/s25main/drivers/VideoDriverWrapper.cpp index 213e5776fa..57ee0b3c61 100644 --- a/libs/s25main/drivers/VideoDriverWrapper.cpp +++ b/libs/s25main/drivers/VideoDriverWrapper.cpp @@ -383,7 +383,7 @@ bool VideoDriverWrapper::LoadAllExtensions() renderer_ = std::make_unique(); else renderer_ = std::make_unique(); -#ifndef __EMSCRIPTEN__ +#if !__EMSCRIPTEN__ if(!renderer_->initOpenGL(videodriver->GetLoaderFunction())) return false; LOG.write(_("OpenGL %1%.%2% supported\n")) % GLVersion.major % GLVersion.minor; diff --git a/libs/s25main/ogl/DummyRenderer.cpp b/libs/s25main/ogl/DummyRenderer.cpp index 34b1e7654d..cf53aafb16 100644 --- a/libs/s25main/ogl/DummyRenderer.cpp +++ b/libs/s25main/ogl/DummyRenderer.cpp @@ -4,14 +4,14 @@ #include "DummyRenderer.h" #include -#ifndef __EMSCRIPTEN__ +#if !__EMSCRIPTEN__ #include "openglCfg.hpp" #include #endif namespace rttrOglMock { RTTR_IGNORE_DIAGNOSTIC("-Wmissing-declarations") -#ifndef __EMSCRIPTEN__ +#if !__EMSCRIPTEN__ void APIENTRY glGenTextures(GLsizei n, GLuint* textures) { static GLuint cur = 0; @@ -36,9 +36,9 @@ void APIENTRY glGetTexLevelParameteriv(GLenum, GLint, GLenum, GLint* params) RTTR_POP_DIAGNOSTIC } // namespace rttrOglMock +#if !__EMSCRIPTEN__ bool DummyRenderer::initOpenGL(OpenGL_Loader_Proc) { -#ifndef __EMSCRIPTEN__ GLVersion = {RTTR_OGL_MAJOR, RTTR_OGL_MINOR}; #define MOCK(FUNC) FUNC = rttrOglMock::FUNC MOCK(glGenTextures); @@ -52,6 +52,6 @@ bool DummyRenderer::initOpenGL(OpenGL_Loader_Proc) MOCK(glColor4ub); MOCK(glDrawArrays); MOCK(glGetTexLevelParameteriv); -#endif return true; } +#endif diff --git a/libs/s25main/ogl/DummyRenderer.h b/libs/s25main/ogl/DummyRenderer.h index 91c3a31e69..3f445ce9eb 100644 --- a/libs/s25main/ogl/DummyRenderer.h +++ b/libs/s25main/ogl/DummyRenderer.h @@ -11,7 +11,9 @@ class glArchivItem_Bitmap; class DummyRenderer : public IRenderer { public: +#if !__EMSCRIPTEN__ bool initOpenGL(OpenGL_Loader_Proc) override; +#endif void Draw3DBorder(const Rect&, bool /*elevated*/, glArchivItem_Bitmap& /*texture*/) override {} void Draw3DContent(const Rect&, bool /*elevated*/, glArchivItem_Bitmap& /*texture*/, bool /*illuminated*/, unsigned /*color*/) override diff --git a/libs/s25main/ogl/IRenderer.h b/libs/s25main/ogl/IRenderer.h index 78a3106d71..e7f10e6e96 100644 --- a/libs/s25main/ogl/IRenderer.h +++ b/libs/s25main/ogl/IRenderer.h @@ -18,7 +18,9 @@ class IRenderer using OpenGL_Loader_Proc = void* (*)(const char*); virtual ~IRenderer() = default; +#if !__EMSCRIPTEN__ virtual bool initOpenGL(OpenGL_Loader_Proc) = 0; +#endif /// Synchronize the rendering pipeline. Usually not required unless measuring something virtual void synchronize(){}; /// Draw a border around rect with 3D effect From 98fd863467ed72a7963893f3d4fc0b1ab3517e0b Mon Sep 17 00:00:00 2001 From: Roman Turchin Date: Tue, 24 Jun 2025 12:31:10 +0200 Subject: [PATCH 09/11] [emscripten] sync persistance store on save game; replace slow glDrawArrays with glBegin/glEnd --- libs/s25main/CMakeLists.txt | 7 +- libs/s25main/GlobalGameSettings.cpp | 7 + libs/s25main/Savegame.cpp | 8 + libs/s25main/TerrainRenderer.cpp | 75 ++++--- libs/s25main/WindowManager.cpp | 2 +- libs/s25main/ogl/glArchivItem_Bitmap.cpp | 32 ++- libs/s25main/ogl/glArchivItem_Bitmap.h | 4 + .../ogl/glArchivItem_Bitmap_Player.cpp | 14 +- libs/s25main/ogl/glFont.cpp | 10 +- libs/s25main/ogl/glSmartBitmap.cpp | 12 +- libs/s25main/world/GameWorldView.cpp | 198 +++++++++++------- libs/s25main/world/GameWorldView.h | 15 +- 12 files changed, 234 insertions(+), 150 deletions(-) diff --git a/libs/s25main/CMakeLists.txt b/libs/s25main/CMakeLists.txt index aabfd033ad..d70b068037 100644 --- a/libs/s25main/CMakeLists.txt +++ b/libs/s25main/CMakeLists.txt @@ -66,11 +66,10 @@ target_link_libraries(s25Main PUBLIC if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") target_link_libraries(s25Main PUBLIC videoSDL2 audioSDL) set(USE_FLAGS "--use-port=sdl2 --use-port=sdl2_image \ - --use-port=sdl2_mixer -sSDL2_MIXER_FORMATS=ogg \ - -I/src/contrib/gl4es/include") + --use-port=sdl2_mixer -sSDL2_MIXER_FORMATS=ogg") set_target_properties(s25Main PROPERTIES - COMPILE_FLAGS "${USE_FLAGS}" - LINK_FLAGS "${USE_FLAGS} -sFULL_ES2 -L/src/contrib/gl4es/lib/ -lgl4es" + COMPILE_FLAGS "${USE_FLAGS} -I/src/contrib/gl4es/include" + LINK_FLAGS "${USE_FLAGS}" ) add_definitions(-DRTTR_OGL_ES=0) else () diff --git a/libs/s25main/GlobalGameSettings.cpp b/libs/s25main/GlobalGameSettings.cpp index 730af63fe2..6261801cb5 100644 --- a/libs/s25main/GlobalGameSettings.cpp +++ b/libs/s25main/GlobalGameSettings.cpp @@ -16,6 +16,9 @@ #include #include #include +#if __EMSCRIPTEN__ +#include +#endif GlobalGameSettings::GlobalGameSettings() : speed(GameSpeed::Normal), objective(GameObjective::None), startWares(StartWares::Normal), lockedTeams(false), @@ -192,6 +195,10 @@ void GlobalGameSettings::SaveSettings() const SETTINGS.addons.configuration.clear(); for(const AddonWithState& addon : addons) SETTINGS.addons.configuration.insert(std::make_pair(static_cast(addon.addon->getId()), addon.status)); + EM_ASM(FS.syncfs(err => { + if (err) console.error("Failed to sync fs", err); + return true; + })); } /** diff --git a/libs/s25main/Savegame.cpp b/libs/s25main/Savegame.cpp index 29244bb5b1..019d21dd93 100644 --- a/libs/s25main/Savegame.cpp +++ b/libs/s25main/Savegame.cpp @@ -7,6 +7,9 @@ #include "s25util/BinaryFile.h" #include #include +#if __EMSCRIPTEN__ +#include +#endif std::string Savegame::GetSignature() const { @@ -47,6 +50,11 @@ bool Savegame::Save(BinaryFile& file, const std::string& mapName) WriteGGS(file); WriteGameData(file); + EM_ASM(FS.syncfs(err => { + if (err) console.error("Failed to sync fs", err); + return true; + })); + return true; } diff --git a/libs/s25main/TerrainRenderer.cpp b/libs/s25main/TerrainRenderer.cpp index 870341a480..640de5a1da 100644 --- a/libs/s25main/TerrainRenderer.cpp +++ b/libs/s25main/TerrainRenderer.cpp @@ -230,7 +230,11 @@ void TerrainRenderer::UpdateVertexColor(const MapPoint pt, const GameWorldViewer break; case Visibility::Visible: // Normal sichtbar +#if __EMSCRIPTEN__ + GetVertex(pt).color = clr; +#else GetVertex(pt).color = clr / 2.f; +#endif break; } } @@ -797,7 +801,11 @@ void TerrainRenderer::Draw(const Position& firstPt, const Position& lastPt, cons } // Modulate2x +#if __EMSCRIPTEN__ + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); +#else glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); +#endif glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE, 2.0f); // Disable alpha blending @@ -988,50 +996,41 @@ void TerrainRenderer::DrawWays(const PreparedRoads& sorted_roads) const { if(itRoad.value().empty()) continue; - Tex2C3Ver2* curVertexData = vertexData.get(); const glArchivItem_Bitmap& texture = *roadTextures[itRoad.index()]; PointF scaledTexSize = texture.GetSize() / PointF(texture.GetTexSize()); + VIDEODRIVER.BindTexture(texture.GetTextureNoCreate()); + + glBegin(GL_QUADS); for(const auto& it : itRoad.value()) { - curVertexData->tx = 0.0f; - curVertexData->ty = 0.0f; - curVertexData->r = curVertexData->g = curVertexData->b = it.color1; - Position tmpP = it.pos + begin_end_coords[it.dir][0]; - curVertexData->x = GLfloat(tmpP.x); - curVertexData->y = GLfloat(tmpP.y); - - curVertexData++; - - curVertexData->tx = 0.0f; - curVertexData->ty = scaledTexSize.y; - curVertexData->r = curVertexData->g = curVertexData->b = it.color1; - tmpP = it.pos + begin_end_coords[it.dir][1]; - curVertexData->x = GLfloat(tmpP.x); - curVertexData->y = GLfloat(tmpP.y); - - curVertexData++; - - curVertexData->tx = scaledTexSize.x; - curVertexData->ty = scaledTexSize.y; - curVertexData->r = curVertexData->g = curVertexData->b = it.color2; - tmpP = it.pos2 + begin_end_coords[it.dir][2]; - curVertexData->x = GLfloat(tmpP.x); - curVertexData->y = GLfloat(tmpP.y); - - curVertexData++; - curVertexData->tx = scaledTexSize.x; - curVertexData->ty = 0.0f; - curVertexData->r = curVertexData->g = curVertexData->b = it.color2; - tmpP = it.pos2 + begin_end_coords[it.dir][3]; - curVertexData->x = GLfloat(tmpP.x); - curVertexData->y = GLfloat(tmpP.y); - - curVertexData++; - } + const auto& coords = begin_end_coords[it.dir]; - VIDEODRIVER.BindTexture(texture.GetTextureNoCreate()); - glDrawArrays(GL_QUADS, 0, itRoad.value().size() * 4); + GLfloat r1 = it.color1; + GLfloat r2 = it.color2; + + Position p0 = it.pos + coords[0]; + Position p1 = it.pos + coords[1]; + Position p2 = it.pos2 + coords[2]; + Position p3 = it.pos2 + coords[3]; + + // Vertex 0 + glColor3f(r1, r1, r1); + glTexCoord2f(0.0f, 0.0f); glVertex2f(GLfloat(p0.x), GLfloat(p0.y)); + + // Vertex 1 + glColor3f(r1, r1, r1); + glTexCoord2f(0.0f, scaledTexSize.y); glVertex2f(GLfloat(p1.x), GLfloat(p1.y)); + + // Vertex 2 + glColor3f(r2, r2, r2); + glTexCoord2f(scaledTexSize.x, scaledTexSize.y); glVertex2f(GLfloat(p2.x), GLfloat(p2.y)); + + // Vertex 3 + glColor3f(r2, r2, r2); + glTexCoord2f(scaledTexSize.x, 0.0f); glVertex2f(GLfloat(p3.x), GLfloat(p3.y)); + } + glEnd(); } // Note: No glDisableClientState as we did not enable it } diff --git a/libs/s25main/WindowManager.cpp b/libs/s25main/WindowManager.cpp index ab8f5c50a2..b110868bc1 100644 --- a/libs/s25main/WindowManager.cpp +++ b/libs/s25main/WindowManager.cpp @@ -58,7 +58,7 @@ void WindowManager::DrawCursor() default: break; } if(resId) - LOADER.GetImageN("resource", resId)->DrawFull(VIDEODRIVER.GetMousePos()); + LOADER.GetImageN("resource", resId)->DrawCursor(VIDEODRIVER.GetMousePos()); } /** diff --git a/libs/s25main/ogl/glArchivItem_Bitmap.cpp b/libs/s25main/ogl/glArchivItem_Bitmap.cpp index a28faac407..bd13034fea 100644 --- a/libs/s25main/ogl/glArchivItem_Bitmap.cpp +++ b/libs/s25main/ogl/glArchivItem_Bitmap.cpp @@ -61,11 +61,25 @@ void glArchivItem_Bitmap::Draw(Rect dstArea, Rect srcArea, unsigned color /*= CO texCoords[0].y = texCoords[3].y = srcOrig.y; texCoords[1].y = texCoords[2].y = srcEndPt.y; - glVertexPointer(2, GL_FLOAT, 0, vertices.data()); - glTexCoordPointer(2, GL_FLOAT, 0, texCoords.data()); - VIDEODRIVER.BindTexture(GetTexture()); - glColor4ub(GetRed(color), GetGreen(color), GetBlue(color), GetAlpha(color)); - glDrawArrays(GL_QUADS, 0, 4); + if (isCursor) + { + glVertexPointer(2, GL_FLOAT, 0, vertices.data()); + glTexCoordPointer(2, GL_FLOAT, 0, texCoords.data()); + VIDEODRIVER.BindTexture(GetTexture()); + glColor4ub(GetRed(color), GetGreen(color), GetBlue(color), GetAlpha(color)); + glDrawArrays(GL_QUADS, 0, 4); + } + else + { + VIDEODRIVER.BindTexture(GetTexture()); + glColor4ub(GetRed(color), GetGreen(color), GetBlue(color), GetAlpha(color)); + glBegin(GL_QUADS); + glTexCoord2f(texCoords[0].x, texCoords[0].y); glVertex2f(vertices[0].x, vertices[0].y); + glTexCoord2f(texCoords[1].x, texCoords[1].y); glVertex2f(vertices[1].x, vertices[1].y); + glTexCoord2f(texCoords[2].x, texCoords[2].y); glVertex2f(vertices[2].x, vertices[2].y); + glTexCoord2f(texCoords[3].x, texCoords[3].y); glVertex2f(vertices[3].x, vertices[3].y); + glEnd(); + } } void glArchivItem_Bitmap::DrawFull(const Rect& destArea, unsigned color) @@ -78,6 +92,14 @@ void glArchivItem_Bitmap::DrawFull(const DrawPoint& dstPos, unsigned color) DrawFull(Rect(dstPos, GetSize()), color); } +void glArchivItem_Bitmap::DrawCursor(const DrawPoint& dstPos) +{ + isCursor = true; + DrawFull(Rect(dstPos, GetSize()), COLOR_WHITE); + isCursor = false; +} + + void glArchivItem_Bitmap::DrawPart(const Rect& destArea, const DrawPoint& offset, unsigned color) { Draw(destArea, Rect(offset, destArea.getSize()), color); diff --git a/libs/s25main/ogl/glArchivItem_Bitmap.h b/libs/s25main/ogl/glArchivItem_Bitmap.h index 21d95b29a9..1311f99d42 100644 --- a/libs/s25main/ogl/glArchivItem_Bitmap.h +++ b/libs/s25main/ogl/glArchivItem_Bitmap.h @@ -22,6 +22,8 @@ class glArchivItem_Bitmap : public virtual libsiedler2::baseArchivItem_Bitmap, p void DrawFull(const Rect& destArea, unsigned color = COLOR_WHITE); /// Draw the texture to the given position with full size void DrawFull(const DrawPoint& dstPos, unsigned color = COLOR_WHITE) override; + /// Draw cursor to the given position (todo: figure out why glBegin/End not rendering cursor, fix it, remove DrawCursor as it's dirty fix) + void DrawCursor(const DrawPoint& dstPos); /// Draw a rectangular part of the texture. offset specifies the offset from the origin of the texture void DrawPart(const Rect& destArea, const DrawPoint& offset, unsigned color = COLOR_WHITE); /// Draw a rectangular part of the texture from the origin of it @@ -36,4 +38,6 @@ class glArchivItem_Bitmap : public virtual libsiedler2::baseArchivItem_Bitmap, p protected: void FillTexture() override; Extent CalcTextureSize() const override; + + bool isCursor = false; }; diff --git a/libs/s25main/ogl/glArchivItem_Bitmap_Player.cpp b/libs/s25main/ogl/glArchivItem_Bitmap_Player.cpp index ba5492d5fc..95da9f6479 100644 --- a/libs/s25main/ogl/glArchivItem_Bitmap_Player.cpp +++ b/libs/s25main/ogl/glArchivItem_Bitmap_Player.cpp @@ -111,13 +111,15 @@ void glArchivItem_Bitmap_Player::Draw(Rect dstArea, Rect srcArea, unsigned color colors[4].a = GetAlpha(player_color); colors[7] = colors[6] = colors[5] = colors[4]; - glEnableClientState(GL_COLOR_ARRAY); - glVertexPointer(2, GL_FLOAT, 0, vertices.data()); - glTexCoordPointer(2, GL_FLOAT, 0, texCoords.data()); - glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors.data()); VIDEODRIVER.BindTexture(GetTexture()); - glDrawArrays(GL_QUADS, 0, 8); - glDisableClientState(GL_COLOR_ARRAY); + + glBegin(GL_QUADS); + for (int i = 0; i < 8; ++i) { + glColor4ub(colors[i].r, colors[i].g, colors[i].b, colors[i].a); + glTexCoord2f(texCoords[i].x, texCoords[i].y); + glVertex2f(vertices[i].x, vertices[i].y); + } + glEnd(); } void glArchivItem_Bitmap_Player::FillTexture() diff --git a/libs/s25main/ogl/glFont.cpp b/libs/s25main/ogl/glFont.cpp index 9fcce96f5e..b7133976b1 100644 --- a/libs/s25main/ogl/glFont.cpp +++ b/libs/s25main/ogl/glFont.cpp @@ -248,11 +248,15 @@ void glFont::Draw(DrawPoint pos, const std::string& text, FontStyle format, unsi for(GlPoint& pt : texList.texCoords) pt /= texSize; - glVertexPointer(2, GL_FLOAT, 0, texList.vertices.data()); - glTexCoordPointer(2, GL_FLOAT, 0, texList.texCoords.data()); VIDEODRIVER.BindTexture(texture); glColor4ub(GetRed(color), GetGreen(color), GetBlue(color), GetAlpha(color)); - glDrawArrays(GL_QUADS, 0, texList.vertices.size()); + + glBegin(GL_QUADS); + for (size_t i = 0, l = texList.vertices.size(); i < l; ++i) { + glTexCoord2f(texList.texCoords[i].x, texList.texCoords[i].y); + glVertex2f(texList.vertices[i].x, texList.vertices[i].y); + } + glEnd(); } template diff --git a/libs/s25main/ogl/glSmartBitmap.cpp b/libs/s25main/ogl/glSmartBitmap.cpp index 17c5ee7fda..ef80ac2fe6 100644 --- a/libs/s25main/ogl/glSmartBitmap.cpp +++ b/libs/s25main/ogl/glSmartBitmap.cpp @@ -332,11 +332,11 @@ void glSmartBitmap::drawRect(Rect dstArea, Rect srcArea, unsigned color /*= 0xFF } else numQuads = 4; - glEnableClientState(GL_COLOR_ARRAY); - glVertexPointer(2, GL_FLOAT, 0, vertices.data()); - glTexCoordPointer(2, GL_FLOAT, 0, curTexCoords.data()); - glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors.data()); VIDEODRIVER.BindTexture(texture); - glDrawArrays(GL_QUADS, 0, numQuads); - glDisableClientState(GL_COLOR_ARRAY); + glBegin(GL_QUADS); + for(int i = 0; i < numQuads; ++i) { + glColor4ub(colors[i].r, colors[i].g, colors[i].b, colors[i].a); + glTexCoord2f(curTexCoords[i].x, curTexCoords[i].y); glVertex2f(vertices[i].x, vertices[i].y); + } + glEnd(); } diff --git a/libs/s25main/world/GameWorldView.cpp b/libs/s25main/world/GameWorldView.cpp index 0439c3f2ad..da2b09ae99 100644 --- a/libs/s25main/world/GameWorldView.cpp +++ b/libs/s25main/world/GameWorldView.cpp @@ -114,17 +114,43 @@ float GameWorldView::GetCurrentTargetZoomFactor() const return targetZoomFactor_; } -struct ObjectBetweenLines +struct ObjDraw { - noBase& obj; - DrawPoint pos; // Zeichenposition + noBase* obj; + DrawPoint pos; + ObjDraw(noBase* obj, const DrawPoint& pos) : obj(obj), pos(pos) {} +}; + +struct ObjFOW +{ + const FOWObject* obj; + DrawPoint pos; + ObjFOW(const FOWObject* obj, const DrawPoint& pos) : obj(obj), pos(pos) {} +}; + +struct ObjConstructionAid +{ + ITexture* obj; + DrawPoint pos; + ObjConstructionAid(ITexture* obj, const DrawPoint& pos) : obj(obj), pos(pos) {} +}; - ObjectBetweenLines(noBase& obj, const DrawPoint& pos) : obj(obj), pos(pos) {} +struct ObjBS +{ + glSmartBitmap& obj; + DrawPoint pos; + unsigned color; + unsigned player_color; + ObjBS(glSmartBitmap& obj, const DrawPoint& pos, unsigned color, unsigned player_color) : obj(obj), pos(pos), color(color), player_color(player_color) {} }; void GameWorldView::Draw(const RoadBuildState& rb, const MapPoint selected, bool drawMouse, unsigned* water) { SetNextZoomFactor(); + std::vector renderQueueObjects; + std::vector renderQueueFOW; + std::vector renderQueueBQ; + std::vector renderQueueBS; const auto windowSize = VIDEODRIVER.GetWindowSize(); const auto& guiScale = VIDEODRIVER.getGuiScale(); @@ -159,9 +185,6 @@ void GameWorldView::Draw(const RoadBuildState& rb, const MapPoint selected, bool for(int y = firstPt.y; y <= lastPt.y; ++y) { - // Figuren speichern, die in dieser Zeile gemalt werden müssen - // und sich zwischen zwei Zeilen befinden, da sie dazwischen laufen - std::vector between_lines; for(int x = firstPt.x; x <= lastPt.x; ++x) { @@ -180,31 +203,72 @@ void GameWorldView::Draw(const RoadBuildState& rb, const MapPoint selected, bool Visibility visibility = gwv.GetVisibility(curPt); - DrawBoundaryStone(curPt, curPos, visibility); + DrawBoundaryStone(curPt, curPos, visibility, renderQueueBS); if(visibility == Visibility::Visible) { - DrawObject(curPt, curPos); - DrawMovingFiguresFromBelow(terrainRenderer, Position(x, y), between_lines); - DrawFigures(curPt, curPos, between_lines); + renderQueueObjects.push_back(ObjDraw(GetWorld().GetNode(curPt).obj, curPos)); + + DrawMovingFiguresFromBelow(Direction::NorthEast, terrainRenderer, Position(x, y), renderQueueObjects); + DrawMovingFiguresFromBelow(Direction::NorthWest, terrainRenderer, Position(x, y), renderQueueObjects); + + //Draw figures + for(noBase& figure : GetWorld().GetFigures(curPt)) + { + if(figure.IsMoving()) + { + // Drawn from above + Direction curMoveDir = static_cast(figure).GetCurMoveDir(); + if(Direction::NorthEast == curMoveDir || curMoveDir == Direction::NorthWest) + continue; + // Draw later + renderQueueObjects.push_back(ObjDraw(&figure, curPos)); + } + else + { + // Ansonsten jetzt schon zeichnen (not now, but in different layer) + renderQueueObjects.push_back(ObjDraw(&figure, curPos)); + } + } // Construction aid mode if(show_bq) - DrawConstructionAid(curPt, curPos); + { + DrawConstructionAid(curPt, curPos, renderQueueBQ); + } } else if(visibility == Visibility::FogOfWar) { - const FOWObject* fowobj = gwv.GetYoungestFOWObject(MapPoint(curPt)); - if(fowobj) - fowobj->Draw(curPos); + renderQueueFOW.push_back(ObjFOW(gwv.GetYoungestFOWObject(MapPoint(curPt)), curPos)); } for(IDrawNodeCallback* callback : drawNodeCallbacks) + { callback->onDraw(curPt, curPos); + } } + } + // just group draw calls this gives +5fps in emscripten build + for(auto& bs : renderQueueBS) + { + bs.obj.draw(bs.pos, bs.color, bs.player_color); + } + + for(auto& renderQueueObject : renderQueueObjects) + { + if (!renderQueueObject.obj) continue; + renderQueueObject.obj->Draw(renderQueueObject.pos); + } + + for(auto& fow : renderQueueFOW) + { + if (!fow.obj) continue; + fow.obj->Draw(fow.pos); + } - // Figuren zwischen den Zeilen zeichnen - for(auto& between_line : between_lines) - between_line.obj.Draw(between_line.pos); + for(auto& bq : renderQueueBQ) + { + if (!bq.obj) continue; + bq.obj->DrawFull(bq.pos); } if(show_names || show_productivity) @@ -444,45 +508,19 @@ void GameWorldView::DrawProductivity(const noBaseBuilding& no, const DrawPoint& } } -void GameWorldView::DrawFigures(const MapPoint& pt, const DrawPoint& curPos, - std::vector& between_lines) const +void GameWorldView::DrawMovingFiguresFromBelow(Direction dir, const TerrainRenderer& terrainRenderer, const DrawPoint& curPos, + std::vector& rq) { - for(noBase& figure : GetWorld().GetFigures(pt)) - { - if(figure.IsMoving()) - { - // Drawn from above - Direction curMoveDir = static_cast(figure).GetCurMoveDir(); - if(curMoveDir == Direction::NorthEast || curMoveDir == Direction::NorthWest) - continue; - // Draw later - between_lines.push_back(ObjectBetweenLines(figure, curPos)); - } else if(figure.GetGOT() == GO_Type::Ship) - between_lines.push_back(ObjectBetweenLines(figure, curPos)); // TODO: Why special handling for ships? - else - // Ansonsten jetzt schon zeichnen - figure.Draw(curPos); - } -} + // Get figures opposite the current dir and check if they are moving in this dir + // Coordinates transform + Position curOffset; + MapPoint curPt = terrainRenderer.ConvertCoords(GetNeighbour(curPos, dir + 3u), &curOffset); + Position figPos = GetWorld().GetNodePos(curPt) - offset + curOffset; -void GameWorldView::DrawMovingFiguresFromBelow(const TerrainRenderer& terrainRenderer, const DrawPoint& curPos, - std::vector& between_lines) -{ - // First draw figures moving towards this point from below - static const std::array aboveDirs = {{Direction::NorthEast, Direction::NorthWest}}; - for(Direction dir : aboveDirs) + for(noBase& figure : GetWorld().GetFigures(curPt)) { - // Get figures opposite the current dir and check if they are moving in this dir - // Coordinates transform - Position curOffset; - MapPoint curPt = terrainRenderer.ConvertCoords(GetNeighbour(curPos, dir + 3u), &curOffset); - Position figPos = GetWorld().GetNodePos(curPt) - offset + curOffset; - - for(noBase& figure : GetWorld().GetFigures(curPt)) - { - if(figure.IsMoving() && static_cast(figure).GetCurMoveDir() == dir) - between_lines.push_back(ObjectBetweenLines(figure, figPos)); - } + if(figure.IsMoving() && static_cast(figure).GetCurMoveDir() == dir) + rq.push_back(ObjDraw(&figure, figPos)); } } @@ -498,7 +536,7 @@ constexpr auto getBqImgs() return imgs; } -void GameWorldView::DrawConstructionAid(const MapPoint& pt, const DrawPoint& curPos) +void GameWorldView::DrawConstructionAid(const MapPoint& pt, const DrawPoint& curPos, std::vector &rq) { BuildingQuality bq = gwv.GetBQ(pt); if(bq != BuildingQuality::Nothing) @@ -506,59 +544,59 @@ void GameWorldView::DrawConstructionAid(const MapPoint& pt, const DrawPoint& cur constexpr auto bqImgs = getBqImgs(); auto* bm = LOADER.GetMapTexture(bqImgs[bq]); // Draw building quality icon - bm->DrawFull(curPos); + //bm->DrawFull(curPos); + rq.push_back(ObjConstructionAid(bm, curPos)); // Show ability to construct military buildings if(GetWorld().GetGGS().isEnabled(AddonId::MILITARY_AID)) { if(!GetWorld().IsMilitaryBuildingNearNode(pt, gwv.GetPlayerId()) - && (bq == BuildingQuality::Hut || bq == BuildingQuality::House || bq == BuildingQuality::Castle + && (bq == BuildingQuality::Hut + || bq == BuildingQuality::House + || bq == BuildingQuality::Castle || bq == BuildingQuality::Harbor)) - LOADER.GetImageN("map_new", 20000)->DrawFull(curPos - DrawPoint(-1, bm->GetSize().y + 5)); + { + //LOADER.GetImageN("map_new", 20000)->DrawFull(curPos - DrawPoint(-1, bm->GetSize().y + 5)); + rq.push_back(ObjConstructionAid(LOADER.GetImageN("map_new", 20000), curPos - DrawPoint(-1, bm->GetSize().y + 5))); + } } } } -void GameWorldView::DrawObject(const MapPoint& pt, const DrawPoint& curPos) const +void GameWorldView::DrawBoundaryStone(const MapPoint& pt, const DrawPoint pos, Visibility vis, std::vector& rq) { - noBase* obj = GetWorld().GetNode(pt).obj; - if(!obj) - return; - - obj->Draw(curPos); -} - -void GameWorldView::DrawBoundaryStone(const MapPoint& pt, const DrawPoint pos, Visibility vis) -{ - if(vis == Visibility::Invisible) - return; + if(vis == Visibility::Invisible) return; const bool isFoW = vis == Visibility::FogOfWar; - const BoundaryStones& boundary_stones = - isFoW ? gwv.GetYoungestFOWNode(pt).boundary_stones : GetWorld().GetNode(pt).boundary_stones; + const BoundaryStones& boundary_stones = isFoW + ? gwv.GetYoungestFOWNode(pt).boundary_stones + : GetWorld().GetNode(pt).boundary_stones; const unsigned char owner = boundary_stones[BorderStonePos::OnPoint]; - if(!owner) - return; + if(!owner) return; const Nation nation = GetWorld().GetPlayer(owner - 1).nation; unsigned player_color = GetWorld().GetPlayer(owner - 1).color; if(isFoW) + { player_color = CalcPlayerFOWDrawColor(player_color); + } const auto curVertexPos = gwv.GetTerrainRenderer().GetVertexPos(pt); for(const auto bPos : helpers::EnumRange{}) { DrawPoint curPos; if(bPos == BorderStonePos::OnPoint) + { curPos = pos; + } else if(boundary_stones[bPos]) - curPos = pos - - DrawPoint((curVertexPos - gwv.GetTerrainRenderer().GetNeighbourVertexPos(pt, toDirection(bPos))) - / 2.0f); - else - continue; - LOADER.boundary_stone_cache[nation].draw(curPos, isFoW ? FOW_DRAW_COLOR : COLOR_WHITE, player_color); + { + curPos = pos - DrawPoint((curVertexPos - gwv.GetTerrainRenderer().GetNeighbourVertexPos(pt, toDirection(bPos))) / 2.0f); + } + else continue; + rq.push_back(ObjBS(LOADER.boundary_stone_cache[nation], curPos, isFoW ? FOW_DRAW_COLOR : COLOR_WHITE, player_color)); + //LOADER.boundary_stone_cache[nation].draw(curPos, isFoW ? FOW_DRAW_COLOR : COLOR_WHITE, player_color); } } diff --git a/libs/s25main/world/GameWorldView.h b/libs/s25main/world/GameWorldView.h index b7d3302e41..c6cd664223 100644 --- a/libs/s25main/world/GameWorldView.h +++ b/libs/s25main/world/GameWorldView.h @@ -5,6 +5,7 @@ #pragma once #include "DrawPoint.h" +#include "gameTypes/Direction.h" #include "gameTypes/MapCoordinates.h" #include "gameTypes/MapTypes.h" #include @@ -26,7 +27,10 @@ class IDrawNodeCallback virtual void onDraw(const MapPoint& pt, const DrawPoint& displayPt) = 0; }; -struct ObjectBetweenLines; +struct ObjDraw; +struct ObjFOW; +struct ObjConstructionAid; +struct ObjBS; class GameWorldView { @@ -126,12 +130,9 @@ class GameWorldView private: void CalcFxLx(); - void DrawBoundaryStone(const MapPoint& pt, DrawPoint pos, Visibility vis); - void DrawObject(const MapPoint& pt, const DrawPoint& curPos) const; - void DrawConstructionAid(const MapPoint& pt, const DrawPoint& curPos); - void DrawFigures(const MapPoint& pt, const DrawPoint& curPos, std::vector& between_lines) const; - void DrawMovingFiguresFromBelow(const TerrainRenderer& terrainRenderer, const DrawPoint& curPos, - std::vector& between_lines); + void DrawBoundaryStone(const MapPoint& pt, DrawPoint pos, Visibility vis, std::vector& rq); + void DrawConstructionAid(const MapPoint& pt, const DrawPoint& curPos, std::vector& rq); + void DrawMovingFiguresFromBelow(Direction dir, const TerrainRenderer& terrainRenderer, const DrawPoint& curPos, std::vector& rq); void DrawNameProductivityOverlay(const TerrainRenderer& terrainRenderer); void DrawProductivity(const noBaseBuilding& no, const DrawPoint& curPos); From 4ddac87198ff5d9ad04f9edad5827c947d1aaceb Mon Sep 17 00:00:00 2001 From: Roman Turchin Date: Wed, 25 Jun 2025 01:08:32 +0200 Subject: [PATCH 10/11] [emscripten] hide some ui stuff not applicable in web --- libs/s25client/CMakeLists.txt | 4 ++-- libs/s25client/s25client.cpp | 7 +++---- libs/s25main/desktops/dskMainMenu.cpp | 5 ++++- libs/s25main/desktops/dskOptions.cpp | 11 ++++++----- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/libs/s25client/CMakeLists.txt b/libs/s25client/CMakeLists.txt index 3df73dd7ad..a154425e9b 100644 --- a/libs/s25client/CMakeLists.txt +++ b/libs/s25client/CMakeLists.txt @@ -26,14 +26,14 @@ if (CMAKE_SYSTEM_NAME MATCHES "Emscripten") set_target_properties(s25client PROPERTIES COMPILE_FLAGS "${USE_FLAGS}" LINK_FLAGS "${USE_FLAGS} \ - -sALLOW_MEMORY_GROWTH -sINITIAL_MEMORY=256mb -sMEMORY_GROWTH_LINEAR_STEP=64mb \ + -sALLOW_MEMORY_GROWTH -sINITIAL_MEMORY=256mb -sMEMORY_GROWTH_LINEAR_STEP=128mb \ -sSTACK_SIZE=1024kb \ -sENVIRONMENT=web \ -sMODULARIZE \ -sEXPORT_ALL -sEXPORTED_RUNTIME_METHODS=callMain,addRunDependency,removeRunDependency \ -sEXIT_RUNTIME \ -sFULL_ES2 -L/src/contrib/gl4es/lib/ -lgl4es \ - -lwebsocket.js -lidbfs.js -sCASE_INSENSITIVE_FS" + -lwebsocket.js -lidbfs.js -sCASE_INSENSITIVE_FS --profiling-funcs" SUFFIX ".js" ) target_link_libraries(s25client PRIVATE s25Main Boost::program_options rttr::vld) diff --git a/libs/s25client/s25client.cpp b/libs/s25client/s25client.cpp index 1ae137af33..13b497f18f 100644 --- a/libs/s25client/s25client.cpp +++ b/libs/s25client/s25client.cpp @@ -52,9 +52,6 @@ #ifdef __EMSCRIPTEN__ # include -EM_JS(void, main_ready, (), { - Module?.gameReady?.(); -}); #endif namespace bfs = boost::filesystem; @@ -502,7 +499,9 @@ int RunProgram(po::variables_map& options) // Hauptschleife #ifdef __EMSCRIPTEN__ - main_ready(); + EM_ASM({ + Module?.gameReady?.(); + }); emscripten_set_main_loop(&mainLoop, 0, true); #else while(GAMEMANAGER.Run()) diff --git a/libs/s25main/desktops/dskMainMenu.cpp b/libs/s25main/desktops/dskMainMenu.cpp index 2f886ff5b1..db8d5bb116 100644 --- a/libs/s25main/desktops/dskMainMenu.cpp +++ b/libs/s25main/desktops/dskMainMenu.cpp @@ -39,9 +39,11 @@ dskMainMenu::dskMainMenu() // "Einzelspieler" AddTextButton(ID_btSingleplayer, DrawPoint(115, 180), Extent(220, 22), TextureColor::Green2, _("Singleplayer"), NormalFont); +#if !__EMSCRIPTEN__ // again requires emscripten libwebsocket patching and dedicated websocket <-> tcp/udp proxy server // "Mehrspieler" AddTextButton(ID_btMultiplayer, DrawPoint(115, 210), Extent(220, 22), TextureColor::Green2, _("Multiplayer"), NormalFont); +#endif // "Optionen" AddTextButton(ID_btOptions, DrawPoint(115, 250), Extent(220, 22), TextureColor::Green2, _("Options"), NormalFont); // "Intro" @@ -51,9 +53,10 @@ dskMainMenu::dskMainMenu() AddTextButton(ID_btReadme, DrawPoint(115, 310), Extent(220, 22), TextureColor::Green2, _("Readme"), NormalFont); // "Credits" AddTextButton(ID_btCredits, DrawPoint(115, 340), Extent(220, 22), TextureColor::Green2, _("Credits"), NormalFont); +#if !__EMSCRIPTEN__ // "Programm verlassen" AddTextButton(ID_btQuit, DrawPoint(115, 390), Extent(220, 22), TextureColor::Red1, _("Quit program"), NormalFont); - +#endif AddImage(ID_logo, DrawPoint(20, 20), LOADER.GetImageN("logo", 0)); using namespace std::chrono_literals; diff --git a/libs/s25main/desktops/dskOptions.cpp b/libs/s25main/desktops/dskOptions.cpp index aafc3417b4..f985a24354 100644 --- a/libs/s25main/desktops/dskOptions.cpp +++ b/libs/s25main/desktops/dskOptions.cpp @@ -113,7 +113,7 @@ constexpr auto sectionSpacingCommon = 10; constexpr auto tabButtonsStartPosition = DrawPoint(80, 510); constexpr auto optionRowsStartPosition = DrawPoint(80, 80); } // namespace - +#if !__EMSCRIPTEN__ static VideoMode getAspectRatio(const VideoMode& vm) { // First some a bit off values where the aspect ratio is defined by convention @@ -133,7 +133,7 @@ static VideoMode getAspectRatio(const VideoMode& vm) else return ratio; } - +#endif dskOptions::dskOptions() : Desktop(LOADER.GetImageN("setup013", 0)) { AddText(ID_txtOptions, DrawPoint(400, 10), _("Options"), COLOR_YELLOW, FontStyle::CENTER, LargeFont); @@ -307,6 +307,7 @@ dskOptions::dskOptions() : Desktop(LOADER.GetImageN("setup013", 0)) mainGroup->SetSelection(SETTINGS.global.showGFInfo); curPos = optionRowsStartPosition; +#if !__EMSCRIPTEN__ // all this stuff in emscripten build is controlled by browser's API and launcher; emscripten adds too bloated and buggy shim in lib lhtml5 groupGraphics->AddText(ID_txtResolution, curPos, _("Fullscreen resolution:"), COLOR_YELLOW, FontStyle{}, NormalFont); groupGraphics->AddComboBox(ID_cbResolution, curPos + ctrlOffset, ctrlSize, TextureColor::Grey, NormalFont, 150); @@ -317,7 +318,7 @@ dskOptions::dskOptions() : Desktop(LOADER.GetImageN("setup013", 0)) mainGroup->AddTextButton(ID_btOn, curPos + ctrlOffset, ctrlSize, TextureColor::Grey, _("Fullscreen"), NormalFont); mainGroup->AddTextButton(ID_btOff, curPos + ctrlOffset2, ctrlSize, TextureColor::Grey, _("Windowed"), NormalFont); curPos.y += rowHeight + sectionSpacing; - +#endif groupGraphics->AddText(ID_txtFramerate, curPos, _("Limit Framerate:"), COLOR_YELLOW, FontStyle{}, NormalFont); groupGraphics->AddComboBox(ID_cbFramerate, curPos + ctrlOffset, ctrlSizeLarge, TextureColor::Grey, NormalFont, 150); curPos.y += rowHeight + sectionSpacing; @@ -402,7 +403,7 @@ dskOptions::dskOptions() : Desktop(LOADER.GetImageN("setup013", 0)) // Graphics // { - +#if !__EMSCRIPTEN__ loadVideoModes(); // Und zu der Combobox hinzufügen @@ -427,7 +428,7 @@ dskOptions::dskOptions() : Desktop(LOADER.GetImageN("setup013", 0)) // "Vollbild" setzen groupGraphics->GetCtrl(ID_grpFullscreen)->SetSelection(SETTINGS.video.fullscreen); //-V807 - +#endif // "Limit Framerate" füllen auto* cbFrameRate = groupGraphics->GetCtrl(ID_cbFramerate); if(VIDEODRIVER.HasVSync()) From 666d680a371f536aae771c37154e4bc9f0112f35 Mon Sep 17 00:00:00 2001 From: Roman Turchin Date: Thu, 26 Jun 2025 11:38:03 +0200 Subject: [PATCH 11/11] [emscripten] require js wrapper that idbfs sync required: allows to show loading/please wait popup while this operation is performed --- libs/s25main/GlobalGameSettings.cpp | 7 +++---- libs/s25main/Replay.cpp | 9 +++++++++ libs/s25main/Savegame.cpp | 9 +++------ libs/s25main/desktops/dskCampaignSelection.cpp | 2 +- libs/s25main/desktops/dskMainMenu.cpp | 1 + libs/s25main/ingameWindows/iwPlayReplay.cpp | 6 ++++++ 6 files changed, 23 insertions(+), 11 deletions(-) diff --git a/libs/s25main/GlobalGameSettings.cpp b/libs/s25main/GlobalGameSettings.cpp index 6261801cb5..78d5875c35 100644 --- a/libs/s25main/GlobalGameSettings.cpp +++ b/libs/s25main/GlobalGameSettings.cpp @@ -195,10 +195,9 @@ void GlobalGameSettings::SaveSettings() const SETTINGS.addons.configuration.clear(); for(const AddonWithState& addon : addons) SETTINGS.addons.configuration.insert(std::make_pair(static_cast(addon.addon->getId()), addon.status)); - EM_ASM(FS.syncfs(err => { - if (err) console.error("Failed to sync fs", err); - return true; - })); +#if __EMSCRIPTEN__ + EM_ASM(Module.requireSync?.()); +#endif } /** diff --git a/libs/s25main/Replay.cpp b/libs/s25main/Replay.cpp index ebc28a5a5c..7543406589 100644 --- a/libs/s25main/Replay.cpp +++ b/libs/s25main/Replay.cpp @@ -12,6 +12,9 @@ #include #include #include +#if __EMSCRIPTEN__ +#include +#endif std::string Replay::GetSignature() const { @@ -107,7 +110,13 @@ bool Replay::StopRecording() // All done. Replace uncompressed replay compressedReplay.Close(); file.Close(); +#if __EMSCRIPTEN__ // prevent replay rename fault with link external fs exception + boost::filesystem::copy_file(tmpReplayFile.filePath, filepath_, boost::filesystem::copy_options::overwrite_existing); + boost::filesystem::remove(tmpReplayFile.filePath); + EM_ASM(Module.requireSync?.()); +#elif boost::filesystem::rename(tmpReplayFile.filePath, filepath_); +#endif return true; } catch(const std::exception& e) { diff --git a/libs/s25main/Savegame.cpp b/libs/s25main/Savegame.cpp index 019d21dd93..ba539a4f54 100644 --- a/libs/s25main/Savegame.cpp +++ b/libs/s25main/Savegame.cpp @@ -49,12 +49,9 @@ bool Savegame::Save(BinaryFile& file, const std::string& mapName) WritePlayerData(file); WriteGGS(file); WriteGameData(file); - - EM_ASM(FS.syncfs(err => { - if (err) console.error("Failed to sync fs", err); - return true; - })); - +#if __EMSCRIPTEN__ + EM_ASM(Module.requireSync?.()); +#endif return true; } diff --git a/libs/s25main/desktops/dskCampaignSelection.cpp b/libs/s25main/desktops/dskCampaignSelection.cpp index 36e294798c..83509afd6d 100644 --- a/libs/s25main/desktops/dskCampaignSelection.cpp +++ b/libs/s25main/desktops/dskCampaignSelection.cpp @@ -86,7 +86,7 @@ dskCampaignSelection::dskCampaignSelection(CreateServerInfo csi) Extent(secondColumnExtentX, mutlilineExtentY), TextureColor::Grey, NormalFont); AddTextButton(ID_btBack, DrawPoint(380, 560), Extent(200, 22), TextureColor::Red1, _("Back"), NormalFont); - AddTextButton(ID_Next, DrawPoint(590, 560), Extent(200, 22), TextureColor::Green2, _("Continue"), NormalFont); + AddTextButton(ID_Next, DrawPoint(590, 560), Extent(200, 22), TextureColor::Green2, _("Continue"), NormalFont)->SetEnabled(false); showCampaignInfo(false); diff --git a/libs/s25main/desktops/dskMainMenu.cpp b/libs/s25main/desktops/dskMainMenu.cpp index db8d5bb116..1b72b17ae0 100644 --- a/libs/s25main/desktops/dskMainMenu.cpp +++ b/libs/s25main/desktops/dskMainMenu.cpp @@ -39,6 +39,7 @@ dskMainMenu::dskMainMenu() // "Einzelspieler" AddTextButton(ID_btSingleplayer, DrawPoint(115, 180), Extent(220, 22), TextureColor::Green2, _("Singleplayer"), NormalFont); + #if !__EMSCRIPTEN__ // again requires emscripten libwebsocket patching and dedicated websocket <-> tcp/udp proxy server // "Mehrspieler" AddTextButton(ID_btMultiplayer, DrawPoint(115, 210), Extent(220, 22), TextureColor::Green2, _("Multiplayer"), diff --git a/libs/s25main/ingameWindows/iwPlayReplay.cpp b/libs/s25main/ingameWindows/iwPlayReplay.cpp index 3e689d03c3..97414769ba 100644 --- a/libs/s25main/ingameWindows/iwPlayReplay.cpp +++ b/libs/s25main/ingameWindows/iwPlayReplay.cpp @@ -20,6 +20,9 @@ #include "gameData/const_gui_ids.h" #include "s25util/Log.h" #include +#if __EMSCRIPTEN__ +#include +#endif namespace bfs = boost::filesystem; @@ -223,4 +226,7 @@ void iwPlayReplay::Msg_MsgBoxResult(const unsigned msgbox_id, const MsgboxResult PopulateTable(); } } +#if __EMSCRIPTEN__ + EM_ASM(Module.requireSync?.()); +#endif }