TVS Intiface Bridge

July 17, 2026 · View on GitHub

A BepInEx plugin for The Villain Simulator that drives an Intiface / Buttplug.io toy — a vibrator or a linear stroker — from what's actually happening in the game: thrust depth, arousal, and hits. No game files are modified; the plugin reads gameplay state at runtime, so it's fully reversible and survives game updates.

  • Works with any Buttplug-supported device — no Lovense hardware or Lovense Connect required.
  • Drives both device kinds from the same signal: a stroker mirrors position, a vibrator's intensity tracks the same value.
  • Reads real gameplay — live penetration depth, arousal, and whip/hit strikes — not the game's built-in 1-D Lovense scalar.
  • Works in desktop and VR mode.

Installation

1. Install BepInEx (one time)

This mod needs BepInEx 5.4.x, x64, Mono build (The Villain Simulator is a Unity Mono game).

  1. Download BepInEx_win_x64_5.4.x.zip from the BepInEx releases.
  2. Extract it into your game folder — the one containing TheVillainSimulator.exe. You should now have winhttp.dll and a BepInEx/ folder next to the exe.
  3. Launch the game once, then quit. This makes BepInEx generate its BepInEx/plugins/ folder.

Steam / Proton (Linux): add a launch option so the BepInEx loader is picked up: WINEDLLOVERRIDES="winhttp=n,b" %command%

2. Install this mod

  1. Download TVS-Intiface-vX.Y.Z.zip from this repo's Releases.
  2. Extract it into your game folder (same place as TheVillainSimulator.exe). It places TVSIntiface.dll into BepInEx/plugins/. (Or just copy TVSIntiface.dll into BepInEx/plugins/ manually.)
  3. Launch the game once so the plugin writes its config file (BepInEx/config/com.peter.tvs.intiface.cfg).

Using it

  1. Install and run Intiface Central (or intiface-engine). Start its server — the default address is ws://127.0.0.1:12345 (shown under Server Status).
  2. Connect your toy in Intiface Central and confirm it's listed.
  3. Launch the game and play. The log line [TVSIntiface] v2.0.0 active -> ws://127.0.0.1:12345 in BepInEx/LogOutput.log confirms the bridge started.

The toy now reacts live to the scene. Nothing else to configure for a basic setup.

What drives the toy

Each frame the plugin produces one signal and sends it to every connected device (stroker position + vibrator intensity), in priority order:

  1. Hit / whip → one short ~50% pulse, then nothing. (Stimulation like fingering or vibrators does not count as a hit — only actual strikes.)
  2. Penetration → live thrust depth. The stroker mirrors the real on-screen position; the vibrator's intensity tracks depth. If several holes are filled at once (ass + vagina + mouth), the most recent penetration wins. Depth is auto-calibrated per penetration — the plugin learns the travel range as you thrust, so it works across toys, holes and characters without tuning.
  3. Arousal (vibrator-in-her / passive drift / aphrodisiac serum) → the vibrator's intensity equals the arousal level; the stroker gently oscillates, faster as arousal rises.
  4. Orgasm / anything else → idle (off).

By default everything is read from the captive heroine (her holes and her arousal). Set BaseOnHeroine = false to drive from the player's own dick + arousal instead.


Configuration

BepInEx/config/com.peter.tvs.intiface.cfg (created on first launch):

SectionKeyDefaultMeaning
GeneralEnabledtrueMaster switch for the bridge.
IntifaceServerUrlws://127.0.0.1:12345Intiface server address (see Server Status in Intiface Central).
SignalBaseOnHeroinetrueDrive from the captive heroine's holes + arousal (true), or the player's own dick + arousal (false).
SignalInvertDepthfalseFlip stroke direction if the stroker moves opposite to the on-screen thrust.
SignalStrokeAmplitude1.0Stroke length (0–1) when oscillating in arousal mode.
SignalOscMinHz0.3Stroke frequency (Hz) at zero arousal, in arousal-oscillation mode.
SignalOscMaxHz2.0Stroke frequency (Hz) at full arousal.
SignalHitPulseLevel0.5Level (0–1) of the brief hit/whip pulse.
SignalHitPulseMs150Duration (ms) of the hit/whip pulse.
SignalMinDepthSpan0.03Auto-calibration: minimum observed thrust travel before depth drives the stroker.
OutputLinearRateHz20How often stroker position commands are streamed (Hz).
OutputMaxIntensity1.0Upper cap on vibrator intensity (0–1).

Stroker moving the wrong way? Flip InvertDepth.


Limitations

  • One signal for all devices. Every connected toy gets the same logical value — the plugin doesn't route different activities to different devices.
  • Depth needs a stroke to calibrate. At the start of a new penetration the stroker rests at the bottom until it has seen enough travel to learn the range (MinDepthSpan); it then tracks live.
  • If a scene ends abruptly without a clean withdrawal, the plugin prunes the penetration on the next frame and falls back to arousal/idle.

Uninstall

Delete TVSIntiface.dll from BepInEx/plugins/ (and its config file). To remove BepInEx too, delete winhttp.dll, doorstop_config.ini, and the BepInEx/ folder from the game directory.


Building from source

Requires the .NET SDK and a game install that has BepInEx (for the reference assemblies — they are not redistributed here).

dotnet build src/TVSIntiface.csproj -c Release -p:GameDir="/path/to/TheVillainSimulator-48a"

(Or set the TVS_GAME_DIR environment variable instead of -p:GameDir.) The output TVSIntiface.dll is written to src/bin/Release/, and also copied into the game's BepInEx/plugins/ if that folder exists.

How it works

The plugin talks the Buttplug protocol directly over a tiny hand-rolled RFC-6455 WebSocket (MiniWebSocket.cs) and JSON parser (Json.cs) — no extra DLLs to ship and no reliance on the Unity Mono System.Net.WebSockets stack.

Gameplay is read via Harmony patches on the game's own methods:

  • ZNEDickToyBase.InsertionBegin / InsertionEnd register and drop ongoing penetrations (timestamped so the youngest wins). Depth is the toy tip's Z in the hole-entrance frame, auto-ranged per penetration.
  • ZNEHeroStatusController.makeCharacterArousalReactionForDamage isolates actual strikes for the hit pulse.
  • Arousal is read live from the heroine's status controller (or the player dick controller).

Because this game disables the BepInEx manager object, a plugin's own Update never runs — so the signal loop lives on its own DontDestroyOnLoad GameObject that the plugin creates on startup.

License

MIT.