) {
const classes = useMemo(() => {
const vriantsMap = {
@@ -32,7 +34,9 @@ export function CheckBox({
toggle: {
checkbox: classNames('hidden'),
toggle: classNames('w-10 h-4 rounded-full relative transition-colors'),
- pin: classNames('h-2 w-2 bg-background-10 rounded-full absolute m-1'),
+ pin: classNames(
+ 'h-2 w-2 bg-background-10 rounded-full absolute m-1 transition-opacity'
+ ),
},
};
return vriantsMap[variant];
@@ -60,7 +64,8 @@ export function CheckBox({
'w-full py-3 flex gap-2 items-center text-standard-bold',
{
'px-3': outlined,
- 'cursor-pointer': !disabled,
+ 'cursor-pointer': !disabled || !loading,
+ 'cursor-default': disabled || loading,
}
)}
>
@@ -71,23 +76,26 @@ export function CheckBox({
name={name}
className={classes.checkbox}
type="checkbox"
- disabled={disabled}
+ disabled={disabled || loading}
{...props}
/>
{variant === 'toggle' && (
diff --git a/gui/src/components/settings/pages/GeneralSettings.tsx b/gui/src/components/settings/pages/GeneralSettings.tsx
index db8416f0ed..d384670e79 100644
--- a/gui/src/components/settings/pages/GeneralSettings.tsx
+++ b/gui/src/components/settings/pages/GeneralSettings.tsx
@@ -31,6 +31,7 @@ import {
SettingsPagePaneLayout,
} from '@/components/settings/SettingsPageLayout';
import { HandsWarningModal } from '@/components/settings/HandsWarningModal';
+import { MagnetometerToggleSetting } from './MagnetometerToggleSetting';
import { DriftCompensationModal } from '@/components/settings/DriftCompensationModal';
interface SettingsForm {
@@ -808,6 +809,10 @@ export function GeneralSettings() {
'settings-general-tracker_mechanics-save_mounting_reset-enabled-label'
)}
/>
+
>
(null);
+ // used to disable the tracker specific toggle if false
+ const [globalToggle, setGlobalToggle] = useState(false);
+ const [waitingMag, setWaitingMag] = useState(false);
+ const { control, watch, handleSubmit, reset } =
+ useForm({
+ defaultValues: { magToggle: settingType === 'tracker' },
+ });
+
+ const onSubmit = useCallback(
+ (values: MagnetometerToggleForm) => {
+ if (originalValue.current === values.magToggle) return;
+ setWaitingMag(true);
+ const req = new ChangeMagToggleRequestT();
+ if (trackerNum !== undefined) {
+ const id = new TrackerIdT(
+ deviceId ? new DeviceIdT(deviceId) : undefined,
+ trackerNum
+ );
+ req.trackerId = id;
+ }
+
+ req.enable = values.magToggle;
+ sendRPCPacket(RpcMessage.ChangeMagToggleRequest, req);
+ },
+ [trackerNum, deviceId]
+ );
+
+ useEffect(() => {
+ const subscription = watch(() => handleSubmit(onSubmit)());
+ return () => subscription.unsubscribe();
+ }, []);
+
+ useEffect(() => {
+ const req = new MagToggleRequestT();
+ if (trackerNum !== undefined) {
+ const id = new TrackerIdT(
+ deviceId ? new DeviceIdT(deviceId) : undefined,
+ trackerNum
+ );
+ req.trackerId = id;
+ sendRPCPacket(RpcMessage.MagToggleRequest, new MagToggleRequestT());
+ }
+ sendRPCPacket(RpcMessage.MagToggleRequest, req);
+ }, [trackerNum, deviceId]);
+
+ useRPCPacket(RpcMessage.MagToggleResponse, (mag: MagToggleResponseT) => {
+ if (trackerNum !== undefined && mag.trackerId?.trackerNum === undefined) {
+ setGlobalToggle(mag.enable);
+ }
+ if (
+ mag.trackerId?.trackerNum !== trackerNum ||
+ mag.trackerId?.deviceId?.id !== deviceId
+ ) {
+ return;
+ }
+ originalValue.current = mag.enable;
+ setWaitingMag(false);
+ reset({ magToggle: mag.enable });
+ });
+
+ return settingType === 'general' ? (
+ <>
+
+
+ {l10n.getString(
+ 'settings-general-tracker_mechanics-use_mag_on_all_trackers'
+ )}
+
+ }}
+ >
+
+
+
+
+ >
+ ) : (
+
+
+ {l10n.getString('tracker-settings-use_mag')}
+
+
,
+ magSetting: (
+
+ ),
+ }}
+ >
+
+
+
+
+
+
+ );
+}
diff --git a/gui/src/components/tracker/TrackerSettings.tsx b/gui/src/components/tracker/TrackerSettings.tsx
index 9b18455ab8..85151a715d 100644
--- a/gui/src/components/tracker/TrackerSettings.tsx
+++ b/gui/src/components/tracker/TrackerSettings.tsx
@@ -33,6 +33,7 @@ import { SingleTrackerBodyAssignmentMenu } from './SingleTrackerBodyAssignmentMe
import { TrackerCard } from './TrackerCard';
import { Quaternion } from 'three';
import { useAppContext } from '@/hooks/app';
+import { MagnetometerToggleSetting } from '@/components/settings/pages/MagnetometerToggleSetting';
const rotationsLabels: [Quaternion, string][] = [
[rotationToQuatMap.BACK, 'tracker-rotation-back'],
@@ -276,6 +277,18 @@ export function TrackerSettingsPage() {
{tracker?.device?.hardwareInfo?.boardType || '--'}
+
+
+ {l10n.getString('tracker-infos-magnetometer')}
+
+
+ {tracker?.tracker.info?.magnetometer === undefined
+ ? '--'
+ : l10n.getString('tracker-infos-magnetometer-status', {
+ status: tracker.tracker.info.magnetometer,
+ })}
+
+
{l10n.getString('tracker-infos-network_version')}
@@ -403,6 +416,13 @@ export function TrackerSettingsPage() {
)}
+ {tracker?.tracker.info?.isImu && (
+
+ )}
{l10n.getString('tracker-settings-name_section')}
diff --git a/server/build.gradle.kts b/server/build.gradle.kts
index e20ea0c164..645f9c3831 100644
--- a/server/build.gradle.kts
+++ b/server/build.gradle.kts
@@ -37,7 +37,7 @@ configure {
"java.util.*,kotlin.math.*,dev.slimevr.autobone.errors.*" +
",io.github.axisangles.ktmath.*,kotlinx.atomicfu.*" +
",dev.slimevr.tracking.trackers.*,dev.slimevr.desktop.platform.ProtobufMessages.*" +
- ",com.illposed.osc.*,android.app.*",
+ ",solarxr_protocol.rpc.*,kotlinx.coroutines.*,com.illposed.osc.*,android.app.*",
"ij_kotlin_allow_trailing_comma" to true,
)
val ktlintVersion = "1.2.1"
diff --git a/server/core/build.gradle.kts b/server/core/build.gradle.kts
index 0c15a3b551..cc400bc2ef 100644
--- a/server/core/build.gradle.kts
+++ b/server/core/build.gradle.kts
@@ -76,6 +76,7 @@ dependencies {
implementation("org.java-websocket:Java-WebSocket:1.+")
implementation("com.melloware:jintellitype:1.+")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")
+ implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1")
// Jitpack
implementation("com.github.SlimeVR:oscquery-kt:566a0cba58")
diff --git a/server/core/src/main/java/dev/slimevr/config/ServerConfig.java b/server/core/src/main/java/dev/slimevr/config/ServerConfig.java
deleted file mode 100644
index 2daf2a749c..0000000000
--- a/server/core/src/main/java/dev/slimevr/config/ServerConfig.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package dev.slimevr.config;
-
-public class ServerConfig {
-
- private int trackerPort = 6969;
-
- public int getTrackerPort() {
- return trackerPort;
- }
-}
diff --git a/server/core/src/main/java/dev/slimevr/config/ServerConfig.kt b/server/core/src/main/java/dev/slimevr/config/ServerConfig.kt
new file mode 100644
index 0000000000..717d334fe8
--- /dev/null
+++ b/server/core/src/main/java/dev/slimevr/config/ServerConfig.kt
@@ -0,0 +1,49 @@
+package dev.slimevr.config
+
+import dev.slimevr.VRServer
+import kotlinx.coroutines.async
+import kotlinx.coroutines.awaitAll
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.sync.Mutex
+
+class ServerConfig {
+ val trackerPort: Int = 6969
+
+ var useMagnetometerOnAllTrackers: Boolean = false
+ private set
+
+ private val magMutex = Mutex()
+ suspend fun defineMagOnAllTrackers(state: Boolean) = coroutineScope {
+ magMutex.lock()
+ try {
+ if (useMagnetometerOnAllTrackers == state) return@coroutineScope
+
+ VRServer.instance.deviceManager.devices.filter { it.magSupport }.map {
+ async {
+ if (!state) {
+ it.setMag(false)
+ return@async
+ }
+
+ val every = it.trackers.all { (_, t) -> t.config.shouldHaveMagEnabled == true }
+ if (every) {
+ it.setMag(true)
+ return@async
+ }
+
+ it.trackers.filterValues { it.config.shouldHaveMagEnabled == true }
+ .map { (_, t) ->
+ async {
+ // FIXME: Tracker gets restarted after each setMag, what will happen for devices with 3 trackers?
+ it.setMag(true, t.trackerNum)
+ }
+ }.awaitAll()
+ }
+ }.awaitAll()
+
+ useMagnetometerOnAllTrackers = state
+ } finally {
+ magMutex.unlock()
+ }
+ }
+}
diff --git a/server/core/src/main/java/dev/slimevr/config/TrackerConfig.kt b/server/core/src/main/java/dev/slimevr/config/TrackerConfig.kt
index 122abc5c1f..7770bec6be 100644
--- a/server/core/src/main/java/dev/slimevr/config/TrackerConfig.kt
+++ b/server/core/src/main/java/dev/slimevr/config/TrackerConfig.kt
@@ -2,6 +2,7 @@ package dev.slimevr.config
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.node.JsonNodeFactory
+import dev.slimevr.VRServer
import dev.slimevr.tracking.trackers.Tracker
import io.github.axisangles.ktmath.ObjectQuaternion
@@ -16,12 +17,18 @@ class TrackerConfig {
var mountingResetOrientation: ObjectQuaternion? = null
var allowDriftCompensation: Boolean? = null
+ /**
+ * Only checked if [ServerConfig.useMagnetometerOnAllTrackers] enabled
+ */
+ var shouldHaveMagEnabled: Boolean? = null
+
constructor()
constructor(tracker: Tracker) {
this.designation = if (tracker.trackerPosition != null) tracker.trackerPosition!!.designation else null
this.customName = tracker.customName
allowDriftCompensation = tracker.isImu()
+ shouldHaveMagEnabled = tracker.isImu()
}
companion object {
@@ -37,3 +44,6 @@ class TrackerConfig {
}
}
}
+
+val Tracker.config: TrackerConfig
+ get() = VRServer.instance.configManager.vrConfig.getTracker(this)
diff --git a/server/core/src/main/java/dev/slimevr/protocol/datafeed/DataFeedBuilder.java b/server/core/src/main/java/dev/slimevr/protocol/datafeed/DataFeedBuilder.java
index bd60898624..3eb480af0d 100644
--- a/server/core/src/main/java/dev/slimevr/protocol/datafeed/DataFeedBuilder.java
+++ b/server/core/src/main/java/dev/slimevr/protocol/datafeed/DataFeedBuilder.java
@@ -139,6 +139,7 @@ public static int createTrackerInfos(
TrackerInfo.addMountingResetOrientation(fbb, createQuat(fbb, mountResetFix));
}
+ TrackerInfo.addMagnetometer(fbb, tracker.getMagStatus().getSolarType());
TrackerInfo.addIsHmd(fbb, tracker.isHmd());
return TrackerInfo.endTrackerInfo(fbb);
diff --git a/server/core/src/main/java/dev/slimevr/protocol/rpc/RPCHandler.kt b/server/core/src/main/java/dev/slimevr/protocol/rpc/RPCHandler.kt
index 02f3971aa0..d9995f647e 100644
--- a/server/core/src/main/java/dev/slimevr/protocol/rpc/RPCHandler.kt
+++ b/server/core/src/main/java/dev/slimevr/protocol/rpc/RPCHandler.kt
@@ -2,9 +2,11 @@ package dev.slimevr.protocol.rpc
import com.google.flatbuffers.FlatBufferBuilder
import dev.slimevr.autobone.errors.BodyProportionError
+import dev.slimevr.config.config
import dev.slimevr.protocol.GenericConnection
import dev.slimevr.protocol.ProtocolAPI
import dev.slimevr.protocol.ProtocolHandler
+import dev.slimevr.protocol.datafeed.DataFeedBuilder
import dev.slimevr.protocol.rpc.autobone.RPCAutoBoneHandler
import dev.slimevr.protocol.rpc.reset.RPCResetHandler
import dev.slimevr.protocol.rpc.serial.RPCProvisioningHandler
@@ -20,33 +22,14 @@ import dev.slimevr.tracking.trackers.TrackerPosition.Companion.getByBodyPart
import dev.slimevr.tracking.trackers.TrackerUtils.getTrackerForSkeleton
import io.eiren.util.logging.LogManager
import io.github.axisangles.ktmath.Quaternion
+import kotlinx.coroutines.*
import solarxr_protocol.MessageBundle
import solarxr_protocol.datatypes.TransactionId
-import solarxr_protocol.rpc.AssignTrackerRequest
-import solarxr_protocol.rpc.ChangeSkeletonConfigRequest
-import solarxr_protocol.rpc.ClearDriftCompensationRequest
-import solarxr_protocol.rpc.ClearMountingResetRequest
-import solarxr_protocol.rpc.HeightResponse
-import solarxr_protocol.rpc.LegTweaksTmpChange
-import solarxr_protocol.rpc.LegTweaksTmpClear
-import solarxr_protocol.rpc.OverlayDisplayModeChangeRequest
-import solarxr_protocol.rpc.OverlayDisplayModeResponse
-import solarxr_protocol.rpc.RecordBVHRequest
-import solarxr_protocol.rpc.RecordBVHStatus
-import solarxr_protocol.rpc.ResetRequest
-import solarxr_protocol.rpc.ResetType
-import solarxr_protocol.rpc.RpcMessage
-import solarxr_protocol.rpc.RpcMessageHeader
-import solarxr_protocol.rpc.ServerInfosResponse
-import solarxr_protocol.rpc.SetPauseTrackingRequest
-import solarxr_protocol.rpc.SkeletonConfigRequest
-import solarxr_protocol.rpc.SkeletonResetAllRequest
-import solarxr_protocol.rpc.StatusSystemRequest
-import solarxr_protocol.rpc.StatusSystemResponse
-import solarxr_protocol.rpc.StatusSystemResponseT
+import solarxr_protocol.rpc.*
class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler() {
private var currTransactionId: Long = 0
+ private val mainScope = CoroutineScope(SupervisorJob())
init {
RPCResetHandler(this, api)
@@ -197,6 +180,18 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler
+ this.onMagToggleRequest(conn, messageHeader)
+ }
+
+ registerPacketListener(
+ RpcMessage.ChangeMagToggleRequest,
+ ) { conn: GenericConnection, messageHeader: RpcMessageHeader ->
+ this.onChangeMagToggleRequest(conn, messageHeader)
+ }
}
private fun onServerInfosRequest(
@@ -478,7 +473,92 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler,
+ var ran: Boolean = false,
+ )
+
+ private val queues: MutableMap, Deque> = ConcurrentHashMap()
+ suspend fun setConfigFlag(device: UDPDevice, configTypeId: ConfigTypeId, state: Boolean, sensorId: Int = 255) {
+ if (device.timedOut) return
+ val triple = Triple(device.address, configTypeId, sensorId)
+ val queue = queues.computeIfAbsent(triple) { _ -> ConcurrentLinkedDeque() }
+
+ suspendCancellableCoroutine {
+ val waiter = ConfigStateWaiter(state, it)
+ queue.add(waiter)
+ it.invokeOnCancellation {
+ queue.remove(waiter)
+ }
+ }
+ }
+
+ private fun actualSetConfigFlag(device: UDPDevice, configTypeId: ConfigTypeId, state: Boolean, sensorId: Int) {
+ val packet = UDPPacket25SetConfigFlag(sensorId, configTypeId, state)
+ bb.limit(bb.capacity())
+ bb.rewind()
+ parser.write(bb, null, packet)
+ socket.send(DatagramPacket(rcvBuffer, bb.position(), device.address))
}
override fun run() {
@@ -243,6 +300,19 @@ class TrackersUDPServer(private val port: Int, name: String, private val tracker
parser.parse(bb, connection)
.filterNotNull()
.forEach { processPacket(received, it, connection) }
+
+ queues.forEach { (t, p) ->
+ val q = p.firstOrNull() ?: return@forEach
+ if (q.ran) return@forEach
+
+ val device = connectionsByAddress[t.first] ?: run {
+ p.removeFirst()
+ LogManager.info("[TrackerServer] Device ${t.first} not connected, so can't communicate with it")
+ return@forEach
+ }
+ actualSetConfigFlag(device, t.second, q.expectedState, t.third)
+ if (!device.timedOut) q.ran = true
+ }
} catch (ignored: SocketTimeoutException) {
} catch (e: Exception) {
LogManager.warning(
@@ -311,7 +381,7 @@ class TrackersUDPServer(private val port: Int, name: String, private val tracker
private fun processPacket(received: DatagramPacket, packet: UDPPacket, connection: UDPDevice?) {
val tracker: Tracker?
when (packet) {
- is UDPPacket0Heartbeat, is UDPPacket1Heartbeat -> {}
+ is UDPPacket0Heartbeat, is UDPPacket1Heartbeat, is UDPPacket25SetConfigFlag -> {}
is UDPPacket3Handshake -> setUpNewConnection(received, packet)
@@ -402,14 +472,14 @@ class TrackersUDPServer(private val port: Int, name: String, private val tracker
is UDPPacket15SensorInfo -> {
if (connection == null) return
- setUpSensor(connection, packet.sensorId, packet.sensorType, packet.sensorStatus)
+ setUpSensor(connection, packet.sensorId, packet.sensorType, packet.sensorStatus, packet.sensorConfig.magStatus)
// Send ack
bb.limit(bb.capacity())
bb.rewind()
parser.writeSensorInfoResponse(bb, connection, packet)
socket.send(DatagramPacket(rcvBuffer, bb.position(), connection.address))
LogManager.info(
- "[TrackerServer] Sensor info for ${connection.descriptiveName}/${packet.sensorId}: ${packet.sensorStatus}",
+ "[TrackerServer] Sensor info for ${connection.descriptiveName}/${packet.sensorId}: ${packet.sensorStatus}, mag ${packet.sensorConfig.magStatus}",
)
}
@@ -418,8 +488,7 @@ class TrackersUDPServer(private val port: Int, name: String, private val tracker
}
is UDPPacket20Temperature -> {
- tracker = connection?.getTracker(packet.sensorId)
- if (tracker == null) return
+ tracker = connection?.getTracker(packet.sensorId) ?: return
tracker.temperature = packet.temperature
}
@@ -455,7 +524,7 @@ class TrackersUDPServer(private val port: Int, name: String, private val tracker
}
LogManager.info(
- "[TrackerServer] User action from ${connection.descriptiveName } received. $name performed.",
+ "[TrackerServer] User action from ${connection.descriptiveName} received. $name performed.",
)
}
@@ -469,6 +538,22 @@ class TrackersUDPServer(private val port: Int, name: String, private val tracker
connection.firmwareFeatures = packet.firmwareFeatures
}
+ is UDPPacket24AckConfigChange -> {
+ if (connection == null) return
+ val queue = queues[Triple(connection.address, packet.configType, packet.sensorId)] ?: run {
+ LogManager.severe("[TrackerServer] Error, acknowledgment of config change that we don't have in our queue.")
+ return
+ }
+ val changed = queue.removeFirst()
+ changed.channel.resume(true)
+ val trackers = if (SensorSpecificPacket.isGlobal(packet.sensorId)) {
+ connection.trackers.values.toList()
+ } else {
+ listOf(connection.getTracker(packet.sensorId) ?: return)
+ }
+ LogManager.info("[TrackerServer] Acknowledged config change on ${connection.descriptiveName} (${trackers.map { it.trackerNum }.joinToString()}). Config changed on ${packet.configType}")
+ }
+
is UDPPacket200ProtocolChange -> {}
}
}
diff --git a/server/core/src/main/java/dev/slimevr/tracking/trackers/udp/UDPDevice.kt b/server/core/src/main/java/dev/slimevr/tracking/trackers/udp/UDPDevice.kt
index afc0a56d79..7407657685 100644
--- a/server/core/src/main/java/dev/slimevr/tracking/trackers/udp/UDPDevice.kt
+++ b/server/core/src/main/java/dev/slimevr/tracking/trackers/udp/UDPDevice.kt
@@ -1,6 +1,7 @@
package dev.slimevr.tracking.trackers.udp
import dev.slimevr.NetworkProtocol
+import dev.slimevr.VRServer
import dev.slimevr.tracking.trackers.Device
import dev.slimevr.tracking.trackers.Tracker
import java.net.InetAddress
@@ -13,7 +14,7 @@ class UDPDevice(
override val hardwareIdentifier: String,
override val boardType: BoardType = BoardType.UNKNOWN,
override val mcuType: MCUType = MCUType.UNKNOWN,
-) : Device() {
+) : Device(true) {
override val id: Int = nextLocalDeviceId.incrementAndGet()
@@ -53,6 +54,17 @@ class UDPDevice(
var timedOut = false
override val trackers = ConcurrentHashMap()
+ override suspend fun setMag(state: Boolean, sensorId: Int) {
+ if (sensorId == 255) {
+ VRServer.instance.trackersServer.setConfigFlag(this, ConfigTypeId(1u), state)
+ trackers.forEach { (_, t) -> t.setMagPrivate(state) }
+ } else {
+ require(trackers[sensorId] != null) { "There is no tracker $sensorId in device ${toString()}" }
+ VRServer.instance.trackersServer.setConfigFlag(this, ConfigTypeId(1u), state, sensorId)
+ trackers[sensorId]!!.setMagPrivate(state)
+ }
+ }
+
var firmwareFeatures = FirmwareFeatures()
fun isNextPacket(packetId: Long): Boolean {
diff --git a/server/core/src/main/java/dev/slimevr/tracking/trackers/udp/UDPPacket.kt b/server/core/src/main/java/dev/slimevr/tracking/trackers/udp/UDPPacket.kt
index 6691602acd..50fe7825b2 100644
--- a/server/core/src/main/java/dev/slimevr/tracking/trackers/udp/UDPPacket.kt
+++ b/server/core/src/main/java/dev/slimevr/tracking/trackers/udp/UDPPacket.kt
@@ -223,6 +223,7 @@ data class UDPPacket14Error(var errorNumber: Int = 0) :
data class UDPPacket15SensorInfo(
var sensorStatus: Int = 0,
var sensorType: IMUType = IMUType.UNKNOWN,
+ var sensorConfig: SensorConfig = SensorConfig(0u),
) : UDPPacket(15),
SensorSpecificPacket {
override var sensorId = 0
@@ -233,6 +234,9 @@ data class UDPPacket15SensorInfo(
sensorType =
IMUType.getById(buf.get().toUInt() and 0xFFu) ?: IMUType.UNKNOWN
}
+ if (buf.remaining() > 0) {
+ sensorConfig = SensorConfig(buf.getShort().toUShort())
+ }
}
companion object {
@@ -350,6 +354,30 @@ data class UDPPacket23RotationAndAcceleration(
}
}
+data class UDPPacket24AckConfigChange(
+ override var sensorId: Int = 0,
+ var configType: ConfigTypeId = ConfigTypeId(0u),
+) : UDPPacket(24),
+ SensorSpecificPacket {
+ override fun readData(buf: ByteBuffer) {
+ sensorId = buf.get().toInt() and 0xFF
+ configType = ConfigTypeId(buf.getShort().toUShort())
+ }
+}
+
+data class UDPPacket25SetConfigFlag(
+ override var sensorId: Int = 255,
+ var configType: ConfigTypeId,
+ var state: Boolean,
+) : UDPPacket(25),
+ SensorSpecificPacket {
+ override fun writeData(buf: ByteBuffer) {
+ buf.put(sensorId.toByte())
+ buf.putShort(configType.v.toShort())
+ buf.put(if (state) 1 else 0)
+ }
+}
+
data class UDPPacket200ProtocolChange(
var targetProtocol: Int = 0,
var targetProtocolVersion: Int = 0,
diff --git a/server/core/src/main/java/dev/slimevr/tracking/trackers/udp/UDPProtocolParser.kt b/server/core/src/main/java/dev/slimevr/tracking/trackers/udp/UDPProtocolParser.kt
index e0c7e3e0f7..9f359226c9 100644
--- a/server/core/src/main/java/dev/slimevr/tracking/trackers/udp/UDPProtocolParser.kt
+++ b/server/core/src/main/java/dev/slimevr/tracking/trackers/udp/UDPProtocolParser.kt
@@ -114,6 +114,7 @@ class UDPProtocolParser {
PACKET_TEMPERATURE -> UDPPacket20Temperature()
PACKET_USER_ACTION -> UDPPacket21UserAction()
PACKET_FEATURE_FLAGS -> UDPPacket22FeatureFlags()
+ PACKET_ACK_CONFIG_CHANGE -> UDPPacket24AckConfigChange()
PACKET_PROTOCOL_CHANGE -> UDPPacket200ProtocolChange()
else -> null
}
@@ -147,6 +148,8 @@ class UDPProtocolParser {
const val PACKET_USER_ACTION = 21
const val PACKET_FEATURE_FLAGS = 22
const val PACKET_ROTATION_AND_ACCELERATION = 23
+ const val PACKET_ACK_CONFIG_CHANGE = 24
+ const val PACKET_SET_CONFIG_FLAG = 25
const val PACKET_BUNDLE = 100
const val PACKET_BUNDLE_COMPACT = 101
const val PACKET_PROTOCOL_CHANGE = 200
diff --git a/solarxr-protocol b/solarxr-protocol
index 60f3146f91..73fca6300d 160000
--- a/solarxr-protocol
+++ b/solarxr-protocol
@@ -1 +1 @@
-Subproject commit 60f3146f914e2a9e35d759a8146be213e715d6d2
+Subproject commit 73fca6300d70ee5e73258ce98a7dbae4d63b9cc8