Add real PinMAME (VPinMAME.Controller) integration for ROM-based tables
Statically links the real PinMAME emulation core (libpinmame) instead of leaving VPinMAME.Controller creation to fail, which was crashing table scripts on ROM-based tables before they could spawn a ball. PinMAME's own run_game()->cpu_run() loop is split into a one-shot init, a per-frame step, and a one-shot teardown (patches/pinmame/0004) so it runs cooperatively on the same frame callback as vpinball's own loop instead of on a real std::thread, which hard-aborts under Emscripten's single-threaded runtime - three smaller wasm32 portability fixes to libpinmame itself round out the patch set (0001-0003). Adds pinball.loadRom() to supply a ROM zip, written to the table-relative pinmame/roms/ path vpinball's own plugin already checks. Confirmed against a real community ROM-based table: Controller creation and game identification succeed, and a missing ROM now fails cleanly instead of crashing the page - actual ROM-driven gameplay is still unconfirmed since no ROM was available (or sought out) to test with. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SoSarxLgY33Kax5UNXcafZ
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
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();
|
||||
Reference in New Issue
Block a user