Skip to content

Commit cd7a585

Browse files
committed
Added function to the server to install it's own drivers.
1 parent 9353504 commit cd7a585

12 files changed

Lines changed: 320 additions & 207 deletions

File tree

server/core/src/main/java/dev/slimevr/protocol/rpc/RPCHandler.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import dev.slimevr.protocol.datafeed.createTrackerId
1010
import dev.slimevr.protocol.rpc.autobone.RPCAutoBoneHandler
1111
import dev.slimevr.protocol.rpc.firmware.RPCFirmwareUpdateHandler
1212
import dev.slimevr.protocol.rpc.games.vrchat.RPCVRChatHandler
13+
import dev.slimevr.protocol.rpc.installinfo.RPCInstallInfoHandler
1314
import dev.slimevr.protocol.rpc.reset.RPCResetHandler
1415
import dev.slimevr.protocol.rpc.serial.RPCProvisioningHandler
1516
import dev.slimevr.protocol.rpc.serial.RPCSerialHandler
@@ -52,6 +53,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
5253
RPCVRChatHandler(this, api)
5354
RPCTrackingChecklistHandler(this, api)
5455
RPCUserHeightCalibration(this, api)
56+
RPCInstallInfoHandler(this, api)
5557

5658
registerPacketListener(
5759
RpcMessage.AssignTrackerRequest,
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package dev.slimevr.protocol.rpc.installinfo
2+
3+
import com.google.flatbuffers.FlatBufferBuilder
4+
import dev.slimevr.protocol.GenericConnection
5+
import dev.slimevr.protocol.ProtocolAPI
6+
import dev.slimevr.protocol.rpc.RPCHandler
7+
import io.eiren.util.logging.LogManager
8+
import solarxr_protocol.rpc.InstalledInfoResponse.createInstalledInfoResponse
9+
import solarxr_protocol.rpc.RpcMessage
10+
import solarxr_protocol.rpc.RpcMessageHeader
11+
import java.io.IOException
12+
13+
class RPCInstallInfoHandler(var rpcHandler: RPCHandler, var api: ProtocolAPI){
14+
init {
15+
rpcHandler.registerPacketListener(RpcMessage.InstalledInfoRequest, ::onInstalledInfoRequest)
16+
}
17+
18+
fun onInstalledInfoRequest(conn: GenericConnection, messageHeader: RpcMessageHeader?) {
19+
val udevResponse = executeShellCommand("udevadm", "cat")
20+
var response = false
21+
if (udevResponse.contains("slime")) {
22+
response = true
23+
}
24+
else {
25+
response = false
26+
}
27+
28+
val fbb = FlatBufferBuilder(1024)
29+
val outbound = this.rpcHandler.createRPCMessage(
30+
fbb,
31+
RpcMessage.InstalledInfoResponse,
32+
createInstalledInfoResponse(fbb, response),
33+
)
34+
fbb.finish(outbound)
35+
conn.send(fbb.dataBuffer())
36+
}
37+
38+
39+
40+
private fun executeShellCommand(vararg command: String): String = try {
41+
val process = ProcessBuilder(*command)
42+
.redirectErrorStream(true)
43+
.start()
44+
process.inputStream.bufferedReader().readText().also {
45+
process.waitFor()
46+
}
47+
} catch (e: IOException) {
48+
LogManager.warning("Error executing shell command: ${e.message}")
49+
"Error executing shell command: ${e.message}"
50+
}
51+
}

server/desktop/build.gradle.kts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
1010

1111
plugins {
1212
kotlin("jvm")
13+
kotlin("plugin.serialization") version "2.3.10"
1314
application
1415
id("com.gradleup.shadow")
1516
id("com.github.gmazzo.buildconfig")
@@ -67,6 +68,10 @@ dependencies {
6768
exclude(group = "com.fazecast", module = "android")
6869
}
6970
implementation("org.hid4java:hid4java:0.8.0")
71+
implementation("io.ktor:ktor-client-core:3.0.3")
72+
implementation("io.ktor:ktor-client-cio:3.0.3")
73+
implementation("io.ktor:ktor-client-content-negotiation:3.0.3")
74+
implementation("io.ktor:ktor-serialization-kotlinx-json:3.0.3")
7075
}
7176

7277
tasks.shadowJar {

server/desktop/src/main/java/dev/slimevr/desktop/Main.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import dev.slimevr.bridge.Bridge
99
import dev.slimevr.config.ConfigManager
1010
import dev.slimevr.desktop.firmware.DesktopSerialFlashingHandler
1111
import dev.slimevr.desktop.games.vrchat.DesktopVRCConfigHandler
12+
import dev.slimevr.desktop.install.drivers.InstallDrivers
1213
import dev.slimevr.desktop.platform.SteamVRBridge
1314
import dev.slimevr.desktop.platform.linux.UnixSocketBridge
1415
import dev.slimevr.desktop.platform.linux.UnixSocketRpcBridge
@@ -99,6 +100,9 @@ fun main(args: Array<String>) {
99100
return
100101
}
101102

103+
val installDrivers = InstallDrivers()
104+
installDrivers.runUpdater()
105+
102106
val configDir = resolveConfig()
103107
LogManager.info("Using config dir: $configDir")
104108

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package dev.slimevr.desktop.install.drivers
2+
3+
class InstallDrivers {
4+
5+
val os = System.getProperty("os.name").lowercase()
6+
7+
fun runUpdater() {
8+
9+
if (os.contains("linux")) {
10+
val linuxUpdater = Linux()
11+
val linuxFlavour = executeShellCommand("uname", "-n")
12+
linuxUpdater.updateLinux()
13+
} else if (os.contains("windows")) {
14+
println("Running windows updater")
15+
val windowsUpdater = Windows()
16+
windowsUpdater.updateWindows()
17+
} else if (os.contains("darwin")) {
18+
println("I dunno")
19+
} else {
20+
println("guess I'll die")
21+
}
22+
return
23+
}
24+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package dev.slimevr.desktop.install.drivers
2+
3+
import io.eiren.util.logging.LogManager
4+
import io.ktor.client.HttpClient
5+
import io.ktor.client.call.body
6+
import io.ktor.client.engine.cio.CIO
7+
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
8+
import io.ktor.client.plugins.onDownload
9+
import io.ktor.client.plugins.timeout
10+
import io.ktor.client.request.get
11+
import io.ktor.client.request.prepareGet
12+
import io.ktor.client.statement.bodyAsChannel
13+
import io.ktor.http.contentLength
14+
import io.ktor.serialization.kotlinx.json.json
15+
import io.ktor.utils.io.jvm.javaio.copyTo
16+
import kotlinx.coroutines.Dispatchers
17+
import kotlinx.coroutines.launch
18+
import kotlinx.coroutines.runBlocking
19+
import kotlinx.coroutines.sync.Semaphore
20+
import kotlinx.coroutines.sync.withPermit
21+
import kotlinx.coroutines.withContext
22+
import kotlinx.serialization.Serializable
23+
import kotlinx.serialization.json.Json
24+
import kotlinx.serialization.json.JsonArray
25+
import java.io.File
26+
import java.io.FileOutputStream
27+
import java.io.IOException
28+
import java.lang.Exception
29+
import java.math.BigInteger
30+
import java.nio.file.Files
31+
import java.nio.file.Paths
32+
import java.security.MessageDigest
33+
import java.util.zip.ZipEntry
34+
import java.util.zip.ZipFile
35+
import kotlin.time.Duration.Companion.seconds
36+
37+
fun executeShellCommand(vararg command: String): String = try {
38+
val process = ProcessBuilder(*command)
39+
.redirectErrorStream(true)
40+
.start()
41+
process.inputStream.bufferedReader().readText().also {
42+
process.waitFor()
43+
}
44+
} catch (e: IOException) {
45+
LogManager.warning("Error executing shell command: ${e.message}")
46+
"Error executing shell command: ${e.message}"
47+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package dev.slimevr.desktop.install.drivers
2+
3+
import io.eiren.util.logging.LogManager
4+
import java.nio.file.Paths
5+
import kotlin.io.path.Path
6+
import kotlin.io.path.exists
7+
8+
class Linux {
9+
10+
val path = Paths.get("").toAbsolutePath().toString()
11+
12+
fun updateLinux() {
13+
updateLinuxSteamVRDriver()
14+
feeder()
15+
updateUdev()
16+
}
17+
18+
fun updateLinuxSteamVRDriver() {
19+
val vrPathRegContents = executeShellCommand("${System.getProperty("user.home")}/.steam/steam/steamapps/common/SteamVR/bin/vrpathreg.sh")
20+
val isDriverRegistered = vrPathRegContents.contains("slimevr")
21+
if (!isDriverRegistered) {
22+
executeShellCommand(
23+
"${System.getProperty("user.home")}/.steam/steam/steamapps/common/SteamVR/bin/vrpathreg.sh",
24+
"adddriver",
25+
"$path/$LINUXSTEAMVRDRIVERDIRECTORY/slimevr",
26+
)
27+
} else {
28+
LogManager.info("steamVR driver is already registered. Skipping...")
29+
}
30+
}
31+
32+
fun feeder() {
33+
executeShellCommand("chmod", "+x", "$path/$LINUXFEEDERDIRECTORY/SlimeVR-Feeder-App")
34+
executeShellCommand("$path/$LINUXFEEDERDIRECTORY/SlimeVR-Feeder-App", "--install")
35+
LogManager.info("feeder is registered.")
36+
}
37+
38+
fun updateUdev() {
39+
val file = Path("/etc/udev/rules.d/69-slimevr-devices.rules")
40+
if (file.exists()) {
41+
LogManager.info("Udev rules already exist")
42+
return
43+
}
44+
val res = executeShellCommand("pkexec", "cp", "$path/69-slimevr-devices.rules", "/etc/udev/rules.d/69-slimevr-devices.rules")
45+
if (res.contains("Error")) {
46+
LogManager.warning("Error installing udev rules")
47+
} else {
48+
LogManager.info("Successfully installed udev rules")
49+
}
50+
}
51+
52+
companion object {
53+
// Linux URLs
54+
private const val LINUXSTEAMVRDRIVERDIRECTORY = "slimevr-openvr-driver-x64-linux"
55+
private const val LINUXFEEDERDIRECTORY = "SlimeVR-Feeder-App-Linux"
56+
57+
}
58+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package dev.slimevr.desktop.install.drivers
2+
3+
import io.eiren.util.logging.LogManager
4+
5+
6+
class Updater {
7+
8+
val os = System.getProperty("os.name").lowercase()
9+
10+
fun runUpdater() {
11+
if (os.contains("linux")) {
12+
LogManager.info("Running linux updater")
13+
val linuxUpdater = Linux()
14+
val linuxFlavour = executeShellCommand("uname", "-n")
15+
linuxUpdater.updateLinux()
16+
} else if (os.contains("windows")) {
17+
LogManager.info("Running windows updater")
18+
val windowsUpdater = Windows()
19+
windowsUpdater.updateWindows()
20+
} else if (os.contains("darwin")) {
21+
LogManager.info("I dunno")
22+
} else {
23+
LogManager.info("guess I'll die")
24+
}
25+
}
26+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package dev.slimevr.desktop.install.drivers
2+
3+
import io.eiren.util.logging.LogManager
4+
import java.nio.file.Paths
5+
6+
class Windows {
7+
8+
val path = Paths.get("").toAbsolutePath().toString()
9+
10+
fun updateWindows() {
11+
// First check if everything is already installed. Install it if it isn't
12+
usbDrivers()
13+
steamVRDriver()
14+
feeder()
15+
}
16+
17+
fun usbDrivers() {
18+
19+
val installedDriversList = executeShellCommand("powershell.exe", "pnputil /enum-drivers")
20+
val ch341ser = installedDriversList.contains("ch341ser.inf")
21+
val ch343ser = installedDriversList.contains("ch343ser.inf")
22+
val silabser = installedDriversList.contains("silabser.inf")
23+
24+
if (ch341ser && ch343ser && silabser) {
25+
LogManager.info("drivers already installed!")
26+
return
27+
}
28+
LogManager.info("Cannot find one of the drivers, installing drivers")
29+
val driverinstallOutput = executeShellCommand("$path\\installusbdrivers.bat")
30+
LogManager.info(driverinstallOutput)
31+
}
32+
33+
34+
fun feeder() {
35+
executeShellCommand("${path}\\${WINDOWSFEEDERDIRECTORY}\\SlimeVR-Feeder-App.exe", "--install")
36+
}
37+
38+
fun steamVRDriver() {
39+
val steamVRLocation = executeShellCommand("powershell.exe", "-Command", "(Get-ItemProperty \'HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Steam App 250820\').InstallLocation").trim()
40+
if (!steamVRLocation.contains("SteamVR")) {
41+
LogManager.warning("SteamVR not installed, cannot install SlimeVR Steam driver.")
42+
return
43+
}
44+
val vrPathRegContents = executeShellCommand("${steamVRLocation}\\bin\\win64\\vrpathreg.exe", "finddriver", "slimevr")
45+
val isDriverRegistered = vrPathRegContents.contains("WINDOWSSTEAMVRDRIVERDIRECTORY")
46+
if (isDriverRegistered) {
47+
LogManager.info("steamVR driver is already registered. Skipping...")
48+
return
49+
}
50+
executeShellCommand(
51+
"${steamVRLocation}\\bin\\win64\\vrpathreg.exe",
52+
"adddriver",
53+
"${
54+
Paths.get(
55+
"",
56+
).toAbsolutePath()
57+
}\\${WINDOWSSTEAMVRDRIVERDIRECTORY}\\slimevr",
58+
)
59+
}
60+
61+
companion object {
62+
private const val WINDOWSSTEAMVRDRIVERDIRECTORY = "slimevr-openvr-driver-win64"
63+
private const val WINDOWSFEEDERDIRECTORY = "SlimeVR-Feeder-App-win64"
64+
65+
}
66+
}

server/desktop/src/main/java/dev/slimevr/desktop/platform/ProtobufBridge.kt

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import dev.slimevr.desktop.platform.ProtobufMessages.*
77
import dev.slimevr.tracking.trackers.Tracker
88
import dev.slimevr.tracking.trackers.TrackerStatus
99
import dev.slimevr.tracking.trackers.TrackerStatus.Companion.getById
10-
import dev.slimevr.tracking.trackers.TrackerUtils
1110
import dev.slimevr.util.ann.VRServerThread
1211
import io.eiren.util.ann.Synchronize
1312
import io.eiren.util.ann.ThreadSafe
@@ -219,11 +218,6 @@ abstract class ProtobufBridge(@JvmField protected val bridgeName: String) : ISte
219218

220219
"mounting_reset" -> instance.resetTrackersMounting(resetSourceName)
221220

222-
"feet_mounting_reset" -> instance.resetTrackersMounting(
223-
resetSourceName,
224-
TrackerUtils.feetsBodyParts,
225-
)
226-
227221
"pause_tracking" ->
228222
instance
229223
.togglePauseTracking(resetSourceName)

0 commit comments

Comments
 (0)