Migrate core/dev.slimevr.protocol to Kotlin (#1688)

Co-authored-by: Butterscotch! <bscotchvanilla@gmail.com>
This commit is contained in:
Maya
2026-01-14 17:50:04 +03:00
committed by GitHub
parent ea242960b3
commit 8f57ef2de4
34 changed files with 1777 additions and 1987 deletions

View File

@@ -1,49 +0,0 @@
package dev.slimevr.protocol;
import java.util.ArrayList;
import java.util.List;
public class ConnectionContext {
private final List<DataFeed> dataFeedList = new ArrayList<>();
private final List<Integer> subscribedTopics = new ArrayList<>();
private boolean useSerial = false;
private boolean useProvisioning = false;
private boolean useAutoBone = false;
public List<DataFeed> getDataFeedList() {
return dataFeedList;
}
public List<Integer> getSubscribedTopics() {
return subscribedTopics;
}
public boolean useSerial() {
return useSerial;
}
public void setUseSerial(boolean useSerial) {
this.useSerial = useSerial;
}
public boolean useAutoBone() {
return useAutoBone;
}
public void setUseAutoBone(boolean useAutoBone) {
this.useAutoBone = useAutoBone;
}
public boolean useProvisioning() {
return useProvisioning;
}
public void setUseProvisioning(boolean useProvisioning) {
this.useProvisioning = useProvisioning;
}
}

View File

@@ -0,0 +1,10 @@
package dev.slimevr.protocol
class ConnectionContext {
val dataFeedList: MutableList<DataFeed> = mutableListOf()
val subscribedTopics: MutableList<Int> = mutableListOf()
var useSerial: Boolean = false
var useProvisioning: Boolean = false
var useAutoBone: Boolean = false
}

View File

@@ -1,26 +0,0 @@
package dev.slimevr.protocol;
import solarxr_protocol.data_feed.DataFeedConfigT;
public class DataFeed {
private DataFeedConfigT config;
private Long timeLastSent;
public DataFeed(DataFeedConfigT config) {
this.config = config;
this.timeLastSent = System.currentTimeMillis();
}
public DataFeedConfigT getConfig() {
return config;
}
public Long getTimeLastSent() {
return timeLastSent;
}
public void setTimeLastSent(Long timeLastSent) {
this.timeLastSent = timeLastSent;
}
}

View File

@@ -0,0 +1,7 @@
package dev.slimevr.protocol
import solarxr_protocol.data_feed.DataFeedConfigT
class DataFeed(val config: DataFeedConfigT) {
var timeLastSent: Long = System.currentTimeMillis()
}

View File

@@ -1,14 +0,0 @@
package dev.slimevr.protocol;
import java.nio.ByteBuffer;
import java.util.UUID;
public interface GenericConnection {
UUID getConnectionId();
ConnectionContext getContext();
void send(ByteBuffer bytes);
}

View File

@@ -0,0 +1,12 @@
package dev.slimevr.protocol
import java.nio.ByteBuffer
import java.util.UUID
interface GenericConnection {
val connectionId: UUID
val context: ConnectionContext
fun send(bytes: ByteBuffer)
}

View File

@@ -1,69 +0,0 @@
package dev.slimevr.protocol;
import dev.slimevr.VRServer;
import dev.slimevr.protocol.datafeed.DataFeedHandler;
import dev.slimevr.protocol.pubsub.PubSubHandler;
import dev.slimevr.protocol.rpc.RPCHandler;
import solarxr_protocol.MessageBundle;
import solarxr_protocol.data_feed.DataFeedMessageHeader;
import solarxr_protocol.pub_sub.PubSubHeader;
import solarxr_protocol.rpc.RpcMessageHeader;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
public class ProtocolAPI {
public final VRServer server;
public final RPCHandler rpcHandler;
public final DataFeedHandler dataFeedHandler;
public final PubSubHandler pubSubHandler;
private final List<ProtocolAPIServer> servers = new ArrayList<>();
public ProtocolAPI(VRServer server) {
this.server = server;
this.rpcHandler = new RPCHandler(this);
this.dataFeedHandler = new DataFeedHandler(this);
this.pubSubHandler = new PubSubHandler(this);
}
public void onMessage(GenericConnection conn, ByteBuffer message) {
MessageBundle messageBundle = MessageBundle.getRootAsMessageBundle(message);
try {
for (int index = 0; index < messageBundle.dataFeedMsgsLength(); index++) {
DataFeedMessageHeader header = messageBundle.dataFeedMsgsVector().get(index);
this.dataFeedHandler.onMessage(conn, header);
}
for (int index = 0; index < messageBundle.rpcMsgsLength(); index++) {
RpcMessageHeader header = messageBundle.rpcMsgsVector().get(index);
this.rpcHandler.onMessage(conn, header);
}
for (int index = 0; index < messageBundle.pubSubMsgsLength(); index++) {
PubSubHeader header = messageBundle.pubSubMsgsVector().get(index);
this.pubSubHandler.onMessage(conn, header);
}
} catch (AssertionError e) {
// Catch flatbuffer errors
e.printStackTrace();
}
}
public List<ProtocolAPIServer> getAPIServers() {
return servers;
}
public void registerAPIServer(ProtocolAPIServer server) {
this.servers.add(server);
}
public void removeAPIServer(ProtocolAPIServer server) {
this.servers.remove(server);
}
}

View File

@@ -0,0 +1,48 @@
package dev.slimevr.protocol
import dev.slimevr.VRServer
import dev.slimevr.protocol.datafeed.DataFeedHandler
import dev.slimevr.protocol.pubsub.PubSubHandler
import dev.slimevr.protocol.rpc.RPCHandler
import solarxr_protocol.MessageBundle
import java.nio.ByteBuffer
class ProtocolAPI(val server: VRServer) {
val rpcHandler: RPCHandler = RPCHandler(this)
val dataFeedHandler: DataFeedHandler = DataFeedHandler(this)
val pubSubHandler: PubSubHandler = PubSubHandler(this)
val apiServers: MutableList<ProtocolAPIServer> = ArrayList()
fun onMessage(conn: GenericConnection, message: ByteBuffer) {
val messageBundle = MessageBundle.getRootAsMessageBundle(message)
try {
for (index in 0..<messageBundle.dataFeedMsgsLength()) {
val header = messageBundle.dataFeedMsgsVector().get(index)
this.dataFeedHandler.onMessage(conn, header)
}
for (index in 0..<messageBundle.rpcMsgsLength()) {
val header = messageBundle.rpcMsgsVector().get(index)
this.rpcHandler.onMessage(conn, header)
}
for (index in 0..<messageBundle.pubSubMsgsLength()) {
val header = messageBundle.pubSubMsgsVector().get(index)
this.pubSubHandler.onMessage(conn, header)
}
} catch (e: AssertionError) {
// Catch flatbuffer errors
e.printStackTrace()
}
}
fun registerAPIServer(server: ProtocolAPIServer) {
this.apiServers.add(server)
}
fun removeAPIServer(server: ProtocolAPIServer) {
this.apiServers.remove(server)
}
}

View File

@@ -1,9 +0,0 @@
package dev.slimevr.protocol;
import java.util.stream.Stream;
public interface ProtocolAPIServer {
Stream<GenericConnection> getAPIConnections();
}

View File

@@ -0,0 +1,7 @@
package dev.slimevr.protocol
import java.util.stream.Stream
interface ProtocolAPIServer {
val apiConnections: Stream<GenericConnection>
}

View File

@@ -1,425 +0,0 @@
package dev.slimevr.protocol.datafeed;
import com.google.flatbuffers.FlatBufferBuilder;
import dev.slimevr.tracking.trackers.Device;
import dev.slimevr.tracking.trackers.Tracker;
import dev.slimevr.tracking.trackers.udp.MagnetometerStatus;
import dev.slimevr.tracking.trackers.udp.UDPDevice;
import io.github.axisangles.ktmath.Quaternion;
import io.github.axisangles.ktmath.Vector3;
import solarxr_protocol.data_feed.Bone;
import solarxr_protocol.data_feed.DataFeedUpdate;
import solarxr_protocol.data_feed.device_data.DeviceData;
import solarxr_protocol.data_feed.device_data.DeviceDataMaskT;
import solarxr_protocol.data_feed.tracker.TrackerData;
import solarxr_protocol.data_feed.tracker.TrackerDataMaskT;
import solarxr_protocol.data_feed.tracker.TrackerInfo;
import solarxr_protocol.datatypes.DeviceId;
import solarxr_protocol.datatypes.Ipv4Address;
import solarxr_protocol.datatypes.Temperature;
import solarxr_protocol.datatypes.TrackerId;
import solarxr_protocol.datatypes.hardware_info.HardwareInfo;
import solarxr_protocol.datatypes.hardware_info.HardwareStatus;
import solarxr_protocol.datatypes.math.Quat;
import solarxr_protocol.datatypes.math.Vec3f;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
public class DataFeedBuilder {
public static int createHardwareInfo(FlatBufferBuilder fbb, Device device) {
int nameOffset = device.getFirmwareVersion() != null
? fbb.createString(device.getFirmwareVersion())
: 0;
int manufacturerOffset = device.getManufacturer() != null
? fbb.createString(device.getManufacturer())
: 0;
int firmwareDateOffset = device.getFirmwareDate() != null
? fbb.createString(device.getFirmwareDate())
: 0;
int hardwareIdentifierOffset = fbb.createString(device.getHardwareIdentifier());
HardwareInfo.startHardwareInfo(fbb);
HardwareInfo.addFirmwareVersion(fbb, nameOffset);
HardwareInfo.addManufacturer(fbb, manufacturerOffset);
HardwareInfo.addHardwareIdentifier(fbb, hardwareIdentifierOffset);
HardwareInfo.addFirmwareDate(fbb, firmwareDateOffset);
if (device instanceof UDPDevice udpDevice) {
var address = udpDevice.getIpAddress().getAddress();
HardwareInfo
.addIpAddress(
fbb,
Ipv4Address
.createIpv4Address(
fbb,
ByteBuffer.wrap(address).getInt()
)
);
HardwareInfo.addNetworkProtocolVersion(fbb, udpDevice.protocolVersion);
}
// BRUH MOMENT
// TODO need support: HardwareInfo.addHardwareRevision(fbb,
// hardwareRevisionOffset);
// TODO need support: HardwareInfo.addDisplayName(fbb, de);
HardwareInfo.addMcuId(fbb, device.getMcuType().getSolarType());
HardwareInfo.addOfficialBoardType(fbb, device.getBoardType().getSolarType());
return HardwareInfo.endHardwareInfo(fbb);
}
public static int createTrackerId(FlatBufferBuilder fbb, Tracker tracker) {
TrackerId.startTrackerId(fbb);
TrackerId.addTrackerNum(fbb, tracker.getTrackerNum());
if (tracker.getDevice() != null)
TrackerId.addDeviceId(fbb, DeviceId.createDeviceId(fbb, tracker.getDevice().getId()));
return TrackerId.endTrackerId(fbb);
}
public static int createVec3(FlatBufferBuilder fbb, Vector3 vec) {
return Vec3f
.createVec3f(
fbb,
vec.getX(),
vec.getY(),
vec.getZ()
);
}
public static int createQuat(FlatBufferBuilder fbb, Quaternion quaternion) {
return Quat
.createQuat(
fbb,
quaternion.getX(),
quaternion.getY(),
quaternion.getZ(),
quaternion.getW()
);
}
public static int createTrackerInfos(
FlatBufferBuilder fbb,
boolean infoMask,
Tracker tracker
) {
if (!infoMask)
return 0;
int displayNameOffset = fbb.createString(tracker.getDisplayName());
int customNameOffset = tracker.getCustomName() != null
? fbb.createString(tracker.getCustomName())
: 0;
TrackerInfo.startTrackerInfo(fbb);
if (tracker.getTrackerPosition() != null)
TrackerInfo.addBodyPart(fbb, tracker.getTrackerPosition().getBodyPart());
TrackerInfo.addEditable(fbb, tracker.getUserEditable());
TrackerInfo.addIsComputed(fbb, tracker.isComputed());
TrackerInfo.addDisplayName(fbb, displayNameOffset);
TrackerInfo.addCustomName(fbb, customNameOffset);
if (tracker.getImuType() != null) {
TrackerInfo.addImuType(fbb, tracker.getImuType().getSolarType());
}
// TODO need support: TrackerInfo.addPollRate(fbb, tracker.);
if (tracker.isImu()) {
TrackerInfo.addIsImu(fbb, true);
TrackerInfo
.addAllowDriftCompensation(
fbb,
tracker.getResetsHandler().getAllowDriftCompensation()
);
} else {
TrackerInfo.addIsImu(fbb, false);
TrackerInfo.addAllowDriftCompensation(fbb, false);
}
if (tracker.getAllowMounting()) {
Quaternion quaternion = tracker.getResetsHandler().getMountingOrientation();
Quaternion mountResetFix = tracker.getResetsHandler().getMountRotFix();
TrackerInfo.addMountingOrientation(fbb, createQuat(fbb, quaternion));
TrackerInfo.addMountingResetOrientation(fbb, createQuat(fbb, mountResetFix));
}
TrackerInfo.addMagnetometer(fbb, tracker.getMagStatus().getSolarType());
TrackerInfo.addIsHmd(fbb, tracker.isHmd());
TrackerInfo.addDataSupport(fbb, tracker.getTrackerDataType().getSolarType());
return TrackerInfo.endTrackerInfo(fbb);
}
public static int createTrackerPosition(FlatBufferBuilder fbb, Tracker tracker) {
return createVec3(fbb, tracker.getPosition());
}
public static int createTrackerRotation(FlatBufferBuilder fbb, Tracker tracker) {
return createQuat(fbb, tracker.getRawRotation());
}
public static int createTrackerAcceleration(FlatBufferBuilder fbb, Tracker tracker) {
return createVec3(fbb, tracker.getAcceleration());
}
public static int createTrackerMagneticVector(FlatBufferBuilder fbb, Tracker tracker) {
return createVec3(fbb, tracker.getMagVector());
}
public static int createTrackerTemperature(FlatBufferBuilder fbb, Tracker tracker) {
if (tracker.getTemperature() == null)
return 0;
return Temperature.createTemperature(fbb, tracker.getTemperature());
}
public static int createTrackerData(
FlatBufferBuilder fbb,
TrackerDataMaskT mask,
Tracker tracker
) {
int trackerInfosOffset = DataFeedBuilder.createTrackerInfos(fbb, mask.getInfo(), tracker);
int trackerIdOffset = DataFeedBuilder.createTrackerId(fbb, tracker);
int stayAlignedOffset = 0;
if (mask.getStayAligned()) {
stayAlignedOffset = DataFeedBuilderKotlin.INSTANCE
.createTrackerStayAlignedTracker(fbb, tracker.getStayAligned());
}
TrackerData.startTrackerData(fbb);
TrackerData.addTrackerId(fbb, trackerIdOffset);
if (trackerInfosOffset != 0)
TrackerData.addInfo(fbb, trackerInfosOffset);
if (mask.getStatus())
TrackerData.addStatus(fbb, tracker.getStatus().getId() + 1);
if (mask.getPosition() && tracker.getHasPosition())
TrackerData.addPosition(fbb, DataFeedBuilder.createTrackerPosition(fbb, tracker));
if (mask.getRotation() && tracker.getHasRotation())
TrackerData.addRotation(fbb, DataFeedBuilder.createTrackerRotation(fbb, tracker));
if (mask.getLinearAcceleration() && tracker.getHasAcceleration())
TrackerData
.addLinearAcceleration(
fbb,
DataFeedBuilder.createTrackerAcceleration(fbb, tracker)
);
if (mask.getTemp()) {
int trackerTemperatureOffset = DataFeedBuilder.createTrackerTemperature(fbb, tracker);
if (trackerTemperatureOffset != 0)
TrackerData.addTemp(fbb, trackerTemperatureOffset);
}
if (tracker.getAllowMounting() && tracker.getHasRotation()) {
if (mask.getRotationReferenceAdjusted()) {
TrackerData
.addRotationReferenceAdjusted(fbb, createQuat(fbb, tracker.getRotation()));
}
if (mask.getRotationIdentityAdjusted()) {
TrackerData
.addRotationIdentityAdjusted(
fbb,
createQuat(fbb, tracker.getIdentityAdjustedRotation())
);
}
} else if (tracker.getAllowReset() && tracker.getHasRotation()) {
if (mask.getRotationReferenceAdjusted()) {
TrackerData
.addRotationReferenceAdjusted(fbb, createQuat(fbb, tracker.getRotation()));
}
if (mask.getRotationIdentityAdjusted()) {
TrackerData
.addRotationIdentityAdjusted(fbb, createQuat(fbb, tracker.getRawRotation()));
}
}
if (mask.getTps()) {
TrackerData.addTps(fbb, (int) tracker.getTps());
}
if (mask.getRawMagneticVector() && tracker.getMagStatus() == MagnetometerStatus.ENABLED) {
TrackerData.addRawMagneticVector(fbb, createTrackerMagneticVector(fbb, tracker));
}
if (mask.getStayAligned()) {
TrackerData.addStayAligned(fbb, stayAlignedOffset);
}
return TrackerData.endTrackerData(fbb);
}
public static int createTrackersData(
FlatBufferBuilder fbb,
DeviceDataMaskT mask,
Device device
) {
if (mask.getTrackerData() == null)
return 0;
List<Integer> trackersOffsets = new ArrayList<>();
device
.getTrackers()
.forEach(
(key, value) -> trackersOffsets
.add(DataFeedBuilder.createTrackerData(fbb, mask.getTrackerData(), value))
);
DeviceData.startTrackersVector(fbb, trackersOffsets.size());
trackersOffsets.forEach(offset -> DeviceData.addTrackers(fbb, offset));
return fbb.endVector();
}
public static int createDeviceData(
FlatBufferBuilder fbb,
int id,
DeviceDataMaskT mask,
Device device
) {
if (!mask.getDeviceData())
return 0;
if (device.getTrackers().size() <= 0)
return 0;
Tracker firstTracker = device.getTrackers().get(0);
if (firstTracker == null) {
// Not actually the "first" tracker, but do we care?
firstTracker = device.getTrackers().entrySet().iterator().next().getValue();
}
Tracker tracker = firstTracker;
if (tracker == null)
return 0;
HardwareStatus.startHardwareStatus(fbb);
HardwareStatus.addErrorStatus(fbb, tracker.getStatus().getId());
if (tracker.getBatteryVoltage() != null) {
HardwareStatus.addBatteryVoltage(fbb, tracker.getBatteryVoltage());
}
if (tracker.getBatteryLevel() != null) {
HardwareStatus.addBatteryPctEstimate(fbb, (int) tracker.getBatteryLevel().floatValue());
}
if (tracker.getPing() != null) {
HardwareStatus.addPing(fbb, tracker.getPing());
}
if (tracker.getSignalStrength() != null) {
HardwareStatus.addRssi(fbb, (short) tracker.getSignalStrength().floatValue());
}
if (tracker.getPacketLoss() != null) {
HardwareStatus.addPacketLoss(fbb, tracker.getPacketLoss());
}
if (tracker.getPacketsLost() != null) {
HardwareStatus.addPacketsLost(fbb, tracker.getPacketsLost());
}
if (tracker.getPacketsReceived() != null) {
HardwareStatus.addPacketsReceived(fbb, tracker.getPacketsReceived());
}
if (tracker.getBatteryRemainingRuntime() != null) {
HardwareStatus.addBatteryRuntimeEstimate(fbb, tracker.getBatteryRemainingRuntime());
}
int hardwareDataOffset = HardwareStatus.endHardwareStatus(fbb);
int hardwareInfoOffset = DataFeedBuilder.createHardwareInfo(fbb, device);
int trackersOffset = DataFeedBuilder.createTrackersData(fbb, mask, device);
int nameOffset = device.getName() != null
? fbb.createString(device.getName())
: 0;
DeviceData.startDeviceData(fbb);
DeviceData.addCustomName(fbb, nameOffset);
DeviceData.addId(fbb, DeviceId.createDeviceId(fbb, id));
DeviceData.addHardwareStatus(fbb, hardwareDataOffset);
DeviceData.addHardwareInfo(fbb, hardwareInfoOffset);
DeviceData.addTrackers(fbb, trackersOffset);
return DeviceData.endDeviceData(fbb);
}
public static int createSyntheticTrackersData(
FlatBufferBuilder fbb,
TrackerDataMaskT trackerDataMaskT,
List<Tracker> trackers
) {
if (trackerDataMaskT == null)
return 0;
List<Integer> trackerOffsets = new ArrayList<>();
trackers
.forEach(
(tracker) -> trackerOffsets
.add(DataFeedBuilder.createTrackerData(fbb, trackerDataMaskT, tracker))
);
DataFeedUpdate.startSyntheticTrackersVector(fbb, trackerOffsets.size());
trackerOffsets.forEach((tracker -> DataFeedUpdate.addSyntheticTrackers(fbb, tracker)));
return fbb.endVector();
}
public static int createDevicesData(
FlatBufferBuilder fbb,
DeviceDataMaskT deviceDataMaskT,
List<Device> devices
) {
if (deviceDataMaskT == null)
return 0;
int[] devicesDataOffsets = new int[devices.size()];
for (int i = 0; i < devices.size(); i++) {
Device device = devices.get(i);
devicesDataOffsets[i] = DataFeedBuilder
.createDeviceData(fbb, device.getId(), deviceDataMaskT, device);
}
return DataFeedUpdate.createDevicesVector(fbb, devicesDataOffsets);
}
public static int createBonesData(
FlatBufferBuilder fbb,
boolean shouldSend,
List<dev.slimevr.tracking.processor.Bone> bones
) {
if (!shouldSend) {
return 0;
}
var boneOffsets = new int[bones.size()];
for (var i = 0; i < bones.size(); ++i) {
var bi = bones.get(i);
var headPosG = bi.getPosition();
var rotG = bi.getGlobalRotation();
var length = bi.getLength();
Bone.startBone(fbb);
var rotGOffset = createQuat(fbb, rotG);
Bone.addRotationG(fbb, rotGOffset);
var headPosGOffset = Vec3f
.createVec3f(fbb, headPosG.getX(), headPosG.getY(), headPosG.getZ());
Bone.addHeadPositionG(fbb, headPosGOffset);
Bone.addBodyPart(fbb, bi.getBoneType().bodyPart);
Bone.addBoneLength(fbb, length);
boneOffsets[i] = Bone.endBone(fbb);
}
return DataFeedUpdate.createBonesVector(fbb, boneOffsets);
}
}

View File

@@ -0,0 +1,503 @@
package dev.slimevr.protocol.datafeed
import com.google.flatbuffers.FlatBufferBuilder
import dev.slimevr.guards.ServerGuards
import dev.slimevr.tracking.processor.Bone
import dev.slimevr.tracking.processor.skeleton.HumanSkeleton
import dev.slimevr.tracking.processor.stayaligned.poses.RelaxedPose
import dev.slimevr.tracking.processor.stayaligned.trackers.RestDetector
import dev.slimevr.tracking.processor.stayaligned.trackers.StayAlignedTrackerState
import dev.slimevr.tracking.trackers.Device
import dev.slimevr.tracking.trackers.Tracker
import dev.slimevr.tracking.trackers.udp.MagnetometerStatus
import dev.slimevr.tracking.trackers.udp.UDPDevice
import io.github.axisangles.ktmath.Quaternion
import io.github.axisangles.ktmath.Vector3
import solarxr_protocol.data_feed.DataFeedUpdate
import solarxr_protocol.data_feed.device_data.DeviceData
import solarxr_protocol.data_feed.device_data.DeviceDataMaskT
import solarxr_protocol.data_feed.stay_aligned.StayAlignedPose
import solarxr_protocol.data_feed.stay_aligned.StayAlignedTracker
import solarxr_protocol.data_feed.tracker.TrackerData
import solarxr_protocol.data_feed.tracker.TrackerDataMaskT
import solarxr_protocol.data_feed.tracker.TrackerInfo
import solarxr_protocol.datatypes.DeviceId
import solarxr_protocol.datatypes.Ipv4Address
import solarxr_protocol.datatypes.Temperature
import solarxr_protocol.datatypes.TrackerId
import solarxr_protocol.datatypes.hardware_info.HardwareInfo
import solarxr_protocol.datatypes.hardware_info.HardwareStatus
import solarxr_protocol.datatypes.math.Quat
import solarxr_protocol.datatypes.math.Vec3f
import java.nio.ByteBuffer
import java.util.function.Consumer
fun createHardwareInfo(fbb: FlatBufferBuilder, device: Device): Int {
val nameOffset = if (device.firmwareVersion != null) {
fbb.createString(device.firmwareVersion)
} else {
0
}
val manufacturerOffset = if (device.manufacturer != null) {
fbb.createString(device.manufacturer)
} else {
0
}
val firmwareDateOffset = if (device.firmwareDate != null) {
fbb.createString(device.firmwareDate)
} else {
0
}
val hardwareIdentifierOffset = fbb.createString(device.hardwareIdentifier)
HardwareInfo.startHardwareInfo(fbb)
HardwareInfo.addFirmwareVersion(fbb, nameOffset)
HardwareInfo.addFirmwareDate(fbb, firmwareDateOffset)
HardwareInfo.addManufacturer(fbb, manufacturerOffset)
HardwareInfo.addHardwareIdentifier(fbb, hardwareIdentifierOffset)
if (device is UDPDevice) {
val address = device.ipAddress.address
HardwareInfo
.addIpAddress(
fbb,
Ipv4Address
.createIpv4Address(
fbb,
ByteBuffer.wrap(address).getInt().toLong(),
),
)
HardwareInfo.addNetworkProtocolVersion(fbb, device.protocolVersion)
}
// BRUH MOMENT
// TODO need support: HardwareInfo.addHardwareRevision(fbb,
// hardwareRevisionOffset);
// TODO need support: HardwareInfo.addDisplayName(fbb, de);
HardwareInfo.addMcuId(fbb, device.mcuType.getSolarType())
HardwareInfo.addOfficialBoardType(fbb, device.boardType.getSolarType())
return HardwareInfo.endHardwareInfo(fbb)
}
fun createTrackerId(fbb: FlatBufferBuilder, tracker: Tracker): Int {
TrackerId.startTrackerId(fbb)
TrackerId.addTrackerNum(fbb, tracker.trackerNum)
if (tracker.device != null) {
TrackerId.addDeviceId(
fbb,
DeviceId.createDeviceId(fbb, tracker.device.id),
)
}
return TrackerId.endTrackerId(fbb)
}
fun createVec3(fbb: FlatBufferBuilder, vec: Vector3): Int = Vec3f
.createVec3f(
fbb,
vec.x,
vec.y,
vec.z,
)
fun createQuat(fbb: FlatBufferBuilder, quaternion: Quaternion): Int = Quat
.createQuat(
fbb,
quaternion.x,
quaternion.y,
quaternion.z,
quaternion.w,
)
fun createTrackerInfos(
fbb: FlatBufferBuilder,
infoMask: Boolean,
tracker: Tracker,
): Int {
if (!infoMask) return 0
val displayNameOffset = fbb.createString(tracker.displayName)
val customNameOffset = if (tracker.customName != null) {
fbb.createString(tracker.customName)
} else {
0
}
TrackerInfo.startTrackerInfo(fbb)
if (tracker.trackerPosition != null) {
TrackerInfo.addBodyPart(
fbb,
tracker.trackerPosition!!.bodyPart,
)
}
TrackerInfo.addEditable(fbb, tracker.userEditable)
TrackerInfo.addIsComputed(fbb, tracker.isComputed)
TrackerInfo.addDisplayName(fbb, displayNameOffset)
TrackerInfo.addCustomName(fbb, customNameOffset)
if (tracker.imuType != null) {
TrackerInfo.addImuType(fbb, tracker.imuType.getSolarType())
}
// TODO need support: TrackerInfo.addPollRate(fbb, tracker.);
if (tracker.isImu()) {
TrackerInfo.addIsImu(fbb, true)
TrackerInfo
.addAllowDriftCompensation(
fbb,
tracker.resetsHandler.allowDriftCompensation,
)
} else {
TrackerInfo.addIsImu(fbb, false)
TrackerInfo.addAllowDriftCompensation(fbb, false)
}
if (tracker.allowMounting) {
val quaternion = tracker.resetsHandler.mountingOrientation
val mountResetFix = tracker.resetsHandler.mountRotFix
TrackerInfo.addMountingOrientation(fbb, createQuat(fbb, quaternion))
TrackerInfo.addMountingResetOrientation(fbb, createQuat(fbb, mountResetFix))
}
TrackerInfo.addMagnetometer(fbb, tracker.magStatus.getSolarType())
TrackerInfo.addIsHmd(fbb, tracker.isHmd)
TrackerInfo.addDataSupport(fbb, tracker.trackerDataType.getSolarType())
return TrackerInfo.endTrackerInfo(fbb)
}
fun createTrackerPosition(fbb: FlatBufferBuilder, tracker: Tracker): Int = createVec3(fbb, tracker.position)
fun createTrackerRotation(fbb: FlatBufferBuilder, tracker: Tracker): Int = createQuat(fbb, tracker.getRawRotation())
fun createTrackerAcceleration(fbb: FlatBufferBuilder, tracker: Tracker): Int = createVec3(fbb, tracker.getAcceleration())
fun createTrackerMagneticVector(fbb: FlatBufferBuilder, tracker: Tracker): Int = createVec3(fbb, tracker.getMagVector())
fun createTrackerTemperature(fbb: FlatBufferBuilder, tracker: Tracker): Int {
if (tracker.temperature == null) return 0
return Temperature.createTemperature(fbb, tracker.temperature!!)
}
fun createTrackerData(
fbb: FlatBufferBuilder,
mask: TrackerDataMaskT,
tracker: Tracker,
): Int {
val trackerInfosOffset = createTrackerInfos(fbb, mask.info, tracker)
val trackerIdOffset = createTrackerId(fbb, tracker)
var stayAlignedOffset = 0
if (mask.stayAligned) {
stayAlignedOffset =
createTrackerStayAlignedTracker(fbb, tracker.stayAligned)
}
TrackerData.startTrackerData(fbb)
TrackerData.addTrackerId(fbb, trackerIdOffset)
if (trackerInfosOffset != 0) TrackerData.addInfo(fbb, trackerInfosOffset)
if (mask.status) TrackerData.addStatus(fbb, tracker.status.id + 1)
if (mask.position && tracker.hasPosition) {
TrackerData.addPosition(
fbb,
createTrackerPosition(fbb, tracker),
)
}
if (mask.rotation && tracker.hasRotation) {
TrackerData.addRotation(
fbb,
createTrackerRotation(fbb, tracker),
)
}
if (mask.linearAcceleration && tracker.hasAcceleration) {
TrackerData
.addLinearAcceleration(
fbb,
createTrackerAcceleration(fbb, tracker),
)
}
if (mask.temp) {
val trackerTemperatureOffset = createTrackerTemperature(fbb, tracker)
if (trackerTemperatureOffset != 0) {
TrackerData.addTemp(
fbb,
trackerTemperatureOffset,
)
}
}
if (tracker.allowMounting && tracker.hasRotation) {
if (mask.rotationReferenceAdjusted) {
TrackerData
.addRotationReferenceAdjusted(
fbb,
createQuat(fbb, tracker.getRotation()),
)
}
if (mask.rotationIdentityAdjusted) {
TrackerData
.addRotationIdentityAdjusted(
fbb,
createQuat(fbb, tracker.getIdentityAdjustedRotation()),
)
}
} else if (tracker.allowReset && tracker.hasRotation) {
if (mask.rotationReferenceAdjusted) {
TrackerData
.addRotationReferenceAdjusted(
fbb,
createQuat(fbb, tracker.getRotation()),
)
}
if (mask.rotationIdentityAdjusted) {
TrackerData
.addRotationIdentityAdjusted(
fbb,
createQuat(fbb, tracker.getRawRotation()),
)
}
}
if (mask.tps) {
TrackerData.addTps(fbb, tracker.tps.toInt())
}
if (mask.rawMagneticVector && tracker.magStatus == MagnetometerStatus.ENABLED) {
TrackerData.addRawMagneticVector(
fbb,
createTrackerMagneticVector(fbb, tracker),
)
}
if (mask.stayAligned) {
TrackerData.addStayAligned(fbb, stayAlignedOffset)
}
return TrackerData.endTrackerData(fbb)
}
fun createTrackersData(
fbb: FlatBufferBuilder,
mask: DeviceDataMaskT,
device: Device,
): Int {
if (mask.trackerData == null) return 0
val trackersOffsets: MutableList<Int> = ArrayList()
device
.trackers
.forEach { (_: Int, value: Tracker) ->
trackersOffsets
.add(createTrackerData(fbb, mask.trackerData, value))
}
DeviceData.startTrackersVector(fbb, trackersOffsets.size)
trackersOffsets.forEach(
Consumer { offset: Int ->
DeviceData.addTrackers(
fbb,
offset,
)
},
)
return fbb.endVector()
}
fun createDeviceData(
fbb: FlatBufferBuilder,
id: Int,
mask: DeviceDataMaskT,
device: Device,
): Int {
if (!mask.deviceData) return 0
if (device.trackers.isEmpty()) return 0
var firstTracker = device.trackers[0]
if (firstTracker == null) {
// Not actually the "first" tracker, but do we care?
firstTracker = device.trackers.entries.iterator().next().value
}
val tracker: Tracker = firstTracker
HardwareStatus.startHardwareStatus(fbb)
HardwareStatus.addErrorStatus(fbb, tracker.status.id)
if (tracker.batteryVoltage != null) {
HardwareStatus.addBatteryVoltage(fbb, tracker.batteryVoltage!!)
}
if (tracker.batteryLevel != null) {
HardwareStatus.addBatteryPctEstimate(fbb, tracker.batteryLevel!!.toInt())
}
if (tracker.ping != null) {
HardwareStatus.addPing(fbb, tracker.ping!!)
}
if (tracker.signalStrength != null) {
HardwareStatus.addRssi(fbb, tracker.signalStrength!!.toShort())
}
if (tracker.packetLoss != null) {
HardwareStatus.addPacketLoss(fbb, tracker.packetLoss!!)
}
if (tracker.packetsLost != null) {
HardwareStatus.addPacketsLost(fbb, tracker.packetsLost!!)
}
if (tracker.packetsReceived != null) {
HardwareStatus.addPacketsReceived(fbb, tracker.packetsReceived!!)
}
val hardwareDataOffset = HardwareStatus.endHardwareStatus(fbb)
val hardwareInfoOffset = createHardwareInfo(fbb, device)
val trackersOffset = createTrackersData(fbb, mask, device)
val nameOffset = if (device.name != null) {
fbb.createString(device.name)
} else {
0
}
DeviceData.startDeviceData(fbb)
DeviceData.addCustomName(fbb, nameOffset)
DeviceData.addId(fbb, DeviceId.createDeviceId(fbb, id))
DeviceData.addHardwareStatus(fbb, hardwareDataOffset)
DeviceData.addHardwareInfo(fbb, hardwareInfoOffset)
DeviceData.addTrackers(fbb, trackersOffset)
return DeviceData.endDeviceData(fbb)
}
fun createSyntheticTrackersData(
fbb: FlatBufferBuilder,
trackerDataMaskT: TrackerDataMaskT?,
trackers: MutableList<Tracker>,
): Int {
if (trackerDataMaskT == null) return 0
val trackerOffsets: MutableList<Int> = ArrayList()
trackers
.forEach(
Consumer { tracker: Tracker ->
trackerOffsets
.add(createTrackerData(fbb, trackerDataMaskT, tracker))
},
)
DataFeedUpdate.startSyntheticTrackersVector(fbb, trackerOffsets.size)
trackerOffsets.forEach(
(
Consumer { tracker: Int ->
DataFeedUpdate.addSyntheticTrackers(
fbb,
tracker,
)
}
),
)
return fbb.endVector()
}
fun createDevicesData(
fbb: FlatBufferBuilder,
deviceDataMaskT: DeviceDataMaskT?,
devices: MutableList<Device>,
): Int {
if (deviceDataMaskT == null) return 0
val devicesDataOffsets = IntArray(devices.size)
for (i in devices.indices) {
val device = devices[i]
devicesDataOffsets[i] =
createDeviceData(fbb, device.id, deviceDataMaskT, device)
}
return DataFeedUpdate.createDevicesVector(fbb, devicesDataOffsets)
}
fun createBonesData(
fbb: FlatBufferBuilder,
shouldSend: Boolean,
bones: MutableList<Bone>,
): Int {
if (!shouldSend) {
return 0
}
val boneOffsets = IntArray(bones.size)
for (i in bones.indices) {
val bi = bones[i]
val headPosG =
bi.getPosition()
val rotG =
bi.getGlobalRotation()
val length = bi.length
solarxr_protocol.data_feed.Bone.startBone(fbb)
val rotGOffset = createQuat(fbb, rotG)
solarxr_protocol.data_feed.Bone.addRotationG(fbb, rotGOffset)
val headPosGOffset = Vec3f
.createVec3f(fbb, headPosG.x, headPosG.y, headPosG.z)
solarxr_protocol.data_feed.Bone.addHeadPositionG(fbb, headPosGOffset)
solarxr_protocol.data_feed.Bone.addBodyPart(fbb, bi.boneType.bodyPart)
solarxr_protocol.data_feed.Bone.addBoneLength(fbb, length)
boneOffsets[i] = solarxr_protocol.data_feed.Bone.endBone(fbb)
}
return DataFeedUpdate.createBonesVector(fbb, boneOffsets)
}
fun createStayAlignedPose(
fbb: FlatBufferBuilder,
humanSkeleton: HumanSkeleton,
): Int {
val relaxedPose = RelaxedPose.fromTrackers(humanSkeleton)
StayAlignedPose.startStayAlignedPose(fbb)
StayAlignedPose.addUpperLegAngleInDeg(fbb, relaxedPose.upperLeg.toDeg())
StayAlignedPose.addLowerLegAngleInDeg(fbb, relaxedPose.lowerLeg.toDeg())
StayAlignedPose.addFootAngleInDeg(fbb, relaxedPose.foot.toDeg())
return StayAlignedPose.endStayAlignedPose(fbb)
}
fun createTrackerStayAlignedTracker(
fbb: FlatBufferBuilder,
state: StayAlignedTrackerState,
): Int {
StayAlignedTracker.startStayAlignedTracker(fbb)
StayAlignedTracker.addYawCorrectionInDeg(fbb, state.yawCorrection.toDeg())
StayAlignedTracker.addLockedErrorInDeg(
fbb,
state.yawErrors.lockedError.toL2Norm().toDeg(),
)
StayAlignedTracker.addCenterErrorInDeg(
fbb,
state.yawErrors.centerError.toL2Norm().toDeg(),
)
StayAlignedTracker.addNeighborErrorInDeg(
fbb,
state.yawErrors.neighborError.toL2Norm().toDeg(),
)
StayAlignedTracker.addLocked(
fbb,
state.restDetector.state == RestDetector.State.AT_REST,
)
return StayAlignedTracker.endStayAlignedTracker(fbb)
}
fun createServerGuard(fbb: FlatBufferBuilder, serverGuards: ServerGuards): Int = solarxr_protocol.data_feed.server.ServerGuards.createServerGuards(
fbb,
serverGuards.canDoMounting,
serverGuards.canDoYawReset,
serverGuards.canDoUserHeightCalibration,
)

View File

@@ -1,50 +0,0 @@
package dev.slimevr.protocol.datafeed
import com.google.flatbuffers.FlatBufferBuilder
import dev.slimevr.guards.ServerGuards
import dev.slimevr.tracking.processor.skeleton.HumanSkeleton
import dev.slimevr.tracking.processor.stayaligned.poses.RelaxedPose
import dev.slimevr.tracking.processor.stayaligned.trackers.RestDetector
import dev.slimevr.tracking.processor.stayaligned.trackers.StayAlignedTrackerState
import solarxr_protocol.data_feed.stay_aligned.StayAlignedPose
import solarxr_protocol.data_feed.stay_aligned.StayAlignedTracker
object DataFeedBuilderKotlin {
fun createStayAlignedPose(
fbb: FlatBufferBuilder,
humanSkeleton: HumanSkeleton,
): Int {
val relaxedPose = RelaxedPose.fromTrackers(humanSkeleton)
StayAlignedPose.startStayAlignedPose(fbb)
StayAlignedPose.addUpperLegAngleInDeg(fbb, relaxedPose.upperLeg.toDeg())
StayAlignedPose.addLowerLegAngleInDeg(fbb, relaxedPose.lowerLeg.toDeg())
StayAlignedPose.addFootAngleInDeg(fbb, relaxedPose.foot.toDeg())
return StayAlignedPose.endStayAlignedPose(fbb)
}
fun createTrackerStayAlignedTracker(
fbb: FlatBufferBuilder,
state: StayAlignedTrackerState,
): Int {
StayAlignedTracker.startStayAlignedTracker(fbb)
StayAlignedTracker.addYawCorrectionInDeg(fbb, state.yawCorrection.toDeg())
StayAlignedTracker.addLockedErrorInDeg(fbb, state.yawErrors.lockedError.toL2Norm().toDeg())
StayAlignedTracker.addCenterErrorInDeg(fbb, state.yawErrors.centerError.toL2Norm().toDeg())
StayAlignedTracker.addNeighborErrorInDeg(fbb, state.yawErrors.neighborError.toL2Norm().toDeg())
StayAlignedTracker.addLocked(fbb, state.restDetector.state == RestDetector.State.AT_REST)
return StayAlignedTracker.endStayAlignedTracker(fbb)
}
fun createServerGuard(fbb: FlatBufferBuilder, serverGuards: ServerGuards): Int = solarxr_protocol.data_feed.server.ServerGuards.createServerGuards(
fbb,
serverGuards.canDoMounting,
serverGuards.canDoYawReset,
serverGuards.canDoUserHeightCalibration,
)
}

View File

@@ -1,192 +0,0 @@
package dev.slimevr.protocol.datafeed;
import com.google.flatbuffers.FlatBufferBuilder;
import dev.slimevr.protocol.DataFeed;
import dev.slimevr.protocol.GenericConnection;
import dev.slimevr.protocol.ProtocolAPI;
import dev.slimevr.protocol.ProtocolHandler;
import dev.slimevr.tracking.trackers.Tracker;
import io.eiren.util.logging.LogManager;
import solarxr_protocol.MessageBundle;
import solarxr_protocol.data_feed.*;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;
public class DataFeedHandler extends ProtocolHandler<DataFeedMessageHeader> {
private final ProtocolAPI api;
public DataFeedHandler(ProtocolAPI api) {
this.api = api;
registerPacketListener(DataFeedMessage.StartDataFeed, this::onStartDataFeed);
registerPacketListener(DataFeedMessage.PollDataFeed, this::onPollDataFeedRequest);
this.api.server.addOnTick(this::sendDataFeedUpdate);
}
private void onStartDataFeed(GenericConnection conn, DataFeedMessageHeader header) {
StartDataFeed req = (StartDataFeed) header.message(new StartDataFeed());
if (req == null)
return;
int dataFeeds = req.dataFeedsLength();
List<DataFeed> feedList = conn.getContext().getDataFeedList();
synchronized (feedList) {
feedList.clear();
for (int i = 0; i < dataFeeds; i++) {
// Using the object api here because we
// need to copy from the buffer, anyway let's
// do it from here and send the reference to an arraylist
DataFeedConfigT config = req.dataFeeds(i).unpack();
feedList.add(new DataFeed(config));
}
}
}
private void onPollDataFeedRequest(
GenericConnection conn,
DataFeedMessageHeader messageHeader
) {
PollDataFeed req = (PollDataFeed) messageHeader.message(new PollDataFeed());
if (req == null)
return;
FlatBufferBuilder fbb = new FlatBufferBuilder(300);
int messageOffset = this.buildDatafeed(fbb, req.config().unpack(), 0);
DataFeedMessageHeader.startDataFeedMessageHeader(fbb);
DataFeedMessageHeader.addMessage(fbb, messageOffset);
DataFeedMessageHeader.addMessageType(fbb, DataFeedMessage.DataFeedUpdate);
int headerOffset = DataFeedMessageHeader.endDataFeedMessageHeader(fbb);
MessageBundle.startDataFeedMsgsVector(fbb, 1);
MessageBundle.addDataFeedMsgs(fbb, headerOffset);
int datafeedMessagesOffset = fbb.endVector();
int packet = createMessage(fbb, datafeedMessagesOffset);
fbb.finish(packet);
conn.send(fbb.dataBuffer());
}
public int buildDatafeed(FlatBufferBuilder fbb, DataFeedConfigT config, int index) {
int devicesOffset = DataFeedBuilder
.createDevicesData(
fbb,
config.getDataMask(),
this.api.server.deviceManager
.getDevices()
);
// Synthetic tracker is computed tracker apparently
int trackersOffset = DataFeedBuilder
.createSyntheticTrackersData(
fbb,
config.getSyntheticTrackersMask(),
this.api.server
.getAllTrackers()
.stream()
.filter(Tracker::isComputed)
.collect(Collectors.toList())
);
var h = this.api.server.humanPoseManager;
int bonesOffset = DataFeedBuilder
.createBonesData(
fbb,
config.getBoneMask(),
h.getAllBones()
);
int stayAlignedPoseOffset = 0;
if (config.getStayAlignedPoseMask()) {
stayAlignedPoseOffset = DataFeedBuilderKotlin.INSTANCE
.createStayAlignedPose(fbb, this.api.server.humanPoseManager.skeleton);
}
int serverGuardsOffset = 0;
if (config.getServerGuardsMask()) {
serverGuardsOffset = DataFeedBuilderKotlin.INSTANCE
.createServerGuard(fbb, this.api.server.getServerGuards());
}
return DataFeedUpdate
.createDataFeedUpdate(
fbb,
devicesOffset,
trackersOffset,
bonesOffset,
stayAlignedPoseOffset,
index,
serverGuardsOffset
);
}
public void sendDataFeedUpdate() {
long currTime = System.currentTimeMillis();
this.api.getAPIServers().forEach((server) -> server.getAPIConnections().forEach((conn) -> {
FlatBufferBuilder fbb = null;
List<DataFeed> feedList = conn.getContext().getDataFeedList();
synchronized (feedList) {
int configsCount = feedList.size();
int[] data = new int[configsCount];
for (int index = 0; index < configsCount; index++) {
DataFeed feed = feedList.get(index);
Long lastTimeSent = feed.getTimeLastSent();
DataFeedConfigT configT = feed.getConfig();
if (currTime - lastTimeSent > configT.getMinimumTimeSinceLast()) {
if (fbb == null) {
// That way we create a buffer only when needed
fbb = new FlatBufferBuilder(300);
}
int messageOffset = this.buildDatafeed(fbb, configT, index);
DataFeedMessageHeader.startDataFeedMessageHeader(fbb);
DataFeedMessageHeader.addMessage(fbb, messageOffset);
DataFeedMessageHeader.addMessageType(fbb, DataFeedMessage.DataFeedUpdate);
data[index] = DataFeedMessageHeader.endDataFeedMessageHeader(fbb);
feed.setTimeLastSent(currTime);
int messages = MessageBundle.createDataFeedMsgsVector(fbb, data);
int packet = createMessage(fbb, messages);
fbb.finish(packet);
conn.send(fbb.dataBuffer());
}
}
}
}));
}
@Override
public void onMessage(GenericConnection conn, DataFeedMessageHeader message) {
BiConsumer<GenericConnection, DataFeedMessageHeader> consumer = this.handlers[message
.messageType()];
if (consumer != null)
consumer.accept(conn, message);
else
LogManager
.info(
"[ProtocolAPI] Unhandled Datafeed packet received id: " + message.messageType()
);
}
@Override
public int messagesCount() {
return DataFeedMessage.names.length;
}
public int createMessage(FlatBufferBuilder fbb, int datafeedMessagesOffset) {
MessageBundle.startMessageBundle(fbb);
if (datafeedMessagesOffset > -1)
MessageBundle.addDataFeedMsgs(fbb, datafeedMessagesOffset);
return MessageBundle.endMessageBundle(fbb);
}
}

View File

@@ -0,0 +1,177 @@
package dev.slimevr.protocol.datafeed
import com.google.flatbuffers.FlatBufferBuilder
import dev.slimevr.protocol.DataFeed
import dev.slimevr.protocol.GenericConnection
import dev.slimevr.protocol.ProtocolAPI
import dev.slimevr.protocol.ProtocolAPIServer
import dev.slimevr.protocol.ProtocolHandler
import dev.slimevr.tracking.trackers.Tracker
import io.eiren.util.logging.LogManager
import solarxr_protocol.MessageBundle
import solarxr_protocol.data_feed.DataFeedConfigT
import solarxr_protocol.data_feed.DataFeedMessage
import solarxr_protocol.data_feed.DataFeedMessageHeader
import solarxr_protocol.data_feed.DataFeedUpdate
import solarxr_protocol.data_feed.PollDataFeed
import solarxr_protocol.data_feed.StartDataFeed
import java.util.function.Consumer
import java.util.stream.Collectors
class DataFeedHandler(private val api: ProtocolAPI) : ProtocolHandler<DataFeedMessageHeader>() {
init {
registerPacketListener(DataFeedMessage.StartDataFeed, ::onStartDataFeed)
registerPacketListener(DataFeedMessage.PollDataFeed, ::onPollDataFeedRequest)
this.api.server.addOnTick { this.sendDataFeedUpdate() }
}
private fun onStartDataFeed(conn: GenericConnection, header: DataFeedMessageHeader) {
val req = header.message(StartDataFeed()) as StartDataFeed? ?: return
val dataFeeds = req.dataFeedsLength()
val feedList = conn.context.dataFeedList
synchronized(feedList) {
feedList.clear()
for (i in 0..<dataFeeds) {
// Using the object api here because we
// need to copy from the buffer, anyway let's
// do it from here and send the reference to an arraylist
val config = req.dataFeeds(i).unpack()
feedList.add(DataFeed(config))
}
}
}
private fun onPollDataFeedRequest(
conn: GenericConnection,
messageHeader: DataFeedMessageHeader,
) {
val req = messageHeader.message(PollDataFeed()) as PollDataFeed? ?: return
val fbb = FlatBufferBuilder(300)
val messageOffset = this.buildDatafeed(fbb, req.config().unpack(), 0)
DataFeedMessageHeader.startDataFeedMessageHeader(fbb)
DataFeedMessageHeader.addMessage(fbb, messageOffset)
DataFeedMessageHeader.addMessageType(fbb, DataFeedMessage.DataFeedUpdate)
val headerOffset = DataFeedMessageHeader.endDataFeedMessageHeader(fbb)
MessageBundle.startDataFeedMsgsVector(fbb, 1)
MessageBundle.addDataFeedMsgs(fbb, headerOffset)
val datafeedMessagesOffset = fbb.endVector()
val packet = createMessage(fbb, datafeedMessagesOffset)
fbb.finish(packet)
conn.send(fbb.dataBuffer())
}
fun buildDatafeed(fbb: FlatBufferBuilder, config: DataFeedConfigT, index: Int): Int {
val devicesOffset = createDevicesData(
fbb,
config.dataMask,
this.api.server.deviceManager
.devices,
)
// Synthetic tracker is computed tracker apparently
val trackersOffset = createSyntheticTrackersData(
fbb,
config.syntheticTrackersMask,
this.api.server
.allTrackers
.stream()
.filter(Tracker::isComputed)
.collect(Collectors.toList()),
)
val h = this.api.server.humanPoseManager
val bonesOffset =
createBonesData(
fbb,
config.boneMask,
h.allBones.toMutableList(),
)
var stayAlignedPoseOffset = 0
if (config.stayAlignedPoseMask) {
stayAlignedPoseOffset = createStayAlignedPose(fbb, this.api.server.humanPoseManager.skeleton)
}
var serverGuardsOffset = 0
if (config.serverGuardsMask) {
serverGuardsOffset = createServerGuard(fbb, this.api.server.serverGuards)
}
return DataFeedUpdate
.createDataFeedUpdate(
fbb,
devicesOffset,
trackersOffset,
bonesOffset,
stayAlignedPoseOffset,
index,
serverGuardsOffset,
)
}
fun sendDataFeedUpdate() {
val currTime = System.currentTimeMillis()
this.api.apiServers.forEach(
Consumer { server: ProtocolAPIServer ->
server.apiConnections.forEach { conn: GenericConnection ->
var fbb: FlatBufferBuilder? = null
val feedList = conn.context.dataFeedList
synchronized(feedList) {
val configsCount = feedList.size
val data = IntArray(configsCount)
for (index in 0..<configsCount) {
val feed = feedList[index]
val lastTimeSent = feed.timeLastSent
val configT = feed.config
if (currTime - lastTimeSent > configT.minimumTimeSinceLast) {
if (fbb == null) {
// That way we create a buffer only when needed
fbb = FlatBufferBuilder(300)
}
val messageOffset = this.buildDatafeed(fbb, configT, index)
DataFeedMessageHeader.startDataFeedMessageHeader(fbb)
DataFeedMessageHeader.addMessage(fbb, messageOffset)
DataFeedMessageHeader.addMessageType(fbb, DataFeedMessage.DataFeedUpdate)
data[index] = DataFeedMessageHeader.endDataFeedMessageHeader(fbb)
feed.timeLastSent = currTime
val messages = MessageBundle.createDataFeedMsgsVector(fbb, data)
val packet = createMessage(fbb, messages)
fbb.finish(packet)
conn.send(fbb.dataBuffer())
}
}
}
}
},
)
}
override fun onMessage(conn: GenericConnection, message: DataFeedMessageHeader) {
val consumer = this.handlers[message.messageType().toInt()]
if (consumer != null) {
consumer.accept(conn, message)
} else {
LogManager
.info(
"[ProtocolAPI] Unhandled Datafeed packet received id: " + message.messageType(),
)
}
}
override fun messagesCount(): Int = DataFeedMessage.names.size
fun createMessage(fbb: FlatBufferBuilder, datafeedMessagesOffset: Int): Int {
MessageBundle.startMessageBundle(fbb)
if (datafeedMessagesOffset > -1) MessageBundle.addDataFeedMsgs(fbb, datafeedMessagesOffset)
return MessageBundle.endMessageBundle(fbb)
}
}

View File

@@ -1,45 +0,0 @@
package dev.slimevr.protocol.pubsub;
import solarxr_protocol.pub_sub.TopicIdT;
import java.util.Objects;
// This class is so the HashMap referencing the TopicId as key works
// it needs a unique hashcode based on the topicId and also an equals function
// because equals hashcode does not mean equals strings
public class HashedTopicId {
private final TopicIdT inner;
private final int hashcode;
public HashedTopicId(TopicIdT topicIdT) {
this.inner = topicIdT;
this.hashcode = (inner.getAppName()
+ "."
+ inner.getOrganization()
+ "."
+ inner.getTopic()).hashCode();
}
public TopicIdT getInner() {
return inner;
}
@Override
public int hashCode() {
return hashcode;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
HashedTopicId that = (HashedTopicId) o;
return Objects.equals(inner.getOrganization(), that.getInner().getOrganization())
&& Objects.equals(inner.getAppName(), that.getInner().getAppName())
&& Objects.equals(inner.getTopic(), that.getInner().getTopic());
}
}

View File

@@ -0,0 +1,29 @@
package dev.slimevr.protocol.pubsub
import solarxr_protocol.pub_sub.TopicIdT
// This class is so the HashMap referencing the TopicId as key works
// it needs a unique hashcode based on the topicId and also an equals function
// because equals hashcode does not mean equals strings
class HashedTopicId(val inner: TopicIdT) {
private val hashcode: Int = (
(
inner.appName +
"." +
inner.organization +
"." +
inner.topic
)
).hashCode()
override fun hashCode(): Int = hashcode
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || javaClass != other.javaClass) return false
val that = other as HashedTopicId
return inner.organization == that.inner.organization &&
inner.appName == that.inner.appName &&
inner.topic == that.inner.topic
}
}

View File

@@ -1,185 +0,0 @@
package dev.slimevr.protocol.pubsub;
import com.google.flatbuffers.FlatBufferBuilder;
import dev.slimevr.protocol.GenericConnection;
import dev.slimevr.protocol.ProtocolAPI;
import dev.slimevr.protocol.ProtocolHandler;
import io.eiren.util.logging.LogManager;
import solarxr_protocol.MessageBundle;
import solarxr_protocol.pub_sub.*;
import java.util.HashMap;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
public class PubSubHandler extends ProtocolHandler<PubSubHeader> {
private final ProtocolAPI api;
// Two ways maps for faster reading when handling lots of packets
public HashMap<HashedTopicId, Integer> topicsHandle = new HashMap<>();
public HashMap<Integer, HashedTopicId> handleTopics = new HashMap<>();
public AtomicInteger nextLocalHandle = new AtomicInteger();
public PubSubHandler(ProtocolAPI api) {
super();
this.api = api;
registerPacketListener(PubSubUnion.SubscriptionRequest, this::onSubscriptionRequest);
registerPacketListener(PubSubUnion.TopicHandleRequest, this::onTopicHandleRequest);
registerPacketListener(PubSubUnion.Message, this::onTopicMessage);
}
private int getTopicHandle(TopicIdT topicIdT) {
HashedTopicId hashedTopicId = new HashedTopicId(topicIdT);
Integer handleT = topicsHandle.get(hashedTopicId);
// if no handle exists for this topic id we create one and return it
// anyway
if (handleT == null) {
handleT = nextLocalHandle.incrementAndGet();
topicsHandle.put(hashedTopicId, handleT);
handleTopics.put(handleT, hashedTopicId);
}
return handleT;
}
public void onSubscriptionRequest(GenericConnection conn, PubSubHeader messageHeader) {
SubscriptionRequest req = (SubscriptionRequest) messageHeader.u(new SubscriptionRequest());
if (req == null)
return;
int subHandle = -1;
if (req.topicType() == Topic.TopicHandle) {
TopicHandle handle = (TopicHandle) req.topic(new TopicHandle());
if (handle != null && handleTopics.containsKey(handle.id()))
subHandle = handle.id();
} else if (req.topicType() == Topic.TopicId) {
TopicId topicId = (TopicId) req.topic(new TopicId());
if (topicId != null)
subHandle = getTopicHandle(topicId.unpack());
}
assert subHandle != -1;
final int finalSubHandle = subHandle;
Optional<Integer> first = conn
.getContext()
.getSubscribedTopics()
.stream()
.filter((handle) -> handle == finalSubHandle)
.findFirst();
if (!first.isPresent()) {
conn.getContext().getSubscribedTopics().add(finalSubHandle);
}
FlatBufferBuilder fbb = new FlatBufferBuilder(32);
int topicIdOffset = TopicId.pack(fbb, handleTopics.get(finalSubHandle).getInner());
int topicHandleOffset = TopicHandle.createTopicHandle(fbb, finalSubHandle);
int outbound = createMessage(
fbb,
PubSubUnion.TopicMapping,
TopicMapping.createTopicMapping(fbb, topicIdOffset, topicHandleOffset)
);
fbb.finish(outbound);
conn.send(fbb.dataBuffer());
}
public void onTopicHandleRequest(GenericConnection conn, PubSubHeader messageHeader) {
TopicHandleRequest req = (TopicHandleRequest) messageHeader.u(new TopicHandleRequest());
if (req == null)
return;
TopicHandleRequestT topicRequest = req.unpack();
int handle = getTopicHandle(topicRequest.getId());
FlatBufferBuilder fbb = new FlatBufferBuilder(32);
int topicIdOffset = TopicId.pack(fbb, topicRequest.getId());
int topicHandleOffset = TopicHandle.createTopicHandle(fbb, handle);
int outbound = createMessage(
fbb,
PubSubUnion.TopicMapping,
TopicMapping.createTopicMapping(fbb, topicIdOffset, topicHandleOffset)
);
fbb.finish(outbound);
conn.send(fbb.dataBuffer());
}
public void onTopicMessage(GenericConnection c, PubSubHeader messageHeader) {
Message req = (Message) messageHeader.u(new Message());
if (req == null)
return;
MessageT messageT = req.unpack();
int subHandle = 1;
if (messageT.getTopic().getType() == Topic.TopicHandle) {
subHandle = messageT.getTopic().asTopicHandle().getId();
} else if (messageT.getTopic().getType() == Topic.TopicId) {
subHandle = getTopicHandle(messageT.getTopic().asTopicId());
}
assert subHandle != -1;
int finalSubHandle = subHandle;
this.api.getAPIServers().forEach((server) -> {
server.getAPIConnections().forEach((conn) -> {
// Make sure that we are not sending a message to ourselves
// And check that the receiver has subscribed to the topic
if (
!conn.getConnectionId().equals(c.getConnectionId())
&& conn.getContext().getSubscribedTopics().contains(finalSubHandle)
) {
FlatBufferBuilder fbb = new FlatBufferBuilder(32);
int outbound = createMessage(
fbb,
PubSubUnion.Message,
Message.pack(fbb, messageT)
);
fbb.finish(outbound);
conn.send(fbb.dataBuffer());
}
});
});
}
@Override
public void onMessage(GenericConnection conn, PubSubHeader message) {
BiConsumer<GenericConnection, PubSubHeader> consumer = this.handlers[message.uType()];
if (consumer != null)
consumer.accept(conn, message);
else
LogManager
.info("[ProtocolAPI] Unhandled PubSub packet received id: " + message.uType());
}
@Override
public int messagesCount() {
return PubSubUnion.names.length;
}
public int createMessage(FlatBufferBuilder fbb, byte messageType, int messageOffset) {
int[] data = new int[1];
data[0] = PubSubHeader.createPubSubHeader(fbb, messageType, messageOffset);
int messages = MessageBundle.createPubSubMsgsVector(fbb, data);
MessageBundle.startMessageBundle(fbb);
MessageBundle.addPubSubMsgs(fbb, messages);
return MessageBundle.endMessageBundle(fbb);
}
}

View File

@@ -0,0 +1,170 @@
package dev.slimevr.protocol.pubsub
import com.google.flatbuffers.FlatBufferBuilder
import dev.slimevr.protocol.GenericConnection
import dev.slimevr.protocol.ProtocolAPI
import dev.slimevr.protocol.ProtocolAPIServer
import dev.slimevr.protocol.ProtocolHandler
import io.eiren.util.logging.LogManager
import solarxr_protocol.MessageBundle
import solarxr_protocol.pub_sub.Message
import solarxr_protocol.pub_sub.PubSubHeader
import solarxr_protocol.pub_sub.PubSubUnion
import solarxr_protocol.pub_sub.SubscriptionRequest
import solarxr_protocol.pub_sub.Topic
import solarxr_protocol.pub_sub.TopicHandle
import solarxr_protocol.pub_sub.TopicHandleRequest
import solarxr_protocol.pub_sub.TopicId
import solarxr_protocol.pub_sub.TopicIdT
import solarxr_protocol.pub_sub.TopicMapping
import java.util.concurrent.atomic.AtomicInteger
import java.util.function.Consumer
class PubSubHandler(private val api: ProtocolAPI) : ProtocolHandler<PubSubHeader>() {
// Two ways maps for faster reading when handling lots of packets
var topicsHandle: HashMap<HashedTopicId, Int> = HashMap()
var handleTopics: HashMap<Int, HashedTopicId> = HashMap()
var nextLocalHandle: AtomicInteger = AtomicInteger()
init {
registerPacketListener(PubSubUnion.SubscriptionRequest, ::onSubscriptionRequest)
registerPacketListener(PubSubUnion.TopicHandleRequest, ::onTopicHandleRequest)
registerPacketListener(PubSubUnion.Message, ::onTopicMessage)
}
private fun getTopicHandle(topicIdT: TopicIdT): Int {
val hashedTopicId = HashedTopicId(topicIdT)
var handleT = topicsHandle.get(hashedTopicId)
// if no handle exists for this topic id we create one and return it
// anyway
if (handleT == null) {
handleT = nextLocalHandle.incrementAndGet()
topicsHandle[hashedTopicId] = handleT
handleTopics[handleT] = hashedTopicId
}
return handleT
}
fun onSubscriptionRequest(conn: GenericConnection, messageHeader: PubSubHeader) {
val req =
messageHeader.u(SubscriptionRequest()) as SubscriptionRequest? ?: return
var subHandle = -1
if (req.topicType() == Topic.TopicHandle) {
val handle = req.topic(TopicHandle()) as TopicHandle?
if (handle != null && handleTopics.containsKey(handle.id())) subHandle = handle.id()
} else if (req.topicType() == Topic.TopicId) {
val topicId = req.topic(TopicId()) as TopicId?
if (topicId != null) subHandle = getTopicHandle(topicId.unpack())
}
assert(subHandle != -1)
val finalSubHandle = subHandle
val first = conn
.context
.subscribedTopics
.stream()
.filter { handle: Int -> handle == finalSubHandle }
.findFirst()
if (!first.isPresent) {
conn.context.subscribedTopics.add(finalSubHandle)
}
val fbb = FlatBufferBuilder(32)
val topicIdOffset = TopicId.pack(fbb, handleTopics.get(finalSubHandle)!!.inner)
val topicHandleOffset = TopicHandle.createTopicHandle(fbb, finalSubHandle)
val outbound = createMessage(
fbb,
PubSubUnion.TopicMapping,
TopicMapping.createTopicMapping(fbb, topicIdOffset, topicHandleOffset),
)
fbb.finish(outbound)
conn.send(fbb.dataBuffer())
}
fun onTopicHandleRequest(conn: GenericConnection, messageHeader: PubSubHeader) {
val req = messageHeader.u(TopicHandleRequest()) as TopicHandleRequest? ?: return
val topicRequest = req.unpack()
val handle = getTopicHandle(topicRequest.id)
val fbb = FlatBufferBuilder(32)
val topicIdOffset = TopicId.pack(fbb, topicRequest.id)
val topicHandleOffset = TopicHandle.createTopicHandle(fbb, handle)
val outbound = createMessage(
fbb,
PubSubUnion.TopicMapping,
TopicMapping.createTopicMapping(fbb, topicIdOffset, topicHandleOffset),
)
fbb.finish(outbound)
conn.send(fbb.dataBuffer())
}
fun onTopicMessage(c: GenericConnection, messageHeader: PubSubHeader) {
val req = messageHeader.u(Message()) as Message? ?: return
val messageT = req.unpack()
var subHandle = 1
if (messageT.topic.type == Topic.TopicHandle) {
subHandle = messageT.topic.asTopicHandle().id
} else if (messageT.topic.type == Topic.TopicId) {
subHandle = getTopicHandle(messageT.topic.asTopicId())
}
assert(subHandle != -1)
val finalSubHandle = subHandle
this.api.apiServers.forEach(
Consumer { server: ProtocolAPIServer ->
server.apiConnections.forEach { conn: GenericConnection ->
// Make sure that we are not sending a message to ourselves
// And check that the receiver has subscribed to the topic
if (conn.connectionId != c.connectionId &&
conn.context.subscribedTopics
.contains(finalSubHandle)
) {
val fbb = FlatBufferBuilder(32)
val outbound = createMessage(
fbb,
PubSubUnion.Message,
Message.pack(fbb, messageT),
)
fbb.finish(outbound)
conn.send(fbb.dataBuffer())
}
}
},
)
}
override fun onMessage(conn: GenericConnection, message: PubSubHeader) {
val consumer = this.handlers[message.uType().toInt()]
if (consumer != null) {
consumer.accept(conn, message)
} else {
LogManager
.info("[ProtocolAPI] Unhandled PubSub packet received id: " + message.uType())
}
}
override fun messagesCount(): Int = PubSubUnion.names.size
fun createMessage(fbb: FlatBufferBuilder, messageType: Byte, messageOffset: Int): Int {
val data = IntArray(1)
data[0] = PubSubHeader.createPubSubHeader(fbb, messageType, messageOffset)
val messages = MessageBundle.createPubSubMsgsVector(fbb, data)
MessageBundle.startMessageBundle(fbb)
MessageBundle.addPubSubMsgs(fbb, messages)
return MessageBundle.endMessageBundle(fbb)
}
}

View File

@@ -1,28 +0,0 @@
package dev.slimevr.protocol.rpc;
import com.google.flatbuffers.FlatBufferBuilder;
import dev.slimevr.tracking.processor.HumanPoseManager;
import dev.slimevr.tracking.processor.config.SkeletonConfigOffsets;
import solarxr_protocol.rpc.SkeletonConfigResponse;
import solarxr_protocol.rpc.SkeletonPart;
public class RPCBuilder {
public static int createSkeletonConfig(
FlatBufferBuilder fbb,
HumanPoseManager humanPoseManager
) {
int[] partsOffsets = new int[SkeletonConfigOffsets.values().length];
for (int index = 0; index < SkeletonConfigOffsets.values().length; index++) {
SkeletonConfigOffsets val = SkeletonConfigOffsets.values[index];
int part = SkeletonPart
.createSkeletonPart(fbb, val.id, humanPoseManager.getOffset(val));
partsOffsets[index] = part;
}
int parts = SkeletonConfigResponse.createSkeletonPartsVector(fbb, partsOffsets);
return SkeletonConfigResponse.createSkeletonConfigResponse(fbb, parts, 0);
}
}

View File

@@ -6,15 +6,15 @@ 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.datafeed.createTrackerId
import dev.slimevr.protocol.rpc.autobone.RPCAutoBoneHandler
import dev.slimevr.protocol.rpc.firmware.RPCFirmwareUpdateHandler
import dev.slimevr.protocol.rpc.games.vrchat.RPCVRChatHandler
import dev.slimevr.protocol.rpc.reset.RPCResetHandler
import dev.slimevr.protocol.rpc.serial.RPCProvisioningHandler
import dev.slimevr.protocol.rpc.serial.RPCSerialHandler
import dev.slimevr.protocol.rpc.settings.RPCSettingsBuilder
import dev.slimevr.protocol.rpc.settings.RPCSettingsHandler
import dev.slimevr.protocol.rpc.settings.createSettingsResponse
import dev.slimevr.protocol.rpc.setup.RPCHandshakeHandler
import dev.slimevr.protocol.rpc.setup.RPCTapSetupHandler
import dev.slimevr.protocol.rpc.setup.RPCUtil.getLocalIp
@@ -460,7 +460,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
}
val tracker = api.server.getTrackerById(req.trackerId().unpack()) ?: return
val trackerId = DataFeedBuilder.createTrackerId(fbb, tracker)
val trackerId = createTrackerId(fbb, tracker)
val response = MagToggleResponse.createMagToggleResponse(
fbb,
trackerId,
@@ -499,7 +499,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
// Don't apply magnetometer setting if use magnetometer global setting is not enabled
if (!api.server.configManager.vrConfig.server.useMagnetometerOnAllTrackers) {
val fbb = FlatBufferBuilder(32)
val trackerId = DataFeedBuilder.createTrackerId(fbb, tracker)
val trackerId = createTrackerId(fbb, tracker)
val response = MagToggleResponse.createMagToggleResponse(
fbb,
trackerId,
@@ -516,7 +516,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
}
val fbb = FlatBufferBuilder(32)
val trackerId = DataFeedBuilder.createTrackerId(fbb, tracker)
val trackerId = createTrackerId(fbb, tracker)
val response = MagToggleResponse.createMagToggleResponse(
fbb,
trackerId,
@@ -607,7 +607,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
fun sendSettingsChangedResponse(conn: GenericConnection, messageHeader: RpcMessageHeader?) {
val fbb = FlatBufferBuilder(32)
val settings = RPCSettingsBuilder.createSettingsResponse(fbb, api.server)
val settings = createSettingsResponse(fbb, api.server)
val outbound = createRPCMessage(fbb, RpcMessage.SettingsResponse, settings, messageHeader)
fbb.finish(outbound)
conn.send(fbb.dataBuffer())

View File

@@ -50,8 +50,8 @@ class RPCAutoBoneHandler(
) {
val req = messageHeader
.message(AutoBoneProcessRequest()) as AutoBoneProcessRequest
if (conn.context.useAutoBone()) return
conn.context.setUseAutoBone(true)
if (conn.context.useAutoBone) return
conn.context.useAutoBone = true
api.server
.autoBoneHandler
.startProcessByType(getById(req.processType()))
@@ -67,7 +67,7 @@ class RPCAutoBoneHandler(
success: Boolean,
) {
forAllListeners { conn ->
if (!conn.context.useAutoBone()) {
if (!conn.context.useAutoBone) {
return@forAllListeners
}
@@ -95,7 +95,7 @@ class RPCAutoBoneHandler(
fbb.finish(outbound)
conn.send(fbb.dataBuffer())
if (completed) {
conn.context.setUseAutoBone(false)
conn.context.useAutoBone = false
}
}
}
@@ -106,7 +106,7 @@ class RPCAutoBoneHandler(
override fun onAutoBoneEpoch(epoch: Epoch) {
forAllListeners { conn ->
if (!conn.context.useAutoBone()) {
if (!conn.context.useAutoBone) {
return@forAllListeners
}

View File

@@ -108,11 +108,11 @@ class RPCResetHandler(var rpcHandler: RPCHandler, var api: ProtocolAPI) : ResetL
fun forAllListeners(action: Consumer<in GenericConnection?>?) {
this.api
.getAPIServers()
.apiServers
.forEach(
Consumer { server: ProtocolAPIServer? ->
server!!
.getAPIConnections()
Consumer { server: ProtocolAPIServer ->
server
.apiConnections
.forEach(action)
},
)

View File

@@ -1,87 +0,0 @@
package dev.slimevr.protocol.rpc.serial;
import com.google.flatbuffers.FlatBufferBuilder;
import dev.slimevr.protocol.GenericConnection;
import dev.slimevr.protocol.ProtocolAPI;
import dev.slimevr.protocol.rpc.RPCHandler;
import dev.slimevr.serial.ProvisioningListener;
import dev.slimevr.serial.ProvisioningStatus;
import dev.slimevr.serial.SerialPort;
import solarxr_protocol.rpc.*;
import java.util.function.Consumer;
public class RPCProvisioningHandler implements ProvisioningListener {
public RPCHandler rpcHandler;
public ProtocolAPI api;
public RPCProvisioningHandler(RPCHandler rpcHandler, ProtocolAPI api) {
this.rpcHandler = rpcHandler;
this.api = api;
rpcHandler
.registerPacketListener(
RpcMessage.StartWifiProvisioningRequest,
this::onStartWifiProvisioningRequest
);
rpcHandler
.registerPacketListener(
RpcMessage.StopWifiProvisioningRequest,
this::onStopWifiProvisioningRequest
);
this.api.server.provisioningHandler.addListener(this);
}
public void onStartWifiProvisioningRequest(
GenericConnection conn,
RpcMessageHeader messageHeader
) {
StartWifiProvisioningRequest req = (StartWifiProvisioningRequest) messageHeader
.message(new StartWifiProvisioningRequest());
if (req == null)
return;
this.api.server.provisioningHandler.start(req.ssid(), req.password(), req.port());
conn.getContext().setUseProvisioning(true);
}
public void onStopWifiProvisioningRequest(
GenericConnection conn,
RpcMessageHeader messageHeader
) {
StopWifiProvisioningRequest req = (StopWifiProvisioningRequest) messageHeader
.message(new StopWifiProvisioningRequest());
if (req == null)
return;
conn.getContext().setUseProvisioning(false);
this.api.server.provisioningHandler.stop();
}
@Override
public void onProvisioningStatusChange(ProvisioningStatus status, SerialPort port) {
FlatBufferBuilder fbb = new FlatBufferBuilder(32);
WifiProvisioningStatusResponse.startWifiProvisioningStatusResponse(fbb);
WifiProvisioningStatusResponse.addStatus(fbb, status.id);
int update = WifiProvisioningStatusResponse.endWifiProvisioningStatusResponse(fbb);
int outbound = rpcHandler
.createRPCMessage(fbb, RpcMessage.WifiProvisioningStatusResponse, update);
fbb.finish(outbound);
this.forAllListeners((conn) -> conn.send(fbb.dataBuffer()));
}
private void forAllListeners(Consumer<? super GenericConnection> action) {
this.api
.getAPIServers()
.forEach(
(server) -> server
.getAPIConnections()
.filter(conn -> conn.getContext().useProvisioning())
.forEach(action)
);
}
}

View File

@@ -0,0 +1,68 @@
package dev.slimevr.protocol.rpc.serial
import com.google.flatbuffers.FlatBufferBuilder
import dev.slimevr.protocol.GenericConnection
import dev.slimevr.protocol.ProtocolAPI
import dev.slimevr.protocol.ProtocolAPIServer
import dev.slimevr.protocol.rpc.RPCHandler
import dev.slimevr.serial.ProvisioningListener
import dev.slimevr.serial.ProvisioningStatus
import dev.slimevr.serial.SerialPort
import solarxr_protocol.rpc.*
import java.util.function.Consumer
class RPCProvisioningHandler(var rpcHandler: RPCHandler, var api: ProtocolAPI) : ProvisioningListener {
init {
rpcHandler.registerPacketListener(RpcMessage.StartWifiProvisioningRequest, ::onStartWifiProvisioningRequest)
rpcHandler.registerPacketListener(RpcMessage.StopWifiProvisioningRequest, ::onStopWifiProvisioningRequest)
this.api.server.provisioningHandler.addListener(this)
}
fun onStartWifiProvisioningRequest(
conn: GenericConnection,
messageHeader: RpcMessageHeader,
) {
val req = messageHeader
.message(StartWifiProvisioningRequest()) as StartWifiProvisioningRequest?
if (req == null) return
this.api.server.provisioningHandler.start(req.ssid(), req.password(), req.port())
conn.context.useProvisioning = true
}
fun onStopWifiProvisioningRequest(
conn: GenericConnection,
messageHeader: RpcMessageHeader,
) {
val req = messageHeader
.message(StopWifiProvisioningRequest()) as StopWifiProvisioningRequest?
if (req == null) return
conn.context.useProvisioning = false
this.api.server.provisioningHandler.stop()
}
override fun onProvisioningStatusChange(status: ProvisioningStatus, port: SerialPort?) {
val fbb = FlatBufferBuilder(32)
WifiProvisioningStatusResponse.startWifiProvisioningStatusResponse(fbb)
WifiProvisioningStatusResponse.addStatus(fbb, status.id)
val update = WifiProvisioningStatusResponse.endWifiProvisioningStatusResponse(fbb)
val outbound = rpcHandler
.createRPCMessage(fbb, RpcMessage.WifiProvisioningStatusResponse, update)
fbb.finish(outbound)
this.forAllListeners(Consumer { conn: GenericConnection -> conn.send(fbb.dataBuffer()) })
}
private fun forAllListeners(action: Consumer<in GenericConnection?>?) {
this.api
.apiServers
.forEach(
Consumer { server: ProtocolAPIServer ->
server
.apiConnections
.filter { conn: GenericConnection -> conn.context.useProvisioning }
.forEach(action)
},
)
}
}

View File

@@ -1,303 +0,0 @@
package dev.slimevr.protocol.rpc.serial;
import com.google.flatbuffers.FlatBufferBuilder;
import dev.slimevr.protocol.GenericConnection;
import dev.slimevr.protocol.ProtocolAPI;
import dev.slimevr.protocol.rpc.RPCHandler;
import dev.slimevr.serial.SerialListener;
import dev.slimevr.serial.SerialPort;
import io.eiren.util.logging.LogManager;
import org.jetbrains.annotations.NotNull;
import solarxr_protocol.rpc.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
public class RPCSerialHandler implements SerialListener {
public RPCHandler rpcHandler;
public ProtocolAPI api;
public RPCSerialHandler(RPCHandler rpcHandler, ProtocolAPI api) {
this.rpcHandler = rpcHandler;
this.api = api;
rpcHandler
.registerPacketListener(
RpcMessage.SerialTrackerRebootRequest,
this::onSerialTrackerRebootRequest
);
rpcHandler
.registerPacketListener(
RpcMessage.SerialTrackerGetInfoRequest,
this::onSerialTrackerGetInfoRequest
);
rpcHandler
.registerPacketListener(
RpcMessage.SerialTrackerFactoryResetRequest,
this::onSerialTrackerFactoryResetRequest
);
rpcHandler
.registerPacketListener(
RpcMessage.SerialTrackerGetWifiScanRequest,
this::onSerialTrackerGetWifiScanRequest
);
rpcHandler
.registerPacketListener(
RpcMessage.SerialTrackerCustomCommandRequest,
this::onSerialTrackerCustomCommandRequest
);
rpcHandler.registerPacketListener(RpcMessage.SetWifiRequest, this::onSetWifiRequest);
rpcHandler.registerPacketListener(RpcMessage.OpenSerialRequest, this::onOpenSerialRequest);
rpcHandler
.registerPacketListener(RpcMessage.CloseSerialRequest, this::onCloseSerialRequest);
rpcHandler
.registerPacketListener(RpcMessage.SerialDevicesRequest, this::onRequestSerialDevices);
this.api.server.serialHandler.addListener(this);
}
@Override
public void onSerialDisconnected() {
FlatBufferBuilder fbb = new FlatBufferBuilder(32);
SerialUpdateResponse.startSerialUpdateResponse(fbb);
SerialUpdateResponse.addClosed(fbb, true);
int update = SerialUpdateResponse.endSerialUpdateResponse(fbb);
int outbound = rpcHandler.createRPCMessage(fbb, RpcMessage.SerialUpdateResponse, update);
fbb.finish(outbound);
this.forAllListeners((conn) -> {
conn.send(fbb.dataBuffer());
conn.getContext().setUseSerial(false);
});
}
@Override
public void onSerialLog(String str, boolean server) {
FlatBufferBuilder fbb = new FlatBufferBuilder(32);
int logOffset = fbb.createString(str);
SerialUpdateResponse.startSerialUpdateResponse(fbb);
SerialUpdateResponse.addLog(fbb, logOffset);
int update = SerialUpdateResponse.endSerialUpdateResponse(fbb);
int outbound = rpcHandler.createRPCMessage(fbb, RpcMessage.SerialUpdateResponse, update);
fbb.finish(outbound);
this.forAllListeners((conn) -> {
conn.send(fbb.dataBuffer());
});
}
@Override
public void onNewSerialDevice(SerialPort port) {
FlatBufferBuilder fbb = new FlatBufferBuilder(32);
int portOffset = fbb.createString(port.getPortLocation());
int nameOffset = fbb.createString(port.getDescriptivePortName());
int deviceOffset = SerialDevice.createSerialDevice(fbb, portOffset, nameOffset);
int newSerialOffset = NewSerialDeviceResponse
.createNewSerialDeviceResponse(fbb, deviceOffset);
int outbound = rpcHandler
.createRPCMessage(fbb, RpcMessage.NewSerialDeviceResponse, newSerialOffset);
fbb.finish(outbound);
this.api
.getAPIServers()
.forEach(
(server) -> server
.getAPIConnections()
.forEach((conn) -> {
conn.send(fbb.dataBuffer());
})
);
}
@Override
public void onSerialConnected(SerialPort port) {
FlatBufferBuilder fbb = new FlatBufferBuilder(32);
SerialUpdateResponse.startSerialUpdateResponse(fbb);
SerialUpdateResponse.addClosed(fbb, false);
int update = SerialUpdateResponse.endSerialUpdateResponse(fbb);
int outbound = rpcHandler.createRPCMessage(fbb, RpcMessage.SerialUpdateResponse, update);
fbb.finish(outbound);
this.forAllListeners((conn) -> {
conn.send(fbb.dataBuffer());
});
}
public void onSerialTrackerRebootRequest(
GenericConnection conn,
RpcMessageHeader messageHeader
) {
SerialTrackerRebootRequest req = (SerialTrackerRebootRequest) messageHeader
.message(new SerialTrackerRebootRequest());
if (req == null)
return;
this.api.server.serialHandler.rebootRequest();
}
public void onSerialTrackerGetInfoRequest(
GenericConnection conn,
RpcMessageHeader messageHeader
) {
SerialTrackerGetInfoRequest req = (SerialTrackerGetInfoRequest) messageHeader
.message(new SerialTrackerGetInfoRequest());
if (req == null)
return;
this.api.server.serialHandler.infoRequest();
}
public void onSerialTrackerFactoryResetRequest(
GenericConnection conn,
RpcMessageHeader messageHeader
) {
SerialTrackerFactoryResetRequest req = (SerialTrackerFactoryResetRequest) messageHeader
.message(new SerialTrackerFactoryResetRequest());
if (req == null)
return;
this.api.server.serialHandler.factoryResetRequest();
}
public void onSerialTrackerGetWifiScanRequest(
GenericConnection conn,
RpcMessageHeader messageHeader
) {
SerialTrackerGetWifiScanRequest req = (SerialTrackerGetWifiScanRequest) messageHeader
.message(new SerialTrackerGetWifiScanRequest());
if (req == null)
return;
this.api.server.serialHandler.wifiScanRequest();
}
public void onSerialTrackerCustomCommandRequest(
GenericConnection conn,
RpcMessageHeader messageHeader
) {
SerialTrackerCustomCommandRequest req = (SerialTrackerCustomCommandRequest) messageHeader
.message(new SerialTrackerCustomCommandRequest());
if (req == null || req.command() == null)
return;
this.api.server.serialHandler.customCommandRequest(Objects.requireNonNull(req.command()));
}
private void onRequestSerialDevices(GenericConnection conn, RpcMessageHeader messageHeader) {
SerialDevicesRequest req = (SerialDevicesRequest) messageHeader
.message(new SerialDevicesRequest());
if (req == null)
return;
FlatBufferBuilder fbb = new FlatBufferBuilder(32);
List<Integer> devicesOffsets = new ArrayList<>();
try {
this.api.server.serialHandler.getKnownPorts().forEach((port) -> {
int portOffset = fbb.createString(port.getPortLocation());
int nameOffset = fbb.createString(port.getDescriptivePortName());
devicesOffsets.add(SerialDevice.createSerialDevice(fbb, portOffset, nameOffset));
});
} catch (Throwable e) {
LogManager.severe("Using serial ports is not supported on this platform", e);
}
SerialDevicesResponse.startDevicesVector(fbb, devicesOffsets.size());
devicesOffsets.forEach(offset -> SerialDevicesResponse.addDevices(fbb, offset));
int devices = fbb.endVector();
int serialDeviceOffsets = SerialDevicesResponse.createSerialDevicesResponse(fbb, devices);
int outbound = rpcHandler
.createRPCMessage(fbb, RpcMessage.SerialDevicesResponse, serialDeviceOffsets);
fbb.finish(outbound);
conn.send(fbb.dataBuffer());
}
public void onSetWifiRequest(GenericConnection conn, RpcMessageHeader messageHeader) {
SetWifiRequest req = (SetWifiRequest) messageHeader.message(new SetWifiRequest());
if (req == null)
return;
if (
req.password() == null
|| req.ssid() == null
|| !this.api.server.serialHandler.isConnected()
)
return;
this.api.server.serialHandler.setWifi(req.ssid(), req.password());
}
public void onOpenSerialRequest(GenericConnection conn, RpcMessageHeader messageHeader) {
OpenSerialRequest req = (OpenSerialRequest) messageHeader.message(new OpenSerialRequest());
if (req == null)
return;
conn.getContext().setUseSerial(true);
this.api.server.queueTask(() -> {
try {
this.api.server.serialHandler.openSerial(req.port(), req.auto());
} catch (Exception e) {
LogManager.severe("Unable to open serial port", e);
} catch (Throwable e) {
LogManager.severe("Using serial ports is not supported on this platform", e);
}
FlatBufferBuilder fbb = new FlatBufferBuilder(32);
SerialUpdateResponse.startSerialUpdateResponse(fbb);
SerialUpdateResponse.addClosed(fbb, !this.api.server.serialHandler.isConnected());
int update = SerialUpdateResponse.endSerialUpdateResponse(fbb);
int outbound = rpcHandler
.createRPCMessage(fbb, RpcMessage.SerialUpdateResponse, update);
fbb.finish(outbound);
conn.send(fbb.dataBuffer());
});
}
public void onCloseSerialRequest(GenericConnection conn, RpcMessageHeader messageHeader) {
CloseSerialRequest req = (CloseSerialRequest) messageHeader
.message(new CloseSerialRequest());
if (req == null)
return;
conn.getContext().setUseSerial(false);
this.api.server.serialHandler.closeSerial();
FlatBufferBuilder fbb = new FlatBufferBuilder(32);
SerialUpdateResponse.startSerialUpdateResponse(fbb);
SerialUpdateResponse.addClosed(fbb, !this.api.server.serialHandler.isConnected());
int update = SerialUpdateResponse.endSerialUpdateResponse(fbb);
int outbound = rpcHandler.createRPCMessage(fbb, RpcMessage.SerialUpdateResponse, update);
fbb.finish(outbound);
conn.send(fbb.dataBuffer());
}
public void forAllListeners(Consumer<? super GenericConnection> action) {
this.api
.getAPIServers()
.forEach(
(server) -> server
.getAPIConnections()
.filter(conn -> conn.getContext().useSerial())
.forEach(action)
);
}
@Override
public void onSerialDeviceDeleted(@NotNull SerialPort port) {
}
}

View File

@@ -0,0 +1,263 @@
package dev.slimevr.protocol.rpc.serial
import com.google.flatbuffers.FlatBufferBuilder
import dev.slimevr.protocol.GenericConnection
import dev.slimevr.protocol.ProtocolAPI
import dev.slimevr.protocol.ProtocolAPIServer
import dev.slimevr.protocol.rpc.RPCHandler
import dev.slimevr.serial.SerialListener
import dev.slimevr.serial.SerialPort
import io.eiren.util.logging.LogManager
import solarxr_protocol.rpc.*
import java.util.*
import java.util.function.Consumer
class RPCSerialHandler(var rpcHandler: RPCHandler, var api: ProtocolAPI) : SerialListener {
init {
rpcHandler.registerPacketListener(RpcMessage.SerialTrackerRebootRequest, ::onSerialTrackerRebootRequest)
rpcHandler.registerPacketListener(RpcMessage.SerialTrackerGetInfoRequest, ::onSerialTrackerGetInfoRequest)
rpcHandler.registerPacketListener(RpcMessage.SerialTrackerFactoryResetRequest, ::onSerialTrackerFactoryResetRequest)
rpcHandler.registerPacketListener(RpcMessage.SerialTrackerGetWifiScanRequest, ::onSerialTrackerGetWifiScanRequest)
rpcHandler.registerPacketListener(RpcMessage.SerialTrackerCustomCommandRequest, ::onSerialTrackerCustomCommandRequest)
rpcHandler.registerPacketListener(RpcMessage.SetWifiRequest, ::onSetWifiRequest)
rpcHandler.registerPacketListener(RpcMessage.OpenSerialRequest, ::onOpenSerialRequest)
rpcHandler.registerPacketListener(RpcMessage.CloseSerialRequest, ::onCloseSerialRequest)
rpcHandler.registerPacketListener(RpcMessage.SerialDevicesRequest, ::onRequestSerialDevices)
this.api.server.serialHandler.addListener(this)
}
override fun onSerialDisconnected() {
val fbb = FlatBufferBuilder(32)
SerialUpdateResponse.startSerialUpdateResponse(fbb)
SerialUpdateResponse.addClosed(fbb, true)
val update = SerialUpdateResponse.endSerialUpdateResponse(fbb)
val outbound = rpcHandler.createRPCMessage(fbb, RpcMessage.SerialUpdateResponse, update)
fbb.finish(outbound)
this.forAllListeners(
Consumer { conn: GenericConnection ->
conn.send(fbb.dataBuffer())
conn.context.useSerial = false
},
)
}
override fun onSerialLog(str: String, server: Boolean) {
val fbb = FlatBufferBuilder(32)
val logOffset = fbb.createString(str)
SerialUpdateResponse.startSerialUpdateResponse(fbb)
SerialUpdateResponse.addLog(fbb, logOffset)
val update = SerialUpdateResponse.endSerialUpdateResponse(fbb)
val outbound = rpcHandler.createRPCMessage(fbb, RpcMessage.SerialUpdateResponse, update)
fbb.finish(outbound)
this.forAllListeners(
Consumer { conn: GenericConnection ->
conn.send(fbb.dataBuffer())
},
)
}
override fun onNewSerialDevice(port: SerialPort) {
val fbb = FlatBufferBuilder(32)
val portOffset = fbb.createString(port.portLocation)
val nameOffset = fbb.createString(port.descriptivePortName)
val deviceOffset = SerialDevice.createSerialDevice(fbb, portOffset, nameOffset)
val newSerialOffset = NewSerialDeviceResponse
.createNewSerialDeviceResponse(fbb, deviceOffset)
val outbound = rpcHandler
.createRPCMessage(fbb, RpcMessage.NewSerialDeviceResponse, newSerialOffset)
fbb.finish(outbound)
this.api
.apiServers
.forEach(
Consumer { server: ProtocolAPIServer ->
server
.apiConnections
.forEach { conn: GenericConnection ->
conn.send(fbb.dataBuffer())
}
},
)
}
override fun onSerialConnected(port: SerialPort) {
val fbb = FlatBufferBuilder(32)
SerialUpdateResponse.startSerialUpdateResponse(fbb)
SerialUpdateResponse.addClosed(fbb, false)
val update = SerialUpdateResponse.endSerialUpdateResponse(fbb)
val outbound = rpcHandler.createRPCMessage(fbb, RpcMessage.SerialUpdateResponse, update)
fbb.finish(outbound)
this.forAllListeners(
Consumer { conn: GenericConnection ->
conn.send(fbb.dataBuffer())
},
)
}
fun onSerialTrackerRebootRequest(
conn: GenericConnection,
messageHeader: RpcMessageHeader,
) {
val req = messageHeader
.message(SerialTrackerRebootRequest()) as SerialTrackerRebootRequest?
if (req == null) return
this.api.server.serialHandler.rebootRequest()
}
fun onSerialTrackerGetInfoRequest(
conn: GenericConnection,
messageHeader: RpcMessageHeader,
) {
val req = messageHeader
.message(SerialTrackerGetInfoRequest()) as SerialTrackerGetInfoRequest?
if (req == null) return
this.api.server.serialHandler.infoRequest()
}
fun onSerialTrackerFactoryResetRequest(
conn: GenericConnection,
messageHeader: RpcMessageHeader,
) {
val req = messageHeader
.message(SerialTrackerFactoryResetRequest()) as SerialTrackerFactoryResetRequest?
if (req == null) return
this.api.server.serialHandler.factoryResetRequest()
}
fun onSerialTrackerGetWifiScanRequest(
conn: GenericConnection,
messageHeader: RpcMessageHeader,
) {
val req = messageHeader
.message(SerialTrackerGetWifiScanRequest()) as SerialTrackerGetWifiScanRequest?
if (req == null) return
this.api.server.serialHandler.wifiScanRequest()
}
fun onSerialTrackerCustomCommandRequest(
conn: GenericConnection,
messageHeader: RpcMessageHeader,
) {
val req = messageHeader
.message(SerialTrackerCustomCommandRequest()) as SerialTrackerCustomCommandRequest?
if (req == null || req.command() == null) return
this.api.server.serialHandler.customCommandRequest(Objects.requireNonNull(req.command()))
}
private fun onRequestSerialDevices(conn: GenericConnection, messageHeader: RpcMessageHeader) {
val req = messageHeader
.message(SerialDevicesRequest()) as SerialDevicesRequest?
if (req == null) return
val fbb = FlatBufferBuilder(32)
val devicesOffsets: MutableList<Int> = ArrayList()
try {
this.api.server.serialHandler.knownPorts.forEach { port: SerialPort ->
val portOffset = fbb.createString(port.portLocation)
val nameOffset = fbb.createString(port.descriptivePortName)
devicesOffsets.add(SerialDevice.createSerialDevice(fbb, portOffset, nameOffset))
}
} catch (e: Throwable) {
LogManager.severe("Using serial ports is not supported on this platform", e)
}
SerialDevicesResponse.startDevicesVector(fbb, devicesOffsets.size)
devicesOffsets.forEach(Consumer { offset: Int -> SerialDevicesResponse.addDevices(fbb, offset) })
val devices = fbb.endVector()
val serialDeviceOffsets = SerialDevicesResponse.createSerialDevicesResponse(fbb, devices)
val outbound = rpcHandler
.createRPCMessage(fbb, RpcMessage.SerialDevicesResponse, serialDeviceOffsets)
fbb.finish(outbound)
conn.send(fbb.dataBuffer())
}
fun onSetWifiRequest(conn: GenericConnection, messageHeader: RpcMessageHeader) {
val req = messageHeader.message(SetWifiRequest()) as SetWifiRequest? ?: return
if (req.password() == null || req.ssid() == null || !this.api.server.serialHandler.isConnected) {
return
}
this.api.server.serialHandler.setWifi(req.ssid(), req.password())
}
fun onOpenSerialRequest(conn: GenericConnection, messageHeader: RpcMessageHeader) {
val req =
messageHeader.message(OpenSerialRequest()) as OpenSerialRequest? ?: return
conn.context.useSerial = true
this.api.server.queueTask {
try {
this.api.server.serialHandler.openSerial(req.port(), req.auto())
} catch (e: Exception) {
LogManager.severe("Unable to open serial port", e)
} catch (e: Throwable) {
LogManager.severe(
"Using serial ports is not supported on this platform",
e,
)
}
val fbb = FlatBufferBuilder(32)
SerialUpdateResponse.startSerialUpdateResponse(fbb)
SerialUpdateResponse.addClosed(
fbb,
!this.api.server.serialHandler.isConnected,
)
val update = SerialUpdateResponse.endSerialUpdateResponse(fbb)
val outbound = rpcHandler
.createRPCMessage(fbb, RpcMessage.SerialUpdateResponse, update)
fbb.finish(outbound)
conn.send(fbb.dataBuffer())
}
}
fun onCloseSerialRequest(conn: GenericConnection, messageHeader: RpcMessageHeader) {
val req = messageHeader
.message(CloseSerialRequest()) as CloseSerialRequest?
if (req == null) return
conn.context.useSerial = false
this.api.server.serialHandler.closeSerial()
val fbb = FlatBufferBuilder(32)
SerialUpdateResponse.startSerialUpdateResponse(fbb)
SerialUpdateResponse.addClosed(fbb, !this.api.server.serialHandler.isConnected)
val update = SerialUpdateResponse.endSerialUpdateResponse(fbb)
val outbound = rpcHandler.createRPCMessage(fbb, RpcMessage.SerialUpdateResponse, update)
fbb.finish(outbound)
conn.send(fbb.dataBuffer())
}
fun forAllListeners(action: Consumer<in GenericConnection?>?) {
this.api
.apiServers
.forEach(
Consumer { server: ProtocolAPIServer ->
server
.apiConnections
.filter { conn: GenericConnection -> conn.context.useSerial }
.forEach(action)
},
)
}
override fun onSerialDeviceDeleted(port: SerialPort) {
}
}

View File

@@ -1,423 +0,0 @@
package dev.slimevr.protocol.rpc.settings;
import com.google.flatbuffers.FlatBufferBuilder;
import dev.slimevr.VRServer;
import dev.slimevr.bridge.ISteamVRBridge;
import dev.slimevr.config.*;
import dev.slimevr.filtering.TrackerFilters;
import dev.slimevr.tracking.processor.HumanPoseManager;
import dev.slimevr.tracking.processor.config.SkeletonConfigToggles;
import dev.slimevr.tracking.processor.config.SkeletonConfigValues;
import dev.slimevr.tracking.trackers.TrackerRole;
import solarxr_protocol.rpc.*;
import solarxr_protocol.rpc.settings.*;
public class RPCSettingsBuilder {
public static int createOSCRouterSettings(
FlatBufferBuilder fbb,
OSCConfig config
) {
int addressStringOffset = fbb.createString(config.getAddress());
int oscSettingOffset = OSCSettings
.createOSCSettings(
fbb,
config.getEnabled(),
config.getPortIn(),
config.getPortOut(),
addressStringOffset
);
OSCRouterSettings.startOSCRouterSettings(fbb);
OSCRouterSettings.addOscSettings(fbb, oscSettingOffset);
return OSCRouterSettings.endOSCRouterSettings(fbb);
}
public static int createVRCOSCSettings(
FlatBufferBuilder fbb,
VRCOSCConfig config
) {
int addressStringOffset = fbb.createString(config.getAddress());
int generalSettingOffset = OSCSettings
.createOSCSettings(
fbb,
config.getEnabled(),
config.getPortIn(),
config.getPortOut(),
addressStringOffset
);
int oscSettingOffset = OSCTrackersSetting
.createOSCTrackersSetting(
fbb,
config.getOSCTrackerRole(TrackerRole.HEAD, false),
config.getOSCTrackerRole(TrackerRole.CHEST, false),
config.getOSCTrackerRole(TrackerRole.WAIST, false),
config.getOSCTrackerRole(TrackerRole.LEFT_KNEE, false)
&& config.getOSCTrackerRole(TrackerRole.RIGHT_KNEE, false),
config.getOSCTrackerRole(TrackerRole.LEFT_FOOT, false)
&& config.getOSCTrackerRole(TrackerRole.RIGHT_FOOT, false),
config.getOSCTrackerRole(TrackerRole.LEFT_ELBOW, false)
&& config.getOSCTrackerRole(TrackerRole.RIGHT_ELBOW, false),
config.getOSCTrackerRole(TrackerRole.LEFT_HAND, false)
&& config.getOSCTrackerRole(TrackerRole.RIGHT_HAND, false)
);
VRCOSCSettings.startVRCOSCSettings(fbb);
VRCOSCSettings.addOscSettings(fbb, generalSettingOffset);
VRCOSCSettings.addTrackers(fbb, oscSettingOffset);
VRCOSCSettings.addOscqueryEnabled(fbb, config.getOscqueryEnabled());
return VRCOSCSettings.endVRCOSCSettings(fbb);
}
public static int createVMCOSCSettings(
FlatBufferBuilder fbb,
VMCConfig config
) {
int addressStringOffset = fbb.createString(config.getAddress());
int generalSettingOffset = OSCSettings
.createOSCSettings(
fbb,
config.getEnabled(),
config.getPortIn(),
config.getPortOut(),
addressStringOffset
);
String vrmJson = config.getVrmJson();
int vrmJsonOffset = 0;
if (vrmJson != null)
vrmJsonOffset = fbb.createString(vrmJson);
VMCOSCSettings.startVMCOSCSettings(fbb);
VMCOSCSettings.addOscSettings(fbb, generalSettingOffset);
if (vrmJson != null)
VMCOSCSettings.addVrmJson(fbb, vrmJsonOffset);
VMCOSCSettings.addAnchorHip(fbb, config.getAnchorHip());
VMCOSCSettings.addMirrorTracking(fbb, config.getMirrorTracking());
return VMCOSCSettings.endVMCOSCSettings(fbb);
}
public static int createFilterSettings(
FlatBufferBuilder fbb,
FiltersConfig filtersConfig
) {
return FilteringSettings
.createFilteringSettings(
fbb,
TrackerFilters.getByConfigkey(filtersConfig.getType()).getId(),
filtersConfig.getAmount()
);
}
public static int createDriftCompensationSettings(
FlatBufferBuilder fbb,
DriftCompensationConfig driftCompensationConfig
) {
return DriftCompensationSettings
.createDriftCompensationSettings(
fbb,
driftCompensationConfig.getEnabled(),
driftCompensationConfig.getPrediction(),
driftCompensationConfig.getAmount(),
driftCompensationConfig.getMaxResets()
);
}
public static int createTapDetectionSettings(
FlatBufferBuilder fbb,
TapDetectionConfig tapDetectionConfig
) {
return TapDetectionSettings
.createTapDetectionSettings(
fbb,
tapDetectionConfig.getFullResetDelay(),
tapDetectionConfig.getFullResetEnabled(),
tapDetectionConfig.getFullResetTaps(),
tapDetectionConfig.getYawResetDelay(),
tapDetectionConfig.getYawResetEnabled(),
tapDetectionConfig.getYawResetTaps(),
tapDetectionConfig.getMountingResetDelay(),
tapDetectionConfig.getMountingResetEnabled(),
tapDetectionConfig.getMountingResetTaps(),
tapDetectionConfig.getSetupMode(),
tapDetectionConfig.getNumberTrackersOverThreshold()
);
}
public static int createSteamVRSettings(FlatBufferBuilder fbb, ISteamVRBridge bridge) {
int steamvrTrackerSettings = 0;
if (bridge != null) {
steamvrTrackerSettings = SteamVRTrackersSetting
.createSteamVRTrackersSetting(
fbb,
bridge.getShareSetting(TrackerRole.WAIST),
bridge.getShareSetting(TrackerRole.CHEST),
bridge.getAutomaticSharedTrackers(),
bridge.getShareSetting(TrackerRole.LEFT_FOOT),
bridge.getShareSetting(TrackerRole.RIGHT_FOOT),
bridge.getShareSetting(TrackerRole.LEFT_KNEE),
bridge.getShareSetting(TrackerRole.RIGHT_KNEE),
bridge.getShareSetting(TrackerRole.LEFT_ELBOW),
bridge.getShareSetting(TrackerRole.RIGHT_ELBOW),
bridge.getShareSetting(TrackerRole.LEFT_HAND),
bridge.getShareSetting(TrackerRole.RIGHT_HAND)
);
}
return steamvrTrackerSettings;
}
public static int createModelSettings(
FlatBufferBuilder fbb,
HumanPoseManager humanPoseManager,
LegTweaksConfig legTweaksConfig,
SkeletonConfig skeletonConfig
) {
int togglesOffset = ModelToggles
.createModelToggles(
fbb,
humanPoseManager.getToggle(SkeletonConfigToggles.EXTENDED_SPINE_MODEL),
humanPoseManager.getToggle(SkeletonConfigToggles.EXTENDED_PELVIS_MODEL),
humanPoseManager.getToggle(SkeletonConfigToggles.EXTENDED_KNEE_MODEL),
humanPoseManager.getToggle(SkeletonConfigToggles.FORCE_ARMS_FROM_HMD),
humanPoseManager.getToggle(SkeletonConfigToggles.FLOOR_CLIP),
humanPoseManager.getToggle(SkeletonConfigToggles.SKATING_CORRECTION),
humanPoseManager.getToggle(SkeletonConfigToggles.TOE_SNAP),
humanPoseManager.getToggle(SkeletonConfigToggles.FOOT_PLANT),
humanPoseManager.getToggle(SkeletonConfigToggles.SELF_LOCALIZATION),
humanPoseManager.getToggle(SkeletonConfigToggles.USE_POSITION),
humanPoseManager.getToggle(SkeletonConfigToggles.ENFORCE_CONSTRAINTS),
humanPoseManager.getToggle(SkeletonConfigToggles.CORRECT_CONSTRAINTS)
);
int ratiosOffset = ModelRatios
.createModelRatios(
fbb,
humanPoseManager.getValue(SkeletonConfigValues.WAIST_FROM_CHEST_HIP_AVERAGING),
humanPoseManager.getValue(SkeletonConfigValues.WAIST_FROM_CHEST_LEGS_AVERAGING),
humanPoseManager.getValue(SkeletonConfigValues.HIP_FROM_CHEST_LEGS_AVERAGING),
humanPoseManager.getValue(SkeletonConfigValues.HIP_FROM_WAIST_LEGS_AVERAGING),
humanPoseManager.getValue(SkeletonConfigValues.HIP_LEGS_AVERAGING),
humanPoseManager.getValue(SkeletonConfigValues.KNEE_TRACKER_ANKLE_AVERAGING),
humanPoseManager.getValue(SkeletonConfigValues.KNEE_ANKLE_AVERAGING)
);
int legTweaksOffset = LegTweaksSettings
.createLegTweaksSettings(
fbb,
legTweaksConfig.getCorrectionStrength()
);
int skeletonConfigOffset = SkeletonHeight
.createSkeletonHeight(
fbb,
skeletonConfig.getHmdHeight(),
skeletonConfig.getFloorHeight()
);
return ModelSettings
.createModelSettings(
fbb,
togglesOffset,
ratiosOffset,
legTweaksOffset,
skeletonConfigOffset
);
}
public static int createAutoBoneSettings(FlatBufferBuilder fbb, AutoBoneConfig autoBoneConfig) {
return AutoBoneSettings
.createAutoBoneSettings(
fbb,
autoBoneConfig.getCursorIncrement(),
autoBoneConfig.getMinDataDistance(),
autoBoneConfig.getMaxDataDistance(),
autoBoneConfig.getNumEpochs(),
autoBoneConfig.getPrintEveryNumEpochs(),
autoBoneConfig.getInitialAdjustRate(),
autoBoneConfig.getAdjustRateDecay(),
autoBoneConfig.getSlideErrorFactor(),
autoBoneConfig.getOffsetSlideErrorFactor(),
autoBoneConfig.getFootHeightOffsetErrorFactor(),
autoBoneConfig.getBodyProportionErrorFactor(),
autoBoneConfig.getHeightErrorFactor(),
autoBoneConfig.getPositionErrorFactor(),
autoBoneConfig.getPositionOffsetErrorFactor(),
autoBoneConfig.getCalcInitError(),
autoBoneConfig.getRandomizeFrameOrder(),
autoBoneConfig.getScaleEachStep(),
autoBoneConfig.getSampleCount(),
autoBoneConfig.getSampleRateMs(),
autoBoneConfig.getSaveRecordings(),
autoBoneConfig.getUseSkeletonHeight(),
autoBoneConfig.getRandSeed()
);
}
/**
* Writes values from AutoBoneSettings to an AutoBoneConfig.
*
* @param autoBoneSettings The settings to read from.
* @param autoBoneConfig The config to write to.
* @return The autoBoneConfig parameter.
*/
public static AutoBoneConfig readAutoBoneSettings(
AutoBoneSettings autoBoneSettings,
AutoBoneConfig autoBoneConfig
) {
if (autoBoneSettings.hasCursorIncrement()) {
autoBoneConfig.setCursorIncrement(autoBoneSettings.cursorIncrement());
}
if (autoBoneSettings.hasMinDataDistance()) {
autoBoneConfig.setMinDataDistance(autoBoneSettings.minDataDistance());
}
if (autoBoneSettings.hasMaxDataDistance()) {
autoBoneConfig.setMaxDataDistance(autoBoneSettings.maxDataDistance());
}
if (autoBoneSettings.hasNumEpochs()) {
autoBoneConfig.setNumEpochs(autoBoneSettings.numEpochs());
}
if (autoBoneSettings.hasPrintEveryNumEpochs()) {
autoBoneConfig.setPrintEveryNumEpochs(autoBoneSettings.printEveryNumEpochs());
}
if (autoBoneSettings.hasInitialAdjustRate()) {
autoBoneConfig.setInitialAdjustRate(autoBoneSettings.initialAdjustRate());
}
if (autoBoneSettings.hasAdjustRateDecay()) {
autoBoneConfig.setAdjustRateDecay(autoBoneSettings.adjustRateDecay());
}
if (autoBoneSettings.hasSlideErrorFactor()) {
autoBoneConfig.setSlideErrorFactor(autoBoneSettings.slideErrorFactor());
}
if (autoBoneSettings.hasOffsetSlideErrorFactor()) {
autoBoneConfig.setOffsetSlideErrorFactor(autoBoneSettings.offsetSlideErrorFactor());
}
if (autoBoneSettings.hasFootHeightOffsetErrorFactor()) {
autoBoneConfig
.setFootHeightOffsetErrorFactor(autoBoneSettings.footHeightOffsetErrorFactor());
}
if (autoBoneSettings.hasBodyProportionErrorFactor()) {
autoBoneConfig
.setBodyProportionErrorFactor(autoBoneSettings.bodyProportionErrorFactor());
}
if (autoBoneSettings.hasHeightErrorFactor()) {
autoBoneConfig.setHeightErrorFactor(autoBoneSettings.heightErrorFactor());
}
if (autoBoneSettings.hasPositionErrorFactor()) {
autoBoneConfig.setPositionErrorFactor(autoBoneSettings.positionErrorFactor());
}
if (autoBoneSettings.hasPositionOffsetErrorFactor()) {
autoBoneConfig
.setPositionOffsetErrorFactor(autoBoneSettings.positionOffsetErrorFactor());
}
if (autoBoneSettings.hasCalcInitError()) {
autoBoneConfig.setCalcInitError(autoBoneSettings.calcInitError());
}
if (autoBoneSettings.hasRandomizeFrameOrder()) {
autoBoneConfig.setRandomizeFrameOrder(autoBoneSettings.randomizeFrameOrder());
}
if (autoBoneSettings.hasScaleEachStep()) {
autoBoneConfig.setScaleEachStep(autoBoneSettings.scaleEachStep());
}
if (autoBoneSettings.hasSampleCount()) {
autoBoneConfig.setSampleCount(autoBoneSettings.sampleCount());
}
if (autoBoneSettings.hasSampleRateMs()) {
autoBoneConfig.setSampleRateMs(autoBoneSettings.sampleRateMs());
}
if (autoBoneSettings.hasSaveRecordings()) {
autoBoneConfig.setSaveRecordings(autoBoneSettings.saveRecordings());
}
if (autoBoneSettings.hasUseSkeletonHeight()) {
autoBoneConfig.setUseSkeletonHeight(autoBoneSettings.useSkeletonHeight());
}
if (autoBoneSettings.hasRandSeed()) {
autoBoneConfig.setRandSeed(autoBoneSettings.randSeed());
}
return autoBoneConfig;
}
public static int createArmsResetModeSettings(
FlatBufferBuilder fbb,
ResetsConfig resetsConfig
) {
return ResetsSettings
.createResetsSettings(
fbb,
resetsConfig.getResetMountingFeet(),
resetsConfig.getMode().getId(),
resetsConfig.getYawResetSmoothTime(),
resetsConfig.getSaveMountingReset(),
resetsConfig.getResetHmdPitch()
);
}
public static int createSettingsResponse(FlatBufferBuilder fbb, VRServer server) {
ISteamVRBridge bridge = server.getVRBridge(ISteamVRBridge.class);
return SettingsResponse
.createSettingsResponse(
fbb,
RPCSettingsBuilder.createSteamVRSettings(fbb, bridge),
RPCSettingsBuilder
.createFilterSettings(
fbb,
server.configManager.getVrConfig().getFilters()
),
RPCSettingsBuilder
.createDriftCompensationSettings(
fbb,
server.configManager.getVrConfig().getDriftCompensation()
),
RPCSettingsBuilder
.createOSCRouterSettings(
fbb,
server.configManager.getVrConfig().getOscRouter()
),
RPCSettingsBuilder
.createVRCOSCSettings(
fbb,
server.configManager.getVrConfig().getVrcOSC()
),
RPCSettingsBuilder
.createVMCOSCSettings(
fbb,
server.configManager.getVrConfig().getVMC()
),
RPCSettingsBuilder
.createModelSettings(
fbb,
server.humanPoseManager,
server.configManager.getVrConfig().getLegTweaks(),
server.configManager.getVrConfig().getSkeleton()
),
RPCSettingsBuilder
.createTapDetectionSettings(
fbb,
server.configManager.getVrConfig().getTapDetection()
),
RPCSettingsBuilder
.createAutoBoneSettings(
fbb,
server.configManager.getVrConfig().getAutoBone()
),
RPCSettingsBuilder
.createArmsResetModeSettings(
fbb,
server.configManager.getVrConfig().getResetsConfig()
),
RPCSettingsBuilderKotlin.INSTANCE
.createStayAlignedSettings(
fbb,
server.configManager.getVrConfig().getStayAlignedConfig()
),
RPCSettingsBuilderKotlin.INSTANCE
.createHIDSettings(
fbb,
server.configManager.getVrConfig().getHidConfig()
)
);
}
}

View File

@@ -0,0 +1,458 @@
package dev.slimevr.protocol.rpc.settings
import com.google.flatbuffers.FlatBufferBuilder
import dev.slimevr.VRServer
import dev.slimevr.bridge.ISteamVRBridge
import dev.slimevr.config.AutoBoneConfig
import dev.slimevr.config.DriftCompensationConfig
import dev.slimevr.config.FiltersConfig
import dev.slimevr.config.HIDConfig
import dev.slimevr.config.LegTweaksConfig
import dev.slimevr.config.OSCConfig
import dev.slimevr.config.ResetsConfig
import dev.slimevr.config.SkeletonConfig
import dev.slimevr.config.StayAlignedConfig
import dev.slimevr.config.TapDetectionConfig
import dev.slimevr.config.VMCConfig
import dev.slimevr.config.VRCOSCConfig
import dev.slimevr.filtering.TrackerFilters.Companion.getByConfigkey
import dev.slimevr.tracking.processor.HumanPoseManager
import dev.slimevr.tracking.processor.config.SkeletonConfigToggles
import dev.slimevr.tracking.processor.config.SkeletonConfigValues
import dev.slimevr.tracking.trackers.TrackerRole
import solarxr_protocol.rpc.AutoBoneSettings
import solarxr_protocol.rpc.DriftCompensationSettings
import solarxr_protocol.rpc.FilteringSettings
import solarxr_protocol.rpc.HIDSettings
import solarxr_protocol.rpc.OSCRouterSettings
import solarxr_protocol.rpc.OSCSettings
import solarxr_protocol.rpc.OSCTrackersSetting
import solarxr_protocol.rpc.ResetsSettings
import solarxr_protocol.rpc.SettingsResponse
import solarxr_protocol.rpc.StayAlignedSettings
import solarxr_protocol.rpc.SteamVRTrackersSetting
import solarxr_protocol.rpc.TapDetectionSettings
import solarxr_protocol.rpc.VMCOSCSettings
import solarxr_protocol.rpc.VRCOSCSettings
import solarxr_protocol.rpc.settings.LegTweaksSettings
import solarxr_protocol.rpc.settings.ModelRatios
import solarxr_protocol.rpc.settings.ModelSettings
import solarxr_protocol.rpc.settings.ModelToggles
import solarxr_protocol.rpc.settings.SkeletonHeight
fun createOSCRouterSettings(
fbb: FlatBufferBuilder,
config: OSCConfig,
): Int {
val addressStringOffset = fbb.createString(config.address)
val oscSettingOffset = OSCSettings
.createOSCSettings(
fbb,
config.enabled,
config.portIn,
config.portOut,
addressStringOffset,
)
OSCRouterSettings.startOSCRouterSettings(fbb)
OSCRouterSettings.addOscSettings(fbb, oscSettingOffset)
return OSCRouterSettings.endOSCRouterSettings(fbb)
}
fun createVRCOSCSettings(
fbb: FlatBufferBuilder,
config: VRCOSCConfig,
): Int {
val addressStringOffset = fbb.createString(config.address)
val generalSettingOffset = OSCSettings
.createOSCSettings(
fbb,
config.enabled,
config.portIn,
config.portOut,
addressStringOffset,
)
val oscSettingOffset = OSCTrackersSetting
.createOSCTrackersSetting(
fbb,
config.getOSCTrackerRole(TrackerRole.HEAD, false),
config.getOSCTrackerRole(TrackerRole.CHEST, false),
config.getOSCTrackerRole(TrackerRole.WAIST, false),
config.getOSCTrackerRole(TrackerRole.LEFT_KNEE, false) &&
config.getOSCTrackerRole(TrackerRole.RIGHT_KNEE, false),
config.getOSCTrackerRole(TrackerRole.LEFT_FOOT, false) &&
config.getOSCTrackerRole(TrackerRole.RIGHT_FOOT, false),
config.getOSCTrackerRole(TrackerRole.LEFT_ELBOW, false) &&
config.getOSCTrackerRole(TrackerRole.RIGHT_ELBOW, false),
config.getOSCTrackerRole(TrackerRole.LEFT_HAND, false) &&
config.getOSCTrackerRole(TrackerRole.RIGHT_HAND, false),
)
VRCOSCSettings.startVRCOSCSettings(fbb)
VRCOSCSettings.addOscSettings(fbb, generalSettingOffset)
VRCOSCSettings.addTrackers(fbb, oscSettingOffset)
VRCOSCSettings.addOscqueryEnabled(fbb, config.oscqueryEnabled)
return VRCOSCSettings.endVRCOSCSettings(fbb)
}
fun createVMCOSCSettings(
fbb: FlatBufferBuilder,
config: VMCConfig,
): Int {
val addressStringOffset = fbb.createString(config.address)
val generalSettingOffset = OSCSettings
.createOSCSettings(
fbb,
config.enabled,
config.portIn,
config.portOut,
addressStringOffset,
)
val vrmJson = config.vrmJson
var vrmJsonOffset = 0
if (vrmJson != null) vrmJsonOffset = fbb.createString(vrmJson)
VMCOSCSettings.startVMCOSCSettings(fbb)
VMCOSCSettings.addOscSettings(fbb, generalSettingOffset)
if (vrmJson != null) VMCOSCSettings.addVrmJson(fbb, vrmJsonOffset)
VMCOSCSettings.addAnchorHip(fbb, config.anchorHip)
VMCOSCSettings.addMirrorTracking(fbb, config.mirrorTracking)
return VMCOSCSettings.endVMCOSCSettings(fbb)
}
fun createFilterSettings(
fbb: FlatBufferBuilder,
filtersConfig: FiltersConfig,
): Int = FilteringSettings
.createFilteringSettings(
fbb,
getByConfigkey(filtersConfig.type)!!.id,
filtersConfig.amount,
)
fun createDriftCompensationSettings(
fbb: FlatBufferBuilder,
driftCompensationConfig: DriftCompensationConfig,
): Int = DriftCompensationSettings
.createDriftCompensationSettings(
fbb,
driftCompensationConfig.enabled,
driftCompensationConfig.prediction,
driftCompensationConfig.amount,
driftCompensationConfig.maxResets,
)
fun createTapDetectionSettings(
fbb: FlatBufferBuilder,
tapDetectionConfig: TapDetectionConfig,
): Int = TapDetectionSettings
.createTapDetectionSettings(
fbb,
tapDetectionConfig.fullResetDelay,
tapDetectionConfig.fullResetEnabled,
tapDetectionConfig.fullResetTaps,
tapDetectionConfig.yawResetDelay,
tapDetectionConfig.yawResetEnabled,
tapDetectionConfig.yawResetTaps,
tapDetectionConfig.mountingResetDelay,
tapDetectionConfig.mountingResetEnabled,
tapDetectionConfig.mountingResetTaps,
tapDetectionConfig.setupMode,
tapDetectionConfig.numberTrackersOverThreshold,
)
fun createSteamVRSettings(fbb: FlatBufferBuilder, bridge: ISteamVRBridge?): Int {
var steamvrTrackerSettings = 0
if (bridge != null) {
steamvrTrackerSettings = SteamVRTrackersSetting
.createSteamVRTrackersSetting(
fbb,
bridge.getShareSetting(TrackerRole.WAIST),
bridge.getShareSetting(TrackerRole.CHEST),
bridge.getAutomaticSharedTrackers(),
bridge.getShareSetting(TrackerRole.LEFT_FOOT),
bridge.getShareSetting(TrackerRole.RIGHT_FOOT),
bridge.getShareSetting(TrackerRole.LEFT_KNEE),
bridge.getShareSetting(TrackerRole.RIGHT_KNEE),
bridge.getShareSetting(TrackerRole.LEFT_ELBOW),
bridge.getShareSetting(TrackerRole.RIGHT_ELBOW),
bridge.getShareSetting(TrackerRole.LEFT_HAND),
bridge.getShareSetting(TrackerRole.RIGHT_HAND),
)
}
return steamvrTrackerSettings
}
fun createModelSettings(
fbb: FlatBufferBuilder,
humanPoseManager: HumanPoseManager,
legTweaksConfig: LegTweaksConfig,
skeletonConfig: SkeletonConfig,
): Int {
val togglesOffset = ModelToggles
.createModelToggles(
fbb,
humanPoseManager.getToggle(SkeletonConfigToggles.EXTENDED_SPINE_MODEL),
humanPoseManager.getToggle(SkeletonConfigToggles.EXTENDED_PELVIS_MODEL),
humanPoseManager.getToggle(SkeletonConfigToggles.EXTENDED_KNEE_MODEL),
humanPoseManager.getToggle(SkeletonConfigToggles.FORCE_ARMS_FROM_HMD),
humanPoseManager.getToggle(SkeletonConfigToggles.FLOOR_CLIP),
humanPoseManager.getToggle(SkeletonConfigToggles.SKATING_CORRECTION),
humanPoseManager.getToggle(SkeletonConfigToggles.TOE_SNAP),
humanPoseManager.getToggle(SkeletonConfigToggles.FOOT_PLANT),
humanPoseManager.getToggle(SkeletonConfigToggles.SELF_LOCALIZATION),
humanPoseManager.getToggle(SkeletonConfigToggles.USE_POSITION),
humanPoseManager.getToggle(SkeletonConfigToggles.ENFORCE_CONSTRAINTS),
humanPoseManager.getToggle(SkeletonConfigToggles.CORRECT_CONSTRAINTS),
)
val ratiosOffset = ModelRatios
.createModelRatios(
fbb,
humanPoseManager.getValue(SkeletonConfigValues.WAIST_FROM_CHEST_HIP_AVERAGING),
humanPoseManager.getValue(SkeletonConfigValues.WAIST_FROM_CHEST_LEGS_AVERAGING),
humanPoseManager.getValue(SkeletonConfigValues.HIP_FROM_CHEST_LEGS_AVERAGING),
humanPoseManager.getValue(SkeletonConfigValues.HIP_FROM_WAIST_LEGS_AVERAGING),
humanPoseManager.getValue(SkeletonConfigValues.HIP_LEGS_AVERAGING),
humanPoseManager.getValue(SkeletonConfigValues.KNEE_TRACKER_ANKLE_AVERAGING),
humanPoseManager.getValue(SkeletonConfigValues.KNEE_ANKLE_AVERAGING),
)
val legTweaksOffset = LegTweaksSettings
.createLegTweaksSettings(
fbb,
legTweaksConfig.correctionStrength,
)
val skeletonConfigOffset = SkeletonHeight
.createSkeletonHeight(
fbb,
skeletonConfig.hmdHeight,
skeletonConfig.floorHeight,
)
return ModelSettings
.createModelSettings(
fbb,
togglesOffset,
ratiosOffset,
legTweaksOffset,
skeletonConfigOffset,
)
}
fun createAutoBoneSettings(
fbb: FlatBufferBuilder,
autoBoneConfig: AutoBoneConfig,
): Int = AutoBoneSettings
.createAutoBoneSettings(
fbb,
autoBoneConfig.cursorIncrement,
autoBoneConfig.minDataDistance,
autoBoneConfig.maxDataDistance,
autoBoneConfig.numEpochs,
autoBoneConfig.printEveryNumEpochs,
autoBoneConfig.initialAdjustRate,
autoBoneConfig.adjustRateDecay,
autoBoneConfig.slideErrorFactor,
autoBoneConfig.offsetSlideErrorFactor,
autoBoneConfig.footHeightOffsetErrorFactor,
autoBoneConfig.bodyProportionErrorFactor,
autoBoneConfig.heightErrorFactor,
autoBoneConfig.positionErrorFactor,
autoBoneConfig.positionOffsetErrorFactor,
autoBoneConfig.calcInitError,
autoBoneConfig.randomizeFrameOrder,
autoBoneConfig.scaleEachStep,
autoBoneConfig.sampleCount,
autoBoneConfig.sampleRateMs,
autoBoneConfig.saveRecordings,
autoBoneConfig.useSkeletonHeight,
autoBoneConfig.randSeed,
)
/**
* Writes values from AutoBoneSettings to an AutoBoneConfig.
*
* @param autoBoneSettings The settings to read from.
* @param autoBoneConfig The config to write to.
* @return The autoBoneConfig parameter.
*/
fun readAutoBoneSettings(
autoBoneSettings: AutoBoneSettings,
autoBoneConfig: AutoBoneConfig,
): AutoBoneConfig {
if (autoBoneSettings.hasCursorIncrement()) {
autoBoneConfig.cursorIncrement = autoBoneSettings.cursorIncrement()
}
if (autoBoneSettings.hasMinDataDistance()) {
autoBoneConfig.minDataDistance = autoBoneSettings.minDataDistance()
}
if (autoBoneSettings.hasMaxDataDistance()) {
autoBoneConfig.maxDataDistance = autoBoneSettings.maxDataDistance()
}
if (autoBoneSettings.hasNumEpochs()) {
autoBoneConfig.numEpochs = autoBoneSettings.numEpochs()
}
if (autoBoneSettings.hasPrintEveryNumEpochs()) {
autoBoneConfig.printEveryNumEpochs = autoBoneSettings.printEveryNumEpochs()
}
if (autoBoneSettings.hasInitialAdjustRate()) {
autoBoneConfig.initialAdjustRate = autoBoneSettings.initialAdjustRate()
}
if (autoBoneSettings.hasAdjustRateDecay()) {
autoBoneConfig.adjustRateDecay = autoBoneSettings.adjustRateDecay()
}
if (autoBoneSettings.hasSlideErrorFactor()) {
autoBoneConfig.slideErrorFactor = autoBoneSettings.slideErrorFactor()
}
if (autoBoneSettings.hasOffsetSlideErrorFactor()) {
autoBoneConfig.offsetSlideErrorFactor =
autoBoneSettings.offsetSlideErrorFactor()
}
if (autoBoneSettings.hasFootHeightOffsetErrorFactor()) {
autoBoneConfig
.footHeightOffsetErrorFactor =
autoBoneSettings.footHeightOffsetErrorFactor()
}
if (autoBoneSettings.hasBodyProportionErrorFactor()) {
autoBoneConfig
.bodyProportionErrorFactor = autoBoneSettings.bodyProportionErrorFactor()
}
if (autoBoneSettings.hasHeightErrorFactor()) {
autoBoneConfig.heightErrorFactor = autoBoneSettings.heightErrorFactor()
}
if (autoBoneSettings.hasPositionErrorFactor()) {
autoBoneConfig.positionErrorFactor = autoBoneSettings.positionErrorFactor()
}
if (autoBoneSettings.hasPositionOffsetErrorFactor()) {
autoBoneConfig
.positionOffsetErrorFactor = autoBoneSettings.positionOffsetErrorFactor()
}
if (autoBoneSettings.hasCalcInitError()) {
autoBoneConfig.calcInitError = autoBoneSettings.calcInitError()
}
if (autoBoneSettings.hasRandomizeFrameOrder()) {
autoBoneConfig.randomizeFrameOrder = autoBoneSettings.randomizeFrameOrder()
}
if (autoBoneSettings.hasScaleEachStep()) {
autoBoneConfig.scaleEachStep = autoBoneSettings.scaleEachStep()
}
if (autoBoneSettings.hasSampleCount()) {
autoBoneConfig.sampleCount = autoBoneSettings.sampleCount()
}
if (autoBoneSettings.hasSampleRateMs()) {
autoBoneConfig.sampleRateMs = autoBoneSettings.sampleRateMs()
}
if (autoBoneSettings.hasSaveRecordings()) {
autoBoneConfig.saveRecordings = autoBoneSettings.saveRecordings()
}
if (autoBoneSettings.hasUseSkeletonHeight()) {
autoBoneConfig.useSkeletonHeight = autoBoneSettings.useSkeletonHeight()
}
if (autoBoneSettings.hasRandSeed()) {
autoBoneConfig.randSeed = autoBoneSettings.randSeed()
}
return autoBoneConfig
}
fun createArmsResetModeSettings(
fbb: FlatBufferBuilder,
resetsConfig: ResetsConfig,
): Int = ResetsSettings
.createResetsSettings(
fbb,
resetsConfig.resetMountingFeet,
resetsConfig.mode.id,
resetsConfig.yawResetSmoothTime,
resetsConfig.saveMountingReset,
resetsConfig.resetHmdPitch,
)
fun createSettingsResponse(fbb: FlatBufferBuilder, server: VRServer): Int {
val bridge = server.getVRBridge(ISteamVRBridge::class.java)
return SettingsResponse
.createSettingsResponse(
fbb,
createSteamVRSettings(fbb, bridge),
createFilterSettings(
fbb,
server.configManager.vrConfig.filters,
),
createDriftCompensationSettings(
fbb,
server.configManager.vrConfig.driftCompensation,
),
createOSCRouterSettings(
fbb,
server.configManager.vrConfig.oscRouter,
),
createVRCOSCSettings(
fbb,
server.configManager.vrConfig.vrcOSC,
),
createVMCOSCSettings(
fbb,
server.configManager.vrConfig.vmc,
),
createModelSettings(
fbb,
server.humanPoseManager,
server.configManager.vrConfig.legTweaks,
server.configManager.vrConfig.skeleton,
),
createTapDetectionSettings(
fbb,
server.configManager.vrConfig.tapDetection,
),
createAutoBoneSettings(
fbb,
server.configManager.vrConfig.autoBone,
),
createArmsResetModeSettings(
fbb,
server.configManager.vrConfig.resetsConfig,
),
createStayAlignedSettings(
fbb,
server.configManager.vrConfig.stayAlignedConfig,
),
createHIDSettings(fbb, server.configManager.vrConfig.hidConfig),
)
}
fun createStayAlignedSettings(
fbb: FlatBufferBuilder,
config: StayAlignedConfig,
): Int = StayAlignedSettings
.createStayAlignedSettings(
fbb,
config.enabled,
false, // deprecated
config.hideYawCorrection,
config.standingRelaxedPose.enabled,
config.standingRelaxedPose.upperLegAngleInDeg,
config.standingRelaxedPose.lowerLegAngleInDeg,
config.standingRelaxedPose.footAngleInDeg,
config.sittingRelaxedPose.enabled,
config.sittingRelaxedPose.upperLegAngleInDeg,
config.sittingRelaxedPose.lowerLegAngleInDeg,
config.sittingRelaxedPose.footAngleInDeg,
config.flatRelaxedPose.enabled,
config.flatRelaxedPose.upperLegAngleInDeg,
config.flatRelaxedPose.lowerLegAngleInDeg,
config.flatRelaxedPose.footAngleInDeg,
config.setupComplete,
)
fun createHIDSettings(
fbb: FlatBufferBuilder,
config: HIDConfig,
): Int = HIDSettings
.createHIDSettings(
fbb,
config.trackersOverHID,
)

View File

@@ -1,43 +0,0 @@
package dev.slimevr.protocol.rpc.settings
import com.google.flatbuffers.FlatBufferBuilder
import dev.slimevr.config.HIDConfig
import dev.slimevr.config.StayAlignedConfig
import solarxr_protocol.rpc.HIDSettings
import solarxr_protocol.rpc.StayAlignedSettings
object RPCSettingsBuilderKotlin {
fun createStayAlignedSettings(
fbb: FlatBufferBuilder,
config: StayAlignedConfig,
): Int = StayAlignedSettings
.createStayAlignedSettings(
fbb,
config.enabled,
false, // deprecated
config.hideYawCorrection,
config.standingRelaxedPose.enabled,
config.standingRelaxedPose.upperLegAngleInDeg,
config.standingRelaxedPose.lowerLegAngleInDeg,
config.standingRelaxedPose.footAngleInDeg,
config.sittingRelaxedPose.enabled,
config.sittingRelaxedPose.upperLegAngleInDeg,
config.sittingRelaxedPose.lowerLegAngleInDeg,
config.sittingRelaxedPose.footAngleInDeg,
config.flatRelaxedPose.enabled,
config.flatRelaxedPose.upperLegAngleInDeg,
config.flatRelaxedPose.lowerLegAngleInDeg,
config.flatRelaxedPose.footAngleInDeg,
config.setupComplete,
)
fun createHIDSettings(
fbb: FlatBufferBuilder,
config: HIDConfig,
): Int = HIDSettings
.createHIDSettings(
fbb,
config.trackersOverHID,
)
}

View File

@@ -18,24 +18,9 @@ import kotlin.math.*
class RPCSettingsHandler(var rpcHandler: RPCHandler, var api: ProtocolAPI) {
init {
rpcHandler.registerPacketListener(RpcMessage.SettingsRequest) { conn: GenericConnection, messageHeader: RpcMessageHeader? ->
this.onSettingsRequest(
conn,
messageHeader,
)
}
rpcHandler
.registerPacketListener(
RpcMessage.ChangeSettingsRequest,
) { conn: GenericConnection?, messageHeader: RpcMessageHeader ->
this.onChangeSettingsRequest(
conn,
messageHeader,
)
}
rpcHandler.registerPacketListener(RpcMessage.SettingsResetRequest) { conn: GenericConnection, messageHeader: RpcMessageHeader? ->
this.onSettingsResetRequest(conn, messageHeader)
}
rpcHandler.registerPacketListener(RpcMessage.SettingsRequest, ::onSettingsRequest)
rpcHandler.registerPacketListener(RpcMessage.ChangeSettingsRequest, ::onChangeSettingsRequest)
rpcHandler.registerPacketListener(RpcMessage.SettingsResetRequest, ::onSettingsResetRequest)
}
fun onSettingsRequest(conn: GenericConnection, messageHeader: RpcMessageHeader?) {
@@ -331,7 +316,7 @@ class RPCSettingsHandler(var rpcHandler: RPCHandler, var api: ProtocolAPI) {
.vrConfig
.autoBone
RPCSettingsBuilder.readAutoBoneSettings(autoBoneSettings, autoBoneConfig)
readAutoBoneSettings(autoBoneSettings, autoBoneConfig)
}
if (req.resetsSettings() != null) {
@@ -391,7 +376,7 @@ class RPCSettingsHandler(var rpcHandler: RPCHandler, var api: ProtocolAPI) {
val settings = SettingsResponse
.createSettingsResponse(
fbb,
RPCSettingsBuilder.createSteamVRSettings(fbb, bridge), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
createSteamVRSettings(fbb, bridge), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
)
val outbound =
rpcHandler.createRPCMessage(fbb, RpcMessage.SettingsResponse, settings)

View File

@@ -3,7 +3,7 @@ package dev.slimevr.protocol.rpc.setup
import com.google.flatbuffers.FlatBufferBuilder
import dev.slimevr.protocol.GenericConnection
import dev.slimevr.protocol.ProtocolAPI
import dev.slimevr.protocol.datafeed.DataFeedBuilder
import dev.slimevr.protocol.datafeed.createTrackerId
import dev.slimevr.protocol.rpc.RPCHandler
import dev.slimevr.setup.TapSetupListener
import dev.slimevr.tracking.trackers.Tracker
@@ -20,7 +20,7 @@ class RPCTapSetupHandler(
override fun onStarted(tracker: Tracker) {
val fbb = FlatBufferBuilder(32)
val idOffset = DataFeedBuilder.createTrackerId(fbb, tracker)
val idOffset = createTrackerId(fbb, tracker)
val update = TapDetectionSetupNotification.createTapDetectionSetupNotification(fbb, idOffset)
val outbound =
rpcHandler.createRPCMessage(fbb, RpcMessage.TapDetectionSetupNotification, update)

View File

@@ -9,6 +9,7 @@ import org.java_websocket.WebSocket;
import org.java_websocket.drafts.Draft_6455;
import org.java_websocket.handshake.ClientHandshake;
import org.java_websocket.server.WebSocketServer;
import org.jetbrains.annotations.NotNull;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
@@ -101,7 +102,7 @@ public class WebsocketAPI extends WebSocketServer implements ProtocolAPIServer {
}
@Override
public Stream<GenericConnection> getAPIConnections() {
public @NotNull Stream<GenericConnection> getApiConnections() {
return this.getConnections().stream().map(conn -> {
var c = conn.<WebsocketConnection>getAttachment();
return (GenericConnection) c;

View File

@@ -141,7 +141,7 @@ public class UnixSocketRpcBridge implements dev.slimevr.bridge.Bridge,
}
@Override
public java.util.stream.Stream<GenericConnection> getAPIConnections() {
public java.util.stream.Stream<GenericConnection> getApiConnections() {
return this.selector
.keys()
.stream()