TheNexusAvenger / Enigma

Provides SteamVR tracker data to the Roblox client.
MIT License
12 stars 1 forks source link

Enigma

It's an enigma that this can project exist.

Enigma is a Roblox library and desktop application for providing OpenVR (SteamVR) tracker information to the Roblox client. Unlike other approaches, such as tigerVR, there is no server component required, but the approach to transfer data has some limitations.

Running

In order to run Enigma, the application needs to be downloaded from GitHub releases needs to be downloaded and ran. The first run sets up a Roblox Studio companion plugin. It is required to restart open Studio windows after the first run.

Once Enigma is running, it wait for OpenVR (SteamVR) to be detected, and start reading and sending tracker data using the methods below. Data will only be sent when a Roblox window is focused and when the application is running.

Command Line Arguments

A couple of command line arguments can be added to change how Enigma runs.

Graphical User Interface

At the moment, no user interface is provided. A SteamVR overlay for statistics and temporarily pausing/resuming would be ideal, but help is wanted to achieve this (OpenGL or Vulkan knowledge potentially required). A desktop application will most likely be made in the near future using Avalonia due to it being cross-platform.

Roblox Library API

The Roblox library has a few functions in the root module.

Below is a very simple example:

local Enigma = require(game:GetService("ReplicatedStorage"):WaitForChild("Enigma"))

Enigma:Enable()
while true do
    print(`Left foot CFrame: {Enigma:GetUserCFrame("LeftFoot")}`) --Might be nil at any time!
    task.wait()
end

The UserCFrames returned are meant to be mixed with the results of UserInputService::GetUserCFrame.

How It Works

Normal Operation

Enigma sends data using a combination of the system clipboard and a TextBox in Roblox. When an active Roblox window is detected, Enigma will continuously overwrite the keyboard, then run Ctrl + A and Ctrl + V to override the data in the TextBox.

Companion Plugin (Roblox Studio Only)

Due to blindly pasting with Roblox Studio being destructive, a companion plugin is automatically set up when Enigma is run. The plugin will always attempt to send heartbeat requests every 3 seconds, and will poll the latest data that would have been pushed about 60 times a second only while in run mode. The clipboard is not used to it being overwriting scripts.

Note: The plugin might be limited to replicating data at 8hz due to a Roblox Studio bug.

Limitations

macOS and Linux Support

At the moment, macOS and Linux probably do not work. It is unclear if they can be supported.

TextCopy and ASP.NET Core are cross-platform, though.

Building

Enigma uses .NET 8 for the desktop application and Rojo for the companion plugin and Roblox library. However, using Python with the publish.py script is the recommended way to create new releases.

# Windows
python ./publish.py
# macOS/Linux
python3 ./publish.py

When creating releases, .NET will use Native AOT for building to create smaller binaries with better performance and less memory. For macOS and Linux, see the prerequisites section on Microsoft's documentation.

Custom Messsages

While Enigma's desktop application and intended use is for SteamVR trackers, all components are set up for Enigma to be re-used for generic messages.

Below is an example of how to send a generic message using Enigma.Core:

await RobloxPlugins.CopyPluginAsync(); // Optional to set up the plugin, but only call once!
var appInstances = new AppInstances(); // Only call this once!
await appInstances.RobloxOutput.PushDataAsync("MyData");

And to read data on the client:

local ReplicatedStorage = game:GetService("ReplicatedStorage")

local Enigma = ReplicatedStorage:WaitForChild("Enigma")
local CombinedInput = require(Enigma:WaitForChild("Input"):WaitForChild("CombinedInput"))
local CompanionPluginInput = require(Enigma:WaitForChild("Input"):WaitForChild("CompanionPluginInput"))
local TextBoxInput = require(Enigma:WaitForChild("Input"):WaitForChild("TextBoxInput"))

local Input = CombinedInput.new(CombinedInput.new(), CompanionPluginInput.new())
print(Input:GetCurrentText()) --Remember that this can be anything (including empty strings) or 2 messages back-to-back!

Custom Application

You can replicate the main parts of the application by providing the following:

  1. When active, send an F13 key press when starting and every 250ms or sooner.
  2. After the first F13 key press is sent, send left Ctrl down, then A press, then V press, and Ctrl up.
  3. (Optional) For the Roblox Studio companion plugin, have the following endpoints: a. GET http://localhost:52821/enigma/data, which returns the current data for the client. b. POST http://localhost:52821/enigma/heartbeat, which is for the studio to say it is active and to disable clipboard pasting. This endpoint is required for the data fetching in the companion plugin.

License

Enigma is available under the terms of the MIT License. See LICENSE for details.