Skip to content

About

VPX plugin that bridges Visual Pinball X to the Mission Pinball Framework (MPF) via BCP, enabling MPF-driven game logic on all platforms

Resources

Stars

2 stars

Watchers

0 watching

Forks

Repository files navigation

mpf-vpx-plugin

Two independently enabled plugins for Visual Pinball X:

Plugin Purpose Connection
MPF Bridge (mpf) Switches, coils, lamps and MPF-driven game logic BCP, normally localhost:5051
MPF GMC Media (mpfmedia) Live GMC video on VPX Flashers Local TCP from mpf-gmc-media

The controller replaces the Windows-only mpf-vpcom-bridge using VPX's native plugin SDK. It retains CreateObject("MPF.Controller"); the VBScript helper below adapts Changed* responses for existing table scripts.

The video plugin is optional. Switch/coil control works without GMC or the media service. The video plugin does not open a BCP connection or poll controller outputs. Neither plugin distributes game tables or assets.

Platforms and downloads

CI builds and tests both plugins on these native runners:

Platform Archive suffix Controller binary Video binary
Windows amd64 windows-amd64 plugin-mpf64.dll plugin-mpfmedia.dll
macOS arm64 macos-arm64 plugin-mpf.dylib plugin-mpfmedia.dylib
Linux amd64 linux-amd64 plugin-mpf.so plugin-mpfmedia.so
Linux arm64 linux-arm64 plugin-mpf.so plugin-mpfmedia.so

Each successful job provides mpf-plugin-<platform>.tar.gz and mpfmedia-plugin-<platform>.tar.gz, containing mpf/ and mpfmedia/ respectively. Download development artifacts from Actions or tagged builds from Releases. Older releases may contain only the controller and use earlier archive names. The two-plugin archives become release assets when a new version is tagged.

The SDK is pinned to candidate v10.8.1-3788-2151290. Source compatibility metadata has no natively validated VPX builds yet, and current macOS application metadata cannot establish that exact build. macOS CI targets macOS 15 or newer. Windows/Linux CI checks compilation and synthetic transport, not full live-game compatibility. Intel macOS and 32-bit Windows are outside this CI matrix.

See compatibility before preparing a candidate archive.

Installation

1. Install the controller plugin

Close VPX, download the controller archive for your architecture, and extract its mpf/ folder into your VPX plugins directory:

Platform Plugins directory
macOS VPinballX_BGFX.app/Contents/Resources/plugins/
Linux <vpx-install>/plugins/
Windows <vpx-install>\plugins\

After installation you should have:

plugins/
  mpf/
    plugin.cfg
    plugin-mpf.dylib   (macOS)
    plugin-mpf.so       (Linux)
    plugin-mpf64.dll    (Windows 64-bit)

2. Enable the plugin

VPX comes in two flavors that affect how you configure plugins:

Windows with editor — open VPX, go to Preferences > Plugins, find "MPF Bridge" in the list, and enable it. Recording and recording path can also be configured from this screen.

Standalone player (macOS, Linux, Windows standalone) — the standalone player has no settings UI. Edit VPinballX.ini directly and add:

[Plugin.MPF]
Enable = 1

The ini file is located at:

Platform Path
macOS / Linux ~/.vpinball/VPinballX.ini
Windows VPinballX.ini in the VPX install directory

macOS code signing

On macOS, VPX must be signed with library validation disabled for third-party plugins to load. If your VPX copy was re-signed locally, re-sign it with:

codesign --force --sign - --entitlements entitlements.plist \
    --preserve-metadata=identifier,requirements,flags,runtime \
    ~/Applications/VPinballX_BGFX.app

Where entitlements.plist contains:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
    "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.security.cs.disable-library-validation</key>
    <true/>
</dict>
</plist>

3. Install the VBScript helper

Copy mpf_controller.vbs from the installed mpf/ directory (or scripts/mpf_controller.vbs in this source checkout) into the same folder as your .vpx table file (or any directory VPX can load scripts from).

This helper converts the plugin's object-based Changed* responses into the 2D variant arrays that existing table scripts expect.

Add this line near the top of your table script (after any existing ExecuteGlobal lines):

ExecuteGlobal GetTextFile("mpf_controller.vbs")

Then replace the direct Controller.Changed* calls in your timer sub:

Before After
Controller.ChangedLamps MPF_ChangedLamps(Controller)
Controller.ChangedSolenoids MPF_ChangedSolenoids(Controller)
Controller.ChangedGIStrings MPF_ChangedGIStrings(Controller)
Controller.ChangedLEDs MPF_ChangedLEDs(Controller)
Controller.ChangedFlashers MPF_ChangedFlashers(Controller)
Controller.HardwareRules MPF_HardwareRules(Controller)

All other Controller methods (Switch, PulseSW, IsCoilActive, Run, Stop, Mech) work unchanged.

MPF configuration

In your MPF machine config:

hardware:
    platform: virtual_pinball

Usage

  1. Start MPF: mpf both (the media controller satisfies the BCP client connection MPF waits for before starting its own BCP server).
  2. Start VPX and load your table.
  3. The table script's CreateObject("MPF.Controller") is handled by this plugin.
  4. Controller.Run connects to MPF's BCP server (default: localhost:5051).

To specify a custom address/port in your table script:

Controller.Run "192.168.1.100", 5051

Optional live GMC video

  1. Install and start mpf-gmc-media with its Godot exporter. The media service README describes its separate runtime requirements. Cockpit can manage this service and generate .mpf-cockpit/media.json plus media.vpx.ini.

  2. Close VPX and extract the video archive's mpfmedia/ directory alongside mpf/ in the plugins directory. Enable it in VPX preferences:

    [Plugin.MPFMedia]
    Enable = 1
  3. Run mpf-cockpit up before launching VPX directly, so the media service is available. Place the local connection file next to the table. Cockpit generates .mpf-cockpit/media.vpx.ini alongside media.json; copy that .vpx.ini file to the table directory as media.vpx.ini, or keep the table in the project root so its .mpf-cockpit/ directory is found automatically. You can now launch the table from VPX's graphical interface.

  4. Configure each Flasher's display source using the configured numeric source ID: ctrl://MPFMedia/display?id=0, ctrl://MPFMedia/display?id=1, etc. Size the surfaces to preserve each source's aspect ratio.

On each table start, the plugin uses the VPX SDK's table path and searches in this order:

  1. GMM_CONFIG, when explicitly set (a media.json path selects its companion media.vpx.ini; a .vpx.ini path can also be supplied directly).
  2. <table-name>.media.vpx.ini beside <table-name>.vpx.
  3. media.vpx.ini in the table directory.
  4. .mpf-cockpit/media.vpx.ini in the table directory.

The first existing configuration wins; an invalid explicit selection does not silently fall back to another project. The plugin stops its media connection when the table closes and resolves configuration again for the next table. It does not depend on the graphical launcher's working directory.

media.json remains the service configuration; the plugin reads its generated INI companion. Keep the connection file local: it contains an authentication token and should not be embedded in or distributed with a table. If the service configuration changes, update the copied connection file too.

Both plugins can run together, or the video plugin can run alone with a display-only table.

Video is received as raw RGB frames, with no encoding in the VPX plugin. Only subscribed sources are requested. Audio remains in Godot; recording and encoding belong to the separate media service. If the service disconnects, the plugin retries while displaying black or its last frame. See media integration details.

BCP traffic recording

The plugin can record all BCP traffic during a game session for debugging and test generation.

Windows with editor — enable recording and set the output path from Preferences > Plugins > MPF Bridge.

Standalone player — add these settings to VPinballX.ini:

[Plugin.MPF]
Enable = 1
EnableRecording = 1
RecordingPath = /path/to/recordings

Recordings are saved as JSONL files (one JSON object per line), named YYYY-MM-DD_HH-MM-SS_mpf_recording.jsonl.

Each event has a category for filtering:

Category Direction Description
input vpx_to_mpf Switch changes, pulse events, mech writes
state both Polled state: solenoids, lamps, LEDs, GI, flashers, hardware rules, coil active checks
query both Read-only queries: switch reads, mech reads

Example events:

{"ts":0.000,"wall":"2026-04-15T17:33:56.506674Z","cat":"input","dir":"vpx_to_mpf","cmd":"set_switch","params":{"number":"2","value":"bool:True"}}
{"ts":2.364,"cat":"state","dir":"mpf_to_vpx","cmd":"changed_solenoids","result":[["coil1",true]]}

Building from source

Requirements: CMake 3.20+, a C++20 compiler, Python 3 for the synthetic media transport test, and Git to fetch the pinned SDK.

cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release
ctest --test-dir build --output-on-failure -C Release

Both plugins build by default into build/dist/mpf/ and build/dist/mpfmedia/. To build only the video consumer, use cmake --build build --config Release --target mpf-media-plugin. The media transport test loads the real dynamic library and sends synthetic frames over an isolated loopback connection; it does not connect to a game.

To build against a specific VPX version:

cmake -B build -DVPX_TAG=v10.8.1-3788-2151290

License

Both plugins use GPLv3 — see LICENSE. The pinned VPX SDK headers are marked GPLv3+. The separate media service retains its own license. Distribution archives include this repository's license text.

About

VPX plugin that bridges Visual Pinball X to the Mission Pinball Framework (MPF) via BCP, enabling MPF-driven game logic on all platforms

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages