mirror of
https://github.com/SlimeVR/SlimeVR-Server.git
synced 2026-04-06 02:01:58 +02:00
@@ -507,14 +507,17 @@ settings-osc-router-network-address-placeholder = IPV4 address
|
||||
## OSC VRChat settings
|
||||
settings-osc-vrchat = VRChat OSC Trackers
|
||||
# This cares about multilines
|
||||
settings-osc-vrchat-description =
|
||||
Change VRChat-specific settings to receive headset (HMD) data and send
|
||||
tracker data for FBT without SteamVR (ex. Quest standalone).
|
||||
settings-osc-vrchat-description-v1 =
|
||||
Change settings specific to the OSC Trackers standard used for sending
|
||||
tracking data to applications without SteamVR (ex. Quest standalone).
|
||||
Make sure to enable OSC in VRChat via the Action Menu under OSC > Enabled.
|
||||
To allow receiving HMD and controller data from VRChat, go in your main menu's
|
||||
settings under Tracking & IK > Allow Sending Head and Wrist VR Tracking OSC Data.
|
||||
settings-osc-vrchat-enable = Enable
|
||||
settings-osc-vrchat-enable-description = Toggle the sending and receiving of data.
|
||||
settings-osc-vrchat-enable-label = Enable
|
||||
settings-osc-vrchat-network = Network ports
|
||||
settings-osc-vrchat-network-description = Set the ports for listening and sending data to VRChat.
|
||||
settings-osc-vrchat-network-description-v1 = Set the ports for listening and sending data. Can be left untouched for VRChat.
|
||||
settings-osc-vrchat-network-port_in =
|
||||
.label = Port In
|
||||
.placeholder = Port in (default: 9001)
|
||||
@@ -522,7 +525,7 @@ settings-osc-vrchat-network-port_out =
|
||||
.label = Port Out
|
||||
.placeholder = Port out (default: 9000)
|
||||
settings-osc-vrchat-network-address = Network address
|
||||
settings-osc-vrchat-network-address-description = Choose which address to send out data to VRChat (check your Wi-Fi settings on your device).
|
||||
settings-osc-vrchat-network-address-description-v1 = Choose which address to send out data to. Can be left untouched for VRChat.
|
||||
settings-osc-vrchat-network-address-placeholder = VRChat ip address
|
||||
settings-osc-vrchat-network-trackers = Trackers
|
||||
settings-osc-vrchat-network-trackers-description = Toggle the sending of specific trackers via OSC.
|
||||
|
||||
@@ -131,7 +131,7 @@ export function VRCOSCSettings() {
|
||||
<div className="flex flex-col pt-2 pb-4">
|
||||
<>
|
||||
{l10n
|
||||
.getString('settings-osc-vrchat-description')
|
||||
.getString('settings-osc-vrchat-description-v1')
|
||||
.split('\n')
|
||||
.map((line, i) => (
|
||||
<Typography color="secondary" key={i}>
|
||||
@@ -162,7 +162,7 @@ export function VRCOSCSettings() {
|
||||
</Typography>
|
||||
<div className="flex flex-col pb-2">
|
||||
<Typography color="secondary">
|
||||
{l10n.getString('settings-osc-vrchat-network-description')}
|
||||
{l10n.getString('settings-osc-vrchat-network-description-v1')}
|
||||
</Typography>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-3 pb-5">
|
||||
@@ -199,7 +199,7 @@ export function VRCOSCSettings() {
|
||||
<div className="flex flex-col pb-2">
|
||||
<Typography color="secondary">
|
||||
{l10n.getString(
|
||||
'settings-osc-vrchat-network-address-description'
|
||||
'settings-osc-vrchat-network-address-description-v1'
|
||||
)}
|
||||
</Typography>
|
||||
</div>
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
package dev.slimevr.android
|
||||
|
||||
import android.content.Context
|
||||
import android.net.wifi.WifiManager
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import dev.slimevr.Keybinding
|
||||
import dev.slimevr.VRServer
|
||||
@@ -50,6 +52,12 @@ fun main(activity: AppCompatActivity) {
|
||||
vrServer = VRServer(
|
||||
configPath = File(activity.filesDir, "vrconfig.yml").absolutePath,
|
||||
serialHandlerProvider = { _ -> AndroidSerialHandler(activity) },
|
||||
acquireMulticastLock = {
|
||||
val wifi = activity.getSystemService(Context.WIFI_SERVICE) as WifiManager
|
||||
val lock = wifi.createMulticastLock("slimevr-jmdns-multicast-lock")
|
||||
lock.setReferenceCounted(true)
|
||||
lock.acquire()
|
||||
},
|
||||
)
|
||||
vrServer.start()
|
||||
Keybinding(vrServer)
|
||||
|
||||
@@ -36,7 +36,8 @@ configure<com.diffplug.gradle.spotless.SpotlessExtension> {
|
||||
"ij_kotlin_packages_to_use_import_on_demand" to
|
||||
"java.util.*,kotlin.math.*,dev.slimevr.autobone.errors.*" +
|
||||
",io.github.axisangles.ktmath.*,kotlinx.atomicfu.*" +
|
||||
",dev.slimevr.tracking.trackers.*,dev.slimevr.desktop.platform.ProtobufMessages.*",
|
||||
",dev.slimevr.tracking.trackers.*,dev.slimevr.desktop.platform.ProtobufMessages.*" +
|
||||
",com.illposed.osc.*",
|
||||
"ij_kotlin_allow_trailing_comma" to true,
|
||||
)
|
||||
val ktlintVersion = "1.2.1"
|
||||
|
||||
@@ -50,6 +50,7 @@ allprojects {
|
||||
// Use jcenter for resolving dependencies.
|
||||
// You can declare any Maven/Ivy/file repository here.
|
||||
mavenCentral()
|
||||
maven(url = "https://jitpack.io")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,12 +69,15 @@ dependencies {
|
||||
implementation("org.apache.commons:commons-lang3:3.12.0")
|
||||
implementation("org.apache.commons:commons-collections4:4.4")
|
||||
|
||||
implementation("com.illposed.osc:javaosc-core:0.8")
|
||||
implementation("com.illposed.osc:javaosc-core:0.9")
|
||||
implementation("org.java-websocket:Java-WebSocket:1.+")
|
||||
implementation("com.melloware:jintellitype:1.+")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")
|
||||
implementation("it.unimi.dsi:fastutil:8.5.12")
|
||||
|
||||
// Jitpack
|
||||
implementation("com.github.SlimeVR:oscquery-kt:566a0cba58")
|
||||
|
||||
testImplementation(kotlin("test"))
|
||||
// Use JUnit test framework
|
||||
testImplementation(platform("org.junit:junit-bom:5.9.0"))
|
||||
|
||||
@@ -47,6 +47,7 @@ class VRServer @JvmOverloads constructor(
|
||||
driverBridgeProvider: SteamBridgeProvider = { _, _ -> null },
|
||||
feederBridgeProvider: (VRServer) -> ISteamVRBridge? = { _ -> null },
|
||||
serialHandlerProvider: (VRServer) -> SerialHandler = { _ -> SerialHandlerStub() },
|
||||
acquireMulticastLock: () -> Any? = { null },
|
||||
configPath: String,
|
||||
) : Thread("VRServer") {
|
||||
@JvmField
|
||||
@@ -60,6 +61,7 @@ class VRServer @JvmOverloads constructor(
|
||||
private val tasks: Queue<Runnable> = LinkedBlockingQueue()
|
||||
private val newTrackersConsumers: MutableList<Consumer<Tracker>> = FastList()
|
||||
private val onTick: MutableList<Runnable> = FastList()
|
||||
private val lock = acquireMulticastLock()
|
||||
val oSCRouter: OSCRouter
|
||||
|
||||
@JvmField
|
||||
@@ -141,8 +143,6 @@ class VRServer @JvmOverloads constructor(
|
||||
// Initialize OSC handlers
|
||||
vrcOSCHandler = VRCOSCHandler(
|
||||
this,
|
||||
humanPoseManager,
|
||||
driverBridge,
|
||||
configManager.vrConfig.vrcOSC,
|
||||
computedTrackers,
|
||||
)
|
||||
@@ -282,13 +282,11 @@ class VRServer @JvmOverloads constructor(
|
||||
fun updateSkeletonModel() {
|
||||
queueTask {
|
||||
humanPoseManager.updateSkeletonModelFromServer()
|
||||
vrcOSCHandler.setHeadTracker(TrackerUtils.getTrackerForSkeleton(trackers, TrackerPosition.HEAD))
|
||||
if (this.getVRBridge(ISteamVRBridge::class.java)?.updateShareSettingsAutomatically() == true) {
|
||||
RPCSettingsHandler.sendSteamVRUpdatedSettings(protocolAPI, protocolAPI.rpcHandler)
|
||||
}
|
||||
}
|
||||
vrcOSCHandler.setHeadTracker(
|
||||
TrackerUtils.getTrackerForSkeleton(trackers, TrackerPosition.HEAD),
|
||||
)
|
||||
}
|
||||
|
||||
fun resetTrackersFull(resetSourceName: String?) {
|
||||
|
||||
@@ -10,6 +10,10 @@ public interface OSCHandler {
|
||||
|
||||
public void refreshSettings(boolean refreshRouterSettings);
|
||||
|
||||
public void updateOscReceiver(int portIn, String[] args);
|
||||
|
||||
public void updateOscSender(int portOut, String address);
|
||||
|
||||
public void update();
|
||||
|
||||
public OSCPortOut getOscSender();
|
||||
|
||||
@@ -70,79 +70,19 @@ class VMCHandler(
|
||||
anchorHip = config.anchorHip
|
||||
mirrorTracking = config.mirrorTracking
|
||||
|
||||
// Stops listening and closes OSC port
|
||||
val wasListening = oscReceiver != null && oscReceiver!!.isListening
|
||||
if (wasListening) {
|
||||
oscReceiver!!.stopListening()
|
||||
}
|
||||
val wasConnected = oscSender != null && oscSender!!.isConnected
|
||||
if (wasConnected) {
|
||||
try {
|
||||
oscSender!!.close()
|
||||
} catch (e: IOException) {
|
||||
LogManager.severe("[VMCHandler] Error closing the OSC sender: $e")
|
||||
}
|
||||
}
|
||||
updateOscReceiver(
|
||||
config.portIn,
|
||||
arrayOf(
|
||||
"/VMC/Ext/Bone/Pos",
|
||||
"/VMC/Ext/Hmd/Pos",
|
||||
"/VMC/Ext/Con/Pos",
|
||||
"/VMC/Ext/Tra/Pos",
|
||||
"/VMC/Ext/Root/Pos",
|
||||
),
|
||||
)
|
||||
updateOscSender(config.portOut, config.address)
|
||||
|
||||
if (config.enabled) {
|
||||
// Instantiates the OSC receiver
|
||||
try {
|
||||
val port = config.portIn
|
||||
oscReceiver = OSCPortIn(port)
|
||||
if (lastPortIn != port || !wasListening) {
|
||||
LogManager.info("[VMCHandler] Listening to port $port")
|
||||
}
|
||||
lastPortIn = port
|
||||
} catch (e: IOException) {
|
||||
LogManager
|
||||
.severe(
|
||||
"[VMCHandler] Error listening to the port ${config.portIn}: $e",
|
||||
)
|
||||
}
|
||||
|
||||
// Starts listening for VMC messages
|
||||
if (oscReceiver != null) {
|
||||
val listener = OSCMessageListener { event: OSCMessageEvent -> this.handleReceivedMessage(event) }
|
||||
val listenAddresses = arrayOf(
|
||||
"/VMC/Ext/Bone/Pos",
|
||||
"/VMC/Ext/Hmd/Pos",
|
||||
"/VMC/Ext/Con/Pos",
|
||||
"/VMC/Ext/Tra/Pos",
|
||||
"/VMC/Ext/Root/Pos",
|
||||
)
|
||||
|
||||
for (address in listenAddresses) {
|
||||
oscReceiver!!
|
||||
.dispatcher
|
||||
.addListener(OSCPatternAddressMessageSelector(address), listener)
|
||||
}
|
||||
|
||||
oscReceiver!!.startListening()
|
||||
}
|
||||
|
||||
// Instantiate the OSC sender
|
||||
try {
|
||||
val address = InetAddress.getByName(config.address)
|
||||
val port = config.portOut
|
||||
oscSender = OSCPortOut(InetSocketAddress(address, port))
|
||||
if ((lastPortOut != port && lastAddress !== address) || !wasConnected) {
|
||||
LogManager
|
||||
.info(
|
||||
"[VMCHandler] Sending to port $port at address $address",
|
||||
)
|
||||
}
|
||||
lastPortOut = port
|
||||
lastAddress = address
|
||||
|
||||
oscSender!!.connect()
|
||||
outputUnityArmature = UnityArmature(false)
|
||||
} catch (e: IOException) {
|
||||
LogManager
|
||||
.severe(
|
||||
"[VMCHandler] Error connecting to port ${config.portOut} at the address ${config.address}: $e",
|
||||
)
|
||||
}
|
||||
|
||||
// Load VRM data
|
||||
if (outputUnityArmature != null && config.vrmJson != null) {
|
||||
val vrmReader = VRMReader(config.vrmJson!!)
|
||||
@@ -176,6 +116,79 @@ class VMCHandler(
|
||||
if (refreshRouterSettings) server.oSCRouter.refreshSettings(false)
|
||||
}
|
||||
|
||||
override fun updateOscReceiver(portIn: Int, args: Array<String>) {
|
||||
// Stops listening and closes OSC port
|
||||
val wasListening = oscReceiver != null && oscReceiver!!.isListening
|
||||
if (wasListening) {
|
||||
oscReceiver!!.stopListening()
|
||||
}
|
||||
|
||||
if (config.enabled) {
|
||||
// Instantiates the OSC receiver
|
||||
try {
|
||||
oscReceiver = OSCPortIn(portIn)
|
||||
if (lastPortIn != portIn || !wasListening) {
|
||||
LogManager.info("[VMCHandler] Listening to port $portIn")
|
||||
}
|
||||
lastPortIn = portIn
|
||||
} catch (e: IOException) {
|
||||
LogManager
|
||||
.severe(
|
||||
"[VMCHandler] Error listening to the port $portIn: $e",
|
||||
)
|
||||
}
|
||||
|
||||
// Starts listening for VMC messages
|
||||
if (oscReceiver != null) {
|
||||
val listener = OSCMessageListener { event: OSCMessageEvent -> this.handleReceivedMessage(event) }
|
||||
|
||||
for (address in args) {
|
||||
oscReceiver!!
|
||||
.dispatcher
|
||||
.addListener(OSCPatternAddressMessageSelector(address), listener)
|
||||
}
|
||||
|
||||
oscReceiver!!.startListening()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateOscSender(portOut: Int, ip: String) {
|
||||
// Stop sending
|
||||
val wasConnected = oscSender != null && oscSender!!.isConnected
|
||||
if (wasConnected) {
|
||||
try {
|
||||
oscSender!!.close()
|
||||
} catch (e: IOException) {
|
||||
LogManager.severe("[VMCHandler] Error closing the OSC sender: $e")
|
||||
}
|
||||
}
|
||||
|
||||
if (config.enabled) {
|
||||
// Instantiate the OSC sender
|
||||
try {
|
||||
val addr = InetAddress.getByName(ip)
|
||||
oscSender = OSCPortOut(InetSocketAddress(addr, portOut))
|
||||
if ((lastPortOut != portOut && lastAddress !== addr) || !wasConnected) {
|
||||
LogManager
|
||||
.info(
|
||||
"[VMCHandler] Sending to port $portOut at address $ip",
|
||||
)
|
||||
}
|
||||
lastPortOut = portOut
|
||||
lastAddress = addr
|
||||
|
||||
oscSender!!.connect()
|
||||
outputUnityArmature = UnityArmature(false)
|
||||
} catch (e: IOException) {
|
||||
LogManager
|
||||
.severe(
|
||||
"[VMCHandler] Error connecting to port $portOut at the address $ip: $e",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleReceivedMessage(event: OSCMessageEvent) {
|
||||
when (event.message.address) {
|
||||
// Is bone (rotation)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package dev.slimevr.osc
|
||||
|
||||
import com.illposed.osc.MessageSelector
|
||||
import com.illposed.osc.OSCBundle
|
||||
import com.illposed.osc.OSCMessage
|
||||
import com.illposed.osc.OSCMessageEvent
|
||||
@@ -12,9 +11,7 @@ import com.illposed.osc.transport.OSCPortOut
|
||||
import com.jme3.math.FastMath
|
||||
import com.jme3.system.NanoTimer
|
||||
import dev.slimevr.VRServer
|
||||
import dev.slimevr.bridge.ISteamVRBridge
|
||||
import dev.slimevr.config.VRCOSCConfig
|
||||
import dev.slimevr.tracking.processor.HumanPoseManager
|
||||
import dev.slimevr.tracking.trackers.Device
|
||||
import dev.slimevr.tracking.trackers.Tracker
|
||||
import dev.slimevr.tracking.trackers.TrackerPosition
|
||||
@@ -28,7 +25,6 @@ import io.github.axisangles.ktmath.Vector3
|
||||
import java.io.IOException
|
||||
import java.net.InetAddress
|
||||
import java.net.InetSocketAddress
|
||||
import java.util.*
|
||||
|
||||
private const val OFFSET_SLERP_FACTOR = 0.5f // Guessed from eyeing VRChat
|
||||
|
||||
@@ -37,25 +33,36 @@ private const val OFFSET_SLERP_FACTOR = 0.5f // Guessed from eyeing VRChat
|
||||
*/
|
||||
class VRCOSCHandler(
|
||||
private val server: VRServer,
|
||||
private val humanPoseManager: HumanPoseManager,
|
||||
private val steamvrBridge: ISteamVRBridge?,
|
||||
private val config: VRCOSCConfig,
|
||||
private val computedTrackers: List<Tracker>,
|
||||
) : OSCHandler {
|
||||
private val localIp = InetAddress.getLocalHost().hostAddress
|
||||
private val loopbackIp = InetAddress.getLoopbackAddress().hostAddress
|
||||
private val vrsystemTrackersAddresses = arrayOf(
|
||||
"/tracking/vrsystem/head/pose",
|
||||
"/tracking/vrsystem/leftwrist/pose",
|
||||
"/tracking/vrsystem/rightwrist/pose",
|
||||
)
|
||||
private val oscTrackersAddresses = arrayOf(
|
||||
"/tracking/trackers/*/position",
|
||||
"/tracking/trackers/*/rotation",
|
||||
)
|
||||
private var oscReceiver: OSCPortIn? = null
|
||||
private var oscSender: OSCPortOut? = null
|
||||
private var oscQuerySender: OSCPortOut? = null
|
||||
private var oscMessage: OSCMessage? = null
|
||||
private var vrcHmd: Tracker? = null
|
||||
private var headTracker: Tracker? = null
|
||||
private var oscTrackersDevice: Device? = null
|
||||
private var vrsystemTrackersDevice: Device? = null
|
||||
private val oscArgs = FastList<Float?>(3)
|
||||
private val trackersEnabled: BooleanArray = BooleanArray(computedTrackers.size)
|
||||
private var lastPortIn = 0
|
||||
private var lastPortOut = 0
|
||||
private var lastAddress: InetAddress? = null
|
||||
private var oscPortIn = 0
|
||||
private var oscPortOut = 0
|
||||
private var oscIp: InetAddress? = null
|
||||
private var oscQuerySenderState = false
|
||||
private var oscQueryPortOut = 0
|
||||
private var oscQueryIp: String? = null
|
||||
private var timeAtLastError: Long = 0
|
||||
private val timer = Timer()
|
||||
private var listenTrackers = false
|
||||
private var receivingPositionOffset = Vector3.NULL
|
||||
private var postReceivingPositionOffset = Vector3.NULL
|
||||
private var receivingRotationOffset = Quaternion.IDENTITY
|
||||
@@ -63,6 +70,7 @@ class VRCOSCHandler(
|
||||
private val postReceivingOffset = EulerAngles(EulerOrder.YXZ, 0f, FastMath.PI, 0f).toQuaternion()
|
||||
private var timeAtLastReceivedRotationOffset = System.currentTimeMillis()
|
||||
private var fpsTimer: NanoTimer? = null
|
||||
private var vrcOscQueryHandler: VRCOSCQueryHandler? = null
|
||||
|
||||
init {
|
||||
refreshSettings(false)
|
||||
@@ -82,11 +90,96 @@ class VRCOSCHandler(
|
||||
}
|
||||
}
|
||||
|
||||
// Stops listening and closes OSC port
|
||||
updateOscReceiver(config.portIn, vrsystemTrackersAddresses + oscTrackersAddresses)
|
||||
updateOscSender(config.portOut, config.address)
|
||||
|
||||
if (vrcOscQueryHandler == null && config.enabled) {
|
||||
vrcOscQueryHandler = VRCOSCQueryHandler(this)
|
||||
} else if (vrcOscQueryHandler != null && !config.enabled) {
|
||||
vrcOscQueryHandler?.close()
|
||||
vrcOscQueryHandler = null
|
||||
}
|
||||
|
||||
if (refreshRouterSettings) {
|
||||
server.oSCRouter.refreshSettings(false)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an OSC Sender from OSCQuery
|
||||
*/
|
||||
fun addOSCQuerySender(oscPortOut: Int, oscIP: String) {
|
||||
val addr = InetAddress.getByName(oscIP)
|
||||
oscQuerySenderState = true
|
||||
oscQueryIp = oscIP
|
||||
oscQueryPortOut = oscPortOut
|
||||
if (oscPortOut != portOut || (oscIP != address.hostName && !(oscIP == localIp && address.hostName == loopbackIp))) {
|
||||
try {
|
||||
oscQuerySender = OSCPortOut(InetSocketAddress(addr, oscPortOut))
|
||||
oscQuerySender?.connect()
|
||||
LogManager.info("[VRCOSCHandler] OSCQuery sender sending to port $oscPortOut at address $oscIP")
|
||||
} catch (e: IOException) {
|
||||
LogManager.severe("[VRCOSCHandler] Error connecting to port $oscPortOut at the address $oscIP: $e")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close/remove the osc query sender
|
||||
*/
|
||||
fun closeOscQuerySender(newState: Boolean) {
|
||||
oscQuerySender?.let {
|
||||
try {
|
||||
it.close()
|
||||
oscQuerySender = null
|
||||
oscQuerySenderState = newState
|
||||
} catch (e: IOException) {
|
||||
LogManager.severe("[VRCOSCHandler] Error closing the OSC sender: $e")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateOscReceiver(portIn: Int, args: Array<String>) {
|
||||
// Stop listening
|
||||
val wasListening = oscReceiver != null && oscReceiver!!.isListening
|
||||
if (wasListening) {
|
||||
oscReceiver!!.stopListening()
|
||||
}
|
||||
|
||||
if (config.enabled) {
|
||||
// Instantiates the OSC receiver
|
||||
try {
|
||||
oscReceiver = OSCPortIn(portIn)
|
||||
if (oscPortIn != portIn || !wasListening) {
|
||||
LogManager.info("[VRCOSCHandler] Listening to port $portIn")
|
||||
}
|
||||
oscPortIn = portIn
|
||||
vrcOscQueryHandler?.updateOSCQuery(portIn.toUShort())
|
||||
} catch (e: IOException) {
|
||||
LogManager
|
||||
.severe(
|
||||
"[VRCOSCHandler] Error listening to the port $portIn: $e",
|
||||
)
|
||||
}
|
||||
|
||||
// Starts listening for VRC or OSCTrackers messages
|
||||
oscReceiver?.let {
|
||||
val listener = OSCMessageListener { event: OSCMessageEvent ->
|
||||
handleReceivedMessage(event)
|
||||
}
|
||||
for (address in args) {
|
||||
it.dispatcher.addListener(
|
||||
OSCPatternAddressMessageSelector(address),
|
||||
listener,
|
||||
)
|
||||
}
|
||||
it.startListening()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateOscSender(portOut: Int, ip: String) {
|
||||
// Stop sending
|
||||
val wasConnected = oscSender != null && oscSender!!.isConnected
|
||||
if (wasConnected) {
|
||||
try {
|
||||
@@ -95,221 +188,218 @@ class VRCOSCHandler(
|
||||
LogManager.severe("[VRCOSCHandler] Error closing the OSC sender: $e")
|
||||
}
|
||||
}
|
||||
|
||||
if (config.enabled) {
|
||||
// Instantiates the OSC receiver
|
||||
try {
|
||||
val port = config.portIn
|
||||
oscReceiver = OSCPortIn(port)
|
||||
if (lastPortIn != port || !wasListening) {
|
||||
LogManager.info("[VRCOSCHandler] Listening to port $port")
|
||||
}
|
||||
lastPortIn = port
|
||||
} catch (e: IOException) {
|
||||
LogManager
|
||||
.severe(
|
||||
"[VRCOSCHandler] Error listening to the port " +
|
||||
config.portIn +
|
||||
": " +
|
||||
e,
|
||||
)
|
||||
}
|
||||
|
||||
// Starts listening for VRC or OSCTrackers messages
|
||||
if (oscReceiver != null) {
|
||||
val listener = OSCMessageListener { event: OSCMessageEvent -> handleReceivedMessage(event) }
|
||||
val vrcSelector: MessageSelector = OSCPatternAddressMessageSelector(
|
||||
"/avatar/parameters/Upright",
|
||||
)
|
||||
val trackersPositionSelector: MessageSelector = OSCPatternAddressMessageSelector(
|
||||
"/tracking/trackers/*/position",
|
||||
)
|
||||
val trackersRotationSelector: MessageSelector = OSCPatternAddressMessageSelector(
|
||||
"/tracking/trackers/*/rotation",
|
||||
)
|
||||
oscReceiver!!.dispatcher.addListener(vrcSelector, listener)
|
||||
oscReceiver!!.dispatcher.addListener(trackersPositionSelector, listener)
|
||||
oscReceiver!!.dispatcher.addListener(trackersRotationSelector, listener)
|
||||
listenTrackers = false
|
||||
oscReceiver!!.startListening()
|
||||
// Delay so we can actually detect if SteamVR is running
|
||||
scheduleStartListeningSteamVR(1000)
|
||||
}
|
||||
|
||||
// Instantiate the OSC sender
|
||||
try {
|
||||
val address = InetAddress.getByName(config.address)
|
||||
val port = config.portOut
|
||||
oscSender = OSCPortOut(InetSocketAddress(address, port))
|
||||
if ((lastPortOut != port && lastAddress !== address) || !wasConnected) {
|
||||
LogManager
|
||||
.info(
|
||||
"[VRCOSCHandler] Sending to port " +
|
||||
port +
|
||||
" at address " +
|
||||
address.toString(),
|
||||
)
|
||||
val addr = InetAddress.getByName(ip)
|
||||
oscSender = OSCPortOut(InetSocketAddress(addr, portOut))
|
||||
if (oscPortOut != portOut && oscIp !== addr || !wasConnected) {
|
||||
LogManager.info("[VRCOSCHandler] Sending to port $portOut at address $ip")
|
||||
}
|
||||
lastPortOut = port
|
||||
lastAddress = address
|
||||
oscSender!!.connect()
|
||||
oscPortOut = portOut
|
||||
oscIp = addr
|
||||
oscSender?.connect()
|
||||
} catch (e: IOException) {
|
||||
LogManager
|
||||
.severe(
|
||||
"[VRCOSCHandler] Error connecting to port " +
|
||||
config.portOut +
|
||||
" at the address " +
|
||||
config.address +
|
||||
": " +
|
||||
e,
|
||||
"[VRCOSCHandler] Error connecting to port $portOut at the address $ip: $e",
|
||||
)
|
||||
return
|
||||
}
|
||||
}
|
||||
if (refreshRouterSettings) server.oSCRouter.refreshSettings(false)
|
||||
}
|
||||
|
||||
private fun scheduleStartListeningSteamVR(delay: Long) {
|
||||
val resetTask: TimerTask = object : TimerTask() {
|
||||
override fun run() {
|
||||
listenTrackers = true
|
||||
if (oscQueryPortOut == portOut && (oscQueryIp == ip || (oscQueryIp == localIp && ip == loopbackIp))) {
|
||||
if (oscQuerySender != null) {
|
||||
// Close the oscQuerySender if it has the same port/ip
|
||||
closeOscQuerySender(true)
|
||||
}
|
||||
} else if (oscQuerySender == null && oscQuerySenderState) {
|
||||
// Instantiate the oscQuerySender if it could not be instantiated.
|
||||
addOSCQuerySender(oscQueryPortOut, oscQueryIp!!)
|
||||
}
|
||||
}
|
||||
timer.schedule(resetTask, delay)
|
||||
}
|
||||
|
||||
private fun handleReceivedMessage(event: OSCMessageEvent) {
|
||||
if (listenTrackers) {
|
||||
if (event.message.address.equals("/avatar/parameters/Upright")) {
|
||||
// Receiving HMD data from VRChat
|
||||
if (steamvrBridge != null && !steamvrBridge.isConnected()) {
|
||||
if (vrcHmd == null) {
|
||||
val vrcDevice = server.deviceManager.createDevice("VRChat OSC", null, "VRChat")
|
||||
server.deviceManager.addDevice(vrcDevice)
|
||||
vrcHmd = Tracker(
|
||||
device = vrcDevice,
|
||||
id = VRServer.getNextLocalTrackerId(),
|
||||
name = "VRC HMD",
|
||||
displayName = "VRC HMD",
|
||||
trackerPosition = TrackerPosition.HEAD,
|
||||
trackerNum = 0,
|
||||
hasPosition = true,
|
||||
userEditable = false,
|
||||
isComputed = true,
|
||||
usesTimeout = true,
|
||||
isHmd = true,
|
||||
)
|
||||
vrcDevice.trackers[0] = vrcHmd!!
|
||||
server.registerTracker(vrcHmd!!)
|
||||
}
|
||||
if (vrsystemTrackersAddresses.contains(event.message.address)) {
|
||||
// Receiving Head and Wrist pose data thanks to OSCQuery
|
||||
// Create device if it doesn't exist
|
||||
if (vrsystemTrackersDevice == null) {
|
||||
// Instantiate OSC Trackers device
|
||||
vrsystemTrackersDevice = server.deviceManager.createDevice("VRC VRSystem", null, "VRChat")
|
||||
server.deviceManager.addDevice(vrsystemTrackersDevice!!)
|
||||
}
|
||||
|
||||
// Sets HMD status to OK
|
||||
vrcHmd!!.status = TrackerStatus.OK
|
||||
// Look at xxx in "/tracking/vrsystem/xxx/pose" to know TrackerPosition
|
||||
var name = "VRChat "
|
||||
val trackerPosition = when (event.message.address.split('/')[3]) {
|
||||
"head" -> {
|
||||
name += "head"
|
||||
TrackerPosition.HEAD
|
||||
}
|
||||
|
||||
// Sets the HMD y position to
|
||||
// the vrc Upright parameter (0-1) * the user's height
|
||||
vrcHmd!!
|
||||
.position = Vector3(
|
||||
0f,
|
||||
event
|
||||
.message
|
||||
.arguments[0] as Float * humanPoseManager.userHeightFromConfig,
|
||||
0f,
|
||||
"leftwrist" -> {
|
||||
name += "left hand"
|
||||
TrackerPosition.LEFT_HAND
|
||||
}
|
||||
|
||||
"rightwrist" -> {
|
||||
name += "right hand"
|
||||
TrackerPosition.RIGHT_HAND
|
||||
}
|
||||
|
||||
else -> {
|
||||
LogManager.warning("[VRCOSCHandler] Received invalid body part in message \"${event.message.address}\"")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Try to get the tracker
|
||||
var tracker = vrsystemTrackersDevice!!.trackers[trackerPosition.ordinal]
|
||||
|
||||
// Build the tracker if it doesn't exist
|
||||
if (tracker == null) {
|
||||
tracker = Tracker(
|
||||
device = vrsystemTrackersDevice,
|
||||
id = VRServer.getNextLocalTrackerId(),
|
||||
name = name,
|
||||
displayName = name,
|
||||
trackerNum = trackerPosition.ordinal,
|
||||
trackerPosition = trackerPosition,
|
||||
hasRotation = true,
|
||||
hasPosition = true,
|
||||
userEditable = true,
|
||||
isComputed = true,
|
||||
needsReset = trackerPosition != TrackerPosition.HEAD,
|
||||
usesTimeout = true,
|
||||
)
|
||||
vrsystemTrackersDevice!!.trackers[trackerPosition.ordinal] = tracker
|
||||
server.registerTracker(tracker)
|
||||
}
|
||||
|
||||
// Sets the tracker status to OK
|
||||
tracker.status = TrackerStatus.OK
|
||||
|
||||
// Update tracker position
|
||||
tracker.position = Vector3(
|
||||
event.message.arguments[0] as Float,
|
||||
event.message.arguments[1] as Float,
|
||||
-(event.message.arguments[2] as Float),
|
||||
)
|
||||
|
||||
// Update tracker rotation
|
||||
val (w, x, y, z) = EulerAngles(
|
||||
EulerOrder.YXZ,
|
||||
event.message.arguments[3] as Float * FastMath.DEG_TO_RAD,
|
||||
event.message.arguments[4] as Float * FastMath.DEG_TO_RAD,
|
||||
event.message.arguments[5] as Float * FastMath.DEG_TO_RAD,
|
||||
).toQuaternion()
|
||||
val rot = Quaternion(w, -x, -y, z)
|
||||
tracker.setRotation(rot)
|
||||
|
||||
tracker.dataTick()
|
||||
} else {
|
||||
// Receiving OSC Trackers data. This is not from VRChat.
|
||||
if (oscTrackersDevice == null) {
|
||||
// Instantiate OSC Trackers device
|
||||
oscTrackersDevice = server.deviceManager.createDevice("OSC Tracker", null, "OSC Trackers")
|
||||
server.deviceManager.addDevice(oscTrackersDevice!!)
|
||||
}
|
||||
|
||||
// Extract the xxx in "/tracking/trackers/xxx/..."
|
||||
val splitAddress = event.message.address.split('/')
|
||||
val trackerStringValue = splitAddress[3]
|
||||
val dataType = event.message.address.split('/')[4]
|
||||
if (trackerStringValue == "head") {
|
||||
// Head data
|
||||
if (dataType == "position") {
|
||||
// Position offset
|
||||
receivingPositionOffset = Vector3(
|
||||
event.message.arguments[0] as Float,
|
||||
event.message.arguments[1] as Float,
|
||||
-(event.message.arguments[2] as Float),
|
||||
)
|
||||
vrcHmd!!.dataTick()
|
||||
}
|
||||
} else {
|
||||
// Receiving OSC Trackers data
|
||||
if (oscTrackersDevice == null) {
|
||||
// Instantiate OSC Trackers device
|
||||
oscTrackersDevice = server.deviceManager.createDevice("OSC Tracker", null, "OSC Trackers")
|
||||
server.deviceManager.addDevice(oscTrackersDevice!!)
|
||||
}
|
||||
|
||||
// Extract the x in "/tracking/trackers/x.../..."
|
||||
val trackerStringValue = event.message.address.toString().subSequence(19, 20)
|
||||
if (trackerStringValue == "h") {
|
||||
// Head data
|
||||
val slimeHead = headTracker
|
||||
if (event.message.address.toString() == "/tracking/trackers/head/position") {
|
||||
// Position offset
|
||||
receivingPositionOffset = Vector3(
|
||||
event.message.arguments[0] as Float,
|
||||
event.message.arguments[1] as Float,
|
||||
-(event.message.arguments[2] as Float),
|
||||
)
|
||||
|
||||
if (slimeHead != null && slimeHead.hasPosition) {
|
||||
postReceivingPositionOffset = slimeHead.position
|
||||
headTracker?.let {
|
||||
if (it.hasPosition) {
|
||||
postReceivingPositionOffset = it.position
|
||||
}
|
||||
} else {
|
||||
// Rotation offset
|
||||
val (w, x, y, z) = EulerAngles(EulerOrder.YXZ, event.message.arguments[0] as Float * FastMath.DEG_TO_RAD, event.message.arguments[1] as Float * FastMath.DEG_TO_RAD, event.message.arguments[2] as Float * FastMath.DEG_TO_RAD).toQuaternion()
|
||||
receivingRotationOffsetGoal = Quaternion(w, -x, -y, z).inv()
|
||||
}
|
||||
} else {
|
||||
// Rotation offset
|
||||
val (w, x, y, z) = EulerAngles(EulerOrder.YXZ, event.message.arguments[0] as Float * FastMath.DEG_TO_RAD, event.message.arguments[1] as Float * FastMath.DEG_TO_RAD, event.message.arguments[2] as Float * FastMath.DEG_TO_RAD).toQuaternion()
|
||||
receivingRotationOffsetGoal = Quaternion(w, -x, -y, z).inv()
|
||||
|
||||
receivingRotationOffsetGoal = if (slimeHead != null && slimeHead.hasRotation) {
|
||||
slimeHead.getRotation().project(Vector3.POS_Y).unit() * receivingRotationOffsetGoal
|
||||
headTracker.let {
|
||||
receivingRotationOffsetGoal = if (it != null && it.hasRotation) {
|
||||
it.getRotation().project(Vector3.POS_Y).unit() * receivingRotationOffsetGoal
|
||||
} else {
|
||||
receivingRotationOffsetGoal
|
||||
}
|
||||
|
||||
// If greater than 300ms, snap to rotation
|
||||
if (System.currentTimeMillis() - timeAtLastReceivedRotationOffset > 300) {
|
||||
receivingRotationOffset = receivingRotationOffsetGoal
|
||||
}
|
||||
|
||||
// Update time variable
|
||||
timeAtLastReceivedRotationOffset = System.currentTimeMillis()
|
||||
}
|
||||
} else {
|
||||
// Trackers data (1-8)
|
||||
val trackerId = trackerStringValue[0].digitToInt()
|
||||
var tracker = oscTrackersDevice!!.trackers[trackerId]
|
||||
|
||||
if (tracker == null) {
|
||||
tracker = Tracker(
|
||||
device = oscTrackersDevice,
|
||||
id = VRServer.getNextLocalTrackerId(),
|
||||
name = "OSC Tracker #$trackerId",
|
||||
displayName = "OSC Tracker #$trackerId",
|
||||
trackerNum = trackerId,
|
||||
trackerPosition = null,
|
||||
hasRotation = true,
|
||||
hasPosition = true,
|
||||
userEditable = true,
|
||||
isComputed = true,
|
||||
needsReset = true,
|
||||
usesTimeout = true,
|
||||
)
|
||||
oscTrackersDevice!!.trackers[trackerId] = tracker
|
||||
server.registerTracker(tracker)
|
||||
}
|
||||
|
||||
// Sets the tracker status to OK
|
||||
tracker.status = TrackerStatus.OK
|
||||
|
||||
if (event.message.address.toString() == "/tracking/trackers/$trackerId/position") {
|
||||
tracker.position = receivingRotationOffset.sandwich(
|
||||
Vector3(
|
||||
event.message.arguments[0] as Float,
|
||||
event.message.arguments[1] as Float,
|
||||
-(event.message.arguments[2] as Float),
|
||||
) - receivingPositionOffset,
|
||||
) + postReceivingPositionOffset
|
||||
} else {
|
||||
val (w, x, y, z) = EulerAngles(EulerOrder.YXZ, event.message.arguments[0] as Float * FastMath.DEG_TO_RAD, event.message.arguments[1] as Float * FastMath.DEG_TO_RAD, event.message.arguments[2] as Float * FastMath.DEG_TO_RAD).toQuaternion()
|
||||
val rot = Quaternion(w, -x, -y, z)
|
||||
tracker.setRotation(receivingRotationOffset * rot * postReceivingOffset)
|
||||
// If greater than 300ms, snap to rotation
|
||||
if (System.currentTimeMillis() - timeAtLastReceivedRotationOffset > 300) {
|
||||
receivingRotationOffset = receivingRotationOffsetGoal
|
||||
}
|
||||
|
||||
tracker.dataTick()
|
||||
// Update time variable
|
||||
timeAtLastReceivedRotationOffset = System.currentTimeMillis()
|
||||
}
|
||||
} else {
|
||||
// Trackers data (1-8)
|
||||
val trackerId = trackerStringValue.toInt()
|
||||
var tracker = oscTrackersDevice!!.trackers[trackerId]
|
||||
|
||||
if (tracker == null) {
|
||||
tracker = Tracker(
|
||||
device = oscTrackersDevice,
|
||||
id = VRServer.getNextLocalTrackerId(),
|
||||
name = "OSC Tracker #$trackerId",
|
||||
displayName = "OSC Tracker #$trackerId",
|
||||
trackerNum = trackerId,
|
||||
trackerPosition = null,
|
||||
hasRotation = true,
|
||||
hasPosition = true,
|
||||
userEditable = true,
|
||||
isComputed = true,
|
||||
needsReset = true,
|
||||
usesTimeout = true,
|
||||
)
|
||||
oscTrackersDevice!!.trackers[trackerId] = tracker
|
||||
server.registerTracker(tracker)
|
||||
}
|
||||
|
||||
// Sets the tracker status to OK
|
||||
tracker.status = TrackerStatus.OK
|
||||
|
||||
if (dataType == "position") {
|
||||
// Update tracker position
|
||||
tracker.position = receivingRotationOffset.sandwich(
|
||||
Vector3(
|
||||
event.message.arguments[0] as Float,
|
||||
event.message.arguments[1] as Float,
|
||||
-(event.message.arguments[2] as Float),
|
||||
) - receivingPositionOffset,
|
||||
) + postReceivingPositionOffset
|
||||
} else {
|
||||
// Update tracker rotation
|
||||
val (w, x, y, z) = EulerAngles(
|
||||
EulerOrder.YXZ,
|
||||
event.message.arguments[0] as Float * FastMath.DEG_TO_RAD,
|
||||
event.message.arguments[1] as Float * FastMath.DEG_TO_RAD,
|
||||
event.message.arguments[2] as Float * FastMath.DEG_TO_RAD,
|
||||
).toQuaternion()
|
||||
val rot = Quaternion(w, -x, -y, z)
|
||||
tracker.setRotation(receivingRotationOffset * rot * postReceivingOffset)
|
||||
}
|
||||
|
||||
tracker.dataTick()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun update() {
|
||||
// Update current time
|
||||
val currentTime = System.currentTimeMillis().toFloat()
|
||||
|
||||
// Gets timer from vrServer
|
||||
if (fpsTimer == null) {
|
||||
fpsTimer = VRServer.instance.fpsTimer
|
||||
@@ -319,6 +409,9 @@ class VRCOSCHandler(
|
||||
receivingRotationOffset = receivingRotationOffset.interpR(receivingRotationOffsetGoal, OFFSET_SLERP_FACTOR * (fpsTimer?.timePerFrame ?: 1f))
|
||||
}
|
||||
|
||||
// Update current time
|
||||
val currentTime = System.currentTimeMillis().toFloat()
|
||||
|
||||
// Send OSC data
|
||||
if (oscSender != null && oscSender!!.isConnected) {
|
||||
// Create new bundle
|
||||
@@ -382,7 +475,8 @@ class VRCOSCHandler(
|
||||
}
|
||||
|
||||
try {
|
||||
oscSender!!.send(bundle)
|
||||
oscSender?.send(bundle)
|
||||
oscQuerySender?.send(bundle)
|
||||
} catch (e: IOException) {
|
||||
// Avoid spamming AsynchronousCloseException too many
|
||||
// times per second
|
||||
@@ -400,9 +494,9 @@ class VRCOSCHandler(
|
||||
}
|
||||
|
||||
private fun getVRCOSCTrackersId(trackerPosition: TrackerPosition?): Int {
|
||||
// The order doesn't matter and changing it
|
||||
// won't break anything except make debugging harder
|
||||
// between different versions. They just need to range from 1-8
|
||||
// Needs to range from 1-8.
|
||||
// Don't change as third party applications may rely
|
||||
// on this for mapping trackers to body parts.
|
||||
return when (trackerPosition) {
|
||||
TrackerPosition.HIP -> 1
|
||||
TrackerPosition.LEFT_FOOT -> 2
|
||||
@@ -435,7 +529,8 @@ class VRCOSCHandler(
|
||||
oscArgs,
|
||||
)
|
||||
try {
|
||||
oscSender!!.send(oscMessage)
|
||||
oscSender?.send(oscMessage)
|
||||
oscQuerySender?.send(oscMessage)
|
||||
} catch (e: IOException) {
|
||||
LogManager
|
||||
.warning("[VRCOSCHandler] Error sending OSC message to VRChat: $e")
|
||||
@@ -448,11 +543,11 @@ class VRCOSCHandler(
|
||||
|
||||
override fun getOscSender(): OSCPortOut = oscSender!!
|
||||
|
||||
override fun getPortOut(): Int = lastPortOut
|
||||
override fun getPortOut(): Int = oscPortOut
|
||||
|
||||
override fun getAddress(): InetAddress = lastAddress!!
|
||||
override fun getAddress(): InetAddress = oscIp!!
|
||||
|
||||
override fun getOscReceiver(): OSCPortIn = oscReceiver!!
|
||||
|
||||
override fun getPortIn(): Int = lastPortIn
|
||||
override fun getPortIn(): Int = oscPortIn
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
package dev.slimevr.osc
|
||||
|
||||
import OSCQueryNode
|
||||
import OSCQueryServer
|
||||
import ServiceInfo
|
||||
import dev.slimevr.protocol.rpc.setup.RPCUtil
|
||||
import io.eiren.util.logging.LogManager
|
||||
import randomFreePort
|
||||
import java.io.IOException
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
private const val serviceStartsWith = "VRChat-Client"
|
||||
private const val queryPath = "/tracking/vrsystem"
|
||||
|
||||
/**
|
||||
* Handler for OSCQuery for VRChat using our library
|
||||
* https://github.com/SlimeVR/oscquery-kt
|
||||
*/
|
||||
class VRCOSCQueryHandler(
|
||||
private val vrcOscHandler: VRCOSCHandler,
|
||||
) {
|
||||
private val oscQueryServer: OSCQueryServer
|
||||
|
||||
init {
|
||||
// Request data
|
||||
val localIp = RPCUtil.getLocalIp()
|
||||
val httpPort = randomFreePort()
|
||||
oscQueryServer = OSCQueryServer(
|
||||
"SlimeVR-Server-$httpPort",
|
||||
OscTransport.UDP,
|
||||
localIp,
|
||||
vrcOscHandler.portIn.toUShort(),
|
||||
httpPort,
|
||||
)
|
||||
oscQueryServer.rootNode.addNode(OSCQueryNode(queryPath))
|
||||
oscQueryServer.init()
|
||||
LogManager.info("[VRCOSCQueryHandler] SlimeVR OSCQueryServer started at http://$localIp:$httpPort")
|
||||
|
||||
try {
|
||||
// Add service listener
|
||||
LogManager.info("[VRCOSCQueryHandler] Listening for VRChat OSCQuery")
|
||||
oscQueryServer.service.addServiceListener(
|
||||
"_osc._udp.local.",
|
||||
onServiceAdded = ::serviceAdded,
|
||||
)
|
||||
} catch (e: IOException) {
|
||||
LogManager.warning("[VRCOSCQueryHandler] " + e.message)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the OSC service's port
|
||||
*/
|
||||
fun updateOSCQuery(port: UShort) {
|
||||
if (oscQueryServer.oscPort != port) {
|
||||
thread(start = true) {
|
||||
oscQueryServer.updateOscService(port)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a service is added
|
||||
*/
|
||||
private fun serviceAdded(info: ServiceInfo) {
|
||||
// Check the service name
|
||||
if (!info.name.startsWith(serviceStartsWith)) return
|
||||
|
||||
// Get url from ServiceInfo
|
||||
val ip = info.inetAddresses[0].hostAddress
|
||||
val port = info.port
|
||||
|
||||
// create a new OSCHandler for this service
|
||||
vrcOscHandler.addOSCQuerySender(port, ip)
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the OSCQueryServer and the associated OSC sender.
|
||||
*/
|
||||
fun close() {
|
||||
vrcOscHandler.closeOscQuerySender(false)
|
||||
thread(start = true) {
|
||||
oscQueryServer.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,7 @@ allprojects {
|
||||
// Use jcenter for resolving dependencies.
|
||||
// You can declare any Maven/Ivy/file repository here.
|
||||
mavenCentral()
|
||||
maven(url = "https://jitpack.io")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -122,10 +122,10 @@ fun main(args: Array<String>) {
|
||||
::provideSteamVRBridge,
|
||||
::provideFeederBridge,
|
||||
{ _ -> DesktopSerialHandler() },
|
||||
configDir,
|
||||
configPath = configDir,
|
||||
)
|
||||
vrServer.start()
|
||||
|
||||
|
||||
// Start service for USB HID trackers
|
||||
TrackersHID(
|
||||
"Sensors HID service",
|
||||
|
||||
Reference in New Issue
Block a user