This mod enables interactions with ChilloutVR using OSC. It's very similar to other social VR games OSC Implementation, so most external applications should work without many (if any) changes.
- Change avatar parameters
- Control the game inputs (like Gestures, movement, etc)
- Trigger special game features (like flight, mute, etc)
- Configurable endpoints (parameters address & type conversion)
- Change avatar by providing avatar id
- Spawn and delete props
- Interact with props (settings/reading their location and synced parameters)
- Retrieving the tracking data and battery info from
trackers
,hmd
,controllers
,base stations
, andplay space
- Resend all cached events (like all the current parameters) triggered via an endpoint
More features can be added, exploring CVR possibilities to the max. Feel free to submit Feature Requests in the github.
There is also a python library that abstracts all the api provided by this mod. This is a great starting point if you plan developing something in python.
- OSC Avatar
- OSC Inputs
- OSC Props
- OSC Tracking
- Avatar Json Configurations
- OSC Config
- OSC ChatBox
- Debugging
- Configuration
For now there are 6 categories of endpoints you can use:
- OSC Avatar for listening/triggering avatar changes, and also their parameters
- OSC Inputs for triggering inputs/actions.
- OSC Props for interacting with props.
- OSC Tracking to fetch tracking information (headset, controllers, trackers, play space).
- OSC Config configuration/utilities via OSC.
- OSC ChatBox to send ChatBox messages/isTyping via OSC.
This is a long read ;_; Before you get discouraged if you only want to slap a mod that enables OSC just slap the mod in
and it should work. And if all you want is to have it react to OSC messages you can turn on the Performance Mode
in
the configuration. This will make it so the mod won't send OSC messages out with the tracking data etc, and I'd say that
would be a decent basic setup.
If you are interested in listening to events from the mod then you're in for a great time! As this allows to do a lot of fun things.
I have encapsulated this API into a python library with some examples, and you won't need to worry about endpoints, parameters OSC server/client, as everything is done with dataclasses and methods to send and receive info. I will maintaining the library updated with the mod! The examples are especially interesting.
The first module is the avatar module, where you are able to interface with avatar related stuff. You can change the current avatar you're using via OSC, and also listen to those changes (you'll get the event either way).
Also you're able to change and listen to the avatar parameters. This part part has a lot of customization options because you're able to change the addresses and types via a config (not required).
Whenever you load into an avatar the mod will send a message to /avatar/change
containing as the first and only
argument a string representing the avatar UUID.
/avatar/change
Mod will send:
arg#1
- Avatar GUID [string]arg#2
- Path to the avatar json config [string], this is new to this mod, but as it is an additional param it won't break existing osc apps
Mod will receive:
arg#1
- Avatar GUID [string]
New: The mod will also listen to the address /avatar/change
, so if you send a UUID of an avatar
(that you have permission to use), it will change your avatar in-game. This is enabled
by default, but you can go to
the configurations and disable it.
You can listen and trigger parameter changes on your avatar via OSC messages, by default the endpoint to change and listen to parameter changes as follow.
/avatar/parameters/<parameter_name>
Where <parameter_name>
would be the name of your parameter. The parameter name is case sensitive!
arg#1
- Parameter value [ float | int | bool | null ], for triggers you can ignore sending a parameter, the value will be ignored either way.
These are certain limitations using the endpoint above, because according to the OSC spec you can't have #
in the
last member of the OSC address. So some OSC clients will have issues setting local parameters (because in cvr they
require a #
). I had to hack my way to force my client to allow #
on the address ;_;
/avatar/parameter
Here is where you can interact with the game in a more generic ways, allow you to send controller inputs or triggering actions in the game.
/input/<input_name>
Where the <input_name>
is the actual name of the input you're trying to change.
arg#1
- Input value [ float | int | bool ]
There are some inputs that are not present that exist in other VR Social platforms, this is due CVR not having those features implemented yet. Like rotating the object you're holding with keyboard inputs. And some others that are new, and I'll be making them as [new] while listing them.
Note: The inputs will stick with the latest value you send, so lets say if you send /input/Jump
with the value 1
will act the same as you holding down the key to Jump, and it will only be released when you send /input/Jump
with the
value 0
. So don't forget to reset them in your apps, otherwise you might end up jumping forever.
There are 3 types of Inputs:
Axes expecting a float
value that ranges between -1
/0
and 1
. They are namely used for things that require a
range of values instead of a on/off, for example the Movement, where Horizontal
can be set to -0.5
which would be
the same as having the thumbstick on your controller to the left (because it's a negative value) but only halfway
(because it's -0.5, -1 would be all the way to the left).
/input/Horizontal
- Move your avatar right1
or left-1
/input/Vertical
- Move your avatar forward1
or backwards-1
/input/LookHorizontal
- Look right1
or left-1
/input/MoveHoldFB
- Move a held object forwards1
and backwards-1
/input/LookVertical
- [new] Look up1
or down-1
/input/GripLeftValue
- [new] Left hand trigger grip released0
or pulled to max1
/input/GripRightValue
- [new] Right hand trigger grip released0
or pulled to max1
Buttons are expecting boolean
values, which can be represented by the boolean types true
for button
pressed and false
for released. You can also send integers
with the values 1
for pressed and 0
for released.
Note1: Don't forget to release the buttons, as it will prevent you from sending the press event again.
Note2: Some inputs will keep triggering the value to the input (when it says while true/1
) and others will trigger
just once (when it says when true/1
). This is referring to the little description on each input on the next section.
/input/MoveForward
- Move forward whiletrue
/1
/input/MoveBackward
- Move backwards whiletrue
/1
/input/MoveLeft
- Move left whiletrue
/1
/input/MoveRight
- Move right whiletrue
/1
/input/LookLeft
- Look left whiletrue
/1
/input/LookRight
- Look right whiletrue
/1
/input/ComfortLeft
- Look left whiletrue
/1
/input/ComfortRight
- Look right whiletrue
/1
/input/DropRight
- Drops currently held object on the right hand whentrue
/1
/input/UseRight
- Uses currently held object on the right hand whentrue
/1
/input/GrabRight
- Grabs currently object targeted by right hand (or crosshair in desktop) whentrue
/1
/input/DropLeft
- Drops currently held object on the left hand whentrue
/1
/input/UseLeft
- Uses currently held object on the left hand whentrue
/1
/input/GrabLeft
- Grabs currently object targeted by left hand (doesn't work in desktop) whentrue
/1
/input/Jump
- Jump whiletrue
/1
/input/Run
- Run whiletrue
/1
/input/PanicButton
- Disables all avatars and props whentrue
/1
(might require to reloading the instance to revert)/input/QuickMenuToggleLeft
- Toggles the quick menu whentrue
/1
/input/QuickMenuToggleRight
- Toggles the big menu whentrue
/1
/input/Voice
- Toggles the local mute setting whentrue
/1
/input/Crouch
- Toggles crouch whentrue
/1
/input/Prone
- Toggles prone whentrue
/1
/input/IndependentHeadTurn
- Enables being able to look around without moving the body whiletrue
/1
/input/Zoom
- Enables zoom whiletrue
/1
/input/Reload
- Reloads the UI whentrue
/1
[Blacklisted by default]/input/ToggleNameplates
- Toggles nameplates whentrue
/1
/input/ToggleHUD
- Toggles the HUD whentrue
/1
/input/ToggleFlightMode
- Toggles flight mode whentrue
/1
/input/Respawn
- Respawns whentrue
/1
[Blacklisted by default]/input/ToggleCamera
- Toggles camera whentrue
/1
/input/ToggleSeated
- Toggles seated mode whentrue
/1
/input/QuitGame
- Closes the game whentrue
/1
[Blacklisted by default]
You can have certain inputs blacklisted, as some can be very annoying. By default the Reload
, Respawn
, and QuitGame
are on the blacklist (Reload
at the time of writing was bugged and would crash your game (same as spamming F5
)).
You can also disable the whole input module on the configuration as well.
Values are similar to Axes
but removes the restriction of being between -1
and 1
, they are used to send values to
certain properties of the game. The values are of the type float
or int
and their range is dependent on each entry.
Note: Most of these default to the value 0, but there are exceptions. As the other inputs you need to reset to the default value otherwise they will remain the last value you sent.
/input/Emote
- Sets which emote to play when settings the value. Default:-1
/input/GestureLeft
- Sets which gesture to perform on the left hand. Default:0
/input/GestureRight
- Sets which gesture to perform on the right hand. Default:0
/input/Toggle
- Sets which toggle is active. Default:0
This mod module allows to interact with props. I've purposely added limitations to some interactions with props, you can check these on bellow. These limitations exist to prevent both abuse and some weird behavior that might happen.
All props require the Prop GUID in their address (<prop_guid>): 1aa10cac-36ba-4e01-b58d-a76dc35f61bb
This value can be known before hand, as it's the same guid that gets assigned when you upload something.
Some props require the Prop instance ID in their address (<prop_instance_id>): 8E143EA45EE8
This value is a string with 12 characters corresponding to a Hexadecimal value. This value is created when you spawn a
prop in an instance, and the best way to obtain it is by listening to the Create
and Availability
addresses.
You're able to spawn props by providing their GUID. Keep in mind that you can only spawn props you have access to and there is a limit of 20 props spawned by yourself.
You can also listen for prop spawn events, which you can use to grab the instance IDs of said props.
Here we have two addresses, when you want to create a prop you don't include an instance ID, but when you receive the information a prop was created, it will come with the instance ID on the address
/prop/create
The address is the same, but the parameters will be different depending whether you are listening for spawn events, or you're sending messages to spawn props.
Mod will send:
arg#1
- Prop GUID [string]arg#2
- Instance ID of the prop spawned [string]arg#3
- Count of sub-sync transforms [int], if count = 1 it means you can send location_sub to the index = 0
Mod is expecting to receive:
arg#1
- Prop GUID [string]arg#2
- local position x [Optional] [float]arg#3
- local position y [Optional] [float]arg#4
- local position z [Optional] [float]
Those local position arguments are optional, they define where the prop should be spawned in relation to your play space. If you want to provide a value, you need to provide all of them. If no values are provided they'll default to 0.
As the name suggests you can delete props that you've spawned. You can do so by providing the GUID of the prop as well as their instance ID to uniquely identify them.
You can also listen for prop deletion events, which you can use to know that a certain prop instance stopped existing and won't become available for interaction anymore.
/prop/delete
arg#1
- Prop GUID [string]arg#2
- Instance ID of the prop spawned [string]
This address will be called every time a prop has their availability changed. What what I mean by availability is where you are able to control or not this prop. The props become available when no remote player is grabbing, telegrabbing, nor has it attached to themselves. Also this only affects props spawned by yourself!
The only exception is it will say it is available but you won't be able to set the location if you (the local player) is grabbing, telegrabbing, or has it attached.
Obviously this is an address set by the game, so you can't send osc messages to try to change it.
/prop/available
arg#1
- Prop GUID [string]arg#2
- Instance ID of the prop spawned [string]arg#3
- Whether the prop interactions are availabletrue
, or notfalse
[bool]
Here you will be able to listen and write to the prop's synced parameters. You will need to provide the GUID of the prop and it's instance ID to do so.
- You need to be the player that spawned the prop.
- The prop not being controlled by a remote player [grabbed | telegrabbed | attached]
/prop/parameter
arg#1
- Prop GUID [string]arg#2
- Instance ID of the prop spawned [string]arg#3
- Sync parameter name (read note bellow) [string]arg#4
- Value to be set on the sync parameter [float]
Note: the Sync parameter name is the name you defined in the CVR Spawnable Component, and not the actual name of parameter inside of the animator. This name is case sensitive.
You are also able to listen and set the location of a prop. This is very powerful as you can for example link the tracking data you receive form the tracking module to a prop so it's controlled by the tracker.
Note: If you disable the Tracking Module, it will also disable the updates of the Props Location. You can still set their positions without any issue tho.
- You need to be the player that spawned the prop.
- The prop not being controlled by any player (both remote and local) [grabbed | telegrabbed | attached]
/prop/location
arg#1
- Prop GUID [string]arg#2
- Instance ID of the prop spawned [string]arg#3
- position.x [float]arg#4
- position.y [float]arg#5
- position.z [float]arg#6
- rotation.x [float]arg#7
- rotation.y [float]arg#8
- rotation.z [float]
You are also able to listen and set the location of a prop's sub-sync transforms. This is very powerful as you can for example link the tracking data you receive form the tracking module to a sub-sync so it's controlled by the tracker.
Note: If you disable the Tracking Module, it will also disable the updates of the Location Sub. You can still set their positions without any issue tho.
- You need to be the player that spawned the prop.
- The prop not being controlled by any player (both remote and local) [grabbed | telegrabbed | attached]
/prop/location_sub
arg#1
- Prop GUID [string]arg#2
- Instance ID of the prop spawned [string]arg#3
- Index of the prop sub-sync transform [int], starts from 0 and increments following the order set in the CVR Spawnable Componentarg#4
- position.x [float]arg#5
- position.y [float]arg#6
- position.z [float]arg#7
- rotation.x [float]arg#8
- rotation.y [float]arg#9
- rotation.z [float]
This mod module allows to read tracking data from the game namely from tracked devices, and the play space. You can only listen to these, don't try messages to those (or else).
The mod will keep sending the current play space position and rotation. This is especially useful if you want to create avatar animations to drive the position of objects. Because the avatar origin is the play space origin. Meaning if you have world space coordinates you want to make local to the avatar, you can do it by using the play space location data to perform the calculations.
Both the position and rotation(euler angles) are in world space. And the address we're going to be sending is:
/tracking/play_space
The values are sent as float
type arguments, and the values order is the following:
arg#1
- position.x [float]arg#2
- position.y [float]arg#3
- position.z [float]arg#4
- rotation.x [float]arg#5
- rotation.y [float]arg#6
- rotation.z [float]
You can listen here for steam vr device connected change status. Every device starts assuming it is disconnected so you
will always receive a connected = True
as the first event from a device.
/tracking/device/status
arg#1
- Connected [bool], whether the device was connectedTrue
or disconnectedFalse
arg#2
- Device type [string], Possible values:hmd
,base_station
,left_controller
,right_controller
,tracker
, andunknown
arg#3
- Steam tracked index [int], given by SteamVR, it's unique for each device (see note bellow)arg#4
- Device name [string], given by SteamVR, in some cases (like base stations) there is no name the string will be empty.
The mod also exposes the tracking information for tracked devices, like base stations, controllers, and trackers.
Both the position and rotation are for world space, and the rotation is sent in euler angles.
/tracking/device
arg#01
- Device type [string], Possible values:hmd
,base_station
,left_controller
,right_controller
,tracker
, andunknown
arg#02
- Steam tracked index [int], given by SteamVR, it's unique for each device (see note bellow)arg#03
- Device name [string], given by SteamVR, in some cases (like base stations) there is no name the string will be empty.arg#04
- Position.x [float]arg#05
- Position.y [float]arg#06
- Position.z [float]arg#07
- Rotation.x [float]arg#08
- Rotation.y [float]arg#09
- Rotation.z [float]#arg10
- Battery percentage [float], from 0 to 1. If the devices is not reporting the battery info to SteamVR it will send 0 instead.
This address will work if the game has started in VR
mode.
Note: Steam tracked index gives way to identify uniquely a connected device. This id is unique across all types of devices, and is assigned by SteamVR and seems to be incrementing from 0, it will not change until SteamVR is restarted.
There are configurations to disable/enable the tracking module and also define which update rate it should send the
osc messages. The default value for the update rate is 0
which makes it sending at every frame of the game.
Consider lowering this value if you don't need such update rates as it lowers the overhead performance impact.
The value is
defined in seconds.
Note: This will also affect the updates on the prop location updates.
This mod module allows configure/interact with the mod via osc.
This endpoint will reset the caches for both avatar and props, and re-send the init events. It's useful if you start your osc application after the game is running and require those initial events. Since this mod doesn't keep spamming updates this is very useful sync the state with your app (if you need).
/config/reset
N/A
Here is where you can send Chat Box messages or is typing notifications, this requires the mod ChatBox to work.
You can use this endpoint to send text messages via the Chat Box.
/chatbox/input
arg#1
- Message [string], the message content you want to send. The mod allows a maximum of 2000 Characters.arg#2
- Send Immediately [bool], whether the msg is directly sent, or opens the keyboard and pastes the msg.arg#3
- Sound Notification [Optional] [bool], whether the message will do a sound notification or not. Defaults toFalse
if not provided.arg#4
- Display in ChatBox [Optional] [bool], whether the message will be displayed in the ChatBox or not. Defaults toTrue
if not provided.arg#5
- Display in History Window [Optional] [bool], whether the message will be displayed in the History Window or not. Defaults toFalse
if not provided.
You can set the typing state for the ChatBox. Whether it's on or off, and whether it should send a sound notification.
/chatbox/typing
arg#2
- Is Typing [bool], whether Is Typing is active or not.arg#3
- Sound Notification [Optional] [bool], whether the start typing will do a sound notification or not. Defaults toFalse
if not provided.
When you load into an avatar you will trigger the generation of a json configuration file, this file will be located at:
"C:\Users\kafeijao\AppData\LocalLow\Alpha Blend Interactive\ChilloutVR\OSC\usr_4a0661f1-4eeb-426e-52ec-1b2f48e609b3\Avatars\avtr_5bf29e54-b3dd-4c15-ba72-c4dc6e410efb.json"
{
"id": "5bf29e54-b3dd-4c15-ba72-c4dc6e410efb",
"name": "Farday",
"parameters": [
{
"name": "GestureLeft",
"output": {
"address": "/avatar/parameters/GestureLeft",
"type": "Float"
}
},
{
"name": "OutfitBoots",
"input": {
"address": "/avatar/parameters/OutfitBoots",
"type": "Bool"
},
"output": {
"address": "/avatar/parameters/OutfitBoots",
"type": "Bool"
}
}
]
}
You can use these files to let your external programs know which parameters are available to listen/change and their types.
Having an input is optional, as there are some Parameters that you are not allowed to change (via the change parameter
address), like the GestureLeft
in our example (if you want to change those check OSC Inputs).
There are 4 types, Float
, Int
, Bool
, and Trigger
. The Trigger
type will appear here but won't work by default,
you need to enable Triggers parameter type on the mod configuration.
id
- Avatar unique idname
- Name of the avatarparameters
name
- Name of the parameter, needs to match 1:1 and it is case sensitiveinput
[Optional]address
- Address [string] the mod will listen for incoming OSC messages [ignored]type
- Expected type of the incoming data:Int
,Bool
,Float
,Trigger
[ignored]
output
address
- Address [string] where the mod will send OSC messages [working]type
- Expected type of the outgoing data:Int
,Bool
,Float
,Trigger
[working] This will actively convert the parameter to the type you specify. Even if it differs from the parameter type.
Todo:
- Handle the input type and address.
The mod will always use the json configurations output
to decide where to send the parameter changes. Also will
respect the type you chose on the output by converting before sending. But beware, if you want to use this feature
you need to disable the replaceConfigIfExists
configuration, otherwise the game will overwrite your json config files
when loading an avatar.
For now the input
address/type of the json config is not used. Maybe will be implemented in a near future.
You are able to overwrite the location of the mentioned json configs gets stored in the configurations. Also the file
path uuids get prefixed with usr_
for user ids, and avtr_
for avatar ids (this is enabled by default).
This is to ensure compatibility with other existent applications.
Currently there is no easy way to debug. I would recommend using my other mod CCK.Debugger, among other things it allows you to see a menu with all your avatar parameters. Which will update realtime including the changes via OSC.
Most options to configure are on the Melon Loader configuration file. To access it install the mod, and run the game at least one time so the configuration gets generated. After that you can visit (this might change if you have the game installed somewhere else):
C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\UserData\MelonPreferences.cfg
You can then edit and look for [OSC]
line, bellow it there should be all configurations with a little description.
You can edit whether the game is running or not, they should take effect as soon as you save the file.
You can configure the ports the mod uses to receive and send the messages. By default it receives on port 9000
and
sends on port 9001
. So your external program should send to 9000
, and in case you want to listen for messages from
the mod, your program should listen to 9001
.
You can change these values on steam Launch Options
(right click cvr and then properties), or on the Melon Loader
configuration. Note that the Launch Options
will override your Melon Loader configuration.
--osc=inPort:senderIP:outPort
Note: If you want to replicate the default settings you would use: --osc=9000:127.0.0.1:9001