Actually no, maybe this was a bit of miscommunication. You get a log line for every button press from DeoVR (I’ve confirmed this). However the action is only triggered once every two clicks. So when it’s disabled initially in DeoVR and you then start the plugin it would be:
first click enabled (no action triggered but a log line is shown)
click two disabled (action is triggered and a log line is shown).
So about the action queue. I had to add this since doing it directly like below doesn’t work:
RegisterAction(“RemoteControlPlugin::ReturnToBase”, () =>
{
InvokeAction<DeviceAxis, double>(“Serial/0::Axis::Range::Minimum::Set”, DeviceAxis.Parse(“L0”), min);
});
The action queue makes sure it is running in the same thread and then it does work. Maybe there is a better way but that’s why it’s there.
You may try adding a random timeout as I did to avoid script change resetting my AB loop
Or try the InvokeActionAsync maybe
Some additional feedback then.
If you register an action you have to unregistered it while disposing otherwise you will have stuck actions in the MFP UI (I might make it so the plugin base unregistered the actions for you just in case). This will also prevent the plugin from fully unloading.
When you call EnqueueAction
with an async lambda, you are actually passing a Task not Action, but EnqueueAction
accepts an Action
so the Task is converted. Then inside ProcessActionQueue
when you invoke the actions with action();
the task is not awaited so it is executed instantly (it will not wait for SendHttpPostOverTcp
to finish).
This means that multiple calls to EnqueueAction
with async lambdas will be executed at the same time, and thus its not thread safe.
You would have to make EnqueueAction
accept Func<Task>
as action, and inside ProcessActionQueue
you would do await action();
If you add a Button Press
and Button Release
shortcut with the same action it will be triggered each click. Unless you are describing some bug.
Oh, yea thats an issue. This will push a new action into the internal queue and wait for it to finish, but since you are inside another action that is already getting processed it causes a dead lock.
Don’t know if there is an easy way to fix this.
For now you can invoke the action directly bypassing the internal queue by adding , invokeDirectly: true
as the last argument to InvokeAction
Your queue does not run each action on the same thread, because you call ProcessActionQueue
from two different tasks that can run on two different threads. Also you are creating Task actions (async lambdas) that are passed to EnqueueAction and they can also run on different threads.
Async/await programming is not that straight forward if you have threads/tasks/UI mixed together.
Thanks again for the detailed write up!
I’ll take a look at cleaning everything up properly and the invoke immediately did indeed fix it so I can get rid of the queue. So thanks for that.
The only thing that doesn’t work like you said is the toggle. I really think this is a bug then:
Blockquote If you add a
Button Press
andButton Release
shortcut with the same action it will be triggered each click. Unless you are describing some bug.
Adding a press and release will just trigger the action twice for the same log line (if the logline triggers an action which it doesn’t always do). I really think that if your intention is to trigger the listeners on each log line, then there is a bug.
To illustrate this I’ve started up MFP from scratch and kept all the DeoVRInputProcessor logs and the triggered action logs. There are 2 things of note
- You can see that the actions are only triggered after two DeoVR messages are received. So only when is down (enabled) is false will the action trigger. Every other log line.
- Even though the plugin is running and I’ve intentionally waited a few seconds and made sure DeoVR showed connected. The first commands are ignored all together.
Logs
2024-12-30 20:27:20.4287|DEBUG|MultiFunPlayer.Input.DeoVR.DeoVRInputProcessor|Waiting for incoming TCP connection [Endpoint: 0.0.0.0:38882]
2024-12-30 20:27:22.0605|DEBUG|MultiFunPlayer.Input.DeoVR.DeoVRInputProcessor|Received command “{“ReturnToBase”:false,“ManualMode”:false,“EdgingMode”:false,“ScriptPaused”:true}”
2024-12-30 20:27:22.0605|DEBUG|MultiFunPlayer.Input.DeoVR.DeoVRInputProcessor|Waiting for incoming TCP connection [Endpoint: 0.0.0.0:38882]
2024-12-30 20:27:33.6998|DEBUG|MultiFunPlayer.Input.DeoVR.DeoVRInputProcessor|Received command “{“Time”:“2024-12-30T19:27:35.942822Z”,“IsHolding”:false,“IsDown”:false,“Type”:3}”
2024-12-30 20:27:33.6998|DEBUG|MultiFunPlayer.Input.DeoVR.DeoVRInputProcessor|Waiting for incoming TCP connection [Endpoint: 0.0.0.0:38882]
2024-12-30 20:27:35.0535|DEBUG|MultiFunPlayer.Input.DeoVR.DeoVRInputProcessor|Received command “{“Time”:“2024-12-30T19:27:37.299761Z”,“IsHolding”:false,“IsDown”:true,“Type”:3}”
2024-12-30 20:27:35.0535|DEBUG|MultiFunPlayer.Input.DeoVR.DeoVRInputProcessor|Waiting for incoming TCP connection [Endpoint: 0.0.0.0:38882]
2024-12-30 20:27:35.9107|DEBUG|MultiFunPlayer.Input.DeoVR.DeoVRInputProcessor|Received command “{“Time”:“2024-12-30T19:27:38.154497Z”,“IsHolding”:false,“IsDown”:false,“Type”:3}”
2024-12-30 20:27:35.9107|DEBUG|MultiFunPlayer.Input.DeoVR.DeoVRInputProcessor|Waiting for incoming TCP connection [Endpoint: 0.0.0.0:38882]
2024-12-30 20:27:36.1072|TRACE|MultiFunPlayer.Shortcut.ShortcutActionRunner|Invoking “RemoteControlPlugin::EdgingMode” action [Configuration: “”, Gesture: SimpleInputGestureData { }]
2024-12-30 20:27:36.1427|TRACE|MultiFunPlayer.Shortcut.ShortcutActionRunner|Invoking “Debug::Log” action [Configuration: “Info, >>>>> Edging mode action triggered”, Gesture: SimpleInputGestureData { }]
2024-12-30 20:27:36.1427|INFO|MultiFunPlayer|>>>>> Edging mode action triggered
2024-12-30 20:27:36.9913|DEBUG|MultiFunPlayer.Input.DeoVR.DeoVRInputProcessor|Received command “{“Time”:“2024-12-30T19:27:39.2346Z”,“IsHolding”:false,“IsDown”:true,“Type”:3}”
2024-12-30 20:27:36.9913|DEBUG|MultiFunPlayer.Input.DeoVR.DeoVRInputProcessor|Waiting for incoming TCP connection [Endpoint: 0.0.0.0:38882]
2024-12-30 20:27:37.9106|DEBUG|MultiFunPlayer.Input.DeoVR.DeoVRInputProcessor|Received command “{“Time”:“2024-12-30T19:27:40.143938Z”,“IsHolding”:false,“IsDown”:false,“Type”:3}”
2024-12-30 20:27:37.9106|DEBUG|MultiFunPlayer.Input.DeoVR.DeoVRInputProcessor|Waiting for incoming TCP connection [Endpoint: 0.0.0.0:38882]
2024-12-30 20:27:38.1117|TRACE|MultiFunPlayer.Shortcut.ShortcutActionRunner|Invoking “RemoteControlPlugin::EdgingMode” action [Configuration: “”, Gesture: SimpleInputGestureData { }]
2024-12-30 20:27:38.1117|TRACE|MultiFunPlayer.Shortcut.ShortcutActionRunner|Invoking “Debug::Log” action [Configuration: “Info, >>>>> Edging mode action triggered”, Gesture: SimpleInputGestureData { }]
2024-12-30 20:27:38.1117|INFO|MultiFunPlayer|>>>>> Edging mode action triggered
2024-12-30 20:27:38.4732|DEBUG|MultiFunPlayer.Input.DeoVR.DeoVRInputProcessor|Received command “{“Time”:“2024-12-30T19:27:40.710938Z”,“IsHolding”:false,“IsDown”:true,“Type”:3}”
2024-12-30 20:27:38.4732|DEBUG|MultiFunPlayer.Input.DeoVR.DeoVRInputProcessor|Waiting for incoming TCP connection [Endpoint: 0.0.0.0:38882]
2024-12-30 20:27:39.5451|DEBUG|MultiFunPlayer.Input.DeoVR.DeoVRInputProcessor|Received command “{“Time”:“2024-12-30T19:27:41.788289Z”,“IsHolding”:false,“IsDown”:false,“Type”:3}”
2024-12-30 20:27:39.5451|DEBUG|MultiFunPlayer.Input.DeoVR.DeoVRInputProcessor|Waiting for incoming TCP connection [Endpoint: 0.0.0.0:38882]
2024-12-30 20:27:39.7452|TRACE|MultiFunPlayer.Shortcut.ShortcutActionRunner|Invoking “RemoteControlPlugin::EdgingMode” action [Configuration: “”, Gesture: SimpleInputGestureData { }]
2024-12-30 20:27:39.7452|TRACE|MultiFunPlayer.Shortcut.ShortcutActionRunner|Invoking “Debug::Log” action [Configuration: “Info, >>>>> Edging mode action triggered”, Gesture: SimpleInputGestureData { }]
2024-12-30 20:27:39.7452|INFO|MultiFunPlayer|>>>>> Edging mode action triggered
Sorry I still dont understand.
Received command "{"ReturnToBase":false,"ManualMode":false,"EdgingMode":false,"ScriptPaused":true}"
Waiting for incoming TCP connection [Endpoint: 0.0.0.0:38882]
Button Press
Received command "{"ReturnToBase":false,"ManualMode":false,"EdgingMode":false,"ScriptPaused":false}"
Button Release
Waiting for incoming TCP connection [Endpoint: 0.0.0.0:38882]
Received command "{"ReturnToBase":false,"ManualMode":false,"EdgingMode":false,"ScriptPaused":true}"
Button Press
Waiting for incoming TCP connection [Endpoint: 0.0.0.0:38882]
Received command "{"ReturnToBase":false,"ManualMode":false,"EdgingMode":false,"ScriptPaused":false}"
Button Release
Waiting for incoming TCP connection [Endpoint: 0.0.0.0:38882]
Received command "{"ReturnToBase":false,"ManualMode":false,"EdgingMode":false,"ScriptPaused":true}"
Button Press
Waiting for incoming TCP connection [Endpoint: 0.0.0.0:38882]
Received command "{"ReturnToBase":false,"ManualMode":false,"EdgingMode":false,"ScriptPaused":false}"
Button Release
Waiting for incoming TCP connection [Endpoint: 0.0.0.0:38882]
edit:
The whole “api” for those 4 buttons is the most convoluted shit I’ve seen.
It seems like the “[Hold]” versions of haptics shortcuts are not being sent on “v3” api (which MFP uses), they were sent only on “v2”, also the ReturnToBase
button only is sent on the false state, the true state is not being sent because of some magic if checks.
Haha yeah noticed some strange things too. Most notably the ReturnToBase command sends
Received command "{"ReturnToBase":false,"ManualMode":false,"EdgingMode":false,"ScriptPaused":false}"
while the rest does this
Received command “{“Time”:“2024-12-30T19:27:35.942822Z”,“IsHolding”:false,“IsDown”:false,“Type”:3}”
That’s why I misuse the pause script button for return to base at the moment. As in my logic return to base works like a switch but with SLR it works differently I suppose. Haven’t used my handy in a while and never used that feature on the Handy so I don’t know how it is supposed to work. Could check for you if you don’t have one yourself.
Okay, so the button press was configured as a button click (my bad). After changing that to button press the behavior is indeed a lot better. There is still some weird behavior though: The first few commands are always missed and sometimes I notice a haptic command not resulting in a button trigger. It also dropped out for a while after I put the headset down and put it back on again but I’m guessing that it just lost connection there for a while. It works a lot better now though so that’s nice.
Annotated logs
--> First two aren't registering
2024-12-31 10:16:02.0249|DEBUG|MultiFunPlayer.Input.DeoVR.DeoVRInputProcessor|Received command "{"Time":"2024-12-31T09:16:03.170535Z","IsHolding":false,"IsDown":true,"Type":3}"
2024-12-31 10:16:04.0607|DEBUG|MultiFunPlayer.Input.DeoVR.DeoVRInputProcessor|Received command "{"Time":"2024-12-31T09:16:05.205943Z","IsHolding":false,"IsDown":false,"Type":3}"
2024-12-31 10:16:05.1422|DEBUG|MultiFunPlayer.Input.DeoVR.DeoVRInputProcessor|Received command "{"Time":"2024-12-31T09:16:06.283747Z","IsHolding":false,"IsDown":true,"Type":3}"
2024-12-31 10:16:05.1422|INFO|MultiFunPlayer|Button press
2024-12-31 10:16:06.4434|DEBUG|MultiFunPlayer.Input.DeoVR.DeoVRInputProcessor|Received command "{"Time":"2024-12-31T09:16:07.30625Z","IsHolding":false,"IsDown":false,"Type":3}"
2024-12-31 10:16:06.4434|INFO|MultiFunPlayer|Button release
2024-12-31 10:16:07.0139|DEBUG|MultiFunPlayer.Input.DeoVR.DeoVRInputProcessor|Received command "{"Time":"2024-12-31T09:16:07.795076Z","IsHolding":false,"IsDown":true,"Type":3}"
2024-12-31 10:16:07.0139|INFO|MultiFunPlayer|Button press
--> This one doesn't trigger the action
2024-12-31 10:16:07.6388|DEBUG|MultiFunPlayer.Input.DeoVR.DeoVRInputProcessor|Received command "{"Time":"2024-12-31T09:16:08.694596Z","IsHolding":false,"IsDown":true,"Type":3}"
2024-12-31 10:16:07.7867|DEBUG|MultiFunPlayer.Input.DeoVR.DeoVRInputProcessor|Received command "{"Time":"2024-12-31T09:16:08.939403Z","IsHolding":false,"IsDown":false,"Type":3}"
2024-12-31 10:16:07.7867|INFO|MultiFunPlayer|Button release
2024-12-31 10:16:08.0500|DEBUG|MultiFunPlayer.Input.DeoVR.DeoVRInputProcessor|Received command "{"Time":"2024-12-31T09:16:09.194332Z","IsHolding":false,"IsDown":true,"Type":3}"
2024-12-31 10:16:08.0500|INFO|MultiFunPlayer|Button press
--> Tried again later, it didn't produce any logs or actions for a while but then it did again
2024-12-31 10:24:29.5606|DEBUG|MultiFunPlayer.Input.DeoVR.DeoVRInputProcessor|Received command "{"Time":"2024-12-31T09:24:30.446153Z","IsHolding":false,"IsDown":true,"Type":3}"
2024-12-31 10:24:30.1583|DEBUG|MultiFunPlayer.Input.DeoVR.DeoVRInputProcessor|Received command "{"Time":"2024-12-31T09:24:31.313858Z","IsHolding":false,"IsDown":false,"Type":3}"
2024-12-31 10:24:30.1583|INFO|MultiFunPlayer|Button release
2024-12-31 10:24:30.8521|DEBUG|MultiFunPlayer.Input.DeoVR.DeoVRInputProcessor|Received command "{"Time":"2024-12-31T09:24:32.013793Z","IsHolding":false,"IsDown":true,"Type":3}"
2024-12-31 10:24:30.8521|INFO|MultiFunPlayer|Button press
2024-12-31 10:24:31.3621|DEBUG|MultiFunPlayer.Input.DeoVR.DeoVRInputProcessor|Received command "{"Time":"2024-12-31T09:24:32.51424Z","IsHolding":false,"IsDown":false,"Type":3}"
2024-12-31 10:24:31.3621|INFO|MultiFunPlayer|Button release
--> keeps working from here
Anyways, I’ve been diving deeper into MFP now that I’ve got it running smoothly, and I’m discovering all kinds of great features it brings to the table. Really a lot nicer then just the handy controls once you get it going!
I must say though, that I did find the initial setup and learning process to be pretty challenging. Have you thought about some centralized (community) documentation or a guide or something? Think that could make a huge difference for newbies. Especially with things like setting up the SLR buttons etc.
On a related note, I think it could be really valuable to include a SLR starter plugin with a built-in web server you can access from a Quest browser with the same sliders as people are used to from the SLR handy settings (similar to what I made but completely contained in the plugin). I won’t use it personally as I’ve got everything running the way I like now, but I feel like this would be a game-changer for any new SLR users.
Just some thoughts though
Because the system was not designed for toggles, button press/release check for rising/falling edge, in the logs your first command has a true state.
Also multiple true events are not executed unless you have Handle repeating
option checked in the shortcut settings.
I’ve added a separate shortcuts for toggles, using current shortcuts for toggles had too many small issues.
Toggle shortcuts also provide state data you can use:
RegisterAction<IToggleInputGestureData>("Plugin::Action",
data => Logger.Info("State: {0}, IsInitialState: {1}", data.State, data.IsInitialState));
I’ll send you a build on patreon.
Its supposed to be here: intro | MultiFunPlayer
But I can never get around to actually writing it. There is a lot of features and their behavior with other features to document.
Cool! That sounds like a great solution.
And yeah writing documentation is a bitch. Know that from experience
Anyways, time to recover from the new years hangover now
Hi I am unable to connect to SLR with the following error:
Error when connecting to DeoVR:
MultiFunPlayer.MediaSource.MediaSourceException: Could not find a running DeoVR process
at MultiFunPlayer.MediaSource.ViewModels.DeoVRMediaSource.OnConnectingAsync(ConnectionType connectionType)
at MultiFunPlayer.MediaSource.AbstractMediaSource.ConnectAsync(ConnectionType connectionType)
I have a premium SLR subscription and a patreon version of MFP. I have tried both login and code methods with the same error.
Current endpoint is default: 127.0.0.1:23554
Any suggestions to connect?
How do I get the current time position of the script shown in graph?
Nvm it’s double start = ReadProperty<double>("Media::Position");
Did you enable the necessary options in DeoVR as described in the patreon post?
If you are running DeoVR on a quest you need to change the endpoint IP to your quest IP.
I now have a working SetariaPlayer written as MFP plugin
Is it better then original one? Probably not yet (tho I’m not sure, maybe it is? Would someone like to test it out?)
Can I see it being better? Totally yes
For scenes, I cut off the whole animation strip, and run the piece game requests, with matching animation speed, looped if needed
When nothing happens there is a simple filler
For guns, attacks and damages, I add them over the filler.
Better algorithms definitely exist, but this simple one is already good enough
- LutellariaMFPPlugin.zip ~ pixeldrain (unpack into mfp/plugins, requires latest patreon MFP)
@Yoooi I just bought the patreon version, and need little help please. im using quest 3 standalone connected to deovr/slr and osr2(krhull firmware) connected to laptop via serial with your software. All working good i connect and stream from slr but having trouble getting haptics buttons to work. I set the haptics assignments in deovr settings, but pressing them doesnt do anything. What am i missing?
You probably did not create shortcuts for those toggles in MFP, there is no default behavior for them.
You go to application settings at the top of the window and you can find shortcuts there.
You have to connect to DeoVR, then start the gesture capture, and press the buttons in DeoVR to enable/disable the toggle you want, they should show up in MFP.
You add “Button Press” shortcut to configure what happens when a toggle is enabled, and “Button Release” shortcut to configure what happens when a toggle is disabled.
After adding you can assign any action you want to them.
I’ve sent you the toggles build on patreon, please let me know if that works better.
cool, under actions i see like a 100 actions. I cant figure out what action is for say pause script and edging mode?
thanks
If you want to just pause the script you can use Axis::Bypas:Script::Set
, set it to true on “Button Press”, set it to false on “Button Release”.
There is no such thing as default edging mode because it means something different for different people.
One solution is to enable motion provider on L0 axis and configure it to some slow pattern you want, then you can enable it during script by using Axis::MotionProviderBlend::Set
with value set to 100%, and disable it with value set to 0%.
I would also add Axis::Sync
as first action to prevent sudden device movement.
Note that the DeoVR “buttons” behave like toggles, on one controller button press you enable edge mode, on second you disable it.
Next release will have better support for toggles as current button shortcuts have minor issues, like you will have to press the controller button 2-3 times for the first time, after that it will work normally.
What would you think of merging Click/Hold/Press/Release into a single shortcut type where you select the Invoke type from dropdown? (somewhat like the current Hold settings)
Also please add modifier keys, I’d like to have Shift+Mouse4 (or something) for “reset A0 max to 100%” action
Don’t see the point of doing it now, maybe from the start they could be made into one shortcut.
Yes I would want to add mixing of input gestures at some point.
Tho you can make this work with the current shortcuts.
- Add “Button Release” for left mouse button
- Set name in settings to “shift+mouse”
- Disable shortcut
- Add “Button Press” for shift button
- Add “Shortcut::Enabled::Set” and set name: “shift+mouse”, enabled: “true”
- Add “Button Release” for shift button
- Add “Shortcut::Enabled::Set” and set name: “shift+mouse”, enabled: “false”
This will basically enable the mouse shortcut only while holding shift.