diff --git a/CMakeLists.txt b/CMakeLists.txt index 745b91b..edb4112 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -810,8 +810,29 @@ elseif(PLATFORM STREQUAL "emscripten") src/input/OpenPinDevHandler.cpp ) + # PinMAME VPX-side plugin glue (registers VPinMAME.Controller for real + # ROM-driven scoring/switches/solenoids) - statically linked and + # registered from player.cpp instead of desktop VP's dynamic /plugins + # folder scan, same as the built-in "vpx" plugin already is. The real + # emulation core it drives (libpinmame.a) is staged into + # VPINBALL_WASM_DEPS_DIR by vpinball-wasm's own build-deps.sh (see + # patches/pinmame/ for the 3 portability fixes that wasm32 build needed). + set(VPX_PINMAME_PLUGIN_SOURCES + plugins/pinmame/common.cpp + plugins/pinmame/PinMAMEPlugin.cpp + plugins/pinmame/Controller.cpp + plugins/pinmame/ControllerSettings.cpp + plugins/pinmame/Game.cpp + plugins/pinmame/Games.cpp + plugins/pinmame/GameSettings.cpp + plugins/pinmame/Rom.cpp + plugins/pinmame/Roms.cpp + plugins/pinmame/Settings.cpp + ) + add_executable(vpinball ${VPX_STANDALONE_SOURCES} + ${VPX_PINMAME_PLUGIN_SOURCES} ${CMAKE_SOURCE_DIR}/src/core/EmscriptenBridge.cpp ) @@ -824,6 +845,7 @@ elseif(PLATFORM STREQUAL "emscripten") ${VPINBALL_WASM_DEPS_DIR}/include/libwinevbs/wine/include src plugins + plugins/pinmame ) target_compile_definitions(vpinball PRIVATE @@ -863,6 +885,7 @@ elseif(PLATFORM STREQUAL "emscripten") freetype freeimage winevbs + pinmame glad ) diff --git a/src/core/player.cpp b/src/core/player.cpp index 60c7d5f..ec9ebdd 100644 --- a/src/core/player.cpp +++ b/src/core/player.cpp @@ -79,6 +79,19 @@ using namespace VPX; // leave as-is as e.g. VPM relies on this #define WIN32_PLAYER_WND_CLASSNAME _T("VPPlayer") +#ifdef __EMSCRIPTEN__ +// Statically-linked player plugin entry points (see the __EMSCRIPTEN__ +// branch in Player::Player below) - same declaration shape used for the +// iOS/Android static plugin table in lib/src/VPinballLib.cpp, just declared +// at file scope here since MSGPI_EXPORT's __attribute__((visibility(...))) +// isn't accepted on a block-scope declaration. +MSGPI_EXPORT void MSGPIAPI PinMAMEPluginLoad(const uint32_t sessionId, const MsgPluginAPI* api); +MSGPI_EXPORT void MSGPIAPI PinMAMEPluginUnload(); + +// For PinmameEmscriptenStep(), called once per frame in EmscriptenStepFrame() +// below - see that function's own comment. +#include "pinmame/libpinmame.h" +#endif Player::Player(PinTable *const table, const PlayMode playMode) : m_ptable(table) @@ -128,6 +141,24 @@ Player::Player(PinTable *const table, const PlayMode playMode) #ifdef __LIBVPINBALL__ VPinballLib::VPinballLib::SetupStaticPlugins(m_pluginManager); +#elif defined(__EMSCRIPTEN__) + // A browser sandbox has no dynamic-library-loading /plugins folder to + // scan (see the ScanPluginFolder call in the #else branch below), so + // player plugins have to be statically linked into this same wasm binary + // and registered by function pointer instead - the same technique + // VPinballLib::SetupStaticPlugins already uses for the iOS/Android + // library build (lib/src/VPinballLib.cpp), just scoped here to the one + // plugin vpinball-wasm actually ships: PinMAME, which is what lets + // table scripts get a real (non-Nothing) VPinMAME.Controller for ROM- + // driven scoring/switches/solenoids instead of failing on + // CreateObject("VPinMAME.Controller") - see patches/pinmame/ for the + // wasm32 portability fixes its emulation core (libpinmame) needed. + // + // Registered only, not Load()-ed here: the per-plugin enable/Load loop + // just below (shared with the dynamic-scan #else branch) already does + // that for every entry in m_pluginManager.GetPlugins(), so calling Load() + // here too would just load it twice. + m_pluginManager.RegisterPlugin("PinMAME", "PinMAME", "PinMAME", "", "", "https://github.com/vpinball/pinmame", &PinMAMEPluginLoad, &PinMAMEPluginUnload); #else class SDLModuleLoader final : public MsgPI::MsgModuleLoader { @@ -2002,6 +2033,12 @@ bool Player::EmscriptenStepFrame() if (GetCloseState() != CS_PLAYING && GetCloseState() != CS_USER_INPUT) return false; + // PinMAME (VPinMAME.Controller for ROM-driven tables) is likewise + // single-threaded here instead of running on its own real thread - see + // PinmameEmscriptenStep's own comment in libpinmame.cpp. A no-op when no + // ROM-based table has called Controller.Run(). + PinmameEmscriptenStep(); + UpdateGameLogic(); PrepareFrame(); UpdateGameLogic();