Skip to content

Commit

Permalink
And, done (...probably ...?)
Browse files Browse the repository at this point in the history
  • Loading branch information
KimihikoAkayasaki committed Sep 17, 2024
1 parent e14c293 commit dc5c91a
Show file tree
Hide file tree
Showing 12 changed files with 1,203 additions and 439 deletions.
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
<h1 dir=auto>
<b>Kinect One (Fake)</b>
<b>Amethyst Tracking Relay</b>
<a style="color:#9966cc;" href="https://github.com/KinectToVR/Amethyst">Amethyst</a>
<text>device plugin</text>
<text>plugin</text>
</h1>

## **License**
This project is licensed under the GNU GPL v3 License

## **Overview**
This repo is a pure implementation of the `ITrackingDevice` interface,
providing Amethyst support for the Xbox One Kinect, as a fake device.
Both the handler and the plugin itself ([available here](https://github.com/KimihikoAkayasaki/plugin_Relay/tree/main/plugin_Relay)) are written in C#
providing Amethyst support for the Tracking Relay to forward tracking data.
Both the handler and the plugin itself ([available here](https://github.com/KinectToVR/plugin_Relay/tree/main/plugin_Relay)) are written in C#

## **Downloads**
You're going to find built plugins in [repo Releases](https://github.com/KimihikoAkayasaki/plugin_Relay/releases/latest).
You're going to find built plugins in [repo Releases](https://github.com/KinectToVR/plugin_Relay/releases/latest).

## **Build & Deploy**
Both build and deployment instructions [are available here](https://github.com/KimihikoAkayasaki/plugin_Relay/blob/main/.github/workflows/build.yml).
Both build and deployment instructions [are available here](https://github.com/KinectToVR/plugin_Relay/blob/main/.github/workflows/build.yml).
- Open in Visual Studio and publish using the prepared publish profile
(`plugin_Relay``Publish``Publish``Open folder`)
- Copy the published plugin to the `plugins` folder of your local Amethyst installation
Expand Down
112 changes: 112 additions & 0 deletions plugin_Relay/Assets/Strings/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,125 @@
"id": "/Statuses/Failure",
"translation": "Failure!\nS_SHUT\nThe device is offline!"
},
{
"id": "/Statuses/Exception",
"translation": "Server exception!\nE_EXCEPTION\nThe server couldn't be set up properly because a fatal exception occurred: {}"
},
{
"id": "/Statuses/WasShutDown",
"translation": "Server was shut down!\nS_SHUTBYUSER\nThe server was manually shut down, click 'Refresh' to restart it."
},
{
"id": "/Settings/Labels/LocalIP/One",
"translation": "Your Local IP:"
},
{
"id": "/Settings/Labels/LocalIP/Multiple",
"translation": "Your Local IP: (One of)"
},
{
"id": "/Settings/Labels/Port",
"translation": "Web server port:"
},
{
"id": "/Buttons/Reconnect",
"translation": "Reconnect"
},
{
"id": "/Buttons/Disconnect",
"translation": "Disconnect"
},
{
"id": "/Titles/RelayStatus",
"translation": "Status:"
},
{
"id": "/Buttons/Copy",
"translation": "Copy Error"
},
{
"id": "/Buttons/JoinDiscord",
"translation": "Join Discord"
},
{
"id": "/Cached",
"translation": "(Cached)"
},
{
"id": "/Refresh/Test",
"translation": "Testing service connection..."
},
{
"id": "/Refresh/Ping",
"translation": "Tested ping time:"
},
{
"id": "/Refresh/Error",
"translation": "Connection error:"
},
{
"id": "/Refresh/Apply",
"translation": " (Reconnect to apply updated settings)"
},
{
"id": "/Refresh/ApplyServer",
"translation": "Click 'Refresh' to apply"
},
{
"id": "/Statuses/Failure/Version",
"translation": "Cannot read tracking data!\nE_UNAVAILABLE\nYou're using an unsupported version on Amethyst."
},
{
"id": "/Settings/Discovery/Header",
"translation": "Amethyst Tracking Relay server discovered on {}!"
},
{
"id": "/Settings/Discovery/Subtitle",
"translation": "Would you like to connect to it?"
},
{
"id": "/Statuses/Discovery/Connect",
"translation": "Connect"
},
{
"id": "/RelayStatuses/Success",
"translation": "Success!\nS_OK\nEverything's good!"
},
{
"id": "/RelayStatuses/ServiceError",
"translation": "Service error!\nE_SERVICE_ERRROR\nThe data receiver service couldn't be instantiated due to an error. {}"
},
{
"id": "/RelayStatuses/ConnectionError",
"translation": "Connection error!\nE_CONNECTION_ERRROR\nCouldn't connect to the data service server due to an error. {}"
},
{
"id": "/RelayStatuses/ConnectionLost",
"translation": "Connection lost!\nE_CONNECTION_LOST\nConnection to the data service server was lost due to an error. {}"
},
{
"id": "/RelayStatuses/DevicesListEmpty",
"translation": "No remote devices!\nE_NO_DEVICES\nThe remote device list received from the server was empty."
},
{
"id": "/RelayStatuses/BackFeedDetected",
"translation": "Backfeed detected!\nE_BACKFEED_CONF\nAmethyst was not supposed to create device loopholes. Your configuration is not supported. Think about what you have done while doing something productive instead."
},
{
"id": "/RelayStatuses/NotInitialized",
"translation": "Not initialized!\nE_NOTINITIALIZED\nThe relay service client is not initialized. click 'Refresh' to restart it."
},
{
"id": "/RelayStatuses/Disconnected",
"translation": "Client disconnected!\nE_DISCONNECTED\nThe relay service client was disconnected. click 'Refresh' to restart it."
},
{
"id": "/RelayStatuses/Other",
"translation": "Service exception!\nE_OTHER\nAn unexpected exception has occurred. {}"
},
{
"id": "/DeviceStatuses/Placeholder",
"translation": "Remote device unavailable!\nE_NOT_INITIALIZED\nAmethyst Tracking Relay is not available, this remote device is not going to work right now. To fix this, try refreshing the Tracking Relay and checking its status."
}
]
}
109 changes: 109 additions & 0 deletions plugin_Relay/Beacon/Beacon.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace plugin_Relay.Beacon;

public class Beacon : IDisposable
{
internal const int DiscoveryPort = 35891;
private readonly UdpClient _udp;

public Beacon(string beaconType, ushort advertisedPort)
{
BeaconType = beaconType;
AdvertisedPort = advertisedPort;
BeaconData = "";

_udp = new UdpClient();
_udp.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
_udp.Client.Bind(new IPEndPoint(IPAddress.Any, DiscoveryPort));

try
{
_udp.AllowNatTraversal(true);
}
catch (Exception ex)
{
Debug.WriteLine("Error switching on NAT traversal: " + ex.Message);
}
}

public string BeaconType { get; }
public ushort AdvertisedPort { get; }
public bool Stopped { get; private set; }

public string BeaconData { get; set; }

public void Dispose()
{
Stop();
}

public void Start()
{
Stopped = false;
_udp.BeginReceive(ProbeReceived, null);
}

public void Stop()
{
Stopped = true;
}

private void ProbeReceived(IAsyncResult ar)
{
var remote = new IPEndPoint(IPAddress.Any, 0);
var bytes = _udp.EndReceive(ar, ref remote);

// Compare beacon type to probe type
var typeBytes = Encode(BeaconType);
if (HasPrefix(bytes, typeBytes))
{
// If true, respond again with our type, port and payload
var responseData = Encode(BeaconType)
.Concat(BitConverter.GetBytes((ushort)IPAddress.HostToNetworkOrder((short)AdvertisedPort)))
.Concat(Encode(BeaconData)).ToArray();
_udp.Send(responseData, responseData.Length, remote);
}

if (!Stopped) _udp.BeginReceive(ProbeReceived, null);
}

internal static bool HasPrefix<T>(IEnumerable<T> haystack, IEnumerable<T> prefix)
{
var enumerable = haystack.ToList();
var second = prefix.ToList();

return enumerable.Count >= second.Count &&
enumerable.Zip(second, (a, b) => a.Equals(b)).All(x => x);
}

/// <summary>
/// Convert a string to network bytes
/// </summary>
internal static IEnumerable<byte> Encode(string data)
{
var bytes = Encoding.UTF8.GetBytes(data);
var len = IPAddress.HostToNetworkOrder((short)bytes.Length);

return BitConverter.GetBytes(len).Concat(bytes);
}

/// <summary>
/// Convert network bytes to a string
/// </summary>
internal static string Decode(IEnumerable<byte> data)
{
var listData = data as IList<byte> ?? data.ToList();

var len = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(listData.Take(2).ToArray(), 0));
if (listData.Count < 2 + len) throw new ArgumentException("Too few bytes in packet");

return Encoding.UTF8.GetString(listData.Skip(2).Take(len).ToArray());
}
}
Loading

0 comments on commit dc5c91a

Please sign in to comment.