FredyGames build of Simutrans 124.5 (r11993) for WebAssembly, Emscripten 6.0.10, 2026-11-11. Applied by build-simutrans.sh on top of the official simutrans-src-124-5.zip (patch -p1). Under clause 3(a) of the Artistic License these changes are freely available (published with the game at fredygames.com and in its source package); every changed file says so in its header and marks each change with a "FredyGames (2026-11-11)" comment. All changes are inside __EMSCRIPTEN__ blocks, so builds for other platforms are unchanged. Changes: - src/simutrans/sys/simsys_s2.cc: the game loop hands control back to the browser about every 20 ms (emscripten_sleep with Asyncify, in dr_flush and when no event is pending); on a window resize the SDL renderer is kept and only the texture is recreated (destroying the renderer deletes its WebGL context, and Emscripten then drops all mouse and touch handlers of the canvas). - src/simutrans/network/network.cc: no network: the network core never starts and no server can be opened, so the game never contacts the list server, never announces itself and cannot join games. - src/simutrans/gui/banner.cc: the start screen has no "Play Online" (join game) and no "Install" (pakset downloader) buttons; pak64 ships with the game. --- a/src/simutrans/gui/banner.cc +++ b/src/simutrans/gui/banner.cc @@ -1,6 +1,9 @@ /* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) + * + * Changed by FredyGames (fredygames.com) on 2026-11-11 for its WebAssembly build of + * Simutrans 124.5: see the comments marked "FredyGames (2026-11-11)". */ #include "../simcolor.h" @@ -99,7 +102,10 @@ // Play online button join_map.init( button_t::roundbox | button_t::flexible, "join game"); join_map.add_listener( this ); +#ifndef __EMSCRIPTEN__ + // FredyGames (2026-11-11): no online play in the browser build add_component( &join_map ); +#endif // Options button options.init( button_t::roundbox | button_t::flexible, "Optionen"); @@ -109,7 +115,10 @@ // Install button install.init( button_t::roundbox | button_t::flexible, "Install"); install.add_listener( this ); +#ifndef __EMSCRIPTEN__ + // FredyGames (2026-11-11): the browser build ships pak64 and downloads nothing add_component( &install ); +#endif new_component(false, true); // narrow screen --- a/src/simutrans/network/network.cc +++ b/src/simutrans/network/network.cc @@ -1,6 +1,9 @@ /* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) + * + * Changed by FredyGames (fredygames.com) on 2026-11-11 for its WebAssembly build of + * Simutrans 124.5: see the comments marked "FredyGames (2026-11-11)". */ /* basic network functionality, borrowed from OpenTTD */ @@ -81,6 +84,10 @@ */ static bool network_initialize() { +#ifdef __EMSCRIPTEN__ + // FredyGames (2026-11-11): the browser build has no network play (no sockets, no list server). + return false; +#endif if(!network_active) { socket_list_t::reset(); @@ -393,6 +400,12 @@ */ bool network_init_server( int port, const vector_tpl &listen_addrs ) { +#ifdef __EMSCRIPTEN__ + // FredyGames (2026-11-11): no server in the browser build. + (void)port; (void)listen_addrs; + dbg->warning( "network_init_server()", "Network games are not available in this build." ); + return false; +#endif if ( port==0 ) { dbg->fatal( "network_init_server()", "Cannot host on port 0!" ); } --- a/src/simutrans/sys/simsys_s2.cc +++ b/src/simutrans/sys/simsys_s2.cc @@ -1,6 +1,9 @@ /* * This file is part of the Simutrans project under the Artistic License. * (see LICENSE.txt) + * + * Changed by FredyGames (fredygames.com) on 2026-11-11 for its WebAssembly build of + * Simutrans 124.5: see the comments marked "FredyGames (2026-11-11)". */ #if !defined __APPLE__ && !defined __ANDROID__ @@ -14,6 +17,9 @@ #endif #include +#ifdef __EMSCRIPTEN__ +#include +#endif #include #ifdef __CYGWIN__ @@ -379,7 +385,14 @@ if( sync_blit ) { flags |= SDL_RENDERER_PRESENTVSYNC; } +#ifdef __EMSCRIPTEN__ + // FredyGames (2026-11-11): on resize the renderer is kept (see dr_textur_resize) + if( renderer == NULL ) { + renderer = SDL_CreateRenderer( window, -1, flags ); + } +#else renderer = SDL_CreateRenderer( window, -1, flags ); +#endif if( renderer == NULL ) { dbg->warning( "internal_create_surfaces(SDL2)", "Couldn't create accelerated renderer: %s", SDL_GetError() ); @@ -508,10 +521,18 @@ // First free surface and then renderer. SDL_FreeSurface( screen ); screen = NULL; +#ifdef __EMSCRIPTEN__ + // FredyGames (2026-11-11): keep the renderer and only replace the texture. Destroying the + // renderer deletes its WebGL context, and Emscripten then removes every mouse and touch + // handler of the canvas (the game would stop reacting to the mouse after the first resize). + SDL_DestroyTexture( screen_tx ); + screen_tx = NULL; +#else // This destroys texture as well. SDL_DestroyRenderer( renderer ); renderer = NULL; screen_tx = NULL; +#endif internal_create_surfaces( tex_pitch, tex_h ); if( screen ) { @@ -559,6 +580,21 @@ } +#ifdef __EMSCRIPTEN__ +// FredyGames (2026-11-11): the browser only draws the canvas and delivers input while the game +// hands control back to it. Simutrans runs its own loop, so with Asyncify we yield every ~20 ms. +static uint32 web_last_yield = 0; +static void web_yield() +{ + const uint32 now = SDL_GetTicks(); + if( now - web_last_yield >= 20 ) { + emscripten_sleep(0); + web_last_yield = SDL_GetTicks(); + } +} +#endif + + void dr_prepare_flush() { return; @@ -577,6 +613,9 @@ SDL_RenderCopy( renderer, screen_tx, &rSrc, NULL ); SDL_RenderPresent( renderer ); +#ifdef __EMSCRIPTEN__ + web_yield(); +#endif } @@ -683,6 +722,9 @@ SDL_Event event; event.type = 1; if (SDL_PollEvent(&event) == 0) { +#ifdef __EMSCRIPTEN__ + web_yield(); +#endif return; } @@ -1089,6 +1131,9 @@ void dr_sleep(uint32 usec) { SDL_Delay( usec ); +#ifdef __EMSCRIPTEN__ + web_last_yield = SDL_GetTicks(); +#endif }