Skip to content

Commit dc5c91a

Browse files
committed
And, done (...probably ...?)
1 parent e14c293 commit dc5c91a

File tree

12 files changed

+1203
-439
lines changed

12 files changed

+1203
-439
lines changed

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
<h1 dir=auto>
2-
<b>Kinect One (Fake)</b>
2+
<b>Amethyst Tracking Relay</b>
33
<a style="color:#9966cc;" href="https://github.com/KinectToVR/Amethyst">Amethyst</a>
4-
<text>device plugin</text>
4+
<text>plugin</text>
55
</h1>
66

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

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

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

1818
## **Build & Deploy**
19-
Both build and deployment instructions [are available here](https://github.com/KimihikoAkayasaki/plugin_Relay/blob/main/.github/workflows/build.yml).
19+
Both build and deployment instructions [are available here](https://github.com/KinectToVR/plugin_Relay/blob/main/.github/workflows/build.yml).
2020
- Open in Visual Studio and publish using the prepared publish profile
2121
(`plugin_Relay``Publish``Publish``Open folder`)
2222
- Copy the published plugin to the `plugins` folder of your local Amethyst installation

plugin_Relay/Assets/Strings/en.json

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,125 @@
99
"id": "/Statuses/Failure",
1010
"translation": "Failure!\nS_SHUT\nThe device is offline!"
1111
},
12+
{
13+
"id": "/Statuses/Exception",
14+
"translation": "Server exception!\nE_EXCEPTION\nThe server couldn't be set up properly because a fatal exception occurred: {}"
15+
},
16+
{
17+
"id": "/Statuses/WasShutDown",
18+
"translation": "Server was shut down!\nS_SHUTBYUSER\nThe server was manually shut down, click 'Refresh' to restart it."
19+
},
1220
{
1321
"id": "/Settings/Labels/LocalIP/One",
1422
"translation": "Your Local IP:"
1523
},
1624
{
1725
"id": "/Settings/Labels/LocalIP/Multiple",
1826
"translation": "Your Local IP: (One of)"
27+
},
28+
{
29+
"id": "/Settings/Labels/Port",
30+
"translation": "Web server port:"
31+
},
32+
{
33+
"id": "/Buttons/Reconnect",
34+
"translation": "Reconnect"
35+
},
36+
{
37+
"id": "/Buttons/Disconnect",
38+
"translation": "Disconnect"
39+
},
40+
{
41+
"id": "/Titles/RelayStatus",
42+
"translation": "Status:"
43+
},
44+
{
45+
"id": "/Buttons/Copy",
46+
"translation": "Copy Error"
47+
},
48+
{
49+
"id": "/Buttons/JoinDiscord",
50+
"translation": "Join Discord"
51+
},
52+
{
53+
"id": "/Cached",
54+
"translation": "(Cached)"
55+
},
56+
{
57+
"id": "/Refresh/Test",
58+
"translation": "Testing service connection..."
59+
},
60+
{
61+
"id": "/Refresh/Ping",
62+
"translation": "Tested ping time:"
63+
},
64+
{
65+
"id": "/Refresh/Error",
66+
"translation": "Connection error:"
67+
},
68+
{
69+
"id": "/Refresh/Apply",
70+
"translation": " (Reconnect to apply updated settings)"
71+
},
72+
{
73+
"id": "/Refresh/ApplyServer",
74+
"translation": "Click 'Refresh' to apply"
75+
},
76+
{
77+
"id": "/Statuses/Failure/Version",
78+
"translation": "Cannot read tracking data!\nE_UNAVAILABLE\nYou're using an unsupported version on Amethyst."
79+
},
80+
{
81+
"id": "/Settings/Discovery/Header",
82+
"translation": "Amethyst Tracking Relay server discovered on {}!"
83+
},
84+
{
85+
"id": "/Settings/Discovery/Subtitle",
86+
"translation": "Would you like to connect to it?"
87+
},
88+
{
89+
"id": "/Statuses/Discovery/Connect",
90+
"translation": "Connect"
91+
},
92+
{
93+
"id": "/RelayStatuses/Success",
94+
"translation": "Success!\nS_OK\nEverything's good!"
95+
},
96+
{
97+
"id": "/RelayStatuses/ServiceError",
98+
"translation": "Service error!\nE_SERVICE_ERRROR\nThe data receiver service couldn't be instantiated due to an error. {}"
99+
},
100+
{
101+
"id": "/RelayStatuses/ConnectionError",
102+
"translation": "Connection error!\nE_CONNECTION_ERRROR\nCouldn't connect to the data service server due to an error. {}"
103+
},
104+
{
105+
"id": "/RelayStatuses/ConnectionLost",
106+
"translation": "Connection lost!\nE_CONNECTION_LOST\nConnection to the data service server was lost due to an error. {}"
107+
},
108+
{
109+
"id": "/RelayStatuses/DevicesListEmpty",
110+
"translation": "No remote devices!\nE_NO_DEVICES\nThe remote device list received from the server was empty."
111+
},
112+
{
113+
"id": "/RelayStatuses/BackFeedDetected",
114+
"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."
115+
},
116+
{
117+
"id": "/RelayStatuses/NotInitialized",
118+
"translation": "Not initialized!\nE_NOTINITIALIZED\nThe relay service client is not initialized. click 'Refresh' to restart it."
119+
},
120+
{
121+
"id": "/RelayStatuses/Disconnected",
122+
"translation": "Client disconnected!\nE_DISCONNECTED\nThe relay service client was disconnected. click 'Refresh' to restart it."
123+
},
124+
{
125+
"id": "/RelayStatuses/Other",
126+
"translation": "Service exception!\nE_OTHER\nAn unexpected exception has occurred. {}"
127+
},
128+
{
129+
"id": "/DeviceStatuses/Placeholder",
130+
"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."
19131
}
20132
]
21133
}

plugin_Relay/Beacon/Beacon.cs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Diagnostics;
4+
using System.Linq;
5+
using System.Net;
6+
using System.Net.Sockets;
7+
using System.Text;
8+
9+
namespace plugin_Relay.Beacon;
10+
11+
public class Beacon : IDisposable
12+
{
13+
internal const int DiscoveryPort = 35891;
14+
private readonly UdpClient _udp;
15+
16+
public Beacon(string beaconType, ushort advertisedPort)
17+
{
18+
BeaconType = beaconType;
19+
AdvertisedPort = advertisedPort;
20+
BeaconData = "";
21+
22+
_udp = new UdpClient();
23+
_udp.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
24+
_udp.Client.Bind(new IPEndPoint(IPAddress.Any, DiscoveryPort));
25+
26+
try
27+
{
28+
_udp.AllowNatTraversal(true);
29+
}
30+
catch (Exception ex)
31+
{
32+
Debug.WriteLine("Error switching on NAT traversal: " + ex.Message);
33+
}
34+
}
35+
36+
public string BeaconType { get; }
37+
public ushort AdvertisedPort { get; }
38+
public bool Stopped { get; private set; }
39+
40+
public string BeaconData { get; set; }
41+
42+
public void Dispose()
43+
{
44+
Stop();
45+
}
46+
47+
public void Start()
48+
{
49+
Stopped = false;
50+
_udp.BeginReceive(ProbeReceived, null);
51+
}
52+
53+
public void Stop()
54+
{
55+
Stopped = true;
56+
}
57+
58+
private void ProbeReceived(IAsyncResult ar)
59+
{
60+
var remote = new IPEndPoint(IPAddress.Any, 0);
61+
var bytes = _udp.EndReceive(ar, ref remote);
62+
63+
// Compare beacon type to probe type
64+
var typeBytes = Encode(BeaconType);
65+
if (HasPrefix(bytes, typeBytes))
66+
{
67+
// If true, respond again with our type, port and payload
68+
var responseData = Encode(BeaconType)
69+
.Concat(BitConverter.GetBytes((ushort)IPAddress.HostToNetworkOrder((short)AdvertisedPort)))
70+
.Concat(Encode(BeaconData)).ToArray();
71+
_udp.Send(responseData, responseData.Length, remote);
72+
}
73+
74+
if (!Stopped) _udp.BeginReceive(ProbeReceived, null);
75+
}
76+
77+
internal static bool HasPrefix<T>(IEnumerable<T> haystack, IEnumerable<T> prefix)
78+
{
79+
var enumerable = haystack.ToList();
80+
var second = prefix.ToList();
81+
82+
return enumerable.Count >= second.Count &&
83+
enumerable.Zip(second, (a, b) => a.Equals(b)).All(x => x);
84+
}
85+
86+
/// <summary>
87+
/// Convert a string to network bytes
88+
/// </summary>
89+
internal static IEnumerable<byte> Encode(string data)
90+
{
91+
var bytes = Encoding.UTF8.GetBytes(data);
92+
var len = IPAddress.HostToNetworkOrder((short)bytes.Length);
93+
94+
return BitConverter.GetBytes(len).Concat(bytes);
95+
}
96+
97+
/// <summary>
98+
/// Convert network bytes to a string
99+
/// </summary>
100+
internal static string Decode(IEnumerable<byte> data)
101+
{
102+
var listData = data as IList<byte> ?? data.ToList();
103+
104+
var len = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(listData.Take(2).ToArray(), 0));
105+
if (listData.Count < 2 + len) throw new ArgumentException("Too few bytes in packet");
106+
107+
return Encoding.UTF8.GetString(listData.Skip(2).Take(len).ToArray());
108+
}
109+
}

0 commit comments

Comments
 (0)