MultiFunPlayer v1.32.1 - Multi axis funscript player - Now with SLR script streaming

VSCode might keep the file open constantly then, Visual Studio creates a temp file, removes original, renames temp file on each save.
I’ll see if a file change can be detected.

You are not hijacking anything. If Internal is not connected then you cant control it. And you can only control it via Internal:: actions.

In your plugin you have to handle MediaSeekMessage and publish MediaPositionChangedMessage with force seek.

So I guess it’s a bug in Internal Source then, please fix if possible and makes sense.

Seems to work perfectly now from initial testing, thanks for the update :slight_smile:

SLR browser/quest remote control plugin

image

For anybody interested, this is the updated script that uses the new toggles that will be released in 1.32.1. It’s improved in various ways and now uses System.Net.Http instead of TCP. This unfortunately will require you to start mfp as administrator though (can be solved by using a different library but this is fine for me for now). I’ll probably change it to a web socket in the future anyways. The plugin could potentially host a basic web page you can go to in your quest, getting similar functionality to deovr with all the MFP benefits. I didn’t add that here as for me it’s part of a larger app of mine but you can see my earlier post for details on how to do this yourself: MultiFunPlayer v1.32.0 - Multi axis funscript player - Now with SLR script streaming - #1261 by fenderwq

RemoteControlPlugin.cs.txt (17.7 KB)

2 Likes

How should my EDI/SetariaPlayer/FunscriptPlayer-compatible plugin be called?

  • MultiGamePlayer
  • MultiFunGamer
  • Other (in comments)
0 voters

MultiFunPlayer v1.32.1:

Download: timed patreon only exclusive
Patreon build: https://www.patreon.com/posts/118304718

1.32.1

Additional patreon only changelog:

  • Fix SLR not using cached session when logging in with email
  • Fix SLR not using cached session when username letter case differs from session username
  • Fix DeoVR not staring if SLR fails to log in
  • Convert DeoVR haptics input from buttons to toggles
  • Improve behavior of DeoVR haptics input

Changelog:

  • Use force seek when updating position after a seek request in Internal source
  • Add support for toggles in shortcuts
  • Fix crash when trying to create shortcuts with missing type
  • Fix local script loading failing when one of the matched scripts is corrupted
  • Improve performance of shortcut action runner
Plugins:
  • Add or remove plugins after adding or removing folders
  • Recompile plugins on file change event
  • Recompile plugins when adding or removing xaml file
  • Limit plugin path depth to one subfolder
  • Automatically unregister all plugin registered actions on dispose
  • Add helpers for starting plugin background tasks/threads that are cleaned up on dispose
  • Fix plugin assembly not unloading
  • Fix plugin compilation error when plugin file contains classes with no base class
  • Fix plugin compilation error when non plugin class has constructors

1.32.0

Additional patreon only changelog:

  • Support SLR login using login code
    MultiFunPlayer_eFKZISyfLM
  • Allow connecting DeoVR even when SLR login fails or credentials are empty
  • Don’t use cached session if username is different
  • Fix “wrong password” error when logging in to SLR
  • Fix SLR api sending local scripts causing double sync

Changelog:

  • Rework and simplify plugin system
    devenv_uEsUsCvzOg
    • Allow placing plugins in subfolders
    • Expose available actions and properties to plugins
    • Allow plugins to create their own UI
    • Improve performance of creating assembly references for plugins
    • Register missing properties for plugins where possible to match registered actions
    • Sample plugins: MultiFunPlayer Sample Plugins · GitHub
  • Update to .net 9 (performance improvements)
  • Allow assigned actions to be in disabled state instead of getting removed from shortcuts if plugin or output target action is unregistered (#88)
    P4FSRlLpSY
  • Add support for auto-home and speed limit when using PolledUpdate (#180)
  • Fix unable to place breakpoints in plugins due to plugin assembly embedded source not including BOM
  • Fix nested dialogs getting stuck invisible in certain situations
  • Fix custom curve motion provider tiling when using makima interpolation
  • Fix manual axis transitions loosing steps with shortcuts in relative mode
  • Fix axis value flipping repeatedly with invert option enabled by reverting it to invert script only
  • Fix auto-home taking shorter time to complete than configured
  • Fix SyncOnMediaResourceChanged not having any effect when set to false
  • Fix MotionProvider::Custom Curve::Points::Set actions not updating display name
  • Fix MotionProvider::Custom Curve::Points::Set actions adding additional point on each launch

If you like what I’m doing, please consider supporting me on Patreon
https://www.patreon.com/yoooi

Thanks
It does fail with “file is used by another program” once every while but works
Consider adding a little wait/retry

Is there a way to view multiple graphs at once? Or at least to view a graph of non-L0 axis?

Mkay I’ve got problems
MFP with my plugin is too slow to update multiaxis script at 60fps
2-axis is fine, but when I make it 3-axis it starts lagging behind
I wonder if it’s problem with my code or the MFP
Gonna test

No, could be made as a plugin I guess.

Use scroll wheel on the heatmap to switch the axes.

MFP updates internally at 400hz and each output at up to 333hz so I dont think its the problem.

My measurements say that setting up 6-axis takes 0 alot of time even for short scripts
Could you perf the ChangeScriptMessage please?

2025-01-12 18:53:29.6150|WARN|MultiFunPlayer.Plugins.TominFapHero2.HttpServerPlugin|Action: /Live2D/Tomin/v2/live (http://localhost:5000/Live2D/Tomin/v2/live?Day=11DAY_HAnime&Hand/35=0.5890,-10.0997&Penis/55=0.5646,-10.2049&Penis/60=0.5490,-10.0123&now=1736697209620)
2025-01-12 18:53:29.6150|WARN|MultiFunPlayer.Plugins.TominFapHero2.HttpServerPlugin|Projection Time: 0 ms
2025-01-12 18:53:29.6150|WARN|MultiFunPlayer.Plugins.TominFapHero2.HttpServerPlugin|SetScript 6 axes 28 keys 0.000-2.382 28 keys L0, L1, L2, R0, R1, R2
2025-01-12 18:53:29.6150|INFO|MultiFunPlayer.UI.Controls.ViewModels.ScriptViewModel|Received ChangeScriptMessage [Axes: L0, L1, L2, R0, R1, R2]
2025-01-12 18:53:29.6258|INFO|MultiFunPlayer.UI.Controls.ViewModels.ScriptViewModel|Set L0 script to [Name: "", Source: ""]
2025-01-12 18:53:29.6258|DEBUG|MultiFunPlayer.UI.Controls.ViewModels.ScriptViewModel|Searching for valid index [Axis: L0]
2025-01-12 18:53:29.6258|INFO|MultiFunPlayer.UI.Controls.ViewModels.ScriptViewModel|Set L1 script to [Name: "", Source: ""]
2025-01-12 18:53:29.6258|INFO|MultiFunPlayer.UI.Controls.ViewModels.ScriptViewModel|Set L2 script to [Name: "", Source: ""]
2025-01-12 18:53:29.6258|DEBUG|MultiFunPlayer.UI.Controls.ViewModels.ScriptViewModel|Searching for valid index [Axis: L1]
2025-01-12 18:53:29.6258|DEBUG|MultiFunPlayer.UI.Controls.ViewModels.ScriptViewModel|Searching for valid index [Axis: L2]
2025-01-12 18:53:29.6258|INFO|MultiFunPlayer.UI.Controls.ViewModels.ScriptViewModel|Set R0 script to [Name: "", Source: ""]
2025-01-12 18:53:29.6258|DEBUG|MultiFunPlayer.UI.Controls.ViewModels.ScriptViewModel|Searching for valid index [Axis: R0]
2025-01-12 18:53:29.6406|INFO|MultiFunPlayer.UI.Controls.ViewModels.ScriptViewModel|Set R1 script to [Name: "", Source: ""]
2025-01-12 18:53:29.6406|DEBUG|MultiFunPlayer.UI.Controls.ViewModels.ScriptViewModel|Searching for valid index [Axis: R1]
2025-01-12 18:53:29.6406|INFO|MultiFunPlayer.UI.Controls.ViewModels.ScriptViewModel|Set R2 script to [Name: "", Source: ""]
2025-01-12 18:53:29.6406|DEBUG|MultiFunPlayer.UI.Controls.ViewModels.ScriptViewModel|Searching for valid index [Axis: R2]
2025-01-12 18:53:29.6406|WARN|MultiFunPlayer.Plugins.TominFapHero2.HttpServerPlugin|SetScript 6 axes ok
2025-01-12 18:53:29.6406|WARN|MultiFunPlayer.Plugins.TominFapHero2.HttpServerPlugin|Elapsed Time: 210201 ticks 21 ms (serializing: 811 ticks)
2025-01-12 18:53:29.6406|WARN|MultiFunPlayer.Plugins.TominFapHero2.HttpServerPlugin|UpdateKfScript Time: 211083 ms
2025-01-12 18:53:29.6406|WARN|MultiFunPlayer.Plugins.TominFapHero2.HttpServerPlugin|Request time: 21 ms

Edit: I’ve perfed more, it seems every axis takes 1-3ms no matter what

Heatmap is internal class so thats hard

The script change message was not designed for what you are doing, you should set the axis value via Axis::Value::Set (with invokeDirectly: true) and it should work better.
Any change to the internal axis state requires a lock.

You could also try keeping a reference to the keyframe collection that you create the script resource with and add new keyframes without sending a new script. But I dont know how well will that work if at all.

As far as I see it requires a lock per axis
Would it be faster is there is only a single lock that is locked once for the whole change?
What are even the use cases where you want to lock each axis separately?

Technically yes, but the lock should be held at most for 0.3-0.5ms.
The multiple locks are from the old versions where the update thread did not lock all axes for an update like it does now.

Anyways, I still don’t understand what takes 3ms of constant per non-primary axis
I’d perf if I’d have the code I guess

The code on github is pretty much the same, current code uses the new lock which is supposed to be faster.

1 Like

Btw I experience complete freezes (after which I have to kill MFP) when I click on GUI while it’s updating script in realtime
Should I lock something somewhere?

        s.RegisterAction<double>("Media::ScriptOffset::Set",
            s => s.WithLabel("Value").AsNumericUpDown(stringFormat: "{0:F2}s"), value => GlobalOffset = value);

There is an action for this, but can I read the property?

I would need more information, preferably steps to reproduce, what do you click specifically. It’s probably your plugin making a dead lock some how.

Media::ScriptOffset
All actions have a corresponding property to read.

Most likely


This is where it hangs it seems
Should I make a lock or something?
Hmm
Maybe I shouldn’t use async where I don’t need it so I can just make a global lock?

Okay makes sense. I search code of public version and it wasn’t there iirc


Feature requests:

  • a static L0/L1

I could not make this code to dead lock, so its probably not that.

No, the axes are parsed on startup, you are not guaranteed that L0 or L1 will even exist if someone configures their device that way.

I do have a repro (but it’s heavy)

  • Have something spam HTTP requests faster then they cane be processed so it’s busy 100% of the time
  • HTTP requests update script with ChangeScriptMessage
  • If you publish another message in void HandleMessage(MediaSeekMessage message) => PublishMessage(new MediaPositionChangedMessage(message.Position, true)); from UI thread while it’s still processing ChangeScriptMessage in HTTP thread, it will hang

So, “you shouldn’t use lock in async code” or something?
I’ve hacked in some SemaphoreSlim (idk what’s that) and it seems to work now
A regular lock didn’t work, a SemaphoreSlim sis work